Front/React

React 이미지 처리

NYoun 2024. 4. 14. 00:35

이전 포스팅으로 React에서 이미지 미리보기와 파일 저장 요청등의 기능을 정리했다.

이번에는 이미지 출력에 대해 어떻게 처리하는지에 대한 정리.

 

 

FileReader로 처리

FileReader는 blob 또는 file과 같은 객체로부터 데이터를 읽어들이기 위한 목적으로 사용되는 객체다.

보통 img 태그의 src 속성에 리소스를 다루기 위해서는 readAsDataURL을 통해 처리한다.

 

가장 먼저 사용자가 파일 선택한 뒤 미리보기 출력을 위한 처리 코드다.

//files는 하나의 이미지 파일이라고 가정.
const { files } = props;
const [imgSrc, setImgSrc] = useState('');

useEffect(() => {
    const reader = new FileReader();
    reader.onload = image => {
        setImgSrc(String(image.target.result));
    }
    
    reader.readAsDataURL(files);
});

 

FileReader 객체 생성 후 onload가 작성되어있지만 readAsDataURL을 통해 파일을 먼저 읽어 변환한다.

readAsDataURL은 base 64 형태의 data URL로 데이터를 읽고 변환하게 되므로 base64 형태의 data URL로 files를 읽어 변환한다.

데이터를 모두 읽고 나면 onload 이벤트가 발생하게 되고 .result를 통해 성공 시 읽어 들인 결과를 문자열 타입으로 imgSrc state에 set을 하게 된다.

 

다음은 서버로부터 파일을 요청해 전달 받은 뒤 FileReader로 처리하는 코드다.

const { imageName } = props;
const [imgSrc, setImgSrc] = useState('');

useEffect(() => {
    displayImage(imageName);
}, [imageName]);

const displayName = async (imageName) => {
    await axios.get(`display/${imageName}`
        , {
            headers: {'Content-Type': 'application/json'}
            , responseType: 'blob'
        })
        .then(res => {
            const file = new File([res.data], 'imageName')
            const reader = new FileReader()
            reader.onload = image => {
                setImgSrc(String(image.target.result)
            }
            reader.readAsDataURL(file);
        })
        .catch(err => {
            console.error('display axios error : ', err);
        });
}

 

서버에 파일 요청을 한뒤 응답으로 받기 위해서는 axios의 responseType이 blob이어야 한다.

res.data인 응답받은 파일을 new File로 파일 객체를 만들어준다.

readAsDataURL로 해당 파일 객체를 읽은 뒤 변환하도록 하고 onload가 발생했을 때 읽어들인 결과를 imgSrc state에 set 하도록 처리한다.

 

이 두 방법의 차이는 file 객체를 생성하는지 안하는지 정도의 차이다.

사실 둘다 file 객체를 readAsDataURL에 넘겨주는 것이긴 한데, 선택한 파일 미리보기의 경우 파일을 전달받았기 때문에 굳이 file 객체를 새로 만들어줄 필요가 없는것이고, 서버에 요청해 받는 경우는 응답받은 데이터를 파일 객체로 만들어주는 것에서 차이가 발생할 뿐이다.

 

FileReader를 통해 처리하는 이 방법은 데이터를 base64 형태로 읽는 만큼 src에 적용할때마다 만들어진 객체를 공유하지 않고 새로 문자열을 파싱해 이미지로 만드는 작업을 하기 때문에 속도가 느리다.

또한, 이렇게 처리한 뒤에 개발자 도구에서 src 속성의 값을 보면 어마어마하게 긴 문자열을 볼 수 있다.

 

 

createObjectURL로 처리

가장 불편했던 건 너무 긴 문자열이 출력된다는 것이었다.

그래서 다른 방법을 여러 방면으로 찾아봤는데 그 방법이 createObjectURL을 사용하는 방법이었다.

createObjectURL을 사용하면 blob 객체의 url 주소값으로 이미지를 불러올 수 있다.

이렇게 생성된 주소값은 blob 객체를 바라보고 메모리에 올라가있기 때문에 객체를 새로 만들지 않고 바로 가져다 쓰므로 FileReader를 사용할 때 보다 속도도 빠르다.

src에 생성되는 주소는 실제 서버에는 존재하지 않고 해당 브라우저에서만 사용 가능한 URL이다.

이때 유효한 범위는 '해당 문서'다.

새로고침하거나 다른 페이지에서 사용하려고 한다면 제대로 사용할 수 없다.

그리고 주소 역시 blob:http://localhost:3000/~~~~ 형태로 생성되고 간결해진 것을 확인할 수 있다.

//새로운 파일 선택.
//동일하게 files는 하나의 파일이라고 가정.
const { files } = props;
const [imgSrc, setImgSrc] = useState('');

useEffect(() => {
    const url = window
                .URL
                .createObjectURL(
                    files
                );
    setImgSrc(url);
});


//서버에 파일을 요청해 blob으로 반환 받은 뒤 처리.
const { imageName } = props;
const [imgSrc, setImgSrc] = useState('');

useEffect(() => {
    displayImage(imageName);
}, [imageName]);

const displayName = async (imageName) => {
    await axios.get(`display/${imageName}`
        , {
            headers: {'Content-Type': 'application/json'}
            , responseType: 'blob'
        })
        .then(res => {
            const url = window
                        .URL
                        .createObjectURL(
                            new Blob([res.data], {type: res.headers['content-type']})
                        );
            setImgSrc(url);
        })
        .catch(err => {
            console.error('display axios error : ', err);
        });
}

 

이 방법의 단점으로는 메모리 이슈가 존재한다.

blob 객체가 URL로 변환되어 매핑이 이루어진 채 메모리에 저장되면 명시적으로 해당 URL이 해제되기 전까지 브라우저는 URL이 유효하다고 판단하기 때문에 JS 엔진에서 가비지 컬렉션이 이루어지지 않는다.

따라서 Blob URL을 사용한 이후 더이상 사용하지 않을 시점이라고 판단되는 경우에는 명시적으로 해제해주는 것이 좋다.

이때 명시적으로 해제하는 코드는 window.URL.revokeObjectURL(createObjectURL) 이다.

 

이미지를 화면에 출력하지 않고 다운로드로 blob을 사용하고자 한다면 다운로드 클릭시에만 필요하기 때문에 처리 후 바로 해제하는 방법으로 메모리 누수를 방지할 수 있다.

 

 

참고 블로그

 

 

https://avengersrhydon1121.tistory.com/280

 

[vue.js] URL.createObjectURL 을 이용한 이미지 처리

우선 설명하기전에 Blob에 대해서 설명하고 넘어가려 한다. Blob: Binary Large Object의 줄임말로 써 이미지, 사운드 파일 같이 하나의 큰 파일을 의미하며, 이런 파일들은 특별한 방법으로 다루어야하

avengersrhydon1121.tistory.com

 

https://growth-msleeffice.tistory.com/74

 

Binary / Base64 / Blob / ArrayBuffer / File

1. Binary Binary란 이진 데이터를 의미하며 1과 0만을 사용하여 2개의 수를 나타내는 진법을 뜻하는, 컴퓨터를 다루는데 있어 가장 근본이 되는 체계라고 한다. 2. Base64 컴퓨터는 모든 데이터를 0과 1

growth-msleeffice.tistory.com