새로 리팩터링 해서 구현한 CRUD를 블로그에 작성해야 하고,,🥹 다른 스택 사용한 것도 작성해야 하는데 언제 다 쓸지 벌써 막막하다.. 일단 최근에 OAuth2를 공부하면서 의존성을 추가해 구현하는 방법을 사용하다가, 프로젝트를 진행하는 중 REST API를 많이 사용하다 보니 소셜 로그인 또한 REST 형식으로 구현하기 위해 방법을 뒤적뒤적하면서 공부하고 진행하던 프로젝트에 도입해봤다. 크게 어려운 부분은 없기 때문에 누구나 소셜 로그인을 REST 형식으로 구현하기 어렵지 않을 것이다.
첫 번째로 클라이언트가 구글 로그인 창으로 이동되게끔 Controller에서 url을 redirect 해줄 것이다. 그전에, 구글 OAuth API 사용을 위해 google cloud에서 환경 설정해주어야 한다.
위 사이트를 참고하면 google cloud에서 클라이언트를 등록하고 OAuth 정보를 구성하는데 크게 도움 될 것이다. 위 블로그의 경우 REST 형식으로 구현하지 않고 OAuth2를 직접 추가하는 방법으로 구현하였다. 우리는 REST 형식으로 구현할 것 이기 때문에 클라이언트를 설정하는 부분을 참고하도록 하자.
설정이 완료되었다면 Controller에서 위에서 OAuth 동의 후 사용자 인증 정보를 가지고 자신이 설정한 redirect URL을 가지고 구글 로그인 창으로 이동되게끔 만들어보자.
위와 같이 맵핑된 주소로 넘어가면 바로 구글 로그인 창으로 넘어가게끔 구현해주었다. GoogleOAuth 클래스에서 getOauthRedirectURL에서 가져온 URL을 통해 이동한다. GoogleOAuth 클래스의 getOauthRedirectURL에서 구글 로그인 창을 어떻게 반환하는지 살펴보면
구글 로그인 url과 clientId, redirecturl를 조합해서 url을 바로 넘겨줄 것이다. 참고로 application.properties에 민감한 정보들을 설정해두고 각각의 변수에 대입해주었다. 아래를 참고해 모자이크 부분에 자신이 발급받은 키들을 넣어주자
여기까지 잘 진행되었다면 서버를 실행해 맵핑된 주소로 넘어가면
맵핑 주소로 넘어갔을 경우 위 구글 로그인 창으로 넘어가게 될 것이다. 이 상태에서 로그인을 한다면 우리가 URL에 설정해둔 redirectURL로 화면이 넘어갈 것이고 주소에 code가 발급되었을 것이다. 아직 redirect에 아무것도 구현해놓지 않아서 상태 오류가 뜰 것 이기 때문에 이제 redirect 됐을 경우의 Service를 구현해보도록 하자.
위와 같이 리다이렉트 될 경우 맵핑 주소로 구글에서 전달해주는 code를 요청받은 후 Service로 code값을 넘겨주면 된다. Controller에서는 크게 구현해줄 건 없고 구글에서 넘겨준 code값을 이용해 Service에 넘겨주는 아주 간단한 역할은 한다. 리다이렉트 될 경우 URL에 설정해둔 대로 바로 code값이 넘어오니 @RequestParam을 통해 값을 바로 받아오기만 하면 된다.
그렇다면 넘겨준 code값을 이용해서 구글 로그인 Service를 구현해보도록 하자.
GoogleOAuth 클래스에서 일련의 처리과정을 거쳐 구글 로그인 및 사용자 정보를 가져올 것인데, 위 사진만 이해를 한다면 구글 로그인을 진행하는데 크게 문제없을 것이다. 이 코드를 이해하기 전에 googleOAuth 클래스의 requestAccessToken, getAccessToken, requestUserInfo, getUserInfo 메서드가 어떻게 구성되어있는지 먼저 살펴본 이후에 다시 설명에 들어가도록 하겠다.
GoogleOAuth 클래스의 requestAccessToken 메서드이다. 이 메서드를 구현하기 위해서는 RestTemplate을 사용할 건데
위 사진과 같이 RestTemplate을 설정해주도록 하자. 설정이 되었다면 다시 requestAccessToken 메서드를 한줄한줄씩 이해를 해보도록 하자. restTemplate 객체를 미리 생성해두고 params를 Map으로 생성 후 요청받은 code값과 자신의 clientId, Secret code, redirecturl과 grant_type까지 put을 완료한 다음 생성해두었던 restTemplate에 GOOGLE_TOKEN_REQUEST_URL ( 이 게시글의 위를 살펴보면 어떻게 변수가 선언되어있는지 확인할 수 있다. ), map으로 저장한 params, String 클래스를 등록 gk고 ResponseEntity 객체로 저장해준다.
만약 상태 코드가 문제없을 경우 responseEntity를 return 해준다. 이러한 과정으로 우리는 accessTokenReponse라는 객체에 return 되는 값을 담을 것이고 이 accessToken을 자바 객체 형식으로 변환할 것인데 googleOAuth.getAccessToken 메서드가 이 과정을 처리해준다. 아래 사진으로 메서드를 살펴보도록 하자.
우리는 변환한 accessToken에 대한 정보를 자바 객체 형식으로 저장해야기 때문에 정보를 담을 Dto를 하나 만들어서 객체를 생성하여 그 안에 담아준 이후 return 해줄 것이다. 담을 Dto의 선언 값들은 아래와 같다.
여기까지 문제없이 진행되었다면 return 된 값을 가지고 requestUserInfo 메서드로 들어갈 것이다. 이번에는 requestUserInfo메서드가 어떠한 처리 과정을 거치는지 확인해보자.
우리는 access_token을 발급받고 객체로 변환해 Dto를 통해 정보를 담아내는 것까지 성공했기 때문에 이 token을 가지고 사용자의 정보를 요청할 것이다. 그러기 위해선 헤더에 GoogleOauthTokenDto에서 get으로 access_token을 가져와서 삽입해줄 것이다.
HttpEntity를 하나 생성하여 만들어둔 헤더를 삽입하고 처음에 선언해둔 유저 정보를 요청하는 URL로 GET 요청과 생성한 HttpEntity를 넣고 return 해준다면 성공적으로 사용자의 정보를 가져올 것이다.
하지만 위와 마찬가지로 이는 자바 객체로 변환시켜주어야 하기 때문에 Dto를 통해 사용자의 정보를 담는 과정을 위에서 거친 것처럼 반복하게 된다. 그래서 googleOAuth.getUserInfo 메서드는 이 과정에서 사용된다.
objectMapper를 하나 생성하여 받아온 response의 body 값을 가져오고 GoogleUserInfoDto를 하나 생성하여 그 안에 담아주고 그대로 return 하면 사용자의 정보까지 가져올 수 있다! 아래는 GoogleUserInfoDto의 코드이다.
이 과정이 마무리되었다면 구글 계정을 통한 로그인에 성공하고 사용자의 정보를 가져오는 것도 성공했다. 이제는 개발자가 알아서 이 정보들을 어떻게 처리하고 언제가 로그인이고 회원가입인지 등을 직접 처리한다. 필자의 경우 아래와 같이 코딩하였다.
getGoogleUserInfoDto 메서드를 통해 아까의 일련과정을 처리를 하였다! 처리가 완료되면 유저의 정보를 return 해주기 때문에 GoogleUserInfoDto 객체를 하나 생성하여 담아주고 만약 그 유저의 이메일이 로컬 서버에 존재하지 않는다면 회원가입 이후 getSingleResult라는 메서드를 통해 JWT 토큰 발급 및 로그인을 진행해주었고 존재할 경우에는 바로 JWT 토큰 발급과 로그인을 진행해주었다.
하나하나씩 차근차근 따라온다면 소셜 로그인을 REST API 방식으로 구현하는 것은 어렵지 않다. 한번 구현해놓으면 또 용이하게 쓰일 곳도 많고 로직을 개선함에 있어서 더 효율적으로 짜낼 수 있다면 소셜 로그인 코드를 리팩터링 하는 것도 좋은 방법이다. 중간중간 개발할 때 값이 제대로 들어오는지 log를 찍었는데 막히는 부분이 있다면 log를 계속 찍어보면서 틀린 곳을 찾아보는 것도 좋다.
오랜만에 블로그에 제대로 된 개발일지를 좀 쓴 거 같은데 다음에는 kakao 로그인에 대해서 작성을 해보려 한다. (개발은 했지만 작성하기가 생각보다 귀찮...ㄷ..)
무튼 이렇게 구글 로그인을 구현하는 방법을 알아보았다. 그러면 다들 열코!
'모험가의 Spring > Spring' 카테고리의 다른 글
JPA findAll() 정렬해서 가져오는법 (0) | 2022.10.01 |
---|---|
Spring _ 카카오 로그인 REST API 구현 OAuth2 (2) | 2022.09.30 |
JpaRepository에서 Optional 객체에 담긴 정보 가져오기 (0) | 2022.09.30 |
인텔리제이로 Spring을 개발할때 TIP ! (0) | 2022.09.05 |
예외 처리 _ Custom Exception 만들기 (0) | 2022.08.21 |