제로에서 프로덕션까지: 정신을 잃지 않고 VPS에 앱을 배포한 방법
I’m happy to translate the article for you, but I’ll need the text you’d like translated. Could you please paste the content (or the portion you want translated) here? I’ll keep the source line exactly as you provided and preserve all formatting, code blocks, URLs, and technical terms.
전제
모든 개발자는 그런 순간을 겪습니다: 자랑스러운 무언가를 만들었고, 내 노트북에서는 잘 작동하고, 친구들은 기다리고, 사용자들은 준비가 되었다… 그런데 누군가가 “그렇다면 실제로 사람들은 어떻게 쓰나요?” 라고 묻습니다.
그 질문은 나를 하루 종일 이어진 토끼굴로 이끌었습니다—한밤중에 VPS를 구매하고, 원격 서버에서 깜빡이는 커서를 바라보며, 마침내 올바른 **https://**가 적용된 라이브 도메인에서 브라우저로 내 앱이 로드되는 모습을 보는 것까지. 이것이 그 이야기입니다.
왜 VPS인가?
VPS(가상 사설 서버)를 호텔 방 대신 아파트를 임대하는 것에 비유해 보세요.
- Shared hosting (Heroku, Railway 등) – 호텔: 편리하고 관리가 되지만, 낯선 사람들과 벽을 공유하고 규칙이 여러분 것이 아닙니다.
- VPS – 아파트: 여러분만의 공간이며 모든 것을 직접 제어하지만, 배관 문제 등 모든 책임을 스스로 져야 합니다.
저는 OVHcloud를 선택했습니다. 이들의 입문용 VPS 플랜은 가격 경쟁력이 뛰어나며, 특히 동일 사양의 AWS나 DigitalOcean에 비해 저렴합니다. 저는 Ubuntu 24.04가 설치된 VPS를 주문했으며, 몇 분 안에 SSH 자격 증명이 포함된 이메일을 받았습니다.
TIP: 데이터 센터 지역을 선택할 때는 대부분의 사용자가 위치한 곳에 가장 가까운 지역을 고르세요. 지연 시간은 인지된 성능을 저해하는 조용한 살인자입니다. 사용자가 라고스에 있다면 오리건에 호스팅하지 마세요.
첫 로그인
VPS IP와 루트 자격 증명이 포함된 이메일이 도착하면, 마치 빈 아파트의 열쇠를 건네받은 듯한 느낌이 듭니다. 들어가서 둘러보면 아직 아무것도 없다는 것을 알게 됩니다—가구도, 장식도 없고, 오직 당신의 도구와 야망만이 남아 있습니다. 그래서 가장 먼저 하는 일은 로그인하는 것입니다.
ssh root@YOUR_VPS_IP
시스템 업데이트
apt update && apt upgrade -y
서버 잠금
왜?
서버가 프로비저닝되는 순간, 이미 열린 포트를 스캔하는 자동화된 봇들의 표적이 됩니다. 서버는 퍼블릭 인터넷에 존재하며—퍼블릭 인터넷은 친절하지 않습니다. 단 하나의 의존성을 설치하거나 설정 파일을 작성하기 전에, 먼저 누가 머신에 접근할 수 있는지 잠궈야 합니다. 새 집 주위에 울타리를 치는 것과 같습니다.
UFW 설치 및 설정
UFW(Uncomplicated Firewall)는 더 복잡한 iptables 방화벽 규칙 위에 있는 Ubuntu의 친절한 래퍼입니다. 철학: 기본적으로 모든 것을 차단하고, 필요한 것만 연다.
# Install UFW
apt install ufw -y
# Deny ALL incoming connections by default
ufw default deny incoming
# Allow all outgoing (your server needs to reach the internet)
ufw default allow outgoing
# Always allow SSH first — if you forget this and enable the firewall,
# you will lock yourself out permanently. Don't be that person.
ufw allow 22/tcp
# Allow HTTP and HTTPS for the web
ufw allow 80/tcp
ufw allow 443/tcp
# Allow port 3000 for Coolify's dashboard
ufw allow 3000/tcp
# Enable the firewall
ufw enable
규칙 확인:
ufw status verbose
비유: UFW는 아파트 건물 입구의 경비원과 같습니다. 그가 명시적으로 허용한 포트(거주자)만 들여보내고 나머지는 모두 돌려보냅니다. 열어두지 않은 모든 포트는 외부 세계에 존재하지 않는 문과 같습니다.
Fail2ban으로 능동적인 보호 추가
UFW는 수동적입니다; 단순히 문을 차단할 뿐입니다. 누군가가 적극적으로 문을 두드리며 들어오려 하면 어떨까요? SSH 무차별 로그인 공격은 실제 존재합니다. 누군가가 인터넷상의 모든 IP에 봇을 띄워 admin/admin, root/password, root/123456 등을 분당 수천 번 시도합니다. 보호가 없으면 서버는 그 상황을 그대로 받아들일 뿐입니다.
Fail2ban은 로그를 감시하고 일정 횟수 이상 실패한 IP를 일시적으로 차단합니다—반복 위반자를 내쫓는 바운서와 같습니다.
apt install fail2ban -y
로컬 설정 파일을 생성합니다(기본 파일을 직접 편집하면 업데이트 시 덮어씌워집니다):
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
nano /etc/fail2ban/jail.local
[sshd] 섹션을 찾아 다음과 같이 설정합니다:
[sshd]
enabled = true
port = ssh
maxretry = 5
bantime = 3600
findtime = 600
서비스를 활성화하고 시작합니다:
systemctl enable fail2ban
systemctl start fail2ban
누가 차단됐는지 확인:
fail2ban-client status sshd
비유: UFW가 집 주변의 벽이라면, Fail2ban은 손잡이를 다섯 번 이상 흔드는 사람에게 자동으로 경찰을 부르는 초인종 카메라와 같습니다.
이제 서버가 강화되었습니다. 울타리와 바운서가 생겼습니다. 여기 있어서는 안 될 사람은 들어올 수 없습니다.
Coolify로 애플리케이션 배포
이제 재미있는 부분—실제로 서버에 뭔가를 올리는 단계입니다. 어려운 방법을 선택할 수도 있습니다: Nginx를 수동으로 설치하고, 설정 파일을 작성하고, Docker를 직접 관리하며, 새벽 2시에 SSL 인증서를 잡아먹는 식으로 말이죠. 아니면 Coolify를 설치하면 브라우저 UI를 통해 이 모든 작업을 대신 해줍니다.
Coolify는 오픈소스이며 자체 호스팅이 가능한 Platform‑as‑a‑Service (PaaS)로, 저수준 인프라(Docker, Nginx, SSL, CI/CD)를 추상화하고 앱, 데이터베이스, 워커를 배포할 수 있는 깔끔한 대시보드를 제공합니다.
(이 가이드의 나머지 부분에서는 Coolify 설치, 앱 배포, SSL 설정 및 배포 후 팁을 계속해서 다룹니다.)
Coolify – 당신의 자체 호스팅 “Heroku”
Coolify를 Heroku‑ 또는 Railway‑ 스타일의 플랫폼으로 생각하면 되지만, 서버와 데이터를 직접 소유하고 VPS 제공업체에 직접 비용을 지불합니다. 다음을 처리합니다:
- 배포
- SSL 인증서
- 환경 변수
- 데이터베이스
- 리버스‑프록시
모두 깔끔한 웹 UI를 통해 제공됩니다.
1️⃣ 설치 (One‑Liner)
wget -q https://get.coollabs.io/coolify/install.sh -O install.sh
sudo bash install.sh
설치 프로그램은:
- Docker 설치 (Coolify는 모든 것을 컨테이너에서 실행합니다)
- Coolify 자체 컨테이너 설정
- 서비스를 포트 3000에서 시작합니다
2️⃣ 첫 실행 – 설정 마법사
브라우저를 열고 다음 주소로 이동합니다:
http://YOUR_VPS_IP:3000
Coolify 설정 마법사가 표시됩니다. 관리자 계정을 생성하면 바로 시작할 수 있습니다.
비유:
Coolify = 땅을 구입하고 직접 쇼핑몰을 건설하는 것 (전체 제어).
Heroku = 다른 사람의 몰에 부스를 임대하는 것 (편리하지만 제한적).
3️⃣ 데이터베이스 추가
모든 프로덕션 앱은 영구적인 데이터 저장소가 필요합니다.
- 탐색:
Databases → PostgreSQL → New - 버전 선택 (PostgreSQL 15 또는 16)
- 이름을 지정하고 Deploy 클릭
Coolify는 Docker 내부에 DB를 프로비저닝합니다. 내부 연결 문자열을 복사하세요:
postgresql://postgres:your_password@YOUR_VPS_IP:5432/postgres
왜 PostgreSQL을 선택하고 SQLite를 사용하지 않나요?
SQLite는 파일 기반 DB로 개발 단계에서는 좋지만, 재배포 시 파일이 사라집니다. PostgreSQL은 제대로 된 영구적이며 동시성을 지원하는 서버입니다.
4️⃣ 백엔드 (API) 배포
4.1 새 애플리케이션 만들기
- 메뉴:
Applications → New - GitHub 저장소 연결
- backend 디렉터리 선택
- 빌드 팩으로 Nixpacks 선택 (Node.js 자동 감지)
4.2 환경 변수 설정
| Variable | Value |
|---|---|
NODE_ENV | production |
DATABASE_URL | postgresql://postgres:your_password@YOUR_VPS_IP:5432/postgres |
JWT_SECRET | 아주 긴 무작위 문자열 |
FRONTEND_URL | https://pos.yourdomain.com |
PORT | 3000 |
강력한 JWT 비밀키를 생성하세요 (Git에 절대 커밋하지 말 것):
openssl rand -hex 32
4.3 도메인 할당 및 배포
- 도메인:
https://api.yourdomain.com - Deploy 클릭
Coolify는 다음을 수행합니다:
- GitHub에서 코드를 가져옴
- Nixpacks로 빌드
- Docker 컨테이너에서 실행
- Traefik (내장 리버스 프록시)를 통해 노출
- 자동으로 무료 Let’s Encrypt SSL 인증서 요청
/health 엔드포인트가 응답을 반환하면 API가 정상적으로 운영됩니다.
5️⃣ 프론트엔드 배포 (React/Vite)
5.1 다른 애플리케이션 만들기
Applications → New(같은 레포)- frontend 디렉터리 선택
- Build pack: Static / Vite
5.2 환경 변수
VITE_API_URL=https://api.yourdomain.com
5.3 도메인 및 Rewrite 규칙
- 도메인:
https://pos.yourdomain.com - Static site config – SPA가 항상
index.html을 제공하도록 rewrite 규칙을 추가:
/* → /index.html
배포하고 로그를 확인하세요. “Build successful” 가 표시되면 정상적으로 배포된 것입니다.
6️⃣ DNS – 도메인을 VPS에 연결하기
Your services are still reachable only via the IP address. Add A records at your DNS provider (example uses Namecheap):
| Host | Value (VPS IP) | TTL |
|---|---|---|
api | YOUR_VPS_IP | Automatic |
pos | YOUR_VPS_IP | Automatic |
DNS 전파는 최대 48시간이 걸릴 수 있지만, 보통 몇 분 안에 해결됩니다.
7️⃣ 모든 것 확인하기
새로운(시크릿 모드가 아닌) 탭을 엽니다:
https://pos.yourdomain.com
-
잠금 아이콘이 있는 로그인 페이지 → 정상 작동 중입니다.
-
Coolify 오류 / 502 → 문제 해결:
- 백엔드 상태 확인:
https://api.yourdomain.com/health - Coolify에서 앱 로그 보기 (
App → Logs) - 환경 변수 확인 (특히
DATABASE_URL)
- 백엔드 상태 확인:
프로덕션 디버깅은
console.log가 아니라 로그를 읽는 것을 의미하며, 변경 사항마다 재배포가 트리거됩니다.
8️⃣ 보안 추가 기능 (UFW & Fail2Ban)
- UFW → 방화벽을 활성화하기 전에 허용할 포트를 설정합니다.
- Fail2Ban → 반복적인 악의적인 시도를 차단합니다 (그 이름은 문자 그대로 “봇을 차단될 때까지 실패하게 한다”는 의미입니다).
먼저 방화벽을 설정하고, 그 다음에 Fail2Ban을 설치하세요; 그렇지 않으면 스스로 차단당할 수 있습니다.
TL;DR 체크리스트
- Coolify 설치 (한 줄 명령)
- 마법사 실행 → 관리자 계정
- PostgreSQL DB 생성 → 연결 문자열 복사
- 백엔드 배포 → 환경 변수 설정, 도메인
api.yourdomain.com - 프론트엔드 배포 →
VITE_API_URL설정, 도메인pos.yourdomain.com, 재작성 규칙 추가 api와pos에 대한 DNS A 레코드 추가 → VPS IP로 지정- SSL 확인 (자물쇠 아이콘) 및 헬스 엔드포인트 확인
- 보안을 위해 UFW 및 Fail2Ban 구성
이제 완전한 자체 호스팅, 프로덕션 준비된 플랫폼을 갖게 되었습니다—VPS 위의 나만의 “Heroku”. 🚀
✅ 서버 설정 및 배포 체크리스트
| 단계 | 설명 |
|---|---|
| 1 | VPS 구매 (OVHcloud) — Ubuntu 24.04 |
| 2 | 패키지 업데이트 – apt update && apt upgrade |
| 3 | UFW 설정 – 기본 차단, 포트 22, 80, 443, 3000 열기 |
| 4 | Fail2ban 설치 – maxretry 5, bantime 1h |
| 5 | Coolify v4 설치 – :3000 에서 접근 |
| 6 | Coolify를 통해 PostgreSQL 프로비저닝 |
| 7 | 백엔드 배포 (Node.js / Nixpacks) – 필요한 환경 변수 포함 |
| 8 | 프론트엔드 배포 (React / Vite / 정적) – SPA 재작성 규칙 적용 |
| 9 | DNS A 레코드 설정 (Namecheap) |
| 10 | SSL 검증 – Let’s Encrypt 사용 (Coolify 자동) |
| 11 | 실제 엔드포인트 테스트 |
결과: 빈 서버에서 라이브 앱까지, 시작부터 끝까지 하루 만에 완료했습니다. 마법이 아니라 단순히 작업 순서를 알고 있었기 때문입니다.