Apollo-client React Coding Work Flow
Apollo-client React Coding Work Flow

Apollo-client React Coding Work Flow

Created
Nov 17, 2021 05:39 AM
Tags
apollo
GraphQL
GraphQL code generator
Prisma
nexus GraphQL
author
notion image
해당 POST 는 ad-fi-admin 프로젝트의 src > Views > Brand > DashBoard > AdRevenue 를 참고하여 작성되었습니다.
DIRECTORY STRUCTURE
`ad-fi-admin`
└── . . .
└── src
│   └── . . .
│   └── Views
│   │   └── . . .
│   │   └── Brand
│   │   │   └── . . .
│   │   │   └── Dashboard
│   │   │   │   └── AdRevenue
│   │   │   │   │   └── AdRevenueByDate.graphql
│   │   │   │   │   └── AdRevenue.tsx
│   │   │   │   │   └── DefaultProps.tsx
│   │   │   │   │   └── index.tsx
│   │   │   │   │   └── types.tsx
│   │   │   │   └── . . .

Step By Step 🚶

Container 작성

File Naming Convention : Container 파일의 명칭은 index.tsx 로 작성합니다.
  • 전체 코드#
알아두기

유사한 문구

  • Types : graphql-codegen 을 사용하여 자동으로 생성된 코드 입니다.
  • ./Types : 개발자가 직접 작성한 코드로 typeinterfacechecker 를 export 합니다.

재사용 하는 모듈

  • useError : Contexts/erroHandlerContext 에 작성해놓은 모듈을 사용합니다.
  • Loader : Components/Loader 에 작성해놓은 모듈을 사용합니다.
// => 파일명 : index.tsx

import React from "react";
import { useBrandGetPureRevenueByDateQuery } from "Types";
import { useError } from "Contexts/errorHandlerContext";
import AdRevenue from "./AdRevenue";
import Loader from "Components/Loader";
import moment from "moment";
import { barData } from "./Types";

export default function AdRevenueContainer() {
  const { onError } = useError();
  const start = moment().subtract(29, "day").startOf("day").toISOString();
  const end = moment().endOf("day").toISOString();
  const { data, loading } = useBrandGetPureRevenueByDateQuery({
    variables: { start, end },
    onError,
    fetchPolicy: "network-only",
  });

  return (
    <>
      {loading && <Loader />}
      <AdRevenue barData={barData(data)} />
    </>
  );
}

Presenter 작성

File Naming Convention : Presenter 파일의 명칭은 폴더명 과 동일하게 작성합니다.
  • 전체 코드#material-ui 를 사용한 컴포넌트 디자인, 구성등을 Presenter 컴포넌트에 작성합니다.
import React from "react";
import { Grid } from "@material-ui/core";
import BarChartCard from "Components/BrandBarChartCard";
import { PropType } from "./Types";
import { PureRevenueIcon } from "Components/Icons";
import { theme } from "theme";
import DefaultProps from "./DefaultProps";

export default function ClickPlayCountByBrand(props: PropType) {
  const { barData } = props;

  return (
    <Grid item xs={12}>
      <BarChartCard
        title="광고 순 매출"
        icon={PureRevenueIcon}
        data={barData}
        keys={Object.keys(barData[0]).splice(1)}
        indexBy="day"
        colors={[theme.danbiTheme.colors.middle_danbiblue]}
        legendBottom=""
        legendLeft=""
        legend={false}
        tooltipState={true}
        tooltip="30일 간의 광고 순매출을 나타낸 그래프"
      />
    </Grid>
  );
}

ClickPlayCountByBrand.defaultProps = DefaultProps;

Graphql 작성

알아두기
query 혹은 mutation 키워드와 같이 작성하는 함수명은 기본적으로는 선택사항입니다.하지만, graphql-codegen 을 사용하여 typehooklazy hook 을 만들기 위해서는 필수적으로 기입해야 하는 사항입니다.

Bad Code

query ($start: DateTime!, $end: DateTime!) { . . . }

Good Code

query brandGetPureRevenueByDate($start: DateTime!, $end: DateTime!) { . . . }
  • query 정상동작 확인#danbi-api graphql playground 를 통해서 작성한 querymutation의 정상 동작을 확인 합니다.
notion image
  • query 작성#graphql playground 를 통해서 정상동작이 확인된 graphql 코드를 복사하여 파일을 생성합니다.
AdPureRevenueByDate.graphql
query brandGetPureRevenueByDate($start: DateTime!, $end: DateTime!) {
  me {
    brand {
      pureRevenueByDate(start: $start, end: $end) {
        date
        pureRevenue
      }
    }
  }
}

Type 생성

알아두기
graphql-codegen 을 사용하면 TypeHookLazy Hook 이 자동으로 생성됩니다.이렇게 생성된 TypeHookLazy Hook 은 data type을 고정하거나 react.js 에서 data 를 불러올때 사용됩니다.
  • Script 실행#
yarn dev:codegen
  • Script 실행 결과
`ad-fi-admin`
└── . . .
└── src
│   └── Types
│   │   └── index.tsx       <= Look Here !!!
│   └── . . .
│   └── Views
│   │   └── . . .
│   │   └── Brand
│   │   │   └── . . .
│   │   │   └── Dashboard
│   │   │   │   └── AdRevenue
│   │   │   │   │   └── AdRevenueByDate.graphql
│   │   │   │   │   └── AdRevenue.tsx
│   │   │   │   │   └── DefaultProps.tsx
│   │   │   │   │   └── index.tsx
│   │   │   │   │   └── types.tsx
│   │   │   │   └── . . .

types.tsx 작성

types.tsx 는 총 3개의 모듈을 export 합니다.
  • Checker#
알아두기
lodash 라이브러리를 사용하여 아래와 같은 상황에서의 예외처리를 진행합니다.
  • 데이터가 Undefined인 경우 : isUndefined()
  • 데이터가 Null인 경우 : isNull()
  • 데이터가 빈 배열인 경우 : isEmpty()
import { BrandGetPureRevenueByDateQuery } from "Types";
import { BarDatum } from "@nivo/bar";
import defaultBarData from "./DefaultProps";
import { isUndefined, isNull, isEmpty, compact } from "lodash";

export const barData = (data: BrandGetPureRevenueByDateQuery | undefined) => {
  if (isUndefined(data) || isNull(data)) return defaultBarData;
  if (isUndefined(data.me) || isNull(data.me))
    throw new Error("로그인하지 않았습니다.");
  if (
    isUndefined(data.me.brand) ||
    isNull(data.me.brand) ||
    isEmpty(data.me.brand)
  )
    throw new Error("브랜드 사용자가 아닙니다.");

  const {
    me: {
      brand: [{ pureRevenueByDate }],
    },
  } = data;

  if (isUndefined(pureRevenueByDate) || isNull(pureRevenueByDate))
    return defaultBarData;

  const result = compact(
    pureRevenueByDate.map((item) => {
      return {
        day: item ? item.date : "_",
        revenue: item ? item.pureRevenue : 0,
      };
    }),
  );

  return result;
};

. . .
  • type barData
. . .

export type barData = BarDatum[];
  • interface PropType Presenter 컴포넌트에서 props 의 type 으로 사용됩니다.
. . .

export interface PropType {
  barData: barData;
}

etc .

  • defaultProps 초기값을 설정하여 컴포넌트 렌더링시 컴포넌트가 비어있는것을 방지합니다.
DefaultProp.tsx
export default [
  {
    day: "_",
    revenue: 0,
  },
];