[REFACTOR] API 동작 흐름 컴포넌트 훅으로 분리 - #52
Conversation
|
Warning Review limit reached
Next review available in: 22 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthrough분석·검색·리포트 페이지의 조회와 상태 관리 로직을 커스텀 훅으로 이동했습니다. 페이지는 훅이 제공하는 상태와 액션을 연결하며, 검색 목록 UI의 오버레이와 헤더 치수도 조정되었습니다. Changes분석 선행문헌 흐름
검색 흐름
이력·상세 조회
검색 목록 UI
Estimated code review effort: 4 (Complex) | ~45 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
⚔️ Resolve merge conflicts 💡
🧪 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 (9)
src/hooks/useReport.ts (1)
30-65: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift세 훅이 동일한 "인증 게이트 + caseId 대조 + reloadCount" 조회 패턴을 복제하고 있습니다.
로직 분리 자체는 아주 좋은 방향입니다 👍. 다만
isInitialized/accessToken게이트,{caseId, data}형태 보관 후 현재id와 대조,reloadCount로 재조회,errorMessage·isLoading파생 계산이 세 파일에 그대로 반복되어 있어, 한쪽만 고치면 동작이 어긋나기 쉽습니다.useAuthedResource<T>(id, fetcher, errorMessages)같은 제네릭 훅으로 추출하고 각 훅은 도메인 상태(선택 논리, 비교 결과 저장 등)만 담당하게 하는 구성을 권합니다.
src/hooks/useReport.ts#L30-L65: 조회/파생상태 블록을 공용 훅 호출로 대체하고REPORT_ERROR_MESSAGES만 주입.src/hooks/useNoveltyAnalysis.ts#L34-L70: 동일하게 공용 훅으로 교체하고saveComparison만 남기기.src/hooks/useInventiveStepAnalysis.ts#L74-L130: 공용 훅으로 교체하되 성공 콜백에서selectedLogics/aiRecommendedArgumentIds/placeholderArgumentIds파생 로직만 유지.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/useReport.ts` around lines 30 - 65, 중복된 인증 게이트·caseId 대조·reloadCount 재조회·에러 및 로딩 파생 로직을 제네릭 공용 훅 useAuthedResource<T>(id, fetcher, errorMessages)로 추출하고 세 훅이 이를 사용하도록 변경하세요. src/hooks/useReport.ts#L30-L65에서는 REPORT_ERROR_MESSAGES만 주입하고, src/hooks/useNoveltyAnalysis.ts#L34-L70에서는 saveComparison만 유지하세요. src/hooks/useInventiveStepAnalysis.ts#L74-L130에서는 성공 콜백의 selectedLogics·aiRecommendedArgumentIds·placeholderArgumentIds 파생 로직만 유지하세요.src/hooks/useInventiveStepAnalysis.ts (3)
45-51: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value순수 유틸
getArgument는 훅 모듈 밖으로 (선택).
getArgument는 상태와 무관한 순수 함수인데 훅 파일에서 export되어, 페이지가 데이터 로직 훅을 유틸 목적으로도 import하게 됩니다.@/lib/inventiveStep같은 모듈로 옮기면 모듈 경계와 재사용성이 명확해집니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/useInventiveStepAnalysis.ts` around lines 45 - 51, Move the pure exported getArgument function and its required logic-key mapping dependencies from useInventiveStepAnalysis.ts into a reusable utility module such as `@/lib/inventiveStep`, then update all imports and exports to use that module while preserving its existing lookup behavior.
212-218: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
reload의 초기화 대상이 일부만 포함됩니다 (선택).
aiRecommendedArgumentIds와updateError는 그대로 남아, 재조회가 실패하면 이전 상태의 배지 근거·에러 문구가 화면에 남습니다. 초기화 집합을 한 곳으로 모으면 누락이 줄어듭니다.♻️ 제안
const reload = () => { setResult(null); setRequestError(null); setSelectedLogics(new Set()); + setAiRecommendedArgumentIds(new Set()); setPlaceholderArgumentIds(new Set()); + setUpdateError(null); setReloadCount((count) => count + 1); };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/useInventiveStepAnalysis.ts` around lines 212 - 218, reload 함수의 초기화 대상에 aiRecommendedArgumentIds와 updateError를 추가해 재조회 시 이전 추천 근거와 오류 메시지가 남지 않도록 하세요. 기존 초기화 항목과 함께 하나의 초기화 집합으로 관리해 누락을 방지하고, 재조회 카운트 증가 동작은 유지하세요.
143-151: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win응답 정규화로
as InventiveStepArgument[]를 제거하세요.
updateInventiveArgument응답은InventiveStepArgument이고 현재content도 서버 응답에 포함되므로 요청body.content를 덮어써 서버 상태와 불일치할 수 있어요. React/Next 서버 컴포넌트 환경에서는 데이터가 서버 정규화 상태를 기준으로 동기화되므로,updatedArgument.content사용 후Record<string, never>처리를 제외하면 타입 단정도 필요 없어집니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/useInventiveStepAnalysis.ts` around lines 143 - 151, In the argument update mapping within the inventive step analysis hook, use the server-normalized updatedArgument.content instead of conditionally overwriting content with body.content. Remove the `as InventiveStepArgument[]` type assertion and preserve the existing `updatedArgument` fields so the mapped result is inferred as the correct type, excluding any `Record<string, never>` handling.src/hooks/useAnalysisPriorArts.ts (1)
88-123: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value늦게 도착한 상세 응답이 다른 사건 화면에 반영될 수 있습니다 (권장).
에러 코드 매핑과 fallback 문구 처리는 깔끔합니다 👍. 다만
getPriorArtDetail응답 대기 중 사용자가 다른 사건으로 이동하면, 뒤늦게 도착한 응답이 새 화면의 우측 패널에 그대로 반영됩니다.id를 캡처해 검증하거나 요청 시퀀스를 두면 안전합니다. 참고로 함수 정체성 안정화가 필요해지면useCallback(React 문서 "useCallback")로 감싸는 편이 좋습니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/useAnalysisPriorArts.ts` around lines 88 - 123, Prevent stale getPriorArtDetail results from updating the current panel after the user switches cases or selections. In handleSelect, capture the relevant case/selection identity or request sequence before awaiting, then verify it is still current before applying setSelectedPatent or setSelectError; keep the existing error mapping and finally cleanup behavior intact.src/app/(main)/myhistory/[id]/novelty/page.tsx (1)
67-67: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value시그니처는 정확히 맞습니다 👍 (참고)
NoveltyTable이Promise.allSettled로 여러 건을 저장하는 구조와saveComparison(comparisonId, body)가 잘 맞물립니다. 다만saveComparison은 매 렌더마다 새 참조가 되므로, 향후NoveltyTable을React.memo로 감싸거나 자식 effect 의존성에 넣게 되면 훅 쪽에서useCallback(React 문서 "useCallback") 적용이 필요합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/app/`(main)/myhistory/[id]/novelty/page.tsx at line 67, Wrap saveComparison in useCallback so its function reference remains stable across renders when passed to NoveltyTable. Include every value used by saveComparison in the dependency array, preserving its existing behavior and signature.src/hooks/useNoveltyAnalysis.ts (1)
83-91: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value선택 사항입니다:
citation을string | null로 정규화하세요.현재는
NoveltyTable에서 비어 있는 인용문을null로 보내고 있어서 여기까지undefined가 전달되는 경로는 보이지 않습니다. 다만 응답이나 request에서citationproperty가 누락되면 optional property 접근으로undefined가 유지될 수 있으니, 타입 안정성을 위해 fallback으로 정규화하는 것이 좋습니다. React Server Component/Client Component 관계에서는 이 부분이 서버-클라이언트 상태 동기화보다는 TypeScript 타입 계약의 명확도로 작용합니다.♻️ 제안
- citation: updated.citation !== undefined ? updated.citation : body.citation, + citation: updated.citation ?? body.citation ?? null,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/useNoveltyAnalysis.ts` around lines 83 - 91, Normalize the citation assignment in the comparison update within useNoveltyAnalysis so it always produces a string or null, including when both updated.citation and body.citation are undefined. Preserve the existing preference for updated.citation when provided and use an explicit null fallback for missing values.src/hooks/useSearchProgress.ts (2)
25-30: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win타입 안정성 팁:
TERMINAL_MESSAGE를status.status유니온에 맞춰 좁혀보면 어떨까요?지금은
Record<string, (status: SearchStatusResponse) => string>으로 선언돼 있어서,SearchStatusResponse["status"]에 새로운 값(예:"timeout")이 추가돼도 TypeScript가 매핑 누락을 잡아주지 못해요. TypeScript 공식 문서의 Mapped Types처럼 키를 실제 유니온 타입에서 파생시키면, 새 상태가 추가될 때 컴파일 타임에 바로 알 수 있어 유지보수성이 좋아집니다.💡 제안: 키 타입을 좁혀서 누락을 컴파일 타임에 잡기
-const TERMINAL_MESSAGE: Record<string, (status: SearchStatusResponse) => string> = { +type TerminalStatus = Exclude<SearchStatusResponse["status"], "in_progress" | "completed">; + +const TERMINAL_MESSAGE: Record<TerminalStatus, (status: SearchStatusResponse) => string> = { no_results: () => "조건에 맞는 선행기술을 찾지 못했어요.", invalid_input: (status) => status.reasonInvalid || "입력하신 내용을 다시 확인해주세요.", failed: (status) => status.error || status.reasonInvalid || "탐색 중 오류가 발생했습니다.", cancelled: () => "탐색이 취소되었습니다.", };
SearchStatusResponse["status"]가 실제로 리터럴 유니온인지 확인이 필요해서 검증 태그를 붙였어요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/useSearchProgress.ts` around lines 25 - 30, Update TERMINAL_MESSAGE to derive its keys from SearchStatusResponse["status"] using a mapped type or equivalent exhaustive type, while preserving the existing status handlers and callback signatures. Ensure adding a new status such as timeout causes a compile-time missing-key error.
96-113: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win"ApiError → 메시지 매핑 → fallback" 패턴, 공용 헬퍼로 뽑아내면 4곳이 한 줄로 줄어요.
두 훅 파일에서 동일한 에러 변환 로직이 4번 반복되고 있어요. 로직 자체는 정확하지만, 나중에
useNoveltyAnalysis,useReport등 다른 훅에서도 같은 패턴이 또 나올 가능성이 높아 보여서(PR 스택 개요 참고) 지금 헬퍼로 뽑아두면 유지보수성이 확 좋아질 거예요.
src/hooks/useSearchProgress.ts#L96-L113:poll의 catch 블록을 아래 헬퍼로 교체.src/hooks/useSearchProgress.ts#L55-L64:handleStop의 catch 블록을 동일 헬퍼로 교체.src/hooks/useSearchResult.ts#L44-L55:getPriorArts조회 catch 블록을 동일 헬퍼로 교체.src/hooks/useSearchResult.ts#L92-L102:handleImportPatentNumber의 catch 블록을 동일 헬퍼로 교체.🔧 제안: `src/lib/api/error.ts`에 공용 헬퍼 추가
export class ApiError extends Error { status: number; errorCode: string; errors?: ApiFieldError[]; // ... } + +export function resolveApiErrorMessage( + err: unknown, + messages: Record<string, string>, + fallback: string +): string { + if (err instanceof ApiError) { + return messages[err.errorCode] || err.message || fallback; + } + return fallback; +}그리고 각 catch 블록은 이렇게 단순해집니다:
- } catch (err) { - if (err instanceof ApiError) { - setCancelError( - SEARCH_CANCEL_ERROR_MESSAGES[err.errorCode] || - err.message || - "탐색 중단 중 오류가 발생했습니다." - ); - } else { - setCancelError("탐색 중단 중 오류가 발생했습니다."); - } - } finally { + } catch (err) { + setCancelError( + resolveApiErrorMessage(err, SEARCH_CANCEL_ERROR_MESSAGES, "탐색 중단 중 오류가 발생했습니다.") + ); + } finally {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/useSearchProgress.ts` around lines 96 - 113, 중복된 ApiError 메시지 매핑과 fallback 로직을 공용 에러 메시지 헬퍼로 추출하세요. src/lib/api/error.ts에 헬퍼를 추가하고, src/hooks/useSearchProgress.ts의 96-113 및 55-64, src/hooks/useSearchResult.ts의 44-55 및 92-102 각 catch 블록에서 해당 헬퍼를 사용하도록 교체해 동일한 메시지 동작을 유지하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/hooks/useAnalysisPriorArts.ts`:
- Around line 53-85: Update the useEffect in useAnalysisPriorArts to reset
isLoading, priorArts, and error when the effect starts, before validating id.
For missing or non-numeric id values, explicitly stop loading and return; for
valid ids, retain the existing cancellation and request behavior so prior
results are cleared while the new request is pending.
In `@src/hooks/useReport.ts`:
- Around line 73-79: Update the useReport return value to omit the raw
accessToken and expose an isAuthenticated boolean instead; derive it from token
availability within useReport. Update the report page consumer to destructure
and use isAuthenticated for the reload-button condition, preserving the existing
report, errorMessage, isLoading, and reload behavior.
In `@src/hooks/useSearchResult.ts`:
- Around line 32-64: Update the caseId guard in the useSearchResult effect to
set an appropriate error message when caseId is absent before returning, so
invalid direct access is distinguishable from a valid empty result. Preserve the
existing fetch and cancellation behavior when caseId is present.
In `@src/hooks/useTechDetail.ts`:
- Around line 21-64: Update the id-change synchronization in useTechDetail so
its existing useEffect resets detail, error, and loading state before requesting
the new prior-art record. Initialize loading through the state’s normal default
rather than relying on Boolean(priorArtId), and preserve the existing
cancellation and fetch-result handling for each priorArtId.
---
Nitpick comments:
In `@src/app/`(main)/myhistory/[id]/novelty/page.tsx:
- Line 67: Wrap saveComparison in useCallback so its function reference remains
stable across renders when passed to NoveltyTable. Include every value used by
saveComparison in the dependency array, preserving its existing behavior and
signature.
In `@src/hooks/useAnalysisPriorArts.ts`:
- Around line 88-123: Prevent stale getPriorArtDetail results from updating the
current panel after the user switches cases or selections. In handleSelect,
capture the relevant case/selection identity or request sequence before
awaiting, then verify it is still current before applying setSelectedPatent or
setSelectError; keep the existing error mapping and finally cleanup behavior
intact.
In `@src/hooks/useInventiveStepAnalysis.ts`:
- Around line 45-51: Move the pure exported getArgument function and its
required logic-key mapping dependencies from useInventiveStepAnalysis.ts into a
reusable utility module such as `@/lib/inventiveStep`, then update all imports and
exports to use that module while preserving its existing lookup behavior.
- Around line 212-218: reload 함수의 초기화 대상에 aiRecommendedArgumentIds와 updateError를
추가해 재조회 시 이전 추천 근거와 오류 메시지가 남지 않도록 하세요. 기존 초기화 항목과 함께 하나의 초기화 집합으로 관리해 누락을 방지하고,
재조회 카운트 증가 동작은 유지하세요.
- Around line 143-151: In the argument update mapping within the inventive step
analysis hook, use the server-normalized updatedArgument.content instead of
conditionally overwriting content with body.content. Remove the `as
InventiveStepArgument[]` type assertion and preserve the existing
`updatedArgument` fields so the mapped result is inferred as the correct type,
excluding any `Record<string, never>` handling.
In `@src/hooks/useNoveltyAnalysis.ts`:
- Around line 83-91: Normalize the citation assignment in the comparison update
within useNoveltyAnalysis so it always produces a string or null, including when
both updated.citation and body.citation are undefined. Preserve the existing
preference for updated.citation when provided and use an explicit null fallback
for missing values.
In `@src/hooks/useReport.ts`:
- Around line 30-65: 중복된 인증 게이트·caseId 대조·reloadCount 재조회·에러 및 로딩 파생 로직을 제네릭 공용
훅 useAuthedResource<T>(id, fetcher, errorMessages)로 추출하고 세 훅이 이를 사용하도록 변경하세요.
src/hooks/useReport.ts#L30-L65에서는 REPORT_ERROR_MESSAGES만 주입하고,
src/hooks/useNoveltyAnalysis.ts#L34-L70에서는 saveComparison만 유지하세요.
src/hooks/useInventiveStepAnalysis.ts#L74-L130에서는 성공 콜백의
selectedLogics·aiRecommendedArgumentIds·placeholderArgumentIds 파생 로직만 유지하세요.
In `@src/hooks/useSearchProgress.ts`:
- Around line 25-30: Update TERMINAL_MESSAGE to derive its keys from
SearchStatusResponse["status"] using a mapped type or equivalent exhaustive
type, while preserving the existing status handlers and callback signatures.
Ensure adding a new status such as timeout causes a compile-time missing-key
error.
- Around line 96-113: 중복된 ApiError 메시지 매핑과 fallback 로직을 공용 에러 메시지 헬퍼로 추출하세요.
src/lib/api/error.ts에 헬퍼를 추가하고, src/hooks/useSearchProgress.ts의 96-113 및 55-64,
src/hooks/useSearchResult.ts의 44-55 및 92-102 각 catch 블록에서 해당 헬퍼를 사용하도록 교체해 동일한
메시지 동작을 유지하세요.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 50eb5b7a-76d4-4f49-9269-ee030370feda
📒 Files selected for processing (16)
src/app/(main)/analysis/(workspace)/[id]/page.tsxsrc/app/(main)/analysis/[id]/[patentId]/page.tsxsrc/app/(main)/myhistory/[id]/novelty/page.tsxsrc/app/(main)/search/loading/page.tsxsrc/app/(main)/search/result/page.tsxsrc/app/(main)/tech/[id]/page.tsxsrc/app/(report)/myhistory/[id]/report/page.tsxsrc/components/searchlist/ProjectList.tsxsrc/components/searchlist/ResultListHeader.tsxsrc/hooks/useAnalysisPriorArts.tssrc/hooks/useInventiveStepAnalysis.tssrc/hooks/useNoveltyAnalysis.tssrc/hooks/useReport.tssrc/hooks/useSearchProgress.tssrc/hooks/useSearchResult.tssrc/hooks/useTechDetail.ts
| useEffect(() => { | ||
| const caseId = Number(id); | ||
| if (!id || Number.isNaN(caseId)) return; | ||
|
|
||
| let cancelled = false; | ||
|
|
||
| getPriorArts(caseId) | ||
| .then((result) => { | ||
| if (cancelled) return; | ||
| setPriorArts(result.priorArts); | ||
| setError(null); | ||
| }) | ||
| .catch((err) => { | ||
| if (cancelled) return; | ||
| if (err instanceof ApiError) { | ||
| setError( | ||
| PRIOR_ARTS_ERROR_MESSAGES[err.errorCode] || | ||
| err.message || | ||
| "선행문헌 목록을 불러오는 중 오류가 발생했습니다." | ||
| ); | ||
| } else { | ||
| setError("선행문헌 목록을 불러오는 중 오류가 발생했습니다."); | ||
| } | ||
| }) | ||
| .finally(() => { | ||
| if (cancelled) return; | ||
| setIsLoading(false); | ||
| }); | ||
|
|
||
| return () => { | ||
| cancelled = true; | ||
| }; | ||
| }, [id]); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
id가 유효하지 않거나 바뀔 때 로딩/목록 상태가 정리되지 않습니다.
두 가지 경로가 문제입니다.
id가 비어 있거나 숫자가 아니면 55번 줄에서 early return 하므로finally가 실행되지 않고isLoading이 초기값true로 영구 고정됩니다. 페이지는 "불러오는 중..."만 계속 보여주고 에러 메시지도 뜨지 않습니다.id가 바뀌어 effect가 재실행될 때setIsLoading(true)/setPriorArts([])가 없어, 새 요청이 끝날 때까지 이전 사건의 선행문헌 목록이 로딩 표시 없이 그대로 노출됩니다.
React 공식 문서의 "Fetching data with Effects" 예시처럼, effect 진입 시 결과 상태를 리셋해두면 두 문제를 함께 막을 수 있습니다.
🐛 제안: effect 진입 시 상태 리셋 + 유효하지 않은 id 처리
useEffect(() => {
const caseId = Number(id);
- if (!id || Number.isNaN(caseId)) return;
+ if (!id || Number.isNaN(caseId)) {
+ setPriorArts([]);
+ setError("잘못된 사건 번호입니다.");
+ setIsLoading(false);
+ return;
+ }
let cancelled = false;
+ setIsLoading(true);
+ setPriorArts([]);
+ setError(null);
getPriorArts(caseId)📝 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.
| useEffect(() => { | |
| const caseId = Number(id); | |
| if (!id || Number.isNaN(caseId)) return; | |
| let cancelled = false; | |
| getPriorArts(caseId) | |
| .then((result) => { | |
| if (cancelled) return; | |
| setPriorArts(result.priorArts); | |
| setError(null); | |
| }) | |
| .catch((err) => { | |
| if (cancelled) return; | |
| if (err instanceof ApiError) { | |
| setError( | |
| PRIOR_ARTS_ERROR_MESSAGES[err.errorCode] || | |
| err.message || | |
| "선행문헌 목록을 불러오는 중 오류가 발생했습니다." | |
| ); | |
| } else { | |
| setError("선행문헌 목록을 불러오는 중 오류가 발생했습니다."); | |
| } | |
| }) | |
| .finally(() => { | |
| if (cancelled) return; | |
| setIsLoading(false); | |
| }); | |
| return () => { | |
| cancelled = true; | |
| }; | |
| }, [id]); | |
| useEffect(() => { | |
| const caseId = Number(id); | |
| if (!id || Number.isNaN(caseId)) { | |
| setPriorArts([]); | |
| setError("잘못된 사건 번호입니다."); | |
| setIsLoading(false); | |
| return; | |
| } | |
| let cancelled = false; | |
| setIsLoading(true); | |
| setPriorArts([]); | |
| setError(null); | |
| getPriorArts(caseId) | |
| .then((result) => { | |
| if (cancelled) return; | |
| setPriorArts(result.priorArts); | |
| setError(null); | |
| }) | |
| .catch((err) => { | |
| if (cancelled) return; | |
| if (err instanceof ApiError) { | |
| setError( | |
| PRIOR_ARTS_ERROR_MESSAGES[err.errorCode] || | |
| err.message || | |
| "선행문헌 목록을 불러오는 중 오류가 발생했습니다." | |
| ); | |
| } else { | |
| setError("선행문헌 목록을 불러오는 중 오류가 발생했습니다."); | |
| } | |
| }) | |
| .finally(() => { | |
| if (cancelled) return; | |
| setIsLoading(false); | |
| }); | |
| return () => { | |
| cancelled = true; | |
| }; | |
| }, [id]); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/hooks/useAnalysisPriorArts.ts` around lines 53 - 85, Update the useEffect
in useAnalysisPriorArts to reset isLoading, priorArts, and error when the effect
starts, before validating id. For missing or non-numeric id values, explicitly
stop loading and return; for valid ids, retain the existing cancellation and
request behavior so prior results are cleared while the new request is pending.
| useEffect(() => { | ||
| if (!caseId) return; | ||
|
|
||
| let cancelled = false; | ||
|
|
||
| getPriorArts(Number(caseId)) | ||
| .then((result) => { | ||
| if (cancelled) return; | ||
| setPriorArts(result.priorArts); | ||
| setTotalCount(result.totalCount); | ||
| setError(null); | ||
| }) | ||
| .catch((err) => { | ||
| if (cancelled) return; | ||
| if (err instanceof ApiError) { | ||
| setError( | ||
| PRIOR_ARTS_ERROR_MESSAGES[err.errorCode] || | ||
| err.message || | ||
| "선행문헌 목록을 불러오는 중 오류가 발생했습니다." | ||
| ); | ||
| } else { | ||
| setError("선행문헌 목록을 불러오는 중 오류가 발생했습니다."); | ||
| } | ||
| }) | ||
| .finally(() => { | ||
| if (cancelled) return; | ||
| setIsLoading(false); | ||
| }); | ||
|
|
||
| return () => { | ||
| cancelled = true; | ||
| }; | ||
| }, [caseId]); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
caseId가 없을 때 사용자에게 아무 안내도 없어요.
useSearchProgress는 caseId가 없으면 /search?resume=1로 돌려보내는데, 이 훅은 32번째 줄에서 그냥 return만 하고 끝나요. 그러면 isLoading은 계속 false(초기값이 Boolean(caseId)라 애초에 false), error도 null이라서 사용자는 "탐색한 선행기술 0건"이라는 텅 빈 화면만 보게 됩니다. 잘못된 진입(예: caseId 없이 직접 URL 접근)을 구분할 수 있는 안내가 있으면 좋겠어요.
🔧 제안: caseId 없을 때 에러 상태로 안내
useEffect(() => {
- if (!caseId) return;
+ if (!caseId) {
+ setError("사건 정보를 찾을 수 없습니다. 다시 탐색을 시작해주세요.");
+ return;
+ }
let cancelled = false;📝 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.
| useEffect(() => { | |
| if (!caseId) return; | |
| let cancelled = false; | |
| getPriorArts(Number(caseId)) | |
| .then((result) => { | |
| if (cancelled) return; | |
| setPriorArts(result.priorArts); | |
| setTotalCount(result.totalCount); | |
| setError(null); | |
| }) | |
| .catch((err) => { | |
| if (cancelled) return; | |
| if (err instanceof ApiError) { | |
| setError( | |
| PRIOR_ARTS_ERROR_MESSAGES[err.errorCode] || | |
| err.message || | |
| "선행문헌 목록을 불러오는 중 오류가 발생했습니다." | |
| ); | |
| } else { | |
| setError("선행문헌 목록을 불러오는 중 오류가 발생했습니다."); | |
| } | |
| }) | |
| .finally(() => { | |
| if (cancelled) return; | |
| setIsLoading(false); | |
| }); | |
| return () => { | |
| cancelled = true; | |
| }; | |
| }, [caseId]); | |
| useEffect(() => { | |
| if (!caseId) { | |
| setError("사건 정보를 찾을 수 없습니다. 다시 탐색을 시작해주세요."); | |
| return; | |
| } | |
| let cancelled = false; | |
| getPriorArts(Number(caseId)) | |
| .then((result) => { | |
| if (cancelled) return; | |
| setPriorArts(result.priorArts); | |
| setTotalCount(result.totalCount); | |
| setError(null); | |
| }) | |
| .catch((err) => { | |
| if (cancelled) return; | |
| if (err instanceof ApiError) { | |
| setError( | |
| PRIOR_ARTS_ERROR_MESSAGES[err.errorCode] || | |
| err.message || | |
| "선행문헌 목록을 불러오는 중 오류가 발생했습니다." | |
| ); | |
| } else { | |
| setError("선행문헌 목록을 불러오는 중 오류가 발생했습니다."); | |
| } | |
| }) | |
| .finally(() => { | |
| if (cancelled) return; | |
| setIsLoading(false); | |
| }); | |
| return () => { | |
| cancelled = true; | |
| }; | |
| }, [caseId]); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/hooks/useSearchResult.ts` around lines 32 - 64, Update the caseId guard
in the useSearchResult effect to set an appropriate error message when caseId is
absent before returning, so invalid direct access is distinguishable from a
valid empty result. Preserve the existing fetch and cancellation behavior when
caseId is present.
# Conflicts: # src/app/(main)/analysis/[id]/[patentId]/page.tsx # src/app/(main)/search/loading/page.tsx
병합 커밋 시 useSearchProgress.ts의 activeSearchStore/resetSearchForm 연동 변경사항이 스테이징되지 않아 이전 버전으로 커밋된 것을 바로잡음.
[REFACTOR] API 동작 흐름 컴포넌트 훅으로 분리
📌 작업 내용
✅ 변경 사항
🔗 관련 이슈
Closes #
🧪 체크리스트
📸 스크린샷
없음
💬 리뷰 요청 사항
기타
Summary by CodeRabbit