Passport와 JWT를 이용하여 Auth 구현하기[3]

Written on August 15, 2019

앞선 포스팅을 통해 소셜로그인을 위한 서버쪽 로직은 완성되었다. 이제 클라이언트 쪽(React Native)은 어떻게 구현하는지 알아보자. ios 위주로만 작성하였다.

다른 API와 같이 axios를 썼다가 시간만 엄청 날렸다..😭 결론적으로 소셜로그인에서는 axios로 서버에 요청을 보내는 것이 아니라, React Native에서 웹페이지를 띄워주는 WebView의 연결 uri로 http://localhost:3000/auth/facebook를 설정해주면 된다.

소셜로그인

SignUpScreen.js

먼저, SignUpScreen 컴포넌트를 작성하는데, 소셜로그인 연결 버튼과, WebView를 띄워줄 Modal을 만든다. 이 컴포넌트 내에서 바로 WebView를 사용할 수도 있다.

//SignUpScreen.js
class SignUpScreen extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      socialModalVisible: false,
      source: undefined
    };
  }

  signupWithSocial = async social => {
    this.setState({
      socialModalVisible: !this.state.socialModalVisible,
      source: `http://localhost:3000/auth/${social}`,
    });
  };

  closeSocialModal = () => {
    this.setState({
      socialModalVisible: !this.state.socialModalVisible
    });
  };

  render() {
        const { source, socialModalVisible } = this.state;
        return (
          <View>
            {source !== undefined ? (
              <SocialWebviewModal
                visible={socialModalVisible}
                source={source}
                closeSocialModal={this.closeSocialModal}
              />
            ) : null}
            <TouchableOpacity
              onPress={() => this.signupWithSocial('facebook')}
            >
              <Text>Sign up with Facebook<Text/>
            </TouchableOpacity>
          </View>
        )
  }
}

SocialWebviewModal.js

이제 WebView가 렌더 될 Modal을 작성해 준다. WebView는 uri이나 html로 웹페이지를 띄우는데, source={{ uri: STRING }} 혹은 source={{ html: STRING }}를 props로 가져야 한다.

//SocialWebviewModal.js
const SocialWebviewModal = props => {
  return (
    <Modal
      animationType="slide"
      transparent={true}
      visible={props.visible}
      style={styles.container}
    >
      <SocialWebview
        source={{ uri: props.source }}
        closeSocialModal={props.closeSocialModal}
      />
    </Modal>
  );
};
export default SocialWebviewModal;

const styles = StyleSheet.create({
  container: {
    flex: 1
  }
});

SocialWebview.js

React Native의 WebView는 Deprecated 되었기 때문에, React Native Community의 WebView를 사용해야 한다.

ios의 경우 WKWebView와 UIWebView가 있는데, UIWebView 또한 Deprecated 되었기 때문에 WKWebView를 사용해야 하며, 이때, 웹뷰의 셋팅인 UserAgent를 입력해 주어야 한다.

import { WebView } from 'react-native-webview';

let userAgent =
  'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1';

const SocialWebview = props => {
  return (
    <SafeAreaView style={styles.container}>
      <WebView
        ref={ref => (this.webview = ref)}
        source={props.source}
        userAgent={userAgent}
        useWebKit={true}
        javaScriptEnabled={true}
      />
    </SafeAreaView>
  );
};
export default SocialWebview;
});

여기까지 하고나면 Facebook을 통해 로그인을 할 수 있게 되고, 로그인이 완료되면 로그인 결과({ userToken: STRING, success: BOOLEAN })가 Modal 창에 그대로 뜬다. 우리가 원하는 것은 로그인이 되면 모달창이 닫히고, 로그인 결과가 성공이면 userToken을 asyncStorage에 저장하는 것이다.

WebViewinjectedJavaScriptonMessage props로 WebView의 콘텐츠와 앱이 통신할 수 있다. injectedJavaScriptWebView내에 콘텐츠가 로딩되고 난 후 그 웹페이지에 자바스크립트 동작을 추가할 수 있는데, 로그인 요청에 대한 response는 html의 <pre>라는 태그로 감싸져 있다. 웹페이지 돔에 접근하여 response를 window.ReactNativeWebView.postMessage()를 통해 WebView에 전달하고, WebViewonMessage로 내용을 받아 후처리를 하면 된다.

  const INJECTED_JAVASCRIPT =
    '(function() {if(window.document.getElementsByTagName("pre").length>0){window.ReactNativeWebView.postMessage((window.document.getElementsByTagName("pre")[0].innerHTML));}})();';
  const _handleMessage = async event => {
    console.log(JSON.parse(event.nativeEvent.data));
    let result = JSON.parse(event.nativeEvent.data);
    let success = result.success;
    if (success) {
      let userToken = result.userToken;
      try {
        await AsyncStorage.setItem('userToken', userToken);
      } catch (e) {
        console.log(e);
      }
    }
    props.closeSocialModal();
  };

작성된 함수를 injectedJavaScriptonMessage props로 정의하면 끝!

      <WebView
        ref={ref => (this.webview = ref)}
        source={props.source}
        style={styles.webviewStyle}
        userAgent={userAgent}
        useWebKit={true}
        javaScriptEnabled={true}
        injectedJavaScript={INJECTED_JAVASCRIPT}
        onMessage={_handleMessage}
      />


관련 post

👩🏻‍💻 배우는 것을 즐기는 프론트엔드 개발자 입니다
부족한 블로그에 방문해 주셔서 감사합니다 🙇🏻‍♀️

in the process of becoming the best version of myself