๐Ÿ“Œ Spring Boot ๋„ค์ด๋ฒ„ ๋กœ๊ทธ์ธ ์—ฐ๋™

My Pale Blue Dotยท2025๋…„ 5์›” 14์ผ
0

SPRING BOOT

๋ชฉ๋ก ๋ณด๊ธฐ
26/40
post-thumbnail

๐Ÿ“… 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
  • ๋ฐœ๊ธ‰ ์ •๋ณด ํ™•์ธ:
    • Client ID, Client Secret
  • ์‚ฌ์šฉ์ž ์ •๋ณด ์ œ๊ณต ํ•ญ๋ชฉ ์„ ํƒ (์ด๋ฆ„, ์ด๋ฉ”์ผ ๋“ฑ)

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_typeauthorization_code
client_id๋ฐœ๊ธ‰๋ฐ›์€ Client ID
client_secret๋ฐœ๊ธ‰๋ฐ›์€ Client Secret
codecallback์œผ๋กœ ๋ฐ›์€ code
statecallback์œผ๋กœ ๋ฐ›์€ 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๋‹จ๊ณ„์‚ฌ์šฉ์ž ์ •๋ณด ์š”์ฒญ ๋ฐ ์ถœ๋ ฅ

๐Ÿ”— ์ฐธ๊ณ  ์ž๋ฃŒ


profile
Here, My Pale Blue.๐ŸŒ

0๊ฐœ์˜ ๋Œ“๊ธ€