프로젝트/할일목록 앱 (RN)

26. 소셜로그인 - 네이버 로그인 후 프로필 정보 보여주기

syleemomo 2023. 11. 10. 16:44
728x90

https://velog.io/@2ast/React-Native-%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC

 

React Native) 로그인에 대하여

아마 아무나 붙잡고 '로그인이 무엇인지 아느냐'라고 묻는다면 모두가 '안다'라고 답할 것이다. 우리는 평소에도 숨쉬듯이 로그인이라는 단어를 말하고, 실제로 의사소통에 큰 무리가 없을 정도

velog.io

 

npm install @react-native-async-storage/async-storage

리액트 네이티브에서 로컬스토리지를 사용하기 위하여 해당 라이브러리를 설치한다. 네이버 로그인후 발급받은 액세스 토큰을 저장하기 위함이다. 

import React, {useState, useEffect } from 'react';
import {SafeAreaView, Button, View, Text, ScrollView, StyleSheet, Dimensions, TouchableWithoutFeedback, Image } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage'
import { WebView } from 'react-native-webview';
import { useRoute } from '@react-navigation/native'

const NAVER_CLIENT_ID = 'KFv6qukJ1q8DqHRUGOs9'
const NAVER_CLIENT_SECRET = 'vMHgRoSTC3'
const STATE = encodeURIComponent("sunrise")
const REDIRECT_URI = encodeURIComponent('http://192.168.200.9:8081')
const SERVER_URL = 'http://192.168.200.9:8081'
const NAVER_AUTH_URL = `https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=${NAVER_CLIENT_ID}&state=${STATE}&redirect_uri=${REDIRECT_URI}`
console.log(NAVER_AUTH_URL)
const LOGIN_BTN_WIDTH = 300

const NaverLoginButton = ({navigation}) => {
  const route = useRoute();
  const { width, height } = Dimensions.get('window')
  const [loginStatus, setLoginStatus] = useState(false)
  const [userInfo, setUserInfo] = useState(null)

  const login = async () => {
    console.log("네이버 로그인시작")
    setLoginStatus(true)
  };

  const handleResponseFromNaverLogin = async (state) => {
      let url = state.url // 액세스토큰 발급을 위한 코드 추출
      console.log("로그인 응답 - ", url)
      let queryString = url.split('?')[1]
      console.log(queryString)
      let queryParameters = queryString.split('&')
      console.log(queryParameters)
      queryParameters = queryParameters.map(param => {
        const paramName = param.split('=')[0]
        const paramValue = param.split('=')[1]
        return { [paramName]: paramValue }
      })
      console.log("쿼리스트링: ", queryParameters)

      const code = queryParameters[0].code 
      // const state = queryParameters[1].state
      const TOKEN_URL = `https://nid.naver.com/oauth2.0/token?grant_type=authorization_code&client_id=${NAVER_CLIENT_ID}&client_secret=${NAVER_CLIENT_SECRET}&code=${code}&state=${STATE}`

      let result = await fetch(TOKEN_URL, {  // 액세스토큰 조회
        headers: { 'Content-Type': 'application/json' }
      })
      result = await result.json()
      console.log("최종 응답: ", result)
      console.log("액세스 토큰 - ", result.access_token)
      console.log("리프레쉬 토큰 - ", result.refresh_token)
      console.log("토큰만료(초) - ", result.expires_in)

      const naver_access_token = encodeURIComponent(result.access_token) // 프로필 정보 조회
      const PROFILE_URL = "https://openapi.naver.com/v1/nid/me"
      result = await fetch(PROFILE_URL, {
        headers: {
          'Authorization': `Bearer ${naver_access_token}`, 
        }
      })
      result = await result.json()
      console.log("최종 응답: ", result)
      const { response } = result
      // const { age, birthday, birthyear, email, gender, id, mobile, mobile_e164, name, nickname, profile_image } = response 
      console.log('-------------------- 네이버 사용자 정보 -------------------------')
      console.log('사용자 이름: ', response?.name)
      console.log('사용자 별명: ', response?.nickname)
      console.log('사용자 나이: ', response?.age)
      console.log('사용자 성별: ', response?.gender)
      console.log('사용자 생일: ', `${response?.birthyear}-${response?.birthday}`)
      console.log('사용자 이메일: ', response?.email)
      console.log('사용자 연락처: ', response?.mobile)

      if(response){
        // setUserInfo(response)
        setLoginStatus(false)
        AsyncStorage.setItem('NaverAccessToken', naver_access_token) // 로컬스토리지에 액세스토큰 저장
        navigation.navigate('App', { userInfo: response })
      }
  }

  return (
    <View
      style={styles.btnWrapper}>
        {loginStatus && <WebView
        source={{ uri: NAVER_AUTH_URL }}
        style={[styles.webView, {width, height}]}
        scrollEnabled
        onNavigationStateChange={(state) => handleResponseFromNaverLogin(state)}
      />}

      {/* {!loginStatus && userInfo && 
        <View>
          <Text style={styles.result}>네이버 로그인 성공</Text>
          <Text style={styles.result}>{userInfo.name && userInfo.name}</Text>
          <Text style={styles.result}>{userInfo.nickname && userInfo.nickname}</Text>
          <Text style={styles.result}>{userInfo.age && userInfo.age}</Text>
          <Text style={styles.result}>{userInfo.birthyear && userInfo.birthday && `${userInfo?.birthyear}-${userInfo?.birthday}`}</Text>
          <Text style={styles.result}>{userInfo.email && userInfo.email}</Text>
          <Text style={styles.result}>{userInfo.mobile && userInfo.mobile}</Text>
        </View>
      } */}
      
      {!loginStatus && <TouchableWithoutFeedback onPress={login}>
        <Image source={require('../assets/imgs/naver-login-btn.png')} style={[styles.loginBtn, {width: LOGIN_BTN_WIDTH, left: (width / 2) - (LOGIN_BTN_WIDTH / 2)}]}></Image>
      </TouchableWithoutFeedback>}
    </View>
  );
}

export default NaverLoginButton

const styles = StyleSheet.create({
  btnWrapper: {
    flex: 1,
    position: 'absolute',
    left: 0, top: 0, right: 0, bottom: 0,
    // backgroundColor: 'red'
  },
  webView: {
    position: 'absolute',
    left: 0, top: 0, right: 0, bottom: 0,
    // backgroundColor: 'yellow',
    zIndex: 5,
    elevation: 5,
  },
  loginBtn: {
    height: 40, 
    borderRadius: 3,
    position: 'absolute', 
    bottom: 150,
    // backgroundColor: 'blue',
    // zIndex: 1,
    // elevation: -1,
    // resizeMode: "contain",
  },
  result: {
    fontSize: 30,
    fontWeight: 'bold',
    color: '#fff'
  }
})

components > NaverLoginButton.js 파일에 위와 같이 작성한다. 

import AsyncStorage from '@react-native-async-storage/async-storage'

리액트 네이티브에서 로컬스토리지를 사용하기 위하여 해당 라이브러리를 임포트한다.

const LOGIN_BTN_WIDTH = 300

로그인버튼의 너비를 설정한다.

const NaverLoginButton = ({navigation}) => {
 // 중략
}

로그인후 홈화면으로 이동하기 위하여 navigation 객체를 props 로 전달받는다.

const naver_access_token = encodeURIComponent(result.access_token) // 프로필 정보 조회

네이버 로그인 개발자 가이드에 액세스 토큰은 URL 인코딩을 하는것이 좋다고 해서 코드를 수정하였다. 

result = await fetch(PROFILE_URL, {
    headers: {
      'Authorization': `Bearer ${result.access_token}`, 
    }
})

기존의 코드를 아래와 같이 변경하였다.

result = await fetch(PROFILE_URL, {
    headers: {
      'Authorization': `Bearer ${naver_access_token}`, 
    }
})

 

if(response){
    setUserInfo(response)
    setLoginStatus(false)
}

기존에는 로그인 성공후 랜딩페이지에서 프로필정보를 보여주기 위하여 위와 같이 작성하였다. 하지만 수정된 코드에서는 네이버 로그인 이후 발급받은 액세스 토큰을 로컬스토리지에 저장하고, 홈화면(App)으로 이동한다. 물론 화면전환하면서 프로필 정보를 함께 넘겨준다. 왜냐하면 홈화면에서는 프로필 정보에 포함된 이메일 정보로 해당 사용자가 작성한 할일목록만 쿼리(필터링)해야 하기 때문이다. 

if(response){
    // setUserInfo(response)
    setLoginStatus(false)
    AsyncStorage.setItem('NaverAccessToken', naver_access_token) // 로컬스토리지에 액세스토큰 저장
    navigation.navigate('App', { userInfo: response })
  }

프로필 정보는 더이상 랜딩페이지에서 보여주지 않으므로 주석처리하거나 제거한다.

{/* {!loginStatus && userInfo && 
    <View>
      <Text style={styles.result}>네이버 로그인 성공</Text>
      <Text style={styles.result}>{userInfo.name && userInfo.name}</Text>
      <Text style={styles.result}>{userInfo.nickname && userInfo.nickname}</Text>
      <Text style={styles.result}>{userInfo.age && userInfo.age}</Text>
      <Text style={styles.result}>{userInfo.birthyear && userInfo.birthday && `${userInfo?.birthyear}-${userInfo?.birthday}`}</Text>
      <Text style={styles.result}>{userInfo.email && userInfo.email}</Text>
      <Text style={styles.result}>{userInfo.mobile && userInfo.mobile}</Text>
    </View>
  } */}
<TouchableWithoutFeedback onPress={login}>
    <Image source={require('../assets/imgs/naver-login-btn.png')} style={styles.loginBtn}></Image>
</TouchableWithoutFeedback>

네이버 로그인 버튼을 보여주는 기존의 코드를 아래와 같이 수정하였다. 기존에는 웹뷰에서도 네이버 로그인 버튼이 보였다. 그래서 아래와 같이 loginStatus 가 false 인 경우에만 버튼이 보여지도록 하였다. 로그인버튼의 위치는 absolute 로 설정하고 left 값을 설정하여 화면 가로방향으로 중앙에 배치하였다. transform: translate(-50%, 0)를 적용한 것과 유사하다.

{!loginStatus && <TouchableWithoutFeedback onPress={login}>
    <Image source={require('../assets/imgs/naver-login-btn.png')} style={[styles.loginBtn, {width: LOGIN_BTN_WIDTH, left: (width / 2) - (LOGIN_BTN_WIDTH / 2)}]}></Image>
</TouchableWithoutFeedback>}

 

btnWrapper: {
    position: 'absolute',
    left: 0, right: 0,
    bottom: 150,
    // backgroundColor: 'red',
    alignItems: 'center'
  },

네이버 로그인버튼의 스타일을 아래와 같이 변경하였다. 버튼을 감싸는 컨테이너는 화면전체를 다 차지하도록 수정하였다. 

btnWrapper: {
    flex: 1,
    position: 'absolute',
    left: 0, top: 0, right: 0, bottom: 0,
    // backgroundColor: 'red'
  },

 

webView: {
    flex: 1,
  },

웹뷰도 마찬가지로 전체화면을 다 차지할 수 있도록 수정하였다. 또한 zIndex, elevation 을 설정하여 화면에서 항상 앞쪽에 보일수 있도록 하였다. 

webView: {
    position: 'absolute',
    left: 0, top: 0, right: 0, bottom: 0,
    // backgroundColor: 'yellow',
    zIndex: 5,
    elevation: 5,
  },
loginBtn: {
    width: 303, 
    height: 45, 
    borderRadius: 3
  },

실제 버튼의 스타일은 absolute 로 잡고 화면 밑바닥에서 150dp 정도 위에 배치하였다. 

loginBtn: {
    height: 40, 
    borderRadius: 3,
    position: 'absolute', 
    bottom: 150,
    // backgroundColor: 'blue',
    // zIndex: 1,
    // elevation: -1,
    // resizeMode: "contain",
  },

 

 

import React, { useEffect, useState } from 'react'
import { SafeAreaView, View, Text, StyleSheet, StatusBar, Button, Image, TouchableHighlight } from 'react-native'
import AsyncStorage from '@react-native-async-storage/async-storage'
import { GoogleSignin } from '@react-native-google-signin/google-signin'
import auth from '@react-native-firebase/auth'

function SettingsScreen({navigation}){
  const [userInfo, setUserInfo] = useState(null) 
  const [naverUserInfo, setNaverUserInfo] = useState(null)
 
  const googleSigninConfigure = () => { 
    GoogleSignin.configure({
      webClientId:
        '137262950194-gcouccatiffjp4ei8aqfi05uruv5j4dl.apps.googleusercontent.com',
    })
  }
  
  const signOut = async () => {
    const naver_access_token = await AsyncStorage.getItem('NaverAccessToken') // 로컬스토리지에 저장된 액세스토큰 조회

    if(naver_access_token){ // 네이버 로그아웃
      await AsyncStorage.removeItem('NaverAccessToken')
      navigation.navigate('Landing')
    }else{ // 구글 로그아웃
      try {
        await GoogleSignin.signOut()
        setUserInfo(null)
        navigation.navigate('Landing')
      } catch (error) {
        console.error('failed to logout, error: ', error)
      }
    }
  }

  getCurrentUser = async () => {
    const naver_access_token = await AsyncStorage.getItem('NaverAccessToken') // 로컬스토리지에 저장된 액세스토큰 조회

    if(naver_access_token){ // 네이버로그인 
      const PROFILE_URL = "https://openapi.naver.com/v1/nid/me"
      let result = await fetch(PROFILE_URL, {
          headers: {
            'Authorization': `Bearer ${naver_access_token}`, 
          }
        })
      result = await result.json()
      console.log("로그인한 네이버 사용자정보: ", result)
      const { response } = result

      if(response){
        setNaverUserInfo(response)
      }
    }else{ // 구글로그인
      googleSigninConfigure()
      const currentUser = await GoogleSignin.getCurrentUser()
      setUserInfo(currentUser)
    }
  }

  useEffect(() => {
    getCurrentUser()
  }, [])

  return (
    <SafeAreaView style={styles.block}>
      <StatusBar backgroundColor="#a8c8ffff"></StatusBar>
      <View>
        {userInfo && userInfo.user && 
        (<View style={styles.profileInfo}>
          <View>
            <Image source={{uri: userInfo.user.photo}} style={styles.profileImg}/>
          </View>
          <View style={styles.profileText}>
            <Text style={[styles.info, { fontWeight: 'bold', fontSize: 20 }]}>{userInfo.user.name}</Text>
            <Text style={styles.info}>{userInfo.user.email}</Text>
          </View>
        </View>)
        }
        {naverUserInfo && 
        (<View style={styles.profileInfo}>
          <View>
            <Image source={{uri: naverUserInfo.profile_image}} style={styles.profileImg}/>
          </View>
          <View style={styles.profileText}>
            <Text style={[styles.info, { fontWeight: 'bold', fontSize: 20 }]}>{naverUserInfo.nickname}</Text>
            <Text style={styles.info}>{naverUserInfo.email}</Text>
          </View>
        </View>)
        }
      </View>
      <TouchableHighlight onPress={signOut} style={styles.logoutBtnWrapper}>
          <View style={[styles.logoutBtn, { backgroundColor: "#a8c8ffff" }]}>
            <Text style={styles.logoutBtnText}>로그아웃</Text>
          </View>
      </TouchableHighlight>
    </SafeAreaView>
  )
}

const styles = StyleSheet.create({
  block: {
    flex: 1,
    justifyContent: 'flex-start',
  },
  profileInfo: {
    marginHorizontal: 'auto',
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
    padding: 20,
    backgroundColor: '#eee',
  },
  profileText: {
    borderRadius: 10,
    padding: 20,
  },
  info: {
    fontSize: 15,
    fontWeight: 'bold'
  },
  profileImg: {
    width: 50,
    height: 50,
    borderRadius: 25,
    marginLeft: 'auto',
    marginRight: 'auto',
  },
  logoutBtnWrapper: {
    flexDirection: 'row',
    position: 'absolute',
    bottom: 0
  },
  logoutBtn: {
    flex: 1,
    height: 35,
    justifyContent: 'center',
    alignItems: 'center',
    overflow: 'hidden',
  },
  logoutBtnText: {
    color: '#fff',
    letterSpacing: 3,
    fontWeight: 'bold',
    fontSize: 15,
    textAlign: 'center',
  },
  
})
export default SettingsScreen

screens > SettingsScreen.js 파일을 위와 같이 수정한다.

import AsyncStorage from '@react-native-async-storage/async-storage'

네이버 로그인 성공후 로컬 스토리지에 저장해뒀던 액세스 토큰을 조회하기 위하여 해당 라이브러리를 임포트한다.

const [naverUserInfo, setNaverUserInfo] = useState(null)

 네이버로부터 전달받은 프로필정보를 저장하기 위하여 state 를 정의한다. 

const signOutWithGoogle = async () => {
    try {
      await GoogleSignin.signOut()
      setUserInfo(null)
      navigation.navigate('Landing')
    } catch (error) {
      console.error('failed to logout, error: ', error)
    }
  }

기존의 로그아웃 로직을 아래와 같이 수정하였다. 기존에는 구글 로그아웃만 수행하면 되었지만 현재는 네이버 로그아웃을 따로 해주어야 한다. 그러한 이유로 로컬 스토리지에서 네이버 액세스 토큰을 조회하여 토큰이 존재하면 네이버로 로그인한 경우이고 그렇지 않으면 구글로 로그인한 경우이다. 조건문으로 분기한 다음에 네이버 로그아웃의 경우에는 로컬스토리지에 존재하는 액세스 토큰을 제거하고 랜딩페이지로 이동한다. 액세스 토큰을 제거하더라도 실질적인 로그아웃이 된 것은 아니지만 현재 투두앱에서는 더이상 네이버에서 프로필 정보를 조회하지 못하므로 로그아웃하였다고 간주할 수 있다.

const signOut = async () => {
    const naver_access_token = await AsyncStorage.getItem('NaverAccessToken') // 로컬스토리지에 저장된 액세스토큰 조회

    if(naver_access_token){ // 네이버 로그아웃
      await AsyncStorage.removeItem('NaverAccessToken')
      navigation.navigate('Landing')
    }else{ // 구글 로그아웃
      try {
        await GoogleSignin.signOut()
        setUserInfo(null)
        navigation.navigate('Landing')
      } catch (error) {
        console.error('failed to logout, error: ', error)
      }
    }
  }

 

getCurrentUser = async () => {
    const currentUser = await GoogleSignin.getCurrentUser()
    setUserInfo(currentUser)
  }

현재 사용자의 프로필 정보를 조회하는 기존의 코드를 아래와 같이 수정하였다. 

getCurrentUser = async () => {
    const naver_access_token = await AsyncStorage.getItem('NaverAccessToken') // 로컬스토리지에 저장된 액세스토큰 조회

    if(naver_access_token){ // 네이버로그인 
      const PROFILE_URL = "https://openapi.naver.com/v1/nid/me"
      let result = await fetch(PROFILE_URL, {
          headers: {
            'Authorization': `Bearer ${naver_access_token}`, 
          }
        })
      result = await result.json()
      console.log("로그인한 네이버 사용자정보: ", result)
      const { response } = result

      if(response){
        setNaverUserInfo(response)
      }
    }else{ // 구글로그인
      googleSigninConfigure()
      const currentUser = await GoogleSignin.getCurrentUser()
      setUserInfo(currentUser)
    }
  }

로그아웃과 동일하게 네이버 액세스 토큰의 존재여부에 따라 조건문을 적용해서 분기하였다. 네이버 액세스 토큰이 존재하면 해당 토큰을 이용하여 프로필 정보를 조회하고 setNaverUserInfo 함수로 프로필 정보를 state 에 저장한다. 구글 로그인은 동일하지만 googleSigninConfigure 함수는 더이상 useEffect 에서 실행할 필요가 없으므로 위와 같이 구글로그인인 경우에만 실행하도록 코드를 수정하였다. 

useEffect(() => {
    getCurrentUser()
  }, [])

useEffect 에서 googleSigninConfigure 함수가 제외되었다. 

{naverUserInfo && 
    (<View style={styles.profileInfo}>
      <View>
        <Image source={{uri: naverUserInfo.profile_image}} style={styles.profileImg}/>
      </View>
      <View style={styles.profileText}>
        <Text style={[styles.info, { fontWeight: 'bold', fontSize: 20 }]}>{naverUserInfo.nickname}</Text>
        <Text style={styles.info}>{naverUserInfo.email}</Text>
      </View>
    </View>)
}

네이버 로그인에 성공하여 프로필 정보가 존재하면(naverUserInfo) 네이버의 프로필 정보를 화면에 보여줄 수 있도록 위와 같은 UI를 추가해준다. 구글 프로필 정보를 보여주는 UI와 스타일은 동일하다. 다만, 네이버와 구글의 프로필 정보를 담고있는 데이터 포맷이 다르기 때문에 각각 다른 이름의 프로퍼티를 조회하여 넣어주었다. 

<TouchableHighlight onPress={signOut} style={styles.logoutBtnWrapper}>
 // 중략
</TouchableHighlight>

onPress 에 설정된 이벤트 핸들러 함수의 이름을 signOut 으로 변경하였다.

 

import React, { useState, useEffect } from 'react'
import { SafeAreaView, View, Text, StyleSheet, StatusBar, ScrollView, Dimensions, ImageBackground  } from 'react-native'
import LandingPage from '../components/LandingPage'
import landingData from '../data/landingData'
import LoginButton from '../components/LoginButton';
import NaverLoginButton from '../components/NaverLoginButton';

import { GoogleSignin } from '@react-native-google-signin/google-signin'
import AsyncStorage from '@react-native-async-storage/async-storage'

function LandingScreen({navigation}){
    const { width, height } = Dimensions.get('window')
    const [currentPageIndex, setCurrentPageIndex] = useState(0)
    console.log('페이지 번호: ', currentPageIndex)

    const setCurrentPage = (e) => {
        const { x } = e.nativeEvent.contentOffset // x : 스크롤 위치
        console.log("스크롤 위치: ", x, "화면너비: ", width)
        const nextPageIndex = Math.ceil(x / width) // x / width : 스크롤 위치 / 화면너비 -> 페이지번호
        console.log(nextPageIndex)
        if(nextPageIndex !== currentPageIndex){
            setCurrentPageIndex(nextPageIndex)
        }
    }

    const getUserInfo = async () => {
        // await GoogleSignin.signOut()
        const result = await GoogleSignin.getCurrentUser()
        console.log("구글 사용자정보: ", result)
        if(result){
            // result.then(user => {
            //     console.log('user : ',user)
            //     if(user){
                  navigation.navigate('App', { userInfo: result.user })
        //         }
        //     })
        }
    }
    const getNaverUsreInfo = async (naver_access_token) => {
        const PROFILE_URL = "https://openapi.naver.com/v1/nid/me"
        let result = await fetch(PROFILE_URL, {
            headers: {
                'Authorization': `Bearer ${naver_access_token}`, 
            }
        })
        result = await result.json()
        console.log("로그인한 네이버 사용자정보: ", result)
        const { response } = result
        navigation.navigate('App', { userInfo: response })
    }
    const goHomeIfLogined = async () => {
        const naver_access_token = await AsyncStorage.getItem('NaverAccessToken') // 로컬스토리지에 저장된 액세스토큰 조회

        // 네이버 로그인여부 체크
        if(naver_access_token){ // 네이버 로그인
            getNaverUsreInfo(naver_access_token)
        }else{ // 구글 로그인
            getUserInfo()
        }
    }
    useEffect(() => {
        goHomeIfLogined()
    }, [])

    return (
        <>
            <StatusBar hidden></StatusBar>
            <SafeAreaView style={styles.block}>
                <ScrollView
                    style={{ flex: 1 }}
                    horizontal={true} // 수평 스크롤링
                    scrollEventThrottle={16} // 스크롤 이벤트 감지하는 시간간격 (ms)
                    pagingEnabled={true} // 스크롤시 페이지네이션
                    showsHorizontalScrollIndicator={false} // 스크롤바 숨기기
                    onScroll={setCurrentPage}
                >
                    {landingData.map((page, index) => (
                        <LandingPage
                            width={width}
                            height={height}
                            {...page}
                            key={index}
                        />
                    ))}
                </ScrollView>

                <View style={styles.scrollIndicatorWrapper}>
                    {Array(3).fill(0).map((_, index) => (
                        <View key={index} style={[styles.scrollIndicator, { opacity: currentPageIndex === index ? 1: 0.3}]}></View>
                    ))}
                </View>

                <LoginButton navigation={navigation}/>
                <NaverLoginButton navigation={navigation}/>
            </SafeAreaView>
        </>
    )
}

const styles = StyleSheet.create({
  block: {
    flex: 1
  },
  scrollIndicatorWrapper:{
    position: 'absolute',
    left: 0, right: 0,
    bottom: 50,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center'
  },
  scrollIndicator: {
    height: 10,
    width: 10,
    borderRadius: 10 / 2,
    backgroundColor: '#aaa',
    marginLeft: 10,
  },

})
export default LandingScreen

screens > LandingScreen.js 파일을 위와 같이 수정한다. 

import AsyncStorage from '@react-native-async-storage/async-storage'

랜딩페이지에서는 로그인 여부에 따라 곧바로 홈화면으로 이동할 수도 있기 때문에 네이버 액세스 토큰을 조회하기 위하여 위와 같이 해당 라이브러리를 임포트한다.

const getUserInfo = async () => await GoogleSignin.getCurrentUser()

기존에 구글로그인 여부를 판단하는 코드를 아래와 같이 수정하였다. useEffect 안에 있는 코드까지 다 포함하였다. result 의 데이터 자료형은 프로미스가 아니라 객체이므로 then 을 사용할 수 없어서 에러가 발생하였다. 그래서 아래와 같이 그냥 홈화면으로 곧바로 전환하도록 하였다. 

const getUserInfo = async () => {
        // await GoogleSignin.signOut()
        const result = await GoogleSignin.getCurrentUser()
        console.log("구글 사용자정보: ", result)
        if(result){
            // result.then(user => {
            //     console.log('user : ',user)
            //     if(user){
                  navigation.navigate('App', { userInfo: result.user })
        //         }
        //     })
        }
}

네이버 로그인 여부를 판단하고 프로필 정보를 조회하는 아래 코드가 추가되었다. 네이버에서 발급받은 액세스 토큰이 존재하면 해당 토큰으로 프로필 정보를 조회하고 홈화면(App)으로 이동한다. 즉, 앱을 종료하였다가 다시 실행하면 로그인 여부에 따라 랜딩페이지에 머물거나 곧바로 홈화면으로 이동한다. 

const getNaverUsreInfo = async (naver_access_token) => {
        const PROFILE_URL = "https://openapi.naver.com/v1/nid/me"
        let result = await fetch(PROFILE_URL, {
            headers: {
                'Authorization': `Bearer ${naver_access_token}`, 
            }
        })
        result = await result.json()
        console.log("로그인한 네이버 사용자정보: ", result)
        const { response } = result
        navigation.navigate('App', { userInfo: response })
    }

로컬 스토리지에서 네이버 액세스 토큰을 조회하고, 해당 토큰의 유무에 따라 네이버 또는 구글에서 서로 다른 프로필 정보를 조회할 수 있도록 하였다. useEffect 에서 해당 코드를 바로 처리하지 않고, goHomeIfLogined 함수로 한번 더 감싸준 이유는 useEffect 의 콜백함수에는 async 키워드를 붙이지 못하기 때문이다. 

const goHomeIfLogined = async () => {
        const naver_access_token = await AsyncStorage.getItem('NaverAccessToken') // 로컬스토리지에 저장된 액세스토큰 조회

        // 네이버 로그인여부 체크
        if(naver_access_token){ // 네이버 로그인
            getNaverUsreInfo(naver_access_token)
        }else{ // 구글 로그인
            getUserInfo()
        }
    }

useEffect 함수에서는 단순히 goHomeIfLogined 함수만 처리한다. 

useEffect(() => {
    goHomeIfLogined()
}, [])

네이버 로그인버튼에서 로그인 성공시 홈화면으로 이동할 수 있도록 navigation 속성을 추가하여 navigation 객체를 전달해준다. 

<NaverLoginButton navigation={navigation}/>

 

랜딩페이지 소셜로그인 구현화면

 

네이버 프로필 정보

728x90