본문으로 건너뛰기
The JVM Index
뒤로 가기
spring

Spring Boot 4·Framework 7 Web 변경점 총정리: 무엇이 바뀌었나 (Spring Web 다시 읽기 1)

이 글 수정

Spring Boot 3 시절, REST API에 버전을 붙이려면 어떻게 했나. @RequestMapping/v1, /v2를 손으로 박거나, 커스텀 RequestCondition을 만들거나, 헤더 라우팅을 직접 짰다. 에러 응답 포맷은 팀마다 제각각이었고, “Deprecated된 엔드포인트”를 클라이언트에 알리는 표준 방법 같은 건 없었다. 각자 사내 컨벤션으로 메웠다.

Boot 4와 Framework 7이 바꾼 건 이 “각자 메우던 자리”다. 새 장난감을 던져준 릴리스가 아니라, 그동안 우리가 임시변통으로 채우던 구멍을 RFC 표준 구현으로 메운 릴리스다. 그래서 변경 목록을 훑어보면 유독 RFC 번호가 많이 보인다. 9457, 9745, 8594, 8288, 9110. 우연이 아니라 이번 릴리스의 성격이다.

이 시리즈는 그 표준들을 하나씩, 원문 의도부터 Spring 구현 코드까지 따라가며 읽는다. 1부는 지도를 그린다.

먼저, baseline부터

표준 얘기를 하기 전에 바닥을 깔아야 한다. 코드는 그대로인데 실행 환경이 바뀌면 그게 더 무섭기 때문이다.

이 중 Web 개발자가 당장 체감하는 건 Jackson 3가 기본이 됐다는 점이다. 직렬화는 모든 REST 응답이 지나가는 길목이라, 기본 ObjectMapper가 바뀌는 건 조용한 변화가 아니다. 2부에서 따로 다룬다.

변화 지도: 네 개의 축

Boot 4·FW7의 Web 변화를 무작정 나열하면 스무 개 항목이 되지만, 묶으면 네 덩어리다.

1. Jackson 3: 기본 직렬화 엔진 교체

Framework 7은 전 스택에서 Jackson 3.x를 기본으로 쓰고, Jackson 2.x로 폴백한다. Jackson 2는 deprecated 됐다. 단순 버전 올림이 아니라 패키지 네임스페이스와 Maven 좌표가 바뀌는 메이저 전환이라, 기존 설정 코드가 그대로 안 돈다.

대표적으로 Jackson2ObjectMapperBuilder에 대응하는 Jackson 3용 빌더가 없다. Spring은 Jackson이 제공하는 JsonMapper.builder(), CBORMapper.builder()를 직접 쓰라고 안내한다. 커스텀 ObjectMapper를 빈으로 등록해 쓰던 프로젝트라면 이 지점에서 가장 먼저 막힌다.

2. 네이티브 API 버저닝

MVC와 WebFlux가 API 버저닝을 1급으로 지원한다. ApiVersionStrategy를 중심으로 path, header, query parameter, media type 네 가지 전략을 고른다. 서버는 매핑에 버전을 직접 선언하고, 클라이언트(RestClient·WebClient·HTTP Interface)는 ApiVersionInserter로 요청에 버전을 주입한다.

서두에서 말한 “손으로 /v1 박던” 그 자리가 여기로 대체된다. 3부 주제다.

3. RFC 표준 구현: 이번 릴리스의 정체성

여기가 시리즈의 무게중심이다. 흩어져 있던 HTTP 관례들이 RFC 구현으로 들어왔다.

RFC 9457을 미리 맛보면 이런 JSON이다. 어느 Spring Boot 4 서비스에서 에러가 나든 같은 모양으로 떨어진다.

{
  "type": "https://example.com/probs/out-of-credit",
  "title": "You do not have enough credit.",
  "status": 403,
  "detail": "Your current balance is 30, but that costs 50.",
  "instance": "/account/12345/msgs/abc"
}

프론트엔드 인터셉터 하나로 모든 마이크로서비스의 에러를 같은 방식으로 파싱할 수 있게 된다는 게 핵심 이득이다. 4부(Deprecation 헤더)와 5부(Problem Details)에서 RFC 원문 필드 정의까지 펼친다.

4. 선언적 클라이언트와 테스트

@HttpExchange 기반 HTTP Interface Client 구성이 1급으로 정리됐고, 비반응형 RestTestClient가 새로 들어왔다. 서버만 표준화되고 클라이언트·테스트가 따라오지 않으면 반쪽이라, 이 축이 나머지 셋을 닫아준다. 7부에서 다룬다.

왜 하필 “표준”인가

기능 하나하나는 따로 외워도 된다. 그런데 한 발 물러서면 방향이 보인다.

Spring이 자체 컨벤션으로 풀던 문제들을 IETF RFC 구현으로 바꾸는 흐름이다. 버전 deprecation을 사내 헤더로 알리던 걸 RFC 9745 Deprecation 헤더로, 제각각이던 에러 바디를 RFC 9457 ProblemDetail로. 이게 왜 중요하냐면, 표준은 상호운용성을 공짜로 준다. 내 서버가 RFC 9457로 에러를 내면, 그 포맷을 아는 모든 클라이언트 라이브러리·게이트웨이·관측 도구가 별도 약속 없이 알아듣는다. 사내 컨벤션은 사내에서만 통한다.

솔직히 이건 화려한 변화는 아니다. 신기능 데모처럼 박수가 나오진 않는다. 그런데 마이크로서비스를 여러 개 굴려본 입장에서, “팀마다 다른 에러 포맷”이 만드는 누수를 표준 하나로 막는 건 생각보다 큰 일이다.

시리즈 로드맵

표준/원리 딥다이브 7부작으로 간다. 각 편은 RFC 원문 의도 → Spring 구현 매핑 → 코드의 순서를 따른다.

  1. (이 글) 개요: 변화 지도와 baseline
  2. Jackson 3 마이그레이션: 패키지·좌표 전환, JsonMapper.builder(), Jackson2ObjectMapperBuilder 부재 대응, HttpMessageConverter 영향
  3. 네이티브 API 버저닝: ApiVersionStrategy 4개 전략, 서버 매핑 + 클라이언트 ApiVersionInserter
  4. API Deprecation을 표준 헤더로: StandardApiVersionDeprecationHandler와 RFC 9745·8594·8288
  5. RFC 9457 Problem Details: ProblemDetail·ErrorResponse, 프레임워크 전역 기본화, 검증 에러 확장
  6. HTTP 표준 정렬: RFC 9110과 HttpStatus 개편, RFC 7239 ForwardedHeaderFilter
  7. HTTP Interface Client & RestTestClient: @HttpExchange 1급 구성과 테스트

다음 글은 Jackson 3다. 기본 직렬화 엔진이 바뀐 자리부터 손에 잡히는 코드로 들어간다.

참고


이 글 수정
이 글 공유하기:

이전 글
Spring Boot 4 Jackson 3 마이그레이션: 직렬화 엔진이 바뀐 자리 (Spring Web 다시 읽기 2)
다음 글
RFC란 무엇인가: 인터넷이 합의에 도달하는 방법