본문으로 건너뛰기

리서치 AI 분석과 전달 구조 설계

단계적 LLM 분석과 SSE 기반 전달로 리서치 AI의 품질과 사용성을 함께 설계했습니다.

이 요약은 AI가 원문을 분석해 생성했습니다. 정확한 내용은 원문 기준으로 확인하세요.

TL;DR

우아한형제들은 설문 자유 응답과 인터뷰·UT 녹취록을 분석하는 사내 리서치 AI 도우미를 구축하면서 결과 생성과 결과 전달을 별도의 문제로 다뤘습니다. 분석 품질을 안정화하기 위해 응답 다듬기, 중분류 통합, 대분류 통합으로 LLM 작업을 나누고, Agentic RAG로 조사와 관련된 유저골만 골라 프롬프트에 넣었으며, Langfuse와 연구자 평가를 연결해 개선 지점을 추적했습니다. 분석 시간이 길어지는 문제에는 job_id 기반 SSE 스트림과 progress·completed·failed·cancelled·heartbeat 이벤트를 적용하고, Web Audio API로 완료 알림음을 만들었습니다. 결과가 나온 뒤에는 분석 결과를 근거로 질문하는 채팅을 붙이고, 세션을 첫 질문 직전에 만들며, React 바깥의 서비스 레이어에서 여러 탭의 답변 스트림을 유지했습니다. 글의 핵심은 LLM 프롬프트 한 문장보다 작업 단계·평가 루프·비동기 사용자 경험을 함께 설계해야 분석 결과가 실제 인사이트로 이어진다는 점입니다.

빠른 이해

새로운 점

LLM 분석 파이프라인과 비동기 프론트엔드 전달 경험을 함께 설계하고, job_id를 평가 추적·SSE 스트림·후속 채팅의 공통 연결점으로 사용한 점입니다.

핵심 메커니즘

자료와 조사 목적을 입력받아 응답 정제→중분류 통합→대분류 통합을 수행하고, 조사 주제와 Summary Point로 관련 유저골을 선별해 다음 LLM 단계에 주입합니다. 분석 작업은 job_id를 발급한 뒤 SSE로 진행 이벤트를 전송하며, 완료된 결과는 같은 식별자를 기준으로 채팅 세션과 답변 스트림에 연결합니다.

섹션별 상세

01

리서치 AI 도우미의 분석 범위

우아한형제들의 사용자경험분석팀은 수천 명의 설문 자유 응답을 이해하기 쉬운 카테고리로 분류하고 인터뷰·사용성 테스트 녹취록을 요약하는 리서치 AI 도우미를 구축했습니다. 리서처가 설문 응답 엑셀이나 녹취록과 조사 목적을 입력하면 서버의 LLM이 자료를 처리하고 파일과 요약 데이터를 반환하는 흐름입니다. 설문조사 분석은 대규모 응답에서 전체 경향을 찾고, 인터뷰·UT 분석은 사용자의 발화와 행동 맥락을 파악하는 데 초점을 둡니다. 실제 구축 과정에서는 결과를 만드는 일과 사용자가 결과를 받아보는 일이 서로 다른 설계 문제로 드러났으며, 두 문제를 각각 분석 파이프라인과 프론트엔드 경험으로 나누어 해결했습니다.
02

LLM 작업을 단계로 나눈 이유

자유 응답을 한 번에 의미 있는 코드로 분류하자 같은 의미의 문장이 서로 다른 코드로 갈리고 코드의 추상화 수준도 뒤섞였습니다. 예를 들어 배달이 늦다는 두 응답이 배송 속도 불만과 시간 관련 이슈로 나뉘자, LLM이 처리할 입력 크기와 판단 단계를 줄였습니다. 먼저 ‘배달 너무 늦어요 ㅠㅠ’를 ‘배달 지연’처럼 다듬고, ‘무료체험 종료’와 ‘무료배달이 유료로 변경됨’을 중분류에서 통합한 뒤, ‘멤버십 비용 부담’과 ‘최소주문금액 부담’을 대분류인 ‘비용 부담’으로 묶습니다. 사람이 자료를 읽고 기준을 세운 다음 기준에 맞춰 정리하는 순서를 파이프라인에 반영함으로써 응답 정제와 코드 통합의 기준을 분리했습니다.
python
user_goal_df = pd.read_csv(...)
filtered_df = filter_relevant_user_goals(
    user_goal_df, summary_points=summary_points, topic=topic, max_rows=200,
)
return filtered_df.to_markdown(index=False)

전체 유저골 목록에서 조사 주제와 Summary Point에 관련된 행을 선별하고, 선택 결과만 Markdown 표로 변환합니다.

03

리서처 지식과 평가 루프 연결

UT 설계 가이드와 리서처가 정리한 유저골 목록을 파일로 관리하고 필요한 노드의 프롬프트에 표 형태로 주입해, 질문 순서와 유도 가능성 같은 현업 지식을 LLM의 판단 문맥으로 사용했습니다. 유저골 목록 전체를 넣으면 토큰 한도를 넘고 조사와 무관한 목표가 해석에 섞일 수 있어, 조사 주제와 Summary Point를 기준으로 관련 행만 고르는 Agentic RAG를 적용했습니다. 선택 단계에서는 행 번호만 반환하고 최대 200개 행을 다음 노드에 넘겨 불필요한 재작성과 문맥 크기를 줄였습니다. 정답 코드가 하나로 고정되지 않는 LLM 특성 때문에 리서처가 실제 데이터의 결과를 정성·정량적으로 평가하고, Langfuse에 job_id와 사용자 ID를 연결해 단계별 입출력을 되짚으며 프롬프트를 수정하는 루프를 만들었습니다.
typescript
export class JobEventStream {
  private eventSource: EventSource | null = null

  start(): void {
    const url = `${getBaseURL()}/jobs/${this.jobId}/stream`
    this.eventSource = new EventSource(url)

    this.eventSource.addEventListener('progress', (e) => {
      this.handleEvent(e, 'onProgress')
    })

    this.eventSource.addEventListener('completed', (e) => {
      this.handleEvent(e, 'onCompleted')
      this.close() // 완료되면 스트림을 닫아요
    })

    // failed, cancelled, heartbeat ...
  }
}

EventSource 연결을 만들고 분석 진행·완료 이벤트를 상태 관리 함수에 전달하며, 완료 시 스트림을 닫습니다.

typescript
const oscillator = audioContext.createOscillator()
const gainNode = audioContext.createGain()

oscillator.type = 'sine' // 사인파
oscillator.frequency.value = frequency // 음 높이

// 부드럽게 시작하고(Attack) 자연스럽게 사라지도록(Decay)
gainNode.gain.linearRampToValueAtTime(0.25, now + 0.03)
gainNode.gain.exponentialRampToValueAtTime(0.01, now + duration)

oscillator.connect(gainNode)
gainNode.connect(audioContext.destination)

oscillator.start(now) // now = audioContext.currentTime
oscillator.stop(now + duration)

Oscillator로 사인파를 만들고 Gain에서 음량을 조절한 뒤 오디오 출력 목적지로 연결해 완료 알림음을 생성합니다.

04

SSE와 알림음으로 긴 분석 전달

자료량에 따라 분석이 몇 분에서 몇 시간 걸리므로 스피너만 두면 사용자는 작업 중단 여부와 잔여 시간을 알기 어렵고 다른 탭으로 이동한 뒤 완료 사실을 놓치기 쉽습니다. 분석 시작 POST 요청으로 job_id를 발급하고, 브라우저의 EventSource가 GET만 지원한다는 제약에 맞춰 job_id 기반 SSE 연결을 별도로 열어 서버에서 클라이언트로 진행 이벤트를 전송했습니다. progress는 현재 단계와 진행 상태를 갱신하고 completed는 결과를 전달하며 failed·cancelled·heartbeat는 오류·중단·연결 상태를 처리하는 구조입니다. 화면을 떠난 사용자를 위해 완료 시 Web Audio API의 AudioContext에서 Oscillator, Gain, Destination을 순서대로 연결해 코드로 만든 알림음을 재생했고, 음 높이·길이·볼륨을 파일 없이 조절했습니다.
typescript
async ensureSession(conversationId, ...) {
  const existing = this.sessions.get(conversationId)

  // 세션이 있고 만료 전이면 그대로 사용해요
  if (existing && !this.checkSessionExpiry(existing.expiresAt)) return

  // 없거나 만료됐으면 새로 발급받아요
  const response = await conversationAPI.startConversation({ ... })
  this.sessions.set(conversationId, { ... })
}

질문을 보내기 직전에 기존 대화 세션의 만료 시각을 확인하고, 없거나 만료된 경우에만 새 세션을 발급합니다.

05

결과 이후의 채팅과 세션 관리

파일을 내려받아 열어본 뒤 다시 원본 응답을 찾아야 하는 불편을 줄이기 위해, 완료된 분석의 job_id를 근거로 질문할 수 있는 채팅 인터페이스를 결과 화면에 붙였습니다. 사용자가 ‘배달 지연 불만이 제일 많다는데 어떤 응답들인가’처럼 후속 질문을 보내면 LLM이 분석 결과를 문맥으로 사용하고, 기존 JobEventStream을 답변 스트리밍에도 재사용합니다. 채팅 세션은 모든 방을 미리 만들지 않고 첫 질문 직전에 만료 시각을 확인해 살아 있으면 재사용하고 없거나 만료되면 새로 발급하므로 불필요한 컨텍스트 보관과 만료 화면을 함께 줄였습니다. UI 컴포넌트, 여러 대화와 메시지를 보관하는 conversationStore, 세션과 스트림을 관리하는 ConversationSessionService를 분리해 탭을 옮겨도 답변이 계속 스토어에 쌓이도록 구성했습니다.
06

모델 바깥의 서비스 설계

이 사례에서 분석 결과의 품질은 프롬프트 문장 하나보다 사람이 일하는 순서에 맞춘 단계 분리, 리서처 지식의 선택적 주입, 평가 결과를 다시 반영하는 추적 구조에서 좌우됐습니다. 결과 전달은 서버 작업을 progress 이벤트로 드러내고, 완료 시점을 알림음으로 알리며, 결과 파일을 후속 질문의 출발점으로 바꾸는 방식으로 구성했습니다. SSE는 진행 상태와 답변 스트림을 연결하고, 세션 서비스는 화면 생명주기와 독립적으로 여러 대화의 작업을 유지했습니다. 따라서 LLM이 생성한 결과를 실제 업무의 인사이트로 연결하려면 모델 호출뿐 아니라 비동기 처리, 상태 보관, 세션 수명, 사용자의 다음 행동까지 하나의 흐름으로 설계해야 합니다.

용어 해설

에이전틱 검색 증강 생성(Agentic RAG)
LLM이 전체 자료를 한꺼번에 받는 대신 현재 문제와 관련된 항목을 직접 선별한 뒤, 선택한 자료만 다음 처리 단계의 문맥으로 주입하는 방식입니다. 이 글에서는 수백 행의 유저골 목록에서 조사 주제와 Summary Point에 맞는 행만 골라 토큰 사용량과 무관한 정보의 혼입을 줄입니다.
서버 전송 이벤트(SSE)
HTTP 연결을 유지하면서 서버가 클라이언트로 이벤트를 계속 보내는 단방향 통신 방식입니다. 클라이언트가 별도 반복 요청을 하지 않아도 분석 진행률과 완료·실패 상태를 받을 수 있어, 서버 작업의 현재 상태를 실시간으로 전달하는 데 쓰입니다.
EventSource API
브라우저에서 SSE 스트림을 구독하는 API입니다. 이 글의 구조에서는 분석 시작 POST 요청으로 job_id를 받은 뒤 해당 식별자로 GET 기반 스트림을 열고, progress·completed·failed 같은 이벤트별 처리 함수를 연결합니다.
Web Audio API
브라우저에서 오디오를 코드로 생성하고 재생하는 API입니다. AudioContext 안에서 Oscillator로 파형과 음 높이를 만들고 Gain으로 음량 변화를 조절한 뒤 Destination으로 보내므로 별도 mp3 파일 없이 완료 알림음을 구현할 수 있습니다.
Langfuse
LLM 호출 이력과 단계별 입출력을 추적하고 전체 성능을 관찰하는 도구입니다. 글에서는 분석 하나의 job_id를 추적 세션 ID로, 사내 계정 정보를 사용자 ID로 연결해 어떤 분석의 어느 단계에서 문제가 생겼는지 확인하는 데 활용합니다.

기술

  • LLM
  • Agentic RAG
  • Python
  • Langfuse
  • SSE
  • Server-Sent Events
  • EventSource API
  • Web Audio API
  • AudioContext API
  • React
  • React 바깥 서비스 클래스
  • conversationStore
  • ConversationSessionService

언급된 리소스

AI 분석 전체 내용 보기

AI 요약 · 북마크 · 개인 피드 설정 — 무료

출처 · 인용 안내

수집 2026. 09. 04.출처 타입 WEB

인용 시 "요약 출처: AI Trends (aitrends.kr)"를 표기하고, 사실 확인은 원문 보기 기준으로 진행해 주세요. 자세한 기준은 운영 정책을 참고해 주세요.