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