안녕하세요. 오늘은 React 기반 프레임워크인 Next.js를 사용해 첫 번째 웹 애플리케이션을 구축하는 방법을 공유하려고 합니다. 웹 개발에 관심이 있지만 시작을 어떻게 해야 할지 모르는 분들을 위해 기초부터 천천히 설명할 예정입니다. 그럼 시작해 볼까요?

목차
Next.js의 기본 개념과 장점
Next.js는 React 기반의 프레임워크로, 특히 서버 사이드 렌더링(SSR)과 정적 사이트 생성(SSG)을 지원하는 강력한 도구입니다. 그럼 "또 하나의 자바스크립트 프레임워크가 필요한가?" 라고 생각할 수도 있습니다. 하지만 대답은 "그렇다"입니다. Next.js가 제공하는 장점을 살펴보면 왜 그런지 이해하실 수 있을 겁니다.
단순한 React 애플리케이션은 클라이언트 사이드 렌더링(CSR)을 사용하는데, 이는 초기 로딩 속도가 느리고 SEO에 불리합니다. Next.js는 이런 문제를 해결하면서도 React의 장점을 그대로 유지합니다. 간단히 말해서, Next.js는 개발자 경험과 최종 사용자 경험 모두를 개선합니다.
여기서 Next.js의 주요 이점을 간략하게 정리해 보겠습니다.
- 서버 사이드 렌더링(SSR): 페이지가 서버에서 렌더링되어 클라이언트로 전송되므로 초기 로딩 속도가 빠릅니다.
- 정적 사이트 생성(SSG): 빌드 시 HTML을 생성하여 CDN에서 제공할 수 있어 성능이 우수합니다.
- 파일 기반 라우팅: 폴더 구조가 곧 URL 구조가 되므로 복잡한 라우팅 설정이 필요 없습니다.
- API 라우트: 백엔드 API를 동일한 Next.js 프로젝트 내에서 구현할 수 있습니다.
- 자동 코드 스플리팅: 필요한 JavaScript만 각 페이지에 로드됩니다.
개발 환경 설정
Next.js 프로젝트를 시작하기 전에 필요한 개발 환경을 설정해야 합니다. 다행히 그렇게 복잡하지 않습니다. 기본적으로 Node.js와 npm(또는 yarn, pnpm)만 설치되어 있으면 됩니다.
필수 도구 | 최소 버전 | 설치 방법 |
---|---|---|
Node.js | 16.8.0 이상 | Node.js 공식 사이트에서 다운로드 |
npm | 8.0.0 이상 | Node.js와 함께 자동 설치됨 |
코드 에디터 | - | VS Code 권장 |
Node.js가 올바르게 설치되었는지 확인하려면 터미널(명령 프롬프트)에서 다음 명령어를 실행해 보세요.
node -v
npm -v
각 명령어는 설치된 Node.js와 npm의 버전을 출력합니다. 이제 개발 환경이 준비되었으니, Next.js 프로젝트를 생성해 봅시다.
Next.js 프로젝트 생성하기
Next.js 프로젝트를 생성하는 방법은 매우 간단합니다. 터미널을 열고 다음 명령어를 실행하면 됩니다. 이 명령어는 `create-next-app`이라는 도구를 사용하여 Next.js 프로젝트의 기본 구조를 자동으로 생성합니다.
-
프로젝트를 생성할 디렉토리로 이동합니다.
cd Documents mkdir web-projects cd web-projects
-
다음 명령어로 새 Next.js 프로젝트를 생성합니다.
npx create-next-app@latest hello-nextjs # 또는 yarn을 사용하는 경우 # yarn create next-app hello-nextjs
-
생성 과정에서 몇 가지 질문에 답해야 합니다. 초보자라면 대부분 기본값을 사용해도 좋습니다.
? Would you like to use TypeScript? › No / Yes ? Would you like to use ESLint? › No / Yes ? Would you like to use Tailwind CSS? › No / Yes ? Would you like to use 'src/' directory? › No / Yes ? Would you like to use App Router? › No / Yes ? Would you like to customize the default import alias? › No / Yes
참고: 초보자라면 App Router는 'Yes'로, 나머지는 기본값을 사용하는 것이 좋습니다.
-
생성된 프로젝트 디렉토리로 이동합니다.
cd hello-nextjs
위 과정이 모두 완료되면, Next.js 프로젝트가 준비된 것입니다. 생성된 프로젝트는 이미 개발 서버를 실행할 수 있는 상태입니다. 다음 명령어로 개발 서버를 시작해 볼까요?
npm run dev
# 또는
# yarn dev
이제 브라우저에서 http://localhost:3000
에 접속하면 Next.js의 기본 페이지를 볼 수 있습니다. 축하합니다! 이제 Next.js 애플리케이션을 실행하고 있습니다.
npm, yarn, pnpm 중 어떤 패키지 매니저를 사용해야 할지 고민된다면, 팀 프로젝트에서는 팀의 표준을 따르고, 개인 프로젝트에서는 아래 특징을 고려해 선택하세요:
- npm: Node.js 기본 패키지 매니저로, 별도 설치 필요 없음
- yarn: Facebook에서 개발한 패키지 매니저로, 병렬 설치로 속도가 빠름
- pnpm: 디스크 공간을 효율적으로 사용하며, 여러 프로젝트에서 동일한 의존성을 공유
초보자라면 npm으로 시작해도 좋지만, 나중에 더 큰 프로젝트를 진행할 때는 pnpm의 이점을 고려해 볼 만합니다.
Hello World 페이지 만들기
이제 기본 Next.js 프로젝트가 준비되었으니, 우리만의 "Hello World" 페이지를 만들어 봅시다. Next.js 13부터는 App Router 방식을 권장하므로, 이 방식을 사용해 구현하겠습니다.
프로젝트 디렉토리에서 기본으로 생성된 app/page.js
파일을 열어 다음과 같이 내용을 변경해 보겠습니다:
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24">
<div className="text-center">
<h1 className="text-4xl font-bold mb-4">Hello, Next.js World!</h1>
<p className="text-xl">Welcome to my first Next.js application</p>
</div>
</main>
);
}
이제 개발 서버가 실행 중이라면 (그렇지 않다면 npm run dev
로 시작), 브라우저에서 페이지를 새로고침하면 변경 사항이 바로 반영됩니다. Next.js는 Hot Module Replacement(HMR)를 지원하기 때문에 파일을 저장하면 페이지가 자동으로 새로고침됩니다.
위 코드는 매우 간단합니다. Home
함수 컴포넌트를 정의하고, JSX를 반환합니다. 이 JSX에는 main
요소 내에 헤딩과 문단이 포함되어 있습니다. className
속성을 사용해 Tailwind CSS 클래스를 적용했습니다(create-next-app에서 Tailwind CSS를 선택했다면).
더 복잡한 Hello World 페이지를 만들고 싶다면, 약간의 스타일과 상호작용을 추가해 보겠습니다:
'use client';
import { useState } from 'react';
export default function Home() {
const [count, setCount] = useState(0);
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24">
<div className="text-center">
<h1 className="text-4xl font-bold mb-4">Hello, Next.js World!</h1>
<p className="text-xl mb-8">Welcome to my first Next.js application</p>
<div className="mb-8">
<p className="text-lg mb-2">You clicked the button {count} times</p>
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onClick={() => setCount(count + 1)}
>
Click me!
</button>
</div>
<p className="text-gray-600 text-sm">Built with Next.js</p>
</div>
</main>
);
}
위 코드에서 몇 가지 중요한 변경사항을 주목하세요:
-
'use client';
지시문을 파일 최상단에 추가했습니다. 이는 이 컴포넌트가 클라이언트 컴포넌트임을 Next.js에 알려줍니다. 클라이언트 컴포넌트는 브라우저에서 실행되며 인터랙티브한 기능을 가질 수 있습니다. -
useState
훅을 React에서 import하고, 클릭 횟수를 추적하는 상태 변수를 만들었습니다. -
버튼을 추가하고
onClick
이벤트 핸들러를 연결했습니다. 버튼을 클릭할 때마다 카운터가 증가합니다.
이 코드는 React의 기본적인 상태 관리 기능을 보여줍니다. 이제 버튼을 클릭할 때마다 카운터가 증가하는 인터랙티브한 Hello World 페이지가 완성되었습니다!
Next.js 애플리케이션 구조 이해하기
Next.js 프로젝트의 구조를 이해하는 것은 앱을 확장하고 새로운 기능을 추가하는 데 중요합니다. Next.js 13부터 새로운 App Router 방식이 도입되었고, 이는 기존의 Pages Router와는 다른 구조를 가집니다. 여기서는 App Router 기반 프로젝트의 주요 구성 요소를 살펴보겠습니다.
파일/디렉토리 | 설명 |
---|---|
app/ |
App Router 기반의 라우팅 및 UI 컴포넌트를 포함하는 메인 디렉토리입니다. |
app/page.js |
메인 페이지(/)를 정의하는 파일입니다. 우리가 수정한 Hello World 페이지가 여기에 있습니다. |
app/layout.js |
모든 페이지에 공통으로 적용되는 레이아웃을 정의합니다. HTML, body 태그, 메타 데이터 등이 포함됩니다. |
app/globals.css |
전역 CSS 스타일을 정의합니다. Tailwind CSS 기본 스타일도 여기서 가져옵니다. |
public/ |
정적 파일(이미지, 폰트 등)을 저장하는 디렉토리입니다. |
next.config.js |
Next.js 설정 파일로, 환경 변수, 빌드 설정 등을 정의합니다. |
package.json |
프로젝트 의존성과 스크립트를 정의합니다. |
App Router 구조에서 중요한 개념 중 하나는 파일 기반 라우팅입니다. 디렉토리 구조가 URL 경로를 결정합니다. 예를 들어, 새로운 페이지 /about
을 추가하려면 app/about/page.js
파일을 생성하면 됩니다.
애플리케이션 배포하기
Next.js 애플리케이션을 개발한 후에는 실제 사용자가 접근할 수 있도록 배포해야 합니다. Next.js 애플리케이션을 배포하는 여러 방법이 있지만, 가장 쉽고 권장되는 방법은 Vercel을 사용하는 것입니다. Vercel은 Next.js를 개발한 회사이며, Next.js 애플리케이션에 최적화된 호스팅 서비스를 제공합니다.
다음은 Vercel을 사용하여 Next.js 애플리케이션을 배포하는 단계입니다:
-
우선 GitHub, GitLab, Bitbucket 등의 Git 저장소에 프로젝트를 푸시합니다.
git init git add . git commit -m "Initial commit" git remote add origin https://github.com/username/hello-nextjs.git git push -u origin main
- 에 가입하고 로그인합니다.
- Vercel 대시보드에서 "New Project" 버튼을 클릭합니다.
- Git 저장소를 선택하고 프로젝트를 import 합니다.
- 프로젝트 설정을 검토하고 필요한 경우 환경 변수를 추가합니다.
- "Deploy" 버튼을 클릭하여 배포를 시작합니다.
배포가 완료되면 Vercel은 프로젝트를 위한 URL을 제공합니다(예: https://hello-nextjs-username.vercel.app
). 이 URL을 통해 누구나 애플리케이션에 접근할 수 있습니다.
Vercel 외에도 다른 호스팅 옵션도 있습니다:
- Netlify: Vercel과 유사한 배포 경험을 제공합니다.
- AWS Amplify: AWS 생태계 내에서 서버리스 배포를 제공합니다.
- GitHub Pages: 정적 내보내기를 사용한 배포가 가능합니다.
- 자체 서버: Docker 컨테이너 또는 Node.js 서버로 직접 호스팅할 수 있습니다.
프로젝트를 배포한 후 자신만의 커스텀 도메인을 연결하고 싶다면 다음 단계를 따르세요:
- 도메인 등록 사이트(Namecheap, GoDaddy 등)에서 도메인을 구매합니다.
- Vercel 대시보드에서 프로젝트 설정으로 이동합니다.
- "Domains" 탭에서 "Add" 버튼을 클릭합니다.
- 구매한 도메인을 입력하고 안내에 따라 DNS 설정을 구성합니다.
- DNS가 전파되면(보통 24-48시간 소요) 새 도메인이 프로젝트에 연결됩니다.
Vercel이 제공하는 SSL 인증서를 통해 자동으로 HTTPS가 활성화되어 보안이 강화됩니다.
Next.js 13에서 도입된 React 서버 컴포넌트(RSC)와 클라이언트 컴포넌트의 차이점을 이해하는 것이 중요합니다:
- 서버 컴포넌트(기본값): 서버에서 렌더링되고 클라이언트로 HTML을 전송합니다. 번들 크기가 작고 초기 로딩이 빠르지만, 상태나 이벤트 핸들러를 사용할 수 없습니다.
- 클라이언트 컴포넌트(`'use client'`): 브라우저에서 실행되며 React 상태, 효과, 이벤트 핸들러 등의 클라이언트 측 기능을 사용할 수 있습니다.
성능 최적화를 위해 가능한 많은 컴포넌트를 서버 컴포넌트로 유지하고, 상호작용이 필요한 컴포넌트만 클라이언트 컴포넌트로 만드는 것이 좋습니다.
확장 가능한 Next.js 애플리케이션 구조
실제 프로젝트에서는 단순한 Hello World 페이지를 넘어 더 복잡한 구조가 필요합니다. 다음은 확장 가능한 Next.js 애플리케이션을 위한 구조와 샘플 코드입니다.
// 파일 구조 예시
app/
├── components/ // 재사용 가능한 컴포넌트
│ ├── Button.js
│ ├── Header.js
│ └── Footer.js
├── lib/ // 유틸리티 함수, API 클라이언트 등
│ └── utils.js
├── styles/ // 컴포넌트별 CSS 모듈
│ └── header.module.css
├── about/ // /about 경로
│ └── page.js
├── blog/ // /blog 경로
│ ├── page.js
│ └── [slug]/ // 동적 경로: /blog/post-1, /blog/post-2 등
│ └── page.js
├── layout.js // 전역 레이아웃
├── page.js // 홈페이지 (/)
└── globals.css // 전역 스타일
위 구조를 기반으로 몇 가지 주요 파일의 예시 코드를 살펴보겠습니다:
1. app/components/Header.js
import Link from 'next/link';
export default function Header() {
return (
<header className="bg-gray-800 text-white p-4">
<div className="container mx-auto flex justify-between items-center">
<h1 className="text-xl font-bold">My Next.js App</h1>
<nav>
<ul className="flex space-x-4">
<li><Link href="/" className="hover:text-gray-300">Home</Link></li>
<li><Link href="/about" className="hover:text-gray-300">About</Link></li>
<li><Link href="/blog" className="hover:text-gray-300">Blog</Link></li>
</ul>
</nav>
</div>
</header>
);
}
2. app/layout.js
import './globals.css';
import Header from './components/Header';
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });
export const metadata = {
title: 'My Next.js Application',
description: 'Built with Next.js',
};
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>
<Header />
<main className="container mx-auto px-4 py-8">
{children}
</main>
<footer className="bg-gray-800 text-white p-4 text-center">
<p>© {new Date().getFullYear()} My Next.js App. All rights reserved.</p>
</footer>
</body>
</html>
);
}
3. app/about/page.js
export default function AboutPage() {
return (
<div className="max-w-3xl mx-auto">
<h1 className="text-3xl font-bold mb-6">About Us</h1>
<p className="text-lg mb-4">
Welcome to the About page of our Next.js application. This is a simple example of how to create a new page in Next.js.
</p>
<p className="text-lg mb-4">
Next.js makes it easy to create multi-page applications with its file-based routing system. Simply create a new directory in the app folder and add a page.js file.
</p>
</div>
);
}
4. app/blog/[slug]/page.js (동적 라우팅 예시)
// 이 함수는 빌드 시 정적으로 생성할 경로를 결정합니다.
export async function generateStaticParams() {
// 실제로는 API나 CMS에서 데이터를 가져올 수 있습니다.
const posts = [
{ slug: 'getting-started-with-nextjs' },
{ slug: 'why-i-love-react' },
{ slug: 'server-components-explained' }
];
return posts.map((post) => ({
slug: post.slug,
}));
}
// 페이지 컴포넌트
export default function BlogPost({ params }) {
const { slug } = params;
// 실제로는 slug를 사용하여 데이터를 가져올 수 있습니다.
return (
<div className="max-w-3xl mx-auto">
<h1 className="text-3xl font-bold mb-6">Blog Post: {slug}</h1>
<div className="prose lg:prose-xl">
<p>This is the content of the blog post with slug: {slug}.</p>
<p>In a real application, you would fetch the content based on the slug.</p>
</div>
</div>
);
}
5. app/lib/utils.js (유틸리티 함수 예시)
// 날짜 포맷 함수
export function formatDate(date) {
return new Date(date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
}
// URL 슬러그 생성 함수
export function createSlug(title) {
return title
.toLowerCase()
.replace(/[^\w\s-]/g, '') // 특수 문자 제거
.replace(/\s+/g, '-') // 공백을 하이픈으로 변환
.replace(/--+/g, '-'); // 중복 하이픈 제거
}
// 텍스트 자르기 함수 (개요용)
export function truncateText(text, maxLength = 150) {
if (text.length <= maxLength) return text;
return text.slice(0, maxLength) + '...';
}
Next.js와 API 통합하기
실제 애플리케이션에서는 외부 API나 데이터베이스와 통합해야 할 가능성이 높습니다. Next.js에서는 서버 컴포넌트를 사용하여 데이터를 가져오거나, API 라우트를 만들 수 있습니다. 다음은 서버 컴포넌트에서 데이터를 가져오는 예시
// app/users/page.js
// 서버 컴포넌트 (기본값)
export default async function UsersPage() {
// 서버 컴포넌트는 async/await를 직접 사용할 수 있습니다.
const res = await fetch('https://jsonplaceholder.typicode.com/users', {
// 캐시 옵션을 설정할 수 있습니다.
next: { revalidate: 3600 } // 1시간마다 데이터 재검증
});
const users = await res.json();
return (
<div className="max-w-3xl mx-auto">
<h1 className="text-3xl font-bold mb-6">Users</h1>
<ul className="divide-y divide-gray-200">
{users.map((user) => (
<li key={user.id} className="py-4">
<h2 className="text-xl font-semibold">{user.name}</h2>
<p className="text-gray-600">{user.email}</p>
<p className="text-gray-500">{user.company.name}</p>
</li>
))}
</ul>
</div>
);
}
API 라우트를 만들려면 app/api
디렉토리 내에 라우트 파일을 생성합니다:
// app/api/hello/route.js
export async function GET() {
return Response.json({
message: 'Hello, Next.js API!',
time: new Date().toISOString()
});
}
// POST 요청 처리 예시
export async function POST(request) {
const body = await request.json();
// 실제로는 데이터베이스에 저장하거나 다른 처리를 할 수 있습니다.
console.log('Received data:', body);
return Response.json({
success: true,
message: 'Data received',
receivedData: body
});
}
위의 API를 클라이언트 컴포넌트에서 호출하는 예시.
'use client';
import { useState, useEffect } from 'react';
export default function ApiExample() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// API 엔드포인트 호출
fetch('/api/hello')
.then((res) => res.json())
.then((data) => {
setData(data);
setLoading(false);
})
.catch((error) => {
console.error('Error fetching API:', error);
setLoading(false);
});
}, []);
return (
<div className="p-4 border rounded-lg shadow-sm">
<h2 className="text-xl font-semibold mb-4">API Response:</h2>
{loading ? (
<p>Loading...</p>
) : (
<pre className="bg-gray-100 p-4 rounded overflow-x-auto">
{JSON.stringify(data, null, 2)}
</pre>
)}
</div>
);
}
이제 Next.js를 사용하여 첫 번째 웹 애플리케이션을 성공적으로 구축했습니다. 우리는 기본 Hello World 페이지부터 시작하여 컴포넌트, 레이아웃, 페이지, API 라우트 등 Next.js의 주요 개념을 살펴보았습니다. 감사합니다.
'Developer > Web Frontend' 카테고리의 다른 글
[Next.js] 컴포넌트 재사용 (0) | 2025.04.04 |
---|---|
[Next.js] ESLint와 Prettier 설정하기 (0) | 2025.04.03 |
[Next.js] app.js와 _document.js의 역할과 차이점 (0) | 2025.04.02 |
[Next.js] 동적 라우팅 (0) | 2025.04.02 |
[Next.js] Next.js 서버 사이드 렌더링(SSR) (0) | 2025.04.01 |