페이지 이동경로
  • 문서>
  • 카카오스토리>
  • Android

카카오스토리

Android

이 문서는 Kakao SDK for Android(이하 Android SDK)를 사용한 카카오스토리 기능 구현 방법을 안내합니다.

시작하기 전에

모듈 설정

카카오스토리 API는 카카오스토리 모듈의 StoryApiClient가 제공합니다. 또한 카카오스토리 API는 카카오 로그인이 필요한 API입니다. 모듈 설정을 참고하여 build.gradle(Module) 파일에 카카오 로그인 모듈인 v2-user, 카카오스토리 모듈인 v2-story를 추가합니다.

커스텀 URL 스킴 설정

Android SDK를 통해 게시된 스토리의 링크를 통해 서비스 앱을 실행하려면 커스텀 URL 스킴 설정이 필요합니다. ${Project} > android > app > src > AndroidManifest.xml 파일에 아래 설정을 추가합니다. Android 12(API 31) 이상을 타깃으로 하는 앱인 경우, exported 요소를 반드시 "true"로 선언해야 합니다.

<activity 
    android:name=".{YOUR_ACTIVITY_NAME}"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        
        <!-- "kakao${YOUR_NATIVE_APP_KEY}://kakaostory" 형식의 앱 실행 스킴을 설정하는데 사용 -->
        <data android:host="kakaostory"
                android:scheme="kakao${YOUR_NATIVE_APP_KEY}" />
    </intent-filter>
</activity>

사용자 확인하기

기본 정보
권한 사전 설정 카카오 로그인 사용자 동의 레퍼런스
- 플랫폼 등록
카카오 로그인 활성화
동의 항목
필요 - Android SDK
isStoryUser()
ReactiveX Android SDK
isStoryUser()

카카오스토리 미사용으로 인한 에러를 방지하기 위해, 현재 로그인한 사용자가 카카오스토리를 사용하고 있는지 확인합니다.

StoryApiClientisStoryUser()로 현재 로그인한 사용자의 카카오스토리 사용 여부를 확인할 수 있습니다. 사용자의 카카오스토리 이용 여부는 Boolean 값으로 사용(true) 또는 미사용(false)입니다.

Kotlin
RxKotlin
// 카카오스토리 사용자 확인하기
StoryApiClient.instance.isStoryUser { isStoryUser, error ->
    if (error != null) {
        Log.e(TAG, "카카오스토리 사용자 확인 실패", error)
    }
    else {
        Log.i(TAG, "카카오스토리 가입 여부: $isStoryUser")
    }
}
var disposables = CompositeDisposable()

// 카카오스토리 사용자 확인하기
StoryApiClient.rx.isStoryUser()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ isStoryUser ->
        Log.i(TAG, "카카오스토리 가입 여부: $isStoryUser")
    }, { error ->
        Log.e(TAG, "카카스토리 사용자 확인 실패", error)
    })
    .addTo(disposables)

프로필 가져오기

기본 정보
권한 사전 설정 카카오 로그인 사용자 동의 레퍼런스
- 플랫폼 등록
카카오 로그인 활성화
동의 항목
필요 필요:
프로필 정보(닉네임/프로필 사진)
닉네임
프로필 사진
카카오스토리 프로필 URL
공통
StoryProfile
Android SDK
profile()
ReactiveX Android SDK
profile()

현재 로그인한 사용자의 카카오스토리 프로필을 불러옵니다. 사용자는 카카오계정, 카카오톡, 카카오스토리에 각각 프로필을 설정할 수 있으므로, 카카오스토리 프로필 가져오기 API로 불러온 프로필은 사용자 정보카카오톡 프로필 가져오기 API로 조회한 정보와 별개입니다.

사용자 카카오스토리 프로필을 불러오려면 StoryApiClientprofile() API를 사용합니다.

Kotlin
RxKotlin
// 카카오스토리 프로필 가져오기
StoryApiClient.instance.profile { profile, error ->
    if (error != null) {
        Log.e(TAG, "카카오스토리 프로필 가져오기 실패", error)
    }
    else if (profile != null) {
        Log.i(TAG, "카카오스토리 프로필 가져오기 성공" +
                "\n닉네임: ${profile.nickname}" +
                "\n프로필사진: ${profile.thumbnailUrl}" +
                "\n생일: ${profile.birthday}")
    }
}
var disposables = CompositeDisposable()

// 카카오스토리 프로필 가져오기
StoryApiClient.rx.profile()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ profile ->
        Log.i(TAG, "카카오스토리 프로필 가져오기 성공" +
                "\n닉네임: ${profile.nickname}" +
                "\n프로필사진: ${profile.thumbnailUrl}" +
                "\n생일: ${profile.birthday}")
    }, { error ->
        Log.e(TAG, "카카오스토리 프로필 가져오기 실패", error)
    })
    .addTo(disposables)

요청 성공 시 사용자의 카카오스토리 프로필을 StoryProfile 객체로 받습니다. 카카오스토리 프로필 정보의 구성 및 자료형은 REST API 가이드 및 레퍼런스에서 확인할 수 있습니다.

스토리 쓰기

현재 로그인한 사용자의 카카오스토리에 새로운 스토리를 작성합니다. 스토리 쓰기는 글(Post), 사진(Photo)과 글, 링크(Link) 세 가지 종류의 API를 제공합니다.

스토리 종류

타입 설명 메서드
글(Note) 텍스트로 구성된 스토리 postNote()
사진(Photo) 텍스트와 사진으로 구성된 스토리 postPhoto()
링크(Link) 텍스트와 스크랩할 웹 페이지에서 얻은 정보로 구성된 스토리 postLink()

스토리마다 포함되어야 할 구성 요소가 다르지만, 다음 파라미터는 공통적으로 설정 가능합니다. 다음 파라미터를 통해 스토리에 포함할 내용 및 링크 정보를 설정할 수 있습니다.

공통 파라미터

이름 타입 설명 필수
permission Permission 스토리 공개 범위
PUBLIC(전체 공개), FRIEND(친구 공개), ONLY_ME(나만 보기)
기본 값은 PUBLIC(전체 공개)
X
enableShare Boolean 친구 공개 스토리인 경우 공유 설정
(기본값: false)
X
androidExecParam Map<String, String> 스토리의 [해당 앱으로 이동] 버튼을 눌렀을 때 Android 앱 실행 URL에 붙일 파라미터 X
iosExecParam Map<String, String> 스토리의 [해당 앱으로 이동] 버튼을 눌렀을 때 iOS 앱 실행 URL에 붙일 파라미터 X
androidMarketParam Map<String, String> 스토리에서 오픈마켓으로 이동할 때 실행 URL에 붙일 파라미터 X
iosMarketParam Map<String, String> 스토리에서 앱스토어로 이동할 때 실행 URL에 붙일 파라미터 X

요청 성공 시 콜백 함수로 전달되는 StoryPostResult 객체를 통해 작성된 스토리 게시물의 ID를 확인할 수 있습니다.

StoryPostResult
이름 타입 설명
id String 작성된 스토리 ID

종류별 스토리 쓰기 구현 방법과 추가 파라미터 정보는 아래에서 확인할 수 있습니다.

글 스토리 쓰기

기본 정보
권한 사전 설정 카카오 로그인 사용자 동의 레퍼런스
- 플랫폼 등록
카카오 로그인 활성화
동의 항목
필요 필요:
카카오스토리 글 작성
(story_publish)
Android SDK
postNote()
ReactiveX Android SDK
postNote()

postNote() API는 사진이나 웹 페이지 URL 등 다른 요소 없이 글로만 구성된 스토리를 게시할 때 사용합니다. 글 스토리 쓰기 시, 스토리 내용 텍스트를 담은 content 파라미터를 필수 전달해야 합니다.

파라미터
이름 타입 설명 필수
content String 스토리에 들어갈 글, 2048자(char) 미만으로 제한 O

다음은 글 스토리 쓰기 예제입니다. 다른 파라미터는 설정하지 않고 스토리 내용만을 content 파라미터로 전달합니다.

예제
Kotlin
RxKotlin
// 글 스토리 쓰기
val content = "Posting note from Kakao SDK Sample."

StoryApiClient.instance.postNote(content) { storyPostResult, error ->
    if (error != null) {
        Log.e(TAG, "스토리 쓰기 실패", error)
    }
    else if (storyPostResult != null) {
        Log.i(TAG, "스토리 쓰기 성공 [${storyPostResult.id}]")
    }
}
var disposables = CompositeDisposable()

// 글 스토리 쓰기
val content = "Posting note from Kakao SDK Sample."

StoryApiClient.rx.postNote(content)
    .retryWhen(
        // InsufficientScope 에러에 대해 추가 동의 후 재요청
        RxAuthOperations.instance.incrementalAuthorizationRequired(context)
    )
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ storyId ->
        Log.i(TAG, "스토리 쓰기 성공 [${storyId}]")
    }, { error ->
        Log.e(TAG, "스토리 쓰기 실패", error)
    })
    .addTo(disposables)

사진 스토리 쓰기

기본 정보
권한 사전 설정 카카오 로그인 사용자 동의 레퍼런스
- 플랫폼 등록
카카오 로그인 활성화
동의 항목
필요 필요:
카카오스토리 글 작성
(story_publish)
Android SDK
postPhoto()
upload()
ReactiveX Android SDK
postPhoto()
upload()

postPhoto() API는 사진과 글로 구성된 스토리를 게시할 때 사용합니다. 사진 스토리 쓰기 시, 스토리에 첨부할 이미지 정보를 담은 images 파라미터를 필수 전달해야 합니다. 스토리 내용 텍스트를 담은 content는 선택 파라미터입니다.

images 파라미터에는 이미지 파일 URL 문자열(String)을 리스트(List) 형식으로 전달해야 합니다. 만약 기기에 저장된 이미지 파일을 사용하고 싶다면 upload() API를 사용해 카카오 서버에 업로드합니다. 요청 시 업로드할 파일은 리스트 형식으로 전달합니다. 업로드된 이미지의 URL을 리스트 형식으로 받아 postPhoto() 파라미터로 사용할 수 있습니다.

파라미터
이름 타입 설명 필수
content String 스토리에 들어갈 글, 2048자(char) 미만으로 제한 X
images List<String> 스토리에 들어갈 이미지들의 URL O
예제

다음은 이미지 업로드 후 사진 스토리 쓰기를 요청하는 예제입니다.

Kotlin
RxKotlin
// 사진 스토리 쓰기

// 업로드할 사진 파일
// 이 예제에서는 프로젝트 리소스로 추가한 이미지 파일을 사용했습니다.
// 갤러리 등 서비스에 필요한 사진 파일을 준비합니다.
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.sample1)
val file = File(context.cacheDir, "sample1.png")
val stream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)
stream.close()

// 사진 업로드
StoryApiClient.instance.upload(listOf(file)) { uploadedPaths, error ->
    if (error != null) {
        Log.e(TAG, "사진 업로드 실패", error)
    } else {
        Log.d(TAG, "사진 업로드 성공 $uploadedPaths")

        // 사진 스토리 쓰기
        val images = uploadedPaths!!
        val content = "Posting photo from Kakao SDK Sample."

        StoryApiClient.instance.postPhoto(images, content) { storyPostResult, error ->
            if (error != null) {
                Log.e(TAG, "스토리 쓰기 실패", error)
            }
            else if (storyPostResult != null) {
                Log.i(TAG, "스토리 쓰기 성공 [${storyPostResult.id}]")
            }
        }
    }
}
var disposables = CompositeDisposable()

// 사진 스토리 쓰기

// 업로드할 사진 파일
// 이 예제에서는 프로젝트 리소스로 추가한 이미지 파일을 사용했습니다.
// 갤러리 등 서비스에 필요한 사진 파일을 준비합니다.
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.sample1)
val file = File(context.cacheDir, "sample1.png")
val stream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)
stream.close()

// 사진 업로드
StoryApiClient.rx.upload(listOf(file))
    .flatMap {

        // 사진 스토리 쓰기
        val images = it
        val content = "Posting photo from Kakao SDK Sample."

        StoryApiClient.rx.postPhoto(images, content)
    }
    .retryWhen(
        // InsufficientScope 에러에 대해 추가 동의 후 재요청
        RxAuthOperations.instance.incrementalAuthorizationRequired(context)
    )
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ storyId ->
        Log.i(TAG, "스토리 쓰기 성공 [${storyId}]")
    }, { error ->
        Log.e(TAG, "스토리 쓰기 실패", error)
    })
    .addTo(disposables)

링크 스토리 쓰기

기본 정보
권한 사전 설정 카카오 로그인 사용자 동의 레퍼런스
- 플랫폼 등록
카카오 로그인 활성화
동의 항목
필요 필요:
카카오스토리 글 작성
(story_publish)
공통
LinkInfo
Android SDK
postLink()
linkInfo()
ReactiveX Android SDK
postLink()
linkInfo()

postLink() API는 웹 페이지를 공유하는 스토리를 게시할 때 사용합니다. 링크 스토리 쓰기 시, 공유할 웹 페이지 정보를 담은 linkInfo 파라미터를 필수 전달해야 합니다. 스토리 내용 텍스트를 담은 content는 선택 파라미터입니다.

파라미터 값으로 전달할 LinkInfo객체는 linkInfo() API를 사용하여 구합니다. linkInfo()에 공유할 웹 페이지 URL을 전달하면, 카카오 SDK가 오픈 그래프 프로토콜(Open Graph Protocol)에 따라 웹 페이지 정보를 스크랩하여 LinkInfo 객체로 반환합니다.

따라서 링크 스토리 쓰기 요청 시에는 먼저 linkInfo()를 호출하여 공유할 웹 페이지 정보를 담은 LinkInfo 객체를 구하고, 이 요청이 성공하면 이어서 postLink()를 호출하도록 구현하는 것을 권장합니다.

파라미터
이름 타입 설명 필수
content String 스토리에 들어갈 글, 2048자(char) 미만으로 제한 X
linkInfo LinkInfo 웹 페이지 스크랩 정보 O

다음은 웹 페이지 스크랩 후 링크 스토리 쓰기를 요청하는 예제입니다.

Kotlin
RxKotlin
// 링크 스토리 쓰기

// 지정된 URL로 링크 만들기
StoryApiClient.instance.linkInfo("https://www.kakaocorp.com") { linkInfoResult, error ->
    if (error != null) {
        Log.e(TAG, "링크 만들기 실패", error)
    } else {
        Log.d(TAG, "링크 만들기 성공 $linkInfoResult")

        // 링크 스토리 쓰기
        val linkInfo = linkInfoResult!!
        val content = "Posting link from Kakao SDK Sample."

        StoryApiClient.instance.postLink(linkInfo, content) { storyPostResult, error ->
            if (error != null) {
                Log.e(TAG, "스토리 쓰기 실패", error)
            }
            else if (storyPostResult != null) {
                Log.i(TAG, "스토리 쓰기 성공 [${storyPostResult.id}]")
            }
        }
    }
}
var disposables = CompositeDisposable()

// 링크 스토리 쓰기

// 지정된 URL로 링크 만들기
StoryApiClient.rx.linkInfo("https://www.kakaocorp.com")
    .flatMap {
        // 링크 스토리 쓰기
        val linkInfo = it
        val content = "Posting link from Kakao SDK Sample."
        StoryApiClient.rx.postLink(linkInfo, content)
    }
    .retryWhen(
        // InsufficientScope 에러에 대해 추가 동의 후 재요청
        RxAuthOperations.instance.incrementalAuthorizationRequired(context)
    )
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ storyId ->
        Log.i(TAG, "스토리 쓰기 성공 [${storyId}]")
    }, { error ->
        Log.e(TAG, "스토리 쓰기 실패", error)
    })
    .addTo(disposables)

내 스토리 가져오기

현재 로그인한 사용자의 카카오스토리에서 스토리 목록을 불러옵니다.

다음 두 가지 방법으로 스토리를 가져올 수 있습니다.

  • 여러 개의 스토리 가져오기를 통해 최근 스토리 정보 받아 특정 스토리를 스토리 ID로 지정하여 스토리 정보 받기
  • 여러 개의 스토리 가져오기를 통해 최근 스토리 정보 받기

스토리 가져오기 요청 시 하나의 스토리를 요청하는지, 여러 개의 스토리를 요청하는지에 따라 전달해야 할 파라미터가 다릅니다.

여러 개의 스토리 가져오기

기본 정보
권한 사전 설정 카카오 로그인 사용자 동의 레퍼런스
- 플랫폼 등록
카카오 로그인 활성화
동의 항목
필요 필요:
카카오스토리 글 목록
(story_read)
공통
Story
Android SDK
stories()
ReactiveX Android SDK
stories()

현재 로그인한 사용자의 카카오스토리에서 스토리 목록을 불러옵니다. stories()로 요청하며, 요청 성공 시 각 스토리의 구성 요소를 담은 Story 객체의 리스트를 반환합니다.

Story 객체는 스토리 고유 번호인 ID를 포함합니다. stories() 응답은 각 스토리의 댓글이나 반응과 같은 상세 정보를 포함하지 않으므로, 각 스토리의 상세 정보를 확인하려면 해당 스토리 ID 값을 파라미터로 전달하여 stories()를 호출해야 합니다.

예제
Kotlin
RxKotlin
// 여러 개의 스토리 가져오기
StoryApiClient.instance.stories { stories, error ->
    if (error != null) {
        Log.e(TAG, "스토리 가져오기 실패", error)
    }
    else {
        Log.i(TAG, "스토리 가져오기 성공 \n$stories")
    }
}
var disposables = CompositeDisposable()

// 여러 개의 스토리 가져오기
StoryApiClient.rx.stories()
    .retryWhen(
        // InsufficientScope 에러에 대해 추가 동의 후 재요청
        RxAuthOperations.instance.incrementalAuthorizationRequired(context)
    )
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ stories ->
        Log.i(TAG, "스토리 가져오기 성공 \n$stories")
    }, { error ->
        Log.e(TAG, "스토리 가져오기 실패", error)
    }).addTo(disposables)

하나의 스토리 가져오기

기본 정보
권한 사전 설정 카카오 로그인 사용자 동의 레퍼런스
- 플랫폼 등록
카카오 로그인 활성화
동의 항목
필요 필요:
카카오스토리 글 목록
(story_read)
공통
Story
Android SDK
story()
ReactiveX Android SDK
story()

하나의 스토리를 지정해 요청합니다.

story()는 현재 로그인한 사용자의 카카오스토리에서 지정한 스토리의 상세 정보를 가져옵니다. 스토리 id를 필수 전달해야 합니다. 특정 스토리 ID를 지정하여 요청하면 해당 스토리의 댓글을 포함한 상세 정보를 받을 수 있습니다.

파라미터
이름 타입 설명 필수
id String 상세 정보를 가져올 스토리 ID O
예제
Kotlin
RxKotlin
// 하나의 스토리 가져오기

// 이 예제에서는 받고자 하는 스토리 아이디를 얻기 위해 전체 목록을 조회하고 첫번째 스토리 아이디를 사용했습니다.
StoryApiClient.instance.stories { stories, error ->
    if (error != null) {
        Log.e(TAG, "스토리 가져오기 실패", error)
    }
    else if (stories != null) {
        if (stories.isEmpty()) {
            Log.e(TAG, "내 스토리가 하나도 없어요 ㅠㅠ")
        }
        else {

            // 정보를 원하는 스토리 아이디
            val storyId = stories.first().id

            // 하나의 스토리 가져오기
            StoryApiClient.instance.story(storyId) { story, error ->
                if (error != null) {
                    Log.e(TAG, "스토리 가져오기 실패", error)
                }
                else if (story != null) {
                    Log.i(TAG, "스토리 가져오기 성공" +
                            "\n아이디: ${story.id}" +
                            "\n미디어 형식: ${story.mediaType}" +
                            "\n작성일자: ${story.createdAt}" +
                            "\n내용: ${story.content}")
                }
            }
        }
    }
var disposables = CompositeDisposable()

// 하나의 스토리 가져오기

// 이 예제에서는 받고자 하는 스토리 아이디를 얻기 위해 전체 목록을 조회하고 첫번째 스토리 아이디를 사용했습니다.
StoryApiClient.rx.stories()
    .retryWhen(
        // InsufficientScope 에러에 대해 추가 동의 후 재요청
        RxAuthOperations.instance.incrementalAuthorizationRequired(context)
    )
    .flatMap { stories ->
        if (stories.isNotEmpty()) {

            // 정보를 원하는 스토리 아이디
            val storyId = stories.first().id

            StoryApiClient.rx.story(storyId)
        }
        else {
            Single.error(ClientError(ClientErrorCause.IllegalState, "No stories"))
        }
    }
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ story ->
        Log.i(TAG, "스토리 가져오기 성공" +
                "\n아이디: ${story.id}" +
                "\n미디어 형식: ${story.mediaType}" +
                "\n작성일자: ${story.createdAt}" +
                "\n내용: ${story.content}")
    }, { error ->
        Log.e(TAG, "스토리 가져오기 실패", error)
    }).addTo(disposables)

스토리 구성 요소는 REST API 가이드 및 레퍼런스에서 확인할 수 있습니다.

내 스토리 삭제하기

기본 정보
권한 사전 설정 카카오 로그인 사용자 동의 레퍼런스
- 플랫폼 등록
카카오 로그인 활성화
동의 항목
필요 필요:
카카오스토리 글 목록
(story_read)
Android SDK
delete()
ReactiveX Android SDK
delete()

현재 로그인한 사용자의 카카오스토리에서 지정한 ID에 해당하는 스토리를 삭제합니다. 삭제할 스토리 게시물의 ID를 알고 있어야 요청 가능하므로, 내 스토리 가져오기 또는 스토리 쓰기 요청을 통해 해당 스토리의 ID를 확인하거나, 참고할 수 있는 스토리 ID 값이 있어야 합니다.

파라미터
이름 타입 설명 필수
id String 삭제할 스토리 ID O
예제

다음은 현재 로그인한 사용자의 모든 스토리를 불러온 뒤, 그중 첫 번째 스토리를 삭제하는 예제입니다.

Kotlin
RxKotlin
// 내 스토리 삭제하기

// 이 예제에서는 삭제고자 하는 스토리 아이디를 얻기 위해 전체 목록을 조회하고 첫번째 스토리 아이디를 사용했습니다.
StoryApiClient.instance.stories { stories, error ->
    if (error != null) {
        Log.e(TAG, "스토리 가져오기 실패", error)
    }
    else if (stories != null) {
        if (stories.isEmpty()) {
            Log.e(TAG, "내 스토리가 하나도 없어요 ㅠㅠ")
        }
        else {

            // 삭제를 원하는 스토리 아이디
            val storyId = stories.first().id

            // 스토리 삭제하기
            StoryApiClient.instance.delete(storyId) { error ->
                if (error != null) {
                    Log.e(TAG, "스토리 삭제 실패", error)
                } else {
                    Log.i(TAG, "스토리 삭제 성공 [$storyId]")
                }
            }
        }
    }
}
var disposables = CompositeDisposable()

// 내 스토리 삭제하기

// 이 예제에서는 삭제고자 하는 스토리 아이디를 얻기 위해 전체 목록을 조회하고 첫번째 스토리 아이디를 사용했습니다.
StoryApiClient.rx.stories()
    .retryWhen(
        // InsufficientScope 에러에 대해 추가 동의 후 재요청
        RxAuthOperations.instance.incrementalAuthorizationRequired(context)
    )
    .flatMapCompletable { stories ->
        if (stories.isNotEmpty()) {

            // 삭제를 원하는 스토리 아이디
            val storyId = stories.first().id

            StoryApiClient.rx.delete(storyId)
        }
        else {
            Completable.error(ClientError(ClientErrorCause.IllegalState, "내 스토리가 하나도 없어요 ㅠㅠ"))
        }
    }
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({
        Log.i(TAG, "스토리 삭제 성공")
    }, { error ->
        Log.e(TAG, "스토리 삭제 실패", error)
    }).addTo(disposables)

더 보기