TIL/[TIL]

[TIL]Spring Boot 인터셉터 등록, REST API 응답 설계, Validation, Swagger

namerong 2026. 6. 2. 15:51

1. 학습 주제

기존에 만든 인터셉터를 실제 요청 경로에 등록하는 방법과, REST API에서 상태 코드와 응답 body를 명확하게 설계하는 방법을 학습했다.

추가로 Bean Validation을 이용한 요청 값 검증, 검증 실패 시 전역 예외 응답 처리, Swagger를 이용한 API 문서화까지 다뤘다.

핵심은 다음과 같다.

  • WebMvcConfigurer로 인터셉터 적용 경로와 실행 순서 등록
  • preHandle()에서 Controller 실행 전 요청 검사
  • ResponseEntity로 HTTP 상태 코드, header, body 직접 제어
  • ResponseMessage로 성공 응답 구조 통일
  • @Valid와 Validation 어노테이션으로 요청 DTO 검증
  • Swagger 어노테이션으로 API 설명 추가

2. POST /api/v1/admin/menus에는 어떤 인터셉터가 적용되는가

요청:

POST /api/v1/admin/menus

이 요청에는 두 개의 인터셉터가 모두 적용된다.

1. StopwatchInterceptor
2. ApiKeyInterceptor

이유는 WebConfiguration.java에 이렇게 등록되어 있기 때문이다.

registry.addInterceptor(stopwatchInterceptor)
        .addPathPatterns("/api/v1/**")
        .order(1);

registry.addInterceptor(apiKeyInterceptor)
        .addPathPatterns("/api/v1/admin/**")
        .order(2);

/api/v1/admin/menus는 /api/v1/**에도 해당하고, /api/v1/admin/**에도 해당한다.

따라서 실행 순서는 다음과 같다.

StopwatchInterceptor preHandle()
↓
ApiKeyInterceptor preHandle()
↓
Controller 실행
↓
StopwatchInterceptor afterCompletion()

order(1)이 order(2)보다 먼저 실행된다.


3. preHandle()이란 무엇인가

preHandle()은 Controller가 실행되기 전에 호출되는 인터셉터 메서드이다.

ApiKeyInterceptor.java에서는 관리자 API 요청 전에 API Key를 검사한다.

String apiKey = request.getHeader("X-API-KEY");

키가 맞으면:

return true;

요청이 계속 진행되고 Controller가 실행된다.

키가 없거나 틀리면:

return false;

요청 흐름이 중단되고 Controller는 실행되지 않는다.

즉 preHandle()은 Controller 앞에서 요청을 통과시킬지 막을지 결정할 수 있다.


4. Filter, Interceptor, AOP 차이

Filter

Filter는 Servlet 스펙이다.

Spring MVC에 들어오기 전 요청을 처리한다.
DispatcherServlet 앞단에서 동작한다.

흐름으로 보면:

Client
↓
Filter
↓
DispatcherServlet
↓
Interceptor
↓
Controller

Filter는 Spring MVC보다 더 앞에서 동작하기 때문에 인코딩, 보안, CORS, 인증 토큰 검사 같은 전역 요청 처리에 자주 사용된다.


5. Interceptor

Interceptor는 Spring MVC 기능이다.

Controller 실행 전후 요청을 처리한다.
DispatcherServlet이 Controller를 찾은 뒤,
Controller 호출 전후에 동작한다.

이번 코드의 예시는 다음과 같다.

  • StopwatchInterceptor: 요청 처리 시간 측정
  • ApiKeyInterceptor: 관리자 API Key 검사

Interceptor는 웹 요청 흐름에 밀접하다.


6. AOP

AOP는 메서드 실행 전후 공통 로직 처리에 사용된다.

Service, Repository 등 다양한 Spring Bean 메서드에 적용 가능
HTTP 요청이 아니어도 Spring Bean 메서드 실행 흐름에 적용 가능

즉 Interceptor는 웹 요청 중심이고, AOP는 Bean 메서드 실행 중심이다.

예를 들어 Service 메서드 실행 시간 측정, 트랜잭션, 로깅 같은 로직은 AOP와 잘 어울린다.


7. HTTP 상태 코드 정리

200 OK

조회 성공
응답 body 있음

예:

return ResponseEntity.ok().body(responseMessage);

회원 목록 조회, 단건 조회처럼 데이터를 내려줄 때 사용한다.


8. 201 Created

새 리소스 생성 성공
Location header로 새 리소스 위치 안내 가능

ResponseEntityTestController.java에서는 회원 등록 후 이렇게 응답한다.

return ResponseEntity
        .created(URI.create("/api/v1/response/users" + newUser.getNo()))
        .build();

created()는 HTTP 상태 코드를 201 Created로 만든다.
응답 body는 없지만, header에 새로 생성된 리소스 위치를 담을 수 있다.


9. 204 No Content

요청 처리 성공
응답 body 없음
수정/삭제 성공에 자주 사용

수정과 삭제는 처리 결과로 데이터를 꼭 내려주지 않아도 되는 경우가 많다.

return ResponseEntity
        .noContent()
        .build();

이렇게 하면 204 No Content 응답이 나간다.


10. ResponseEntity란 무엇인가

ResponseEntity는 Spring에서 HTTP 응답을 직접 구성할 수 있게 해주는 객체이다.

제어할 수 있는 것:

HTTP status
HTTP headers
HTTP body

예:

return ResponseEntity
        .ok()
        .headers(headers)
        .body(responseMessage);

즉 단순히 객체만 반환하는 것보다, API 응답을 더 명확하게 설계할 수 있다.


11. ResponseMessage란 무엇인가

ResponseMessage.java는 성공 응답 body 구조를 일정하게 맞추기 위한 DTO이다.

private int httpStatus;
private String message;
private Map<String, Object> results;

예상 응답 구조는 이런 식이다.

{
  "httpStatus": 200,
  "message": "조회 성공",
  "results": {
    "users": []
  }
}

이렇게 응답 구조를 통일하면 프론트엔드가 응답을 예측하기 쉬워진다.


12. Bean Validation

section02.valid에서는 요청 DTO에 검증 규칙을 붙이는 방법을 학습했다.

UserDTO.java

@NotNull(message = "아이디는 반드시 입력되어야 합니다.")
@NotBlank(message = "아이디는 공백일 수 없습니다.")
private String id;

@Length(max = 10, message = "비밀번호는 길이 10을 초과할 수 없습니다.")
private String pwd;

@Size(max = 10, message = "이름은 길이 10을 초과할 수 없습니다.")
private String name;

@Past(message = "가입일은 현재보다 과거 날짜가 입력 되어야 합니다.")
private Date enrollDate;

검증 실행은 Controller에서 @Valid로 한다.

public ResponseEntity<Void> registUser(@Valid @RequestBody UserDTO user)

@Valid는 @RequestBody로 만들어진 DTO가 검증 규칙을 지키는지 확인한다.
검증 실패 시 MethodArgumentNotValidException이 발생한다.


13. Validation 예외 처리

ExceptionController.java는 전역 예외 처리 클래스이다.

@RestControllerAdvice
public class ExceptionController {
}

검증 실패 예외는 여기서 처리한다.

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> methodValidException(MethodArgumentNotValidException e)

검증 실패 종류에 따라 에러 코드를 다르게 만든다.

NotNull  → ERROR_CODE_0001
NotBlank → ERROR_CODE_0002
Size     → ERROR_CODE_0003

실패 응답은 ErrorResponse.java 구조로 내려간다.

private String code;
private String description;
private String detail;

즉 에러도 성공 응답처럼 일정한 구조로 관리한다.


14. Swagger

section03.swagger에서는 API 문서화를 다뤘다.

SwaggerConfig.java

@Configuration
public class SwaggerConfig {

    @Bean
    public OpenAPI openAPI() {
        return new OpenAPI().info(swaggerInfo());
    }
}

Swagger 문서 기본 정보를 설정한다.

.title("Ohgiraffers API")
.description("SpringBoot Swagger 연동 테스트")
.version("1.0.0")

Controller에서는 API 설명을 어노테이션으로 붙인다.

@Tag(name="user 관련 api", description = "회원 조회, 등록, 수정, 삭제 API")
@Operation(summary = "회원 목록 조회/검색")
@ApiResponse(responseCode = "200", description = "회원 목록 조회 성공")

즉 Swagger는 API를 직접 실행해보고 문서로 확인할 수 있게 해주는 도구이다.


15. 전체 흐름 정리

260602의 전체 흐름은 이렇게 볼 수 있다.

요청 들어옴
↓
Filter는 DispatcherServlet 앞단에서 동작 가능
↓
DispatcherServlet이 Controller 찾음
↓
Interceptor preHandle 실행
↓
Controller 실행
↓
ResponseEntity로 상태 코드/header/body 구성
↓
Interceptor afterCompletion 실행
↓
응답 반환

그리고 Controller 내부에서는:

@RequestBody로 JSON 받기
↓
@Valid로 DTO 검증
↓
검증 실패 시 전역 예외 처리
↓
성공 시 ResponseMessage 또는 상태 코드로 응답

16. 핵심 정리

  1. POST /api/v1/admin/menus에는 StopwatchInterceptor와 ApiKeyInterceptor가 모두 적용된다.
  2. StopwatchInterceptor는 /api/v1/** 전체에 적용된다.
  3. ApiKeyInterceptor는 /api/v1/admin/** 경로에만 적용된다.
  4. preHandle()은 Controller 실행 전에 호출된다.
  5. preHandle()에서 return false를 하면 Controller가 실행되지 않는다.
  6. Filter는 Servlet 스펙이고 DispatcherServlet 앞단에서 동작한다.
  7. Interceptor는 Spring MVC 기능이고 Controller 실행 전후에 동작한다.
  8. AOP는 HTTP 요청 여부와 상관없이 Spring Bean 메서드 실행 전후에 적용할 수 있다.
  9. 200 OK는 조회 성공처럼 body가 있는 응답에 자주 사용한다.
  10. 201 Created는 새 리소스 생성 성공을 의미하며 Location header를 함께 줄 수 있다.
  11. 204 No Content는 수정/삭제 성공처럼 body가 없는 응답에 자주 사용한다.
  12. ResponseMessage는 성공 응답 body 구조를 일정하게 맞추기 위한 DTO이다.
  13. @Valid는 요청 DTO의 검증 규칙을 실행한다.
  14. @RestControllerAdvice와 @ExceptionHandler로 검증 실패 응답을 공통 처리할 수 있다.
  15. Swagger는 API 설명과 테스트 문서를 자동화하는 도구이다.