차트 로직 리팩토링하기

Poco ·2019년 11월 24일
0

1. 들어가며

최근에 마음이 급해서 우선 돌아가는 코드를 짜려다 보니 최소한의 설계도, 코드품질도 없이 급급하게 코드를 짜곤 했는데요. 주말을 맞이하여 리팩토링을 해보았습니다.

데이터 시각화를 위한 차트의 경우에는 특정 조건별 데이터를 뽑아내야 합니다.
ex (a)특정기간별 매출 (b)상품종류별 매출 (c)신고종류별 횟수

오늘은 특정기간, 상품종류에 따라 데이터를 보여주는 코드를 리팩토링하며 배운점들을 적어보고자 합니다.

2. 특정기간별 데이터 보여주기

차트는 시간에 따른 데이터가 들어가다보니 결국 데이터는 배열형태로 들어가야 합니다. 그렇다면 특정 기간별 데이터는 어떻게 뽑아내는것이 좋을까요?

  1. 저는 처음에는 아래와 같이 원본 배열을 slice를 해서 얕은 복사를 한다음에 splice로 특정기간을 잘라내는 식으로 코드를 작성하였습니다.
const batch1breakstart = moment('2019/07/20').diff(moment('2019/04/14'), 'days')
    const batch1breakend = moment('2019/09/16').diff(moment('2019/04/14'), 'days')
    
    const batch2start = moment('2019/07/18').diff(moment('2019/04/14'), 'days')
    const batch3start = moment('2019/11/11').diff(moment('2019/04/14'), 'days')
    
    const copedReportsWithBrokenByDayBatch1 = reportsWithBrokenByDayBatch1.slice()
    const copedReportsWithBrokenByDayBatch2 = reportsWithBrokenByDayBatch2.slice()
    const copedReportsWithBrokenByDayBatch3 = reportsWithBrokenByDayBatch3.slice()
    copedReportsWithBrokenByDayBatch1.splice(batch1breakstart, batch1breakend - batch1breakstart - 1)
    copedReportsWithBrokenByDayBatch2.splice(0, batch2start)
    copedReportsWithBrokenByDayBatch3.splice(0, batch3start)

그러나 splice의 인자인 시작일과 끝일을 맞춰주는것도 어렵고, slice로 복사한것을 splice로 자르는 이런 난잡한 과정이 찜찜했습니다. 중간의 시작일과 끝일도 하드코딩이 되어있구요.

  1. filter함수를 이용하자!

생각해보면 배열의 원소를 객체로 만들고 시간을 key값으로 가지고 있으면 필터하기가 훨씬 더 쉬운것같아서 데이터형식을 변경해주었습니다.

    const BATCH_1_BREAK_START = '2019-MM-AB'
    const BATCH_1_BREAK_END = '2019-MM-CD'
    const BATCH_2_START = '2019-MM-EF'
    const BATCH_3_START = '2019-MM-GH'
    
    const filteredReportsWithBrokenByDayBatch1 = reportsWithBrokenByDayBatch1.filter(s => !moment(s.reportAt).isBetween(BATCH_1_BREAK_START, BATCH_1_BREAK_END))
    const filteredReportsWithBrokenByDayBatch2 = reportsWithBrokenByDayBatch2.filter(s => moment(s.reportAt).isSameOrAfter(BATCH_2_START))
    const filteredReportsWithBrokenByDayBatch3 = reportsWithBrokenByDayBatch3.filter(s => moment(s.reportAt).isSameOrAfter(BATCH_3_START))

진짜 왜 splice를 처음에 사용했을까요? 1분만 멈춰서 생각해볼걸 그랬어요.

3. 하드코딩 리팩토링하기

특정 상품별 데이터를 보여주는 코드는 원래 상품종류가 N가지라고 했을때 N개의 배열이 하드코딩이 되어있었습니다. 그런데, 상품의 종류가 늘어날때마다 새로운 배열을 만들고 다시 차트에 추가하는 해야한다면요?
단기적으로 마음이 편할 수 있지만 그것이 장기적으로 손해인것은 확실한것같습니다.

그래서 우선, 하드코딩된 데이터들을 일괄 처리할 수 있는 방법에서부터
리팩토링을 시작해보았습니다.

아래의 코드는 차트의 시간축을 구하는 로직입니다.
기존의 코드는 이렇게 하드코딩이 되어있었는데요.

    const dayLabelsBatch1 = props.dayAxis.filter(time => !time.isBetween(BATCH_1_BREAK_START, BATCH_1_BREAK_END)).map(formatWithDOW)
    const dayLabelsBatch2 = props.dayAxis.filter(time => time.isSameOrAfter(BATCH_2_START)).map(formatWithDOW)
    const dayLabelsBatch3 = props.dayAxis.filter(time => time.isSameOrAfter(BATCH_3_START)).map(formatWithDOW)

아래와 같이 idx별로 validTimeFilter를 리턴하는 함수를 만들고, idx별로 다르게 실행하도록 하였습니다.

    const validTimeFilterByBatch = (idx) => (time) => {
            let condition
            switch (idx + 1) {
            case 1:
                condition = !time.isBetween(BATCH_1_BREAK_START, BATCH_1_BREAK_END)
                break
            case 2:
                condition = time.isSameOrAfter(BATCH_2_START)
                break
            case 3:
                condition = time.isSameOrAfter(BATCH_3_START)
                break
            }
            return condition
        }
    
    const dayLabelsByBatch = Array(BATCH_AMOUNT).fill({}).map((el, idx) => props.dayAxis.filter(time => validTimeFilterByBatch(idx)(time)).map(formatWithDOW))

validTimeFilter는 dayLabelsByBatch 뿐만아니라 시간에따른 필터가 필요한 부분에서는 계속 사용할 수 있게되었네요!

4. 느낀점

  • 데이터는 쌩 배열이 아닌 항상 객체가 포함된 배열로 가지고 다니는것이 좋은것같다는 생각이 들었습니다.
  • 급하다고 설계를 미뤄서는 안됬는데, 후회가 되네요. 내일출근해서부터는 바쁘더라도 작게 설계하고 작게 구현하고 다시 설계하는 연습을 다시 해보려고 합니다.
  • 일괄처리를 하는 방법을 먼저 고민해보려고 합니다. 이전에는 금방떠오르지 않아, 일단 구현하고 리팩토링하면되겠지!라고 생각했는데 일단 구현하다보니 산으로 가더라구요.
profile
안녕하세요. 개발자와 마케터 경험을 가지고 PM으로 일하는 김선욱(aka 포코) 입니다. 제품 개선, 애드 테크, 광고 수익 최적화에 관심이 많습니다. velog에는 일하면서 얻은 인사이트를 기록하고 있습니다.

0개의 댓글