reat-native 환경에서 이미지 업로드 적용하는 방법,
next..js에선 input의 type ='file'을 사용해서 이미지 업로드를 구현할 수 있지만 react-native에선 없기 때문에 라이브러리를 사용.
1. expo-image-picker
2. react-native-picker
이 두개가 대표적인듯.
나는 expo를 사용중이기 때문에 expo-image-picker 적용,
last publish도 2일전이니 꾸준하게 디벨롭중인 것 같다.
- expo-image-picker 장점
1. 간편한 설치 및 사용
- expo에서 바로 사용 가능하며 네이티브 코드를 건드릴 필요가 없다.
2. 안전성
- expo 환경에 테스트 가능, 다양한 기기에서 일관되게 작용
3. 이미지 및 비디오 선택 가능
- 사용자가 이미지를 선택하거나 , 카메라를 사용할 수 있는 기능을 제
4. 간편한 권한 관리
- 카메라 및 미디어 라이브러리 권한을 쉽게 요청
설치
npx expo install expo-image-picker
찾아보니 npm 뿐만 아니라 expo 사이트에서도 예제가 잘 나온다. https://docs.expo.dev/versions/latest/sdk/imagepicker/
- 예제 코드 분석 -
const pickImage = async () => {
// No permissions request is necessary for launching the image library
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
console.log(result);
if (!result.canceled) {
setImage(result.assets[0].uri);
}
};
pickImage 함수를 비동기 (async)로 해서 expo-image-picker 라이브러리를 사용해 이미지 또는 비디오를 선택하고 선택된 이미지의 url을 상태에 저장하는 코드,
ImagePicker.launchImageLibraryAsync()
- 사용자가 이미지/ 비디오 라이브러리에 파일을 선택할 수 있게 해준다.
launchImageLibraryAsync의 옵션 객체들
- mediaTypes : 미디어 타입을 선택하는 옵션, all, videos, image가 있다.
- allowsEditing : 사용자가 이미지를 선택한 후에 편집을 할 수 있는 옵션, boolean값,
- aspect : 이미지 편집시 적용되는 가로 세로 비율, 숫자 배열이 들어간다. 예제는 aspect[4,3] 이므로 가로 세로 4:3 비율이 된다. allowsEditing=true 일때만 적용된다.
- quality : 품질을 결정하는 옵션으로 1이 최고품질을의미한다.
if (!result.canceled) {
setImage(result.assets[0].uri);
}
- 사용자가 결과를 취소하지 않았을 경우 setImage에 선택된 url을 저장.
result. assets의 옵션들
export type ImagePickerAsset = {
uri: string;
assetId?: string | null;
width: number;
height: number;
type?: 'image' | 'video';
fileName?: string | null;
fileSize?: number;
exif?: Record<string, any> | null;
base64?: string | null;
duration?: number | null;
mimeType?: string;
};
native에서는 파일을 주로 두가지 방법으로 처리한다.
1. url 방식
2. base64 방식
- URI 기반 파일 처리:
- React Native에서 파일을 다룰 때는 주로 파일의 경로(URI)를 이용. 예를 들어, 카메라나 미디어 라이브러리에서 선택한 이미지 파일은 경로(URI)로 반환된다.
- 이 URI를 그대로 사용해 서버에 업로드할 수는 없으며, 네이티브 코드나 라이브러리를 사용해 파일을 읽고, FormData를 통해 전송해야 한다.
- Base64 인코딩:
- 일부 API나 서버는 파일을 Base64로 인코딩한 후 전송하는 방식을 요구한다.
- expo-file-system과 같은 라이브러리를 사용하여 파일을 Base64로 인코딩한 후 이를 서버로 전송할 수 있다.
언제 URL을 사용하고 언제 Base64를 사용할까?
-URL
- 파일이 서버에 저장되어 있고, 클라이언트가 그 파일에 접근해야 할 때 사용.
- 이미지, 비디오, PDF 등 대용량 파일을 전송할 때 URL을 사용하는 것이 좋다.
- 이는 서버가 해당 파일을 호스팅하고 클라이언트는 해당 파일을 URL을 통해 다운로드하는 방식
- Base64
- 작은 파일이나 임베디드 데이터(이미지, 아이콘 등)를 전송하거나 저장할 때 유용
- 예를 들어, HTML 페이지 안에 이미지를 직접 넣어야 하거나, 네트워크 연결 없이 데이터를 전달해야 할 때 사용
- 그러나 대용량 파일에 대해서는 Base64가 파일 크기를 늘리기 때문에 적합하지 않다.
나는 s3에서 url을 받는 방식으로 이미지를 업로드 하기 때문에 url방식으로 선택했다.
이 경우ㄴ 서버에서 이미지를 저장하고, 해당 이미지의 URL만 클라이언트에 전달하는 방식
const { mutate: imageUpload, isPending } = useMutation({
mutationKey: ["profileImageUploadKey"],
mutationFn: async (image: FormData) => {
const res = await instance.post(`/api`, image);
return res.data;
},
onSuccess: (data) => {
setFileUrl(data.imageUrl);
},
onError: (error) => {
console.error("파일 업로드 실패:", error);
},
});
useMutation을 사용해 이미지를 서버에 업로드후 url을 받아오는 비동기 요청을 구현한 코드이다.
image를 인자로 받아 request body값에 넣어준다.
그 후 업로드가 성공하면 s3에서 리턴하는 url을 setFileUrl에 담아준다.
const pickImage = async () => {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== "granted") {
alert("카메라 롤 접근 권한이 필요합니다.");
return;
}
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images, // 이미지 파일만 선택
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
};
사진첩 접근 권한 설정과, 권한이 있으면 이미지에 대한 옵션 설정.
if (!result.canceled && result.assets.length > 0) {
const selectedImageUri = result.assets[0].uri;
const fileExtension = selectedImageUri.split(".").pop()?.toLowerCase();
// MIME 타입 설정
const mimeType = fileExtension === "png" ? "image/png" : "image/jpeg";
const uniqueFileName = `profile-${Date.now()}.${fileExtension}`;
// FormData에 이미지 추가
const formData = new FormData();
formData.append("image", {
uri: selectedImageUri,
name: `profile.${fileExtension}`,
type: mimeType,
} as any); // TS 오류를 피하기 위한 캐스팅
// 이미지 업로드 요청
imageUpload(formData);
}
- selectedImageUri에 사진첩에서 선택한 이미지의 uri를 담아준다.
- fileExtension 함수는 파일 uri를 확인하고 . 을 기준으로 파일의 확장자를 추출한다. => 파일 업로드 확장자 제한하기 위한 것.
예를 들어 selectedFileUri 가 "file:///path/to/image.jpg" 이런 형식으로 나왔다면 split메서드를 통해
["file:///path/to/image", "jpg"]로 반환, 그 후 .pop으로 배열의 마지막 인덱스를 반환해주면 확장자를 소문자로 추출,
그럼 .jpg만 남는다.
- 서버에 폼데이터를 전송하기 위해 new formData 객체 생성,
- 이미지를 업로드할 때는 파일의 URI, 이름, 그리고 MIME 타입을 지정
=> uri에는 사진첩에서 선택한 이미지의 uri를 ,
=> name은 업로드 시간과 확장자를 기준으로 uniqueFileName 변수를 설정해서 정해준다.
=> type은 mineType으로 확장자를 추출했을때 png면 image/png 아니면 image/jpeg로 넣어준다.
- 앱에서도 서버로 통신할때 HTTP프로토콜을 사용하기 때문에 mineType 을 지정해줘야한다.
- mineType 은 서버와 클라이언트가 파일의 **포맷(형식)**을 인식할 수 있도록 정의한 문자열
- 예를 들어, image/jpeg, image/png, text/plain 등
- 마지막엔 useMutation 함수인 imageUpload 에 formData객체를 인자로 넣어서 서버 업로드 요청,
'프로젝트 답지' 카테고리의 다른 글
[Dap JI ] expo 스텍관리문제 (1) | 2024.11.21 |
---|---|
[Dap JI ] 스토어 승인 요소 (3) | 2024.11.20 |
[Dap JI ] React-native-webView 적용해보기 ( 진행중) (0) | 2024.09.04 |
[Dap JI - issue] 이미지 업로드 (0) | 2024.08.26 |
[Dap JI - issue] 동영상 삭제에 관하여 (0) | 2024.08.23 |