[삽질의 기록]
안드로이드 네이티브로 네이버 로그인을 구현하는 기능을 구현했다.
API 30부터 AsyncTask의 지원이 중단되므로 아래 링크를 참고하여 Custom Thread를 필요한 부분만 일부 구현하여 로그인 기능을 만들었다.
우선 네이버 아이디로 로그인하는 SDK를 다운로드 한 후 압축을 푼다.
다운로드는 아래 링크에서 할 수 있다.
github.com/naver/naveridlogin-sdk-android/releases
SDK 압축을 풀면 .aar 파일을 확인할 수 있다.
이를 라이브러리로 적용하기위해 아래 사진과 같이 File > New > New Module을 클릭한다.
여기서 import .JAR/.AAR Package를 클릭한다.
여기서 네이버 아이디로 로그인 하기 SDK를 다운로드 후 압축을 푼 .aar 파일 경로를 입력한 후 Finish를 클릭한다.
다음으로 File > Project Structure > Dependencies > app 폴더 선택 > Dependency 여러 개 있는 부분에서 + 버튼 클릭 > Module dependency > 해당 sdk 추가한 후 apply 클릭하면, 모듈이 등록된다.
build.gradle(:Module)
dependencies {
implementation project(path: ':naveridlogin_android_sdk_4.2.6')
}
위 과정이 안 되는 경우, 직접 build.gradle(:Module)에 등록하면 된다.
아래와 같이 직접 등록하는 방법도 있다.
이 때, 당연히 해당 프로젝트 파일 안에 libs 폴더 안에 aar 파일이 있어야 한다.
dependencies {
implementation files('libs/naveridlogin_android_sdk_4.2.6.aar')
}
다음으로, 네이버 로그인을 하기 위해서 인터넷 권한을 얻어야한다.
따라서, 메니페스트 파일에 아래 코드를 삽입한다.
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
그리고 하나하나 입력하기 귀찮기 때문에 client_id, client_secret 등을 미리 strings.xml 파일에 넣어놓는다.
res/values/strings.xml
<resources>
<string name="client_id">{your client id}</string>
<string name="client_secret">{your client secret}</string>
<string name="client_name">{your client name (아무거나 상관 없음)}</string>
</resources>
client_id와 client_secret 값은 Naver Developer에서 얻을 수 있다.
우선, 본인의 애플리케이션을 생성한다.
생성했다면, Application > 내 애플리케이션 > 애플리케이션 정보에 client_id와 secret 값이 보인다.
이 값을 strings.xml에 넣는다.
이 화면에서 API 설정에서 본인이 로그인에서 제공하는 정보 중 어떤 것을 얻을 지 체크도 한다.
이 과정이 끝나면, 다시 프로젝트로 돌아온다.
본인이 로그인을 구현하는 화면에 아래와 같은 코드를 삽입한다.
activity_login.xml
<com.nhn.android.naverlogin.ui.view.OAuthLoginButton
android:id="@+id/naver_login_btn"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@mipmap/ic_naver_login_round"
android:layout_centerInParent="true"/>
그리고, login 부분을 초기화하는 함수를 아래와 같이 작성한다.
이 init() 함수를 onCreate() 함수에서 호출한다.
LoginActivity.java
private void init(){
OAUTH_CLIENT_ID = mContext.getString(R.string.client_id);
OAUTH_CLIENT_SECRET = mContext.getString(R.string.client_secret);
OAUTH_CLIENT_NAME = mContext.getString(R.string.client_name);
mOAuthLoginInstance = OAuthLogin.getInstance();
mOAuthLoginInstance.init(mContext, OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_CLIENT_NAME);
mOAuthLoginButton = (OAuthLoginButton) findViewById(R.id.naver_login_btn);
mOAuthLoginButton.setOAuthLoginHandler(mOAuthLoginHandler);
}
다음으로, LoginHandler 코드를 작성한다. 여기서 성공적으로 login을 한다면, accessToken을 받을 수 있다.
이 accessToken을 통해 사용자 정보를 받아올 수 있다. 사용자 정보를 받아오는 함수는 getUser()에서 받아온다.
LoginActivity.java
private OAuthLoginHandler mOAuthLoginHandler = new OAuthLoginHandler() {
@Override
public void run(boolean success) {
if(success){
String accessToken = mOAuthLoginInstance.getAccessToken(mContext);
long expiresAt = mOAuthLoginInstance.getExpiresAt(mContext);
getUser(accessToken);
}
else{
String errorCode = mOAuthLoginInstance.getLastErrorCode(mContext).getCode();
String errorDesc = mOAuthLoginInstance.getLastErrorDesc(mContext);
}
}
};
getUser 함수는 다음과 같다. getUser 함수에서 getUserTask라는 CustomThread를 생성 후 실행한다.
LoginActivity.java
private void getUser(String token){
new GetUserTask().execute(token);
}
GetUserTask class는 아래와 같이 구성되어있다.
ThreadTask class에는 doInBackGround와 onPostExecute 함수로 구성되어있다.
또한, Naver Developer의 네이버 회원 프로필 조회 API 명세를 통해 HttpURLConnection을 통해 사용자 정보를 얻어왔다.
LoginActivity.java
private class GetUserTask extends ThreadTask<String, String>{
@Override
protected String doInBackground(String s) {
String header = "Bearer " + s;
String url = "https://openapi.naver.com/v1/nid/me";
Map<String, String> requestHeaders = new HashMap<>();
requestHeaders.put("Authorization", header);
String responseBody = get(url, requestHeaders);
return responseBody;
}
private String get(String url, Map<String, String> requestHeaders){
HttpURLConnection connection = connect(url);
try {
connection.setRequestMethod("GET");
for (Map.Entry<String, String> header : requestHeaders.entrySet()) {
connection.setRequestProperty(header.getKey(), header.getValue());
}
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
return readBody(connection.getInputStream());
} else {
return readBody(connection.getErrorStream());
}
} catch (IOException e) {
throw new RuntimeException("API 요청 및 응답 실패");
} finally {
connection.disconnect();
}
}
private HttpURLConnection connect(String apiurl){
try{
URL url = new URL(apiurl);
return (HttpURLConnection)url.openConnection();
} catch (MalformedURLException e) {
throw new RuntimeException("API URL이 잘못되었습니다. : " + apiurl, e);
} catch (IOException e) {
throw new RuntimeException("연결을 실패했습니다. : " + apiurl, e);
}
}
private String readBody(InputStream body){
InputStreamReader streamReader = new InputStreamReader(body);
try(BufferedReader lineReader = new BufferedReader(streamReader)){
StringBuilder responseBody = new StringBuilder();
String line;
while((line = lineReader.readLine()) != null){
responseBody.append(line);
}
return responseBody.toString();
} catch (IOException e) {
throw new RuntimeException("API 응답을 읽는데 실패했습니다. ", e);
}
}
/*
nickname, email, gender, birthday 외에도 회원 이름(본명?), 프로필 사진, 연령대도 가져올 수 있음.
*/
@Override
protected void onPostExecute(String s) {
try {
JSONObject jsonObject = new JSONObject(s);
if(jsonObject.getString("resultcode").equals("00")){
JSONObject object = new JSONObject(jsonObject.getString("response"));
String nickname = object.getString("nickname");
String email = object.getString("email");
String gender = object.getString("gender");
String birthday = object.getString("birthday");
model = new NaverUserModel(nickname, email, gender, birthday);
}
loginDoneActivity();
} catch (JSONException e) {
e.printStackTrace();
}
}
}
Naver Developer 회원 정보 조회 API 명세
developers.naver.com/docs/login/profile/
+) ThreadTask.java
public abstract class ThreadTask<T1, T2> implements Runnable {
T1 mArgument;
T2 mResult;
final public void execute(final T1 arg){
mArgument = arg;
Thread thread = new Thread(this);
thread.start();
try {
thread.join();
}
catch (InterruptedException e){
e.printStackTrace();
onPostExecute(null);
return;
}
onPostExecute(mResult);
}
@Override
public void run() {
mResult = doInBackground(mArgument);
}
protected abstract T2 doInBackground(T1 arg);
protected abstract void onPostExecute(T2 Result);
}
++) 깃허브로 정리
github.com/Banlim/implement_social_login/blob/main/naver_login/README.md
+++) Retrofit2 + Rxjava2를 통해 회원 프로필 조회하는 방법 추가 예정.
'안드로이드' 카테고리의 다른 글
[Android] Retrofit2로 recyclerview 페이징 처리하기 (0) | 2020.12.10 |
---|---|
[Android] Disable tooltip Text in Bottom Navigation View (0) | 2020.12.03 |
[Android] Bottom navigation view + FAB border customizing (0) | 2020.12.03 |
[Android] Powermenu android.view.windowLeaked Error (2) | 2020.12.02 |
[Android] FAB image size 조정하기 (0) | 2020.12.02 |