시작하며
안녕하세요! 껌딱지 개발팀에서 기술 리드이자 백엔드 파트 리드 및 개발을 맡고 있는 BackEnd 개발자 종하입니다!
어느덧 팀원들간의 블로그 작성 사이클이 한바퀴 돌아 제 차례가 다시 오게 되었네요 ㅎㅎ..
오늘은 기술적인 얘기를 좀 해볼까 합니다, 저는 그 동안 저의 엔드포인트 설계가 이게 맞나.. 싶으면서 코드를 짯었어요.그러다 오랜만에 제가 좋아하는 선배랑 코드를 같이 볼 시간이 생겼고 선배가 "Exception Handler"를 사용하는게 어때?라고 하자마자
저는 어? 그런게 있다고? 라고 생각하여 바로 도입을 하고 매우 만족했던 경험에 대해서 공유드리려고 합니다.
기존의 예외처리 방법
try {
Example e = exampleService.example();
return result("success", "성공!");
} catch (내가 만든 예외1 | 내가 만든 예외2 e)
return result("fail", e.getMessage);
}
기존에는 위 코드 처럼 컨트롤러단에서 예외 처리를 하고 있었습니다., 서비스단에서 커스텀 되어 있는 예외를 throw하면 컨트롤러에서 catch해서 메세지를 반환해주는 방식으로 사용하고 있었죠. 저는 이 방식도 꽤나 만족하며 쓰고 있었지만 좀 코드가 더러워 보이긴 했어요.
예외처리와 로깅 부분이 항상 신경이 쓰였습니다. 서비스 회사들을 아직 경험해본적이 없어서 어떤 방식이 Best Practice인지 잘 몰랐기도 합니다. 하지만 뭔가 서비스 회사들은 이런식으로 컨트롤러 코드를 짜지 않을것이라는 생각이 들곤 했어요, 그냥 당연히 그럴것 같은 느낌?
Exception Handler란?
@ExceptionHandler은 Spring에서 예외처리를 위하여 제공하는 어노테이션입니다, @Controller 또는 @RestController 어노테이션을 사용하는 컨트롤러 클래스의 예외를 잡아서 대신 처리해주는 기능을 수행합니다.
@ExceptionHandler(RuntimeException.class)
protected BasicErrorResult handleRuntimeException(RuntimeException e) {
return new BasicErrorResult("fail", e.getMessage());
}
ExceptionHandler의 사용 예제, 어노테이션에 예외 종류를 명시하고 함수 인자값에 catch할 예외들을 명시해주면 됩니다.
하지만 이런식으로 단순하게 ExceptionHandler 어노테이션만 사용한다면 컨트롤러 별로 코드를 넣어줘야 합니다.
그래서 전역적으로 예외를 관리할 수 있는 @RestControllerAdvice 어노테이션에 대하여 알아보았습니다.
RestControllerAdvice란?
Spring에서 전역적으로 예외처리를 할 수 있는 어노테이션을 두가지 제공합니다, @ControllerAdvice와 @RestControllerAdvice이다.
RestControllerAdvice는 ControllerAdvice와 달리 json형태로 응답을 반환하기 때문에 자연스럽게 사용하게 되었습니다.
@RestControllerAdvice
public class GlobalExceptionHandler {
DiscordLogger discordLogger = DiscordLogger.instance();
public record BasicErrorResult(String result, String message) {}
@ExceptionHandler(RuntimeException.class)
protected BasicErrorResult handleRuntimeException(RuntimeException e) {
return new BasicErrorResult("fail", e.getMessage());
}
@ExceptionHandler(Exception.class)
protected BasicErrorResult handleUnExpectedException(Exception e) {
discordLogger.send(String.format("🚨경고🚨 예상되지 않은 오류 발생! 오류 정보 : %s", e.getMessage()),Scope.here);
return new BasicErrorResult("fail", "예상치 못한 오류가 발생했습니다, 관리자에게 제보해주세요");
}
}
현재는 다음과 같이 GlobalExceptionHandler 클래스를 만들어 사용중, 현재 선언되어 있는 커스텀 예외들이 다 RuntimeException을 상속 받고 있어서 저런식으로 선언하였지만 중간 클래스를 다시 만들어 내가 선언한 커스텀 예외들만 처리할 수 있도록 수정할 계획입니다. 그리고 예상치 못한 오류를 잡기 위해 최상위 Exception 클래스를 선언해서 나머지 오류들은 모두 디스코드로 알림을 받도록 코드를 하였습니다. 일단 처음에 임시로 적용해본 예제이고 예외처리 관련 클래스 상하관계를 잘 설계하여 수정할 예정입니다.
ExceptionHandler를 도입하고 좋아진 점
일단 제일 먼저 컨트롤러의 코드가 전보다 깔끔해진 부분이 제일 맘에 듭니다.
try/catch 문을 많이 사용할때는 코드가 보기 좋지 않았는데 ExceptionHandler로 인해서 컨트롤러에 예외처리 코드가 사라지게 되니 한눈에 중요 부분을 알아볼 수 있고 실패 처리도 통일해서 할 수 있어서 너무 좋은것 같습니다.
마치며
항상 예외처리와 로깅부분에 대해서 고민이 많았습니다. 어떤 방식이 BestPractice인지 많이 고민했는데 ExceptionHandler를 도입하면서 예외처리 부분에서는 한숨 돌린것 같습니다. 아직 가야 할 길이 멀지만 ㅎㅎ.. 다음 글에서는 로깅 부분을 개선하고 그 내용에 대해서 정리하는 글로 돌아오도록 하겠습니다!
'우리들의 성장일지' 카테고리의 다른 글
[팀 껌딱지] PWA(프로그레시브 웹 앱)이란? (1) | 2023.12.04 |
---|---|
[팀 껌딱지] 비즈니스 기획: e-비즈니스는 어떻게 시작할까? (1) | 2023.11.28 |
[팀 껌딱지] 피그마가 무엇일까? (1) | 2023.11.12 |
[팀 껌딱지] 백엔드 기초 - 스프링(Spring) 프레임워크란? (0) | 2023.11.06 |
[팀 껌딱지] 네이버 지도 API 사용하기 (1) | 2023.10.30 |