본문으로 바로가기
이번 강의는 무엇을 노리고 있을까?
LinkedInGitHub
WORKSPACE

EXPLORER

157 POSTS
BLOG
면접질문
해야하는 거
AI 시대, 개발자는 사라지는가?
Python Counter, 빈도수를 쉽게 세는 방법
정렬
1. 투 포인터
DFS와 BFS
알고리즘 논리
코딩 테스트 핵심 알고리즘 정리파이썬 딕셔너리와 코딩 테스트 활용
예시로 살펴보는 AXAX 프로젝트, 문제 정의부터 확산까지AX 시대의 가치 정의와 현장 리딩
MSA
1. Singleton PatternSOLID
Kafka의 핵심 설계 원리Kafka 메시징 시스템의 구성과 동작 방식Kafka 기본 개념과 EC2 Docker 구성
쿠버네티스 입문
Modular Monolith1. MonolithMSA, 서비스 분리와 운영의 원리
Socket이란 무엇인가
네크워크 참조 모델
전체 데이터 구조REST API의 개념과 설계 원칙2. Field/ Parameter / Argument/ this
Actuator 란?EntityManagerJPA 연관관계 매핑JPA 트랜잭션(Transaction)입력값 검증Java의 AOP(Aspect Oriented Programming)Async : AsynchronousBeanJPA(Java Persistence API)Proxy 패턴Spring MVCdocker container1. Spirng 필요컴포넌트 스캔
Spring 컨테이너
application.yamlLombok
이번 강의는 무엇을 노리고 있을까?Spring AI를 배우기 전에 정리할 것23
1. 기술 스택
개발 문서를 읽기 위한 핵심 기술 용어
Docker를 이해하기 위한 운영체제 기초
sigterm
6. 설정과 저장소, 앱을 운영할 수 있는 상태로 만들기5. Service와 Ingress, 요청은 어디로 흐를까4. 직접 실험하는 Kubernetes, Pod 복구부터 롤백까지3. kubectl과 Pod, 상태에서 원인을 찾는 법2. 클러스터는 명령을 어떻게 Pod로 바꿀까1. 쿠버네티스, 원하는 상태와 컨테이너 이미지Calico쿠버네티스 입문, 원하는 상태를 유지하는 시스템
Psql JSONB, 행 잠금, 멱등성Psql 함수,프로시져,트리거
머신러닝 입문딥러닝 학습 기본 개념데이터 시각화기초 통계와 ML 파이프라인 연결분석 자동화와 파이프라인 설계
CNN 아키텍처 발전 과정이미지 세그멘테이션 모델과 핵심 개념객체 탐지 모델과 핵심 개념
딥러닝 데이터셋 엔지니어링
딥러닝 학습 문제 진단과 디버깅
도메인 적응 방법현대 LLM 워크플로의 패턴
Transformer에서 LoRA 적용 대상 정하기LoRA (Low-Rank Adaptation)
LLM 양자화와 QLoRA
분산 학습과 MLOps딥러닝 모델 경량화와 추론 최적화딥러닝 기본 학습 테크닉딥러닝 중급 학습 테크닉
Mixture of Experts(MoE) 핵심 개념멀티모달 파운데이션 모델 핵심 개념State Space Model과 MambaTransformer와 Vision Transformer
Latent Space
1Chunking?
DevOps 기초 1편
실습에서는?Agile 개요, 왜 필요한가?
CI/CD 기초 4편: 배포 전략과 운영CI/CD 기초 3편: Jenkins와 Argo CD를 이용한 GitOps 배포CI/CD 기초 2편: Docker 이미지와 배포 파이프라인CI/CD 기초 1편: 개념과 GitHub Actions
OCI: 컨테이너 이미지와 런타임의 공통 표준Docker 기초 13편: Compose Healthcheck와 실전 구성Docker 기초 12편: Compose 네트워크와 VolumeDocker 기초 11편: Compose 명령어와 환경 변수Docker 기초 10편: Compose 기본 구조와 이미지 빌드Docker 기초 9편: Docker 및 Kubernetes 네트워크Docker 기초 8편: 컨테이너 런타임과 격리Docker 기초 7편: 이미지 Layer와 tar 내부 구조Docker 기초 6편: 이미지 Layer와 빌드 최적화Docker 기초 5편: 컨테이너 기본 명령어와 VolumeDocker 기초 4편: 가상화와 컨테이너 이미지 생명주기Docker 기초 3편: CI/CD 연결과 배포 원칙Docker 기초 2편: Layer, Registry, Volume과 NetworkDocker 기초 1편: Dockerfile, Image와 Container
NginxNginx 로드 밸런싱과 HTTPSNginx 리버스 프록시와 Spring Boot 연결Nginx 기초와 동작 구조
05. Pinia 상태 관리: store 설계와 사용법04. Vue 컴포넌트 설계: props, emit, slot과 생명주기03. Vue Composition API 정리02. Vue 기초 문법 점검: JavaScript, 템플릿01. Vue.js 입문: 핵심 구조와 렌더링
Java 심화 Part 3: 함수형 프로그래밍과 LambdaJava 심화 Part 2: AnnotationJava 심화 Part 1: Reflection
Java 기초 Part 5: Stream APIJava 기초 Part 4: 제네릭Java 기초 Part 3: 제어문Java 기초 Part 2: 주석과 JavadocJava 기초 Part 1: 백엔드 배경과 Java 실행 구조
Java 디버깅 Part 1: 자주 헷갈리는 핵심 개념Java 디버깅 Part 2: VS Code 자동 컴파일과 프로젝트 구조
Java 실행과 JVM Part 3: ClassLoader와 JVM 메모리Java 실행과 JVM Part 2: 메모리와 데이터 흐름Java 실행과 JVM Part 1: Java와 Python 컴파일 비교
Java 객체지향 Part 5: static 메서드와 중첩 클래스Java 객체지향 Part 4: 상속과 인터페이스Java 객체지향 Part 3: 좋은 설계와 OOP 4대 특성Java 객체지향 Part 2: OOP 핵심 문법Java 객체지향 Part 1: 클래스, 객체, 필드와 생성자
Spring 기초 Part 11: Actuator와 애플리케이션 모니터링Spring 기초 Part 10: 비동기 처리와 @AsyncSpring 기초 Part 9: JPA 트랜잭션과 동시성 제어Spring 기초 Part 8: AOP와 공통 관심사 분리Spring 기초 Part 7: Proxy 패턴과 Spring ProxySpring 기초 Part 6: JPA 연관관계 매핑Spring 기초 Part 5: EntityManager와 영속성 컨텍스트Spring 기초 Part 4: JPA, Entity와 RepositorySpring 기초 Part 3: REST API 요청값과 입력값 검증Spring 기초 Part 2: Spring MVC 요청 처리 흐름Spring 기초 Part 1: IoC, Bean, DI와 주요 Annotation
DNS = Domain Name System
Spring Boot, WebSocket, Vue, Docker 로 Raspberry Pi 실시간 모니터링 프로젝트 만들기 - 1편1. Spring Boot 구현
1. GitHub Project 만들기
Python 코드 품질: 디버깅부터 테스트와 자동화까지Python 01. 실행 구조와 실무 기초
sLLM 핵심 기술과 전체 구조제한된 자원에서 sLLM 구축하기
시대 단상에 대한 주저리주저리
WORKSPACE

SEARCH

제목, 카테고리와 태그로 검색하세요.

VERSION CONTROL

SOURCE CONTROL

masterGitHub Pages
저장소 열기
BUILD STATUS

RUN AND DEBUG

게시물은 GitHub Actions에서 검증하고 정적 페이지로 빌드합니다.

Actions 열기
WORKSPACE

MANAGE

홈 열기전체 게시물태그 보기블로그 소개
이번 강의는 무엇을 노리고 있을까?●
workspace>posts>backend>spring ai>요약.md
Backend / SPRING AI2026.08.191 min read0 tags

이번 강의는 무엇을 노리고 있을까?

Spring Boot 계층 구조 위에 AI를 얹고, 프롬프트로 출력을 제어하고, RAG로 근거를 붙이고, Tool로 실제 행동을 수행한 뒤에, 보안과 메모리, 운영지표 관리해보자

이번 강의는 무엇을 노리고 있을까?

Spring Boot 계층 구조 위에 AI를 얹고, 프롬프트로 출력을 제어하고, RAG로 근거를 붙이고, Tool로 실제 행동을 수행한 뒤에, 보안과 메모리, 운영지표 관리해보자

문서 사전 처리

문서

-> DocumentReader
-> Splitter
-> Chunk
-> EmbeddingModel
-> VectorStore

사용자 요청 처리

사용자
-> Controller
-> Service
-> ChatClient
-> Advisor 체인
    -> Safety
    -> Memory
    -> RAG
    -> Audit
    -> Token Meter
-> ChatModel
    -> 문서가 필요하면 RAG
    -> 실시간 행동이 필요하면 Tool
-> 구조화된 DTO
-> 사용자 응답

운영

토큰 측정 지연 시간 측정 오류율 측정 감사 로그 폴백 모델

공부하기 전에 알아놓을 개념

1. LLM은 기억하거나 시실을 조회하거나 프로그램이 아니다.

LLM은 입력을 읽고 다음 토큰을 확률적으로 생성하는 모델

따라서 다음 특성이 생김

  • 같은 질문에도 답이 조금 달라짐
  • 모르는 내용을 그럴듯하게 만들 수 있다.
  • 대화 내요을 스스로 기억하지 않는다
  • 이전 대화를 기억시키려면 매번 대화 이력을 다시 넣어야 한다
  • 입력과 출력의 길이, 비용, 속도는 토큰을 기준으로 결정된다.
  • 컨텍스트 윈도우를 넘으면 오래된 내용이 잘리거나 사용할 수 없게 된다.

Memory는 진짜 기억이 아니라, 이전 대화를 저장했다가 다시 프롬프트에 넣는 기능

RAG는 진짜 학습이 아니라, 외부 문서를 찾아 프롬프트에 넣는 기능

2. AI도 기존 Spring Boot 계층 구조 안에 넣어야 한다.

기본 역할은 다음과 같다.

계층책임
ControllerHTTP 요청 수신, 입력 검증, 응답 반환
Service업무 흐름, 정책 판단, AI 호출 조합
Repository 또는 MapperDB와 외부 시스템 접근
DTO계층 간 요청과 응답 형식
ConfigChatClient, Advisor, VectorStore 같은 빈 구성
Advisor로깅, 안전, 메모리, RAG 같은 공통 처리

가장 중요한 규칙은 Controller가 ChatClient를 직접 호출하지 않는 것.

Controller는 AI가 어떤 모델을 사용하는지, 어떤 프롬프트를 사용하는지, RAG가 붙는지 알 필요가 없다. Controller는 Service만 호출하고,Service가 적절한 시점에 ChatClient를 호출해야 한다.

모델이나 프롬프트가 바뀌어도 웹 계층이 영향을 받지 않도록 설계해야 한다.

자료 역시 AI 코드를 Config, Advisor, Service로 분산하고, Controller는 AI 모르게 하도록 정리해야 한다.

2 Spring AI의 중심은 추상화

  • 추상화? : 필요한 기능만 보여주는 것 (메서드로 구현한다.)
추상화역할
ChatModel프롬프트를 받아 텍스트를 생성
EmbeddingModel텍스트를 의미 벡터로 변환
VectorStore벡터를 저장하고 유사한 문서를 검색

실제로도 ChatClient를 주로 사용한다.

String answer = chatClient.prompt()
    .system("너는 상담 도우미다.")
    .user(question)
    .call()
    .content();

ChatClient는 단순한 모델 호출 이외에도 다음 기능을 제공

  • 시스템 프롬프트
  • 호출 옵션
  • Advisor 연결
  • Tool 연결
  • 구조화 출력
  • 스트리밍
  • 응답 메타데이터

세 추상화를 잘 결합하면 채팅, 검색, RAG, 문서 검색, 에이전트 기능으로 확장할 수 있다.

또한 애플리케이션 코드는 추상화에 의존하고, 공급자는 Gradle 의존성과 application.yml 설정으로 교체하도록 설계

3. ChatClient는 용도별로 나누는 것이 좋다.

하나의 ChatClient로 요약, 분류, 창작, 상담을 모두 처리하면 설정이 충돌한다.

따라서 아래와 같이 나누는 것이 좋다.

extractClient
    목적 : 분류, 추출
    temperature : 0
    규칙 : 추측 금지, 없으면 null
supportClient
    목적 : 상담
    temperature : 0.2
    규칙 : 친절한 문장
ideaClient
    목적 : 아이디어 생성
    temperature : 0.8
    규칙 : 다양한 아이디어

온도는 모델의 지식향을 조절하는 값이 아니락, 출력의 무작위성을 조절

  • 분류, 추출, 요약 : 낮은 온도
  • 아이디어, 창작 : 높은 온도

또한 시스템 프롬프트, 옵션, Advisor는 호출할 때마다 적는 것이 아니라 ChatClient Bean의 기본값으로 구성하는 편이 일관성이 높다.

4. 프롬프트는 업무 지지서

종흔 프롬프트의 기본 요소는 네 가지

요소질문
역할누구의 관점에서 답하는가
맥락어떤 자료와 상황을 참고하는가
지시정확히 무엇을 해야 하는가
형식어떤 모양으로 출력해야 하는가

역할

너는 사내 고객 문의 분류기다.

[맥락]
문의 카테고리는 결제, 배송, 환불, 기타 네 가지다.

[지시]
사용자 문의를 가장 적절한 카테고리로 분류하라.
확신이 없으면 기타로 분류하라.

[형식]
카테고리 이름 하나만 출력하라.

-> 업무 지시는 이렇게 하는 게 좋음

말로 설명하기 어려운 경우에는

FewShot 예시를 넣도록

입력: 카드 결제가 두 번 됐어요
출력: 결제

입력: 택배가 아직 안 왔어요
출력: 배송

프롬프트가 계속 길어진다면 지시를 계속 추가하기보다 작업을 Router, Chaining, Evaluator 단계로 나눌 수 있다. 자료는 정확도가 부족할 때 프롬프트를 계속 늘리지 말고 호출을 테스트 가능한 단계로 분리하라고 강조

5. 문자열보다는 구조화 출력을 사용해야 한다.

String result = chatClient.prompt()
    .user(question)
    .call()
    .content();

String[] values = result.split(",");

모델이 쉼표를 빼거나 설명을 추가하면 깨질 수 있으므로 일단 형식으로 정리

enum Category {
    BILLING, DELIVERY, REFUND, ETC
}

record Ticket(
    Category category,
    int priority,
    List<String> keywords
) {}
  • 여기서 알고 가기 : record = 여러 데이터를 하나로 묶어 전달하기 위한 간결한 불변 데이터 객체

그리고 객체로 받는다.

Ticket ticket = chatClient.prompt()
    .user(question)
    .call()
    .entity(Ticket.class);
  • 문자열 파싱 코드가 사라진다.
  • 필드 타입이 보장된다.
  • enum으로 허용값을 제한할 수 있다.
  • 목록과 중첩 객체를 그대로 받을 수 있다.
  • 형식 위반을 조용히 통과시키지 않고 실패시킬 수 있다.

다만 모델 출력은 항상 성공하지 못하므로, 자료 온도 0, 형식 재요청, 안전한 기본값의 세 단계를 준비해야 함.

6. 스트리밍은 총시간보다 체감시간을 줄인다

call()은 응답이 완성될 때까지 기다린 뒤 한 번에 반환한다.

String answer = chatClient.prompt()
    .user(question)
    .call()
    .content();

stream()은 생성되는 조각을 바로 반환한다.

Flux<String> answer = chatClient.prompt()
    .user(question)
    .stream()
    .content();

Stream()을 사용하면 전체 생성 시간이 같더라도, 더 빨리 출력되기 때문에 사용자는 빠르다고 느낀다.

다만 스트리밍에는 반드시 다음 처리가 필요하다.

  • 클라이언트 연결 취소
  • 전체 타임아웃
  • 오류 시 대체 메시지
  • 스트림 종료 처리
  • 스트리밍 경로의 별도 계측

7. 임베딩은 의미를 좌표로 바꾸는 과정

임베딩은 문장을 숫자 벡터로 변환하는 것

"강아지"
    → [0.12, -0.54, 0.87, ...]

"반려견"
    → [0.14, -0.51, 0.84, ...]

뜻이 비슷한 문장은 벡터 공간에사도 가깝다.

VectorStore는 다음을 수행

문서 저장:
문서 -> 임베딩 -> 벡터 저장

질문 검색
질문 -> 임베딩 -> 가까운 문서 벡터 검색

임베딩 모델을 바꾸면 기존 벡터를 그대로 사용할 수 없다. -> 차원이 같더라도 의미 공간이 달라질 수 있으므로 전체 문서를 다시 임베딩하고 재색인

8. Chunking은 RAG 품질을 결정하는 핵심 설계

Chunking은 긴 문서를 검색하기 좋은 크기로 나누는 작업

너무 작으면

  • 앞뒤 맥락이 잘린다,
  • 주어가 무엇인지 알 수 없다.
  • 조항과 예외 조건이 분리된다.

너무 크면 :

  • 질문과 관련 없는 내용까지 함께 검색된다.
  • 잡음이 늘어난다.
  • 모델에 넣는 토큰이 증가한다.

Overlap은 인접 청그가 일부 문장을 공유하게 하여 경계에서 근거가 잘리는 것을 막는다.

자료에서는 일반적인 시작값으로 800에서 1200자, 겹침 10에서 20퍼센트를 제시

질문 하나에 답할 만한 분량인가

PDF 178페이지의 청킹 표 역시 문서의 구조와 용도에 따라 크기를 바꾸도록 설명

청크에는 처음부터 다음 메타데이터 넣을 필요 있음

  • 출처
  • 페이지
  • 문서 종류
  • 부서
  • 버전
  • 유효기간
  • 접근 권한

메타데이터가 있어야 출처 표시, 권한 필터, 만료 문서 제외, 최신 버전 선택이 가능.

9. RAG는 검색과 생성의 결합

RAG는 두 개의 파이프라인으로 구분

사전 인덱싱

문서
  → Reader
  → Splitter
  → Chunk
  → Embedding
  → VectorStore

질문별 검색과 생성

질문
  → 질문 임베딩
  → 관련 청크 검색
  → 질문과 근거를 프롬프트에 결합
  → 모델 응답
  → 출처 반환

RAG 파이프라인은 사전 준비 단계인 Indexing과 질문마다 수행되는 Retrieval을 명확히 분리

RAG 답변이 틀렸을 때는 다음 순서로 확인해야 한다.

  • 관련 문서가 실제로 저장되어 있는가
  • 검색 결과에 정답 청크가 포함되는가
  • 정답 청크가 상위에 배치되는가
  • 메타데이터 필터가 잘못 걸리지 않았는가
  • 그다음에 프롬프트와 모델을 확인

검색 결과에 정답이 없다면 프롬프트를 아무리 고쳐도 답은 좋아지지 않는다.

자료도 품질 문제가 발생하면 모델의 답보다 검색 결과를 먼저 눈으로 확인해야 한다.

RAG 고급 전략

문제방법
질문과 문서의 표현이 너무 다름HyDE
질문이 짧거나 모호함질의 변환
정답 문서가 검색되지 않음다중 질의 확장
정답 문서가 검색됐지만 순서가 낮음재순위
제품 코드처럼 정확한 문자열이 중요함Hybrid Search
검색은 작게, 문맥은 크게 넣고 싶음Parent-Child
검색 결과가 부족하면 다시 찾고 싶음Agentic RAG

넓게 검색하고, 재순위한 뒤, 좁게 모델에 넣는다.

예를 들어 20개를 검색하고 재순위한 뒤 상위 4개만 프롬프트에 넣으면 검색 재현율은 유지하면서 잡음과 토큰을 줄일 수 있다.

RAG 평가는 검색과 생성을 나누어서 측정한다.

단계지표
검색Recall@k
검색Precision@k
생성Faithfulness
생성Answer Relevancy

검색 실패와 생성 실패를 한 점수로 뭉치면 어느 부분을 알 수 없기 때문

10. Tool Calling은 모델이 실행하는 것이 아님

판단은 모델이 하고, 실행은 우리 코드가 함

모델은 Tool의 이름, 설명, 파라미터 스키마를 보고 어떤 Tool을 호출할지 결정한다. 실제 DB 조회, API 요청, 파일 변경은 Spring 애플리케이션의 메서드가 수행한다.

모델은 Tool의 이름, 설명, 파라미터 스키마를 보고 어떤 Tool을 호출할지 결정

실제 DB 조회, API 요청, 파일 변경은 Spring 애플리케이션의 메서드가 수행

@Tool(description = "주문번호로 배송 상태를 조회한다")
String orderStatus(
    @ToolParam(description = "주문번호") String orderId,
    ToolContext context
) {
    String userId = context.getContext().get("userId").toString();
    return orderService.findOwned(orderId, userId);
}

Tool 설계에서 중요한 점은 다음과 같다.

  • 설명을 명확하게 작성한다.
  • 하나의 Tool은 하나의 책임만 가진다.
  • 입력값을 검증한다.
  • 권한을 Tool 내부에서 검사한다.
  • 실패를 모델이 이해할 수 있는 메시지로 반환한다.
  • DB Entity 전체가 아니라 필요한 정보만 반환한다.
  • 사용자 ID 같은 보안 정보는 프롬프트가 아니라 ToolContext로 전달

11. gent는 똑똑한 존재가 아니라 반복 구조

Agent는 다음 과정을 반복한다.

Reason
-> Act
-> Observe
-> 다시 Reason

Tool calling이 한 번의 도구 사용이면, Agent는 결과를 보고 도구를 다시 선택하는 여러 단계의 Tool Calling

반드시 제한해야 할 것은

  • 최대 반복 횟수
  • 최대 토큰
  • 최대 실행 시간
  • 동일 Tool과 동일 인자 반복
  • Tool별 재시도 횟수
  • 전체 비용 예산

자료는 Agent를 만들 때 잘 수행하는 방법보다, 언제 멈출 지 잘 설정하는 게 더 중요하다. (우리의 토큰은 무한하지 않으니까....ㅋㅋㅋ 무한한 토큰이 있다면 시도해보시길)

환불, 삭제, 발송, 취소같이 한 번 실행하면 되돌리기 어려운 작업은 모델이 완료하게 하면 안된다. 일단은 Tool은 승인 요청만 하고, 실제 처리는 승인 이후에 실행하는 HITL 구조가 좀 필요하다.

12. MCP는 Tool 연결의 표준 규격

MCP는 AI 애플리케이션과 외부 Tool 또는 Resource를 연결하는 표준 프로토콜

MCP Client
-> 외부 MCP Server의 Tool을 사용

MCP Server
-> 우리 시스템의 Tool을 다른 AI 애플리케이션에 공개

번 MCP 서버로 만들면 여러 AI 애플리케이션이 같은 Tool을 공유할 수 있다. 다만 원격 실행 통로가 될 수 있으므로 인증과 공개 범위 통제가 필요

MCP는 Spring AI 기초를 배우는 단계에서 가장 먼저 익혀야 할 개념은 아니다. 기본 Tool Calling이 안정된 뒤 Tool 수가 많아지고 다른 애플리케이션과 공유해야 할 때 배우는 것이 적절

13. Advisor는 모든 AI 요청이 지나가는 파이프라인

Advisor는 AOP나 인터셉터와 비슷한 개념

모든 모델 요청에 공통으로 필요한 처리를 한 곳에 모은다.

  • 안전 필터
  • 대화 메모리
  • RAG 근거 주입
  • 감사 로그
  • 토큰 계측
  • 지연 시간 측정
  • 응답 후처리

권장 흐름

요청:

Audit
  → Safety
  → Memory
  → RAG
  → Logger
  → Model

응답:
Model
  → Logger
  → RAG
  → Memory
  → Safety
  → Audit

Safety가 Memory 뒤에 있다면, 차단해야 할 공격 문장이 이미 대화 이략에 저장될 수 있음 따라서 안전 필터는 메모리 저장보다 앞에 있어야 한다.

14. Memory는 저장소와 보존 정책까지 포함하는 개념

ChatMemory는 이전 메시지를 저장하고 새 요청에 다시 주입

개발 단계에서는 InMemory를 사용할 수 있지만, 운영에서는 다음을 고려해야 한다.

저장소적합한 상황
InMemory로컬 개발, 테스트
JDBC일반적인 영속 운영
Redis다중 인스턴스, 짧은 TTL
Cassandra초대량 장기 저장

대화가 길어지면 모든 메시지를 계속 넣을 수 없으므로 다음 전략이 필요

최근 N개 메시지만 유지 오래된 대화를 요약 중요 결정만 별도 저장 대화 보존 기간 설정 사용자별 삭제 기능 개인정보 마스킹

대화 이력에는 개인정보가 빠르게 쌓이므로 저장소를 선택할 때 보존 기간과 삭제 절차도 동시에 설계해야 한다. (정말 고려해야 할 것들이 많다!)

15. 운영에서는 모델 품질만 보면 안 되고, 다른 것들도 같이 고려해야 한다.

운영 지표는 최소한 다음은 측정해야 한다.

  • 입력 토큰
  • 출력 토큰
  • 총 토큰
  • 첫 토큰까지의 시간
  • 전체 응답 시간
  • RAG 검색 시간
  • Tool 호출 횟수
  • Tool 실패율
  • 모델 오류율
  • 풀백 발생 횟수

AI 호출은 외부 의존성이므로 주 공급자가 실패하더라도 서비스 전체가 죽지 않도록 다음 폴백을 준비해야 한다.

주 모델 실패
-> 보조 모델
-> 캐시
-> 정형 응답
-> 최소 기능 응답

요약

반드시 머릿속에 있어야 하는 개념

익힐 것이해 완료 기준
Controller, Service, Repository 역할ChatClient가 왜 Service에 있어야 하는지 설명할 수 있다
LLM의 확률성과 무상태성Memory가 왜 이력을 다시 넣는 기능인지 설명할 수 있다
ChatModel과 ChatClient 차이실무에서 ChatClient를 쓰는 이유를 말할 수 있다
Embedding과 VectorStore키워드가 달라도 검색되는 이유를 설명할 수 있다
RAG의 두 단계Indexing과 Retrieval을 구분할 수 있다
Tool Calling판단은 모델, 실행은 코드라고 설명할 수 있다
Advisor순서가 왜 보안 정책이 되는지 설명할 수 있다
Agent반복 횟수와 비용 상한이 필요한 이유를 설명할 수 있다

반드시 한 번은 직접 구현해야 하는 것

(이거는 주말에 한 번 해보면 될 듯하다.)

구현할 것완료 기준
용도별 ChatClient 빈요약용과 창작용의 옵션을 다르게 설정
구조화 출력entity()로 record와 enum 반환
SSE 스트리밍stream()에 timeout과 cancel 처리
RAG 인제스트문서 읽기, 청킹, 임베딩, VectorStore 적재
RAG 검색topK, threshold, metadata filter 적용
출처 반환답변과 함께 문서명 또는 페이지 반환
Tool@Tool, @ToolParam, ToolContext 적용
승인 게이트환불이나 삭제를 실행하지 않고 승인 요청으로 접수
Advisor 체인Safety가 Memory보다 앞에 오도록 구성
운영 지표토큰, 지연, Tool 호출을 Micrometer로 측정

기본 RAG와 Tool이 완성된 뒤 배우기

(다음 포스팅에서 제공할 예정)

HyDE Agentic RAG Parent-Child Retrieval Contextual Retrieval Multi-Agent MCP Server 공급자별 고유 옵션 복잡한 Evaluator-Optimizer 루프

효율적인 학습 순서

1. 기본 AI API 만들기

Controller
  → Service
  → ChatClient
  → 문자열 또는 DTO 반환
  • Controller에 ChatClient가 없다.
  • ChatClient는 Config에서 빈으로 생성된다.
  • API Key는 환경변수로 주입된다.
  • 예외가 안전한 응답으로 변환된다.

2. 출력 품질과 형식 제어

  • 역할, 맥락, 지시, 형식이 있는 프롬프트 작성
  • Few-shot 추가
  • temperature 실험
  • entity() 구조화 출력
  • stream() SSE 출력

완료 기준:

  • 문자열 파싱 코드가 없다.
  • 같은 분류 입력에 결과가 안정적이다.
  • 스트리밍 취소와 타임아웃이 동작한다.

3. RAG 완성

문서
  → Chunk
  → Embedding
  → VectorStore
  → 검색
  → 출처가 있는 답변
  • 검색 결과를 별도 API로 확인할 수 있다.
  • 검색된 청크의 출처와 점수가 보인다.
  • 문서에 없는 질문에는 모른다고 답한다.
  • 문서 재색인 시 중복 청크가 쌓이지 않는다.

4. Tool과 운영 완성

목표: 실시간 주문 조회 Tool 사용자 권한 검증 환불 승인 게이트 Memory와 Advisor 토큰과 지연 계측 폴백 응답 완료 기준: 다른 사용자의 데이터를 조회할 수 없다. 위험 작업은 즉시 실행되지 않는다. Safety가 Memory보다 먼저 실행된다. 모델 장애가 발생해도 기본 서비스는 유지된다

PREVIOUSSpring 기초 Part 7: Proxy 패턴과 Spring ProxyNEXTSpring 기초 Part 8: AOP와 공통 관심사 분리
DISCUSSION

COMMENTS

GitHub 계정으로 로그인하여 댓글을 남길 수 있습니다. 댓글은 GitHub Discussions에 공개 저장되며, 작성 내용과 GitHub 프로필 정보가 다른 방문자에게 보일 수 있습니다.

GitHub 로그인 후 댓글 쓰기Discussion 열기
DOCUMENT STRUCTURE

이 문서에는 목차가 없습니다.

DOCUMENT INFO
TYPE
Markdown
DATE
2026.08.19
READ
1 min read
WORDS
0
CATEGORY
Backend / SPRING AI
RELATED DOCUMENTS
Spring AI를 배우기 전에 정리할 것23쿠버네티스 입문Kafka의 핵심 설계 원리Kafka 메시징 시스템의 구성과 동작 방식
main Backend / SPRING AI
1 min readUTF-8Markdown
본문 글씨 크기
RECENTLY OPENED1
이번 강의는 무엇을 노리고 있을까?recently opened