✍️ Intro.
리액트 기초 문법 공부를 끝마친 후 공부한 내용을 활용해보고자 만들었습니다.
💻 사용 기술
Design tool: Figma
Editor: VScode
Lang
- html
- css
- React.js (Router, Redux, styled-component)
배포: Github.io
📅 개발 기간
총 8일 소요 (디자인 2일, 개발 6일)
🗂 폴더 구조
📁 components
- 화면을 구성하는 작은 요소 단위의 엘리먼트들을 구성하는 폴더입니다.
📁 images
- 백그라운드 이미지, 아이콘 등을 모아놓은 폴더입니다.
📁 pages
- Router를 통해 이동하는 페이지들을 구성하는 폴더입니다.
📁 style
- styled-components를 활용한 글로벌스타일과 테마(다크/라이트모드)
- 텍스트 사이즈, 컬러 팔레트, css flex를 JS에 변수로 담아 리액트 문법 내에서 사용할 수 있도록 하였습니다.
🕹 구현 기능
- 라이트/다크 모드 테마 구현 (초기값 라이트 모드로 설정)
- 노트 생성 기능 (uuid 4를 사용하여 고유 아이디 값 부여)
- 노트 수정 기능 (moment.js를 사용하여 수정한 날짜 업데이트)
- 노트 삭제 기능
- 필터 정렬 기능
- 검색 기능
1. Theme Mode
Redux를 사용하여 isDarkMode의 init을 false로 지정한 뒤 다크 모드가 활성화되면 dispatch(darkMode())를 통해
값을 true로 변경하도록 하고, 비활성화가 되면 dispatch(lightMode())를 통해 값을 false로 변경하도록 하였고.
토글 클릭 시 true/false 조건에 따라 dispatch가 실행되도록 하였습니다.
styled-components의 ThemeProvider를 이용하여 버튼 토글 시 조건값에 따라 style/theme.js에 담겨 있는 테마별
스타일 값을 각 컴포넌트들의 props 값으로 전달하여 라이트/다크모드 테마에 맞는 스타일을 변경하도록 했습니다.
라이트/다크모드 클릭할 때마다 해당 값을 'theme'라는 키 값으로 로컬스토리지에 저장 될 수 있도록 하였고,
useEffect()를 사용하여 로컬스토리지 'theme'의 키 값을 확인 후 해당 값을 앱 로드 시 테마의 초기값으로 설정하도록 하였습니다.
2. Create Note
노트를 작성 후 확인 버튼 클릭 시, JSON.stringify()를 이용하여 노트 정보를 객체에서 문자열로 변환 한 뒤 localStorage.setItem()을 이용해 로컬스토리지 저장소에 노트 정보를 배열로 저장합니다.
노트 생성 시 고유 ID값은 uuid4를 이용하여 랜덤으로 생성하고,
노트 생성/수정 일자와 시간은 모두 moment.js를 사용하여 timestamp를 생성하고
노트리스트는 .map()을 이용하여 화면에 리스트를 바인딩합니다.
3. Edit Note
노트 생성과 노트 수정 페이지는 동일한 레이아웃(Detail.js)을 사용합니다.
노트 클릭 시 Route path를 통해 Detail.js에 고유 ID를 파라미터 값으로 전달합니다. 전달받은 ID파라미터 값은 useParams()를 이용해 state로 저장하고, 파라미터 존재 여부에 따라 노트 생성/수정 화면을 분기 처리합니다.
ID파라미터 값이 있다면 노트 수정 페이지 화면을 보여주고, ID파라미터 값이 존재하지 않는다면 신규 노트 생성 화면을 보여줍니다.
Detail.js 페이지 로드 시, useEffect() 함수 내부에서 ID파라미터 값과, localStorage.getItem()을 하여 가져온 'noteList' 배열 리스트의 id값을 .find() 함수를 통해 탐색하고 비교합니다.
ID값이 서로 일치하면 동일한 ID값을 가진 노트 데이터를 가져와 노트 타이틀과 내용을 각 input과 textarea에 바인딩합니다.
노트의 내용을 수정 후 확인 버튼 클릭 시 로컬스토리지에 수정 정보를 업데이트합니다.
노트 리스트 페이지에서 노트 리스트를 렌더링 할 때 fromNow() 함수를 사용하여
각 노트의 updateAt 시간부터 ~ 현재 시간의 간격을 비교 후 '몇 초 전 수정'과 같이 최근 수정 시간을 직관적으로 보여줍니다.
4. Delete Note
findIndex()를 이용해 로컬스토리지 'noteList'의 ID값이 동일한 배열의 인덱스 값을 가져온 뒤 noteIdx 변수에 담습니다.
노트 제거 버튼 클릭 시 'noteList'의 noteIdx번째 배열을 .splice() 함수를 이용해 제거 한 뒤 로컬스토리지에 업데이트하고
노트 수정 화면을 빠져나옵니다.
5. Sort
셀렉트 박스의 셀렉트 옵션은 const selectList = ['최근생성순', '최근수정순'] 으로 배열로 보관하여
selectList.map()으로 셀렉트 리스트를 렌더링 해줍니다.
셀렉트 박스 onClick시 setSelected(e.target.value)를 이용해 현재 셀렉트 된 값으로 셀렉트 옵션을 변경합니다.
useEffect를 통해 selected의 state가 변경될 때마다 sort() 함수를 이용하여 배열을 정렬합니다.
6. Search
.filter()를 이용해 search 바에서 받아온 value값을 'noteList' 배열 안에 포함되어 있는지 .includes() 함수를 통해 탐색하고 return 된 리스트 값을 화면에 바인딩합니다.
🌱 회고록
회고록은 혼잣말처럼 반말로 작성하겠습니다 👀!
프로젝트 진행 전 👶
프로젝트 진행 전 가장 두려웠던 것은 '내가 해낼 수 있을까?'였다.
리액트 문법의 가장 기초만을 배웠고, JS문법에 대한 자신감도 높지 않았기에 걱정이 가장 앞섰던 것으로 기억한다.
어떻게 만들어야 할지 감이 오지 않았기 때문에 시작하는 것조차 막막했고.
리액트 문법을 공부하면서(토이프로젝트 시작 전) 만들어야 할 기능들을 메모장에 간단하게 정리하며 정보들을 찾아봤다.
피그마로 디자인하면서 개발해야 할 사항들을 가시화하고 계획하며 프로젝트 준비를 본격적으로 시작했고.
만들어야 할 디자인 레이아웃과 기능들이 눈에 보이면서 '할 수 있을거야.' 라는 마음이 조금은 자라나기 시작했다.
토이프로젝트의 시작 이유는 내가 공부한 리액트 기초 문법 내에서 최대한 활용해보자 였고
- useState를 사용하고 객체나 배열을 복사하여 set 해주는 것.
- Router를 이용하여 SPA의 페이지 이동을 구현하되 파라미터 값을 전달해볼 것.
- 아직은 생소한 Redux를 간단하게나마 혼자서 만들어 사용해볼 것.
- scss대신 styled-components를 활용도 있게 사용하고 컴포넌트를 모듈처럼 사용해볼 것.
- 로컬스토리지를 생성하여 배열이나 객체로 값을 담아놓고 가져와 동적으로 바인딩 해볼 것.
위의 내용들을 토대로 프로젝트의 목표를 세웠다.
프로젝트 진행하면서 어려웠던 점 🥺
처음 시작할 때 가장 어려웠던 점은, 아직 익숙지 않은 리액트 문법과 state / props의 전달 방식 등이 어려웠던 것 같다.
기능 개발 중 맨 첫 번째로 만들었던 것이 라이트/다크모드였는데.
리액트 문법도 어려운 상태에서 styled-components는 '이런 게 있구나~' 하고 알고만 있는 상태에서
삼항연산자를 이용해 theme의 스타일을 담아놓은 객체를 props로 전달하여 토글 상태에 따라 앱의 디자인이 변경되도록 기능을 구현하는 것이 정말 어려웠던 것으로 기억한다.
사실 초반에 저 라이트/다크모드 개발하는 것만 1.5일 정도 걸렸던 것 같다...ㅠㅠ
그럼에도 불구하고 기능을 하나 개발하고 나니 자신감이 조금 붙기도 하고 '아 알겠다.' 라는 생각이 들기도 하고
퍼져있던 지식들이 조금씩 조금씩 모여드는 기분이 들면서 중반부부턴 개발하는데 점차 속도가 붙기 시작했었다.
퍼블리셔로 일 할 때에는 scss로 컬러셋이나 폰트스타일을 변수나 mixin으로 담아 재사용성을 높여 사용하는 것을 최우선의 목표로 삼았던 만큼 styled-components를 활용할 때에도 flex / pallette / txtSize를 JS파일에 담아 button과 같은 컴포넌트들에 props로 전달하여 확장성을 높이도록 노력했다. 그래서 <Button palette={palette.blue} > 이 처럼 버튼을 생성하면 파란색 배경의 버튼이 생성되도록 하는 등 디자인 요소 측면에서 고민을 많이 했었던 것으로 기억한다.
그 외에도 select박스를 일반 html에서 퍼블리싱 하듯 만들었더니 오류 났던 것이 가장 어려웠던 것 같다.
search 기능이나 sort 기능 구현에 대한 걱정만 있었지.. 의외로 select 박스가 나에게 복병이 될 줄은 꿈에도 몰랐던 것으로 기억한다.
그 당시 나는 select를 만드는 것이 기존 html 파일에서 만드는 것과 동일할 거라고 생각했었다.
그래서 팡팡 터지는 에러메시지를 보고 '이게 무슨 말이지...ㅠㅠ'라고 무척 답답해하며 미친 듯이 구글링을 했던 기억이 있다.
그래도 다행히 구글링을 하다 보니 개념이 이해되기 시작했고,
기존 html에서 select 박스의 selected 속성 대신 props로 셀렉트 속성값을 전달해줘야 한다는 것을 알게 되어
selectList 배열의 값과 길이만큼 동적으로 셀렉트 박스를 구현하는 것을 성공해서 소소하게 기뻤던 것으로 기억한다.
그 외에도 아직 개념이 잡히지 않았던 Route 페이지 이동과 파라미터값 전달 방법도 조금 어려웠던 기억이 있는데
이 Route의 경로가 프로젝트 배포 때에도 내 속을 썩였던 것으로 기억한다.
초반에는 <Route path="/" >
이런 식으로 작성한 뒤 깃허브에 배포하였는데 아예 백지로 앱 자체가 렌더링이 되질 않는 걸 보고
프로젝트 배포할 수 없을 정도로 내가 뭔가 큰 실수를 처음부터 했나 보다라고 생각해버려서 식은땀만 흘렸던 기억이 있다.
알고 봤더니 내가 package.json에 homepage 경로를 깃허브로 설정해준 탓에 상대경로가 아닌 절대경로가 필요했던 것이고
Route path={process.env.PUBLIC_URL + '/'} >
로 퍼블릭 경로를 설정해주니 문제없이 배포된 것을 확인하는 것을 끝으로 나의 첫 번째 리액트 토이프로젝트가 끝이 났다.
프로젝트 진행 후 얻은 것 🙌
JS문법과 리액트 문법 둘 다 자신감이 부족했던 내가 작게나마 혼자의 힘으로 기능을 구현하고 프로젝트를 완수했다는 것에서 오는 뿌듯함과 자신감이 너무 좋았다.
그리고 막연하게 갖고 있던 개발에 대한 두려움이 조금은 사라졌고, 모르는 것은 찾아보고 생각만 하지 말고 손을 움직이고 코드를 만지다 보면 해답을 찾을 수 있다는 것을 깨달으면서 문제 해결 능력 스킬을 조금이나마 키운 게 아닐까 싶다.
그리고 코드를 보는 눈이 조금은 넓어진 듯해서 좋고, '다음번엔 뭐를 만들어보지?' 라는 생각을 하며 개발에 대한 즐거움을 얻고 나의 가능성을 엿본 듯 해서 더욱 기쁘다.
더 공부해야 할 것 ✍️
일반 변수에 담을지 state에 담을지 고민하는 습관을 키우고 Redux를 활용하여 좀 더 다양한 기능을 구현해볼 수 있도록 공부해야 할 것 같다. 또한 아직 사용해보지 못한 다른 리액트 hook에 대해서도 좀 더 공부해야겠다.
그리고 이번 프로젝트를 통해 JS 개념의 깊이 차이가 개발의 수준을 높인다는 것을 크게 느꼈기 때문에
JS에서는 class나 prototype 그리고 배열에 대한 매서드 및 동기 비동기에 대한 공부를 앞으로 좀 더 심도 있고 확실하게 공부해야 할 것 같다.
🙇♀️ End 🙇♀️
부족한 긴 글 봐주셔서 감사합니다 :D !!
Reference
토이프로젝트 레퍼런스를 참고했던 기술 블로그입니다.
- devstone님 기술 블로그 (블로그 링크)
- eunjin님 기술 블로그 (블로그 링크)