CSS-in-JS, Emotion과 Styled-components

사자·2024년 9월 4일
post-thumbnail

최근 프로젝트에서 Next.js + Sass를 사용했는데, 스타일 코드 내부로 prop을 전달할 수 없다는 점에서 불편함을 느꼈다. 특히 color 프로퍼티와 같이 특정 값(예: #ddd, #000 ...)을 지정해야 하는 부분은 jsx 코드에서 인라인 스타일을 활용해 css 값을 변경해 주었는데, 스타일 - 레이아웃 - 로직 코드 역할 분리에 큰 해를 끼쳤다는 죄책감이 들어 css-in-js 스타일링이 그리워졌다.

그래서 조사해본, emotion과 Styled-components의 차이점.
두 라이브러리를 비교해보고, 더 적합한 라이브러리를 선택해 프로젝트 리팩토링에 적용해볼까 한다.

CSS-in-JS

CSS-in-JS를 사용하면 JavaScript 코드 내에서 CSS 스타일을 작성할 수 있다.
아래는 styled-components 코드 예시이다.

import styled from 'styled-components';

const Button = styled.button`
  background-color: blue;
  color: white;
  padding: 10px;
`;

// 사용 예
const Component = () => {
  return <Button>Click Me</Button>
};

export default Component

이처럼 CSS-in-JS 방식을 사용할 경우 React 컴포넌트와 함께 스타일을 정의할 수 있기 때문에 어떤 UI와 연관된 스타일인지 확인하기에 용이하고, 일반 css를 사용했을 때 클래스명이 중복되어 의도치 않은 스타일 충돌이 발생하는 것을 방지할 수 있다는 장점으로 인기를 얻었다.

Emotion과 styled-components

styled-componentsEmotion은 React 개발 환경에서 가장 인기있는 CSS-in-JS라이브러리이다. 제공하는 기능이 동일하고, 둘 모두 sass 문법을 사용하기 때문에 스타일을 정의하는 문법에는 차이가 없다.

스타일을 JSX에 적용하는 방법이 다를 뿐이다.

두 라이브러리는 모두 다음과 같은 기능을 지원한다.(참고)

  • Props 전달
  • 미디어 쿼리 사용
  • 전역 스타일
  • &(Nested) 셀렉터
  • 서버 사이드 렌더링 지원
  • 테마 지원
  • 스타일 조합

생태계

npm trend를 확인해보면, styled-components의 사용량이 Emotion 사용량의 두 배 가까이된다.

그런만큼 styled-components의 커뮤니티 자원이 더 크고, 여러 UI 라이브러리와 통합이 잘 되어 있는 편이다. 그러나 두 라이브러리 모두 꾸준하게 사용량이 있고 어느정도 규모를 키워왔기 때문에 업데이트도 꾸준히 되고 있고, 공식 문서가 잘 쓰여 있어 이용에 어려움은 없다.

코드스타일 비교하기

styled-components

styled-components를 사용할 때에는, styled 모듈을 불러와 사용할 태그와 함께 템플릿 리터럴을 사용한다.

템플릿 리터럴 내부에는 sass 문법의 css 코드를 작성할 수 있으며, 이렇게 만든 Styled-Component는 실제 JSX 코드에서 컴포넌트로 사용할 수 있다.

import styled from 'styled-components';

const Button = styled.button`
  background-color: blue;
  color: white;
  padding: 10px;
`;

// 사용 예
const Component = () => {
  return <Button>Click Me</Button>
};

export default Component

emotion

emotion은 두가지의 스타일 정의 방식을 지원한다.

방법-1) 태그드 템플릿 리터럴을 활용해 정의한다.(styled-components와 동일)

/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';

// 태그드 템플릿 리터럴을 사용하여 스타일 정의
const StyledButton = styled.button`
  background-color: blue;
  color: white;
  padding: 10px;
  border: none;
  border-radius: 5px;

  &:hover {
    background-color: darkblue;
  }
`;

// 사용 예
const Component = () => {
  return (
    <div>
      <StyledButton>Click Me</StyledButton>
    </div>
  );
};

export default Component;

방법-2)스타일을 별도로 정의한 후, JSX에서 해당 스타일을 컴포넌트의 css prop에 전달하여 적용한다.

/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';

// 스타일 객체를 생성해 스타일 정의
const buttonStyle = css`
  background-color: blue;
  color: white;
  padding: 10px;
`;


// 사용 예
const Component = () => {
  // html 태그명 사용, css Prop 전달
  return (
    <button css={buttonStyle}>Click Me</button>
  )
};

export default Component

styled-components의 css 스타일 정의 문법

styled-component에서도 Emotion의 스타일 객체 정의와 비슷한 기능을 제공한다.

다음과 같이 css 문법을 정의해, styled-component의 템플릿 리터럴 내에 ${스타일객체}형식으로 포함시켜 스타일을 재사용 할 수 있다.

import styled, { css } from 'styled-components';

// 스타일을 변수로 정의
const buttonStyles = css`
  background-color: blue;
  color: white;
  padding: 10px;
  border: none;
  border-radius: 5px;

  &:hover {
    background-color: darkblue;
  }
`;

// 스타일을 재사용하여 컴포넌트 정의
const Button = styled.button`
  ${buttonStyles}
`;

하지만 이렇게 만든 buttonStyles 객체를 컴포넌트로 사용하거나, 컴포넌트의 prop으로 적용시킬 수 없다는 문법적인 차이점이 있다.

공통 스타일 사용하기

Emotion을 사용했을 때의 장점 중 하나는, css 스타일을 특정 컴포넌트에 종속시키는 것이 아니기 때문에 여러 태그에 동일한 스타일을 적용하기 유용하다는 점이다.

// Emotion 예시
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';

const commonStyle = css`
  background-color: blue;
  color: white;
  padding: 10px;
`;


// 사용 예
const Component = () => {
  // 어떤 html 태그를 사용하는 지 관계 없이 중복 사용 가능
  return (
    <div css={commonStyle}>
	    <button css={commonStyle}>Click Me</button>
	</div>
  )
};

export default Component

styled-components에서 여러 html 요소에 같은 스타일을 적용하고 싶으면, 공통 스타일을 정의한 뒤 사용할 HTML 태그마다 스타일 컴포넌트를 생성해 주어야 한다는 불편함이 있다. (Emotion도 style component를 사용하면 같은 문제가 발생한다.)

// styled-component 예시
import styled, { css } from 'styled-components';

// 공통 스타일 정의
const commonStyle = css`
  background-color: blue;
  color: white;
  padding: 10px;
`;

// 스타일을 적용한 컴포넌트 정의
const StyledDiv = styled.div`
  ${commonStyle}
`;

const StyledButton = styled.button`
  ${commonStyle}
`;

// 사용 예
const Component = () => {
  return (
    <StyledDiv>
      <StyledButton>Click Me</StyledButton>
    </StyledDiv>
  );
};

export default Component;

결론

  • 지금까지 프로젝트에서 주로 styled-components를 사용해 왔는데, Emotion도 거의 같은 문법에 같은 기능을 제공하는 것으로 보인다. 성능에 있어서도 큰 차이를 보이지 않고, Emotion 사용시 의존하는 모듈을 함께 설치할 경우 라이브러리 용량도 크기가 의미 있을 만큼의 차이가 없다.

  • 생각보다도 두 라이브러리에 큰 차이가 없어서 추후 프로젝트 진행 시 팀원들에게 좀더 익숙한 라이브러리를 선택하게 될 것 같다.

  • 이번 프로젝트 리팩토링에는 Emotion을 적용시켜볼까 한다. 조사로 알아보는 것 보다도, 실제 사용하면서 느끼는 차이점이 있을 것 같다.

참고자료

profile
FrontEnd Developer 🦁사자의 성장 로그

0개의 댓글