Skip to content

Refactor/next cache rsc #116

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

Open
wants to merge 4 commits into
base: refactor/frontend-structure
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { CourseDetail } from '@/types/api/academic';
import { cache } from 'react';

interface IAcademicDetailProps {
semesterGrades: Array<{
year: number;
semester: string;
earnedCredits: number;
attemptedCredits: number;
semesterGpa: number;
classRank?: number;
totalStudents?: number;
}>;
courses: {
major: CourseDetail[];
liberal: CourseDetail[];
};
}
export const getAcademicDetail = cache(async (year: number, semester: number) : Promise<IAcademicDetailProps> => {
console.log('🔥 실제 fetch 발생! [getAcademicDetail]');
// const cookieStore = await cookies();
// const token = cookieStore.getAll();

Comment on lines +20 to +23
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

불필요한 코드 제거 필요

디버깅용 콘솔 로그와 주석 처리된 코드는 프로덕션 환경에서 제거하는 것이 좋습니다.

- console.log('🔥 실제 fetch 발생! [getAcademicDetail]');
- // const cookieStore = await cookies();
- // const token = cookieStore.getAll();
📝 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
console.log('🔥 실제 fetch 발생! [getAcademicDetail]');
// const cookieStore = await cookies();
// const token = cookieStore.getAll();

const abtoken = process.env.TEST_TOKEN;
const token = process.env.TEST_TOKEN2;


const res = await fetch(`http://localhost:3000/api/get-academic/${year}/${semester}`, {
cache: 'force-cache',
credentials: 'include',
headers: {
'Cookie': `${abtoken}; ${token}`,
},
next: {
revalidate: 60 * 60 * 24,
tags: [`academic-detail-${year}-${semester}`],
},
});

const result = await res.json();




return {
semesterGrades: result.semesterGrades,
courses: result.courses,
};
Comment on lines +40 to +48
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

불필요한 공백 라인 정리 필요

코드의 가독성을 높이기 위해 불필요한 공백 라인을 정리해주세요.

  const result = await res.json();

-

-

-

  
  return {
    semesterGrades: result.semesterGrades,
    courses: result.courses,
  };
📝 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 result = await res.json();
return {
semesterGrades: result.semesterGrades,
courses: result.courses,
};
const result = await res.json();
return {
semesterGrades: result.semesterGrades,
courses: result.courses,
};

});
Comment on lines +19 to +49
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

데이터 페칭 구현이 로컬 환경에 의존하고 있습니다.

API 엔드포인트가 하드코딩된 localhost:3000을 사용하고 있으며, 환경 변수에서 테스트 토큰을 직접 가져오고 있습니다. 이는 프로덕션 환경에서 작동하지 않을 수 있습니다.

다음과 같이 수정하는 것을 권장합니다:

- const abtoken = process.env.TEST_TOKEN;
- const token = process.env.TEST_TOKEN2;

- const res = await fetch(`http://localhost:3000/api/get-academic/${year}/${semester}`, {
+ // 상대 경로 또는 환경 변수를 사용하여 API 엔드포인트 설정
+ const res = await fetch(`/api/get-academic/${year}/${semester}`, {
  cache: 'force-cache', 
  credentials: 'include',
-  headers: {
-    'Cookie': `${abtoken}; ${token}`,
-  },
  next: {
    revalidate: 60 * 60 * 24,
    tags: [`academic-detail-${year}-${semester}`],
  },
});
📝 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 const getAcademicDetail = cache(async (year: number, semester: number) : Promise<IAcademicDetailProps> => {
console.log('🔥 실제 fetch 발생! [getAcademicDetail]');
// const cookieStore = await cookies();
// const token = cookieStore.getAll();
const abtoken = process.env.TEST_TOKEN;
const token = process.env.TEST_TOKEN2;
const res = await fetch(`http://localhost:3000/api/get-academic/${year}/${semester}`, {
cache: 'force-cache',
credentials: 'include',
headers: {
'Cookie': `${abtoken}; ${token}`,
},
next: {
revalidate: 60 * 60 * 24,
tags: [`academic-detail-${year}-${semester}`],
},
});
const result = await res.json();
return {
semesterGrades: result.semesterGrades,
courses: result.courses,
};
});
export const getAcademicDetail = cache(async (year: number, semester: number) : Promise<IAcademicDetailProps> => {
console.log('🔥 실제 fetch 발생! [getAcademicDetail]');
// const cookieStore = await cookies();
// const token = cookieStore.getAll();
// 상대 경로 또는 환경 변수를 사용하여 API 엔드포인트 설정
const res = await fetch(`/api/get-academic/${year}/${semester}`, {
cache: 'force-cache',
credentials: 'include',
next: {
revalidate: 60 * 60 * 24,
tags: [`academic-detail-${year}-${semester}`],
},
});
const result = await res.json();
return {
semesterGrades: result.semesterGrades,
courses: result.courses,
};
});

25 changes: 25 additions & 0 deletions app/(main)/academic-detail/[year]/[semester]/apis/getSemesters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { cache } from 'react';

interface Semester {
year: number;
semester: number;
}


export const getSemesters = cache(async () : Promise<Semester[]> => {
const abtoken = process.env.TEST_TOKEN;
const token = process.env.TEST_TOKEN2;
console.log('🔥 실제 fetch 발생! [getSemesters]');
Comment on lines +9 to +12
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

환경 변수 사용 방식 검토 필요

개발 목적으로 보이는 TEST_TOKENTEST_TOKEN2 환경 변수명이 사용되고 있습니다. 운영 환경에서는 더 명확한 변수명을 사용하는 것이 좋습니다. 또한 콘솔 로그는 운영 환경에서 제거하는 것이 좋습니다.

-  const abtoken = process.env.TEST_TOKEN;
-  const token = process.env.TEST_TOKEN2;
-  console.log('🔥 실제 fetch 발생! [getSemesters]');
+  const authToken = process.env.AUTH_TOKEN;
+  const sessionToken = process.env.SESSION_TOKEN;
📝 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 const getSemesters = cache(async () : Promise<Semester[]> => {
const abtoken = process.env.TEST_TOKEN;
const token = process.env.TEST_TOKEN2;
console.log('🔥 실제 fetch 발생! [getSemesters]');
export const getSemesters = cache(async () : Promise<Semester[]> => {
const authToken = process.env.AUTH_TOKEN;
const sessionToken = process.env.SESSION_TOKEN;


const res = await fetch(`http://localhost:3000/api/get-semesters`, {
cache: 'force-cache',
headers: {
'Cookie': `${abtoken}; ${token}`,
},
credentials: 'include',
next: { tags: ['semesters'], revalidate: 60 * 60 * 24 }
});
Comment on lines +14 to +21
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

하드코딩된 URL 및 캐싱 전략 재검토 필요

로컬호스트 URL이 하드코딩되어 있어 운영 환경에서 문제가 될 수 있습니다. 상대 경로나 환경 변수를 사용하는 것이 더 적절합니다. 또한 force-cachenext.revalidate를 동시에 사용하고 있는데, 이 두 설정의 상호작용을 확인해야 합니다.

-  const res = await fetch(`http://localhost:3000/api/get-semesters`, {
+  const res = await fetch(`/api/get-semesters`, {
📝 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 res = await fetch(`http://localhost:3000/api/get-semesters`, {
cache: 'force-cache',
headers: {
'Cookie': `${abtoken}; ${token}`,
},
credentials: 'include',
next: { tags: ['semesters'], revalidate: 60 * 60 * 24 }
});
const res = await fetch(`/api/get-semesters`, {
cache: 'force-cache',
headers: {
'Cookie': `${abtoken}; ${token}`,
},
credentials: 'include',
next: { tags: ['semesters'], revalidate: 60 * 60 * 24 }
});


if (!res.ok) {throw new Error('Failed to fetch');}
return res.json();
});
Comment on lines +23 to +25
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

오류 처리 개선 필요

현재 오류 처리는 기본적인 메시지만 제공합니다. 사용자에게 더 명확한 오류 메시지를 제공하고, 가능하다면 오류 상황에 맞는 구체적인 처리 로직을 추가하는 것이 좋습니다.

-  if (!res.ok) {throw new Error('Failed to fetch');}  
+  if (!res.ok) {
+    const errorText = await res.text().catch(() => 'Unknown error');
+    throw new Error(`학기 정보를 가져오는데 실패했습니다: ${res.status} ${errorText}`);
+  }
📝 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
if (!res.ok) {throw new Error('Failed to fetch');}
return res.json();
});
if (!res.ok) {
const errorText = await res.text().catch(() => 'Unknown error');
throw new Error(`학기 정보를 가져오는데 실패했습니다: ${res.status} ${errorText}`);
}
return res.json();
});

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import AcademicSummaryCard from "../../../../components/AcademicSummaryCard/AcademicSummaryCard";
import SectionCourses from "./SectionCourses/SectionCourses";
import type { CourseDetail } from "@/types/api/academic";

interface AcademicDetailProps {
semesterGrades: Array<{
year: number;
semester: string;
earnedCredits: number;
attemptedCredits: number;
semesterGpa: number;
classRank?: number;
totalStudents?: number;
}>;
courses: {
major: CourseDetail[];
liberal: CourseDetail[];
};
}

export default function AcademicDetailContent({data}: {data: AcademicDetailProps}) {

console.log('data',data);
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

프로덕션 코드에서 디버깅 콘솔 로그 제거 필요

디버깅을 위한 콘솔 로그는 프로덕션 코드에서 제거해야 합니다.

- console.log('data',data);
📝 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
console.log('data',data);

return (
<>
{/* 학기 성적 요약 */}
<AcademicSummaryCard
earnedCredits={data.semesterGrades[0].earnedCredits}
gpa={data.semesterGrades[0].semesterGpa}
classRank={data.semesterGrades[0].classRank}
totalStudents={data.semesterGrades[0].totalStudents}
/>
Comment on lines +27 to +32
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

예외 처리 필요

data.semesterGrades가 비어 있거나 undefined인 경우에 대한 예외 처리가 없습니다. 데이터가 없는 경우 semesterGrades[0]에 접근하면 오류가 발생할 수 있습니다.

+ {data.semesterGrades?.length > 0 ? (
  <AcademicSummaryCard
    earnedCredits={data.semesterGrades[0].earnedCredits}
    gpa={data.semesterGrades[0].semesterGpa} 
    classRank={data.semesterGrades[0].classRank}
    totalStudents={data.semesterGrades[0].totalStudents}
  />
+ ) : (
+  <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
<AcademicSummaryCard
earnedCredits={data.semesterGrades[0].earnedCredits}
gpa={data.semesterGrades[0].semesterGpa}
classRank={data.semesterGrades[0].classRank}
totalStudents={data.semesterGrades[0].totalStudents}
/>
{data.semesterGrades?.length > 0 ? (
<AcademicSummaryCard
earnedCredits={data.semesterGrades[0].earnedCredits}
gpa={data.semesterGrades[0].semesterGpa}
classRank={data.semesterGrades[0].classRank}
totalStudents={data.semesterGrades[0].totalStudents}
/>
) : (
<div>학기 성적 정보가 없습니다.</div>
)}

<div className="gap-24"></div>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

자식 요소가 없는 JSX 요소는 self-closing 태그로 사용

자식 요소가 없는 div 요소는 self-closing 태그로 사용하는 것이 권장됩니다.

- <div className="gap-24"></div>
+ <div className="gap-24" />
📝 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
<div className="gap-24"></div>
<div className="gap-24" />
🧰 Tools
🪛 Biome (1.9.4)

[error] 33-34: JSX elements without children should be marked as self-closing. In JSX, it is valid for any element to be self-closing.

Unsafe fix: Use a SelfClosingElement instead

(lint/style/useSelfClosingElements)

{/* 전공 과목 목록 */}
<SectionCourses title="전공" courses={data.courses.major} />
{/* 교양 과목 목록 */}
<div className="gap-24"></div>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

자식 요소가 없는 JSX 요소는 self-closing 태그로 사용

자식 요소가 없는 div 요소는 self-closing 태그로 사용하는 것이 권장됩니다.

- <div className="gap-24"></div>
+ <div className="gap-24" />
📝 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
<div className="gap-24"></div>
<div className="gap-24" />

<SectionCourses title="교양" courses={data.courses.liberal} />
</>
);
}

11 changes: 11 additions & 0 deletions app/(main)/academic-detail/[year]/[semester]/components/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use client'

import { TopNavigation } from "@/components/ui/TopNavigation";
import { useInternalRouter } from "@/hooks/useInternalRouter";

export default function NavBar() {
const router = useInternalRouter();
return (
<TopNavigation.Preset title="학기별 세부 성적" type="back" onNavigationClick={() => router.back()} />
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client';

import { useEffect, useState } from 'react';
import { ROUTES } from '@/constants/routes';
import { useInternalRouter } from '@/hooks/useInternalRouter';
import styles from './SemesterSlider.module.scss';
Expand All @@ -13,42 +12,15 @@ interface Semester {
interface SemesterSliderProps {
currentYear: number;
currentSemester: number;
semesters: Semester[];
}

export default function SemesterSlider({ currentYear, currentSemester }: SemesterSliderProps) {
export default function SemesterSlider({ currentYear, currentSemester , semesters}: SemesterSliderProps) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

컴포넌트 서명이 업데이트되었지만 포맷이 개선될 수 있습니다.

함수 서명에서 currentSemester 다음에 공백이 누락되어 있습니다. 포맷을 일관되게 유지하기 위해 수정하는 것이 좋겠습니다.

-export default function SemesterSlider({ currentYear, currentSemester , semesters}: SemesterSliderProps) {
+export default function SemesterSlider({ currentYear, currentSemester, semesters }: SemesterSliderProps) {
📝 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 SemesterSlider({ currentYear, currentSemester , semesters}: SemesterSliderProps) {
export default function SemesterSlider({ currentYear, currentSemester, semesters }: SemesterSliderProps) {

const router = useInternalRouter();
const [semesters, setSemesters] = useState<Semester[]>([]);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
const fetchSemesters = async () => {
try {
const response = await fetch('/api/get-semesters', {
next: {
revalidate: 3600, // 1시간마다 재검증
},
});
if (!response.ok) {
throw new Error('Failed to fetch semesters');
}
const data = await response.json();
setSemesters(data);
} catch (error) {
console.error('Error fetching semesters:', error);
} finally {
setIsLoading(false);
}
};

fetchSemesters();
}, []);

if (isLoading) {
return null;
}

const handleSemesterClick = (year: number, semester: number) => {
router.replace(ROUTES.ACADEMIC_DETAIL, { year, semester });
router.replace(ROUTES.ACADEMIC_DETAIL, { params: [year, semester] });
};

const getSemesterLabel = (semester: number): string => {
Expand All @@ -66,6 +38,7 @@ export default function SemesterSlider({ currentYear, currentSemester }: Semeste
}
};


return (
<div className={styles.sliderContainer}>
{semesters.map(sem => (
Expand Down
14 changes: 14 additions & 0 deletions app/(main)/academic-detail/[year]/[semester]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const fetchCache = 'auto'

import NavBar from './components/NavBar';
import styles from './layout.module.scss';

export default function GraduationProgressLayout({ children }: { children: React.ReactNode }) {

return (
<div className={styles.container}>
<NavBar />
<div className={styles.content}>{children}</div>
</div>
);
}
Comment on lines +6 to +14
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

컴포넌트 이름과 기능 간의 불일치가 있습니다.

컴포넌트 이름은 GraduationProgressLayout이지만 NavBar의 제목은 "학기별 세부 성적"입니다. 이는 명명 규칙에 혼란을 줄 수 있습니다.

-export default function GraduationProgressLayout({ children }: { children: React.ReactNode }) {
+export default function AcademicDetailLayout({ children }: { children: React.ReactNode }) {
📝 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 GraduationProgressLayout({ children }: { children: React.ReactNode }) {
return (
<div className={styles.container}>
<NavBar />
<div className={styles.content}>{children}</div>
</div>
);
}
export default function AcademicDetailLayout({ children }: { children: React.ReactNode }) {
return (
<div className={styles.container}>
<NavBar />
<div className={styles.content}>{children}</div>
</div>
);
}

27 changes: 27 additions & 0 deletions app/(main)/academic-detail/[year]/[semester]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

import { Suspense } from 'react';
import AcademicDetailContent from './components/AcademicDetailContent';
import { getSemesters } from './apis/getSemesters';
import { getAcademicDetail } from './apis/getAcademicDetail';
import SemesterSlider from './components/SemesterSlider';

export const fetchCache = 'force-cache';

export default async function AcademicDetailPage({params}: {params: Promise<{year: number, semester: number}>}) {
const {year, semester} = await params;

const [semesters, data] = await Promise.all([
getSemesters(),
getAcademicDetail(year, semester),
]);

return (
<Suspense fallback={<div></div>}>
<div className="gap-8"></div>
<SemesterSlider currentYear={year} currentSemester={semester} semesters={semesters} />
<div className="gap-20"></div>
<AcademicDetailContent data={data} />
Comment on lines +19 to +23
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

빈 div 요소 개선 필요

비어있는 div 요소가 간격을 위해 사용되고 있습니다. 하지만 이는 시맨틱하지 않고, 스타일링을 위한 요소 생성은 좋은 방법이 아닙니다. 마진이나 패딩을 사용하거나, 전용 스페이서 컴포넌트를 만드는 것이 더 나은 방법입니다. 또한 정적 분석 도구에서 지적한 대로 자식 요소가 없는 JSX 요소는 자체 닫힘 태그로 표시해야 합니다.

-      <div className="gap-8"></div>
+      <div className="gap-8" />
       <SemesterSlider currentYear={year} currentSemester={semester} semesters={semesters} />
-      <div className="gap-20"></div>
+      <div className="gap-20" />
📝 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
<Suspense fallback={<div></div>}>
<div className="gap-8"></div>
<SemesterSlider currentYear={year} currentSemester={semester} semesters={semesters} />
<div className="gap-20"></div>
<AcademicDetailContent data={data} />
<Suspense fallback={<div></div>}>
<div className="gap-8" />
<SemesterSlider currentYear={year} currentSemester={semester} semesters={semesters} />
<div className="gap-20" />
<AcademicDetailContent data={data} />
🧰 Tools
🪛 Biome (1.9.4)

[error] 19-19: JSX elements without children should be marked as self-closing. In JSX, it is valid for any element to be self-closing.

Unsafe fix: Use a SelfClosingElement instead

(lint/style/useSelfClosingElements)


[error] 20-20: JSX elements without children should be marked as self-closing. In JSX, it is valid for any element to be self-closing.

Unsafe fix: Use a SelfClosingElement instead

(lint/style/useSelfClosingElements)


[error] 22-22: JSX elements without children should be marked as self-closing. In JSX, it is valid for any element to be self-closing.

Unsafe fix: Use a SelfClosingElement instead

(lint/style/useSelfClosingElements)

</Suspense>
);
Comment on lines +18 to +25
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Suspense 사용이 적절합니다.

Suspense를 사용하여 로딩 상태를 처리하는 방식은 적절합니다. 다만, 현재 fallback은 비어 있는데, 사용자 경험을 향상시키기 위해 로딩 인디케이터나 스켈레톤 UI를 추가하는 것을 고려해보세요.

-    <Suspense fallback={<div></div>}>
+    <Suspense fallback={<div className="p-4 text-center">학업 정보를 불러오는 중...</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
return (
<Suspense fallback={<div></div>}>
<div className="gap-8"></div>
<SemesterSlider currentYear={year} currentSemester={semester} semesters={semesters} />
<div className="gap-20"></div>
<AcademicDetailContent data={data} />
</Suspense>
);
return (
<Suspense fallback={<div className="p-4 text-center">학업 정보를 불러오는 중...</div>}>
<div className="gap-8"></div>
<SemesterSlider currentYear={year} currentSemester={semester} semesters={semesters} />
<div className="gap-20"></div>
<AcademicDetailContent data={data} />
</Suspense>
);
🧰 Tools
🪛 Biome (1.9.4)

[error] 19-19: JSX elements without children should be marked as self-closing. In JSX, it is valid for any element to be self-closing.

Unsafe fix: Use a SelfClosingElement instead

(lint/style/useSelfClosingElements)


[error] 20-20: JSX elements without children should be marked as self-closing. In JSX, it is valid for any element to be self-closing.

Unsafe fix: Use a SelfClosingElement instead

(lint/style/useSelfClosingElements)


[error] 22-22: JSX elements without children should be marked as self-closing. In JSX, it is valid for any element to be self-closing.

Unsafe fix: Use a SelfClosingElement instead

(lint/style/useSelfClosingElements)

}

94 changes: 0 additions & 94 deletions app/(main)/academic-detail/components/AcademicDetailContent.tsx

This file was deleted.

16 changes: 0 additions & 16 deletions app/(main)/academic-detail/layout.tsx

This file was deleted.

14 changes: 0 additions & 14 deletions app/(main)/academic-detail/page.tsx

This file was deleted.

8 changes: 2 additions & 6 deletions app/api/get-academic/[year]/[semester]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { NextResponse } from 'next/server';
import { createClient } from '@/lib/supabase/server';
import type { Database } from '@/types/supabase';

export const dynamic = 'force-dynamic';

type CourseAreaType = Database['public']['Enums']['course_area_type'];

Expand Down Expand Up @@ -82,13 +81,10 @@ function getCourseCategory(areaType: CourseAreaType): 'major' | 'liberal' {
return 'liberal';
}

export async function GET(
request: NextRequest,
props: { params: Promise<{ year: string; semester: string }> }
) {
export async function GET(request: NextRequest, props: { params: Promise<{ year: string; semester: string }> }) {
const params = await props.params;
try {
const supabase = createClient();
const supabase = await createClient();
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Supabase 클라이언트 비동기 초기화 수정

createClient() 함수를 await로 호출하여 비동기 초기화를 올바르게 처리하도록 수정되었습니다. 이전 코드는 비동기 함수를 제대로 기다리지 않아 잠재적인 문제가 있었습니다.

이 수정은 중요한 버그를 해결하며, Supabase 클라이언트가 완전히 초기화된 후에 사용되도록 보장합니다.


// 인증된 사용자 확인
const {
Expand Down
Loading