새로운 개인 프로젝트를 진행하게 되었다. 그 프로젝트에서 카카오 로그인을 구현하려 한다.

 

카카오 로그인 API는 다음과 같이 구현된다. 

 

 

 

 

 

 

우선 유저 DB는 다음과 같다. 맨 마지막 컬럼 BOOLEAN 형식의 `is_kakao`는 해당 유저가 카카오 유저인지 밝혀주는 역할을 한다.

 

 

 

https://developers.kakao.com/console/app 우선 카카오 developers 사이트에 가서 내 애플리케이션을 누르면 다음과 같은 화면이 뜬다. 애플리케이션 추가하기를 눌러 본인이 원하는 대로 설정한 후 등록한다.

 

 

 

 

 

 

등록 후 클릭해서 들어가면 앱키가 나오는데, 여기서 REST API 키가 중요하다. 복사하자.

 

 

 

로그인 완료 시, 리다이렉트 할 경로이다. 나는 다음과 같이 등록하였다.

 

 

 

 

 

 

로그인 시, 동의 받을 개인정보, 닉네임, 프로필사진, 카카오계정에 다음과 같이 필수동의로 설정해놓자.

 

 

 

 

 

// 로그인 페이지
@RequestMapping(value = "/login", method = RequestMethod.GET, produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView getLogin() {
    String client_id = "8908f5fd6288606dcad177472698dfbf";
    String redirect_uri = "http://localhost:8080/auth_kakao";
    String location = "https://kauth.kakao.com/oauth/authorize?response_type=code&client_id="+ client_id +"&redirect_uri="+ redirect_uri;

    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("location", location);
    modelAndView.setViewName("/user/login");
    return modelAndView;
}

 

이미 만들어놓은 일반적인 로그인 페이지 부분 GET 맵핑을 다음과 같이 수정한다. 우선 client_id 부분은 위에서 복사해놓은 REST API 키를 붙여놓고, redirect_uri 역시 위에서 설정해놓은 리다이렉트 경로를 붙여넣기 한다.

location 경로는 내 REST API키와 리다이렉트 경로를 파라미터로 가지고 있는 카카오 로그인 페이지 화면이다. 모델 뷰로 이 경로를 가지고 HTML로 가서

 

 

 

 

여기 카카오 로그인 버튼 a태그에 다음 주소를 설정하게 하면 저 버튼을 누를 시 카카오 로그인 화면으로 이동하게 된다.

 

 

@Controller
@RequestMapping("")
public class KakaoLoginController {
    private final KakaoService kakaoService;
    private final UserService userService;

    private String client_id = "8908f5fd6288606dcad177472698dfbf";
    private String redirect_uri = "http://localhost:8080/auth_kakao";

    @Autowired
    public KakaoLoginController(KakaoService kakaoService, UserService userService) {
        this.kakaoService = kakaoService;
        this.userService = userService;
    }

    @RequestMapping(value = "/auth_kakao", method = RequestMethod.GET)
    public String authKakao(
            @RequestParam("code") String code,
            UserEntity user,
            HttpSession session
    ) throws IOException {
        String accessToken = this.kakaoService.getAccessTokenFromKakao(client_id, code);
        HashMap<String, Object> userInfo = this.kakaoService.getUserInfo(accessToken);

 

일단 카카오 로그인을 하면 위에 redirect_uri 주소로 code 파라미터와 함께 리다이렉트하게 되는데, 그 주소("/auth_kakao")를 GET 방식으로 다음과 같이 구현한다. 그리고 받아온 code 파라미터역시 작성한다. 일단 서비스에 코드를 통해 토큰을 발급 요청하는 getAccessTokenFromKakao() 서비스와 받은 토큰으로 사용자 정보를 가져오는 getUserInfo() 서비스를 만든다.

 

 

 

public String getAccessTokenFromKakao(String client_id, String code) throws IOException {
    //------kakao POST 요청------
    String reqURL = "https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id="+client_id+"&code=" + code;
    URL url = new URL(reqURL);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("POST");

    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));

    String line = "";
    String result = "";

    while ((line = br.readLine()) != null) {
        result += line;
    }

    ObjectMapper objectMapper = new ObjectMapper();
    Map<String, Object> jsonMap = objectMapper.readValue(result, new TypeReference<Map<String, Object>>() {
    });

    System.out.println("Response Body : " + result);

    String accessToken = (String) jsonMap.get("access_token");
    String refreshToken = (String) jsonMap.get("refresh_token");
    String scope = (String) jsonMap.get("scope");

    System.out.println("Access Token : " + accessToken);
    System.out.println("Refresh Token : " + refreshToken);
    System.out.println("Scope : " + scope);

    return accessToken;
}

 

서비스에 토큰 발급받는 서비스를 만들었다. reqURL 경로는 카카오에 코드를 담아서 accessToken을 받는 과정이다.

 

 

 

 

 

public HashMap<String, Object> getUserInfo(String access_Token) throws IOException {
    // 클라이언트 요청 정보
    HashMap<String, Object> userInfo = new HashMap<String, Object>();


    //------kakao GET 요청------
    String reqURL = "https://kapi.kakao.com/v2/user/me";
    URL url = new URL(reqURL);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    conn.setRequestProperty("Authorization", "Bearer " + access_Token);

    int responseCode = conn.getResponseCode();
    System.out.println("responseCode : " + responseCode);

    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));

    String line = "";
    String result = "";

    while ((line = br.readLine()) != null) {
        result += line;
    }

    System.out.println("Response Body : " + result);

    // jackson objectmapper 객체 생성
    ObjectMapper objectMapper = new ObjectMapper();
    // JSON String -> Map
    Map<String, Object> jsonMap = objectMapper.readValue(result, new TypeReference<Map<String, Object>>() {
    });


    //사용자 정보 추출
    Map<String, Object> properties = (Map<String, Object>) jsonMap.get("properties");
    Map<String, Object> kakao_account = (Map<String, Object>) jsonMap.get("kakao_account");

    Long id = (Long) jsonMap.get("id");
    String nickname = properties.get("nickname").toString();
    String profileImage = properties.get("profile_image").toString();
    String email = kakao_account.get("email").toString();
    System.out.println("아이디" + id);
    System.out.println("닉네임" + nickname);
    System.out.println("프로필이미지" + profileImage);
    System.out.println("이메일" + email);

    //userInfo에 넣기
    userInfo.put("id", id);
    userInfo.put("nickname", nickname);
    userInfo.put("profileImage", profileImage);
    userInfo.put("email", email);
    return userInfo;
}

 

받은 토큰으로 사용자 정보를 가져오는 역할을 하는 서비스, reqURL 경로에 Bearer 토큰을 추가하여 GET 요청을 보내면 ResponseBody에 사용자 정보를 body값을 받게 된다.

 

 

 

 

 

@RequestMapping(value = "/auth_kakao", method = RequestMethod.GET)
public String authKakao(
        @RequestParam("code") String code,
        UserEntity user,
        HttpSession session
) throws IOException {
    String accessToken = this.kakaoService.getAccessTokenFromKakao(client_id, code);
    HashMap<String, Object> userInfo = this.kakaoService.getUserInfo(accessToken);
    System.out.println("id : " + userInfo.get("id"));

    // 로그인, 회원가입 로직 추가
    // 만약 처음 로그인 시, 회원 DB에 해당 이메일이 없다면
    if (this.userService.getUserByEmail( "kakao_" + userInfo.get("email").toString() ) == null) {
        // 회원가입하기
        this.kakaoService.kakaoRegister(userInfo, user);
    }
    // 로그인
    UserEntity loginUser = this.kakaoService.kakaoLogin(userInfo, user);
    if (loginUser == null) {
        return "redirect:/user/login";
    }
    session.setAttribute("user", loginUser);

    return "redirect:/";
}

 

최종 컨트롤러, 사용자 정보까지 받아왔다면 그것을 이용해서 로그인과 회원가입을 구현하면 된다.

 

 

 

 

 

 

로그아웃을 구현하기 위해 카카오 로그인의 고급 부분에 들어가서 로그아웃 URI 경로를 등록한다.

 

 

 

 

// 로그아웃
@RequestMapping(value = "/logout", method = RequestMethod.GET, produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView getLogout(HttpSession session) {
    UserEntity user = (UserEntity) session.getAttribute("user");    // 로그인한 유저 정보 불러오기

    String client_id = "8908f5fd6288606dcad177472698dfbf";
    String redirect_uri = "http://localhost:8080/logout_kakao";

    ModelAndView modelAndView = new ModelAndView();
    if (user.isKakao()) {   // 만약 카카오 로그인이라면
        String kakaoLogoutUrl = "https://kauth.kakao.com/oauth/logout?client_id=" + client_id + "&logout_redirect_uri=" + redirect_uri;
        return new ModelAndView("redirect:" + kakaoLogoutUrl);
    } else {                // 일반 로그인이라면
        session.setAttribute("user", null);
        modelAndView.setViewName("redirect:/");
        return modelAndView;
    }
}

 

일단 기존 로그아웃 맵핑을 다음과 같이 설정했다. 일단 로그인한 유저 세션의 정보를 불러온다(세션 정보 안에 유저 엔티티로 구성된 데이터가 들어가있다). client_id는 본인의 REST_API 키를 붙여넣기 하고, 리다이렉트경로는 위에서 설정한 로그아웃 리다이렉트 경로를 붙여넣기 한다.

 

만약 카카오 로그인 유저일 경우 로그아웃 경로를 다음과 같이 설정 후, 그 주소로 리다이렉트하게 한다. 일반 로그인 유저일 경우 원래 로직대로 동작하게끔 한다.

 

 

 

 

// 카카오 로그아웃
@RequestMapping(value = "/logout_kakao", method = RequestMethod.GET)
public String logoutKakao(HttpSession session) {
    session.setAttribute("user", null);
    return "redirect:/";
}

 

최종적으로 로그아웃을 하면 여기에 리다이렉트하게 되는데, HttpSession의 "user"를 null로 설정해 로그아웃되게 하였고, 홈 페이지로 리다이렉트하면 된다.

 

 

 

이로서 카카오 로그인 로그아웃 완성...

 

굳이 이 방법이 아니더라도 다른 방법으로 구현해보는 것도 나쁘지 않을지도.

'API' 카테고리의 다른 글

[API] 포트원 API 사용하기 (Spring Boot)  (0) 2024.07.11
[API] 카카오 지도 API 추가하기  (0) 2024.03.25
[API] 다음 주소 찾기 API  (0) 2024.01.24

+ Recent posts