Skip to content
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
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ jobs:
run: yarn run ci

- name: Build Next.js app for E2E tests
run: NEXT_PUBLIC_TEST="1" yarn workspace @ject-5-fe/app build
env:
NEXT_PUBLIC_TEST: "1"
run: yarn workspace @ject-5-fe/app build

- name: Cache E2E test results
uses: actions/cache@v4
Expand Down
25 changes: 25 additions & 0 deletions service/app/src/app/__tests__/helpers/log.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { test as setup } from "@playwright/test"

setup("attach browser logs", async ({ page }) => {
page.on("console", (msg) => {
console.log(`[browser:${msg.type()}] ${msg.text()}`)
})

page.on("pageerror", (err) => {
console.log(`[pageerror] ${err.message}\n${err.stack ?? ""}`)
})

page.on("requestfailed", (req) => {
console.log(
`[requestfailed] ${req.method()} ${req.url()} -> ${req.failure()?.errorText ?? "unknown"}`,
)
})

page.on("response", (res) => {
if (res.status() >= 400) {
console.log(
`[response:${res.status()}] ${res.request().method()} ${res.url()}`,
)
}
})
})
2 changes: 1 addition & 1 deletion service/app/src/app/__tests__/homePOM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class HomePOM {

// 게임 섹션
this.gameSectionTitle = page.getByRole("heading", { level: 2 })
this.viewMoreGamesButton = page.getByRole("button", {
this.viewMoreGamesButton = page.getByRole("link", {
name: "게임 더 보기",
})
this.gameSectionCard = page.getByTestId("gamecard-root")
Expand Down
29 changes: 29 additions & 0 deletions service/app/src/app/_components/GameSection/GameSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Suspense } from "react"

import { getDefaultGame } from "@/entities/game/api/getDefaultGame"

import { GameSectionClient } from "./GameSectionClient"
import { GameSectionHeader } from "./GameSectionHeader"
import { GameSectionSkeleton } from "./GameSectionSkeleton"

interface GameSectionProps {
className?: string
}

export const GameSection = async ({ className = "" }: GameSectionProps) => {
const response = await getDefaultGame({ cache: "force-cache" })
const games = response.data.games

return (
<section
className={`flex w-full flex-col items-center justify-center self-stretch p-0 ${className}`}
>
<div className="flex min-w-[952px] flex-col gap-28">
<GameSectionHeader />
<Suspense fallback={<GameSectionSkeleton />}>
<GameSectionClient games={games} />
</Suspense>
Comment on lines +23 to +25
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Suspense 경계가 효과가 없습니다

Line 14에서 getDefaultGame을 호출하고 await한 후, 이미 해결된 데이터를 Line 24에서 GameSectionClient에 props로 전달하고 있습니다. GameSectionClient는 클라이언트 컴포넌트이며 이미 사용 가능한 데이터를 렌더링하기만 하므로 suspend되지 않습니다.

Suspense는 비동기 작업이 진행 중일 때만 fallback을 표시합니다. 현재 구조에서는 데이터가 이미 fetching 완료된 후이므로 GameSectionSkeleton이 표시되지 않습니다.

다음 중 하나를 선택하세요:

  1. 데이터 fetching을 별도의 서버 컴포넌트로 분리하고 해당 컴포넌트를 Suspense로 감싸기
  2. Suspense를 제거하고 로딩 상태를 다른 방식으로 처리하기
🔎 제안된 수정 (옵션 1: 데이터 fetching 분리)

별도의 서버 컴포넌트를 생성:

// GameSectionContent.tsx
import { getDefaultGame } from "@/entities/game/api/getDefaultGame"
import { GameSectionClient } from "./GameSectionClient"

export const GameSectionContent = async () => {
  const response = await getDefaultGame({ cache: "force-cache" })
  const games = response.data.games
  
  return <GameSectionClient games={games} />
}

그런 다음 GameSection에서 사용:

-export const GameSection = async ({ className = "" }: GameSectionProps) => {
-  const response = await getDefaultGame({ cache: "force-cache" })
-  const games = response.data.games
-
+export const GameSection = ({ className = "" }: GameSectionProps) => {
   return (
     <section
       className={`flex w-full flex-col items-center justify-center self-stretch p-0 ${className}`}
     >
       <div className="flex min-w-[952px] flex-col gap-28">
         <GameSectionHeader />
         <Suspense fallback={<GameSectionSkeleton />}>
-          <GameSectionClient games={games} />
+          <GameSectionContent />
         </Suspense>
       </div>
     </section>
   )
 }

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

🤖 Prompt for AI Agents
In service/app/src/app/_components/GameSection/GameSection.tsx around lines 23
to 25 (and noting getDefaultGame is awaited at line 14), the Suspense boundary
is ineffective because the data is already fetched and passed into the client
component; either move the data fetching into a separate async server component
and render the client inside Suspense, or remove the Suspense and handle loading
state another way. To fix, choose one: (A) create a new server component (e.g.,
GameSectionContent) that calls getDefaultGame and returns <GameSectionClient
games={...} />, then keep Suspense around that server component so the fallback
displays during fetch; or (B) delete the Suspense wrapper here and implement
explicit loading handling inside the client component (or via a prop) since the
data is provided synchronously.

</div>
</section>
)
}
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.

⚠️ Potential issue | 🟡 Minor

파일명 규칙 위반

코딩 가이드라인에 따르면 모든 파일명은 camelCase를 사용해야 하지만, 이 파일은 PascalCase(GameSection.tsx)를 사용하고 있습니다. gameSection.tsx로 변경해야 합니다.

코딩 가이드라인 기준

🤖 Prompt for AI Agents
In service/app/src/app/_components/GameSection/GameSection.tsx around lines 1 to
29, the filename uses PascalCase (GameSection.tsx) which violates the project's
camelCase file naming convention; rename the file to gameSection.tsx and update
any imports/exports that reference the old filename (search the repo for
"GameSection" imports and change them to "gameSection"), verify
TypeScript/ESLint build passes, and ensure any path-sensitive references (tests,
storybook, index files) are updated accordingly.

45 changes: 45 additions & 0 deletions service/app/src/app/_components/GameSection/GameSectionClient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"use client"

import type { GameListItem } from "@/entities/game"
import { useGamePreview } from "@/entities/game/hooks/useGamePreview"
import * as GameCard from "@/entities/game/ui/GameCard/gameCard"

interface GameSectionClientProps {
games: GameListItem[]
}

export const GameSectionClient = ({ games }: GameSectionClientProps) => {
const { openPreview } = useGamePreview()

return (
<div
className="flex items-center justify-between"
data-testid="game-section-cards"
>
{games?.map((game) => (
<GameCard.Root
key={game.gameId}
className="w-[178px]"
title={game.gameTitle}
onClick={() => openPreview(game)}
aria-label="게임 카드"
tabIndex={0}
>
<GameCard.Image
src={game.gameThumbnailUrl ?? "/checker.svg"}
alt={game.gameTitle}
sizes="178px"
placeholder="blur"
blurDataURL="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzI3IiBoZWlnaHQ9IjQ1OSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZTVlN2ViIi8+PC9zdmc+"
>
<GameCard.Badge>{game.questionCount}문제</GameCard.Badge>
{game.isShared && (
<GameCard.Badge variant="bottom-left">공유</GameCard.Badge>
)}
</GameCard.Image>
<GameCard.Description>{game.gameTitle}</GameCard.Description>
</GameCard.Root>
))}
</div>
)
}
Comment on lines +1 to +45
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

파일명 규칙 위반

코딩 가이드라인에 따르면 모든 파일명은 camelCase를 사용해야 하지만, 이 파일은 PascalCase(GameSectionClient.tsx)를 사용하고 있습니다. gameSectionClient.tsx로 변경해야 합니다.

코딩 가이드라인 기준

🤖 Prompt for AI Agents
In service/app/src/app/_components/GameSection/GameSectionClient.tsx lines 1-45:
the filename uses PascalCase but your project requires camelCase; rename the
file to gameSectionClient.tsx (use git mv to preserve history), update all
imports and any barrel/index exports that reference GameSectionClient to the new
filename, adjust any export/default names if your linter enforces
filename-export parity, run the TypeScript/ESLint build to find remaining
references and fix them, then commit the rename.

15 changes: 15 additions & 0 deletions service/app/src/app/_components/GameSection/GameSectionHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { PrimaryBoxButton } from "@ject-5-fe/design/components/button"
import Link from "next/link"

export const GameSectionHeader = () => {
return (
<div className="flex w-full items-center justify-between">
<h2 className="typography-heading-lg-semibold text-text-interactive-secondary">
어떤 게임으로 시작해 볼까요?
</h2>
<PrimaryBoxButton size="md" _style="outline" asChild>
<Link href="/games">게임 더 보기</Link>
</PrimaryBoxButton>
</div>
)
}
Comment on lines +1 to +15
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

파일명 규칙 위반

코딩 가이드라인에 따르면 모든 파일명은 camelCase를 사용해야 하지만, 이 파일은 PascalCase(GameSectionHeader.tsx)를 사용하고 있습니다. gameSectionHeader.tsx로 변경해야 합니다.

컴포넌트 구현은 적절합니다. asChild 패턴을 사용한 Link 래핑이 올바르게 적용되었습니다.

코딩 가이드라인 기준

🤖 Prompt for AI Agents
In service/app/src/app/_components/GameSection/GameSectionHeader.tsx lines 1-15,
the file name violates the camelCase rule; rename the file to
gameSectionHeader.tsx and update all references/imports to the new path. Perform
a git mv to preserve history, update any barrel/index exports or test/storybook
imports that reference the old filename, and ensure TypeScript import paths and
Next.js any-case-sensitive environments reflect the change.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { GameCardSkeleton } from "@/entities/game/ui/GameCard/GameCardSkeleton"

export const GameSectionSkeleton = () => {
return (
<div className="flex items-center justify-between">
{Array.from({ length: 4 }).map((_, index) => (
<GameCardSkeleton key={index} />
))}
</div>
)
}
Comment on lines +1 to +11
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

파일 이름 규칙 위반

코딩 가이드라인에 따르면 모든 파일 이름은 camelCase를 사용해야 하지만, 이 파일은 PascalCase를 사용하고 있습니다.

  • 현재: GameSectionSkeleton.tsx
  • 권장: gameSectionSkeleton.tsx

마찬가지로 디렉토리 이름도 GameSection/이 아닌 gameSection/이어야 합니다. 코드베이스의 다른 파일들(예: gamePreview.tsx, gameLibraryGrid.tsx)은 camelCase 규칙을 따르고 있으므로 일관성을 위해 이 파일도 동일한 규칙을 따라야 합니다.

기준 코딩 가이드라인

코딩 가이드라인 명시 사항:

  • **/*: All file names must be camelCase (not PascalCase or kebab-case)
  • **/*.{ts,tsx}: Component names must use PascalCase in the code (while file names use camelCase)
🤖 Prompt for AI Agents
In service/app/src/app/_components/GameSection/GameSectionSkeleton.tsx around
lines 1 to 11, the file and containing directory use PascalCase which violates
the camelCase file naming guideline; rename the file to gameSectionSkeleton.tsx
and the directory to gameSection/, keep the exported component name
GameSectionSkeleton (PascalCase) unchanged in the code, and update all import
paths across the codebase to point to the new
./gameSection/gameSectionSkeleton.tsx location (including barrel/index files and
any tests) so no import breaks occur.

Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { useIntersectionObserver } from "react-simplikit"

import { useGamePreview } from "@/entities/game/hooks/useGamePreview"
import { useInfiniteMyGames } from "@/entities/game/model/useInfiniteMyGames"
import { GameCardOptions } from "@/entities/game/ui/components/gameCardOptions"
import { GameLibrarySkeleton } from "@/entities/game/ui/components/gameLibrarySkeleton"
import * as GameCard from "@/shared/gameCard"
import * as GameCard from "@/entities/game/ui/GameCard/gameCard"
import { GameCardOptions } from "@/entities/game/ui/gameCardOptions"
import { GameLibrarySkeleton } from "@/entities/game/ui/gameLibrarySkeleton"

import { useDashboardGameActions } from "../hooks/useDashboardGameActions"

Expand Down
4 changes: 2 additions & 2 deletions service/app/src/app/games/components/gamesLibrarySection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { useIntersectionObserver } from "react-simplikit"

import { useGamePreview } from "@/entities/game/hooks/useGamePreview"
import { useInfiniteGameList } from "@/entities/game/model/useInfiniteGameList"
import { GameLibrarySkeleton } from "@/entities/game/ui/components/gameLibrarySkeleton"
import * as GameCard from "@/shared/gameCard"
import * as GameCard from "@/entities/game/ui/GameCard/gameCard"
import { GameLibrarySkeleton } from "@/entities/game/ui/gameLibrarySkeleton"

import { filterInput } from "../utils/filterInput"
import { GameCardActions } from "./gameCardActions"
Expand Down
25 changes: 5 additions & 20 deletions service/app/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,15 @@
"use client"

import { ErrorBoundary } from "react-error-boundary"

import { useAuthStore } from "@/entities/auth"
import { GameSection } from "@/widgets/GameSection"
import { HeroSection } from "@/widgets/HeroSection"
import { GameSection } from "@/app/_components/GameSection/GameSection"
import { HeroSection } from "@/app/_components/HeroSection"
import { HomeNavigation } from "@/widgets/HomeNavigation"

import ErrorPage from "./error"

function HomeContent() {
const { isAuthenticated } = useAuthStore()

export default function Home() {
return (
<main className="flex min-h-screen w-full flex-col items-start gap-112 bg-background-primary">
<HomeNavigation isLoggedIn={isAuthenticated} />
<HomeNavigation />
<HeroSection />
<GameSection />
</main>
)
}

export default function Home() {
return (
<ErrorBoundary FallbackComponent={ErrorPage}>
<HomeContent />
</ErrorBoundary>
)
}
export const dynamic = "force-dynamic"
Copy link

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
# Check page.tsx and GameSection for dynamic data fetching requirements

echo "=== Checking page.tsx content ==="
if [ -f "service/app/src/app/page.tsx" ]; then
  cat -n "service/app/src/app/page.tsx"
else
  echo "File not found"
fi

echo ""
echo "=== Checking GameSection directory ==="
find service/app/src/app/_components -name "*GameSection*" -type f 2>/dev/null | head -20

echo ""
echo "=== Checking GameSection implementation ==="
find service/app/src/app/_components -path "*GameSection*" -type f -name "*.tsx" -o -name "*.ts" 2>/dev/null | head -5

Repository: JECT-Study/JECT-5-FE

Length of output: 1268


🏁 Script executed:

#!/bin/bash
# Check GameSection components for dynamic data patterns

echo "=== GameSection.tsx ==="
cat -n "service/app/src/app/_components/GameSection/GameSection.tsx"

echo ""
echo "=== GameSectionClient.tsx ==="
wc -l "service/app/src/app/_components/GameSection/GameSectionClient.tsx"
head -60 "service/app/src/app/_components/GameSection/GameSectionClient.tsx"

Repository: JECT-Study/JECT-5-FE

Length of output: 2760


force-dynamic 설정을 재검토하세요 - 현재 구조상 불필요합니다.

페이지에 export const dynamic = "force-dynamic"이 설정되어 있지만, GameSection은 이미 cache: "force-cache"로 데이터를 페칭하고 Suspense 경계로 클라이언트 컴포넌트를 적절히 감싸고 있습니다. page.tsx는 동적 함수(cookies, headers, searchParams)를 직접 사용하지 않으므로 force-dynamic이 필요하지 않습니다.

제안:

  • force-dynamic 제거 - Suspense 경계가 비동기 데이터 처리를 올바르게 관리합니다
  • 현재 구조는 Partial Prerendering(PPR)에 이상적이므로 정적 쉘은 캐시되고 동적 부분만 스트리밍됩니다
🤖 Prompt for AI Agents
In service/app/src/app/page.tsx around line 15, remove the unnecessary export
const dynamic = "force-dynamic" since this page does not use dynamic functions
(cookies, headers, searchParams) and GameSection already handles data fetching
with cache: "force-cache" wrapped by Suspense; delete that line so the page can
use the default PPR/static shell behavior and confirm dev build/SSR behaves as
expected.

2 changes: 1 addition & 1 deletion service/app/src/entities/game/hooks/useGamePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useCallback } from "react"
import type { GameListItem } from "@/entities/game"
import { getGameDetail } from "@/entities/game/api/getGameDetail"
import { GameQuestion } from "@/entities/game/model/game"
import { GamePreview } from "@/entities/game/ui/components/gamePreview"
import { GamePreview } from "@/entities/game/ui/gamePreview"

import { useGameEntryNavigation } from "./useGameEntryNavigation"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const GameCardSkeleton = () => {
return (
<div className="flex w-[178px] flex-col items-start gap-[14px]">
<div className="size-[178px] animate-pulse rounded-[10px] bg-gray-200" />
<div className="h-[46px] w-[178px] animate-pulse rounded bg-gray-200" />
</div>
)
}
Comment on lines +1 to +8
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

파일명 규칙 위반

코딩 가이드라인에 따르면 모든 파일명은 camelCase를 사용해야 하지만, 이 파일은 PascalCase(GameCardSkeleton.tsx)를 사용하고 있습니다. gameCardSkeleton.tsx로 변경해야 합니다.

컴포넌트 구현 자체는 적절합니다. 스켈레톤 UI가 명확하게 정의되어 있고 Tailwind 유틸리티가 올바르게 사용되었습니다.

코딩 가이드라인 기준

🤖 Prompt for AI Agents
In service/app/src/entities/game/ui/GameCard/GameCardSkeleton.tsx lines 1-8: the
file name uses PascalCase which violates the camelCase filename rule; rename the
file to gameCardSkeleton.tsx and update all imports/exports that reference this
path (including any index/barrel files, tests, storybooks, and build configs) to
the new path, keeping the component identifier GameCardSkeleton unchanged; run a
project-wide search to replace old path references, adjust any relative import
casing, and run tests/linter to verify nothing breaks.

1 change: 0 additions & 1 deletion service/app/src/entities/game/ui/components/index.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import Link from "next/link"
import { useIntersectionObserver } from "react-simplikit"

import type { GameListItem } from "@/entities/game/model"
import * as GameCard from "@/shared/gameCard"
import * as GameCard from "@/entities/game/ui/GameCard/gameCard"

import { useActions } from "../../../../app/games/hooks/useGameCardActions"
import { useActions } from "../../../app/games/hooks/useGameCardActions"
import { GameLibrarySkeleton } from "./gameLibrarySkeleton"

interface GameLibraryGridProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from "@ject-5-fe/design/components/dialog"
import { Cross, Play } from "@ject-5-fe/design/icons"

import * as GameCard from "@/shared/gameCard"
import * as GameCard from "@/entities/game/ui/GameCard/gameCard"

interface GamePreviewProps {
className?: string
Expand Down
14 changes: 6 additions & 8 deletions service/app/src/mocks/mswProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,16 @@ const MSWContext = createContext<MSWContextValue>({
export const useMsw = () => useContext(MSWContext)

export const MSWProvider = ({ children }: { children: React.ReactNode }) => {
const [isMswReady, setIsMswReady] = useState(
process.env.NODE_ENV === "production",
)
const shouldInitMsw =
process.env.NODE_ENV !== "production" || !!process.env.NEXT_PUBLIC_TEST

const [isMswReady, setIsMswReady] = useState(!shouldInitMsw)
const [isMswError, setIsMswError] = useState(false)

useEffect(() => {
const init = async () => {
try {
if (
process.env.NODE_ENV !== "production" ||
process.env.NEXT_PUBLIC_TEST //테스트 환경에서 msw활용을 위해 주입되는 환경변수
) {
if (shouldInitMsw) {
const { initMsw } = await import("./index")
await initMsw()
}
Expand All @@ -36,7 +34,7 @@ export const MSWProvider = ({ children }: { children: React.ReactNode }) => {
}

init()
}, [])
}, [shouldInitMsw])

return (
<MSWContext.Provider value={{ isMswReady, isMswError }}>
Expand Down
57 changes: 0 additions & 57 deletions service/app/src/shared/api/fetchClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,6 @@ import { type ApiError, type ApiSuccess, errorSchema } from "./types/response"

const isDev = process.env.NODE_ENV === "development"

// const base = createFetchClient({
// baseUrl: process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000",
// timeout: 10000,
// credentials: "include",
// })

const _instance = ky.create({
prefixUrl: process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000",
credentials: "include",
Expand Down Expand Up @@ -84,54 +78,3 @@ export const fetchClient = {
export const isError = (e: unknown): e is HTTPError<ApiError> => {
return isHTTPError(e)
}

// if (process.env.NODE_ENV === "development") {
// base.addRequestInterceptor(async (url, options) => {
// console.log("🚀 Request:", url, options)
// return { url, options }
// })
// base.addResponseInterceptor(async (response) => {
// console.log("📥 Response:", response.status, response.url)
// return response
// })
// }

// base.addResponseInterceptor(
// async (response: Response) => {
// if (response.status === 401 && typeof window !== "undefined") {
// localStorage.removeItem("auth_user")
// document.cookie =
// "JSESSIONID=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;"
// window.dispatchEvent(new CustomEvent("auth:session-expired"))
// }

// let body: unknown
// try {
// body = await response.clone().json()
// } catch {
// body = undefined
// }

// const successResponse = successSchema.safeParse(body)
// if (successResponse.success) {
// return response
// } //성공 데이터

// const errorResponse = errorSchema.safeParse(body)

// if (errorResponse.success) {
// throw new FetchError(response.status, errorResponse.data.error)
// } //에러 데이터
// else {
// throw new FetchError(response.status, {
// code: "unknown error",
// message: response.statusText || "unknown error occured",
// data: null,
// }) //예상하지 못한 에러
// }
// },
// async (error) => {
// console.error("Response interceptor error:", error)
// throw error
// },
// )
Loading
Loading