React

[React] 다중 파일 전송 material ui, axios

carrotdy 2022. 7. 27. 15:02

1. FORM 작성

material ui를 사용하여 form을 작성하였다.

TextField는 input과 같은 속성을 가지고 있으며 pdf,img 외에는 파일을 올릴 수 없도록 설정하였다.

그리고 다중 파일을 업로드 하려면 multiple 속성은 반드시 붙여야한다.

 

<Grid
    item
    xs={10}
    sm={10}
    sx={{ mt: { xs: 20, md: 10 }, margin: "0 auto" }}
 >
    <label htmlFor="input-file" onChange={handleAddImages}>
      <TextField                           //material ui 사용
        type="file"
        inputProps={{
          accept: "application/pdf,image/*",
          multiple: true,
        }}
        {...register("files", { required: false })}
        onChange={onUploadImage}          //파일업로드하면 onChange 함수 실행
      />
    </label>
  </Grid>
 <Box sx={{ display: "flex", justifyContent: "center" }}>
    <LoadingButton
      color="primary"
      variant="contained"
      loading={isLoading}
      onClick={openModal}
      sx={{ mt: 5 }}
    >
      작성완료
    </LoadingButton>
    {confirmOpen && (
      <ConfirmDialog
        open={confirmOpen}
        content="게시글을 등록하시겠습니까?"
        onClose={setConfirmOpen}
        cancelText="취소"
        okText="등록"
        onConfirm={() => onConfirm(files)}
      />
    )}
 </Box>

 

 


 

2. formData 객체에 담기

 

파일업로드를 누르면 onUploadImage 함수가 실행이된다.

 

onChange

onChange함수는 이벤트 객체 e를 파라미터로 받아와서 사용할 수 있는데, 이 객채의 e.target은 이벤트가 발생한 DOM인 input DOM을 가르키게 된다.

즉, DOM의 value값, e.target.value를 조회하면 현재 input에 입력한 값이 무엇인지 알 수 있다.

 

 

formData

 

formData.append('key', value);

 

formData 객체는 XMLHttpRequest 전송을 위하여 설계된 특수한 객체 형태 이다.
특수한 개체이다 보니 특정한 조작을 가하지 않으면 문자열 화도 불가능하다.

 

파일을 http 통신으로 전송하려면 formData 객체를 이용해서 보내야한다.

formData의 객체는 key, value 형식으로 되어있는 객체이다. 

파일을 다중으로 formData에 추가하려면 아래와 같이 반복문을 활용해야한다.

 

 

const [files, setFiles] = useState<Array<ImageType.ImageModel>>([]);
export namespace ImageType {
  export interface ImageModel {
    id: number;
    originalName: string;
    downloadUrl: string;
  }
}
 
  const onUploadImage = async (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files) {
      const uploadFile = e.target.files;          // form의 input 속성을 가져옴
      const formData = new FormData();            // formData 객체 생성
      for (let i = 0; i < uploadFile.length; i++) {  
        formData.append("files", uploadFile[i]);  //반복문을 활용하여 파일들을 formDataR객체에 추가
      }

   const response = await ImageService.Image.uploadImage(formData);  //axios 서버 호출
      if (response.status === 201) {
        const existFiles = files;                    //기존파일
        const newFiles = response.data.result;       //새로운파일
        setFiles(existFiles.concat(newFiles));       //기존파일과 새로운파일을 하나의 배열로 만듬
        console.log(files);      // 아래 이미지 참고
      } else {
        toast.error(response.data.message);
      }
    }
  };
 
 
setFiles(existFiles.concat(newFiles)); concat을 이용해서 기존파일과 새로운 파일을 합쳐도 되지만,
스프레드 연산자를 사용하는것도 좋다. // setFiles([...exisFiles, ...newFiles]);
 
 
Spread Operator
스프레드 연산자를 사용하면 배열, 문자열, 객체 등 반복 가능한 객체를 개별 요소를 분리할 수 있다. 
 
 
 
 
console.log(files)를 입력하면 아래와 같이 콘솔이 찍힌다.
 

 


 

3. 전송하기

 

게시글을 작성하고 파일도 올리고나서 작성완료 버튼을 클릭하면 실행하는 함수이다.
getValues를 활용하여 form에 있는 값들, file의 id, 그리고 content를 data에 저장한다음 
axios를 이용하여 전송해준다.
 
 
 
fileIds : files에 있는 id를 가져오기 위해 바로 map을 돌려 id값만 배열 형식으로 가져왔는데 
아래와 같은 방법으로도 사용할 수 있다.
 
     let arrValues = [];

     for (let i = 0; i < files.length; i++) {
     arrValues.push(files[i].id);

     console.log('arrValues', arrValues);
}​
 
const onConfirm = async (files: Array<ImageType.ImageModel>) => {
    setLoading(true);

    if (content?.trim() === "") {    //내용을 입력하지 않으면 alert창이 뜬다.
      alert("내용을 입력해주세요.");
      setLoading(false);
      return;
    }

    const form = getValues();   
    const data = { ...form, fileIds: files.map((data) => data.id), content };

    const response = await BusinessService.Business.createBusiness(data);

    if (response.status === 201) {
      location.href = Path.business.business;
    } else {
      toast.error(response.data.message);
    }
    setLoading(false);
  };
export namespace BusinessService {
  export const Business = {
    createBusiness: async (
      model: BusinessBindingModel.IBusinessCreateBindingModel,
    ) => {
      try {
        const { data, status } = await AxiosContext.post(
          `/api/sales-data`,
          model,
        );
        return { data: data, status: status };
      } catch (error) {
        return { error: error };
      }
    },
export namespace BusinessBindingModel {
  export interface IBusinessCreateBindingModel {
    title: string;
    content: string;
    isOpen: boolean;
    fileIds: number[];
    files?: Array<ImageType.ImageModel>;
}

 

 


 

참고 (concat, spread operator 예제)

// concat
let arr1 = [1,2,3]; 
let arr2 = [4,5,6]; 

let arr = arr1.concat(arr2); 
console.log(arr); // [ 1, 2, 3, 4, 5, 6 ] 


// spread operator
let arr1 = [1,2,3]; 
let arr2 = [4,5,6]; 

let arr = [...arr1, ...arr2]; 
console.log(arr); // [ 1, 2, 3, 4, 5, 6 ]

 

 

 

반응형