Skip to content

Feat/#120 홈페이지(Home) SSG 카테고리 & CSR 랭킹 하이브리드 렌더링 구현#121

Merged
leeleeleeleejun merged 6 commits intodevelopfrom
feat/#120
Mar 13, 2026
Merged

Feat/#120 홈페이지(Home) SSG 카테고리 & CSR 랭킹 하이브리드 렌더링 구현#121
leeleeleeleejun merged 6 commits intodevelopfrom
feat/#120

Conversation

@leeleeleeleejun
Copy link
Copy Markdown
Member

@leeleeleeleejun leeleeleeleejun commented Mar 13, 2026

#️⃣연관된 이슈

📝작업 내용

홈 페이지를 SSG로 최적화하면서 사용자별 실시간 데이터(랭킹)는 CSR로 처리하는 하이브리드 렌더링 전략을 구현했습니다

1. 홈 페이지 SSG 설정

  • export const dynamic = 'force-static' 추가하여 정적 생성 강제

2. Categories 섹션 SSG 구현

  • React Query 기반 클라이언트 컴포넌트 → async/await 기반 서버 컴포넌트로 전환
  • 빌드 타임에 카테고리 데이터를 HTML에 포함하여 초기 로딩 속도 개선
  • 불필요한 HydrationBoundaryPage 제거

3. Ranking 섹션 CSR 구현

  • RankingSection: 서버 컴포넌트로 UI 구조 담당
  • RankingListFetcher: 클라이언트 컴포넌트로 데이터 fetching 로직 분리
  • RankingListFetcherClient: next/dynamic의 ssr: false 옵션으로 클라이언트 전용 렌더링
  • 동적 import로 SSG 빌드 시 랭킹 섹션 생성 방지, 클라이언트에서만 실시간 데이터 조회

스크린샷 (선택)

빌드 결과물(index.html)에 카테고리 데이터가 포함된 모습 (랭킹 데이터는 미포함)
image

실제 동작

Frame 46

💬리뷰 요구사항(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

Summary by CodeRabbit

릴리스 노트

  • New Features

    • 랭킹 섹션에 새로운 데이터 로딩 컴포넌트 추가
  • Refactor

    • 카테고리 데이터 페칭을 서버 사이드로 변경하여 성능 최적화
    • 랭킹 리스트 컴포넌트의 렌더링 구조 개선
    • API 기본 URL 처리 로직 강화
  • Chores

    • 빌드 및 환경 설정 업데이트

- rewrite 때문에 빌드 시 에러 발생
- NEXT_PHASE 환경 변수를 globalEnv에 추가
- 불필요한 HydrationBoundaryPage 제거 및 구조 단순화
- RankingSection을 서버 컴포넌트로 전환하여 UI 구조 담당
- RankingListFetcher 생성: 클라이언트에서 실시간 랭킹 데이터 조회
- RankingListFetcherClient 생성: next/dynamic으로 CSR 보장 (ssr: false)
- 홈 페이지 SSG를 유지하면서 랭킹 데이터는 클라이언트에서만 렌더링
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 13, 2026

Walkthrough

이 PR은 RankingListFetcher 및 RankingListFetcherClient 컴포넌트를 도입하고, Categories를 서버 컴포넌트로 전환하며, 홈 페이지를 강제 정적 렌더링으로 업데이트합니다. 또한 axiosInstance가 빌드 타임과 런타임 URL 선택을 지원하도록 수정되었습니다.

Changes

Cohort / File(s) Summary
Ranking Section 컴포넌트
apps/web/app/(home)/_components/RankingSection/RankingListFetcher.tsx, apps/web/app/(home)/_components/RankingSection/RankingListFetcherClient.tsx, apps/web/app/(home)/_components/RankingSection/RankingSection.tsx
새로운 클라이언트 측 RankingListFetcher 컴포넌트 추가 및 동적 래퍼(ssr: false) 생성. RankingSection에서 서버 측 데이터 페칭 로직을 제거하고 클라이언트 컴포넌트로 대체.
페이지 수준 렌더링 전략
apps/web/app/(home)/page.tsx, apps/web/app/profile/page.tsx
홈 페이지에 force-static 동적 설정 추가 및 HydrationBoundaryPage 제거. 프로필 페이지에 force-dynamic 설정 추가.
Categories 컴포넌트 서버 전환
apps/web/app/_components/Categories/Categories.tsx
클라이언트 측 React Query 기반 데이터 페칭을 서버 측 직접 데이터 페칭(getCategories)으로 변환. useSuspenseQuery 제거 및 async 함수로 변경.
Axios 및 인프라 설정
apps/web/app/_lib/axiosInstance.ts, turbo.json
환경 인식 기반 URL 선택을 위한 getBaseURL() 함수 추가 및 빌드 타임/런타임 구분 로직 구현. 요청 인터셉터에서 빌드 타임에 토큰 검색 우회. Turbo 설정에서 NEXT_PHASE 환경 변수 추가.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Suggested labels

✨ Feature

Poem

🐰 서버에서 클라이언트로, 정적으로 동적으로,
데이터는 경계를 넘어 춤을 춘다네.
Categories는 빌드타임에, Ranking은 브라우저에서,
마치집 강아지처럼 올바른 자리를 찾아 앉는다! 🌟

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 변경 사항의 핵심을 명확하게 반영하고 있습니다. 홈페이지에 SSG 카테고리와 CSR 랭킹의 하이브리드 렌더링을 구현한 것으로, 제목이 정확하게 이 내용을 설명하고 있습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#120
📝 Coding Plan
  • Generate coding plan for human review comments

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

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

Tip

You can make CodeRabbit's review stricter and more nitpicky using the `assertive` profile, if that's what you prefer.

Change the reviews.profile setting to assertive to make CodeRabbit's nitpick more issues in your PRs.

@leeleeleeleejun leeleeleeleejun marked this pull request as ready for review March 13, 2026 08:35
@leeleeleeleejun leeleeleeleejun changed the title Feat/#120 Feat/#120 홈페이지(Home) SSG 카테고리 & CSR 랭킹 하이브리드 렌더링 구현 Mar 13, 2026
Copy link
Copy Markdown
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: 5

🧹 Nitpick comments (1)
apps/web/app/(home)/_components/RankingSection/RankingListFetcher.tsx (1)

3-8: import 순서가 저장소 규칙과 다릅니다.

Line 3-8에서 외부 패키지(@tanstack/react-query)가 absolute import 사이에 섞여 있습니다.

🔧 제안 패치
 import type { RankingPlaceSort } from '@/_apis/schemas/place'
 import { useCampusStore } from '@/_store/campus'
 import { useSuspenseQuery } from '@tanstack/react-query'
 import { usePlaceQueries } from '@/_apis/queries/place'
 import { EmptyFallback } from '@/_components/EmptyFallback'
 import { PlaceListItem } from '@/_components/PlaceListItem'
+import { useSuspenseQuery } from '@tanstack/react-query'

As per coding guidelines "Organize imports in the following order: Node/built-in, external packages, absolute imports, relative imports".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/`(home)/_components/RankingSection/RankingListFetcher.tsx around
lines 3 - 8, The imports in RankingListFetcher.tsx are out of the project's
ordering rules: the external package import useSuspenseQuery from
'@tanstack/react-query' is currently between absolute imports. Move the external
import so that all external/package imports (e.g., useSuspenseQuery) appear
after built-ins (if any) and before absolute imports (any paths starting with
'@/...'), keeping the other symbols (RankingPlaceSort, useCampusStore,
usePlaceQueries, EmptyFallback, PlaceListItem) in the absolute imports group;
then save/format to preserve grouping.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/app/_components/Categories/Categories.tsx`:
- Around line 5-7: The Categories server component currently awaits
getCategories() and lets exceptions bubble to SSG; wrap the getCategories() call
in a try/catch inside the async Categories function, call console.error with the
caught error (including context like "Categories fetch failed"), and fall back
to a safe minimal value (e.g., const categories: Category[] = [] or a single
"All" category) so the component can render during build-time failures; also
propagate this error handling into any getCategories onError callback if one
exists so it logs and returns the same fallback instead of throwing.

In `@apps/web/app/_lib/axiosInstance.ts`:
- Around line 11-30: The getBaseURL function lacks an explicit return type and
the axios request interceptor reads cookies on the server causing dynamic cookie
API calls; update getBaseURL to declare a return type string (change to const
getBaseURL = (): string => {...}) and modify the axiosInstance request
interceptor (the code that calls getCookie('accessToken')) to only read cookies
in the browser (guard with typeof window !== 'undefined' or similar) so
server-side/static builds never invoke cookies()/getCookie; ensure axiosInstance
still sets baseURL via getBaseURL() after the type change.
- Around line 12-27: The getBaseURL function lacks an explicit return type; add
a return type annotation (e.g., getBaseURL(): string | undefined) to satisfy
strict TypeScript and noUncheckedIndexedAccess, and ensure all return paths (the
build-time branch returning process.env.NEXT_PUBLIC_API_URL_BUILD ||
process.env.NEXT_PUBLIC_API_URL and the default returning
process.env.NEXT_PUBLIC_API_URL) conform to that type; update the function
signature in apps/web/app/_lib/axiosInstance.ts to include the explicit return
type.

In `@apps/web/app/`(home)/_components/RankingSection/RankingListFetcher.tsx:
- Around line 10-14: The component RankingListFetcher is missing an explicit
return type; update its function signature to declare a React return type (e.g.
change "export const RankingListFetcher = ({ rankingPlaceSort }: {
rankingPlaceSort: RankingPlaceSort }) => {" to include ": React.ReactElement" or
": React.ReactElement | null" as appropriate) so the component has a concrete
TypeScript return type; ensure React is imported for the type if needed (import
type { ReactElement } from 'react' and use ": ReactElement" or use
"JSX.Element").

In `@turbo.json`:
- Line 4: The globalEnv array in turbo.json is missing API URL related keys
(NEXT_PUBLIC_API_URL, NEXT_PUBLIC_API_URL_BUILD, NEXT_PUBLIC_API_URL_HOST,
NEXT_PUBLIC_API_URL_BASE), so add those four entries to the "globalEnv" list to
ensure changes to API endpoints invalidate Turbo cache; update the "globalEnv"
value (the symbol to change is globalEnv in turbo.json) to include these keys
alongside NODE_ENV, NEXT_RUNTIME, CI, NEXT_PHASE.

---

Nitpick comments:
In `@apps/web/app/`(home)/_components/RankingSection/RankingListFetcher.tsx:
- Around line 3-8: The imports in RankingListFetcher.tsx are out of the
project's ordering rules: the external package import useSuspenseQuery from
'@tanstack/react-query' is currently between absolute imports. Move the external
import so that all external/package imports (e.g., useSuspenseQuery) appear
after built-ins (if any) and before absolute imports (any paths starting with
'@/...'), keeping the other symbols (RankingPlaceSort, useCampusStore,
usePlaceQueries, EmptyFallback, PlaceListItem) in the absolute imports group;
then save/format to preserve grouping.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c567f069-d291-49cf-bcb9-8939303af312

📥 Commits

Reviewing files that changed from the base of the PR and between c2f901d and 72cb65d.

📒 Files selected for processing (7)
  • apps/web/app/(home)/_components/RankingSection/RankingListFetcher.tsx
  • apps/web/app/(home)/_components/RankingSection/RankingListFetcherClient.tsx
  • apps/web/app/(home)/_components/RankingSection/RankingSection.tsx
  • apps/web/app/(home)/page.tsx
  • apps/web/app/_components/Categories/Categories.tsx
  • apps/web/app/_lib/axiosInstance.ts
  • turbo.json

Comment on lines +5 to 7
export const Categories = async () => {
const categories = await getCategories()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

카테고리 API 실패가 SSG 전체 실패로 전파됩니다.

Line 6에서 예외를 그대로 전파하면 빌드 타이밍의 일시적 API 장애가 홈 페이지 정적 생성 실패로 이어질 수 있습니다. 최소한의 fallback과 로깅을 넣어 주세요.

🔧 제안 패치
-export const Categories = async () => {
-  const categories = await getCategories()
+export const Categories = async (): Promise<JSX.Element> => {
+  let categories = []
+  try {
+    categories = await getCategories()
+  } catch (error) {
+    console.error('Failed to fetch categories in Categories', error)
+  }
 
   return (

As per coding guidelines "Handle errors with console.error and implement error handling logic in onError callbacks".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/_components/Categories/Categories.tsx` around lines 5 - 7, The
Categories server component currently awaits getCategories() and lets exceptions
bubble to SSG; wrap the getCategories() call in a try/catch inside the async
Categories function, call console.error with the caught error (including context
like "Categories fetch failed"), and fall back to a safe minimal value (e.g.,
const categories: Category[] = [] or a single "All" category) so the component
can render during build-time failures; also propagate this error handling into
any getCategories onError callback if one exists so it logs and returns the same
fallback instead of throwing.

Comment on lines +10 to +14
export const RankingListFetcher = ({
rankingPlaceSort,
}: {
rankingPlaceSort: RankingPlaceSort
}) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

fd -t f "RankingListFetcher.tsx" --exec cat -n {}

Repository: knu-matzip/Matzip-Client

Length of output: 1318


컴포넌트의 명시적 반환 타입을 추가하세요.

RankingListFetcher는 TypeScript 엄격 모드에서 필수적으로 반환 타입을 선언해야 합니다. 현재 함수 시그니처에 반환 타입이 누락되어 있습니다.

🔧 제안 패치
 export const RankingListFetcher = ({
   rankingPlaceSort,
 }: {
   rankingPlaceSort: RankingPlaceSort
-}) => {
+}): JSX.Element => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/`(home)/_components/RankingSection/RankingListFetcher.tsx around
lines 10 - 14, The component RankingListFetcher is missing an explicit return
type; update its function signature to declare a React return type (e.g. change
"export const RankingListFetcher = ({ rankingPlaceSort }: { rankingPlaceSort:
RankingPlaceSort }) => {" to include ": React.ReactElement" or ":
React.ReactElement | null" as appropriate) so the component has a concrete
TypeScript return type; ensure React is imported for the type if needed (import
type { ReactElement } from 'react' and use ": ReactElement" or use
"JSX.Element").

"$schema": "https://turborepo.com/schema.json",
"ui": "tui",
"globalEnv": ["NODE_ENV","NEXT_PUBLIC_API_URL", "NEXT_RUNTIME", "CI"],
"globalEnv": ["NODE_ENV", "NEXT_RUNTIME", "CI", "NEXT_PHASE"],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python - <<'PY'
import json, pathlib, re

turbo = json.loads(pathlib.Path("turbo.json").read_text())
global_env = set(turbo.get("globalEnv", []))

targets = [
    "apps/web/app/_lib/axiosInstance.ts",
    "apps/web/middleware.ts",
    "apps/web/next.config.ts",
]

used = set()
for f in targets:
    p = pathlib.Path(f)
    if not p.exists():
        continue
    used |= set(re.findall(r'process\.env\.([A-Z0-9_]+)', p.read_text()))

focus = sorted([k for k in used if k.startswith("NEXT_PUBLIC_API_URL") or k == "NEXT_PHASE"])
missing = sorted(set(focus) - global_env)

print("globalEnv:", sorted(global_env))
print("used env keys:", focus)
print("missing from globalEnv:", missing)
PY

Repository: knu-matzip/Matzip-Client

Length of output: 398


globalEnv에서 API URL 계열 키가 빠져 Turbo 캐시가 오염될 수 있습니다.

Line 4의 globalEnv 설정에서 NEXT_PUBLIC_API_URL, NEXT_PUBLIC_API_URL_BUILD, NEXT_PUBLIC_API_URL_HOST, NEXT_PUBLIC_API_URL_BASE가 누락되어 있습니다. 이 환경 변수들이 변경되어도 빌드 캐시가 무효화되지 않아, 오래된 API 엔드포인트 설정이 재사용될 위험이 있습니다.

🔧 제안 패치
-  "globalEnv": ["NODE_ENV", "NEXT_RUNTIME", "CI", "NEXT_PHASE"],
+  "globalEnv": [
+    "NODE_ENV",
+    "NEXT_RUNTIME",
+    "CI",
+    "NEXT_PHASE",
+    "NEXT_PUBLIC_API_URL",
+    "NEXT_PUBLIC_API_URL_BUILD",
+    "NEXT_PUBLIC_API_URL_HOST",
+    "NEXT_PUBLIC_API_URL_BASE"
+  ],
📝 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
"globalEnv": ["NODE_ENV", "NEXT_RUNTIME", "CI", "NEXT_PHASE"],
"globalEnv": [
"NODE_ENV",
"NEXT_RUNTIME",
"CI",
"NEXT_PHASE",
"NEXT_PUBLIC_API_URL",
"NEXT_PUBLIC_API_URL_BUILD",
"NEXT_PUBLIC_API_URL_HOST",
"NEXT_PUBLIC_API_URL_BASE"
],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@turbo.json` at line 4, The globalEnv array in turbo.json is missing API URL
related keys (NEXT_PUBLIC_API_URL, NEXT_PUBLIC_API_URL_BUILD,
NEXT_PUBLIC_API_URL_HOST, NEXT_PUBLIC_API_URL_BASE), so add those four entries
to the "globalEnv" list to ensure changes to API endpoints invalidate Turbo
cache; update the "globalEnv" value (the symbol to change is globalEnv in
turbo.json) to include these keys alongside NODE_ENV, NEXT_RUNTIME, CI,
NEXT_PHASE.

Copy link
Copy Markdown
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: 1

🧹 Nitpick comments (1)
apps/web/app/_lib/axiosInstance.ts (1)

72-96: 응답 인터셉터에 빌드 시점 가드 추가 권장

응답 인터셉터의 401 토큰 갱신 로직(라인 72-96)에 isBuildTime 가드가 없어 빌드 시점에 401 응답 발생 시 getToken()setCookie()가 서버 컨텍스트에서 실행될 수 있습니다. 현재 카테고리 API는 공개 엔드포인트이지만, 향후 빌드 시점에 인증이 필요한 데이터를 페칭하는 경우에 대비해 다음 패턴을 고려하세요:

if (error.response?.status === 401 && !originalRequest._retry) {
  if (isBuildTime) return Promise.reject(error)
  // ... 기존 토큰 갱신 로직
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/_lib/axiosInstance.ts` around lines 72 - 96, Add a build-time
guard inside the response interceptor's 401 retry block: before calling
getToken() or setCookie() (inside the if (error.response?.status === 401 &&
!originalRequest._retry) branch in the axios response interceptor), check if
isBuildTime and immediately return Promise.reject(error) to avoid executing
getToken, setCookie, or retrying the request during server/build execution;
preserve setting originalRequest._retry and the existing refresh flow (getToken,
setCookie, update originalRequest.headers.Authorization, and return
axiosInstance(originalRequest)) for non-build-time runs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/app/_lib/axiosInstance.ts`:
- Around line 20-27: The getBaseURL function uses non-null assertions on
process.env.NEXT_PUBLIC_API_URL and NEXT_PUBLIC_API_URL_BUILD which can cause
runtime errors if those env vars are missing; update getBaseURL to safely read
those variables (no "!"), validate their presence at runtime and either throw a
clear error or return a sensible fallback, e.g., check isBuildTime and prefer
process.env.NEXT_PUBLIC_API_URL_BUILD ?? process.env.NEXT_PUBLIC_API_URL, and if
still undefined throw a descriptive Error mentioning
NEXT_PUBLIC_API_URL/NEXT_PUBLIC_API_URL_BUILD; also ensure your CI/build config
(e.g., turbo.json globalEnv) includes these keys so they are available at build
time.

---

Nitpick comments:
In `@apps/web/app/_lib/axiosInstance.ts`:
- Around line 72-96: Add a build-time guard inside the response interceptor's
401 retry block: before calling getToken() or setCookie() (inside the if
(error.response?.status === 401 && !originalRequest._retry) branch in the axios
response interceptor), check if isBuildTime and immediately return
Promise.reject(error) to avoid executing getToken, setCookie, or retrying the
request during server/build execution; preserve setting originalRequest._retry
and the existing refresh flow (getToken, setCookie, update
originalRequest.headers.Authorization, and return
axiosInstance(originalRequest)) for non-build-time runs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 67fdfeeb-8705-4ab0-b22d-1fa4533e95e5

📥 Commits

Reviewing files that changed from the base of the PR and between 72cb65d and 58ee346.

📒 Files selected for processing (2)
  • apps/web/app/_lib/axiosInstance.ts
  • apps/web/app/profile/page.tsx

Comment on lines +20 to +27
const getBaseURL = (): string => {
if (isBuildTime) {
return (
process.env.NEXT_PUBLIC_API_URL_BUILD || process.env.NEXT_PUBLIC_API_URL!
)
}
return process.env.NEXT_PUBLIC_API_URL!
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

환경 변수에 non-null assertion(!) 사용은 런타임 오류 위험이 있습니다.

process.env.NEXT_PUBLIC_API_URL!에 non-null assertion을 사용하면 TypeScript 타입 검사를 우회하게 됩니다. strict 모드와 noUncheckedIndexedAccess가 활성화된 상태에서, 환경 변수가 설정되지 않은 경우 undefinedbaseURL로 전달되어 런타임 오류가 발생할 수 있습니다.

또한 turbo.jsonglobalEnvNEXT_PUBLIC_API_URLNEXT_PUBLIC_API_URL_BUILD가 포함되어 있지 않아 빌드 시점에 환경 변수가 누락될 위험이 있습니다.

🔧 제안 패치
 const getBaseURL = (): string => {
+  const buildUrl = process.env.NEXT_PUBLIC_API_URL_BUILD
+  const defaultUrl = process.env.NEXT_PUBLIC_API_URL
+
   if (isBuildTime) {
-    return (
-      process.env.NEXT_PUBLIC_API_URL_BUILD || process.env.NEXT_PUBLIC_API_URL!
-    )
+    const url = buildUrl ?? defaultUrl
+    if (!url) {
+      throw new Error('API URL이 설정되지 않았습니다: NEXT_PUBLIC_API_URL_BUILD 또는 NEXT_PUBLIC_API_URL 환경 변수를 확인하세요.')
+    }
+    return url
+  }
+
+  if (!defaultUrl) {
+    throw new Error('API URL이 설정되지 않았습니다: NEXT_PUBLIC_API_URL 환경 변수를 확인하세요.')
   }
-  return process.env.NEXT_PUBLIC_API_URL!
+  return defaultUrl
 }

Based on learnings: "Use TypeScript in strict mode with noUncheckedIndexedAccess enabled and return types required on functions"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/_lib/axiosInstance.ts` around lines 20 - 27, The getBaseURL
function uses non-null assertions on process.env.NEXT_PUBLIC_API_URL and
NEXT_PUBLIC_API_URL_BUILD which can cause runtime errors if those env vars are
missing; update getBaseURL to safely read those variables (no "!"), validate
their presence at runtime and either throw a clear error or return a sensible
fallback, e.g., check isBuildTime and prefer
process.env.NEXT_PUBLIC_API_URL_BUILD ?? process.env.NEXT_PUBLIC_API_URL, and if
still undefined throw a descriptive Error mentioning
NEXT_PUBLIC_API_URL/NEXT_PUBLIC_API_URL_BUILD; also ensure your CI/build config
(e.g., turbo.json globalEnv) includes these keys so they are available at build
time.

@leeleeleeleejun leeleeleeleejun merged commit 8729a8a into develop Mar 13, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 홈페이지(Home) SSG 카테고리 & CSR 랭킹 하이브리드 렌더링 구현

1 participant