다시 만드는 TodoList
다시 만들어 보는 TodoList
원래 새로운 언어를 배울 때 가장 먼저 하는 일이 "Hello world" 인 것처럼, 프론트엔드 세계에서는 Todolist 만들기를 제일 먼저 하는 실습이다.
어떤 책을 봐도 웬만해서는 TodoList가 있고, 온라인 강의나 여러 포스팅에서도 다루는만큼 초보자가 얻어갈 것이 많다.
나 역시 처음 html, css, js
로 만든 것이 TodoList였고, vue
, react
로도 만들어봤기 때문에 익숙한 주제였다.
사실 뻔한 주제이기 때문에 다시 만든다고 해도 얻어갈 수 있는 것이 있을까 했지만, 최대한 여러 조건들을 지키려고 노력하면서 만들었다.
WalkThrough
실제 코딩에 앞서 실제 프로젝트를 하는 것 처럼 먼저 구조를 잡고 시작했다.
사실 너무 가벼워서 굳이 구조를 잡을 필요는 없지만..
또한 커밋 메시지도 기능별로 작성하려고 노력하였다.
그리고, 초보자의 마음으로 useState
과 custom hook
만을 사용하여 구현하였다.
- 화면 설계
- 전체적인 디자인 레이아웃 설계 - 디렉토리 구조 잡기
- 컴포넌트를 미리 분할
- 목적별로 파일 분기 - state 설계
- 할 일 목록 / 완료된 일 배열로 관리 - 실제 개발 시작
-layout
->css
-> 로직 순으로 개발
완성 화면
배포는 vercel로 하였다.
배포 링크
화면 설계
사실 개발자에게 가장 어려운 것이 디자인이 아닐까 한다...
예쁜 디자인을 하는 것은 언제나 어렵기 때문에 ios messenger
를 참고하여 레이아웃 및 색을 선정하게 되었다.
그리고 크게 나누면 총 세 개의 컴포넌트가 구성되도록 하였는데,
할 일 목록, 완료된 일 목록, 입력을 받는 필드로 구성하였다.
디렉토리 구조
하나의 페이지로 구성된 프로젝트이기 때문에 페이지별로 나누지는 않았다.
공식 질문 문서
리액트 어플리케이션 구조 - 아토믹 디자인
각 파일을 목적에 맞게 분리하였고,
components
에는 컴포넌트들을 모아놓았다.
만약 페이지가 여러 개라면, 여러 페이지에서 공유되는 components
들을 가장 상위 폴더에 놓고, 각각의 pages 하위에 components
파일을 만들고 그 page에서 사용하는 components
를 모아놓을 것 같다.
state 설계
list
형태로 보여줄 것이기 때문에 배열을 생각했다.
특히 해야할 일과 완료된 일을 구분하는 것 외에는 둘 사이에 차이가 없기 때문에 isDone
과 같은 필드를 만들어서 true false
로 관리할까도 생각했지만,
만약 실제 서비스라면 처음에 api fetch
를 할 때 두 배열을 따로 받아 각각 다른 배열에 저장할 것 같다고 생각했다. (백엔드 입장에서도 그게 더 편할 것 같다는 생각)
그래서 todoList
finishedList
두 개로 나누어서 state
관리를 하였다.
실제 개발 순서
실제 개발 순서는 다음과 같다.
- 기본적인 레이아웃 잡기
- css 적용하기
- component별로 하나씩 완성
-input form
->todoList
->finishedList
- 기능 구현
- 추가 -> 삭제 -> 완료(toggle) 순으로 구현- Typescript 적용
사실 Typescript
를 마지막에 적용한 것은 마지막에 생각이 나서 적용하게 되었다. (왜 Typescript
가 좋은지 이야기할 것도 있고 해서)
먼저 layout
, css
작업을 하고
기능 구현을 하고,
마지막 ts
로 리팩토링하였다.
신경쓴 점들
custom hook
적용utils
에서 form validate check한 점- list 추가,삭제,완료 O(1)로 적용
custom hook
custom hook
의 경우 input
을 받을 때와 list
의 추가, 삭제에 대한 hook
을 만들었다.
form
에서 input을 받을 때는 워낙 많이 쓰이는 hook
이기 때문에 특별할 것은 없고 추가로 뒤에서 validate check
를 할 때 통과하지 못한다면 form
을 초기화하기 위해 입력창을 초기화하는 함수만 추가했다.
list
의 추가는 단순히 배열의 맨 뒤에 넣고 state를 업데이트하면 끝이기 때문에 쉽게 구현할 수 있었고, 삭제의 경우 해당 element
를 클릭했을 때 배열의 index를 input으로 받아서 target에 바로 접근할 수 있도록 구현하였다.
form validate check
간단한 정규식을 사용하였다.
export const validateForm = (input: string): boolean => {
const res = input.replace(/\s/g, '');
if (res === '') return false;
return true;
};
공백을 없애서 유저가 space만 입력하여 비어있는 상태로 등록되는 것을 막았다.
Typescript 끼얹기
사실 이 부분이 제일 행복한 시간이었다.
원래 Typescript
를 매우 싫어했는데, 뭐만 하면 자꾸 빨간글씨로 에러를 뱉어대는 귀찮은 친구 라고 생각했었다.
그러나 이번에 다시 제대로 공부하면서 왜 Typescript
를 사용하는지 알게 되었고, js
에서 ts
로 수정을 하면서 이렇게 가벼운 프로젝트도 내가 생각보다 엄격하게 코딩을 하고 있지 않다는 것을 느꼈다.
JS -> TS
추가로 ts.package.json
파일이다.
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"]
}
Design Pattern
그전까지 프로젝트에서는 매 파일마다 interface
를 정의하여 사용했고, 심지어 같은 type
의 interface
가 반복되어도 무지성으로 코드를 작성했었는데, 이번에 공부를 새로 하면서 새로운 사실을 알게 되었다.
사실 반복되는 component
도 줄이는 것이 당연하기 때문에, 반복되는 interface
또한 줄이는 것이 맞다.
그래서 공유되거나 반복되는 type
들을 한 곳에 모아서 관리해준다고 한다.
상위 디렉토에 interfaces.tsx
를 만들고, 그 안에 다음과 같이 작성해 주었다.
그 후, 해당 type
들이 필요한 곳에서 import
하여 사용하였다.
코드리뷰 후 알게 된 사실
Anyscript 탈출
결론
쉽다고 생각하는 것도 지나치지 말자.
사실 지금 작성한 코드도 100% 최선인가? 하는 물음에 섣불리 대답할 수 없을 것 같다.
그만큼 앞으로 갈 길이 멀다~
추가로 적용할 것
적용한 후, 해당 과정도 포스팅하도록 하겠다.
Author And Source
이 문제에 관하여(다시 만드는 TodoList), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@n0wkim/remade-todolist저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)