본문 바로가기
리액트

리액트 네이티브와 Expo로 아이폰 앱 만들기: 로그인, 회원가입 백엔드 api 연결하기

by 플라퉁 2023. 8. 7.
728x90
반응형

안녕하세요 오늘은 리액트 네이티브를 사용한 간단한 앱에서 로그인,회원가입 기능을 구현하여보겠습니다.

 

저번과 다른점은 타입스크립트로 변경하였고 

 

상태관리를 위해 recoil을 적용하였습니다.

 

일단 프로젝트의 구조를 먼저 살펴보겠습니다.

저번에 추가하여 회원가입 스크린을 만들고 리코일 아톰을 추가하였습니다.

 

먼저 auth.ts입니다.

 

import { atom } from "recoil";
import axios from "axios";
import AsyncStorage from "@react-native-async-storage/async-storage";
import instance from "../../axios/AxiosInstance";

interface User {
  username: string;
  userId: string;
  token?: string;
}

export const userState = atom<User>({
  key: "user",
  default: { username: "", userId: "" }
});

export const isLoggedInState = atom<boolean>({
  key: "isLoggedIn",
  default: false
});

export const updateLoginStatus = async () => {
  const token = await AsyncStorage.getItem("token");
  return token ? true : false;
};

export const updateAccessToken = async (refreshToken: string) => {
  const response: any = await axios.post(
    "http://localhost:8093/api/update-token",
    { refreshToken }
  );
  const newAccessToken = response.data.accessToken;
  await AsyncStorage.setItem("token", newAccessToken);
  return newAccessToken;
};

export const login = async (formData: { userId: string; password: string }) => {
  const response = await axios.post(
    "http://localhost:8093/api/login",
    formData
  );
  const { username, userId, token, refreshToken } = response.data;
  console.log("Login response userId:", userId); // 응답으로 받은 userId 확인
  await AsyncStorage.setItem("token", token); // 로컬 스토리지에 토큰 저장
  await AsyncStorage.setItem("userId", userId); // 로컬 스토리지에 userId 저장
  await AsyncStorage.setItem("refreshToken", refreshToken); // 로컬 스토리지에 리프레쉬 토큰 저장
  return { username, userId, token, refreshToken };
};

export const signup = async (formData: {
  username: string;
  userId: string;
  password: string;
}) => {
  const response = await axios.post(
    "http://localhost:8093/api/signup",
    formData
  );
  const { username, userId, token, refreshToken } = response.data;

  await AsyncStorage.setItem("token", token); // 로컬 스토리지에 토큰 저장
  await AsyncStorage.setItem("userId", userId); // 로컬 스토리지에 userId 저장
  await AsyncStorage.setItem("refreshToken", refreshToken); // 로컬 스토리지에 리프레쉬 토큰 저장
  return { username, userId, token, refreshToken };
};

export const logout = async () => {
  try {
    await axios.post("http://localhost:8093/api/logout");

    // 로컬 스토리지에서 토큰 제거
    await AsyncStorage.removeItem("token");
    await AsyncStorage.removeItem("refreshToken");
    await AsyncStorage.removeItem("userId"); // 로컬 스토리지에서 userId 제거
  } catch (error) {
    console.error("Error during logout:", error);
  }
};

백엔드 api에서 받은 토큰들을 AsyncStorage 에 저장하여 관리합니다.

 

다음은 새로 추가한 회원가입 창입니다.

 

import React, { useState } from "react";
import { StyleSheet, Text, View, Button, TextInput } from "react-native";
import { StackNavigationProp } from "@react-navigation/stack";
import { RootStackParamList } from "../App";
import { signup } from "../recoil/atoms/auth"; // 실제 파일 경로로 수정해주세요.
import { useSetRecoilState } from "recoil";
import { userState, isLoggedInState } from "../recoil/atoms/auth"; // userState와 isLoggedInState를 정의한 파일 경로로 수정해주세요.

type SignupScreenNavigationProp = StackNavigationProp<
  RootStackParamList,
  "Register"
>;

type Props = {
  navigation: SignupScreenNavigationProp;
};

const SignupScreen: React.FC<Props> = ({ navigation }) => {
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [userId, setUserId] = useState("");
  const setUser = useSetRecoilState(userState);
  const setIsLoggedIn = useSetRecoilState(isLoggedInState);

  const handleSignup = async () => {
    // 회원가입 API 호출 및 회원가입 로직 처리
    try {
      const {
        username: loggedInUsername,
        userId: loggedInUserId,
        token
      } = await signup({ username, userId, password });
      setUser({ username: loggedInUsername, userId: loggedInUserId, token });
      setIsLoggedIn(true);
      console.log(`Signup successful for userId: ${loggedInUserId}`);

      // 여기서 사용자를 다음 화면(예: 메인 화면)으로 이동시킬 수 있습니다.
      navigation.navigate("Home");
    } catch (error) {
      console.error("Error during signup:", error);
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Sign Up</Text>
      <TextInput
        style={styles.input}
        placeholder="Username"
        value={username}
        onChangeText={setUsername}
      />
      <TextInput
        style={styles.input}
        placeholder="Password"
        secureTextEntry
        value={password}
        onChangeText={setPassword}
      />
      <TextInput
        style={styles.input}
        placeholder="UserId"
        value={userId}
        onChangeText={setUserId}
      />
      <Button title="Sign Up" onPress={handleSignup} />
      <Button
        title="Back to Login"
        onPress={() => navigation.navigate("Login")}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center"
  },
  title: {
    fontSize: 24,
    marginBottom: 20
  },
  input: {
    width: "80%",
    padding: 10,
    borderWidth: 1,
    borderColor: "#ccc",
    borderRadius: 5,
    marginBottom: 10
  }
});

export default SignupScreen;

리액트 네이티브를 사용하니까 앱도 웹과 유사하게 코드를 작성할 수 있어

 

편했습니다.

 

깃헙 주소 첨부합니다.https://github.com/muganghskim/nativeTest

 

GitHub - muganghskim/nativeTest

Contribute to muganghskim/nativeTest development by creating an account on GitHub.

github.com

 

localhost로 api 요청 시 네트워크 에러가 발생하였는데 정확한 ip 주소를 입력하여 에러를 해결하였습니다.

 

리프레쉬 토큰을 사용하여 로그인을 유지시키는 방법은 아직 미구현하였습니다.

 

감사합니다.

728x90
반응형

댓글