
안드로이드의 파일 시스템은 리눅스 기반으로 구성되어 있으며, 앱이 데이터를 저장하고 접근하는 방식을 정의한다. 일반적인 리눅스와 비슷하지만 모바일 환경에 특화된 구조와 보안 메커니즘을 가지고 있다.
내부 저장소는 앱 전용 저장 공간이다. 다른 앱이나 사용자가 직접 접근할 수 없으며, 앱이 삭제되면 함께 삭제된다.
경로: /data/data/패키지명/
주요 특징:
// 파일 쓰기
val filename = "myfile.txt"
val fileContents = "Hello World"
context.openFileOutput(filename, Context.MODE_PRIVATE).use {
it.write(fileContents.toByteArray())
}
// 파일 읽기
context.openFileInput(filename).bufferedReader().useLines { lines ->
lines.forEach { println(it) }
}
// 파일 경로 가져오기
val file = File(context.filesDir, filename)
// 캐시 디렉토리
val cacheFile = File(context.cacheDir, "temp.txt")
내부 저장소 구조:
/data/data/패키지명/
├── files/ # 일반 파일 (context.filesDir)
├── cache/ # 임시 파일 (context.cacheDir)
├── databases/ # SQLite 데이터베이스
└── shared_prefs/ # SharedPreferences XML 파일
외부 저장소는 공유 가능한 저장 공간이다. 과거에는 실제 SD카드를 의미했지만, 현재는 대부분 기기에서 내장 메모리의 일부를 외부 저장소로 사용한다.
경로: /sdcard/Android/data/패키지명/
주요 특징:
// 앱 전용 외부 디렉토리
val appExternalDir = context.getExternalFilesDir(null)
val file = File(appExternalDir, "document.pdf")
// 특정 타입별 디렉토리
val picturesDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val musicDir = context.getExternalFilesDir(Environment.DIRECTORY_MUSIC)
val documentsDir = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
// 외부 캐시
val externalCache = context.externalCacheDir
경로: /sdcard/Pictures/, /sdcard/Documents/ 등
주요 특징:
Android 10부터는 개인정보 보호를 위해 Scoped Storage가 도입되었다. 앱이 외부 저장소에 무분별하게 접근하는 것을 방지한다.
주요 변경사항:
// MediaStore로 이미지 저장 (Android 10+)
val resolver = context.contentResolver
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "photo.jpg")
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
}
val imageUri = resolver.insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
)
imageUri?.let { uri ->
resolver.openOutputStream(uri)?.use { outputStream ->
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
}
}
사용자가 직접 파일을 선택하거나 저장 위치를 지정할 수 있게 하는 시스템이다. 클라우드 저장소도 지원한다.
// 파일 선택
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/pdf"
}
startActivityForResult(intent, REQUEST_CODE_OPEN_FILE)
// 파일 생성
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "text/plain"
putExtra(Intent.EXTRA_TITLE, "newfile.txt")
}
startActivityForResult(intent, REQUEST_CODE_CREATE_FILE)
// 결과 처리
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_OPEN_FILE && resultCode == RESULT_OK) {
data?.data?.let { uri ->
contentResolver.openInputStream(uri)?.use { inputStream ->
// 파일 읽기
val text = inputStream.bufferedReader().readText()
}
}
}
}
안드로이드 운영체제의 핵심 파일들이 저장된 읽기 전용 파티션이다.
/system/
├── app/ # 시스템 앱 (설정, 전화, 메시지 등)
├── priv-app/ # 권한이 높은 시스템 앱
├── framework/ # 안드로이드 프레임워크 JAR 파일
├── lib/ # 시스템 공유 라이브러리 (.so 파일)
├── bin/ # 실행 파일 (sh, dalvikvm 등)
├── xbin/ # 추가 실행 파일
├── fonts/ # 시스템 폰트
├── media/ # 부팅음, 알림음 등
├── usr/ # 사용자 데이터 (키보드 레이아웃 등)
└── build.prop # 시스템 속성 설정 파일
특징:
// build.prop 속성 읽기
val manufacturer = Build.MANUFACTURER // 제조사
val model = Build.MODEL // 모델명
val androidVersion = Build.VERSION.RELEASE // 안드로이드 버전
사용자 데이터와 앱 데이터가 저장되는 파티션이다. 실제로 앱이 주로 사용하는 공간이다.
/data/
├── data/ # 앱별 데이터 디렉토리
│ └── 패키지명/
├── app/ # 설치된 앱 APK
├── dalvik-cache/ # DEX 최적화 파일
├── system/ # 시스템 데이터
│ ├── packages.xml # 설치된 패키지 정보
│ └── users/ # 사용자별 데이터
├── local/ # 로컬 소켓
└── media/ # 미디어 데이터베이스
시스템 캐시 파티션이다. OTA 업데이트 파일이나 복구 로그가 임시로 저장된다.
특징:
임시 파일을 저장하는 디렉토리이다. 리눅스의 tmpfs로 RAM에 마운트된다.
특징:
일반 앱에서는 /tmp 대신 앱의 캐시 디렉토리를 사용해야 한다:
val tempFile = File(context.cacheDir, "temp_file.tmp")
tempFile.writeText("임시 데이터")
// 사용 후 삭제
tempFile.delete()
/ # 루트 파일 시스템 (rootfs)
├── /system # 운영체제 (읽기 전용)
├── /vendor # 하드웨어 관련 (읽기 전용)
├── /data # 사용자/앱 데이터 (읽기/쓰기)
├── /cache # 시스템 캐시 (읽기/쓰기)
├── /sdcard # 외부 저장소 심볼릭 링크
├── /proc # 프로세스 정보 (가상)
├── /sys # 시스템/장치 정보 (가상)
├── /dev # 장치 파일 (가상)
├── /tmp # 임시 파일 (tmpfs, 메모리 기반)
└── /mnt # 마운트 포인트
상황에 따라 적절한 저장소를 선택해야 한다.
안드로이드 버전에 따라 저장소 권한이 달라진다.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Scoped Storage 도입. 레거시 모드를 사용하려면:
<application android:requestLegacyExternalStorage="true">
<!-- 모든 파일 접근 (특별한 경우만) -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (!Environment.isExternalStorageManager()) {
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
intent.data = Uri.parse("package:${packageName}")
startActivity(intent)
}
}
세분화된 미디어 권한:
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_VIDEO
),
REQUEST_MEDIA
)
}