Skip to content
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

Merged
merged 8 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
@tailwind components;
@tailwind utilities;

html,
body {
width: 100%;
height: 100%;
}
.loadingSpinner {
width: 40px;
height: 40px;
Expand Down Expand Up @@ -42,3 +47,10 @@
src: url("https://fastly.jsdelivr.net/gh/Project-Noonnu/[email protected]/Pretendard-Regular.woff")
format("woff");
}

.list-style {
list-style-type: square;
padding-inline-start: 1.5em;
list-style-position: inside;
line-height: 24px;
}
2 changes: 1 addition & 1 deletion src/app/login/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function Layout({
children: React.ReactNode;
}>) {
return (
<div className="lg:max-w-2000 flex h-screen w-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 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">
{children}
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/app/main/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function Layout({
children: React.ReactNode;
}>) {
return (
<div className="lg:max-w-2000 flex h-screen flex-col px-4 py-8 pt-30 md:px-12 lg:px-32">
<div className="lg:max-w-2000 px-4 py-8 pt-30 md:px-12 lg:px-32">
{children}
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/app/members/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function Layout({
children: React.ReactNode;
}>) {
return (
<div className="lg:max-w-2000 flex h-screen w-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 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">
{children}
</div>
);
Expand Down
6 changes: 1 addition & 5 deletions src/app/my-account/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,5 @@ export default function Layout({
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<div className="lg:max-w-2000 flex h-screen flex-col bg-[#F5F6F8]">
{children}
</div>
);
return <div className="lg:max-w-2000 h-full bg-[#F5F6F8]">{children}</div>;
}
6 changes: 1 addition & 5 deletions src/app/portfolio/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,5 @@ export default function Layout({
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<div className="lg:max-w-2000 flex h-screen flex-col bg-[#F5F6F8]">
{children}
</div>
);
return <div className="lg:max-w-2000 h-full bg-[#F5F6F8]">{children}</div>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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>
);
}
Comment on lines +1 to +29
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

컨센서스 컴포넌트 개선사항

다음과 같은 개선이 필요합니다:

  1. 각 등급별 데이터를 동적으로 처리
  2. 접근성 개선 (aria-label 추가)
  3. 타입 정의
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>
   );
 }

Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
"use client";

type FinancialRatio = {
stockAccountingYearMonth: string;
grossMarginRatio: number;
businessProfitRate: number;
netInterestRate: number;
roeValue: number;
earningsPerShare: number;
salesPerShare: number;
bookValuePerShare: number;
reserveRate: number;
liabilityRate: number;
};

type NumberKeys = {
[K in keyof FinancialRatio]: FinancialRatio[K] extends number ? K : never;
}[keyof FinancialRatio];

type FinancialMetric = {
label: string;
key: NumberKeys;
isPercentage?: boolean;
};

const FINANCIAL_METRICS: FinancialMetric[] = [
{ label: "총마진율", key: "grossMarginRatio", isPercentage: true },
{ label: "사업 수익률", key: "businessProfitRate", isPercentage: true },
{ label: "순이자율", key: "netInterestRate", isPercentage: true },
{ label: "ROE", key: "roeValue", isPercentage: true },
{ label: "EPS(주당순이익)", key: "earningsPerShare" },
{ label: "SPS(주당매출액)", key: "salesPerShare" },
{ label: "BPS(주당순자산)", key: "bookValuePerShare" },
{ label: "주식유보율", key: "reserveRate", isPercentage: true },
{ label: "부채율", key: "liabilityRate", isPercentage: true },
];

const formatValue = (value: number | null, isPercentage?: boolean) => {
if (value === null) return "-";
const formattedNumber = value.toLocaleString();
return isPercentage ? `${formattedNumber}%` : formattedNumber;
};
Comment on lines +38 to +42
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

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;
 };
  1. NaN과 Infinity 값에 대한 처리가 필요합니다.
  2. 큰 숫자의 경우 단위 표시(예: 백만, 억)를 고려해보세요.
📝 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
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;
};


function TableHeader({ years }: { years: string[] }) {
return (
<thead className="border-y border-solid border-gray-100 bg-green-100">
<tr className="h-45">
<th
rowSpan={2}
className="border-r border-solid border-gray-100 text-16-500"
>
주요재무정보
</th>
<th colSpan={years.length} className="text-16-500">
연간
</th>
</tr>
<tr className="h-45 border-t border-solid border-gray-100">
{years.map((year) => (
<th
key={year}
className="border-x border-solid border-gray-100 text-16-500 last:border-r-0"
>
{year}
</th>
))}
</tr>
</thead>
);
}

function TableRow({
label,
values,
isPercentage,
}: {
label: string;
values: (number | null)[];
isPercentage?: boolean;
}) {
return (
<tr className="h-45 border-y border-solid border-gray-100">
<th className="border-r border-solid border-gray-100 text-16-500">
{label}
</th>
{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>
))}
Comment on lines +86 to +94
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

테이블 셀 렌더링 최적화 필요

배열 인덱스를 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"
+  >
  1. 고유한 key 값을 생성하여 React의 재조정(reconciliation) 성능을 개선하세요.
  2. eslint 주석을 제거하고 올바른 key 사용을 권장합니다.

Committable suggestion skipped: line range outside the PR's diff.

</tr>
);
}

function NoDataMessage() {
return (
<div className="w-full p-8 text-center text-gray-500">
데이터가 없습니다.
</div>
);
}

interface FinancialStatementsProps {
data?: FinancialRatio[];
}

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>
);
}
Comment on lines +111 to +138
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

FinancialStatements 컴포넌트 개선 사항

메인 컴포넌트의 기능성 향상이 필요합니다.

  1. 데이터 정렬 기능 추가
  2. 메모이제이션을 통한 성능 최적화
  3. 반응형 테이블 구현
+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]
+  );

추가로 고려할 사항:

  1. 스켈레톤 UI 추가
  2. 데이터 로딩 상태 처리
  3. 에러 바운더리 구현
📝 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
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>
);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client";

import Consensus from "./consensus";
import FinancialStatements from "./financial-statements";
import TargetPrice from "./target-price";

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>
Comment on lines +11 to +12
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

날짜 하드코딩 개선 필요

기준 날짜가 하드코딩되어 있습니다. 실제 데이터와 연동 시 동적으로 처리되어야 합니다.

-<span className="text-14-500 text-gray-100">[기준: 2024. 08. 16]</span>
+<span className="text-14-500 text-gray-100">[기준: {formatDate(referenceDate)}]</span>

Committable suggestion skipped: line range outside the PR's diff.

</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>
Comment on lines +14 to +18
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

리스트 아이템 중복 및 하드코딩

동일한 텍스트가 3번 반복되어 있습니다. 실제 데이터를 매핑하는 구조로 변경이 필요합니다.

-<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>
+<ul className="list-style mb-24 border-y-2 border-solid border-gray-100 py-14 text-14-500">
+  {companyDetails.map((detail, index) => (
+    <li key={index}>{detail}</li>
+  ))}
+</ul>

Committable suggestion skipped: line range outside the PR's diff.


<FinancialStatements />
<Consensus />
<TargetPrice />
<div className="text-14-500 text-gray-100">
전 세계 1200개 리서치 회사의 정보를 종합적으로 분석합니다. <br />
기준 2024.08.29 . 레피니티브 제공
</div>
Comment on lines +23 to +26
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

분석 정보 날짜 동적 처리 필요

분석 정보의 기준 날짜도 하드코딩되어 있습니다. API 연동 시 실제 데이터를 사용하도록 수정이 필요합니다.

-기준 2024.08.29 . 레피니티브 제공
+기준 {formatDate(analysisDate)} . 레피니티브 제공

Committable suggestion skipped: line range outside the PR's diff.

</div>
);
}
Comment on lines +7 to +29
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

타입 안정성 및 접근성 개선 필요

  1. TypeScript 인터페이스 정의가 필요합니다.
  2. 시맨틱 HTML 요소 사용이 필요합니다.
interface CompanyOverviewProps {
  referenceDate: Date;
  companyDetails: string[];
  analysisDate: Date;
}
-export default function CompanyOverview() {
+export default function CompanyOverview({
+  referenceDate,
+  companyDetails,
+  analysisDate
+}: CompanyOverviewProps) {

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
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>
);
}
Comment on lines +1 to +17
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

하드코딩된 값들을 props로 전달하도록 개선이 필요합니다.

현재 목표주가와 범위가 하드코딩되어 있어 재사용성이 제한됩니다. 다음과 같은 개선사항을 제안드립니다:

  1. 데이터를 props로 전달받도록 수정
  2. 가격 범위 표시 바에 대한 접근성 개선 (aria-label 추가)
  3. 반응형 디자인 고려

다음과 같이 수정하는 것을 제안드립니다:

- 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.

Suggested change
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>
);
}

31 changes: 31 additions & 0 deletions src/app/search/[id]/_components/detail-info/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"use client";

import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/components/common/tabs";

import CompanyOverview from "./company-overview";
import RelativeNews from "./relative-news";

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>
);
}
Comment on lines +13 to +31
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

탭 컴포넌트의 상태 관리 및 에러 처리 개선이 필요합니다.

현재 구현에서 다음과 같은 개선사항이 필요합니다:

  1. 탭 상태 유지 관리
  2. 에러 바운더리 추가
  3. 로딩 상태 처리

다음과 같은 구조를 제안드립니다:

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>
  );
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"use client";

export default function RelativeNews() {
return (
<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>
코스피, 외국인 &quot;바이 코리아&quot;에 2680 선 마감.. LG 에너지
솔루
</td>
<td>머니 투데이</td>
<td>2024.09.02 </td>
</tr>
</tbody>
</table>
Comment on lines +5 to +24
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

테이블 구조 및 접근성 개선이 필요합니다.

현재 테이블 구현에서 다음과 같은 개선사항이 필요합니다:

  1. 테이블 헤더에 적절한 스코프 속성 추가
  2. 반응형 테이블 구현 (모바일 대응)
  3. 데이터 로딩 상태 처리

다음과 같이 수정하는 것을 제안드립니다:

- <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">
        코스피, 외국인 &quot;바이 코리아&quot;에 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.

Suggested change
<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>
코스피, 외국인 &quot;바이 코리아&quot; 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">
코스피, 외국인 &quot;바이 코리아&quot; 2680 마감.. LG 에너지
솔루
</td>
<td className="p-2">머니 투데이</td>
<td className="p-2">2024.09.02</td>
</tr>
</tbody>
</table>
</div>

);
}
6 changes: 1 addition & 5 deletions src/app/search/[id]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,5 @@ export default function Layout({
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<div className="lg:max-w-2000 flex h-screen flex-col bg-[#F5F6F8]">
{children}
</div>
);
return <div className="lg:max-w-2000 bg-[#F5F6F8]">{children}</div>;
Copy link

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.tsxportfolio/layout.tsxh-full 클래스를 사용
  • search/layout.tsxflex flex-col 클래스를 사용
  • 현재 파일(search/[id]/layout.tsx)은 이러한 클래스들이 모두 제거됨

상위 검색 페이지(search/layout.tsx)에서 사용 중인 flex flex-col 레이아웃과의 일관성을 위해 이러한 클래스들의 재검토가 필요합니다.

🔗 Analysis chain

레이아웃 스타일 변경사항 검토 필요

화면 높이 관련 클래스(h-screen)와 flex 관련 클래스(flex-col)가 제거되었습니다. 이로 인해 컨텐츠 높이가 동적으로 조정될 수 있습니다. 다음 사항들을 확인해주세요:

  1. 스크롤 동작이 의도한대로 작동하는지
  2. 하위 컴포넌트들의 배치가 올바른지

다음 스크립트로 다른 레이아웃 파일들과의 일관성을 확인할 수 있습니다:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# 레이아웃 컴포넌트들의 스타일 클래스 검색
rg -g "layout.tsx" "className=" src/app

Length of output: 1261

}
2 changes: 2 additions & 0 deletions src/app/search/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Link from "next/link";

import CandlestickChartContainer from "./_components/candle-chart-container";
import DetailInfo from "./_components/detail-info";
import OrderStock from "./_components/order-stock";
import StockHeader from "./_components/stock-header";
import TutorialContainer from "./_components/tutorial/tutorial-container";
Expand Down Expand Up @@ -74,6 +75,7 @@ export default async function StockPage({

<OrderStock stockName={stockName} stockInfo={initialData.stockData} />
</div>
<DetailInfo />
</div>
);
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion src/app/search/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ export default function Layout({
}: Readonly<{
children: React.ReactNode;
}>) {
return <div className="lg:max-w-2000 flex h-screen flex-col">{children}</div>;
return <div className="lg:max-w-2000 flex flex-col">{children}</div>;
}
2 changes: 1 addition & 1 deletion src/components/nav-bar/_components/nav-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export default function NavMenu() {
};

return (
<menu className="fixed left-0 top-0 z-[999] flex h-screen w-82 flex-col items-center border-r bg-white py-20">
<menu className="fixed left-0 top-0 z-[999] flex h-full w-82 flex-col items-center border-r bg-white py-20">
<LogoIcon />
<div className="mt-30 flex flex-col items-center space-y-45">
{NAV_ITEMS.map((item) => (
Expand Down
Loading
Loading