포인트3 테크니컬 하우스

유연한 변경을 위한 컴포넌트

윤수민
윤수민May 6, 2024
2 min read|

유연한 변경을 위한 컴포넌트

컴포넌트란?

  • 전체 시스템을 구성하는 하나의 부품 또는 모듈

프론트엔드의 관점의 컴포넌트는?

  • 정의 : UI를 구성하는 UI요소
  • 의미 :과거의 웹사이트는 서버에서 완성된 html을 보내주는 방식으로 만들었기 때문에, 컴포넌트에 대한 개념이 중요하진 않았다. 하지만 이러한 방식으로 인한 사용자의 웹 복잡도의 증가와 사용성이 저하되는 문제가 나타나게 되었고 AJAX라는 기술을 활용해 필요한 부분만 부분적으로 데이터를 수정해 렌더링하게 되었고, 필요한 부분에 대해 컴포넌트 단위로 개발을 하게 되었다.

컴포넌트가 필요한 이유


우리는 수많은 변화에 마주한다 제품이 변화되지 않는다면 사용자가 어려움없이 잘사용하고 있다는것이다.
그렇다면 사용자들이 제품을 대부분 잘사용할까? 사용자 지표를 보면 그렇지 않는다고 한다.

좋은 제품을 만들기 위해서는 “변경”이 필요하다.
고객이 원하는것을 찾고 빠르게 전달하기위해 제품은 변화를 겪으며 올바른 성장을 하게 된다.

변경은 곧 고객의 “니즈” → 곧 “기회” 이다.
하지만 우리는 사용자를 모르고 어떤 니즈를 가지는지 모르기 떄문에 어떤 변경이 일어날지 예측할수없다

”그렇기에 우리는 변경에 대응할수 있어야 한다”

변경에 유연한 컴포넌트 만들기

1.Headless 기반 추상화
Headless란?

특징

  • UI를 관심사에서 제외하고 데이터에만 집중하여 모듈화 하는 패턴
  • 한가지 문제에만 집중하여 더 많은곳에서 사용되고 다른 변경으로부터 격리 가능

2. 한가지의 역할, 한가지의 역할을 하는 컴포넌트의 조합

  • 위와 같은 쪼개진 컴포넌트 구성으로 서로의 변경이 서로에게 영향을 끼치지 않게 됨
  • 각각의 컴포넌트가 유연한 변경을 가능하게함

3.도메인 분리하기


”우리는 도메인을 분리하고 인터페이스를 일반화 하는것이 필요하다”

일반적인 인터페이스로 분리하면 좋은점은?

  • 일반적인 인터페이스로 분리하게 되면 컴포넌트의 역할을 이해하기 쉬워진다.

- 이해하기 쉽게하기 위해서는 사람들이 많이 알고있는 일반적인 인터페이스 구성

- 컴포넌트 인터페이스가 표준에 가까울수록 이해하기 쉬운 컴포넌트이다.

컴포넌트 활용 예시)

이전 코드

  • sidebar를 한개의 파일로 구현
// Sidebar.tsx

import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Box, Flex, HStack, Stack } from '@chakra-ui/react';

const Sidebar: React.FC<Props> = () => {
    const navigate = useNavigate();
    const [selected, setSelected] = useState<string>('/');
     ...
     const menuItems = [
        {
            name: '홈',
            route: '/',
            fontSize: '14px',
            px: '4',
            icon: <StarIcon />,
        },
        {
            name: '정산내역',
            route: '/p3',
            fontSize: '14px',
            px: '4',
            icon: <StarIcon />,
        },
        
        return (
            <Flex>
                <Stack
                w="100%"
                h="100%"
                ...
                >
                {menuItems.map((item) => (
                    <HStack
                        key={item.name}
                        px={item.px} // 각 항목의 패딩 적용
                        py="2"
                        fontSize={item.fontSize}
                        ...
                     >
                        {item.icon}
                        <Box>{item.name}</Box>
                    </HStack>
                ))}
               </Stack>
           </Flex>
         );
       };
       export default Sidebar;
typescript

개선 코드

  • sidebarItem 페이지를 추가 하여 sidbarItem 수정이 용이하도록 직관적으로 코드개선
//Sidebar.tsx

import React from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import SidebarItem from './SidebarItem';

const Sidebar: React.FC<Props> = () => {
    const navigate = useNavigate();
    const location = uselocagion();
    const [selected, setSelected] = useState<string>('/');
        ...
        return (
            <Flex>
                <Stack
                w="100%"
                h="100%"
                ...
                >
                <SidebarItem
                    icon={<StarIcon />}
                    name="홈"
                    action={() => navigate('/')}
                    isSelected={location.pathname === '/'}
                />
                <SidebarItem
                    icon={<StarIcon />}
                    name="정산내역"
                    action={() => navigate('/p3')}
                    isSelected={location.pathname === '/p3'}
                />
               </Stack>
           </Flex>
         );
       };
       export default Sidebar;
typescript
 //SidebarItem.tsx
 
import React from 'react';
import { HStack, Box, useTheme } from '@chakra-ui/react';

const SidebarItem: React.FC<SidebarItemProps> = ({
    icon,
    name,
    action,
    ...
}) => {
    const theme = useTheme();

    return (
        <HStack
            px={px}
            py={2}
            fontSize={fontSize}
            cursor={cursorPointer ? 'pointer' : 'default'}
            _hover={{
                bg: hoverEffect ? theme.colors.gray[200] : undefined,
            }}
            onClick={action}
            borderRadius="lg"
            color={isSelected ? 'red' : 'initial'}
        >
            {icon}
            <Box>{name}</Box>
        </HStack>
    );
};

export default SidebarItem;
typescript

우리가 나아가야할 방향성


1. 인터페이스를 먼저 고민하기

  • 의도가 무엇인가? 이 컴포넌트의 기능은 무엇인가? 어떻게 표현되는가?

2. 컴포넌트를 나누는 이유를 생각하기

  • 컴포넌트를 분리하면 실제로 복잡도를 낮추는가?
  • 컴포넌트를 분리하면 재사용 가능한 컴포넌트인가?

출처: 토스 SLASH22:


우테코 테크톡: