Javascript/React

[React/TypeScript] 구글 소셜 로그인 버튼 커스텀 (solve to custom google login button using idea)

해당 글은 구글 소셜 로그인 설정 방법은 포함하고 있지 않습니다. 설정 후 버튼을 커스텀하는 방법에 대해 작성했습니다.
하지만 코드를 자세히 읽어 보시면 설정 방법도 힌트를 얻어가실 수 있어요.

 

문제상황

구글 로그인 버튼을 위 이미지처럼 이쁘장~하게 커스텀하고 싶은데 잘 안된다.

 

전 이쁜 버튼을 원해요

gsi 개발문서에서 제공하는 'renderButton'메서드로 버튼을 만들면 이렇게 나온다.

renderButton으로 특정위치에 iframe이 추가된다.

 

내가 시도한 방법들

맨 마지막이 성공한건데, 실패한 것들도 읽어보시면 도움이 될 거예요!

1. css로 iframe 내 스타일 수정하기

불가능. 외부 css로 iframe 내 스타일을 적용할 수 없다.

 

2. gsi 개발문서 다시 읽어보기

불가능. 자유로운 디자인 커스텀이 없다.

진짜 없다. 내 개성 지켜줘

개성이 중시된 요즘 사회에서 당연히 커스텀할 수 있는 옵션은 더 줘야 하는 게 아닌가.

tmi로 지원이 중단된 '구버전 google 로그인'을 보면 매써드 내 옵션으로 조금 더 자유롭게 스타일을 바꿀 수 있게 했던 것 같다. 

 

3. 외부 모듈 설치해서 사용하기

가능. 가장 빠른 방법이지만, 개인적으로는 모듈을 늘리고 싶지 않았다.

yarn링크 걸어두었다.

 

react-google-login

import GoogleSocialLogin from 'react-google-login'

 <GoogleSocialLogin
            clientId={`${GOOGLE_REST_API_KEY}`}
            onSuccess={(res) => console.log(res, '성공')}
            onFailure={(res) => console.log(res, '실패')}
            render={(renderProps) => (
              <div className='social_login_box google' onClick={renderProps.onClick}>
                <div className='social_login_image_box'>
                  <img src={googleIcon} alt='google_login' />
                </div>
                <div className='social_login_text_box'>구글로 시작하기</div>
                <div className='social_login_blank_box'> </div>
              </div>
            )}
/>

 

@react-oauth

ReactDOM.render(
  <GoogleLogin
    clientId="658977310896-knrl3gka66fldh83dao2rhgbblmd4un9.apps.googleusercontent.com"
    render={renderProps => (
      <button onClick={renderProps.onClick} disabled={renderProps.disabled}>This is my custom Google button</button>
    )}
    buttonText="Login"
    onSuccess={responseGoogle}
    onFailure={responseGoogle}
    cookiePolicy={'single_host_origin'}
  />,
  document.getElementById('googleButton')
);

 

공통적으로 render에 커스텀 하고 싶은 스타일을 리턴하는 함수를 적거나, style:object 옵션을 쓰면 되는 것 같다.

 

4. .click 사용하기

불가능. 크롬 브라우저에서만 가능하고 사파리, 삼성브라우저에서 이슈가 있다. 아무래도 iframe 내 요소에 접근하는 거라 그런 것 같다.

function onClickGooglelogin() {
  const googleBtn: HTMLElement = document.querySelector(
    '[aria-labelledby="button-label"]',
  ) as HTMLElement;
  googleBtn?.click();
}

커스텀 버튼을 클릭했을 때 작동하는 함수를 만든다.

커스텀 버튼 클릭 시, renderButton으로 iframe의 버튼([aria-labelledby="button-label"])이 클릭된 것과 동일하다. (실질적으로 구글 로그인되는 버튼)

 

5. css꼼수 쓰기 (css hack)

tailwind랑 style css로 적어서 그대로..

부모요소 안에 gsi에서 랜더 되는 버튼과 커스텀 버튼을 포함시킨다.

부모에 relative, 자식에 absolute 해서 랜더버튼을 커스텀 버튼 위에 놓이게 한다.

gsi의 renderButton메서드로 iframe이 생기면 transform: scale(1.5);transform-origin: top;로 사이즈를 키워 커스텀 버튼을 충분히 덮도록 한다.

그리고 커스텀 버튼은 보이지 않게 opacity 0 처리.

부모요소를 벗어난 영역은 필요가 없음으로 overflow hidden 해준다.

이로써 사용자는 커스텀 버튼이 보이지만 클릭하면 render 된 버튼이 클릭되는 것이다.

 

그리하여 나온 코드

/* style.css */
#google-login-button iframe{
  height: 100px !important;
  transform: scale(1.5);
  transform-origin: top;
}
//GoogleLogin.tsx

// https://github.com/anthonyjgrove/react-google-login/issues/502
// https://developers.google.com/identity/gsi/web/reference/js-reference#CredentialResponse
import { useEffect, useRef, useState } from 'react';

import useScript from '@/utils/useScript';
import { GlobalEnv } from '@/api/config';
import { ReactSVG } from 'react-svg';

export default function GoogleLogin({
  onGoogleSignIn = (res: CredentialResponse) => {},
}) {
  const googleSignInButton = useRef<HTMLDivElement>(null);

  // Load the script asynchronously
  const status = useScript(`https://accounts.google.com/gsi/client`, {
    removeOnUnmount: false,
  });

  useEffect(() => {
    if (typeof window.google !== 'undefined') {
      // https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.initialize
      window.google.accounts.id.initialize({
        client_id: GlobalEnv.viteGoogleClientId,
        callback: onGoogleSignIn,
      });

      if (googleSignInButton.current) {
        window.google.accounts.id.renderButton(googleSignInButton.current, {
          type: 'standard',
          theme: 'outline',
          text: 'signin_with',
          size: 'large',
          width: '416px',
          shape: 'square',
        });
      }
    }
  }, [status]);

  return status === 'ready' ? (
    <div className='relative overflow-hidden'>
      <div className='absolute h-full w-full opacity-0'>
        <div ref={googleSignInButton} id='google-login-button'></div>
      </div>
      <button
        type='button'
        className={마음껏 커스텀~}
      >
        <ReactSVG src='/assets/icons/Google.svg' className='inline-block w-full' />
        구글 로그인
      </button>
    </div>
  ) : null;
}

다른 분들 코드를 보니까 useScript를 사용하신 걸 봤다.

script load/ready 상태에 따라 함수를 실행시킬 수 있는 게 유익해 보여서 적용했다.

useScript는 원래 react-hook인데, 지금 프로젝트에서는 react-hook을 안 써서 useScript만 가져와서 unit에 직접 추가했다.

 

//SignIn.tsx

const SignIn = () => {
  // https://stackoverflow.com/questions/49819183/react-what-is-the-best-way-to-handle-login-and-authentication
  const onGoogleSignIn = (res: CredentialResponse) => {
    const { credential } = res;
    if (credential) {
    //로그인 실행 로직
      setIdToken(credential);
      onGoogleLoginButton({ idToken: credential });
    }
  };
    return (
        <GoogleLogin onGoogleSignIn={onGoogleSignIn} />
    )
}

 

typescript를 위한 gsi type 참고

https://www.dolthub.com/blog/2022-05-04-google-signin-migration/

 

 

해결 굿.

4시간 걸린.. 해결 뚝딱!

728x90
반응형