Skip to content

katej927/dashboard-app

Repository files navigation

Dashboard App

대시보드앱1

1️⃣ 프로젝트 개요

그래프를 이용한 마케팅 데이터 시각화 프로젝트

  • 프론트엔드 4명, 풀스택 1명

  • 구현한 부분 : 통합 광고 현황 그래프

  • 기간

    • 팀 전체 : ‘22.5.23~5.26

    • 개인 (나) : ‘22.5.24~5.26

2️⃣ Links

👉 화면 확인 : 배포 링크

👉 코드 확인 : 내가 구현한 코드, 팀 전체 코드

3️⃣ Techs

※ 제가 사용하지 않은 기술들은 생략했습니다.

  • React, Typescript, scss + classnames
  • Recoil
  • victory
  • dayjs

4️⃣ 구현 내용

※ 팀원분들이 구현하신 것들은 생략했습니다.

🔸 통합 광고 현황 그래프

  1. 드롭 다운
주간 / 일별로 선택가능
  • 주간

    • UI

      대시보드앱5

    • 구현 방법

      동일 주차의 평균을 구하여 주차 별로 반환

      1. 선택한 날짜들이 해당하는 월의 주차(n월 n주)를 구함

      2. 동일한 주에 해당하는 날짜들의 갯수를 구하고 데이터는 모두 더한 후 해당 주차의 데이터 총합/해당 주차의 날짜 총 갯수를 반환하여 주차별 평균을 반환

      • 코드

        ./src/routes/dashboard/integratedAd/graphTwoTypes/_shared/utils.ts

        const getWeekNumber = (cur: string) => {
          const currentDate = dayjs(cur).date();
          const weekDay = dayjs(cur).startOf('month').day();
          const weekNum = Math.trunc((weekDay - 1 + currentDate) / 7) + 1;
          return `${dayjs(cur).month() + 1}${weekNum}주`;
        };
        
        const convertWeeklyData = (integratedAdInfo: IDay[], btnOption: Btn) => {
          let arrIndex = 0;
          let daysInWeek = 0;
          return integratedAdInfo?.reduce(
            (acc, cur, i, src) => {
              const accX = getWeekNumber(acc[arrIndex].x);
              const curX = getWeekNumber(cur.date);
              const btnValue = Array.isArray(btnOption) ? (cur[btnOption[0]] * cur[btnOption[1]]) / 100 : cur[btnOption];
        
              if (i === 0) return acc;
        
              daysInWeek += 1;
        
              if (accX === curX) {
                acc[arrIndex] =
                  i === src.length - 1
                    ? { x: accX, y: Math.round((acc[arrIndex].y + btnValue) / (daysInWeek += 1)) }
                    : { x: cur.date, y: acc[arrIndex].y + btnValue };
                return acc;
              }
        
              acc[arrIndex] = { x: accX, y: Math.round(acc[arrIndex].y / daysInWeek) };
        
              daysInWeek = 0;
              arrIndex += 1;
              acc[arrIndex] = { x: cur.date, y: btnValue };
              return acc;
            },
            [
              {
                x: integratedAdInfo[0].date,
                y: Math.round(
                  Array.isArray(btnOption)
                    ? (integratedAdInfo[0][btnOption[0]] + integratedAdInfo[0][btnOption[1]]) / 100
                    : integratedAdInfo[0][btnOption]
                ),
              },
            ]
          );
        };
    • 선택이 불가능한 경우

      7일 이하의 기간을 선택했을 경우

      7일 이하의 기간 선택 시, 기간을 선택하는 드롭다운을 비활성화 시켜 주간 선택 불가

  • 일별

    • UI

      대시보드앱6

    • 선택한 기간의 모든 데이터를 일별로 보여준다.

    • x축의 눈금 갯수

      기본적으로 눈금의 갯수(tickCount)를 7로 설정하여 보여지는 눈금 갯수에 통일성을 줌.

    • 코드

      ./src/routes/dashboard/integratedAd/graphTwoTypes/_shared/utils.ts

      const convertDailyData = (integratedAdInfo: IDay[], btn: Btn) =>
        integratedAdInfo?.map((day) => {
          return { x: day.date, y: Math.round(Array.isArray(btn) ? (day[btn[0]] * day[btn[1]]) / 100 : day[btn]) };
        });
선택 시 반환할 데이터 계산
  • UI

    대시보드앱7

  • 가공하는 데이터

    • 단위 값 (%, 원, 회)

    • 그래프에 보여줄 데이터 배열

    • y축 값 (y값 중의 최대값) 등

  • 코드

    ./src/routes/dashboard/integratedAd/graphTwoTypes/_shared/utils.ts

    const formatReturnData = (unitVal: string, integratedAdInfo: IDay[], btn: Btn, periodOption: PeriodOptions) => {
      const formatedData =
        periodOption === '일간' ? convertDailyData(integratedAdInfo, btn) : convertWeeklyData(integratedAdInfo, btn);
      return {
        unit: unitVal,
        formatedData,
        maxValue: formatedData && Math.max(...formatedData.map((obj: IFormatedData) => obj.y)),
      };
    };
    
    export const convertData = (integratedAdInfo: IDay[], btnOption: PrimaryOptions, periodOption: PeriodOptions) => {
      if (btnOption === 'ROAS') return formatReturnData('%', integratedAdInfo, 'roas', periodOption);
      if (btnOption === '광고비') return formatReturnData('원', integratedAdInfo, 'cost', periodOption);
      if (btnOption === '클릭 수') return formatReturnData('회', integratedAdInfo, 'click', periodOption);
      if (btnOption === '노출 수') return formatReturnData('회', integratedAdInfo, 'imp', periodOption);
      if (btnOption === '매출') return formatReturnData('원', integratedAdInfo, 'convValue', periodOption);
      if (btnOption === '전환 수') return formatReturnData('회', integratedAdInfo, ['cvr', 'click'], periodOption);
      return undefined;
    };
첫 번째 드롭 다운에서 선택한 지표를 두번째 드롭다운(옵셔널)에서 선택 불가
  • UI

  • 구현 이유

    1번째, 2번째 드롭 다운의 옵션들이 같기 때문에 서로 다른 옵션을 선택하도록 설정함.

  • 구현 방법

    filter 메소드 활용

  • 코드

    ./src/routes/dashboard/integratedAd/graphTwoTypes/_shared/utils.ts

    export const filterGraphOpt = (otherOpt: PrimaryOptions) => PRIMARY_OPTIONS.filter((option) => option !== otherOpt);
    
    export const filterPeriodOpt = (preiod: IPeriod) => {
      const { startDate, endDate } = preiod;
      const start = dayjs(startDate);
      const periodDiff = start.diff(endDate, 'd');
      return Math.abs(periodDiff) > 6 ? ['일간', '주간'] : ['일간'];
    };
2가지 드롭 다운이 모두 선택될 경우, 그래프 우측에 y2 눈금자 제공
  • UI

    대시보드앱9

  • 구현 이유

    2가지의 데이터를 선택했기 때문에 2가지의 y축 눈금으로 데이터를 수치화해서 정보를 보여준다.

  • 구현 방법

    y2축에 할당할 축(VictoryAxis)와 선(VictoryLine )을 key로 연결

  • 코드

    ./src/routes/dashboard/integratedAd/graphTwoTypes/index.tsx

    				(생략)
    				{secondOption !== '없음' && (
              <VictoryAxis
                dependentAxis
                key={secondOption}
                offsetX={960}
                tickFormat={(t) => {
                  return `${convertNumToUnit(t * (max2ndOption ?? 1) * 2)}${unit2ndOption}`;
                }}
                tickLabelComponent={<VictoryLabel verticalAnchor='start' textAnchor='start' {...properties.label2} />}
                {...properties.xAxis}
              />
            )}
    				(생략)
            {!isLoading && secondOption !== '없음' && (
              <VictoryLine
                key={secondOption}
                data={data2ndOption}
                y={(datum) => datum.y / ((max2ndOption ?? 1) * 2)}
                {...properties.line2}
              />
            )}
    				(생략)
  1. 그래프
숫자 단위 변환
  • UI

  • 값이 1만 이하일 경우

    반올림으로 소수점 제거 후, 천 단위마다 콤마(,) 형성

  • 값이 1만~1조 일 경우

    한글 단위로 변환 (ex. 5백만원)

  • 코드

     export const convertNumToUnit = (num: number) => {
       if (num < 10000) {
         return Math.round(num)
           .toString()
           .replace(/\B(?=(\d{3})+(?!\d))/g, ',');
       }
     
       const transUnit = [
         { value: 1e12, symbol: '조' },
         { value: 1e11, symbol: '천억' },
         { value: 1e10, symbol: '백억' },
         { value: 1e9, symbol: '십억' },
         { value: 1e8, symbol: '억' },
         { value: 1e7, symbol: '천만' },
         { value: 1e6, symbol: '백만' },
         { value: 1e5, symbol: '십만' },
         { value: 1e4, symbol: '만' },
         { value: 1e3, symbol: '천' },
       ];
       let i;
       for (i = 0; i < transUnit.length; i += 1) {
         if (num >= transUnit[i].value) {
           return (num / transUnit[i].value).toFixed(1).replace(/\.?0+$/, '') + transUnit[i].symbol;
         }
       }
       return num;
     };
툴팁 제공

그래프의 선을 hover하면 툴팁 확인 가능

  • UI

    대시보드앱4