> ## 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.

# 전면 광고

> Flutter 앱에서 전면 광고를 구현하는 방법을 안내합니다.

## 개요

전면 광고는 화면 전체를 덮는 풀스크린 광고입니다. 자연스러운 전환 시점(예: 게임 레벨 완료, 페이지 이동 등)에 표시하면 효과적입니다.

### 주요 특징

* 화면 전체를 덮는 풀스크린 광고
* 이미지 및 동영상 광고 지원
* 풀스크린 이벤트 콜백 제공
* 로드와 표시를 분리하여 유연한 타이밍 제어

<Note>
  개발 환경에서는 테스트 유닛 ID를 사용하세요: `PUBLIC_TEST_UNIT_ID_INTERSTITIAL`
</Note>

***

## AdropInterstitialAd

### 생성자

```dart theme={null}
AdropInterstitialAd({
  required String unitId,
  AdropInterstitialListener? listener,
})
```

**파라미터**

| 파라미터       | 타입                          | 필수 | 설명                   |
| ---------- | --------------------------- | -- | -------------------- |
| `unitId`   | `String`                    | Y  | 애드컨트롤 콘솔에서 생성한 유닛 ID |
| `listener` | `AdropInterstitialListener` | N  | 광고 이벤트 리스너           |

### 속성

| 속성               | 타입               | 설명                 |
| ---------------- | ---------------- | ------------------ |
| `isLoaded`       | `bool`           | 광고 로드 완료 여부        |
| `unitId`         | `String`         | 광고 유닛 ID           |
| `creativeId`     | `String`         | 크리에이티브 ID          |
| `txId`           | `String`         | 트랜잭션 ID            |
| `campaignId`     | `String`         | 캠페인 ID             |
| `destinationURL` | `String`         | 목적지 URL            |
| `browserTarget`  | `BrowserTarget?` | 브라우저 타겟 (외부 또는 내부) |

### 메서드

| 메서드         | 반환 타입          | 설명                                                |
| ----------- | -------------- | ------------------------------------------------- |
| `load()`    | `Future<void>` | 광고를 로드합니다                                         |
| `show()`    | `Future<void>` | 광고를 화면에 표시합니다                                     |
| `close()`   | `Future<void>` | 전면 광고를 프로그래밍 방식으로 닫습니다 (Android 전용, iOS에서는 no-op) |
| `dispose()` | `Future<void>` | 리소스를 해제합니다                                        |

<Note>
  `close()` 호출 시 광고가 닫히고 `onAdDidDismissFullScreen` 콜백이 이어서 호출됩니다. `onAdBackButtonPressed` 콜백에서 `close()`를 호출하지 않으면 광고는 닫히지 않고 콜백만 수신됩니다. 닫을지 여부는 개발자가 결정합니다.
</Note>

***

## 기본 사용법

```dart theme={null}
import 'package:flutter/material.dart';
import 'package:adrop_ads_flutter/adrop_ads_flutter.dart';

class InterstitialExample extends StatefulWidget {
  const InterstitialExample({super.key});

  @override
  State<InterstitialExample> createState() => _InterstitialExampleState();
}

class _InterstitialExampleState extends State<InterstitialExample> {
  bool isLoaded = false;
  AdropInterstitialAd? interstitialAd;

  @override
  void initState() {
    super.initState();
    _createInterstitialAd();
  }

  void _createInterstitialAd() {
    interstitialAd = AdropInterstitialAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropInterstitialListener(
        onAdReceived: (ad) {
          debugPrint('전면 광고 로드 성공: ${ad.creativeId}');
          setState(() {
            isLoaded = true;
          });
        },
        onAdFailedToReceive: (ad, errorCode) {
          debugPrint('전면 광고 로드 실패: $errorCode');
        },
        onAdClicked: (ad) {
          debugPrint('전면 광고 클릭');
        },
        onAdDidPresentFullScreen: (ad) {
          debugPrint('전면 광고 표시됨');
        },
        onAdDidDismissFullScreen: (ad) {
          debugPrint('전면 광고 닫힘');
        },
        onAdFailedToShowFullScreen: (ad, errorCode) {
          debugPrint('전면 광고 표시 실패: $errorCode');
        },
        onAdBackButtonPressed: (ad) {
          debugPrint('백버튼 누름');
        },
      ),
    );
  }

  void loadAd() {
    interstitialAd?.load();
  }

  void showAd() {
    if (isLoaded) {
      interstitialAd?.show();
    }
  }

  @override
  void dispose() {
    interstitialAd?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('전면 광고 예제')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: loadAd,
              child: const Text('광고 로드'),
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: isLoaded ? showAd : null,
              child: const Text('광고 표시'),
            ),
          ],
        ),
      ),
    );
  }
}
```

***

## AdropInterstitialListener

전면 광고 이벤트를 처리하는 리스너입니다.

### 콜백 함수

```dart theme={null}
AdropInterstitialListener(
  onAdReceived: (AdropAd ad) {
    // 광고 로드 성공
  },
  onAdClicked: (AdropAd ad) {
    // 광고 클릭
  },
  onAdImpression: (AdropAd ad) {
    // 광고 노출
  },
  onAdWillPresentFullScreen: (AdropAd ad) {
    // 광고가 표시되기 직전 (iOS 전용)
  },
  onAdDidPresentFullScreen: (AdropAd ad) {
    // 광고가 표시됨
  },
  onAdWillDismissFullScreen: (AdropAd ad) {
    // 광고가 닫히기 직전 (iOS 전용)
  },
  onAdDidDismissFullScreen: (AdropAd ad) {
    // 광고가 닫힘
  },
  onAdFailedToReceive: (AdropAd ad, AdropErrorCode errorCode) {
    // 광고 로드 실패
  },
  onAdFailedToShowFullScreen: (AdropAd ad, AdropErrorCode errorCode) {
    // 광고 표시 실패
  },
  onAdBackButtonPressed: (AdropAd ad) {
    // 백버튼 누름 (Android 전용)
  },
)
```

### 콜백 설명

| 콜백                           | 설명                                       |
| ---------------------------- | ---------------------------------------- |
| `onAdReceived`               | 광고 로드 성공 시 호출                            |
| `onAdClicked`                | 광고 클릭 시 호출                               |
| `onAdImpression`             | 광고 노출 시 호출                               |
| `onAdWillPresentFullScreen`  | 광고가 표시되기 직전 호출 (iOS 전용)                  |
| `onAdDidPresentFullScreen`   | 광고가 표시된 후 호출                             |
| `onAdWillDismissFullScreen`  | 광고가 닫히기 직전 호출 (iOS 전용)                   |
| `onAdDidDismissFullScreen`   | 광고가 닫힌 후 호출                              |
| `onAdFailedToReceive`        | 광고 로드 실패 시 호출                            |
| `onAdFailedToShowFullScreen` | 광고 표시 실패 시 호출                            |
| `onAdBackButtonPressed`      | 전면 광고가 표시된 상태에서 백버튼을 누르면 호출 (Android 전용) |

***

## 광고 라이프사이클

```mermaid theme={null}
graph TD
    A[생성] --> B[load 호출]
    B --> C{로드 결과}
    C -->|성공| D[onAdReceived]
    C -->|실패| E[onAdFailedToReceive]
    D --> F[show 호출]
    F --> G{표시 결과}
    G -->|성공| H[onAdWillPresentFullScreen]
    G -->|실패| I[onAdFailedToShowFullScreen]
    H --> J[onAdDidPresentFullScreen]
    J --> K[사용자 상호작용]
    K -->|클릭| L[onAdClicked]
    K -->|닫기| M[onAdWillDismissFullScreen]
    K -->|백버튼 Android| P[onAdBackButtonPressed]
    P -->|close 호출| M
    M --> N[onAdDidDismissFullScreen]
    N --> O[dispose 또는 재생성]
```

***

## 광고 재생성

전면 광고는 일회성입니다. 한 번 표시된 광고는 다시 표시할 수 없으므로, 새로운 광고를 로드하려면 인스턴스를 재생성해야 합니다.

```dart theme={null}
class _InterstitialState extends State<InterstitialWidget> {
  bool isLoaded = false;
  bool isShown = false;
  AdropInterstitialAd? interstitialAd;

  @override
  void initState() {
    super.initState();
    _createInterstitialAd();
  }

  void _createInterstitialAd() {
    interstitialAd?.dispose();
    interstitialAd = AdropInterstitialAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropInterstitialListener(
        onAdReceived: (ad) {
          setState(() {
            isLoaded = true;
          });
        },
        onAdDidPresentFullScreen: (ad) {
          setState(() {
            isShown = true;
          });
        },
        onAdDidDismissFullScreen: (ad) {
          // 광고가 닫힌 후 새 광고 준비
          _createInterstitialAd();
          interstitialAd?.load();
        },
      ),
    );

    setState(() {
      isLoaded = false;
      isShown = false;
    });
  }

  @override
  void dispose() {
    interstitialAd?.dispose();
    super.dispose();
  }
}
```

***

## 에러 처리

```dart theme={null}
class _InterstitialState extends State<InterstitialWidget> {
  bool isLoaded = false;
  AdropErrorCode? errorCode;
  AdropInterstitialAd? interstitialAd;

  void _createInterstitialAd() {
    interstitialAd = AdropInterstitialAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropInterstitialListener(
        onAdReceived: (ad) {
          setState(() {
            isLoaded = true;
            errorCode = null;
          });
        },
        onAdFailedToReceive: (ad, error) {
          setState(() {
            isLoaded = false;
            errorCode = error;
          });
          _handleError(error);
        },
        onAdFailedToShowFullScreen: (ad, error) {
          setState(() {
            errorCode = error;
          });
          _handleError(error);
        },
      ),
    );
  }

  void _handleError(AdropErrorCode error) {
    switch (error) {
      case AdropErrorCode.network:
        debugPrint('네트워크 오류가 발생했습니다.');
        break;
      case AdropErrorCode.adNoFill:
        debugPrint('노출할 광고가 없습니다.');
        break;
      case AdropErrorCode.adShown:
        debugPrint('이미 표시된 광고입니다.');
        break;
      default:
        debugPrint('오류 발생: ${error.code}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ElevatedButton(
          onPressed: interstitialAd?.load,
          child: const Text('광고 로드'),
        ),
        ElevatedButton(
          onPressed: isLoaded ? interstitialAd?.show : null,
          child: const Text('광고 표시'),
        ),
        if (errorCode != null)
          Text(
            '에러: ${errorCode?.code}',
            style: const TextStyle(color: Colors.red),
          ),
      ],
    );
  }
}
```

***

## 모범 사례

### 1. 적절한 타이밍에 표시

전면 광고는 자연스러운 전환 시점에 표시하세요.

```dart theme={null}
// 좋은 예: 레벨 완료 후
void onLevelComplete() {
  showInterstitialAd();
  navigateToNextLevel();
}

// 나쁜 예: 앱 시작 직후
void onAppStart() {
  showInterstitialAd(); // 사용자 경험 저하
}
```

### 2. 미리 로드하기

광고를 미리 로드해두면 표시 시점에 지연 없이 보여줄 수 있습니다.

```dart theme={null}
class GameScreen extends StatefulWidget {
  @override
  State<GameScreen> createState() => _GameScreenState();
}

class _GameScreenState extends State<GameScreen> {
  AdropInterstitialAd? interstitialAd;
  bool isAdReady = false;

  @override
  void initState() {
    super.initState();
    _preloadAd();
  }

  void _preloadAd() {
    interstitialAd = AdropInterstitialAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropInterstitialListener(
        onAdReceived: (ad) {
          setState(() {
            isAdReady = true;
          });
        },
        onAdDidDismissFullScreen: (ad) {
          // 다음 광고 미리 로드
          _preloadAd();
        },
      ),
    );
    interstitialAd?.load();
  }

  void showAdIfReady() {
    if (isAdReady) {
      interstitialAd?.show();
      setState(() {
        isAdReady = false;
      });
    }
  }

  @override
  void dispose() {
    interstitialAd?.dispose();
    super.dispose();
  }
}
```

### 3. 빈도 제한

사용자 경험을 위해 광고 표시 빈도를 제한하세요.

```dart theme={null}
class AdManager {
  static int _interstitialCount = 0;
  static const int _maxInterstitialsPerSession = 3;
  static DateTime? _lastInterstitialTime;
  static const Duration _minInterval = Duration(minutes: 2);

  static bool canShowInterstitial() {
    if (_interstitialCount >= _maxInterstitialsPerSession) {
      return false;
    }

    if (_lastInterstitialTime != null) {
      final elapsed = DateTime.now().difference(_lastInterstitialTime!);
      if (elapsed < _minInterval) {
        return false;
      }
    }

    return true;
  }

  static void onInterstitialShown() {
    _interstitialCount++;
    _lastInterstitialTime = DateTime.now();
  }
}
```

### 4. 리소스 관리

사용하지 않는 광고 인스턴스는 반드시 해제하세요.

```dart theme={null}
@override
void dispose() {
  interstitialAd?.dispose();
  super.dispose();
}
```

***

## 전체 예제

```dart theme={null}
import 'package:flutter/material.dart';
import 'package:adrop_ads_flutter/adrop_ads_flutter.dart';

class InterstitialAdPage extends StatefulWidget {
  const InterstitialAdPage({super.key});

  @override
  State<InterstitialAdPage> createState() => _InterstitialAdPageState();
}

class _InterstitialAdPageState extends State<InterstitialAdPage> {
  bool isLoaded = false;
  bool isLoading = false;
  bool isShown = false;
  AdropErrorCode? errorCode;
  AdropInterstitialAd? interstitialAd;

  @override
  void initState() {
    super.initState();
    _createInterstitialAd();
  }

  void _createInterstitialAd() {
    interstitialAd?.dispose();
    interstitialAd = AdropInterstitialAd(
      unitId: 'PUBLIC_TEST_UNIT_ID_INTERSTITIAL', // 테스트 유닛 ID
      listener: AdropInterstitialListener(
        onAdReceived: (ad) {
          debugPrint('광고 로드 성공');
          debugPrint('크리에이티브 ID: ${ad.creativeId}');
          debugPrint('캠페인 ID: ${ad.campaignId}');
          setState(() {
            isLoaded = true;
            isLoading = false;
            errorCode = null;
          });
        },
        onAdClicked: (ad) {
          debugPrint('광고 클릭: ${ad.destinationURL}');
        },
        onAdImpression: (ad) {
          debugPrint('광고 노출: ${ad.creativeId}');
        },
        onAdWillPresentFullScreen: (ad) {
          debugPrint('광고 표시 예정');
        },
        onAdDidPresentFullScreen: (ad) {
          debugPrint('광고 표시됨');
          setState(() {
            isShown = true;
          });
        },
        onAdWillDismissFullScreen: (ad) {
          debugPrint('광고 닫힘 예정');
        },
        onAdDidDismissFullScreen: (ad) {
          debugPrint('광고 닫힘');
          // 새 광고 준비
          _createInterstitialAd();
        },
        onAdFailedToReceive: (ad, error) {
          debugPrint('광고 로드 실패: $error');
          setState(() {
            isLoaded = false;
            isLoading = false;
            errorCode = error;
          });
        },
        onAdFailedToShowFullScreen: (ad, error) {
          debugPrint('광고 표시 실패: $error');
          setState(() {
            errorCode = error;
          });
        },
        onAdBackButtonPressed: (ad) {
          debugPrint('백버튼 누름');
        },
      ),
    );

    setState(() {
      isLoaded = false;
      isShown = false;
      errorCode = null;
    });
  }

  void loadAd() {
    setState(() {
      isLoading = true;
      errorCode = null;
    });
    interstitialAd?.load();
  }

  void showAd() {
    if (isLoaded) {
      interstitialAd?.show();
    }
  }

  @override
  void dispose() {
    interstitialAd?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('전면 광고 예제'),
      ),
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // 로드 버튼
              ElevatedButton(
                onPressed: isLoading ? null : loadAd,
                child: Text(isLoading ? '로딩 중...' : '광고 로드'),
              ),
              const SizedBox(height: 16),

              // 표시 버튼
              ElevatedButton(
                onPressed: isLoaded ? showAd : null,
                child: const Text('광고 표시'),
              ),
              const SizedBox(height: 16),

              // 재설정 버튼
              TextButton(
                onPressed: (isShown || errorCode != null)
                    ? () => _createInterstitialAd()
                    : null,
                child: const Text('재설정'),
              ),
              const SizedBox(height: 24),

              // 상태 표시
              Text('로드됨: ${isLoaded ? "예" : "아니오"}'),
              Text('표시됨: ${isShown ? "예" : "아니오"}'),

              // 에러 표시
              if (errorCode != null) ...[
                const SizedBox(height: 16),
                Text(
                  '에러: ${errorCode?.code}',
                  style: const TextStyle(color: Colors.red),
                ),
              ],
            ],
          ),
        ),
      ),
    );
  }
}
```

***

## 백필 광고

백필 광고가 활성화된 경우, 직광고가 없을 때 자동으로 백필 광고가 로드됩니다. 에러 코드를 통해 백필 관련 상태를 처리할 수 있습니다.

```dart theme={null}
interstitialAd = AdropInterstitialAd(
  unitId: 'YOUR_UNIT_ID',
  listener: AdropInterstitialListener(
    onAdReceived: (ad) {
      debugPrint('광고 로드됨: ${ad.creativeId}');
    },
    onAdFailedToReceive: (ad, errorCode) {
      switch (errorCode) {
        case AdropErrorCode.adNoFill:
          debugPrint('직광고 없음');
          break;
        case AdropErrorCode.backfillNoFill:
          debugPrint('백필 광고도 없습니다');
          break;
        default:
          debugPrint('광고 로드 실패: ${errorCode.code}');
      }
    },
  ),
);
```

<Note>
  백필 광고를 사용하려면 네이티브 플랫폼에 백필 의존성을 추가해야 합니다. [시작하기](/ko/sdk/flutter/overview)를 참고하세요.
</Note>

***

## 다음 단계

<CardGroup cols={2}>
  <Card title="보상형 광고" href="/ko/sdk/flutter/rewarded">
    보상을 제공하는 보상형 광고 구현하기
  </Card>

  <Card title="팝업 광고" href="/ko/sdk/flutter/popup">
    팝업 형태로 표시되는 광고 구현하기
  </Card>

  <Card title="레퍼런스" href="/ko/sdk/flutter/reference">
    타입, 메서드, 에러 코드 참고하기
  </Card>
</CardGroup>
