다시 만드는 TodoList

7864 단어 ReacttypescriptReact

다시 만들어 보는 TodoList

원래 새로운 언어를 배울 때 가장 먼저 하는 일이 "Hello world" 인 것처럼, 프론트엔드 세계에서는 Todolist 만들기를 제일 먼저 하는 실습이다.

어떤 책을 봐도 웬만해서는 TodoList가 있고, 온라인 강의나 여러 포스팅에서도 다루는만큼 초보자가 얻어갈 것이 많다.

나 역시 처음 html, css, js 로 만든 것이 TodoList였고, vue, react로도 만들어봤기 때문에 익숙한 주제였다.

사실 뻔한 주제이기 때문에 다시 만든다고 해도 얻어갈 수 있는 것이 있을까 했지만, 최대한 여러 조건들을 지키려고 노력하면서 만들었다.

WalkThrough

실제 코딩에 앞서 실제 프로젝트를 하는 것 처럼 먼저 구조를 잡고 시작했다.
사실 너무 가벼워서 굳이 구조를 잡을 필요는 없지만..
또한 커밋 메시지도 기능별로 작성하려고 노력하였다.
그리고, 초보자의 마음으로 useStatecustom 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

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를 정의하여 사용했고, 심지어 같은 typeinterface 가 반복되어도 무지성으로 코드를 작성했었는데, 이번에 공부를 새로 하면서 새로운 사실을 알게 되었다.

사실 반복되는 component 도 줄이는 것이 당연하기 때문에, 반복되는 interface 또한 줄이는 것이 맞다.

그래서 공유되거나 반복되는 type 들을 한 곳에 모아서 관리해준다고 한다.

상위 디렉토에 interfaces.tsx 를 만들고, 그 안에 다음과 같이 작성해 주었다.

그 후, 해당 type들이 필요한 곳에서 import하여 사용하였다.

코드리뷰 후 알게 된 사실

Anyscript 탈출

결론

쉽다고 생각하는 것도 지나치지 말자.
사실 지금 작성한 코드도 100% 최선인가? 하는 물음에 섣불리 대답할 수 없을 것 같다.
그만큼 앞으로 갈 길이 멀다~

깃헙 레포

추가로 적용할 것

해당 링크

적용한 후, 해당 과정도 포스팅하도록 하겠다.

좋은 웹페이지 즐겨찾기