해당 포스트는 프로젝트의 UI를 구현하는 것을 기록하기 위한 포스트입니다.

프로젝트의 개요

간단한 편지를 주고 받을 수 있는 앱을 개발하고 있다.
사용자들 간의 링크 공유를 통해 편지를 작성할 수 있고 보낸 편지를 해당 링크의 사용자의 리스트에서 확인하고 클릭하여 열람할 수 있는 앱이다.

자세한 개요는 프로젝트의 구현 후 배포 링크를 공유하도록 할 것이다.


구현할 페이지 기능 소개

프로젝트의 디자인을 완료하고 드디어 퍼블리싱를 진행했다.

내가 퍼블리싱을 맡은 파트는 ‘받은 편지의 리스트’, ‘받은 편지의 열람’ 페이지이다.

받은 편지 리스트

그 중 ‘받은 편지의 리스트’ 페이지에는 전체 복주머니 / 읽은 복주머니 / 안읽은 복주머니 리스트를 볼 수 있는 ‘탭 메뉴’가 있다.

실질적으로 React로 프로젝트는 처음 하기 때문에 나중을 위해 UI를 구현한 과정을 기록하려 한다.

탭 메뉴 버튼 구현

TypeScript, styled-components를 사용하여 구현하고 있다.

우선 기능 구현을 하지 않은 JSX의 코드이다.

<WrapperBox>
  <PageDescription>받은 복주머니</PageDescription>
</WrapperBox>

위의 이미지에 나와있는 것처럼 탭 메뉴 버튼은 3개이다.
그래서 ul 태그를 이용하여 자식 태그인 li 안에 button 태그를 넣어 구현할 것이다.

li 태그를 버튼의 용도로 구현해도 되지만 굳이 button 태그를 추가한 이유는 button 태그를 사용하여 태그를 사용하는 목적을 분명히 하는 것이 좋다고 생각했다.

하나씩 직접 JSX 코드에 작성해도 되지만 나는 map 메서드를 사용하여 해당 버튼 컴포넌트를 전개하려고 한다.

아래의 tabMenuArr는 객체를 가지는 배열로 title이라는 프로퍼티를 가지는데 이것은 버튼의 content로 사용할 것이다.

const tabMenuArr = [
  { title: "받은 복주머니" },
  { title: "읽은 복주머니" },
  { title: "안읽은 복주머니" },
];

이제 ul 태그와 button을 담을 li 태그를 styled-components로 생성하도록 하자.

const TabMenuBtnList = styled.ul`
  display: flex;
  justify-content: space-between;
`;

const TabMenuBtn = styled.button`
  width: 100px;
  padding: 12px 0;
  font-weight: bold;
  color: #fff;
  border-radius: 3px;
  background-color: #d9d9d9;

  &.active {
    background-color: #e48254;
  }
`;

TabMenuBtnListul 태그로 li 태그를 묶어서 flex를 통해 li 사이의 동일한 간격(space-between)으로 정렬해주는 역할을 컴포넌트이다.

그리고 TabMenuBtnbutton 태그로 li 태그의 자식 요소로 추가할 것이다.
탭 메뉴를 선택했을 때 active라는 className을 추가하여 배경색을 변경할 것이다.

그러기 위해서는 버튼을 클릭했을 때 해당 버튼의 고유의 값을 통해 클릭한 버튼과 클릭하지 않은 버튼을 구별할 필요가 있다.

그래서 해당 컴포넌트 내에 useState를 통해 해당 버튼의 고유의 값을 저장한다.

그리고 해당 값을 업데이트하는 함수 selectMenuHandler를 선언해준다.

const [active, setActive] = useState(0);

const selectMenuHandler = (index: number) => {
  setActive(index);
};

해당 State의 초기값을 0으로 한 이유는 map 메서드를 사용하는 이유가 될 수 있다.

아래의 코드는 tabMenuArrmap 메서드로 렌더링하는 코드이다.

<TabMenuBtnList>
  {tabMenuArr.map((el, index) => (
    <li key={el.title} onClick={() => selectMenuHandler(index)}>
      <TabMenuBtn className={index === active ? "active" : ""}>
        {el.title}
      </TabMenuBtn>
    </li>
  ))}
</TabMenuBtnList>

여기서 map 메서드의 두번째 인자인 index는 순회하는 elindex(순서)가 된다.

해당 li를 클릭 시 index를 전달받은 selectMenuHandler가 호출이 되면서 active State가 업데이트된다.

active의 값이 map 메서드의 인자인 index와 동일할 경우 위에서 설정한 className인 active를 추가하고 아니라면 추가하지 않는다.

<TabMenuBtn className={index === active ? "active" : ""}>{el.title}</TabMenuBtn>

이렇게 되면 버튼을 클릭할 때마다 클릭한 버튼에 className이 추가되어 스타일이 변경된다.

탭메뉴 완성


클릭한 메뉴에 따른 컨텐츠 구현

나는 탭 메뉴 버튼을 묶은 TabMenuBtnList의 밑에 해당 메뉴에 해당하는 목록을 표시하려고 한다.

해당 기능은 버튼의 index의 값을 저장한 State를 사용하여 간단하게 구현할 수 있다.

const [active, setActive] = useState(0);
{
  active === 0 && <div>전체 복주머니</div>;
}
{
  active === 1 && <div>읽은 복주머니</div>;
}
{
  active === 2 && <div>안읽은 복주머니</div>;
}

이것은 active state의 값이 0, 1, 2일 때 && 연산자를 통해 버튼에 해당하는 목록을 가진 컴포넌트를 렌더링하는 방식이다.

지금은 렌더링할 데이터가 없어 div 태그를 임시로 이용했다.

탭 메뉴에 따라 바뀌는 컨텐츠

댓글남기기