React에서 eject없이 Scoped SASS (.scss) 사용

개요


  • scoped sass (파일 내 한정으로 적용되는 스타일)를 사용하고 싶다.
  • 하지만 npm run eject
  • cra-sass 를 도입하면 간단하게 할 수 있을 것 같다

  • 참고문헌


  • How to use Sass and CSS Modules with create-react-app
  • leighs-hammer/cra-sass

  • 실행 환경


  • create-react-app 로 만든 react project
  • 기존 프로젝트이므로 version 모르겠습니다

  • TypeScript

  • 샘플 코드 (변경 전)


    node-sass 를 넣고 보통에 scss를 사용하면 이렇게 된다.

    Sample.tsx



    Sample.tsx
    import * as React from "react";
    import "./Sample.scss";
    
    export const Sample: React.FC = () => {
      return (
        <div className="outer">
          OUTER
          <div className="inner">INNER</div>
          <ul>
            {["red", "blue", "green"].map((each, index) => (
              <li className={each} key={index}>
                {each.toUpperCase()}
              </li>
            ))}
          </ul>
        </div>
      );
    };
    
    export default Sample;
    

    Sample.scss



    Sample.scss
    .outer {
      &,
      * {
        display: flex;
        flex-direction: column;
        padding: 8px;
        border-left: 1px solid gray;
      }
    
      font-size: 1.2rem;
      .inner {
        font-weight: bold;
      }
      ul {
        li {
          &.red {
            color: red;
          }
          &.green {
            color: green;
          }
          &.blue {
            color: blue;
          }
        }
      }
    }
    

    빌드 결과(html)


    <div class="outer">
      OUTER
      <div class="inner">INNER</div>
      <ul>
        <li class="red">RED</li>
        <li class="blue">BLUE</li>
        <li class="green">GREEN</li>
      </ul>
    </div>
    

    실행 결과





    이 구현의 문제점



    Sample.scss에 설명 된 스타일의 범위는 전역입니다.
    즉, Sample.tsx 와 동시에 로드되는 컴퍼넌트에,
    같은 className (예 : outer)가 할당되면 서로 영향을 받아 버그의 원인이됩니다.

    해결책



    닫힌 scope를 취급할 수 있는 sass loader를 도입한다

    도입 절차



    cra-sass 실행npx cra-sass
    그러면 뭔가 가득 설치하고 프로젝트가 마 개조된다

    package.json
    @ devDependencies
    +    "cra-sass": "0.0.5",
    
    @ dependencies
    +    "node-sass-chokidar": "^1.4.0",
    +    "npm-add-script": "^1.1.0",
    +    "npm-run-all": "^4.1.5",
    
    @ scripts
    -    "start": "react-scripts start",
    -    "build": "react-scripts --max-old-space-size=2048 build",
    +    "start": "npm-run-all -p watch-css start-js",
    +    "build": "npm run build-css && react-scripts build",
         "test": "react-scripts test",
    -    "eject": "react-scripts eject"
    +    "eject": "react-scripts eject",
    +    "build-css": "node-sass-chokidar src/ -o src/",
    +    "watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive",
    +    "start-js": "react-scripts start"
    
    --max-old-space-size=2048 라든지 없어져 망가졌잖아!
    그래서 쓸데없이 쓰러진 사촌은 고쳐 둡니다.

    package.json
    -    "build": "react-scripts --max-old-space-size=2048 build",
    +    "build": "npm run build-css && react-scripts --max-old-space-size=2048 build",
    

    샘플 코드 (리팩터 후)



    Sample.scss


    Sample.module.scss 로 개명

    Sample.tsx


  • scss import
  • className의 할당 방법

  • 만 변경

    Sample.tsx
    import * as React from "react";
    import styles from "./Sample.module.scss";
    
    export const Sample: React.FC = () => {
      return (
        <div className={styles.outer}>
          OUTER
          <div className={styles.inner}>INNER</div>
          <ul>
            {["red", "blue", "green"].map((each, index) => (
              <li className={styles[each]} key={index}>
                {each.toUpperCase()}
              </li>
            ))}
          </ul>
        </div>
      );
    };
    
    export default Sample;
    

    빌드 결과


    <div class="Sample_outer__144wv">
      OUTER
      <div class="Sample_inner__EiBfI">INNER</div>
      <ul>
        <li class="Sample_red__1ktYQ">RED</li>
        <li class="Sample_blue__32ZOZ">BLUE</li>
        <li class="Sample_green__2OrZU">GREEN</li>
      </ul>
    </div>
    

    css 부분 발췌
    .Sample_outer__144wv {
      font-size: 1.2rem; }
      .Sample_outer__144wv,
      .Sample_outer__144wv * {
        display: flex;
        flex-direction: column;
        padding: 8px;
        border-left: 1px solid gray; }
      .Sample_outer__144wv .Sample_inner__EiBfI {
        font-weight: bold; }
      .Sample_outer__144wv ul li.Sample_red__1ktYQ {
        color: red; }
      .Sample_outer__144wv ul li.Sample_green__2OrZU {
        color: green; }
      .Sample_outer__144wv ul li.Sample_blue__32ZOZ {
        color: blue; }
    

    다른 일



    스크립트가 손상되지 않았는지 확인합시다.



    start, build, test 가 cra-sass 에 의해 파괴될 우려가 있다
    특히 default에서 변경하는 경우주의합시다.

    .css가 .scss와 같은 계층에 출력되기 때문에 멋지다.


  • node-sass-chokidar의 주름
  • 그러나 output하지 않게하는 옵션과 가연
  • 귀찮아서 다른 계층으로 뱉어서 ignore하기로 결정했습니다.

    package.json
    -    "build-css": "node-sass-chokidar src/ -o src/",
    -    "watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive",
    +    "build-css": "node-sass-chokidar src/ -o built-css/",
    +    "watch-css": "npm run build-css && node-sass-chokidar src/ -o built-css/ --watch --recursive",
    

    .gitignore
    +/built-css
    

    node-sass를 이미 사용한 경우 더 이상 필요하지 않습니다.


    npm r node-sass

    오시마



    이것으로 쾌적한 React x Scoped SASS 생활이 시작된다
  • 좋은 웹페이지 즐겨찾기