이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • 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 적용 연결

참고 문서