팀원분이 addTarget
말고 addAction
으로 써보는게 어떠냐 라고 스쳐지나간 듯 들었던 이야기가 있었다.
그 당시 addAction
은 모르겠고 빨리 코드를 작성해야 하니 아는 코드로 하느라 addTarget
으로 그냥 작성해버렸는데..
addAction
은 Swift에서 UIButton
이나 UIControl
의 액션을 설정할 때 사용하는 메서드라고 한다.
위에서 내가 헷갈렸 듯 addTarget
과 유사한 역할을 하긴 하지만,
더 간결한 문법과 클로저 기반 방식을 지원한다는 점에서 차이가 있다고 한다.
addAction
?addAction
은 iOS 14 이상에서 사용할 수 있는 UIControl
메서드인데, 버튼이나 다른 UI 요소에 액션을 추가할 때 사용된다고 한다.
클로저 기반으로 동작하고, UIAction
객체를 전달받아 사용된다.
let button = UIButton()
button.addAction(UIAction { _ in
print("Button tapped!")
}, for: .touchUpInside)
addTarget
과의 차이점 분석 표특징 | addTarget(_:action:for:) | addAction(_:for:) |
---|---|---|
iOS 지원 버전 | iOS 2.0+ | iOS 14.0+ |
방식 | 타겟-액션 패턴 | 클로저 기반 UIAction 사용 |
사용법 | 메서드 호출 시, 객체와 셀렉터를 전달 | 클로저로 직접 동작 정의 |
가독성 | 코드가 다소 장황할 수 있음 | 더 간결하고 명확한 문법 제공 |
동작 | 대상 객체(target )이 필요함 | 클로저만으로 간단히 동작 정의 가능 |
addTarget
의 경우button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
// #selector 쓰기 귀찮았던 사람 손
@objc func buttonTapped() {
print("Button tapped!")
}
addAction
의 경우button.addAction(UIAction { _ in
print("Button tapped!")
}, for: .touchUpInside)
확실히 가독성 면에서 뛰어나고 불필요한 코드는 없앤 addAction이 더 좋아보인다.
addAction
을 사용하는 이유는 무엇이냐클로저 기반의 간결함
addTarget
은 메서드를 따로 정의해야 하지만, addAction
은 한 줄의 클로저로 동작을 정의할 수 있다.더 나은 가독성
타겟-액션 패턴의 한계 극복
addTarget
은 타겟 객체(target
)에 강하게 묶여 있지만, addAction
은 클로저를 사용하기 때문에 더 유연하게 활용할 수가있다.단순한 액션 : 버튼 하나의 간단한 동작을 정의할 때는 addAction
이 적합하다.
복잡한 로직 : 동작을 여러 메서드로 분리하거나 리팩토링이 필요한 경우에는 addTarget
을 사용할 수 있다.
addAction
에서 클로저의 매개변수로 이벤트를 받을 수 있다.
button.addAction(UIAction { action in
print("Button tapped at: \(action.timestamp)")
}, for: .touchUpInside)
여기서 action.timestamp
사용 했는데
action.timestamp
는 버튼이 눌렸을 때의 타임스탬프(시간)를 나타내준다.
이 값은 TimeInterval
타입(1970년 1월 1일 이후의 초)인데,
Date 객체로 변환하거나 디버깅용으로 바로 출력할 수 있는 코드다.
그리고 버튼이 .touchUpInside
이벤트를 받았을 때 클로저 내부 코드가 실행된다.
여러 이벤트에 대해 addAction
을 정의할 수 있다.
button.addAction(UIAction { _ in
print("Touch started")
}, for: .touchDown) // 사용자가 화면에 손가락을 올리기만 해도 이 이벤트가 트리거됨.
button.addAction(UIAction { _ in
print("Touch ended")
}, for: .touchUpInside) // 버튼의 영역 안에서 손을 떼야만 이 이벤트가 트리거됨.
아무래도 addAction
은 클로저를 활용하기 때문에, 클로저 안에 로직이 복잡해지면 가독성이 떨어질 수 있긴하다.
특히 클로저 내부에서 또 다른 클로저를 사용하거나, 중첩된 로직을 작성하게 되면 코드가 더 복잡하고 읽기 어려워질 가능성이 있으므로 그 부분을 유의하며 작성해야한다.
중첩된 클로저
Self 참조 문제
self
를 참조해야 할 경우, 메모리 누수를 신경 써야 한다.[weak self]
를 항상 고려해줘야하기 때문에 간단한 액션에서조차 추가적인 코드를 작성해야 할 필요가 생긴다.로직 분리의 어려움
addAction
이 복잡할 때의 예시를 보자button.addAction(UIAction { _ in
// 첫 번째 작업
fetchData { result in
// 두 번째 작업
processResult(result) { processedData in
// 세 번째 작업
print("Processed data: \(processedData)")
}
}
}, for: .touchUpInside)
위와 같은 경우 클로저가 계속 중첩되는 것을 보면, 역시나 코드가 복잡해지고 추적이 어렵다.
로직 분리
클로저 내부에서 모든 로직을 처리하지 말고, 별도의 메서드로 분리하면 가독성이 좋아진다.
button.addAction(UIAction { [weak self] _ in
self?.handleButtonTap()
}, for: .touchUpInside)
// 별도 메서드
func handleButtonTap() {
fetchData { result in
processResult(result) { processedData in
print("Processed data: \(processedData)")
}
}
}
addTarget
사용 고려
그래도 만약 클로저가 너무 복잡하다면.. addTarget
방식을 사용하는 것이 오히려 더 깔끔할 수 있다!
button.addTarget(self, action: #selector(handleButtonTap), for: .touchUpInside)
@objc func handleButtonTap() {
fetchData { result in
processResult(result) { processedData in
print("Processed data: \(processedData)")
}
}
}
클로저의 로직을 축소
가능하면 클로저 내부에서 간단한 작업만 수행하도록 설계하는 것이 좋다.
button.addAction(UIAction { _ in
print("Button tapped!")
}, for: .touchUpInside)
addAction
을 사용하는게 좋을 것 같다. 이유는 클로저 기반이라 더 짧고 명확한 코드 작성이 가능하기 때문이다.addTarget
방식을 사용하는 것이 더 적합할 수 있다.addAction
과 addTarget
은 상황에 따라 서로 보완적으로 사용할 수 있다는 걸 알았으니, 사용하게 된다면 적절한 선택을 하면 될 것 같다.
addAction
은 간결하고 클로저 기반으로, iOS 14 이상에서 사용하는 최신 방식이고, addTarget
은 여전히 유효해서 더 오래된 iOS 버전에서도 동작한다.만약 iOS 14 이상만 타겟으로 잡는다면 addAction
을 적극적으로 활용하는게 좋다고 한다.
iOS 14부터 addAction가 등장했는데, Apple이 권장하는 최신 설계 방식이라고 한다.
Apple은 최신 API를 점점 더 클로저 기반으로 설계하고 있다는데 아무래도 addAction을 사용하는게 Apple의 디자인 철학과 더 잘 맞는 앱을 개발할 수 있을 것 같다.
오늘 addAction과 addTarget에 대해 공부해봤다.
확실히 두 방식의 차이점과 장단점을 잘 이해할 수 있었던 것 같다.
내가 나중에 보려고 정리해두자면,
addTarget
addAction
어차피 대부분은 iOS 14 이상으로 만들다보니 addAction이 아무래도 간결하고 좀더 명확한 방식이어서 적극적으로 활용할 만할 것 같다.
그래도! 복잡한 로직이나 여러 버튼을 처리해야 할 때는 addTarget을 쓸 필요도 있다는 점을 기억해두자.
MVC 혹은 MVVM에서 일반적으로 button은 올바른 설계구조상 View 에 종속적인 관계이기 때문에, 순환참조가 발생 가능성은 낮습니다. 하지만 addAction을 호출한 View를 암시적으로 self 로 강참조하기에 인스턴스가 메모리에서 해제되지않을 수 있습니다.
좀 더 안전한 구조를 지향하면서, 편리성을 가져가고싶다면 꼭 weak가 아닌, 라이프사이클을 고려한 unowned 도 추천드립니다.
button.addAction(UIAction(handler: tappedKeyPadButton), for: .touchUpInside) 와 같이 함수를 변수로 전달할 수 있습니다
최.신.좋.아.