|

Spring AI는 무엇을 해결하려는 걸까: Spring Boot에서 LLM 앱을 만들 때 달라지는 점

Spring AI 글 대표 이미지
Spring AI가 Spring Boot에서 LLM 앱을 만들 때 제공하는 ChatClient, prompt template, vector store, tool calling의 역할을 정리합니다. 기존 service 계층과 AI 호출 경계를 나눠 설명합니다.

Spring AI는 Spring Boot 애플리케이션에서 LLM 기능을 더 일관된 방식으로 붙이기 위한 프로젝트입니다. 단순히 API 호출 wrapper를 하나 제공하는 것이 아니라, chat, prompt, vector store, tool calling 같은 요소를 Spring 개발 흐름에 맞춰 다루게 해줍니다.

중요한 점은 Spring AI가 도메인 설계를 대신해주는 것이 아니라, AI 호출을 Spring 서비스 구조 안에 넣을 때 필요한 공통 부품을 제공한다는 것입니다.

Spring AI 역할 정리 카드
Spring AI는 Spring Boot에서 ChatClient, prompt, vector store, tool calling을 일관된 추상화로 다루게 해준다.

Spring AI는 왜 등장했을까

LLM 기능은 단순 HTTP 호출로도 붙일 수 있습니다. 하지만 실제 서비스에서는 prompt template, 모델 선택, 응답 파싱, RAG 검색, tool calling, retry, 관측 로그가 함께 필요해집니다.

이 요소들을 각 팀이 제각각 만들면 Spring 애플리케이션 안에서 테스트와 설정, 운영 방식이 흩어집니다. Spring AI는 이 공통 문제를 Spring 스타일의 API와 설정으로 묶으려는 접근입니다.

ChatClient는 무엇을 바꾸는가

ChatClient는 모델 호출을 fluent API로 구성하게 해줍니다. prompt, user message, system message, 응답 타입 같은 요소를 한 흐름에서 다룰 수 있습니다.

String answer = chatClient.prompt()
    .system("You are a helpful coding assistant.")
    .user("Explain JWT authentication in Spring Security.")
    .call()
    .content();

prompt template은 왜 중요할까

운영 서비스에서 prompt는 문자열 하나가 아니라 버전 관리되는 정책에 가깝습니다. 사용자 입력, 도메인 규칙, 출력 형식, 금지사항을 안정적으로 합쳐야 합니다.

prompt template을 명시적으로 다루면 테스트와 리뷰가 쉬워지고, 나중에 모델을 바꿔도 호출부 전체가 흔들리는 일을 줄일 수 있습니다.

VectorStore는 RAG 경계를 만든다

RAG를 붙이면 문서 저장, embedding, similarity search, metadata filter가 필요합니다. Spring AI의 VectorStore 추상화는 여러 vector database와의 연결을 일관된 방식으로 다루는 데 도움을 줍니다.

List<Document> results = vectorStore.similaritySearch(
    SearchRequest.query("Spring Security JWT")
        .withTopK(5)
);

tool calling은 service 경계를 조심해야 한다

tool calling은 모델이 외부 함수나 도구를 호출하게 하는 기능입니다. 다만 모든 service method를 tool로 공개하면 권한과 감사 추적이 흐려질 수 있습니다.

  • 읽기 작업과 쓰기 작업을 나눈다
  • 위험한 작업은 사람 승인 또는 별도 정책을 둔다
  • tool 입력과 출력 schema를 명확히 한다
  • 도메인 service를 그대로 노출하지 말고 AI용 facade를 둔다

기존 Spring service와의 경계

AI 호출은 application service 안에서 하나의 외부 의존성처럼 다루는 편이 안전합니다. 도메인 로직은 여전히 deterministic하게 유지하고, LLM은 분류, 초안 생성, 검색 보조처럼 불확실성을 감당할 수 있는 경계에 둡니다.

실무 적용 순서

  1. LLM이 맡을 업무와 맡지 않을 업무를 먼저 나눈다
  2. ChatClient 호출을 AI client 또는 adapter로 감싼다
  3. prompt template과 출력 형식을 테스트 가능한 파일/객체로 관리한다
  4. RAG가 필요하면 VectorStore와 metadata filter를 먼저 설계한다
  5. tool calling은 읽기 도구부터 작게 시작한다

정리

Spring AI는 Spring Boot에서 LLM 앱을 만들 때 반복되는 연결 코드를 줄이고, AI 기능을 Spring 구조 안에서 다루게 해줍니다. 하지만 좋은 AI 기능은 여전히 도메인 경계, 권한, 평가, fallback 설계가 함께 있어야 합니다.

공식 기능은 Spring AI reference를 기준으로 확인하는 것이 좋습니다. 인증이 붙은 API 서버 흐름은 Spring Security JWT 글과 함께 보면 좋습니다.


보강: Spring AI를 붙이기 전 나눌 책임

Spring AI를 도입한다고 해서 Controller에서 바로 ChatClient를 호출하는 구조가 좋은 구조가 되는 것은 아닙니다. 일반적인 외부 API처럼 application service, AI adapter, prompt template, response validator를 나누는 편이 운영에 강합니다.

Controller
  -> Application Service
    -> AiUseCase / AiClient
      -> ChatClient
      -> PromptTemplate
      -> ResponseValidator

이 구조의 장점은 모델이나 prompt가 바뀌어도 Controller와 도메인 service가 크게 흔들리지 않는다는 점입니다.

ChatClient 호출을 어디에 둘까

ChatClient는 편리하지만, service 곳곳에서 직접 호출하면 비용 추적과 테스트가 어려워집니다. 호출부는 하나의 adapter로 감싸고, 업무 service는 그 adapter의 의도 있는 메서드를 호출하게 만드는 편이 좋습니다.

public interface ProductDescriptionGenerator {
    ProductDescription generate(Product product);
}

@Service
class SpringAiProductDescriptionGenerator
        implements ProductDescriptionGenerator {
    private final ChatClient chatClient;

    public ProductDescription generate(Product product) {
        return chatClient.prompt()
            .user(buildPrompt(product))
            .call()
            .entity(ProductDescription.class);
    }
}

prompt를 코드 리뷰 대상으로 만든다

LLM 앱에서 prompt는 비즈니스 정책과 비슷합니다. 출력 tone, 금지 표현, 안전 규칙, 실패 시 동작이 들어가기 때문입니다. 그래서 prompt를 문자열 조합으로 숨겨두면 리뷰가 어렵습니다.

  • prompt template 파일 또는 전용 클래스로 관리한다
  • prompt version을 로그에 남긴다
  • 출력 schema와 함께 테스트한다
  • 모델 변경 전후 회귀 테스트 질문 세트를 둔다

VectorStore를 붙일 때 주의할 점

Spring AI의 VectorStore 추상화는 편리하지만, RAG 품질은 추상화만으로 해결되지 않습니다. chunk 크기, metadata filter, 문서 최신성, 권한 처리가 함께 설계되어야 합니다.

SearchRequest request = SearchRequest.query(userQuestion)
    .withTopK(6)
    .withFilterExpression("tenant == 'team-a'");

List<Document> docs = vectorStore.similaritySearch(request);

tool calling을 운영 기능에 바로 붙이지 않는다

tool calling은 강력하지만, 처음부터 쓰기 작업에 연결하면 위험합니다. 계정 변경, 결제, publish, 삭제 같은 작업은 모델 판단만으로 실행하지 않도록 승인 단계를 두는 편이 안전합니다.

  1. 읽기 전용 tool부터 시작한다
  2. 입력 schema를 좁게 만든다
  3. 실행 전 권한을 코드로 확인한다
  4. 중요 작업은 사람 승인 후 실행한다
  5. tool call 결과와 실패 사유를 로그로 남긴다

테스트 전략

  • AI adapter는 fake 구현으로 application service를 테스트한다
  • prompt는 golden question set으로 회귀 테스트한다
  • schema validation 실패 케이스를 만든다
  • rate limit과 timeout을 강제로 발생시켜 fallback을 확인한다

함께보면 좋은 글