새소식

반응형
Study

Emotion의 배경지식 / 사용법 (CSS in JS)

  • -
반응형

Emotion?

Emotion은 JavaScriptcss 스타일을 작성하도록 설계된 라이브러리입니다.

본격적으로 Emotion에 대해 알아보기 이전에 간단히 다양한 웹 스타일링 기술을 알아보겠습니다.

 

다양한 웹 스타일링 기술

CSS

CSS(Cascading Style Sheets)는 HTML 이나 XML로 작성된 문서의 표시 방법을 기술하기 위한 스타일 시트 언어입니다.
CSS는 요소가 화면, 종이, 음성이나 다른 매체 상에 어떻게 렌더링 되어야 하는지 지정합니다.

CSS의 문제점

- Global namespace: 모든 스타일이 global에 선언되어 중복되지 않는 class 이름을 적용해야 한다.
- Dependencies: CSS간의 의존 관계를 관리 하기 힘들다
- Dead Code Elimination: 기능 추가, 변경, 삭제 과정에서 불필요한 CSS를 제거하기 어렵다.
- Minification: 클래스 이름의 최소화가 어렵다.
- Sharing Constants: JS 코드와 상태값을 공유 할수 없다.
- Non-deterministic Resolution: CSS 로드 순서에 따라 스타일 우선 순위가 달라진다.
- Isolation: CSS와 JS가 분리된 탓에 상속에 따른 격리가 어렵다.

feat. Christopher Chedeau(일명 Vjeux)- 페이스북 Front-end engineer

 

* CSS-in-CSS

CSS 모듈(Module)

CSS를 사용 할 때 클래스 이름을 고유한 값으로 자동으로 만들어서 컴포넌트 스타일 클래스 이름이 중첨되는 현상을 방지해 주는 기술
- CSS 모듈을 이용하면 클래스명이 충돌하는 단점을 극복할 수 있다.
- CSS 모듈은 컴포넌트 단위로 스타일을 적용할 때 유용하다.

CSS 전처리기(Preprocessor)

자신만의 특별한 Syntax를 가지고 CSS를 생성하도록 하는 프로그램. CSS의 문제점을 프로그래밍방식(변수, 함수, 상속 등)을 사용 보완하였습니다. CSS 전처리기에는 다양한 모듈이 존재하는데 Sass, Less, Stylus 가 대표적입니다.

* CSS-in-JS

CSS-in-JS는 단어 그대로 자바스크립트 코드에서 CSS를 작성하는 방식을 말합니다. 2014년 페이스북 개발자인 Christopher Chedeau aka Vjeux가 처음 소개하였습니다.

Styled Components, Emotion가 대표적입니다.

Styled-components

기존 돔을 만드는 방식인 CSS, SCSS 파일을 밖에 두고, 태그나 Id, class 이름으로 가져와 쓰지 않고, 동일한 컴포넌트에서 컴포넌드 이름을 쓰듯 스타일을 지정하는 것을 styled-components 라고 부릅니다.

 

Emotion 사용법

emotion.js는 주로 Framwork Agnostic 과 React 두가지 방식으로 사용합니다.

설치

# Framework Agnostic
$ npm install @emotion/css
or
$ yarn add @emotion/css

# React
$ npm install @emotion/react
or
$ yarn add @emotion/react

 

사용 (공식문서 예문)

ccs() 함수는 CSS 스타일 선언 내용을 인자로 받는데 문자형과 객체형으로 넘길수 있습니다. 

// 문자형 스타일
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

const color = 'white'

render(
  <div
    css={css`
      padding: 32px;
      background-color: hotpink;
      font-size: 24px;
      border-radius: 4px;
      &:hover {
        color: ${color};
      }
    `}
  >
    Hover to change color.
  </div>
)
// 객체형 스타일
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

const color = 'white'

render(
  <div
    css={css({
      padding: '32px',
      backgroundColor: 'hotpink',
      fontSize: '24px',
      borderRadius: '4px',
      cursor: 'pointer',
      '&:hover': {
        color: `${color}`,
      },
    })}
  >
    Hover to change color.
  </div>
)

JSX pragma

emotion의 css prop을 제대로 사용하기 위해서는 파일 상단에 pragma 를 선언해 주어야 합니다.

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

pragma란 컴파일러에게 하는 전처리 명령이라고 생각하면 되는데 babel 트랜스파일러에게 JSX코드를 변환할때 React의 jsx() 함수 대신 Emotion의 jsx() 함수를 사용하라고 알려주는 역할을 합니다.

React v16 이하 의 오래된 버전을 사용하고 있는 프로젝트에서는 약간 다른 형태로 사용하고 jsx()함수도 불러와야 합니다.

// React v16 이하
/** @jsx jsx */
import { css, jsx } from "@emotion/react";

pragma 지우기

매번 사용할 때 마다 pragma를 사용하는건 무척 번거로운 일입니다.

사용하는 버전에 따라 @emotion/babel-prest-css-prop 나 @babel/preset-react 의 importSource옵션 변경을 통하여 pragma 없이 사용할 수 있습니다.

저 같은 경우에는 Next.js 세팅을 하고 있는데 Next.js의 경우는 tsconfig.json 에 아래 옵션만 추가해주시면 간단히 해결됩니다.

// tsconfig.json
"jsxImportSource": "@emotion/react",

일반적인 스타일링

<button> 요소의 CSS prop을 통해서 다양한 CSS 속성 정의를 객체로 넘길 수 있습니다.

// EmotionBtn.js

function EmotionBtn({ children }) {
  return (
    <button
      css={{
        border: '1px solid gray',
        borderRadius: '6px',
        color: 'black',
        fontSize: '14px',
        padding: '10px 16px',
        cursor: 'pointer',
      }}
    >
      {children}
    </button>
  )
}

export default EmotionBtn

만든 컴포넌트를 사용합니다.

import EmotionBtn from 'EmotionBtn'

function App() {
  return <EmotionBtn>Button</EmotionBtn>
}

해당 버튼을 브라우저에서 확인해보면 <button> 요소에 Emotion이 자동으로 생성해준 클래스 이름이 붙어 있는 것을 확인하실 수 있습니다. (클래스 이름은 랜덤으로 생성됩니다.)

<button class="css-1ayjb79-Btn">Button</button>

스타일도 확인해 보면 선언한내용과 같은것을 확인 할 수 있습니다. 선언한 것과 다른것이 있다면 Emotion에서 자동으로 브라우저별 필요한 vendor prefixing을 해줍니다.

.css-1ayjb79-Btn {
  border: 1px solid gray;
  border-radius: 6px;
  color: black;
  font-size: 14px
  padding: 10px 16px;
  cursor: pointer;
  -webkit-appearance: none;
  -moz-appearance: none;
  -ms-appearance: none;
  appearance: none;
}

Prop을 이용한 스타일링

Prop에 따라 스타일의 변화를 주는 스타일링을 할 수 있습니다. <VariableBtn> 컴포넌트에 variant 라는 prop을 추가하고 변화를 주어 스타일링을 해보도록 하겠습니다.

// VariableBtn.js

const colors = {
  default: 'black',
  danger: 'red',
  outline: 'blue',
}

function VariableBtn({ children, variant }) {
  return (
    <button
      css={{
        border: '1px solid gray',
        borderRadius: '6px',
        color: colors[variant],
        fontSize: '14px',
        padding: '10px 16px',
        cursor: 'pointer',
        appearance: 'none',
        userSelect: 'none',
      }}
    >
      {children}
    </button>
  )
}

export default VariableBtn

사용할 때에는 아래와 같이 porp에 값을 변경시켜 줍니다.

import VariableBtn from 'VariableBtn'

function App() {
  return (
    <>
      <VariableBtn variant="default">default</VariableBtn>
      <VariableBtn variant="danger">danger</VariableBtn>
      <VariableBtn variant="outline">outline</VariableBtn>
    </>
  );
}

prop을 이용해서 스타일링의 변화를 줄때 하나의 속성만이 아니라 여러개를 이용 할 수도 있습니다. 이런 경우에는 CSS속성을 객체로 만들어줍니다.

// MultiVariableBtn.js

const colors = {
  default: 'black',
  danger: 'red',
  outline: 'blue',
}

const sizeStyle = {
  sm: {
    fontSize: '12px',
    padding: '3px 12px',
  },
  md: {
    fontSize: '14px',
    padding: '5px 16px',
  },
  lg: {
    fontSize: '16px',
    padding: '9px 20px',
  },
}

function MultiVariableBtn({ children, size = 'md', variant = 'default' }) {
  return (
    <button
      css={{
        border: '1px solid gray',
        borderRadius: '6px',
        color: colors[variant],
        ...sizeStyle[size],
        cursor: 'pointer',
        appearance: 'none',
        userSelect: 'none',
      }}
    >
      {children}
    </button>
  )
}

export default MultiVariableBtn

사용할 때에는 간단히 props를 전달해주면 됩니다.

import MultiVariableBtn from 'MultiVariableBtn'

function App() {
  return (
    <>
      <MultiVariableBtn size="sm" variant="defalt">
        Sm Size
      </MultiVariableBtn>
      <MultiVariableBtn size="md" variant="danger">
        Md Size
      </MultiVariableBtn>
      <MultiVariableBtn size="lg" variant="outline">
        Lg Size
      </MultiVariableBtn>
    </>
  );
}

Global 스타일링

전역에 스타일링이 필요한 경우에도 간편하게 스타일링을 추가 할수 있습니다.

// Global.js

import { Global } from '@emotion/react'

const GlobalStyle = () => (
  <Global
    styles={{
      '*': {
        color: 'blue',
      },
      a: {
        color: 'red',
      },
    }}
  />
)

export default GlobalStyle
// index.js

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import GlobalStyle from "./global";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <React.StrictMode>
    <GlobalStyle />
    <App />
  </React.StrictMode>
);

 

Reference

https://emotion.sh/docs/introduction

https://www.daleseo.com/emotion/

https://gong-check.github.io/dev-blog/FE/%EC%98%A8%EC%8A%A4%ED%83%80/emotion%20%EC%A0%81%EC%9A%A9%EA%B8%B0/

https://tech.osci.kr/2022/06/14/%EC%9B%B9-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90-%EC%8A%A4%ED%83%80%EC%9D%BC-%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0-with-emotion/

https://velog.io/@nightowl094/Next.js-Emotion-css-prop-%EC%84%A4%EC%A0%95

반응형

'Study' 카테고리의 다른 글

Next.js 기초부터 알아보기  (0) 2023.01.10
스토리북으로 개발하기  (0) 2022.12.20
Jest로 테스트코드 작성하기  (0) 2022.12.12
React Query  (0) 2022.11.28
React 상태관리 라이브러리 1탄 (Redux)  (0) 2022.11.21
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.