[solo project] 솔로 프로젝트 회고
코드스테이츠 부트캠프에서 솔로프로젝트를 진행했습니다.
디자인 시안은 피그마로 전달받았고 데이터는 Swagger API 문서를 참고하여 불러왔습니다.
🔨구현 기능
- 모달
- redux를 사용하여 모달창 on, off의 상태를 전역에서 관리하였습니다.
- 아이템 컴포넌트 이미지를 클릭하면 모달창이 열리게 구현을 했습니다.
- 모달이 닫히는 경우는 다음과 같습니다.
1. 모달창 위의 X 버튼 클릭
2. 모달 이외의 영역 클릭 - 이미지 위의 북마크 기능을 수행하는 별이미지에는 클릭 시 event.stopPropagation() 메서드를 사용해 이벤트 버블링을 막았습니다.
- 무한스크롤
- react-intersection-observer 라이브러리의 useInView훅을 사용했습니다.
- 불러오는 데이터의 최하단 부분요소에 ref를 설정하고 해당 요소가 뷰포트에 들어온다면 inView가 true 상태가 되고 그때마다 데이터를 16개씩 불러올 수 있게 useEffect 훅을 사용했습니다.
- 북마크기능
- 북마크 된 아이템 데이터의 id값을 localStorage에 저장하여 새로고침을 했을 때도 북마크 한 아이템이 남아있게 하였습니다.
- redux-toolkit을 사용해서 전역 상태 관리소인 store에 localStorage에 북마크 된 아이템의 id 배열을 state로 저장하였습니다.
- 아이템 컴포넌트의 별을 클릭할 때 아이템 데이터의 id를 store로 보내서 원래 저장된 id의 배열중에 클릭된 id가 있는지 여부를 검사하여 있다면 배열에서 해당 id를 제거하는 splice를 없다면 push를 해주었습니다.
- 타입별 데이터 렌더링
- 필터 버튼을 클릭하면 상태 변경함수에 해당 type을 인자로 넘겨서 type의 상태를 변경해 주었습니다.
- 데이터를 아이템 컴포넌트에 각각 넣어주기 전에 filter함수로 각 데이터의 type이 클릭된 타입과 같은 데이터를 걸러주어 해당 타입의 데이터만 렌더링 되게 하였습니다.
- 토스트알람
- react-toastify 라이브러를 사용하여 구현하였습니다.
- 최상위 컴포넌트인 App.js에서 토스트 컴포넌트를 위치시키고 북마크 상태를 관리하는 store에서 해당 아이템 북마크 상태여부에 따라 toast메서드를 통해 알람 메시지를 설정하였습니다.
- 토스트알람 유지시간 등은 라이브러리에서 제공하는 옵션을 사용하였습니다.
🔨만난 오류들
- 북마크기능 구현하기 위한 시도
data
.filter(d=>{
const bookmarkData = JSON.parse(localStorage.getItem('bookmark'))
return bookmarkData !== null && bookmarkData.includes(d.id)
})
당시에 무슨 생각으로 작성했는지 모를 코드입니다.
const bookmark = useSelector(state => state.bookmark)
data
.filter(d=>{
return bookmark && bookmark.includes(d.id)
})
스토어에 저장된 북마크 상태를 활용해서 아이템 컴포넌트에 데이터 뿌려주기를 구현했습니다.
하지만 이 코드는 곧바로 오류를 만나게 되는데 그 상황은 아래와 같습니다.
- 메인화면에서 북마크 아이템이 들어오는 순서
data.filter((d) => {
return bookmark && bookmark.includes(d.id)
})
문제 : 북마크 된 아이템이 메인화면에 들어올 때 순서대로 들어오지 않는 오류가 있었습니다.
원래 생각대로라면 왼쪽부터 순서대로 들어오는 것인데 북마크를 클릭할 때마다 세 번째로 클릭된 아이템이 첫 번째 위치로 들어간다던가 갑자기 서로 위치가 바뀌는 등의 현상이 있었습니다.
데이터를 filter함수를 이용해 타입별로 걸러주고 나서 slice함수로 첫 번째 요소부터 4번째 요소까지 아이템 컴포넌트로 넘겨주는데 이때 데이터가 들어오는 순서 보장이 안되기 때문에 랜덤으로 화면에 랜더링이 되었습니다.
해결 : 북마크 된 아이템의 id를 기준으로 데이터를 filter 해주었습니다.
bookmark.map((id) => {
return data.filter(d=>d.id===id)[0]
})
이 부분은 처음부터 데이터를 받아오는 방법을 잘못생각해서 효율적이지 못한 코드를 작성한 것 같아 다음 프로젝트에서는 처음부터 받은 데이터를 어떻게 다뤄야 할지 생각을 잘해야겠습니다.
- 햄버거 아이콘 클릭 시 버블창 다른 컴포넌트에 가려짐 현상
문제: 버블에다 z-index 주면 되겠다고 생각했지만 올라오질 않아서 무엇이 문제이지 했는데 결국 header 컴포넌트를 전체적으로 올려줘야 하는 문제였습니다.
해결 : header 컴포넌트는 z-index:1 이 되어서 나중에 모달창 구현시 모달창 제외영역에 header 컴포넌트가 포함이 안되어 그 부분 또한 z-index:2로 설정해 주었습니다.
- 햄버거 버튼 영역 제외 클릭 시 닫힘 기능
header 컴포넌트에서 햄버거 버튼을 클릭하면 메뉴창이 나타나고 메뉴창 이외의 영역을 클릭했을 때 닫히는 모달창과 같은 기능을 수행합니다. 그래서 이 부분을 컴포넌트로 만들어줬으면 좋았을지.. 도..? 모르겠습니다. 아무튼 header 컴포넌트 안에서 상태를 관리했더니 header 컴포넌트 영역한정이라 App.js로 상태관리하는 곳을 변경하였습니다.
문제 : 이때 메뉴버튼 또한 메뉴창 제외 영역이므로 메뉴창이 나타나기 도전에 false가 되어버려 창이 열리지 않는 현상이 일어났습니다.
해결 : 상태를 false로 변경하는 경우는 메뉴창이 떠있을 때 즉 true 상태일 때만 수행되도록 변경해 주었습니다.
const [isOpen, setIsOpen] = useState(false);
const clickMenuOpenHandler = () => {
setIsOpen(true);
};
const clickMenuCloseHandler = () => {
if (isOpen) {
setIsOpen(false);
}
};
🔨의문이 들었던 점
- css module에서는 클래스명은 무슨 네이밍 규칙을 쓸까?
소소하게 궁금한 것이었는데 GPT가 케밥표기법 ex) modal-container을 일반적으로 쓴다고 합니다. 하지만 저는 이번 프로젝트에서 습관적으로 카멜케이스를 사용을 해서 앞으로는 주의해서 사용해야겠습니다.
- 사이즈는 단위는 px로? rem으로?
절대단위보다 상대단위를 사용하는 게 좋다고 합니다. 예컨대 글자를 rem으로 설정 시 사용자가 직접 브라우저 내에서 글자 크기 조절이 가능하지만 px로 설정 시 불가능하다고 합니다. margin이나 padding은 rem으로 많이 주긴 하지만 image의 width 같은 건 주는 경우가 없다고 합니다.
냠냠맨님이 알려주신 이번에 프로젝트를 하면서 유용하게 사용한 사이트입니다. px과 rem을 계산해 주는 사이트인데 개인적으로 숫자 뒤에 px, rem이라는 문자열도 붙여줬더라면 복붙 하기 더 좋았을 거 같습니다. 게으른 나.. ㅇ<-<
https://nekocalc.com/px-to-rem-converter
PX to REM converter (instantly and bidirectional)
PX ↔︎ REM conversion tables PixelsREM1px0.06rem2px0.13rem3px0.19rem4px0.3rem5px0.3rem6px0.4rem8px0.5rem10px0.6rem12px0.8rem14px0.9rem15px0.9rem16px1rem18px1.1rem20px1.3rem24px1.5rem25px1.6rem28px1.8rem32px2rem36px2rem40px3rem44px3rem48px3rem50px3re
nekocalc.com
- 라이브러리를 사용하는 것은 좋을까?
이번에 프로젝트에서 몇 가지의 라이브러리를 사용했습니다. 토스트알람이라던가 무한스크롤 같은 경우는 순수 리액트로만 구현할 수 있지만 라이브러리 사용이 궁금하기도 하고 편하기도 해서 사용을 해보았습니다. 해당 기능을 구현을 하고 나니 무언가 알 수 없는 죄책감 같은 게 밀려왔는데 질의응답시간에서 좋은 라이브러리라면 사용해도 좋지만 라이브러리가 하루아침에 사라진다면 기능을 구현할 수 있는 능력이 있어야 한다고 하셨습니다. 맞는 말씀이라 생각했고 동작 원리만 안다면 언제든 도전해 볼 수 있으니 리팩토링을 하면서 라이브러리 없이 구현을 해봐야겠습니다.
리액트 또한 라이브러리이고 같이 스터디를 하시는 분께서 어떤 기능을 리액트로 구현 전에 바닐라 자바스크립트로 구현해 보자 하신 적이 있는데 지금에야 비로소 그 의도를 이해할 수 있게 되었습니다.
🔨알게 된 점
- 깃 사용법
깃 사용법과 더불어 깃 플로우 전략에 대해서 알게 되었습니다.
새로운 프로젝트는 main을 기반으로 별도 브랜치를 생성해서 각 기능에 맞는 브랜치를 생성해서 로컬에서 작업을 진행하고 원격 브랜치에 push를 해줍니다. 코드리뷰를 받고싶거나, 코드를 병합할 준비가 되었다면 PR(Pull Request)를 만들어줍니다.
이번 프로젝트에서는 혼자 진행하기 때문에 따로 main에서 나만의 브랜치를 만들진 않았지만 추후 협업에 있어서는 깃 전략에 따라 작업을 진행하게 될 것 같습니다. 여기서 커밋 메시지와 브랜치명에 통일감을 주어 가독성을 높여야 함을 느꼈습니다.
브랜치명을 처음에 잘못설정하면 바꾸기도 여간 귀찮은 일이 아니었고 커밋 메시지 또한 중구난방으로 적어놓은 모습을 보니 제가 작성했음에도 불구하고 알아보기 힘들었습니다.
손가락에 url들을 꼬깔콘처럼 들고 다니시는 까악이 님이 공유해 주신 커밋 컨벤션을 앞으로 잘 따라야겠습니다.
Git | git 커밋 컨벤션 설정하기
개발자로 시작한지 얼마 안되고 나서, 첫 직장은 지금도 다니고 있는 모든것을 처음부터 새로 시작하는 스타트업이다.프론트엔드는 작성자 혼자 뿐이었고, 아무것도 모르는 주니어 개발자가 하
velog.io
깃 명령어 또한 헷갈리는 점이 많아 어려웠습니다.
아래와 같이 이번에 자주 사용한 명령어를 흐름에 따라 정리해 보았습니다.
git switch feature/header
git add ./src/components/header
git status
git commit -m feature/header
git push --set-upstream origin feature/header
git branch feature/footer
git switch feature/footer
git branch -d feature/header
...
git pull origin main
- 컴포넌트 나누는 것, 데이터 받아오는 흐름
처음 과제를 받았을 때 header, footer 컴포넌트 pages를 나누는 작업 빼고는 어떻게 구조를 짜야할지 막막했습니다. 그래서 마구잡이로 컴포넌트를 생성하다 보니 데이터를 네 번이나 받아오고 파일마다 중복된 코드가 많아지게 되었습니다. 이 부분은 감을 잡기도 어려워서 질의응답시간에 질문을 했는데 시간이 모자라서 다 듣진 못했지만 이번에 진행한 과제를 기준으로 다시 생각해 보고 다른 분들 코드를 참고해야 할 것 같습니다.
- 서버데이터를 postman으로 확인해서 파일로 저장하면 서버처럼 사용이 가능하다!
- 토스트 컴포넌트를 넣으려는 시도
기능을 편하게 구현하려고 만들어진 라이브러리인데 사용법을 정확히 숙지하지 못하고 사용하면 사용 안 하느니만 못하다는 깨달음을 얻었습니다. 토스트 컴포넌트를 bookmark store의 reducer함수에 넣으려고 하고, 알람창의 메시지를 dispatch를 통해 넘겨주려는 허튼짓을 시도했었습니다..
🛌 마무리
평소 12시에는 잠자리에 드는데 솔로 프로젝트 기간동안 2시는 기본으로 넘겨서 자고 8시에 일어나서 프로젝트를 하다 보니 많이 지치고 피곤하긴 했습니다. 하지만 동기분들과 으쌰으쌰하면서 오류에 부딪히고 해결하는 과정이 재미있어서 나름 즐겁게 진행한 것 같습니다. 그 해결하는 과정 속에서도 저의 부족한 점을 정말 많이 발견했고 그 부분을 모두 이 회고에 담고 싶었지만 그 순간의 기록을 놓친 게 참 아쉽습니다. 전체적으로 부족하지만 그래도 잃은 안색보다 얻은 지식이 많았던 이번 프로젝트에서 많이 배웠습니다. 구현했던 기능들을 바탕으로 새로운 프로젝트를 시작해 볼 것이며 꼭 git 사용법을 제대로 익혀야겠습니다.