449. 내구성 테스트 (Endurance / Soak Test) - 장시간 부하를 주어 메모리 누수(Leak) 등 확인
핵심 인사이트 (3줄 요약)
- 본질: 내구성 테스트(Endurance Test 또는 Soak Test)는 시스템에 극단적인 폭주 트래픽을 주지 않고 적당한 일상 부하를 24시간, 72시간 등 장기간(Long-term) 끝없이 주입하여 서서히 썩어가는 시스템의 결함을 찾아내는 지구력 검증 기법이다.
- 가치: 1시간짜리 부하 테스트나 스트레스 테스트로는 절대 찾을 수 없는, 가비지 컬렉터(GC)가 지우지 못한 메모리 누수(Memory Leak), 데이터베이스 커넥션 고갈, 로그 파일 용량 초과로 인한 디스크 100% 뻗음 같은 '서서히 타들어 가는 유령 버그'를 유일하게 잡아낸다.
- 융합: 자바의 힙 덤프(Heap Dump) 분석, 스레드 프로파일링(APM), 가비지 컬렉션(GC) 튜닝과 완벽하게 융합되어, "오픈 당일은 엄청 빨랐는데, 3일만 지나면 서버가 죽어요"라는 전형적인 엔터프라이즈 레거시의 저주를 타파하는 핵심 수술 도구로 쓰인다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
-
개념: Soak은 '물에 오랫동안 담가놓는다'는 뜻이다. 동시 접속자 10만 명을 때리는 게 아니다. 평범하게 동시 접속자 1천 명을 3박 4일 동안 쉬지 않고 쏴본다. 1일 차에는 서버 메모리 사용량이 30%였다가, 3일 차에 95%까지 서서히 차올라 결국 OOM(Out of Memory)으로 죽는 끔찍한 현상을 관찰한다.
-
필요성: 은행 차세대 시스템을 1,000억 원 들여 만들었다. 성능 테스트 1시간은 완벽하게 통과했다. 화려하게 오픈식까지 마쳤는데, 오픈 1주일째 되는 일요일 새벽에 모든 WAS 서버가 원인 모를 이유로 메모리를 뿜으며 전멸했다. 서버를 재부팅하니 다시 멀쩡해졌다. 그런데 또 1주일 뒤 일요일에 다 같이 죽었다. 코드를 짤 때 객체를 생성하고 지우는(Close) 한 줄을 빼먹어서, 1주일 동안 쓰레기 객체가 메모리에 산더미처럼 쌓여 터진 것이다. 이런 1주일짜리 잠복기 버그는 짧고 강한 '스트레스 테스트'로는 절대 못 잡는다. **"오래 켜놔 봐야 안다"**는 진리가 내구성 테스트의 필요성이다.
-
💡 비유: 내구성 테스트는 자동차의 **'10만 km 내구 주행 테스트'**와 같습니다. 스포츠카를 트랙에 올리고 시속 300km로 10분 달리는 것(스트레스 테스트)으로는 엔진의 파워를 알 수 있습니다. 하지만 시속 100km로 3박 4일 동안 쉬지 않고 달렸을 때(내구성 테스트), 미세하게 새던 엔진 오일이 결국 바닥나서 엔진이 녹아내리는지, 타이어 편마모가 생기는지는 오직 오랫동안 달려봐야만 잡을 수 있는 치명적 결함입니다.
-
등장 배경 및 발전 과정:
- 단기 테스트의 맹점: C/C++ 시절 포인터 메모리 해제(
free) 누락이 많았으나, 테스트가 짧아 운영 중 서버가 자주 죽어 1일 1 재부팅이 당연시되었다. - 가비지 컬렉터(GC)의 배신: Java 환경이 오며 GC가 메모리를 청소해 주어 안심했다. 그러나
HashMap같은 정적 변수에 데이터를 계속 쌓기만 하고 지우지 않으면 GC도 손을 놓아버리는 '조용한 메모리 누수'가 엔터프라이즈의 가장 큰 적이 되었다. - APM과 장기 프로파일링 (현재): 제니퍼(Jennifer)나 데이터독(Datadog) 같은 APM 에이전트를 달고 72시간 롱런 테스트를 돌려, 메모리 증가 곡선(Leak)을 눈으로 확인하고 힙 덤프를 뜨는 것이 차세대 구축 시 필수 관문(오픈 승인 조건)이 되었다.
- 단기 테스트의 맹점: C/C++ 시절 포인터 메모리 해제(
-
📢 섹션 요약 비유: 내구성 테스트는 **'양동이 밑빠진 독 테스트'**입니다. 바가지로 1분 동안 미친 듯이 물을 부어보는 것(부하 테스트)은 양동이가 튼튼한지 봅니다. 하지만 양동이에 바늘구멍(메모리 누수)이 뚫려있다면 1분 테스트로는 티도 안 납니다. 물을 적당히 틀어놓고 3일 밤낮을 내버려 둬야, 그 바늘구멍으로 물이 다 새어나가 바닥이 드러나는 끔찍한 실체를 잡아낼 수 있습니다.
Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)
1. 내구성 테스트 곡선 아키텍처
트래픽 그래프가 길고 지루하게, 끝없이 이어지는 평야와 같다.
[ VUser (트래픽 / 메모리 점유율) ]
100%| [ 결국 메모리 100% 도달 (OOM 붕괴) ] 💥
| /
| (메모리가 서서히 증가) /
| /
30%| ───────────────────/ ─> [ 트래픽은 평온하게 계속 1천 명 유지 ]
|
└-------------------------------------------------------> [ 시간 (Time) ]
1시간 24시간 48시간 72시간
- 실행 원리: 트래픽은 1천 명 수준(평소 부하의 70~80%)으로 일정하게 유지한다. 대신 APM 대시보드를 켜놓고 'Java Heap Memory' 그래프 곡선을 뚫어지게 본다. 정상 서버라면 가비지 컬렉터(GC)가 돌 때마다 톱니바퀴 모양(차오르고 깎임)을 그리며 평균 30%를 영원히 유지해야 한다. 만약 GC가 도는데도 그래프의 저점이 30% -> 40% -> 60%로 우상향 곡선을 탄다면, 100% 메모리 누수 당첨이다.
2. 내구성 테스트가 잡아내는 3대 유령 버그
- 메모리 누수 (Memory Leak)
- 개발자가 객체를
List나Map캐시에 담아놓고remove()하는 걸 깜빡했다. 3일 뒤 JVM 힙 메모리가 쓰레기로 꽉 차서OutOfMemoryError를 뿜고 서버가 즉사한다.
- 개발자가 객체를
- 커넥션 풀 (Connection Pool) / 파일 디스크립터 고갈
- DB에 쿼리를 날리고
Connection.close()를 안 했다. 1,000개가 넘는 커넥션이 3일 동안 허공에 둥둥 떠 있다가, 더 이상 DB를 잡을 수 없어 시스템이 멈춰(Hang) 버린다.
- DB에 쿼리를 날리고
- 디스크 100% 초과 (Disk Full)
- "로그는 하드디스크에 쌓이니까 괜찮아"라고 무시했다가, 에러 로그가 초당 1만 줄씩 72시간 동안 쌓여 디스크 용량 500GB를 다 채워버렸다. 리눅스 OS 자체가 디스크 쓰기 불가로 처참하게 멈춰버린다.
- 📢 섹션 요약 비유: 이 3가지 버그는 모두 **'나쁜 습관이 부른 성인병'**입니다. 밥 먹고 양치질 한 번 안 했다고 오늘 당장 썩지 않습니다(단기 테스트 통과). 하지만 1년 내내 안 닦으면(내구성 테스트) 이빨이 몽땅 썩어 내려앉는 원리입니다. 사용한 자원(메모리, DB, 파일)을 쓰고 나서 꼭 제자리(Close)에 돌려놓지 않은 나쁜 코드 습관을 찾아내는 엑스레이 검사입니다.
Ⅲ. 융합 비교 및 다각도 분석
1. 내구성(Endurance) vs 스트레스(Stress) 비교
오래 때리느냐(지구력), 강하게 때리느냐(파괴력)의 명확한 목적 차이다.
| 비교 척도 | 내구성 테스트 (Soak / Endurance) | 스트레스 테스트 (Stress) |
|---|---|---|
| 부하 주입량 | 중간 수준 (평소 트래픽의 70~80%) | 극한 수준 (임계점을 뚫어버리는 무한 트래픽) |
| 테스트 지속 시간 | 최소 12시간 ~ 72시간 (3박 4일) | 길어야 1시간 내외 (서버가 죽을 때까지만) |
| 주요 발견 결함 | 메모리 누수, 커넥션 미반환, 디스크 꽉 참 | CPU 100% 한계, 스레드 데드락, 롤백 실패 |
| 현실 예시 | 고속도로 1,000km 쉬지 않고 크루즈 주행 | 엑셀 끝까지 밟아 엔진 RPM 레드존 찍어보기 |
과목 융합 관점
-
소프트웨어 공학 (동적 분석, Dynamic Analysis): 내구성 테스트는 단순히 밖에서 부하만 줘서는 의미가 없다. 서버 내부에 동적 분석 툴(JProfiler, Eclipse MAT)을 붙여놓고 테스트를 돌려야 한다. 72시간 뒤 서버가 죽기 직전에 **힙 덤프(Heap Dump)**를 떠서 열어보면, "오마이갓!
UserSession객체 1억 개가 15GB의 메모리를 다 파먹고 있잖아!"라는 충격적인 결함 원인을 10분 만에 지목해 낼 수 있다. -
운영체제 (가비지 컬렉션, GC): 내구성 테스트의 핵심 타겟은 OS 위에 돌아가는 JVM의 **가비지 컬렉터(Garbage Collector)**다. 72시간 동안 서버가 안 뻗어도 성공이 아니다. 만약 GC가 쓰레기를 지우기 위해 'Stop-The-World (모든 앱을 1초 멈추고 청소)'를 너무 자주, 길게 호출한다면 시스템은 3일 내내 뚝뚝 끊기게 된다. 내구성 테스트를 통해 GC 알고리즘을 G1GC나 ZGC로 튜닝하는 힌트를 얻는다.
-
📢 섹션 요약 비유: 스트레스 테스트가 단거리 스프린터의 최대 심박수 엑스레이라면, 내구성 테스트는 마라톤 선수의 연골 마모도 엑스레이입니다. 아무리 심장이 튼튼해도 40km를 뛰다가 연골이 다 닳아서(메모리 누수) 주저앉으면 끝입니다. 연골이 갈리는 건 오직 40km를 끝까지 뛰어봐야만 찍을 수 있습니다.
Ⅳ. 실무 적용 및 기술사적 판단
실무 시나리오
-
시나리오 — 매주 일요일 새벽 서버를 강제 재부팅하는 끔찍한 관행: 공공기관의 모바일 앱이다. 개발팀은 원인을 찾을 수 없었다. 오픈 이후 평일엔 잘 돌다가, 토요일 밤만 되면 서버가 메모리 부족(OOM)으로 느려지다 죽었다. 결국 운영팀은 2년째 "매주 토요일 자정에 서버 20대를 자동으로 재부팅하는 크론탭(Cron) 쉘 스크립트"를 걸어놓고 땜질 처방으로 연명하고 있었다.
- 아키텍트의 해결책: 전형적인 메모리 릭(Memory Leak)에 대한 항복 선언이자 안티패턴이다. 서버를 주기적으로 껐다 켜서 쓰레기(메모리)를 날려버리는 건 병을 숨기는 진통제일 뿐이다. 아키텍트는 즉시 스테이징 서버를 똑같이 구성하고 72시간 내구성(Soak) 테스트를 걸어야 한다. APM 대시보드를 열고, 그래프가 우상향하는지 확인한 후 힙 덤프(Heap Dump)를 떠서 메모리를 꽉 쥐고 놓아주지 않는 '정적(Static) 컬렉션 변수'나 '스트림(Stream) 미해제' 로직을 찾아내어 발본색원(Close 처리) 해야만 영원한 평화를 얻을 수 있다.
-
시나리오 — 로깅(Logging) 폭탄이 부른 디스크 I/O 전면 마비: 48시간 내구성 테스트를 돌렸다. CPU도 안정적, 메모리도 톱니바퀴를 예쁘게 그리며 정상이었다. "와, 완벽하네! 퇴근합시다!" 했는데 50시간째에 서버 5대가 일제히 먹통이 되며 뻗었다. 터미널 접속(SSH)조차 불가능했다.
- 아키텍트의 해결책: 로그 로테이션(Log Rotation) 설정 누락의 재앙이다. 개발자가 에러가 터질 때마다 친절하게 스택 트레이스(Stack trace) 500줄을 파일에 쓰도록 놔뒀는데, 테스트 50시간 동안 로그 파일 용량이 100GB를 넘겨버려 OS의 디스크가 100% 꽉 차버린(Disk Full) 것이다. 아키텍트는
Logback.xml에 들어가서 "하루에 한 번 로그 파일을 쪼개고, 30일이 지난 로그는 무조건 삭제하라(MaxHistory), 압축해서 백업하라"는 로그 로테이션 및 아카이빙 정책을 강제 설정해야 한다. 디스크 용량 관리도 내구성 테스트의 핵심 지표다.
- 아키텍트의 해결책: 로그 로테이션(Log Rotation) 설정 누락의 재앙이다. 개발자가 에러가 터질 때마다 친절하게 스택 트레이스(Stack trace) 500줄을 파일에 쓰도록 놔뒀는데, 테스트 50시간 동안 로그 파일 용량이 100GB를 넘겨버려 OS의 디스크가 100% 꽉 차버린(Disk Full) 것이다. 아키텍트는
도입 체크리스트
- 조직적: 주말(Weekend)을 이용한 롱런 테스트 프로세스가 정착되어 있는가? 내구성 테스트는 3박 4일 동안 서버를 잡고 있어야 하므로 평일(개발 기간)에는 절대 돌릴 수가 없다. 목요일 퇴근할 때 젠킨스로 부하를 걸어놓고(Fire and Forget), 월요일 아침 출근해서 살아있는지(생존) 죽었는지(시체 검안, 힙 덤프) 확인하는 슬기로운 자동화 파이프라인 문화가 필수적이다.
- 기술적: 서드파티 커넥션(Third-party Connection) 누수가 없는가? 내부 DB뿐만 아니라 외부 결제 PG사 API를 쏘고 커넥션 종료(HTTP Client Close)를 제대로 안 해주는 실수가 잦다. 내구성 테스트를 돌리면 2일 차에 "PG사 연결 풀(Pool)이 고갈되었습니다"라며 서드파티 쪽에서 먼저 서버를 끊어버리는 현상을 캐치할 수 있다.
try-with-resources문법 사용을 강제하여 무조건 닫히게 코딩 표준을 잡아야 한다.
안티패턴
-
"1시간 돌려보고 x 24시간 곱해서 괜찮겠지? 추측하기": 부하 테스트를 딱 1시간만 돌려놓고, "음, 1시간에 메모리 1% 찼으니까, 24시간 지나도 24%밖에 안 차겠네? 내구성 통과!"라고 엑셀로 산수 계산을 하고 때워버리는 안티패턴. 메모리 누수나 가비지 컬렉터의 임계 폭발 현상은 엑셀의 정비례 공식대로 절대 움직이지 않는다. 인내심을 가지고 실제 72시간의 시간을 갈아 넣지 않으면 영원히 잡을 수 없다.
-
📢 섹션 요약 비유: 내구성 테스트를 산수로 추측하는 것은, 지붕에 빗방울이 1분에 1방울 떨어지는 걸 보고 "음, 하루 지나면 종이컵 한 컵 차겠네"라고 무시하는 것과 같습니다. 사실 그 빗방울이 3일 동안 천장의 나무 기둥을 썩게 만들어서 4일째에 천장 전체가 폭삭 주저앉아 집이 망가지는(메모리 누수 붕괴) 진짜 무서운 재앙을 간과한 미친 짓입니다.
Ⅴ. 기대효과 및 결론
정량/정성 기대효과
| 구분 | 1시간 단기 부하 테스트만 진행 후 오픈 (AS-IS) | 72시간 롱런 내구성 테스트 통과 후 오픈 (TO-BE) | 개선 효과 |
|---|---|---|---|
| 정량 | 1주일 주기로 메모리 부족 발생하여 새벽 강제 재부팅 | 메모리 릭(Leak) 제로화로 재부팅 없이 1년 연속 무중단 | 서버 좀비화 방지 및 무인 운영(Zero-Touch) 100% 달성 |
| 정량 | 운영 중 커넥션 고갈 시 원인 파악 및 덤프에 3일 소요 | 오픈 전 덤프 분석 및 로직 교정으로 결함 완전 도려냄 | 런타임 메모리 장애 발생에 따른 MTTR, 기회비용 제로화 |
| 정성 | "주말에 또 서버 죽으면 어떡하지?"라는 불안과 공포 | "1달을 켜놔도 절대 안 죽어"라는 완벽한 엔지니어링 신뢰 | 인프라 운영팀(Ops)과 개발팀(Dev) 간의 신뢰도 및 워라밸 급상승 |
미래 전망
- AI 기반 메모리 누수 자동 헌팅(Hunting): 과거엔 인간 아키텍트가 힙 덤프(Heap Dump)를 까서 거미줄처럼 얽힌 객체 1,000만 개를 눈으로 추적했다. 이제는 APM 솔루션에 딥러닝(AI)이 탑재되어, 24시간만 테스트를 돌려도 AI가 그래프 곡선의 미세한 패턴을 분석하여 "3일 뒤 OOM 발생 예정! 범인은
OrderService45번 줄의List객체 미해제입니다!"라고 족집게처럼 정답을 프린트해 주는 시대로 진입했다. - 컨테이너 기반 자가 치유(Self-Healing)의 양면성: 쿠버네티스(k8s)의 무서운 점은, 파드(Pod)가 메모리 누수로 죽으려고 하면 LMK(Low Memory Killer)로 팍 죽여버리고 즉시 새 파드를 띄워버린다는 것이다. 밖에서 보면 서비스가 1초도 안 멈추고 돌아가는 것처럼 보이기 때문에, 멍청한 개발자들은 자기 코드에 메모리 누수가 10년째 있는 줄도 모르고 살아가는 '좀비 코딩'의 역설적 시대가 열렸다. 그래서 역설적으로 롱런 모니터링이 더 중요해졌다.
참고 표준
- OOM (Out Of Memory Error): 자바 힙(Heap) 메모리가 100% 꽉 차서 쓰레기 청소(GC)를 미친 듯이 돌려도 더 이상 1바이트의 객체도 생성할 수 없을 때 JVM이 비명을 지르며 서버를 죽여버리는 치명적 에러. 내구성 테스트가 잡으려는 최종 빌런.
- Heap Dump (힙 덤프): 서버가 OOM으로 뻗기 직전의 메모리 상태(누가 10GB를 파먹고 있는지)를 찰칵! 하고 사진 찍어서 파일(
.hprof)로 저장해 내는 기술. 범인을 잡는 가장 완벽한 엑스레이 필름.
내구성 테스트(Soak Test)는 단순히 시간을 버티는 훈련이 아니다. 그것은 소프트웨어가 시간이라는 가장 가혹한 풍화 작용 앞에서도 썩거나 닳지 않고, 영속성(Immortality)을 가질 수 있는가를 증명하는 우아한 연금술이다. 1시간을 버티는 코드는 학원생도 짤 수 있다. 하지만 1,000시간을 켜두어도 메모리가 단 1바이트도 새지 않고, 디스크 용량이 안정적인 톱니바퀴를 그리며 도는 '시간을 이겨내는 시스템'은 오직 보이지 않는 누수 구멍을 찾아 덤프(Dump)를 뒤지며 밤을 새운 집요한 아키텍트만이 만들어낼 수 있는 숭고한 장인정신의 결정체다.
- 📢 섹션 요약 비유: 내구성 테스트는 우주정거장의 **'산소 누출 테스트'**입니다. 땅에서 10분 동안 산소가 잘 나오는 걸 확인(단기 테스트)하고 우주로 쏘아 올렸는데, 사실 눈에 보이지 않는 바늘구멍으로 산소가 1분에 0.01g씩 새고 있었다면 우주비행사는 1주일 뒤 우주 미아가 되어 질식사합니다. 1주일 내내 지상에서 밀폐시켜 놓고(내구성 테스트) 공기가 1g이라도 주는지 확인해야만 진짜 우주(오픈)로 나갈 자격이 있습니다.
📌 관련 개념 맵 (Knowledge Graph)
| 개념 명칭 | 관계 및 시너지 설명 |
|---|---|
| 스트레스 테스트 (Stress Test) | 강하고 짧게 후려쳐서 뼈가 부러지는지 보는 파괴 테스트. 내구성 테스트가 서서히 독약을 먹여서 피를 말려 죽이는 롱런 테스트라면, 정반대의 성격을 지님. |
| 메모리 누수 (Memory Leak) | 내구성 테스트가 잡아야 하는 제1의 암살자. 개발자가 다 쓴 객체를 휴지통에 안 버리고 계속 주머니(List/Map)에 넣고 다녀서 결국 주머니가 터져 죽는 현상. |
| 가비지 컬렉션 (Garbage Collection) | 메모리 누수를 치워주는 청소부 로봇. 하지만 개발자가 변수를 강하게 쥐고 놓지 않으면, 이 똑똑한 로봇조차 무서워서 쓰레기를 못 지우고 OOM 파국을 맞이한다. |
| 힙 덤프 (Heap Dump) | 72시간의 내구성 테스트 끝에 서버가 죽었을 때, "도대체 누가 메모리를 파먹었어!"라고 범인을 찾기 위해 서버 뇌(메모리)를 해부해 보는 엑스레이 파일. |
| APM (Application Performance Monitoring) | 제니퍼, 데이터독 등 내구성 테스트 72시간 내내 모니터에 메모리와 CPU 곡선을 우상향하는지 톱니바퀴를 그리는지 감시해 주는 24시간 심박수 모니터링 기계. |
👶 어린이를 위한 3줄 비유 설명
- 내가 물총에 물을 가득 채우고 10번만 쏴봤을 땐 물이 줄어드는 걸 거의 못 느꼈어요(단기 테스트 통과). 완벽하다고 생각했죠.
- 그런데 그 물총을 베란다에 3일 동안 그냥 놔두었더니, 아주 작은 바늘구멍으로 물이 한 방울씩 새어 나가서 3일 뒤엔 물총이 텅 비어버렸어요(메모리 누수)!
- 이렇게 10분 돌려보고 괜찮다고 믿는 게 아니라, 아주 오랜 시간(3일, 7일) 동안 지켜보면서 아주 미세하게 고장 나거나 새는 곳이 없는지 끝까지 확인하는 인내심 테스트를 **'내구성 테스트'**라고 부른답니다!