[React+Spring] 파일 처리 API 연동

HJ·2024년 1월 30일
0

React+Spring

목록 보기
5/11

아래 내용은 코드로 배우는 React with 스프링부트 API서버 강의와 함께 설명이 부족한 부분에 대해서 조사하여 추가한 내용입니다.


파일 전송


[ AddComponent ]

function AddComponent() {
    const uploadRef = useRef();

    const handleClickAdd = (event) => {
        const formData = new FormData();
        const files = uploadRef.current.files;

        for (let i = 0; i < files.length; i++) {
            formData.append("files", files[i]);
        }
        // formData 의 key 가 DTO 에 있는 변수명과 동일해야함
        formData.append("itemName", item.itemName);
        formData.append("itemDesc", item.itemDesc);
        formData.append("price", item.price);
    }

    return (
        ...
        <input
            className="w-3/5 p-6 rounded-r border border-solid border-neutral-300 shadow-md"
            ref={uploadRef}
            type={'file'} 
            multiple={true}>
        </input>
        ...
    );
}

useRef 는 함수 컴포넌트에서 ref를 생성하고 조작하기 위해 사용되며 이를 통해 DOM 요소에 직접 접근할 수 있습니다. useRef 를 사용하여 생성된 객체는 .current 프로퍼티를 통해 현재 값에 접근합니다. 위의 예시에서는 useRef 를 사용하여 uploadRef를 생성하고, 이를 JSX의 ref 속성에 전달하여 DOM 요소에 접근합니다.

FormData 는 HTML의 form 엘리먼트를 프로그래밍 방식으로 제어하기 위한 객체입니다. 주로 파일 업로드와 같은 경우에 사용되며, FormData 객체를 사용하면 간단하게 폼 데이터를 구성하고 서버로 전송할 수 있습니다.



[ itemApi ]

export const addItem = async(itemObj) => {
    const header = {
        headers: {
            "Content-Type" : "multipart/form-data"
        }
    }
    const res = await axios.post(`${prefix}/add`, itemObj, header);
    return res.data;
}

위의 예시는 FormData 를 전달받아서 서버로 전달하는 역할을 수행합니다. FormData 이기 때문에 header 를 지정해주어야 하며, 이를 axios.post 에 전달할 데이터와 함께 전달합니다.




이미지 출력


[ 썸네일 출력 ]

<div className="w-full overflow-hidden ">
    <img alt="item" className="m-auto rounded-md w-60"
        src={`${API_SERVER_HOST}/api/items/view/th_${item.uploadedFileNames[0]}`} />
</div>

서버에서 받아온 목록 리스트를 반복하면서 각 item 에 저장된 uploadedFileNames 의 첫 번째 항목을 img 태그를 활용해 화면에 출력합니다. 이때 이미지가 없다면 서버의 FileUtils 에 의해 디폴트 이미지가 출력되게 됩니다.


[ 상품 이미지 전체 출력 ]

<div className="w-full justify-center flex flex-col m-auto items-center">
    {item.uploadedFileNames.map((imgFile, i) =>
        <img alt="item" key={i} className="p-4 w-1/2" src={`${API_SERVER_HOST}/api/items/view/${imgFile}`} />
    )}
</div>

상품 상세 조회 화면에서 이미지를 불러오는 코드입니다. map 을 통해 생성하기 때문에 key 값이 필요하며, 이 코드에서는 업로드 된 파일명을 반복하면서 호출하기 때문에 이미지가 없으면 디폴트 이미지가 출력되지 않고, 아무 이미지도 보이지 않게 됩니다.




파일 수정


[ 기존 이미지 삭제 ]

const deleteImage = (imageName) => {
    const remainImgs = item.uploadedFileNames.filter(imgName => imgName != imageName);
    item.uploadedFileNames = remainImgs;
    setItem({...item});
}
...
{item.uploadedFileNames.map((imgFile, i) => (
    <div className="flex justify-center flex-col w-1/3 m-1 align-baseline" key={i}>
        <img alt="img" src={`${API_SERVER_HOST}/api/items/view/th_${imgFile}`} />
        <button className="bg-red-500 text-3xl text-white p-0.5 rounded-full mb-2"
            onClick={() => deleteImage(imgFile)}>DELETE</button>
    </div>
))}

기존 이미지를 삭제하는 경우, delete 버튼을 누르면 현재 보여지고 있는 itemuploadedFileNames 에서 해당 사진을 제외시키면 되기 때문에 filter 를 이용하여 새로운 배열을 만들고 그 배열을 item 의 상태로 변경합니다.



[ 서버로 파일 전송 ]

const handleClickSave = () => {
    const formData = new FormData();
    const files = uploadRef.current.files;
    // 신규 파일 추가
    for (let i = 0; i < files.length; i++) {
        formData.append("files", files[i]);
    }

    formData.append("itemName", item.itemName);
    formData.append("itemDesc", item.itemDesc);
    formData.append("price", item.price);
    formData.append("delFlag", item.delFlag);

    // 업로드 된 파일 유지( 삭제한 파일은 상태 업데이트로 인해 배열에서 삭제되었음 )
    for(let i = 0; i < item.uploadedFileNames.length; i++) {
        formData.append("uploadedFileNames", item.uploadedFileNames[i]);
    }
    ...
}

수정의 경우, 상품을 등록하는 것과 동일하지만 한 가지 차이점이 존재하는데 바로 기존에 존재하는 파일을 유지해서 보내주는 것입니다.

item 객체가 가진 uploadedFileNames 배열에 있는 항목들은 삭제되지 않은 항목들 이기 때문에 이를 이용하여 uploadedFileNames 라는 이름으로 서버에 전달해야 기존 사진이 삭제되지 않고 유지됩니다.

0개의 댓글