할 일 앱 실습 - 로직
할 일 앱 실습에서 디자인 다음으로 예제 코드와 비교한 것은 로직이였다.
컴포넌트를 나누는 기준
1. SVG
svg 코드를 컴포넌트로 분리하는 것은 바로 납득했고 딱히 의문의 여지도 없었다. 하나 유념해둬야겠다 생각한 것은 컴포넌트를 저장하는 경로와 파일 네이밍이였다.
내가 혼자서 분리했을 때는 src 하위 경로에 icon 폴더를 생성하여, 연필 아이콘은 Edit.tsx, 삭제 아이콘은 Delete.tsx로 저장했었다. 하지만 예제 코드는 components/svg 경로에 SvgPencil.tsx, SvgClose.tsx로 각각 저장해두었다. svg 코드도 결국 jsx의 한 부분이니까 컴포넌트로 분류하고, 연필 아이콘이 꼭 Edit의 역할을 하지 않을 수도 있으니 아이콘 모양으로 저장하는 것이 더 유연성있는 네이밍이라고 생각이 되었다. (모양으로 이름을 지으면 연필 아이콘을 ‘수정’ 버튼뿐만 아니라 ‘글쓰기’ 아이콘으로도 재사용할 수 있다.)
2. 기능
App.tsx 파일에 쭉 작성했던 코드를 기능 별로 컴포넌트화 했다.
이 부분은 나도 비슷하게 진행해서 직관적으로 받아들였다.

3. 폼 요소
폼 요소를 분리하는건 처음에는 왜 굳이 나눴을까 하는 고민을 했다. 왜냐하면 예제 코드에서 폼 요소를 분리하는 것과 분리하지 않는 것이 서로 차이가 없었기 때문이다. 비교해보면 대소문자만 바뀌었다…
// 폼 요소 분리 전
<button className="todo__button"></button>
// 폼 요소 분리 후
<Button className="todo__button"></button>
하지만 뒤 쪽에서 input을 분리하는 것을 보고 조금 감을 잡았는데, type을 필터링하는 코드가 있었기 때문이다.
type InputProps = Omit<React.ComponentPropsWithRef<'input'>, 'type'> & {
type? : 'text' };
export default function Input(props: InputProps) {
const { ...rest } = props;
return <input { ...rest } />
이 코드는 input 요소에서 text type만 허용하는 필터링을 Input 컴포넌트에 적용한 것이다. 위 코드 처럼 폼 요소를 분리해서 적용하면 앱 전체에 적용하는 폼 요소에 공통적인 속성을 편하게 조정할 수 있다. type 필터링 뿐만 아니라 스타일을 적용하는 클래스를 포함해서 컴포넌트화 하면 기본 스타일에 원하는 스타일을 가감하는 식으로 사용할 수 있다. 피그마의 인스턴스 본체를 만드는 느낌이 든다.
리팩토링: 데이터 구조와 상태 업데이트 로직 개선
인터페이스
예제 코드에서는 할 일 아이템 자료구조를 id, title, done이 있는 인터페이스로 만들어서 사용했다.
interface Todo {
id: number;
title: string;
done: boolean;
}
나는 그냥 string 배열로 만들어 사용해서 id가 정확하지 않았고, 할 일 완료(done) 정보가 각 ui의 체크 상태로만 존재했다. 특히 문자열 배열로만 관리하면, 같은 텍스트를 가진 할 일이 중복될 때 삭제 또는 수정 시 엉뚱한 항목이 변경될 위험이 있다.
이렇게 자료구조를 구성하여 사용하는건 꼭 익히고 싶어서 기존 로직을 지우고 해당 인터페이스를 사용하여 코드를 다시 작성한 후 재비교하였다.
수정 함수
할 일 아이템에서 수정이 가능한 요소는 title과 done 두 가지이다. 나는 이 두가지를 수정하는 함수를 하나로 통일해서 썼고, 예제에서는 각각 분리해서 썼다.
함수를 하나로 합치면 코드는 줄어들 수 있지만, toggleTodo와 modifyTodo로 나누면 함수 이름만 봐도 어떤 동작을 하는지 명확히 알 수 있다. 또한 유지보수 측면에서도 각 기능이 분리되어 있는 것이 디버깅에 유리할 것 같다.
useState 메서드 콜백 인자
useState의 메서드에 콜백 인자를 넣는 것을 까먹었다!
const [ todos, setTodos ] = useState<Todo[]>([]);
const deleteTodo = (id: number) => {
setTodos((todos) => todos.filter(todo => todo.id !== id));
}
React의 state 업데이트는 비동기적으로 배치 처리될 수 있다. 따라서 todos 변수를 직접 참조하면 이전 시점의 데이터를 참조할 위험이 있다. 콜백 함수를 사용하면 항상 최신 상태의 값을 보장받을 수 있어 안전하다.
set 할 때 콜백 인자를 넣는 것은 안정적인 상태 업데이트를 위해 빼먹지 않는 것이 좋으니 앞으로는 유의하자.