|

LLM 서비스 경계는 어디에 둘까: 도메인 로직과 AI 호출 분리

LLM 서비스 경계는 어디에 둘까: 도메인 로직과 AI 호출 분리
LLM 호출은 도메인 로직 안으로 섞지 말고 책임 경계를 먼저 정해야 한다

LLM 서비스 경계를 정하지 않고 기존 서비스에 AI 기능을 붙이면 처음에는 빠르게 보입니다.

하지만 시간이 지나면 프롬프트, 정책, 검증, 저장 로직이 한 메서드 안에 섞이고, 작은 프롬프트 수정이 도메인 동작까지 흔드는 문제가 생깁니다.


LLM 서비스 경계를 먼저 한 줄로 보기

LLM 서비스 경계 설계 카드
AI 기능을 붙일 때 나눠야 할 책임

가장 중요한 원칙은 도메인 로직과 AI 호출을 같은 책임으로 보지 않는 것입니다. LLM은 문장을 생성하고 해석을 돕지만, 서비스의 최종 규칙을 대신 책임지면 안 됩니다.

AI가 제안하고, 도메인이 검증하고, 서비스가 저장한다는 흐름으로 나누면 설계가 훨씬 안정됩니다.


나쁜 경계는 어떤 모양일까

흔한 문제는 서비스 메서드 안에서 프롬프트를 만들고, LLM을 호출하고, 결과를 파싱하고, 검증 없이 바로 저장하는 구조입니다.

fun createSummary(orderId: OrderId): Summary {
    val order = orderRepository.get(orderId)
    val prompt = "이 주문을 요약해줘: $order"
    val text = llmClient.complete(prompt)
    val summary = Summary(text)
    return summaryRepository.save(summary)
}

이 코드는 짧지만 위험합니다. 프롬프트가 도메인 코드 안에 박혀 있고, LLM 응답의 형식과 품질을 검증하지 않으며, 저장 정책도 분리되어 있지 않습니다.


경계를 나누면 책임이 선명해진다

LLM 기능을 기존 서비스에 붙일 때는 최소한 네 가지 책임을 분리하는 편이 좋습니다.

  • Prompt builder: 입력 데이터를 어떤 맥락으로 모델에 줄지 결정
  • LLM gateway: 모델 호출, timeout, retry, 비용 로깅 처리
  • Result validator: 응답 형식, 금지어, 필수 필드, 도메인 규칙 검증
  • Domain service: 승인된 결과만 실제 상태 변화로 반영
fun createSummary(orderId: OrderId): SummaryDraft {
    val order = orderRepository.get(orderId)
    val request = promptBuilder.buildOrderSummaryRequest(order)
    val response = llmGateway.generate(request)
    val draft = summaryParser.parse(response)
    return summaryPolicy.validateDraft(order, draft)
}

여기서 반환값을 바로 최종 Summary로 저장하지 않고 SummaryDraft로 둔 점이 중요합니다. AI 결과는 아직 제안이며, 도메인 검증과 필요하면 사람의 승인을 거쳐야 합니다.


도메인 규칙은 프롬프트가 아니라 코드에 남겨야 한다

프롬프트에 “환불 불가 상품은 환불 가능하다고 쓰지 마”라고 적을 수는 있습니다. 하지만 이것만으로 규칙을 보장했다고 볼 수는 없습니다.

환불 가능 여부, 가격 계산, 권한, 개인정보 노출 금지 같은 규칙은 코드에서 검증해야 합니다. LLM은 설명 문장이나 초안 생성에 도움을 주고, 최종 판단은 도메인 정책이 맡아야 합니다.


테스트도 두 갈래로 나눠야 한다

LLM 기능 테스트는 일반 단위 테스트처럼 항상 같은 문자열을 기대하기 어렵습니다. 그래서 테스트도 분리해야 합니다.

  • 도메인 테스트: LLM 없이도 규칙과 불변식이 지켜지는지 확인
  • 프롬프트 회귀 테스트: 대표 입력에서 응답 품질이 크게 망가지지 않는지 확인
  • 파서 테스트: 모델 응답이 흔들려도 안전하게 실패하는지 확인
  • 운영 지표: 비용, 지연, 승인률, 재생성률을 기록

이렇게 나누면 프롬프트가 바뀌어도 도메인 규칙 테스트는 안정적으로 유지되고, AI 품질 문제는 별도의 eval로 추적할 수 있습니다.


정리

LLM 기능을 기존 서비스에 붙일 때 가장 위험한 선택은 AI 호출을 도메인 로직 안에 자연스럽게 섞어 넣는 것입니다.

출처 확인: OpenAI 개발 문서, DDD Reference, NIST AI RMF를 참고해 AI 호출과 도메인 책임을 분리하는 방향으로 정리했습니다.

프롬프트 생성, 모델 호출, 결과 검증, 도메인 반영을 분리하면 수정 범위가 작아지고, 실패했을 때 어느 책임에서 문제가 생겼는지도 추적하기 쉬워집니다.

함께 읽을 글로는 AI eval은 왜 필요할까AI 에이전트란 무엇인가가 좋습니다.

함께보면 좋은 글