포스트

Final (32)

프로젝트 막바지에는 기능 구현에 쫓겨 블로그 정리를 제대로 하지 못했다. 그래서 이 글은 당시 GitHub 커밋 내역과, 프로젝트 기간에 직접 적어 둔 업무 내역 목록을 AI(Claude Code)에게 보여주고 그걸 바탕으로 내용을 다시 정리했다. 코드는 실제 커밋에 있던 것을 가져왔다.

프로필 수정 페이지에 남아 있던 문제 두 가지를 고쳤다. 이 코드는 팀원(moremirim)이 7월 2일에 올려준 “공지사항의 FB연결, 프로필 수정관련 예외처리” 커밋에 같이 들어 있다.


프로필 수정 예외처리

남아 있던 문제는 이랬다.

  1. 닉네임은 그대로 두고 프로필 이미지만 바꾸려고 하면 저장이 안 됐다
  2. 지금 내가 쓰고 있는 닉네임으로 중복확인을 하면 “이미 닉네임이 존재합니다.” 가 떴다

2번은 당연한 결과였다. 전체 유저 목록에는 나 자신도 들어 있으니, 내 닉네임과 비교하면 항상 중복으로 나온다. 그리고 1번은 2번 때문에 생겼다. 저장하려면 중복확인을 통과해야 하는데, 닉네임을 그대로 두면 2번 때문에 통과를 할 수가 없었다.


내 닉네임은 중복에서 빼기

중복을 볼 때 닉네임이 같으면서 uid 가 나와 다른 사람만 중복으로 보도록 조건을 바꿨다. 빈 닉네임도 여기서 막는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@objc func validateName() {
    let manageManager = ManageManager()
    viewModel = ManageViewModel(manageManager: manageManager)

    guard let nickName = userNameTextField.text, !nickName.isEmpty else {
        isValidate = false
        validateLabel.textColor = .red
        validateLabel.text = "닉네임을 입력해주세요."
        return
    }

    guard let uid = Auth.auth().currentUser?.uid else { return }

    self.viewModel.getUsers { [weak self] in
        if let userArray = self?.viewModel.userArray, !userArray.contains(where: { $0.nickName == nickName && $0.uid != uid }) {
            self?.isValidate = true
            self?.validateLabel.textColor = .blue
            self?.validateLabel.text = "입력하신 닉네임은 사용 가능합니다."
        } else {
            self?.isValidate = false
            self?.validateLabel.textColor = .red
            self?.validateLabel.text = "이미 닉네임이 존재합니다."
        }
    }
}

이제 닉네임을 바꾸지 않아도 중복확인을 통과할 수 있어서, 이미지만 바꿔서 저장하는 것도 된다.

다만 구현 당시 컨디션이 좋지 않아서, 이미지를 바꿀 때도 중복확인 버튼을 한 번 누르고 저장하는 방식으로 마무리했다. 안내 문구에도 그걸 적어 두었다.

1
label.text = "닉네임 변경 전 중복확인 검사를 해주세요.\n프로필 이미지 변경시에도 중복확인 검사를 해주세요."

이미지를 받은 다음에 저장하기

26일 글에서 남겨 둔 문제도 여기서 같이 고쳤다. 이미지를 바꾸지 않았으면 기존 이미지를 Kingfisher 로 받아서 같이 저장하는데, 받기가 끝나기 전에 updateProfile 이 먼저 불리고 있었다.

그래서 저장 흐름을 함수 세 개로 나누고, 이미지를 받은 뒤에 다음 단계로 넘어가게 했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@objc func saveChanges() {
    guard let newUserName = userNameTextField.text, !newUserName.isEmpty else {
        showMessage(title: "닉네임을 입력해주세요", message: "닉네임을 입력해주세요.")
        return
    }

    guard isValidate else {
        showMessage(title: "중복확인을 해주세요", message: "닉네임 중복확인을 먼저 해주세요.")
        return
    }

    ProgressHUD.animate()
    guard let uid = Auth.auth().currentUser?.uid else { return }

    if profileImage == nil {
        KingfisherManager.shared.retrieveImage(with: URL(string: gotProfileImage!)!) { [weak self] result in
            switch result {
            case .success(let image):
                self?.validateAndUpdateProfile(uid: uid, userName: newUserName, selectedImage: image.image)
            case .failure(let error):
                ProgressHUD.dismiss()
                self?.showMessage(title: "에러 발생", message: "\(error)가 발생했습니다")
            }
        }
    } else {
        validateAndUpdateProfile(uid: uid, userName: newUserName, selectedImage: profileImage!)
    }
}
  1. saveChanges: 빈 닉네임과 중복확인 여부를 먼저 거른다. 새 이미지가 없으면 기존 이미지를 받고, 받기가 끝난 completion 안에서 다음 단계를 부른다
  2. validateAndUpdateProfile: 저장 직전에 한 번 더 중복을 확인한다. 중복확인을 누른 뒤 저장하기 전에 다른 사람이 같은 닉네임을 가져갔을 수도 있기 때문이다
  3. updateProfile: 실제로 저장하고 완료 Alert 를 띄운다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func validateAndUpdateProfile(uid: String, userName: String, selectedImage: UIImage) {
    let manageManager = ManageManager()
    viewModel = ManageViewModel(manageManager: manageManager)

    viewModel.getUsers { [weak self] in
        guard let self = self else { return }
        if let userArray = self.viewModel?.userArray {
            if userArray.contains(where: { $0.nickName == userName && $0.uid != uid }) {
                ProgressHUD.dismiss()
                self.showMessage(title: "닉네임 중복", message: "이미 존재하는 닉네임입니다.")
            } else {
                self.updateProfile(uid: uid, userName: userName, selectedImage: selectedImage)
            }
        } else {
            ProgressHUD.dismiss()
            self.showMessage(title: "에러 발생", message: "사용자 목록을 불러오는 데 실패했습니다.")
        }
    }
}

func updateProfile(uid: String, userName: String, selectedImage: UIImage) {
    userManager.updateProfile(uid: uid, nickName: userName, profile: selectedImage) { [weak self] result in
        switch result {
        case .success(()):
            ProgressHUD.dismiss()
            self?.showMessage(title: "수정 완료", message: "프로필 정보가 수정 되었습니다.") {
                self?.navigationController?.popViewController(animated: true)
            }
        case .failure(let error):
            ProgressHUD.dismiss()
            self?.showMessage(title: "에러 발생", message: "(error.localizedDescription)가 발생했습니다.")
        }
    }
}

마지막 에러 메시지는 \(error.localizedDescription) 에서 \ 가 빠져서, 에러 내용 대신 글자가 그대로 나온다. 지금 다시 보니 보이는 실수다.


같이 고친 것

닉네임을 고치면 isValidate 를 되돌리면서, 안내 문구도 처음 상태로 돌려놓았다. 확인을 통과한 뒤에 닉네임을 고쳤는데 “사용 가능합니다” 가 그대로 남아 있으면 헷갈리기 때문이다.

1
2
3
4
5
6
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    isValidate = false
    validateLabel.textColor = ThemeColor.mainBlack
    validateLabel.text = "닉네임 변경 전 중복확인 검사를 해주세요."
    return true
}

저장 버튼은 직접 만든 UIButton 대신 NavigationBar 의 기본 버튼(UIBarButtonItem)으로 바꿨다.

1
2
3
let saveBarButton = UIBarButtonItem(title: "저장", style: .done, target: self, action: #selector(saveChanges))
saveBarButton.tintColor = ThemeColor.mainOrange
navigationItem.rightBarButtonItem = saveBarButton

기존 프로필 이미지를 띄울 때 gotProfileImage! 로 강제 언래핑하던 것도 if let 으로 바꿨다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.