9148c358d0
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를 Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류. - 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로 자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거, 동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거. - 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming, Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business, Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로, 나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는 title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백). 원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지. - 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서. - 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는 지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지. - Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경. - 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
7.1 KiB
7.1 KiB
id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
| id | title | category | status | source_trust_level | verification_status | created_at | updated_at | tags | tech_stack | applied_in | aliases | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ios-swift-macros-deep | Swift Macros — compile-time codegen | Coding | draft | B | conceptual | 2026-05-09 | 2026-05-09 |
|
|
|
Swift Macros
Swift 5.9+ 의 hygenic codegen. Freestanding (
#stringify(x)) + attached (@Observable). Boilerplate ↓.
📖 핵심 개념
- Compile-time expansion (no runtime).
- Hygenic (scope 격리).
- SwiftSyntax 가 backbone.
- Type-safe.
💻 코드 패턴
Freestanding macro
let (result, source) = #stringify(2 + 3)
print(result) // 5
print(source) // "(2 + 3)"
→ #stringify 가 expression + source string.
@Observable (Apple)
import Observation
@Observable
class UserModel {
var name: String = ""
var age: Int = 0
}
// Auto-generates:
// - Tracking infrastructure
// - withMutation calls
// - Observer registration
→ SwiftUI 가 자동 observe.
@Model (SwiftData)
import SwiftData
@Model
class Item {
var name: String
var quantity: Int
init(name: String, quantity: Int) {
self.name = name
self.quantity = quantity
}
}
// Auto-generates:
// - Persistence
// - Relationships
// - Identity
Custom macro 작성
// Package.swift
import CompilerPluginSupport
import PackageDescription
let package = Package(
name: "MyMacros",
platforms: [.macOS(.v13)],
products: [
.library(name: "MyMacros", targets: ["MyMacros"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0"),
],
targets: [
.macro(
name: "MyMacroPlugin",
dependencies: [
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
]
),
.target(name: "MyMacros", dependencies: ["MyMacroPlugin"]),
]
)
Stringify implementation
import SwiftSyntax
import SwiftSyntaxMacros
public struct StringifyMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
guard let arg = node.arguments.first?.expression else {
return ""
}
return "(\(arg), \(literal: arg.description))"
}
}
// MyMacros 모듈
@freestanding(expression)
public macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: "MyMacroPlugin", type: "StringifyMacro")
Attached macro
@AddInit
struct User {
let name: String
let age: Int
}
// Auto-generates:
// init(name: String, age: Int) {
// self.name = name
// self.age = age
// }
public struct AddInitMacro: MemberMacro {
public static func expansion(...) throws -> [DeclSyntax] {
// Generate init from struct properties.
}
}
@Test (Swift Testing)
import Testing
@Test
func addTwoNumbers() {
#expect(1 + 1 == 2)
}
@Test(arguments: [(1, 2, 3), (5, 5, 10)])
func add(_ a: Int, _ b: Int, _ expected: Int) {
#expect(a + b == expected)
}
→ XCTest 의 modern.
Use case
- @Observable: SwiftUI state.
- @Model: SwiftData persistence.
- @Test: testing.
- Custom: builder, Codable boilerplate, lens.
→ Boilerplate 가 큰 → macro 의 답.
Power 와 cost
+ Boilerplate 줄임.
+ Type-safe.
+ Compile-time error.
- Compile time ↑.
- Debug 어려움 (expanded code).
- Learning curve.
→ Library author 가 자주 작성.
App dev 가 사용 만 흔함.
Diagnostics
public static func expansion(...) throws -> [DeclSyntax] {
guard let structDecl = declaration.as(StructDeclSyntax.self) else {
throw MacroError.notStruct
}
if structDecl.members.isEmpty {
context.diagnose(Diagnostic(
node: declaration,
message: SimpleDiagnosticMessage(
message: "Struct has no members",
diagnosticID: .init(domain: "MyMacros", id: "empty"),
severity: .error
)
))
}
// ...
}
→ Compile error in Xcode.
Expanded code 보기
Xcode Editor → Right-click → Expand Macro.
또는:
swift -dump-ast file.swift
→ Generated code 검토.
Conformance macro
@AddCodable
struct User {
let name: String
}
// Generates: Codable conformance.
→ Boilerplate ↓ (Swift 가 자동 Codable 도 OK 가, custom logic 필요 시 macro).
Macro vs property wrapper
Property wrapper:
- Runtime.
- 작은 logic.
- 매 access 가 cost.
Macro:
- Compile-time.
- 큰 codegen.
- 0 runtime cost.
→ @Published (옛 Combine) → @Observable (modern macro).
Macro vs protocol extension
Protocol extension:
- 다이나믹 dispatch.
- 매 type 가 자체.
Macro:
- 정적 codegen.
- 정밀 control.
→ 매 use case 가 다름.
함정
- Compile time 폭발 (큰 macro).
- Diagnostics 가 confusing.
- Macro 의 변경 = 모든 user recompile.
- Test 가 어려움 (compile-time).
- IDE auto-complete 가 약함.
Production examples
- @Observable (Apple, Swift Observation framework).
- @Model (SwiftData).
- @Test (Swift Testing).
- @AsyncFailable (실험).
- TCA (The Composable Architecture)의 @Reducer.
Library author 의 use case
- Codable 보다 정밀 JSON.
- Protocol witness table.
- Dependency injection.
- DSL builder.
- Lens / optic.
Compile time
큰 macro = 큰 expansion = 큰 compile.
- 10 macro × 1000 line = 10000 line generated.
- Incremental compile 가 partial.
→ Profile + optimize.
Macro testing
import SwiftSyntaxMacrosTestSupport
func testStringify() {
assertMacroExpansion(
'''
#stringify(1 + 2)
''',
expandedSource: '''
(1 + 2, "1 + 2")
''',
macros: ['stringify': StringifyMacro.self]
)
}
→ Snapshot test.
vs Sourcery / GYB (옛)
Sourcery: 외부 tool, code generation.
GYB: Apple internal.
Swift Macros: native, type-safe.
→ Macro 가 modern.
Future
2026: Macros 가 mainstream.
- 더 많은 framework 가 macro.
- TCA / Vapor 가 macro 채택.
- Codegen ecosystem 발달.
🤔 의사결정 기준
| 작업 | 추천 |
|---|---|
| State management | @Observable |
| Persistence | @Model (SwiftData) |
| Testing | @Test |
| Boilerplate (init, codable) | Custom macro |
| 작은 codegen | Property wrapper |
| 외부 tool | Sourcery (legacy) |
❌ 안티패턴
- 모든 거 macro: compile time.
- Diagnostics 없음: bad UX.
- Test 없음: silent break.
- Macro 의 macro: complexity.
- Big logic in macro: compile slow.
🤖 LLM 활용 힌트
- Swift 5.9+ macro.
- Apple 의 @Observable / @Model / @Test.
- Library author 의 답.
- SwiftSyntax + SwiftCompilerPlugin.