Skip to main content

네트워킹

많은 모바일 앱은 원격 URL에서 리소스를 불러와야 한다. REST API에 POST 요청을 보내야 할 수도 있고, 다른 서버에서 정적 콘텐츠를 가져와야 할 수도 있다.

Fetch 사용하기

React Native는 네트워킹 요구를 충족하기 위해 Fetch API를 제공한다. XMLHttpRequest나 다른 네트워킹 API를 사용해 본 적이 있다면 Fetch가 익숙하게 느껴질 것이다. 더 많은 정보를 원한다면 MDN의 Fetch 사용하기 가이드를 참고할 수 있다.

요청 보내기

임의의 URL에서 콘텐츠를 가져오려면 URL을 fetch에 전달한다:

tsx
fetch('https://mywebsite.com/mydata.json');

fetch는 HTTP 요청을 커스터마이징할 수 있는 두 번째 인자를 선택적으로 받는다. 추가 헤더를 지정하거나 POST 요청을 보낼 때 유용하다:

tsx
fetch('https://mywebsite.com/endpoint/', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
firstParam: 'yourValue',
secondParam: 'yourOtherValue',
}),
});

전체 속성 목록은 Fetch Request 문서를 참고한다.

응답 처리하기

위 예제에서는 요청을 보내는 방법을 보여줬다. 이제는 응답을 어떻게 처리하는지 알아보자.

네트워크 작업은 기본적으로 비동기 방식으로 동작한다. fetch 메서드는 Promise를 반환하므로, 비동기 코드를 작성하기가 간편하다:

tsx
const getMoviesFromApi = () => {
return fetch('https://reactnative.dev/movies.json')
.then(response => response.json())
.then(json => {
return json.movies;
})
.catch(error => {
console.error(error);
});
};

React Native 앱에서는 async / await 문법도 사용할 수 있다:

tsx
const getMoviesFromApiAsync = async () => {
try {
const response = await fetch(
'https://reactnative.dev/movies.json',
);
const json = await response.json();
return json.movies;
} catch (error) {
console.error(error);
}
};

fetch에서 발생할 수 있는 오류를 잡는 것을 잊지 말자. 그렇지 않으면 오류가 조용히 무시될 수 있다.

기본적으로 iOS 9.0 이상에서는 App Transport Security(ATS)가 적용된다. ATS는 모든 HTTP 연결이 HTTPS를 사용하도록 요구한다. http로 시작하는 URL에서 데이터를 가져와야 한다면, 먼저 ATS 예외를 추가해야 한다. 접근해야 할 도메인을 미리 알고 있다면, 해당 도메인에만 예외를 추가하는 것이 더 안전하다. 런타임에 도메인을 알 수 없는 경우에는 ATS를 완전히 비활성화할 수 있다. 하지만 2017년 1월부터 Apple의 App Store 심사에서는 ATS를 비활성화할 때 합리적인 근거를 요구한다는 점을 유의하자. 자세한 내용은 Apple의 문서를 참고하자.

Android에서는 API 레벨 28부터 기본적으로 일반 텍스트 트래픽이 차단된다. 이 동작은 앱 매니페스트 파일에서 android:usesCleartextTraffic를 설정하여 변경할 수 있다.

다른 네트워킹 라이브러리 사용하기

React Native는 XMLHttpRequest API를 기본적으로 지원한다. 따라서 frisbeeaxios와 같은 XMLHttpRequest에 의존하는 서드파티 라이브러리를 사용할 수 있다. 필요하다면 XMLHttpRequest API를 직접 사용하는 것도 가능하다.

tsx
const request = new XMLHttpRequest();
request.onreadystatechange = e => {
if (request.readyState !== 4) {
return;
}

if (request.status === 200) {
console.log('success', request.responseText);
} else {
console.warn('error');
}
};

request.open('GET', 'https://mywebsite.com/endpoint/');
request.send();

XMLHttpRequest의 보안 모델은 웹과 다르다. 네이티브 앱에서는 CORS 개념이 존재하지 않는다.

WebSocket 지원

React Native는 WebSockets도 지원한다. WebSocket은 단일 TCP 연결을 통해 양방향 통신 채널을 제공하는 프로토콜이다.

tsx
const ws = new WebSocket('ws://host.com/path');

ws.onopen = () => {
// 연결이 열림
ws.send('something'); // 메시지 전송
};

ws.onmessage = e => {
// 메시지 수신
console.log(e.data);
};

ws.onerror = e => {
// 오류 발생
console.log(e.message);
};

ws.onclose = e => {
// 연결 종료
console.log(e.code, e.reason);
};

fetch와 쿠키 기반 인증의 알려진 문제

현재 fetch에서 동작하지 않는 옵션은 다음과 같다:

  • redirect:manual
  • credentials:omit
  • 안드로이드에서 동일한 이름의 헤더가 있을 경우, 마지막 헤더만 남는 문제가 있다. 임시 해결 방법은 여기서 확인할 수 있다: https://github.com/facebook/react-native/issues/18837#issuecomment-398779994.
  • 쿠키 기반 인증은 현재 불안정하다. 관련 이슈는 여기서 확인할 수 있다: https://github.com/facebook/react-native/issues/23185.
  • iOS의 경우, 최소한 302 리다이렉트를 통해 이동할 때 Set-Cookie 헤더가 있더라도 쿠키가 제대로 설정되지 않는다. 리다이렉트를 수동으로 처리할 수 없기 때문에, 세션이 만료된 경우 무한 요청이 발생할 수 있다.

iOS에서 NSURLSession 설정하기

일부 애플리케이션에서는 iOS에서 실행 중인 React Native 앱의 네트워크 요청에 사용되는 기본 NSURLSession에 커스텀 NSURLSessionConfiguration을 제공하는 것이 적절할 수 있다. 예를 들어, 앱에서 발생하는 모든 네트워크 요청에 대해 커스텀 사용자 에이전트 문자열을 설정하거나, NSURLSession에 임시 NSURLSessionConfiguration을 제공해야 할 수 있다. 이때 RCTSetCustomNSURLSessionConfigurationProvider 함수를 사용해 이러한 커스텀 설정을 구현할 수 있다. RCTSetCustomNSURLSessionConfigurationProvider를 호출할 파일에 다음 import 문을 추가해야 한다:

objectivec
#import <React/RCTHTTPRequestHandler.h>

RCTSetCustomNSURLSessionConfigurationProvider는 앱의 생명 주기 초기에 호출되어 React가 필요로 할 때 즉시 사용할 수 있도록 준비해야 한다. 예를 들어 다음과 같이 구현할 수 있다:

objectivec
-(void)application:(__unused UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// RCTSetCustomNSURLSessionConfigurationProvider 설정
RCTSetCustomNSURLSessionConfigurationProvider(^NSURLSessionConfiguration *{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
// 세션 구성
return configuration;
});

// React 설정
_bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
}