Code › tail-villain

끝난 면접이 다음 연습이 되기까지

면접 결과를 토픽 진행도와 복습 흐름으로 연결하며 드러난 상태 모델링 문제

면접이 끝났는데, 제품은 아직 끝난 면접을 제대로 쓰지 못하고 있었다.

대화는 저장됐고, 점수와 피드백도 있었다. 화면으로 다시 들어가면 어떤 답변을 했는지도 볼 수 있었다. 그런데 그 결과가 다음 연습으로 자연스럽게 이어지지는 않았다. 사용자는 한 번 면접을 보고 나왔지만, 시스템 입장에서는 그 사람이 어떤 토픽을 어느 정도 통과했는지, 언제 다시 봐야 하는지, 무엇을 약점으로 남겼는지가 아직 선명하지 않았다.

이 상태로 기능을 더 붙이면 이상한 방향으로 커질 것 같았다. 질문을 더 잘 만들고, 화면을 더 예쁘게 만들고, 캐릭터를 더 늘려도 연습 루프가 닫히지 않으면 결국 매번 새로 시작하는 모의면접에 가까웠기 때문이다.

그래서 새 기능을 늘리기보다 끝난 면접이 다음 연습으로 이어지는 길을 먼저 잡기로 했다.


테일빌런에서 면접 세션은 단순한 채팅 로그가 아니다. 사용자가 특정 토픽을 골라서 빌런 면접관과 대화하고, 답변의 빈틈을 드러내고, 마지막에 피드백을 받는 하나의 연습 단위다. 그렇다면 세션이 끝났을 때 토픽도 같이 변해야 한다.

예를 들어 “분산 환경에서 캐시 일관성을 어떻게 다뤘는가” 같은 토픽을 연습했다고 해보자. 답변이 꽤 괜찮았다면 일주일 뒤에 다시 봐도 될 수 있고, 핵심 트레이드오프를 놓쳤다면 하루 뒤에 다시 꺼내야 할 수 있다. 점수만 저장해두면 나중에 보기에는 편하지만, 실제 연습을 이끌어주지는 못한다.

그래서 토픽 쪽에 면접 결과를 남기기 시작했다. 최신 점수, 답변 품질, 요약, 강점, 빈틈, 마지막 연습 시각, 다음 복습 시각을 저장했다. 점수가 높으면 복습 간격을 조금 길게 잡고, 애매하면 며칠 뒤로, 약하면 바로 다시 보게 하는 단순한 규칙도 넣었다.

복잡한 알고리즘은 아니었다. 처음부터 완벽한 간격 반복 시스템을 만들려고 하면 오히려 제품의 감각을 잃기 쉽다. 지금 필요한 건 “이 토픽은 끝났다”가 아니라 “이 토픽은 이렇게 끝났고, 다음에는 이 정도 시점에 다시 보면 좋다”는 최소한의 기억 데이터였다.


이 데이터가 생기자 대시보드의 역할도 달라졌다.

기존 대시보드는 로드맵과 테스트 상태를 보여주는 쪽에 가까웠다. 그런데 연습 결과가 토픽에 붙기 시작하면 대시보드는 단순한 입구가 아니라 오늘 무엇을 다시 봐야 하는지 알려주는 화면이 되어야 한다. 전체 토픽이 몇 개인지, 그중 몇 개를 연습했는지, 지금 다시 봐야 할 토픽이 무엇인지가 더 중요해졌다.

그래서 review-now 토픽과 weak 토픽을 따로 보여주도록 바꿨다. 지금 복습할 것과 약한 것을 한눈에 보여주면 사용자는 다음 행동을 덜 고민해도 된다. 연습 도구에서 이 차이는 꽤 크다. 사용자가 “뭐 하지?”를 매번 직접 판단해야 하면 결국 덜 쓰게 되기 때문이다.

토픽 카드에도 면접 진행 요약을 붙였고, 토픽별 면접 기록을 다시 볼 수 있는 히스토리 다이얼로그를 만들었다. 다만 끝난 세션을 다시 열 수는 있어도 수정되지는 않게 했다. 이미 끝난 면접은 당시의 기록으로 남아야 한다. 과거 대화를 다시 이어붙이기 시작하면 점수와 피드백의 의미도 흐려진다.

이건 개발 편의보다 사용자 입장에서 더 자연스러운 모델이었다. 끝난 시험지를 다시 고쳐서 제출하지 않듯이, 끝난 면접도 다시 볼 수는 있지만 그 기록 자체를 바꾸지는 않는 편이 맞았다.


면접 흐름 안에서도 작은 문제가 있었다.

AI 면접관이 “좋습니다, 다음으로 넘어가죠”라고 말했는데 실제 다음 질문을 주지 않는 경우가 있었다. 사람끼리 대화라면 잠깐 이상하고 넘어갈 수 있지만, 제품 안에서는 바로 멈춘 화면이 된다. 사용자는 다음에 무엇을 입력해야 할지 모른다.

처음에는 텍스트를 보고 질문처럼 생겼는지 판단하는 식으로 처리할 수 있다고 생각했다. 물음표가 있는지, 문장 모양이 질문에 가까운지 보는 방식이다. 하지만 면접관이 자연어로 말하는 순간 그런 규칙은 금방 흔들린다. “이 부분을 더 설명해보세요”는 물음표가 없어도 질문이고, “좋은 질문입니다.”는 물음표가 있어도 사용자에게 던지는 질문이 아니다.

그래서 면접관 응답에 다음 질문을 구조화해서 받도록 바꿨다. nextQuestion이라는 필드가 있으면 프론트엔드는 그걸 기준으로 다음 입력 상태를 만들 수 있다. 모델이 자연스럽게 말하는 것도 중요하지만, 제품이 반드시 보장해야 하는 부분은 자연어 분위기에 맡기면 안 된다.

수동 완료 흐름도 손봤다. 사용자가 “면접 완료”를 누르면 그냥 닫히는 게 아니라 마지막 AI 평가를 거쳐서 세션이 끝나게 했다. 그래야 완료 화면이 단순 종료 상태가 아니라, 이번 연습의 결과를 정리해주는 화면이 된다.


이 작업을 하면서 인증 쪽도 같이 손을 좀 보았다.

사용자의 세션과 토픽 데이터는 항상 조심해서 다뤄야 하는데, 확인해보니 에이전트가 localStorage에 refresh token을 저장하려고 했다. 이 방식은 XSS(Cross-Site Scripting) 공격에 취약하기 때문에 httpOnly cookie에 저장하도록 했다.


중간중간 깨진 것도 많았다.

토픽 진행도 필드를 추가하자 Prisma 타입과 실제 데이터베이스 스키마가 어긋났고, 마이그레이션이 빠진 상태에서는 로드맵 API가 500을 냈다. 대시보드 카운트가 전부 0으로 보이기도 했다. 리스트 응답에 토픽 진행도 데이터가 포함되지 않았으니, 프론트가 아무리 계산해도 숫자가 나올 수 없었다.

완료 화면에서는 같은 피드백이 두 번 보였다. 기능을 붙이다 보면 이런 중복은 은근히 자주 생긴다. 백엔드는 요약과 전체 피드백을 각각 주고, 프론트는 둘 다 친절하게 보여주려고 하다가 결과적으로 같은 말을 반복하는 식이다. 그래서 중복되는 내용은 걷어내고, 자세한 피드백은 펼쳐서 보게 했다.

이런 문제들은 대단한 알고리즘 버그는 아니지만, 제품의 신뢰를 많이 깎는다. 사용자는 내부 구조를 모른다. 그냥 숫자가 0으로 보이고, 같은 말이 두 번 나오고, 다음 질문이 없으면 “아직 덜 만들어졌구나”라고 느낀다.


이날 작업을 지나고 나서 테일빌런의 중심이 조금 더 분명해졌다.

AI가 질문을 잘하는 것만으로는 부족하다. 좋은 질문은 한 번의 대화를 만들 수 있지만, 좋은 연습은 이전 결과가 다음 행동으로 이어질 때 생긴다. 어떤 토픽을 했고, 어디가 약했고, 언제 다시 봐야 하는지 제품이 기억해야 한다.

채팅 로그는 기록이고, 토픽 진행도는 방향이다. 둘 다 필요하지만 역할은 다르다. 기록만 있으면 사용자가 다시 해석해야 하고, 방향만 있으면 왜 그런 판단이 나왔는지 믿기 어렵다. 끝난 면접을 읽을 수 있게 남기고, 동시에 다음 복습으로 이어지게 만드는 이유가 거기에 있었다.

결국 면접 연습 도구는 “대화를 생성하는 제품”이 아니라 “연습이 쌓이게 하는 제품”이어야 한다. 빌런 면접관이 날카롭게 질문하는 것도 중요하지만, 그 질문을 지나간 뒤 사용자가 조금이라도 더 나은 상태로 돌아오게 만드는 게 더 중요하다.