From pg-backend-skills
IROVEN 백엔드 에러 핸들링 및 예외 처리 표준. Use when handling errors, creating custom exceptions, implementing GlobalExceptionHandler, or writing logging code.
How this skill is triggered — by the user, by Claude, or both
Slash command
/pg-backend-skills:error-handlingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
IROVEN 백엔드 팀의 에러 핸들링, 예외 처리, 로깅 표준을 정의합니다.
IROVEN 백엔드 팀의 에러 핸들링, 예외 처리, 로깅 표준을 정의합니다.
tools.jackson 패키지, unchecked exception)RestClient (RestTemplate deprecated)모든 응답 코드는 StatusCode enum으로 중앙 관리:
package com.pg.{servicename}.ex;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* 응답 상태 코드 관리 Enum
*/
@Getter
@RequiredArgsConstructor
public enum StatusCode {
// HTTP 표준 코드
SUCCESS(200, "성공"),
CREATED(201, "생성됨"),
BAD_REQUEST(400, "잘못된 요청"),
UNAUTHORIZED(401, "인증되지 않음"),
FORBIDDEN(403, "권한 없음"),
NOT_FOUND(404, "리소스를 찾을 수 없음"),
INTERNAL_SERVER_ERROR(500, "서버 내부 오류"),
// 도메인별 커스텀 코드 (1000번대)
USER_NOT_FOUND(1001, "사용자를 찾을 수 없음"),
FILE_UPLOAD_FAILED(1013, "파일 업로드 실패"),
FILE_NOT_FOUND(1026, "파일을 찾을 수 없음"),
ORDER_NOT_FOUND(1028, "주문을 찾을 수 없음"),
CONCURRENT_MODIFICATION(1038, "동시 수정 충돌"),
;
private final int code;
private final String message;
}
규칙:
ex/
├── GlobalExceptionHandler.java
├── StatusCode.java
├── file/
│ ├── FileNotFoundException.java
│ └── FileUploadException.java
├── payment/
│ └── PaymentException.java
└── settlement/
└── SettlementException.java
package com.pg.{servicename}.ex.file;
/**
* 파일을 찾을 수 없을 때 발생하는 예외
*/
public class FileNotFoundException extends RuntimeException {
public FileNotFoundException(String message) {
super(message);
}
public FileNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
핵심 규칙:
RuntimeException 상속ex/{domain}/)package com.pg.{servicename}.ex;
import com.pg.{servicename}.common.ResponseData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* Bean Validation 예외 처리
* Request DTO의 @Valid 검증 실패 시 발생
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ResponseData<Object>> handleMethodArgumentNotValidException(
MethodArgumentNotValidException ex) {
log.warn("MethodArgumentNotValidException", ex);
List<String> errorMessages = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.toList();
String errorMessage = errorMessages.isEmpty()
? "유효성 검사 오류가 발생했습니다."
: String.join(", ", errorMessages);
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ResponseData.of(StatusCode.BAD_REQUEST.getCode(), errorMessage, null));
}
/**
* 파일 업로드 예외 처리
*/
@ExceptionHandler(FileUploadException.class)
public ResponseEntity<ResponseData<Object>> handleFileUploadException(FileUploadException ex) {
log.warn("FileUploadException: {}", ex.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ResponseData.of(StatusCode.FILE_UPLOAD_FAILED.getCode(), ex.getMessage(), null));
}
/**
* 파일 크기 초과 예외 처리
*/
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ResponseEntity<ResponseData<Object>> handleMaxUploadSizeExceededException(
MaxUploadSizeExceededException ex) {
log.warn("MaxUploadSizeExceededException: {}", ex.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ResponseData.of(StatusCode.BAD_REQUEST.getCode(),
"파일 크기가 허용 범위를 초과했습니다.", null));
}
/**
* 예상하지 못한 예외 처리 (catch-all)
* 반드시 가장 마지막에 위치
*/
@ExceptionHandler(Exception.class)
public ResponseEntity<ResponseData<Object>> handleGenericException(Exception ex) {
log.error("Unexpected exception occurred", ex);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ResponseData.of(StatusCode.INTERNAL_SERVER_ERROR.getCode(),
"서버 내부 오류가 발생했습니다. 관리자에게 문의하세요.", null));
}
}
핵심 규칙:
@ExceptionHandler에 Javadoc 주석 필수log.warn(), 시스템 예외: log.error()ResponseData.of(statusCode, message, null) 형식@Transactional(readOnly = true)
public FileInfo getFileInfo(Long fileId) {
return fileInfoRepository.findById(fileId)
.orElseThrow(() -> new FileNotFoundException("파일을 찾을 수 없습니다. fileId=" + fileId));
}
@Transactional
public FileInfo uploadFile(MultipartFile file, String folder) {
String objectName = UUID.randomUUID() + "_" + file.getOriginalFilename();
// 1. 스토리지 업로드
String filePath = storageService.upload(file, objectName);
try {
// 2. DB 저장
FileInfo fileInfo = FileInfo.of(file, objectName, filePath);
return fileInfoRepository.save(fileInfo);
} catch (Exception e) {
// 3. DB 실패 시 스토리지 파일 삭제 (보상 처리)
log.error("DB 저장 실패, 스토리지 파일 삭제: {}", objectName, e);
storageService.delete(objectName);
throw new FileUploadException("파일 업로드 중 오류가 발생했습니다.", e);
}
}
| 레벨 | 용도 | 예시 |
|---|---|---|
DEBUG | 개발 디버깅 | log.debug("Finding user by id: {}", id) |
INFO | 정상 비즈니스 플로우 | log.info("주문 생성 완료: orderId={}", orderId) |
WARN | 비즈니스 예외 (예상된 에러) | log.warn("사용자 없음: userId={}", userId) |
ERROR | 시스템 예외 (예상치 못한 에러) | log.error("DB 저장 실패", ex) |
// ✅ 권장: 구조화된 정보
log.info("주문 생성 요청: userId={}, amount={}", userId, request.getTotalAmount());
log.warn("FileUploadException: {}", ex.getMessage());
log.error("Unexpected exception occurred", ex); // 스택트레이스 포함
// ❌ 피하기: 문자열 연결
log.info("User " + userId + " created order");
핵심 규칙:
{} 사용 (문자열 연결 금지)ERROR 레벨에서는 Exception 객체를 마지막 인자로 전달 (스택트레이스 출력)ex/{domain}/ 패키지에 위치하는가?RuntimeException을 상속하는가?@ExceptionHandler에 Javadoc이 있는가?log.warn(), 시스템 예외는 log.error()인가?{}를 사용하는가?npx claudepluginhub seonhyeokjun/pg-ai-skills --plugin pg-backend-skillsImplements standardized API error handling with RFC 7807 responses, typed error classes, middleware, and monitoring. Use for consistent HTTP errors across endpoints.
Standardizes error handling across frontend and backend layers with exception hierarchy, error categories, response formats, and boundary patterns.
Implements error handling patterns: custom error hierarchies, structured logging, retry strategies, circuit breakers, and graceful degradation. Includes Express global error handler and anti-patterns checklist.