Tech/CSS

React in CSS

tanktwo 2022. 8. 1. 20:32
  1. 배경
  2. React의 다양한 CSS 사용방법
  3. 다양한 방법 비교
    1. default
      1. CSS
      2. 내 방법
    2. CSS-in-CSS
      1. CSS Module
      2. SASS(SCSS)
    3. CSS-in-JS
      1. Style Jsx
      2. Style Component
    4. Utilty First
      1. Tailwind
  4. 결론

 

1. 배경

React 에서 CSS를 사용하는 방법은 막연하게 “다양하다"고만 생각하고 있었고, 저는 가장 단순하고 직관적인 JSX 내부에 CSS를 입력하는 방법으로 사용했습니다.

반복되는 CSS는 컴포넌트화 시켜서 사용하긴 했지만, 그 프로젝트용 단발성으로 사용하고 다른 페이지(프로젝트)에서는 거의 사용되지 않았습니다.

그럼에도 크게 문제가 되지 않았던 이유는 페이지가 복잡하지 않고, 디자인의 중요도가 높지 않았기 때문입니다.

각 도메인에서 CSS Framework를 사용해서 (antd, material, bulma, bootstrap5) 쉽게 컴포넌트를 가져다 쓰고, 그 안에서 CSS를 조금씩 추가해서 사용해도 쉽게 만들 수 있었습니다.

최근 두달동안 프로젝트를 진행하면서 기존과는 다르게, 좁은 페이지 안에 상대적으로 복잡한 (반응하는) 기능, 버튼 등을 넣어야 했고,

기존의 JSX내부에 CSS를 사용하는 방법은 금새 지저분하고 한눈에 보이지 않는 코드가 되었다고 느껴졌습니다.

2. React의 CSS 사용방법

CSS 작성 방법은 다양합니다. 크게 둘로 나누면

  1. CSS-in-CSS : CSS나 SASS등 별도의 스타일 파일에 작성하는 전통적 방법
  2. CSS-in-JS, Utility-First (Tailwind CSS) 등 기존 문제를 해결하기 위해 등장한 새로운 방법
  • npm trend에서 최근 가장 많이 사용한 css 모듈들을 비교

https://npmtrends.com/@emotion/core-vs-styled-components-vs-styled-jsx-vs-tailwindcss

3. 다양한 방법 비교

React, Typescript 만 사용하여 비교 intelli j - new project

3-0-0. CSS (default)

https://codesandbox.io/embed/vigilant-moon-6rv0f0?fontsize=14&hidenavigation=1&theme=dark

// App.tsx
import React from 'react';
import './App.css';

function App() {
  return (
    <div className="App">
      <div className="box">
        <div className="box-red"/>
        <div className="box-orange"/>
        <div className="box-yellow"/>
        <div className="box-green"/>
        <div className="box-blue"/>
        <div className="box-indigo"/>
        <div className="box-purple"/>
      </div>
    </div>
  );
}

export default App;
// App.css

.App {
    text-align: center;
}

.box {
    background-color: white;
    width: 90vw;
    height: 90vh;
    border: 1px solid;
    margin: 10px;
    display: flex;
}

.box > *:hover {
    background-color: black
}

.box-red {
    background-color: red;
    width: 50px;
    height: 50px;
}

.box-orange {
    background-color: orange;
    width: 100px;
    height: 100px;
}

.box-yellow {
    background-color: yellow;
    width: 150px;
    height: 150px;
}

.box-green {
    background-color: green;
    width: 200px;
    height: 200px;
}

.box-blue {
    background-color: blue;
    width: 250px;
    height: 250px;
}

.box-indigo {
    background-color: indigo;
    width: 300px;
    height: 300px;
}

.box-purple {
    background-color: purple;
    width: 350px;
    height: 350px;
}

 

3-0-1. 내가 사용하던 방법

JSX 의 style에 객체를 넣어주는 방식

// App.js
import React from 'react';
import ColorBox from './ColorBox';

function App() {
  return (
    <div className="App" style={{
      textAlign: "center"
    }}>
      <p>3-0-1 My Way</p>
      <div className="box" style={{
        backgroundColor: "white",
        width: "90vw",
        height: "90vh",
        border: "1px solid",
        margin: "10px",
        display: "flex"
      }}>
        <ColorBox color="red"/>
        <ColorBox color="orange"/>
        <ColorBox color="yellow"/>
        <ColorBox color="green"/>
        <ColorBox color="blue"/>
        <ColorBox color="indigo"/>
        <ColorBox color="purple"/>
      </div>
    </div>
  );
}

export default App;
// ColorBox.tsx
import React, {useState} from "react";

const colorList = ['black', 'red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'purple'] as const;
type color = typeof colorList[number]

export default function ColorBox({color}: { color: color }) {
  const [style, setStyle] = useState({backgroundColor: color, width: handleSizeByColor(), height: handleSizeByColor()});

  const onMouseEnter = () => setStyle({...style, backgroundColor: 'black'})
  const onMouseLeave = () => setStyle({...style, backgroundColor: color})

  function handleSizeByColor() {
    switch (color) {
      case 'red':
        return 50
      case 'orange':
        return 100
      case 'yellow':
        return 150
      case 'green':
        return 200
      case 'blue':
        return 250
      case 'indigo':
        return 300
      default:
        return 350
    }
  }

  return (
    <div style={style} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}/>
  )
}

 

3-1. CSS-in-CSS

CSS나 SASS등 별도의 스타일 파일에 작성하는 전통적 방법

 

3-1-1. CSS Module

CSS를 사용할 때 클래스 이름을 고유한 값으로 자동으로 만들어서 컴포넌트 스타일 클래스 이름이 중첩되는 현상을 방지해 주는 기술

  • 장점
    • 클래스명 고민 및 충돌 가능성 제거
  • 단점
    • 기존의 CSS의 단점
    • 컴포넌트별 CSS파일을 만들어서 관리
// App.js
import React from "react";
import styles from "./Box.module.css";

function App() {
  return (
    <div className={styles.App}>
      <p>3-1-1 CSS Module</p>
      <div className={styles.box}>
        <div className={styles.red} />
        <div className={styles.orange} />
        <div className={styles.yellow} />
        <div className={styles.green} />
        <div className={styles.blue} />
        <div className={styles.indigo} />
        <div className={styles.purple} />
      </div>
    </div>
  );
}

export default App;
// Box.module.css

.App {
    text-align: center;
}

.box {
    background-color: white;
    width: 90vw;
    height: 90vh;
    border: 1px solid;
    margin: 10px;
    display: flex;
}

.box > *:hover {
    background-color: black;
}

.red {
    background-color: red;
    width: 50px;
    height: 50px;
}

.orange {
    background-color: orange;
    width: 100px;
    height: 100px;
}

.yellow {
    background-color: yellow;
    width: 150px;
    height: 150px;
}

.green {
    background-color: green;
    width: 200px;
    height: 200px;
}

.blue {
    background-color: blue;
    width: 250px;
    height: 250px;
}

.indigo {
    background-color: indigo;
    width: 300px;
    height: 300px;
}

.purple {
    background-color: purple;
    width: 350px;
    height: 350px;
}

 

3-1-2. SCSS(SASS)

코드의 재활용성을 올리고, 가독성을 올리는 등 CSS에서 보이던 단점을 보완하고, 개발의 효율을 올리기 위해 만들어진 CSS 전처리기 입니다.

CSS를 편리하게 사용할 수 있도록 하며 추가 기능 또한 있는 확장판

  • 장점
    • css 파일(.scss) 내에서 SCSS 내장 함수, 변수, 조건문, 반복문을 사용 가능
    • CSS보다 심플한 표기법으로 CSS를 구조화
    • 가독성과 재사용성을 높여주어 유지보수가 쉬워짐
  • 단점
    • 전처리기를 위한 모듈 필요 (node-sass)
    • 컴파일 시간 소요
// App.js
import React from "react";
import './App.scss';

function App() {
  return (
    <div className="App">
      <p>3-1-2 SCSS</p>
      <div className="box">
        <div className="custom-box red"/>
        <div className="custom-box orange"/>
        <div className="custom-box yellow"/>
        <div className="custom-box green"/>
        <div className="custom-box blue"/>
        <div className="custom-box indigo"/>
        <div className="custom-box purple"/>
      </div>
    </div>
  );
}

export default App;
// App.scss
$red : #FF0000FF;
$lightRed : lighten($red, 20%);

.App {
  text-align: center;
}

.box {
  background-color: white;
  width: 90vw;
  height: 90vh;
  border: 1px solid;
  margin: 10px;
  display: flex;
}

.custom-box {
  &.red {
    background-color: $lightRed;
    width: 50px;
    height: 50px;
  }

  &.orange {
    background-color: orange;
    width: 100px;
    height: 100px;
  }

  &.yellow {
    background-color: yellow;
    width: 150px;
    height: 150px;
  }

  &.green {
    background-color: green;
    width: 200px;
    height: 200px;
  }

  &.blue {
    background-color: blue;
    width: 250px;
    height: 250px;
  }

  &.indigo {
    background-color: indigo;
    width: 300px;
    height: 300px;
  }

  &:hover {
    background: lighten(black, 30%);
  }

  &.purple {
    background-color: purple;
    width: 350px;
    height: 350px;
  }
}

 

3-2. CSS in JS

자바스크립트 코드에서 CSS를 작성하는 방식.

2014년 페이스북 개발자인 Christopher Chedeau aka Vjeux가 처음으로 기존 CSS 관리의 어려움을 해결한 페이스북의 사례와 함께 소개했습니다.

http://blog.vjeux.com/2014/javascript/react-css-in-js-nationjs.html

공통적인 장단점

  • 장점 - 클래스명 고민 및 충돌 가능성 제거
  • 단점 - CSS in CSS보다 상대적으로 페이지 전환 시간이 길다

 

3-2-1. Styled JSX

Next.js의 자체 CSS 라이브러리

  • 장점
    • 하위 컴포넌트로 CSS속성이 전달 X
  • 단점?
    • Next.js 를 사용하지 않는다면 굳이 쓸정도로 메리트가 없음
export default function Home() {
  return (
    <div className="container">
      <p>3-2-1 Style_Jsx</p>
      <div className="box">
        <div className="red"/>
        <div className="custom-box-red"/>
        <div className="custom-box-orange"/>
        <div className="custom-box-yellow"/>
        <div className="custom-box-green"/>
        <div className="custom-box-blue"/>
        <div className="custom-box-indigo"/>
        <div className="custom-box-purple"/>
      </div>
      <style jsx>{`
        .container {
          text-align: center;
        }
        .box {
          background-color: white;
          width: 90vw;
          height: 90vh;
          border: 1px solid;
          margin: 10px;
          display: flex;
        }
        
        .box > *:hover {
          background-color: black
        }
        
        .custom-box-red {
            background-color: red;
            width: 50px;
            height: 50px;
        }
        
        .custom-box-red {
            background-color: red;
            width: 50px;
            height: 50px;
        }
        
        .custom-box-orange {
            background-color: orange;
            width: 100px;
            height: 100px;
        }
        
        .custom-box-yellow {
            background-color: yellow;
            width: 150px;
            height: 150px;
        }
        
        .custom-box-green {
            background-color: green;
            width: 200px;
            height: 200px;
        }
        
        .custom-box-blue {
            background-color: blue;
            width: 250px;
            height: 250px;
        }
        
        .custom-box-indigo {
            background-color: indigo;
            width: 300px;
            height: 300px;
        }
        
        .custom-box-purple {
            background-color: purple;
            width: 350px;
            height: 350px;
        }
      `}</style>

      <style jsx global>{`
        html,
        body {
          padding: 0;
          margin: 0;
          font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
            Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue,
            sans-serif;
        }

        * {
          box-sizing: border-box;
        }
      `}</style>
    </div>
  )
}

 

3-2-2. Style Component

  • 장점
    • CSS를 페이지 단위가 아닌 컴포넌트 레벨로 구현
    • 자바스크립트와 CSS 사이의 변수와 함수를 공유
  • 단점
    • 러닝 커브
    • 새로운 의존성 발생
// App.js
import styled from 'styled-components';

const Container = styled.div`
  text-align: center
`;
const Box = styled.div`
  background-color: white;
  width: 90vw;
  height: 90vh;
  border: 1px solid;
  margin: 10px;
  display: flex;
`;
const CustomBox =  styled.div`
  width: ${(props) => handleSizeByColor(props.color)}px;
  height: ${(props) => handleSizeByColor(props.color)}px;
  background-color: ${(props) => props.color};
  &:hover { background-color: black; }
`;

function handleSizeByColor(color) {
  switch (color) {
    case 'red':
      return 50
    case 'orange':
      return 100
    case 'yellow':
      return 150
    case 'green':
      return 200
    case 'blue':
      return 250
    case 'indigo':
      return 300
    default:
      return 350
  }
}

function App() {
  return (
    <Container>
      <p>3-2-2 Style Component</p>
      <Box>
        <CustomBox color='red' />
        <CustomBox color='orange' />
        <CustomBox color='yellow' />
        <CustomBox color='green' />
        <CustomBox color='blue' />
        <CustomBox color='indigo' />
        <CustomBox color='purple' />
      </Box>
    </Container>
  );
}

export default App;

 

3-3 Utility-First (유틸 CSS)

 

3-3-1. Tailwind (소개)

import React from "react";

function Button() {

return (
    <button className="px-8 py-4 m-0 h-12 border border-solid rounded-lg bg-white">
        버튼
    </button>
}
  • Tailwind는 주로 사용하는 CSS를 모아둔 집합.
  • 주로 사용되는 숫자를 모아두고 이 중에서 사용할 수 있도록 해줍니다. fontSize는 ‘xs’, ‘sm’, ‘base’, ‘lg’, ‘xl’, ‘2xl’, …, ‘9xl’ 의 크기만 지원하고 있어서 이 중에서 선택하여 사용할 수 있습니다.
예시 : <div className="p-[8px] h-[35px] text-[12px] rounded-[5px]">
  • 장점
    • 빠른 개발
    • 빌드 시, 사용하지 않는 클래스가 제거되어 번들 크기가 최소로 유지
    • 일관된 디자인 구축 가능
    • 수정시 일일이 필요한 코드를 찾을 필요가 없음
  • 단점
    • 러닝 커브
    • 동적 변수를 사용할 수 없는 문제 (JavaScript 코드 사용 불가)
    • **애니메이션(animation)과 트랜지션(transition) 사용에 제약**이 있습니다. **animate-spin**과 같이 미리 만들어진 애니메이션만 제공하기 때문에 필요한 애니메이션을 추가해야 합니다.
  •  

 

결론

 당장 내부툴을 위한 도메인이고 나혼자 개발하는것이 아니기 때문에, 굳이 다른 라이브러리를 사용하는것 보다는 CSS Module을 사용하는게 좋겠다고 생각했다. 만약 라이브러리를 사용한다면 Tailwind를 시도해 볼 것 같다.

'Tech > CSS' 카테고리의 다른 글

Position  (0) 2023.07.17