모바일 기기의 외부 저장소는 적절한 권한이 있는 모든 애플리케이션이 액세스할 수 있는 이동식 데이터 매체입니다. Android Open Source Project, 2026에 따르면, Android 10부터 Google은 Scoped Storage를 도입했습니다. 이는 파일 시스템에 대한 직접 액세스를 제한하고 공유 미디어 파일 작업을 위해 MediaStore API 사용을 요구하는 모델입니다. 이 접근 방식은 사용자 데이터의 보안을 강화하고 애플리케이션 간 정보 유출을 방지합니다.
핵심 사항
READ_EXTERNAL_STORAGE 및 WRITE_EXTERNAL_STORAGE는 Android 9 이하에서 필수입니다외부 저장소는 애플리케이션의 보호된 샌드박스의 일부가 아니며 다른 프로그램이 액세스할 수 있는 메모리 영역입니다. Android에서 이 용어는 일반적으로 SD 카드를 의미하지만, 외부 저장소에는 OTG 어댑터를 통해 연결된 USB 드라이브와 파일 시스템 수준에서 통합된 클라우드 드라이브도 포함됩니다. 내부 저장소와의 주요 차이점은 격리 부족입니다. 적절한 권한이 있는 모든 애플리케이션이 외부 저장소의 파일을 읽고 수정할 수 있습니다.
Android에서 외부 저장소에는 두 가지 주요 모드가 있습니다: 휴대용과 채택 가능(adoptable)입니다. SD 카드를 휴대용 저장소로 포맷하면 이동식으로 유지되며 FAT32 또는 exFAT로 포맷됩니다. 채택 가능 저장소 모드에서는 카드가 ext4로 포맷되고 AES-128을 사용하여 암호화되며, 시스템은 이를 내부 메모리의 확장으로 처리합니다. 채택 가능 모드는 Android 6.0 Marshmallow부터 사용 가능하지만, 기기 제조업체는 종종 펌웨어에서 이 기능을 비활성화합니다. Google Play Console 통계에 따르면 약 12%의 Android 기기만이 채택 가능 저장소를 지원하므로, SD 카드를 통한 내부 메모리 확장에만 의존하는 것은 권장되지 않습니다.
iOS에는 기존의 SD 카드가 없습니다. Apple은 모든 저장소가 내부 플래시 메모리와 iCloud를 기반으로 구축된 폐쇄형 아키텍처를 사용합니다. 외부 드라이브는 Lightning 또는 USB-C 포트를 통해 지원되지만, 이에 대한 액세스는 시스템 파일 관리자 Files 및 Document Picker와 통합된 애플리케이션으로 제한됩니다. 사용자는 외부 소스에서 열 파일을 결정합니다 — 애플리케이션은 명시적 동의 없이 연결된 드라이브를 스캔할 수 없습니다.
외부 미디어 선택은 속도, 용량 및 휴대성 요구 사항에 따라 다릅니다. 다양한 미디어 유형은 모바일 애플리케이션에서 서로 다른 속도 특성, 파일 시스템 및 사용 사례를 가지고 있습니다.
| 미디어 유형 | 최대 용량 | 읽기 속도 | 파일 시스템 |
|---|---|---|---|
| SD UHS-I | 2 TB | 최대 104 MB/s | exFAT / FAT32 |
| SD UHS-II | 2 TB | 최대 312 MB/s | exFAT / FAT32 |
| USB 3.0 OTG | 2 TB | 최대 400 MB/s | exFAT / NTFS / FAT32 |
| USB-C SSD | 4 TB | 최대 1000 MB/s | exFAT / APFS / NTFS |
SD 카드는 쓰기 속도에 따라 분류됩니다: Class 10(10 MB/s), U1(10 MB/s), U3(30 MB/s), V30(30 MB/s) 및 V90(90 MB/s). 4K 비디오 녹화에는 최소 U3 또는 V30 카드가 필요합니다. 대용량 미디어 파일(사진 처리, 비디오 편집)을 다루는 모바일 애플리케이션의 경우 U3 클래스 이상의 카드를 사용하는 것이 좋습니다. 카드 속도는 애플리케이션 성능에 직접적인 영향을 미칩니다: Class 10 카드에서 4K 비디오를 녹화하면 대역폭 부족으로 프레임 드롭이 발생할 수 있습니다. 애플리케이션용 SD 카드를 선택할 때는 속도 클래스뿐만 아니라 애플리케이션 성능 클래스 등급에도 주의하세요: A1 및 A2는 데이터베이스 및 애플리케이션 캐시 작업에 중요한 최소 임의 읽기 및 쓰기 성능을 나타냅니다.
USB-OTG(On-The-Go)는 어댑터를 통해 모바일 기기에 외부 USB 드라이브를 연결할 수 있게 합니다. Android는 버전 3.1부터 OTG를 지원하지만, 드라이브를 마운트하려면 파일 시스템을 지원하는 커널이 필요합니다. FAT32와 exFAT는 기본적으로 지원되며, NTFS는 사용자 지정 커널이 있는 기기 또는 Paragon 라이브러리를 통해서만 지원됩니다. iOS에서는 USB 드라이브 지원이 iOS 13의 Files 앱과 함께 도입되었습니다. 드라이브로 작업하려면 애플리케이션이 UIDocumentPickerViewController를 사용해야 하며, 사용자가 선택한 파일에만 액세스할 수 있습니다 — iOS는 드라이브의 파일 시스템에 대한 전체 액세스를 허용하지 않습니다.
클라우드 서비스 — iCloud, Google Drive, Dropbox — 는 시스템 제공자를 통해 기기의 파일 시스템에 통합될 수 있습니다. iOS에서 iCloud Drive는 애플리케이션의 Sandbox 컨테이너의 일부이며 파일은 사용자의 기기 간에 자동으로 동기화됩니다. Android에서 Google Drive는 파일 읽기 및 쓰기를 위한 API를 제공하지만 파일 시스템에 직접 마운트되지는 않습니다. 대용량 데이터를 다루는 모바일 애플리케이션의 경우 클라우드 저장소는 SD 카드의 대안이 될 수 있으며, 특히 확장 슬롯이 없는 기기에서 유용합니다.
Android에서 외부 저장소 작업은 운영 체제 버전과 Scoped Storage 모델에 따라 다릅니다. 파일 액세스 프로세스는 Android 9 이하, Android 10–12 및 Android 13+에서 다릅니다.
Android 10부터 공유 미디어 파일에 액세스하는 기본 방법은 MediaStore API입니다. 이 API는 공유 저장소에서 이미지, 비디오 및 오디오 파일을 읽고 쓰기 위한 통합 인터페이스를 제공합니다. 애플리케이션은 자체 파일에 액세스하기 위해 READ_EXTERNAL_STORAGE 권한이 필요하지 않지만, 다른 애플리케이션의 파일을 읽으려면 여전히 명시적인 사용자 권한이 필요합니다. MediaStore는 미디어 파일을 자동으로 인덱싱하고 쿼리를 위한 ContentResolver를 제공합니다.
val collection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
val projection = arrayOf(
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DISPLAY_NAME
)
val cursor = contentResolver.query(collection, projection, null, null, null)
쿼리를 실행하면 파일의 URI가 포함된 Cursor를 받게 됩니다. 파일을 읽으려면 contentResolver.openInputStream(uri)를 통해 InputStream을 엽니다. 쓰기도 contentResolver.openOutputStream(uri)를 통해 유사하게 수행됩니다. MediaStore는 이름 충돌을 자동으로 처리하며 contentResolver.insert(uri, values)를 통해 새 파일을 삽입하는 기능을 제공하여 생성된 파일의 URI를 반환합니다.
Storage Access Framework(SAF)는 Android 10+에서 임의 파일에 액세스하는 권장 방법입니다. SAF는 시스템 파일 선택기 대화 상자를 제공하여 사용자가 애플리케이션에 특정 파일 또는 디렉토리에 대한 액세스 권한을 부여합니다. 애플리케이션은 기기가 재부팅되거나 설정에서 권한이 명시적으로 취소될 때까지 유효한 액세스 URI를 받습니다. SAF는 READ_EXTERNAL_STORAGE 또는 WRITE_EXTERNAL_STORAGE 권한이 필요하지 않으므로 Google Play 게시가 간소화되고 권한 요청 수가 줄어듭니다.
val requestCode = 42
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION
or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
startActivityForResult(intent, requestCode)
iOS에서 외부 파일 작업은 UIDocumentPickerViewController를 기반으로 구축되며, 사용자가 특정 파일 또는 디렉토리를 선택하기 위해 엽니다. 애플리케이션은 외부 드라이브의 파일 시스템에 직접 액세스할 수 없습니다 — 사용자가 명시적으로 선택한 파일에만 액세스할 수 있습니다. 파일 선택 후 애플리케이션은 Sandbox 컨테이너의 임시 URL을 받고 FileManager를 통해 파일로 작업할 수 있습니다.
UIDocumentPickerViewController를 사용하면 사용자가 iCloud Drive, 연결된 USB 드라이브 또는 타사 클라우드 서비스 등 사용 가능한 소스에서 하나 이상의 파일을 선택할 수 있습니다. 선택 후 컨트롤러는 애플리케이션이 세션 중에 읽을 수 있는 URL 배열을 반환합니다. 애플리케이션 종료 후에도 액세스를 유지하려면 startAccessingSecurityScopedResource()를 호출하고 UserDefaults에 보안 범위 북마크를 저장해야 합니다.
let picker = UIDocumentPickerViewController(
forOpeningContentTypes: [.data, .image],
asCopy: true
)
picker.delegate = self
func documentPicker(
controller: UIDocumentPickerViewController,
didPickDocumentsAt urls: [URL]
) {
guard let url = urls.first else { return }
let data = try Data(contentsOf: url)
}
asCopy: true 매개변수는 iOS가 파일을 애플리케이션의 Sandbox 컨테이너에 복사하고 애플리케이션이 원본 파일과 독립적인 자체 복사본을 얻음을 의미합니다. asCopy: false를 지정하면 애플리케이션이 원본 파일에 대한 참조를 받지만 액세스하려면 url.startAccessingSecurityScopedResource()를 호출해야 합니다. 이 호출 없이 파일을 읽으면 예외가 발생합니다. 파일 작업을 마친 후에는 항상 url.stopAccessingSecurityScopedResource()를 호출하여 리소스를 해제하고 설명자 누수를 방지하세요.
외부 저장소의 보안은 파일이 다른 애플리케이션에서 액세스할 수 있기 때문에 내부 저장소보다 낮습니다. READ_EXTERNAL_STORAGE 권한이 있는 모든 애플리케이션은 SD 카드의 모든 파일을 읽을 수 있으며, 여기에는 애플리케이션이 민감한 데이터를 임시로 저장하기 위해 생성한 파일도 포함됩니다. 악성 애플리케이션이 SD 카드를 스캔하고 암호화되지 않은 사용자 데이터를 추출할 수 있습니다. 따라서 인증 토큰, 비밀번호 또는 사용자의 개인 데이터를 외부 저장소에 일반 텍스트로 저장하는 것은 강력히 권장되지 않습니다.
Android 10+에서 Scoped Storage는 공유 파일 시스템에 대한 애플리케이션의 액세스를 크게 제한합니다. 애플리케이션은 권한 없이 자체적으로 생성한 파일만 읽을 수 있으며, 다른 애플리케이션의 파일에 액세스하려면 SAF 대화 상자를 통한 명시적인 사용자 동의가 필요합니다. 그러나 이 제한은 대상 SDK 버전이 29 미만인 애플리케이션에는 적용되지 않습니다 — 이전 모델에서 계속 작동하여 애플리케이션을 업데이트하지 않은 사용자에게 위험을 초래합니다. Google Play는 2021년부터 모든 신규 및 업데이트된 애플리케이션에 targetSdkVersion 29+를 요구합니다.
포맷 및 암호화는 보안의 또 다른 중요한 측면입니다. 휴대용 저장소로 포맷된 SD 카드는 기본적으로 시스템에 의해 암호화되지 않습니다. 채택 가능 저장소를 사용하는 경우에도 기기 제조업체가 펌웨어에서 이 기능을 활성화한 경우에만 암호화가 활성화됩니다. 외부 저장소에 저장된 민감한 데이터를 보호하려면 AndroidX Security의 EncryptedFile 라이브러리를 사용하세요. 이 라이브러리는 시스템 암호화와 관계없이 AES256-GCM을 사용하여 각 파일을 개별적으로 암호화합니다.
외부 저장소의 효과적인 사용은 데이터 가용성과 보안 간의 균형이 필요합니다. 사용자 파일의 안정적인 저장을 보장하려면 다음 규칙을 따르세요.
Environment.getExternalStorageState()를 사용하여 SD 카드 상태를 확인합니다. 카드가 제거되었거나, 손상되었거나, 쓰기 불가능할 수 있습니다외부 저장소는 장애 시 데이터 무결성을 보장하지 않는다는 점을 기억하는 것이 중요합니다. SD 카드는 특히 Class 4 및 Class 6의 저가형 모델에서 제한된 덮어쓰기 주기를 가지고 있습니다. 집중적인 쓰기(예: 로깅 또는 스트리밍 데이터 캐싱)의 경우 카드가 몇 개월 내에 고장날 수 있습니다. 이러한 시나리오에서는 내부 메모리 또는 클라우드 저장소를 사용하고 사용자가 손실을 수용할 수 있다고 생각하는 파일만 외부 저장소에 저장하세요. StatFs를 통해 정기적으로 SD 카드 상태를 확인하고 중요한 파일 시스템 오류를 사용자에게 알리세요.
자주 묻는 질문
Android 13+에서는 READ_EXTERNAL_STORAGE 권한이 더 세분화된 권한으로 대체되었습니다: READ_MEDIA_IMAGES, READ_MEDIA_VIDEO 및 READ_MEDIA_AUDIO. 다른 파일 유형의 경우 SAF를 사용하세요.
네, 채택 가능 저장소(adoptable storage) 기능은 Android 6.0+에서 사용 가능합니다. 카드는 AES-128 암호화와 함께 ext4로 포맷됩니다. 그러나 많은 제조업체가 이 옵션을 비활성화하며 약 88%의 기기가 채택 가능 저장소를 지원하지 않습니다.
Environment.getExternalStorageState() 메서드를 사용합니다. 이 메서드는 상태 문자열을 반환합니다. MEDIA_MOUNTED 값은 카드를 읽고 쓸 수 있음을 의미합니다. 다른 값은 오류 또는 카드 없음을 나타냅니다.
Scoped Storage는 애플리케이션을 자체 컨테이너와 공유 MediaStore 미디어 갤러리로 제한합니다. 전체 액세스는 기기의 모든 파일을 읽을 수 있도록 허용합니다. Scoped Storage는 사용자 데이터의 보안을 향상시키며 새 애플리케이션에 필수입니다.
ACTION_OPEN_DOCUMENT_TREE 작업으로 Intent를 실행합니다. 그러면 시스템 디렉토리 선택기 대화 상자가 열립니다. 선택 후 재부팅까지 유효한 URI 권한을 받습니다. 나중에 사용하기 위해 이 URI를 SharedPreferences에 저장하세요.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.