Java로 REST API 호출 샘플 코드

API를 호출하기전에 SDK를 통해서 로그인을 해서 access token 을 발급 받으신 다음에 테스트 하시면 수월합니다.
access token 받는 방법은 아래 링크를 확인해 주세요.

Kakao API 사용 초기에 테스트를 위한 목적으로 작성된 코드이므로 그대로 사용하는 것을 지양합니다.

Kakao API Helper Class


import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
//import com.google.gson.Gson;

public class KakaoRestApiHelper {

    public enum HttpMethodType { POST, GET, DELETE }

    private static final String API_SERVER_HOST  = "https://kapi.kakao.com";

    private static final String USER_SIGNUP_PATH = "/v1/user/signup";
    private static final String USER_UNLINK_PATH = "/v1/user/unlink";
    private static final String USER_LOGOUT_PATH = "/v1/user/logout";
    private static final String USER_ME_PATH = "/v1/user/me";
    private static final String USER_UPDATE_PROFILE_PATH = "/v1/user/update_profile";
    private static final String USER_IDS_PATH = "/v1/user/ids";

    private static final String STORY_PROFILE_PATH = "/v1/api/story/profile";
    private static final String STORY_ISSTORYUSER_PATH = "/v1/api/story/isstoryuser";
    private static final String STORY_MYSTORIES_PATH = "/v1/api/story/mystories";
    private static final String STORY_MYSTORY_PATH = "/v1/api/story/mystory";
    private static final String STORY_DELETE_MYSTORY_PATH = "/v1/api/story/delete/mystory";
    private static final String STORY_POST_NOTE_PATH = "/v1/api/story/post/note";
    private static final String STORY_UPLOAD_MULTI_PATH = "/v1/api/story/upload/multi";
    private static final String STORY_POST_PHOTO_PATH = "/v1/api/story/post/photo";
    private static final String STORY_LINKINFO_PATH = "/v1/api/story/linkinfo";
    private static final String STORY_POST_LINK_PATH = "/v1/api/story/post/link";

    private static final String TALK_PROFILE_PATH = "/v1/api/talk/profile";

    private static final String PUSH_REGISTER_PATH = "/v1/push/register";
    private static final String PUSH_TOKENS_PATH = "/v1/push/tokens";
    private static final String PUSH_DEREGISTER_PATH = "/v1/push/deregister";
    private static final String PUSH_SEND_PATH = "/v1/push/send";

    private static final ObjectMapper JACKSON_OBJECT_MAPPER = new ObjectMapper();
    //private static final Gson GSON = new Gson();
    private static final String PROPERTIES_PARAM_NAME = "properties";

    private static final List<String> adminApiPaths = new ArrayList<String>();

    static {
        adminApiPaths.add(USER_IDS_PATH);
        adminApiPaths.add(PUSH_REGISTER_PATH);
        adminApiPaths.add(PUSH_TOKENS_PATH);
        adminApiPaths.add(PUSH_DEREGISTER_PATH);
        adminApiPaths.add(PUSH_SEND_PATH);
    }

    private String accessToken;
    private String adminKey;

    public void setAccessToken(final String accessToken) {
        this.accessToken = accessToken;
    }

    public void setAdminKey(final String adminKey) {
        this.adminKey = adminKey;
    }

    ///////////////////////////////////////////////////////////////
    // User Management
    ///////////////////////////////////////////////////////////////

    public String signup() {
        return request(HttpMethodType.POST, USER_SIGNUP_PATH);
    }

    public String signup(final Map<String, String> params) {
        return request(HttpMethodType.POST, USER_SIGNUP_PATH, PROPERTIES_PARAM_NAME + "=" + mapToJsonStr(params));
    }

    public String unlink() {
        return request(HttpMethodType.POST, USER_UNLINK_PATH);
    }

    public String logout() {
        return request(HttpMethodType.POST, USER_LOGOUT_PATH);
    }

    public String me() {
        return request(USER_ME_PATH);
    }

    public String updatProfile(final Map<String, String> params) {
        return request(HttpMethodType.POST, USER_UPDATE_PROFILE_PATH, PROPERTIES_PARAM_NAME + "=" + mapToJsonStr(params));
    }

    public String getUserIds() {
        return request(USER_IDS_PATH);
    }

    public String getUserIds(final Map<String, String> params) {
        return request(HttpMethodType.GET, USER_IDS_PATH, mapToParams(params));
    }

    ///////////////////////////////////////////////////////////////
    // Kakao Story
    ///////////////////////////////////////////////////////////////

    public String isStoryUser() {
        return request(STORY_ISSTORYUSER_PATH);
    }

    public String storyProfile() {
        return request(STORY_PROFILE_PATH);
    }

    public String postNote(final Map<String, String> params) {
        return request(HttpMethodType.POST, STORY_POST_NOTE_PATH, mapToParams(params));
    }

    public String postLink(final Map<String, String> params) {
        return request(HttpMethodType.POST, STORY_POST_LINK_PATH, mapToParams(params));
    }

    public String postPhoto(final Map<String, String> params) {
        return request(HttpMethodType.POST, STORY_POST_PHOTO_PATH, mapToParams(params));
    }

    public String getMyStory(final Map<String, String> params) {
        return request(HttpMethodType.GET, STORY_MYSTORY_PATH, mapToParams(params));
    }

    public String getMyStories() {
        return request(STORY_MYSTORIES_PATH);
    }

    public String getMyStories(final Map<String, String> params) {
        return request(HttpMethodType.GET, STORY_MYSTORIES_PATH, mapToParams(params));
    }

    public String deleteMyStory(final String id) {
        return request(HttpMethodType.DELETE, STORY_DELETE_MYSTORY_PATH, "?id=" + id);
    }

    public String deleteMyStory(final Map<String, String> params) {
        return request(HttpMethodType.DELETE, STORY_DELETE_MYSTORY_PATH, mapToParams(params));
    }

    public String getLinkInfo(String url) {
        return request(HttpMethodType.GET, STORY_LINKINFO_PATH, "?url=" + url);
    }

    public String uploadMulti(File[] files) {

        if (files.length == 0)
            return null;

        String CRLF = "\r\n";
        String TWO_HYPHENS = "--";
        String BOUNDARY = "---------------------------012345678901234567890123456";
        HttpsURLConnection conn = null;
        DataOutputStream dos = null;
        FileInputStream fis = null;

        int bytesRead, bytesAvailable, bufferSize;
        byte[] buffer;
        int maxBufferSize = 1 * 1024 * 1024;

        // Request
        try {
            URL url = new URL(API_SERVER_HOST + STORY_UPLOAD_MULTI_PATH);
            conn = (HttpsURLConnection) url.openConnection();
            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + BOUNDARY);
            conn.setRequestProperty("Authorization", "Bearer " + accessToken);
            conn.setRequestProperty("Cache-Control", "no-cache");

            dos = new DataOutputStream(conn.getOutputStream());

            for (File f : files) {
                dos.writeBytes(TWO_HYPHENS + BOUNDARY + CRLF);
                dos.writeBytes("Content-Disposition: form-data; name=\"file\";" + " filename=\"" + f.getName() + "\"" + CRLF);
                dos.writeBytes(CRLF);
                fis = new FileInputStream(f);
                bytesAvailable = fis.available();
                bufferSize = Math.min(bytesAvailable, maxBufferSize);
                buffer = new byte[bufferSize];
                bytesRead = fis.read(buffer, 0, bufferSize);
                while (bytesRead > 0) {
                    dos.write(buffer, 0, bufferSize);
                    bytesAvailable = fis.available();
                    bufferSize = Math.min(bytesAvailable, maxBufferSize);
                    bytesRead = fis.read(buffer, 0, bufferSize);
                }
                dos.writeBytes(CRLF);
            }

            // finish delimiter
            dos.writeBytes(TWO_HYPHENS + BOUNDARY + TWO_HYPHENS + CRLF);

            fis.close();
            dos.flush();
            dos.close();

        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            if (dos != null) try { dos.close(); } catch (IOException ignore) {}
            if (fis != null) try { fis.close(); } catch (IOException ignore) {} }

        // Response
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = new BufferedInputStream(conn.getInputStream());
            reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            String line;
            StringBuilder builder = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                builder.append(line).append("\n");
            }
            reader.close();
            return builder.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try { reader.close(); } catch (IOException ignore) {}
            }
            if (inputStream != null) {
                try { inputStream.close(); } catch (IOException ignore) {}
            }
            conn.disconnect();
        }

        return null;
    }

    ///////////////////////////////////////////////////////////////
    // Kakao Talk
    ///////////////////////////////////////////////////////////////

    public String talkProfile() {
        return request(TALK_PROFILE_PATH);
    }

    ///////////////////////////////////////////////////////////////
    // Push Notification
    ///////////////////////////////////////////////////////////////

    public String registerPush(final Map<String, String> params) {
        return request(HttpMethodType.POST, PUSH_REGISTER_PATH, mapToParams(params));
    }

    public String getPushTokens(final Map<String, String> params) {
        return request(HttpMethodType.GET, PUSH_TOKENS_PATH, mapToParams(params));
    }

    public String deregisterPush(final Map<String, String> params) {
        return request(HttpMethodType.POST, PUSH_DEREGISTER_PATH, mapToParams(params));
    }

    public String sendPush(final Map<String, String> params) {
        return request(HttpMethodType.POST, PUSH_SEND_PATH, mapToParams(params));
    }

    public String request(final String apiPath) {
        return request(HttpMethodType.GET, apiPath, null);
    }

    public String request(final HttpMethodType httpMethod, final String apiPath) {
        return request(httpMethod, apiPath, null);
    }

    public String request(HttpMethodType httpMethod, final String apiPath, final String params) {

        String requestUrl = API_SERVER_HOST + apiPath;
        if (httpMethod == null) {
            httpMethod = HttpMethodType.GET;
        }
        if (params != null && params.length() > 0
                && (httpMethod == HttpMethodType.GET || httpMethod == HttpMethodType.DELETE)) {
            requestUrl += params;
        }

        HttpsURLConnection conn;
        OutputStreamWriter writer = null;
        BufferedReader reader = null;
        InputStreamReader isr = null;

        try {
            final URL url = new URL(requestUrl);
            conn = (HttpsURLConnection) url.openConnection();
            conn.setRequestMethod(httpMethod.toString());

            if (adminApiPaths.contains(apiPath)) {
                conn.setRequestProperty("Authorization", "KakaoAK " + this.adminKey);
            } else {
                conn.setRequestProperty("Authorization", "Bearer " + this.accessToken);
            }

            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            conn.setRequestProperty("charset", "utf-8");

            if (params != null && params.length() > 0 && httpMethod == HttpMethodType.POST) {
                conn.setDoOutput(true);
                writer = new OutputStreamWriter(conn.getOutputStream());
                writer.write(params);
                writer.flush();
            }

            final int responseCode = conn.getResponseCode();
            System.out.println(String.format("\nSending '%s' request to URL : %s", httpMethod, requestUrl));
            System.out.println("Response Code : " + responseCode);
            if (responseCode == 200)
                isr = new InputStreamReader(conn.getInputStream());
            else
                isr = new InputStreamReader(conn.getErrorStream());

            reader = new BufferedReader(isr);
            final StringBuffer buffer = new StringBuffer();
            String line;
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            System.out.println(buffer.toString());
            return buffer.toString();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (writer != null) try { writer.close(); } catch (Exception ignore) { }
            if (reader != null) try { reader.close(); } catch (Exception ignore) { }
            if (isr != null) try { isr.close(); } catch (Exception ignore) { }
        }

        return null;
    }

    public String urlEncodeUTF8(String s) {
        try {
            return URLEncoder.encode(s, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new UnsupportedOperationException(e);
        }
    }

    public String mapToParams(Map<String, String > map) {
        StringBuilder paramBuilder = new StringBuilder();
        for (String key : map.keySet()) {
            paramBuilder.append(paramBuilder.length() > 0 ? "&" : "");
            paramBuilder.append(String.format("%s=%s", urlEncodeUTF8(key),
                    urlEncodeUTF8(map.get(key).toString())));
        }
        return paramBuilder.toString();
    }

    public String mapToJsonStr(Map<String, String > map) {
        return JACKSON_OBJECT_MAPPER.writeValueAsString(map);
        // return GSON.toJson(map);
    }
}

위의 Helper Class에 대한 Test Code


import java.io.File;
import java.util.HashMap;
import java.util.Map;



/**
 * SDK를 통해 받은 access token을 지정하시면
 * REST API를 테스트해 보실 수 있는 샘플입니다.
 * 푸시 알림 관련 API를 테스트하시려면 admin key 지정해야 합니다.
 */

public class Main {

    private static KakaoRestApiHelper apiHelper = new KakaoRestApiHelper();

    public static void main(String[] args) throws InterruptedException {
        new Main().test();
    }

    /**
     * 테스트가 필요한 api 블럭만 주석을 풀고 테스트하세요.
     */
    public void test() {
        // access token 지정
        apiHelper.setAccessToken("[YOUR APP KEY]");

        // 푸시 알림이나 유저 아이디 리스트가 필요할 때 설정 합니다. 
        // (디벨로퍼스 내에 앱설정 메뉴를 가시면 있습니다)
        apiHelper.setAdminKey("[YOUR ADMIN KEY]");

        testUserManagement();
        testKakaoStory();
        testKakaoTalk();
        testPush();
    }

    public void testUserManagement() {

        Map<String, String> paramMap;

        /*
        // 앱 사용자 정보 요청 (signup 후에 사용 가능)
        apiHelper.me();
        */

        // 앱 연결
        // apiHelper.signup();

        /*
        // 앱 연결(파라미터)
        paramMap = new HashMap<String, String>();
        paramMap.put("properties", "{\"nickname\":\"test\"}");
        apiHelper.signup(paramMap);
        */

        // 앱 탈퇴
        //apiHelper.unlink();

        // 앱 로그아웃
        //apiHelper.logout();

        /*
        // 앱 사용자 정보 업데이트
        paramMap = new HashMap<String, String>();
        paramMap.put("properties", "{\"nickname\":\"test\"}");
        apiHelper.updatProfile(paramMap);
        */

        // 앱 사용자 리스트 요청
        //apiHelper.getUserIds();

        /*
        // 앱 사용자 리스트 요청 (파라미터)
        paramMap = new HashMap<String, String>();
        paramMap.put("limit", "100");
        paramMap.put("fromId", "1");
        paramMap.put("order", "asc");
        apiHelper.getUserIds(paramMap);
        */
    }

    public void testKakaoStory() {

        final String TEST_MYSTORY_ID = "[TEST MY STORY ID]";
        final String TEST_SCRAP_URL = "https://developers.kakao.com";

        // on Linux or Mac
        final File TEST_UPLOAD_FILE1 = new File("/xxx/sample1.png");
        final File TEST_UPLOAD_FILE2 = new File("/xxx/sample2.png");

        // on windows
        // final File TEST_UPLOAD_FILE = new File("C:\\~~/sample.png");

        Map<String, String> paramMap;

        /*
        // 스토리 프로파일 요청
        apiHelper.storyProfile();
        */

        /*
        // 스토리 유저인지 확인
        apiHelper.isStoryUser();
        */

        /*
        // 복수개의 내스토리 정보 요청
        // https://dev.kakao.com/docs/restapi#복수개의-내스토리-정보-요청
        apiHelper.getMyStories();
        */

        /*
        // 복수개의 내스토리 정보 요청 (특정 아이디 부터)
        paramMap = new HashMap<String, String>();
        paramMap.put("last_id", TEST_MYSTORY_ID);
        apiHelper.getMyStories(paramMap);
        */

        /*
        // 내스토리 정보 요청
        // https://dev.kakao.com/docs/restapi#카카오스토리-내스토리-정보-요청
        paramMap = new HashMap<String, String>();
        paramMap.put("id", TEST_MYSTORY_ID);
        apiHelper.getMyStory(paramMap);
        */

        // 내스토리 삭제
        // paramMap = new HashMap<String, String>();
        // paramMap.put("id", TEST_MYSTORY_ID);
        // apiHelper.deleteMyStory(paramMap);

        // 스토리 포스팅 공통 파라미터. 필요한 것만 선택하여 사용.
        paramMap = new HashMap<String, String>();
        paramMap.put("permission", "A"); // A : 전체공개, F: 친구에게만 공개, M: 나만보기
        paramMap.put("enable_share", "false"); // 공개 기능 허용 여부
        paramMap.put("android_exec_param", "cafe_id=1234"); // 앱 이동시 추가 파라미터
        paramMap.put("ios_exec_param", "cafe_id=1234");
        paramMap.put("android_market_param", "cafe_id=1234");
        paramMap.put("ios_market_param", "cafe_id=1234");

        // 글 포스팅일 경우에는 content는 필수지만 링크/사진 포스팅일 때는 옵션.
        paramMap.put("content", "더 나은 세상을 꿈꾸고 그것을 현실로 만드는 이를 위하여 카카오에서 앱 개발 플랫폼 서비스를 시작합니다.");

        String result;

        /*
        // 글 포스팅
        result = apiHelper.postNote(paramMap);
        if (result != null && result.indexOf("code\":-") == -1) {
            String postedId = result.split("\"")[3];
            System.out.println("postedId:" + postedId);
            if (apiHelper.deleteMyStory(postedId).equals(""))
                System.out.println("deleted test my story " + postedId);
        }
        */

        /*
        // 사진 포스팅 (최대 10개까지 가능)
        String uploadedImageObj = apiHelper.uploadMulti(
                new File[]{
                        TEST_UPLOAD_FILE1,
                        TEST_UPLOAD_FILE2
                });
        if (uploadedImageObj != null) {
            System.out.println("uploaded file(s) successfully.");
            System.out.println(uploadedImageObj);
            paramMap.put("image_url_list", uploadedImageObj);
            result = apiHelper.postPhoto(paramMap);
            if (result != null && result.indexOf("code\":-") == -1) {
                String postedId = result.split("\"")[3];
                System.out.println("postedId:" + postedId);
                if (apiHelper.deleteMyStory(postedId).equals(""))
                    System.out.println("deleted test my story " + postedId);
            }

        } else {
            System.out.println("failed to upload");
        }
        */

        /*
        // 링크 포스팅
        final String linkInfoObj = apiHelper.getLinkInfo(TEST_SCRAP_URL);
        if (linkInfoObj != null) {
            paramMap.put("link_info", linkInfoObj);
            result = apiHelper.postLink(paramMap);
            if (result != null && result.indexOf("code\":-") == -1) {
                String postedId = result.split("\"")[3];
                System.out.println("postedId:" + postedId);
                if (apiHelper.deleteMyStory(postedId).equals(""))
                    System.out.println("deleted test my story " + postedId);
            }
        }
        */

    }

    public void testKakaoTalk() {

        // 카카오톡 프로필 요청
        apiHelper.talkProfile();
    }

    public void testPush() {

        Map<String, String> paramMap;

        // 파라미터 설명
        // @param uuid 사용자의 고유 ID. 1~(2^63 -1), 숫자가만 가능
        // @param push_type  gcm or apns
        // @param push_token apns(64자) or GCM으로부터 발급받은 push token
        // @param uuids 기기의 고유한 ID 리스트 (최대 100개까지 가능)

        // 푸시 알림 관련 API를 테스트하시려면 admin key 지정해야 합니다.

        /*
        // 푸시 등록
        paramMap = new HashMap<String, String>();
        paramMap.put("uuid", "10000");
        paramMap.put("push_type", "gcm");
        paramMap.put("push_token", "xxxxxxxxxx");
        paramMap.put("device_id", "");
        apiHelper.registerPush(paramMap);
        */

        /*
        // 푸시 토큰 조회
        paramMap = new HashMap<String, String>();
        paramMap.put("uuid", "10000");
        apiHelper.getPushTokens(paramMap);
        */

        /*
        // 푸시 해제
        paramMap = new HashMap<String, String>();
        paramMap.put("uuid", "10000");
        paramMap.put("push_type", "gcm");
        paramMap.put("push_token", "xxxxxxxxxx");
        apiHelper.deregisterPush(paramMap);
        */

        /*
        // 푸시 보내기
        paramMap = new HashMap<String, String>();
        paramMap.put("uuids", "[\"1\",\"2\", \"3\"]");
        apiHelper.sendPush(paramMap);
        */
    }
}

1개의 좋아요

이 토픽은 이제 고정이 풀렸습니다. 더이상 카테고리 상단에 보이지 않을 것입니다.

1개의 좋아요

Java로 REST API 호출 샘플 코드에 수정해야할 부분을 발견하였는데요. 수정하여 다른분과 공유하려면 어떻게 해야되나요?

2개의 좋아요

@hermitkim1 오 감사합니다!! 댓글로 편하게 알려주세요. 어차피 git repo등으로 관리하는게 아니고 여기 토픽 텍스트라 제가 반영할께요!

1개의 좋아요

REST API의 사용자 정보를 저장하는 부분은 아래와 같이 요청을 합니다.


Header

HTTP Method
POST

URL
https://kapi.kakao.com/v1/user/update_profile

Parameter
properties=

ex) ‘properties={“nickname”:“홍길동”,“age”:“22”}’


개발자 가이드에서 인용한 위 Parameter 예제와 같이 properties={key:value, key:value} 형태입니다.
그래서 updateProfile() 메소드에서 request() 메소드의 3번째 Parameter 부분을 아래와 같이 수정 하였고

수정 전:

public String updatProfile(final Map<String, String> params) {
    return request(HttpMethodType.POST, USER_UPDATE_PROFILE_PATH, mapToParams(params));
}

수정 후:

public String updatProfile(final Map<String, String> params) {
    	return request(HttpMethodType.POST, USER_UPDATE_PROFILE_PATH, "properties=" + mapToJsonStr(params));
}

mapToParams() 메소드 밑에 아래와 같은 mapToJsonStr() 메소드를 추가하였습니다.

public String mapToJsonStr(Map<String, String > map) {
    return new Gson().toJson(map);
}
1개의 좋아요

@hermitkim1 버그가 있었네요~ 말씀하신 부분에 대해 약간만 수정하여 반영하였습니다. 감사해요~

도움이 된것 같아 기분이 좋네요 ㅎㅎ

2개의 좋아요

우선 좋은 샘플 코드 너무 감사드립니다.

안드로이드 스튜디오에서 헬퍼 코드를 그대로 복사해서 붙혀넣기해서 컴파일하던중
return JACKSON_OBJECT_MAPPER.writeValueAsString(map); 코드에서
다음과 같이 에러가 발생했습니다.
Error:(364, 56) error: unreported exception JsonProcessingException; must be caught or declared to be thrown

Jakson~ .jar파일도 다운받아서 컴파일하도록 했습니다.
import까지 무사히 잘 컴파일 되었는데 왜 해당 함수를 호출하는 부분에서 에러가 나는건지
혹시 아시는분 계시면 답변 부탁드립니다.
감사합니다.

한글 깨질때 캐릭터셋 지정
isr = new InputStreamReader(conn.getInputStream(), “UTF-8”);

1개의 좋아요

안녕하세요!! 위 코드 샘플대로 java로 rest api 구현해서 이미지 포스팅을 테스트를 하고 있습니다. 근데 계속

Sending ‘POST’ request to URL : https://kapi.kakao.com/v1/api/story/post/photo
Response Code : 403
{“msg”:“insufficient scopes.”,“code”:-402,“api_type”:“STORY_POST_PHOTO”,“required_scopes”:[“story_publish”],“allowed_scopes”:[“profile”]}

이 메세지가 뜨더군요~ ㅠㅠ 내 애플리케이션의 사용자 관리에서 접근권한 관리항목 부분을 전부 연결시 선택을 해도 계속 뜨네요 ㅠㅠ. 보아하니 동적동의 같은게 필요한거 같은데 저 위 샘플 코드에는 동적동의 하는 부분이 없는거 같아서요…혹시 방법좀 알수 있을까요,ㅠㅠㅠㅠ 제발좀 부탁드립니다…

@shingoonk
사용자관리에서 동의항목을 설정하는건, 해당 서비스에서 필요하다고 설정하시는거구요.
실제 서비스에서 그 동의항목에 해당하는 기능을 사용하시려면 유저의 동의가 필요합니다.
지금 에러는 유저가 동의하지 않아서 발생하는 에러입니다.
유저에게 동의를 받으시려면, 추가항목 동의 받기가이드 참고하셔서 동의창을 띄우고 동의를 획득한 뒤에 스토리 포스팅 기능을 사용하실 수 있습니다.

동적동의 가이드가 Javascript 코드로 어떻게 구현이 가능한가요? 가이드를 읽어봤는데 이해가 잘 안돼서요 ㅠㅠ .
도움 부탁드립니다…혹시 javascript로 구현한 간단한 샘플 예제같은게 있나요?
아 또한가지가 다른 유저 아이디로 테스트 해보았는데, 로그인할때 젤 처음에 로그인하자마자 동의창이 자동으로 팝업으로 또 떠서 전부 다 동의하고 진행하면 사진 포스팅을 하려고 하면

    if (statusObj.status == "not_connected") {
      alert('You should log in first.');
    }

여기로 들어오더라구요…ㅠㅠ 왜그런지 잘모르겠습니다…이것도 해결방법좀 알려주세요 ㅠㅠ

아래는 제 현재 스크립트 테스트 코드에요 ㅠㅠ

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
	<script src="<c:url value="/resources/kakao.min.js" />"></script>
	<title>Home</title>
</head>
<body>
<style type='text/css'>
  /* <![CDATA[ */
#file-input-wrapper {
    display: none;
    margin-top: 50px;
}
  /* ]]> */
</style>
<a id="kakao-login-btn"></a>
<div id="file-input-wrapper">
<p>Post image to KakaoStory:</p>
<input id="file-input" type="file" multiple=""/>
</div>
<div>
<p id="post-result"></p>
<img id="post-image" src=""/>
</div>
<script type='text/javascript'>
  //<![CDATA[
// 사용할 앱의 JavaScript 키를 설정해 주세요.
Kakao.init('d023889352dcfd0871a8a28152cd6c1e');
Kakao.Auth.createLoginButton({
  container: '#kakao-login-btn',
  success: function(authObj) {
	alert(JSON.stringify(authObj))
    document.getElementById('file-input-wrapper').style.display = "block";
  },
  fail: function(err) {
    alert(JSON.stringify(err))
  }
});
document.getElementById('file-input').onchange = function (event) {
  Kakao.Auth.getStatus(function(statusObj) {
    if (statusObj.status == "not_connected") {
      alert('You should log in first.');
    } else {
      // API를 호출합니다.
      Kakao.API.request({
        url: '/v1/api/story/upload/multi',
        files: event.target.files
      }).then(function (res) {
        // 이전 API 호출이 성공한 경우 다음 API를 호출합니다.
        return Kakao.API.request({
          url: '/v1/api/story/post/photo',
          data: {
            image_url_list: res,
            content: 'Test'
          }
        });
      }).then(function (res) {
        return Kakao.API.request({
          url: '/v1/api/story/mystory',
          data: { id: res.id }
        });
      }).then(function (res) {
        document.getElementById('post-result').innerHTML = JSON.stringify(res);
        document.getElementById('post-image').src = res.media[0].original;
      }, function(err) {
        alert(JSON.stringify(err));
      });
    }
  });
};
  //]]>
</script>

</body>
</html>