• 참고
/* svelte */
export let myprop; // 콤포넌트 외부로부터 프로퍼티를 가져올 수 있음
let myvar; // 콤포넌트 내에서 반응적인 변수를 만들 수 있음

SAPPER 특징

  • server-side rendering, code-splitting, scoped styling, declarative routing and live reloading with hot-module replacement
  • PWA 지원
  • src/routes 디렉토리에 .svelte파일을 추가하여 검색엔진에 최적화(SEO)된 페이지(SSR)를 작성할 수 있다.
  • src/routes 디렉토리에 .js파일을 추가하여 서버 라우트(JSON API)를 작성할 수 있다.
  • 파일 이름에 경로매개변수 및 정규식 사용 가능
  • 서비스워커
  • 링크는 콤포넌트가 아닌 엘리먼트

기본 폴더 구조

  • ‘src/client.js’ : @sapper/app 모듈의 start 함수를 실행

  • src/server.js (Express,Polka앱) : static 폴더서브, sapper.middleware 실행, process.env.PORT 리스닝

  • src/service-worker.js : 서비스 워커는 프록시 서버처럼 동작. 파일 캐시, 오프라인 작업 등과 관련. Sapper가 서비스 워커를 알아서 컨트롤하지는 않고, 개발자가 service-worker.js 파일에 로직을 작성해야 함. @sapper/service-worker 에서 다음을 임포트할 수 있음 - static 폴더의 files 배열, 번들러가 생성한 shell, 페이지 요청 관련 routes, 서비스워커가 생성된 timestamp(useful for generating unique cache names)

  • static : 폰트, 이미지 등 파일. /favicon.png 로 요청하면 static/favicon.png 파일을 응답. Sapper가 관리하는 것은 아니지만(보통 sirv 등을 사용) 폴더의 파일 목록을 읽어올 수는 있으므로 쉽게 캐시 매니페스트를 생성할 수 있음(service-worker.js)

  • src/template.html : 서버 응답 템플릿. %sapper.base% 등을 Sapper가 inject 해줌

  • src/routes/_error.svelte : 에러가 발생하면 보여지는 스페셜 페이지. 페이지 내에서 error 객체(http status code)에 접근 가능.

  • src/routes : 페이지서버라우트 작성

    • 페이지 : .svelte 파일로 작성. 서버 렌더링 페이지를 보여준 후 클라이언트에서 goto를 사용하여 페이지 간 이동 처리.
    • 서버 라우트 : .js로 작성하며, HTTP 메소드에 대응하는 함수(get, post, del 등)로 이루어짐. 각 함수는 request, response 객체와, next 함수를 인수로 받음. JSON API를 만드는 데 유용.

파일 이름 규칙

  • / : src/routes/index.svelte
  • /about : src/routes/about.svelte 또는 src/routes/about/index.svelte
  • /blog/what-is-sapper : src/routes/blog/[slug].svelte 또는 src/routes/blog.[slug].svelte
    • page.params.slug == ‘what-is-sapper’
  • /folder/file : src/routes/[myfolder]/[myfile].svelte
    • page.params.myfolder == ‘folder’
    • page.params.myfile == ‘file’
  • /myspread/2020/what-is-sapper : src/routes/myspread/[...myparams].svelte
    • page.params.myparams[0] = ‘2020’
    • page.params.myparams[1] = ‘what-is-sapper’
  • 파일 또는 폴더명이 _로 시작하면 라우트를 만들지 않음 : src/routes/_helpers/datetime.js

  • 정규식 : src/routes/items/[id([0-9]+)].svelte, 사용불가문자: / \ ? : ( )

클라이언트 API

  • start({ target })

  • goto(href, options?)

  • prefetch(href)

  • prefetchRoutes (라우트?)

레이아웃

  • src/routes/_layout.svelte : 모든 페이지에 적용되는 공통 레이아웃
  • src/routes/settings/_layout.svelte : 서브 페이지에만 적용. 부모 레이아웃이 우선 적용된다. (서브 레이아웃의 preload는 적용되지 않는 듯…)
  • export let segment : 현재 URL의 경로를 나타내는 프로퍼티
  • <slot></slot> : 본문을 삽입할 곳
<script>
    export let segment;
</script>
<div class="submenu">
    <a class:selected={segment === "profile"} href="settings/profile">Profile</a>
    <a class:selected={segment === "status"} href="settings/status">Status</a>
</div>
<slot></slot>
  • 만약 서브 페이지에 부모 레이아웃을 적용하지 않고 서브 레이아웃만 적용하고 싶다면 다음과 같이 해보자.
{#if segment != 'mysub'}
	... 부모 레이아웃 내용 ...
	<slot></slot>
	... 부모 레이아웃 내용 ...
{:else}
	<slot></slot>
{/if}

페이지

페이지는 svelte 콤포넌트이다. 페이지 간의 전환은 클라이언트에서 처리된다. 단, 최초 방문 시에는 서버 렌더 + 클라이언트 라우터 초기화 등의 일이 일어난다는 듯하다.

페이지는 <script context="module"><script> 블록을 갖는데, 이들 코드가 언제 실행되는지 다음과 같은 시나리오를 통해 알아보자.

  • / 페이지에 방문 : 서버에서 <script><script context="module"> 가 실행되고, 클라이언트에서 <script> 가 실행된다.
  • / 페이지에서 /2020/05/11/sapper 링크에 마우스호버 : 클라이언트에서 <script context="module"> 의 preload 함수가 실행된다. (아래에서 별도로 다룸)
  • / 페이지에서 /2020/05/11/sapper 링크를 클릭 : 클라이언트 라우터에 의한 페이지 전환이 일어나 클라이언트에서 <script> 블록이 실행된다.
  • /2020/05/11/sapper 페이지에서 /2020/04/27/tailwindcss 링크에 마우스호버 : 페이지 전환은 일어나지 않지만, 클라이언트에서 <script context="module"> 블록의 preload 함수가 실행된다.
  • /2020/05/11/sapper 페이지에서 /2020/04/27/tailwindcss 링크를 클릭 : 아무런 블럭도 실행되지 않는다. (이미 preload한 상태)

요약하면, 스크립트는 최초 방문 시에만 서버와 클라이언트에서 실행되고, 이후에는 클라이언트에서 실행되는데, 페이지를 이동하면 <script> 블록이, 같은 페이지에서 다른 글을 조회하면 <script context="module"> 블록(preload)이 실행된다.

Preload

  • 페이지context="module" 스크립트에 선언하고, 콤포넌트 생성 전에 실행된다.
  • <a rel=prefetch> : 페이지 링크에 마우스호버나 터치 이벤트가 발생하는 즉시 해당 페이지의 preload 함수를 실행하여 반응 시간을 단축할 수 있다.
  • preload는 클라이언트 또는 서버에서 실행될 수 있다. console.log()로 확인해보자.
    • /2020/05/11/sapper 페이지 링크에 마우스를 올렸을 때 : 클라이언트(브라우저 콘솔)에만 출력된다.
    • /2020/05/11/sapper 페이지에 직접 방문했을 때 : 서버(cmd창)에만 출력된다.
  • preload는 2개의 인자를 받아 URL 파라미터, 세션 등에 접근할 수 있으며, 내부적으로 fetch 등 3개의 메소드를 사용할 수 있다. 또한 반환값을 갖는다.
    • 인자
      • page : URL 파라미터 등에 접근하기 위한 인자. { host, path, params, query }
      • session : 세션에 접근하기 위한 인자. 세션은 server.js의 sapper.middleware에 전달된 session 옵션에 의해 서버에서 생성된다.
    • 메소드
      • this.fetch(url, options) : 서버측에서 제약없이 fetch를 하기 위한 메소드인 듯
      • this.error(statusCode, error) : 잘못된 URL을 fetch한 경우 등에는 에러 페이지로…
      • this.redirect(statusCode, location) : 렌더링을 취소하고 다른 페이지로…
      • 참고로 preload 내에서 console.log(this)를 출력해보면 { fetch:f, error:f, redirect:f } 가 출력된다.
    • 반환값
      • preload 에서 Promise를 리턴하면 페이지 렌더링이 딜레이된다고 한다.
export async function preload({ params }, session) {
	const res = await this.fetch(`secret-data.json`, {
		credentials: 'include'
	});
	if (res.status === 200) {
		this.redirect(302, 'login');
	}
	this.error(404, 'Not found');
}

Stores

  • 페이지의 preload 함수에서 사용하는 pagesession 값을 페이지의 나머지 부분(콤포넌트)에서도 접근하려면 stores를 사용. svelte store와 마찬가지로 $page 등으로 접근.
<script>
	import { stores } from '@sapper/app';
	const { preloading, page, session } = stores();
</script>
{#if $session.user}
	마이페이지
{:else}
	로그인
{/if}

서드파티 라이브러리

  • window 등을 참조하는 라이브러리의 경우 서버 측에서 라이브러리 임포트를 하면 오류 발생(서버에서는 window를 참조할 수 없으니…). 해결은 onMount를 사용한 임포트. (참고)
import { onMount } from 'svelte';
onMount(async () => {
	const module = await import('my-non-ssr-component');
});
  • onMount 만으로 해결되지 않는 특수한 경우에는 setTimeout(function(){}, 0)으로 해결되는 경우가 있다.

그 밖에

  • 빌드 : 프로덕션 - sapper build
  • 익스포트 : 스태틱 사이트 내보내기? - sapper export
  • 배포
  • 보안
  • 기본URL :
  • 테스팅 : cypress 등
  • 디버깅 : 서버 코드 디버깅 - ndb 등

참고

  • https://sapper.svelte.dev/docs#Making_a_component_SSR_compatible