Dokku를 이용한 Sveltekit 배포 스크립트

이때껏 Sveltekit로 간단한 사이트를 몇 개 만들었다. 매번 수동으로 Dokku를 설정하는 것이 번거로워서 이 글에 설정법을 간단히 정리한다.

이 글은 adapter-node 만을 다룬다. adapter-node의 기본 설정 방법은 https://svelte.dev/docs/kit/adapter-node 에서 확인할 수 있다.

다른 어뎁터로 빌드했다면 적용되지 않는다.

Dokku에 프로젝트를 올리면 herokuish가 package-lock.jsonpackage.json 를 확인하여 npm 프로젝트로 인식한다. 그리고 알아서 nodejs 빌드팩으로 프로젝트를 빌드한다. 우리는 빌드 설정 및 실행만 잡아주면 된다.

우선 adapter-node를 설치한다

npm i -D @sveltejs/adapter-node

svelte.config.jsadapter-node를 연결하고, build output 경로를 설정한다. 최상단의 import adapter 와 kit → adapter 아래의 out 필드를 수정하자.

import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

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

        kit: {
                // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
                // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
                // See https://svelte.dev/docs/kit/adapters for more information about adapters.
                adapter: adapter({out: 'build'})
        }
};

export default config;

이제 npm run build를 실행하면 build 폴더 아래에 결과물이 나온다. Dokku가 이것을 실행하게 해야한다.

루트 디렉터리에 Procfile을 만들고, 아래와 같이 내용을 설정한다.

web: node build

이제 [Sveltekit 빌드 + 알아서 실행] 까지 된다. 하지만 POST 요청시 Cross-site POST form submissions are forbidden 라는 오류가 발생한다. 그러므로, 우리는 Origin 설정을 해야한다. 쉘에서 아래 명령어를 실행한다.

dokku config:set 프로젝트명 ORIGIN=사이트의_http(s)주소

# 예시: dokku config:set samgukji-front ORIGIN=https://samgukji.esm.kr

이렇게만 하면 알아서 Sveltekit이 빌드 및 실행된다.

Cross-site POST form submissions are forbidden 문제

결론적으로 ORIGIN 값을 “브라우저가 보는 사이트 주소”로 설정하면 된다. 사실 이것 때문에 몇시간을 삽질했다.

필자는 다음과 같은 서버 구조를 가지고 있다:

  • 사용자는 https://app.esm.kr로 접속한다.
  • 로드벨런서는 주소 경로에 따라서 http://app-svelte.inner.esm.krhttp://app-backend.inner.esm.kr 로 요청을 나눈다. (경로가 /api 로 시작하면 API 서버로 요청을 넘긴다.)
  • 각 주소에 따라 적절한 VM에 연결이 넘어간다.
  • 각 VM에서 Dokku를 통해 설치된 nginx를 거쳐 내부 컨테이너로 HTTP 요청을 넘긴다.
  • 로드벨런서 까지는 https를 사용하나, 내부망 통신은 전부 http를 사용한다.

Cross-site POST form submissions are forbidden 오류는 Sveltekit 서버 프로그램이 인식한 도메인(주소)과 실제 받은 요청의 도메인이 다르면 발생한다. 어느 방식으로 인식하는지 몰라서 (1) 로드 벨런서의 Nginx에서 Http Host 헤더도 수정해 봤고, (2) 내부 도메인도 app.esm.kr로 바꾸어봤다. (3) HTTP_PROTO_FOR 등도 설정하여 프로토콜 설정등도 해보았다. 그 외에도 여러 삽질을 했으나 번번히 실패했다.

이 문제는 브라우저가 HTTP 헤더에 담긴 Origin 값과 환경 변수의 들어간 ORIGIN 설정 값과 일치해야 해결된다. 프로토콜(https)도 맞춰줘야 한다. 내부망에서 http를 쓰든, https를 쓰든 상관 없다. 사용자가 https://samgukji.esm.kr로 접속한다면 ORIGIN=https://samgukji.esm.kr 로 설정하고, http로 접속한다면 ORIGIN=http://samgukji.esm.kr 로 설정한다.

몇가지 실험을 해 보니까, HTTP 헤더의 Host값 하고는 영 관련이 없어 보인다. HTTP 헤더의 Origin 값을 중점적으로 체크하자.

체크 하는 방법?

sveltekit에 들어오는 모든 요청은 /src/hook.server.jshandle 함수를 거치게 된다. 별다른 수가 없다면, 여기서 console.log를 찍어보고 판단하자. (파일이 없으면 만들면 된다.)

import { redirect } from '@sveltejs/kit'

export async function handle({ event, resolve }) {
    console.log(event)

    return await resolve(event)
}
HTTP 요청이 오면 hook.server.jshandle()을 거친 뒤에 adapter-node의 헨들러를 거친다.
adapter-node의 헨들러 단에서 Origin 체크를 하니까, 앞의 handle()에서 디버그 정보를 찍어볼 수 있다.

그리고 dokku logs ~~~~ -t 를 이용하여 출력을 확인하면 된다. 아래와 같이 나올 것이다:

HTTP의 Host 헤더는 아무런 상관이 없다. Origin 헤더와 맞게만 하자.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

최신 글