안드로이드 파일 시스템
안드로이드 파일 시스템
안드로이드 파일 시스템은 리눅스 커널 위에 구축된 구조화된 환경으로, 디바이스 전체의 데이터 저장을 관리하고 체계적으로 구성합니다. 각 애플리케이션에 격리된 저장 공간을 제공하고, 사용자 콘텐츠를 위한 공유 디렉터리와 운영 체제 자체를 위한 보호된 파티션(partition)을 함께 관리하는 것이 핵심입니다. 안드로이드 앱 개발에서 파일 시스템의 구조를 정확히 이해하는 것은 보안, 성능, 사용자 경험 모든 측면에서 매우 중요합니다. 이 챕터를 학습하고 나면 다음 내용을 이해하실 수 있습니다.
- 안드로이드 파일 시스템의 주요 파티션과 디렉터리 구조를 파악할 수 있습니다.
- 내부 저장소(internal storage)와 외부 저장소(external storage)의 차이점을 설명할 수 있습니다.
- 범위 지정 저장소(scoped storage)가 공유 파일에 대한 직접 접근을 어떻게 제한하는지 이해할 수 있습니다.
- 주어진 사용 사례에 적합한 저장소 API를 올바르게 선택할 수 있습니다.
파티션과 디렉터리 구조
안드로이드는 파일 시스템을 여러 핵심 파티션으로 구성하며, 각 파티션은 고유한 역할을 담당합니다.
- 시스템 파티션 (
/system): 운영 체제, 프레임워크 라이브러리, 시스템 앱, 설정 파일이 포함됩니다. 이 파티션은 무단 수정을 방지하기 위해 읽기 전용(read-only)으로 마운트됩니다. - 데이터 파티션 (
/data): 모든 애플리케이션 데이터를 저장합니다. 각 앱은/data/data/<패키지명>경로에 전용 디렉터리를 가지며, 해당 앱만 접근할 수 있습니다. 이러한 접근 제한은 앱의 UID에 연결된 리눅스 파일 권한을 통해 커널 수준에서 강제됩니다. - 캐시 파티션 (
/cache): OTA 업데이트 파일과 같은 임시 데이터를 보관합니다. 공장 초기화 시 이 파티션의 내용은 유지되지 않습니다. - 벤더 파티션 (
/vendor): 디바이스 제조사가 제공하는 하드웨어 전용 라이브러리, 펌웨어, 설정 파일이 포함됩니다./system과 마찬가지로 읽기 전용입니다. - 외부 저장소 (
/sdcard또는/storage): 여러 앱이 접근할 수 있는 공유 저장 공간을 제공합니다. 이름과 달리, 최신 디바이스에서는 탈착식 SD 카드가 아닌 내장 플래시 메모리를 사용하는 경우가 대부분입니다.
멀티 유저 프로필을 지원하는 디바이스에서는 각 사용자별로 별도의 /data/user/<userId> 디렉터리가 생성됩니다. Context 메서드를 사용하면 올바른 사용자 디렉터리로 자동 해석(resolve)되므로, 앱에서 멀티 유저 경로를 직접 처리할 필요가 없습니다.
내부 저장소
내부 저장소(internal storage)는 /data/data/<패키지명> 하위에 각 애플리케이션에 할당된 전용 디렉터리를 의미합니다. 이 디렉터리에 저장된 파일은 해당 앱만 접근할 수 있으며, 다른 앱에서는 경로를 알더라도 읽을 수 없습니다. 안드로이드 프레임워크는 이 공간을 다루기 위한 Context 메서드를 제공합니다.
// 내부 저장소에 파일 쓰기
val file = File(context.filesDir, "config.json")
file.writeText("""{"theme": "dark"}""")
// 파일 읽기
val content = file.readText()
filesDir에 저장된 파일은 앱이 삭제되거나 사용자가 앱 데이터를 초기화할 때까지 유지됩니다. 임시 파일이 필요한 경우에는 cacheDir을 사용하면 되는데, 저장 공간이 부족해지면 시스템이 이 디렉터리의 파일을 자동으로 삭제할 수 있습니다. 한편, noBackupFilesDir 디렉터리는 디바이스 고유 토큰이나 등록 ID처럼 로컬에는 유지해야 하지만 Auto Backup에는 포함하고 싶지 않은 파일을 저장하기에 적합합니다.
내부 저장소는 디바이스의 내장 플래시 메모리에 위치하므로 항상 사용 가능합니다. 별도의 런타임 권한도 필요하지 않으며, 사용자가 앱을 삭제하거나 설정에서 데이터를 초기화하면 해당 데이터가 자동으로 제거됩니다.
단순한 읽기/쓰기 작업에는 context.openFileOutput()과 context.openFileInput()을 사용할 수 있습니다. 구조화된 데이터의 경우에는 Room이나 DataStore가 더 적합한데, 동시성 처리, 마이그레이션, 반응형 업데이트를 기본적으로 지원하기 때문입니다. SharedPreferences는 간단한 키-값 쌍에 적합하지만, 메인 스레드에서 동기식 디스크 I/O가 발생하는 문제와 트랜잭션 보장이 부족하다는 한계가 있으며, DataStore는 이러한 문제를 해결하도록 설계되었습니다.