[카테고리:] 미분류

  • Breakpoint 심층 이해: WWDC24에서 배운 디버깅 기술

    Swift와 Xcode로 개발하는 동안 발생하는 문제를 해결하기 위해 Breakpoint는 필수적인 도구입니다. WWDC24의 “Run, Break, Inspect: Explore effective debugging in LLDB” 세션에서 소개된 Breakpoint의 동작 방식과 고급 기능에 대해 심층적으로 알아보겠습니다.

    Breakpoint의 기본 메커니즘 이해하기

    단일 코드에 여러 Breakpoint가 생성되는 이유

    Button("Hello, world!") {
        someMethod()
    }
    

    위 코드에서 Button 생성자에 Breakpoint를 설정했을 때, 흥미로운 현상이 발생합니다. 코드 한 곳에 Breakpoint를 설정했는데, 실행 시 여러 다른 위치에서 프로그램이 중단됩니다.

    이러한 현상은 Breakpoint Navigator에서 확인할 수 있습니다. 하나의 Breakpoint를 설정했음에도 여러 개의 Breakpoint 항목이 표시됩니다. 이는 Xcode가 서로 다른 코드 경로를 통해 동일한 Breakpoint에 도달할 수 있는 모든 경우를 추적하기 때문입니다.

    Button 예제에서 구체적으로:

    1. Breakpoint 1.1: Button이 생성되는 시점
    2. Breakpoint 1.2: Button 생성자의 trailing closure가 실행되는 시점

    복잡한 Button에서의 Breakpoint

    action과 label이 모두 있는 Button을 생성하는 경우:

    @State private var isAddedToWatchLater = false
    
    Button(action: { self.isAddedToWatchLater.toggle() }) {
        Text(isAddedToWatchLater ? "Added to Watch Later" : "Add to Watch Later")
    }
    

    이 코드에 Breakpoint를 설정하면 Xcode는 세 개의 서로 다른 Breakpoint를 생성합니다:

    1. Breakpoint 1.1: Button의 생성자가 호출될 때
    2. Breakpoint 1.2: Button의 action closure가 실행될 때
    3. Breakpoint 1.3: Button의 trailing closure(UI 표시 부분)가 실행될 때

    실제 실행 순서는 1.1 → 1.3 → 1.2로, 버튼이 생성되고, UI가 렌더링된 후, 사용자 액션에 따라 action closure가 실행됩니다.

    LLDB에서 Breakpoint 관리하기

    Breakpoint 목록 확인

    LLDB 콘솔에서 더 자세한 정보를 확인할 수 있습니다:

    (lldb) breakpoint list
    

    이 명령어는 현재 설정된 모든 Breakpoint의 상세 정보(ID, 위치, 히트 카운트 등)를 표시합니다. 이를 통해 Navigator에서 보이는 것보다 더 많은 정보를 얻을 수 있습니다.

    Breakpoint 비활성화

    특정 경로의 Breakpoint를 비활성화하는 방법:

    1. Navigator에서 해당 Breakpoint의 파란색 점을 클릭하여 흐린 파란색으로 변경
    2. 또는 Breakpoint를 우클릭하고 ‘Disable Breakpoint Location’ 선택
    3. LLDB에서는 breakpoint disable <ID> 명령어 사용

    Swift Error Breakpoint: 오류 지점 빠르게 찾기

    에러 처리 코드에서 실제 오류 발생 지점을 찾는 것은 어려울 수 있습니다. Swift Error Breakpoint는 이런 상황에서 매우 유용합니다.

    실제 사용 예시

    JSON 파싱 예제를 살펴보겠습니다:

    let jsonData = self.jsonString.data(using: .utf8)!
    
    do {
        let videos = try decoder.decode([Video].self, from: jsonData)
        for video in videos {
            print(video.title, video.description)
        }
    } catch {
        // error 처리가 없음
    }
    

    만약 JSON 데이터 중 하나의 객체에 imageName 대신 imag와 같은 오타가 있다면:

    1. 일반적인 접근: 파싱 로직에 Breakpoint를 설정하고 모든 객체를 하나씩 확인
    2. 향상된 접근: Swift Error Breakpoint 사용

    Swift Error Breakpoint 설정 방법

    1. Breakpoint Navigator에서 좌측 하단의 ‘+’ 버튼 클릭
    2. ‘Swift Error Breakpoint’ 선택
    3. 프로그램 실행

    이제 Swift 에러가 처음 발생하는 지점에서 프로그램이 자동으로 중단됩니다. LLDB를 사용하여 해당 시점의 상태(예: 오류가 발생한 객체의 ID)를 검사할 수 있습니다.

    (lldb) po error
    (lldb) po id
    

    이를 통해 오류가 발생한 정확한 객체와, 문제가 있는 부분을 빠르게 식별할 수 있습니다.

    High-firing Breakpoint 관리 기술

    반복문이나 자주 호출되는 함수에 설정된 Breakpoint는 프로그램을 과도하게 중단시켜 디버깅 효율성을 저하시킬 수 있습니다. WWDC에서는 이러한 “High-firing Breakpoint”를 효과적으로 관리하는 세 가지 기술을 소개했습니다.

    1. Breakpoint Conditions

    특정 조건이 충족될 때만 Breakpoint가 활성화되도록 설정:

    for video in videos {
        print(video.title, video.description) // Breakpoint 위치
    }
    

    이 코드에서 description의 길이가 30자를 초과하는 경우에만 중단하고 싶다면:

    1. Breakpoint를 우클릭하고 ‘Edit Breakpoint’ 선택
    2. Condition 필드에 video.description.count > 30 입력

    이제 조건이 true인 경우에만 프로그램이 중단됩니다.

    2. Breakpoint Action과 임시 Breakpoint (tbreak)

    특정 함수 호출 후에만 Breakpoint를 활성화하는 고급 기법:

    for video in videos {
        if video.hasRemoteMedia {
            video.loadRemoteMedia() // 첫 번째 Breakpoint 위치
        }
        processSomething() // 두 번째 Breakpoint 위치를 조건부로 활성화하고 싶음
    }
    

    loadRemoteMedia()가 호출된 경우에만 processSomething()에서 중단하려면:

    1. loadRemoteMedia() 함수에 Breakpoint 설정
    2. ‘Edit Breakpoint’ → ‘Add Action’ → ‘Debugger Command’ 선택
    3. tbreak processSomething 또는 tbreak {filename}:{linenumber} 입력

    여기서 tbreak는 “temporary breakpoint”의 약자로, 해당 위치에서 한 번만 프로그램을 중단시킨 후 자동으로 제거됩니다. LLDB는 이를 “one-shot breakpoint”라고 표시합니다.

    임시 Breakpoint는 LLDB에서 직접 생성할 수도 있습니다:

    (lldb) tbreak processSomething
    

    3. Breakpoint Ignore Count

    Breakpoint가 특정 횟수 동안 무시되고, 그 이후에만 활성화되도록 설정:

    1. Breakpoint를 우클릭하고 ‘Edit Breakpoint’ 선택
    2. ‘Ignore’ 필드에 원하는 횟수 입력

    이 설정은 처음 N번의 Breakpoint 히트를 무시하고, N+1번째부터 프로그램을 중단시킵니다. 특정 반복에서만 검사하고 싶을 때 유용합니다.

    실전 디버깅 워크플로우 최적화

    위에서 배운 기술들을 조합하여 효율적인 디버깅 워크플로우를 구성할 수 있습니다:

    1. 문제 식별: Swift Error Breakpoint를 사용하여 오류 발생 지점 찾기
    2. 상세 분석: 조건부 Breakpoint를 사용하여 특정 상황에서만 코드 검사
    3. 흐름 추적: 임시 Breakpoint(tbreak)를 사용하여 특정 함수 호출 간의 관계 분석
    4. 반복 문제 해결: Ignore Count를 사용하여 N번째 반복에서만 검사

    이러한 기술들을 상황에 맞게 적용하면 복잡한 문제도 더 효율적으로 해결할 수 있습니다.

    결론

    Breakpoint는 단순히 코드 실행을 중단시키는 도구를 넘어, 다양한 기능을 제공하는 강력한 디버깅 도구입니다. WWDC24에서 소개된 기술들을 활용하면 더 효율적이고 정확한 디버깅이 가능해집니다. 특히 tbreak와 같은 일회성 Breakpoint는 특정 상황만 빠르게 확인하고 싶을 때 매우 유용합니다.

    이러한 고급 Breakpoint 기술들을 마스터하면 복잡한 문제도 더 체계적으로 접근하고 해결할 수 있으며, 궁극적으로 개발 생산성 향상에 크게 기여할 수 있습니다.


    출처

    • WWDC24 세션: “Run, Break, Inspect: Explore effective debugging in LLDB”