ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 6. CSS Module - 컴포넌트 스타일링 | 벨로퍼트
    Front-end/React.js 2020. 7. 16. 22:34
    반응형

     

    이번에는 CSS Module 이라는 기술에 대해서 알아봅시다. 리액트 프로젝트에서 컴포넌트를 스타일링 할 때 CSS Module 이라는 기술을 사용하면, CSS 클래스가 중첩되는 것을 완벽히 방지할 수 있습니다.

     

    CRA 로 만든 프로젝트에서 CSS Module 를 사용 할 때에는, CSS 파일의 확장자를 .module.css 로 하면 되는데요, 예를 들어서 다음과 같이 Box.module.css 라는 파일을 만들게 된다면

    /src/Box.module.css :

    .Box {
      background: black;
      color: white;
      padding: 2rem;
    }

    리액트 컴포넌트 파일에서 해당 CSS 파일을 불러올 때 CSS 파일에 선언한 클래스 이름들이 모두 고유해집니다. 고유 CSS 클래스 이름이 만들어지는 과정에서는 파일 경로, 파일 이름, 클래스 이름, 해쉬값 등이 사용 될 수 있습니다.

     

    예를 들어서 Box 컴포넌트를 만든다면 다음과 같이 코드를 작성하는데요

    /src/Box.js :

    import React from "react";
    import styles from "./Box.module.css";
    
    function Box() {
      return <div className={styles.Box}>{styles.Box}</div>;
    }
    
    export default Box;

    className을 설정 할 때에는 styles.Box 이렇게 import로 불러온 styles 객체 안에 있는 값을 참조해야 합니다.

    [출처 : https://react.vlpt.us/styling/02-css-module.html]

    클래스 이름에 대하여 고유한 이름들이 만들어지기 때문에, 실수로 CSS 클래스 이름이 다른 관계 없는 곳에서 사용한 CSS 클래스 이름과 중복되는 일에 대하여 걱정 할 필요가 없습니다.

     

    이 기술은 다음과 같은 상황에 사용하면 유용합니다.

     · 레거시 프로젝트에 리액트를 도입할 때 (기존 프로젝트에 있던 CSS 클래스와 이름이 중복되어도 스타일이 꼬이지 않게 해줍니다.)

     · CSS 클래스를 중복되지 않게 작성하기 위하여 CSS 클래스 네이밍 규칙을 만들기 귀찮을 때

     

    리액트 컴포넌트를 위한 클래스를 작성 할 때 제가 자주 사용하는 CSS 클래스 네이밍 규칙은 다음과 같습니다.

     1. 컴포넌트의 이름은 다른 컴포넌트랑 중복되지 않게 한다.

     2. 컴포넌트의 최상단 CSS 클래스는 컴포넌트의 이름과 일치시킨다. (예: .Button)

     3. 컴포넌트 내부에서 보여지는 CSS 클래스는 CSS Selector 를 잘 활용한다. (예: .MyForm .my-input)

     

    이런 규칙 외에도 BEM Convention 이란 것 도 있는데, 개인적으로 리액트 컴포넌트와 사용하기엔 불편한 점이 있어서 부적합하다고 생각합니다 (주관적인 의견입니다.)

     

    만약 CSS 클래스 네이밍 규칙을 만들고 따르기 싫다면, CSS Module 을 사용하시면 됩니다.

     

    이번 튜토리얼에서는, 새로운 리액트 프로젝트를 생성해서 CSS Module 기술을 사용하여 커스텀 체크박스 컴포넌트를 만드는 방법을 배워보도록 하겠습니다.

     

    우선 새로운 프로젝트를 생성해주세요.

    $ npx create-react-app styling-with-css-module

     

    그리고, CSS Module 별도로 설치해야 할 라이브러리는 없습니다. 이 기능은 webpack 에서 사용하는 css-loader 에서 지원되는데, CRA 로 만든 프로젝트에는 이미 적용이 되어있으니 바로 사용하면 됩니다.

     

    프로젝트를 에디터로 열어주시고, src 디렉터리에 components 디렉터리를 만든 후 , 그 안에 CheckBox.js 를 생성해주세요.

     

    먼저 CheckBox 컴포넌트의 틀 부터 준비해주겠습니다.

    /src/components/CheckBox.js :

    import React from 'react';
    
    function CheckBox({ children, checked, ...rest }) {
      return (
        <div>
          <label>
            <input type="checkbox" checked={checked} {...rest} />
            <div>{checked ? '체크됨' : '체크 안됨'}</div>
          </label>
          <span>{children}</span>
        </div>
      );
    }
    
    export default CheckBox;

    지금 당장은, 스타일링도 하지 않고, 체크 아이콘도 사용하지 않고 그냥 이 컴포넌트에 필요한 HTML 태그들만 미리 선언을 해주었습니다.

     

    여기서 ...rest 를 사용한 이유는, CheckBox 컴포넌트에게 전달하게 될 name, onChange 같은 값을 그대로 input 에게 넣어주기 위함입니다.

     

    다 만드셨으면 App 컴포넌트에서 렌더링해보세요.

    /src/App.js :

    import React, { useState } from 'react';
    import CheckBox from './components/CheckBox';
    
    function App() {
      const [check, setCheck] = useState(false);
      const onChange = e => {
        setCheck(e.target.checked);
      };
      return (
        <div>
          <CheckBox onChange={onChange} checked={check}>
            다음 약관에 모두 동의
          </CheckBox>
          <p>
            <b>check: </b>
            {check ? 'true' : 'false'}
          </p>
        </div>
      );
    }
    
    export default App;

    이제 yarn start 명령어를 사용하여 개발 서버를 열으신 뒤, 다음과 같이 체크 안됨 문구를 눌렀을 때 체크박스의 값이 잘 바뀌는지 확인해보세요.

     

    지금 input 이 아닌 텍스트 부분을 선택했는데도 값이 바뀌는 이유는 현재 우리가 해당 내용을 label 태그로 감싸줬기 때문입니다.

     

    이제, 스타일링을 해봅시다! 스타일링을 하기 전에 react-icons 라는 라이브러리를 설치해주세요.

    $ yarn add react-icons

     

    이 라이브러리를 사용하면 Font Awesome, Ionicons, Material Design Icons, 등의 아이콘들을 컴포넌트 형태로 쉽게 사용 할 수 있습니다. 해당 라이브러리의 문서 를 열으셔서 원하는 아이콘들을 불러와서 사용하시면 되는데요, 우리는 Material Design Icons 의 MdCheckBox, MdCheckBoxOutline 을 사용하겠습니다.

     

    CheckBox 컴포넌트를 다음과 같이 수정해보세요.

    /src/components/CheckBox.js :

    import React from 'react';
    import { MdCheckBox, MdCheckBoxOutlineBlank } from 'react-icons/md';
    
    function CheckBox({ children, checked, ...rest }) {
      return (
        <div>
          <label>
            <input type="checkbox" checked={checked} {...rest} />
            <div>{checked ? <MdCheckBox /> : <MdCheckBoxOutlineBlank />}</div>
          </label>
          <span>{children}</span>
        </div>
      );
    }
    
    export default CheckBox;

    이렇게 수정을 해주면, 텍스트 대신 아이콘이 나타나게 될 것입니다. 이제 컴포넌트를 스타일링 해봅시다.

     

    CheckBox.module.css 파일을 components 디렉터리에 생성 후 다음 코드를 입력해주세요.

    /src/components/CheckBox.module.css : 

    .checkbox {
      display: flex;
      align-content: center;
    }
    
    .checkbox label {
      cursor: pointer;
    }
    
    /* 실제 input 을 숨기기 위한 코드 */
    .checkbox input {
      width: 0;
      height: 0;
      position: absolute;
      opacity: 0;
    }
    
    .checkbox span {
      font-size: 1.125rem;
      font-weight: bold;
    }
    
    .icon {
      display: flex;
      align-content: center;
      /* 아이콘의 크기는 폰트 사이즈로 조정 가능 */
      font-size: 2rem;
      margin-right: 0.25rem;
      color: #adb5bd;
    }
    
    .checked {
      color: #339af0;
    }

    CSS Module 을 작성 할 때에는 CSS 클래스 이름이 다른 곳에서 사용되는 CSS 클래스 이름과 중복될 일이 없기 때문에 .icon, .checkbox 같은 짧고 흔한 이름을 사용해도 상관이 없습니다.

     

    CSS 코드를 다 작성하셨으면 CheckBox.js 에서 사용을 해보겠습니다.

    /src/components/CheckBox.js :

    import React from 'react';
    import { MdCheckBox, MdCheckBoxOutlineBlank } from 'react-icons/md';
    import styles from './CheckBox.module.css';
    
    function CheckBox({ children, checked, ...rest }) {
      return (
        <div className={styles.checkbox}>
          <label>
            <input type="checkbox" checked={checked} {...rest} />
            <div className={styles.icon}>
              {checked ? (
                <MdCheckBox className={styles.checked} />
              ) : (
                <MdCheckBoxOutlineBlank />
              )}
            </div>
          </label>
          <span>{children}</span>
        </div>
      );
    }
    
    export default CheckBox;

    이제, 우리 컴포넌트의 스타일이 잘 반영됐는지 확인해보세요.

     

    개발자 도구로 엘리먼트를 선택해보시면 다음과 같이 고유한 클래스 이름이 만들어진 것을 확인 할 수 있습니다.

     

    CSS Module 을 사용 할 때에는 styles.icon 이런 식으로 객체안에 있는 값을 조회해야 하는데요, 만약 클래스 이름에 - 가 들어가 있다면 다음과 같이 사용해야합니다: styles['my-class']

     

    그리고, 만약에 여러개가 있다면 다음과 같이 작성해합니다: ${styles.one} ${styles.two}

     

    조건부 스타일링을 해야 한다면 더더욱 번거롭겠지요? ${styles.one} ${condition ? styles.two : ''}

     

    우리가 이전 섹션에서 Sass 를 배울 때 썼었던 classnames 라이브러리에는 bind 기능이 있는데요, 이 기능을 사용하면 CSS Module 을 조금 더 편하게 사용 할 수 있습니다.

     

    우선, 설치를 해주세요.

    $ yarn add classnames

     

    그 다음, CheckBox.js 를 다음과 같이 수정해보세요.

    /src/components/CheckBox.js :

    import React from 'react';
    import { MdCheckBox, MdCheckBoxOutlineBlank } from 'react-icons/md';
    import styles from './CheckBox.module.css';
    import classNames from 'classnames/bind';
    
    const cx = classNames.bind(styles);
    
    function CheckBox({ children, checked, ...rest }) {
      return (
        <div className={cx('checkbox')}>
          <label>
            <input type="checkbox" checked={checked} {...rest} />
            <div className={cx('icon')}>
              {checked ? (
                <MdCheckBox className={cx('checked')} />
              ) : (
                <MdCheckBoxOutlineBlank />
              )}
            </div>
          </label>
          <span>{children}</span>
        </div>
      );
    }
    
    export default CheckBox;

    classnames 의 bind 기능을 사용하면, CSS 클래시 이름을 지정해 줄 때 cx('클래스이름') 과 같은 형식으로 편하게 사용 할 수 있습니다. 여러개의 CSS 클래스를 사용해야하거나, 조건부 스타일링을 해야 한다면 더더욱 편하겠지요?

    cx('one', 'two')
    cx('my-component', {
      condition: true
    })
    cx('my-component', ['another', 'classnames'])

     

     

    기타 내용

    참고로, CSS Module 은 Sass 에서도 사용 할 수 있습니다. 그냥 확장자를 .module.scss 로 바꿔주면 됩니다. 물론, 그 전에 node-sass 를 설치해야합니다.

     

    그리고, CSS Module 을 사용하고 있는 파일에서 클래스 이름을 고유화 하지 않고 전역적 클래스이름을 사용하고 싶다면 다음과 작성하면 됩니다.

    :global .my-global-name {
    
    }

    만약 Sass 를 사용한다면 다음과 같이 할 수도 있겠죠.

    :global {
      .my-global-name {
    
      }
    }

    반대로, CSS Module 을 사용하지 않는 곳에서 특정 클래스에서만 고유 이름을 만들어서 사용하고 싶다면 다음과 같이 할 수 있습니다.

    :local .make-this-local {
    
    }

    Sass 라면 이렇게 표현 할수 있겠죠?

    :local {
      .make-this-local {
    
      }
    }

     

     


    SUMMARY

    이번 튜토리얼에서는 CSS Module을 사용하는 방법을 배웠습니다. 이 기술은 레거시 프로젝트에 리액트를 도입하게 될 때, 또는 클래스 이름 짓는 규칙을 정하기 힘든 상황이거나 번거로울 때 사용하면 편합니다.

    반응형

    댓글

Luster Sun