2024년 10월 작업

김의석 ·2024년 10월 22일

Hello! Poko Ver.2

목록 보기
18/28

2024년 10월 23일

Nginx 및 보안 설정 강화 문제

문제

  • 문제 요약: Nginx를 통한 보안 강화 작업과 Django 서버에 대한 악성 요청 차단을 위해 Fail2Ban 및 Nginx 설정을 조정하는 과정에서 오류 발생.

  • 오류 메시지:

    • Failed to access socket path: /var/run/fail2ban/fail2ban.sock
    • DisallowedHost: Invalid HTTP_HOST header

문제 해결 과정

  1. Fail2Ban의 Nginx 설정 강화
[nginx-http-auth]
enabled = true
port    = http,https
logpath = /var/log/nginx/error.log
maxretry = 5

[nginx-badbots]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/access.log       
maxretry = 3

[nginx-404]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/access.log
maxretry = 10

Django 출석 데이터 중복 문제

문제

  • 문제 요약: 출석 데이터를 등록하는 과정에서 같은 날짜의 데이터가 중복되어 저장되지 않도록 예외 처리 로직을 추가

  • 오류 메시지:출석 데이터가 이미 저장되어 있습니다.

문제 해결 과정

  1. 출석 데이터 중복 처리
    api의 return과 차이점을 아
if Attendance.objects.filter(date=date).exists():
    raise serializers.ValidationError(
        {"detail": f"{date}의 출석 데이터가 저장되어있습니다."}
    )
  1. React에서의 error 처리
if (error.response && error.response.data && error.response.data.detail) {
    Modal.error({
        title: '출석 데이터 중복',
        content: error.response.data.detail,
    });
}

raise serializers.ValidationError와 return Response의 차이

raise serializers.ValidationError:

  • serializer에서 데이터 유효성 검사를 실패했을 때 사용.
  • 예외 발생 시 처리 방식 : 해당 구문을 사용하면, 예외가 발생하여 즉시 코드 실행이 중단되고, 적절한 오류 메시지가 전달.
  • 에러 메시지 전달
if Attendance.objects.filter(date=date).exists():
    raise serializers.ValidationError(
        {"detail": f"{date}의 출석 데이터가 저장되어있습니다."}
    )
  • Response 결과
{"detail": "해당 날짜의 출석 데이터가 이미 저장되어 있습니다."} 

* 메세지 임의 지정 가능 

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST):

  • serializer가 유효성 검사를 실패했을 때 사용됩니다. 유효성 검사가 실패하면, serializer.errors를 통해 오류 메시지를 반환.
  • 응답 처리 방식: 이 구문은 예외를 발생시키는 대신, 정상적으로 코드가 계속 실행되며, 유효성 검사가 실패한 결과를 반환.
  • 에러 메세지 전달
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  • Response 결과
{
  "date": ["이 필드는 필수 항목입니다."],
  "attendance": ["출석 데이터는 필수입니다."]
}

* 자동 생성된 메세지 

예외처리와 유효성 검증의 차이

즉시 중단 (예외 발생)

  • 즉시 중단: 예외를 발생시키는 raise 구문을 사용하면, 그 시점에서 코드 실행이 중단. 이후의 코드는 실행되지 않고, 예외 처리 메커니즘이 작동.
  • 흐름 제어: 예외가 발생하면, 호출된 함수는 그 자리에서 바로 멈추고, 에러 메시지나 특정 예외 핸들러로 제어가 시작. 예외가 핸들링되지 않으면 프로그램은 종료.
if Attendance.objects.filter(date=date).exists():
    raise serializers.ValidationError(
        {"detail": f"{date}의 출석 데이터가 저장되어있습니다."}
    )
    
# 이 이후의 코드는 실행되지 않는다.

끝까지 실행 후 반환

  • 끝까지 실행: 정상적인 처리나 오류 메시지를 반환하는 return 구문을 사용하면, 해당 함수의 코드가 끝까지 실행된 후에 결과를 반환. 에러가 발생했더라도 예외를 발생시키지 않고, 오류 메시지를 담아 반환.
  • 흐름 제어: 코드 실행은 계속 진행되며, 마지막에 return 구문을 통해 처리 결과가 반환. 만약 검증에서 실패하거나 오류가 발생해도 이를 예외로 처리하지 않고, 결과를 오류로 반환하는 것.
if Attendance.objects.filter(date=date).exists():
    return Response({"error": f"{date}의 출석 데이터가 이미 저장되어있습니다."}, status=status.HTTP_400_BAD_REQUEST)

# 이후 코드도 실행될 수 있음
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)

React-Django 간 날짜 처리 문제

문제

  • 문제 요약: React에서 API로 날짜를 전달할 때, Modal에 표시되는 날짜와 서버로 전송되는 날짜가 다른 것을 확인.

문제 해결 과정

  1. 날짜 포맷 문제: React에서 new Date().toISOString().split('T')[0] 방식으로 날짜를 전송했으나, Modal에서 new Date().toLocaleDateString()으로 출력하여 날짜 차이가 발생함.

  2. 날짜 처리 통일
    API와 Modal 아래와 같이 코드 수정

// API
const getFormattedDate = (date) => {
      const year = date.getFullYear();
      const month = String(date.getMonth() + 1).padStart(2, '0'); 
      const day = String(date.getDate()).padStart(2, '0');
      return `${year}-${month}-${day}`;
    };

 const handleSubmit = async () => {
      const attendanceData = students.map(student => ({
        id : student.id,
        attendance: checkedStudents.includes(student.id)
      }));

      const currentDate = getFormattedDate(new Date());

      try {
        const response = await postAttendanceData(currentDate, attendanceData);
        console.log('서버 응답:', response);
// Modal
          <AttendanceModal
              isOpen={isModalOpen}
              onClose={closeModal}
              students={students}
              checkedStudents={checkedStudents}
              handleCheck={handleCheck}
              handleSubmit={handleSubmit}
              getFormattedDate={getFormattedDate(new Date())}
            >
                
          <p className="modal-subtitle">등록일: <b>{getFormattedDate}</b></p>

2024년 10월 25일

API PATCH API 제작

ModelViewSet의 주요 메서드

  1. create: POST 요청을 처리
  2. list: GET 요청을 처리(여러 리소스 조회)
  3. retrieve: GET 요청을 처리(특정 리소스 조회)
  4. update: PUT 요청을 처리(전체 리소스 업데이트)
  5. partial_update: PATCH 요청을 처리하며(일부 리소스 업데이트)
  6. destroy: DELETE 요청을 처리
  7. get_queryset: 데이터베이스에서 조회할 기본 쿼리셋을 정의하는 메서드
  8. get_object:
    단일 객체를 가져올 때 사용하는 메서드. retrieve, update, partial_update, destroy 메서드에서 호출.

update와 partial_update의 차이

update (PUT 요청)

요청 데이터: 모든 필드를 포함하여 전송
예시:

기존 리소스:

{
    "name": "김민수",
    "grade": "1",
    "gender": "남"
}
PUT 요청 시:

{
    "name": "김민수",
    "grade": "2", 
    "gender": "남"
}
결과:

name: “김민수” (유지)
grade: “2” (업데이트됨)
gender: “남” (유지)
  1. partial_update (PATCH 요청)

요청 데이터: 수정하려는 필드만 포함
예시:

기존 리소스:

{
    "name": "김민수",
    "grade": "1",
    "gender": "남"
}
PATCH 요청 시:

{
    "grade": "2"
}
결과:

name: “김민수” (유지)
grade: “2” (업데이트됨)
gender: “남” (유지)

Custom update 제작

다수의 출석 데이터를 한번에 업데이트

  1. ModelViewSet의 Custom update(bulk_update) 메서드
@action(detail=False, methods=["patch"], url_path="bulk-update")
def bulk_update(self, request, *args, **kwargs):
    serializer = BulkAttendanceSerializer(data=request.data)
        if serializer.is_valid():
        updated_records = serializer.update(None, serializer.validated_data)
           return Response(
            self.get_serializer(updated_records, many=True).data,
            status=status.HTTP_200_OK,
            )
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

동작

@action 데코레이터

  • bulk_update는 @action 데코레이터로 정의되었으며, 이는 DRF에서 ViewSet에 맞춤형 동작을 추가하는 방법. methods=["patch"]를 통해 PATCH 요청을 처리.

  • URL은 /api/attendance/attendance-records/bulk-update/ 형태로 접근.

  • Serializer 데이터 검증:
    BulkAttendanceSerializer로 요청 데이터를 검증.

요청 데이터 구조

{
  "date": "2024-10-24",
  "attendance": [
    {"id": 54, "attendance": false},
    {"id": 55, "attendance": false}
  ]
}
  • BulkAttendanceSerializer를 먼저 검증(validation) 함.

데이터 검증 및 저장

  • serializer.is_valid()에서 데이터가 유효하면, serializer.update()를 호출하여 검증된 데이터를 사용해 출석 데이터를 업데이트.
  • serializer.update() 메서드는 수신한 출석 데이터를 업데이트하고, 업데이트된 기록들을 반환.

응답 생성

  • 업데이트된 출석 데이터를 응답으로 반환. 이때 self.get_serializer(updated_records, many=True)를 통해 여러 레코드를 직렬화.
  1. BulkAttendanceSerializer의 update 메서드
def update(self, instance, validated_data):
    date = validated_data.get("date")
    attendance_list = validated_data.get("attendance")

    updated_records = []

    for attendance_data in attendance_list:
        member_id = attendance_data.get("id")
        attendance_status = attendance_data.get("attendance")

        attendance_record = Attendance.objects.filter(
            name_id=member_id, date=date
        ).first()

        if attendance_record:
            attendance_record.attendance = attendance_status
            attendance_record.save()
            updated_records.append(attendance_record)

    return updated_records

동작

입력 데이터 분리

  • validated_data는 검증된 데이터를 의미. date와 attendance 필드가 각각 추출.
  • attendance_list는 출석 정보의 리스트.

출석 데이터 업데이트

  • attendance_list에서 각 출석 정보를 순회.
  • 각 출석 정보에는 학생의 id와 attendance 상태가 포함.
  • 이 학생의 기존 출석 데이터를 데이터베이스에서 조회.
  • 데이터가 존재하면, 해당 학생의 출석 상태를 업데이트한 후, 데이터베이스에 저장.

업데이트된 출석 기록 저장

  • 수정된 출석 데이터를 updated_records 리스트에 추가.
  • 모든 출석 기록에 대해 업데이트 작업이 완료되면, updated_records를 반환.

전반적인 흐름

  1. PATCH 요청: 클라이언트가 PATCH 요청을 보내면, bulk_update 메서드가 호출.
  2. 데이터 검증: 요청 데이터는 BulkAttendanceSerializer로 검증.
  3. 출석 데이터 수정: 검증된 데이터가 유효하면, 각 학생의 출석 상태를 업데이트하고, 수정된 레코드를 반환.
  4. 응답 반환: 업데이트된 출석 데이터를 응답으로 클라이언트에 반환.

profile
널리 이롭게

0개의 댓글