프로젝트 소개 및 목표
🔗 관련 에러 해결 가이드
React + Node.js 풀스택 앱 배포하기는 프론트엔드부터 백엔드, 그리고 실제 서버 배포까지 전체 웹 개발 프로세스를 경험할 수 있는 실전 프로젝트입니다. 이 가이드를 통해 React로 사용자 인터페이스를 구축하고, Node.js와 Express로 RESTful API를 개발한 뒤, AWS, Heroku, Vercel 등의 클라우드 플랫폼에 실제로 배포하는 방법을 배웁니다. 단순히 로컬 환경에서만 작동하는 앱이 아닌, 전 세계 누구나 접속할 수 있는 실제 서비스를 만들어보세요. 포트폴리오에 추가할 수 있는 완성도 높은 풀스택 애플리케이션을 구축하는 것이 이 프로젝트의 최종 목표입니다.
필요한 기술 스택
이 프로젝트를 완성하기 위해 다음 기술들이 필요합니다:
- 프론트엔드: React 18+, React Router, Axios, CSS Modules 또는 styled-components
- 백엔드: Node.js 18+, Express.js, MongoDB 또는 PostgreSQL, JWT 인증
- 개발 도구: Git, npm/yarn, Postman 또는 Insomnia (API 테스트용)
- 배포 플랫폼: Vercel/Netlify (프론트엔드), Render/Railway (백엔드), MongoDB Atlas (데이터베이스)
- 기타: Docker (선택사항), Nginx (선택사항), SSL 인증서 (Let’s Encrypt)
프로젝트 셋업
개발 환경을 구축하고 프로젝트 구조를 설정합니다.
1. 프로젝트 폴더 구조 생성
mkdir fullstack-app
cd fullstack-app
mkdir client server
2. 백엔드 초기화
cd server
npm init -y
npm install express mongoose dotenv cors bcryptjs jsonwebtoken
npm install -D nodemon
3. 프론트엔드 초기화
cd ../client
npx create-react-app .
npm install axios react-router-dom
이제 개발을 시작할 준비가 완료되었습니다. 백엔드는 server 폴더에서, 프론트엔드는 client 폴더에서 작업합니다.
단계별 구현 과정
Step 1: 백엔드 API 서버 구축
먼저 Express 서버를 설정하고 기본 라우팅을 구성합니다.
server/index.js 파일을 생성합니다:
const express = require('express');
const cors = require('cors');
const dotenv = require('dotenv');
const mongoose = require('mongoose');
dotenv.config();
const app = express();
const PORT = process.env.PORT || 5000;
// 미들웨어
app.use(cors());
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', (req, res) => {
res.json({ message: 'API 서버가 정상 작동 중입니다!' });
});
// 사용자 라우트
const userRoutes = require('./routes/users');
app.use('/api/users', userRoutes);
// 게시물 라우트
const postRoutes = require('./routes/posts');
app.use('/api/posts', postRoutes);
app.listen(PORT, () => {
console.log(`서버가 포트 ${PORT}에서 실행 중입니다.`);
});
Step 2: 데이터베이스 모델 정의
MongoDB를 사용한 User와 Post 모델을 생성합니다.
server/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
},
email: {
type: String,
required: true,
unique: true,
lowercase: true,
trim: true
},
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();
this.password = await bcrypt.hash(this.password, 10);
next();
});
// 비밀번호 검증 메서드
userSchema.methods.comparePassword = async function(candidatePassword) {
return await bcrypt.compare(candidatePassword, this.password);
};
module.exports = mongoose.model('User', userSchema);
server/models/Post.js:
const mongoose = require('mongoose');
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true,
trim: true
},
content: {
type: String,
required: true
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
tags: [String],
likes: {
type: Number,
default: 0
},
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Post', postSchema);
Step 3: 인증 미들웨어 구현
server/middleware/auth.js:
const jwt = require('jsonwebtoken');
const authMiddleware = (req, res, next) => {
try {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ message: '인증 토큰이 없습니다.' });
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.userId = decoded.userId;
next();
} catch (error) {
res.status(401).json({ message: '유효하지 않은 토큰입니다.' });
}
};
module.exports = authMiddleware;
Step 4: API 라우트 구현
server/routes/users.js:
const express = require('express');
const router = express.Router();
const jwt = require('jsonwebtoken');
const User = require('../models/User');
const authMiddleware = require('../middleware/auth');
// 회원가입
router.post('/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({ message: '이미 존재하는 사용자입니다.' });
}
const user = new User({ username, email, password });
await user.save();
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({ message: '서버 오류가 발생했습니다.', error: error.message });
}
});
// 로그인
router.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ message: '이메일 또는 비밀번호가 잘못되었습니다.' });
}
const isPasswordValid = await user.comparePassword(password);
if (!isPasswordValid) {
return res.status(401).json({ message: '이메일 또는 비밀번호가 잘못되었습니다.' });
}
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({ message: '서버 오류가 발생했습니다.', error: error.message });
}
});
// 프로필 조회
router.get('/profile', authMiddleware, async (req, res) => {
try {
const user = await User.findById(req.userId).select('-password');
res.json(user);
} catch (error) {
res.status(500).json({ message: '서버 오류가 발생했습니다.' });
}
});
module.exports = router;
Step 5: React 프론트엔드 구축
이제 React + Node.js 풀스택 앱 배포하기 프로젝트의 프론트엔드를 구현합니다.
client/src/App.js:
import React from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import { AuthProvider } from './context/AuthContext';
import Navbar from './components/Navbar';
import Home from './pages/Home';
import Login from './pages/Login';
import Register from './pages/Register';
import Dashboard from './pages/Dashboard';
import PrivateRoute from './components/PrivateRoute';
import './App.css';
function App() {
return (
} />
} />
} />
}
/>
} />
);
}
export default App;
client/src/context/AuthContext.js:
import React, { createContext, useState, useContext, useEffect } from 'react';
import axios from 'axios';
const AuthContext = createContext();
export const useAuth = () => useContext(AuthContext);
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const token = localStorage.getItem('token');
if (token) {
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
fetchUser();
} else {
setLoading(false);
}
}, []);
const fetchUser = async () => {
try {
const response = await axios.get(`${process.env.REACT_APP_API_URL}/api/users/profile`);
setUser(response.data);
} catch (error) {
console.error('사용자 정보 로드 실패:', error);
localStorage.removeItem('token');
} finally {
setLoading(false);
}
};
const login = async (email, password) => {
const response = await axios.post(`${process.env.REACT_APP_API_URL}/api/users/login`, {
email,
password
});
localStorage.setItem('token', response.data.token);
axios.defaults.headers.common['Authorization'] = `Bearer ${response.data.token}`;
setUser(response.data.user);
return response.data;
};
const register = async (username, email, password) => {
const response = await axios.post(`${process.env.REACT_APP_API_URL}/api/users/register`, {
username,
email,
password
});
localStorage.setItem('token', response.data.token);
axios.defaults.headers.common['Authorization'] = `Bearer ${response.data.token}`;
setUser(response.data.user);
return response.data;
};
const logout = () => {
localStorage.removeItem('token');
delete axios.defaults.headers.common['Authorization'];
setUser(null);
};
const value = {
user,
login,
register,
logout,
loading
};
return {children} ;
};
Step 6: 환경 변수 설정
server/.env:
PORT=5000
MONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/fullstack-app
JWT_SECRET=your_super_secret_jwt_key_change_this_in_production
client/.env:
REACT_APP_API_URL=http://localhost:5000
테스트 및 배포
로컬 테스트
개발 환경에서 앱을 실행하여 모든 기능이 정상 작동하는지 확인합니다.
# 백엔드 실행 (server 폴더)
npm run dev
# 프론트엔드 실행 (client 폴더)
npm start
배포 프로세스
React + Node.js 풀스택 앱 배포하기의 마지막 단계입니다.
1. MongoDB Atlas 설정
- MongoDB Atlas에서 무료 클러스터 생성
- 네트워크 액세스 설정 (0.0.0.0/0 허용)
- 데이터베이스 사용자 생성 및 연결 문자열 복사
2. 백엔드 배포 (Render)
- Render.com에서 새 Web Service 생성
- GitHub 저장소 연결
- 환경 변수 설정 (MONGODB_URI, JWT_SECRET, PORT)
- 빌드 명령:
cd server && npm install - 시작 명령:
node server/index.js
3. 프론트엔드 배포 (Vercel)
- Vercel CLI 설치:
npm install -g vercel - 클라이언트 폴더에서
vercel명령 실행 - 환경 변수 설정:
REACT_APP_API_URL=https://your-api.render.com - 프로덕션 빌드 및 배포
4. CORS 설정 업데이트
// server/index.js
const corsOptions = {
origin: ['https://your-frontend.vercel.app', 'http://localhost:3000'],
credentials: true
};
app.use(cors(corsOptions));
마무리 및 확장 아이디어
축하합니다! React + Node.js 풀스택 앱 배포하기 프로젝트를 완성했습니다. 이제 실제 운영 환경에서 작동하는 풀스택 애플리케이션을 보유하게 되었습니다. 추가로 구현할 수 있는 기능들:
- 파일 업로드: AWS S3 또는 Cloudinary를 이용한 이미지 업로드
- 실시간 기능: Socket.io를 활용한 실시간 채팅 또는 알림
- 검색 기능: Elasticsearch 또는 MongoDB의 텍스트 인덱스를 활용한 전체 텍스트 검색
- 페이지네이션: 대용량 데이터 처리를 위한 커서 기반 또는 오프셋 기반 페이지네이션
- CI/CD 파이프라인: GitHub Actions를 이용한 자동 테스트 및 배포
- 모니터링: Sentry, LogRocket 등을 이용한 에러 추적 및 사용자 행동 분석
이 프로젝트는 포트폴리오에 추가하기에 완벽하며, 실제 취업 면접에서 기술 스택과 배포 경험을 어필할 수 있는 강력한 자료가 됩니다.
📚 함께 읽으면 좋은 글
React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!
📅 2025. 10. 16.
🎯 React + Node.js 풀스택 앱 배포하기
React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!
📅 2025. 10. 9.
🎯 React + Node.js 풀스택 앱 배포하기
React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!
📅 2025. 10. 7.
🎯 React + Node.js 풀스택 앱 배포하기
React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!
📅 2025. 10. 5.
🎯 React + Node.js 풀스택 앱 배포하기
React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!
📅 2025. 10. 3.
🎯 React + Node.js 풀스택 앱 배포하기
💡 위 글들을 통해 더 깊이 있는 정보를 얻어보세요!
📢 이 글이 도움되셨나요? 공유해주세요!
여러분의 공유 한 번이 더 많은 사람들에게 도움이 됩니다 ✨
🔥 공유할 때마다 블로그 성장에 큰 힘이 됩니다! 감사합니다 🙏
💬 여러분의 소중한 의견을 들려주세요!
React + Node.js 풀스택 앱 배포하기에 대한 여러분만의 경험이나 노하우가 있으시나요?
⭐ 모든 댓글은 24시간 내에 답변드리며, 여러분의 의견이 다른 독자들에게 큰 도움이 됩니다!
🎯 건설적인 의견과 경험 공유를 환영합니다 ✨
🔔 블로그 구독하고 최신 글을 받아보세요!
🌟 프로젝트 아이디어부터 다양한 실생활 정보까지!
매일 새로운 유용한 콘텐츠를 만나보세요 ✨
📧 RSS 구독 | 🔖 북마크 추가 | 📱 모바일 앱 알림 설정
지금 구독하고 놓치는 정보 없이 업데이트 받아보세요!