Go 성능 분석과 최적화 입문: pprof로 병목 지점 찾는 방법
테스트까지 작성했다면, 이제 한 단계 더 나아가
**“이 코드가 얼마나 효율적으로 동작하는가”**를 고민하게 된다.
Go는 성능을 직접 튜닝하지 않아도
상당히 준수한 결과를 내는 편이지만,
트래픽이 늘거나 처리량이 중요해지는 순간
막연한 추측이 아니라 근거 있는 분석이 필요해진다.
이때 사용하는 도구가 바로 pprof다.
이 글에서는
- pprof가 무엇인지
- 어떤 성능 문제를 볼 수 있는지
- 실무에서 최소한으로 활용하는 방법
을 중심으로 정리한다.
pprof는 무엇을 해주는 도구인가
pprof는 Go에서 제공하는 성능 프로파일링 도구다.
코드를 뜯어보거나 로그를 찍지 않아도, 다음 정보를 수집할 수 있다.
- CPU를 어디서 많이 쓰는지
- 메모리를 어디서 할당하는지
- goroutine이 왜 늘어나는지
- 어떤 함수가 병목인지
중요한 점은
**“느린 이유를 추측하지 않게 해준다”**는 것이다.
언제 pprof를 써야 할까
pprof는 이런 상황에서 의미가 있다.
- 응답 속도가 점점 느려질 때
- CPU 사용률이 비정상적으로 높을 때
- 메모리 사용량이 계속 증가할 때
- goroutine이 줄지 않고 쌓일 때
⚠️ 주의할 점
아직 문제가 없는데 미리 최적화하려고 pprof를 보는 건
대부분 시간 낭비가 된다.
문제가 관측된 이후에 사용하는 게 맞다.
pprof 사용 준비: net/http/pprof
가장 간단한 시작 방법은
표준 라이브러리의 net/http/pprof를 사용하는 것이다.
import _ "net/http/pprof"
그리고 HTTP 서버를 실행하면 끝이다.
go func() {
http.ListenAndServe("localhost:6060", nil)
}()
이렇게 하면 다음 엔드포인트가 자동으로 열린다.
- /debug/pprof/
- /debug/pprof/profile
- /debug/pprof/heap
[이미지: Go pprof HTTP 엔드포인트 구조]
실무에서는
- 내부 포트에서만 열거나
- 인증 뒤에서만 접근 가능하게
구성하는 게 일반적이다.
CPU 프로파일링 기본 흐름
CPU 사용량 분석은 보통 이렇게 진행한다.
go tool pprof http://localhost:6060/debug/pprof/profile
- 기본 30초 동안 CPU 사용 정보 수집
- 이후 pprof 콘솔 진입
(pprof)
이 상태에서 다양한 명령어를 사용할 수 있다.
가장 먼저 보는 명령어: top
(pprof) top
- CPU를 가장 많이 사용한 함수 목록
- “어디가 느린지”를 빠르게 파악 가능
실무에서는
이 결과만으로도
“건드려야 할 곳”이 명확해지는 경우가 많다.
호출 흐름 보기: web
(pprof) web
- 함수 호출 관계를 그래프로 시각화
- 어떤 경로에서 병목이 생겼는지 한눈에 확인
[이미지: pprof call graph 예시]
이 그래프를 보면
“생각보다 여기서 시간이 많이 쓰이네”라는 포인트가 거의 항상 나온다.
메모리 프로파일링(heap)
메모리 문제를 볼 때는 heap 프로파일을 사용한다.
go tool pprof http://localhost:6060/debug/pprof/heap
여기서 주로 보는 건 다음이다.
- 메모리를 많이 할당하는 함수
- 반복적으로 생성되는 객체
- 해제되지 않고 남아 있는 구조
⚠️ 주의할 점
메모리 사용량이 많다고 해서
무조건 문제가 있는 건 아니다.
증가 추세(leak) 가 있는지가 핵심이다.
goroutine 프로파일링
goroutine이 계속 늘어난다면
대개 다음 중 하나다.
- channel 송수신이 막힘
- goroutine 종료 조건이 없음
- context 취소 처리가 누락됨
pprof에서는 goroutine 상태도 확인할 수 있다.
go tool pprof http://localhost:6060/debug/pprof/goroutine
이 정보를 보면
어디서 goroutine이 멈춰 있는지 비교적 명확하게 드러난다.
[이미지: goroutine 상태 분포 예시]
벤치마크 테스트와 pprof
pprof는 서버뿐 아니라
벤치마크 테스트와도 함께 사용할 수 있다.
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(1, 2)
}
}
go test -bench=. -cpuprofile=cpu.out
go tool pprof cpu.out
이 방식은
- 특정 함수 성능 비교
- 리팩터링 전/후 차이 확인
에 매우 유용하다.
pprof를 쓸 때 흔한 실수
⚠️ 자주 하는 실수
- top 결과만 보고 바로 최적화
- 전체 비율이 아닌 단일 수치만 보고 판단
- IO 대기 시간을 CPU 문제로 착각
pprof 결과는 항상
“왜 이런 결과가 나왔는지”를 함께 해석해야 한다.
실무에서의 현실적인 사용 기준
실제로는 이렇게 사용하는 경우가 많다.
- 지표에서 이상 징후 발견
- pprof로 병목 후보 확인
- 한두 군데만 개선
- 다시 측정
모든 코드를 빠르게 만드는 게 아니라,
가장 영향이 큰 부분만 건드리는 것이 핵심이다.
정리
pprof는
성능 문제를 감으로 다루지 않게 해주는 도구다.
- CPU, 메모리, goroutine 상태 확인 가능
- 실제 데이터 기반으로 판단 가능
- 최소한의 노력으로 큰 효과를 낼 수 있다
여기까지 왔다면,
Go 언어의 문법 → 구조 → 동시성 → 운영 → 성능까지
한 사이클을 전부 경험한 셈이다.
다음 단계로는
- 간단한 API 서버 실습 시리즈
- 실제 장애 사례 기반 성능 개선
- Go로 작성한 프로젝트 회고
같은 콘텐츠로 확장해도 자연스럽다.