Swift/개념 & 응용

[Swift] Enum을 이용한 ViewModel Action 정의

유정주 2023. 3. 14. 16:54
반응형

ViewModelAction의 필요성

MVVM 패턴에서 ViewModel은 비즈니스 로직을 정의합니다.

ViewController에서는 ViewModel 객체를 통해 비즈니스 로직을 수행하는 메서드를 호출하죠.

 

이때, ViewModel에서 수행하는 액션들을 Enum으로 묶으면,

ViewController가 ViewModel의 개별적인 메서드들을 직접 호출하는 것보다 명확하고 안전하게 메서드를 호출할 수 있습니다.

또한 개별 메서드를 private 접근제어자로 설정이 가능하여 캡슐화도 엄격해집니다.

 

그럼 이제 방법을 알아보도록 합시다.

 

Enum ViewModelActions 정의

ViewModel의 Action들을 묶은 Enum을 정의합시다.

enum ViewModelActions {
    case save(Int)
    case delete(Int)
    case deleteAll
}

저는 case 이름을 호출시킬 메서드명으로 설정했는데요.

꼭 이렇게 해야하는건 아니고, 프로젝트 전역적으로 일관적이게, 명확하게 설정하기만 하면 됩니다.

 

메서드에 전달할 파라미터들은 Enum의 연관값을 활용합니다.

save, delete를 저장하거나 삭제할 파라미터가 필요합니다.

그래서 Int형을 받는 연관값을 추가해서 호출하는 쪽에서 값을 넘길 수 있게 작성했습니다.

 

이제 이 ViewModelActions를 어떻게 사용하는지 알아봅시다.

 

ViewModelActions 사용

ViewModel 클래스를 정의했습니다.

final class ViewModel {
    func action(_ actions: ViewModelActions) {
        switch actions {
        case .save(let data):
            save(data)
        case .delete(let data):
            delete(data)
        case .deleteAll:
            deleteAll()
        }
    }
    
    private func save(_ data: Int) {
        print("Save \(data)")
    }
    
    private func delete(_ data: Int) {
        print("Delete \(data)")
    }
    
    private func deleteAll() {
        print("Delete All Data")
    }
}

위의 ViewModelActions도 클래스 멤버로 넣어도 괜찮습니다.

(여기서는 코드 길이때문에 보기 불편하실까봐 분리했어요.)

 

다음은 ViewController(혹은 ViewModel을 사용하는 곳)에서 사용하는 방법입니다.

let viewModel = ViewModel()

viewModel.action(.save(1))
viewModel.action(.delete(1))
viewModel.action(.deleteAll)

ViewModel 객체를 생성하고

action 메서드에 원하는 ViewModelActions을 전달하여 호출하면 됩니다.

Enum 자동완성 덕분에 ViewModel 액션 호출이 명확하고 안전해 집니다.

 

ViewModelAction 구조 장점

수월한 ViewModel 파악

action 메서드는 ViewController에서 사용하는 메서드입니다.

파라미터 타입을 ViewModelActions로 설정한 덕분에

ViewController에서는 ViewModel이 하는 일을 쉽게 파악할 수 있습니다.

 

또 코드를 파악할 때도 쉽습니다.

ViewModel 클래스 전체 코드를 보지 않아도

이 Enum 하나만 보면 ViewModel의 역할을 한 눈에 파악할 수 있습니다.

당장 이 예제만 봐도 ViewModel 클래스를 보지 않아도 save, delete, deleteAll 동작을 하겠구나! 라는걸 알 수 있죠.

 

추가로, 이것과 똑같은 동작을 하는 다른 ViewModel에서 이 Enum을 재활용할 수도 있습니다.

저장과 삭제는 충분히 다른 ViewModel에서도 수행될 수 있는 액션입니다.

따라서 위의 ViewModelActions Enum을 재활용해서 중복되는 코드를 줄일 수 있고,

네이밍도 적절히 해준다면 코드 파악도 쉽겠죠.

 

더 뛰어난 ViewModel 캡슐화, 은닉화

action 메서드에서는 파라미터로 전달받은 action 종류에 따라 적절한 메서드를 호출합니다.

이런 구조는 메서드들을 private로 선언할 수 있게 해서 ViewModel의 캡슐화, 은닉화 정도를 높입니다.

 

위 사진처럼 Xcode의 자동완성에서도 action만 보이기 때문에

ViewController에서도 메서드를 잘못 호출하는 휴먼오류를 줄일 수 있습니다.

 

더 명확한 역할 분리, 더 낮은 결합성

마지막으로 ViewController와 ViewModel의 역할이 명확히 구분되고 결합성이 낮아집니다.

 

예를 들어, ViewModel의 save 액션을 delete -> save  순서로 메서드를 호출하기로 변경했다고 해봅시다.

action 메서드로 구분하지 않았다면 ViewController 코드를

viewModel.delete(...)
viewModel.save(...)

이렇게 변경해야 합니다.

즉, ViewModel의 변경이 ViewController까지 영향을 줍니다.

 

save( ) 안에서 delete( )를 호출하면 안 되나? 라는 생각을 할 수 있지만

save의 역할이 불명확해지고, save만 필요한 다른 곳에서 save 메서드를 사용할 수 없게 됩니다.

(지금은 너무 간단한 코드라 조금 억지스러운 부분도 있지만, 실제 상황에서는 충분히 고려해야할 점입니다.)

 

이런 경우에 ViewModelActions를 이용한다면, ViewModel에서 어떤 케이스가 추가되더라도 

ViewController의 코드는 변경하지 않아도 됩니다.

왜냐하면 action 메서드 안의 내용을 바꾸면 되니까요.

 

이렇게 Action Enum을 사용하여 ViewController와 ViewModel의 결합성을 낮출 수 있습니다.

 

마무리

오늘은 Enum을 이용해 ViewModel Action을 정의하는 방법을 알아봤습니다.

 

저는 ViewModel과 ViewController의 결합성을 낮춘다는 점이 가장 매력적이었고,

가독성과 명확성을 높인다는 점도 매력적이었습니다.

 

MVVM 디자인 패턴을 사용 중이시라면 한 번 적용해보는 것도 좋은 경험이 되실겁니다.

 

감사합니다!

 

전체 코드

enum ViewModelActions {
    case save(Int)
    case delete(Int)
    case deleteAll
}

final class ViewModel {
    func action(_ actions: ViewModelActions) {
        switch actions {
        case .save(let data):
            save(data)
        case .delete(let data):
            delete(data)
        case .deleteAll:
            deleteAll()
        }
    }
    
    private func save(_ data: Int) {
        print("Save \(data)")
    }
    
    private func delete(_ data: Int) {
        print("Delete \(data)")
    }
    
    private func deleteAll() {
        print("Delete All Data")
    }
}

let viewModel = ViewModel()

viewModel.action(.save(1))
viewModel.action(.delete(1))
viewModel.action(.deleteAll)

아직은 초보 개발자입니다.

더 효율적인 코드 훈수 환영합니다!

공감 댓글 부탁드립니다.

 

 

반응형