안녕하세요.
Spring AI에 대한 내용을 정리해보겠습니다. 🙇♂️
Spring AI는 Java/Spring 개발자가 기존 애플리케이션에 생성형 AI 기능을 쉽고 표준화된 방식으로 통합할 수 있도록 지원하는 AI 애플리케이션 프레임워크입니다. Spring의 핵심 철학인 추상화(Abstraction), 의존성 주입(DI), 자동 설정(Auto-configuration)을 AI 영역으로 확장한 것이 핵심입니다.
생성형 AI 기능: LLM, Embedding Model, Vector Database, RAG, Tool Calling, MCP 등
AI Model 추상화: OpenAI, Anthropic, Gemini, Ollama 등 다양한 벤더의 LLM이나 Embedding 모델을 일관된 API 구조로 사용할 수 있게 해줍니다. 모델이 바뀌어도 코드를 거의 수정할 필요가 없어 유연합니다.
Spring IoC/DI: AI 구성 요소들을 Spring의 Bean으로 등록하고 관리하며 의존성 주입(DI)을 통해 ChatClient나 ChatModel 등을 자유롭게 가져다 쓸 수 있습니다.
Auto Configuration: Spring Boot 기반의 자동 설정 기능을 지원하여 복잡한 초기 설정 없이 설정 파일(application.yml)에 API Key 등을 적어두는 것만으로 즉시 AI 기능을 활성화할 수 있습니다.
ChatClient: 마치 체인처럼 메서드를 연속해서 호출하는 Fluent API 방식을 제공하여 직관적이고 편리하게 LLM을 호출하고 응답을 받아올 수 있습니다.
Advisor: AI 요청을 처리할 때 대화 기억(Memory)이나 RAG 같은 부가 기능을 블록처럼 끼워 넣어 쉽게 확장할 수 있도록 돕는 기능입니다.
RAG (검색 증강 생성): 문서(Document), 텍스트 조각화, 임베딩, 벡터 저장소(VectorStore) 등을 추상화하여 사내 데이터나 문서를 AI가 참고하도록 만드는 복잡한 과정을 쉽게 구현할 수 있습니다.
Tool Calling: 자바 메서드와 LLM을 서로 연결하여 AI가 상황에 맞게 필요한 기능을 판단하고 실제 액션(데이터 조회, 외부 API 호출 등)을 수행하게 만듭니다.
MCP (Model Context Protocol): 외부 Tool이나 Resource 생태계와 표준화된 방식으로 연동할 수 있도록 지원합니다.
Spring 생태계 통합: Spring MVC/WebFlux(웹), Spring Security(보안), Spring Data(데이터베이스) 등 기존 Spring 기술들과 자연스럽게 결합됩니다.
Enterprise 친화성: 기업용 업무 시스템에 AI 기능을 이질감 없이 녹여내어 대규모 서비스에서도 안정적으로 운영할 수 있는 기반을 제공합니다.

ChatModel, EmbeddingModel, ImageModel 등을 통해 다양한 AI 모델을 추상화합니다.Prompt, Message(System/User/Assistant), PromptTemplate을 제공하여 체계적인 프롬프트 관리가 가능합니다.BeanOutputConverter 등을 활용해 비정형 LLM 응답을 Java 객체로 곧바로 변환할 수 있습니다.DocumentReader, Splitter, VectorStore, Advisor를 통해 RAG를 쉽게 구현하고, Micrometer를 기반으로 트레이싱 및 메트릭을 수집합니다.Spring AI 기반 에이전트(Agent)는 사용자의 단순한 질문에 텍스트로 답하는 것을 넘어 복잡한 목표를 스스로 추론(Reasoning)하고 시스템을 조작해 행동(Action)까지 수행하는 '일하는 AI 시스템'을 의미함
예시 시나리오로 보는 에이전트의 동작 방식
Spring AI 에이전트의 4가지 핵심 구성 요소


// 프롬프트를 직접 조립하고 응답을 파싱해야 함
Prompt prompt = new Prompt(new UserMessage("오늘 날씨 어때?"));
ChatResponse response = chatModel.call(prompt);
String result = response.getResult().getOutput().getContent();
/ai 요청 처리):chatClient.prompt(): 대화를 시작합니다..user(userInput): 사용자의 입력(질문)을 전달합니다..call(): 백그라운드에서 LLM AI를 호출합니다..content(): 결과 중에서 순수 텍스트 내용(String)만 깔끔하게 추출합니다.// 가독성이 높고 직관적인 체이닝(Chaining) 방식 사용
String result = chatClient
.prompt()
.user("Spring AI란 무엇인가?")
.call()
.content();
| 구분 | ChatModel | ChatClient |
|---|---|---|
| 추상화 수준 | 저수준 (Low-level API) | 고수준 (High-level Fluent API) |
| 주요 용도 | AI 모델과의 기본 통신 및 데이터 규격 변환 | 애플리케이션 개발 시 프롬프트 작성, 옵션 설정, 부가 기능(Advisor/Tool) 조합 |
| 코드 스타일 | 객체를 직접 생성하고 응답 파싱 과정이 다소 길음 (new Prompt(...) → chatModel.call(...) → getResult()) | 메서드 체이닝으로 간결하고 직관적 (chatClient.prompt().user(...).call().content()) |

messages: LLM과 주고받을 대화 메시지들의 리스트(List<Message>).options: 모델 이름, 창의성 조절을 위한 temperature, 최대 토큰 수(maxTokens) 등 모델 호출 시 필요한 설정값(ChatOptions).// ChatModel에 전달하는 모델 요청 객체
public final class Prompt implements ModelRequest<List<Message>> {
// LLM에 전달할 Message 목록
private final List<Message> messages;
// 모델 호출 옵션 (model, temperature, maxTokens 등)
private final ChatOptions options;
}
SystemMessageUserMessageAssistantMessageToolResponseMessage{"location": "분당센터", "distance": "1.2km"} (AI가 요청한 주변 AS 센터 조회 자바 메서드의 실행 결과 데이터를 다시 AI에게 전달)1. 동기 방식
의미: 웹 브라우저나 클라이언트가 요청을 보냈을 때, AI가 답변을 다 완성할 때까지 기다렸다가 한 번에 통째로 텍스트 응답을 받는 방식입니다.
@GetMapping("/ai")
public String generation(@RequestParam String userInput) {
//The Fluent API in Action
return this.chatClient.prompt()
.user(userInput)
.call()
.content()
}
코드 흐름:
chatClient.prompt(): 대화 요청을 시작합니다..user(userInput): 사용자가 입력한 질문(문자열)을 담습니다..call(): 백그라운드에서 AI 모델을 동기 방식으로 호출합니다..content(): 결과 응답 중 순수 텍스트 내용(String)만 추출하여 반환합니다.2. 비동기/스트리밍 방식
의미: ChatGPT처럼 AI가 답변을 한 글자씩 실시간으로 줄줄이 만들어 내는(타이핑 효과) 스트리밍 응답 방식입니다. 스프링의 리액티브(Reactive) 기술인 Flux<String>을 사용합니다.
@GetMapping(value = "/ai", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestParam String userInput) {
return chatClient.prompt()
.user(userInput)
.stream()
.content();
}
코드 흐름:
Flux<String>이며, 미디어 타입도 TEXT_EVENT_STREAM_VALUE(SSE)를 사용합니다..stream(): 동기 방식의 .call() 대신 스트리밍 호출 방식을 지정합니다..content(): 토큰(단어 조각) 단위로 생성되는 텍스트를 실시간 연속 데이터 흐름(Flux)으로 내려보냅니다.@Bean 설정을 통해, OpenAI를 바라보는 chatClient와 Anthropic(Claude)을 바라보는 chatClient를 각각 따로 만들어 하나의 프로젝트 안에서 여러 모델을 동시에 골라 쓸 수 있도록 구성합니다.// 기본 설정이 적용된 ChatClient 생성 코드
@Configuration
public class ChatClientConfig {
@Bean
public ChatClient chatClient(ChatClient.Builder chatClientBuilder,
ChatMemory chatMemory,
VectorStore vectorStore) {
return chatClientBuilder
.defaultSystem("당신은 사내 기술지원 AI Agent입니다.")
.defaultAdvisors(
// 대화 기억(Memory) 기능을 기본 장착
MessageChatMemoryAdvisor.builder(chatMemory).build(),
// 사내 문서 검색(RAG)을 위한 벡터 스토어 어드바이저 기본 장착
QuestionAnswerAdvisor.builder(vectorStore).build()
)
.build();
}
}
// Multi-Model 환경 설정 코드 (OpenAI 및 Claude 분리)
@Configuration
public class MultiModelConfig {
// OpenAI 전용 ChatClient 설정
@Bean
public ChatClient openAiChatClient(OpenAiChatModel openAiChatModel) {
return ChatClient.builder(openAiChatModel)
.defaultSystem("당신은 OpenAI 기반 AI입니다.")
.build();
}
// Anthropic(Claude) 전용 ChatClient 설정
@Bean
public ChatClient claudeChatClient(AnthropicChatModel anthropicChatModel) {
return ChatClient.builder(anthropicChatModel)
.defaultSystem("당신은 Claude 기반 AI입니다.")
.build();
}
}