์ด ๊ฐ€์ด๋“œ์—์„œ๋Š” React๋‚˜ Vue์™€ ๊ฐ™์€ ์ „ํ†ต์ ์ธ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๊ฒฝ๋Ÿ‰ ๋Œ€์•ˆ์ธ Svelte๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

Svelte๋Š” ์ฝ”๋“œ๋ฅผ ์ „์ฒ˜๋ฆฌํ•˜๊ณ  ์ตœ์ ํ™”๋œ ์ˆœ์ˆ˜ JavaScript๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ๊ตฌ์ถ•๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ƒํƒœ ๋ณ€๊ฒฝ์„ ์ถ”์ ํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ€์ƒ DOM์ด ํ•„์š” ์—†๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜๋ฉฐ, ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ตœ์†Œํ•œ์˜ ์ถ”๊ฐ€ ์˜ค๋ฒ„ํ—ค๋“œ๋กœ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

Svelte์— ๋Œ€ํ•ด ๋” ๋ฐฐ์šฐ๊ณ  ์‚ฌ์šฉ๋ฒ•์„ ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด, ํŠœํ† ๋ฆฌ์–ผ๊ณผ ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

์ด ๊ฐ€์ด๋“œ๋Š” Build a plugin์„ ์™„๋ฃŒํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.

Visual Studio Code

Svelte์—๋Š” Svelte ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ตฌ๋ฌธ ๊ฐ•์กฐ ๋ฐ ํ’๋ถ€ํ•œ IntelliSense๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ณต์‹ Visual Studio Code ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์ •ํ•˜๊ธฐ

Svelte๋กœ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋นŒ๋“œํ•˜๋ ค๋ฉด ์ข…์†์„ฑ์„ ์„ค์น˜ํ•˜๊ณ  Svelte๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘์„ฑ๋œ ์ฝ”๋“œ๋ฅผ ์ปดํŒŒ์ผํ•˜๋„๋ก ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. TypeScript์˜ ํƒ€์ž… ์ „์šฉ ๊ธฐ๋Šฅ๋งŒ ์‚ฌ์šฉํ•˜๋ ค๋Š” ๊ฒฝ์šฐ svelte-preprocess๋Š” ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

  1. ํ”Œ๋Ÿฌ๊ทธ์ธ ์ข…์†์„ฑ์— Svelte๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค:

    npm install --save-dev svelte svelte-preprocess esbuild-svelte svelte-check

    Info

    Svelte๋Š” ์ตœ์†Œ TypeScript 5.0์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. TypeScript 5.0์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋ ค๋ฉด ํ„ฐ๋ฏธ๋„์—์„œ ๋‹ค์Œ์„ ์‹คํ–‰ํ•˜์„ธ์š”.

    npm install typescript@~5.0.0
  2. tsconfig.json์„ ํ™•์žฅํ•˜์—ฌ ์ผ๋ฐ˜์ ์ธ Svelte ๋ฌธ์ œ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์œ ํ˜• ๊ฒ€์‚ฌ๋ฅผ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค. svelte-preprocess์—๋Š” verbatimModuleSyntax๊ฐ€ ํ•„์š”ํ•˜๊ณ  svelte-check๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ž‘๋™ํ•˜๋ ค๋ฉด skipLibCheck๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

    {
      "compilerOptions": {
        "verbatimModuleSyntax": true,
        "skipLibCheck": true,
        // ...
      },
      "include": [
        "**/*.ts",
        "**/*.svelte"
      ]
    }
  3. esbuild.config.mjs์—์„œ ํŒŒ์ผ ์ƒ๋‹จ์— ๋‹ค์Œ ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค:

    import esbuildSvelte from 'esbuild-svelte';
    import { sveltePreprocess } from 'svelte-preprocess';
  4. ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ชฉ๋ก์— Svelte๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

    const context = await esbuild.context({
      plugins: [
        esbuildSvelte({
          compilerOptions: { css: 'injected' },
          preprocess: sveltePreprocess(),
        }),
      ],
      // ...
    });
  5. package.json์— svelte-check๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

    {
      // ...
      "scripts": {
        // ...
        "svelte-check": "svelte-check --tsconfig tsconfig.json"
      }
    }

Svelte ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑํ•˜๊ธฐ

ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์— Counter.svelte๋ผ๋Š” ์ƒˆ ํŒŒ์ผ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค:

<script lang="ts">
  interface Props {
    startCount: number;
  }
 
  let {
    startCount
  }: Props = $props();
 
  let count = $state(startCount);
 
  export function increment() {
    count += 1;
  }
</script>
 
<div class="number">
  <span>My number is {count}!</span>
</div>
 
<style>
  .number {
    color: red;
  }
</style>

Svelte ์ปดํฌ๋„ŒํŠธ ๋งˆ์šดํŠธํ•˜๊ธฐ

Svelte ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๊ธฐ์กด HTML ์š”์†Œ์— ๋งˆ์šดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, Obsidian์˜ ์‚ฌ์šฉ์ž ์ •์˜ ItemView์— ๋งˆ์šดํŠธํ•˜๋Š” ๊ฒฝ์šฐ:

import { ItemView, WorkspaceLeaf } from 'obsidian';
 
// Counter Svelte ์ปดํฌ๋„ŒํŠธ์™€ `mount`, `unmount` ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
import Counter from './Counter.svelte';
import { mount, unmount } from 'svelte';
 
export const VIEW_TYPE_EXAMPLE = 'example-view';
 
export class ExampleView extends ItemView {
  // ์ด ItemView์— ๋งˆ์šดํŠธ๋œ Counter ์ธ์Šคํ„ด์Šค๋ฅผ ์œ ์ง€ํ•  ๋ณ€์ˆ˜์ž…๋‹ˆ๋‹ค.
  counter: ReturnType<typeof Counter> | undefined;
 
  constructor(leaf: WorkspaceLeaf) {
    super(leaf);
  }
 
  getViewType() {
    return VIEW_TYPE_EXAMPLE;
  }
 
  getDisplayText() {
    return 'Example view';
  }
 
  async onOpen() {
    // Svelte ์ปดํฌ๋„ŒํŠธ๋ฅผ ItemView์˜ content ์š”์†Œ์— ์ฒจ๋ถ€ํ•˜๊ณ  ํ•„์š”ํ•œ props๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
    this.counter = mount(Counter, {
      target: this.contentEl,
      props: {
        startCount: 5,
      }
    });
 
    // ์ปดํฌ๋„ŒํŠธ ์ธ์Šคํ„ด์Šค๊ฐ€ ํƒ€์ž…ํ™”๋˜์—ˆ์œผ๋ฏ€๋กœ, ๋‚ด๋ณด๋‚ธ `increment` ๋ฉ”์†Œ๋“œ๋Š” TypeScript์— ์•Œ๋ ค์ ธ ์žˆ์Šต๋‹ˆ๋‹ค.
    this.counter.increment();
  }
 
  async onClose() {
    if (this.counter) {
      // ItemView์—์„œ Counter๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
      unmount(this.counter);
    }
  }
}

์ด ์ƒˆ๋กœ์šด ๋ทฐ๋ฅผ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค์— ํ†ตํ•ฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ Views๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.