
TroubleShooting
1) Django의 DATABASE 설정이 docker-compose 기준(HOST=db)인데,
단독 docker run으로 실행한 Postgres는 해당 이름/네트워크가 아니어서
Django가 DB를 찾지 못해 connection refused 발생.
2) Django가 PostgreSQL(DB)에 접속하려고 했는데, localhost:5432에서 실행 중인 Postgres 서버가 아예 없음. → 그래서 showmigrations가 DB에 연결을 못하고 터짐.
3) docker compose -f docker-compose.local.yml up db -d 로 실행하면
compose 네트워크·서비스 이름(db)·환경변수가 Django 설정과 정확히 일치하여
Django → Postgres 연결이 정상적으로 이루어져 문제 해결됨.

git branch --set-upstream-to=origin/develop developgit pull -u origin developpermission_classes = [AllowAny]docker compose -f docker-compose.local.yml down -v
docker compose -f docker-compose.local.yml up -d
python manage.py migrate

poetry run black .
git add .
git commit -m "chore: format code with black"
git push

poetry run dmypy run -- . 오류-> None 추가def create(self, validated_data):
↓
from typing import Any, Dict
def create(self, validated_data: Dict[str, Any]) -> Question:
from rest_framework.request import Request
from rest_framework.response import Response
def post(self, request: Request) -> Response:

You have 2 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): courses.

path("spec/questions", QuestionCreateSpecAPIView.as_view()),
path("spec/questions", QuestionListSpecAPIView.as_view()),# 실제 API → View가 하나 → Method는 View 내부에서 분기
class QuestionAPIView(APIView):
def get(self): ...
def post(self): ...
/spec/questions # GET (목록 스펙)
/spec/questions/create # POST (등록 스펙)
# 기존
def custom_exception_handler(
exc: Exception,
context: dict[str, Any],
) -> Optional[Response]:
response = exception_handler(exc, context)
if response is None:
return None
view = context.get("view")
is_question_create_api = isinstance(view, QuestionCreateAPIView)
# "포맷 통일" detail -> error_detail(메시지 내용은 바꾸지 않음)
## 403도 따로 설정하지 않아도 포맷되서 error_detail로 나옴
if isinstance(response.data, dict) and "detail" in response.data:
response.data = {"error_detail": response.data["detail"]}
# 1) 400: serializer validation
if isinstance(exc, ValidationError) and is_question_create_api:
response.data = {"error_detail": "유효하지 않은 질문 등록 요청입니다."}
# 2) 401: 인증 안 됨
elif isinstance(exc, NotAuthenticated) and is_question_create_api:
response.data = {"error_detail": "로그인한 수강생만 질문을 등록할 수 있습니다."}
# 3) 404: 카테고리 없음
elif isinstance(exc, CategoryNotFoundError) and is_question_create_api:
response.data = {"error_detail": str(exc.detail)}
# 4) 409: 중복제목
elif isinstance(exc, DuplicateQuestionTitleError) and is_question_create_api:
response.data = {"error_detail": str(exc.detail)}
return response
# 변화
def custom_exception_handler(exc, context):
response = exception_handler(exc, context)
if response is None:
return None
request = context.get("request")
method = request.method if request else None
# 공통 포맷 통일 -> 403도 알아서 처리
if isinstance(response.data, dict) and "detail" in response.data:
response.data = {"error_detail": response.data["detail"]}
# POST /questions (등록)
if method == "POST":
if isinstance(exc, ValidationError):
response.data = {"error_detail": "유효하지 않은 질문 등록 요청입니다."}
elif isinstance(exc, NotAuthenticated):
response.data = {"error_detail": "로그인한 수강생만 질문을 등록할 수 있습니다."}
elif isinstance(exc, CategoryNotFoundError):
response.data = {"error_detail": str(exc.detail)}
elif isinstance(exc, DuplicateQuestionTitleError):
response.data = {"error_detail": str(exc.detail)}
# GET /questions (조회)
elif method == "GET":
if isinstance(exc, ValidationError):
response.data = {"error_detail": "유효하지 않은 질문 목록 조회 요청입니다."}
return response
class QuestionAPIView(APIView):
def get(self, request, *args, **kwargs):
return QuestionListAPIView.as_view()(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return QuestionCreateAPIView.as_view()(request, *args, **kwargs)
class QuestionAPIView(APIView):
authentication_classes = []
def get_permissions(self) -> list[BasePermission]:
# GET: 모두 허용
if self.request.method == "GET":
return []
# POST: 질문 등록 권한
if self.request.method == "POST":
return [QuestionCreatePermission()]
return []
authentication_classes = []를 박아버림 authentication_classes = []만 지우면 class QuestionAPIView(APIView):
def get_authenticators(self):
if self.request.method == "GET":
return []
return super().get_authenticators()
def get_permissions(self):
if self.request.method == "POST":
return [QuestionCreatePermission()]
return []
# GET /questions
get_authenticators()
→ []
→ 인증 생략
→ request.user = AnonymousUser
get_permissions()
→ []
→ 권한 검사 생략
→ 목록 조회 성공 (200)
POST /questions
get_authenticators()
→ super()
→ [JWTAuthentication]
JWTAuthentication.authenticate()
→ 토큰 검증
→ request.user = User
get_permissions()
→ [QuestionCreatePermission]
→ request.user.is_authenticated == True
→ 질문 생성 성공