
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
- db와 proxy 컨테이너는 정상 실행
- 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 포트 미허용
해결 방법

- AWS EC2 → Security Group → Inbound Rules 설정
- 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 필드를 전제로 동작 → 충돌 발생
진단 절차
- admin.site.register(models.User)로 기본 UserAdmin을 그대로 등록했는지 확인
- 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는 서비스 실행 순서를 보장하지 않음 (병렬 실행)
진단 절차
- DB 컨테이너 로그에서 database system is ready to accept connections 메시지 시점 확인
- 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로 처리
'Programming > Django' 카테고리의 다른 글
| [Django] VS code에서 Debug하는 법 (0) | 2025.09.22 |
|---|---|
| EC2에서 Django + Docker 배포 중 발생한 run.sh 오류 해결기 (3) | 2025.08.14 |
| [Django] 중첩된 시리얼라이저(Nested Serializers) 완벽 이해하기 (1) | 2025.08.11 |
| DRF(Django REST Framework) 개발자를 위한 비교: APIView vs. Viewsets 🧐 (2) | 2025.08.07 |
| Serializer, 대체 뭘까? 🧐 (3) | 2025.08.07 |