프로젝트 소개 및 목표
🔗 관련 에러 해결 가이드
React + Node.js 풀스택 앱 배포하기는 현대 웹 개발의 핵심 기술을 한 번에 경험할 수 있는 실전 프로젝트입니다. 이 가이드를 통해 프론트엔드부터 백엔드, 데이터베이스 연동, 그리고 실제 서버 배포까지 전체 개발 프로세스를 완성하게 됩니다. React로 동적인 사용자 인터페이스를 구축하고, Node.js와 Express로 RESTful API 서버를 만들며, MongoDB를 활용한 데이터 관리, 마지막으로 클라우드 플랫폼에 배포하여 실제 운영 환경을 경험합니다. 포트폴리오로 활용할 수 있는 완성도 높은 풀스택 애플리케이션을 만들어보세요.
필요한 기술 스택
이 프로젝트를 진행하기 위해서는 다음 기술들이 필요합니다:
- 프론트엔드: React 18+, Axios, React Router, Styled-components
- 백엔드: Node.js 18+, Express.js, Mongoose, dotenv
- 데이터베이스: MongoDB Atlas (클라우드 DB)
- 배포 플랫폼: Vercel (프론트엔드), Render 또는 Railway (백엔드)
- 개발 도구: Git, VS Code, Postman (API 테스트)
Node.js와 npm이 설치되어 있어야 하며, 기본적인 JavaScript, React, Express 지식이 있으면 더욱 수월하게 진행할 수 있습니다.
프로젝트 셋업
먼저 프로젝트 디렉토리를 생성하고 초기 설정을 진행합니다. 터미널에서 다음 명령어를 실행하세요:
# 프로젝트 폴더 생성
mkdir fullstack-app
cd fullstack-app
# 백엔드 초기화
mkdir backend
cd backend
npm init -y
npm install express mongoose dotenv cors nodemon
# 프론트엔드 생성
cd ..
npx create-react-app frontend
cd frontend
npm install axios react-router-dom styled-components
package.json의 scripts 부분을 수정하여 개발 서버를 편리하게 실행할 수 있도록 설정합니다. 백엔드의 경우 nodemon을 사용하여 자동 재시작 기능을 활성화합니다.
단계별 구현 과정
1단계: MongoDB 설정 및 연결
MongoDB Atlas에서 무료 클러스터를 생성합니다. atlas.mongodb.com에 접속하여 계정을 만들고 새 클러스터를 생성한 후 연결 문자열을 복사합니다. backend 폴더에 .env 파일을 생성하고 다음과 같이 설정합니다:
MONGODB_URI=mongodb+srv://username:[email protected]/myapp
PORT=5000
NODE_ENV=development
backend/server.js 파일을 생성하고 Express 서버와 MongoDB 연결을 설정합니다:
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
require('dotenv').config();
const app = express();
// 미들웨어
app.use(cors());
app.use(express.json());
// MongoDB 연결
mongoose.connect(process.env.MONGODB_URI)
.then(() => console.log('MongoDB 연결 성공'))
.catch(err => console.error('MongoDB 연결 실패:', err));
// 기본 라우트
app.get('/', (req, res) => {
res.json({ message: '풀스택 앱 API 서버' });
});
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`서버가 포트 ${PORT}에서 실행 중입니다`);
});
2단계: 백엔드 API 구축
간단한 할일 관리 앱을 만들어봅니다. backend/models/Todo.js 파일을 생성하여 데이터 모델을 정의합니다:
const mongoose = require('mongoose');
const todoSchema = new mongoose.Schema({
title: {
type: String,
required: true,
trim: true
},
description: {
type: String,
trim: true
},
completed: {
type: Boolean,
default: false
},
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Todo', todoSchema);
backend/routes/todos.js 파일을 생성하여 CRUD API를 구현합니다:
const express = require('express');
const router = express.Router();
const Todo = require('../models/Todo');
// 모든 할일 조회
router.get('/', async (req, res) => {
try {
const todos = await Todo.find().sort({ createdAt: -1 });
res.json(todos);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// 할일 생성
router.post('/', async (req, res) => {
const todo = new Todo({
title: req.body.title,
description: req.body.description
});
try {
const newTodo = await todo.save();
res.status(201).json(newTodo);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
// 할일 수정
router.patch('/:id', async (req, res) => {
try {
const todo = await Todo.findById(req.params.id);
if (!todo) return res.status(404).json({ message: '할일을 찾을 수 없습니다' });
if (req.body.title) todo.title = req.body.title;
if (req.body.description) todo.description = req.body.description;
if (req.body.completed !== undefined) todo.completed = req.body.completed;
const updatedTodo = await todo.save();
res.json(updatedTodo);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
// 할일 삭제
router.delete('/:id', async (req, res) => {
try {
const todo = await Todo.findByIdAndDelete(req.params.id);
if (!todo) return res.status(404).json({ message: '할일을 찾을 수 없습니다' });
res.json({ message: '할일이 삭제되었습니다' });
} catch (error) {
res.status(500).json({ message: error.message });
}
});
module.exports = router;
server.js에 라우트를 연결합니다:
const todoRoutes = require('./routes/todos');
app.use('/api/todos', todoRoutes);
3단계: React 프론트엔드 구축
frontend/src/services/api.js 파일을 생성하여 API 통신 함수를 작성합니다:
import axios from 'axios';
const API_URL = process.env.REACT_APP_API_URL || 'http://localhost:5000/api';
const api = axios.create({
baseURL: API_URL,
headers: {
'Content-Type': 'application/json'
}
});
export const todoAPI = {
getAll: () => api.get('/todos'),
create: (data) => api.post('/todos', data),
update: (id, data) => api.patch(`/todos/${id}`, data),
delete: (id) => api.delete(`/todos/${id}`)
};
export default api;
frontend/src/components/TodoList.js 파일을 생성하여 할일 목록 컴포넌트를 만듭니다:
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import { todoAPI } from '../services/api';
const TodoList = () => {
const [todos, setTodos] = useState([]);
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [loading, setLoading] = useState(false);
useEffect(() => {
fetchTodos();
}, []);
const fetchTodos = async () => {
try {
setLoading(true);
const response = await todoAPI.getAll();
setTodos(response.data);
} catch (error) {
console.error('할일 불러오기 실패:', error);
} finally {
setLoading(false);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!title.trim()) return;
try {
await todoAPI.create({ title, description });
setTitle('');
setDescription('');
fetchTodos();
} catch (error) {
console.error('할일 생성 실패:', error);
}
};
const toggleComplete = async (id, completed) => {
try {
await todoAPI.update(id, { completed: !completed });
fetchTodos();
} catch (error) {
console.error('할일 수정 실패:', error);
}
};
const deleteTodo = async (id) => {
try {
await todoAPI.delete(id);
fetchTodos();
} catch (error) {
console.error('할일 삭제 실패:', error);
}
};
return (
할일 관리 앱
{loading ? (
로딩 중...
) : (
{todos.map(todo => (
toggleComplete(todo._id, todo.completed)}
/>
{todo.title}
{todo.description && {todo.description} }
deleteTodo(todo._id)}>삭제
))}
)}
);
};
const Container = styled.div`
max-width: 800px;
margin: 0 auto;
padding: 20px;
`;
const Form = styled.form`
margin-bottom: 30px;
`;
const Input = styled.input`
width: 100%;
padding: 12px;
margin-bottom: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
`;
const Textarea = styled.textarea`
width: 100%;
padding: 12px;
margin-bottom: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
min-height: 80px;
`;
const Button = styled.button`
padding: 12px 24px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
&:hover {
background-color: #0056b3;
}
`;
const TodoListContainer = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
`;
const TodoItem = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px;
background-color: #f8f9fa;
border-radius: 4px;
`;
const TodoContent = styled.div`
display: flex;
gap: 12px;
align-items: flex-start;
`;
const Checkbox = styled.input`
margin-top: 4px;
`;
const Title = styled.h3`
margin: 0 0 5px 0;
text-decoration: ${props => props.completed ? 'line-through' : 'none'};
color: ${props => props.completed ? '#999' : '#333'};
`;
const Description = styled.p`
margin: 0;
font-size: 14px;
color: #666;
`;
const DeleteButton = styled.button`
padding: 8px 16px;
background-color: #dc3545;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
background-color: #c82333;
}
`;
export default TodoList;
frontend/src/App.js를 수정하여 TodoList 컴포넌트를 사용합니다:
import React from 'react';
import TodoList from './components/TodoList';
function App() {
return (
);
}
export default App;
테스트 및 배포
React + Node.js 풀스택 앱 배포하기의 마지막 단계는 테스트와 실제 배포입니다. 먼저 로컬에서 백엔드와 프론트엔드를 동시에 실행하여 테스트합니다:
# 백엔드 실행 (backend 폴더에서)
npm run dev
# 프론트엔드 실행 (frontend 폴더에서)
npm start
Postman으로 API 엔드포인트를 테스트한 후 배포를 진행합니다. 백엔드 배포 (Render): render.com에서 새 Web Service를 생성하고 GitHub 레포지토리를 연결합니다. 환경 변수에 MONGODB_URI를 설정하고 Build Command는 npm install, Start Command는 node server.js로 설정합니다. 프론트엔드 배포 (Vercel): vercel.com에서 프로젝트를 임포트하고 환경 변수 REACT_APP_API_URL에 백엔드 URL을 설정합니다. 배포 후 실제 URL에서 앱이 정상 작동하는지 확인합니다.
마무리 및 확장 아이디어
이제 React + Node.js 풀스택 앱 배포하기 프로젝트가 완성되었습니다! 이 기본 구조를 바탕으로 다양한 기능을 추가할 수 있습니다: 사용자 인증 (JWT), 파일 업로드, 실시간 업데이트 (Socket.io), 검색 및 필터링, 페이지네이션, 다크모드 등을 구현해보세요. Redux나 Context API로 상태 관리를 개선하고, TypeScript를 도입하여 타입 안정성을 높일 수도 있습니다. CI/CD 파이프라인을 구축하여 자동 배포를 설정하면 더욱 전문적인 개발 환경을 구축할 수 있습니다. 이 프로젝트는 포트폴리오로 활용하기에 완벽하며, 실무에서 요구되는 풀스택 개발 역량을 증명할 수 있습니다.
📚 함께 읽으면 좋은 글
React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!
📅 2025. 11. 3.
🎯 React + Node.js 풀스택 앱 배포하기
React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!
📅 2025. 10. 27.
🎯 React + Node.js 풀스택 앱 배포하기
JWT 인증 시스템 구현하기 – 완성까지 한번에!
📅 2025. 11. 10.
🎯 JWT 인증 시스템 구현하기
REST API 서버 구축 단계별 튜토리얼 – 완성까지 한번에!
📅 2025. 11. 9.
🎯 REST API 서버 구축 단계별 튜토리얼
실시간 채팅 앱 만들기 with Socket.io – 완성까지 한번에!
📅 2025. 11. 9.
🎯 실시간 채팅 앱 만들기 with Socket.io
💡 위 글들을 통해 더 깊이 있는 정보를 얻어보세요!
📢 이 글이 도움되셨나요? 공유해주세요!
여러분의 공유 한 번이 더 많은 사람들에게 도움이 됩니다 ✨
🔥 공유할 때마다 블로그 성장에 큰 힘이 됩니다! 감사합니다 🙏
💬 여러분의 소중한 의견을 들려주세요!
React + Node.js 풀스택 앱 배포하기 관련해서 궁금한 점이 더 있으시다면 언제든 물어보세요!
⭐ 모든 댓글은 24시간 내에 답변드리며, 여러분의 의견이 다른 독자들에게 큰 도움이 됩니다!
🎯 건설적인 의견과 경험 공유를 환영합니다 ✨
🔔 블로그 구독하고 최신 글을 받아보세요!
🌟 프로젝트 아이디어부터 다양한 실생활 정보까지!
매일 새로운 유용한 콘텐츠를 만나보세요 ✨
📧 RSS 구독 | 🔖 북마크 추가 | 📱 모바일 앱 알림 설정
지금 구독하고 놓치는 정보 없이 업데이트 받아보세요!