[성능 개선 이야기] APM이란 무엇인가? 사상, 기능, 그리고 언어별 작동 원리
안녕하세요, 가야태자 @talkit 입니다.
저는 요즘 성능 개선 관련 업무를 진행하고 있는데, 최근 방문한 고객사 시스템에 APM이 없는 것을 확인하고 오픈소스 APM인 Scouter(스카우터)를 도입해서 유용하게 사용하고 있습니다.
해당 제품을 사용하면서 문득 늘 당연하게 쓰던 APM은 과연 '어떤 사상에서 출발했고, 어떤 기능을 하며, 우리가 쓰는 프로그래밍 언어의 어떤 특징을 이용해서 모니터링을 수행하는지' 깊게 궁금해졌습니다.
해서 오늘은 APM의 핵심 개념과 언어별 모니터링 작동 원리에 대해서 깊이 있게 알아보고, 다음 글에서 구체적인 제품들에 대해 알아보는 시간을 가지도록 하겠습니다.
[성능 개선 이야기] APM이란 무엇인가? 사상, 기능, 그리고 언어별 작동 원리 (Java, C++, Python, Node.js)
1. APM은 어떤 사상에서 출발했을까?
과거의 모니터링은 주로 인프라(System) 중심이었습니다. "서버의 CPU 사용량은 얼마인가?", "메모리가 꽉 차지 않았나?", "네트워크 인/아웃 트래픽은 어떤가?" 등 서버 장비 자체의 건강 상태를 체크하는 데 집중했죠.
하지만 웹 서버(WAS), 데이터베이스, 외부 API 등이 유기적으로 얽힌 현대의 아키텍처에서는 이상한 일이 벌어지기 시작했습니다. 인프라 모니터링 상으로는 서버가 아주 멀쩡하고 평온한데, 정작 "사용자는 서비스가 너무 느려서 터지려고 한다"고 불평하는 현상이 발생한 것입니다.
여기서 APM(Application Performance Monitoring)의 핵심 사상이 탄생합니다. 바로 "사용자 경험(UX)과 비즈니스 트랜잭션 중심의 모니터링"입니다. 인프라 장비가 아니라, 사용자의 요청(Request)이 시스템 내부에 들어온 순간부터 나갈 때까지의 '애플리케이션 내부 흐름'을 낱낱이 파헤쳐보자는 아이디어에서 출발한 것입니다.
2. APM의 핵심 기능은 무엇일까?
APM은 서비스라는 환자의 몸속을 들여다보는 X-ray나 MRI와 같은 역할을 합니다. 대표적인 핵심 기능은 다음과 같습니다.
트랜잭션 추적 (Transaction Tracing): 사용자가 버튼을 클릭한 순간부터 어떤 클래스와 메서드를 거쳐 결과가 반환되었는지 그 전체 경로를 추적합니다.
병목 구간 탐지: 전체 응답 시간이 3초라면, 그중 DB 쿼리를 실행하는 데 2.5초가 걸렸는지, 외부 API를 호출하는 데 지연이 있었는지 정확한 원인 지점을 짚어냅니다.
실시간 가시성 (Topology & Dashboard): 복잡하게 얽힌 서비스 간의 호출 관계를 시각적인 맵으로 그려주고, 현재 활성화된 요청 수(Active Service)나 에러율을 실시간 대시보드로 보여줍니다.
코드 레벨의 프로파일링: 문제가 되는 로직을 소스 코드의 특정 메서드나 SQL 쿼리 단위까지 파고들어 내려가서 확인할 수 있게 해줍니다.
3. 언어별 어떤 특징을 이용해 모니터링을 진행할까?
개발자가 모니터링을 하겠다고 소스 코드 사이사이에 일일이 시간 측정용 코드를 심는 것은 불가능에 가깝습니다. 그래서 APM은 우리가 사용하는 프로그래밍 언어와 런타임의 고유한 특성을 활용해, 소스 코드를 한 줄도 수정하지 않고도 마법처럼 모니터링을 수행합니다.
짚고 넘어가는 핵심 요약
Java: 컴파일 시점(정확히는 클래스가 로딩되는 찰나)에 바이트코드를 조작하여 코드를 주입합니다.
C++: 런타임 조작이 어려워 컴파일 타임 훅(Hook)이나 OS 레벨의 시스템 콜 추적을 활용합니다.
Python / Node.js: 극도의 유연성을 가진 인터프리터 및 이벤트 루프 특성을 살려, 실행 시점(런타임)에 함수를 통째로 감싸거나 바꿔치기(Wrapping)합니다.
① Java 환경: 바이트코드 조작 (Bytecode Instrumentation)
Java 애플리케이션이 실행될 때 -javaagent라는 JVM 옵션을 주어 APM 에이전트를 함께 띄웁니다. JVM의 클래스 로더(Class Loader)가 컴파일된 클래스 파일(.class)을 읽어와 메모리에 올리는 바로 그 찰나의 순간에, APM 에이전트가 이 바이트코드를 가로챕니다. 그리고 메서드의 시작과 끝부분에 모니터링을 위한 시간 측정용 코드를 동적으로 '끼워 넣고(Weaving)' 메모리에 적재합니다. 덕분에 개발자는 원본 소스 코드를 전혀 건드리지 않고도 완벽한 프로파일링 데이터를 얻을 수 있습니다.
② C++ 환경: 컴파일 타임 훅 및 OS 커널 추적
C++ 같은 네이티브 컴파일 언어는 가상 머신(VM)이 없기 때문에 런타임에 코드를 동적으로 조작하기가 매우 까다롭습니다. 따라서 컴파일할 때 프로파일링용 훅(Hook) 코드를 함께 빌드하거나, 애플리케이션 외부에서 OS 레벨의 기술(예: 리눅스의 eBPF, ptrace 등)을 활용해 시스템 콜(System Call)을 추적하고 성능을 측정하는 방식을 사용합니다.
③ Python 환경: 몽키 패칭 (Monkey Patching)
Python은 모든 것이 객체로 취급되고 런타임 구조를 동적으로 변경할 수 있는 극도의 유연성을 가지고 있습니다. Python APM 에이전트는 애플리케이션이 시작될 때 가장 먼저 로드되어, Django, FastAPI 같은 웹 프레임워크나 SQLAlchemy 같은 DB 라이브러리의 핵심 함수를 런타임에 동적으로 가짜 함수(Wrapper)로 가로채 교체해 버립니다. 또한 인터프리터 자체 내장 함수인 sys.setprofile() 등을 활용해 함수 호출 시점을 정밀하게 모니터링합니다.
④ Node.js 환경: 모듈 래핑 및 Async Hooks
Node.js는 단일 스레드 기반의 비동기 이벤트 루프 아키텍처를 가집니다. Node.js APM 에이전트는 require()나 import 시스템을 가로채서 http나 mysql 같은 핵심 모듈이 로드될 때 프록시(Proxy) 객체로 감싸서 내보내는 모듈 래핑(Shimming) 기술을 씁니다. 수많은 비동기 콜백들이 뒤섞이는 환경에서 요청의 맥락(Context)을 잃지 않기 위해, Node.js 내부의 async_hooks API를 사용하여 비동기 자원의 생명주기를 유기적으로 추적하고 연결합니다.
마치며
결과적으로 APM은 '시스템이 살아있는지'를 넘어 '애플리케이션이 얼마나 효율적으로 일하고 있는지'를 언어적, 런타임적 특성을 극한으로 활용해 추적하는 예술적인 도구라고 할 수 있습니다. 현재 저희가 도입한 오픈소스 Scouter 역시 이러한 기술들을 바탕으로 Java, Python, Node.js 에이전트를 각각 제공하며 데이터를 훌륭하게 수집해 주고 있습니다.
오늘은 이렇게 APM의 출발 사상과 작동 원리라는 이론적 토대를 다져보았습니다. 다음 글에서는 시장을 이끌고 있는 대표적인 APM 제품들의 종류와 상용 솔루션 및 오픈소스의 장단점을 비교해 보는 시간을 가지도록 하겠습니다. 감사합니다!
💡 더 깊이 알아보기: APM 기술 기반 학습 링크
오늘 다룬 내용들을 더욱 심도 있게 공부하고 싶으신 분들을 위해, 각 언어별 핵심 기술과 Scouter에 대한 유용한 링크들을 공유합니다.
- APM 전반 및 사상 (Observability)
• Google SRE Book - Monitoring Distributed Systems: https://sre.google/sre-book/monitoring-distributed-systems/ • 설명: 구글의 SRE(Site Reliability Engineering) 관점에서 바라보는 분산 시스템 모니터링의 철학을 배울 수 있는 교과서적인 자료입니다.
• What is Observability? (by Honeycomb): https://www.honeycomb.io/observability/ • 설명: 단순한 모니터링을 넘어, 현대 APM이 추구하는 '관측 가능성(Observability)'의 개념을 명확히 이해할 수 있습니다. - Java 환경: 바이트코드 조작
• Java Agents & Instrumentation API (Oracle Official): https://docs.oracle.com/en/java/javase/11/docs/api/java.instrument/java/lang/instrument/package-summary.html • 설명: JVM 레벨에서 코드를 동적으로 조작하기 위한 핵심 API인 java.lang.instrument 패키지의 공식 문서입니다.
• Introduction to ASM (Java Bytecode Manipulation Framework): https://asm.ow2.io/ • 설명: Scouter를 포함한 많은 Java APM들이 내부적으로 사용하는 가장 강력하고 빠른 바이트코드 조작 프레임워크인 ASM의 공식 사이트입니다. - C++ 환경: OS 및 커널 추적
• eBPF (Extended Berkeley Packet Filter) 공식 사이트: https://ebpf.io/ • 설명: 최신 C++ 및 리눅스 기반 시스템 모니터링의 핵심 기술로 떠오른 eBPF의 개념과 활용 사례를 배울 수 있습니다. - Python & Node.js 환경: 런타임 조작 및 비동기 추적
• Python sys.settrace() 공식 문서: https://docs.python.org/3/library/sys.html#sys.settrace • 설명: Python APM 에이전트가 코드 레벨 프로파일링을 수행할 때 사용하는 핵심 내장 함수의 상세 사양입니다.
• Node.js async_hooks 공식 문서: https://nodejs.org/api/async_hooks.html • 설명: Node.js의 복잡한 비동기 흐름을 추적하기 위해 APM 에이전트들이 필수적으로 사용하는 API 문서입니다. - 우리가 쓰는 도구: Scouter (스카우터)
• Scouter 공식 깃허브 위키 (Korean): https://github.com/scouter-project/scouter/wiki • 설명: 아키텍처, 설치 방법, Java/Python/Node.js 에이전트 설정 등 실무에 바로 적용할 수 있는 가장 정확한 정보가 담긴 곳입니다. 가야태자님도 이 문서를 가장 많이 참고하셨을 것입니다.
• Naver Engineering - Scouter 오픈소스化 이야기: https://d2.naver.com/helloworld/903673 • 설명: Scouter가 탄생하게 된 배경과 초기 개발팀의 고민을 엿볼 수 있는 흥미로운 블로그 글입니다.

APM이다 해줘. 내부에 흐르는 암호화 데이터는 볼 수 없는 거죠
0.00 SBD,
1.45 STEEM,
1.45 SP
[booming-kr-auto]
보팅 완료했습니다 🙌
글감을 제공해주셔서 감사합니다. ^^
로그만 안찍으면 키 같은 변수는 감시 하지 않습니다. ^^
로그를 찍으시면 보입니다.