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”

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다