Swift Macros: 메타프로그래밍 실전 예제
Swift Macros 완전 가이드: Swift 6의 body 매크로(SE-0415)를 포함한 freestanding 및 attached 매크로 작성, swift-syntax를 활용한 AST 조작, 보일러플레이트를 줄이는 실전 예제 제공.

Swift Macros는 코드를 작성하고 유지 보수하는 방식을 근본적으로 바꿉니다. Swift 5.9에서 도입되어 Swift 6에서 크게 확장된 매크로는 정적 타입 안전성을 유지하면서 컴파일 타임에 코드를 생성할 수 있습니다. C 전처리기 매크로와 달리 Swift Macros는 타입 안전하고 컴파일러에 통합되어 있으며 Xcode의 "Expand Macro" 기능을 포함한 개발 도구에서 완벽하게 지원됩니다.
이 가이드는 기본 개념부터 고급 구현까지 Swift Macros 작성 과정을 처음부터 끝까지 다룹니다. Swift 6에서 도입된 body 매크로(SE-0415)를 포함하여 모든 예제에서 Swift 6과 swift-syntax 600.0.0 이상을 사용합니다.
Swift Macros의 종류 이해하기
Swift는 두 가지 주요 매크로 범주를 제공하며 각 범주는 서로 다른 사용 사례에 적합합니다. freestanding 매크로는 표현식이나 선언으로 단독 동작하고, attached 매크로는 기존 선언에 부착되어 이를 수정하거나 보완합니다.
freestanding 매크로: 표현식과 선언
freestanding 매크로는 # 기호로 시작하며 값을 반환(표현식)하거나 새로운 선언을 생성할 수 있습니다. 다음은 표현식 매크로의 구체적인 예시입니다.
// Freestanding expression macro - generates a value
let buildInfo = #buildDate
// Expansion → "2026-08-23 10:30:45"
// Freestanding macro with arguments
let message = #stringify(1 + 2)
// Expansion → "1 + 2 = 3"
// Freestanding declaration macro - creates declarations
#makeCase("success", "failure", "pending")
// Expansion →
// case success
// case failure
// case pending표현식과 선언의 핵심 차이는 결과에 있습니다. 표현식은 값을 만들어내고, 선언은 구조적 코드(타입, 함수, 변수)를 생성합니다.
attached 매크로: 여섯 가지 역할
attached 매크로는 @로 시작하여 선언 앞에 위치합니다. Swift 6에서는 이 매크로에 여섯 가지 역할을 정의하며, 함수 구현을 생성하는 새로운 body 역할도 포함됩니다.
// @attached(peer) - adds declarations at the same level
@AddAsync
func fetchUser(id: Int) -> User { ... }
// Expansion → adds func fetchUserAsync(id: Int) async -> User
// @attached(accessor) - adds getters/setters
@UserDefault("theme")
var currentTheme: String
// Expansion → adds get { UserDefaults.standard.string(...) }
// @attached(member) - adds members to a type
@AutoEquatable
struct Point {
var x: Int
var y: Int
}
// Expansion → adds static func == (lhs: Point, rhs: Point) -> Bool
// @attached(memberAttribute) - applies attributes to members
@CodableKeys
struct Config {
var apiUrl: String
var timeout: Int
}
// Expansion → adds @CodingKey("api_url") before apiUrl
// @attached(extension) - adds protocol conformances
@Hashable
struct User {
var id: Int
var name: String
}
// Expansion → adds extension User: Hashable { ... }
// @attached(body) - NEW in Swift 6: synthesizes function bodies
@Remote
func fetchData(endpoint: String) -> Data
// Expansion → generates the entire function implementation이 역할을 조합하면 코드를 동시에 여러 측면에서 변환하는 강력한 매크로를 만들 수 있습니다.
Swift 6 Body 매크로: SE-0415
Swift 6에서는 SE-0415를 통해 함수 본문 매크로가 도입되었습니다. 이를 통해 매크로가 함수의 구현을 합성하거나 대체할 수 있습니다. 기존의 매크로 역할로는 처리할 수 없었던 사용 사례에 대응합니다.
BodyMacro 프로토콜
BodyMacro 프로토콜은 함수 본문의 완전한 대체를 가능하게 합니다.
import SwiftSyntax
import SwiftSyntaxMacros
public protocol BodyMacro: AttachedMacro {
static func expansion(
of node: AttributeSyntax,
providingBodyFor declaration: some DeclSyntaxProtocol &
WithOptionalCodeBlockSyntax,
in context: some MacroExpansionContext
) throws -> [CodeBlockItemSyntax]
}매크로는 원래 선언을 받아 함수 본문이 될 코드 블록 항목을 반환합니다. 기존 본문이 있으면 대체됩니다.
원격 프로시저 호출 매크로 구현
body 매크로는 함수 시그니처에서 네트워크 레이어 코드를 생성할 때 특히 유용합니다.
import SwiftSyntax
import SwiftSyntaxMacros
public struct RemoteMacro: BodyMacro {
public static func expansion(
of node: AttributeSyntax,
providingBodyFor declaration: some DeclSyntaxProtocol &
WithOptionalCodeBlockSyntax,
in context: some MacroExpansionContext
) throws -> [CodeBlockItemSyntax] {
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
throw MacroError.invalidSyntax("@Remote requires a function")
}
let functionName = funcDecl.name.text
let parameters = funcDecl.signature.parameterClause.parameters
// Generate parameter encoding
let paramDict = parameters.map { param in
let name = param.firstName.text
return ""(name)": (name)"
}.joined(separator: ", ")
// Generate the RPC body
return [
"""
let params: [String: Any] = [(raw: paramDict)]
return try await RPCClient.shared.call(
method: "(raw: functionName)",
params: params
)
"""
]
}
}선언과 사용 방법
/// Generates a remote procedure call implementation
@attached(body)
public macro Remote() = #externalMacro(
module: "MyMacrosPlugin",
type: "RemoteMacro"
)class APIService {
@Remote
func createUser(name: String, email: String) async throws -> User
@Remote
func deleteUser(id: UUID) async throws -> Bool
// Each function body is generated at compile time:
// func createUser(name: String, email: String) async throws -> User {
// let params: [String: Any] = ["name": name, "email": email]
// return try await RPCClient.shared.call(
// method: "createUser",
// params: params
// )
// }
}하나의 함수에 적용할 수 있는 body 매크로는 최대 1개입니다. 로깅이나 트레이싱 같은 조합 가능한 프리앰블 주입에 대해서는 향후 Swift 버전에서 PreambleMacro 프로토콜이 검토되고 있습니다.
매크로를 만들기 위한 프로젝트 구성
Swift Macros를 만들려면 특정한 구조의 Swift Package가 필요합니다. 이 패키지는 Swift 코드를 추상 구문 트리(AST)로 다루는 공식 라이브러리 swift-syntax에 의존합니다.
Package.swift 구조
// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "MyMacros",
platforms: [.macOS(.v10_15), .iOS(.v13)],
products: [
// Library exposing macros to the main project
.library(
name: "MyMacros",
targets: ["MyMacros"]
),
// Executable for testing macros
.executable(
name: "MyMacrosClient",
targets: ["MyMacrosClient"]
)
],
dependencies: [
// Required dependency for macros - use 600.0.0+ for Swift 6
.package(
url: "https://github.com/swiftlang/swift-syntax.git",
from: "600.0.0"
)
],
targets: [
// Compiler plugin containing implementation
.macro(
name: "MyMacrosPlugin",
dependencies: [
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax")
]
),
// Target exposing macro declarations
.target(
name: "MyMacros",
dependencies: ["MyMacrosPlugin"]
),
// Test client
.executableTarget(
name: "MyMacrosClient",
dependencies: ["MyMacros"]
),
// Unit tests
.testTarget(
name: "MyMacrosTests",
dependencies: [
"MyMacrosPlugin",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax")
]
)
]
)이 구성은 매크로의 선언(클라이언트 코드가 보는 부분)과 구현(컴파일 시점에 실행되는 부분)을 명확히 분리합니다.
최소 세 개의 파일이 필요합니다. 선언을 위한 MyMacros.swift, 구현을 위한 MyMacrosPlugin.swift, 테스트를 위한 MyMacrosTests.swift입니다. 이러한 분리는 유지 보수를 한층 수월하게 만듭니다.
표현식 매크로 만들기
표현식 매크로는 코드에서 곧바로 사용할 수 있는 값을 생성합니다. 다음은 변수 이름을 포함한 사용자 정의 오류 메시지와 함께 옵셔널을 언래핑하는 #unwrap 매크로 예시입니다. Swift의 에러 처리 패턴에 대해서는 Swift structured concurrency 가이드도 참조하시기 바랍니다.
매크로 선언
import Foundation
/// Macro that unwraps an optional with an explicit error message
/// Usage: let value = #unwrap(optionalValue)
/// Expansion: guard let optionalValue else { fatalError("...") }; optionalValue
@freestanding(expression)
public macro unwrap<T>(_ value: T?) -> T = #externalMacro(
module: "MyMacrosPlugin",
type: "UnwrapMacro"
)시그니처는 매크로가 옵셔널을 받아 옵셔널이 아닌 값을 반환한다는 것을 나타냅니다. #externalMacro는 플러그인 안의 구현을 가리킵니다.
swift-syntax로 구현하기
import SwiftSyntax
import SwiftSyntaxMacros
import SwiftCompilerPlugin
public struct UnwrapMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
// Get the first argument passed to the macro
guard let argument = node.argumentList.first?.expression else {
throw MacroError.missingArgument
}
// Extract the variable name for the error message
let variableName = argument.description.trimmingCharacters(
in: .whitespacesAndNewlines
)
// Generate the expansion code
// Uses an immediately-invoked closure to encapsulate the guard
return """
{
guard let value = (argument) else {
fatalError("Failed to unwrap '\((literal: variableName))' - value was nil")
}
return value
}()
"""
}
}
// Custom errors for macros
enum MacroError: Error, CustomStringConvertible {
case missingArgument
case invalidSyntax(String)
var description: String {
switch self {
case .missingArgument:
return "The macro requires an argument"
case .invalidSyntax(let message):
return "Invalid syntax: (message)"
}
}
}expansion 메서드는 매크로 호출을 나타내는 AST 노드와 컴파일 컨텍스트를 받아 생성된 코드가 담긴 ExprSyntax를 반환합니다.
플러그인 등록
import SwiftCompilerPlugin
import SwiftSyntaxMacros
@main
struct MyMacrosPlugin: CompilerPlugin {
// List all macros provided by this plugin
let providingMacros: [Macro.Type] = [
UnwrapMacro.self,
RemoteMacro.self,
AutoInitMacro.self,
// Add other macros here
]
}이 진입점은 플러그인이 제공하는 매크로를 컴파일러에 알립니다.
iOS 면접 준비가 되셨나요?
인터랙티브 시뮬레이터, flashcards, 기술 테스트로 연습하세요.
attached의 member 매크로 만들기
member 매크로는 기존 타입에 멤버(프로퍼티, 메서드, 중첩 타입)를 추가합니다. 다음은 모든 저장 프로퍼티를 가진 이니셜라이저를 자동으로 생성하는 @AutoInit 매크로입니다.
선언과 구현 전체
/// Automatically generates an initializer with all stored properties
@attached(member, names: named(init))
public macro AutoInit() = #externalMacro(
module: "MyMacrosPlugin",
type: "AutoInitMacro"
)import SwiftSyntax
import SwiftSyntaxMacros
public struct AutoInitMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
// Verify the macro is applied to a struct or class
guard declaration.is(StructDeclSyntax.self) ||
declaration.is(ClassDeclSyntax.self) else {
throw MacroError.invalidSyntax(
"@AutoInit can only be applied to structs and classes"
)
}
// Collect stored properties
let properties = declaration.memberBlock.members
.compactMap { $0.decl.as(VariableDeclSyntax.self) }
.filter { isStoredProperty($0) }
// Generate initializer parameters
let parameters = properties.compactMap { property -> String? in
guard let binding = property.bindings.first,
let identifier = binding.pattern.as(IdentifierPatternSyntax.self),
let type = binding.typeAnnotation?.type else {
return nil
}
let name = identifier.identifier.text
let typeName = type.description.trimmingCharacters(in: .whitespaces)
// Check if the property has a default value
if binding.initializer != nil {
return "(name): (typeName) = (binding.initializer!.value)"
}
return "(name): (typeName)"
}
// Generate assignments in the init body
let assignments = properties.compactMap { property -> String? in
guard let binding = property.bindings.first,
let identifier = binding.pattern.as(IdentifierPatternSyntax.self) else {
return nil
}
let name = identifier.identifier.text
return "self.(name) = (name)"
}
// Build the complete initializer
let initDecl: DeclSyntax = """
public init((raw: parameters.joined(separator: ", "))) {
(raw: assignments.joined(separator: "
"))
}
"""
return [initDecl]
}
// Check if a variable is a stored property (not computed)
private static func isStoredProperty(_ variable: VariableDeclSyntax) -> Bool {
guard let binding = variable.bindings.first else { return false }
// A computed property has an accessor block with get/set
if let accessor = binding.accessorBlock {
// If it's a block with explicit accessors, it's computed
if accessor.accessors.is(AccessorDeclListSyntax.self) {
return false
}
}
// let or var without accessor = stored property
return true
}
}AutoInit 매크로 사용
@AutoInit
struct User {
let id: UUID
var name: String
var email: String
var isActive: Bool = true
}
// Automatically generated code:
// public init(id: UUID, name: String, email: String, isActive: Bool = true) {
// self.id = id
// self.name = name
// self.email = email
// self.isActive = isActive
// }
// Usage
let user = User(id: UUID(), name: "Alice", email: "alice@example.com")
// isActive uses the default value이 매크로는 이니셜라이저 보일러플레이트를 줄여 주므로, 프로퍼티가 많은 데이터 모델에서 특히 가치가 큽니다.
async 코드 생성을 위한 attached peer 매크로
peer 매크로는 어노테이션이 붙은 선언과 같은 수준에 새로운 선언을 추가합니다. 다음은 completion handler 기반 함수의 async 버전을 생성하는 @AddAsync 매크로입니다. 이 패턴은 레거시 네트워킹 코드를 마이그레이션할 때 특히 유용하며, Combine vs async/await 마이그레이션 가이드에서 자세히 다룹니다.
/// Automatically generates an async version of a function with completion handler
@attached(peer, names: suffixed(Async))
public macro AddAsync() = #externalMacro(
module: "MyMacrosPlugin",
type: "AddAsyncMacro"
)import SwiftSyntax
import SwiftSyntaxMacros
public struct AddAsyncMacro: PeerMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
// Verify it's a function
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
throw MacroError.invalidSyntax(
"@AddAsync requires a function"
)
}
let functionName = funcDecl.name.text
let asyncFunctionName = "(functionName)Async"
// Analyze parameters to find the completion handler
let parameters = funcDecl.signature.parameterClause.parameters
// Filter parameters (exclude completion handler)
var regularParams: [String] = []
var completionType: String? = nil
for param in parameters {
let paramType = param.type.description
// Detect a completion handler (closure with Result or simple value)
if paramType.contains("->") && paramType.contains("Void") {
// Extract the return type from completion
completionType = extractCompletionReturnType(from: paramType)
} else {
let paramName = param.firstName.text
let paramSecondName = param.secondName?.text
let label = paramSecondName ?? paramName
regularParams.append("(paramName): (paramType)")
}
}
guard let returnType = completionType else {
throw MacroError.invalidSyntax(
"No completion handler found"
)
}
// Generate arguments for internal call
let callArgs = parameters.dropLast().map { param in
let name = param.firstName.text
return "(name): (name)"
}.joined(separator: ", ")
// Generate the async function
let asyncFunc: DeclSyntax = """
func (raw: asyncFunctionName)((raw: regularParams.joined(separator: ", "))) async throws -> (raw: returnType) {
try await withCheckedThrowingContinuation { continuation in
(raw: functionName)((raw: callArgs.isEmpty ? "" : callArgs + ", ")completion: { result in
switch result {
case .success(let value):
continuation.resume(returning: value)
case .failure(let error):
continuation.resume(throwing: error)
}
})
}
}
"""
return [asyncFunc]
}
// Extract return type from a Result type
private static func extractCompletionReturnType(from type: String) -> String {
// Simplified pattern - in production, use the AST
if let match = type.range(of: #"Result<([^,]+)"#, options: .regularExpression) {
var result = String(type[match])
result = result.replacingOccurrences(of: "Result<", with: "")
return result.trimmingCharacters(in: .whitespaces)
}
return "Void"
}
}AddAsync 매크로 시연
class NetworkService {
@AddAsync
func fetchUser(
id: Int,
completion: @escaping (Result<User, Error>) -> Void
) {
// Implementation with callback
URLSession.shared.dataTask(with: URL(string: "/users/(id)")!) { data, _, error in
if let error = error {
completion(.failure(error))
} else if let data = data {
let user = try? JSONDecoder().decode(User.self, from: data)
completion(.success(user!))
}
}.resume()
}
// Automatically generates:
// func fetchUserAsync(id: Int) async throws -> User {
// try await withCheckedThrowingContinuation { continuation in
// fetchUser(id: id, completion: { result in
// switch result {
// case .success(let value):
// continuation.resume(returning: value)
// case .failure(let error):
// continuation.resume(throwing: error)
// }
// })
// }
// }
}
// Modern usage with async/await
let user = try await networkService.fetchUserAsync(id: 42)생성되는 함수의 이름은 @attached 속성의 names:에 선언해야 합니다. 여기서는 suffixed(Async)가 사용되어 원래 이름 뒤에 "Async" 접미사가 붙은 함수가 생성됨을 의미합니다.
매크로의 단위 테스트
매크로는 결과적으로 컴파일될 코드를 생성하므로 테스트가 필수입니다. Swift는 이런 테스트를 돕기 위해 SwiftSyntaxMacrosTestSupport를 제공합니다. swift-syntax 603.0.0 이상에서는 XCTest와 함께 Swift Testing도 지원됩니다.
import SwiftSyntaxMacros
import SwiftSyntaxMacrosTestSupport
import XCTest
@testable import MyMacrosPlugin
final class MyMacrosTests: XCTestCase {
// Dictionary of macros to test
let testMacros: [String: Macro.Type] = [
"unwrap": UnwrapMacro.self,
"AutoInit": AutoInitMacro.self,
"AddAsync": AddAsyncMacro.self,
"Remote": RemoteMacro.self
]
func testUnwrapMacroExpansion() throws {
assertMacroExpansion(
"""
let value = #unwrap(optionalString)
""",
expandedSource: """
let value = {
guard let value = optionalString else {
fatalError("Failed to unwrap 'optionalString' - value was nil")
}
return value
}()
""",
macros: testMacros
)
}
func testAutoInitMacroWithStruct() throws {
assertMacroExpansion(
"""
@AutoInit
struct Point {
let x: Int
var y: Int
}
""",
expandedSource: """
struct Point {
let x: Int
var y: Int
public init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
""",
macros: testMacros
)
}
func testAutoInitWithDefaultValues() throws {
assertMacroExpansion(
"""
@AutoInit
struct Config {
var timeout: Int = 30
var retryCount: Int
}
""",
expandedSource: """
struct Config {
var timeout: Int = 30
var retryCount: Int
public init(timeout: Int = 30, retryCount: Int) {
self.timeout = timeout
self.retryCount = retryCount
}
}
""",
macros: testMacros
)
}
func testAutoInitFailsOnEnum() throws {
assertMacroExpansion(
"""
@AutoInit
enum Status {
case active
}
""",
expandedSource: """
enum Status {
case active
}
""",
diagnostics: [
DiagnosticSpec(
message: "@AutoInit can only be applied to structs and classes",
line: 1,
column: 1
)
],
macros: testMacros
)
}
func testRemoteMacroExpansion() throws {
assertMacroExpansion(
"""
@Remote
func fetchUser(id: Int) async throws -> User
""",
expandedSource: """
func fetchUser(id: Int) async throws -> User {
let params: [String: Any] = ["id": id]
return try await RPCClient.shared.call(
method: "fetchUser",
params: params
)
}
""",
macros: testMacros
)
}
}이 테스트는 코드의 정확한 확장과 잘못된 사용에 대한 적절한 오류 메시지를 모두 검증합니다.
매크로 디버깅과 조사
Xcode는 매크로를 디버깅하고 생성된 코드를 이해할 수 있도록 여러 도구를 제공합니다.
Xcode에서의 확장
// Right-click on macro call → "Expand Macro"
// Displays generated code inline
@AutoInit
struct Product {
let id: UUID
var name: String
var price: Decimal
}
// To see the expansion:
// 1. Right-click on @AutoInit
// 2. Select "Expand Macro"
// 3. Generated code displays inline for inspection and debugging개발 중 로깅
public struct DebugMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
// Print the node's AST to understand the structure
print("=== DEBUG MACRO ===")
print("Node: (node)")
print("Arguments: (node.argumentList)")
// Complete dump of the syntax tree
dump(node)
// Continue with normal expansion
return "42"
}
}swift-ast-explorer로 AST 살펴보기
온라인 도구 swift-ast-explorer.com은 임의의 Swift 코드에 대한 구문 트리를 시각적으로 보여 줍니다. 매크로 구현 시 AST 노드를 어떻게 탐색해야 하는지 이해하는 데 매우 유용합니다. Swift에 내장된 테스트 매크로를 확인할 때도 도움이 되며, 이에 대해서는 Swift Testing 프레임워크 가이드에서 자세히 다룹니다.
Swift Macros 모범 사례
유지 보수가 가능한 매크로를 만들려면 몇 가지 관례를 지키고 흔히 발생하는 함정을 피해야 합니다.
검증과 오류 메시지
public struct ValidatedMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
// Validate usage context
guard declaration.is(StructDeclSyntax.self) else {
// Clear error messages with possible localization
context.diagnose(
Diagnostic(
node: node,
message: MacroDiagnosticMessage(
id: "invalid-target",
message: "This macro can only be applied to structs",
severity: .error
)
)
)
return []
}
// Check required arguments
guard let arguments = node.arguments else {
context.diagnose(
Diagnostic(
node: node,
message: MacroDiagnosticMessage(
id: "missing-args",
message: "Required arguments missing",
severity: .error
)
)
)
return []
}
// Implementation...
return []
}
}
// Structure for diagnostic messages
struct MacroDiagnosticMessage: DiagnosticMessage {
let id: String
let message: String
let severity: DiagnosticSeverity
var diagnosticID: MessageID {
MessageID(domain: "MyMacros", id: id)
}
}가독성 좋은 코드 생성
// Avoid: hard-to-read generated code
let badCode: DeclSyntax = "public init(a:Int,b:String,c:Bool){self.a=a;self.b=b;self.c=c}"
// Preferred: properly formatted generated code
let goodCode: DeclSyntax = """
public init(
a: Int,
b: String,
c: Bool
) {
self.a = a
self.b = b
self.c = c
}
"""생성된 코드는 "Expand Macro"를 통해 개발자가 직접 살펴보기 때문에 손으로 작성한 코드만큼 읽기 쉬워야 합니다.
소스
- SE-0415: Function Body Macros, Swift 6.0에서 구현
- swift-syntax releases, Swift 6 기준 600.0.0 이상 사용
- Swift.org Blog, Swift 6.3 릴리스 노트
Swift Macros로 가능한 것들
Swift Macros는 정적 타입 안전성을 유지하면서 보일러플레이트를 제거할 수 있는 강력한 도구입니다. 이 기술은 다음을 가능하게 합니다.
- 두 가지 카테고리: freestanding(
#)과 attached(@) - Swift 6의 여섯 가지 attached 역할: peer, accessor, member, memberAttribute, extension, 그리고 새로운 body 역할
- swift-syntax 600.0.0 이상과 AST 조작을 이용한 구현
SwiftSyntaxMacrosTestSupport를 활용한 필수 테스트(Swift Testing도 지원)- 구현을 위해 별도 패키지가 필요
- Xcode의 "Expand Macro"를 통한 디버깅
- 개발자 경험에 필수적인 명확한 오류 메시지
Swift Macros는 Equatable, Codable과 같은 conformance 생성, 고급 프로퍼티 래퍼 작성, 시그니처에서 RPC 구현 합성, 콜백 기반 API를 async/await으로 현대화하는 작업에 특히 유용합니다.
연습을 시작하세요!
면접 시뮬레이터와 기술 테스트로 지식을 테스트하세요.
iOS 코드의 버그를 찾을 수 있나요
실제 코드 한 조각, 숨은 버그 하나, 하루 한 번. 계정 없이 바로 도전할 수 있습니다.

작성자
Anthony Fillion-MailletSharpSkill 창업자
10년 이상 풀스택 개발을 해왔습니다. SharpSkill을 운영하며 이곳에 게시되는 모든 내용에 책임을 집니다.
2026년 8월 23일 업데이트
태그
공유
관련 기사

Swift에서 Combine vs async/await: 점진적 마이그레이션 패턴
Swift에서 Combine에서 async/await로 마이그레이션하는 완전한 가이드: 점진적 전략, 브리징 패턴, iOS 코드베이스의 패러다임 공존.

2026년 iOS 접근성 면접 질문: VoiceOver와 Dynamic Type
iOS 면접 대비를 위한 핵심 접근성 질문: VoiceOver, Dynamic Type, 시맨틱 traits, 접근성 감사.

StoreKit 2 인터뷰: 구독 관리 및 영수증 검증
StoreKit 2, 구독 관리, 영수증 검증, 인앱 구매 구현에 관한 iOS 인터뷰 질문을 실용적인 Swift 코드 예제와 함께 마스터하십시오.