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

Refactor/next cache rsc #116

Open
wants to merge 4 commits into
base: refactor/frontend-structure
Choose a base branch
from

Conversation

gyumong
Copy link
Owner

@gyumong gyumong commented Apr 6, 2025

Summary by CodeRabbit

  • 신규 기능

    • 새 학기별 성적 상세 페이지가 추가되어, 최근 성적 정보와 전공 및 교양 과목 내역을 한눈에 확인할 수 있습니다.
    • 개선된 학기 선택 슬라이더와 네비게이션 바로 직관적인 인터페이스와 손쉬운 학기 전환이 가능합니다.
  • 개선 사항

    • 데이터 캐싱 및 비동기 처리 최적화를 통해 성적 조회 속도가 향상되었습니다.

Copy link

vercel bot commented Apr 6, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
chukchuk-haksa ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 6, 2025 5:16pm

Copy link
Contributor

coderabbitai bot commented Apr 6, 2025

Walkthrough

이번 변경 사항은 학사 상세 정보와 관련된 새로운 API와 React 컴포넌트를 추가하고, 기존 관련 파일들을 대체하는 내용을 포함합니다. 또한 API 라우트 및 Supabase 클라이언트의 생성 과정을 동기식에서 비동기식으로 전환하고, 서비스 클래스들과 파사드에서 정적 비동기 팩토리 메서드를 도입하는 리팩토링이 이루어졌습니다.

Changes

파일 변경 사항
app/(main)/academic-detail/[year]/[semester]/apis/getAcademicDetail.ts,
app/(main)/academic-detail/[year]/[semester]/apis/getSemesters.ts,
app/(main)/academic-detail/[year]/[semester]/components/AcademicDetailContent.tsx,
app/(main)/academic-detail/[year]/[semester]/components/NavBar.tsx,
app/(main)/academic-detail/[year]/[semester]/components/SemesterSlider.tsx,
app/(main)/academic-detail/[year]/[semester]/layout.tsx,
app/(main)/academic-detail/[year]/[semester]/page.tsx
새로운 API, 컴포넌트, 레이아웃 파일 추가(캐시 전략, 데이터 페칭, 인터페이스 선언 포함)
app/(main)/academic-detail/components/AcademicDetailContent.tsx,
app/(main)/academic-detail/layout.tsx,
app/(main)/academic-detail/page.tsx
기존 학사 상세 관련 파일 삭제
app/api/get-academic/[year]/[semester]/route.ts,
app/api/get-dashboard/route.ts,
app/api/get-semesters/route.ts,
app/api/graduation-progress/route.ts,
app/api/suwon-scrape/resync/route.ts,
app/api/suwon-scrape/start/route.ts,
app/api/target-gpa/route.ts,
app/api/users/delete/route.ts
API 라우트 내에서 Supabase 클라이언트 생성 함수를 비동기식(await createClient())으로 전환 및 포맷 정리, 불필요한 export 제거
lib/auth/supabase.ts,
lib/supabase/server.ts
createClient 함수 및 쿠키 처리 방식을 동기에서 비동기로 전환
lib/supabase/facades/dashboard-facade.ts,
lib/supabase/services/academic-record-service.ts,
lib/supabase/services/course-offering-service.ts,
lib/supabase/services/course-service.ts,
lib/supabase/services/department-service.ts,
lib/supabase/services/graduation-progress-service.ts,
lib/supabase/services/professor-service.ts,
lib/supabase/services/student-course-service.ts,
lib/supabase/services/student-service.ts
각 서비스 및 파사드 클래스에서 생성자 기본값 제거 및 SupabaseClient를 명시적으로 전달하도록 수정, 정적 비동기 팩토리 메서드(static async create()) 추가
server/infrastructure/supabase/repository/SupabaseProfessorRepository.ts Supabase 클라이언트를 명시적으로 전달하는 생성자 변경 및 정적 비동기 팩토리 메서드 추가
package.json "dev" 스크립트에서 Turbopack 플래그(--turbopack) 제거

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AcademicDetailPage
  participant API_GetSemesters as getSemesters API
  participant API_GetAcademicDetail as getAcademicDetail API
  participant SemesterSlider
  participant AcademicDetailContent

  User->>AcademicDetailPage: 페이지 요청
  AcademicDetailPage->>API_GetSemesters: 학기 데이터 요청
  AcademicDetailPage->>API_GetAcademicDetail: 학사 상세 데이터 요청
  API_GetSemesters-->>AcademicDetailPage: 학기 데이터 응답
  API_GetAcademicDetail-->>AcademicDetailPage: 학사 상세 데이터 응답
  AcademicDetailPage->>SemesterSlider: 데이터 전달 후 렌더링
  AcademicDetailPage->>AcademicDetailContent: 데이터 전달 후 렌더링
Loading
sequenceDiagram
  participant Caller
  participant DashboardFacade
  participant SupabaseClient
  participant StudentService
  participant AcademicRecordService

  Caller->>DashboardFacade: await DashboardFacade.create()
  DashboardFacade->>SupabaseClient: await createClient()
  DashboardFacade->>StudentService: await StudentService.create()
  DashboardFacade->>AcademicRecordService: await AcademicRecordService.create()
  DashboardFacade-->>Caller: DashboardFacade 인스턴스 반환
Loading

Poem

봄바람 맞으며 코드길을 달려요,
토끼 발자국 소리처럼 깔끔하게!
비동기 리듬에 맞춰 새로움 춤추고,
API와 컴포넌트가 만나 노래하네 🐇
변화의 길 위를 즐겁게 뛰노는 우리!

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary or @횟초리 to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @일해라노예야 anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 15

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e876115 and 875463c.

📒 Files selected for processing (31)
  • app/(main)/academic-detail/[year]/[semester]/apis/getAcademicDetail.ts (1 hunks)
  • app/(main)/academic-detail/[year]/[semester]/apis/getSemesters.ts (1 hunks)
  • app/(main)/academic-detail/[year]/[semester]/components/AcademicDetailContent.tsx (1 hunks)
  • app/(main)/academic-detail/[year]/[semester]/components/NavBar.tsx (1 hunks)
  • app/(main)/academic-detail/[year]/[semester]/components/SemesterSlider.tsx (2 hunks)
  • app/(main)/academic-detail/[year]/[semester]/layout.tsx (1 hunks)
  • app/(main)/academic-detail/[year]/[semester]/page.tsx (1 hunks)
  • app/(main)/academic-detail/components/AcademicDetailContent.tsx (0 hunks)
  • app/(main)/academic-detail/layout.tsx (0 hunks)
  • app/(main)/academic-detail/page.tsx (0 hunks)
  • app/api/get-academic/[year]/[semester]/route.ts (1 hunks)
  • app/api/get-dashboard/route.ts (1 hunks)
  • app/api/get-semesters/route.ts (1 hunks)
  • app/api/graduation-progress/route.ts (1 hunks)
  • app/api/suwon-scrape/resync/route.ts (1 hunks)
  • app/api/suwon-scrape/start/route.ts (1 hunks)
  • app/api/target-gpa/route.ts (1 hunks)
  • app/api/users/delete/route.ts (1 hunks)
  • lib/auth/supabase.ts (1 hunks)
  • lib/supabase/facades/dashboard-facade.ts (1 hunks)
  • lib/supabase/server.ts (1 hunks)
  • lib/supabase/services/academic-record-service.ts (1 hunks)
  • lib/supabase/services/course-offering-service.ts (1 hunks)
  • lib/supabase/services/course-service.ts (1 hunks)
  • lib/supabase/services/department-service.ts (1 hunks)
  • lib/supabase/services/graduation-progress-service.ts (1 hunks)
  • lib/supabase/services/professor-service.ts (1 hunks)
  • lib/supabase/services/student-course-service.ts (1 hunks)
  • lib/supabase/services/student-service.ts (1 hunks)
  • package.json (1 hunks)
  • server/infrastructure/supabase/repository/SupabaseProfessorRepository.ts (1 hunks)
💤 Files with no reviewable changes (3)
  • app/(main)/academic-detail/layout.tsx
  • app/(main)/academic-detail/components/AcademicDetailContent.tsx
  • app/(main)/academic-detail/page.tsx
🧰 Additional context used
🧬 Code Definitions (17)
app/api/suwon-scrape/start/route.ts (2)
lib/supabase/server.ts (1)
  • createClient (4-23)
lib/supabase/client.ts (1)
  • createClient (4-9)
app/(main)/academic-detail/[year]/[semester]/layout.tsx (2)
app/(main)/academic-detail/[year]/[semester]/page.tsx (1)
  • fetchCache (8-8)
app/(main)/academic-detail/[year]/[semester]/components/NavBar.tsx (1)
  • NavBar (6-11)
lib/supabase/services/professor-service.ts (2)
types/supabase.ts (1)
  • Database (3-857)
lib/supabase/server.ts (1)
  • createClient (4-23)
lib/supabase/services/student-service.ts (4)
types/supabase.ts (1)
  • Database (3-857)
lib/supabase/services/department-service.ts (1)
  • DepartmentService (5-79)
lib/supabase/server.ts (1)
  • createClient (4-23)
lib/supabase/client.ts (1)
  • createClient (4-9)
lib/auth/supabase.ts (1)
lib/supabase/server.ts (1)
  • createClient (4-23)
app/api/get-dashboard/route.ts (1)
lib/supabase/facades/dashboard-facade.ts (1)
  • DashboardFacade (11-61)
app/(main)/academic-detail/[year]/[semester]/page.tsx (2)
app/(main)/academic-detail/[year]/[semester]/layout.tsx (1)
  • fetchCache (1-1)
app/(main)/academic-detail/[year]/[semester]/components/AcademicDetailContent.tsx (1)
  • AcademicDetailContent (21-41)
lib/supabase/services/graduation-progress-service.ts (2)
types/supabase.ts (1)
  • Database (3-857)
lib/supabase/server.ts (1)
  • createClient (4-23)
app/api/get-semesters/route.ts (1)
lib/supabase/server.ts (1)
  • createClient (4-23)
lib/supabase/services/course-offering-service.ts (2)
types/supabase.ts (1)
  • Database (3-857)
lib/supabase/server.ts (1)
  • createClient (4-23)
app/api/target-gpa/route.ts (1)
lib/supabase/services/student-service.ts (1)
  • StudentService (18-196)
lib/supabase/facades/dashboard-facade.ts (4)
lib/supabase/services/student-service.ts (1)
  • StudentService (18-196)
lib/supabase/services/academic-record-service.ts (1)
  • AcademicRecordService (22-164)
lib/supabase/server.ts (1)
  • createClient (4-23)
server/infrastructure/supabase/repository/SupabaseUserRepository.ts (1)
  • SupabaseUserRepository (8-78)
lib/supabase/services/academic-record-service.ts (1)
lib/supabase/server.ts (1)
  • createClient (4-23)
lib/supabase/services/course-service.ts (2)
types/supabase.ts (1)
  • Database (3-857)
lib/supabase/server.ts (1)
  • createClient (4-23)
lib/supabase/services/student-course-service.ts (2)
types/supabase.ts (1)
  • Database (3-857)
lib/supabase/server.ts (1)
  • createClient (4-23)
app/api/get-academic/[year]/[semester]/route.ts (3)
app/api/get-semesters/route.ts (1)
  • GET (4-34)
app/api/graduation-progress/route.ts (1)
  • GET (10-54)
lib/supabase/server.ts (1)
  • createClient (4-23)
lib/supabase/services/department-service.ts (3)
types/supabase.ts (1)
  • Database (3-857)
lib/supabase/server.ts (1)
  • createClient (4-23)
lib/supabase/client.ts (1)
  • createClient (4-9)
🪛 Biome (1.9.4)
app/(main)/academic-detail/[year]/[semester]/page.tsx

[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)

app/(main)/academic-detail/[year]/[semester]/components/AcademicDetailContent.tsx

[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)


[error] 38-38: 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)

🔇 Additional comments (41)
app/(main)/academic-detail/[year]/[semester]/layout.tsx (2)

1-1: Next.js의 캐싱 설정이 적절합니다.

fetchCache를 'auto'로 설정하여 Next.js가 요청에 따라 캐싱 전략을 최적화할 수 있도록 하였습니다. 이는 성능 향상에 도움이 됩니다.


3-4: 컴포넌트 임포트가 적절합니다.

필요한 NavBar 컴포넌트와 스타일을 올바르게 가져오고 있습니다.

package.json (1)

6-6:

❓ Verification inconclusive

Turbopack 플래그 제거에 대한 검토 필요

개발 스크립트에서 --turbopack 플래그가 제거되었습니다. 이는 개발 환경에서의 빌드 성능에 영향을 줄 수 있습니다. 의도적인 변경인지 확인이 필요합니다.


🌐 Web query:

Next.js 15.2.4에서 Turbopack 사용이 권장되지 않는 이유가 있나요?

💡 Result:

Next.js 15.2.4에서 Turbopack 사용 시 주의가 필요한 이유는 다음과 같습니다:

주요 이슈 및 제한사항

1. 프로덕션 빌드 미지원

  • Turbopack은 여전히 next dev(개발 서버) 환경에서만 작동하며, next build를 통한 프로덕션 빌드는 지원되지 않습니다[2][7]. 이로 인해 개발과 배포 환경 간 차이가 발생할 수 있습니다.

2. 특정 환경에서의 안정성 문제

  • Windows + Firefox 조합에서 Sourcemap 경고가 발생하며, URL 파싱 오류로 인한 콘솔 에러가 확인되었습니다[9]. 이 문제는 Turbopack이 생성한 소스맵 경로가 Windows 파일 시스템과 호환되지 않아 발생합니다.

3. 일부 기능 미구현

  • Babel 변환 지원이 제한적이며 .babelrc 파일을 직접 사용할 수 없습니다[2]
  • App Router의 root layout 자동 생성 기능이 작동하지 않아 수동 설정이 필요합니다[1][2]
  • @next/font(구버전) 및 AMP 지원이 중단되었습니다[2]

4. 성능 이슈

  • 대형 프로젝트에서 메모리 사용량이 증가하는 사례가 보고되었으며[5][7], 디스크 캐싱 미지원으로 인한 초기 컴파일 속도 저하가 발생할 수 있습니다[6][7].

권장 사용 가이드

# Turbopack 활성화 시
next dev --turbo
  • 사용이 권장되는 경우: 대규모 애플리케이션의 빠른 개발 반복이 필요한 경우[4][6]
  • 지양해야 하는 경우: Windows 개발 환경, 프로덕션 빌드 필요 시, 특정 최적화 기능이 필요한 경우[2][9]

Next.js 팀은 Turbopack의 지속적인 개선을 위해 .next/trace-turbopack 파일 생성 기능을 제공하며[1][2], 성능 이슈 발생 시 해당 파일을 GitHub 이슈에 첨부할 것을 권장합니다[1][5]. 프로덕션 환경 사용을 위해서는 공식 릴리즈 노트에서 지속적인 업데이트 확인이 필요합니다[7][8].

Citations:


검토: Turbopack 플래그 제거 의도 재확인 요청

패키지의 "dev": "next dev", 스크립트에서 이전에 사용되던 --turbopack 플래그가 제거된 것을 확인했습니다.
Next.js 15.2.4에서는 Turbopack이 프로덕션 빌드를 지원하지 않으며, Windows+Firefox 환경의 안정성 문제, 일부 기능 미구현 및 성능 이슈 등이 보고되고 있습니다.
따라서 이번 변경이 이러한 제약 사항을 반영한 의도적인 결정이라면 문제가 없으나, 다음 사항을 재확인해주시기 바랍니다:

  • 프로젝트 요구사항에 따라 Turbopack을 사용해야 하는지 여부
  • Turbopack 사용이 필요한 경우, 권장 플래그인 --turbo 적용 여부
app/(main)/academic-detail/[year]/[semester]/components/NavBar.tsx (3)

1-1: 클라이언트 컴포넌트 정의가 적절합니다.

라우팅 기능을 사용하기 위해 'use client' 지시어를 올바르게 선언했습니다.


3-4: 필요한 컴포넌트와 훅 임포트가 적절합니다.

UI 컴포넌트와 내부 라우터 훅을 올바르게 가져오고 있습니다.


6-11: NavBar 컴포넌트 구현이 깔끔합니다.

useInternalRouter를 올바르게 사용하고 있으며, TopNavigation.Preset 컴포넌트에 적절한 속성을 제공하고 있습니다.

app/api/users/delete/route.ts (1)

11-11: 비동기 Supabase 클라이언트 생성으로 개선됐습니다.

Supabase 클라이언트 생성 코드를 비동기 방식으로 수정하여 클라이언트가 완전히 초기화된 후에 사용되도록 변경했습니다. 이는 안정성을 높이는 좋은 개선사항입니다.

app/api/suwon-scrape/resync/route.ts (1)

38-38: 비동기 처리 방식으로 적절히 변경되었습니다.

createClient()가 비동기 함수로 변경되었으므로 await를 사용하여 Promise가 resolve될 때까지 기다리도록 수정한 것이 적절합니다. 이 변경으로 Supabase 클라이언트가 완전히 초기화된 후에 다른 리포지토리 인스턴스를 생성하고 포털 데이터를 가져오는 작업이 진행됩니다.

app/api/get-semesters/route.ts (1)

6-6: 비동기 처리 방식으로 적절히 변경되었습니다.

createClient()가 이제 비동기 함수이므로 await를 추가하여 Promise가 resolve될 때까지 기다리도록 수정한 것이 적절합니다. 이 변경으로 Supabase 클라이언트가 완전히 초기화된 후에 인증 및 데이터 조회 작업이 진행됩니다.

app/api/graduation-progress/route.ts (1)

12-12: 비동기 처리 방식으로 적절히 변경되었습니다.

createClient()가 비동기 함수로 변경되었으므로 await를 사용하여 Promise가 resolve될 때까지 기다리도록 수정한 것이 올바른 접근 방식입니다. 이 변경으로 Supabase 클라이언트가 완전히 초기화된 후에 서비스 인스턴스 생성 및 후속 작업이 진행됩니다.

lib/auth/supabase.ts (1)

7-7: 비동기 처리 방식으로 적절히 변경되었습니다.

createClient()가 비동기 함수로 변경되었으므로 await를 사용하여 Promise가 resolve될 때까지 기다리도록 수정한 것이 적절합니다. 이 변경으로 Supabase 클라이언트가 완전히 초기화된 후에 nonce를 검색하고 로그인 작업을 수행하게 됩니다.

app/api/suwon-scrape/start/route.ts (1)

39-39: Supabase 클라이언트 생성 방식 변경이 적절히 적용되었습니다.

createClient 함수가 비동기 함수로 변경되었으므로, await를 사용하여 올바르게 호출하는 방식으로 업데이트되었습니다. 이는 lib/supabase/server.ts에서의 변경사항과 일치합니다.

app/(main)/academic-detail/[year]/[semester]/apis/getSemesters.ts (1)

1-7: 인터페이스 정의와 캐시 설정이 적절합니다.

React의 cache 함수를 사용하여 데이터 중복 요청을 방지하는 방식은 적절합니다. Semester 인터페이스도 명확하게 정의되어 있습니다.

lib/supabase/server.ts (1)

1-5: 비동기 함수로의 리팩토링이 적절히 이루어졌습니다.

createClient 함수를 비동기 함수로 변경하고 cookies() 함수를 올바르게 await 처리한 것은 좋은 개선입니다. 이로 인해 쿠키 저장소가 제대로 초기화된 후에 Supabase 클라이언트를 생성할 수 있게 되었습니다.

app/(main)/academic-detail/[year]/[semester]/page.tsx (2)

10-16: 병렬 데이터 페칭 구현이 효율적입니다.

Promise.all을 사용하여 여러 데이터 요청을 병렬로 처리하는 것은 성능 측면에서 효율적입니다. 또한 params에서 yearsemester를 올바르게 추출하고 있습니다.


8-8:

❓ Verification inconclusive

캐싱 전략 확인 필요

fetchCache 값이 'force-cache'로 설정되어 있습니다. 이는 데이터가 항상 캐시에서 제공되도록 하며, 새로운 데이터를 가져오지 않을 수 있습니다. 동적 데이터를 다루는 경우, 적절한 재검증 전략을 고려해야 합니다.


🌐 Web query:

Next.js 14에서 fetchCache 옵션과 force-cache의 동작 방식

💡 Result:

Next.js 14에서 fetchCache 옵션과 force-cache 설정은 데이터 캐싱 동작을 세밀하게 제어하기 위한 핵심 메커니즘입니다. 이들의 상호작용과 기본 동작 방식을 다음과 같이 정리할 수 있습니다:

1. 기본 캐싱 동작

  • Next.js 14에서는 fetch 요청 시 cache: "force-cache"기본값으로 설정되어 있습니다[1][4]. 이는 명시적으로 cache: "no-store"를 지정하지 않는 한 모든 fetch 요청 결과가 자동으로 캐싱됨을 의미합니다.
  • 캐시된 데이터는 재검증(revalidation) 주기 없이 계속 사용됩니다[1][8].

2. force-cache의 세부 동작

const res = await fetch('https://api.example.com/data', { cache: 'force-cache' });
  • 캐시 적중 시: 저장된 데이터를 즉시 반환하며 백엔드 요청을 수행하지 않습니다[3][7].
  • 캐시 미스 시: 백엔드에서 데이터를 가져온 후 캐시에 저장합니다[7].
  • 재검증: next.revalidate 옵션으로 시간 기준 재검증 주기를 설정할 수 있습니다(예: { next: { revalidate: 3600 } })[10].

3. fetchCache 옵션의 영향력

페이지/레이아웃에서 export const fetchCache를 설정하면 개별 fetch 호출의 캐시 옵션을 전역적으로 제어할 수 있습니다[2][5]:

설정 값 효과
force-no-store 모든 fetch 요청을 no-store로 강제 적용(개별 설정 무시)[2][5]
force-cache 모든 fetch 요청을 force-cache로 강제 적용(개별 설정 무시)[2][5]
default-no-store 기본값을 no-store로 설정하되, 개별 force-cache 설정은 허용[2][5]

4. 동적 렌더링과의 상호작용

  • 동적 경로(dynamic = 'force-dynamic') 사용 시 풀 라우트 캐시가 무효화되지만, force-cache가 설정된 fetch 요청은 데이터 캐시를 계속 활용합니다[4][8].
  • revalidatePath/revalidateTag를 사용하면 특정 태그와 연관된 캐시를 수동으로 갱신할 수 있습니다[4][10].

5. 디버깅 및 모니터링

// next.config.mjs
const nextConfig = { logging: { fetches: { fullUrl: true } } };
  • 이 설정을 통해 콘솔에서 cache: skip(캐시 미사용) 또는 cache: hit(캐시 적중) 상태를 확인할 수 있습니다[1][7].

6. 버전별 차이점

  • Next.js 15 RC부터는 fetch 기본값이 no-store로 변경되었으나, Next.js 14에서는 여전히 force-cache가 기본 동작입니다[4][8].

이 메커니즘들을 조합하면 정적 생산성과 동적 최신성 사이의 균형을 유연하게 조절할 수 있습니다. 예를 들어 자주 변경되지 않는 CMS 데이터는 force-cache로 캐십하고, 실시간 주가 정보는 no-store로 처리하는 식의 전략이 가능합니다[3][8].

Citations:


캐싱 옵션 검토 요청

  • 현재 페이지(app/(main)/academic-detail/[year]/[semester]/page.tsx)에서 전역 캐싱 옵션이 force-cache로 설정되어 있습니다.
  • Next.js 14에서 force-cache는 기본 캐싱 정책으로, 별도의 재검증(revalidation) 옵션이 없으면 캐시된 데이터가 계속 재사용됩니다.
  • 만약 이 페이지의 데이터가 동적으로 변경되어 최신 데이터가 필요하다면, no-store 옵션이나 next.revalidate를 활용해 재검증 전략을 추가하는 방향을 고려해보시기 바랍니다.
app/api/target-gpa/route.ts (1)

14-14: 비동기 팩토리 메서드를 사용한 서비스 초기화 개선

StudentService 인스턴스 생성 방식이 동기식 생성자 호출에서 비동기 팩토리 메서드로 변경되었습니다. 이는 Supabase 클라이언트의 비동기 초기화를 올바르게 처리하여 코드의 안정성을 향상시킵니다.

app/api/get-academic/[year]/[semester]/route.ts (1)

84-84: GET 함수 시그니처 개선

함수 시그니처가 보다 간결한 형태로 재구성되었습니다. 이는 코드 가독성을 향상시키며 기능적 변화 없이 코드 스타일을 개선합니다.

app/api/get-dashboard/route.ts (1)

8-8: 팩토리 메서드를 통한 의존성 주입 개선

여러 서비스를 수동으로 초기화하는 대신 DashboardFacade.create() 팩토리 메서드를 사용하여 코드가 간결해졌습니다. 이 방식은 의존성 주입을 더 명확하게 하고 초기화 로직을 캡슐화하여 코드 유지보수성을 향상시킵니다.

lib/supabase/services/graduation-progress-service.ts (2)

22-22: 생성자 매개변수 요구사항 개선

생성자에서 supabase 매개변수의 기본값이 제거되어 명시적으로 SupabaseClient 인스턴스를 전달해야 합니다. 이는 의존성을 더 명확하게 하고 잠재적인 초기화 문제를 방지합니다.


24-27: 정적 팩토리 메서드 추가

비동기 create() 메서드가 추가되어 Supabase 클라이언트를 올바르게 초기화하고 서비스 인스턴스를 생성합니다. 이 패턴은 다른 서비스 클래스와 일관성을 유지하며, 비동기 초기화를 보다 안전하게 처리합니다.

lib/supabase/services/professor-service.ts (2)

6-11: 정적 팩토리 메서드 패턴 적용 - 좋은 변경

Supabase 클라이언트를 생성하고 서비스 인스턴스를 반환하는 정적 create 메서드 추가는 좋은 패턴입니다. 의존성 주입을 명시적으로 하여 코드의 가독성과 테스트 가능성을 향상시켰습니다.


21-57: 기존 메서드 로직에 대한 검토

getOrCreateProfessorByName 메서드의 로직은 적절하게 구현되어 있습니다. 교수를 이름으로 조회하고, 없으면 새로 생성하는 로직이 명확합니다. 에러 처리도 잘 되어 있습니다.

lib/supabase/services/student-service.ts (3)

21-26: 생성자 의존성 주입 패턴 적용 - 좋은 변경

Supabase 클라이언트를 명시적으로 주입받도록 생성자를 변경한 것은 좋은 패턴입니다. 코드의 명확성과 테스트 가능성이 향상되었습니다.


28-31: 정적 팩토리 메서드 패턴 적용 - 좋은 변경

비동기적으로 Supabase 클라이언트를 생성하고 서비스 인스턴스를 반환하는 정적 create 메서드 추가는 좋은 패턴입니다. 초기화 로직을 한 곳에 집중시키고 사용하기 쉬운 API를 제공합니다.


33-195: 기존 메서드 로직에 대한 검토

StudentService 클래스의 메서드 로직은 전반적으로 잘 구현되어 있습니다. 인증된 사용자 ID를 가져오는 메서드, 학생 정보 초기화, 포털 연동 해제, 연동 상태 확인, 목표 학점 업데이트, 학생 정보 조회 등의 기능이 명확하게 구현되어 있습니다. 각 메서드의 에러 처리도 적절합니다.

lib/supabase/services/course-offering-service.ts (1)

28-33: 비동기 초기화 패턴으로의 전환이 잘 되었습니다.

기존 동기식 생성자에서 명시적인 의존성 주입 방식과 비동기 static factory 메서드로 변경한 것은 좋은 접근법입니다. 이렇게 하면 Supabase 클라이언트의 생성과 관리가 더 명확해지고, 서버 컴포넌트에서 활용하기에 더 적합합니다.

특히 createClient()가 비동기 함수이므로, 생성자에서 직접 호출하는 대신 정적 팩토리 메서드를 통해 비동기적으로 처리하는 것이 올바른 방식입니다.

lib/supabase/services/academic-record-service.ts (1)

23-28: 비동기 생성 패턴 구현이 일관성 있게 적용되었습니다.

생성자에서 의존성을 명시적으로 받도록 변경하고, 정적 팩토리 메서드를 통해 비동기 초기화를 처리하는 방식이 CourseOfferingService와 일관되게 적용되었습니다. 이는 코드의 일관성과 예측 가능성을 높여줍니다.

이 패턴은 서버 컴포넌트 환경에서 Supabase 클라이언트를 안전하게 생성하고 사용할 수 있게 해줍니다.

app/(main)/academic-detail/[year]/[semester]/components/SemesterSlider.tsx (3)

12-16: Props 인터페이스 확장이 적절하게 이루어졌습니다.

SemesterSliderProps 인터페이스에 semesters 배열을 추가한 것은 컴포넌트의 책임을 데이터 소스로부터 분리하는 좋은 접근법입니다.


21-24: 라우팅 로직이 개선되었습니다.

파라미터 전달 방식을 객체에서 배열로 변경한 것은 Next.js의 동적 라우팅 패턴과 더 잘 맞습니다. 이는 URL 구조가 /academic-detail/[year]/[semester] 형식으로 정의되어 있기 때문에 적절한 변경입니다.


41-41: 상태 관리 로직이 제거되었습니다.

기존 상태 관리 및 데이터 페칭 로직이 제거되고, 부모 컴포넌트로부터 semesters 데이터를 props로 받도록 변경된 것은 컴포넌트의 책임을 명확히 분리하는 좋은 리팩토링입니다. 이제 이 컴포넌트는 데이터 표시에만 집중할 수 있습니다.

lib/supabase/services/course-service.ts (1)

13-18: 비동기 생성 패턴이 일관되게 적용되었습니다.

다른 서비스 클래스들과 마찬가지로, CourseService도 명시적 의존성 주입과 비동기 정적 팩토리 메서드 패턴을 도입했습니다. 이러한 일관된 패턴 적용은 코드베이스의 유지보수성을 높여줍니다.

특히 createClient()가 비동기 함수이므로, 이를 사용하여 서비스 객체를 생성하는 정적 메서드를 제공하는 것은 적절한 접근법입니다.

lib/supabase/services/department-service.ts (2)

6-6: 생성자가 명시적으로 SupabaseClient를 요구하도록 변경되었습니다.

이전에는 생성자 내부에서 createClient()를 통해 기본 Supabase 클라이언트를 생성했으나, 이제는 외부에서 주입받도록 변경되었습니다. 이는 의존성 주입 패턴을 따르는 좋은 변화입니다.


8-11: 정적 팩토리 메서드 추가는 좋은 패턴입니다.

새로 추가된 create() 정적 메서드는 비동기적으로 Supabase 클라이언트를 생성하고 이를 사용하여 DepartmentService 인스턴스를 반환합니다. 이는 비동기 초기화를 깔끔하게 처리하며, 다음과 같은 장점이 있습니다:

  1. 클라이언트 생성 로직을 캡슐화
  2. 일관된 인스턴스 생성 패턴 제공
  3. 비동기 초기화 프로세스를 명확하게 표현
server/infrastructure/supabase/repository/SupabaseProfessorRepository.ts (2)

10-10: 생성자 매개변수가 명시적으로 변경되었습니다.

생성자가 이제 명시적으로 supabase 클라이언트를 필요로 하며, private readonly로 선언되어 있습니다. 이는 의존성 주입을 명확히 하고 클래스의 불변성을 향상시키는 좋은 변화입니다.


12-15: 비동기 팩토리 메서드 추가는 적절합니다.

새로 추가된 create() 정적 메서드는 createClient()를 비동기적으로 호출하여 Supabase 클라이언트를 얻고, 이를 사용하여 새 SupabaseProfessorRepository 인스턴스를 반환합니다. 이 패턴은 다음과 같은 이점이 있습니다:

  1. 클라이언트 생성의 비동기 특성을 적절히 처리
  2. 사용하기 쉬운 API 제공
  3. 다른 서비스 클래스와의 일관성 유지
lib/supabase/facades/dashboard-facade.ts (3)

7-9: 필요한 의존성이 적절하게 추가되었습니다.

새로운 의존성(SupabaseAuthService, SupabaseUserRepository, createClient)을 import하여 static create 메서드에서 사용할 수 있도록 했습니다. 이는 DashboardFacade의 기능을 확장하면서도 코드의 구조를 개선하는 좋은 변화입니다.


15-16: 생성자 매개변수가 명시적 의존성 주입을 위해 개선되었습니다.

이전에는 StudentService와 AcademicRecordService를 생성자 내부에서 직접 초기화했지만, 이제는 외부에서 주입받도록 변경되었습니다. 이는 다음과 같은 이점이 있습니다:

  1. 단위 테스트 용이성 향상
  2. 의존성 관리의 유연성 증가
  3. 관심사의 분리(SoC) 강화

19-26: 정적 팩토리 메서드 추가는 효과적인 의존성 관리를 제공합니다.

새로 추가된 create() 정적 메서드는 DashboardFacade의 모든 의존성을 생성하고 이를 사용하여 새 인스턴스를 반환합니다. 이 접근 방식은:

  1. 복잡한 의존성 트리를 캡슐화하여 사용자 코드를 단순화
  2. 비동기 초기화를 깔끔하게 처리
  3. 모든 의존성이 적절히 초기화되도록 보장

이 방식으로 API 라우트나 컴포넌트에서 DashboardFacade를 사용할 때 의존성 생성에 대한 걱정 없이 단순히 await DashboardFacade.create()를 호출하면 됩니다.

lib/supabase/services/student-course-service.ts (2)

14-14: 의존성 주입을 위한 생성자 개선이 이루어졌습니다.

이전에는 생성자에서 기본값으로 createClient()를 사용했지만, 이제는 외부에서 SupabaseClient를 명시적으로 주입받도록 변경되었습니다. 이는 코드의 테스트 용이성을 향상시키고 의존성 관리를 명확히 하는 좋은 변화입니다.


16-19: 비동기 팩토리 메서드 추가는 일관된 패턴을 유지합니다.

새로 추가된 create() 정적 메서드는 다른 서비스 클래스들과 일관된 패턴을 유지하며, Supabase 클라이언트의 비동기 생성을 적절히 처리합니다. 이 접근 방식은:

  1. 서비스 인스턴스화 과정의 일관성 유지
  2. 비동기 클라이언트 생성의 복잡성 추상화
  3. 사용자 코드를 더 단순하고 명확하게 만듦

다른 서비스 클래스들과 함께 이러한 변경은 전체 애플리케이션의 아키텍처를 개선합니다.

Comment on lines +6 to +14
export default function GraduationProgressLayout({ children }: { children: React.ReactNode }) {

return (
<div className={styles.container}>
<NavBar />
<div className={styles.content}>{children}</div>
</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)

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

컴포넌트 이름은 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>
);
}

Comment on lines +14 to +21
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 }
});
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 }
});

Comment on lines +23 to +25
if (!res.ok) {throw new Error('Failed to fetch');}
return res.json();
});
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();
});

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

Comment on lines +18 to +25
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>
);
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)

Comment on lines +27 to +32
<AcademicSummaryCard
earnedCredits={data.semesterGrades[0].earnedCredits}
gpa={data.semesterGrades[0].semesterGpa}
classRank={data.semesterGrades[0].classRank}
totalStudents={data.semesterGrades[0].totalStudents}
/>
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>
)}

{/* 전공 과목 목록 */}
<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" />

classRank={data.semesterGrades[0].classRank}
totalStudents={data.semesterGrades[0].totalStudents}
/>
<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)


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

}

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) {

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.

1 participant