배포 — 내 방 컴퓨터를 세상에 연결하기
도메인 · HTTPS · 리버스 프록시 · NAT 뚫기
앱은 완성됐습니다. 그런데 주소를 보세요.
http://127.0.0.1:5000 ← 이 주소는 '이 컴퓨터'라는 뜻입니다
친구에게 이 주소를 보내면? 친구의 브라우저는 친구 컴퓨터를 찾아갑니다. 당연히 아무것도 없죠. 우리가 만든 것을 세상이 보게 하려면 세 가지가 필요합니다.
사람이 기억할 도메인. myshop.kr
가로채기를 막는 HTTPS 인증서
바깥에서 내 서버까지 들어올 통로
돈을 들이면 쉽습니다. 클라우드 서버를 빌리면 되니까요. 하지만 우리는 집에 굴러다니는 우분투 컴퓨터 한 대로 해 볼 겁니다. 비용은 도메인 값(연 1~2만 원)뿐이에요. 이것을 가난한 자의 웹서버라고 부릅시다. 🏠
① 도메인 등록과 Cloudflare 연결
도메인은 빌리는 것입니다. 등록기관(가비아·후이즈·Cloudflare Registrar 등)에서 1년 단위로 빌려요. 그다음 이름을 관리할 곳(네임서버)을 Cloudflare로 지정합니다.
| 타입 | 뜻 | 예시 |
|---|---|---|
| A | 이름 → IPv4 주소 | shop → 203.0.113.7 |
| AAAA | 이름 → IPv6 주소 | shop → 2001:db8::7 |
| CNAME | 이름 → 다른 이름 | www → myshop.kr |
| TXT | 메모 (소유 증명 등) | google-site-verification=… |
Cloudflare 화면에서 레코드 옆의 주황색 구름 ☁️이 이 강의의 핵심입니다.
- Cloudflare는 주소만 알려 줌
- 브라우저가 내 집 IP로 직접 접속
- 우리 집 주소가 세상에 공개됩니다 😨
- 모든 요청이 Cloudflare를 거쳐 옵니다
- 내 집 IP는 숨겨집니다
- HTTPS·캐시·공격 차단은 덤
주황 구름을 켜는 순간, Cloudflare는 우리 서버의 리버스 프록시가 됩니다. 그게 무슨 뜻인지 지금부터 봅시다.
② 프록시와 리버스 프록시 — 대신 말해 주는 사람
프록시(proxy)는 '대리인'이라는 뜻입니다. 누구를 대신하느냐에 따라 이름이 갈려요. 이 구분만 정확히 잡으면 배포의 절반은 이해한 겁니다.
손님(클라이언트)을 대신합니다.
나 → [프록시] → 여러 웹사이트
- 서버는 진짜 손님이 누군지 모릅니다
- 학교·회사의 차단 장치, VPN이 이것
- 설정하는 사람: 손님 쪽
가게(서버)를 대신합니다.
여러 손님 → [리버스 프록시] → 내 서버들
- 손님은 진짜 서버가 어디인지 모릅니다
- NGINX, Cloudflare가 이것
- 설정하는 사람: 서버 주인(우리!)
식당에 비유하면 이렇습니다. 포워드 프록시는 내가 부끄러워서 대신 주문해 주는 친구, 리버스 프록시는 식당 입구의 안내 직원이에요. 손님은 주방(Flask)에 직접 들어가지 않습니다. 안내 직원이 자리를 배정하고, 주문을 주방에 전달하죠.
요청 하나가 지나가는 길
+ Flask:5310
# /etc/nginx/sites-available/myshop.kr
server {
listen 443 ssl http2;
server_name myshop.kr;
# ── HTTPS 인증서 (Cloudflare Origin Certificate) ──
ssl_certificate /etc/ssl/certs/myshop.kr.pem;
ssl_certificate_key /etc/ssl/private/myshop.kr.key;
# ── 정적 파일은 NGINX가 직접 (파이썬을 깨우지 않는다) ──
location /static/ {
alias /var/www/myshop/static/;
expires 30d;
}
# ── 나머지는 뒤에 있는 Flask에게 넘긴다 ──
location / {
proxy_pass http://127.0.0.1:5310; # ← 리버스 프록시의 핵심 한 줄
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server { # http 로 온 손님은 https 로 돌려보낸다
listen 80;
server_name myshop.kr;
return 301 https://$host$request_uri;
}
X-Forwarded-For를 눈여겨보세요. 프록시를 거치면 서버 입장에서
모든 손님이 프록시 IP로 보입니다. 그래서 프록시가
"원래 손님은 이 사람이었어요"라고 쪽지를 붙여 전달하는 거예요.
③ HTTPS — 자물쇠는 무엇을 지키나
주소창의 🔒 는 두 가지를 약속합니다. 암호화(중간에서 훔쳐봐도 못 읽음)와 신원 확인(이 서버가 진짜 myshop.kr이 맞음).
둘째가 더 중요합니다. 암호화만으로는 가짜 서버와 안전하게 대화할 수도 있으니까요. 그래서 인증서(certificate)가 필요합니다. 인증서는 신분증이고, 인증기관(CA)이 그 신분증에 도장을 찍어 줍니다.
Cloudflare 뒤에 서면 암호화 구간이 두 개가 됩니다. 각각 다른 인증서를 씁니다.
Universal SSL — Cloudflare가 무료로 발급하고 자동 갱신합니다. 우리가 할 일은 없어요.
Origin Certificate — Cloudflare가 발급하는 15년짜리 인증서. NGINX에 넣습니다.
브라우저 ──🔒①──▶ Cloudflare ──🔒②──▶ 우리 집 NGINX ──평문──▶ Flask
Universal SSL Origin Cert 같은 컴퓨터 안이라 안전
④ 진짜 문제 — NAT라는 벽
여기까지 다 했는데도 친구는 여전히 접속하지 못합니다. 왜일까요?
집에 있는 기기들은 진짜 인터넷 주소를 가지고 있지 않습니다.
노트북도 폰도 192.168.0.x 같은 사설 주소를 쓰죠.
공유기 한 대만 진짜 주소(공인 IP)를 가지고, 나머지를 대신 내보내 줍니다.
이 변환을 NAT라고 합니다.
NAT는 나가는 전화는 되지만 들어오는 전화는 안 되는 구조입니다. 내가 유튜브에 접속하는 건 되는데, 바깥에서 내 컴퓨터로 들어오지는 못하는 이유예요. 집 서버를 운영하려면 이 벽을 어떻게든 넘어야 합니다.
| 방법 | 원리 | 문제점 |
|---|---|---|
| 포트포워딩 | 공유기에 "80번 전화는 내선 50번으로" 규칙 등록 | 공인 IP가 수시로 바뀜(유동 IP) · 집 IP가 그대로 노출 · 공유기마다 설정이 다름 |
| DDNS | IP가 바뀔 때마다 DNS를 자동 갱신 | 바뀌는 순간 몇 분간 접속 불가 |
| DMZ | 한 기기를 통째로 바깥에 노출 | 😱 모든 포트가 열립니다. 절대 권장하지 않음 |
| CGNAT 회선 | 통신사가 공인 IP조차 주지 않음 | 포트포워딩이 아예 불가능 |
| Cloudflare 터널 | 서버가 바깥으로 먼저 연결해 통로를 유지 | ✅ 위 문제가 전부 사라집니다 |
⑤ cloudflared 터널 — 벽을 뚫지 않고 넘는 법
발상이 근사합니다. 바깥에서 들어올 수 없다면, 안에서 나가면 됩니다.
집 서버에서 cloudflared라는 작은 프로그램을 띄우면,
이 프로그램이 Cloudflare를 향해 바깥으로 연결을 겁니다(443 아웃바운드).
NAT도 방화벽도 나가는 연결은 원래 허용하니 아무 설정이 필요 없어요.
그리고 그 연결을 끊지 않고 계속 붙잡고 있습니다.
손님의 요청이 Cloudflare에 도착하면, Cloudflare는 이미 열려 있는 그 통로에 요청을 밀어 넣습니다. 공유기 설정도, 포트 개방도, 공인 IP도 필요 없어요.
- 공유기 설정 0 · 열린 포트 0
- 집 IP 완전히 비공개
- 유동 IP·CGNAT여도 상관없음
- HTTPS는 Cloudflare가 처리
- Cloudflare에 의존하게 됩니다
- 터널 토큰이 유출되면 남이 내 통로를 씁니다
- 집 전기·인터넷이 끊기면 사이트도 멈춤
# ── 우분투에 설치하고 터널 만들기 ──────────────
sudo apt install cloudflared # (또는 공식 .deb 내려받기)
cloudflared tunnel login # 브라우저가 열리고 도메인을 고르면 됩니다
cloudflared tunnel create myshop # 터널 생성 → 자격증명 파일이 만들어짐
cloudflared tunnel route dns myshop myshop.kr # DNS에 CNAME 자동 등록
# ── ~/.cloudflared/config.yml ─────────────────
tunnel: myshop
credentials-file: /home/ubuntu/.cloudflared/<터널ID>.json
ingress:
- hostname: myshop.kr
service: http://localhost:80 # ← 우리 집 NGINX 로 넘긴다
- service: http_status:404 # 나머지는 전부 404 (마지막 줄은 필수)
# ── 항상 켜져 있도록 서비스 등록 ───────────────
sudo cloudflared service install
sudo systemctl enable --now cloudflared
sudo systemctl status cloudflared
이제 https://myshop.kr 로 전 세계에서 접속됩니다.
공유기는 손도 대지 않았고, 우리 집 IP는 아무도 모릅니다.
🎮 배포 실험실 — 32가지 조합을 직접 눌러 보세요
아래 실험실에서 DNS · 집까지 오는 길 · 서버 앞단 · SSL 모드를 골라 접속해 보세요. 실제 서버를 건드리지 않고 결과만 보여 주는 시뮬레이터입니다. 이런 것들을 꼭 확인해 보세요.
- 회색 구름 + NAT 그대로 →
ERR_CONNECTION_TIMED_OUT. 왜 안 될까요? - 주황 구름 + NAT 그대로 → Error 522. 522는 배포 초보자가 가장 많이 만나는 번호입니다.
- 주황 구름 + 포트포워딩 + Flexible → 자물쇠는 보이는데 절반이 평문입니다.
- 주황 구름 + 포트포워딩 + Full(strict) + Flask 직접 → Error 525. 이유를 생각해 보세요.
- 🏆 권장 구성 버튼을 누르면 경고가 몇 개 남나요?
아래쪽 🛰️ 지금 이 요청이 진짜로 지나온 길은 시뮬레이션이 아닙니다. 여러분의 브라우저가 방금 보낸 요청이 실제로 어떤 손을 거쳐 왔는지, 서버가 받은 헤더를 그대로 되짚어 보여 줍니다.
🧪 요청 한 번의 여행을 따라가 봅시다
주소창에 myshop.kr을 치고 엔터를 누른 뒤 화면이 뜨기까지,
요청은 여러 손을 거칩니다. 각 구간이 무슨 일을 하는지 코드로 정리해 봤어요.
🗄️ 운영도 결국 데이터입니다
배포하고 나면 새로운 표가 하나 생깁니다 — 접속 기록(access log)이에요. NGINX가 남긴 로그를 DB에 넣으면, 지금까지 배운 SQL로 사이트를 분석할 수 있습니다.
🎓 20강, 여기가 끝이자 시작입니다
도메인 · HTTPS · NAT 통과 · 방패
리버스 프록시 · TLS · 정적 파일
판단하고 화면을 만든다
모든 것의 바닥 — 유일한 진실
1강에서 상자 하나에 이름표를 붙였습니다.
20강에서 그 상자는 전 세계가 접속하는 서비스가 되었습니다. 🎉
이제 여러분에게 필요한 건 강의가 아니라 만들고 싶은 것 하나입니다.
동아리 출석부든, 급식 평점 앱이든 좋습니다. 오늘 시작해 보세요.
cloudflared 터널로 외부 접속이 되는 이유는?