# iOS SDK의 기능 플래그

`Pushwoosh.configure.featureFlags`는 Control Panel에서 애플리케이션에 구성된 [기능 플래그](/ko/product/audience-data-and-segmentation/feature-flags/)를 iOS 앱에 제공합니다. Pushwoosh는 현재 기기와 사용자에 대해 서버에서 플래그 규칙을 평가하고, SDK는 그 결과를 반환합니다. 즉, 이 기기에 플래그가 켜져 있는지와 어떤 속성 값이 적용되는지를 반환합니다.

<Aside type="tip" title="베타 기능">
기능 플래그는 베타 단계이며 이 기능이 포함된 요금제에서만 사용할 수 있습니다. 다른 요금제에서는 서버가 플래그를 반환하지 않으므로 모든 플래그가 코드에서 전달한 기본값으로 대체됩니다.
</Aside>

## 사전 요구 사항

* [iOS SDK 개요](/ko/developer/pushwoosh-sdk/ios-sdk/)에 설명된 대로 통합한 Pushwoosh iOS SDK 7.2.10 이상. 기능 플래그는 필수 모듈인 `PushwooshFramework`와 `PushwooshCore`에 포함되어 있으므로 다른 모듈을 추가할 필요가 없습니다. 기능 플래그는 iOS 앱 자체에서 동작합니다. Notification Service Extension 같은 앱 확장 프로그램은 플래그를 요청하지 않습니다.
* **Audience** → **Feature Flags**에서 생성한 플래그가 하나 이상 있어야 합니다. 해당 플래그의 **Key**와 속성의 키 및 유형이 필요합니다. 키는 대소문자를 포함해 그곳에 표시된 그대로 사용하세요. 예를 들어 `get("New_Checkout")`은 키가 `new_checkout`인 플래그를 찾지 못합니다.
* 앱이 리버스 프록시를 통해 SDK 요청을 보내는 경우 프록시 URL은 `json/1.3/`으로 끝나야 합니다. 그렇지 않으면 SDK가 플래그를 요청하지 않으며, 모든 플래그가 지정한 기본값으로 대체됩니다.

## 플래그 읽기

항상 기본값을 전달하세요. 기본값은 SDK가 기기의 플래그를 가져오기 전까지, 그리고 플래그가 없거나 보관되었거나 요금제에 기능 플래그가 포함되지 않은 경우에 적용됩니다.

```swift
import PushwooshFramework

let flags = Pushwoosh.configure.featureFlags

if let checkout = flags.get("new_checkout"), checkout.isEnabled {
    showNewCheckout(
        buttonColor: checkout.string(forProperty: "button_color", default: "blue") ?? "blue",
        maxItems: Int(checkout.number(forProperty: "max_items", default: 3))
    )
} else {
    showOldCheckout()
}
```

플래그가 켜져 있는지만 확인하려면 `isEnabled`를 사용하세요.

```swift
let isNewCheckout = Pushwoosh.configure.featureFlags.isEnabled("new_checkout", default: false)
```

Objective-C에서는 `[PushwooshConfig featureFlags]`를 호출하세요.

## 플래그가 반환하는 내용

`get(_:)`은 `PWFeatureFlag` 객체를 반환하며, SDK가 아직 기기의 플래그를 가지고 있지 않거나 응답에 플래그가 없으면 `nil`을 반환합니다. 보관된 플래그는 응답에 포함되지 않습니다. `get`은 저장된 플래그만 읽으며 네트워크를 기다리지 않습니다. 객체에는 다음 속성이 있습니다.

| 속성 | 유형 | 설명 |
| :---- | :---- | :---- |
| `key` | String | 플래그 키입니다. |
| `isEnabled` | Bool | 이 기기에 플래그가 켜져 있는지 여부입니다. |
| `ruleId` | String | 결과를 결정한 규칙입니다(예: `r_1a2b3c4d`). 기기와 일치하는 규칙이 없으면 `default`이고, 플래그가 강제 종료(kill)된 경우 빈 문자열입니다. |

속성 유형에 맞는 메서드로 속성 값을 읽으세요. 플래그가 꺼져 있으면 속성에는 Control Panel에서 설정한 기본값이 들어 있습니다.

| 메서드 | 반환값 |
| :---- | :---- |
| `string(forProperty:default:)` | **String** 속성의 값 또는 지정한 기본값입니다. |
| `number(forProperty:default:)` | **Number** 속성의 값(`Double`) 또는 지정한 기본값입니다. |
| `bool(forProperty:default:)` | **Boolean** 속성의 값 또는 지정한 기본값입니다. |
| `jsonObject(forProperty:)` | 객체를 담은 **JSON** 속성의 값을 딕셔너리로 반환하거나 `nil`을 반환합니다. |
| `jsonArray(forProperty:)` | 배열을 담은 **JSON** 속성의 값 또는 `nil`입니다. |

각 메서드는 속성이 없거나 다른 유형의 값을 가지고 있으면 지정한 기본값을 반환하며, JSON 메서드는 `nil`을 반환합니다.

`get(_:)`이 `nil`을 반환하면 `isEnabled(_:default:)`는 기본값을 반환합니다.

<span id="keep-flags-up-to-date" />

## 플래그를 최신 상태로 유지하기

SDK는 마지막 응답을 기기에 최대 24시간 동안 저장합니다. 저장된 플래그는 요청 당시의 User ID와 앱의 Application Code에 대해서만 유효합니다. SDK는 다음 시점에 플래그를 새로 고칩니다.

* 세션이 시작될 때, 즉 앱이 처음 활성화되거나 백그라운드에 30분 이상 있다가 다시 활성화될 때
* 앱이 포그라운드에 있는 동안 1시간마다
* 기기가 푸시 알림에 등록된 후, 이전 요청이 등록 전에 전송된 경우
* `setUserId`로 User ID가 변경된 직후. 이전 사용자의 저장된 플래그는 삭제되므로 새 사용자는 이를 볼 수 없습니다.

새 값에 반응하려면 `PWFeatureFlagsUpdateListener`를 채택한 리스너를 추가하세요. 리스너의 `featureFlagsDidUpdate()`는 플래그를 변경한 새로 고침 후에 메인 스레드에서 호출됩니다. SDK는 리스너를 약한 참조로 보유합니다. 리스너에 대한 강한 참조를 유지하지 않으면 리스너가 해제되어 호출되지 않습니다.

```swift
final class CheckoutViewController: UIViewController, PWFeatureFlagsUpdateListener {
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        Pushwoosh.configure.featureFlags.addUpdateListener(self)
    }

    override func viewWillDisappear(_ animated: Bool) {
        Pushwoosh.configure.featureFlags.removeUpdateListener(self)
        super.viewWillDisappear(animated)
    }

    func featureFlagsDidUpdate() {
        render()
    }
}
```

사용자를 다른 Segment로 이동시킬 수 있는 작업 이후처럼 플래그를 직접 요청하려면 `refresh(completion:)`을 호출하세요. 저장된 플래그가 유효한 동안에는 5분에 최대 한 번만 요청을 보내며, 그 기간 안에서는 저장된 플래그로 응답합니다. completion은 메인 스레드에서 실행됩니다.

```swift
Pushwoosh.configure.featureFlags.refresh { error in
    if let error = error as NSError? {
        let status = error.code
        // the stored flags, or your defaults, stay in effect
    }
    render()
}
```

실패하면 오류의 도메인은 `PWFeatureFlagsErrorDomain`이고, `code`는 HTTP 상태입니다. 요청이 HTTP 응답을 받지 못했거나 전송되지 않은 경우 코드는 `0`이고, 서버가 응답했지만 응답을 사용할 수 없는 경우 `200`입니다.

5분 제한 없이 새로 고침을 테스트하려면 `Info.plist`에서 Boolean 키 `Pushwoosh_FEATURE_FLAGS_DISABLE_THROTTLE`을 `YES`로 설정하세요.

Pushwoosh가 기기의 Segment를 제때 확인하지 못하면 부분 응답을 반환합니다. SDK는 이 응답을 기기에 저장하지 않으며, 다음 새로 고침에서 전체 응답을 받아옵니다.

Control Panel에서 플래그를 강제 종료(kill)하면 기기는 다음 새로 고침 때 기본 속성 값과 함께 꺼진 상태의 플래그를 받습니다. SDK가 서버에 연결할 수 없으면 저장된 플래그가 24시간이 될 때까지 계속 사용하고, 그 이후에는 지정한 기본값을 사용합니다.

<span id="log-impressions" />

## 노출 기록하기

노출(impression)은 사용자가 플래그가 제어하는 코드에 실제로 도달했음을 Pushwoosh에 알려 주므로, 기능을 받은 사용자와 받지 않은 사용자를 비교할 수 있습니다. 사용자가 기능 또는 이전 버전을 보는 시점에 `logImpression(_:)`을 호출하세요. 플래그가 꺼진 사용자가 비교 그룹이므로 켜진 분기와 꺼진 분기 모두에서 호출해야 합니다.

```swift
let flags = Pushwoosh.configure.featureFlags
let isOn = flags.isEnabled("new_checkout", default: false)
flags.logImpression("new_checkout")
if isOn {
    showNewCheckout()
} else {
    showOldCheckout()
}
```

SDK는 다음 속성과 함께 `PW_FeatureFlagImpression` 이벤트를 전송합니다.

| 속성 | 유형 | 값 |
| :---- | :---- | :---- |
| `flag_key` | string | 플래그 키입니다. |
| `rule_id` | string | 결과를 결정한 규칙으로, `ruleId`와 같습니다. |
| `enabled` | boolean | 플래그가 켜져 있었는지 여부입니다. |

첫 번째 노출이 도착하면 Pushwoosh가 이러한 속성과 함께 `PW_FeatureFlagImpression`을 **Audience** → **Events**에 추가하므로 이벤트를 미리 만들 필요가 없습니다. SDK는 한 세션에서 플래그, 규칙, 상태별로 노출을 한 번만 전송하며, 해당 키에 대한 플래그가 없으면 아무것도 전송하지 않습니다. 전송에 실패하면 해당 플래그에 대한 다음 `logImpression` 호출 시 다시 전송합니다.

노출을 활용하면 마케터가 기능에 도달한 사용자의 Segment를 만들 수도 있습니다. [기능 플래그 조건](/ko/product/audience-data-and-segmentation/feature-flags/#find-users-who-reached-a-feature)의 **기능을 봄 (30일)** 상태를 사용하세요.

## 메서드 참조

`Pushwoosh.configure.featureFlags`에는 다음 메서드가 있습니다.

| 메서드 | 설명 |
| :---- | :---- |
| `get(_:)` | 키에 해당하는 `PWFeatureFlag` 또는 `nil`을 반환합니다. 저장된 플래그만 읽습니다. |
| `isEnabled(_:default:)` | 플래그가 켜져 있는지 여부를 반환하며, 플래그가 없으면 기본값을 반환합니다. |
| `refresh(completion:)` | 플래그를 요청하며, 저장된 플래그가 유효한 동안에는 5분에 한 번으로 제한됩니다. completion은 `nil` 또는 `PWFeatureFlagsErrorDomain`의 오류를 받으며, completion 자체는 `nil`일 수 있습니다. |
| `addUpdateListener(_:)` | 플래그를 변경한 모든 새로 고침 후에 리스너의 `featureFlagsDidUpdate()`를 호출합니다. 리스너는 약한 참조로 보유됩니다. |
| `removeUpdateListener(_:)` | `addUpdateListener(_:)`로 추가한 리스너를 제거합니다. |
| `logImpression(_:)` | 플래그의 현재 규칙과 상태에 대한 `PW_FeatureFlagImpression` 이벤트를 전송합니다. |