-
Notifications
You must be signed in to change notification settings - Fork 1
Feat/#51 Vercel 배포 관련 작업 #52
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
Changes from all commits
e77bb60
c66d438
2dec101
8f7a573
2945fb0
bcc5e8b
b6118cc
045b340
3a79456
bced125
877eb8f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,6 @@ | ||||||||
| 'use client' | ||||||||
|
|
||||||||
| import { useState } from 'react' | ||||||||
| import { usePathname } from 'next/navigation' | ||||||||
| import { useSuspenseQuery } from '@tanstack/react-query' | ||||||||
| import { useCategoryQueries } from '@/_apis/queries/category' | ||||||||
| import { Icon } from '@repo/ui/components/Icon' | ||||||||
|
|
@@ -9,21 +9,24 @@ import { Header } from '@repo/ui/components/Header' | |||||||
| import { Flex } from '@repo/ui/components/Layout' | ||||||||
| import { HeaderBackButton } from '@/_components/HeaderBackButton' | ||||||||
| import { RowCategories, Places } from './_components' | ||||||||
| import { Suspense, useEffect } from 'react' | ||||||||
| import { Spinner } from '@heroui/react' | ||||||||
|
|
||||||||
| type Props = { | ||||||||
| initId: string | ||||||||
| } | ||||||||
|
|
||||||||
| export const CategoryDetailPage = ({ initId }: Props) => { | ||||||||
| const [id, setId] = useState(initId) | ||||||||
|
|
||||||||
| export const CategoryDetailPage = () => { | ||||||||
| const { data: categories } = useSuspenseQuery(useCategoryQueries.list()) | ||||||||
| const activeCategory = categories.find((category) => category.id === id) | ||||||||
| const activeCategoryId = usePathname().split('/')[2] || '0' | ||||||||
| const activeCategory = categories.find( | ||||||||
| (category) => category.id === activeCategoryId, | ||||||||
| ) | ||||||||
|
|
||||||||
| const setIdFunc = (id: string) => { | ||||||||
| setId(id) | ||||||||
| window.history.replaceState(null, '', `/categories/${id}`) | ||||||||
| } | ||||||||
|
|
||||||||
| useEffect(() => { | ||||||||
| document.title = `공주대 맛집 | ${activeCategory?.name}` | ||||||||
| }, [activeCategory]) | ||||||||
|
Comment on lines
+26
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. document.title 직접 조작을 제거하세요.
다음과 같이 useEffect를 제거하세요: - useEffect(() => {
- document.title = `공주대 맛집 | ${activeCategory?.name}`
- }, [activeCategory])
-📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||
|
|
||||||||
| return ( | ||||||||
| <> | ||||||||
| <Header | ||||||||
|
|
@@ -38,8 +41,15 @@ export const CategoryDetailPage = ({ initId }: Props) => { | |||||||
| } | ||||||||
| className={'border-b-1 border-gray-50'} | ||||||||
| /> | ||||||||
| <RowCategories id={id} categories={categories} setIdFunc={setIdFunc} /> | ||||||||
| <Places id={id} /> | ||||||||
| <RowCategories | ||||||||
| id={activeCategoryId} | ||||||||
| categories={categories} | ||||||||
| setIdFunc={setIdFunc} | ||||||||
| /> | ||||||||
| {/*Todo: 맛집 리스트 스켈레톤으로 변경하기*/} | ||||||||
| <Suspense fallback={<Spinner className={'my-auto'} />}> | ||||||||
| <Places id={activeCategoryId} /> | ||||||||
| </Suspense> | ||||||||
| </> | ||||||||
| ) | ||||||||
| } | ||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,39 @@ | ||
| import type { Metadata } from 'next' | ||
| import { HydrationBoundaryPage } from '@/HydrationBoundaryPage' | ||
| import { useCategoryQueries } from '@/_apis/queries/category' | ||
| import { CategoryDetailPage } from '@/categories/[id]/CategoryDetailPage' | ||
| import { getCategories } from '@/_apis/services/category' | ||
|
|
||
| const Page = async ({ params }: { params: Promise<{ id: string }> }) => { | ||
| const { id } = await params | ||
| export async function generateMetadata({ | ||
| params, | ||
| }: { | ||
| params: Promise<{ id: string }> | ||
| }): Promise<Metadata> { | ||
| const { id: categoryId } = await params | ||
|
|
||
| const data = await getCategories() | ||
| const categoryName = data?.find(({ id }) => id === categoryId)?.name | ||
|
|
||
| return { | ||
| title: `${categoryName}`, | ||
| description: `공주대학교 근처 ${categoryName} 맛집`, | ||
| openGraph: { | ||
| title: `공주대 맛집 | ${categoryName}`, | ||
| description: `공주대학교 근처 ${categoryName} 맛집`, | ||
| locale: 'ko-KR', | ||
| type: 'website', | ||
| }, | ||
| } | ||
| } | ||
|
Comment on lines
+7
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. generateMetadata에 에러 핸들링을 추가하세요. 카테고리를 찾지 못했을 때를 대비한 에러 핸들링이 필요합니다. 다음과 같이 폴백을 추가하세요: export async function generateMetadata({
params,
}: {
params: Promise<{ id: string }>
}): Promise<Metadata> {
const { id: categoryId } = await params
const data = await getCategories()
- const categoryName = data?.find(({ id }) => id === categoryId)?.name
+ const categoryName = data?.find(({ id }) => id === categoryId)?.name || '카테고리'
return {
title: `${categoryName}`,
description: `공주대학교 근처 ${categoryName} 맛집`,
openGraph: {
title: `공주대 맛집 | ${categoryName}`,
description: `공주대학교 근처 ${categoryName} 맛집`,
locale: 'ko-KR',
type: 'website',
},
}
}🤖 Prompt for AI Agents |
||
|
|
||
| const Page = () => { | ||
| return ( | ||
| <HydrationBoundaryPage | ||
| prefetch={async (queryClient) => { | ||
| await queryClient.prefetchQuery(useCategoryQueries.list()) | ||
| }} | ||
| > | ||
| <CategoryDetailPage initId={id} /> | ||
| <CategoryDetailPage /> | ||
| </HydrationBoundaryPage> | ||
| ) | ||
| } | ||
|
Comment on lines
+29
to
39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HydrationBoundary를 사용하는 페이지에 dynamic 설정이 누락되었습니다. PR 설명에 따르면 HydrationBoundary를 사용하는 페이지에 다음과 같이 dynamic export를 추가하세요: import type { Metadata } from 'next'
import { HydrationBoundaryPage } from '@/HydrationBoundaryPage'
import { useCategoryQueries } from '@/_apis/queries/category'
import { CategoryDetailPage } from '@/categories/[id]/CategoryDetailPage'
import { getCategories } from '@/_apis/services/category'
+export const dynamic = 'force-dynamic'
+
export async function generateMetadata({
🤖 Prompt for AI Agents |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| 'use client' | ||
|
|
||
| import dynamic from 'next/dynamic' | ||
|
|
||
| const MapComponent = dynamic(() => import('./MapComponent'), { | ||
| ssr: false, | ||
| loading: () => <div>지도 로딩 중...</div>, | ||
| }) | ||
|
|
||
| export const MapContainer = () => <MapComponent /> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,31 @@ | ||
| import type { Metadata } from 'next' | ||
| import { usePlaceQueries } from '@/_apis/queries/place' | ||
| import { HydrationBoundaryPage } from '@/HydrationBoundaryPage' | ||
| import { PlaceDetailPage } from './PlaceDetailPage' | ||
| import { getPlaceDetail } from '@/_apis/services/place' | ||
|
|
||
| export async function generateMetadata({ | ||
| params, | ||
| }: { | ||
| params: Promise<{ id: string }> | ||
| }): Promise<Metadata> { | ||
| const { id } = await params | ||
|
|
||
| const data = await getPlaceDetail(id) | ||
| const { placeName } = data | ||
| const pageDescription = `공주대학교 근처 ${placeName} 맛집 상세 정보` | ||
|
|
||
| return { | ||
| title: `${placeName}`, | ||
| description: pageDescription, | ||
| openGraph: { | ||
| title: `공주대 맛집 | ${placeName}`, | ||
| description: pageDescription, | ||
| locale: 'ko-KR', | ||
| type: 'website', | ||
| }, | ||
| } | ||
| } | ||
|
Comment on lines
+7
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain메타데이터 생성이 올바르게 구현되었습니다.
다만, 다음 스크립트로 HydrationBoundaryPage를 사용하면서 dynamic export가 없는 페이지를 확인하세요: 🏁 Script executed: #!/bin/bash
# HydrationBoundaryPage를 사용하지만 dynamic export가 없는 파일 찾기
for file in $(rg -l "HydrationBoundaryPage" --type=tsx --type=ts apps/web/app); do
if ! grep -q "export const dynamic" "$file"; then
echo "Missing dynamic export: $file"
fi
doneLength of output: 27 고마워요. 스크립트에 문제가 있네요. Length of output: 265 파일 타입 지정 문제가 있네요. 다른 방식으로 접근하겠습니다. 🏁 Script executed: #!/bin/bash
# 파일 타입 필터링 없이 HydrationBoundaryPage 검색
echo "=== Files using HydrationBoundaryPage ==="
rg "HydrationBoundaryPage" apps/web/app -l
echo ""
echo "=== Current file status ==="
echo "File: apps/web/app/places/[id]/page.tsx"
if grep -q "export const dynamic" "apps/web/app/places/[id]/page.tsx"; then
echo "Dynamic export: ✓ Present"
else
echo "Dynamic export: ✗ Missing"
fi
echo ""
echo "=== Checking all identified files for dynamic export ==="
rg "HydrationBoundaryPage" apps/web/app -l | while read file; do
if grep -q "export const dynamic" "$file"; then
echo "✓ HAS: $file"
else
echo "✗ LACKS: $file"
fi
doneLength of output: 877
검증 결과,
대부분의 🤖 Prompt for AI Agents |
||
|
|
||
| const Page = async ({ params }: { params: Promise<{ id: string }> }) => { | ||
| const { id } = await params | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Next.js 라우터 사용을 고려하세요.
window.history.replaceState를 직접 사용하는 것은 Next.js의 클라이언트 사이드 라우팅을 우회합니다. 이는 프리페칭, 스크롤 복원 등의 Next.js 기능과 충돌할 수 있습니다.다음 스크립트를 실행하여 Next.js router를 사용하는 다른 예시가 있는지 확인하세요:
next/navigation의useRouter를 사용하는 것이 더 적절할 수 있습니다:🏁 Script executed:
Length of output: 27
새로운 스크립트를 실행하여 코드베이스를 재검증하겠습니다.
Length of output: 4609
🏁 Script executed:
Length of output: 170
🏁 Script executed:
Length of output: 1746
Next.js 라우터 사용으로 변경하세요.
이 파일은 이미
'use client'선언이 있고next/navigation에서usePathname을 임포트하고 있습니다. 코드베이스의 다른 컴포넌트들(HeaderHomeButton.tsx,HeaderBackButton.tsx등)도useRouter를 사용하여 네비게이션을 처리하고 있습니다.window.history.replaceState를 직접 사용하면 Next.js의 클라이언트 사이드 라우팅 기능(프리페칭, 스크롤 복원 등)이 제대로 작동하지 않을 수 있습니다.다음과 같이 수정하세요:
🤖 Prompt for AI Agents