개발 노트

React && DJango 파일 업로드

j4ko 2020. 12. 14. 00:55
728x90
반응형

 

 

"이 포스팅은 React와 django를 이용해 파일 업로드를 성공한 내용을 기록하는 것이고 django에서 파일 업로드를 하는 전체적인 설정 같은 세부 내용은 다루지 않습니다."

 

Django Setting

django의 기본 개념을 설명하는 글은 아니므로 짧게짧게 기술하도록 하겠습니다.

# 파일 업로드를 위한 모델 생성
[models.py]
from django.db import models

class UploadFileModel(models.Model):
    description = models.CharField(max_length=255)
    files = models.FileField(upload_to="documents", null=True)
    upload_at = models.DateTimeField(auto_now=True)​

위의 코드는 models.py에 파일을 저장할 때 쓰이는 정보들입니다. 

# forms.py
from django import forms

# Model Import 
from .models import UploadFileModel

class DocumentForm(forms.ModelForm):
    class Meta:
        model = UploadFileModel
        fields = ("description","files")

 forms.py는 Front에서 Form을 구현할 떄 이 내용에 맞춰서 form 태그를 구성하면 됩니다. fields의 내용들을 보면 UploadFileModel에서 가져온 내용임을 알 수 있습니다.

# views.py

import json

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator

from .forms import DocumentForm

@method_decorator(csrf_exempt, name="dispatch")
def model_form_upload(request):
    if request.method == "POST":
        form = DocumentForm(request.POST,request.FILES)
        if form.is_valid():
            form.save()
            return HttpResponse(json.dumps({"status": "Success"}))
        else:
            return HttpResponse(json.dumps({"status": "Failed"}))

django를 공부하셨던 분이면 위의 views.py가 하는 행위가 무엇인지 감이 올 겁니다. 세팅은 이 정도로 충분한 것 같습니다. django에서 파일 업로드에 관한 내용을 더 알고 싶으면 docs.djangoproject.com/en/3.1/topics/http/file-uploads/를 통해 읽어봅시다.

 

 

React 

React에서는 간단히 다음과 같이 JSX를 만들어줍시다.

import React from "react";

function Labs() {
    return (
        <>
            <h1>File Upload Test</h1>
            <form onSubmit={uploadModule}>
                description<input type="text" name="description"/>
                <br/>
                files<input type="file" name="files"/>
                <input type="submit" value="SUBMIT"/>
            </form>
        </>
    );
}
export default Labs;

파일 업로드를 구현할 form은 위와 같이 정의했습니다. django model에서 설정한 값대로 "description", "files"를 입력받은 input 태그를 정의합시다. 다음으로는 파일 업로드를 위한 function을 만들어봅시다.

import React from "react";
import axios from "axios"; // axios 모듈 추가

function Labs() {
   
    /* 추가된 코드 */
    const uploadModule = async (e) => {
        e.preventDefault();
        const desc = e.target[0].value;
        
        # event로 file 객체 얻기
        const upload_file = e.target[1].files[0];
        
		# 폼 데이터 생성
        const formData = new FormData();
        formData.append("description", desc);
        formData.append("files", upload_file);
        formData.append("enctype", "multipart/form-data")
		
        # 파일을 업로드 시킬 Server 주소
        const URL = "http://127.0.0.1:8000/uploads/labs"

        axios({
            method: "post",
            url: URL,
            data: formData,
            headers: {
                "Content-Type": "multipart/form-data",

            }
        }).then(function (response) {
            console.log(response)
        })
    }

    return (
        <>
            <h1>File Upload Test</h1>
            <form onSubmit={uploadModule}>
                description<input type="text" name="description" />
                <br />
            files<input type="file" name="files" />

                <input type="submit" value="SUBMIT" />
            </form>

        </>
    );

}

export default Labs;
삽질을 하던 도 중 파일을 업로드할 때는 fecth를 쓸 수도 있고 axios를 쓸 수도 있다고 합니다. 저는 axios를 써서 코드를 작성했습니다.

 

 

삽질한 내용들

처음에는 django에서 파일을 받지 못해서 request 내용에 대한 데이터를 관찰했습니다. 

view.py에서 form.is_valid()가 True가 되지 않아서 "form" 변수에 어떤 값이 저장되는지 확인해봤습니다. 결과는 위와 같이 File에 해당하는 정보가 유효하지 않기 때문에 is_valid() 메서드가 동작이 되지 않아서 File Upload에 실패했습니다.

그렇다면 request.POST와 request.FILES 에는 어떤 값이 있는지 확인할 필요가 있었습니다. request.POST에는 값이 제대로 받아지고 있지만 request.FILES에는 "MultiValueDict"라는 Dict Type이 비워져 있는 걸 확인할 수 있었습니다. 어째선지 여기에다 값을 채우면 파일 업로드가 가능하겠구나라는 느낌이 들어서 앞서 formData에 event 객체를 통해 file을 찾는 구문을 추가했습니다.

const upload_file = e.target[1].files[0];

이후 multiValieDict는 다음과 같은 값을 저장하고 있었습니다.

결과는 다음과 같습니다.

 

728x90
반응형