Skip to content

conventions API Convention

Seoyoung Cho edited this page Jan 15, 2026 · 4 revisions

API 컨벤션

목차

1. 공통 응답 (API Response) 컨벤션

모든 API는 CommonApiResponse로 통일된 응답 형식을 사용합니다.

응답 구조

{
  "code": "S200",
  "message": "성공",
  "data": { ... }
}

Success Code 추가 방법

SuccessCode는 global에서 통합 관리합니다.

  1. 공통 성공 코드 (SuccessCode.java)
public enum SuccessCode implements SuccessType {
	SUCCESS("S200", "성공");

	private final String code;
	private final String message;

	SuccessCode(String code, String message) {
		this.code = code;
		this.message = message;
	}

	@Override
	public String getCode() {
		return code;
	}

	@Override
	public String getMessage() {
		return message;
	}
}
  1. Controller에서 사용
@GetMapping("/{id}")
public CommonApiResponse<UserResponseDto> getUser(@PathVariable Long id) {
    UserResponseDto user = userService.getUser(id);
    return CommonApiResponse.success(SuccessCode.USER_RETRIEVED, user);
}

코드 네이밍 규칙

  • S###: 공통 성공 코드
  • CH###: Challenge 성공 코드
  • 각 도메인별로 001부터 099까지 할당

2. 예외 처리 컨벤션

도메인별 ErrorCode와 Exception을 사용하여 관리합니다.

예외 처리 구조

GlobalExceptionHandler
    ├── BaseException (커스텀 비즈니스 예외 - 도메인별 Exception의 부모 클래스)
    │       ├── UserException
    │       ├── ChallengeException
    │       ├── ProcedureException
    │       ├── UserProcedureException
    │       └── AiClientException
    ├── MethodArgumentNotValidException (@Valid 검증 실패)
    ├── ConstraintViolationException, HandlerMethodValidationException (메서드 파라미터 검증 실패 처리)
    ├── MissingRequestHeaderException (필수 요청 헤더 누락 처리)
    ├── IllegalArgumentException (입력 값 검증 실패 처리)
    ├── HttpMessageNotReadableException (JSON 파싱 실패)
    ├── ObjectOptimisticLockingFailureException (낙관적 락 충돌 처리)
    └── Exception (그 외 모든 예외)

Error Code 추가 방법

  1. 공통 에러 코드 (global/response/error/ErrorCode.java)
public enum ErrorCode implements ErrorType {
    // 공통 에러 (C001~C099)
    INVALID_INPUT("C001", "입력값이 올바르지 않습니다", 400),
    INVALID_FORMAT("C002", "데이터 형식이 올바르지 않습니다", 400),
    CONCURRENT_UPDATE("C003", "다른 사용자가 동시에 수정했습니다. 다시 시도해주세요.", 409),

    INTERNAL_SERVER_ERROR("C999", "서버 내부 오류가 발생했습니다", 500);

    private final String code;
    private final String message;
    private final int status;

    // 생성자 및 getter 생략
}
  1. 도메인별 에러 코드 (각 도메인의 exception 패키지에 위치)
// domain/user/exception/UserErrorCode.java
@Getter
@RequiredArgsConstructor
public enum UserErrorCode implements ErrorType {
    // User 도메인 에러 (U001 ~ U099)
    USER_NOT_FOUND("U001", "사용자를 찾을 수 없습니다", 404),
    INVALID_USER_NAME("U002", "유효하지 않은 사용자 이름입니다", 400),
    USER_ALREADY_EXISTS("U004", "이미 존재하는 사용자입니다", 409);

    private final String code;
    private final String message;
    private final int status;
}
  1. 도메인별 Exception 클래스
// domain/user/exception/UserException.java
public class UserException extends BaseException {

    public UserException(UserErrorCode errorCode) {
        super(errorCode);
    }
}

Service에서 예외 발생

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserService {

    private final UserRepository userRepository;

    public UserResponseDto getUser(Long id) {
        // 도메인별 Exception과 ErrorCode 사용
        User user = userRepository.findById(id)
                .orElseThrow(() -> new UserException(UserErrorCode.USER_NOT_FOUND));

        return UserResponseDto.from(user);
    }
}

새로운 도메인 에러 추가 시

  1. 도메인 패키지 내에 exception 패키지 생성
  2. XxxErrorCode enum 생성 (ErrorType 구현)
  3. XxxException 클래스 생성 (BaseException 상속)

코드 네이밍 규칙

  • C###: 공통 에러 코드 (Common)
  • U###: User 도메인
  • UP###: UserProcedure 도메인
  • P###: Procedure 도메인
  • CH###: Challenge 도메인
  • AI###: AI 도메인
  • 각 도메인별로 001부터 099까지 할당

에러 응답 형식

도메인별 에러 응답

{
  "code": "U001",
  "message": "사용자를 찾을 수 없습니다",
  "data": null
}

Validation 실패 시

{
  "code": "C001",
  "message": "입력값이 올바르지 않습니다",
  "data": {
    "email": "올바른 이메일 형식이 아닙니다",
    "name": "이름은 필수입니다"
  }
}

3. Swagger 컨벤션

Swagger를 통해 자동으로 API 문서가 생성됩니다.

Swagger 접속

  • 개발 환경: http://localhost:8080/swagger-ui/index.html
  • 배포 환경: ${SWAGGER_BASE_URL}/swagger-ui/index.html

Controller에 Swagger 어노테이션 추가

@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
@Tag(name = "User", description = "사용자 관련 API")
public class UserController {

    private final UserService userService;

    @Operation(
        summary = "사용자 조회",
        description = "사용자 ID로 사용자 정보를 조회합니다."
    )
    @ApiExceptions({UserErrorCode.class, ErrorCode.class})
    @GetMapping("/{id}")
    public CommonApiResponse<UserResponseDto> getUser(
        @Parameter(description = "사용자 ID", required = true, example = "1")
        @PathVariable Long id
    ) {
        UserResponseDto response = userService.getUser(id);
        return CommonApiResponse.success(SuccessCode.SUCCESS, response);
    }

    @Operation(
        summary = "사용자 정보 수정",
        description = "사용자의 이름 또는 나이를 수정합니다. 제공된 필드만 수정됩니다."
    )
    @ApiExceptions({UserErrorCode.class, ErrorCode.class})
    @PatchMapping("/{id}")
    public CommonApiResponse<UserResponseDto> updateUser(
        @Parameter(description = "사용자 ID", required = true, example = "1")
        @PathVariable Long id,
        @Valid @RequestBody UserUpdateRequestDto request
    ) {
        UserResponseDto response = userService.updateUser(id, request);
        return CommonApiResponse.success(SuccessCode.SUCCESS, response);
    }
}

@ApiExceptions 어노테이션:

  • 해당 API에서 발생 가능한 에러 코드들을 지정
  • Swagger 문서에 자동으로 에러 응답 예시가 생성됨
  • HTTP 상태 코드별로 그룹화되어 표시

예시:

@ApiExceptions({UserErrorCode.class, ErrorCode.class})

위 코드는 Swagger에서 다음과 같이 표시됩니다:

  • 400 Bad Request: INVALID_INPUT, INVALID_FORMAT
  • 404 Not Found: USER_NOT_FOUND
  • 500 Internal Server Error: INTERNAL_SERVER_ERROR

DTO에 Swagger 어노테이션 추가

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Schema(description = "회원 생성 요청")
public class UserRequestDto {

    @Schema(description = "회원 이름", example = "홍길동", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotBlank(message = "이름은 필수입니다")
    private String name;

    @Schema(description = "이메일 주소", example = "hong@example.com", requiredMode = Schema.RequiredMode.REQUIRED)
    @Email(message = "올바른 이메일 형식이 아닙니다")
    private String email;
}

@Getter
@Builder
@Schema(description = "회원 정보 응답")
public class UserResponseDto {

    @Schema(description = "회원 ID", example = "1")
    private Long id;

    @Schema(description = "회원 이름", example = "홍길동")
    private String name;

    @Schema(description = "이메일 주소", example = "hong@example.com")
    private String email;

    @Schema(description = "생성일시", example = "2024-01-15T10:30:00")
    private LocalDateTime createdAt;
}

Swagger 어노테이션 정리

어노테이션 사용 위치 설명
@Tag Controller 클래스 API 그룹 정의
@Operation Controller 메서드 API 설명 (summary, description)
@ApiExceptions Controller 메서드 발생 가능한 에러 코드 지정 (자동 문서화)
@Parameter 메서드 파라미터 파라미터 설명 (description, required, example)
@Schema DTO 클래스/필드 스키마 설명

Clone this wiki locally