동적 라우팅은 현대 웹 애플리케이션에서 사용자 경험을 개선하고, 유지보수성을 높이는 데 자주 사용되는 기술 중 하나이다. 특히 Next.js에서는 파일 기반 라우팅 (App Router)을 제공하여 보다 쉽게 동적 라우팅을 구현할 수 있다.
URL 쿼리와 상태 관리를 결합하여 SEO 최적화 및 직관적인 URL 구조를 유지하면서 동적 라우팅 구현 과정에 대해서 작성하고자 한다.
동적 라우팅은 URL의 일부를 동적으로 변경하여 다양한 페이지를 렌더링하는 기술이다.
특정 포스트의 ID에 따라 URL을 다르게 설정하고 해당 데이터를 렌더링함으로써 보다 유연한 페이지 구성이 가능하고, SEO 최적화에도 도움이 되는 기술이라고 할 수 있다.
useSearchParams
를 활용한 동적 라우팅Next.js 13버전 이후부터는 App Router 구조를 통해 파일 기반 라우팅과 URL 쿼리 관리를 효율적으로 제공한다.
next/navigation에서 제공하는 useSearchParams
와 useRouter
훅을 사용하여 URL의 상태를 읽거나 변경하는 작업을 보다 간단하게 처리가 가능하다.
useSearchParams는 URL의 쿼리 파라미터를 읽을 수 있도록 도와주는 훅이다.
const searchParams = useSearchParams();
const queryCategory = searchParams.get('name'); // URL에서 'name' 파라미터를 읽는다.
React 컴포넌트 내에서 상태 관리 시, URL의 쿼리 파라미터를 동적으로 반영하고 싶을 때 유용하다.
현재 URL에서 특정 쿼리 값을 읽을 수 있고, URL이 변경되면 쿼리 값을 실시간으로 반영한다는 특징이 있다.
useRouter는 URL의 이동을 제어하는 훅으로, 동적 라우팅에서 필수적인 역할을 한다.
router.replace(`/service?name=${encodeURIComponent(selectedButtonName)}`, {scroll: false});
push
와 replace
메서드를 활용하여 사용자가 특정 동작을 트리거 하였을 때, URL을 동적으로 업데이트할 수 있는 기능을 제공한다.
router.push
: 새 URL로 이동하며 브라우저 기록에 추가된다.
router.replace
: 현재 URL을 새 URL로 교체하며 브라우저 기록에 추가되지 않는다.
'use client';
import React, { useEffect, useState } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { useDispatch } from 'react-redux';
import {
setServiceHospitalCategory,
setServiceMainCategory,
} from '@/store/slice/serviceCategorySlice';
import {
ServiceSectionButtonName,
ServiceHospitalCategorySchema,
} from '@/lib/ServiceSectionContentMapZodSchema';
const ServiceSectionOrganism = () => {
const router = useRouter();
const searchParams = useSearchParams();
const dispatch = useDispatch();
const [selectedButtonName, setSelectedButtonName] = useState('');
const [selectedHospitalButtonName, setSelectedHospitalButtonName] = useState(
'의료 관광 제휴 병원 모집'
);
useEffect(() => {
const queryCategory = searchParams.get('name');
if (queryCategory) {
dispatch(setServiceMainCategory(queryCategory));
}
}, [searchParams, dispatch]);
const handleClickButton = (buttonName: string) => {
const newSelectedButtonName =
selectedButtonName === buttonName ? '' : buttonName;
setSelectedButtonName(newSelectedButtonName);
dispatch(setServiceMainCategory(buttonName));
router.replace(`/service?name=${encodeURIComponent(newSelectedButtonName)}`, {
scroll: false,
});
};
const handleClickHospitalButton = (buttonName: string) => {
const newSelectedButtonName =
selectedHospitalButtonName === buttonName ? '' : buttonName;
setSelectedHospitalButtonName(newSelectedButtonName);
dispatch(setServiceHospitalCategory(buttonName));
router.replace(
`/service?name=병원 제휴&subName=${encodeURIComponent(newSelectedButtonName)}`,
{ scroll: false }
);
};
return (
<div>
{/* 생략 */}
</div>
);
};
export default ServiceSectionOrganism;