> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rev.iq/llms.txt
> Use this file to discover all available pages before exploring further.

# 데스크톱 앱

> Electron, Tauri 등 모든 데스크톱 앱에 광고를 게재하세요

RevIQ는 웹 기술로 UI를 렌더링하는 모든 데스크톱 앱에서 작동합니다. 앱이
윈도우에 HTML을 표시한다면 RevIQ로 수익화할 수 있습니다.

## 지원 프레임워크

* **Electron**: `rev-utils` 헬퍼를 통한 공식 지원 ([아래 참조](#electron))
* **Tauri**
* **NW\.js, CEF, Wails, Neutralino** 등 Chromium 또는 webview 기반 프레임워크
* **webview를 내장한 네이티브 앱**: WebView2(Windows), WKWebView(macOS),
  WebKitGTK(Linux)

웹사이트와 동일한 방식으로 앱이 로드하는 페이지에 RevIQ 스크립트를 추가하세요.
[프론트엔드](/ko/quickstart/frontend)를 참고하세요. 그런 다음 앱이 아래 두 가지
데스크톱 요구 사항을 충족하는지 확인하세요.

## 데스크톱 요구 사항

1. **광고 클릭은 사용자의 기본 브라우저에서 엽니다.** 사용자가 광고를 클릭하면 앱
   윈도우를 이동시키지 말고 기본 브라우저에서 랜딩 페이지를 여세요. 링크는 실제
   클릭이 있을 때만 엽니다. 사용자가 발생시키지 않은 이동과 팝업, 광고가 시작한
   파일 다운로드는 차단하세요.
2. **표준 브라우저 User Agent를 전송합니다.** 많은 프레임워크가 User Agent 문자열에
   프레임워크 이름과 앱 이름(예: `Electron/…`, `MyApp/…`)을 추가합니다. 이 부분을
   제거해 일반 브라우저와 같은 문자열로 만드세요. 비표준 User Agent는 IVT(무효
   트래픽)로 플래그될 수 있습니다. <Tooltip tip="IVT 벤더는
   비표준 사용자 에이전트를 포함한 여러 신호를 확인하며, 자동화된 트래픽으로
   잘못 플래그할 수 있습니다"> \[?] </Tooltip>

## Electron

`rev-utils`가 두 가지 요구 사항을 모두 처리합니다.

### 설치

rev-utils 라이브러리를 설치하여 Electron 설정에 광고 완전 호환성을 추가합니다.
패키지는 수시로 업데이트될 수 있습니다.

```sh theme={null}
npm i -D rev-utils@https://r2.reviq.app/rev-utils-v1.0.3.tgz
```

### 사용법

`new BrowserWindow`로 윈도우를 생성한 후, 윈도우와 `electron` 글로벌을
`setupRevUtils`에 전달합니다.

```js main.js theme={null}
const { setupRevUtils } = require("rev-utils");

// 여기서 글로벌 electron을 임포트하는 것에 주의하세요!
const electron = require("electron"); 
// 이 모듈 객체에서 디스트럭처링하여 임포트할 수 있습니다
const { BrowserWindow } = electron;

const myWindow = new BrowserWindow();

setupRevUtils(myWindow, electron);
```

<Info>
  내부적으로 `setupRevUtils`는 윈도우에 대해 두 가지 함수를 호출합니다

  * `setupLinkHandler`는 광고를 열고 (악의적인 동작을 차단합니다!)
  * `setUserAgent`는 User Agent 문자열에서 비표준 부분을 제거합니다. 이는
    IVT(무효 트래픽)로 플래그될 수 있습니다. <Tooltip tip="IVT 벤더는
    비표준 사용자 에이전트를 포함한 여러 신호를 확인하며, 자동화된 트래픽으로
    잘못 플래그할 수 있습니다"> \[?] </Tooltip>
</Info>

## Tauri

RevIQ 스크립트와 광고 슬롯을 넣은 페이지를 자사 도메인에 만들고([프론트엔드](/ko/quickstart/frontend) 참고),
표준 User Agent를 보내고 광고 클릭을 사용자의 브라우저에서 여는 자식 webview에 표시합니다.

```rust src-tauri/src/ads.rs theme={null}
use tauri::webview::{NewWindowResponse, WebviewBuilder};
use tauri::{LogicalPosition, LogicalSize, WebviewUrl, Window};
use tauri_plugin_opener::OpenerExt;

// A standard Chrome User Agent. Ad providers don't recognize Tauri's default.
const ADS_USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36";

pub fn add_ads_webview(window: &Window) -> tauri::Result<()> {
    let url = "https://your-domain.com/app-ads.html".parse().unwrap();
    let builder = WebviewBuilder::new("ads", WebviewUrl::External(url))
        // Ads render in iframes, so the click script must run in every frame.
        .initialization_script_for_all_frames(include_str!("ads-init.js"))
        .user_agent(ADS_USER_AGENT)
        // Ad clicks go through open_ad_link, never through new windows.
        .on_new_window(|_, _| NewWindowResponse::Deny);

    window.add_child(
        builder,
        LogicalPosition::new(0.0, 0.0),
        LogicalSize::new(300.0, 250.0),
    )?;
    Ok(())
}

#[tauri::command]
pub fn open_ad_link(app: tauri::AppHandle, url: String) {
    // Only web pages; never file:// or custom schemes.
    if url.starts_with("https://") || url.starts_with("http://") {
        let _ = app.opener().open_url(url, None::<String>);
    }
}
```

<Info>
  `Window::add_child`는 Tauri의 `unstable` 기능에 포함되어 있습니다. `src-tauri/Cargo.toml`의
  `tauri`에서 활성화하세요: `tauri = { version = "2", features = ["unstable"] }`.
</Info>

이 스크립트는 webview의 모든 프레임에서 실행됩니다. 광고는 iframe 안에 렌더링되므로 각 프레임이 클릭을
알리고 링크를 자사 페이지로 전달하며, 페이지는 실제 클릭 직후에만 링크를 엽니다.

```js src-tauri/src/ads-init.js theme={null}
// Runs in every frame of the ads webview.
const AD_PAGE_ORIGIN = "https://your-domain.com";
// How long after a click an ad may open its landing page.
const CLICK_WINDOW_MS = 1000;

// Your page (the top frame) opens a landing page only right after a real click.
if (window.top === window) {
  let lastClick = -Infinity;
  window.addEventListener("message", (event) => {
    if (event.data?.adClick) lastClick = performance.now();
    if (
      event.data?.adOpenUrl &&
      performance.now() - lastClick < CLICK_WINDOW_MS
    ) {
      lastClick = -Infinity;
      window.__TAURI_INTERNALS__?.invoke("open_ad_link", {
        url: event.data.adOpenUrl,
      });
    }
  });
}

// Every frame, ad iframes included, reports clicks and hands links to the
// top frame instead of navigating.
document.addEventListener(
  "click",
  (event) => {
    window.top.postMessage({ adClick: true }, AD_PAGE_ORIGIN);
    const link = event.target.closest?.("a");
    if (link?.href) {
      event.preventDefault();
      window.top.postMessage({ adOpenUrl: link.href }, AD_PAGE_ORIGIN);
    }
  },
  true,
);

window.open = (url) => {
  if (url) window.top.postMessage({ adOpenUrl: String(url) }, AD_PAGE_ORIGIN);
  return null;
};
```

opener 플러그인과 명령을 등록합니다.

```rust src-tauri/src/main.rs theme={null}
tauri::Builder::default()
    .plugin(tauri_plugin_opener::init())
    .invoke_handler(tauri::generate_handler![ads::open_ad_link])
```

광고 페이지는 자사 도메인에서 로드되는 원격 콘텐츠이므로, [capability](https://v2.tauri.app/security/capabilities/)로 해당 오리진을 허용하지 않으면 Tauri가 명령 호출을 차단합니다. `ads` webview에 필요한 명령 하나만 허용하세요:

```json src-tauri/capabilities/ads.json theme={null}
{
  "$schema": "../gen/schemas/remote-schema.json",
  "identifier": "ads",
  "webviews": ["ads"],
  "remote": { "urls": ["https://your-domain.com"] },
  "permissions": ["core:default", "allow-open-ad-link"]
}
```

해당 명령에 대한 권한을 정의합니다:

```toml src-tauri/permissions/open-ad-link.toml theme={null}
[[permission]]
identifier = "allow-open-ad-link"
description = "Allow the ads webview to open an ad's landing page in the browser."
commands.allow = ["open_ad_link"]
```

<Tip>
  Windows에서는 광고 webview의 추적 방지 수준을 없음으로 설정하세요. `with_webview`를 통해
  `ICoreWebView2Profile3::SetPreferredTrackingPreventionLevel`를 `COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_NONE`으로 호출합니다. 그렇지 않으면 WebView2가 입찰자가 재방문 사용자를 식별하는 쿠키를
  차단해 입찰가가 낮아집니다.
</Tip>

## 기타 프레임워크

각 프레임워크의 API로 두 가지 요구 사항을 충족하세요. webview에 표준 브라우저 User Agent를 설정하고,
광고 프레임의 링크 클릭과 `window.open`을 실제 클릭 직후에만 시스템 브라우저에서 열도록 하세요.
전체 패턴은 위 Tauri 예시를 참고하세요.

<Note>
  여기에 없는 프레임워크를 사용하시나요? 웹 콘텐츠를 렌더링한다면 작동합니다.
  설정에 도움이 필요하면 RevIQ 담당자에게 문의하세요.
</Note>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.