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("회원가입 버튼 클릭")
}
}