Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Walkthrough페이지 기반 응답(content, totalElements 등)에서 커서 기반 응답(links, hasNext, lastId)으로 API 응답 형식이 변경되었습니다. 타입 정의와 목 데이터가 이 형식에 맞게 수정되었고, 클라이언트의 fetchLinks 매핑이 body.data.content → body.data.links로 변경되었습니다. 서버 BFF 엔드포인트(src/app/api/links/route.ts)는 GET 프록시와 POST의 본문 유효성 검사를 위해 핸들러가 교체되었으며, 무한 스크롤 훅 useGetInfiniteLinks가 추가되고 AllLink 컴포넌트는 이를 사용하도록 리팩터링되었습니다. useGetLinks는 옵션 인자가 제거되어 시그니처가 단순화되었습니다. Possibly related PRs
🚥 Pre-merge checks | ✅ 4✅ 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 |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
src/app/(dev)/link-api-demo/LinkApiDemo.tsx (1)
172-172:links.length ?? 0는 불필요한 null 병합입니다.Line [172]에서
links는 항상 배열이라length가undefined가 되지 않습니다. 표현을 단순화해도 동작은 동일합니다.제안 수정안
- <p className="text-gray600 text-sm">총 {links.length ?? 0}개</p> + <p className="text-gray600 text-sm">총 {links.length}개</p>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(dev)/link-api-demo/LinkApiDemo.tsx at line 172, Simplify the JSX expression that shows the count in LinkApiDemo.tsx by removing the unnecessary nullish coalescing; replace the usage of "links.length ?? 0" in the <p> element with just "links.length" (since links is always an array) so the displayed total becomes "총 {links.length}개".src/types/api/linkApi.ts (1)
33-35: 뷰 모델 키 네이밍을 장기적으로 통일하는 걸 권장합니다.Line 33-35에서 API는
links, View는content를 써서 계층 간 용어가 갈립니다. 지금도 동작은 가능하지만, 이후 훅/컴포넌트 확장 시 혼동을 줄이려면 키를 통일하거나 명시적 매퍼 유틸을 두는 편이 유지보수에 유리합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/types/api/linkApi.ts` around lines 33 - 35, The view model uses key "content" while the API type uses "links", causing inconsistent naming; update LinkListViewData to use the same key as the API (change its property from content: Link[] to links: Link[]) or add an explicit mapper (e.g., mapLinkListApiDataToView) that converts { links } -> { content } and update all consumers to use the unified shape; reference the types LinkListViewData and LinkListApiData and the keys links/content when making the change so callers and tests are updated accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/api/links/route.ts`:
- Around line 26-28: The POST input validation currently only checks body.url;
add validation for body.title in the same handler (the POST function in
src/app/api/links/route.ts) so requests with missing or non-string/empty title
are rejected early. Update the condition that checks body.url to also verify
body.title (typeof title === 'string' and title.trim() non-empty) and return a
400 NextResponse.json({ success: false, message: '제목이 필요합니다.' }, { status: 400
}) when it fails, keeping behavior consistent with the existing URL error
response.
In `@src/hooks/useGetInfiniteLinks.ts`:
- Around line 23-27: The infinite-query cache key conflicts with the standard
links query because both use ['links', params] and omit the size, so update
useGetInfiniteLinks to use a distinct key that includes an "infinite" marker and
the page size: change the queryKey type to ['links', 'infinite',
Omit<LinkListParams, 'lastId'> | undefined, number] and set the actual queryKey
in useGetInfiniteLinks to ['links', 'infinite', params, size]; ensure any
references inside the hook (e.g., the useInfiniteQuery call in
useGetInfiniteLinks and its queryFn/pageParam handling) use the new key so
infinite scroll caches do not collide with useGetLinks.
In `@src/lib/server/apiClient.ts`:
- Around line 25-27: serverApiClient currently spreads caller-provided options
after setting cache:'no-store', allowing callers to override the no-cache
behavior; change the fetch init merge so the forced cache setting wins (e.g.,
spread options first then set cache:'no-store' or explicitly assign cache after
merging) when building the init object used in the fetch call that uses
API_BASE_URL, endpoint, and options to ensure cache is always 'no-store'.
In `@src/types/api/linkApi.ts`:
- Around line 24-29: The LinkListApiData interface's lastId is currently typed
as number but may be null for empty results; update the contract by making
lastId nullable (e.g., number | null) in the LinkListApiData definition and then
search for and adjust any consumers of LinkListApiData (call sites that read
lastId) to handle a null value safely (guards or fallback logic), ensuring links
and hasNext semantics remain unchanged.
---
Nitpick comments:
In `@src/app/`(dev)/link-api-demo/LinkApiDemo.tsx:
- Line 172: Simplify the JSX expression that shows the count in LinkApiDemo.tsx
by removing the unnecessary nullish coalescing; replace the usage of
"links.length ?? 0" in the <p> element with just "links.length" (since links is
always an array) so the displayed total becomes "총 {links.length}개".
In `@src/types/api/linkApi.ts`:
- Around line 33-35: The view model uses key "content" while the API type uses
"links", causing inconsistent naming; update LinkListViewData to use the same
key as the API (change its property from content: Link[] to links: Link[]) or
add an explicit mapper (e.g., mapLinkListApiDataToView) that converts { links }
-> { content } and update all consumers to use the unified shape; reference the
types LinkListViewData and LinkListApiData and the keys links/content when
making the change so callers and tests are updated accordingly.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/apis/linkApi.tssrc/app/(dev)/link-api-demo/LinkApiDemo.tsxsrc/app/(route)/all-link/AllLink.tsxsrc/app/api/links/route.tssrc/hooks/useGetInfiniteLinks.tssrc/hooks/useGetLinks.tssrc/lib/server/apiClient.tssrc/mocks/fixtures/links.tssrc/types/api/linkApi.ts
81b5606 to
79945e1
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/hooks/useGetInfiniteLinks.ts (1)
17-17:size입력값 보정을 추가하면 방어력이 올라갑니다.Line [17]에서
size가 0 이하/비정상 값으로 들어와도 그대로 요청됩니다. 최소 1 이상 정수로 보정하는 처리를 권장합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/useGetInfiniteLinks.ts` at line 17, The size calculation in useGetInfiniteLinks currently assigns options?.size directly (const size = options?.size ?? DEFAULT_PAGE_SIZE) and allows 0/invalid values; fix it by normalizing size to a positive integer: read options?.size, coerce to a number, floor it, and clamp to a minimum of 1 (or fallback to DEFAULT_PAGE_SIZE when NaN/undefined); update the variable referenced as size in useGetInfiniteLinks to use this normalized value so all downstream requests receive an integer >= 1.src/app/(route)/all-link/AllLink.tsx (1)
28-29: 에러 발생 시 재시도 옵션 추가를 고려해 보세요현재 에러 상태에서 단순 메시지만 표시되고 있습니다. 사용자가 재시도할 수 있도록 버튼을 추가하면 UX가 개선될 수 있습니다.
💡 재시도 버튼 추가 예시
- if (isError) return <p className="text-gray600">링크를 불러오지 못했습니다.</p>; + if (isError) return ( + <div className="flex flex-col items-center gap-2"> + <p className="text-gray600">링크를 불러오지 못했습니다.</p> + <button + onClick={() => refetch()} + className="text-blue500 underline" + > + 다시 시도 + </button> + </div> + );
useGetInfiniteLinks에서refetch도 함께 구조 분해하여 사용하세요:const { data, isLoading, isError, fetchNextPage, hasNextPage, isFetchingNextPage, refetch } = useGetInfiniteLinks();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(route)/all-link/AllLink.tsx around lines 28 - 29, The component currently returns a plain error message when isError is true; update the AllLink.tsx render to destructure refetch from useGetInfiniteLinks (e.g., const { data, isLoading, isError, fetchNextPage, hasNextPage, isFetchingNextPage, refetch } = useGetInfiniteLinks()) and replace the static error <p> with a small error UI that shows the message and a "Retry" button which calls refetch when clicked (ensure the button is disabled while isLoading/isFetchingNextPage as appropriate and keep existing className styling like text-gray600).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/api/links/route.ts`:
- Around line 26-31: Before validating body.title/body.url, ensure the request
body is a non-null object: add an early check like if (!body || typeof body !==
'object' || Array.isArray(body)) return NextResponse.json({ success: false,
message: '유효한 요청 본문이 필요합니다.' }, { status: 400 }); then proceed to the existing
checks that reference body.title and body.url; update the validation branches
that call NextResponse.json to rely on this prior guard so accesses to
body.title/body.url cannot throw.
In `@src/hooks/useGetInfiniteLinks.ts`:
- Around line 34-37: The current getNextPageParam in useGetInfiniteLinks returns
lastPage.lastId when lastPage.hasNext is true, but if lastId is null this will
cause repeated requests with pageParam: null; update getNextPageParam (the
function named getNextPageParam in useGetInfiniteLinks) to guard against a
nullable cursor by returning undefined when lastPage.lastId is falsy (or
explicitly null/undefined) even if lastPage.hasNext is true, so pagination stops
when there is no valid next cursor.
In `@src/mocks/fixtures/links.ts`:
- Around line 365-369: The mockLinkListData fixture sets lastId to
mockLinks[mockLinks.length - 1]?.id ?? 0 which uses 0 as a fallback; change the
fallback to null to match the expected type (number | null) and to avoid
confusing “no cursor” with a numeric cursor. Update the lastId expression in
mockLinkListData to return null when mockLinks is empty (use
mockLinks[mockLinks.length - 1]?.id ?? null) so the data contract and semantics
align.
---
Nitpick comments:
In `@src/app/`(route)/all-link/AllLink.tsx:
- Around line 28-29: The component currently returns a plain error message when
isError is true; update the AllLink.tsx render to destructure refetch from
useGetInfiniteLinks (e.g., const { data, isLoading, isError, fetchNextPage,
hasNextPage, isFetchingNextPage, refetch } = useGetInfiniteLinks()) and replace
the static error <p> with a small error UI that shows the message and a "Retry"
button which calls refetch when clicked (ensure the button is disabled while
isLoading/isFetchingNextPage as appropriate and keep existing className styling
like text-gray600).
In `@src/hooks/useGetInfiniteLinks.ts`:
- Line 17: The size calculation in useGetInfiniteLinks currently assigns
options?.size directly (const size = options?.size ?? DEFAULT_PAGE_SIZE) and
allows 0/invalid values; fix it by normalizing size to a positive integer: read
options?.size, coerce to a number, floor it, and clamp to a minimum of 1 (or
fallback to DEFAULT_PAGE_SIZE when NaN/undefined); update the variable
referenced as size in useGetInfiniteLinks to use this normalized value so all
downstream requests receive an integer >= 1.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/apis/linkApi.tssrc/app/(dev)/link-api-demo/LinkApiDemo.tsxsrc/app/(route)/all-link/AllLink.tsxsrc/app/api/links/route.tssrc/hooks/useGetInfiniteLinks.tssrc/hooks/useGetLinks.tssrc/lib/server/apiClient.tssrc/mocks/fixtures/links.tssrc/types/api/linkApi.ts
79945e1 to
670ba5d
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/app/(dev)/link-api-demo/LinkApiDemo.tsx (1)
172-172: 여기서는?? 0를 제거해도 됩니다.
links가 기본값[]를 가지므로links.length는 항상 숫자입니다.수정 예시
- <p className="text-gray600 text-sm">총 {links.length ?? 0}개</p> + <p className="text-gray600 text-sm">총 {links.length}개</p>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(dev)/link-api-demo/LinkApiDemo.tsx at line 172, The displayed count uses a redundant nullish coalescing with links.length; remove the "?? 0" from the JSX in LinkApiDemo.tsx so the paragraph renders the numeric links.length directly (variable: links, component: LinkApiDemo).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/`(route)/all-link/AllLink.tsx:
- Around line 37-42: The error UI currently renders the retry text twice: the
Button component in AllLink.tsx already displays "다시 시도" via its label prop
(Button onClick={() => refetch()} label="다시 시도"), and a duplicate literal "다시
시도" follows it; remove the standalone duplicate string after the Button in the
component's return so only the Button shows the retry text.
In `@src/hooks/useGetInfiniteLinks.ts`:
- Around line 14-31: The computed page size currently ignores params.size
(useGetInfiniteLinks), using only options?.size/default; change the size
calculation to derive the effectiveSize from params.size if present, otherwise
options?.size or DEFAULT_PAGE_SIZE, then use that effectiveSize in the queryKey
and when calling fetchLinks (replace references to size with effectiveSize) so
callers who pass size in params are honored; update any variable name (size →
effectiveSize) and ensure queryKey ['links','infinite', params, effectiveSize]
and fetchLinks({...params, size: effectiveSize}) are used.
---
Nitpick comments:
In `@src/app/`(dev)/link-api-demo/LinkApiDemo.tsx:
- Line 172: The displayed count uses a redundant nullish coalescing with
links.length; remove the "?? 0" from the JSX in LinkApiDemo.tsx so the paragraph
renders the numeric links.length directly (variable: links, component:
LinkApiDemo).
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/apis/linkApi.tssrc/app/(dev)/link-api-demo/LinkApiDemo.tsxsrc/app/(route)/all-link/AllLink.tsxsrc/app/api/links/route.tssrc/hooks/useGetInfiniteLinks.tssrc/hooks/useGetLinks.tssrc/lib/server/apiClient.tssrc/mocks/fixtures/links.tssrc/types/api/linkApi.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- src/app/api/links/route.ts
- src/apis/linkApi.ts
670ba5d to
c623ceb
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (3)
src/hooks/useGetInfiniteLinks.ts (1)
6-16: 옵션 타입명은 훅 이름과 맞추면 가독성이 더 좋아집니다.현재 의미는 맞지만, 이름을 맞춰 두면 추후 훅이 늘어날 때 구분이 쉬워집니다.
이름 정리 예시
-type UseGetLinksOptions = { +type UseGetInfiniteLinksOptions = { enabled?: boolean; size?: number; }; @@ export function useGetInfiniteLinks( params?: Omit<LinkListParams, 'lastId' | 'size'>, - options?: UseGetLinksOptions + options?: UseGetInfiniteLinksOptions ) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/useGetInfiniteLinks.ts` around lines 6 - 16, Rename the options type UseGetLinksOptions to UseGetInfiniteLinksOptions to match the hook name useGetInfiniteLinks; update all references (including the options parameter in the useGetInfiniteLinks signature) so the type name is consistent and preserves current optional fields (enabled, size) and behavior (DEFAULT_PAGE_SIZE remains unchanged).src/app/api/links/route.ts (1)
35-45: 검증된title/url은 trim 후 전달하는 편이 더 안전합니다.현재는 유효성만 확인하고 원본 문자열을 전달하므로, 선행/후행 공백이 그대로 저장될 수 있습니다.
정규화 반영 예시
- const data = await serverApiClient('/v1/links', { + const sanitizedBody = { + ...body, + title: body.title.trim(), + url: body.url.trim(), + }; + + const data = await serverApiClient('/v1/links', { method: 'POST', - body: JSON.stringify(body), + body: JSON.stringify(sanitizedBody), });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/api/links/route.ts` around lines 35 - 45, Trim and normalize the validated title and url before sending to serverApiClient so leading/trailing whitespace isn't stored: after the existing checks for body.title and body.url (the validation block) assign trimmed values (e.g., body.title = body.title.trim(); body.url = body.url.trim()) or build a new payload object with trimmedTitle/trimmedUrl and pass that to serverApiClient('/v1/links', ...) instead of the raw body; ensure you reference the same variable names used in this file (body, serverApiClient) so validation and the POST payload stay consistent.src/app/(route)/all-link/AllLink.tsx (1)
57-59:onLoadMore콜백은 더 간단히 표현할 수 있습니다.동작은 동일하므로 함수 참조를 직접 넘기면 가독성이 조금 더 좋아집니다.
간단한 정리 예시
- onLoadMore={() => { - fetchNextPage(); - }} + onLoadMore={fetchNextPage}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(route)/all-link/AllLink.tsx around lines 57 - 59, 현재 AllLink.tsx에서 onLoadMore에 화살표 함수로 fetchNextPage를 감싸 전달하고 있는데, 동작은 동일하므로 불필요한 래퍼를 제거하고 onLoadMore에 직접 함수 참조 fetchNextPage를 전달하세요; 대상 식별자는 onLoadMore prop과 fetchNextPage 함수입니다.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/app/`(route)/all-link/AllLink.tsx:
- Around line 57-59: 현재 AllLink.tsx에서 onLoadMore에 화살표 함수로 fetchNextPage를 감싸 전달하고
있는데, 동작은 동일하므로 불필요한 래퍼를 제거하고 onLoadMore에 직접 함수 참조 fetchNextPage를 전달하세요; 대상 식별자는
onLoadMore prop과 fetchNextPage 함수입니다.
In `@src/app/api/links/route.ts`:
- Around line 35-45: Trim and normalize the validated title and url before
sending to serverApiClient so leading/trailing whitespace isn't stored: after
the existing checks for body.title and body.url (the validation block) assign
trimmed values (e.g., body.title = body.title.trim(); body.url =
body.url.trim()) or build a new payload object with trimmedTitle/trimmedUrl and
pass that to serverApiClient('/v1/links', ...) instead of the raw body; ensure
you reference the same variable names used in this file (body, serverApiClient)
so validation and the POST payload stay consistent.
In `@src/hooks/useGetInfiniteLinks.ts`:
- Around line 6-16: Rename the options type UseGetLinksOptions to
UseGetInfiniteLinksOptions to match the hook name useGetInfiniteLinks; update
all references (including the options parameter in the useGetInfiniteLinks
signature) so the type name is consistent and preserves current optional fields
(enabled, size) and behavior (DEFAULT_PAGE_SIZE remains unchanged).
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/apis/linkApi.tssrc/app/(dev)/link-api-demo/LinkApiDemo.tsxsrc/app/(route)/all-link/AllLink.tsxsrc/app/api/links/route.tssrc/hooks/useGetInfiniteLinks.tssrc/hooks/useGetLinks.tssrc/lib/server/apiClient.tssrc/mocks/fixtures/links.tssrc/types/api/linkApi.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- src/lib/server/apiClient.ts
- src/app/(dev)/link-api-demo/LinkApiDemo.tsx
- src/apis/linkApi.ts
c623ceb to
48d7c31
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/`(dev)/link-api-demo/LinkApiDemo.tsx:
- Line 172: The displayed count text is misleading: change the static copy "총
{links.length}개" to indicate it's the number of currently shown items (e.g., "현재
표시 {links.length}개") in the LinkApiDemo component; locate the JSX element that
renders <p className="text-gray600 text-sm">총 {links.length}개</p> and update the
string to reflect "현재 표시" (or similar) using the existing links variable so the
UI accurately represents the shown/queried count.
In `@src/app/`(route)/all-link/AllLink.tsx:
- Around line 36-42: Narrow the global error branch so only initial-load
failures hide the whole list: change the check using useInfiniteQuery flags
(isError) to ensure it only triggers when there's no existing data (e.g.,
isError && !data?.pages?.length) or by checking isFetchNextPageError separately;
keep existing list rendering when data exists and show a non-blocking inline
error or retry button tied to refetch/fetchNextPage. Update the conditional in
AllLink.tsx that currently reads "if (isError) return …" to use the combined
condition (isError && no pages) and handle isFetchNextPageError inline while
still exposing refetch/fetchNextPage actions.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/apis/linkApi.tssrc/app/(dev)/link-api-demo/LinkApiDemo.tsxsrc/app/(route)/all-link/AllLink.tsxsrc/app/api/links/route.tssrc/hooks/useGetInfiniteLinks.tssrc/hooks/useGetLinks.tssrc/lib/server/apiClient.tssrc/mocks/fixtures/links.tssrc/types/api/linkApi.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- src/apis/linkApi.ts
- src/app/api/links/route.ts
- src/hooks/useGetInfiniteLinks.ts
- src/lib/server/apiClient.ts
- src/types/api/linkApi.ts
48d7c31 to
dddf07f
Compare
관련 이슈
PR 설명
src/app/api/links/route.ts작성linkApi.ts에서fetchLinks,createLinks를 라우트 거치도록 수정useGetInfiniteLinks.ts훅 작성