본문 바로가기

Android Tutorial/Facebook(페이스북)

[Scrumptious Tutorial] 1 - Authenticate

[Scrumptious Tutorial] 1 - Authenticate



이 튜토리얼은 Facebook SDK for Android 로 유저를 인증하는 방법을 설명합니다.


우리는 Facebook SDK 의 코어 오브젝트를 소개합니다: Session.

이 오브젝트는 유저에게 권한을 부여하고, Facebook 로그인 플로우를 관리하고, 세션을 관리하는데 사용됩니다.

어떤 페이스북 API 호출은 Session 인스턴스를 사용하는 인증된 사용자가 필요합니다. 앱이 Session 인스턴스를 생성하고 그들을 관리하는 동안, Session 클래스는 애플리케이션에 한번에 한 사용자가 로그인하는, 일반적인 시나리오를 심플하게 하기위한 helper 를 제공합니다.


또한 페이스북 SDK 는 Session 관리를 위해 Activity 나 Fragment 에 사용할 수 있는 UiLifecycleHelper 클래스를 제공합니다. 이 UiLifecycleHelper 클래스는 인증 플로우를 관리할수 있고, Activity 나 Fragment 가 UiLifecycleHelper 인스턴스에 전달하는 Session.StatusCallback 콜백 구현을 통해 세션 상태에 대한 탭 유지를 할 수 있습니다. Session.StatusCallback 구현은 session 변화에 반응하고 조치를 취하도록 설정 할 수 있습니다.


LoginButton 은 유저 인증을 위한 유저인터페이스를 드랍할 수 있는, 페이스북 SDK 에서 제공되는 커스터마이즈된 Button View 입니다. 그것은 session 상태를 유지합니다. 더불어, LoginButton 과 UiLifecycleHelper 클래스는 애플리케이션에 빠르게 페이스북 인증을 추가하는데 사용 될 수 있습니다.


이 튜토리얼은 다음 내용들을 설명합니다:

Note: 이 튜토리얼을 시작하기 전에, 기본 페이스북 앱을 설정했는지 확인하세요.






Step 1: 유저 인터페이스 설정 (Set Up the User Interface)


앱의 인증 플로우는 main Activity 와 비 인증 유저인터페이스와 인증된 유저인터페이스 두개의 Fragment 를 포함합니다. 비 인증 UI 는 사용자에게 앱 브랜딩과 로그인 버튼을 보여줍니다. 인증된 UI 는 개인화된 경험과 스토리 설정을 도와주는 옵션 리스트를 보여줍니다. 이 문서에 있는 인증된 UI 추가를 원치않으면, 다음에 해결할수도 있겠죠. 하지만 당신은 인증된 UI 에 대한 기본 UI 표현을 로그아웃 기능을 구현하는 데 설정할 수 있습니다. 


비 인증 UI 는 SplashFragment 로 만들어져서 표현됩니다. 인증된 UI 는 SelectionFragment 라고 만들어져서 표현됩니다.



Step 1a: 로그인 유저 인터페이스 설정 (Set up The Login User Interface)


먼저, 튜토리얼에 사용할 image asset 을 복사합니다. 이런 asset 의 대부분은 로그인 플로우에 나타납니다. 그래서 그것들을 복사 할 수 있죠. image asset 은 Facebook SDK for Android 의 완성된 샘플 패키지에 있습니다. Scruptious sample 의 res/drawable 에 있는 이미지들을 카피하고, 당신의 안드로이드 프로젝트의 res/drawable 폴더에 붙여넣습니다. 만약 프로젝트에 drawable 이라는 이름의 폴더가 없다면 먼저 drawable 폴더를 만들어줍니다.


다음으로 비 인증 UI 에 사용할 새로운 Fragment 클래스를 만듭니다. 클래스 이름은 SplashFragment 로 하구요, superclass 는 Fragment 로 해줍니다. 예전 버전 안드로이드도 지원하려면 android.support.v4.app.Fragment 옵션이 있습니다.


이제 이 fragment 를 위한 새로운 layout 을 만듭니다. res/layout 밑에 "splash.xml" 이라는 이름의 Android XML file 을 만들어줍니다. 루트 엘리먼트로 LinearLayout 을 선택합니다. 레이아웃에 애플리케이션 아이콘, 애플리케이션 이름 레이블, 애플리케이션 설명, 로그인 버튼등으로 구성된 UI 컴포넌트를 추가합니다. 로그인 버튼은 자동으로 상태를 추적하고 유지하는 커스터마이즈된 페이스북 로그인 버튼을 사용 하도록 구현되어있습니다. XML 파일은 아래와 같아야 합니다.

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:orientation="vertical"
              android:background="#303040" >

    <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:gravity="center_horizontal"
            android:orientation="horizontal" >
        <ImageView
                android:id="@+id/splash_icon"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginLeft="10dp"
                android:gravity="center"
                android:src="@drawable/icon" />
        <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:gravity="center"
                android:textColor="#AFDEFE"
                android:textSize="28sp"
                android:typeface="serif"
                android:textStyle="italic"
                android:text="@string/app_name" />
    </LinearLayout>


    <TextView
            android:id="@+id/profile_name"
            android:layout_width="174dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="35dp"
            android:lines="2"
            android:textSize="17sp"
            android:text="@string/get_started"
            android:layout_gravity="center_horizontal"
            android:gravity="center_horizontal"/>

    <com.facebook.widget.LoginButton
            android:id="@+id/login_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="30dp"
            android:layout_marginBottom="30dp" />

</LinearLayout>

strings 레퍼런스와 관련하여 몇가지 에러를 볼수 있는데, 그건 아직 string 들이 정의되어있지 않기때문입니다. 이런 스트링들을 처리하려면, res/values/strings.xml 파일을 열고, 스트링 리소스를 추가해줍니다. 

<string name="get_started">To get started, log in using Facebook</string>

당신은 또한 완성된 샘플과 매칭되도록 app_name 스트링 리소스 값을 수정해주어야 합니다.

<string name="app_name">Scrumptious</string>

이제 SplashFragment 클래스를 열고, onCreateView() 메소드를 오버라이드해서 방금 만든 레이아웃을 뷰로 설정해줍니다.

@Override
public View onCreateView(LayoutInflater inflater, 
        ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.splash, 
            container, false);
    return view;
}

만약 어떤 에러가 발생한다면, 키를 눌러서 이클립스에서 누락된 라이브러리들을 import 해줍니다.

  • Mac: Command-Shift-O
  • Windows: Control-Shift-O



Step 1b: 인증된 UI 설정 (Set Up the Authenticated UI)


인증된 UI 에 사용할 새로운 프래그먼트 클래스를 만들어줍니다. 이름은 "SelectionFragment" 로 하고, superclass 는 Fragment 로 해줍니다.


이제 이 새로운 프래그먼트에 사용할 layout 을 만들어줍니다. res/layout 밑에 "selection.xml" 이라는 이름으로 만들어줍니다. 루트 엘리먼트로는 LinearLayout 을 선택해주고요. 당신은 유저가 로그인된걸 확인하기 위해 기본 UI 를 추가할겁니다. 나중에 프로젝트에, 당신은 유저 이름과 그들이 먹은 음식 같은 옵션 리스트 정보같은걸 기본 UI 에 대체시킬겁니다. LinearLayout 컨테이너에 아래 뷰들을 추가해줍니다. 

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:layout_gravity="center"
    android:textColor="#333"
    android:textSize="18sp"
    android:text="Welcome, you are now logged in."  />

이건 임시 UI 이므로 하드코딩된 문자열에 대한 layout warning 들은 무시해줍니다.


이제 SelectionFragment 클래스를 열고, onCreateView() 메소드를 오버라이드해서 방금 만든 레이아웃을 뷰로 설정해줍니다.

@Override
public View onCreateView(LayoutInflater inflater, 
        ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    View view = inflater.inflate(R.layout.selection, 
            container, false);
    return view;
}

디버깅 목적으로 사용할 private 상수를 추가합니다.

private static final String TAG = "SelectionFragment";



Step 1c: 메인 레이아웃 설정 (Set up the Main Layout)


이제 우리가 정의한 두 프래그먼트의 호스트로서 메인 Activity의 레이아웃을 설정합니다.


res/layout/main.xml 파일을 수정합니다. 컨텐츠를 LinearLayout 이 parent 가 되게 변경합니다. 두개의 프래그먼트를 레이아웃으로 추가합니다. 아마 이렇게 보이게 되겠죠:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <fragment android:name="com.facebook.scrumptious.SelectionFragment"
          android:id="@+id/selectionFragment"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />
    <fragment android:name="com.facebook.scrumptious.SplashFragment"
          android:id="@+id/splashFragment"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />
</LinearLayout>

당신의 프래그먼트의 android:name 속성 값은 당신의 정규화된 클래스 이름에 해당됩니다. 

패키지 이름을 com.facebook.scrumptious 에서 당신 클래스들의 패키지 이름으로 바꿔줍니다.


당신은 페이스북 로그인을 구현하는 기본 UI 설정을 완료했습니다.






Step 2: 인증 로직 연결 (Wire Up the Authentication Logic)


이번 스텝에서, 유저가 인증받지 않았을 때 로그인 UI 를 보여주는 인증 로직을 연결할 거구요, 유저가 인증되었을 때는 기본 인사 메시지를 보여줄겁니다. 


여기 당신이 구현할 로직 플로우가 있습니다:


  • 메인 액티비티를 호스트로 하는 프래그먼트를 찾아서, 그것들을 처음엔 숨겨둡니다. 
  • 메인 액티비티에서 Session.StatusCallback 리스너를 설정합니다. UiLifecycleHelper 인스턴스를 만들고 리스너의 생성자에 전달합니다. 
  • 이 리스너 구현은 세션 상태 변화와 프래그먼트와 관련된 것들을 보여주는 것에 응답하는 메소드를 호출합니다. 관련 프래그먼트가 보여지기 전에, 그 프래그먼트 백 스택은 클리어 됩니다. 세션 상태 변화 응답은 오직 액티비티가 active 할때만 발생됩니다. 
  • 프래그먼트가 resume 될때마다, 유저 세션 상태를 체크하고, 인증된 UI 또는 비 인증 UI 를 보여주는 처리를 합니다.


프래그먼트설정을 시작합니다. MainActivity 클래스를 열고, Activity 대신 FragmentActivity 의 하위 클래스로 바꿔줍니다.

public class MainActivity extends Activity FragmentActivity {
메인 액티비티에서 처리하기 위한 Fragment 배열을 privatae 으로 정의합니다. 또한 이 프래그먼트들을 나중에 정교하게 다루는데 사용하기 위한 배열 인덱스 상수를 정의합니다.

private static final int SPLASH = 0;
private static final int SELECTION = 1;
private static final int FRAGMENT_COUNT = SELECTION +1;

private Fragment[] fragments = new Fragment[FRAGMENT_COUNT];

만약 Fragment 클래스를 import 할때, 선택지가 나온다면, android.support.v4.app.Fragment 를 선택합니다.


이제, onCreate() 메소드에서 프래그먼트를 처음엔 숨겨줍니다.

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    FragmentManager fm = getSupportFragmentManager();
    fragments[SPLASH] = fm.findFragmentById(R.id.splashFragment);
    fragments[SELECTION] = fm.findFragmentById(R.id.selectionFragment);

    FragmentTransaction transaction = fm.beginTransaction();
    for(int i = 0; i < fragments.length; i++) {
        transaction.hide(fragments[i]);
    }
    transaction.commit();
}

마찬가지로 import 할때 선택지가 나오면, android.support.v4.app.Fragment~ 를 선택해줍니다.


다음으로 세션 관리 관련 코드를 추가합니다. 당신인 세션 상태 변화 때 호출되는 메소드를 정의함으로써 시작합니다.


프래그먼트를 보여주고 다른 프래그먼트를 숨기는 걸 책임지는 private 메소드를 정의합니다: 

private void showFragment(int fragmentIndex, boolean addToBackStack) {
    FragmentManager fm = getSupportFragmentManager();
    FragmentTransaction transaction = fm.beginTransaction();
    for (int i = 0; i < fragments.length; i++) {
        if (i == fragmentIndex) {
            transaction.show(fragments[i]);
        } else {
            transaction.hide(fragments[i]);
        }
    }
    if (addToBackStack) {
        transaction.addToBackStack(null);
    }
    transaction.commit();
}

현재 보여지고 있는 액티비티를 나타내는 플래그에 대한 코드를 추가합니다. 이 플래그는 세션 상태 변화를 체크하는데 사용됩니다. 먼저 이 플래그에 대한 private 변수를 추가합니다.

private boolean isResumed = false;

다음으로, 액티비티가 멈추거나(paused) 재개되었을 때(resumed) 클리어 하거나 셋 해줍니다.

@Override
public void onResume() {
    super.onResume();
    isResumed = true;
}

@Override
public void onPause() {
    super.onPause();
    isResumed = false;
}

다음으로 세션 상태 변경으로 인해 호출되는 private 메소드를 정의합니다. 이 메소드는 유저 인증 상태에 기반한 관련 fragment 를 보여줍니다. 액티비티가 보여질때 isResumed flag 를 사용함으로써 유저 인터페이스 변경 사항을 처리합니다. 프래그먼트 백스택은 showFragment() 메소드가 호출되기 전에는 clear 상태이고, 적절한 프래그먼트 정보로 호출 됩니다.  

private void onSessionStateChange(Session session, SessionState state, Exception exception) {
    // Only make changes if the activity is visible
    if (isResumed) {
        FragmentManager manager = getSupportFragmentManager();
        // Get the number of entries in the back stack
        int backStackSize = manager.getBackStackEntryCount();
        // Clear the back stack
        for (int i = 0; i < backStackSize; i++) {
            manager.popBackStack();
        }
        if (state.isOpened()) {
            // If the session state is open:
            // Show the authenticated fragment
            showFragment(SELECTION, false);
        } else if (state.isClosed()) {
            // If the session state is closed:
            // Show the login fragment
            showFragment(SPLASH, false);
        }
    }
}

 import 할때, 선택하는 옵션이 나오면 com.facebook.Session 을 선택합니다.


다음으로, 프래그먼트가 새로 인스턴스 되는 경우를 처리해야합니다. 그리고 적절한 설정이 필요한 인증된 UI vs 비 인증 UI 를 처리해야합니다. onResumeFragments() 메소드를 오버라이드 해서 세션 변경 사항을 처리합니다.

@Override
protected void onResumeFragments() {
    super.onResumeFragments();
    Session session = Session.getActiveSession();

    if (session != null && session.isOpened()) {
        // if the session is already open,
        // try to show the selection fragment
        showFragment(SELECTION, false);
    } else {
        // otherwise present the splash screen
        // and ask the user to login.
        showFragment(SPLASH, false);
    }
}
이제 UiLifecycleHelper 를 사용해서 세션을 추적하고, 세션 상태 변화 리스너를 작동시킵니다. 먼저 UiLifecycleHelper 오브젝트를 위한 private 변수를 만들어줍니다. 그리고 세션 상태 변화 리스너를 구현합니다.
private UiLifecycleHelper uiHelper;
private Session.StatusCallback callback = 
    new Session.StatusCallback() {
    @Override
    public void call(Session session, 
            SessionState state, Exception exception) {
        onSessionStateChange(session, state, exception);
    }
};
Session.StatusCallback 구현은 call() 메소드를 오버라이드 합니다. 그리고 미리 정의해둔 onSessionStatusChange() 메소드를 포함합니다.

다음으로, onCreate() 메소드에서, UiLifecycleHelper 인스턴스를 생성하고 리스너를 전달합니다.
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    uiHelper = new UiLifecycleHelper(this, callback);
    ....
세션을 추적하는데 필요한 액티비티 생애 주기 메소드에서 UiLifecycleHelper 를 호출합니다.
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    uiHelper = new UiLifecycleHelper(this, callback);
    uiHelper.onCreate(savedInstanceState);
    ....
}

@Override
public void onResume() {
    super.onResume();
    uiHelper.onResume();
    isResumed = true;
}

@Override
public void onPause() {
    super.onPause();
    uiHelper.onPause();
    isResumed = false;
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    uiHelper.onActivityResult(requestCode, resultCode, data);
}

@Override
public void onDestroy() {
    super.onDestroy();
    uiHelper.onDestroy();
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    uiHelper.onSaveInstanceState(outState);
}
안드로이드 애플리케이션을 빌드하고 실행합니다. 오류없이 실행되는지 확인합니다. 당신은 로그인 화면을 볼 수 있을 것입니다. 로그인 버튼을 클릭했을 때, 퍼미션 다이얼로그를 볼 수 있을 겁니다. 한번 당신이 승인하면, 당신은 스크린에서 환영텍스트를 볼 수 있을 겁니다.





Step 3: 로그아웃 플로우 추가 (Add The Logout Flow)


이번 스텝에서, 당신은 유저에게 앱에서 페이스북 세션 정보를 클리어 할 수 있는 능력을 제공할 것입니다.


당신은 앱에서 로그아웃 하는데 UserSettingsFragment 를 UI 로 제공해서 사용하도록 만들 수 있습니다. 이 프래그먼트는 유저 세션 상태에 따라 로그인 또는 로그아웃 버튼을 보여줍니다. 만약 유저가 인증되 경우, 유저의 프로필 사진과 이름을 보여줍니다.


이 예제에서, 당신은 유저가 인증될때, 그리고 그들이 디바이스의 메뉴 버튼을 클릭할 때 마다, 이 프래그먼트에 메인 액티비티가 호스트가 되도록 설정할 것입니다. 당신은 SelectionFragment 가 현재 활성화 된 경우에만 메뉴 옵션을 보여주는 코드를 추가할 것입니다. 이 SelectionFragment 프래그먼트는 유저가 로그인됬을 때 보여집니다.


이 기능들을 구현하기 위해 당신이 해야하는 단계들입니다: 

  • 메인 레이아웃에 UserSettingsFragment 클래스를 추가합니다.
  • 설정 프래그먼트를 위한 별도의 상수를 추가합니다.
  • 유저 인증 상태나, SelectionFragment 프래그먼트가 보여지는지에 기반해 보여주거나(show) 숨기기(hide) 위해 onPrepareOptionsMenu() 메소드 오버라이드합니다. 
  • 설정 메뉴 옵션이 선택 되었을 때, UserSettingsFragment 프래그먼트를 보여주기 위해 onOptionsItemSelected() 메소드를 오버라이드 합니다.
메인 레이아웃 파일, res/layout/main.xml 을 열어서, UserSettingsFragmentFragment 를 레이아웃에 추가합니다:
....
    <fragment android:name="com.facebook.widget.UserSettingsFragment"
          android:id="@+id/userSettingsFragment"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />
</LinearLayout>

메인 액티비티 클래스를 열어서, 설정 프래그먼트 를 위한 새로운 private 상수를 추가합니다:

private static final int SETTINGS = 2;

다음으로, 부가적인 설정 프래그먼트에 기반해 fragment count 변수를 수정합니다.

private static final int FRAGMENT_COUNT = SELECTION +1;
private static final int FRAGMENT_COUNT = SETTINGS +1;

그런 다음, 설정 프래그먼트에 대한 메뉴 항목을 표시하는 새로운 private 변수를 추가합니다. UserSettingsFragment 를 보여주도록 작동시키는데 사용합니다.

private MenuItem settings;

다음엔, onCreate() 를 수정해서 UserSettingFragment 인스턴스를 fragment 배열에 추가합니다.

....
fragments[SELECTION] = fm.findFragmentById(R.id.selectionFragment);
fragments[SETTINGS] = fm.findFragmentById(R.id.userSettingsFragment);

res/values/string.xml 파일에 메뉴 옵션을 위한 새로운 스트링 리소스를 추가합니다.

<string name="settings">Settings</string>

이제, 옵션 메뉴 디스플레이를 준비하는 onPrepareOptionMenu() 메소드를 오버라이드합니다.

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    // only add the menu when the selection fragment is showing
    if (fragments[SELECTION].isVisible()) {
        if (menu.size() == 0) {
            settings = menu.add(R.string.settings);
        }
        return true;
    } else {
        menu.clear();
        settings = null;
    }
    return false;
}

마지막으로, 세팅 메뉴가 클릭되었을 때, UserSettingFragment 를 보여주기위한 onOptionItemSelected() 메소드를 오버라이드 합니다.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (item.equals(settings)) {
        showFragment(SETTINGS, true);
        return true;
    }
    return false;
}

Note: 만약 당신이 이전에 onCreateOptionMenu() 가 호출되도록 정의한 메소드가 있다면, 이 코드는 더이상 필요하지 않으니 삭제합니다. 이 코드는 기본 프로젝트가 생성될때 추가되었을 수 있습니다.


에러 없이 실행되는지 확인하고 프로젝트를 빌드, 실행합니다. 일단 인증 되었다면, "Settings" 라고 보여지는 메뉴 옵션을 클릭합니다. 세팅 메뉴 아이템을 클릭하면 유저 이름, 프로필 사진 그리고 로그아웃 버튼이 보여집니다. 로그아웃 버튼을 클릭하면 로그인 뷰 화면으로 돌아가야합니다.






다음 단계 (Next Steps)


로그인 한 사용자에 대해 앱을 개인화 하는 방법에 대해 자세히 알아보십시오.






관련 샘플 (Related Samples)


  • SessionLoginSample
  • HelloFacebookSample