카테고리 없음

내일배움캠프 TIL 48일차

sogummi 2023. 5. 18. 03:16

Today I Learned 

1) 현재 같은 방의 이름을 입력하였을 때뜨는 경고창 메세지가 원하는 내용이 아니라 단순히 TypeError로 뜨는 것이 궁금했음

class RoomsSerializer(serializers.ModelSerializer):
    class Meta:
        model = Rooms
        fields = '__all__'

    def validate(self, attrs):
        if attrs['max_members'] < 0:
            raise ValidationError({'max': '인원은 1인 이상부터 가능 합니다!'})

        if attrs['max_members'] < 0 or attrs['max_members'] > 10:
            raise ValidationError(
                {'max_members': '인원은 1인 이상부터 가능 합니다! 최대 인원은 10명까지입니다.'})

        if check_existing_room(name=attrs['name'], spot=attrs['spot']):
            print("테스트11111")
            raise ValidationError({'name': '이미 존재하는 방입니다!'})
        return attrs

2) RoomsSerializer에서 validate를 커스텀한 error메세지를 프론트 alert창에 출력하고 싶었음

async function createRoom() {
  const accessToken = localStorage.getItem('access')
  const name = document.getElementById("name").value;
  const max_members = parseInt(document.getElementById("max_members").value);
  const spot = document.getElementById("spot").value;
  const status = document.getElementById("status").value;
  const price = parseInt(document.getElementById("price").value);
  const description = document.getElementById("description").value;
  const img = document.getElementById("img");


  const formData = new FormData();
  formData.append("name", name,);
  formData.append("max_members", max_members);
  formData.append("spot", spot);
  formData.append("status", status);
  formData.append("price", price);
  formData.append("description", description);
  formData.append("image", img.files[0]);


  response = await fetch(`http://127.0.0.1:8000/manager/rooms/`, {
    headers: {
      'Authorization': `Bearer ${accessToken}`
    },
    method: 'POST',
    body: formData
  })

    .then(data => {
      console.log("테스트")
      const nameError = data.name[0] || null;
      const maxMembersError = data.max_members[0] || null;
      const descriptionError = data.description[0] || null;
      const priceError = data.price[0] || null;

      if (nameError) {
        alert(`Name Error: ${nameError}`);
      } else if (maxMembersError) {
        alert(`Max Members Error: ${maxMembersError}`);
      } else if (descriptionError) {
        alert(`Description Error: ${descriptionError}`);
      } else if (priceError) {
        alert(`Price Error: ${priceError}`);
      } else {
        alert("객실이 등록되었습니다.");
        window.location.reload();
      }
    })

    .catch(error => {
      alert(error);
    });

TypeError: Cannot read properties of undefined (reading '0') 라는 내용의 alert창이 뜨지만
network - Response에서는 validate에서 정의한 메세지가 출력됨을 볼 수 있었다. 


정의한 에러 메세지가 아닌 TypeError메세지가 뜨는이유 

class RoomView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        admin = get_object_or_404(AdminUser, admin_user=request.user)
        rooms = Rooms.objects.filter(spot=admin.spot)
        serializer = RoomsSerializer(rooms, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

    def post(self, request):
        serializer = RoomsSerializer(data=request.data)
        print(serializer)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            print(serializer.errors)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

is_valid() 메서드는 모든 유효성 검사를 통과해야 True를 반환함

커스텀한

validate() 메서드에서 ValidationError을 발생시키면 is_valid()False가 되고, serializer.errors에 오류 정보가 기록된다. 따라서 serializer.data에는 유효성 검사를 통과하지 못한 필드에 대한 값이 없으며, 해당 필드를 가져올 때 TypeError가 발생하게 되는 것!

또한 이러한 에러를 js에서 처리하는 것이 아닌 js에서는 백엔드에서 작성한 메세지를 출력하도록 구성해야하기 때문에 전체적인 코드의 변화가 필요한 상황

    .then(function (response) {
      if (response.status === 201) {
        // 성공적인 응답(200)의 경우 별도의 로직 실행
        return response.json();
      } else if (response.status === 400) {
        // 에러 응답(400)의 경우 서버의 JSON 데이터를 alert
        return response.json().then(function (data) {
          for (let key in data) {
            alert(`${key} : ${data[key]}`);
          }
          return Promise.reject('서버 에러')
        });
      } else {
        // 다른 상태 코드의 경우 에러 처리
        return Promise.reject('서버 응답 에러');
      }
    })
    .then(function (data) {
      // 별도의 로직 실행 (성공적인 응답인 경우에만 도달)
      console.log(data);
      alert("객실이 등록되었습니다.");
    })
    .catch(function (error) {
      // 에러 처리
      console.error(error);

    });

위 코드처럼 status에 따라 별도의 로직을 작성해주면 됨
Serializer에서 에러를 발생하면 response로 400status를 반환해주고,
js에서는 status가 400일 경우의 로직을 처리해주면 되는데 이 때도 일일이 에러마다 처리해주는 코드를 작성하는 것이 아니라 for문과 딕셔너리를 사용하여 에러의 value값을 alert에 뜨도록 해주면 된다.
=> Serializer의 validate에러 메세지에서도 딕셔너리 형태로 보내주어야 함 



+) 

is_valid() 메서드를 통과했지만 커스텀 유효성 검사에서 실패한 경우에도 에러 메시지와 객실 등록 메시지가 모두 표시되는 이유는 Promise.reject를 사용하지 않았기 때문!
return 값으로 Promise.reject를 작성하지 않으면 일어나는 현상이다.
Promise.reject를 작성해야 에러 상태로 전환되어 catch error 블록으로 이동하기 때문(js는 순서대로 실행됨)