참고 자료 : 초보 개발자를 위한 스프링5 프로그래밍 입문
1. 날짜를 이용한 회원 검색 기능
> selectBtRegdate() 메서드는
REGDATE 값이 두 파라미터로 전달받은 from과 to 사이에 있는
Member 목록을 구한다.
package spring;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
public class MemberDao {
private JdbcTemplate jdbcTemplate;
private RowMapper<Member> memRowMapper =
new RowMapper<Member>() {
@Override
public Member mapRow(ResultSet rs, int rowNum)
throws SQLException {
Member member = new Member(rs.getString("EMAIL"),
rs.getString("PASSWORD"),
rs.getString("NAME"),
rs.getTimestamp("REGDATE").toLocalDateTime());
member.setId(rs.getLong("ID"));
return member;
}
};
public MemberDao(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
.....
public List<Member> selectByRegdate(LocalDateTime from, LocalDateTime to) {
List<Member> results = jdbcTemplate.query(
"select * from MEMBER where REGDATE between ? and ? " +
"order by REGDATE desc",
new RowMapper<Member>() {
@Override
public Member mapRow(ResultSet rs, int rowNum)
throws SQLException {
Member member = new Member(
rs.getString("EMAIL"),
rs.getString("PASSWORD"),
rs.getString("NAME"),
rs.getTimeStamp("REGDATE").toLocalDateTime());
member.setId(rs.getLong("ID"));
return member;
}
},
from, to);
return results;
}
}
2. 커맨드 객체 DATE 타입 프로퍼티 변환처리 : @DateTimeFormat
검색 기준 시간을 표현하기 위해
커맨드 클래스를 구현함
package controller;
import java.time.LocalDateTime;
import org.springframework.format.annotation.DateTimeFormat;
public class ListCommand {
@DateTimeFormat(pattern = "yyyyMMddHH")
private LocalDateTime from;
@DateTimeFormat(pattern = "yyyyMMddHH")
private LocalDateTime to;
public LocalDateTime getFrom() {
return from;
}
public void setFrom(LocalDateTime from) {
this.from = from;
}
public LocalDateTime getTo() {
return to;
}
public void setTo(LocalDateTime to) {
this.to = to;
}
}
스프링은 Long이나 int 같은 기본 데이터 타입으로의 변환은
기본적으로 처리해주지만,
LocalDateTime 타입으로의 변환은 추가 설정이 필요함
@DateTimeFormat 어노테이션을 적용하면 된다.
해당 어노테이션이 적용되어 있으면
@DateTimeFormat 에서 지정한 형식을 이용하여
문자열을 LocalDateTIme 타입으로 변환함.
yyyyMMddHH
2018030115의 문자열을
2018년 3월 1일 15시 값을 갖는 LocalDateTIme 객체로 변환해줌
컨트롤러 클래스는 별도 설정 없이
ListCommand 클래스를
커맨드 객체로 사용하면 된다.
package controller;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import spring.Member;
import spring.MemberDao;
@Controller
public class MemberListController {
private MemberDao memberDao;
public void setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
}
@RequestMapping("/members")
public String list(
@ModelAttribute("cmd") ListCommand listCommand,
Errors errors, Model model) {
if (errors.hasErrors()) {
return "member/memberList";
}
if (listCommand.getFrom() != null && listCommand.getTo() != null) {
List<Member> members = memberDao.selectByRegdate(
listCommand.getFrom(), listCommand.getTo());
model.addAttribute("members", members);
}
return "member/memberList";
}
}
새로운 컨트롤러 코드를 작성했으니
ControllerConfig 설정 클래스에
빈 설정을 추가해주면 된다.
Error 타입 파라미터를 가지게 되면,
@DateTImeFormat에 지정한 형식에 맞지 않을 경우
Errors 객체에 typeMismatch 에러 코드를 추가함.
에러코드로 typeMismatch를 추가하므로
메시지 프로퍼티 파일에 해당 메시지를 추가하면
에러 메세지를 보여줄 수 있음.
3. 변환 처리에 대한 이해
WebDataBinder가 문자열을 LocalDateTIme 타입으로 변환하는데 관여함
스프링 MVC는 요청 매핑 어노테이션 적용 메서드와
DispatcherServlet 사이를 연결하기 위해서
RequestMappingHandlerAdapter 객체를 사용함
이 핸들러 어댑터 객체는
요청 파라미터와 커맨드 객체 사이의 변환 처리를 위해
WebDataBinder를 이용함
커맨드 객체란?
소프트웨어 디자인 패턴 중 하나인 "커맨트 패턴"에서 나온 용어로
특정한 작업을 나타내는 객체를 만들어
해당 작업을 수행하는 방법을 캡슐화하고
이를 호출하는 객체와 실제 작업을 수행하는 객체를
분리하는 것을 목적으로 함
WebDataBinder느 직접 타입을 변환하지 앟고
ConversionService에 그 역할을 위임한다.
@EnableWebMvc 어노테이션을 사용하면
DefaultFormattingConversionService를
ConversionService로 사용함.
4. MemberDao 클래스 중복 코드 정리 및 메서드 추가
:
package spring;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
public class MemberDao {
private JdbcTemplate jdbcTemplate;
private RowMapper<Member> memRowMapper =
new RowMapper<Member>() {
@Override
public Member mapRow(ResultSet rs, int rowNum)
throws SQLException {
Member member = new Member(rs.getString("EMAIL"),
rs.getString("PASSWORD"),
rs.getString("NAME"),
rs.getTimestamp("REGDATE").toLocalDateTime());
member.setId(rs.getLong("ID"));
return member;
}
};
public MemberDao(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public Member selectByEmail(String email) {
List<Member> results = jdbcTemplate.query(
"select * from MEMBER where EMAIL = ?",
memRowMapper, email);
return results.isEmpty() ? null : results.get(0);
}
public void insert(Member member) {
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con)
throws SQLException {
// 파라미터로 전달받은 Connection을 이용해서 PreparedStatement 생성
PreparedStatement pstmt = con.prepareStatement(
"insert into MEMBER (EMAIL, PASSWORD, NAME, REGDATE) " +
"values (?, ?, ?, ?)",
new String[] { "ID" });
// 인덱스 파라미터 값 설정
pstmt.setString(1, member.getEmail());
pstmt.setString(2, member.getPassword());
pstmt.setString(3, member.getName());
pstmt.setTimestamp(4,
Timestamp.valueOf(member.getRegisterDateTime()));
// 생성한 PreparedStatement 객체 리턴
return pstmt;
}
}, keyHolder);
Number keyValue = keyHolder.getKey();
member.setId(keyValue.longValue());
}
public void update(Member member) {
jdbcTemplate.update(
"update MEMBER set NAME = ?, PASSWORD = ? where EMAIL = ?",
member.getName(), member.getPassword(), member.getEmail());
}
public List<Member> selectAll() {
List<Member> results = jdbcTemplate.query("select * from MEMBER",
memRowMapper);
return results;
}
public int count() {
Integer count = jdbcTemplate.queryForObject(
"select count(*) from MEMBER", Integer.class);
return count;
}
public List<Member> selectByRegdate(LocalDateTime from, LocalDateTime to) {
List<Member> results = jdbcTemplate.query(
"select * from MEMBER where REGDATE between ? and ? " +
"order by REGDATE desc",
memRowMapper,
from, to);
return results;
}
public Member selectById(Long memId) {
List<Member> results = jdbcTemplate.query(
"select * from MEMBER where ID = ?",
memRowMapper, memId);
return results.isEmpty() ? null : results.get(0);
}
}
5. @PathVariable을 이용한 경로 변수 처리
경로의 일부가 고정되어 있지 않고
달라질 때 사용할 수 있는 것이
@PathVariable 어노테이션 !
가변 경로를 처리할 수 있음
{경로변수}와 같이
중괄호로 둘러 쌓인 부분을 경로 변수라고 부른다.
여기에 해당하는 값은
같은 경로 변수 이름을 지정한 @PathVariable 파라미터에 전달이 된다.
예를 들어
/member/{id}에서 {id}에 해당하는 부분의 경로 값을
@PathVariable("id") 어노테이션이 적용되
memId 파라미터에 전달된다.
6. 컨트롤러 익셉션 처리하기
익셉션 화면이 보이는 것보다
알맞게 익셉션을 처리하여
사용자에게 더 적합한 안내를 해주는 것이 좋다.
타입 변환 실패에 따른 익셉션 시
@ExceptionHandler 어노테이션을 사용한다.
같은 컨트롤러에 @ExceptionHandler 어노테이션을 적용한 메서드가 존재하면
그 메서드가 익셉션을 처리함.
즉, 컨트롤러에서 발생한 익셉션을 직접 처리하고 싶다면
해당 어노테이션을 적용하 메서드를 구현하면 된다.
익셉션 객체에 대한 정보를 알고 싶다면
메서드의 파라미터로 익셉션 객체를 전달받아 사용한다.
@ExceptionHandler(TypeMismatchException.class)
public String handleTypeMisMatchException(TypeMismatchException ex) {
// ex 사용해서 로그 남기는 등 작업
return "member/invalide";
}
6.1. @ControllerAdvice를 이용한 공통 익셉션 처리
: @ExceptionHandle 어노테이션을 적용하면
해당 컨트롤러에서 발생한 익셉션 만을 처리함.
여러 컨트롤러에서 동일하게 처리할 익셉션이 발생하면
@ControllerAdvice 어노테이션을 이용하여
중복을 없앨 수 있음.
@ControllerAdvice("spring")
public class CommandExceptionHandle {
@ExceptionHandler(RuntimeException.class)
public String handleRuntimeException() {
return "error/commonException";
}
}
@ControllerAdvice 어노테이션이 적용된 클래스는
지정한 범위의 컨트롤러에
공통으로 사용될 설정을 지정할 수 있음.
위 코드는 spring 패키지와 그 하위 패키지에 속한 컨트롤러 클래스를 위한
공통기능을 정의함
단, @ControllerAdvice 적용 클래스가 동작하려면
해당 클래스를 스프링에 빈으로 등록해야 함
'여니의 프로그래밍 study > Spring & Spring Boot' 카테고리의 다른 글
[Spring] Chapter 15 ~ 17 : 간단한 웹 어플리케이션의 구조, JSON 응답과 요청처리, 프로필과 프로퍼티 파일 (0) | 2023.12.29 |
---|---|
[Spring] MVC 3 : 세션, 인터셉터, 쿠키 (1) | 2023.12.26 |
[Spring] MVC 2 : 메시지, 커맨드 객체 검증 (1) | 2023.12.08 |
[Spring] MVC 1 : 요청 매핑, 커맨드 객체, 리다이렉트, 폼 태그, 모델 (Chapter 11) (0) | 2023.12.02 |
[Spring] 스프링 mvc 프레임워크 동작 방식 (Chapter 10) (0) | 2023.11.25 |