도커
docker-compose.yml
안좋은 방법
version: '3.8'
services:
db:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
- POSTGRES_DB=promise_db
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/app
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgres://postgres:postgres@db:5432/promise_db
depends_on:
- db
volumes:
postgres_data:
개선 방법
- env 파일 활용
- 데이터베이스 비밀번호 같은 민감한 정보를 .env 파일로 분리하여 관리
- container_name을 직접 지정
- 터미널에서 실행 중인 도커를 확인할 때 어떤 컨테이너인지 한눈에 알아볼 수 있음
- networks를 설정
- 앱의 컨테이너들끼리만 안전하게 통신할 수 있는 전용망을 구축
- 자동화(command 명령어)
- 컨테이너가 켜질 때 python manage.py migrate를 먼저 실행하도록 설정
설정
.env파일 생성
docker-compose.yml 작성
- 도커 컴포즈 안에서
${변수명} 형태를 사용하면 .env 파일에 있는 값을 동적으로 불러옴
version: '3.8'
services:
backend:
build: .
container_name: promise_backend
volumes:
- ./:/app
ports:
- "8000:8000"
env_file:
- .env
environment:
- POSTGRES_HOST=promise_db
depends_on:
- db
networks:
- app_network
command: >
sh -c "python manage.py migrate &&
python manage.py runserver 0.0.0.0:8000"
db:
image: postgres:15
container_name: promise_db
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- app_network
volumes:
postgres_data:
networks:
app_network:
driver: bridge
Django 설정(settings.py) 수정
- 파이썬의 내장 모듈인 os를 사용하면
- 현재 프로그램이 실행되고 있는 운영체제의 환경 변수(os.environ)에 접근할 수 있음
- 도커 컴포즈가
.env의 내용들을 컨테이너 환경 변수로 밀어 넣어 주었기 때문에
- 장고는 텍스트 파일의 비밀번호를 직접 읽는 대신 운영체제에 설정된 값을 안전하게 가져와 사용하는 원리
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
——————————————————————————————————————[비교]—————————————————————————————————————————
import os
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('POSTGRES_DB', 'promise_db'),
'USER': os.environ.get('POSTGRES_USER', 'postgres'),
'PASSWORD': os.environ.get('POSTGRES_PASSWORD', 'your_secret_password'),
'HOST': os.environ.get('POSTGRES_HOST', 'promise_db'),
'PORT': '5432',
}
}
서버 실행
docker compose up --build
문제발생
2026-04-12 14:40:40.554 | Traceback (most recent call last):
2026-04-12 14:40:40.554 | File "/usr/local/lib/python3.13/site-packages/django/db/backends/postgresql/base.py", line 25, in <module>
2026-04-12 14:40:40.554 | import psycopg as Database
2026-04-12 14:40:40.554 | ModuleNotFoundError: No module named 'psycopg'
2026-04-12 14:40:40.554 |
2026-04-12 14:40:40.554 | During handling of the above exception, another exception occurred:
2026-04-12 14:40:40.554 |
2026-04-12 14:40:40.554 | Traceback (most recent call last):
2026-04-12 14:40:40.554 | File "/usr/local/lib/python3.13/site-packages/django/db/backends/postgresql/base.py", line 27, in <module>
2026-04-12 14:40:40.554 | import psycopg2 as Database
2026-04-12 14:40:40.554 | ModuleNotFoundError: No module named 'psycopg2'
2026-04-12 14:40:40.554 |
2026-04-12 14:40:40.554 | During handling of the above exception, another exception occurred:
2026-04-12 14:40:40.554 |
2026-04-12 14:40:40.554 | Traceback (most recent call last):
2026-04-12 14:40:40.555 | File "/app/manage.py", line 23, in <module>
2026-04-12 14:40:40.555 | main()
2026-04-12 14:40:40.555 | ~~~~^^
2026-04-12 14:40:40.555 | File "/app/manage.py", line 19, in main
2026-04-12 14:40:40.555 | execute_from_command_line(sys.argv)
2026-04-12 14:40:40.555 | ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/site-packages/django/core/management/__init__.py", line 443, in execute_from_command_line
2026-04-12 14:40:40.555 | utility.execute()
2026-04-12 14:40:40.555 | ~~~~~~~~~~~~~~~^^
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/site-packages/django/core/management/__init__.py", line 417, in execute
2026-04-12 14:40:40.555 | django.setup()
2026-04-12 14:40:40.555 | ~~~~~~~~~~~~^^
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/site-packages/django/__init__.py", line 24, in setup
2026-04-12 14:40:40.555 | apps.populate(settings.INSTALLED_APPS)
2026-04-12 14:40:40.555 | ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/site-packages/django/apps/registry.py", line 116, in populate
2026-04-12 14:40:40.555 | app_config.import_models()
2026-04-12 14:40:40.555 | ~~~~~~~~~~~~~~~~~~~~~~~~^^
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/site-packages/django/apps/config.py", line 269, in import_models
2026-04-12 14:40:40.555 | self.models_module = import_module(models_module_name)
2026-04-12 14:40:40.555 | ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/importlib/__init__.py", line 88, in import_module
2026-04-12 14:40:40.555 | return _bootstrap._gcd_import(name[level:], package, level)
2026-04-12 14:40:40.555 | ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2026-04-12 14:40:40.555 | File "<frozen importlib._bootstrap>", line 1395, in _gcd_import
2026-04-12 14:40:40.555 | File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
2026-04-12 14:40:40.555 | File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
2026-04-12 14:40:40.555 | File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
2026-04-12 14:40:40.555 | File "<frozen importlib._bootstrap_external>", line 1023, in exec_module
2026-04-12 14:40:40.555 | File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/site-packages/django/contrib/auth/models.py", line 5, in <module>
2026-04-12 14:40:40.555 | from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/site-packages/django/contrib/auth/base_user.py", line 43, in <module>
2026-04-12 14:40:40.555 | class AbstractBaseUser(models.Model):
2026-04-12 14:40:40.555 | ...<120 lines>...
2026-04-12 14:40:40.555 | )
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/site-packages/django/db/models/base.py", line 145, in __new__
2026-04-12 14:40:40.555 | new_class.add_to_class("_meta", Options(meta, app_label))
2026-04-12 14:40:40.555 | ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/site-packages/django/db/models/base.py", line 393, in add_to_class
2026-04-12 14:40:40.555 | value.contribute_to_class(cls, name)
2026-04-12 14:40:40.555 | ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/site-packages/django/db/models/options.py", line 238, in contribute_to_class
2026-04-12 14:40:40.555 | self.db_table, connection.ops.max_name_length()
2026-04-12 14:40:40.555 | ^^^^^^^^^^^^^^
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/site-packages/django/utils/connection.py", line 15, in __getattr__
2026-04-12 14:40:40.555 | return getattr(self._connections[self._alias], item)
2026-04-12 14:40:40.555 | ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/site-packages/django/utils/connection.py", line 62, in __getitem__
2026-04-12 14:40:40.555 | conn = self.create_connection(alias)
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/site-packages/django/db/utils.py", line 196, in create_connection
2026-04-12 14:40:40.555 | backend = load_backend(db["ENGINE"])
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/site-packages/django/db/utils.py", line 116, in load_backend
2026-04-12 14:40:40.555 | return import_module("%s.base" % backend_name)
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/importlib/__init__.py", line 88, in import_module
2026-04-12 14:40:40.555 | return _bootstrap._gcd_import(name[level:], package, level)
2026-04-12 14:40:40.555 | ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2026-04-12 14:40:40.555 | File "/usr/local/lib/python3.13/site-packages/django/db/backends/postgresql/base.py", line 29, in <module>
2026-04-12 14:40:40.555 | raise ImproperlyConfigured("Error loading psycopg2 or psycopg module")
2026-04-12 14:40:40.555 | django.core.exceptions.ImproperlyConfigured: Error loading psycopg2 or psycopg module
원인
ModuleNotFoundError: No module named 'psycopg2'
- 장고 웹 서버가 데이터베이스(PostgreSQL)와 데이터를 주고받으려면
- 중간에서 통역을 해줄 파이썬 전용 통신 패키지가 필요함
- 현재 도커 컨테이너 내부에 이 패키지가 설치되지 않아서 발생한 문제
해결
- poetry add psycopg2-binary

스웨거
drf-spectacular
패키지 설치
poetry add drf-spectacular
settings.py 수정
- REST_FRAMEWORK 설정에서
- 스키마(데이터의 구조와 규칙) 생성 담당자를 drf_spectacular로 교체함으로써
- 앞으로 작성할 모든 데이터 모델과 뷰의 모양새가 OpenAPI 표준에 맞추어 번역되게 됨
THIRD_PARTY_APPS: list[str] = []
——————————————————————————————————————[비교]—————————————————————————————————————————
THIRD_PARTY_APPS: list[str] = [
'rest_framework',
'drf_spectacular',
]
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}
SPECTACULAR_SETTINGS = {
'TITLE': 'Promise App API',
'DESCRIPTION': '약속 잡기 어플리케이션을 위한 백엔드 API 명세서입니다.',
'VERSION': '1.0.0',
}
urls.py
from django.contrib import admin
from django.urls import path, include
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
urlpatterns = [
path('admin/', admin.site.urls),
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
]
결과
