파란하늘의 지식창고
article thumbnail
Published 2022. 7. 9. 23:49
SvelteKit 공부하기 Study/JavaScript
반응형

이 글의 내용 중 SvelteKit Docs 문서 부분은 Sveltekit가 Release 되기 전 문서 내용입니다.

글 작성 이후 session이 삭제되고, +접두사로 파일 이름 규칙이 변경되는 등 대대적인 변경이 있었습니다.

따라서 SvelteKit Docs 내용 부분은 현재 도움이 되지 않습니다.

SvelteKit을 공부하기 전에 Svelte 부터 공부해야 합니다.

2022.06.23 - [Study/JavaScript] - Svelte 공부하기


Svelte는 tutorial 사이트에서 직접 예제를 테스트해볼 수 있어서 공부하기 편했다.

SvelteKit은 예제를 테스트 해볼 수 있는 사이트 제공이 없다.

따라서 로컬에 설치하고 개인적인 프로젝트를 만들면서 나한테 필요한 설정들에 대해서 정리해 보고 tutorial의 내용을 정리해 보았다.

자세한 내용은 SvelteKit 문서를 참고하면 된다.

https://kit.svelte.dev/docs

SvelteKit 문서의 경우 Configuration까지만 적어두었다.
(Types, SEO, Asset Handling, Migrating from sapper, Additional Resources, Appendix는 적지 않음)

SvelteKit 프로젝트 만들기

만드는 건 SvelteKit 홈페이지 첫 화면에 안내되어 있다.

https://kit.svelte.dev/

다만 npm이 아닌 pnpm으로 만들었다.

pnpm create svelte bluesky-project-front-sveltekit

선택지를 묻는 항목에서 순서대로 Skelection project, TypeScript, ESLint 사용, Prettier 사용, Playwright 미사용을 선택하고 생성하였다.

다음으로 생성된 프로젝트로 들어가서 install을 실행하고 Visual Studio Code를 연다.

cd bluesky-project-front-sveltekit
pnpm install
code .

만약 Visual Studio Code에 Vite 확장이 설치되어 있으면 알아서 다음 명령을 수행하여 서버를 port 4000으로 실행한다.

npx vite --port=4000

따로 실행하고자 한다면 다음과 같이 실행해도 된다.

pnpm run dev

package.json에 최초 설정되는 내용은 다음과 같다.

{
    "name": "bluesky-project-front-sveltekit",
    "version": "0.0.1",
    "scripts": {
        "dev": "vite dev",
        "build": "vite build",
        "package": "svelte-kit package",
        "preview": "vite preview",
        "prepare": "svelte-kit sync",
        "check": "svelte-check --tsconfig ./tsconfig.json",
        "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
        "lint": "prettier --check --plugin-search-dir=. . && eslint .",
        "format": "prettier --write --plugin-search-dir=. ."
    },
    "devDependencies": {
        "@sveltejs/adapter-auto": "next",
        "@sveltejs/kit": "next",
        "@typescript-eslint/eslint-plugin": "^5.27.0",
        "@typescript-eslint/parser": "^5.27.0",
        "eslint": "^8.16.0",
        "eslint-config-prettier": "^8.3.0",
        "eslint-plugin-svelte3": "^4.0.0",
        "prettier": "^2.6.2",
        "prettier-plugin-svelte": "^2.7.0",
        "svelte": "^3.44.0",
        "svelte-check": "^2.7.1",
        "svelte-preprocess": "^4.10.6",
        "tslib": "^2.3.1",
        "typescript": "^4.7.4",
        "vite": "^2.9.13"
    },
    "type": "module"
}

개인 프로젝트 설정

Proxy 설정하기 (vite.config.js 설정)

proxy 설정은 해당 프로젝트에서 특정 주소에 대해서 api호출을 다른 대상으로 호출하도록 처리하여 따로 별도의 BE framework과 연동할 수 있게 하는 설정이다.

내 경우 api를 제공하는 부분은 java Spring Boot로 만든 서버를 사용하고 웹페이지 구성을 위해 SvelteKit을 사용하기 때문에 SvelteKit에서 proxy 설정이 필요하다.

vue의 경우 vue가 자체적으로 설정할 수 있도록 vue.config.js (nuxt 사용 시엔 nuxt.config.js)에 devServer.proxy 옵션이 있었다.

https://cli.vuejs.org/config/#devserver-proxy

해당 설정은 webpack.config.js의 설정을 vue나 nuxt config.js에서 할 수 있게 지원하는 설정인 듯하다.

https://webpack.js.org/configuration/dev-server/

vite를 사용하는 경우에도 proxy 설정이 있다.

https://vitejs.dev/config/#server-proxy

해당 설정을 추가해 본다.

import { sveltekit } from '@sveltejs/kit/vite';

/** @type {import('vite').UserConfig} */
const config = {
    plugins: [sveltekit()],
    server: {
        proxy: {
            '/api': {
                target: 'http://localhost:8084/',
                changeOrigin: true
            }
        }
    }
};

export default config;

해당 설정이 잘 동작하는지 ajax 호출을 테스트해 보면 된다.

<script>
    $: userInfo = fetch('/api/user/loginInfo', {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json'
            // 'Content-Type': 'application/x-www-form-urlencoded',
        }
    }).then((response) => response.json());
</script>

{#await userInfo}
    <p>...Loading</p>
{:then userInfo}
    id : {userInfo.id}<br />
    name : {userInfo.name}
{:catch error}
    <p>오류가 발생했습니다.</p>
{/await}

sveltestrap 사용하기

sveltestrap은 bootstrap을 svelte component 형태로 사용할 수 있게 해주는 라이브러리이다.
bootstrap을 사용하길 원하는 경우 bootstrap을 직접 사용하는 것보다 더 효과적으로 사용할 수 있다.
초반에 bootstrap을 사용하려다가 tailwind css로 변경하였지만 설정하던 과정은 적어둔다.

SvelteStrap 사이트

pnpm i sveltestrap

공통 사용 영역이므로 __layout.svelte 에 다음과 같이 설정

<script>
    import { Styles } from 'sveltestrap';
</script>

<Styles />
<slot />

다음과 같이 설정해서 사용한다.

<script>
    import { Button } from 'sveltestrap';
</script>
<Button>test</Button>

tailwindcss 사용해 보기

이제까진 계속 bootstrap관련 라이브러리를 사용했었는데 요즘은 tailwindcss도 많은 관심을 받고 있다.

2021 CSS framework Rankings

한번 써보기로 한다.

tailwindcss의 경우 각 라이브러리 별 설정 방법을 자세히 안내하고 있다.

Install Tailwind CSS with SvelteKit

해당 가이드를 그대로 따라만 하면 된다.
관련 라이브러리 들을 아래와 같이 추가하고

pnpm install -D tailwindcss postcss autoprefixer
npx tailwindcss init tailwind.config.cjs -p

svelte.config.js에 preprocess를 다음과 같이 설정하고

import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/kit/vite';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    adapter: adapter()
  },
  preprocess: vitePreprocess()
};

export default config;

tailwind.config.cjs에 content 설정을 다음과 같이 하고 (이 부분은 아마 설정이 되어 있을 것이다.)

module.exports = {
  content: ['./src/**/*.{html,js,svelte,ts}'],
  theme: {
    extend: {}
  },
  plugins: []
};

./src/app.css 파일을 만들어 다음과 같이 설정한다.

@tailwind base;
@tailwind components;
@tailwind utilities;

이제 사용하는 설정을 ./src/routes/+layout.svelte에 다음과 같이 추가한다.

<script>
  import "../app.css";
</script>

<slot />

여기까지 진행하였으면 모든 svelte 파일에서 tailwindcss를 사용할 수 있게 된다.

<h1 class="text-3xl font-bold underline">
  Hello world!
</h1>

style을 굳이 선언 안 해도 되는 장점이 있고 쓰다 보면 CSS에 대해 자연스럽게 공부할 수 있는 장점도 있지만 극단적으로 class를 도배해야 하는 단점이 있다.

그래도 공부할 땐 bootstrap보다 이걸 사용하는 게 더 좋을 것 같다.

무료 template을 공개한 사이트를 몇 군데 찾아서 적당히 사용하면 되지 않을까 싶다. (어디가 더 좋은지 모름)

fontawesome 사용하기

fontawesome은 수많은 아이콘을 쉽게 사용할 수 있게 해 준다.
유료 및 무료 버전이 있고 각 패키지 별 설치가 가능하지만 간단하게 사용하기 위해 다음과 같이 추가하였다.

pnpm install @fortawesome/fontawesome-free

최상위 __layout.svelte에 다음과 같이 추가한다.

<script>
    import '@fortawesome/fontawesome-free/js/all.min.js';
</script>

이제 손쉽게 아이콘을 사용할 수 있다.

<i class="fa-brands fa-facebook" />

date-fns 사용하기

한동안 사용하던 moment js가 maintenance mode가 되어 더 이상 개발되지 않는다.
대체 라이브러리로 date-fns, dayjs, luxon 등이 있는데 이중 date-fns가 가장 많이 사용하고 있어 사용해 보았다.

date-fns 홈페이지

설치는 다음과 같다.

pnpm i date-fns

사용은 다음과 같다.

import { format, formatDistanceToNow } from 'date-fns';
import { ko } from 'date-fns/locale';

const targetDate = new Date(dateStr);

formatDistanceToNow(targetDate, { addSuffix: true, locale: ko });    // *초 전, *일 전, *년 전...

toast ui editor

toast ui editor는 naver에서 제공하는 라이브러리이다.
markdown으로 작성할 수 있다.

vue를 사용할 때엔 제공하는 vue-editor wrapper를 사용하였지만 svelte를 사용할 경우 wrapper 없이 사용해야 한다.

toast ui editor 홈페이지

설치는 다음과 같다.

pnpm i @toast-ui/editor

사용은 다음과 같다.

<script type="ts">
    import { onMount } from 'svelte';
    import Editor from '@toast-ui/editor';
    import '@toast-ui/editor/dist/toastui-editor.css';

    onMount(async () => {
        var targetEl = document.querySelector('#editor');

        if (targetEl != null) {
            const editor = new Editor({
                el: targetEl,
                height: '500px',
                initialEditType: 'markdown',
                previewStyle: 'vertical'
            });
        }
    });
</script>

<div id="editor" />

navigation을 통한 접근은 정상 호출되지만 현재 해당 페이지 호출 시 아래와 같은 에러가 발생한다.

ReferenceError: self is not defined

빌드 설정하기 (adapter-static SPA mode 사용하기)

내 경우 백엔드 서버는 java Spring Boot를 사용하고 있다.
sveltekit로 빌드한 정적 페이지를 java 서버에 올려서 사용하게 된다.

https://kit.svelte.dev/docs/adapter-static#spa-mode

이를 위한 설정은 다음과 같다.

먼저 adapter-static을 설치한다.

pnpm i -D @sveltejs/adapter-static

svelte.config.js를 다음과 같이 설정한다.

import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/kit/vite';

/** @type {import('@sveltejs/kit').Config} */
const config = {
	// Consult https://kit.svelte.dev/docs/integrations#preprocessors
	// for more information about preprocessors
	preprocess: vitePreprocess(),

	kit: {
		adapter: adapter({
			fallback: 'sveltekit.html'
		}),
		prerender: { entries: [] }
	}
};

export default config;

src/routes/+layout.js에 ssr false 설정을 다음과 같이 한다.

export const ssr = false;

이제 빌드를 한다.

pnpm run build

빌드된 결과물은 ./build 하위에 생성된다.

설정한 주소마다 html 페이지가 만들어지는게 아니고 단일 html 페이지가 생성된다.
요청 주소가 다르더라도 모두 이 페이지를 호출하면 요청한 주소에 따라 개발한대로 처리가 된다.

Spring Boot 기반 프로젝트의 /src/main/resources/static 하위에 위 파일을 넣어두고
java controller 설정에서 sveltekit.html를 바라보도록 호출할 주소들을 설정한다.

@Controller
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public class IndexController {

	@GetMapping({"/", "/index", "접근하는 모든 주소..."})
	public String index() {
		return "forward:sveltekit.html";
	}
	
}

이렇게 설정하면 sveltekit으로 FE 개발, java Spring Boot로 BE 개발을 분리 할 수 있게 된다.


SvelteKit Docs 내용

Project structure

Project files

src

src 디렉터리에는 project의 핵심이 들어있다.

  • lib 에는 $lib alias를 통해 가져올 수 있거나 svelte-kit package를 사용하여 배포할 수 있는 라이브러리 코드가 포함되어 있다.
  • params 에는 app에 필요한 모든 param matcher가 포함되어 있다.
  • routes 에는 application의 pagesendpoints 가 포함되어 있다.
  • app.html 은 다음 placeholder를 포함하는 HTML 문서인 page template이다.
    • %sveltekit.head% - app에 필요한 <link><script> element 및 <svelte:head> content
    • %sveltekit.body% - 렌더링 된 page의 markup
    • %sveltekit.assets% - page에서 paths.assets path로의 상대 경로
    • %sveltekit.nonce% - 수동으로 포함된 link 및 script에 대한 CSP nonce (사용되는 경우)
  • hook.js (선택 사항) application의 hooks가 포함되어 있다.
  • service-worker.js (선택 사항) service worker가 포함되어 있다.

TypeScript를 사용하는 경우 .js 파일 대신 .ts 파일을 사용할 수 있다.

static

robots.txt 또는 favicon.png 같이 제공되어야 하는 static asset은 여기에 포함된다.

package.json

package.json 파일은 @sveltejs/kit , sveltevitedevDependencies 에 포함해야 한다.

npm create svelte 로 프로젝트를 만들면 package.json"type" : "module" 이 포함되어 있다.
.js 파일은 importexport keyword가 있는 기본 JavaScript module로 해석된다.
legacy CommonJS 파일에는 .cjs file 확장자가 필요하다.

svelte.config.js

이 파일에는 Svelte 및 SvelteKit configuration이 포함되어 있다.

tsconfig.json

이 파일(또는 .ts 파일보다 type-checked .js 파일을 선호하는 경우 jsconfig.json )은 npm create svelte@latest 동안 typechecking을 추가한 경우 TypeScript를 구성한다.
SvelteKit는 특정 구성이 특정 방식으로 설정되어 있기 때문에 자체 configuration이 extends 되는 자체 .svelte-kit/tsconfig.json 파일을 생성한다.

vite.config.js

SvelteKit 프로젝트는 실제로 다른 Vite configuration과 함께 @sveltejs/kit/vite plugin을 사용하는 Vite 프로젝트이다.

Other files

test

npm create svelte@latest 동안 테스트를 추가하도록 선택하면 test 디렉터리가 생성된다.

.svelte-kit

프로젝트를 개발하고 빌드할 때 SvelteKit는 .svelte-kit 디렉토리 (outDir로 구성 가능)에 파일을 생성한다.
언제든지 내용을 무시하고 삭제할 수 있다.
다음 dev 또는 build 시 재 성성된다.

Web standards

Fetch APIs

SvelteKit는 network에서 data를 가져오기 위해 fetch를 사용한다.
브라우저뿐만 아니라 hook과 endpoint에서도 사용할 수 있다.

credential을 유지하면서 HTTP 호출을 사용하지 않고 server-side 렌더링 중에 endpoint를 직접 호출하기 위해 load function에서 특별한 버전의 fetch 를 사용할 수 있다.
(load 외부의 server-side code에서 credential이 있는 fetch를 수행하려면 cookie 및/또는 autorization header를 명시적으로 전달해야 한다.)
또한 relative 요청을 수행할 수 있는 반면 server-side fetch 에는 일반적으로 정규화된 URL이 필요하다.

fetch 자체 외에도 Fetch API에는 다음 interface가 포함된다.

Request

Request 의 instance는 hook 및 endpoint에서 event.request 로 액세스 할 수 있다.
예를 들어 request.json()request.formData() 와 같은 유용한 method가 포함되어 있다.
endpoint에 게시된 데이터를 가져온다.

Response

Response 의 instance는 await fetch(…) 에서 반환된다.
기본적으로 SvelteKit app은 requestresponse 로 바꾸는 machine이다.

Headers

Header interface는 들어오는 request.headers 를 읽고 나가는 response.headers 를 설정할 수 다.

src/routes/what-is-my-user-agent.js :

/** @type {import('@sveltejs/kit').RequestHandler} */
export function get(event) {
  // log all headers
  console.log(...event.request.headers);

  return {
    body: {
      // retrieve a specific header
      userAgent: event.request.headers.get('user-agent')
    }
  };
}

Stream APIs

대부분의 경우 endpoint는 userAgent example과 같이 완전한 data를 반환한다.
때로는 너무 커서 한 번에 memory에 담을 수 없거나 chunk로 전달되는 response를 반환해야 할 수 있으며 이를 위해 platform은 stream(ReadableStream, WritableStream 및 TransformStream)을 제공한다.

URL APIs

URL은 URL interface로 표시되며 여기에는 originpathname (browser의 hash )과 같은 유용한 properties가 포함된다.
이 interface는 hook 및 endpoint의 event.url , page의 $page.url , beforeNavigateafterNavigatefromto 등 다양한 위치에 표시된다.

URLSearchParams

URL이 있는 곳마다 URLSearchParams 의 instance인 url.searchParams 를 통해 query paramete에 액세스 할 수 있다.

const foo = url.searchParams.get('foo');

Web Crypto

Web Crypto API는 crypto global을 통해 제공된다.
콘텐츠 보안 정책 헤더에 내부적으로 사용되지만 UUID 생성과 같은 작업에도 사용할 수 있다.

Routing

Pages

page 구성은 src/routes 하위에 .svelte 파일을 만들면 된다.

예를 들어 src/routes/about.svelte 파일을 만들면 /about 으로 접근을 할 수 있다.
또한 폴더를 만들어도 동일하게 path로 구성된다.
예를 들어 src/routes/about/index.svelte 파일을 만들면 /about 으로 접근을 할 수 있다.

src/routes/index.svelte :

<svelte:head>
  <title>Welcome</title>
</svelte:head>

<h1>Hello and welcome to my site!</h1>

<a href="/about">About my site</a>

src/routes/about.svelte/src/routes/about/index.svelte 가 동시에 존재하면 src/routes/about.svelte 를 우선한다.

동적 파라미터를 path 주소로 가지는 경우 대괄호 [path] 를 사용한다.
예를 들어 src/routes//blog/[blogId]/list.svelte 와 같이 사용할 수 있다.

route에는 src/routes/[category]/[item].svelte 같이 여러 dynamic parameter를 가질 수 있고 또는 src/routes/[category]-[item].svelte 처럼 -로 구분 지어 지정할 수도 있다.

Endpoints

endpoint는 HTTP method에 해당하는 request handler function을 내보내는 .js (또는 .ts ) 파일로 작성된 module이다.
Request handler를 사용하면 server (예: database 또는 filesystem)에서만 사용할 수 있는 데이터를 읽고 쓸 수 있다.

response를 나타내는 { status, headers, body } object를 반환해야 한다.

src/routes/random.js :

/** @type {import('@sveltejs/kit').RequestHandler} */
export async function GET() {
  return {
    status: 200,
    headers: {
      'access-control-allow-origin': '*'
    },
    body: {
      number: Math.random()
    }
  };
}
  • status 는 HTTP status code이다.
    • 2xx - successful response (default는 200 )
    • 3xx - redirection (location header가 함께 있어야 함)
    • 4xx - client error
    • 5xx - server error
  • header 는 위와 같이 plain object이거나 header class의 instance일 수 있다.
  • body 는 plain object일 수도 있고 오류가 발생할 경우 Error일 수도 있다.
    JSON으로 serialized 된다.

GET 또는 HEAD response는 body 를 포함해야 하지만 이 제한을 벗어나는 세 가지 properties는 모두 선택사항이다.

Page endpoints

endpoint의 page와 filename (확장자 제외)이 동일한 경우 페이지는 client-side navigation 동안 fetch 를 통해 또는 SSR 동안 직접 function 호출을 통해 endpoint에서 props를 가져온다.

(page가 filename에서 named layouts 또는 matchers에 대한 구문을 사용하는 경우 해당 page endpoint의 filename에도 해당 layout이 포함되어야 한다. )

예를 들어 src/routes/items/[id].svelte page의 경우

src/routes/items/[id].svelte :

<script>
  // populated with data from the endpoint
  export let item;
</script>

<h1>{item.title}</h1>

src/routes/items/[id].js endpoint와 짝을 이룬다.
($lib import에 대해서는 이후에 설명함)

src/routes/items/[id].js :

import db from '$lib/database';

/** @type {import('./__types/[id]').RequestHandler} */
export async function GET({ params }) {
  // `params.id` comes from [id].js
  const item = await db.get(params.id);

  if (item) {
    return {
      status: 200,
      headers: {},
      body: { item }
    };
  }

  return {
    status: 404
  };
}

위의 GET function의 type은 ./__types/[id].d.ts 에서 가져온 것으로 이 파일은 SvelteKit (outDir 내부, rootDirs 옵션 사용)에서 생성한 파일로 param 에 액세스 할 때 type 안정성을 제공한다.
자세한 내용은 generated types section을 참고하면 된다.

page 대신 raw data를 가져오려면 request에 accept: application/json header를 포함하거나 편의상 URL에 /__data.json 을 추가한다. (예: /items/[id]/__data.json )

Standalone endpoints

일반적으로 endpoint는 해당 endpoint가 쌍으로 구성된 페이지에 데이터를 제공하기 위해 존재한다.

그러나 페이지와 별도로 존재할 수 있다.

standalone endpoint는 반환된 body type에 비해 약간 더 유연하다.

object 및 Error instance 외에도 Uint8Array 또는 ReadableStream 을 반환할 수 있다.

standalone endpoint는 원하는 경우 파일 확장자를 제공할 수 있으며 그렇지 않은 경우 직접 액세스 할 수 있다.

filename endpoint
src/routes/data/index.json.js /data.json
src/routes/data.json.js /data.json
src/routes/data/index.js /data
src/routes/data.js /data

POST, PUT, PATCH, DELETE

endpoint는 해당 function을 export 함으로써 모든 HTTP method를 처리할 수 있다.

export function POST(event) {...}
export function PUT(event) {...}
export function PATCH(event) {...}
export function DELETE(event) {...}

이러한 function은 get 과 마찬가지로 page로 전달될 body 를 props로 반환할 수 있다.

get 의 4xx / 5xx response는 error page 렌더링을 초래하지만 비 GET request에 대한 유사한 response는 렌더링 form validation error와 같은 작업을 수행할 수 없다.

src/routes/items.js :

import * as db from '$lib/database';

/** @type {import('./__types/items').RequestHandler} */
export async function GET() {
  const items = await db.list();

  return {
    body: { items }
  };
}

/** @type {import('./__types/items').RequestHandler} */
export async function POST({ request }) {
  const [errors, item] = await db.create(request);

  if (errors) {
    // return validation errors
    return {
      status: 400,
      body: { errors }
    };
  }

  // redirect to the newly created item
  return {
    status: 303,
    headers: {
      location: `/items/${item.id}`
    }
  };
}

src/routes/items.svelte :

<script>
  // The page always has access to props from `GET`...
  export let items;
  // ...plus props from `post` when the page is rendered
  // in response to a POST request, for example after
  // submitting the form below
  export let errors;
</script>

{#each items as item}
  <Preview item={item}/>
{/each}

<form method="post">
  <input name="title">

  {#if errors?.title}
    <p class="error">{errors.title}</p>
  {/if}

  <button type="submit">Create item</button>
</form>

Body parsing

request object는 standard Request clsss의 instance이다.
따라서 request body에 쉽게 액세스 할 수 있다.

/** @type {import('@sveltejs/kit').RequestHandler} */
export async function POST({ request }) {
  const data = await request.formData(); // or .json(), or .text(), etc

  await create(data);
  return { status: 201 };
}

Setting cookies

Endpoint는 set-cookies 와 함께 headers object를 반환하여 cookie를 설정할 수 있다.
여러 cookie를 동시에 설정하려면 배열을 반환한다.

/** @type {import('@sveltejs/kit').RequestHandler} */
export function GET() {
  return {
    headers: {
      'set-cookie': [cookie1, cookie2]
    }
  };
}

HTTP method overrides

HTML

HTML <Form> element는 기본적으로 GETPOST method만 지원한다.
configuration에서 PUTDELETE 같은 다른 method를 지정하고 form의 action에 \_method-VERB parameter(name을 설정할 수 있음)를 추가하여 사용할 수 있다.

svelte.config.js :

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    methodOverride: {
      allowed: ['PUT', 'PATCH', 'DELETE']
    }
  }
};

export default config;
<form method="post" action="/todos/{id}?_method=PUT">
  <!-- form elements -->
</form>

native <form> behavior를 사용하면 JavaScript가 실패하거나 비활성화되어도 app이 계속 동작한다.

private modules

.well-known 이 아닌 _ 또는 . 이 있는 파일 및 디렉터리는 기본적으로 비공개이며, 즉 routes를 만들지 않는다.
(그러나 routes를 생성하는 파일에서 import 할 수 있음)
routes configuration을 사용하여 공개 또는 비공개로 간주되는 모듈을 구성할 수 있다.

Advanced routing

Rest parameters

route segment의 수를 알 수 없는 경우 rest 구문을 사용할 수 있다.
예를 들어 Github의 file viewer를 다음과 같이 구현할 수 있다.

/[org]/[repo]/tree/[branch]/[...file]

이 경우 /sveltejs/kit/tree/master/documentation/docs/01-routing.md 에 대한 요청은 다음 parameter를 페이지에서 사용할 수 있다.

{
  org: 'sveltejs',
  repo: 'kit',
  branch: 'master',
  file: 'documentation/docs/01-routing.md'
}

/src/routes/a/[…rest]/z.svelte/a/z (parameter가 없을 경우), /a/b/z/a/b/c/z 등과 일치한다.
예를 들어 matcher를 사용하는 경우와 같이 rest parameter의 값이 valid 한 지 확인한다.

Matching

/src/routes/archive/[page] 와 같은 route는 /archive/3 과 일치하지만 /archive/potato 와도 일치한다.
이를 원하지 않을 때 parameter string ("3" 또는 "potato" )를 사용하는 matcher를 추가하여 route parameter가 잘 구성되어 있는지 확인하고 valid 한 경우 true 를 params 디렉터리로 반환한다.

src/params/integer.js :

/** @type {import('@sveltejs/kit').ParamMatcher} */
export function match(param) {
  return /^\d+$/.test(param);
}
src/routes/archive/[page=integer]

pathname이 일치하지 않으면 SvelteKit은 다른 routes와 일치한 지 확인하고 없으면 404를 반환한다.

Sorting

여러 route가 지정된 path와 일치할 수 있다.
예를 들어 각 경로는 /foo-abc 와 일치한다.

src/routes/[...catchall].svelte
src/routes/[a].js
src/routes/[b].svelte
src/routes/foo-[c].svelte

SvelteKit은 어떤 route가 요청되는지 알아야 한다.

다음과 같은 규칙에 따라 찾는다.

  • 더 구체적인 route가 우선순위
  • standalone endpoint가 동일한 page 보다 우선순위 높음
  • matcher를 사용한 parameter([name=type] 가 없는 parameter ([name] ) 보다 우선순위 높음
  • rest parameter가 우선 순위 낮음
  • 동등한 경우 알파벳 순으로 우선 순위 높음

위 우선순위에 따라 /foo-abc 호출은 src/routes/foo-abc.svelte 를 호출하고
/foo-dev 는 덜 구체적인 경로가 아닌 /src/routes/foo-[c].svelte 를 호출한다.

src/routes/foo-[c].svelte
src/routes/[a].js
src/routes/[b].svelte
src/routes/[...catchall].svelte

Encoding

file 이름은 URI로 디코딩된다.
예를 들어 %40[username].svelte 와 같은 파일 이름을 @ 로 시작하는 문자와 일치한다.

assert.equal(
  decodeURIComponent('%40[username].svelte'),
  '@[username].svelte'
);

% 문자를 표현하려면 %25 를 사용한다.

Layouts

모든 페이지에 적용되는 레이아웃을 만들려면 src/routes/__layout.svelte 파일을 만들면 된다.

기본 layout (따로 layout을 설정하지 않을 경우 SvelteKit에서 사용하는 layout)은 다음과 같다.

<slot></slot>

Nested layouts

단일 /settings page가 아니라 /settings/profile/settings/notifications 와 같이 공유 하위 메뉴가 는 중첩된 페이지가 있는 경우 /settings 하위 페이지에만 적용되는 layout을 만들 수 있다.

src/routes/settings/__layout.svelte :

<h1>Settings</h1>

<div class="submenu">
  <a href="/settings/profile">Profile</a>
  <a href="/settings/notifications">Notifications</a>
</div>

<slot></slot>

Named layouts

app의 일부는 기본 layout 이외의 다른 것이 필요할 수 있다.
이러한 경우 이름이 지정된 layout을 만들 수 있다.

src/routes/__layout-foo.svelte :

<div class="foo">
  <slot></slot>
</div>

해당 레이아웃을 사용할 파일의 이름을 다음과 같이 만든다.

src/routes/my-special-page@foo.svelte :

<h1>I am inside __layout-foo</h1>

Scoping

named layout은 어느 depth에서도 만들 수 있고 동일한 하위 트리의 모든 component에 적용된다.
예를 들어 __layout-foo/x/one/x/two 에 적용되지만 /x/three/four 에는 적용되지 않는다.

src/routes/
├ x/
│ ├ __layout-foo.svelte
│ ├ one@foo.svelte       # ✅ page has `@foo`
│ ├ two@foo.svelte       # ✅ page has `@foo`
│ └ three.svelte         # ❌ page does not have `@foo`
└ four@foo.svelte        # ❌ page has `@foo`, but __layout-foo is not 'in scope'

Inheritance chains

layout은 named layout, 동일한 디렉터리 또는 상위 디렉터리에서 상속하도록 선택할 수 있다.
예를 들어 x/y/__layout@root.svelte/x/y (/x/y/one , /x/y/two/x/y/three 가 모두 이 layout에서 상속됨)에 대해 이름이 없으므로 default layout이다.
@root 를 지정하였기 때문에 __layout.sveltex/__layout.svelte 를 건너뛰고 가장 가까운 __layout-root.svelte 에서 직접 상속한다.

src/routes/
├ x/
│ ├ y/
│ │ ├ __layout@root.svelte
│ │ ├ one.svelte
│ │ ├ two.svelte
│ │ └ three.svelte
│ └ __layout.svelte
├ __layout.svelte
└ __layout-root.svelte

__layout-root.svelte 에 하나의 <slot /> 이 포함된 경우 이는 @root 를 추가하여 app의 모든 페이지 또는 중첩 layout에 대해 빈 layout으로 '재설정(reset)' 할 수 있음을 의미한다.

parent가 지정되지 않은 경우 layout은 tree에서 제일 가까운 default(즉, 이름이 지정되지 않은) layout을 상속한다.
__layout.svelte 에서 상속하는 __layout-root.svelte 와 같이 tree의 default layout에서 상속하는 것이 유용하다.
명시적으로 @default 를 지정하여 /x/y/one 및 형제가 x/__layout.svelte 를 사용하지 않고 app의 default layout 사용하도록 허용하여 이를 수행할 수 있다.

src/routes/
├ x/
│ ├ y/
│ │ ├ __layout@root.svelte
│ │ ├ one.svelte
│ │ ├ two.svelte
│ │ └ three.svelte
│ └ __layout.svelte
├ __layout.svelte
└ __layout-root@default.svelte

default 는 예약된 이름이다. 즉, __layout-default.svelte 파일을 가질 수 없다.

Error pages

page가 로드되지 않으면 (Loading 참조) SvelteKit은 error page를 렌더링 한다.
layout 및 page와 함께 __error.svelte component를 만들어 이 페이지를 customize 할 수 있다.

예를 들어 src/routes/settings/notifications/index.svelte 가 로드에 실패하면 SvelteKit은 동일한 레이아웃에 src/routes/settings/notifications/__error.svelte 를 렌더링 한다. (존재하는 경우)
그렇지 않으면 부모 layout에서 src/routes/settings/__error.svelte 를 렌더링 하거나 root layout에서 src/routes/__error.svelte 를 렌더링 한다.

SvelteKit은 src/routes/__error.svelte 가 없으면 기본 error page를 제공하지만 직접 만들어 사용하는 게 좋다.

error component에 load function이 있는 경우 errorstatus properties가 함께 호출된다.

<script context="module">
  /** @type {import('@sveltejs/kit').Load} */
  export function load({ error, status }) {
    return {
      props: {
        title: `${status}: ${error.message}`
      }
    };
  }
</script>

<script>
  export let title;
</script>

<h1>{title}</h1>

layout은 page store를 통해 errorstatus 에 액세스 할 수 있다.

서버 측 stack trace는 사용자에게 권한 있는 정보가 노출되는 것을 방지하기 위해 production error 에서 제거된다.

404s

nested error page는 특정 page를 렌더링 하는 동안 오류가 발생한 경우에만 렌더링 된다.
기존 경로와 일치하지 않는 요청의 경우 SvelteKit은 대신 일반 404를 렌더링 한다.
예를 들어 이러한 경로가 주어지면

src/routes/
├ __error.svelte
├ marx-brothers/
│ ├ __error.svelte
│ ├ chico.svelte
│ ├ harpo.svelte
│ └ groucho.svelte

/marx-brothers/karl 을 방문하면 /marx-brothers/__error.svelte 가 렌더링 되지 않는다.
Nested error page를 렌더링 하려면 /marx-brothers/\* 요청과 일치하는 경로를 만들고 404를 반환해야 한다.

src/routes/
├ __error.svelte
├ marx-brothers/
│ ├ __error.svelte
│ ├ [...path].svelte
│ ├ chico.svelte
│ ├ harpo.svelte
│ └ groucho.svelte

src/routes/marx-brothers/[...path].svelte :

<script context="module">
  /** @type {import('./__types/[...path]').Load} */
  export function load({ params }) {
    return {
      status: 404,
      error: new Error(`Not found: /marx-brothers/${params.path}`)
    };
  }
</script>

Loading

page 또는 layout을 정의하는 component는 component가 생성되기 전에 실행되는 load function을 export할 수 있다.
이 function은 server-side rendering과 client 양쪽 다 실행되며 page가 rendering 되기 전에 data를 가져오고 처리하여 loading spinner를 방지할 수 있다.

page의 data가 endpoint에서 오는 경우 load function이 필요하지 않을 수 있다.
예를 들어 다음과 같은 외부 API에서 data를 가져오는 경우와 같이 더 많은 유연성이 필요할 때 유용하다.

src/routes/blog/[slug].svelte :

<script context="module">
  /** @type {import('./__types/[slug]').Load} */
  export async function load({ params, fetch, session, stuff }) {
    const url = `https://cms.example.com/article/${params.slug}.json`;
    const response = await fetch(url);

    return {
      status: response.status,
      props: {
        article: response.ok && (await response.json())
      }
    };
  }
</script>

<script context="module"> - 이 component가 rendering되기 전에 load 가 실행되기 때문에 필요하다.
component 별 instance인 code는 두 번째 <script> tag로 이동해야 한다.

endpoints와 마찬가지로 page는 generated type (위의 예에서 ./[slug] )을 가져와 params 가 올바르게 입력되었는지 확인할 수 있다.

load 는 server와 client 모두에서 load가 실행된다는 점을 제외하고 next.js의 getStaticProps 또는 getServerSideProps 와 유사하다.
위의 예에서 사용자가 이 페이지에 대한 link를 클릭하면 data는 서버를 거치지 않고 cms.example.com 에서 가져온다.

SvelteKit의 load 는 다음과 같은 special properties가 있는 fetch implementation을 전달받는다.

  • server의 cookie에 엑세스 할 수 있다.
  • HTTP 호출을 실행하지 않고 app의 자체 endpoint에 대해 요청을 할 수 있다.
  • 사용할 때 response의 복사본을 만든 다음 hydration을 위해 initial page load에 포함된 상태로 보낸다.

load 는 page및 layout component(import component가 아님)에만 적용되며 기본 rendering option을 사용하여 server와 browser 모두에서 실행된다.

load block 내에서 호출되는 code :

  • native fetch를 사용하는 대신 SvelteKit에서 제공하는 fetch wrapper를 사용해야 한다.
  • window, document 또는 browser-specific object를 참조해선 안된다.
  • client에 노출될 API key 또는 secret을 직접 참조해서는 안되며 대신 필요한 secret을 사용하는 endpoint를 호출해야 한다.

request 별 state를 global variable에 저장하지 않고 대신 database connection caching 및 holding 같은 cross-cutting에만 사용하는 것이 좋다.

Server의 shared state를 변경하면 현재 client 뿐만 아니라 모든 client에 영향을 미친다.

Input

load function은 8개의 field (url , params , props , fetch , session , stuff , status , error )를 포함하는 object를 전달받는다.
load function은 반응적(reactive)이며 parameter가 변경되면 다시 실행되지만 function에서 사용되는 경우에만 해당한다.
특히 url , session 또는 stuff 가 function에서 사용되면 값이 변경될 때마다 다시 실행되며 params 의 개별 property들도 마찬가지이다.

function 선언의 구조화 parameter를 사용하는 것으로 간주하기에 충분하다.

Url

urlorigin , hostname , pathnamesearchParams (parsed query string을 URLSearchParams object로 포함)와 같은 property들을 포함하는 URL 의 instance이다.
url.hash 는 server에서 사용할 수 없으므로 load 하는 동안 엑세스 할 수 없다.

일부 환경에서는 server-side rendering 중 request header에서 파생된다.
예를 들어 adapter-node를 사용하는 경우 URL이 정확하도록 adapter를 구성해야 할 수 있다.

params

paramsurl.pathname 및 route filename에서 파생된다.

src/routes/a/[b]/[…c] 와 같은 route filename과 /a/x/y/zurl.pathname 의 경우 params object는 다음과 같다.

{
  "b": "x",
  "c": "y/z"
}

props

load하는 page에 endpoint가 있는 경우 해당 page에서 반환된 data는 leaf component의 load function 내에서 props 로 엑세스할 수 있다.
endpoint가 없는 layout component 및 page의 경우 props 는 empty object가 된다.

fetch

fetch 는 몇 가지 추가 기능이 있고 native fetch web API와 동일합니다.

  • page request에 대한 cookeauthorization header를 상속하므로 server에서 자격 증명 요청 만드는데 사용할 수 있다.
  • server에서 relative request를 할 수 있다. (일반적으로 fetch 는 server context에서 사용될 때 origin이 있는 URL이 필요하다.)
  • endpoint에 대한 request는 HTTP 호출의 overhead 없이 server-side rendering 중에 handler function으로 직접 이동한다.
  • server-side rendering 중에 response가 capture되어 rendering된 HTML에 inline 된다.
  • hydration 동안 HTML에서 response를 읽어 일관성을 보장하고 추가 network request를 방지한다.

cookie는 대상 host가 SvelteKit application 또는 해당 application의 보다 구체적인 subdomain 동일한 경우에만 전달된다.

Session

session 은 현재 request와 관련된 server (예 : 현재 사용자)의 data를 전달할 수 있다.
default는 undefined 이다.
사용법을 배우려면 getSession 을 참조한다.

stuff

stuff 는 layout에서 하위 layout 및 page로 전달되며 사용가능하게 만드는데 필요한 다른 모든 것으로 채워질 수 있다.
root __layout.svelte component의 경우 {} 와 동일하지만 해당 component의 load function이 stuff property가 있는 object를 반환하면 후속 load function에서 사용할 수 있다.

status

status 는 error page를 rendering 할 때 HTTP status code이고 그렇지 않으면 null 이다.

error

error 는 error page를 rendering 할 때 throw 된 (또는 이전 load 에서 반환된) error이고 그렇지 않으면 null 이다.

Output

load 에서 Promise를 반환하면 SvelteKit는 promise가 해결될 때까지 rendering을 지연한다.
return value에는 아래에 나열된 여러 property들이 있으며 모두 선택사항이다. (return value도 마찬가지임)

status , error , redirectcache 는 error page를 rendering할 때 무시된다.

status

page의 HTTP status code이다.
error 를 반환하는 경우 4xx 또는 5xx 응답이어야 한다.
redirect 를 반환하는 경우 3xx 응답이어야 한다.
기본값은 200 이다.

error

load 중에 문제가 발생하면 4xx 또는 5xx status code와 함께 error를 설명하는 string 또는 Error object를 반환한다.

redirect

page를 redirect 해야 하는 경우 (page가 더 이상 사용되지 않거나 사용자가 로그인해야 하는 경우 등) 3xx status code와 함께 redirect 되어야 하는 위치가 포함된 string 을 반환한다.

redirect 문자열은 올바르게 인코딩된 URI여야 한다.
absolute URI와 relative URI 모두 허용된다.

cache

cache: {
  "maxage": 300,
  "private": false
}

page가 cache 되도록 하려면 page의 max age(second)를 설명하는 number 로 설정된 maxage property를 포함하는 cache object를 반환한다.
선택적으로 resulting Cache-Control header가 비공개 또는 공개여야 하는지 여부를 나타내는 boolean private property도 포함한다. (즉 개별 브라우저와 함께 CDN에서 cache 할 수 있음)

cache.private 가 정의되지 않은 경우 SvelteKit은 다음과 같이 경험적 방법을 사용하여 자동으로 설정한다.
load function가 credentialled fetch 를 수행하거나 page에서 session 을 사용하는 경우 page는 private로 간주된다.

이것은 layout이 아닌 page에만 적용된다.

props

load function이 props object를 반환하면 props는 렌더링 될 때 component에 전달된다.

stuff

이것은 기존 stuff 와 병합되고 후속 layout및 page component의 load funcion으로 전달된다.

결합된 stuff 는 page store를 $page.stuff 로 사용하는 component에서 사용할 수 있으며 page가 data를 layout으로 '상향'으로 전달할 수 있는 mechanism을 제공한다.

dependencies

page가 의존하는 URL을 나타내는 문자열 배열로, 이후에 invalidate 와 함께 사용하여 load 를 다시 실행할 수 있다.
custom API client를 사용하는 경우에만 dependencies 에 추가하면 된다.
제공된 fetch function으로 로드된 URL은 자동으로 추가된다.

URL은 로드되는 page에 대해 absolute 또는 relative일 수 있으며 encoded 되어야 한다.

Hooks

optional src/hook.js (또는 src/hook.ts 또는 src/hook/index.js ) 파일은 서버에서 실행되는 4개의 function (모두 optional)을 export 한다. - handle , handleError , getSessionexteralFetch

이 파일의 위치는 config.kit.files.hooks로 구성할 수있다.

handle

이 function은 SvelteKit 서버가 request를 수신할 때마다 실행된다. (app이 실행되는 동안 또는 사전 렌더링 중에 발생).
그리고 response를 결정한다.
request를 나타내는 event object와 SvelteKit의 router를 호출하고 그에 따라 response (페이지 렌더링 또는 endpoint 호출)을 생성하는 resolve 라는 function을 receive 한다.
이를 통해 response header 또는 body를 수정하거나 SvelteKit를 완전히 우회할 수 있다. (예: 프로그래밍 방식으로 endpoint 구현)

src/hook.js :

/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
  if (event.url.pathname.startsWith('/custom')) {
    return new Response('custom response');
  }

  const response = await resolve(event);
  return response;
}

이미 사전 렌더링 된 page를 포함하는 static asset에 대한 요청은 SvelteKit에서 처리되지 않는다.

구현되지 않은 경우 기본 값은 ({event, resolve}) => resolve(event) 이다.
endpoint로 전달되는 요청에 custom data를 추가하려면 아래와 같이 event.locals Object를 채운다.

src/hook.js :

/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
  event.locals.user = await getUserInformation(event.request.headers.get('cookie'));

  const response = await resolve(event);
  response.headers.set('x-custom-header', 'potato');

  return response;
}

sequence helper function을 사용하여 multiple handle function을 추가할 수 있다.

resolve 는 또한 response가 렌더링되는 방식을 더 잘 제어할 수 있는 두 번째 optional parameter 지원한다.
해당 parameter는 다음 field를 가질 수 있는 object이다.

- `ssr : boolean` (default `true` ) `false` 인 경우 server-side rendering 대신 empty 'shell' page를 렌더링한다.
- `transformPage(opts: { html: string }): string` - HTML에 custom transform 적용

src/hook.js :

/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
  const response = await resolve(event, {
    ssr: !event.url.pathname.startsWith('/admin'),
    transformPage: ({ html }) => html.replace('old', 'new')
  });

  return response;
}

server-side rendering을 비활성화 하면 SvelteKit app이 single-page app 또는 SPA로 효과적으로 바뀐다.
대부분의 경우 이는 권장되지 않는다. (부록 참조)
비활성화하는 것이 정말 적절한지 고려하고 모든 request에 대해가 아니라 선택적으로 비활성화 한다.

handleError

렌더링 중에 error가 발생하면 이 function은 error 및 error를 발생시킨 event 와 함께 호출된다.
이를 통해 data를 tracking service로 보내거나 error를 console에 print하기 전에 formatting을 customize 할 수 있다.

개발 중에 Svelte code의 구문 오류로 error가 발생하면 error 위치를 강조 표시하는 frame property가 추가된다.

구현되지 않은 경우 SvelteKit은 default formatting으로 error를 기록한다.

src/hook.js :

/** @type {import('@sveltejs/kit').HandleError} */
export async function handleError({ error, event }) {
  // example integration with https://sentry.io/
  Sentry.captureException(error, { event });
}

handleError 는 uncaught exception의 경우에만 호출된다.
page와 endpoint가 4xx 및 5xx status code로 명시적으로 response할 때는 호출되지 않는다.

getSession

이 function은 event object를 가져와 client에서 엑세스 할 수 있는 session object를 반환하므로 사용자에게 안전하게 노출되어야 한다.
SvelteKit server가 page를 렌더링 할 때마다 실행된다.

구현되지 않은 경우 session은 {} 이다.

src/hook.js :

/** @type {import('@sveltejs/kit').GetSession} */
export function getSession(event) {
  return event.locals.user
    ? {
        user: {
          // only include properties needed client-side —
          // exclude anything else attached to the user
          // like access tokens etc
          name: event.locals.user.name,
          email: event.locals.user.email,
          avatar: event.locals.user.avatar
        }
      }
    : {};
}

session 은 serializable 해야 한다.
즉, function 또는 custom class와 같은 것을 포함해서는 안되며 built-in JavaScript data type만 포함해야 한다.

externalFetch

이 function을 사용하면 server에서 (또는 pre-rendering 중에) 실행되는 load function 내에서 발생하는 외부 resource에 대한 fetch request를 modify (또는 replace) 할 수 있다.

예를 들어 load function은 사용자가 각 page로 client-side navigation을 수행할 때 https://api.yourapp.com 과 같은 public URL에 요청을 할 수 있지만 SSR 중에는 API와 public internet 사이에 있는 proxy 및 load balancer를 포함하여 API를 직접 호출 하는 것이 합리적일 수 있다.

/** @type {import('@sveltejs/kit').ExternalFetch} */
export async function externalFetch(request) {
  if (request.url.startsWith('https://api.yourapp.com/')) {
    // clone the original request, but change the URL
    request = new Request(
      request.url.replace('https://api.yourapp.com/', 'http://localhost:9999/'),
      request
    );
  }

  return fetch(request);
}

Modules

SvelteKit은 application에서 사용할 수 있는 많은 module을 만든다.

$app/env

import { browser, dev, prerendering } from '$app/env';

browser

app이 브라우저에서 실행 중인 경우 true이다.

dev

dev server가 실행 중인지 여부.
이는 NODE_ENV 또는 MODE 에 해당한다고 보장되지 않는다.

const dev: boolean;

prerendering

prerendering인 경우 true , 그 외엔 false 이다.

const prerendering: boolean;

$app/navigation

import {
  afterNavigate,
  beforeNavigate,
  disableScrollHandling,
  goto,
  invalidate,
  prefetch,
  prefetchRoutes
} from '$app/navigation';

afterNavigate

page가 mount 되고 SvelteKit이 새 URL로 이동하지만 해당 component가 남아있을 때마다 실행되는 lifecycle function이다.

function afterNavigate(
  fn: (navigation: { from: URL | null; to: URL }) => void
): void;

beforeNavigate

링크를 클릭하거나 goto 를 호출하거나 브라우저의 뒤로/앞으로 컨트롤을 사용하여 새 URL(내부 또는 외부)로 이동하기 전에 트리거하는 navigation interceptor
이는 탐색이 완료되거나 예정된 URL을 조회하는 것을 조건부로 방지하려는 경우 유용하다.

function beforeNavigate(
  fn: (navigation: { from: URL; to: URL | null; cancel: () => void }) => void
): void;

disableScrollHandling

page가 탐색 후 업데이트 될 때 호출되는 경우(예: onMount 또는 aftgerNavigate 또는 action) 이는 SvelteKit 의 built-in scroll handling을 비활성화 한다.
이는 사용자의 기대치를 깨트리기 때문에 일반적으로 권장하지 않는다.

function disableScrollHandling(): void;

goto

SvelteKit이 지정된 url 을 탐색할 때 (또는 탐색에 실패하는 경우 Promise가 거부할 때) 해결되는 Promise를 반환한다.

function goto(
  url: string | URL,
  opts?: {
    replaceState?: boolean;
    noscroll?: boolean;
    keepfocus?: boolean;
    state?: any;
  }
): Promise<void>;

invalidate

현재 active page에 속한 load function이 문제의 resource를 fetch 하는 경우 다시 실행하거나 invalidated resource가 page 자체인 경우 page endpoint에서 데이터를 다시 가져온다.
이후에 page가 업데이트 될 때 resolve하는 Promise 를 반환한다.

function invalidate(
  dependency: string | ((href: string) => boolean)
): Promise<void>;

prefetch

프로그래밍 방식으로 주어진 page를 미리 가져온다.

  1. page의 code가 load되었는지 확인하고
  2. 적절한 옵션으로 page의 load function을 호출한다.

이것은 사용자가 sveltekit:prefetch 를 사용하여 <a> element를 탭하거나 마우스를 over할 때 SvelteKit이 trigger하는 것과 동일한 동작이다.
다음 navigation이 href 인 경우 load에서 반환된 값이 사용되어 navigation이 즉시 이루어진다.
prefetch가 완료되면 resolve하는 Promise를 반환한다.

function prefetch(href: string): Promise<void>;

prefetchRoutes

아직 가져오지 않은 route에 대한 code를 프로그래밍 방식으로 미리 가져온다.
일반적으로 subsequent navigation 속도를 높이기 위해 이것을 호출할 수 있다.

argument가 제공되지 않으면 모든 route를 fetch하고 그렇지 않으면 /about (src/routes/about.svelte 와 일치) 또는 /blog/* (/routes/blog/[slug].svelte 와 일치)와 같은 일치하는 pathname으로 route를 지정할 수 있다.

prefetch와 달리 개별 page에 대해 load를 호출하지 않는다.
route가 prefetch 되었을 때 resolve되는 Promise를 반환한다.

function prefetchRoutes(routes?: string[]): Promise<void>;

$app/paths

import { base, assets } from '$app/paths';

assets

config.kit.paths.assets 와 일치하는 absolute path이다.

config.kit.paths.assets 값이 지정되면 asset이 최종 URL에 아직 존재하지 않기 때문에 vite dev 또는 vite preview 중에 /_svelte_kit_assets 로 대체된다.

const assets: `https://${string}` | `http://${string}`;

base

config.kit.paths.base 와 일치하는 문자열이다.
빈 문자열이 아닌 한 / 로 시작되어야 하지만 / 로 끝나지 않아야 한다. (예: /base-path )

const base: `/${string}`;

$app/stores

import { getStores, navigating, page, session, updated } from '$app/stores';

store는 context의 역할을 한다.
root component의 context에 추가된다.
즉, sessionpage 는 동일한 서버에서 동시에 처리되는 여러 request 간에 공유되는 것이 아니라 서버의 각 request에 고유하므로 session 에 사용자별 데이터를 포함하는것이 안전하다.

그 때문에 component 초기화 중에 store를 subscribe 해야 한다. (component에서 $page 처럼 store 값을 참조하는 경우 자동으로 발생함)

getStores

getContext 관련 편의 function.
component 초기화 중에 호출해야 한다.
어떤 이유로 component가 mount 될 때까지 store subscription을 연기해야 하는 경우에만 이것을 사용한다.

function getStores(): {
  navigating: typeof navigating;
  page: typeof page;
  session: typeof session;
  updated: typeof updated;
};

navigating

readable store.
navigation이 시작되면 그 값은 { from: URL, to: URL } 이고 navigating이 끝나면 null로 되돌아간다.

const navigating: Readable<Navigation | null>;

page

값에 page data가 포함된 readable store.

const page: Readable<Page>;

session

초기 값이 getSession 에서 반환된 값인 writable store.
쓸 수는 있지만 변경 사항이 서버에 유지되지는 않는다.
사용자가 직접 구현해야 함.

const session: Writable<App.Session>;

updated

초기 값이 false 인 readable store.
version.pollInterval 이 0이 아닌 값이면 SvelteKit은 app의 새 버전을 polling하고 하나를 감지하면 store 값을 true 로 업데이트한다.
update.check() 는 polling에 관계없이 즉시 check를 강제 실행한다.

const updated: Readable<boolean> & { check: () => boolean };

$lib

이는 src/lib 또는 config.kit.files.lib 으로 지정된 디렉토리에 대한 간단한 alias이다.
../../../../ 같은 처리 없이 일반 component 및 utility module에 엑세스 할 수 있다.

$service-worker

import { build, files, prerendered, version } from '$service-worker';

이 module은 service worker만 사용할 수 있다.

build

Vite에서 생성된 파일을 나타내는 URL string array로 cache.addAll(build) 로 캐싱하기에 적합하다.

const build: string[];

files

static directory 또는 config.kit.files.assets 에 의해 지정된 모든 디렉토리의 파일을 나타내는 URL string array이다.
config.kit.serviceWorker.files 를 사용하여 static directory에서 포함한 파일을 customize 할 수 있다.

const files: string[];

prefendered

미리 렌더링된 page 및 endpoint에 해당하는 pathname의 array.

const prerendered: string[];

version

config.kit.version 을 참조한다.
service worker 내부에 고유한 cache name을 생성하는데 유용하므로 나중에 app을 배포하면 이전 cache를 무효화할 수 있다.

const version: string;

@sveltejs/kit/hooks

sequence

middleware 같은 방식으로 여러 handle 호출을 sequencing하기 위한 helper function이다.

src/hook.js :

import { sequence } from '@sveltejs/kit/hooks';

/** @type {import('@sveltejs/kit').Handle} */
async function first({ event, resolve }) {
  console.log('first pre-processing');
  const result = await resolve(event);
  console.log('first post-processing');
  return result;
}

/** @type {import('@sveltejs/kit').Handle} */
async function second({ event, resolve }) {
  console.log('second pre-processing');
  const result = await resolve(event);
  console.log('second post-processing');
  return result;
}

export const handle = sequence(first, second);

위의 예는 다음과 같이 print 된다.

first pre-processing
second pre-processing
second post-processing
first post-processing
function sequence(...handlers: Handle[]): Handle;

@sveltejs/kit/node

node와 유사한 환경의 adapter에서 사용하는 utility들.

getRequest

function getRequest(
  base: string,
  request: import('http').IncomingMessage
): Promise<Request>;

setResponse

function setResponse(
  res: import('http').ServerResponse,
  response: Response
): void;

@sveltejs/kit/node/polyfills

native implementation을 제공하지 않는 환경의 adapter에서 사용하는 fetch 및 관련 interface용 polyfill이다.

installPolyfills

다양한 web API를 전역으로 사용할 수 있도록 한다.

  • crypto
  • fetch
  • Headers
  • Request
  • Response
function installPolyfills(): void;

@sveltejs/kit/vite

sveltekit

SvelteKit Vite plugin을 반환한다.

function sveltekit(): Plugin[];

Service workers

service worker는 app 내에서 network request를 처리하는 proxy server 역할을 한다.
이렇게 하면 app을 offline에서 작동할 수 있지만 offline 지원이 필요하지 않더라도 (또는 구축 중인 app type 때문에 현실적으로 구현할 수 없는 경우에도) service worker를 사용하여 구축된 JS 및 CSS를 prefetch하여 navigation 속도를 높일 수 있다.

SvelteKit에서 src/service-worker.js file (또는 src/service-worker.ts 또는 src/service-worker/index.js 등)이 면 Vite로 빌드되고 자동으로 등록된다.
service worker를 고유한 logic으로 등록해야 하는 경우 자동 등록을 비활성화 할 수 있다.
(예: 사용자에게 업데이트 요청, 정기적 업데이트 구성, workbox 사용 등)

service worker의 위치를 변경하고 프로젝트 구성에서 자동 등록을 비활성화할 수 있다.

service worker 내에서 $service-worker module에 엑세스할 수 있다.

(브라우저는 아직 이 context에서 import 를 지원하지 않기 때문에) bundle로 제공되어야 하고 client-side app의 build manifest에 따라 달라지기 때문에 service worker는 development가 아닌 production guild에서만 작동한다.
로컬에서 테스트하려면 vite preview 를 사용한다.

Anchor options

sveltekit:prefetch

SvelteKit은 코드 분할을 사용하여 app을 작은 chunk로(route당 하나씩) 분할하여 빠른 시작시간을 보장한다.

src/routes/blog/[slug].svelte 예제와 같이 dynamic route의 경우 충분하지 않다.
blog post를 렌더링 하려면 해당 데이터를 가져와야 하며 slug가 무엇인지 알기전까지는 그렇게 할 수 없다.
최악의 경우 브라우저가 서버에서 데이터가 돌아올 때까지 대기할 때 지연이 발생할 수 있다.

데이터를 미리 가져옴으로써 이를 완화할 수 있다.
link에 sveltekit:prefetch attribute를 추가하면…

<a sveltekit:prefetch href="blog/what-is-sveltekit">What is SvelteKit?</a>

click event가 navigation을 trigger 할 때까지 기다리지 않고 사용자가 (데스크톱에서) link 위로 mouse를 가져가거나 (모바일에서) link를 터치하면 SvelteKit이 이 page의 load function을 실행하도록 한다.
일반적으로 이것은 우리에게 몇 백 millisecond를 추가로 소비한다.
이는 lag을 느끼는 user interface와 빠르게 느껴지는 user interface의 차이이다.

router setting이 false 이면 prefetch가 동작하지 않는다.

$app/navigation 에서 프로그래밍 방식으로 prefetch 를 호출할 수도 있다.

sveltekit:reload

기본적으로 SvelteKit runtime은 <a> element에 대한 click을 가로채고 page route 중 하나와 일치하는 상대 (same-origin) URL에 대한 normal browser navigation을 우회한다.
때때로 SvelteKit에 특정 link가 normal browser navigation에 의해 처리되어야 한다고 알려야 한다.
예를 들어 SvelteKit app의 일부가 아닌 domain의 다른 page에 연결하거나 endpoint에 연결하는 경우가 있다.

link에 sveltekit:reload attribute를 추가하면…

<a sveltekit:reload href="path">Path</a>

…link를 클릭하면 브라우저가 전체 page를 다시 load하여 navigate 한다.

rel="external" attribute가 있는 link는 동일한 처리를 받는다.
또한 prerendering 중에는 무시된다.

sveltekit:noscroll

internal link로 이동할 때 SvelteKit는 브라우저의 기본 navigation 동작을 반영한다.
사용자가 page의 맨 위에 있도록 scroll 위치를 0.0으로 변경한다.
(link에 #hash 가 포함되어 있지 않으면 일치하는 ID가 있는 요소로 scroll 된다.)

경우에 따라 이 동작을 비활성화할 수 있다.
link에 sveltekit:noscroll attribute를 추가하면…

<a href="path" sveltekit:noscroll>Path</a>

...link를 클릭한 후 scroll을 방지한다.

Events

SvelteKit는 app이 hydrate 되면 window object에서 sveltekit:start CustomEvent를 emit한다.
아마도 사용할 필요는 없지만 (예를 들어) integration test의 맥락에서 유용할 수 있다.

Adapters

SvelteKit app을 배포하려면 먼저 배포 대상에 맞게 조정해야 한다.
apapter는 빌드된 app을 input으로 사용하고 배포를 위한 output을 생성하는 작은 plugin이다.

기본적으로 프로젝트는 production environment를 감지하고 가능한 경우 적절한 adapter를 선택하는 @sveltejs/adapter-auto 를 사용하도록 구성된다.
platform이 (아직) 지원되지 않는 경우 custom adapter 를 설치하거나 작성해야 할 수 있다.

새로운 environment에 대한 지원 추가 정보는 adapter-auto README를 참조하면 된다.

Supported environments

SvelteKit는 공식적으로 지원되는 여러 adapter를 제공한다.

default adapter인 adapter-auto 를 사용하여 다음 platform에 deploy할 수 있다.

  • adapter-cloudflare 로 Cloudflare Pages
  • adapter-netlify 로 Netlify
  • adapter-vercel 로 Vercel

Node.js

간단한 node server를 생성하려면 @sveltejs/adapter-node package를 설치하고 svelte.config.js 를 업데이트 한다.

import adapter from '@sveltejs/adapter-node';

이를 통해 vite buildbuild directory 내부에 자체 포함된 node app을 생성한다.
custom output directory 같은 옵션을 adapter에 전달할 수 있다.

import adapter from '@sveltejs/adapter-node';

export default {
  kit: {
        adapter: adapter({ out: 'my-output-directory' })
  }
};

####Static sites

대부분의 adapter는 site의 prerendering 가능한 page에 대해 static HTML을 생성한다.
경우에 따라 전체 app을 prerendering 할 수 있다.
이 경우 @sveltejs/adapter-static 을 사용하여 모든 page에 대한 static HTML을 생성할 수 있다.
fully static site는 GitHub Pages와 같은 static host를 포함하여 다양한 platform에서 호스팅 될 수 있다.

import adapter from '@sveltejs/adapter-static';

또한 adapter-static 을 사용하여 fallback page를 지정하여 single-page apps (SPAs)을 생성할 수 있다.

trailingSlash가 환경에 적절하게 설정되어 있는지 확인해야 한다.
host가 /a 에 대한 요청을 받았을 때 /a.html 을 렌더링하지 않으면 대신 /a/index.html 을 생성하기 위해 trailingSlash: 'always' 를 설정해야 한다.

Platform-specific context

일부 adapter는 request에 대한 추가 정보에 엑세스할 수 있다.
예를 들어 Cloudflare worker는 KV namespace 등이 포함된 env object에 엑세스할 수 있다.
이것은 platform property로 hook 및 endpoint에서 사용되는 RequestEvent 에 전달할 수 있다.
자세한 내용은 각 adapter의 documentation을 참조하면 된다.

Community adapters

다른 platform을 위한 추가 community-provided adapter가 있다.
package manager로 관련 adapter를 설치한 후 svelte.config.js 를 업데이트 하면 된다.

import adapter from 'svelte-adapter-[x]';

Writing custom adapters

사용하려는 것과 유사한 platform에 대한 adapter source를 보고 starting point로 복사하는 것이 좋다.

adapter package는 adapter를 생성하는 다음 API를 구현해야 한다.

/** @param {AdapterSpecificOptions} options */
export default function (options) {
  /** @type {import('@sveltejs/kit').Adapter} */
  const adapter = {
    name: 'adapter-package-name',
    async adapt(builder) {
      // adapter implementation
    }
  };

  return adapter;
}

Adapter type및 해당 parameter는 types/config.d.ts에서 사용할 수 있다.

adapt method 내에는 adapter가 수행해야 하는 여러 가지 작업이 있다.

  • build directory 지우기
  • builder.writeClient , builder.writePrerendered , builder.writeServerbuilder.writeStatic 을 사용하여 SvelteKit output 작성
  • ouput code
    • ${builder.getServerDirectory()}/index.js 에서 Server import
    • builder.generateManifest({ relativePath }) 로 생성된 manifest를 사용하여 app을 instance화
    • platform의 request를 listen 하고 필요한 경우 standard request로 변환하고 server.respond(request, { getClientAddress }) function을 호출하여 response를 생성하고 이에 응답
    • server.respond 에 전달된 platform option을 통해 platform별 정보를 SvelteKit에 노출
    • 필요한 경우 대상 platform에서 작동하도록 globally shims fetch 를 수행
      SvelteKit은 node-fetch 를 사용할 수 있는 platform에 @sveltejs/kit/install-fetch helper를 제공함
    • 필요한 경우 대상 platform의 dependencies를 설치할 필요가 없도록 output을 bundle로 묶는다.
    • 사용자가 static file과 생성된 JS/CSS를 대상 platform의 올바른 위치에 둔다.

가능한 경우 .svelte-kit/[adapter-name] 아래에 중간 output을 두고 build/ directory 아래에 adapter output을 두는 것이 좋다.

adapter API는 1.0전에 변경될 수도 있다.

Page options

기본적으로 SvelteKit는 서버에서 먼저 모든 component를 렌더링하고 HTML로 client에 보낸다.
그런 다음 브라우저에서 component를 다시 렌더링하여 hydration이라는 프로세스에서 interactive로 만든다.
이러한 이유로 component가 두 위치에서 모두 실행될 수 있는지 확인해야 한다.
그런 다음 SvelteKit는 subsequent navigation을 가지는 router를 초기화한다.

app별 또는 page 별로 이들 각각을 제어할 수 있다.
각 page별 설정은 context="module" 을 사용하여 layout이 아닌 페이지에만 적용된다.

둘 다 지정하면 충돌 시 page별 설정이 app별 설정보다 우선 적용된다.

router

SvelteKit에는 navigation(사용자가 링크를 클릭하거나 뒤로/앞으로 버튼과 상호작용)을 가로채고 브라우저가 reload하여 navigation을 처리하도록 하는 대신 page 내용을 업데이트 하는 client-side router가 포함되어 있다.

특정 상황에서는 app-wide browser.router config option 또는 page-level router export를 사용하여 client-side routing을 비활성화해야 할 수 있다.

<script context="module">
  export const router = false;
</script>

router가 이미 active 상태인지 여부에 관계없이 이 page의 navigation에 대해 client-side routing이 비활성화된다.

hydrate

일반적으로 SvelteKit는 서버에서 렌더링된 HTML을 interactive page로 hydrate 한다.
일부 page에는 JavaScript가 전혀 필요하지 않다.
많은 블로그 게시물과 '정보' 페이지가 이 범주에 속한다.
이러한 경우 app-wide browser.hydrate config option또는 page 수준의 hydrate export를 사용하여 app이 boot up 할 때 hydrate를 건너뛸 수 있다.

<script context="module">
  export const hydrate = false;
</script>

hydrate와 router가 모두 false이면 SvelteKit은 page에 JavaScript를 전혀 추가하지 않는다.
handle에서 server-side rendering이 비활성화 된 경우 hydrate는 true여야 하며 그렇지 않으면 content가 rendering되지 않는다.

prerender

최소한 app의 일부 page는 빌드시 생성된 간단한 HTML 파일로 표시될 수 있다.
이러한 page는 미리 렌더링할 수 있다.

prerender annotation이 있는 모든 page에 대해 prerendering이 자동으로 발생한다.

<script context="module">
  export const prerender = true;
</script>

또는 config.kit.prerender.defaulttrue로 설정하고 prerendering 불가로 명시적으로 표시된 page를 제외한 모든 것을 prerendering 할 수 있다.

script context="module">
  export const prerender = false;
</script>

전체 app이 prerendering에 적합한 경우 adapter-static을 사용할 수 있다.
그러면 모든 static webserver에서 사용하기에 적합한 파일이 출력된다.

reprenderer는 app의 root에서 시작하여 찾은 prerendering 가능한 page에 대한 HTML을 생성한다.
각 page는 prerendering 후보인 다른 page를 가리키는 <a> element를 검색한다.
이 때문에 일반적으로 엑세스 해야 하는 page를 지정할 필요가 없다.
prerenderer가 엑세스해야 하는 page를 지정해야 하는 경우 prerender configuration의 entries option 사용하여 지정할 수 있다.

when not to prerender

기본 규칙은 다음과 같다.
page를 prerendering 할 수 있으려며 page를 직접 방문하는 두명의 사용자가 server에서 동일한 contents를 가져와야한다.

모든 page가 prerendering에 적합한 것은 아니다.
rerendering된 모든 contents는 모든 사용자에게 표시된다.
물론 prerendering 된 page의 onMount에서 개인화된 data를 가져올 수 있지만 빈 초기 content 또는 load indicator가 포함되므로 사용자 경험이 저하될 수 있다.

이전의 src/routes/blog/[slug].svelte 예제와 같이 page parameter를 기반으로 data를 로드하는 page를 prerendering 할 수 있다.
prerenderer는 load 내부에서 이루어진 request를 가로채므로 src/routes/blog/[slug].json.js 에서 제공되는 data도 capture 된다.

prerendering 중에 url.searchParams에 엑세스하는 것은 금지되어 있다.
사용해야 하는 경우 browser (예: onMount)에서만 사용해야 한다.

Route conflicts

prerendering은 filesystem에 쓰기 때문에 directory와 file이 같은 이름을 갖도록 하는 두 개의 endpoint를 가질 수 없다.
예를 들어 src/routes/foo/index.jssrc/routes/foo/bar.jsfoofoo/bar를 생성하려고 시도하지만 이는 불가능하다.

이러한 이유로 항상 file extension을 포함하는 것이 좋다.
/src/routes/foo/index.json.jssrc/routes/foo/bar.json.jsfoo.jsonfoo/bar.json이 된다.
json 파일이 조화롭게 나란히 존재한다.

page의 경우 foo 대신 foo/index.html을 작성하여 이 문제를 우회한다.

packaging

svelte-kit package는 현재 실험적인 기능이며 semantic versioning 규칙이 적용되지 않는다.
이전 버전과 호환되지 않는 변경 사항 향후 release에서 발생할 수 있다.

SvelteKit을 사용하여 component와 app을 빌드할 수 있다.

app을 만들 때 src/routes의 내용은 공개 대상이다.
src/lib에는 app의 내부 라이브러리가 포함되어 있다.

SvelteKit component library는 src/lib이 공개 비트라는 점을 제외하고 SveltKit app과 구조가 정확히 동일하다.
src/routes는 library와 함께 제공되는 document 또는 demo site일 수 있고 개발 중에 사용하는 sandbox일 수도 있다.

svelte-kit package를 실행하면 src/lib의 내용을 가져와 다음을 포함하는 (configure 가능한) package directory를 생성한다.

  • custom include/exclude option을 구성하지 않는 한 src/lib의 모든 파일.
    component는 사전 처리되고 TypeScript 파일은 JavaScript로 변환된다.
  • Svelte, JavaScript 및 TypeScript file에 대해 생성되는 Type definition (d.ts file)
    이를 위해 typescript >= 4.0.0svelte2tsx >= 0.4.1을 설치해야 한다.
    type definition은 implementation 옆에 배치되고 직접 작성한 d.ts file은 그대로 복사된다.
    생성을 비성화 할 수 있지만 권장하지 않는다.
  • "script" field를 제외한 모든 field가 있는 project root에서 복사한 package.json
    "dependencies" field가 포함되어 있다.
    즉 document 또는 demo site 에만 필요한 package를 "devDependencies"에 추가해야 한다.
    "type": "module""exports" field가 원본 파일에 정의되지 않은 경우 추가된다.

"export" field에는 package의 entry point가 포함된다.
기본적으로 src/lib에 있는 모든 파일은 underscore로 시작하지 않는 한 (또는 underscore로 시작하는 디렉토리에 있지 않는 한) entry point로 처리되지만 이 동작을 구성할 수 있다.
src/lib/index.js 또는 src/lib/index.svelte 파일이 있는 경우 package root로 처리된다.

예를 들어 src/lib/Foo.svelte component와 이를 re-export한 src/lib/index.js module이 있는 경우 library consumer는 다음 중 하나를 수행할 수 있다.

import { Foo } from 'your-library';
import Foo from 'your-library/Foo.svelte';

Publishing

생성된 package를 publish하려면

npm publish ./package

위의 ./package는 생성된 디렉토리 이름을 참조하므로 custom package.dir을 구성하는 경우 그에 따라 변경한다.

Caveats

이것은 비교적 실험적인 기능이며 아직 완전히 구현되지 않았다.
Svelte 파일 (사전처리됨) 및 TypeScript 파일 (JavaScript로 변환됨)을 제외한 모든 파일은 있는 그대로 복사된다.

Command Line Interface

SvelteKit project는 Vite를 사용한다.
즉, 대부분의 경우 해당 CLI를 사용한다. (npm run dev/build/preview script를 통해서지만)

  • vite dev - development server 시작
  • vite build - app의 production version 빌드
  • vite preview - production version을 로컬에서 실행

그러나 SvelteKit에는 배포 가능한 package를 만들고 초기화하기 위한 자체 CLI가 포함되어 있다.

svelte-kit package

svelte-kit package는 현재 실험적인 기능이며 Semantic Versioning 규칙이 적용되지 않는다.
이전 버전과 호환되지 않는 변경 사항은 향후 release에서 발생할 수 있다.

packaging을 참조하세요. svelte-kit package는 다음 option을 허용한다.

  • -w / --watch - src/lib의 파일에서 변경 사항을 확인하고 package를 다시 빌드한다.

svelte-kit sync

svelte-kit sync는 type 및 tsconfig.json과 같은 project에 대해 생성된 파일을 만든다.
새 프로젝트를 만들면 prepare script로 목록화되고 npm lifecycle의 일부로 자동으로 실행되므로 일반적으로 이 명령을 실행할 필요가 없다.

Configuration

project configuration은 svelte.config.js 파일에 있다.
모든 값은 optional이다.
default 값이 있는 전체 option list는 다음과 같다.

svelte.config.js

/** @type {import('@sveltejs/kit').Config} */
const config = {
  // options passed to svelte.compile (https://svelte.dev/docs#compile-time-svelte-compile)
  compilerOptions: {},

  // an array of file extensions that should be treated as Svelte components
  extensions: ['.svelte'],

  kit: {
    adapter: undefined,
    alias: {},
    appDir: '_app',
    browser: {
      hydrate: true,
      router: true
    },
    csp: {
      mode: 'auto',
      directives: {
        'default-src': undefined
        // ...
      }
    },
    moduleExtensions: ['.js', '.ts'],
    files: {
      assets: 'static',
      hooks: 'src/hooks',
      lib: 'src/lib',
      params: 'src/params',
      routes: 'src/routes',
      serviceWorker: 'src/service-worker',
      template: 'src/app.html'
    },
    inlineStyleThreshold: 0,
    methodOverride: {
      parameter: '_method',
      allowed: []
    },
    outDir: '.svelte-kit',
    package: {
      dir: 'package',
      emitTypes: true,
      // excludes all .d.ts and files starting with _ as the name
      exports: (filepath) => !/^_|\/_|\.d\.ts$/.test(filepath),
      files: () => true
    },
    paths: {
      assets: '',
      base: ''
    },
    prerender: {
      concurrency: 1,
      crawl: true,
      default: false,
      enabled: true,
      entries: ['*'],
      onError: 'fail',
      origin: 'http://sveltekit-prerender'
    },
    routes: (filepath) => !/(?:(?:^_|\/_)|(?:^\.|\/\.)(?!well-known))/.test(filepath),
    serviceWorker: {
      register: true,
      files: (filepath) => !/\.DS_Store/.test(filepath)
    },
    trailingSlash: 'never',
    version: {
      name: Date.now().toString(),
      pollInterval: 0
    }
  },

  // options passed to svelte.preprocess (https://svelte.dev/docs#compile-time-svelte-preprocess)
  preprocess: null
};

export default config;

adapter

vite build를 실행할 때 실행하고 출력이 다른 platform에 대해 변환되는 방식을 결정한다.
adapter 문서를 참조하면 된다.

alias

import문의 값을 바꾸는 데 사용되는 0 개 이상의 alias를 포함하는 object이다.
이러한 alias는 Vite 및 TypeScript에 자동으로 전달된다.

예를 들어 다음과 같이 componentutils folder alias를 추가할 수 있다.

svelte.config.js :

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    alias: {
      $components: 'src/components',
      $utils: 'src/utils'
    }
  }
};

built-in $lib alias는 packaging에 사용되므로 config.kit.files.lib에 의해 제어된다.

appDir

빌드된 JS 및 CSS (및 imported assets)가 제공되는 path.assets에 대한 relative direcotry이다.
(파일 이름에는 content-based hash가 포함되어 있어 무기한 캐시될 수 잇다.)
/로 시작하거나 끝나서는 안된다.

browser

다음 boolean value 중 0개 이상을 포함하는 object

  • hydrate - client-side app으로 server rendering HTML을 hydrate 할지 여부.
    (app 전체에서 이것을 false로 설정하는 경우는 드물다.)
  • router - client-side router app 전체를 활성화하거나 비활성화

csp

다음 값 중 0개 이상을 포함하는 object

  • mode - 'hash', 'nonce' 또는 'auto'
  • directives - [directive]: value[] object 쌍
  • reportOnly - CSP report-only mode에 대한 [directive]: value[] object 쌍

Content Security Policy configuration.
CSP resource를 로드할 수 있는 위치를 제한하여 cross-site scripting (XSS) 공격으로부터 사용자를 보호하는데 도움이 된다.
예를 들어 아래와 같이 구성하면…

svelte.config.js :

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    csp: {
      directives: {
        'script-src': ['self']
      },
      reportOnly: {
        'script-src': ['self']
      }
    }
  }
};

export default config;

...외부 사이트에서 script loading을 방지한다.
SvelteKit는 생성하는 모든 inline style 및 script에 대해 (mode로 지정된) nonce 또는 hash 로 지정된 지시문을 보강한다.

page가 prerendering 되면 CSP header가 <meta http-equiv> tag를 통해 추가된다.
(이 경우 frame-ancestors, report-urisandbox 지시문은 무시된다.)

mode'auto' 면 SvelteKit은 동적으로 rendering된 page를 nonces로 사용하고 미리 prerendering된 page에 hash를 사용한다.
reprendering 된 page에 nonce를 사용하는 것은 안전하지 않으므로 금지된다.

대부분의 Svelte transition은 inline <style> element를 생성하여 작동한다.
app에서 이를 사용하는 경우 style-src 지시문을 지정하지 않은 상태로 두거나 unsafe-inline을 추가해야 한다.

modeExtensions

SvelteKit이 module로 취급할 파일 확장자의 배열이다.
config.extension 또는 config.kit.moduleExtensions와 일치하지 않는 확장자를 가진 파일은 router에서 무시된다.

files

다음 string value 중 0 개 이상을 포함하는 object

  • assets - favicon.ico 또는 manifest.json과 같이 stable URL을 갖고 처리를 거치지 않아야 하는 static file을 넣을 장소
  • hooks - hook module의 위치
  • lib - codebase 전체에서 $lib로 엑세스 할 수 있는 app의 내부 library
  • params - parameter matcher를 포함하는 디렉토리
  • routes - app의 구조를 정의하는 파일
  • serviceWorker - service worker의 entry point 위치
  • template - HTML response를 위한 template의 위치

inlineStyleThreshold

HTML head의 <style> block 내부에 CSS를 inline 한다.
이 옵션은 inline 할 CSS 파일의 최대 길이를 지정하는 숫자이다.
page에 필요하고 이 값보다 작은 모든 CSS 파일은 <style> block에 병합되고 inline된다.

그 결과 초기 요청이 줄어들고 First Contentful Paint score가 향상될 수 있다.
그러나 더 큰 HTML output을 생성하고 browser cache의 효율성을 감소시킨다.
사용하는 것이 좋다.

methodOverride

HTTP Method Override를 참고.
다음 중 0 개 이상을 포함하는 object

  • parameter - 의도한 method value를 전달하는데 사용할 query parameter 이름
  • allowed - 원래 request method를 override 할 때 사용할 수 있는 HTTP method array

outDir

SvelteKit이 devbuild 중에 파일을 쓰는 디렉토리이다.
version control에서 이 디렉토리를 제외해야 한다.

package

package 생성과 관련된 옵션

  • dir - output directory
  • emitTypes - 기본적으로 svelte-kit package.d.ts 파일 형식으로 package type을 자동으로 생성한다.
    type 생성은 configurable하지만 ecosystem quality를 위해서는 항상 type을 생성하는 것이 좋다.
    false로 설정할 때 타당한 이유가 있는지 확인해야 한다. (예를 들어 handwritten type definition을 제공하려는 경우)
  • exports - (filepath: string) => boolean type의 function.
    true이면 filepath가 package.jsonexport field에 포함된다.
    package.json source의 기존 값은 우선적으로 원래 export field의 값과 병합된다.
  • files - (filepath: string) => boolean type의 function.
    true인 경우 file이 처리되고 dir에 지정된 최종 output folder로 복사된다.

advanced filepath matching을 위해 globbing library와 함께 exportfile option을 사용할 수 있다.

svelte.config.js :

import mm from 'micromatch';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    package: {
      exports: (filepath) => {
        if (filepath.endsWith('.d.ts')) return false;
        return mm.isMatch(filepath, ['!**/_*', '!**/internal/**']);
      },
      files: mm.matcher('!**/build.*')
    }
  }
};

export default config;

paths

다음 string 값 중 0개 이상을 포함하는 object

  • assets - app의 파일이 제공되는 absolute path.
    이는 파일이 일종의 storage bucket에서 제공되는 경우에 유용하다.
  • base - 빈 문자열이 아닌 한 시작해야 하지만 / 로 끝나지 않는 root-relative path (예: /base-path).
    이는 app이 제공는 위치를 지정하고 app이 root가 아닌 경로에 있도록 허용한다.

prerender

다음 중 0개 이상을 포함하는 object

  • concurrency - 동시에 prerendering할 수 있는 page 수.
    JS는 single-thread이지만 prerendering 성능이 network에 종속된 경우 (예: 원격 CMS에서 content load) network response를 기다리는 동안 다른 작업을 처리하여 속도를 높일 수 있다.
  • crawl - SvelteKit이 seed page의 link를 따라 prerendering 할 page를 찾아야 하는지 여부를 결정한다.
  • default - 포함하지 않은 page를 prerendering하려면 true로 설정한다.
    export const prerender = false
  • enabled - prerendering을 완전히 비활성화 하려면 false로 설정한다.
  • entries - prerendering하거나 crowling을 시작할 page의 배열 (crawl: true인 경우).
    * string은 모든 non-dynamic routes (즉, [parameters]가 없는 page)를 포함다.
  • onError
    • 'fail' - (기본값) link를 따라갈 때 routing error가 발생하면 build에 실패한다.
    • 'continue' - routing error가 발생해도 빌드를 계속한다.
    • function - crawling 세부 정보를 기반으로 빌드를 기록, throw 및 실패하거나 선택한 다른 작업을 수행할 수 있는 custom error handler
import adapter from '@sveltejs/adapter-static';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    adapter: adapter(),
    prerender: {
      onError: ({ status, path, referrer, referenceType }) => {
        if (path.startsWith('/blog')) throw new Error('Missing a blog page!');
        console.warn(
          `${status} ${path}${referrer ? ` (${referenceType} from ${referrer})` : ''}`
        );
      }
    }
  }
};

export default config;
  • origin - prerendering 중 url.origin의 값.
    렌더링된 content에 포함된 경우 유용하다.

routes

어떤 파일이 경로를 생성하고 어떤 파일이 private module로 취급되는지를 결정하는 (filepath => string) => boolean function.

serviceWorker

다음 값 중 0 개 이상을 포함하는 object

  • register - false로 설정하면 automatic service worker 등록이 비활성화 됨
  • files - (filepath: string) => boolean type의 function
    true면 지정된 파일을 $service-worker.files에서 사용할 수 있으며 그렇지 않으면 제외됨

trailingSlash

URL을 확인할 때 후행(trailing) slash를 제거, 추가 또는 무시할 지 여부
(이는 endpoint가 아니니 page에만 적용됨)

  • 'never' - /x//x 로 redirect
  • 'always' - /x/x/ 로 redirect
  • 'ignore' - 후행 slash를 자동으로 추가하거나 제거하지 않음.
    /x/x/는 동등하게 처리됨

이 옵션은 prerendering에도 영향을 준다.
trailingSlahalways이면 /about과 같은 route는 about/index.html 파일을 생성하고 그렇지 않으면 static webserver convention을 미러링하는 about.html을 생성한다.

후행 slash를 ignore 하는 것은 권장되지 않는다.
relative path의 의미는 두 경우 (/x./y/y 이지만 /x/ 에선 /x/y)가 다르며 /x/x/ 는 SEO에 유해한 별도의 URL로 취급된다.
이 옵션을 사용하는 경우 handle function 내부의 request.path에서 후행 slash를 조건부로 추가하거나 제거하는 logic을 구현해야 한다.

version

다음 값 중 0개 이상을 포함하는 개체:

  • name - 현재 app version 문자열
  • pollInterval - version 변경을 polling 하는 간격 (milliseconds)

사람들이 사용하는 동안 새 버전의 app을 배포하면 client-size navigation에 버그가 있을 수 있다.
새 page의 code가 이미 load된 경우 오래된 content가 있을 수 있다.
그렇지 않은 경우 app의 route manifest가 더 이상 존재하지 않는 JavaScript file을 가리킬 수 있다.
SvelteKit은 여기에 지정된 이름(기본적으로 빌드의 timestamp)을 사용하여 새 버전이 배포되었음을 감지한 후 기존 full-page navigation으로 falling back하여 이 문제를 해결한다.

pollInterval이 0이 아닌 값으로 설정하면 SvelteKit은 background에서 새 버전을 polling화고 업데이트 된 저장소를 감지하면 updated store의 값을 true로 설정한다.

반응형
profile

파란하늘의 지식창고

@Bluesky_

내용이 유익했다면 광고 배너를 클릭 해주세요