본문으로 건너뛰기
최서희Frontend Engineer
← 블로그

Frontend

[유데미 러닝크루] Next.js 스터디 4주차 – 파일 기반 라우팅 완벽 정리! 동적 경로 & 필터링까지

· 5분 읽기

📑 목차

1. 4주차 진행 범위

📝 세션 12 ~ 17 강의까지 진행

[Udemy 강의 소개]

이번 스터디에서 수강하는 강의는 【한글자막】 Next.js 15 & React - 완벽 가이드이다. 이 강의에서는 Next.js 15를 기초부터 학습하고, 앱 라우터 & 페이지 라우터를 활용하여 풀스택 앱을 구축하는 방법을 배울 수 있다.

💡 강의 정보

  • 강의명: 【한글자막】 Next.js 15 & React - 완벽 가이드
  • 강의 시간: 40시간
  • 수강생: 5,021명
  • 강의 평가: ⭐ 4.7/5 (585명 평가)
  • 언어: 한국어 자막 제공

📌 강의 주요 내용

✅ Next.js 15의 앱 라우터 & 페이지 라우터 ✅ 서버/클라이언트 컴포넌트 이해 ✅ 서버 액션 기반 데이터 처리 ✅ SSR·파일 기반 라우팅·인증 기능

2. 4주차 학습 내용 정리 — 파일 기반 라우팅, 동적 경로, 필터링

이번 주는 Pages Router 관점에서 라우트/동적경로/필터링을 구현.

1️⃣ 프로젝트 시작 및 라우팅 계획

  • 시작: Starting Project 다운로드 → 프로젝트 폴더에서 npm install
  • 주요 라우트 (총 4개)
    • / — 메인(주요 이벤트 목록)
    • /events — 모든 이벤트 목록
    • /events/[eventId] — 개별 이벤트 상세
    • /events/[...slug] — 연/월 필터링

2️⃣ Next.js 페이지 & 동적 라우팅 구현

✔ 홈 (/)

  • pages/index.js — getFeaturedEvents()로 주요 이벤트만 추려서 EventList로 렌더

✔ 모든 이벤트 (/events)

  • pages/events/index.js — getAllEvents()로 전체 목록, EventList 재사용

✔ 동적 상세 (/events/[eventId])

  • pages/events/[eventId].js — getEventById()로 상세 데이터
  • useRouter()로 eventId 추출
  • EventSummary, EventLogistics, EventContent 구성
// pages/events/[eventId].js
import { useRouter } from "next/router";
import { getEventById } from "../../helpers/api-util";
import EventSummary from "../../components/event-detail/event-summary";
import EventLogistics from "../../components/event-detail/event-logistics";
import EventContent from "../../components/event-detail/event-content";

export default function EventDetailPage(){
  const router = useRouter();
  const { eventId } = router.query;
  const event = getEventById(eventId);

  if (!event) return <p>Loading...</p>;

  return (
    <>
      <EventSummary title={event.title} />
      <EventLogistics date={event.date} address={event.location} image={event.image} imageAlt={event.title} />
      <EventContent><p>{event.description}</p></EventContent>
    </>
  );
}

✔ 다중 동적 경로 (/events/[...slug])

  • useRouter()로 slug에서 [year, month] 추출
  • getFilteredEvents({ year, month })로 목록 필터
  • 유효성 검사(숫자 아님/범위 벗어남) → 오류 메시지
// pages/events/[...slug].js
import { useRouter } from "next/router";
import { getFilteredEvents } from "../../helpers/api-util";
import EventList from "../../components/events/event-list";
import ErrorAlert from "../../components/ui/error-alert";

export default function FilteredEventsPage(){
  const router = useRouter();
  const slug = router.query.slug;

  if (!slug) return <p>Loading...</p>;

  const year = +slug[0];
  const month = +slug[1];

  if (isNaN(year) || isNaN(month) || year < 2020 || year > 2030 || month < 1 || month > 12) {
    return <ErrorAlert><p>Invalid filter. Please check values!</p></ErrorAlert>;
  }

  const events = getFilteredEvents({ year, month });

  if (!events || events.length === 0) {
    return <ErrorAlert><p>No events found!</p></ErrorAlert>;
  }

  return <EventList items={events} />;
}

3️⃣ 필터링 기능 (연/월 선택) & 프로그래밍 내비게이션

✔ 검색 폼 컴포넌트 — components/events/events-search.js

  • <select>로 연/월 선택, useRef로 값 읽기
  • useRouter().push()로 /events/2021/5 등으로 이동
// components/events/events-search.js
import { useRef } from "react";
import { useRouter } from "next/router";

export default function EventsSearch(){
  const router = useRouter();
  const yearRef = useRef();
  const monthRef = useRef();

  function submitHandler(e){
    e.preventDefault();
    const y = yearRef.current.value;
    const m = monthRef.current.value;
    router.push(`/events/${y}/${m}`);
  }

  return (
    <form onSubmit={submitHandler}>
      <select ref={yearRef} defaultValue="2023">
        <option value="2021">2021</option>
        <option value="2022">2022</option>
        <option value="2023">2023</option>
      </select>

      <select ref={monthRef} defaultValue="5">
        {Array.from({length:12}).map((_,i)=>(
          <option key={i+1} value={i+1}>{i+1}</option>
        ))}
      </select>

      <button>찾기</button>
    </form>
  );
}

4️⃣ 공통 컴포넌트 & 스타일링

  • 헤더/네비: components/layout/main-header.js → _app.js에서 전체 래핑
  • 버튼 UI: components/ui/button.js — Link를 감싼 재사용 버튼 구성
    • Next 13 미만: <Link href="..."><a>...</a></Link>
    • Next 13 이상: <Link>에 <a> 불필요
  • 에러 표시: components/ui/error-alert.js로 사용자 친화적 오류 메시지
// components/ui/button.js (예시)
import Link from "next/link";
export default function Button({ link, children }){
  if (link) return <Link className="btn" href={link}>{children}</Link>;
  return <button className="btn">{children}</button>;
}

🔥 최종 정리 — 기능 점검

  • ✅ 기본 페이지 동작 확인 (/, /events)
  • ✅ 상세 페이지 (/events/[eventId]) 정상
  • ✅ 연/월 필터 (/events/[...slug]) 정상
  • ✅ URL 직접 입력 (/events/2023/2 등) 동작
  • ✅ 모든 페이지에서 네비게이션 정상

팁 — 데이터가 비어 있으면 “No events found!” 메시지로 UX 보완.

📌 부록: 무료 아이콘 다운로드 사이트

이름특징
HeroiconsTailwind 팀의 무료 아이콘 · SVG/JSX 복사
Font Awesome가장 유명 · 무료/유료 제공
Material IconsGoogle Material · SVG/폰트
Iconmonstr심플/선명 · SVG/PNG
Flaticon방대한 컬렉션 · SVG/PNG/EPS (계정 필요)
The Noun Project디자이너 참여 대형 라이브러리 · 출처 표기
Remix Icon간결/일관성 높은 무료 아이콘 · SVG/JSX

댓글

GitHub 계정으로 댓글을 남길 수 있어요.