스프링 프로젝트를 진행하다보면
중복된 에러 잡는 코드가 많아진다. 이러한 코드를 서비스단, 혹은 컨트롤 단에서 다 잡아주게되면 코드가 많아져 나중에
가독성이 떨어질 수 있다.
보통 Restfull 하다라는 것은 Rest API 의 원칙을 잘 지킨다는 말인데. URI만 보더라도 리소스를 명확하게 인식 할 수 있도록 표현 한 설계 방식을 의미한다 . 그래서 FrontEnd 와 협업시에 상태코드와, 성공여부, 데이터 만 보내주면, Front에서 그것 만 보고 코드를 짤 수 있게
만들어 줘야한다.
ResponseEntity 안에는 HTTP 상태코드, 응답헤더 ,응답 본문 등 이 들어간다.
바로 예시를 들어서 해보겠다.
회원가입으로 예를 들어보면
@PostMapping("/member/create")
public ResponseEntity<CommonResponse> memberCreate(@Valid @RequestBody MemberCreateReqDto memberCreateReqDto) {
Member member = memberService.create(memberCreateReqDto);
return new ResponseEntity<>(new CommonResponse(HttpStatus.CREATED, "member successfully created", member.getId()), HttpStatus.CREATED);
}
이렇게 코드가 짜져있는데.
memeberCreateReqDto 에는 회원가입에 필요하다고 생각한 email, name, password만 들어있다.
return을 잘 보면 되는데
위에서 ResponseEntitiy에 HTTP 상태코드, 응답헤더 ,응답 본문 등이 들어간다고 했다.
만약 성공했을때의 Response를 돌려주기 위해서 CommonResponse 클래스를 선언해주었다.
@Data
@AllArgsConstructor
public class CommonResponse {
private HttpStatus httpStatus;
private String message;
private Object result; // memberId
}
CommonResponse안에는 상태코드, 메세지, 결과가 들어있다. 그리고 ResponseEntity 에 상태코드를 담아서 넣어줬다.
결과는 이렇게 담기게 된다.
그러면 실패했을때는 어떻게 되냐 ? 코드가 정상적으로 안 돌아가면 에러를 터트려야하고, 에러메세지도 던져줘야한다.
그래서 ErrorResponseDto 도 클래스로 선언했다.
public class ErrorResponseDto {
public static ResponseEntity<Map<String, Object>> makMessage(HttpStatus status, String Message) {
HashMap<String, Object> body = new HashMap<>();
body.put("status", Integer.toString(status.value()));
body.put("status message", status.getReasonPhrase());
body.put("error message", Message);
return new ResponseEntity<>(body, status);
}
}
에러가 터졌을때 메세지를 터트려 주는건데, 상태와 메시지 를 받아와서 ResponseEntity 형식으로 만들어준다.
그럼 에러가 터지는 부분을 봐보겠다.
그러기에 앞서 member/create 에러 잡는 코드를 안 건드려놔서 로그인 코드로 해보겠다.
@PostMapping("/doLogin")
public ResponseEntity<CommonResponse> memberLogin(@Valid @RequestBody LoginReqDto loginReqDto) {
Member member = memberService.login(loginReqDto);
String jwtToken = jwtTokenProvider.createToken(member.getEmail(), member.getRole().toString());
Map<String, Object> member_info = new HashMap<>();
member_info.put("id", member.getId());
member_info.put("token", jwtToken);
return new ResponseEntity<>(new CommonResponse(HttpStatus.OK, "member successfully logined", member_info), HttpStatus.OK);
}
만약에 성공했을시 토큰과 함께 나온다.
이제 실패시를 확인해보겠다.
없는 이메일을 넘겨주면
MemberService
public Member login(LoginReqDto loginReqDto) throws IllegalArgumentException{
Member member = memberRepository.findByEmail(loginReqDto.getEmail())
.orElseThrow(() -> new IllegalArgumentException("존재하지 않은 이메일입니다."));
if (!passwordEncoder.matches(loginReqDto.getPassword(), member.getPassword())) {
throw new IllegalArgumentException("비밀번호가 일치하지 않습니다.");
}
return member;
}
원래대로라면 여기서 에러가 터져서 Controller에서 잡아주는 식으로 해준다. 하지만 내 결과는
Handler가 잡아준다.
- ExceptionHandlerClass
@RestControllerAdvice
@Slf4j
public class ExeptionHandlerClass {
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<Map<String, Object>> entityNotFoundHandler(EntityNotFoundException e) {
log.error("Handler EntityNotFoundException message : " + e.getMessage());
return ErrorResponseDto.makMessage(HttpStatus.NOT_FOUND, e.getMessage());
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<Map<String, Object>> IllegalArguHandler(IllegalArgumentException e) {
log.error("Handler IllegalArgumentException message : " + e.getMessage());
return ErrorResponseDto.makMessage(HttpStatus.BAD_REQUEST, e.getMessage());
}
}
에러가 터졌을때 ExeptionHandlerClass 에서 잡아준다.
즉 위에 IllegalArgumentException 에러가 터졌을때. Handler가 나타나서 에러를 잡아주고 로그를 출력해준다.
중간에 에러가 터졌을때 잡아채기 위해서
@RestControllerAdvice
를 붙여준다.
@ExceptionHandler(EntityNotFoundException.class)
는 말그대로 EntitiyNotFoundException이 터지면 채간다.
그리고 return으로 ErrorResponseDto로 넘겨주면서
ErrorResponseDto 안에 있는
status message , error emssage, status 를 body에 넣고 , 상태값과 같이 리턴해준다.
댓글