SvelteKit + Supabase 통합 - 1일차
포스트
취소

SvelteKit + Supabase 통합 - 1일차

Ben Davis 유투버의 SvelteKit + Supabase 심화학습을 따라한 클론 프로젝트입니다. 소스는 깃허브 에 있습니다.

0. 개요

  • TailwindCSS 설정
  • supabase 로컬 개발 환경 설정
  • Prisma (postgresql) + multi-schema

화면 캡쳐

svltk-supabase-app-blog 블로그 포스트 리스트 (메인)

svltk-supabase-app-blog 블로그 포스트 페이지

1. 프로젝트 생성

SvelteKit 프로젝트 생성

1
2
3
4
5
6
7
8
pnpm create svelte@latest svltk-supabase-app
  - Skeleton project
  - TypeScript

cd svltk-supabase-app
pnpm install

pnpm run dev

TailwindCSS 설정

  1. Install TailwindCSS, tailwind-merge
  2. tailwind.config.js 에 template paths 추가
  3. app.css 에 Tailwind directives 추가
  4. 최상위 +layout.svelteapp.css import
  5. +page.svelte 에서 TailwindCSS classes 를 사용해 작동 확인
1
2
3
4
pnpm install -D tailwindcss postcss autoprefixer tailwind-merge
pnpx tailwindcss init -p

pnpm run dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cat <<EOF > tailwind.config.js
const defaultTheme = require('tailwindcss/defaultTheme');

/** @type {import('tailwindcss').Config} */
export default {
  content: ['./src/**/*.{html,js,svelte,ts}'],
  theme: {
    fontFamily: {
      sans: ['"Noto Sans KR"', ...defaultTheme.fontFamily.sans],
      serif: ['"Noto Serif KR"', ...defaultTheme.fontFamily.serif],
      mono: ['D2Coding', ...defaultTheme.fontFamily.mono],
    },      
  },
  plugins: [],
};
EOF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* src/app.css */

/* fonts: Noto Color Emoji, Noto Sans KR, Noto Serif KR */
@import url('https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&family=Noto+Sans+KR:wght@300;400;500;700&family=Noto+Serif+KR:wght@400;700&display=swap');
@import url("//cdn.jsdelivr.net/gh/wan2land/d2coding/d2coding-ligature-full.css");

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

@layer base {
  body {
    @apply bg-white dark:bg-gray-800;
  }
}
1
2
3
4
5
6
<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import '../app.css';
</script>

<slot />
1
2
<!-- src/routes/+page.svelte -->
<h1 class="text-3xl font-bold underline">Hello, SvelteKit!</h1>

선택사항 : Tailwind Nesting 플러그인

1
2
3
4
5
6
7
8
// postcss.config.js
export default {
  plugins: {
    'tailwindcss/nesting': {},
    tailwindcss: {},
    autoprefixer: {},
  },
};

2. supabase 로컬 개발 환경 설정

참고문서

supabase cli 설치 및 설정

  1. CLI 설치 (OS X)
  2. supabase login : access token 입력 (클라우드와 link 할 때 필요)
  3. 프로젝트용 superbase docker 컨테이너 실행
    • 우선 로컬에 docker 서비스가 활성화 되어 있어야 한다.
1
$ brew install supabase/tap/supabase
1
2
$ supabase login
# - access_token 입력
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ cd ${PROJECT_ROOT}

$ supabase init
# `./supabase/config.toml` 생성됨

$ supabase start
# project_id 이름으로 도커 컨테이너 그룹 실행

$ supabase status
# - DB URL
# - API URL
# - anon key

# open Studio => http://localhost:54323/

$ pnpm install @supabase/supabase-js
# pnpm install @supabase/auth-helpers-sveltekit

$ supabase stop
# supabase stop --no-backup  # 백업 없이 종료 (초기화)

SvelteKit 에서 Supabase 사용하기

  1. .env 설정
  2. Supabase Studio - SQL Query : countries 테이블 생성
  3. client.ts 생성 : anon_key, url 을 환경변수에서 가져오기
  4. supabase client 로 데이터 가져오기
  5. +page.svelte 에서 데이터 출력

참고

1
2
3
4
# .env
DATABASE_URL="postgresql://postgres:postgres@localhost:54322/postgres"
SUPABASE_ANON_KEY="..."
SUPABASE_URL="http://localhost:54321"
1
2
3
4
5
6
7
8
9
10
11
-- Create the table
CREATE TABLE countries (
 id SERIAL PRIMARY KEY,
 name VARCHAR(255) NOT NULL
);
-- Insert some sample data into the table
INSERT INTO countries (name) VALUES ('United States');
INSERT INTO countries (name) VALUES ('Canada');
INSERT INTO countries (name) VALUES ('Mexico');

select * from countries;
1
2
3
4
5
6
7
8
9
10
11
12
13
// src/lib/supabase/client.ts
import { createClient } from '@supabase/supabase-js';
import { SUPABASE_ANON_KEY, SUPABASE_URL } from '$env/static/private';

export const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
  db: {
    schema: 'public',
  },
  auth: {
    persistSession: false,
  },
});

1
2
3
4
5
6
7
8
9
10
// src/routes/+page.server.ts
import type { PageServerLoad } from './$types';
import { supabase } from '$lib/supabase/client';

export const load: PageServerLoad = async ({ params }) => {
  const { data } = await supabase.from('countries').select();
  return {
    countries: data ?? [],
  };
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- src/routes/+page.svelte -->
<script lang="ts">
  import type { PageData } from './$types';

  export let data: PageData;
</script>

<div class="container mx-auto">
  <h1 class="text-3xl font-bold underline">Hello, SvelteKit!</h1>

  <ul class="list-disc">
    {#each data.countries as country}
    <li key="{country.id}">{country.name}</li>
    {/each}
  </ul>
</div>

참고: Typescript 위한 database types 생성

1
supabase gen types typescript --local --debug > src/lib/database.types.ts

3. Prisma 설치 및 설정

brew 패키지 매니저로 설치한 경우 버전이 안맞아 previewFeatures 가 작동 안되는 경우가 있으니 npm 또는 pnpm 을 붙여서 사용해야 한다.

Prisma 설치

참고: Using Prisma with SvelteKit

1
2
3
4
5
6
7
8
9
10
$ pnpm install -D prisma @prisma/client

$ pnpx prisma init --datasource-provider postgresql

# 이미 DB 에 스키마가 존재하는 경우 
$ pnpx prisma db pull
$ pnpx prisma generate

# 또는, 모델링 작성후 DB 에 반영하려면
$ pnpx prisma db push

Prisma multi-schema

멀티 스키마 의존라이브러리를 설치한다.

1
2
3
4
5
6
7
8
9
$ pnpm add prisma-multischema

$ pnpx prisma-multischema

$ pnpx prisma db pull  # 기존 스키마를 읽고

$ pnpx prisma db push  # 새로운(변경된) 스키마를 반영한다

$ pnpx prisma generate # prisma client 를 생성한다

prisma/schema.prisma 에 멀티 스키마를 위한 변경사항을 작성한다

  • db 에 schemas 를 나열하고
  • model 마다 @@schema 를 명시해야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
  previewFeatures = ["multiSchema"]
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
  schemas  = ["auth", "public"]
}

model countries {
  id   Int    @id @default(autoincrement())
  name String @db.VarChar(255)

  @@schema("public")
}

model user {
  id     Int     @id
  name String @db.VarChar(255)

  @@schema("auth")
}

Adding Prisma Migrate to an existing project

1
2
3
4
5
6
7
$ pnpx prisma db push  # 새로운(변경된) 스키마를 반영한다

# migration 을 위한 스크립트 생성
$ pnpx prisma migrate diff \
--from-empty \
--to-schema-datamodel prisma/schema.prisma \
--script > prisma/migrations/0_init/migration.sql

초기 수정사항이 더 생긴 경우,

  • migration.sql 수정 후에
    • CREATE INDEX blog_post_title_idx ON blog."Post" (title);
  • 반영 (실패! 왜 안되지?) : 초기화 migration 관련 이야기 같음
1
2
3
4
$ pnpx prisma migrate resolve --applied 0_init
Error: P3008

The migration `0_init` is already recorded as applied in the database.

Adding Database Access to your SvelteKit App with Prisma

소스 : 깃허브 - sonylomo/demo-sveltekit

  1. 편의상 blog 스키마를 만들고, 테이블 생성
  2. prisma/schema.prisma 스키마 작성
  3. 스키마 변경 사항을 반영하고
  4. seed 데이터 $lib/data.json 을 insert (소스 참고)
  5. $lib/prisma.ts : prisma 클라이언트 생성
  6. SvelteKit load 함수 작성
    • / Get all published posts
    • /drafts Get all drafted posts
    • /p/[id] Get a single post by its id
  7. SvelteKit action 함수 작성
    • route/create POST : Create a new post in your database
    • route/p/[id] PUT : Publish a post by its id
    • route/p/[id] DELETE : Delete a post by its id
    • route/signup POST : Create a new user
1
create schema blog;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
  previewFeatures = ["multiSchema"]
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
  schemas  = ["blog", "public"]
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  published Boolean  @default(false)
  author    User?    @relation(fields: [authorId], references: [id])
  authorId  Int?

  @@schema("blog")
}

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]

  @@schema("blog")
}
1
2
3
pnpx prisma db push

pnpx vite-node prisma/seed.ts

4. 도커 배포

adapter-node 오류

1
2
3
4
5
6
7
8
9
$ pnpm add -D @sveltejs/adapter-node
$ pnpm add dotenv

$ pnpm run build 
$ pnpm run preview  # vite preview

# node 실행시 __dirname 관련 오류로 실행 안됨! (prisma 문제)
# `.env` 읽기를 위해 dentenv/config 모듈을 먼저 수행
$ node -r dotenv/config build

prisma 이슈

vite dev 또는 vite preview 에서는 문제가 없다. (단, node 실행시 오류)

vite preview 설정

node 서버 대신에 vite 를 사용하여 웹앱을 기동시키자.

  • 외부 노출용 호스팅 설정
  • port 설정 : 동일 port 가 사용중이면 exit
    • server (개발용) pnpm run dev
    • preview (빌드용) pnpm run preview
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// vite.config.ts
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [sveltekit()],
  server: {
    host: true,
    port: 3000,
    strictPort: true,
    watch: {
      usePolling: true,
    },
  },
  preview: {
    port: 8000,
  },
});

// ** vite 설정 안하는 경우, 파라미터로 설정 --host, --port
// CMD [ "pnpm", "run", "preview", "--host", "0.0.0.0"]

도커 컴포즈 설정

  • node 18 + pnpm 패키지 매니저
  • preview 실행
1
2
3
4
5
6
7
8
9
10
11
12
FROM node:18-slim
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
COPY . /app
WORKDIR /app

RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
RUN pnpm run build

EXPOSE 8000
CMD [ "pnpm", "run", "preview" ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
version: '3'

services:
  vite_docker:
    # docker build -t svltk-supabase-app --no-cache .
    build: .
    container_name: vite_docker
    ports:
      - 8000:8000
    networks:
      - webappnetwork

networks:
  webappnetwork:
    driver: bridge
1
2
3
4
5
6
7
$ docker build -t svltk-supabase-app --no-cache .
$ docker run -it -p 4173:4173 --rm svltk-supabase-app bash
$ docker run -dp 4173:4173 --rm \
  --name svltk-supabase-app svltk-supabase-app

$ docker compose up --build -d 
$ docker compose down -v

9. Review

  • 아직 본론은 안들어 갔다. (이제 시작)

참고문서

 
 

끝!   읽어주셔서 감사합니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.