날짜를 이용한 회원 검색 기능
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 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);
}
}
커맨드 객체 Date 타입 프로퍼티 변환 처리: @DateTimeFormat
@Getter
@Setter
public class ListCommand {
// 형식 변경
@DateTimeFormat(pattern = "yyyyMMddHH" )
private LocalDateTime from;
@DateTimeFormat(pattern = "yyyyMMddHH" )
private LocalDateTime to;
}
스프링은 Long나 int와 같은 기본 데이터 타입으로의 변환은 기본적으로 처리해주지만LocalDateTime 타입의 변환은 추가 설정이 필요하다.
package controller;
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;
import java.util.List;
@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";
}
}
변환 처리에 대한 이해
@DateTimeFormat 애노테이션을 사용하면 지정한 형식의 문자열을 LocalDateTime 타입으로 변환해준다는 것을 예제를 통해 확인했다. 여기서 궁금증이 하나 생긴다. 누가 문자열을 LocalDateTime 타입으로 변환하는지에 대한 것이다. 답은 WebDataBinder에 있다.
스프링 MVC는 요청 매핑 애노테이션 적용 메서드와 DispatcherServlet 사이를 연결하기 위해RequestMappingHandlerAdapter 객체를 사용한다. 이 핸들러 어댑터 객체는 요청 파라미터와 커맨드 객체 사이의 변환 처리를 위해 WebDataBinder를 이용한다.
WebDataBinder는 커맨드 객체를 생성한다. 그리고 커맨드 객체의 프로퍼티와 같은 이름을 갖는 요청 파라미터를 이용해서 프로퍼티 값을 생성한다.
ConversionService에 그 역할을 위임한다. 스프링 MVC를 위한 설정인 @EnableWebMvc 애노테이션을 사용하면 DefaultFormattingConversionService를 ConversionService로 사용한다.
DefaultFormattingConversionServic 는 int, long과 같은 기본 데이터 타입뿐만 아니라@DateTimeFormat 애노테이션을 사용한 시간 관련 타입 변환 기능을 제공한다. 이런 이유로 커맨드로 이용할 클래스에 @DateTimeFormat 애노테이션만 붙이면 지정한 형식의 문자열을 시간 타입 값으로 받을 수 있는 것이다.
@PathVariable을 이용한 경로 변수 처리
package controller;
import org.springframework.beans.TypeMismatchException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import spring.Member;
import spring.MemberDao;
import spring.MemberNotFoundException;
@Controller
public class MemberDetailController {
private MemberDao memberDao;
public void setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
}
// {}사이에 있는 변수를 경로변수라고 부름, 이 변수는 @PathVariable애노테이션이 붙은
// 파라미터에 전달된다.
@GetMapping("/members/{id}")
public String detail(@PathVariable("id") Long memId, Model model) {
Member member = memberDao.selectById(memId);
if (member == null) {
throw new MemberNotFoundException();
}
model.addAttribute("member", member);
return "member/memberDetail";
}
// 같은 컨트롤러에 @ExceptionHandler 애노테이션을 이용한 메서드가 존재하면
// 그 메서드가 익셉션을 처리한다.
@ExceptionHandler(TypeMismatchException.class) // 경로 변수값의 타입이 올바르지 않을 때
public String handleTypeMismatchException() {
return "member/invalidId";
}
@ExceptionHandler(MemberNotFoundException.class)
public String handleNotFoundException() {
return "member/noMember";
}
}
@ControllerAdvice를 이용한 공통 익셉션 처리
여러 컨트롤러에서 동일하게 처리할 익셉션이 발생하면 @ControllerAdvice 애노테이션을 이용해서 중복을 없앨 수 있다.
//"spring" 패키지와 그 하위 패키지에 속한 컨트롤러 래스를 위한 공통 기능을 정의
@ControllerAdvice("spring")
public class CommonExceptionHandler {
@Exception Handler(RuntimeException.class}
public String handleRuntimeException() {
return "error/commonException":
}
}
@ExceptionHandler 적용메서드의 우선 순위
- 같은 컨트롤러에 위치한 @ExceptionHandler 메서드 중 해당 익셉션을 처리할 수 있는 메서드 를 검색
- 같은 클래스에 위치한 메서드가 익셉션을 처리할 수 없을 경우 @ControllerAdvice 클래스에 위치한 @ExceptionHandler 메서드를 검색
@ExceptionHandler 애노테이션 적용 매서드의 파라미터와 리턴 타입
@ExceptionHandler 애노테이션을 붙인 메서 드는 파라미터를 가질 수 있다.
- HttpServletRequest, HttpServletResponse , HttpSession
- Model
- 익셉션
리턴 가능한 타입은 다음과 같다.
- ModelAndV1ew
- String (뷰 이름)
- (@ResponseBody 애노테이션을 붙인 경우) 임의 객체 ( 1 장 참고)
- ResponseEntity (16 장 참고)
Reference
초보 웹개발자를 위한 스프링5 프로그래밍 입문
'Spring > 스프링5 프로그래밍 입문' 카테고리의 다른 글
Chapter 16. JSON 응답과 요청 처리 (0) | 2023.04.13 |
---|---|
Chapter 15. 간단한 웹 어플리케이션 구조 (0) | 2023.04.12 |
Chapter 13. 세션, 인터셉터, 쿠키 (0) | 2023.04.10 |
Chapter12. MVC 2: 메시지, 커맨드 객체 검증 (0) | 2023.04.07 |
Chapter 10. 스프링 MVC 프레임워크 동작 방식 (0) | 2023.04.03 |