Articles

[번역] Part 2. CSS 모듈 사용하기

Bittersweet- 2022. 6. 24. 16:54
728x90

 

 

 

** 번역에 의역과 오역이 충분히 있을 수 있으므로, 가능하신 분들은 그냥 아래의 링크로 가셔서 원문을 읽어보시길 추천합니다~

Part 2. Getting Started with CSS Module

 

Getting Started with CSS Modules | CSS-Tricks

There isn't one single approach with CSS Modules to making the JavaScript templates, the CSS files, or the build steps to make them work. In this post, which

css-tricks.com

 

 

 


 

 

 

 

CSS 모듈에는 Javascript 템플릿, CSS 파일 또는 이를 작동시키는 빌드 단계를 만드는 단일 접근 방식이 없다. 이 글은 CSS 모듈 관련된 시리즈 중 하나로 접근 방식을 살펴볼 것이다. 이 글의 목표는 CSS 모듈 프로젝트를 시작하고 실행시키는 것이다.

 

🔗 아티클 시리즈

part 1: What are CSS Modules and why do need them? 

part 2: Getting Started with CSS Modules (지금 위치)

part 3: React + CSS Modules = 😍

 

 

 

이 프로젝트에서 CSS가 클라이언트 사이드 측 Javascript에 의존해서는 안된다는 요구사항이 있으므로 배포되기 전 빌드 단계에서 모든 것을 작동하는 HTML, CSS로  처리해야 한다.

우리는 빌드 시스템이자 모듈 번들러인 Webpack을 사용할 것이다. 다음 글에서는 정적 HTML을 브라우저에 렌더링 하는 실제 프로젝트에 적합하도록 코드를 만들어 볼 것이다.

 

시작해보자!

 

 

 

Webpack 설치

 

npm과 node로 설치한 후 빈 디렉토리를 설정하고 다음을 실행한다.

npm init -y

실행되면 package.json 파일을 만들고 기본값이 설정된다. 이 파일로 하여금 프로젝트에 필요한 종속성을 확인할 수 있다 - 다른 사람들이 npm으로 이 프로젝트 설치할 때 다운로드 및 설치되는 항목에 대한 지침으로 사용된다.

 

Webpack은 빌드 프로세스를 처리합니다. CSS, Javascript, HTML 파일을 탐색하여 실행합니다. 하지만 Webpack이란 무엇인가?

Maxim Fabre는 webpack이 빌드 시스템인지 모듈 번들러인지 궁금해 했다.

 

" 둘다 맞다 - 그리고 이것은 두 가지를 모두 수행한다는 것을 의미하는 것이 아니라 두가지를 결합한다는 것을 의미한다. Webpack은 assets을 빌드한 다음 모듈을 별도로 번들로 묶지 않고 assets을 모듈 그 자체로 간주한다. 임포트하고 수정하고 조작할 수 있으며 궁극적으로 최종 번들에 포함될 수 있다. "

 

지금 이 말이 무슨 말인지 모르겠다 해도 걱정하지 마라. Sass, Gulp, npm도 익숙하지 않고 무서워다는 것을 기억하는가? 우리는 이 또한 알아낼 것이다.

 

하나의 JavaScript 파일이 해당 코드 덩어리를 가져올 수 있도록 종속성을 정의하여  Webpack이 모듈을 올바르게 "번들링" 하는지 확인하자.

첫째, 우리는 전역적으로 Webpack을 설치한다. 그러면 터미널에서 Webpack 명령에 접근할 수 있다.

npm install webpack -g

전역적으로 설치가 끝난 후 Webpack을 로컬 프로젝트에 설치하도록 한다.

npm i -D webpack

이제 index.js 파일을 /src 디렉토리에 생성하자. 일반적으로 모든 정적자산(예: 이미지, 폰트, css 파일 및 마크업)이 있는 디렉토리를 만들고 싶다. 내가 작성하는 모든 코드는 일반적으로 /src 디렉토리에 있는 반면, 기계에 의해 작성되거나 특정 프로세스에서 해석되는 모든 코드는 /build 디렉토리에 있어야 한다. /src 디렉토리의 내용을 처리하고 /build 디렉토리를 완전히 다시 빌드하기 때문에 /build 디렉토리를 삭제해도 아무런 문제가 발생하지 않는다. 이 경우, Webpack이 /src 디렉토리의 모든것을 살펴보고, 특정 프로세스에서 수행한 다음 해당 코드를 /build 로 이동시키기를 원한다.

 

/src 디렉토리에 alert.js이라는 빈 파일을 추가할 수도 있다(이 부분은 잠시 후 다시 설명하겠다). 그리고 아래와 같이 webpack.config.js 파일을 생성하여 /src 바깥의 디렉토리(root)에 위치하도록 한다.

package.json
webpack.config.js
/node_modules
/src
  index.js
  alert.js

webpack.config.js 파일(Webpack 설정)에 다음과 같이 작성해준다.

module.exports = {
  entry: './src',
  output: {
    path: 'build',
    filename: 'bundle.js',
  },
};

여기서부터 webpack 명령을 실행할 때마다 Webpack은 /src에 있는 모든 assets을 살펴보고 종속성 트리를 만든다.

 

src/index.js 파일로 돌아가 다음과 같이 작성한다.

require("./alert.js");

그리고 alert.js파일에는 다음와 같이 작성한다.

alert("LODE NOISES");

이제 root에 index.html 파일을 만들고 <body> 닫힘 태그 바로 직전에 사용할 번들을 스크립트 태그로 추가한다.

<!DOCTYPE html>
<head>
  <meta charset="UTF-8">
  <title>Document name</title>
</head>
<body>
  <h1>CSS Modules demo</h1>
  
  <script src="build/bundle.js"></script>
</body>
</html>

bundle.js 파일은 Webpack으로 인해 생성된다. 생성을 위해 Webpack 명령어를 실행하기만 하면 된다. 좀 더 쉽고 편하게 만들기 위해서 빌드 스크립트로 package.json 파일을 업데이트 한다. 다음의 코드가 파일에서 찾아야 할 내용이다.

"scripts": {
  "test": "echo 'Error: no test specified' && exit 1"
},

이것이 npm이 설치 시 기본으로 설정된 값이지만 다음의 코드로 대체하고 Webpack을 실행하고 브라우저 창을 여는 자체 명령 스크립트를 만들 수 있다.

"scripts": {
  "start": "webpack && open index.html"
},

따라서 npm start를 실행할 때마다 자동으로 webpack build 명령을 실행하고 브라우저에서 index 파일을 연다. 그리고 이제 어떻게 되는지 보자.

뭔가 작동하고 있다. 이것은 index.js파일이 alert.js 파일에서 코드를 가져오고 Webpack이 모든것을 적절하게 번들링하고 있음을 보여준다. 이제 alert.js파일을 삭제하면 npm start 를 다시 실행할 때 오류가 발생한다.

가져올 모듈을 찾지 못했을 때 보여지는 Webpack 에러 화면이다. 그러나 이제 이 모든 작업을 확인했으므로 index.js파일에서 require문을 스크랩하고 Webpack에 대해 배우는 다음 단계로 이동할 수 있다.

 

 

 


 

 

 

첫번째 로더 추가

 

Webpack의 로더는 굉장히 중요하다. Maxime, Fabre 는 이에 대해 이렇게 말했다.

 

" 로더는 기본적으로 '이런 종류의 파일을 만났을 때 이 파일로 이것을 하세요'라고 말하는 작은 플러그인이다. "

 

Maxime의 튜토리얼을 보면 그는 Babel 로더를 추가했는데, 이는 Babel을 통해 ES2015와 JavaScript언어의 최신 개선 사항을 사용할 수 있기 때문에 정말 좋은 출발점이 된다. 따라서 이전에 다른 모듈을 require 가져왔던 것 대신 import를 사용할 수 있습니다. Babel을 사용하면 클래스, 화살표 함수 및 기타 멋진 기능을 사용할 수도 있다.

 

"Bebel과 같은 도구를 사용하면 오늘날 새로운 ES2015 코드를 작성하고 브라우저가 그 코드를 지원할 수 있도록 이전 버전의 JavaScript로 변환하기 위해 트랜스파일(전처리와 유사)이라는 작업을 수행할 수 있다. 이것은 Sass가 작동하는 방식과 유사하다. 처음에 Sass 구문으로 코드를 작성한 다음 전처리기가 표준 CSS로 컴파일한다. "

다음은 Webpack Babel 로더와 Babel을 실행하는데 필요한 모듈을 설치한다.

npm i -D babel-loader babel-core babel-preset-env

root 경로에 있는 .babelrc 파일에서 우리가 사용할 JavaScript 구문을 설정할 수 있다.

{
  "presets": ["babel-preset-env"]
}

이제 우리는 모든 .js 파일에서 Babel을 실행하고 싶지만 우리가 작성하는 파일, 나중에 설치하는 다른 모듈에는 고유한 구문이 있을 수 있으므로 해당 코드를 엉망으로 만들고 싶지 않다. 여기에서 Webpack 로더가 작동한다. webpack.config.js 파일을 열고 해당 코드를 다음과 같이 바꿀 수 있다.

module.exports = {
  entry: './src',
  output: {
    path: 'build',
    filename: 'bundle.js',
  },
  module: {
    loaders: [
      {
        test: /\.js/,
        loader: 'babel-loader',
        include: __dirname + '/src',
      }
    ],
  }
};

로더 배열 내부의 테스트 키/값 쌍은 Webpack에 어떤 유형의 파일에 대해 작업을 수행하고 싶은지 알려주는 반면 include는 프로젝트에서 해당 작업이 수행되기를 원하는 위치를 정확하게 알려준다.

 

Babel이 Webpack과 연동하여 동작하는지 테스트해보자. 새파일('src/robot.js')에 다음과 같이 작성해보자.

const greetings = (text, person) => {
  return `${text}, ${person}. I read you but I'm sorry, I'm afraid I can't do that.`;
}

export default greetings;

이 JavaScript 파일은 export , const 및 let, 화살표 함수, 템플릿 리터럴과 같은 ES2015 특정 기능을 사용하고 있다.

이제 다음과 같이 해당 모듈을 src/index.js 파일로 import 할 수 있다.

import greetings from './robot.js';
document.write(greetings('Affirmative', 'Dave'));

마지막으로 우리가 해야할 일은 npm start를 다시 실행하는 것 뿐이다. 그러면 브라우저에 "Affirmative, Dave. I read you but I'm sorry, I'm afraid I can't do that." 나올 것이다. 이것은 단순히 Babel이 제대로 작동하고 있음을 확인 시켜준다. 아직 CSS 모듈은 아니지만 확실히 한 단계 더 가까워졌다. 하지만 계속 진행하기 전 src/robot.js와 src/index.js에서 모든 코드를 삭제한다.

 

 

 


 

 

 

로딩 스타일

 

이제 템플릿이 거의 작동하므로 css-loader와 style-loader라는 두개의 로더를 추가로 설치해야 한다.

npm i -D css-loader style-loader

css-loader는 CSS 파일을 가져와서 모든 종속성을 읽는 반면 style-loader는 해당 스타일을 마크업에 직접 포함한다. src/app.css에 CSS를 작성하여 테스트를 해보자.

.element {
  background-color: blue;
  color: white;
  font-size: 20px;
  paddig: 20px;
}

그 후 스타일 시트를 src/index.js 파일로 import 한다.

import styles from './app.css'

let element = `
  <div class="element">
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequatur laudantium recusandae itaque libero velit minus ex reiciendis veniam. Eligendi modi sint delectus beatae nemo provident ratione maiores, voluptatibus a tempore!</p>
  </div>`;
  
  document.write(element);

잠깐, 스타일 시트를 JavaScript 파일의 종속성으로 만들었나? 맞다. 그렇게 만들었다. 하지만 제대로 작동하기 전, 이것이 왜 유용한지 알기 전 먼저 webpsck.config.js를 다시 구성해야 한다.

module.exports = {
  entry: './src',
  output: {
    path: 'build',
    filename: 'bundle.js',
  },
  module: {
    loaders: [
      {
        test: /\.js/,
        loader: 'babel',
        include: __dirname + '/src',
      },
      {
        test: /\.css/,
        loader: ['style', 'css'],
        include: __dirname + '/src'
      }
    ],
  }
};

npm start로 실행시키면 이렇게 나온다.

결과적으로 개발자 도구에서 "요소를 검사"하면 스타일 로더가 해당 파일을 문서의 <head>에 있는 <style> 태그에 배치했음을 알 수 있다.

방금 일어난 일을 살펴보자. 다른 CSS 파일을 요청하는 JavaScript 파일을 만들었고 그 코드는 웹 페이지에 포함되어있다. 그래서 더 현실적인 예에서 우리는 button.js 파일을 만들고 button.css를 이것의 종속성으로 만들 수 있다. 그런 다음 해당 JavaScript를 템플릿으로 구성하고 일부 HTML을 내보내는 다른 파일로 가져올 수 있다. 이것은 우리 코드를 모듈화하고 읽기 쉽게 만들어줬다.

 

개인적으로 코드를 깔끔하게 유지하기 위해 모든 코드를 인라인으로 추가하는 대신 별도의 CSS 파일을 사용하는 것을 선호한다. 그렇게 하려면 extract text이라는 Webpack 플러그인을 사용해야 한다.

 

"모든 파일의 requre('styles.css')는 별도의 CSS 출력 파일로 이동한다. 따라서 스타일이 더이상 자바스크립트에 인라인되지 않고 css 번들 파일(style.css)로 분리된다. 만약 사용하는 스타일 시트의 규모가 크다면, 스타일 시트 번들이 자바스크립트 번들과 병렬로 로드되기 때문에 속도가 더 빠르다."

 

npm으로 먼저 설치한다.

npm i -D extract-text-webpack-plugin

이제 webpak.config.js 파일을 요청하고 여기에 CSS 로드를 배치하여 다시 한번 업데이트 시킨다.

var ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  entry: './src',
  output: {
    path: 'build',
    filename: 'bundle.js',
  },
  module: {
    loaders" [
      {
        text: /\.js/,
        loader: 'babel",
        include: __dirname + '/src',
      },
      {
        rest: /\.css/,
        loader: ExtractTextPlugin.extract('css')
      }
    ],
  },
  plugins: [
    new ExtractTextPlugin('styles.css')
  ]
};

ExtractTextPlugin 은 'styles.css'파일을 생성하게 된다.

 

style-loader를 완벽하게 제거했다는 사실을 눈치챘을 것이다. 더이상 이러한 스타일(인라인)이 마크업에 추가되는 것을 원하지 않기 때문이다. 이제 /build 디렉토리를 열면 내부에 모든 코드가 적혀 있는 styles.css 파일이 생성된 것을 확인할 수 있다. 그리고 index.html 파일에는 <head> 코드 내부에 우리의 스타일 시트를 연결해줄 수 있다.

<link rel="stylesheet" href="build/styles.css">

npm start를 한번 더 실행시키면 - 스타일이 페이지에 마술처럼 나타난다.

 

이제 페이지에서 작업하는 CSS과 HTML이 있으므로 로컬 범위의 모든 이점을 얻기 위해 클래스 이름을 어떻게 조작할까? 우리가 해야하는 것은 다음과 같이 webpack.config.js를 업데이트 시키는 것 뿐이다.

{
  test: /\.css/,
  loader: ExtractTextPlugin.extract('css?modules&importLoaders=1&localIndentName=[name]__[local]___[hash:base64:5]'),
}

이렇게 되면 클래스 이름 끝에 텍스트 생성이 추가된다. CSS 모듈은 CSS 로더를 통해 Webpack에 추가할 수 있는 클래스를 변경하는 해시이다.

 

다음으로, 우리는 index.js 파일을 styles.element클래스로 업데이트 해야한다.

import styles from './app.css'

let element = `
  <div class="${styles.element}">
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequatur laudantium recusandae itaque libero velit minus ex reiciendis veniam. Eligendi modi sint delectus beatae nemo provident ratione maiores, voluptatibus a tempore!</p>
  </div>
`

document.write(element);

npm start를 또 한번 실행하면 코드가 webpack에 의해 처리되었으므로 웹 페이지에 삽입되는 클래스가 이제 다음과 같이 보이기 때문에 로컬 범위는 더이상 문제가 되지 않는다.

<div class="app__element___1MmQg">
  ...
</div>

 

 

 

 


 

 

 

 

아직 풀리지 않은 질문들이 많기 때문에 아직 끝나지 않았다. 개발 단계에서 우리가 어떻게 이런 코드를 작성할 것인가? 마크업을 하는데 있어서 지저분한 document.write 규칙을 어떻게 피해갈 수 있을까?

우리는 모듈과 파일을 어떻게 구성할 것인가? CSS 모듈을 설정하고 실행하는 것은 작업의 절반에 불과하다. 다음으로 우리는 코드의 베이스를 다른 시스템에서 어떻게 이식할 것인지에 대해 생각해야 합니다.

 

다음 튜토리얼에서는 React가 아주 작은 모듈을 생성하는 것을 도우며, 또한 여러 템플릿에서 정적 마크업을 생성하는 방법과 Sass 및 PostCSS와 같은 다른 기능을 프로젝트에 추가하는 방법을 알아보도록 하겠다.

 

 

 

 


🔗 아티클 시리즈

part 1: What are CSS Modules and why do need them? 

part 2: Getting Started with CSS Modules (지금 위치)

part 3: React + CSS Modules = 😍