LabHub
배우기 러닝패스 코스

クジラを探したのに、猫が現れた

遅れて届いた応答を退ける検索hook

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

React+TypeScript 검색 hook을 직접 구현하고, 오래된 응답과 취소·재시도·예외를 검사합니다. 제공 UI에서 실제 React 화면을 보며 연습합니다. HTML 폼 작성과 긴 목록 최적화 전체를 이 한 실습으로 대신하지 않습니다.

왜 중요한가

성공한 응답도 현재 사용자의 의도와 다르면 잘못된 화면입니다. 취소는 자원 회수이고 결과 무시는 화면의 정확성입니다. 두 책임을 분리하면 취소를 지원하지 않는 비동기 도구도 시험할 수 있습니다. Promise의 거절과 호출 시 throw 역시 다른 경로입니다. 함수·클로저·Promise·TypeScript 유니온과 기본 React hook을 읽을 수 있어야 합니다.

준비

처음 한 번 node /opt/react-workbench/prepare.mjs /root/react-search-lab lesson을 실행하세요. 이미 존재하면 기존 사본을 계속 사용하세요. 지우거나 덮어쓰지 않습니다. 런타임 npm install은 필요 없습니다. 수정 파일은 /root/react-search-lab/src/useSearch.ts입니다. 함께 제공된 src/types.ts에서 Query(text, revision), Search(query, signal), SearchState의 다섯 상태 계약을 읽으세요. src/types.ts와 제공 search의 동작은 바꾸지 않습니다. 단계 예시는 최초 틀이며 정답이 아닙니다.

단계

  1. 검색을 보내기 전과 기다릴 때를 나눈다 — lesson 모드로 /root/react-search-lab 사본을 준비하세요. src/useSearch.ts에 useSearch(request, search)를 export하고 SearchState를 반환합니다. 빈 request.text는 idle, 비어 있지 않으면 loading과 해당 query입니다. 입력을 비워 새로 제출하면 idle로 돌아옵니다. 응답 처리는 다음 단계입니다.
  2. 성공·빈 결과·실패에 다른 안내를 준다 — 주어진 search(request.text, controller.signal)를 호출하세요. 1개 이상 결과는 success와 원래 items, 0개는 empty, Error 거절은 error와 error.message입니다. 각 상태에 해당 query를 유지하고, 실패 후 새 검색은 loading을 거쳐 성공할 수 있어야 합니다. search를 자체 구현으로 교체하지 마세요.
  3. 고양이가 고래 화면을 빼앗지 못하게 한다 — 여러 검색을 제출하고 응답을 역순으로 완료해도 최신 제출의 결과만 남게 하세요. 최신 요청이 실패한 뒤 오래된 성공이 도착해도 최신 오류를 유지합니다. 취소를 무시하는 전송에서도 성립해야 합니다.
  4. 오래된 장애 안내도 버린다 — 최신 성공 뒤 오래된 실패, 최신 실패 뒤 더 오래된 실패가 와도 화면을 바꾸지 못하게 하세요. 최신 요청의 Error.message는 그대로 보존합니다. 앞 단계 성공 응답 보호도 유지합니다.
  5. 필요 없는 요청을 정리한다 — 새 제출로 effect가 바뀔 때 이전 요청의 signal.aborted가 true가 되게 하세요. 최신 요청은 필요할 동안 false여야 하고, 컴포넌트 해제 시 true여야 합니다. 취소 여부와 늦은 응답 무시를 각각 유지합니다.
  6. 같은 단어의 새 의도를 구별한다 — Query의 revision이 달라진 동일 검색도 새로운 요청이어야 합니다. 같은 단어의 두 요청을 역순으로 완료하면 새 결과만 남습니다. 빈 제출은 idle로 돌아가고 이전 응답이 결과를 복원하지 못해야 합니다.
  7. Promise를 받기도 전에 난 오류를 처리한다 — search가 Promise를 반환하기 전에 Error를 던져도 React 화면 전체가 죽지 않고 error와 그 message를 표시하게 하세요. 이후 새 검색으로 정상 복구할 수 있어야 합니다. 비동기 거절·취소·경합 계약도 유지합니다.
  8. 설정·정리를 반복하고 전체 계약을 재검사한다 — 개발 StrictMode의 설정→정리→재설정에서 첫 요청은 취소되고, 첫 응답이 늦게 와도 두 번째 결과가 남는지 검사하세요. 전체 채점으로 1~7단계와 이 조건을 함께 확인합니다. npm run typecheck와 npm run build 후 3000 포트 미리보기에서 역순 성공·실패·빈 결과·재시도를 직접 확인하세요.

참고

작업 폴더에서 npm run typecheck, npm run build, npm start 순으로 실행하면 http://127.0.0.1:3000/ 미리보기를 볼 수 있습니다. 마지막 /를 유지하세요. 소스를 바꾸면 다시 빌드하고 미리보기를 새로 고칩니다. 시작 단계에서는 아직 응답이 표시되지 않을 수 있습니다. 수동 배달은 HTTP가 아닌 결정적 Promise 모형입니다. 실제 HTTP 비교 화면에서는 실습 서버의 합성 자료를 받습니다. 채점은 학생 hook의 strict 타입과 실제 React+jsdom 상태를 검사하며 학생 파일을 변경하지 않습니다. 이 검사는 실제 브라우저 레이아웃·성능·스크린리더 사용성 검증을 대신하지 않습니다. 각 단계는 앞 단계도 누적 검사합니다. 128KiB 이하 일반 소스 파일, 작업 프로세스 12초 제한을 적용합니다. 마지막 회귀 단계는 이전 정답으로 통과할 수 있습니다. 새 기능 대신 기존 계약을 재검증하기 때문입니다. 필요하면 +시간으로 세션을 연장하고 종료 전에 수정 소스와 관찰 기록을 따로 저장하세요. 세션 종료 후 파일은 유지되지 않습니다.

검색을 보내기 전과 기다릴 때를 나눈다

lesson 모드로 /root/react-search-lab 사본을 준비하세요. src/useSearch.ts에 useSearch(request, search)를 export하고 SearchState를 반환합니다. 빈 request.text는 idle, 비어 있지 않으면 loading과 해당 query입니다. 입력을 비워 새로 제출하면 idle로 돌아옵니다. 응답 처리는 다음 단계입니다.

useState의 초기값과 useEffect에서 제출된 검색을 처리하는 일을 나눠 보세요. 검색창에 입력 중인 draft를 직접 읽지 않습니다.

성공·빈 결과·실패에 다른 안내를 준다

주어진 search(request.text, controller.signal)를 호출하세요. 1개 이상 결과는 success와 원래 items, 0개는 empty, Error 거절은 error와 error.message입니다. 각 상태에 해당 query를 유지하고, 실패 후 새 검색은 loading을 거쳐 성공할 수 있어야 합니다. search를 자체 구현으로 교체하지 마세요.

AbortController로 signal을 만들고 Promise의 then과 catch를 연결하세요. empty는 장애가 아니라 정상 응답의 한 종류입니다.

고양이가 고래 화면을 빼앗지 못하게 한다

여러 검색을 제출하고 응답을 역순으로 완료해도 최신 제출의 결과만 남게 하세요. 최신 요청이 실패한 뒤 오래된 성공이 도착해도 최신 오류를 유지합니다. 취소를 무시하는 전송에서도 성립해야 합니다.

각 effect 설정 안에서 만들어진 유효성 값을 성공 콜백이 확인하게 하고, 정리에서 그 값만 만료시키세요. 모든 요청이 공유하는 true/false 하나는 다시 켜질 수 있습니다.

오래된 장애 안내도 버린다

최신 성공 뒤 오래된 실패, 최신 실패 뒤 더 오래된 실패가 와도 화면을 바꾸지 못하게 하세요. 최신 요청의 Error.message는 그대로 보존합니다. 앞 단계 성공 응답 보호도 유지합니다.

then만 보호하면 catch는 여전히 화면을 바꿀 수 있습니다. 두 경로가 같은 effect의 유효 기간을 읽는지 확인하세요.

필요 없는 요청을 정리한다

새 제출로 effect가 바뀔 때 이전 요청의 signal.aborted가 true가 되게 하세요. 최신 요청은 필요할 동안 false여야 하고, 컴포넌트 해제 시 true여야 합니다. 취소 여부와 늦은 응답 무시를 각각 유지합니다.

정리 함수 안에서 이 effect가 만든 controller만 취소하세요. 결과 무시만으로 외부 작업이 멈추지는 않습니다.

같은 단어의 새 의도를 구별한다

Query의 revision이 달라진 동일 검색도 새로운 요청이어야 합니다. 같은 단어의 두 요청을 역순으로 완료하면 새 결과만 남습니다. 빈 제출은 idle로 돌아가고 이전 응답이 결과를 복원하지 못해야 합니다.

의존성에 검색 문자열만 있으면 같은 단어의 재제출을 놓칩니다. request는 제출마다 새 객체라는 제공 UI의 계약을 활용하세요.

Promise를 받기도 전에 난 오류를 처리한다

search가 Promise를 반환하기 전에 Error를 던져도 React 화면 전체가 죽지 않고 error와 그 message를 표시하게 하세요. 이후 새 검색으로 정상 복구할 수 있어야 합니다. 비동기 거절·취소·경합 계약도 유지합니다.

search(...).catch(...)에서는 search 자체의 throw가 catch에 닿지 않습니다. 호출을 Promise 콜백 안으로 옮기거나 try/catch로 두 경로를 함께 다뤄 보세요.

설정·정리를 반복하고 전체 계약을 재검사한다

개발 StrictMode의 설정→정리→재설정에서 첫 요청은 취소되고, 첫 응답이 늦게 와도 두 번째 결과가 남는지 검사하세요. 전체 채점으로 1~7단계와 이 조건을 함께 확인합니다. npm run typecheck와 npm run build 후 3000 포트 미리보기에서 역순 성공·실패·빈 결과·재시도를 직접 확인하세요.

마지막 단계는 새 플래그를 더하는 과제가 아니라 누적 구현의 회귀 검사입니다. 개발 StrictMode의 호출 횟수를 운영에서도 같다고 가정하지 마세요.