카카오 데이터센터에 화재가 나서 전체 카카오 서비스가 이용 불가한 상황이 발생했다.
뜨개장터는 카카오 로그인 만을 통해서 회원가입하여 서비스를 이용 할 수 있다.
따라서 카카오가 복원되기 전까지 뜨개장터는 아무도 이용할 수 없게 되었다.
(데이터센터 화재라니 .. 한 3일정도 소요될 것 이라고 예상했었다)
따라서 뜨개장터를 굴러가게 만들기 위해 최대한 빨리 네이버 로그인 API 를 적용시키기로 했다.
1. 애플리케이션 등록
- 이름, 전화번호, 이메일 3가지를 가져오기로 선택했음
애플리케이션을 등록하면 즉시 Client ID 와 Clisent Secret 키가 발급된다.
발급된 키로는 테스트 로그인만 가능하다. (개발자 ID로 테스트 가능)
실사용을 위해서는 네이버로부터 검수를 받아야 한다.
2. 코드작성
- 사용자로 로그인 예시
▷ js
//네이버 로그인 버튼 클릭시
$('#naverLoginBtn').click(function(){
var role = "";
if ($('#checkbox_user').is(":checked")) {
role = "user";
} else {
role = "seller";
}
var kakaoUrl = "https://nid.naver.com/oauth2.0/authorize?" +
"response_type=code" +
"&client_id={client_id}" +
"&redirect_uri=http://knitmarket.shop/naverLogin/requestToken_" + role +
"&state=STATE_STRING";
location.href = kakaoUrl;
});
▷ service
@Service
public class NaverLoginService {
//application.properties 로부터 Client ID 와 Clisent Secret 키 주입 받음
@Value("${naverLogin.sk}")
private String CLIENT_SECRET;
@Value("${naverLogin.ck}")
private String CLIENT_ID;
public String getAccessToken(String code, String state) throws JsonProcessingException {
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP Body 생성
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", CLIENT_ID);
body.add("client_secret", CLIENT_SECRET);
body.add("code", code);
body.add("state", state);
// HTTP 요청 보내기
HttpEntity<MultiValueMap<String, String>> naverTokenRequest =
new HttpEntity<>(body, headers);
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://nid.naver.com/oauth2.0/token",
HttpMethod.POST,
naverTokenRequest,
String.class
);
// HTTP 응답 (JSON) -> 액세스 토큰 파싱
String responseBody = response.getBody();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBody);
return jsonNode.get("access_token").asText();
}
public JsonNode getNaverUserInfo(String accessToken)
throws JsonProcessingException {
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP 요청 보내기
HttpEntity<MultiValueMap<String, String>> naverUserInfoRequest = new HttpEntity<>(headers);
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://openapi.naver.com/v1/nid/me",
HttpMethod.POST,
naverUserInfoRequest,
String.class
);
// HTTP 응답 받아오기
String responseBody = response.getBody();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBody);
JsonNode jsonResponse = jsonNode.get("response");
return jsonResponse;
}
}
▷ controller
@RequestMapping("/naverLogin/requestToken_user")
public String authNaver_user(@RequestParam String code, @RequestParam String state,
Model model, HttpServletRequest request,
HttpServletResponse response)
throws JsonProcessingException {
String role = "user";
String accessToken = naver.getAccessToken(code, state);
JsonNode json = naver.getNaverUserInfo(accessToken);
String id = json.get("id").asText();
String name = json.get("name").asText();
String email = json.get("email").asText();
String hp = json.get("mobile").asText().replace("-", "");
log.info("id={}, name={}, email={}, mobile={}", id, name, email, hp);
UserRequestDto userRequestDto = null;
Long userId;
String url="/";
String msg = "";
//db 중복 확인
if (!userService.existsByEmail(email)) { //신규가입
userRequestDto = new UserRequestDto(email, name, hp, id);
User user = userService.save (userRequestDto);
userId = user.getId();
log.info("신규회원가입 회원번호={}", userId);
//장바구니 생성
CartDto createCartDto = CartDto.builder()
.user(user)
.build();
Cart cart = cartRepository.save(createCartDto.toEntity());
msg =name+"님, 회원가입을 축하드립니다 !";
}else{ //기존회원로그인
userId = userService.findByEmail(email).getId();
log.info("기존회원 회원번호 ={}",userId);
msg =name+"님, [사용자] 로그인되었습니다";
}
//세션저장
HttpSession session=request.getSession();
session.setAttribute("id", userId);
session.setAttribute("email", email);
session.setAttribute("access_Token",id);
session.setAttribute("name", name);
session.setAttribute("role",role);
model.addAttribute("url",url);
model.addAttribute("msg",msg);
return "common/message";
}
3. 검수요청
검수요청을 할 때
네이버에서 받아온 정보들을 사용자가 확인할 수 있어야 하는 조건이 있어
내 정보에서 전화번호를 확인할 수 있도록 수정했다
검수요청을 하면 아래와 같이 상태가 변경된다.
4. 테스트 로그인
검수를 기다리는 동안 로컬에서 로그인 테스트를 해보기로 한다
오류가 발생한다
리다이렉트 url 을 https 로 설정해서 발생했던 오류였다.
http://localhost:8080/ 로 변경하니 해결됨
정상적으로 회원가입 창이 뜬다.
이제 소셜 로그인 방법이 2개 되었으니
어떤 소셜 계정을 통해 회원가입했는지를 구분하기 위해서 소셜 로그인키를 회원정보에 함께 저장하기로 한다.
social_private_key 이름으로 컬럼을 생성했다.
다만, 이 상태로는 한 회원이 카카오 / 네이버 두개 계정을 사용해 중복 가입 할 수 있다.
네이버 계정으로 상품을 구매하고 카카오 계정으로 로그인해서 구매내역을 확인하면
다른 계정으로 인식되어 구매내역이 없음으로 뜨기 때문에 사용자가 혼란스러울 여지가 있다.
사용자가 문의하면 다른 계정으로 로그인 해서 구매내역을 확인하라고 하면 해결할 수 있는 문제이기 때문에
우선 이대로 배포는 할 수 있겠지만, 이중 가입이 안되도록 하는 방안을 생각해봐야겠다.
5. 검수완료
'프로젝트' 카테고리의 다른 글
[ERROR] 프로젝트 빌드 에러 : Execution failed for task ':compileJava'.> invalid source release: 11 (1) | 2022.11.04 |
---|---|
[LOG] Logback 로그관리 (0) | 2022.10.22 |
[SpringBoot & JPA 프로젝트] 토스페이먼츠 카드사심사 후 라이브 API 적용 & 결제 TEST (성공/실패) (0) | 2022.10.11 |
[SpringBoot & JPA 프로젝트] 아쉬운점 & 보완할점 (2) | 2022.10.05 |
[SpringBoot & JPA 프로젝트] 사용자 / 판매자 카카오 로그인하기 (0) | 2022.09.29 |