Fix(estension): extension api v3 update및 edit error 해결#302
Fix(estension): extension api v3 update및 edit error 해결#302jjangminii merged 7 commits intodevelopfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Walkthrough확장 프로그램에서 저장된 아티클 조회 흐름을 articleId 기반으로 도입하고, article 상세 API(v3) 훅을 추가하여 MainPop에 Changes
Sequence DiagramsequenceDiagram
participant App
participant SavedHook as useGetArticleSaved
participant DetailHook as useGetArticleDetail
participant API as axios:getArticleDetail
participant MainPop
App->>SavedHook: 요청 (savedArticle)
SavedHook-->>App: savedArticle?.data
alt 저장된 아티클 있음
App->>App: articleId = savedArticle?.data?.id
App->>DetailHook: 호출(articleId)
DetailHook->>API: GET /api/v3/articles/{articleId}
API-->>DetailHook: articleDetail
DetailHook-->>App: articleDetail
App->>MainPop: render(savedData=articleDetail)
else 저장된 아티클 없음
App->>MainPop: render(without savedData)
end
alt 중복 감지
App->>App: DuplicatePop 표시/포커스 조정
end
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~30 minutes Possibly Related PRs
Suggested Labels
Suggested Reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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 |
|
✅ Storybook chromatic 배포 확인: |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (2)
packages/design-system/src/components/toast/Toast.tsx (1)
4-5:text와children모두 없는 경우 방어 코드 고려두 prop이 모두 optional이므로, 둘 다 제공되지 않으면 빈 Toast가 렌더링됩니다. 의도된 동작이라면 무시하셔도 되지만, 최소 하나의 콘텐츠를 요구하려면 타입 레벨에서 제약을 추가하는 것이 좋습니다.
♻️ 타입 제약 추가 제안
// 최소 하나의 콘텐츠를 강제하려면: export type ToastProps = | { text: string; children?: React.ReactNode } | { text?: string; children: React.ReactNode };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/design-system/src/components/toast/Toast.tsx` around lines 4 - 5, The Toast component currently allows both text and children to be optional which permits rendering an empty Toast; change the ToastProps type to require at least one content prop by replacing the current optional prop shape with a discriminated union that enforces either text or children be present (refer to ToastProps, text, children and the Toast component) and update any call sites or default props accordingly so type-checking prevents both from being undefined.apps/extension/src/apis/query/queries.ts (1)
66-71: Non-null assertion 사용 개선 제안
articleId!는enabled: !!articleId조건으로 보호되어 런타임에는 안전하지만, 타입 시스템에서 명시적으로 처리하면 더 안전합니다.♻️ 명시적 타입 처리 제안
export const useGetArticleDetail = (articleId?: number) => { return useQuery({ queryKey: ['articleDetail', articleId], - queryFn: () => getArticleDetail(articleId!), + queryFn: () => getArticleDetail(articleId as number), enabled: !!articleId, }); };또는 조건부 실행을 더 명확히:
export const useGetArticleDetail = (articleId?: number) => { return useQuery({ queryKey: ['articleDetail', articleId] as const, queryFn: async () => { if (!articleId) throw new Error('articleId is required'); return getArticleDetail(articleId); }, enabled: !!articleId, }); };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/extension/src/apis/query/queries.ts` around lines 66 - 71, The useGetArticleDetail hook uses a non-null assertion (articleId!) inside queryFn; instead remove the assertion and make queryFn explicitly guard the value or narrow its type: in useGetArticleDetail wrap queryFn in an async function that first checks if (!articleId) throw new Error('articleId is required') before calling getArticleDetail(articleId), keep enabled: !!articleId and the queryKey ['articleDetail', articleId] (optionally as const) so the runtime guard satisfies TypeScript and avoids the non-null assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/extension/src/apis/axios.ts`:
- Line 64: Remove the leftover debug comment "이거 오류남 확인 요망" from the axios
module source (search for that exact string in apps/extension/src/apis/axios.ts)
so no debug/troubleshooting comments remain in production code; if any related
temporary console.logs or commented-out code were added for debugging around the
axios setup (e.g., in the axios instance creation or interceptors), delete them
as well or convert them to proper logging using the module's logger functions.
- Around line 69-72: putArticle currently returns the whole Axios response and
spreads the payload, causing inconsistency with other APIs; update putArticle to
call apiRequest.put(`/api/v1/articles/${articleId}`, data) (remove the
unnecessary `{ ...data }` spread) and return response.data to match
postArticle/getArticleSaved behavior so callers can access the payload
uniformly.
In `@apps/extension/src/App.tsx`:
- Around line 41-42: Remove the debug console.log statements in App.tsx that
print savedArticle and savedArticle?.data?.articleId (the two console.log lines)
before merging; either delete those lines or replace them with an appropriate
logger guarded by an environment-based debug flag (e.g., process.env.NODE_ENV
!== 'production') if runtime debug output is needed, and ensure no leftover
console.log calls referencing savedArticle remain in the component.
- Around line 12-13: The code inconsistently reads savedArticle?.data?.id into
articleId but logs savedArticle?.data?.articleId — confirm which field the API
returns (id or articleId) and use that same property everywhere (update
articleId extraction and any logging to match); also remove the non-null
assertion when calling useGetArticleDetail (change
useGetArticleDetail(articleId!) to useGetArticleDetail(articleId)) since the
hook already supports an optional articleId and guards itself via enabled:
!!articleId.
- Line 53: useGetArticleDetail currently returns data that can be undefined;
update useGetArticleDetail to declare an explicit return type (e.g., { data?:
ArticleResponse; isLoading: boolean; isError: boolean; error?: Error }) and
ensure callers respect that shape; in the component, check isLoading and isError
from useGetArticleDetail and render a loading indicator or error UI instead of
passing undefined to MainPop, and only pass savedData={articleDetail ?? null}
(or conditionally render <MainPop /> when articleDetail is available) so
savedData matches MainPop's expected ArticleResponse | null type.
In `@apps/extension/src/pages/MainPop.tsx`:
- Around line 109-113: Remove the leftover debug console.log statements in the
MainPop React component: delete the two console.log calls that print
'savedData?.categoryResponse?.categoryName' and savedData (and the similar
console.log around line 119) inside the MainPop.tsx component (search for
console.log usages in the MainPop function or component render body); if you
need to keep debug output for development, replace them with a conditional
debug/logger check (e.g., use a dev-only flag or a logger utility) instead of
raw console.log.
---
Nitpick comments:
In `@apps/extension/src/apis/query/queries.ts`:
- Around line 66-71: The useGetArticleDetail hook uses a non-null assertion
(articleId!) inside queryFn; instead remove the assertion and make queryFn
explicitly guard the value or narrow its type: in useGetArticleDetail wrap
queryFn in an async function that first checks if (!articleId) throw new
Error('articleId is required') before calling getArticleDetail(articleId), keep
enabled: !!articleId and the queryKey ['articleDetail', articleId] (optionally
as const) so the runtime guard satisfies TypeScript and avoids the non-null
assertion.
In `@packages/design-system/src/components/toast/Toast.tsx`:
- Around line 4-5: The Toast component currently allows both text and children
to be optional which permits rendering an empty Toast; change the ToastProps
type to require at least one content prop by replacing the current optional prop
shape with a discriminated union that enforces either text or children be
present (refer to ToastProps, text, children and the Toast component) and update
any call sites or default props accordingly so type-checking prevents both from
being undefined.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 39aa16b2-91af-4f85-9ed3-2dd10e143763
⛔ Files ignored due to path filters (2)
packages/design-system/src/icons/source/check_circle.svgis excluded by!**/*.svgpackages/design-system/src/icons/source/notsave_circle.svgis excluded by!**/*.svg
📒 Files selected for processing (7)
apps/extension/src/App.tsxapps/extension/src/apis/axios.tsapps/extension/src/apis/query/queries.tsapps/extension/src/pages/MainPop.tsxapps/extension/src/types/types.tspackages/design-system/src/components/toast/Toast.tsxpackages/design-system/src/icons/iconNames.ts
| const response = await apiRequest.put(`/api/v1/articles/${articleId}`, { | ||
| ...data, | ||
| }); | ||
| return response; |
There was a problem hiding this comment.
putArticle 반환값 일관성 문제
다른 API 함수들(postArticle, getArticleSaved 등)은 모두 response.data를 반환하지만, putArticle만 response 전체를 반환합니다. 이로 인해 호출 측에서 응답 데이터 접근 시 혼란이 발생할 수 있습니다.
또한 { ...data } 스프레드는 여기서 불필요합니다. Axios는 전달받은 객체를 직렬화하므로 원본이 변경되지 않습니다.
🐛 일관성 있는 반환값 수정 제안
export const putArticle = async (
articleId: number,
data: PutArticleRequest
) => {
- const response = await apiRequest.put(`/api/v1/articles/${articleId}`, {
- ...data,
- });
- return response;
+ const response = await apiRequest.put(`/api/v1/articles/${articleId}`, data);
+ return response.data;
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const response = await apiRequest.put(`/api/v1/articles/${articleId}`, { | |
| ...data, | |
| }); | |
| return response; | |
| const response = await apiRequest.put(`/api/v1/articles/${articleId}`, data); | |
| return response.data; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/extension/src/apis/axios.ts` around lines 69 - 72, putArticle currently
returns the whole Axios response and spreads the payload, causing inconsistency
with other APIs; update putArticle to call
apiRequest.put(`/api/v1/articles/${articleId}`, data) (remove the unnecessary `{
...data }` spread) and return response.data to match postArticle/getArticleSaved
behavior so callers can access the payload uniformly.
| const articleId = savedArticle?.data?.id; | ||
| const { data: articleDetail } = useGetArticleDetail(articleId!); |
There was a problem hiding this comment.
articleId 추출 및 사용 불일치
Line 12에서 savedArticle?.data?.id로 articleId를 추출하고, Line 42에서는 savedArticle?.data?.articleId를 로깅합니다. API 응답에서 올바른 필드가 무엇인지 확인하고 일관되게 사용해주세요.
또한 articleId! non-null assertion은 articleId가 undefined일 때 타입 에러를 숨깁니다. useGetArticleDetail 훅 내부에서 enabled: !!articleId로 보호되어 있어 런타임에는 안전하지만, 명시적으로 처리하는 것이 좋습니다.
🐛 수정 제안
const { data: savedArticle } = useGetArticleSaved(url);
- const articleId = savedArticle?.data?.id;
- const { data: articleDetail } = useGetArticleDetail(articleId!);
+ const articleId = savedArticle?.data?.articleId; // 또는 id - API 응답 확인 후 결정
+ const { data: articleDetail } = useGetArticleDetail(articleId);useGetArticleDetail이 이미 articleId?: number를 받으므로 non-null assertion이 불필요합니다.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const articleId = savedArticle?.data?.id; | |
| const { data: articleDetail } = useGetArticleDetail(articleId!); | |
| const articleId = savedArticle?.data?.articleId; | |
| const { data: articleDetail } = useGetArticleDetail(articleId); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/extension/src/App.tsx` around lines 12 - 13, The code inconsistently
reads savedArticle?.data?.id into articleId but logs
savedArticle?.data?.articleId — confirm which field the API returns (id or
articleId) and use that same property everywhere (update articleId extraction
and any logging to match); also remove the non-null assertion when calling
useGetArticleDetail (change useGetArticleDetail(articleId!) to
useGetArticleDetail(articleId)) since the hook already supports an optional
articleId and guards itself via enabled: !!articleId.
| /> | ||
| ) : ( | ||
| <MainPop type={mainPopType} /> | ||
| <MainPop type={mainPopType} savedData={articleDetail} /> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: articleDetail 데이터 흐름 및 타입 확인
echo "=== MainPop savedData prop 타입 확인 ==="
rg -n "savedData\?:" apps/extension/src/pages/MainPop.tsx -A2
echo ""
echo "=== useGetArticleDetail 반환 타입 확인 ==="
rg -n "useGetArticleDetail" apps/extension/src/apis/query/queries.ts -A10Repository: Pinback-Team/pinback-client
Length of output: 481
🏁 Script executed:
#!/bin/bash
echo "=== App.tsx의 useGetArticleDetail 사용 부분 ==="
rg -n "useGetArticleDetail\|articleDetail" apps/extension/src/App.tsx -B5 -A5
echo ""
echo "=== getArticleDetail 함수의 반환 타입 ==="
rg -n "getArticleDetail" apps/extension/src/apis/query/queries.ts -B2 -A8
echo ""
echo "=== ArticleResponse 타입 정의 확인 ==="
rg -n "type ArticleResponse\|interface ArticleResponse" apps/extension/src -A5Repository: Pinback-Team/pinback-client
Length of output: 558
🏁 Script executed:
#!/bin/bash
echo "=== App.tsx 전체 내용 확인 ==="
cat -n apps/extension/src/App.tsx | head -80
echo ""
echo "=== ArticleResponse 타입 정의 위치 찾기 ==="
rg -l "ArticleResponse" apps/extension/src/
echo ""
echo "=== getArticleDetail 함수 정의 찾기 ==="
fd -e ts -e tsx apps/extension/src/apis | xargs rg -l "getArticleDetail"Repository: Pinback-Team/pinback-client
Length of output: 3027
🏁 Script executed:
#!/bin/bash
echo "=== ArticleResponse 타입 정의 ==="
cat -n apps/extension/src/types/types.ts | head -100
echo ""
echo "=== getArticleDetail 및 useGetArticleDetail 전체 구현 ==="
sed -n '1,100p' apps/extension/src/apis/query/queries.ts | cat -nRepository: Pinback-Team/pinback-client
Length of output: 2894
🏁 Script executed:
#!/bin/bash
echo "=== getArticleDetail 함수 정의 (axios.ts) ==="
rg -n "getArticleDetail" apps/extension/src/apis/axios.ts -B2 -A10
echo ""
echo "=== Axios 응답 타입 구조 ==="
rg -n "export.*getArticleDetail\|^export const getArticleDetail\|return.*getArticleDetail" apps/extension/src/apis/axios.ts -B5 -A15Repository: Pinback-Team/pinback-client
Length of output: 335
articleDetail 타입과 로딩/에러 상태 처리 확인 필요
useGetArticleDetail에서 반환되는 data는 쿼리 완료 전 undefined이므로, articleDetail의 타입은 ArticleResponse | undefined입니다. 하지만 MainPop의 savedData prop은 ArticleResponse | null을 기대합니다.
다음을 개선해주세요:
useGetArticleDetail에 명시적 반환 타입 추가articleDetail이undefined일 때의 처리 (로딩 상태 확인 및 조건부 렌더링)- 쿼리 실패 시 에러 상태 처리
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/extension/src/App.tsx` at line 53, useGetArticleDetail currently returns
data that can be undefined; update useGetArticleDetail to declare an explicit
return type (e.g., { data?: ArticleResponse; isLoading: boolean; isError:
boolean; error?: Error }) and ensure callers respect that shape; in the
component, check isLoading and isError from useGetArticleDetail and render a
loading indicator or error UI instead of passing undefined to MainPop, and only
pass savedData={articleDetail ?? null} (or conditionally render <MainPop /> when
articleDetail is available) so savedData matches MainPop's expected
ArticleResponse | null type.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
apps/extension/src/apis/axios.ts (1)
64-72:⚠️ Potential issue | 🟠 Major
putArticle만 반환 shape가 달라집니다.여기서만
AxiosResponse전체를 반환하면 같은 모듈의 다른 API 래퍼들과 계약이 달라져 호출부가 쉽게 깨집니다.response.data만 반환하도록 맞추고,{ ...data }스프레드도 제거하는 편이 안전합니다.수정 예시
export const putArticle = async ( articleId: number, data: PutArticleRequest ) => { - const response = await apiRequest.put(`/api/v1/articles/${articleId}`, { - ...data, - }); - return response; + const response = await apiRequest.put(`/api/v1/articles/${articleId}`, data); + return response.data; };#!/bin/bash # putArticle의 반환 shape가 같은 모듈의 다른 래퍼와 다른지 확인하고, # 호출부가 payload를 기대하는지 점검합니다. sed -n '1,90p' apps/extension/src/apis/axios.ts | nl -ba rg -nP --type=ts '\bputArticle\s*\(' -g '!apps/extension/src/apis/axios.ts' -C2🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/extension/src/apis/axios.ts` around lines 64 - 72, The putArticle wrapper returns the full AxiosResponse which differs from other API wrappers; change putArticle (the function using apiRequest.put) to send data directly (remove the unnecessary `{ ...data }` spread) and return only `response.data` instead of the whole `response` so its return shape matches the rest of the module and caller expectations.
🧹 Nitpick comments (1)
apps/extension/src/apis/axios.ts (1)
74-76:getArticleDetail응답 타입을 명시해 주세요.지금은
response.data.data가 사실상any로 흘러서, 이번 PR에서 수정한id/articleId매핑 회귀를 컴파일 타임에 못 잡습니다.apiRequest.get<...>()제네릭과 명시적 반환 타입으로 고정해 두는 편이 안전합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/extension/src/apis/axios.ts` around lines 74 - 76, The getArticleDetail function returns response.data.data as any which hides type errors; update getArticleDetail to use apiRequest.get's generic parameter (e.g., apiRequest.get<ApiResponse<ArticleDetail>>(...)) and add an explicit return type for getArticleDetail (e.g., Promise<ArticleDetail>) so the compiler validates the shape (including id/articleId) at call sites; reference the getArticleDetail function and the apiRequest.get generic to locate where to add the types.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@apps/extension/src/apis/axios.ts`:
- Around line 64-72: The putArticle wrapper returns the full AxiosResponse which
differs from other API wrappers; change putArticle (the function using
apiRequest.put) to send data directly (remove the unnecessary `{ ...data }`
spread) and return only `response.data` instead of the whole `response` so its
return shape matches the rest of the module and caller expectations.
---
Nitpick comments:
In `@apps/extension/src/apis/axios.ts`:
- Around line 74-76: The getArticleDetail function returns response.data.data as
any which hides type errors; update getArticleDetail to use apiRequest.get's
generic parameter (e.g., apiRequest.get<ApiResponse<ArticleDetail>>(...)) and
add an explicit return type for getArticleDetail (e.g., Promise<ArticleDetail>)
so the compiler validates the shape (including id/articleId) at call sites;
reference the getArticleDetail function and the apiRequest.get generic to locate
where to add the types.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 23deded5-cd81-46d5-88fc-8899736c8181
📒 Files selected for processing (2)
apps/extension/src/apis/axios.tsapps/extension/src/pages/MainPop.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/extension/src/pages/MainPop.tsx
📌 Related Issues
📄 Tasks
⭐ PR Point (To Reviewer)
1️⃣ 토스트 아이콘 추가
토스트에 아이콘이 추가되어 디자인 시스템 Toast에 childern을 추가하였습니다
2️⃣ 아티클 상세 조회 API v3 업데이트 및 수정 시 데이터 미반영 문제 해결
아티클 수정하기를 눌렀을 때 MainPop에 기존 정보가 반영되지 않는 문제를 확인했습니다.
먼저 해당 현상이 발생하는 원인을 확인하기 위해 익스텐션에서 사용 중인 API를 점검했습니다.
확인 결과, 익스텐션에서는 아티클 상세 조회 API를 사용하지 않고 아티클 저장 여부 확인 API를 통해 기존 데이터를 표시하고 있었습니다.
하지만 서버에서 아티클 상세 조회 API가 v1 → v3로 업데이트되었음에도 익스텐션에서는 해당 변경이 반영되지 않아 데이터가 정상적으로 표시되지 않는 문제가 발생하고 있었습니다.
이에 따라 다음과 같이 로직을 수정했습니다.
아티클 저장 여부 확인 API에서는 아티클이 존재하는지만 확인하고 articleId만 가져오도록 사용
→ /api/v3/articles/{articleId} 아티클 상세 조회 API를 추가 호출하여 실제 수정 화면에 필요한 데이터를 가져오도록 구조 변경
하지만 적용 후에도 console과 MainPop에서 undefined가 발생하는 문제가 있었습니다.
원인을 확인해보니 API 버전에 따라 필드명이 달랐습니다.
이 차이로 인해 articleId가 정상적으로 전달되지 않아 상세 조회 API가 호출되지 않는 문제가 발생하고 있었습니다.
각 API에서 내려주는 필드명에 맞게 값을 매핑하도록 수정하여 문제를 해결했습니다.
또한 아티클 상세 조회를 v3 API로 업데이트한 이후 수정 후 저장이 정상적으로 반영되는 것까지 확인했습니다.
📷 Screenshot
Summary by CodeRabbit
릴리스 노트
새로운 기능
개선 사항