๐Ÿ“ท 70. React Native ์นด๋ฉ”๋ผ ๋ฐ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ์ „๋žต โ€” react-native-image-picker, expo-image-picker, S3 ์—…๋กœ๋“œ๊นŒ์ง€

JM_Devยท2025๋…„ 8์›” 5์ผ
0
post-thumbnail

๋ชจ๋ฐ”์ผ ์•ฑ์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์ฐ๊ฑฐ๋‚˜ ์„ ํƒํ•ด์„œ ์—…๋กœ๋“œํ•˜๋Š” ๊ธฐ๋Šฅ์€
SNS, ์‡ผํ•‘, ํ—ฌ์Šค์ผ€์–ด ๋“ฑ ๊ฑฐ์˜ ๋ชจ๋“  ์„œ๋น„์Šค์—์„œ ๋“ฑ์žฅํ•œ๋‹ค.

์ด๋ฒˆ ๊ธ€์—์„œ๋Š”

  • ์–ด๋–ค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์จ์•ผ ํ•˜๋Š”์ง€
  • ์–ด๋–ค ์˜ต์…˜๋“ค์„ ์„ค์ •ํ•ด์•ผ ํ•˜๋Š”์ง€
  • ์—…๋กœ๋“œ๊นŒ์ง€ ์–ด๋–ป๊ฒŒ ์—ฐ๊ฒฐํ•˜๋Š”์ง€
    React Native์—์„œ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ๊ธฐ๋Šฅ ์ „์ฒด ํ๋ฆ„์„ ์ •๋ฆฌํ–ˆ๋‹ค.

โœ… 1. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ ํƒ ๊ธฐ์ค€

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌํŠน์ง•์ถ”์ฒœ ์ƒํ™ฉ
react-native-image-picker์นด๋ฉ”๋ผ + ๊ฐค๋Ÿฌ๋ฆฌ ์ง€์›, ๋„ค์ดํ‹ฐ๋ธŒ ๊ธฐ๋ฐ˜Bare Workflow
expo-image-pickerExpo ํ”„๋กœ์ ํŠธ ์ „์šฉ, ์‚ฌ์šฉ ํŽธํ•จExpo ๊ธฐ๋ฐ˜ ์•ฑ
expo-camera์ง์ ‘ ์นด๋ฉ”๋ผ UI ๊ตฌํ˜„ ๊ฐ€๋Šฅ์ปค์Šคํ…€ UI ํ•„์š”ํ•  ๋•Œ

๐Ÿ“ฆ 2. react-native-image-picker ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

์„ค์น˜

npm install react-native-image-picker
npx pod-install

๊ถŒํ•œ ์„ค์ • (Android)

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

์ด๋ฏธ์ง€ ์„ ํƒ ์˜ˆ์‹œ

import { launchImageLibrary } from 'react-native-image-picker';

const pickImage = () => {
  launchImageLibrary(
    {
      mediaType: 'photo',
      quality: 0.8,
    },
    (response) => {
      if (response.didCancel) return;
      if (response.assets) {
        console.log(response.assets[0].uri); // ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ
      }
    }
  );
};

โœ… ๊ฐค๋Ÿฌ๋ฆฌ์—์„œ ์ด๋ฏธ์ง€ ์„ ํƒ
โœ… response.assets[0].uri๋ฅผ ํ†ตํ•ด ์ด๋ฏธ์ง€ ์ ‘๊ทผ


๐Ÿ“ธ 3. ์นด๋ฉ”๋ผ๋กœ ์ดฌ์˜ํ•ด์„œ ์—…๋กœ๋“œ

import { launchCamera } from 'react-native-image-picker';

const takePhoto = () => {
  launchCamera(
    {
      mediaType: 'photo',
      saveToPhotos: true,
    },
    (response) => {
      if (response.assets) {
        const uri = response.assets[0].uri;
        uploadImageToServer(uri);
      }
    }
  );
};
  • saveToPhotos: true ์˜ต์…˜์œผ๋กœ ์ดฌ์˜ ํ›„ ์ €์žฅ๋„ ๊ฐ€๋Šฅ

โ˜๏ธ 4. ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ์˜ˆ์‹œ (S3 or ์„œ๋ฒ„)

const uploadImageToServer = async (uri: string) => {
  const formData = new FormData();
  formData.append('file', {
    uri,
    type: 'image/jpeg',
    name: 'photo.jpg',
  });

  await fetch('https://your-api.com/upload', {
    method: 'POST',
    headers: {
      'Content-Type': 'multipart/form-data',
    },
    body: formData,
  });
};

โœ… ์„œ๋ฒ„๋กœ multipart/form-data ์ „์†ก
โœ… ์‹ค์ œ ๋ฐฑ์—”๋“œ ๊ตฌํ˜„์— ๋”ฐ๋ผ ํŒŒ์ผ๋ช…, ํ† ํฐ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Œ


๐Ÿ” 5. ๊ถŒํ•œ ์ฒ˜๋ฆฌ ํŒ (Android & iOS)

  • Android 13 ์ด์ƒ: READ_MEDIA_IMAGES ๊ถŒํ•œ ํ•„์š”
  • iOS: Info.plist์— ์„ค๋ช… ์ถ”๊ฐ€
<key>NSCameraUsageDescription</key>
<string>ํ”„๋กœํ•„ ์‚ฌ์ง„ ์ดฌ์˜์„ ์œ„ํ•ด ์นด๋ฉ”๋ผ ์ ‘๊ทผ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>์‚ฌ์ง„ ์—…๋กœ๋“œ๋ฅผ ์œ„ํ•ด ์‚ฌ์ง„ ์ ‘๊ทผ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.</string>

๐Ÿง  ์‹ค์ „ ๊ฒฝํ—˜

์ฒ˜์Œ์—” Expo ๊ธฐ๋ฐ˜์œผ๋กœ ๋น ๋ฅด๊ฒŒ expo-image-picker๋ฅผ ์ผ์ง€๋งŒ,
์นด๋ฉ”๋ผ UI ์ปค์Šคํ„ฐ๋งˆ์ด์ง•์ด ํ•„์š”ํ•ด์ง€๋ฉด์„œ
react-native-image-picker๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ–ˆ๋‹ค.

์ด๋ฏธ์ง€ ์—…๋กœ๋“œ๋Š” FormData ๊ธฐ๋ฐ˜์œผ๋กœ ์ฒ˜๋ฆฌํ–ˆ๊ณ ,
์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์œ„ํ•ด ์ด๋ฏธ์ง€ ๋ฆฌ์‚ฌ์ด์ง• โ†’ ์—…๋กœ๋“œ ์ˆœ์„œ๋กœ ์ฒ˜๋ฆฌํ•ด์„œ
์„ฑ๋Šฅ๊ณผ UX ๋ชจ๋‘ ์ฑ™๊ธธ ์ˆ˜ ์žˆ์—ˆ๋‹ค.


๐Ÿ“ท โ€œ์‚ฌ์ง„ ํ•œ ์žฅ์—๋„ UX์™€ ์„ฑ๋Šฅ์ด ์ˆจ์–ด์žˆ๋‹ค.โ€


profile
๊ฐœ๋ฐœ์ž๋กœ ์ทจ์—…์„ ์ค€๋น„ ์ค‘ ์ด๋ฉฐ, ์—ด์‹ฌํžˆ ๊ณต๋ถ€ ์ค‘ ์ž…๋‹ˆ๋‹ค!

0๊ฐœ์˜ ๋Œ“๊ธ€