프리온보딩 | 2번째 과제 회고 - 1주차

noopy·2022년 1월 30일
5

프리온보딩

목록 보기
2/6
post-thumbnail

Wanted-preonboarding 1-2

깃허브 링크
배포 링크
노션

📕 구현 명세

(내가 맡은 부분 ✅)

상품 등록 페이지 만들기

  • 노출 및 판매기간
  • 상품 기본 정보
  • 상품 옵션 ✅
  • 상품 등록
  • 상품 정보 고시 ✅

🪞 데모

상품 옵션 테이블(OptionTable)

  • 옵션 세트(이미지, 옵션 정보) 추가 삭제
  • 옵션 세트 내 이미지 추가, 삭제
  • 옵션 세트 내 옵션 추가, 삭제
  • 옵션 세트 내 옵션 내 추가, 삭제
  • 옵션 세트 내 옵션을 모두 삭제하면 옵션 세트 또한 삭제

  • 할인율 처리
    • 할인율 소수점 버림 처리
    • 정상가, 판매가 같을 시 할인율 없음 표시
  • 재고 처리
    • 옵션 세트 내 모든 옵션에 대한 총 재고 수량 저장

  • 비과세, 과세 처리
    • 과세 옵션 선택 시 해당 옵션의 저장된 판매가의 부과세(VAT) 10%로 저장

// 상품 옵션 데이터 로직
[
  {
    index: 0, // 각 옵션세트 인덱스
    imageInfo: {},
    optionInfo: [ // 옵션 세트 내 옵션 정보
    {
      index: 0,
      optionName: '',
      normalPrice: 0, // 정상가
      price: 0, // 할인가
      discount: 0,
      stock: 0,
      isVAT: true,
      VAT: 0, // 부과세
      },
    ],
    totalStock: 0, // 옵션 세트 내 모든 옵션의 stock 총합
    additoryOptions: [ // 옵션 세트 내 옵션 내 추가
    {
      index: 0,
      addOptionName: '',
      addOptionNormalPrice: 0,
      },
    ],
  }
]

상품 정보고시 테이블(ItemInformationTable)

  • context 분리 및 리팩토링

⏰ 진행 과정

첫번째 과제와 달리 이번 과제부터는 각자 역할을 맡아 구현하고 합치는 과정으로 진행했다. 가장 작은 base 컴포넌트와 공통으로 사용할 컴포넌트들을 만든 뒤 issue로 등록 후 구현하고싶은 테이블을 골라서 만들었다.

리액트에 익숙하지 않은 팀원이 선뜻 역할을 맡기 어려워하여 base 컴포넌트를 만들 땐 라이브러리를 쓸 수 있는 Date-Picker 를 만들고, 테이블에선 해당 컴포넌트를 많이 사용하면서도 상대적으로 난이도가 낮은 테이블을 배정하여 모두가 구현할 수 있도록 진행했다.

1️⃣ 상품 옵션 테이블

상품 옵션 테이블을 만들며 느낀 것은 백앤드가 API를 만드는 과정을 조금이나마 이해할 수 있었다는 것이다. 배열 안에 객체, 객체 안에 배열, 배열 안에 객체...가 만들어지는 과정을 생각하며 코드를 짜는 것은 상당히 어려웠다.
로직을 작성하기 전에 기존에 useState 만으로도 가능할 것인가를 되짚었지만, 컴포넌트 props drilling이 너무 깊어지고, 로직이 복잡할 것 같아 context API를 적용해보기로 했다. (진짜 잘한 선택이라고 생각한다 👏)

context API를 처음 적용해보았는데, 차후에 배울 redux도 결국엔 context API를 활용하여 만든 것이라 하니 이해할 때 도움이 많이 될 듯 했다. 전역 상태관리는 옵션 테이블상품 정보고시 테이블, 최종 저장하기를 위한 데이터 모으기 로직에서 사용되었다.

🔑 KEY POINT

  1. action.type은 최대한 REST API 스럽게 지어서 어떤 역할을 하는지 명확히 하기
import {
  ADD_OPTION_SET,
  DELETE_OPTION_SET,
  ADD_OPTION_IMAGE,
  ADD_OPTION,
  DELETE_OPTION,
  UPDATE_OPTION_INFO,
  ADD_ADDITORY_OPTION,
  UPDATE_ADDITORY_OPTION,
  DELETE_ADDITORY_OPTION,
} from './types'
  1. 특정 row 삭제 후 index 재조정 작업 거치기

사진과 같이 여러 개의 row가 있을 때, 특정 row를 삭제하기 위해 data-index에서 index를 받아와서 filter로 매치시켰다. 문제는 삭제 후 컴포넌트를 재렌더링하며 React.Children.map에서 index를 재조정시켜주더라 (?)
예를 들어 0, 1, 2 의 data-index가 할당된 열들이 있을 때, 1번 열을 삭제하면 컴포넌트 리렌더링 후 data-index는 0, 1로 다시 할당된다. 😲
이 때문에 기존에 reducer에 저장된 데이터들의 index도 다시 재조정해주는 작업이 필요했다.
이 작업은 그리 어렵지 않았다. 배열을 받아서 map에서 제공하는 index로 다시 할당해 반환하는 함수를 만들어 사용했다.

export const getArrangedIndexArray = (array) =>
  array.map((element, newIndex) => ({
    ...element,
    index: newIndex,
}))
  1. onChange가 일어나는 태그들은 디바운스로 자동저장기능 넣어주기

모든 작업들은 실제 API를 받아왔을 때, 백앤드와 통신할 때 어떻게 할 것인가를 염두에 두고 고안하려고 노력했다.
사용자가 input을 입력하다가 중간에 로직이 날아가거나 예상치 못해 페이지가 새로고침되는 상황이 있을 수 있기 때문에 중간중간 저장하기를 위한 기능이 필요했다. 또한 한 페이지에 옵션 정보뿐만 아니라 엄청나게 많은 정보를 입력해야 하므로 이 기능은 필수였다. 임시저장을 위한 버튼을 만들까 생각했지만, 제공된 시안을 최대한 그대로 따라가는 게 맞다고 생각이 들어 디바운스를 적용하기로 했다. 사실 디바운스를 적용하는 것은 처음이라 조금 떨렸다 ㅎ.

export const debounceGenerator = (ms) => {
  let id
  return (cb) => {
    if (id) {
      clearTimeout(id)
    }
    id = setTimeout(() => {
      cb()
      id = null
    }, ms)
  }
}

내가 만든 로직은 왠지 모르게 잘 작동하지 않아 팀원분께 부탁드렸다. 알고보니 useCallback을 사용하지 않아 onChange가 일어날 때마다 리렌더링이 발생해 함수도 계속 선언되고 있었다 ㅜㅜ. useCallback으로 함수를 감싸줄 경우 deps의 값이 바뀌지 않으면 함수를 재사용할 수 있다.

const debounceFn = useCallback(debounceGenerator(800), [])

위에서 export 한 debounceGenerator 함수를 useCallback으로 감싸 반환한 함수로 사용하면 된다. 거짓말처럼 깔끔하게 디바운스가 적용되어 좋았다. 👍
이제 사용자는 데이터를 잃을 걱정없이 마음껏 작성하면 된다!!!

  1. 시멘틱 태그를 잘 활용하여 코드 사용량 줄이기

form 태그는 data를 submit할 때 많이 사용된다. 중요한 것은 내부의 input들과 button들도 form 태그와 유기적으로 연결된다는 것인데 의외로 간과하고 있는 부분이기도 한 것 같다. 요구사항 명세서에선 필수적으로 작성해야 하는 input들이 있다. 이 때는 form 태그 안에서 input에 required 속성을 넣어주면 작성되지 않은 input들이 있을 경우 submit이 되지 않고 브라우저에서 입력하라고 알려준다.


아래의 이 입력란을 작성하세요. 보이쥬?
추가로 화면이 새로고침 되지 않도록 form에 e.preventDefault를 넣어주는 것도 잊으면 안된다.

2️⃣ 상품 정보고시 테이블

상품 정보고시 테이블은 다른 팀원이 맡아주셨는데 context API의 작동방식을 잘 몰라서 내 옵션 테이블을 위한 context를 갖다 쓰신 것 같다 😭 사실상 로직은 같은데 하나의 context를 같이 쓰려고 하니 상품 정보고시에서 버튼을 누르면 옵션 테이블에서도 작동하고 머 이런저런 꼬이는 일이 발생했다.
비록 제출기한이 지났지만 그래도 끝까지 깔끔하게 마무리하고 싶어 context를 분리하고 상대경로로 작성된 것들을 수정하고 시멘틱 태그들도 정리하고 해보았다 ㅎㅎ. 데브코스를 할 때였으면 할 일이 너무너무 산더미처럼 쌓여서 그냥 담에 하거나 흐린눈 했을 텐데 이번에는 그러지 않기로 했으니까!!! 하고 나니 뿌듯하다.

🌈 결론

어쨌거나 contextAPI도 이해하고, 뭔가 복잡한 로직들을 잘 정리했다고 생각해서 뿌듯한 프로젝트였다. 깔끔하게 코드를 작성하지는 못한 것 같지만 발전하는 게 느껴지는 보람찬 하루~!

profile
💪🏻 아는 걸 설명할 줄 아는 개발자 되기

1개의 댓글

comment-user-thumbnail
2022년 1월 30일

저도 텍스트 수정 어떻게 구현할지 고민 많이 했었는데 디바운스 처리가 있었군요!
참고할게요! 감사합니다!

답글 달기