nextjs/웹사이트 만들기
산정보 보기 웹사이트 만들기(3) - 각 산의 상세정보 페이지 만들기
lamarcK
2025. 4. 14. 18:11
앞서 실행한 홈페이지에서 지도 이동으로 지역을 누르면 아래와 같은 목록이 구현되도록 했다.
**연결 과정
//app/page.tsx
<MapComponent
states={states}
className="w-full h-auto"
/>
//MapComponent.tsx
router.push(`/provinces/${provinceName}`);
해당 부분을 통해 지도의 특정 지역을 클릭하면 해당 지역의 provinceName(예: '경기도', '서울시' 등)을 URL에 포함시켜
/provinces/경기도 또는 /provinces/서울시와 같은 경로로 이동시킨다.
**provinces/[PROVINCE]/page.tsx
import { getMountainsByState } from '@/lib/db'
import MountainCard from '@/components/MountainCard'
export default async function ProvincePage({
params
}: {
params: { PROVINCE: string }
}) {
const resolvedParams = await params
const decodedProvince = decodeURIComponent(resolvedParams.PROVINCE)
const mountains = await getMountainsByState(decodedProvince)
return (
<div className="space-y-6">
<h1 className="text-3xl font-bold">{decodedProvince} 산 목록</h1>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{mountains.map((mountain) => (
<MountainCard key={mountain.ID} mountain={mountain} />
))}
</div>
</div>
)
}
그러면 해당 페이지에서 2번째 db를 호출해서 필요한 정보를 간략하게 보여준다.
export async function getMountainsByState(province: string): Promise<Mountain[]> {
const db = await openDb();
try {
return await db.all(
'SELECT FMMNT_INFO_ID as ID, MNTN_NM as NAME, MNTN_INFO_POFLC as ADDRESS, PROVINCE FROM T WHERE PROVINCE IS NOT NULL AND PROVINCE = ?',
[province]
);
} catch (error) {
console.error('Error fetching T:', error);
return [];
}
}
'SELECT FMMNT_INFO_ID as ID, MNTN_NM as NAME, MNTN_INFO_POFLC as ADDRESS, PROVINCE FROM T WHERE PROVINCE IS NOT NULL AND PROVINCE = ?',
- SQL의 AS 구문을 사용해 데이터베이스 컬럼명을 더 사용하기 편한 변수명으로 매핑하고
- SELECT 부분을 통해 해당 정보를 가져와서 provinces/[PROVINCE]/page.tsx 페이지 구성에 사용한다.
****provinces/[PROVINCE]/page.tsx
{mountains.map((mountain) => (
<MountainCard key={mountain.ID} mountain={mountain} />
))}
프롭스로 <MountainCard>를 호출한다.
**MountainCard.tsx
import { Mountain } from '@/types'
import Link from 'next/link'
export default function MountainCard({ mountain }: { mountain: Mountain }) {
return (
<Link
href={`/mountains/${mountain.ID}`}
className="block bg-white rounded-lg shadow hover:shadow-lg transition-shadow overflow-hidden"
>
<div className="p-4 space-y-2">
<h2 className="text-xl font-semibold">{mountain.NAME}</h2>
<p className="text-gray-600 text-sm">{mountain.ADDRESS}</p>
</div>
</Link>
)
}
<Link
href={`/mountains/${mountain.ID}`}
className="block bg-white rounded-lg shadow hover:shadow-lg transition-shadow overflow-hidden"
>
2번째 DB에서 받은 정볼르 사용해서 링크의 주소를 /mountains/${mountain.ID} 식으로 설정하는데 해당 주소를 통해서 mountains/[ID]/page로 이동한다.
그러면 해당 페이지에서 3번째 db를 호출해서 상세 정보를 보여준다.
import { getMountainById } from '@/lib/db'
import Image from 'next/image'
import { notFound } from 'next/navigation'
type Props = {
params: { ID: string }
}
export default async function MountainPage({ params}: Props) {
const { ID } = await params // params를 await
const mountain = await getMountainById(ID) // ID 사용
if (!mountain) {
notFound()
}
if (mountain.IMAGE === `http://www.forest.go.kr/newkfsweb/cmm/fms/getImage.do?fileSn=1&atchFileId=`) {
mountain.IMAGE = '/default-mountain.jpeg';
}
return (
<div className="max-w-4xl mx-auto">
<h1 className="text-3xl font-bold mb-6">{mountain.NAME}</h1>
<div className="relative h-96 mb-6" style={{display:'flex', justifyContent:'center', }}>
<Image
src={mountain.IMAGE}
alt={mountain.NAME}
width={0}
height={0}
priority
sizes="200px"
style={{ height: '400px', objectFit: 'cover', width:'auto', maxWidth:'500px'
}}
className="rounded-lg object-cover"
/>
</div>
<div className="bg-white p-6 rounded-lg shadow-lg">
<h2 className="text-xl font-semibold mb-4">상세 정보</h2>
<div className="space-y-2">
<p><strong>높이:</strong> {mountain.HEIGHT}m</p>
<p><strong>위치:</strong> {mountain.PROVINCE}</p>
<p><strong>설명:</strong> {mountain.DESCRIPTION}</p>
</div>
</div>
</div>
)
}
타입스크립트의 무결성 때문인지 params를 await하지 않으면 오류 메시지가 뜬다.
**db.ts
export async function getMountainById(id: string): Promise<Mountain | null> {
const db = await openDb();
try {
return await db.get(
`SELECT
FMMNT_INFO_ID as ID,
MNTN_NM as NAME,
MNTN_INFO_POFLC as ADDRESS,
MNTN_HGHT as HEIGHT,
PROVINCE,
DTL_INFO_CONT as DESCRIPTION,
MNTN_INFO_IMAGE_URL as IMAGE
FROM T
WHERE FMMNT_INFO_ID = ?`,
[id]
);
} catch (error) {
console.error('Error fetching T:', error);
return null;
}
}