본문 바로가기
Spring

공통에러 처리, RestFull 에서 ResponseEntity , @RestControllerAdvice

by windy7271 2024. 2. 4.
728x90
반응형

 

스프링 프로젝트를 진행하다보면 

중복된 에러 잡는 코드가 많아진다. 이러한 코드를 서비스단, 혹은 컨트롤 단에서 다 잡아주게되면 코드가 많아져 나중에 

가독성이 떨어질 수 있다.

 

보통 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에 넣고 , 상태값과 같이 리턴해준다.

반응형

댓글