앱을 개발할 때 사용자에게 특정 상황을 알리기 위해 UIAlertController를 사용한다. 하지만 여러 곳에서 알럿을 호출하다 보면 코드가 중복되기 쉽고, 관리가 어려워진다. 이를 해결하기 위해 AlertManager를 활용하여 알럿을 관리하고, 최근에는 AlertMessage
enum을 따로 정의하여 더 깔끔한 구조로 리팩토링했다.
이번 글에서는 기존 코드의 문제점을 짚어보고, 개선해보자
기존에는 showImageLimitAlert()
, showEmptyTextAlert()
등 특정 상황마다 별도의 메서드를 만들었다.
// 기존 AlertManager의 알럿 메서드
func showImageLimitAlert() {
showAlert(title: "이미지를 추가할 수 없습니다.",
message: "하나의 글에 최대 한 개의 이미지만 추가할 수 있습니다.")
}
func showEmptyTextAlert() {
showAlert(title: "행복 기록이 없습니다",
message: "내용을 입력해주세요!")
}
이처럼 각 알럿마다 개별적인 메서드를 만들다 보니 코드가 비대해지고 유지보수가 어려웠다.
새로운 알럿이 추가될 때마다 새로운 메서드를 만들 필요 없이, 하나의 메서드에서 해결할 방법이 필요했다.
각 메서드에서 제목(title)과 메시지(message)를 직접 입력하는 방식이었다.
이렇게 되면 같은 메시지를 여러 곳에서 사용하더라도 수정이 필요할 때 일일이 찾아서 바꿔야 하는 불편함이 생긴다.
showAlert(title: "현재 카메라 사용에 대한 접근 권한이 없습니다.",
message: "설정 > Sodam 탭에서 접근 권한을 활성화 해주세요.")
앱 전체에서 일관된 메시지를 유지하려면, 알럿 메시지를 별도의 enum으로 분리하는 것이 더 좋은 방법이다.
중복된 알럿 메서드를 정리하고, 알럿 메시지를 AlertMessage
enum으로 통합했다.
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에서 일괄 관리하므로, 코드 가독성이 좋아지고 유지보수가 쉬워졌다.
✅ 같은 메시지를 여러 곳에서 사용할 수 있어 일관성을 유지할 수 있다.
이제 AlertManager
에서 중복된 개별 알럿 메서드를 제거하고, AlertMessage
를 활용하여 하나의 메서드에서 모든 알럿을 처리하도록 리팩토링했다.
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에서 가져오므로 코드가 더 깔끔해졌다.
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
에 추가만 하면 된다.
🚀 AlertManager가 더욱 재사용 가능하고 유지보수하기 쉬운 구조로 변경되었다.
🚀 코드 중복이 사라지고, 알럿 메시지가 한 곳에서 관리되어 일관성이 유지된다.
🚀 새로운 알럿 추가가 필요할 때도 간단하게 AlertMessage
에만 추가하면 된다.
이제 알럿을 더 효율적으로 관리할 수 있다.