๐Ÿ“Œ Spring Boot๋กœ ์นด์นด์˜คํ†ก ์นœ๊ตฌ์—๊ฒŒ ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๊ธฐ

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

SPRING BOOT

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

๐Ÿ“… 2025-05-13


โœ… ์ด ๋ฌธ์„œ๋กœ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

์ด ๋ฌธ์„œ ํ•˜๋‚˜๋กœ ๋‹ค์Œ ์ž‘์—…์„ ์ง์ ‘ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  • ์นด์นด์˜ค ๊ฐœ๋ฐœ์ž์„ผํ„ฐ์—์„œ ์•ฑ ์„ค์ •
  • ์ธ๊ฐ€ ์ฝ”๋“œ ์š”์ฒญ ๋ฐ access token ๋ฐœ๊ธ‰
  • ์นœ๊ตฌ ๋ชฉ๋ก ์กฐํšŒ
  • ํ…œํ”Œ๋ฆฟ ๊ธฐ๋ฐ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์นœ๊ตฌ์—๊ฒŒ ์ „์†ก
  • Spring Boot ํ”„๋กœ์ ํŠธ์— ๋ฐ”๋กœ ์ ์šฉ

1๏ธโƒฃ ์นด์นด์˜ค ๊ฐœ๋ฐœ์ž์„ผํ„ฐ ์„ค์ •

1. ํ…Œ์ŠคํŠธ ์•ฑ ์ƒ์„ฑ (๋น„์ฆˆ ์•ฑ ์ž๋™ ๋“ฑ๋ก๋จ)

  1. https://developers.kakao.com ์ ‘์† โ†’ ๋กœ๊ทธ์ธ
  2. [๋‚ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜] > ํ…Œ์ŠคํŠธ ์•ฑ ์ƒ์„ฑ
  3. ์ƒ์„ฑ๋œ ์•ฑ์€ ์ž๋™์œผ๋กœ ๋น„์ฆˆ ์•ฑ ์ฒ˜๋ฆฌ๋จ โ†’ ์นœ๊ตฌ ๋ฉ”์‹œ์ง€ ์ „์†ก ๊ฐ€๋Šฅ

2. ํ”Œ๋žซํผ ์„ค์ •

[๋‚ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ > ํ”Œ๋žซํผ] > Web ํ”Œ๋žซํผ ์ถ”๊ฐ€

http://localhost:8090
http://192.168.16.11:8090

3. Redirect URI ๋“ฑ๋ก

[์นด์นด์˜ค ๋กœ๊ทธ์ธ > Redirect URI ๋“ฑ๋ก]

http://192.168.16.11:8090/kakao/callback

4. ๋™์˜ ํ•ญ๋ชฉ ์„ค์ •

[์นด์นด์˜ค ๋กœ๊ทธ์ธ > ๋™์˜ ํ•ญ๋ชฉ] โ†’ ํ•ญ๋ชฉ ์ถ”๊ฐ€ ํ›„ โ€˜์ด์šฉ ์ค‘โ€™ ์„ค์ •

ํ•ญ๋ชฉ ์ด๋ฆ„ID์„ค๋ช…
์นด์นด์˜คํ†ก ๋ฉ”์‹œ์ง€ ์ „์†กtalk_message์นœ๊ตฌ์—๊ฒŒ ๋ฉ”์‹œ์ง€ ์ „์†ก
์นœ๊ตฌ ๋ชฉ๋กfriendsuuid, ๋‹‰๋„ค์ž„, ํ”„๋กœํ•„ ๋“ฑ ์กฐํšŒ

2๏ธโƒฃ ์ „์ฒด ๊ตฌํ˜„ ํ๋ฆ„ ์š”์•ฝ

๋ธŒ๋ผ์šฐ์ €์—์„œ ์ธ๊ฐ€์ฝ”๋“œ ์š”์ฒญ โ†’ ์ฝœ๋ฐฑ์—์„œ access_token ๋ฐœ๊ธ‰
 โ†’ ์นœ๊ตฌ ๋ชฉ๋ก ์š”์ฒญ โ†’ allowed_msg=true ํ•„ํ„ฐ๋ง
 โ†’ ๋ฉ”์‹œ์ง€ ํ…œํ”Œ๋ฆฟ ๊ตฌ์„ฑ โ†’ ์นœ๊ตฌ์—๊ฒŒ ๋ฉ”์‹œ์ง€ ์ „์†ก

3๏ธโƒฃ ์ „์ฒด ์ฝ”๋“œ (RestController ๊ธฐ๋ฐ˜)

@RestController
@Slf4j
@RequestMapping("/kakao")
public class KakaoFriendMessageController {

    private final String CLIENT_ID = "์นด์นด์˜ค REST API ํ‚ค";
    private final String REDIRECT_URI = "http://192.168.16.11:8090/kakao/callback";

    private KakaoTokenResponse kakaoTokenResponse;
    private KakaoFriendsResponse kakaoFriendsResponse;

    // STEP 1: ์ธ๊ฐ€ ์ฝ”๋“œ ์š”์ฒญ URL
    @GetMapping("/getCodeFriends")
    public String getCodeFriends() {
        return "redirect:https://kauth.kakao.com/oauth/authorize"
            + "?client_id=" + CLIENT_ID
            + "&redirect_uri=" + REDIRECT_URI
            + "&response_type=code"
            + "&scope=friends,talk_message"; // ์นœ๊ตฌ + ๋ฉ”์‹œ์ง€ ๊ถŒํ•œ ์š”์ฒญ
    }

    // STEP 2: access_token ๋ฐœ๊ธ‰
    @GetMapping("/callback")
    public String callback(@RequestParam("code") String code) {
        String url = "https://kauth.kakao.com/oauth/token";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("grant_type", "authorization_code");
        params.add("client_id", CLIENT_ID);
        params.add("redirect_uri", REDIRECT_URI);
        params.add("code", code);

        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(params, headers);
        RestTemplate rt = new RestTemplate();
        ResponseEntity<KakaoTokenResponse> response = rt.exchange(url, HttpMethod.POST, entity, KakaoTokenResponse.class);

        System.out.println("โœ… AccessToken ์‘๋‹ต: " + response);
        this.kakaoTokenResponse = response.getBody();

        return "AccessToken ๋ฐœ๊ธ‰ ์™„๋ฃŒ!";
    }

    // STEP 3: ์นœ๊ตฌ ๋ชฉ๋ก ์š”์ฒญ
    @GetMapping("/friends")
    public String getFriends() {
        String url = "https://kapi.kakao.com/v1/api/talk/friends";

        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "Bearer " + kakaoTokenResponse.getAccess_token());

        HttpEntity<?> entity = new HttpEntity<>(headers);
        RestTemplate rt = new RestTemplate();
        ResponseEntity<KakaoFriendsResponse> response = rt.exchange(url, HttpMethod.GET, entity, KakaoFriendsResponse.class);

        System.out.println("๐Ÿ‘ฅ ์นœ๊ตฌ ๋ชฉ๋ก ์‘๋‹ต: " + response);
        this.kakaoFriendsResponse = response.getBody();

        return "์นœ๊ตฌ ๋ชฉ๋ก ์กฐํšŒ ์™„๋ฃŒ!";
    }

    // STEP 4: ์นœ๊ตฌ์—๊ฒŒ ๋ฉ”์‹œ์ง€ ์ „์†ก
    @GetMapping("/message/friends/{message}")
    public String sendMessage(@PathVariable("message") String message) {
        String url = "https://kapi.kakao.com/v1/api/talk/friends/message/send";

        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "Bearer " + kakaoTokenResponse.getAccess_token());
        headers.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");

        // allowed_msg = true์ธ ์นœ๊ตฌ๋งŒ ์ถ”์ถœ
        JSONArray uuids = new JSONArray();
        for (Element friend : kakaoFriendsResponse.getElements()) {
            if (friend.isAllowed_msg()) {
                uuids.add(friend.getUuid());
            }
        }

        // ํ…œํ”Œ๋ฆฟ ๋ฉ”์‹œ์ง€ ๊ตฌ์„ฑ
        JSONObject template = new JSONObject();
        template.put("object_type", "text");
        template.put("text", message);
        template.put("link", new JSONObject()); // ํ•„์ˆ˜ ๊ตฌ์กฐ
        template.put("button_title", ""); // ๋ฒ„ํŠผ ์—†์Œ

        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("template_object", template.toString());
        params.add("receiver_uuids", uuids.toString());

        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(params, headers);
        RestTemplate rt = new RestTemplate();
        ResponseEntity<String> response = rt.exchange(url, HttpMethod.POST, entity, String.class);

        System.out.println("๐Ÿ“จ ๋ฉ”์‹œ์ง€ ์ „์†ก ์‘๋‹ต: " + response);
        return "์นœ๊ตฌ์—๊ฒŒ ๋ฉ”์‹œ์ง€ ์ „์†ก ์™„๋ฃŒ!";
    }

    // Kakao AccessToken DTO
    @Data
    private static class KakaoTokenResponse {
        private String access_token;
        private String token_type;
        private String refresh_token;
        private int expires_in;
        private String scope;
        private int refresh_token_expires_in;
    }

    // Kakao ์นœ๊ตฌ ๋ชฉ๋ก DTO
    @Data
    private static class Element {
        private String profile_nickname;
        private String profile_thumbnail_image;
        private boolean allowed_msg;
        private Object id;
        private String uuid;
        private boolean favorite;
    }

    @Data
    private static class KakaoFriendsResponse {
        private ArrayList<Element> elements;
        private int total_count;
        private Object after_url;
        private int favorite_count;
    }
}

โš ๏ธ ์‹ค๋ฌด & ํ…Œ์ŠคํŠธ ์œ ์˜์‚ฌํ•ญ

ํ•ญ๋ชฉ์„ค๋ช…
โœ… ํ…œํ”Œ๋ฆฟ ๋ฉ”์‹œ์ง€ ์ „์šฉ์นœ๊ตฌ์—๊ฒŒ๋Š” ์ž์œ  ํ…์ŠคํŠธ ์ „์†ก ๋ถˆ๊ฐ€ โ†’ ๋ฐ˜๋“œ์‹œ template_object ์‚ฌ์šฉ
โœ… allowed_msg ํ•„ํ„ฐ๋ง์นœ๊ตฌ ๋ชฉ๋ก ์ค‘ ๋ฉ”์‹œ์ง€ ์ˆ˜์‹  ๋™์˜ํ•œ ์‚ฌ๋žŒ๋งŒ ๊ฐ€๋Šฅ
โœ… ํŒ€์› ๊ฐ„ ํ…Œ์ŠคํŠธ ์ „์šฉ์•ฑ ํŒ€์›์œผ๋กœ ๋“ฑ๋ก๋œ ์‚ฌ๋žŒ๋ผ๋ฆฌ๋งŒ ๋ฉ”์‹œ์ง€ ์ฃผ๊ณ ๋ฐ›์„ ์ˆ˜ ์žˆ์Œ
โš ๏ธ ๋กœ๊ทธ์™€ ์‘๋‹ต ์ถœ๋ ฅ์‹ค์Šต ์ค‘์ด๋ผ๋ฉด ๋ฐ˜๋“œ์‹œ System.out.println(...)์œผ๋กœ ์‘๋‹ต ํ™•์ธํ•ด์•ผ ํ•จ

๐Ÿ”— ์ฐธ๊ณ  ๋ฌธ์„œ


profile
Here, My Pale Blue.๐ŸŒ

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