react CSS-in-JS 라이브러리 @stitches/react 예시

katanazero·2022년 6월 23일
4

react

목록 보기
15/15
post-custom-banner

CSS-in-JS 란?

  • javascript 내에서 컴포넌트를 스타일링하는 방법 -> 이렇게 작성해서 나온 결과물을 styled componets 라고 이야기함
  • CSS 파일을 따로 구분하지 않으며, 컴포넌트내에 전달되는 props 에 따라 스타일링을 쉽게쉽게 적용이 가능
  • CSS-in-JS -> JS 안에 CSS를 작성해서 관리하자구!(이전에 따로 CSS 파일을 구분하는 기법은 CSS-in-CSS 방식이다)

CSS-in-JS 를 사용하는 이유는 아래와 같다

  • CSS 격리
  • CSS 를 분리해두면 파일이늘어나고, 이를 불러와 사용해서 상태에 따라 유동적으로 스타일링을 적용하려면 상대적으로 어려움 + 코드가 지저분해짐
  • 각 컴포넌트내에 CSS 코드가 존재하기때문에 작업시 가시성이 좋음
  • SASS 처럼 중첩 스타일 규칙 작성 가능
  • 라이브러리들과 레퍼런스들이 잘 구축이 되어있으며 현재도 발전중(현재는 zero-runtime 을 주장하는 CSS-in-JS 라이브러리가 등장)

https://www.npmtrends.com/@stitches/react-vs-emotion-vs-stitches/react-vs-styled-components-vs-jss

위 4가지 이외에도 라이브러리들이 더 존재한다.


@stitches/react

# With npm
npm install @stitches/react

# With yarn
yarn add @stitches/react

이번에 사내에서 외주 react 프로젝트를 진행하게 되어서 이전에는 CSS-in-JS 관련한 라이브러리 사용경험이 없어서 사용을 해보게되었습니다.

해당 라이브러리를 사용하면서 좋았던점은 풍부한 기능과 variant 주도 컴포넌트 스타일링이 가능한 부분이었습니다.(+ 해당 라이브러리에 릴리즈 주기, 유지보수가 진행이 되는지, 다른 라이브러리와의 충돌문제 또는 버저닝이슈가 있는지등도 확인을 해보았습니다)

import { styled } from '@stitches/react';
// or

import { createStitches } from "@stitches/react";
export const { styled } = createStitches({
  media: {
    xs: "(min-width: 360px)",
    sm: "(min-width: 640px)",
    md: "(min-width: 768px)",
    lg: "(min-width: 1024px)"
  }
});

styled 객체를 import 하여 스타일드 컴포넌트 작성이 가능합니다.(첫번째 인자로 element 명을 문자열로 받으며, 두번째 인자로는 style 객체를 받습니다.)
createStitches 객체를 import 하여 media 속성을 정의한 styled 객체를 생성하여 반응형에 대해서도 작업이 가능합니다.

선언한 반응형에 대한 속성에 접근을 하려면 스타일드 컴포넌트내에서 @{media object property name} 으로 사용이 가능합니다. (@xs, @sm ..)

아래에 @stitches/react 를 활용하여 간단한 버튼 컴포넌트를 작성해보도록 하겠습니다.

// stitchesUtils.ts

import { createStitches } from "@stitches/react";
export const { styled } = createStitches({
  media: {
    xs: "(min-width: 360px)",
    sm: "(min-width: 640px)",
    md: "(min-width: 768px)",
    lg: "(min-width: 1024px)"
  }
});
import { styled } from "../../utils/stitchesUtils";
import type * as Stitches from "@stitches/react";

const ButtonStyled = styled("button", {
  appearance: "none",
  border: "0px",
  borderRadius: "999px",

  variants: {
    color: {
      primary: {
        backgroundColor: "#1976d2",
        color: "white"
      },
      violet: {
        backgroundColor: "blueviolet",
        color: "white"
      }
    },
    size: {
      md: {
        fontSize: "16px",
        height: "36px",
        padding: "0 16px"
      },
      lg: {
        fontSize: "18px",
        height: "46px",
        padding: "0 22px"
      }
    },
    disabled: {
      true: {
        backgroundColor: "#e6e6e6",
        color: "rgba(0, 0, 0, 0.7)",
        pointerEvents: "none"
      }
    }
  },

  defaultVariants: {
    color: "primary",
    size: "md"
  }
});
type ButtonStyledVariantsType = Stitches.VariantProps<typeof ButtonStyled>;

interface IButtonProps {
  variants?: string;
  disabled?: boolean;
  size?: string;
  label?: string;
  handleClick?(): void;
}

export default function Button({
  variants = "primary",
  disabled,
  size,
  label = "",
  handleClick
}: IButtonProps) {
  const onClick = () => {
    if (handleClick) handleClick();
  };
  return (
    <ButtonStyled
      color={variants as ButtonStyledVariantsType["color"]}
      size={size as ButtonStyledVariantsType["size"]}
      disabled={disabled}
      onClick={onClick}
    >
      {label}
    </ButtonStyled>
  );
}

@stitches/react 의 styled API 에서 두번째 인자는 style 객체를 받는다고 이야기하였습니다. 이 해당 객체에는 variant API 사용이 가능합니다.

const Button = styled('button', {
  // base styles

  variants: {
    color: {
      violet: {
        backgroundColor: 'blueviolet',
        color: 'white',
        '&:hover': {
          backgroundColor: 'darkviolet',
        },
      },
      gray: {
        backgroundColor: 'gainsboro',
        '&:hover': {
          backgroundColor: 'lightgray',
        },
      },
    },
  },
});

() => <Button color="violet">Button</Button>;

variant API 를 활용하여 컴포넌트에 다양한 스타일링이 가능하며, 컴포넌트 스타일링에 유연성과 좋은 유지보수성을 제공해줍니다. + variant 를 통해서 스타일링 관심사 분리를 명확하게 가져갈수 있습니다.

// https://stitches.dev/docs/typescript
import type * as Stitches from '@stitches/react';

그리고 typescript 에 대해 타입을 제공해줘서 바로 사용이 가능합니다.
저는 위 예제코드에서 Stitches.VariantProps 타입을 활용해서 스타일드 컴포넌트에 variant 타입을 type alias 형태로 선언하여 사용하였습니다.


  • CSS-in-JS 를 라이브러리를 처음사용해서 작성한 스타일드 컴포넌트는 이전까지 SCSS 로 스타일 코드를 작성하고 이를 *.module.scss 형태로 import 하여 이를 제어해나가면서 작업해 나갔던 방식에 비교를 해보면 훨씬 작업하기가 수월하였다.
  • variant API 를 아주 다양하게 제공해줘서 스타일링을 관심사별로 구분이 가능한 부분이 좋았으며 좀 더 적극적으로 사용해서 DRY 원칙을 지키면서 사용을 해보고싶다.
  • 마치 Vue.js 에서처럼 개발하는 경험을 주었다.(Vue 는 컴포넌트내에 style 태그영역이 존재하여, 해당 태그에 스타일 코드를 작성해 나가는게 가능하며 이를 통해 한 컴포넌트내에서 바로바로 확인이 가능하다. + 물론 외부 CSS 를 불러오는것도 가능)

codesandbox : https://codesandbox.io/s/stitches-react-example-hxyrfz

@charset "UTF-8";

.buttonBasic {
  box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
  background-color: #0a8fdc;
  color: #ffffff;

  border : none;
  appearance: none;
  outline: none;
  border-radius: 8px;
  font-size: 16px;
  padding : 10px 16px;
  cursor: pointer;

  transition: background-color 0.1s ease-in;

  @media (hover: hover) {
    &:hover {
      background-color: #07649a;
    }
  }

}

.buttonBasicFullWidth {
  width: 100%;
}

.buttonBasicFlat {
  box-shadow: none;
}
// ButtonBasicComponent.tsx
import classes from './ButtonBasicComponent.module.scss';
import clsx from 'clsx';

interface IButtonBasicComponentProps {
  name?: string;
  fullWidth?: boolean;
  handleClick?(): void;
  flat?: boolean;
}

export default function ButtonBasicComponent({
  name = '',
  fullWidth = false,
  handleClick,
  flat = false,
}: IButtonBasicComponentProps) {
  const onClick = () => {
    if (handleClick) handleClick();
  };

  return (
    <button
      className={clsx(classes.buttonBasic, fullWidth && classes.buttonBasicFullWidth, flat && classes.buttonBasicFlat)}
      type="button"
      onClick={onClick}
    >
      {name}
    </button>
  );
}

이제는 스타일드 컴포넌트 뽕맛을 보아서 위처럼 작업을 하는게 꺼려질거같다..
저기에 BEM 표기법으로 CSS 구조를 잡아가면 그거는 그거대로 지옥이 열리고, 문제는 className 에 props 에 따라 해당하는 클래스를 적절히 적용해줘야하는데 잘못 작성하면 참 지저분해진다.

profile
developer life started : 2016.06.07 ~ 흔한 Front-end 개발자
post-custom-banner

0개의 댓글