본문 바로가기
개발 팁

풀스택 앱 무료 배포 완전 가이드 — Vercel + Railway + MongoDB Atlas

by 루까(Luka) 2026. 2. 25.
반응형

React + Node.js 풀스택 앱을 무료로 배포하는 방법은 크게 두 가지다. 백엔드를 Vercel Serverless로 처리하는 방법과 Railway에 독립 서버로 올리는 방법이다. 각각 장단점이 다르므로 프로젝트 성격에 맞게 선택하면 된다.


두 방법 비교

항목 Vercel (Serverless) Railway (Express 서버)
백엔드 형태 Serverless Function 항상 켜진 Express 서버
콜드 스타트 있음 (첫 요청 2~5초) 없음
Express 구조 변경 필요 (listen() 제거) 불필요 (그대로 사용)
WebSocket 불가 가능
무료 한도 넉넉함 월 $5 크레딧 제공
적합한 경우 간단한 REST API 복잡한 서버, 상태 유지 필요 시

간단한 CRUD API라면 Vercel Serverless로 충분하다. WebSocket이 필요하거나 Express 구조를 그대로 유지하고 싶다면 Railway가 낫다.


공통: MongoDB Atlas 설정

두 방법 모두 DB는 MongoDB Atlas M0 무료 티어를 쓴다.

클러스터 생성

  1. mongodb.com/atlas → 회원가입
  2. Create a deploymentM0 Free 선택
  3. Provider: AWS, Region: Seoul(ap-northeast-2) 선택
  4. Cluster Name 입력 → Create Deployment

DB 유저 생성

Database AccessAdd New Database User

  • Authentication: Password
  • Username / Password 설정
  • Role: Read and write to any database

네트워크 접근 허용

Network AccessAdd IP AddressAllow Access from Anywhere (0.0.0.0/0)

Vercel과 Railway 모두 서버 IP가 고정되어 있지 않아서 전체 허용이 필요하다.

Connection String 복사

DatabaseConnectDrivers → connection string 복사

mongodb+srv://<username>:<password>@cluster0.xxxxx.mongodb.net/<dbname>?retryWrites=true&w=majority

방법 1: Vercel Serverless (프론트 + 백엔드 통합)

Express 앱을 Serverless Function으로 변환해서 Vercel 하나로 처리한다.

백엔드 구조 변경

backend/
├── api/
│   └── index.js       ← Vercel 진입점
├── src/
│   ├── app.js         ← app.listen() 제거, app만 export
│   ├── routes/
│   └── models/
├── vercel.json
└── package.json

app.js에서 listen() 제거:

// src/app.js
import express from 'express'
import cors from 'cors'
import mongoose from 'mongoose'

const app = express()

app.use(cors({
  origin: process.env.FRONTEND_URL || 'http://localhost:5173',
  credentials: true,
}))
app.use(express.json())

mongoose.connect(process.env.MONGODB_URI)

app.use('/api/auth', authRouter)
app.use('/api/plans', plansRouter)

export default app  // listen() 없이 export만

api/index.js 생성:

import app from '../src/app.js'
export default app

vercel.json 생성:

{
  "version": 2,
  "builds": [{ "src": "api/index.js", "use": "@vercel/node" }],
  "routes": [{ "src": "/api/(.*)", "dest": "api/index.js" }]
}

Vercel 배포

백엔드

  1. Vercel → Add New Project → 저장소 선택
  2. Root Directory: backend
  3. Environment Variables:
MONGODB_URI  = mongodb+srv://...
JWT_SECRET   = 랜덤 문자열
FRONTEND_URL = https://your-frontend.vercel.app

프론트엔드

  1. Add New Project → 같은 저장소 선택
  2. Root Directory: frontend
  3. Environment Variables:
VITE_API_URL = https://your-backend.vercel.app/api

Vercel Serverless 주의사항

콜드 스타트: 일정 시간 요청이 없으면 인스턴스가 내려간다. 다음 첫 요청 시 DB 연결부터 시작해 2~5초 지연이 생긴다. mongoose 연결을 캐싱하면 같은 인스턴스 내 재요청 속도를 높일 수 있다.

// src/db.js — mongoose 연결 캐싱
let cached = global.mongoose || { conn: null, promise: null }
global.mongoose = cached

export async function connectDB() {
  if (cached.conn) return cached.conn
  if (!cached.promise) {
    cached.promise = mongoose.connect(process.env.MONGODB_URI)
  }
  cached.conn = await cached.promise
  return cached.conn
}

WebSocket 불가: Serverless는 요청-응답 구조라 지속 연결이 필요한 WebSocket을 지원하지 않는다.


방법 2: Railway (독립 Express 서버)

Express 구조를 그대로 유지하면서 Railway에 서버를 올린다. 코드 수정이 거의 없다.

railway.json 생성 (선택)

Railway는 package.json의 scripts를 자동으로 감지하지만, 명시적으로 설정하고 싶다면 추가한다.

{
  "$schema": "https://railway.app/railway.schema.json",
  "build": {
    "builder": "NIXPACKS"
  },
  "deploy": {
    "startCommand": "node src/server.js",
    "restartPolicyType": "ON_FAILURE"
  }
}

포트 설정

Railway는 환경 변수 PORT를 자동으로 주입한다. 서버 코드에서 반드시 이 값을 사용해야 한다.

// src/server.js
const PORT = process.env.PORT || 5001
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`)
})

Railway 배포

  1. railway.app → 회원가입 (GitHub 연동)
  2. New ProjectDeploy from GitHub repo → 저장소 선택
  3. Root Directory: backend
  4. Variables 탭에서 환경 변수 설정:
NODE_ENV         = production
MONGODB_URI      = mongodb+srv://...
JWT_SECRET       = 랜덤 문자열
GOOGLE_MAPS_API_KEY = ...
ODSAY_API_KEY    = ...
CORS_ORIGIN      = https://your-frontend.vercel.app
  1. NetworkingPublic NetworkingGenerate Domain (또는 Custom Domain)
  2. 생성된 URL 확인: https://your-backend.up.railway.app

Vercel 프론트엔드 배포

백엔드 Railway URL을 환경 변수에 입력한다.

VITE_API_URL = https://your-backend.up.railway.app/api

CORS 설정

Railway 배포 시 CORS_ORIGIN 환경 변수를 활용한다.

// src/app.js
app.use(cors({
  origin: [
    'http://localhost:5173',
    process.env.CORS_ORIGIN,  // Vercel 프론트엔드 URL
  ],
  credentials: true,
}))

Railway 무료 한도

Railway는 매달 $5 크레딧을 무료로 제공한다. 소규모 서버는 이 범위 안에서 운영 가능하다. 트래픽이 늘면 유료 플랜으로 전환해야 한다.


공통: 배포 후 자주 겪는 문제

API 호출이 안 된다

환경 변수를 추가했는데 반영이 안 됐다면 Redeploy를 한 번 해야 한다. 환경 변수는 배포 시점에 주입되기 때문이다.

CORS 오류

브라우저 콘솔에 CORS policy 에러가 뜨면 백엔드 origin에 프론트엔드 URL이 정확히 들어가 있는지 확인한다. 끝에 / 슬래시 하나 차이로도 막힌다.

// ❌ 와일드카드 + credentials 조합은 동작하지 않는다
origin: '*', credentials: true

// ✅ 도메인을 명시해야 한다
origin: ['http://localhost:5173', process.env.CORS_ORIGIN]

MongoDB 연결 실패

MongooseServerSelectionError: connection timed out

Network Access에서 0.0.0.0/0이 등록되어 있는지 확인한다. 등록 후 수 분이 지나야 적용된다. connection string의 <password>에 특수문자(@, # 등)가 있다면 URL 인코딩(%40, %23)이 필요하다.


배포 후 체크리스트

□ 프론트엔드 URL 접속 → 정상 로드 확인
□ 회원가입 → Atlas Data Explorer에서 DB 저장 확인
□ 로그인 → JWT 토큰 발급 확인
□ 인증이 필요한 API 호출 → 정상 응답 확인
□ 브라우저 콘솔 CORS 에러 없는지 확인
□ 모바일에서 접속 확인

정리

상황 추천
간단한 REST API, 빠르게 배포 Vercel Serverless
Express 코드 그대로 유지, WebSocket 필요 Railway
콜드 스타트 허용 불가 Railway
완전 무료 유지 Vercel Serverless

두 방법 모두 GitHub push 시 자동 재배포된다. 로컬에서 개발하고 push하면 배포까지 자동으로 처리된다.

반응형