본문 바로가기
모바일 가이드

Expo Image Picker: 2026년 완벽 가이드

React Native 앱에서 Expo Image Picker를 완벽하게 마스터하세요. 설치, 권한, 카메라/갤러리 접근, 크롭, base64, 업로드까지 이 가이드에서 모두 다룹니다.

Expo Image Picker: 2026년 완벽 가이드

UI는 다 준비됐고, 프로필 화면에 “Upload photo” 버튼도 붙였는데, 이제 쉬워 보이던 부분이 갑자기 쉽지 않아집니다. 실제 이미지 선택 흐름은 네이티브 권한, OS가 제어하는 인터페이스, 많은 개발자가 예상하는 것과 다른 반환 형태, 그리고 실제 빌드를 배포한 뒤에야 드러나는 자잘한 빌드 타임 디테일까지 건드리기 때문입니다.

바로 이 지점에서 Expo Image Picker가 필요합니다. 기기 라이브러리에서 이미지나 비디오를 선택하거나 카메라로 사진을 찍을 수 있도록 시스템 UI를 여는 공식 Expo 라이브러리입니다. 자세한 내용은 Expo 패키지 저장소에서 확인할 수 있습니다. 실제로 이는 네이티브 미디어 입력으로 가는 안정적인 브리지를 제공하지만, 모든 기기에서 똑같이 동작하는 커스텀 미디어 경험을 제공하는 것은 아닙니다.

이 가이드는 데모가 아닌 첫 실제 구현을 위해 작성되었습니다. 프로덕션에서 중요한 결정들에 초점을 맞춥니다. managed와 bare 워크플로우 설정, 나중에 예상치 못한 문제를 일으키지 않는 권한 처리, 안전한 결과 파싱, 그리고 사용자가 파일을 고른 뒤의 실용적인 upload 패턴까지. 커스텀 네이티브 환경에서 작업 중이라면, 이 방식이 다른 접근과 어떻게 다른지 이해해 두면 도움이 됩니다. 참고로 Capgo가 대안 가이드에서 비교한 Expo EAS update 옵션도 살펴보세요..

목차

Expo Image Picker 시작하기

프로덕트 매니저가 프로필 사진 기능을 요청합니다. 일주일 뒤, 같은 기능에 영수증 upload, 사고 보고용 카메라 캡처, 그리고 사용자가 권한을 거부했을 때의 재시도 로직까지 추가되어야 합니다. 이미지 입력은 네이티브 권한, OS UI, 임시 파일 처리, 백엔드 upload 흐름과 맞물려 있어 요구사항이 빠르게 확장됩니다.

expo-image-picker 이 작업을 위해 Expo SDK 모듈을 사용합니다. 이 모듈은 플랫폼별 picker나 카메라 UI를 열고, React Native code에서 처리할 수 있는 형태로 선택된 미디어를 반환합니다. JavaScript API는 작습니다. 핵심 과제는 managed 및 bare 프로젝트 모두에서 네이티브 설정, 권한 흐름, 결과 처리를 올바르게 구현하는 것입니다.

주요 트레이드오프는 명확합니다. 커스텀 picker를 직접 만드는 대신 iOS와 Android의 기본 미디어 UI를 사용하는 것입니다. 이는 보통 더 나은 결과를 가져옵니다. 사용자는 이미 시스템 화면에 익숙하고, 권한 prompt는 OS 방식대로 동작하며, 개발 팀은 JavaScript로 갤러리 구현체를 유지보수할 필요가 없기 때문입니다.

React 인터페이스가 있는 네이티브 통합 기능으로 생각하세요.

이런 관점이 도움이 되는 이유는 피커를 호출하는 버튼 자체에서 오류가 나는 경우는 드물기 때문입니다. 보통 다음 세 가지 중 하나에서 문제가 발생합니다:

  • 네이티브 설정: 플러그인 설정 누락, 잘못된 권한 문자열, 또는 설정 변경 후 오래된 빌드
  • 런타임 동작: 사용자가 접근을 거부하거나, iOS에서 제한된 라이브러리 접근을 허용하거나, 선택 없이 플로우를 취소하는 경우
  • 결과 파싱: 현재 API는 assets 배열을 반환하므로, 직접 읽는 이전 예제들은 result.uri 실패합니다.

워크플로우 선택은 설정 경로도 바꿉니다. Managed Expo 앱에서는 대부분의 네이티브 작업이 app config에서 처리되며, 해당 config가 변경되면 재빌드가 필요합니다. Bare 앱에서도 Expo 모듈 API를 사용할 수 있지만, 기본 iOS 및 Android 프로젝트 설정을 더 직접적으로 확인해야 합니다. 팀에서 Expo Go 대신 커스텀 클라이언트를 사용한다면, 이 가이드는 Capgo의 Expo EAS 대안 Expo Go 이상의 네이티브 모듈 테스트가 필요한 팀을 위해.

이 차이는 가이드의 나머지 부분에서 매우 중요합니다. 단순히 잘 작동하는 것(happy path)만으로는 부족하기 때문입니다. 진정한 picker 구현이란 두 워크플로우 모두에서 잘 작동하고, 사용자에게 혼란을 주지 않으면서 플랫폼별 권한 문제를 처리하며, 로컬 미리보기에 그치지 않고 업로드 레이어에 사용 가능한 파일을 전달할 수 있어야 합니다.

설치 및 필수 설정

설치는 명령어 하나로 끝납니다. 하지만 네이티브 설정을 올바르게 구성하는 것이 실제 기기, 커스텀 dev client, 그리고 프로덕션 빌드에서 picker가 제대로 작동할지를 결정합니다.

expo-image-picker 사진, 비디오, 카메라 캡처를 위한 플랫폼 picker를 React 관점의 API로 제공합니다. JavaScript 호출은 간단하지만, 설정은 그렇지 않습니다. 사진 및 카메라 접근 권한은 React Native가 아닌 iOS와 Android에서 제어하기 때문입니다.

노트북 화면에 code를 입력하며 Expo image picker 구현 작업을 하고 있는 개발자.

Expo의 버전 인식 설치 프로그램을 사용하세요:

npx expo install expo-image-picker

사용하세요 expo install 대신 npm install 또는 yarn add. Expo matches the package version to your SDK, which avoids a common class of native compatibility problems. If you are comparing how Expo modules fit into your release process, this Expo 툴링 개요 유용한 참고 자료입니다.

Managed workflow 설정

Managed workflow에서는 Expo가 빌드 시점에 네이티브 변경 사항을 적용할 수 있도록 app config에 플러그인을 선언해야 합니다.

예시 코드 app.json:

{
  "expo": {
    "plugins": ["expo-image-picker"]
  }
}

이것이 최소한의 설정입니다. 실제로는 권한 요청 문구도 함께 추가하는 것이 일반적입니다. 특히 iOS에서는 시스템 프롬프트에 앱이 왜 권한을 필요로 하는지 설명해야 합니다. 문구는 사용자 액션에 맞춰 구체적으로 작성하세요. “미디어 접근 권한이 필요합니다”보다는 “프로필 사진을 업로드합니다”가 더 좋습니다.

운영 시 많은 시간을 낭비하게 만드는 디테일이 하나 있습니다. 바로 plugins권한 문자열이나 기타 네이티브 설정을 변경할 때는 반드시 다시 빌드해야 한다는 점입니다. JavaScript를 리로드하는 것만으로는 해당 변경 사항이 적용되지 않습니다. Expo Go를 사용할 경우 이미 클라이언트에 포함된 기능으로 제한됩니다. Development build나 production build에서는 새로운 빌드를 진행해야만 네이티브 프로젝트에 설정이 반영됩니다.

Bare React Native 설정 상세

Bare 앱에서도 API 패키지는 동일하지만, 네이티브 프로젝트를 직접 더 많이 확인해야 합니다. 가장 먼저 확인할 것은 iOS usage descriptions입니다. 앱에서 라이브러리를 열거나, 카메라를 실행하거나, 오디오와 함께 비디오를 녹화해야 한다면, 빌드하기 전에 API에 해당하는 권한 문자열이 포함되어 있는지 확인해야 합니다. Info.plist 빌드하기 전에 확인하세요.

Bare 프로젝트를 위한 실무 체크리스트는 다음과 같습니다:

  1. 설치 expo-image-picker 포함 npx expo install expo-image-picker.
  2. 프로젝트에서 Expo config plugins를 사용 중이라면 플러그인 설정을 추가하세요.
  3. iOS 사용 권한 설명(usage descriptions)이 제공하는 기능과 일치하는지 확인하세요.
  4. 네이티브 설정을 변경한 후에는 iOS 및 Android 앱을 다시 빌드해야 합니다.

권한 텍스트가 누락되면 UI code는 정상이고 버튼 핸들러도 실행되기 때문에 런타임 버그처럼 보일 때가 많습니다. 실제 오류는 스택 하단에서 발생합니다. 저는 보통 Info.plist컴포넌트 code를 건드리기 전에 앱 설정과 현재 빌드에 최신 네이티브 변경 사항이 포함되었는지 확인합니다.

다음 습관을 들이면 설정을 더 예측 가능하게 관리할 수 있습니다:

  • 실제 동작에 맞는 권한 텍스트 작성: 사용자가 왜 이 prompt가 뜨는지 이해할 수 있어야 합니다.
  • 카메라와 라이브러리 설정을 분리하여 구성: 한쪽이 실패하더라도 다른 쪽은 작동할 수 있습니다.
  • 네이티브 변경 후 재빌드: hot reload와 fast refresh는 native permissions를 업데이트하지 않습니다.
  • 기기에서 테스트: simulator에서는 권한 및 카메라 관련 이슈가 숨겨질 수 있습니다.

개발 중에는 picker가 잘 작동하지만 TestFlight이나 Play Store 빌드에서 문제가 발생한다면, 우선 설정 문제로 간주하세요. 대부분의 경우 설정 문제입니다.

Camera 및 Media Library 접근하기

사용자가 “Upload photo”를 탭하면 카메라나 라이브러리가 열리기를 기대합니다. 이때 앱의 역할은 명확합니다. 적절한 시스템 UI를 열고, 화면이 멈추지 않게 거절이나 취소를 처리하며, 미리보기나 upload에 사용할 수 있는 로컬 파일 참조를 반환하는 것입니다.

iOS와 Android 모두에서 managed 및 bare build를 테스트해 보기 전까지는 간단해 보일 수 있습니다. JavaScript API는 간결하게 유지되지만, 런타임 동작은 여전히 OS prompt, 기기 하드웨어, 그리고 이전에 설정한 native permissions에 따라 달라집니다.

카메라 또는 미디어 라이브러리 소스 중 하나를 선택하는 모바일 앱 image picker 프로세스를 보여주는 플로우차트입니다.

최소한의 기능을 갖춘 안전한 컴포넌트

핵심 흐름은 Expo managed 및 bare workflow 프로젝트 모두 동일합니다. 관련 권한을 요청하고, picker를 실행한 뒤, 사용자가 취소했는지 확인하고, 다음에서 첫 번째 asset을 읽어옵니다. result.assets.

기본적인 컴포넌트 구조는 다음과 같습니다:

import { useState } from 'react';
import { View, Button, Image, Alert } from 'react-native';
import * as ImagePicker from 'expo-image-picker';

export default function PhotoInput() {
  const [imageUri, setImageUri] = useState<string | null>(null);

  const pickFromLibrary = async () => {
    const permission = await ImagePicker.requestMediaLibraryPermissionsAsync();

    if (!permission.granted) {
      Alert.alert('Permission required', 'Please allow photo library access.');
      return;
    }

    const result = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ['images'],
      allowsEditing: true,
      quality: 1,
    });

    if (result.canceled) return;

    const asset = result.assets?.[0];
    if (!asset?.uri) return;

    setImageUri(asset.uri);
  };

  const takePhoto = async () => {
    const permission = await ImagePicker.requestCameraPermissionsAsync();

    if (!permission.granted) {
      Alert.alert('Permission required', 'Please allow camera access.');
      return;
    }

    const result = await ImagePicker.launchCameraAsync({
      allowsEditing: true,
      quality: 1,
    });

    if (result.canceled) return;

    const asset = result.assets?.[0];
    if (!asset?.uri) return;

    setImageUri(asset.uri);
  };

  return (
    <View>
      <Button title="Choose from library" onPress={pickFromLibrary} />
      <Button title="Take photo" onPress={takePhoto} />
      {imageUri ? (
        <Image
          source={{ uri: imageUri }}
          style={{ width: 200, height: 200 }}
        />
      ) : null}
    </View>
  );
}

여기서 세 가지 세부 사항이 중요합니다.

  • 라이브러리와 카메라 권한을 각각 별도로 요청하세요. 두 권한은 독립적으로 작동합니다.
  • 사용자가 취소한 경우를 에러 상태가 아닌 일반적인 사용자 동작으로 처리하세요.
  • 다음에서 읽기: assets[0], 피커가 최상위 객체가 아닌 에셋 배열을 반환하기 때문입니다. uri.

라이브러리 및 카메라 플로우

기능을 가장 빠르게 구현하고 싶다면 라이브러리 플로우부터 시작하세요. 테스트가 더 쉽고, 시뮬레이터 환경에서도 잘 작동하며, 카메라 하드웨어 관련 예외 상황을 피할 수 있습니다. 결과 처리 로직이 안정화된 후에 카메라 지원을 추가하세요.

카메라 경로는 개발 단계에서 실패할 가능성이 더 높습니다. iOS Simulator 지원이 제한적이며, Android 에뮬레이터는 실제 기기와 다른 카메라 동작을 보일 수 있습니다. Bare 프로젝트의 경우, 실제 원인은 네이티브 설정이나 테스트 환경임에도 불구하고 컴포넌트 code 문제로 오해할 수 있습니다.

깔끔한 UI 패턴은 피커 API를 호출하기 전에 사용자에게 소스를 먼저 묻는 것입니다:

const showPickerOptions = () => {
  Alert.alert('Upload image', 'Choose a source', [
    { text: 'Camera', onPress: takePhoto },
    { text: 'Photo Library', onPress: pickFromLibrary },
    { text: 'Cancel', style: 'cancel' },
  ]);
};

이렇게 분리하면 각 함수의 역할이 명확해집니다. 또한 나중에 분석(analytics), 피처 플래그(feature flags) 또는 백엔드 전용 규칙을 추가하기에도 용이합니다. 예를 들어, 어떤 팀은 프로필 이미지에는 라이브러리 업로드를 허용하지만, 본인 인증에는 반드시 카메라로 직접 촬영한 사진만 요구하기도 합니다.

앱 전체에서 Expo 외부의 파일 접근 패턴을 지원하거나 네이티브 스택 간의 컨벤션을 비교 중이라면, 이 Capacitor 사진 라이브러리 참조 내용이 유용한 참고가 될 것입니다.

팀원이나 QA에게 이 흐름을 보여줄 때 짧은 데모가 있으면 도움이 됩니다:

시스템 UI에서 기대할 수 있는 동작

expo-image-picker 플랫폼 피커나 카메라 UI가 열립니다. 앱이 해당 흐름의 모든 화면을 제어하는 것은 아닙니다. 이 차이는 매우 중요한데, “내 기기에서는 잘 된다”라는 말이 종종 “내가 테스트한 경로를 OS가 허용했다”라는 의미이기 때문입니다.

iOS의 경우 사용자가 전체 액세스 대신 제한된 라이브러리 액세스 권한만 부여할 수 있습니다. Android는 OS 버전과 제조사 스킨에 따라 피커 동작이 다를 수 있습니다. Managed workflow 프로젝트에서는 Expo가 네이티브 연결 작업을 대신 처리해 줍니다. Bare workflow 프로젝트에서는 빌드된 앱에 변경한 네이티브 권한 설정이 포함되었는지 확인해야 합니다. JavaScript 호출부는 두 경우 모두 동일할 수 있지만, 런타임 결과는 달라질 수 있습니다.

저는 보통 기능을 완료했다고 판단하기 전에 다음 케이스들을 테스트합니다:

  • 첫 권한 요청
  • 권한 거부
  • 사용자 취소
  • 라이브러리 선택 성공
  • 실제 기기에서 카메라 촬영 성공
  • 반환된 로컬 URI의 즉각적인 프리뷰

이 케이스들은 실제 프로덕션 동작과 직결됩니다. 또한 파일을 서버, moderation pipeline 또는 다음과 같은 게시 엔드포인트로 전송해야 하는 다음 단계로 깔끔하게 넘어갈 수 있도록 준비해 줍니다. Instagram 미디어 게시 API.

Picker 결과 및 옵션 처리하기

Picker 결과는 실제 프로덕션 로직이 필요한 부분입니다. 시스템 UI는 단순한 파일 경로가 아닌 구조화된 객체를 반환하며, 여기서 작은 실수가 발생하면 프리뷰 오류, 업로드 실패 또는 사용자 취소 시 앱 크래시로 이어질 수 있습니다.

결과 객체를 올바르게 읽는 방법

현재 Expo 앱에서 중요한 결과 형태는 result.assets[0].uri, 최상위 레벨이 아닌 result.uri입니다. 네이티브 설정은 다르더라도 JavaScript API는 동일하기 때문에, 이 디테일은 managed 및 bare workflow 프로젝트 모두에 영향을 미칩니다.

Guard-first 패턴을 사용하세요:

const result = await ImagePicker.launchImageLibraryAsync({
  mediaTypes: ['images'],
  allowsEditing: true,
  quality: 1,
});

if (result.canceled) {
  return;
}

const asset = result.assets?.[0];
if (!asset) {
  return;
}

const { uri } = asset;
setImageUri(uri);

이는 제가 가장 자주 접하는 두 가지 실패 케이스를 처리합니다. Picker를 취소하면 읽을 수 있는 asset이 제공되지 않으며, code가 항상 존재한다고 가정하면 result.assets[0] 런타임 에러가 발생합니다.

URI를 확보했다면, 프리뷰를 렌더링하는 것은 간단합니다:

<Image source={{ uri: imageUri }} style={{ width: 240, height: 240 }} />

나중에 upload할 계획이라면 전체를 유지하세요 asset 단순히 URI뿐만 아니라 관련 객체도 포함됩니다. 실제로, fileName, mimeType, width, height, 그리고 fileSize 검증, 로깅 또는 더 깔끔한 multipart 요청을 구성하는 데 유용합니다.

후속 동작을 변경하는 옵션

일부 피커 옵션은 선택 화면 이상의 영향을 미칩니다. 파일 크기, 편집 동작, 그리고 백엔드에서 수용해야 할 데이터 형식을 결정합니다.

옵션 유형 변경 사항 일반적인 용도
mediaTypes 배열 사용자 선택 범위 제한 API가 이미지 파일만 허용하는 경우, 선택 항목을 이미지로 제한하세요
allowsEditing boolean 지원되는 경우 OS의 자르기 또는 편집 UI를 제공합니다 아바타, 정사각형 커버, 영수증 촬영 등
quality number 지원되는 이미지 출력물을 압축합니다 모바일 네트워크에서의 upload 크기를 줄입니다
base64 boolean 결과값에 인코딩된 이미지 데이터를 추가합니다 인라인 이미지 데이터가 명시적으로 필요한 통합 환경에서만 사용하세요

놓치기 쉬운 몇 가지 트레이드오프가 있습니다:

  • allowsEditing 이미지 슬롯의 모양이나 크기가 고정된 경우 유용합니다. 서버에서 자체적인 crop 파이프라인을 처리하고 원본 파일이 필요한 경우에는 유용성이 떨어집니다.
  • quality upload 시간, 메모리 압박 및 서버 스토리지에 영향을 미칩니다. quality: 1 항상 최선의 선택은 아닙니다.
  • mediaTypes 백엔드 규칙과 일치해야 합니다. 서버에서 비디오를 거부한다면, 피커가 이를 반환하지 않도록 하세요.
  • base64 메모리 내 페이로드 크기를 증가시킵니다. 수신 서비스에서 반드시 요구하는 경우가 아니라면 피하세요.

마지막 포인트는 저사양 기기에서 중요합니다. 미리보기와 multipart upload에는 로컬 파일 URI를 전달하는 것이 보통 더 효율적입니다. Base64도 유효한 용도가 있지만, 파일 참조를 전달하는 방식에 비해 비용이 많이 듭니다.

URI vs base64

대부분의 앱에서 규칙은 간단합니다:

  • 미리보기에는 URI를 사용하세요.
  • 미리보기에는 URI를 파일 upload에는 사용하세요.
  • 사용하세요 base64 수신 시스템에서 인코딩된 콘텐츠를 명시적으로 요청할 때만 사용해야 합니다.

이 패턴을 사용하면 picker code의 크기를 작게 유지하고 테스트도 쉬워집니다. 또한, 나중에 외부 플랫폼으로 게시하는 서비스들을 포함하여 많은 백엔드 미디어 플로우의 구축 방식과도 일치합니다. 예를 들어, Instagram 미디어 게시 API.

팀에서 빈번하게 OTA 업데이트를 배포하거나 이미지 중심의 에셋을 앱 배포 프로세스에 포함시킨다면, 여기서의 파일 크기 결정이 전체 pipeline에 영향을 미칩니다. 다음 가이드는 앱 업데이트를 위한 이미지 최적화 방법을 다루며, picker 설정 시 유용한 참고 자료가 될 것입니다.

실제 앱을 위한 더 안전한 결과 패턴

데모용 code의 경우, 오직 imageUri 만 저장해도 괜찮습니다. 하지만 프로덕션 환경에서는 정규화된 객체를 저장하세요. 그래야 다음 단계인 미리보기, 검증, upload 또는 재시도 시 매번 picker의 raw 응답을 다시 해석할 필요가 없습니다.

const result = await ImagePicker.launchImageLibraryAsync({
  mediaTypes: ['images'],
  allowsEditing: true,
  quality: 0.8,
});

if (result.canceled || !result.assets?.length) {
  return;
}

const asset = result.assets[0];

setSelectedImage({
  uri: asset.uri,
  fileName: asset.fileName ?? 'upload.jpg',
  mimeType: asset.mimeType ?? 'image/jpeg',
  width: asset.width,
  height: asset.height,
  fileSize: asset.fileSize ?? null,
});

이렇게 하면 앱 내부에서 예측 가능한 하나의 데이터 구조를 가질 수 있습니다. 또한 다른 곳에서 native 차이를 해결하는 동안 앱 code이 안정적으로 유지되므로, managed 및 bare 프로젝트를 일관되게 관리하기가 더 쉬워집니다.

마지막으로 한 가지만 더 확인하세요. 만약을 대비해 불필요한 결과 필드를 활성화하지 마세요. 꼭 필요한 데이터만 요청하고, 피커가 일반적인 파일 처리 단계로 변질되지 않도록 선택 기능 자체에 집중하게 만드세요.

고급 패턴 및 플랫폼별 차이점

피커 기능은 첫 번째 선택한 이미지가 재시도, 인증 헤더, 네이티브 권한 차이, 실제 upload 엔드포인트를 거쳐야 하는 순간부터 복잡해지기 시작합니다. expo-image-picker 선택 기능은 잘 처리합니다. 나머지 기능 구현은 앱의 몫입니다.

이미지 Upload: 로컬 vs 서버 저장소 고려 사항이라는 제목의 인포그래픽으로, 서버 저장소의 장단점을 보여줍니다.

실용적인 upload 패턴

파일 upload를 기대하는 API의 경우, FormData 여전히 가장 안전한 기본값입니다. Rails, Node, Laravel, Django, Go 등 일반적인 백엔드에서 모두 작동하며, 피커를 전송 관련 로직과 분리해 줍니다.

async function uploadImage(imageUri: string) {
  const formData = new FormData();

  formData.append('file', {
    uri: imageUri,
    name: 'upload.jpg',
    type: 'image/jpeg',
  } as any);

  const response = await fetch('https://your-api.example.com/uploads', {
    method: 'POST',
    body: formData,
    headers: {
      Accept: 'application/json',
    },
  });

  if (!response.ok) {
    throw new Error('Upload failed');
  }

  return response.json();
}

그 code 정도면 경로가 작동함을 증명하기에 충분하지만, 프로덕션 앱에는 보통 한 단계가 더 필요합니다. 가능한 경우 선택된 에셋으로부터 name 및 type 데이터를 추출하고, 인증은 피커 함수 외부에서 처리하며, upload 상태를 피커 상태와 분리하여 요청이 실패하더라도 사용자가 라이브러리를 다시 열어야 하는 상황을 방지하세요.

리뷰 과정에서 자주 발생하는 실패 사례를 방지하기 위한 몇 가지 체크리스트입니다:

  • 로컬 파일 존재 여부 확인 uri 요청을 빌드하기 전에 확인하세요
  • 업로드 전 미리보기를 렌더링하여 사용자가 잘못된 파일을 조기에 발견하도록 합니다
  • 요청이 진행 중일 때 중복 탭을 방지합니다
  • 네트워크 실패를 피커 취소나 권한 오류와 분리하여 처리합니다
  • 대용량 파일, 지원되지 않는 MIME 타입 또는 인증 누락에 대해 백엔드 검증이 거부될 것을 예상하세요

백엔드에서 multipart 대신 base64를 요구한다면, 이는 보통 피커의 요구사항이 아닌 서버의 제약 사항입니다. Multipart가 메모리 비용이 저렴하고 모바일에서 다루기 더 쉽습니다.

플랫폼 차이가 실제로 중요한 지점

피커 UI는 네이티브이므로 네이티브 동작을 그대로 따릅니다. 이는 사용자가 보는 화면과 code이 가정해야 할 동작 모두에 영향을 미칩니다.

iOS의 편집 흐름과 권한 prompt는 Apple의 컨벤션을 따릅니다. 제한된 사진 접근 권한은 권한이 완전히 부여된 기기의 테스트 계정에서 보았던 것보다 더 적은 에셋을 반환할 수 있습니다. Android의 경우, OS 버전과 제조사 스킨에 따라 피커 동작이 더 다양하며, 특히 앨범, 파일 이름, 카메라 캡처 반환 방식에서 차이가 납니다. Bare React Native 앱은 네이티브 설정을 직접 관리하므로 이러한 차이를 더 직접적으로 느끼지만, managed Expo 앱 역시 피커를 완벽하게 균일한 것이 아닌 플랫폼별 특성을 가진 것으로 취급하는 code이 필요합니다.

실무적인 규칙은 간단합니다. 기기 간의 동일한 UI나 메타데이터에 의존하지 말고, 검증 가능한 필드에 의존하세요.

실제 앱에서 중요한 몇 가지 예시입니다:

  • 편집 및 자르기: iOS와 Android의 UI 및 자르기 동작이 서로 다릅니다
  • 반환되는 메타데이터: fileName, mimeType, 그리고 fileSize 데이터가 없거나 일관되지 않을 수 있으므로 fallback을 추가하세요
  • 권한 설정: iOS 사진 접근은 선택한 항목으로 제한될 수 있으며, Android는 OS 버전 및 시스템 피커 지원 여부에 따라 동작이 달라집니다
  • 카메라 출력: 촬영된 이미지는 라이브러리 자산과 파일명, 방향 또는 압축 특성이 다를 수 있습니다

팀이 Expo 외의 환경에서도 작업한다면, DesignStack 앱 개발 가이드 단일 라이브러리를 넘어 미디어 처리 결정을 내릴 때 유용한 Android 관련 정보를 제공합니다.

Managed vs. bare workflow 차이점

이 시점부터는 설정 방식이 운영 측면에서 중요해지기 시작합니다.

Managed workflow에서는 권한 문자열과 플러그인 설정이 주로 app config에 포함되며, 새로운 build를 생성할 때 native 변경 사항이 적용됩니다. 이는 JavaScript 영역을 깔끔하게 유지해주지만, config 수정 사항이 다음 native build 전까지는 반영되지 않음을 의미합니다. OTA 업데이트로는 누락된 native 권한을 패치할 수 없습니다.

Bare workflow에서는 동일한 기능이라도 고려해야 할 요소가 더 많습니다. iOS usage descriptions, Android manifest 동작, 패키지 설치 및 rebuild 타이밍을 직접 확인해야 합니다. 장점은 제어권이 높다는 것이지만, 단점은 picker 이슈가 JavaScript 호출부가 아닌 native 설정 때문에 발생할 수 있다는 점입니다.

Expo와 Capacitor 사이를 전환하는 팀은 종종 이 추상화 계층이 얼마나 다른지 과소평가하곤 합니다. Capgo에서는 다음 내용을 유용하게 설명하고 있습니다. Capacitor가 플랫폼 차이를 처리하는 방식팀이 어느 정도 수준의 native setup을 직접 관리할지 결정할 때 좋은 비교 기준이 될 것입니다.

두 workflow 모두에서 제 선호도는 일관됩니다. picker의 code 범위를 좁게 유지하고, 결과를 한 번 정규화하며, 전용 API 계층을 통해 upload하고, 플랫폼별 동작은 추측으로 덮어버리기보다 명시적으로 설정하고 테스트해야 할 대상으로 취급하십시오.

일반적인 문제 해결하기

대부분의 Expo Image Picker 버그는 몇 가지 카테고리로 나뉩니다. 가장 빠른 해결 방법은 config, 권한, 결과 처리, 또는 렌더링 중 어느 계층에서 실패가 발생하는지 파악하는 것입니다.

모바일 개발 프로젝트에서 expo-image-picker 라이브러리 사용 시 발생하는 일반적인 문제들을 해결하기 위한 체크리스트입니다.

주요 오류 빠른 점검

피커가 열리지 않거나 권한 오류가 발생하면 네이티브 설정을 먼저 확인하세요. 특히 Bare 앱의 경우, iOS usage description 누락이 주요 원인인 경우가 많습니다.

사용자가 피커를 닫은 후 앱이 크래시된다면 결과 처리 로직을 점검하세요. 많은 구현 사례에서 여전히 URI가 직접 반환된다고 가정하고 다음 단계를 canceled 건너뛰곤 합니다.

다음 매핑을 참고하세요:

  • Permission denied 오류: 앱 설정과 네이티브 권한 문자열을 확인한 후 다시 빌드하세요.
  • undefined 이미지 URI: 에서 읽기, result.assets?.[0]?.uri대신 result.uri.
  • 취소 후 아무 반응이 없는 경우: 맞을 수도 있습니다. 취소 시에는 아무 작업도 하지 않는 상태로 처리하세요.
  • 이미지가 렌더링되지 않을 때: URI가 state에 저장되었고 다음으로 전달되었는지 확인하세요: <Image source={{ uri }} />.
  • 시뮬레이터에서 카메라가 이상하게 작동할 때: 라이브러리 버그를 찾기 전에 실제 기기에서 먼저 테스트하세요.

간단한 프로덕션 체크리스트

배포 전 최종 점검 단계로 사용하세요:

  • Expo 도구로 설치하기: 사용하기 npx expo install expo-image-picker.
  • 네이티브 설정 구성: 플러그인과 필요한 권한 설명을 추가하세요.
  • 의도적으로 권한 요청하기: 카메라와 미디어 라이브러리 흐름을 분리하세요.
  • 모든 결과값에 대해 다음을 수행하세요: 확인 result.canceled 및 안전하게 읽기 assets[0].
  • URI 기반 업로드를 권장합니다: base64는 특수한 경우에만 사용하세요.
  • 실제 기기에서 테스트하세요: 특히 카메라 캡처 및 권한 요청(prompt) 확인이 필요합니다.

팀에서 React Native 프로젝트와 함께 Capacitor 또는 Electron 앱을 배포한다면, Capgo 매번 스토어 심사를 기다리지 않고도 JavaScript, CSS, 설정 및 에셋 업데이트를 전달할 수 있는 옵션입니다. 업로드 UI, 유효성 검사 규칙, 문구 또는 피커 흐름 관련 에셋 처리와 같이 이미지 관련 수정 사항이 웹 레이어에 있는 경우 매우 유용합니다.

Capacitor 앱용 Live updates

웹 레이어 버그가 라이브 상태라면, 앱 스토어 승인을 며칠씩 기다리지 말고 Capgo를 통해 수정 사항을 배포하세요. 네이티브 변경 사항은 정상 리뷰 절차를 그대로 따르는 반면, 사용자는 백그라운드에서 업데이트를 받습니다.

블로그 최신 글

Capgo는 정말 프로페셔널한 모바일 앱을 만드는 데 필요한 최고의 인사이트를 제공합니다.