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

프로젝트 소개 및 목표

React + Node.js 풀스택 앱 배포하기는 프론트엔드부터 백엔드, 데이터베이스, 그리고 실제 서버 배포까지 전 과정을 경험할 수 있는 실전 프로젝트입니다. 이 가이드를 통해 React로 구축한 클라이언트와 Node.js Express 서버를 연동하고, MongoDB 데이터베이스를 연결한 후 AWS 또는 Heroku에 배포하는 전체 워크플로우를 학습합니다. 단순한 튜토리얼을 넘어 실무에서 사용하는 CI/CD 파이프라인 구축, 환경변수 관리, HTTPS 설정까지 다루어 포트폴리오로 활용할 수 있는 완성도 높은 애플리케이션을 만들 수 있습니다.

필요한 기술 스택

이 프로젝트를 완성하기 위해서는 다음 기술들이 필요합니다. 프론트엔드: React 18+, React Router, Axios, CSS Modules 또는 Styled-components. 백엔드: Node.js 18+, Express.js, MongoDB (Mongoose), JWT 인증, bcrypt. 배포 및 인프라: Docker, Nginx, AWS EC2/S3 또는 Heroku, PM2, GitHub Actions. 각 기술에 대한 기본 이해가 있다면 더욱 수월하게 진행할 수 있으며, 모르는 부분은 구현하면서 학습할 수 있습니다.

프로젝트 셋업

먼저 프로젝트 디렉토리 구조를 생성합니다. 루트 폴더에 client(React)와 server(Node.js) 폴더를 분리하여 관리합니다.

mkdir fullstack-deploy-app
cd fullstack-deploy-app
mkdir client server

# React 앱 생성
cd client
npx create-react-app .

# Express 서버 설정
cd ../server
npm init -y
npm install express mongoose dotenv cors bcryptjs jsonwebtoken
npm install --save-dev nodemon

server 폴더에 기본 구조를 만듭니다: models, routes, controllers, middleware 폴더를 생성하고 server.js 파일을 루트에 배치합니다. 환경변수 관리를 위해 .env 파일을 생성하고 .gitignore에 추가합니다.

단계별 구현 과정

1단계: Express 서버 기본 설정

server/server.js 파일에 Express 서버를 구성합니다.

const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
require('dotenv').config();

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

// 미들웨어
app.use(cors({
  origin: process.env.CLIENT_URL || 'http://localhost:3000',
  credentials: true
}));
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('/api/health', (req, res) => {
  res.json({ status: 'OK', message: 'Server is running' });
});

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

2단계: 사용자 인증 시스템 구축

models/User.js에 사용자 모델을 정의합니다.

const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

const userSchema = new mongoose.Schema({
  email: {
    type: String,
    required: true,
    unique: true,
    lowercase: true
  },
  password: {
    type: String,
    required: true,
    minlength: 6
  },
  name: {
    type: String,
    required: true
  },
  createdAt: {
    type: Date,
    default: Date.now
  }
});

userSchema.pre('save', async function(next) {
  if (!this.isModified('password')) return next();
  this.password = await bcrypt.hash(this.password, 12);
  next();
});

userSchema.methods.comparePassword = async function(candidatePassword) {
  return await bcrypt.compare(candidatePassword, this.password);
};

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

controllers/authController.js에 회원가입 및 로그인 로직을 구현합니다.

const jwt = require('jsonwebtoken');
const User = require('../models/User');

exports.register = async (req, res) => {
  try {
    const { email, password, name } = req.body;
    
    const existingUser = await User.findOne({ email });
    if (existingUser) {
      return res.status(400).json({ message: '이미 존재하는 이메일입니다' });
    }

    const user = await User.create({ email, password, name });
    const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
      expiresIn: '7d'
    });

    res.status(201).json({
      success: true,
      token,
      user: { id: user._id, email: user.email, name: user.name }
    });
  } catch (error) {
    res.status(500).json({ message: '서버 오류가 발생했습니다', error: error.message });
  }
};

exports.login = async (req, res) => {
  try {
    const { email, password } = req.body;
    
    const user = await User.findOne({ email });
    if (!user || !(await user.comparePassword(password))) {
      return res.status(401).json({ message: '이메일 또는 비밀번호가 올바르지 않습니다' });
    }

    const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
      expiresIn: '7d'
    });

    res.json({
      success: true,
      token,
      user: { id: user._id, email: user.email, name: user.name }
    });
  } catch (error) {
    res.status(500).json({ message: '서버 오류가 발생했습니다', error: error.message });
  }
};

3단계: React 클라이언트 구축

client/src에 API 통신을 위한 axios 인스턴스를 생성합니다.

// src/api/axios.js
import axios from 'axios';

const instance = axios.create({
  baseURL: process.env.REACT_APP_API_URL || 'http://localhost:5000/api',
  headers: {
    'Content-Type': 'application/json'
  }
});

instance.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

export default instance;

로그인 컴포넌트를 구현합니다.

// src/components/Login.js
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import api from '../api/axios';

function Login() {
  const [formData, setFormData] = useState({ email: '', password: '' });
  const [error, setError] = useState('');
  const navigate = useNavigate();

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const { data } = await api.post('/auth/login', formData);
      localStorage.setItem('token', data.token);
      localStorage.setItem('user', JSON.stringify(data.user));
      navigate('/dashboard');
    } catch (err) {
      setError(err.response?.data?.message || '로그인에 실패했습니다');
    }
  };

  return (
    

로그인

{error &&
{error}
}
setFormData({...formData, email: e.target.value})} required /> setFormData({...formData, password: e.target.value})} required />
); } export default Login;

4단계: 프로덕션 빌드 준비

React 앱을 빌드하고 Express 서버가 정적 파일을 서빙하도록 설정합니다.

// server.js에 추가
const path = require('path');

// API 라우트들...
app.use('/api/auth', require('./routes/authRoutes'));

// 프로덕션 환경에서 React 빌드 파일 서빙
if (process.env.NODE_ENV === 'production') {
  app.use(express.static(path.join(__dirname, '../client/build')));
  
  app.get('*', (req, res) => {
    res.sendFile(path.join(__dirname, '../client/build/index.html'));
  });
}

테스트 및 배포

Docker를 이용한 컨테이너화

루트 디렉토리에 Dockerfile을 생성합니다.

FROM node:18-alpine

WORKDIR /app

# 서버 의존성 설치
COPY server/package*.json ./server/
RUN cd server && npm ci --only=production

# 클라이언트 빌드
COPY client/package*.json ./client/
RUN cd client && npm ci
COPY client/ ./client/
RUN cd client && npm run build

# 서버 코드 복사
COPY server/ ./server/

WORKDIR /app/server
EXPOSE 5000

CMD ["node", "server.js"]

AWS EC2 배포

EC2 인스턴스에 접속하여 다음 명령어를 실행합니다.

# Docker 설치
sudo apt update
sudo apt install docker.io -y

# 프로젝트 클론 및 빌드
git clone https://github.com/yourusername/fullstack-deploy-app.git
cd fullstack-deploy-app
sudo docker build -t fullstack-app .

# 환경변수와 함께 컨테이너 실행
sudo docker run -d -p 80:5000 \
  -e MONGODB_URI="your_mongodb_uri" \
  -e JWT_SECRET="your_jwt_secret" \
  -e NODE_ENV="production" \
  --name fullstack-app \
  fullstack-app

Nginx를 리버스 프록시로 설정하여 HTTPS를 적용하고, PM2를 사용하여 프로세스를 관리할 수도 있습니다. GitHub Actions를 활용하면 코드 푸시 시 자동으로 배포되는 CI/CD 파이프라인을 구축할 수 있습니다.

마무리 및 확장 아이디어

React + Node.js 풀스택 앱 배포하기 프로젝트를 완성했습니다! 이제 기본적인 CRUD 기능을 추가하거나, Redis를 이용한 캐싱, WebSocket을 활용한 실시간 기능, S3를 이용한 파일 업로드 등으로 확장할 수 있습니다. 모니터링을 위해 Sentry나 LogRocket을 연동하고, 성능 최적화를 위해 CDN을 활용하는 것도 좋은 학습 기회가 됩니다. 완성된 프로젝트는 포트폴리오에 추가하여 실전 경험을 어필할 수 있으며, 지속적으로 개선하면서 풀스택 개발 역량을 키워나갈 수 있습니다. React + Node.js 풀스택 앱 배포하기는 단순한 튜토리얼이 아닌, 실무 수준의 애플리케이션 개발과 배포 전 과정을 경험할 수 있는 종합적인 학습 프로젝트입니다.

📚 함께 읽으면 좋은 글

1

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

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

2

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

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

3

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

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

4

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

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

5

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

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

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

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

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

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

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

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

💡
유용한 정보 공유

궁금한 점 질문

🤝
경험담 나누기

👍
의견 표현하기

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

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

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

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

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

💡
최신 트렌드
2025년 기준

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

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

답글 남기기