2. 디버깅 기법으로 앱 로직 이해하기
디버거를 사용할 때 제일 중요한 것은 '내가 조사하려는 로직이 무엇인가?'를 아는 것이다.
브레이크 포인트
- 앱 실행을 중단 시킬 위치는 브레이크 포인트를 찍어 표시한다.
- 브레이크 포인트를 찍으면 해당 코드라인에서 실행이 중단되고 해당 스코프의 모든 변수와 그 값을 살펴볼 수 있다.
- 또 실행 트레이스를 이용하면 코드 라인을 탐색할 때 현재 위치를 기억할 수 있다.
실행 스택 트레이스
- 실행 스택 트레이스는 마치 실제 지도처럼 디버거가 중단 시킨 코드 라인의 실행 경로를 나타내며, 이후 어디로 나아가야 할지 결정하는 데 도움을 준다.
- 스택 트레이스는 디버거가 실행을 멈춘 지점까지 메서드가 서로 어떻게 호출하는지 나타내며, 메서드명, 클래스명, 호출한 코드 라인을 자세히 표시한다.
- 스프링 프레임워크 같이 숨겨친 로직을 찾을 때 실행 스택 트레이스를 자주 사용한다.
- 책에서는 인텔리 제이를 활용해 스프링 AOP를 디버깅하는 예시를 보여준다.
디버거 기초
- 스텝 오버: 동일한 메서드에서 다음 코드 라인으로 계속 실행한다.
- 스텝 인투: 현재 라인에서 호출된 메서드 중 하나의 내부에서 실행을 계속한다.
- 스텝 아웃: 조사하던 메서드를 호출한 메서드로 실행을 되돌린다.
코드의 작동 방식을 알고 싶다면 스텝 오버로 시작하는게 좋다.
스텝 인투를 할 때마다 조사 플랜이 새로 열리면서 전체 프로세스가 점점 더 복잡해진다.
메서드를 읽다가 이해가 안되는 코드의 첫 라인을 찾고, 해당 코드 라인에 브레이크 포인트를 찍고 거기서부터 조사를 시작하자.
처음부터 스텝인투하면 프로세스가 너무 복잡해진다.
만약 성능 문제, 멀티 스레드 앱 문제, 힙 or 스레드 문제가 발생하면 디버거 말고 다른 도구를 사용해야한다.
3. 고급 디버깅 기법으로 문제의 근본 원인 찾기
3.1 조건부 브레이크 포인트로 조사 시간 최소화
조건부 브레이크포인트 특정한 조건을 더 만족할 경우에만 코드 라인에서 앱 실행을 중단시키는 방법이다.
IntelliJ에서 브레이크 포인트에 오른쪽 버튼을 클릭하고 해당 브레이크포인트가 적용될 조건을 입력하면 된다.
그러면 조건부 브레이크 포인트를 활용할 수 있다.
이런식으로 하면 반복문에서 i가 7인 케이스부터 조사를 시작한다. 아닌 케이스들은 모두 건너뛴다.
조건부 브레이크에도 단점이 존재하는데 스코프에 있는 변숫값을 디버거가 지속적으로 가로채서 브레이크 포인트 조건을 평가해야 하므로 실행 성능에 상당히 큰 영향을 미친다.
위 조건부 브레이크 포인트의 팝업에서 More를 누르고 들어가면 실행 정보를 로깅하는 기능 등 더 많은 기능을 사용할 수 있다.
3.2 실행을 중단시키지 않고도 브레이크 포인트를 사용하는 방법
인텔리제이에서 아래와 같은 설정을 하면 브레이크 포인트에서 실행을 중단하지 않고 특정 세부 정보를 기록하도록 지시할 수 있다.
1. Suspend의 체크를 풀어 실행을 멈추지 않게 한다.
2. Evaluate and log에 체크를 하고 보고 싶은 변수를 입력한다.
위와 같이 진행되면 i == 7일 때 멈추지 않고 i를 로그에 남긴다. (i는 당연히 7이지만)
3.3 조사 시나리오를 동적으로 변경하기
디버깅 도중에 스코프 안에 있는 변숫값을 변경하는 식으로 코드 조사를 간편하게 할 수 있다.
먼저 다음과 같은 두 가지 시나리오를 생각해보자.
- 실행 시간이 긴 프로세스의 문제점을 조사하는 경우. 실행 완료까지 1시간 이상이 걸리는데 특정 인숫값이 전달될 때에만 아웃풋이 엉뚱하게 나오는데, 이를 살펴보고 싶다.
- 실행이 매우 빠른 코드가 있는데, 보안 제약이 걸려 있어서 로컬에서 실행이 불가능한 경우. 특정 인숫값과 연관이 있는데 이 생각이 옳다는 사실을 증명하고 싶다.
첫 번째 시나리오는 브레이크 포인트가 찍힌 라인에서 중단될 때까지 기다리는 시간이 너무 길다.
두 번째 시나리오는 브레이크 포인트를 사용해볼만하다.
위 두 시나리오에서 디버거를 사용해 스코프의 변숫값을 바꾸는게 효율적이다.
다시 말하지만, 디버거가 실행을 중단시킨 상태에서 스코프에 있는 변수 중 하나의 값을 변경할 수 있다.
인텔리제이에서 값을 바꾸려는 변수를 선택한 다음 마우스 오른쪽 버튼을 클릭한다.
여기서 set Value를 누르고 값을 설정할 수 있다.
이러면 스코프에 있는 타입은 유지하고 변숫값만 바꾸면서 다른 조건에서 실행할 때 어떻게 작동되는지 관찰할 수 있다.
3.4 조사 케이스를 되감기
디버거로 코드를 조사하면서 '시간을 되돌리는' 이런 접근 방식을 실행 프레임 드로핑 또는 실행 프레임 퀴팅이라고 한다.
실행 프레임을 드롭한다는 것은, 실행 스택 트레이스에서 한 레이어 뒤로 간다는 뜻이다.
예를 들어, 어떤 메서드에서 스텝 인투했다가 되돌아가고 싶을 때, 실행 프레임을 삭제하면 메서드가 호출됐던 위치로 돌아가는 것이다.
필자의 인텔리제이에서 현재 실행 스택 트레이스에서 메서드 레이어를 선택하고 우클릭을 하면 Reset Frame이 나오는데,
이것이 바로 프레임 드롭이다. 책에서는 Drop Frame이라고 나왔다.
프레임 드롭은 스텝아웃과 아주 비슷하다.
둘다 실행 플랜을 닫고 실행 스택 트레이스에서 아래 방향으로 내려가는데, 프레임 드롭은 실행을 계속하지 않고 현재 플랜을 생성한 메서드의 실행 지점 이전으로 돌아간다는 점이 다르다.
참고로 프레임 드롭은 주의할점이 존재한다.
앱의 내부 메모리 밖에서 값을 변경하는 커맨드를 실행하면, 이를테면 다음과 같은 경우에는 프레임을 드롭해도 해당 변경분은 되돌릴 수 없다.
- DB에 있는 데이터 수정
- 파일 시스템 변경
- 다른 앱을 호출하여 해당 앱의 데이터르 변경
- 다른 앱이 읽는 큐에 메시지를 추가해서 해당 앱의 데이터를 변경한다.
- 이메일 메시지 전송
4. 원격 앱 디버깅
원격 디버깅이란 디버깅 기술을 로컬 시스템 대신 외부 환경에 있는 앱에 적용하는 것이다.
절대로 프로덕션에서 원격 디버깅을 수행하지말자. 절대로!!!
보통 개발, 스테이징, 프로덕션 환경을 사용하는데 개발과 스테이징에서만 원격 디버깅을 진행하자.
원격 디버깅을 하려면 앱을 실행할 때 에이전트라는 소프트웨어를 앱에 부착해야 한다.
이 디버깅 에이전트를 붙이면 이런 현상이 나타날 수 있다.
- 에이전트 때문에 앱 실행속도가 느려질 수 있다. 속도가 느려지면 예기치 않은 성능 문제가 발생할 수 있다.
- 에이전트 디버거 도구와 통신하려면 네트워크를 통해야 하는데, 특정 포트를 오픈하는 과정에서 보안 취약 요소가 발생할 수 있다.
- 앱의 어떤 부분을 다른 곳에서 동시 사용 중일 경우, 해당 코드를 디버깅하면 전체적인 기능에 간섭을 일으킬 수 있다.
- 디버깅을 하다가 앱이 무한정 차단돼서 프로세스를 재시작해야할 수도 있다.
원격 환경에서 조사하기
원격 디버깅할 앱은 커맨드 라인에서 java로 앱을 시작할 때 -agentlib:jdwp 매개변수를 추가하여 디버거 에이전트를 연결시켜야 한다.
이때 디버거 도구를 연결할 포트를 지정한다.
기본적으로 디버거 에이전트는 일종의 서버처럼 작동되면서,
디버거 도구가 설정된 포트에 접속해서 디버깅 작업을 수행할 수 있는 관문 역할을 한다.
아래와 같은 명령어를 사용해 디버거 에이전트를 앱에 부착해야 한다.
java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 app.jar
인텔리제이 환경에서 원격 앱에 디버거로 접속하는 방법을 알아보자.
원격 환경에서 실행 중인 앱에 디버거로 접속하는 절차는 다음과 같다.
- 실행 구성을 추가 (Run > Edit Configurations > + 버튼 > Remote JVM Debug
- 디버거 에이전트의원격지 주소를 설정 (구성 이름, 주소(IP)와 포트)
- 앱 디버깅을 시작 (로컬 디버깅과 동일하다)
이상으로 포스팅을 마칩니다.
참고 자료
댓글