사용자 목록 실습

#React

실습 목표

사용자 목록을 동적 렌더링하는 실습을 진행했다. 버튼을 눌러서 필터링이 가능한 사용자 목록 출력 페이지를 만드는 실습이다.

작성한 코드

// App.tsx
import { useState } from "react";
import UserItem from "./components/UserItem";

export default function App()
{
  const [role, setRole] = useState('All');

  const arr = [
    { name: 'Alice', isActive: true, role: 'Admin' },
    { name: 'Bob', isActive: false, role: 'User' },
    { name: 'Charlie', isActive: true, role: 'User' },
    { name: 'David', isActive: true, role: 'Guest' },
    { name: 'Eve', isActive: true, role: 'Admin' },
  ];

  return (
    <>
      <button onClick={ () => (setRole('All')) }>All</button>
      <button onClick={ () => (setRole('Admin')) }>Admin</button>
      <button onClick={ () => (setRole('User')) }>User</button>
      <button onClick={ () => (setRole('Guest')) }>Guest</button>
      { 
        arr.map((item) => (
          role == 'All' ? <UserItem key={item.name} name={item.name} isActive={item.isActive} role={item.role} /> : 
            role == item.role && <UserItem key={item.name} name={item.name} isActive={item.isActive} role={item.role} />
        ))
      }
    </>
  );
}

// components/UserItem.tsx
export default function UserItem({ name, isActive, role }:{ name: string, isActive: boolean, role: string })
{
  let userColor;
  if (role == 'Admin') userColor = 'red';
  else if (role == 'User') userColor = 'blue';
  else userColor = 'yellow';

  return <div style={{ backgroundColor: userColor }}>{ isActive ? name : `${ name } (inactive)`}</div>;
}

정답과 로직 비교

코드를 작성하면서 처음에 고민했던 부분은 어디서 뭘 필터링할지였다. 컴포넌트를 분리하는건 별 고민없이 진행했는데, 필터링 조건을 어느 위치에 넣을지 생각보다 고민을 많이 했다. 사실 마음만 먹으면 부모 컴포넌트에서 모든 조건문을 다 처리하고 컴포넌트에 값만 전달해도 구현은 되지만, 캡슐화를 잘 해보고 싶어서 망설임이 길었다. 내 결론은 목록 자체를 필터링 하는것 빼고는 모든 조건(배경색, ‘inactive’ 문구 출력)을 자식 컴포넌트에서 처리하는 것이였다. 정답 로직과 비교해보니 서로 같아서 이게 어느정도 괜찮은 방법이라고 파악했다.

두번째로 고민했던 부분은 사용자 목록을 모두 보여주는 부분이였다. 버튼을 눌러 role 별로 사용자 목록을 보여주는건 어렵지 않았지만, All 버튼을 눌렀을 때 모든 목록을 다 보여주는 부분을 어떻게 구현할지 좀 고민을 했다. 그래서 JSX 코드 안에서 map 안에 삼항연산자와 AND 논리 연산자를 같이 써 마치 if else 문 처럼 구현했다.

        arr.map((item) => (
          role == 'All' ? <UserItem key={item.name} name={item.name} isActive={item.isActive} role={item.role} /> : 
            role == item.role && <UserItem key={item.name} name={item.name} isActive={item.isActive} role={item.role} />
        ))

이 부분을 정답 로직에서는 filter 메서드를 사용해서 해결했다. filter 메서드는 배열의 모든 요소를 하나씩 돌면서, 전달한 함수(콜백 함수)가 true를 반환하는 요소만 따로 모아 새로운 배열로 반환한다. filter 메서드를 내 코드에 적용하면 아래와 같다.

// App.tsx
...
  const [role, setRole] = useState('All');
  const filteredArr = arr.filter((item)=>{
  	if (role === 'All') return true;
    return item.role === role;
  });

  return (
		...
        filteredArr.map((item) => (
        	<UserItem key={item.name} name={item.name} isActive={item.isActive} role={item.role} />
        ))
        ...

새로운 배열을 arr.filter를 이용하여 지정한 다음, JSX를 return 하는 곳에서는 간단히 출력하는 로직만 두었다. JSX 외부에서 필터링을 할 수 있는 것은 최대한 걸러낸 뒤 JSX에 데이터를 전달하는 방법이 더 좋은 것 같다.