Skip to content

Commit

Permalink
Merge pull request #6 from mym0404/enhance-error-handling
Browse files Browse the repository at this point in the history
Enhance error handling
  • Loading branch information
mym0404 authored Apr 27, 2024
2 parents ba7c3f3 + 7b6d67e commit 59eeae8
Show file tree
Hide file tree
Showing 13 changed files with 336 additions and 76 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ please select the package link from below.
| [Core](https://rnkakao.dev/docs/intro) | ![common-android](https://img.shields.io/badge/common-2.20.1-green?style=flat-square) | ![common-ios](https://img.shields.io/badge/common-2.22.0-lightblue?style=flat-square) | [![badge](https://img.shields.io/npm/dm/@react-native-kakao/core.svg?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@react-native-kakao/core) ||
| [Share](https://rnkakao.dev/docs/share/intro) | ![share-android](https://img.shields.io/badge/share-2.20.1-green?style=flat-square) | ![share-ios](https://img.shields.io/badge/share-2.22.0-lightblue?style=flat-square) | [![badge](https://img.shields.io/npm/dm/@react-native-kakao/share.svg?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@react-native-kakao/share) ||
| [User](https://rnkakao.dev/docs/user/intro) | ![user-android](https://img.shields.io/badge/user-2.20.1-green?style=flat-square) | ![user-ios](https://img.shields.io/badge/user-2.22.0-lightblue?style=flat-square) | [![badge](https://img.shields.io/npm/dm/@react-native-kakao/user.svg?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@react-native-kakao/user) ||
| [Social](https://rnkakao.dev/docs/social/intro) | ![talk-android](https://img.shields.io/badge/talk-2.20.1-green?style=flat-square) ![friend-android](https://img.shields.io/badge/friend-2.20.1-green?style=flat-square) | ![talk-ios](https://img.shields.io/badge/talk-2.22.0-lightblue?style=flat-square) ![friend-ios](https://img.shields.io/badge/friend-2.22.0-lightblue?style=flat-square) | [![badge](https://img.shields.io/npm/dm/@react-native-kakao/friend.svg?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@react-native-kakao/friend) | 📦 |
| [Social](https://rnkakao.dev/docs/social/intro) | ![talk-android](https://img.shields.io/badge/talk-2.20.1-green?style=flat-square) ![friend-android](https://img.shields.io/badge/friend-2.20.1-green?style=flat-square) | ![talk-ios](https://img.shields.io/badge/talk-2.22.0-lightblue?style=flat-square) ![friend-ios](https://img.shields.io/badge/friend-2.22.0-lightblue?style=flat-square) | [![badge](https://img.shields.io/npm/dm/@react-native-kakao/social.svg?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@react-native-kakao/friend) | 📦 |
| [Navi](https://rnkakao.dev/docs/navi/intro) | ![navi-android](https://img.shields.io/badge/navi-2.20.1-green?style=flat-square) | ![navi-ios](https://img.shields.io/badge/navi-2.22.0-lightblue?style=flat-square) | [![badge](https://img.shields.io/npm/dm/@react-native-kakao/navi.svg?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@react-native-kakao/navi) ||
| [Cert](https://rnkakao.dev/docs/cert/intro) | | | [![badge](https://img.shields.io/npm/dm/@react-native-kakao/cert.svg?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@react-native-kakao/cert) | 📦 |
| [Talk](https://rnkakao.dev/docs/talk/intro) | | | [![badge](https://img.shields.io/npm/dm/@react-native-kakao/talk.svg?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@react-native-kakao/talk) | 📦 |
Expand Down
8 changes: 8 additions & 0 deletions docs/docs/advanced/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"label": "Advanced",
"position": 999,
"link": {
"type": "generated-index",
"description": "더 자세한 내용들에 대하여"
}
}
170 changes: 170 additions & 0 deletions docs/docs/advanced/error-handling.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
---
sidebar_position: 1
---

# 에러 핸들링

이 글에서 말하는 에러는 API호출 시에 `Promise``reject`되어 `catch`같은 로직으로 잡아낼 수 있는 에러를 의미합니다.

## API호출에 에러가 발생하는 시점의 동기화

:::info
현재 지원하는 플랫폼인 `Android``iOS` 기준으로 에러가 어떤 상황에 발생하는지는 **로직적으로 거의 차이가 없습니다.**
:::

예를 들어, Native Kakao SDK의 Android와 iOS의 유저 정보를 가져오는 `me` 함수가 있다고 하겠습니다.

Kakao SDK는 내부적으로 콜백 방식의 구현(이 패키지는 ReactiveX는 굳이 쓰지 않습니다.)을 취합니다.

대부분의 API wrapper 함수는 다음과 같은 형태를 취합니다.

```swift title='ios.swift'
@objc public func me(
_ resolve: @escaping RCTPromiseResolveBlock,
reject: @escaping RCTPromiseRejectBlock
) {
onMain {
UserApi.shared.me { user, error in
if let error {
RNCKakaoUtil.reject(reject, error)
} else if let user {
resolve([
"id": user.id as Any
//...
])
} else {
RNCKakaoUtil.reject(reject, "user not found")
}
}
}
}
```

```kotlin title='android.kt'
@ReactMethod
override fun me(promise: Promise) =
onMain {
UserApiClient.instance.me { user, error ->
if (error != null) {
promise.rejectWith(error)
} else if (user == null) {
promise.rejectWith("user not found")
} else {
promise.resolve(
argMap().apply {
putIntIfNotNull("id", user.id?.toInt())
// ...
},
)
}
}
}
```

Kakao SDK의 콜백 함수들은 형태가 동일하게 항상 첫 인자는 결과값, 두 번째 인자는 에러를 의미합니다.

만약 에러가 `null` 값이 아니라면 에러가 발생한 것입니다.

또한 이 패키지에선 첫 인자가 유의미한 값이 포함되어서 오는지도 확인합니다.

:::note
이 동작은 아마도 의미없는 행위(TILT)일 것입니다.
:::

둘 다 아니라면 성공적으로 결과값을 반환합니다.

## 에러의 형태

JavaScript로 `catch`로 전달되는 에러 객체는 플랫폼별로 조금 다릅니다.

Native Kakao SDK는 플랫폼별로 에러 코드로 소통하지만 이 패키지에선 모든 에러의 인터페이스를 통합하기보단 그대로 유의미한 값을 담아
transitivity하게 전달하는 방식을 취합니다.

하지만 대부분의 에러는 `code`로 판단 가능하며 대략 다음과 같은 구조를 가집니다.

이것은 이 패키지가 편리한 에러 핸들링을 위해 의미있는 값을 전달하기 위해 많은 관심을 기울여 개발되었기 때문입니다.

- Android

```js
{
"message": "authentication tokens don't exist.",
"code": "TokenNotFound",
"nativeStackAndroid": [],
"userInfo": {
"isAppsFailed": false,
"isInvalidTokenError": false,
"isClientFailed": true,
"fatal": true,
"isAuthFailed": false,
"isApiFailed": false,
"nativeErrorMessage": "authentication tokens don't exist."
}
}
```

- iOS

```js
{
"code": "TokenNotFound",
"message": "authentication tokens not exist.",
"nativeStackIOS": [
"0 KakaoExample 0x000000010069dc9c RCTJSErrorFromCodeMessageAndNSError + 112",
"1 KakaoExample 0x00000001009a023c ___ZZN8facebook5react15ObjCTurboModule13createPromiseERNS_3jsi7RuntimeENSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEEU13block_pointerFvU13block_pointerFvP11objc_objectEU13block_pointerFvP8NSStringSH_P7NSErrorEEENK3$_0clES4_RKNS2_5ValueEPSQ_m_block_invoke.59 + 388",
"2 KakaoExample 0x00000001002c5e20 $sSo8NSStringCSgACSo7NSErrorCSgIeyByyy_SSSgAGs5Error_pSgIegggg_TR + 380",
// ...
],
"domain": "RNCKakaoErrorDomain",
"userInfo": {
"isAuthFailed": false,
"isInvalidTokenError": false,
"isAppsFailed": false,
"isClientFailed": true,
"isApiFailed": false,
"fatal": false,
"nativeErrorMessage": "The operation couldn’t be completed. (KakaoSDKCommon.SdkError error 0.)"
}
}

```

중요한 것은 `code`, `message`이고 기타 부가적인 정보는 `userInfo`에 포함되어 있습니다.

:::warning 경고
`code`는 Native Kakao SDK의 Enum의 case 이름들을 그대로 가져온 것이기 때문에 Native Kakao SDK에서
두 케이스의 이름이 다르게 정의되었다면 다를 수 있습니다. 하지만 대부분의 경우 동일합니다.
:::

## 에러를 핸들링하는 예시

실제 어떠한 `code`값들이 존재하는지는 Android, iOS 공식 API 문서를 참고해서 확인하실 수 있습니다.

하지만 대략 다음과 같은 방법으로 대부분의 에러를 정확히 구별해 핸들링할 수 있습니다.

```tsx
selectSingleFriend({ mode: 'popup', options: {} })
.then((res) => showMessage({ message: formatJson(res) }))
.catch((e) => {
if (e && typeof e === 'object') {
if (e.code === 'TokenNotFound') {
showMessage({ type: 'warning', message: '토큰을 얻어오지 못했습니다' });
} else {
// ...
}
} else {
showMessage({ type: 'warning', message: '알 수 없는 에러입니다' });
}
})
```



## Android의 ActivityNotFoundException

Android의 `Context`가 필요한 API들은 React Native Module에서 제공하는 `getCurrentActivity()`를 통해 동작됩니다.

`ReactApplicationContext`는 theme에 대한 정보나 `startActivity`같은 행위에서 추가적인 플래그를 요구하여 예기치 못한
결과로 귀결될 수 있기 때문입니다.

따라서 `getCurrentActivity()``null`이 반환될 경우에도 에러가 반환됩니다.
25 changes: 25 additions & 0 deletions docs/docs/advanced/thread.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
sidebar_position: 2
---

# 쓰레드

## API가 동작하는 쓰레드

모든 API는 명시적으로 **Main Thread**에서 동작합니다.

사실상 Kakao Native SDK의 함수들은 콜백 방식으로 동작하고 콜백 내부는 Main Thread에서 동작합니다.

하지만 모든 로직을 Main Thread에서 실행시킴으로써 혹시 모를 에러를 방지합니다.

이 패키지에서 제공하는 함수들은 대부분 Native SDK를 포팅한 일반적인 유틸리티 함수이기 때문에 무거운 연산을 수행하지 않는다고 판단했습니다.

이 동작은 추후에 성능적인 개선이 필요하다고 논의될 시, 언제든지 수정될 수 있습니다.

:::tip
iOS의 Native Module에서 requireMainQueueSetup은 앱의 부팅 시간을 늘릴 수 있기 때문에 사용하지 않고 필요하지 않기때문에 사용하지 않습니다.
:::




15 changes: 13 additions & 2 deletions docs/docs/share/intro.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ sidebar_position: 1







import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';


# 시작하기

## 시작하기 전에
Expand All @@ -20,6 +23,14 @@ import TabItem from '@theme/TabItem';

[공식 문서](https://developers.kakao.com/docs/latest/ko/message/common)를 참고해주시기 바랍니다.

![image](https://raw.githubusercontent.com/mym0404/image-archive/master/202404271646217.webp)

:::tip
위 이미지는 카카오톡이 설치되지 않은 환경에서 실행시켰을 때 웹 브라우저도 이용해서 공유를 할 수 있는 상황을 보여주는 이미지입니다.

카카오톡이 설치된 기기인 경우 카카오톡에서 공유가 동작됩니다.
:::

## 설치하기

<Tabs>
Expand All @@ -44,4 +55,4 @@ import TabItem from '@theme/TabItem';

카카오 공유 및 카카오 메세지를 구현하기에 앞서 카카오 개발자 콘솔에서 추가로 설정해야합니다.

[공식 문서](https://developers.kakao.com/docs/latest/ko/message/prerequisite)를 참고해주세요.
[공식 문서](https://developers.kakao.com/docs/latest/ko/message/prerequisite)를 참고해주세요.
12 changes: 12 additions & 0 deletions docs/docs/social/select-talk-friends.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ API를 사용하기 위해서 카카오 로그인 및 관련 동의 항목이
사용 권한 신청에 대해서는 [공식 문서](https://developers.kakao.com/docs/latest/ko/kakaotalk-social/common#policy-request-permission)를 참고해주세요.
:::

![image](https://raw.githubusercontent.com/mym0404/image-archive/master/202404271649815.webp)

동의 항목이 이용중 동의로 설정되어있다면 해당 기능을 이용하려는 API를 호출하려 할 시에 권한 허용 안내가 위 이미지같이 노출됩니다.

<img alt={"full screen"} src={'https://raw.githubusercontent.com/mym0404/image-archive/master/202404271650614.webp'} width={600}/>

`mode='full'` 형태의 전체 화면을 채우는 피커입니다.

<img alt={"full screen"} src={'https://raw.githubusercontent.com/mym0404/image-archive/master/202404271651715.webp'} width={600}/>

`mode='popup'` 형태의 팝업 형식의 피커입니다.

## 공통 타입들

친구 피커를 설정하는 옵션 객체와 친구 객체는 다음과 같이 정의됩니다.
Expand Down
8 changes: 7 additions & 1 deletion docs/docs/user/login.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ sidebar_position: 5

## 로그인하기

카카오 로그인하기를 이용해 회원가입, 로그인, 추가 동의내역 받기, 약관 동의시키기 등을 수행할 수 있습니다.

![image](https://raw.githubusercontent.com/mym0404/image-archive/master/202404271643817.webp)

## Usage

`login()` 함수를 호출해주면 바로 실행됩니다.

```tsx
Expand Down Expand Up @@ -100,4 +106,4 @@ login()

## 카카오톡이 사용 가능한지 확인하기

`isKakaoTalkLoginAvailable()`로 확인할 수 있습니다. 반환값은 `Promise<boolean>` 입니다.
`isKakaoTalkLoginAvailable()`로 확인할 수 있습니다. 반환값은 `Promise<boolean>` 입니다.
2 changes: 1 addition & 1 deletion docs/docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const config: Config = {
{
docs: {
sidebarPath: './sidebars.ts',
editUrl: `${repoUrl}/tree/main/doc/`,
editUrl: `${repoUrl}/tree/main/docs/`,
},
theme: {
customCss: './src/css/custom.css',
Expand Down
2 changes: 1 addition & 1 deletion example/src/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default function Page() {
{'Native Kakao Sdk All In One Solution'}
</Txt>
<Box my={10} w={'100%'} h={px(1)} bg={'text'} opacity={0.8} />
<Box gap={4}>
<Box gap={4} w={'100%'} alignItems={'center'}>
<Btn
minW={px(240)}
title={'Login'}
Expand Down
14 changes: 11 additions & 3 deletions example/src/app/social.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,23 @@ export default function Page() {
<Btn
title={'Select Single Friend'}
onPress={() =>
selectSingleFriend({ mode: 'full', options: {} })
selectSingleFriend({ mode: 'popup', options: {} })
.then((res) => showMessage({ message: formatJson(res) }))
.catch((e) => showMessage({ type: 'warning', message: e.message }))
.catch((e) => {
if (e && typeof e === 'object') {
if (e.code === 'TokenNotFound') {
showMessage({ type: 'warning', message: '토큰을 얻어오지 못했습니다' });
} else {
// ...
}
}
})
}
/>
<Btn
title={'Select Multiple Friends'}
onPress={() =>
selectMultipleFriends({ mode: 'full', options: {} })
selectMultipleFriends({ mode: 'popup', options: {} })
.then((res) => showMessage({ message: formatJson(res) }))
.catch((e) => showMessage({ type: 'warning', message: e.message }))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package net.mjstudio.rnkakao.core.util

import android.util.Log
import net.mjstudio.rnkakao.core.BuildConfig

private fun debugE(message: Any?) {
if (BuildConfig.DEBUG) Log.e("RNCKakao", "⭐️" + message.toString())
}

fun debugE(vararg message: Any?) {
var str = ""
for (i in message) {
str += i.toString() + ", "
}
debugE(str)
}
Loading

0 comments on commit 59eeae8

Please sign in to comment.