Skip to content

[FEAT] 구체화된 API 명세 반영하여 API 재정비 - #14

Merged
suwonthugger merged 14 commits into
mainfrom
feat/13-api-spec-v2
Jul 28, 2026
Merged

[FEAT] 구체화된 API 명세 반영하여 API 재정비#14
suwonthugger merged 14 commits into
mainfrom
feat/13-api-spec-v2

Conversation

@suwonthugger

@suwonthugger suwonthugger commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

👥 작업 배경 및 목적

  • 뉴스/영향분석/그래프 API를 v2 명세로 재구성하고, 온톨로지 스코프를 반도체 단일 섹터로 축소했다.
  • 관심종목 브리핑(티커 역조회) 기능을 세션 메모리 기반 스크랩 기능으로 대체했다.
  • 뉴스 클릭 후 이어지는 그래프 하이라이팅·영향 분석 패널의 가독성/피드백이 부족하다는 사용자 피드백을 반영해 UI 전반을 다듬었다.
  • 관련 Issue 번호: [FEAT] 구체화된 API 명세 반영하여 API 재정비 #13

🛠️ 주요 변경 사항

API v2 재구성 & 데이터 스코프 축소

  • src/lib/mappers.ts : (신규) wire(snake_case) 응답 → 도메인(camelCase) 타입 변환 계층. api.ts/handlers.ts는 wire 그대로 반환하고 queries.ts가 매핑해서 컴포넌트에 넘김

  • src/lib/api.ts, src/mocks/handlers.ts : GET /news, GET /news/{id}/impact, GET /graph?mode=full v2 명세로 재구성. 기존 analysis+graph 두 엔드포인트를 `n합(분석 패널과 그래프 패널이 같은 쿼리 키공유), 뉴스 id를 숫자로 전환

  • src/lib/queries.ts : 신규 매핑 계층을 거치

  • src/types/index.ts : v2 응답 타입 반영

  • src/lib/data.ts : 2차전지·방산 온톨로지 노하고 반도체 항목만 유지, mock 뉴스 60건으로확장(무한 스크롤 다중 페이지 검증용) - src/lib/layout.ts : SECTOR_CENTERS를 단

  • src/lib/atoms.ts : selectedNewsIdAtom을 숫자 타입으로 전환, sectorFilterAtom/브리핑 관련 atom 제거, scrappedNewsIdsAtom 추가

브리핑 → 스크랩 기능 전환

  • src/components/scrap/ScrapSheet.tsx : (신규, briefing/BriefingSheet.tsx 대체) 세션 메모리(scrappedNewsIdsAtom)에 담긴 스크랩 뉴스 목록을 보여주는 시트
  • src/components/scrap/ScrapFab.tsx : (신규, BriefingFab.tsx에서 이름 변경) 우하단 고정 FAB, 스크랩 건수 배지 표시
  • src/components/news/NewsCard.tsx : 스크랩 토글 버튼의 유니코드 별(★/☆)을 lucide Star 아이콘으로 교체
  • src/routes/app.tsx : BriefingSheetScrapSheet로 교체

그래프 하이라이팅 개선

  • src/components/graph/graph.css : 영향기점(origin) 노드 주변 pulse 링이 2회 재생 후 사라지던 것을 무한 반복(infinite)으로 변경해 강조 효과가 계속 유지되도록 수정. .ep.idle(전체 관계망 기본 엣지) 규칙 추가로 흐릿하던 기본 선을 진하게 조정

영향 분석 패널 UI/UX 개선

  • src/components/analysis/AnalysisPanel.tsx : 기사 요약 텍스트 색상 대비·굵기 개선(WCAG AA 미달 색상 교체)
  • src/components/analysis/MainStockCard.tsx : 영향기점 카드에 유리 sheen 효과(은은하게 빛나는 애니메이션) 추가 — box-shadow 오프셋 대신 테두리 두께가 변하지 않는 방식으로 다듬음
  • src/components/analysis/RelatedList.tsx : 관계로 이어진 종목의 근거 텍스트를 클릭 없이 카드 하단에 항상 노출(기존엔 클릭해야만 보였음), 상승/하락 방향에 맞춰 배경 톤 구분
  • src/components/analysis/RationaleBox.tsx : 최종 요약 텍스트 가독성 개선(font-weight 적용)

뉴스 목록 개선

  • src/components/news/NewsPanel.tsx : "최신 — 클릭 시 뉴스 목록 쿼리 즉시 재조회, 로딩 중 아이콘 회전 + 버튼 비활성화
  • src/components/news/NewsList.tsx, src/com.tsx : v2 API 응답(숫자 id)에 맞춰 로직 정리

타이포그래피 가독성 정책 적용 (전 화면 공통)

기존 디자인(크기·레이아웃)은 유지한 채, 굵기·색상 규칙만 통일:

  • 본문/설명 텍스트: font-weight: 400으로 방치되던 실제 정보 문단에 font-medium 이상 최소 적용
  • 캡션/메타 텍스트: 가장 옅은 --n-400/--n-300 토큰 사용 금지, --n-500 이상으로 통일
  • 미니 섹션 라벨: font-bold + --n-600으로 통일
  • 헤드라인: font-semibold/font-bold 혼재를 font-bold로 통일
  • 대상 파일: Header.tsx, AnalysisPanel.tsx, MainStockCard.tsx, RationaleBox.tsx, RelatedList.tsx, GraphPanel.tsx, NetworkView.tsx, graph.css, MobileNewsBar.tsx, NewsCard.tsx, NewsList.tsx, ScrapSheet.tsx, VerifyDetail.tsx, VerifyList.tsx, VerifyView.tsx
  • 부수적으로 발견한 버그 수정: ScrapSheet.tsx의 존재하지 않는 var(--n-800) 참조,
    VerifyDetail.tsx/VerifyView.tsx 차트의 하드코딩된 hex 색상 → CSS 변수 교체

코드 리뷰어가 집중해서 봐야 할 부분

  • src/lib/mappers.ts(신규 매핑 계층)와 queries.ts의 연결 지점 — wire ↔ 도메인 타입 변환이 누락 없이 이뤄지는지
  • src/lib/data.ts의 mock 데이터 축소가 다른 화면(특히 verify)에 영향 없는지
  • 타이포그래피 정책 커밋은 diff가 넓지만 전부 className/style 색상·굵기 값 교체이며 구조 변경 없음

🧪 테스트 결과 (선택)

  • pnpm lint, pnpm build 통과
  • Playwright로 뉴스맵 / 영향분석 / 전체 관계망 / 예측 검증 4개 화면 시각 검증, 콘솔 에러 없음 확인

GET /news, GET /news/{id}/impact, GET /graph?mode=full 새 명세에 맞춰
MSW mock 구조를 재구성한다.

- lib/mappers.ts 추가: wire(snake_case) 응답을 도메인(camelCase) 타입으로
  변환하는 계층. lib/api.ts·mocks/handlers.ts는 wire 그대로 반환하고,
  lib/queries.ts가 매핑해 컴포넌트에 넘긴다
- 기존 analysis+graph 두 엔드포인트를 news/{id}/impact 하나로 통합
  (분석 패널과 그래프 패널이 같은 쿼리 키를 공유)
- 뉴스 목록 커서 페이징은 유지하되 sector 파라미터/필드 제거, 뉴스 id를
  숫자로 전환, mock 뉴스를 60건으로 확장해 무한 스크롤 다중 페이지 검증
- 관심종목 브리핑(티커 역조회) 기능 삭제 → 세션 메모리 기반 스크랩
  (components/scrap) 기능으로 대체
당분간 반도체 스코프만 다루기로 하면서 2차전지·방산 온톨로지 노드/엣지와
관련 뉴스를 제거하고, 전체 관계망 레이아웃도 단일 클러스터 중앙 배치로
변경한다. 파급 경로 표시 기능 자체는 그대로다.

- lib/data.ts: RAW_COMPANIES/RAW_CONCEPTS/RAW_EDGES를 반도체 항목만 남기고
  정리, 수기 작성 뉴스도 반도체 9건만 남겨 재번호, 필러 뉴스 생성 개수
  조정(9+51=60건, 무한 스크롤 3페이지 유지)
- lib/layout.ts: SECTOR_CENTERS를 단일 섹터 중앙 좌표로 변경
- 예측 검증(verify) 화면의 mock 데이터는 온톨로지와 무관한 별도
  데이터라 변경하지 않음
전체 관계망의 기본(비선택) 엣지가 restingState 'idle'로 세팅되는데
전용 CSS 규칙이 없어 베이스 스타일(연한 회색, 얇은 선)을 그대로 써서
잘 보이지 않았다. .ep.idle 규칙을 추가해 색과 두께를 강조한다.
영향 분석 패널의 "기사 요약" 섹션 텍스트가 옅은 회색이라 잘 안 보였다.
줄 번호 배지는 --n-400(배경 대비 약 2.1:1, WCAG AA 미달)에서 --n-700으로,
본문·라벨·언론사 행 텍스트도 한 단계씩 진하게 조정했다.
색상 대비만 개선했던 이전 커밋에 이어, font-weight를 추가해 얇아 보이던 문제를 해결한다.
pulse 링 애니메이션이 2회 재생 후 사라지던 것을 무한 반복으로 바꿔, 영향기점 노드 주변 강조 효과가 계속 유지되도록 한다.
그래프 origin 노드와 같은 방향색(주황/딥애저)으로 은은하게 숨쉬듯 빛나는 box-shadow 애니메이션을 추가해, 뉴스 클릭 후 영향 기점 카드가 시각적으로 계속 강조되도록 한다.
"최신 뉴스" 옆에 새로고침 버튼을 추가해 뉴스 목록 쿼리를 즉시 다시 불러올 수 있게 한다. 새로고침 중에는 아이콘이 회전하며 버튼이 비활성화된다.
유니코드 별 문자(★/☆) 대신 shadcn/lucide-react의 Star 아이콘을 사용하도록 교체한다. 스크랩 상태에 따라 채움 여부만 다르고 기존 색상 로직은 유지한다.
box-shadow 스프레드가 커졌다 작아졌다 하며 테두리가 굵어지는 느낌을 주던 방식 대신, 테두리는 고정하고 유리에 빛이 스치듯 은은한 sheen만 주기적으로 흐르게 해 더 담백하게 다듬는다. 사용자 피드백으로 테두리 두께를 기존 border-2 수준으로 되돌렸다.
클릭 전까지 카드에 상세 근거가 있다는 표시가 전혀 없던 문제를 해결한다. 하단에 "근거 보기/접기" 텍스트와 회전하는 셰브런 아이콘을 추가하고, 펼쳐진 카드는 은은한 배경 톤과 fade-in 애니메이션으로 상태를 드러낸다.
은은한 sheen 스윕만 남기고 ambient box-shadow를 제거한다. 더 이상 쓰이지 않는 --glow-color/--glow-shadow 커스텀 프로퍼티도 함께 정리.
기존 디자인(크기·레이아웃)은 유지한 채, 굵기·색상만 일관된 규칙으로 정리했다.

- 본문/설명 텍스트(기사요약 근거, 최종요약, 검증 요약문 등): font-weight 없이(400) 렌더링되던
  실제 정보 문단에 font-medium 이상을 최소값으로 적용
- 캡션/메타 텍스트(관계 경로, 통계 설명, 테이블 헤더 등): 가장 옅은 --n-400/--n-300 토큰 사용을
  금지하고 --n-500 이상으로 통일
- 미니 섹션 라벨(기사 요약, 영향 기점, 통계 라벨, 테이블 헤더 등): font-bold + --n-600으로 통일
- 헤드라인(뉴스 제목, 검증 목록 제목 등): font-semibold/font-bold로 혼재되던 걸 font-bold로 통일

부수적으로 발견한 버그도 함께 수정: ScrapSheet.tsx의 존재하지 않는 var(--n-800) 참조,
VerifyDetail.tsx/VerifyView.tsx 차트의 하드코딩된 hex 색상을 CSS 변수로 교체.
클릭해야만 보이던 근거 텍스트를 카드 하단에 항상 노출하도록 바꾼다. 토글 상태와 셰브런을 제거하고, 근거 박스는 상승/하락 방향에 맞춰 톤을 입혀 시각적으로 구분한다.
@suwonthugger suwonthugger linked an issue Jul 28, 2026 that may be closed by this pull request
3 tasks
@suwonthugger suwonthugger self-assigned this Jul 28, 2026
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

뉴스 분석·브리핑 구조를 통합 /api/news/{id}/impact 응답과 영향 화면으로 전환하고, 뉴스 목록을 커서 기반 단일 스트림으로 변경했다. 카드 스크랩을 Jotai 세션 메모리로 관리하며, 관련 타입·쿼리·mock API·화면 스타일과 문서를 갱신했다.

Changes

뉴스 영향 데이터 계약 및 API

Layer / File(s) Summary
영향 계약과 데이터 모델
src/types/index.ts, src/lib/mappers.ts, src/lib/api.ts, src/lib/data.ts, src/lib/atoms.ts
뉴스 ID를 숫자로 변경하고 NewsImpact, origin/related stocks, snake_case wire 응답 매퍼, 통합 impact API와 결정론적 데모 데이터를 추가했다. 기존 분석·브리핑 타입과 API를 제거했다.
쿼리와 mock 엔드포인트
src/lib/queries.ts, src/mocks/handlers.ts
뉴스 목록을 커서 기반 쿼리로 변경하고 useNewsImpactQuery/api/news/:id/impact를 연결했다. 기존 분석·그래프·브리핑 엔드포인트를 제거했다.

영향 화면과 스크랩 흐름

Layer / File(s) Summary
영향 분석과 그래프 화면
src/components/analysis/*, src/components/graph/*, src/styles.css
분석·그래프 화면이 통합 impact 데이터를 사용하며, 원인 종목 카드·관련 종목 BFS 정렬·최종 요약을 새 데이터 구조로 렌더링한다.
뉴스 목록과 스크랩 시트
src/components/news/*, src/components/scrap/*, src/routes/app.tsx, docs/KOSLINK-FRONTEND.md
섹터 필터를 제거하고 뉴스 카드의 스크랩 토글, 세션 저장 목록, 스크랩 시트와 선택 뉴스 이동을 추가했다. 관련 화면 설명과 폴더 구조 문서도 갱신했다.
검증 화면과 시각 스타일
src/components/verify/*, src/components/Header.tsx, src/components/graph/graph.css, src/styles.css
검증·헤더·그래프의 글꼴과 색상 토큰을 조정하고 새로고침 버튼 및 원인 카드 강조 스타일을 추가했다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant NewsCard
  participant ScrapSheet
  participant Jotai
  participant ImpactQuery
  participant ImpactAPI

  User->>NewsCard: 스크랩 토글
  NewsCard->>Jotai: 뉴스 ID 저장 또는 제거
  User->>ScrapSheet: 스크랩 목록 열기
  ScrapSheet->>Jotai: 저장된 뉴스 ID 조회
  User->>ScrapSheet: 파급 경로 보기
  ScrapSheet->>Jotai: 선택 뉴스 ID와 map 뷰 설정
  ScrapSheet->>ImpactQuery: 선택 뉴스 영향 조회
  ImpactQuery->>ImpactAPI: GET /api/news/{id}/impact
  ImpactAPI-->>ImpactQuery: 영향·그래프 응답
Loading

Possibly related PRs

Suggested labels: feature

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed 제목이 API v2 재구성, 스크랩 전환, 쿼리/타입 정비라는 핵심 변경과 잘 맞습니다.
Description check ✅ Passed 배경/목적, 주요 변경 사항, 테스트 결과가 모두 포함되어 템플릿 요구사항을 대부분 충족합니다.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/13-api-spec-v2

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (3)
src/lib/api.ts (1)

191-199: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

mode 파라미터를 받아 놓고 무시합니다.

GetGraphParams.mode가 필수인데 인자 자체는 optional이고 본문은 _params를 전혀 보지 않습니다. 지금은 값이 'full' 하나뿐이라 동작에 문제는 없지만, 모드가 늘어나면 조용히 전체 그래프를 돌려주게 됩니다. 최소한 주석으로 "현재 full만 지원"임을 남기거나 파라미터를 제거하는 편이 낫습니다.

🤖 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 191 - 199, Update getGraph and GetGraphParams so
the API does not accept a required-but-ignored mode: either remove the unused
parameter and interface, or explicitly document that only “full” is currently
supported while validating the provided mode. Keep the current full-graph return
behavior unchanged.
src/lib/data.ts (1)

448-454: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

필러 뉴스의 publishedAt만 UTC 표기입니다.

CURATED_NEWS+09:00 오프셋 문자열인데 여기서는 toISOString()이라 Z 표기로 섞입니다. 시각 자체는 동일해 표시에는 영향이 없지만, 문서 §5.1 예시와 일관되게 KST 오프셋으로 포맷하는 편이 목데이터 검수에 편합니다.

🤖 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/data.ts` around lines 448 - 454, Update the filler-news timestamp
assignment in the data-generation flow around cursor and publishedAt so it
formats dates with an explicit +09:00 KST offset instead of Date.toISOString()’s
Z suffix. Preserve the same instant and ensure the output matches CURATED_NEWS
and the documented §5.1 timestamp format.
docs/KOSLINK-FRONTEND.md (1)

153-153: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

펜스 코드블록에 언어 지정(MD040).

markdownlint가 세 곳(153, 188, 266)에서 언어 미지정을 지적합니다. 요청 라인이라 http 또는 text로 지정하면 됩니다.

Also applies to: 188-188, 266-266

🤖 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 `@docs/KOSLINK-FRONTEND.md` at line 153, Update the three fenced code blocks at
the referenced documentation sections to include an explicit language tag, using
http for HTTP examples or text where appropriate, so markdownlint MD040 passes.

Source: Linters/SAST tools

🤖 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 147-169: 문서의 §5.1 뉴스 목록 응답에서 nextCursor가 camelCase인 이유를 명시하세요. 상단
wire 응답 규칙의 예외 설명과 NewsListPageWire 계약이 일치하도록 커서 필드도 의도적인 예외임을 밝히거나, 응답 예시와 관련
타입을 next_cursor로 통일하세요.

In `@src/components/analysis/RelatedList.tsx`:
- Around line 22-28: Update hopOf and the related impact classification so
tickers absent from graph.nodes do not default to hop 0 or receive the
direct-news label. Use a distinct unmatched fallback, such as one greater than
the maximum computed hop or a separate tier, while preserving hop 0 only for the
graph’s actual origin/direct targets.

In `@src/components/graph/graph.css`:
- Line 183: Update .ring.go in src/components/graph/graph.css:183 to run
koslink-pulse once with animation-fill-mode: forwards instead of infinite.
Update .origin-card-glow::after in src/styles.css:418-449 to run
koslink-card-sheen once with forwards, and add the required
prefers-reduced-motion handling.

In `@src/components/news/NewsCard.tsx`:
- Around line 29-35: NewsCard의 상위 role="button"과 내부 스크랩 버튼이 중첩되어 키보드 이벤트가
onSelect까지 버블링됩니다. 카드 선택 컨트롤과 스크랩 컨트롤을 형제 button 요소로 분리하고, 각 컨트롤이 자체 클릭 및 키보드
동작만 처리하도록 onSelect와 스크랩 동작의 연결을 조정하세요. 중첩 인터랙티브 요소와 상위 선택 동작으로의 이벤트 전파를 제거하세요.

In `@src/components/news/NewsPanel.tsx`:
- Around line 16-35: Update useIsFetching and invalidateQueries in NewsPanel to
pass exact: true with queryKeys.news(), limiting refresh state and invalidation
to the news list query only while leaving newsImpact queries unaffected.

In `@src/components/scrap/ScrapSheet.tsx`:
- Line 42: Update the SheetContent width classes in ScrapSheet so the sheet
cannot exceed narrow mobile viewports: use full available width while retaining
424px as the maximum width, and preserve the existing gap, padding, and
responsive max-width styling.

In `@src/lib/api.ts`:
- Around line 129-150: Handle an empty record.originStocks case before accessing
originStocks[0].nodeId by returning null or the established explicit error
result. Update the propagation construction in the relatedStocks mapping to use
all origin names via originNames.join(', ') instead of only originNames[0],
while preserving the existing behavior for non-empty origins.

In `@src/lib/data.ts`:
- Around line 429-446: Update the neighbor edge selection and mapping around
relatedStocks to include edges where either e.source or e.target equals
originId. Map the opposite endpoint to otherId, use it as nodeId, preserve
relation labels, and apply the existing competitor direction handling
consistently for both edge orientations.

In `@src/lib/queries.ts`:
- Around line 21-26: Update the queryKeys definitions so news() and
newsImpact(newsId) use separate root namespaces, preventing invalidation of
impact-analysis queries when NewsPanel invalidates the news list. Preserve the
existing parameter and key structure within each namespace, and update all
consumers of these keys consistently.

---

Nitpick comments:
In `@docs/KOSLINK-FRONTEND.md`:
- Line 153: Update the three fenced code blocks at the referenced documentation
sections to include an explicit language tag, using http for HTTP examples or
text where appropriate, so markdownlint MD040 passes.

In `@src/lib/api.ts`:
- Around line 191-199: Update getGraph and GetGraphParams so the API does not
accept a required-but-ignored mode: either remove the unused parameter and
interface, or explicitly document that only “full” is currently supported while
validating the provided mode. Keep the current full-graph return behavior
unchanged.

In `@src/lib/data.ts`:
- Around line 448-454: Update the filler-news timestamp assignment in the
data-generation flow around cursor and publishedAt so it formats dates with an
explicit +09:00 KST offset instead of Date.toISOString()’s Z suffix. Preserve
the same instant and ensure the output matches CURATED_NEWS and the documented
§5.1 timestamp format.
🪄 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: 5659084b-26e7-4e34-a145-0f918fb6987d

📥 Commits

Reviewing files that changed from the base of the PR and between 6f592ca and 6b9513f.

📒 Files selected for processing (29)
  • docs/KOSLINK-FRONTEND.md
  • src/components/Header.tsx
  • src/components/analysis/AnalysisPanel.tsx
  • src/components/analysis/MainStockCard.tsx
  • src/components/analysis/RationaleBox.tsx
  • src/components/analysis/RelatedList.tsx
  • src/components/briefing/BriefingSheet.tsx
  • src/components/graph/GraphPanel.tsx
  • src/components/graph/NetworkView.tsx
  • src/components/graph/graph.css
  • src/components/news/MobileNewsBar.tsx
  • src/components/news/NewsCard.tsx
  • src/components/news/NewsList.tsx
  • src/components/news/NewsPanel.tsx
  • src/components/scrap/ScrapFab.tsx
  • src/components/scrap/ScrapSheet.tsx
  • src/components/verify/VerifyDetail.tsx
  • src/components/verify/VerifyList.tsx
  • src/components/verify/VerifyView.tsx
  • src/lib/api.ts
  • src/lib/atoms.ts
  • src/lib/data.ts
  • src/lib/layout.ts
  • src/lib/mappers.ts
  • src/lib/queries.ts
  • src/mocks/handlers.ts
  • src/routes/app.tsx
  • src/styles.css
  • src/types/index.ts
💤 Files with no reviewable changes (1)
  • src/components/briefing/BriefingSheet.tsx

Comment thread docs/KOSLINK-FRONTEND.md
Comment on lines +147 to 169
베이스 `/api`. 인증 헤더 없음. 전부 TanStack Query로 감싼다. wire 응답은 아래처럼
snake_case다 — `lib/mappers.ts`가 이를 camelCase 도메인 타입으로 변환한 뒤
컴포넌트에 넘긴다(그래프 노드/엣지만 예외로, 명세 그대로 camelCase다).

### 5.1 뉴스 목록 (커서 기반 페이징 · 무한 스크롤)

```
GET /api/news?limit=20&sector=반도체&cursor=n3
GET /api/news?limit=20&cursor=10020
```

```jsonc
{
"items": [
{
"id": "n1",
"news_id": 10001,
"title": "SK하이닉스, HBM4 양산 위해 청주 M15X 증설 확정",
"press": "연합뉴스",
"publishedAt": "2026-07-18T09:12:00Z",
"sector": "반도체",
"published_at": "2026-07-18T09:12:00+09:00",
},
],
"nextCursor": "n1", // 다음 페이지 요청 시 그대로 실어 보낸다. 마지막 페이지면 null
"nextCursor": "10001", // 다음 페이지 요청 시 그대로 실어 보낸다. 마지막 페이지면 null
}
```

@coderabbitai coderabbitai Bot Jul 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

nextCursor만 camelCase인 이유를 명시해 주세요.

§5.1 예시 바디는 news_id/published_at snake_case인데 nextCursor만 camelCase입니다. src/lib/mappers.tsNewsListPageWire와는 일치하지만, 상단 전제(L147-149)에는 "그래프 노드/엣지만 예외"라고만 적혀 있어 백엔드 구현 시 혼동될 수 있습니다. 커서 필드도 예외로 명시하거나 next_cursor로 통일하는 편이 안전합니다.

🧰 Tools
🪛 markdownlint-cli2 (0.23.0)

[warning] 153-153: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 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 `@docs/KOSLINK-FRONTEND.md` around lines 147 - 169, 문서의 §5.1 뉴스 목록 응답에서
nextCursor가 camelCase인 이유를 명시하세요. 상단 wire 응답 규칙의 예외 설명과 NewsListPageWire 계약이
일치하도록 커서 필드도 의도적인 예외임을 밝히거나, 응답 예시와 관련 타입을 next_cursor로 통일하세요.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추후 세훈 구체화

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@suwonthugger, 알겠습니다. nextCursor의 wire 계약 및 문서 표기 방식은 세훈님 구체화 후 후속으로 정리하면 되겠습니다. 현재 리뷰 코멘트는 미해결 상태로 유지할게요.

원하시면 이 PR과 해당 코멘트를 연결한 후속 GitHub 이슈로 등록해 드릴까요?

Comment on lines +22 to +28
const index = buildGraphIndex(graph.nodes, graph.edges)
const built = bfsBuild(index, graph.originId)
const hopByTicker = new Map<string, number>()
graph.nodes.forEach((n) => {
if (n.ticker) hopByTicker.set(n.ticker, built.level[n.id] ?? 0)
})
const hopOf = (ticker: string) => hopByTicker.get(ticker) ?? 0

@coderabbitai coderabbitai Bot Jul 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

그래프에 없는 종목의 hop 기본값이 0이라 '뉴스 직접 대상'으로 잘못 표시될 수 있습니다.

hopOf가 매칭 실패 시 0을 반환하는데, impactOf(0)은 '뉴스 직접 대상'(원인 종목용 라벨)을 반환합니다. relatedStockstickergraph.nodes에 없는 경우(조인 키 불일치 등) 관계 종목이 마치 뉴스 기점인 것처럼 표시됩니다. ticker 문자열은 그래프 노드 id보다 약한 조인 키이므로, 현재는 mock 데이터 특성상 항상 일치하지만 향후 실 백엔드 계약이 어긋나면 오표시로 이어질 수 있습니다. 매칭 실패 시 명확히 구분되는 기본값(예: 최대 hop+1 혹은 별도 tier)을 쓰는 편이 안전합니다.

🤖 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 22 - 28, Update hopOf
and the related impact classification so tickers absent from graph.nodes do not
default to hop 0 or receive the direct-news label. Use a distinct unmatched
fallback, such as one greater than the maximum computed hop or a separate tier,
while preserving hop 0 only for the graph’s actual origin/direct targets.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추후 세훈 구체화

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@suwonthugger, 알겠습니다. 해당 케이스는 추후 구체화하는 것으로 두고, 현재 리뷰 코멘트는 열어두겠습니다. 원하시면 후속 작업용 GitHub 이슈로 등록해 드릴게요.

Comment thread src/components/graph/graph.css
Comment thread src/components/news/NewsCard.tsx
Comment thread src/components/news/NewsPanel.tsx
<SheetTrigger asChild>
<ScrapFab count={scrapped.length} />
</SheetTrigger>
<SheetContent className="w-[424px] gap-0 p-0 sm:max-w-[424px]">

@coderabbitai coderabbitai Bot Jul 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

모바일 뷰포트에서 시트 내용이 잘립니다.

Line 42의 w-[424px]는 375px 등 좁은 화면에서 우측 시트의 좌측 내용을 화면 밖으로 밀어냅니다. w-full max-w-[424px]처럼 뷰포트 폭을 넘지 않도록 제한하세요.

수정 예시
-      <SheetContent className="w-[424px] gap-0 p-0 sm:max-w-[424px]">
+      <SheetContent className="w-full max-w-[424px] gap-0 p-0">
📝 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.

Suggested change
<SheetContent className="w-[424px] gap-0 p-0 sm:max-w-[424px]">
<SheetContent className="w-full max-w-[424px] gap-0 p-0">
🤖 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/scrap/ScrapSheet.tsx` at line 42, Update the SheetContent
width classes in ScrapSheet so the sheet cannot exceed narrow mobile viewports:
use full available width while retaining 424px as the maximum width, and
preserve the existing gap, padding, and responsive max-width styling.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추후 반영

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@suwonthugger 알겠습니다. 이 PR에서는 미해결로 유지하겠습니다. 후속 작업으로 GitHub 이슈를 생성해 드릴까요?

🐰💭

Comment thread src/lib/api.ts
Comment thread src/lib/data.ts
Comment thread src/lib/queries.ts
@suwonthugger suwonthugger changed the title Feat/13 api spec v2 [FEAT] 구체화된 API 명세 반영하여 API 재정비 Jul 28, 2026
@suwonthugger
suwonthugger merged commit 366fe65 into main Jul 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 구체화된 API 명세 반영하여 API 재정비

1 participant