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

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

1. 프로젝트 소개 및 목표

이번 REST API 서버 구축 단계별 튜토리얼에서는 Node.js와 Express를 활용하여 실무에서 바로 사용할 수 있는 RESTful API 서버를 처음부터 끝까지 구축해보겠습니다. 이 튜토리얼을 통해 기본적인 CRUD 작업부터 인증, 에러 핸들링, 데이터베이스 연동까지 모든 과정을 단계별로 학습할 수 있습니다. 완성된 프로젝트는 포트폴리오로 활용하거나 실제 서비스의 백엔드 기반으로 사용할 수 있으며, 현대적인 웹 개발의 핵심 개념을 이해하는 데 큰 도움이 될 것입니다. 초보자도 따라할 수 있도록 모든 단계를 상세히 설명하며, 실무 팁과 베스트 프랙티스도 함께 제공합니다.

2. 필요한 기술 스택

REST API 서버 구축 단계별 튜토리얼을 진행하기 위해 다음 기술들이 필요합니다:

  • Node.js (v16 이상): 서버 사이드 JavaScript 런타임
  • Express.js: 빠르고 간결한 웹 프레임워크
  • MongoDB: NoSQL 데이터베이스 (또는 PostgreSQL)
  • Mongoose: MongoDB ODM 라이브러리
  • JWT: 토큰 기반 인증
  • dotenv: 환경 변수 관리
  • Postman 또는 Thunder Client: API 테스트 도구

기본적인 JavaScript 지식과 HTTP 프로토콜에 대한 이해가 있다면 더욱 좋습니다.

3. 프로젝트 셋업

프로젝트를 시작하기 위한 초기 설정을 진행합니다. 먼저 새로운 프로젝트 디렉토리를 생성하고 npm을 초기화합니다.

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

# npm 초기화
npm init -y

# 필요한 패키지 설치
npm install express mongoose dotenv bcryptjs jsonwebtoken
npm install --save-dev nodemon

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

rest-api-tutorial/
├── src/
│   ├── controllers/
│   ├── models/
│   ├── routes/
│   ├── middleware/
│   └── config/
├── .env
├── .gitignore
├── server.js
└── package.json

.env 파일을 생성하여 환경 변수를 설정합니다:

PORT=3000
MONGODB_URI=mongodb://localhost:27017/rest-api-tutorial
JWT_SECRET=your_jwt_secret_key_here

package.json에 스크립트를 추가합니다:

"scripts": {
  "start": "node server.js",
  "dev": "nodemon server.js"
}

4. 단계별 구현 과정

4.1 기본 서버 설정

먼저 server.js 파일에 기본적인 Express 서버를 구성합니다.

// server.js
const express = require('express');
const mongoose = require('mongoose');
const dotenv = require('dotenv');

// 환경 변수 로드
dotenv.config();

const app = express();
const PORT = process.env.PORT || 3000;

// 미들웨어 설정
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// MongoDB 연결
mongoose.connect(process.env.MONGODB_URI, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
})
.then(() => console.log('MongoDB 연결 성공'))
.catch((err) => console.error('MongoDB 연결 실패:', err));

// 기본 라우트
app.get('/', (req, res) => {
  res.json({ message: 'REST API 서버가 실행 중입니다!' });
});

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

4.2 데이터 모델 생성

사용자(User) 모델을 생성하여 데이터베이스 스키마를 정의합니다.

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

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

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

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

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

4.3 컨트롤러 구현

비즈니스 로직을 처리하는 컨트롤러를 작성합니다.

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

// JWT 토큰 생성 함수
const generateToken = (userId) => {
  return jwt.sign({ id: userId }, process.env.JWT_SECRET, {
    expiresIn: '7d'
  });
};

// 회원가입
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({ 
        success: false, 
        message: '이미 존재하는 사용자입니다' 
      });
    }
    
    // 새 사용자 생성
    const user = await User.create({ username, email, password });
    
    // 토큰 생성
    const token = generateToken(user._id);
    
    res.status(201).json({
      success: true,
      message: '회원가입 성공',
      data: {
        user: {
          id: user._id,
          username: user.username,
          email: user.email
        },
        token
      }
    });
  } catch (error) {
    res.status(500).json({ 
      success: false, 
      message: '서버 오류가 발생했습니다', 
      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({ 
        success: false, 
        message: '이메일 또는 비밀번호가 올바르지 않습니다' 
      });
    }
    
    // 비밀번호 확인
    const isPasswordValid = await user.comparePassword(password);
    if (!isPasswordValid) {
      return res.status(401).json({ 
        success: false, 
        message: '이메일 또는 비밀번호가 올바르지 않습니다' 
      });
    }
    
    // 토큰 생성
    const token = generateToken(user._id);
    
    res.json({
      success: true,
      message: '로그인 성공',
      data: {
        user: {
          id: user._id,
          username: user.username,
          email: user.email
        },
        token
      }
    });
  } catch (error) {
    res.status(500).json({ 
      success: false, 
      message: '서버 오류가 발생했습니다', 
      error: error.message 
    });
  }
};

// 사용자 프로필 조회
exports.getProfile = async (req, res) => {
  try {
    const user = await User.findById(req.user.id).select('-password');
    
    if (!user) {
      return res.status(404).json({ 
        success: false, 
        message: '사용자를 찾을 수 없습니다' 
      });
    }
    
    res.json({
      success: true,
      data: { user }
    });
  } catch (error) {
    res.status(500).json({ 
      success: false, 
      message: '서버 오류가 발생했습니다', 
      error: error.message 
    });
  }
};

// 모든 사용자 조회
exports.getAllUsers = async (req, res) => {
  try {
    const users = await User.find().select('-password');
    
    res.json({
      success: true,
      count: users.length,
      data: { users }
    });
  } catch (error) {
    res.status(500).json({ 
      success: false, 
      message: '서버 오류가 발생했습니다', 
      error: error.message 
    });
  }
};

4.4 인증 미들웨어 구현

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

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

const auth = async (req, res, next) => {
  try {
    // 헤더에서 토큰 추출
    const token = req.header('Authorization')?.replace('Bearer ', '');
    
    if (!token) {
      return res.status(401).json({ 
        success: false, 
        message: '인증 토큰이 필요합니다' 
      });
    }
    
    // 토큰 검증
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    
    // 사용자 확인
    const user = await User.findById(decoded.id);
    if (!user) {
      return res.status(401).json({ 
        success: false, 
        message: '유효하지 않은 토큰입니다' 
      });
    }
    
    // 요청 객체에 사용자 정보 추가
    req.user = user;
    next();
  } catch (error) {
    res.status(401).json({ 
      success: false, 
      message: '인증에 실패했습니다', 
      error: error.message 
    });
  }
};

module.exports = auth;

4.5 라우트 설정

API 엔드포인트를 정의하는 라우트를 구성합니다.

// src/routes/userRoutes.js
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
const auth = require('../middleware/auth');

// 공개 라우트
router.post('/register', userController.register);
router.post('/login', userController.login);

// 보호된 라우트 (인증 필요)
router.get('/profile', auth, userController.getProfile);
router.get('/users', auth, userController.getAllUsers);

module.exports = router;

server.js에 라우트를 연결합니다:

// server.js에 추가
const userRoutes = require('./src/routes/userRoutes');

// 라우트 설정
app.use('/api/auth', userRoutes);

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

// 전역 에러 핸들링
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ 
    success: false, 
    message: '서버 내부 오류가 발생했습니다',
    error: process.env.NODE_ENV === 'development' ? err.message : undefined
  });
});

5. 테스트 및 배포

5.1 API 테스트

Postman이나 Thunder Client를 사용하여 API를 테스트합니다.

회원가입 테스트:

POST http://localhost:3000/api/auth/register
Content-Type: application/json

{
  "username": "testuser",
  "email": "[email protected]",
  "password": "password123"
}

로그인 테스트:

POST http://localhost:3000/api/auth/login
Content-Type: application/json

{
  "email": "[email protected]",
  "password": "password123"
}

프로필 조회 테스트:

GET http://localhost:3000/api/auth/profile
Authorization: Bearer YOUR_JWT_TOKEN

5.2 배포 준비

프로덕션 환경을 위한 설정을 추가합니다.

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

const connectDB = async () => {
  try {
    const conn = await mongoose.connect(process.env.MONGODB_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
    
    console.log(`MongoDB Connected: ${conn.connection.host}`);
  } catch (error) {
    console.error(`Error: ${error.message}`);
    process.exit(1);
  }
};

module.exports = connectDB;

Heroku, AWS, 또는 DigitalOcean에 배포할 수 있습니다. MongoDB Atlas를 사용하여 클라우드 데이터베이스를 설정하고, 환경 변수를 프로덕션 환경에 맞게 구성합니다.

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

REST API 서버 구축 단계별 튜토리얼을 통해 기본적인 RESTful API 서버를 완성했습니다. 이제 다음과 같은 기능을 추가하여 프로젝트를 확장할 수 있습니다:

  • 비밀번호 재설정: 이메일 인증을 통한 비밀번호 복구 기능
  • 파일 업로드: Multer를 사용한 프로필 이미지 업로드
  • 페이지네이션: 대량의 데이터를 효율적으로 처리
  • 검색 및 필터링: 쿼리 파라미터를 활용한 고급 검색
  • 레이트 리미팅: API 남용 방지를 위한 요청 제한
  • API 문서화: Swagger를 사용한 자동 API 문서 생성
  • 로깅: Winston 또는 Morgan을 활용한 로그 관리
  • 테스트 코드: Jest와 Supertest를 사용한 단위 및 통합 테스트

이 프로젝트는 포트폴리오로 활용하거나 실제 서비스의 백엔드로 발전시킬 수 있는 견고한 기반이 됩니다. 지속적으로 코드를 개선하고 새로운 기능을 추가하면서 실력을 향상시켜 보세요!

📚 함께 읽으면 좋은 글

1

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

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

2

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

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

3

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

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

4

실시간 채팅 앱 만들기 with Socket.io – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 11. 4.
🎯 실시간 채팅 앱 만들기 with Socket.io

5

React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 11. 3.
🎯 React + Node.js 풀스택 앱 배포하기

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

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

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

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

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

이 글에서 가장 도움이 된 부분은 어떤 것인가요?

💡
유용한 정보 공유

궁금한 점 질문

🤝
경험담 나누기

👍
의견 표현하기

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

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

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

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

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

💡
최신 트렌드
2025년 기준

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

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

답글 남기기