구글 로그인 시퀀스 다이어그램
구글 계정으로 로그인하는 OAuth 2.0을 다음과 같은 과정을 거쳐서 구성하려고 한다. UI/UX적으로 로그인/회원가입을 한번에 처리하고 싶어서, 구글 로그인 요청이 들어오면 1) 기존에 계정이 있을 경우 그냥 로그인만하고, 2) 계정이 없을 경우 회원가입을 진행해주는 로직으로 만들었다.
- 클라이언트는 Google OAuth Server에
Authorization Code
를 요청한다. - Google OAuth Server는 미리 설정되어 있는
Redirect URI
로 Authorization Code를 전송한다. 여기서는 클라이언트가 구글 로그인을 처리하고 싶은 경로가 Redirect URI가 된다. - 클라이언트가 이 Authorization Code를 Spring Boot 백엔드 서버에 전송한다.
- 서버는 Authorization Code와
Client Secret
등을 Google OAuth Server에 전송해Access Token
을 발급받는다. 이 엑세스 토큰은 Google 리소스 서버에서 원하는 정보에 접근하기 위한 토큰이다. - Access Token으로 사용자를 구분할 수 있는
개인 정보(이메일, 프로필 사진 URL 등)
을 받는다. - 사용자의 개인 정보를 기반으로, Spring Boot 백엔드 서버는 DB에서 가입 여부를 체크한다.
- 기존에 가입한 회원이면 JWT 토큰만 발급하고(
로그인
), 존재하지 않는 회원이면 DB에 저장후 JWT 토큰을 발급한다(회원가입 및 로그인
). - Spring Boot 백엔드 서버는 클라이언트에게 JWT 토큰을 발급한다.
참고로 Google OAuth Server에 Authorization Code를 요청하는 부분을 클라이언트가 맡는 이유는 구글 로그인 화면을 프론트엔드에서 처리해야 하기 때문이다.
GCP에서 프로젝트 생성하기
구글 클라우드에서는 모든 리소스가 프로젝트 안에 들어가 있어야 한다. 개발 중인 서비스 이름으로 프로젝트를 하나 만든 후, 해당 프로젝트를 선택한다.
GCP에서 OAuth 동의하기
프로젝트에 들어갔으면, "OAuth 동의 화면" 메뉴로 가서 먼저 동의를 한다. Google 계정이 있는 모든 외부 사용자가 사용할 수 있도록 한다.
엡에 대한 정보를 입력한다. 이 정보는 구글 로그인 시 사용자에게 제공되는 정보이다. 앱 이름과 사용자 지원 이메일, 개발자 연락처 정보는 필수적으로 입력한다.
오른쪽에서 이렇게 등록한 정보가 어떻게 보일지 알 수 있다. 앱 로고가 있다면 로고 파일도 등록한다.
이 OAuth 인증이 어떤 범위의 데이터를 가져올 수 있는지를 설정해준다. OAuth 인증이 이루어지면 리소스 서버에서 다양한 데이터를 가져올 수 있다. 내 서비스에서는 추가적인 데이터 말고, 기본적인 이메일 주소 및 개인 정보만 가져와서 로그인/회원가입만 할 것이므로, 범위를 .../auth/userinfo.email
과 .../auth/userinfo.profile
두 개로 설정해준다.
업데이트 버튼을 눌러 설정한 범위를 업데이트해준다.
그 후에 테스트시 사용할 구글 계정을 등록해준다. 프로덕션으로 출시하기 전에는 여기서 등록한 테스트 사용자만 이 OAuth 인증을 쓸 수 있다. 개발할 때 사용할 테스트 계정을 등록해준다(나중에 추가할 수도 있으므로, 지금 반드시 설정하지 않아도 된다).
앱 등록이 완료되면 대시보드로 돌아간다.
GCP에서 OAuth 클라이언트 생성하기
이제 사용자 인증 정보로 이동해서 "사용자 인증 정보 만들기"를 선택한 후, "OAuth 클라이언트 ID"를 만든다.
이 클라이언트는 앱을 식별하는데 사용된다. 웹 서비스의 경우 "웹 애플리케이션"을 선택한다.
그 후 리디렉션 URI를 만들어준다. 여기서 중요한게, 이 리디렉션은 프론트엔드와 백엔드 API 경로 둘 다 만들어야 한다. 그 이유는 원래는 프론트엔드로 리디렉트해야 하지만, 백엔드 개발자가 테스트하기 위해서는 리디렉션 URI에 백엔드 서버 API 경로도 있어야 하기 때문이다.
테스트용 백엔드 서버 경로인 http://localhost:8080/dev/login/oauth/google
를 입력해주고, 사용자 인증을 만든다.
생성이 완료되면 다음과 같이 클라이언트 ID와 클라이언트 보안 비밀번호가 만들어진다. 이 중 클라이언트 보안 비밀번호는 절대 노출되서는 안 되는 값이다.
브라우저에서 확인
생성이 완료되었으면 브라우저에서 로그인이 가능한지 확인한다. 이때 꼭 다음과 같은 URL로 접근해야 한다.
https://accounts.google.com/o/oauth2/auth?client_id=[CLIENT_ID]&redirect_uri=[REDIRECT_URI]&response_type=code&scope=https://www.googleapis.com/auth/userinfo.email+profile
client_id
에는 위에서 아까 발급 받은 "클라이언트 ID"를 넣어주고, redirect_uri
에는 아까 위에서 설정한 리다이렉트 URL을 넣어준다. 나는 백엔드 서버로 테스트할 것이므로, 파라미터 값으로 아까 설정한 http://localhost:8080/dev/login/oauth/google
을 넣어줬다. 다른 API 경로라면 그 경로로 넣어주면 된다. 그리고 여기서 꼭 중요한게, scope
를 꼭 https://www.googleapis.com/auth/userinfo.email+profile
로 설정해야 한다. userinfo.email
나 userinfo.profile
로만 설정하면 원하는 정보를 다 가져올 수 없다.
이 URL로 접근하면 다음과 같은 화면이 뜬다.
아까 만든 테스트 계정으로 접근한다. 그럼 아직 백엔드 서버에서 API 관련 로직이 아무것도 없으므로 에러만 발생한다. 자세한 코드는 다음 글에서 다루려고 한다.
scope관련 트러블 슈팅
scope
를 꼭 https://www.googleapis.com/auth/userinfo.email+profile
로 설정해야 하는 이유는 다음과 같다. scope
를 userinfo.profile
로만 설정하면 아래와 같은 프로필 관련 데이터만 리소스 서버에서 얻어올 수 있다.
{
"id" : "00000000000000000000",
"name" : "홍길동",
"given_name" : "길동",
"family_name" : "홍",
"picture" : "https://url 경로",
"locale" : "ko"
}
scope
를 userinfo.profile
로만 설정하면 아래와 같이 이메일 관련 데이터만 가져올 수 있다.
{
"id" : "00000000000000000000",
"email" : "sample@gmail.com",
"verified_email" : true,
"picture" : "https://url 경로"
}
userinfo.email+profile
로 설정한 경우 아래와 같이 모든 정보가 출력되서 보인다. 소셜 로그인 시 이메일, 사용자 이름, 프로필 사진 URL 이 3가지를 전부 얻어오고 싶다면 다음과 같이 반드시 userinfo.email+profile
로 설정해야 한다.
{
"id" : "00000000000000000000",
"email" : "sample@gmail.com",
"verified_email" : true,
"name" : "홍길동",
"given_name" : "길동",
"family_name" : "홍",
"picture" : "https://url 경로",
"locale" : "ko"
}