본문 바로가기

개발

iOS Combine 으로 회원가입 로직 만들기

728x90

iOS Combine을 사용한 회원가입 로직 구현

Combine 프레임워크를 사용하여 iOS 앱에서 효율적이고 반응형 회원가입 로직을 구현하는 방법을 살펴보겠습니다.

1. 회원가입 양식 UI 구성

  • 사용자 이름, 이메일, 비밀번호 입력 필드를 포함하는 UI를 구성합니다.
  • 각 필드에 대한 입력 유효성 검사 로직을 구현합니다.

2. Combine을 사용한 입력 스트림 처리

  • 각 입력 필드에서 CurrentValueSubject를 사용하여 입력 값을 Observable로 변환합니다.
  • map 연산자를 사용하여 입력 값을 필요한 형식으로 변환합니다.
  • validate() 함수를 사용하여 입력 값의 유효성을 검사하고 오류 메시지를 생성합니다.

3. Combine을 사용한 회원가입 로직 구현

  • combineLatest 연산자를 사용하여 사용자 이름, 이메일, 비밀번호 Observable을 결합합니다.
  • flatMap 연산자를 사용하여 회원가입 API 호출을 수행합니다.
  • decode 연산자를 사용하여 API 응답 데이터를 디코딩합니다.
  • subscribe 메서드를 사용하여 응답 처리 및 결과 UI 업데이트를 수행합니다.

예시 코드

import SwiftUI
import Combine

struct SignupView: View {
    @State private var username = ""
    @State private var email = ""
    @State private var password = ""
    @State private var errorMessage = ""
    @State private var isLoading = false
    @State private var isRegistered = false

    private let disposeBag = DisposeBag()

    var body: some View {
        VStack {
            TextField("사용자 이름", text: $username)
                .padding()

            TextField("이메일", text: $email)
                .padding()

            SecureField("비밀번호", text: $password)
                .padding()

            Button("회원가입") {
                isLoading = true

                let input = Publishers.CombineLatest(
                    usernamePublisher,
                    emailPublisher,
                    passwordPublisher
                )
                .map { username, email, password in
                    (username, email, password)
                }
                .validate()
                .flatMap { userData in
                    signupAPI(userData: userData)
                }
                .decode(type: SignupResponse.self)
                .map { response in
                    response.isSuccess
                }
                .subscribe(on: DispatchQueue.main) { [weak self] success in
                    guard let self = self else { return }

                    self->isLoading = false

                    if success {
                        self->isRegistered = true
                    } else {
                        self->errorMessage = "회원가입에 실패했습니다."
                    }
                }
            }
            .padding()

            if isRegistered {
                Text("회원가입이 완료되었습니다.")
            } else if !errorMessage.isEmpty {
                Text(errorMessage)
                    .foregroundColor(.red)
            }

            if isLoading {
                ProgressView()
            }
        }
        .padding()
    }

    private var usernamePublisher: AnyPublisher<String, Never> {
        CurrentValueSubject(username).eraseToAnyPublisher()
    }

    private var emailPublisher: AnyPublisher<String, Never> {
        CurrentValueSubject(email).eraseToAnyPublisher()
    }

    private var passwordPublisher: AnyPublisher<String, Never> {
        CurrentValueSubject(password).eraseToAnyPublisher()
    }
}

private struct SignupResponse: Decodable {
    let isSuccess: Bool
}

private func signupAPI(userData: (String, String, String)) -> AnyPublisher<SignupResponse, Error> {
    // 실제 API 호출 코드를 작성합니다.
    return Just((isSuccess: true)).setFailureType(to: Error.self).eraseToAnyPublisher()
}

private extension Publishers {
    static func validate<T: Combine.CombinePublisher>(
        _ publisher: T,
        errorPrefix: String = "",
        validations: [(_ value: T.Value) -> String?] = []
    ) -> AnyPublisher<T.Value, Error> {
        publisher
            .flatMap { value in
                validations.reduce(Optional.none) { result, validation in
                    result.orElse(validation(value)) {
                        Error.init(message: errorPrefix + validation(value)!)
                    }
                }
            }
            .eraseToAnyPublisher()
    }
}

설명:

  • 1. UI 구성:

사용자 이름, 이메일, 비밀번호 입력 필드와 회원가입 버튼을 포함하는 UI를 구성합니다.
각 필드에 대한 입력 유효성 검사 로직을 구현합니다.

  • 2. 입력 스트림 처리:

CurrentValueSubject를 사용하여 각 입력 필드의 값을 Observable로 변환합니다.
map연산자를 사용하여 입력 값을 필요한 형식으로 변환합니다.
validate()함수를 사용하여 입력 값의 유효성을 검사하고 오류 메시지를 생성합니다.

  • 3. 회원가입 로직:

combineLatest 연산자를 사용하여 사용자 이름, 이메일, 비밀번호 Observable을 결합합니다.
flatMap연산자를 사용하여 회원가입 API 호출을 수행합니다.
decode연산자를 사용하여 API 응답 데이터를 디코딩합니다.
subscribe메서드를 사용하여 응답 처리 및 결과 UI 업데이트를 수행합니다.