Skip to content

거래내역 input 모달

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

거래내역 input form은 거래내역을 추가하거나 수정할 때 사용하는 모달창을 얘기한다.

컴포넌트 구조

store

거래내역 input form은 총 9가지의 input이 필요하고 메세지를 파싱해서 데이터를 넣는 메세지로 추가 textarea가 필요했다. 이 정보들을 모두 모아 api 요청을 날리고 store에 추가를 하기 위해서는 이 input의 데이터들을 모두 모아놓는 것이 필요했다. 따라서 가장 거래내역 input form의 상위 컴포넌트인 TransactionAddForm에 useState를 이용해서 했으나 mobx를 더 사용해보고 싶은 마음에 store로 분리해서 모달의 state를 관리했다.

store를 분리해서 관리해보니 props로 계속해서 전달을 해야할 때와는 다르게 store에 어떤 state를 새로 추가해서 사용할 때 여러 props로 전달을 해야하지 않아도 된다는 장점이 있었다. 약간 의존성이 더 떨어진 느낌이 들었다.

store에는 9가지의 input에 해당하는 값과 메세지를 파싱해서 데이터를 넣는 메세지로 추가 textbox의 state에 해당하는 message 값, 현재 탭이 거래내역 추가 form인지 메세지로 추가인지를 나타내는 state가 있었다.

컴포넌트

날짜 컴포넌트

날짜를 선택하는 input을 만들 때 처음에는 input type date를 이용해서 입력을 받을까 고민을 했다. 그런데 input type date는 브라우저 마다 보이는 방식이 다르고 그것의 style을 css로 바꿔서 쓸 수 없다는 단점이 있었다.

당시에 민섭님이 달력 페이지에 달력을 만든 뒤였으나 재사용성을 고려하지 않고 만든 뒤라서 달력을 재사용해서 input을 만드는 것은 무리인 것 같다는 결론을 내렸다. 만약 처음에 모든 페이지나 컴포넌트의 구조나 설계를 하고 시작했으면 재사용성을 조금 더 늘릴 수 있었을 것 같은데 아쉬움이 조금 있다.

그래서 날짜를 년, 월, 일 별로 드랍다운을 이용해서 만들기로 결정했다. select를 이용해서 드랍다운을 만들었다. 그런데 select의 기본 기능으로 만들었을 때 드랍다운으로 나오는 아이템의 갯수를 지정을 할 수가 없었다. 드랍다운이 아니라 select에 보이는 아이템의 갯수는 선택을 하게 할 수 있었으나 원했던 것은 맨 처음에 select에 보이는 아이템은 한 개고 이것을 클릭 했을 때 드랍다운으로 원하는 갯수가 나오도록 하는 것이었다. 따라서 div를 이용해서 새로 드랍다운을 만들어야 되는 것 같았다. 그래서 이것은 간단하지만 비용이 크다고 생각해 react-select 라이브러리를 사용해서 만들었다.

기본 날짜는 현재 날짜로 만들었고 년도는 현재 년도의 +, - 30년으로 만들었다.

select의 값이 변했을 때 store의 input 값을 변경시켜주는 이벤트 핸들러가 필요했다. 년, 월, 일 별로 각자 다른 input을 변경시켜야 하지만 해야하는 동작은 똑같았다. 따라서 고차함수를 만들어 각 select에서 사용하도록 했다.

const onSelectChange = (type: string) => (item: any) => {
    setInput({ ...input, [type]: item.value });
  };

...

return (
    <Select
        value={yearOptions.find(({ value }) => value === +input.year)}
        onChange={onSelectChange('year')}
        options={yearOptions}
        styles={selectStyles}
    />
    
   <Select
        value={monthOptions.find(({ value }) => value === +input.month)}
        onChange={onSelectChange('month')}
        options={monthOptions}
        styles={selectStyles}
    />
      
)

카테고리 컴포넌트

카테고리 컴포넌트는 선택된 수입/지출 타입에 따라서 보여줘야하는 카테고리가 달랐다. 따라서 accountbook store의 카테고리 중에서 타입이 선택된 타입과 같은 것만 나오도록 filter를 이용해서 구현했다.

 {categoryPool ? (
            categoryPool
              .filter(({ type }) => type === input.type)
              .map(({ name, icon, type }) => (
                <label key={name}>
                  <input
                    type="radio"
                    name="category"
                    data-name={name}
                    data-icon={icon}
                    data-type={type}
                    onChange={onCategoryChange}
                    checked={name === input.category.name}
                  />
                  <span>{name}</span>
                </label>
              ))
          ) : (
            <h3 className="no__category">카테고리를 설정해주세요.</h3>
          )}

카테고리 목록의 필터와는 다르게 한 개만 선택을 할 수 있어야 하기 때문에 input type radio로 만들었다.

처음에는 input 다음에 나오는 것을 span이 아니라 div로 만들었다. 그러니 글자가 길어지거나 할 때 div의 크기가 글자의 길이에 따라서 조정이 되는 것이 아니라 글자가 넘어가버리거나 하였다. 그래서 span으로 변경해 글자의 길이에 따라서 길이가 조정이 되도록 만들었다. 또한 카테고리의 종류가 많아지면 자동으로 다음 행으로 넘어가 보이게 만들기 위해서 flex-wrap: wrap;을 사용했다.

결제수단 컴포넌트

결제수단 컴포넌트도 기본적으로 카테고리 컴포넌트와 비슷하다. radio로 이루어져 있고 input을 안 보이게 처리하고 input의 다음 것으로 보이는 것을 대신하게 했다. 다만 다른 점은 카드 모양으로 만들고 hover시 애니메이션을 만들었다.

카드가 처음에 겹쳐져 있는 모습으로 보이게 하기 위해서 첫번째가 아닌 나머지는 모두 왼쪽 margin을 마이너스 값을 주어 겹쳐져 있도록 했다. 그리고 hover시에 해당 아이템이 위로 살짝 나오고 겹쳐지지 않은 모습으로 나오게 하기 위해서 transform을 사용했다.

.payment__card__item:not(:first-child) {
  margin-left: -100px;
}

.payment__card__item:hover {
  transform: translateY(-10px);
}

.payment__card__item:hover ~ .payment__card__item {
  transform: translateX(105px);
}

금액 컴포넌트

금액 컴포넌트는 처음에 input type number로 만들었다. input의 값이 숫자이기 때문에 그렇게 만들고 만약 모바일에서 값을 넣을 경우 키보드가 숫자만 나오기 때문이었다. 그런데 돈을 나타내는 것이기 때문에 천원 단위로 쉼표가 반드시 찍혀야한다는 피드백이 있었다.

처음에 쉼표가 나오게 하기 위해서 생각한 것은 input type을 text로 변경하고 들어온 값에 대해서 쉼표를 찍어서 value를 바꿔주는 것이었다. 그런데 text로 변경하면 키보드가 모바일에서 숫자만 나오지 않는 것이 안 좋다고 생각해 input 전체를 label로 감싸고 input의 color를 투명하게 하고 label의 background-color를 투명하게 해 input에는 숫자만 입력이 되지만 label에는 쉼표가 추가된 문자열이 나오도록 했다.

하지만 label에는 커서가 나오지 않았다. pointer의 모양은 텍스트를 클릭 할 때의 모양으로 css 속성을 통해서 바꿀 수 있었으나 input의 중간 값을 바꿀 수는 없었다. 이런점이 치명적이라는 피드백이 있어 결국 input을 text로 바꿔 해결했다.

수정? 추가?

거래내역 아이템을 선택해 수정을 눌렀을 때도 추가를 눌렀을 때와 마찬가지로 모달창이 뜨지만 현재 입력되어 있는 값이 넣어진 상태로 나오고자 했다. 이렇게 하기 위해서는 모달 store의 input에 값을 넣어주면 되었다. 그리고 submit이 되었을 때 추가와 수정이 다른 동작을 하게 만들기 위해서 input에 _id가 있으면 수정, 없으면 추가로 구현했다.

Clone this wiki locally