SwiftUI 앱 수익화 완벽 가이드 — Google AdMob 배너·전면 광고, ATT와 개인정보 보호 설정

iPhone 앱을 개발하고 App Store에 출시했다면 다음으로 고민하게 되는 부분은 수익화입니다.

무료 앱에서 가장 일반적으로 활용하는 수익 모델 중 하나가 광고입니다.

Android에서는 Google AdMob을 이용해 배너 광고, 전면 광고, 보상형 광고 등을 구현할 수 있습니다. iOS에서도 Google Mobile Ads SDK를 사용하면 유사한 방식으로 광고를 표시할 수 있습니다.

하지만 iOS는 Android와 다른 부분이 있습니다.

대표적으로 App Tracking Transparency(ATT), IDFA, SKAdNetwork, App Store 개인정보 공개 항목 등이 있습니다.

광고가 정상적으로 표시된다고 해서 개인정보 보호 설정까지 올바르게 완료된 것은 아닙니다.

이번 글에서는 SwiftUI 앱에 AdMob을 연동하는 방법부터 광고 요청 시점과 개인정보 동의 처리까지 알아보겠습니다.

1. 이번 글에서 구현할 기능

  • Google AdMob 앱 및 광고 단위 생성

  • Swift Package Manager로 광고 SDK 설치

  • Info.plist 설정

  • UMP 사용자 동의 관리

  • ATT 추적 권한 처리

  • SwiftUI 배너 광고

  • 적응형 배너 크기 설정

  • 전면 광고 로딩 및 표시

  • 광고 표시 빈도 제한

  • 테스트 광고 사용

  • 개인정보 설정 화면

  • App Store 제출 전 점검

예제 환경

항목내용
개발 언어Swift
UI 프레임워크SwiftUI
앱 최소 버전iOS 17
광고 SDKGoogle Mobile Ads SDK
개인정보 동의Google UMP SDK
추적 권한AppTrackingTransparency
개발 도구Xcode

이번 글의 코드는 Google Mobile Ads SDK 13 계열의 Swift API 명칭을 기준으로 설명합니다. 다른 SDK 버전을 사용한다면 클래스와 메서드 이름을 확인해야 합니다.


2. Android와 iOS AdMob 비교

Android에서 AdMob을 사용해 본 개발자라면 기본적인 광고 흐름은 익숙할 것입니다.

AndroidiOS역할
MobileAds.initialize()MobileAds.shared.start()광고 SDK 초기화
AdViewBannerView배너 광고
AdRequestRequest광고 요청
InterstitialAdInterstitialAd전면 광고
FullScreenContentCallbackFullScreenContentDelegate전체 화면 광고 이벤트
UMP SDKUMP SDK사용자 동의
Android 광고 IDIDFA광고 식별자
AndroidManifest.xmlInfo.plist앱 설정

두 플랫폼 모두 AdMob을 사용하지만 광고 식별자 접근 권한과 개인정보 처리 방식에는 차이가 있습니다.


3. AdMob 광고 구조 이해하기

AdMob 광고를 사용하려면 먼저 Google AdMob에서 앱을 등록해야 합니다.

기본 구조는 다음과 같습니다.

Google AdMob
    │
    └── iOS 앱
         │
         ├── 배너 광고 단위
         │
         ├── 전면 광고 단위
         │
         └── 보상형 광고 단위

AdMob에서는 앱 ID와 광고 단위 ID를 구분합니다.

앱 ID

ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy

광고 단위 ID

ca-app-pub-xxxxxxxxxxxxxxxx/yyyyyyyyyy

앱 ID에는 ~가 사용되고 광고 단위 ID에는 /가 사용됩니다.

두 값을 혼동하면 광고 SDK 초기화 또는 광고 요청 과정에서 문제가 발생할 수 있습니다.


4. Google Mobile Ads SDK 설치

Xcode에서 프로젝트를 열고 다음 메뉴로 이동합니다.

File
  → Add Package Dependencies...

다음 주소를 입력합니다.

https://github.com/googleads/swift-package-manager-google-mobile-ads.git

패키지를 추가하고 앱 타깃에 Google Mobile Ads SDK를 연결합니다.

Swift 파일에서는 다음과 같이 가져옵니다.

import GoogleMobileAds

이제 SwiftUI 앱에서 AdMob API를 사용할 수 있습니다.


5. UMP SDK 설치

사용자 개인정보 동의를 관리하려면 Google User Messaging Platform SDK를 함께 사용합니다.

Xcode의 Add Package Dependencies에서 다음 주소를 추가합니다.

https://github.com/googleads/swift-package-manager-google-user-messaging-platform.git

Swift 코드에서는 다음과 같이 사용합니다.

import UserMessagingPlatform

UMP는 지역과 광고 설정 등에 따라 필요한 개인정보 메시지를 표시하고, 광고 요청 가능 여부를 확인하는 데 사용합니다.

단순히 사용자의 동의 여부를 UserDefaults에 직접 저장해 판단하는 방식은 피해야 합니다.


6. Info.plist에 앱 ID 등록

AdMob 앱 ID를 Info.plist에 등록합니다.

<key>GADApplicationIdentifier</key>
<string>ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy</string>

여기에는 AdMob 콘솔에서 발급받은 실제 iOS 앱 ID를 입력해야 합니다.

주의: 광고 단위 ID를 입력하는 곳이 아닙니다.


7. SKAdNetwork 설정

iOS에서는 광고 성과 측정에 SKAdNetwork 또는 Apple의 관련 어트리뷰션 기능이 사용될 수 있습니다.

Google Mobile Ads SDK 문서에서 안내하는 SKAdNetworkItems를 Info.plist에 설정해야 합니다.

형식은 다음과 같습니다.

<key>SKAdNetworkItems</key>
<array>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>example.skadnetwork</string>
    </dict>
</array>

example.skadnetwork는 구조를 설명하기 위한 자리표시자입니다.

실제 배포 시에는 Google 공식 문서와 사용하는 미디에이션 광고 네트워크가 요구하는 식별자 목록을 반영해야 합니다.

식별자 목록은 변경될 수 있으므로 오래된 블로그에서 복사한 목록을 그대로 사용하지 않는 것이 좋습니다.


8. UMP 사용자 동의 처리

광고 요청 전에 개인정보 동의 상태를 확인해야 합니다.

Google은 앱을 실행할 때마다 UMP 동의 정보를 업데이트하도록 안내합니다.

동의 처리의 기본 흐름은 다음과 같습니다.

앱 실행
   │
   ▼
UMP 동의 정보 업데이트
   │
   ▼
필요한 동의 메시지 표시
   │
   ▼
canRequestAds 확인
   │
   ├── true  → 광고 SDK 및 광고 요청 진행
   │
   └── false → 광고 요청 보류

동의 메시지가 필요한지 여부는 앱의 대상 지역과 AdMob 개인정보 메시지 설정 등에 따라 달라질 수 있습니다.


9. ConsentManager 구현

ConsentManager.swift

import Foundation
import Observation
import UserMessagingPlatform

@MainActor
@Observable
final class ConsentManager {

    private(set) var canRequestAds = false

    private(set) var isPrivacyOptionsRequired = false

    private(set) var isProcessing = false

    func gatherConsent() async {

        guard !isProcessing else {
            return
        }

        isProcessing = true

        defer {
            isProcessing = false
        }

        let parameters = RequestParameters()

        do {

            try await ConsentInformation.shared
                .requestConsentInfoUpdate(
                    with: parameters
                )

            try await ConsentForm
                .loadAndPresentIfRequired(
                    from: nil
                )

        } catch {

            print(
                "UMP 처리 오류: \(error.localizedDescription)"
            )
        }

        canRequestAds =
            ConsentInformation.shared.canRequestAds

        isPrivacyOptionsRequired =
            ConsentInformation.shared
                .privacyOptionsRequirementStatus == .required
    }

    func presentPrivacyOptions() async throws {

        try await ConsentForm.presentPrivacyOptionsForm(
            from: nil
        )

        canRequestAds =
            ConsentInformation.shared.canRequestAds
    }
}

이 코드는 UMP 동의 정보를 갱신하고 필요한 동의 메시지를 표시합니다.

동의 처리가 끝난 뒤에는 canRequestAds를 확인합니다.

이 값은 단순히 사용자가 맞춤형 광고에 동의했다는 의미와 동일하지 않습니다. UMP가 현재 상태에서 광고 요청을 허용하는지를 나타냅니다.

또한 UMP 동의와 Apple의 ATT 추적 권한은 별개입니다.


10. ATT란?

ATT는 App Tracking Transparency의 약자입니다.

앱이 다른 회사의 앱이나 웹사이트에서 수집한 데이터와 연결해 사용자를 추적하려는 경우 Apple의 ATT 요구사항이 적용될 수 있습니다.

광고 식별자인 IDFA 접근도 ATT와 관련됩니다.

ATT를 요청하는 앱은 Info.plist에 사용 목적을 작성해야 합니다.

<key>NSUserTrackingUsageDescription</key>
<string>앱 이용과 관련된 광고를 제공하고 광고 성과를 측정하기 위해 사용합니다.</string>

이 문구는 앱에서 실제로 수행하는 데이터 처리 목적과 일치해야 합니다.

AdMob을 설치했다는 이유만으로 모든 앱에서 무조건 ATT 팝업을 표시해야 하는 것은 아닙니다. 실제 추적 활동과 광고 SDK 설정을 기준으로 판단해야 합니다.


11. ATT 권한 요청 구현

TrackingPermissionManager.swift

import AppTrackingTransparency

@MainActor
enum TrackingPermissionManager {

    static func requestIfNeeded() async {

        guard ATTrackingManager
            .trackingAuthorizationStatus == .notDetermined
        else {
            return
        }

        let status = await ATTrackingManager
            .requestTrackingAuthorization()

        switch status {

        case .authorized:
            print("추적 권한 허용")

        case .denied:
            print("추적 권한 거부")

        case .restricted:
            print("추적 권한 제한")

        case .notDetermined:
            print("권한 미결정")

        @unknown default:
            break
        }
    }
}

ATT 권한은 사용자가 거부할 수 있습니다.

사용자가 거부했다고 해서 앱의 기본 기능을 차단하거나 반복해서 권한을 요구해서는 안 됩니다.

ATT가 거부된 경우에도 관련 정책과 동의 조건을 충족하는 범위에서 광고를 제공할 수 있습니다.


12. 광고 SDK 초기화

Google Mobile Ads SDK는 광고 요청 전에 초기화합니다.

import GoogleMobileAds

MobileAds.shared.start()

실제 앱에서는 UMP 동의 처리와 ATT 관련 결정을 고려해 초기화 시점을 설계하는 것이 좋습니다.

특히 광고 SDK 초기화 시 미디에이션 어댑터 등 추가 구성요소가 초기화될 수 있으므로 필요한 개인정보 선택과 요청 설정을 먼저 처리해야 합니다.

이번 예제에서는 앱 시작 시 다음 순서로 처리하겠습니다.

  1. UMP 동의 정보 갱신

  2. 필요한 개인정보 메시지 표시

  3. 앱의 추적 정책에 따라 ATT 처리

  4. 광고 요청 가능 여부 확인

  5. 광고 SDK 초기화

  6. 광고 로딩


13. AdMobManager 구현

AdMobManager.swift

import Foundation
import Observation
import GoogleMobileAds

@MainActor
@Observable
final class AdMobManager {

    private(set) var isInitialized = false

    func initializeIfNeeded() {

        guard !isInitialized else {
            return
        }

        isInitialized = true

        MobileAds.shared.start()
    }
}

SDK 초기화가 여러 번 호출되지 않도록 상태를 관리합니다.

실제 앱에서는 광고 로딩을 시작하기 전에 SDK 초기화 완료 여부와 필요한 미디에이션 설정을 확인하도록 확장할 수 있습니다.


14. SwiftUI 앱 시작 구조

이제 UMP와 광고 SDK 초기화를 연결합니다.

AdSampleApp.swift

import SwiftUI

@main
struct AdSampleApp: App {

    var body: some Scene {

        WindowGroup {
            ContentView()
        }
    }
}

ContentView.swift

import SwiftUI

struct ContentView: View {

    @State private var consentManager =
        ConsentManager()

    @State private var adMobManager =
        AdMobManager()

    @State private var isReady = false

    var body: some View {

        Group {

            if isReady {

                MainView(
                    canShowAds:
                        consentManager.canRequestAds,
                    consentManager:
                        consentManager
                )

            } else {

                ProgressView("앱 준비 중...")
            }
        }
        .task {

            await consentManager.gatherConsent()

            // 실제로 ATT가 필요한 앱에서만 요청합니다.
            // UMP 처리와 ATT 요청을 동시에 표시하지 않습니다.

            if consentManager.canRequestAds {

                adMobManager.initializeIfNeeded()
            }

            isReady = true
        }
    }
}

위 코드는 UMP 처리 후 광고를 시작하는 기본 구조입니다.

ATT를 실제로 사용하는 앱이라면 광고 초기화 전에 TrackingPermissionManager.requestIfNeeded()를 적절한 시점에 호출하도록 구성해야 합니다.

또한 UMP의 canRequestAds가 false인 경우 광고가 없는 상태에서도 앱의 주요 기능은 정상 동작하도록 설계하는 것이 좋습니다.


15. SwiftUI 배너 광고 구현

SwiftUI에서 AdMob 배너 광고를 표시하려면 UIKit 기반 광고 View를 연결할 수 있습니다.

UIViewRepresentable을 이용하면 됩니다.

먼저 Google에서 제공하는 iOS 테스트 배너 광고 단위 ID를 사용하겠습니다.

ca-app-pub-3940256099942544/2435281174

이 값은 개발과 테스트에 사용하는 공식 테스트 광고 단위입니다.

실제 출시할 때는 자신의 광고 단위 ID로 변경해야 합니다.


16. BannerAdView 구현

BannerAdView.swift

import SwiftUI
import GoogleMobileAds

struct BannerAdView: UIViewRepresentable {

    let adSize: AdSize
    let adUnitID: String

    func makeUIView(
        context: Context
    ) -> BannerView {

        let banner = BannerView(
            adSize: adSize
        )

        banner.adUnitID = adUnitID

        banner.load(Request())

        return banner
    }

    func updateUIView(
        _ uiView: BannerView,
        context: Context
    ) {
    }
}

BannerView는 UIKit 기반 광고 View입니다.

SwiftUI에서는 이를 UIViewRepresentable로 감싸서 사용할 수 있습니다.


17. 적응형 배너 광고

고정 크기의 배너보다는 화면 너비에 맞춰 광고 크기를 계산하는 적응형 배너를 사용하는 것이 좋습니다.

import SwiftUI
import GoogleMobileAds

struct AdaptiveBannerView: View {

    let adUnitID: String

    var body: some View {

        GeometryReader { geometry in

            let width = max(
                geometry.size.width,
                1
            )

            let adSize =
                largeAnchoredAdaptiveBanner(
                    width: width
                )

            BannerAdView(
                adSize: adSize,
                adUnitID: adUnitID
            )
            .frame(
                width: adSize.size.width,
                height: adSize.size.height
            )
            .frame(
                maxWidth: .infinity
            )
        }
        .frame(height: 100)
    }
}

이 코드는 적응형 배너 크기를 계산하는 구조를 보여줍니다.

실제 앱에서는 계산된 광고 높이를 부모 레이아웃에 반영하고, 기기 회전이나 화면 크기 변경 시 광고 크기를 다시 계산해야 합니다.

특히 iPad, 멀티태스킹 화면, 접히는 디스플레이처럼 화면 너비가 변경되는 환경에서는 고정 너비를 가정하지 않는 것이 좋습니다.


18. 화면 하단에 배너 배치

SwiftUI에서는 safeAreaInset을 이용해 화면 하단에 광고 영역을 배치할 수 있습니다.

import SwiftUI

struct MainView: View {

    let canShowAds: Bool
    let consentManager: ConsentManager

    var body: some View {

        NavigationStack {

            List(1...30, id: \.self) { index in

                Text("목록 항목 \(index)")
            }
            .navigationTitle("샘플 앱")
            .safeAreaInset(edge: .bottom) {

                if canShowAds {

                    AdaptiveBannerView(
                        adUnitID:
                            "ca-app-pub-3940256099942544/2435281174"
                    )
                }
            }
            .toolbar {

                ToolbarItem(
                    placement: .topBarTrailing
                ) {

                    NavigationLink("설정") {

                        PrivacySettingsView(
                            consentManager:
                                consentManager
                        )
                    }
                }
            }
        }
    }
}

이 구조는 광고가 목록 콘텐츠를 직접 덮지 않도록 별도 영역을 확보합니다.

사진 편집 앱이라면 편집 도구 버튼과 광고가 너무 가까워지지 않도록 배치해야 합니다.

광고 영역을 사용자의 주요 작업 영역이나 저장 버튼 위에 겹쳐 표시하는 것은 피하는 것이 좋습니다.


19. 전면 광고란?

전면 광고는 앱 화면 전체를 덮는 광고 형식입니다.

Android의 InterstitialAd와 비슷합니다.

일반적으로 다음과 같은 자연스러운 전환 시점에 사용하는 것이 적합합니다.

  • 게임 스테이지 완료

  • 작업 완료 후 다음 화면 이동

  • 콘텐츠 소비 단위 종료

  • 사용자가 명확하게 구분할 수 있는 화면 전환

반면 다음 상황에서는 신중해야 합니다.

  • 앱 실행 직후 갑작스럽게 표시

  • 사용자가 편집 중일 때 표시

  • 저장 버튼을 누르자마자 저장을 방해

  • 화면을 조금 이동할 때마다 표시

  • 광고 닫기 직후 또 다른 광고 표시

광고 노출 횟수를 늘리는 것보다 사용자 경험을 유지하는 것이 중요합니다.


20. 전면 광고 테스트 ID

Google에서 제공하는 iOS 전면 광고 테스트 ID는 다음과 같습니다.

ca-app-pub-3940256099942544/4411468910

이 값으로 개발 중 광고 로딩과 표시를 테스트할 수 있습니다.


21. InterstitialAdManager 구현

InterstitialAdManager.swift

import Foundation
import Observation
import GoogleMobileAds

@MainActor
@Observable
final class InterstitialAdManager:
    NSObject,
    FullScreenContentDelegate {

    private var interstitialAd: InterstitialAd?

    private(set) var isLoading = false

    private var loadedAt: Date?

    private let adUnitID =
        "ca-app-pub-3940256099942544/4411468910"

    func loadAd() async {

        guard !isLoading,
              interstitialAd == nil else {
            return
        }

        isLoading = true

        defer {
            isLoading = false
        }

        do {

            let ad = try await InterstitialAd.load(
                with: adUnitID,
                request: Request()
            )

            ad.fullScreenContentDelegate = self

            interstitialAd = ad
            loadedAt = .now

        } catch {

            print(
                "전면 광고 로딩 실패: \(error.localizedDescription)"
            )
        }
    }

    func showAd() {

        guard let interstitialAd else {
            return
        }

        if let loadedAt,
           Date().timeIntervalSince(loadedAt) >= 3600 {

            self.interstitialAd = nil
            self.loadedAt = nil

            Task {
                await loadAd()
            }

            return
        }

        interstitialAd.present(from: nil)
    }

    func adDidDismissFullScreenContent(
        _ ad: any FullScreenPresentingAd
    ) {

        interstitialAd = nil
        loadedAt = nil

        Task {
            await loadAd()
        }
    }

    func ad(
        _ ad: any FullScreenPresentingAd,
        didFailToPresentFullScreenContentWithError error: Error
    ) {

        print(
            "광고 표시 실패: \(error.localizedDescription)"
        )

        interstitialAd = nil
        loadedAt = nil

        Task {
            await loadAd()
        }
    }
}

전면 광고는 미리 로딩해 두고 자연스러운 시점에 표시하는 것이 좋습니다.

또한 로딩한 광고는 무기한 보관하면 안 됩니다. Google은 전면 광고의 만료를 고려해 약 1시간 이내에 새로 로딩하도록 안내합니다.


22. 전면 광고 표시 시점 제어

전면 광고를 무조건 표시하는 대신 앱의 사용 흐름에 맞춰 빈도를 제한하겠습니다.

예를 들어 콜라주를 여러 번 저장했을 때만 광고 표시를 검토할 수 있습니다.

import Foundation

@MainActor
final class AdFrequencyController {

    private var completedCount = 0

    private var lastShownAt: Date?

    func shouldShowInterstitial() -> Bool {

        completedCount += 1

        guard completedCount >= 3 else {
            return false
        }

        if let lastShownAt,
           Date().timeIntervalSince(lastShownAt) < 180 {

            return false
        }

        completedCount = 0
        lastShownAt = .now

        return true
    }
}

이 예제는 세 번의 작업 완료와 3분의 시간 간격을 기준으로 광고 표시 여부를 판단합니다.

이 수치는 Google의 공식 권장 횟수가 아니라 설명을 위한 예시입니다.

실제 앱에서는 광고 정책과 사용자 경험을 고려해 빈도를 결정해야 합니다.

또한 광고 로딩 실패나 표시 실패 시에도 사용자의 작업은 정상적으로 완료되어야 합니다.


23. 사진 콜라주 앱에 광고 적용하기

사진 콜라주 앱이라면 다음과 같은 광고 배치를 고려할 수 있습니다.

화면광고 형식고려 사항
프로젝트 목록배너목록 콘텐츠와 구분
템플릿 선택배너템플릿 선택을 방해하지 않음
사진 편집가급적 광고 최소화터치 오작동 방지
저장 완료전면 광고 검토저장 완료 후 자연스러운 전환
설정일반적으로 불필요개인정보 설정 접근성 확보

특히 편집 화면은 사용자가 손가락으로 사진을 이동하거나 확대하는 공간입니다.

이 화면에 광고를 배치하면 터치 오작동이 발생할 수 있습니다.

따라서 프로젝트 목록이나 템플릿 선택 화면에 배너를 표시하고, 편집 화면에서는 광고를 최소화하는 전략이 적합할 수 있습니다.


24. 개인정보 설정 화면 구현

UMP 설정에 따라 사용자가 개인정보 선택을 변경할 수 있는 진입점이 필요할 수 있습니다.

이를 위해 앱 설정 화면에 개인정보 옵션을 추가하겠습니다.

PrivacySettingsView.swift

import SwiftUI

struct PrivacySettingsView: View {

    let consentManager: ConsentManager

    @State private var errorMessage: String?

    var body: some View {

        Form {

            Section("개인정보") {

                if consentManager.isPrivacyOptionsRequired {

                    Button("광고 개인정보 설정") {

                        Task {

                            do {

                                try await consentManager
                                    .presentPrivacyOptions()

                            } catch {

                                errorMessage =
                                    error.localizedDescription
                            }
                        }
                    }
                }

                Link(
                    "개인정보처리방침",
                    destination: URL(
                        string:
                            "https://example.com/privacy"
                    )!
                )
            }
        }
        .navigationTitle("설정")
        .alert(
            "오류",
            isPresented: Binding(
                get: { errorMessage != nil },
                set: {
                    if !$0 {
                        errorMessage = nil
                    }
                }
            )
        ) {
            Button("확인", role: .cancel) {}
        } message: {
            Text(errorMessage ?? "")
        }
    }
}

https://example.com/privacy는 예시 주소입니다.

실제 출시 시에는 앱의 개인정보처리방침 URL로 변경해야 합니다.

또한 사용자가 개인정보 선택을 변경한 뒤에는 새로운 동의 상태에 맞게 이후 광고 요청을 처리해야 합니다.


25. UMP와 ATT의 차이

UMP와 ATT는 서로 대체할 수 있는 기능이 아닙니다.

구분UMPATT
제공 주체GoogleApple
목적광고 관련 개인정보 선택 관리앱 간 추적 권한 관리
주요 대상적용 지역·규정·광고 설정Apple의 추적 정의에 해당하는 활동
구현UserMessagingPlatformAppTrackingTransparency
거부 시동의 상태에 맞는 광고 처리추적 제한 및 IDFA 접근 제한

따라서 UMP에서 동의를 받았다고 해서 ATT 권한이 자동으로 허용되는 것은 아닙니다.

반대로 ATT를 허용했다고 해서 UMP 동의 절차를 생략할 수 있는 것도 아닙니다.


26. ATT 권한을 거부하면 광고가 나오지 않을까?

그렇지는 않습니다.

ATT 거부는 모든 광고를 금지한다는 의미가 아닙니다.

광고 제공 방식과 개인정보 처리 설정에 따라 추적을 사용하지 않는 광고를 제공할 수 있습니다.

다만 맞춤형 광고, 광고 측정, 광고 식별자 사용 등에 영향을 줄 수 있습니다.

광고 수익에 어떤 영향을 주는지는 앱 사용자 지역, 광고 수요, 광고 형식, 광고 네트워크 설정 등에 따라 달라집니다.

따라서 ATT 허용률만으로 예상 수익을 단정하는 것은 적절하지 않습니다.


27. App Store 개인정보 공개 항목

App Store Connect에서는 앱의 개인정보 처리 방식을 공개해야 합니다.

광고 SDK를 사용하는 앱은 앱 코드에서 직접 수집하지 않는 정보라도 SDK가 처리하는 데이터를 확인해야 합니다.

대표적으로 검토할 수 있는 항목은 다음과 같습니다.

  • 식별자

  • 사용 데이터

  • 진단 데이터

  • 위치 관련 정보

  • 광고 데이터

  • 추적 목적의 데이터 사용 여부

실제 공개 항목은 사용 중인 Google Mobile Ads SDK 버전, 미디에이션 구성, 동의 설정 및 앱의 데이터 처리 방식에 따라 달라집니다.

Google의 데이터 공개 안내와 Apple의 App Privacy 문서를 함께 확인해야 합니다.


28. 개인정보처리방침에 포함할 내용

AdMob을 사용하는 앱이라면 개인정보처리방침에 광고와 관련된 내용을 명확하게 작성하는 것이 좋습니다.

예를 들어 다음 사항을 검토할 수 있습니다.

  1. 광고 서비스 제공자

  2. 광고 SDK 사용 목적

  3. 처리될 수 있는 데이터 유형

  4. 맞춤형 광고 및 추적 관련 사항

  5. 사용자 동의와 선택 변경 방법

  6. 제3자 개인정보처리방침 링크

  7. 문의 방법

  8. 데이터 보관 및 처리 정책

단, 개인정보처리방침은 앱에서 실제로 수행하는 데이터 처리와 일치해야 합니다.

단순히 다른 앱의 개인정보처리방침을 복사하는 것은 피해야 합니다.


29. app-ads.txt 설정

AdMob을 사용하는 앱에서는 app-ads.txt도 확인해야 합니다.

app-ads.txt는 앱의 광고 인벤토리를 판매하도록 허가된 광고 판매자를 선언하는 파일입니다.

예를 들어 개발자 웹사이트의 루트 경로에 다음과 같이 배치할 수 있습니다.

https://example.com/app-ads.txt

파일 내용에는 AdMob에서 안내하는 게시자 정보를 입력합니다.

예시 형식은 다음과 같습니다.

google.com, pub-XXXXXXXXXXXXXXXX, DIRECT, f08c47fec0942fa0

여기서 게시자 ID는 자신의 AdMob 게시자 ID로 교체해야 합니다.

또한 App Store에 등록된 개발자 웹사이트 정보와 app-ads.txt 파일을 호스팅하는 도메인의 연결 관계를 확인해야 합니다.

Android 앱에서 사용하던 app-ads.txt 설정 경험을 iOS에도 활용할 수 있지만, 앱 스토어에 등록된 개발자 웹사이트 정보가 올바른지 별도로 점검해야 합니다.


30. 테스트 광고가 중요한 이유

개발 과정에서는 반드시 테스트 광고를 사용해야 합니다.

실제 광고 단위를 사용하면서 반복적으로 광고를 클릭하거나 테스트하면 무효 트래픽 문제가 발생할 수 있습니다.

Google에서 제공하는 iOS 테스트 광고 단위는 다음과 같습니다.

광고 형식테스트 광고 단위 ID
배너ca-app-pub-3940256099942544/2435281174
전면ca-app-pub-3940256099942544/4411468910

출시 전에는 실제 광고 단위 ID로 교체해야 합니다.

자신의 광고 단위로 테스트할 때는 Google이 안내하는 테스트 기기 설정을 적용해야 합니다.


31. 광고가 표시되지 않을 때 확인할 사항

광고가 나오지 않는다고 해서 무조건 코드 오류는 아닙니다.

다음 항목을 순서대로 확인하는 것이 좋습니다.

  • AdMob 앱 ID가 올바른가?

  • 광고 단위 ID가 올바른가?

  • Google Mobile Ads SDK가 초기화되었는가?

  • UMP의 canRequestAds 상태를 확인했는가?

  • 테스트 광고 단위로도 실패하는가?

  • 인터넷 연결은 정상인가?

  • 광고 View의 크기가 정상인가?

  • 광고 요청 실패 로그가 있는가?

  • 광고 단위가 새로 생성된 상태인가?

  • AdMob 계정 또는 앱에 게재 제한이 있는가?

광고 요청 실패 원인은 Google Mobile Ads SDK의 오류 로그와 Ad Inspector를 이용해 확인할 수 있습니다.


32. Ad Inspector 활용

Ad Inspector는 광고 요청과 광고 소스 관련 문제를 확인하는 데 도움이 되는 Google의 진단 도구입니다.

특히 다음 상황에서 유용합니다.

  • 광고 요청 실패

  • 미디에이션 설정 문제

  • 특정 광고 네트워크의 응답 실패

  • 광고 로딩 지연

  • 테스트 기기에서의 광고 동작 확인

실제 광고가 표시되지 않을 때는 무작정 광고 요청을 반복하기보다 오류 코드와 광고 응답 정보를 먼저 확인하는 것이 좋습니다.


33. 광고 수익 지표 이해하기

AdMob 수익을 분석할 때 자주 사용하는 지표는 다음과 같습니다.

지표의미
Requests광고 요청 수
Impressions광고 노출 수
Match rate광고 요청에 대한 광고 매칭 비율
eCPM광고 노출 1,000회당 예상 수익
Estimated earnings예상 수익

예를 들어 광고 노출이 10,000회이고 eCPM이 2달러라면 단순 계산한 예상 수익은 다음과 같습니다.

[
\frac{10,000}{1,000}\times2=20\text{달러}
]

이 수치는 계산 예시일 뿐 실제 광고 수익을 보장하지 않습니다.

국가, 광고 형식, 사용자 구성, 광고 수요 등에 따라 eCPM은 크게 달라질 수 있습니다.


34. 광고 수익화 전략

광고를 많이 표시한다고 해서 항상 수익이 증가하는 것은 아닙니다.

사용자가 광고 때문에 앱 사용을 중단하면 장기적으로 수익이 감소할 수도 있습니다.

특히 사진 편집 앱에서는 사용자가 작업에 집중할 수 있도록 하는 것이 중요합니다.

초기 출시 단계에서는 다음과 같은 전략을 고려할 수 있습니다.

  • 프로젝트 목록에 배너 광고

  • 편집 화면에서는 광고 최소화

  • 저장 완료 후 자연스러운 전환에서 전면 광고 검토

  • 광고 표시 빈도 제한

  • 사용자가 동의 선택을 변경할 수 있는 설정 제공

  • 광고 오류 시에도 주요 기능 정상 제공

이러한 구조를 먼저 적용하고 실제 사용자 지표를 확인하면서 개선하는 것이 좋습니다.


35. 출시 전 테스트 체크리스트

  • AdMob iOS 앱 등록

  • 앱 ID와 광고 단위 ID 구분

  • Google Mobile Ads SDK 설치

  • UMP SDK 설치

  • Info.plist 앱 ID 등록

  • SKAdNetwork 설정 확인

  • UMP 동의 메시지 구성

  • 앱 실행 시 동의 정보 업데이트

  • canRequestAds 확인

  • ATT 적용 필요 여부 검토

  • ATT 설명 문구 확인

  • 배너 테스트 광고 표시

  • 전면 테스트 광고 로딩

  • 전면 광고 닫기 처리

  • 광고 표시 빈도 제한

  • 광고 요청 실패 처리

  • 개인정보 설정 화면

  • 개인정보처리방침 URL

  • App Store 개인정보 공개 항목

  • app-ads.txt 확인

  • 실제 iPhone 테스트

  • TestFlight 테스트

  • 출시 전 광고 단위 ID 교체


36. 마무리

SwiftUI 앱에서도 Google AdMob을 이용해 배너 광고와 전면 광고를 구현할 수 있습니다.

Android에서 AdMob을 사용해 본 개발자라면 광고 요청과 로딩, 표시, 콜백 처리 구조는 비교적 익숙할 것입니다.

하지만 iOS에서는 광고 SDK 연동 외에도 ATT, UMP, App Store 개인정보 공개 항목 등을 함께 검토해야 합니다.

이번 글의 핵심을 정리하면 다음과 같습니다.

  1. Google Mobile Ads SDK는 Swift Package Manager로 설치할 수 있습니다.

  2. AdMob 앱 ID와 광고 단위 ID는 서로 다릅니다.

  3. SwiftUI에서는 UIViewRepresentable로 배너 광고를 표시할 수 있습니다.

  4. 적응형 배너를 사용하면 화면 너비에 맞는 광고를 요청할 수 있습니다.

  5. 전면 광고는 미리 로딩하고 자연스러운 전환 시점에 표시해야 합니다.

  6. UMP를 이용해 개인정보 동의 상태를 관리할 수 있습니다.

  7. ATT와 UMP는 서로 다른 목적의 개인정보 보호 기능입니다.

  8. 개인정보 선택에 따라 광고 요청을 제어해야 합니다.

  9. 개발 과정에서는 반드시 테스트 광고를 사용해야 합니다.

  10. 광고 수익보다 앱의 주요 기능과 사용자 경험을 우선해야 합니다.

iOS 앱 수익화의 핵심은 광고를 표시하는 코드 자체보다 개인정보 보호 요구사항을 충족하면서 사용자 경험을 해치지 않는 광고 구조를 만드는 것입니다.


공식 참고 자료

태그: iOS, Swift, SwiftUI, AdMob, Google Mobile Ads SDK, 배너 광고, 전면 광고, ATT, IDFA, UMP, SKAdNetwork, app-ads.txt, App Store, 개인정보 보호, iPhone 앱 수익화