[Next.js 시리즈 #4] 실전 Data Fetching: ‘프로젝트’ 페이지 데이터로 채워보기

안녕하세요! 지난 3부에서 완성한 멋진 디자인에 이제 실제 알맹이를 채워 넣을 시간입니다. 오늘은 우리 웹사이트의 ‘프로젝트(Projects)’ 메뉴를 클릭했을 때, 외부에서 데이터를 가져와 화면에 자동으로 뿌려주는 Data Fetching의 마법을 함께 부려보겠습니다.

데이터 페칭

1. 헤더 메뉴 연결하기 (app/layout.tsx)

데이터를 가져오기 전, 먼저 상단 메뉴를 클릭했을 때 해당 페이지로 이동할 수 있게 링크를 연결해 주어야 합니다. Next.js에서는 페이지 전체를 새로고침하지 않고 부드럽게 이동하기 위해 next/link 컴포넌트를 사용합니다.

[app/layout.tsx 수정하기]

import Link from 'next/link'; // Link 컴포넌트를 불러옵니다.
import './globals.css';

export const metadata = {
  title: '나만의 Next.js 웹사이트',
  description: '성장하는 개발자의 기록',
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="ko">
      <body className="flex flex-col min-h-screen">
        {/* 공통 헤더 섹션 */}
        <header className="p-4 bg-white border-b sticky top-0 z-50">
          <nav className="max-w-5xl mx-auto flex justify-between items-center">
            <Link href="/" className="text-xl font-bold text-blue-600">
              MyDevBlog
            </Link>
            <ul className="flex gap-4 text-sm font-medium text-gray-600">
              {/* href 속성에 폴더 이름을 넣어 페이지를 연결합니다 */}
              <li>
                <Link href="/" className="hover:text-blue-500">
                  홈
                </Link>
              </li>
              <li>
                <Link href="/about" className="hover:text-blue-500">
                  소개
                </Link>
              </li>
              <li>
                <Link href="/projects" className="hover:text-blue-500">
                  프로젝트
                </Link>
              </li>
            </ul>
          </nav>
        </header>

        {/* 페이지별 본문 내용 */}
        <main className="flex-grow max-w-5xl mx-auto w-full p-4">{children}</main>

        {/* 공통 푸터 섹션 */}
        <footer className="p-8 bg-gray-50 border-t mt-10">
          <div className="max-w-5xl mx-auto text-center text-gray-500 text-sm">
            © 2026 MyDevBlog. 함께 성장하는 개발자들을 위하여.
          </div>
        </footer>
      </body>
    </html>
  );
}

2. 프로젝트 리스트 페이지 만들기 (app/projects/page.tsx)

이제 실제 데이터를 불러올 차례입니다. app 폴더 아래에 projects 폴더를 만들고, 그 안에 page.tsx 파일을 생성하세요. 주니어 개발자분들이 흔히 겪는 any 타입 에러를 방지하기 위해 Interface를 추가하는 것도 잊지 마세요!

[app/projects/page.tsx 작성하기]

// 1. 데이터의 생김새를 정의합니다.
interface Project {
  id: number;
  title: string;
  body: string;
}

async function getProjects(): Promise<Project[]> {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=6');

  if (!res.ok) {
    throw new Error('데이터를 불러오지 못했습니다.');
  }

  // 가져온 데이터가 Project 배열 형태임을 알려줍니다.
  return res.json();
}

export default async function ProjectsPage() {
  const projects = await getProjects();

  return (
    <div className="py-10">
      <h2 className="text-3xl font-bold mb-8 text-blue-600">My Projects</h2>

      <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
        {/* 이제 project는 'any'가 아니라 'Project' 타입입니다! */}
        {projects.map((project: Project) => (
          <div key={project.id} className="p-6 border rounded-2xl bg-white">
            <h3 className="text-xl font-bold mb-2 capitalize">{project.title.slice(0, 20)}</h3>
            <p className="text-gray-500 text-sm">{project.body}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

3. [Level Up!] 예제 데이터를 넘어 실제 데이터 활용하기

방금 사용한 데이터는 학습용 예제입니다. 하지만 여러분의 블로그를 더 멋지게 만들고 싶다면 **영화 포스터 API(TMDB)**나 도서 정보 API 같은 실제 데이터를 연결해 보세요.

  • 왜 실제 API인가요? 영화 포스터처럼 시각적인 이미지가 포함된 데이터를 가져오면 웹사이트가 훨씬 더 전문적인 서비스처럼 보입니다.
  • 데이터 구조 확인 필수: API마다 데이터를 담아주는 바구니(JSON 구조)가 다릅니다. 어떤 곳은 results 안에, 어떤 곳은 data 안에 리스트를 넣어주니 꼭 console.log로 먼저 확인해 보세요!
  • 보안 주의: 실제 API를 쓸 때 필요한 API_KEY는 코드에 직접 적지 않고 .env라는 파일에 숨겨야 합니다. 이건 배포 편에서 자세히 다룰게요!

4. 주니어 개발자를 위한 포인트 레슨

  • Link vs <a> 태그: Next.js에서 일반 <a> 태그를 쓰면 페이지 전체가 새로고침되어 ‘Single Page Application(SPA)’의 장점이 사라집니다. 꼭 <Link>를 사용해 주세요.
  • 서버 컴포넌트의 위력: async/await를 컴포넌트 내부에서 바로 사용할 수 있는 이유는 이 컴포넌트가 Server Component이기 때문입니다. 덕분에 브라우저가 아닌 서버에서 데이터를 가져와 더 빠르게 화면을 보여줄 수 있습니다.

마치며

오늘 우리는 메뉴를 클릭하면 실제 데이터가 화면에 멋지게 정렬되는 과정을 구현했습니다. 이제 우리 웹사이트는 단순한 디자인을 넘어, 진짜 정보를 다루는 서비스의 형태를 갖추게 되었습니다.

그런데 전 세계 사람들에게 웹사이트를 공개하기 전, 꼭 필요한 과정이 하나 더 있죠? 바로 이 사이트를 만든 ‘여러분’을 소개하는 일입니다. 그래서 다음 시간에는 배포에 앞서 **[보너스 편: 나를 알리는 세련된 ‘소개(About)’ 페이지 만들기]**를 먼저 다뤄보려 합니다.

여러분의 온라인 이력서가 될 소개 페이지까지 완성한 뒤, 마지막 5부에서 대망의 배포를 함께 진행해 보겠습니다. 오늘도 한 걸음 성장하신 여러분을 진심으로 응원합니다!

감사합니다.


참고 자료

JSONPlaceholder 에 대한 공식 정보는 아래 자료를 참고하시면 도움이 됩니다.

 다른 글도 함께 읽어보세요