Relay와 Back4App을 통한 React Native 인증 구현
Relay를 사용한 React Native 인증
소개
GraphQL API를 사용하여 사용자가 가입하거나 로그인한 후, 언제든지 로그인한 사용자를 검색하는 데 사용할 수 있는 세션 토큰을 받게 됩니다. 세션 토큰은 Relay Mutation에서 생성됩니다. 이러한 Relay Mutation 예제는 이전 가이드에서 찾을 수 있습니다.가입하기 또는 로그인하기.
세션 토큰 값은 현재 세션을 나타내며 사용자가 인증되었는지 여부를 제어합니다. 인증 순간에 이 값은 헤더 매개변수에 있어야 합니다. Relay에서는 환경을 사용하여 헤더 매개변수를 처리하므로 이 파일에 세션 토큰을 삽입해야 합니다.
헤더에 세션을 추가한 후, 각 요청은 Back4App에 의해 인증되며 사용자는 개인 리소스에 접근할 수 있습니다.
언제든지 이 프로젝트에 접근하여 스타일과 전체 코드를 확인할 수 있습니다.
목표
헤더 매개변수에 세션 토큰을 사용하여 Back4App에서 사용자 요청을 인증합니다.
전제 조건
- Parse Server 버전 3.10 이상을 사용하여 Back4App에서 생성된 앱입니다.
- 이 튜토리얼에서는 Expo를 React Native 프레임워크로 사용할 것입니다;
- 이 튜토리얼에서는 Javascript를 기본 구현 언어로 사용할 것입니다;
- 이 튜토리얼에서는 Async Storage를 사용할 것입니다;
1 - Async Storage 설치
튜토리얼을 마친 후 회원가입 또는 로그인, 앱은 세션 토큰을 받게 됩니다. Async Storage를 사용하여 토큰을 저장합시다. 공식 문서를 따라 앱에 Async Storage 라이브러리를 설치하세요.
Async Storage, Redux 또는 선호하는 로컬 저장 솔루션을 사용할 수 있습니다. 이 값이 환경에서 사용 가능하다는 것만 확인하세요.
2 - 토큰 가져오기
다음의 마지막 가이드 코드. 세션 토큰을 가져오고 이 값을 Async Storage를 사용하여 애플리케이션에 지속시켜야 합니다.
세션 토큰 상태 관리를 useState 훅에서 Async Storage로 변경하는 것부터 시작하세요. 첫 번째 단계는 환경 파일 내에 Async Storage에서 세션 토큰을 가져오는 함수를 만드는 것입니다.
비동기 저장소를 가져옵니다:
1 import AsyncStorage from '@react-native-async-storage/async-storage';이제 함수를 만듭니다:
1 export const getSessionToken = async () => {
2 const sessionToken = await AsyncStorage.getItem('sessionToken');
3 return sessionToken;
4 };3 - 클라이언트 측에 토큰 저장하기
이제 세션 토큰을 useState 훅을 사용하여 관리하는 대신 지속하도록 로그인 컴포넌트를 개선합시다. 이 컴포넌트는 세션 토큰이 지속되기 때문에 앱을 다시 로드할 때도 로그인 상태를 유지합니다.
로그인 컴포넌트를 엽니다. onSubmit의 onCompleted 내부에서 세션 토큰을 Async Storage에 저장하여 다음과 같은 결과를 얻습니다:
그런 다음 onCompleted를 개선합니다:
1 onCompleted: async (response) => {
2 if(!response?.logIn || response?.logIn === null) {
3 alert('Error while logging');
4 return;
5 }
6
7 const { viewer } = response?.logIn;
8 const { sessionToken, user } = viewer;
9
10 if (sessionToken !== null) {
11 setUserLogged(user);
12 alert(`user ${user.username} successfully logged`);
13 await AsyncStorage.setItem('sessionToken', sessionToken);
14 return;
15 }
16 },그 후, SignIn 컴포넌트 선언 내부에 세션 토큰을 위한 새로운 useState를 생성합니다:
1 const [sessionToken, setSessionToken] = useState(null);컴포넌트가 마운트될 때마다 호출되는 useEffect를 추가하고 세션 토큰이 있는지 확인합니다 (다음에서 가져옵니다getSessionToken 환경 파일:
1 useEffect(() => {
2 (async () => {
3 const sT = await getSessionToken();
4 setSessionToken(sT);
5 })();
6 }, []);마지막으로, 이제 새로운 useState를 처리하기 위해 onCompleted를 다시 변경합시다. 새로운 코드 라인을 가져옵니다:
1 onCompleted: async (response) => {
2 if (!response?.logIn || response?.logIn === null) {
3 alert('Error while logging');
4 return;
5 }
6
7 const { viewer } = response?.logIn;
8 const { sessionToken, user } = viewer;
9
10 if (sessionToken !== null) {
11 setSessionToken(sessionToken);
12 await AsyncStorage.setItem('sessionToken', sessionToken);
13 return;
14
15 }
16 },사용자가 로그인한 useState를 제거하고, 아래 두 줄을 각각의 위치에서 삭제하세요:
1 const [userLogged, setUserLogged] = useState(null);그리고
1 setUserLogged(user);우리는 알림을 피하고 사용자의 정보와 토큰을 useState에 설정한 후 Async Storage에 토큰을 저장합니다.
이제 세션 토큰을 처리하도록 if를 변경합니다.
1 if (sessionToken) {
2 return (
3 <View>
4 <Text>User logged</Text>
5 </View>
6 );
7 }4 - SignIn 컴포넌트의 최종 결과
모든 변경 후, SignIn 컴포넌트는 아래와 비슷할 것입니다.
1 import React, {useEffect, useState} from 'react';
2 import LogInMutation from './mutations/LogInMutation';
3 import environment, { getSessionToken } from '../../relay/environment';
4 import {FormikProvider, useFormik} from 'formik';
5 import { Button, Text, TextInput, View, TouchableOpacity } from 'react-native';
6 import AsyncStorage from '@react-native-async-storage/async-storage';
7
8 const SignIn = () => {
9 const [sessionToken, setSessionToken] = useState(null);
10
11 useEffect(() => {
12 (async () => {
13 const sT = await getSessionToken();
14 setSessionToken(sT);
15 })();
16 }, []);
17
18 const onSubmit = async (values) = {
19 const { username, password } = values;
20 const input = {
21 username,
22 password,
23 };
24
25 LogInMutation.commit({
26 environment,
27 input,
28 onCompleted: async (response) => {
29 if (!response?.logIn || response?.logIn === null) {
30 alert('Error while logging');
31 return;
32 }
33
34 const { viewer } = response?.logIn;
35 const { sessionToken, user } = viewer;
36
37 if (sessionToken !== null) {
38 setSessionToken(sessionToken);
39 setUserLogged(user);
40
41 await AsyncStorage.setItem('sessionToken', sessionToken);
42 return;
43 }
44 },
45 onError: (errors) => {
46 alert(errors[0].message);
47 },
48 });
49 };
50
51 const formikbag = useFormik({
52 initialValues: {
53 username: '',
54 password: '',
55 },
56 onSubmit,
57 });
58
59 const { handleSubmit, setFieldValue } = formikbag;
60
61 if (sessionToken) {
62 return (
63 <View style={ {marginTop: 15, alignItems: 'center'} }>
64 <Text>User logged</Text>
65 </View>
66 );
67 }
68
69 return (
70 <FormikProvider value={formikbag}>
71 <View style={Styles.login_wrapper}>
72 <View style={Styles.form}>
73 <Text>Username</Text>
74 <TextInput
75 name={"username"}
76 style={Styles.form_input}
77 autoCapitalize="none"
78 onChangeText={(text) => setFieldValue("username", text)}
79 />
80 <Text>Password</Text>
81 <TextInput
82 style={Styles.form_input}
83 name={"password"}
84 autoCapitalize="none"
85 secureTextEntry
86 onChangeText={(text) => setFieldValue("password", text)}
87 />
88 <TouchableOpacity onPress={() => handleSubmit()}>
89 <View style={Styles.button}>
90 <Text style={Styles.button_label}>{"Sign in"}</Text>
91 </View>
92 </TouchableOpacity>
93 </View>
94 </View>
95 </FormikProvider>
96 );
97 };
98
99 export default SignIn;5 - 테스트
이제 Sign In 컴포넌트의 새로운 변경 사항을 테스트할 시간입니다. 로그인된 사용자가 없도록 애플리케이션을 종료하고 다시 열어주세요.
비동기 저장소(Async Storage)를 지우는 것도 잊지 마세요. AsyncStorage.clear() 메서드를 호출하여 실제 상태를 지울 수 있습니다.
다시 로그인하면 다음 메시지가 표시됩니다.

6 - 릴레이 환경에 세션 토큰 설정하기
이제, Back4App GraphQL API에 대한 애플리케이션의 요청에 세션 토큰을 삽입해 보겠습니다. 환경 파일 안에서 sessionToken을 가져와서 headers 객체에 추가합니다. headers에서 변수 X-Parse-Session-Token에 sessionToken을 전달해야 합니다. 여기서 우리는 이미 생성한 getSessionToken 함수를 재사용할 것입니다.
다음 fetchQuery 함수 전에 함수를 생성하고 다음 코드를 붙여넣습니다:
1 export const getToken = async () => {
2 const sessionToken = await getSessionToken();
3
4 if (sessionToken) {
5 return {
6 'X-Parse-Session-Token': sessionToken,
7 };
8 }
9
10 return {};
11 };위의 함수는 세션 토큰이 존재할 경우에만 이를 가져옵니다. 이제 이를 headers 객체에 구조 분해하여 추가합니다.
1 const headers = {
2 Accept: 'application/json',
3 'Content-type': 'application/json',
4 'X-Parse-Application-Id': 'YOUR_APP_ID_HERE',
5 'X-Parse-Client-Key': 'YOUR_CLIENT_KEY_HERE',
6 ...await getToken(),
7 };headers 바로 아래에 요청을 위한 try catch가 있습니다. 요청 후 http 상태가 401일 때 처리할 if를 설정해 보겠습니다. 이는 현재 토큰이 더 이상 유효하지 않음을 의미합니다. 따라서, 우리는 저장소를 지우고 현재 사용자를 종료할 것입니다:
1 try {
2 const response = await fetch(`https://parseapi.back4app.com/graphql`, {
3 method: "POST",
4 headers,
5 body,
6 });
7
8 const data = await response.json();
9
10
11 // this if will retrive the response when status code 401 and clear the session token
12 if (response.status === 401) {
13 await AsyncStorage.getItem("sessionToken");
14 return;
15 }
16
17 if (data.errors) {
18 throw data.errors;
19 }
20
21 return data;
22 } catch (err) {
23 console.log("err on fetch graphql", err);
24
25 throw err;
26 }이제 귀하의 애플리케이션은 Back4App 백엔드에서 개인 리소스를 검색할 수 있습니다. 그리고 세션 토큰이 존재하지 않으면, 우리는 그것을 전달하지 않기 때문에 중단되지 않을 것입니다.
사용자에게 원하는 수준의 액세스를 보장하기 위해 보안 메커니즘을 구성하는 것을 잊지 마십시오. 액세스를 더 잘 이해하기 위해 다음 링크를 확인하십시오 보안 문서에서 Parse.
결론
이 가이드에서는 비동기 저장소를 사용하여 세션 토큰을 저장했으며, 이제 로그인한 사용자가 필요한 리소스를 검색할 수 있습니다.
다음 문서에서는 로그인한 사용자에 대한 정보를 검색하고 이를 표시하기 위해 useState를 사용하지 않는 컴포넌트를 준비해 보겠습니다.
사용자 가입을 위해 이 튜토리얼의 세션 토큰을 처리하는 동일한 접근 방식을 따를 수 있습니다.