Kakao OAuth 2.0 for Android

이미지 출처: http://www.applen.or.kr/news/articleView.html?idxno=31005


1. 디버그용 키해시 추출

https://developers.kakao.com/docs/android#시작하기-샘플앱-실행

2. 샘플앱 키해시 등록

https://developers.kakao.com/user/sample-app

3. 라이브러리 및 샘플 프로젝트 다운로드

https://developers.kakao.com/docs/sdk

4. API 사용가이드 상세

https://developers.kakao.com/docs/android#시작하기-앱-생성

5. 카카오 로그인 API 주요설정 및 코드

  • 5.1. AndroidManifest.xml(GlobalApplication 및 AppKey 설정)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.blog.korn123.android_cookbook_2017">
 
    <application
        android:name="com.kakao.sdk.sample.common.GlobalApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <meta-data
            android:name="com.kakao.sdk.AppKey"
            android:value="@string/kakao_app_key"/>
cs


  • 5.2. AndroidManifest.xml(Activity 설정)

1
2
3
4
5
6
7
8
9
10
<activity
    android:name=".kakao.KakaoLoginActivity"
    android:configChanges="screenSize|orientation"
    android:launchMode="singleTask"
    android:theme="@android:style/Theme.Light.NoTitleBar"/>
<activity
    android:name=".kakao.UsermgmtMainActivity"
    android:configChanges="keyboardHidden|orientation|screenSize"
    android:launchMode="singleTop"
    android:windowSoftInputMode="adjustResize|stateHidden"/>
cs


  • 5.3. GlobalApplication.java(KakaoSDK 초기화)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Override
public void onCreate() {
    super.onCreate();
    instance = this;
 
    KakaoSDK.init(new KakaoSDKAdapter());
 
    final RequestQueue requestQueue = Volley.newRequestQueue(this);
 
    ImageLoader.ImageCache imageCache = new ImageLoader.ImageCache() {
        final LruCache<String, Bitmap> imageCache = new LruCache<String, Bitmap>(30);
 
        @Override
        public void putBitmap(String key, Bitmap value) {
            imageCache.put(key, value);
        }
 
        @Override
        public Bitmap getBitmap(String key) {
            return imageCache.get(key);
        }
    };
 
    imageLoader = new ImageLoader(requestQueue, imageCache);
}
cs


  • 5.4. KakaoLoginActivity.java(kakao에서 제공하는 OAuth 2.0 취득을 위한 Activity)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
 * Copyright 2014-2015 Kakao Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.kakao.sdk.sample.common;
 
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
 
import com.kakao.auth.ISessionCallback;
import com.kakao.auth.KakaoSDK;
import com.kakao.auth.Session;
import com.kakao.sdk.sample.R;
import com.kakao.sdk.sample.common.widget.KakaoToast;
import com.kakao.util.exception.KakaoException;
import com.kakao.util.helper.log.Logger;
 
/**
 * 샘플에서 사용하게 될 로그인 페이지
 * 세션을 오픈한 후 action을 override해서 사용한다.
 *
 * @author MJ
 */
public class SampleLoginActivity extends BaseActivity {
    private SessionCallback callback;
 
    /**
     * 로그인 버튼을 클릭 했을시 access token을 요청하도록 설정한다.
     *
     * @param savedInstanceState 기존 session 정보가 저장된 객체
     */
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        callback = new SessionCallback();
        Session.getCurrentSession().addCallback(callback);
        if (!Session.getCurrentSession().checkAndImplicitOpen()) {
            setContentView(R.layout.layout_common_kakao_login);
        }
    }
 
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (Session.getCurrentSession().handleActivityResult(requestCode, resultCode, data)) {
            return;
        }
 
        super.onActivityResult(requestCode, resultCode, data);
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Session.getCurrentSession().removeCallback(callback);
    }
 
    private class SessionCallback implements ISessionCallback {
 
        @Override
        public void onSessionOpened() {
            redirectSignupActivity();
        }
 
        @Override
        public void onSessionOpenFailed(KakaoException exception) {
            if(exception != null) {
                Logger.e(exception);
            }
 
            setContentView(R.layout.layout_common_kakao_login);
        }
    }
}
 
cs


  • 5.5. UsermgmtMainActivity.java(kakao에서 제공하는 사용자정보 취득 및 관리용 Activity)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/**
 * Copyright 2014-2016 Kakao Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.kakao.sdk.sample.usermgmt;
 
import java.util.Map;
 
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.kakao.auth.ApiResponseCallback;
import com.kakao.auth.AuthService;
import com.kakao.auth.network.response.AccessTokenInfoResponse;
import com.kakao.network.ErrorResult;
import com.kakao.sdk.sample.R;
import com.kakao.sdk.sample.common.BaseActivity;
import com.kakao.sdk.sample.common.log.Logger;
import com.kakao.sdk.sample.common.widget.KakaoToast;
import com.kakao.sdk.sample.common.widget.ProfileLayout;
import com.kakao.usermgmt.UserManagement;
import com.kakao.usermgmt.callback.LogoutResponseCallback;
import com.kakao.usermgmt.callback.MeResponseCallback;
import com.kakao.usermgmt.callback.UnLinkResponseCallback;
import com.kakao.usermgmt.response.model.UserProfile;
 
/**
 * 가입된 사용자가 보게되는 메인 페이지로 사용자 정보 불러오기/update, 로그아웃, 탈퇴 기능을 테스트 한다.
 */
public class UsermgmtMainActivity extends BaseActivity {
    private UserProfile userProfile;
    private ProfileLayout profileLayout;
    private ExtraUserPropertyLayout extraUserPropertyLayout;
 
    /**
     * 로그인 또는 가입창에서 넘긴 유저 정보가 있다면 저장한다.
     * @param savedInstanceState 기존 session 정보가 저장된 객체
     */
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initializeView();
    }
 
    @Override
    protected void onResume(){
        super.onResume();
        userProfile = UserProfile.loadFromCache();
        if(userProfile != null)
            showProfile();
    }
 
    /**
     * 사용자의 정보를 변경 저장하는 API를 호출한다.
     */
    private void onClickUpdateProfile() {
        final Map<StringString> properties = extraUserPropertyLayout.getProperties();
        UserManagement.requestUpdateProfile(new UsermgmtResponseCallback<Long>() {
            @Override
            public void onSuccess(Long result) {
                userProfile.updateUserProfile(properties);
                if (userProfile != null) {
                    userProfile.saveUserToCache();
                }
                KakaoToast.makeToast(getApplicationContext(), "succeeded to update user profile", Toast.LENGTH_SHORT).show();
                Logger.d("succeeded to update user profile" + userProfile);
                showProfile();
            }
 
        }, properties);
    }
 
    private void onClickAccessTokenInfo() {
        AuthService.requestAccessTokenInfo(new ApiResponseCallback<AccessTokenInfoResponse>() {
            @Override
            public void onSessionClosed(ErrorResult errorResult) {
                redirectLoginActivity();
            }
 
            @Override
            public void onNotSignedUp() {
                // not happened
            }
 
            @Override
            public void onFailure(ErrorResult errorResult) {
                String message = "failed to get access token info. msg=" + errorResult;
                Logger.e(message);
                KakaoToast.makeToast(getApplicationContext(), message, Toast.LENGTH_LONG).show();
            }
 
            @Override
            public void onSuccess(AccessTokenInfoResponse accessTokenInfoResponse) {
                long userId = accessTokenInfoResponse.getUserId();
                Logger.d("this access token is for userId=" + userId);
 
                long expiresInMilis = accessTokenInfoResponse.getExpiresInMillis();
                Logger.d("this access token expires after " + expiresInMilis + " milliseconds.");
 
                KakaoToast.makeToast(getApplicationContext(), "this access token for user(id="+ userId+") expires after " + expiresInMilis + " milliseconds.", Toast.LENGTH_LONG).show();
            }
        });
    }
 
    private void onClickLogout() {
        UserManagement.requestLogout(new LogoutResponseCallback() {
            @Override
            public void onCompleteLogout() {
                redirectLoginActivity();
            }
        });
    }
 
    private void onClickUnlink() {
        final String appendMessage = getString(R.string.com_kakao_confirm_unlink);
        new AlertDialog.Builder(this)
            .setMessage(appendMessage)
            .setPositiveButton(getString(R.string.com_kakao_ok_button),
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        UserManagement.requestUnlink(new UnLinkResponseCallback() {
                            @Override
                            public void onFailure(ErrorResult errorResult) {
                            }
 
                            @Override
                            public void onSessionClosed(ErrorResult errorResult) {
                                redirectLoginActivity();
                            }
 
                            @Override
                            public void onNotSignedUp() {
                                redirectSignupActivity();
                            }
 
                            @Override
                            public void onSuccess(Long result) {
                                redirectLoginActivity();
                            }
                        });
                        dialog.dismiss();
                    }
                })
            .setNegativeButton(getString(R.string.com_kakao_cancel_button),
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                }).show();
 
    }
 
 
    private void showProfile() {
        if (profileLayout != null) {
            profileLayout.setUserProfile(userProfile);
        }
 
        if (extraUserPropertyLayout != null) {
            extraUserPropertyLayout.showProperties(userProfile.getProperties());
        }
    }
 
    private void initializeView() {
        setContentView(R.layout.layout_usermgmt_main);
        ((TextView)findViewById(R.id.text_title)).setText(getString(R.string.text_usermgmt));
        findViewById(R.id.title_back).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
 
        initializeButtons();
        initializeProfileView();
    }
 
    private void initializeButtons() {
        final Button buttonMe = (Button) findViewById(R.id.buttonMe);
        buttonMe.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                profileLayout.requestMe();
            }
        });
 
        final Button buttonUpdateProfile = (Button) findViewById(R.id.buttonUpdateProfile);
        buttonUpdateProfile.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                onClickUpdateProfile();
            }
        });
 
        final Button logoutButton = (Button) findViewById(R.id.logout_button);
        logoutButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                onClickLogout();
            }
        });
 
        final Button unlinkButton = (Button) findViewById(R.id.unlink_button);
        unlinkButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                onClickUnlink();
            }
        });
 
        final Button tokenInfoButton = (Button) findViewById(R.id.token_info_button);
        tokenInfoButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                onClickAccessTokenInfo();
            }
        });
    }
 
    private void initializeProfileView() {
        profileLayout = (ProfileLayout) findViewById(R.id.com_kakao_user_profile);
        profileLayout.setMeResponseCallback(new MeResponseCallback() {
            @Override
            public void onNotSignedUp() {
                redirectSignupActivity();
            }
 
            @Override
            public void onFailure(ErrorResult errorResult) {
                String message = "failed to get user info. msg=" + errorResult;
                Logger.e(message);
                KakaoToast.makeToast(getApplicationContext(), message, Toast.LENGTH_LONG).show();
            }
 
            @Override
            public void onSessionClosed(ErrorResult errorResult) {
                redirectLoginActivity();
            }
 
            @Override
            public void onSuccess(UserProfile result) {
                KakaoToast.makeToast(getApplicationContext(), "succeeded to get user profile", Toast.LENGTH_SHORT).show();
                if (result != null) {
                    UsermgmtMainActivity.this.userProfile = result;
                    userProfile.saveUserToCache();
                    showProfile();
                }
            }
        });
 
        extraUserPropertyLayout = (ExtraUserPropertyLayout) findViewById(R.id.extra_user_property);
    }
 
    private abstract class UsermgmtResponseCallback<T> extends ApiResponseCallback<T> {
        @Override
        public void onNotSignedUp() {
            redirectSignupActivity();
        }
 
        @Override
        public void onFailure(ErrorResult errorResult) {
            String message = "failed to get user info. msg=" + errorResult;
            Logger.e(message);
            KakaoToast.makeToast(getApplicationContext(), message, Toast.LENGTH_LONG).show();
        }
 
        @Override
        public void onSessionClosed(ErrorResult errorResult) {
            redirectLoginActivity();
        }
    }
}
 
cs


Table of contents


Open source project on GitHub


Jupyter/IPython Notebook


Open API


NoSQL


Jekyll


Gradle


Kotlin


C++

  • C++ keywords
  • 윤성우 열혈강의 C++ 프로그래밍 예제코드 [펼치기]
    1. C언어 기반의 C++ 1
      • printf와 scanf를 대신하는 입출력 방식
      • 함수 오버로딩(Function Overloading)
      • 매개변수의 디폴트 값(Default Value)
      • 인라인 함수(Inline)함수
      • 이름공간(namespace)에 대한 소개
    2. C언어 기반의 C++ 2
      • 새로운 자료형 bool
      • 참조자(Reference)의 이해
      • 참조자(Reference)와 함수
      • maloc & free를 대신하는 new & delete
      • C++에서 C언어의 표준함수 호출하기
    3. 클래스의 기본
      • C++에서의 구조체
      • 클래스(Class)와 객체(Object)
      • 객체지향 프로그래밍의 이해
    4. 클래스의 완성
      • 정보은닉(Information Hiding)
      • 캡슐화(Encapsulation)
      • 생성자(Constructor)와 소멸자(Destructor)
      • 클래스와 배열 그리고 this 포인터
    5. 복사 생성자
      • ‘복사 생성자’ 와의 첫 만남
      • ‘깊은 복사’와 ‘얕은 복사’
      • 복사 생성자의 호출시점
    6. friend와 static 그리고 const
      • const와 관련해서 아직 못다한 이야기
      • 클래스와 함수에 대한 friend 선언
      • C++에서의 static
    7. 상속(Inheritance)의 이해
      • 상속에 들어가기에 앞서
      • 상속의 문법적인 이해
      • protected 선언과 세 가지 형태의 상속
      • 상속을 위한 조건
    8. 상속과 다형성
      • 객체 포인터의 참조관계
      • 가상함수(Vitual Function)
      • 가상 소멸자와 참조자의 참조 가능성
    9. 가상(Virtual)의 원리와 다중상속
      • 멤버함수와 가상함수의 동작원리
      • 다중상속(Multiple Inheritance)에 대한 이해
    10. 연산자 오버로딩 1
      • 연산자 오버로딩의 이해와 유형
      • 단항 연산자의 오버로딩의
      • 교환법칙 문제의 해결
      • cout, cin 그리고 endl의 정체
    11. 연산자 오버로딩 2
      • 반드시 해야 하는 대입 연산자의 오버로딩
      • 배열의 인덱스 연산자 오버로딩
      • 그 이외의 연산자 오버로딩
    12. String 클래스의 디자인
      • C++ 표준과 표즌 string 클래스
      • 문자열 처리 클래스의 정의
    13. 템플릿(Template) 1
      • 템플릿(Template)에 대한 이해와 함수 템플릿
      • 클래스 템플릿(Class Temlpate)
    14. 템플릿(Template) 2
      • Chapter 13에서 공부한 내용의 확장
      • 클래스 템플릿의 특수화(Class Temlpate Specialization)
      • 템플릿의 인자
      • 템플릿과 static
    15. 예외처리(Exception Handling)
      • 예외상황과 예외처리의 이해
      • C++의 예외처리 메커니즘
      • Stack Unwinding(스택 풀기)
      • 예외상황을 표현하는 예외 클래스의 설계
      • 예외처리와 관련된 또 다른 특성들
    16. C++의 형 변환 연산자
      • C++에서의 형 변환 연산

ETC