์ด ๊ฐ€์ด๋“œ์—์„œ๋Š” React๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. React๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๋ณ€ํ™˜ํ•˜๋ ค๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ๋ทฐ๊ฐ€ ์ด๋ฏธ ์žˆ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.

ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋นŒ๋“œํ•˜๊ธฐ ์œ„ํ•ด ๋ณ„๋„์˜ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๋Š” ์—†์ง€๋งŒ, React๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋Š” ๋ช‡ ๊ฐ€์ง€ ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:

  • React์— ๋Œ€ํ•œ ๊ธฐ์กด ๊ฒฝํ—˜์ด ์žˆ๊ณ  ์ต์ˆ™ํ•œ ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ.
  • ํ”Œ๋Ÿฌ๊ทธ์ธ์—์„œ ์žฌ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€ ๊ธฐ์กด React ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ์„ ๋•Œ.
  • ํ”Œ๋Ÿฌ๊ทธ์ธ์— ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ๋‚˜ ์ผ๋ฐ˜์ ์ธ HTML elements๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ ๋ฒˆ๊ฑฐ๋กœ์šด ๋‹ค๋ฅธ ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•  ๋•Œ.

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

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

    npm install react react-dom
  2. React์— ๋Œ€ํ•œ ํƒ€์ž… ์ •์˜๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค:

    npm install --save-dev @types/react @types/react-dom
  3. tsconfig.json์˜ compilerOptions ๊ฐ์ฒด์—์„œ JSX ์ง€์›์„ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค:

    {
      "compilerOptions": {
        "jsx": "react-jsx"
      }
    }

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

ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์— ReactView.tsx๋ผ๋Š” ์ƒˆ ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  ๋‹ค์Œ ๋‚ด์šฉ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค:

ReactView.tsx
export const ReactView = () => {
  return <h4>Hello, React!</h4>;
};

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

React ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด HTML ์š”์†Œ์— ๋งˆ์šดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ์˜ˆ์ œ๋Š” ReactView ์ปดํฌ๋„ŒํŠธ๋ฅผ this.contentEl ์š”์†Œ์— ๋งˆ์šดํŠธํ•ฉ๋‹ˆ๋‹ค:

import { StrictMode } from 'react';
import { ItemView, WorkspaceLeaf } from 'obsidian';
import { Root, createRoot } from 'react-dom/client';
import { ReactView } from './ReactView';
 
const VIEW_TYPE_EXAMPLE = 'example-view';
 
class ExampleView extends ItemView {
	root: Root | null = null;
 
	constructor(leaf: WorkspaceLeaf) {
		super(leaf);
	}
 
	getViewType() {
		return VIEW_TYPE_EXAMPLE;
	}
 
	getDisplayText() {
		return 'Example view';
	}
 
	async onOpen() {
		this.root = createRoot(this.contentEl);
		this.root.render(
			<StrictMode>
				<ReactView />,
			</StrictMode>,
		);
	}
 
	async onClose() {
		this.root?.unmount();
	}
}

createRoot ๋ฐ unmount()์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ReactDOM ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

React ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ƒํƒœ ํ‘œ์‹œ์ค„ ํ•ญ๋ชฉ๊ณผ ๊ฐ™์€ ๋ชจ๋“  HTMLElement์— ๋งˆ์šดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž‘์—…์ด ๋๋‚˜๋ฉด this.root.unmount()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ œ๋Œ€๋กœ ์ •๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

App ์ปจํ…์ŠคํŠธ ์ƒ์„ฑํ•˜๊ธฐ

React ์ปดํฌ๋„ŒํŠธ ์ค‘ ํ•˜๋‚˜์—์„œ App ๊ฐ์ฒด์— ์ ‘๊ทผํ•˜๋ ค๋ฉด ์ด๋ฅผ ์ข…์†์„ฑ์œผ๋กœ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ปค์ง€๋ฉด์„œ App ๊ฐ์ฒด๋ฅผ ๋ช‡ ๊ตฐ๋ฐ์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ ์ „์ฒด ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ๋ฅผ ํ†ตํ•ด ์ „๋‹ฌํ•˜๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

๋˜ ๋‹ค๋ฅธ ๋Œ€์•ˆ์€ ์•ฑ์— ๋Œ€ํ•œ React ์ปจํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ค์–ด React ๋ทฐ ๋‚ด๋ถ€์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์—์„œ ์ „์—ญ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  1. createContext()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ ์•ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

    context.ts
    import { createContext } from 'react';
    import { App } from 'obsidian';
     
    export const AppContext = createContext<App | undefined>(undefined);
  2. ReactView๋ฅผ ์ปจํ…์ŠคํŠธ ์ œ๊ณต์ž๋กœ ๊ฐ์‹ธ๊ณ  ์•ฑ์„ ๊ฐ’์œผ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

    view.tsx
    this.root = createRoot(this.contentEl);
    this.root.render(
      <AppContext.Provider value={this.app}>
        <ReactView />
      </AppContext.Provider>
    );
  3. ์ปดํฌ๋„ŒํŠธ์—์„œ ์ปจํ…์ŠคํŠธ๋ฅผ ๋” ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์‚ฌ์šฉ์ž ์ •์˜ ํ›…์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

    hooks.ts
    import { useContext } from 'react';
    import { AppContext } from './context';
     
    export const useApp = (): App | undefined => {
      return useContext(AppContext);
    };
  4. ReactView ๋‚ด์˜ ๋ชจ๋“  React ์ปดํฌ๋„ŒํŠธ์—์„œ ํ›…์„ ์‚ฌ์šฉํ•˜์—ฌ ์•ฑ์— ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค.

    ReactView.tsx
    import { useApp } from './hooks';
     
    export const ReactView = () => {
      const { vault } = useApp();
     
      return <h4>{vault.getName()}</h4>;
    };

์ž์„ธํ•œ ๋‚ด์šฉ์€ React ๋ฌธ์„œ์˜ Passing Data Deeply with Context ๋ฐ Reusing Logic with Custom Hooks๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.