인프런 커뮤니티 질문&답변

유형주님의 프로필 이미지
유형주

작성한 질문수

파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트

포스팅 쓰기 화면 및 API 구현

안녕하세요 강사님 이미지 파일, 문서 파일(pdf, docs) 업로드 하는데 아래와 같은 에러메시지가 나옵니다.

작성

·

337

0

안녕하세요 강사님 강의 잘 복습하고 있습니다. 

복습중에 antd를 사용하지는 않고 한번 파일을 업로드

해보려 시도중인데 파일(이미지, 문서) 업로드시 아래와 같은

에러가 발생하여 문의드립니다.. ㅜㅜ

위 파일을 제외하고는 장고에 포스트가 잘 작성되는데.. 

해당 파일을 포함하면 위와 같은 에러메시지가 발생합니다.

 

const onChangeTitle = (e) => {
        setTitle(e.target.value)
    }
    console.log(title)
    const onChangeCategory = (e) => {
        setCategory(e.target.value)
    }
    console.log(category)

    const onChangeContent = (e) => {
        setContent(e.target.value)
    }
    console.log(content)

    const handleFileCapture = async (e) => {
        await Setuploadfile({[e.target.name]: e.target.files[0]})
    };
    console.log({attachedfile})

    const handleImageCapture =async (e) => {
        await SetuploadImage({[e.target.name]: e.target.files[0]})
    };
    console.log({coverimg})

    const handleChange = (e) => {
        setChecked(e.target.checked);
    };
    console.log('is_public : ', {checked})




const onSubmit = (e) => {
    e.preventDefault()

    const formData = new FormData();
    formData.append("title", title);
    formData.append("category", category);
    formData.append("content", content);
    formData.append("attached_file", attachedfile);
    formData.append("cover_img", coverimg);
    formData.append("is_public", checked)
    console.log('formData :', formData)

    const headers = {
        Authorization: `JWT ${jwtToken}`,
        "Content-Type": "multipart/form-data",
    };

    Axios.post("http://127.0.0.1:8000/api/posts/", formData, {headers})
        .then(response => {
            history.push('/')
            console.log(response['status'])
        })
        .catch(error => {
            console.log(error.response)
        })
};

위 console.log로 찍어보면 내용이나 파일 값(상태값)은

잘 반영되는거 같은데..  submit만 하면 에러가 발생합니다.ㅜㅜ 

 

 

위는 리액트단 코드이고

아래는 장고입니다.

- models.py

class Post(BaseModel):
	author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='post_author')
	category = models.CharField(max_length=100, default='all')
	title = models.CharField(max_length=100, default='')
	content = models.TextField(default='')
	attached_file = models.FileField(blank=True, upload_to="shareinfo/post/cover/%Y/%m/%d", verbose_name='Attached File')
	cover_img = models.ImageField(blank=True, upload_to="shareinfo/post/cover/%Y/%m/%d", verbose_name='Cover Image')
	post_tag_set = models.ManyToManyField('Tag', blank=True)
	like_user_set = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='post_likes')
	ip = models.GenericIPAddressField(null=True, editable=False)
	is_public = models.BooleanField(default=True)

 

- serializer.py

class PostSerializer(ModelSerializer):
	author = AuthorSerializer(read_only=True)
	is_like = serializers.SerializerMethodField("post_likes_field", read_only=True)
	post_tag_set = serializers.CharField(source='extract_tag_list', read_only=True)

	class Meta:
		model = Post
		fields = ['author', 'is_like', 'post_tag_set', 'title', 'category', 'content', 'attached_file', 'cover_img', 'is_public']

	def post_likes_field(self, post):

 

-views.py

class PostViewSet(ModelViewSet):
	queryset = Post.objects.all().filter(is_public=True)
	parser_classes = [MultiPartParser, FormParser]
	serializer_class = PostSerializer

	def get_queryset(self):
		qs = super().get_queryset()
		qs = qs.filter(
			Q(author=self.request.user) |
			Q(author__in=self.request.user.following_set.all())
		)
		return qs

	def perform_create(self, serializer):
		serializer.save(author = self.request.user, ip=self.request.META['REMOTE_ADDR'])
		return super().perform_create(serializer)

 

파일이 어떠한 형식으로 변환되어야지 장고에 저장이 되는거같은데... 어렵습니다..

답변 1

1

이진석님의 프로필 이미지
이진석
지식공유자

안녕하세요.

attached_file 필드에 대해서 "The submitted data was not a file. Check the encoding type on the form." 에러 메세지는 클라이언트 단에서 서버로의 요청이 정상적인 파일 업로드 요청이 아닌 듯 합니다.

팁을 하나 드리자면, 서버 api 개발과 클라이언트를 동시에 개발하려 하시기 보다 서버 api를 먼저 개발하시고 검증 후에 클라이언트를 개발하시기를 권해드립니다. 어느 하나 확실히 구현된 쪽이 있어야 하는 데, 둘 다 명확하지 않다면 개발에서 혼선이 생길 수 밖에 없습니다.

서버 api 개발을 하실 때 사용하실 수 있는 클라이언트는 httpie도 좋구요. postman과 같은 gui 클라이언트를 써보셔도 좋습니다. 이러한 클라이언트에서 파일 업로드 기능도 제공해주니까 편리하게 요청을 날려보실 수 있습니다. // 이런 툴들을 쓰는 훈련도 꼭 필요합니다.

그리고 DRF에서 모델의 FileField를 그대로 ModelSerializer에 필드명을 그대로 지정하시고, 기본 ModelViewSet에서 queryset과 serializer_class만 지정하셔도 파일 업로드 및 저장은 지원됩니다. 새로운 장고 프로젝트를 만들어서, 테스트를 해보세요.

장고 서버 단에서 요청을 처리하는 뷰에서 breakpoint을 잡아서 request.data 값을 확인해보세요. 그럼 요청 필드 내역들을 손쉽게 확인하실 수 있습니다. // 어떤 값이 오고 가는 지를 눈으로 확인해보시면,  처리되는 프로세스에 대한 이해도가 훨씬 올라가실 겁니다.

지금 파일 업로드 부분이 잘 안 되시는 것이니, 최대한 간소하게 모델을 만들어서 파일 업로드 기능에 포커스를 두시고 구현해보세요. 클라이언트도 마찬가지이구요. 그럼 구현이 훨씬 용이해지실 겁니다.

api 구현이 검증이 되셨다면, 클라이언트 단에서 구현을 이어가시면 되는 데요.

FormData는 브라우저 기본에서 지원하는 기능이기에 아래와 같은 수많은 개발문서를 참고해보실 수 있습니다. 기본 html에서의 예시코드도 잘 설명되어있습니다.
https://www.freecodecamp.org/news/formdata-explained/

FormData를 post 요청을 보내실 때 굳이 Content-Type을 지정하실 필요는 없고 FormData 지정만으로 충분합니다.

보여주신 코드에서 Setuploadfile가 어떤 역할을 하는 지는 잘 모르겠는데요. e.target.files[0] 을 그대로 FormData 인스턴스에 append하셔서 파일 전송을 하실 수 있습니다. 특별한 변환없이도 FormData를 통해 파일 업로드를 하실 수 있습니다.

차근차근 확인해보시고, 또 질문주세요.

화이팅입니다. :-)

유형주님의 프로필 이미지
유형주
질문자

잘 해결했습니다 ㅎㅎ.. FormData 인스턴스에 append 부분에서 문제가 있었습니다

답변 감사드립니다 !!

유형주님의 프로필 이미지
유형주

작성한 질문수

질문하기