-
[Android] 카카오 계정으로 로그인하기프로그래밍/Android 2017. 12. 27. 16:18
이번 포스팅은 카카오톡과 연동하여 로그인하는 방법을 다뤄보겠습니다.
이번 포스팅 후에 페이스북, 구글 등 다른 SNS와도 연동하여 로그인 하는 방법을 이어 포스팅 할 계획입니다.
그럼 바로 시작하겠습니다.
프로젝트에 Kakao SDK 추가하기
카카오 로그인을 연동하기 위해서는 카카오에서 제공하는 SDK를 프로젝트에 추가해야 합니다.
카카오 디벨로퍼에서 SDK 라이브러리를 받아 사용해도 무관합니다만, 간단하게 Gradle 설정을 통해 추가해보도록 하겠습니다.
build.gradle (Project: - ) 파일을 열어 다음과 같이 두 줄을 추가해주세요.
build.gradle (Project: - )
allprojects {
repositories {
jcenter()
mavenCentral()
maven { url 'http://devrepo.kakao.com:8088/nexus/content/groups/public/' }
}
}
다음으로 gradle.properties 파일을 열어 다음 두 줄을 추가해주세요.
gradle.properties
KAKAO_SDK_GROUP=com.kakao.sdk
KAKAO_SDK_VERSION=1.5.1
여기서 SDK 버전은 최신버전을 확인하시고 넣어주시면 됩니다.
최신버전 확인은 카카오 디벨로퍼에서 확인하시면 됩니다.
다음으로 build.gradle (Module : app) 의 dependencies 에 다음과 같이 추가해주세요.
build.gradle (Module: app)
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:26.+'
testCompile 'junit:junit:4.12'
// 카카오톡 SDK 추가
compile group: project.KAKAO_SDK_GROUP, name: 'usermgmt',
version: project.KAKAO_SDK_VERSION
}
App Key 발급 및 등록하기
연동을 위해서는 SDK외에도 App Key를 발급받아 프로젝트에 등록해주어야 합니다. App Key는 카카오 디벨로퍼 사이트에서 발급 받으실 수 있습니다.
사이트에 접속하신 뒤에, 로그인을 하시고 나면 아래와 같은 화면이 나옵니다.
좌측에 보이는 앱 만들기 버튼을 클릭합니다.
Kakao Developers > 앱 만들기
앱 아이콘은 이후에도 변경이 가능하니, 걱정하지 않으셔도 됩니다.
앱 이름을 작성하신 후에, 확인을 눌러 앱을 만들면 다음과 같이 키가 발급됩니다.
(아래 그림의 키 값들은 보이지 않도록 지워져 있습니다.)
App Key 발급
발급 받은 키 중에 저희가 사용할 것은 네이티브 앱 키 입니다. 키 값을 일단 복사해서 프로젝트에 붙여두겠습니다.
strings.xml
<resources>
// 카카오 앱 키
<string name="kakao_app_key">(네이티브 앱 키)</string>
</resources>
그럼 다시 디벨로퍼 사이트 화면으로 돌아와서, 나머지 설정을 하겠습니다.
좌측 메뉴 중에 개요를 선택해주세요.
내 애플리케이션 > 개요
개요 화면을 보시면 제일 상단에 '카카오 계정 로그인' 메뉴가 있습니다. 아래 설명이 잘 되어 있네요. '사용자 관리' 링크를 클릭하셔서 사용자 관리를 활성화 시키도록 하겠습니다.
활성화 시에 나타나는 옵션들은 필요에 따라 추가하시거나 빼주시면 됩니다.
개인정보 보호 항목에서 사용할 항목에 대해서는 수집목적을 반드시 작성하셔야 정상적으로 저장이 가능합니다.
개요 > 사용자 관리
다시 개요 화면으로 돌아와서 이번에는 '앱 정보' 메뉴를 보겠습니다.
여기서 앱 이름과 아이콘 또는 각 키 값들을 재발급 받을 수 있습니다.
지금은 플랫폼을 추가해야 하기 때문에 아래 그림과 같이 플랫폼 메뉴의 플랫폼 추가 버튼을 눌러주세요.
개요 > 사용자 관리
버튼을 누르면 아래와 같은 다이얼로그가 보이실겁니다.
위에 Android에 체크 해주시고 패키지명에 프로젝트의 패키지명을 작성해주시면 됩니다. 마켓 URL은 패키지명 작성 후에 자동으로 작성되어지니 신경 안쓰셔도 됩니다.
플랫폼 추가 다이얼로그
모두 작성 후에 추가 버튼을 누르시면 아래 그림과 같이 플랫폼이 추가 된 것을 확인 하실 수 있습니다.
개요 > 사용자 관리
여기서 끝이 아닙니다!. 등록 된 플랫폼을 보시면 키 해시 값이 비어있는 것을 확인 하실 수 있는데요, 키 해시가 등록된 앱에서만 SDK를 이용하여 API를 호출할 수 있습니다. 당연히 채워 넣어야겠죠?
해시키 구하는 방법은 여러가지가 있지만, 제 블로그에도 포스팅 한게 있으니, 참고하시면 될 듯 합니다.
프로젝트 해키시 구하기
이어서, 얻어오신 키 해시 값을 플랫폼 정보의 키 해시 값에 추가하여 주신 뒤에 저장을 누르시면 됩니다.
이것으로 카카오 디벨로퍼에 앱을 등록하는 부분은 끝났습니다.
그럼 아까 저장해 두었던 네이티브 앱 키를 프로젝트에 등록 시키기 위해 manifests.xml 파일을 열어 수정을 하도록 하겠습니다.
manifests.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="rebuild.com.kakaotalk_login">
<!-- 인터넷 사용 권한 -->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!-- 카카오톡 메타데이터 -->
<meta-data
android:name="com.kakao.sdk.AppKey"
android:value="@string/kakao_app_key" />
<activity android:name=".LoginActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
로그인 서버와 통신을 위해 인터넷 사용 권한을 추가합니다. 또한 전에 strings.xml에 저장해두었던 네이티브 앱 키를 메타데이터로 등록해주시면 됩니다.
여기까지 하셨다면, 기초 설정은 끝납니다.
프로젝트와 Kakao SDK 연결하기
Kakao SDK를 프로젝트에서 사용하기 위해서는 SDK와 앱을 연결해 주어야 합니다. 이번에는 그 역할을 해주는 Adapter를 작성하고 초기화 하는 부분을 살펴보겠습니다.
Kakao SDK를 사용하기 위해서는 초기화가 필요합니다. 초기화는 GlobalApplication 공유 클래스를 만들어 앱 수준에서 관리하도록 하겠습니다.
GlobalApplication.Java
package rebuild.com.kakaotalk_login.GlobalApplication;
import android.app.Application;
import com.kakao.auth.KakaoSDK;
import rebuild.com.kakaotalk_login.Kakao.KakaoSDKAdapter;
public class GlobalApplication extends Application {
private static GlobalApplication instance;
public static GlobalApplication getGlobalApplicationContext() {
if (instance == null) {
throw new IllegalStateException("This Application does not inherit com.kakao.GlobalApplication");
}
return instance;
}
@Override
public void onCreate() {
super.onCreate();
instance = this;
// Kakao Sdk 초기화
KakaoSDK.init(new KakaoSDKAdapter());
}
@Override
public void onTerminate() {
super.onTerminate();
instance = null;
}
}
GlobalApplication 객체를 공유하기 위해 디자인 패턴 중 싱글 톤 패턴을 이용하여 작성하였습니다. onCreate()에서는 Kakao SDK를 초기화하고 있습니다. KakaoSDKAdapter 클래스는 아직 작성하지 않았기 때문에 에러가 발생하는 것이 정상입니다.
작성을 완료하셨다면, manifests.xml에 추가하셔야 합니다.
manifests.xml
<application
android:name=".GlobalApplication"
... >
이어서 KakaoSDKAdapter를 작성해 보도록 하겠습니다.
KakaoSDKAdapter.java
package rebuild.com.kakaotalk_login.Kakao;
import android.content.Context;
import com.kakao.auth.ApprovalType;
import com.kakao.auth.AuthType;
import com.kakao.auth.IApplicationConfig;
import com.kakao.auth.ISessionConfig;
import com.kakao.auth.KakaoAdapter;
import rebuild.com.kakaotalk_login.GlobalApplication.GlobalApplication;
public class KakaoSDKAdapter extends KakaoAdapter {
// 로그인 시 사용 될, Session의 옵션 설정을 위한 인터페이스 입니다.
@Override
public ISessionConfig getSessionConfig() {
return new ISessionConfig() {
// 로그인 시에 인증 타입을 지정합니다.
@Override
public AuthType[] getAuthTypes() {
// Auth Type
// KAKAO_TALK : 카카오톡 로그인 타입
// KAKAO_STORY : 카카오스토리 로그인 타입
// KAKAO_ACCOUNT : 웹뷰 다이얼로그를 통한 계정연결 타입
// KAKAO_TALK_EXCLUDE_NATIVE_LOGIN : 카카오톡 로그인 타입과 함께 계정생성을 위한 버튼을 함께 제공
// KAKAO_LOGIN_ALL : 모든 로그인 방식을 제공
return new AuthType[] {AuthType.KAKAO_ACCOUNT};
}
// 로그인 웹뷰에서 pause와 resume시에 타이머를 설정하여, CPU의 소모를 절약 할 지의 여부를 지정합니다.
// true로 지정할 경우, 로그인 웹뷰의 onPuase()와 onResume()에 타이머를 설정해야 합니다.
@Override
public boolean isUsingWebviewTimer() {
return false;
}
// 로그인 시 토큰을 저장할 때의 암호화 여부를 지정합니다.
@Override
public boolean isSecureMode() {
return false;
}
// 일반 사용자가 아닌 Kakao와 제휴 된 앱에서 사용되는 값입니다.
// 값을 지정하지 않을 경우, ApprovalType.INDIVIDUAL 값으로 사용됩니다.
@Override
public ApprovalType getApprovalType() {
return ApprovalType.INDIVIDUAL;
}
// 로그인 웹뷰에서 email 입력 폼의 데이터를 저장할 지 여부를 지정합니다.
@Override
public boolean isSaveFormData() {
return true;
}
};
}
// Application이 가지고 있는 정보를 얻기 위한 인터페이스 입니다.
@Override
public IApplicationConfig getApplicationConfig() {
return new IApplicationConfig() {
@Override
public Context getApplicationContext() {
return GlobalApplication.getGlobalApplicationContext();
}
};
}
}
KakaoSDKAdapter 클래스는 KakaoAdpater를 상속 받아 구현하였습니다. 내부에는 ISessionConfig와 IApplicationConfig를 설정하기 위한 인터페이스로 구성되어있습니다.
ISessionConfig는 Session에 대한 옵션 값들을 설정 할 수 있습니다. 여기서 Session은 로그인 객체를 유지 시키는 객체로 Access Token을 관리하는 역할을 합니다.
IApplicationConfig는 애플리케이션이 가지고 있는 정보를 전달하는 역할을 합니다.
각각의 자세한 설명은 주석을 참고 하시면 될 듯합니다.
로그인 타입의 경우, 각각의 옵션을 설정하신 뒤에 실행시켜 보시고 원하시는 타입을 사용하시면 됩니다.
Login 버튼 구현하기
로그인 버튼을 구현할 때, 가장 손쉬운 방법은 Kakao에서 제공하는 버튼을 사용하는 경우입니다.
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
tools:context="rebuild.com.kakaotalk_login.Activity.LoginActivity">
<com.kakao.usermgmt.LoginButton
android:id="@+id/btn_kakao_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
이렇게만 버튼을 넣어주면 끝입니다. 아직 로그인 결과에 따른 콜백에 대해 설명은 하지 않았지만, 기본적으로 버튼만 달아주면 알아서 다 해줘서 굉장히 편리합니다.
하지만,
저 버튼은 디자인이 정해져있기 때문에 앱의 디자인 컨셉과 맞지 않을 수도 있습니다.
이런 경우, 원하는 디자인을 사용하면서도 카카오톡 로그인을 할 수 있는 몇 가지 방법이 있습니다.
첫번째로 카카오에서 제공하는 버튼을 안보이게 한 뒤에, 커스텀으로 제작한 버튼을 누를 경우, 클릭 효과를 주는 performClick()메서드를 이용하는 방법입니다.
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
tools:context="rebuild.com.kakaotalk_login.Activity.LoginActivity">
<Button
android:id="@+id/btn_custom_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="카카오 로그인" />
<com.kakao.usermgmt.LoginButton
android:id="@+id/btn_kakao_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone" />
</LinearLayout>
위에서는 간단하게 버튼을 두었습니다. 원하시는 형태에 따라 레이아웃을 구현하시면 되겠습니다. 기존의 카카오버튼은 보이지 않게 숨겼습니다.
그리고 Activity로 와서,
LoginActivity.java
package rebuild.com.kakaotalk_login;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.kakao.usermgmt.LoginButton;
import java.security.MessageDigest;
public class LoginActivity extends AppCompatActivity {
private Context mContext;
private Button btn_custom_login;
private LoginButton btn_kakao_login;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mContext = getApplicationContext();
btn_custom_login = (Button) findViewById(R.id.btn_custom_login);
btn_custom_login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
btn_kakao_login.performClick();
}
});
btn_kakao_login = (LoginButton) findViewById(R.id.btn_kakao_login);
}
}
커스텀한 버튼의 클릭 리스너에 카카오버튼.performClick()만 달아주면 끝입니다. 그렇다면 실제로 화면에 보이는건 커스텀 한 버튼만 보이게 되고 버튼을 눌렀을 때도 카카오버튼을 클릭한 것과 같은 효과가 나오게 됩니다.
두번째 방법은 커스텀한 버튼에 직접적으로 Session을 오픈 시키도록 코드를 구성하는 방법입니다.
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
tools:context="rebuild.com.kakaotalk_login.Activity.LoginActivity">
<Button
android:id="@+id/btn_custom_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="카카오 로그인" />
</LinearLayout>
이번에는 커스텀 버튼에서 직접 Session을 오픈할 예정이니, 카카오버튼은 없어도 됩니다.
LoginActivity.java
package rebuild.com.kakaotalk_login;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import java.security.MessageDigest;
public class LoginActivity extends AppCompatActivity {
private Context mContext;
private Button btn_custom_login;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mContext = getApplicationContext();
btn_custom_login = (Button) findViewById(R.id.btn_custom_login);
btn_custom_login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Session session = Session.getCurrentSession();
session.addCallback(new SessionCallback());
session.open(AuthType.KAKAO_LOGIN_ALL, LoginActivity.this);
}
});
}
}
위와 같이, 커스텀 버튼의 클릭 리스너에서 Session을 오픈하도록 한 줄만 추가하시면 됩니다.
간단하게 로그인버튼을 구현하는 부분을 살펴보았는데요, 로그인 버튼의 경우, 카카오에서 권장하는 디자인이 있으니 확인 후에 작성하시는 것이 좋습니다.
물론 제공되는 버튼을 써달라고 하시겠지만, 커스텀해서 사용하는 경우도 문의하실 경우 답변을 자세히 해주신답니다.
Session Callback 클래스 구현하기
로그인 요청을 위한 설정은 모두 마쳤으니, 이번에는 로그인 결과를 전달 받기 위한 Callback 클래스를 구현해보겠습니다.
SessionCallback.java
package rebuild.com.kakaotalk_login.Kakao;
import android.util.Log;
import com.kakao.auth.ISessionCallback;
import com.kakao.network.ErrorResult;
import com.kakao.usermgmt.UserManagement;
import com.kakao.usermgmt.callback.MeResponseCallback;
import com.kakao.usermgmt.response.model.UserProfile;
import com.kakao.util.exception.KakaoException;
public class SessionCallback implements ISessionCallback {
// 로그인에 성공한 상태
@Override
public void onSessionOpened() {
requestMe()
}
// 로그인에 실패한 상태
@Override
public void onSessionOpenFailed(KakaoException exception) {
Log.e("SessionCallback :: ", "onSessionOpenFailed : " + exception.getMessage());
}
// 사용자 정보 요청
public void requestMe() {
// 사용자정보 요청 결과에 대한 Callback
UserManagement.requestMe(new MeResponseCallback() {
// 세션 오픈 실패. 세션이 삭제된 경우,
@Override
public void onSessionClosed(ErrorResult errorResult) {
Log.e("SessionCallback :: ", "onSessionClosed : " + errorResult.getErrorMessage());
}
// 회원이 아닌 경우,
@Override
public void onNotSignedUp() {
Log.e("SessionCallback :: ", "onNotSignedUp");
}
// 사용자정보 요청에 성공한 경우,
@Override
public void onSuccess(UserProfile userProfile) {
Log.e("SessionCallback :: ", "onSuccess");
String nickname = userProfile.getNickname();
String email = userProfile.getEmail();
String profileImagePath = userProfile.getProfileImagePath();
String thumnailPath = userProfile.getThumbnailImagePath();
String UUID = userProfile.getUUID();
long id = userProfile.getId();
Log.e("Profile : ", nickname + "");
Log.e("Profile : ", email + "");
Log.e("Profile : ", profileImagePath + "");
Log.e("Profile : ", thumnailPath + "");
Log.e("Profile : ", UUID + "");
Log.e("Profile : ", id + "");
}
// 사용자 정보 요청 실패
@Override
public void onFailure(ErrorResult errorResult) {
Log.e("SessionCallback :: ", "onFailure : " + errorResult.getErrorMessage());
}
});
}
}
ISessionCallback은 Session의 상태 변경에 따른 Callback을 받도록 구성되어있습니다. onSessionOpend()는 Access Token을 성공적으로 발급 받은 상태로 즉, 로그인에 성공 시에 진입합니다. 이후, 로그인 된 사용자의 정보를 요청하여 제공 받을 수 있습니다. onSessionOpenFailed()는 로그인에 실패한 경우로, 여기서 실패라 함은 네트워크 에러 또는 일반적인 에러 사항에 대한 비정상적인 동작에 의한 에러 등을 말합니다.
정상적으로 로그인에 성공한 경우에는 사용자 정보를 요청하여, 콜백을 받을 수 있습니다. 제공 받을 수 있는 정보는 아래와 같습니다.
Nickname
- 사용자의 닉네임 정보입니다. 별도의 권한 없이 기본적으로 제공합니다.
Email
- 사용자의 이메일 정보입니다. 카카오디벨로퍼에서 해당 프로젝트의 사용자관리에서 제공여부를 결정할 수 있습니다.
ProfileImagePath
- 사용자의 프로필 이미지(640x640) URL 정보입니다. 별도의 권한 없이 기본적으로 제공합니다.
TumbnailImagePath
- 사용자의 프로필 이미지의 썸네일 이미지(110x110) URL 정보입니다. 별도의 권한 없이 기본적으로 제공합니다.
UUID
- 앱과 연동 시에 발급 되는 고유한 id 정보입니다. 별도의 권한 없이 기본적으로 제공합니다.
id
- 인증여부를 확인하는 사용자의 id 정보입니다. 별도의 권한 없이 기본적으로 제공합니다.
requestMe()의 경우, 앱과 연결 과정에서 최초 한번만 카카오톡 또는 카카오스토리 서비스와 동기화 연동을 하게 됩니다. 이때 연동되는 데이터는 카카오톡 또는 카카오스토리의 가입 당시의 정보를 기준으로 합니다. 이는 즉, 사용자가 해당 정보를 카카오톡 또는 카카오스트로에서 변경하더라도 변경된 데이터는 반영되지 않는다는 것을 뜻합니다.만약, 추가적으로 카카오톡 또는 카카오스토리의 프로필을 요청한다면, 해당 프로필은 가장 최신에 변경 된 프로필 정보를 반환합니다.각각의 제공되는 정보는 다음과 같습니다.
카카오톡 프로필 요청
- 닉네임
- 프로필 이미지(썸네일 및 일반 이미지 URL)
- 국가 코드
※ 참고 : https://dev.kakao.com/docs/android/kakaotalk-api
카카오스토리 프로필 요청
- 닉네임
- 프로필 이미지
- 배경 이미지
- 본인 스토리의 permanent link
- 년도는 포함되지 않은 생일
※ 참고 : https://dev.kakao.com/docs/android/kakaostory-api
내 스토리 정보 요청
- 스토리의 경우 기본적으로 내 스토리 정보(포스팅 내용 등)을 제공합니다.
이제 제공된 정보를 가지고 원하는 페이지로 이동 시키거나, 관리 DB등에 저장하여 사용하면 되겠죠?
로그아웃 하기
로그인을 했으니, 로그아웃 요청에 대해서도 알아보겠습니다.
로그아웃 요청은 매우 간단합니다.
UserManagement.requestLogout(new LogoutResponseCallback() {
@Override
public void onCompleteLogout() {
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
startActivity(intent);
}
});
UserManagement의 requestLogout() 인터페이스만 정의해주시면 됩니다. 위의 예제에서는 로그아웃에 성공했을 경우, 로그인 화면으로 돌아가도록 구성하였습니다.
실행화면
실행화면입니다.
로그인 화면의 버튼을 취급하여 로그인을 한 뒤, 콜백에서 사용자 정보를 요청하여 메인화면에 띄워주고 있습니다. 메인화면에는 로그아웃 버튼을 배치하여, 로그아웃 시에 다시 로그인 화면으로 돌아오도록 설정하였습니다.
실행화면
긴 글 읽어주셔서 감사합니다.
잘못된 부분이나 궁금하신 점은 댓글로 알려주시면 감사하겠습니다.
'프로그래밍 > Android' 카테고리의 다른 글
[Android] 페이스북 계정으로 로그인 하기 (5) 2018.01.17 [Android] HashKey 구하기 (3) 2018.01.16 [Android] TTS 기능 사용하기 (0) 2017.12.27 [Android] 안드로이드 스튜디오에서 JNI 사용하기 (12) 2017.12.15 [Android] ListView 이해하기 (0) 2017.10.23