Swift Macro와 SwiftSyntax로 코드 자동 생성 및 분석하기
Swift Macro와 SwiftSyntax로 코드 자동 생성 및 분석하기
Swift 프로그래밍 언어는 그 성능과 사용 편의성으로 인해 많은 개발자들 사이에서 인기를 얻고 있습니다. Swift는 또한 새로운 기능들을 빠르게 도입하여 개발자들에게 더 많은 도구와 기능을 제공하고 있습니다. 그 중 하나가 바로 Macro와 SwiftSyntax입니다. 이 글에서는 Swift Macro
와 SwiftSyntax
를 활용하여 코드 자동 생성 및 분석을 어떻게 수행할 수 있는지에 대해 알아보겠습니다.
Swift Macro란?
Macro
는 반복적인 코드 생성을 자동화하는 기능입니다. 이는 코드 작성 속도를 크게 향상시킬 수 있으며, 코드의 일관성을 유지하는 데 도움이 됩니다. Swift에서 Macros는 강력한 기능을 제공하며, 이를 통해 코드 블록을 템플릿 형태로 만들고 필요한 곳에 삽입할 수 있습니다.
Macro 사용 예제
간단한 예제로, 로그 출력을 자동으로 추가하는 Macro를 만들어보겠습니다.
swift#define LOG_METHOD [NSLog(@"%s", __PRETTY_FUNCTION__)] void myFunction() { LOG_METHOD; // This will print the function name to the log // Rest of the code }
이 예제에서는 로그 출력을 위한 간단한 Macro
를 정의했습니다. 이제 함수 내에서 LOG_METHOD
를 호출하면 자동으로 해당 함수 이름이 로그에 출력됩니다.
SwiftSyntax란?
SwiftSyntax
는 Swift 코드를 구문 분석하고 변환하는 기능을 제공합니다. 이를 통해 코드 분석, 리팩토링, 그리고 도구 제작이 가능해집니다. SwiftSyntax는 Swift 코드를 구문 트리(Syntax Tree
)로 표현하며, 이 트리를 조작하여 코드를 수정하거나 새로운 코드를 삽입할 수 있습니다.
SwiftSyntax 예제
아래는 SwiftSyntax를 사용하여 Swift 코드 파일을 분석하고 함수 이름을 추출하는 예제입니다.
swiftimport SwiftSyntax class FunctionVisitor: SyntaxVisitor { var functions: [String] = [] override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind { if let identifier = node.identifier.tokenKind.text { functions.append(identifier) } return .skipChildren } } let url = URL(fileURLWithPath: "path/to/your/swiftfile.swift") let sourceFile = try SyntaxParser.parse(url) let visitor = FunctionVisitor() visitor.walk(sourceFile) print(visitor.functions)
이 예제에서는 FunctionVisitor
클래스를 만들어 함수 선언(FunctionDeclSyntax
) 노드를 방문하며, 해당 노드의 함수 이름을 추출하여 리스트에 저장합니다. 이후, 대상 Swift 파일을 파싱하고, 결과적으로 함수 이름 목록을 출력합니다.
실전 예제: Model 코드를 자동 생성하기
Swift Macro와 SwiftSyntax를 결합하여 실제 개발에 유용한 기능을 만들어 보겠습니다. 이번 예제에서는 주어진 JSON 구조에 따라 Swift 모델 코드를 자동 생성하는 예제를 다룹니다.
JSON 파일 예
json{ "name": "String", "age": "Int", "isActive": "Bool" }
위 JSON 파일에 대해 Swift 모델 코드를 생성할 수 있습니다.
모델 코드 자동 생성
swiftimport SwiftSyntax class ModelGenerator: SyntaxRewriter { var json: [String: String] init(json: [String: String]) { self.json = json } override func visit(_ node: SourceFileSyntax) -> Syntax { var properties: [DeclSyntax] = [] for (key, type) in json { let property = SyntaxFactory.makeVariableDecl( attributes: nil, modifiers: nil, letOrVarKeyword: SyntaxFactory.makeLetKeyword().withLeadingTrivia(.spaces(4)), bindings: SyntaxFactory.makePatternBindingList([ SyntaxFactory.makePatternBinding( pattern: SyntaxFactory.makeIdentifierPattern( identifier: SyntaxFactory.makeIdentifier(key)), typeAnnotation: SyntaxFactory.makeTypeAnnotation( colon: SyntaxFactory.makeColonToken(), type: SyntaxFactory.makeTypeIdentifier(type)), initializer: nil, accessor: nil, trailingComma: nil) ]) ) properties.append(property) } let modelStruct = SyntaxFactory.makeStructDecl( attributes: nil, modifiers: nil, structKeyword: SyntaxFactory.makeStructKeyword().withLeadingTrivia(.spaces(0)), identifier: SyntaxFactory.makeIdentifier("GeneratedModel"), genericParameterClause: nil, inheritanceClause: nil, genericWhereClause: nil, members: SyntaxFactory.makeMemberDeclBlock( leftBrace: SyntaxFactory.makeLeftBraceToken(), members: SyntaxFactory.makeMemberDeclList(properties), rightBrace: SyntaxFactory.makeRightBraceToken()) ) return SyntaxFactory.makeSourceFile( statements: SyntaxFactory.makeCodeBlockItemList([modelStruct]), eofToken: node.eofToken ) } } let json = ["name": "String", "age": "Int", "isActive": "Bool"] let generator = ModelGenerator(json: json) let sourceFile = SyntaxFactory.makeSourceFile let generatedCode = generator.visit(sourceFile) print(generatedCode)
이 예제는 JSON 파일의 구조에 맞춰 Swift 모델 코드를 자동 생성하는 내용입니다. ModelGenerator
클래스는 SyntaxRewriter
를 상속받아 visit
메소드를 재정의하여 모델의 속성을 생성하고, 이를 새로운 Swift 소스 파일로 반환합니다.
결론
Swift Macro와 SwiftSyntax는 개발자가 반복적인 작업을 자동화하고, 쉽게 코드를 분석 및 변환할 수 있도록 도와주는 강력한 도구입니다. 이 글에서는 두 가지 기능을 소개하고, 실제로 어떻게 활용할 수 있는지 예제를 통해 설명했습니다. 적절히 활용한다면 생산성을 크게 향상시킬 수 있는 기능들입니다.