icon
17장 : 실전 프로젝트

프로젝트 기획 및 설계

이 장에서는 앞선 챕터들에서 학습한 Next.js의 핵심 개념과 고급 주제들을 종합하여 실제 웹 애플리케이션을 기획하고 설계하는 과정을 다룹니다. 이론적인 지식을 넘어서, 실제 프로젝트를 어떻게 시작하고 구조화하며, 어떤 기술 스택을 선택하고, 기능을 단계적으로 구현해 나갈지 구체적인 가이드를 제공합니다.

이번 절에서는 "프로젝트 기획 및 설계"에 초점을 맞춰, 아이디어 구체화부터 요구사항 정의, 기술 스택 선정, 아키텍처 설계, 그리고 데이터 모델링까지의 과정을 다루겠습니다.


프로젝트 아이디어 구체화 및 목표 설정

모든 성공적인 프로젝트의 시작은 명확한 아이디어와 목표에서 비롯됩니다. 어떤 종류의 애플리케이션을 만들고 싶은지, 누가 사용할 것인지, 어떤 문제를 해결할 것인지 등을 명확히 합니다.

아이디어 도출 및 선정

  • 관심 분야 탐색: 자신이 관심 있거나 잘 아는 분야에서 아이디어를 찾습니다. (예: 독서 기록 앱, 재료 관리 앱, 스터디 그룹 매칭 서비스, 간단한 이커머스 스토어)
  • 문제점 인식: 일상생활이나 업무에서 불편함을 느꼈던 점, 개선이 필요하다고 생각하는 점을 찾아봅니다.
  • 기존 서비스 분석: 유사한 기존 서비스가 있다면, 그들의 장단점을 분석하고 차별화될 수 있는 요소를 모색합니다.
  • 실현 가능성 고려: 주어진 시간과 기술 역량 내에서 구현 가능한 아이디어를 선정합니다. 너무 거창한 아이디어보다는 작고 핵심적인 기능부터 시작하는 것이 좋습니다.

예시 프로젝트 아이디어: 간단한 "온라인 북스토어 (Online Bookstore)"

  • 문제 정의: 사용자들이 쉽게 책을 검색하고, 상세 정보를 확인하며, 장바구니에 담아 주문할 수 있는 간단한 플랫폼이 필요하다.
  • 대상 사용자: 책 구매에 관심 있는 일반 사용자.
  • 핵심 가치: 사용자 친화적인 인터페이스, 빠른 검색, 간편한 구매 프로세스.

프로젝트 목표 설정 (SMART 원칙)

선정된 아이디어를 바탕으로 구체적인 목표를 설정합니다. SMART 원칙(Specific, Measurable, Achievable, Relevant, Time-bound)을 적용하면 좋습니다.

  • S (Specific): 사용자가 책을 검색하고, 상세 페이지를 보고, 장바구니에 담아 가상으로 주문할 수 있는 웹 애플리케이션 개발.
  • M (Measurable): 최소 50권의 책 데이터 구축, 검색 기능 응답 시간 1초 이내, 장바구니 및 주문 프로세스 구현.
  • A (Achievable): Next.js와 기본적인 웹 기술 스택(MongoDB/PostgreSQL, Express/Next.js API Routes)만으로 구현 가능.
  • R (Relevant): Next.js 학습 내용을 총체적으로 적용하며, 실제 서비스 개발 경험 습득에 기여.
  • T (Time-bound): 4주 이내에 핵심 기능 개발 및 배포 완료.

요구사항 정의 및 기능 목록 작성

프로젝트의 목표를 달성하기 위해 필요한 기능들을 구체적으로 정의합니다. 사용자 관점에서 어떤 기능을 제공해야 하는지 상세하게 나열합니다.

핵심 기능 (MVP)

최소 기능 제품(MVP - Minimum Viable Product)은 프로젝트의 핵심 가치를 전달할 수 있는 최소한의 기능 집합입니다. MVP를 먼저 개발하여 빠르게 피드백을 받고 점진적으로 기능을 확장합니다.

  • 사용자 인증 (선택 사항): 회원가입, 로그인, 로그아웃 (간단한 북스토어라면 초기 MVP에서는 제외 가능)
  • 도서 목록 조회: 모든 도서 목록을 페이지네이션과 함께 표시.
  • 도서 검색: 도서 제목, 저자 등으로 검색.
  • 도서 상세 정보: 특정 도서의 상세 정보(제목, 저자, 설명, 가격, 이미지 등) 표시.
  • 장바구니 기능: 도서 추가, 수량 변경, 도서 삭제.
  • 주문 기능 (가상): 장바구니의 도서를 가상으로 주문 (결제 시스템 연동은 MVP에서 제외).

추가 기능 (향후 확장 고려)

MVP 이후 확장할 수 있는 기능들을 미리 구상해 봅니다.

  • 사용자 리뷰 및 평점 시스템.
  • 위시리스트 기능.
  • 추천 도서 기능 (개인화).
  • 관리자 페이지 (도서 추가/수정/삭제, 주문 관리).
  • 실제 결제 시스템 연동 (Stripe, Toss Payments 등).
  • 국제화 (i18n) 지원.
  • 푸시 알림.

기술 스택 선정

Next.js를 기반으로 하지만, 백엔드, 데이터베이스, 스타일링, 배포 등 전반적인 기술 스택을 결정합니다.

프론트엔드 (Next.js 기반)

  • 프레임워크: Next.js (React 기반)
    • 렌더링: SSR (도서 상세 페이지, 검색 결과), SSG (정적인 정보 페이지), 클라이언트 컴포넌트 (장바구니, 주문, 검색 입력) 적절히 활용.
    • 데이터 페칭: fetch API, SWR 또는 React Query.
  • 타입스크립트: 안정성과 개발 생산성 향상을 위해 TypeScript 사용.
  • 스타일링:
    • CSS Modules: 컴포넌트별 스코프 CSS.
    • Tailwind CSS: 유틸리티 우선 CSS 프레임워크 (빠른 UI 개발).
    • Styled-components / Emotion (선택 사항): 컴포넌트 기반 스타일링. (이 프로젝트에서는 Tailwind CSS를 주력으로 사용)
  • 폼 관리: React Hook Form (폼 유효성 검사 및 상태 관리).

백엔드 및 API

  • API 구현 방식: Next.js API Routes 또는 Server Actions.
    • 간단한 애플리케이션이므로 별도의 백엔드 서버 없이 Next.js 자체 API 기능을 활용.
  • 인증: NextAuth.js (소셜 로그인 및 세션 관리) - 선택 사항, MVP에서는 제외 가능.
  • 데이터베이스: NoSQL 또는 RDB
    • MongoDB (NoSQL): 유연한 스키마, 빠른 개발 (Mongoose ODM).
    • PostgreSQL (RDB): 관계형 데이터, 안정성 (Prisma ORM 또는 raw SQL).
    • (예시 프로젝트에서는 MongoDB와 Mongoose를 선택)

배포

  • 호스팅: Vercel (Next.js에 최적화된 배포 플랫폼).
  • 데이터베이스 호스팅: MongoDB Atlas (클라우드 MongoDB 서비스).

아키텍처 설계

애플리케이션의 전체적인 구조와 데이터 흐름을 시각화합니다.

시스템 아키텍처 다이어그램

graph TD
    A[User Browser] -- HTTP/HTTPS Request --> B(Vercel CDN / Edge)
    B -- Cache Miss / Dynamic Request --> C(Next.js App on Vercel)
    C -- API Routes / Server Actions --> D(Vercel Serverless Function)
    D -- Database Query --> E(MongoDB Atlas)
    E -- Database Response --> D
    D -- API Response --> C
    C -- HTML / JSON --> B
    B -- Response --> A

설명

  • User Browser: 사용자가 웹 애플리케이션에 접근하는 클라이언트.
  • Vercel CDN / Edge: 사용자의 요청을 가장 가까운 엣지 로케이션에서 처리하여 빠른 응답을 제공. 정적 자산(JS, CSS, 이미지) 캐싱 및 동적 요청 라우팅.
  • Next.js App on Vercel: Next.js 애플리케이션의 프론트엔드 로직 (React 컴포넌트) 및 SSR/SSG 페이지.
  • Vercel Serverless Function: Next.js의 API Routes 또는 Server Actions로 구현된 백엔드 로직. 필요할 때만 실행되는 서버리스 함수 형태.
  • MongoDB Atlas: 클라우드 기반 MongoDB 데이터베이스. 도서 정보, 장바구니 데이터, 주문 정보 등을 저장.

폴더/파일 구조 설계

Next.js 13의 App Router 기반으로 프로젝트 구조를 설계합니다.

your-bookstore/
├── app/                  # App Router의 루트 (Next.js 13+ 앱 라우팅)
│   ├── layout.tsx        # 전역 레이아웃
│   ├── page.tsx          # 홈 페이지
│   ├── loading.tsx       # 전역 로딩 UI
│   ├── error.tsx         # 전역 에러 UI
│   ├── not-found.tsx     # 404 페이지
│   ├── api/              # API Routes (서버리스 함수)
│   │   ├── books/        # 도서 관련 API
│   │   │   ├── route.ts  # GET (목록), POST (생성)
│   │   │   └── [id]/route.ts # GET (상세), PUT (수정), DELETE (삭제)
│   │   └── cart/route.ts # 장바구니 API
│   ├── books/            # 도서 관련 페이지
│   │   ├── page.tsx      # 도서 목록 페이지
│   │   └── [id]/page.tsx # 도서 상세 페이지 (동적 라우팅)
│   ├── cart/page.tsx     # 장바구니 페이지
│   └── order/page.tsx    # 주문 페이지
├── components/           # 재사용 가능한 UI 컴포넌트
│   ├── ui/               # Tailwind CSS 기반의 일반 UI 컴포넌트
│   │   ├── Button.tsx
│   │   └── Input.tsx
│   ├── BookCard.tsx
│   ├── Header.tsx
│   ├── Footer.tsx
│   ├── CartItem.tsx
│   └── ...
├── lib/                  # 유틸리티 함수, 헬퍼, 설정 등
│   ├── db.ts             # 데이터베이스 연결 및 모델 정의
│   ├── utils.ts          # 일반 유틸리티 함수
│   └── constants.ts      # 상수 정의
├── public/               # 정적 파일 (이미지, 폰트, 매니페스트 등)
│   ├── images/
│   ├── icons/
│   ├── favicon.ico
│   └── manifest.json
├── styles/               # 전역 스타일 (Tailwind CSS 설정 등)
│   └── globals.css
├── __tests__/            # 테스트 파일
│   ├── unit/
│   ├── components/
│   └── e2e/
├── prisma/               # Prisma ORM 사용 시 스키마 파일
├── .env.local            # 로컬 환경 변수
├── next.config.js        # Next.js 설정
├── tailwind.config.ts    # Tailwind CSS 설정
├── tsconfig.json         # TypeScript 설정
└── package.json          # 프로젝트 의존성 및 스크립트

데이터 모델링

애플리케이션이 다룰 데이터의 구조를 정의합니다. NoSQL (MongoDB)을 기준으로 예시를 들어보겠습니다.

주요 엔티티 식별

  • Book: 도서 정보.
  • User: 사용자 정보 (인증 기능 구현 시).
  • CartItem: 장바구니에 담긴 도서 항목.
  • Order: 주문 정보.

스키마 정의 (MongoDB/Mongoose 예시)

lib/db.ts 또는 models/ 디렉토리 내에 정의

lib/db.ts
// lib/db.ts 또는 models/Book.ts
import mongoose, { Schema, Document } from 'mongoose';

// Book 스키마 정의
export interface IBook extends Document {
  title: string;
  author: string;
  description: string;
  price: number;
  imageUrl: string;
  isbn: string;
  publishedDate: Date;
  genre: string[];
  stock: number;
}

const BookSchema: Schema = new Schema({
  title: { type: String, required: true },
  author: { type: String, required: true },
  description: { type: String, required: true },
  price: { type: Number, required: true },
  imageUrl: { type: String, required: true },
  isbn: { type: String, required: true, unique: true },
  publishedDate: { type: Date, default: Date.now },
  genre: [{ type: String }],
  stock: { type: Number, default: 0 },
});

// 이미 정의된 모델이 없으면 생성
const Book = mongoose.models.Book || mongoose.model<IBook>('Book', BookSchema);

// CartItem 스키마 정의 (사용자 인증 후 User 모델과 연결)
export interface ICartItem extends Document {
  userId: mongoose.Types.ObjectId; // User 모델의 ID
  bookId: mongoose.Types.ObjectId; // Book 모델의 ID
  quantity: number;
  addedAt: Date;
}

const CartItemSchema: Schema = new Schema({
  userId: { type: Schema.Types.ObjectId, ref: 'User', required: true },
  bookId: { type: Schema.Types.ObjectId, ref: 'Book', required: true },
  quantity: { type: Number, required: true, min: 1 },
  addedAt: { type: Date, default: Date.now },
});

const CartItem = mongoose.models.CartItem || mongoose.model<ICartItem>('CartItem', CartItemSchema);

// Order 스키마 정의
export interface IOrder extends Document {
  userId: mongoose.Types.ObjectId; // User 모델의 ID
  items: Array<{
    bookId: mongoose.Types.ObjectId;
    quantity: number;
    priceAtPurchase: number; // 구매 시점의 가격
  }>;
  totalPrice: number;
  orderDate: Date;
  status: 'pending' | 'completed' | 'cancelled';
}

const OrderSchema: Schema = new Schema({
  userId: { type: Schema.Types.ObjectId, ref: 'User', required: true },
  items: [
    {
      bookId: { type: Schema.Types.ObjectId, ref: 'Book', required: true },
      quantity: { type: Number, required: true },
      priceAtPurchase: { type: Number, required: true },
    },
  ],
  totalPrice: { type: Number, required: true },
  orderDate: { type: Date, default: Date.now },
  status: { type: String, enum: ['pending', 'completed', 'cancelled'], default: 'pending' },
});

const Order = mongoose.models.Order || mongoose.model<IOrder>('Order', OrderSchema);

// 데이터베이스 연결 함수
let cachedDb = null;
export async function connectToDatabase() {
  if (cachedDb) {
    return cachedDb;
  }
  const MONGODB_URI = process.env.MONGODB_URI;
  if (!MONGODB_URI) {
    throw new Error('Please define the MONGODB_URI environment variable inside .env.local');
  }
  const conn = await mongoose.connect(MONGODB_URI, {
    bufferCommands: false, // Node.js 드라이버의 기본 버퍼링을 비활성화
  });
  cachedDb = conn;
  return conn;
}

export { Book, CartItem, Order };

프로젝트 기획 및 설계 단계는 실제 개발의 기초를 다지는 가장 중요한 과정입니다. 이 단계에서 충분한 시간을 투자하여 명확한 목표, 구체적인 기능, 적절한 기술 스택, 그리고 견고한 아키텍처를 정의한다면, 이후 개발 과정의 효율성과 프로젝트의 성공 가능성을 크게 높일 수 있을 것입니다. 다음 절에서는 이 설계를 바탕으로 실제 프로젝트 환경을 설정하고 개발을 시작하는 방법을 다루겠습니다.