이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- AI 기능을 Controller에 직접 넣으면 어떤 문제가 생기는가?
- AI Service 계층은 어떤 책임으로 나누는 것이 좋은가?
- 기존 도메인 서비스와 AI 기능은 어떻게 연결해야 하는가?
개요
AI 기능은 기존 백엔드 아키텍처 안에 들어와야 한다. Controller에서 모델 SDK를 직접 호출하면 빠르게 만들 수 있지만, 운영 책임이 흩어진다.
AI Service는 prompt, context, model client, validation, usage recording을 조율하는 application service로 두는 편이 안전하다.
추천 구조
Controller
AiUseCaseService
PromptRenderer
ContextBuilder
LlmClient
OutputValidator
UsageRecorder
TraceRecorder각 구성 요소는 테스트와 교체가 가능해야 한다.
코드로 이해하기
@Service
public class AiAnswerService {
private final ContextBuilder contextBuilder;
private final PromptRenderer promptRenderer;
private final LlmClient llmClient;
private final AnswerValidator answerValidator;
private final UsageRecorder usageRecorder;
public AiAnswerService(
ContextBuilder contextBuilder,
PromptRenderer promptRenderer,
LlmClient llmClient,
AnswerValidator answerValidator,
UsageRecorder usageRecorder
) {
this.contextBuilder = contextBuilder;
this.promptRenderer = promptRenderer;
this.llmClient = llmClient;
this.answerValidator = answerValidator;
this.usageRecorder = usageRecorder;
}
public AiAnswer answer(User user, String question) {
AiContext context = contextBuilder.buildFor(user, question);
String prompt = promptRenderer.render("doc-qa-v1", context, question);
LlmGenerateResult result = llmClient.generate(prompt);
usageRecorder.record(user, result);
return answerValidator.validate(result.text());
}
}서비스는 흐름을 조율하고, 각 책임은 별도 객체에 둔다.
백엔드 설계 판단
- AI 결과가 도메인 상태를 변경하는가?
- AI 결과를 사용자가 수정할 수 있는가?
- AI 실패가 전체 요청 실패인가, 부분 실패인가?
- AI 호출이 트랜잭션 안에 들어가면 안 되는가?
- 모델 호출을 mock으로 대체한 테스트가 있는가?
모델 호출은 외부 I/O이므로 DB transaction 안에서 오래 기다리지 않도록 주의한다.
장애 상황과 대응
Controller에 모델 호출이 있으면 장애 대응이 어렵다.
- timeout 설정이 흩어진다.
- prompt version이 로그에 없다.
- token usage 기록이 누락된다.
- validation 실패가 500이 된다.
계층을 나누면 장애 지점을 좁힐 수 있다.
인프라 협업 포인트
아키텍처 문서에는 외부 provider, vector DB, queue, worker, secret, trace 흐름을 표시해야 한다. 인프라팀은 어떤 컴포넌트가 어디로 outbound하는지 알아야 한다.
실전 팁
- AI 기능을 domain service 내부 깊숙이 숨기지 않는다.
- LLM 호출은 adapter와 timeout을 가진 외부 호출로 둔다.
- prompt와 context 조립은 테스트한다.
- usage recording은 MVP부터 넣는다.
위험 신호!
- Controller에서 SDK를 직접 호출한다.
- 모델 호출이 DB transaction 안에 있다.
- prompt version과 model profile이 로그에 없다.
- AI 실패가 모두 RuntimeException으로 뭉개진다.
확인 질문
확인 질문
- AI Service를 계층으로 나누는 이유는 무엇인가?
- prompt, context, 모델 호출, 검증, 비용 기록을 독립적으로 테스트하고 운영하기 위해서다.
- 모델 호출을 트랜잭션 안에 넣으면 왜 위험한가?
- 외부 호출 지연이 DB lock과 transaction 시간을 늘릴 수 있기 때문이다.
Spring AI 적용 연결
- Spring Boot 서비스에서 AI 계층을 분리할 때는 Controller, Application Service, AI Gateway,
ChatClient경계를 명확히 둔다. 구체 구조는 03. Spring Boot 계층 설계와 AI Gateway에서 이어진다. - 여러 기능을 하나의 서비스 설계로 연결하는 예시는 13. 고객지원 RAG Capstone을 함께 본다.