← 프로젝트 목록
CS 문의 자동 분류
AI(Bedrock) + Virtual Thread + NDJSON 스트리밍 — Full-stack 단독 구현
AWS BedrockSpring AIVirtual ThreadsNDJSONNext.jsFull-stackHexagonal
배경 및 제약조건
- •CS 운영팀이 고객 문의를 수동으로 분류하는 데 하루 수 시간 소요
- •1000건 순차 처리 시 30분 소요 — Bedrock rate limit으로 무한 병렬 불가
- •분류 진행 상황을 실시간으로 확인할 수 없으면 UX 불만
- •백엔드(Spring Boot) + 프론트(Next.js) Full-stack 단독 구현
설계 구조
전체 흐름
엑셀 업로드 → 미리보기(5건) → 분류 실행 → 실시간 스트리밍 → 엑셀 다운로드
[Next.js 16] sk-admin-web
├── useClassify() 커스텀 훅 — ReadableStream 파싱
├── PreviewTable (컬럼 + 샘플 확인)
├── ResultTable (실시간 진행률 + 건별 결과)
└── Feature-Sliced Design (entities/features/app)
[Spring Boot 4.0] sk-admin-app
├── QuestionClassifyController
│ ├── /preview → JSON (컬럼 + 샘플 5건)
│ ├── /classify → StreamingResponseBody + NDJSON
│ └── /export → xlsx binary
├── QuestionClassifyService (Application — 얇은 오케스트레이터)
├── ParallelClassifyExecutor (Domain Service)
│ └── Virtual Threads + Semaphore(5)
├── ClassifyRetryPolicy (재시도 3회, 2초 간격)
└── QuestionClassifier (Bedrock ChatClient, Claude Haiku 4.5)
NDJSON 메시지 포맷:
{"type":"meta","total":100}
{"type":"result","index":1,"data":{"csId":"CS001","level1":"배송",...}}
{"type":"done","success":98,"failed":2}Problem & Solution
Problem
1000건 순차 처리 30분 — Bedrock rate limit으로 무한 병렬도 불가
Solution
Java 25 Virtual Threads로 건별 경량 스레드 생성 + Semaphore(5)로 동시 호출 수 제한. Bedrock rate limit 보호하면서 처리 시간 5배 단축 (~3~4분)
Problem
수 분간 진행 상황을 알 수 없는 UX — SSE는 복잡, WebSocket은 과잉
Solution
StreamingResponseBody + NDJSON(줄 구분 JSON)으로 건별 결과를 실시간 전송. 프론트에서 ReadableStream으로 파싱하여 진행률 바 + 결과 테이블 즉시 렌더링
Problem
Spring AI 1.0의 Bedrock auto-configuration이 Boot 4.0.1과 호환되지 않아 ClassNotFoundException
Solution
auto-config 비활성화 + BedrockConfig에서 ChatClient 수동 빈 등록. retry/timeout/maxTokens 직접 설정
기술적 챌린지
엑셀 HTML 엔티티 처리
레거시 시스템 엑셀에 HTML 엔티티(&, <)가 포함되어 AI 분류 정확도 저하. 읽기/쓰기 양쪽에서 디코딩 처리
성과
- ✓CS 문의 분류 시간 대폭 단축 (1000건 기준 3~4분)
- ✓NDJSON 실시간 스트리밍으로 즉각적 UX
- ✓Virtual Threads + Semaphore로 Bedrock rate limit 보호 + 안정성
- ✓Full-stack 단독 구현 (백엔드 DDD/Hexagonal + 프론트 Next.js/FSD + AI Bedrock)