맨땅에 코딩

RE:BORN Frontend - React Native Naver Login 구현 본문

KAU 2024 (3학년)/산학프로젝트

RE:BORN Frontend - React Native Naver Login 구현

나는 푸딩 2025. 2. 21. 10:20

안녕하세요 오랜만에 산학 개발 일지? 를 작성하러 왔습니다.

한창 RE:BORN 프로젝트 리뉴얼을 준비 중인데요, 리뉴얼 된 버전에서는 네이버, 카카오, 구글 이렇게 3가지의 소셜 로그인을 구현할 계획입니다.

 

그래서 이전에 산학프로젝트에서 네이버 소셜 로그인 했던 걸 들쳐내봅니다...

지금 다시 보면 코드 진짜 더럽다고 생각할텐데.. (이땐 코드를 이쁘게 짜는거고 뭐고 그냥 급하게 호다닥 했습니다..)

 

아무튼 어떻게 구현했는지 알아봅시다.

 

먼저, 구글링을 통해 탐색하던 중

https://github.com/crossplatformkorea/react-native-naver-login

 

GitHub - crossplatformkorea/react-native-naver-login: 리엑트 네이티브 네이버 로그인 라이브러리

리엑트 네이티브 네이버 로그인 라이브러리. Contribute to crossplatformkorea/react-native-naver-login development by creating an account on GitHub.

github.com

참고할만한 깃허브 소스를 찾아냈고, 이 소스를 활용하여 구현해 보았습니다.

 

일단 다음 패키지 install이 필요합니다.

npm i @react-native-seoul/naver-login

 

그리고 다음과 같이 코드를 작성해주었습니다.

 

LoginScreen.js

import React, { useState } from "react";
import { View, Text, StyleSheet, Image, TouchableOpacity } from "react-native";
import axios from "axios";
import { buttonStyles } from "../../components";
import { colors } from "../../theme";
import NaverLogin from "@react-native-seoul/naver-login";
import { useAccessToken, useDeviceToken } from "../../context/AccessTokenContext";

const consumerKey = "";
const consumerSecret = "";
const appName = "Hello";
const serviceUrlScheme = "navertest";

const LoginScreen = ({ navigation: { navigate } }) => {
  const [success, setSuccessResponse] = useState();
  const [failure, setFailureResponse] = useState();
  const [getProfileRes, setGetProfileRes] = useState();
  const [nickname, setNickname] = useState("");
  const [email, setEmail] = useState("");
  const { setAccessToken } = useAccessToken();
  const { deviceToken } = useDeviceToken();

  const sendUserProfileToServer = async (accessToken) => {
    try {
      const profileResult = await NaverLogin.getProfile(accessToken);
      if (profileResult) {
        const { nickname, email } = profileResult.response;
        const username = "{naver}" + email.split("@")[0];
        const provider = "naver";

        setNickname(nickname);
        setEmail(email);
        setGetProfileRes(profileResult);

        const userData = {
          email,
          username,
          nickname,
          provider,
          deviceToken,
        };

        axios
          .post("http://reborn.persi0815.site/token/generate", userData)
          .then((response) => {
            const { accessToken, signIn } = response.data.result;
            setAccessToken(accessToken);
            if (signIn === "wasUser") {
              navigate("Tabs", { screen: "main" });
            } else navigate("IntroStack", { screen: "Intro" });
          })
          .catch((error) => {
            console.error("ERROR", error);
            if (error.response) {
              // 요청이 이루어졌으나 서버가 2xx 이외의 상태 코드로 응답
              console.error("Error Response:", error.response.data);
              console.error("Status:", error.response.status);
              console.error("Headers:", error.response.headers);
            } else if (error.request) {
              // 요청이 이루어졌으나 응답을 받지 못함
              console.error("Error Request:", error.request);
            } else {
              // 요청을 만들 때 문제가 발생함
              console.error("Error Message:", error.message);
            }
          });
      }
    } catch (e) {
      setGetProfileRes(undefined);
      setNickname("");
      setEmail("");
    }
  };

  const login = async () => {
    try {
      const { failureResponse, successResponse } = await NaverLogin.login({
        appName,
        consumerKey,
        consumerSecret,
        serviceUrlScheme,
      });

      setSuccessResponse(successResponse);
      setFailureResponse(failureResponse);

      if (successResponse) {
        setAccessToken(successResponse.accessToken);
        sendUserProfileToServer(successResponse.accessToken);
      }
    } catch (error) {
      console.error("로그인 에러:", error);
    }
  };

  return (
    <View style={styles.container}>
      <View style={styles.introContainer}>
        <Text style={styles.introTitle}>
          PET <Text style={{ color: colors.palette.Yellow }}>RE</Text>BORN,
          {"\n"}로그인 하기
        </Text>
      </View>
      <View style={styles.contentContainer}>
        <Image
          style={{ width: "90%", height: "83%", bottom: "8%", resizeMode: 'center' }}
          source={require("../../Assets/icons/app_icon22.png")}
        />
        <View style={styles.buttonContainer}>
          <TouchableOpacity style={buttonStyles.buttonLogin} onPress={login}>
            <View style={styles.buttonContent}>
              <Image
                source={require("../../Assets/icons/naver_logo.png")}
                style={styles.naverLogo}
              />
              <Text style={styles.buttonText}>네이버 아이디로 로그인 </Text>
            </View>
          </TouchableOpacity>
        </View>
      </View>
    </View>
  );
};
export default LoginScreen;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: colors.palette.White,
  },
  introContainer: {
    marginTop: "10%",
  },
  contentContainer: {
    alignItems: "center",
  },
  introTitle: {
    fontSize: 45,
    textAlign: "left",
    paddingLeft: "5%",
    fontFamily: "Poppins-SemiBold",
    color: colors.palette.BrownDark,
  },
  buttonContainer: {
    position: "absolute",
    top: "70%",
    bottom: "20%",
  },
  buttonContent: {
    flexDirection: "row",
    alignItems: "center",
  },
  naverLogo: {
    width: "10%",
    height: "100%",
    marginRight: "5%",
  },
  buttonText: {
    textAlign: "center",
    color: colors.palette.White,
    fontFamily: "Poppins-Medium",
    fontSize: 14,
  },
});

 

로직을 간단하게 설명하자면 다음과 같습니다.

 

 

1. 네이버 로그인 처리 (login 함수)

- @react-native-seoul/naver-login 패키지를 사용하여 로그인 요청을 보냄

- 로그인 성공 시 successResponse.accessToken을 useAccessToken의 setAccessToken을 통해 저장

- 이후 sendUserProfileToServer 함수를 호출하여 사용자 정보를 서버에 전송

 

2. 네이버 프로필 정보 가져오기 (sendUserProfileToServer 함수)

- NaverLogin.getProfile(accessToken)을 사용하여 프로필 정보를 가져옴

- 사용자 정보(닉네임, 이메일)를 상태 변수(nickname, email)에 저장

- 백엔드에 POST 요청을 보내 사용자 정보를 저장 및 처리

- 응답을 바탕으로 Tabs/main 또는 IntroStack/Intro 화면으로 이동

 

3. 에러 핸들링

- 로그인 실패 시 failureResponse에 저장

- 서버 응답 실패 시 catch 문을 통해 에러 상세 정보를 출력

 

근데 뭐, 코드가 생각 보다 더러울 줄 알았는데, 급하게 한 것 치고는 나쁘지 않은 것 같다.

활용할 소스가 제대로 잘 되어있어서 그랬는지..

 

이번에 리뉴얼 할때는 3가지의 소셜 로그인이 가능하도록 구현해야하는데, 네이버 소셜 로그인을 구현했던 경험을 토대로

잘 구현이 되길 바랄 뿐...!

 

고럼 이만~