403에러가 나요. 에러 원인이 뭔지를 모르겠어요

문의 시, 사용하시는 개발환경과 디벨로퍼스 앱ID를 알려주세요.
___ID 940389

org.springframework.web.client.HttpClientErrorException: 403 Forbidden
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:63)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:531)
at com.kakao.controller.KakaoController.customAction(KakaoController.java:144)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:529)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:623)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:673)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:389)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)

로그인해서 code와 토큰까지 잘받아왔는데… 그뒤에 메시지 나에게전송이나 친구목록 불러와서 전송하려는 단계에서 막혔습니다… 에러해결원인이 뭔지를 모르겟어요 … 따로 권한 풀라는거 다 한것 같은데 아직 권한을 제가 못받아서 서버에서 거절당하는건가요? 뭐를 신청해야 하나요…?

(1) 카카오 API는 Error Response Body로 상세한 에러 내용을 전달합니다.
에러 응답을 확인하지 않고 개발하기는 매우 어려워 보입니다. 개발하시는 환경에 따른 방법으로 에러 응답 로깅하셔서 메시지 확인하시면서 개발 하시면 좋을 것같습니다.

(2) /v2/api/talk/memo/default/send API 호출 시, 아래 응답이 전달되었습니다.

{"required_scopes":["talk_message"],"code":-402,"msg":"insufficient scopes.","allowed_scopes":["profile_image","profile_nickname"],"api_type":"TALK_MEMO_DEFAULT_SEND"}

메시지를 발송하려면 카카오 로그인한 이용자에게 발신자/수신자 모두에게 동의를 받아야합니다.
아래 내용 체크리스트 참고하셔서 빠진 부분 확인해보시면 좋을 것같습니다.

친구 api와 피커, 메시지 api 사용을 위한 체크 리스트

카카오톡 API 호출 오류: 403 Forbidden
상태 코드: 403
에러 응답 바디: {“msg”:“insufficient scopes.”,“code”:-402,“api_type”:“TALK_MEMO_DEFAULT_SEND”,“required_scopes”:[“talk_message”],“allowed_scopes”:[“profile_image”,“profile_nickname”]} 이렇게 오류가 발생하는것은… 권한오류인가요? 권한설정다했는데 뭐 받아야하나요,?

질문입니다 .ㅜㅜ

에러 응답 바디: {“msg”:“insufficient scopes.”,“code”:-402,“api_type”:“TALK_MEMO_DEFAULT_SEND”,“required_scopes”:[“talk_message”],“allowed_scopes”:[“profile_image”,“profile_nickname”]} 이렇게 오류가 발생하는것은… 권한오류인가요? 권한설정다했는데 뭐 받아야하나요,?

메시지 발송하려는 계정이 940389 디벨로퍼스앱에서 카카오 로그인 시 “카카오톡 메시지 전송(talk_message)” 동의항목에 동의 하지 않았다는 의미 입니다.

카카오톡 API 호출 오류: 403 Forbidden
상태 코드: 403
에러 응답 바디: {“msg”:“insufficient scopes.”,“code”:-402,“api_type”:“TALK_MEMO_DEFAULT_SEND”,“required_scopes”:[“talk_message”],“allowed_scopes”:[“profile_image”,“profile_nickname”]}

카카오톡 메시지 전송 talk_message 선택 동의또는 이용중 동의 둘다 했는데도 같은상황입니다, ㅜ

카카오톡 메시지 전송 talk_message 선택 동의또는 이용중 동의 둘다 했는데도 같은상황입니다, ㅜ

디벨로퍼스 설정을 그렇게하시고

발신자 수신자 모두 카카오 로그인 하면서 해당 항목에 동의해야하고,
액세스 토큰 까지 받아서 확정 해야하는데 이렇게 하셨을까요?


✓ 제공하는 서비스에서 내 계정과 조회할 친구 쌍방이 로그인 및 추가항목 동의

현재는 수신자 발신자 모두 저로 해서 하고있습니다 친구한테 보내는거 말고 나에게 메시지 보내기 하고있습니다. 그래서 동의하고 엑세스 토큰은 java 컨트롤러 이용해서 받아오고있습니다. ㅜㅜ

@Controller
@RequestMapping(“/kakao/*”)
public class KakaoController {
@RequestMapping(value= “/login”)
public ModelAndView redirectToKakaoLogin() {
System.out.println(“로긴함수실행”);
return new ModelAndView(“redirect:” + “카카오계정{“REST API 키”}&response_type=code&redirect_uri=http://localhost:8080/kakao/callback”);
}

@GetMapping("/callback")
public String kakaoCallback(@RequestParam("code") String code, Model model, HttpServletRequest request) {
    System.out.println("코드 받음: " + code);
    model.addAttribute("kakaoCode", code);
    
    // 토큰 받아오기
    String accessToken = getAccessToken(code);

    // 받아온 토큰 출력
    System.out.println("Access Token: " + accessToken);
    model.addAttribute("accessToken", accessToken);
    
    HttpSession session = request.getSession();
    session.setAttribute("accessToken", accessToken);
    return "home";
}

public static String getAccessToken(String code) {
	System.out.println("토큰함수실행");
    String access_token = null;
    try {
        // API 엔드포인트 URL
        String url = "https://kauth.kakao.com/oauth/token";

        // 파라미터 설정
        String grantType = "authorization_code";
        String clientId = {"REST API 키"};
        String redirectUri = "http://localhost:8080/kakao/callback";

        // 파라미터 문자열 생성
        String params = "grant_type=" + grantType +
                        "&client_id=" + clientId +
                        "&redirect_uri=" + redirectUri +
                        "&code=" + code;

        // URL 객체 생성
        URL obj = new URL(url);

        // HttpURLConnection 객체 생성 및 설정
        HttpURLConnection con = (HttpURLConnection) obj.openConnection();
        con.setRequestMethod("POST");
        con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        con.setDoOutput(true);

        // POST 데이터 전송
        OutputStream os = con.getOutputStream();
        os.write(params.getBytes(StandardCharsets.UTF_8));
        os.flush();
        os.close();

        // 응답 받기
        int responseCode = con.getResponseCode();
        BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuilder response = new StringBuilder();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
        in.close();

        // 응답 처리
        if (responseCode == HttpURLConnection.HTTP_OK) {
            // JSON 파싱
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode jsonNode = objectMapper.readTree(response.toString());
            access_token = jsonNode.get("access_token").asText();
        } else {
            System.out.println("토큰 요청 실패. 응답 코드: " + responseCode);
        }

    } catch (Exception e) {
        e.printStackTrace();
    }

    return access_token;
}

@GetMapping("/custom-action")
public String customAction(Model model, HttpSession session) {
    String accessToken = (String) session.getAttribute("accessToken");
    if (accessToken != null) {
        System.out.println("전송");
        System.out.println(accessToken);
        model.addAttribute("accessToken", accessToken); // 모델에 accessToken 추가
        
        try {
            // 카카오톡 메시지를 보내는 API 엔드포인트 URL
            String url = "https://kapi.kakao.com/v2/api/talk/memo/default/send";

            // 메시지 내용
            String message = "안녕하세요, 카카오톡 메시지를 보내는 예제입니다!";

            // 카카오톡 API 요청에 필요한 헤더 설정
            HttpHeaders headers = new HttpHeaders();
            headers.set("Authorization", "Bearer " + accessToken);

            // 카카오톡 메시지 전송을 위한 요청 본문 설정
            HttpEntity<String> request = new HttpEntity<String>(message, headers);

            // RestTemplate 객체 생성
            RestTemplate restTemplate = new RestTemplate();

            // POST 요청으로 메시지 전송
            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, request, String.class);

            if (response.getStatusCode().is2xxSuccessful()) {
                // 메시지 전송 성공
                System.out.println("카카오톡 메시지가 성공적으로 전송되었습니다!");
            } else {
                // 메시지 전송 실패
                System.out.println("카카오톡 메시지 전송 실패. 상태 코드: " + response.getStatusCodeValue());
            }
        } catch (HttpClientErrorException e) {
            // HttpClientErrorException 발생 시, 에러 메시지 출력
            System.out.println("카카오톡 API 호출 오류: " + e.getMessage());
            System.out.println("상태 코드: " + e.getStatusCode());
            System.out.println("에러 응답 바디: " + e.getResponseBodyAsString());
        } catch (Exception e) {
            // 그 외 다른 예외 발생 시, 에러 메시지 출력
            e.printStackTrace();
        }
      } 
    else {
        // 세션에 accessToken이 없는 경우 또는 값이 비어있는 경우
        System.out.println("토큰이 없습니다.");
    }

    return "home";
}

}

그러면 되야하는데 이상하네요.

로그인한 계정으로
/v2/user/me 사용자정보조회의 id(앱유저ID) 알려주시겠어요?

2915699239 이렇게 나옵니다

해당 계정은

profile_nickname, profile_image 외에 동의된 항목이 없습니다.

확인 부탁드려요.