Lined Notebook

네이버 로그인 API 사용

by 사슴비행기

사실 네이버 로그인은 다른 블로그를 보는 것보다

그냥 가이드 문서를 보는게 더 보기가 좋다.

네이버 로그인 SDK는 주석이 안 달려 있어서

가이드 문서를 꼼꼼하게 읽는 것이 중요하다.

 

0. iOS 가이드 문서

https://developers.naver.com/docs/login/ios/ios.md

 

1. 네이버 개발자 사이트 설정

애플리케이션 등록

일단 애플리케이션을 등록한다.

위 사진은 일단 임의로 작성한 것인데,

먼저, 애플리케이션 이름을 정하고

사용할 API 서비스를 네이버 로그인으로 선택하고

사용자 정보 중에서 필요한 정보를 체크한다.

 

나중에 검수를 따로 받아야 하니,

꼭 필요한 정보만 필수로 받도록 하는 게 좋다.

 

환경 추가 부분이 플랫폼 추가 부분이다.

이미 네이버 SNS 로그인 서비스를 다른 플랫폼(web, android...)에서

사용하고 있다면, 플랫폼만 추가하면 된다.

iOS를 추가해주고 다운로드 URL, URL Scheme을 적어준다.

사진의 가이드 문구에서 알려주는 것처럼

앱이 앱스토어에 올라가져 있으면

본인의 앱의 스토어 주소를 적으면 되고,

아직 올리기 전이라면, 서비스 웹 주소를 적으면 된다.

그리고 URL Scheme는 네이버 로그인창 (네이버앱 또는 사파리)을 띄우고

다시 본인의 앱으로 돌아올 때 필요한 Scheme값이다.

나중에 프로젝트 파일에 설정해야 할 부분이 있으니 참고.

그리고 사파리를 사용했을 때

URL Scheme에 대문자가 있으면

인증이 제대로 되지 않는 문제가 발생할 수 있다고 하니

되도록 소문자로 하는게 좋을 것 같다.

 

2. 프로젝트 설정

 

네이버 개발자 사이트에서 등록한 URL Scheme을

프로젝트 타깃의 Info -> URL Types에 적으면 된다.

 

그리고 Info.plist 파일에

LSApplicationQueriesSchemes에 Array로

naversearchapp, naversearchthirdlogin을 추가하면 되는데

Info.plist를 우클릭해서 Source Code로 보기를 한 다음

<key>LSApplicationQueriesSchemes</key>
	<array>
		<string>naversearchapp</string>
		<string>naversearchthirdlogin</string>
	</array>

이렇게 추가하는게 더 쉬울 수도 있다.

 

3. Cocoapods로 추가

https://developers.naver.com/docs/login/sdks/sdks.md

 

여기에서 Github으로 iOS 로그인 라이브러리를 제공하고 있다.

Github에 들어가 보면 어떻게 추가해야 하는지 잘 나와 있으니 참고하자.

 

4. 네이버 로그인 활성화

이제 네이버 로그인을 활성화 하려면

AppDelegate에서 설정을 해주어야 한다.

 

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool

이 함수 안에

/// 네이버 로그인 셋팅
    func settingNaverSNSLogin() {
        
        let instance = NaverThirdPartyLoginConnection.getSharedInstance()
        //네이버 앱으로 인증하는 방식 활성화
        instance?.isNaverAppOauthEnable = true
        //SafariViewController에서 인증하는 방식 활성화
        instance?.isInAppOauthEnable = true
        //인증 화면을 아이폰의 세로모드에서만 적용
        instance?.isOnlyPortraitSupportedInIphone()
        
        instance?.serviceUrlScheme = kServiceAppUrlScheme
        instance?.consumerKey = kConsumerKey
        instance?.consumerSecret = kConsumerSecret
        instance?.appName = kServiceAppName
    }

이 코드를 넣어 주면 되는데,

네이버 로그인 인스턴스를 만들고,

네이버 앱으로 인증하는 방법을 활성화 하고

사파리 앱으로 인증하는 방법도 활성화한다.

그리고 인증화면을 세로모드에서만 적용할 거라면

isOnlyPortraitSupportedInIphone() 메서드도 호출해준다.

 

그리고 그 아래에 있는 코드는

위의 1. 네이버 개발자 사이트 설정 에서

애플리케이션을 등록하면 생기는

값들이 있는데, 이것을 셋팅해주어야 한다.

일단 kServiceAppUrlScheme를 코드 라인 중 아무 곳에나 적고

cmd + ctl + j 단축키를 누르면

해당 변수가 선언된 곳으로 이동할 수 있는데

kServiceAppUrlScheme는 아까 전에

URL Scheme에 적었던 값을 넣으면 되고

kConsumerKey는

[네이버 개발자 사이트] -> [내 애플리케이션] -> [개요]

에 있는 값 중에서 Client ID에 있는 값을 적으면 된다.

kConsumerSecret는 

[네이버 개발자 사이트] -> [내 애플리케이션] -> [개요]

에 있는 값 중에 Client Secret를 '보기' 버튼을 눌러서

나오는 값을 적으면 된다.

kServiceAppName은 애플리케이션 이름을 적으면 된다.

 

5. Scheme에 따른 처리

 

네이버 앱 또는 사파리로 이동했다가

결과 값을 들고 다시 앱으로 돌아왔을 때

스킴을 처리해주는 코드도 추가해야 한다.

이것은 SceneDelegate를 지원하면,

AppDelegate와 SceneDelegate 모두 설정해 주어야 한다.

 

1) AppDelegate

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool

이 메서드에 아래 코드 추가

NaverThirdPartyLoginConnection.getSharedInstance().application(app, open: url, options: options)

 

2) SceneDelegate

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>)

이 메서드에 아래 코드 추가

NaverThirdPartyLoginConnection.getSharedInstance().receiveAccessToken(URLContexts.first?.url)

 

6. 네이버 로그인을 처리하는 싱글톤 만들기

여기서는 그냥 화면에서 필요한 코드를

따로따로 구현해도 되고

여러 군데에서 같은 코드를 쓰기 귀찮다면,

나 처럼 싱글톤 클래스를 만들어도 될 것 같다.

 

아래 코드 설명은 주석과 네아로 iOS 가이드 문서를

참고하면 될 것 같다.

(쓰기 귀찮아서 그런거 아님)

 

아, 참고로 아래 코드에서는 사용자 정보를

앱 내에 저장한다고 되어 있는데

(아래 코드엔 한 줄로 되어 있지만 UserDefualt에 저장하고 있다)

샘플앱이라서 그런거고,

실제 앱에 적용할 때는

좀 더 신중하게 저장하거나

리프레시 토큰, 엑세스 토큰으로

필요할 때만 사용자 정보를 가져와서

보여주는 게 맞는 것 같다.

 

 

//
//  NaverSNSLogin.swift
//
//  Created by app on 2022/06/13.
//

import Foundation
import NaverThirdPartyLogin
import Alamofire

class NaverSNSLogin: NSObject {
    
    //싱글톤 접근 상수
    static let shared: NaverSNSLogin = NaverSNSLogin()
    
    //네이버 로그인 인스턴스
    private let instance = NaverThirdPartyLoginConnection.getSharedInstance()
    
    //네이버 로그인 성공시 처리
    var success: ((_ loginData: NaverLogin) -> Void)? = { loginData in
        //doSomething
    }
    //네이버 로그인 실패시 처리
    var failure: ((_ error: AFError) -> Void)? = { error in
        print(error.localizedDescription)
        //doSomething
    }
    
    //네이버 사용자 정보를 받아올 구조체
    struct NaverLogin: Decodable {
        var resultCode: String
        var response: Response
        var message: String
        
        struct Response: Decodable {
            var email: String
            var id: String
            var name: String
        }
        
        enum CodingKeys: String, CodingKey {
            case resultCode = "resultcode"
            case response
            case message
        }
    }
    
    // 사용자 정보를 받아오기 전에 토큰 체크
    private func getInfo() {
        
        guard let isValidAccessToken = instance?.isValidAccessTokenExpireTimeNow() else {
            //로그인 필요
            login()
            return
        }
        
        if !isValidAccessToken {
            //접근 토큰 갱신 필요
            refreshToken()
            return
        } else {
            userInfo()
        }
    }
    
    //로그인 한다.
    func login() {
        instance?.delegate = self
        instance?.requestThirdPartyLogin()
    }
    
    //토큰을 갱신한다.
    func refreshToken() {
        instance?.delegate = self
        self.instance?.requestAccessTokenWithRefreshToken()
    }
    
    //로그아웃한다.
    func logout() {
        instance?.delegate = self
        SampleAppUser.shared.removeAllData()	//앱에 저장된 사용자 정보 삭제
        instance?.resetToken()
    }
    
    //네이버 로그인 서비스 연결을 해지한다.
    func disConnect() {
        instance?.delegate = self
        SampleAppUser.shared.removeAllData()	//앱에 저장된 사용자 정보 삭제
        instance?.requestDeleteToken()
    }
    
    //사용자 정보를 받아온다.
    func userInfo() {
        
        guard let tokenType = instance?.tokenType else { return }
        guard let accessToken = instance?.accessToken else { return }
        
        let urlStr = "https://openapi.naver.com/v1/nid/me"
        let url = URL(string: urlStr)!
        
        let authorization = "\(tokenType) \(accessToken)"
        let req = AF.request(url, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: ["Authorization": authorization])
        
        req.responseDecodable(of: NaverLogin.self) { [self] response in
            print(response)
            print(response.result)
            
            switch response.result {
            case .success(let loginData):
                print(loginData.resultCode)
                print(loginData.message)
                print(loginData.response)
                
                if let success = self.success {
                    success(loginData)
                }
                
                break
            case .failure(let error):
                print("error: \(error.localizedDescription)")
                
                if let failure = self.failure {
                    failure(error)
                }
                
                break
            }
        }
    }
}


//MARK: - Naver Login Delegate
extension NaverSNSLogin: NaverThirdPartyLoginConnectionDelegate {
    // 로그인에 성공한 경우 호출 됨
    func oauth20ConnectionDidFinishRequestACTokenWithAuthCode() {
        print("네이버 로그인 성공")
        getInfo()
    }
    // 토큰 갱신 성공 시 호출 됨
    func oauth20ConnectionDidFinishRequestACTokenWithRefreshToken() {
        print("네이버 토큰 갱신 성공")
        getInfo()
    }
    // 연동해제 성공한 경우 호출 됨
    func oauth20ConnectionDidFinishDeleteToken() {
        print("네이버 연동 해제 성공")
    }
    // 모든 error인 경우 호출 됨
    func oauth20Connection(_ oauthConnection: NaverThirdPartyLoginConnection!, didFailWithError error: Error!) {
        let alert = UIAlertController(title: "네이버 SNS 로그인 실패", message: "이유: \(String(error.localizedDescription))\n문제가 반복된다면 관리자에게 문의하세요.", preferredStyle: .alert)
        let ok = UIAlertAction(title: "확인", style: .default)
        alert.addAction(ok)
        
        //topViewController를 구해서, 있으면 alert을 띄움
        if let vc = UIApplication.topViewController(base: nil) {
            vc.present(alert, animated: true)
        }
    }
    
}

 

 


7. Error

Request could not be completed because Connection is Busy

네이버 로그인을 하는데 로그창에 이 문구가 뜨면서 네이버 로그인 창이나 아무 동작을 하지 않는다면,

몇 번 하다보면 인증절차 없이 로그인이 되어, 로그인 유지 상태로 들어가는 경우일 수 있다.

이런경우 토큰이 있는지 확인하고 바로 사용자 데이터를 얻어서 로그인이  된 화면으로 이동시키면 될 듯 하다.

 

-canOpenURL: failed for URL: "naversearchapp://" - error: "The operation couldn’t be completed. (OSStatus error -10814.)"

2. 프로젝트 설정의 Info.plist에서 naversearchapp을 다시 확인 해봐야 한다.

나의 경우는 기존에 잘 등록이 되어 있는데도 이 에러가 떠서 기존에 넣어놨던 값을 삭제하고

다시 등록하였더니 되었다. (왜지...)

-canOpenURL: failed for URL: "naversearchthirdlogin://" - error: "The operation couldn’t be completed. (OSStatus error -10814.)"

이 것도 바로 위 오류와 마찬가지다.

2. 프로젝트 설정의 Info.plist에서 naversearchthirdlogin을 다시 확인 해봐야 한다.

 

 

 


8. 참고한 블로그

 

[Swift] Naver 로그인 API 사용하기(네아로 사용하기)

NAVER 로그인 API 사용하기 Record  작성일 2019. 10. 11 (금) 2020. 06. 23(화) Swift 버전 Swift 5 Swift 5.1 Xcode 버전 11.1 11.5 안녕하세요 :] 학원의 프로젝트도 끝나고 해서 오랜만에 블로그를 다시 시작..

developer-fury.tistory.com

 

'swift > Library Use' 카테고리의 다른 글

swift collectionView로 card view 만들기  (0) 2019.07.02
Swift AMPopTip Library 사용  (0) 2019.07.01

블로그의 정보

Beautiful Coding

사슴비행기

활동하기