Django 미니 프로젝트 완성

명단·과제·제출 관계를 통합하고 예외·테스트·README로 10주 결과 검증하기

10주 결과물: 설명 가능한 작은 서비스

1–9주의 명단·과제·제출을 하나의 실행 가능한 로컬 프로젝트로 묶습니다. 새 프레임워크를 추가하는 주가 아닙니다. 다른 팀원이 README만 보고 실행하고 정상 요청뿐 아니라 잘못된 요청도 예상한 응답을 받는 것이 목표입니다.

필수 범위는 회원 CRUD·검색, Admin 과제 관리, 제출 관계·중복 방지, 예외 처리와 테스트입니다. 로그인한 회원이 자기 과제를 제출하는 공개 서비스는 인증·권한 확장까지 완료한 뒤 공개합니다.

이전 주차의 예외를 화면으로 연결하기

9주의 PROTECT 정책 때문에 제출 기록이 있는 회원을 삭제하면 예외가 발생합니다. lions/views.py에 import하고 기존 member_delete를 교체합니다.

Python
from django.db.models.deletion import ProtectedErrorfrom django.http import HttpResponse
@require_POSTdef member_delete(request, pk):    member = get_object_or_404(Member, pk=pk)    try:        member.delete()    except ProtectedError:        return HttpResponse("제출 기록이 있어 삭제할 수 없습니다. 목록으로 돌아가 주세요.", status=409)    return redirect("lions:member-list")

409는 현재 데이터 상태와 충돌해 삭제할 수 없다는 뜻입니다. 모든 예외를 except Exception으로 잡아 성공처럼 반환하지 않습니다. 예상하지 못한 오류는 서버 로그로 원인을 찾고 사용자에게 내부 스택·비밀값을 노출하지 않습니다.

고정 JSON을 반환하던 4주의 member_api도 현재 DB와 연결합니다. @require_GET는 유지합니다.

Python
@require_GETdef member_api(request):    members = list(Member.objects.order_by("id").values("id", "name", "track"))    return JsonResponse({"members": members})

기존 MemberApiTests.test_returns_members는 고정 가상 이름을 기대하므로 테스트 시작 부분에 Member.objects.create(name="가람", track="frontend")를 추가합니다. 빈 DB 테스트도 별도로 추가하면 { "members": [] } 계약을 확인할 수 있습니다. 요구 변경에 맞게 테스트를 바꾸는 이유를 PR에 적습니다.

사용자 관점의 통합 검사

아래 표를 팀원이 직접 실행하고 실제 결과를 기록합니다. 기대 결과를 복사한 것만으로 검증 완료로 표시하지 않습니다.

시나리오기대 결과확인할 데이터
빈 명단 조회빈 상태 안내와 200Member 0개
유효한 회원 등록목록 이동Member 1개 증가
공백 이름·잘못된 트랙입력 오류행 수 변화 없음
기존 회원 수정이름·트랙 반영같은 ID, 행 수 동일
없는 ID 수정404변화 없음
GET 삭제405변화 없음
제출 기록 있는 회원 삭제409회원과 제출 보존
같은 과제 재제출기존 제출 반환Submission 1개
검색 URL 새로 열기같은 조건 적용검색 조건에 맞는 회원

삭제 충돌을 자동 검사하려면 기존 테스트에 추가합니다.

Python
class DeleteConflictTests(TestCase):    def test_keeps_member_with_submission(self):        member = Member.objects.create(name="가람", track="backend")        assignment = Assignment.objects.create(title="최종 실습", due_at=timezone.now())        Submission.objects.create(member=member, assignment=assignment)        response = self.client.post(reverse("lions:member-delete", args=[member.pk]))        self.assertEqual(response.status_code, 409)        self.assertTrue(Member.objects.filter(pk=member.pk).exists())

이 코드는 앞 주 lions/tests.py의 import와 이어집니다. 파일을 따로 분리했다면 TestCase·reverse·timezone·세 모델을 해당 파일에서도 import합니다.

재현 가능한 실행 안내

가상환경에서 python -m pip freeze > requirements.txt로 실제 사용한 버전을 고정합니다. 새 폴더에 저장소를 clone하여 다음 순서가 통하는지 검증합니다.

Shell
python -m venv .venv# 운영체제에 맞게 가상환경 활성화python -m pip install -r requirements.txtpython manage.py migratepython manage.py createsuperuserpython manage.py testpython manage.py runserver

README에는 Python·Django 버전, 실행 명령, /members/·/api/members/·/admin/, 관리자 생성 방법, ERD, 가상 데이터 입력 순서, 남은 한계를 넣습니다. 비밀번호를 README에 공유하지 않습니다. Admin에서 회원→과제→제출 순서로 가상 데이터를 넣으면 fixture 없이도 실습을 재현할 수 있습니다.

세션 운영과 발표

권장 120분: 요구 범위 확정 15분, 예외 연결 30분, 상호 설치·검사 35분, 수정 20분, 발표 20분입니다.

발표는 ① 문제와 사용자 ② 정상 흐름 시연 ③ 잘못된 요청 하나 ④ 관계와 삭제 정책 ⑤ 테스트와 남은 제한 순서로 5분 안에 진행합니다. 기능 개수를 늘리기보다 한 흐름을 처음부터 끝까지 설명합니다.

완료 기준은 독립 설치 성공, 위 시나리오 실제 확인, 테스트 통과, 비밀값 없는 저장소, 미구현 기능의 명시입니다. 공개 배포를 결과물에 포함한다면 DEBUG·HTTPS·허용 호스트·인증·소유자 권한·정적 파일·DB 백업까지 검증해야 합니다.

선택 확장

React와 JSON 계약으로 연결하려면 Django REST API, 외부 사용자를 받으려면 Django 인증·권한과 배포 문서로 이동합니다. 기본 세션에서는 두 확장을 미완료라고 해서 Python·MTV·ORM의 학습 성취를 무효로 평가하지 않습니다.

연결된 PBL 미션과 VOD

주차·미션참고 VOD 범위
10주 · 미니 프로젝트Python 첫걸음 13·14장

5–10주는 동일한 Django 구조·CRUD VOD 범위를 반복 참조한다. 주차별 산출물은 서로 다르므로 문서 실습을 순서대로 확장한다.

강좌 안내: 참고 강좌 1. 이 문서는 영상 전체를 옮긴 전사 자료가 아닙니다. 세부 내용은 위 공식 문서에서 확인합니다.