이 튜토리얼에서는 SD 카드(외부 저장소)의 폴더나 파일에 데이터를 기록할 수 있도록 안드로이드 권한을 설정하는 방법을 단계별로 살펴봅니다. 외부 저장소 접근의 핵심은 매니페스트에 저장소 권한을 선언하고, 저장소 상태(마운트 여부·읽기 전용 여부)를 확인한 뒤 파일 입출력을 수행하는 것입니다.
1단계: 새 프로젝트 만들기
Android Studio에서 File → New Project 메뉴로 이동한 뒤, 프로젝트 생성에 필요한 모든 정보를 입력하여 새 프로젝트를 만듭니다.
2단계: 레이아웃 작성 (res/layout/activity_main.xml)
텍스트를 입력할 수 있는 입력창과 저장(SAVE)·읽기(READ) 버튼, 결과를 표시하는 TextView로 화면을 구성합니다. 아래 코드를 res/layout/activity_main.xml 파일에 추가하세요.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="12dp"
tools:context=".MainActivity">
<TextView
android:textStyle="bold"
android:textAlignment="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Reading and Writing to External Storage"
android:textSize="24sp" />
<EditText
android:layout_marginTop="30dp"
android:id="@+id/myInputText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:gravity="top|start"
android:lines="5"
android:minLines="3">
</EditText>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:orientation="horizontal"
android:weightSum="1.0">
<Button
android:id="@+id/saveExternalStorage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="SAVE" />
<Button
android:id="@+id/getExternalStorage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="READ" />
</LinearLayout>
<TextView
android:layout_marginTop="10dp"
android:id="@+id/response"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text=""
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
3단계: MainActivity.java 작성
다음 코드를 src/MainActivity.java에 추가합니다. 저장 버튼을 누르면 입력한 내용이 외부 저장소의 MyFolder 폴더에 있는 SampleFile.txt 파일로 기록되고, 읽기 버튼을 누르면 해당 파일의 내용을 다시 불러와 화면에 표시합니다. 또한 외부 저장소가 마운트되어 사용 가능한지, 읽기 전용 상태인지 확인하여 저장 버튼을 활성화하거나 비활성화합니다.
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class MainActivity extends AppCompatActivity {
EditText editText;
TextView textView;
Button saveButton, readButton;
File myExternalFile;
String myData = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = findViewById(R.id.myInputText);
textView = findViewById(R.id.response);
saveButton = findViewById(R.id.saveExternalStorage);
saveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
FileOutputStream fos = new FileOutputStream(myExternalFile);
fos.write(editText.getText().toString().getBytes());
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
editText.setText("");
textView.setText("SampleFile.txt saved to External Storage...");
}
});
readButton = findViewById(R.id.getExternalStorage);
readButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
FileInputStream fis = new FileInputStream(myExternalFile);
DataInputStream in = new DataInputStream(fis);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String strLine;
while ((strLine = br.readLine()) != null) {
myData = myData + strLine;
}
in.close();
} catch (IOException e) {
e.printStackTrace();
}
editText.setText(myData);
textView.setText("SampleFile.txt data retrieved from Internal Storage...");
}
});
if (!isExternalStorageAvailable() || isExternalStorageReadOnly()) {
saveButton.setEnabled(false);
} else {
String fileName = "SampleFile.txt";
String filePath = "MyFolder";
myExternalFile = new File(getExternalFilesDir(filePath), fileName);
}
}
private static boolean isExternalStorageReadOnly() {
String extStorageState = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED_READ_ONLY.equals(extStorageState);
}
private static boolean isExternalStorageAvailable() {
String extStorageState = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(extStorageState);
}
}
4단계: 매니페스트에 권한 선언 (AndroidManifest.xml)
외부 저장소를 읽고 쓰려면 반드시 READ_EXTERNAL_STORAGE와 WRITE_EXTERNAL_STORAGE 권한을 매니페스트에 선언해야 합니다. 아래 코드를 androidManifest.xml에 추가하세요.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="app.com.sample">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
참고: Android 6.0(API 23) 이상을 타깃으로 하는 앱이라면 매니페스트 선언만으로는 부족하며, 실행 시점에 사용자에게 권한을 요청하는 런타임 권한 처리도 함께 구현해야 합니다. 또한 Android 10(API 29)부터는 범위 지정 저장소(Scoped Storage) 정책이 적용되므로, 이 예제처럼 getExternalFilesDir()로 얻는 앱 전용 디렉터리를 사용하거나 MediaStore API를 활용하는 것이 좋습니다.
앱 실행 및 결과 확인
이제 애플리케이션을 실행해 보겠습니다. 실제 안드로이드 기기가 컴퓨터에 연결되어 있다고 가정합니다. Android Studio에서 프로젝트의 액티비티 파일 중 하나를 연 뒤 툴바의 Run 아이콘을 클릭하고, 목록에서 자신의 모바일 기기를 선택하세요. 그러면 기기에 아래와 같은 기본 화면이 표시됩니다.

