Spring Boot에서 FastAPI와 통신하는 방법
Spring Boot로 백엔드 서버를 만들다 보면 모든 기능을 하나의 서버에서 처리할 수도 있다. 하지만 AI 기능이 들어가면 이야기가 조금 달라진다.
예를 들어 회원 관리, 로그인, 결제, 게시글, 템플릿 관리는 Spring Boot가 담당하고, RAG 검색, LLM 응답 생성, 코드 분석, 문서 요약 같은 AI 기능은 FastAPI가 담당하도록 나눌 수 있다.
Frontend
↓
Spring Boot
↓
FastAPI
이 구조에서 중요한 질문은 하나다.
Spring Boot와 FastAPI는 어떤 방식으로 통신해야 할까?
1. Spring Boot와 FastAPI를 함께 사용하는 이유
Spring Boot는 Java 기반 백엔드 서버를 만들 때 많이 사용된다.
회원, 인증, 권한, 결제, 게시글, 관리자 기능처럼 서비스의 핵심 기능을 안정적으로 처리하기 좋다.
반면 FastAPI는 Python 기반 웹 프레임워크이다.
Python은 AI, 머신러닝, 데이터 처리, RAG, LLM 관련 라이브러리와 잘 어울린다. 그래서 AI 기능을 구현할 때는 FastAPI를 따로 두는 것이 편하다.
예를 들어 다음과 같이 역할을 나눌 수 있다.
| 서버 | 주요 역할 |
|---|---|
| Spring Boot | 회원, 인증, 결제, 템플릿 관리, API Gateway 역할 |
| FastAPI | AI 응답 생성, RAG 검색, 코드 분석, 문서 요약 |
정리하면 Spring Boot와 FastAPI를 함께 사용하는 이유는 다음과 같다.
Spring Boot는 서비스의 핵심 로직을 담당하고, FastAPI는 AI 기능을 담당하도록 책임을 분리하기 위해서이다.
2. 가장 기본적인 방식은 REST API 통신이다
Spring Boot와 FastAPI가 통신하는 가장 기본적인 방법은 REST API 방식이다.
쉽게 말하면 Spring Boot가 FastAPI의 API 주소로 HTTP 요청을 보내고, FastAPI는 그 요청을 처리한 뒤 JSON 형태로 응답을 돌려준다.
예를 들어 사용자가 AI 질문을 보냈다고 하자.
사용자 질문 입력
↓
Spring Boot 서버
↓
FastAPI 서버 호출
↓
FastAPI가 AI 응답 생성
↓
Spring Boot가 결과를 받아 사용자에게 반환
이때 Spring Boot는 FastAPI의 API 주소로 요청을 보낸다.
POST http://fastapi-server/api/v1/ai/ask
요청 데이터는 보통 JSON 형태로 보낸다.
{
"question": "JWT와 세션의 차이가 뭐야?"
}
FastAPI는 이 요청을 처리하고 다시 JSON으로 응답한다.
{
"answer": "JWT는 토큰 기반 인증 방식이고, 세션은 서버에 인증 상태를 저장하는 방식입니다.",
"references": ["JWT 문서", "Spring Security 문서"]
}
정리하면 REST API 통신은 다음과 같다.
Spring Boot가 FastAPI의 API를 HTTP로 호출하고, JSON 데이터를 주고받는 방식이다.
3. Spring Boot에서 FastAPI를 호출하는 선택지
Spring Boot에서 FastAPI를 호출하려면 HTTP Client가 필요하다.
대표적인 선택지는 다음과 같다.
| 선택지 | 특징 | 추천 상황 |
|---|---|---|
| RestClient | 동기식 HTTP Client | 일반적인 요청/응답 |
| WebClient | 비동기, 논블로킹 HTTP Client | AI 스트리밍, 오래 걸리는 요청 |
| HTTP Interface | 인터페이스 기반 HTTP Client | 호출 API가 많을 때 |
| OpenFeign | 선언형 REST Client | 기존 프로젝트에서 Feign 사용 중일 때 |
| Message Queue | 비동기 작업 처리 | AI 작업이 오래 걸릴 때 |
4. RestClient
가장 먼저 추천할 수 있는 방식은 RestClient이다.
RestClient는 Spring Boot에서 외부 API를 호출할 때 사용할 수 있는 동기식 HTTP Client이다.
쉽게 말하면 다음과 같은 구조에 잘 맞는다.
Spring Boot가 FastAPI에 요청
→ FastAPI가 응답
→ Spring Boot가 결과를 받아 처리
예를 들어 AI 질문에 대한 답변을 한 번에 받아오는 기능이라면 RestClient로 충분하다.
Spring Boot 예시
package com.example.ai;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
@Component
public class FastApiClient {
private final RestClient restClient;
public FastApiClient(RestClient.Builder builder) {
this.restClient = builder
.baseUrl("http://localhost:8000")
.build();
}
public AiAnswerResponse ask(AiAnswerRequest request) {
return restClient.post()
.uri("/api/v1/ai/ask")
.body(request)
.retrieve()
.body(AiAnswerResponse.class);
}
}
요청 DTO는 다음과 같이 만들 수 있다.
package com.example.ai;
public record AiAnswerRequest(
String question
) {
}
응답 DTO는 다음과 같이 만들 수 있다.
package com.example.ai;
import java.util.List;
public record AiAnswerResponse(
String answer,
List<String> references
) {
}
FastAPI 예시
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class AiAnswerRequest(BaseModel):
question: str
class AiAnswerResponse(BaseModel):
answer: str
references: list[str]
@app.post("/api/v1/ai/ask", response_model=AiAnswerResponse)
def ask_ai(request: AiAnswerRequest) -> AiAnswerResponse:
return AiAnswerResponse(
answer=f"질문에 대한 AI 응답: {request.question}",
references=["문서 A", "문서 B"]
)
정리하면 RestClient는 다음과 같다.
Spring Boot에서 FastAPI를 가장 단순하게 호출할 수 있는 동기식 HTTP Client이다.
5. WebClient
WebClient는 비동기, 논블로킹 방식의 HTTP Client이다.
RestClient와 가장 큰 차이는 처리 방식이다.
| 구분 | RestClient | WebClient |
|---|---|---|
| 처리 방식 | 동기식 | 비동기, 논블로킹 |
| 사용 난이도 | 쉬움 | 조금 어려움 |
| 추천 상황 | 일반 API 호출 | 스트리밍, 많은 외부 API 호출 |
| AI 응답 스트리밍 | 부적합 | 적합 |
예를 들어 ChatGPT처럼 답변이 한 번에 나오는 것이 아니라 조금씩 출력되는 기능을 만들고 싶다면 WebClient가 더 적합하다.
사용자 질문
↓
Spring Boot
↓
FastAPI
↓
AI 응답을 조금씩 스트리밍
↓
Frontend에 실시간 출력
WebClient 예시는 다음과 같다.
package com.example.ai;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@Component
public class FastApiWebClient {
private final WebClient webClient;
public FastApiWebClient(WebClient.Builder builder) {
this.webClient = builder
.baseUrl("http://localhost:8000")
.build();
}
public Mono<AiAnswerResponse> ask(AiAnswerRequest request) {
return webClient.post()
.uri("/api/v1/ai/ask")
.bodyValue(request)
.retrieve()
.bodyToMono(AiAnswerResponse.class);
}
}
다만 일반적인 요청/응답만 필요하다면 처음부터 WebClient를 사용할 필요는 없다. 구조가 단순한 프로젝트에서는 RestClient가 더 읽기 쉽고 관리하기 편하다.
정리하면 WebClient는 다음과 같다.
AI 응답 스트리밍이나 비동기 처리가 필요할 때 사용하는 Spring의 HTTP Client이다.
6. HTTP Interface
FastAPI에 호출해야 하는 API가 많아지면 HTTP Interface도 고려할 수 있다.
HTTP Interface는 Java 인터페이스에 API 호출 메서드를 정의하고, Spring이 실제 HTTP 호출 객체를 만들어주는 방식이다.
예를 들어 다음과 같이 작성할 수 있다.
package com.example.ai;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.service.annotation.HttpExchange;
import org.springframework.web.service.annotation.PostExchange;
@HttpExchange("/api/v1/ai")
public interface FastApiHttpClient {
@PostExchange("/ask")
AiAnswerResponse ask(@RequestBody AiAnswerRequest request);
}
이 방식은 FastAPI에 호출할 API가 많을 때 유용하다.
예를 들어 AI 서버에 다음과 같은 API가 있다고 하자.
/api/v1/ai/ask
/api/v1/ai/summary
/api/v1/ai/code-review
/api/v1/ai/rag/search
이런 API 호출을 Service 코드 안에 전부 작성하면 코드가 지저분해질 수 있다.
이때 HTTP Interface를 사용하면 API 호출 코드를 인터페이스 중심으로 정리할 수 있다.
정리하면 HTTP Interface는 다음과 같다.
FastAPI 호출 API가 많아졌을 때, 인터페이스 기반으로 HTTP 호출 코드를 깔끔하게 관리하는 방식이다.
