- 로그인을 처리하는데 예외처리에서 문제가 생겼다. 로그인을 하게되면 service에서 repository를 불러와 아이디를 가지고 있는 사용자인지, 비밀번호가 맞는지를 체크하고 만약 실패하면 IllegalArgumentException을 던진다.
@Transactional
public Member login(LoginRequestDto dto) {
Member member = memberRepository.findByLoginEmail(dto.getEmail())
.orElseThrow(() -> new IllegalArgumentException("해당 사용자가 없습니다."));
if (!dto.getPassword().equals(member.getPassword())) {
throw new IllegalArgumentException("비밀번호가 맞지 않습니다.");
}
return member;
}
- 문제는 이 예외를 controller에서 어떻게 처리하여 뷰를 넘겨 thymeleaf가 받을 수 있게 주는 것인가이다. 에러 페이지가 아닌 다시 로그인 화면을 넘겨주어 그곳에 메시지를 보여주려고 한다.
1. try catch
@PostMapping("/login")
public String login(@Validated @ModelAttribute LoginRequestDto dto,
BindingResult bindingResult,
@RequestParam(defaultValue = "/") String redirectURL,
HttpServletRequest request) {
if (bindingResult.hasErrors()) {
return "login/loginForm";
}
Member member;
try {
member = loginService.login(dto);
} catch (IllegalArgumentException e) {
bindingResult.reject("globalError", e.getMessage());
}
HttpSession session = request.getSession();
session.setAttribute("login_member", member);
return "redirect:" + redirectURL;
}
- try catch로 처리하여 넘겨주는 방법은 해당 Exception을 잡아 넘겨주는 것이다. 그런데 만약 여러 Exception이 만들어지게 될 겨우 모두 잡아줘야 한다는 것과 가독성, 번거로움이 있다.
2. @ExceptionHandler
@ExceptionHandler(IllegalArgumentException.class)
public String ex(IllegalArgumentException e, Model model) {
model.addAttribute(new LoginRequestDto());
return "login/loginForm";
}
- ExceptionHandler를 사용하는 것도 좋지만 내 상황에선 뷰를 넘겨야 하는데 로그인 폼에 맞는 dto를 넘겨줘야 한다는 번거로운 문제가 있다.
해결
- 문제 자체는 반환 값을 뷰를 넘겨주려고 해서 그런 것이다. 이걸 api 방식으로 바꾸어 넘겨주려고 한다. 해결하려는 순서는 이렇다.
- 명확한 오류 코드와 메시지 관리를 위해 커스텀 Exception을 만든다.
- exception들을 ExceptionHandler로 처리하여 api로 넘겨준다.
- 기존 form에서 submit하던 방식을 JS로 요청하여 api로 응답받는다.
1. 커스텀 Exception
- 기존 사용하던 IllegalArgumentException을 더 명확히 API 응답을 하기위해 커스텀 Exception을 만들어 응답하려고 한다.
// LoginException.class
@Getter
public class LoginException extends RuntimeException {
private final LoginExceptionCode loginExceptionCode;
public LoginException(LoginExceptionCode loginExceptionCode) {
super(loginExceptionCode.getMessage());
this.loginExceptionCode = loginExceptionCode;
}
}
// LoginExceptionCode.enum
@Getter
public enum LoginExceptionCode {
LOGIN_NOTFOUND_MEMBER(HttpStatus.BAD_REQUEST, "LOGIN_001", "존재하지 않는 회원입니다."),
LOGIN_PASSWORD_MISMATCH(HttpStatus.BAD_REQUEST, "LOGIN_002", "비밀번호가 맞지 않습니다.");
private final HttpStatus status;
private final String code;
private final String message;
LoginExceptionCode(HttpStatus status, String code, String message) {
this.status = status;
this.code = code;
this.message = message;
}
}
- LoginException이라는 커스텀 exception을 만들고 enum으로 오류 코드를 만들어 줬다.
// LoginService.class
@RequiredArgsConstructor
@Service
public class LoginService {
private final MemberRepository memberRepository;
@Transactional
public Member login(LoginRequestDto dto) {
Member member = memberRepository.findByLoginEmail(dto.getEmail())
.orElseThrow(() -> new LoginException(LoginExceptionCode.LOGIN_NOTFOUND_MEMBER));
if (!dto.getPassword().equals(member.getPassword())) {
throw new LoginException(LoginExceptionCode.LOGIN_PASSWORD_MISMATCH);
}
return member;
}
}
- 기존 사용하던 IllegalArgumentException에서 LoginException를 발생하여 오류 코드로 더 명확해졌다.
@Getter
public class ExceptionResponse {
private final HttpStatus status;
private final String code;
private final String message;
public ExceptionResponse(ExceptionCode exceptionCode) {
this.status = exceptionCode.getStatus();
this.code = exceptionCode.getCode();
this.message = exceptionCode.getMessage();
}
}
- 클라이언트에 보낼 ExceptionResponse를 만들었다.
- ExceptionCode는 enum인데 다른 포스트에 따로 기록해두었다.
@ExceptionHandler(LoginException.class)
public ResponseEntity<ExceptionResponse> loginExHandle(LoginException e) {
log.error("[loginExHandle] ex", e);
ExceptionResponse exceptionResponse = new ExceptionResponse(e.getLoginExceptionCode());
return ResponseEntity.status(exceptionResponse.getStatus()).body(exceptionResponse);
}
- ResponseEntity를 반환하는 곳에 정보를 넘겨주었다.
- 이제 이 정보를 가지고 뷰에서 보여주면 끝
참고
'Spring > Spring' 카테고리의 다른 글
[JWT] JWT에 대해서 (0) | 2023.01.21 |
---|---|
스프링 BindingResult 에러 메시지 JSON으로 응답하기 (0) | 2023.01.17 |
Interface로 추상화하여 Enum 사용 (0) | 2023.01.11 |
[Spring] spring 버전 (0) | 2022.12.20 |
댓글