---
title: 플러그인 접근 권한 작성
sidebar:
  order: 11
i18nReady: true
---

import { Steps } from '@astrojs/starlight/components';
import ShowSolution from '@components/ShowSolution.astro';
import Cta from '@fragments/cta.mdx';
import TranslationNote from '@components/i18n/TranslationNote.astro';

이 장의 연습 목적은 자신만의 플러그인을 작성할 때 플러그인 접근 권한을 어떻게 만드는지 더 깊이 이해하는 것입니다.

연습이 끝나면 자신의 플러그인에 간단한 접근 권한을 설정할 수 있는 능력을 갖추게 될 것입니다.
접근 권한이 부분적으로 자동 생성되고 수동으로 생성되는 Tauri 플러그인의 예를 보여줍니다.

<Steps>

1. ### Tauri 플러그인 만들기

   이 예에서는 Tauri [`cli`](/reference/cli/)[영어 사이트]를 이용하여 Tauri 플러그인의 소스 코드 구조체를 부트스트랩(시작)합니다.
   모든 [필수 사항](/ko/start/prerequisites/)이 설치되었는지 확인하고, `cargo tauri info`를 실행하여 Tauri CLI가 올바른 버전인지 확인하십시오.

   출력 결과에는 `tauri-cli`의 버전이 "`2.x`"임이 표시되어야 합니다.
   이 단계별 설명 절차에서는 `pnpm`을 사용하여 진행하지만, 다른 패키지 관리자도 선택할 수 있습니다. 선택한 패키지 관리자에 따라 명령을 바꿔주세요.

   최신 버전이 설치되어 있다면, Tauri CLI를 사용하여 플러그인 생성을 시작할 수 있습니다.

    {/* prettier-ignore */}
    <ShowSolution>
    ```sh
    mkdir -p tauri-learning
    cd tauri-learning
    cargo tauri plugin new test
    cd tauri-plugin-test
    pnpm install
    pnpm build
    cargo build
    ```
    </ShowSolution>
    
2. ### 새로운 명령 만들기

    실용적이고 간단한 예를 보여주기 위해, 명령이 사용자 입력을 임시 폴더 내의 파일에 쓰고 해당 파일에 사용자 지정 헤더를 추가하는 처리를 가정해 보겠습니다.

    이 명령에 "`write_custom_file`"이라는 이름을 붙이고, "`src/commands.rs`"에 구현하고, 플러그인 빌더에 추가하여 프론트엔드에 공개합니다.

    Tauri의 코어 유틸리티는 이 명령의 `allow`(허용) 및 `deny`(거부) 권한을 자동으로 생성하므로 이 접근 권한에 대해 고려할 필요가 없습니다.

    <ShowSolution>

    명령 구현: (아래 마지막 부분이 "`write_custome_file`" 명령을 추가하는 부분)

    ```rust title="src/commands.rs" ins={15-22} ins=", Manager"
    use tauri::{AppHandle, command, Runtime};

    use crate::models::*;
    use crate::Result;
    use crate::TestExt;

    #[command]
    pub(crate) async fn ping<R: Runtime>(
        app: AppHandle<R>,
        payload: PingRequest,
    ) -> Result<PingResponse> {
        app.test1().ping(payload)
    }

    #[command]
    pub(crate) async fn write_custom_file<R: Runtime>(
        user_input: String,
        app: AppHandle<R>,
    ) -> Result<String> {
        std::fs::write(app.path().temp_dir().unwrap(), user_input)?;
        Ok("success".to_string())
    }

    ```

    새로운 명령에 내장된 접근 권한을 자동 생성합니다:

    ```rust title="src/build.rs" ins=""write_custom_file""
    const COMMANDS: &[&str] = &["ping", "write_custom_file"];
    ```

    이러한 내장 접근 권한은 Tauri 빌드 시스템에 의해 자동으로 생성되며, `permissions/autogenerated/commands` 폴더에 표시됩니다.
    기본적으로 `enable-<command>`(〇〇 허용) 및 `deny-<command>`(〇〇 거부) 접근 권한이 생성됩니다.

    </ShowSolution>

3. ### 새로운 명령 공개

   "2단계"는 실제 명령 구현을 작성하기 위한 것이었습니다.
   이어서 해당 명령을 프론트엔드에 공개하여 사용할 수 있도록 합니다.

   <ShowSolution>

   새로 구현한 명령에 프론트엔드 IPC 요청을 전달하기 위한 "호출 핸들러"를 생성하도록 Tauri 빌더를 설정합니다:

   ```rust title="src/lib.rs"  ins="commands::write_custom_file,"
   pub fn init<R: Runtime>() -> TauriPlugin<R> {
   Builder::new("test")
       .invoke_handler(tauri::generate_handler![
           commands::ping,
           commands::write_custom_file,
       ])
       .setup(|app, api| {
           #[cfg(mobile)]
           let test = mobile::init(app, api)?;
           #[cfg(desktop)]
           let test = desktop::init(app, api)?;
           app.manage(test);

           // 명령에서 접근할 수 있도록 "상태"를 관리(manage)
           app.manage(MyState::default());
           Ok(())
       })
       .build()
   }
   ```

   프론트엔드 모듈에 새로운 명령을 공개합니다.

   이 단계는 이 샘플 애플리케이션이 프론트엔드 모듈을 정상적으로 가져오기 위해 필수적입니다.
   이 처리는 편의를 위한 것이며 보안에 영향을 미치지 않습니다. 왜냐하면 명령 핸들러가 이미 생성되어 있고 명령을 프론트엔드에서 수동으로 호출할 수 있기 때문입니다.

   ```ts title="guest-js/index.ts" ins={11-13}
   import { invoke } from '@tauri-apps/api/core';

   export async function ping(value: string): Promise<string | null> {
     return await invoke<{ value?: string }>('plugin:test|ping', {
       payload: {
         value,
       },
     }).then((r) => (r.value ? r.value : null));
   }

   export async function writeCustomFile(user_input: string): Promise<string> {
     return await invoke('plugin:test|write_custom_file', {
       userInput: user_input,
     });
   }
   ```

   :::tip
   호출 매개변수는 "카멜 케이스 CamelCase"(결합하는 단어의 첫 글자를 대문자)로 작성해야 합니다. 이 예에서는 "`user_input`"이 아니라 "`userInput`"이라는 표기법이 됩니다.
   :::

   자신의 패키지가 빌드되는지 확인합니다:

   ```
   pnpm build
   ```

   </ShowSolution>

4. ### 기본 플러그인 접근 권한 정의

    이 플러그인 예에서는 기본적으로 `write_custom_file` 명령을 공개해야 하므로 이 명령을 `default.toml`의 접근 권한(permissions)에 추가해야 합니다.

    <ShowSolution>

    방금 공개한 새로운 명령을 허용하기 위해 이 명령을 기본 접근 권한 세트에 추가합니다.

    ```toml title="permissions/default.toml" ins=", "allow-write-custom-file""
    "$schema" = "schemas/schema.json"
    [default]
    description = "Default permissions for the plugin"
    permissions = ["allow-ping", "allow-write-custom-file"]
    ```

    </ShowSolution>

5. ### 샘플 애플리케이션에서 테스트 명령 호출

    생성된 플러그인 디렉토리 구조에는 `examples/tauri-app` 폴더가 있으며, 여기에는 플러그인을 테스트하기 위해 즉시 사용할 수 있는 Tauri 애플리케이션이 포함되어 있습니다.

    새로운 명령을 추가했으므로 해당 새로운 명령을 호출하도록 프론트엔드를 약간 변경해야 합니다.

    <ShowSolution>

    ```svelte title="src/App.svelte" del={11-13,42-45} ins={14-16,45-49}
    <script>
      import Greet from './lib/Greet.svelte'
      import { ping, writeCustomFile } from 'tauri-plugin-test-api'

      let response = ''

      function updateResponse(returnValue) {
        response += `[${new Date().toLocaleTimeString()}]` + (typeof returnValue === 'string' ? returnValue : JSON.stringify(returnValue)) + '<br>'
      }

      function _ping() {
        ping("Pong!").then(updateResponse).catch(updateResponse)
      }
      function _writeCustomFile() {
        writeCustomFile("HELLO FROM TAURI PLUGIN").then(updateResponse).catch(updateResponse)
      }
    </script>

    <main class="container">
      <h1>Welcome to Tauri!</h1>

      <div class="row">
        <a href="https://vitejs.dev" target="_blank">
          <img src="/vite.svg" class="logo vite" alt="Vite Logo" />
        </a>
        <a href="https://tauri.app" target="_blank">
          <img src="/tauri.svg" class="logo tauri" alt="Tauri Logo" />
        </a>
        <a href="https://svelte.dev" target="_blank">
          <img src="/svelte.svg" class="logo svelte" alt="Svelte Logo" />
        </a>
      </div>

      <p>
        Click on the Tauri, Vite, and Svelte logos to learn more.
      </p>

      <div class="row">
        <Greet />
      </div>

      <div>
        <button on:click="{_ping}">Ping</button>
        <div>{@html response}</div>
      </div>
      <div>
        <button on:click="{_writeCustomFile}">Write</button>
        <div>{@html response}</div>
      </div>


    </main>

    <style>
      .logo.vite:hover {
        filter: drop-shadow(0 0 2em #747bff);
      }

      .logo.svelte:hover {
        filter: drop-shadow(0 0 2em #ff3e00);
      }
    </style>
    ```

    이를 실행하고 "쓰기" 버튼을 누르면 다음과 같은 화면이 표시되어야 합니다:

    ```

    success (성공)

    ```

    임시 폴더에는 새로 구현된 플러그인 명령의 메시지가 포함된 `test.txt` 파일이 있어야 합니다.
    🥳

    </ShowSolution>

 </Steps>
