페이지방식 웹개발
페이지방식 웹개발이란 웹개발에 필요한 3가지 요소인 문서의 구조를 이루는 html, 문서를 꾸미는 css, 문서를 동적으로 제어하는 javascript 이 세가지 요소를 결합하여 .html 이라는 페이지를 만들면 브라우저가 이 페이지를 렌더링해서 보여주는 전통적인 방식의 웹개발 방식이다.
하지만 페이지방식의 웹개발은 페이지마다 중복 코드가 발생, 의존성 관리, 글로벌 변수 관리 등의 문제점이 있어서 최신프런트엔드 웹개발 방식인 모듈환경 웹개발 방식으로 변화하고 있다.
페이지방식 웹개발은 난이도가 낮고 진입장벽이 낮지만 모듈방식의 최신프런트엔드 웹개발은 진입장벽이 높은 편이다. 모듈환경으로의 웹개발로 변화가 된 가장 중요한 두가지 개념은 npm과 webpack (grunt, gulp 등) 이다. 최신 프런트엔드 라이브러리인 react, vue, angular 등 대부분이 모듈번들러 기반이므로 반드시 알아야 할 내용들이다.
npm은 node package manager 로 모듈 관리자이다. webpack은 여러가지 모듈을 모아서 하나로 만들어주는 모듈 번들러로, webpack 이외에 vite, parcel 등 여러가지가 존재하지만 webpack을 이해하면 다른 모듈번들러도 쉽게 이해할수 있다.
먼저, 페이지 방식으로 된 웹페이지를 하나 만들고 모듈 방식으로 변경해가면서 웹팩에 대한 기본 개념을 익혀보자.
프로젝트를 시작할 적당할 폴더를 만든다. c:\eastflag\webpack-demo 와 같이 특정한 폴더를 만든 다음, 윈도우 탐색기에서 해당 폴더를 우클릭해서 webstorm을 열거나 만일 webstorm이라는 context-memu가 없다면 webstorm에서 해당 폴더를 open한다.
- src 폴더 아래에 js폴더를 만들고 library.js 화일을 만들고 아래와 같이 입력한다.
1 2 3 4 |
function join(word1, word2) { // _ is Lodash variable, currently included via a script, is required for this line to work return _.join([word1, word2], ' '); } |
- src 폴더 아래에 index.js 화일을 만들고 아래와 같이 입력한다.
1 2 3 4 5 6 7 8 9 |
function component() { let element = document.createElement('div'); // call join function that is includeed by library.js element.innerHTML = join('Hello', 'webpack'); return element; } document.body.appendChild(component()); |
- index.html을 추가하고 아래와 같이 입력한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>webpack demo</title> <script src="https://unpkg.com/lodash@4.16.6"></script> </head> <body> <script src="./js/library.js"></script> <script src="./index.js"></script> </body> </html> |
위의 코드는 index.html 문서에 lodash.sh라는 외부 자바스크립트 라이브러리와 내부에서 정의한 library.js 화일과 index.js 세개의 화일을 포함하여 하나의 웹페이지를 구성한다. 실행해보면 Hello webpack 이라는 문구가 보일것이다. 실행 방법은 index.html 파일을 우클릭후에 run을 클릭한다.
- 이 페이지방식 웹의 문제점을 나열하면 아래와 같다.
1. 글로벌 환경으로 실행되기 때문에 변수의 충돌이 일어날 가능성이 있다.
만일 library.js에서 변수 a를 선언하고 index.js에서 변수 a를 또 선언하면 충돌이 일어난다.
실제로 jquery에서 사용하는 $라는 변수는 다른 라이브러리에서도 많이 사용하기 때무에 충돌이 일어날 수 있다. 그래서 네임스페이스 패턴이나 IIFE 패턴을 사용하여 충돌을 피하는 방법을 사용하게 된다.
모듈방식에서는 컴파일시에 중복이 됨을 알려주기 때문에 이 문제를 해결할 수 있다.
2. 로딩순서에 의존성이 있다.
로딩 순서를 아래와 같이 순서를 바꾸어서 테스트해보자.
<script src=”src/index.js”></script>
<script src=”src/js/library”></script>
순서를 바꾸면 에러가 난다. index.js에서 join 함수를 호출하는데 이 함수는 아직 로딩이 되지 않았기 때문이다. 즉, index.js 화일은 library.js 화일에 의존하게 된다.
이제 이 부분이 모듈 환경으로 넘어가면 npm이 의존성이 있는 화일을 알아서 먼저 설치해주게 되므로 의존성 문제를 해결할 수 있게 된다.
3. 페이지가 바뀔때 마다 이전페이지에서 로딩했던 동일한 자바스크립트 화일들이 대부분 다시 로딩 되어져야 한다.
페이지 방식의 웹은 대부분 여러개의 페이지가 결합되어서 하나의 웹 사이트를 구축하므로 각 페이지마다 동일한 css 화일들과 js 화일들이 중복되어서 로딩되게 된다. 이렇게 덩치가 큰 웹페이지는 모바일 환경에 적합하지가 않다.
모듈방식에서는 SPA 방식으로 중복을 없애면서도 코드 스플릿팅 기술을 사용하여 모바일에서도 잘 로딩되도록 할 수 있다.
모듈 저장소 설치
위의 웹 페이지 방식을 모듈방식으로 변경한다.
- 먼저 npm init 으로 모듈저장소를 먼저 만든다.
1 |
npm init |
이 명령을 수행한 후에 어떤 변화가 있는가? package.json 파일이 만들어졌을 것이다. 이 화일은 모듈방식 개발에서 가장 중요한 환경 화일이다.
이제부터 설치하는 모든 모듈들은 node_modules에 저장되고 그 정보는 package.json 화일에 기록되게 된다.
- 외부 라이브러리인 lodash 화일을 설치한다.
1 2 |
# 의존성 모듈 설치 npm install --save lodash |
페이지방식에서는 CDN 환경으로 lodash 라이브러리를 가져온다. 모듈방식에서는 npmjs라는 모듈 저장소에서 이 화일을 가져온 후에 node_modules라는 로컬 폴더에 저장하게 된다.
lodash라는 모듈은 누군가가 벌써 모듈화해서 npm 저장소에 넣어 두었다. 그렇기 때문에 install 명령을 사용하면 그 저장소에서 모듈을 다운로드 해서 설치하게 된다.
이 저장소는 npmjs.com 이라는 곳이다. 여기에 가서 lodash라는 모듈을 확인해보자.
—-save 의 의미는 의존성 모듈을 package.json 화일에 기록하라는 것이다.
이 모듈을 설치한 다음 node_modules를 열어서 lodash 화일이 설치되어있는지 확인해보자.
- 모듈번들러인 webpack을 설치한다.
1 2 |
# 개발 의존성 모듈 설치 npm install webpack webpack-cli --save-dev |
설치후 node_modules 를 확인해보면 lodash 폴더처럼 webpack, webpack-cli 두개 폴더가 추가로 더 생길거 같지만 그렇지 않고 수많은 모듈들이 설치되어있을 것이다. 그 이유가 좀전에 말한 의존성과 관계가 있다. webpack에서 다른 모듈들을 가져와서 사용, 즉 의존하고 있기 때문이다. webpack이 의존하는 모듈들이 몇개일까? npmjs 사이트에 가서 dependancy 탭을 확인해보면 직접적으로 의존하고 있는 모듈들을 찾을수 있다. 그리고 해당 모듈들을 찾아서 들어가면 또 의존하는 모듈들을 있다는것을 알 수 있다.
예를들어, webpack이 현재 24개의 dependancy를 가지고 있는데 그중에 첫번째인 @types/eslint-scope 를 클릭해서 들어가보자.
그러면 @types/eslint, @types/estree 두 개의 dependancy를 가지고 있다. 그 중에 첫번째를 클릭해서 들어가면 @types/estree, @types/json-schema 두개의 dependancy를 가지고 있다. 그 중에 첫번째를 클릭해서 들어가면 그제서야 dependancy가 0개이다.
즉 A => B => C => D. A가 B 에 의존하고 B가 C에 의존하고 C가 D에 의존하면 A 모듈은 A, B, C, D 이 모듈이 모두 필요하다. 하위 dependancy가 0이 될 때까지 모두 가져와야 한다. 이것 npm라는 패키지 관리자가 해주는 역할이다.
—-save-dev 는 개발 의존성을 package.json에 기록하라는 뜻이다. 개발에 필요한 모듈은 아니지만 개발하는데 필요한 모듈이라는 뜻으로 실제 production할때는 필요없는 모듈이고 개발할때만 필요한 모듈이라는 뜻이다.
여기까지 진행하면 페이지 폴더 구조는 아래와 같을 것이다.
1 2 3 4 5 6 7 |
webpack-demo |- package.json + |- index.html + |- /src + |- index.js + |- /js + |- library.js |
모듈 방식으로 변경하기
페이지방식에서 _라는 변수는 lodash 라이브러리가 index.html이라는 웹페이지에 글로벌 변수로 인식되어 어디서나 사용할 수 있다.
모듈이라는 의미는 이제 글로벌 변수가 아니라 특정 화일에만 사용하겠다는 의미이다. 그러므로, 모듈로 사용하기 위해서는 좀 전에 node_modules로 로컬에 설치한 lodash 모듈을 가져와서 사용하겠다는 부분을 추가해야한다.
모듈을 정의하는 스펙은 여러가지가 있는데 nodejs에서 사용하는 방식인 CommonJS 스펙은 모듈을 정의하고 배포하는 부분이 export , 모듈을 가져오는 부분이 require 이다.
es 모듈에서는 모듈을 정의하는 부분이 export 로 가져오는 부분이 import로 바뀌어진다.
모듈방식에 대한 자세한 내용은 다섯번째 챕터에서 자세히 다룬다.
- 먼저, index.html에서 lodash를 가져오는 script 태그를 삭제 혹은 주석 처리한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>webpack demo</title> <!-- <script src="https://unpkg.com/lodash@4.16.6"></script>--> </head> <body> <script src="./library.js"></script> <script src="./index.js"></script> </body> </html> |
- 그리고, library.js 를 다음과 같이 수정한다.
1 2 3 4 5 6 |
import _ from 'lodash'; export function join(word1, word2) { // _ is Lodash variable, currently included via a script, is required for this line to work return _.join([word1, word2], ' '); } |
lodash 모듈을 가져오기 위해서 es 모듈 방식을 사용해서 import 해서 가져왔다. 전체를 가져오는 default import 방식이다.
그리고 join 펑션은 index.js에서 사용하므로 해당 펑션을 export 를 사용해서 정의하였다. join 펑션만 내보내는 named export 이다.
- index.js에서는 library.js 모듈의 join 펑션을 아래와 같이 가져온다.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import {join} from './js/library'; const component = () => { console.log('test'); let element = document.createElement('div'); // call join function element.innerHTML = join('Hello', 'webpack!!'); return element; } document.body.appendChild(component()); |
자 이제 브라우저에서 실행을 해보면 될 것 같지만 에러가 날 것이다. 왜냐하면 아직 import 혹은 require이라는 모듈 방식을 브라우저가 이해하지 못하고 페이지 방식으로 동작하기 때문이다.
그래서 브라우저가 이해할 수 있도록 페이지방식으로 컴파일을 수행해야 하는데 이것을 수행하는 역할이 웹팩의 역할이다.
터미널에서 webpack을 수행하기 위해서는 node_modules 아래의 webpack 폴더 아래 bin 폴더의 webpack.js를 node로 실행해야 한다.
node node_modules/webpack/bin/webpack.js
이렇게 긴 명령어를 입력하지 않고 간단하게 하는 방법은 스크립트에등록하는 방법이다. 아래와 같이 package.json에 build 스크립트에 webpack을 등록한다.
1 2 3 4 5 |
... "scripts": { "build": "webpack" }, ... |
그리고 터미널창에 아래와 같이 입력한다.
1 |
npm run build |
dist 폴더에 main.js 화일이 생성되었을 것이다.
이 화일을 실행하기 위해서는 index.html 화일을 dist 폴더에 복사한 후에 index.html의 ./src/index.js 를 ./main.js 로 바꾸어 준 다음 실행해 보자. 잘 실행되는것을 알수 있을것이다.
webpack 환경화일 만들기
webpack.config.js 화일을 추가하자.
폴더 구조는 아래와 같을 것이다.
1 2 3 4 5 6 7 8 9 10 11 |
webpack-demo |- /dist |- index.html |- main.js |- package.json + |- index.html + |- webpack.config.js + |- /src + |- index.js + |- /js + |- libraryx.js |
webpack.config.js는 다음과 같다.
1 2 3 4 5 6 7 8 9 |
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, 'dist') } }; |
이제 이 환경화일을 읽어서 다시 실행해보자.
build 스크립트를 아래와 같이 수정한다.
1 2 3 4 5 |
... "scripts": { "build": "webpack --config webpack.config.js" }, ... |
webpack에서 가장 중요한 두가지는 entry point라는 진입점과 번들링된 결과를 출력하는 output 폴더이다.
여기서는 src/index.js가 entry 포인트가 되며 여기서 부터 시작해서 모든 필요한 모듈들을 번들링 한 다음에
dist/main.js로 output 결과물을 생산하게 된다.
( webpack 버전 5에서는 entry point가 src/index.js output 폴더가 dist/main.js가 디폴트이다)
dist 폴더에 main.js가 정상적으로 생성되었는지 확인하고 또한 실행해서 결과를 확인해보자.
package.json 의 전체 내용은 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
{ "name": "webpack-demo", "version": "1.0.0", "description": "", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config webpack.config.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "lodash": "^4.17.21" } "devDependencies": { "webpack": "^5.78.0", "webpack-cli": "^5.0.1" }, } |