본문 바로가기
Programming/Django

해결 이슈 모음: Django, Docker 기반 Rest API 서비스를 만들면서..

by Mandy's 2025. 8. 20.
EC2 로 띄운 Swagger 기반 API 기능을 담은 웹페이지

recipe에 관련한 간단한 API 서비스를 개발하고, 이를 Swagger 기반의 웹 문서로 제공하는 작업을 진행하였습니다.

API 서버는 Django REST Framework(DRF)로 작성되었고, Docker와 AWS EC2 환경에서 배포하였습니다.

실습과 배포 과정에서 다양한 에러를 경험하고 해결하였으며, 그 사례들을 정리하여 공유합니다.


1) EC2에서 Django 프로젝트를 Docker로 배포하는 과정에서 발생한 run.sh 오류

문제 상황

 
docker-compose -f docker-compose-deploy.yml up -d​

오류 메시지:

Error response from daemon: failed to create task for container: exec: "run.sh": executable file not found in $PATH
  • dbproxy 컨테이너는 정상 실행
  • app 컨테이너만 실행 실패

원인 분석

  • app 컨테이너는 원래 run.sh를 사용하지 않음
  • 기존 Docker 이미지에 CMD/ENTRYPOINT가 run.sh로 남아 있음
  • Docker 캐시 때문에 여전히 run.sh 실행을 시도 → 실행 실패

해결 과정

Step 1. 기존 컨테이너 및 이미지 삭제

docker-compose -f docker-compose-deploy.yml down -v docker rmi <app-image-id>
  • -v: DB/Static 볼륨 제거 (필요 시)
  • 기존 잘못된 app 이미지 삭제

Step 2. 캐시 없이 재빌드

docker-compose -f docker-compose-deploy.yml build --no-cache app
  • 새로운 이미지 빌드 → Dockerfile 설정대로 CMD 적용

Step 3. 포그라운드 실행 확인

docker-compose -f docker-compose-deploy.yml up app
  • 정상 실행 여부 확인
  • run.sh 관련 오류 재발 없음

Step 4. 백그라운드 실행

docker-compose -f docker-compose-deploy.yml up -d
  • 모든 컨테이너(app, db, proxy) 정상 실행

EC2 접속 문제 해결

  • 오류: ERR_CONNECTION_REFUSED
  • 원인: EC2 보안 그룹에서 8000 포트 미허용

해결 방법

  1. AWS EC2 → Security Group → Inbound Rules 설정
  2. TCP 8000 포트, Source: 0.0.0.0/0 허용

접속 URL

  • Django Admin: http://<EC2 퍼블릭 DNS>:8000/admin
  • API 문서(DRF 자동 문서화): http://<EC2 퍼블릭 DNS>:8000/api/docs/

추가 확인 사항

  • ALLOWED_HOSTS 설정: 퍼블릭 DNS나 IP 포함 필수
  • 컨테이너 로그 확인: 포트 바인딩, 마이그레이션 에러 여부 확인
  • API 엔드포인트 테스트: /api/, /api/v1/ 응답 상태로 정상 여부 판별

핵심 포인트

  • app 컨테이너는 run.sh 필요 없음 → CMD는 manage.py 실행
  • proxy 컨테이너만 run.sh 사용
  • 이미지 캐시 문제 주의: 기존 이미지 삭제 + 캐시 없이 재빌드 필수
  • 외부 접속 문제 해결: 보안 그룹 포트 허용 + ALLOWED_HOSTS 설정 확인

2) FieldError: Unknown field(s) (username) specified for User. (Django Admin)

문제

  • Admin 페이지 접근/리스트/생성 테스트 중 username 관련 FieldError 발생

원인

  • 이메일을 아이디로 쓰는 커스텀 User 모델 사용 중
  • 기본 UserAdmin은 username 필드를 전제로 동작 → 충돌 발생

진단 절차

  1. admin.site.register(models.User)로 기본 UserAdmin을 그대로 등록했는지 확인
  2. AUTH_USER_MODEL이 커스텀 모델로 올바르게 지정되었는지 확인

해결 과정

  • UserAdmin을 커스터마이징하여 username 필드를 제거하고 이메일 기반 필드셋으로 재정의
  • fieldsets, add_fieldsets, list_display, ordering 등을 직접 정의
class UserAdmin(BaseUserAdmin):
    ordering = ['id']
    list_display = ['email', 'name']
    fieldsets = (
        (None, {'fields': ('email', 'password')}),
        (_('Permissions'), {'fields': ('is_active','is_staff','is_superuser')}),
        (_('Important dates'), {'fields': ('last_login',)}),
    )
    readonly_fields = ['last_login']
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email','password1','password2','name','is_active','is_staff','is_superuser'),
        }),
    )

admin.site.register(models.User, UserAdmin)
  • 커스텀 유저 모델을 쓰면 반드시 전용 UserAdmin 작성
  • Admin 페이지 테스트(list, change, add)를 기본 세트로 유지

3) OperationalError (DB 준비 전 애플리케이션 기동: Race Condition)

증상

  • Docker/CI에서 간헐적 DB 연결 실패 발생
  • OperationalError: could not connect to server 메시지 확인

원인

  • PostgreSQL 컨테이너가 기동/초기화 완료 전에 Django가 연결 시도
  • Docker Compose는 서비스 실행 순서를 보장하지 않음 (병렬 실행)

진단 절차

  1. DB 컨테이너 로그에서 database system is ready to accept connections 메시지 시점 확인
  2. Django 컨테이너 시작 시점과 비교해 조기 실행 여부 판별

해결 과정

  • wait_for_db라는 Django 커스텀 커맨드 작성
  • DB 연결 가능 여부 확인 후 실패 시 1초 대기 → 재시도 반복
class Command(BaseCommand):
    def handle(self, *args, **options):
        self.stdout.write('Waiting for database...')
        db_up = False
        while not db_up:
            try:
                self.check(databases=['default'])
                db_up = True
            except (Psycopg2OpError, OperationalError):
                self.stdout.write('Database unavailable, waiting 1 second...')
                time.sleep(1)

        self.stdout.write(self.style.SUCCESS('Database available!'))
  • docker-compose.yml에 적용:
command: >
  sh -c "python manage.py wait_for_db && \
         python manage.py migrate && \
         python manage.py runserver 0.0.0.0:8000"
 
  • GitHub Actions에서도 테스트 실행 전 python manage.py wait_for_db 추가

재발 방지

  • DB 기동 → 마이그레이션 → 앱 실행 순서를 하나의 명령어로 고정
  • 테스트/배포 파이프라인에서 동일한 흐름을 공유
  • Postgres healthcheck를 보조적으로 사용하되, 최종 보장은 wait_for_db로 처리