범위가 지정된 외부 저장소 액세스 관리하기

사용자에게 파일의 더 많은 권한을 제공하고 파일이 복잡해지는 것을 제한하기 위해, Android 10(API 레벨 29) 이상을 타겟팅하는 앱은 외부 저장소로 범위가 지정된 액세스 또는 범위 지정 저장소가 기본적으로 부여됩니다. 이러한 앱은 저장소의 앱별 디렉터리(Context.getExternalFilesDir()을 사용해 액세스함) 및 특정 유형의 미디어를 볼 수 있습니다. 앱에서 앱별 디렉터리나 MediaStore에 있지 않은 파일에 액세스해야 하는 경우가 아니면 범위 지정 저장소를 사용하는 것이 좋습니다.

다음 표에는 범위 지정 저장소가 파일 액세스에 미치는 영향이 요약되어 있습니다.

파일 위치 권한 필요 액세스 방법(*) 앱 제거 시 파일 삭제 여부
앱별 디렉터리 없음 getExternalFilesDir()
미디어 컬렉션
(사진, 동영상, 오디오)
READ_EXTERNAL_STORAGE
다른 앱의 파일에
액세스하는 경우에만
MediaStore 아니요
다운로드
(문서 및
eBook)
없음 저장소 액세스 프레임워크
(시스템의 파일 선택기 로드)
아니요

*저장소 액세스 프레임워크를 사용하여 권한을 요청하지 않고 앞의 표에 명시된 각 위치에 액세스할 수 있습니다.

이 페이지에서는 범위 지정 저장소를 사용할 때 앱이 액세스할 수 있는 파일을 설명하고 앱에서 외부 저장소 기기에 저장된 파일을 계속 공유, 액세스, 수정할 수 있도록 앱을 업데이트하는 방법을 설명합니다.

파일 액세스에 필요한 권한

범위 지정 저장소가 있는 앱은 앱별 디렉터리 내부와 외부 모두에서 앱이 생성하는 파일을 대상으로 한 읽기/쓰기 액세스 권한을 항상 가집니다. 이에 따라, 자체에서 생성한 파일만 저장하고 액세스하는 앱의 경우 READ_EXTERNAL_STORAGE 권한이나 WRITE_EXTERNAL_STORAGE 권한을 요청할 필요가 없습니다.

그러나 다른 앱에서 생성된 파일에 액세스하려면 다음 조건이 모두 충족되어야 합니다.

  1. 앱에 READ_EXTERNAL_STORAGE 권한이 부여되었습니다.
  2. 파일이 다음의 잘 정의된 미디어 컬렉션 중 하나에 있습니다.

'downloads' 디렉터리에 있는 파일을 비롯하여 다른 앱에서 만든 다른 파일에 액세스하려면 앱에서 사용자가 특정 파일을 선택하도록 허용하는 저장소 액세스 프레임워크를 사용해야 합니다.

READ_EXTERNAL_STORAGE 권한이 있어도 외부 저장소 기기의 원본 파일 시스템 뷰에 액세스하는 앱은 앱별 디렉터리에만 액세스할 수 있습니다. 앱이 원본 파일 시스템 뷰를 사용하여 이 디렉터리 외부의 파일을 열려고 하면 다음 오류가 발생합니다.

  • 관리형 코드에서는 FileNotFoundException이 발생합니다.
  • 네이티브 코드에서는 EPERM 커널 오류가 발생합니다.

미디어 데이터 제한사항

범위 지정 저장소에는 다음과 같은 미디어 관련 데이터 제한이 적용됩니다.

  • 앱에 ACCESS_MEDIA_LOCATION 권한이 부여되지 않으면 이미지 파일 내 Exif 메타데이터가 수정됩니다. 사진의 위치 정보에 액세스하는 방법에 관한 섹션에서 자세히 알아보세요.
  • MediaStore.Files 테이블은 자체가 필터링되어 사진, 동영상, 오디오 파일만 표시됩니다. 예를 들어 PDF 파일은 표시되지 않습니다.
  • 미디어 파일 액세스는 자바 기반 코드나 Kotlin 기반 코드에서 MediaStore를 사용하여 시작해야 합니다. 네이티브 코드에서 미디어 파일에 액세스하는 방법에 관한 안내를 참고하세요.

미디어 파일 작업을 하는 방법을 설명하는 가이드에서는 MediaStore 내에서 개별 문서 및 문서 트리에 액세스하기 위한 권장사항을 제공합니다. 앱이 범위 지정 저장소를 사용하는 경우 미디어에 액세스하는 이 방법이 필요합니다.

사진의 위치 정보

일부 사진의 경우 사용자가 사진이 촬영된 장소를 볼 수 있는 위치 정보가 Exif 메타데이터에 들어 있습니다. 그러나 이 위치 정보는 민감한 정보이므로, 앱이 범위 지정 저장소를 사용하는 경우 Android 10은 기본적으로 앱에서 발생한 정보를 숨깁니다.

앱이 사진 위치 정보에 액세스해야 하는 경우 다음 단계를 완료하세요.

  1. 앱의 manifest에서 ACCESS_MEDIA_LOCATION 권한을 요청합니다.
  2. 다음 코드 스니펫과 같이 MediaStore 개체에서 setRequireOriginal()을 호출하여 사진의 URI를 전달합니다.

    Kotlin

        // Get location data from the ExifInterface class.
        val photoUri = MediaStore.setRequireOriginal(photoUri)
        contentResolver.openInputStream(photoUri).use { stream ->
            ExifInterface(stream).run {
                // If lat/long is null, fall back to the coordinates (0, 0).
                val latLong = ?: doubleArrayOf(0.0, 0.0)
            }
        }
        

    자바

        Uri photoUri = Uri.withAppendedPath(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                cursor.getString(idColumnIndex));
    
        final double[] latLong;
    
        // Get location data from the ExifInterface class.
        photoUri = MediaStore.setRequireOriginal(photoUri);
        InputStream stream = getContentResolver().openInputStream(photoUri);
        if (stream != null) {
            ExifInterface exifInterface = new ExifInterface(stream);
            double[] returnedLatLong = exifInterface.getLatLong();
    
            // If lat/long is null, fall back to the coordinates (0, 0).
            latLong = returnedLatLong != null ? returnedLatLong : new double[2];
    
            // Don't reuse the stream associated with the instance of "ExifInterface".
            stream.close();
        } else {
            // Failed to load the stream, so return the coordinates (0, 0).
            latLong = new double[2];
        }
        

범위 지정 저장소 선택 해제하기

앱이 범위 지정 저장소와 완전히 호환되기 전에는 앱의 타겟 SDK 레벨이나 requestLegacyExternalStorage manifest 속성에 따라 범위 지정 저장소를 일시적으로 선택 해제할 수 있습니다.

  • Android 9(API 레벨 28) 이하를 타겟팅합니다.
  • Android 10 이상을 타겟팅하는 경우 앱의 manifest 파일에서 requestLegacyExternalStorage의 값을 true로 설정합니다.

        <manifest ... >
          <!-- This attribute is "false" by default on apps targeting
               Android 10 or higher. -->
          <application android:requestLegacyExternalStorage="true" ... >
            ...
          </application>
        </manifest>
        

Android 9 이하를 타겟팅하는 앱이 범위 지정 저장소를 사용할 때 어떻게 동작하는지 테스트하려면 requestLegacyExternalStorage의 값을 false로 설정하여 동작을 선택할 수 있습니다.