구분raw 폴더앱 전용 외부 메모리공유 외부 메모리
위치/res/raw/Android/data/패키지명/files/sdcard
클래스InputStreamFileInputStreamFileInputStream
접근 메소드openRawResource()getExternalFilesDir(null)System.getenv("EXTERNAL_STORAGE")
권한 필요❌ (앱 전용)✅ (READ/WRITE/MANAGE)
쓰기 가능❌ (읽기 전용)
공유 여부앱 내부앱 전용여러 앱 공유

1. 읽기 전용 파일 (raw 폴더 활용)

  • 개발 PC에서 미리 작성한 텍스트 파일(사용 약관 등)을 앱에 포함시킬 때 사용한다.

  • 파일은 /res/raw 폴더에 저장한다.

  • 한글 손상 방지를 위해 인코딩을 UTF-8로 설정한다.

  • 접근: resources.openRawResource(R.raw.파일명)

  • 반환: InputStream (FileInputStream이 아님)

  • 읽어올 데이터의 크기: inputStream.available()

val inputStream = resources.openRawResource(R.raw.raw_test)
 
val txt = ByteArray(inputStream.available())
 
inputStream.read(txt)
binding.tvRaw.text = txt.toString(Charsets.UTF_8)
inputStream.close()

2. 외부 메모리

  • 음악·영상·사진처럼 여러 앱이 공유해 사용하는 파일을 저장하는 공간으로, 내장 메모리보다 큰 저장 공간을 쓸 수 있다.

2.1. 권한 설정

  • AndroidManifest.xmlREAD/WRITE/MANAGE_EXTERNAL_STORAGE 퍼미션을 추가하고, <application>requestLegacyExternalStorage="true" 속성을 넣는다.
<uses-permission
	android:name="android.permission.MANAGE_EXTERNAL_STORAGE" 
	tools:ignore="ScopedStorage" />
 
<application
	android:requestLegacyExternalStorage="true">
  • MainActivity.kt 코드에서는 SDK 버전(Android R 이상/미만)에 따라 분기하여 권한을 요청한다.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    if (!Environment.isExternalStorageManager()) {
        val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
        this.startActivity(intent)
    }
} else {
    val permissions = arrayOf(
        android.Manifest.permission.READ_EXTERNAL_STORAGE,
        android.Manifest.permission.WRITE_EXTERNAL_STORAGE
    )
    ActivityCompat.requestPermissions(this, permissions, 1001)
}

2.2. 경로 확인 방법

앱 전용 외부 메모리공유 외부 메모리로 구분된다.

  • 앱 전용 경로: getExternalFilesDir(null)

    • → 예: /storage/emulated/0/Android/data/패키지명/files
  • 공유 경로: System.getenv("EXTERNAL_STORAGE")

val externalDir = System.getenv("EXTERNAL_STORAGE")
// 결과: /sdcard

2.3. 파일 읽기

경로만 지정하는 점을 빼면 raw 파일 처리와 거의 동일하며, FileInputStream(경로)을 사용한다.

val fileInputStream = FileInputStream(externalDir + "/exmem.txt")

2.4. 디렉터리 생성/삭제

  • File 객체의
    • mkdir()(생성), delete()(삭제) 사용
    • mkdirs()mkdir -p처럼 중간 경로까지 한 번에 생성
val externalDir = System.getenv("EXTERNAL_STORAGE") + "/test_dir"
val pathDir = File("$externalDir/test")
 
binding.btnMkdir.setOnClickListener {
    pathDir.mkdir()      // mkdirs()는 mkdir -p 와 동일 (중간 경로까지 생성)
}
binding.btnRmdir.setOnClickListener {
    pathDir.delete()
}

2.5. 폴더/파일 목록 확인

  • File(경로).listFiles()가 파일 목록을 배열로 반환한다.
var sysFiles = File(sysDir).listFiles()
  • isDirectory / isFile로 폴더와 파일을 구분한다.
for (i in sysFiles.indices) {
	if (sysFiles[i].isDirectory == true)
		strFileName = "<폴더> " + sysFiles[i].toString()
	else
		strFileName = "<파일> " + sysFiles[i].toString()
}
  • 시스템 폴더 루트는 Environment.getRootDirectory()(=/system)로 얻는다.
val sysDir = Environment.getRootDirectory().absolutePath // /system

3. 실습: 간단 이미지 뷰어

외부 저장소(/sdcard/test_dir)의 이미지를 버튼으로 넘겨 보는 앱이다.

  • 핵심 로직: listFiles()로 폴더 내 파일을 가져온 뒤, isFile이면서 파일명이 "renoir"로 시작하는 것만 리스트에 추가
  • 이미지 표시는 BitmapFactory.decodeFile(경로)로 비트맵을 만들어 ImageView에 설정
  • “이전/다음 그림” 버튼은 인덱스(curNum)를 ±1 하고 abs(curNum % 10)로 순환시켜 처리