NestJS 백엔드와 Svelte 컴파일러, 그 빌드 결과물을 통합하는 방법입니다

 

TL;DR

   전체 코드는 GitHub에 있습니다. 링크

 

 

목차

  1. NestJS with Svelt

 

 

NestJS with Svelt

NestJS 

nest new nestjs-with-svelte

   NestJS는 Nest CLI를 이용하여 쉽게 프로젝트를 생성할 수 있습니다. 제일 먼저 NestJS 프로젝트를 생성해 줍니다. 저는 패키지 매니저를 주로 npm 대신 yarn을 사용하기 때문에 yarn으로 선택하였습니다.

 

/* 프로젝트 폴더 트리 */
.
├── .eslintrc.js
├── .git
│   └── (중략)
├── .gitignore
├── .prettierrc
├── README.md
├── nest-cli.json
├── node_modules
│   └── (중략)
├── package.json
├── src
│   ├── app.controller.spec.ts
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   └── main.ts
├── test
│   ├── app.e2e-spec.ts
│   └── jest-e2e.json
├── tsconfig.build.json
├── tsconfig.json
└── yarn.lock

   설치가 완료되면 프로젝트에는 위와 같은 구조가 생성됩니다.

   NestJS는 정적 웹 페이지를 프로젝트에 포함시켜 제공할 수 있습니다. 예를들어 프론트엔드로 React를 사용할 때, NestJS 따로, React 따로 서버를 켤 필요가 없이 한꺼번에 실행시킬 수 있게 됩니다. 이렇게 배포할 경우 CORS 문제도 해결될 수 있다는 장점이 있습니다. 여기서는 빌드시 Svelte 파일을 컴파일하고, 정적 파일을 함께 배포하게 됩니다. 먼저 패키지를 설치해 줍니다.

/* yarn 사용 시 */
yarn add @nestjs/serve-static

/* npm 사용 시 */
npm install --save @nestjs/serve-static

   설치가 완료되었으면, 정적 파일을 NestJS 부트스트랩에 포함시켜줘야 합니다. 레퍼런스에서 써진 대로 client라는 폴더에 넣어 실행시키겠습니다. 이때 무조건 index.html라는 파일을 제공하기 때문에 해당 폴더에 index.html이 존재해야 합니다.

 

app.module.ts
index.html
main.ts

  app.module.ts에 정적 파일의 위치를 imports에 작성해주고 임시로 index.html 파일을 만들어 주었습니다. 이렇게 정적 파일을 제공하는 경우 Controller에 작성된 Uri를 제외한 모든 Uri는 정적 파일로 가게 됩니다. 즉 해당 도메인으로 들어갔을 때 정적 파일이 제일 먼저 제공되어야 한다면 루트 uri에 아무 것도 없어야 합니다. 단순히 NestJS의 Controller에서 경로가 매핑되지 않게 해도 되지만, 여기서는 '/api'라는 경로를 추가해 백엔드에서는 무조건 '/api' 경로로 탐색되게 하겠습니다. main.ts에 'api'라는 전역 접두사를 설정해주면 됩니다. 

 

  yarn start:debug를 작성하여 디버깅 모드로 테스트해봅시다. http://localhost:3000/으로 접속해 테스트 해보면 방금 index.html에 입력한 텍스트가 잘 뜨는 것을 확인할 수 있습니다. http://localhost:3000/api로 들어가면 app.controller.ts 파일에서 확인할 수 있는 Hello World!가 나옵니다.

 

   백엔드 쪽 설정은 이제 거의 다 끝났습니다. 남은 것은 svelte를 설치하고 연동하는 것 뿐입니다.

 

 

 

/* yarn 사용 시 */
yarn add sirv-cli
yarn add -D @rollup/plugin-commonjs @rollup/plugin-node-resolve \
			rollup rollup-plugin-css-only rollup-plugin-livereload \
            rollup-plugin-svelte rollup-plugin-terser svelte

/* npm 사용 시 */
npm install --save sirv-cli
npm install --save-dev @rollup/plugin-commonjs @rollup/plugin-node-resolve \
			rollup rollup-plugin-css-only rollup-plugin-livereload \
            rollup-plugin-svelte rollup-plugin-terser svelte

   Svelte를 설치해 줍시다. 자기 패키지 매니저에 맞게 설치하시면 되겠습니다. Svelte는 rollup이라는 것을 쓰기 때문에 실행하려면 rollup.config.js가 필요합니다. 이번 프로젝트에서는 Svelte 파일들은 svelte 폴더에, 빌드된 파일들은 client에 넣겠습니다. 따라서 거기에 맞게 변경된 기본적인 스크립트는 아래와 같습니다. (기본적인 .svelte 파일이 저장되는 svelte 폴더, 기타 정적 파일이 들어가는 public 폴더, 빌드된 파일이 들어가는 client 폴더 이렇게 세 폴더로 구성되게 됩니다.) 또한 svelt라는 스크립트를 추가하여 svelt가 별도로 실행될 수 있게 만들었습니다. 프로젝트 폴더의 루트에 넣어줍시다.

/* Svelte 폴더 구조 */
.
├── svelte /* 소스 파일 저장 위치 */
├── client
│   ├── public /* 정적 파일 제공 위치 */
│   ├── build /* 빌드 파일 위치 */
│   ├── favicon.png
│   ├── global.css
│   └── index.html
└── /* 나머지 생략*/
import svelte from 'rollup-plugin-svelte';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
import css from 'rollup-plugin-css-only';

const production = !process.env.ROLLUP_WATCH;

function serve() {
	let server;

	function toExit() {
		if (server) server.kill(0);
	}

	return {
		writeBundle() {
			if (server) return;
			server = require('child_process').spawn('yarn', ['svelt', '--', '--dev'], {
				stdio: ['ignore', 'inherit', 'inherit'],
				shell: true
			});

			process.on('SIGTERM', toExit);
			process.on('exit', toExit);
		}
	};
}

export default {
	input: 'svelte/main.js',
	output: {
		sourcemap: true,
		format: 'iife',
		name: 'app',
		file: 'client/build/bundle.js'
	},
	plugins: [
		svelte({
			compilerOptions: {
				// 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({ output: 'bundle.css' }),

		// If you have external dependencies installed from
		// npm, you'll most likely need these plugins. In
		// some cases you'll need additional configuration -
		// consult the documentation for details:
		// https://github.com/rollup/plugins/tree/master/packages/commonjs
		resolve({
			browser: true,
			dedupe: ['svelte']
		}),
		commonjs(),

		// In dev mode, call `npm run start` once
		// the bundle has been generated
		!production && serve(),

		// Watch the `public` directory and refresh the
		// browser on changes when not in production
		!production && livereload('client/public'),

		// If we're building for production (npm run build
		// instead of npm run dev), minify
		production && terser()
	],
	watch: {
		clearScreen: false
	}
};

   NestJS의 패키지 스크립트 중에서 저희는 주로 build, start, start:debug, start:prod만 쓰니 이 스크립트 들에 맞게 pre 스크립트를 추가해 줍시다. 그리고 svelte라는 스크립트도 추가해 줍니다.

{
  (전략)
  "scripts": {
    "prebuild": "rimraf dist && rollup -c",
    "build": "nest build",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "start": "nest start",
    "start:dev": "nest start --watch",
    "prestart:debug": "rollup -c -w &"
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/main",
    "svelt": "sirv client",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json"
  },
  (후략)
}

 

 

디버그 테스트 움짤

   일반적으로 디버깅 할 때는 터미널에 start:debug를 작성하여 실행합니다. 전체적인 흐름은 아래와 같습니다.

  1. 터미널에서 yarn start:debug 실행하면
  2. prestart:debug 스크립트가 먼저 실행됨
  3. 써진대로 rollup -c -w & 명령어가 백그라운드로 실행
  4. rollup은 rollup.config.js에 써진대로 동작함
  5. rollup.config.js에 작성된 yarn svelt -- --dev를 실행하여
  6. svelt라는 스크립트의 명렁어, sirv client --dev가 실행되어 디버그 모드로 svelte 동작
  7. 이후 start:debug 명령어대로 nestjs가 디버깅 모드로 실행됨

   따라서 (포트를 바꾸지 않았으니) localhost:5000에 svelte와, localhost:3000/api에 nestjs 백엔드가 동시에 돌아가게 됩니다.

   

 

 

빌드 후 실행 테스트 움짤

   다음으로 빌드를 했을 때입니다.

 

  1. 터미널에서 yarn build를 실행하면
  2. prebuild가 먼저 실행됨
  3. rimraf dist와 rollup -c 명령어가 실행되어 svelte 파일을 컴파일한 후
  4. NestJS를 빌드함
  5. 터미널에 yarn start:prod로 빌드된 파일을 실행하면
  6. node dist/main에 있는 빌드된 nestjs 파일을 실행합니다.
  7. 위에서 적은 대로 client 폴더에 있는 index.html 정적 파일도 같이 제공됩니다.

 

   필요한 나머지 파일들은 sveltsveltejs/template를 설치하면 나오는 파일들을 전부 가져오고 일부 수정했습니다. 이하는 추가하고 수정한 파일들입니다.

 

 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/bundle.css'>

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

<body>
</body>
</html>
~                

 index.html

 

global.css

더보기
html, body {
	position: relative;
	width: 100%;
	height: 100%;
}

body {
	color: #333;
	margin: 0;
	padding: 8px;
	box-sizing: border-box;
	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}

a {
	color: rgb(0,100,200);
	text-decoration: none;
}

a:hover {
	text-decoration: underline;
}

a:visited {
	color: rgb(0,80,160);
}

label {
	display: block;
}

input, button, select, textarea {
	font-family: inherit;
	font-size: inherit;
	-webkit-padding: 0.4em 0;
	padding: 0.4em;
	margin: 0 0 0.5em 0;
	box-sizing: border-box;
	border: 1px solid #ccc;
	border-radius: 2px;
}

input:disabled {
	color: #ccc;
}

button {
	color: #333;
	background-color: #f4f4f4;
	outline: none;
}

button:disabled {
	color: #999;
}

button:not(:disabled):active {
	background-color: #ddd;
}

button:focus {
	border-color: #666;
}

global.css

 

main.js

더보기
import App from './App.svelte';

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

export default app;

main.js

 

App.svelte

더보기
<script>
  export let name;
</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>
  main {
    text-align: center;
    padding: 1em;
    max-width: 240px;
    margin: 0 auto;
  }

  h1 {
    color: #ff3e00;
    text-transform: uppercase;
    font-size: 4em;
    font-weight: 100;
  }

  @media (min-width: 640px) {
    main {
      max-width: none;
    }
  }
</style>

App.svelte

 

자세한 파일들은 GitHub 레포지토리에서 확인해 주세요

 

 

/* 프로젝트 폴더 트리 */

.
├── .eslintrc.js
├── .git
│   └── /* 중략 */
├── .gitignore
├── .prettierrc
├── README.md
├── client
│   ├── build
│   │   ├── bundle.css
│   │   ├── bundle.js
│   │   └── bundle.js.map
│   ├── favicon.png
│   ├── global.css
│   ├── index.html
│   └── public
├── dist
│   ├── app.controller.d.ts
│   ├── app.controller.js
│   ├── app.controller.js.map
│   ├── app.module.d.ts
│   ├── app.module.js
│   ├── app.module.js.map
│   ├── app.service.d.ts
│   ├── app.service.js
│   ├── app.service.js.map
│   ├── main.d.ts
│   ├── main.js
│   ├── main.js.map
│   └── tsconfig.build.tsbuildinfo
├── nest-cli.json
├── package.json
├── rollup.config.js
├── src
│   ├── app.controller.spec.ts
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   └── main.ts
├── svelte
│   ├── App.svelte
│   └── main.js
├── test
│   ├── app.e2e-spec.ts
│   └── jest-e2e.json
├── tsconfig.build.json
├── tsconfig.json
├── yarn-error.log
└── yarn.lock

소스 트리

 

 

 

 

참고

docs.nestjs.com/

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Progamming), FP (Functional Programming), and FRP (Functional Reac

docs.nestjs.com

 

svelte.dev/

 

Svelte • Cybernetically enhanced web apps

Svelte is a radical new approach to building user interfaces. Whereas traditional frameworks like React and Vue do the bulk of their work in the browser, Svelte shifts that work into a compile step that happens when you build your app. Instead of using tec

svelte.dev

 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기