๐
2025-05-14
๐ ํ์ต ๋ด์ฉ
- ๋ค์ด๋ฒ OAuth2 ๋ก๊ทธ์ธ ์ฐ๋ (Spring Boot 3.x)
- Access Token ๋ฐ๊ธ ๋ฐ ์ฌ์ฉ์ ์ ๋ณด ์์ฒญ
RestTemplate์ ํตํ ์ธ๋ถ API ํธ์ถ
- ์ฌ์ฉ์ ์ ๋ณด DTO ๋งคํ ๋ฐ Thymeleaf ์ฐ๋
1๏ธโฃ ๋ค์ด๋ฒ ์ ํ๋ฆฌ์ผ์ด์
๋ฑ๋ก
- ๋ค์ด๋ฒ ๊ฐ๋ฐ์์ผํฐ โ ์ ํ๋ฆฌ์ผ์ด์
๋ฑ๋ก
- ํ์ ์ค์ :
- ์ฌ์ฉ API: โ
๋ค์ด๋ฒ ๋ก๊ทธ์ธ
- ํ๊ฒฝ ์ถ๊ฐ: PC ์น
- ์๋น์ค URL:
http://localhost:8090
- Callback URL:
http://localhost:8090/naver/callback
- ๋ฐ๊ธ ์ ๋ณด ํ์ธ:
- ์ฌ์ฉ์ ์ ๋ณด ์ ๊ณต ํญ๋ชฉ ์ ํ (์ด๋ฆ, ์ด๋ฉ์ผ ๋ฑ)
2๏ธโฃ ๋ก๊ทธ์ธ ์์ฒญ URL ๊ตฌ์ฑ
https://nid.naver.com/oauth2.0/authorize?
response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=http%3A%2F%2Flocalhost%3A8090%2Fnaver%2Fcallback
&state=STATE_STRING
3๏ธโฃ Access Token ๋ฐ๊ธ ์์ฒญ
โ
์์ฒญ URL
https://nid.naver.com/oauth2.0/token
โ
ํ๋ผ๋ฏธํฐ
| ์ด๋ฆ | ์ค๋ช
|
|---|
| grant_type | authorization_code |
| client_id | ๋ฐ๊ธ๋ฐ์ Client ID |
| client_secret | ๋ฐ๊ธ๋ฐ์ Client Secret |
| code | callback์ผ๋ก ๋ฐ์ code |
| state | callback์ผ๋ก ๋ฐ์ state |
| redirect_uri | ์ฝ๋ฐฑ URI (๋ฑ๋ก๋ ๊ฒ๊ณผ ๋์ผํด์ผ ํจ) |
4๏ธโฃ ์ฌ์ฉ์ ์ ๋ณด ์์ฒญ
โ
์์ฒญ URL
https://openapi.naver.com/v1/nid/me
- Authorization ํค๋์
Bearer access_token ์ถ๊ฐ ํ์
- ์๋ต JSON ๊ตฌ์กฐ (์์):
{
"resultcode": "00",
"message": "success",
"response": {
"id": "example_user_id",
"profile_image": "https://example.com/image.png",
"email": "user@example.com",
"name": "ํ๊ธธ๋"
}
}
5๏ธโฃ ์ ์ฒด ์ปจํธ๋กค๋ฌ ์ฝ๋ (Spring Boot)
package com.example.demo.C05Naver;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;
@Controller
@Slf4j
@RequestMapping("/naver")
public class C01NaverLoginController {
private String NAVER_CLIENT_ID = "YOUR_CLIENT_ID";
private String NAVER_CLIERNT_SECRET = "YOUR_CLIENT_SECRET";
private String REDIRECT_URL = "http://localhost:8090/naver/callback";
private NaverTokenResponse naverTokenResponse;
@GetMapping("/login")
public String login() {
log.info("GET /naver/login...");
return "redirect:https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id="
+ NAVER_CLIENT_ID + "&state=STATE_STRING&redirect_uri=" + REDIRECT_URL;
}
@GetMapping("/callback")
public String callback(@RequestParam("code") String code,
@RequestParam("state") String state) {
log.info("GET /naver/callback | code: {}, state: {}", code, state);
String url = "https://nid.naver.com/oauth2.0/token";
HttpHeaders header = new HttpHeaders();
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "authorization_code");
params.add("client_id", NAVER_CLIENT_ID);
params.add("client_secret", NAVER_CLIERNT_SECRET);
params.add("code", code);
params.add("state", state);
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(params, header);
RestTemplate rt = new RestTemplate();
ResponseEntity<NaverTokenResponse> response =
rt.exchange(url, HttpMethod.POST, entity, NaverTokenResponse.class);
this.naverTokenResponse = response.getBody();
return "redirect:/naver/main";
}
@GetMapping("/main")
public void main(Model model) {
log.info("GET /naver/main...");
String url = "https://openapi.naver.com/v1/nid/me";
HttpHeaders header = new HttpHeaders();
header.add("Authorization", "Bearer " + this.naverTokenResponse.getAccess_token());
HttpEntity<?> entity = new HttpEntity<>(header);
RestTemplate rt = new RestTemplate();
ResponseEntity<NaverProfileResponse> response =
rt.exchange(url, HttpMethod.POST, entity, NaverProfileResponse.class);
model.addAttribute("profile", response.getBody());
}
@Data
private static class NaverTokenResponse {
public String access_token;
public String refresh_token;
public String token_type;
public String expires_in;
}
@Data
private static class Response {
public String id;
public String profile_image;
public String email;
public String name;
}
@Data
private static class NaverProfileResponse {
public String resultcode;
public String message;
public Response response;
}
}
6๏ธโฃ Thymeleaf ๋ทฐ (main.html)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Naver Login</title>
</head>
<body>
<h1>NAVER MAIN</h1>
<hr>
<p>์ด๋ฆ: <span th:text="${profile.response.name}">์ด๋ฆ</span></p>
<p>์ด๋ฉ์ผ: <span th:text="${profile.response.email}">์ด๋ฉ์ผ</span></p>
<img th:src="${profile.response.profile_image}" alt="ํ๋กํ ์ด๋ฏธ์ง" />
</body>
</html>
๐ฅ ์์ฝ
| ๋จ๊ณ | ์ค๋ช
|
|---|
| 1๋จ๊ณ | ๋ค์ด๋ฒ ์ฑ ๋ฑ๋ก |
| 2๋จ๊ณ | ๋ก๊ทธ์ธ URL๋ก ๋ฆฌ๋ค์ด๋ ํธ |
| 3๋จ๊ณ | Callback์์ code ์์ |
| 4๋จ๊ณ | Access Token ๋ฐ๊ธ |
| 5๋จ๊ณ | ์ฌ์ฉ์ ์ ๋ณด ์์ฒญ ๋ฐ ์ถ๋ ฅ |
๐ ์ฐธ๊ณ ์๋ฃ