이제는 Android Scoped Storage를 준비해야 할 때

Myungwook
11 min readNov 10, 2019

--

Android 10 (API level 29)를 Target API로 설정한 앱이 Android 10이상의 단말에 설치되는 경우, 외부저장소에 대해 Scoped storage 모드로 동작하게 됩니다.

외부 저장소(External Storage)란?

애플리케이션이 파일을 쓸 수 있는 영역을 의미합니다.
다른 앱들에 노출되지 않는 내부 저장소와는 다르게, 다른 앱들에 노출이 되어 접근이 가능한 저장공간 입니다. 누가 생성한 파일인지, 어떤 경로로 저장된 파일인지 관계없이 모든 파일에 접근이 가능합니다. 제한 없이 모든 종류의 파일 생성이 가능하고요.

Android Pie(혹은 그 이하 버전)에서 이 영역에 접근하기 위해서는 READ/WRITE_EXTERNAL_STORAGE와 같은 Stoarge Permission을 받아야 합니다. 사용자로부터 이 퍼미션을 승인 받게 되면, sdcard 내의 모든 영역에 접근이 가능하고, MediaStore에 Query도 가능합니다.

외부 저장소에 변화가 필요한 이유

Storage 권한은 Android 앱에서 가장 많이 사용되는 권한인데요, 이 권한의 남용으로 그동안 많은 문제들이 발생했었다고 합니다.

첫번째는 파일의 관리를 좀더 쉽게 하기 위함입니다. 파일들이 어떤 앱에 소속되어있는지 명확하지 않아 관리가 어려웠습니다. /sdcard 루트 디렉토리내에 마음껏 파일과 디렉토리를 만들수 있고, 관리되지 않아 앱이 삭제 된 이후에도 사용하지 않는 파일들이 많이 남아있었습니다.

Android 10부터는 어떤 파일이 어떤 앱에 소속되어있는지, 시스템이 기억을 해두었다가, 앱이 언인스톨 될 때에, 파일도 함께 제거되도록 합니다.

두번째는 앱의 데이터를 보호하기 위해서 입니다. 안드로이드 보안 권장사항 문서에서는 외부 저장소는 언제든 사용자가 삭제할 수 있고, 모든 앱에서 수정이 가능하기 때문에, 민감한 정보는 저장하지 않아야 한다고 권장하고 있습니다. 그리고 Man-in-the-Disk라는 CheckPoint에서 발행한 포스트를 보면, 이 권장사항을 지키지 않았을 경우에, 악의적인 목적을 가진 앱이, 우리의 앱의 데이터를 어떻게 수정하고 어떻게 공격할 수 있는지에 대해 알 수 있습니다. 한번 읽어보시길 권장드리겠습니다. 안드로이드 보안 권장사항을 모두 지킨다고 해도 근본적으로 이런 약점에 노출될 수 밖에 없습니다.

Android 10에서는 다른 앱이 우리 앱에 소속된 파일들을 볼 수 없도록 샌드박스 구조의 저장소로 변경하여 앱에 관련된 데이터들을 보호합니다.

세번째는 유저의 데이터를 보호하기 위해서 입니다. 그동안에는 사용자가 앱을 통해 다운로드 했거나 만든 파일들을 다른 앱에서 쉽게 접근이 가능했었습니다. 악의적인 의도를 가진 앱이 사용자의 개인정보가 들어가있는 파일들에 대해 접근하려고 하면 충분히 접근이 가능했던 구조였습니다.

Android 10에서는 앱이 언제 어떤 파일을 사용하는지 사용자가 알도록 하는 절차를 추가하여 유저의 데이터를 보호하도록 합니다.

Android 10에서의 외부저장소 변화

Andtoid 10의 외부 저장소는 Scoped Storage이라는 모드가 적용됩니다.

기존 외부 저장소의 Public File 공간이 모두 사라지고 개별 앱 공간이 샌드박스 형식으로 격리되어, 이제 다른 앱의 파일에 직접 접근할 수 없습니다.

MediaStore는 추가권한 없이 사용이 가능하게 되었습니다. 기존과는 다르게 Storage permission 없이도 파일을 넣을 수 있고, 내 앱이 추가한 파일은 계속 읽고 쓰는 것이 가능합니다. 다른 앱이 저장한 파일을 보기 위해서는 여전히Storage Permission는 필요합니다.

기존에는 파일의 경로만 가지고 있었다면, 파일에 접근하여 읽고 쓰는 것이 가능했었습니다. 이제 Scoepd Storage Mode에서는 파일 경로만으로 접근하는 것은 불가능합니다. 시스템 파일 선택기를 열어 사용자가 직접 디스크에 있는 파일을 선택하게 해야 접근이 가능하게 됩니다. 직접 파일 선택을 하면서 어떤 파일을 어떤 앱에서 사용하는지 사용자가 명확하게 알수 있기 때문에, 유저의 데이터 보호에 도움이 된다고 설명하고 있습니다.

변경사항에 대하여 좀더 자세히 알아보도록 하겠습니다.

/sdcard 영역 접근 불가

이제 앱에서는 더이상 /sdcard 이하의 영역에 직접 접근할 수 없습니다. 직접 접근을 시도하려고 하면, FileNotFoundException이 발생하거나, 권한이 없다는 에러가 발생하게 됩니다.

최상위 경로를 가지고오도록 했던 Environment.getExternalStorageDirectory()는 deprecated 되었습니다.

루트 디렉토리나 다른 앱의 디렉토리를 이제 볼 수 없기 때문에, ASTRO나 ES file Exploer와 같은 파일 관리자 앱의 역할이 많이 축소될 수 있을 것 같습니다.

Android 11부터 다시 file path의 사용을 허용한다고 합니다. 자세한 내용은 이링크에서 확인해주세요~! https://medium.com/androiddevelopers/modern-user-storage-on-android-e9469e8624f9

개별 앱 공간의 변경

개별앱의 공간은 샌드박스 모델로 격리되게 되어, 다른 앱의 저장공간에는 더이상 접근할 수 없습니다. 추가 권한없이 읽고 쓰기는 여전히 가능하고요, 이 공간 만큼은 파일의 절대경로로 직접 접근하는 것이 여전히 가능합니다.

아마 기존 앱에서 경로 형태로 파일을 참조하는 케이스가 많을 것 같아서 하위호환성을 위해 고려된 부분일 것 같습니다. 개별 앱 공간은 변화없이 기존과 동일한 경로를 갖고 있고요, 기본 파일관리 앱으로 접근하는 것도 가능합니다.

다른 앱으로 우리 앱의 파일을 전달할때는 파일경로를 통해 전달하는 것은 이제 불가능합니다. 대신 FileProvider를 사용해 해당 파일에 접근할 수 있는 ContentUri를 만들고, 공유 받을 앱에 임시로 URI 접근 권한을 허용하는 방법을 사용해야 합니다. 이건 아마 기존에 Target을 올리시면서 많이 작업해놓으셨을 것 같습니다.

공용 저장 공간의 변경

파일을 다른 앱이 사용할 수 있도록 하거나, 사용자가 탐색기를 통해 파일을 바로 열어볼 수 있게 하기 위해서는 공용 저장공간에 파일을 저장해야합니다. 또 앱이 삭제된 이후에도 남아 있어야 하는 파일이 있다면 마찬 가지로 이곳에 파일을 저장해야 합니다.

기존에는 /sdcard, 루트 디렉토리 자체가 거대한 공용저장공간으로서의 역할을 했었고, 그 자체가 External Storage 였다면, 이제는 MediaStore와 StorageAccessFramework 등을 이용해서 공용저장공간에 읽고 쓰는 작업을 해야하고, 이 영역만을 External Storage라고 부를 수 있을 것 같습니다.

사진과 동영상, 음악은 이미 MediaStore에 존재했던 컬렉션들이고요, 그 이외의 파일들을 지원하기 위해 Download 컬렉션이 추가되었습니다. Download에 저장된 파일들은 자신이 생성한 파일을 제외하고는 시스템 파일 탐색기를 통해서 사용자가 명시적으로 선택한 경우에만 접근이 가능합니다.

파일 접근 정리

이 표는 구글 개발자 문서 내용을 가져온건데요, 파일에 대한 접근을 다시 간단히 정리해보겠습니다.

개별앱 공간은 권한이 필요없고, 앱 제거시 삭제가 됩니다. 외부 앱에서 접근이 불가능하다는 점을 제외하고는, 변화가 가장 없습니다.

사진, 비디오, 음악, 다운로드 파일들을 사용자가 다른 앱에서 사용하게 하기 위해서는 MediaStore나 Storage Access Framework를 사용하면 되고, 앱 삭제시 제거되지 않습니다. 다른 앱 파일을 접근할 때에만 READ/WRITE_EXTERNAL_STORAGE 퍼미션이 있으면 됩니다.

Scoped Storage Mode 대응하기

구글에서 Scoped 모드를 시작한다고 발표했을때, 여러 개발자 커뮤니티에서, 앱에 적용하기까지 많은 시간이 필요하다는 의견을 받았다고 합니다. 그래서 처음에는 바로 Scoped mode를 적용하려고 했다가, Beta 3에서 앱의 타켓을 Q로 올렸을 때에, 활성화되는 것으로 완화하였습니다.

Target을 Android 10 (API level 29)로 올린 앱이, 10이상의 단말에 새로 설치되는 경우에만 Storage mode가 활성화 됩니다. Android의 저장소 정책은 앱이 처음 설치했을때의 상태를 유지해준다고 합니다. 만약에 Android P 단말에서 여러분의 앱이 설치가 되고, 사용자의 단말이 Android 10 디바이스로 업그레이드가 되거나, 앱을 Target level 29로 변경하여 업그레이드가 되었을때도기존과 동일한 Storage mode로 동작하게 된다고 합니다.

일시적으로 opt out 할수 있는 기능도 지원됩니다. Scoped mode 대응 작업을 하시다가 버그를 발견하게 되면, Manifest에 requestLegacyExternalStorage 플래그를 추가하셔서, 임시로 opt-out을 하시고, 구글 관계자께 연락하시거나구글 이슈트래커에 남기시셔야 할 것 같습니다.

그리고 다음 major platform 릴리즈에서는 임시 Opt-out 기능이 동작하지 않을 예정이라고 합니다. 그때부터는 Target에 상관없이 모든 앱에 scoped mode로 동작할 예정이라고 하니, 제 생각에는 늦어도 2020년 봄까지는 Scoped mode를 완전히 대응하셔야 하지 않을까 싶습니다.

2020년 3월 Android 11 Developer Preview 에서, Target을 Android 11로 변경하기 전까지는 requestLegacyExternalStorage 플래그를 유지할 것이라고 발표하였습니다. 따라서 Target을 Android 11로 하기 전까지 조금더 시간을 확보할 수 있을 것으로 보입니다.

예제를 살펴보겠습니다. 타겟 sdk를 29로 올리고, requestLegacyExternalStorageManifest flag를 true로 주면, 임시로 opt out 할 수 있습니다. 이것으로 안드로이드에 예전 스토리지 모드를 유지하고 싶다라고 표시하게 됩니다.

걱정되는 이슈는 다른 개발자가 개발한 Shared library를 앱에 포함하여 사용하고 있는 경우입니다. 직접 수정이 어려우실텐데요, 이 경우에도 이 플레그를 추가하시고, 라이브러리 개발자에게, 연락해서, Scoped mode가 정상동작할 수 있도록 업데이트를 요청하셔야 합니다.

Environment.isExternalStorageLegacy()는 현재 앱의 저장소 모드가 기존 스토리지 모드인지 Scoped mode인지,확인할 수 있게 해줍니다. 이 메소드로 구분해서 기존 파일 저장 방식을 사용하거나 새로운 파일저장 방식을 사용해야 할 것 같습니다.

또 한가지 걱정스러운 점은 실제 개발하면서 좀더 자세히 알아봐야 알겠지만, 한 앱에서 Android 10 미만 저장방식과 Android 10 이후 저장방식을 모두 준비하고 모드에 따라 분기를 태워야 할 가능성도 있을 것 같습니다. 앱에 따라서는 굉장히 혼란스러울 수도 있을 것 같습니다.

정리

지금까지 Android 10에서 발표된 외부저장소의 변화, Storage Scope에 대해 알아보았습니다. 변화된 외부 저장소에 맞추어, 이제 우리 앱에서 만든 파일을 다른 앱들에 노출하기 위해서는 MediaStore를 사용하거나 Storage Access Framework를 사용해야 합니다. 다음 포스트에서는 Android 10에서 변경된 MediaStore에 대해서 알아보도록 하겠습니다.

이 포스트는 GDG Android Korea에서 주최한 I/O 19 Extended in Korea Android 에서 2019년 6월 15일 발표한 What’s New in Shared Storage의 발표내용을 토대로 작성하였습니다.

본문 내용 중 잘못 된 부분이 있거나, 최근 변경되었으나 반영되지 않은 사항이 있다면, 정정할 수 있도록 말씀해주시면 너무너무 감사하겠습니다.

Reference

--

--