구간 제어 통합
코드 기반 통합을 사용하여 NetFUNNEL iOS 에이전트로 구간 제어를 구현하는 완전한 가이드입니다.
이것은 사용 가능한 두 가지 통합 방법 중 하나입니다. 통합 방법 개요를 참조하여 기본 제어와 비교하고 사용 사례에 가장 적합한 방법을 선택하세요.
작동 방식
사용자 경험:
- 사용자가 버튼을 클릭하거나 작업을 트리거합니다
- 대기실이 현재 화면에 나타납니다
- 진입이 허용되면 대기실이 닫히고 사용자가 활성 구간에 진입합니다
- 사용자는 구간 내에서 여러 화면을 탐색할 수 있습니다
- 사용자가 프로세스를 완료하면 키가 반환되고 다음 사용자가 진입할 수 있습니다
최적 사용 사례:
- 다단계 프로세스 (체크아웃, 등록, 예약)
- 특정 영역에서 고정된 동시 사용자 수 유지
- 사용자 세션 관리
- 제어된 용량이 필요한 리소스 집약적인 워크플로우
기본 제어와의 주요 차이점:
- 기본 제어: 진입 속도를 제어합니다 (키가 빠르게 반환됨)
- 구간 제어: 고정된 동시 사용자 수를 유지합니다 (구간 종료까지 키가 유지됨)
사전 요구 사항
- 설치 및 초기화 완료
- NetFUNNEL 콘솔 접근 권한
- iOS 개발 환경
이 가이드는 샘플 애플리케이션을 사용하여 구간 제어 통합 패턴을 보여줍니다. 실제 애플리케이션 코드는 여기에 표시된 예제와 다를 것입니다. 통합 개념을 이해하는 데 집중하고 패턴을 특정 코드베이스, 함수 이름, 비즈니스 로직에 맞게 조정하세요.
💡 연습 템플릿: NetFUNNEL SDK 통합 연습을 위한 iOS 애플리케이션 (단일 ViewController) 템플릿이 준비된 샘플 프로젝트를 확인하세요.
1단계: 세그먼트 생성
코드 기반 통합은 기본 제어와 구간 제어를 모두 지원합니다. 이 가이드는 구간 제어를 사용합니다.
1.1 새 세그먼트 생성
- NetFUNNEL 콘솔 →
프로젝트→세그먼트로 이동합니다 +버튼을 클릭하여 새 세그먼트를 생성합니다

1.2 제어 유형 선택
구간 제어를 선택하고 다음을 클릭합니다

1.3 세그먼트 구성
세그먼트 이름: 설명적인 이름을 입력합니다 (예: "체크아웃 프로세스", "등록 흐름", "예약 구간")

진입 상태:
- ✅ 세그먼트 활성화 활성화됨
- 진입 상태:
대기(사용자를 대기실로 보냄)

대기실 적용:
- 테스트를 위해 기본 설정 사용
- 라이브 메시지는 비워둡니다

진입 허용 수:
- 테스트를 위해
0으로 설정합니다 (아무도 허용되지 않음, 대기실이 항상 나타남) - 이것은 동시 사용자 수를 제어합니다 - 구간에서 동시에 활성화될 수 있는 사용자 수

1.4 세그먼트 생성
생성을 클릭하여 세그먼트를 완료합니다

2단계: 키 발급 통합 지점 식별 (nfStartSection)
다음 예제는 시연 목적으로 샘플 애플리케이션을 사용합니다. 실제 애플리케이션 코드는 여기에 표시된 것과 자연스럽게 다를 것입니다. 통합 패턴을 특정 코드 구조, 버튼 ID, 함수 이름, 비즈니스 로직에 맞게 조정하세요.
💡 연습 프로젝트가 필요하신가요? NetFUNNEL SDK 통합 연습을 위한 iOS 애플리케이션 (단일 ViewController) 템플릿이 준비된 샘플 프로젝트를 확인하세요.
샘플 애플리케이션 이해하기:
NetFUNNEL 보호를 적용해야 하는 위치를 이해하기 위해 샘플 애플리케이션을 살펴보겠습니다:
2.1 레이아웃에서 대상 버튼 식별
// MainView.swift
struct MainView: View {
@StateObject private var navigationManager = NavigationManager.shared
var body: some View {
ScrollView {
VStack(spacing: 0) {
// Hero Section (간결성을 위해 생략)
// ... 기존 UI 구조 ...
// Cards Section
VStack(spacing: 20) {
// 다른 버튼 - 대상이 아님
Button(action: {
navigationManager.navigateWithDelay(to: .basicControl)
}) {
ModernCard(
icon: "arrow.right.circle.fill",
title: "Basic Control",
subtitle: "Code-based Integration",
description: "Simple linear navigation flow",
accentColor: MaterialColors.lightPrimary
)
}
.buttonStyle(ModernCardButtonStyle())
// 🎯 대상 버튼: 구간 제어 기능 카드
// 이것이 NetFUNNEL로 보호하려는 버튼입니다
Button(action: {
navigationManager.navigateWithDelay(to: .sectionControlSection1)
}) {
ModernCard(
icon: "list.bullet.circle.fill",
title: "Section Control",
subtitle: "Code-based Integration",
description: "Multi-step navigation flow",
accentColor: MaterialColors.lightSecondary
)
}
.buttonStyle(ModernCardButtonStyle())
}
.padding(.horizontal, 20)
Spacer(minLength: 40)
}
}
// 추가 UI 수정자는 간결성을 위해 생략
// ... background, frame, navigationBarHidden 등 ...
}
}
이 샘플에 대한 가정:
- "Section Control" 버튼은 다단계 프로세스를 나타냅니다
- 이것은 다음일 수 있습니다: 체크아웃 흐름, 등록 프로세스, 예약 워크플로우, 또는 다단계 양식
- 사용자는 여러 SwiftUI View를 탐색합니다: Section1 → Section2 → End
- 이 전체 흐름에서 동시에 활성화될 수 있는 사용자 수를 제어해야 합니다
2.2 이 버튼의 클릭 리스너 찾기
클릭 리스너란 무엇인가요?
클릭 리스너(액션 핸들러 또는 이벤트 핸들러라고도 함)는 사용자가 UI 요소와 상호작용할 때 실행되는 코드입니다. SwiftUI에서는 일반적으로 Button(action:) 클로저 구문을 사용하여 정의됩니다.
SwiftUI 버튼 구조 이해하기:
Button(action: {
// 🎯 이것이 클릭 리스너입니다
// 사용자가 버튼을 탭하면 여기의 코드가 실행됩니다
}) {
// 이것이 버튼의 시각적 모양입니다
// 버튼이 어떻게 보이는지 정의합니다
}
샘플 애플리케이션에서:
// MainView.swift
struct MainView: View {
@StateObject private var navigationManager = NavigationManager.shared
var body: some View {
ScrollView {
VStack(spacing: 0) {
// Hero Section (간결성을 위해 생략)
// ... 기존 UI 구조 ...
// Cards Section
VStack(spacing: 20) {
// 다른 버튼 - 대상이 아님 (NetFUNNEL 보호 불필요)
Button(action: {
// 이 버튼은 NetFUNNEL 보호가 필요하지 않습니다
navigationManager.navigateWithDelay(to: .basicControl)
}) {
ModernCard(
icon: "arrow.right.circle.fill",
title: "Basic Control",
subtitle: "Code-based Integration",
description: "Simple linear navigation flow",
accentColor: MaterialColors.lightPrimary
)
}
.buttonStyle(ModernCardButtonStyle())
// 🎯 대상 버튼: 구간 제어 기능 카드
Button(action: {
// 🎯 클릭 리스너: 여기서 버튼 액션이 정의됩니다
// 사용자가 버튼을 클릭하면 실행되는 코드입니다
// 현재: navigationManager.navigateWithDelay(to: .sectionControlSection1)
// 이것을 NetFUNNEL 보호로 감쌀 것입니다
navigationManager.navigateWithDelay(to: .sectionControlSection1)
}) {
// 🎨 버튼 모양: 이것이 버튼이 어떻게 보이는지 정의합니다
ModernCard(
icon: "list.bullet.circle.fill",
title: "Section Control",
subtitle: "Code-based Integration",
description: "Multi-step navigation flow",
accentColor: MaterialColors.lightSecondary
)
}
.buttonStyle(ModernCardButtonStyle())
}
.padding(.horizontal, 20)
Spacer(minLength: 40)
}
}
// 추가 UI 수정자는 간결성을 위해 생략
// ... background, frame, navigationBarHidden 등 ...
}
}
핵심 포인트:
- 클릭 리스너 위치:
Button(action:)클로저 내부 - 현재 액션:
navigationManager.navigateWithDelay(to: .sectionControlSection1)이 즉시 실행됩니다 - 통합 대상: 여기에 NetFUNNEL 보호를 추가할 것입니다
2.3 클릭 리스너 내부에서 일어나는 일 검토
함수가 하는 일:
// NavigationManager.swift
func navigateWithDelay(to destination: NavigationDestination, customDelay: TimeInterval? = nil) {
let delay = customDelay ?? appPreferences.getNavigationDelay()
loadingDialogManager.showLoading() // ← 로딩 표시기 표시
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
self.loadingDialogManager.hideLoading() // ← 로딩 표시기 숨김
self.navigate(to destination) // ← 대상 화면으로 이동
}
}
요약: 로딩 표시 → 대기 → 로딩 숨김 → 이동. 이것은 서버 처리를 시뮬레이션하며 NetFUNNEL 보호가 필요합니다.
2.4 통합 지점 식별
- 대상 버튼:
MainView의 "Section Control" 버튼 (다단계 프로세스 버튼) - 클릭 리스너:
Button(action:)클로저가 클릭 핸들러를 설정합니다 - 통합 위치:
navigationManager.navigateWithDelay()가 호출되기 직전 - 여기서 하는 이유: 이것은 구간 진입이 시작되기 직전의 정확한 순간입니다
- 보호 전략: 구간 진입 전에 NetFUNNEL 구간 큐를 추가합니다
전체 흐름 분석:
- 뷰 생성:
MainViewbody가 버튼을 생성합니다 - 버튼 설정:
Button(action:)이 버튼의 클릭 핸들러를 설정합니다 - 사용자 액션: 사용자가 "Section Control" 버튼을 클릭합니다
- 현재 동작:
navigationManager.navigateWithDelay()가 즉시 실행됩니다 - 구간 로드: 이것이 다단계 구간 프로세스로의 진입을 트리거합니다
로직:
- NetFUNNEL 없이: 버튼 클릭 → 즉시 구간 진입 → 잠재적 동시 사용자 수 초과
- NetFUNNEL 사용: 버튼 클릭 → 큐 확인 → 제어된 구간 진입 → 동시 사용자 수 유지
3단계: 키 발급 함수 구현 (nfStartSection)
아래 예제는 샘플 애플리케이션의 버튼 핸들러에 NetFUNNEL을 통합하는 방법을 보여줍니다. 이 패턴을 실제 코드 구조에 맞게 조정하세요 - 보호가 필요한 다른 함수 이름, 이벤트 핸들러 또는 비즈니스 로직이 있을 수 있습니다.
3.1 키 가져오기
먼저 콘솔에서 프로젝트 키와 세그먼트 키를 찾으세요:
- NetFUNNEL 콘솔 →
프로젝트→세그먼트로 이동합니다 - 세그먼트를 클릭합니다
- 프로젝트 키와 세그먼트 키를 복사합니다

3.2 nfStartSection 함수 이해하기
nfStartSection 함수는 다음과 같은 기본 구조를 가집니다:
- Swift
- Objective-C
Netfunnel.shared.nfStartSection(
projectKey: "your_project_key", // 콘솔에서 가져옴
segmentKey: "your_segment_key" // 콘솔에서 가져옴
)
[[Netfunnel shared] nfStartSectionWithProjectKey:@"your_project_key"
segmentKey:@"your_segment_key"];
nfStartSection 매개변수, 델리게이트 처리, 응답 형식에 대한 자세한 내용은 API 참조를 참조하세요.
3.3 현재 코드로 시작하기
현재 구현:
// MainView.swift
Button(action: {
navigationManager.navigateWithDelay(to: .sectionControlSection1)
}) {
ModernCard(
icon: "list.bullet.circle.fill",
title: "Section Control",
subtitle: "Code-based Integration",
description: "Multi-step navigation flow",
accentColor: MaterialColors.lightSecondary
)
}
.buttonStyle(ModernCardButtonStyle())
핵심 개념:
- 비즈니스 로직:
navigationManager.navigateWithDelay(to: .sectionControlSection1)은 다단계 구간으로의 진입을 나타냅니다 - 통합 지점: 이 구간 진입을 NetFUNNEL 보호로 감싸야 합니다
- 감싸기 전략: 다단계 프로세스가 시작되기 전에
nfStartSection()을 사용하여 동시 사용자 수를 제어합니다
여기서 감싸는 이유:
- 이것은 구간 진입이 시작되기 직전의 정확한 순간입니다
- 여기서 감싸면 전체 다단계 구간이 동시 사용자 수 초과로부터 보호됩니다
- 비즈니스 로직은 변경되지 않습니다 - 동시 사용자 수 제어 레이어만 추가합니다
3.4 필요한 import 추가
NetFUNNEL을 구현하기 전에 SwiftUI View에 필요한 import를 추가하세요:
- Swift
- Objective-C
// MainView.swift
import Netfunnel_iOS
// MainViewController.m
#import <Netfunnel_iOS/Netfunnel_iOS.h>
핵심 Import:
Netfunnel_iOS- 메인 NetFUNNEL 프레임워크
3.5 기본 NetFUNNEL 보호 추가 (성공만)
설치 및 초기화 단계에서 이미 NetfunnelHandler.swift가 있어야 합니다. 아직 해당 단계를 완료하지 않았다면 돌아가서 먼저 완료하세요.
비즈니스 로직 감싸기:
// MainView.swift
import SwiftUI
import Netfunnel_iOS
struct MainView: View {
@StateObject private var navigationManager = NavigationManager.shared
var body: some View {
ScrollView {
VStack(spacing: 0) {
// Hero Section은 간결성을 위해 생략
// ... 기존 UI 구조 ...
// Cards Section
VStack(spacing: 20) {
// Basic Control 기능 카드
Button(action: {
navigationManager.navigateWithDelay(to: .basicControl)
}) {
ModernCard(
icon: "arrow.right.circle.fill",
title: "Basic Control",
subtitle: "Code-based Integration",
description: "Simple linear navigation flow",
accentColor: MaterialColors.lightPrimary
)
}
.buttonStyle(ModernCardButtonStyle())
// 구간 제어 기능 카드 - NetFUNNEL로 보호됨
Button(action: {
startSectionControl() // NetFUNNEL 보호 함수 호출
}) {
ModernCard(
icon: "list.bullet.circle.fill",
title: "Section Control",
subtitle: "Code-based Integration",
description: "Multi-step navigation flow",
accentColor: MaterialColors.lightSecondary
)
}
.buttonStyle(ModernCardButtonStyle())
}
.padding(.horizontal, 20)
Spacer(minLength: 40)
}
}
// 추가 UI 수정자는 간결성을 위해 생략
// ... background, frame, navigationBarHidden 등 ...
.onAppear {
setupCallbacks()
}
}
private func setupCallbacks() {
// NetFUNNEL 응답에 대한 콜백 클로저 설정
NetfunnelHandler.shared.onSuccess = { projectKey, segmentKey, statusCode, message in
NSLog("onSuccess \(statusCode) \(message)")
// NetFUNNEL이 진입을 허용할 때 원래 비즈니스 로직 실행
navigationManager.navigateWithDelay(to: .sectionControlSection1)
}
NetfunnelHandler.shared.onError = { projectKey, segmentKey, statusCode, message in
NSLog("onError \(statusCode) \(message)")
}
NetfunnelHandler.shared.onNetworkError = { projectKey, segmentKey, statusCode, message in
NSLog("onNetworkError \(statusCode) \(message)")
}
NetfunnelHandler.shared.onBlock = { projectKey, segmentKey, statusCode, message in
NSLog("onBlock \(statusCode) \(message)")
}
NetfunnelHandler.shared.onClose = { projectKey, segmentKey, statusCode, message in
NSLog("onClose \(statusCode) \(message)")
}
NetfunnelHandler.shared.onContinue = { projectKey, segmentKey, statusCode, message, aheadWait, behindWait, waitTime, progressRate in
NSLog("onContinue \(statusCode) \(message)")
}
}
private func startSectionControl() {
// NetFUNNEL 구간 큐 보호 시작
Netfunnel.shared.nfStartSection(
projectKey: "your_project_key",
segmentKey: "your_segment_key"
)
}
// 색상 계산 속성은 간결성을 위해 생략
// private var backgroundColor: Color { ... }
}
변경 사항:
- 감싸기:
navigationManager.navigateWithDelay()가 이제onSuccess콜백 내부에 있습니다 - 조건부: 특정 버튼만 NetFUNNEL 보호를 받습니다
- 성공만: NetFUNNEL이 진입을 허용할 때만 비즈니스 로직이 실행됩니다
- 완전한 인터페이스: 모든 필수 델리게이트 메서드가 적절한 로깅과 함께 구현되었습니다
iOS에서 NetfunnelDelegate는 모든 메서드를 구현해야 합니다. onSuccess만 구현할 수 없습니다 - 모든 델리게이트 메서드에 대한 구현을 제공해야 합니다. 이 구현은 모든 응답 유형에 대한 적절한 로깅과 함께 완전한 델리게이트 처리를 제공합니다.
이제 구현을 테스트하여 모든 것이 올바르게 작동하는지 확인하세요.
앱을 실행하고 "Section Control (Code-based Integration)" 버튼을 클릭하세요. 화면에 대기실 WebView가 나타나야 합니다. 모든 것이 올바르게 작동하는지 확인하려면 Xcode 콘솔에서 NetFUNNEL 로그를 확인하세요.
대기실이 보이지 않으면 세그먼트 구성 단계에서 진입 허용 수를 0으로 설정했는지 확인하세요. 이 설정은 사용자가 대기실로 보내지는지 또는 직접 진행할 수 있는지를 제어합니다.
더 나은 디버깅을 위해 NetFUNNEL 로깅 활성화:
AppDelegate 초기화에서 printLog = true를 설정하여 디버그 로깅을 활성화한 다음 Xcode 콘솔을 확인하세요. 자세한 설정 지침은 NetFUNNEL 초기화 테스트) 섹션을 참조하세요.
확인할 사항:
진입 허용 수 = 0으로 Section Control 버튼을 클릭하면 다음과 같은 로그가 표시됩니다:
[NF4] [2025-09-19 16:56:42.832] Initialization successful. NetFUNNEL Version: 4.3.2-onprem
[NF4] [2025-09-19 16:56:58.500] Sending initialEntry request: https://nf4-onprem-demo-4525.stclab.com/ts.wseq?opcode=5101&sid=service_1&aid=segKey_2548
[NF4] [2025-09-19 16:56:58.651] Received initialEntry response: Response(timestamp: Optional(1758268618669), code: 201, key: Optional("A5E68F1AB764D05967A41DB8161C611D0952823AE901BA73FA347E320385E354A8FC7C43E5D15069EEB8F9252A1DFC79BCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B706CFFE9F9934671086EDABAE8FEDFA67722C312C302C302C302C302C30"), nwait: Optional(0), nnext: Optional(0), tps: Optional(0.0), ttl: Optional(1), ip: Optional("nf4-onprem-demo-4525.stclab.com"), port: Optional(443), preBeginTs: nil, preEndTs: nil, postBeginTs: nil, postEndTs: nil, sticky: Optional("nf1"), vwrHtml: Optional("https://nf4-onprem-demo-4525.stclab.com/content/netfunnel-statics/assets/vwr-page/page/1/1/1/index.html"), liveMessage: nil, chkEnterCnt: Optional(0), waitOrderYn: Optional(0), msg: nil, vwrType: Optional("wait"))
[NF4] [2025-09-19 16:56:59.282] Fetching HTML content from following URL. https://nf4-onprem-demo-4525.stclab.com/content/netfunnel-statics/assets/vwr-page/page/1/1/1/index.html
[NF4] [2025-09-19 16:56:59.512] Sending reentry request: https://nf4-onprem-demo-4525.stclab.com:443/ts.wseq?opcode=5002&key=A5E68F1AB764D05967A41DB8161C611D0952823AE901BA73FA347E320385E354A8FC7C43E5D15069EEB8F9252A1DFC79BCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B706CFFE9F9934671086EDABAE8FEDFA67722C312C302C302C302C302C30&sticky=nf1
[NF4] [2025-09-19 16:56:59.563] Received reentry response: Response(timestamp: Optional(1758268619604), code: 201, key: Optional("A5E68F1AB764D05967A41DB8161C611D462C3B2234ABB4D5F3283A4F82C0952F7F82C9A3B775DE797F13B051D1E06451BCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B70694D7C8E4BEA05A019FF78476D2CEAF2F2C312C302C302C302C302C30"), nwait: Optional(0), nnext: Optional(0), tps: Optional(0.0), ttl: Optional(1), ip: Optional("nf4-onprem-demo-4525.stclab.com"), port: Optional(443), preBeginTs: nil, preEndTs: nil, postBeginTs: nil, postEndTs: nil, sticky: Optional("nf1"), vwrHtml: Optional("https://nf4-onprem-demo-4525.stclab.com/content/netfunnel-statics/assets/vwr-page/page/1/1/1/index.html"), liveMessage: nil, chkEnterCnt: Optional(0), waitOrderYn: Optional(0), msg: nil, vwrType: Optional("wait"))
모든 것이 작동 중임을 나타내는 핵심 지표:
| 로그 메시지 | 의미 | 상태 |
|---|---|---|
[NF4] Initialization successful | NetFUNNEL 에이전트가 올바르게 로드됨 | ✅ 좋음 |
[NF4] Sending initialEntry request | nfStartSection()이 성공적으로 호출됨, NetFUNNEL 서버로 요청 전송됨 | ✅ 좋음 |
[NF4] Received initialEntry response with code: 201 | 서버가 WAIT 상태로 응답함 | ✅ 좋음 |
[NF4] Fetching HTML content | 대기실 WebView가 로드 중, 대기실 HTML이 로드되고 있음 | ✅ 좋음 |
[NF4] Sending reentry request | 주기적 재진입 요청 (폴링) | ✅ 좋음 |
[NF4] Received reentry response with code: 201 | 서버가 WAIT 상태로 응답함 | ✅ 좋음 |
응답 확인:
- 로그에서
ts.wseq?opcode=5002요청을 찾으세요 - 응답이
code=201(WAIT)을 표시하는지 확인하세요 201= WAIT,200= PASS (진입 허용)- 여기서는 진입 허용 수가 0 (동시 사용자 수 = 0)이므로 올바른 응답은
201입니다
문제가 발생하는 경우:
- 로그가 나타나지 않음: 초기화에서
printLog = true가 설정되어 있는지 확인하세요 - 대기실이 없음: 콘솔에서 진입 허용 수가 0으로 설정되어 있는지 확인하세요
- 앱 충돌: 모든 필수 델리게이트 메서드가 올바르게 구현되었는지 확인하세요
- 네트워크 오류: 서버 URL과 네트워크 연결을 확인하세요
대기실과 로그가 보이면 기본 구현이 작동하는 것입니다! 이제 더 나은 오류 관리를 위해 델리게이트 처리를 향상시킬 수 있습니다. 진입을 테스트하려면 콘솔에서 진입 허용 수를 1 (동시 사용자 수 = 1)로 변경하세요.
3.6 완전한 콜백 처리 추가
이제 더 나은 오류 관리와 사용자 경험을 위해 콜백 처리를 개선하겠습니다:
이전 단계에서 모든 콜백 메서드를 기본 로깅으로 구현했습니다. 이제 프로덕션 환경에서 강력한 서비스 가용성과 최적의 사용자 경험을 보장하기 위해 오류 처리를 향상시키겠습니다.
완전한 콜백 처리가 필수인 이유:
- 사용자 경험: 다른 응답 유형에 적절한 사용자 피드백이 필요합니다
- 서비스 안정성: 오류 상태가 사용자의 워크플로우를 중단하지 않아야 합니다
- 디버깅: 포괄적인 로깅이 문제를 빠르게 식별하는 데 도움이 됩니다
- 비즈니스 연속성: NetFUNNEL에 문제가 발생해도 서비스가 계속되어야 합니다
강력한 오류 처리로 setupCallbacks 향상:
// MainView.swift - 완전한 처리가 포함된 업데이트된 setupCallbacks
private func setupCallbacks() {
NetfunnelHandler.shared.onSuccess = { projectKey, segmentKey, statusCode, message in
// 사용자가 진행할 수 있음 - 원래 로직 실행
NSLog("onSuccess \(statusCode) \(message)")
navigationManager.navigateWithDelay(to: .sectionControlSection1)
}
NetfunnelHandler.shared.onError = { projectKey, segmentKey, statusCode, message in
// 시스템 오류 발생 - 강력한 서비스 가용성을 위해 원래 로직 진행
NSLog("onError \(statusCode) \(message)")
navigationManager.navigateWithDelay(to: .sectionControlSection1)
}
NetfunnelHandler.shared.onNetworkError = { projectKey, segmentKey, statusCode, message in
// 네트워크 오류 발생 - 디버깅을 위해 로그만 남기고 비즈니스 로직은 실행하지 않음
NSLog("onNetworkError \(statusCode) \(message)")
// 참고: 여기서는 비즈니스 로직을 실행하지 않음 - 아래 설명 참조
}
NetfunnelHandler.shared.onBlock = { projectKey, segmentKey, statusCode, message in
// 사용자가 차단됨 - 적절한 메시지 표시
NSLog("onBlock \(statusCode) \(message)")
}
NetfunnelHandler.shared.onClose = { projectKey, segmentKey, statusCode, message in
// 사용자가 대기실을 닫음 - 적절히 처리
NSLog("onClose \(statusCode) \(message)")
// 메인 화면에 유지 - 사용자가 취소함
}
NetfunnelHandler.shared.onContinue = { projectKey, segmentKey, statusCode, message, aheadWait, behindWait, waitTime, progressRate in
NSLog("onContinue \(statusCode) \(message)")
}
}
응답 처리 전략:
| 응답 유형 | 액션 | 비즈니스 로직 |
|---|---|---|
| Success | 실행 | ✅ 예 |
| Error | 실행 | ✅ 예 |
| NetworkError | 로그만 | ❌ 아니오 |
| Block | 로그만 | ❌ 아니오 |
| Close | 로그만 | ❌ 아니오 |
| Continue | 로그만 | ❌ 아니오 |
onError는 비즈니스 로직을 실행하지만 onNetworkError는 실행하지 않는 이유:
onError (상태 코드 500) - 서버 오류:
- 시나리오: NetFUNNEL 서버가 내부 오류를 만남
- 전략: 서비스 가용성을 유지하기 위해 비즈니스 로직 실행
- 근거: 서버 오류는 일반적으로 일시적이며 사용자 접근을 차단하지 않아야 함
- 결과: NetFUNNEL에 문제가 있어도 계속 작동하는 강력한 서비스
onNetworkError (상태 코드 1001, 1002) - 네트워크 문제:
- 시나리오: 네트워크 연결 문제 (오프라인, 타임아웃)
- 전략: 로그만 남기고 비즈니스 로직은 실행하지 않음
- 근거: 자동 네트워크 복구를 위해
useNetworkRecoveryMode = true사용 - 결과: 네트워크 문제 중에는 사용자가 대기실에 머물고 연결이 복구되면 자동으로 재개됨
네트워크 복구 모드 구성:
최적의 네트워크 오류 처리를 위해 AppDelegate에서 네트워크 복구를 활성화하세요:
// AppDelegate.swift
Netfunnel.initialize(
clientId: "{{CLIENT_ID}}",
delegate: NetfunnelHandler.shared, // self가 아닌 NetfunnelHandler 사용
useNetworkRecoveryMode: true // 자동 네트워크 복구 활성화
)
변경 사항:
- 강력한 오류 처리:
onError는 최대 서비스 가용성을 위해 비즈니스 로직을 진행합니다 - 스마트 네트워크 처리:
onNetworkError는 수동 처리 대신 네트워크 복구 모드에 의존합니다 - 향상된 사용자 경험: 다른 응답 상태의 더 나은 처리
- 프로덕션 준비: 강력한 오류 처리가 NetFUNNEL에 문제가 있어도 서비스가 계속되도록 보장합니다
- 포괄적인 로깅: 모든 응답 유형 및 디버깅에 대한 자세한 콘솔 메시지
모든 델리게이트 응답 유형, 상태 코드, 응답 객체 구조 및 고급 콜백 패턴에 대한 자세한 정보는 API 참조를 참조하세요.
3.7 핵심 구현 포인트
- 프로젝트/세그먼트 키: NetFUNNEL 콘솔에서 정확한 키 사용
- 필수 import:
Netfunnel_iOSimport 추가 - 델리게이트 구현: 모든 콜백 메서드 구현 (NetfunnelDelegate 요구 사항)
- 강력한 오류 처리:
onSuccess와onError는 서비스 가용성을 위해 비즈니스 로직을 실행합니다onNetworkError는 로그만 남깁니다 (자동 복구를 위해useNetworkRecoveryMode = true사용)
- 뷰 생명주기: 초기화 및 UI 업데이트에
onAppear사용 - 포괄적인 로깅: 디버깅을 위해 모든 델리게이트 응답 로깅
4단계: 키 반환 통합 지점 식별 (nfStopSection)
다음 예제는 시연 목적으로 샘플 애플리케이션을 사용합니다. 실제 애플리케이션 코드는 여기에 표시된 것과 자연스럽게 다를 것입니다. 통합 패턴을 특정 코드 구조, 비즈니스 로직 완료 지점, 키 반환 요구 사항에 맞게 조정하세요.
💡 연습 프로젝트가 필요하신가요? NetFUNNEL SDK 통합 연습을 위한 iOS 애플리케이션 (단일 ViewController) 템플릿이 준비된 샘플 프로젝트를 확인하세요.
샘플 애플리케이션 이해하기:
NetFUNNEL 키를 반환하기 위해 nfStopSection을 호출해야 하는 위치를 이해하기 위해 샘플 애플리케이션을 살펴보겠습니다.
4.1 구간 완료 지점 식별
nfStartSection 성공 후 현재 흐름:
// 3단계에서 - nfStartSection이 성공할 때
NetfunnelHandler.shared.onSuccess = { projectKey, segmentKey, statusCode, message in
NSLog("onSuccess \(statusCode) \(message)")
// NetFUNNEL이 진입을 허용할 때 원래 비즈니스 로직 실행
navigationManager.navigateWithDelay(to: .sectionControlSection1)
}
다음에 일어나는 일:
- 사용자가 SectionControlSection1View로 이동 - 프로세스의 첫 번째 단계
- 사용자가 SectionControlSection2View로 이동 - 프로세스의 두 번째 단계
- 사용자가 SectionControlEndView로 이동 - 최종 완료 단계
- 사용자의 구간 세션이 완료됨 - 여기서 키를 반환해야 함
- 키를 반환해야 함 - 큐의 다음 사용자가 구간에 진입할 수 있도록
nfStopSection 구현에 대한 중요 참고 사항:
nfStopSection은 먼저 nfStartSection을 호출할 필요 없이 독립적으로 호출할 수 있습니다. nfStartSection이 호출되지 않았다면 NetFUNNEL은 필요시 키 해제를 자동으로 처리하거나 키가 없으면 아무 작업도 하지 않습니다. 이로 인해 nfStopSection은 조건부 확인 없이 어떤 시나리오에서도 안전하게 호출할 수 있어 구현이 단순해집니다.
4.2 키 반환을 위한 통합 지점 식별
통합 지점 옵션:
| 통합 지점 | 사용 시기 | 장점 |
|---|---|---|
| 구간 종료 뷰 | 다단계 완료 흐름 | 명확한 완료 지점, 대부분의 경우에 작동 |
| 비즈니스 로직 완료 | 복잡한 작업 (API 호출, 처리) | 정밀한 제어, 실제 작업 후 키 반환 |
| 사용자 액션 완료 | 사용자 시작 완료 | 사용자가 작업을 완료할 때 키 반환 |
4.3 적절한 통합 지점 선택
샘플 애플리케이션의 경우:
현재 비즈니스 흐름: 다단계 탐색 프로세스
- 하는 일: 사용자가 Section1 → Section2 → End를 거침
- 완료 시점: SectionControlEndView가 성공적으로 나타날 때
- 최적 통합 지점: SwiftUI 뷰 생명주기 이벤트 (onAppear, onDisappear)
통합 전략:
- 구간 완료: SectionControlEndView가 나타날 때 키 반환
- 단순하고 신뢰할 수 있음: 탐색 기반 구간 흐름에 작동
- 사용자 경험: 사용자가 전체 구간을 완료했을 때 키가 반환됨
4.4 통합 지점 로직 확인
전체 흐름 분석:
- 사용자가 버튼 클릭 →
nfStartSection()호출됨 - 대기실 나타남 → 사용자가 큐에서 대기
- 진입 허용됨 →
onSuccess델리게이트 실행 - 구간 탐색 시작 → 사용자가 Section1 → Section2 → End를 진행
- SectionControlEndView 나타남 → 사용자가 구간을 완료함
- 키 반환 필요 → 구간 슬롯을 해제하기 위해
nfStopSection()호출
이 통합 지점이 합리적인 이유:
- 사용자 경험: 사용자가 전체 구간을 완료했을 때 키가 반환됨
- 동시 사용자 수 관리: 현재 사용자가 구간을 완료하자마자 다음 사용자가 진입할 수 있음
- 리소스 효율성: 불필요한 동시 사용자 수 차단을 방지
- 구현 단순성: 구현 및 유지 관리가 쉬움
핵심 통찰: nfStopSection 통합 지점은 사용자의 의도한 구간 작업이 실제로 완료되고 큐에 대기했던 구간 사용을 마친 위치여야 합니다.
진입 후 빠르게 키를 반환하는 기본 제어와 달리, 구간 제어는 전체 구간이 완료될 때까지 키를 유지합니다. 이 근본적인 차이는 nfStopSection()을 언제, 어디서 호출해야 하는지에 영향을 미칩니다:
- 기본 제어: 진입 직후 키 반환 (예: 뷰 로드 후)
- 구간 제어: 전체 구간 동안 키 유지 (예: Section1 → Section2 → End)
5단계: 키 반환 함수 구현 (nfStopSection)
아래 예제는 구간이 완료될 때 키를 반환하는 다양한 접근 방식을 보여줍니다. 애플리케이션 아키텍처에 가장 적합한 접근 방식을 선택하세요 - SwiftUI 뷰 탐색, API 호출 또는 기타 비즈니스 로직 완료 후 키를 반환해야 하는지 여부에 따라 다릅니다.
5.1 nfStopSection 함수 이해하기
nfStopSection 함수는 다음과 같은 기본 구조를 가집니다:
- Swift
- Objective-C
Netfunnel.shared.nfStopSection(
projectKey: "your_project_key", // nfStartSection 키와 정확히 일치해야 함
segmentKey: "your_segment_key" // nfStartSection 키와 정확히 일치해야 함
)
[[Netfunnel shared] nfStopSectionWithProjectKey:@"your_project_key"
segmentKey:@"your_segment_key"];
핵심 요구 사항:
- 정확한 키 일치: 키는
nfStartSection()에서 사용된 것과 동일해야 합니다 - 타이밍:
nfStartSection()직후가 아닌 구간 완료 후 호출
5.2 필요한 import 추가
nfStopSection을 구현하기 전에 View에 필요한 import를 추가하세요:
- Swift
- Objective-C
import SwiftUI
import Netfunnel_iOS
#import <UIKit/UIKit.h>
#import <Netfunnel_iOS/Netfunnel_iOS.h>
핵심 Import:
Netfunnel_iOS- 메인 NetFUNNEL 프레임워크 (nfStartSection에 이미 import됨)
5.3 기본 키 반환 추가 (SwiftUI 뷰 생명주기)
SectionControlEndView가 로드될 때 키 반환:
// SectionControlEndView.swift
import SwiftUI
import Netfunnel_iOS
struct SectionControlEndView: View {
@StateObject private var navigationManager = NavigationManager.shared
var body: some View {
VStack(spacing: 0) {
// 성공 섹션
VStack(spacing: 24) {
// 성공 아이콘 및 제목은 간결성을 위해 생략
// ... 성공 UI 요소 ...
Text("Section Control Complete")
.font(.system(size: 28, weight: .bold, design: .rounded))
.foregroundColor(primaryTextColor)
.multilineTextAlignment(.center)
Text("This is the completion page for Section Control (Code-based Integration).")
.font(.system(size: 16, weight: .regular, design: .rounded))
.multilineTextAlignment(.center)
.foregroundColor(secondaryTextColor)
.padding(.horizontal, 32)
}
.padding(.top, 60)
.padding(.bottom, 40)
Spacer()
// 메인으로 돌아가기 버튼
Button(action: {
navigationManager.navigateBackWithDelay()
}) {
Text("Back to Main")
}
.buttonStyle(ModernButtonStyle())
}
// 추가 UI 수정자는 간결성을 위해 생략
// ... background, frame, navigationBarHidden 등 ...
.onAppear {
// SwiftUI 뷰가 완전히 로드될 때 NetFUNNEL 키 반환
Netfunnel.shared.nfStopSection(
projectKey: "your_project_key",
segmentKey: "your_segment_key"
)
}
}
// 색상 계산 속성은 간결성을 위해 생략
// private var backgroundColor: Color { ... }
// private var primaryTextColor: Color { ... }
// private var secondaryTextColor: Color { ... }
}
변경 사항:
- SwiftUI 뷰 생명주기: SectionControlEndView가 완전히 로드될 때 키 반환
- 단순한 접근: 기본 탐색 시나리오에 작동
- 직접 호출: NetFUNNEL이 초기화되고 사용 가능하다고 가정
이제 키 반환 구현을 테스트하여 모든 것이 올바르게 작동하는지 확인하세요.
앱을 실행하고 다음 테스트 시퀀스를 따르세요:
- "Section Control (Code-based Integration)" 버튼 클릭 - 대기실이 나타나야 함
- NetFUNNEL 콘솔에서 진입 허용 수를 0에서 1로 변경 - 이것은 진입을 허용함
- 대기실 닫힘 관찰 - 사라지고 SectionControlSection1View로 이동해야 함
- Section1 → Section2 → End 탐색 - 구간 흐름 완료
- Xcode 콘솔에서 키 타임아웃 확장 로그 확인 - 구간 중에 5003 요청 로그가 보여야 함
- Xcode 콘솔에서 키 반환 로그 확인 - 구간이 완료될 때 5004 요청 로그가 보여야 함
더 나은 디버깅을 위해 NetFUNNEL 로깅 활성화:
AppDelegate 초기화에서 printLog = true를 설정하여 디버그 로깅을 활성화한 다음 Xcode 콘솔을 확인하세요. 자세한 설정 지침은 NetFUNNEL 초기화 테스트) 섹션을 참조하세요.
확인할 사항:
구간 흐름(Section1 → Section2 → End)을 완료하면 다음과 같은 로그가 표시됩니다:
[NF4] [2025-09-19 16:56:42.832] Initialization successful. NetFUNNEL Version: 4.3.2-onprem
[NF4] [2025-09-19 16:56:58.500] Sending initialEntry request: https://nf4-onprem-demo-4525.stclab.com/ts.wseq?opcode=5101&sid=service_1&aid=segKey_2548
[NF4] [2025-09-19 16:56:58.651] Received initialEntry response: Response(timestamp: Optional(1758268618669), code: 201, key: Optional("A5E68F1AB764D05967A41DB8161C611D0952823AE901BA73FA347E320385E354A8FC7C43E5D15069EEB8F9252A1DFC79BCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B706CFFE9F9934671086EDABAE8FEDFA67722C312C302C302C302C302C30"), nwait: Optional(0), nnext: Optional(0), tps: Optional(0.0), ttl: Optional(1), ip: Optional("nf4-onprem-demo-4525.stclab.com"), port: Optional(443), preBeginTs: nil, preEndTs: nil, postBeginTs: nil, postEndTs: nil, sticky: Optional("nf1"), vwrHtml: Optional("https://nf4-onprem-demo-4525.stclab.com/content/netfunnel-statics/assets/vwr-page/page/1/1/1/index.html"), liveMessage: nil, chkEnterCnt: Optional(0), waitOrderYn: Optional(0), msg: nil, vwrType: Optional("wait"))
[NF4] [2025-09-19 16:56:59.282] Fetching HTML content from following URL. https://nf4-onprem-demo-4525.stclab.com/content/netfunnel-statics/assets/vwr-page/page/1/1/1/index.html
[NF4] [2025-09-19 16:56:59.512] Sending reentry request: https://nf4-onprem-demo-4525.stclab.com:443/ts.wseq?opcode=5002&key=A5E68F1AB764D05967A41DB8161C611D0952823AE901BA73FA347E320385E354A8FC7C43E5D15069EEB8F9252A1DFC79BCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B706CFFE9F9934671086EDABAE8FEDFA67722C312C302C302C302C302C30&sticky=nf1
[NF4] [2025-09-19 16:56:59.563] Received reentry response: Response(timestamp: Optional(1758268619604), code: 201, key: Optional("A5E68F1AB764D05967A41DB8161C611D462C3B2234ABB4D5F3283A4F82C0952F7F82C9A3B775DE797F13B051D1E06451BCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B70694D7C8E4BEA05A019FF78476D2CEAF2F2C312C302C302C302C302C30"), nwait: Optional(0), nnext: Optional(0), tps: Optional(0.0), ttl: Optional(1), ip: Optional("nf4-onprem-demo-4525.stclab.com"), port: Optional(443), preBeginTs: nil, preEndTs: nil, postBeginTs: nil, postEndTs: nil, sticky: Optional("nf1"), vwrHtml: Optional("https://nf4-onprem-demo-4525.stclab.com/content/netfunnel-statics/assets/vwr-page/page/1/1/1/index.html"), liveMessage: nil, chkEnterCnt: Optional(0), waitOrderYn: Optional(0), msg: nil, vwrType: Optional("wait"))
... (code: 201로 반복 폴링) ...
[NF4] [2025-09-19 16:57:06.903] Received reentry response: Response(timestamp: Optional(1758268626936), code: 200, key: Optional("A5E68F1AB764D05967A41DB8161C611DD2AA40A9437F4974D770B5C0AA132C311128743E2F216A772B70073896EB3DB127202420DE5AD49E70AB545EDE6D88C5DA85D3F8F85FA7FA7B7F120E3C54DAC2902D1EA62CCD2C8E006BBC45D5A08001715A51C76F286957F8DE56D6092043B1302C302C30"), nwait: Optional(0), nnext: Optional(0), tps: Optional(0.0), ttl: Optional(0), ip: Optional("nf4-onprem-demo-4525.stclab.com"), port: Optional(443), preBeginTs: nil, preEndTs: nil, postBeginTs: nil, postEndTs: nil, sticky: Optional("nf1"), vwrHtml: Optional("https://nf4-onprem-demo-4525.stclab.com/content/netfunnel-statics/assets/vwr-page/page/1/1/1/index.html"), liveMessage: nil, chkEnterCnt: Optional(1), waitOrderYn: Optional(0), msg: nil, vwrType: Optional(""))
[NF4] [2025-09-19 16:57:06.905] Sending key refresh request: https://nf4-onprem-demo-4525.stclab.com:443/ts.wseq?opcode=5003&key=A5E68F1AB764D05967A41DB8161C611DD2AA40A9437F4974D770B5C0AA132C311128743E2F216A772B70073896EB3DB127202420DE5AD49E70AB545EDE6D88C5DA85D3F8F85FA7FA7B7F120E3C54DAC2902D1EA62CCD2C8E006BBC45D5A08001715A51C76F286957F8DE56D6092043B1302C302C30&sticky=nf1
[NF4] [2025-09-19 16:57:06.949] Received refreshKey response. code=201, key=Optional("A5E68F1AB764D05967A41DB8161C611DD2AA40A9437F4974D770B5C0AA132C311128743E2F216A772B70073896EB3DB127202420DE5AD49E70AB545EDE6D88C5DA85D3F8F85FA7FA7B7F120E3C54DAC2902D1EA62CCD2C8E006BBC45D5A08001715A51C76F286957F8DE56D6092043B1312C302C30")
... (구간 중 반복되는 키 새로고침 주기) ...
[NF4] [2025-09-19 16:57:14.199] Sending returnKey request: https://nf4-onprem-demo-4525.stclab.com:443/ts.wseq?opcode=5004&key=A5E68F1AB764D05967A41DB8161C611D7F3D0E71CEF0573FF58477D0DD6D83AA1366D3C88161ABC35A8FE1E1DA92B83B27202420DE5AD49E70AB545EDE6D88C5DA85D3F8F85FA7FA7B7F120E3C54DAC2902D1EA62CCD2C8E006BBC45D5A080019F32F0077492B7D5F8DB92356655AFF1312C302C30&sticky=nf1
[NF4] [2025-09-19 16:57:14.236] Received returnKey response. code=200, key=Optional("A5E68F1AB764D05967A41DB8161C611D7F3D0E71CEF0573FF58477D0DD6D83AA1366D3C88161ABC35A8FE1E1DA92B83B27202420DE5AD49E70AB545EDE6D88C5DA85D3F8F85FA7FA7B7F120E3C54DAC2902D1EA62CCD2C8E006BBC45D5A080019F32F0077492B7D5F8DB92356655AFF1312C302C30")
모든 것이 작동 중임을 나타내는 핵심 지표:
| 로그 메시지 | 의미 | 상태 |
|---|---|---|
[NF4] Sending initialEntry request | nfStartSection()이 성공적으로 호출됨, NetFUNNEL 서버로 초기 진입 요청 전송됨 | ✅ 좋음 |
[NF4] Received initialEntry response with code: 201 | 서버가 WAIT 상태로 응답함 | ✅ 좋음 |
[NF4] Sending reentry request (반복) | 주기적 재진입 요청 (폴링) | ✅ 좋음 |
[NF4] Received reentry response with code=200 | 진입 허용됨 - 대기실이 닫혀야 함 | ✅ 좋음 |
[NF4] Sending key refresh request | 구간 제어: 키 타임아웃 확장 요청 (기본 20초) | ✅ 좋음 |
[NF4] Received refreshKey response with code=201 | 구간 제어: 키 타임아웃이 성공적으로 확장됨 | ✅ 좋음 |
[NF4] Sending returnKey request | nfStopSection()이 호출됨 | ✅ 좋음 |
[NF4] Received returnKey response with code=200 | 키 반환 성공 | ✅ 좋음 |
테스트 시퀀스:
- 진입 허용 수 = 0으로 시작: 대기실이 나타남, initialEntry 요청 전송됨
- 진입 허용 수를 1로 변경: reentry 응답이
code=200으로 변경됨, 대기실이 닫힘 - 구간 진입: 사용자가 구간에 진입함, 키 새로고침 요청이 자동으로 시작됨
- 키 타임아웃 확장: 구간 제어가 자동으로 키 타임아웃을 확장함 (5003 요청)하여 자동 반환을 방지
- 구간 탐색: 사용자가 Section1 → Section2 → End를 진행
- 구간 완료: SectionControlEndView
onAppear()가nfStopSection()호출 - 키 반환: 5004 요청/응답이 키 반환을 확인함
구간 제어는 기본 제어와 비교하여 고유한 키 관리 동작을 가집니다:
- 키 타임아웃 확장 (5003): 구간 중 자동 반환을 방지하기 위해 키 타임아웃을 자동으로 확장함 (기본 20초)
- 타임아웃 구성: NetFUNNEL 콘솔 → 세그먼트 → 고급 설정 →
타임아웃에서 타임아웃 구성 - 동시 사용자 수: 명시적 완료까지 구간에서 고정된 동시 사용자 수를 유지
- 자동 관리: NetFUNNEL이 타임아웃 확장을 자동으로 처리함 - 수동 개입 불필요
- 수동 반환:
nfStopSection()이 명시적으로 호출될 때만 키가 반환됨
문제가 발생하는 경우:
- returnKey 로그 없음: SectionControlEndView에서
nfStopSection()이 호출되는지 확인하세요 - 키 반환 실패:
nfStopSection()의 프로젝트/세그먼트 키가nfStartSection()과 정확히 일치하는지 확인하세요 - 구간이 완료되지 않음: 구간 탐색 흐름이 올바르게 작동하는지 확인하세요
- 대기실이 닫히지 않음: 콘솔에서 진입 허용 수가 1로 설정되어 있는지 확인하세요
- 키가 자동 반환됨: NetFUNNEL 콘솔 → 세그먼트 → 고급 설정 →
타임아웃에서 타임아웃이 너무 짧은지 확인하세요 - 5003 로그 없음: 구간 지속 시간이 타임아웃 기간(기본 20초)을 초과하는지 확인하세요
대기실 → 진입 → 구간 탐색 → 구간 완료 → 키 반환의 전체 흐름이 보이면 구현이 올바르게 작동하는 것입니다! 이제 오류 처리를 향상시키고 더 강력한 델리게이트 관리를 추가할 수 있습니다.
5.4 핵심 구현 포인트
구간이 완료되면 NetFUNNEL 키를 반환하세요. NetFUNNEL이 타임아웃 후(기본 20초) 자동으로 키를 반환하지만, 수동 반환은 더 나은 사용자 경험과 동시 사용자 수 관리 효율성을 제공합니다.
키 반환 규칙:
- ✅ 항상: 구간 완료 후(성공 또는 실패) 키 반환
- ⚠️ 자동 타임아웃: 수동으로 반환하지 않으면 NetFUNNEL이 자동으로 키를 반환함
- 타이밍:
nfStartSection()직후가 아닌 구간 완료 후 호출
구현 체크리스트:
- 정확한 키 일치:
nfStopSection()의 키는nfStartSection()과 정확히 일치해야 함 - SwiftUI 뷰 생명주기: 키 반환 타이밍에
onAppear사용 - 일관된 키: 구간 흐름 전체에서 동일한 프로젝트/세그먼트 키 사용
nfStopSection 매개변수, 응답 처리 및 고급 키 반환 패턴에 대한 자세한 정보는 API 참조를 참조하세요.
6단계: 대기실 테스트 (진입 허용 수 = 0)
아래 테스트 단계는 샘플 애플리케이션을 기반으로 합니다. 테스트 프로세스를 실제 애플리케이션에 맞게 조정하세요 - 버튼 이름, View 탐색, 확인 단계를 특정 구현에 맞게 교체하세요.
6.1 액션 트리거
-
Xcode 콘솔 로그 지우기 (깨끗한 상태에서 관찰하려는 경우)
- Xcode에서 콘솔의 휴지통 아이콘을 클릭하여 이전 로그 지우기
- 또는 Cmd+K를 사용하여 콘솔 지우기
-
보호된 버튼 클릭 (예: "Section Control (Code-based Integration)" 버튼)
예상 결과: 대기실 WebView가 현재 화면에 나타남
6.2 대기실 표시 확인
다음 요소가 올바르게 표시되는지 확인하세요:
진입 허용 수가 0으로 설정되고 한 명의 사용자만 연결된 경우 확인:
- 나의 대기 순서: 1
- 예상 대기 시간: 표시되지 않음 (구간 제어만)
- 내 뒤의 대기자 수: 0
구간 제어는 이미 구간에 있는 사용자의 완료 시간이 다양하고 예측하기 어렵기 때문에 예상 대기 시간을 표시하지 않습니다. 진입이 빠르고 예측 가능한 기본 제어와 달리, 구간 제어 사용자는 다단계 프로세스를 완료하는 데 다양한 시간을 소비할 수 있습니다.
6.3 Xcode 콘솔 활동 확인
NetFUNNEL 로그 확인:
AppDelegate에서 printLog = true를 설정하여 디버그 로깅을 활성화한 다음 Xcode 콘솔을 확인하세요. 다음과 같은 로그가 표시되어야 합니다:
[NF4] [2025-09-19 16:56:42.832] Initialization successful. NetFUNNEL Version: 4.3.2-onprem
[NF4] [2025-09-19 16:56:58.500] Sending initialEntry request: https://nf4-onprem-demo-4525.stclab.com/ts.wseq?opcode=5101&sid=service_1&aid=segKey_2548
[NF4] [2025-09-19 16:56:58.651] Received initialEntry response: Response(timestamp: Optional(1758268618669), code: 201, key: Optional("A5E68F1AB764D05967A41DB8161C611D0952823AE901BA73FA347E320385E354A8FC7C43E5D15069EEB8F9252A1DFC79BCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B706CFFE9F9934671086EDABAE8FEDFA67722C312C302C302C302C302C30"), nwait: Optional(0), nnext: Optional(0), tps: Optional(0.0), ttl: Optional(1), ip: Optional("nf4-onprem-demo-4525.stclab.com"), port: Optional(443), preBeginTs: nil, preEndTs: nil, postBeginTs: nil, postEndTs: nil, sticky: Optional("nf1"), vwrHtml: Optional("https://nf4-onprem-demo-4525.stclab.com/content/netfunnel-statics/assets/vwr-page/page/1/1/1/index.html"), liveMessage: nil, chkEnterCnt: Optional(0), waitOrderYn: Optional(0), msg: nil, vwrType: Optional("wait"))
[NF4] [2025-09-19 16:56:59.282] Fetching HTML content from following URL. https://nf4-onprem-demo-4525.stclab.com/content/netfunnel-statics/assets/vwr-page/page/1/1/1/index.html
[NF4] [2025-09-19 16:56:59.512] Sending reentry request: https://nf4-onprem-demo-4525.stclab.com:443/ts.wseq?opcode=5002&key=A5E68F1AB764D05967A41DB8161C611D0952823AE901BA73FA347E320385E354A8FC7C43E5D15069EEB8F9252A1DFC79BCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B706CFFE9F9934671086EDABAE8FEDFA67722C312C302C302C302C302C30&sticky=nf1
[NF4] [2025-09-19 16:56:59.563] Received reentry response: Response(timestamp: Optional(1758268619604), code: 201, key: Optional("A5E68F1AB764D05967A41DB8161C611D462C3B2234ABB4D5F3283A4F82C0952F7F82C9A3B775DE797F13B051D1E06451BCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B70694D7C8E4BEA05A019FF78476D2CEAF2F2C312C302C302C302C302C30"), nwait: Optional(0), nnext: Optional(0), tps: Optional(0.0), ttl: Optional(1), ip: Optional("nf4-onprem-demo-4525.stclab.com"), port: Optional(443), preBeginTs: nil, preEndTs: nil, postBeginTs: nil, postEndTs: nil, sticky: Optional("nf1"), vwrHtml: Optional("https://nf4-onprem-demo-4525.stclab.com/content/netfunnel-statics/assets/vwr-page/page/1/1/1/index.html"), liveMessage: nil, chkEnterCnt: Optional(0), waitOrderYn: Optional(0), msg: nil, vwrType: Optional("wait"))
... (code: 201로 반복 폴링) ...
모든 것이 작동 중임을 나타내는 핵심 지표:
| 로그 메시지 | 의미 | 상태 |
|---|---|---|
[NF4] Initialization successful | NetFUNNEL 에이전트가 올바르게 로드됨 | ✅ 좋음 |
[NF4] Sending initialEntry request | nfStartSection()이 성공적으로 호출됨, NetFUNNEL 서버로 초기 진입 요청 전송됨 | ✅ 좋음 |
[NF4] Received initialEntry response with code: 201 | 서버가 WAIT 상태로 응답함 | ✅ 좋음 |
[NF4] Received reentry response with code: 201 | 서버가 WAIT 상태로 응답함 | ✅ 좋음 |
[NF4] Fetching HTML content | 대기실 WebView가 로드 중, 대기실 HTML이 로드되고 있음 | ✅ 좋음 |
[NF4] Sending reentry request | 주기적 재진입 요청 (폴링) | ✅ 좋음 |
[NF4] Received reentry response with code: 201 | 서버가 WAIT 상태로 응답함 | ✅ 좋음 |
응답 확인:
- 로그에서
ts.wseq?opcode=5002요청을 찾으세요 - 응답이
code=201(WAIT)을 표시하는지 확인하세요 201= WAIT,200= PASS (진입 허용)- 여기서는 진입 허용 수가 0 (동시 사용자 수 = 0)이므로 올바른 응답은
201입니다
7단계: 구간 진입 테스트 (진입 허용 수 = 1)
7.1 세그먼트 설정 업데이트
- NetFUNNEL 콘솔로 돌아가기
- 세그먼트의
수정버튼 클릭하여 편집 화면 열기

- 진입 허용 수를
0에서1로 변경 - 하단의
확인클릭

확인을 클릭하자마자 대기실이 사라지고 즉시 SectionControlSection1View로 이동합니다. 이 순간을 관찰하려면 대기실이 현재 표시된 다른 화면을 열어두세요.
7.2 구간 진입 확인
예상 결과: 대기실이 즉시 사라지고 SectionControlSection1View로 이동함
진입 확인을 위해 Xcode 콘솔 확인:
다음과 같은 로그가 표시되어야 합니다:
[NF4] [2025-09-19 16:57:06.903] Received reentry response: Response(timestamp: Optional(1758268626936), code: 200, key: Optional("A5E68F1AB764D05967A41DB8161C611DD2AA40A9437F4974D770B5C0AA132C311128743E2F216A772B70073896EB3DB127202420DE5AD49E70AB545EDE6D88C5DA85D3F8F85FA7FA7B7F120E3C54DAC2902D1EA62CCD2C8E006BBC45D5A08001715A51C76F286957F8DE56D6092043B1302C302C30"), nwait: Optional(0), nnext: Optional(0), tps: Optional(0.0), ttl: Optional(0), ip: Optional("nf4-onprem-demo-4525.stclab.com"), port: Optional(443), preBeginTs: nil, preEndTs: nil, postBeginTs: nil, postEndTs: nil, sticky: Optional("nf1"), vwrHtml: Optional("https://nf4-onprem-demo-4525.stclab.com/content/netfunnel-statics/assets/vwr-page/page/1/1/1/index.html"), liveMessage: nil, chkEnterCnt: Optional(1), waitOrderYn: Optional(0), msg: nil, vwrType: Optional(""))
[NF4] [2025-09-19 16:57:06.905] Sending key refresh request: https://nf4-onprem-demo-4525.stclab.com:443/ts.wseq?opcode=5003&key=A5E68F1AB764D05967A41DB8161C611DD2AA40A9437F4974D770B5C0AA132C311128743E2F216A772B70073896EB3DB127202420DE5AD49E70AB545EDE6D88C5DA85D3F8F85FA7FA7B7F120E3C54DAC2902D1EA62CCD2C8E006BBC45D5A08001715A51C76F286957F8DE56D6092043B1302C302C30&sticky=nf1
[NF4] [2025-09-19 16:57:06.949] Received refreshKey response. code=201, key=Optional("A5E68F1AB764D05967A41DB8161C611DD2AA40A9437F4974D770B5C0AA132C311128743E2F216A772B70073896EB3DB127202420DE5AD49E70AB545EDE6D88C5DA85D3F8F85FA7FA7B7F120E3C54DAC2902D1EA62CCD2C8E006BBC45D5A08001715A51C76F286957F8DE56D6092043B1312C302C30")
성공적인 구간 진입의 핵심 지표:
| 로그 메시지 | 의미 | 상태 |
|---|---|---|
[NF4] Received reentry response with code=200 | 진입 허용됨 - 대기실이 닫혀야 함 | ✅ 좋음 |
[NF4] Sending key refresh request | 구간 제어: 키 타임아웃 확장 요청 전송됨 (5003) | ✅ 좋음 |
[NF4] Received refreshKey response with code=201 | 구간 제어: 키 타임아웃이 성공적으로 확장됨 | ✅ 좋음 |
7.3 구간 탐색 테스트
구간 흐름을 탐색하세요:
- Section1 → Section2 → End: 다단계 구간 프로세스 완료
- 각 단계가 올바르게 작동하는지 확인: View 간 원활한 탐색 보장
- 타임아웃 확장 로그 확인: 구간 중 주기적인 5003 요청 찾기
예상 동작:
- 사용자가 구간 내에서 자유롭게 탐색할 수 있음 (Section1 → Section2 → End)
- NetFUNNEL이 구간 중에 키 타임아웃을 자동으로 확장함 (5003 요청)
- 명시적 완료까지 동시 사용자 수가 유지됨
7.4 키 반환 확인
성공적인 키 반환 확인:
End View에 도달했을 때 Xcode 콘솔에서 다음 로그를 찾으세요:
[NF4] [2025-09-19 16:57:14.199] Sending returnKey request: https://nf4-onprem-demo-4525.stclab.com:443/ts.wseq?opcode=5004&key=A5E68F1AB764D05967A41DB8161C611D7F3D0E71CEF0573FF58477D0DD6D83AA1366D3C88161ABC35A8FE1E1DA92B83B27202420DE5AD49E70AB545EDE6D88C5DA85D3F8F85FA7FA7B7F120E3C54DAC2902D1EA62CCD2C8E006BBC45D5A080019F32F0077492B7D5F8DB92356655AFF1312C302C30&sticky=nf1
[NF4] [2025-09-19 16:57:14.236] Received returnKey response. code=200, key=Optional("A5E68F1AB764D05967A41DB8161C611D7F3D0E71CEF0573FF58477D0DD6D83AA1366D3C88161ABC35A8FE1E1DA92B83B27202420DE5AD49E70AB545EDE6D88C5DA85D3F8F85FA7FA7B7F120E3C54DAC2902D1EA62CCD2C8E006BBC45D5A080019F32F0077492B7D5F8DB92356655AFF1312C302C30")
성공적인 키 반환의 핵심 지표:
| 로그 메시지 | 의미 | 상태 |
|---|---|---|
[NF4] Sending returnKey request | nfStopSection()이 호출됨 (5004) | ✅ 좋음 |
[NF4] Received returnKey response with code=200 | 키 반환 성공 | ✅ 좋음 |
요약
필수 사항 (반드시 수행)
설정:
- 설치 및 초기화 완료 및 NetFUNNEL 에이전트가 올바르게 로드되는지 확인
- 콘솔에서 구간 제어로 세그먼트 생성
- 콘솔에서 프로젝트 키와 세그먼트 키 가져오기
- 테스트를 위해 진입 허용 수를 0으로, 프로덕션을 위해 1 이상으로 설정
통합:
- 필수 import 추가:
Netfunnel_iOS및NetfunnelDelegate - 모든 델리게이트 메서드 구현 (iOS 프로토콜 요구 사항)
- 버튼 핸들러에서
Netfunnel.shared.nfStartSection()으로 구간 진입 감싸기 onSuccess델리게이트를 처리하여 구간 진입 로직 실행- 구간 완료 후
Netfunnel.shared.nfStopSection()호출 nfStartSection()과nfStopSection()모두에서 동일한 키 사용
오류 처리:
onError를 처리하여 구간 진입 진행 (서비스 가용성 유지)onNetworkError를 로깅만 처리 (useNetworkRecoveryMode = true사용)onBlock을 적절한 사용자 메시지 표시로 처리onClose를 현재 뷰 컨트롤러에 유지로 처리 (사용자가 취소함)- 모든 델리게이트 응답에 대한 포괄적인 로깅 구현
테스트:
- 진입 허용 수 = 0으로 대기실이 나타나는지 테스트
- 진입 허용 수 = 1로 진입이 작동하는지 테스트
- 구간 완료 후 키 반환이 발생하는지 확인
- Xcode 콘솔에서 NetFUNNEL 로그 확인
선택 사항 (있으면 좋음)
오류 처리 향상:
- 기본 로깅 대신 사용자 친화적인 오류 메시지 추가
- 다른 응답 유형에 대한 사용자 정의 오류 처리 전략 구현
- NetFUNNEL 이벤트에 대한 분석 추적 추가
코드 구성:
- 중앙 집중식 구성 상수 생성
- 재사용 가능한 NetFUNNEL 래퍼 함수 구축
- 여러 뷰 컨트롤러에 걸친 모듈식 통합 패턴 구현
모범 사례
필수 Import
- Swift
- Objective-C
import Netfunnel_iOS
#import <Netfunnel_iOS/Netfunnel_iOS.h>
이러한 import가 중요한 이유:
- NetFUNNEL 통합에 필수
- NetFUNNEL을 사용하는 모든 View에 추가해야 함
nfStartSection()및nfStopSection()함수 모두에 필요
완전한 콜백 구현
- Swift
- Objective-C
private func setupCallbacks() {
NetfunnelHandler.shared.onSuccess = { projectKey, segmentKey, statusCode, message in
NSLog("onSuccess \(statusCode) \(message)")
// NetFUNNEL이 진입을 허용할 때 원래 비즈니스 로직 실행
navigationManager.navigateWithDelay(to: .sectionControlSection1)
}
NetfunnelHandler.shared.onError = { projectKey, segmentKey, statusCode, message in
NSLog("onError \(statusCode) \(message)")
// 시스템 오류 발생 - 강력한 서비스 가용성을 위해 원래 로직 진행
navigationManager.navigateWithDelay(to: .sectionControlSection1)
}
NetfunnelHandler.shared.onNetworkError = { projectKey, segmentKey, statusCode, message in
NSLog("onNetworkError \(statusCode) \(message)")
// 네트워크 오류 발생 - 디버깅을 위해 로그만 남기고 비즈니스 로직은 실행하지 않음
}
NetfunnelHandler.shared.onBlock = { projectKey, segmentKey, statusCode, message in
NSLog("onBlock \(statusCode) \(message)")
// 사용자가 차단됨 - 적절한 메시지 표시
}
NetfunnelHandler.shared.onClose = { projectKey, segmentKey, statusCode, message in
NSLog("onClose \(statusCode) \(message)")
// 사용자가 대기실을 닫음 - 적절히 처리
}
NetfunnelHandler.shared.onContinue = { projectKey, segmentKey, statusCode, message, aheadWait, behindWait, waitTime, progressRate in
NSLog("onContinue \(statusCode) \(message)")
// 대기 진행 정보 업데이트
}
}
- (void)setupCallbacks {
NetfunnelHandler.shared.onSuccess = ^(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message) {
NSLog(@"onSuccess %ld %@", (long)statusCode, message);
// NetFUNNEL이 진입을 허용할 때 원래 비즈니스 로직 실행
[self.navigationManager navigateWithDelayToDestination:NavigationDestinationSectionControlSection1];
};
NetfunnelHandler.shared.onError = ^(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message) {
NSLog(@"onError %ld %@", (long)statusCode, message);
// 시스템 오류 발생 - 강력한 서비스 가용성을 위해 원래 로직 진행
[self.navigationManager navigateWithDelayToDestination:NavigationDestinationSectionControlSection1];
};
NetfunnelHandler.shared.onNetworkError = ^(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message) {
NSLog(@"onNetworkError %ld %@", (long)statusCode, message);
// 네트워크 오류 발생 - 디버깅을 위해 로그만 남기고 비즈니스 로직은 실행하지 않음
};
NetfunnelHandler.shared.onBlock = ^(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message) {
NSLog(@"onBlock %ld %@", (long)statusCode, message);
// 사용자가 차단됨 - 적절한 메시지 표시
};
NetfunnelHandler.shared.onClose = ^(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message) {
NSLog(@"onClose %ld %@", (long)statusCode, message);
// 사용자가 대기실을 닫음 - 적절히 처리
};
NetfunnelHandler.shared.onContinue = ^(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message, NSInteger aheadWait, NSInteger behindWait, NSString *waitTime, NSInteger progressRate) {
NSLog(@"onContinue %ld %@", (long)statusCode, message);
// 대기 진행 정보 업데이트
};
}
핵심 원칙: 항상 모든 콜백 메서드를 구현하세요 - NetfunnelDelegate 요구 사항입니다.
중앙 집중식 구성
- Swift
- Objective-C
// 키를 한 곳에 저장
struct NetFunnelConfig {
static let projectKey = "your_project_key"
static let segmentKey = "your_segment_key"
}
// 모든 곳에서 동일한 구성 사용
Netfunnel.shared.nfStartSection(
projectKey: NetFunnelConfig.projectKey,
segmentKey: NetFunnelConfig.segmentKey
)
Netfunnel.shared.nfStopSection(
projectKey: NetFunnelConfig.projectKey,
segmentKey: NetFunnelConfig.segmentKey
)
// 키를 한 곳에 저장
@interface NetFunnelConfig : NSObject
+ (NSString *)projectKey;
+ (NSString *)segmentKey;
@end
@implementation NetFunnelConfig
+ (NSString *)projectKey { return @"your_project_key"; }
+ (NSString *)segmentKey { return @"your_segment_key"; }
@end
// 모든 곳에서 동일한 구성 사용
[[Netfunnel shared] nfStartSectionWithProjectKey:[NetFunnelConfig projectKey]
segmentKey:[NetFunnelConfig segmentKey]];
[[Netfunnel shared] nfStopSectionWithProjectKey:[NetFunnelConfig projectKey]
segmentKey:[NetFunnelConfig segmentKey]];
장점:
- 전체 앱에서 키를 쉽게 업데이트
- 복사-붙여넣기 오류 감소
- 구성에 대한 단일 소스
강력한 오류 처리 전략
- Swift
- Objective-C
// onError는 비즈니스 로직을 실행하지만 onNetworkError는 실행하지 않는 이유:
NetfunnelHandler.shared.onError = { projectKey, segmentKey, statusCode, message in
NSLog("onError \(statusCode) \(message)")
// 시나리오: NetFUNNEL 서버가 내부 오류를 만남
// 전략: 서비스 가용성을 유지하기 위해 비즈니스 로직 실행
// 근거: 서버 오류는 일반적으로 일시적이며 사용자 접근을 차단하지 않아야 함
// 결과: NetFUNNEL에 문제가 있어도 계속 작동하는 강력한 서비스
navigationManager.navigateWithDelay(to: .sectionControlSection1)
}
NetfunnelHandler.shared.onNetworkError = { projectKey, segmentKey, statusCode, message in
NSLog("onNetworkError \(statusCode) \(message)")
// 시나리오: 네트워크 연결 문제 (오프라인, 타임아웃)
// 전략: 로그만 남기고 비즈니스 로직은 실행하지 않음
// 근거: 자동 네트워크 복구를 위해 useNetworkRecoveryMode = true 사용
// 결과: 네트워크 문제 중에는 사용자가 대기실에 머물고 연결이 복구되면 자동으로 재개됨
}
// onError는 비즈니스 로직을 실행하지만 onNetworkError는 실행하지 않는 이유:
NetfunnelHandler.shared.onError = ^(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message) {
NSLog(@"onError %ld %@", (long)statusCode, message);
// 시나리오: NetFUNNEL 서버가 내부 오류를 만남
// 전략: 서비스 가용성을 유지하기 위해 비즈니스 로직 실행
// 근거: 서버 오류는 일반적으로 일시적이며 사용자 접근을 차단하지 않아야 함
// 결과: NetFUNNEL에 문제가 있어도 계속 작동하는 강력한 서비스
[self.navigationManager navigateWithDelayToDestination:NavigationDestinationSectionControlSection1];
};
NetfunnelHandler.shared.onNetworkError = ^(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message) {
NSLog(@"onNetworkError %ld %@", (long)statusCode, message);
// 시나리오: 네트워크 연결 문제 (오프라인, 타임아웃)
// 전략: 로그만 남기고 비즈니스 로직은 실행하지 않음
// 근거: 자동 네트워크 복구를 위해 useNetworkRecoveryMode = true 사용
// 결과: 네트워크 문제 중에는 사용자가 대기실에 머물고 연결이 복구되면 자동으로 재개됨
};
핵심 원칙: 서비스 가용성을 유지하기 위해 시스템 오류에서 항상 비즈니스 로직을 진행하세요.
네트워크 복구 모드 구성
- Swift
- Objective-C
// AppDelegate.swift
Netfunnel.initialize(
clientId: "{{CLIENT_ID}}",
delegate: NetfunnelHandler.shared, // self가 아닌 NetfunnelHandler 사용
useNetworkRecoveryMode: true // 자동 네트워크 복구 활성화
)
// AppDelegate.m
[agent initializeWithClientId:@"{{CLIENT_ID}}"
delegate:[NetfunnelHandler sharedInstance] // self가 아닌 NetfunnelHandler 사용
useNetworkRecoveryMode:YES]; // 자동 네트워크 복구 활성화
장점:
- 네트워크 연결 문제 자동 처리
- 네트워크가 복구되면 사용자가 자동으로 재개
- 수동 네트워크 오류 처리 복잡성 감소
항상 키 반환
- Swift
- Objective-C
// 성공적인 작업 후 키 반환
struct SectionControlEndView: View {
var body: some View {
VStack {
// UI 콘텐츠
}
.onAppear {
// SwiftUI 뷰가 완전히 로드될 때 키 반환
Netfunnel.shared.nfStopSection(
projectKey: NetFunnelConfig.projectKey,
segmentKey: NetFunnelConfig.segmentKey
)
}
}
}
// 성공적인 작업 후 키 반환
- (void)viewDidLoad {
[super viewDidLoad];
// 뷰 컨트롤러가 완전히 로드될 때 키 반환
[[Netfunnel shared] nfStopSectionWithProjectKey:[NetFunnelConfig projectKey]
segmentKey:[NetFunnelConfig segmentKey]];
}
키를 반환할 때:
- SwiftUI View가 완전히 로드된 후
- 구간 완료 후 (Section1 → Section2 → End)
- 비즈니스 작업 완료 후
- 작업이 실패한 경우에도
키 일치
- Swift
- Objective-C
// 시작과 중지는 동일한 키를 사용해야 함
let projectKey = "your_project_key"
let segmentKey = "your_segment_key"
Netfunnel.shared.nfStartSection(
projectKey: projectKey,
segmentKey: segmentKey
)
Netfunnel.shared.nfStopSection(
projectKey: projectKey, // 정확히 일치해야 함
segmentKey: segmentKey // 정확히 일치해야 함
)
// 시작과 중지는 동일한 키를 사용해야 함
NSString *projectKey = @"your_project_key";
NSString *segmentKey = @"your_segment_key";
[[Netfunnel shared] nfStartSectionWithProjectKey:projectKey
segmentKey:segmentKey];
[[Netfunnel shared] nfStopSectionWithProjectKey:projectKey // 정확히 일치해야 함
segmentKey:segmentKey]; // 정확히 일치해야 함
포괄적인 로깅
- Swift
- Objective-C
// AppDelegate에서 디버그 로깅 활성화
Netfunnel.initialize(
clientId: "{{CLIENT_ID}}",
delegate: NetfunnelHandler.shared, // self가 아닌 NetfunnelHandler 사용
printLog: true // 자세한 로깅 활성화
)
// Xcode 콘솔에서 로그 확인
// AppDelegate에서 디버그 로깅 활성화
[agent initializeWithClientId:@"{{CLIENT_ID}}"
delegate:[NetfunnelHandler sharedInstance] // self가 아닌 NetfunnelHandler 사용
printLog:YES]; // 자세한 로깅 활성화
// Xcode 콘솔에서 로그 확인
로깅 장점:
- NetFUNNEL 통합 문제를 쉽게 디버깅
- 요청/응답 흐름 추적
- 키 반환 작업 모니터링
- 네트워크 연결 문제 식별
일반적인 문제 및 문제 해결
대기실이 나타나지 않음
증상: 버튼 클릭은 정상적으로 작동하지만 대기실이 나타나지 않음
디버그 단계:
- Xcode 콘솔에서 iOS 오류 확인:
⌘+Shift+Y→ "Error"로 필터링 - AppDelegate에서 NetFUNNEL 초기화 확인
- 콘솔에서 세그먼트가 활성화되었는지 확인 (비활성화되지 않음)
- 테스트를 위해 진입 허용 수가 0으로 설정되었는지 확인
- 프로젝트 키와 세그먼트 키가 콘솔과 정확히 일치하는지 확인 (대소문자 구분)
델리게이트가 실행되지 않음
증상: nfStartSection()이 호출되었지만 응답을 받지 못함
디버그 단계:
- Xcode 콘솔에서 NetFUNNEL 로그 확인: "NetFUNNEL"로 필터링
- NetFUNNEL 서버에 대한 네트워크 연결 확인
- 세그먼트가 활성화되었는지 확인 (비활성화되지 않음)
- 대기실을 강제하기 위해 진입 허용 수 = 0으로 시도
- 모든 콜백 메서드가 구현되었는지 확인 (NetfunnelDelegate 요구 사항)
사용자가 큐에 갇힘
증상: 첫 번째 사용자는 진입하지만 두 번째 사용자는 통과하지 못함
디버그 단계:
- 구간 완료 후
nfStopSection()이 호출되는지 확인 nfStopSection()의 키가nfStartSection()과 정확히 일치하는지 확인nfStopSection()실행을 방해하는 iOS 오류 찾기- Xcode 콘솔에서
[NF4] Sending returnKey request로그 확인 - 로그에
[NF4] Sending returnKey request가 나타나는지 확인
대기실이 표시되지만 진입을 허용하지 않음
증상: 대기실이 나타나지만 사용자가 통과하지 못함, 진입 허용 수 = 1인 경우에도
디버그 단계:
- 콘솔에서 세그먼트 상태 확인 - "Block" 모드가 아닌지 확인
- 진입 허용 수가 1 이상으로 설정되었는지 확인
- Xcode 콘솔에서
[NF4] Sending reentry request확인 (재진입 요청) - NetFUNNEL 로그에서 오류 응답 찾기
- NetFUNNEL 서버에 대한 네트워크 연결 확인
NetFUNNEL 호출 시 앱 충돌
증상: nfStartSection() 또는 nfStopSection() 호출 시 앱 충돌
디버그 단계:
- 필수 import가 추가되었는지 확인:
Netfunnel_iOS - AppDelegate에서 NetFUNNEL이 초기화되었는지 확인
- 모든 콜백 메서드가 구현되었는지 확인 (NetfunnelDelegate 요구 사항)
- 초기화에서 델리게이트가 올바르게 설정되었는지 확인 (
NetfunnelHandler.shared) - NetFUNNEL 에이전트가 올바르게 설치되었는지 확인
키 반환 실패
증상: nfStopSection()이 호출되었지만 키가 반환되지 않음
디버그 단계:
nfStopSection()의 키가nfStartSection()과 정확히 일치하는지 확인 (대소문자 구분)- Xcode 콘솔에서
[NF4] Sending returnKey request메시지 확인 - 로그에서
[NF4] Sending returnKey request찾기 [NF4] Received returnKey response가code=200인지 확인nfStopSection()이 적절한 생명주기 메서드에서 호출되는지 확인
구간 타임아웃 문제
증상: 구간 완료 전에 키가 자동으로 반환됨
디버그 단계:
- NetFUNNEL 콘솔 → 세그먼트 → 고급 설정 →
타임아웃에서 타임아웃 설정 확인 - 구간 지속 시간이 타임아웃 기간(기본 20초)을 초과하는지 확인
[NF4] Sending key refresh request로그 찾기 (키 타임아웃 확장)- 타임아웃이 만료되기 전에
nfStopSection()이 호출되는지 확인 - 구간 탐색 흐름이 타임아웃 기간 내에 완료되는지 확인
네트워크 복구 문제
증상: 네트워크 연결 문제 중 사용자가 갇힘
디버그 단계:
- AppDelegate 초기화에서
useNetworkRecoveryMode = true확인 - NetFUNNEL 서버에 대한 네트워크 연결 확인
- 네트워크 문제를 시뮬레이션하기 위해 비행기 모드 켜기/끄기로 테스트
- Xcode 콘솔에서
onNetworkError델리게이트 호출 모니터링 onNetworkError가 비즈니스 로직을 실행하지 않는지 확인 (복구 모드에 의존)
QA 체크리스트
구현 전 확인
- 프로젝트 키 / 세그먼트 키가 콘솔과 정확히 일치함 (콘솔에서 재확인)
- AppDelegate에서 NetFUNNEL 에이전트가 올바르게 초기화됨
- 필수 import 추가됨:
Netfunnel_iOS - 모든 콜백 메서드 구현됨 (NetfunnelDelegate 요구 사항)
대기실 테스트 (진입 허용 수 = 0)
- 진입 허용 수 = 0일 때 대기실 WebView가 올바르게 표시됨
- 대기실이 올바른 세부 정보를 표시함:
- 나의 대기 순서: 1
- 예상 대기 시간: 표시되지 않음 (구간 제어만)
- 내 뒤의 대기자 수: 0
- 대기 중 **
[NF4] Sending reentry request**가 Xcode 콘솔에 주기적으로 나타남 - **
[NF4] Received reentry response**가 **code=201(WAIT)**을 표시함
구간 진입 테스트 (진입 허용 수 = 1)
- 진입 허용 수를 1로 변경할 때
onSuccess콜백이 실행됨 -
onSuccess콜백이 원래 구간 진입 로직을 실행함 - 진입 시 대기실이 즉시 사라짐
- SectionControlSection1View로 탐색이 성공적으로 발생함
구간 탐색 테스트
- 구간 탐색이 올바르게 작동함 (Section1 → Section2 → End)
- 키 타임아웃 확장이 자동으로 발생함 (5003 요청)
- **
[NF4] Sending key refresh request**가 구간 중 주기적으로 나타남 - **
[NF4] Received refreshKey response**가 **code=201**을 표시함 - 명시적 완료까지 동시 사용자 수가 유지됨
키 반환 확인
- 구간 완료 지점에서 키 반환이 올바르게 작동함
- **
[NF4] Sending returnKey request**가 Xcode 콘솔에 나타남 - **
[NF4] Sending returnKey request**가 HTTP 200으로 발생함 - **
[NF4] Received returnKey response**가 **code=200**을 표시함 - 키 반환이
nfStartSection호출당 정확히 한 번 발생함 - 키 반환이 구간 완료 후에 발생함
오류 처리
- 모든 필수 상태에 대한 콜백 분기 구현됨:
-
onSuccess- 원래 로직 실행 -
onError- 시스템 오류를 적절히 처리함 (구간 진입 진행) -
onNetworkError- 네트워크 문제를 적절히 처리함 (로깅만) -
onBlock- 차단된 상태를 적절히 처리함 -
onClose- 사용자 취소를 적절히 처리함 -
onContinue- 대기 진행을 적절히 처리함
-
- AppDelegate 초기화에서 네트워크 복구 모드 활성화됨
- 키 일치 -
nfStartSection과nfStopSection에서projectKey/segmentKey가 동일함 - 모든 델리게이트 응답에 대한 포괄적인 로깅 구현됨
Xcode 콘솔 확인
- NetFUNNEL 로그가 Xcode 콘솔에 표시됨 ("NetFUNNEL"로 필터링)
- 초기화 성공 메시지가 나타남
- 버튼 클릭 시 초기 진입 요청 전송 메시지가 나타남
- HTML 콘텐츠 가져오기 메시지가 나타남
- 키 새로고침 요청 전송 메시지가 나타남 (구간 제어 전용)
- 키 반환 요청 전송 메시지가 나타남
- 키 반환 응답 수신이
code=200으로 나타남