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

프로젝트 소개 및 목표

React + Node.js 풀스택 앱 배포하기는 프론트엔드부터 백엔드, 데이터베이스, 그리고 실제 프로덕션 배포까지 전 과정을 경험할 수 있는 실전 프로젝트입니다. 이 가이드를 통해 React로 사용자 인터페이스를 구축하고, Node.js와 Express로 RESTful API를 개발하며, MongoDB를 연동한 뒤 Heroku와 Vercel을 이용해 실제 서비스를 배포하는 방법을 배웁니다. 단순한 튜토리얼을 넘어 실무에서 사용하는 환경 변수 관리, CORS 설정, 프로덕션 빌드 최적화까지 다루어 포트폴리오로 활용할 수 있는 완성도 높은 애플리케이션을 만들 수 있습니다.

필요한 기술 스택

이 프로젝트를 진행하기 위해서는 다음과 같은 기술 스택이 필요합니다. 프론트엔드는 React 18, React Router, Axios를 사용하며, 백엔드는 Node.js, Express.js, MongoDB(Mongoose)를 활용합니다. 개발 도구로는 Git, npm/yarn, VS Code를 사용하고, 배포 플랫폼으로는 백엔드용 Heroku(또는 Render), 프론트엔드용 Vercel(또는 Netlify), 데이터베이스용 MongoDB Atlas를 이용합니다. Node.js 16 이상과 npm이 설치되어 있어야 하며, Git과 GitHub 계정도 필요합니다.

프로젝트 셋업

먼저 프로젝트 루트 디렉토리를 생성하고 백엔드와 프론트엔드를 분리된 폴더로 구성합니다. 터미널에서 다음 명령어를 실행하여 초기 구조를 만듭니다.

mkdir fullstack-app
cd fullstack-app
mkdir server client

백엔드 서버를 먼저 설정합니다. server 폴더로 이동하여 npm 프로젝트를 초기화하고 필요한 패키지를 설치합니다.

cd server
npm init -y
npm install express mongoose cors dotenv
npm install --save-dev nodemon

프론트엔드는 Create React App을 사용하여 빠르게 구성합니다.

cd ../client
npx create-react-app .
npm install axios react-router-dom

이제 기본적인 프로젝트 구조가 완성되었으며, 각각의 폴더에서 독립적으로 개발을 진행할 수 있습니다.

단계별 구현 과정

1단계: MongoDB Atlas 설정 및 데이터베이스 연결

MongoDB Atlas에 접속하여 무료 클러스터를 생성합니다. 클러스터 생성 후 Database Access에서 사용자를 추가하고, Network Access에서 IP 주소를 화이트리스트에 등록합니다(개발 단계에서는 0.0.0.0/0 허용). 연결 문자열을 복사한 뒤, server 폴더에 .env 파일을 생성하여 환경 변수를 설정합니다.

MONGODB_URI=mongodb+srv://username:[email protected]/fullstack-app?retryWrites=true&w=majority
PORT=5000
NODE_ENV=development

2단계: Express 백엔드 API 구축

server/index.js 파일을 생성하여 Express 서버의 기본 구조를 작성합니다.

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('/api/health', (req, res) => {
  res.json({ status: 'OK', message: '서버가 정상 작동 중입니다' });
});

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

다음으로 데이터 모델을 정의합니다. server/models/Task.js 파일을 생성합니다.

const mongoose = require('mongoose');

const taskSchema = 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('Task', taskSchema);

RESTful API 라우트를 구현합니다. server/routes/tasks.js 파일을 생성합니다.

const express = require('express');
const router = express.Router();
const Task = require('../models/Task');

// 모든 작업 조회
router.get('/', async (req, res) => {
  try {
    const tasks = await Task.find().sort({ createdAt: -1 });
    res.json(tasks);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});

// 작업 생성
router.post('/', async (req, res) => {
  const task = new Task({
    title: req.body.title,
    description: req.body.description
  });
  
  try {
    const newTask = await task.save();
    res.status(201).json(newTask);
  } catch (error) {
    res.status(400).json({ message: error.message });
  }
});

// 작업 수정
router.patch('/:id', async (req, res) => {
  try {
    const task = await Task.findById(req.params.id);
    if (!task) return res.status(404).json({ message: '작업을 찾을 수 없습니다' });
    
    if (req.body.title) task.title = req.body.title;
    if (req.body.description) task.description = req.body.description;
    if (req.body.completed !== undefined) task.completed = req.body.completed;
    
    const updatedTask = await task.save();
    res.json(updatedTask);
  } catch (error) {
    res.status(400).json({ message: error.message });
  }
});

// 작업 삭제
router.delete('/:id', async (req, res) => {
  try {
    const task = await Task.findById(req.params.id);
    if (!task) return res.status(404).json({ message: '작업을 찾을 수 없습니다' });
    
    await task.deleteOne();
    res.json({ message: '작업이 삭제되었습니다' });
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});

module.exports = router;

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

const taskRoutes = require('./routes/tasks');
app.use('/api/tasks', taskRoutes);

package.json의 scripts 섹션에 개발 스크립트를 추가합니다.

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

3단계: React 프론트엔드 개발

client/src 폴더에 API 통신을 위한 api.js 파일을 생성합니다.

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 getTasks = () => api.get('/tasks');
export const createTask = (task) => api.post('/tasks', task);
export const updateTask = (id, task) => api.patch(`/tasks/${id}`, task);
export const deleteTask = (id) => api.delete(`/tasks/${id}`);

export default api;

src/components/TaskList.js 컴포넌트를 생성합니다.

import React, { useState, useEffect } from 'react';
import { getTasks, createTask, updateTask, deleteTask } from '../api';
import './TaskList.css';

function TaskList() {
  const [tasks, setTasks] = useState([]);
  const [newTask, setNewTask] = useState({ title: '', description: '' });
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchTasks();
  }, []);

  const fetchTasks = async () => {
    try {
      const response = await getTasks();
      setTasks(response.data);
      setLoading(false);
    } catch (error) {
      console.error('작업 목록 조회 실패:', error);
      setLoading(false);
    }
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!newTask.title.trim()) return;
    
    try {
      await createTask(newTask);
      setNewTask({ title: '', description: '' });
      fetchTasks();
    } catch (error) {
      console.error('작업 생성 실패:', error);
    }
  };

  const handleToggle = async (task) => {
    try {
      await updateTask(task._id, { completed: !task.completed });
      fetchTasks();
    } catch (error) {
      console.error('작업 수정 실패:', error);
    }
  };

  const handleDelete = async (id) => {
    try {
      await deleteTask(id);
      fetchTasks();
    } catch (error) {
      console.error('작업 삭제 실패:', error);
    }
  };

  if (loading) return 
로딩 중...
; return (

할 일 관리

setNewTask({ ...newTask, title: e.target.value })} /> setNewTask({ ...newTask, description: e.target.value })} />
{tasks.map(task => (

{task.title}

{task.description &&

{task.description}

}
))}
); } export default TaskList;

App.js를 수정하여 TaskList 컴포넌트를 렌더링합니다.

import React from 'react';
import TaskList from './components/TaskList';
import './App.css';

function App() {
  return (
    
); } export default App;

테스트 및 배포

로컬 테스트

두 개의 터미널을 열어 각각 백엔드와 프론트엔드를 실행합니다.

# 터미널 1 - 백엔드
cd server
npm run dev

# 터미널 2 - 프론트엔드
cd client
npm start

브라우저에서 http://localhost:3000 접속하여 작업 생성, 수정, 삭제가 정상 작동하는지 확인합니다.

Heroku에 백엔드 배포

server 폴더에 Procfile을 생성합니다.

web: node index.js

Heroku CLI로 배포를 진행합니다.

cd server
heroku create your-app-name
heroku config:set MONGODB_URI="your-mongodb-uri"
heroku config:set NODE_ENV=production
git init
git add .
git commit -m "Initial commit"
git push heroku master

Vercel에 프론트엔드 배포

client 폴더에 .env.production 파일을 생성합니다.

REACT_APP_API_URL=https://your-app-name.herokuapp.com/api

Vercel CLI로 배포합니다.

cd client
npm run build
npx vercel --prod

React + Node.js 풀스택 앱 배포하기가 완료되었습니다. 이제 실제 URL을 통해 어디서든 애플리케이션에 접근할 수 있습니다.

마무리 및 확장 아이디어

이번 React + Node.js 풀스택 앱 배포하기 프로젝트를 통해 프론트엔드부터 백엔드, 데이터베이스, 배포까지 전체 개발 사이클을 경험했습니다. 이 기본 구조를 바탕으로 사용자 인증(JWT), 파일 업로드, 실시간 알림(Socket.io), 검색 및 필터링 기능을 추가할 수 있습니다. CI/CD 파이프라인을 구축하여 GitHub Actions로 자동 배포를 설정하거나, Docker를 활용한 컨테이너화, Redis를 이용한 캐싱 등 더 고급 기능으로 확장해보세요. 이 프로젝트는 포트폴리오에 추가하기에 완벽한 실전 경험이 될 것입니다.

📚 함께 읽으면 좋은 글

1

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

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

2

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

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

3

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

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

4

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

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

5

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

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

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

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

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

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

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

React + Node.js 풀스택 앱 배포하기에 대한 여러분만의 경험이나 노하우가 있으시나요?

💡
유용한 정보 공유

궁금한 점 질문

🤝
경험담 나누기

👍
의견 표현하기

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

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

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

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

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

💡
최신 트렌드
2025년 기준

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

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

답글 남기기