🖼️ 문제 상황
React와 Next.js, 그리고 Typescript을 기반으로 한 프로젝트입니다.
마이 페이지의 프로필 편집 기능에서 서버에 patch 요청을 보낼 때, body에 소개글을 제외한 프로필 이미지와 닉네임 값이 필수로 들어가야 했습니다.
그리고 api는 이미지 파일을 미리 서버에 업로드하여 url을 응답으로 받고, 해당 url을 닉네임 및 소개글과 함께 담아 최종적인 프로필 편집 patch 요청을 보내는 방식이었습니다.
사용자가 프로필 사진을 따로 지정하지 않은 경우도 있기 때문에 이미지 파일이 선택되지 않았다면 로컬에서 임포트 해 온 'defaultProfileImage'를 담아 요청을 보낼 계획이었습니다.
그러나 생각했던 것보다 다양한 이미지의 타입으로 인해 타입 오류가 발생하였습니다.
- <input type="file">로 선택한 이미지 타입 = File
- 로컬에서 임포트 해 온 'defaultProfileImage' 이미지 타입 = StaticImageData
- Next.js는 최적화를 위해 이미지 임포트 시 StaticImageData라는 타입으로 이미지를 처리함
- StaticImageData 타입의 구조는 다음과 같음
interface StaticImageData {
src: string;
height: number;
width: number;
blurDataURL?: string;
}
- 'URL.createObjectURL()'을 이용하여 File 타입의 이미지를 변환한 이미지 타입 = 꼭 File로 인식되어야 함(string 불가)
- 미리보기 사진을 띄우기 위해 'URL.createObjectURL()'로 File 타입 이미지의 url을 생성함
- 반환된 url은 'Blob: ~url~' 형태이므로 string이 아닌 File로 인식되어야 함
- 서버에 이미지 파일을 업로드하여 응답으로 받은 url = string
- 빈 값일 때 이미지 변수를 초기화할 값 = null
🖼️ 해결 방법
프로필 컴포넌트에서 useQuery로 기존 프로필 내용을 불러와 프로필 편집 모달에 프롭스로 전달해주는 방식이고,
편집이 끝난 뒤 저장하기 버튼을 눌렀을 때 동작할 onClick(or onSubmit) 함수 등 여러 곳에서 프로필 이미지 값을 사용하는 상황이었습니다.
그로 인해 A 파일에서 정의한 이미지 타입을 수정하면 B 파일의 타입이 오류가 나는 상황이 반복되었습니다.
따라서 파일들의 관계와 요구되는 이미지 타입을 파악하기 위해
노트에다가 프로필 이미지가 사용되는 파일의 관계를 그려보고, 각 파일별로 요구하는 프로필 이미지의 타입을 정리해보았습니다.
대부분의 프로필 이미지 타입은 File과 string, 그리고 null로 정의한 인터페이스를 재사용하며 타입을 지정할 수 있었기에 아래와 같이 정의하여 사용하고,
type ProfileContentsTypes = {
image: string | File | null
nickname: string
description: string
}
미리보기나 기본 이미지처럼 별도의 처리가 필요한 경우에는 이미지가 쓰이는 곳에서 직접 예외처리를 해주었습니다.
// ex1)
const profileImage = (typeof image === 'string') ? image) : (
image ? URL.createObjectURL(image) : defaultProfileImage);
// ex2)
if (previewImage && typeof previewImage === 'string') {
URL.revokeObjectURL(previewImage)
}
<Image
src={typeof previewImage === 'string' ? previewImage : ''}
alt="업로드 한 이미지 미리보기"
width={140}
height={140}
/>
현재는 닉네임과 함께 프로필 이미지 또한 필수값으로 지정해두어, 사용자가 프로필 이미지를 선택하지 않아 빈 값인 경우 저장하기 버튼이 비활성화 되도록 구현하였습니다.
그러나 사용자 경험을 좀 더 향상시키기 위해 프로필 이미지가 빈 값이어도 오류 없이 요청이 가도록 하는 방법을 찾아 개선할 수 있을 것 같습니다.
읽어주셔서 감사합니다:)