Heroku 대안 비교: Vercel·Netlify·Railway·Render·Fly.io의 적합한 용도
이 글의 핵심
Heroku를 대신할 PaaS 7곳을 가격표가 아니라 실행 모델(서버리스 함수인가, 상시 실행 컨테이너인가)과 과금 방식(정액인가, 사용량 기반인가) 중심으로 비교합니다. 요금과 무료 제공량은 자주 바뀌므로 작성 시점 기준의 성격만 적고, 정확한 금액은 각 공식 요금 페이지에서 확인하도록 안내합니다.
2022년 11월 말, Heroku가 무료 dyno와 무료 Postgres·Redis를 종료하면서 사이드 프로젝트를 Heroku에 올려 두던 많은 개발자가 다른 곳을 찾기 시작했습니다. 선택지는 많지만, 요금표를 나란히 놓고 비교하면 오히려 헷갈립니다. 플랫폼마다 실행 모델이 다르기 때문입니다.
이 글은 가격 숫자보다 먼저 두 가지 질문으로 플랫폼을 나눕니다.
- 코드가 어떻게 실행되는가? 요청이 올 때만 뜨는 서버리스 함수인가, 계속 떠 있는 프로세스(컨테이너)인가.
- 어떻게 과금되는가? 플랜별 정액인가, CPU·메모리·트래픽 사용량 기반인가.
이 두 가지가 정해지면 “WebSocket 서버를 올릴 수 있는가”, “요금이 갑자기 늘 수 있는가”, “첫 요청이 느린가” 같은 질문의 답이 대부분 따라 나옵니다.
요금에 관한 안내: 이 글의 요금·무료 제공량 설명은 작성 시점(2026년 9월) 기준의 대략적인 성격만 적었습니다. PaaS 요금제는 1년에도 몇 번씩 바뀌므로, 결정하기 전에 반드시 각 플랫폼의 공식 요금 페이지를 확인하세요.
전체 비교
| 플랫폼 | 실행 모델 | 과금 방식 | Docker | DB 통합 | 무료로 시작 |
|---|---|---|---|---|---|
| Vercel | 서버리스 함수 + CDN | 플랜 + 사용량 | 불가 | 외부 연동 | 개인·비상업용 무료 플랜 |
| Netlify | 정적 호스팅 + 서버리스 함수 | 플랜 + 사용량 | 불가 | 외부 연동 | 무료 플랜 |
| Railway | 상시 실행 컨테이너 | 사용량 기반 | 가능 | 대시보드에서 생성 | 체험 크레딧 |
| Render | 상시 실행 컨테이너 + 정적 사이트 | 인스턴스 정액 | 가능 | 대시보드에서 생성 | 무료 인스턴스(유휴 시 중지) |
| Fly.io | 전 세계 리전의 경량 VM(Machines) | 사용량 기반 | 가능(사실상 필수) | 자체 Postgres 옵션 | 정책 변경이 잦음 |
| AWS Amplify | 정적 호스팅 + AWS 서버리스 백엔드 | AWS 종량제 | 불가(호스팅 기준) | AWS 서비스 | AWS 프리 티어 조건 |
| DO App Platform | 컨테이너 + 정적 사이트 | 인스턴스 정액 | 가능 | 관리형 DB 연결 | 정적 사이트 일부 무료 |
표만 보면 비슷해 보이지만, 실제로 갈리는 지점은 “서버리스냐 상시 실행이냐”입니다. 서버리스 쪽(Vercel, Netlify, Amplify)은 트래픽이 없을 때 비용이 거의 들지 않는 대신 함수 실행 시간 제한과 콜드 스타트가 있고, WebSocket처럼 연결을 오래 붙잡는 서버에는 맞지 않습니다. 상시 실행 쪽(Railway, Render, Fly.io, DigitalOcean)은 Heroku와 사고방식이 같아서 옮기기 쉽지만, 트래픽이 없어도 인스턴스가 떠 있는 만큼 비용이 나갑니다.
서버리스 쪽을 고를 때 자주 놓치는 비용이 데이터베이스 연결 수입니다. 상시 실행 서버는 프로세스 하나가 커넥션 풀을 만들어 재사용하지만, 서버리스 함수는 동시 요청마다 인스턴스가 따로 뜨고 각 인스턴스가 자기 연결을 엽니다. 트래픽이 몰리면 PostgreSQL의 max_connections를 넘겨 sorry, too many clients already 같은 에러가 나기 쉽습니다. Heroku에서 서버 하나로 잘 돌던 앱을 Vercel로 옮긴 뒤 부하가 생기는 순간 DB 연결 에러가 나는 것은 이 때문이며, PgBouncer 같은 커넥션 풀러를 거치거나 Neon·Supabase가 제공하는 풀링용 연결 문자열을 써야 합니다.
Vercel: Next.js와 프론트엔드 중심
Vercel은 Next.js를 만든 회사이고, Next.js의 새 기능(서버 컴포넌트, ISR, 이미지 최적화, 미들웨어)을 설정 없이 가장 먼저 지원합니다. Git 저장소를 연결하면 브랜치·PR마다 프리뷰 URL이 생기는 흐름은 프론트엔드 팀에게 특히 편합니다.
주의할 점
- 서버 코드는 서버리스 함수로 실행됩니다. 함수마다 최대 실행 시간과 번들 크기 제한이 있고, 플랜에 따라 한도가 다릅니다. 오래 걸리는 작업이나 상시 연결(WebSocket 서버)은 다른 곳에 두는 편이 낫습니다.
- 무료 Hobby 플랜은 개인·비상업적 용도로 제한됩니다. 회사 프로젝트나 수익이 나는 서비스라면 처음부터 유료 플랜을 기준으로 계산해야 합니다.
- 데이터베이스는 직접 호스팅하지 않고 마켓플레이스 통합(Neon, Upstash, Supabase 등)으로 연결합니다.
npm install -g vercel
cd my-nextjs-app
vercel # 프리뷰 배포
vercel --prod # 프로덕션 배포
맞는 프로젝트: Next.js 앱, React·Vue SPA, 정적 사이트, 가벼운 API 라우트. 맞지 않는 프로젝트: 장시간 실행 작업, WebSocket 서버, 큐 워커.
Netlify: 정적 사이트와 JAMstack
Netlify는 정적 사이트 호스팅을 대중화한 플랫폼입니다. Hugo·Astro·Gatsby 같은 정적 사이트 생성기로 만든 블로그나 문서 사이트에 잘 맞고, 폼 처리(Netlify Forms), 리다이렉트 규칙, 브랜치 배포 같은 부가 기능이 기본으로 들어 있습니다.
서버 코드는 Netlify Functions(서버리스)로 실행되므로 Vercel과 같은 종류의 제약이 있습니다. 요청이 드문 함수는 콜드 스타트로 첫 응답이 느릴 수 있고, 복잡한 백엔드를 올리는 용도는 아닙니다.
npm install -g netlify-cli
netlify login
netlify deploy # 초안 배포
netlify deploy --prod # 프로덕션 배포
맞는 프로젝트: 정적 블로그·문서·마케팅 사이트, SPA, 간단한 폼 처리.
Railway: Heroku와 가장 비슷한 경험
Railway는 Heroku에서 넘어오는 사람이 가장 덜 낯설어하는 플랫폼입니다. 저장소를 연결하면 언어를 감지해 빌드하고(Dockerfile이 있으면 그것을 사용), 같은 프로젝트 화면에서 PostgreSQL·MySQL·Redis를 추가해 환경 변수로 연결할 수 있습니다. 웹 서버, 워커, DB를 한 프로젝트에 묶어 관리하기 편합니다.
과금 방식이 핵심입니다. Railway는 인스턴스 크기를 고르는 정액제가 아니라 실제로 쓴 CPU·메모리·네트워크만큼 과금합니다. 작은 앱은 저렴하게 유지되지만, 메모리 누수가 있거나 트래픽이 늘면 청구액도 그대로 따라 늘어납니다. 사용량 알림과 한도(usage limit)를 처음부터 설정해 두는 것이 좋습니다.
npm install -g @railway/cli
railway login
railway init
railway up
railway domain # 공개 도메인 생성
맞는 프로젝트: Node·Python·Go 백엔드, 풀스택 앱, 워커가 딸린 서비스, 여러 서비스를 한 프로젝트로 묶는 구성.
Render: 예측 가능한 정액 인스턴스
Render는 웹 서비스, 백그라운드 워커, 크론 잡, 정적 사이트, PostgreSQL, Key Value(Redis 호환)를 한곳에서 제공합니다. Railway와 기능은 비슷하지만 과금이 인스턴스 크기별 정액이라 월 비용을 예측하기 쉽습니다.
무료 인스턴스의 특성: 무료 웹 서비스는 일정 시간 요청이 없으면 중지(spin down)되고, 다음 요청이 오면 다시 띄우느라 첫 응답이 수십 초 가까이 걸릴 수 있습니다. 무료 PostgreSQL은 생성 후 일정 기간이 지나면 만료됩니다. 데모나 포트폴리오에는 충분하지만, 실제 사용자가 있는 서비스라면 유료 인스턴스를 기준으로 생각해야 합니다.
render.yaml(Blueprint)로 서비스와 DB를 코드로 정의할 수 있습니다.
# render.yaml
services:
- type: web
name: my-app
runtime: node
buildCommand: npm ci
startCommand: npm start
envVars:
- key: DATABASE_URL
fromDatabase:
name: mydb
property: connectionString
databases:
- name: mydb
databaseName: mydatabase
user: myuser
Blueprint 파일은 저장소에 커밋해 두면 새 환경을 만들 때 대시보드 클릭을 반복하지 않아도 된다는 점이 장점이지만, 비밀 값은 여기에 적지 않습니다. sync: false로 표시한 환경 변수는 대시보드에서 따로 입력하게 되어 있으므로, API 키 같은 값은 그 방식으로 넣어야 저장소에 새어 나가지 않습니다.
맞는 프로젝트: 월 비용을 고정하고 싶은 백엔드, 크론 잡이 있는 서비스, Heroku의 web/worker 구성을 그대로 옮기는 경우.
Fly.io: 사용자 가까이에서 실행
Fly.io는 Docker 이미지를 Firecracker 기반 경량 VM(Machines)으로 바꿔 전 세계 여러 리전에 띄웁니다. 사용자가 여러 대륙에 퍼져 있고 지연 시간이 중요한 서비스, WebSocket이나 게임 서버처럼 상시 연결이 필요한 서비스에 강점이 있습니다. 내부 네트워크(WireGuard 기반)와 fly ssh console로 머신에 직접 접속하는 기능도 제공합니다.
대신 다른 PaaS보다 인프라에 가까운 개념(리전, 볼륨, 머신 스케일링)을 직접 이해해야 합니다. 볼륨은 특정 머신·리전에 묶이므로, 여러 리전에 앱을 띄우면 데이터 계층을 어떻게 둘지도 함께 설계해야 합니다. 과금은 사용량 기반이고 신규 가입자 무료 제공량 정책이 여러 번 바뀌어 왔으므로 현재 조건을 꼭 확인하세요.
curl -L https://fly.io/install.sh | sh
fly auth login
cd my-app
fly launch # Dockerfile 감지 후 fly.toml 생성
fly deploy
fly logs
fly launch가 만든 fly.toml에는 요청이 없을 때 머신을 멈추고 요청이 오면 다시 띄우는 자동 중지·시작 설정이 기본으로 들어가는 경우가 많습니다. 비용은 줄지만 멈춘 뒤 첫 요청은 머신 부팅을 기다려야 하고, 백그라운드에서 큐를 소비하거나 스케줄러를 돌리는 프로세스라면 요청이 없다는 이유로 멈춰 작업이 조용히 밀릴 수 있습니다. 상시 실행이 필요한 프로세스는 최소 실행 머신 수를 1 이상으로 두어야 합니다.
맞는 프로젝트: 멀티 리전 배포, 저지연 API, WebSocket 서버, Docker에 익숙한 팀.
AWS Amplify: 이미 AWS를 쓰는 팀
Amplify는 프론트엔드 호스팅과 CI/CD에, Cognito(인증)·AppSync(GraphQL)·Lambda·DynamoDB 같은 AWS 서비스를 백엔드로 붙이는 도구입니다. 조직이 이미 AWS 계정·IAM·결제 체계를 갖추고 있다면 새 벤더를 들이지 않아도 된다는 점이 가장 큰 장점입니다.
반대로 AWS를 처음 쓰는 개인에게는 IAM 권한, 서비스별 종량제 요금, 리전 개념까지 한꺼번에 배워야 해서 부담이 큽니다. 요금은 사용한 AWS 서비스별로 따로 청구되므로, 결제 알림(AWS Budgets)을 먼저 설정하는 것을 권합니다.
맞는 프로젝트: AWS 중심 조직의 웹 앱, Cognito 인증이 필요한 SPA, 서버리스 아키텍처.
DigitalOcean App Platform: 단순한 정액 컨테이너
App Platform은 DigitalOcean 위에서 컨테이너와 정적 사이트를 실행하는 PaaS입니다. 화면 구성이 단순하고 인스턴스 크기별 정액이라 비용이 예측 가능합니다. 정적 사이트는 일정 개수까지 무료로 올릴 수 있고, 컨테이너 서비스와 관리형 데이터베이스는 유료입니다. 이미 DigitalOcean Droplet이나 관리형 DB를 쓰고 있다면 같은 계정 안에서 묶어 관리하기 좋습니다.
맞는 프로젝트: 단순한 백엔드 API, 예측 가능한 월 비용이 중요한 소규모 서비스.
프로젝트 유형별 선택
| 프로젝트 유형 | 먼저 볼 곳 | 이유 |
|---|---|---|
| 정적 블로그·문서 | Netlify, Vercel | CDN·HTTPS·브랜치 배포가 기본, 정적 트래픽은 저렴 |
| Next.js 앱 | Vercel | 새 기능 지원이 가장 빠르고 설정이 거의 없음 |
| 백엔드 + DB | Railway, Render | DB를 같은 곳에서 만들고 환경 변수로 연결 |
| WebSocket·상시 연결 | Fly.io, Render, Railway | 요청마다 뜨는 함수가 아니라 계속 떠 있는 프로세스 |
| 멀티 리전 저지연 | Fly.io | 여러 리전에 머신을 띄우는 것이 기본 모델 |
| AWS 중심 조직 | AWS Amplify | 기존 IAM·결제·서비스와 통합 |
제가 선택할 때 가장 먼저 보는 것은 “이 앱에 연결을 오래 붙잡는 코드가 있는가”입니다. WebSocket, 서버 전송 이벤트(SSE), 큐 소비자, 긴 배치 작업이 있으면 서버리스 플랫폼은 후보에서 빠지고, 없으면 트래픽이 적을 때 비용이 거의 0에 가까운 서버리스 쪽이 유리한 경우가 많습니다.
Heroku에서 옮길 때
확인할 것
- 환경 변수:
heroku config로 목록을 뽑아 새 플랫폼에 옮깁니다.DATABASE_URL은 새 DB 주소로 바뀌므로 그대로 복사하지 않습니다. - 데이터베이스:
heroku pg:backups:capture후heroku pg:backups:download로 덤프를 받고pg_restore로 새 DB에 복원합니다. 대상 DB의 PostgreSQL 메이저 버전이 같거나 높은지 먼저 확인하세요. - Procfile:
web:프로세스는 새 플랫폼의 시작 명령으로,worker:·release:프로세스는 별도 서비스나 배포 전 명령(pre-deploy command)으로 옮깁니다.release:단계의 마이그레이션을 빠뜨리는 실수가 흔합니다. - 포트: Heroku처럼 대부분의 플랫폼이
PORT환경 변수를 주입합니다. 코드에 포트를 하드코딩했다면process.env.PORT를 읽도록 바꿉니다. - 도메인: 새 플랫폼에서 인증서가 발급된 것을 확인한 뒤 DNS를 바꿉니다. 전환 전 TTL을 낮춰 두면 문제가 생겼을 때 되돌리기 쉽습니다.
- 로컬 파일 시스템: Heroku dyno의 파일 시스템은 재시작하면 사라지는 임시 공간이었고, 대부분의 대안도 마찬가지입니다. 사용자가 올린 파일을 로컬 디스크에 저장하고 있었다면 S3 같은 오브젝트 스토리지로 옮겨야 하며, 영구 디스크(볼륨)를 붙일 수 있는 플랫폼이라도 볼륨이 인스턴스 하나에 묶여 수평 확장이 막힌다는 점을 감안해야 합니다.
- DB 연결 옵션: Heroku Postgres는 SSL 연결을 요구해서 코드에
ssl: { rejectUnauthorized: false }같은 설정을 넣어 둔 경우가 많습니다. 새 DB가 사설 네트워크 주소를 주거나 인증서 체계가 다르면 이 설정 때문에 연결이 실패하거나 반대로 불필요하게 검증을 끈 상태가 남으므로, 새 플랫폼 문서에 맞게 다시 정합니다.
옮기는 작업에서 제가 가장 조심하는 부분은 데이터 이전과 트래픽 전환 사이의 틈입니다. 덤프를 뜬 뒤 DNS를 바꾸기 전까지 옛 Heroku 앱에 들어온 쓰기는 새 DB에 없습니다. 사용자가 있는 서비스라면 옛 앱을 유지보수 모드(heroku maintenance:on)로 돌려 쓰기를 멈춘 다음 마지막 덤프를 뜨고, 복원과 DNS 전환을 끝낸 뒤 새 쪽에서 로그인·결제 같은 핵심 흐름을 확인하는 순서를 지키는 편이 안전합니다. 옛 앱과 DB는 문제가 없음을 확인할 때까지 며칠 남겨 두어야 되돌릴 길이 있습니다.
Railway로 옮기는 예
# 1. Heroku 환경 변수 내보내기 (DATABASE_URL 등은 새 값으로 교체)
heroku config --shell > heroku.env
# 2. Railway 프로젝트 생성 및 DB 추가 (DB는 대시보드에서 추가)
railway init
# 3. 시작 명령은 railway.json 또는 대시보드에서 지정
{
"deploy": {
"startCommand": "npm start"
}
}
# 4. 배포 및 도메인 생성
railway up
railway domain
내보낸 heroku.env에는 비밀 값이 들어 있으므로 저장소에 커밋되지 않도록 .gitignore에 넣고, 옮긴 뒤에는 지우는 것이 안전합니다.
서버리스형과 상시 실행형부터 나누기
- 서버리스 함수 중심(Vercel, Netlify, Amplify)과 상시 실행 컨테이너(Railway, Render, Fly.io, DigitalOcean)로 먼저 나누면 선택이 쉬워집니다.
- 과금은 정액(Render, DigitalOcean)과 사용량 기반(Railway, Fly.io)으로 나뉘고, 사용량 기반이라면 한도·알림을 처음부터 설정하세요.
- 무료 플랜에는 유휴 시 중지, DB 만료, 상업적 사용 제한 같은 조건이 붙습니다. 실제 사용자가 있는 서비스는 유료 플랜 기준으로 계산하세요.
- 요금과 무료 조건은 자주 바뀝니다. 이 글의 표는 성격 비교용이고, 금액은 공식 요금 페이지에서 확인해야 합니다.