React Native 앱에 인증을 추가하는 방법은 무엇인가요?

리액트 네이티브(엑스포) 인증 커버

인증은 거의 모든 앱의 핵심 구성 요소입니다.

이 실용적인 문서에서는 React Native 인증을 시작하는 단계별 가이드를 제공합니다.

또한 인증의 기본 사항, 인증과 비교하는 방법, 인증 흐름 및 인증 사용의 이점에 대해 설명합니다.

인증이란 무엇인가요?

인증은 누군가의 신원을 확인하는 과정입니다. 일반적으로 인증에는 세 가지 방법이 있습니다:

  1. 알고 있는 정보(예: 비밀번호, PIN)
  2. 소지하고 있는 물건(예: 휴대폰, 열쇠)
  3. 본인 정보(예: 지문, 홍채)

대부분의 애플리케이션은 첫 번째 옵션을 사용하지만, 보안 수준을 높이기 위해 여러 가지 옵션을 결합하는 경우가 많습니다. 이 개념을 멀티팩터 인증(MFA)이라고 합니다.

인증 흐름

일반적인 인증 흐름은 다음과 같이 작동합니다:

  1. 사용자가 비밀번호를 서버로 전송합니다.
  2. 서버는 비밀번호를 해시하고 데이터베이스의 비밀번호 해시와 비교합니다.
  3. 해시가 일치하면 서버는 세션을 생성하고 세션 토큰을 사용자에게 보냅니다. 반면에 해시가 일치하지 않으면 오류가 발생합니다.
  4. 그런 다음 사용자는 각 요청에 세션 토큰을 사용합니다. 각 요청 시 서버는 토큰이 존재하고 유효한지 확인합니다.

위의 흐름은 소위 토큰 인증입니다. 다른 인증 시스템으로는 JSON 웹 토큰 (JWT), 기본 액세스 인증, 소셜 로그인 등이 있습니다.

인증 대 권한 부여

인증은 사용자의 신원을 확인하는 행위이고 권한 부여는 사용자가 작업을 수행할 수 있는 충분한 권한을 가지고 있는지 확인하는 행위입니다.

권한 부여 모델에는 필수 액세스 제어(MAC), 재량 액세스 제어(DAC), 역할 기반 권한 부여 등이 있습니다.

인증의 이점

모바일 앱에서 인증을 사용하면 얻을 수 있는 몇 가지 이점을 살펴보겠습니다.

보안

인증은 무단 액세스로부터 앱을 보호하고 합법적인 사용자만 서비스에 액세스할 수 있도록 보장합니다.

이를 통해 데이터 유출 및 사이버 공격을 방지하고 앱 환경을 안전하고 신뢰할 수 있는 상태로 유지할 수 있습니다.

개인화

인증을 통해 앱 내에서 개인화된 사용자 경험을 제공할 수 있습니다. 사용자가 로그인하면 필요에 따라 맞춤 설정, 환경 설정 및 추천에 액세스할 수 있습니다.

이러한 개인화는 앱을 더욱 매력적이고 사용자 친화적으로 만듭니다. 보다 관련성 있고 즐거운 경험을 제공함으로써 사용자를 유지하는 데 도움이 됩니다.

법적 요구 사항

일부 국가의 법률에 따라 사용자의 신원을 확인해야 하는 경우가 있습니다. 그 예로 고객 파악(KYC)을 들 수 있습니다.

거의 모든 금융 앱이 이를 의무적으로 수행해야 합니다. 이러한 규정을 준수하거나 사용자의 개인 정보를 보호하려면 적절한 인증 시스템을 갖추어야 합니다.

React Native 앱에 인증을 추가하는 방법은 무엇인가요?

이 글의 이 부분에서는 React Native(Expo) 앱에 Back4app 인증을 추가하는 방법에 대한 단계별 가이드를 제공합니다.

전제 조건

  • 서비스형 백엔드(BaaS)에 대한 기본 이해
  • JavaScript IDE, Node.js, 모바일 에뮬레이터 또는 물리적 장치
  • JavaScript 코드 읽기 및 쓰기 기능
  • React Native 및 Expo 경험
  • 무료 Back4app 계정

Back4app이란 무엇인가요?

Back4app은 최고의 오픈소스 서비스형 백엔드(BaaS) 솔루션 중 하나입니다. 2015년부터 사용되어 온 성숙하고 안정적인 플랫폼입니다.

주요 기능으로는 실시간 데이터베이스, 클라우드 코드 기능, 자동 RESTful/GraphQL API 생성 등이 있습니다!

Back4app은 사용하기 쉽고 직관적인 관리자 대시보드와 고급 사용자를 위한 명령줄 인터페이스(CLI)를 제공합니다.

또한 JavaScript, PHP, Flutter, Dart 등 가장 인기 있는 프로그래밍 언어 및 프레임워크용 SDK를 제공합니다.

가장 좋은 점은 Back4app이 무료 티어를 제공한다는 것입니다. 무료 티어는 테스트 및 프로토타이핑에 적합하며 다음과 같은 기능이 포함되어 있습니다:

  • 25,000/월 요청
  • 250MB 데이터 스토리지
  • 1GB 데이터 전송
  • 1GB 파일 저장 용량

왜 Back4app을 사용하나요?

  • 소셜 인증 지원
  • 대부분의 프로그래밍 언어 및 프레임워크용 SDK 제공
  • 손쉬운 설정 및 사용
  • 탁월한 고객 지원

Back4app 인증이 Firebase 인증과 어떻게 비교되는지 알아보려면 React Firebase 인증에 대한 궁극적인 가이드를 확인하세요.

프로젝트 소개 – React Native 인증 시작하기

Back4app 인증을 사용하는 프로덕션에 바로 사용할 수 있는 React Native 애플리케이션을 구축하겠습니다. 이 앱을 통해 사용자는 등록, 로그인 및 프로필을 관리할 수 있습니다.

또한 앱에는 인증된 사용자를 위한 탭과 인증되지 않은 사용자를 위한 탭이 두 개 있습니다.

최종 결과물은 다음과 같이 표시됩니다:

Back4app 인증 리액트 네이티브 앱

코딩을 시작해보자!

백엔드(Back4app)

이 섹션에서는 Back4app 앱을 만들고, 기본 사용자 모델을 확장하고, 백엔드에 연결하는 데 필요한 API 키를 얻습니다.

앱 만들기

먼저 Back4app 계정에 로그인하거나 아직 계정이 없는 경우 계정을 생성합니다.

로그인하면 앱 목록으로 리디렉션됩니다. 새 앱을 만들려면 ‘새 앱 만들기’를 클릭합니다.

Back4app 앱 목록

Back4app 플랫폼에서는 서비스형 백엔드(BaaS) 또는 서비스형 컨테이너(CaaS)의 두 가지 유형의 앱을 배포할 수 있습니다. 인증은 BaaS에 포함되어 있으므로 이를 선택합니다.

Back4app 서비스형 백엔드 선택

그런 다음, 앱에 설명이 포함된 이름을 지정하고 데이터베이스를 NoSQL로 남겨둔 다음 “만들기”를 클릭합니다.

Back4app 앱 구성

플랫폼에서 앱이 생성될 때까지 약 2분 정도 기다립니다. Back4app은 앱 레이어 만들기부터 데이터베이스 설정, 보안, 확장 등 모든 작업을 수행합니다.

완료되면 데이터베이스 인터페이스로 리디렉션됩니다.

Back4app 데이터베이스 보기

데이터베이스 수정

다음으로 데이터베이스 클래스에 대해 이야기해 보겠습니다.

모든 Back4app 데이터베이스 클래스에는 다음과 같은 기본 필드가 제공됩니다:

+-----------+------------+-----------------------------------------+
| Data type | Name       | Description                             |
+-----------+------------+-----------------------------------------+
| String    | objectId   | Object's unique identifier              |
+-----------+------------+-----------------------------------------+
| Date      | createdAt  | Date of object creation                 |
+-----------+------------+-----------------------------------------+
| Date      | updatedAt  | Date of object's last update            |
+-----------+------------+-----------------------------------------+
| ACL       | ACL        | Access Control List (security features) |
+-----------+------------+-----------------------------------------+

그리고 사용자 클래스에는 몇 가지 추가 필드가 있습니다:

+-----------+----------------+--------------------------+----------+
| Data type | Name           | Default value            | Required |
+-----------+----------------+--------------------------+----------+
| String    | username       |                          | yes      |
+-----------+----------------+--------------------------+----------+
| String    | email          |                          | no       |
+-----------+----------------+--------------------------+----------+
| Boolean   | emailVerified  | false                    | no       |
+-----------+----------------+--------------------------+----------+
| String    | password       |                          | yes      |
+-----------+----------------+--------------------------+----------+
| Object*   | authData       | {}                       | yes      |
+-----------+----------------+--------------------------+----------+

기본 Back4app 사용자 클래스는 대부분의 사용 사례에 적합할 것입니다. 그럼에도 불구하고 이를 확장하는 것은 쉽습니다. 그 방법을 보여드리기 위해 약력(바이오) 필드를 추가해 보겠습니다.

먼저 데이터베이스 테이블 위에 있는 ‘+ 열’ 버튼을 클릭합니다.

Back4app 데이터베이스 열 추가

열 생성 양식에서 데이터 유형으로 “문자열”을 선택하고 이름을 바이오로 설정하고 필수로 설정한 다음 “추가”를 클릭합니다.

Back4app 데이터베이스 필드 추가

이제 Back4app 데이터베이스 클래스의 작동 방식과 사용자 모델을 확장하는 방법을 알았습니다.

API 키

프론트엔드에서 백엔드에 연결하려면 앱의 API 키를 받아야 합니다.

키를 받으려면 Back4app 앱으로 이동하여 사이드바에서 ‘보안 및 키’를 선택하세요. “클라이언트 키”와 “JavaScript 키”를 메모해 두세요.

Back4app API 키

백엔드 쪽에서는 여기까지입니다.

프론트엔드(React Native)

이 섹션에서는 Expo 앱을 만들고, 컴포넌트 라이브러리를 설치하고, 탐색을 설정하고, 화면을 관리하고, 마지막으로 프론트엔드와 백엔드를 연결해 보겠습니다.

앱 만들기

React Native 앱을 생성하기 위해 create-expo-app 유틸리티를 사용하겠습니다. 이 유틸리티는 디렉토리 구조를 생성하고 TypeScript를 구성하는 등의 작업을 통해 React Native 앱 생성을 간소화합니다.

먼저 다음 명령을 실행합니다:

npx create-expo-app@latest back4app-expo-auth
cd back4app-expo-auth

이 명령은 create-expo-app이 아직 설치되어 있지 않은 경우에도 설치합니다.

부트스트랩 프로젝트의 디렉토리 구조는 다음과 같습니다:

back4app-expo-auth/
├── app                 - Layouts, screens
├── assets              - Static assets (e.g. images, videos, fonts)
├── components          - Reusable components used through the app
├── constants           - Static variables & configurations
├── hooks               - Custom React hooks
├── scripts             - Development scripts
├── app.json            - Expo configuration & metadata
├── expo-env.d.ts       - Expo TypeScript declarations
├── ...    

개발 서버를 시작합니다:

$ npx expo start

마지막으로 A를 눌러 Android 에뮬레이터에서 앱을 엽니다. 또는 iOS 에뮬레이터 또는 실제 iOS 기기를 사용할 수도 있습니다. 앱이 열리면 기본 Expo 화면이 표시됩니다.

엑스포 기본 화면

React Native Paper

UI 개발 프로세스를 간소화하기 위해 React Native Paper를 사용하겠습니다.

React Native Paper는 사용하기 쉬운 고품질의 리액트 네이티브 애플리케이션용 컴포넌트 라이브러리입니다. 거의 모든 사용 사례를 포괄하는 다양한 사전 제작 컴포넌트를 제공합니다.

먼저 NPM을 통해 설치하세요:

$ npm install react-native-paper react-native-safe-area-context 

iOS용으로 빌드하는 경우 라이브러리의 네이티브 부분도 연결해야 합니다:

npx pod-install

다음으로, 사용하지 않는 컴포넌트를 프로덕션에 포함하지 않도록 Babel을 구성합니다:

// babel.config.js

module.exports = function(api) {
  api.cache(true);
  return {
    presets: ["babel-preset-expo"],
    env: {
      production: {
        plugins: ["react-native-paper/babel"],
      },
    },
  };
};

그런 다음 app/_layout.tsx로 이동하여 다음과 같이 앱을 PaperProvider로 래핑합니다:

// app/_layout.tsx

// ...

export default function RootLayout() {

  // ...

  return (
    <ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
      <PaperProvider>
        <Stack>
          <Stack.Screen name="index" options={{headerShown: false}}/>
          <Stack.Screen name="(auth)" options={{headerShown: false}}/>
          <Stack.Screen name="(tabs)" options={{headerShown: false}}/>
          <Stack.Screen name="+not-found"/>
        </Stack>
      </PaperProvider>
    </ThemeProvider>
  );
}

파일 상단에 있는 가져오기 기능을 잊지 마세요:

import {PaperProvider} from "react-native-paper";

완벽합니다, 컴포넌트 라이브러리를 성공적으로 설치했습니다.

컨테이너

또 한 가지 할 일은 컨테이너 컴포넌트를 만드는 것입니다. 이 컴포넌트는 모든 화면에서 사용되며, 콘텐츠가 화면 가장자리로 가지 않도록 모든 면에 여백을 추가합니다.

구성 요소 폴더에 Container.tsx 파일을 만듭니다:

// components/Container.tsx

import React from "react";
import {View} from "react-native";

export type ContainerProps = {
  children: React.ReactNode;
}

export function Container({children}: ContainerProps) {
  return (
    <View style={{margin: 12}}>
      {children}
    </View>
  );
}

탐색 및 화면

프로젝트 소개에서 언급했듯이 앱에는 두 개의 탐색 탭이 있습니다.

하나는 인증된 사용자용이고 다른 하나는 인증되지 않은 사용자용입니다. 인증된 탭에서는 사용자가 자신의 프로필을 관리할 수 있고 다른 탭에서는 로그인하거나 계정을 만들 수 있습니다.

이를 위해 먼저 다음과 같은 디렉토리 구조를 만듭니다:

app/
├── (auth)/
│   ├── _layout.tsx
│   ├── login.tsx
│   └── register.tsx
├── (tabs)/
│   ├── _layout.tsx
│   └── profile.tsx
├── +html.tsx
├── +not-found.tsx
├── _layout.tsx
└── index.tsx

Expo 라우터에 대한 자세한 내용은 공식 문서를 참조하세요.

Expo 레이아웃에는 일반적으로 상용구 코드가 많이 포함되어 있습니다. 이 글에 코드를 가득 채우고 싶지 않으니 GitHub에서 파일 내용을 가져와 주세요:

  1. (auth)/_layout.tsx
  2. (tabs)/_layout.tsx
  3. _layout.tsx

계속 진행하여 index.tsx에 다음을 배치합니다:

// app/index.tsx

import React, {useEffect} from "react";
import {ActivityIndicator} from "react-native-paper";
import {useRouter} from "expo-router";
import {View} from "react-native";

export default function IndexScreen() {

  const router = useRouter();
  const isAuthenticated = true;

  useEffect(() => {
    setTimeout(() => {
      if (isAuthenticated) {
        router.push("profile");
      } else {
        router.push("login");
      }
    }, 1000);
  }, []);

  return (
    <View style={{
      flex: 1,
      justifyContent: "center",
      alignItems: "center",
    }}>
      <ActivityIndicator
        size="large"
        animating={true}
      />
    </View>
  );
}

index.tsx 파일은 앱의 진입점입니다. 이 파일에서 사용자가 인증되었는지 확인한 다음 그에 따라 리디렉션합니다. 현재 리디렉션은 isAuthenticated 변수를 기반으로 합니다.

그런 다음 화면의 코드를 가져옵니다:

  1. app/(auth)/login.tsx
  2. app/(auth)/register.tsx
  3. app/(tabs)/profile.tsx

화면 코드는 매우 간단합니다. 양식과 기타 UI를 만드는 데 React Native Paper를 사용하는 React Native 코드입니다. 또한 사용State()사용효과()와 같은 기본 React 훅을 활용합니다.

Parse SDK

백엔드에 연결하기 위해 Parse SDK를 사용합니다. 이 SDK는 데이터 저장, 조작, 사용자 인증 및 기타 기능을 위한 메서드를 제공합니다.

먼저 NPM을 통해 설치합니다:

$ npm install parse @react-native-async-storage/async-storage --save
$ npm install --save-dev @types/parse

또한 앱이 종료될 때 사용자 세션을 유지하기 위해 @react-native-async-storage/async-storage 패키지를 설치했습니다. 이 패키지가 없으면 사용자가 애플리케이션을 열 때마다 인증해야 합니다.

그런 다음 프로젝트 루트에 다음과 같이 .env 파일을 만듭니다:

EXPO_PUBLIC_APPLICATION_ID=<your-back4app-application-id>
EXPO_PUBLIC_JAVASCRIPT_KEY=<your-back4app-client-key>

교체해야 합니다. <your-back4app-application-id><your-back4app-client-key> 를 이 문서의 백엔드 섹션에서 얻은 API 키로 교체해야 합니다.

다음과 같이 _layout.tsx에서 Parse을 초기화합니다:

// app/_layout.tsx

// ...
import Parse from "parse/react-native.js";
import AsyncStorage from "@react-native-async-storage/async-storage";
import ParseContext from "@/context/parseContext";

Parse.setAsyncStorage(AsyncStorage);
Parse.initialize(
  process.env.EXPO_PUBLIC_APPLICATION_ID ?? "",
  process.env.EXPO_PUBLIC_JAVASCRIPT_KEY ?? "",
)
Parse.serverURL = "https://parseapi.back4app.com/";

async function testParse() {
  try {
    const testMessage = new Parse.Object("TestMessage");
    testMessage.set("message", "Hello, World!");
    await testMessage.save();
  } catch (error) {
    console.log("Error saving the test message: ", error);
  }
}
testParse().then(() => console.log("Successfully connected to Parse!"));

// ...

또한 데이터베이스에 “Hello, world!” 메시지를 추가하여 Back4app에 대한 연결을 테스트하는 testParse() 함수도 포함했습니다. Expo 서버를 다시 시작하고 에뮬레이터에서 앱을 실행하여 연결이 작동하는지 확인합니다.

Back4app 데이터베이스 테스트 메시지

앱의 데이터베이스 보기로 이동하여 메시지가 표시되는지 확인합니다.

“오류: crypto.getRandomValues()가 지원되지 않습니다”라는 오류가 표시되는 경우. 다음 종속성을 설치하세요:

npm install react-native-get-random-values --save
npm i --save-dev @types/react-native-get-random-values

그런 다음 _layout.tsx 상단에 가져오기를 추가합니다(Parse 가져오기 전):

import "react-native-get-random-values";

개발 서버를 다시 시작하면 모든 것이 정상적으로 작동합니다.

모든 화면에서 Parse 인스턴스를 사용할 수 있게 하려면 React 컨텍스트를 사용하여 전달합니다.

먼저 컨텍스트 디렉터리를 만들고 그 안에 다음 parseContext.ts 파일을 넣습니다:

import {createContext} from "react";

const ParseContext = createContext<typeof Parse | null>(null);

export default ParseContext;

그런 다음 Parse 인스턴스를 전달하면서 전체 앱을 래핑합니다:

// app/_layout.tsx

// ...

return (
  <ParseContext.Provider value={Parse}>
    {/* ... */}
  </ParseContext.Provider>
)

데이터 검색 및 조작

이 마지막 섹션에서는 Parse SDK를 사용하여 사용자를 인증하고 사용자 정보를 가져옵니다.

모든 화면에서 동일한 코드를 반복하는 대신 useParse 훅을 만들겠습니다. 이 훅은 컨텍스트에서 Parse 인스턴스를 가져오고, 사용자 정보를 가져오고, 모든 것이 준비되면 업데이트를 트리거합니다.

컨텍스트 폴더에 useParse.ts라는 새 파일을 만듭니다:

// context/useParse.ts

import {useContext, useEffect, useState} from "react";
import ParseContext from "@/context/parseContext";

export function useParse() {
  const parse = useContext(ParseContext) as typeof Parse;
  const [parseUser, setParseUser] = useState<Parse.User | null>(null);
  const [isParseLoaded, setIsParseLoaded] = useState(false);

  useEffect(() => {
    (async () => {
      try {
        setParseUser(await parse.User.currentAsync());
      } catch (e) {
        console.error(e);
      } finally {
        setIsParseLoaded(true);
      }
    })();

  }, []);

  return {parse, parseUser, isParseLoaded};
}

그런 다음 isAuthenticated를 실제 세션 확인으로 대체하여 index.tsx를 수정합니다:

// app/index.tsx

// ...
import {useParse} from "@/hooks/useParse";

export default function IndexScreen() {

  const router = useRouter();
  const {parse, parseUser, isParseLoaded} = useParse();

  useEffect(() => {
    if (!isParseLoaded) return;
    (async () => {
      if (parseUser) {
        console.log("User is authenticated!");
        console.log(parseUser.toJSON());
        router.replace("/profile");
      } else {
        console.log("User is not authenticated.");
        console.log({});
        router.replace("/(auth)/login");
      }
    })();
  }, [isParseLoaded]);

  return (
    // ...
  );
}

그런 다음 login.tsx를 수정하여 사용자 로그인을 합니다:

// app/(auth)/login.tsx

// ...
import {useParse} from "@/hooks/useParse";

export default function LoginScreen() {

  const router = useRouter();
  const {parse, parseUser, isParseLoaded} = useParse();

  // ...

  const onLogin = async () => {
    // ...
    try {
      await parse.User.logIn(username, password);
      router.push("/(tabs)/profile");
    } catch (error: any) {
      setError(error.message);
    }
  }

  return (
    // ...
  );
}

그런 다음 register.tsx를 login.tsx와 비슷하게 수정하되 이번에는 TODO를 다음과 같이 바꾸세요:

// app/(auth)/register.tsx

try {
  const user = await parse.User.signUp(username, password, undefined, undefined);
  user.setEmail(email);
  await user.save();
  router.replace("/(tabs)/profile")
} catch (error: any) {
  setError(error.message);
}

마지막으로 프로필 정보를 표시하도록 profile.tsx를 수정합니다:

// app/(tabs)/profile.tsx

// ...
import {useParse} from "@/hooks/useParse";

export default function IndexScreen() {

  const router = useRouter();
  const {parse, parseUser, isParseLoaded} = useParse();

  // ...

  useEffect(() => {
    if (!parseUser) return;
    setBio(parseUser.get("bio") || "");
  }, [parseUser]);

  const onSave = async () => {
    if (!parseUser) return;
    parseUser.set("bio", bio);
    try {
      await parseUser.save();
      setSuccess("Bio saved successfully.");
    } catch (error: any) {
      setError(error.message);
    }
  }

  const onLogout = async () => {
    router.replace("/(auth)/login");
    await parse.User.logOut();
  }

  return (
    <Container>
      {!isParseLoaded ? (
        <ActivityIndicator
          size="large"
          animating={true}
        />
      ) : (
        <>
          {/* ... */}
        </>
      )}
    </Container>
  );
}

반환 호출에서 할 일을 변경하는 것을 잊지 마세요. 파스유저!.get사용자명()}{파스유저!.get이메일( ) }을 사용하세요.

이 시점에서 앱이 정상적으로 작동해야 합니다. 개발 서버를 다시 시작하고 계정을 만들고 로그인, 로그아웃, 자기소개를 변경하여 테스트합니다. 마지막으로 변경 사항이 데이터베이스에 반영되었는지 확인합니다.

결론

이제 인증의 정의, 인증의 이점, 인증과 인증의 비교에 대해 알아보았습니다.

또한 Back4app 인증을 설정하고 이를 React Native(Expo 기반) 애플리케이션과 통합하는 방법을 배웠습니다.

향후 단계

  1. Google, Facebook, Apple의 소셜 인증에 대해 알아보기
  2. Back4app 관리자 앱에서 멋진 실시간 관리 패널을 확인하세요.

최종 소스 코드는 GitHub에서 확인할 수 있습니다.


Leave a reply

Your email address will not be published.