Watchpoint — 와치포인트의 종류와 Xcode에서의 설정 방법

저자: IT Sectr 게시일: 2026-05-07 읽는 시간: 8 분

Watchpoint(와치포인트) — 지정된 변수나 메모리 영역의 값이 변경될 때 프로그램 실행을 일시 중지하는 디버깅 메커니즘입니다. 브레이크포인트가 코드 라인에 연결되어 있는 반면, 와치포인트는 프로그램의 어느 부분에서 수정이 발생하든 데이터 변경을 추적합니다. Apple Developer Documentation에 따르면, 와치포인트는 데이터 경합, 예기치 않은 속성 변경 및 객체 수명 주기 추적 디버깅에 필수적입니다.

핵심 사항

  • Watchpoint — 변수의 변경을 모니터링하는 지점으로, 지정된 메모리 영역에 쓰기가 발생하면 트리거됩니다.
  • Xcode는 GUI 및 LLDB 콘솔을 통해 조건부 와치포인트를 포함한 와치포인트를 지원합니다.
  • 객체의 속성 변수에는 변수 이름 대신 메모리 주소가 포함된 와치포인트가 필요합니다.
  • 와치포인트를 사용하면 각 쓰기 작업의 하드웨어 모니터링으로 인해 성능이 크게 저하됩니다.
  • Watchpoints는 디버깅 세션 간에 유지되지 않습니다 — 각 실행 후 다시 설정해야 합니다.

와치포인트란 무엇이며 작동 원리

Watchpoint — 지정된 메모리 주소의 값이 변경될 때 프로그램 실행을 일시 중지하는 디버거 메커니즘입니다. 브레이크포인트가 특정 코드 라인에 도달하면 반응하는 반면, 와치포인트는 특정 메모리 셀에 쓰기가 발생하면 반응합니다 — 코드의 어떤 부분이 쓰기를 수행했는지와 관계없이. 이는 특히 멀티스레드 애플리케이션에서 예기치 않은 변수 수정을 찾는 데 필수적입니다.

작동 원리는 프로세서 하드웨어 지원에 기반합니다. 최신 ARM 칩(Apple Silicon, Qualcomm Snapdragon)은 4~8개의 하드웨어 와치포인트 레지스터를 제공합니다. 프로세서가 와치포인트와 일치하는 주소에 쓰기 명령을 실행하면 하드웨어 인터럽트가 발생하고 디버거가 이를 포착합니다. 하드웨어 레지스터가 소진되면 LLDB는 소프트웨어 모드로 전환됩니다 — 각 쓰기 명령을 단계별로 확인하여 실행 속도를 수십 배 저하시킵니다.

ARM Architecture Reference Manual에 따르면, 하드웨어 와치포인트는 Data Watchpoint and Trace(DWT) 모듈 수준에서 작동하며 실행 코드 수정이 필요하지 않습니다. 응답 시간은 나노초 단위로, 오디오 또는 비디오 프레임 버퍼와 같은 데이터 처리 루프에서 고주파 변수 변경도 추적할 수 있습니다.

와치포인트가 브레이크포인트보다 효과적인 경우

Watchpoint는 어떤 변수가 변경되고 있는지는 알지만 어디서 변경되는지 모를 때 필수적입니다. 일반적인 시나리오: UIView의 frame 속성이 명백한 이유 없이 변경됨, 백그라운드 스레드의 카운터가 예측 불가능하게 리셋됨, isLoading 플래그가 네트워크 요청 완료 전에 전환됨. 이러한 각 경우에 쓰기가 발생할 수 있는 모든 위치에 브레이크포인트를 설정하는 것은 비실용적입니다. 와치포인트는 단일 명령으로 문제를 해결합니다.

와치포인트의 종류와 설정 방법

Xcode와 LLDB는 세 가지 유형의 와치포인트를 지원합니다: watchpoint set variable — 지역 변수 모니터링용; watchpoint set expression — 주소를 반환하는 표현식 모니터링용; watchpoint set — 원시 메모리 주소 모니터링용. 각 유형에는 고유한 적용 영역이 있습니다.

Watchpoint 유형LLDB 명령어용도
변수watchpoint set variable -w write self.count지역 및 전역 변수, 구조체 속성
표현식watchpoint set expression -w write -- &self->mutex.lock포인터로 구조체 필드, 인덱스로 배열 요소
주소watchpoint set -w write 0x600000c4b80이전 LLDB 출력의 특정 메모리 주소

Xcode GUI를 통한 설정

Xcode에서 디버그 영역을 통해 와치포인트를 설정할 수 있습니다: 브레이크포인트에서 중지하고, Variables View 패널에서 원하는 변수를 찾은 후 마우스 오른쪽 버튼을 클릭하고 Watch Variable을 선택합니다. Xcode는 올바른 변수 이름과 컨텍스트로 watchpoint set variable 명령을 자동으로 실행합니다. 그 후 디버거는 값이 변경될 때마다 중지됩니다 — 콘솔로 전환하지 않고 빠른 디버깅에 편리합니다. 그러나 이 방법은 변수가 현재 프레임의 범위 내에 있을 때만 작동합니다.

swift
func processItems(_ items: [String]) {
    var index = 0
    // GUI를 통해 index에 와치포인트 설정:
    // 여기서 중지, 마우스 오른쪽 버튼 클릭 → Watch Variable
    for item in items {
        index += 1
        print("항목 \(index): \(item)")
    }
}

LLDB를 통한 와치포인트 관리

LLDB는 콘솔에서 와치포인트를 관리하기 위한 완전한 명령어 세트를 제공합니다. 이는 GUI보다 더 많은 제어를 제공합니다: 관찰 영역 크기 설정, 트리거 조건 지정, 계산된 표현식에서 얻은 주소에 와치포인트 생성, 트리거 시 자동 작업 실행이 가능합니다. 명령 인터페이스는 관찰 매개변수를 빠르게 변경해야 하는 복잡한 시나리오를 디버깅할 때 특히 유용합니다.

기본 와치포인트 명령어

watchpoint set variable 명령어는 범위를 고려하여 변수 이름을 받습니다: Objective-C self 속성에는 self->_property를, Swift에는 self.property를 사용합니다. -w write 매개변수는 쓰기 추적을 설정하고, -w read — 읽기 추적(일부 아키텍처에서만 사용 가능), -s size — 영역 크기(바이트)를 설정합니다. 와치포인트 설정 후 watchpoint list 명령어로 목록을 볼 수 있습니다.

lldb
(lldb) watchpoint set variable -w write -s 8 self.balance
Watchpoint 1: addr = 0x600000c4b80 size = 8 state = enabled  type = w
    watchpoint spec: 'self.balance'
(lldb) watchpoint set expression -w write -- self->items._storage
Watchpoint 2: addr = 0x600003a4c00 size = 8
(lldb) watchpoint list
1: location = 0x600000c4b80, type = write, variable = '_balance'
2: location = 0x600003a4c00, type = write, expression = 'self->items._storage'

조건부 와치포인트

브레이크포인트와 마찬가지로 와치포인트는 조건부 트리거를 지원합니다. -c 매개변수는 Swift 또는 Objective-C로 조건을 설정합니다. 예를 들어, newValue > 1000 조건의 와치포인트는 기록된 값이 1000을 초과할 때만 중지됩니다. 이는 초당 수천 개의 변경을 생성하는 루프나 센서를 디버깅할 때 중요합니다 — 그렇지 않으면 디버거가 모든 변경에서 중지되어 작업이 불가능해집니다.

lldb
(lldb) watchpoint set variable -w write self.temperature -c "(int)$newValue > 100"
Watchpoint 3: addr = 0x600000e4a20, condition = '(int)$newValue > 100'
(lldb) watchpoint modify 3 -C "po self.temperature" -G true
// 값을 자동으로 로깅하고 실행 계속

객체 속성 및 컬렉션의 와치포인트

Objective-CSwift 객체 속성에 대한 와치포인트의 특징 — 와치포인트는 속성 이름이 아니라 객체 메모리의 ivar(인스턴스 변수) 필드 주소에 설정됩니다. 이는 새 객체가 할당될 때마다(예: ViewController 재생성 시) 메모리 주소가 변경되어 와치포인트가 무효화됨을 의미합니다. 재시작 간에 속성을 지속적으로 모니터링하려면 객체 초기화 시점에 와치포인트를 다시 설정해야 합니다.

컬렉션 요소의 와치포인트

배열사전의 요소를 모니터링하려면 특정 요소의 주소 계산이 필요합니다. 예를 들어, 배열의 세 번째 요소를 모니터링하려면 watchpoint set expression -- &array[2]를 사용합니다. 배열이 내부 버퍼를 재할당하면(용량을 초과하여 요소 추가 시) 와치포인트가 무효화됩니다 — LLDB는 Watchpoint 1 has an invalid address 오류를 보고합니다. 이러한 경우 컬렉션 크기 변경 후 와치포인트를 다시 설정해야 합니다.

lldb
(lldb) expr var $arr = [10, 20, 30, 40, 50]
(lldb) watchpoint set expression -w write -- &$arr[2]
Watchpoint 4: addr = 0x1000a4b20, size = 8
(lldb) expr $arr[2] = 99
Watchpoint 4 hit: old value: 30, new value: 99

와치포인트를 통한 retain count 추적

Objective-C에서는 objc_object 구조체의 retainCount 필드에 와치포인트를 설정하여 객체의 retain count를 추적할 수 있습니다. LLDB는 watchpoint set expression -w write -- (int*)[object retainCount]를 통해 이를 수행합니다. 그러나 ARC(자동 참조 카운팅)를 사용하는 Swift 객체의 경우 retain count에 직접 접근할 수 없습니다 — 대신 Instruments 또는 Memory Graph Debugger를 사용하여 누수 분석을 수행하세요. Apple에 따르면, retain count의 와치포인트는 ARC 최적화가 비활성화된 Debug 빌드에서만 작동합니다.

와치포인트의 제한 사항 및 성능

Watchpoints에는 고려해야 할 몇 가지 중요한 제한 사항이 있습니다. 주요 제한 — 하드웨어 와치포인트의 수는 ARM 아키텍처에서 4~8개, x86에서 최대 4개 레지스터로 제한됩니다. 모든 하드웨어 레지스터가 사용 중이면 LLDB는 소프트웨어 와치포인트 모드로 전환됩니다: 예외를 생성하기 위해 추적 범위 내의 모든 쓰기 명령을 수정합니다. 이로 인해 실행 속도가 10~50배 저하되므로 실제로는 동시에 2~3개 이상의 활성 와치포인트를 사용하지 않는 것이 좋습니다.

두 번째 제한 — 메모리 재할당 시 와치포인트의 무효화입니다. 가비지 컬렉션이나 ARC가 트리거되고 객체가 메모리에서 이동할 때(힙 압축이 있는 언어의 경우) 와치포인트 주소가 잘못됩니다. Swift와 Objective-C에서 ARC는 객체를 이동하지 않지만 배열과 문자열 재할당은 동일한 효과를 유발합니다. LLDB는 Watchpoint N address (0x...) doesn’t contain a valid allocation 메시지로 이에 대해 경고합니다.

세 번째 제한 — 변수 가시성입니다. 지역 변수의 와치포인트는 해당 변수가 현재 스택 프레임의 범위 내에 있을 때만 작동합니다. 함수가 종료되면 와치포인트가 자동으로 제거됩니다. 전역 변수나 장수명 객체의 필드를 모니터링하는 경우 와치포인트는 watchpoint delete로 명시적으로 삭제되거나 프로세스가 종료될 때까지 유지됩니다.

lldb
(lldb) watchpoint delete 1
1 watchpoints deleted.
(lldb) watchpoint delete               # 모든 watchpoint 삭제
2 watchpoints deleted.
(lldb) watchpoint disable 1             # 일시적으로 비활성화
(lldb) watchpoint enable 1              # 다시 활성화

ARM에 따르면, Apple Silicon(M1~M4)의 와치포인트는 1~8바이트의 모니터링 영역 크기를 지원합니다. 8바이트를 초과하는 구조체를 모니터링하려면 각 필드에 여러 와치포인트를 설정해야 합니다. 이는 CGRect(16바이트) 또는 UIEdgeInsets(16바이트)와 같은 복잡한 데이터 구조체를 디버깅할 때 중요합니다.

자주 묻는 질문

와치포인트와 브레이크포인트의 차이점은 무엇인가요?

Breakpoint는 코드 라인에 연결되어 해당 라인에 도달하면 중지됩니다. Watchpoint는 메모리 주소에 연결되어 프로그램의 어디에서든 해당 주소에 쓰기가 발생하면 중지됩니다. 와치포인트는 “누가 값을 변경하고 있는지”를 찾고, 브레이크포인트는 “이 라인에서 무엇이 일어나는지”를 찾습니다.

동시에 몇 개의 와치포인트를 설정할 수 있나요?

ARM(Apple Silicon 포함)에서 4~8개의 하드웨어 와치포인트. 이를 초과하면 와치포인트가 소프트웨어 모드로 전환되어 실행 속도가 10~50배 저하됩니다. 한 번에 2~3개 이상의 활성 와치포인트를 사용하지 않는 것이 좋습니다.

애플리케이션 재시작 후 와치포인트가 작동하지 않는 이유는 무엇인가요?

Watchpoints는 디버깅 세션 간에 유지되지 않습니다. 새로 실행할 때마다 메모리 주소가 변경되므로 와치포인트를 다시 설정해야 합니다. 예외는 고정 주소를 가진 전역 변수의 와치포인트입니다.

Swift 속성에 와치포인트를 설정할 수 있나요?

네, 하지만 와치포인트는 속성 자체가 아니라 속성의 ivar(백업 저장소)에 설정됩니다. Swift에서는 watchpoint set variable self.property를 사용하세요 — LLDB가 속성 이름으로 해당 ivar를 자동으로 찾습니다.

와치포인트가 애플리케이션 성능에 어떤 영향을 미치나요?

하드웨어 와치포인트는 성능에 영향을 미치지 않습니다 — 인터럽트가 프로세서 수준에서 발생합니다. 소프트웨어 와치포인트(하드웨어 레지스터가 소진된 경우)는 LLDB가 모든 쓰기 명령을 확인하므로 실행 속도가 10~50배 저하됩니다.

요약

  • Watchpoint — 메모리 변경을 모니터링하는 지점으로, 모든 스레드에서 지정된 주소에 쓰기가 발생하면 트리거됩니다.
  • 하드웨어 와치포인트는 프로세서 DWT 레지스터를 사용하며 실행 속도를 저하시키지 않지만 4~8개 레지스터로 제한됩니다.
  • LLDB는 세 가지 유형의 와치포인트를 지원합니다: 변수, 표현식 및 원시 메모리 주소.
  • 조건부 와치포인트는 지정된 표현식이 참으로 평가될 때만 트리거됩니다 — 고주파 변경 디버깅에 필수적입니다.
  • 객체 속성 및 컬렉션 요소의 Watchpoints는 메모리 재할당 시 무효화됩니다.
  • Watchpoints는 세션 간에 유지되지 않으며 변수가 범위를 벗어나면 제거됩니다.
  • Watchpoint는 변수는 알지만 수정 위치를 모를 때 최선의 선택이며, 특히 멀티스레드 코드에서 유용합니다.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기