[FEAT] API 명세 구체화 및 정리 - #7
Conversation
SSR이 필요 없는 프로젝트라 TanStack Start의 서버 렌더링/하이드레이션 계층을 걷어내고, TanStack Router는 그대로 유지한 채 index.html + main.tsx 기반의 일반 Vite React SPA 구조로 변경한다. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
실제 Neo4j 온톨로지(Stock/Role/Theme, SUPPLY_TO/RELATED_TO)를 가정해 mock API
형태를 정리한다. 프론트가 실제로 렌더링에 쓰는 최소 필드만 받도록 노드 종류를
STOCK/CONCEPT로, 엣지 극성은 relationType(COMPETITOR)에서 유도하도록 바꾸고,
뉴스 클릭 시 파급 경로 그래프를 GET /api/news/{id}/graph로 분리했다. 좌표는
서버가 주지 않으므로 hop 레벨 기반 배치를 프론트에서 계산하도록 그래프
BFS/인덱싱 로직을 lib/graphIndex.ts로 재사용 가능하게 뽑아냈다.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
뉴스맵 탭과 달리 검증 탭 컨테이너에 높이 고정이 없어 콘텐츠가 늘어나는 만큼 페이지 전체가 스크롤됐다. .map과 동일하게 화면 높이에 맞추고, 목록·상세 카드 내부에서만 스크롤되도록 flex 레이아웃을 조정했다. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughReact 19 + Vite CSR 구조로 전환하고 뉴스 분석의 ChangesCSR 앱 부트스트랩 및 라우터 구성
영향 그래프 계약과 데이터 서비스
영향 그래프 화면 연결
예측 검증 화면 레이아웃
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant GraphPanel
participant QueryHook
participant ImpactGraphAPI
participant BFSUtilities
GraphPanel->>QueryHook: selectedNewsId로 영향 그래프 요청
QueryHook->>ImpactGraphAPI: GET news/{newsId}/graph
ImpactGraphAPI-->>QueryHook: nodes와 edges 응답
QueryHook-->>GraphPanel: NewsImpactGraph 전달
GraphPanel->>BFSUtilities: buildGraphIndex와 bfsBuild 호출
BFSUtilities-->>GraphPanel: hop level과 경로 데이터 반환
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Deploying koslink-fe with
|
| Latest commit: |
9c90385
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://bdc5ccd4.koslink-fe.pages.dev |
| Branch Preview URL: | https://feat-6-clear-up-api-spec.koslink-fe.pages.dev |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
src/lib/api.ts (1)
169-173: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value
requireNode(id)가 던지는 예외의 처리 경로를 확인해 주세요.
chain에 온톨로지에 없는 id가 섞이면 MSW 핸들러(src/mocks/handlers.tsL42-49)에서 예외가 그대로 올라가 404가 아닌 미처리 오류가 됩니다. 실제 Neo4j 백엔드 연동 시 데이터 정합성이 깨질 여지가 있으니 응답 경계에서의 처리 방침을 정해두는 게 좋습니다.🤖 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/lib/api.ts` around lines 169 - 173, requireNode(id)가 존재하지 않는 온톨로지 ID에서 던지는 예외가 API 응답 밖으로 전파되지 않도록, ImpactGraphNode를 생성하는 chain 처리 경계에서 예외를 포착하세요. 누락된 노드는 미처리 오류가 아닌 404 응답으로 변환하고, 정상적인 nodeIds 처리와 directionById 적용은 유지하세요.src/main.tsx (1)
15-24: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win
enableMocking()실패 시 앱이 렌더되지 않습니다.MSW
worker.start()가 거부되면.then콜백이 실행되지 않아 빈 화면 + unhandled rejection만 남습니다. 개발 환경 한정이지만 원인 파악이 어렵습니다.♻️ 렌더링을 mocking 실패와 분리
-enableMocking().then(() => { +function render() { const rootEl = document.getElementById('root')! startTransition(() => { createRoot(rootEl).render( <StrictMode> <RouterProvider router={router} /> </StrictMode>, ) }) -}) +} + +enableMocking() + .catch((error) => { + console.error('MSW worker 시작 실패', error) + }) + .finally(render)🤖 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/main.tsx` around lines 15 - 24, Update the enableMocking startup flow so the application render callback runs even when enableMocking() rejects. Handle the mocking failure without leaving an unhandled rejection, while preserving the existing createRoot, StrictMode, and RouterProvider rendering behavior.src/lib/queries.ts (1)
102-111: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win
useNewsImpactGraphQuery에staleTime설정 고려
useGraphQuery는 정적 데이터라staleTime: Infinity를 쓰는데, 뉴스별 파급 그래프도 발행 후 값이 바뀌지 않는 과거 데이터에 가까워 동일한 최적화가 적용 가능해 보입니다. 현재는 기본 staleTime이라 뉴스 간 전환·재포커스 시 불필요한 재요청이 발생할 수 있습니다.♻️ 제안 diff
export function useNewsImpactGraphQuery(newsId: string | null) { return useQuery({ queryKey: queryKeys.newsImpactGraph(newsId ?? ''), queryFn: () => fetchNewsImpactGraph(newsId as string), enabled: !!newsId, placeholderData: () => newsId ? (getNewsImpactGraph(newsId) ?? undefined) : undefined, + staleTime: Infinity, }) }🤖 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/lib/queries.ts` around lines 102 - 111, Update useNewsImpactGraphQuery to set staleTime: Infinity, matching the existing useGraphQuery behavior for effectively immutable graph data and avoiding unnecessary refetches during news switching or window refocus.src/components/analysis/RelatedList.tsx (1)
2-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
buildGraphIndex+bfsBuild중복 계산 — 공유 훅으로 추출 권장동일한
impactGraph에 대해GraphPanel.tsx의buildFocusScene과 이 컴포넌트가 각각 독립적으로buildGraphIndex/bfsBuild를 호출합니다(쿼리 자체는 캐시되지만 인덱스/BFS 재계산은 두 곳에서 중복).lib/queries.ts에useImpactGraphIndex(newsId)같은 훅을 추가해impactGraph,index,built를useMemo로 한 번만 계산해 재사용하면 중복을 없애고 두 화면 간 계산 결과 불일치 위험도 줄일 수 있습니다.🤖 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/components/analysis/RelatedList.tsx` around lines 2 - 24, Extract the shared impact-graph derivation into a hook such as useImpactGraphIndex in lib/queries.ts, memoizing the queried impactGraph, buildGraphIndex result, and bfsBuild result. Update RelatedList and GraphPanel’s buildFocusScene flow to consume this hook instead of independently calling buildGraphIndex and bfsBuild, while preserving their existing rendering behavior.
🤖 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 `@docs/KOSLINK-FRONTEND.md`:
- Around line 232-236: Update the Markdown code fences for the GET
/api/news/{id}/graph and GET /api/graph examples to include the http language
tag, preserving their existing contents and formatting.
In `@src/lib/api.ts`:
- Around line 155-173: Ensure each related record’s r.nodeId is added to nodeIds
alongside its chain values, keeping nodeIds aligned with directionById so every
related node with direction data is included in the generated nodes array.
---
Nitpick comments:
In `@src/components/analysis/RelatedList.tsx`:
- Around line 2-24: Extract the shared impact-graph derivation into a hook such
as useImpactGraphIndex in lib/queries.ts, memoizing the queried impactGraph,
buildGraphIndex result, and bfsBuild result. Update RelatedList and GraphPanel’s
buildFocusScene flow to consume this hook instead of independently calling
buildGraphIndex and bfsBuild, while preserving their existing rendering
behavior.
In `@src/lib/api.ts`:
- Around line 169-173: requireNode(id)가 존재하지 않는 온톨로지 ID에서 던지는 예외가 API 응답 밖으로
전파되지 않도록, ImpactGraphNode를 생성하는 chain 처리 경계에서 예외를 포착하세요. 누락된 노드는 미처리 오류가 아닌 404
응답으로 변환하고, 정상적인 nodeIds 처리와 directionById 적용은 유지하세요.
In `@src/lib/queries.ts`:
- Around line 102-111: Update useNewsImpactGraphQuery to set staleTime:
Infinity, matching the existing useGraphQuery behavior for effectively immutable
graph data and avoiding unnecessary refetches during news switching or window
refocus.
In `@src/main.tsx`:
- Around line 15-24: Update the enableMocking startup flow so the application
render callback runs even when enableMocking() rejects. Handle the mocking
failure without leaving an unhandled rejection, while preserving the existing
createRoot, StrictMode, and RouterProvider rendering behavior.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: ef051841-4a4a-4164-9e11-233b8ccd028b
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (25)
CLAUDE.mdREADME.mddocs/KOSLINK-FRONTEND.mdindex.htmlpackage.jsonsrc/client.tsxsrc/components/analysis/RelatedList.tsxsrc/components/graph/GraphPanel.tsxsrc/components/verify/VerifyDetail.tsxsrc/components/verify/VerifyView.tsxsrc/integrations/tanstack-query/root-provider.tsxsrc/lib/api.tssrc/lib/data.tssrc/lib/graphIndex.tssrc/lib/layout.tssrc/lib/queries.tssrc/main.tsxsrc/mocks/handlers.tssrc/routeTree.gen.tssrc/router.tsxsrc/routes/__root.tsxsrc/routes/index.tsxsrc/styles.csssrc/types/index.tsvite.config.ts
💤 Files with no reviewable changes (4)
- src/client.tsx
- src/integrations/tanstack-query/root-provider.tsx
- package.json
- src/routeTree.gen.ts
getNewsImpactGraph가 related[].chain 원소만으로 nodeIds를 모으고 있어, chain의 마지막 원소가 r.nodeId와 다른 레코드가 생기면 directionById에는 값이 있지만 해당 노드가 응답 nodes 배열에는 없는 상태가 될 수 있었다. r.nodeId를 항상 명시적으로 nodeIds에 추가해 계약을 고정한다. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
enableMocking이 import.meta.env.DEV로만 게이팅돼 있어 pnpm build && pnpm preview에서는 MSW가 뜨지 않았다. placeholderData로 잠깐 정상 렌더된 뒤, 404난 실 fetch가 재시도까지 실패하면 쿼리가 error 상태가 되며 data가 undefined로 떨어져 그래프·목록이 통째로 사라졌다. docs/KOSLINK-FRONTEND.md §11이 "빌드된 정적 파일로 발표한다"고 명시하고 있어 이 경로가 반드시 동작해야 한다. http.ts와 동일하게 VITE_API_BASE_URL 유무로 모킹 여부를 판단하도록 통일했다. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/main.tsx`:
- Around line 14-18: Update the startup flow around enableMocking so a rejected
promise is caught and logged, then createRoot(...).render() still executes.
Preserve the existing behavior for successful mock-worker initialization and
bypass configuration.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
👥 작업 배경 및 목적
🛠️ 주요 변경 사항
🧪 테스트 결과 (선택)
👥 작업 배경 및 목적
실제 백엔드(Neo4j 온톨로지) 연동을 앞두고, API 명세를 구체화하기 위해 mock API의 타입/응답 형태를 실 서버 스펙에 맞게 정리했습니다. 뉴스 클릭 시 보여줄 파급 경로 그래프를 별도 엔드포인트로 분리하고, 온톨로지 노드/엣지 타입을 프론트가 실제로 쓰는 최소 필드로 재설계했습니다.
같은 브랜치에 아래 두 작업도 함께 포함되어 있습니다(이슈 범위 밖이지만 선행 작업으로 처리):
🛠️ 주요 변경 사항
API 명세 구체화 (모킹 API 수정) — 핵심
NewsRelatedStock.chain 제거, ImpactG 입 신규 추가
변경
/api/news/{id}/graph 섹션 신규 추가
TanStack Start 제거 → CSR 전환
예측 검증 탭 레이아웃 수정
목록/상세 카드가 남는 공간을 채워 내
내부 스크롤되도록 변경
리뷰어가 특히 봐줬으면 하는 부분: sr경(특히 relationType으로 극성을대체한 부분)과 새 /api/news/{id}/graph 엔드포인트 계약이 실제 백엔드 스펙과 맞는지 확인 부탁드립니다.
🧪 테스트 결과 (선택)
로컬 환경 테스트 결과나 실행 화면 스크린샷이 있다면 첨부해 주세요.
pnpm lint, pnpm build 통과 확인
헤드리스 브라우저로 뉴스맵(1홉/2홉 파급, 전체 관계망 토글, 노드 하이라이트, 관심종목 브리핑)과
예측 검증 탭(내부 스크롤, 페이지 미 콘솔/HTTP 에러 없음을 확인
📎 API 명세 (백엔드 공유용)
공통 규칙
cursor는 opaque 값. 이전 응답의nextCursor를 다음 요청에 그대로 실어 보냄. 마지막 페이지면nextCursor: null.originId기준 BFS로 hop 레벨을 계산해 처리.엔드포인트 요약
/api/news/api/news/{id}/analysis/api/news/{id}/graph/api/graph/api/briefing/api/verifyGET
/api/news— 뉴스 목록 조회커서 페이징으로 뉴스 목록을 조회한다.
Request
Response 200
{ "items": [ { "id": "n1", "title": "SK하이닉스, HBM4 양산 위해 청주 M15X 증설 확정… 2027년 가동", "press": "연합뉴스", "publishedAt": "2026-07-20T09:12:00+09:00", "sector": "반도체" } ], "nextCursor": "n1" }GET
/api/news/{id}/analysis— 뉴스 영향 분석 패널요약 + 원문 + 기점 + 관련종목 + 근거를 한 번에 조회한다.
Request
Response 200
{ "newsId": "n1", "title": "SK하이닉스, HBM4 양산 위해 청주 M15X 증설 확정… 2027년 가동", "sector": "반도체", "article": { "summary": [ "SK하이닉스가 청주 M15X 공장 증설 투자를 확정했다고 공시했다.", "HBM4 양산 대응이 목적이며 2027년 상반기 가동이 목표다.", "장비 발주는 올해 4분기부터 순차 집행될 예정이다." ], "originUrl": "https://www.yna.co.kr/", "press": "연합뉴스", "publishedAt": "2026-07-20T09:12:00+09:00" }, "main": { "nodeId": "sk", "name": "SK하이닉스", "ticker": "000660", "direction": "UP", "reason": "HBM4 증설 발표로 생산능력이 직접 확대되는 당사자" }, "related": [ { "nodeId": "hanmi", "name": "한미반도체", "ticker": "042700", "direction": "UP", "relation": "장비 공급" }, { "nodeId": "ss", "name": "삼성전자", "ticker": "005930", "direction": "DOWN", "relation": "경쟁 관계" } ], "rationale": { "event": "SK하이닉스가 HBM4 대응 목적의 청주 M15X 증설을 확정", "propagation": "장비를 대는 한미반도체·HPSP·원익IPS에는 수주 확대 요인, 경쟁하는 삼성전자에는 점유율 압박 요인", "precedent": "동일 유형 증설 공시 5건 중 4건에서 장비주가 익일 평균 +3.1%" } }Response 404 — 존재하지 않는 뉴스 ID: 빈 바디.
GET
/api/news/{id}/graph— 뉴스 파급 경로 서브그래프 (신규)Request
Response 200
{ "newsId": "n1", "originId": "sk", "nodes": [ { "id": "sk", "name": "SK하이닉스", "kind": "STOCK", "ticker": "000660", "sector": "반도체", "marketCap": 1200000, "direction": "UP" }, { "id": "hanmi", "name": "한미반도체", "kind": "STOCK", "ticker": "042700", "sector": "반도체", "marketCap": 120000, "direction": "UP" }, { "id": "hpsp", "name": "HPSP", "kind": "STOCK", "ticker": "403870", "sector": "반도체", "marketCap": 26000, "direction": "UP" }, { "id": "wonik", "name": "원익IPS", "kind": "STOCK", "ticker": "240810", "sector": "반도체", "marketCap": 22000, "direction": "UP" }, { "id": "ss", "name": "삼성전자", "kind": "STOCK", "ticker": "005930", "sector": "반도체", "marketCap": 4600000, "direction": "DOWN" } ], "edges": [ { "id": "e2", "source": "sk", "target": "hanmi", "relation": "장비 공급" }, { "id": "e6", "source": "sk", "target": "hpsp", "relation": "고압 어닐링" }, { "id": "e5", "source": "sk", "target": "wonik", "relation": "증착 장비" }, { "id": "e3", "source": "sk", "target": "ss", "relation": "경쟁", "relationType": "COMPETITOR" } ] }필드 규칙
originId기준 BFS로 hop 레벨을 계산해 배치.direction: 파급 경로에 포함된STOCK노드에만 존재 (없으면 필드 생략).relationType없는 엣지는 전부 "동조" 관계로 취급 — 경쟁·대체 관계일 때만"COMPETITOR"등을 채움.kind: "CONCEPT"는 실제 온톨로지의 Role/Theme를 통칭. 프론트는 거래 가능 여부만 구분하면 됨.GET
/api/graph— 전체 온톨로지 그래프전체 관계망 뷰용. (전체 36개 노드 · 52개 엣지 중 일부만 예시)
Request
Response 200
{ "nodes": [ { "id": "sk", "name": "SK하이닉스", "kind": "STOCK", "ticker": "000660", "sector": "반도체", "marketCap": 1200000 }, { "id": "hbm", "name": "HBM", "kind": "CONCEPT", "sector": "반도체" } ], "edges": [ { "id": "e0", "source": "sk", "target": "hbm", "relation": "주력 생산" }, { "id": "e3", "source": "sk", "target": "ss", "relation": "경쟁", "relationType": "COMPETITOR" } ] }POST
/api/briefing— 관심종목 역방향 브리핑Request
Response 200
{ "totalNews": 25, "matched": [ { "ticker": "042700", "name": "한미반도체", "direction": "UP", "relation": "장비 공급", "chain": ["sk", "hanmi"], "newsId": "n1", "newsTitle": "SK하이닉스, HBM4 양산 위해 청주 M15X 증설 확정… 2027년 가동" } ], "unmatched": [ { "ticker": "005930", "name": "삼성전자" } ] }GET
/api/verify— 예측 검증 데이터news는 커서 페이징,daily는 페이징 없음.Request
Response 200
{ "daily": [ { "date": "2026-06-26", "hitRate": 0.62 }, { "date": "2026-07-25", "hitRate": 0.69 } ], "news": [ { "newsId": "v1", "date": "07-17", "sector": "반도체", "title": "SK하이닉스, 엔비디아향 HBM 공급 계약 확대", "items": [ { "name": "한미반도체", "predicted": "UP", "actualReturn": 3.42, "hit": true, "pathLabel": "장비 공급 · 1단계" } ] } ], "nextCursor": "v10" }Summary by CodeRabbit