ํ์ ์์ ์์ฃผ ์ฐ๋ ๋ฐฑ์๋ ์คํฌ — ์์ธ ์ฒ๋ฆฌ ์ ๋๋ก ํ๊ธฐ. ์ปจํธ๋กค๋ฌ๋ง๋ค try-catch๋ฅผ ๋๋ฐฐํ๊ณ ์๋ค๋ฉด, API ์๋ฌ ์๋ต์ด ์ ๊ฐ๊ฐ์ผ ๊ฐ๋ฅ์ฑ์ด ๋๋ค. ์ ์ญ ์์ธ ์ฒ๋ฆฌ, ์ปค์คํ ์์ธ ์ค๊ณ, ํ์ค ์๋ฌ ์๋ต(ProblemDetail)๊น์ง — ์คํ๋ง์์ ์์ธ๋ฅผ ๊น๋ํ๊ฒ ๋ค๋ฃจ๋ ๋ฒ์ ์ค์ ์ฝ๋๋ก ์ ๋ฆฌํ๋ค.
๋ค์ด๊ฐ๋ฉฐ
๊ธฐ๋ฅ ๊ฐ๋ฐ์ ์ซ๊ธฐ๋ค ๋ณด๋ฉด ์์ธ ์ฒ๋ฆฌ๋ ๋ ๋ท์ ์ผ๋ก ๋ฐ๋ฆฐ๋ค. ๊ทธ๋ฌ๋ ์์ธ ์ฒ๋ฆฌ๋ API ํ์ง์ ์ข์ฐํ๋ ์คํฌ์ด๋ค. ์๋ชป ๋์ ธ์ง 500 ์๋ฌ ํ๋๊ฐ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ๋ฉ์ถ๊ฒ ํ๊ณ , ์ ๊ฐ๊ฐ์ธ ์๋ฌ ์๋ต ํ์์ด ํด๋ผ์ด์ธํธ ์ฝ๋๋ฅผ ์ง์ ๋ถํ๊ฒ ๋ง๋ ๋ค.
๊ฐ์ฅ ํํ ์ํฐํจํด์ ์ปจํธ๋กค๋ฌ๋ง๋ค try-catch๋ฅผ ๋๋ฐฐํ๋ ๊ฒ์ด๋ค. ๊ฐ์ ์ฝ๋๊ฐ ์ค๋ณต๋๊ณ , ์๋ต ํ์์ด ๋ฉ์๋๋ง๋ค ๋ฌ๋ผ์ง๊ณ , ์ด๋๊ฐ์์๋ ์ฒ๋ฆฌ๋ฅผ ๊น๋นกํ๋ค. ์ด๊ฑธ ํ ๊ณณ์ผ๋ก ๋ชจ์ผ๋ ๊ฒ ์ด ๊ธ์ ํต์ฌ์ด๋ค.
์ข์ ์์ธ ์ฒ๋ฆฌ์ ๋ชฉํ๋ ๋ ๊ฐ์ง๋ค. ํ๋, ์์ธ ์ฒ๋ฆฌ๋ฅผ ํ ๊ณณ์ ๋ชจ์ ์ค๋ณต์ ์์ค๋ค. ๋, ๋ชจ๋ ์๋ฌ๊ฐ ์ผ๊ด๋ ํ์์ผ๋ก ์๋ต๋๊ฒ ํ๋ค. ๊ทธ๋์ผ ํด๋ผ์ด์ธํธ๊ฐ ์๋ฌ๋ฅผ ์์ธก ๊ฐ๋ฅํ๊ฒ ๋ค๋ฃฐ ์ ์๋ค.
์ด ๊ธ์์๋ ํฉ์ด์ง try-catch์ ๋ฌธ์ ๋ถํฐ, ์คํ๋ง์ ์ ์ญ ์์ธ ์ฒ๋ฆฌ(@RestControllerAdvice), ๋น์ฆ๋์ค ์์ธ๋ฅผ ํ์
์ผ๋ก ์ค๊ณํ๋ ๋ฒ, ํ์ค ์๋ฌ ํฌ๋งท์ธ ProblemDetail(RFC 9457), ๊ทธ๋ฆฌ๊ณ ๊ฒ์ฆ ์๋ฌ ์ฒ๋ฆฌ๊น์ง ์ค์ ์ฝ๋๋ก ๋ณธ๋ค. ์์ ๋ Spring Boot 3.x ๊ธฐ์ค์ด๋ค.
1. ์ ์์ธ ์ฒ๋ฆฌ ์ ๋ต์ด ํ์ํ๊ฐ
๋จผ์ ์ํฐํจํด๋ถํฐ ๋ณด์. ์ปจํธ๋กค๋ฌ๋ง๋ค ์ด๋ ๊ฒ try-catch๋ก ๊ฐ์ธ๋ ์ฝ๋๋ค.
// ๋์ ์: ์ปจํธ๋กค๋ฌ๋ง๋ค try-catch — ์ค๋ณต๋๊ณ , ์๋ต ํ์๋ ์ ๊ฐ๊ฐ์ด๋ค
@GetMapping("/users/{id}")
public ResponseEntity<?> getUser(@PathVariable Long id) {
try {
User user = userService.findById(id);
return ResponseEntity.ok(user);
} catch (UserNotFoundException e) {
return ResponseEntity.status(404).body(Map.of("error", "์ฌ์ฉ์ ์์"));
} catch (Exception e) {
return ResponseEntity.status(500).body(Map.of("message", "์๋ฒ ์ค๋ฅ"));
}
}
๋ฌธ์ ๊ฐ ํ๋์ด ์๋๋ค. ์ด try-catch๊ฐ ๋ชจ๋ ์ปจํธ๋กค๋ฌ ๋ฉ์๋์ ๋ฐ๋ณต๋๊ณ , ์ด๋ค ๊ณณ์ error, ์ด๋ค ๊ณณ์ message ํค๋ฅผ ์ฐ๋ฉฐ ์๋ต ํ์์ด ์ ๊ฐ๊ฐ์ด๋ค. ์ ๋ฉ์๋์์ catch๋ฅผ ๋น ๋จ๋ฆฌ๋ฉด ๊ทธ๋๋ก 500์ด ๋
ธ์ถ๋๋ค.

ํด๋ฒ์ ์์ธ ์ฒ๋ฆฌ๋ฅผ ์ปจํธ๋กค๋ฌ์์ ๊ฑท์ด๋ด ํ ๊ณณ์ผ๋ก ๋ชจ์ผ๋ ๊ฒ์ด๋ค. ๊ทธ๋ฌ๋ฉด ์ปจํธ๋กค๋ฌ๋ ์ ์ ํ๋ฆ์๋ง ์ง์คํ๊ณ , ์์ธ๋ ๊ทธ๋ฅ ๋์ง๋ฉด ๋๋ค.
2. ์์ธ๊ฐ ๋์ ธ์ง๋ฉด ๋ฌด์จ ์ผ์ด ์ผ์ด๋๋๊ฐ
์คํ๋ง์์ ์ปจํธ๋กค๋ฌ๋ ์๋น์ค๊ฐ ์์ธ๋ฅผ ๋์ง๋ฉด, ๊ทธ ์์ธ๋ ํธ์ถ ์คํ์ ํ๊ณ ์๋ก ์ ํ๋๋ค. ์๋ฌด๋ ์ก์ง ์์ผ๋ฉด ์คํ๋ง์ ๊ธฐ๋ณธ ์ฒ๋ฆฌ๊ธฐ๊ฐ ๋ฐ์์ ์ฒ๋ฆฌํ๋๋ฐ, ์ด๋ ๋์ค๋ ๊ฒ ๊ทธ ์ ๋ช ํ Whitelabel ์๋ฌ ํ์ด์ง๋ ๋ฐ๋ฐํ ๊ธฐ๋ณธ JSON์ด๋ค.
{ "timestamp": "...", "status": 500, "error": "Internal Server Error", "path": "/api/users/42" }
์ด ๊ธฐ๋ณธ ์๋ต์ ๋ ๊ฐ์ง๊ฐ ์์ฝ๋ค. ์ด๋ค ๋น์ฆ๋์ค์ ์ด์ ๋ก ์คํจํ๋์ง ์ ์ ์๊ณ , ํ์์ ์ฐ๋ฆฌ๊ฐ ํต์ ํ ์ ์๋ค. ๊ทธ๋์ ์์ธ๋ฅผ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ์ง์ ์์ ๊ฐ๋ก์ฑ ์ํ๋ ํ์์ผ๋ก ๋ฐ๊ฟ์ฃผ๋ ์ฅ์น๊ฐ ํ์ํ๋ค. ๊ทธ๊ฒ ๋ค์์ @RestControllerAdvice๋ค.
3. ์ ์ญ ์์ธ ์ฒ๋ฆฌ — @RestControllerAdvice
@RestControllerAdvice๋ ๋ชจ๋ ์ปจํธ๋กค๋ฌ์์ ๋ฐ์ํ ์์ธ๋ฅผ ํ ๊ณณ์์ ์ฒ๋ฆฌํ๊ฒ ํด์ฃผ๋ ์คํ๋ง์ ํต์ฌ ๋๊ตฌ๋ค. @ExceptionHandler๋ก "์ด ์์ธ๋ ์ด๋ ๊ฒ ์๋ตํ๋ผ"๋ฅผ ์ ์ธํ๋ค.
@Slf4j
@RestControllerAdvice // ๋ชจ๋ ์ปจํธ๋กค๋ฌ์ ์์ธ๋ฅผ ์ฌ๊ธฐ์ ๊ฐ๋ก์ฑ๋ค
public class GlobalExceptionHandler {
// ํน์ ๋น์ฆ๋์ค ์์ธ ์ฒ๋ฆฌ
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(UserNotFoundException e) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse("USER_NOT_FOUND", e.getMessage()));
}
// ์์ํ์ง ๋ชปํ ๋ชจ๋ ์์ธ์ '์ตํ ๋ฐฉ์ด์ '
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleUnexpected(Exception e) {
log.error("์ฒ๋ฆฌ๋์ง ์์ ์์ธ ๋ฐ์", e); // 5xx๋ ๋ฐ๋์ ๋ก๊น
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse("INTERNAL_ERROR", "์๋ฒ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค"));
}
}
์ด์ ์ปจํธ๋กค๋ฌ๋ ์ด๋ ๊ฒ ๊น๋ํด์ง๋ค. ์์ธ๋ ์ก์ง ์๊ณ ๊ทธ๋ฅ ๋์ง๋ฉด ์์ ํธ๋ค๋ฌ๊ฐ ์์์ ์ฒ๋ฆฌํ๋ค.
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id); // try-catch ์์ด, ์์ธ๋ ๊ทธ๋ฅ ์ ํ
}

์์ธ๊ฐ ์๋น์ค ๊น์ ๊ณณ์์ ๋์ ธ์ ธ๋, ์คํ์ ํ๊ณ ์ฌ๋ผ์ @RestControllerAdvice๊ฐ ํ ๋ฒ์ ์ก๋๋ค. ์ฒ๋ฆฌ ๋ก์ง์ด ํ ๊ณณ์ ๋ชจ์ฌ ์ค๋ณต์ด ์ฌ๋ผ์ง๊ณ ํ์์ด ํต์ผ๋๋ค.
4. ์ปค์คํ ์์ธ ์ค๊ณ — ๋น์ฆ๋์ค ๊ท์น์ ํ์ ์ผ๋ก
์์ธ ์ข ๋ฅ๊ฐ ๋์ด๋ ๋๋ง๋ค ํธ๋ค๋ฌ๋ฅผ ํ๋์ฉ ์ถ๊ฐํ๋ฉด ๊ทธ๊ฒ๋๋ก ๋ฒ๊ฑฐ๋กญ๋ค. ๋ ๋์ ๋ฐฉ๋ฒ์ ์๋ฌ๋ฅผ ์ฝ๋๋ก ์ ์ํ๊ณ , ๊ณตํต ์์ธ ํ๋๋ก ๋ฌถ๋ ๊ฒ์ด๋ค. ๋จผ์ ์๋ฌ ์ฝ๋๋ฅผ enum์ผ๋ก ์ ์ํ๋ค.
@Getter
@RequiredArgsConstructor
public enum ErrorCode {
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค"),
DUPLICATE_EMAIL(HttpStatus.CONFLICT, "์ด๋ฏธ ์ฌ์ฉ ์ค์ธ ์ด๋ฉ์ผ์
๋๋ค"),
INVALID_INPUT(HttpStatus.BAD_REQUEST, "์๋ชป๋ ์์ฒญ์
๋๋ค");
private final HttpStatus status; // ๊ฐ ์๋ฌ๊ฐ ์ด๋ค HTTP ์ํ์ธ์ง
private final String message;
}
๊ทธ๋ฆฌ๊ณ ์ด ์ฝ๋๋ฅผ ๋ด๋ ๊ณตํต ๋น์ฆ๋์ค ์์ธ๋ฅผ ๋ง๋ ๋ค.
@Getter
public class BusinessException extends RuntimeException {
private final ErrorCode errorCode;
public BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
}
์ด์ ์๋น์ค์์๋ ์ํฉ์ ๋ง๋ ์๋ฌ ์ฝ๋๋ก ์์ธ๋ฅผ ๋์ง๊ธฐ๋ง ํ๋ฉด ๋๋ค.
public User findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));
}
ํธ๋ค๋ฌ๋ ๋จ ํ๋๋ฉด ์ถฉ๋ถํ๋ค. BusinessException ํ๋๋ง ์ฒ๋ฆฌํ๋ฉด ๋ชจ๋ ๋น์ฆ๋์ค ์์ธ๊ฐ ์ปค๋ฒ๋๋ค.
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusiness(BusinessException e) {
ErrorCode code = e.getErrorCode();
return ResponseEntity
.status(code.getStatus())
.body(new ErrorResponse(code.name(), code.getMessage()));
}
์๋ฌ์ ์ข ๋ฅ(HTTP ์ํ + ๋ฉ์์ง)๊ฐ enum ํ ๊ณณ์ ๋ชจ์ฌ ๊ด๋ฆฌ๋๋, ์ ์๋ฌ๋ฅผ ์ถ๊ฐํ ๋ enum์ ํ ์ค๋ง ๋ํ๋ฉด ๋๋ค. ์ด๋ค ์ํฉ์ ์ด๋ค HTTP ์ํ ์ฝ๋๋ฅผ ์ธ์ง๋ ์ฌ๊ธฐ์ ์ผ๊ด๋๊ฒ ์ ํด์ง๋ค.
5. ์ผ๊ด๋ ์๋ฌ ์๋ต — ProblemDetail (RFC 9457)
์๋ฌ ์๋ต ํ์์ ์ง์ ๋ง๋ค์ด๋ ๋์ง๋ง, ์ฌ์ค ํ์ค์ด ์๋ค. RFC 9457(Problem Details for HTTP APIs)์ API ์๋ฌ ์๋ต์ ๊ณตํต ํฌ๋งท์ ์ ์ํ๊ณ , Spring 6(๋ถํธ 3)๋ถํฐ ProblemDetail๋ก ๊ธฐ๋ณธ ์ง์ํ๋ค. ์ง์ ๋ง๋ ErrorResponse ๋์ ์ด๊ฑธ ์ฐ๋ฉด ํ์ค์ ๋ฐ๋ฅด๋ฉด์ ์ฝ๋๋ ์ค๋ค.
@ExceptionHandler(BusinessException.class)
public ProblemDetail handleBusiness(BusinessException e) {
ErrorCode code = e.getErrorCode();
// status์ detail๋ก ํ์ค ์๋ฌ ๋ฐ๋ ์์ฑ
ProblemDetail problem = ProblemDetail.forStatusAndDetail(
code.getStatus(), code.getMessage());
problem.setTitle(code.name());
problem.setProperty("errorCode", code.name()); // ํ์ค ์ธ ์ปค์คํ
ํ๋ ์ถ๊ฐ
return problem;
}
์๋ต์ ์ด๋ ๊ฒ, ํ์ค ํ์์ผ๋ก ๋๊ฐ๋ค. Content-Type๋ ์๋์ผ๋ก application/problem+json์ด ๋๋ค.
{
"type": "about:blank",
"title": "USER_NOT_FOUND",
"status": 404,
"detail": "์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค",
"instance": "/api/users/42",
"errorCode": "USER_NOT_FOUND"
}

type·title·status·detail·instance๊ฐ ํ์ค ํ๋์ด๊ณ , setProperty๋ก errorCode ๊ฐ์ ์ปค์คํ
ํ๋๋ฅผ ์น์ ์ ์๋ค. ํด๋ผ์ด์ธํธ๋ ์ด์ ์ด๋ค API๋ฅผ ํธ์ถํ๋ ๊ฐ์ ๊ตฌ์กฐ๋ก ์๋ฌ๋ฅผ ํ์ฑํ๋ฉด ๋๋ค. ์ฐธ๊ณ ๋ก application.properties์ spring.mvc.problemdetails.enabled=true๋ง ์ผ๋ ์คํ๋ง์ ๊ธฐ๋ณธ ์์ธ๋ค์ด ์ด ํฌ๋งท์ผ๋ก ์๋ต๋๋ค.
6. ๊ฒ์ฆ ์๋ฌ ์ฒ๋ฆฌ — @Valid
๋ ํ๋ ์์ฃผ ๋ง์ฃผ์น๋ ๊ฒ ์
๋ ฅ ๊ฒ์ฆ์ด๋ค. ์์ฒญ DTO์ ๊ฒ์ฆ ์ ๋ํ
์ด์
์ ๋ถ์ด๊ณ @Valid๋ฅผ ๊ฑธ๋ฉด, ์คํ๋ง์ด ์๋์ผ๋ก ๊ฒ์ฆํ๊ณ ์คํจ ์ ์์ธ๋ฅผ ๋์ง๋ค.
public record SignupRequest(
@NotBlank(message = "์ด๋ฆ์ ํ์์
๋๋ค")
String name,
@Email(message = "์ฌ๋ฐ๋ฅธ ์ด๋ฉ์ผ ํ์์ด ์๋๋๋ค")
String email,
@Min(value = 1, message = "๋์ด๋ 1 ์ด์์ด์ด์ผ ํฉ๋๋ค")
int age
) {}
@PostMapping("/users")
public User signup(@Valid @RequestBody SignupRequest request) {
return userService.signup(request); // ๊ฒ์ฆ ํต๊ณผํ ๊ฐ๋ง ์ฌ๊ธฐ ๋๋ฌ
}
๊ฒ์ฆ์ ์คํจํ๋ฉด MethodArgumentNotValidException์ด ๋์ ธ์ง๋ค. ์ด๊ฑธ ์ ์ญ ํธ๋ค๋ฌ์์ ์ก์, ์ด๋ค ํ๋๊ฐ ์ ํ๋ ธ๋์ง ๋ด์ ์๋ตํ๋ค.
@ExceptionHandler(MethodArgumentNotValidException.class)
public ProblemDetail handleValidation(MethodArgumentNotValidException e) {
ProblemDetail problem = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST);
problem.setTitle("VALIDATION_FAILED");
// ํ๋๋ณ ์๋ฌ ๋ฉ์์ง๋ฅผ ๋ชจ์์ ๋ด๋๋ค
Map<String, String> errors = new HashMap<>();
e.getBindingResult().getFieldErrors().forEach(err ->
errors.put(err.getField(), err.getDefaultMessage()));
problem.setProperty("errors", errors);
return problem;
}
์ด์ ์๋ชป๋ ์ ๋ ฅ์๋ "์ด๋ ํ๋๊ฐ ์ด๋ป๊ฒ ํ๋ ธ๋์ง"๊ฐ ์น์ ํ๊ฒ ์๋ต๋์ด, ํ๋ก ํธ์๋๊ฐ ํผ ์๋ฌ๋ฅผ ๋ฐ๋ก ํ์ํ ์ ์๋ค.
7. ์ค๋ฌด ํ ์ ๋ฆฌ
๋ง์ง๋ง์ผ๋ก, ์์ธ ์ฒ๋ฆฌ์์ ํํ ๋์น๋ ๊ฒ๋ค์ด๋ค.
- 4xx์ 5xx๋ฅผ ๋ช ํํ ๊ตฌ๋ถํ๋ผ. ํด๋ผ์ด์ธํธ ์๋ชป(์๋ชป๋ ์ ๋ ฅ, ์๋ ๋ฆฌ์์ค)์ 4xx, ์๋ฒ ์๋ชป(๋ฒ๊ทธ, ์ธ๋ถ ์ฐ๋ ์คํจ)์ 5xx๋ค. ์ด ๊ตฌ๋ถ์ด HTTP ์ํ ์ฝ๋๋ฅผ ์ ๋๋ก ์ฐ๋ ์ถ๋ฐ์ ์ด๋ค.
- 5xx๋ ๋ก๊น ํ๊ณ , 4xx๋ ๋๊ฐ ํ์ง ๋ง๋ผ. 5xx๋ ์ฐ๋ฆฌ๊ฐ ๊ณ ์ณ์ผ ํ ๋ฌธ์ ๋ผ ์คํ ํธ๋ ์ด์ค๋ฅผ ๋จ๊ฒจ์ผ ํ๋ค. ๋ฐ๋ฉด 4xx(์ฌ์ฉ์์ ์๋ชป๋ ์์ฒญ)๊น์ง error ๋ ๋ฒจ๋ก ๋ก๊น ํ๋ฉด ๋ก๊ทธ๊ฐ ๋ ธ์ด์ฆ๋ก ๊ฐ๋ ์ฐฌ๋ค.
- ์๋ฌ ์๋ต์ ๋ฏผ๊ฐ ์ ๋ณด๋ฅผ ๋ ธ์ถํ์ง ๋ง๋ผ. ์คํ ํธ๋ ์ด์ค, ๋ด๋ถ ๊ฒฝ๋ก, SQL ๋ฑ์ด ์๋ต ๋ฐ๋์ ์์ฌ ๋๊ฐ๋ฉด ๋ณด์ ์ทจ์ฝ์ ์ด ๋๋ค. ์์ธ ์์ธ์ ๋ก๊ทธ์๋ง ๋จ๊ธฐ๊ณ , ํด๋ผ์ด์ธํธ์๋ ์์ ํ ๋ฉ์์ง๋ง ์ค๋ค.
- ์์ธ๋ฅผ ํ๋ฆ ์ ์ด๋ก ๋จ์ฉํ์ง ๋ง๋ผ. "๊ฐ์ด ์์ผ๋ฉด ์์ธ"๊ฐ ํญ์ ์ณ์ ๊ฑด ์๋๋ค. ์ ์์ ์ผ๋ก ์์ ์ ์๋ ๋ถ์ฌ(๋น ๋ชฉ๋ก ๋ฑ)๋ ์์ธ๊ฐ ์๋๋ผ ์ ์ ๋ฐํ์ผ๋ก ๋ค๋ฃจ๋ ๊ฒ ๋ง๋ค.
@Transactional๋กค๋ฐฑ ๊ท์น์ ์ฃผ์ํ๋ผ. ์คํ๋ง์ ๊ธฐ๋ณธ์ ์ผ๋กRuntimeException์๋ง ๋กค๋ฐฑํ๋ค. ์ปค์คํ ์์ธ๋ฅผRuntimeException๊ธฐ๋ฐ์ผ๋ก ๋ง๋ ์ด์ ์ด๊ธฐ๋ ํ๋ค(ํธ๋์ญ์ ์ ํ ์ฐธ๊ณ ).
๋ง์น๋ฉฐ
์์ธ ์ฒ๋ฆฌ๋ "์๋ฌ๊ฐ ๋ฌ์ ๋ ๋์ถฉ ๋ง๋" ๋ฐฉ์ด๊ฐ ์๋๋ผ, API๊ฐ ์คํจ๋ฅผ ์ผ๊ด๋๊ณ ์์ธก ๊ฐ๋ฅํ๊ฒ ํํํ๋ ์ค๊ณ๋ค. ์ปจํธ๋กค๋ฌ์ try-catch๋ฅผ ๊ฑท์ด๋ด @RestControllerAdvice๋ก ๋ชจ์ผ๊ณ , ๋น์ฆ๋์ค ์์ธ๋ฅผ ์๋ฌ ์ฝ๋๋ก ํ์
ํํ๊ณ , ์๋ต์ ProblemDetail ํ์ค์ผ๋ก ํต์ผํ๋ฉด, ์๋ฌ ์ฒ๋ฆฌ ์ฝ๋๊ฐ ๊ทน์ ์ผ๋ก ๋จ์ํด์ง๋ฉด์ ํ์ง์ ์ฌ๋ผ๊ฐ๋ค.
ํต์ฌ๋ง ๋ค์ ์ง์ผ๋ฉด, ์์ธ๋ ๋์ง๊ณ , ์ฒ๋ฆฌ๋ ํ ๊ณณ์ ๋ชจ์ผ๊ณ , ์๋ต์ ํ์ค ํ์์ผ๋ก ํต์ผํ๋ค. ์ด ์ธ ๊ฐ์ง๋ง ์ง์ผ๋ "์ปจํธ๋กค๋ฌ๋ง๋ค try-catch"์ ์ง์ฅ์์ ๋ฒ์ด๋ ์ ์๋ค. ๊ทธ๋ฆฌ๊ณ ์ผ๊ด๋ ์๋ฌ ์๋ต์ ๋ฐฑ์๋๋ง์ ์ด๋์ด ์๋๋ผ, ๊ทธ API๋ฅผ ์ฐ๋ ํ๋ก ํธ์๋์ ๋ค๋ฅธ ์๋น์ค ๋ชจ๋์ ์ด๋์ด๋ค.
์ฐธ๊ณ ์ถ์ฒ
- Spring Framework — Error Responses (ProblemDetail, RFC 9457) — https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-ann-rest-exceptions.html
- Spring Framework — Bean Validation — https://docs.spring.io/spring-framework/reference/core/validation/beanvalidation.html
- Baeldung — Error Handling for REST with Spring — https://www.baeldung.com/exception-handling-for-rest-with-spring
- RFC 9457 — Problem Details for HTTP APIs — https://www.rfc-editor.org/rfc/rfc9457
'๐ Tech Stack > Backend' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| @Transactional ์ ๋ฆฌ (0) | 2026.06.22 |
|---|---|
| ๋ฐฐ์น ์ฒ๋ฆฌ vs ์คํธ๋ฆผ ์ฒ๋ฆฌ (1) | 2026.06.18 |
| HTTP ์ํ ์ฝ๋(Status Code) ์ ๋ฆฌ (1) | 2026.06.15 |
| ์น ์ธ์ฆ ๋ฐฉ์ ๋น๊ต (์ฟ ํค, ์ธ์ , ํ ํฐ) (1) | 2026.06.09 |
| JPA N+1 ์ฌํ๋ถํฐ Fetch Join, Batch Size๊น์ง (0) | 2026.06.03 |