이번 글에서는 아직까지도 이미지를 파일형태로 서버 api 보낼 때 생기는 오류를 해결하지 못하여 'react-native-blob-util'가 아닌 'react-native-fs' 라이브러리를 활용해 해결해 보고자 아래의 공식문서를 참고하여 'react-native-fs' 사용법에 대해 정리해보고자 한다.
https://github.com/itinance/react-native-fs
GitHub - itinance/react-native-fs: Native filesystem access for react-native
Native filesystem access for react-native. Contribute to itinance/react-native-fs development by creating an account on GitHub.
github.com
react-native-fs 라이브러리란?
react-native-fs 라이브러리는 react-native환경에서 파일 시스템에 접근하여 파일과 디렉토리를 관리할 수 있도록 도와주는 라이브러리이다. 이 라이브러리를 통해 react-native환경에서 파일 읽기, 파일 쓰기, 파일 삭제, 디렉토리 생성 및 삭제 등의 기능을 수행할 수 있다.
react-native-fs 라이브러리의 특징들
● 다양한 파일 작업 기능들을 지원: 파일을 생성하고, 읽고, 쓰며, 삭제하는 기능들 뿐만 아니라, 파일이름의 변경과 파일 위치 이동, 파일크기나 생성날짜들을 조회하는 기능까지 다양한 작업들을 지원해 준다.
● 파일 경로접근의 용이성: DocumentDirectoryPath, TemporaryDirectoryPath와 같은 속성들을 활용해 파일의 경로들을 쉽게 접근할 수 있도록 지원해 준다.
● 대용향 파일처리 기능: 대용량 파일을 처리하기 위한 Stream 기능을 지원하기에 큰 사이즈의 파일들을 효율적으로 읽고 쓸 수 있고, 이를 통해 메모리 사용을 최적화할 수 있다.
● URL을 통한 다운로드와 업로드 기능: URL로부터 직접 파일을 다운로드하거나 업로드할 수 있을 기능들을 제공해 주며, 이를 통해 앱 내에서 resource들을 서버로부터 받아오거나 서버에 파일을 전송할 때 유용하게 사용된다.
● 백그라운드 작업 기능: 파일 다운로드나 업로드를 백그라운드에서 할 수 있도록 지원해 주기에 앱의 UI를 멈추지 않고 파일 작업들을 수행할 수 있음
react-native-fs 주요 메서들
● writeFile (filePath, contents, encoding-type): filePath에 지정된 경로에 contents내용을 담아 파일을 작성해 주는 메서드이며, encoding-type은 선택적으로 작성할 수 있고, 기본값은 'UTF8'이다.
● readFile (filePath, encoding-type): filePath에 지정된 경로에 있는 파일의 내용을 읽어서 문자열로 반환해 주는 메서드이며, encoding-type은 선택적으로 작성할 수 있고, 기본값은 'UTF8'이다.
● appendFile (filePath, contents, encoding-type ): filePath에 지정된 경로에 있는 기존 파일에 내용을 추가해 주는 메서드이며, encoding-type은 선택적으로 작성할 수 있고, 기본값은 'UTF8'이다.
● write (filePath, contents, position, encoding-type ): filePath에 지정된 경로에 있는 파일의 특정 위치(position 파라미)에 데이터를 작성해 주는 메서드이며, encoding-type은 선택적으로 작성할 수 있고, 기본값은 'UTF8'이다. 주로 파일 내의 특정 부분을 수정할 때 유용하게 쓰인다.
● unlink (filePath): filePath에 지정된 경로에 있는 파일을 삭제시켜 주는 메서드이다.
● moveFile (srcPath, destPath): srcPath경로에 있는 파일을 destPath경로의 위치로 이동시켜 주는 메서드이다.
● copyFile (srcPath, destPath): srcPath경로에 있는 파일을 destPath경로로 복제시켜 주는 메서드이다.
● mkdir (dirPath): 새 디렉토리를 생성시켜 주는 메서드이다.
● rmdir (dirPath): dir경로에 있는 디렉토리를 삭제시켜 주는 메서드이다.
● readdir (dirPath): 디렉토리 내의 파일 및 서브디렉터리 목록을 반환시켜 주는 메서드이다.
● downloadFile (options): 원격 서버에서 파일을 다운로드하는 메서드이다. options 객체는 fromUrl, toFile, headers, background, discretionary, cacheable 등의 속성들을 설정할 수 있다.
● uploadFiles (options): 하나 이상의 파일을 서버로 업로드시켜 주는 메서드이다. options 객체는 toUrl, files, headers, fields, method 등의 속성들을 설정할 수 있다.
● stat (filePath): 파일 또는 디렉토리의 속성들을 조회하는 메서드이다. (크기, 수정된 시간, 생성 시간 등)
경로지정과 관련된 여러 속성들 ( 안드로이드 환경)
● CachesDirectoryPath: 캐시 디렉토리의 경로로, 일시적으로 데이터를 저장하고자 할 때 사용되는 경로이다.
● DocumentDirectoryPath: 문서 디렉토리의 경로로, 클라이언트 측 데이터나 애플리케이션 관련 문서들을 저장하는 데 사용되는 경로이다.
● ExternalDirectoryPath: 외부 저장소에 대한 경로이다.
● ExternalStorageDirectoryPath: 외부 저장소의 디렉토리에 대한 경로로, 외부 SD카드와 같은 저장소에 접근할 때 주로 사용된다.
react-native-fs 사용법 (Windows 버전)
react-native-fs 다운로드
아래 명령어를 입력하여 react-native-fs 라이브러리를 다운로드하여주자. (react-native link는 RN 0.60 버전 이상부터는 자동으로 해줌, 저는 0.74 버전을 사용 중이기에 따로 설정하지 않았습니다.)
npm install react-native-fs
파일 접근 권한 설정
[ android - app - src - main - AndroidManifest.xml] 경로로 들어가 아래코드를 추가하여 SD카드, 앨범 등과 같은 외부 저장소 사용에 대한 권한을 주도록 하자.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
react-native-fs 라이브러리 module 세팅
[android - settings.gradle] 경로로 들어가 ' settings.gradle' 파일에 아래의 코드를 추가해 react-native-fs 라이브러리 모듈을 현재 프로젝트에서 인식할 수 있게 해 주자.
...
include ':react-native-fs'
project(':react-native-fs').projectDir = new File(settingsDir, '../node_modules/react-native-fs/android')
그다음, [android - app - build.gradle] 경로로 들어가 아래 코드를 추가해 의존성에 해당 라이브러리를 추가하고 앱 빌드과정에 해당 라이브러리를 포함되게 만들어주자.
...
dependencies {
...
implementation project(':react-native-fs') // 이부분 추가
}
위와 같이 'react-native-fs' 라이브러리에 대한 모듈 설정과 외부 저장소에 대한 접근 권한, 의존성 세팅을 맞췄다면, 공식문서에 나와있는 버전대로 react native환경에서 안드로이드 네이티브에 최적화된 기능들을 쓸 수 있도록 패키지들을 추가하는 코드를 작성해 주자. (react-native 0.29.0 버전 이상은 아래 나와있는 걸 그대로 사용하면 됩니다.)
import com.rnfs.RNFSPackage;
public class MainApplication extends Application implements ReactApplication {
// ...
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new RNFSPackage()
);
}
'react-native-fs' 라이브러리를 사용하기 위한 기본적인 세팅을 마쳤으니 이제 본격적으로 파일을 관리하기 위해 이 라이브러리를 사용하는 방법에 대해 정리해 보도록 하겠다.
파일 쓰기 기본 문법
import RNFS from 'react-native-fs';
const path = RNFS.DocumentDirectoryPath + '/test.txt'; //DocumentDirectoryPath를 활용해 test.txt로 파일경로 설정
const writeFile = async () => { // 파일 쓰기 함수
try {
await RNFS.writeFile(path, '파일내용 입력', 'utf8');
} catch (error) {
console.error('파일 작성 도중 발생하는 에러 예외처리, error: ', error);
}
};
파일 읽기 기본 문법
import RNFS from 'react-native-fs';
const path = RNFS.DocumentDirectoryPath + '/test.txt'; //DocumentDirectoryPath를 활용해 test.txt로 파일경로 설정
const writeFile = async () => { // 파일 읽기 함수
try {
const content = await RNFS.readFile(path, 'utf8');
} catch (error) {
console.error('파일 읽는 도중 발생하는 에러 예외처리, error: ', error);
}
};
파일 삭제 기본 문법
import RNFS from 'react-native-fs';
const path = RNFS.DocumentDirectoryPath + '/test.txt'; //DocumentDirectoryPath를 활용해 test.txt로 파일경로 설정
const deleteFile = async () => { // 파일 삭제 함수
try {
await RNFS.unlink(path);
} catch (error) {
console.error('파일 삭제 도중 발생하는 에러 예외처리, error: ', error);
}
};
이미지 파일 저장 기본 문법
import RNFS from 'react-native-fs';
const imagePath = RNFS.DocumentDirectoryPath + '/test.jpg'; // DocumentDirectoryPath를 활용해 test.jpg로 파일경로 설정
const saveImage = async (imageUri) => { // 이미지 파일 저장 함수
try {
await RNFS.copyFile(imageUri, imagePath);
} catch (error) {
console.error('이미지 저장 도중 발생하는 에러 예외처리, error: ', error);
}
};
이미지 파일 읽기 기본 문법
import RNFS from 'react-native-fs';
const imagePath = RNFS.DocumentDirectoryPath + '/test.jpg'; // DocumentDirectoryPath를 활용해 test.jpg로 파일경로 설정
const loadImage = async () => { // 이미지 파일 읽기 함수
try {
const fileContents = await RNFS.readFile(imagePath, 'base64');
console.log('이미지 파일 내용: ', fileContents);
return `data:image/jpeg;base64,${fileContents}`;
} catch (error) {
console.error('이미지 읽기 도중 발생하는 에러 예외처리, error: ', error);
}
};
아래는 현재 작업 중인 파일에서 테스트파일을 만들어 image-picker로 선택한 이미지를 파일시스템에 저장한 다음 렌더링할 때 해당 이미지를 불러와본 예제이다. (이 예제는 추후 서버로 전송하는 것까지 완료된다면 관련 글을 작성할 것 이기에 해당글에서는 자세히 설명하지 않겠다.)
import React, {Component, useState} from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Image, ScrollView, TextInput, Platform } from 'react-native';
import {launchCamera, launchImageLibrary} from 'react-native-image-picker';
import axios from 'axios';
import { getToken, refreshAccessToken } from './token'
import RNFS from 'react-native-fs';
import RNFetchBlob from 'react-native-blob-util';
class AxiosTest extends Component {
state = {
imageUri: [],
imageType: null,
imageName: null,
}
postHouseData = () => {
this.state.imageUri.forEach((uri, index) => {
RNFS.stat(uri)
.then((stats) => {
console.log(`Image ${index}:`, stats);
if (stats.isFile()) {
console.log(`파일이 저장된 Uri: ${uri}`);
console.log(`파일크기: ${stats.size} bytes`);
console.log(`최근 수정일: ${stats.mtime}`);
console.log(`파일 존재여부: ${stats.isFile()}`);
}
})
.catch((error) => {
console.error(`Error retrieving file stats for URI ${uri}:`, error);
});
});
};
addImage = () => {
const options = {
mediaType: 'photo',
quality: 1,
maxWidth: 300,
maxHeight: 300,
includeBase64: false,
}
launchImageLibrary(options, response => {
if (response.didCancel) {
console.log('사용자가 ImagaPicker를 취소했습니다.');
} else if (response.error) {
console.log('ImagePicker내에서 에러가 발생했습니다: ', response.error);
} else if (response.customButton) {
console.log('사용자가 custom버튼을 눌렀습니다: ', response.customButton);
} else {
const { uri, type, fileName } = response.assets[0];
const newFilePath = `${RNFS.TemporaryDirectoryPath}/${fileName}`;
const fileUri = `file://${newFilePath}`
console.log(`Uri: ${uri}\ \n Type: ${type} \n Name: ${fileName} \n fileUri: ${fileUri}`);
RNFS.copyFile(uri, newFilePath)
.then(() => {
this.setState(prevState => ({
imageUri: [...prevState.imageUri, fileUri],
imageType: type,
imageName: fileName,
}));
})
.catch(err => console.error('File Copy Error:', err));
};
// console.log(`Uri: ${this.state.imageUri}, newFilePath: ${newFilePath}, Type: ${this.state.imageType}, Name: ${this.state.imageName}`);
});
};
render() {
return (
<View style={styles.container}>
<View style={styles.houseIMGView}>
<TouchableOpacity style={styles.ModifySelectView} onPress={this.addImage}>
<Text style={styles.InfoModify}> 사진 추가 </Text>
</TouchableOpacity>
<ScrollView style={styles.addHouseIMGView}
showsHorizontalScrollIndicator={false}
horizontal={true}>
{this.state.imageUri.length > 0 ? (
this.state.imageUri.map((uri, index) => (
<Image
key={index}
style={styles.houseIMG}
source={{ uri: uri }}
/>
))
) : (
<Text>이미지가 아직 로드되지 않았습니다.</Text>
)}
</ScrollView>
</View>
<TouchableOpacity style={styles.reservationBtn} onPress={() => this.postHouseData()}>
<Text style={styles.InfoModify}> 숙소 등록하기</Text>
</TouchableOpacity>
</View>
)
}
}
// 스타일 시트
const styles = StyleSheet.create({
container : {
alignItems: 'center',
justifyContent: "center",
height: '100%',
},
houseIMGView : {
alignItems: 'center',
justifyContent: "center",
width: "96%",
// backgroundColor: 'gray',
},
InfoModify: {
fontSize: 30,
margin: 40,
},
houseIMG: {
height: 200,
width: 200,
},
});
export default AxiosTest;


'프로그래밍 언어 > React Native (RN)' 카테고리의 다른 글
React Native에서 Google Map API 사용하기 (0) | 2024.08.07 |
---|---|
multipart/form-data 형태로 JSON 데이터와 이미지 파일 서버에 같이 보내기 (1) | 2024.07.29 |
FormData란? (1) | 2024.07.19 |
Axios 사용법 (0) | 2024.07.18 |
React Native 컴포넌트 정리 (0) | 2024.06.27 |