1편과 이어지는 2편 글임
1편🙊
https://velog.io/@maddie/Android-카카오-로그인-화면-구현하기
이전 글에서 카카오 로그인 화면 구현과 기능에 필요한 기본 설정을 해주었다.
카카오 로그인만 해서 되는게 아니라, 프론트에서 카카오 서버로부터 액세스 토큰을 받으면 이를 백엔드에 전달해줘야 한다.🙈
https://developers.kakao.com/docs
카카오 디벨로퍼 홈페이지를 따라하면 이전 글인 1편에 있는 내용을 모두 구현할 수 있지만,
그 이후의 내용은 잘 안나와있어
몇일을 삽질했다.
우선 백엔드와 http 통신을 해야 하기 때문에,
레트로핏을 사용할거다.
레트로핏 기본 설정과 사용방법은 아래 링크 참고
내가 씀🙊
https://velog.io/@maddie/Android-Retrofit으로-로그인-기능-구현
import com.google.gson.GsonBuilder
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.Body
import retrofit2.http.Header
import retrofit2.http.POST
interface LoginService {
@POST("/oauth/kakao/")
fun postAccessToken(
//@Header("access_token") token: String
@Body jsonParams : UserModel
): Call<LoginBackendResponse>
companion object {
private val gson = GsonBuilder().setLenient().create()
fun loginRetrofit(): LoginService {
return Retrofit.Builder()
.baseUrl("https:BASE URL 주소")
// .addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(LoginService::class.java)
}
}
}
import com.google.gson.GsonBuilder
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitHelper {
val BASE_URL: String = "https://8e27-211-106-114-186.jp.ngrok.io/"
var gson = GsonBuilder().setLenient().create()
fun getRetrofitInstance(): Retrofit {
val builder: Retrofit.Builder = Retrofit.Builder()
val retrofit = builder.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
return retrofit
}
}
==JSON data를 받아올 데이터 클래스
data class UserModel(
val accessToken : String ?= null
)
data class LoginBackendResponse(
val email : String,
//200: 성공, 300,400: 에러
val accessToken : String,
val refreshToken : String
)
/* 이렇게도 쓸 수 있음
data class DataclassEx(
val `data`: List<Data>,
val message: String,
val status: Int,
val isLogin: Boolean
)
{
data class Data(
val id: Int,
val image: String,
val user_id: Int,
val user_img: String,
val user_name: String
)
}
*/
import android.content.ContentValues.TAG
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import com.example.kakaologin5.databinding.ActivityMainBinding
import com.kakao.sdk.auth.model.OAuthToken
import com.kakao.sdk.common.model.AuthErrorCause
import com.kakao.sdk.common.model.ClientError
import com.kakao.sdk.common.model.ClientErrorCause
import com.kakao.sdk.user.UserApiClient
import com.kakao.sdk.user.model.User
import retrofit2.Call
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class MainActivity : AppCompatActivity() {
private var retrofit: Retrofit = RetrofitHelper.getRetrofitInstance() // RetrofitClient의 instance 불러오기
private lateinit var binding : ActivityMainBinding
private var authToken : String ?= null
var api : LoginService = retrofit.create(LoginService::class.java)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
retrofit = Retrofit.Builder()
.baseUrl("https://8e27-211-106-114-186.jp.ngrok.io/")
.addConverterFactory(GsonConverterFactory.create())
.build()
//로그인 정보 확인
UserApiClient.instance.accessTokenInfo{ tokenInfo, error ->
if(error != null){
Toast.makeText(this, "토큰 정보 보기 실패", Toast.LENGTH_SHORT).show()
}
else if(tokenInfo != null) {
Toast.makeText(this, "토큰 정보 보기 성공", Toast.LENGTH_SHORT).show()
}
}
/* Click_listener */
binding.btnLogin.setOnClickListener {
kakaoLogin() //로그인
}
binding.btnLogout.setOnClickListener {
kakaoLogout() //로그아웃
}
}
private fun kakaoLogin() {
// 카카오계정으로 로그인 공통 callback 구성
// 카카오톡으로 로그인 할 수 없어 카카오계정으로 로그인할 경우 사용됨
val callback: (OAuthToken?, Throwable?) -> Unit = { token, error ->
if (error != null) {
when {
error.toString() == AuthErrorCause.AccessDenied.toString() -> {
Toast.makeText(this, "접근이 거부 됨(동의 취소)", Toast.LENGTH_SHORT).show()
}
error.toString() == AuthErrorCause.InvalidClient.toString() -> {
Toast.makeText(this, "유효하지 않은 앱", Toast.LENGTH_SHORT).show()
}
error.toString() == AuthErrorCause.InvalidGrant.toString() -> {
Toast.makeText(this, "인증 수단이 유효하지 않아 인증할 수 없는 상태", Toast.LENGTH_SHORT).show()
}
error.toString() == AuthErrorCause.InvalidRequest.toString() -> {
Toast.makeText(this, "요청 파라미터 오류", Toast.LENGTH_SHORT).show()
}
error.toString() == AuthErrorCause.InvalidScope.toString() -> {
Toast.makeText(this, "유효하지 않은 scope ID", Toast.LENGTH_SHORT).show()
}
error.toString() == AuthErrorCause.Misconfigured.toString() -> {
Toast.makeText(this, "설정이 올바르지 않음(android key hash)", Toast.LENGTH_SHORT).show()
}
error.toString() == AuthErrorCause.ServerError.toString() -> {
Toast.makeText(this, "서버 내부 에러", Toast.LENGTH_SHORT).show()
}
error.toString() == AuthErrorCause.Unauthorized.toString() -> {
Toast.makeText(this, "앱이 요청 권한이 없음", Toast.LENGTH_SHORT).show()
}
else -> { // Unknown
Toast.makeText(this, "기타 에러", Toast.LENGTH_SHORT).show()
}
}
} else if (token != null) {
Log.i(TAG, "카카오계정으로 로그인 성공 ${token.accessToken}")
binding.tvAccessToken.text = "access token : \n${token.accessToken}\n"
authToken = token.accessToken
api.postAccessToken(UserModel(accessToken = authToken)).enqueue(object : retrofit2.Callback<LoginBackendResponse>{
override fun onResponse(call: Call<LoginBackendResponse>, response: Response<LoginBackendResponse>) {
Log.d("로그인 통신 성공", response.toString())
Log.d("로그인 통신 성공", response.body().toString())
when (response.code()) {
200 -> {
Log.d("로그인 성공" , "ggg")
}
405 -> Toast.makeText(
this@MainActivity,
"로그인 실패 : 아이디나 비번이 올바르지 않습니다",
Toast.LENGTH_LONG
).show()
500 -> Toast.makeText(
this@MainActivity,
"로그인 실패 : 서버 오류",
Toast.LENGTH_LONG
).show()
}
}
override fun onFailure(call: Call<LoginBackendResponse>, t: Throwable) {
Log.d("통신 로그인..", "전송 실패")
}
})
}
}
// 카카오톡이 설치되어 있으면 카카오톡으로 로그인, 아니면 카카오계정으로 로그인
if (UserApiClient.instance.isKakaoTalkLoginAvailable(this@MainActivity)) {
UserApiClient.instance.loginWithKakaoTalk(this@MainActivity) { token, error ->
if (error != null) {
Log.e(TAG, "카카오톡으로 로그인 실패", error)
// 사용자가 카카오톡 설치 후 디바이스 권한 요청 화면에서 로그인을 취소한 경우,
// 의도적인 로그인 취소로 보고 카카오계정으로 로그인 시도 없이 로그인 취소로 처리 (예: 뒤로 가기)
if (error is ClientError && error.reason == ClientErrorCause.Cancelled) {
return@loginWithKakaoTalk
}
// 카카오톡에 연결된 카카오계정이 없는 경우, 카카오계정으로 로그인 시도
UserApiClient.instance.loginWithKakaoAccount(this@MainActivity, callback = callback)
} else if (token != null) {
Log.i(TAG, "카카오톡으로 로그인 성공 ${token.accessToken}")
}
}
} else {
UserApiClient.instance.loginWithKakaoAccount(this@MainActivity, callback = callback)
}
}
private fun kakaoLogout(){
// 로그아웃
UserApiClient.instance.logout { error ->
if (error != null) {
Toast.makeText(this, "로그아웃 실패 $error", Toast.LENGTH_SHORT).show()
}else {
binding.tvIsloginned.text = "로그아웃 되었습니다."
Toast.makeText(this, "로그아웃 성공", Toast.LENGTH_SHORT).show()
}
}
}
}