본문으로 건너뛰기
버전: 4.6.1

기본 제어 통합

코드 기반 통합을 사용하여 NetFUNNEL iOS 에이전트로 기본 제어를 구현하는 완전한 가이드입니다.

통합 방법

이것은 사용 가능한 두 가지 통합 방법 중 하나입니다. 통합 방법 개요를 참조하여 구간 제어와 비교하고 사용 사례에 가장 적합한 방법을 선택하세요.


작동 방식

사용자 경험:

  1. 사용자가 버튼을 클릭하거나 작업을 트리거합니다
  2. 대기실이 현재 화면에 나타납니다
  3. 진입이 허용되면 대기실이 닫히고 서비스가 계속됩니다

최적 사용 사례:

  • 버튼 클릭 보호 (로그인, 체크아웃, 주문)
  • API 호출 제한
  • 특정 작업에 대한 정밀한 제어

구간 제어와의 주요 차이점:

  • 기본 제어: 진입 속도를 제어합니다 (키가 빠르게 반환됨)
  • 구간 제어: 고정된 동시 사용자 수를 유지합니다 (구간 종료까지 키가 유지됨)

사전 요구 사항

샘플 애플리케이션 예제

이 가이드는 샘플 애플리케이션을 사용하여 기본 제어 통합 패턴을 보여줍니다. 실제 애플리케이션 코드는 여기에 표시된 예제와 다를 것입니다. 통합 개념을 이해하는 데 집중하고 패턴을 특정 코드베이스, 함수 이름, 비즈니스 로직에 맞게 조정하세요.

💡 연습 템플릿: NetFUNNEL SDK 통합 연습을 위한 iOS 애플리케이션 (단일 ViewController) 템플릿이 준비된 샘플 프로젝트를 확인하세요.


1단계: 세그먼트 생성

두 가지 제어 유형 모두 지원

코드 기반 통합은 기본 제어구간 제어를 모두 지원합니다. 이 가이드는 기본 제어를 사용합니다.

1.1 새 세그먼트 생성

  1. NetFUNNEL 콘솔 → 프로젝트세그먼트로 이동합니다
  2. + 버튼을 클릭하여 새 세그먼트를 생성합니다

세그먼트 생성 버튼

1.2 제어 유형 선택

기본 제어를 선택하고 다음을 클릭합니다

기본 제어 선택

1.3 세그먼트 구성

세그먼트 이름: 설명적인 이름을 입력합니다 (예: "로그인 버튼", "체크아웃 프로세스", "API 호출 보호")

세그먼트 이름 입력

진입 상태:

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

진입 상태 설정

대기실 적용:

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

대기실 설정

진입 허용 수:

  • 테스트를 위해 0으로 설정합니다 (아무도 허용되지 않음, 대기실이 항상 나타남)

진입 허용 수 설정

1.4 세그먼트 생성

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

세그먼트 생성됨


2단계: 키 발급 통합 지점 식별 (nfStart)

샘플 애플리케이션

다음 예제는 시연 목적으로 샘플 애플리케이션을 사용합니다. 실제 애플리케이션 코드는 여기에 표시된 것과 자연스럽게 다를 것입니다. 통합 패턴을 특정 코드 구조, 버튼 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) {
// 🎯 대상 버튼: 기본 제어 기능 카드
// 이것이 NetFUNNEL로 보호하려는 버튼입니다
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())

// 다른 버튼 - 대상이 아님
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 등 ...
}
}

이 샘플에 대한 가정:

  • "Basic Control" 버튼은 리소스 집약적인 작업을 나타냅니다
  • 이것은 다음일 수 있습니다: 사용자 로그인, 결제 처리, 파일 업로드, 데이터 내보내기
  • 많은 사용자가 동시에 이 버튼을 클릭하면 서버에 과부하가 발생할 수 있습니다
  • 다른 버튼은 덜 중요하며 보호가 필요하지 않습니다

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) {
// 🎯 대상 버튼: 기본 제어 기능 카드
Button(action: {
// 🎯 클릭 리스너: 여기서 버튼 액션이 정의됩니다
// 사용자가 버튼을 클릭하면 실행되는 코드입니다
// 현재: navigationManager.navigateWithDelay(to: .basicControl)
// 이것을 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())

// 다른 버튼 - 대상이 아님 (NetFUNNEL 보호 불필요)
Button(action: {
// 이 버튼은 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: .basicControl)이 즉시 실행됩니다
  • 통합 대상: 여기에 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 통합 지점 식별

  1. 대상 버튼: MainView의 "Basic Control" 버튼 (리소스 집약적인 버튼)
  2. 클릭 리스너: Button(action:) 클로저가 클릭 핸들러를 설정합니다
  3. 통합 위치: navigationManager.navigateWithDelay()가 호출되기 직전
  4. 여기서 하는 이유: 이것은 서버 처리가 시작되기 직전의 정확한 순간입니다
  5. 보호 전략: 서버 호출 전에 NetFUNNEL 큐를 추가합니다

전체 흐름 분석:

  1. 뷰 생성: MainView body가 버튼을 생성합니다
  2. 버튼 설정: Button(action:)이 버튼의 클릭 핸들러를 설정합니다
  3. 사용자 액션: 사용자가 "Basic Control" 버튼을 클릭합니다
  4. 현재 동작: navigationManager.navigateWithDelay()가 즉시 실행됩니다
  5. 서버 부하: 이것이 리소스 집약적인 작업을 트리거합니다

로직:

  • NetFUNNEL 없이: 버튼 클릭 → 즉시 서버 요청 → 잠재적 과부하
  • NetFUNNEL 사용: 버튼 클릭 → 큐 확인 → 제어된 서버 요청 → 성공

3단계: 키 발급 함수 구현 (nfStart)

코드에 맞게 조정

아래 예제는 샘플 애플리케이션의 버튼 액션 메서드에 NetFUNNEL을 통합하는 방법을 보여줍니다. 이 패턴을 실제 코드 구조에 맞게 조정하세요 - 보호가 필요한 다른 함수 이름, 이벤트 핸들러 또는 비즈니스 로직이 있을 수 있습니다.

3.1 키 가져오기

먼저 콘솔에서 프로젝트 키와 세그먼트 키를 찾으세요:

  1. NetFUNNEL 콘솔 → 프로젝트세그먼트로 이동합니다
  2. 세그먼트를 클릭합니다
  3. 프로젝트 키세그먼트 키를 복사합니다

프로젝트 및 세그먼트 키

3.2 nfStart 함수 이해하기

nfStart 함수는 다음과 같은 기본 구조를 가집니다:

Netfunnel.shared.nfStart(
projectKey: "your_project_key", // 콘솔에서 가져옴
segmentKey: "your_segment_key" // 콘솔에서 가져옴
)
API 참조

nfStart 매개변수, 델리게이트 처리, 응답 형식에 대한 자세한 내용은 API 참조를 참조하세요.

3.3 현재 코드로 시작하기

현재 구현:

// MainView.swift
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())

핵심 개념:

  • 비즈니스 로직: navigationManager.navigateWithDelay(to: .basicControl)은 리소스 집약적인 작업을 나타냅니다
  • 통합 지점: 이 비즈니스 로직을 NetFUNNEL 보호로 감싸야 합니다
  • 감싸기 전략: 비즈니스 로직이 실행되기 전에 nfStart()를 사용하여 액세스를 제어합니다

여기서 감싸는 이유:

  • 이것은 서버 부하가 시작되기 직전의 정확한 순간입니다
  • 여기서 감싸면 전체 다운스트림 작업이 보호됩니다
  • 비즈니스 로직은 변경되지 않습니다 - 큐 레이어만 추가합니다

3.4 필요한 import 추가

NetFUNNEL을 구현하기 전에 SwiftUI View에 필요한 import를 추가하세요:

// MainView.swift
import Netfunnel_iOS

핵심 Import:

  • Netfunnel_iOS - 메인 NetFUNNEL 프레임워크

3.5 기본 NetFUNNEL 보호 추가 (성공만)

NetfunnelHandler 이미 생성됨

설치 및 초기화 단계에서 이미 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) {
// 기본 제어 기능 카드 - NetFUNNEL로 보호됨
Button(action: {
startBasicControl() // NetFUNNEL 보호 함수 호출
}) {
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)
}) {
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: .basicControl)
}

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 startBasicControl() {
// NetFUNNEL 큐 보호 시작
Netfunnel.shared.nfStart(
projectKey: "your_project_key",
segmentKey: "your_segment_key"
)
}

// 색상 계산 속성은 간결성을 위해 생략
// private var backgroundColor: Color { ... }
}

변경 사항:

  • 감싸기: navigationManager.navigateWithDelay()가 이제 onSuccess 콜백 내부에 있습니다
  • 조건부: 특정 버튼만 NetFUNNEL 보호를 받습니다
  • 성공만: NetFUNNEL이 진입을 허용할 때만 비즈니스 로직이 실행됩니다
  • 완전한 인터페이스: 모든 필수 델리게이트 메서드가 적절한 로깅과 함께 구현되었습니다
iOS 델리게이트 요구 사항

iOS에서 NetfunnelDelegate는 모든 메서드를 구현해야 합니다. onSuccess만 구현할 수 없습니다 - 모든 델리게이트 메서드에 대한 구현을 제공해야 합니다. 이 구현은 모든 응답 유형에 대한 적절한 로깅과 함께 완전한 델리게이트 처리를 제공합니다.

이제 구현이 올바르게 작동하는지 테스트해보겠습니다.

앱을 실행하고 "Basic Control" 버튼을 클릭하세요. 화면에 대기실 WebView가 나타나야 합니다. 모든 것이 올바르게 작동하는지 확인하려면 Xcode 콘솔에서 NetFUNNEL 로그를 확인하세요.

대기실이 나타나지 않는 경우

대기실이 보이지 않으면 세그먼트 구성 단계에서 진입 허용 수를 0으로 설정했는지 확인하세요. 이 설정은 사용자가 대기실로 보내지거나 직접 진행할 수 있는지 제어합니다.

더 나은 디버깅을 위한 NetFUNNEL 로깅 활성화:

AppDelegate 초기화에서 printLog = true를 설정하여 디버그 로깅을 활성화한 다음 Xcode 콘솔을 확인하세요. 자세한 설정 지침은 NetFUNNEL 초기화 테스트) 섹션을 참조하세요.

확인할 사항:

진입 허용 수 = 0으로 "Basic Control" 버튼을 클릭하면 다음과 같은 로그가 표시되어야 합니다:

[NF4] [2025-09-18 15:17:48.452] Initialization successful. NetFUNNEL Version: 4.3.2-onprem
[NF4] [2025-09-18 15:17:52.331] Sending initialEntry request: https://nf4-onprem-demo-4525.stclab.com/ts.wseq?opcode=5101&sid=service_1&aid=test_liam_01
[NF4] [2025-09-18 15:17:52.475] Received initialEntry response: Response(timestamp: Optional(1758176272469), code: 201, key: Optional("000F49F7F52ACADEF43B48BA3B72504953385B97C9B710E930978A54657743A944B31A90F7E4D124D288493C55ED1695BCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B706459E4B85A186035300FD8474FD450E3D342C312C302C302C302C302C30"), 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-18 15:17:53.154] 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-18 15:17:53.481] Sending reentry request: https://nf4-onprem-demo-4525.stclab.com:443/ts.wseq?opcode=5002&key=000F49F7F52ACADEF43B48BA3B72504953385B97C9B710E930978A54657743A944B31A90F7E4D124D288493C55ED1695BCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B706459E4B85A186035300FD8474FD450E3D342C312C302C302C302C302C30&sticky=nf1
[NF4] [2025-09-18 15:17:53.542] Received reentry response: Response(timestamp: Optional(1758176273542), code: 201, key: Optional("000F49F7F52ACADEF43B48BA3B725049E96705109E9BE7300670B2EB820666F55758079BA9E0833F05741BAB6B0407A3BCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B706459E4B85A186035300FD8474FD450E3D342C312C302C302C302C302C30"), 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 successfulNetFUNNEL 에이전트가 올바르게 로드됨✅ 정상
[NF4] Sending initialEntry requestnfStart()가 성공적으로 호출됨, 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 상태로 응답함✅ 정상

문제가 발생한 경우:

  • 로그가 나타나지 않음: 초기화에서 printLog = true가 설정되었는지 확인하세요
  • 대기실이 없음: 콘솔에서 진입 허용 수가 0으로 설정되었는지 확인하세요
  • 앱 충돌: 모든 필수 델리게이트 메서드가 올바르게 구현되었는지 확인하세요
  • 네트워크 오류: 서버 URL과 네트워크 연결을 확인하세요

대기실과 로그가 보이면 기본 구현이 작동 중입니다! 이제 더 나은 오류 관리를 위해 콜백 처리를 향상시킬 수 있습니다. 진입을 테스트하려면 콘솔에서 진입 허용 수를 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: .basicControl)
}

NetfunnelHandler.shared.onError = { projectKey, segmentKey, statusCode, message in
// 시스템 오류 발생 - 강력한 서비스 가용성을 위해 원래 로직 진행
NSLog("onError \(statusCode) \(message)")
navigationManager.navigateWithDelay(to: .basicControl)
}

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 참조

모든 델리게이트 응답 유형, 상태 코드, 응답 객체 구조 및 고급 콜백 패턴에 대한 자세한 정보는 API 참조를 참조하세요.

3.7 핵심 구현 포인트

  1. 프로젝트/세그먼트 키: NetFUNNEL 콘솔의 정확한 키 사용
  2. 필수 import: Netfunnel_iOS import 추가
  3. 델리게이트 구현: 모든 콜백 메서드 구현 (NetfunnelDelegate 요구 사항)
  4. 강력한 오류 처리:
    • onSuccessonError는 서비스 가용성을 위해 비즈니스 로직을 실행합니다
    • onNetworkError는 로그만 기록합니다 (자동 복구를 위해 useNetworkRecoveryMode = true 사용)
  5. 뷰 생명주기: 초기화 및 UI 업데이트에 onAppear 사용
  6. 포괄적인 로깅: 디버깅을 위해 모든 델리게이트 응답 로깅

4단계: 키 반환 통합 지점 식별 (nfStop)

샘플 애플리케이션

다음 예제는 시연 목적으로 샘플 애플리케이션을 사용합니다. 실제 애플리케이션 코드는 여기에 표시된 것과 자연스럽게 다를 것입니다. 통합 패턴을 특정 코드 구조, 비즈니스 로직 완료 지점, 키 반환 요구 사항에 맞게 조정하세요.

💡 연습 프로젝트가 필요하신가요? NetFUNNEL SDK 통합 연습을 위한 iOS 애플리케이션 (단일 ViewController) 템플릿이 준비된 샘플 프로젝트를 확인하세요.

샘플 애플리케이션 이해하기:

NetFUNNEL 키를 반환하기 위해 nfStop을 호출해야 하는 위치를 이해하기 위해 샘플 애플리케이션을 살펴보겠습니다.

4.1 비즈니스 로직 완료 지점 식별

nfStart 성공 후 현재 흐름:

// 3단계에서 - nfStart가 성공할 때
NetfunnelHandler.shared.onSuccess = { projectKey, segmentKey, statusCode, message in
NSLog("onSuccess \(statusCode) \(message)")
// NetFUNNEL이 진입을 허용할 때 원래 비즈니스 로직 실행
navigationManager.navigateWithDelay(to: .basicControl)
}

다음에 일어나는 일:

  1. 사용자가 BasicControlView로 이동 - SwiftUI View 생명주기가 시작됩니다
  2. 뷰가 완전히 로드됨 - onAppear가 트리거됩니다
  3. 사용자의 세션이 활성화됨 - 이제 보호된 기능을 사용할 수 있습니다
  4. 키가 반환되어야 함 - 큐의 다음 사용자가 진입할 수 있도록

nfStop 구현에 대한 중요 참고 사항: nfStopnfStart가 먼저 호출될 필요 없이 독립적으로 호출할 수 있습니다. nfStart가 호출되지 않았다면 NetFUNNEL이 필요한 경우 키 해제를 자동으로 처리하거나 키가 없으면 아무 작업도 하지 않습니다. 이로 인해 조건부 확인 없이 어떤 시나리오에서도 nfStop을 안전하게 호출할 수 있어 구현이 단순해집니다.

4.2 키 반환을 위한 통합 지점 식별

통합 지점 옵션:

통합 지점사용 시기장점
SwiftUI View 생명주기간단한 네비게이션 흐름구현이 쉽고 대부분의 경우에 작동함
비즈니스 로직 완료복잡한 작업 (API 호출, 처리)정밀한 제어, 실제 작업 후 키 반환
사용자 액션 완료사용자 시작 작업사용자가 작업을 완료할 때 키 반환

4.3 적절한 통합 지점 선택

샘플 애플리케이션의 경우:

현재 비즈니스 로직: navigationManager.navigateWithDelay(to: .basicControl)

  • 하는 일: BasicControlView로 이동합니다
  • 완료 시점: BasicControlView가 성공적으로 로드될 때
  • 최적 통합 지점: SwiftUI View 생명주기 이벤트 (onAppear)

통합 전략:

  1. SwiftUI View 생성: BasicControlView가 완전히 로드될 때 키 반환
  2. 간단하고 신뢰할 수 있음: 네비게이션 기반 흐름에 작동합니다
  3. 사용자 경험: 사용자가 실제로 서비스를 사용할 수 있을 때 키가 반환됩니다

4.4 통합 지점 로직 확인

전체 흐름 분석:

  1. 사용자가 버튼 클릭nfStart() 호출됨
  2. 대기실 나타남 → 사용자가 큐에서 대기함
  3. 진입 허용됨onSuccess 콜백이 발생함
  4. 비즈니스 로직 실행navigationManager.navigateWithDelay(to: .basicControl) 실행됨
  5. BasicControlView 로드됨 → 사용자가 이제 서비스를 사용할 수 있음
  6. 키 반환이 필요함 → 큐 슬롯을 해제하기 위해 nfStop() 호출

이 통합 지점이 합리적인 이유:

  • 사용자 경험: 사용자가 실제로 서비스를 사용할 수 있을 때 키가 반환됩니다
  • 큐 관리: 현재 사용자가 준비되면 다음 사용자가 즉시 진입할 수 있습니다
  • 리소스 효율성: 불필요한 큐 차단을 방지합니다
  • 구현 단순성: 구현 및 유지 관리가 쉽습니다

핵심 통찰: nfStop 통합 지점은 사용자의 의도한 비즈니스 작업이 진정으로 완료되고 큐에서 대기한 서비스의 이점을 얻을 수 있는 위치여야 합니다.


5단계: 키 반환 함수 구현 (nfStop)

애플리케이션에 맞게 조정

아래 예제는 키를 반환하는 다양한 접근 방식을 보여줍니다. 애플리케이션 아키텍처에 가장 적합한 접근 방식을 선택하세요 - ViewController 네비게이션 후, API 호출 후, 또는 다른 비즈니스 로직 완료 후 키를 반환해야 하는지 여부에 따라 다릅니다.

5.1 nfStop 함수 이해하기

nfStop 함수는 다음과 같은 기본 구조를 가집니다:

Netfunnel.shared.nfStop(
projectKey: "your_project_key", // nfStart 키와 정확히 일치해야 함
segmentKey: "your_segment_key" // nfStart 키와 정확히 일치해야 함
)

핵심 요구 사항:

  • 정확한 키 일치: 키는 nfStart()에서 사용된 것과 동일해야 합니다
  • 타이밍: nfStart() 직후가 아닌 비즈니스 로직 완료 후 호출합니다

5.2 필요한 import 추가

nfStop을 구현하기 전에 View에 필요한 import를 추가하세요:

import SwiftUI
import Netfunnel_iOS

핵심 Import:

  • Netfunnel_iOS - 메인 NetFUNNEL 프레임워크 (nfStart에 이미 import됨)

5.3 기본 키 반환 추가 (SwiftUI View 생명주기)

BasicControlView가 로드될 때 키 반환:

// BasicControlView.swift
import SwiftUI
import Netfunnel_iOS

struct BasicControlView: View {
@StateObject private var navigationManager = NavigationManager.shared

var body: some View {
VStack(spacing: 0) {
// 성공 섹션
VStack(spacing: 24) {
// 성공 아이콘 및 제목은 간결성을 위해 생략
// ... 성공 UI 요소 ...

Text("Basic Control Complete")
.font(.system(size: 28, weight: .bold, design: .rounded))
.foregroundColor(primaryTextColor)
.multilineTextAlignment(.center)

Text("This is the completion page for Basic 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.nfStop(
projectKey: "your_project_key",
segmentKey: "your_segment_key"
)
}
}

// 색상 계산 속성은 간결성을 위해 생략
// private var backgroundColor: Color { ... }
// private var primaryTextColor: Color { ... }
// private var secondaryTextColor: Color { ... }
}

변경 사항:

  • SwiftUI View 생명주기: BasicControlView가 완전히 로드될 때 키 반환
  • 간단한 접근: 기본 네비게이션 시나리오에 작동합니다
  • 직접 호출: NetFUNNEL이 초기화되고 사용 가능하다고 가정합니다

이제 키 반환 구현이 올바르게 작동하는지 테스트해보겠습니다.

앱을 실행하고 다음 테스트 시퀀스를 따르세요:

  1. "Basic Control (Code-based Integration)" 버튼 클릭 - 대기실이 나타나야 합니다
  2. NetFUNNEL 콘솔에서 진입 허용 수를 0에서 1로 변경 - 이것이 진입을 허용합니다
  3. 대기실이 닫히는 것을 관찰 - 사라지고 BasicControlView로 이동해야 합니다
  4. Xcode 콘솔에서 키 반환 로그 확인 - 완료 로그가 표시되어야 합니다

더 나은 디버깅을 위한 NetFUNNEL 로깅 활성화:

AppDelegate 초기화에서 printLog = true를 설정하여 디버그 로깅을 활성화한 다음 Xcode 콘솔을 확인하세요. 자세한 설정 지침은 NetFUNNEL 초기화 테스트) 섹션을 참조하세요.

확인할 사항:

대기실 단계에서 진입 허용 수를 0에서 1로 변경하면 다음과 같은 로그가 표시되어야 합니다:

[NF4] [2025-09-18 15:26:39.184] Sending initialEntry request: https://nf4-onprem-demo-4525.stclab.com/ts.wseq?opcode=5101&sid=service_1&aid=test_liam_01
[NF4] [2025-09-18 15:26:39.265] Received initialEntry response: Response(timestamp: Optional(1758176799306), code: 201, key: Optional("38AED8AD2AB482D3042E4564072A4B01F2FF579E4EB078FCB0C8421263F389F8B9586BFF9617864B33217D368B3B2A1FBCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B706459E4B85A186035300FD8474FD450E3D352C312C312C302C302C302C30"), nwait: Optional(1), 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-18 15:26:39.803] 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-18 15:26:39.871] Sending reentry request: https://nf4-onprem-demo-4525.stclab.com:443/ts.wseq?opcode=5002&key=38AED8AD2AB482D3042E4564072A4B01F2FF579E4EB078FCB0C8421263F389F8B9586BFF9617864B33217D368B3B2A1FBCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B706459E4B85A186035300FD8474FD450E3D352C312C312C302C302C302C30&sticky=nf1
[NF4] [2025-09-18 15:26:39.920] Received reentry response: Response(timestamp: Optional(1758176799970), code: 201, key: Optional("38AED8AD2AB482D3042E4564072A4B014AE7EF0CDA16397B678ED3382A6A8BAF1CEB37B55CCC33944E7335CBCAA4A95FBCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B706459E4B85A186035300FD8474FD450E3D342C312C302C302C302C302C30"), 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-18 15:26:48.450] Received reentry response: Response(timestamp: Optional(1758176808499), code: 200, key: Optional("38AED8AD2AB482D3042E4564072A4B01A6758441574083251F3F4BC91E4E9873D39834BE911F6D95F3EB16E3BE77DBB36AA7C2ADE2FDDEF7523C7393E95B8401DA85D3F8F85FA7FA7B7F120E3C54DAC2071A87CC017BE50129F11B1499A36DF643308DE76E9345037F51A6CCA3A36C6E2C302C302C30"), 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(""), liveMessage: nil, chkEnterCnt: Optional(1), waitOrderYn: Optional(0), msg: nil, vwrType: Optional(""))
[NF4] [2025-09-18 15:26:50.174] Sending returnKey request: https://nf4-onprem-demo-4525.stclab.com:443/ts.wseq?opcode=5004&key=38AED8AD2AB482D3042E4564072A4B01A6758441574083251F3F4BC91E4E9873D39834BE911F6D95F3EB16E3BE77DBB36AA7C2ADE2FDDEF7523C7393E95B8401DA85D3F8F85FA7FA7B7F120E3C54DAC2071A87CC017BE50129F11B1499A36DF643308DE76E9345037F51A6CCA3A36C6E2C302C302C30&sticky=nf1
[NF4] [2025-09-18 15:26:50.223] Received returnKey response. code=200, key=Optional("38AED8AD2AB482D3042E4564072A4B01A6758441574083251F3F4BC91E4E9873D39834BE911F6D95F3EB16E3BE77DBB36AA7C2ADE2FDDEF7523C7393E95B8401DA85D3F8F85FA7FA7B7F120E3C54DAC2071A87CC017BE50129F11B1499A36DF643308DE76E9345037F51A6CCA3A36C6E2C302C302C30")

모든 것이 작동 중임을 나타내는 핵심 지표:

로그 메시지의미상태
[NF4] Sending initialEntry requestNetFUNNEL 서버로 초기 요청 전송됨✅ 정상
[NF4] Received initialEntry response with code: 201서버가 WAIT 상태로 응답함✅ 정상
[NF4] Fetching HTML content대기실 HTML이 로드되고 있음✅ 정상
[NF4] Sending reentry request (반복됨)주기적인 재진입 요청 (폴링)✅ 정상
[NF4] Received reentry response with code: 200진입 허용됨 - 대기실이 닫혀야 함✅ 정상
[NF4] Sending returnKey request서버로 키 반환 요청 전송됨✅ 정상
[NF4] Received returnKey response with code=200키 반환 성공✅ 정상

테스트 시퀀스:

  1. 진입 허용 수 = 0으로 시작: 대기실이 나타나고 재진입 요청이 반복됩니다
  2. 진입 허용 수를 1로 변경: 재진입 응답이 code: 200으로 변경되고 대기실이 닫힙니다
  3. SwiftUI View 로드: BasicControlView onAppearnfStop()을 호출합니다
  4. 키 반환: returnKey 요청/응답이 키 반환을 확인합니다

문제가 발생한 경우:

  • returnKey 로그가 없음: BasicControlView에서 nfStop()이 호출되는지 확인하세요
  • 키 반환이 실패함: nfStart()nfStop() 사이의 프로젝트/세그먼트 키가 정확히 일치하는지 확인하세요
  • SwiftUI View가 로드되지 않음: onSuccess 콜백이 네비게이션을 올바르게 실행하는지 확인하세요
  • 대기실이 닫히지 않음: 콘솔에서 진입 허용 수가 1로 설정되었는지 확인하세요

대기실 → 진입 → SwiftUI view 로드 → 키 반환의 전체 흐름이 보이면 구현이 올바르게 작동 중입니다! 이제 오류 처리를 향상시키고 더 강력한 콜백 관리를 추가할 수 있습니다.

5.4 핵심 구현 포인트

모범 사례: 즉시 키 반환

비즈니스 로직이 완료되면 NetFUNNEL 키를 반환하세요. NetFUNNEL이 타임아웃 후 자동으로 키를 반환하지만, 수동 반환은 더 나은 사용자 경험과 큐 효율성을 제공합니다.

키 반환 규칙:

  • 항상: 비즈니스 로직 완료 후 키 반환 (성공 또는 실패)
  • ⚠️ 자동 타임아웃: 수동으로 반환하지 않으면 NetFUNNEL이 자동으로 키를 반환합니다
  • 타이밍: nfStart() 직후가 아닌 비즈니스 로직 완료 후 호출합니다

구현 체크리스트:

  1. 정확한 키 일치: nfStop()의 키는 nfStart()와 정확히 일치해야 합니다
  2. SwiftUI View 생명주기: 키 반환 타이밍에 onAppear 사용
  3. 일관된 키: 전체 흐름에서 동일한 프로젝트/세그먼트 키 사용
완전한 API 참조

nfStop 매개변수, 응답 처리 및 고급 키 반환 패턴에 대한 자세한 정보는 API 참조를 참조하세요.


6단계: 대기실 테스트 (진입 허용 수 = 0)

애플리케이션으로 테스트

아래 테스트 단계는 샘플 애플리케이션을 기반으로 합니다. 테스트 프로세스를 실제 애플리케이션에 맞게 조정하세요 - 버튼 이름, 뷰 컨트롤러 네비게이션, 확인 단계를 특정 구현에 맞게 교체하세요.

6.1 작업 트리거

  1. Xcode 콘솔 로그 지우기 (깨끗한 상태에서 관찰하려는 경우)

    • Xcode에서 콘솔의 휴지통 아이콘을 클릭하여 이전 로그를 지웁니다
    • 또는 Cmd+K를 사용하여 콘솔을 지웁니다
  2. 보호된 버튼 클릭 (예: "Basic Control (Code-based Integration)" 버튼)

예상 결과: 현재 화면에 대기실 WebView가 나타납니다

클릭 전 iOS 메인 화면 iOS 대기실 WebView

6.2 대기실 표시 확인

다음 요소가 올바르게 표시되는지 확인하세요:

진입 허용 수가 0으로 설정되고 한 명의 사용자만 연결된 경우 다음을 확인하세요:

  • 나의 대기 순서: 1
  • 예상 대기 시간: 00:00:00 (시:분:초)
  • 내 뒤의 대기자 수: 0
iOS 대기실 WebView

6.3 Xcode 콘솔 활동 확인

NetFUNNEL 로그 확인:

AppDelegate 초기화에서 printLog = true를 설정하여 디버그 로깅을 활성화한 다음 Xcode 콘솔을 확인하세요. 다음과 같은 로그가 표시되어야 합니다:

[NF4] [2025-09-18 15:26:39.184] Sending initialEntry request: https://nf4-onprem-demo-4525.stclab.com/ts.wseq?opcode=5101&sid=service_1&aid=test_liam_01
[NF4] [2025-09-18 15:26:39.265] Received initialEntry response: Response(timestamp: Optional(1758176799306), code: 201, key: Optional("38AED8AD2AB482D3042E4564072A4B01F2FF579E4EB078FCB0C8421263F389F8B9586BFF9617864B33217D368B3B2A1FBCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B706459E4B85A186035300FD8474FD450E3D352C312C312C302C302C302C30"), nwait: Optional(1), 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-18 15:26:39.803] 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-18 15:26:39.871] Sending reentry request: https://nf4-onprem-demo-4525.stclab.com:443/ts.wseq?opcode=5002&key=38AED8AD2AB482D3042E4564072A4B01F2FF579E4EB078FCB0C8421263F389F8B9586BFF9617864B33217D368B3B2A1FBCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B706459E4B85A186035300FD8474FD450E3D352C312C312C302C302C302C30&sticky=nf1
[NF4] [2025-09-18 15:26:39.920] Received reentry response: Response(timestamp: Optional(1758176799970), code: 201, key: Optional("38AED8AD2AB482D3042E4564072A4B014AE7EF0CDA16397B678ED3382A6A8BAF1CEB37B55CCC33944E7335CBCAA4A95FBCFD08B8F833299D4D743DBF3C84B6D4E168B227EF3E2A74958D7ED95766B706459E4B85A186035300FD8474FD450E3D342C312C302C302C302C302C30"), 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] Sending initialEntry requestNetFUNNEL 서버로 초기 요청 전송됨✅ 정상
[NF4] Received initialEntry response with code: 201서버가 WAIT 상태로 응답함✅ 정상
[NF4] Fetching HTML content대기실 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이므로 올바른 응답은 201입니다

7단계: 진입 테스트 (진입 허용 수 = 1)

7.1 세그먼트 설정 업데이트

  1. NetFUNNEL 콘솔로 돌아가기
  2. 세그먼트의 수정 버튼 클릭하여 편집 화면 열기

NetFUNNEL 콘솔 세그먼트 수정 버튼

  1. 진입 허용 수0에서 1로 변경
  2. 하단에서 확인 클릭

진입 허용 수 업데이트 변경 사항 확인

즉시 효과

확인을 클릭하자마자 대기실이 사라지고 즉시 BasicControlViewController로 이동합니다. 이 순간을 관찰하려면 대기실이 현재 표시된 다른 화면을 열어두세요.

7.2 진입 확인

예상 결과: 대기실이 즉시 사라지고 BasicControlViewController로 네비게이션이 발생합니다

iOS 서비스 진입 성공

Xcode 콘솔에서 진입 확인 확인:

다음과 같은 로그가 표시되어야 합니다:

[NF4] [2025-09-18 15:26:48.450] Received reentry response: Response(timestamp: Optional(1758176808499), code: 200, key: Optional("38AED8AD2AB482D3042E4564072A4B01A6758441574083251F3F4BC91E4E9873D39834BE911F6D95F3EB16E3BE77DBB36AA7C2ADE2FDDEF7523C7393E95B8401DA85D3F8F85FA7FA7B7F120E3C54DAC2071A87CC017BE50129F11B1499A36DF643308DE76E9345037F51A6CCA3A36C6E2C302C302C30"), 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(""), liveMessage: nil, chkEnterCnt: Optional(1), waitOrderYn: Optional(0), msg: nil, vwrType: Optional(""))

성공적인 진입의 핵심 지표:

로그 메시지의미상태
[NF4] Received reentry response with code: 200진입 허용됨 - 대기실이 닫혀야 함✅ 정상

7.3 키 반환 확인

성공적인 키 반환 확인:

Xcode 콘솔에서 다음 로그를 찾으세요:

[NF4] [2025-09-18 15:26:50.174] Sending returnKey request: https://nf4-onprem-demo-4525.stclab.com:443/ts.wseq?opcode=5004&key=38AED8AD2AB482D3042E4564072A4B01A6758441574083251F3F4BC91E4E9873D39834BE911F6D95F3EB16E3BE77DBB36AA7C2ADE2FDDEF7523C7393E95B8401DA85D3F8F85FA7FA7B7F120E3C54DAC2071A87CC017BE50129F11B1499A36DF643308DE76E9345037F51A6CCA3A36C6E2C302C302C30&sticky=nf1
[NF4] [2025-09-18 15:26:50.223] Received returnKey response. code=200, key=Optional("38AED8AD2AB482D3042E4564072A4B01A6758441574083251F3F4BC91E4E9873D39834BE911F6D95F3EB16E3BE77DBB36AA7C2ADE2FDDEF7523C7393E95B8401DA85D3F8F85FA7FA7B7F120E3C54DAC2071A87CC017BE50129F11B1499A36DF643308DE76E9345037F51A6CCA3A36C6E2C302C302C30")

성공적인 키 반환의 핵심 지표:

로그 메시지의미상태
[NF4] Sending returnKey request서버로 키 반환 요청 전송됨✅ 정상
[NF4] Received returnKey response with code=200키 반환 성공✅ 정상

요약

필수 사항 (반드시 수행)

설정:

  • 설치 및 초기화 완료 및 NetFUNNEL 에이전트가 올바르게 로드되는지 확인
  • 콘솔에서 기본 제어로 세그먼트 생성
  • 콘솔에서 프로젝트 키와 세그먼트 키 가져오기
  • 테스트를 위해 진입 허용 수를 0으로, 프로덕션을 위해 1 이상으로 설정

통합:

  • 필수 import 추가: Netfunnel_iOS
  • 모든 콜백 메서드 구현 (NetfunnelDelegate 요구 사항)
  • 액션 메서드에서 비즈니스 로직을 Netfunnel.shared.nfStart()로 감싸기
  • 비즈니스 로직을 실행하기 위해 onSuccess 콜백 처리
  • 비즈니스 로직 완료 후 Netfunnel.shared.nfStop() 호출
  • nfStart()nfStop() 모두에서 동일한 키 사용

오류 처리:

  • onError를 비즈니스 로직 진행으로 처리 (서비스 가용성 유지)
  • onNetworkError를 로그만 기록하도록 처리 (useNetworkRecoveryMode = true 사용)
  • onBlock을 적절한 사용자 메시지 표시로 처리
  • onClose를 현재 뷰 컨트롤러에 유지하도록 처리 (사용자가 취소함)
  • 모든 델리게이트 응답에 대한 포괄적인 로깅 구현

테스트:

  • 진입 허용 수 = 0으로 대기실이 나타나는지 테스트
  • 진입 허용 수 = 1로 진입이 작동하는지 테스트
  • 완료 후 키 반환이 발생하는지 확인
  • Xcode 콘솔에서 NetFUNNEL 로그 확인

선택 사항 (있으면 좋음)

오류 처리 향상:

  • 기본 로깅 대신 사용자 친화적인 오류 메시지 추가
  • 다양한 응답 유형에 대한 사용자 정의 오류 처리 전략 구현
  • NetFUNNEL 이벤트에 대한 분석 추적 추가

코드 구성:

  • 중앙 집중식 구성 상수 생성
  • 재사용 가능한 NetFUNNEL 래퍼 함수 구축
  • 여러 뷰 컨트롤러에 걸쳐 모듈식 통합 패턴 구현

모범 사례

필수 Import

import Netfunnel_iOS

이러한 import가 중요한 이유:

  • NetFUNNEL 통합에 필수적입니다
  • NetFUNNEL을 사용하는 모든 View에 추가해야 합니다
  • nfStart()nfStop() 함수 모두에 필요합니다

완전한 콜백 구현

private func setupCallbacks() {
NetfunnelHandler.shared.onSuccess = { projectKey, segmentKey, statusCode, message in
NSLog("onSuccess \(statusCode) \(message)")
// NetFUNNEL이 진입을 허용할 때 원래 비즈니스 로직 실행
navigationManager.navigateWithDelay(to: .basicControl)
}

NetfunnelHandler.shared.onError = { projectKey, segmentKey, statusCode, message in
NSLog("onError \(statusCode) \(message)")
// 시스템 오류 발생 - 강력한 서비스 가용성을 위해 원래 로직 진행
navigationManager.navigateWithDelay(to: .basicControl)
}

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)")
// 대기 진행 정보 업데이트
}
}

핵심 원칙: 항상 모든 콜백 메서드를 구현하세요 - NetfunnelDelegate 요구 사항입니다.

중앙 집중식 구성

// 키를 한 곳에 저장
struct NetFunnelConfig {
static let projectKey = "your_project_key"
static let segmentKey = "your_segment_key"
}

// 모든 곳에서 동일한 구성 사용
Netfunnel.shared.nfStart(
projectKey: NetFunnelConfig.projectKey,
segmentKey: NetFunnelConfig.segmentKey
)

Netfunnel.shared.nfStop(
projectKey: NetFunnelConfig.projectKey,
segmentKey: NetFunnelConfig.segmentKey
)

장점:

  • 전체 앱에서 키를 쉽게 업데이트할 수 있습니다
  • 복사-붙여넣기 오류를 줄입니다
  • 구성에 대한 단일 소스입니다

강력한 오류 처리 전략

// onError는 비즈니스 로직을 실행하지만 onNetworkError는 실행하지 않는 이유:

NetfunnelHandler.shared.onError = { projectKey, segmentKey, statusCode, message in
NSLog("onError \(statusCode) \(message)")
// 시나리오: NetFUNNEL 서버가 내부 오류를 만남
// 전략: 서비스 가용성을 유지하기 위해 비즈니스 로직 실행
// 근거: 서버 오류는 일반적으로 일시적이며 사용자 액세스를 차단해서는 안 됩니다
// 결과: NetFUNNEL에 문제가 있어도 계속되는 강력한 서비스
navigationManager.navigateWithDelay(to: .basicControl)
}

NetfunnelHandler.shared.onNetworkError = { projectKey, segmentKey, statusCode, message in
NSLog("onNetworkError \(statusCode) \(message)")
// 시나리오: 네트워크 연결 문제 (오프라인, 타임아웃)
// 전략: 로그만 기록하고 비즈니스 로직은 실행하지 않음
// 근거: 자동 네트워크 복구를 위해 useNetworkRecoveryMode = true 사용
// 결과: 네트워크 문제 중 사용자는 대기실에 머물고 연결이 복구되면 자동으로 재개됩니다
}

핵심 원칙: 서비스 가용성을 유지하기 위해 시스템 오류에서 항상 비즈니스 로직을 진행하세요.

네트워크 복구 모드 구성

// AppDelegate.swift
Netfunnel.initialize(
clientId: "{{CLIENT_ID}}",
delegate: NetfunnelHandler.shared, // self가 아닌 NetfunnelHandler 사용
useNetworkRecoveryMode: true // 자동 네트워크 복구 활성화
)

장점:

  • 네트워크 연결 문제의 자동 처리
  • 네트워크가 복구되면 사용자가 자동으로 재개됩니다
  • 수동 네트워크 오류 처리 복잡성을 줄입니다

항상 키 반환

// 성공적인 작업 후 키 반환
struct BasicControlView: View {
var body: some View {
VStack {
// UI 콘텐츠
}
.onAppear {
// SwiftUI 뷰가 완전히 로드될 때 키 반환
Netfunnel.shared.nfStop(
projectKey: NetFunnelConfig.projectKey,
segmentKey: NetFunnelConfig.segmentKey
)
}
}
}

키를 반환할 때:

  • SwiftUI View가 완전히 로드된 후
  • API 호출 완료 후
  • 비즈니스 작업 완료 후
  • 작업이 실패한 경우에도

키 일치

// 시작과 중지는 동일한 키를 사용해야 함
let projectKey = "your_project_key"
let segmentKey = "your_segment_key"

Netfunnel.shared.nfStart(
projectKey: projectKey,
segmentKey: segmentKey
)

Netfunnel.shared.nfStop(
projectKey: projectKey, // 정확히 일치해야 함
segmentKey: segmentKey // 정확히 일치해야 함
)

포괄적인 로깅

// AppDelegate에서 디버그 로깅 활성화
Netfunnel.initialize(
clientId: "{{CLIENT_ID}}",
delegate: NetfunnelHandler.shared, // self가 아닌 NetfunnelHandler 사용
printLog: true // 자세한 로깅 활성화
)

// Xcode 콘솔에서 로그 확인

로깅의 장점:

  • NetFUNNEL 통합 문제를 쉽게 디버깅할 수 있습니다
  • 요청/응답 흐름을 추적할 수 있습니다
  • 키 반환 작업을 모니터링할 수 있습니다
  • 네트워크 연결 문제를 식별할 수 있습니다

일반적인 문제 및 문제 해결

대기실이 나타나지 않음

증상: 버튼 클릭이 정상적으로 작동하지만 대기실이 나타나지 않음

디버그 단계:

  1. Xcode 콘솔에서 iOS 오류 확인: ⌘+Shift+Y → "Error"로 필터링
  2. AppDelegate에서 NetFUNNEL 초기화 확인
  3. 콘솔에서 세그먼트가 활성화되었는지 확인 (비활성화되지 않음)
  4. 테스트를 위해 진입 허용 수가 0으로 설정되었는지 확인
  5. 프로젝트 키와 세그먼트 키가 콘솔과 정확히 일치하는지 확인 (대소문자 구분)

델리게이트가 발생하지 않음

증상: nfStart()가 호출되었지만 응답을 받지 못함

디버그 단계:

  1. Xcode 콘솔에서 NetFUNNEL 로그 확인: "NetFUNNEL"로 필터링
  2. NetFUNNEL 서버에 대한 네트워크 연결 확인
  3. 세그먼트가 활성화되었는지 확인 (비활성화되지 않음)
  4. 대기실을 강제로 표시하기 위해 진입 허용 수 = 0으로 시도
  5. 모든 콜백 메서드가 구현되었는지 확인 (NetfunnelDelegate 요구 사항)

사용자가 큐에 갇힘

증상: 첫 번째 사용자는 진입하지만 두 번째 사용자는 통과하지 못함

디버그 단계:

  1. 비즈니스 로직 완료 후 nfStop()이 호출되는지 확인
  2. nfStop()의 키가 nfStart()와 정확히 일치하는지 확인
  3. nfStop() 실행을 방해하는 iOS 오류 찾기
  4. Xcode 콘솔에서 [NF4] Sending returnKey request 로그 확인
  5. 로그에 [NF4] Sending returnKey request가 나타나는지 확인

대기실이 표시되지만 진입을 허용하지 않음

증상: 대기실이 나타나지만 사용자가 통과하지 못함, 진입 허용 수 = 1인 경우에도

디버그 단계:

  1. 콘솔에서 세그먼트 상태 확인 - "Block" 모드가 아닌지 확인
  2. 진입 허용 수가 1 이상으로 설정되었는지 확인
  3. Xcode 콘솔에서 [NF4] Sending reentry request 확인 (재진입 요청)
  4. NetFUNNEL 로그에서 오류 응답 찾기
  5. NetFUNNEL 서버에 대한 네트워크 연결 확인

NetFUNNEL 호출 시 앱 충돌

증상: nfStart() 또는 nfStop() 호출 시 앱이 충돌함

디버그 단계:

  1. 필수 import가 추가되었는지 확인: Netfunnel_iOS
  2. AppDelegate에서 NetFUNNEL이 초기화되었는지 확인
  3. 모든 콜백 메서드가 구현되었는지 확인 (NetfunnelDelegate 요구 사항)
  4. 초기화에서 델리게이트가 올바르게 설정되었는지 확인 (NetfunnelHandler.shared)
  5. NetFUNNEL 에이전트가 올바르게 설치되었는지 확인

키 반환이 실패함

증상: nfStop()이 호출되었지만 키가 반환되지 않음

디버그 단계:

  1. nfStop()의 키가 nfStart()와 정확히 일치하는지 확인 (대소문자 구분)
  2. Xcode 콘솔에서 [NF4] Sending returnKey request 메시지 확인
  3. 로그에서 [NF4] Sending returnKey request 찾기
  4. code=200과 함께 [NF4] Received returnKey response 확인
  5. nfStop()이 적절한 생명주기 메서드에서 호출되는지 확인

네트워크 복구 문제

증상: 네트워크 연결 문제 중 사용자가 갇힘

디버그 단계:

  1. AppDelegate 초기화에서 useNetworkRecoveryMode = true 확인
  2. NetFUNNEL 서버에 대한 네트워크 연결 확인
  3. 네트워크 문제를 시뮬레이션하기 위해 비행기 모드 켜기/끄기로 테스트
  4. Xcode 콘솔에서 onNetworkError 델리게이트 호출 모니터링
  5. onNetworkError가 비즈니스 로직을 실행하지 않는지 확인 (복구 모드에 의존)

QA 체크리스트

구현 전 확인

  • 프로젝트 키 / 세그먼트 키가 콘솔과 정확히 일치함 (콘솔에서 재확인)
  • AppDelegate에서 NetFUNNEL 에이전트가 올바르게 초기화됨
  • 필수 import 추가됨: Netfunnel_iOS
  • 모든 콜백 메서드 구현됨 (NetfunnelDelegate 요구 사항)

대기실 테스트 (진입 허용 수 = 0)

  • 진입 허용 수 = 0일 때 대기실 WebView가 올바르게 표시됨
  • 대기실이 올바른 세부 정보를 표시함:
    • 나의 대기 순서: 1
    • 예상 대기 시간: 00:00:00 (시:분:초)
    • 내 뒤의 대기자 수: 0
  • 대기 중에 Xcode 콘솔에 **[NF4] Sending reentry request**가 주기적으로 나타남
  • **[NF4] Received reentry response**가 **code=201 (WAIT)**을 표시함

진입 테스트 (진입 허용 수 = 1)

  • 진입 허용 수를 1로 변경할 때 onSuccess 델리게이트가 발생함
  • onSuccess 델리게이트가 원래 비즈니스 로직을 실행함
  • 진입 시 대기실이 즉시 사라짐
  • 대상 SwiftUI View로 네비게이션이 성공적으로 발생함

키 반환 확인

  • 완료 지점에서 키 반환이 올바르게 작동함
  • Xcode 콘솔에 **[NF4] Sending returnKey request**가 나타남
  • **[NF4] Sending returnKey request**가 HTTP 200과 함께 발생함
  • **[NF4] Received returnKey response**가 **code=200**을 표시함
  • 키 반환이 nfStart 호출당 정확히 한 번 발생함
  • 키 반환이 비즈니스 로직 완료 발생함

오류 처리

  • 모든 필수 상태에 대해 델리게이트 분기가 구현됨:
    • onSuccess - 원래 로직 실행
    • onError - 시스템 오류를 적절하게 처리 (비즈니스 로직 진행)
    • onNetworkError - 네트워크 문제를 적절하게 처리 (로그만)
    • onBlock - 차단된 상태를 적절하게 처리
    • onClose - 사용자 취소를 적절하게 처리
    • onContinue - 대기 진행을 적절하게 처리
  • AppDelegate 초기화에서 네트워크 복구 모드 활성화됨
  • 키 일치 - nfStartnfStop에서 projectKey/segmentKey가 동일함
  • 모든 델리게이트 응답에 대한 포괄적인 로깅 구현됨

Xcode 콘솔 확인

  • Xcode 콘솔에 NetFUNNEL 로그가 표시됨 ("NetFUNNEL"로 필터링)
  • 초기화 성공 메시지가 나타남
  • 버튼 클릭 시 Sending initialEntry request 메시지가 나타남
  • Fetching HTML content 메시지가 나타남
  • Sending returnKey request 메시지가 나타남
  • code=200과 함께 Received returnKey response가 나타남