Android 저장소 사용 사례 및 권장사항

사용자가 파일을 더욱 세밀하게 제어하도록 하고 파일이 복잡해지는 것을 제한하기 위해 Android 10에는 범위 지정 저장소라는 앱의 새로운 저장소 패러다임이 도입되었습니다. 범위 지정 저장소는 앱이 기기 외부 저장소에서 파일을 저장하고 파일에 액세스하는 방식을 변경합니다. 범위 지정 저장소를 지원하도록 앱을 이전하려면 이 가이드에 설명된 일반적인 저장소 사용 사례 권장사항을 따르세요. 사용 사례는 미디어 파일 처리미디어가 아닌 파일 처리라는 두 카테고리로 구성됩니다.

대부분의 경우 앱은 다른 앱에서 액세스할 필요가 없거나 액세스하면 안 되는 파일을 만듭니다. 시스템은 이러한 파일을 관리하기 위해 앱별 저장소 위치를 제공합니다.

Android에서 파일을 저장하고 액세스하는 방법에 관한 자세한 내용은 저장소 교육 가이드를 참고하세요.

미디어 파일 처리

이 섹션에서는 미디어 파일(동영상, 이미지, 오디오 파일)을 처리하는 일반적인 사용 사례와 앱에서 사용할 수 있는 대략적인 접근 방식을 설명합니다. 다음 표에는 이러한 각 사용 사례가 요약되어 있으며 추가 세부정보가 포함된 각 섹션으로 연결되는 링크도 포함되어 있습니다.

사용 사례 요약
모든 이미지 또는 동영상 파일 표시 모든 Android 버전에 동일한 접근 방식을 사용합니다.
특정 폴더의 이미지 또는 동영상 표시 모든 Android 버전에 동일한 접근 방식을 사용합니다.
사진의 위치 정보에 액세스 앱에서 범위 지정 저장소를 사용하는 경우 한 가지 접근 방식을 사용합니다. 앱에서 범위 지정 저장소를 선택 해제하는 경우 다른 접근 방식을 사용합니다.
새 다운로드의 저장소 위치 정의 앱에서 범위 지정 저장소를 사용하는 경우 한 가지 접근 방식을 사용합니다. 앱에서 범위 지정 저장소를 선택 해제하는 경우 다른 접근 방식을 사용합니다.
기기로 사용자 미디어 파일 내보내기 모든 Android 버전에 동일한 접근 방식을 사용합니다.
단일 작업으로 여러 미디어 파일 수정 또는 삭제 Android 11에서는 한 가지 접근 방식을 사용합니다. Android 10의 경우 범위 지정 저장소를 선택 해제하고 대신 Android 9 이하의 접근 방식을 사용합니다.
이미 존재하는 단일 이미지 가져오기 모든 Android 버전에 동일한 접근 방식을 사용합니다.
단일 이미지 캡처 모든 Android 버전에 동일한 접근 방식을 사용합니다.
다른 앱과 미디어 파일 공유 모든 Android 버전에 동일한 접근 방식을 사용합니다.
특정 앱과 미디어 파일 공유 모든 Android 버전에 동일한 접근 방식을 사용합니다.
직접 파일 경로를 사용하는 코드 또는 라이브러리에서 파일에 액세스 Android 11에서는 한 가지 접근 방식을 사용합니다. Android 10의 경우 범위 지정 저장소를 선택 해제하고 대신 Android 9 이하의 접근 방식을 사용합니다.

여러 폴더의 이미지 또는 동영상 파일 표시

query() API를 사용하여 미디어 컬렉션을 쿼리합니다. 미디어 파일을 필터링 또는 정렬하려면 projection, selection, selectionArgs, sortOrder 매개변수를 조정합니다.

특정 폴더의 이미지 또는 동영상 표시

다음과 같은 접근 방식을 사용합니다.

  1. 앱 권한 요청에 설명된 권장사항에 따라 READ_EXTERNAL_STORAGE 권한을 요청합니다.
  2. 디스크에 있는 미디어 항목의 절대 파일 시스템 경로가 포함된 MediaColumns.DATA 값에 기반하여 미디어 파일을 검색합니다.

참고: 기존 미디어 파일에 액세스할 때 로직에서 DATA 열의 값을 사용할 수 있습니다. 이 값에 유효한 파일 경로가 있기 때문입니다. 그러나 파일을 항상 사용할 수 있다고 가정하지 마세요. 발생할 수 있는 모든 파일 기반 I/O 오류를 처리할 준비를 합니다.

반면에 미디어 파일을 만들거나 업데이트하려면 DATA 열을 사용하지 마세요. 대신 DISPLAY_NAME 열과 RELATIVE_PATH 열을 사용하세요.

사진의 위치 정보에 액세스

앱에서 범위 지정 저장소를 사용한다면 미디어 저장소 가이드의 사진 위치 정보 섹션의 단계를 따릅니다.

새 다운로드의 저장소 위치 정의

앱에서 범위 지정 저장소를 사용한다면 다운로드한 미디어 파일을 저장하려는 위치에 유의해야 합니다.

다른 앱에서 파일에 액세스해야 하면 다운로드 또는 문서 컬렉션에 잘 정의된 미디어 컬렉션을 사용하는 것이 좋습니다.

Android 11 이상에서는 외부 앱별 디렉터리 내에 있는 파일은 개발자가 DownloadManager를 사용하여 이러한 파일을 가져오더라도 다른 앱에서 액세스할 수 없습니다.

기기로 사용자 미디어 파일 내보내기

사용자 미디어 파일을 저장할 적절한 기본 위치를 정의합니다.

단일 작업으로 여러 미디어 파일 수정 또는 삭제

앱이 실행되는 Android 버전에 기반하여 로직을 통합합니다.

Android 11에서 실행

다음과 같은 접근 방식을 사용합니다.

  1. MediaStore.createWriteRequest() 또는 MediaStore.createTrashRequest()를 사용하여 앱의 쓰기 또는 삭제 요청의 대기 중인 인텐트를 만든 다음 이 인텐트를 호출하여 파일 집합의 수정 권한을 요청하는 메시지를 사용자에게 표시합니다.
  2. 사용자의 응답을 평가합니다.

    • 권한이 부여되면 수정 또는 삭제 작업을 진행합니다.
    • 권한이 부여되지 않으면 앱의 기능에 권한이 왜 필요한지 사용자에게 설명합니다.

Android 11 이상에서 제공되는 이러한 메서드를 사용하여 미디어 파일 그룹 관리 방법을 자세히 알아보세요.

Android 10에서 실행

앱이 Android 10(API 수준 29)을 타겟팅하는 경우 범위 지정 저장소를 선택 해제하고 Android 9 이하의 접근 방식을 계속 사용하여 이 작업을 실행합니다.

Android 9 이하에서 실행

다음과 같은 접근 방식을 사용합니다.

  1. 앱 권한 요청에 설명된 권장사항에 따라 WRITE_EXTERNAL_STORAGE 권한을 요청합니다.
  2. MediaStore API를 사용하여 미디어 파일을 수정 또는 삭제합니다.

이미 존재하는 단일 이미지 가져오기

이미 존재하는 단일 이미지를 가져오려는 경우(예: 사용자 프로필의 사진으로 사용) 앱에서 작업에 자체 UI를 사용하거나 시스템 선택 도구를 사용할 수 있습니다.

자체 사용자 인터페이스 표시

다음과 같은 접근 방식을 사용합니다.

  1. 앱 권한 요청에 설명된 권장사항에 따라 READ_EXTERNAL_STORAGE 권한을 요청합니다.
  2. query() API를 사용하여 미디어 컬렉션을 쿼리합니다.
  3. 앱의 맞춤 UI에 결과를 표시합니다.

시스템 선택 도구 사용

사용자에게 가져올 이미지를 선택하라고 요청하는 ACTION_GET_CONTENT 인텐트를 사용합니다.

시스템 선택 도구에서 사용자에게 선택하도록 표시하는 이미지 유형을 필터링하려는 경우 setType() 또는 EXTRA_MIME_TYPES를 사용하면 됩니다.

단일 이미지 캡처

앱에서 사용할 단일 이미지를 캡처하려면(예: 사용자 프로필의 사진으로 사용) ACTION_IMAGE_CAPTURE 인텐트를 사용하여 사용자에게 기기 카메라로 사진을 찍으라고 요청합니다. 시스템은 캡처된 사진을 MediaStore.Images 테이블에 저장합니다.

다른 앱과 미디어 파일 공유

insert() 메서드를 사용하여 MediaStore에 직접 기록을 추가합니다. 자세한 내용은 미디어 저장소 가이드의 항목 추가 섹션을 참조하세요.

특정 앱과 미디어 파일 공유

파일 공유 설정 가이드에 설명된 대로 Android FileProvider 구성요소를 사용합니다.

직접 파일 경로를 사용하는 코드 또는 라이브러리에서 파일에 액세스

앱이 실행되는 Android 버전에 기반하여 로직을 통합합니다.

Android 11에서 실행

다음과 같은 접근 방식을 사용합니다.

  1. 앱 권한 요청에 설명된 권장사항에 따라 READ_EXTERNAL_STORAGE 권한을 요청합니다.
  2. 직접 파일 경로를 사용하여 파일에 액세스합니다.

자세한 내용은 직접 파일 경로를 사용하여 미디어 파일을 여는 방법에 관한 섹션을 참고하세요.

Android 10에서 실행

앱이 Android 10(API 수준 29)을 타겟팅하는 경우 범위 지정 저장소를 선택 해제하고 Android 9 이하의 접근 방식을 계속 사용하여 이 작업을 실행합니다.

Android 9 이하에서 실행

다음과 같은 접근 방식을 사용합니다.

  1. 앱 권한 요청에 설명된 권장사항에 따라 WRITE_EXTERNAL_STORAGE 권한을 요청합니다.
  2. 직접 파일 경로를 사용하여 파일에 액세스합니다.

미디어가 아닌 파일 처리

이 섹션에서는 미디어가 아닌 파일을 처리하는 일반적인 사용 사례와 앱에서 사용할 수 있는 대략적인 접근 방식을 설명합니다. 다음 표에는 이러한 각 사용 사례가 요약되어 있으며 추가 세부정보가 포함된 각 섹션으로 연결되는 링크도 포함되어 있습니다.

사용 사례 요약
문서 파일 열기 모든 Android 버전에 동일한 접근 방식을 사용합니다.
보조 저장소 볼륨의 파일에 쓰기 Android 11에서는 한 가지 접근 방식을 사용합니다. 이전 버전의 Android에는 다른 접근 방식을 사용합니다.
기존 저장소 위치의 기존 파일 이전 가능하면 파일을 범위 지정 저장소로 이전합니다. 필요한 경우 Android 10의 범위 지정 저장소를 선택 해제합니다.
다른 앱과 콘텐츠 공유 모든 Android 버전에 동일한 접근 방식을 사용합니다.
미디어가 아닌 파일 캐시 모든 Android 버전에 동일한 접근 방식을 사용합니다.
기기로 미디어가 아닌 파일 내보내기 앱에서 범위 지정 저장소를 사용하는 경우 한 가지 접근 방식을 사용합니다. 앱에서 범위 지정 저장소를 선택 해제하는 경우 다른 접근 방식을 사용합니다.

문서 파일 열기

ACTION_OPEN_DOCUMENT 인텐트를 사용하여 사용자에게 시스템 선택 도구로 열 파일을 선택하라고 요청합니다. 시스템 선택 도구에서 사용자에게 선택하도록 표시하는 파일 유형을 필터링하려는 경우 setType() 또는 EXTRA_MIME_TYPES를 사용하면 됩니다.

예를 들어 다음 코드를 사용하여 PDF, ODT 및 TXT 파일을 모두 찾을 수 있습니다.

Kotlin

startActivityForResult(
        Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
            addCategory(Intent.CATEGORY_OPENABLE)
            type = "*/*"
            putExtra(Intent.EXTRA_MIME_TYPES, arrayOf(
                    "application/pdf", // .pdf
                    "application/vnd.oasis.opendocument.text", // .odt
                    "text/plain" // .txt
            ))
        },
        REQUEST_CODE
      )

Java

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("*/*");
        intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {
                "application/pdf", // .pdf
                "application/vnd.oasis.opendocument.text", // .odt
                "text/plain" // .txt
        });
        startActivityForResult(intent, REQUEST_CODE);

보조 저장소 볼륨의 파일에 쓰기

보조 저장소 볼륨에는 SD 카드가 포함됩니다. StorageVolume 클래스를 사용하여 지정된 저장소 볼륨에 관한 정보에 액세스할 수 있습니다.

앱이 실행되는 Android 버전에 기반하여 로직을 통합합니다.

Android 11에서 실행

다음과 같은 접근 방식을 사용합니다.

  1. 범위 지정 저장소 모델을 사용합니다.
  2. Android 10(API 수준 29) 이하를 타겟팅합니다.
  3. WRITE_EXTERNAL_STORAGE 권한을 선언합니다.
  4. 다음 액세스 유형 중 하나를 실행합니다.
    • MediaStore API를 사용하는 파일 액세스
    • File이나 fopen()과 같은 API를 사용하는 직접 파일 경로 액세스

이전 버전에서 실행

저장소 액세스 프레임워크를 사용하면 사용자가 보조 저장소 볼륨에서 앱이 파일을 쓸 수 있는 위치를 선택할 수 있습니다.

기존 저장소 위치의 기존 파일 이전

디렉터리가 앱별 디렉터리나 공개 공유 디렉터리가 아니라면 기존 저장소 위치로 간주됩니다. 앱이 기존 저장소 위치에서 파일을 만들거나 사용하면 범위 지정 저장소를 사용하여 액세스할 수 있는 위치로 앱 파일을 이전하고 필요한 앱 변경을 실행하여 범위 지정 저장소의 파일을 사용하는 것이 좋습니다.

데이터 이전을 위해 기존 저장소 위치 액세스 권한 유지

범위 지정 저장소를 사용하여 액세스할 수 있는 위치로 앱 파일을 이전하려면 앱은 기존 저장소 위치 액세스 권한을 유지해야 합니다. 사용해야 하는 접근 방식은 앱의 타겟 API 수준에 따라 다릅니다.

앱이 Android 11을 타겟팅하는 경우
  1. 사용자가 Android 11을 타겟팅하는 앱의 새 버전으로 업그레이드할 때 앱에서 사용자의 데이터를 이전할 수 있도록 preserveLegacyExternalStorage 플래그를 true로 설정하여 기존 저장소 모델을 유지합니다.

  2. 앱이 계속해서 Android 10 기기의 기존 저장소 위치에 있는 파일에 액세스할 수 있도록 지속적으로 범위 지정 저장소를 선택 해제합니다.

앱이 Android 10을 타겟팅하는 경우

범위 지정 저장소를 선택 해제하여 여러 Android 버전에서 앱 동작을 더 쉽게 유지할 수 있습니다.

앱 데이터 이전

앱이 이전할 준비가 되면 다음 접근 방식을 사용합니다.

  1. Android 10 이하를 타겟팅합니다.
  2. 범위 지정 저장소를 선택 해제하여 이전해야 하는 파일에 앱이 액세스할 수 있도록 합니다.
  3. File API를 사용하여 /sdcard/ 아래 현재 위치에서 범위 지정 저장소로 액세스할 수 있는 위치로 파일을 이동하는 코드를 배포합니다.

    1. 모든 비공개 앱 파일을 getExternalFilesDir() 메서드에서 반환된 디렉터리로 이동합니다.
    2. 미디어가 아닌 공유 파일을 Downloads/ 디렉터리의 앱 전용 하위 디렉터리로 이동합니다.
  4. /sdcard/ 디렉터리에서 앱의 기존 저장소 디렉터리를 삭제합니다.

사용자는 새 버전의 앱을 설치한 후 기기에서 데이터 이전 프로세스를 완료합니다. 분석 이벤트를 만들어 사용자층 전반의 이전 프로세스를 모니터링할 수 있습니다.

사용자가 데이터를 이전한 후 Android 11을 타겟팅하는 앱에 또 다른 업데이트를 게시합니다.

다른 앱과 콘텐츠 공유

앱 파일을 다른 단일 앱과 공유하려면 FileProvider를 사용하세요. 모두 서로 파일을 공유해야 하는 앱의 경우 각 앱에 콘텐츠 제공업체를 사용한 다음 앱이 컬렉션에 추가될 때 데이터를 동기화하는 것이 좋습니다.

미디어가 아닌 파일 캐시

사용해야 하는 접근 방식은 캐시해야 하는 파일 유형에 따라 다릅니다.

기기로 미디어가 아닌 파일 내보내기

미디어가 아닌 파일을 저장할 적절한 기본 위치를 정의합니다. 사용자가 앱별 디렉터리의 파일을 좀 더 일반적으로 액세스 가능한 위치로 내보내도록 허용합니다. MediaStore의 다운로드 또는 문서 컬렉션을 사용하여 미디어가 아닌 파일을 기기로 내보냅니다.

앱별 파일 처리

앱이 다른 앱에서 액세스할 필요가 없거나 액세스하면 안 되는 파일을 만드는 경우 이러한 파일을 앱별 저장소 위치에 저장할 수 있습니다.

내부 저장소 디렉터리

시스템은 다른 앱에서 이러한 위치에 액세스하는 것을 방지하고, Android 10(API 수준 29) 이상에서는 이러한 위치가 암호화됩니다. 이러한 위치는 앱만 액세스할 수 있는 민감한 정보를 저장하기에 좋습니다.

외부 저장소 디렉터리

앱별 파일을 저장하는 데 내부 저장소 공간이 충분하지 않으면 대신 외부 저장소를 사용해보세요. 다른 앱에 적절한 권한이 있는 경우 이 디렉터리에 액세스할 수 있지만, 이 디렉터리에 저장된 파일은 앱에서만 사용하도록 설계되었습니다.

Android 4.4(API 수준 19) 이상에서는 앱이 외부 저장소 내 앱별 디렉터리에 액세스하기 위해 저장소 관련 권한을 요청할 필요가 없습니다.

사용자가 앱을 제거하면 앱별 저장소에 저장된 파일이 삭제되므로 사용자가 앱과 관계없이 유지되기를 예상하는 항목을 저장하는 데 이 저장소를 사용해서는 안 됩니다.

범위 지정 저장소를 일시적으로 선택 해제

앱이 범위 지정 저장소와 완전히 호환되기 전에 테스트프로덕션 앱에서 모두 일시적으로 선택 해제할 수 있습니다.

테스트에서 선택 해제

Android 10(API 수준 29) 이상에서는 앱 테스트가 기본적으로 저장소 샌드박스에서 실행됩니다. 이 샌드박스는 앱이 앱별 디렉터리와 공개적으로 공유된 디렉터리 외부의 파일에 액세스하는 것을 방지합니다.

테스트에서 스크린샷이나 디버깅 데이터, 커버리지 데이터, 성능 측정항목과 같은 호스트의 파일을 출력하면 이러한 파일을 전역 디렉터리에 쓸 수 있습니다. 이렇게 하려면 am instrument를 호출하는 관련 하네스에 다음 플래그를 추가합니다.

-e no-isolated-storage 1

이 플래그는 계측 테스트 사례의 모든 동작에 영향을 미치고 호출된 모든 테스트 코드에 영향을 미칩니다. 따라서 이 플래그를 사용할 때는 앱과 범위 지정 저장소의 호환성을 검증할 수 없습니다. 테스트 출력의 경우 대신 셸에서 읽을 수 있는 앱 범위 지정 저장소에 쓰는 것이 더 좋습니다. 그런 다음 앱 범위 지정 디렉터리를 가져올 수 있습니다. 가져올 디렉터리를 결정하려면 getExternalMediaDirs()를 호출합니다.

프로덕션 앱에서 선택 해제

앱이 Android 10(API 수준 29) 이하를 타겟팅한다면 프로덕션 앱에서 범위 지정 저장소를 일시적으로 선택 해제할 수 있습니다. 그러나 Android 10을 타겟팅한다면 앱의 매니페스트 파일에서 requestLegacyExternalStorage 값을 true로 설정해야 합니다.

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

Android 10 이하를 타겟팅하는 앱이 범위 지정 저장소를 사용할 때 어떻게 동작하는지 테스트하려면 requestLegacyExternalStorage의 값을 false로 설정하여 동작을 선택할 수 있습니다. Android 11을 실행하는 기기에서 테스트하는 경우 앱 호환성 플래그를 사용하여 범위 지정 저장소가 있거나 없는 앱의 동작을 테스트할 수도 있습니다.

추가 리소스

Android 저장소에 관한 자세한 내용은 다음 자료를 참고하세요.

블로그 게시물