Swift에서 AlertManager 리팩토링하기

sonny·6일 전
0

TIL

목록 보기
127/133

앱을 개발할 때 사용자에게 특정 상황을 알리기 위해 UIAlertController를 사용한다. 하지만 여러 곳에서 알럿을 호출하다 보면 코드가 중복되기 쉽고, 관리가 어려워진다. 이를 해결하기 위해 AlertManager를 활용하여 알럿을 관리하고, 최근에는 AlertMessage enum을 따로 정의하여 더 깔끔한 구조로 리팩토링했다.

이번 글에서는 기존 코드의 문제점을 짚어보고, 개선해보자


1. 기존 코드의 문제점

1️⃣ 중복되는 알럿 메서드

기존에는 showImageLimitAlert(), showEmptyTextAlert() 등 특정 상황마다 별도의 메서드를 만들었다.

// 기존 AlertManager의 알럿 메서드
func showImageLimitAlert() {
    showAlert(title: "이미지를 추가할 수 없습니다.", 
              message: "하나의 글에 최대 한 개의 이미지만 추가할 수 있습니다.")
}

func showEmptyTextAlert() {
    showAlert(title: "행복 기록이 없습니다", 
              message: "내용을 입력해주세요!")
}

이처럼 각 알럿마다 개별적인 메서드를 만들다 보니 코드가 비대해지고 유지보수가 어려웠다.
새로운 알럿이 추가될 때마다 새로운 메서드를 만들 필요 없이, 하나의 메서드에서 해결할 방법이 필요했다.


2️⃣ 하드코딩된 알럿 메시지

각 메서드에서 제목(title)과 메시지(message)를 직접 입력하는 방식이었다.
이렇게 되면 같은 메시지를 여러 곳에서 사용하더라도 수정이 필요할 때 일일이 찾아서 바꿔야 하는 불편함이 생긴다.

showAlert(title: "현재 카메라 사용에 대한 접근 권한이 없습니다.", 
          message: "설정 > Sodam 탭에서 접근 권한을 활성화 해주세요.")

앱 전체에서 일관된 메시지를 유지하려면, 알럿 메시지를 별도의 enum으로 분리하는 것이 더 좋은 방법이다.


2. AlertMessage Enum으로 메시지 관리하기

중복된 알럿 메서드를 정리하고, 알럿 메시지를 AlertMessage enum으로 통합했다.

🛠 AlertTypes.swift

enum AlertMessage {
    case emptyText
    case imageLimit
    case alreadyWrittenToday
    case writeCompleted
    case cameraPermission
    case imagePermission

    var title: String {
        switch self {
        case .emptyText:
            return "행복 기록이 없습니다"
        case .imageLimit:
            return "이미지를 추가할 수 없습니다."
        case .alreadyWrittenToday:
            return "오늘의 소확행 작성 완료!"
        case .writeCompleted:
            return "작성 완료"
        case .cameraPermission:
            return "현재 카메라 사용에 대한 접근 권한이 없습니다."
        case .imagePermission:
            return "현재 사진 라이브러리 접근에 대한 권한이 없습니다."
        }
    }

    var message: String {
        switch self {
        case .emptyText:
            return "내용을 입력해주세요!"
        case .imageLimit:
            return "하나의 글에 최대 한 개의 이미지만 \n 추가할 수 있습니다."
        case .alreadyWrittenToday:
            return "내일 또 당신의 소소한 행복을 작성해주세요"
        case .writeCompleted:
            return "글이 성공적으로 작성되었습니다!"
        case .cameraPermission, .imagePermission:
            return "설정 > Sodam 탭에서 접근 권한을 활성화 해주세요."
        }
    }
}

알럿 메시지를 AlertMessage enum에서 일괄 관리하므로, 코드 가독성이 좋아지고 유지보수가 쉬워졌다.
같은 메시지를 여러 곳에서 사용할 수 있어 일관성을 유지할 수 있다.


3. AlertManager 리팩토링

이제 AlertManager에서 중복된 개별 알럿 메서드를 제거하고, AlertMessage를 활용하여 하나의 메서드에서 모든 알럿을 처리하도록 리팩토링했다.

🛠 변경 후 AlertManager.swift

final class AlertManager {
    
    private weak var viewController: UIViewController?
    
    init(viewController: UIViewController) {
        self.viewController = viewController
    }
    
    /// 알럿 표시 메서드
    func showAlert(alertMessage: AlertMessage, actions: [UIAlertAction] = []) {
        let alertController = UIAlertController(
            title: alertMessage.title,
            message: alertMessage.message,
            preferredStyle: .alert
        )
        
        if actions.isEmpty {
            alertController.addAction(UIAlertAction(title: "확인", style: .default))
        } else {
            actions.forEach { alertController.addAction($0) }
        }
        
        viewController?.present(alertController, animated: true)
    }
}

이제 showAlert(alertMessage:) 하나의 메서드에서 모든 알럿을 처리할 수 있다.
각 알럿의 메시지는 AlertMessage enum에서 가져오므로 코드가 더 깔끔해졌다.


4. 적용 코드 변경

📌 openCamera() (카메라 접근)

@objc private func openCamera() {
    guard writeViewModel.imageCount < 1 else {
        alertManager.showAlert(alertMessage: .imageLimit)
        return
    }

    cameraPickerService.requestAccess(self) { [weak self] isGranted in
        guard let self = self else { return }
        if isGranted {
            cameraPickerService.show(self)
        } else {
            self.alertManager.showGoToSettingsAlert(for: .camera)
        }
    }
}

📌 addImage() (이미지 추가)

@objc private func addImage() {
    guard writeViewModel.imageCount < 1 else {
        alertManager.showAlert(alertMessage: .imageLimit)
        return
    }

    photoLibraryPickerService.requestAccess(self) { [weak self] isGranted in
        guard let self = self else { return }
        if isGranted {
            photoLibraryPickerService.show(self)
        } else {
            self.alertManager.showGoToSettingsAlert(for: .image)
        }
    }
}

📌 submitText() (작성 완료)

@objc private func submitText() {
    guard !writeView.getTextViewText().trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
        alertManager.showAlert(alertMessage: .emptyText)
        return
    }
}

각 메서드에서 alertManager.showAlert(alertMessage:)만 호출하면 되므로 코드가 훨씬 단순해졌다.
새로운 알럿이 필요할 경우 AlertMessage에 추가만 하면 된다.


5. 리팩토링 결과

🚀 AlertManager가 더욱 재사용 가능하고 유지보수하기 쉬운 구조로 변경되었다.
🚀 코드 중복이 사라지고, 알럿 메시지가 한 곳에서 관리되어 일관성이 유지된다.
🚀 새로운 알럿 추가가 필요할 때도 간단하게 AlertMessage에만 추가하면 된다.

이제 알럿을 더 효율적으로 관리할 수 있다.

profile
iOS 좋아. swift 좋아.

0개의 댓글

관련 채용 정보