프로젝트/팀 프로젝트) MEMO:RE

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

휴일이 2023. 9. 18. 21:51

 

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

하지만 활용은 못했었다.

 

 

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

그럼 뭐다? 활용한다!

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

 

 

 

	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