← 프로젝트 목록으로

Colio-Catch

Plers · 2026.02 – 2026.07

Frontend · FE 2 / BE 1 / 기획 / 디자인

AI 에이전트 대화 기반 채용 스크리닝 B2B SaaS

ReactTypeScriptTanStack QueryZustandTailwindEmotionFSD

구현 기능

  • 채팅 리스트 리렌더링 최적화 (커밋 40.3ms → 27.1ms)
  • 연속 검색 경쟁 상태(race condition) 수정
  • 멀티턴 대화형 검색 설계 (/search → /search/refine)
  • AI 에이전트 대화 히스토리 · 입력 UI
  • 후보자 프로필 카드 및 트렌딩 지원자 위젯 UI
  • 휴대폰 인증 기반 비밀번호 재설정 플로우

트러블슈팅

1. 채팅 리스트 리렌더링 최적화

문제
  • 후보자 카드 클릭으로 선택 상태만 바뀌어도 전체 메시지 리스트가 다시 렌더링됐습니다.
원인
  • 메시지 렌더링 JSX가 map 콜백 안에 그대로 인라인되어 있어 선택 상태가 바뀔 때마다 화면의 모든 메시지 블록이 함께 리렌더됐습니다.
해결
  • 응답 블록을 AgentMessageBlock 컴포넌트로 분리해 React.memo로 감쌌습니다.
  • 선택 핸들러를 useCallback으로 안정화해 memo가 실제로 동작하도록 했습니다.
  • 다른 카드를 클릭해도 나머지 메시지가 재렌더되지 않는지 검증하는 회귀 테스트를 추가했습니다.
개선 전 · 렌더 커밋 40.3ms, 5~6개 카드 리렌더
개선 후 · 렌더 커밋 27.1ms, 1개 카드만 리렌더
src/widgets/agent-chatting/ui/AgentMessageBlock.tsx
export const AgentMessageBlock = memo(AgentMessageBlockBase);
src/widgets/agent-chatting/ui/AgentChattingView.tsx
const handleSelectCandidate = useCallback((id: string) => {
  setSelectedCandidates((prev) => toggle(prev, id));
}, []);
AgentChattingView.test.tsx
// 카드 클릭 시 다른 메시지 블록은 재렌더되지 않아야 한다
expect(added).toBeLessThan(4);

결과: 리렌더 커밋 40.3ms → 27.1ms, 재렌더되는 카드 5~6개 → 1개

2. 연속 검색 경쟁 상태 수정

문제
  • 빠르게 연속으로 검색할 때 먼저 보낸 느린 요청의 응답이 나중에 도착해 최신 결과를 덮어쓰는 문제가 있었습니다.
원인
  • 이전 요청을 취소하거나 응답 순서를 검증하지 않고, 도착하는 대로 상태에 그대로 반영하고 있었습니다.
해결
  • 레이스 컨디션을 재현하는 회귀 테스트로 먼저 검증했습니다.
  • 요청마다 AbortController로 이전 요청을 취소하고, 요청 시퀀스 번호를 비교해 최신 응답만 반영하도록 가드를 추가했습니다.
src/entities/agent-chat/model/useChatStore.ts
inFlightController?.abort();
inFlightController = new AbortController();
const seq = ++requestSeq;

const result = await search(query, { signal: inFlightController.signal });
if (seq !== requestSeq) return; // 더 최신 요청이 이미 진행 중이면 무시

applyResult(result);

결과: 연속 검색 시 늦게 도착한 이전 응답이 최신 결과를 덮어쓰는 문제 해결