본문 바로가기
프로젝트/팀 프로젝트) MEMO:RE

리팩토링 ) 컨트롤러를 핸들러와 응답 객체를 만들어 리팩토링 하기!

by 휴일이 2023. 9. 18.

 

어렴풋이 알았었다. 핸들러의 존재.

하지만 활용은 못했었다.

 

 

하지만 이번 과제를 계기로 핸들러의 존재를 알게 되었다.

그럼 뭐다? 활용한다!

아맞다 과제 회사는 떨어짐...ㅋ굿 ㅋㅋ

 

 

 

	try {

            String tagJson = tagService.writeForMain(id, tagDto);
            jsonObject.addProperty("tag", tagJson);

        } catch (UserNotFoundException e) {

            log.error("헤더에 들어있는 id 가 진짜 id 가 아니었다 Id = {}", id);
            jsonObject.addProperty("response", Response.USER_NOT_FOUND);
            return webService.badResponse(jsonObject);

        }

 

내 컨트롤러에는 이런 무분별한 try-catch 문들이 많았다.

이건 컨트롤러에서 직접 예외를 잡는 방법이다.

 

 

 

하지만 예외가 생길 경우!

이렇게 컨트롤러에서까지도 예외를 던져주면

핸들러에게 예외를 잡는 행위를 온전히 위임할 수 있다는 사실 ! (듀듕!)

 

 

 

핸들러를 만드는 법은 간단하당.

 

@ControllerAdvice
public class UserExceptionHandler {

핸들러 역할을 할 클래스에 @ControllerAdvice 애노테이션을 붙여준 후

 

@ExceptionHandler(UserNotFoundException.class)

메서드 위에는 @ExceptionHandler 로 어떤 예외를 잡는가 표시해주고

 

public ResponseEntity<Response> userNotFoundHandler(UserNotFoundException e) {

해당 메서드 매개변수로 해당 예외를 잡아주고

return Response.badRequest(e.getMessage());

응답을 해준다.

 

 

@ControllerAdvice
public class UserExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<Response> userNotFoundHandler(UserNotFoundException e) {
        return Response.badRequest(e.getMessage());
    }
    @ExceptionHandler(UsernameDuplException.class)
    public ResponseEntity<Response> usernameDuplicateHandler(UsernameDuplException e) {
        return Response.badRequest(e.getMessage());
    }
    @ExceptionHandler(LoginException.class)
    public ResponseEntity<Response> loginExceptionHandler(LoginException e) {
        return Response.badRequest(e.getMessage());
    }

}

 

그러면 요런 형식의 핸들러가 만들어진다!!!ㅎ_ㅎ

나는 e.getMessage() 를 넣어, 해당 예외가 발생했을 때 내가 넣은 예외 메시지를 응답 객체에 넣어주었당.ㅎㅎㅎ

그러면 프론트에서 좀 더 쉽게 예외 이유를 알 수 있겠지요? ㅎㅎㅎㅎㅎ 헷

 

 

 

응답 형식도 좀 변경했다.

Response 객체를 따로 만들었다.

 

public class Response {
    String message;
    HttpStatus status;
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    LocalDateTime timestamp;
    Object data;

    public static ResponseEntity<Response> ok(String message, Object data) {
        return responseWithData(HttpStatus.OK, message, data);
    }
    public static ResponseEntity<Response> ok() {
        return response(HttpStatus.OK, "SUCCESS");
    }
    public static ResponseEntity<Response> ok(Object data) {
        return responseWithData(HttpStatus.OK, "SUCCESS", data);
    }
    public static ResponseEntity<Response> badRequestWithData(String message, Object data) {
        return responseWithData(HttpStatus.BAD_REQUEST, message, data);
    }
    public static ResponseEntity<Response> badRequest(String message) {
        return response(HttpStatus.BAD_REQUEST, message);
    }

    private static ResponseEntity<Response> responseWithData(HttpStatus status, String message, Object data) {
        return ResponseEntity.status(status).body(
                Response.builder()
                        .status(status)
                        .message(message)
                        .timestamp(LocalDateTime.now())
                        .data(data)
                        .build()
        );
    }
    private static ResponseEntity<Response> response(HttpStatus status, String message) {
        return ResponseEntity.status(status).body(
                Response.builder()
                        .status(status)
                        .message(message)
                        .timestamp(LocalDateTime.now())
                        .build()
        );
    }
}

 

ResponseEntity<Response> 형식으로 응답한다.

응답은

메시지, 스테이터스코드, 타임스탬프(응답시간), 데이터(있다면 같이 보내기) 를 보낼 수 있다.

 

 

데이터를 같이 보내는 응답을 보며 확인해보자.

    private static ResponseEntity<Response> responseWithData(HttpStatus status, String message, Object data) {
        return ResponseEntity.status(status).body(
                Response.builder()
                        .status(status)
                        .message(message)
                        .timestamp(LocalDateTime.now())
                        .data(data)
                        .build()
        );
    }

ResponseEntity 를 보내되, 바디에 Response 형식이 들어가는 리턴이다.

그래서 바디에 Response 를 빌딩하는 코드를 써넣고

스테이터스, 메시지, 타임스탬프, 데이터를 함께 넣어주었다.

ResponseEntity 바디 안에 Response 객체를 써넣고 응답해주면 된다.

 

    public static ResponseEntity<Response> ok(Object data) {
        return responseWithData(HttpStatus.OK, "SUCCESS", data);
    }

다른 클래스들이 직접 사용하는 ok 응답 객체다.

성공했다면 메시지는 간단하게 SUCCESS 로 주고, OK도 기본이고, 데이터만 주어진 걸 넣는다.

그리고 응답 객체는 모든 컨트롤러가 사용하므로

인스턴스를 만들지 않고도 사용할 수 있도록

편하게 스태틱 메서드로 만든당!~

 

 

그러면 결론은...원본 컨트롤러에서는?

 

    @GetMapping
    public ResponseEntity<Response> list(HttpServletRequest request) throws UserNotFoundException {
        Long id = webService.getIdInHeader(request);

        List<String> tagList = tagService.tagList(id);
        String tagListJson = webService.objectToJson(tagList);

        return Response.ok(tagListJson);
    }

이렇게 간단한 코드가 만들어지는 것이다.

try-catch 문이 없어지고 예외를 던지기만 하면 간단하게 모든 처리가 가능하다!

이렇게 핸들러와 응답용 객체를 사용해서 아쥬아쥬 편리하게 컨트롤러 리팩토링 완료!!!!

 

-> 사실 tagList 를 직접 응답 객체에 넣어서 사용해도 된다.

하지만 ... 프론트에서 이미 tagList { 리스트 내역 } 형식으로 받기 때문에

부득이하게 제이슨으로 변환하여 사용할 수 밖에 없다는 구슬픈 사실 ㅠㅠ

이래서 처음부터 설계를 잘 하라는 건가보당...

 

 

해당 리팩토링 관련 커밋은 이곳에서 확인 가능하답니당!^_^

https://github.com/h0l1da2/MEMO-RE_BE/commit/d4c5db3f4d1e3f2af18e88a1e47ca470142e0c7f

 

refactor: Exception try-catch -> Handler 로 바꿈 · h0l1da2/MEMO-RE_BE@d4c5db3

핸들러에서 예외 잡는 것으로 변경

github.com

 

728x90