지난번 TCA를 분석하기위해 코드 한줄한줄 분석하는 작업을 진행했는데
@Perception.Bindable
위의 구문을 공부하다가 해당 내용이 너무 깊어져서 혼자 공부하다 하루를 보냈다 보기에는 간단한? 한줄이지만 내부에는 너무나도 많은 Swift의 신기술이 들어가있다 그중에서도 아 공부해야지 하면서도 공부를 못했던 WWDC 2023에서 새로나온 기술 Macro를 챙기지 않고서는 TCA에 대한 공부를 더이상 진행을 할수 없을 것 같아 오늘은 TCA를 분석하기전에 Macro에 대해 자세히 알아보고 지나가려고한다.
여러가지 참고자료들을 공부했지만 아래의 블로그의 글이 가장 잘 설명이 되어있고 발표영상도 너무나도 대단하여 해당 영상을 2번이상은 본 것 같다.
https://sujinnaljin.medium.com/swift-%EB%A7%A4%ED%81%AC%EB%A1%9C-5e232b78dc5b
[Swift] 매크로
매크로요..? 수강 신청 매크로 말고요..????
sujinnaljin.medium.com
Swift에서 Macro가 무엇일까?
사실 많은 개발자들은 이미 매크로와 비슷한 개념을 이용하고있었습니다.
아래의 예시 구조체가 있습니다.
struct TestModel: Codable {
var A: String
}
struct TestModel: Codable {
var A: String
// 자동 생성되는 CodingKeys enum
private enum CodingKeys: String, CodingKey {
case A
}
// 자동 생성되는 디코딩 초기화 메서드
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
A = try container.decode(String.self, forKey: .A)
}
// 자동 생성되는 인코딩 메서드
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(A, forKey: .A)
}
}
이렇게 Codable을 실제 프로젝트는 위에서 처럼 단순히 Codable을 채택을 한거 뿐이지만
실제 프로젝트에서는 아래의 코드들이 자동으로 생성이 되게 됩니다.
Macro에 대해 이제 다 알았어요!! 아 특정 개념을 도입하면 자동으로 거기에 맞는 인터페이스에 따라 코드를 자동으로 생성해주는 구나
쉽네 끝 이라고하면 정말 좋겠지만 이제 그냥 Macro의 가장 큰 틀을 알게 된 것 뿐이에요
그럼 Macro는 무엇일까? 그건 위의 예시의 Codable처럼 자동으로 생성되는 코드들을 개발자들이 직접 정의해서 만들수 있도록 열어준 기능이다 라고 생각하면 될 것 같습니다.
에플은 Macro를 이야기하면서 아래의 원칙들을 강조했는데
- 안전성(Safety) - 매크로는 컴파일 시간에 검증되며, 타입 안전성을 유지하고 런타임 오류를 줄이도록 설계되었습니다.
- 투명성(Transparency) - 매크로가 생성하는 코드는 개발자가 검사하고 이해할 수 있어야 합니다. 이를 위해 매크로 확장을 볼 수 있는 기능을 제공합니다.
- 명확성(Clarity) - 매크로 사용 시 무엇이 일어나는지 명확해야 하며, "마법"같이 느껴지지 않아야 합니다.
- 강력함(Power) - 매크로는 실용적인 문제를 해결할 수 있을 만큼 충분히 강력해야 합니다.
정말 기본에 충실하면서 가장 중요한 원칙들을 모두 따라야한다는 사실을 알 수 있습니다.
안정성을 뛰어야하며 코드가 숨겨지지 않아야 하며 명확하게 무엇인지 알려줘야하지만 그 효과는 강력해야한다는
설명만 들어보면 정말 엄청나다 라고 생각이 들수밖에없습니다.
위의 개념을 기본으로 나진님이 작성해주신 블로그글을 한번더 보게되면 사용방법이나 어떻게 설계를 진행해야하는지등을 이해할수있습니다.
그래서 다시 본론으로 돌아와서 TCA에서도 코드의 중복을 줄이면서 안정적이면서 투명한 명확한 인터페이스들을 제공하게 되는데 그중 하나가 위에 이야기한 @Perception.Bindable 입니다.
그럼 @Perception.Bindable 을 분석해보겠습니다.
@Perception.Bindable의 기능
- 양방향 바인딩 지원: SwiftUI의 @Binding 속성과 유사하게 TCA의 상태 모델에서 양방향 데이터 바인딩을 가능하게 합니다.
- 보일러플레이트 코드 감소: 상태 변경을 처리하는 액션과 리듀서 로직을 자동으로 생성합니다.
- 타입 안전성 보장: 컴파일 시점에 코드를 생성하므로 런타임 오류 가능성을 줄입니다.
기본적으로 SwiftUI에서 사용하던 Binding의 기능을 TCA에서도 사용할수 있도록 해주는 매크로(없었다면 코드가 엄청 많이 생성이 되고 개발자에게 제공이 어려운수준까지 될확률이 높았다) 입니다.
SwiftUI에서 제공하는 많은 컴포넌트들에서 필요한 @Binding이 필요한 기능들에 대응을 할수있다.
그런데 @Perception.Bindable이 MVI 설계에 맞을까?
개인적인 생각인데 TCA와 같은 MVI 아키텍처가 과연 SwiftUI와 적합한지는 의문이 된다
MVI아키텍처의 근본적인 목적은 State는 모두 Action을 통해 들어와 mutate 단계에서 처리가 되는게 목표인데 이미 SwiftUI는 양방향 바인딩을 기본으로 생각하고 컴포넌트가 생성되고있는데 이 근본적인 간격이 과연 어떻게 매꿔질수있을지가 좀 의문이긴하다.
만약 나였다면 저위의 코드를 어떻게 사용을 하였을까?
struct RootView: View {
var store: StoreOf<RootDomain>
var body: some View {
WithPerceptionTracking {
TabView(
selection: .init(get: {
store.selectedTab
}, set: { tab in
store.send(.tabSelected(tab))
})
) {
ProductListView(
store: self.store.scope(
state: \.productListState,
action: \.productList
)
)
.tabItem {
Image(systemName: "list.bullet")
Text("Products")
}
.tag(RootDomain.Tab.products)
ProfileView(
store: self.store.scope(
state: \.profileState,
action: \.profile
)
)
.tabItem {
Image(systemName: "person.fill")
Text("Profile")
}
.tag(RootDomain.Tab.profile)
}
}
}
}
오히려 위의 코드가 좀 더 MVI 스럽지 않을까 라는 생각을 하였다
@Perception.Bindable을 통해 코드를 자동으로 Bindable하게 만들어주는것은 정말 좋지만 그건 너무 오버스팩이 아닌가 라는 생각을 하였다.
오늘도 여기까지만 분석을 한동안은 TCA / Macro 집중적으로 분석을 진행할 것 같다.
'SwiftUI' 카테고리의 다른 글
SwiftUI 공부 with TCA (0) | 2025.04.06 |
---|---|
swift 인-아웃 파라미터 (In-Out Parameters) (0) | 2021.06.28 |
앱스토어 등록시 전화 번호 : 유효하지 않은 필드입니다. (8) | 2021.01.26 |
Geometryreader란? Geometryreader 사용하기 (0) | 2021.01.20 |
5392b68f539b489193e4508c39687898 (0) | 2020.12.27 |