Spring Boot 프로젝트에서, Google 및 KaKao 플랫폼 서버를 이용하여 인증 과정을 거친 이후, 사용자에게 추가 정보를 입력받아서 회원 가입을 진행하였는데, 해당 절차를 간단히 기록함.
1. Google
Google OAuth을 사용한 로그인 구현은 크게 두 가지로 나뉘는 것으로 보인다.
1-1. REST API 방식
- 그림은 첫 번째 링크를 참조했으며, 절차는 그림과 같다.
- 이 경우 구글 클라우드 > API 및 서비스 > 사용자 인증 정보 에서, Redirection URI를 http://localhost:8080/auth/google/callback로 입력하여 client-id, client-secret 값을 받아야 한다.
- https://developers.google.com/youtube/v3/guides/auth/server-side-web-apps?hl=ko 구글 공식 문서를 참고해서 client_id, redirect_uri, response_type, scope 등의 정보를 Google의 OAuth 2.0 엔드포인트인https://accounts.google.com/o/oauth2/v2/auth 로 전달하여 통신하는 방식
- code 값을 받은 뒤 구글 서버로 access token 요청을 보내 받아온 뒤, access token을 제공해서 유저 정보를 받아온다.
1-2. Spring Security를 사용하여 인증된 OAuth2User 객체를 반환받아 처리하는 방식
- OAuth2UserService<OAuth2UserRequest, OAuth2User> or DefaultOAuth2UserService 인터페이스를 상속받아 loadUser 메소드를 Override해서 userRequest 정보를 받아 오는 방식
- https://github.com/Moojun/Security-OAuth
- 위 링크에 예시 코드를 작성해 놓았다.
이 중 공식 문서를 참고한, 1번 REST API 방식으로 코드를 작성하였음.
- 참고한 공식 문서: https://developers.google.com/youtube/v3/guides/auth/server-side-web-apps?hl=ko
- 아래 사진 참고: https://developers.google.com/identity/protocols/oauth2
2. REST API 방식으로 구현한 이유
- 본 프로젝트에서는 소셜 인증 이후, 사용자에게 추가 정보를 입력받아서 회원 가입 절차를 진행함.
- Spring Security를 이용하여 OAuth2User 객체를 받아서 처리하는 경우, 일단 로그인 처리를 해서 db에 데이터를 저장한 다음에 추가 정보를 입력해야 하는 방식으로 보이며 검색한 결과 역시 동일하였음.
- 즉, loadUser 메소드 내에서 User 데이터를 저장하기 전에, 구글 인증 이후 다른 화면으로 이동시켜서 추가적인 정보를 입력받게 하는 부분을 구현하는 것이 불가능하다고 생각된다.
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
log.info(">> CustomOauth2UserService의 loadUser 메소드");
OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();
OAuth2User oAuth2User = delegate.loadUser(userRequest);
// OAuth2 서비스 id (구글, 카카오, 네이버)
String registrationId = userRequest.getClientRegistration().getRegistrationId();
// OAuth2 로그인 진행 시 키가 되는 필드 값(PK)
String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
// OAuth2UserService
OAuthAttributes attributes = OAuthAttributes.of(registrationId,
userNameAttributeName, oAuth2User.getAttributes());
/* 여기서 저장하기 전에 추가 정보를 입력받아서 저장해야 함 */
// 하지만 어떻게?
User user = saveOrUpdate(attributes);
return new DefaultOAuth2User(Collections.singleton(new SimpleGrantedAuthority(user.getRoleKey())),
attributes.getAttributes(),
attributes.getNameAttributeKey());
}
여기서 생각이 두 가지로 나뉘었는데, 위의 내용을 참고하면 loadUser()를 사용하는 메소드는 1번 방식만 가능함.
- 먼저 User table에 소셜(google 등) 인증 결과로 받은 유저의 기본 정보를 insert한 다음, 추가 정보를 입력받은 뒤 기존 user 정보를 update하는 방식
- Google Server로 access token 정보를 요청하여 Google User Data를 가져온 다음, 사용자에게 추가 정보 입력을 받은 뒤 한 번에 db로 insert하는 방식
하지만 1번의 경우는 적절하지 않다고 판단하였다. 그 이유는,
- User table을 설계할 당시, 추가적인 정보 중 유저에게 필수로 입력받을 정보의 column은 not null로 설정할 것이므로, 유저의 정보를 먼저 table에 insert 할 때 not null column에는 dummy data를 삽입해야 한다.
- 만약 사용자가 구글 로그인 이후, 추가 정보를 입력하는 도중 브라우저를 닫은 뒤 다시 열면, 다음 번에 사용자가 다시 구글 로그인을 통해 회원 가입 시 처리하는 과정이 꼬인다.
따라서 2번 방식, 즉 Google Server로 access token 정보를 요청하여 Google User Data를 가져온 다음, 사용자에게 추가 정보 입력을 받은 뒤 한 번에 db로 insert하는 방식을 사용하기로 하였으며, 이를 위해 REST API 방식으로 코드를 작성하였음.
3. KaKao
KaKao OAuth을 사용한 로그인 구현 역시 REST API 방식과 Spring Security를 이용한 방식으로 나뉘며, Google과 마찬가지로 REST API 방식으로 구현함
- REST API | Kakao Developers 참고하여 구현
- 카카오 Auth Server에 Access token을 요청한 다음 카카오 유저 데이터를 받아오는 과정(https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#req-user-info)에서, ObjectMapper 를 사용해서 JSON 데이터를 Java dto 객체로 변환하였는데, 이 때 내부 클래스를 중첩해서 사용해야 하는 상황이 발생해서 코드가 지저분해졌다.
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class KaKaoOAuthUser {
private String id;
private KakaoAccount kakaoAccount;
@Getter
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public static class KakaoAccount {
private Profile profile;
private String email;
@Getter
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Profile {
private String nickname;
}
}
}
4. 소스 코드
https://github.com/Moojun/Spring-REST-API-Social-Login
플랫폼 서버에서 인증 이후 클라이언트로 User 정보를 jwt 토큰으로 암호화하여 전달함.
해당 데이터를 전달한 이유는 아래 5번에서 설명
4-1. 문제점(6/27 추가)
Front: Next.js , Server: Spring Boot
REST API 방식으로 개발하는 경우, 위의 코드를 사용하면 응답 결과(DTO)를 클라이언트에서 처리할 수 없다.
<a href="http://localhost:8080/auth/google">구글로 로그인 요청</a> 에서, 사용자가 브라우저에서 해당 링크를 누른 뒤 이메일 및 비밀번호를 입력하면, 서버에서 리다이렉트 처리까지 완료하기 때문에 클라이언트에서 응답 결과를 처리할 수 없음.
따라서 아래 그림에서, 4~7번 과정은 Next.js 웹 서버에서 담당하고, 8~9번 과정은 api 서버에서 담당하는 것이 바람직하다.
api 서버는 엑세스 토큰을 플랫폼 서버로 전달해서, 해당 유저 정보를 가져온 다음 이 정보를 사용한다.
결론은, 4번의 코드는 웹 서버와 api 서버 사이에 통신을 위해서는 적절하지 않음.
추가] 백엔드 개발을 오래한 지인에게 들은 내용을 정리하면, 보통 클라이언트-서버 간에 소셜 회원가입/로그인을 구현하는 경우,
클라이언트에서 Google SDK, Kakao SDK 등 SDK를 사용하고, api 서버에서는 엑세스 토큰을 클라이언트에서 받아서 플랫폼 서버에 요청을 보내서 유저 정보를 반환받은 이후, 로직을 구현한다고 한다.
따라서 프론트에서 NextAuth.js 라이브러리를 사용해서 소셜 로그인을 구현하기로 함.
5. 서비스 회원가입 이후 사용자에게 추가 정보 입력받기
- 본 프로젝트에서는 소셜 로그인 이후, 추가적인 정보(학교, 졸업 예정 연도, 학점 등의 필수 정보와 자격증, 경력 등의 선택 정보)를 사용자에게 입력받아야 한다.
- 위 사진은 알바몬 회원가입 예시로, 회원가입 시 구글 플랫폼 서버에서 사용자 인증을 거친 이후 바로 추가적인 정보 입력을 사용자에게 요구한다.
- 사용자에게 추가 정보를 입력받도록 구현하는 방법은 크게 두 가지이다.
- 플랫폼 서버(Google, Kakao)에서 user data를 가져온 뒤, 서버에서 in-memory db(redis 등)에 저장하였다가, 클라이언트로부터 추가 정보 데이터를 받은 다음 in-memory db를 조회하여 데이터를 꺼내온 뒤, 한 번에 db에 insert한다.
- 플랫폼 서버(Google, Kakao)에서 user data를 가져온 뒤 JWT 토큰을 사용하여 암호화한 다음, 클라이언트에 전달한다. 클라이언트에서는 복호화 과정을 통해 데이터를 검증한 다음, 추가 정보와 함께 서버로 전달한다. 서버에서는 해당 정보들을 받아 한 번에 db에 insert한다.
- 이 중, 2번 방식이 더 효과적이라고 생각되어서 2번 방식으로 개발을 진행함.
참고 링크
1-1. REST API 방식
소셜 로그인 요청 Redirect 처리 - 2 (Spring Boot 레퍼런스를 보면서 구현해보는 구글 소셜 로그인 REST API - 4)
Spring Boot에서 구글 소셜 로그인 REST 방식으로 구현하기
[OAuth 2.0] 스프링부트로 Google 로그인 구현해보기 -2
Google은 Refresh Token을 쉽게 내주지 않는다.
구글 공식 문서: https://developers.google.com/youtube/v3/guides/auth/server-side-web-apps?hl=ko
1-2. Spring Security를 사용하여 인증된 OAuth2User 객체를 반환받아 처리하는 방식
이동욱님 저서 '스프링 부트와 AWS로 혼자 구현하는 웹 서비스'
Spring Boot 게시판 OAuth 2.0 구글 로그인 구현
Springboot 구글 OAuth2 로그인 + JWT
구글, 카카오, 네이버, 페이스북 로그인 기본 절차, [Spring Boot] OAuth2 소셜 로그인 가이드 (구글, 페이스북, 네이버, 카카오)
Social Login google, kakao, naver 구현: https://developerbee.tistory.com/245
[SpringBoot] 간단한 게시판 만들기 #6 - SpringSecurity 개념 및 구현: https://ksh-coding.tistory.com/66
2-1. REST API 방식(Kakao)
[Spring Boot] 카카오 OAuth2.0 구현하기
'멘질멘질] 2023 졸업 프로젝트' 카테고리의 다른 글
Ubuntu] Docker 용량 줄이기 (0) | 2023.06.25 |
---|---|
Ubuntu] Next.js Dockerfile 경량화(Optimize) (0) | 2023.06.22 |
JWT] Secret key 랜덤 생성 (0) | 2023.06.16 |
Spring Boot] 406 Not Acceptable 오류 (0) | 2023.06.13 |
Junit5] 테스트에서 *.properties 정보 읽어오지 못하는 경우 해결 방법 (0) | 2023.06.13 |