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

# 네이티브 광고

> React Native 앱에서 네이티브 광고를 구현하는 방법을 안내합니다.

## 개요

네이티브 광고는 앱의 디자인에 맞게 광고 UI를 자유롭게 커스터마이징할 수 있는 광고 포맷입니다. 광고 소재(이미지, 동영상), 제목, 설명, CTA 버튼 등의 요소를 개별적으로 배치하여 자연스러운 사용자 경험을 제공할 수 있습니다.

### 주요 기능

* **완전한 UI 커스터마이징**: 앱의 디자인 시스템에 맞게 광고 레이아웃을 자유롭게 구성
* **다양한 미디어 지원**: 이미지 및 동영상 광고 소재 지원
* **유연한 클릭 처리**: 전체 클릭 또는 커스텀 클릭 처리 가능
* **프로필 정보 표시**: 광고주 프로필 로고 및 이름 지원
* **백필 광고 지원**: 백필 광고 여부 확인 가능

***

## AdropNativeAd 클래스

네이티브 광고를 로드하고 관리하는 메인 클래스입니다.

### 생성자

```typescript theme={null}
new AdropNativeAd(
  unitId: string,
  useCustomClick?: boolean,
  preferredAdChoicesPosition?: AdropAdChoicesPosition
)
```

<ParamField path="unitId" type="string" required>
  광고 지면 ID (콘솔에서 발급)
</ParamField>

<ParamField path="useCustomClick" type="boolean" default="false">
  커스텀 클릭 처리 사용 여부

  * `true`: 개발자가 직접 클릭 처리를 구현 (비디오 광고 컨트롤러 사용 가능)
  * `false`: AdropNativeAdView가 모든 클릭 이벤트를 자동으로 처리
</ParamField>

<ParamField path="preferredAdChoicesPosition" type="AdropAdChoicesPosition" default="AdropAdChoicesPosition.topRight">
  백필 네이티브 광고의 AdChoices 마크 표시 위치
</ParamField>

### 메서드

| 메서드         | 설명             |
| ----------- | -------------- |
| `load()`    | 광고를 요청하고 로드합니다 |
| `destroy()` | 광고 리소스를 해제합니다  |

### 프로퍼티

| 프로퍼티             | 타입                      | 설명                      |
| ---------------- | ----------------------- | ----------------------- |
| `isLoaded`       | `boolean`               | 광고 로드 완료 여부 (읽기 전용)     |
| `unitId`         | `string`                | 광고 지면 ID (읽기 전용)        |
| `useCustomClick` | `boolean`               | 커스텀 클릭 처리 사용 여부 (읽기 전용) |
| `creativeId`     | `string`                | 광고 소재 ID (읽기 전용)        |
| `txId`           | `string`                | 트랜잭션 ID (읽기 전용)         |
| `campaignId`     | `string`                | 캠페인 ID (읽기 전용)          |
| `isBackfilled`   | `boolean`               | 백필 광고 여부 (읽기 전용)        |
| `isVideoAd`      | `boolean`               | 비디오 광고 여부 (읽기 전용)       |
| `browserTarget`  | `BrowserTarget`         | 브라우저 타겟 설정 (읽기 전용)      |
| `properties`     | `AdropNativeProperties` | 광고 속성 정보 (읽기 전용)        |
| `listener`       | `AdropNativeAdListener` | 광고 이벤트 리스너              |

***

## AdropNativeAdView 컴포넌트

네이티브 광고를 표시할 컨테이너 컴포넌트입니다.

### Props

```typescript theme={null}
interface Props extends ViewProps {
  nativeAd?: AdropNativeAd
}
```

<ParamField path="nativeAd" type="AdropNativeAd">
  표시할 네이티브 광고 인스턴스
</ParamField>

### 사용 예시

```tsx theme={null}
<AdropNativeAdView nativeAd={nativeAd} style={styles.adContainer}>
  {/* 광고 요소들 */}
</AdropNativeAdView>
```

<Note>
  `AdropNativeAdView`는 광고 컨텍스트를 제공하므로, 모든 광고 요소 뷰들은 이 컴포넌트 내부에 배치해야 합니다.
</Note>

***

## 네이티브 광고 요소 뷰

### AdropProfileLogoView

광고주 프로필 로고를 표시하는 이미지 컴포넌트입니다.

```tsx theme={null}
<AdropProfileLogoView style={styles.profileLogo} />
```

**Props**: `Omit<ImageProps, 'source'>` (React Native의 Image 컴포넌트 속성, `source`는 자동으로 설정됩니다)

### AdropProfileNameView

광고주 프로필 이름을 표시하는 텍스트 컴포넌트입니다.

```tsx theme={null}
<AdropProfileNameView style={styles.profileName} />
```

**Props**: `TextProps` (React Native의 Text 컴포넌트 속성)

### AdropHeadLineView

광고 제목을 표시하는 텍스트 컴포넌트입니다.

```tsx theme={null}
<AdropHeadLineView style={styles.headline} />
```

**Props**: `TextProps` (React Native의 Text 컴포넌트 속성)

### AdropBodyView

광고 본문을 표시하는 텍스트 컴포넌트입니다.

```tsx theme={null}
<AdropBodyView style={styles.body} />
```

**Props**: `TextProps` (React Native의 Text 컴포넌트 속성)

### AdropMediaView

광고 이미지 또는 동영상을 표시하는 미디어 컴포넌트입니다.

```tsx theme={null}
<AdropMediaView style={styles.media} />
```

**Props**: `ViewProps` (React Native의 View 컴포넌트 속성)

<Warning>
  **네이티브 광고의 영상 크리에이티브**는 반드시 `properties.creative`(HTML 페이로드)를 `WebView`로 렌더링해야 합니다. `properties.asset`을 `react-native-video` 등 자체 플레이어에 직접 전달하면 SDK의 영상 트래킹 파이프라인을 거치지 않게 되어 **VTR이 측정되지 않고** `onAdVideoStart` / `onAdVideoEnd` 콜백도 호출되지 않습니다. [영상 트래킹과 VTR](#영상-트래킹과-vtr) 섹션을 참고하세요.
</Warning>

### AdropCallToActionView

행동 유도 문구(CTA)를 표시하는 텍스트 컴포넌트입니다.

```tsx theme={null}
<AdropCallToActionView style={styles.cta} />
```

**Props**: `TextProps` (React Native의 Text 컴포넌트 속성)

### AdropAdvertiserView

광고주 이름을 표시하는 텍스트 컴포넌트입니다.

```tsx theme={null}
<AdropAdvertiserView style={styles.advertiser} />
```

**Props**: `TextProps` (React Native의 Text 컴포넌트 속성)

### AdropIconView

광고 아이콘을 표시하는 이미지 컴포넌트입니다.

```tsx theme={null}
<AdropIconView style={styles.icon} />
```

**Props**: `Omit<ImageProps, 'source'>` (React Native의 Image 컴포넌트 속성, `source`는 자동으로 설정됩니다)

***

## AdropNativeAdListener 인터페이스

광고 이벤트를 처리하는 리스너 인터페이스입니다.

```typescript theme={null}
interface AdropNativeAdListener {
  onAdReceived?: (ad: AdropNativeAd) => void
  onAdClicked?: (ad: AdropNativeAd) => void
  onAdImpression?: (ad: AdropNativeAd) => void
  onAdFailedToReceive?: (ad: AdropNativeAd, errorCode?: any) => void
  onAdVideoStart?: (ad: AdropNativeAd) => void
  onAdVideoEnd?: (ad: AdropNativeAd) => void
}
```

| 콜백 메서드                | 설명                   |
| --------------------- | -------------------- |
| `onAdReceived`        | 광고 수신 성공 시 호출됩니다     |
| `onAdClicked`         | 광고 클릭 시 호출됩니다        |
| `onAdImpression`      | 광고 노출 시 호출됩니다        |
| `onAdFailedToReceive` | 광고 수신 실패 시 호출됩니다     |
| `onAdVideoStart`      | 동영상 광고 재생 시작 시 호출됩니다 |
| `onAdVideoEnd`        | 동영상 광고 재생 종료 시 호출됩니다 |

***

## AdropNativeProperties 타입

광고 속성 정보를 담는 타입입니다.

```typescript theme={null}
type AdropNativeProperties = {
  icon?: string                      // 아이콘 이미지 URL
  cover?: string                     // 커버 이미지 URL
  headline?: string                  // 광고 제목
  body?: string                      // 광고 본문
  creative?: string                  // 광고 소재 HTML (WebView 렌더링용)
  asset?: string                     // 에셋 정보
  destinationURL?: string            // 목적지 URL
  advertiserURL?: string             // 광고주 URL
  accountTag?: string                // 계정 태그
  creativeTag?: string               // 소재 태그
  advertiser?: string                // 광고주 이름
  callToAction?: string              // 행동 유도 문구
  profile?: AdropNativeProfile       // 프로필 정보
  extra?: Record<string, string>     // 추가 정보
  isBackfilled?: boolean             // 백필 광고 여부
}
```

***

## AdropNativeProfile 타입

광고주 프로필 정보를 담는 타입입니다.

```typescript theme={null}
type AdropNativeProfile = {
  displayName: string                // 프로필 표시 이름
  displayLogo: string                // 프로필 로고 이미지 URL
}
```

***

## 구현 예제

다음은 네이티브 광고를 구현하는 전체 예제입니다.

```tsx theme={null}
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import {
  AdropBodyView,
  AdropHeadLineView,
  AdropMediaView,
  AdropNativeAd,
  AdropNativeAdView,
  AdropProfileLogoView,
  AdropProfileNameView,
} from 'adrop-ads-react-native'
import type { AdropNativeAdListener } from 'adrop-ads-react-native'
import { WebView } from 'react-native-webview'
import {
  Button,
  Dimensions,
  ScrollView,
  StyleSheet,
  Text,
  View,
  Linking,
  Platform,
} from 'react-native'

const NativeAdExample: React.FC = () => {
  const [nativeAd, setNativeAd] = useState<AdropNativeAd>()
  const [isLoaded, setIsLoaded] = useState(false)
  const [errorCode, setErrorCode] = useState('')

  // URL 열기 핸들러
  const openUrl = useCallback((url: string) => {
    Linking.openURL(url).catch((err) =>
      console.error('URL 열기 실패:', err)
    )
  }, [])

  // 광고 이벤트 리스너
  const listener = useMemo(
    (): AdropNativeAdListener => ({
      onAdReceived: (ad) => {
        console.log('광고 수신:', ad.unitId, ad.properties)
        setIsLoaded(true)
        setErrorCode('')
      },
      onAdFailedToReceive: (_, error) => {
        console.log('광고 수신 실패:', error)
        setErrorCode(error)
      },
      onAdClicked: (ad) => {
        console.log('광고 클릭:', ad.unitId)
      },
      onAdImpression: (ad) => {
        console.log('광고 노출:', ad.unitId)
      },
    }),
    []
  )

  // 광고 초기화
  const initialize = useCallback(
    (unitId: string) => {
      const adropNativeAd = new AdropNativeAd(unitId)
      adropNativeAd.listener = listener

      setNativeAd((prev) => {
        prev?.destroy()
        return adropNativeAd
      })

      setIsLoaded(false)
      setErrorCode('')
    },
    [listener]
  )

  // 컴포넌트 마운트 시 광고 초기화
  useEffect(() => {
    initialize('YOUR_UNIT_ID')

    // 컴포넌트 언마운트 시 광고 해제
    return () => {
      nativeAd?.destroy()
    }
  }, [])

  // 광고 로드
  const load = () => nativeAd?.load()

  // 광고 뷰 렌더링
  const adView = useMemo(() => {
    if (!isLoaded) return null

    return (
      <AdropNativeAdView
        nativeAd={nativeAd}
        style={{
          ...styles.adContainer,
          width: Dimensions.get('window').width,
        }}
      >
        {/* 프로필 영역 */}
        <View style={styles.rowContainer}>
          <AdropProfileLogoView style={styles.profileLogo} />
          <AdropProfileNameView style={styles.profileName} />
        </View>

        {/* 광고 제목 */}
        <AdropHeadLineView style={styles.headline} />

        {/* 광고 본문 */}
        <AdropBodyView style={styles.body} />

        {/* 백필 광고 또는 비디오가 아닌 광고는 AdropMediaView, 비디오 광고는 WebView */}
        {!nativeAd?.isVideoAd || nativeAd?.isBackfilled ? (
          // 백필 광고 또는 비디오가 아닌 광고: AdropMediaView 사용
          <AdropMediaView style={styles.media} />
        ) : (
          // 비디오 광고: WebView로 렌더링
          <WebView
            source={{
              html: nativeAd?.properties?.creative ?? '',
            }}
            style={styles.media}
            javaScriptEnabled={true}
            mediaPlaybackRequiresUserAction={false}
            allowsInlineMediaPlayback={true}
            scrollEnabled={false}
            onNavigationStateChange={(event) => {
              // Android WebView 이벤트
              if (
                event.url &&
                event.url !== 'about:blank' &&
                !event.url.startsWith('data:')
              ) {
                openUrl(event.url)
              }
            }}
            onOpenWindow={(event) => {
              // iOS WebView 이벤트 (window.open)
              if (event.nativeEvent?.targetUrl) {
                openUrl(event.nativeEvent.targetUrl)
              }
            }}
          />
        )}
      </AdropNativeAdView>
    )
  }, [isLoaded, nativeAd, openUrl])

  return (
    <ScrollView>
      <View style={styles.container}>
        {/* 광고 로드 버튼 */}
        <View style={styles.button}>
          <Button title="광고 로드" onPress={load} />
        </View>

        {/* 광고 뷰 */}
        {adView}

        {/* 에러 표시 */}
        {errorCode && (
          <Text style={styles.error}>
            에러: {errorCode}
          </Text>
        )}
      </View>
    </ScrollView>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    paddingVertical: 5,
    paddingHorizontal: 16,
  },
  button: {
    marginVertical: 8,
  },
  adContainer: {
    paddingHorizontal: 16,
  },
  rowContainer: {
    flexDirection: 'row',
    justifyContent: 'flex-start',
    alignItems: 'center',
    marginBottom: 8,
  },
  profileLogo: {
    width: 32,
    height: 32,
    marginRight: 8,
    borderRadius: 16,
  },
  profileName: {
    fontSize: 14,
    fontWeight: 'bold',
    color: 'black',
  },
  headline: {
    fontSize: 16,
    fontWeight: 'bold',
    color: 'black',
    marginTop: 8,
  },
  body: {
    fontSize: 14,
    color: 'black',
    marginVertical: 8,
  },
  media: {
    width: '100%',
    height: 360,
    marginTop: 8,
  },
  error: {
    color: 'red',
    marginVertical: 8,
  },
})

export default NativeAdExample
```

***

## 테스트 유닛 ID

개발 및 테스트 시 다음 테스트 유닛 ID를 사용하세요.

| 플랫폼         | 포맷              | 테스트 유닛 ID                               |
| ----------- | --------------- | --------------------------------------- |
| Android/iOS | 네이티브 (이미지)      | `PUBLIC_TEST_UNIT_ID_NATIVE`            |
| Android/iOS | 네이티브 비디오 (16:9) | `PUBLIC_TEST_UNIT_ID_NATIVE_VIDEO_16_9` |
| Android/iOS | 네이티브 비디오 (9:16) | `PUBLIC_TEST_UNIT_ID_NATIVE_VIDEO_9_16` |

<Warning>
  프로덕션 배포 전에 반드시 실제 유닛 ID로 교체하세요.
</Warning>

***

## 에러 처리

광고 로드 실패 시 `onAdFailedToReceive` 콜백에서 에러 코드를 받을 수 있습니다.

### 주요 에러 코드

| 에러 코드                           | 설명            |
| ------------------------------- | ------------- |
| `AdropErrorCode.network`        | 네트워크 오류       |
| `AdropErrorCode.internal`       | 내부 오류         |
| `AdropErrorCode.initialize`     | 초기화 오류        |
| `AdropErrorCode.invalidUnit`    | 유효하지 않은 유닛 ID |
| `AdropErrorCode.adNoFill`       | 광고 없음         |
| `AdropErrorCode.adLoading`      | 광고 로딩 중       |
| `AdropErrorCode.backfillNoFill` | 백필 광고도 없음     |

### 에러 처리 예제

```typescript theme={null}
const listener: AdropNativeAdListener = {
  onAdFailedToReceive: (ad, errorCode) => {
    switch (errorCode) {
      case AdropErrorCode.adNoFill:
        console.log('광고 없음 - 광고 영역 숨기기')
        break
      case AdropErrorCode.network:
        console.log('네트워크 오류 - 재시도')
        setTimeout(() => ad.load(), 3000)
        break
      default:
        console.log('광고 로드 실패:', errorCode)
    }
  }
}
```

***

## 모범 사례

### 1. 리소스 해제

컴포넌트 언마운트 시 반드시 광고 리소스를 해제하세요.

```typescript theme={null}
useEffect(() => {
  const ad = new AdropNativeAd('YOUR_UNIT_ID')
  setNativeAd(ad)

  return () => {
    ad.destroy()
  }
}, [])
```

### 2. 비디오 광고 및 백필 광고 처리

비디오 광고 여부 및 백필 광고 여부에 따라 적절한 렌더링 방식을 선택하세요.

```tsx theme={null}
{!nativeAd?.isVideoAd || nativeAd?.isBackfilled ? (
  <AdropMediaView style={styles.media} />
) : (
  <WebView source={{ html: nativeAd?.properties?.creative ?? '' }} />
)}
```

### 3. URL 처리

WebView에서 URL 클릭 시 외부 브라우저로 열리도록 처리하세요.

```tsx theme={null}
<WebView
  onNavigationStateChange={(event) => {
    if (event.url && event.url !== 'about:blank') {
      Linking.openURL(event.url)
    }
  }}
/>
```

### 4. 광고 요소 선택적 사용

앱의 디자인에 맞게 필요한 광고 요소만 선택적으로 사용하세요.

```tsx theme={null}
<AdropNativeAdView nativeAd={nativeAd}>
  {/* 최소 구성: 미디어와 제목만 표시 */}
  <AdropHeadLineView style={styles.headline} />
  <AdropMediaView style={styles.media} />
</AdropNativeAdView>
```

***

## 영상 트래킹과 VTR

Adrop은 네이티브 광고의 영상 지표 — VTR(영상 완수율), `onAdVideoStart`, `onAdVideoEnd` — 를 SDK 미디어 컨테이너 안에서 영상이 렌더링될 때에만 수집합니다. React Native에서는 [구현 예제](#구현-예제) 섹션처럼 `properties.creative`(HTML 페이로드)를 `WebView`로 렌더링하는 것이 그 방식입니다.

`properties.asset` 필드는 원본 이미지 또는 영상 파일의 URL을 반환합니다. 이 값은 썸네일 표시나 매체사 자체 분석 등 **부가 메타데이터** 용도로 노출된 것이며, 재생을 직접 처리하기 위한 것이 아닙니다.

<Warning>
  영상 네이티브 광고의 `properties.asset` URL을 자체 영상 플레이어(`react-native-video`, `expo-av` 또는 서드파티 플레이어)에 직접 전달하지 마세요. SDK 미디어 컨테이너를 거치지 않으면 다음 현상이 발생합니다.

  * 해당 지면에서 **VTR(영상 완수율)이 수집되지 않습니다.**
  * `onAdVideoStart` / `onAdVideoEnd` 콜백이 **호출되지 않습니다.**
  * 해당 유닛의 영상 성과 집계가 누락되거나 0으로 보고됩니다.

  클릭(`onAdClicked`)과 노출(`onAdImpression`) 트래킹은 `AdropNativeAdView`에 연결되어 있으므로 계속 동작하지만, 영상 관련 신호는 손실됩니다.
</Warning>

### 권장 패턴

영상 크리에이티브는 `AdropNativeAdView` 내부에 배치한 `WebView`로 HTML 크리에이티브를 렌더링하세요.

```tsx theme={null}
{!nativeAd?.isVideoAd || nativeAd?.isBackfilled ? (
  <AdropMediaView style={styles.media} />
) : (
  <WebView
    source={{ html: nativeAd?.properties?.creative ?? '' }}
    style={styles.media}
    javaScriptEnabled
    mediaPlaybackRequiresUserAction={false}
    allowsInlineMediaPlayback
    scrollEnabled={false}
  />
)}
```

`AdropNativeAd`의 `isVideoAd`와 `isBackfilled` 플래그를 사용하면 asset URL을 직접 검사하지 않고도 `<AdropMediaView />`(이미지 또는 백필 크리에이티브)와 `<WebView />`(다이렉트 영상 크리에이티브)를 분기할 수 있습니다.

<Note>
  특정 지면이 반드시 자체 플레이어를 사용해야 하고 VTR이 측정되지 않는 것을 수용한다면, 광고를 `AdropNativeAdView`에 바인딩한 채로 유지해 클릭과 노출 어트리뷰션은 계속 확보할 수 있습니다. 다만 해당 지면을 내부 리포트에서 **비-VTR 인벤토리**로 분류하고 SDK가 측정한 영상 성과와 섞지 마세요.
</Note>

***

## AdChoices 위치 조정

백필 네이티브 광고에서 AdChoices 마크가 표시될 코너를 지정할 수 있습니다. `AdropNativeAd` 생성자의 세 번째 인자로 `preferredAdChoicesPosition`을 전달하세요.

```tsx theme={null}
import { AdropNativeAd, AdropAdChoicesPosition } from 'adrop-ads-react-native'

const nativeAd = new AdropNativeAd(
  'YOUR_UNIT_ID',
  false, // useCustomClick
  AdropAdChoicesPosition.bottomRight
)

nativeAd.listener = {
  onAdReceived: (ad) => console.log('수신 완료'),
  onAdFailedToReceive: (ad, errorCode) => console.log('실패', errorCode),
}

nativeAd.load()
```

| 값                                    | 설명          |
| ------------------------------------ | ----------- |
| `AdropAdChoicesPosition.topLeft`     | 좌측 상단       |
| `AdropAdChoicesPosition.topRight`    | 우측 상단 (기본값) |
| `AdropAdChoicesPosition.bottomLeft`  | 좌측 하단       |
| `AdropAdChoicesPosition.bottomRight` | 우측 하단       |

<Note>
  이 설정은 백필 네트워크에 전달되는 선호 위치 힌트로, 백필 네이티브 광고에만 적용됩니다 — 직광고에는 영향이 없습니다. 백필 네트워크가 정책상 다른 위치로 표시할 수 있습니다.
</Note>

***

## 다음 단계

<CardGroup cols={2}>
  <Card title="배너 광고" icon="rectangle-ad" href="/ko/sdk/react-native/banner">
    배너 광고 구현하기
  </Card>

  <Card title="전면 광고" icon="window-maximize" href="/ko/sdk/react-native/interstitial">
    전면 광고 구현하기
  </Card>

  <Card title="팝업 광고" icon="up-right-from-square" href="/ko/sdk/react-native/popup">
    팝업 광고 구현하기
  </Card>

  <Card title="보상형 광고" icon="gift" href="/ko/sdk/react-native/rewarded">
    보상형 광고 구현하기
  </Card>
</CardGroup>
