개요

Spring AI는 스프링 생태계에서 대규모 언어 모델(LLM)을 쉽게 통합할 수 있도록 설계된 프로젝트입니다. OpenAI, Anthropic Claude, Google Gemini, Ollama 등 다양한 AI 프로바이더를 통일된 API로 접근할 수 있으며, 2024년부터 빠르게 발전하여 2025년에는 1.0 GA 릴리스에 도달했습니다. 기존 스프링 개발자가 익숙한 패턴으로 AI 기능을 애플리케이션에 녹여낼 수 있다는 점이 가장 큰 장점입니다.

핵심 개념

ChatClient는 Spring AI의 중심 추상화입니다. RestTemplate이 HTTP 통신을 추상화하듯, ChatClient는 다양한 LLM과의 대화를 추상화합니다. 프로바이더를 교체하더라도 비즈니스 코드를 수정할 필요가 없습니다.

Prompt Template 기능은 스프링의 MessageSource와 유사한 방식으로 프롬프트를 외부화할 수 있게 합니다. 프롬프트를 코드에서 분리하여 관리할 수 있어 유지보수성이 크게 향상됩니다.

RAG(Retrieval Augmented Generation) 지원도 내장되어 있습니다. VectorStore 추상화를 통해 PGVector, Chroma, Milvus 등의 벡터 데이터베이스와 연동하여 도메인 특화 AI 응답을 구현할 수 있습니다.

Function Calling 기능을 통해 LLM이 스프링 빈의 메서드를 직접 호출할 수 있습니다. 이는 AI 에이전트 패턴 구현에 핵심적인 역할을 합니다.

실전 예제

Spring AI 의존성을 추가하고 기본적인 챗봇을 구현해봅시다.

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
@RestController
@RequestMapping("/api/chat")
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder
            .defaultSystem("당신은 Spring 전문 기술 어시스턴트입니다.")
            .build();
    }

    @PostMapping
    public String chat(@RequestBody String userMessage) {
        return chatClient.prompt()
            .user(userMessage)
            .call()
            .content();
    }

    @GetMapping("/stream")
    public Flux<String> chatStream(@RequestParam String message) {
        return chatClient.prompt()
            .user(message)
            .stream()
            .content();
    }
}

RAG 패턴으로 문서 기반 질의응답을 구현합니다.

@Service
public class DocumentQaService {

    private final ChatClient chatClient;
    private final VectorStore vectorStore;

    public DocumentQaService(ChatClient.Builder builder,
                              VectorStore vectorStore) {
        this.chatClient = builder.build();
        this.vectorStore = vectorStore;
    }

    public String askAboutDocuments(String question) {
        List<Document> similar = vectorStore.similaritySearch(
            SearchRequest.query(question).withTopK(5)
        );

        String context = similar.stream()
            .map(Document::getContent)
            .collect(Collectors.joining("\n"));

        return chatClient.prompt()
            .system("다음 문서를 기반으로 답변하세요:\n" + context)
            .user(question)
            .call()
            .content();
    }
}

활용 팁

  • 프로바이더 전환: application.yml에서 spring.ai.openaispring.ai.anthropic으로 변경하면 Claude로 즉시 전환됩니다.
  • 비용 관리: ChatOptions에서 maxTokens를 제한하고, 캐싱 레이어를 추가하여 API 비용을 절감하세요.
  • 에러 핸들링: Rate Limit이나 타임아웃에 대비하여 Spring Retry와 함께 사용하면 안정성이 높아집니다.
  • 로컬 개발: Ollama와 연동하면 로컬에서 무료로 LLM 테스트가 가능합니다.
  • Observability: Micrometer 연동으로 토큰 사용량, 응답 시간 등을 모니터링할 수 있습니다.

마무리

Spring AI는 스프링 개발자가 AI를 도입하는 가장 자연스러운 경로입니다. 스프링의 철학인 추상화와 느슨한 결합을 AI 영역에도 그대로 적용하여, 프로바이더 종속 없이 AI 기능을 구현할 수 있습니다. RAG, Function Calling, 멀티모달 지원까지 빠르게 발전하고 있으므로, 지금이 도입을 검토하기 좋은 시점입니다.