Beomy

[Svelte] Svelte + TS + SCSS + α

  21 mins read  

전처리를 해주는 svelte-preprocess가 Svelte 공식 지원 라이브러리로 편입되면서, Svelte가 TypeScript를 공식 지원하게 되었습니다. 이번 포스트에서는 Rollup, Webpack 번들러에서 svelte-preprocess를 사용해서 Svelte에 TypeScript와 SCSS, autoprefixer, alias 가능을 적용한 프로젝트를 생성하는 방법을 살펴보도록 하겠습니다.

Rollup

Rollup 번들러를 사용해서 Svelte에 TypeScript와 SCSS, autoprefixer, alias를 사용할 수 있도록 설정해 보도록 하겠습니다. 아래 코드와 같이 Svelte에서 제공하는 sveltejs/template 템플릿을 다운로드하고 setupTypeScript 파일을 실행 후 패키지를 다운로드합니다.

npx degit sveltejs/template svelte-typescript-app
cd svelte-typescript-app
node scripts/setupTypeScript.js
npm install

svelte.config.js 생성

VS Code를 사용할 경우 프로젝트 루트에 svelte.config.js 파일을 생성해 주어야 몇 가지 문법을 에러로 잡아내지 않습니다. rollup.config.js의 svelte 옵션을 svelte.config.js 파일을 만들고 해당 파일에 아래와 같이 옮겨 적어 줍니다.

// svelte.config.js
const sveltePreprocess = require('svelte-preprocess')
const production = !process.env.ROLLUP_WATCH;

module.exports = {
  // enable run-time checks when not in production
  dev: !production,
  // we'll extract any component CSS out into
  // a separate file - better for performance
  css: css => {
    css.write('public/build/bundle.css');
  },
  preprocess: sveltePreprocess(),
}

rollup.config.js는 svelte 옵션을 아래와 같이 가져와 사용합니다.

// rollup.config.js
//...
export default {
  //...
  plugins: [
    svelte(require('./svelte.config')),
    //...
  ],
  //...
};

TypeScript 설정

node scripts/setupTypeScript.js를 실행하면 TypeScript가 적용된 템플릿으로 변경됩니다.

sveltePreprocesssourceMap 추가

디버깅에 용이하게 svelte.config.js 파일의 sveltePreprocess 함수에 sourceMap 설정을 추가하도록 하겠습니다.

// svelte.config.js
//...
module.exports = {
  //...
  preprocess: sveltePreprocess({
    sourceMap: !production
  }),
}

TypeScript 사용

위의 코드와 같이 설정이 끝나면 아래와 같이 TypeScript 사용이 가능해집니다.

<!-- App.svelte -->
<script lang="ts"> // lang="ts"를 선언한 <script>에서 TypeScript 사용할  있습니다.
  export let name: string;
</script>

<main>
  <h1>Hello {name}!</h1>
  <p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p>
</main>

SCSS 설정

scss를 사용할 수 있도록 아래 코드와 같이 패키지를 다운로드합니다.

npm i -D sass rollup-plugin-scss

소스코드에서 SCSS 파일 import하기

svelte-preprocess를 사용하면 Svelte 컴포넌트에서 <style lang="scss">를 사용해서 SCSS를 사용할 수 있지만, .scss 파일을 생성해서 스타일을 적용할 수는 없습니다. 소스코드에서 SCSS 파일을 import할 수 있도록 설정해 보도록 하겠습니다. rollup.config.js를 아래와 같이 수정합니다.

// rollup.config.js
//...
import scss from 'rollup-plugin-scss';

export default {
  plugins: [
    //...
    scss({
      output: 'public/build/assets.css'
    }),
    //...
  ]
};

import된 SCSS 파일은 public/build/assets.css 파일로 번들 되는데, public/index.html 파일에서 아래와 같이 번들 된 CSS 파일을 가져와야 합니다.

<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset='utf-8'>
  <meta name='viewport' content='width=device-width,initial-scale=1'>

  <title>Svelte app</title>

  <link rel='icon' type='image/png' href='/favicon.png'>
  <link rel='stylesheet' href='/global.css'>
  <link rel='stylesheet' href='/build/assets.css'> <!-- CSS 우선순의에 유의해서 선언 위치를 정해줍니다. -->
  <link rel='stylesheet' href='/build/bundle.css'>

  <script defer src='/build/bundle.js'></script>
</head>

<body>
</body>
</html>

위에 코드와 같이 설정이 끝나면 아래와 같이 main.ts에서 SCSS 파일 import가 가능해집니다.

// src/main.ts
import './assets/scss/common.scss';
import App from './App.svelte';

const app = new App({
  target: document.body,
  props: {
    name: 'world'
  }
});

export default app;

prependData 설정

SCSS 파일에 정의된 스타일이나 변수들을 컴포넌트의 <style>에서 @import 해서 사용해야 할 때, prependData를 사용하면 컴포넌트의 <style>에서 @import를 사용하지 않고 SCSS 변수를 가져와 사용할 수 있습니다. 아래와 같이 svelte.config.js 파일에서 prependData를 정의할 수 있습니다.

// svelte.config.js
//...
module.exports = {
  //...
  preprocess: sveltePreprocess({
    //...
    scss: {
      prependData: `@import "src/assets/scss/variables.scss";`
    }
  }),
}

위의 설정이 끝나면, 아래와 같이 SCSS 변수 사용이 가능합니다.

/* src/assets/scss/variables.scss */
$primary-color: #ff3e00;
<!-- App.svelte -->
<script lang="ts">
  export let name: string;
</script>

<main>
  <h1>Hello {name}!</h1>
  <p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p>
</main>

<style lang="scss">
  /* ... */
  main {
    /* ... */
    h1 {
      color: $primary-color; /* SCSS 변수 사용이 가능해집니다. */
      text-transform: uppercase;
      font-size: 4em;
      font-weight: 100;
    }
  }
  /* ... */
</style>

PostCSS 설정

자동 접두사를 사용하기 위해서 PostCSS를 사용해야 합니다. 아래 코드와 같이 postcssautoprefixer, rollup-plugin-postcss 패키지를 다운로드합니다.

npm i -D postcss autoprefixer rollup-plugin-postcss

컴포넌트의 스타일에 autoprefixer 설정

svelte.config.js 파일에서 아래와 같이 autoprefixer를 설정해 줍니다.

// svelte.config.js
const autoprefixer = require('autoprefixer');

//...
module.exports = {
  //...
  preprocess: sveltePreprocess({
    //...
    postcss: {
      plugins: [autoprefixer()]
    },
  }),
}

위의 설정이 끝나면 아래 코드와 같이 정의된 스타일이,

<script lang="ts">
  export let name: string;
</script>

<main>
  <h1>Hello {name}!</h1>
  <p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p>
</main>

<style lang="scss">
  /* ... */
  main {
    /* ... */
    h1 {
      user-select: none;
      /* ... */
    }
  }
  /* ... */
</style>

아래 그림처럼 -webkit, -moz, -ms 등, 브라우저 밴더 접두사가 추가됩니다.

autoprefixer

SCSS 파일에 autoprefixer 설정

rollup.config.js에 아래 코드와 같이 postcss를 사용해 줍니다.

// rollup.config.js
//...
import scss from 'rollup-plugin-scss';

export default {
  plugins: [
    //...
    postcss({
      plugins: [require('autoprefixer'),]
    }),
    //...
  ]
};

위의 설정이 끝나면 아래 코드와 같이 정의된 스타일이,

main {
  h1 {
    margin: 0;
    user-select: none;
  }
}

아래 그림처럼 -webkit, -moz, -ms 등, 브라우저 밴더 접두사가 추가됩니다.

autoprefixer

Alias 설정

컴포넌트를 만들고 사용하다 보면 import comp from '../../components/Item.svelte' 와 같이 상대 경로로 import하게 됩니다. import를 사용하는 컴포넌트 파일의 경로가 변경되면 import한 경로를 모두 바꿔줘야 하는데, 이런 귀찮은 작업을 Alias를 사용하면 최소화할 수 있습니다.

아래 코드와 같이 패키지를 다운로드합니다.

npm i -D @rollup/plugin-alias

패키지 다운로드가 끝나면,rollup.config.js 파일을 아래 코드와 같이 수정합니다.

// rollup.config.js
//...
import alias from '@rollup/plugin-alias';
import path from 'path';
//...

export default {
  //...
  plugins: [
    //...
    alias({
      entries: [
        { find: '@', replacement: path.resolve(__dirname, 'src') }
      ]
    }),
    //...
};

Alias를 사용해서 TypeScript 파일에서 TypeScript 파일을 import 하기 위해서는 아래 코드와 같이 tsconfig.json 파일을 수정해야 합니다.

// tsconfig.json
{
  //...
  "compilerOptions": {
    "baseUrl": "./",
    "paths": { "@/*": ["src/*"] }
  }
}

위의 설정이 끝나면, 아래 코드와 같이 alias를 사용할 수 있습니다. src 디렉토리 위치를 @로 대체한 예제입니다.

// main.ts
import '@/assets/scss/common.scss';
import App from '@/App.svelte';

const app = new App({
  target: document.body,
  props: {
    name: 'world'
  }
});

export default app;

Webpack

Webpack 번들러를 사용해서 Svelte에 TypeScript와 SCSS, autoprefixer, alias를 사용할 수 있도록 설정해 보도록 하겠습니다. 아래 코드와 같이 Svelte에서 제공하는 sveltejs/template-webpack 템플릿을 다운로드하고 패키지를 다운로드합니다.

npx degit sveltejs/template svelte-typescript-app
cd svelte-typescript-app
npm install

Webpack 번들러를 사용한 템플릿은 Rollup 번들러를 사용한 템플릿과 다르게 전처리기가 적용된 템플릿으로 변경해 주는 스크립트가 없어 따로 패키지를 다운로드하고 설정해야 합니다. TypeScript와 SCSS를 사용하기 위해 svelte-preprocess 패키지를 다운로드합니다.

npm i -D svelte-preprocess

svelte.config.js 생성

Rollup 번들러와 동일한 이유로 파일을 프로젝트 루트에 svelte.config.js 파일을 만들고, webpack.config.jssvelte-loader 옵션을 옮겨 적고, preprocess 옵션도 추가해 줍니다.

// svelte.config.js
const sveltePreprocess = require('svelte-preprocess');

module.exports = {
  preprocess: sveltePreprocess(),
  emitCss: true,
  hotReload: true
}

webpack.config.js는 svelte 옵션을 아래와 같이 가져와 사용합니다.

// webpack.config.js
//...
module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.svelte$/,
        use: {
          loader: 'svelte-loader',
          options: require('./svelte.config')
        }
      },
      //...
    ]
  },
  //...
};

TypeScript 설정

Webpack 번들러를 사용한 템플릿은 Rollup 번들러를 사용한 템플릿과 다르게 TypeScript가 적용된 템플릿으로 변경해 주는 스크립트가 없습니다. TypeScript를 사용할 수 있게 설정하기 위해 아래 코드와 같이 패키지를 다운로드합니다.

npm i -D typescript @tsconfig/svelte ts-loader

sveltePreprocesssourceMap 추가

디버깅을 좀 더 용이하게 하기 위해 svelte.config.js 파일의 sveltePreprocess 함수에 sourceMap 설정을 추가해 줍니다.

// svelte.config.js
//...
const mode = process.env.NODE_ENV || 'development';
const prod = mode === 'production';

module.exports = {
  preprocess: sveltePreprocess({
    sourceMap: !prod
  }),
  //...
}

webpack.config.js에 TypeScript 설정

webpack.config.js를 아래 코드와 같이 수정해 줍니다.

// webpack.config.js
//...
module.exports = {
  entry: {
    bundle: ['./src/main.ts']
  },
  resolve: {
    //...
    extensions: ['.mjs', '.js', '.svelte', '.tsx', '.ts'],
    //...
  },
  module: {
    rules: [
      //...
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ]
  },
  //...
};
  • entry.bundle 경로를 main.js에서 main.ts로 변경합니다.
  • main.js 파일을 main.ts 파일로 파일명을 변경합니다.
  • resolve.extenstions의 배열에 TypeScript를 사용하기 위해 .tsx.ts를 추가해 줍니다.
  • module.rolusts-loader 설정을 추가해 줍니다.

tsconfig.json 파일 생성

프로젝트 루트 위치에 tsconfig.json 파일을 생성하고 아래 코드와 같이 설정합니다.

{
  "extends": "@tsconfig/svelte/tsconfig.json",

  "include": ["src/**/*"],
  "exclude": ["node_modules/*", "__sapper__/*", "public/*"],
}

위의 코드와 같이 설정이 끝나면 아래와 같이 TypeScript 사용이 가능해집니다.

TypeScript 사용

<!-- App.svelte -->
<script lang="ts"> // lang="ts"를 선언한 <script>에서 TypeScript 사용할  있습니다.
  export let name: string;
</script>

<main>
  <h1>Hello {name}!</h1>
  <p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p>
</main>

SCSS 설정

scss를 사용할 수 있도록 아래 코드와 같이 패키지를 다운로드합니다.

npm i -D sass sass-loader

소스코드에서 SCSS 파일 import하기

Webpack 번들러에서도 소스코드에서 SCSS 파일을 import할 수 있도록 설정해 보도록 하겠습니다. webpack.config.js에서 style-loadercss-loader를 사용하는 부분을 아래와 같이 수정합니다.

// webpack.config.js
//...

module.exports = {
  //...
  module: {
    rules: [
      //...
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          /**
           * MiniCssExtractPlugin doesn't support HMR.
           * For developing, use 'style-loader' instead.
           * */
          prod ? MiniCssExtractPlugin.loader : 'style-loader',
          'css-loader',
          'sass-loader'
        ]
      },
      //...

    ]
  },
  //...
};

위의 코드와 같이 설정이 끝나면 아래와 같이 main.ts에서 SCSS 파일 import가 가능해집니다.

// src/main.ts
import './assets/scss/common.scss';
import App from './App.svelte';

const app = new App({
  target: document.body,
  props: {
    name: 'world'
  }
});

export default app;

prependData 설정

prependData를 설정하는 방법은 Rollup과 동일합니다. svelte.config.js 파일을 아래와 같이 수정합니다.

// svelte.config.js
//...
module.exports = {
  //...
  preprocess: sveltePreprocess({
    //...
    scss: {
      prependData: `@import "src/assets/scss/variables.scss";`
    }
  }),
  //...
}

위의 설정이 끝나면, 아래와 같이 SCSS 변수 사용이 가능합니다.

/* src/assets/scss/variables.scss */
$primary-color: #ff3e00;
<!-- App.svelte -->
<script lang="ts">
  export let name: string;
</script>

<main>
  <h1>Hello {name}!</h1>
  <p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p>
</main>

<style lang="scss">
  /* ... */
  main {
    /* ... */
    h1 {
      color: $primary-color; /* SCSS 변수 사용이 가능해집니다. */
      text-transform: uppercase;
      font-size: 4em;
      font-weight: 100;
    }
  }
  /* ... */
</style>

PostCSS 설정

postcssautoprefixer, postcss-loader 패키지를 다운로드합니다.

npm i -D postcss autoprefixer postcss-loader

컴포넌트의 스타일에 autoprefixer 설정

svelte.config.js 파일에서 아래와 같이 autoprefixer를 설정해 줍니다.

// svelte.config.js
const autoprefixer = require('autoprefixer');

//...
module.exports = {
  //...
  preprocess: sveltePreprocess({
    //...
    postcss: {
      plugins: [autoprefixer()]
    }
  }),
}

위의 설정이 끝나면 아래 코드와 같이 정의된 스타일이,

<script lang="ts">
  export let name: string;
</script>

<main>
  <h1>Hello {name}!</h1>
  <p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p>
</main>

<style lang="scss">
  /* ... */
  main {
    /* ... */
    h1 {
      user-select: none;
      /* ... */
    }
  }
  /* ... */
</style>

아래 그림처럼 -webkit, -moz, -ms 등, 브라우저 밴더 접두사가 추가됩니다.

autoprefixer

SCSS 파일에 autoprefixer 설정

webpack.config.js에 아래 코드와 같이 postcss-loader를 사용해 줍니다.

// webpack.config.js
//...

module.exports = {
  //...
  module: {
    rules: [
      //...
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          /**
           * MiniCssExtractPlugin doesn't support HMR.
           * For developing, use 'style-loader' instead.
           * */
          prod ? MiniCssExtractPlugin.loader : 'style-loader',
          'css-loader',
          'sass-loader',
          'postcss-loader'
        ]
      },
      //...

    ]
  },
  //...
};

postcss.config.js를 프로젝트 루트에 아래 코드와 같이 만들어 줍니다.

// postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}

위의 설정이 끝나면 아래 코드와 같이 정의된 스타일이,

main {
  h1 {
    margin: 0;
    user-select: none;
  }
}

아래 그림처럼 -webkit, -moz, -ms 등, 브라우저 밴더 접두사가 추가됩니다.

autoprefixer

Alias 설정

Alias를 사용하기 위해 webpack.config.js 파일을 아래 코드와 같이 수정합니다.

// webpack.config.js
//...
module.exports = {
  //...
  resolve: {
    alias: {
      //...
      '@': path.resolve(__dirname, 'src')
    },
    //...
  },
  //...
};

Rollup과 동일하게 tsconfig.json 파일을 아래와 같이 수정해야 합니다.

// tsconfig.json
{
  //...
  "compilerOptions": {
    "baseUrl": "./",
    "paths": { "@/*": ["src/*"] }
  }
}

위의 설정이 끝나면, 아래 코드와 같이 alias를 사용할 수 있습니다. src 디렉토리 위치를 @로 대체한 예제입니다.

// main.ts
import '@/assets/scss/common.scss';
import App from '@/App.svelte';

const app = new App({
  target: document.body,
  props: {
    name: 'world'
  }
});

export default app;

svelte-check 설정

Rollup에서는 템플릿에 svelte-check 패키지가 추가되어 있지만, Webpack에서는 추가되어 있지 않기 때문에 아래와 같이 패키지를 설치해야 합니다.

npm i -D svelte-check

패키지 다운로드가 되면 package.jsonscript에 아래와 같이 validate를 추가해 줍니다.

// package.json
{
  //...
  "scripts": {
    //...
    "validate": "svelte-check"
  }
}

npm run validate 명령어를 실행하면 Svelte를 컴파일 할 때 발생하는 error와 warning를 확인할 수 있습니다.

부록: 템플릿

지금까지 이야기했던 설정을 추가해서 Rollup 템플릿과 Webpack 템플릿을 만들었습니다.

beomy/template

아래 명령어를 사용하여 Svelte + TS + SCSS + α(rollup, autoprefixer, alias)를 사용할 수 있는 프로젝트 보일러 플레이트를 만들 수 있습니다.

npx degit beomy/template my-svelte-project

beomy/template-webpack

아래 명령어를 사용하여 Svelte + TS + SCSS + α(webpack, autoprefixer, alias)를 사용할 수 있는 프로젝트 보일러 플레이트를 만들 수 있습니다.

npx degit beomy/template-webpack my-svelte-project

참고