resumate 06. 프론트 전면 개편 — 컨텍스트 바, 보관함, 면접 연습
사이드카에는 인증, 보관함, PDF 내보내기, OCR, 음성이 이미 다 붙어 있다. 그런데 화면에서 그걸 쓸 방법이 없었다. 오늘은 그 기능들을 쓸 화면을 만들었다.
무엇을 넘길지 담을 필드부터 넓혔다
먼저 agentStore의 SharedContext를 넓혔다.
| 필드 | 담는 것 |
|---|---|
attachments | 캐릭터별 첨부 파일 |
resumeVersions | 이력서 버전 이력 |
contextSelections | 캐릭터별로 어떤 자료를 넘길지 |
contextSelections가 오늘의 핵심이다. 설계 원칙 첫 줄인 "명시적인 자료 전달"을 코드로 옮긴 것이다.
API 키 설정 화면을 지웠다
ApiKeySetup.tsx를 삭제했다. 34줄짜리 파일 하나 지운 건데, 이 프로젝트에서는 꽤 상징적인 변경이었다.
이 프로젝트는 Pro 구독료를 이미 냈는데 API 비용을 또 내야 하냐는 데서 시작했다. 그런데 정작 앱에는 API 키를 입력받는 화면이 남아 있었다. 파일 히스토리를 보니 프로젝트 첫날부터 있었고, 그 뒤로 손댄 건 4월 리브랜딩 때 문자열 하나가 전부였다. 넉 달 동안 아무도 안 건드린 화면이 앱의 첫인상 자리에 있었다.
대신 AuthBadge를 만들었다. 사이드카의 auth.ts에게 물어봐서 로그인 상태를 보여주고, 로그인이 안 돼 있으면 로그인 시작 버튼을 띄운다. 사용자가 할 일은 키를 발급받아 붙여넣는 게 아니라 claude CLI에 로그인하는 것뿐이다. 이게 이 앱의 정체성이라서 화면에서도 그렇게 보여야 했다.
컨텍스트 바: 무엇을 넘길지 사용자가 고른다
오늘 만든 것 중 제일 고민이 길었던 부분이다.
문제 상황은 이렇다. Paws에게 "이 공고에 맞춰 이력서 고쳐줘"라고 하면 Paws는 무엇을 알아야 하나? 이력서는 당연히 필요하고 채용공고도 필요하다. Nova가 정리한 프로젝트 경험은 넣으면 좋지만 항상은 아니다. Echo의 면접 기록은 대개 필요 없다.
원래는 이걸 자동으로 처리했다. 대화 히스토리를 요약해서 프롬프트 앞에 붙이는 방식이다. 동작은 했는데 두 가지가 문제였다. 사용자가 뭐가 들어갔는지 모른다는 것, 그리고 필요 없는 자료까지 들어가서 토큰을 쓴다는 것.
그래서 뒤집었다. 프로젝트, 이력서, 채용공고, 면접 기록을 체크박스로 늘어놓고 사용자가 직접 고르게 했다.
| 컴포넌트 | 역할 |
|---|---|
ContextBar | 어떤 자료를 넘길지 선택 |
ResumeSourcePicker | 어느 이력서를 쓸지 지정 |
ExportControls | 첨삭본 생성 / PDF 내보내기 |
ResumeSourcePicker를 따로 둔 이유가 있다. 이력서가 한 개가 아니게 됐기 때문이다. Nyx에 등록한 기본 이력서, 캐릭터별로 첨부한 파일, 첨삭해서 저장한 버전 중 어느 걸 쓸지 매번 고를 수 있어야 했다.
AI에게 자동으로 판단시키는 것보다 이게 낫다고 봤다. 사용자는 자기 이력서를 나보다 잘 안다.
응답이 끊기면 기다리지 않는다
사이드카 클라이언트에 인증·보관함·음성 API를 붙이면서 무응답 타임아웃도 같이 넣었다.
스트리밍 응답이 중간에 조용히 멎는 경우가 있다. 에러도 안 나고 종료 이벤트도 안 온다. 그럼 프론트엔드는 로딩 스피너를 영원히 돌린다.
마지막 이벤트가 온 시점부터 시간을 재고, 일정 시간 아무것도 안 오면 요청을 abort한다. 이벤트가 하나 올 때마다 타이머를 리셋하니까, 응답이 길어서 오래 걸리는 것과 아예 끊긴 것을 구분할 수 있다.
면접 연습은 질문·답변·평가가 한 세트여야 했다
사이드카에 만들어둔 보관 저장소를 화면에 연결했다. 올린 원본을 목록으로 보고, 다시 다운로드하고, 지울 수 있다. 414줄이 들어갔다.
면접 연습은 Echo에 붙인 새 패널이다. 질문을 고르고, 답변을 텍스트로 쓰거나 녹음하고, 피드백을 받는다. 녹음한 건 사이드카의 whisper 전사 경로를 탄다.
Echo의 기존 화면은 질문 목록만 보여주고 답변은 채팅으로 하게 돼 있었는데, 면접 연습은 그거보다 구조가 있어야 했다. 질문에서 답변, 평가까지가 한 세트로 묶여야 다시 볼 때 의미가 있다.
에이전트 패널 네 개를 이 참에 다 뜯어고쳤다. 1600줄 넘게 바뀌었다.
캐릭터 위치를 맞추려고 개발용 튜너를 만들었다
홈, 파일트리, 채팅 패널, 캐릭터 독, 전역 스타일까지 손봤다.
캐릭터 애니메이션 위치를 맞추는 게 은근히 성가셔서 개발용 튜너를 따로 만들었다. 캐릭터 크기와 위치를 슬라이더로 조정해보고 값을 확인하는 화면이다. 프로덕션 빌드에는 안 들어간다.
이런 도구를 만드는 게 시간 낭비처럼 느껴질 때가 있는데, 캐릭터 네 개의 위치를 코드 고치고 빌드하고 확인하는 걸 반복하는 것보다는 확실히 빨랐다.
기능이 늘면서 권한도 늘었다
면접 답변을 녹음하려면 마이크 권한이 필요했고, 독과 작업창 위치를 맞추려면 창 위치·크기 권한이 필요했다. 사이드카와 OCR 헬퍼는 바이너리 리소스로 앱에 포함시켰다.
같이 고친 버그가 둘 있다. 앱 종료 시 사이드카를 정리하지 않는 문제, 그리고 OCR 헬퍼 경로를 사이드카에게 안 넘겨주던 문제다. OCR 헬퍼 경로는 사이드카 바이너리 옆에 두고 파일명만 바꿔서 찾게 했다. 개발 모드와 패키징된 앱에서 경로가 달라지는데, 둘 다 "사이드카 옆"이라는 규칙은 같아서다.
알아서 해주는 만큼 사용자는 모르게 된다
오늘 지운 코드 중에 ApiKeySetup.tsx가 제일 기분 좋았다. 34줄짜리인데, 그게 남아 있는 동안은 앱이 자기 정체성과 다른 말을 하고 있었다.
컨텍스트를 자동으로 판단하지 않고 사용자에게 고르게 한 것도 비슷한 결정이었다. AI 앱을 만들다 보면 이것도 알아서 해주면 좋지 않을까로 기울기 쉬운데, 알아서 해주는 만큼 사용자는 무슨 일이 일어나는지 모르게 된다. 자기 이력서가 어디로 가는지는 알아야 한다고 생각했다.
화면은 다 붙었지만 테스트가 하나도 없다. AI가 자료에 없는 내용을 지어내는 것도 그대로고, 프롬프트가 개발 직군 자료를 전제하고 있는 것도 아직 안 건드렸다.