안드로이드 파일 시스템이란?

aufcl4858·2025년 10월 11일
post-thumbnail

안드로이드의 파일 시스템은 리눅스 기반으로 구성되어 있으며, 앱이 데이터를 저장하고 접근하는 방식을 정의한다. 일반적인 리눅스와 비슷하지만 모바일 환경에 특화된 구조와 보안 메커니즘을 가지고 있다.

안드로이드 저장소의 종류

내부 저장소 (Internal Storage)

내부 저장소는 앱 전용 저장 공간이다. 다른 앱이나 사용자가 직접 접근할 수 없으며, 앱이 삭제되면 함께 삭제된다.

경로: /data/data/패키지명/

주요 특징:

  • 앱 삭제 시 자동으로 삭제된다
  • 항상 사용 가능하다 (SD카드처럼 마운트 해제되지 않는다)
  • 별도의 권한이 필요 없다
  • 보안이 중요한 데이터 저장에 적합하다
// 파일 쓰기
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 파일

외부 저장소 (External Storage)

외부 저장소는 공유 가능한 저장 공간이다. 과거에는 실제 SD카드를 의미했지만, 현재는 대부분 기기에서 내장 메모리의 일부를 외부 저장소로 사용한다.

앱 전용 외부 저장소

경로: /sdcard/Android/data/패키지명/

주요 특징:

  • Android 10(API 29) 이상에서는 권한이 필요 없다
  • 앱 삭제 시 자동으로 삭제된다
  • Android 10 미만에서는 다른 앱이 접근할 수 있다
// 앱 전용 외부 디렉토리
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 사용)

Scoped Storage (Android 10+)

Android 10부터는 개인정보 보호를 위해 Scoped Storage가 도입되었다. 앱이 외부 저장소에 무분별하게 접근하는 것을 방지한다.

주요 변경사항:

  • 앱은 자신의 외부 저장소 디렉토리에만 자유롭게 접근할 수 있다
  • 다른 앱의 파일이나 공유 저장소는 MediaStore API나 SAF를 사용해야 한다
  • 사진의 위치 정보 같은 민감한 메타데이터 접근이 제한된다
// 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)
    }
}

Storage Access Framework (SAF)

사용자가 직접 파일을 선택하거나 저장 위치를 지정할 수 있게 하는 시스템이다. 클라우드 저장소도 지원한다.

// 파일 선택
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 파티션

안드로이드 운영체제의 핵심 파일들이 저장된 읽기 전용 파티션이다.

/system/
├── app/              # 시스템 앱 (설정, 전화, 메시지 등)
├── priv-app/         # 권한이 높은 시스템 앱
├── framework/        # 안드로이드 프레임워크 JAR 파일
├── lib/              # 시스템 공유 라이브러리 (.so 파일)
├── bin/              # 실행 파일 (sh, dalvikvm 등)
├── xbin/             # 추가 실행 파일
├── fonts/            # 시스템 폰트
├── media/            # 부팅음, 알림음 등
├── usr/              # 사용자 데이터 (키보드 레이아웃 등)
└── build.prop        # 시스템 속성 설정 파일

특징:

  • 읽기 전용이다 (루팅하지 않으면 수정 불가)
  • OTA 업데이트 시 변경된다
  • 공장 초기화 시에도 유지된다
  • 일반 앱은 접근할 수 없다
// build.prop 속성 읽기
val manufacturer = Build.MANUFACTURER  // 제조사
val model = Build.MODEL                // 모델명
val androidVersion = Build.VERSION.RELEASE  // 안드로이드 버전

/data 파티션

사용자 데이터와 앱 데이터가 저장되는 파티션이다. 실제로 앱이 주로 사용하는 공간이다.

/data/
├── data/             # 앱별 데이터 디렉토리
│   └── 패키지명/
├── app/              # 설치된 앱 APK
├── dalvik-cache/     # DEX 최적화 파일
├── system/           # 시스템 데이터
│   ├── packages.xml  # 설치된 패키지 정보
│   └── users/        # 사용자별 데이터
├── local/            # 로컬 소켓
└── media/            # 미디어 데이터베이스

/cache 파티션

시스템 캐시 파티션이다. OTA 업데이트 파일이나 복구 로그가 임시로 저장된다.

특징:

  • 일반 앱은 접근할 수 없다
  • 공간이 부족하면 시스템이 자동으로 정리한다
  • 복구 모드에서 캐시 파티션 삭제가 가능하다 (안전한 작업)

/tmp 디렉토리

임시 파일을 저장하는 디렉토리이다. 리눅스의 tmpfs로 RAM에 마운트된다.

특징:

  • 메모리 기반이다 (재부팅 시 모두 삭제됨)
  • 시스템 프로세스와 루트 권한 앱만 접근 가능하다
  • 일반 앱은 직접 접근할 수 없다
  • APK 설치 과정이나 시스템 작업에 사용된다

일반 앱에서는 /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             # 마운트 포인트

저장소 선택 가이드

상황에 따라 적절한 저장소를 선택해야 한다.

내부 저장소를 사용해야 할 때

  • 인증 토큰, 비밀번호 같은 민감한 정보
  • 앱 설정 파일
  • 다른 앱과 공유할 필요 없는 데이터
  • 크기가 작은 파일

앱 전용 외부 저장소를 사용해야 할 때

  • 큰 용량의 미디어 파일
  • 앱 삭제 시 함께 삭제되어야 하는 데이터
  • 백업 파일

공유 외부 저장소를 사용해야 할 때

  • 사용자가 다른 앱에서도 사용할 파일 (사진, 문서 등)
  • 앱 삭제 후에도 유지되어야 하는 파일
  • 사용자가 다운로드한 파일

SAF를 사용해야 할 때

  • 사용자가 직접 파일을 선택해야 하는 경우
  • 클라우드 저장소 지원이 필요한 경우
  • 다른 앱의 데이터에 접근해야 하는 경우

저장소 권한

안드로이드 버전에 따라 저장소 권한이 달라진다.

Android 9 이하

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Android 10

Scoped Storage 도입. 레거시 모드를 사용하려면:

<application android:requestLegacyExternalStorage="true">

Android 11-12

<!-- 모든 파일 접근 (특별한 경우만) -->
<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)
    }
}

Android 13+

세분화된 미디어 권한:

<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
    )
}
profile
데브누누

0개의 댓글