npm package: @psyrenpark/api
Description: psyrenpark api
Author: psyrenpark
Latest version: 3.0.1

## api

### 제작이유

amplify를 활용한 인증/미인증/cdn api를 백엔드/클라이언트에서 공유하기위함

- rest api 지원
<!-- - graphql [일부 테스트중] // appsync 사용 -->

- react 지원
- react-native 지원
- vue.js 지원
- gatsby.JS 지원
- next.js(테스트중)

### 업데이트 내용

- v3.0.1

라이브러리 최신화
제거된 기능 제거

- v1.0.65

loadingCallback의 null 허용

로딩 처리는 react-query 사용 권장

- v1.0.58

아래 부분 참고

- api default 옵션 관련 작업 로딩
- api 재시도 기능 추가

- v1.0.51

```
appsync 에러 처리

  try {

    var textSample = {
      type: "TEXT", //"IMAGE" , "SYSTEM", "MOVIE"
      text: newMessages[0].text,
    };

    var data = await Api.graphql(
      {
        query: mutations.createChatMessage,
        variables: {
          input: {
            chat_room_no: chat_room_no,
            chat_message_type_code_id: "DEFAULT",
            chat_message_content: JSON.stringify(textSample),
          },
        },
      },
      {
        // fetchPolicy: "cache-only",
      },
      (flag) => {
        console.log(flag);
      }
    );

  } catch (error) {
    console.log("log -> ------------------------------");
    console.log("log -> ~ testFunction ~ error", { error });
    console.log("log -> ~ testFunction ~ error", error.response);
    console.log("log -> ------------------------------");
  }

  /*
   log -> ~ testFunction ~ error [
      {
          "path": [
            "data"
          ],
          "data": null,
          "errorType": "INCLUDE_CHAT_BAN_WORD",
          "errorInfo": [
            {
                "errorType": "INCLUDE_CHAT_BAN_WORD",
                "message": "금칙어가 포함되어 있습니다."
            }
          ],
          "locations": [
            {
                "line": 2,
                "column": 3,
                "sourceName": null
            }
          ],
          "message": "금칙어가 포함되어 있습니다."
      }
    ]
  */

```

- v1.0.46

```
appsync 사용 예시
/src/graphql/subscriptions.js 예시
구독 기능 설명 추가
```

---

### 필요 파일

```
각 프로젝트의 개발 환경에 맞는 aws-exports.js 요청

React- native는
<project root>/aws-exports.js
web/cms은
<project root>/src/aws-exports.js
```

### 설정 및 설치

```properties
yarn add @psyrenpark/auth
yarn add @psyrenpark/api
yarn add @psyrenpark/storage
yarn add amazon-cognito-identity-js
yarn add aws-amplify

// react-native 추가 설치
yarn add @react-native-community/netinfo
```

### 최신 버전 적용 방법

새버전 공지시 npm에 맞는것 재설치

```properties
yarn add @psyrenpark/auth
yarn add @psyrenpark/api
yarn add @psyrenpark/storage
```

- 이후 최신버전으로 변경되었는지 확인할것.

---

### 루트에서 세팅

```javascript
//--------------------------------------------------
// 각 프로젝트 루트
import { Auth } from "@psyrenpark/auth";
import { Api } from "@psyrenpark/api";
import { Storage } from "@psyrenpark/storage";
import awsmobile from "./aws-exports";
Auth.setConfigure(awsmobile);
Api.setConfigure(awsmobile);
Storage.setConfigure(awsmobile);

//--------------------------------------------------
// 혹 import가 지원 되지 않는 javascript 버전에서 사용시

// aws-exports
// export default awsmobile;
module.exports = awsmobile; // 로 변경

// 각 프로젝트 루트
const { apiObject } = require("./api");
const { Api } = require("@psyrenpark/api");
const { Auth, AuthType } = require("@psyrenpark/auth");
const { Storage } = require("@psyrenpark/storage");
const awsmobile = require("./aws-exports");

Auth.setConfigure(awsmobile);
Api.setConfigure(awsmobile);
Storage.setConfigure(awsmobile);
```

### 로딩 콜백 함수 샘플

로딩에 대한 불편함을 해결하기위해 콜백형식의 함수를 주입가능하도록 되어있음

```javascript
// 아래 auth 함수가 실행될때 로딩함수를 넣으면
// 시작시 isLoading => true
// 종료시 isLoading => false
// 로 주입한 함수가 호출됨
const loadingFunction = (isLoading) => {
  dispatch({ type: "SET_IS_LOADING", payload: isLoading });
  // or
  setIsLoading(isLoading);
};
```

---

### apiObject

- api들 모아 놓은 object
- 여기에 모든 api들을 함수화 한다.
- 로그인이 필요없이 사용가능한 noneauth 버전
- 일반적인 로그인 필요 api 인 api 버전
- 속도향상을 위해 캐싱된 api은 cdn 버전
- 서버 개발자는 테스트를 위해 이를 작성하여 테스트후 공유한다.
- 백엔드와 별도의 테스트가 필요하다면 아래 [테스트 필요시] 참고할것

```javascript
//--------------------------------------
// api/index.js
import { Api } from "@psyrenpark/api";

const projectName = "ll"; // 각 프로젝트 단축명
const cognitoType = "admin"; //  코그티노 종류 // admin, cust
const projectEnv = "prod"; // 각 프로젝트 환경 // dev, test, prod

const v1Api = `${projectName}${cognitoType}-${projectEnv}-api-v1`;
const v1Cdn = `${projectName}${cognitoType}-${projectEnv}-cdn-v1`;
const v1NoneAuth = `${projectName}${cognitoType}-${projectEnv}-noneauth-v1`;
const v1Cms = `${projectName}${cognitoType}-${projectEnv}-cms-v1`;

export const apiObject = {
  //------------------------------------------
  // 인증 없는 api
  /**
   * [] cdn 테스트
   * @param {string} langCode               - 예시 언어코드
   * @param {Function} LoadingCallback      - 로딩 콜백
   */
  getTestNoneauth: (
    {
      langCode, //
      // 필요에 맞게 파라미터를 넣는다.
      // ...
    },
    loadingFunction,
  ) => {
    var apiName = v1Noneauth; // 인증 유, 인증 무 api 구분은 이부분이 다르다.
    var path = "/test_test_test"; // test_test_test는 무조건 테스트 api로써 반드시 작동한다.
    var myInit = {
      headers: {}, // OPTIONAL
      // body: {  // post나 put일경우 사용한다.
      //
      // },
      queryStringParameters: {
        langCode: langCode,
      },
      // response: true,  // axios 원형 response 필요할 경우 ture로 설정한다.
    };
    // get, post, put, del 상황에 맞게 사용한다
    return Api.get(apiName, path, myInit, loadingFunction);
  },

  //------------------------------------------
  // 인증 있는 api
  getTestApi: (
    {
      langCode,
      // ...
      // 필요에 맞게 파라미터를 넣는다.
    },
    loadingFunction,
  ) => {
    var apiName = v1Api; // 인증 유, 인증 무 api 구분은 이부분이 다르다.
    var path = "/test_test_test";
    var myInit = {
      headers: {}, // OPTIONAL
      // body: {  // post나 put일경우 사용한다.
      //
      // },
      queryStringParameters: {
        langCode: langCode,
      },
      // response: true,  // axios 원형 response 필요할 경우 ture로 설정한다.
    };
    // get, post, put, del 상황에 맞게 사용한다
    return Api.get(apiName, path, myInit, loadingFunction);
  },

  //------------------------------------------
  // 개발계에 테스트시 (디폴트 계정으로만 테스트 가능)
  getTestCdn: (
    {
      langCode,
      // ...
      // 필요에 맞게 파라미터를 넣는다.
    },
    loadingFunction,
  ) => {
    var apiName = v1Cdn; // 인증 유, 인증 무 api 구분은 이부분이 다르다.
    var path = "/test_test_test";
    var myInit = {
      headers: {}, // OPTIONAL
      // body: {  // post나 put일경우 사용한다.
      //
      // },
      queryStringParameters: {
        langCode: langCode,
      },
      // response: true,  // axios 원형 response 필요할 경우 ture로 설정한다.
    };

    // 테스트 필요시
    // 테스트가 필요할경우 로딩콜백 뒤에 {url, port}를 추가한다. // 백엔드 개발자에게 테스트 요청
    // get, post, put, del 상황에 맞게 사용한다
    return Api.get(apiName, path, myInit, LoadingCallback, {
      url: "http://18.177.73.12",
      port: 3006,
    });

    // return Api.get(apiName, path, myInit, loadingFunction);
  },
};
```

### 실제 리액트에서 사용 예시

- jsx에서 버튼클릭등 에서 사용

```javascript
//--------------------------------------
// api/index.js
//--------------------------------------
// api 호출
// 특정 버튼 클릭등 에서 아래 함수 호출
import React from "react";
import { apiObject } from "../../api";

export const TestComponent = (props) => {
  const testApiFunction = async () => {
    try {
      var userData = await apiObject.getTestCdn(
        {
          langCode: "ko",
        },
        loadingFunction,
      );
    } catch (error) {
      if (error.response) {
        // Request made and server responded
        console.log(error.response.data);
        console.log(error.response.status);
        console.log(error.response.headers);
      } else if (error.request) {
        // The request was made but no response was received
        console.log(error.request);
      } else {
        // Something happened in setting up the request that triggered an Error
        console.log("Error", error.message);
      }
    }
  };

  return (
    <div>
      <button onClick={testApiFunction}>테스트</button>
    </div>
  );
};
```

### appsync 사용 예시

- 구독

```javascript
//--------------------------------------
// appsync를 사용하여 실시간 데이터 관련

// 아래 3파일은 서버 담당자가 제공
// /src/graphql/queries.js
// /src/graphql/mutations.js
// /src/graphql/subscriptions.js

//--------------------------------------
// /src/graphql/subscriptions.js 예시
//--------------------------------------

import gql from "graphql-tag";

export const onCreateCpMsg = gql`
  subscription onCreateCpMsg($pk: String!) {
    data: onCreateCpMsg(pk: $pk) {
      pk
      sk
      dt
      msg_type
      info
    }
  }
`;

//--------------------------------------
// 구독 관련 샘플
//--------------------------------------

import { API, graphqlOperation } from "aws-amplify";

import * as subscriptions from "../graphql/subscriptions";
import * as mutations from "../graphql/mutations";
import * as queries from "../graphql/queries";

let subscribeRef = null;

const subscribe_key = "cust_20210402000333";

const Main = (props) => {
  const setMessage = async (data) => {
    console.log("log -> --------------------------");
    console.log("log -> ~ Main ~ data", data);
    console.log("log -> --------------------------");
  };

  const appSyncInit = () => {
    if (subscribeRef) {
      return;
    }

    subscribeRef = API.graphql(
      graphqlOperation(subscriptions.onCreateCpMsg, {
        pk: subscribe_key,
      }),
    ).subscribe({
      next: (data) => setChatMessage(data.value.data.data),
    });
  };

  const appSyncRelease = () => {
    if (subscribeRef) {
      subscribeRef.unsubscribe();
      subscribeRef = null;
    }
  };

  useEffect(() => {
    appSyncInit();
    return () => {
      appSyncRelease();
    };
  }, []);
  return {};
};
```

## api default 옵션 관련 작업 로딩

- 아래 코드를 사용시 모든 Api 에서 로딩, 재시도를 세팅가능

```javascript
// 각 프로젝트 에서 한번만
import { Api } from "@psyrenpark/api";

Api.setApiClassValue({
  loadingCallback: (flag) => { console.log("loading : ", flag ) }
  retries: 0
});

```

## api 재시도 기능 추가

- 에러시 재시도

```javascript
var apiName = v1Cdn; // 인증 유, 인증 무 api 구분은 이부분이 다르다.
var path = "/test_test_test";
var myInit = {
  headers: {}, // OPTIONAL
  // body: {  // post나 put일경우 사용한다.
  //
  // },
  queryStringParameters: {
    langCode: langCode,
  },
  // response: true,  // axios 원형 response 필요할 경우 ture로 설정한다.
};

// 마지막 파라미터 에 재시도할 카운트 넣을것
return Api.get(apiName, path, myInit, LoadingCallback, null, 3);
```