April 12, 2019

webpack-spritesmith로 CSS 스프라이트 자동화 적용기

문서(doc, ppt, xls 등)를 볼 수 있는 이미지 뷰어를 개발 할 때, IE와 모바일 브라우저에서 이미지 요청을 줄여 성능을 향상 시키기 위해 CSS Sprite 자동화를 적용하면서 알게된 것을 정리 합니다.


시작하기에 앞서 CSS Sprite 자동화를 적용한 프로젝트는 webpackscss를 사용하고 있으며, 이에 맞게 CSS 스프라이트 자동화를 적용 했습니다.

CSS 스프라이트(Sprite)란?

페이지의 이미지 요청 수를 감소시키기 위한 대표적인 방법입니다. 낱개의 이미지들을 합친 하나의 스프라이트 이미지와 낱개의 이미지의 위치와 크기 등의 스타일이 작성된 CSS 파일을 사용해서 적용합니다. 스프라이트 이미지로 합치기에 적합한 이미지는 웹 페이지에서 자주 사용되는 로고, 아이콘, 배경 이미지 등이 있습니다.

CSS 스프라이트 자동화 적용

적용에 앞서

처음에는 CSS 스프라이트를 CSS Sprites Generator와 같은 도구를 이용해서 적용하려고 했습니다. 스프라이트 이미지 파일과 낱개의 이미지에 대한 스타일이 작성된 CSS 파일을 생성해주기 때문입니다.

css sprite generator

하지만 추후에 이미지가 추가/제거 되는 경우 매번 도구를 사용해서 이미지를 관리해줘야하는 단점이 있었습니다. 이를 해결할 수 있는 방법을 찾게 되었고, webpack-spritesmith라는 webpack 플러그인을 알게 되었습니다.

webpack-spritesmith

webpack-spritesmith 플러그인은 스프라이트 이미지와 낱개의 이미지의 위치, 크기 등에 대한 스타일을 SASS/LESS/Stylus 파일로 생성해주는 webpack 플러그인입니다.


예를 들어 스프라이트 이미지로 합칠 낱개의 이미지들을 이용해서

icons

아래와 같이 하나의 스트라이트 이미지와 SCSS 파일을 생성됩니다.

스프라이트 이미지

sprite image

스프라이트 SCSS 파일

이 파일에는 이미지 파일명동일한 변수명의 인자를 전달 받으면 각각의 이미지에 대한 너비, 높이, 이미지 파일 경로, 좌표 등의 스타일을 적용해주는 믹스인변수가 작성되어 있습니다.

$mobile-actionbar-more-icon-on-name: 'mobile_actionbar_more_icon_on';
$mobile-actionbar-more-icon-on-x: 0px;
$mobile-actionbar-more-icon-on-y: 0px;
$mobile-actionbar-more-icon-on-offset-x: 0px;
$mobile-actionbar-more-icon-on-offset-y: 0px;
$mobile-actionbar-more-icon-on-width: 36px;
$mobile-actionbar-more-icon-on-height: 36px;
$mobile-actionbar-more-icon-on-total-width: 108px;
$mobile-actionbar-more-icon-on-total-height: 108px;
$mobile-actionbar-more-icon-on-image: '../images/sprite-mobile.png';
$mobile-actionbar-more-icon-on: (0px, 0px, 0px, 0px, 36px, 36px, 108px, 108px, '../images/sprite-mobile.png', 'mobile_actionbar_more_icon_on', );..

...

@mixin sprite($sprite) {
  @include sprite-image($sprite);
  @include sprite-position($sprite);
  @include sprite-width($sprite);
  @include sprite-height($sprite);
}

...

사용법은 스프라이트 이미지를 사용하는 SCSS 파일에서 스프라이트 SCSS 파일을 import 하고 사용하려는 기존 이미지 파일명과 동일한 변수명을 sprite 믹스인의 인자로 전달하면 됩니다.

@import '[PATH]/sprite';

[SELECTOR] {
  @cinlude sprite($mobile_actionbar_more_icon);
}

webpack에서 css 번들 파일을 보면 아래와 같이 적용된 것을 확인 할 수 있습니다.
[SELECTOR] {
  background-image: url([PATH]/[SPRITE_IMG_NAME].png);
  background-position: -36px 0px;
  width: 36px;
  height: 36px; 
}

프로젝트에 적용

1) 프로젝트 구조

프로젝트 구조는 src 디렉토리 내에 있는 ts, scss, images 디렉토리 내 파일들을 webpack으로 번들링해서 루트 디렉토리에 js, css, image 파일들을 출력 하도록 구성되어 있습니다.

2) webpack 설정 파일에 CSS 스프라이트 설정

아래와 같이 빌드되도록 webpack 설정 파일을 작성 했습니다.

webpack build order

webpack-spritesmith 모듈을 설치합니다.

$ npm i -D webpack-spritesmith

스프라이트 이미지는 src/images 디렉토리, 스프라이트 scss 파일은 spritesmith-generated 디렉토리에 생성되도록 webpack 설정 파일을 작성합니다.

webpack-spritesmith 모듈을 불러옵니다.

...
var SpritesmithPlugin = require('webpack-spritesmith');
var MobileSpritesmithPlugin = require('webpack-spritesmith');

이미지 파일을 로드하기 위해 file-loader 모듈을 추가합니다.

...

module.exports = {
  ...

  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|svg)$/,
        use: {
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: 'images',
            publicPath: '../images/',
          }
        }
      },
      ...
      },
    ]
  },

spritesmith 모듈이 스프라이트 파일을 생성하도록 plugin 프로퍼티에 SpritesmithPlugin 객체를 생성해서 추가합니다.

src 객체의 cwd 프로퍼티에는 image 소스 파일의 위치를 설정하고, glob 프로퍼티에는 확장자를 설정합니다.

target 객체의 images 프로퍼티에는 스프라이트 이미지가 생성될 위치와 파일명을 설정하고, css 프로퍼티에는 스프라이트 scss 파일이 생성될 위치와 파일명을 설정합니다.

apiOptions 객체의 cssImageRef 프로퍼티는 스프라이트 scss 파일의 api에서 참조하는 생성된 스프라이트 이미지의 경로를 설정합니다.

...

  plugins: [
    new SpritesmithPlugin({
      src: {
        cwd: path.resolve(__dirname, 'src', 'images', 'web'),
        glob: '*.png'
      },
      target: {
        image: path.resolve(__dirname, 'src/images/sprite-web.png'),
        css: path.resolve(__dirname, 'src/spritesmith-generated/sprite-web.scss')
      },
      apiOptions: {
        cssImageRef: "../images/sprite-web.png"
      }
    }),
    new MobileSpritesmithPlugin({
      src: {
        cwd: path.resolve(__dirname, 'src', 'images', 'mobile'),
        glob: '*.png'
      },
      target: {
        image: path.resolve(__dirname, 'src/images/sprite-mobile.png'),
        css: path.resolve(__dirname, 'src/spritesmith-generated/sprite-mobile.scss')
      },
      apiOptions: {
        cssImageRef: "../images/sprite-mobile.png"
      }
    }),

  ...
}

...

3) 스프라이트 SCSS 파일을 불러와서 사용

스프라이트 이미지를 사용하고자 하는 scss 파일에 스프라이트 scss 파일을 불러온 후 낱개의 이미지 파일명과 동일한 이름의 변수를 인자로 받는 sprite 믹스인 구문을 추가합니다.

...
@import '../spritesmith-generated/sprite-mobile';

...
.more-btn {
  &__img {
    @include sprite($mobile_actionbar_more_icon);

    &--on {
      @include sprite($mobile_actionbar_more_icon_on);
    }
  }
}

4) 번들 파일 확인

webpack 빌드 후 CSS 번들 파일을 확인해보면 아래와 같이 SCSS 파일이 빌드된 것을 확인할 수 있습니다.

.more-btn__img {
    background-image: url(../images/sprite-mobile.png);
    background-position: -36px 0px;
    width: 36px;
    height: 36px; }
    .more-btn__img--on {
      background-image: url(../images/sprite-mobile.png);
      background-position: 0px 0px;
      width: 36px;
      height: 36px; }

마치며

Webpack 설정 파일에 webpack-spritesmith 플러그인을 추가해서 스프라이트 이미지와 스프라이트 SCSS 파일을 자동으로 생성하고 사용할 수 있도록 CSS 스프라이트 자동화를 적용해보았습니다. Webpack과 SCSS를 사용하는 프로젝트에서 이미지가 추가될 때마다 스프라이트 이미지 파일 생성과 스프라이트 CSS 파일 수정을 직접 하고 있다면 이 플러그인 추가를 고민해보는 것도 좋을 것 같습니다.

yunheur

한 걸음씩 걸어 나아가는 중인 웹 개발자입니다.

TOC