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

# 팝업 광고

> Android 앱에서 팝업 광고를 구현하는 방법을 안내합니다.

## 개요

팝업 광고(Popup Ad)는 특정 순간에 화면에 나타나는 광고 포맷입니다. 앱 실행 시, 콘텐츠 로드 완료 시, 특정 이벤트 발생 시 등 원하는 타이밍에 표시할 수 있으며, 사용자가 닫기 버튼을 누르거나 "오늘 하루 보지 않기"를 선택하여 종료할 수 있습니다.

### 특징

* 화면 중앙 또는 하단에 표시되는 팝업 형태
* 이미지 및 동영상 광고 지원
* 닫기, 딤(배경) 클릭, "오늘 하루 보지 않기" 옵션 제공
* 배경색, 텍스트 색상 등 커스터마이징 가능
* 비침투적이면서도 효과적인 광고 경험

<Note>
  개발 환경에서는 테스트 유닛 ID를 사용하세요. [테스트 유닛 ID](#테스트-유닛-id) 섹션을 참고하세요.
</Note>

***

## 구현 단계

팝업 광고는 다음 4단계로 구현합니다:

1. **초기화** - AdropPopupAd 인스턴스 생성
2. **리스너 설정** - 광고 및 닫기 이벤트 수신을 위한 리스너 설정
3. **광고 로드** - 광고 요청 및 수신
4. **광고 표시** - 화면에 광고 표시

***

## 기본 구현

### 기본 사용법

<CodeGroup>
  ```kotlin Kotlin theme={null}
  import io.adrop.ads.popupAd.AdropPopupAd
  import io.adrop.ads.popupAd.AdropPopupAdListener
  import io.adrop.ads.popupAd.AdropPopupAdCloseListener
  import io.adrop.ads.model.AdropErrorCode

  class MainActivity : AppCompatActivity() {
      private var popupAd: AdropPopupAd? = null

      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.activity_main)

          // 팝업 광고 로드
          loadPopupAd()
      }

      // 1. 광고 초기화 및 로드
      private fun loadPopupAd() {
          popupAd = AdropPopupAd(this, "YOUR_POPUP_UNIT_ID")
          popupAd?.popupAdListener = popupAdListener
          popupAd?.closeListener = popupCloseListener
          popupAd?.load()
      }

      // 2. 광고 표시
      private fun showPopupAd() {
          popupAd?.let { ad ->
              if (ad.isLoaded) {
                  ad.show(this)
              }
          }
      }

      override fun onDestroy() {
          super.onDestroy()
          popupAd?.destroy()
      }
  }
  ```

  ```java Java theme={null}
  import io.adrop.ads.popupAd.AdropPopupAd;
  import io.adrop.ads.popupAd.AdropPopupAdListener;
  import io.adrop.ads.popupAd.AdropPopupAdCloseListener;
  import io.adrop.ads.model.AdropErrorCode;

  public class MainActivity extends AppCompatActivity {
      private AdropPopupAd popupAd;

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);

          // 팝업 광고 로드
          loadPopupAd();
      }

      // 1. 광고 초기화 및 로드
      private void loadPopupAd() {
          popupAd = new AdropPopupAd(this, "YOUR_POPUP_UNIT_ID");
          popupAd.setPopupAdListener(popupAdListener);
          popupAd.setCloseListener(popupCloseListener);
          popupAd.load();
      }

      // 2. 광고 표시
      private void showPopupAd() {
          if (popupAd != null && popupAd.isLoaded()) {
              popupAd.show(this);
          }
      }

      @Override
      protected void onDestroy() {
          super.onDestroy();
          if (popupAd != null) {
              popupAd.destroy();
          }
      }
  }
  ```
</CodeGroup>

### 리스너 구현

<CodeGroup>
  ```kotlin Kotlin theme={null}
  // 광고 리스너 구현
  private val popupAdListener = object : AdropPopupAdListener {
      // 광고 수신 성공 (필수)
      override fun onAdReceived(ad: AdropPopupAd) {
          Log.d("Adrop", "팝업 광고 수신 완료")
          // 광고가 준비되면 표시
          showPopupAd()
      }

      // 광고 수신 실패 (필수)
      override fun onAdFailedToReceive(ad: AdropPopupAd, errorCode: AdropErrorCode) {
          Log.d("Adrop", "팝업 광고 수신 실패: $errorCode")
      }

      // 광고 노출 (선택)
      override fun onAdImpression(ad: AdropPopupAd) {
          Log.d("Adrop", "팝업 광고 노출")
      }

      // 광고 클릭 (선택)
      override fun onAdClicked(ad: AdropPopupAd) {
          Log.d("Adrop", "팝업 광고 클릭")
      }

      // 팝업 광고가 표시되기 직전 (선택)
      override fun onAdWillPresentFullScreen(ad: AdropPopupAd) {
          Log.d("Adrop", "팝업 광고 표시 직전")
      }

      // 팝업 광고가 표시된 직후 (선택)
      override fun onAdDidPresentFullScreen(ad: AdropPopupAd) {
          Log.d("Adrop", "팝업 광고 표시 완료")
      }

      // 팝업 광고가 닫히기 직전 (선택)
      override fun onAdWillDismissFullScreen(ad: AdropPopupAd) {
          Log.d("Adrop", "팝업 광고 닫히기 직전")
      }

      // 팝업 광고가 닫힌 직후 (선택)
      override fun onAdDidDismissFullScreen(ad: AdropPopupAd) {
          Log.d("Adrop", "팝업 광고 닫힘")
          // 다음 광고를 미리 로드
          loadPopupAd()
      }

      // 팝업 광고 표시 실패 (선택)
      override fun onAdFailedToShowFullScreen(ad: AdropPopupAd, errorCode: AdropErrorCode) {
          Log.d("Adrop", "팝업 광고 표시 실패: $errorCode")
      }

      // 동영상 재생 시작 (선택)
      override fun onAdVideoStart(ad: AdropPopupAd) {
          Log.d("Adrop", "팝업 광고 동영상 재생 시작")
      }

      // 동영상 재생 종료 (선택)
      override fun onAdVideoEnd(ad: AdropPopupAd) {
          Log.d("Adrop", "팝업 광고 동영상 재생 종료")
      }
  }

  // 닫기 리스너 구현
  private val popupCloseListener = object : AdropPopupAdCloseListener {
      // 닫기 버튼 클릭 (선택)
      override fun onClosed(ad: AdropPopupAd) {
          Log.d("Adrop", "팝업 광고 닫기 버튼 클릭")
      }

      // 딤(배경) 클릭 (선택)
      override fun onDimClicked(ad: AdropPopupAd) {
          Log.d("Adrop", "팝업 광고 딤 영역 클릭")
      }

      // "오늘 하루 보지 않기" 클릭 (선택)
      override fun onTodayOffClicked(ad: AdropPopupAd) {
          Log.d("Adrop", "오늘 하루 보지 않기 선택")
          // 오늘 날짜를 저장하여 다음 표시를 제어
          val prefs = getSharedPreferences("adrop_prefs", Context.MODE_PRIVATE)
          prefs.edit().putLong("last_popup_hidden_date", System.currentTimeMillis()).apply()
      }
  }
  ```

  ```java Java theme={null}
  // 광고 리스너 구현
  private final AdropPopupAdListener popupAdListener = new AdropPopupAdListener() {
      // 광고 수신 성공 (필수)
      @Override
      public void onAdReceived(@NotNull AdropPopupAd ad) {
          Log.d("Adrop", "팝업 광고 수신 완료");
          // 광고가 준비되면 표시
          showPopupAd();
      }

      // 광고 수신 실패 (필수)
      @Override
      public void onAdFailedToReceive(@NotNull AdropPopupAd ad, @NotNull AdropErrorCode errorCode) {
          Log.d("Adrop", "팝업 광고 수신 실패: " + errorCode);
      }

      // 광고 노출 (선택)
      @Override
      public void onAdImpression(@NotNull AdropPopupAd ad) {
          Log.d("Adrop", "팝업 광고 노출");
      }

      // 광고 클릭 (선택)
      @Override
      public void onAdClicked(@NotNull AdropPopupAd ad) {
          Log.d("Adrop", "팝업 광고 클릭");
      }

      // 팝업 광고가 표시되기 직전 (선택)
      @Override
      public void onAdWillPresentFullScreen(@NotNull AdropPopupAd ad) {
          Log.d("Adrop", "팝업 광고 표시 직전");
      }

      // 팝업 광고가 표시된 직후 (선택)
      @Override
      public void onAdDidPresentFullScreen(@NotNull AdropPopupAd ad) {
          Log.d("Adrop", "팝업 광고 표시 완료");
      }

      // 팝업 광고가 닫히기 직전 (선택)
      @Override
      public void onAdWillDismissFullScreen(@NotNull AdropPopupAd ad) {
          Log.d("Adrop", "팝업 광고 닫히기 직전");
      }

      // 팝업 광고가 닫힌 직후 (선택)
      @Override
      public void onAdDidDismissFullScreen(@NotNull AdropPopupAd ad) {
          Log.d("Adrop", "팝업 광고 닫힘");
          // 다음 광고를 미리 로드
          loadPopupAd();
      }

      // 팝업 광고 표시 실패 (선택)
      @Override
      public void onAdFailedToShowFullScreen(@NotNull AdropPopupAd ad, @NotNull AdropErrorCode errorCode) {
          Log.d("Adrop", "팝업 광고 표시 실패: " + errorCode);
      }

      // 동영상 재생 시작 (선택)
      @Override
      public void onAdVideoStart(@NotNull AdropPopupAd ad) {
          Log.d("Adrop", "팝업 광고 동영상 재생 시작");
      }

      // 동영상 재생 종료 (선택)
      @Override
      public void onAdVideoEnd(@NotNull AdropPopupAd ad) {
          Log.d("Adrop", "팝업 광고 동영상 재생 종료");
      }
  };

  // 닫기 리스너 구현
  private final AdropPopupAdCloseListener popupCloseListener = new AdropPopupAdCloseListener() {
      // 닫기 버튼 클릭 (선택)
      @Override
      public void onClosed(@NotNull AdropPopupAd ad) {
          Log.d("Adrop", "팝업 광고 닫기 버튼 클릭");
      }

      // 딤(배경) 클릭 (선택)
      @Override
      public void onDimClicked(@NotNull AdropPopupAd ad) {
          Log.d("Adrop", "팝업 광고 딤 영역 클릭");
      }

      // "오늘 하루 보지 않기" 클릭 (선택)
      @Override
      public void onTodayOffClicked(@NotNull AdropPopupAd ad) {
          Log.d("Adrop", "오늘 하루 보지 않기 선택");
          // 오늘 날짜를 저장하여 다음 표시를 제어
          SharedPreferences prefs = getSharedPreferences("adrop_prefs", Context.MODE_PRIVATE);
          prefs.edit().putLong("last_popup_hidden_date", System.currentTimeMillis()).apply();
      }
  };
  ```
</CodeGroup>

***

## 커스터마이징

팝업 광고의 외형을 커스터마이징할 수 있습니다.

### 배경색 및 텍스트 색상 설정

<CodeGroup>
  ```kotlin Kotlin theme={null}
  private fun loadPopupAd() {
      popupAd = AdropPopupAd(this, "YOUR_POPUP_UNIT_ID")
      popupAd?.popupAdListener = popupAdListener
      popupAd?.closeListener = popupCloseListener

      // 배경색 설정 (딤 영역)
      popupAd?.backgroundColor = Color.parseColor("#CC000000")

      // "오늘 하루 보지 않기" 텍스트 색상
      popupAd?.hideForTodayTextColor = Color.WHITE

      // 닫기 버튼 텍스트 색상
      popupAd?.closeTextColor = Color.WHITE

      // CTA 버튼 텍스트 색상
      popupAd?.ctaTextColor = Color.BLUE

      popupAd?.load()
  }
  ```

  ```java Java theme={null}
  private void loadPopupAd() {
      popupAd = new AdropPopupAd(this, "YOUR_POPUP_UNIT_ID");
      popupAd.setPopupAdListener(popupAdListener);
      popupAd.setCloseListener(popupCloseListener);

      // 배경색 설정 (딤 영역)
      popupAd.setBackgroundColor(Color.parseColor("#CC000000"));

      // "오늘 하루 보지 않기" 텍스트 색상
      popupAd.setHideForTodayTextColor(Color.WHITE);

      // 닫기 버튼 텍스트 색상
      popupAd.setCloseTextColor(Color.WHITE);

      // CTA 버튼 텍스트 색상
      popupAd.setCtaTextColor(Color.BLUE);

      popupAd.load();
  }
  ```
</CodeGroup>

### 커스터마이징 옵션

| 속성                      | 타입     | 설명                   | 기본값           |
| ----------------------- | ------ | -------------------- | ------------- |
| `backgroundColor`       | `Int?` | 팝업 배경(딤) 색상          | 검은색 (투명도 0.8) |
| `hideForTodayTextColor` | `Int?` | "오늘 하루 보지 않기" 텍스트 색상 | 흰색            |
| `closeTextColor`        | `Int?` | 닫기 버튼 텍스트 색상         | 흰색            |
| `ctaTextColor`          | `Int?` | CTA 버튼 텍스트 색상        | 시스템 기본값       |

***

## 커스텀 클릭 처리

광고 클릭 시 기본 브라우저 열기 대신 커스텀 처리를 하려면 `useCustomClick` 속성을 사용하세요.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  private fun loadPopupAd() {
      popupAd = AdropPopupAd(this, "YOUR_POPUP_UNIT_ID")

      // 커스텀 클릭 처리 활성화
      popupAd?.useCustomClick = true

      popupAd?.popupAdListener = object : AdropPopupAdListener {
          override fun onAdReceived(ad: AdropPopupAd) {
              showPopupAd()
          }

          override fun onAdFailedToReceive(ad: AdropPopupAd, errorCode: AdropErrorCode) {
              Log.d("Adrop", "광고 수신 실패: $errorCode")
          }

          override fun onAdClicked(ad: AdropPopupAd) {
              // 커스텀 클릭 처리
              Log.d("Adrop", "광고 클릭됨: ${ad.destinationURL}")

              // 원하는 URL로 열기
              popupAd?.open("https://your-custom-url.com")

              // 또는 In-App 브라우저로 열기
              // openInAppBrowser(ad.destinationURL)
          }
      }

      popupAd?.load()
  }
  ```

  ```java Java theme={null}
  private void loadPopupAd() {
      popupAd = new AdropPopupAd(this, "YOUR_POPUP_UNIT_ID");

      // 커스텀 클릭 처리 활성화
      popupAd.setUseCustomClick(true);

      popupAd.setPopupAdListener(new AdropPopupAdListener() {
          @Override
          public void onAdReceived(@NotNull AdropPopupAd ad) {
              showPopupAd();
          }

          @Override
          public void onAdFailedToReceive(@NotNull AdropPopupAd ad, @NotNull AdropErrorCode errorCode) {
              Log.d("Adrop", "광고 수신 실패: " + errorCode);
          }

          @Override
          public void onAdClicked(@NotNull AdropPopupAd ad) {
              // 커스텀 클릭 처리
              Log.d("Adrop", "광고 클릭됨: " + ad.getDestinationURL());

              // 원하는 URL로 열기
              popupAd.open("https://your-custom-url.com");

              // 또는 In-App 브라우저로 열기
              // openInAppBrowser(ad.getDestinationURL());
          }
      });

      popupAd.load();
  }
  ```
</CodeGroup>

***

## 광고 속성

팝업 광고 객체에서 다음 속성들을 사용할 수 있습니다:

<ParamField path="unitId" type="String">
  광고 유닛 ID
</ParamField>

<ParamField path="isLoaded" type="Boolean">
  광고가 로드되었는지 여부
</ParamField>

<ParamField path="creativeId" type="String">
  현재 표시 중인 광고 크리에이티브 ID
</ParamField>

<ParamField path="destinationURL" type="String">
  광고 클릭 시 이동할 URL
</ParamField>

<ParamField path="txId" type="String">
  트랜잭션 ID (광고 노출 추적용)
</ParamField>

<ParamField path="campaignId" type="String">
  캠페인 ID
</ParamField>

***

## 리스너 메서드

### AdropPopupAdListener (광고 이벤트)

#### 필수 메서드

<ParamField body="onAdReceived" type="(AdropPopupAd) -> Unit">
  광고 수신 성공 시 호출됩니다. 이 시점에서 `show()`를 호출하여 광고를 표시할 수 있습니다.
</ParamField>

<ParamField body="onAdFailedToReceive" type="(AdropPopupAd, AdropErrorCode) -> Unit">
  광고 수신 실패 시 호출됩니다. 에러 코드를 통해 실패 원인을 확인할 수 있습니다.
</ParamField>

#### 선택 메서드

<ParamField body="onAdImpression" type="(AdropPopupAd) -> Unit">
  광고 노출이 기록되었을 때 호출됩니다.
</ParamField>

<ParamField body="onAdClicked" type="(AdropPopupAd) -> Unit">
  사용자가 광고를 클릭했을 때 호출됩니다.
</ParamField>

<ParamField body="onAdWillPresentFullScreen" type="(AdropPopupAd) -> Unit">
  팝업 광고가 표시되기 직전에 호출됩니다.
</ParamField>

<ParamField body="onAdDidPresentFullScreen" type="(AdropPopupAd) -> Unit">
  팝업 광고가 화면에 표시된 직후 호출됩니다.
</ParamField>

<ParamField body="onAdWillDismissFullScreen" type="(AdropPopupAd) -> Unit">
  팝업 광고가 닫히기 직전에 호출됩니다.
</ParamField>

<ParamField body="onAdDidDismissFullScreen" type="(AdropPopupAd) -> Unit">
  팝업 광고가 닫힌 직후 호출됩니다. 다음 광고를 미리 로드하기 좋은 시점입니다.
</ParamField>

<ParamField body="onAdFailedToShowFullScreen" type="(AdropPopupAd, AdropErrorCode) -> Unit">
  광고 표시 실패 시 호출됩니다. 에러 코드를 통해 실패 원인을 확인할 수 있습니다.
</ParamField>

<ParamField body="onAdVideoStart" type="(AdropPopupAd) -> Unit">
  동영상 팝업 광고에서 동영상 재생이 시작되었을 때 호출됩니다.
</ParamField>

<ParamField body="onAdVideoEnd" type="(AdropPopupAd) -> Unit">
  동영상 팝업 광고에서 동영상 재생이 종료되었을 때 호출됩니다.
</ParamField>

### AdropPopupAdCloseListener (닫기 이벤트)

모든 메서드가 선택사항입니다.

<ParamField body="onClosed" type="(AdropPopupAd) -> Unit">
  사용자가 닫기 버튼을 클릭했을 때 호출됩니다.
</ParamField>

<ParamField body="onDimClicked" type="(AdropPopupAd) -> Unit">
  사용자가 팝업 외부(딤 영역)를 클릭했을 때 호출됩니다.
</ParamField>

<ParamField body="onTodayOffClicked" type="(AdropPopupAd) -> Unit">
  사용자가 "오늘 하루 보지 않기"를 선택했을 때 호출됩니다. 이 콜백에서 날짜를 저장하여 하루 동안 광고를 표시하지 않도록 구현할 수 있습니다.
</ParamField>

***

## "오늘 하루 보지 않기" 구현

사용자가 "오늘 하루 보지 않기"를 선택했을 때 적절히 처리하는 방법입니다.

### SharedPreferences를 이용한 구현

<CodeGroup>
  ```kotlin Kotlin theme={null}
  class PopupAdManager(private val context: Context) {
      private val prefs = context.getSharedPreferences("adrop_prefs", Context.MODE_PRIVATE)
      private val todayOffKey = "popup_ad_today_off_date"
      private var popupAd: AdropPopupAd? = null

      // 오늘 광고를 표시할 수 있는지 확인
      fun canShowAdToday(): Boolean {
          val lastHiddenDate = prefs.getLong(todayOffKey, 0L)
          if (lastHiddenDate == 0L) return true

          val calendar = Calendar.getInstance()
          calendar.timeInMillis = lastHiddenDate
          val hiddenDay = calendar.get(Calendar.DAY_OF_YEAR)
          val hiddenYear = calendar.get(Calendar.YEAR)

          calendar.timeInMillis = System.currentTimeMillis()
          val currentDay = calendar.get(Calendar.DAY_OF_YEAR)
          val currentYear = calendar.get(Calendar.YEAR)

          // 저장된 날짜가 오늘이 아니면 표시 가능
          return hiddenDay != currentDay || hiddenYear != currentYear
      }

      // 광고 로드 및 표시
      fun loadAndShowAd(activity: Activity, unitId: String) {
          if (!canShowAdToday()) {
              Log.d("Adrop", "오늘은 광고를 표시하지 않습니다")
              return
          }

          popupAd?.destroy()
          popupAd = AdropPopupAd(activity, unitId)
          popupAd?.popupAdListener = popupAdListener
          popupAd?.closeListener = popupCloseListener
          popupAd?.load()
      }

      private val popupAdListener = object : AdropPopupAdListener {
          override fun onAdReceived(ad: AdropPopupAd) {
              // 광고 수신 후 자동 표시
              if (context is Activity) {
                  ad.show(context)
              }
          }

          override fun onAdFailedToReceive(ad: AdropPopupAd, errorCode: AdropErrorCode) {
              Log.d("Adrop", "광고 수신 실패: $errorCode")
          }
      }

      private val popupCloseListener = object : AdropPopupAdCloseListener {
          override fun onTodayOffClicked(ad: AdropPopupAd) {
              // 오늘 날짜를 저장
              prefs.edit().putLong(todayOffKey, System.currentTimeMillis()).apply()
              Log.d("Adrop", "오늘 하루 팝업 광고를 표시하지 않습니다")
          }
      }

      fun destroy() {
          popupAd?.destroy()
          popupAd = null
      }
  }
  ```

  ```java Java theme={null}
  public class PopupAdManager {
      private Context context;
      private SharedPreferences prefs;
      private static final String PREFS_NAME = "adrop_prefs";
      private static final String TODAY_OFF_KEY = "popup_ad_today_off_date";
      private AdropPopupAd popupAd;

      public PopupAdManager(Context context) {
          this.context = context;
          this.prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
      }

      // 오늘 광고를 표시할 수 있는지 확인
      public boolean canShowAdToday() {
          long lastHiddenDate = prefs.getLong(TODAY_OFF_KEY, 0L);
          if (lastHiddenDate == 0L) return true;

          Calendar calendar = Calendar.getInstance();
          calendar.setTimeInMillis(lastHiddenDate);
          int hiddenDay = calendar.get(Calendar.DAY_OF_YEAR);
          int hiddenYear = calendar.get(Calendar.YEAR);

          calendar.setTimeInMillis(System.currentTimeMillis());
          int currentDay = calendar.get(Calendar.DAY_OF_YEAR);
          int currentYear = calendar.get(Calendar.YEAR);

          // 저장된 날짜가 오늘이 아니면 표시 가능
          return hiddenDay != currentDay || hiddenYear != currentYear;
      }

      // 광고 로드 및 표시
      public void loadAndShowAd(Activity activity, String unitId) {
          if (!canShowAdToday()) {
              Log.d("Adrop", "오늘은 광고를 표시하지 않습니다");
              return;
          }

          if (popupAd != null) {
              popupAd.destroy();
          }

          popupAd = new AdropPopupAd(activity, unitId);
          popupAd.setPopupAdListener(popupAdListener);
          popupAd.setCloseListener(popupCloseListener);
          popupAd.load();
      }

      private final AdropPopupAdListener popupAdListener = new AdropPopupAdListener() {
          @Override
          public void onAdReceived(@NotNull AdropPopupAd ad) {
              // 광고 수신 후 자동 표시
              if (context instanceof Activity) {
                  ad.show((Activity) context);
              }
          }

          @Override
          public void onAdFailedToReceive(@NotNull AdropPopupAd ad, @NotNull AdropErrorCode errorCode) {
              Log.d("Adrop", "광고 수신 실패: " + errorCode);
          }
      };

      private final AdropPopupAdCloseListener popupCloseListener = new AdropPopupAdCloseListener() {
          @Override
          public void onTodayOffClicked(@NotNull AdropPopupAd ad) {
              // 오늘 날짜를 저장
              prefs.edit().putLong(TODAY_OFF_KEY, System.currentTimeMillis()).apply();
              Log.d("Adrop", "오늘 하루 팝업 광고를 표시하지 않습니다");
          }
      };

      public void destroy() {
          if (popupAd != null) {
              popupAd.destroy();
              popupAd = null;
          }
      }
  }
  ```
</CodeGroup>

### 사용 예시

<CodeGroup>
  ```kotlin Kotlin theme={null}
  class MainActivity : AppCompatActivity() {
      private lateinit var popupAdManager: PopupAdManager

      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.activity_main)

          popupAdManager = PopupAdManager(this)

          // 앱 시작 시 팝업 광고 표시
          popupAdManager.loadAndShowAd(this, "YOUR_POPUP_UNIT_ID")
      }

      override fun onDestroy() {
          super.onDestroy()
          popupAdManager.destroy()
      }
  }
  ```

  ```java Java theme={null}
  public class MainActivity extends AppCompatActivity {
      private PopupAdManager popupAdManager;

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);

          popupAdManager = new PopupAdManager(this);

          // 앱 시작 시 팝업 광고 표시
          popupAdManager.loadAndShowAd(this, "YOUR_POPUP_UNIT_ID");
      }

      @Override
      protected void onDestroy() {
          super.onDestroy();
          popupAdManager.destroy();
      }
  }
  ```
</CodeGroup>

***

## 팝업 타입

팝업 광고는 두 가지 타입으로 제공됩니다:

| 타입             | 설명    | 위치              |
| -------------- | ----- | --------------- |
| `POPUP_BOTTOM` | 하단 팝업 | 화면 하단에서 올라오는 형태 |
| `POPUP_CENTER` | 중앙 팝업 | 화면 중앙에 표시되는 형태  |

<Note>
  팝업 타입은 애드컨트롤 콘솔에서 광고 유닛 생성 시 설정됩니다. 코드에서 별도로 지정할 필요가 없습니다.
</Note>

***

## 모범 사례

### 1. 적절한 표시 시점

팝업 광고는 다음 시점에 표시하는 것이 효과적입니다:

```kotlin theme={null}
// ✅ 좋은 예: 앱 시작 시
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    loadAndShowPopupAd()
}

// ✅ 좋은 예: 콘텐츠 로드 완료 시
fun onContentLoaded() {
    if (shouldShowPopupAd()) {
        showPopupAd()
    }
}

// ✅ 좋은 예: 특정 이벤트 완료 시
fun onAchievementUnlocked() {
    showPopupAd()
}

// ❌ 나쁜 예: 사용자가 작업 중일 때
fun onUserTyping() {
    showPopupAd() // 사용자 경험 저해
}
```

### 2. "오늘 하루 보지 않기" 존중

사용자가 "오늘 하루 보지 않기"를 선택했다면 반드시 준수하세요.

```kotlin theme={null}
fun shouldShowPopupAd(): Boolean {
    // 오늘 하루 보지 않기 확인
    if (!canShowAdToday()) {
        return false
    }

    // 다른 조건 확인
    return true
}
```

### 3. 빈도 제한

팝업 광고를 너무 자주 표시하지 마세요.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  class PopupAdFrequencyManager(private val context: Context) {
      private val prefs = context.getSharedPreferences("adrop_prefs", Context.MODE_PRIVATE)
      private val lastShownKey = "popup_ad_last_shown"
      private val minimumInterval = 3600000L // 1시간 (밀리초)

      fun canShowAd(): Boolean {
          // 오늘 하루 보지 않기 확인
          if (!canShowAdToday()) {
              return false
          }

          // 최소 시간 간격 확인
          val lastShown = prefs.getLong(lastShownKey, 0L)
          if (lastShown == 0L) return true

          val currentTime = System.currentTimeMillis()
          return currentTime - lastShown >= minimumInterval
      }

      fun recordAdShown() {
          prefs.edit().putLong(lastShownKey, System.currentTimeMillis()).apply()
      }

      private fun canShowAdToday(): Boolean {
          // 위의 "오늘 하루 보지 않기" 구현 참고
          return true
      }
  }
  ```

  ```java Java theme={null}
  public class PopupAdFrequencyManager {
      private SharedPreferences prefs;
      private static final String PREFS_NAME = "adrop_prefs";
      private static final String LAST_SHOWN_KEY = "popup_ad_last_shown";
      private static final long MINIMUM_INTERVAL = 3600000L; // 1시간 (밀리초)

      public PopupAdFrequencyManager(Context context) {
          this.prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
      }

      public boolean canShowAd() {
          // 오늘 하루 보지 않기 확인
          if (!canShowAdToday()) {
              return false;
          }

          // 최소 시간 간격 확인
          long lastShown = prefs.getLong(LAST_SHOWN_KEY, 0L);
          if (lastShown == 0L) return true;

          long currentTime = System.currentTimeMillis();
          return currentTime - lastShown >= MINIMUM_INTERVAL;
      }

      public void recordAdShown() {
          prefs.edit().putLong(LAST_SHOWN_KEY, System.currentTimeMillis()).apply();
      }

      private boolean canShowAdToday() {
          // 위의 "오늘 하루 보지 않기" 구현 참고
          return true;
      }
  }
  ```
</CodeGroup>

### 4. 광고 미리 로드

사용자 경험을 위해 광고를 미리 로드하세요.

```kotlin theme={null}
class MainActivity : AppCompatActivity() {
    private var popupAd: AdropPopupAd? = null
    private var isAdReady = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 미리 로드
        preloadPopupAd()
    }

    private fun preloadPopupAd() {
        popupAd = AdropPopupAd(this, "YOUR_POPUP_UNIT_ID")
        popupAd?.popupAdListener = object : AdropPopupAdListener {
            override fun onAdReceived(ad: AdropPopupAd) {
                isAdReady = true
            }

            override fun onAdFailedToReceive(ad: AdropPopupAd, errorCode: AdropErrorCode) {
                Log.d("Adrop", "광고 수신 실패: $errorCode")
            }
        }
        popupAd?.load()
    }

    fun showPopupAdIfReady() {
        if (isAdReady) {
            popupAd?.show(this)
        }
    }
}
```

### 5. 가로 모드 처리

팝업 광고는 세로 모드에서만 지원됩니다. 가로 모드에서는 `onAdFailedToShowFullScreen`이 호출됩니다.

```kotlin theme={null}
private val popupAdListener = object : AdropPopupAdListener {
    override fun onAdReceived(ad: AdropPopupAd) {
        // 세로 모드일 때만 표시
        if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
            ad.show(this@MainActivity)
        }
    }

    override fun onAdFailedToShowFullScreen(ad: AdropPopupAd, errorCode: AdropErrorCode) {
        if (errorCode == AdropErrorCode.ERROR_CODE_LANDSCAPE_UNSUPPORTED) {
            Log.d("Adrop", "팝업 광고는 세로 모드에서만 지원됩니다")
        }
    }

    override fun onAdFailedToReceive(ad: AdropPopupAd, errorCode: AdropErrorCode) {
        Log.d("Adrop", "광고 수신 실패: $errorCode")
    }
}
```

***

## 테스트 유닛 ID

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

| 광고 타입            | 테스트 유닛 ID                                     |
| ---------------- | --------------------------------------------- |
| 팝업 (하단 이미지)      | `PUBLIC_TEST_UNIT_ID_POPUP_BOTTOM`            |
| 팝업 (중앙 이미지)      | `PUBLIC_TEST_UNIT_ID_POPUP_CENTER`            |
| 팝업 비디오 (하단 16:9) | `PUBLIC_TEST_UNIT_ID_POPUP_BOTTOM_VIDEO_16_9` |
| 팝업 비디오 (하단 9:16) | `PUBLIC_TEST_UNIT_ID_POPUP_BOTTOM_VIDEO_9_16` |
| 팝업 비디오 (중앙 16:9) | `PUBLIC_TEST_UNIT_ID_POPUP_CENTER_VIDEO_16_9` |
| 팝업 비디오 (중앙 9:16) | `PUBLIC_TEST_UNIT_ID_POPUP_CENTER_VIDEO_9_16` |

### 테스트 광고 사용 예시

<CodeGroup>
  ```kotlin Kotlin theme={null}
  // 테스트 환경과 프로덕션 환경 분리
  val popupUnitId = if (BuildConfig.DEBUG) {
      "PUBLIC_TEST_UNIT_ID_POPUP_BOTTOM"
  } else {
      "YOUR_PRODUCTION_UNIT_ID"
  }

  popupAd = AdropPopupAd(this, popupUnitId)
  ```

  ```java Java theme={null}
  // 테스트 환경과 프로덕션 환경 분리
  String popupUnitId;
  if (BuildConfig.DEBUG) {
      popupUnitId = "PUBLIC_TEST_UNIT_ID_POPUP_BOTTOM";
  } else {
      popupUnitId = "YOUR_PRODUCTION_UNIT_ID";
  }

  popupAd = new AdropPopupAd(this, popupUnitId);
  ```
</CodeGroup>

<Warning>
  실제 배포 시에는 반드시 애드컨트롤 콘솔에서 생성한 실제 유닛 ID를 사용하세요.
</Warning>

***

## 완전한 예제

<CodeGroup>
  ```kotlin Kotlin theme={null}
  import android.graphics.Color
  import android.os.Bundle
  import android.util.Log
  import androidx.appcompat.app.AppCompatActivity
  import io.adrop.ads.model.AdropErrorCode
  import io.adrop.ads.popupAd.AdropPopupAd
  import io.adrop.ads.popupAd.AdropPopupAdCloseListener
  import io.adrop.ads.popupAd.AdropPopupAdListener

  class PopupAdActivity : AppCompatActivity() {
      private var popupAd: AdropPopupAd? = null

      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.activity_popup_ad)

          // 버튼 클릭 시 광고 로드 및 표시
          findViewById<Button>(R.id.btn_show_popup).setOnClickListener {
              loadPopupAd()
          }
      }

      private fun loadPopupAd() {
          // 기존 광고 정리
          popupAd?.destroy()

          // 팝업 광고 생성
          popupAd = AdropPopupAd(this, "YOUR_POPUP_UNIT_ID")

          // 커스터마이징
          popupAd?.apply {
              backgroundColor = Color.parseColor("#CC000000")
              hideForTodayTextColor = Color.WHITE
              closeTextColor = Color.WHITE
              ctaTextColor = Color.BLUE
              useCustomClick = false
          }

          // 리스너 설정
          popupAd?.popupAdListener = object : AdropPopupAdListener {
              override fun onAdReceived(ad: AdropPopupAd) {
                  Log.d("Adrop", "팝업 광고 수신 완료")
                  showPopupAd()
              }

              override fun onAdFailedToReceive(ad: AdropPopupAd, errorCode: AdropErrorCode) {
                  Log.d("Adrop", "팝업 광고 수신 실패: $errorCode")
              }

              override fun onAdImpression(ad: AdropPopupAd) {
                  Log.d("Adrop", "팝업 광고 노출")
                  Log.d("Adrop", "txId: ${ad.txId}, campaignId: ${ad.campaignId}")
              }

              override fun onAdClicked(ad: AdropPopupAd) {
                  Log.d("Adrop", "팝업 광고 클릭: ${ad.destinationURL}")
              }

              override fun onAdDidPresentFullScreen(ad: AdropPopupAd) {
                  Log.d("Adrop", "팝업 광고 표시됨")
              }

              override fun onAdDidDismissFullScreen(ad: AdropPopupAd) {
                  Log.d("Adrop", "팝업 광고 닫힘")
                  // 다음 광고 미리 로드
                  loadPopupAd()
              }

              override fun onAdFailedToShowFullScreen(ad: AdropPopupAd, errorCode: AdropErrorCode) {
                  Log.d("Adrop", "팝업 광고 표시 실패: $errorCode")
              }
          }

          popupAd?.closeListener = object : AdropPopupAdCloseListener {
              override fun onClosed(ad: AdropPopupAd) {
                  Log.d("Adrop", "닫기 버튼 클릭")
              }

              override fun onDimClicked(ad: AdropPopupAd) {
                  Log.d("Adrop", "딤 영역 클릭")
              }

              override fun onTodayOffClicked(ad: AdropPopupAd) {
                  Log.d("Adrop", "오늘 하루 보지 않기 선택")
                  saveHiddenDate()
              }
          }

          // 광고 로드
          popupAd?.load()
      }

      private fun showPopupAd() {
          popupAd?.let { ad ->
              if (ad.isLoaded) {
                  ad.show(this)
              }
          }
      }

      private fun saveHiddenDate() {
          val prefs = getSharedPreferences("adrop_prefs", MODE_PRIVATE)
          prefs.edit().putLong("last_popup_hidden_date", System.currentTimeMillis()).apply()
      }

      override fun onDestroy() {
          super.onDestroy()
          popupAd?.destroy()
      }
  }
  ```

  ```java Java theme={null}
  import android.graphics.Color;
  import android.os.Bundle;
  import android.util.Log;
  import android.widget.Button;
  import androidx.appcompat.app.AppCompatActivity;
  import io.adrop.ads.model.AdropErrorCode;
  import io.adrop.ads.popupAd.AdropPopupAd;
  import io.adrop.ads.popupAd.AdropPopupAdCloseListener;
  import io.adrop.ads.popupAd.AdropPopupAdListener;
  import org.jetbrains.annotations.NotNull;

  public class PopupAdActivity extends AppCompatActivity {
      private AdropPopupAd popupAd;

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_popup_ad);

          // 버튼 클릭 시 광고 로드 및 표시
          findViewById(R.id.btn_show_popup).setOnClickListener(v -> loadPopupAd());
      }

      private void loadPopupAd() {
          // 기존 광고 정리
          if (popupAd != null) {
              popupAd.destroy();
          }

          // 팝업 광고 생성
          popupAd = new AdropPopupAd(this, "YOUR_POPUP_UNIT_ID");

          // 커스터마이징
          popupAd.setBackgroundColor(Color.parseColor("#CC000000"));
          popupAd.setHideForTodayTextColor(Color.WHITE);
          popupAd.setCloseTextColor(Color.WHITE);
          popupAd.setCtaTextColor(Color.BLUE);
          popupAd.setUseCustomClick(false);

          // 리스너 설정
          popupAd.setPopupAdListener(new AdropPopupAdListener() {
              @Override
              public void onAdReceived(@NotNull AdropPopupAd ad) {
                  Log.d("Adrop", "팝업 광고 수신 완료");
                  showPopupAd();
              }

              @Override
              public void onAdFailedToReceive(@NotNull AdropPopupAd ad, @NotNull AdropErrorCode errorCode) {
                  Log.d("Adrop", "팝업 광고 수신 실패: " + errorCode);
              }

              @Override
              public void onAdImpression(@NotNull AdropPopupAd ad) {
                  Log.d("Adrop", "팝업 광고 노출");
                  Log.d("Adrop", "txId: " + ad.getTxId() + ", campaignId: " + ad.getCampaignId());
              }

              @Override
              public void onAdClicked(@NotNull AdropPopupAd ad) {
                  Log.d("Adrop", "팝업 광고 클릭: " + ad.getDestinationURL());
              }

              @Override
              public void onAdDidPresentFullScreen(@NotNull AdropPopupAd ad) {
                  Log.d("Adrop", "팝업 광고 표시됨");
              }

              @Override
              public void onAdDidDismissFullScreen(@NotNull AdropPopupAd ad) {
                  Log.d("Adrop", "팝업 광고 닫힘");
                  // 다음 광고 미리 로드
                  loadPopupAd();
              }

              @Override
              public void onAdFailedToShowFullScreen(@NotNull AdropPopupAd ad, @NotNull AdropErrorCode errorCode) {
                  Log.d("Adrop", "팝업 광고 표시 실패: " + errorCode);
              }
          });

          popupAd.setCloseListener(new AdropPopupAdCloseListener() {
              @Override
              public void onClosed(@NotNull AdropPopupAd ad) {
                  Log.d("Adrop", "닫기 버튼 클릭");
              }

              @Override
              public void onDimClicked(@NotNull AdropPopupAd ad) {
                  Log.d("Adrop", "딤 영역 클릭");
              }

              @Override
              public void onTodayOffClicked(@NotNull AdropPopupAd ad) {
                  Log.d("Adrop", "오늘 하루 보지 않기 선택");
                  saveHiddenDate();
              }
          });

          // 광고 로드
          popupAd.load();
      }

      private void showPopupAd() {
          if (popupAd != null && popupAd.isLoaded()) {
              popupAd.show(this);
          }
      }

      private void saveHiddenDate() {
          getSharedPreferences("adrop_prefs", MODE_PRIVATE)
                  .edit()
                  .putLong("last_popup_hidden_date", System.currentTimeMillis())
                  .apply();
      }

      @Override
      protected void onDestroy() {
          super.onDestroy();
          if (popupAd != null) {
              popupAd.destroy();
          }
      }
  }
  ```
</CodeGroup>

***

## 문제 해결

### 광고가 표시되지 않음

<AccordionGroup>
  <Accordion title="onAdReceived가 호출되지 않음">
    * SDK가 초기화되었는지 확인
    * 유닛 ID가 올바른지 확인
    * 네트워크 연결 상태 확인
    * Adrop SDK 초기화 시 `production` 설정 확인
  </Accordion>

  <Accordion title="onAdFailedToReceive가 호출됨">
    * 에러 코드를 확인하여 원인 파악
    * 테스트 환경에서는 테스트 유닛 ID 사용
    * 광고 인벤토리 부족 시 나중에 재시도
    * `ERROR_CODE_AD_HIDE_FOR_TODAY`: "오늘 하루 보지 않기"가 활성화된 상태
  </Accordion>

  <Accordion title="show() 호출 후 아무 일도 일어나지 않음">
    * `onAdReceived` 콜백 이후에 `show()`를 호출했는지 확인
    * Activity가 유효한 상태인지 확인
    * `isLoaded` 속성을 확인하여 광고가 로드되었는지 확인
    * 세로 모드인지 확인 (가로 모드는 지원되지 않음)
  </Accordion>

  <Accordion title="가로 모드에서 광고가 표시되지 않음">
    팝업 광고는 세로 모드에서만 지원됩니다. 가로 모드에서 `show()`를 호출하면 `onAdFailedToShowFullScreen`이 `ERROR_CODE_LANDSCAPE_UNSUPPORTED` 에러 코드와 함께 호출됩니다.
  </Accordion>
</AccordionGroup>

### "오늘 하루 보지 않기"가 작동하지 않음

```kotlin theme={null}
// 날짜 비교 로직 확인
fun isToday(timestamp: Long): Boolean {
    val calendar = Calendar.getInstance()
    val today = calendar.apply {
        timeInMillis = System.currentTimeMillis()
    }

    val targetDay = Calendar.getInstance().apply {
        timeInMillis = timestamp
    }

    return today.get(Calendar.YEAR) == targetDay.get(Calendar.YEAR) &&
           today.get(Calendar.DAY_OF_YEAR) == targetDay.get(Calendar.DAY_OF_YEAR)
}

// 사용
fun canShowAdToday(): Boolean {
    val prefs = getSharedPreferences("adrop_prefs", Context.MODE_PRIVATE)
    val lastHiddenDate = prefs.getLong("last_popup_hidden_date", 0L)

    return lastHiddenDate == 0L || !isToday(lastHiddenDate)
}
```

***

## 관련 문서

<CardGroup cols={2}>
  <Card title="시작하기" href="/ko/sdk/android">
    SDK 설치 및 초기화
  </Card>

  <Card title="전면 광고" href="/ko/sdk/android/interstitial">
    전면 광고 구현하기
  </Card>

  <Card title="보상형 광고" href="/ko/sdk/android/rewarded">
    보상형 광고 구현하기
  </Card>

  <Card title="배너 광고" href="/ko/sdk/android/banner">
    배너 광고 구현하기
  </Card>
</CardGroup>
