출석부 초기 이슈 정리

김의석 ·2024년 10월 22일

Hello! Poko Ver.2

목록 보기
16/28

migrations 이슈

Django의 migrations 문제 및 데이터베이스 충돌. 특정 마이그레이션들이 서로 의존 관계가 맞지 않아 충돌 발생.

문제

  • 문제 요약:

    • Django migrations 실행 중 InconsistentMigrationHistory 오류가 발생함.
    • 오류 메시지: "Migration report.0001_initial is applied before its dependency attendance.0001_initial on database 'default'."
    • 두 앱(reportattendance) 간의 migration 의존성 충돌 문제.
    • report 앱의 마이그레이션이 attendance 앱의 마이그레이션에 의존하지만, 두 마이그레이션 간 순서가 맞지 않아 오류가 발생함.
  • 오류 메시지:

    • django.db.migrations.exceptions.InconsistentMigrationHistory
    • "table "report_membercheck" already exists" (같은 테이블 중복 생성 오류)

문제 해결 과정

  1. 마이그레이션 충돌 문제 해결:

    • attendance 앱의 migration 파일 삭제: attendance 앱의 마이그레이션 파일을 삭제하여 마이그레이션 상태를 초기화함.
    • 다시 migrate 시도: python manage.py migrate attendance zero 명령어로 마이그레이션 초기화 시도.
    • InconsistentMigrationHistory 오류 해결: 이후 report 앱과 attendance 앱 간의 마이그레이션 순서를 맞추기 위해, attendance 앱의 마이그레이션이 먼저 적용되도록 설정.
  2. 이미 존재하는 테이블 삭제:

    • DB shell을 통한 해결:
      • sqlite3.OperationalError: table "report_membercheck" already exists라는 메시지에 따라, 데이터베이스 쉘에 접속하여 이미 존재하는 테이블을 직접 삭제함.
      • 해당 테이블(report_membercheck)을 삭제한 후 마이그레이션을 다시 실행하여 문제 해결.
      • Django DB shell 명령어 사용:
        • python manage.py dbshell을 통해 데이터베이스 쉘에 접속.
        • DROP TABLE report_membercheck; 명령어로 테이블을 삭제.
  3. 정상적인 마이그레이션 완료:

    • attendancereport 앱의 migration을 성공적으로 수행하여 데이터베이스 구조 충돌 문제를 해결함.

Lightsail 개발 서버에서 발생한 Django Migrations 문제

문제 요약

  • 문제 상황: AWS Lightsail 개발 서버에서 report 앱의 마이그레이션을 정상적으로 수행했음에도 불구하고, PostgreSQL 데이터베이스에 report 관련 테이블이 생성되지 않는 문제가 발생.
  • 오류 메시지: 마이그레이션은 정상적으로 완료된 것으로 나타났으나, PostgreSQL에서 테이블이 보이지 않음.
  • 발생 원인: PostgreSQL과 Django 마이그레이션 사이에 일종의 불일치가 발생해, 자동으로 테이블이 생성되지 않는 문제가 발생. 로컬 환경에서는 SQLite3를 사용하여 정상적으로 작동하지만, 배포된 PostgreSQL 환경에서는 테이블이 생성되지 않음.

문제 해결 과정

  1. 마이그레이션 파일 확인 및 재실행:

    • Django에서 report 앱의 마이그레이션 상태를 확인 (python3 manage.py showmigrations report).
    • 마이그레이션은 정상적으로 적용된 것으로 확인되었으나, 테이블은 여전히 PostgreSQL에 생성되지 않음.
    • 여러 차례 마이그레이션을 초기화하고 다시 실행했으나, 문제는 해결되지 않음.
  2. SQL로 마이그레이션 파일 변환 (sqlmigrate 사용):

    • Django의 python3 manage.py sqlmigrate <app_name> <migration_number> 명령어를 사용하여, 마이그레이션 파일이 실제로 어떤 SQL 명령어를 수행하는지 확인.
    • 예시 명령어:
      python3 manage.py sqlmigrate report 0001
    • 이 명령어를 사용하여 마이그레이션 파일이 데이터베이스에서 수행할 SQL 명령어를 출력하고, 이 명령어를 PostgreSQL에 수동으로 입력하여 테이블을 생성함.
  3. PostgreSQL 수동 테이블 생성:

    • sqlmigrate 명령어로 생성된 SQL 스크립트를 복사하여 PostgreSQL에 수동으로 실행.
    • 수동으로 실행한 SQL 명령어를 사용하여, Django의 report 앱에 필요한 테이블을 PostgreSQL에 생성.
    • 생성된 테이블:
      • report_membercheck
      • report_comment
      • report_usercheck
  4. 외래키 및 인덱스 수동 생성:

    • 테이블 생성 후, 외래키 제약 조건과 인덱스를 추가하여 Django ORM에서 정상적으로 사용할 수 있도록 설정.
    • 수동으로 SQL 명령어를 통해 외래키와 인덱스를 모두 PostgreSQL에 적용.
  5. 문제 해결:

    • PostgreSQL에 report 앱의 테이블이 수동으로 생성되었으며, Django에서 해당 테이블을 정상적으로 사용할 수 있음.
    • 테이블을 수동으로 생성한 후, 데이터베이스와 Django 마이그레이션 시스템의 일관성을 확보하기 위해 추가 조치가 필요함.

결론

  • Django 마이그레이션 시스템이 PostgreSQL에서 테이블을 자동으로 생성하지 못하는 문제가 원인이었음.
  • 일시적인 해결책으로 python3 manage.py sqlmigrate 명령어를 통해 마이그레이션 파일을 SQL로 변환하고, 이를 수동으로 PostgreSQL에 적용.
  • 이후 자동 마이그레이션으로 돌아가기 위해, 마이그레이션 파일과 데이터베이스 상태를 다시 동기화할 필요가 있음.

추가 정보: sqlmigrate 명령어 사용 방법

  • 사용법: python3 manage.py sqlmigrate <app_name> <migration_number>
  • 기능: 마이그레이션 파일이 수행할 SQL 명령어를 출력하는 명령어. 해당 SQL을 직접 데이터베이스에 수동으로 적용할 수 있음.

수동 테이블 생성 및 ORM 작동 확인

문제:

  • 문제 요약: report 앱의 테이블이 PostgreSQL에서 정상적으로 생성되지 않는 문제 발생. Django의 자동 마이그레이션 기능을 사용하여 테이블을 생성하지 못하는 상황이 발생했음.
  • 해결 시도: Django에서 migrate 명령어가 정상적으로 실행되었으나, 테이블은 생성되지 않음.

문제 해결 과정:

  1. 수동으로 테이블 생성:

    • python3 manage.py sqlmigrate report 0001_initial 명령어를 통해 SQL 생성 쿼리를 추출.
    • 추출한 SQL을 PostgreSQL 데이터베이스에 수동으로 실행하여 테이블을 생성.
    • 해당 SQL을 수동으로 실행하여 report_comment, report_membercheck, report_usercheck 테이블을 생성.
  2. ORM 작동 확인:

    • Django ORM을 사용해 수동으로 생성된 테이블에서 데이터의 삽입 및 조회를 테스트.
    • 예시:
      from report.models import MemberCheck
      MemberCheck.objects.all()  # ORM을 통해 데이터 조회
    • 데이터 조회 결과, ORM이 정상적으로 작동함을 확인함.
  3. 테이블 관계 설정:

    • 외래 키 제약 조건을 포함한 관계 설정을 SQL로 수동 적용:
      ALTER TABLE "report_membercheck" ADD CONSTRAINT "report_membercheck_name_id_3247e6db_fk_attendance_member_name" FOREIGN KEY ("name_id") REFERENCES "attendance_member" ("name") DEFERRABLE INITIALLY DEFERRED;
  4. 결과:

    • 수동으로 테이블을 생성하고 Django ORM이 정상적으로 작동함을 확인.

React-Django 간 CORS 이슈

  • 문제점: React에서 Django API로 데이터를 요청할 때 Network Error가 발생하는 이슈.
  • 해결 과정:
    1. Django의 settings.py 파일에서 CORS_ALLOWED_ORIGINS에 React 앱의 주소를 추가하여 문제를 해결.
    2. CORS_ALLOW_CREDENTIALSTrue로 설정하여 자격 증명을 허용.
    CORS_ALLOWED_ORIGINS = [
        "http://localhost:3000",  # React 앱의 주소
        "http://poko-dev.com",  # 도메인 이름 (http 사용)
        "https://poko-dev.com",  # 도메인 이름 (https 사용)
    ]
    CORS_ALLOW_CREDENTIALS = True
    • 이후 React에서 Django API를 정상적으로 호출하여 데이터를 가져올 수 있게 됨.

Serializer HTTP 요청에 따라 처리하는 로직 변경 이슈

  • 문제점: HTTP 요청(GET, POST, PATCH 등)에 따라 응답하는 데이터의 필드를 다르게 처리할 필요가 있음.

  • 해결 과정:

    1. get_serializer_class 메서드를 이용해 요청 메서드에 따라 다른 Serializer를 반환하도록 구현.
    2. GET 요청에는 MemberSerializer를, POST나 PATCH 요청에는 FullMemberSerializer를 사용하도록 로직을 분리.
    class MembersViewSet(ModelViewSet):
        serializer_class = MemberSerializer
    
        def get_queryset(self):
            user = "teacher1@example.com"
            return Member.objects.filter(teacher__email=user)
    
        def get_serializer_class(self):
            if self.action in ["list", "retrieve"]:
                return MemberSerializer
            elif self.action in ["update", "create"]:
                return FullMemberSerializer
            return super().get_serializer_class()
    • MemberSerializer: id와 name만 반환.
    • FullMemberSerializer: 모든 필드를 처리.

React의 student, attendancedata가 Django API response 데이터를 매핑하는 로직

  • 문제점: React에서 학생(student)과 출석 데이터(attendanceData)가 Django에서 반환된 데이터를 올바르게 매핑하는 데 어려움 발생.
  • 해결 과정:
    1. AttendanceChart.js에서 useMemoreduce 함수를 사용하여 학생 정보와 출석 데이터를 각각 매핑.
    2. Django에서 반환된 데이터를 학생 ID와 날짜별로 처리하여 각 학생의 출석 여부를 테이블 형식으로 표시.
    const dataSource = useMemo(() => {
        return students.map(student => {
            const studentAttendance = data.reduce((acc, dateEntry) => {
                acc[dateEntry.date] = dateEntry.attendance[student.id];  
                return acc;
            }, {});
            return {
                key: student.id,
                name: student.name,
                ...studentAttendance,
            };
        });
    }, [data, students]);

Django attendance app의 Attendance table date, attendance 필드 유형 변경시 migrations 파일 수정을 통해 데이터를 유지하면서 변경한 이슈

  • 문제점: attendance 필드를 CharField에서 BooleanField로, date 필드를 DateTimeField에서 DateField로 변경하면서 기존 데이터를 유지하고 변환해야 함.
  • 해결 과정:
    1. 마이그레이션 파일을 수정하여 기존 데이터를 변환하는 작업을 추가.
    2. "출석"을 True, "결석"을 False로 변환하고, DateTime에서 Date만 남겨서 데이터 변환.
    def convert_attendance_and_date(apps, schema_editor):
        Attendance = apps.get_model("attendance", "Attendance")
        
        for record in Attendance.objects.all():
            if record.attendance == "출석":
                record.attendance = True
            elif record.attendance == "결석":
                record.attendance = False
            record.date = record.date.date()  # DateTime에서 Date만 추출
            record.save()

React UI 올바른 표기를 위해 Django API response 데이터 구조를 중첩 구조로 변경한 이슈

  • 문제점: Django API에서 데이터를 받아온 후, React UI에서 각 학생의 출석 정보가 올바르게 표시되지 않는 문제.

  • 해결 과정:

    1. Django API에서 반환하는 출석 데이터를 날짜별 중첩 구조로 변경하여, 각 날짜에 해당 학생의 출석 상태를 포함.
    2. Django AttendanceViewSet에서 list 메서드를 수정해 데이터를 구조화.
    def list(self, request, *args, **kwargs):
        queryset = self.get_queryset()
        attendance_data = defaultdict(dict)
    
        for record in queryset:
            date_str = record.date.strftime("%Y-%m-%d")
            attendance_data[date_str][record.name.id] = record.attendance
    
        response_data = [
            {"date": date, "attendance": attendance}
            for date, attendance in attendance_data.items()
        ]
    
        return Response(response_data)
    • 이 변경을 통해 날짜별로 학생의 출석 정보가 정확하게 매핑되고, React UI에서 정상적으로 출력됨.

profile
널리 이롭게

0개의 댓글