Skip to content

[Step2.3] hippo: TanStack Query 적용하기 - #7

Open
meteorqz6 wants to merge 9 commits into
hippo-adv-2.2from
hippo-adv-2.3
Open

[Step2.3] hippo: TanStack Query 적용하기#7
meteorqz6 wants to merge 9 commits into
hippo-adv-2.2from
hippo-adv-2.3

Conversation

@meteorqz6

@meteorqz6 meteorqz6 commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

개인 목표 달성 여부

  • 서버 상태와 클라이언트 상태를 구분하고, TanStack Query로 서버 상태를 분리한다.
  • useQuery, useMutation, QueryClient 개념을 직접 사용하며 TanStack Query 구조를 익힌다.
  • Optimistic Update를 구현하며 UX와 데이터 정합성 사이의 트레이드오프를 체감한다.

Summary by CodeRabbit

  • New Features

    • 서버 상태 관리를 위한 새 데이터 조회/추가 흐름이 도입되었습니다.
    • 개발 중 상태 확인을 돕는 도구가 추가되었습니다.
    • 필터 선택값이 새로고침 후에도 유지됩니다.
  • Bug Fixes

    • 음식점 목록 로딩 및 오류 처리가 더 안정적으로 개선되었습니다.
    • 추가 작업 시 즉시 반영 후 실패하면 이전 상태로 되돌아가도록 개선되었습니다.
  • Documentation

    • TanStack Query 사용법, 상태 분리 기준, 예시 코드와 학습 가이드가 새로 정리되었습니다.

@meteorqz6 meteorqz6 self-assigned this Jun 28, 2026
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Free

Run ID: 555b27cc-5184-4118-b5d6-33dbf4780f94

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Zustand 기반의 useRestaurantStore를 제거하고, 서버 상태는 TanStack Query(useRestaurantsQuery, useAddRestaurantMutation)로, 클라이언트 필터 상태는 새로운 useFilterStore(Zustand persist)로 분리했습니다. main.jsxQueryClientProviderReactQueryDevtools가 추가되었으며, 관련 README가 전면 교체/추가되었습니다.

Changes

TanStack Query 마이그레이션

Layer / File(s) Summary
패키지 및 QueryClientProvider 설정
package.json, src/main.jsx
@tanstack/react-query, @tanstack/react-query-devtools 의존성 추가 후, main.jsx에서 QueryClient 인스턴스를 생성하고 앱 트리를 QueryClientProvider로 래핑하며 ReactQueryDevtools를 포함합니다.
쿼리/뮤테이션 훅 및 필터 스토어 신규 구현
src/queries/useRestaurantsQuery.js, src/queries/useAddRestaurantMutation.js, src/store/useFilterStore.js
useRestaurantsQueryqueryKey: ["restaurants"]getRestaurants를 조회합니다. useAddRestaurantMutationcreateRestaurant를 호출하며 optimistic update(취소→스냅샷→임시 반영→롤백→무효화) 전체 흐름을 구현합니다. useFilterStoreselectedCategorysessionStorage에 persist합니다.
컴포넌트 및 App 마이그레이션
src/App.jsx, src/components/RestaurantList.jsx, src/components/AddRestaurantModal.jsx
AppselectedCategory 소스를 useFilterStore로 교체합니다. RestaurantList는 스토어+useEffect 대신 useRestaurantsQuery로 데이터를 가져오며, AddRestaurantModalmutation.mutate를 사용하고 성공/실패를 onSuccess/onError 콜백으로 처리합니다. useRestaurantStore는 전체 삭제됩니다.
문서 업데이트
README.md, 02-state-management-tools/2.3-TanStack Query/README.md
루트 README를 TanStack Query 학습 가이드(서버/클라이언트 상태 분리, optimistic update, useOptimistic 비교, Zustand와의 역할 비교)로 전면 교체하고, 미션 디렉토리에 신규 README를 추가합니다.

추정 코드 리뷰 노력

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

Comment @coderabbitai help to get the list of available commands.

@meteorqz6

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@meteorqz6 meteorqz6 changed the title Hippo adv 2.3 [Step2.3] hippo: TanStack Query 적용하기 Jun 28, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[배움]
useQuery, useMutationsrc/queries/ 폴더에 커스텀 훅으로 분리한 구조가 인상적이었습니다. 저는 컴포넌트 안에 직접 작성했는데, 이렇게 분리하면 같은 쿼리를 여러 컴포넌트에서 재사용할 때나 queryKey 변경 시 한 곳만 수정하면 된다는 장점이 있을 것 같습니다!

Comment thread src/queries/useRestaurantsQuery.js Outdated

export function useRestaurantsQuery() {
return useQuery({
queryKey: ["restaurants"],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[논의]
["restaurants"]useRestaurantsQuery.jsuseAddRestaurantMutation.js에 각각 직접 작성하셨는데, 저는 RESTAURANTS_QUERY_KEY 상수를 별도 파일로 분리했습니다. 쿼리 훅이 한 폴더에 모여있다면 파일 내 상수로도 충분히 관리될 것 같은데, 상수 파일 분리 없이 인라인으로 작성하신 이유가 궁금합니다. 실무에서는 "몇 군데서 참조하냐"에 따라 파일 내 상수 vs 별도 파일로 나누거나, 쿼리가 다양해지면 restaurantKeys.all, restaurantKeys.detail(id) 같은 팩토리 패턴으로 발전시키기도 하더라고요.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

별도의 상수 파일로 두면 좋을 것 같네요. 반영하겠습니다!

Comment on lines +17 to +23
mutation.mutate(
{ category, name, description },
{
onSuccess: () => onClose(),
onError: () => alert("음식점 추가에 실패했습니다. 다시 시도해주세요."),
},
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[논의]
onMutate에서 목록을 낙관적으로 업데이트하면서도 모달은 onSuccess(서버 응답 후)에서 닫히도록 구현하셨는데, 이 조합에서 목록에는 새 항목이 이미 보이는데 모달은 아직 열려있는 상태가 짧게 발생하지 않나요? 저는 낙관적 업데이트라면 모달도 onMutate에서 함께 닫는 게 일관성 있다고 판단해서 그렇게 구현했는데, 혹시 다르게 판단하신 이유가 있으신지 궁금합니다!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지적하신 부분은 맞습니다. 하지만 의도가 있는 구현이었는데 서버 실패 시 유저가 폼을 다시 채울 수 있게 하려고 했습니다. onMutate에서 모달을 먼저 닫아버리면 실패 시 유저가 입력했던 내용이 사라집니다. 하지만 onSuccess에서 모달을 닫으면 실패했을 때 모달이 그대로 남아 있어서 유저가 바로 수정하고 다시 제출할 수 있습니다.

Comment on lines +21 to +23
onError: (_err, _newRestaurant, context) => {
queryClient.setQueryData(["restaurants"], context.previousRestaurants);
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[배움]
onError: (_err, _newRestaurant, context) 처럼 미사용 파라미터에 _ 접두사를 각각 붙이셨는데, 저는 (err, _, context)처럼 _ 단독으로 썼습니다. _err, _newRestaurant 방식이 파라미터 자리 의미(어떤 인자인지)를 유지하면서 의도적으로 사용하지 않음을 명시적으로 표현한다는 점이 더 명확한 것 같아서 좋은 것 같습니다!

Comment thread src/components/RestaurantList.jsx Outdated
Comment on lines +18 to +19
{isLoading && <p>불러오는 중...</p>}
{error && <p>{error}</p>}
{error && <p>{error.message}</p>}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[제안]
로딩과 에러를 JSX 인라인으로 처리하셨는데, early return 패턴으로 바꾸면 로딩/에러 상태일 때 빈 리스트가 함께 렌더링되는 걸 방지할 수 있습니다.

if (isLoading) return <p>불러오는 중...</p>;
if (error) return <p>{error.message}</p>;

어떻게 생각하시나요?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

early return을 사용하는 방법이 있다는 것을 알려주셔서 감사합니다. 적용해보겠습니다!

Comment thread README.md

`onSuccess` 대신 `onSettled`를 쓴 이유는, 실패 후 롤백된 상태에서도 서버 데이터와 동기화가 필요하기 때문이다. `onSuccess`는 성공 시에만 실행되지만 `onSettled`는 성공/실패 상관없이 항상 실행된다.

### useOptimistic (React 19)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[배움]
README에 useOptimistic (React 19)과 TanStack Query Optimistic Update를 비교한 섹션이 인상적이었습니다. 저는 이 주제를 따로 학습하지 않았는데, Server Actions 환경에서는 롤백 코드 없이 자동 처리된다는 점이 흥미로웠습니다. 현재 미션 구조(REST API + fetch)에서는 TanStack Query가 더 자연스럽다는 것도 배워갑니다!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants