들어가기, 첫 사내프로젝트의 시작
정규직으로 입사하자마자 2024년 상반기 중고나라의 핵심 프로젝트인 셀러 지원 센터가 시작되었다. 프로젝트 초기에는 기획자와 개발자가 함께 모여 이 시스템이 어떤 비즈니스 모델과 계획으로 발전할지 논의하는 시간을 가졌다. 수차례 회의를 통해 기획안이 하나씩 확정되기 시작했고, 우리 팀도 이에 맞춰 기술 스택을 선정하고 업무를 분담을 진행하였다.
그렇게 순조롭게 진행되고 있는 줄 알았던 가운데,,, 알게된 사실이 하나 있었다.
바로 디자이너 없이 개발을 스스로 진행해야 한다는 것이었다.
디자이너 부재가 만들어낸 초기 셀러지원센터 화면
당연히, 디자인 시스템도 없었기에 폰트와 색상 모두 제 각각 이루어진 화면으로 Figma 기획안이 전달되었다. 아래와 같은 사진이었다.
색상은 흑백 계열로 구성되어 있고, 칼럼만 나열된 어색한 테이블, 정렬되지 않는 검색 필터, 토글 기능처럼 보이지만 실제로는 접히지 않는 사이드바 등 나를 당황하게 하는 요소들이 대부분이었다.
사실 처음에는 이러한 화면을 보자마자 디자인보다는 어드민 시스템 구축이 처음이었기에 기능 구현을 우선순위에 두고 작업을 시작했다.로그인, 화면 구성, API 연결 등의 기능 구현을 어느 정도 진행하다 보니, 디자인에 대한 걱정이 점차 커지기 시작했다.
특히, 첫 번째 사진처럼 테이블을 보여주는 화면이 여러 곳에 있었는데, 각각의 화면 배치가 조화를 이루지 못해 어색함을 주는 현상이 발생했다. 분명 기획 화면 상에서는 같은 테이블임에도, 전체적인 UI/UX가 불균형을 이루어 사용자 경험이 굉장히 떨어지는 느낌을 받았다.
그러다가 문득 생각이 들었다.
이렇게 기능 구현 열심히해서 배포하더라도,
이 디자인으로는 사용자의 신뢰를 잃을 것임이 분명해보인다.
회사의 구성원으로서, 어드민 시스템 같지 않은 화면으로 중고나라 셀러 지원 센터를 오픈할 수는 없었다.
개발자가 빠르게 바꾼 어드민 시스템 디자인
우리 팀은 향후 확장성을 고려하여 shadcn/ui를 채택하여 개발을 진행하고 있었고, 나는 빠르게 해당 라이브러리와 관련해서 참고할만한 템플릿이 있는지 검색했다. 그 중 선택한 repository에서 테이블, 사이드바, 비슷한 정보를 묶어서 보여줄 수 있는 Card 레이아웃 컴포넌트를 셀러 지원 센터에 적용하였다.
📍 사용자에게 필요한 기능을 추가한 테이블
기존에는 shadcn/ui 로만 구성되었던 테이블을, react-table 라이브러리를 활용해 칼럼과 데이터를 관리하는 구조로 변경하였다. 더 나아가 사용자 편의성을 위해 아래의 3가지 기능을 추가하였다.
- Row 수 선택 기능
테이블에서 보여주는 row의 수를 10,20,30개로 선택할 수 있는 기능 - 페이지 네비게이션
{ 현재 페이지 } / { 총 페이지 수 } 를 알려주며, 처음과 마지막 페이지 도달 시, 이전/다음 버튼 비활성화 기능 - 좌우 스크롤 기능
column의 수에 따른 테이블의 가로 길이 증가로 인해 텍스트 데이터가 압축되는 현상을 막기 위한 테이블 좌/우 스크롤 기능
좌/우 스크롤 기능 경우에는 shadcn/ui 에서 제공하는 Scroll-area 컴포넌트를 활용하여 작업하였다.
이전보다 정렬된 ui로 테이블이 제공되었고, 테이블에 있는 데이터를 편하게 볼 수 있는 구조로 변경되었다.
📍 shadcn/ui의 component를 활용한 사이드바
그 다음으로 진행한 작업은 사이드바 개선이었다. 기존에 구현되어 있던 사이드바는 useState로 토글 상태를 관리하고, framer-motion으로 토글 상태에 따른 애니메이션을 보여주는 형식으로 구현되어있었다.
하지만, 사이드바를 접으면 내부에 어떤 메뉴의 목록이 있었는지 확인할 수 없는 불편함이 발생하고 있었다.
이를 개선한 결과가 아래의 영상이다.
템플릿에서 제공했던 사이드바는 중앙 토글 버튼을 클릭하면 최상단 메뉴의 하위 메뉴가 접힌 상태로 초기화되면서 동작하였다.
지금은 메뉴가 많지 않아서 토글로 인한 메뉴 상태가 초기화되는 것이 그렇게 큰 불편함을 일으키지는 않을 것이라고 판단하였다.
하지만, 메뉴가 많아지는 상황을 고려해보면, 사용자가 열어둔 메뉴가 토글로 인해 다시 닫히면, 사이드바를 열었을 때 해당 메뉴를 찾는 번거로움이 생길 수 있다고 느껴졌다.
이에 대한 불편함을 해결하기 위해, 셀러 지원 센터의 사이드바는 사용자가 현재 위치한 화면에 맞춰 토글 버튼을 눌러도 해당 화면에 매칭되는 메뉴 리스트가 펼쳐진 상태로 유지되도록 재구성했다. 또한, 셀러 화면에는 서브 메뉴가 많지 않았기 때문에 드롭다운 기능을 제거하고, 관리자 화면에서만 드롭다운 기능이 활성화되도록 조건 분기를 추가하였다.
// ✅ 최상단 메뉴의 하위 자식들인지 체크
export default function useCheckActiveNav() {
const { pathname } = useParsed();
const checkActiveNav = (nav: string) => {
const currentPathname = pathname || '';
const isSeller = currentPathname.includes('/seller');
return isSeller ? currentPathname === nav : currentPathname.startsWith(nav);
};
return { checkActiveNav };
}
이를 구현하기 위해 useCheckActiveNav 라는 커스텀 훅을 만들어 드롭다운 Nav 컴포넌트에서 사용하였다.
구조 설명은 아래와 같다.
- 현재 경로가 /seller/dashboard이고, checkActiveNav('/seller/dashboard')를 호출하면
-> /seller 경로이므로 정확히 일치해야 활성화로 간주하여 true를 반환 - 만약 현재 경로가 /about이고, checkActiveNav('/about')를 호출하면
-> 경로가 /seller가 아니므로 /about으로 시작하는지를 확인하여 true를 반환
다음은 useCheckActiveNav를 활용한 NavLinkDropdown 컴포넌트이다.
function NavLinkDropdown({ title, icon, label, sub, closeNav }: NavLinkProps) {
const { checkActiveNav } = useCheckActiveNav();
// ✅ 현재 URL 경로와 서브메뉴의 href를 비교
const isChildActive = !!sub?.find((s) => checkActiveNav(s.href));
// ✅ ADMIN 메뉴에 대해서만 드롭다운 기능 활성화
const isToggle = label === 'ADMIN';
const renderSubMenu = (
<ul>
{sub!.map((sublink) => (
<li key={sublink.title} className="my-1 ml-8">
<NavLink {...sublink} subLink closeNav={closeNav} />
</li>
))}
</ul>
);
return (
<Collapsible defaultOpen={isChildActive}>
<CollapsibleTrigger
className={cn(
'group h-12 w-full rounded-none px-6',
'flex items-center',
'text-sm',
isToggle ? 'cursor-pointer' : 'cursor-default',
)}
>
<div className="mr-2">{icon}</div>
{title}
{isToggle && ( // ✅ 아이콘 및 드롭다운 버튼 렌더링
<span
className={cn(
'ml-auto transition-all group-data-[state="open"]:-rotate-180',
)}
>
<IconChevronDown stroke={1} />
</span>
)}
</CollapsibleTrigger>
{isToggle ? ( // ✅ 서브메뉴 렌더링
<CollapsibleContent className="collapsibleDropdown" asChild>
{renderSubMenu}
</CollapsibleContent>
) : (
renderSubMenu
)}
</Collapsible>
);
}
- checkActiveNav 사용
useCheckActiveNav() 훅을 사용해, 현재 URL 경로와 서브메뉴의 href를 비교하여 서브메뉴가 활성화 상태 확인 - isToggle 조건 설정
메뉴의 label이 'ADMIN'일 경우, isToggle을 true로 설정하여 드롭다운 설정 - Collapsible 컴포넌트
메뉴가 isChildActive일 경우 기본적으로 열려 있고, 'ADMIN' 메뉴에 대해서만 드롭다운 기능을 활성화하고, SELLER 메뉴는 기본적으로 드롭다운 기능 비활성화 - 아이콘 및 드롭다운 버튼 렌더링
'ADMIN' 메뉴에는 오른쪽에 IconChevronDown 아이콘을 추가해 드롭다운 표시 & 아이콘이 클릭 시 회전 애니메이션을 적용 - 서브메뉴 렌더링
'ADMIN' 메뉴일 때는 드롭다운으로 서브메뉴가 렌더링되고, 그렇지 않으면 기본 리스트로 서브메뉴가 표시
이 과정을 통해 마침내 셀러 지원 센터에 필요한 사이드 바를 완성할 수 있었다.
📍카드 레이아웃 제작
그 다음으로는 앞서 살펴보았던 셀러 > 회원 정보 화면에서는 다양한 정보들이 구분선 없이 정렬되어 있어, 배치가 어색하게 느껴지는 현상이 발생했다. 이를 개선하기 위해, 각 정보의 성격에 맞게 세부 내용을 카드 형식으로 묶어서 보여줄 수 있는 CardLayout을 구현하였다.
import React, { ReactElement } from 'react';
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from '@shared/components/ui/card';
import { cn } from '@shared/utils/cn';
type Props = {
title: string;
icon?: ReactElement;
} & React.HTMLAttributes<HTMLDivElement>;
export default function CardLayout({
title,
icon,
children,
className,
...props
}: Props) {
return (
<div className={cn('grid gap-4', className)} {...props}>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="header-24 font-semibold">{title}</CardTitle>
{icon}
</CardHeader>
<CardContent>{children}</CardContent>
</Card>
</div>
);
}
CardLayout 컴포넌트는 제목과 선택적인 아이콘을 가진 카드를 생성하고, 내부에는 children으로 전달된 내용을 렌더링하는 구조를 가지고 있다. 이를 통해서 여러 화면에서 재사용할 수 있는 유연한 컴포넌트로 활용하면서 사용자에게 일관된 UI/UX를 제공할 수 있도록 개선되었다.
CardLayout을 활용한 셀러 정보 화면의 결과이다. 기존 셀러 정보화면과 달리, 기본 정보, 셀러 지원 센터 가입 정보, 마케팅 수신동의 그리고 사업자 정보 (담당자, 대표자, 주소, 등록 서류 등)와 같이 분류된 정보를 각각 구분된 섹션으로 표현되고 있었다. 이렇게 개선된 레이아웃을 통해 정보를 보다 명확하고 깔끔하게 보여줌으로써 사용자 경험을 크게 향상시켰다.
빠른 UI/UX 수정을 통해 얻게 된 내부 피드백
기존 기획안대로 만들어졌던 모든 화면의 디자인을 단 2일 만에 수정하였고, 해당 버전을 STG 환경에 배포하여 셀러 지원 센터 개발 팀장님께 먼저 보고드렸다.
정말 다행히도 긍정적인 피드백을 얻을 수 있었고, 해당 내용은 빠르게 프로덕트 팀으로 전달되었다.
당시, 1차 배포를 위한 QA가 진행 중이었으나, 셀러 회원에게 더 좋은 경험을 제공하기 위해서 부득이하게 QA 중간에 디자인이 변경되었다.
그럼에도 끝까지 QA를 잘 마무리해주신 매니저님들께 감사했으며 1차 오픈을 개선된 UI/UX로 진행할 수 있다는 안도감이 밀려왔다.
짧지만 강한 임팩트를 만들어주었던 UI/UX 개선 작업
디자이너 없이 개발자 스스로 템플릿을 찾고, 더 좋은 사용자 경험을 직접 찾아 나가는 과정이 꽤나 흥미로웠다.
단순히 컴포넌트만을 만들어내는 코드 공장이 아닌 사용자에게 더 나은 경험을 제공하고 서비스에 대한 긍정적인 인식을 심어줄 수 있는 컴포넌트를 만드는 데 성취감을 느낄 수 있었다.
또한, 기능 구현에만 집중했던 과거와 달리, 이번 프로젝트를 통해 사용자 경험의 중요성을 깨닫고, 사용자의 입장에서 생각하며 프로덕트를 개발하는 시각을 키울 수 있었다.
앞으로도 사용자 중심의 사고를 바탕으로 서비스를 개발하는 엔지니어로 성장하기 위해 꾸준히 노력할 것을 다짐한 계기가 되었다.
'WEB > React' 카테고리의 다른 글
중앙화된 인증 처리 시스템 구축하기 (1) | 2024.09.25 |
---|---|
[ React ] 비즈니스 로직 분리하기 (0) | 2023.12.09 |
액션/계산/데이터를 활용한 useMuation 기능 개선 (3) | 2023.12.07 |
React의 패키지 구조와 용어 정리 (0) | 2023.12.03 |
[ React ] useQuery 와 useMuataion를 활용한 기능개선 (0) | 2023.11.30 |