DEV - iOS/iOS

[Swift / JAVA] iOS 프로젝트 Google Login 서버 만들기

베이비코더 2022. 11. 1. 20:56
반응형

https://im-babycoder.tistory.com/entry/Swift-iOS-프로젝트-Google-Login-구현하기

 

[Swift] iOS 프로젝트 Google Login 구현하기

친구들과 같이 진행하는 프로젝트에 Google Login 기능을 넣기로 했다. https://developers.google.com/identity/sign-in/ios/start-integrating iOS 및 macOS용 Google 로그인 시작하기 | Authentication | Google Developers 이 페이지

im-babycoder.tistory.com

이 글에 이어서 SpringBoot 서버를 만들어봤다.

 

https://developers.google.com/identity/sign-in/ios/backend-auth

 

백엔드 서버로 인증  |  Authentication  |  Google Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. Switch to English 의견 보내기 백엔드 서버로 인증 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 백엔드

developers.google.com

방법은 역시나 구글 공식 문서 참고


LoginViewController.swift

var loginModel: LoginModel?

// SignIn Google Action
@IBAction func signInWithGoogle(_ sender: Any) {
    GIDSignIn.sharedInstance.signIn(with: self.signInConfig, presenting: self) { user, error in
        guard error == nil else { return }
        guard let user = user else { return }

        user.authentication.do { authentication, error in
            guard error == nil else { return }
            guard let authentication = authentication else { return }

            let idToken = authentication.idToken
            // 백엔드 서버로 idToken 전송
            self.requestIdTokenAuth(idToken: idToken!)
        }

        // request 후 화면 이동 코드 생략
        // if loginModel.joinYn == nil ... 등등
    }
}

func requestIdTokenAuth(idToken: String) {
    guard let postData = try? JSONEncoder().encode(["idToken" : idToken, "appType" : "ios"]) else { return }
    let url = URL(string: "http://localhost:8080/test/login")
    var request = URLRequest(url: url!)
    request.httpMethod = "POST"
    request.httpBody = postData
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard error == nil else { return }

        if let data = data, let dataString = String(data: data, encoding: .utf8) {
            print("Response data string:\n \(dataString)")
            do {
                self.loginModel = try JSONDecoder().decode(LoginModel.self, from: data)
            } catch {
                print(error)
            }
        }
    }
    task.resume()
}

구글 로그인에 성공해서 idToken이 생성되면 이 값을 서버로 전송하는 requestIdTokenAuth 메소드를 만들었다.

Json 형식으로 idToken과 appType을 함께 담아서 httpBody에 실어 보냈다.

(appType은 현재 진행하는 프로젝트에서는 ios와 web 두 곳에서 구글 로그인 진입을 하기 때문에 만든 것)

 

서버에서 응답으로 보내주는 값은 LoginModel.swift Codable 구조체를 만들어서 받아왔다.(하단 참고)

GoogleSignInController.java

import java.util.Collections;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.googleapis.util.Utils;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.openbible.cymply.demo.google.vo.GoogleSignInVO;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;


@RestController
public class GoogleSignInController {

    // api/iosGoogleTest
    @PostMapping("/test/login") // url 이름 잘 맞추기
    public GoogleSignInRVO cymplyLogin(@RequestBody GoogleSignInPVO pvo) throws Exception {
        GoogleSignInRVO resultVO = new GoogleSignInRVO();

        HttpTransport transport = Utils.getDefaultTransport();
        JsonFactory jsonFactory = Utils.getDefaultJsonFactory();

        GoogleIdToken idToken = null;
        if("ios".equals(pvo.getAppType())) {
            // iOS 접속
            GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
            .setAudience(Collections.singletonList("iOS_클라이언트_ID")).build();
            idToken = verifier.verify(pvo.getIdToken());
        } else {
            // WEB 접속
            GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
            .setAudience(Collections.singletonList("WEB_클라이언트_ID")).build();     
            idToken = verifier.verify(pvo.getIdToken());
        }

        if(idToken != null) {
            Payload payload = idToken.getPayload();

            String email = payload.getEmail();
            System.out.println("login email : " + email);

            // Email DB 조회 결과 setting (하드코딩)
            resultVO.setJoinYN("N");
            resultVO.setLoginYN("Y");
            resultVO.setNickname(null);
        } else {
            System.out.println("Invalid ID token.");
        }

        return resultVO;
    }
    
}

payload를 사용해서 email 외에도 이름 등등 로그인한 유저의 정보를 받아올 수 있다.(공식 문서 참고)

 

로그인한 유저 정보를 DB 조회를 해서 이 유저가 서비스에 회원가입 했는지, 닉네임은 설정했는지 등등

결과를 VO에 담아서 리턴해준다.

 

보통 Controller에 이 모든 로직을 담지 않고 Service단으로 나누는데 일단 테스트니까 Controller에 모두 구현했다.

 

이 리턴한 VO는 Swift에서 다시 JSON 형태로 받아 사용할 수 있다.

 

ParameterVO

ios나 web 클라이언트에서 서버가 받는 객체 VO

import lombok.Data;

@Data
public class GoogleSignInPVO {
    private String idToken;
    private String appType;   
}

 

Lombok @Data 어노테이션으로 변수 선언만으로도 Getter, Setter 메소드를 사용할 수 있게 했다.

다시 클라이언트로 응답을 보낼 VO도 위와 같이 만들어서 응답 값을 담아서 return해주면 된다.

Return Result VO

import lombok.Data;

@Data
public class GoogleSignInPVO {
    private String joinYn;
    private String loginYn;
    private String nickname;   
}

자바에서 이 객체들에 값을 담아 보낸다면

클라이언트에서도 이름을 잘 맞춰서 가져올 수 있게 해야한다.

LoginModel.swift

import Foundation

struct LoginModel: Codable {
    let joinYn: String
    let loginYn: String
    let nickname: String?
}

응답으로 받은 Data를 Model에 담아 다시 ios 클라이언트에서 사용할 수 있다.

 

iOS에서 Request 요청이 안 보내진다면

info.plist

이걸 추가해서 HTTP 통신 가능하게 해야된다.

 

 

반응형