-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: 기업상세정보 ui구현 #84
Conversation
Walkthrough이 풀 리퀘스트는 여러 레이아웃 및 컴포넌트에 걸쳐 UI 스타일링과 구조를 개선하는 변경 사항을 포함합니다. 주요 변경 사항은 글로벌 CSS 파일 업데이트, 다양한 레이아웃 컴포넌트의 높이 및 클래스 조정, 그리고 주식 검색 상세 페이지를 위한 새로운 컴포넌트 추가를 포함합니다. Changes
Assessment against linked issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Tip CodeRabbit's docstrings feature is now available as part of our Early Access Program! Simply use the command Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 12
🧹 Nitpick comments (6)
tailwind.config.ts (1)
54-54
: gray-80 색상의 이름을 Tailwind 컨벤션에 맞게 수정해주세요.Tailwind CSS는 색상 단계를 100 단위로 사용하는 것이 컨벤션입니다.
gray-80
을gray-100
과 비교했을 때 더 밝은 색상이므로, 이를gray-50
으로 변경하는 것이 좋겠습니다.gray: { - 80: "#D9D9D9", + 50: "#D9D9D9",src/app/my-account/layout.tsx (1)
13-13
: 공통 레이아웃 컴포넌트 도입 고려여러 레이아웃 파일에서 동일한 스타일(
lg:max-w-2000 h-full bg-[#F5F6F8]
)이 반복되고 있습니다. 코드 재사용성과 일관성을 위해 다음과 같은 개선을 제안드립니다:공통 레이아웃 컴포넌트를 생성하여 스타일을 통합 관리하는 것이 좋을 것 같습니다. 예시:
// src/components/layouts/BaseLayout.tsx export default function BaseLayout({ children }: { children: React.ReactNode }) { return <div className="lg:max-w-2000 h-full bg-[#F5F6F8]">{children}</div>; }이렇게 하면:
- 스타일 변경 시 한 곳에서만 수정하면 됩니다
- 레이아웃 일관성을 보장할 수 있습니다
- 코드 중복을 줄일 수 있습니다
src/app/search/[id]/_components/detail-info/relative-news/index.tsx (1)
3-26
: 데이터 상태 관리 및 타입 정의가 필요합니다.뉴스 데이터를 동적으로 처리하기 위한 구조가 필요합니다.
다음과 같은 구조를 제안드립니다:
interface NewsItem { title: string; source: string; date: string; } interface RelativeNewsProps { news: NewsItem[]; isLoading?: boolean; } export default function RelativeNews({ news, isLoading = false }: RelativeNewsProps) { if (isLoading) { return <div>로딩 중...</div>; } if (!news.length) { return <div>관련 뉴스가 없습니다.</div>; } return ( // 테이블 구현... ); }src/app/search/[id]/_components/detail-info/index.tsx (1)
17-28
: 탭 컨텐츠의 지연 로딩 구현을 고려해주세요.현재 모든 탭 컨텐츠가 동시에 로드되고 있습니다. 성능 최적화를 위해 지연 로딩을 구현하는 것이 좋습니다.
import dynamic from 'next/dynamic'; const CompanyOverview = dynamic(() => import('./company-overview'), { loading: () => <div>로딩 중...</div> }); const RelativeNews = dynamic(() => import('./relative-news'), { loading: () => <div>로딩 중...</div> });src/app/globals.css (1)
51-56
: list-style 클래스 네이밍 및 구조 개선
- Tailwind 컨벤션에 맞는 네이밍으로 변경
- 재사용 가능한 컴포넌트로 분리 고려
-.list-style { +.custom-list { list-style-type: square; padding-inline-start: 1.5em; list-style-position: inside; line-height: 24px; }src/app/search/[id]/_components/detail-info/company-overview/financial-statements.tsx (1)
3-24
: 타입 정의의 개선 가능성타입 정의가 잘 구성되어 있으나, 몇 가지 개선 사항이 있습니다:
type FinancialRatio = { stockAccountingYearMonth: string; - grossMarginRatio: number; - businessProfitRate: number; - netInterestRate: number; - roeValue: number; - earningsPerShare: number; - salesPerShare: number; - bookValuePerShare: number; - reserveRate: number; - liabilityRate: number; + grossMarginRatio: number | null; + businessProfitRate: number | null; + netInterestRate: number | null; + roeValue: number | null; + earningsPerShare: number | null; + salesPerShare: number | null; + bookValuePerShare: number | null; + reserveRate: number | null; + liabilityRate: number | null; };
- API에서 null 값이 올 수 있으므로, 타입을
number | null
로 정의하는 것이 안전합니다.- 각 필드에 대한 JSDoc 문서화를 추가하면 유지보수성이 향상될 것입니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
src/app/globals.css
(2 hunks)src/app/login/layout.tsx
(1 hunks)src/app/main/layout.tsx
(1 hunks)src/app/members/layout.tsx
(1 hunks)src/app/my-account/layout.tsx
(1 hunks)src/app/portfolio/layout.tsx
(1 hunks)src/app/search/[id]/_components/detail-info/company-overview/consensus.tsx
(1 hunks)src/app/search/[id]/_components/detail-info/company-overview/financial-statements.tsx
(1 hunks)src/app/search/[id]/_components/detail-info/company-overview/index.tsx
(1 hunks)src/app/search/[id]/_components/detail-info/company-overview/target-price.tsx
(1 hunks)src/app/search/[id]/_components/detail-info/index.tsx
(1 hunks)src/app/search/[id]/_components/detail-info/relative-news/index.tsx
(1 hunks)src/app/search/[id]/layout.tsx
(1 hunks)src/app/search/[id]/page.tsx
(2 hunks)src/app/search/layout.tsx
(1 hunks)src/components/nav-bar/_components/nav-menu.tsx
(1 hunks)tailwind.config.ts
(2 hunks)
✅ Files skipped from review due to trivial changes (2)
- src/app/search/layout.tsx
- src/components/nav-bar/_components/nav-menu.tsx
🔇 Additional comments (8)
tailwind.config.ts (1)
43-43
: 초록색 900 색상이 적절하게 추가되었습니다.
기업 상세정보 UI에 사용될 새로운 다크 그린 색상이 기존 색상 팔레트와 잘 어울립니다.
src/app/portfolio/layout.tsx (1)
13-13
: 높이 설정 변경이 적절해 보입니다
h-screen
에서 h-full
로의 변경은 중첩된 레이아웃 구조에서 더 적절해 보입니다. 상위 컨테이너의 높이를 기준으로 동작하므로 레이아웃의 유연성이 향상될 것으로 예상됩니다.
src/app/main/layout.tsx (1)
14-14
: 레이아웃 스타일링이 적절히 개선되었습니다.
반응형 패딩과 최대 너비 제한을 통해 다양한 화면 크기에서 일관된 레이아웃을 제공할 수 있게 되었습니다.
src/app/login/layout.tsx (1)
14-14
:
모바일 화면에서의 최소 너비 제한을 재검토해주세요.
min-w-400
설정으로 인해 모바일 기기(특히 작은 화면)에서 가로 스크롤이 발생할 수 있습니다.
다음 스크립트로 모바일 최적화가 필요한 다른 컴포넌트들을 확인해보겠습니다:
다음과 같이 수정하는 것을 제안드립니다:
- <div className="lg:max-w-2000 flex size-full min-w-400 shrink-0 flex-col items-center justify-center bg-gradient-to-b from-green-100 via-green-50 to-white px-4 py-8 md:px-12 lg:px-16">
+ <div className="lg:max-w-2000 flex size-full w-full shrink-0 flex-col items-center justify-center bg-gradient-to-b from-green-100 via-green-50 to-white px-4 py-8 md:px-12 lg:px-16">
src/app/members/layout.tsx (1)
14-14
: 로그인 레이아웃과 동일한 모바일 최적화 이슈가 있습니다.
로그인 컴포넌트에서 언급된 min-w-400
관련 이슈가 이 컴포넌트에도 동일하게 적용됩니다.
src/app/globals.css (1)
5-9
: html/body 스타일 설정 검토 필요
Tailwind의 min-h-screen
이나 h-screen
유틸리티와 충돌할 수 있습니다. 레이아웃 컴포넌트에서 이러한 유틸리티를 사용하는 경우 예상치 못한 동작이 발생할 수 있습니다.
src/app/search/[id]/page.tsx (2)
4-4
: 새로운 DetailInfo 컴포넌트 import 추가됨
DetailInfo 컴포넌트가 추가되었습니다. 이는 기업 상세 정보를 표시하기 위한 새로운 UI 요소입니다.
78-78
: DetailInfo 컴포넌트의 데이터 연동 확인 필요
DetailInfo 컴포넌트가 props 없이 렌더링되고 있습니다. API 연동이 계획되어 있다면, 이후 props 전달을 위한 리팩토링이 필요할 것으로 보입니다.
다음 사항들을 고려해주세요:
- API 연동 시 필요한 데이터 구조 설계
- 에러 처리 방안
- 로딩 상태 처리
{children} | ||
</div> | ||
); | ||
return <div className="lg:max-w-2000 bg-[#F5F6F8]">{children}</div>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Let me analyze the layout patterns across the codebase. I can now generate the final response based on the search results.
레이아웃 스타일 변경이 일관성에 영향을 줄 수 있습니다
검토 결과, 다른 유사한 레이아웃 컴포넌트들과 스타일 클래스가 일치하지 않습니다:
my-account/layout.tsx
와portfolio/layout.tsx
는h-full
클래스를 사용search/layout.tsx
는flex flex-col
클래스를 사용- 현재 파일(
search/[id]/layout.tsx
)은 이러한 클래스들이 모두 제거됨
상위 검색 페이지(search/layout.tsx
)에서 사용 중인 flex flex-col
레이아웃과의 일관성을 위해 이러한 클래스들의 재검토가 필요합니다.
🔗 Analysis chain
레이아웃 스타일 변경사항 검토 필요
화면 높이 관련 클래스(h-screen
)와 flex 관련 클래스(flex-col
)가 제거되었습니다. 이로 인해 컨텐츠 높이가 동적으로 조정될 수 있습니다. 다음 사항들을 확인해주세요:
- 스크롤 동작이 의도한대로 작동하는지
- 하위 컴포넌트들의 배치가 올바른지
다음 스크립트로 다른 레이아웃 파일들과의 일관성을 확인할 수 있습니다:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# 레이아웃 컴포넌트들의 스타일 클래스 검색
rg -g "layout.tsx" "className=" src/app
Length of output: 1261
export default function TargetPrice() { | ||
return ( | ||
<div> | ||
<div className="mt-13 text-16-500">목표주가</div> | ||
<div className="flex justify-between"> | ||
<div className="text-20-700">43.90</div> | ||
<div className="text-12-400"> | ||
<div>목표가범위</div> | ||
<div>26.00~60.00</div> | ||
<div className="mt-5 h-6 w-260 rounded-2 bg-green-500"> | ||
<span className="inline-block size-16 rounded-full bg-lime-300" /> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
하드코딩된 값들을 props로 전달하도록 개선이 필요합니다.
현재 목표주가와 범위가 하드코딩되어 있어 재사용성이 제한됩니다. 다음과 같은 개선사항을 제안드립니다:
- 데이터를 props로 전달받도록 수정
- 가격 범위 표시 바에 대한 접근성 개선 (aria-label 추가)
- 반응형 디자인 고려
다음과 같이 수정하는 것을 제안드립니다:
- export default function TargetPrice() {
+ interface TargetPriceProps {
+ currentPrice: string;
+ minPrice: string;
+ maxPrice: string;
+ }
+
+ export default function TargetPrice({
+ currentPrice,
+ minPrice,
+ maxPrice
+ }: TargetPriceProps) {
return (
<div>
<div className="mt-13 text-16-500">목표주가</div>
<div className="flex justify-between">
- <div className="text-20-700">43.90</div>
+ <div className="text-20-700">{currentPrice}</div>
<div className="text-12-400">
<div>목표가범위</div>
- <div>26.00~60.00</div>
+ <div>{minPrice}~{maxPrice}</div>
<div className="mt-5 h-6 w-260 rounded-2 bg-green-500">
- <span className="inline-block size-16 rounded-full bg-lime-300" />
+ <span
+ className="inline-block size-16 rounded-full bg-lime-300"
+ aria-label={`현재 목표가: ${currentPrice}`}
+ role="progressbar"
+ aria-valuemin={parseFloat(minPrice)}
+ aria-valuemax={parseFloat(maxPrice)}
+ aria-valuenow={parseFloat(currentPrice)}
+ />
</div>
</div>
</div>
</div>
);
}
📝 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.
export default function TargetPrice() { | |
return ( | |
<div> | |
<div className="mt-13 text-16-500">목표주가</div> | |
<div className="flex justify-between"> | |
<div className="text-20-700">43.90</div> | |
<div className="text-12-400"> | |
<div>목표가범위</div> | |
<div>26.00~60.00</div> | |
<div className="mt-5 h-6 w-260 rounded-2 bg-green-500"> | |
<span className="inline-block size-16 rounded-full bg-lime-300" /> | |
</div> | |
</div> | |
</div> | |
</div> | |
); | |
} | |
interface TargetPriceProps { | |
currentPrice: string; | |
minPrice: string; | |
maxPrice: string; | |
} | |
export default function TargetPrice({ | |
currentPrice, | |
minPrice, | |
maxPrice | |
}: TargetPriceProps) { | |
return ( | |
<div> | |
<div className="mt-13 text-16-500">목표주가</div> | |
<div className="flex justify-between"> | |
<div className="text-20-700">{currentPrice}</div> | |
<div className="text-12-400"> | |
<div>목표가범위</div> | |
<div>{minPrice}~{maxPrice}</div> | |
<div className="mt-5 h-6 w-260 rounded-2 bg-green-500"> | |
<span | |
className="inline-block size-16 rounded-full bg-lime-300" | |
aria-label={`현재 목표가: ${currentPrice}`} | |
role="progressbar" | |
aria-valuemin={parseFloat(minPrice)} | |
aria-valuemax={parseFloat(maxPrice)} | |
aria-valuenow={parseFloat(currentPrice)} | |
/> | |
</div> | |
</div> | |
</div> | |
</div> | |
); | |
} |
<table className="border-y border-solid border-gray-100"> | ||
<caption className="text-left">관련뉴스</caption> | ||
<thead> | ||
<tr className="bg-green-100"> | ||
<th>제목</th> | ||
<th>정보 제공</th> | ||
<th>날짜</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr className="border-y border-solid border-gray-100"> | ||
<td> | ||
코스피, 외국인 "바이 코리아"에 2680 선 마감.. LG 에너지 | ||
솔루 | ||
</td> | ||
<td>머니 투데이</td> | ||
<td>2024.09.02 </td> | ||
</tr> | ||
</tbody> | ||
</table> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
테이블 구조 및 접근성 개선이 필요합니다.
현재 테이블 구현에서 다음과 같은 개선사항이 필요합니다:
- 테이블 헤더에 적절한 스코프 속성 추가
- 반응형 테이블 구현 (모바일 대응)
- 데이터 로딩 상태 처리
다음과 같이 수정하는 것을 제안드립니다:
- <table className="border-y border-solid border-gray-100">
+ <div className="overflow-x-auto">
+ <table className="w-full border-y border-solid border-gray-100">
<caption className="text-left">관련뉴스</caption>
<thead>
<tr className="bg-green-100">
- <th>제목</th>
- <th>정보 제공</th>
- <th>날짜</th>
+ <th scope="col" className="p-2">제목</th>
+ <th scope="col" className="p-2">정보 제공</th>
+ <th scope="col" className="p-2">날짜</th>
</tr>
</thead>
<tbody>
<tr className="border-y border-solid border-gray-100">
- <td>
+ <td className="p-2">
코스피, 외국인 "바이 코리아"에 2680 선 마감.. LG 에너지
솔루
</td>
- <td>머니 투데이</td>
- <td>2024.09.02 </td>
+ <td className="p-2">머니 투데이</td>
+ <td className="p-2">2024.09.02</td>
</tr>
</tbody>
</table>
+ </div>
📝 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.
<table className="border-y border-solid border-gray-100"> | |
<caption className="text-left">관련뉴스</caption> | |
<thead> | |
<tr className="bg-green-100"> | |
<th>제목</th> | |
<th>정보 제공</th> | |
<th>날짜</th> | |
</tr> | |
</thead> | |
<tbody> | |
<tr className="border-y border-solid border-gray-100"> | |
<td> | |
코스피, 외국인 "바이 코리아"에 2680 선 마감.. LG 에너지 | |
솔루 | |
</td> | |
<td>머니 투데이</td> | |
<td>2024.09.02 </td> | |
</tr> | |
</tbody> | |
</table> | |
<div className="overflow-x-auto"> | |
<table className="w-full border-y border-solid border-gray-100"> | |
<caption className="text-left">관련뉴스</caption> | |
<thead> | |
<tr className="bg-green-100"> | |
<th scope="col" className="p-2">제목</th> | |
<th scope="col" className="p-2">정보 제공</th> | |
<th scope="col" className="p-2">날짜</th> | |
</tr> | |
</thead> | |
<tbody> | |
<tr className="border-y border-solid border-gray-100"> | |
<td className="p-2"> | |
코스피, 외국인 "바이 코리아"에 2680 선 마감.. LG 에너지 | |
솔루 | |
</td> | |
<td className="p-2">머니 투데이</td> | |
<td className="p-2">2024.09.02</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> |
export default function DetailInfo() { | ||
return ( | ||
<div className="mt-16 rounded-10 bg-white p-30"> | ||
<h3 className="mb-25 text-20-700">상세 정보</h3> | ||
<Tabs defaultValue="company-overview"> | ||
<TabsList> | ||
<TabsTrigger value="company-overview">기업 개요</TabsTrigger> | ||
<TabsTrigger value="relative-news">관련 뉴스</TabsTrigger> | ||
</TabsList> | ||
<TabsContent value="company-overview"> | ||
<CompanyOverview /> | ||
</TabsContent> | ||
<TabsContent value="relative-news"> | ||
<RelativeNews /> | ||
</TabsContent> | ||
</Tabs> | ||
</div> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
탭 컴포넌트의 상태 관리 및 에러 처리 개선이 필요합니다.
현재 구현에서 다음과 같은 개선사항이 필요합니다:
- 탭 상태 유지 관리
- 에러 바운더리 추가
- 로딩 상태 처리
다음과 같은 구조를 제안드립니다:
import { useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
interface DetailInfoProps {
initialTab?: string;
}
export default function DetailInfo({ initialTab = 'company-overview' }: DetailInfoProps) {
const [activeTab, setActiveTab] = useState(initialTab);
return (
<div className="mt-16 rounded-10 bg-white p-30">
<h3 className="mb-25 text-20-700">상세 정보</h3>
<ErrorBoundary fallback={<div>에러가 발생했습니다.</div>}>
<Tabs
defaultValue={activeTab}
onValueChange={setActiveTab}
>
{/* 기존 탭 구현... */}
</Tabs>
</ErrorBoundary>
</div>
);
}
export default function CompanyOverview() { | ||
return ( | ||
<div> | ||
<div className="mb-10 flex justify-between"> | ||
<div className="text-16-500">기업정보</div> | ||
<span className="text-14-500 text-gray-100">[기준: 2024. 08. 16]</span> | ||
</div> | ||
<ul className="list-style mb-24 border-y-2 border-solid border-gray-100 py-14 text-14-500"> | ||
<li>한국 및 DX 부문 해외 9개</li> | ||
<li>한국 및 DX 부문 해외 9개</li> | ||
<li>한국 및 DX 부문 해외 9개</li> | ||
</ul> | ||
|
||
<FinancialStatements /> | ||
<Consensus /> | ||
<TargetPrice /> | ||
<div className="text-14-500 text-gray-100"> | ||
전 세계 1200개 리서치 회사의 정보를 종합적으로 분석합니다. <br /> | ||
기준 2024.08.29 . 레피니티브 제공 | ||
</div> | ||
</div> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
타입 안정성 및 접근성 개선 필요
- TypeScript 인터페이스 정의가 필요합니다.
- 시맨틱 HTML 요소 사용이 필요합니다.
interface CompanyOverviewProps {
referenceDate: Date;
companyDetails: string[];
analysisDate: Date;
}
-export default function CompanyOverview() {
+export default function CompanyOverview({
+ referenceDate,
+ companyDetails,
+ analysisDate
+}: CompanyOverviewProps) {
<div className="text-14-500 text-gray-100"> | ||
전 세계 1200개 리서치 회사의 정보를 종합적으로 분석합니다. <br /> | ||
기준 2024.08.29 . 레피니티브 제공 | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
분석 정보 날짜 동적 처리 필요
분석 정보의 기준 날짜도 하드코딩되어 있습니다. API 연동 시 실제 데이터를 사용하도록 수정이 필요합니다.
-기준 2024.08.29 . 레피니티브 제공
+기준 {formatDate(analysisDate)} . 레피니티브 제공
Committable suggestion skipped: line range outside the PR's diff.
export default function Consensus() { | ||
return ( | ||
<div className="w-full"> | ||
<div className="mb-10 text-16-500">컨센서스</div> | ||
<div className="flex w-full justify-between gap-8 text-center text-14-500 text-gray-100"> | ||
<div className="flex-1"> | ||
<div className="mb-6 h-9 w-full bg-green-900" /> | ||
<span>적극 매도</span> | ||
</div> | ||
<div className="flex-1"> | ||
<div className="mb-6 h-9 w-full bg-green-900" /> | ||
<span>매도</span> | ||
</div> | ||
<div className="flex-1"> | ||
<div className="mb-6 h-9 w-full bg-green-900" /> | ||
<span>중립</span> | ||
</div> | ||
<div className="flex-1"> | ||
<div className="mb-6 h-9 w-full bg-green-900" /> | ||
<span>매수</span> | ||
</div> | ||
<div className="flex-1"> | ||
<div className="mb-6 h-9 w-full bg-green-900" /> | ||
<span>적극 매수</span> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
컨센서스 컴포넌트 개선사항
다음과 같은 개선이 필요합니다:
- 각 등급별 데이터를 동적으로 처리
- 접근성 개선 (aria-label 추가)
- 타입 정의
interface ConsensusRating {
label: string;
value: number;
percentage: number;
}
interface ConsensusProps {
ratings: ConsensusRating[];
}
컴포넌트 리팩토링 예시:
-export default function Consensus() {
+export default function Consensus({ ratings }: ConsensusProps) {
return (
<div className="w-full">
<div className="mb-10 text-16-500">컨센서스</div>
- <div className="flex w-full justify-between gap-8 text-center text-14-500 text-gray-100">
- <div className="flex-1">
- <div className="mb-6 h-9 w-full bg-green-900" />
- <span>적극 매도</span>
- </div>
- {/* 반복되는 코드 */}
+ <div className="flex w-full justify-between gap-8 text-center text-14-500 text-gray-100">
+ {ratings.map((rating) => (
+ <div key={rating.label} className="flex-1">
+ <div
+ className="mb-6 h-9 w-full"
+ style={{
+ height: `${rating.percentage}%`,
+ backgroundColor: getRatingColor(rating.value)
+ }}
+ role="progressbar"
+ aria-valuenow={rating.percentage}
+ aria-valuemin={0}
+ aria-valuemax={100}
+ aria-label={`${rating.label} ${rating.percentage}%`}
+ />
+ <span>{rating.label}</span>
+ </div>
+ ))}
</div>
</div>
);
}
const formatValue = (value: number | null, isPercentage?: boolean) => { | ||
if (value === null) return "-"; | ||
const formattedNumber = value.toLocaleString(); | ||
return isPercentage ? `${formattedNumber}%` : formattedNumber; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
formatValue 함수의 안전성 개선 필요
숫자 포맷팅 함수에 예외 처리가 필요합니다.
const formatValue = (value: number | null, isPercentage?: boolean) => {
if (value === null) return "-";
+ if (isNaN(value)) return "-";
+ if (!isFinite(value)) return "-";
const formattedNumber = value.toLocaleString();
return isPercentage ? `${formattedNumber}%` : formattedNumber;
};
- NaN과 Infinity 값에 대한 처리가 필요합니다.
- 큰 숫자의 경우 단위 표시(예: 백만, 억)를 고려해보세요.
📝 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 formatValue = (value: number | null, isPercentage?: boolean) => { | |
if (value === null) return "-"; | |
const formattedNumber = value.toLocaleString(); | |
return isPercentage ? `${formattedNumber}%` : formattedNumber; | |
}; | |
const formatValue = (value: number | null, isPercentage?: boolean) => { | |
if (value === null) return "-"; | |
if (isNaN(value)) return "-"; | |
if (!isFinite(value)) return "-"; | |
const formattedNumber = value.toLocaleString(); | |
return isPercentage ? `${formattedNumber}%` : formattedNumber; | |
}; |
{values.map((value, index) => ( | ||
<td | ||
// eslint-disable-next-line react/no-array-index-key | ||
key={index} | ||
className="border-r border-solid border-gray-100 last:border-r-0" | ||
> | ||
{formatValue(value, isPercentage)} | ||
</td> | ||
))} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
테이블 셀 렌더링 최적화 필요
배열 인덱스를 key로 사용하는 것은 권장되지 않습니다.
-{values.map((value, index) => (
- <td
- // eslint-disable-next-line react/no-array-index-key
- key={index}
- className="border-r border-solid border-gray-100 last:border-r-0"
- >
+{values.map((value, index) => (
+ <td
+ key={`${label}-${index}`}
+ className="border-r border-solid border-gray-100 last:border-r-0"
+ >
- 고유한 key 값을 생성하여 React의 재조정(reconciliation) 성능을 개선하세요.
- eslint 주석을 제거하고 올바른 key 사용을 권장합니다.
Committable suggestion skipped: line range outside the PR's diff.
export default function FinancialStatements({ | ||
data, | ||
}: FinancialStatementsProps) { | ||
if (!data || data.length === 0) { | ||
return <NoDataMessage />; | ||
} | ||
|
||
const years = data.map((item) => item.stockAccountingYearMonth); | ||
|
||
return ( | ||
<table className="mb-20 w-full"> | ||
<caption className="mb-10 text-left text-16-500">재무비율</caption> | ||
<TableHeader years={years} /> | ||
<tbody className="text-center"> | ||
{FINANCIAL_METRICS.map((metric) => ( | ||
<TableRow | ||
key={metric.key} | ||
label={metric.label} | ||
values={data.map((item) => | ||
item[metric.key] !== undefined ? Number(item[metric.key]) : null, | ||
)} | ||
isPercentage={metric.isPercentage} | ||
/> | ||
))} | ||
</tbody> | ||
</table> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
FinancialStatements 컴포넌트 개선 사항
메인 컴포넌트의 기능성 향상이 필요합니다.
- 데이터 정렬 기능 추가
- 메모이제이션을 통한 성능 최적화
- 반응형 테이블 구현
+import { useMemo } from 'react';
+
export default function FinancialStatements({
data,
}: FinancialStatementsProps) {
if (!data || data.length === 0) {
return <NoDataMessage />;
}
- const years = data.map((item) => item.stockAccountingYearMonth);
+ const years = useMemo(() =>
+ data
+ .map((item) => item.stockAccountingYearMonth)
+ .sort((a, b) => b.localeCompare(a)),
+ [data]
+ );
추가로 고려할 사항:
- 스켈레톤 UI 추가
- 데이터 로딩 상태 처리
- 에러 바운더리 구현
📝 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.
export default function FinancialStatements({ | |
data, | |
}: FinancialStatementsProps) { | |
if (!data || data.length === 0) { | |
return <NoDataMessage />; | |
} | |
const years = data.map((item) => item.stockAccountingYearMonth); | |
return ( | |
<table className="mb-20 w-full"> | |
<caption className="mb-10 text-left text-16-500">재무비율</caption> | |
<TableHeader years={years} /> | |
<tbody className="text-center"> | |
{FINANCIAL_METRICS.map((metric) => ( | |
<TableRow | |
key={metric.key} | |
label={metric.label} | |
values={data.map((item) => | |
item[metric.key] !== undefined ? Number(item[metric.key]) : null, | |
)} | |
isPercentage={metric.isPercentage} | |
/> | |
))} | |
</tbody> | |
</table> | |
); | |
} | |
import { useMemo } from 'react'; | |
export default function FinancialStatements({ | |
data, | |
}: FinancialStatementsProps) { | |
if (!data || data.length === 0) { | |
return <NoDataMessage />; | |
} | |
const years = useMemo(() => | |
data | |
.map((item) => item.stockAccountingYearMonth) | |
.sort((a, b) => b.localeCompare(a)), | |
[data] | |
); | |
return ( | |
<table className="mb-20 w-full"> | |
<caption className="mb-10 text-left text-16-500">재무비율</caption> | |
<TableHeader years={years} /> | |
<tbody className="text-center"> | |
{FINANCIAL_METRICS.map((metric) => ( | |
<TableRow | |
key={metric.key} | |
label={metric.label} | |
values={data.map((item) => | |
item[metric.key] !== undefined ? Number(item[metric.key]) : null, | |
)} | |
isPercentage={metric.isPercentage} | |
/> | |
))} | |
</tbody> | |
</table> | |
); | |
} |
500에러 토큰이 만료되서 그런거같으니까 백엔드측에 요청할게요! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
500 에러 부분은 백엔드 측에 토큰 지속적으로 갱신 해달라고 하겠습니다
#️⃣ 이슈
📝 작업 내용
이번 PR에서 작업한 내용을 간략히 설명해주세요.
📸 스크린샷
✅ 체크 리스트
👩💻 공유 포인트 및 논의 사항
자꾸 500에러 떠서 수정할 수가 없어 일단 하드코딩한채로 올립니다. api 연결하면서 수정예정입니다.
Summary by CodeRabbit
Consensus
,FinancialStatements
,CompanyOverview
,TargetPrice
,DetailInfo
, 및RelativeNews
컴포넌트가 추가되었습니다.DetailInfo
컴포넌트는 탭 인터페이스를 통해 회사 개요와 관련 뉴스를 표시합니다.globals.css
에 새로운 리스트 스타일 클래스가 추가되었습니다.