---
title: Relay와 Back4App을 통한 React Native 인증 구현
slug: docs-kr/react-native/graphql/users/react-native-authentication
description: React Native와 Relay로 Back4App에서 사용자 인증 방법을 학습하세요.
image: https://www.back4app.com/images/logo-social.png
createdAt: 2024-03-20T13:33:48.482Z
---

# Relay를 사용한 React Native 인증

## 소개

GraphQL API를 사용하여 사용자가 가입하거나 로그인한 후, 언제든지 로그인한 사용자를 검색하는 데 사용할 수 있는 세션 토큰을 받게 됩니다. 세션 토큰은 Relay Mutation에서 생성됩니다. 이러한 Relay Mutation 예제는 이전 가이드에서 찾을 수 있습니다.[가입하기](https://www.back4app.com/docs/parse-graphql/graphql-sign-up) 또는 [로그인하기](https://www.back4app.com/docs/parse-graphql/graphql-login).

세션 토큰 값은 현재 세션을 나타내며 사용자가 인증되었는지 여부를 제어합니다. 인증 순간에 이 값은 헤더 매개변수에 있어야 합니다. Relay에서는 환경을 사용하여 헤더 매개변수를 처리하므로 이 파일에 세션 토큰을 삽입해야 합니다.

헤더에 세션을 추가한 후, 각 요청은 Back4App에 의해 인증되며 사용자는 개인 리소스에 접근할 수 있습니다.

:::hint{type="success"}
**언제든지 이 프로젝트에 접근하여 스타일과 전체 코드를 확인할 수 있습니다.**

- [JavaScript 예제 저장소](https://github.com/templates-back4app/react-native-graphql-relay-js-users)
:::

## 목표

헤더 매개변수에 세션 토큰을 사용하여 Back4App에서 사용자 요청을 인증합니다.

## 전제 조건

- **Parse Server 버전 3.10 이상을 사용하여 Back4App에서 생성된 앱입니다.**
- **다음의 [Relay 환경 설정 튜토리얼](https://www.back4app.com/docs/react-native/graphql/relay-setup):**
- **다음의 [Relay를 사용한 React Native 로그인 샘플](https://www.back4app.com/docs/react-native/graphql/users/react-native-login-sample):**
- **이 튜토리얼에서는 Expo를 React Native 프레임워크로 사용할 것입니다;**
- **이 튜토리얼에서는 Javascript를 기본 구현 언어로 사용할 것입니다;**
- **이 튜토리얼에서는 Async Storage를 사용할 것입니다;**

## 1 - Async Storage 설치

튜토리얼을 마친 후 [회원가입](https://www.back4app.com/docs/parse-graphql/graphql-sign-up) 또는 [로그인](https://www.back4app.com/docs/parse-graphql/graphql-login), 앱은 세션 토큰을 받게 됩니다. Async Storage를 사용하여 토큰을 저장합시다. 공식 문서를 따라 앱에 Async Storage 라이브러리를 설치하세요.

:::hint{type="success"}
Async Storage, Redux 또는 선호하는 로컬 저장 솔루션을 사용할 수 있습니다. 이 값이 환경에서 사용 가능하다는 것만 확인하세요.
:::

## 2 - 토큰 가져오기

다음의 [마지막 가이드 코드](https://www.back4app.com/docs/parse-graphql/graphql-login). 세션 토큰을 가져오고 이 값을 Async Storage를 사용하여 애플리케이션에 지속시켜야 합니다.

세션 토큰 상태 관리를 useState 훅에서 Async Storage로 변경하는 것부터 시작하세요. 첫 번째 단계는 환경 파일 내에 Async Storage에서 세션 토큰을 가져오는 함수를 만드는 것입니다.

비동기 저장소를 가져옵니다:

```javascript
1    import AsyncStorage from '@react-native-async-storage/async-storage';
```

이제 함수를 만듭니다:

```javascript
1    export const getSessionToken = async () => {
2      const sessionToken = await AsyncStorage.getItem('sessionToken');
3      return sessionToken;
4    };
```

## 3 - 클라이언트 측에 토큰 저장하기

이제 세션 토큰을 useState 훅을 사용하여 관리하는 대신 지속하도록 로그인 컴포넌트를 개선합시다. 이 컴포넌트는 세션 토큰이 지속되기 때문에 앱을 다시 로드할 때도 로그인 상태를 유지합니다.

로그인 컴포넌트를 엽니다. onSubmit의 onCompleted 내부에서 세션 토큰을 Async Storage에 저장하여 다음과 같은 결과를 얻습니다:

그런 다음 onCompleted를 개선합니다:

```javascript
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를 생성합니다:

```javascript
1	const [sessionToken, setSessionToken] = useState(null);
```

컴포넌트가 마운트될 때마다 호출되는 useEffect를 추가하고 세션 토큰이 있는지 확인합니다 (다음에서 가져옵니다<font color="#2166ae">`getSessionToken`</font> 환경 파일:

```javascript
1	useEffect(() => {
2	  (async () => {
3	    const sT = await getSessionToken();
4	    setSessionToken(sT);
5	  })();
6	}, []);
```

마지막으로, 이제 새로운 useState를 처리하기 위해 onCompleted를 다시 변경합시다. 새로운 코드 라인을 가져옵니다:

```javascript
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를 제거하고, 아래 두 줄을 각각의 위치에서 삭제하세요:

```javascript
1	const [userLogged, setUserLogged] = useState(null);
```

그리고

```javascript
1	setUserLogged(user);
```

우리는 알림을 피하고 사용자의 정보와 토큰을 useState에 설정한 후 Async Storage에 토큰을 저장합니다.

이제 세션 토큰을 처리하도록 if를 변경합니다.

```javascript
1	if (sessionToken) {
2	  return (
3	    <View>
4	      <Text>User logged</Text>
5	    </View>
6	  );
7	}
```

## 4 - SignIn 컴포넌트의 최종 결과

모든 변경 후, SignIn 컴포넌트는 아래와 비슷할 것입니다.

```javascript
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 컴포넌트의 새로운 변경 사항을 테스트할 시간입니다. 로그인된 사용자가 없도록 애플리케이션을 종료하고 다시 열어주세요.

:::hint{type="success"}
비동기 저장소(Async Storage)를 지우는 것도 잊지 마세요. <font color="#2166ae">`AsyncStorage.clear()`</font> 메서드를 호출하여 실제 상태를 지울 수 있습니다.
:::

다시 로그인하면 다음 메시지가 표시됩니다.

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/FVeoyIrC2dFPWlcJEFgU0_image.png" size="60" width="828" height="1792" position="center" showCaption="false"}

## 6 - 릴레이 환경에 세션 토큰 설정하기

이제, Back4App GraphQL API에 대한 애플리케이션의 요청에 세션 토큰을 삽입해 보겠습니다. 환경 파일 안에서 sessionToken을 가져와서 headers 객체에 추가합니다. headers에서 변수 X-Parse-Session-Token에 sessionToken을 전달해야 합니다.
여기서 우리는 이미 생성한 <font color="#2166ae">`getSessionToken`</font> 함수를 재사용할 것입니다.

다음 <font color="#2166ae">`fetchQuery`</font> 함수 전에 함수를 생성하고 다음 코드를 붙여넣습니다:

```javascript
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 객체에 구조 분해하여 추가합니다.

```javascript
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를 설정해 보겠습니다. 이는 현재 토큰이 더 이상 유효하지 않음을 의미합니다. 따라서, 우리는 저장소를 지우고 현재 사용자를 종료할 것입니다:

```javascript
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 백엔드에서 개인 리소스를 검색할 수 있습니다. 그리고 세션 토큰이 존재하지 않으면, 우리는 그것을 전달하지 않기 때문에 중단되지 않을 것입니다.

:::hint{type="success"}
사용자에게 원하는 수준의 액세스를 보장하기 위해 보안 메커니즘을 구성하는 것을 잊지 마십시오. 액세스를 더 잘 이해하기 위해 다음 링크를 확인하십시오 [보안 문서](https://docs.parseplatform.org/js/guide/#security)에서 Parse.
:::

## 결론

이 가이드에서는 비동기 저장소를 사용하여 세션 토큰을 저장했으며, 이제 로그인한 사용자가 필요한 리소스를 검색할 수 있습니다.

다음 문서에서는 로그인한 사용자에 대한 정보를 검색하고 이를 표시하기 위해 useState를 사용하지 않는 컴포넌트를 준비해 보겠습니다.

:::hint{type="success"}
사용자 가입을 위해 이 튜토리얼의 세션 토큰을 처리하는 동일한 접근 방식을 따를 수 있습니다.
:::

