🔥 매크로 개발과 디버깅
434자
5분
매크로는 외부 상태에 의존하지 않고 AST(Abstract Syntax Tree)를 변환하기 때문에 테스트를 통한 개발에 매우 적합합니다. 게다가 문자열 리터럴로부터 구문 노드를 만들 수 있어서 테스트를 위한 입력 설정이 간단해집니다. 또한 AST의 description
속성을 읽어서 예상 값과 비교할 문자열을 얻을 수 있죠.
이전 섹션에서 다뤘던 #fourCharacterCode
매크로의 테스트 예제를 살펴봅시다:
let source: SourceFileSyntax = """ let abcd = #fourCharacterCode("ABCD") """ // 테스트할 소스 코드를 문자열 리터럴로 정의합니다. let file = BasicMacroExpansionContext.KnownSourceFile( moduleName: "MyModule", fullFilePath: "test.swift" ) // 소스 파일에 대한 정보를 담은 KnownSourceFile 객체를 생성합니다. let context = BasicMacroExpansionContext(sourceFiles: [source: file]) // 매크로 확장 컨텍스트를 생성하고, 테스트할 소스 파일을 등록합니다. let transformedSF = source.expand( macros:["fourCharacterCode": FourCharacterCode.self], in: context ) // 매크로를 확장하여 변환된 소스 파일을 얻습니다. let expectedDescription = """ let abcd = 1145258561 as UInt32 """ // 예상되는 변환 결과를 문자열로 정의합니다. precondition(transformedSF.description == expectedDescription) // 변환 결과와 예상 결과가 일치하는지 precondition으로 검사합니다.
swift
위 예제에서는 precondition을 사용해 매크로를 테스트했지만, 테스트 프레임워크를 사용할 수도 있습니다.
매크로 개발 과정에서 디버깅이 필요할 때가 있습니다. Xcode에서는 다음과 같은 방법으로 매크로를 디버깅할 수 있어요:
- 브레이크포인트를 설정하세요.
- 매크로 코드에서 중단점을 설정하면 실행이 해당 지점에서 일시 중지됩니다.
- 디버깅 콘솔을 활용하세요.
print()
문을 사용해 변수 값을 출력하고 실행 흐름을 추적할 수 있습니다.dump()
함수로 AST 노드의 내용을 자세히 출력해 볼 수도 있죠.
- LLDB 명령어를 사용하세요.
po
명령으로 변수나 표현식의 값을 출력할 수 있습니다.fr v
명령으로 현재 프레임의 변수 목록을 확인할 수 있습니다.
다음은 LLDB에서 매크로 디버깅 예시입니다:
(lldb) breakpoint set -f FourCharacterCode.swift -l 10 Breakpoint 1: where = MyModule`FourCharacterCode.expansion(of:in:) at FourCharacterCode.swift:10:24, address = 0x00000001000015e0 (lldb) process launch Process 12345 launched: '/path/to/MyApp' (x86_64) Process 12345 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 frame #0: 0x00000001000015e0 MyModule`FourCharacterCode.expansion(of:in:) at FourCharacterCode.swift:10:24 7 public static func expansion( 8 of node: some FreestandingMacroExpansionSyntax, 9 in context: some MacroExpansionContext -> 10 ) -> ExprSyntax { 11 let charactersExpr = node.argumentList.first!.expression 12 let charactersString = charactersExpr.as(StringLiteralExprSyntax.self)! 13 Target 0: (MyApp) stopped. (lldb) po charactersExpr StringLiteralExpr("ABCD") (lldb) fr v (some FreestandingMacroExpansionSyntax) node = 0x000060000056e250 { __storage = { opaque: 0x60000002b300 pointer: 0x000060000056e250 } } (some MacroExpansionContext) context = 0x00007ecd7e16ee80 { __storage = 0x00007ecd7e16ee80 } (some ExprSyntax) charactersExpr = 0x0000600000576260 { __storage = { opaque: 0x60000002b700 pointer: 0x0000600000576260 } }
text
이렇게 디버깅 도구를 활용하면 매크로 개발 과정에서 발생하는 문제를 효과적으로 해결할 수 있습니다.
매크로 개발은 컴파일러와 밀접한 관련이 있기 때문에 Swift 컴파일러의 동작을 잘 이해하는 것이 중요합니다. 특히 AST가 어떻게 구성되고 변환되는지 파악해야 하죠. 그래야 의도한 대로 코드를 생성하는 매크로를 만들 수 있습니다.
이상으로 Swift 매크로 개발과 디버깅에 대해 알아보았습니다. 앞으로도 매크로를 활용해 더욱 강력하고 표현력 있는 Swift 코드를 작성할 수 있기를 바랍니다!