날짜값 변환, PathVariable, 익셉션 처리

HeeSeong·2021년 8월 7일
0
post-thumbnail

날짜값 변환


Jsp에서 input 태그에 입력한 날짜 문자열을 LocalDateTime 타입으로 변환해야 한다. 스프링은 Long이나 int와 같은 기본 데이터 타입으로의 변환은 기본적으로 처리해 주지만 LocalDateTime 타입으로의 변환은 추가 설정이 필요하다. 컨트롤러에서 파라미터로 받아주는 클래스의 날짜 필드에 @DateTimeFormat 애노테이션을 적용하면 된다.


public class ListCommand {

	@DateTimeFormat(pattern = "yyyyMMddHH")
	private LocalDateTime from;
    
	@DateTimeFormat(pattern = "yyyyMMddHH")
	private LocalDateTime to;
    
}

커맨드 객체에 @DateTimeFormat 애노테이션이 적용되어 있으면 지정한 형식을 이용해서 문자열을 LocalDateTime 타입으로 변환한다.


변환 처리에 대한 이해


어떤 것이 문자열을 LocalDateTime 타입으로 변환해주는 것일까? 답은 WebDataBinder에 있다. 스프링 MVC는 요청 매핑 애노테이션 적용 메서드와 DispatcherServlet 사이를 연결하기 위해 RequestMappingHandlerAdapter 객체를 사용한다. 이 핸들러 어댑터 객체는 요청 파라미터와 커맨드 객체 사이의 변환 처리를 위해 WebDataBinder를 이용한다.

WebDataBinder는 커맨드 객체를 생성한다. 그리고 커맨드 객체의 프로퍼티와 같은 이름을 갖는 요청 파라미터를 이용해서 프로퍼티 값을 생성한다.



WebDataBinder는 직접 타입을 변환하지 않고 그림처럼 ConversionService에 그 역할을 위임한다. 스프링 MVC를 위한 설정인 @EnableWebMvc 애노테이션을 사용하면 DefaultFormattingConversionService를 ConversionService로 사용한다.

DefaultFormattingConversionService는 int, long과 같은 기본 데이터 타입뿐만 아니라 @DateTimeFormat 애노테이션을 사용한 시간 관련 타입 변환 기능을 제공한다. 이러한 이유로 커맨드로 사용할 클래스에 @DateTimeFormat 애노테이션만 붙이면 지정한 형식의 문자열을 시간 타입 값으로 받을 수 있는 것이다.


@PathVariable 경로 변수 처리


URL 경로의 일부가 고정되어 있지 않고 달라질 때 사용할 수 있는 것이 @PathVariable 애노테이션이다.


Controller
public class MemberDetailController {

	private MemberDao memberDao;

	public void setMemberDao(MemberDao memberDao) {
		this.memberDao = memberDao;
	}

	@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";
	}
}

매핑 경로에 '{경로변수}'와 같이 중괄호로 둘러 쌓인 부분을 경로 변수라고 부른다. 경로 변수에 해당하는 값은 같은 경로 변수 이름을 지정한 @PathVariable 파라미터에 전달된다. "/members/{id}"에서 {id}에 해당하는 부분의 경로 값을 @PathVariable("id") 애노테이션이 적용된 memberId 파라미터에 전달한다.


컨트롤러 익셉션 처리


사용자가 잘못된 입력 등을 했을때 익셉션 화면이 보이는 것보다 알맞게 익셉션을 처리해서 더 적합한 안내를 해주는 것이 좋다. 이럴 때 유용하게 사용할 수 있는 것이 @ExceptionHandler 애노테이션이다.

같은 컨트롤러에 @ExceptionHandler 애노테이션을 적용한 메서드가 존재하면 그 메서드가 익셉션을 처리한다. 따라서 컨트롤러에서 발생한 익셉션을 직접 처리하고 싶다면 @ExceptionHandler 애노테이션을 적용한 메서드를 구현하면 된다.


@Controller
public class MemberDetailController {

	@ExceptionHandler(TypeMismatchException.class)
	public String handleTypeMismatchException() {
		return "member/invalidId";
	}

	@ExceptionHandler(MemberNotFoundException.class)
	public String handleNotFoundException() {
		return "member/noMember";
	}

}

@ExceptionHandler의 값으로 TypeMismatchException.class를 주고 있다. 이 익셉션은 경로 변수값의 타입이 올바르지 않을 때 발생한다. 이 익셉션이 발생하면 에러 응답을 보내는 대신 handleTypeMismatchException() 메서드를 실행한다. 익셉션 객체에 대한 정보를 알고 싶다면 메서드의 파라미터로 익셉션 객체를 전달받아 사용하면 된다.


@ExceptionHandler(TypeMismatchException.class)
	public String handleTypeMismatchException(TypeMismatchException ex) {
    	// ex~
		return "member/invalidId";
	}

@ControllerAdvice 공통 익셉션 처리


컨트롤러 클래스에 @ExceptionHandler 애노테이션을 적용하면 해당 컨트롤러에서 발생한 익셉션만 처리한다. 다수의 컨트롤러에서 동일 타입의 익셉션이 발생할 수도 있다. 이때 익셉션 처리 코드가 동일하다면 어떻게 해야 할까? 각 컨트롤러 클래스마다 익셉션 처리 메서드를 구현하는 것은 불필요한 코드 중복을 발생시킨다.

여러 컨트롤러에서 동일하게 처리할 익셉션이 발생하면 @ControllerAdvice 애노테이션을 이용해서 중복을 없앨 수 있다.


@ControllerAdvice("spring")
public class CommonExceptionHandler {

	@ExceptionHandler(RuntimeException.class)
	public String handleRuntimeException() {
		return "error/commonException";
	}
}

@ControllerAdvice 애노테이션이 적용된 클래스는 지정한 범위의 컨트롤러에 공통으로 사용될 설정을 지정할 수 있다. 위 코드는 "spring" 패키지와 그 하위 패키지에 속한 컨트롤러 클래스를 위한 공통 기능을 정의했다. spring 패키지와 그 하위 패키지에 속한 컨트롤러에서 RuntimeException이 발생하면 handleRuntimeException() 메서드를 통해서 익셉션을 처리한다.


@ExceptionHandler 적용 메서드의 우선 순위


@Controller 클래스에 있는 @ExceptionHandler 메서드와 컨트롤러 클래스에 있는 @ExceptionHandler 메서드 중 컨트롤러 클래스에 적용된 메서드가 우선한다. 즉 컨트롤러에서 메서드를 실행하는 과정에서 익셉션이 발생하면 다음의 순서로 익셉션을 처리할 @ExceptionHandler 메서드를 찾는다.


  • 같은 컨트롤러에 위치한 @ExceptionHandler 메서드 중 해당 익셉션을 처리할 수 있는 메서드를 검색

  • 같은 클래스에 위치한 메서드가 익셉션을 처리할 수 없을 경우 @ControllerAdvice 클래스에 위치한 @ExceptionHandler 메서드를 검색

profile
끊임없이 성장하고 싶은 개발자

0개의 댓글