REST API 서버 구축 단계별 튜토리얼 – 완성까지 한번에!

REST API 서버 구축 단계별 튜토리얼 – 완성까지 한번에!

1. 프로젝트 소개 및 목표

REST API 서버 구축 단계별 튜토리얼은 Node.js와 Express를 활용하여 실무에서 사용할 수 있는 RESTful API 서버를 처음부터 끝까지 만드는 과정을 다룹니다. 데이터베이스 연동, 인증 시스템, CRUD 작업, 에러 핸들링, 그리고 배포까지 전체 개발 사이클을 경험할 수 있습니다. 이 튜토리얼을 완료하면 실제 프로덕션 환경에서 동작하는 API 서버를 구축할 수 있는 능력을 갖추게 됩니다. 백엔드 개발의 핵심 개념을 이해하고, 포트폴리오에 추가할 수 있는 실전 프로젝트를 완성하는 것이 목표입니다.

2. 필요한 기술 스택

이 프로젝트를 진행하기 위해 필요한 기술 스택은 다음과 같습니다:

  • Node.js (v18 이상): 서버 사이드 JavaScript 런타임
  • Express.js: 웹 애플리케이션 프레임워크
  • MongoDB & Mongoose: NoSQL 데이터베이스 및 ODM
  • JWT (jsonwebtoken): 사용자 인증 토큰 생성
  • bcrypt: 비밀번호 암호화
  • dotenv: 환경 변수 관리
  • express-validator: 입력 데이터 검증
  • Jest & Supertest: 테스트 프레임워크

3. 프로젝트 셋업

먼저 프로젝트 디렉토리를 생성하고 필요한 패키지를 설치합니다.

# 프로젝트 디렉토리 생성
mkdir rest-api-tutorial
cd rest-api-tutorial

# package.json 초기화
npm init -y

# 필수 패키지 설치
npm install express mongoose dotenv bcrypt jsonwebtoken cors helmet express-validator

# 개발 의존성 패키지 설치
npm install --save-dev nodemon jest supertest

프로젝트 구조를 다음과 같이 설정합니다:

rest-api-tutorial/
├── src/
│   ├── config/
│   │   └── database.js
│   ├── models/
│   │   └── User.js
│   ├── routes/
│   │   ├── auth.js
│   │   └── users.js
│   ├── middleware/
│   │   ├── auth.js
│   │   └── errorHandler.js
│   ├── controllers/
│   │   ├── authController.js
│   │   └── userController.js
│   └── app.js
├── tests/
├── .env
├── .gitignore
├── server.js
└── package.json

4. 단계별 구현 과정

Step 1: 데이터베이스 연결 설정

MongoDB 연결을 위한 설정 파일을 작성합니다.

// src/config/database.js
const mongoose = require('mongoose');

const connectDB = async () => {
  try {
    await mongoose.connect(process.env.MONGODB_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
    console.log('MongoDB 연결 성공');
  } catch (error) {
    console.error('MongoDB 연결 실패:', error.message);
    process.exit(1);
  }
};

module.exports = connectDB;

Step 2: User 모델 생성

사용자 정보를 저장할 Mongoose 스키마를 정의합니다.

// src/models/User.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');

const userSchema = new mongoose.Schema({
  username: {
    type: String,
    required: [true, '사용자명은 필수입니다'],
    unique: true,
    trim: true,
    minlength: 3
  },
  email: {
    type: String,
    required: [true, '이메일은 필수입니다'],
    unique: true,
    lowercase: true,
    match: [/^\S+@\S+\.\S+$/, '유효한 이메일을 입력해주세요']
  },
  password: {
    type: String,
    required: [true, '비밀번호는 필수입니다'],
    minlength: 6
  },
  createdAt: {
    type: Date,
    default: Date.now
  }
});

// 비밀번호 암호화 미들웨어
userSchema.pre('save', async function(next) {
  if (!this.isModified('password')) return next();
  
  const salt = await bcrypt.genSalt(10);
  this.password = await bcrypt.hash(this.password, salt);
  next();
});

// 비밀번호 검증 메서드
userSchema.methods.comparePassword = async function(candidatePassword) {
  return await bcrypt.compare(candidatePassword, this.password);
};

module.exports = mongoose.model('User', userSchema);

Step 3: 인증 미들웨어 구현

JWT 토큰을 검증하는 미들웨어를 작성합니다.

// src/middleware/auth.js
const jwt = require('jsonwebtoken');

const auth = async (req, res, next) => {
  try {
    const token = req.header('Authorization')?.replace('Bearer ', '');
    
    if (!token) {
      throw new Error();
    }

    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.userId = decoded.userId;
    next();
  } catch (error) {
    res.status(401).json({ error: '인증이 필요합니다' });
  }
};

module.exports = auth;

Step 4: 인증 컨트롤러 구현

회원가입과 로그인 로직을 처리하는 컨트롤러입니다.

// src/controllers/authController.js
const User = require('../models/User');
const jwt = require('jsonwebtoken');

exports.register = async (req, res) => {
  try {
    const { username, email, password } = req.body;

    // 중복 체크
    const existingUser = await User.findOne({ $or: [{ email }, { username }] });
    if (existingUser) {
      return res.status(400).json({ error: '이미 존재하는 사용자입니다' });
    }

    // 사용자 생성
    const user = new User({ username, email, password });
    await user.save();

    // JWT 토큰 생성
    const token = jwt.sign(
      { userId: user._id },
      process.env.JWT_SECRET,
      { expiresIn: '7d' }
    );

    res.status(201).json({
      message: '회원가입 성공',
      token,
      user: {
        id: user._id,
        username: user.username,
        email: user.email
      }
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
};

exports.login = async (req, res) => {
  try {
    const { email, password } = req.body;

    // 사용자 찾기
    const user = await User.findOne({ email });
    if (!user) {
      return res.status(401).json({ error: '이메일 또는 비밀번호가 올바르지 않습니다' });
    }

    // 비밀번호 검증
    const isMatch = await user.comparePassword(password);
    if (!isMatch) {
      return res.status(401).json({ error: '이메일 또는 비밀번호가 올바르지 않습니다' });
    }

    // JWT 토큰 생성
    const token = jwt.sign(
      { userId: user._id },
      process.env.JWT_SECRET,
      { expiresIn: '7d' }
    );

    res.json({
      message: '로그인 성공',
      token,
      user: {
        id: user._id,
        username: user.username,
        email: user.email
      }
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
};

Step 5: 라우트 설정

API 엔드포인트를 정의합니다.

// src/routes/auth.js
const express = require('express');
const router = express.Router();
const { body } = require('express-validator');
const authController = require('../controllers/authController');

router.post('/register',
  [
    body('username').trim().isLength({ min: 3 }).withMessage('사용자명은 3자 이상이어야 합니다'),
    body('email').isEmail().withMessage('유효한 이메일을 입력해주세요'),
    body('password').isLength({ min: 6 }).withMessage('비밀번호는 6자 이상이어야 합니다')
  ],
  authController.register
);

router.post('/login',
  [
    body('email').isEmail().withMessage('유효한 이메일을 입력해주세요'),
    body('password').notEmpty().withMessage('비밀번호를 입력해주세요')
  ],
  authController.login
);

module.exports = router;

Step 6: Express 애플리케이션 설정

미들웨어와 라우트를 통합합니다.

// src/app.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const authRoutes = require('./routes/auth');

const app = express();

// 보안 미들웨어
app.use(helmet());
app.use(cors());

// 요청 본문 파싱
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// 라우트
app.use('/api/auth', authRoutes);

// 404 핸들러
app.use((req, res) => {
  res.status(404).json({ error: '요청한 리소스를 찾을 수 없습니다' });
});

// 에러 핸들러
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(err.status || 500).json({
    error: err.message || '서버 오류가 발생했습니다'
  });
});

module.exports = app;

Step 7: 서버 시작 파일

// server.js
require('dotenv').config();
const app = require('./src/app');
const connectDB = require('./src/config/database');

const PORT = process.env.PORT || 5000;

// 데이터베이스 연결
connectDB();

// 서버 시작
app.listen(PORT, () => {
  console.log(`서버가 포트 ${PORT}에서 실행 중입니다`);
});

Step 8: 환경 변수 설정

# .env
MONGODB_URI=mongodb://localhost:27017/rest-api-tutorial
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
PORT=5000
NODE_ENV=development

5. 테스트 및 배포

API 테스트 작성

Jest와 Supertest를 사용하여 API 엔드포인트를 테스트합니다.

// tests/auth.test.js
const request = require('supertest');
const app = require('../src/app');
const mongoose = require('mongoose');
const User = require('../src/models/User');

beforeAll(async () => {
  await mongoose.connect(process.env.MONGODB_TEST_URI);
});

afterAll(async () => {
  await User.deleteMany({});
  await mongoose.connection.close();
});

describe('Auth API', () => {
  it('POST /api/auth/register - 회원가입 성공', async () => {
    const response = await request(app)
      .post('/api/auth/register')
      .send({
        username: 'testuser',
        email: '[email protected]',
        password: 'password123'
      });
    
    expect(response.status).toBe(201);
    expect(response.body).toHaveProperty('token');
    expect(response.body.user.email).toBe('[email protected]');
  });

  it('POST /api/auth/login - 로그인 성공', async () => {
    const response = await request(app)
      .post('/api/auth/login')
      .send({
        email: '[email protected]',
        password: 'password123'
      });
    
    expect(response.status).toBe(200);
    expect(response.body).toHaveProperty('token');
  });
});

배포 준비

프로덕션 환경에 배포하기 위한 설정을 추가합니다.

// package.json scripts
{
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js",
    "test": "jest --watchAll --verbose"
  }
}

배포 옵션:

  • Heroku: 간단한 git push로 배포 가능
  • AWS EC2: 더 많은 제어와 확장성 제공
  • DigitalOcean: 비용 효율적인 VPS 옵션
  • Vercel/Railway: 무료 티어와 자동 배포 지원

6. 마무리 및 확장 아이디어

REST API 서버 구축 단계별 튜토리얼을 통해 기본적인 인증 시스템을 갖춘 RESTful API 서버를 완성했습니다. 이제 프로젝트를 확장할 수 있는 아이디어를 소개합니다:

  • 역할 기반 접근 제어(RBAC): 관리자, 일반 사용자 등 역할별 권한 관리
  • 파일 업로드: Multer를 사용한 프로필 이미지 업로드
  • 이메일 인증: Nodemailer로 회원가입 시 이메일 확인
  • 비밀번호 재설정: 토큰 기반 비밀번호 복구 시스템
  • Rate Limiting: express-rate-limit으로 API 호출 제한
  • 로깅 시스템: Winston 또는 Morgan으로 상세한 로그 관리
  • API 문서화: Swagger/OpenAPI로 자동 문서 생성
  • Redis 캐싱: 자주 조회되는 데이터 캐싱으로 성능 향상
  • 소셜 로그인: Passport.js로 Google, GitHub 로그인 통합

REST API 서버 구축 단계별 튜토리얼을 기반으로 실제 프로덕션 레벨의 API 서버를 개발할 수 있으며, 포트폴리오에 추가하여 백엔드 개발 역량을 증명할 수 있습니다. 계속해서 기능을 추가하고 최적화하면서 실무 경험을 쌓아보세요!

📚 함께 읽으면 좋은 글

1

REST API 서버 구축 단계별 튜토리얼 – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 10. 1.
🎯 REST API 서버 구축 단계별 튜토리얼

2

undefined 완벽 해결법 – 원인부터 예방까지

📂 프로젝트 아이디어
📅 2025. 9. 29.
🎯 undefined

3

FastAPI로 REST API 만들기 – 초보자도 쉽게 따라하는 완벽 가이드

📂 Python 튜토리얼
📅 2025. 10. 1.
🎯 FastAPI로 REST API 만들기

4

React Context API 마스터하기 – 초보자도 쉽게 따라하는 완벽 가이드

📂 React 튜토리얼
📅 2025. 10. 1.
🎯 React Context API 마스터하기

5

Python 자동화 스크립트 작성하기 – 초보자도 쉽게 따라하는 완벽 가이드

📂 Python 튜토리얼
📅 2025. 9. 30.
🎯 Python 자동화 스크립트 작성하기

💡 위 글들을 통해 더 깊이 있는 정보를 얻어보세요!

📢 이 글이 도움되셨나요? 공유해주세요!

여러분의 공유 한 번이 더 많은 사람들에게 도움이 됩니다 ✨

🔥 공유할 때마다 블로그 성장에 큰 힘이 됩니다! 감사합니다 🙏

💬 여러분의 소중한 의견을 들려주세요!

이 글을 읽고 새롭게 알게 된 정보가 있다면 공유해주세요!

💡
유용한 정보 공유

궁금한 점 질문

🤝
경험담 나누기

👍
의견 표현하기

⭐ 모든 댓글은 24시간 내에 답변드리며, 여러분의 의견이 다른 독자들에게 큰 도움이 됩니다!
🎯 건설적인 의견과 경험 공유를 환영합니다 ✨

🔔 블로그 구독하고 최신 글을 받아보세요!

📚
다양한 주제
17개 카테고리

정기 업데이트
하루 3회 발행

🎯
실용적 정보
바로 적용 가능

💡
최신 트렌드
2025년 기준

🌟 프로젝트 아이디어부터 다양한 실생활 정보까지!
매일 새로운 유용한 콘텐츠를 만나보세요 ✨

📧 RSS 구독 | 🔖 북마크 추가 | 📱 모바일 앱 알림 설정
지금 구독하고 놓치는 정보 없이 업데이트 받아보세요!

답글 남기기