Facebook 로그인을 AWS Amplify와 Cognito로 구현하기

지금까지의 가이드에서는 Cognito 사용자 풀을 사용하여 데모 노트 앱에 사용자를 등록했습니다. 이는 사용자가 이메일과 비밀번호로 계정을 등록해야 함을 의미합니다. 하지만 여러분은 사용자가 Facebook이나 Google 계정으로 앱에 가입할 수 있기를 원할 수 있습니다. 이렇게 하면 사용자가 사이트마다 다른 이메일과 비밀번호 조합을 기억할 필요가 없어집니다. 이번 장에서는 데모 앱에 “Facebook으로 로그인” 옵션을 추가하는 방법을 살펴보겠습니다.

이번 장에서 사용된 노트 앱 버전은 다음에서 확인할 수 있습니다:

이번 장의 주요 아이디어와 코드는 오랜 독자이자 기여자인 Peter Eman Paver Abastillas가 제공했습니다.

시작하기 전에 사용자가 로그인할 Facebook 앱을 만들어 보겠습니다.

Facebook 앱 만들기

http://developers.facebook.com/로 이동하여 My Apps > Add New App을 클릭해 새 앱을 만드세요.

Facebook 앱 생성 스크린샷

Facebook Login 아래에서 Set Up을 선택하세요.

Facebook Login 선택 스크린샷

그리고 Web을 선택하세요.

Login을 위한 Web 옵션 선택 스크린샷

Quickstart의 첫 번째 단계에서 앱의 URL을 http://localhost:3000으로 설정하세요. 또는 Create React App에서 HTTPS 옵션을 사용한다면 https://localhost:3000으로 설정하세요. Save를 클릭하세요.

웹사이트 URL 설정 스크린샷

Continue를 클릭해 Quickstart의 나머지 단계를 진행할 수 있습니다.

Quickstart 완료 스크린샷

마지막으로 Settings > Basic으로 이동해 App ID를 메모해 두세요.

설정에서 App ID 복사 스크린샷

이 App ID는 AWS와 React 부분을 설정할 때 필요합니다.

다음으로 Cognito Identity Pool을 사용해 아이덴티티를 연동할 것입니다. 이는 사용자가 Facebook 계정으로 가입하면 Identity Pool에 추가되고, 서버리스 백엔드 API에서 사용할 수 있는 ID를 얻게 된다는 의미입니다. 이 ID는 사용자가 나중에 다시 로그인해도 동일하게 유지됩니다. Identity Pool과 User Pool의 차이점이 헷갈린다면 Cognito user pool vs identity pool 챕터를 참고하세요.

Facebook를 인증 프로바이더로 추가하기

AWS 콘솔로 이동한 후, Cognito로 가서 Manage Identity Pools를 클릭하세요.

Manage Identity Pools 선택 스크린샷

앱에서 사용 중인 Identity Pool을 선택하세요.

앱용 Identity Pool 선택 스크린샷

상단에서 Edit identity pool을 클릭하세요.

Edit identity pool 클릭 스크린샷

아래로 스크롤하여 Authentication providers를 확장하세요.

Authentication providers 확장 스크린샷

기본 옵션으로 Cognito가 설정되어 있는 것을 확인할 수 있습니다. Facebook 탭을 선택하고 Unlock을 클릭한 후, 위에서 복사한 Facebook App ID를 붙여넣으세요.

Authentication providers에 Facebook App ID 설정 스크린샷

아래로 스크롤하여 Save Changes를 클릭하세요.

Identity Pool에서 Save Changes 클릭 스크린샷

이제 AWS 설정이 완료되었으니, React 앱으로 이동해 보겠습니다.

AWS Amplify로 Facebook 로그인 설정하기

React 앱에서 Facebook JS SDK와 AWS Amplify를 사용해 Facebook 로그인을 설정할 것입니다. 작업 중인 앱 버전은 GitHub 저장소에서 확인할 수 있습니다.

주요 변경 사항을 간단히 살펴보겠습니다.

Change indicator 먼저 Facebook App ID를 src/config.js에 추가합니다. 파일은 다음과 같이 보일 것입니다.

export default {
  s3: {
    REGION: "YOUR_S3_UPLOADS_BUCKET_REGION",
    BUCKET: "YOUR_S3_UPLOADS_BUCKET_NAME"
  },
  apiGateway: {
    REGION: "YOUR_API_GATEWAY_REGION",
    URL: "YOUR_API_GATEWAY_URL"
  },
  cognito: {
    REGION: "YOUR_COGNITO_REGION",
    USER_POOL_ID: "YOUR_COGNITO_USER_POOL_ID",
    APP_CLIENT_ID: "YOUR_COGNITO_APP_CLIENT_ID",
    IDENTITY_POOL_ID: "YOUR_IDENTITY_POOL_ID"
  },
  social: {
    FB: "YOUR_FACEBOOK_APP_ID"
  }
};

YOUR_FACEBOOK_APP_ID를 위에서 얻은 ID로 바꿔주세요.

Change indicator 다음으로 src/App.jscomponentDidMount 메서드에서 Facebook JS SDK를 로드합니다.

async componentDidMount() {
  this.loadFacebookSDK();

  try {
    await Auth.currentAuthenticatedUser();
    this.userHasAuthenticated(true);
  } catch (e) {
    if (e !== "not authenticated") {
      alert(e);
    }
  }

  this.setState({ isAuthenticating: false });
}

loadFacebookSDK() {
  window.fbAsyncInit = function() {
    window.FB.init({
      appId            : config.social.FB,
      autoLogAppEvents : true,
      xfbml            : true,
      version          : 'v3.1'
    });
  };

  (function(d, s, id){
     var js, fjs = d.getElementsByTagName(s)[0];
     if (d.getElementById(id)) {return;}
     js = d.createElement(s); js.id = id;
     js.src = "https://connect.facebook.net/en_US/sdk.js";
     fjs.parentNode.insertBefore(js, fjs);
   }(document, 'script', 'facebook-jssdk'));
}

또한 Auth.currentAuthenticatedUser 메서드를 사용해 현재 인증된 사용자를 로드합니다. 여기서 Auth는 AWS Amplify 패키지의 일부입니다.

Change indicator src/App.js 상단에서 config를 임포트하는 것을 잊지 마세요.

import config from "./config";

Change indicator 이제 src/components/FacebookButton.js에 Facebook 로그인 버튼 컴포넌트를 만듭니다.

import React, { Component } from "react";
import { Auth } from "aws-amplify";
import LoaderButton from "./LoaderButton";

function waitForInit() {
  return new Promise((res, rej) => {
    const hasFbLoaded = () => {
      if (window.FB) {
        res();
      } else {
        setTimeout(hasFbLoaded, 300);
      }
    };
    hasFbLoaded();
  });
}

export default class FacebookButton extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isLoading: true
    };
  }

  async componentDidMount() {
    await waitForInit();
    this.setState({ isLoading: false });
  }

  statusChangeCallback = response => {
    if (response.status === "connected") {
      this.handleResponse(response.authResponse);
    } else {
      this.handleError(response);
    }
  };

  checkLoginState = () => {
    window.FB.getLoginStatus(this.statusChangeCallback);
  };

  handleClick = () => {
    window.FB.login(this.checkLoginState, {scope: "public_profile,email"});
  };

  handleError(error) {
    alert(error);
  }

  async handleResponse(data) {
    const { email, accessToken: token, expiresIn } = data;
    const expires_at = expiresIn * 1000 + new Date().getTime();
    const user = { email };

    this.setState({ isLoading: true });

    try {
      const response = await Auth.federatedSignIn(
        "facebook",
        { token, expires_at },
        user
      );
      this.setState({ isLoading: false });
      this.props.onLogin(response);
    } catch (e) {
      this.setState({ isLoading: false });
      this.handleError(e);
    }
  }

  render() {
    return (
      <LoaderButton
        block
        bsSize="large"
        bsStyle="primary"
        className="FacebookButton"
        text="Login with Facebook"
        onClick={this.handleClick}
        disabled={this.state.isLoading}
      />
    );
  }
}

여기서 어떤 작업을 하는지 간단히 살펴보겠습니다.

  1. 먼저 waitForInit 메서드에서 Facebook JS SDK가 로드될 때까지 기다립니다. 로드가 완료되면 Login with Facebook 버튼을 활성화합니다.

  2. 사용자가 버튼을 클릭하면 FB.login을 사용해 로그인 프로세스를 시작하고, statusChangeCallback에서 로그인 상태 변경을 감지합니다. 이 메서드를 호출할 때 {scope: "public_profile,email"}을 설정해 사용자의 공개 프로필과 이메일 주소를 요청합니다.

  3. 사용자가 앱에 권한을 부여하면 Facebook에서 받은 정보(사용자의 이메일)를 사용해 AWS Amplify의 Auth.federatedSignIn 메서드를 호출합니다. 이렇게 하면 사용자가 로그인됩니다.

Change indicator 마지막으로 FacebookButton.jssrc/containers/Login.jssrc/containers/Signup.js에서 사용할 수 있습니다.

<FacebookButton
  onLogin={this.handleFbLogin}
/>
<hr />

로그인 및 회원가입 폼 위에 버튼을 추가합니다. 그리고 import FacebookButton from "../components/FacebookButton";를 사용해 임포트하는 것을 잊지 마세요.

Change indicator 또한 핸들러 메서드도 추가합니다.

handleFbLogin = () => {
  this.props.userHasAuthenticated(true);
};

위 코드는 Facebook 회원가입 프로세스가 완료되면 사용자를 React 앱에 로그인시킵니다. src/containers/Signup.js에도 동일하게 추가하세요.

이제 앱으로 이동하면 Facebook으로 로그인 옵션이 보일 것입니다.

Facebook 로그인 옵션 스크린샷

클릭하면 앱으로 로그인하라는 Facebook 다이얼로그가 나타납니다.

Facebook 로그인 다이얼로그 스크린샷

로그인 후에는 이전과 동일하게 앱과 상호작용할 수 있습니다.

로그인된 데모 앱 스크린샷

앱 배포에 대한 마지막 참고사항입니다. 위에서 Facebook이 https://localhost:3000 URL을 사용하도록 설정했던 것을 기억할 것입니다. React 앱을 배포할 때는 이 URL을 실제 URL로 변경해야 합니다. 좋은 방법은 실제 사용자를 위한 Facebook 앱과 로컬 테스트를 위한 Facebook 앱을 각각 만드는 것입니다. 이렇게 하면 URL을 변경할 필요 없이 변경 사항을 테스트할 수 있는 환경을 갖출 수 있습니다.