> ## 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_POPUP_BOTTOM`
  * 중앙: `PUBLIC_TEST_UNIT_ID_POPUP_CENTER`
</Note>

***

## AdropPopupAd

### 생성자

```dart theme={null}
AdropPopupAd({
  required String unitId,
  AdropPopupListener? listener,
  Color? closeTextColor,
  Color? hideForTodayTextColor,
  Color? backgroundColor,
})
```

**파라미터**

| 파라미터                    | 타입                   | 필수 | 설명                      |
| ----------------------- | -------------------- | -- | ----------------------- |
| `unitId`                | `String`             | Y  | 애드컨트롤 콘솔에서 생성한 유닛 ID    |
| `listener`              | `AdropPopupListener` | N  | 광고 이벤트 리스너              |
| `closeTextColor`        | `Color`              | N  | 닫기 버튼 텍스트 색상            |
| `hideForTodayTextColor` | `Color`              | N  | "오늘 하루 보지 않기" 버튼 텍스트 색상 |
| `backgroundColor`       | `Color`              | 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>` | 광고를 닫습니다      |
| `dispose()` | `Future<void>` | 리소스를 해제합니다    |

***

## 기본 사용법

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

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

  @override
  State<PopupExample> createState() => _PopupExampleState();
}

class _PopupExampleState extends State<PopupExample> {
  bool isLoaded = false;
  AdropPopupAd? popupAd;

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

  void _createPopupAd() {
    popupAd = AdropPopupAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropPopupListener(
        onAdReceived: (ad) {
          debugPrint('팝업 광고 로드 성공: ${ad.creativeId}');
          setState(() {
            isLoaded = true;
          });
        },
        onAdFailedToReceive: (ad, errorCode) {
          debugPrint('팝업 광고 로드 실패: $errorCode');
        },
        onAdClicked: (ad) {
          debugPrint('팝업 광고 클릭');
        },
        onAdImpression: (ad) {
          debugPrint('팝업 광고 노출');
        },
        onAdDidPresentFullScreen: (ad) {
          debugPrint('팝업 광고 표시됨');
        },
        onAdDidDismissFullScreen: (ad) {
          debugPrint('팝업 광고 닫힘');
        },
        onAdFailedToShowFullScreen: (ad, errorCode) {
          debugPrint('팝업 광고 표시 실패: $errorCode');
        },
      ),
    );
  }

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

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

  @override
  void dispose() {
    popupAd?.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('광고 표시'),
            ),
          ],
        ),
      ),
    );
  }
}
```

***

## AdropPopupListener

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

### 콜백 함수

```dart theme={null}
AdropPopupListener(
  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) {
    // 광고 표시 실패
  },
  onAdVideoStart: (AdropAd ad) {
    // 동영상 광고 재생 시작
  },
  onAdVideoEnd: (AdropAd ad) {
    // 동영상 광고 재생 종료
  },
)
```

### 콜백 설명

| 콜백                           | 설명                      |
| ---------------------------- | ----------------------- |
| `onAdReceived`               | 광고 로드 성공 시 호출           |
| `onAdClicked`                | 광고 클릭 시 호출              |
| `onAdImpression`             | 광고 노출 시 호출              |
| `onAdWillPresentFullScreen`  | 광고가 표시되기 직전 호출 (iOS 전용) |
| `onAdDidPresentFullScreen`   | 광고가 표시된 후 호출            |
| `onAdWillDismissFullScreen`  | 광고가 닫히기 직전 호출 (iOS 전용)  |
| `onAdDidDismissFullScreen`   | 광고가 닫힌 후 호출             |
| `onAdFailedToReceive`        | 광고 로드 실패 시 호출           |
| `onAdFailedToShowFullScreen` | 광고 표시 실패 시 호출           |
| `onAdVideoStart`             | 동영상 광고 재생 시작 시 호출       |
| `onAdVideoEnd`               | 동영상 광고 재생 종료 시 호출       |

***

## 버튼 색상 커스터마이징

팝업 광고의 닫기 버튼과 "오늘 하루 보지 않기" 버튼의 색상을 커스터마이징할 수 있습니다.

```dart theme={null}
popupAd = AdropPopupAd(
  unitId: 'YOUR_UNIT_ID',
  // 닫기 버튼 텍스트 색상
  closeTextColor: Colors.white,
  // "오늘 하루 보지 않기" 버튼 텍스트 색상
  hideForTodayTextColor: Colors.white,
  // 버튼 배경 색상
  backgroundColor: Colors.black87,
  listener: AdropPopupListener(
    onAdReceived: (ad) {
      setState(() {
        isLoaded = true;
      });
    },
  ),
);
```

### 색상 옵션

| 파라미터                    | 설명                      | 기본값     |
| ----------------------- | ----------------------- | ------- |
| `closeTextColor`        | 닫기 버튼 텍스트 색상            | 시스템 기본값 |
| `hideForTodayTextColor` | "오늘 하루 보지 않기" 버튼 텍스트 색상 | 시스템 기본값 |
| `backgroundColor`       | 버튼 배경 색상                | 시스템 기본값 |

***

## 팝업 위치

팝업 광고의 위치는 애드컨트롤 콘솔에서 유닛 생성 시 설정합니다.

### 중앙 팝업

화면 중앙에 팝업이 표시됩니다. 공지사항, 이벤트 알림 등에 적합합니다.

```dart theme={null}
// 중앙 팝업용 유닛 ID 사용
popupAd = AdropPopupAd(
  unitId: 'YOUR_CENTER_POPUP_UNIT_ID',
  listener: AdropPopupListener(...),
);
```

### 하단 팝업

화면 하단에 팝업이 표시됩니다. 앱 실행 시 배너 형태의 알림에 적합합니다.

```dart theme={null}
// 하단 팝업용 유닛 ID 사용
popupAd = AdropPopupAd(
  unitId: 'YOUR_BOTTOM_POPUP_UNIT_ID',
  listener: AdropPopupListener(...),
);
```

***

## 오늘 하루 보지 않기

사용자가 "오늘 하루 보지 않기"를 선택하면, 해당 기기에서 당일 자정까지 광고가 표시되지 않습니다.

<Note>
  "오늘 하루 보지 않기" 상태에서 광고를 로드하면 `adHideForToday` 에러가 반환됩니다.
</Note>

```dart theme={null}
onAdFailedToReceive: (ad, errorCode) {
  if (errorCode == AdropErrorCode.adHideForToday) {
    debugPrint('사용자가 오늘 하루 보지 않기를 선택했습니다.');
    // 광고 대신 다른 콘텐츠 표시
  }
}
```

***

## 광고 재생성

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

```dart theme={null}
class _PopupState extends State<PopupWidget> {
  bool isLoaded = false;
  bool isShown = false;
  AdropPopupAd? popupAd;

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

  void _createPopupAd() {
    popupAd?.dispose();
    popupAd = AdropPopupAd(
      unitId: 'YOUR_UNIT_ID',
      backgroundColor: Colors.black87,
      listener: AdropPopupListener(
        onAdReceived: (ad) {
          setState(() {
            isLoaded = true;
          });
        },
        onAdDidDismissFullScreen: (ad) {
          setState(() {
            isShown = true;
          });
          // 다음 표시를 위해 새 광고 준비
          _createPopupAd();
          popupAd?.load();
        },
      ),
    );

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

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

***

## 에러 처리

```dart theme={null}
class _PopupState extends State<PopupWidget> {
  bool isLoaded = false;
  AdropErrorCode? errorCode;
  AdropPopupAd? popupAd;

  void _createPopupAd() {
    popupAd = AdropPopupAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropPopupListener(
        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.adHideForToday:
        debugPrint('사용자가 오늘 하루 보지 않기를 선택했습니다.');
        break;
      case AdropErrorCode.adShown:
        debugPrint('이미 표시된 광고입니다.');
        break;
      default:
        debugPrint('오류 발생: ${error.code}');
    }
  }

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

***

## 모범 사례

### 1. 앱 시작 시 팝업 표시

앱 실행 시 공지사항이나 이벤트를 팝업으로 표시합니다.

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

class _SplashScreenState extends State<SplashScreen> {
  AdropPopupAd? popupAd;

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

  void _loadPopupAd() {
    popupAd = AdropPopupAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropPopupListener(
        onAdReceived: (ad) {
          // 광고가 로드되면 표시
          popupAd?.show();
        },
        onAdFailedToReceive: (ad, errorCode) {
          // 광고 로드 실패 시 바로 메인 화면으로 이동
          if (errorCode != AdropErrorCode.adHideForToday) {
            _navigateToMain();
          }
        },
        onAdDidDismissFullScreen: (ad) {
          // 팝업 닫힌 후 메인 화면으로 이동
          _navigateToMain();
        },
      ),
    );
    popupAd?.load();
  }

  void _navigateToMain() {
    Navigator.of(context).pushReplacement(
      MaterialPageRoute(builder: (_) => const MainScreen()),
    );
  }

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

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: CircularProgressIndicator(),
      ),
    );
  }
}
```

### 2. 테마에 맞는 색상 사용

앱의 테마에 맞게 버튼 색상을 설정합니다.

```dart theme={null}
void _createPopupAd() {
  final isDarkMode = Theme.of(context).brightness == Brightness.dark;

  popupAd = AdropPopupAd(
    unitId: 'YOUR_UNIT_ID',
    closeTextColor: isDarkMode ? Colors.white : Colors.black,
    hideForTodayTextColor: isDarkMode ? Colors.white70 : Colors.black54,
    backgroundColor: isDarkMode ? Colors.grey[800] : Colors.grey[200],
    listener: AdropPopupListener(...),
  );
}
```

### 3. 리소스 관리

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

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

***

## 전체 예제

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

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

  @override
  State<PopupAdPage> createState() => _PopupAdPageState();
}

class _PopupAdPageState extends State<PopupAdPage> {
  bool isLoaded = false;
  bool isLoading = false;
  bool isShown = false;
  AdropErrorCode? errorCode;
  AdropPopupAd? popupAd;

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

  void _createPopupAd() {
    popupAd?.dispose();
    popupAd = AdropPopupAd(
      unitId: 'PUBLIC_TEST_UNIT_ID_POPUP_BOTTOM', // 테스트 유닛 ID
      closeTextColor: Colors.white,
      hideForTodayTextColor: Colors.white70,
      backgroundColor: Colors.black87,
      listener: AdropPopupListener(
        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('광고 표시됨');
        },
        onAdWillDismissFullScreen: (ad) {
          debugPrint('광고 닫힘 예정');
        },
        onAdDidDismissFullScreen: (ad) {
          debugPrint('광고 닫힘');
          setState(() {
            isShown = true;
          });
        },
        onAdFailedToReceive: (ad, error) {
          debugPrint('광고 로드 실패: $error');
          setState(() {
            isLoaded = false;
            isLoading = false;
            errorCode = error;
          });
        },
        onAdFailedToShowFullScreen: (ad, error) {
          debugPrint('광고 표시 실패: $error');
          setState(() {
            errorCode = error;
          });
        },
      ),
    );

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

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

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

  @override
  void dispose() {
    popupAd?.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)
                    ? () => _createPopupAd()
                    : 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),
                ),
                if (errorCode == AdropErrorCode.adHideForToday)
                  const Text(
                    '(사용자가 오늘 하루 보지 않기를 선택했습니다)',
                    style: TextStyle(color: Colors.grey, fontSize: 12),
                  ),
              ],
            ],
          ),
        ),
      ),
    );
  }
}
```

***

## 백필 광고

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

```dart theme={null}
popupAd = AdropPopupAd(
  unitId: 'YOUR_UNIT_ID',
  listener: AdropPopupListener(
    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/targeting">
    맞춤형 광고를 위한 타겟팅 설정하기
  </Card>

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

  <Card title="예제" href="/ko/sdk/flutter/examples">
    다양한 구현 예제 살펴보기
  </Card>
</CardGroup>
