프로젝트를 진행하며 카메라, 갤러리로 접근한 이미지 파일을 S3에 업로드하고 받아와 사용해야 하는 경우가 있었다.
RN에서 디바이스의 갤러리, 카메라를 사용하여 가져온 uri에는 로컬 파일 시스템의 경로가 들어있었다.
이에 웹과 앱에서 어떤식으로 파일을 처리하는지 서버에는 어떤 body를 담아 요청을 보내는지 비교해보았다.
import { ImageManipulator, SaveFormat } from "expo-image-manipulator";
import * as ImagePicker from "expo-image-picker";
우선 위 라이브러리를 사용해 카메라와 갤러리에 접근해 사진 이미지를 받아왔다.
const handleResult = useCallback(
async (result: ImagePicker.ImagePickerResult) => {
if (!result.canceled && result.assets?.length) {
const asset = result.assets[0];
const { uri, width, height } = asset;
// width/height 정보가 없으면 그대로 사용
if (!uri || !width || !height) {
setTicketBackgroundUri(uri);
return;
}
try {
// 1) 컨텍스트 생성
const context = ImageManipulator.manipulate(uri);
// 2) 타겟 비율을 유지하면서 더 크게 리사이즈 (center-crop용)
const scale = Math.max(TICKET_WIDTH / width, TICKET_HEIGHT / height);
const resizedWidth = Math.round(width * scale);
const resizedHeight = Math.round(height * scale);
context.resize({ width: resizedWidth, height: resizedHeight });
// 3) 가운데를 기준으로 280 x 462로 crop
const cropOriginX = Math.max(
0,
Math.round((resizedWidth - TICKET_WIDTH) / 2)
);
const cropOriginY = Math.max(
0,
Math.round((resizedHeight - TICKET_HEIGHT) / 2)
);
context.crop({
originX: cropOriginX,
originY: cropOriginY,
width: TICKET_WIDTH,
height: TICKET_HEIGHT,
});
// 4) 최종 렌더
const imageRef = await context.renderAsync();
const result = await imageRef.saveAsync({
compress: 1,
format: SaveFormat.JPEG,
});
setTicketBackgroundUri(result.uri);
} catch (e) {
console.error("image crop error", e);
// 문제 생기면 일단 원본 사용
setTicketBackgroundUri(uri);
}
}
},
[]
);
const closeCameraOption = useCallback(() => {
setIsOpenedCameraOption(false);
}, []);
const handleSelectFromAlbum = useCallback(async () => {
try {
const permission =
await ImagePicker.requestMediaLibraryPermissionsAsync();
if (!permission.granted) {
Alert.alert(
"권한 필요",
"앨범 접근 권한을 허용해야 사진을 선택할 수 있어요."
);
return;
}
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ["images"],
allowsEditing: true,
aspect: [TICKET_WIDTH, TICKET_HEIGHT],
quality: 1,
selectionLimit: 1,
});
await handleResult(result);
} catch (error) {
console.error("handleSelectFromAlbum", error);
Alert.alert("오류", "앨범에서 사진을 불러오는 중 문제가 발생했어요.");
} finally {
closeCameraOption();
}
}, [handleResult, closeCameraOption]);
const handleTakePhoto = useCallback(async () => {
try {
const permission = await ImagePicker.requestCameraPermissionsAsync();
if (!permission.granted) {
Alert.alert("권한 필요", "카메라 권한을 허용해야 촬영할 수 있어요.");
return;
}
const result = await ImagePicker.launchCameraAsync({
allowsEditing: true,
aspect: [TICKET_WIDTH, TICKET_HEIGHT],
quality: 1,
mediaTypes: ["images"],
cameraType: ImagePicker.CameraType.back,
});
await handleResult(result);
} catch (error) {
console.error("handleTakePhoto", error);
Alert.alert("오류", "카메라를 실행하는 중 문제가 발생했어요.");
} finally {
closeCameraOption();
}
}, [handleResult, closeCameraOption]);
위 방식으로 expo-image-manipulator를 사용하여 간단하게 이미지를 가져올 수 있었다.
이후 웹과 앱에서 파일을 처리하는 방식에 대한 차이점을 생각해보았다.
웹(브라우저)
// 웹에서는 File 객체를 직접 사용
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0]; // File 객체
// FormData에 바로 append 가능
const formData = new FormData();
formData.append('file', file);
// 또는 fetch body로 직접 사용 가능
fetch(url, {
method: 'POST',
body: file // File 객체를 직접 사용
});
React Native
// React Native에서는 파일 경로(URI 문자열)만 받음
const uri = "file:///path/to/image.jpg"; // 문자열
// 파일을 읽어와야 함
const imageResponse = await fetch(uri); // URI로 파일 읽기
const imageArrayBuffer = await imageResponse.arrayBuffer();
// 그 다음 업로드
fetch(presignedUrl, {
method: 'PUT',
body: imageArrayBuffer // 바이너리 데이터
});
브라우저에서는 File 객체를 직접 사용하는 반면 RN에서는 uri를 기반으로 파일을 읽어 바이너리 데이터로 변경하여 서버에 POST요청을 보낸다.
웹은 브라우저가 파일 시스템에 직접 접근할 수 없고, 사용자가 선택한 파일만 메모리에 로드된다. 반명 RN은 실제 파일 시스템에 접근하므로 파일 경로를 받고, 필요할 때 해당 경로의 파일을 읽는 방식이다.
'React-Native' 카테고리의 다른 글
| [RN] Expo eas IOS 빌드 & TestFlight테스트 (0) | 2025.12.22 |
|---|---|
| [RN] Expo54버전 채널톡 연동 NOT_INITIALIZED 이슈 (0) | 2025.12.18 |
| [RN] Expo eas build에서 네트워크 요청이 안되는 현상 (0) | 2025.12.10 |
| [RN] React-Native 안전하게 인증 처리하기 (0) | 2025.12.09 |
| [RN] Navigator 구조 설계 (0) | 2025.12.08 |