프로젝트 소개 및 목표
🔗 관련 에러 해결 가이드
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}}
);
}
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 풀스택 앱 배포하기는 단순한 튜토리얼이 아닌, 실무 수준의 애플리케이션 개발과 배포 전 과정을 경험할 수 있는 종합적인 학습 프로젝트입니다.
📚 함께 읽으면 좋은 글
React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!
📅 2025. 11. 11.
🎯 React + Node.js 풀스택 앱 배포하기
React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!
📅 2025. 11. 11.
🎯 React + Node.js 풀스택 앱 배포하기
React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!
📅 2025. 11. 3.
🎯 React + Node.js 풀스택 앱 배포하기
React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!
📅 2025. 10. 27.
🎯 React + Node.js 풀스택 앱 배포하기
실시간 채팅 앱 만들기 with Socket.io – 완성까지 한번에!
📅 2025. 11. 15.
🎯 실시간 채팅 앱 만들기 with Socket.io
💡 위 글들을 통해 더 깊이 있는 정보를 얻어보세요!
📢 이 글이 도움되셨나요? 공유해주세요!
여러분의 공유 한 번이 더 많은 사람들에게 도움이 됩니다 ✨
🔥 공유할 때마다 블로그 성장에 큰 힘이 됩니다! 감사합니다 🙏
💬 여러분의 소중한 의견을 들려주세요!
이 글을 읽고 새롭게 알게 된 정보가 있다면 공유해주세요!
⭐ 모든 댓글은 24시간 내에 답변드리며, 여러분의 의견이 다른 독자들에게 큰 도움이 됩니다!
🎯 건설적인 의견과 경험 공유를 환영합니다 ✨
🔔 블로그 구독하고 최신 글을 받아보세요!
🌟 프로젝트 아이디어부터 다양한 실생활 정보까지!
매일 새로운 유용한 콘텐츠를 만나보세요 ✨
📧 RSS 구독 | 🔖 북마크 추가 | 📱 모바일 앱 알림 설정
지금 구독하고 놓치는 정보 없이 업데이트 받아보세요!