내가 Django 처음 배울 땐 settings.py가 진짜 무서웠다. 괜히 잘못 건드렸다가는 프로젝트가 아예 안 돌아갈 것 같았다. 근데 이번에 이 글 준비하면서 다시 보니까 그때 뭐가 그렇게 어려웠나 싶다. 사실 보면 별 거 없고 말 그대로 설정하는 파일일 뿐인데, 그땐 서버에 대한 기초 지식이 부족해서 더 어렵게 느껴졌던 것 같다. 그래서 이번 기회에 Django를 다시 제대로 공부해보고, Django를 입문하는 사람들에게 조금이나마 도움이 됐으면 하는 마음으로 이 글을 써본다.
Django 프로젝트의 settings.py 파일은 프로젝트의 전반적인 설정을 정의하는 핵심 파일이다. 대부분 입문용 웹 개발로 Django를 배우기 때문에, 시간 내에 기능을 구현하고 간단하게 코드를 작성하는 경우에는 settings.py를 자세히 학습할 필요가 없는 것 같다. 그래서 나 역시 처음 Django를 배울 때는 이 파일에 대한 설명을 대체적으로 생략하고 넘어갔다.
하지만 개발을 계속하고 문제를 수동적으로 찾으며, 더 나은 방향으로 개선하기 위해 프로젝트를 커스텀하게 되면서 settings.py의 중요성을 깨닫게 되었다. 예를 들어, 내장된 USER Model의 Custom이나 MIDDLEWARE 설정 등 과 같이 프로젝트의 핵심 동작에 직접적인 영향을 미친다. 프로젝트를 안정적으로 커스터마이징하고, 유지보수를 쉽게 하기 위해서는 settings.py를 깊이 이해하는 것이 필수적이다.
또한, settings.py를 잘 이해하고 있으면 개발과 배포 환경에서 서로 다른 설정을 쉽게 다룰 수 있다. 예를 들어, 배포 환경에서는 반드시 DEBUG = False로 설정하여 보안을 강화하거나, ALLOWED_HOSTS를 특정 도메인만 허용하도록 설정해 보안을 강화할 수 있다. 데이터베이스 설정 역시 개발 환경에서는 SQLite를 사용하지만, 배포 환경에서는 PostgreSQL이나 MySQL 같은 강력한 데이터베이스를 사용하는 경우가 많다. 추가적으로, 배포 환경에서는 CSRF_COOKIE_SECURE = True나 SESSION_COOKIE_SECURE = True 설정을 통해 쿠키 보안을 강화할 수도 있다.
이외에도 settings.py는 다양한 설정을 제공하며, 이를 제대로 이해하고 활용하면 Django 프로젝트를 더욱 안전하고 효율적으로 운영할 수 있다. 이제, settings.py의 각 설정들을 하나씩 자세히 뜯어보려 한다.
1. 프로젝트 경로 및 기본 경로 설정
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
- BASE_DIR은 프로젝트의 루트 디렉터리를 나타내는 경로이다. 이 경로를 기준으로 프로젝트의 하위 디렉터리와 파일을 상대적으로 참조할 수 있다.
예를 들어, BASE_DIR / "db.sqlite3"는 프로젝트의 루트 디렉터리에 데이터베이스 파일을 생성하는 경로를 의미한다.
BASE_DIR을 사용하면 프로젝트 파일의 경로를 보다 명확하고 일관되게 관리할 수 있어 유지보수가 편리해진다.
정적 파일, 템플릿 파일, 미디어 파일 경로 설정에도 BASE_DIR이 자주 활용된다.
2. 보안 및 디버그 설정
SECRET_KEY = "django-insecure-..."
DEBUG = True
ALLOWED_HOSTS = []
- SECRET_KEY: 암호화 및 보안 관련 중요한 키이다.
Django는 이 키를 사용하여 세션 데이터를 서명하고, CSRF 보호 및 비밀번호 해싱과 같은 중요한 보안 기능을 제공한다. 잘못된 SECRET_KEY를 사용하거나 외부에 노출되면 보안에 심각한 취약점이 발생할 수 있다. 운영 환경에서는 이 키를 절대 외부에 노출해서는 안 된다. 따라서 GitHub에 실제 서비스 코드를 올릴 경우, SECRET_KEY는 .env 파일이나 환경 변수에 저장하고, settings.py에서는 이를 불러오는 방식으로 관리해야 한다.
- DEBUG: 디버그 모드를 활성화할지 여부를 설정한다.
- DEBUG = True: 개발 환경에서 사용되며, Django의 상세한 에러 메시지를 브라우저에 직접 표시한다. 또한, 정적 파일을 개발 서버에서 직접 제공하며, 보안 검사가 약화되어 있다.
- DEBUG = False: 운영 환경에서 사용되며, 에러 메시지가 사용자에게 노출되지 않고, 대신 사용자 정의 에러 페이지를 보여준다. 정적 파일은 웹 서버(Nginx, Apache 등)에서 직접 제공하고, 보안 기능이 강화된다.
DEBUG 설정에 따라 ALLOWED_HOSTS의 설정도 함께 신경 써야 한다. DEBUG = True인 개발 환경에서는 ALLOWED_HOSTS가 빈 리스트이더라도 기본적으로 로컬에서의 모든 요청을 허용한다. 그러나 DEBUG = False인 운영 환경에서는 ALLOWED_HOSTS를 비워둘 경우, 모든 외부 요청이 차단된다. 따라서 운영 환경에서는 ALLOWED_HOSTS에 실제 서비스 도메인을 명시하고, 허가되지 않은 외부 접근을 차단해야 한다.
- ALLOWED_HOSTS: 프로젝트가 서비스할 수 있는 도메인을 명시하는 설정이다.
이 설정은 보안을 강화하기 위해 도입된 것으로, 허용되지 않은 호스트에서의 요청을 차단하는 역할을 한다. 예를 들어, ALLOWED_HOSTS = ['example.com', 'www.example.com']과 같이 설정할 수 있다. 빈 리스트로 설정(ALLOWED_HOSTS = [])하면, 외부에서의 모든 요청이 차단된다. 따라서 배포 환경에서는 실제 서비스 도메인만 지정해야 한다.
3. 설치된 앱 설정
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"myapp1", # 내가 만든 앱
]
- INSTALLED_APPS: 프로젝트에서 사용할 앱을 등록하는 설정이다. (이곳에 등록되지 않으면 동작하지 않는다.) Django의 기본 제공 앱과 사용자가 만든 앱을 여기에 추가할 수 있다. 아마 Django를 배웠다면 가장 먼저 알아야 할 설정 항목이지 않을까 싶다.
4. 미들웨어 설정
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
- MIDDLEWARE: 요청과 응답의 전처리 및 후처리를 담당하는 Django의 미들웨어를 설정한다. 각 미들웨어는 보안, 세션 관리, 인증 등을 다룬다. (이에 대해서는 다른 글에서 자세하게 다룰 예정이니 간단하게 넘어가도록 하겠다.)
5. URL 및 WSGI 설정
ROOT_URLCONF = "config.urls"
WSGI_APPLICATION = "config.wsgi.application"
- ROOT_URLCONF: 프로젝트의 URL 라우팅을 담당하는 설정이다. 프로젝트의 URL 매핑을 담당하는 urls.py 파일을 지정한다. urls.py 파일은 URL 패턴과 뷰를 연결하는 중요한 역할을 한다. 나의 프로젝트 명은 config 이기 때문에 config.urls가 ROOT_URLCONF인 것이다.
- WSGI_APPLICATION: WSGI(Web Server Gateway Interface)는 Django 애플리케이션과 웹 서버를 연결하는 표준 인터페이스이다. Gunicorn이나 uWSGI와 같은 WSGI 서버를 사용할 때 설정해야 한다.
6. 데이터베이스 설정
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
- DATABASES: 프로젝트의 데이터베이스 연결 정보를 설정한다.
SQLite는 Django의 기본 내장 데이터베이스이다. SQLite는 파일 기반으로 동작하기 때문에 설정이 간편하고, 별도의 데이터베이스 서버가 필요 없다. 따라서, 개발 및 소규모 프로젝트에 적합하다. 하지만, 대규모 프로젝트나 동시 요청이 많은 경우 성능이 떨어질 수 있고, 트랜잭션 및 동시성 처리에 제한이 있기 때문에 운영 서버를 따로 관리할 경우 DB를 연결해 주는 것이 좋다.
딕셔너리 형태로 작성되며, 여러 데이터베이스를 동시에 연결할 수도 있다.
- PostgreSQL: "django.db.backends.postgresql"
- MySQL: "django.db.backends.mysql"
- MariaDB: "django.db.backends.mysql"
- Oracle: "django.db.backends.oracle"
MySQL 연결 예시
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mydatabase',
'USER': 'myuser',
'PASSWORD': 'mypassword',
'HOST': 'localhost',
'PORT': '3306',
}
}
Redis 연결 예시 (캐시)
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}
7. 비밀번호 검증 설정
AUTH_PASSWORD_VALIDATORS = [
{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"},
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
]
- AUTH_PASSWORD_VALIDATORS: 사용자가 비밀번호를 설정할 때, 강도를 검증하는 기능이다.
- MinimumLengthValidator: 최소 길이를 설정한다.
- CommonPasswordValidator: 일반적으로 사용되는 비밀번호를 차단한다.
- NumericPasswordValidator: 숫자로만 구성된 비밀번호 방지한다.
이 설정은 Django에 내장된 User 모델을 사용할 때 사용자 회원가입이나 비밀번호 변경 시 자동으로 적용된다. 따라서 위 사진처럼 Admin으로 User를 생성할 때도 이 조건이 검증된다. (영어로 나와있는 것들이 AUTH_PASSWORD_VALIDATORS에서 설정해 둔 검증들이다.)
8. 국제화 및 시간대 설정
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
- LANGUAGE_CODE: 프로젝트의 기본 언어를 설정한다.
- TIME_ZONE: 프로젝트의 시간대를 설정한다.
- USE_I18N: 다국어 지원 활성화한다.
- USE_TZ: 시간대 지원 활성화한다.
LANGUAGE_CODE = 'ko-kr'
TIME_ZONE = 'Asia/Seoul'
한국 시간을 기준으로 하려면 위와 같이 설정해 주면 된다.
9. 정적 파일 설정
STATIC_URL = "static/"
- STATIC_URL: 정적 파일의 URL 경로를 설정한다.
정적 파일은 CSS, JavaScript, 이미지 등을 의미한다. 개발 중에는 python manage.py runserver가 이를 제공하지만, 운영 환경에서는 python manage.py collectstatic 명령어를 사용하여 모든 정적 파일을 한 곳에 모아 웹서버(Nginx, Apache)에서 제공해야 한다.
STATICFILES_DIRS = [
BASE_DIR / "assets",
BASE_DIR / "shared_static",
]
STATIC_ROOT = BASE_DIR / "staticfiles"
이건 기존에 settings.py에 작성된 코드는 아니지만 이런 방식으로 static 파일들을 설정할 수도 있다.
- STATICFILES_DIRS: 개발 환경에서 추가적인 정적 파일 경로를 지정하는 옵션이다.
STATICFILES_DIRS를 사용하면 BASE_DIR / "assets"와 같은 폴더의 정적 파일도 사용할 수 있다. 또한, 여러 경로를 리스트로 지정할 수 있다.
- STATIC_ROOT: 운영 환경에서 사용되는 경로로, python manage.py collectstatic 명령어를 실행했을 때, 모든 정적 파일이 복사되는 디렉터리를 지정한다.
이후 Nginx나 Apache와 같은 웹 서버가 해당 경로의 정적 파일을 서빙한다.
10. 기본 PK 필드 설정
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
- DEFAULT_AUTO_FIELD: 모델의 기본 기본키 필드를 설정한다.
- 가능한 값들: BigAutoField(64비트 정수 키), AutoField(32비트 정수 키), SmallAutoField(16비트 정수 키) 등이 있다.
추가로 settings.py에서 설정할 수 있는 기능들
1. MEDIA_URL
MEDIA_URL = '/media/' # 사용자가 업로드한 파일을 제공하는 URL 경로
MEDIA_ROOT = BASE_DIR / "media" # 업로드된 파일이 저장되는 실제 경로
- MEDIA_URL: 사용자가 업로드한 파일을 웹에서 접근할 수 있는 URL 경로이다.
- MEDIA_ROOT: 업로드된 파일이 실제로 저장되는 경로이다.
MEDIA는 사용자가 업로드하는 파일(이미지, 동영상, PDF, 문서 등)을 다루기 위한 미디어 파일을 의미한다.
STATIC과 헷갈리는 경우가 있는데 STATIC은 CSS, JavaScript, 이미지(로고 등)와 같은 정적 파일을 관리하며, MEDIA는 사용자가 직접 업로드하는 동적 파일을 관리한다는 차이가 있다.
이러한 MEDIA 설정을 할 때는 urls.py, models.py, Template에서 각각의 설정을 추가로 해줘야 한다.
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
# 기존 URL 패턴
]
# 개발 환경에서만 MEDIA 파일 제공
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# models.py
from django.db import models
class Profile(models.Model):
name = models.CharField(max_length=100)
profile_picture = models.ImageField(upload_to='profile_pics/')
resume_file = models.FileField(upload_to='resumes/')
- ImageField: 이미지 파일 업로드를 지원하는 필드이다.
- FileField: 일반 파일(PDF, 문서 등) 업로드를 지원하는 필드이다.
- upload_to: MEDIA_ROOT 하위의 폴더 경로를 지정할 수 있다.
<!-- templates/profile.html -->
<h2>{{ profile.name }}</h2>
<img src="{{ profile.profile_picture.url }}" alt="Profile Picture">
<a href="{{ profile.resume_file.url }}">Download Resume</a>
Template에서 이렇게 .url을 통해 업로드 한 해당 파일을 가져올 수 있다.
물론 배포 시에는 웹 서버에서 설정이 필요하다. 운영 환경(AWS, Nginx, Apache)에서는 DEBUG=False로 설정되기 때문에, Django가 직접 MEDIA 파일을 서빙하지 않는다. 따라서, Nginx나 Apache에서 MEDIA 경로를 직접 설정해야 한다.
location /media/ {
alias /path/to/your/project/media/;
}
위처럼 Nginx에서 경로를 직접 설정해주면 된다.
왜 운영환경에서 Django가 직접 MEDIA를 서빙하지 않을까?
- 보안: MEDIA 파일은 사용자들이 직접 업로드하는 데이터이므로, 악의적인 파일을 업로드할 가능성이 있다. 이를 방지하기 위해 웹 서버(Nginx, Apache)에서 정적 파일을 서빙하고, Django는 애플리케이션 로직만 처리하는 방식으로 보안을 강화할 수 있다.
- 성능: Django는 단일 프로세스로 동작하기 때문에, 대량의 미디어 파일을 직접 제공할 경우 속도가 느려지고 리소스를 과도하게 소모할 수 있다. Django는 정적 파일 서빙에 최적화된 웹 서버가 아니므로, 이를 Nginx 같은 웹 서버가 처리하는 것이 더 적합하다.
- 확장성: 프로젝트의 규모가 커질 경우 CDN(Content Delivery Network)이나 Nginx, Apache 같은 전문 웹 서버 도구를 사용하는 것이 더 효율적이다. Django가 직접 미디어 파일을 서빙할 경우 이러한 분산 아키텍처를 구현하기 어렵다.
2. 로깅
LOGGING = {
'version': 1, # 로깅 설정 버전
'disable_existing_loggers': False, # 기본 로거 비활성화 여부
'handlers': {
'console': { # 터미널에 로그 출력
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
'file': { # 파일에 로그 저장
'level': 'ERROR',
'class': 'logging.FileHandler',
'filename': BASE_DIR / 'logs/django_error.log',
},
},
'loggers': {
'django': { # Django 자체의 로깅 설정
'handlers': ['console', 'file'],
'level': 'DEBUG',
'propagate': True, # 상위 로거로 전파 여부
},
},
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {message}',
'style': '{',
},
},
}
Django의 로깅은 애플리케이션의 오류, 경고 및 디버깅 정보를 효과적으로 기록하기 위해 사용된다. 이를 위해 핸들러(Handler), 로거(Logger), 포맷터(Formatter)를 구성할 수 있다.
- 핸들러(Handlers): 로그 메시지가 발생했을 때, 이를 기록할 경로와 방식을 정의한다. (예: console, file)
- 로거(Loggers): 어떤 수준의 로그를 기록할지, 어떤 핸들러를 사용할지 설정한다.
- 포맷터(Formatters): 로그 메시지의 출력 형식을 정의한다.
로깅은 애플리케이션의 디버깅, 보안 모니터링, 장애 대응에 중요하기 때문에, 프로젝트 환경에 맞는 세밀한 설정이 필요하다. 보다 자세한 로깅 설정은 다음 공식 문서를 참고하면 된다.
3. SMTP 이메일 설정
이메일 전송을 설정할 수 있는 옵션들이다.
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.gmail.com"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = "email@gmail.com"
EMAIL_HOST_PASSWORD = "your-password"
- EMAIL_BACKEND: 사용할 이메일 전송 백엔드 (기본은 SMTP)
- EMAIL_USE_TLS: TLS 암호화 사용 여부
- EMAIL_PORT: SMTP 포트 (TLS는 587, SSL은 465)
- EMAIL_HOST_USER: 이메일 계정
- EMAIL_HOST_PASSWORD: 이메일 비밀번호 (환경 변수로 관리 권장)
4. 보안 설정
운영 환경에서 보안을 강화하기 위한 설정이다.
SECURE_SSL_REDIRECT = True # HTTP -> HTTPS 자동 리디렉션
CSRF_COOKIE_SECURE = True # CSRF 쿠키를 HTTPS 환경에서만 전송
SESSION_COOKIE_SECURE = True # 세션 쿠키를 HTTPS 환경에서만 전송
SECURE_BROWSER_XSS_FILTER = True # XSS 공격 방지
X_FRAME_OPTIONS = "DENY" # 클릭재킹 방지
- SECURE_SSL_REDIRECT: HTTP 요청을 HTTPS로 자동 리디렉션
- CSRF_COOKIE_SECURE: CSRF 쿠키를 HTTPS에서만 전송
- SESSION_COOKIE_SECURE: 세션 쿠키를 HTTPS에서만 전송
- X_FRAME_OPTIONS: 클릭재킹 방지 (DENY, SAMEORIGIN 등 설정 가능)
5. CORS 설정 (교차 출처 리소스 공유)
서로 다른 도메인 간 요청을 허용하기 위한 설정이다.
- CORS_ALLOW_ALL_ORIGINS: 모든 출처 허용 여부
- CORS_ALLOWED_ORIGINS: 특정 출처만 허용
- CORS_ALLOW_CREDENTIALS: 자격 증명 포함 여부
이 외에도 S3 설정, REST API 관련 설정 등 정말 다양한 고급 설정이 가능하다. 필요에 맞게 추가적으로 설정을 적용할 수 있다.
https://docs.djangoproject.com/en/5.1/ref/settings/ 공식 문서의 해당 부분을 보고 알맞는 설정을 찾아보면 될 것 같다.
settings.py의 분리 (개발/운영 환경 분리)
개발 환경과 운영 환경은 보안 요구사항, 성능, 디버깅 수준 등이 다르기 때문에 settings.py를 환경별로 분리하는 것이 필요하다.
왜 분리하는가?
- 보안 강화: 운영 환경에서 DEBUG=False 및 민감한 정보 보호.
- 유지보수 용이성: 개발과 운영 환경에 맞는 별도 설정 관리.
- 배포 자동화: CI/CD 파이프라인에서 환경에 맞는 설정 자동 적용.
설정 분리 방식 예시
프로젝트 디렉터리 구조
myproject/
├── myproject/
│ ├── settings/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── development.py
│ │ └── production.py
base.py (공통 설정)
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent.parent
SECRET_KEY = os.environ.get('SECRET_KEY')
DEBUG = False
ALLOWED_HOSTS = []
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
]
settings라는 폴더가 생겼기 때문에 BASE_URL은 기존보다 .parent를 한번 더 써줘야 한다.
development.py (개발 환경)
from .base import *
DEBUG = True
ALLOWED_HOSTS = []
production.py (운영 환경)
from .base import *
DEBUG = False
ALLOWED_HOSTS = ['example.com']
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
__init__.py (자동 환경 로딩)
import os
if os.environ.get('DJANGO_ENV') == 'production':
from .production import *
else:
from .development import *
이와 같이 settings.py를 환경별로 분리함으로써 보안과 유지보수를 강화할 수 있다.
Django의 settings.py는 프로젝트를 실행할 환경에 맞는 설정을 정의한다. settings.py를 분리 후, ALLOWED_HOSTS를 적절히 지정하지 않을 경우 다음과 같은 오류가 발생할 수 있다.
CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False.
따라서 각 실행 환경이 무엇인지 실행시킬 때 알려줘야 한다. 각 환경에서의 명령어는 다음과 같다.
로컬 환경에서 실행
python manage.py runserver --settings=config.settings.development
운영 환경에서 실행
python manage.py runserver --settings=config.settings.production
로컬 환경 자동화
매번 --settings 옵션을 사용하는 대신, DJANGO_SETTINGS_MODULE 환경 변수를 설정하여 편리하게 서버를 실행할 수 있다.
set DJANGO_SETTINGS_MODULE=config.settings.development
python manage.py runserver
이렇게 설정해두면 --settings 옵션을 추가하지 않아도 서버를 실행할 수 있으며, 필요 시 production 환경으로 변경해 운영 서버를 실행할 수도 있다.
'Django' 카테고리의 다른 글
[Django] Django 프로세스, Request-Response Lifecycle (Web Server, WSGI/ASGI, Middlewares, Django) (1) | 2025.01.24 |
---|---|
[Django] URL dispatcher 공식 문서 파헤치기 (1) | 2025.01.18 |
[Django] User Model, Custom User (Extending User) (0) | 2025.01.11 |
[Django] Django 마이그레이션과 MySQL 활용 (0) | 2025.01.10 |
[Django] Django란? + 기초 실습 (1) | 2025.01.04 |