초보자를 위한 AWS 서브도메인 웹서비스 구축 실습
GitHub의 Flask 프로젝트를 health.oukwon.kr로 서비스하기
웹서비스를 처음 구축하려고 하면 GitHub, AWS, Nginx, Gunicorn, DNS, HTTPS 같은 낯선 용어가 한꺼번에 등장합니다. 처음에는 각각이 무슨 역할을 하는지 구분하기도 어렵습니다.
이번에는 라즈베리파이에서 개발한 수면 상태 체크 PWA를 GitHub에 올린 뒤, AWS 서버에 내려받아
https://health.oukwon.kr
이라는 서브도메인으로 실제 서비스하는 과정을 처음부터 끝까지 진행했습니다.
이 글은 특정 프로젝트의 설치 기록이면서 동시에, 처음 웹서버를 구축하는 사람이 전체 구조를 이해하기 위한 입문서입니다.
1. 먼저 전체 구조를 이해하자
이번에 구축한 서비스의 흐름은 다음과 같습니다.
스마트폰 / PC
↓
health.oukwon.kr
↓
Cloudflare DNS
↓
AWS 서버
↓
Nginx
↓
Gunicorn
↓
Flask
↓
수면 상태 체크 PWA
처음에는 이 구조가 복잡해 보이지만 역할을 하나씩 나누어 보면 어렵지 않습니다.
DNS
인터넷에서
health.oukwon.kr
이라는 이름을 입력했을 때 어느 서버로 가야 하는지를 알려줍니다.
쉽게 말하면 주소 안내소입니다.
Nginx
AWS 서버에 도착한 요청을 어떤 프로그램으로 보낼지 결정합니다.
쉽게 말하면 건물 안내 데스크입니다.
Gunicorn
Flask 프로그램을 실제 서버 환경에서 실행해 주는 프로그램입니다.
Flask
우리가 만든 실제 웹 애플리케이션입니다.
systemd
Gunicorn이 꺼지지 않도록 관리하고, 서버가 재부팅되어도 자동으로 다시 실행해 줍니다.
2. GitHub 프로젝트를 AWS로 가져오기
먼저 AWS Ubuntu 서버에 SSH로 접속했습니다.
현재 위치를 확인합니다.
pwd
결과:
/home/ubuntu
기존 프로젝트도 확인했습니다.
ls
그다음 GitHub 저장소를 AWS로 복제했습니다.
git clone https://github.com/사용자명/snore-app.git
복제가 끝나면:
cd snore-app
프로젝트 상태를 확인합니다.
git status
정상이라면 다음과 비슷한 결과가 나옵니다.
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
이 시점부터 GitHub는 개발 PC와 AWS 서버 사이의 코드 전달 창고 역할을 하게 됩니다.
운영 중 코드를 수정했다면 보통:
개발 환경
→ git add
→ git commit
→ git push
→ AWS에서 git pull
순서로 반영할 수 있습니다.
3. Python 가상환경 만들기
웹 프로젝트를 운영할 때는 시스템 전체에 Python 패키지를 설치하기보다 프로젝트별로 독립된 공간을 만드는 것이 좋습니다.
이것이 가상환경, venv입니다.
python3 -m venv .venv
가상환경을 활성화합니다.
source .venv/bin/activate
그러면 터미널 앞부분이 다음처럼 바뀝니다.
(.venv) ubuntu@서버:~/snore-app$
(.venv)가 보이면 성공입니다.
그다음 필요한 Python 패키지를 설치합니다.
python -m pip install --upgrade pip
pip install -r requirements.txt
설치된 Python과 Gunicorn의 위치도 확인해 봅니다.
which python
which gunicorn
예:
/home/ubuntu/snore-app/.venv/bin/python
/home/ubuntu/snore-app/.venv/bin/gunicorn
이렇게 나오면 프로젝트 전용 가상환경이 잘 만들어진 것입니다.
4. ffmpeg 설치
이번 프로젝트는 스마트폰으로 녹음된 음성을 처리합니다.
스마트폰 브라우저에서는 녹음 파일이 webm, ogg 같은 형태로 만들어질 수 있습니다. Python 분석 프로그램에서 다루기 쉽게 변환하려면 ffmpeg가 필요합니다.
설치합니다.
sudo apt update
sudo apt install -y ffmpeg
확인:
ffmpeg -version
ffmpeg version ...이라는 내용이 나오면 정상입니다.
ffmpeg는 쉽게 말해 음성·영상 파일을 변환하고 처리하는 멀티미디어 공구상자라고 생각하면 됩니다.
5. Nginx보다 먼저 앱 자체를 시험한다
여기서 중요한 서버 운영 습관이 하나 있습니다.
처음부터 Nginx와 DNS까지 연결하지 말고, 앱 자체가 정상 실행되는지 먼저 확인하는 것입니다.
Gunicorn을 직접 실행했습니다.
gunicorn \
--workers 2 \
--threads 4 \
--timeout 600 \
--bind 127.0.0.1:5050 \
app:app
정상이라면:
Listening at: http://127.0.0.1:5050
Booting worker
같은 메시지가 나타납니다.
다른 SSH 창에서 테스트합니다.
curl http://127.0.0.1:5050
웹페이지의 HTML 내용이 출력되면 성공입니다.
여기서 중요한 의미는:
127.0.0.1:5050
↓
Gunicorn
↓
Flask
까지는 정상이라는 것입니다.
외부 인터넷과 아직 연결하지 않았는데도 프로그램 자체가 작동하는지를 먼저 확인한 것입니다.
6. systemd에 정식 서비스로 등록하기
터미널에서 직접 실행한 Gunicorn은 SSH 연결을 끊거나 서버가 재부팅되면 문제가 생길 수 있습니다.
그래서 systemd에 등록합니다.
예를 들어 서비스 파일은 다음과 같은 구조입니다.
[Unit]
Description=Sleep recorder
After=network.target
[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/snore-app
ExecStart=/home/ubuntu/snore-app/.venv/bin/gunicorn \
--workers 2 \
--threads 4 \
--timeout 600 \
--bind 127.0.0.1:5050 \
app:app
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
서비스 파일을 복사합니다.
sudo cp deploy/jamsom.service /etc/systemd/system/jamsom.service
systemd 설정을 다시 읽게 합니다.
sudo systemctl daemon-reload
자동 시작 설정:
sudo systemctl enable jamsom
현재 즉시 시작:
sudo systemctl start jamsom
상태 확인:
sudo systemctl status jamsom --no-pager
다음 두 표현이 중요합니다.
enabled
active (running)
enabled
서버가 재부팅되어도 자동으로 시작한다는 뜻입니다.
active (running)
현재 실제로 실행되고 있다는 뜻입니다.
7. Nginx와 연결하기
이제 외부 요청을 Flask 앱으로 보내줄 Nginx를 설정합니다.
설정 파일을 만듭니다.
sudo vi /etc/nginx/sites-available/health.oukwon.kr
초기에는 HTTPS를 바로 설정하지 않고 HTTP 80번 포트만 먼저 구성했습니다.
server {
listen 80;
listen [::]:80;
server_name health.oukwon.kr;
client_max_body_size 600m;
location / {
proxy_pass http://127.0.0.1:5050;
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;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}
이 설정에서 가장 중요한 부분은:
server_name health.oukwon.kr;
그리고:
proxy_pass http://127.0.0.1:5050;
입니다.
즉:
health.oukwon.kr
↓
Nginx
↓
127.0.0.1:5050
으로 보내라는 뜻입니다.
8. Nginx 설정 활성화
설정 파일만 만들어서는 동작하지 않습니다.
sites-enabled에 연결합니다.
sudo ln -s \
/etc/nginx/sites-available/health.oukwon.kr \
/etc/nginx/sites-enabled/
설정 문법을 검사합니다.
sudo nginx -t
정상이라면:
syntax is ok
test is successful
이라고 나옵니다.
그다음:
sudo systemctl reload nginx
합니다.
nginx -t를 먼저 실행하고 reload하는 습관은 매우 중요합니다.
설정 오류가 있는 상태에서 바로 Nginx를 재시작하는 사고를 예방할 수 있습니다.
9. Cloudflare DNS에 서브도메인 추가
이번 도메인은 Cloudflare에서 관리하고 있었습니다.
기존 설정은 다음과 같았습니다.
academy.oukwon.kr → AWS 서버
oukwon.kr → AWS 서버
www.oukwon.kr → AWS 서버
여기에 health를 추가했습니다.
Cloudflare의:
DNS → Records → Add record
에서 다음처럼 등록합니다.
Type: A
Name: health
IPv4: AWS 공인 IP
Proxy status: Proxied
TTL: Auto
그러면:
health.oukwon.kr
이 AWS 서버로 연결됩니다.
확인:
dig +short health.oukwon.kr
Cloudflare 프록시를 사용할 경우 AWS의 실제 IP가 아니라 Cloudflare IP가 나오는 것이 정상입니다.
10. HTTP 서비스 확인
이제 다음 명령으로 확인합니다.
curl -I http://health.oukwon.kr
다음처럼 나오면 성공입니다.
HTTP/1.1 200 OK
이 시점의 구조는:
인터넷
↓
Cloudflare
↓
AWS Nginx
↓
127.0.0.1:5050
↓
Gunicorn
↓
Flask
입니다.
11. HTTPS 인증서 설치
PWA에서 스마트폰 마이크 같은 기능을 사용하려면 HTTPS가 매우 중요합니다.
이번 서버에는 이미 Certbot이 설치되어 있었습니다.
확인:
certbot --version
기존 인증서도 확인했습니다.
sudo certbot certificates
그다음 새 서브도메인의 인증서를 발급했습니다.
sudo certbot --nginx -d health.oukwon.kr
Certbot은 인증서를 발급하고 Nginx HTTPS 설정까지 자동으로 연결해 줍니다.
발급 후 확인:
sudo certbot certificates
Nginx 확인:
sudo nginx -t
HTTPS 확인:
curl -I https://health.oukwon.kr
최종적으로:
HTTP/2 200
응답이 나왔습니다.
이제 실제 웹서비스 주소는:
https://health.oukwon.kr
이 되었습니다.
12. Cloudflare에서는 Full (strict) 사용
Cloudflare SSL/TLS 설정을 확인해 보니:
Full (strict)
모드였습니다.
이 방식에서는:
사용자
↓ HTTPS
Cloudflare
↓ HTTPS
AWS 서버
양쪽 구간을 모두 암호화합니다.
AWS 서버에도 정상적인 인증서가 있어야 하기 때문에 Let's Encrypt 인증서를 설치한 것입니다.
가능하다면 운영 서비스에서는 Full (strict) 구성을 사용하는 것이 바람직합니다.
13. 장애가 발생하면 어디부터 볼까?
웹서비스가 안 될 때 가장 어려운 것은 “도대체 어디가 문제인가?”입니다.
이번에 가장 유용하게 배운 것은 서비스를 층별로 나누어 점검하는 방법입니다.
먼저 앱부터 확인합니다.
curl http://127.0.0.1:5050
이것이 실패한다면 Gunicorn이나 Flask 쪽 문제일 가능성이 큽니다.
서비스 상태:
sudo systemctl status jamsom --no-pager
로그 확인:
sudo journalctl -u jamsom -n 50 --no-pager
Nginx 검사:
sudo nginx -t
외부 HTTPS 확인:
curl -I https://health.oukwon.kr
DNS 확인:
dig +short health.oukwon.kr
인증서 확인:
sudo certbot certificates
초보자라면 다음 다섯 명령을 웹서버 응급점검 명령으로 기억해 두는 것도 좋습니다.
sudo systemctl status jamsom --no-pager
sudo journalctl -u jamsom -n 50 --no-pager
curl http://127.0.0.1:5050
sudo nginx -t
curl -I https://health.oukwon.kr
14. 문제 위치를 판단하는 간단한 방법
127.0.0.1:5050부터 접속이 안 된다
Flask
Gunicorn
systemd
쪽을 먼저 확인합니다.
내부 5050은 되는데 외부 접속이 안 된다
Nginx
DNS
Cloudflare
HTTPS
쪽을 확인합니다.
웹페이지는 열리는데 녹음만 안 된다
브라우저 마이크 권한
HTTPS
JavaScript
파일 업로드
쪽을 확인합니다.
녹음은 되는데 분석 결과가 나오지 않는다
ffmpeg
detector.py
음성 파일 형식
Python 분석 코드
쪽을 확인합니다.
이렇게 구분하면 막연하게 서버 전체를 의심하지 않아도 됩니다.
15. 이번 실습에서 가장 중요한 것은 명령어가 아니었다
웹서버를 처음 배우면 많은 명령을 외워야 한다고 생각하기 쉽습니다.
하지만 실제로 더 중요한 것은 서비스가 어떤 층으로 구성되어 있는지를 이해하는 것이었습니다.
이번 과정을 한 줄로 정리하면:
GitHub
↓
AWS
↓
가상환경
↓
Gunicorn
↓
systemd
↓
Nginx
↓
DNS
↓
HTTPS
↓
웹서비스
입니다.
각 역할만 이해하면 새로운 서브도메인을 만들 때도 같은 원리를 반복해서 사용할 수 있습니다.
예를 들어 앞으로:
studio.example.com
solar.example.com
iot.example.com
book.example.com
같은 새로운 서비스를 만든다고 해도 기본 원리는 같습니다.
초보자에게 권하고 싶은 배포 순서
처음 웹서비스를 배포한다면 다음 순서를 권합니다.
① GitHub에서 프로젝트 가져오기
② 가상환경 생성
③ 필요한 패키지 설치
④ Gunicorn 단독 실행
⑤ localhost에서 curl 테스트
⑥ systemd 등록
⑦ Nginx 연결
⑧ DNS 연결
⑨ HTTP 테스트
⑩ HTTPS 인증서 발급
⑪ 스마트폰과 외부 PC에서 최종 테스트
특히 중요한 원칙은:
한꺼번에 모두 설정하지 말고 한 층씩 성공 여부를 확인한 뒤 다음 단계로 넘어간다.
입니다.
이 방법을 사용하면 문제가 생겼을 때 원인을 찾기가 훨씬 쉽습니다.
마치며
처음에는 Nginx, Gunicorn, systemd, DNS, SSL 같은 말들이 서로 뒤엉켜 보였습니다.
하지만 실제 서버에서 하나씩 직접 설치하고 확인해 보니 각각의 역할이 분명해졌습니다.
DNS는 서버를 찾아주고,
Nginx는 요청을 안내하고,
Gunicorn은 Flask를 실행하고,
systemd는 서비스를 지켜주고,
Certbot은 HTTPS 인증서를 관리합니다.
이 구조를 이해하는 순간 웹서버 구축은 더 이상 마법 같은 작업이 아닙니다.
작은 웹서비스 하나를 직접 인터넷에 공개해 보는 경험이 서버 공부의 가장 좋은 출발점이라고 생각합니다.

'유틸리티 > Web Programing' 카테고리의 다른 글
| sudo mysql_secure_installation (0) | 2025.10.22 |
|---|---|
| Amazon Linux 2023에서 MariaDB 설치 및 실행 요약 (0) | 2025.10.21 |
| 전자책 파일 일괄 처리, crontab -e (2) | 2025.08.08 |
| AWS EC2에 VSCode 서버 설치 및 실행 (0) | 2025.06.16 |
| phpMyAdmin 설치 방법과, mysqli, PDO 코드 예제 (0) | 2025.06.03 |