튜토리얼을 이어서 진행하겠다. 이번 튜토리얼에서는 CSS까지 함께 작업해보겠다. 이전에 제작한 튜토리얼 앱은 CLI로만 작동했는데, 이제는 브라우저에서도 작동하도록 제작하려고 합니다. src/index.html
파일을 아래와 같이 간단하게 제작한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shout Lorem Ipsum</title>
</head>
<body>
<div id="app"></div>
<script src="index.js"></script>
</body>
</html>
src/index.js
를 실행해보면 에러가 난다.
이는 매우 자연스러운 현상인데, require 문법은 Node.js 문법으로 브라우저에서 지원하지 않기 때문이다. create-react-app
으로 이미 이런저런 프론트엔드 개발을 해오던 개발자는 당황할 수 있다. 더 최신 문법인 import
, export
도 지금까지 잘 써왔는데, require
가 되지 않는게 모순적으로 느껴질 수 있다.
하지만 사실 create-react-app
도 webpack
을 사용한다.
Node.js
에서만 작동할 법한 코드를 모든 브라우저에서 잘 작동할 수 있도록 번들링을 해주었기 때문에 잘 작동했던 것이다. index.html 파일을 dist 디렉터리로 옮겨서 번들 파일과 연결시키겠다.
<!-- dist/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shout Lorem Ipsum Once</title>
</head>
<body>
<div id="app"></div>
<script src="app.bundle.js"></script>
</body>
</html>
src/index.js 도 단순히 console.log만 찍기 보다는, DOM 관련 코드를 조금 더 추가해서 브라우저에서 볼 수 있게 수정한다.
const _ = require("./underbar.js");
const shout = (...sentences) => console.log(...sentences);
const shoutToHTML = (...sentences) => {
const app = document.querySelector("#app");
app.append(
...sentences.map((sentence) => {
const shoutHere = document.createElement("div");
shoutHere.className = "shout";
shoutHere.textContent = sentence;
return shoutHere;
})
);
return;
};
const loremIpsum =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis egestas feugiat elit, ac tincidunt neque vestibulum at. Mauris a eros sit amet urna efficitur tempus.";
const shoutOnce = _.once(shout);
const shoutToHTMLOnce = _.once(shoutToHTML);
shoutOnce(loremIpsum);
shoutOnce(loremIpsum);
shoutOnce(loremIpsum);
shoutOnce(loremIpsum);
shoutToHTMLOnce(loremIpsum);
shoutToHTMLOnce(loremIpsum);
shoutToHTMLOnce(loremIpsum);
shoutToHTMLOnce(loremIpsum);
이렇게 src/index.js 에서 새롭게 작성한 코드는 dist의 app.bundle.js
에 적용되지 않았기 때문에, 다시 npm run build
명령어로 번들링을 진행하고 나서 dist/index.html 을 열면 아래와 비슷한 화면을 확인할 수 있다.
웹페이지가 다소 밋밋해서, H1 요소를 추가하고 CSS를 적용해보겠다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Shout Lorem Ipsum Once</title>
</head>
<body>
<main>
<h1>Shout Lorem Ipsum Once</h1>
<div id="app"></div>
</main>
<script src="app.bundle.js"></script>
</body>
</html>
/* dist/style.css */
* {
box-sizing: border-box;
border: 0;
padding: 0;
margin: 0;
}
main {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
div.shout {
padding: 12px;
margin: 4px;
border-radius: 8px;
border: 0.5px solid gray;
}
webpack은 이렇게 JavaScript와 CSS를 함께 배포해야 할 때, 좀 더 쉽게 배포할 수 있게 로더(loader)를 제공한다. create-react-app
에서 자유롭게 CSS를 import할 수 있었던 이유도 webpack에서 CSS를 불러올 수 있는 툴을 사용했기 때문이다.
dist/style.css
파일의 위치를 src/style.css
로 옮기고, 파일을 index.js
에서 불러와보자. 잘 불러왔는지 node src/index.js
명령으로 확인한다. Node.js
는 그 자체만으로는 CSS를 읽을 수 없어 문법 에러가 발생한다.
// src/index.js
const _ = require('./underbar.js');
require('./style.css');
const shout = (...sentences) => console.log(...sentences);
const shoutToHTML = (...sentences) => {
const app = document.querySelector('#app');
app.append(...sentences.map(sentence => {
const shoutHere = document.createElement('div');
shoutHere.className = 'shout';
shoutHere.textContent = sentence;
return shoutHere;
}))
return;
};
const loremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis egestas feugiat elit, ac tincidunt neque vestibulum at. Mauris a eros sit amet urna efficitur tempus."
const shoutOnce = _.once(shout);
const shoutToHTMLOnce = _.once(shoutToHTML);
shoutOnce(loremIpsum);
shoutOnce(loremIpsum);
shoutOnce(loremIpsum);
shoutOnce(loremIpsum);
shoutToHTMLOnce(loremIpsum);
shoutToHTMLOnce(loremIpsum);
shoutToHTMLOnce(loremIpsum);
shoutToHTMLOnce(loremIpsum);
webpack도 마찬가지입니다. 친절하게도 아래와 같은 에러 메시지를 출력한다.
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See
https://webpack.js.org/concepts/#loaders
이 파일을 다루기 위해서는 적절한 로더가 필요하다. 현재 이파일을 처기하기 위한 로더가 설정되어 있지 않다.
에러에서 제공한 공식문서 링크에서 아래 로더 목록을 찾았다. 이번 튜토리얼에서 필요한 로더는 style-loader, css-loader이다. css-loader 는 CSS를 JS파일 내에서 불러올 수 있고, style-loader 는 불러온 CSS를 style 요소 내에 담아준다.
npm으로 두 로더를 설치하고, webpack.config.js 를 조정한다.
npm i -D css-loader style-loader
// webpack.config.js
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "app.bundle.js",
},
module: {
rules: [
{
// 파일명이 .css로 끝나는 모든 파일에 적용
test: /\.css$/,
// 배열 마지막 요소부터 오른쪽에서 왼쪽 순으로 적용
// 먼저 css-loader가 적용되고, styled-loader가 적용되어야 한다.
// 순서 주의!
use: ["style-loader", "css-loader"],
// loader가 node_modules 안의 있는 내용도 처리하기 때문에
// node_modules는 제외해야 합니다
exclude: /node_modules/,
},
],
},
};
이후 npm run build 명령을 통해 번들링이 잘 되었는지 확인한다. 번들링이 성공한 모습을 확인하실 수 있습니다. 경고가 있어도 에러가 없으면 성공이다.
app.bundle.js 파일을 열어서 유심히 살펴보면 두 로더가 한 일을 얼추 파악할 수 있다. style 요소를 자동 생성하고 지금까지 작성한 CSS를 넣는 방식이다. (아래 화면을 보고 싶으면, VS Code의 Format Document 기능(alt + shift + F)를 사용하면 된다.)
dist/index.html 파일을 열어보니 style 요소가 생성되고 작성한 CSS가 담겨있는 모습을 확인하실 수 있습니다. 이젠 <link rel="stylesheet" href="style.css">
코드는 필요없다.
로더에 대해서 정리하겠습니다.
loader는 JavaScript, JSON이 아닌 파일을 불러오는 역할을 한다.