graphql.org/

 

GraphQL | A query language for your API

Evolve your APIwithout versions Add new fields and types to your GraphQL API without impacting existing queries. Aging fields can be deprecated and hidden from tools. By using a single evolving version, GraphQL APIs give apps continuous access to new featu

graphql.org

 

⭐ GraphQL을 시작하면서

처음에 GraphQL을 말로만 많이 들었지 잘 몰랐었는데 이번 사이드 프로젝트를 하면서 백엔드분이 GraphQL 도입하자고 하셔서 이 기회에 GraphQL에 대해서 공부해보았다. 원래는 FN 개발자 입장으로써 GraphQL에 API 연동해주는 Apollo만 사용해보려 했으나 API를 직접 만들어보는 것도 좋은 경험이니 도전해봤다. GraphQL을 단번에 이해시켜주는데 도움이 컸던 노마드코더 니꼴라스님의 영상을 보며 GraphQL API를 직접 따라 구현해보니 정말 매력적이라고 느꼈다. 👀

 

nomadcoders.co/graphql-for-beginners/lobby

이 영상보면서 영화 REST API를 GraphQL API로 감싸는 작업(Wrappering)까지 수월하게 할 수 있었다.

 


⭐ GraphQL이란 ?

GraphQL은 페이스북에서 만든 API를 위한 쿼리 언어이다. 그리고 GraphQL이 REST보다 API 만드는 것에 훨씬 더 간편하다. 그만큼 백엔드 개발을 쉽게 만들어 준다. GraphQL API를 만들기 위해서는 node와 자바스크립트 백엔드에 익숙해야 한다.

이게 바로 Query 언어입니다

 

 

⭐️ GraphQL  API의 장점

 

GraphQL은 번거로운 오버패치와 언더패치의 단점들을 해결해준다. 즉, GraphQL은 Over-fetching 없이 코드를 짤 수 있고 개발자가 어떤 정보를 원하는지에 대해 통제할 수도 있다. 그리고 Under-fetching을 겪을 필요도 없다. 한 쿼리에 내가 정확하게 원하는 정보만 받을 수 있다. 백엔드 개발자는 API 만들기에 쉽고 프론트엔드 개발자는 구체적이고 정확하게 받아오고 싶은 데이터들만 뽑아 써먹을 수 있음으로 백엔드와 프론트 개발자 모두에게 편리한 라이브러리 인 듯. 

  • Over-fetching : 내가 요청한 영역의 정보보다 더 많은 정보를 서버에서 받는다. 즉, 쓸모없는 정보들도 받게된다. 이는 비효율적이고 개발자들이 뭘 받았는지 모르게 된다.
  • Under-fetching : 어떤 하나를 완성하기위해 다른 요청들을 해야할 때 발생한다. 예를들어 인스타를 만들면 페이지의 피드를 받고 알림도 받고 사용자 프로필도 받게된다. 앱을 처음 시작하려면 이 세가지 요청을 해야 함 ! 즉 3가지 요청이 3번 오고가야 앱이 시작된다는 것이다. 이처럼 REST에서 하나를 완성하려고 많은 소스를 요청하는 것이 Under-fetching이다.

 

⭐️ GraphQL 구조

  • Query (쿼리 = 데이터)
  • Mutation (변형)
  • Subscription (설명)

Query 는 객체 구조로 표현한다. Query는 읽기를 요청하는 구문이고 Mutation은 수정을 요청하는 구문이다. Mutation을 통해 쿼리들을 추가 할 수도 삭제 할 수도 있다. GraphQL 서버에서는 정적인 타입 시스템을 사용해 데이터를 표현하는 Schema가 존재하고, 각 타입의 필드에 대한 함수로 구성된다. 그리고 하나의 EndPoint를 사용함으로써 RESTful API에서 생기는 많은 Enpoint의 복잡성을 줄여줄 수 있다. 이참에 RESTful API와 GraphQL API를 비교해보자

 

  1. RESTful API

    • Resource마다 Endpoint를 갖게 된다.
    • 클라이언트마다 필요한 데이터를 각 Resource 종류별로 요청해야 한다. (요청 횟수가 늘어날 수 있음)
    • Endpoint가 늘어날 수록 관리하기 힘들어진다.
    • 요청과 응답 구조가 미리 정의되어 있다. 때문에 HTTP에 대한 Caching이 가능한 것이 장점이다.
  2. GraphQL

  • HTTP 요청의 횟수를 줄일 수 있다. (원하는 정보를 하나의 쿼리에 모두 담아 요청 가능)
  • HTTP 응답의 사이즈를 줄일 수 있다. (필요한 정보만 부분적으로 요청가능)

 

이처럼 GraphQL은 REST API의 단점들을 보완해주는 역할을 하고 있는데 단점 또한 물론 갖고있다.

⭐ GraphQL의 단점

  • 재귀적인 쿼리가 불가능하다. 결과에 따라 응답의 깊이가 얼마든지 깊어질 수 있는 API를 만들 수 없음
  • 고정된 요청과 응답만 필요한 경우 쿼리로 인해 요청의 크기가 REST API의 경우보다 더 커진다고 한다.

단점에 대한 정보 출처(velog.io/@djaxornwkd12/REST-API-vs-GraphQL-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0)


⭐ GraphQL  API 생성하는데 필요한 것

백엔드가 기존에 API를 만들기 위해서는 장고나 express를 사용하며 데이터를 받아오기까지 각종 프레임워크를 사용하고 작업시간이 오래걸린다고 하는데 그에 비해 GraphQL은 정말 간단명료하게 만들 수 있다. 바로 이 4개의 파일들만 있으면 가능하다.

  • index
  • schema
  • resolvers
  • db

 

- index



가장먼저 해줘야 될 것이 index 작업이다. graphql 세팅해주는 작업이고, 먼저 graphql-yoga 패키지를 설치해줘야 한다. 이걸 설치하는 이유는 그래프큐엘 요가에서 제공해주는 플레이그라운드로 데이터들을 불러오기 때문이다. 그리고 서버명과 resolver를 불러오고 서버가 실행되면 커맨드창에 표시되게 해주자

 

- 스키마 [schema.graphql]

schema.graphql

이제 스키마를 짜줘야한다. graphql은 정적인 타입 시스템을 사용해 데이터를 표현한다. 스키마 Movie에 들어갈 데이터들의 타입들을 정의해주는데, 여기서 쓰인 ! 느낌표는 서비스가 항상 값을 제공하겠다는 약속이다. 그리고 스키마를 짜주면서 동시에 resolvers도 같이 해줘야 한다.

 

- [resolvers.js]

resolvers란?

Query를 해결해준다는 뜻이다. Query는 우리가 가진 데이터베이스에 문제같은 것이다. 그래서 우린 이 Query를 어떤 방식으로 해결(resolve)해야 한다. GraphQL에선 views나 urls같은건 없다. 오직 Query랑 resolvers만 있을 뿐이다. 그리고 resolvers를 내 원하는대로 프로그래밍 할 수 있다. DB로 갈 수도 있고 메모리로도 갈 수 있고 다른 API로도 갈 수 있다. 내 맘대로 가능하다 🙂

 

왼쪽 resolvers, 오른쪽 schema 데이터

 

- [db.js] 데이터베이스 관리하는 파일

 json 파일로 만들어진 영화 REST API를 graphQL로 랩핑 (wrapping) 해주는 과정이다. 이렇게 하고 http://localhost:4000/ 에 접속하면 GraphQL 플레이그라운드가 나온다.  

 

플레이그라운드

이처럼 플레이그라운드에서 쿼리로 movies 객체 안에 들어있는 데이터값들을 볼 수 있으며 mutations로 변형시킬 수도 있는 것을 DOCS에서 확인해볼 수 있다. Mutations으로 데이터를 추가할수도, 삭제도 가능하다.

 

안녕하세요~ 이번에는 바벨(Babel)에 대해 알아보려고 합니다. 😃

 

바벨은 크로스 브라우징의 혼란을 해결해 줄 수 있답니다. 모든 브라우저에서 동작하도록 호환성을 지켜주지요. 타입스크립트, JSX처럼 다른 언어로 분류되는 것도 포함입니다. 먼저 바벨을 사용하려면 패키지들을 설치해주어야 합니다.

 

설치명령어

$npm install @babel/core @babel/cli

@babel/cli는 바벨 터미널을 사용하기 위해 설치해요. 바벨 실행 할 때는 터미널창에

npx babel app.js

를 입력해서 실행하면 됩니다.

 

바벨은 세 단계로 빌드를 진행합니다 !

1. 파싱 : 단어들을 분해하는 과정이라고 보면 됩니다.

const alert = (msg) => window.alert(msg);

이 코드를 봤을 때 const 라는 토큰과 alert라는 토큰과 이렇게 일일이 분해해서 봅니다.

2. 변환 : ES6를 ES5로 변환해주고 마지막으로

3. 출력 해줍니다.

 

1. block-scoping

ES6 문법을 ES5 문법으로 변환해주는 것이 block-scoping 플러그인 입니다. const와 let 처럼 블록 스코핑을 따르는 예약어를 함수 스코핑을 사용하는 var로 변경해줍니다.

$npm install -D @babel/plugin-transform-block-scoping

터미널창에  block-scoping를 설치하고

npx babel app.js --plugins @babel/plugin-transform-block-scoping

위 명령어를 입력하면 var alert = msg => window.alert(msg); 이렇게 출력되는 것을 확인해 볼 수 있습니다.

 

2. transform-arrow-functions

이 플러그인은var alert = msg => window.alert(msg); 여기서 사용된 var는 브라우저 익스플로어에서 인식을 해주는데 => 이 화살표 함수는 브라우저가 인식하지 못합니다. 이거를 변환시켜주는 플러그인이 바로 transform-arrow-functions 플러그인 입니다.

$npm install -D @babel/plugin-transform-arrow-functions

터미널창에 transform-arrow-functions를 설치하고

npx babel app.js --plugins @babel/plugin-transform-block-scoping --plugins @babel/plugin-transform-arrow-functions

위 명령어를 입력해보세요. 그리고 출력된 결과물을 보면

var alert = function (msg) {
  return window.alert(msg);
};

화살표 함수가 사라지고 ES6 문법으로 변환된 것을 확인 됩니다 !

 

 

3. strict-mode

안전하게 작업하려면 엄격모드를 사용해야 합니다. 'use strict' 구문을 추가해야 하므로 strict-mode 플러그인을 설치합시다. 

$npm install @babel/plugin-transform-strict-mode

플러그인이 설치되었다면

npx babel app.js --plugins @babel/plugin-transform-block-scoping --plugins @babel/plugin-transform-arrow-functions

위 명령어를 입력해보세요!

"use strict";

var alert = function (msg) {
  return window.alert(msg);
};

그럼 상단에  use strict가 찍히면서 엄격모드로 변환된 것이 나타납니다. 그리고 플러그인을 사용할 때 명령어들을 모두 사용하려다 보니 커맨드라인 명령어가 점점 길어지기 때문에 설정 파일로 분리하는 것이 좋습니다.

바벨 환경설정 파일인 [babel.config.js] 에서 설정해주시면 되는데요.

module.exports = {
  plugins: [
    "@babel/plugin-transform-block-scoping",
    "@babel/plugin-transform-arrow-functions",
    "@babel/plugin-transform-strict-mode",
  ],
};

이렇게 바벨 기본 설정파일에 그간 설치했던 플러그인들을 담아두면 커맨드 창에 npx babel app.js만 입력하여도 알아서 전부 동작됩니다.

또 다른 방법으로는 프리셋을 사용하시면 됩니다.

 

프리셋

목적에 맞게 여러가지 플러그인을 세트로 모아놓은 것을 프리셋이라고 합니다. [my-babel-preset.js] 라는 파일을 만들어 그동안 설치했던 플러그인들을 담아놓아요.

module.exports = function myBabelPreset() {
  return {
    Plugins: [
      "@babel/plugin-transform-block-scoping",
      "@babel/plugin-transform-arrow-functions",
      "@babel/plugin-transform-strict-mode",
    ],
  };
};

 

그리고 기존에 [babel.config.js] 파일에는

module.exports = {
  presets: ["./my-babel-preset.js"],
};

프리셋 파일을 불러와줍니다. 그럼 프리셋에 있는 모든 플러그인들이 실행 됩니다.

지금까지 했던 과정들은 바벨에 대한 이해를 돕기 위한 시행이었고 사실 이렇게 바벨을 사용하는 것은 실무에서는 잘 쓰이지 않습니다.

다음편에는 바벨을 실무로는 어떻게 사용하는지 알아보겠습니다 !! 😃

안녕하세요 오늘은 웹팩에서 자주 사용하는 플러그인에 대해서 알아보려고 합니다 ! 총 5가지가 있구요. 기능부터 알아봅시다 :)

 

  • 🐥 BannerPlugin : 번들링 된 결과물 상단에다가 빌드 정보를 추가하는 역할이다. 잘 배포 되었는지 확인해보는 용도
  • 🐥 DefinePlugin : API 서버 주소를 넣음 좋다.
  • 🐥 HtmlTemplatePlugin : 이 플러그인은 동적으로 생성되는 자바스크립트와 css 그리고 빌드 타임에 결정되는 값들을 템플릿 파일에 넣어서 html파일을 동적으로 만들어낸다.
  • 🐥 CleanWebpackPlugin : src 경로에 있던 파일들을 빌드하면 dist 폴더에 남는다. 근데 src 폴더에 있는 파일을 삭제해도 dist 폴더에 남아있다. 그래서 얘는 src폴더와 dist폴더에 있는 값들이 비례하게 빌드할 때마다 dist에 남아있는 파일이나 폴더들을 제거해주는 역할이다.
  • 🐥 MiniCssExtractPlugin : 번들된 자바스크립트 파일에서 css만 따로 뽑아내서 css파일만 만들어 내는 플러그인이다.

 


플러그인도 [webpack.config.js] 웹팩 환경설정 파일에 설정 할 수 있고, 로더처럼 사용하시면 됩니다. 

const path = require("path");
const webpack = require("webpack");
const childProcess = require("child_process");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

하지만 로더와 다른 점이 플러그인은 상단에 설치한 플러그인들을 import 해줘야 합니다.

 

1. BannerPlugin

결과물에 빌드 정보나 커밋 버전 같은 걸 추가할 수 있어요. 배포 했을 때 배포가 잘 됐는지 이걸 통해 확인해요.

plugins: [
    new webpack.BannerPlugin({
      banner: `
      Build Date: ${new Date().toLocaleString()}
      Commit Version: ${childProcess.execSync("git rev-parse --short HEAD")}
      Author: 이현주(LeeHyunJu)
      `,
    }),
    ]

입력하고 빌드 했을 시 dist/main.js에 가면 banner 함수에 입력된 내용들이 나타납니다.

  •  Build Date: ${new Date().toLocaleString()}  👉🏻 오늘 날짜를 뽑아내줍니다.
  •  Commit Version: ${childProcess.execSync("git rev-parse --short HEAD")} 👉🏻 커밋 네임
  •  Author: 이현주(LeeHyunJu) 👉🏻 작성자

 

빌드 된 내용

 

2. DefinePlugin

어플리케이션은 개발환경과 운영환경으로 나눠서 운영됩니다. 환경에 따라 API 서버 주소가 다를 수 있어요. 같은 소스 코드를 두 환경에 배포하기 위해서 이런 환경 의존적인 정보를 소스가 아닌 곳에 관리하는 것이 좋습니다. 이러한 환경을 제공하기 위해 DefinePlugin를 제공합니다.

 new webpack.DefinePlugin({
      TWO: JSON.stringify("1+1"),
      "api.domain": JSON.stringify("http://dev.api.domain.com"),
    }),

 

api.domain을 문자열 넣고 싶을 때는 이렇게 출력합니다.

 

3. HtmlTemplatePlugin

html 파일을 후처리 하는데 사용합니다. 빌드 타임의 값을 넣거나 코드를 압축할 수 있습니다. 

npm install html-webpack-plugin
npm install html-webpack-plugin@4

이 플러그인은 웹팩의 기본 패키지가 아니라 따로 설치해 줘야합니다. 패키지 설치 후 버전이 안맞는다면 @4 이런식으로 버전을 맞춰 설치해주시면 됩니다. 얘를 사용하면 좀 더 유동적으로 html을 만들어 낼 수 있어요. 개발버전과 운영버전 두 가지 버전으로 관리가 가능하답니다.

    new HtmlWebpackPlugin({
      template: "./src/index.html",
      templateParameters: {
        env: process.env.NODE_ENV === "development" ? "(개발용)" : "",
      },
      //불필요한 용량들을 제거해주는거
      minify:
        process.env.NODE_ENV === "procution"
          ? {
              collapseWhitespace: true, //빈칸 제거
              removeComments: true, //주석 제거
            }
          : false,
    }),

 

html에는 title에 <title>Document <%=env %></title> 이렇게 ejs 문법을 사용하면 개발용인지 배포용인지 빌드를 통해 출력됩니다. 그리고  HtmlTemplatePlugin에는 minify 라는 속성도 제공해줘서 불필요한 용량들을 제거해 최소화 시켜줍니다. collapseWhitespace는 빈칸 제거를 의미하고 removeComments는 주석제거를 의미합니다.

 

4. CleanWebpackPlugin

빌드 결과물은 아웃풋 경로에 모이는데 과거 파일이 남아 있을 수 있습니다. 이전 빌드 내용이 덮어지면 상관 없지만 그렇지 않으면 아웃풋 폴더에 여전히 남아 있을 수 있습니다. 그래서 이 플러그인은 제거되고 사라진 과거 파일들을 빌드하면서 없애주는 역할을 합니다.

npm install html-webpack-plugin

 

이 플러그인은 다른 플러그인과 다르게 상단에 { } 중괄호를 넣어 가져와야 합니다.

const { CleanWebpackPlugin } = require("clean-webpack-plugin");

 

사용 법은 정말 간단해요 !

new CleanWebpackPlugin(),

이게 끝입니다 😄

 

4. MiniCssExtractPlugin

여러 CSS를 하나의 CSS로 만들어주는 플러그인 입니다. 그리고 이 플러그인은 다른 플러그인과 다르게 modules에서 loader도 설정해 주어야 합니다.

new MiniCssExtractPlugin({ filename: "[name].css" }),

플러그인 사용법

 

module: {
    rules: [
      {
        test: /\.css$/,
        👉🏻 use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
      
 (...)

모듈에 플러그인 사용하기

 

 

그 외에도 

webpack.js.org/plugins/

 

Plugins | webpack

webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.

webpack.js.org

웹팩 공식문서에 다양한 플러그인들이 제공되오니 사용하고 싶은 기능들을 잘 살펴보시고 사용하시길 바랍니다 ㅎㅎ

 

 

 

자주 사용하는 플러그인 종류에 대해서 마치겠습니다 ! 👻

 

https://webpack.js.org/

 

webpack

webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.

webpack.js.org

👩🏻 이번에는 웹팩으로 번들링 작업 해보려고 해요. 번들링 작업 할 때는 웹팩 패키지와 웹팩 터미널 도를 설치해줘야 합니다. 주의해야 될 점이 있는데, 웹팩은 현재 버전 5 까지 나왔거든요. 하지만 저는 버전 4를 설치하는 것을 추천드립니다. 왜냐하면 좀 더 뒤로 나갈수록 플러그인도 알아볼텐데 버전 5 는 플러그인 설치시 아직까지 지원 안되는 것들이 많더라구요. ㅠㅠ

 

설치명령어

$npm install -D webpack-cli
$npm install --save-dev webpack@(버전수)

여기서 -D 는 개발용 dependencies를 뜻합니다. 웹팩 번들링 할 때 필요한 기본 패키지들을 설치 하셨다면 웹팩 환경설정 해주는 파일들을 만들어 볼거에요. [webpack.config.js] 라는 파일명 또는 [webpackfile.js] 파일명 중 더 맘에 드는 이름을 골라 웹팩 환경설정 파일을 만들어주세요. 😀저는 [webpack.config.js]로 만들었습니다. 

 

그리고 웹팩을 실행할 때는 필수적인 옵션이 있어요.

👉 [webpack.config.js]


const path =  require('path');

module.exports = {
  mode: 'development',
  entry: {
    main : './src/app.js',
    main2 : './src/app2.js',
  },
  
  output: {
    path: path.resolve('./dist'),
    filename : '[name].js'
  }
} 

 

1. mode : development, production, none이 있는데 개발환경이냐 운영환경이냐에 따라서 development, production을 설정합니다.

  • development : 개발용 정보를 추가할 때
  • production : 운영에 배포하기 위한 것

2. entry : 모듈의 시작점 입니다.

3. output : 엔트리를 통해 모든걸 하나로 합치고 경로를 저장하는 것을 아웃풋으로 설정해줍니다.

 

이 모든 설정을 끝내셨으면, 터미널 창에 아래 명령어를 실행해보세요!

$node_modules/.bin/webpack --mode development --entry ./src/app.js --output dist/main.js

그럼 여러개의 모듈을 하나의 파일로 만들어지게 됩니다. 아웃풋 폴더 명을 dist라고 설정했으니 dist라는 폴더가 생겼을거에요 😃그리고 packages.json 파일에서도 저희가 설정해 줘야 할 것이 있어요.

👉 [package.json]


{
  "name": "fn_dev_env",
  "version": "1.0.0",
  "description": "<br>",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    👉🏻 "build": "webpack"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "react": "^17.0.1"
  },
  "devDependencies": {
    "webpack": "^4.46.0",
    "webpack-cli": "^4.5.0"
  }
}

여기서 build를 webpack으로 설정하면

$npm run build

터미널창에 이 명령어를 입력했을 시, 현재 프로젝트에 있는 노드 모듈을 뒤져서 웹팩 명령어를 찾아줘요. 그럼 웹팩은 기본  [webpack.config.js] 파일을 읽어서 번들링 해줄 것 입니다. 

 


 

👩🏻 이제는 로더와 자주 사용하는 로더를 알아볼 것 입니다.

로더

웹팩은 모든 파일을 모듈로 바라봅니다. 자바스크립트로 만든 모듈 뿐만 아니라, css, 이미지, 폰트도 전부 모듈로 보기 때문에 import 구문을 사용하면 자바스크립트 코드 안으로 가져올 수 있어요. 이것이 가능한 이유는 웹팩의 로더 덕분이랍니다. 로더는 타입스크립트 같은 다른 언어를 자바스크립트 문법으로 변환해 주거나 이미지를 data URI 형식의 문자열로 변환해주기도 해요. 뿐만 아니라 css 파일을 자바스크립트에서 직접 로딩 할 수 있도록 도와준답니다.

  module: {
    rules: [
      {
        test: /\.js$/,
        use: [path.resolve("./my-webpack-loader.js")],
      },
    ],
  },

로더를 사용할 때는 이렇게 모듈 객체 안에다가 rules 객체를 만들어 rules 안에 배열로 객체화해서 사용할 로더들을 설정해 주면됩니다. 처리할 파일은 test에 위 코드처럼 정규표현식으로 명시해주세요. 

  • test : 로더를 적용할 파일 유형을 뜻합니다.
  • use : 해당 파일에 적용할 로더 이름입니다.

 

자주 사용하는 로더

로더를 사용할 때는 로더 패키지를 설치해주셔야 됩니다.

 

1. CSS-loader

$npm install css-loader style-loader

css 로더를 사용하고 싶다면 이 파일이 JS로 변환될 때 style-loader도 같이 설치해 주셔야해요. 

 

module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },

먼저 설치한 패키지가 뒤로 가는 순서로 입력해주셔야 합니다 !

 

2. image-loader

$npm install file-loader

다음으로 자주 쓰일 로더는 이미지로더입니다. 위 명령어를 통해 패키지를 설치해주세요. 

{
        test: /\.jpg$/,
        loader: "file-loader",
        options: {
          pubilcPath: "./dist/",
          name: "[name].[ext]?[hash]", //이름.확장자?해시값
        },
      },

얘도 css 로더와 마찬가지로 rules 안에 선언하시면 됩니다. options에 있는 publicPath는 이 이미지들이 담길 공간을 의미해요. publicPath는 파일로더가 처리하는 파일을 모듈로 사용했을 때 경로 앞에 추가되는 문자열입니다. 쉽게말해 아웃풋을 dist로 설정했으니 pubilcPath도 같은 경로로 동일하게 dist로 설정해줘야 해요!

이번에는 여러 이미지 사용시 data URI로 처리하는 방법입니다. 사용하는 이미지 갯수가 많다면 네트워크 리소스를 사용하는 부담이 있고 사이트 성능에 영향을 줄 수도 있어요. 한 페이지에서 작은 이미지를 여러개 사용한다면 data URI scheme를 이용하는 방법이 더 낫습니다.

$npm install url-loader

패키지를 설치하셨으면 이제 웹팩 환경설정을 해봅시다.

 

{
        test: /\.(jpg|png|gif|svg)$/,
        loader: "url-loader",
        options: {
          publicPath: "./dist/",
          name: "[name].[ext]?[hash]",
          limit: 20000,
        },
      },

test에 jpg 뿐만 아니라 여러 확장자를 번들링 해주고 싶다면, (확장자명|확장자명|확장자명) 이렇게 띄어쓰기 없이 설정해주시면 됩니다. 그리고 로더는 url-loader로 설정합니다. 여기서 새롭게 추가된 limit은 파일 용량으로 아웃풋을 해주냐, 아니면 data uri로 뿌려주냐 설정해주는데요. 여기서 20000은 20kb를 의미해요. url-loader가 이미지 파일들을 처리할 때 용량이 20kb 미만은 url-loader로 해서 bath 64로 변환처리 해줍니다. 그 이상이면 file-loader로 실행되어 파일이 dist 폴더에 복제된 걸 볼 수 있어요.

이외에도 ts 로더, sass 로더, babel 로더가 있습니다.

npm install --save-dev typescript ts-loader

webpack.js.org/guides/typescript/#loader

 

TypeScript | webpack

webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.

webpack.js.org

 

 

로더 마무리

 module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.(jpg|png|gif|svg)$/,
        loader: "url-loader",
        options: {
          publicPath: "./dist/",
          name: "[name].[ext]?[hash]",
          limit: 20000, //파일 용량도 설정이 가능하다 2kb
        },
      },
    ],
  },

 

이처럼 로더를 사용하고 싶다면 모듈안에 rules 객체를 만들어 이 안에서 배열로 로더 객체를 만들어 설정하시면 됩니다! 웹팩 다루는 기본기만 알아두면 다른 로더들도 사용할 때 쉽게 설정할 수 있을 겁니다. 지금은 가장 대표적인 css로더와 이미지 로더를 알아봤는데, 다음에는 ts로더와 sass로더 스프라이트 이미지 만드는 방법도 알아보고 정리해 보겠습니다.

👩🏻안녕하세요 웹팩 다뤄보는 시간을 가져보겠습니다 ! 제가 웹팩을 공부하는 이유는 좀 더 효율적인 작업 환경을 만들기 위해서인데요! 웹팩의 좋은점 세 가지를 간략하게 소개해드릴게요.

  1. 파일 단위의 자바스크립트 모듈 관리의 필요성 (모듈번들링)
  2. 웹 개발 작업 자동화 도구 (HTML,CSS,JS,이미지 압축 + CSS 전처리기 변환)
  3. 웹 애플리케이션의 빠른 로딩 속도와 높은 성능

을 위해서 사용해보도록 할거에요.

먼저 파일 단위 자바스크립트 모듈 관리 필요성을 알아보자면, 웹 애플리케이션을 제작하려면 HTML,CSS,JS,이미지,폰트 등 많은 파일들이 필요하죠 ! 이게 모두 다 모듈이라고 지칭합니다. 모듈 번들링이란 수많은 파일들을 하나의 파일로 병합 및 압축 해주는 동작을 의미해요. 그리고 웹팩 다뤄보면서 자주 들어보셨을 '빌드하다 = 번들링 = 변환하다' 이 세 가지 뜻이 다 같은 뜻입니다. 🙌

웹 개발 작업 자동화 도구란 HTML,CSS,JS,이미지 압축과 CSS 전처리기 변환 해주는 일들을 자동화 해주는 도구들이 필요했는데 그래서 그런트와 걸프라는 것이 등장했어요. 참고로 걸프는 첫 회사 때 사용해봤는데 진짜 편하더라구요... 하지만 저는 다수가 선택한 웹팩을 배워보겠습니다 !!

웹 어플리케이션의 빠른 로딩 속도와 높은 성능을 위한 이유는 일반적으로 특정 웹사이트를 접근할 때 3초 이내로 웹 사이트가 표시되지 않으면 대부분의 사용자들은 해당 사이트를 벗어나거나 집중력을 잃게 돼요. 그래서 웹사이트 로딩 속도를 높이기 위해 많은 노력들을 해야 되는데, 그 중 대표적인 노력이 브라우저에서 서버로 요청하는 파일 숫자를 줄이는 것입니다. 작업한 파일은 많은데 파일 수를 줄여준다라... 정말 신박한 거 같아요.

 

🐥 웹팩을 알아가기 전, 간단하게 개념 정리 하고 갑시다.

 


 

IIFE (Immediately Invoked Function Expression)

즉시 실행 함수표현은 정의되자마자 즉시 실행되는 자바스크립트 함수를 말해요.
(function () {
    statements
})();

위처럼 첫 번째 괄호로 둘러싸인 익명함수입니다. 이는 전역 스코프에 불필요한 변수를 추가해 오염시키는 것을 방지할 수 있을 뿐만 아니라, IIFE 내부 안으로 다른 변수들이 접근하는 것을 막을 수 있는 방법이에요. 두 번째 괄호는 즉시 실행 함수를 생성하는 괄호입니다. 이를 통해 자바스크립트 엔진은 함수를 즉시 해석해서 실행시켜줘요.

 

다양한 모듈 스펙

자바스크립트 모듈을 구현하는 대표적인 명세가 AMD, CommonJS 입니다. CommonJS는 자바스크립트로 사용하는 모든 환경에서 모듈화 하는 것이 목표입니다. export 키워드로 모듈을 만들고 require() 함수로 불러들이는 방식이지요. 대표적인 서버 사이드 플랫폼인 Node.js에서 이 방식을 사용합니다.

 

내보낼 때 (export)

export function sum(a,b) { return a + b;}

 

가져올 때 (import)

import * as math from './math.js'

 

AMD : 비동기로 로딩되는 환경에서 모듈을 사용하는 것이 목표입니다. 주로 브라우저 환경을 말해요.

UMD : AMD 기반으로 CommonJS 방식까지 지원하는 통합 형태에요. 각 커뮤니티에서 각자의 스펙을 제안하다가 ES2015 에서 표준모듈 시스템을 내놓았어요. 지금은 바벨과 웹팩을 이용해 모듈 시스템을 사용하는 것이 일반적이지요.

 

<script type="module" src="src/math.js"></script>

HTML 브라우저에서는 위 코드처럼 모듈을 사용할 수 있어요. 이렇게 되면 script 구문 안에 type="module" 을 입력해줘야 한답니다. 하지만 이럴 때는 서버를 돌려야 되는데 npm으로 lite-server 패키지를 설치해 줘야해요.

$npx lite-server

설치명령어를 입력하여 설치해주고 현재 폴더를 서버로 만들어 줍니다 ! 

 


 

엔트리 / 아웃풋

웹팩은 여러개 파일을 하나의 파일로 만들어 주는 번들러라고 했잖아요. 하나의 시작점으로부터 의존적인 모듈을 전부 찾아내서 하나의 결과물로 만들어줘요. app.js 부터 시작해 math.js 파일을 찾은 뒤 하나의 파일로 만드는 방식이예요. 위에 사진을 보면 좀 더 이해하기 쉬울 겁니다.

 

그래서 엔트리와 아웃풋이 뭐냐하면, 엔트리는 왼쪽에 있는 파일들을 뜻하고(=시작점) 아웃풋은 오른쪽에 하나의 파일로 만들어진 것을 뜻해요. 웹팩 들어가기에 앞서 개념들을 알아봤는데요. 다음편은 실전으로 설명해 드리겠습니다.

 

{
  "compilerOptions": {
    "esModuleInterop": true, 👉🏻import * as React from 'react'; =>  * as 생략가능하게 해주는 기능
    "sourceMap": true, 👉에러 났을 때 원래 에러난 위치로 찾아가기 좋다
    "lib": ["ES2020", "DOM"], 👉프론트 개발 할 때 꼭 켜두기
    "jsx": "react", 👉다르게 인식되기 전에 리액트꺼라고 지정
    "module": "esnext", 👉 최신 모듈 쓰겠다는 뜻 import, export
    "moduleResolution": "Node", 👉 import, export도 node가 해석할 수 있게
    "target": "es5", 👉 es2020으로 작성하여도 es5로 변환하겠다
    "strict": true, 👉 타입 채킹을 엄격하게 하겠다.
    "resolveJsonModule": true, 👉 json은 import json이라는 것을 허락하겠다.
    
    ---임폴트할 때 편하게 임폴트 해주기---
    "baseUrl": ".", 
    "paths": { 
      "@hooks/*": ["hooks/*"],
      "@components/*": ["components/*"],
      "@layouts/*": ["layouts/*"],
      "@pages/*": ["pages/*"],
      "@utils/*": ["utils/*"],
      "@typings/*": ["typings/*"]
    }
  }
}

Path는 폴더 경로가 ../../../ 이런식으로 접근해야 될 때 절대경로처럼 임폴트 설정 해주는 것이다. 이정도 타입스크립트 설정이 기본이라고 보면 된다. 

 


 

👉🏻 타입스크립트 ----- 바벨 ----> 자바스크립트

바벨이 중간다리 역할을 해준다.

바벨 : 모든 걸 자바스크립트로 만들어주는 아이 

"strict": true, 는 타입스크립트 할 때 무조건 true로 켜둬야한다. 아니면 any같은걸 쓰기 때문에 타입스크립트가 아닌 any스크립트가 된다 ㅋㅋㅋ

클로저는 함수가 생성되는 시점에 접근 가능했던 변수들을 생성 이후에도 계속해서 접근할 수 있게 해주는 기능이다. 접근할 수 있는 변수는 그 함수를 감싸고 있는 상위 함수들의 매개변수와 내부 변수들이다.

 

클로저를 사용한 간단한 코드

function makeAddFunc(x) {
  return function add(y) {
    return x+y;
  }
}
const add5 = makeAddFunc(5);
console.log(add5(1)); // 6

const add7 = makeAddFunc(7);
console.log(add7(1)); //8
console.log(add5(1)); //6

add 함수는 상위 함수인 makeAddFunc의 매개변수 x에 접근할 수 있다. add5 함수가 생성된 이후에도 상위 함수를 호출할 때 사용했던 인수에 접근할 수 있다. 중간에 makeAddFunc(7)이 호출되지만 add5에 영향을 주지는 않는다. 즉, 생성된 add 함수별로 클로저 환경이 생성된다.

 

 

ES6+ 에서는 화살표 함수를 이용해 함수를 정의하는 방법이 추가되었다. 화살표 함수를 사용하면 함수를 정말 간결하게 작성할 수 있다.

const add = (a,b) => a+b;

이런식으로 사용되는데 화살표 함수를 중괄호로 감싸지 않으면 오른쪽의 계산 결과가 반환된다. 

const add = a => a+b;

 

매개변수가 하나라면 매개변수를 감싸는 소괄호도 생략 있다.

const addAndReturnObject = (a,b) => ({return: a+b})

객체를 반환해야 한다면 소괄호로 감싸야 한다.

 

화살표 함수의 코드가 여러줄인 경우

const add = (a,b) => {
  if(a <0 || b<= 0) {
    throw new Error('must be position number');
  }
  return a+b;
}

화살표 함수에 여러 줄의 코드가 필요하다면 이와 같이 전체를 중괄호로 묶고 반환값에는 return이라는 키워드를 사용함 된다.

그리고 ES6는 this 바인딩 때문에 버그가 발생하는 경우 화살표 함수를 사용하면 그 버그를 제어할 수 있다.

function Something() {
  this.value = 1;
  this.increase = () => this.value++;
}

const obj = new Something();
obj.increase();
console.log(obj.value);

const increase = obj.increase;
increase();
console.log(obj.value);

화살표 함수의 increase의 this는 가장 가까운 일반 함수인 Something의 this를 참조한다. Something 함수는 생성자이고 밑에 obj 객체가 생성될 때 호출된다. new 키워드를 이용해서 생성자 함수를 호출하면 this는 생성되는 객체를 참조한다는 점에 유의하자. increase함수의 this는 생성된 객체를 가르킨다. 그러니 호출되는 시점의 객체와는 무관하게 increase함수의 this는 항상 생성된 객체를 참조하고 obj.value는 계속 증가한다.

 

비구조화 심화 학습

비구조화는 객체와 배열이 중첩되어 있을 때도 사용할 수 있다.
const obj = {name: 'mike', mother: {name: 'sara'}};
const {
  name,
  mother: {name : motherName},
} = obj;

console.log(name);
console.log(motherName);
console.log(mother);

이 처럼 세개의 단어가 등장하지만, 비구조화 결과로 motherName이라는 변수만 생성된다. 비구조화에서 기본값의 정의는 변수로 한정되지 않는다.

 

객체비구조화

const index = 1;
const { [`key${index}`]: valueOfTheIndex} = {key1: 123};
console.log(valueOfTheIndex); //123

객체 비구조화에서 계산된 속성명을 사용할 때에는 반드시 별칭을 입력해야한다. 별칭은 단순히 변수명만 입력할 수 있는 것은 아니다.

({foo: obj.prop, bar: arr[0]} = {foo : 123, bar: true});
console.log(obj); // {prop:123}
console.log(arr); // [true]

첫 줄처럼 객체 비구조화를 이용해서 obj 객체의 prop이라는 속성과 배열의 첫 번째 원소에 값을 할당하고 있다.

 

강화된 ES6+ 함수의 기능 

매개변수에 기본값을 줄 수 있게 되었고, 나머지 매개변수를 통해 가변 길이 매개변수를 좀 더 명시적으로 표현할 수 있게 되었다. 명명된 매개변수를 통해 함수를 호출하는 코드의 가독성이 월등히 좋아졌다. 그리고 화살표 함수가 추가되면서 함수 코드가 간결해졌고, this 바인딩에 대한 고민을 덜 수 있게 되었다.

 

매개변수의 추가된 기능

1. 매개변수 기본값 : ES6부터 함수 매개변수에 기본값을 줄 수 있다. 
function printLog(a=1) {
  console.log({a});

}
printLog(); // {a:1}

인수 없이 함수를 호출하므로 a에는 undefined가 입력됨 ! 기본값이 정의된 매개변수에 undefined를 입력하면 정의된 기본값이 1이 사용된다. 객체 비구조화처럼 기본값으로 함수 호출을 넣을 수 있고, 기본값이 필요한 경우에만 함수가 호출된다.

 

2. 나머지 매개변수 : 나머지 매개변수는 입력된 인수 중에서 정의된 매개변수 개수만큼 제외한 나머지를 배열로 만들어 준다. 나머지 매개변수는 매개변수 개수가 가변적일 때 유용하다.
function printLog(a, ...rest) {
  console.log({a, rest});
}

printLog(1,2,3); // {a:1, rest:[2,3]}

하나의 인자를 제외한 나머지 rest 매개변수에 할당한다. ES5에서는 arguments 키워드가 비슷한 역할을 한다. 

 

3. 명명된 매개변수 : 자바스크립트에서 명명된 매개변수는 객체 비구조화를 이용해서 구현할 수 있다.
명명된 매개변수를 사용하면 함수 호출 시 매개변수의 이름과 값을 동시에 적을 수 있으므로 가독성이 높다.
그리고 명명된 매개변수를 사용하면 함수를 호출할 때마다 객체가 생성되기 때문에 비효율적일 것이라고 생각할 수 있다. 하지만 자바스크립트 엔진이 최적화를 통해 새로운 객체를 생성하지 않으므로 안심하고 사용해도 된다.
const numbers = [10,20,30,40];
const result1 = getValues(numbers, 5, 25);
const result2 = getValues({ numbers, greaterThan:5, lessThan:25});// << 이렇게 매개변수의 이름과 값을 동시에 적을 수 있어서 가독성이 좋아졌다.

 

😗이번에는 ES6+에서 객체와 배열에 추가된 문법을 알아보자.

단축 속성명과 계산된 속성명을 이용하면 객체와 배열을 생성하고 수정하는 코드를 쉽게 작성할 수 있다. 또한, 전개 연산자와 비구조화 할당 덕분에 객체와 배열의 속성값을 밖으로 꺼내는 방법이 한결 쉬워졌다.

단축 속성명과 계산된 속성명

1. 단축 속성명

- 단축 속성명은 객체 리터럴 코드를 간편하게 작성할 목적으로 만들어진 문법. 단축 속성명을 사용하면 간편하게 새로운 객체를 만들 수 있다. 

const name = "mike";
const obj = {
 age: 21,
 name,
 getName() { retrun this.name; },
 }

새로 만들려는 객체의 속성값 일부가 이미 변수로 존재하면 간단하게 변수 이름만 적어주면 된다. 이때 속성명은 변수 이름과 같아진다. 속성값이 함수면 function 키워드 없이 함수명만 적어도 된다. 이때 속성명은 함수명과 같아진다. 

 

function makePerson1(age,name) {
 return { age: age, name: name };
}
function makePerson2(age,name) {
 retrun { age, name };
}

첫번째 함수 단축 속성명을 사용하지 않은 경우이고, 두번째 함수가 단축 속성명을 사용한 경우다. 보다시피 단축 속성명을 사용한 경우가 코드를 작성하기도 편하고 간결하고 가독성도 편하다. 

 

2. 계산된 속성명

계산된 속성명은 객체의 속성명을 동적으로 결정하기 위해 나온 문법. 

function makeObject1(key,value){
 const obj = {};
 obj[key] = value;
 return obj;
}
function makeObject2(key,value){
 return{ [key]:value};
}

계산된 속성명을 사용하면 같은 함수를 두 번째 함수처럼 간결하게 작성할 수 있다.

 

전개 연산자와 비구조화 할당

3. 전개 연산자

전개 연산자는 배열이나 객체의 모든 속성을 풀어놓을 때 사용하는 문법이다. 매개변수가 많은 함수를 호출할 때 유용하다 !

Math.max(1,3,7,9); // 이 방식으로는 동적으로 매개변수를 전달할 수 없다.

// 전개 연산자
const numbers = [1,3,7,9];
Math.max(...numbers);

전개 연산자를 사용하면 동적으로 함수의 매개변수를 전달할 수 있다.

 

//배열, 객체 생성
const arr1 = [1,2,3];
const obj1 = {age:23, name:'mike'}

//전개연산자 사용하여 새로운 객체와 배열 생성
const arr2 = [...arr1];
const obj2 = {...obj1};

//전개 연산자 사용해서 새로운 객체가 생성되었기 때문에
//속성을 추가하거나 변경해도 원래의 객체에 영향을 주지 않음!
arr2.push(4);
obj2.age = 80;

배열의 경우 전개 연산자를 사용하면 그 순서가 유지된다.

 

[1, ...[2,3],4]; // [1,2,3,4]
new Date(...[2020, 6, 24]); //2020년 6월 24일

배열 리터럴에서 중간에 전개 연산자를 사용하면 전개 연산자 전후의 순서가 유지된다. 두 번째 함수는 함수의 인수를 정의된 매개변수의 순서대로 입력해야 하므로 순서가 유지되는 전개 연산자의 성질을 이용하기 좋다. 예를 들어 Date 생성자의 매개변수 순서대로 날짜 데이터를 관리하면 Date 객체를 쉽게 생성할 수 있다. 전개 연산자를 사용하면 서로 다른 두 배열이나 객체를 쉽게 합칠 수 있다.

 

const obj1 = {age:21, name:'mike'};
const obj2 = {hobby: 'soccer'};
const obj3 = {...obj1, ...obj2 };
console.log(obj3); // {age:21,name:'mike',hobby:'soccer'}

이 코드에서 obj1과 obj2가 같은 이름의 속성을 가지고 있다면 ? es5까지는 중복된 속성명을 사용하면 에러가 발생했지만, es6부터는 중복된 속성명이 허용된다. 중복된 속성명 사용 시 최종 결과는 마지막 속성명의 값이 되다. 중복된 속성명과 전개 연산자를 이용하면 객체의 특정 속성값을 변경할 때 이전 객체에 영향을 주지 않고 새로운 객체를 만들어 낼 수 있다. 이는 변수를 수정 불가능하도록 관리할 때 유용하게 사용될 수 있다.

 

4. 배열 비구조화

배열 비구조화는 배열의 여러 속성값을 변수로 쉽게 할당할 수 있는 문법이다. 다음은 배열 비구조화를 사용한 코드다.

const arr =[1,2];
const [a,b] = arr;
console.log(a); //1
console.log(b); //2

-------------------

let a, b;
[a,b] = [1,2];

배열의 속성값이 왼쪽의 변수에 순서대로 들어간다. 이렇게 새로운 변수로 할당할 수도 있고 이미 존재하는 변수에 할당할 수도 있다. 

 

const arr =[1,2,3];
const [a,, c] = arr;
console.log(a); //1
console.log(c);//3

그리고 배열 비구조화를 사용하면 제3의 변수가 필요하지 않을 뿐만 아니라 단 한줄의 짧은 코드로 구현할 수 있다. 배열에서 일부 속성값을 무시하고 진행하고 싶다면 건너뛰는 개수만큼 쉼표를 입력하면 된다. 첫 번째 속성값은 변수 a에 할당된다. 두 번째 속성값은 건너뛰고 세 번째 속성값이 변수 c에 할당된다. 쉼표 개수만큼을 제외한 나머지를 새로운 배열로 만들 수도 있다.

 

const arr = [1,2,3];
const [first, ...rest1] = arr;
console.log(rest1);
const [a,b,c, ...rest2] = arr;
console.log(rest2); // []

배열 비구조화 시 마지막에 ...와 함께 변수명을 입력하면 나머지 모든 속성값이 새로운 배열로 만들어진다. 나머지 속성값이 존재하지 않으면 빈 배열이 만들어진다.

 

5. 객체 비구조화

const obj1 = {age:21,name:'mike'}
const {age, name} = obj; // 객체 비구조화에서는 중괄호를 사용
console.log(age); // 21
console.log(name); // mike

객체 비구조화는 객체의 여러 속성값을 변수로 쉽게 할당 할 수 있는 문법이다. 객체 비구조화에서는 중괄호를 사용한다. 배열 비구조화에서는 배열의 순서가 중요 했지만 객체 비구조화에서 순서는 무의미하다. 따라서 name과 age의 순서를 바꿔도 결과는 같다. 단, 배열 비구조화에서 왼쪽 변수의 이름은 임의로 결정할 수 있지만, 객체 비구조화에서는 기존 속성명 그대로 사용해야 한다.

 

+ Recent posts