์ด ํŽ˜์ด์ง€๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ ์ž‘์„ฑ์ž๊ฐ€ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ œ์ถœํ•  ๋•Œ ํ”ํžˆ ๋ฐ›๋Š” ๋ฆฌ๋ทฐ ์˜๊ฒฌ์„ ๋‚˜์—ดํ•ฉ๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€์˜ ๊ฐ€์ด๋“œ๋ผ์ธ์€ ๊ถŒ์žฅ ์‚ฌํ•ญ์ด์ง€๋งŒ, ์‹ฌ๊ฐ์„ฑ์— ๋”ฐ๋ผ ์œ„๋ฐ˜ ์‚ฌํ•ญ์„ ํ•ด๊ฒฐํ•˜๋„๋ก ์š”๊ตฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ์ •์ฑ…

Developer policies์™€ Submission requirements for plugins๋ฅผ ๋ฐ˜๋“œ์‹œ ์ฝ์–ด๋ณด์„ธ์š”.

์ผ๋ฐ˜

์ „์—ญ ์•ฑ ์ธ์Šคํ„ด์Šค ์‚ฌ์šฉ ํ”ผํ•˜๊ธฐ

์ „์—ญ ์•ฑ ๊ฐ์ฒด์ธ app (๋˜๋Š” window.app) ์‚ฌ์šฉ์„ ํ”ผํ•˜์„ธ์š”. ๋Œ€์‹  ํ”Œ๋Ÿฌ๊ทธ์ธ ์ธ์Šคํ„ด์Šค์—์„œ ์ œ๊ณตํ•˜๋Š” ์ฐธ์กฐ์ธ this.app์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

์ „์—ญ ์•ฑ ๊ฐ์ฒด๋Š” ๋””๋ฒ„๊น… ๋ชฉ์ ์œผ๋กœ ๋งŒ๋“ค์–ด์กŒ์œผ๋ฉฐ ํ–ฅํ›„ ์ œ๊ฑฐ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ถˆํ•„์š”ํ•œ ์ฝ˜์†” ๋กœ๊น… ํ”ผํ•˜๊ธฐ

๋ถˆํ•„์š”ํ•œ ๋กœ๊น…์„ ํ”ผํ•ด์ฃผ์„ธ์š”. ๊ธฐ๋ณธ ์„ค์ •์—์„œ๋Š” ๊ฐœ๋ฐœ์ž ์ฝ˜์†”์— ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋งŒ ํ‘œ์‹œ๋˜์–ด์•ผ ํ•˜๋ฉฐ, ๋””๋ฒ„๊ทธ ๋ฉ”์‹œ์ง€๋Š” ํ‘œ์‹œ๋˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํด๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ ๋ฒ ์ด์Šค ์ •๋ฆฌ ๊ณ ๋ คํ•˜๊ธฐ

ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ํ•˜๋‚˜ ์ด์ƒ์˜ .ts ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ๋ฆฌ๋ทฐ์™€ ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ํด๋”๋กœ ์ •๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด๋ณด์„ธ์š”.

ํ”Œ๋ ˆ์ด์Šคํ™€๋” ํด๋ž˜์Šค ์ด๋ฆ„ ๋ฐ”๊พธ๊ธฐ

์ƒ˜ํ”Œ ํ”Œ๋Ÿฌ๊ทธ์ธ์—๋Š” MyPlugin, MyPluginSettings, SampleSettingTab๊ณผ ๊ฐ™์€ ์ผ๋ฐ˜์ ์ธ ํด๋ž˜์Šค์— ๋Œ€ํ•œ ํ”Œ๋ ˆ์ด์Šคํ™€๋” ์ด๋ฆ„์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ ์ด๋ฆ„์„ ๋ฐ˜์˜ํ•˜๋„๋ก ์ด ์ด๋ฆ„๋“ค์„ ๋ณ€๊ฒฝํ•˜์„ธ์š”.

๋ชจ๋ฐ”์ผ

Transclude of Mobile-development#node-and-electron-apis

Transclude of Mobile-development#lookbehind-in-regular-expressions

UI ํ…์ŠคํŠธ

์ด ์„น์…˜์€ ์„ค์ •, ๋ช…๋ น์–ด, ๋ฒ„ํŠผ ๋“ฑ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค์˜ ํ…์ŠคํŠธ ์„œ์‹ ์ง€์ •์— ๋Œ€ํ•œ ๊ฐ€์ด๋“œ๋ผ์ธ์„ ๋‚˜์—ดํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜ Settings โ†’ Appearance์˜ ์˜ˆ์‹œ๋Š” ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค ํ…์ŠคํŠธ์— ๋Œ€ํ•œ ๊ฐ€์ด๋“œ๋ผ์ธ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

  1. ์ผ๋ฐ˜ ์„ค์ •์€ ์ƒ๋‹จ์— ์žˆ์œผ๋ฉฐ ์ œ๋ชฉ์ด ์—†์Šต๋‹ˆ๋‹ค.
  2. ์„น์…˜ ์ œ๋ชฉ์—๋Š” โ€œsettingsโ€๋ผ๋Š” ๋‹จ์–ด๊ฐ€ ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  3. Use Sentence case in UI.

Obsidian์šฉ ํ…์ŠคํŠธ ์ž‘์„ฑ ๋ฐ ์„œ์‹ ์ง€์ •์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

์„ค์ • ์•„๋ž˜์— ์„น์…˜์ด ๋‘ ๊ฐœ ์ด์ƒ์ธ ๊ฒฝ์šฐ์—๋งŒ ์ œ๋ชฉ ์‚ฌ์šฉํ•˜๊ธฐ

์„ค์ • ํƒญ์— โ€œGeneralโ€, โ€œSettingsโ€ ๋˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ ์ด๋ฆ„๊ณผ ๊ฐ™์€ ์ตœ์ƒ์œ„ ์ œ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜์ง€ ๋งˆ์„ธ์š”.

์„ค์ • ์•„๋ž˜์— ์„น์…˜์ด ๋‘ ๊ฐœ ์ด์ƒ ์žˆ๊ณ  ๊ทธ์ค‘ ํ•˜๋‚˜์— ์ผ๋ฐ˜ ์„ค์ •์ด ํฌํ•จ๋œ ๊ฒฝ์šฐ, ์ œ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๊ณ  ์ƒ๋‹จ์— ์œ ์ง€ํ•˜์„ธ์š”.

์˜ˆ๋ฅผ ๋“ค์–ด, Settings โ†’ Appearance ์•„๋ž˜์˜ ์„ค์ •์„ ๋ณด์„ธ์š”.

์„ค์ • ์ œ๋ชฉ์— โ€œsettingsโ€ ํ”ผํ•˜๊ธฐ

์„ค์ • ํƒญ์—์„œ ์„ค์ •์„ ์ •๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์ œ๋ชฉ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ œ๋ชฉ์— โ€œsettingsโ€๋ผ๋Š” ๋‹จ์–ด๋ฅผ ํฌํ•จํ•˜์ง€ ๋งˆ์„ธ์š”. ์„ค์ • ํƒญ ์•„๋ž˜์˜ ๋ชจ๋“  ๊ฒƒ์ด ์„ค์ •์ด๋ฏ€๋กœ ๋ชจ๋“  ์ œ๋ชฉ์— ๋ฐ˜๋ณตํ•˜๋Š” ๊ฒƒ์€ ์ค‘๋ณต์ž…๋‹ˆ๋‹ค.

  • โ€œAdvanced settingsโ€ ๋Œ€์‹  โ€œAdvancedโ€๋ฅผ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค.
  • โ€œSettings for templatesโ€ ๋Œ€์‹  โ€œTemplatesโ€๋ฅผ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค.

UI์—์„œ ๋ฌธ์žฅ ์ผ€์ด์Šค ์‚ฌ์šฉํ•˜๊ธฐ

UI ์š”์†Œ์˜ ๋ชจ๋“  ํ…์ŠคํŠธ๋Š” Title Case ๋Œ€์‹  Sentence case๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์žฅ ์ผ€์ด์Šค์—์„œ๋Š” ๋ฌธ์žฅ์˜ ์ฒซ ๋‹จ์–ด์™€ ๊ณ ์œ  ๋ช…์‚ฌ๋งŒ ๋Œ€๋ฌธ์ž๋กœ ํ‘œ๊ธฐํ•ฉ๋‹ˆ๋‹ค.

  • โ€œTemplate Folder Locationโ€ ๋Œ€์‹  โ€œTemplate folder locationโ€์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค.
  • โ€œCreate New Noteโ€ ๋Œ€์‹  โ€œCreate new noteโ€๋ฅผ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค.

<h1>, <h2> ๋Œ€์‹  setHeading ์‚ฌ์šฉํ•˜๊ธฐ

HTML์˜ ์ œ๋ชฉ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค๋ฅธ ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ฐ„์— ์Šคํƒ€์ผ์ด ์ผ๊ด€๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

new Setting(containerEl).setName('your heading title').setHeading();

๋ณด์•ˆ

innerHTML, outerHTML, insertAdjacentHTML ํ”ผํ•˜๊ธฐ

์‚ฌ์šฉ์ž ์ •์˜ ์ž…๋ ฅ์„ ์‚ฌ์šฉํ•˜์—ฌ innerHTML, outerHTML, insertAdjacentHTML๋กœ DOM ์š”์†Œ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ๋ณด์•ˆ ์œ„ํ—˜์„ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์ œ๋Š” ์‚ฌ์šฉ์ž ์ž…๋ ฅ ${name}์„ ํฌํ•จํ•˜๋Š” ๋ฌธ์ž์—ด์„ ์‚ฌ์šฉํ•˜์—ฌ DOM ์š”์†Œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. name์€ <script>alert()</script>์™€ ๊ฐ™์€ ๋‹ค๋ฅธ DOM ์š”์†Œ๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ž ์žฌ์ ์ธ ๊ณต๊ฒฉ์ž๊ฐ€ ์‚ฌ์šฉ์ž์˜ ์ปดํ“จํ„ฐ์—์„œ ์ž„์˜์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function showName(name: string) {
  let containerElement = document.querySelector('.my-container');
  // ์ด๋ ‡๊ฒŒ ํ•˜์ง€ ๋งˆ์„ธ์š”
  containerElement.innerHTML = `<div class="my-class"><b>Your name is: </b>${name}</div>`;
}

๋Œ€์‹ , createEl(), createDiv(), createSpan()๊ณผ ๊ฐ™์€ DOM API ๋˜๋Š” Obsidian ํ—ฌํผ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ DOM ์š”์†Œ๋ฅผ ๋งŒ๋“œ์„ธ์š”. ์ž์„ธํ•œ ๋‚ด์šฉ์€ HTML elements๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

HTML ์š”์†Œ์˜ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•˜๋ ค๋ฉด el.empty();๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ

ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์–ธ๋กœ๋“œ๋  ๋•Œ ๋ฆฌ์†Œ์Šค ์ •๋ฆฌํ•˜๊ธฐ

์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ์™€ ๊ฐ™์ด ํ”Œ๋Ÿฌ๊ทธ์ธ์— ์˜ํ•ด ์ƒ์„ฑ๋œ ๋ชจ๋“  ๋ฆฌ์†Œ์Šค๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์–ธ๋กœ๋“œ๋  ๋•Œ ํŒŒ๊ดด๋˜๊ฑฐ๋‚˜ ํ•ด์ œ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ฐ€๋Šฅํ•˜๋ฉด registerEvent() ๋˜๋Š” addCommand()์™€ ๊ฐ™์€ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์–ธ๋กœ๋“œ๋  ๋•Œ ๋ฆฌ์†Œ์Šค๋ฅผ ์ž๋™์œผ๋กœ ์ •๋ฆฌํ•˜์„ธ์š”.

export default class MyPlugin extends Plugin {
  onload() {
    this.registerEvent(this.app.vault.on('create', this.onCreate));
  }
 
  onCreate: (file: TAbstractFile) => {
    // ...
  }
}

Note

ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์–ธ๋กœ๋“œ๋  ๋•Œ ์ œ๊ฑฐ๊ฐ€ ๋ณด์žฅ๋˜๋Š” ๋ฆฌ์†Œ์Šค๋Š” ์ •๋ฆฌํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, DOM ์š”์†Œ์— mouseenter ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋“ฑ๋กํ•˜๋ฉด ํ•ด๋‹น ์š”์†Œ๊ฐ€ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚  ๋•Œ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜๋ฉ๋‹ˆ๋‹ค.

onunload์—์„œ ๋ฆฌํ”„(leaf) ๋ถ„๋ฆฌํ•˜์ง€ ์•Š๊ธฐ

์‚ฌ์šฉ์ž๊ฐ€ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์—…๋ฐ์ดํŠธํ•  ๋•Œ, ์—ด๋ ค ์žˆ๋Š” ๋ชจ๋“  ๋ฆฌํ”„๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์–ด๋””๋กœ ์˜ฎ๊ฒผ๋Š”์ง€์— ๊ด€๊ณ„์—†์ด ์›๋ž˜ ์œ„์น˜์—์„œ ๋‹ค์‹œ ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค.

๋ช…๋ น์–ด

๋ช…๋ น์–ด์— ๊ธฐ๋ณธ ๋‹จ์ถ•ํ‚ค ์„ค์ • ํ”ผํ•˜๊ธฐ

๊ธฐ๋ณธ ๋‹จ์ถ•ํ‚ค๋ฅผ ์„ค์ •ํ•˜๋ฉด ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ฐ„์— ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋ฏธ ๊ตฌ์„ฑํ•œ ๋‹จ์ถ•ํ‚ค๋ฅผ ๋ฎ์–ด์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๋ชจ๋“  ์šด์˜ ์ฒด์ œ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ธฐ๋ณธ ๋‹จ์ถ•ํ‚ค๋ฅผ ์„ ํƒํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

๋ช…๋ น์–ด์— ์ ์ ˆํ•œ ์ฝœ๋ฐฑ ์œ ํ˜• ์‚ฌ์šฉํ•˜๊ธฐ

ํ”Œ๋Ÿฌ๊ทธ์ธ์— ๋ช…๋ น์–ด๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ ์ ์ ˆํ•œ ์ฝœ๋ฐฑ ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

  • ๋ช…๋ น์–ด๊ฐ€ ๋ฌด์กฐ๊ฑด ์‹คํ–‰๋˜๋Š” ๊ฒฝ์šฐ callback์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋ช…๋ น์–ด๊ฐ€ ํŠน์ • ์กฐ๊ฑด์—์„œ๋งŒ ์‹คํ–‰๋˜๋Š” ๊ฒฝ์šฐ checkCallback์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋ช…๋ น์–ด์— ์—ด๋ ค ์žˆ๊ณ  ํ™œ์„ฑํ™”๋œ ๋งˆํฌ๋‹ค์šด ์—๋””ํ„ฐ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ, editorCallback ๋˜๋Š” ํ•ด๋‹น editorCheckCallback์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

์ž‘์—… ๊ณต๊ฐ„

workspace.activeLeaf์— ์ง์ ‘ ์ ‘๊ทผ ํ”ผํ•˜๊ธฐ

ํ™œ์„ฑ ๋ทฐ์— ์ ‘๊ทผํ•˜๋ ค๋ฉด ๋Œ€์‹  getActiveViewOfType()๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”:

const view = this.app.workspace.getActiveViewOfType(MarkdownView);
 
// getActiveViewOfType์€ ํ™œ์„ฑ ๋ทฐ๊ฐ€ null์ด๊ฑฐ๋‚˜ MarkdownView๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ null์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
if (view) {
  // ...
}

ํ™œ์„ฑ ๋…ธํŠธ์˜ ์—๋””ํ„ฐ์— ์ ‘๊ทผํ•˜๋ ค๋ฉด ๋Œ€์‹  activeEditor๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”:

const editor = this.app.workspace.activeEditor?.editor;
 
if (editor) {
    // ...
}

์‚ฌ์šฉ์ž ์ •์˜ ๋ทฐ์— ๋Œ€ํ•œ ์ฐธ์กฐ ๊ด€๋ฆฌ ํ”ผํ•˜๊ธฐ

์‚ฌ์šฉ์ž ์ •์˜ ๋ทฐ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋‚˜ ์˜๋„ํ•˜์ง€ ์•Š์€ ๊ฒฐ๊ณผ๋ฅผ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜์ง€ ๋งˆ์„ธ์š”:

this.registerView(MY_VIEW_TYPE, () => this.view = new MyCustomView());

๋Œ€์‹  ์ด๋ ‡๊ฒŒ ํ•˜์„ธ์š”:

this.registerView(MY_VIEW_TYPE, () => new MyCustomView());

ํ”Œ๋Ÿฌ๊ทธ์ธ์—์„œ ๋ทฐ์— ์ ‘๊ทผํ•˜๋ ค๋ฉด Workspace.getActiveLeavesOfType()๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”:

for (let leaf of app.workspace.getActiveLeavesOfType(MY_VIEW_TYPE)) {
  let view = leaf.view;
  if (view instanceof MyCustomView) {
    // ...
  }
}

์ €์žฅ์†Œ(Vault)

ํ™œ์„ฑ ํŒŒ์ผ์— ๋Œ€ํ•ด Vault.modify ๋Œ€์‹  Editor API ์„ ํ˜ธํ•˜๊ธฐ

ํ™œ์„ฑ ๋…ธํŠธ๋ฅผ ํŽธ์ง‘ํ•˜๋ ค๋ฉด Vault.modify() ๋Œ€์‹  Editor ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

Editor๋Š” ์ปค์„œ ์œ„์น˜, ์„ ํƒ ์˜์—ญ, ์ ‘ํžŒ ๋‚ด์šฉ๊ณผ ๊ฐ™์€ ํ™œ์„ฑ ๋…ธํŠธ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. Vault.modify()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋…ธํŠธ๋ฅผ ํŽธ์ง‘ํ•˜๋ฉด ๋ชจ๋“  ์ •๋ณด๊ฐ€ ์†์‹ค๋˜์–ด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ์ €ํ•˜๋ฉ๋‹ˆ๋‹ค.

Editor๋Š” ๋…ธํŠธ์˜ ์ผ๋ถ€๋ฅผ ์ž‘๊ฒŒ ๋ณ€๊ฒฝํ•  ๋•Œ ๋” ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.

๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ํŒŒ์ผ์„ ์ˆ˜์ •ํ•˜๋ ค๋ฉด Vault.modify ๋Œ€์‹  Vault.process ์„ ํ˜ธํ•˜๊ธฐ

ํ˜„์žฌ ์—ด๋ ค ์žˆ์ง€ ์•Š์€ ๋…ธํŠธ๋ฅผ ํŽธ์ง‘ํ•˜๋ ค๋ฉด Vault.modify ๋Œ€์‹  Vault.process ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

process ํ•จ์ˆ˜๋Š” ํŒŒ์ผ์„ ์›์ž์ ์œผ๋กœ ์ˆ˜์ •ํ•˜๋ฏ€๋กœ, ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋™์ผํ•œ ํŒŒ์ผ์„ ์ˆ˜์ •ํ•˜๋Š” ๋‹ค๋ฅธ ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ ์ถฉ๋Œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋…ธํŠธ์˜ ํ”„๋ก ํŠธ๋งคํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋ ค๋ฉด FileManager.processFrontMatter ์„ ํ˜ธํ•˜๊ธฐ

๋…ธํŠธ์˜ ํ”„๋ก ํŠธ๋งคํ„ฐ๋ฅผ ์ถ”์ถœํ•˜๊ณ  YAML์„ ์ˆ˜๋™์œผ๋กœ ํŒŒ์‹ฑํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋Š” ๋Œ€์‹  FileManager.processFrontMatter ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

processFrontMatter๋Š” ์›์ž์ ์œผ๋กœ ์‹คํ–‰๋˜๋ฏ€๋กœ ํŒŒ์ผ์„ ์ˆ˜์ •ํ•ด๋„ ๋™์ผํ•œ ํŒŒ์ผ์„ ํŽธ์ง‘ํ•˜๋Š” ๋‹ค๋ฅธ ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ ์ถฉ๋Œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ƒ์„ฑ๋œ YAML์˜ ์ผ๊ด€๋œ ๋ ˆ์ด์•„์›ƒ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

Adapter API ๋Œ€์‹  Vault API ์„ ํ˜ธํ•˜๊ธฐ

Obsidian์€ ํŒŒ์ผ ์ž‘์—…์„ ์œ„ํ•œ ๋‘ ๊ฐ€์ง€ API๋ฅผ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค: Vault API (app.vault)์™€ Adapter API (app.vault.adapter).

Adapter API์˜ ํŒŒ์ผ ์ž‘์—…์ด ๋งŽ์€ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ๋” ์ต์ˆ™ํ•˜์ง€๋งŒ, Vault API๋Š” ์–ด๋Œ‘ํ„ฐ์— ๋น„ํ•ด ๋‘ ๊ฐ€์ง€ ์ฃผ์š” ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์„ฑ๋Šฅ: Vault API์—๋Š” ํŒŒ์ผ์ด ์ด๋ฏธ Obsidian์— ์•Œ๋ ค์ง„ ๊ฒฝ์šฐ ํŒŒ์ผ ์ฝ๊ธฐ ์†๋„๋ฅผ ๋†’์ผ ์ˆ˜ ์žˆ๋Š” ์บ์‹ฑ ๊ณ„์ธต์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์•ˆ์ „์„ฑ: Vault API๋Š” ๋™์‹œ์— ์“ฐ๊ธฐ ์ค‘์ธ ํŒŒ์ผ์„ ์ฝ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ๊ฒฝ์Ÿ ์กฐ๊ฑด์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ํŒŒ์ผ ์ž‘์—…์„ ์ง๋ ฌ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๊ฒฝ๋กœ๋กœ ํŒŒ์ผ์„ ์ฐพ๊ธฐ ์œ„ํ•ด ๋ชจ๋“  ํŒŒ์ผ ๋ฐ˜๋ณต ํ”ผํ•˜๊ธฐ

์ด๊ฒƒ์€ ํŠนํžˆ ํฐ ์ €์žฅ์†Œ์—์„œ ๋น„ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค. ๋Œ€์‹  Vault.getFileByPath, Vault.getFolderByPath ๋˜๋Š” Vault.getAbstractFileByPath๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

์ด๋ ‡๊ฒŒ ํ•˜์ง€ ๋งˆ์„ธ์š”:

this.app.vault.getFiles().find(file => file.path === filePath);

๋Œ€์‹  ์ด๋ ‡๊ฒŒ ํ•˜์„ธ์š”:

const filePath = 'folder/file.md';
// ํŒŒ์ผ์„ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด
const file = this.app.vault.getFileByPath(filePath);
const folderPath = 'folder';
// ๋˜๋Š” ํด๋”๋ฅผ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด
const folder = this.app.vault.getFolderByPath(folderPath);

์ œ๊ณต๋œ ๊ฒฝ๋กœ๊ฐ€ ํด๋”์šฉ์ธ์ง€ ํŒŒ์ผ์šฉ์ธ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜์„ธ์š”:

const abstractFile = this.app.vault.getAbstractFileByPath(filePath);
 
if (file instanceof TFile) {
	// ํŒŒ์ผ์ž…๋‹ˆ๋‹ค
}
if (file instanceof TFolder) {
	// ํด๋”์ž…๋‹ˆ๋‹ค
}

์‚ฌ์šฉ์ž ์ •์˜ ๊ฒฝ๋กœ๋ฅผ ์ •๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด normalizePath() ์‚ฌ์šฉํ•˜๊ธฐ

์ €์žฅ์†Œ์˜ ํŒŒ์ผ์ด๋‚˜ ํด๋”์— ๋Œ€ํ•œ ์‚ฌ์šฉ์ž ์ •์˜ ๊ฒฝ๋กœ๋ฅผ ๋ฐ›๊ฑฐ๋‚˜ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฝ”๋“œ์—์„œ ์ž์ฒด ๊ฒฝ๋กœ๋ฅผ ๊ตฌ์„ฑํ•  ๋•Œ๋งˆ๋‹ค normalizePath()๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

normalizePath()๋Š” ๊ฒฝ๋กœ๋ฅผ ๋ฐ›์•„ ํŒŒ์ผ ์‹œ์Šคํ…œ ๋ฐ ํฌ๋กœ์Šค ํ”Œ๋žซํผ ์‚ฌ์šฉ์— ์•ˆ์ „ํ•˜๋„๋ก ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค:

  • \ ๋˜๋Š” /๋ฅผ ํ•˜๋‚˜ ์ด์ƒ์˜ \ ๋˜๋Š” /๋ฅผ ๋‹จ์ผ /๋กœ ๋ฐ”๊พธ๋Š” ๋“ฑ ์ˆœ๋ฐฉํ–ฅ ๋ฐ ์—ญ๋ฐฉํ–ฅ ์Šฌ๋ž˜์‹œ ์‚ฌ์šฉ์„ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  • ์„ ํ–‰ ๋ฐ ํ›„ํ–‰ ์ˆœ๋ฐฉํ–ฅ ๋ฐ ์—ญ๋ฐฉํ–ฅ ์Šฌ๋ž˜์‹œ๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
  • ์ค„ ๋ฐ”๊ฟˆ ์—†๋Š” ๊ณต๋ฐฑ \u00A0์„ ์ผ๋ฐ˜ ๊ณต๋ฐฑ์œผ๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.
  • ๊ฒฝ๋กœ๋ฅผ String.prototype.normalize๋ฅผ ํ†ตํ•ด ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
import { normalizePath } from 'obsidian';
const pathToPlugin = normalizePath('//my-folder\file');
// pathToPlugin์€ "//my-folder\"๊ฐ€ ์•„๋‹Œ "my-folder/file"์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค

์—๋””ํ„ฐ

์—๋””ํ„ฐ ํ™•์žฅ ๋ณ€๊ฒฝ ๋˜๋Š” ์žฌ๊ตฌ์„ฑํ•˜๊ธฐ

registerEditorExtension()์„ ์‚ฌ์šฉํ•˜์—ฌ ์—๋””ํ„ฐ ํ™•์žฅ์„ ๋“ฑ๋กํ•œ ํ›„ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ์žฌ๊ตฌ์„ฑํ•˜๋ ค๋ฉด updateOptions()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“  ์—๋””ํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์„ธ์š”.

class MyPlugin extends Plugin {
  private editorExtension: Extension[] = [];
 
  onload() {
    //...
 
    this.registerEditorExtension(this.editorExtension);
  }
 
  updateEditorExtension() {
    // ๋™์ผํ•œ ์ฐธ์กฐ๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ฐฐ์—ด ๋น„์šฐ๊ธฐ
    // (์—ฌ๊ธฐ์„œ ์ƒˆ ๋ฐฐ์—ด์„ ๋งŒ๋“ค์ง€ ๋งˆ์„ธ์š”)
    this.editorExtension.length = 0;
 
    // ์ƒˆ ์—๋””ํ„ฐ ํ™•์žฅ ๋งŒ๋“ค๊ธฐ
    let myNewExtension = this.createEditorExtension();
    // ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•˜๊ธฐ
    this.editorExtension.push(myNewExtension);
 
    // ๋ชจ๋“  ์—๋””ํ„ฐ์— ๋ณ€๊ฒฝ ์‚ฌํ•ญ ์ ์šฉํ•˜๊ธฐ
    this.app.workspace.updateOptions();
  }
}
 

์Šคํƒ€์ผ๋ง

ํ•˜๋“œ์ฝ”๋”ฉ๋œ ์Šคํƒ€์ผ๋ง ๊ธˆ์ง€

์ด๋ ‡๊ฒŒ ํ•˜์ง€ ๋งˆ์„ธ์š”:

const el = containerEl.createDiv();
el.style.color = 'white';
el.style.backgroundColor = 'red';

์‚ฌ์šฉ์ž๊ฐ€ ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ์Šคํƒ€์ผ๋ง์„ ์‰ฝ๊ฒŒ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋ ค๋ฉด CSS ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฝ”๋“œ์— ์Šคํƒ€์ผ๋ง์„ ํ•˜๋“œ์ฝ”๋”ฉํ•˜๋ฉด ํ…Œ๋งˆ์™€ ์Šค๋‹ˆํŽซ์œผ๋กœ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์ด ๋ถˆ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.

๋Œ€์‹  ์ด๋ ‡๊ฒŒ ํ•˜์„ธ์š”:

const el = containerEl.createDiv({cls: 'warning-container'});

ํ”Œ๋Ÿฌ๊ทธ์ธ CSS์— ๋‹ค์Œ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”:

.warning-container {
	color: var(--text-normal);
	background-color: var(--background-modifier-error);
}

ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ์Šคํƒ€์ผ๋ง์„ Obsidian ๋ฐ ๋‹ค๋ฅธ ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ ์ผ๊ด€๋˜๊ฒŒ ๋งŒ๋“ค๋ ค๋ฉด Obsidian์—์„œ ์ œ๊ณตํ•˜๋Š” CSS variables๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ ์‚ฌ๋ก€์— ๋งž๋Š” ๋ณ€์ˆ˜๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ์ง์ ‘ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

TypeScript

var ๋Œ€์‹  const์™€ let ์„ ํ˜ธํ•˜๊ธฐ

์ž์„ธํ•œ ๋‚ด์šฉ์€ ํ˜„๋Œ€ JavaScript์—์„œ var๊ฐ€ ๊ตฌ์‹์œผ๋กœ ๊ฐ„์ฃผ๋˜๋Š” 4๊ฐ€์ง€ ์ด์œ ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

Promise ๋Œ€์‹  async/await ์„ ํ˜ธํ•˜๊ธฐ

์ตœ์‹  ๋ฒ„์ „์˜ JavaScript์™€ TypeScript๋Š” ๋น„๋™๊ธฐ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด async ๋ฐ await ํ‚ค์›Œ๋“œ๋ฅผ ์ง€์›ํ•˜์—ฌ Promise๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋” ์ฝ๊ธฐ ์‰ฌ์šด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜์ง€ ๋งˆ์„ธ์š”:

function test(): Promise<string | null> {
  return requestUrl('https://example.com')
    .then(res => res.text
    .catch(e => {
      console.log(e);
      return null;
    });
}

๋Œ€์‹  ์ด๋ ‡๊ฒŒ ํ•˜์„ธ์š”:

async function AsyncTest(): Promise<string | null> {
  try {
    let res = await requestUrl('https://example.com');
    let text = await r.text;
    return text;
  }
  catch (e) {
    console.log(e);
    return null;
  }
}