Spring Boot + FastAPI 통신에서 발생한 422 Error

mseo39·2025년 9월 14일
0

TIL

목록 보기
8/10
post-thumbnail

환경

  • Spring Boot 3.5.5
  • Java 21
  • FastAPI 서버 연동 (POST /ai/templates)

1️⃣ 문제 상황

Spring Boot에서 FastAPI에 템플릿 생성 요청을 보내는 기능을 구현하던 중 FastAPI에서 422 Unprocessable Entity 오류 발생

  • ⁉️원래 돌아가는 코드였는데 갑자기 안돌아감
  • FastAPI 로그 확인:
    FastAPI 422 로그
  • 핵심 원인: HTTP 프로토콜 버전 불일치

즉, FastAPI는 HTTP/1.1 요청을 기대하는데, Spring Boot RestClient가 HTTP/2로 요청을 보내서 FastAPI가 요청을 제대로 파싱하지 못한 것


2️⃣ 원인 분석

🧐코드를 건들지도 않았는데 왜 갑자기 오류 발생했을까?
HttpComponentsClientHttpRequestFactory를 적용하니깐 왜 되는거지...?

2-1. RestClient 내부 동작

Spring의 RestClient 내부를 보면 어떤 HTTP 클라이언트 팩토리를 사용할지 조건문으로 판단하는 코드를 확인할 수 있음

🤔 근데 조건문 안에 있는 httpComponentsClientPresent, jdkClientPresent와 같은 변수는 뭐지..?


DefaultRestClientBuilder클래스 초기화 블록 부분을 보면 클래스가 로딩될 때 의존성 존재 여부를 체크해서 boolean 변수에 저장함

  • httpComponentsClientPresent → Apache HttpClient 의존성이 존재 여부
  • jdkClientPresent → JDK 내장 java.net.http.HttpClient 존재 여부

즉, 의존성에 따라 내부적으로 사용하는 HTTP 클라이언트 팩토리가 바뀌는 것..

나는 JDK 11 이상을 사용해서 java.net.http.HttpClient가 기본 제공되기 때문에 JdkClientHttpRequestFactory()가 선택된 것


2-1. HTTP 클라이언트 기본 HTTP 버전

🤔 그럼 왜 어떤건 되고 어떤건 안되는걸까? 기본 HTTP 버전을 살펴보자

1) JDK 내장 HttpClient (JdkClientHttpRequestFactory)

public JdkClientHttpRequestFactory() {
        this(HttpClient.newHttpClient());
    }

✅ 아래를 보면 기본 설정으로 HTTP/2를 우선 사용한다고 명시됨

of {@linkplain HttpClient.Version#HTTP_2 HTTP/2}, a redirection policy of

2) Apache HttpClient (HttpComponentsClientHttpRequestFactory)

public static CloseableHttpClient createSystem() {
    return HttpClientBuilder.create().useSystemProperties().build();
}
  • HttpClientBuilder.build() 내부를 보면, 요청 실행을 담당하는 HttpRequestExecutor가 생성됨
HttpRequestExecutor requestExecCopy = this.requestExec;
if (requestExecCopy == null) {
    requestExecCopy = new HttpRequestExecutor();
}
  • HttpRequestExecutor는 기본 설정으로 Http1Config.DEFAULT를 사용하고
public static final Http1Config DEFAULT = (new Builder()).build();

Builder() {
    this.version = HttpVersion.HTTP_1_1;
    this.bufferSize = 8192;
    this.chunkSizeHint = -1;
    ...
}
  • Http1Config.DEFAULT의 Builder 초기값을 보면 version = HttpVersion.HTTP_1_1로 되어있음

✅ 결론: 별도 HTTP/2 설정이 없으면 Apache HttpClient는 HTTP/1.1을 기본으로 사용


3️⃣ 증상 재현

사용한 코드 :

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;

@Configuration
public class RestClientConfig {

    @Value("${rest.ai.base-url}")
    private String baseUrl;

    @Bean
    public RestClient restClient() {
        return RestClient.create(baseUrl);
    }
}
  • httpclient5 의존성을 주석 처리하고 실행 → JDK HttpClient 사용 → HTTP/2 요청 → FastAPI 오류 발생

  • httpclient5 의존성 복원 → Apache HttpClient 사용 → HTTP/1.1 요청 → 정상 동작

4️⃣ 결론

  • 의존성에 따라 내부 HTTP 클라이언트 구현체가 선택됨
  • 사용되는 HTTP 클라이언트 구현체에 따라 기본 HTTP 버전이 다르다
    • JDK HttpClient → HTTP/2
    • Apache HttpClient → HTTP/1.1
profile
하루하루 성실하게

0개의 댓글