구글 로그인 라이브러리인 Google Sign-in platform library가 2023년 3월 이후로 deprecated 되고 Google Identity Services library로 변경됩니다.
react-google-login
라이브러리를 사용중이었는데, 이를 삭제하고 새로운 라이브러리로 마이그레이션 하였습니다.
GCP에서 Client ID를 발급 받는 부분은 생략하였습니다. (구글링하면 잘 설명해놓은 글이 많습니다!)
d.ts
파일에 window.google
타입을 선언 (For Typescript)<!DOCTYPE html>
<html translate="no" class="notranslate">
<head>
...
<!-- Google Sign-in -->
<script src="https://accounts.google.com/gsi/client" async defer></script>
</head>
</html>
import React, { useCallback, useEffect, useRef, useState } from 'react';
import styled from 'styled-components';
import jwt_decode from 'jwt-decode';
interface Props {
handleGoogleLoginSuccess: (data: any) => void;
}
const GoogleLogin = ({ handleGoogleLoginSuccess }: Props) => {
const googleSignInButton = useRef<HTMLDivElement>(null);
const [scriptLoaded, setScriptLoaded] = useState(false);
const handleGoogleSignIn = useCallback(
(res: CredentialResponse) => {
if (!res.clientId || !res.credential) return;
const userObject = jwt_decode(res.credential);
const { email, name } = userObject as ICredential;
LoginIn(email, name); // your sign-in
setScriptLoaded(false);
},
[handleGoogleLoginSuccess],
);
function onClickGooglelogin() {
let element: HTMLElement = document.querySelector(
'[aria-labelledby="button-label"]',
) as HTMLElement;
element.click();
}
useEffect(() => {
if (
typeof window === 'undefined' ||
!window.google ||
!googleSignInButton.current
) {
return;
}
const { google } = window;
const initializeGoogle = () => {
if (!google || scriptLoaded || googleClientId === undefined) return;
setScriptLoaded(true);
google.accounts.id.initialize({
client_id: process.env.REACT_APP_GOOGLE_CLIENT_ID,
callback: handleGoogleSignIn,
});
const parent = document.getElementById('google-login-api');
if (parent) {
google.accounts.id.renderButton(parent, {
type: 'icon',
});
}
};
const script = document.createElement('script');
script.src = 'https://accounts.google.com/gsi/client';
script.onload = initializeGoogle;
script.async = true;
script.id = 'google-client-script';
document.querySelector('body')?.appendChild(script);
return () => {
window.google?.accounts.id.cancel();
document.getElementById('google-client-script')?.remove();
};
}, [handleGoogleSignIn, scriptLoaded]);
return (
<>
<GoogleButton id="google-login-api" ref={googleSignInButton} />
<GoogleButtonImage
src={'asset/images/logo_google.png'}
alt="google login"
onClick={onClickGooglelogin}
/>
</>
);
};
const GoogleButton = styled.div`
display: none;
`;
const GoogleButtonImage = styled.img`
height: 48px;
width: 48px;
border-radius: 50%;
cursor: pointer;
`;
export default GoogleLogin;
구글에서 타입을 제공하진 않지만 만들어주신 분들이 있었습니다. reference를 참고하여 필요한 것을 추가하였습니다.
interface IdConfiguration {
client_id: string;
auto_select?: boolean;
callback: (handleCredentialResponse: CredentialResponse) => void;
login_uri?: string;
native_callback?: (...args: any[]) => void;
cancel_on_tap_outside?: boolean;
prompt_parent_id?: string;
nonce?: string;
context?: string;
state_cookie_domain?: string;
ux_mode?: 'popup' | 'redirect';
allowed_parent_origin?: string | string[];
intermediate_iframe_close_callback?: (...args: any[]) => void;
}
interface ICredential {
iss: string; // The JWT's issuer
nbf: number;
aud: string; // Your server's client ID
sub: string; // The unique ID of the user's Google Account
hd: string; // If present, the host domain of the user's GSuite email address
email: string; // The user's email address
email_verified: boolean; // true, if Google has verified the email address
azp: string;
name: string; // If present, a URL to user's profile picture
picture: string;
given_name: string;
family_name: string;
iat: number; // Unix timestamp of the assertion's creation time
exp: number; // Unix timestamp of the assertion's expiration time
jti: string;
}
interface CredentialResponse {
credential?: string;
select_by?:
| 'auto'
| 'user'
| 'user_1tap'
| 'user_2tap'
| 'btn'
| 'btn_confirm'
| 'brn_add_session'
| 'btn_confirm_add_session';
clientId?: string;
}
interface GsiButtonConfiguration {
type: 'standard' | 'icon';
theme?: 'outline' | 'filled_blue' | 'filled_black';
size?: 'large' | 'medium' | 'small';
text?: 'signin_with' | 'signup_with' | 'continue_with' | 'signup_with';
shape?: 'rectangular' | 'pill' | 'circle' | 'square';
logo_alignment?: 'left' | 'center';
width?: string;
local?: string;
}
interface PromptMomentNotification {
isDisplayMoment: () => boolean;
isDisplayed: () => boolean;
isNotDisplayed: () => boolean;
getNotDisplayedReason: () =>
| 'browser_not_supported'
| 'invalid_client'
| 'missing_client_id'
| 'opt_out_or_no_session'
| 'secure_http_required'
| 'suppressed_by_user'
| 'unregistered_origin'
| 'unknown_reason';
isSkippedMoment: () => boolean;
getSkippedReason: () =>
| 'auto_cancel'
| 'user_cancel'
| 'tap_outside'
| 'issuing_failed';
isDismissedMoment: () => boolean;
getDismissedReason: () =>
| 'credential_returned'
| 'cancel_called'
| 'flow_restarted';
getMomentType: () => 'display' | 'skipped' | 'dismissed';
}
interface RevocationResponse {
successful: boolean;
error: string;
}
interface Credential {
id: string;
password: string;
}
interface Google {
accounts: {
id: {
initialize: (input: IdConfiguration) => void;
prompt: (
momentListener?: (res: PromptMomentNotification) => void,
) => void;
renderButton: (
parent: HTMLElement,
options: GsiButtonConfiguration,
) => void;
disableAutoSelect: () => void;
storeCredential: (credentials: Credential, callback: () => void) => void;
cancel: () => void;
onGoogleLibraryLoad: () => void;
revoke: (
hint: string,
callback: (done: RevocationResponse) => void,
) => void;
};
};
}
interface Window {
google?: Google;
}
https://www.dolthub.com/blog/2022-05-04-google-signin-migration/
https://devjeong.com/react/react-1/
https://www.youtube.com/watch?v=V2ypG7gCcGA