[Typescript/React-Query] 이미지 api 호출 트러블슈팅
프로젝트를 함께 개발하는 팀원이 api response 로 이미지가 내려오는데 에러가 난다고 하더랍니다.
제가 담당하고 있는 도메인에도 추후 동일한 환경의 api 가 제공될 예정이기에, 페어코딩으로 해결했던 내용을 재구성하였습니다.
이전까지 한번도 이미지 자체를 response 데이터로 받아본적이 없었다능...
...핑계다.. 나레기...
hooks / api / ... / foo.ts
처음 코드를 작성할 때는 api 로 부터 이미지 자체를 받는 것이 아닌, 이미지 url을 받을 것이라고 생각했습니다.
그래서 다른 일반적인 api 요청 hooks 와 동일한 컨벤션으로 코드를 작성했지요.
const FooApi = {
getImage: (params: GetFooImageParamsType) => {
return requester.get<GetFooImageResponseType>(
`${BASE_URL}/get-image`,
{ params }
);
}
};
const useGetImage = (params: GetFooImageParamsType) => {
const queryKey = ['foo-image', params];
return useQuery(queryKey, () =>
FooApi.getImage(params),
);
};
export { useGetImage };
이미지를 랜더해주는 컴포넌트에서는 위의 useGetImage
훅의 data response 에서 받아온 url을 Next.js <Image/>
컴포넌트의 url prop 으로 넘겨주기만 하면 될 것이라 생각했습니다.
하지만 보안상 이미지가 저장된 url 이 아닌, api 요청시 실시간으로 복호화한 이미지 자체를 받아와야하는 것으로 요건이 정리되면서,
바이너리 데이터로 가져온 이미지를 보여주어야 했습니다. 그래서 추가적인 코드를 <Image/>
에 넣어주었습니다.
<Image src={`data:image/png;base64,${data.response.image}`} />
위와 같이 src에 base64 형태로 이미지를 표현하겠다고 작성하면 이미지가 보여지는 것입니다.
물론 api 문제가 없고 다른 코드들을 잘 작성했다는 가정하에...!
에러발생?
결과적으로 위 코드는 제대로 동작하지 않았습니다. api 콜 시 확인한 바이너리 데이터가 전부 깨져있었거든요.(복선...)
아 정말 백엔드 뭐하는거야~ 복호화 좀 잘 해주지~~
스웨거에서도 안되면 한마디 해야겠네~^^
백엔드 개발자 대학동기라 센척해봤습니다..ㅎ
그러나 스웨거에서는 너무나도 선명히 잘 보이는 이미지..
역시 못하는건 나여따..
Blob 형태로 변환
스웨거에서는 잘보인다니..
크롬 개발자 도구로 스웨거 이미지를 보니 Blob 형태로 변환되어 이미지가 보여졌습니다.
Blob 은 Binary Large Object 로 큰용량의 이진데이터를 저장가능한 객체라고 볼 수 있습니다.
구글링으로 Blob 형태로 이미지를 변환하는 방법을 우선 찾았습니다.
const FooApi = {
getImage: (params: GetFooImageParamsType) => {
return requester.get<GetFooImageResponseType>(
`${BASE_URL}/get-image`,
{
params,
responseType: 'arraybuffer',
},
);
}
};
const useGetImage = (params: GetFooImageParamsType) => {
const queryKey = ['foo-image', params];
return useQuery(queryKey, () =>
FooApi.getImage(params),
).then((response) => {
const blob = new Blob([response.data], { type: 'image/png' });
const image = URL.createObjectURL(blob);
return image;
})
};
export { useGetImage };
처음 코드와 달라진 점은 두군데입니다.
첫번째는 axios get 요청시 responseType: 'arraybuffer'
를 추가하는 것입니다.
Arraybuffer 란 자바스크립트에서 구현된 버퍼인데, 메모리의 일정공간을 할당하여 바이너리 데이터를 저장합니다.
두번째로 이렇게 가져온 바이너리 데이터를 Blob 형태로 전환하는 것입니다.
useQuery()
로 가져온 데이터를 Blob 형태로 반환 할 수 있도록 코드를 작성했습니다.
결과 값을 확인해보니 성공적으로 잘 전환이 되었습니다만...
여전히 에러발생...! ㄷㄷ
문제는 Blob 변환이 아니었다?
왜 안되는 걸까요?
문제를 해결하는 과정에서 간과했던 부분이 있었습니다. 근본적인 문제해결의 열쇠를 쥐고 있는 것이었죠.
맨 처음 보았던 문제는 바이너리 데이터가 깨졌다는 사실입니다. 처음에는 이 부분을 중요하게 생각하지 않았습니다.
Blob 형태로 변환하든, 하지않든 이미지 그 자체인 바이너리가 깨졌다면 변환 후 결과도 동일할 것입니다.
그렇게 고민한 끝에 한가지 놓친 부분을 찾았습니다.
문제 해결
바이너리 데이터가 깨졌다는 것은 온전한 데이터가 넘어오지 못했을 수 있다는 것입니다.
위에서 작성한 코드는 getImage()
함수를 성공적으로 호출한 후 then()
으로 넘어가 response.data
를 blob으로 변환시키고 있습니다. 하지만 함수 호출에 성공한 후 다음 프로세스를 진행하는 것이 아닌, response 로 받는 바이너리 데이터의 로드가 끝난 후 다음 프로세스를 진행하는 것이 맞겠지요.
아래와 같이 async / await 를 사용하여 코드를 정리하니 더 명확하게 이전 문제가 무엇이었는지 보입니다.
const useGetImage = (params: GetFooImageParamsType) => {
const queryKey = ['foo-image', params];
return useQuery(queryKey, async () => {
const response = await FooApi.getImage(
params,
);
const blob = new Blob([response.data], { type: 'image/png' });
const image = URL.createObjectURL(blob);
return image;
});
};
결과는 성공적 ^^...
이런 기본적인 것을 간과했다니..
나 2년차 맞냐 진짜
마무리하며
기본에 충실하자..
'개발 이야기 > React & Next' 카테고리의 다른 글
[Next.js] 시작부터 원치않는 회귀(redirect)를 해버림(feat. 캐싱) (1) | 2024.06.02 |
---|---|
[Next.js/JWT] Next.js 13 환경에서 권한/인증로직 구현하기 (0) | 2023.12.17 |
[React/디자인 시스템] 알잘딱깔센 표 UI 인터페이스 구현하기 (1) | 2023.04.17 |
[React/Next.js] 특수한 조건의 라우팅 컴포넌트 제작(삽질)기 (8) | 2023.02.18 |
[React/Typescript] Modal/Dialog, 모달 창 만들기 (0) | 2021.11.28 |