๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ’ป ํ”„๋กœ์ ํŠธ/๐Ÿ ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€๐Ÿ 

[Spring, Vue.js] [Spring, Vue.js] ์นด์นด์˜ค ๊ฐ„ํŽธ ๋กœ๊ทธ์ธ ๋งŒ๋“ค๊ธฐโ‘ข - Spring์œผ๋กœ ํ† ํฐ ๊ตํ™˜ํ•˜๊ธฐ (REST API)

by ._.sori 2025. 9. 28.

 

 

 

 

[Spring, Vue.js] ์นด์นด์˜ค ๊ฐ„ํŽธ ๋กœ๊ทธ์ธ ๋งŒ๋“ค๊ธฐโ‘ก - Vue๋กœ ํ”„๋ก ํŠธ์—”๋“œ ์ž‘์„ฑํ•˜๊ธฐ (REST API)

[Spring,Vue.js] ์นด์นด์˜ค ๊ฐ„ํŽธ ๋กœ๊ทธ์ธ ๋งŒ๋“ค๊ธฐ โ‘  - ๊ตฌ์กฐ์™€ ํ๋ฆ„ ํŒŒ์•…ํ•˜๊ณ  ์นด์นด์˜ค ๋””๋ฒจ๋กœํผ์Šค ์„ค์ •ํ•˜๊ธฐ์นด์นด์˜ค ๊ฐ„ํŽธ ๋กœ๊ทธ์ธ์„ ๋งŒ๋“ค์–ด๋ณด์ž!๋จผ์ € ์–ด๋–ป๊ฒŒ ๋งŒ๋“ค์–ด์•ผํ•˜๋Š”์ง€ ๊ตฌ์กฐ์™€ ํ๋ฆ„์„ ํŒŒ์•…ํ•˜๊ณ ์นด์นด์˜ค

post-this.tistory.com

 

 

 

 

ํ”„๋ก ํŠธ์—”๋“œ๊นŒ์ง€ ์ž‘์„ฑํ–ˆ๋‹ค.
์ด์ œ ๋ฐฑ์—”๋“œ๋กœ ๋„˜์–ด์™€์„œ ํ† ํฐ ๊ตํ™˜๊นŒ์ง€ ๋งŒ๋“ค์–ด๋ณด์ž!

 

 

 

 

 

1. ํ™”๋ฉด

  • ๋กœ๊ทธ์ธ ์„ฑ๊ณต

 

  • ๋ฉ”์ธํ™”๋ฉด (๋‹‰๋„ค์ž„ O)

 

 

 

 

 


 

 

 

 

 

2. ์ „์ฒด ์ฝ”๋“œ

package ์—ฌ๋Ÿฌ๋ถ„๊ฑธ๋กœ ๋„ฃ์œผ์„ธ์š” &_&

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.LinkedHashMap;
import java.util.Map;

@RestController
@RequestMapping("/oauth")
@CrossOrigin(origins = "http://localhost:5173", allowCredentials = "true")
public class KakaoController {

    @Value("23558386d3608da96ec5a589aabe5a2b")
    private String clientId;

    @Value("${kakao.redirect-uri}")
    private String redirectUri;

    private final RestTemplate rest = new RestTemplate();


    @PostMapping("/kakao")
    public ResponseEntity<?> kakao(@RequestBody Map<String, String> body) {

        // 1) ํ† ํฐ ๊ตํ™˜
        String code = body.get("code");

        if (!StringUtils.hasText(code)) {
            return ResponseEntity.badRequest().body("code ๋ˆ„๋ฝ");
        }

        try {
            HttpHeaders tokenHeaders = new HttpHeaders();
            tokenHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            
            MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
            form.add("grant_type", "authorization_code");
            form.add("client_id", clientId);
            form.add("redirect_uri", redirectUri);
            form.add("code", code);

            HttpEntity<MultiValueMap<String, String>> tokenReq = new HttpEntity<>(form, tokenHeaders);

            ResponseEntity<Map> tokenRes = rest.postForEntity(
                    "https://kauth.kakao.com/oauth/token", tokenReq, Map.class);

            if (!tokenRes.getStatusCode().is2xxSuccessful() || tokenRes.getBody() == null) {
                return ResponseEntity.status(HttpStatus.BAD_GATEWAY).body("ํ† ํฐ ๊ตํ™˜ ์‹คํŒจ");
            }

            String accessToken = (String) tokenRes.getBody().get("access_token");
            if (!StringUtils.hasText(accessToken)) {
                return ResponseEntity.status(HttpStatus.BAD_GATEWAY).body("access_token ์—†์Œ");
            }



            // 2) ์‚ฌ์šฉ์ž ์ •๋ณด ์กฐํšŒ (๋‹‰๋„ค์ž„๋งŒ ํ•„์š”)
            HttpHeaders userHeaders = new HttpHeaders();

            userHeaders.setBearerAuth(accessToken);

            HttpEntity<Void> userReq = new HttpEntity<>(userHeaders);

            ResponseEntity<Map> userRes = rest.exchange(
                    "https://kapi.kakao.com/v2/user/me",
                    HttpMethod.GET,
                    userReq,
                    Map.class
            );

            if (!userRes.getStatusCode().is2xxSuccessful() || userRes.getBody() == null) {
                return ResponseEntity.status(HttpStatus.BAD_GATEWAY).body("์‚ฌ์šฉ์ž ์ •๋ณด ์š”์ฒญ ์‹คํŒจ");
            }



            // 3) JSON(Map)์—์„œ nickname๋งŒ ๊บผ๋ƒ„
            Map bodyMap = userRes.getBody();
            Map kakaoAccount = (Map) bodyMap.get("kakao_account");

            Map profile = kakaoAccount != null ? (Map) kakaoAccount.get("profile") : null;
            String nickname = profile != null ? (String) profile.get("nickname") : null;

            Map<String, Object> result = new HashMap<>();
            String safeNickname = (nickname != null) ? nickname : "KakaoUser";
            result.put("nickname", safeNickname);
            return ResponseEntity.ok(result);


         
        } catch (Exception ex) {
            ex.printStackTrace();
            return ResponseEntity.status(HttpStatus.BAD_GATEWAY).body("์นด์นด์˜ค ์—ฐ๋™ ์ค‘ ์˜ค๋ฅ˜");
        }
    }
}

 

 

 

 

 


 

 

 

 

 

3. application.properties

kakao.client-id= ๋ฐœ๊ธ‰๋ฐ›์€ REST API KEY ๋„ฃ๊ธฐ
kakao.redirect-uri=http://localhost:5173/oauth/code/kakao
resources ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ src ํ•˜์œ„์— ์žˆ์„ ๊ฒƒ์ด๋‹ค. resources ๋””๋ ‰ํ„ฐ๋ฆฌ ์•ˆ์— application.properties๊ฐ€ ์žˆ๋‹ค. ๊ฑฐ๊ธฐ์— ์ฝ”๋“œ๋ฅผ ์ ์–ด์ฃผ๋ฉด ๋œ๋‹ค. ๋ฌผ๋ก  REST API KEY์™€ redirect-uri๋Š” ๊ฐ์ž ์ •ํ•ด๋‘” ๊ฑธ๋กœ ๋ณ€๊ฒฝํ•ด์„œ ์ ์–ด์•ผ ํ•œ๋‹ค.

 

 

 

 


 

 

 

 

3. KakaoController.java 

// โ‘ 
@RestController

// โ‘ก
@RequestMapping("/oauth")

// โ‘ข
@CrossOrigin(origins = "http://localhost:5173", allowCredentials = "true")
public class KakaoController {


}
โ‘ 
REST API๋Š” ์›น์—์„œ ์ž์›์„ HTTP ๊ทœ์น™์— ๋งž์ถฐ ์ฃผ๊ณ ๋ฐ›๋Š” ๋ฐฉ์‹์ด๋‹ค. URL๋กœ ์ž์›์„ ๊ฐ€๋ฆฌํ‚ค๊ณ , HTTP ๋ฉ”์„œ๋“œ(GET, POST ๋“ฑ)๋กœ ๋ฌด์Šจ ๋™์ž‘์ธ์ง€ ํ‘œํ˜„ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  200 OK, 400 Bad Request์™€ ๊ฐ™์ด ์‘๋‹ต ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์•„๋ฌดํŠผ ์ด๋Ÿฐ ๊ทœ์น™์„ ๊ฐ€์ง€๊ณ  ์ž‘์„ฑํ•˜๋Š”๊ฒŒ REST API๋ผ๊ณ  ํ•˜๋ฉฐ, @RestController๋Š” REST API๋ฅผ ๊ฐœ๋ฐœํ•  ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค. 

@RestController๋Š” View๊ฐ€ ์•„๋‹Œ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ์— ์ ํ•ฉํ•˜๋‹ค. return ResponseEntity.ok(result);์ด๋Ÿฐ ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด, 200 ์‘๋‹ต์ฝ”๋“œ์™€ JSON ๋ฐ”๋””๋ฅผ ๊ฐ™์ด ๋ณด๋‚ธ๋‹ค. ์ด๋ ‡๊ฒŒ ํ™”๋ฉด์ด ์•„๋‹Œ result๋ผ๋Š” ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ @RestController๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค. (๊ทธ๋ƒฅ ๋ฐฑ์—”๋“œ, ํ”„๋ก ํŠธ์—”๋“œ ๋‚˜๋ˆ ์„œ ํ•˜๋Š” ๊ฑฐ๋ฉด @RestController ์“ด๋‹ค๊ณ  ๋ณด๋ฉด ๋œ๋‹ค)

โ‘ก
@RequestMapping์€ ๊ณตํ†ต ๊ฒฝ๋กœ๋ฅผ ์ ์–ด์คฌ๋‹ค. /oauth๋กœ ์˜ค๋Š” ์š”์ฒญ์€ KakaoController์—์„œ ์ฒ˜๋ฆฌํ•œ๋‹ค๊ณ  ์ •์˜ํ•œ ๊ฒƒ์ด๋‹ค.

โ‘ข
@Crossorigin์€ http://localhost:5173์—์„œ ๋ณด๋‚ด๋Š” ํฌ๋กœ์Šค ์˜ค๋ฆฌ์ง„ ์š”์ฒญ์„ ํ—ˆ์šฉํ•˜๊ฒŒ ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  allowCredentials ์ฟ ํ‚ค/์ธ์ฆ์ •๋ณด๋ฅผ ํ•จ๊ป˜ ์‹ค์–ด ๋ณด๋‚ด๋„ ๋˜๊ฒŒ ํ—ˆ์šฉํ•˜๋ผ๋Š” ์„ค์ •ํ–ˆ๋‹ค. ํ”„๋ก ํŠธ(http://localhost:5173)์™€ ๋ฐฑ์—”๋“œ(http://localhost:8080)์˜ ์˜ค๋ฆฌ์ง„์ด ๋‹ค๋ฅด๋ฉด, ๋ธŒ๋ผ์šฐ์ €๊ฐ€ CORS ๊ทœ์น™์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ด ์ฝ”๋“œ๋ฅผ ์ •์˜ํ•˜์ง€ ์•Š์œผ๋ฉด, ํ™”๋ฉด์—์„œ ์นด์นด์˜ค ๊ฐ„ํŽธ ๋กœ๊ทธ์ธ์ด ์ œ๋Œ€๋กœ ์ˆ˜ํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. ์˜ค๋ฆฌ์ง„์ด ๋‹ฌ๋ผ์„œ KakaoController๋กœ ์–ด๋–ค ์š”์ฒญ๋„ ๋ฐ์ดํ„ฐ๋„ ์˜ค์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด ๋‹ค์Œ ์ฝ”๋“œ๋ถ€ํ„ฐ๋Š” KakaoController์— ์ ์–ด์ค€๋‹ค.

 

 

    // โ‘ 
    @Value("${kakao.client-id}")
    private String clientId;

    // โ‘ก
    @Value("${kakao.redirect-uri}")
    private String redirectUri;
โ‘ 
์ฝ”๋“œ ๋‚ด๋ถ€์— ์‹œํฌ๋ฆฟ ํ‚ค๋ฅผ ์ €์žฅํ•  ๊ฒฝ์šฐ ๊นƒํ—ˆ๋ธŒ์— ์ฝ”๋“œ๊ฐ€ ์˜ฌ๋ผ๊ฐ€๋ฉด ์•ˆ๋œ๋‹ค. ๊ทธ๋Ÿด ๊ฒฝ์šฐ application.properties์— ๋ณด์•ˆ์ด ํ•„์š”ํ•œ ๊ฐ’์„ ๋„ฃ์–ด๋‘๊ณ  ์™ธ๋ถ€์— ๋…ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  application.properties์— ๋ณด๊ด€๋œ ๊ฐ’์„ ๊บผ๋‚ด์˜ค๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด @Value๊ฐ€ ๋œ๋‹ค.

โ‘ก
๋™์ผํ•˜๊ฒŒ ์ ์–ด์ค€๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ผญ application.properties์— ์ ์–ด ํ˜„์žฌ ์ฝ”๋“œ์™€ ๊ฐ™์ด ๋ฐ›์ง€ ์•Š์•„๋„ ๋œ๋‹ค. @Value("๋ฐœ๊ธ‰๋ฐ›์€ REST_API_KEY")๋กœ ์ ์–ด๋„ ๋œ๋‹ค.(๊ทผ๋ฐ ๊ทธ๋ ‡๊ฒŒํ•˜๋ฉด @Value๋ฅผ ์“ด ์˜๋ฏธ๋Š” ์‚ฌ๋ผ์ง„๋‹ค) ์•„๋‹ˆ๋ฉด ๋”ฐ๋กœ ๋„ฃ์–ด์ค˜๋„ ๋œ๋‹ค. 

 

 

    // โ‘ 
    private final RestTemplate rest = new RestTemplate();
โ‘ 
RestTemplate๋Š” ์Šคํ”„๋ง์—์„œ ์™ธ๋ถ€ HTTP API๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์“ฐ๋Š” ๋™๊ธฐ ๋ฐฉ์‹์˜ ๊ฐ„๋‹จํ•œ ํด๋ผ์ด์–ธํŠธ์ด๋‹ค. ์—ฌ๊ธฐ์„œ ๋™๊ธฐ ๋ฐฉ์‹์ด๋ž€, ์ž‘์—…์„ ๋งˆ์น  ๋•Œ๊นŒ์ง€ ๋‹ค์Œ ์ž‘์—…์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ˆœ์ฐจ์ ์ธ ๋ฐฉ์‹์œผ๋กœ ๋Œ€์šฉ๋Ÿ‰ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ๋ณด๋‹ค ๋ฐ์ดํ„ฐ์˜ ์ˆœ์ฐจ์ ์ธ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์‚ฌ์šฉํ•œ๋‹ค. 

์นด์นด์˜ค์˜ ๋กœ๊ทธ์ธ ํ๋ฆ„์„ ๋ณด๋ฉด ์ธ๊ฐ€์ฝ”๋“œ๋ฅผ ๋ฐ›๊ณ  -> ๋ฐ›์„๊ฑธ๋กœ ํ† ํฐ ๊ตํ™˜์„ ํ•˜๊ณ  -> ํ† ํฐ์œผ๋กœ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์กฐํšŒ ํ•˜๋Š” ์ˆœ์„œ๋กœ ์ด๋ค„์ ธ ์žˆ๋‹ค. ์ˆœ์ฐจ์ ์ธ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋„ ํ•„์š”ํ•˜์ง€๋งŒ, ๊ฒฐ๊ตญ ํ† ํฐ๋„ ์‚ฌ์šฉ์ž ์ •๋ณด ์กฐํšŒ๋„ ์™ธ๋ถ€ HTTP API์ธ ์นด์นด์˜ค๋ฅผ ํ˜ธ์ถœํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— RestTemplate๊ฐ€ ๋ณต์žกํ•˜๊ฒŒ ์ž‘์„ฑ๋  ์ฝ”๋“œ๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ค€๋‹ค.

๊ฑฐ๊ธฐ๋‹ค ์ฒซ๋ฒˆ์งธ ํฌ์ŠคํŒ…์—์„œ ํ† ํฐ ๊ตํ™˜ํ•  ๋•Œ, application/x-www-form-urlencoded๋กœ ์ „์†กํ•˜๊ณ  JSON ์‘๋‹ต์„ ๋ฐ›๋Š”๋‹ค๊ณ  ๋˜์–ด์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ „์†ก, ์‘๋‹ตํ•˜๋Š” ํ˜•ํƒœ์— ๋งž์ถฐ ๋ณ€ํ™˜ํ•ด์•ผํ•˜๋Š”๋ฐ ๊ทธ๊ฑธ ํ•œ ์ค„๋กœ ๊น”๋”ํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค. 

์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ์ ์€ ์นด์นด์˜ค ๊ฐ„ํŽธ ๋กœ๊ทธ์ธ์— RestTemplate๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ„๋‹จํ•˜๊ฒŒ ๋๋‚ผ ์ˆ˜ ์žˆ๋‹ค๋Š”๊ฑฐ์ง€, ์ด๊ฒŒ ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์ด๋ผ๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. RestTemplate๋ฅผ ๊ณต๋ถ€ํ•˜๋ฉด WebClient์— ๋Œ€ํ•ด์„œ๋„ ์ž์—ฐ์Šค๋ ˆ ์•Œ๊ฒŒ๋˜๋Š”๋ฐ, WebClient๊ฐ€ ๋น„๋™๊ธฐ ๋ฐฉ์‹์œผ๋กœ ์ด๋ค„์ ธ ๋Œ€๋Ÿ‰์œผ๋กœ ๋™์‹œ ํ˜ธ์ถœ์ด ๋“ค์–ด์™€๋„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์„œ ๋” ๊ถŒ์žฅํ•˜๋Š” ์ถ”์„ธ์ด๋‹ค. ์ง€๊ธˆ ํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์€ ๊ฐ„๋‹จํ•œ ํ”„๋กœ์ ํŠธ๋กœ ์‹ค์ œ ์œ ํฌํ• ๊ฒŒ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— RestTemplate๋กœ ์ž‘์„ฑํ–ˆ๋‹ค.

 

 

    // โ‘ 
    @PostMapping("/kakao")
    
    // โ‘ก
    public ResponseEntity<?> kakao(@RequestBody Map<String, String> body) {
    
    }
โ‘ 
Vue์—์„œ await axios.post("http://localhost:8080/oauth/kakao", { code }); ์ด ์ฝ”๋“œ๋ฅผ ๊ธฐ์–ตํ•ด์•ผํ•œ๋‹ค. ํ”„๋ก ํŠธ์—์„œ ์ธ๊ฐ€์ฝ”๋“œ๋ฅผ ๋ฐ›์•„ ๋ฐฑ์—”๋“œ๋กœ ๋ณด๋‚ธ ์ฝ”๋“œ์ด๋‹ค. ์ด๋•Œ post๋กœ ๋ณด๋‚ด์„œ, ๋ฐฑ์—”๋“œ ์‹œ์ ์—์„œ ๋‹ค์‹œ PostMapping์œผ๋กœ ๋ฐ›์•˜๋‹ค.

โ‘ก
์ธ๊ฐ€์ฝ”๋“œ๊ฐ€ ์š”์ฒญ ๋ฐ”๋””๋กœ ๋„์ฐฉํ–ˆ์„ ๊ฒƒ์ด๋‹ค. ๋„์ฐฉํ•œ ํ˜•ํƒœ๋Š” JSON์ด๋‹ค. JSON์€ ์‚ฌ๋žŒ์ด ์ฝ๊ณ  ์“ฐ๊ธฐ ์‰ฌ์šฐ๋ฉด์„œ ๊ธฐ๊ณ„๊ฐ€ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์‰ฌ์šด ํ…์ŠคํŠธ ๊ธฐ๋ฐ˜์˜ ๋ฐ์ดํ„ฐ ๊ตํ™˜ ํ˜•์‹์ด๋‹ค. ์ด๋ ‡๊ฒŒ ์˜จ body๋ฅผ Map<String, String>์œผ๋กœ ๋ณ€ํ™˜ํ•  ๊ฒƒ์ด๋‹ค. ๋ณ€ํ™˜์€ ์–ด๋ ต์ง€ ์•Š๋‹ค. @RequestBody๋ฅผ ๋ถ™์—ฌ ๋ฐ”๋””๋ฅผ ์ฐพ์•„ ์ฝ๋„๋ก ํ•˜๋ฉฐ, ์Šคํ”„๋ง์ด ์•Œ์•„์„œ ์š”๊ตฌ ํƒ€์ž…์— ๋งž๋Š” HttpMessageConverter๋ฅผ ๋ถˆ๋Ÿฌ ์—ญ์ง๋ ฌํ™” ํ•  ๊ฒƒ์ด๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ์™œ Map์œผ๋กœ ๋ฐ”๊พผ๊ฑธ๊นŒ? ์ง€๊ธˆ ์ด ํ”„๋กœ์ ํŠธ๋Š” ๊ฐ„๋‹จํ•˜๊ฒŒ ์นด์นด์˜ค ๊ฐ„ํŽธ ๋กœ๊ทธ์ธ์„ ์ˆ˜ํ–‰ํ•˜๋ ค๊ณ  ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ํ”„๋ก ํŠธ์—์„œ ์˜ค๋“ , ์นด์นด์˜ค์—์„œ ๋ณด๋‚ด์˜ค๋“  JSON์„ ํ‚ค=๊ฐ’ ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•ด ๋น ๋ฅด๊ฒŒ ๋นผ์˜ค๊ธฐ ์œ„ํ•ด Map์„ ๊ณ ๋ฅธ ๊ฒƒ์ด๋‹ค. ๋ฌผ๋ก  ๊ทธ๋งŒํผ ๋‹จ์ ๋„ ์žˆ๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“  ๊ฒƒ๋งŒํผ  ์•ˆ์ „์„ฑ์ด ๋–จ์–ด์ง„๋‹ค. ๊ทธ๋Ÿฐ๊ฒŒ ์‹ซ๋‹ค๋ฉด DTO๋ฅผ ๋งŒ๋“œ๋Š” ๊ฑธ ์ถ”์ฒœํ•œ๋‹ค.

ResponseEntity๋Š” Spring MVC์—์„œ HTTP ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ์ œ์–ดํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๋Š” ํด๋ž˜์Šค๋กœ Response Body, Header, Status Code๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ์–ด, ์„ธ๋ฐ€ํ•œ ์‘๋‹ต ๊ด€๋ฆฌ๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•œ๋‹ค.

์•„๋ž˜ ์ฝ”๋“œ๋Š” kakao ๋ฉ”์„œ๋“œ ์•ˆ์— ๋“ค์–ด๊ฐ„๋‹ค.

 

 

        // โ‘ 
        String code = body.get("code");
        
        // โ‘ก
        if (!StringUtils.hasText(code)) {
            return ResponseEntity.badRequest().body("code ๋ˆ„๋ฝ");
        }
โ‘ 
์—ญ์ง๋ ฌํ™”ํ•œ body์—์„œ ์ธ๊ฐ€์ฝ”๋“œ(code)๋ฅผ ๊ฐ€์ ธ์™€ code์— ๋„ฃ๋Š”๋‹ค.

โ‘ก
StringUtils.hasText๋ฅผ ํ†ตํ•ด code๊ฐ€ null/""/"  "์€ ์•„๋‹Œ์ง€, ์‹ค์ œ ๋ฌธ์ž๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ๋“ค์–ด๊ฐ€ ์žˆ๋Š”์ง€ ๊ฒ€์‚ฌํ•œ๋‹ค. ๋งŒ์•ฝ ํ†ต๊ณผ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, badRequest ์‘๋‹ต์ฝ”๋“œ์™€ ํ•จ๊ป˜ "code ๋ˆ„๋ฝ"์ด๋ผ๋Š” ๋ฌธ์ž์—ด์„ ํ”„๋ก ํŠธ๋กœ ๋ณด๋‚ธ๋‹ค.

 

 

  // โ‘ 
  try {
  
  } catch(e) {
  
  }
โ‘ 
์•ž ํฌ์ŠคํŒ…์—์„œ ์„ค๋ช…ํ–ˆ๋˜ try-catch ๊ตฌ๋ฌธ์ด๋‹ค. try์—๋Š” ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์„ ๋‚˜์—ดํ•˜๊ณ , catch๋Š” ์˜ˆ์™ธ๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ ์ ๋Š”๋‹ค. ์ด ๋‹ค์Œ๋ถ€ํ„ฐ ์ฝ”๋“œ๋Š” try ์•ˆ์— ๋“ค์–ด๊ฐˆ ์ฝ”๋“œ์ด๋‹ค.

 

 

            // โ‘ 
            HttpHeaders tokenHeaders = new HttpHeaders();
            
            // โ‘ก
            tokenHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
โ‘ 
์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ํ† ํฐ ๊ตํ™˜์„ ํ•  ์ฐจ๋ก€๊ฐ€ ์™”๋‹ค. ์นด์นด์˜ค์—์„  ํ† ํฐ์„ ๊ตํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ ์–ด๋†จ๋‹ค. Content-Type์ด x-www-form-urlencoded์ด๋ฉฐ, POST๋กœ ์š”์ฒญํ•˜๋„๋ก ๋˜์–ด์žˆ๋‹ค. ์„œ๋ฒ„๋Š” ํ—ค๋”๋ฅผ ๋ณด๊ณ  ๋ณด๋‚ด์ง„ ๋ฐ”๋””๋ฅผ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ํŒŒ์‹ฑํ• ์ง€ ๊ฒฐ์ •ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ํ—ค๋”์— ๋ฏธ๋ฆฌ ํผ๋ฐฉ์‹์ด๋ผ๊ณ  ์ ์–ด์ค˜์•ผํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ํผ ํ˜•์‹์ž„์„ ์•Œ๋ฆฌ๊ธฐ ์œ„ํ•ด tokenHeaders๋ฅผ ์„ ์–ธํ–ˆ๋‹ค.

โ‘ก
tokenHeaders์— Content-Type์˜ ํผ ํ˜•์‹์— ๋งž์ถฐ MediaType.APPLICATION_FORM_URLENCODED๋กœ ์„ค์ •ํ–ˆ๋‹ค.

 

 

            // โ‘ 
            MultiValueMap<String, String> form = new LinkedMultiValueMap<>();

            // โ‘ก
            form.add("grant_type", "authorization_code");
            form.add("client_id", clientId);
            form.add("redirect_uri", redirectUri);
            form.add("code", code);
โ‘ 
๋ฐ”๋””๋ฅผ ๋‹ด์„ ๊ทธ๋ฆ‡์„ ๋งŒ๋“ค์–ด์•ผํ•œ๋‹ค. form์— ์‹ค์ œ ๋ฐ”๋”” ๋‚ด์šฉ์ด ๋“ค์–ด๊ฐ€๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ž˜์„œ ์ด ๋ฐ”๋””๋ฅผ ์–ด๋–ป๊ฒŒ ๋‹ด์„๊ฑด์ง€ ์ •์˜ํ–ˆ๋‹ค. MultiValueMap์€ ์ž๋ฐ”์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ spring์—์„œ ์ œ๊ณตํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค. Map ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†ํ•  ๋•Œ, Value์˜ ๊ฐ’์„ List๋กœ ๊ฐ์‹ผ ์ฑ„ ์ƒ์†๋ฐ›๋Š”๋‹ค. ๋˜ ํ•˜๋‚˜์˜ key์— ์—ฌ๋Ÿฌ ๊ฐ’์„ ๋„ฃ๊ฑฐ๋‚˜, ์‚ฝ์ž… ์ˆœ์„œ๊ฐ€ ์œ ์ง€๋˜๋Š” ํŠน์ง•์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. 

โ‘ก
์นด์นด์˜ค๊ฐ€ ์š”๊ตฌํ•œ ๊ฒƒ์— ๋งž์ถฐ form์— ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋‹ค. ์•„๋งˆ ํ˜•ํƒœ๋Š” key=value&key=value.. ์ด๋Ÿฐ ์‹์ด ๋œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ† ํฐ์„ ์š”์ฒญํ•  ๊ฐ์ฒด๋Š” ์ด์ œ ๋งŒ๋“ค์–ด์กŒ๋‹ค.

 

 

           // โ‘ 
           HttpEntity<MultiValueMap<String, String>> tokenReq = new HttpEntity<>(form, tokenHeaders);
โ‘ 
HttpEntity๋Š” ๋ฐ”๋””์™€ ํ—ค๋” ์ปจํ…ํŠธ ํƒ€์ž…์„ ํ•จ๊ป˜ ๋ณด๋‚ด๊ธฐ ๋•Œ๋ฌธ์— ํ˜„์žฌ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ํผ ํ˜•์‹์— ๋งž์ถฐ ์‚ฌ์šฉํ•˜๊ธฐ ์•„์ฃผ ์œ ์šฉํ•˜๋‹ค. ๋ฐ”๋””์—๋Š” ํ‚ค=๊ฐ’&ํ‚ค=๊ฐ’์ด ๋“ค์–ด๊ฐ€๊ณ , ํ—ค๋” ์ปจํ…ํŠธ ํƒ€์ž…์—๋Š” ํผํ˜•์‹์ด ๋“ค์–ด๊ฐ„๋‹ค.

 

 

           // โ‘ 
           ResponseEntity<Map> tokenRes = rest.postForEntity("https://kauth.kakao.com/oauth/token", tokenReq, Map.class);
โ‘ 
("https://kauth.kakao.com/oauth/token", tokenReq, Map.class)๋ฅผ ๋ณด๋ฉด ("์š”์ฒญ์„ ๋ณด๋‚ผ ๊ฒฝ๋กœ", ๋ณด๋‚ผ ๊ฐ์ฒด, ์‘๋‹ต์„ ์—ญ์ง๋ ฌํ™”ํ•  ํƒ€์ž…)์„ ์ ์–ด์„œ postForEntity๋กœ POST ์š”์ฒญ์„ ๋ณด๋ƒˆ๋‹ค. postForEntity๋Š” RestTemplate๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํŽธ์˜๋ฉ”์„œ๋“œ์ธ๋ฐ, ์ฃผ๋กœ ์ƒํƒœ/ํ—ค๋”/๋ฐ”๋””๋ฅผ ํ•œ๊บผ๋ฒˆ์— ๋ฐ›๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

์นด์นด์˜ค์˜ ์‘๋‹ต ํ˜•ํƒœ๋Š” JSON์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ด๊ฑธ Map์œผ๋กœ ์—ญ์ง๋ ฌํ™”ํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ tokenRes ์•ž์— ์‘๋‹ต ํƒ€์ž…์„ Map์œผ๋กœ ์„ ์–ธํ–ˆ๋‹ค.

 

 

            // โ‘ 
            if (!tokenRes.getStatusCode().is2xxSuccessful() || tokenRes.getBody() == null) {
                return ResponseEntity.status(HttpStatus.BAD_GATEWAY).body("ํ† ํฐ ๊ตํ™˜ ์‹คํŒจ");
            }

            // โ‘ก
            String accessToken = (String) tokenRes.getBody().get("access_token");
            if (!StringUtils.hasText(accessToken)) {
                return ResponseEntity.status(HttpStatus.BAD_GATEWAY).body("access_token ์—†์Œ");
            }
โ‘ 
tokenRes์˜ ๋ฐ›์•„์˜จ ์ƒํƒœ ์ฝ”๋“œ๊ฐ€ ์„ฑ๊ณต์ ์ด์ง€ ์•Š๊ฑฐ๋‚˜, ๋ฐ›์•„์˜จ ๋ฐ”๋””๊ฐ€ null์ธ ๊ฒฝ์šฐ ์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

โ‘ก
tokenRes์˜ ๋ฐ”๋””์—์„œ access_token์— ๋“ค์–ด์žˆ๋Š” ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™”๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด accessToken์˜ ๋ฌธ์ž์—ด ์œ ํšจ์„ฑ ๊ฒ€์ฆ์ด False์ด๋ฉด ์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

 

 

 

 


 

 

๋‹ค์Œ์€ ๋ฐฑ์—”๋“œ๋กœ ์‚ฌ์šฉ์ž ์ •๋ณด ์กฐํšŒ ๋ถ€๋ถ„์„ ์ ์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋งŒ์•ฝ ๋‹ค ํ•˜๊ณ  ๋‚˜์„œ๋„ ์•ˆ ๋˜๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ๋Œ“๊ธ€์„ ๋‚จ๊ฒจ์ฃผ์„ธ์š”.

์ œ๊ฐ€ ๋นผ๋จน์€ ๋ถ€๋ถ„์ด ์žˆ์„์ง€๋„ ๋ชฐ๋ผ์š”,,, ํ—ท ๐Ÿฅ

! ํ™”์ดํŒ… !

 


 

 

'๐Ÿ’ป ํ”„๋กœ์ ํŠธ > ๐Ÿ ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€๐Ÿ ' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Spring, Vue.js] ์นด์นด์˜ค ๊ฐ„ํŽธ ๋กœ๊ทธ์ธ ๋งŒ๋“ค๊ธฐโ‘ฃ - ์‚ฌ์šฉ์ž ์ •๋ณด ์กฐํšŒํ•ด์„œ ๋‹‰๋„ค์ž„ ์ฝ์–ด์˜ค๊ธฐ (REST API)  (0) 2025.09.30
[Spring, Vue.js] ์นด์นด์˜ค ๊ฐ„ํŽธ ๋กœ๊ทธ์ธ ๋งŒ๋“ค๊ธฐโ‘ก - Vue๋กœ ํ”„๋ก ํŠธ์—”๋“œ ์ž‘์„ฑํ•˜๊ธฐ (REST API)  (1) 2025.09.27
[Spring,Vue.js] ์นด์นด์˜ค ๊ฐ„ํŽธ ๋กœ๊ทธ์ธ ๋งŒ๋“ค๊ธฐ โ‘  - ๊ตฌ์กฐ์™€ ํ๋ฆ„ ํŒŒ์•…ํ•˜๊ณ  ์นด์นด์˜ค ๋””๋ฒจ๋กœํผ์Šค ์„ค์ •ํ•˜๊ธฐ (REST API)  (0) 2025.09.25
[Vue.js] ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ, localStorage๋ฅผ ์ด์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์•„์ด๋””๊ฐ€ ๋ฉ”์ธํ™”๋ฉด์— ๋ณด์ด๋„๋ก ์„ค์ •ํ•˜๊ธฐ!  (2) 2025.09.02
[SpringBoot, Vue.js] ๋กœ๊ทธ์ธ ํ™”๋ฉด ๋งŒ๋“ค๊ธฐ, passwordEncoder.matches๋ฅผ ํ†ตํ•ด ์•”ํ˜ธํ™”๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋น„๊ตํ•˜๊ธฐ.  (5) 2025.08.31