이전글 : https://ios-dev-su.tistory.com/13
이번 포스팅에서는 코드와 구조도 개선하도록 하겠습니다.
- tagFieldChangedFromUser() 함수 개선
// 유저의 텍스트필드 입력에 따른 뷰모델 및 UI 동기화
func tagFieldChangedFromUser() {
if !tagFieldString.isEmpty {
let tags = tagFieldString
.components(separatedBy: ",")
.map{ $0.trimmingCharacters(in: .whitespaces) }
for i in 0..<recommendTagItems.count {
let idx = tags.firstIndex(of: recommendTagItems[i].tag ?? "") ?? -1
recommendTagItems[i].isSelected = (idx != -1)
}
}
updateRecommendTagItemSelectCount()
}
유저가 직접 tagFieldString을 변경했을 때, 추천 태그의 상태를 올바르게 변경하는 함수입니다.
개선 후 함수는 다음과 같습니다.
func updateRecommendTagSelectionFromUserTagFieldEditing() {
if !tagFieldString.isEmpty {
let tags = tagFieldString
.components(separatedBy: ",")
.map{ $0.trimmingCharacters(in: .whitespaces) }
for i in 0..<recommendTagItems.count {
guard let recommendTag = recommendTagItems[i].tag else { continue }
recommendTagItems[i].isSelected = tags.contains(recommendTag)
}
}
updateRecommendTagItemSelectCount()
}
1. 함수명 수정
함수의 역할을 보다 명확하게 설명하는 이름으로 변경했습니다.
updateRecommendTagSelectionFromUserTagFieldEditing = 유저 태그 필드 수정으로부터 추천 태그 선택을 업데이트한다
tagFieldChangedFromUser(유저로부터 태그 필드가 변경되었을 때..) 보다는 함수가 어떤 일을 하는지 쉽게 이해할 수 있습니다.
2. 스타일링, 주석제거
// 유저의 텍스트필드 입력에 따른 뷰모델 및 UI 동기화
이 주석은, 사실, 저 또는 이걸 보는 누군가가 이러한 정보가 필요하다고 생각해서 작성한 것입니다. 즉, 함수를 제대로 안짜고 주석으로 퉁칠려고 한 것입니다. 따라서 지워줍니다.
고차함수는 한 줄을 넘는 것보다 간략하게 표현할 수 있으면 그렇게 하는게 보기가 이쁘더라구요. 그래서 한 줄로 만들었습니다.
3. 로직 수정
recommend tag를 가져오고, 근데 없으면 빈 문자열로 퉁치고, 인덱스를 찾고, 만약 없으면 -1로 만들고 .. 이런건 알고리즘 풀이에서나 볼법한 쓰레기같은 코드입니다.
tags가 recommendTag를 포함하는지 확인하는 로직으로 수정했습니다.
- validateEmail
func validateEmail(text: String) -> Bool {
if text.isEmpty { return true }
let pattern = "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}\\@[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}(\\.[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25})+"
guard let _ = text.range(of: pattern, options: .regularExpression) else {
return false
}
return true
}
이 함수는 다음과같이 개선할 수 있습니다.
func checkEmailValidation(email: String) -> Bool {
if email.isEmpty { return true }
let pattern = "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}\\@[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}(\\.[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25})+"
if let _ = email.range(of: pattern, options: .regularExpression) {
return true
} else {
return false
}
}
1. 함수와 파라미터 이름을 보다 명확하게 수정했습니다.
2. guard문보다 if-let 문이 보다 읽기 자연스럽기에 if-let 으로 수정했습니다.
예를 들어, if let의 경우 "만약 이메일이 특정패턴에 속한다면 ..." 이라고 읽을 수 있습니다.
- 태그 아이템 관련 구조 개선
함수 수정은 여기까지 하고, 구조를 조금 고쳐보도록 하겠습니다.
일단 가장 꼴뵈기싫고 이상하게 보이는 부분은 여기일 것입니다. (전체 코드는 이전 포스팅에서 확인할 수 있습니다.)
뭔가 이상합니다.
가장 이상한 것은 저 태그들의 목록이 "세부사항"이 아니라, "정책"과 관련된 것인데, 대놓고 뷰모델에 있다는 것입니다.
따라서, 저 태그들의 목록을 포함하는 구조체를 만들고 거기에 담도록 하겠습니다.
1. JobTags, RecommendTags 구조체 생성
struct JobTags {
private(set) var tagList = ["제품 디자이너", "시각 디자이너", "UX 디자이너", "패션 디자이너", "3D 아티스트", "크리에이터", "일러스트레이터", "공예가", "화가"]
}
struct RecommendTags {
private(set) var tagList = ["제품", "공예", "그래픽", "회화", "UX", "UI", "모던", "클래식", "오브제", "감성적인", "심플", "귀여운", "키치한", "힙한", "레트로", "트렌디", "부드러운", "몽환적인", "실용적인", "빈티지", "화려한", "재활용", "친환경", "지속가능한", "밝은", "어두운", "차가운", "따뜻한"]
}
2. 태그들을 TagItem으로 바꿔 전달해주는 클래스 생성
final class TagItemConverter {
private let jobTags = JobTags().tagList
private let recommendTags = RecommendTags().tagList
static func getTagItems(from tagList: [String]) -> [TagItem] {
return tagList.map { TagItem(tag: $0, isSelected: false) }
}
func getJobTagItems() -> [TagItem] {
return TagItemConverter.getTagItems(from: jobTags)
}
func getRecommendTagItems() -> [TagItem] {
return TagItemConverter.getTagItems(from: recommendTags)
}
}
3. 수정사항 반영
// properties
private let tagItemConverter = TagItemConverter()
var categoryTagItems: [TagItem]
var recommendTagItems: [TagItem]
// init
self.categoryTagItems = tagItemConverter.getJobTagItems()
self.recommendTagItems = tagItemConverter.getRecommendTagItems()
4. 리리 팩토링
Converter는 태그를 아이템으로 "변환"해주는 함수이지 마이페이지 프로필 설정에 활용될 데이터를 바꿔주는 역할을 하는 것은 아닙니다.
따라서 Converter는 init함수에서 태그 리스트를 받고, 아이템을 반환하는 함수를 제공하는 클래스로 변경했습니다.
struct JobTags {
private init() {}
static var tagList = ["제품 디자이너", "시각 디자이너", "UX 디자이너", "패션 디자이너", "3D 아티스트", "크리에이터", "일러스트레이터", "공예가", "화가"]
}
struct RecommendTags {
private init() {}
static var tagList = ["제품", "공예", "그래픽", "회화", "UX", "UI", "모던", "클래식", "오브제", "감성적인", "심플", "귀여운", "키치한", "힙한", "레트로", "트렌디", "부드러운", "몽환적인", "실용적인", "빈티지", "화려한", "재활용", "친환경", "지속가능한", "밝은", "어두운", "차가운", "따뜻한"]
}
final class TagItemConverter {
private var tagList: [String]
init(tagList: [String]) {
self.tagList = tagList
}
func getTagItems() -> [TagItem] {
return convertTagItems()
}
private func convertTagItems() -> [TagItem] {
return tagList.map { TagItem(tag: $0, isSelected: false) }
}
}
// ViewModel 변경
var categoryTagItems: [TagItem]
var recommendTagItems: [TagItem]
init(user: PlainUser) {
self.user = user
let jobTagList = JobTags.tagList
let RecommendTagList = RecommendTags.tagList
self.categoryTagItems = TagItemConverter(tagList: jobTagList).getTagItems()
self.recommendTagItems = TagItemConverter(tagList: RecommendTagList).getTagItems()
setUpCategoryTagItems()
setUpRecommendTagItems()
downloadProfileImage()
downloadBackgroundImage()
}
5. TagItemConverter Test
포스팅이 길어져서 다음 포스팅에 이어서 작성하겠습니다.
'iOS > Refactoring' 카테고리의 다른 글
Domain + Presentation + MVVM 아키텍처 개선 (0) | 2024.08.09 |
---|---|
홈 화면 UX 개선하기 (리사이징, 메모리 캐싱) (0) | 2024.07.27 |
MyPageProfileEditViewModel 코드 개선하기 (1) (0) | 2024.07.13 |
MyPageNotificationViewModel 코드 개선하기 (0) | 2024.07.09 |
CollectionView Dynamic Header 개선하기 (SupplementaryView) (0) | 2024.06.29 |