Setting(project): axios, tanstack query 세팅#65
Conversation
Walkthrough클라이언트와 익스텐션에 Axios 인스턴스와 TanStack React Query의 QueryClient 옵션 및 팩토리 모듈을 추가하고, 두 앱의 package.json에 Changes
Sequence Diagram(s)sequenceDiagram
actor Caller
participant Env as Environment
participant Factory as getQueryClient()
participant QC as QueryClient
Caller->>Factory: 호출
Factory->>Env: window 존재 여부 확인
alt Server (no window)
Factory->>QC: makeQueryClient() -> 새 QueryClient 생성
Factory-->>Caller: 새 인스턴스 반환
else Browser
opt 최초 호출
Factory->>QC: makeQueryClient() -> 새 QueryClient 생성
Factory->>Factory: browserQueryClient 캐시에 저장
end
Factory-->>Caller: 캐시된 인스턴스 반환
end
sequenceDiagram
actor UI
participant AX as apiRequest (Axios)
participant ENV as import.meta.env
UI->>AX: AX.get/post(...)
AX->>ENV: VITE_BASE_URL 사용하여 요청 구성
AX-->>UI: JSON 응답 반환
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks (3 passed, 2 warnings)❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (6)
apps/client/package.json (1)
13-14: React Query 버전(apps/client/package.json) 통일
client의@tanstack/react-query를 extension과 동일한^5.85.5로 맞춰 중복 설치 및 동작 차이를 방지하세요.- "@tanstack/react-query": "^5.85.3", + "@tanstack/react-query": "^5.85.5",추가로 루트
package.json의pnpm.overrides로 공통 버전을 고정하고,docs디렉터리에 버전 선정 근거를 간단히 기록하면 유지보수에 도움이 됩니다.apps/extension/package.json (1)
14-15: 의존성 정렬 확인extension은 ^5.85.5, client는 ^5.85.3입니다. 상호 의존 코드가 많아질수록 동일 버전 유지가 유리합니다. client 쪽을 ^5.85.5로 올리면 정렬됩니다.
apps/client/src/shared/apis/axiosInstance.ts (1)
1-10: apps/client/src/shared/apis/axiosInstance.ts: axios 인스턴스 하드닝 제안
- baseURL ENV 가드:
import.meta.env.VITE_BASE_URL미설정 시 경고 추가- timeout 10 000ms 기본 설정
Accept: application/json헤더 추가 (필요 시withCredentials주석 해제)적용 예시:
-import axios from 'axios'; +import axios from 'axios'; -const apiRequest = axios.create({ - baseURL: import.meta.env.VITE_BASE_URL, - headers: { - 'Content-Type': 'application/json', - }, -}); +const baseURL = import.meta.env.VITE_BASE_URL as string | undefined; +if (!baseURL) { + // eslint-disable-next-line no-console + console.warn('[axios] VITE_BASE_URL이 설정되지 않았습니다.'); +} + +const apiRequest = axios.create({ + baseURL, + timeout: 10000, + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + // withCredentials: true, // 필요 시 +});apps/client/src/shared/apis/query/queryClientOptions.ts (1)
1-12: QueryClient 옵션 타입 선언 추가
QueryClientConfig에 맞춰satisfies를 적용해 타입 안전성을 높이는 것을 권장합니다.적용 diff:
+import type { QueryClientConfig } from "@tanstack/react-query"; -const queryClientOptions = { +const queryClientOptions = { defaultOptions: { queries: { staleTime: 1000 * 60 * 5, retry: 1, refetchOnMount: false, refetchOnWindowFocus: false, }, }, -}; +} satisfies QueryClientConfig; export default queryClientOptions;apps/client/src/shared/apis/query/getQueryClient.ts (1)
4-17: 함수 시그니처와 브라우저 캐시 초기화 구문 간결화반환 타입 명시와 nullish 병합 할당으로 간결/명확하게 정리 가능합니다. 기능 변화 없이 가독성만 개선됩니다.
적용 diff:
-const makeQueryClient = () => { - return new QueryClient(queryClientOptions); -}; +const makeQueryClient = (): QueryClient => new QueryClient(queryClientOptions); let browserQueryClient: QueryClient | undefined; -const getQueryClient = () => { - if (typeof window === "undefined") { - return makeQueryClient(); - } else { - if (!browserQueryClient) browserQueryClient = makeQueryClient(); - return browserQueryClient; - } -}; +const getQueryClient = (): QueryClient => { + if (typeof window === "undefined") return makeQueryClient(); + browserQueryClient ??= makeQueryClient(); + return browserQueryClient; +};apps/extension/src/apis/query/getQueryClient.ts (1)
4-17: 동일한 미세 리팩터링(타입 명시 + 병합 할당) 적용 제안client와 동일한 스타일로 정리하면 두 구현이 더 쉽게 동기화됩니다.
적용 diff:
-const makeQueryClient = () => { - return new QueryClient(queryClientOptions); -}; +const makeQueryClient = (): QueryClient => new QueryClient(queryClientOptions); let browserQueryClient: QueryClient | undefined; -const getQueryClient = () => { - if (typeof window === "undefined") { - return makeQueryClient(); - } else { - if (!browserQueryClient) browserQueryClient = makeQueryClient(); - return browserQueryClient; - } -}; +const getQueryClient = (): QueryClient => { + if (typeof window === "undefined") return makeQueryClient(); + browserQueryClient ??= makeQueryClient(); + return browserQueryClient; +};
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
apps/client/package.json(1 hunks)apps/client/src/shared/apis/axiosInstance.ts(1 hunks)apps/client/src/shared/apis/query/getQueryClient.ts(1 hunks)apps/client/src/shared/apis/query/queryClientOptions.ts(1 hunks)apps/extension/package.json(1 hunks)apps/extension/src/apis/axiosInstance.ts(1 hunks)apps/extension/src/apis/query/getQueryClient.ts(1 hunks)apps/extension/src/apis/query/queryClientOptions.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.065Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
🔇 Additional comments (3)
apps/extension/src/apis/axiosInstance.ts (1)
1-10:.env*파일에서 VITE_BASE_URL 선언 여부 수동 확인 필요
저장소 내에서.env관련 파일이 검색되지 않아VITE_BASE_URL정의를 확인할 수 없습니다.apps/extension/package.json (1)
26-26: 플러그인 정상 등록 확인됨apps/extension/vite.config.ts의 plugins 배열에서
svgSpritePlugin({...})호출을 확인했습니다.apps/extension/src/apis/query/queryClientOptions.ts (1)
1-12: 타입 안전성 강화:satisfies QueryClientConfig적용 제안 유효
TS 5.9.2에서satisfies키워드 지원, 앱·익스텐션 쿼리 옵션 파일 간 diff 결과 동일(드리프트 없음) 확인.
@tanstack/react-query의존성이apps/extension및apps/client패키지에 동일 버전으로 선언됐는지 수동 검증 필요.
f868eea to
27a0457
Compare
|
✅ Storybook chromatic 배포 확인: |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
apps/client/src/shared/apis/query/getQueryClient.ts (3)
4-17: HMR(Next/Vite)에서 인스턴스 중복·메모리 누수 방지: globalThis에 캐시개발 중 Fast Refresh로 모듈이 재평가되면 module-scope 변수(
browserQueryClient)가 초기화되어 다수의 QueryClient가 생길 수 있습니다. 전역 캐시를 사용해 HMR 안전하게 만들고, 반환 타입을 명시해 타입 안정성도 함께 강화하는 것을 권장합니다.import { QueryClient } from "@tanstack/react-query"; import queryClientOptions from "./queryClientOptions"; -const makeQueryClient = () => { - return new QueryClient(queryClientOptions); -}; +const makeQueryClient = (): QueryClient => new QueryClient(queryClientOptions); -let browserQueryClient: QueryClient | undefined; +declare global { + // dev HMR 대응용 전역 캐시 + var __queryClient__: QueryClient | undefined; +} -const getQueryClient = () => { - if (typeof window === "undefined") { - return makeQueryClient(); - } else { - if (!browserQueryClient) browserQueryClient = makeQueryClient(); - return browserQueryClient; - } -}; +const getQueryClient = (): QueryClient => { + if (typeof window === "undefined") return makeQueryClient(); + if (!globalThis.__queryClient__) { + globalThis.__queryClient__ = makeQueryClient(); + } + return globalThis.__queryClient__; +};
10-17: 대안(현 구조 유지 시): early return로 간결화현재 방식 유지한다면 else 블록을 제거해 가독성만 정리하세요.
-const getQueryClient = () => { - if (typeof window === "undefined") { - return makeQueryClient(); - } else { - if (!browserQueryClient) browserQueryClient = makeQueryClient(); - return browserQueryClient; - } -}; +const getQueryClient = (): QueryClient => { + if (typeof window === "undefined") return makeQueryClient(); + if (!browserQueryClient) browserQueryClient = makeQueryClient(); + return browserQueryClient; +};
1-2: queryClientOptions의 타입 보증 추가 권장options 모듈에서
QueryClientConfig을satisfies로 보증하면 이후 옵션 변경 시 컴파일 타임에 잡힙니다(TS 4.9+ 필요).// apps/client/src/shared/apis/query/queryClientOptions.ts import type { QueryClientConfig } from "@tanstack/react-query"; const queryClientOptions = { defaultOptions: { queries: { staleTime: 1000 * 60 * 5, retry: 1, refetchOnMount: false, refetchOnWindowFocus: false, }, }, } satisfies QueryClientConfig; export default queryClientOptions;TS 버전(>=4.9)과 옵션 키가 v5의
QueryClientConfig와 일치하는지 확인 부탁드립니다.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
apps/client/package.json(1 hunks)apps/client/src/shared/apis/axiosInstance.ts(1 hunks)apps/client/src/shared/apis/query/getQueryClient.ts(1 hunks)apps/client/src/shared/apis/query/queryClientOptions.ts(1 hunks)apps/extension/package.json(1 hunks)apps/extension/src/apis/axiosInstance.ts(1 hunks)apps/extension/src/apis/query/getQueryClient.ts(1 hunks)apps/extension/src/apis/query/queryClientOptions.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
- apps/client/package.json
- apps/client/src/shared/apis/axiosInstance.ts
- apps/extension/src/apis/query/queryClientOptions.ts
- apps/extension/src/apis/axiosInstance.ts
- apps/extension/src/apis/query/getQueryClient.ts
- apps/client/src/shared/apis/query/queryClientOptions.ts
- apps/extension/package.json
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.065Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
🔇 Additional comments (1)
apps/client/src/shared/apis/query/getQueryClient.ts (1)
1-19: CSR 싱글톤 + SSR 신규 생성 패턴 적절브라우저/서버 분기와 싱글톤 전략이 타당합니다. 기본 동작 문제 없습니다.
| const getQueryClient = () => { | ||
| if (typeof window === "undefined") { | ||
| return makeQueryClient(); | ||
| } else { | ||
| if (!browserQueryClient) browserQueryClient = makeQueryClient(); |
There was a problem hiding this comment.
단순 질문!
SSR 환경인지 체크할라고 (typeof window === "undefined") 쓰는걸로 알고 있는데.! React 환경에서도 이 로직을 써야하는지 궁금합니다!
There was a problem hiding this comment.
아.
이.거...
맞아요... 참고하는 곳에서 이거까지 가져왔네요!!!!
해당 내용 수정하고 다시 올리겠습니다~
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
apps/extension/src/apis/query/getQueryClient.ts (2)
4-6: 반환 타입 명시 및 한 줄로 간결화 제안
타입을 명시해 API 계약을 분명히 하고, 구현을 간결화하면 가독성이 좋아집니다.-const makeQueryClient = () => { - return new QueryClient(queryClientOptions); -}; +const makeQueryClient = (): QueryClient => new QueryClient(queryClientOptions);
8-13: getQueryClient의 반환 타입 명시 (서비스 워커 환경 고려)
MV3 서비스 워커는 window가 없어 SSR 감지 로직(예: typeof window === "undefined")이 오작동할 수 있습니다. 이 파일에선 브라우저/확장 환경 전용 싱글톤 유지가 맞고, 반환 타입만 명시하면 충분합니다.-const getQueryClient = () => { +const getQueryClient = (): QueryClient => { if (!browserQueryClient) browserQueryClient = makeQueryClient(); return browserQueryClient; };옵션) 개발 중 HMR에서 캐시 유지가 필요하면 글로벌 캐시를 활용할 수 있습니다(확장 팝업/옵션 페이지 HMR에 유용).
// HMR-safe 캐시 (선택) const globalForQC = globalThis as unknown as { __pinbackQueryClient?: QueryClient }; if (import.meta?.env?.DEV && globalForQC.__pinbackQueryClient) { browserQueryClient = globalForQC.__pinbackQueryClient; } if (import.meta?.env?.DEV) { globalForQC.__pinbackQueryClient = getQueryClient(); }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/client/src/shared/apis/query/getQueryClient.ts(1 hunks)apps/extension/src/apis/query/getQueryClient.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/client/src/shared/apis/query/getQueryClient.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.065Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
🔇 Additional comments (1)
apps/extension/src/apis/query/getQueryClient.ts (1)
1-3: LGTM — 임포트와 옵션 사용이 일관적입니다.
현재 구조(옵션 모듈 분리 + 팩토리 + 싱글톤)가 명확합니다.
| import { QueryClient } from "@tanstack/react-query"; | ||
| import queryClientOptions from "./queryClientOptions"; | ||
|
|
||
| const makeQueryClient = () => { | ||
| return new QueryClient(queryClientOptions); | ||
| }; |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
queryClientOptions의 타입 지정 확인 요청
옵션 객체에 QueryClientConfig를 명시해 변경에 강하게 해두길 권장합니다(컴파일 타임 보장).
예상 형태(다른 파일):
// apps/extension/src/apis/query/queryClientOptions.ts
import type { QueryClientConfig } from "@tanstack/react-query";
const queryClientOptions: QueryClientConfig = {
defaultOptions: {
queries: {
// ...
},
},
};
export default queryClientOptions;다음 스크립트로 타입 지정 여부를 확인해 주세요.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# queryClientOptions.ts 위치 확인
fd -a 'queryClientOptions.ts'
# QueryClientConfig 타입 지정 여부 점검
rg -n -C2 'QueryClientConfig|queryClientOptions' $(fd -a 'queryClientOptions.ts')Length of output: 1266
queryClientOptions에 QueryClientConfig 타입 명시
- apps/extension/src/apis/query/queryClientOptions.ts
- 파일 상단에
import type { QueryClientConfig } from "@tanstack/react-query";추가 const queryClientOptions = { … }선언을
const queryClientOptions: QueryClientConfig = { … }로 변경하여 컴파일 타임 안전성 보장
- 파일 상단에
🤖 Prompt for AI Agents
In apps/extension/src/apis/query/getQueryClient.ts lines 1 to 6, the QueryClient
is created using queryClientOptions without compile-time typing; update the
options file apps/extension/src/apis/query/queryClientOptions.ts by adding
`import type { QueryClientConfig } from "@tanstack/react-query";` at the top and
change the declaration from `const queryClientOptions = { … }` to `const
queryClientOptions: QueryClientConfig = { … }` so the options are statically
typed and provide compile-time safety.
📌 Related Issues
📄 Tasks
⭐ PR Point (To Reviewer)
📌 구조
일단 모노레포의 장점을 살린다고 생각하면 packages에 공통 의존성과 설정을 해야한다고 생각했어요.
그래서 처음에는
이렇게 api-client라는 패키지를 생성하고, 여기서 두 라이브러리 의존성과 instance, queryClient를 위치시키려고 했어요.
하지만 여기서 드는 생각이 apps안에 있는 client와 extension이 같은 config를 공유할까? 였어요. 환경이 아예 다르고 특히나 extension은 조금 레거시한 설정들이 많아서 이 부분에서 설정이 달라질 수 있겠다고 판단했어요.
물론 최소한의 공통 속성만 packages에 빼두고 각 apps에서 config를 추가로 확장하면 되지 않을까도 생각해봤는데, 애초에 apps에서 useQuery등이나 interceptor와 같이 추가로 설정할 때 필요한 기능들이 각 apps에서도 의존성을 가지고 있어야 한다는 한계가 존재한다는 것을 생각했어요.
그러다보니 어차피 각 apps에서 의존성을 추가해야만 하고, 공통된 속성이 그렇게 많지 않아서 공통의 config를 두는 것이 그렇게 많은 장점을 가지고 있지 않겠다고 판단했어요. 오히려 많은 차이점이 생긴다면 분리하는 것이 좋겠다고 판단하기도 했구요.
그래서 최종적으로는 apps에서 각 의존성을 설치하고, queryClient와 instance를 설정하게 되었어요.
혹시나 좋은 의견이 있다면 공유해주세요!!
📌 queryClient에 사용한 싱글톤 패턴
간단하게 queryClient를 생성하는 로직에 싱글톤 패턴을 적용했어요. tanstack query에서 queryClient는 오직 하나만 존재해야 하기 때문에 이 상황이 싱글톤 패턴을 사용하면 좋겠다고 생각이 들었어요.
이렇게 구현해서 사용하는 곳에서 아래와 같이 get 함수로 받아서 사용하면 돼요!
그리고.... 스웨거 OAS로 type generate해주는 라이브러리 도입을 해야하는데.. 모노레포에서의 러닝 커브가 있어서(제가..조금 부족한 지식이 있어서 그런거 같아요) 일단 이대로 작업하고 이후에 추가로 넣어볼게요...!!
Summary by CodeRabbit