토이프로젝트를 진행하면서, 수많은 예외와 마주하고 있습니다.
그 수많은 예외들을 공통적인 포맷을 갖도록 하여 보다 구체적이고 편리한 방식으로 오류 및 예외 처리할 수 있게 하는 것을 위한 글입니다.
공통적인 포맷을 사용하면 모든 예외가 읽기 쉬운 방식으로 표현될 수 있으며,
예외에 대한 더 많은 컨텍스트(정보)를 제공하여 사용자가 문제를 신속하게 해결할 수 있습니다.
1. 공통 응답 포맷 정의하기
예외를 처리할 때 중요한 것을 생각해 보았을 때 아래와 같았습니다.
- 언제 발생하였을까?
- 어떤 상태인가?
- 왜 발생하였을까?
이것들을 반영하였을 때, 포맷 응답 값은 아래와 같았습니다.
{
"timestamp": "2023-03-12T19:31:04.278737",
"status": 400,
"error": "중복된 이메일입니다. 다시 입력해주세요.",
"message": {}
}
1-1. ErrorResponse 만들기
위의 요구 사항들을 반영하게 된다면, 아래와 같이 작성할 수 있습니다.
- timestamp : 언제
- status : Http Status
- error : 에러 설명
- message : 에러에 발생한 이유
data class ErrorResponse(
val timestamp: LocalDateTime,
val status: Int,
val error: String,
val message: Map<String, String>?,
) {
constructor(status: Int, error: String) : this(now(), status, error, emptyMap())
constructor(status: Int, error: String, message: Map<String, String>?) : this(now(), status, error, message)
}
2. CommonException 만들기
이제 공통적인 응답 형식을 설정했으므로 런타임 오류를 처리하기 위해 사용자 정의 예외를 생성할 수 있습니다.
이를 위해 기본 Runtime Exception 클래스를 확장하는 Custom Exception 클래스를 만듭니다.
class CommonException(val exceptionCode: CommonExceptionCode) : RuntimeException()
Custom Exception 및 관련 정보를 제공하기 위하여 CommonExceptionCode라는 enum 클래스를 만듭니다.
이 클래스는 HttpStatus 및 message(예외 발생 이유)를 제공하며, 이것을 통해 보다 쉽게 관리하고 사용할 수 있습니다.
enum class CommonExceptionCode(
val status: HttpStatus,
val message: String,
) {
DUPLICATE_EMAIL(HttpStatus.BAD_REQUEST, "중복된 이메일입니다. 다시 입력해주세요."),
}
3. @RestControllerAdvice
여기서 @RestControllerAdvice를 간단하게 설명드리자면,
Restful 웹 서비스에 대한 모든 예외를 핸들링하기 위한 어노테이션입니다.
런타임 시점에 예외가 발생하면, RestControllerAdvice가 모든 예외를 가로채서 처리하게 됩니다.
이 방법을 통하여 일관성이 생기게 되며, 유지보수가 편해지게 됩니다.
@RestControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(CommonException::class)
fun handleCommonException(e: CommonException): ResponseEntity<ErrorResponse> {
val errorResponse = ErrorResponse(
e.exceptionCode.status.value(),
e.exceptionCode.message,
)
return ResponseEntity(errorResponse, e.exceptionCode.status)
}
}
@ExceptionHandler
해당 어노테이션을 통해 (CommonException::class)가 발생하면,
최종적으로 이전에 만들었던 ErrorResponse를 생성하여 반환하게 됩니다.
마무리
예외 처리가 올바르게 작동하는지 확인하기 위해 테스트 목적으로 임시 컨트롤러를 만들고 HTTP 요청을 할 수 있습니다.
요청
@RestController
@RequestMapping("/common/example")
class ExampleExceptionController {
@GetMapping("/exception/duplicate")
fun returnDuplicateException(): CommonException = throw CommonException(CommonExceptionCode.DUPLICATE_EMAIL)
}
결과

다음 글에서는 모든 요청 DTO에 대한 유효성 검사 응답을 표준화 해보겠습니다.
이것을 통해 모든 요청에 대해 유효성을 검증할 수 있고, 오류 발생을 방지할 수 있습니다!
'토이프로젝트' 카테고리의 다른 글
| 토이프로젝트 하면서 oh-my-opencode + Codex 써본 이야기 (0) | 2026.02.22 |
|---|---|
| 개발 권태기를 벗어나기 위한 토이프로젝트 [2] – 백엔드 세팅부터 실행까지 (1) | 2026.01.25 |
| 개발 권태기를 벗어나기 위해 시작하는 프로젝트 [1] (0) | 2025.12.28 |
| [졸업 작품] 연령 별 제품 추천 SQL (0) | 2022.09.12 |
| [moyeo] Spring + Redis 인증 번호 구현하기 (0) | 2022.08.20 |