UIKit에서 UIButton.tag
를 활용하여 버튼을 구분한 적이 있었는데, 코드리뷰를 통해 안티패턴임을 알게 되었다. 이 글에 tag
를 사용하는 것이 왜 문제가 되는지, 대안은 무엇이 있는지 기록한다.
UIButton.tag
란?
UIKit에서 tag
는 UIView의 속성으로, 개발자가 정수(Int) 값을 부여하여 특정 뷰를 식별할 수 있도록 설계되었다.
let button = UIButton()
button.tag = 1 // 특정 버튼을 식별하기 위한 tag 값 할당
tag
는 단순한 정수 값이므로 여러 개의 버튼을 쉽게 구분할 수 있지만, 이는 가독성과 유지보수성 측면에서 심각한 문제를 유발할 수 있다.
tag
를 사용하는 것이 안티패턴인 이유
매직 넘버(Magic Number) 문제
tag
값은 정수형이므로, 코드에서 해당 숫자가 무엇을 의미하는지 명확하지 않다.
@IBAction func buttonTapped(_ sender: UIButton) {
if sender.tag == 1 {
print("로그인 버튼 클릭")
} else if sender.tag == 2 {
print("회원가입 버튼 클릭")
}
}
위 코드에서 tag == 1
과 tag == 2
가 무엇을 의미하는지 즉시 이해하기 어렵다.
유지보수의 어려움
새로운 버튼이 추가되거나 변경되면 모든 관련 코드를 찾아서 수정해야 한다.
같은
tag
값을 실수로 여러 뷰에 설정하면 예상치 못한 버그가 발생할 수 있다.tag
값을 확인하려면 일일이 코드에서 해당 숫자가 의미하는 바를 찾아야 한다.
타입 안정성이 부족함
tag
는 단순히 Int
이므로, 버튼이 특정 기능과 연결된다는 명확한 보장이 없다.
if sender.tag == 1001 { // 이 값이 무엇을 의미하는지 불명확함
performAction()
}
Swift는 타입 안정성을 중요하게 여기는 언어이지만, tag
를 사용하면 Int
값만으로 UI 요소를 구별해야 하므로 타입 안정성이 깨진다.
tag
의 대안
버튼에 IBAction
직접 연결하기
Xcode의 Interface Builder에서 버튼마다 별도의
IBAction
을 연결하면 가독성이 높아진다.tag
가 없어도 어떤 버튼이 눌렸는지 명확하게 알 수 있다.
@IBAction func loginButtonTapped(_ sender: UIButton) {
print("로그인 버튼 클릭")
}
@IBAction func signUpButtonTapped(_ sender: UIButton) {
print("회원가입 버튼 클릭")
}
enum
과 switch
문 사용
UIButton
에tag
대신enum
을 활용하면 코드의 명확성이 증가한다.tag
를 그대로 사용하면서도 코드 가독성을 높일 수 있다.
enum ButtonType: Int {
case login = 1
case signUp = 2
}
@IBAction func buttonTapped(_ sender: UIButton) {
guard let type = ButtonType(rawValue: sender.tag) else { return }
switch type {
case .login:
print("로그인 버튼 클릭")
case .signUp:
print("회원가입 버튼 클릭")
}
}
UIButton에 식별자 속성 추가
tag
대신 UIButton에 직접 연관된 문자열 속성을 추가하여 사용할 수도 있다.tag
를 그대로 사용하면서도 코드 가독성을 높일 수 있다.
class IdentifiableButton: UIButton {
var identifier: String?
}
@IBAction func buttonTapped(_ sender: IdentifiableButton) {
if sender.identifier == "login" {
print("로그인 버튼 클릭")
} else if sender.identifier == "signUp" {
print("회원가입 버튼 클릭")
}
}