이번주 주간 목표

예외 케이스 등 디테일 기능을 구현하고 일단 프로젝트를 마무리 해보자.
미뤄둔 알고리즘 공부를 다시 병행하면서
틈틈이 리액트로 리팩토링하는 것에 대해 계획을 세워야겠다.

하하 5주차는 예상대로 진행된게 별로 없다.



2022.11.28 ~ 11.29

소비내역 인풋 필수값 디자인 적용 및 기능 구현

인풋에 값을 입력하라는 메세지를 별도 디자인으로 추가하려고 하다가
인트로에서 예산 셋팅할때와 동일하게 값을 입력하지 않으면 버튼이 활성화 되지 않도록 조건값을 추가했다.
placeholder에 (필수)라는 텍스트를 추가했는데, 좋은 ux는 아닌것 같지만 일단 빠르게 진행하기로.

소비내역 내용들 최대글자수 체크

천만원 단위까지 입력할 수 있게 하고싶은데.. 사이즈상 그렇게까지 안나온다.
가볍게 소비기록용 가계부로 기획했으니 백만단위까지도 괜찮지 않을까 하고 그렇게 정리했다.
금액 입력하는 인풋은 데이터 타입이 일정하므로 maxlength로 간단하게 할 수 있었다.

타이틀도 일단은 짧지만 한글기준 최대 10자면 UI가 안무너질 것 같다.
근데 한글과 영문 모두를 합해 제한해야하기 때문에 maxlength로는 안되고 byte 제한이 필요하다.
디자인하면서 요청만하다가 직접 만들어보니 싱기방기

그리고 여러 자료들을 보다보니 어떤 기능을 구현하기전에 상세스펙 정의를 하는것이 좋겠다는 생각이 들었다.

  • 한글, 영문을 자유롭게 입력
  • 최대 20바이트까지 가능
  • 20바이트를 넘어가는 글자는 무시
글자수 byte로 제한하기

찾아보는 글들이 대부분 텍스트를 인코딩하기 위해 escape 함수를 이용하는 사례인데,
코드 작성시 escape에 취소선이 그어져서 찾아보니 더이상 추천하지 않는 deprecated 메소드라고 한다.
그리고 escape 함수를 쓰지 않는 사례를 찾으려고 많은 자료들을 찾아봤다.

문자열의 실제 바이트 수 계산
텍스트를 바이트로 제한하는 컴포넌트 만들기
인풋 글자수 제한

여러 코드를 참고해서 내 프로젝트에 맞게 정리했다.
입력된 값의 텍스트를 각각 분리하고, charCodeAt 함수를 통해 각 글자의 유니코드를 받아와서
128이상이면 한글이니 2바이트, 이하면 영어이므로 1바이트가 더해지도록 했다.
count 값을 통해 totalByte가 maxByte를 넘으면 slice 함수로 뒤에 입력되는 값을 잘라낸다.
(다음날 코드를 수정했으므로 코드는 아래에..)

드디어 완료했다고 좋다고 테스트 영상을 기록하다가 버그 발견
글자를 빠르게 입력하면 인풋에 값이 들어가버린다.. 화살표를 왔다갔다하면 글자가 다시 지워지긴하는데..
화면에 내가 따로 최대글자수를 알려주고 있지 않기떄문에 UX가 상당히 어색해진다 ㅠㅠ
고민필요…



2022.11.30

텍스트 길이 제한 기능을 좀 더 정리

은행 어플에서 송금시 상대방 통장에 표시되는 텍스트를 제한하는 케이스를 확인했는데
메세지를 숨겨놓고 텍스트 제한을 하거나, 안내메세지를 함께 띄우는 경우도 있었다.
일단 인풋에 텍스트가 확실히 정리되길래.. 또 어떤방법이 있을까 고민했다.

maxByte를 넘겼을때 아예 인풋 입력을 막을 수 없을까 하다가 readonly를 붙여봤고
이정도면 원하는 기능이 만들어진 것 같아서 이렇게 정리하기로 했다!
그래서 바이트 제한 함수는 이렇게 되었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// limit the number of characters
function limitSpendTitleByte() {
  spendTitle.removeAttribute("readonly"); // 함수 실행시 일단 readonly 해제

  const titleValue = spendTitle.value;
  const maxByte = 20;
  let totalByte = 0;
  let count = 0;

  for (let i = 0; i < titleValue.length; i++) {
    totalByte += titleValue.charCodeAt(i) > 128 ? 2 : 1;
    // 유니코드가 128이상이면 2바이트, 이하면 1바이트로 더하기
    count++; // 입력값 컷을 위한 임시 변수

    if (totalByte > maxByte) {
      spendTitle.value = titleValue.slice(0, count - 1);
      // count - 1 인덱스까지 잘라서 value에 넣어주기
      spendTitle.setAttribute("readonly", ""); // 더이상 입력 못하게 readonly 추가
    }
  }
}

spendTitle.addEventListener("keyup", limitSpendTitleByte);
// 키보드 입력에 따른 이벤트 함수 실행

img01 드디어 원하는대로 동작하는 인풋!


하지만 역시 순순히 끝날리 없지! 놓친 부분이 있었다.

img02 영문은 20바이트 길이가 너무 길다..

img03 일단 말줄임표 처리..

하하 내가 이럴려고 글자수 제한코드를 이렇게 열심히 만들었나 자괴감 들지만
넘치는 텍스트는 일단 truncate 코드를 mixin으로 정의해서 추가해줬다..

소비 총 금액 summary 영역에 반영

구현해야하는 내용을 정리해보면

  • 소비내역 추가 모달 저장 시 expense값을 totalSpend에 반영
  • 반영 시 금액단위 콤마 추가
  • totalSpend / budget 을 소비 그래프 width값에 반영 (트랜지션 추가)
  • 소비그래프 50%까지는 초록, 80%까지는 노랑, 그 이상 빨강색으로 변경

img04 gif 용량때매 다급한 테스트 기록..ㅎㅎ

위 내용은 생각보다 별 무리없이 구현할 수 있었다.

그러나 여기서 이슈 발생.
지금까지 작업하면서 근본 원인 해결을 못하고 타협한 부분이 몇가지 있는데..

img05



2022.12.01

active-bar의 width가 100%인데 왜 저런 틈이 생겼냐면
absolute로 bacground-bar랑 위치를 잡았는데 top -1px, left -1px을 줘야 비는 부분이 없었기 때문..
왜 그런지 깊이 고민하지 않고 넘어갔던 부분이라 앗차 싶었고
별도 테스트 문서를 만들어 position 속성에 대해 이리저리 테스트하며 다시 공부했다.

  • width, htight값을 % 로 잡을때는 border를 제외한 영역을 전체로 잡는다.
  • box-sizing을 content-box로 바꾸고
    top, left를 border만큼 마이너스로 줘서 위치를 잡으면
    width, htight값을 % 로 잡아도 같은 사이즈로 완전히 겹쳐놓을 수 있다.

img06

그리고 테스트하다보니 새로운 버그 발견 🚨
설정한 기간이 아직 끝나지 않았음에도, 12월로 바뀌니 설정 기간이 12월 5일 시작으로 바로 넘어가버렸다.
앞서 설정한 마지막날인 12월 4일까지는 유지가 되어야하는데..
투두리스트 하나 더 추가!

소비내역 추가 관련 함수 분리

소비내역을 추가하기 위해 많은 단계가 필요하다보니 한개의 함수 안에 다양한 동작이 섞여있었다.
그래서 각 동작을 주제별로 묶어 별도의 함수로 분리하는 작업을 했다.

소비내역 추가 동작을 위한 플로우
  • handleModal 모달을 띄우고 닫음 (toggle class)
  • limitSpendTitleByte 인풋 글자수 제한
  • activeSaveButton 필수 인풋이 입력되면 저장버튼 활성화
  • saveSpend 입력한 내용을 로컬에 저장
  • resetModalInput 인풋 리셋
  • submitSpendList generateSpendList 저장된 내용대로 list 생성

코드가 작동하는 플로우를 쭉 다시 읽어보니 정말 엉망진창이다.
처음부터 정리된 상태로 구현할 수준이 아니라 생각해 진도나가는데 집중했는데
이제는 슬 리팩토링이 필요하겠다. 일단 기능 구현부터 마무리해보자.

리스트 클릭 시 해당 내용의 모달 다시 띄우고 삭제기능 추가

  • 리스트를 클릭하면 해당 데이터를 가진 모달이 다시 띄워져야 한다.
    리스트는 동적 생성된 것이고, targetList를 잡아야함.
  • 내역 삭제하기 버튼 추가 필요
  • 삭제 버튼을 누르면 리스트가 사라지고
  • 내용을 바꾼다음 저장하기 버튼을 누르면 데이터가 수정 저장된다.

동적으로 생성한 list 태그에 이벤트 바인딩시키는 것은
전에 관련 기능을 구현해본 적이 있는데, 그때는 단순히 체크박스를 클릭하면 되는 내용이었다.
이번에는 title, memo등 여러 태그가 합해진 list 영역에 이벤트를 걸고싶은 것이라 조금 다른 상황.

일단 list 조상 중 고정적으로 작성된 div에 이벤트를 걸고
targetList를 e.target.closest('.spend-list-item') 으로 정의했다.
타겟의 부모중 targetList가 있다면 원하는 코드를 실행하도록 함수를 작성했다.



2022.12.02 ~ 04

구조 정리 및 리네이밍

요즘 좋은 인사이트를 얻기위해 다양한 글들을 많이 찾아보고 있는데
기술 부채는 낮은 코드 품질에 대한 것이 아니다
이 글이 계속 생각났다.

리스트 삭제 기능을 구현하려니 역시나 지금까지 쌓여있던 빚더미 같은 코드가 마구 나를 혼란스럽게 했다.
그래서 결국 컴포넌트와 기능 동작 구조를 제대로 정리하고, 리팩토링을 먼저 진행하기로 했다.
피그잼을 이용해 구조를 그려나갔다.

img07 계속 수정하고 추가하는 중..

혼자 공부하다보니 클래스, 변수, 함수명 등을 정하면서 이래도 되나 싶은 것들이 꽤 있었기 때문에
관련 문서들도 찾아보며 공부했다.
(네이밍 컨벤션 이라는 키워드를 알게되었다.)

Naming Classes — Why It Matters, and How to Do It Well
Programmer’s Guide on How to Name Things
함수 네이밍 컨벤션 (Naming Method)

  • 동작의 결과가 명확히 인지되도록 네이밍 할 것 (함수 이름이 길다고 고민하지말자)
  • 초반에 네이밍 구조를 정리할 것
  • 함수명은 변동 가능성을 염두에 두고 정의해야하지만
  • 변수나 클래스명은 현재 역할을 명확하게 인지시킬 수 있을 것
  • 자식 클래스는 부모 클래스명에서 더 구체화된 버전일 것
  • 기능적으로 더 작게 나눠서 정의할 것
  • 이벤트 함수는 on 으로 시작하는게 좋음



전체적으로 필요한 화면, 기능등 다시 훑고 수정 작업 중.
다음주엔 진짜 1차 마무리 해야지

Leave a comment