Skip to content

Spring AI Component

woojin.jang edited this page Jun 30, 2026 · 3 revisions
  • ChatClient 내부에는 유저의 질문이 AI를 거쳐 최종 결과물로 나오기까지 각각의 역할을 수행하는 5가지의 핵심 컴포넌트가 유기적으로 연결되어 있다.
  • AI 대화의 시작과 끝을 관리하는 컨트롤러 역할을 한다.
  • 해당 객체의 .prompt() 메서드를 시작으로 전체 대화 과정을 제어한다.
    • Spring AI 생태계에서 AI 모델(ChatGPT, Claude 등)과 대화를 주고받는 모든 과정을 총괄하고 제어하는 최상위 핵심 인터페이스이다.
    • ChatClient는 이 복잡한 대화 파이프라인을 스프링에서 쉽게 관리하기 위해 만들어졌다.
    • 빌더 패턴(Fluent API) 스타일을 제공하여 필요한 옵션들을 직관적으로 조립 가능하다.
  • 전처리 단계
  • 유저가 보낸 질문이 AI 모델로 넘어가기 직전, 중간에 개입하여 질문을 가공하거나 필요한 배경지식(Context)을 주입한다.
  • 이전 대화 기록을 불러와 질문에 붙여주거나, Vector DB 관련 문서를 찾아와 참고 자료로 결합해주는 역할을 수행한다.
    • AI 모델과 주고받는 요청과 응답을 중간에서 가로채 데이터를 동적으로 수정, 확장, 차단할 수 있게 해주는 컴포넌트이다.
    • 스프링 웹 개발의 인터셉터, 필터, 정통 AOP와 동일한 역할을 수행한다고 볼 수 있다.
  • Chat Memory Advisors(대화 흐름 관리)
    • MessageChatMemoryAdvisor : 대화 이력을 유저/AI 메시지 구조 그대로 유지하며 프롬프트에 추가
    • PromptChatMemoryAdvisor : 과거 대화를 단순 텍스트로 뽑아서 시스템 프롬프트에 삽입
  • Question Answering Advisors(RAG 검색 증강 생성)
    • QuestionAnswerAdvisor : Vector DB 자동 연동 등의 패턴을 수행
    • RetrievalAugmentationAdvisor : 고도화된 RAG 아키텍처를 구현할 때 사용
  • Reasoning & Safety Advisors(추론 및 보안)
    • ReReadingAdvisor(RE2) : LLM의 추론 능력을 높이기 위해 입력된 질문을 한 번 더 읽게끔 프롬프트를 자동으로 보강
    • SafeGuardAdvisor : AI 모델이 해롭거나 부적절한 답변을 생성하지 않도록 사전에 방어막을 쳐준다.

Advisor의 핵심 동작 원리 : 스택(Stack) 구조

  • 실행순서 규칙 : getOrder() 메서드가 반환하는 값이 작을수록(=우선순위가 높을수록) 체인의 가장 바깥쪽(=맨 위)에 위치한다.
  • 요청(Request) 단계 : 우선순위가 높은(=Order 값이 작은) Advisor가 가장 먼저 요청을 가로채서 전처리한다.
  • 응답(Response) 단계 : 우선순위가 높은(=Order 값이 작은) Advisor가 가장 마지막에 응답을 가로채서 후처리함
  • 최종 프롬프트 생성
  • Advisors가 수집한 배경지식(Context)과 유저가 처음 입력한 내용을 결합하여, AI 모델이 완벽하게 이해할 수 있는 최종 프롬프트(명령어)를 완성하는 단계이다.
  • Prompt 객체의 내부는 List<Message>ChatOptions이 있다.
    • ChatOptions : 대화의 맥락을 구성하는 여러 종류의 메시지 묶음
    • List<Message> : 온도, 사용할 모델명과 같은 AI 실행 옵션

MessageType

  • SYSTEM : AI의 성격, 직업, 행동 지침, 제약 사항 등을 세뇌시키는 메시지
  • USER : 실제 서비스를 이용하는 유저가 입력한 질문이나 지시 내용
  • ASSISTANT : AI가 이전에 대답했던 응답 내용을 챗봇이 기억하고 이어가기 위해(Chat Memory) 덧붙이는 메시지
  • TOOL : AI가 외부 API나 함수(예: 날씨 조회, 사내 DB 조회)를 호출하고 돌아온 결과값을 담는 특수 메시지

PromptTemplate

  • 실제로 질문이 고정되어 있지 않기에 유저의 입력값에 따라 질문 내용이 매번 바뀌어야 하므로 PromptTemplate을 필수적으로 사용한다.
  • 비즈니스 로직(자바 코드)과 프롬프트 문구(텍스트)를 깔끔하게 분리할 수 있어 유지보수가 매우 편해진다.
  • AI 모델 실행
  • 완성된 최종 프롬프트를 실제 외부 AI API(OpenAI, 구글 등)에 던져 답변을 받아온다.
  • 이 단계에서 설정(일반 응답 vs 스트리밍)에 따라 데이터를 한 번에 통째로 받을지, 글자 단위로 실시간 스트리밍으로 받을지 응답 규격이 결정된다.
    • OpenAI(ChatGPT), Google(Gemini), Anthropic(Claude) 등 다양한 외부 AI 서비스와 통신하기 위한 자바 표준 인터페이스
    • Spring AI Models는 자바의 다형성(Polymorphism)을 극대화하여, 개발자가 단일한 스프링 인터페이스(Model)만 보고 코딩하도록 설계되어있다.
    • application.yaml과 같은 설정 파일에서 API 키와 의존성 라이브러리만 교체하여 사용 가능하다.
  • ChatModel : 텍스트 대화 엔진, 텍스트(Prompt)를 입력받아 텍스트(ChatResponse)를 반환하는 가장 기본적이고 핵심적인 모델
  • EmbeddingModel : Vector 변환 엔진, RAG(검색 증강 생성) 시스템의 핵심. 줄글 텍스트를 입력받아 Vector DB에 저장할 수 있도록 숫자 배열(Vector)로 변환해주는 모델
  • ImageModel : 이미지 생성 엔진, 텍스트 프롬프트를 입력받아 이미지를 생성
  • AudioModel : 음성 처리 엔진, 음성을 텍스트로 변환(STT)하거나, 텍스트를 음성으로 변환(TTS)하는 모델
  • 후처리 및 객체 변환
  • AI 모델이 뱉어낸 줄글 형태의 텍스트 답변을 받아, 백엔드 로직에서 즉시 사용할 수 있도록 정해진 자바 객체(DTO)나 JSON 규격으로 자동 파싱해주는 최종 변환 단계이다.
    • AI 모델이 뱉어내는 예측 불가능한 문자열(String) 형태의 답변을, 백엔드 서버가 즉시 사용할 수 있는 정형화된 데이터 규격(JSON, 자바 객체, 리스트 등)으로 강제하고 변환해주는 프레임워크 지원 기능이다.
    • Structured Output 기능을 사용하면, 백엔드 로직은 AI의 응답을 String이 아닌 처음부터 자바 DTO나 List<String> 같은 깔끔한 객체 형태로 바로 건네받을 수 있다.
  • BeanOutputConverter : AI의 답변을 개발자가 직접 만든 커스텀 자바 클래스(DTO 또는 Record)로 변환한다.
  • ListOutputConverter : 답변을 단순한 List<String> 형태로 반환한다.
  • MapOutputConverter : 답변을 Map<String, Object> 형태의 키-값 쌍으로 반환한다.
spring:
  application:
    name: spring-ai
  ai:
#    anthropic:
#      api-key: "TEST-API-KEY"
#      chat:
#        model: claude-opus-4-8
#        max-tokens: 4096
    ollama:
      init:
        pull-model-strategy: when_missing
      chat:
        model: hf.co/Qwen/Qwen2.5-1.5B-Instruct-GGUF # https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct-GGUF
@RestController
public class ChatController {

    private final ChatClient client;

    @Autowired
    public ChatController(ChatClient.Builder builder) {
        this.client = builder.build();
    }

    // 1) 단순하게 받기: call().content() 로 응답 본문 텍스트만 String 으로 반환받는다. 가장 기본 형태.
    @GetMapping("/chat/simple")
    public String simple(@RequestParam String prompt) {
        return this.client.prompt()
                .user(prompt)
                .call()
                .content();
    }

    // 2) ChatResponse 객체로 받기: call().chatResponse() 로 본문 + 메타데이터를 통째로 받는다.
    //    본문 텍스트 → getResult().getOutput().getText(), 토큰 사용량 → getMetadata().getUsage()
    @GetMapping("/chat/response")
    public ChatResponse response(@RequestParam String prompt) {
        return this.client.prompt()
                .user(prompt)
                .call()
                .chatResponse();
    }

    // 3) 자바 객체로 받기: call().entity(ActorFilms.class) 로 응답을 DTO(record)에 매핑한다.
    //    Spring AI 가 JSON 출력 포맷 지침을 프롬프트에 자동으로 덧붙여 그 결과를 객체로 변환한다.
    @GetMapping("/chat/entity")
    public ActorFilms entity(@RequestParam String actor) {
        return this.client.prompt()
                .user(u -> u.text("{actor}이(가) 출연한 대표 영화 5편을 알려줘.")
                            .param("actor", actor))
                .call()
                .entity(ActorFilms.class);
    }

    // 4) Flux 로 받기: stream().content() 로 토큰을 생성되는 즉시 흘려보낸다(SSE).
    //    produces = text/event-stream 이라 응답이 data: 청크로 실시간 스트리밍된다. (curl -N 로 확인)
    @GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> stream(@RequestParam String prompt) {
        return this.client.prompt()
                .user(prompt)
                .stream()
                .content();
    }
}

📖 Java

📖 Kotlin

📖 Coroutine

📖 Spring

📖 Spring Security

📖 Spring Batch

📖 Reactive Programming

📖 Database

📖 MySQL

📖 Redis

📖 JPA

📖 QueryDsl

📖 MSA

📖 Kafka

📖 Apache Flink

  • [Apache Flink - Apache Flink Architecture]
  • [Apache Flink - Stream Processing]
  • [Apache Flink - Data Stream API & Window]
  • [Apache Flink - State Management]

📖 HTTP

📖 AWS

📖 Docker

📖 Kubernetes

📖 CI/CD

📖 Nginx

📖 Monitoring

  • [Monitoring - Log Concept]
  • [Monitoring - Log Level & Filter]
  • [Monitoring - Logback]
  • [Monitoring - Log Collection with ELK Stack]
  • [Monitoring - Log Monitoring with Kibana]
  • [Monitoring - Building a Monitoring System with Spring Boot Actuator]
  • [Monitoring - Server Monitoring with Prometheus and Grafana with Discord Alerts]

📖 Test

📖 Effective Java 3/E

📖 Kotlin Academy - Effective Kotlin

📖 Kotlin Academy - 핵심편

📖 스프링으로 시작하는 리액티브 프로그래밍

📖 가상 면접 사례로 배우는 대규모 시스템 설계 기초 1

📖 가상 면접 사례로 배우는 대규모 시스템 설계 기초 2

📖 Clean Code

📖 리팩토링 2판

📖 주니어 백엔드 개발자가 반드시 알아야 할 실무 지식

📖 개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴

📖 Spring AI

Clone this wiki locally