안녕하세요. 린다임니다.
제노에서 소셜 로그인으로 카카오톡을 구현했었는데요..
저희는 어차피 파베를 써야했기 때문에 파베 Auth와 연동하여 사용하였습니다.
원래 파베 Auth 보면 create해서 유저한테 이메일이랑 비밀번호를 받아서
추가시켜주어야 하잖아여 ... ?
그래서 카카오도 구현하면서 그렇게 구현했어요.
어 근데 카카오 소셜 로그인은 비밀번호를 안 받지 않나여 ?
맞아여 그래서.. 유저 식별 id? 값으로 비밀번호를 생성해서
Auth로 가입시켜서 사용하였습니다..
그러니까 카카오로 로그인하면 자동으로 파베 Auth에 추가되도록 구성해 놓았어요.
왜 이렇게 했느냐면.. 다양한 소셜 로그인을 추가로 구현할 때
이메일 중복 가입을 막고 싶어서 그랬는데..
추후에 애플 로그인 .. 추가할 때 보니 애플로그인은 비공계 메일이 가능해서
소용이 없는 짓이었다...고 합니다..
그래도 네이버나 커스텀 회원가입을 구현했다면.. 소용이 있어지겠져..?
킁 그래서 .. 설명을 해보자면..
하긴해야하는데 일단은 제가 구현하면서 팀원들 보라고 정리해놓았던
노션을 복사해 왔어요.. 일단 한번 보시면 이해가지 않으실까 싶습니다..
근데 이러고 async-await 사용한다고 싹 갈아엎긴했는데
기본 로직은 똑같아여. 로직을 보시면 됩니다.. (그것도 제가 정리되면 가져와볼게여..)
먼저 로그인 버튼 눌렀을 때가 시작입니다!!
kakaoLoginButtonTapped()
- 로그인 버튼 눌렀을 때 타는 함수
if AuthApi.hasToken() {
UserApi.shared.accessTokenInfo { _, error in
if let error = error {
print("DEBUG: 카카오톡 토큰 가져오기 에러 \\(error.localizedDescription)")
Task {
await self.kakaoLogin()
}
} else {
// 토큰 유효성 체크 성공 (필요 시 토큰 갱신됨)
print("이미 로그인 한적 있기 때문에 토근 저장이라 바로 다음 페이지로 넘어가면 됨")
UserApi.shared.me { user, error in
if let error = error {
print("DEBUG: 기존회원 로그인 에러 발생!!! \\(error.localizedDescription)")
} else {
print("DEBUG: 기존 회원 로그인 진행!!! ")
Task {
if let email = user?.kakaoAccount?.email {
do {
try await self.userViewModel.login(
email: email,
password: (String(describing: user?.id)))
} catch {
print("로그인 실패 \\(error.localizedDescription)")
}
}
}
}
}
}
}
} else {}
if 문 true 일때 == 이미 회원가입이 된 유저가 다시 로그인 버튼을 눌렀을때
if (AuthApi.hasToken())
- AuthApi.hasToken() 함수를 사용하여 현재 앱에 저장된 카카오 토큰이 있는지 확인
- 토큰이 있는 경우, 사용자의 카카오 로그인 상태를 확인하고 처리함
- AuthApi.hasToken() 함수는 카카오 로그인에 사용되는 토큰의 존재 여부를 확인하는 함수임. 이 함수가 **true**를 반환한다는 것은 현재 앱에 유효한 카카오 로그인 토큰이 저장되어 있음을 나타냄
- 카카오 로그인 토큰 == 사용자가 로그인한 상태
- 이 토큰을 사용하여 사용자를 인증하고 해당 사용자의 정보 및 권한을 확인할 수 있음
- **AuthApi.hasToken()이 true를 반환하면, 현재 앱의 사용자가 이미 카카오 로그인을 수행했음을 의미함
따라서 AuthApi.hasToken()은 현재 앱에 저장된 카카오 로그인 토큰의 존재 여부를 확인하고, 토큰이 있는 경우에는 이를 기반으로 로그인 상태를 관리할 수 있는 도구로 사용됨
회원가입 한 사람임
→ 로그인 진행해야함 → 카카오 서버로부터 사용자의 액세스 토큰을 가져와야함
UserApi.shared.accessTokenInfo { (_, error) in }
- UserApi.shared.accessTokenInfo : 카카오 서버로부터 사용자의 액세스 토큰 정보를 가져옴
- 액세스 토큰 정보란?
- 액세스 토큰은 사용자를 인증하고 권한을 관리하는 데 사용되는 중요한 정보를 포함하는 토큰
- 사용자가 카카오 로그인을 통해 앱에 인증되었음을 나타냄
- 사용자 식별 정보 (사용자의 카카오 계정과 연결되며, 해당 사용자를 식별하는데 사용)
- 권한 및 범위 (앱이 요청한 권한 및 범위 정보를 포함)
- 만료시간
- 발급자 정보
- 기타 필요한 정보
- 액세스 토큰 정보란?
사용자 액세스 토큰 받아오기에서 에러가 발생했을때!!
현재 앱의 사용자가 이미 카카오 로그인을 수행한적이 있는데.. (if = true / 회원가입한 유저인데)
→ 토큰을 못가져옴 --> 토큰을 재발행해주어야함 == 카카오톡 로그인을 다시해주어야 하는것!
if AuthApi.hasToken() {
UserApi.shared.accessTokenInfo { _, error in
if let error = error {
print("DEBUG: 카카오톡 토큰 가져오기 에러 \\(error.localizedDescription)")
Task {
await self.kakaoLogin()
}
} else { }
- 어쨌든 회원가입이 되어있는 유저이기 때문인테 토큰이 없는거기 때문에 회원가입 없이 다시 로그인 ㄱㄱ
- Task {} 로 감싼 이유는 UserApi.shared.accessTokenInfo 후행클로저인데, 클로저 안에서는 Task를 사용해야 async 함수를 사용할 수 있음!!!
사용자 액세스 토큰을 잘 받아왔을때!
현재 앱의 사용자가 이미 카카오 로그인을 수행한적이 있는데.. 토큰정보까지 잘 받아왔을경우!
→ 이제는 사용자의 로그인을 위해서 이메일과 비밀번호가 필요한 타이밍 → UserApi.shared.me 함수 사용
else {
// 토큰 유효성 체크 성공 (필요 시 토큰 갱신됨)
UserApi.shared.me { user, error in
if let error = error {
print("DEBUG: 기존회원 로그인 에러 발생!!! \\(error.localizedDescription)")
} else {
print("DEBUG: 기존 회원 로그인 진행!!! ")
Task {
if let email = user?.kakaoAccount?.email {
do {
try await self.userViewModel.login(
email: email,
password: (String(describing: user?.id)))
} catch {
print("로그인 실패 \\(error.localizedDescription)")
}
}
}
}
}
}
- UserApi.shared.me : 현재 로그인된 KakaoTalk 사용자의 정보를 비동기적으로 가져올 수 있는 함수
- → 현재 카카오톡에 로그인한 사용자의 정보를 가져올 수 있음
- 에러가 발생하지 않았다면, 사용자의 정보를 잘 가져왔다면 ( else 문 )
- userViewModel.login 함수의 매개변수로 해당 유저의 메일(id정보), 유저의 id(password정보) login 함수 실행 (파베에 저장된 auth 정보와 매칭 시켜서 로그인 하는 거임)
➡ 즉,
UserApi.shared.accessTokenInfo는 현재 로그인한 사용자의 액세스 토큰 정보를 반환하며, 이 토큰 정보를 사용하여 카카오 서버로부터 토큰의 유효성을 확인할 수 있음. 토큰의 유효성을 확인하는 것은 사용자가 로그인한 상태를 유지하고, 토큰이 만료되지 않았는지 확인하는 데 중요함.
따라서 AuthApi.hasToken() 함수로 확인한 토큰과 **UserApi.shared.accessTokenInfo**로 가져온 토큰은 동일한 토큰이며, 이 토큰을 사용하여 사용자의 로그인 상태 및 권한을 관리할 수 있다.
다시 if AuthApi.hasToken() {} 여기 실행으로 가서…
회원가입을 하지 않은 유저가 로그인 버튼을 눌렀다면!!
→ 회원가입을 해야함
if AuthApi.hasToken() {
... ...
} else {
Task {
await kakaoLogin()
}
}
- 현재 앱의 사용자가 이미 카카오로 회원가입을 하지 않았다면, 회원가입을 할수있도록 kakaoLogin()을 실행
실질적인 카카오톡 로그인 실행 부분
func kakaoLogin() async {
await MainActor.run(body: {
if UserApi.isKakaoTalkLoginAvailable() {
kakaoLoginInApp() // 카카오톡 앱이 있다면 앱으로 로그인
} else {
kakaoLoginInWeb() // 앱이 없다면 웹으로 로그인 (시뮬레이터)
}
})
}
- UserApi.isKakaoTalkLoginAvailable() : 카카오톡 앱 설치 유무 감별 함수
kakaoLoginInApp()
func kakaoLoginInApp() {
UserApi.shared.loginWithKakaoTalk { oauthToken, error in
if let error = error {
print("DEBUG: 카카오톡 로그인 에러 \\(error.localizedDescription)")
} else {
print("DEBUG: 카카오톡 로그인 Success")
if let token = oauthToken {
print("DEBUG: 카카오톡 토큰 \\(token)")
Task {
await self.loginInFirebase()
}
}
}
}
}
- UserApi.shared.loginWithKakaoTalk : 카톡 SDK를 사용하여 앱을 통해 사용자를 로그인하고 카톡 인증 토큰을 받아오는 함수
- 매개변수 설명
- OAuthToken?: KakaoTalk 로그인에 성공하면 KakaoTalk 인증 토큰이 포함된 OAuthToken 객체가 전달됨. 이 토큰을 사용하여 사용자를 인증하고 사용자의 정보를 가져올 수 있음
- Error?: KakaoTalk 로그인을 시도하다가 발생한 오류가 이 매개변수에 전달됨. 만약 오류가 발생하면 해당 오류를 처리할 수 있음
- 매개변수 설명
- 에러가 발생하지 않고, 토큰 값이 nil이 아니라면 loginInFirebase() 함수 실행
kakaoLoginWeb()
private func kakaoLoginInWeb() {
UserApi.shared.loginWithKakaoAccount { oauthToken, error in
if let error = error {
print("DEBUG: 카카오톡 로그인 에러 \\(error.localizedDescription)")
} else {
print("DEBUG: 카카오톡 로그인 Success")
if let token = oauthToken {
print("DEBUG: 카카오톡 토큰 \\(token)")
Task {
await self.loginInFirebase()
}
}
}
}
}
- UserApi.shared.loginWithKakaoAccount : 카톡 SDK를 사용하여 카톡 계정을 통해 사용자를 로그인하고 카톡 인증 토큰을 받아오는 함수 → 카톡 계정 로그인이 웹뷰로 뜸!!
- 에러가 발생하지 않고, 토큰 값이 nil이 아니라면 loginInFirebase() 함수 실행
loginInFirebase() == 회원가입해서 auth에 올리는 함수
private func loginInFirebase() async {
UserApi.shared.me { user, error in
if let error = error {
print("DEBUG: 카카오톡 사용자 정보가져오기 에러 \\(error.localizedDescription)")
} else {
print("DEBUG: 카카오톡 사용자 정보가져오기 success.")
Task {
if let email = user?.kakaoAccount?.email, let name = user?.kakaoAccount?.profile?.nickname, let gender = user?.kakaoAccount?.gender?.rawValue {
do {
if let profileURL = user?.kakaoAccount?.profile?.profileImageUrl {
let profileString = profileURL.absoluteString
// profileString은 이제 URL을 String으로 변환한 값
try await self.userViewModel.createUser(email: email, passwrod: String(describing: user?.id), name: name, gender: gender, description: "", imagePath: profileString)
print("프로필 이미지 URL: \\(profileString)")
} else {
print("프로필 이미지 URL이 없습니다.")
}
} catch {
print(error.localizedDescription)
}
}
}
}
}
}
- UserApi.shared.me : 현재 로그인된 KakaoTalk 사용자의 정보를 비동기적으로 가져올 수 있는 함수
- → 현재 카카오톡에 로그인한 사용자의 정보를 가져올 수 있음
- 에러가 발생하지 않았다면, 사용자의 정보를 잘 가져왔다면 ( else 문 )
- userViewModel.createUser 함수로 가져온 사용자의 정보로 파베 auth 회원가입 진행
signOut()
/// 카카오톡 로그아웃 (카카오톡 토큰 삭제)
func signOut() {
do {
try Auth.auth().signOut()
UserApi.shared.logout {(error) in
if let error = error {
print(error)
} else {
print("logout() success.")
}
}
print("🔵 로그아웃 성공")
} catch {
print("🔴 로그아웃 실패. 에러메세지: \\(error.localizedDescription)")
}
}
- 앱 삭제 및 탈퇴 시 사용하는 카카오톡 토큰 지우면서, auth에서도 로그아웃하기
- 사용자 액세스 토큰과 리프레시 토큰을 모두 만료시켜, 더 이상 해당 사용자 정보로 카카오 API를 호출할 수 없도록 함
호호.. 구현한지 꽤 지났더니
저도 가물가물하네용.. 'ㅅ'...
블로깅을 더 미루기전에 일단 이거 올려놓고 갑니다...
'회고 > Zeno' 카테고리의 다른 글
제노 배포와 심사 기록 1차 (SwiftUI 개발부터 iOS 배포까지) (1) | 2023.12.17 |
---|