API

session 을 이용한 Kakao로그인-2.구현

zee0541 2024. 12. 5. 22:46

 

https://zee0541.tistory.com/2

 

 

session 을 이용한 kakao로그인- 1.Kakao Developers 설정

먼저, Kakao Developers 에 접속 해서 애플리케이션을 추가 해야한다. https://developers.kakao.com/ Kakao Developers카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기

zee0541.tistory.com

 

kakao developer 설정을 안료 했으면 

코드를 작성하면 된다.

 

저는 Spring Framework 를 사용하였습니다. 참고 부탁드립니다.

 

KakaoVO

@Data              // kakaoVO 는 kakao 사용자 정보를 담는데 사용되는 것이다
public class KakaoVO {

  private long kakaoId;  // kakao 사용자 고유 ID
  private String nickname;  // 사용자 닉네임
  private String kakaoEmail;  // 사용자 이메일 
	
}

UserMapper

public interface UserMapper {
	
	public void insertUser(UserVO vo); //회원가입 처리

	public UserVO login(Map<String, String> params); //로그인 처리
	
	public int checkEmail(String email); //이메일 중복체크
	
	public boolean updateUser(UserVO vo); //회원정보 수정(비밀번호 미포함)
	
	public boolean updateUserPw(UserVO vo); //회원정보 수정(비밀번호 포함)

	public boolean deleteUser(long uno); //회원 탈퇴	
}

 

기존 UserMapper 에서 2개의 인터페이스 메소드 를 추가해야 한다.

   //사용자 이름(username)을 기준으로 해당 사용자의 정보르 검색
    public UserVO findByUsername(String username); 
	
	// 이메일 주소(email)를 기준으로 해당 사용자릐 정보를 검색
	public UserVO searchUsersByEmail(String email);

 

 

KakaoService 전체 코드

@Service
@Log4j
public class KakaService {

	@Autowired
	private UserMapper usermapper;
	
	public String getToken(String code) throws IOException {
	    String host = "https://kauth.kakao.com/oauth/token";
	    HttpURLConnection urlConnection = null;
	    String token = "";

	    try {
	        URL url = new URL(host);
	        urlConnection = (HttpURLConnection) url.openConnection();
	        urlConnection.setRequestMethod("POST");
	        urlConnection.setDoOutput(true);
	        urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

	        try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(urlConnection.getOutputStream()))) {
	            StringBuilder sb = new StringBuilder();
	            sb.append("grant_type=authorization_code");  
	            sb.append("&client_id=REST KEY"); // 앱키 ->  본인 REST KEY 작성
	            sb.append("&redirect_uri=Redirect UR"); // 등록한 Redirect URI 작성  
	            sb.append("&code=").append(code);

	            bw.write(sb.toString());
	            bw.flush();
	        }

	        int responseCode = urlConnection.getResponseCode();
	        log.info("responseCode = " + responseCode);

	        if (responseCode == 200) { // 성공한 경우만 처리
	            StringBuilder result = new StringBuilder();
	            try (BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()))) {
	                String line;
	                while ((line = br.readLine()) != null) {
	                    result.append(line);
	                }
	            }

	            log.info("result = " + result.toString());

	            JsonParser parser = new JsonParser();
	            JsonObject elem = parser.parse(result.toString()).getAsJsonObject();

	            token = elem.get("access_token").getAsString();
	        } else {
	            log.error("Failed to get token. Response code: " + responseCode);
	        }

	    } catch (IOException e) {
	        log.error("Error occurred: " + e.getMessage(), e);
	        throw e;
	    } finally {
	        if (urlConnection != null) {
	            urlConnection.disconnect();
	        }
	    }

	    return token;
	}
	
	public KakaoVO userInfo(String access_Token) throws IOException {
	    log.info("사용자 정보 가져오기 시작------------------------");
	    KakaoVO userInfo = new KakaoVO();

	    String reqURl = "https://kapi.kakao.com/v2/user/me";

	    try {
	        URL url = new URL(reqURl);
	        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
	        conn.setRequestMethod("GET");
	        conn.setRequestProperty("Authorization", "Bearer " + access_Token);

	        int responseCode = conn.getResponseCode();
	        log.info("응답 코드: " + responseCode);

	        if (responseCode == 200) {  // 성공한 경우에만 처리
	            StringBuilder result = new StringBuilder();
	            try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
	                String line;
	                while ((line = br.readLine()) != null) {
	                    result.append(line);
	                }
	            }

	            log.info("카카오 사용자 정보 응답 내용: " + result.toString());  // 전체 응답 로그 출력

	            JsonParser parser = new JsonParser();
	            JsonObject element = parser.parse(result.toString()).getAsJsonObject();

	            if (element.has("properties")) {
	                JsonObject properties = element.get("properties").getAsJsonObject();
	                String nickname = properties.has("nickname") ? properties.get("nickname").getAsString() : null;
	                userInfo.setNickname(nickname);
	                log.info("nickname: " + nickname);
	            } else {
	                log.warn("properties 객체가 존재하지 않습니다.");
	            }

	            if (element.has("kakao_account")) {
	                JsonObject kakao_account = element.get("kakao_account").getAsJsonObject();
	                String email = kakao_account.has("email") ? kakao_account.get("email").getAsString() : null;
	                userInfo.setKakaoEmail(email);
	                log.info("email: " + email);
	            } else {
	                log.warn("kakao_account 객체가 존재하지 않습니다.");
	            }

	            long id = element.has("id") ? element.get("id").getAsLong() : -1;
	            userInfo.setKakaoId(id);
	            log.info("id: " + id);

	        } else {
	            log.error("사용자 정보를 가져오는 데 실패했습니다. 응답 코드: " + responseCode);
	        }

	    } catch (Exception e) {
	        log.error("사용자 정보 가져오는 중 예외 발생: " + e.getMessage(), e);
	    }

	    return userInfo;
	}
	
	public void setAuthenticationWithSession(String email, HttpSession session) {
	    //  데이터베이스에서 사용자 정보 가져오기
	    UserVO kakaouser = usermapper.findByUsername(email);

	    // 사용자 정보를 세션에 저장하기
	    session.setAttribute("user", kakaouser);

	    // 추가로 필요한 데이터를 세션에 저장할 수 있음
	    session.setAttribute("isAuthenticated", true);
	}
	
}

 

메서드 만다 설명을 하겠습니다.

//Kakao OAuth2 인증을 통해 Access Token 응 얻는 메소드 이다
public String getToken(String code) throws IOException {
	  
	    // Kakao에서 Access Token을 발급받기 위한 엔드포인트
	    String host = "https://kauth.kakao.com/oauth/token";
	    HttpURLConnection urlConnection = null;
	    String token = "";

	    try {
	        URL url = new URL(host);
	        urlConnection = (HttpURLConnection) url.openConnection();
	        
	        // post 요청으로 설정 
	        urlConnection.setRequestMethod("POST");
	        
	        //요청 본문에 데이터를 쓸 수 있도록 설정
	        urlConnection.setDoOutput(true);
	        
	        //요청 본문의 데이터 타입을 설정
	        urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

	        try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(urlConnection.getOutputStream()))) {
	            StringBuilder sb = new StringBuilder();
	            sb.append("grant_type=authorization_code");  
	            sb.append("&client_id=REST KEY"); // 앱키 ->  본인 REST KEY 작성
	            sb.append("&redirect_uri=Redirect URI"); // 등록한 Redirect URI 작성  
	            sb.append("&code=").append(code);

	            bw.write(sb.toString());
	            bw.flush();
	        }
          
          // HTTP 응답 코드가 200인 경우에만 Access Token 처리를 진행
	        int responseCode = urlConnection.getResponseCode();
	        log.info("responseCode = " + responseCode);
             
	        if (responseCode == 200) { // 성공한 경우만 처리
	        
	          //BufferedReader를 사용해 Kakao 서버로부터 응답받은 데이터를 읽어온다
	            StringBuilder result = new StringBuilder();
	            try (BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()))) {
	                String line;
	                while ((line = br.readLine()) != null) {
	                    result.append(line);
	                }
	            }

	            log.info("result = " + result.toString());
             
             // access_token : JSON 응답에서 Accsee Token 값을 추출한다.
             // JsonParser: JSON 문자열을 파싱하여 JsonObject로 변환합니다.
	            JsonParser parser = new JsonParser();
	            JsonObject elem = parser.parse(result.toString()).getAsJsonObject();

	            token = elem.get("access_token").getAsString();
	        } else {
	            log.error("Failed to get token. Response code: " + responseCode);
	        }

	    } catch (IOException e) {
	        log.error("Error occurred: " + e.getMessage(), e);
	        throw e;
	    } finally {
	        if (urlConnection != null) {
	            urlConnection.disconnect();
	        }
	    }

	    return token;
	}

 

//String access_Token -> 카카오 인증 서버에서 발급된 엑세스
// KakaoVO -> 사용자의 정보를 담은 데이터 객체
public KakaoVO userInfo(String access_Token) throws IOException {
	    log.info("사용자 정보 가져오기 시작------------------------");
	    KakaoVO userInfo = new KakaoVO();

     // 사용자 정보를 요청하는 카카오 API의 엔드포인드 URL
	    String reqURl = "https://kapi.kakao.com/v2/user/me";

	    try {
	        //HttpURLConnection 객체를 통해 API 서버와 연결
	        // GET 요청 한다.
	        // Authorization 헤더에 Bearer 토큰 형식으로 액세스 토큰을 추가. (OAuth 인증 방식)
	        URL url = new URL(reqURl);
	        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
	        conn.setRequestMethod("GET");
	        conn.setRequestProperty("Authorization", "Bearer " + access_Token);
          
          // 서버에서 반환한 HTTP 응답 코드를 확인
	        int responseCode = conn.getResponseCode();
	        log.info("응답 코드: " + responseCode);

	        if (responseCode == 200) {  // 성공한 경우에만 처리
	           
	           // 서버에서 반환한 JSON 응답 데이터를 읽어서 StringBuilder에 저장.
	            StringBuilder result = new StringBuilder();
	            try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
	                String line;
	                while ((line = br.readLine()) != null) {
	                    result.append(line);
	                }
	            }

	            log.info("카카오 사용자 정보 응답 내용: " + result.toString());  // 전체 응답 로그 출력
              
              //JSON 데이터를 파싱하여 Java에서 처리 가능한 객체로 변환.
	            JsonParser parser = new JsonParser();
	            JsonObject element = parser.parse(result.toString()).getAsJsonObject();

	            if (element.has("properties")) {
	              // 사용자 프로필 정보가 담긴 properties 객체에서 닉네임을 추출.
	                JsonObject properties = element.get("properties").getAsJsonObject();
	                String nickname = properties.has("nickname") ? properties.get("nickname").getAsString() : null;
	                userInfo.setNickname(nickname);
	                log.info("nickname: " + nickname);
	            } else {
	                log.warn("properties 객체가 존재하지 않습니다.");
	            }

	            if (element.has("kakao_account")) {
	            
	              //사용자 계정 정보가 담긴 kakao_account 객체에서 이메일을 추출.
	                JsonObject kakao_account = element.get("kakao_account").getAsJsonObject();
	                String email = kakao_account.has("email") ? kakao_account.get("email").getAsString() : null;
	                userInfo.setKakaoEmail(email);
	                log.info("email: " + email);
	            } else {
	                log.warn("kakao_account 객체가 존재하지 않습니다.");
	            }
                //카카오 사용자의 고유 ID를 추출.
	            long id = element.has("id") ? element.get("id").getAsLong() : -1;
	            userInfo.setKakaoId(id);
	            log.info("id: " + id);

	        } else {
	            log.error("사용자 정보를 가져오는 데 실패했습니다. 응답 코드: " + responseCode);
	        }

	    } catch (Exception e) {
	        log.error("사용자 정보 가져오는 중 예외 발생: " + e.getMessage(), e);
	    }

	    return userInfo;
	}

 

 

public void setAuthenticationWithSession(String email, HttpSession session) {
	    //  데이터베이스에서 사용자 정보 가져오기
	    UserVO kakaouser = usermapper.findByUsername(email);

	    // 사용자 정보를 세션에 저장하기
	    session.setAttribute("user", kakaouser);

	    // 추가로 필요한 데이터를 세션에 저장할 수 있음
	    session.setAttribute("isAuthenticated", true);
	}

 

 UserService

public interface UserService {
	
	public void register(UserVO vo); //회원가입
	
	public boolean checkEmail(String email); //회원가입 이메일 중복체크
	
	public UserVO login(String username, String password); //로그인처리 (DB회원 조회)
	
	public boolean updateUser(UserVO vo); //회원정보 수정 (비밀번호 포함X)
	
	public boolean updateUserPw(UserVO vo); //회원정보 수정 (비밀번호 포함O)
	
	public boolean deleteUserByEmail(long uno); //회원 탈퇴
	
}

 

기존 UserService 에서 메서드 를 추가해야한다.

public UserVO findByUsername(String username);
	
UserVO searchUsersByEmail(String email)

 

UserServicelmple 추가

@Override
	public UserVO findByUsername(String username) {
		
		return mapper.findByUsername(username);
	}

	@Override
    public UserVO searchUsersByEmail(String email) {
        log.info("이메일로 사용자 검색 -----> " + email);
        UserVO userVO = mapper.searchUsersByEmail(email);
        
        if (userVO != null) {
            log.info("이메일로 찾은 사용자: " + userVO);
        } else {
            log.warn("해당 이메일로 사용자를 찾을 수 없습니다.");
        }

        return userVO;
    }

 

UserMapper.xml 추가

 <select id="findByUsername" parameterType="java.lang.String" resultType="org.zerock.domain.UserVO">
    select * from users where name = #{name, jdbcType=VARCHAR}
</select>

<select id="searchUsersByEmail" parameterType="java.lang.String" resultType="org.zerock.domain.UserVO">
    SELECT * FROM users WHERE email = #{email, jdbcType=VARCHAR}
</select>

 

 

Cotroller 같은 경우 KakaoController 클래스 를  만들거나  아니면

UserController 에 추가 해도 된다.

 

저는 따로 class 를 만들지 않고 UserController 에 추가했다.

 

UserController

 @GetMapping("/login/oauth2/code/kakao")
    public String login(@RequestParam("code") String code, HttpSession session, HttpServletRequest request, 
                        RedirectAttributes rttr, Model model) throws IOException {
        log.info("코드 받음: " + code);
        String url = "";

        // Access Token 발급
        String access_token = kakaoservice.getToken(code);
        log.info("발급된 액세스 토큰: " + access_token);

        // 사용자 정보 가져오기
        KakaoVO userInfo = kakaoservice.userInfo(access_token);
        model.addAttribute("code", code);
        model.addAttribute("access_token", access_token);
        model.addAttribute("userInfo", userInfo);

        String email = userInfo.getKakaoEmail();
        String nickname = userInfo.getNickname();

        log.info("사용자 이메일: " + email);
        
        UserVO users = usersService.searchUsersByEmail(email); // 이메일로 사용자 검색

        if (users != null) {
            // 기존 사용자 처리 로직
            session = request.getSession();
            log.info("기존 사용자 세션 설정: " + session);
            session.setAttribute("nickname", userInfo.getNickname());
            session.setAttribute("access_token", access_token);
            session.setAttribute("KakaoId", userInfo.getKakaoId());
            session.setAttribute("email", userInfo.getKakaoEmail());

            log.info("nickname: " + userInfo.getNickname());
            url = "redirect:/";  // 홈 화면으로 이동
        } else {
            // 신규 사용자 처리 로직
            log.info("사용자가 존재하지 않습니다. 신규 사용자 등록 필요");
            rttr.addAttribute("userEmail", email);
            rttr.addAttribute("nickname", nickname);
            url = "redirect:/user/join";  // 회원가입 페이지로 이동
        }

        log.info("리다이렉트 URL: " + url);
        return url;
    }

 

마지막 으로

<a href="https://kauth.kakao.com/oauth/authorize?client_id=REST KEY&redirect_uri=http:Redirect URL&response_type=code&prompt=login">
  <img src="${pageContext.request.contextPath}/resources/img/kakao_login_large_wide.png" alt="카카오 로그인">
</a>

 

작성하면

카카오 이미지 가 나올것이다

 

이미지 를 클릭하면

카카오 로그인 페이지 로 이동한다.

 

인증 까지 했다.

 

그런데 회원가입 페이지 로 이동한다.

 

왜?? 로그인 도 성공했는데 회원가입 페이지로 넘어갈까 생각하다가 알아냈다.

 

이유는  DB user테이블 에  설정 값 이메일 이랑 카카오계정 이메일 이랑 일치 해야지 로그인 성공이 된다,

(Conttoller 에서 일치해야 로그인 성공할수 있도록 구현했다...)

 

그럼, 카카오 계정 이메일 일치하게 DB user테이블 에  값 을 설정 하고 다시 해보겠다.

 

다시 카카오 로그인 페이지 로 이동되고

2단계 인증 까지 성공하면 Spring 에 console 창에 이렇게 나오면 kakao로그인 이 성공한거다.

 

 

 

이번에는  session 으로 구현을 했는데   Security 으로 연동해서 구현하는 것 보다 간단했는데 별 차이는 없었다.

 

 글을 읽으시고 설명이 더 필요하다고 하시면 댓글로 남겨주시면 수정하겠습니다!