구경하러 가기 👉 https://accountbook-5a92a.web.app/
앞으로 프로젝트가 하나 끝날때마다 해당 프로젝트의 진행과정을 간단하게 정리 하려고 한다.
이번에 가계부를 만들며 진행했던 일들을 간단히 나열하면 아래와 같다.
오프라인에 모여서 아이디어를 내며 어떤 프로젝트를 진행할지 회의를 했다.
그 중 다른 팀원중 한명이 낸 아이디어인 가계부
를 만들게 되었다.
첫날에는 각자 어떤페이지를 어떤식으로 만들지만 간단하게 정하고 헤어졌다.
그러고서 내가 CRA
로 프로젝트를 생성했고 가계부 제작을 시작하게 됐다.
가계부에서 핵심기능은 모달
이라고 생각했고 모달
부터 개발을 시작했다.
중요한 부분이라 생각했던 이유는 이렇다.
4개의 페이지중 3개의 페이지에서 모달
이 사용됐으며
핵심기능인 사용내역 수정, 추가에 모두 모달이 사용되기 때문이다.
다른모양의 모달 3개가 필요했기에 몇번의 수정을 거쳐
다른 팀원도 간단하게 사용할 수 있도록 useModal
훅을 만들었다.
하지만 이를 사용하려면
redux
라이브러리를 설치하고store
를 만들어줘야 하기 때문에
다음 프로젝트에선redux
가 아닌Context API
를 사용해 이부분을 수정해줄 예정이다.
그러고 간단하게 쓸 수 있도로 npm으로 패키지를 만들자!
월 선택 컴포넌트의 경우 모달을 개발하니 금방 개발했다.
해당 컴포넌트는 처음 ~ 끝의 년/월을 지정해주고 모달에 렌더링 되게 해줬다.
그러고서 클릭을 하면 해당월로 store
의 상태가 변경된다.
월 선택 모달을 개발하며 문제가 됐던 부분은 이전에 선택한 월을 맨 위에 표시해주는 문제였다.
이건 사용자가 click
한 컴포넌트의 ref
를 forwardRef
를 사용해서 넘겨줬다.
그 후 월 선택 모달이 렌더링되면 아래의 코드를 실행해 문제를 해결했다.
goRef.current?.scrollIntoView({ behavior: 'smooth' });
처음에 달력을 만들때 그냥 display:flex
를 사용해 만들까도 생각했다.
하지만 그것보단 table
을 사용하는게 훨씬 좋아보였다.
만약 table
을 사용하지 않고 구현하면 행
, 렬
을 따로 구현해야 했기 때문이다.
그래서 달력을 만들기전에 다른 컴포넌트에서도 사용이 가능한 Table 컴포넌트를 먼저 만든 후
Calendar 컴포넌트를 생성했다!
만드는 과정은 링크에도 써놨지만 한번더 정리하면 이렇다.
- 2차원 배열에대한 data를 넣으면 렌더링 되는
Table
컴포넌트를 생성한다.- 달의 시작 요일은 매 달 다르다 그래서 이를 계산한한다.
- 1 을 토대로 날짜들에 대한 2차원 배열을 생성한다.
- 2에서 만든 데이터를 0에서 만든 테이블에 넣고 스타일링을한다.
Firebase의 사용법은 공식문서에 정말 자세하게 설명 되어있어서 따로 설명을 보지 않아도 됐다.
하지만 문제가 하나 있었는데 Firebase의 Firestore은 익숙했던 SQL이 아니라
NoSQL기반의 데이터베이스이고 이는 비정규화
라는 개념을 통해 모델링한다는 것이다.
SQL에서는 정규화를 하는 이유는 데이터의 중복을 없애고,
DB 용량을 줄이고 앞에서 말한걸로 인한 문제를 없애기 위해서다.
정규화 단계가 여러개가 있는데 내용이 너무 길어지므로 여기선 다루지 않겠다.
비정규화
때문에 어떻게 모델링할지 정말 많이 고민이 됐었다.
정규화
하지 않으면 중복데이터가 생기는데 이는 어떻게할까?
결론은 비정규화
라는 개념이 중복을 허용하니 그냥 고민 자체가 필요가 없었다.
https://www.youtube.com/watch?v=lW7DWV2jST0&ab_channel=Firebase
위의 Firebase에서 설명해주는 위의 유튜브가 정말 많은 도움이 됐다.
해당 내용은 간략히 설명하면 이렇다
중복되는 데이터의 업데이트, 삭제는 빈번하지 않고 드물기 때문에
대부분의 경우에 성능상 문제가 없고
오히려 이로인해 Table간의 Join이 줄기때문에 비용이 줄어서 I/O 성능이 올라간다.
📌만약 중복되는 데이터의 수정이 빈번하다면 Application상에서 해당 테이블을 참조하면된다.
이렇게 모델링 방법을 해결하고나니 나머지는 일사천리였다.
redux-toolkit
의 createAsyncThunk()
를 사용해 비동기 통신을 해주었고
이를 사용해 만든 액션함수를 필요할때마다 사용하였다.
다 만들고보니 사소한 문제가 몇개 있었다.
첫째, 하나의 컴포넌트에서 useModal
을 여러번 사용해 모달이 두개이상 있을때 렌더링문제
둘째, 모달의 세로길이가 길때 스크롤을 하면 모달 뒷부분이 스크롤되는 문제
어디가 문제인지 감이 안와서 하루정도 헤맸다.
문제는 하나의 컴포넌트에서 useModal을 사용하면 내부에서useState
로 상태를 관리하기 때문에
모달을 여러개 사용하는 컴포넌트 전체가 리렌더링이 발생해 생기는 문제였다.
따라서 내부의 상태들은 외부의 store
에 저장해 해당 문제를 해결했다.
이건 뭐가 문제인지 알아서 바로 해결이 가능했다.
position
이 fixed
가 아닌 엘레먼트는 스크롤이 가능한데, fixed
라면 스크롤이 불가능하다.
근데 top
이 0으로 변경되므로 top
을 저장하고 변경했다가 모달이 닫히면 원상복구 시킨다.
const useBackgroundBlockScroll = () => {
useEffect(() => {
document.body.style.cssText = `
position: fixed;
top: -${window.scrollY}px;
`;
return () => {
const scrollY = document.body.style.top;
document.body.style.cssText = '';
window.scrollTo(0, parseInt(scrollY || '0', 10) * -1);
};
}, []);
};
이를 구현한 useBackgroundBlockScroll
코드인데
useModal에 해당 커스텀훅을 사용하면 모달이 열릴땐 스크롤이 막히고 닫히면 작동한다.
Firebase
를 통해 호스팅을 했기에 GithubAction
을 사용해봤다.
로컬에서 배포를 해도 되지만 이번기회에 배포를 자동화 시키고 싶었기 때문이다.
그러기 위해서 따로 GithubAction
을 설정해도 되지만 Firebase를 설정하면서
자동으로 설정도 가능하다. 자세한 과정이 보고싶다면 !참조!
추가로 .env
파일로 환경변수를 관리해줬기에 이에대한 설정도 해줘야한다.
이건 Github
의 Actions secrets
를 설정해주면 되는데
이역시 다른 글에 적어놨다.
이렇게 설정을 전부 끝마치면 PullRequest시 임시 preview 주소가 생성이 되고
master 브랜치고 merge시 자동으로 배포가 완료된다.
Actions쪽을 보면 배포가 성공적으로 완료됐다고 표시되는걸 볼 수 있다.
예전에 프로젝트를 진행할때는 로컬에서 빌드 -> 배포를 해줘야하는데
이렇게 자동화를 해두니 정말 편리했고 왜 지금까지 시도하지 않았는지 모르겠다.
이번에 팀원들과 가계부를 한달이란 긴 시간동안 만들었다.
만들면서 크게 느낀점이 한개가 있는데 팀원과의 소통이 부족했다는 점이다.
소통이 부족했기에 다른 팀원이 어떤 코드를 작성했는지도 모르고
나도, 팀원도 똑같은걸 계속 물어보는 지경에 이르렀다.
이로인해 일정관리도 어려웠고 코드작성도 지체됐다.
그래서 회의를 통해 아래와같이 협업하기로 결정했다.
끗!