Code › tail-villain

진행 중인 세션으로 돌아가기

진행 중인 세션을 기록이 아닌 재개 가능한 작업으로 다루기 위한 상태 정리

히스토리에서 active session을 눌렀는데, 사용자는 이어서 학습하는 화면이 아니라 과거 리포트를 보는 화면으로 들어가고 있었다.

히스토리 카드의 이동 경로를 고치는 것만으로는 부족했다. active 세션을 과거 리포트로 보내던 원인은 제품이 세션 상태의 의미를 일관되게 사용하지 않는 데 있었다.

active는 기록이 아니었다. 아직 진행 중인 일이다.


tail-villain에서 세션은 채팅방 하나보다 조금 더 무겁다. 사용자는 토픽을 고르고, 코치나 빌런과 대화하고, 중간 상태가 쌓이며, 마지막에 리포트가 만들어진다. 그래서 히스토리는 단순한 로그 목록이 아니라 지금 이어서 해야 할 일과 이미 끝난 일을 구분하는 화면이어야 했다.

그런데 기존 흐름에는 이 구분이 섞여 있었다. 어떤 인터뷰 세션은 새 세션이 시작되면서 기존 active 세션을 abandoned로 밀어냈고, 어떤 흐름은 active session을 재사용했다. 사용자 입장에서는 둘 다 세션 기록으로 보이지만, 백엔드 의미는 달랐다. 더 나쁜 건 abandoned가 사용자가 선택한 상태가 아니라 구현 과정에서 생긴 상태였다는 점이다.

사용자가 세션을 포기한 게 아닌데, 제품이 포기한 것처럼 보이면 신뢰가 깨진다.

그래서 같은 사용자와 같은 학습 대상에 active lane이 이미 있으면 새로 만들지 않고 기존 세션으로 돌아가게 했다. 동시에 여러 요청이 들어와도 active 세션이 여러 개 생기지 않도록 데이터베이스 쪽에 uniqueness 조건을 두고, 충돌이 나면 이긴 세션을 다시 읽어 반환하도록 했다. 단순한 중복 방지 작업이라기보다, 사용자가 잠깐 나갔다 돌아왔을 때 제품이 지금 하던 일을 기억하는지에 대한 문제였다.


히스토리 화면도 같은 기준으로 바꿔야 했다.

completed 세션은 이미 대화가 끝났고 결과가 만들어졌기 때문에 리포트로 보내는 게 맞다. 하지만 active 세션에는 아직 최종 결과가 없고, 사용자가 필요한 것도 분석 화면이 아니라 이어서 말할 수 있는 실행 화면이다.

4월 25일 작업에서는 Study History의 active 항목을 실행 화면으로 직접 연결했다. 혹시 사용자가 live session URL이 아니라 report URL로 직접 들어오더라도 서버 쪽에서 상태를 확인하고 실행 화면으로 돌려보내며, 상태 판별이 끝나기 전에는 본문을 렌더링하지 않도록 막았다. 화면이 잠깐 리포트 skeleton을 보여준 뒤 이동하는 것도 active session에는 맞지 않았기 때문이다.

active 카드가 다시 대화로 이어지면 사용자는 진행 중인 작업을 계속할 수 있다. 같은 카드가 리포트로 열리면 제품은 아직 끝나지 않은 일을 완료된 기록으로 취급하게 된다.


진행률도 손봐야 했다.

세션을 완료할 때 모든 milestone을 100%로 밀어 올리는 로직이 있었고, 보기에는 깔끔했다. 완료한 세션에는 완료된 리포트가 나오며 진행률도 꽉 차 보이지만, 학습 제품에서는 위험한 친절이다.

대화를 마쳤다고 해서 사용자가 모든 내용을 완전히 이해한 건 아니다. 어떤 milestone은 충분히 설명했을 수 있고, 어떤 milestone은 아직 흔들릴 수 있다. 세션 완료는 대화의 종료이지, 모든 학습 목표의 자동 정복이 아니다.

그래서 completion report는 대화 중 실제로 쌓인 progress를 보존하도록 바꿨다. 사용자가 끝까지 왔다는 사실과 각 milestone이 얼마나 단단해졌는지는 분리해서 보여줘야 했다. 진짜로 60%인 부분을 100%로 포장하면 당장은 기분이 좋을지 몰라도, 다음 복습에서 제품이 무엇을 믿고 질문해야 하는지 흐려진다.

truthful progress는 차갑게 보일 수 있지만, 학습 도구에는 그쪽이 더 낫다.


4월 22일의 대시보드 개편도 이 흐름과 붙어 있었다. 대시보드는 단순 리스트에서 벗어나 다음에 무엇을 해야 하는지 더 빨리 보이게 만드는 방향으로 바뀌었다. 버튼 label도 사용자의 세션 상태에 맞춰 시작하기와 계속하기를 구분해야 했다.

이때의 핵심은 화면을 더 화려하게 만드는 일이 아니었다. 사용자가 보는 단어와 실제 상태가 맞아야 했다. active인데 과거 기록처럼 보이면 안 되고, completed인데 계속하기처럼 보여도 안 된다. 로드맵 카드, 히스토리 모달, 세션 화면, 리포트 화면이 모두 같은 상태 의미를 공유해야 사용자가 망설이지 않는다.

Study coach prompt도 비슷한 기준으로 정리했다. 코치가 대답을 반복하거나 “볼까요?” 같은 허락을 구하는 말로 끝내면, 세션은 이어지는 듯하지만 실제 학습은 흐려진다. 그래서 follow-up question은 짧은 이유와 함께 구체적인 다음 질문이나 과제를 던지도록 바꿨다. 세션이 이어진다는 건 URL만 재개되는 게 아니라, 대화의 방향도 이어져야 한다.


돌아보면 이 작업은 히스토리 UI를 고친 일이 아니라 세션의 시간성을 정리한 일이었다.

active session은 현재이고, completed session은 과거다. abandoned는 구현 과정에서 생긴 내부 상태였고, 100% progress는 실제 학습 상태와 무관하게 제품이 만들어낸 낙관이었다.

AI 학습 제품은 사용자를 격려해야 하지만, 상태까지 좋게 꾸며서 보여주면 안 된다. 어디까지 왔는지, 무엇이 아직 진행 중인지, 무엇이 실제로 끝났는지를 정확히 보여줘야 다음 질문도 정확해진다.

진행 중인 세션으로 돌아가게 만든 건 그래서 라우팅 수정이 아니었다. 제품이 사용자의 시간을 제대로 기억하게 만드는 일이었다.