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

개발 에러 해결 가이드 - FixLog 노트

프로젝트 소개 및 목표

React + Node.js 풀스택 앱 배포하기는 프론트엔드부터 백엔드, 그리고 실제 서비스 배포까지 전 과정을 경험할 수 있는 실전 프로젝트입니다. 이 가이드에서는 React로 사용자 인터페이스를 구축하고, Node.js와 Express로 RESTful API 서버를 만들며, MongoDB를 활용한 데이터 저장, 그리고 Heroku와 Netlify를 통한 배포까지 완성합니다. 초보 개발자도 따라할 수 있도록 단계별로 상세히 설명하며, 완성 후에는 포트폴리오로 활용할 수 있는 실전 프로젝트가 됩니다. 풀스택 개발의 전체 흐름을 이해하고, 실무에서 바로 적용 가능한 배포 경험을 쌓을 수 있습니다.

필요한 기술 스택

이 프로젝트를 진행하기 위해 필요한 기술 스택은 다음과 같습니다. 프론트엔드는 React 18, React Router, Axios를 사용하며, 백엔드는 Node.js 18+, Express.js, MongoDB와 Mongoose를 활용합니다. 배포 플랫폼으로는 프론트엔드는 Netlify 또는 Vercel, 백엔드는 Heroku 또는 Railway를 사용합니다. 추가로 환경변수 관리를 위한 dotenv, CORS 처리, JWT 인증 라이브러리가 필요합니다. Node.js와 npm이 설치되어 있어야 하며, Git과 GitHub 계정도 준비해주세요.

프로젝트 셋업

먼저 프로젝트 폴더 구조를 생성합니다. 루트 디렉토리에 clientserver 폴더를 만들어 프론트엔드와 백엔드를 분리합니다. 백엔드 서버 설정을 위해 server 폴더로 이동하여 npm 프로젝트를 초기화하고, Express와 필요한 패키지들을 설치합니다. 프론트엔드는 Create React App 또는 Vite를 사용하여 React 프로젝트를 생성합니다. MongoDB Atlas에서 무료 클러스터를 생성하고 연결 문자열을 받아옵니다. 개발 환경에서는 백엔드를 포트 5000번, 프론트엔드를 포트 3000번에서 실행하며, CORS 설정으로 두 서버 간 통신을 허용합니다. .env 파일을 생성하여 데이터베이스 연결 정보와 API 키를 안전하게 관리합니다.

단계별 구현 과정

1단계: 백엔드 API 서버 구축

먼저 Express 서버의 기본 구조를 설정합니다. server/index.js 파일을 생성하고 다음과 같이 작성합니다:

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

dotenv.config();

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

// 미들웨어 설정
app.use(cors());
app.use(express.json());

// 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 서버입니다' });
});

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);

이제 CRUD 작업을 처리하는 라우트를 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.findByIdAndDelete(req.params.id);
    if (!task) return res.status(404).json({ message: '할일을 찾을 수 없습니다' });
    res.json({ message: '할일이 삭제되었습니다' });
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});

module.exports = router;

server/index.js에 라우트를 추가합니다:

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

2단계: React 프론트엔드 구축

프론트엔드에서는 먼저 API 통신을 위한 서비스 파일을 생성합니다. client/src/services/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 = (taskData) => api.post('/tasks', taskData);
export const updateTask = (id, taskData) => api.patch(`/tasks/${id}`, taskData);
export const deleteTask = (id) => api.delete(`/tasks/${id}`);

export default api;

메인 컴포넌트 client/src/App.js를 구현합니다:

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

function App() {
  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 })} className="input-field" /> setNewTask({ ...newTask, description: e.target.value })} className="input-field" />
{tasks.map((task) => (

handleToggle(task)}>{task.title}

{task.description &&

{task.description}

}
))}
); } export default App;

3단계: 환경 변수 설정

배포를 위해 환경 변수를 설정합니다. server/.env:

MONGODB_URI=your_mongodb_connection_string
PORT=5000

client/.env:

REACT_APP_API_URL=http://localhost:5000/api

테스트 및 배포

로컬 테스트: 백엔드와 프론트엔드를 동시에 실행하여 모든 기능이 정상 작동하는지 확인합니다. server 폴더에서 npm start로 백엔드를 실행하고, client 폴더에서 npm start로 프론트엔드를 실행합니다.

React + Node.js 풀스택 앱 배포하기 단계에서는 먼저 GitHub에 코드를 푸시합니다. 백엔드 배포는 Heroku를 사용합니다. Heroku CLI로 로그인 후 heroku create로 앱을 생성하고, MongoDB URI를 환경변수로 설정한 뒤 git push heroku main으로 배포합니다. 프론트엔드 배포는 Netlify를 사용합니다. Netlify에서 GitHub 저장소를 연결하고, build command를 npm run build, publish directory를 build로 설정합니다. 환경변수에 REACT_APP_API_URL을 Heroku 백엔드 URL로 설정하고 배포합니다.

배포 후에는 실제 운영 환경에서 모든 CRUD 기능이 정상 작동하는지 테스트하고, 브라우저 개발자 도구로 네트워크 요청을 확인합니다. SSL 인증서가 자동으로 적용되었는지 확인하고, 모바일 환경에서도 테스트를 진행합니다.

마무리 및 확장 아이디어

이제 React + Node.js 풀스택 앱 배포하기 프로젝트가 완성되었습니다! 실전 경험을 통해 풀스택 개발의 전체 흐름을 이해하고, 실제 서비스를 배포하는 능력을 갖추게 되었습니다. 이 프로젝트를 확장하려면 사용자 인증(JWT), 프로필 이미지 업로드(AWS S3), 실시간 기능(Socket.io), 검색 및 필터링, 페이지네이션, 다크모드 등을 추가할 수 있습니다. CI/CD 파이프라인을 구축하고, Docker 컨테이너화, 테스트 자동화도 도전해보세요. 완성된 프로젝트는 포트폴리오에 추가하여 취업이나 프리랜서 활동에 활용할 수 있습니다!

📚 함께 읽으면 좋은 글

1

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

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

2

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

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

3

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

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

4

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

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

5

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

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

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

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

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


📘 페이스북


🐦 트위터


✈️ 텔레그램

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

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

이 글에서 가장 도움이 된 부분은 어떤 것인가요?

💡
유용한 정보 공유

궁금한 점 질문

🤝
경험담 나누기

👍
의견 표현하기

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

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

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

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

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

💡
최신 트렌드
2025년 기준

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

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

📱 전체 버전 보기