본문 바로가기

리액트네이티브(ReactNative)

[카메라 어플](2) : 핸드폰 연결, 사진 찍기, 저장하기, 찍은 사진 렌더링

728x90

1. Android Studio 실제 핸드폰(갤럭시 플립1) 연결하기 

지난 번 카메라 연결을 성공했지만, android studio에서 제공하는 virtual 환경 상에서의 연결이었다보니 여간 찜찜한 것이 아니었다.(절반의 성공...) 그리고 실제 앱 개발시에는 virtual 환경 대신 실제 핸드폰과 연결하여 개발을 하고 확인을 하는 것이 일반적이라는 내용을 알게 된 후 나의 핸드폰(갤럭시 플립1)과 android studio를 연결하는 것이 급선무라는 결론에 도달하였다.

 

https://developer.android.com/studio/run/device?hl=ko

 

하드웨어 기기에서 앱 실행  |  Android 개발자  |  Android Developers

Android 디버그 브리지(ADB) 연결을 통한 테스트 및 디버깅을 위해 개발 환경 및 Android 기기를 설정하는 방법을 알아보세요.

developer.android.com

위 링크에는 안드로이드 스튜디오와 실물 핸드폰을 연결하는 방법에 대해 잘 소개하고 있는데, 나의 경우 아래의 순서로 진행하였다.

 

1) 개발자 모드 활성화

우선 핸드폰에서 개발자 모드를 열어야 한다. 나의 경우 개발자 모드가 숨겨져 있었고,

https://www.hardreset.info/ko/devices/samsung/samsung-galaxy-z-flip/gaebalja-obsyeondeul/

위 링크의 글을 참조하여 따라 갤럭식 플립1의 개발자 모드를 활성화시켜주었다.

 

2) android version 11로 업데이트하기

나의 경우 개발을 하는 컴퓨터는 아이맥인 반면 확인을 하는 휴대폰은 갤플립1으로 당장에 이 둘을 연결할 잭이 집에 없는 상황이었다. 따라서 USB 디버깅은 대신 wifi pairing을 진행하기로 하였다. 하지만 왜인지 개발자 모드에서 무선 디버깅 항목이 없었고, 다시 문서를 읽어본 결과 무선 페어링은 안드로이드 버전 11에서부터 지원이 가능한 반면 나의 갤플립1은 안드로이드 버전 10이었다.(또르르...

 

다행히 이는 업데이트를 통해 해결할 수 있었는데,

https://support.google.com/android/answer/7680439?hl=ko

위의 문서에 따라 내 휴대폰의 안드로이드 버전을 11로 업그레이드 해 주었다.

 

3) wifi pairing 연결하기

이후의 절차는 다시 문서대로 쭉 따라 하면 된다.

위 사진처럼 개발자 모드에 무선 디버깅이라는 항목이 있고 들어가면 qr 코드 페어링 혹은 페어링 코드로 기기 페어링 항목이 뜨게 된다.

나는 qr코드로 기기 페어링을 선택해 진행하였다.

 

이처럼 android stuudio device manager의 physical device 탭에 내 휴대폰이 뜨는 것을 확인할 수 있다.

npm run android를 하면, 휴대폰 속에서 만들고 있는 어플이 켜짐 접근 권한을 묻는 창이 한 번 뜬 후에 접근 권한 허용시 카메라가 켜지는 것을 확인할 수 있다.(가상 emulator 처럼 알아서 켜저서 매우 신기했당 ><)

 

2. 카메라 버튼 만들고 사진찍기 

1) camera 버튼 css styling

카메라 어플의 임시 카메라 버튼!!

아직 완전한 디자인 시안이 나오지 않아 임의로 버튼을 디자인하여 앱 중앙 하단부에 박아두었다.

  return (
    <>
      {device && (
        <Camera style={StyleSheet.absoluteFill} isActive video photo device={device} ref={camera} />
      )}
      <View style={styles.area}>
        <TouchableOpacity style={styles.camButton} onPress={onPressButton}>
          <Text></Text>
        </TouchableOpacity>
      </View>
    </>
  );
};

const styles = StyleSheet.create({
  flex: {
    flex: 1,
  },
  area: {
    width: '100%',
    justifyContent: 'center',
    alignItems: 'center',
    position: 'absolute',
    bottom: '10%',
  },
  camButton: {
    width: 50,
    height: 50,
    borderRadius: 50,
    borderWidth: 3,
    borderColor: 'white',
    backgroundColor: '#e34077',
    elevation: 6,
  },
});

 

2) take a photo 함수 작성

 const onPressButton = async () => {
    if (!camera.current) return;
    const photo = await camera.current.takePhoto({
      flash: 'off',
      qualityPrioritization: 'speed',
    });

    console.log(photo);
  };

 

카메라 버튼을 누르고 log를 확인하면

 

{"height": 2268, "isRawPhoto": false, "metadata": {"Orientation": 6, "{Exif}": {"ApertureValue": 1.69, "BrightnessValue": 3.38, "ColorSpace": 1, "DateTimeDigitized": "2023:01:29 18:54:52", "DateTimeOriginal": "2023:01:29 18:54:52", "ExifVersion": "0220", "ExposureBiasValue": 0, "ExposureMode": 0, "ExposureProgram": 2, "ExposureTime": 0.016666666666666666, "FNumber": 1.8, "Flash": 0, "FocalLenIn35mmFilm": 26, "FocalLength": 4.25, "ISOSpeedRatings": [Array], "LensMake": null, "LensModel": null, "LensSpecification": [Array], "MeteringMode": 2, "OffsetTime": null, "OffsetTimeDigitized": null, "OffsetTimeOriginal": null, "PixelXDimension": 4032, "PixelYDimension": 2268, "SceneType": 1, "SensingMethod": 1, "ShutterSpeedValue": 5.906, "SubjectArea": [Array], "SubsecTimeDigitized": "398554", "SubsecTimeOriginal": "398554", "WhiteBalance": 0}, "{TIFF}": {"DateTime": "2023:01:29 18:54:52", "Make": "samsung", "Model": "SM-F700N", "ResolutionUnit": 2, "Software": "F700NKOU1CUA1", "XResolution": 72, "YResolution": 72}}, "path": "/data/user/0/com.sstam_sstam/cache/mrousavy7791361604471542959.jpg", "width": 4032}

 

와 같이 내가 찍은 사진에 대한 정보가 있는것을 알 수 있다.

 

3. 내가 찍은 사진 원하는 폴더위치에 저장하기 : React-native-fs 

사진을 찍었으면 내가 찍은 사진을 확인할 수 있어야 한다. 구글링 결과 react-native-fs(리액트 네이티브 파일시스템)을 이용하여 찍은 사진의 저장 위치를 옮기는 것이 가능했다.

 

우선 모듈 설치부터 gogo

npm i react-native-fs

항상 모듈을 설치한 후에는 재실행을 해줘야 한다.

 

  const onPressButton = async () => {
    if (!camera.current) return;
    const photo = await camera.current.takePhoto({
      flash: 'on',
      qualityPrioritization: 'speed',
    });

    await RNFS.moveFile(`/${photo.path}`, `${RNFS.PicturesDirectoryPath}/temp.jpg`).then(() =>
      console.log('Image Moved', `${photo.path}`, '-- to --', `${RNFS.PicturesDirectoryPath}`),
    );
  };

 

다음으로 RNFS를 이용하여 찍은 사진을 사진 갤러리에 저장되도록하는 함수를 넣어준다.

 

사진이 추가가 되었다!
사진과 갤러리 썸네일과의 괴리 현상 발생!!

 

4. 사진 연속으로 저장하기 : 썸네일과의 괴리 현상 없애기

앱에서 찍은 사진을 갤러리로 옮겨오는 것까지는 어찌어찌 성공하였으나, 역시 카메라 어플의 문턱은 높고도 높았다. 맨 처음에 찍은 사진이 갤러리에서 썸네일로 박혀져있고, 그 다음에는 사진을 찍으면 계속해서 추가가되는식이 아니라 썸네일은 갱신되지 않은 채 가장 최근의 사진으로 계속 덮어씌어지고만 있는 현상이 발생하고 있었다.(왜...왜지?ㅠㅠ)

코드를 확인해보니 아무래도 파일의 이름이 전부다 같게 설정되어서 사진이 연속 저장되지 못하고 덮어쓰기가 되고 있는 것 같았다.

 const fileName = photo.path.split('mrousavy')[1];

    await RNFS.moveFile(`/${photo.path}`, `${RNFS.PicturesDirectoryPath}/${fileName}.jpg`).then(
      () => console.log('Image Moved', `${fileName}`, '-- to --', `${RNFS.PicturesDirectoryPath}`),
    );

그래서 코드를 위와 같이 수정하여 사진을 찍을 때 마다 고유의 이름 값들로 새롭게 저장될 수 있게했다.

사진 연속 저장 완료!

 

FIX ME : 사진을 찍고 갤러리에 찍은 사진이 즉각 반영되지 않는 문제 발생 => 약 1분 정도 시간이 걸리는데, 일반적으로 우리가 사용하는 카메라 어플의 경우 저장이 바로 바로 됨 => 배포 문제일 가능성도 있음(다음주에 테스트 해 볼 것!)

 

 

5. 내가 찍은 사진 화면에 렌더링하기

이번에는 내가 찍은 사진을 어플에서 바로 띄워보기로했다.

 

  const [showCamera, setShowCamera] = useState(true);
  const [photos, setPhotos] = useState([]);
  
  ...
  
  const onPressButton = async () => {
    if (!camera.current) return;
    const photo = await camera.current.takePhoto({
      flash: 'on',
      qualityPrioritization: 'speed',
    });
    setPhotos([...photos, photo]);
    setShowCamera(false);
   ...
  };
   return (
    <>
      {device && showCamera && (
        <Camera style={StyleSheet.absoluteFill} isActive video photo device={device} ref={camera} />
      )}
      {showCamera && (
        <View style={styles.area}>
          <TouchableOpacity style={styles.camButton} onPress={onPressButton}>
            <Text></Text>
          </TouchableOpacity>
        </View>
      )}

      {photos && photos.length > 0 && (
        <View style={{ width: '100%', height: '100%' }}>
          {photos.map((photo, index) => (
            <View key={index}>
              <Image
                style={{ width: '100%', height: '100%' }}
                source={{ uri: 'file://' + photo.path }}
              />
            </View>
          ))}
        </View>
      )}
    </>
  );
};

우선 사진이 찍힌 상태인지 아닌지를 관리하기 위한 showCamera 상태를 만들어 준 뒤 처음 어플에 들어오면 항상 카메라와 버튼이 보이게 한다. 그리고 사진이 찍히고나면 이 값을 false로 바꾸고 찍은 사진의 이미지가 화면 전체에 보여질 수 있도록 한다.

 

사진이 찍히고 찍힌 사진이 어플 전체에 노출

이번주는 여기까지!!!

728x90