2025.7.18: 다시 배포 실습하기

jiyongg·2025년 7월 18일

TIL: Today I Learned

목록 보기
3/30

지난 번 미니 해커톤 회고록에서 배포에 대한 실습이 부족했었다는 내용을 썼었다.

오늘은 미리 세션 때 생성해 두었던 내 EC2 인스턴스를 재활용해서 미니 해커톤의 백엔드를 배포해 보고자 한다.

아래의 작업은 미니 해커톤 때의 소스 코드를 바탕으로 이루어지는 작업들이므로, 이해가 잘 되지 않는다면 미니 해커톤 회고록 1편미니 해커톤 회고록 2편을 읽어보길 바란다.

0. 개요

아래의 순서로 작업이 이루어진다.

  • 🔱 Github 저장소를 포크하고 클론한다.
  • ⚙️ Django 프로젝트의 설정을 변경한다.
    • settings.py를 수정한다.
    • 로컬 설정 파일 local_settings.py를 생성한다.
    • .env 파일을 생성한다.
  • 🧾 연결한 DB 서버의 DB에 Migrate를 실행하고 데이터를 추가한다.
  • 🌐 nginx의 구성 파일 nginx.default.conf를 생성하여 80번 포트 서버를 작성한다.
  • 🛄 Docker를 설정한다.
    • Docker 이미지를 생성하기 위한 Dockerfile을 생성한다.
    • Docker Compose의 구성 파일인 docker-compose.yaml을 생성한다.
  • 🛜 서버를 실행한다 (1차).
    • .env 파일을 생성한다.
    • 서버를 실행한다. (1차 실행)
    • 서버가 잘 동작하면, 다시 서버를 종료한다.
  • ✅ https 프로토콜에 필요한 SSL 인증서를 획득한다. Certbot을 이용해 이것을 자동화할 것이다.
    • docker-compose.yaml을 수정하여 nginx에서 certbot 관련 폴더를 마운트한다. 그리고 certbot 서비스를 추가한다.
    • nginx의 구성 파일을 수정하여 certbot이 Challenge에 필요한 파일을 서버에서 찾아 제공할 수 있도록 설정한다.
    • init-letsencrypt.sh 파일을 다운로드 받아, 약간 수정하고 실행 권한을 주어 실행한다.
  • 🛜 서버를 실행한다 (2차).
    • nginx의 구성 파일을 수정하여 http 서버의 내용을 https 서버로 옮기고, SSL 인증서 정보를 추가한다.
    • 1차 실행과 같은 방법으로 서버를 실행한다.
  • 🖼️ 프론트의 작업물을 백엔드와 합친다.
    • 프론트의 작업물을 포크하고 클론한다.
    • 프론트의 작업물을 빌드한다.
      • package.json에 따라 패키지를 설치한다.
      • 작업물을 빌드한다.
    • 빌드 결과물을 옮긴다.
    • .gitignore를 수정해서 dist 폴더가 커밋되게 한다.
    • docker-compose.yaml을 수정해서 빌드 결과물 dist 폴더를 nginx에 마운트한다.
    • nginx의 구성 파일을 수정하여 프론트와 백엔드 앱을 모두 서비스할 수 있게 구성한다.
  • 🛜 서버를 실행한다 (3차).

1. 🔱 Github 저장소 포크 및 클론

일단 미니 해커톤을 하며 만들고 배포했던 기존 Django 프로젝트는 파트너 A의 EC2 인스턴스와 DB 서버에 맞게 설정되어 있다.

그래서 기존 저장소를 포크를 해서 클론한 뒤, 프로젝트의 설정을 나의 EC2 인스턴스와 DB 서버에 맞게 바꾸고, 내 입맛대로 구성해보고자 한다.

기존 Github 저장소에 접속해서, 해당 저장소를 포크하고, Git Bash에서 포크한 저장소를 클론하였다.

2. ⚙️ Django 설정

이제 Django 프로젝트의 설정을 나의 EC2 인스턴스와 DB 서버를 기준으로 수정한다.

settings.py 수정

현재 settings.pyALLOWED_HOSTS에는 127.0.0.1, 그리고 파트너 A의 탄력적 IP와 도메인이 들어 있다.

ALLOWED_HOSTS에서 파트너 A의 탄력적 IP와 도메인을 내 탄력적 IP와 도메인으로 교체해 준다.

ALLOWED_HOSTS = ['127.0.0.1', '내 탄력적 IP', 'example.tld', 'www.example.tld']

로컬 설정 파일 local_settings.py 생성

settings.py와 같은 경로에서 local_settings.py 파일을 생성해준다.

그리고, .env에서 SECRET_KEY와 DB 접속 정보를 불러오는 코드를 작성해준다.

from decouple import config

SECRET_KEY = config('SECRET_KEY')

import pymysql

pymysql.install_as_MySQLdb()
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': config('DB_NAME'), # DB(스키마) 이름
        'USER': config('DB_USER'), # DB 유저 이름
        'PASSWORD': config('DB_PASSWORD'), # DB 비밀번호
        'HOST': config('DB_HOST'), # DB 엔드포인트
        'PORT': 3306, # MySQL의 기본 포트 번호
    }
}

config 함수는 .env의 파일 속에서 config의 인자에 해당하는 변수의 값을 불러올 것이다.

.env 생성

개발할 때, SECRET_KEY를 분리하려고 미리 .env 파일을 생성해 두고 진행했다. 하지만, .gitignore에 의해서 이 파일은 저장소에 저장되어 있지 않고, 그 저장소를 클론해서 작업중인 상황이기 때문에 .env를 다시 만들어서 수정해야 한다.

.envmanage.py와 같은 경로에 생성한다.

SECRET_KEY = 시크릿 키

DB_NAME = DB 스키마
DB_USER = DB 유저 이름
DB_PASSWORD = DB 비밀번호
DB_HOST = DB 엔드포인트

3. 🧾 DB Migrate 및 데이터 추가

데이터베이스를 내 DB 서버에 연결시켰으니, 이제 이 DB에 Migrate 및 데이터를 추가하는 작업을 해야 한다.

먼저, Django의 migrate를 이용해 DB의 구조를 만들어 준다.

python manage.py migrate

그 후, Django의 shell을 실행해서, 데이터 추가 함수 init_db를 실행한다.

python manage.py shell
>>> from movies.views import init_db
>>> init_db()

init_db가 잘 이루어졌는지 한번 확인해본다.

>>> Movie.objects.all()
<QuerySet [<Movie: Movie object (1)>, <Movie: Movie object (2)>, <Movie: Movie object (3)>, <Movie: Movie object (4)>, <Movie: Movie object (5)>, <Movie: Movie object (6)>, <Movie: Movie object (7)>, <Movie: Movie object (8)>, <Movie: Movie object (9)>, <Movie: Movie object (10)>, <Movie: Movie object (11)>, <Movie: Movie object (12)>, <Movie: Movie object (13)>, <Movie: Movie object (14)>, <Movie: Movie object (15)>, <Movie: Movie object (16)>, <Movie: Movie object (17)>, <Movie: Movie object (18)>, <Movie: Movie object (19)>, <Movie: Movie object (20)>, '...(remaining elements truncated)...']>

MySQL Workbench에서 쿼리를 날려서도 한 번 확인해본다.

select * from movies_movie where id=1;

쿼리 실행은 플로피 디스크 바로 옆의 번개 버튼을 누르면 된다.

이렇게 하면 movies_movie 테이블에서 id가 1인 영화의 모든 필드를 보여주게 된다.

성공하면 Workbench의 아래쪽에

이렇게 뜰 것이다. Result Grid 쪽에 결과가 나타나는데, 보안상 이 부분의 스크린샷은 생략한다.

어찌됐든, Django의 shell에서도, MySQL Workbench에서도 성공적으로 데이터가 조회된다면, 데이터가 잘 추가된 것이다.

4. 🌐 nginx 설정

이번엔 웹서버인 nginx를 설정할 차례이다.

manage.py와 같은 경로에 nginx.default.conf를 생성하고, 80번 포트 서버를 아래와 같이 작성한다.

server {
	listen 80;
	server_name example.tld www.example.tld;

	location / {
		proxy_pass http://web:8000;
		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;
	}
}

example.tldwww.example.tld를 Host로 하고 80번 포트(http)로 들어오는 연결을 받아서, nginx는 web의 8000번 포트에 요청을 전달하게 된다.

여기에서 web이란 Docker에서 설정한 컨테이너 이름인데, 아래의 Docker 설정을 보면 이해할 수 있다.

5. 🛄 Docker 설정

Docker를 설정할 것이다.

manage.py와 같은 경로에 Dockerfiledocker-compose.yaml을 생성한다.

Dockerfile

Dockerfile은 도커 이미지를 만들기 위해 필요한 파일이다.

Dockerfile이 있는 경로에서 docker build -t 이미지명 .을 입력하면,

이미지가 만들어질 것이다.

FROM python:3.10.12

ENV PYTHONUNBUFFERED 1

RUN apt-get -y update
RUN apt-get -y install vim

RUN mkdir /app
ADD . /app
WORKDIR /app

RUN pip install --upgrade pip
RUN pip install -r requirements.txt
COPY .env .

docker-compose.yaml

Docker Compose로 여러 컨테이너를 한 번에 관리하고 실행할 수 있다.

docker-compose.yaml은 이 Docker Compose의 구성 파일이다.

version: '3'
services:
    web:
        image: server
        container_name: web
        command: bash -c "gunicorn movie_review.wsgi:application -b 0.0.0.0:8000"
        ports:
            - '8000:8000'
        restart: always
        env_file:
            - .env
        volumes:
            - .:/app
            - ./static:/app/static
            - ./media:/app/media
        networks:
            - server
    nginx:
        image: nginx:latest
        container_name: nginx
        volumes:
            - ./nginx.default.conf:/etc/nginx/conf.d/default.conf

        restart: always
        ports:
            - '80:80'
        depends_on:
            - web
        networks:
            - server
        command: '/bin/sh -c ''while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g "daemon off;"'''

networks:
    server:
  • gunicorn은 wsgi 앱을 사용한다. gunicorn movie_review.wsgi:application -b 0.0.0.0:8000에서 movie_reviewwsgi.py가 위치한 폴더의 이름이다. wsgi.py 안의 application이라는 변수에 get_wsgi_application()을 통해 불러와진 wsgi 앱이 있다. -b는 바인드를 의미하며,-b 뒤의 주소와 포트에서 통신하겠다는 뜻이다.
  • Docker Compose는 현재 web이라는 서비스와 nginx라는 서비스로 이루어져 있고, 두 컨테이너는 server라는 네트워크에 연결되어 있다.
    • Docker에는 자체적인 DNS가 있어서, 같은 네트워크에 속한 컨테이너끼리 컨테이너 이름을 이용해 통신할 수 있다. 이것을 활용하는 것이 위의 nginx 설정의 proxy_pass http://web:8000; 부분이다.
      • 나도 Docker의 네트워킹에 대해서 정확하게 이해한 것은 아니라, 나중에 좀 더 자세히 공부해 보고자 한다.

6. 🛜 서버 1차 실행

이제 EC2 인스턴스에 접속한다. 나는 윈도우 환경이기 때문에 PuTTY를 이용해서 접속했다.

접속 과정은 생략한다.

일단 접속한 후, 저장소를 클론한다. (물론, 위의 과정에서 커밋 및 푸시를 했다는 가정이다.)

.env 파일 생성

.env.gitignore에 의해서 저장소에 저장되지 않는 파일이기 때문에 EC2 인스턴스에서 직접 만들어야 한다.

manage.py가 있는 경로로 이동해서 아래와 같이 입력한다.

touch .env
  • touch는 파일의 타임스탬프를 수정하는 명령어인데, 파일이 존재하지 않으면 해당 파일을 생성한다고 한다. 여기에서는 .env 파일이 없으므로 touch.env 파일을 만들게 된다.

파일을 만든 후,

sudo vi .env

를 입력하여 vi 편집기를 키고, i를 눌러 삽입 모드로 진입하여

2번 과정의 .env 파일의 내용을 그대로 복사하여 붙여넣기한다.

그리고 esc를 누르고 :wq를 입력하여 저장 및 종료한다.

서버 실행

이제 서버를 실행한다.

사실 EC2 인스턴스에는 Docker가 설치되어 있지 않아서, Docker를 설치하고 권한을 설정해 주어야 하는데, 나는 세션 때 사용했던 EC2 인스턴스를 재활용하고 있어서 미리 Docker 관련 작업이 되어 있다.

Dockerfile이 위치한 곳, 즉 manage.py가 있는 곳에서 아래의 명령을 실행한다.

$ docker build -t server .

그러면 Dockerfile의 내용을 바탕으로, server라는 이름의 이미지가 만들어진다. 이 이미지는 Docker Compose 구성의 web 서비스에서 사용되는 이미지이다.

다음으로, nginx 이미지를 가져온다.

$ docker pull nginx

그리고 Docker Compose를 이용해 서버를 실행시킨다.

$ docker-compose up -d

사진처럼 서비스들이 생성된 후, Docker Compose의 상태를 확인해 본다.

$ docker-compose ps

잘 돌아가는 것 같다.

일단 해야할 작업이 남아있으므로 서버를 종료한다.

$ docker-compose down

7. ✅ SSL 인증서 획득

https 프로토콜을 사용하기 위해서는, SSL 인증서를 획득해야 한다.

SSL 인증서를 발급해주는 대표적인 곳으로 Let's Encrypt가 있다.

SSL 인증서 발급을 희망하는 도메인에서 Challenge를 하여, 특정 파일을 요구한 후 서버가 그 파일을 잘 반환하면 서버는 Challenge를 통과하게 되고, 도메인에 대한 인증서를 발급받는 방식이다.

이것을 Certbot이라는 프로그램을 이용해 자동화할 것이다.

docker-compose.yaml 수정

EC2를 켜둔 상태로, 원래 개발하던 로컬 컴퓨터에서 작업한다.

docker-compose.yaml을 수정하여 certbot을 추가해준다.

version: '3'
services:
   ...
   nginx:
        image: nginx:latest
        container_name: nginx
        volumes:
            - ./nginx.default.conf:/etc/nginx/conf.d/default.conf
            - ./cert/certbot/conf:/etc/letsencrypt
            - ./cert/certbot/www:/var/www/certbot

        restart: always
        ports:
            - '80:80'
            - '443:443'
        depends_on:
            - web
        networks:
            - server
        command: '/bin/sh -c ''while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g "daemon off;"'''
    certbot:
        image: certbot/certbot
        restart: unless-stopped
        volumes:
            - ./cert/certbot/conf:/etc/letsencrypt
            - ./cert/certbot/www:/var/www/certbot
        entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

networks:
    server:
  • nginx를 443번 포트에도 매핑한다. 443은 https 연결에서 사용되는 포트이다.
  • Challenge 과정에서 필요한 파일들을 제공할 수 있게 nginx에 인증 관련 폴더들을 마운트한다.
  • certbot은 Docker Hub에서 배포되고 있는 이미지를 활용한다. certbot에도 인증 관련 폴더들을 마운트해준다. 그리고, entrypoint의 내용은 12시간마다 유효기간이 얼마 남지 않은 인증서를 다시 갱신시키는 내용이다.

nginx 설정

nginx.default.conf를 수정한다.

server {
	listen 80;
	server_name example.tld www.example.tld;

	location / {
		proxy_pass http://web:8000;
		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;
	}
    
    location /.well-known/acme-challenge/ {
		allow all;
		root /var/www/certbot;
	}
}
  • Challenge는 주어진 서버의 /.well-known/acme-challenge/에서 파일을 찾는 것이다.
    • allow all;certbot의 접근을 허가한다.
    • /var/www/certbot은 위의 Docker Compose 설정의 nginx에 있는 volumes에서 - ./cert/certbot/www:/var/www/certbot에 따른 것으로, Docker 내의 nginx 서비스 내에서 certbot이 필요로 하는 파일이 존재하는 경로이다.

이제 수정한 docker-compose.yamlnginx.default.conf를 커밋하고 푸쉬한다.

init-letsenrypt.sh 설정 및 실행

이제 다시 EC2 인스턴스로 돌아온다.

변경 내용을 pull하여 받아오고, 이번에도 manage.py가 있는 경로에서 아래의 명령을 실행한다.

curl -L https://raw.githubusercontent.com/wmnnd/nginx-certbot/master/init-letsencrypt.sh > init-letsencrypt.sh
  • curl은 리눅스에서 HTTP 요청/응답을 받는 도구이다.
  • -L은 서버에서 리다이렉트 응답을 보내면 해당 리다이렉트로 이동하라는 의미이다.
  • 뒤의 주소의 내용을 init-letsencrypt.sh라는 파일에 넣는다. (파일이 없으면 생성한다)
  • https://github.com/wmnnd/nginx-certbot/tree/master 이 저장소는 nginx 환경에서 certbot을 사용할 때 이용할 수 있는 Boilerplate이다. 위의 명령어는 이 저장소에서 certbot 실행 셸 스크립트(sh)인 init-letsencrypt.sh 파일의 내용을 가져와서 저장한다.

생성된 이 파일을 vi 편집기로 열어서 약간 수정해준다.

...

domains=(example.tld)
rsa_key_size=4096
data_path="./cert/certbot"
email="" # Adding a valid address is strongly recommended
staging=1 # Set to 1 if you're testing your setup to avoid hitting request limits

...
  • domains에 도메인을 넣는다.
  • data_path에서 ./data/certbot에서 ./data./cert로 바꾼다.
    • 위의 Docker Compose 구성 파일에서 nginxcertbotvolumes를 보면, ./cert/certbot/~으로 되어 있기 때문이다.
  • 테스트를 위해서 staging=1로 설정한다.

수정했다면 esc를 누르고 :wq 입력 후 엔터하여 저장 및 종료한다.

init-letsencrypt.sh에 실행 권한을 부여한다.

chmod +x init-letsencrypt.sh

이후, init-letsencrypt.sh의 내용을 실행한다.

sudo ./init-letsencrypt.sh

성공하면 Successfully received certificate라는 메시지와 함께, 인증서와 키가 저장된 곳을 출력해준다.

그 후 staging=1staging=0으로 바꾸어 다시 한 번 init-letsencrypt.sh의 내용을 실행하고, 배포용 인증서를 획득해준다.

8. 🛜 서버 2차 실행

nginx 설정

원래 개발하던 로컬 컴퓨터에서 작업한다.

이제 SSL 인증서를 획득하여 https 서버를 사용할 수 있게 되었으므로,

http 서버의 내용을 https 서버로 옮겨준다.

server {
	listen 80;
	server_name example.tld www.exmaple.tld;

	location / {
		return 301 https://$host$request_uri;
	}

	location /.well-known/acme-challenge/ {
		allow all;
		root /var/www/certbot;
	}
}

server {
	listen 443 ssl;
	server_name example.tld www.example.tld;

	ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
	include /etc/letsencrypt/options-ssl-nginx.conf;
	ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

	location / {
		proxy_pass http://web:8000;
		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;
	}

	location /static {
		alias /static;
	}

	location /media {
		alias /media;
	}

	location /.well-known/acme-challenge/ {
		allow all;
		root /var/www/certbot;
	}

	error_page 500 502 503 504 /50x.html;
	location = /50x.html {
		root /usr/share/nginx/html;
	}
}
  • 80번 서버는 http 서버이고, http로 접속하면 https 프로토콜을 사용하게 리다이렉트시킬 것이다.
  • 443번 서버는 https 서버이고, 이에 따라 https 프로토콜에 필요한 ssl 인증서와 ssl 인증서 키를 지정해 두었다. 그리고 위에서 http 서버에 있던 location / 부분을 https 서버로 옮겼다.
  • 500번대 에러는 nginx의 에러이다.

커밋 및 푸쉬해준다.

서버 실행

pull하고, 위의 서버 1차 실행에서 서버를 실행하는 방법과 똑같은 방법으로 실행하면 된다.

9. 🖼️ 프론트엔드의 작업물 합치기

세션의 배포에서 알려준 것은 위의 내용까지였다.

그런데, 미니 해커톤에서는 프론트엔드와 백엔드를 연결해야 하는 작업이 있었다.

내가 활동하는 TIL 톡방에서 이러한 이야기를 보았다.

프론트엔드용 도메인과 백엔드용 도메인을 따로따로 파야하는 것일까요?

nginx의 try_files를 이용해서 EC2 인스턴스 하나만으로 프론트엔드와 백엔드를 모두 서비스할 수 있지 않을까?

참고로, 필자는 프론트엔드나 리액트에 대해선 거의 문외한 수준이기 때문에, 과정이 너무 이상해도 양해 바란다.

프론트의 작업물 포크 및 클론하기

일단 프론트의 작업물을 포크하고 클론한다.

추후에 상황에 따라 코드를 수정해야 할 수도 있기 때문이다.

프론트의 작업물 빌드하기

프론트의 작업물을 빌드해서 생성된 배포용 폴더를 EC2 인스턴스에 넣어 try_files를 이용하고자 한다.

일단, 그러려면 프론트의 작업물을 먼저 빌드해야 한다.

패키지 설치

package.json이 있는 경로에서 아래의 명령을 실행한다.

$ npm install
  • 이렇게 install 뒤에 아무것도 붙이지 않으면 packages.json에 있는 패키지들을 설치해준다.

작업물 빌드

같은 경로에서 아래의 명령을 실행한다.

$ npm run build

우리 프론트 팀원들은 Vite와 React를 사용했기 때문에 dist라는 폴더에 배포할 파일들이 저장되었다.

빌드 결과물 옮기기

생성된 dist를 위의 백엔드 저장소의 manage.py와 같은 경로로 옮겨준다.

.gitignore 수정

현재 .gitignore에서는 dist 폴더가 커밋되지 않게 설정되어 있다.

그래서 이것을 주석 처리해서 dist 폴더가 커밋되게 바꾸어준다. 원래 dist 폴더를 직접 커밋하는 건 좋은 방법은 아닌듯하다.

Docker Compose 구성 수정

이제 프론트엔드 앱을 서비스하기 위해서, 이 dist 폴더를 마운트해야 한다.

docker-compose.yaml을 수정하여 nginx 서비스에 dist 폴더를 마운트시켜준다.

...
services:
    ...
    nginx:
        ...
        volumes:
            - ./nginx.default.conf:/etc/nginx/conf.d/default.conf
            - ./cert/certbot/conf:/etc/letsencrypt
            - ./cert/certbot/www:/var/www/certbot
            - ./dist:/dist
        ...
    ...
...

nginx 설정

nginx 설정 초안

nginx.default.conf를 수정한다.

server {
    listen 443 ssl;
    ...
    
	location / {
		proxy_pass http://web:8000;
		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;

		root /dist;
		try_files $uri $uri/ index.html;
	}
    ...
}
  • proxy_pass로 요청을 web 컨테이너의 8000번 포트에 전달한다.
  • 프론트엔드의 빌드 결과물이 있는 폴더를 root로 지정해준다.
  • try_files$uri(파일)와 $uri/(디렉토리)를 찾고, 없으면 /index.html URI로 내부 리다이렉트가 일어나 루트 폴더의 index.html을 서비스한다.

사실 초안이라는 단어가 암시하듯, 이 설정은 잘 동작하지 않았다. 그래서 다른 방법을 시도했다.

여기서 나는 요청을 web으로 전달하면서, try_files를 통해 같은 location /에서 프론트와 백을 모두 서비스할 수 있을 것이라고 생각했던 것이다...

수정된 nginx 설정

이번에는 백엔드쪽 URL은 location /api/로 시작하게 해보고자 한다.

이때, Django 프로젝트의 url도 수정해주어야 하고, 프론트 작업물에서 fetch 메소드의 url을 수정 후 다시 빌드하여야 한다. 앞서 프론트의 작업물을 포크하고 클론한 이유이다.

server {
    listen 443 ssl;
    ...
    location / {
        root /dist;
        try_files $uri $uri/ /index.html;
    }

	location /api/ {
		proxy_pass http://web:8000;
		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;
	}
    ...
}
  • 기본적으로 프론트엔드의 앱이 서비스된다.
  • 프론트엔드에서 /api/~으로 요청을 보내면, location /api/를 통해 백엔드의 앱으로 요청이 전달된다.

변경 사항들을 커밋 및 푸쉬한다.

10. 🛜 서버 3차 실행

변경 사항들을 pull하고 서버를 실행해본다.

도메인에 접속하여 프론트엔드 앱이 잘 서비스되는지, 프론트엔드 앱과 백엔드 앱 간의 통신이 잘 이루어지고 있는지 체크한다.

필요한 경우 MySQL Workbench로 실제로 데이터베이스에 데이터가 저장되는지 확인해 본다.

11. 🔚 결론

이렇게 해서 세션 때 배운 서버 배포를 실습하고, 거기에 더해서 프론트엔드 앱과 백엔드 앱을 같은 EC2 인스턴스 내에서 서비스해보았다.

이것이 정답인지는 모르겠다. 이렇게 서비스할 경우 프론트와 백의 서버 주소가 같기 때문에, CORS 문제는 생기지 않는데, 대신 이용자에게 의도치 않게 api가 노출되는 문제가 생길 수 있지 않을까 싶다.

상당히 길어졌는데, 여기까지 읽은 사람이 있을까..?

저번에 미니 해커톤을 하다가 겪었던 배포 문제는 사실 순서의 문제였던 것 같다.

내가 욕심을 내서 SSL 인증서 획득과 서버 실행을 동시에 하고자 하였기 때문이다.

그리고 그것이 가능하다고 가정하고 정리한 내용을 파트너 A와 공유하였기에 그런 불상사가 생긴 것이다.. (미안해 파트너 A...😿)

이번에는 서버 실행을 먼저, 그 후 SSL 인증서를 획득하는 순으로 진행하였더니 배포가 잘 이루어졌다.

무엇이든 하나씩 차근차근 진행해야 하고, 욕심을 내면 안된다는 교훈을 얻었다.

profile
그냥 쓰고 싶은 것 쓰는 개발(?) 블로그

0개의 댓글