5/6 TIL

kind J·2022년 5월 6일
0
post-thumbnail

오늘은 1번째 팀별 과제 TodoList 만들기에서 각자 맡은 추가 기능을 구현하였다.
나의 역할은

1. 카테고리 별 리스트 나오기
2. 카테고리 전체보기

두 가지 였다. 결국은 카테고리를 눌렀을때 해당 카테고리에 맞는 리스트들이 분류되서 보여지는 것이다.

일단 우리 프로젝트의 폴더 구조를 살펴보면

이렇게 되어있다.

일단 팀에서 중간 머지 한 부분을 풀 땡겨와서 작업을 시작했다.

Category 가

const CATEGORY = ['work', 'exercise', 'study']
export default function TodoCategory() {
  return (
    <section>
      <h3>category</h3>
      <ul className={styles.categoryInner}>
        {CATEGORY.map((cate) => {
          return <li key={`category-${cate}`}>{cate}</li>
        })}
      </ul>
    </section>
  )
}

이렇게 단순하게 되어있다.
그런데 우리가 더미 데이터 구조를

  {
    id: 1651747060687,
    text: '방청소하기',
    date: '2022-05-05',
    category: 'red',
    done: false,
    isLike: true,
  },
  {
    id: 1651747060688,
    text: 'Typescript 강의 듣기',
    date: '2022-05-05',
    category: 'orange',
    done: true,
    isLike: false,
  },
  {
    id: 1651747060689,
    text: '경주 여행 계획 세우기',
    date: '2022-05-07',
    category: 'yellow',
    done: false,
    isLike: true,
  },

이런식으로 만들기로 했었고 category 종류 (color)에 따라 분리 되어야 하니

현재 TodoCategory 컴포넌트에 있는
CATEGORY 배열부터 수정을 시작했다.

const CATEGORY = [
  {
    'title' : 'work',
    'category' : 'red'
  },
  {
    'title' : 'exercise',
    'category' : 'orange'
  },
  {
    'title' : 'study',
    'category' : 'yellow'
  },
  {
    'title' : 'clean',
    'category' : 'green'
  },
]

이렇게 수정해 놓고

      <ul className={styles.categoryInner}>
        {CATEGORY.map(({title, category}) => {
          return (
            <li key={title}>
              <button 
                type="button" 
                onClick={handleCategory} 
                value={category}>
                {title}
              </button>
            </li>
          )
        })}
      </ul>

위와 같이 수정해 놓으면
카테고리 요소 마다 value 로 category 값을 가지고 있기 때문에 클릭했을때 그 요소의 카테고리가 무엇인지 가져오고 가져온 것만 보이게 메서드로 만들면 되기 때문이다.

onClick 했을때 호출되는 메서드인 handleCategory 는
최상위 컴포넌트인 TodoApp 으로 보내어 props로 내려받았다.

최상위 태그인 TodoApp 컴포넌트에

  const [todos, setTodos] = useState([...INITIAL_TODO])

todos state 가 있어서 이것이 화면에 뿌려지는 것이였다.

나는 이 todos state 를 컨트롤 하면 되는데

처음에는 카테고리에 해당하는 Todoitem 들을 setTodos를 이용해서 filter 로 거르면 된다고 생각했는데 처음 클릭에는 잘 작동하는데 그 다음 클릭에는 작동을 안했다.

그도 그럴만한것이 이미 특정 카테고리로 분리되어 todos에 담겼는데, 다른 카테고리를 클릭했을때 업데이트된 todos에는 당연히 없는 것이다.

기존의 todos를 유지해야했고, delete 기능처럼 아이템들이 사라질 수 있는 filter 메서들를 사용하면 안됐다.

그래서 내가 고안한 방법은
더미데이터로 만든것에 invisible 이라는 상태값을 하나더 추가한것이다.

{
    id: 1651747060687,
    text: '방청소하기',
    date: '2022-05-05',
    category: 'red',
    done: false,
    isLike: true,
    invisible: false,
  },
  {
    id: 1651747060688,
    text: 'Typescript 강의 듣기',
    date: '2022-05-05',
    category: 'orange',
    done: true,
    isLike: false,
    invisible: false,
  },
  {
    id: 1651747060689,
    text: '경주 여행 계획 세우기',
    date: '2022-05-07',
    category: 'yellow',
    done: false,
    isLike: true,
    invisible: false,
  },

일단은 다 false로 셋팅해 두어 전체가 보이도록 하고 카테고리를 클릭했을때 해당 카테고리는 그대로 보이고 아닌것들만 true로 바꾸어 안보이게 만드는 것이다.

그렇게 만든 메서드 handleCategory는 아래와 같다.

  const handleCategory = ({currentTarget}) => {
    const category = currentTarget.value
    if(category){
         setTodos((todos) => todos.map(
           (todo) => (todo.category === category ? 
           { ...todo, invisible: false } : 
           { ...todo, invisible: true } )
         )
       )
    }else{
      setTodos((todos) => todos.map((todo) =>  ({...todo, invisible: false}) ))
    }
  }

handleCategory 코드 설명

category 변수는 내가 클릭한 카테고리의 종류(color 값으로 되어있음)가 무엇인지 가져온다.
그리고 전체보기 가능해야 했기 때문에,

CATEGORY 배열에 전체에 해당하는 요소도 추가했다.

const CATEGORY = [
  {
    'title' : 'all',
    'category' : ''
  },
  {
    'title' : 'work',
    'category' : 'red'
  },
  {
    'title' : 'exercise',
    'category' : 'orange'
  },
  {
    'title' : 'study',
    'category' : 'yellow'
  },
  {
    'title' : 'clean',
    'category' : 'green'
  },
]

all 을 선택했을때는 category에 빈값을 두어
if 문으로 구분을 했다.

  const handleCategory = ({currentTarget}) => {
    const category = currentTarget.value
    
    if(category){
      
    }else{
      
    }
  }

all 선택시

setTodos((todos) => todos.map((todo) =>  ({...todo, invisible: false}) ))

모든 아이템의 invisible : false 가 되어 보여지도록,

각각의 카테고리를 선택할 시

 setTodos((todos) => todos.map(
   (todo) => (todo.category === category ? 
   { ...todo, invisible: false } : 
   { ...todo, invisible: true } )
   )
 )

선택된 카테고리 와 일치하는 것은 invisible: false 해서 보이게
일치하지 않는 것은 invisible: true로 안보이게 조건문을 만들었다.

그리고 TodoItem 컴포넌트에 가서

const { id, text, done, invisible} = todo 

invisible 를 추가하고

cx 라이브러리를 로드해서

import { cx } from '../../styles/index'

TodoItem 컴포넌트 최상위 태그인 li 에다가

  return (
    <li 
    	className={cx(styles.todoElement, {[styles.isHidden]:invisible})} 
    	key={`todo-${id}`}>
      ...
    </li>
  )

invisible 이 true 일 때만 isHidden 클래스를 넣어서 안보이게 하라는 조건문을 넣어주었다.

TodoItem.module.scss

cx 는 두개 이상의 class 를 보기 좋게 쓸수 있게 해주고, 위 코드처럼 조건문도 사용할 수 있어서 가독성이 더 좋아진다.

그리고 TodoItem.propTypes 에도 invisible를 추가했다. 뭔지는 잘 몰랐는데 타입을 설정해줘야하는 부분인것 같아서 다른 상태값 한거 보고 참고했다.

TodoItem.propTypes = {
  todo: PropTypes.shape({
    id: PropTypes.number,
    text: PropTypes.string,
    done: PropTypes.bool,
    invisible: PropTypes.bool,
  }),
  handleToggle: PropTypes.func,
  handleRemove: PropTypes.func,
  handleEditMode: PropTypes.func,
}

리액트가 익숙치도 않고 그래서 너무 막막했는데, 그래도 팀 과제 할때는 민폐는 안 되야지 하는 마음으로 끝까지 하니까 성공했다. 처음에는 막막할 것 같았는데 하고 나니 그래도 뿌듯하고 또 다른것을 도전해 보고싶은 욕심이 생긴다.

profile
프론트앤드 개발자로 일하고 있는 kind J 입니다.

0개의 댓글