Final Project

[ Spring ] REST API로 카카오 로그인 구현하기

HTT 2023. 1. 12. 02:38

* REST API를 이용해 Spring으로 카카로 로그인 구현하기 *

 

 

 

프로젝트에서 구현한 카카오 로그인을 작성해보겠습니다.

(Spring, MySQL 사용)

 

간략한 순서는 아래와 같습니다.

1. Kakao Developers에 가입해 애플리케이션 추가 -> 앱 키 발급받기
2. 플랫폼 설정하기
3. sts에 구현하기

 

 

 

1. 앱 키 발급받기

 

앱 키를 발급받기 위해 Kakao Developers에 가입해 애플리케이션을 추가합니다.

 

 

 

 

애플리케이션 추가  후 내 애플리케이션 > 앱 설정 > 요약정보/앱 키에 들어가 앱 키 정보를 확인합니다.

 

 

 


 

 

2. 플랫폼 설정하기

 

이제 도메인Redirect URI를 등록해줍니다.

 

 

애플리케이션 > 앱 설정 > 플랫폼에서 사이트 도메인을 등록합니다.

 

 

 

애플리케이션 > 제품 설정 > 카카오 로그인에서  활성화 설정을 "ON"으로 바꿔준 후 Redirect URI도 등록합니다.

 

 

처음 구현을 시작할 때  Redirect URI가 대체 무엇인지, 어디에 어떻게 쓰이는 건지 몰라서 한참 해맸던 것 같습니다.

 

Redirect URI란, 카카오에서 보내는 인가코드를 받는 곳의 주소인데, 쉽게 생각해서 이 곳에서 회원가입과 로그인을 하는 과정이 이루어진다고 생각하면 됩니다. 아래의 사진처럼요.

 

확인하고 계속하기를 누르면 로그인 or 회원가입/로그인 처리가 된다.

 

저는 선택 동의 항목으로 이메일만 설정했기 때문에 이메일만 보이는 상태입니다.

 

Redirect URI의 주소는 본인이 원하는 주소로 설정하면 됩니다. 저는 /프로젝트명/kakao로 했으나 /aouth, /login 등 자유롭게 설정해주세요.

 

 

 

애플리케이션 > 제품 설정 > 카카오 로그인 > 동의항목으로 이동해 카카오 회원가입 시 필요한 필수, 선택 정보를 설정합니다.

 

저는 서비스 이용 목적으로 이메일만 설정했습니다.

 

 

 

 

위의 모든 설정을 마쳤다면 REST API를 사용하기 위한 준비가 완료된 것입니다.

 

 

 

sts에 구현하기 앞서 카카오 로그인이 어떤 과정으로 처리되는지 확인해보도록 하겠습니다.

 

 

 

 

사용자가 카카오 로그인 버튼을 누르면 카카오 인증 서버는 동의 항목 선택창을 불러옵니다. 사용자가 동의 항목을 선택하고 "확인하고 계속하기" 버튼을 누르게 되면 서비스 서버는 인가 코드를 받게 되고, 받아온 이 인가 코드로 토큰을 발급받습니다. 서비스 서버가 갖고 있는 토큰으로 카카오 인증 서버에서 토큰을 통해 사용자 정보를 조회하고 회원가입/로그인 처리를 진행하게 됩니다.

 

 

 


 

 

3. sts에 구현하기

 

이제 sts를 이용해 카카오 로그인을 구현해보도록 해보겠습니다.

 

제가 사용한 파일의 목록은 다음과 같습니다.

login.jsp
KakaoController.java
KakaoService.java
CustomerDAOImpl.java
kakao.xml

 

 

 

 

💡 로그인 화면(login.jsp)

<a id="kakao-login-btn" href="https://kauth.kakao.com/oauth/authorize?client_id=ce8affbe0709e40494b6d8ad31a694eb&redirect_uri=http://localhost:10397/evweb/kakao&response_type=code">
<img alt="카카오 로그인 버튼" src="/evweb/images/kakao_login_medium_wide.png">
</a>

 

client_id= [   ]  부분에 위에서 발급받은 REST API 키를 입력하고, redirect_uri= [   ]  부분에 플랫폼에 등록한 Redirect URI를 입력해줍니다.

 

 

 

※ 카카오 로그인 이미지는 kakao developers에서 제공하는 이미지 파일을 사용하시면 됩니다.

 

 

 

 

💡 로그인 처리(KakaoService.java)

@Service
public class KakaoService {
	CustomerDAO dao;
	KakaoService() {};
	@Autowired
    public KakaoService(CustomerDAO dao) {
		super();
		this.dao = dao;
	}


	public String getToken(String code) throws IOException, org.json.simple.parser.ParseException {
        // 인가코드로 토큰받기
        String host = "https://kauth.kakao.com/oauth/token";
        URL url = new URL(host);
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        String token = "";
        try {
            urlConnection.setRequestMethod("POST");
            urlConnection.setDoOutput(true); // 데이터 기록 알려주기

            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(urlConnection.getOutputStream()));
            StringBuilder sb = new StringBuilder();
            sb.append("grant_type=authorization_code");
            sb.append("&client_id=bb8e4e9dda670b70895b6b70b420787b");
            sb.append("&redirect_uri=http://localhost:10397/evweb/kakao");
            sb.append("&code=" + code);

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

            int responseCode = urlConnection.getResponseCode();
            System.out.println("responseCode 확인 = " + responseCode);

            BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            String line = "";
            String result = "";
            while ((line = br.readLine()) != null) {
                result += line;
            }
            System.out.println("result = " + result);

            // json parsing
            JSONParser parser = new JSONParser();
            JSONObject elem = (JSONObject) parser.parse(result);

            String access_token = elem.get("access_token").toString();
            String refresh_token = elem.get("refresh_token").toString();
            System.out.println("refresh_token = " + refresh_token);
            System.out.println("access_token = " + access_token);

            token = access_token;

            br.close();
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


        return token;
    }

    // 유저 정보 받아오기
    public CustomerDTO getUserInfo(String access_token) throws IOException, org.json.simple.parser.ParseException {
        String host = "https://kapi.kakao.com/v2/user/me";
        HashMap<String, Object> result = new HashMap<String, Object>();
        try {
        	// 1.url 객체만들기
            URL url = new URL(host);
            // 2.url 에서 url connection 만들기
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            // 3.URL 연결구성
            urlConnection.setRequestProperty("Authorization", "Bearer " + access_token);
            urlConnection.setRequestMethod("GET");      
            
            //키값, 속성 적용
            int responseCode = urlConnection.getResponseCode();     // 서버에서 보낸 http 상태코드 반환
            System.out.println("responseCode = " + responseCode);
            // responsecode가 200이 아닌 경우 if문을 통해서 예외처리 가능
            
            BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            // 버퍼 사용해 읽기
            String line = "";
            String res = "";
            while((line=br.readLine())!=null)
            {
                res+=line;
            }

            System.out.println("res = " + res);

            // json으로 파싱하기
            JSONParser parser = new JSONParser();
            JSONObject obj = (JSONObject) parser.parse(res);
            JSONObject kakao_account = (JSONObject) obj.get("kakao_account");
            //JSONObject properties = (JSONObject) obj.get("properties");  //닉네임, 프로필이미지 ...
            
            String id = obj.get("id").toString();
            String email = kakao_account.get("email").toString();
            //String birthday = kakao_account.get("birthday").toString();
            
            result.put("id", id);
            result.put("email", email);
            //result.put("birthday", birthday);

            br.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
         return result;
    }
    
    // 정상적으로 받아왔는지 여부
    public String getAgreementInfo(String access_token)
    {
        String result = "";
        String host = "https://kapi.kakao.com/v2/user/scopes";
        try{
            URL url = new URL(host);
            HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
            urlConnection.setRequestMethod("GET");
            urlConnection.setRequestProperty("Authorization", "Bearer "+access_token);

            BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            String line = "";
            while((line=br.readLine())!=null)
            {
                result+=line;
            }

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

            // result is json format
            br.close();

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (ProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }


}

 

json으로 파싱하여 정보를 가져오는데, 이메일과 생일 정보(등)는 "kakao_account"에서, 닉네임, 프로필 이미지(등)는 "properties"에서 가져오게 됩니다. 자신이 어떤 정보를 가져오냐에 따라 어떤 것을 파싱할지 선택하면 됩니다.

자세한 항목은 kakao developers에서 확인할 수 있습니다.

 

저는 이메일만 필요하기 때문에 생일 정보와 "properties"는 주석처리를 해둔 상태이고, 파싱하여 가져온 데이터는 HashMap에 put해줍니다.

 

 

💡 로그인 처리(KakaoController.java)

 

@Controller
public class KakaoController {
	@Autowired
	KakaoService ks;
	customerService service;
	
	@RequestMapping("/kakao")
	public ModelAndView getCI(@RequestParam String code, HttpServletRequest request) throws IOException, ParseException {
		ModelAndView mav = new ModelAndView();
		
		System.out.println("########code######### : " + code);
		String access_token = ks.getToken(code); 
		System.out.println("####access_Token##### : " + access_token);
        
		CustomerDTO userInfo = ks.getUserInfo(access_token);
        
		String viewName = "";
		if (userInfo != null && userInfo.getState().equals("카카오")) {
		//세션저장
		HttpSession session = request.getSession();
     	   	session.setAttribute("user", userInfo);
     	   	viewName = "index";
		}else {
			viewName = "login";
		}
        
		mav.setViewName(viewName);
		return mav;
	}
    
}

 

 

Controller에서 위와 같은 과정이 진행되면 KakaoService에 정의해둔 메소드가 실행되어 알맞은 데이터들을 가져왔을 겁니다. 콘솔에 responseCode가 200번대이면 성공입니다.

 

 

만약 400번이 나온다면 위 과정이 잘 이루어졌는지 확인해보시길 바랍니다(앱 키, redirect uri, 파싱이 알맞게 잘 되었는지 등 확인하기).

 

저는 미리 session에 유저 정보를 저장해둔 상태입니다. 회원 테이블에 "state"라는 컬럼을 추가해서 만약 회원 상태가 "null"이 아니면서, "카카오"라면(카카오 로그인에 성공했다면) session에 데이터를 저장 후 메인화면으로 리턴되도록 처리했습니다.

 

 

다음 게시글에서는 이제 이 회원 데이터를 DB에 저장하는 작업을 해보겠습니다.