Skip to content

캘린더 페이지

김민섭 edited this page Dec 19, 2020 · 2 revisions

📌 캘린더 페이지 구현

구성

캘린더 페이지는 크게 아래와 같은 컴포넌트로 이루어져 있다.

  1. MenuBar : 헤더역할. 여러 탭으로 이동할 수 있는 버튼이 존재. 년/월 정보 존재.
  2. TotalContent : 년/월에 표시된 정보에 해당하는 수입 총액과 지출 총액을 표시.
  3. Calendar : 년/월에 표시된 정보에 해당하는 달력표시. 일자에 맞는 내역이 있을 경우, 수입 총액과 지출 총액 표시.
  4. DetailModal : Calendar에서 일자를 클릭하면 구체적인 내역을 보여준다.
  5. PaymentModal : MenuBar의 결제수단 관리 버튼을 누르면, 페이지 이동이 아닌 모달 형식의 결제내역탭이 보여진다.

구현

위의 컴포넌트중 1번과 5번은 공통으로 사용되는 컴포넌트를 이용했다.
따라서 구현에 관해서는 2,3,4번 컴포넌트에 대해 정리하려고 한다.

1️⃣ TotalContent

  1. MenuBar의 년/월이 변경됨에 따라 다시 렌더링되어야 하므로, 전역의 DateInfo를 observe하도록 연결해준다.
  2. DateInfo의 년/월에 해당하는 내역정보들로부터 수입 총액과 지출 총액을 구한다.
    이때, 해당로직을 컴포넌트에 위치 시킬것인지 store에 위치 시킬것인지 고민을 하였다.
    결론적으로, AccountBook Store에 있는 내역정보로부터 계산을 하는 것이니까 store에 메소드를 만들도록 선택하였다.
    컴포넌트에 함수가 추가 되는 것보다는 store의 메소드를 불러서 사용하는 것이 깔끔하다고 판단하였기 때문인데,
    이런식으로 데이터를 필요한 형태로 가공하는 메소드들을 모두 store에 넣는 것이 나을지, 각 컴포넌트로 모두 분산시키는 것이 나을지는 좀 더 생각해봐야 할 것 같다.
  3. util의 CommaMaker를 사용하여 돈 단위에 적용하는 콤마를 붙여주고, 렌더링 한다.
스크린샷 2020-12-03 오전 2 24 19

2️⃣ DetailModal

  1. DetailModal은 캘린더에서 특정 일자를 클릭했을 때, 해당 일자의 구체적인 내역을 보여주는 모달이다.
  2. 따라서, 특정 일자 클릭 시점에, 해당하는 년/월/일 정보에 해당하는 내역정보들을 모두 가져오는 작업이 필요했다.
  3. 내역데이터를 가진 Store에 년/월/일 정보를 넣어주면 해당 내역들만 return해주는 메소드를 만들어서 사용하였다.
  4. 모달의 특성상 내역창 주변의 overlay를 누르면 모달이 꺼지도록 관리하였다.
스크린샷 2020-12-03 오전 2 49 06

3️⃣ Calendar

  1. Calendar를 그릴 때 핵심은 실제 달력과 요일을 맞추도록 그리는 것과, 해당 일자에 내역정보가 있다면 수입 총합과 지출 총합을 보여주는 것이었다.
  2. 먼저, 달력을 만드는데 있어서 필요한 연산들이 늘어남에 따라 calculateDate라는 util 객체를 만들어 활용하였다.
  3. 또한, Date객체의 여러 메소드들에 대한 이해가 중요했다. 사용한 모든 기능을 설명할 수는 없으므로 참고한 자료를 첨부하였다.
    Date객체 참고자료
    참고로 new Date()에 년/월과 옵션값을 주면 윤년처리는 별도로 해주지 않아도 적용할 수 있었다.
    2/29 확인
스크린샷 2020-12-03 오후 4 10 46
  1. 우선, 달력에서 가장 많은 week가 나오는 경우는 6줄이다. (31일까지 존재하는 달에 1일이 달력의 가장 마지막에 위치하는 경우)
    이 부분을 어떻게 처리할까 하다가 일자가 표기되지 않는 부분은 보여주지 않으면 되겠다 생각하여 우선 테이블에 6줄의 week, 각 week는 7day를 갖도록 하였다.
    이때 Date객체를 이용하여 해당 년/월의 1일에 해당하는 요일을 구해서 첫 week에 지정해주는 것으로 부터 단순히 6줄의 week를 돌며 날짜를 부여한다.
  2. 달력에 오늘을 나타내는 날짜는 애니메이션을 주기 위해 today를 부여하는 작업을 하였는데, new Date()와 현재까지 부여한 날짜를 비교하여 쉽게 설정할 수 있었다.
  3. 또한, 6 week, 7 day의 특성에 따라 각 달의 최대 일수가 넘어가면 더이상 표기하지 않도록 flag역할을 하는 변수를 사용하였다.
  4. 날짜를 부여해 가면서, 만약 해당 날짜에 내역이 존재한다면 수입 총합, 지출 총합을 표기해야 했는데,
    년/월을 입력받으면 일자별 수입 총합, 지출 총합을 객체로 가공하여 return해주는 메소드를 만들어 사용하였다.
    이때 일부러 0원인 경우에도 표기를 하였는데, 수입이 0원인데 지출이 심한경우 또는 지출이 0원인데 수입이 큰 경우등이 사용자로 하여금 더 임팩트가 있을 것 같아서이다.(수정 가능)

시작 요일 변경 기능

  1. 시작요일이 다른 서로다른 달력은 7가지의 경우의 수가 있다.
  2. 동적으로 변화시킬 수 있지만, 디자인 커스텀까지 하기에는 7개의 템플릿을 만들어 사용하는 것도 관리하기 쉬울 것이라는 생각이 들었다.

Issue

  1. 처음엔 반복문이 꽤나 들어가서 자바스크립트로 계산하면서 dom형태를 만들고 innerHTML을 하는 방식으로 가닥을 잡았다.
    리액트가 내부적으로 가상돔을 사용하기 때문에, 이렇게 직접 브라우저의 dom조작을 하면 좋지 않다는 것을 알았지만, 렌더링내에 로직을 녹이는게 복잡해 보여서 이러한 방법을 택했다.
    달력에 내역 값들을 그려주는 작업 또한 마찬가지로, 달력을 그리는 함수를 호출한 후에 내역 값을 그리는 함수를 호출하였다.(그려진 캘린더 위에 insertAdjacentHTML로 넣어주는식)

  2. 위와같이 직접적으로 innerHTML을 사용하다가 dangerouslySetInnerHTML라는 것을 알게 되었는데, 얼핏 두 case가 같다고 생각할 수 있었지만 전혀 달랐다.
    innerHTML로 넣어 준 경우 가상돔에서 실제 돔으로 반영할 때, 최적화를 적용하지 못하게 되어 계속해서 리렌더링하게 되었고,
    dangerouslySetInnerHTML옵션을 통해 dom형태의 스트링값을 넣어주는 경우에는 이러한 문제는 발생하지 않았다.

  3. 하지만 결국 이런식으로 string형태의 돔을 브라우저에 직접 조작한다는 것이 사이트 간 스크립팅 공격의 위험이 있다는 공식문서를 보고, 렌더링 단계에서 위의 작업들을 모두 처리하도록 refactoring하였다.
    공식문서
    그럼에도 꼭 위의 방법을 사용하고 싶다면 아래의 영상을 한번 참고해보면 좋을 것 같다.
    ReactSecurity - Sanitize Content When Setting InnerHTML

Clone this wiki locally