운영 환경에서는 웹 서버가 클라이언트 요청을 수신하고, 웹 애플리케이션 서버를 통해서 장고의 애플리케이션을 호출하는 식으로 동작한다. 참고로, WSGI 규격을 지킨 웹 애플리케이션 서버를 WSGI 서버라고 부르기도 한다.
이번 포스팅에서는 웹 서버와 장고를 연결해주는 wsgi.py에 대해 알아본 뒤에, 배포 전에 해둬야 할 설정 사항들을 알아볼 것이다.
1. wsgi.py
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
application = get_wsgi_application()
- 장고와 웹 서버를 연결하는 데 필요한 파일로, django-admin startproject mysite 명령에 의해 생성된다.
- WSGI 규격을 따라, 호출 가능한 application 객체를 정의하고 있다. 웹 서버는 이 application 객체를 호출하여 장고의 애플리케이션을 실행한다. get_wsgi_application() 메소드는 잠시 후에 들여다보도록 하자.
application = get_wsgi_application()
- application 객체는 운영 웹 서버와 runserver에서 사용된다. runserver에서는 application 객체의 위치를 mysite/settings.py의 WSGI_APPLICATION 속성으로 지정하고, 아파치나 NGINX/uWSGI는 httpd.conf의 WSGIScriptAlias 또는 uwsgi.ini의 module으로 지정한다.
- wsgi.py의 다음 코드는 application 객체를 호출하기 전에 설정 정보를 로딩해주는 코드다. 설정 정보가 담겨 있는 mysite.settings 모듈의 위치를 지정해주고 있다.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
또 다른 방법으로, runserver을 실행할 때 settings 실행 옵션으로 settings 모듈의 위치를 지정해주는 방법이 있다. 이 실행 옵션을 생략하면 wsgi.py를 참조한다.
python manage.py runserver --settings=mysite.settings
2. WSGIHandler
- get_wsgi_application() 메소드 내부를 보니 WSGIHandler 객체를 반환하고 있다. 즉, application 객체는 WSGIHandler 객체다.
application = get_wsgi_application()
def get_wsgi_application():
"""
The public interface to Django's WSGI support. Return a WSGI callable.
Avoids making django.core.handlers.WSGIHandler a public API, in case the
internal WSGI implementation changes or moves in the future.
"""
django.setup(set_prefix=False)
return WSGIHandler()
- WSGIHandler 클래스는 WSGI 규격에서 정의한 애플리케이션의 역할을 수행하는 클래스다. WSGIHandler 클래스는 다음과 같이 정의되어 있다.
class WSGIHandler(base.BaseHandler):
request_class = WSGIRequest
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.load_middleware()
def __call__(self, environ, start_response):
set_script_prefix(get_script_name(environ))
signals.request_started.send(sender=self.__class__, environ=environ)
request = self.request_class(environ)
response = self.get_response(request)
response._handler_class = self.__class__
status = "%d %s" % (response.status_code, response.reason_phrase)
response_headers = [
*response.items(),
*(("Set-Cookie", c.output(header="")) for c in response.cookies.values()),
]
start_response(status, response_headers)
if getattr(response, "file_to_stream", None) is not None and environ.get(
"wsgi.file_wrapper"
):
# If `wsgi.file_wrapper` is used the WSGI server does not call
# .close on the response, but on the file wrapper. Patch it to use
# response.close instead which takes care of closing all files.
response.file_to_stream.close = response.close
response = environ["wsgi.file_wrapper"](
response.file_to_stream, response.block_size
)
return response
WSGIHandler의 __call__() 메소드가 WSGI 규격의 애플리케이션 스펙을 구현한 메소드인데, WSGI 서버에서 WSGIHandler 객체를 호출할 때 실행돼서 마지막에 최종 응답을 반환한다. 정리하자면, 웹 애플리케이션 서버는 wsgi.py를 통해 WSGIHandler 객체를 얻은 뒤에, WSGIHandler 객체를 다시 호출하여 최종 응답을 만들어서 웹 서버에 돌려주는 식으로 동작하는 것이다.
그리고 WSGIHandler의 부모 클래스인 BaseHandler에서는 settings.py의 MIDDLEWARE에 대한 처리를 수행해준다.
3. 장고 프로젝트를 운영 서버에 적용하기 위한 설정 변경 사항
1) 디버그 끄기
- 운영 모드에서는 디버그 정보가 노출되지 않도록 해야 하기 때문에, settings.py의 DEBUG 항목을 False로 지정해두자.
2) ALLOWED_HOSTS 설정
- settings.py에서 장고가 실행되는 서버의 IP 주소나 도메인명을 등록하는 항목이다. 이 항목을 통해 CSRF 공격을 방지할 수 있다.
- DEBUG를 False로 설정했다면 반드시 ALLOWED_HOSTS 항목을 설정해야 한다.
ALLOWED_HOSTS = ['192.168.46.103']
3) STATIC_ROOT 설정
- 운영 모드에서는 웹 서버에게 정적 파일의 루트 경로를 알려줘야 하기 때문에, settings.py의 STATIC_ROOT 항목도 설정해야 한다.
STATIC_ROOT = os.path.join(BASE_DIR, 'www_dir', 'static')
위 디렉토리는 웹 서버의 설정 파일에도 등록해야 한다. 자세한 내용은 각 웹 서버의 문서를 참고하자.
4) STATICFILES_DIRS 설정
- settings.py의 STATICFILES_DIRS 항목에, STATIC_ROOT 항목에서 정의한 디렉토리가 포함되지 않아야 하기 때문에, 이를 체크해주자. 이 이유는 [5) collectstatic 명령]에서 설명할 것이다.
STATICFILES_DIRS = (BASE_DIR / 'static',)
5) collectstatic 명령
- STATICFILES_DIRS 항목에 정의된 정적 파일을 STATIC_ROOT 디렉토리에 복사해주는 명령이다. 그래서 STATICFILES_DIRS 항목에, STATIC_ROOT 항목에서 정의한 디렉토리가 포함되지 않아야 한다.
- 4)에서 설명한 내용을 잘 준수 했다면, 아래 명령을 실행해주자.
python manage.py collectstatic
6) SECRET_KEY 설정
- 프로젝트 내에서 암호화에 사용되는 항목이기 때문에 외부에 노출되어서는 안된다. 그래서 운영 모드에서는 환경 변수나 파일에 저장해두고 settings.py에서 읽어들이도록 하는 것이 좋다.
환경 변수를 통해 읽어 들이기
# settings.py
import os
SECRET_KEY = os.environ['SECRET_KEY']
파일을 통해 읽어 들이기
# settings.py
with open(os.path.join(BASE_DIR, 'www_dir', 'secret_key.txt')) as f:
SECRET_KEY = f.read().strip()
7) 데이터베이스 관련 설정
(1) 데이터베이스 파일 접근 권한 설정
- 운영 모드에서 웹 서버 프로세스의 소유자 권한으로 데이터베이스 파일이나 로그 파일을 액세스 할 수 있도록 설정해야 한다.
settings.py의 DATABASES 항목을 다음과 같이 설정했다고 하면
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db', 'db.sqlite3'),
}
}
다음과 같이 db 디렉토리와 db/db.sqlite3 파일을 읽고 쓸 수 있게 하기 위해 액세스 권한을 변경해주자.
> mkdir db
> mv db.sqlite3 db/
> chmod 777 db/
> chmod 666 db/db.sqlite3
777, 666이라고 되어있는 부분은 모드 변경식을 의미하는데, 모드 변경식을 모른다면 아래 포스팅을 참고하길 바란다.
https://kimcoder.tistory.com/274
(2) 데이터베이스 기밀 정보 숨기기
- 데이터베이스의 패스워드와 같은 기밀 정보들이 settings.py에 하드코딩되어있지는 않은지 확인해보자. 환경 변수나 파일에 따로 저장해두고 settings.py에서 읽어들이도록 하는 것이 좋다.
8) 로그 파일 접근 권한 설정
- 로그 파일에도 다음과 같이 액세스 권한을 부여해주자.
> chmod 777 logs/
> chmod 666 logs/mysite.log
9) 메일 서버 발신자 주소 변경
- 이메일을 보내는 기능이 있다면 settings.py의 SERVER_EMAIL 및 DEFAULT_SERVER_EMAIL 항목을 정확히 설정해야 한다. 장고는 기본적으로 webmaster@localhost와 root@localhost를 발신자로 사용하는데, 몇몇 이메일 서비스들은 이 주소로 발송된 이메일을 차단하기도 하기 때문에 발신자 주소를 변경해두는 것이 좋다.
10) 설정 체크
마지막으로, python manage.py check --deploy 명령을 통해 운영 서버에서 필요한 설정들이 제대로 이루어졌는지 체크해보자.
python manage.py check --deploy
● 참고할만한 자료
운영 서버 적용 전에 확인할만한 체크리스트를 공식 문서에서 제공하기 때문에 확인해보면 좋을 것같다. 이 포스팅에서 설명한 것 외에도 보안, 성능 등의 항목이 포함되어 있다.
https://docs.djangoproject.com/ko/4.1/howto/deployment/checklist/
'Django' 카테고리의 다른 글
[Django] 날짜형 클래스형 뷰에서 404 에러가 발생하는 경우 (0) | 2023.02.11 |
---|---|
[Django] 가상 환경에서 Django 사용하기 (0) | 2023.02.01 |
[Django] apps.py의 역할 (0) | 2023.01.26 |
[Django] 프로젝트 개발 순서 (0) | 2023.01.25 |
[Django] 로깅 (0) | 2023.01.24 |
댓글