오늘은 프로젝트에서 구현을 맡았던 OAuth2.0을 사용하여 카카오톡 로그인을 구현하는 법을 알아보겠습니다!
먼저 OAuth 2.0이 뭔지 알아봐야겠죠?
여러 사이트의 회원가입을 할 때 외부 소셜 계정을 기반으로 간편히 회원가입 및 로그인할 수 있는 어플리케이션을 다들 많이 보셨을 겁니다!
예를 들어 어떤 사이트의 회원가입을 할 때 카카오톡 로그인을 사용하면 그 애플리케이션에서 내 카카오톡 친구를 확인할 수도 있고 카카오톡의 기능을 사용할 수 있는 것을 다들 한 번쯤은 경험하신 적이 있죠?
구글 로그인, 페이스북 로그인, 트위터 로그인 등등
이때 사용되는 프로토콜이 OAuth입니다.
쉽게 말해 사용자들이 따로 비밀번호를 설정하여 회원가입을 하지 않고 다른 사이트의 자신 정보를 들고 와 사용할 수 있도록 해주는 것입니다.
이해가 되시나요?
OAuth 동작에는 크게 세 가지가 있습니다
- Resource Server
Client가 사용하고자 하는 데이터를 보유하고 있는 서버입니다.
위에서 설명드렸던 구글, 페이스북, 카카오톡 등이 이에 해당됩니다.
- Resource Owner
로그인을 하는 사용자. 유저라고 생각하시면 편합니다.
- Client
Resource Server에 접속해서 정보를 가져오고자 하는 애플리케이션입니다.
위 그림은 PAYCO OAuth 2.0 프로세스입니다.
사용자 - Resource Owner
서비스 - Client
인증 서비스, API 서비스 - Resource Server
대충 어떤 건지 감이 오시나요?
OAuth 서비스를 요약해 보면
Client에서 Resource Owner를 대신하여 Resource Server에 요청을 보내서 로그인을 수행하는 것입니다!
이제 OAuth 서비스에 대해 완벽히 이해하셨을 거라 생각하고 구현 진행해 보겠습니다!
위의 PAYCO OAuth 2.0 프로세스의 번호에 의거하여 설명드리겠습니다
잘 따라와 주세요!
일단 카카오톡 아이디를 사용하여 로그인할 저희만의 애플리케이션이 있어야겠죠?
https://developers.kakao.com/
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com
저희는 카카오톡을 사용할 거라 카카오에서 제공하는 Kakao Developers 사이트에 들어가 본인의 애플리케이션을 만들어줍니다!
저희의 어플리케이션 이름은 detour이기 때문에 detour로 만들어줍니다!
자 이제 1번(사용자가 로그인 버튼을 클릭)부터 봐볼까요?
사용자가 카카오톡 로그인하기 버튼을 누릅니다!
그럼 프론트에서 카카오가 제공하는 url를 실행시켜줘야 합니다. 2번(서비스에서 로그인 요청)을 진행하기 위해선
client_id와 redirect_uri가 필요합니다
KAKAO_AUTH_URL = `http://kauth.kakao.com/oauth/authorize?client_id=${process.env.REACT_APP_KAKAO_CLIENT_ID}&redirect_uri=${process.env.REACT_APP_KAKAO_REDIRECT_URI}&response_type=code&prompt=login`;
그럼 client_id는 어디 있고 redirect_uri는 어디 있는데?
한 번 찾아보죠!
어플리케이션을 만들고 왼쪽 상단에 있는
앱 키를 누르면
이런 화면이 뜨실 겁니다!
저희는 REST API를 활용하여 로그인을 구현할 것이 때문에 client_id는 REST API키 옆에 적혀있는 이상한 영어!
그럼 이제 redirect_uri를 설정하러 가야겠죠?
앱 키 바로 아래에 있는 플랫폼을 눌러봅시다!
눌러서 사이트 도메인 설정을 해주시고 (로컬에서 하시면 localhost로 등록!)
사진 바로 밑에 redirect_uri 등록해야 한다고 나오죠?
저 버튼을 클릭해서
카카오 로그인을 활성화시켜 주시고
redirect_uri를 설정해 주어 프론트에서 url을 실행시키면!
3번(로그인 페이지 제공, ID, Password 요청)이 진행되고
아래 그림처럼 4번(사용자가 ID, Password 입력)을 진행하여 통과를 하게 되면
설정한 redirect_uri?code=토큰
카카오서버에서 저희가 설정한 redirect_uri로 5번(Authorization Code 발급)해 줍니다.
그러면 프론트에서 발급한 코드를 받아서 제가(백엔드 개발자)가 구현한 api를 실행시켜 주며 code를 전달해 줍니다
저는 RequestParam으로 전달받게 구현하였습니다
@GetMapping("/login/oauth2/code/kakao")
public ResponseEntity<CommonResponseDto> kakaoLogin(@RequestParam String code, HttpServletResponse response) throws JsonProcessingException, UnsupportedEncodingException {
List<String> kakaoToken = kakaoService.kakaoLogin(code, response);
return ResponseEntity.ok(new CommonResponseDto(200, "카카오 로그인 성공", kakaoToken));
}
그럼 이제 7번(카카오 서버에 사용자의 정보가 담긴 Token 요청)을 진행하게 됩니다.
아래는 토큰을 요청하여 받는 7번과, 8번의 과정이 있습니다!
private String getToken(String code) throws JsonProcessingException {
// 카카오 OAuth 2.0 서버의 토큰 발급 API 엔드포인트 URI를 생성합니다.
// 카카오에서 제공합니다!
URI uri = UriComponentsBuilder
.fromUriString("https://kauth.kakao.com")
.path("/oauth/token")
.encode()
.build()
.toUri();
// HTTP 요청의 헤더를 설정합니다. 이 경우 콘텐츠 타입을 설정하여 서버에 전송할 데이터 형식을 알려줍니다.
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP 요청의 바디를 설정합니다. MultiValueMap을 사용하여 각 요청 파라미터를 추가합니다.
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code"); // 인증 코드로 토큰을 발급받기 위한 grant_type입니다.
body.add("client_id", kakaoClientId); // 카카오에서 발급받은 클라이언트 ID입니다.
body.add("redirect_uri", kakaoRedirectUri); // 카카오 인증 후 리다이렉트될 URI입니다.
body.add("code", code); // 프론트로부터 전달받은 인증 코드입니다.
// POST 요청을 생성합니다. 앞에서 설정한 URI, 헤더, 바디를 사용하여 요청 엔터티를 만듭니다.
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri)
.headers(headers)
.body(body);
// REST 템플릿을 사용하여 카카오 서버로 요청을 보내고, 응답을 받습니다.
ResponseEntity<String> response = restTemplate.exchange(
requestEntity,
String.class
);
// 응답 받은 JSON 문자열을 파싱하여 JsonNode 객체로 변환합니다.
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
// JsonNode에서 액세스 토큰 값을 추출하여 반환합니다.
return jsonNode.get("access_token").asText();
}
이제 토큰을 받았으면 토큰으로 카카오에 회원가입 했던 사용자의 정보를 가공해서 사용해야겠죠??
private KaKaoUserInfoDto getKakaoUserInfo(String accessToken) throws JsonProcessingException {
// 토큰을 가지고 사용자의 정보를 들고오는 엔드포인트 URI를 생성합니다.
// 카카오에서 제공합니다!
URI uri = UriComponentsBuilder
.fromUriString("https://kapi.kakao.com")
.path("/v2/user/me")
.encode()
.build()
.toUri();
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri)
.headers(headers)
.body(new LinkedMultiValueMap<>());
ResponseEntity<String> response = restTemplate.exchange(
requestEntity,
String.class
);
// 정보를 받아와 카카오유저dto를 리턴해줍니다!
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
Long id = jsonNode.get("id").asLong();
String nickname = jsonNode.get("properties")
.get("nickname").asText();
String email = jsonNode.get("kakao_account")
.get("email").asText();
return new KaKaoUserInfoDto(id, nickname, email);
}
이 과정이 지나면 카카오 유저의 정보를 들고 와 여러분들의 어플리케이션에 사용할 수 있게 됩니다!
그리고 여러분의 어플리케이션에 따라 유저 정보로 AccessToken과 RefreshToken을 만들어주고
여러분들 어플리케이션만의 회원가입 로직을 실행해 주시면 됩니다!
저희는 이메일을 비교하여 기존 회원과 이메일이 같으면 아래와 같이 아이디를 통합해 주는
로직을 추가로 구현하여 적용하였습니다.
private User registerKakaoUserIfNeeded(KaKaoUserInfoDto kakaoUserInfo) {
Long kakaoId = kakaoUserInfo.getId();
User kakaoUser = userRepository.findByKakaoId(kakaoId).orElse(null);
if (kakaoUser == null) {
String kakaoEmail = kakaoUserInfo.getEmail();
User sameEmailUser = userRepository.findByEmail(kakaoEmail).orElse(null);
if (sameEmailUser != null) {
kakaoUser = sameEmailUser;
kakaoUser = kakaoUser.kakaoIdUpdate(kakaoId);
} else {
String password = UUID.randomUUID().toString();
String encodedPassword = passwordEncoder.encode(password);
String loginId = UUID.randomUUID().toString();
String email = kakaoUserInfo.getEmail();
kakaoUser = new User(email, encodedPassword, kakaoUserInfo.getNickname(), UserStatusEnum.ACTIVE, UserRoleEnum.USER, kakaoId, loginId);
}
}
return kakaoUser;
}
백엔드에 code를 넘겨준 api가 성공하면 프론트 쪽에서 다시 리다이렉션을 해줘야 합니다!!!
카카오와의 인증은 모두 끝났고 이제 저희 어플리케이션에서 10번, 11번, 12번, 13번을 실행시켜주면
저희가 구현한 사이트에 로그인이 되면서 기능 등을 사용할 수 있게 됩니다!
긴 글 읽느라 수고 많으셨습니다
'Java+Spring 박살내기' 카테고리의 다른 글
Redis와 Gmail SMTP 활용하여 이메일 인증 구현하기 (2) | 2024.07.26 |
---|---|
통합 테스트와 단위 테스트 (0) | 2024.07.08 |
N+1 원인과 해결 방법 (0) | 2024.07.04 |
JPA에서 Lazy Loading 과 Eager Loading (0) | 2024.07.04 |
Swagger, Springdocs (0) | 2024.05.20 |