![notion image](https://www.notion.so/image/https%3A%2F%2Fmiro.medium.com%2Fmax%2F700%2F1*WJ5vIReIqbHhZWCrSr3tiQ.png?table=block&id=c8d26015-3d99-4ea0-9e22-fc00aa54b152&cache=v2)
해당 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
: 개발자가 직접 작성한 코드로type
,interface
,checker
를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
을 사용하여 type
, hook
, lazy hook
을 만들기 위해서는 필수적으로 기입해야 하는 사항입니다.Bad Code
query
($start: DateTime!, $end: DateTime!) { . . . }
Good Code
query
brandGetPureRevenueByDate($start: DateTime!, $end: DateTime!) { . . . }
- query 정상동작 확인#
danbi-api
graphql playground 를 통해서 작성한query
,mutation
의 정상 동작을 확인 합니다.
![notion image](https://www.notion.so/image/https%3A%2F%2Fblog.danbicorp.com%2Fimg%2F2021-01-16%2Fdanbi-ad-fi-admin-work-flow1.png?table=block&id=1a3ed20d-988c-44f8-8399-2ada34dd0c27&cache=v2)
- query 작성#graphql playground 를 통해서 정상동작이 확인된
graphql
코드를 복사하여 파일을 생성합니다.
AdPureRevenueByDate.graphql
query brandGetPureRevenueByDate($start: DateTime!, $end: DateTime!) {
me {
brand {
pureRevenueByDate(start: $start, end: $end) {
date
pureRevenue
}
}
}
}
Type 생성
알아두기
graphql-codegen
을 사용하면 Type
, Hook
, Lazy Hook
이 자동으로 생성됩니다.이렇게 생성된 Type
, Hook
, Lazy 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,
},
];