Obsidian์—์„œ ๋…ธํŠธ๋“ค์˜ ๋ชจ์Œ์„ ๋ณผํŠธ(Vault)๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋ณผํŠธ๋Š” ํ•˜๋‚˜์˜ ํด๋”์™€ ๊ทธ ํ•˜์œ„ ํด๋”๋“ค๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๋‹ค๋ฅธ Node.js ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ฒ˜๋Ÿผ ํŒŒ์ผ ์‹œ์Šคํ…œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, Vault ๋ชจ๋“ˆ์€ ๋ณผํŠธ ๋‚ด์˜ ํŒŒ์ผ๊ณผ ํด๋”๋ฅผ ๋” ์‰ฝ๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์ œ๋Š” ๋ณผํŠธ ๋‚ด์˜ ๋ชจ๋“  ๋งˆํฌ๋‹ค์šด ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค:

const files = this.app.vault.getMarkdownFiles()
 
for (let i = 0; i < files.length; i++) {
  console.log(files[i].path);
}

Tip

๋งˆํฌ๋‹ค์šด ๋ฌธ์„œ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ชจ๋“  ํŒŒ์ผ์„ ๋‚˜์—ดํ•˜๋ ค๋ฉด getFiles()๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

ํŒŒ์ผ ์ฝ๊ธฐ

ํŒŒ์ผ ๋‚ด์šฉ์„ ์ฝ๋Š” ๋‘ ๊ฐ€์ง€ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค: read()์™€ cachedRead().

  • ๋‚ด์šฉ์„ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ‘œ์‹œ๋งŒ ํ•˜๋ ค๋ฉด cachedRead()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋””์Šคํฌ์—์„œ ์—ฌ๋Ÿฌ ๋ฒˆ ์ฝ๋Š” ๊ฒƒ์„ ํ”ผํ•˜์„ธ์š”.
  • ๋‚ด์šฉ์„ ์ฝ๊ณ  ๋ณ€๊ฒฝํ•œ ํ›„ ๋””์Šคํฌ์— ๋‹ค์‹œ ์“ฐ๋ ค๋ฉด read()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜ค๋ž˜๋œ ๋ณต์‚ฌ๋ณธ์œผ๋กœ ํŒŒ์ผ์„ ๋ฎ์–ด์“ฐ๋Š” ๊ฒƒ์„ ํ”ผํ•˜์„ธ์š”.

Info

cachedRead()์™€ read()์˜ ์œ ์ผํ•œ ์ฐจ์ด์ ์€ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ํŒŒ์ผ์„ ์ฝ๊ธฐ ์ง์ „์— Obsidian ์™ธ๋ถ€์—์„œ ํŒŒ์ผ์ด ์ˆ˜์ •๋˜์—ˆ์„ ๋•Œ์ž…๋‹ˆ๋‹ค. ํŒŒ์ผ ์‹œ์Šคํ…œ์ด Obsidian์— ์™ธ๋ถ€์—์„œ ํŒŒ์ผ์ด ๋ณ€๊ฒฝ๋˜์—ˆ์Œ์„ ์•Œ๋ฆฌ์ž๋งˆ์ž cachedRead()๋Š” read()์™€ ๋˜‘๊ฐ™์ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Obsidian ๋‚ด์—์„œ ํŒŒ์ผ์„ ์ €์žฅํ•˜๋ฉด ์ฝ๊ธฐ ์บ์‹œ๋„ ํ”Œ๋Ÿฌ์‹œ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์ œ๋Š” ๋ณผํŠธ ๋‚ด์˜ ๋ชจ๋“  ๋งˆํฌ๋‹ค์šด ํŒŒ์ผ ๋‚ด์šฉ์„ ์ฝ๊ณ  ํ‰๊ท  ๋ฌธ์„œ ํฌ๊ธฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค:

import { Notice, Plugin } from 'obsidian';
 
export default class ExamplePlugin extends Plugin {
  async onload() {
    this.addRibbonIcon('info', 'ํŒŒ์ผ ํ‰๊ท  ๊ธธ์ด ๊ณ„์‚ฐ', async () => {
      const fileLength = await this.averageFileLength();
      new Notice(`ํŒŒ์ผ ํ‰๊ท  ๊ธธ์ด๋Š” ${fileLength}์ž ์ž…๋‹ˆ๋‹ค.`);
    });
  }
 
  async averageFileLength(): Promise<number> {
    const { vault } = this.app;
 
    const fileContents: string[] = await Promise.all(
      vault.getMarkdownFiles().map((file) => vault.cachedRead(file))
    );
 
    let totalLength = 0;
    fileContents.forEach((content) => {
      totalLength += content.length;
    });
 
    return totalLength / fileContents.length;
  }
}

ํŒŒ์ผ ์ˆ˜์ •

๊ธฐ์กด ํŒŒ์ผ์— ํ…์ŠคํŠธ ๋‚ด์šฉ์„ ์“ฐ๋ ค๋ฉด Vault.modify()๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

function writeCurrentDate(vault: Vault, file: TFile): Promise<void> {
  return vault.modify(file, `์˜ค๋Š˜์€ ${new Intl.DateTimeFormat().format(new Date())}์ž…๋‹ˆ๋‹ค.`);
}

ํŒŒ์ผ์˜ ํ˜„์žฌ ๋‚ด์šฉ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ˆ˜์ •ํ•˜๋ ค๋ฉด Vault.process()๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. ๋‘ ๋ฒˆ์งธ ์ธ์ž๋Š” ํ˜„์žฌ ํŒŒ์ผ ๋‚ด์šฉ์„ ์ œ๊ณตํ•˜๊ณ  ์ˆ˜์ •๋œ ๋‚ด์šฉ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ฝœ๋ฐฑ์ž…๋‹ˆ๋‹ค.

// emojify๋Š” ๋ชจ๋“  :)๋ฅผ ๐Ÿ™‚๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.
function emojify(vault: Vault, file: TFile): Promise<string> {
  return vault.process(file, (data) => {
    return data.replace(':)', '๐Ÿ™‚');
  })
}

Vault.process()๋Š” Vault.read()์™€ Vault.modify() ์œ„์— ๊ตฌ์ถ•๋œ ์ถ”์ƒํ™”๋กœ, ํ˜„์žฌ ๋‚ด์šฉ์„ ์ฝ๊ณ  ์—…๋ฐ์ดํŠธ๋œ ๋‚ด์šฉ์„ ์“ฐ๋Š” ์‚ฌ์ด์— ํŒŒ์ผ์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Œ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ์˜๋„ํ•˜์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ ์†์‹ค์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ํ•ญ์ƒ Vault.process()๋ฅผ Vault.read()/Vault.modify()๋ณด๋‹ค ์„ ํ˜ธํ•˜์„ธ์š”.

๋น„๋™๊ธฐ ์ˆ˜์ •

Vault.process()๋Š” ๋™๊ธฐ์‹ ์ˆ˜์ •๋งŒ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ์„ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ:

  1. Vault.cachedRead()๋กœ ํŒŒ์ผ์„ ์ฝ์œผ์„ธ์š”.
  2. ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜์„ธ์š”.
  3. Vault.process()๋กœ ํŒŒ์ผ์„ ์—…๋ฐ์ดํŠธํ•˜์„ธ์š”.

process() ์ฝœ๋ฐฑ์˜ data๊ฐ€ cachedRead()๊ฐ€ ๋ฐ˜ํ™˜ํ•œ ๋ฐ์ดํ„ฐ์™€ ๊ฐ™์€์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”. ๊ฐ™์ง€ ์•Š๋‹ค๋ฉด ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค์— ์˜ํ•ด ํŒŒ์ผ์ด ๋ณ€๊ฒฝ๋œ ๊ฒƒ์ด๋ฏ€๋กœ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ™•์ธ์„ ์š”์ฒญํ•˜๊ฑฐ๋‚˜ ๋‹ค์‹œ ์‹œ๋„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŒŒ์ผ ์‚ญ์ œ

ํŒŒ์ผ์„ ์‚ญ์ œํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค: delete()์™€ trash(). ์‚ฌ์šฉ์ž๊ฐ€ ๋งˆ์Œ์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ• ์ง€ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์‚ฌ์šฉํ•  ๋ฉ”์„œ๋“œ๊ฐ€ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

  • delete()๋Š” ํ”์  ์—†์ด ํŒŒ์ผ์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
  • trash()๋Š” ํŒŒ์ผ์„ ํœด์ง€ํ†ต์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.

trash()๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์‹œ์Šคํ…œ์˜ ํœด์ง€ํ†ต์œผ๋กœ ์ด๋™ํ• ์ง€, ์•„๋‹ˆ๋ฉด ์‚ฌ์šฉ์ž ๋ณผํŠธ ๋ฃจํŠธ์˜ .trash ํด๋”๋กœ ์ด๋™ํ• ์ง€ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŒŒ์ผ์ธ๊ฐ€ ํด๋”์ธ๊ฐ€?

์ผ๋ถ€ ์ž‘์—…์€ ํŒŒ์ผ์ด๋‚˜ ํด๋”์ผ ์ˆ˜ ์žˆ๋Š” TAbstractFile ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฑฐ๋‚˜ ๋ฐ›์•„๋“ค์ž…๋‹ˆ๋‹ค. TAbstractFile์„ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— ํ•ญ์ƒ ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์„ ํ™•์ธํ•˜์„ธ์š”.

const folderOrFile = this.app.vault.getAbstractFileByPath('folderOrFile');
 
if (folderOrFile instanceof TFile) {
  console.log('ํŒŒ์ผ์ž…๋‹ˆ๋‹ค!');
} else if (folderOrFile instanceof TFolder) {
  console.log('ํด๋”์ž…๋‹ˆ๋‹ค!');
}