배경
2022년 4월 경, 죽음의 minishell을 끝마친 나는 갑작스레 납치되어 Wordle의 한국어 버전 iOS 앱 <한들>의 제작에 참여하게 된다.
당시 뜨거운 인기를 끌었던 Wordle의 인기가 식기 전에 앱을 출시하는 게 목표였기에 우리는 2주 동안 미친 듯이 앱을 만들었고, 그 후 각자의 길을 걷느라 앱의 유지보수는 치명적인 버그를 수정하는 선에서만 해왔다.
그런데 이 내놓은 자식 <한들>이 미친 중독성을 바탕으로 대략 월 100달러 이상의 광고 수익을 꼬박꼬박 가져다주게 되자, 돈독이 오른 나는 한들을 만들어 놀고 먹자는 원래 목표를 위해 다시금 한들 개발에 뛰어들게 되는데...
문제상황
원래 기획사항이었지만 미뤄두었던 [푼 문제 다시보기] 기능을 만들기 위해 코드를 훑어보던 나는 그만 경악하고 만다.
당시 우리는 나름 최신 트렌드였던 SwiftUI와 MVVM을 이용해 재사용 가능하고 테스트 가능한 View 작성을 최우선 목적으로 두고 개발을 진행하였지만, 기한이 촉박하다 보니 기능 구현에만 신경을 쓸 수밖에 없었다.
그 결과,
모든
View가
하나의
ViewModel에
의존하고
있었다
린트마저 제정신 차리라고 비명을 지르고 있었다.
이 상태에서 기능을 더 갖다 붙였다간 우리의 소중한 자식 <한들>이 끔찍한 누더기 괴물이 될 것이 명확했기에 나는 리팩토링에 착수하기로 했다.
대체 어디서부터 손을 대야할 지 감이 안 와서 함부로 만질 수가 없었는데, SwiftUI의 장점을 십분 살려 일단 Action 없이 데이터만 필요한 View들부터 고쳐나가기로 했다.
먼저 리팩토링을 해야겠다고 마음먹게 만든 AnswerBoardView부터 고쳐보자.
ViewModel의 의존성을 해제하기 위해서 가장 먼저 해야하는 일은 ViewModel 변수를 주석처리 하는 것이다.
그러면 어디서 어떤 데이터를 필요로 하는지 XCode가 친절하게 알려준다.
위의 데이터들은 모두 ViewModel에 의존할 필요 없이 그냥 game의 데이터만 받아와도 문제없이 작동한다.
필요한 데이터들만 인자로 받도록 수정해주었다.
리팩토링 하는 김에 재사용하게 될 것 같은 컴포넌트들도 별도의 View로 분리하고 offset에 들어가는 변수도 정리하기로 했다.
그리한 결과는 다음과 같았다.
struct AnswerBoardView: View {
let answerBoard: [[Key]]
let currentColumn: Int
let currentRow: Int
let keyButtonWidth: Double = Double(uiSize.width - 40) / 6
var body: some View {
ZStack {
VStack(spacing: -2) {
Horline(height: 3)
.padding([.bottom], 5)
ForEach(answerBoard.indices, id: \.self) { rowIndex in
let row: [Key] = answerBoard[rowIndex]
AnswerBoardRow(row)
}
Horline(height: 3)
}
Image("Filcrow")
.resizable()
.frame(width: 12, height: 16)
.offset(x: filcrowOffsetX
y: filcrowOffsetY)
}
}
}
이제 위의 뷰는 MainViewModel에 의존하지 않고 인자로 들어온 데이터를 받아 화면에 표시할 뿐이다.
이와 같은 방법으로 복잡한 로직을 포함하지 않는 View들은 모두 MainViewModel을 Environment로 받아오지 않도록 변경해주었다.
등등...
그런데 이런식으로 View를 하나하나 만들어 나가다 보니, MainViewModel에 의존하고 있던 View들은 사실 Game이라는 모델의 데이터에 의존하고 있다는 사실을 깨닫게 되었다.
그렇다면 여기서 뷰모델의 존재 의미는 무엇일까? 단순한 로직의 분리를 위해? 근데 그럴 바엔 굳이 ViewModel을 생성하기 보다 각 기능별로 로직을 묶어서 함수로 작성하고 필요할 때 마다 호출해서 사용하는 것이 낫지 않을까?
어차피 Game은 ViewModel에서 Publish하건 View 내부에서 @State로 초기화하건 값이 바뀌면 거기에 연동되어 View가 다시 그려질텐데...
라는 생각을 하고 보니 MVVM 아키텍쳐를 SwiftUI에 그대로 적용하는 것은 뭔가 이상하다는 느낌이 들었다.
그리하여 나는 아예 아키텍쳐 자체를 뜯어고쳐볼 생각을 하게 되는데...
(다음 편에 계속...)
'iOS' 카테고리의 다른 글
[AVFoundation] observer로 AVPlayer의 상태 확인하기 (0) | 2023.02.22 |
---|---|
[SwiftUI] SwiftUI에서 AVPlayerLayer 사용하기 (0) | 2023.02.20 |
[RealmSwift] Realm에 기존에 사용중인 struct 저장 및 불러오기 (0) | 2022.05.08 |
[firebase] firestore에서 받은 데이터 쉽게 decode하기 (0) | 2022.03.27 |
[SwiftUI] List swipeAction을 활성화 한 상태에서 다른 뷰로 전환 시 리스트 멈춤 현상 (0) | 2022.02.22 |
댓글