Skip to content

거래내역 페이지 만들기

박재윤 edited this page Dec 19, 2020 · 1 revision

거래내역 페이지의 구조

  1. menuBar header
    • page nav
    • date nav
  2. list container
    • filter container
      • inout filter
      • category filter
    • transactions
      • transactions of one day
        • transaction item

Store

거래내역 페이지에서 나오는 거래내역의 목록은 필터에서 선택된 카테고리나 수입/지출 타입에 따라서 필터링된 거래내역만 나와야 했다.

이것을 구현하면서 고민을 했던 사항은 서비스 기획상 보여줘야 하는 데이터 구조가 날짜별로 나뉘어져 있었다. 따라서 account book store에 데이터 형태를 바꿔서 가져오는 것이 필요했다. 필터링 한 후에 날짜별로 그룹화를 해서 filteredTransactions에 저장을 해놓았다. 그리고 transactions 컴포넌트에서는 filteredTransactions를 observe 하고 있도록 구현했다.

const transactionsGroupByDate = typeFiltered.reduce((acc, curr) => {
        acc[curr.date] = [...(acc[curr.date] || []), curr];
        return acc;
      }, {});

this.filteredTransactions = transactionsGroupByDate;

Filter 컴포넌트

Filter 컴포넌트에서는 필요한 동작은 다음과 같았다.

  1. 수입/지출 타입과 카테고리로 설정되어 있는 모든 카테고리의 목록을 보여준다.
  2. 수입/지출 타입을 체크박스로 만들어 하나 혹은 여러개를 선택할 수 있도록 한다.
  3. 카테코리를 체크박스로 만들어 하나 혹은 여러개를 선택할 수 있도록 한다.
  4. 카테고리를 선택하면 필터링된 거래내역의 금액 합이 수입/지출 타입 체크박스에 반영된다.
  5. 수입/지출 타입과 카테고리 체크박스 모두 체크박스를 안 보이게 하고 스타일을 지정해 custom 체크박스로 만든다.
  6. 수입/지출 타입과 카테고리 체크박스를 선택하면 필터링 하도록 accountbook store에 반영한다.

1번을 위해서 account book store에 있는 categories를 observe 하도록 만들었다. 이 categories에는 _id, type, name이 들어있다. 그리고 dataset attribute에 각각의 값들을 input 체크박스에 넣어주었다.

2, 3번을 위해서 각각을 input type checkbox로 만들어주었다.

4번을 위해서 카테고리의 onChange 이벤트 핸들러를 만들어주었다. 현재 선택된 카테고리가 무엇이 있는지를 나타내기 위해서 list container 컴포넌트 안에 selectedCatoriesuseState를 이용해서 만들어주고 Filter 컴포넌트에 props로 넘겨주었다. 그리고 카테고리의 onChange 핸들러에서 현재 selectedCategories 안에 이번에 바뀐 체크박스의 값이 없으면 넣어주고 있으면 빼주는 작업을 했다.

const onCategoryChange = useCallback(
    (e: ChangeEvent<HTMLInputElement>) => {
      const targetElement = e.target as HTMLInputElement;
      if (selectedCategories.includes(targetElement.value)) {
        const rest = selectedCategories.filter(
          category => category !== targetElement.value,
        );
        selectCategories(rest);
      } else {
        selectCategories([...selectedCategories, targetElement.value]);
      }
    },
    [selectedCategories, selectedCategories],
  );

5번을 위해서 input의 display 속성을 none으로 해주고 input 다음에 span을 만들어 span에 각 카테고리에 해당하는 카테고리 이름 값을 넣어주었다. 그리고 span의 스타일을 input이 체크가 되었을 때와 체크가 되지 않았을 때에 따라서 스타일이 달라지게 하기 위해서 + css selector를 이용했다.

input[data-type='지출']:checked + span,
    input[data-type='지출']:hover + span {
      padding: 3px;
      border-bottom: 2px solid $spending-color;
      color: $spending-color;
    }

6번을 위해서 selectedCategories를 바꿔주면 자동적으로 하위 컴포넌트인 list component가 다시 렌더링 되고 useEffect에서 accountbook store의 filter 메소드를 호출하도록 구현했다. 따라서 카테고리 input이 클릭되었을 때 filter 컴포넌트 부분에서 해줘야할 일은 selectedCategories를 업데이트 해주는 것 뿐이게 되었다.

거래내역 목록

거래내역 목록 컴포넌트인 transactions 컴포넌트는 filteredTransaction을 가지고 날짜 별로 묶어서 리스트 형태로 보여주는 기능이 필요했다. 따라서 하루치에 해당하는 거래내역을 가지고 있는 transactionsOfOneDay라는 컴포넌트와 거래내역 하나에 해당하는 transactionItem이라는 컴포넌트를 만들었다.

transactions 컴포넌트에서는 filteredTransactions를 날짜별로 정렬을 해서 해당 날짜의 거래내역들을 transactionsOfOneDay에 props로 전달

Object.keys(filteredTransactions)
        .sort()
        .reverse()
        .map(day => (
          <TransactionsOfOneDay
            date={day}
            transactions={filteredTransactions[day]}
            key={day}
          />
        ))

transactionsOfOneDay 컴포넌트에서는 transactionItem 컴포넌트를 map을 이용해 호출했다.

transactionItem 컴포넌트에서는 거래내역의 정보를 나타내고 해당 거래내역을 클릭했을 때 수정이나 삭제 버튼이 나오도록 하기 위해서 버튼 container div를 두고 opacity를 0으로 해두고 position을 absolute로 한 뒤에 right에 음수값을 주어서 오른쪽에 있도록 했다. 그리고 클릭을 하면 reveal class를 해당 div에 넣어주었다. 그러면 reveal에 설정된 animation에 의해서 오른쪽에서 왼쪽으로 나오도록 설정했다.

@keyframes button__in {
  0% {
    opacity: 0;
    right: -15%;
  }

  100% {
    opacity: 1;
    right: 10px;
  }
}

그렇게 하니 버튼이 나타날 때는 오른쪽에서 왼쪽으로 오는 애니메이션이 생겼지만 사라질 때는 바로 사라져버렸다. 사라질 때도 오른쪽으로 사라지도록 만들고 싶어서 state를 하나 만들었다. 버튼 컨테이너의 상태가 reveal 상태인지, hide 상태인지를 나타내는 state를 만들어 classname을 지정해줄 때 state의 값을 classname에 추가해주도록 했다. 그리고 button__out keyframe도 만들어 hide 상태에 넣어주었다.

Clone this wiki locally