테마를 이용한 공용컴포넌트 만들기

최하영·2024년 3월 15일
2

React

목록 보기
3/6
post-thumbnail

이번주는 디자인 토큰을 이용한 대표적인 공용 컴포넌트를 만들어보려고 한다.

🎨디자인 토큰이란?

디자인 토큰은 색상, 크기, 글꼴 등 디자인 변수를 저장하는데 사용하는 요소 단위이다.
예를 들어, 아래 코드는 color 에 관련된 디자인 토큰이다.

//색 관련 디자인 토큰

//메인컬러 라벤더
const MAIN = {
    base: "#AC87C5", // 600 과 동일값
    0: "#FFE5E5",
    200: "#E0AED0",
    400: "#B784B7",
    600: "#AC87C5",
    800: "#8E7AB5",
    1000: "#756AB6",
};
const PALLETE = {
    teal: {
        base: "#709FB0",
        light: "#A0C1B8",
    },
    green: {
        base: "#C6DCBA",
        light: "#D7E4C0",
        weight: "#BBC3A4",
    },
    gray: {
        base: "#C7C8CC",
        light: "#E3E1D9",
        weight: "#B4B4B8",
    },
    peach: {
        base: "#EFB495",
        light: "#EFD595",
        weight: "#ffc0cb",
    },

.....

디자인은 무한히 재사용될 수 있기때문에 이를 토큰화 하여 만들면 추후에 관리하기 편해진다.

그럼 "디자인 토큰만 정하면 되는거 아닌가?" 라는 의문이 들것이다.
간단한 프로젝트의 경우 디자인 토큰만 설정해서 개발자와 디자이너 모두 편리하게 사용할 수 있을거라 생각이 들겠지만 사실은 그렇지 않다. 프로젝트를 시작하는 초기에는 기업마다 만든 디자인 규칙이 잘 이행될 수 있으나 시간이 지나면서 규칙이 지켜지지 않는 경우가 많아진다.

이를 해결하기 위해 나온것이 바로 디자인 토큰을 이용한 디자인 시스템이다.

🖼️디자인 시스템이란?

디자인 시스템이란 디자인 원칙, 재사용 가능한 UI 패턴/ 컴포넌트, 코드로 구성된 시스템을 이야기한다.
즉, 디자인 토큰이 적용된 상태에서 재사용 가능한 UI패턴과 컴포넌트를 코드로 구현하여 사용 가능한 상태로 만들어내는 것이다.
이러한 디자인 시스템을 활용하면 혼란을 방지하고 (팀 내 언어통일) 효율적인 작업을 할 수 있다.

그렇다면 디자인 토큰을 이용하여 테마를 설정하고 공용컴포넌트를 작성해보자.

📑테마 설정

여기서 테마란 디자인 시스템을 구축하는 모든 디자인 관련들을 말한다.
테마를 잘 정의하면 생산성이 증가하고 디자인 일관성이 생길것이다.

사실 개발자 입장에서 보면 button 이나 select 등 재사용할 수 있는 컴포넌트에 대해 디자인 토큰을 일일이 정하는 일은 드물거라고 생각한다.
그래서 나는 공용으로 재사용이 많이 되는 태그에 관해서 테마를 설정했다.

(먼저 color 와 size 디자인 토큰을 만들어둔 상태)

src -> styleTheme 폴더를 두어 button.style.js 파일을 만들었다.
어떤식으로 해야 개발자 입장에서 button을 골라쓸수 있을까 생각하다가 color 와 size로 나눌수 있을것같았다. 다양한 버튼이 필요한 프로젝트라는 가정하에 아래와 같이 테마를 설정하면 재사용하기 쉬울거같다.

//버튼 스타일 테마 
import styled, { css } from "styled-components";
import { COLORS } from "../../designToken/color";
import { SIZES } from "../../designToken/size";

const ButtonColorCss = {
    mainPurple: {
        css: css`
            background-color: ${COLORS.MAIN.base};
            color: ${COLORS.SYSTEM.black};
        `,
        activeColor: css`
            background-color: ${COLORS.MAIN[200]};
        `,
    },
    teal: {
        css: css`
            background-color: ${COLORS.PALLETE.teal.base};
            color: ${COLORS.SYSTEM.black};
        `,
    },
    green: {
        css: css`
            background-color: ${COLORS.PALLETE.green.base};
            color: ${COLORS.SYSTEM.black};
        `,
    },
};

const ButtonSizeCss = {
    mini: {
        css: css`
            width: ${SIZES.BUTTON.mini.width};
            height: ${SIZES.BUTTON.mini.height};
        `,
    },
    small: {
        css: css`
            width: ${SIZES.BUTTON.small.width};
            height: ${SIZES.BUTTON.small.height};
        `,
    },
    medium: {
        css: css`
            width: ${SIZES.BUTTON.medium.width};
            height: ${SIZES.BUTTON.medium.height};
        `,
    },
    large: {
        css: css`
            width: ${SIZES.BUTTON.large.width};
            height: ${SIZES.BUTTON.large.height};
        `,
    },
};

export const StyleBtn = styled.button`
    border: none;
    border-radius: 10px;
    cursor: pointer;
    ${({ color }) => ButtonColorCss[color].css}
    ${({ size }) => ButtonSizeCss[size].css}
    &:active {
        background-color: ${({ color }) => ButtonColorCss[color].activeColor};
        transform: scale(0.8);
    }
`;

이렇게 button에관련하여 테마를 만들었다면 이제 공용컴포넌트를 만들어보자

//버튼 공용컴포넌트
import { StyleBtn } from "../styleTheme/button.style";

const Button = ({ color, size, text, ...restProps }) => {
    return (
        <StyleBtn size={size} color={color} {...restProps}>
            {text}
        </StyleBtn>
    );
};
export default Button;

버튼 컴포넌트에서 color , size, text, restProps 를 props로 설정했다.
그리고 이 컴포넌트를 페이지네이션 컴포넌트에서 사용해보겠다.

//페이지네이션 컴포넌트
import { useSearchParams } from "react-router-dom";
import styled from "styled-components";
import Button from "./button";

function Pagination({ total, limit, currentPage, curParams, setCurParams }) {
    const numPages = Math.ceil(total / limit);
    const [searchParams, setSearchParams] = useSearchParams();
    // + 일 때와, - 일 때
    const handlePageChange = (page) => {
        searchParams.set("currentPage", page);
        setSearchParams(searchParams);
    };
    return (
        <ButtonWrapper>
            <Button color="lemon" size="mini" text="&lt;&lt;" />
            <Button
                color="peach"
                size="mini"
                text="&lt;"
                onClick={() => handlePageChange(currentPage - 1)}
                disabled={currentPage === 1}
            />
            {Array(numPages)
                .fill()
                .map((_, i) => (
                    <Button
                        color="peach"
                        size="mini"
                        text={i + 1}
                        key={i + 1}
                        onClick={() => handlePageChange(i + 1)}
                    />
                ))}
            <Button
                color="peach"
                size="mini"
                text=" &gt;"
                onClick={() => handlePageChange(currentPage + 1)}
                disabled={currentPage === numPages}
            />
            <Button color="lemon" size="mini" text="&gt;&gt;" />
        </ButtonWrapper>
    );
}
const ButtonWrapper = styled.div`
    gap: 5px;
    display: flex;
    justify-content: center;
    align-items: center;
`;
// const SButton = styled.button`
//     border: none;
//     border-radius: 8px;
//     padding: 10px;
//     margin: 0;
//     background: #ffc0cb;
//     color: black;
//     font-size: 1rem;

//     &:hover {
//         background: tomato;
//         cursor: pointer;
//     }
// `;

export default Pagination;

이와같이 적용했는데, 디자인시스템을 설정하지 않았다면 아래 (주석된것)과 같이 작성해야했을것이다. 확실히 코드가 간결해졌다.


해당코드는 사진과 같이 나타난다.
끝..!

다음 글은 이런 공용컴포넌트를 StoryBook 으로 관리하는 방법으로 돌아올 예정이다!

0개의 댓글