{"componentChunkName":"component---src-templates-blog-post-js","path":"/etc/k6-부하-테스트-시나리오-설계와-GitHub-Actions-CI-게이트/","result":{"data":{"site":{"siteMetadata":{"title":"Bottlehs Tech Blog"}},"markdownRemark":{"id":"06026bd6-97b4-51fc-9539-0fc66c2bbd40","excerpt":"k…","html":"<p><img src=\"/assets/etc.png\" alt=\"k6 부하 테스트\" title=\"k6 부하 테스트\"></p>\n<p>성능 문제는 <strong>사용자가 느끼기 전에 발견하기 어렵다</strong>. 로컬에서는 빠른데 스테이징에서 느리고, 스테이징에서는 괜찮은데 프로덕션에서만 터지는 일은 흔하다. 그래서 부하 테스트는 여전히 <strong>가장 비싼 보험 중 하나</strong>다. 잘못하면 시간만 쓰고 결론은 “뭔가 느림”으로 끝나지만, 잘하면 <strong>배포 전에 회귀를 잡고</strong>, 용량 계획과 <strong>SLO</strong> 논의의 근거가 된다.</p>\n<p>이 글은 <strong>Grafana k6</strong>를 기준으로, 스크립트 작성부터 <strong>시나리오 설계</strong>, <strong>지표 읽기</strong>, <strong>CI에 게이트로 묶는 방법</strong>까지 한 흐름으로 정리한다. 특정 클라우드 벤더에 종속되지 않으려는 팀에도 맞도록 구성했다.</p>\n<h2 id=\"왜-k6인가\" style=\"position:relative;\"><a href=\"#%EC%99%9C-k6%EC%9D%B8%EA%B0%80\" aria-label=\"왜 k6인가 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>왜 k6인가</h2>\n<p>부하 도구는 JMeter, Gatling, Locust 등 선택지가 많다. k6가 자주 선택되는 이유는 대략 다음과 같다.</p>\n<ol>\n<li><strong>코드로 시나리오를 쓴다</strong>(JavaScript 호환 문법). Git으로 버전 관리하고 리뷰하기 쉽다.</li>\n<li><strong>CLI가 단순</strong>하고 CI에 붙이기 좋다.</li>\n<li>Grafana 스택과의 연계, 클라우드 실행(k6 Cloud) 등 운영 경로가 정리되어 있다.</li>\n</ol>\n<p>반면 <strong>브라우저 렌더링</strong>이나 복잡한 GUI 시나리오는 다른 도구가 나을 수 있다. k6는 주로 <strong>HTTP/gRPC 등 프로토콜 레벨</strong> 부하에 강하다.</p>\n<h2 id=\"최소-스크립트와-실행\" style=\"position:relative;\"><a href=\"#%EC%B5%9C%EC%86%8C-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%99%80-%EC%8B%A4%ED%96%89\" aria-label=\"최소 스크립트와 실행 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>최소 스크립트와 실행</h2>\n<p>아래는 개념을 보여주는 예시다. 실제 URL·헤더·페이로드는 서비스에 맞게 바꾼다.</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">import</span> http <span class=\"token keyword\">from</span> <span class=\"token string\">\"k6/http\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> check<span class=\"token punctuation\">,</span> sleep <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"k6\"</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> options <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">vus</span><span class=\"token operator\">:</span> <span class=\"token number\">10</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">duration</span><span class=\"token operator\">:</span> <span class=\"token string\">\"30s\"</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> res <span class=\"token operator\">=</span> http<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"https://example.com/api/health\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function\">check</span><span class=\"token punctuation\">(</span>res<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token string-property property\">\"status is 200\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">r</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> r<span class=\"token punctuation\">.</span>status <span class=\"token operator\">===</span> <span class=\"token number\">200</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function\">sleep</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>실행은 <code class=\"language-text\">k6 run script.js</code> 한 줄로 끝난다. 여기서 중요한 것은 <strong>“돌아간다”가 아니라 “무엇을 측정하는가”</strong>다.</p>\n<h2 id=\"부하-모델-vu-rps-스테이지\" style=\"position:relative;\"><a href=\"#%EB%B6%80%ED%95%98-%EB%AA%A8%EB%8D%B8-vu-rps-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80\" aria-label=\"부하 모델 vu rps 스테이지 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>부하 모델: VU, RPS, 스테이지</h2>\n<p>용어를 정확히 쓰자.</p>\n<ul>\n<li><strong>VU(Virtual User)</strong>: 동시에 시나리오를 수행하는 가상 사용자 수다. 생각보다 “동시 접속자”와 일대일로 대응하지 않는다. 사용자 한 명이 짧은 요청을 자주 보내면 VU는 적어도 RPS는 높을 수 있다.</li>\n<li><strong>RPS(Requests Per Second)</strong>: 초당 요청 수. 시스템 처리량의 핵심 지표 중 하나다.</li>\n<li><strong>스테이지(ramp-up/ramp-down)</strong>: VU나 목표 RPS를 시간에 따라 올리고 내리는 구간.</li>\n</ul>\n<p>잘못된 부하 테스트의 대표는 <strong>“그냥 VU 1000으로 때려본다”</strong>다. 현실 트래픽 패턴과 다르면, 병목이 엉뚱한 곳으로 보이거나 반대로 숨는다.</p>\n<h3 id=\"현실적인-패턴-예시\" style=\"position:relative;\"><a href=\"#%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%98%88%EC%8B%9C\" aria-label=\"현실적인 패턴 예시 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>현실적인 패턴 예시</h3>\n<ol>\n<li><strong>점진적 증가</strong>: 낮은 부하에서 시작해 단계적으로 올린다. 어디서 응답 시간이 꺾이는지 본다.</li>\n<li><strong>스파이크</strong>: 짧은 시간에 급증하는 트래픽(이벤트, 뉴스 알림)을 흉내 낸다.</li>\n<li><strong>스트레스</strong>: 한계까지 밀어 <strong>어디가 먼저 무너지는지</strong> 확인한다. 한계치 자체가 문서화된다.</li>\n</ol>\n<p>k6에서는 <code class=\"language-text\">scenarios</code>로 여러 패턴을 동시에 돌릴 수 있다. 팀은 “우리 서비스의 피크 시간대 그래프”를 <strong>대략이라도</strong> 그려 두고 시나리오에 반영해야 한다.</p>\n<h2 id=\"임계값thresholds과-slo-연결\" style=\"position:relative;\"><a href=\"#%EC%9E%84%EA%B3%84%EA%B0%92thresholds%EA%B3%BC-slo-%EC%97%B0%EA%B2%B0\" aria-label=\"임계값thresholds과 slo 연결 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>임계값(thresholds)과 SLO 연결</h2>\n<p>부하 테스트의 산출물은 그래프가 아니라 <strong>합격/불합격 기준</strong>이어야 한다. k6의 <code class=\"language-text\">thresholds</code>는 그 역할을 한다.</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> options <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">thresholds</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">http_req_duration</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"p(95)&lt;500\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">http_req_failed</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"rate&lt;0.01\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>여기서 <strong>p95 지연 500ms 미만</strong> 같은 숫자는 어디서 오는가? 하늘에서 떨어지지 않는다. 제품의 <strong>SLO</strong>(예: “핵심 API p95 300ms”)에서 완화된 버전을 두거나, 스테이징 환경 한계를 반영해 조정한다. 중요한 것은 <strong>이 숫자를 서비스 오너와 합의</strong>했다는 사실이다.</p>\n<h3 id=\"check와-임계값의-차이\" style=\"position:relative;\"><a href=\"#check%EC%99%80-%EC%9E%84%EA%B3%84%EA%B0%92%EC%9D%98-%EC%B0%A8%EC%9D%B4\" aria-label=\"check와 임계값의 차이 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>check()와 임계값의 차이</h3>\n<ul>\n<li><code class=\"language-text\">check()</code>는 <strong>요청 단위 검증</strong>이다. 비즈니스적으로 “장바구니 합계가 맞는가” 같은 것도 넣을 수 있다.</li>\n<li><code class=\"language-text\">thresholds</code>는 <strong>전체 집계</strong>에 대한 패스 조건이다.</li>\n</ul>\n<p>둘 다 없으면 “높은 RPS인데 전부 500 에러” 같은 상황을 놓칠 수 있다.</p>\n<h2 id=\"데이터-준비와-테스트-격리\" style=\"position:relative;\"><a href=\"#%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A4%80%EB%B9%84%EC%99%80-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B2%A9%EB%A6%AC\" aria-label=\"데이터 준비와 테스트 격리 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>데이터 준비와 테스트 격리</h2>\n<p>부하 테스트는 <strong>프로덕션에서 함부로 돌리면 안 된다</strong>는 말이 먼저다. 데이터가 오염되거나, 결제·알림이 실제로 발송될 수 있다.</p>\n<p>권장 패턴은 다음과 같다.</p>\n<ol>\n<li><strong>전용 스테이징</strong>에 최대한 프로덕션과 비슷한 데이터 볼륨(익명화)을 둔다.</li>\n<li><strong>idempotency key</strong>와 <strong>테스트 전용 플래그</strong>로 부작용을 차단한다.</li>\n<li><strong>캐시를 비운 상태</strong>와 <strong>캐시가 찬 상태</strong>를 둘 다 본다. 결과가 크게 다를 수 있다.</li>\n</ol>\n<h2 id=\"메트릭-해석-무엇을-봐야-하는가\" style=\"position:relative;\"><a href=\"#%EB%A9%94%ED%8A%B8%EB%A6%AD-%ED%95%B4%EC%84%9D-%EB%AC%B4%EC%97%87%EC%9D%84-%EB%B4%90%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80\" aria-label=\"메트릭 해석 무엇을 봐야 하는가 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>메트릭 해석: 무엇을 봐야 하는가</h2>\n<p>k6 기본 메트릭 중에서 특히 자주 보는 것은 다음과 같다.</p>\n<ul>\n<li><strong>http<em>req</em>duration</strong>: 지연 분포. p95, p99를 집중적으로 본다.</li>\n<li><strong>http<em>req</em>failed</strong>: 실패율. 타임아웃·5xx·4xx를 구분해 원인을 좁힌다.</li>\n<li><strong>iterations</strong>: 시나리오 반복 횟수. 스크립트 로직이 의도대로인지 확인한다.</li>\n<li><strong>vus / vus_max</strong>: 동시성 수준.</li>\n</ul>\n<p>여기에 애플리케이션 메트릭(CPU, GC, DB 커넥션 풀 대기, 쿼리 지연)을 <strong>함께</strong> 봐야 병목이 어디인지 알 수 있다. k6만 보면 “느리다”밖에 안 나온다.</p>\n<h3 id=\"병목-후보-체크리스트\" style=\"position:relative;\"><a href=\"#%EB%B3%91%EB%AA%A9-%ED%9B%84%EB%B3%B4-%EC%B2%B4%ED%81%AC%EB%A6%AC%EC%8A%A4%ED%8A%B8\" aria-label=\"병목 후보 체크리스트 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>병목 후보 체크리스트</h3>\n<ol>\n<li><strong>애플리케이션 CPU 100%</strong>: 코드 최적화 또는 인스턴스 수평 확장.</li>\n<li><strong>DB CPU 또는 디스크 I/O</strong>: 쿼리·인덱스·연결 풀.</li>\n<li><strong>네트워크 RTT</strong>: 외부 API 의존. 타임아웃·캐시·회로 차단기.</li>\n<li><strong>락 경합</strong>: DB 행 락, Redis 단일 키, 메시지 큐 소비 지연.</li>\n</ol>\n<h2 id=\"grpcwebsocket\" style=\"position:relative;\"><a href=\"#grpcwebsocket\" aria-label=\"grpcwebsocket permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>gRPC·WebSocket</h2>\n<p>HTTP 외 프로토콜을 쓴다면 별도 모듈·스크립트 패턴이 필요하다. 핵심은 동일하다. <strong>동시성 모델</strong>, <strong>페이로드 크기</strong>, <strong>스트림</strong> 특성을 반영해 시나리오를 나눈다.</p>\n<h2 id=\"github-actions에-붙이기--ci-게이트\" style=\"position:relative;\"><a href=\"#github-actions%EC%97%90-%EB%B6%99%EC%9D%B4%EA%B8%B0--ci-%EA%B2%8C%EC%9D%B4%ED%8A%B8\" aria-label=\"github actions에 붙이기  ci 게이트 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>GitHub Actions에 붙이기 — CI 게이트</h2>\n<p>“매 PR마다 풀 부하”는 비용과 안정성 문제로 어렵다. 현실적인 타협은 다음과 같다.</p>\n<ol>\n<li><strong>스모크 수준</strong>: 짧은 시간, 적은 VU로 <strong>회귀만 감지</strong>한다.</li>\n<li><strong>메인 브랜치 야간</strong>: 긴 시나리오는 스케줄 워크플로에서 돌린다.</li>\n<li><strong>릴리즈 직전</strong>: 수동 승인 후 강한 부하.</li>\n</ol>\n<h3 id=\"워크플로-개념\" style=\"position:relative;\"><a href=\"#%EC%9B%8C%ED%81%AC%ED%94%8C%EB%A1%9C-%EA%B0%9C%EB%85%90\" aria-label=\"워크플로 개념 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>워크플로 개념</h3>\n<ul>\n<li><code class=\"language-text\">k6 run</code>이 <strong>exit code 0</strong>이면 통과, 임계값 실패 시 비제로로 실패하게 한다.</li>\n<li>스테이징 URL은 <strong>시크릿</strong>에 두고, 필요하면 <strong>VPN</strong>이나 <strong>허용 IP</strong>를 맞춘다.</li>\n<li>결과를 <strong>아티팩트</strong>로 저장하거나 Grafana에 푸시한다.</li>\n</ul>\n<p>YAML 예시는 환경마다 다르므로, 여기서는 단계만 적는다.</p>\n<ol>\n<li>k6 바이너리 설치(또는 컨테이너)</li>\n<li>스크립트 체크아웃</li>\n<li><code class=\"language-text\">k6 run</code> + 임계값</li>\n<li>실패 시 PR 코멘트 또는 Slack 알림</li>\n</ol>\n<p>이렇게 해야 “어제까지 됐는데 오늘 느려짐”을 <strong>코드 변경과 연결</strong>할 수 있다.</p>\n<h2 id=\"성능-테스트-조직-문화\" style=\"position:relative;\"><a href=\"#%EC%84%B1%EB%8A%A5-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A1%B0%EC%A7%81-%EB%AC%B8%ED%99%94\" aria-label=\"성능 테스트 조직 문화 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>성능 테스트 조직 문화</h2>\n<p>도구만으로는 부족하다. 다음이 있어야 한다.</p>\n<ul>\n<li><strong>성능 예산</strong>(예: 목록 API 한 페이지당 p95 400ms)</li>\n<li><strong>환경 동등성</strong>(스테이징이 프로덕션과 얼마나 비슷한지 문서화)</li>\n<li><strong>회귀 대응 프로세스</strong>(실패한 PR을 누가, 어떤 기준으로 머지할지)</li>\n</ul>\n<p>k6는 이런 문화를 <strong>코드와 숫자로 고정</strong>하는 역할을 한다.</p>\n<h2 id=\"흔한-실수\" style=\"position:relative;\"><a href=\"#%ED%9D%94%ED%95%9C-%EC%8B%A4%EC%88%98\" aria-label=\"흔한 실수 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>흔한 실수</h2>\n<ol>\n<li><strong>캐시 히트율 100%인 부하</strong>: 현실이 아니다.</li>\n<li><strong>think time 없음</strong>: 사용자가 페이지를 읽는 시간 없이 API만 미친 듯이 호출한다.</li>\n<li><strong>클라이언트 한 대에서만</strong>: 네트워크 대역·파일 디스크립터 한계로 <strong>가짜 상한</strong>이 생긴다. 필요하면 분산 실행을 검토한다.</li>\n<li><strong>데이터 의존 순서 무시</strong>: 주문→결제처럼 상태가 있는 시나리오를 한 요청으로 때우면 실패율만 올라간다.</li>\n</ol>\n<h2 id=\"장기-운영-결과를-축적하라\" style=\"position:relative;\"><a href=\"#%EC%9E%A5%EA%B8%B0-%EC%9A%B4%EC%98%81-%EA%B2%B0%EA%B3%BC%EB%A5%BC-%EC%B6%95%EC%A0%81%ED%95%98%EB%9D%BC\" aria-label=\"장기 운영 결과를 축적하라 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>장기 운영: 결과를 축적하라</h2>\n<p>한 번 찍고 끝내면 부하 테스트는 금방 썩는다. <strong>같은 시나리오를 주기적으로</strong> 돌리고, 커밋 해시·환경 버전·DB 사이즈를 메타데이터로 남겨라. 그래프가 “예전보다 느려졌다”를 <strong>증명</strong>할 수 있다.</p>\n<h2 id=\"정리\" style=\"position:relative;\"><a href=\"#%EC%A0%95%EB%A6%AC\" aria-label=\"정리 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>정리</h2>\n<p>k6는 학습 곡선이 비교적 완만하고, <strong>코드 리뷰 가능한 부하 시나리오</strong>를 만든다는 점에서 팀 도구로 잘 맞는다. 핵심은 도구가 아니라 <strong>부하 모델과 임계값을 비즈니스와 합의</strong>하는 일이다. 그 합의가 있으면 CI 게이트는 자연스럽게 생기고, 성능 이슈는 “누군가의 감”이 아니라 <strong>계약</strong>이 된다.</p>\n<p>서비스가 커질수록 성능은 기능과 같다. 사용자는 changelog를 읽지 않는다. <strong>체감 속도</strong>로 평가한다. k6는 그 체감을 배포 전에 재현하게 돕는 도구다. 오늘 스테이징에서 한 번, 임계값을 적어 보고 실패해 보아라. 실패가 보이면 그건 좋은 신호다. 프로덕션에서 터지기 전에 터진 것이니까.</p>\n<p>마지막으로, 부하 테스트 결과는 <strong>회의 자료 한 장</strong>으로 남기길 권한다. 그래프 스크린샷, 임계값 표, 환경 스펙(DB 사이즈, 인스턴스 타입), 실행한 k6 버전까지. 3개월 뒤의 성능 이슈는 거의 항상 “그때와 지금이 다른가?”에서 시작하니까, 기록이 곧 방패다.</p>\n<h2 id=\"부록-스크립트-품질을-올리는-작은-습관\" style=\"position:relative;\"><a href=\"#%EB%B6%80%EB%A1%9D-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%92%88%EC%A7%88%EC%9D%84-%EC%98%AC%EB%A6%AC%EB%8A%94-%EC%9E%91%EC%9D%80-%EC%8A%B5%EA%B4%80\" aria-label=\"부록 스크립트 품질을 올리는 작은 습관 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>부록: 스크립트 품질을 올리는 작은 습관</h2>\n<ul>\n<li>시나리오 파일을 <strong>도메인별로 분리</strong>하고 공통 헬퍼를 둔다.</li>\n<li><strong>태그</strong>로 엔드포인트 그룹을 나눠 리포트에서 비교한다.</li>\n<li><strong>환경 변수</strong>로 베이스 URL·토큰을 주입하고, 기본값을 프로덕션에 두지 않는다.</li>\n</ul>\n<p>이 세 가지만 지켜도, 6개월 뒤의 자신이 고마워할 것이다.</p>\n<h2 id=\"시나리오scenarios로-여러-패턴-동시에-돌리기\" style=\"position:relative;\"><a href=\"#%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4scenarios%EB%A1%9C-%EC%97%AC%EB%9F%AC-%ED%8C%A8%ED%84%B4-%EB%8F%99%EC%8B%9C%EC%97%90-%EB%8F%8C%EB%A6%AC%EA%B8%B0\" aria-label=\"시나리오scenarios로 여러 패턴 동시에 돌리기 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>시나리오(scenarios)로 여러 패턴 동시에 돌리기</h2>\n<p><code class=\"language-text\">export const options</code> 안에 <code class=\"language-text\">scenarios</code>를 정의하면, 한 번의 <code class=\"language-text\">k6 run</code>으로 <strong>서로 다른 트래픽 패턴</strong>을 병렬에 가깝게 섞을 수 있다. 예를 들어 “일반 조회 API”와 “무거운 리포트 API”를 동시에 때려 <strong>리소스 경합</strong>을 본다. 실제 피크 시간에는 사용자 행동이 단일 패턴이 아니기 때문이다.</p>\n<p>설계할 때는 각 시나리오에 <strong>명확한 exec 함수</strong>를 나누고, 공통 헬퍼는 <code class=\"language-text\">import</code>로 묶는다. 이렇게 해야 스크립트가 길어져도 <strong>리뷰 가능한 구조</strong>를 유지한다.</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token comment\">// 개념 예시 — 실제 옵션 키는 k6 문서와 버전을 확인할 것</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> options <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">scenarios</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">browse</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token literal-property property\">executor</span><span class=\"token operator\">:</span> <span class=\"token string\">\"ramping-vus\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">startVUs</span><span class=\"token operator\">:</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">stages</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">duration</span><span class=\"token operator\">:</span> <span class=\"token string\">\"2m\"</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">target</span><span class=\"token operator\">:</span> <span class=\"token number\">50</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">duration</span><span class=\"token operator\">:</span> <span class=\"token string\">\"5m\"</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">target</span><span class=\"token operator\">:</span> <span class=\"token number\">50</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">duration</span><span class=\"token operator\">:</span> <span class=\"token string\">\"2m\"</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">target</span><span class=\"token operator\">:</span> <span class=\"token number\">0</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">exec</span><span class=\"token operator\">:</span> <span class=\"token string\">\"browseFlow\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">spike</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token literal-property property\">executor</span><span class=\"token operator\">:</span> <span class=\"token string\">\"constant-vus\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">vus</span><span class=\"token operator\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">duration</span><span class=\"token operator\">:</span> <span class=\"token string\">\"1m\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">startTime</span><span class=\"token operator\">:</span> <span class=\"token string\">\"3m\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">exec</span><span class=\"token operator\">:</span> <span class=\"token string\">\"spikeFlow\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">function</span> <span class=\"token function\">browseFlow</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">/* ... */</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">function</span> <span class=\"token function\">spikeFlow</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">/* ... */</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p><code class=\"language-text\">startTime</code>으로 시나리오 간 <strong>시간 순서</strong>를 주면, “점진적 증가 후 갑자기 스파이크” 같은 현실적인 시퀀스를 연출할 수 있다.</p>\n<h2 id=\"목표-rps를-쓰는-경우-arrival-rate-실행기\" style=\"position:relative;\"><a href=\"#%EB%AA%A9%ED%91%9C-rps%EB%A5%BC-%EC%93%B0%EB%8A%94-%EA%B2%BD%EC%9A%B0-arrival-rate-%EC%8B%A4%ED%96%89%EA%B8%B0\" aria-label=\"목표 rps를 쓰는 경우 arrival rate 실행기 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>목표 RPS를 쓰는 경우: arrival-rate 실행기</h2>\n<p>VU 기반 모델은 “사용자 동시성”에 직관적이지만, <strong>목표 처리량을 고정</strong>하고 싶을 때는 <code class=\"language-text\">constant-arrival-rate</code> 같은 실행기를 쓴다. 예를 들어 “초당 500건의 주문 생성 요청을 유지할 수 있는가?”처럼 <strong>비즈니스 KPI</strong>와 직결된 질문에 맞춘다.</p>\n<p>이때 주의할 점은, 시스템이 목표 RPS를 감당하지 못하면 <strong>iteration이 밀리고</strong> VU가 부족하다는 신호가 뜬다는 것이다. 그래서 <strong>VU 상한을 넉넉히</strong> 잡거나, 목표 RPS를 단계적으로 올린다.</p>\n<h2 id=\"커스텀-메트릭과-비즈니스-검증\" style=\"position:relative;\"><a href=\"#%EC%BB%A4%EC%8A%A4%ED%85%80-%EB%A9%94%ED%8A%B8%EB%A6%AD%EA%B3%BC-%EB%B9%84%EC%A6%88%EB%8B%88%EC%8A%A4-%EA%B2%80%EC%A6%9D\" aria-label=\"커스텀 메트릭과 비즈니스 검증 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>커스텀 메트릭과 비즈니스 검증</h2>\n<p>HTTP 지연만으로는 부족할 때가 있다. 예를 들어 결제 API는 200이어도 <strong>응답 본문의 <code class=\"language-text\">status: \"failed\"</code></strong>일 수 있다. 이때 <code class=\"language-text\">Counter</code>나 <code class=\"language-text\">Rate</code>를 정의해 <strong>비즈니스 실패율</strong>을 따로 본다.</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> Rate <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"k6/metrics\"</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> bizFail <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Rate</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"biz_payment_failed\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> res <span class=\"token operator\">=</span> http<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token comment\">/* ... */</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> body <span class=\"token operator\">=</span> res<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  bizFail<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>body<span class=\"token punctuation\">.</span>ok <span class=\"token operator\">!==</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>그리고 <code class=\"language-text\">thresholds</code>에 <code class=\"language-text\">biz_payment_failed</code>를 걸어 <strong>“HTTP는 성공인데 장사는 실패”</strong>를 잡는다.</p>\n<h2 id=\"소켓-테스트-분산-실행과-한계\" style=\"position:relative;\"><a href=\"#%EC%86%8C%EC%BC%93-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%B6%84%EC%82%B0-%EC%8B%A4%ED%96%89%EA%B3%BC-%ED%95%9C%EA%B3%84\" aria-label=\"소켓 테스트 분산 실행과 한계 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>소켓 테스트: 분산 실행과 한계</h2>\n<p>한 대의 k6 프로세스는 <strong>네트워크·CPU</strong> 한계로 RPS 상한이 있다. 더 높은 부하가 필요하면 k6 <strong>분산 실행</strong>(execution segments)이나 클라우드 실행을 검토한다. 이때는 <strong>동일 시나리오 버전</strong>을 고정하고, <strong>클럭 동기화</strong> 이슈를 의식해야 한다.</p>\n<p>반대로 “우리 회사 노트북 한 대로 프로덕션과 같은 수준”을 기대하면 안 된다. 부하 생성기 자체가 병목이 되면 <strong>수치가 왜곡</strong>된다. CPU 사용률을 모니터링하라.</p>\n<h2 id=\"소크-테스트soak-오래-눌러보기\" style=\"position:relative;\"><a href=\"#%EC%86%8C%ED%81%AC-%ED%85%8C%EC%8A%A4%ED%8A%B8soak-%EC%98%A4%EB%9E%98-%EB%88%8C%EB%9F%AC%EB%B3%B4%EA%B8%B0\" aria-label=\"소크 테스트soak 오래 눌러보기 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>소크 테스트(Soak): 오래 눌러보기</h2>\n<p>짧은 스파이크만 돌리면 <strong>메모리 누수</strong>, <strong>커넥션 풀 고갈</strong>, <strong>디스크 채움</strong> 같은 느린 문제를 놓친다. 몇 시간 이상 <strong>일정한 부하</strong>를 유지하는 소크 테스트는 별도 일정으로 잡는 것이 좋다. 특히 캐시 TTL, 배치 작업, 로그 로테이션과 겹치는 시간대를 고른다.</p>\n<h2 id=\"github-actions-예시-스케치\" style=\"position:relative;\"><a href=\"#github-actions-%EC%98%88%EC%8B%9C-%EC%8A%A4%EC%BC%80%EC%B9%98\" aria-label=\"github actions 예시 스케치 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>GitHub Actions 예시 스케치</h2>\n<p>실제 워크플로는 조직의 러너·시크릿 정책에 맞춰야 한다. 아래는 <strong>개념을 잡기 위한 스케치</strong>다.</p>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token comment\"># .github/workflows/k6-smoke.yml (개념)</span>\n<span class=\"token comment\"># on:</span>\n<span class=\"token comment\">#   pull_request:</span>\n<span class=\"token comment\">#     branches: [main]</span>\n<span class=\"token comment\"># jobs:</span>\n<span class=\"token comment\">#   k6:</span>\n<span class=\"token comment\">#     runs-on: ubuntu-latest</span>\n<span class=\"token comment\">#     steps:</span>\n<span class=\"token comment\">#       - uses: actions/checkout@v4</span>\n<span class=\"token comment\">#       - name: Install k6</span>\n<span class=\"token comment\">#         run: |</span>\n<span class=\"token comment\">#           sudo gpg -k</span>\n<span class=\"token comment\">#           sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69</span>\n<span class=\"token comment\">#           echo \"deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main\" | sudo tee /etc/apt/sources.list.d/k6.list</span>\n<span class=\"token comment\">#           sudo apt-get update</span>\n<span class=\"token comment\">#           sudo apt-get install k6</span>\n<span class=\"token comment\">#       - name: Run k6 smoke</span>\n<span class=\"token comment\">#         env:</span>\n<span class=\"token comment\">#           BASE_URL: ${{ secrets.STAGING_BASE_URL }}</span>\n<span class=\"token comment\">#         run: k6 run ./load/smoke.js</span></code></pre></div>\n<p>시크릿 이름, k6 설치 방식, 캐시는 팀 표준에 맞게 조정한다. 중요한 것은 <strong>PR마다 동일한 스테이징</strong>을 바라보게 하고, <strong>임계값 실패 시 워크플로가 빨간불</strong>을 켠다는 점이다.</p>\n<h2 id=\"트레이스로그와-상관관계\" style=\"position:relative;\"><a href=\"#%ED%8A%B8%EB%A0%88%EC%9D%B4%EC%8A%A4%EB%A1%9C%EA%B7%B8%EC%99%80-%EC%83%81%EA%B4%80%EA%B4%80%EA%B3%84\" aria-label=\"트레이스로그와 상관관계 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>트레이스·로그와 상관관계</h2>\n<p>가능하다면 k6 실행 ID를 <strong>헤더</strong>로 넘기고, 애플리케이션 로그·분산 트레이스(OpenTelemetry 등)에 같은 ID를 남긴다. 그러면 “느린 요청” 하나를 골라 <strong>DB 쿼리·외부 API·큐 지연</strong>까지 한 줄로 따라갈 수 있다. 부하 테스트는 <strong>관측 가능성</strong>과 세트일 때 가장 강해진다.</p>\n<h2 id=\"비용과-윤리\" style=\"position:relative;\"><a href=\"#%EB%B9%84%EC%9A%A9%EA%B3%BC-%EC%9C%A4%EB%A6%AC\" aria-label=\"비용과 윤리 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>비용과 윤리</h2>\n<p>스테이징이든 어디든, <strong>타사에 피해를 주는 부하</strong>를 절대 보내지 말 것. 허용 IP, WAF, rate limit을 먼저 확인한다. 사내 규정에 <strong>부하 테스트 승인 절차</strong>가 있다면 따른다.</p>\n<h2 id=\"장애-후-재현\" style=\"position:relative;\"><a href=\"#%EC%9E%A5%EC%95%A0-%ED%9B%84-%EC%9E%AC%ED%98%84\" aria-label=\"장애 후 재현 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>장애 후 재현</h2>\n<p>프로덕션 장애가 났을 때, <strong>같은 트래픽 패턴을 k6로 재현</strong>하면 수정이 빨라진다. 이때 “그때의 페이로드 분포”를 대략이라도 복원하는 것이 핵심이다. 로그 샘플링이나 대표 트레이스를 모아 <strong>시나리오를 버전 관리</strong>하라.</p>\n<h2 id=\"결과-해석-숫자-뒤에-숨은-이야기\" style=\"position:relative;\"><a href=\"#%EA%B2%B0%EA%B3%BC-%ED%95%B4%EC%84%9D-%EC%88%AB%EC%9E%90-%EB%92%A4%EC%97%90-%EC%88%A8%EC%9D%80-%EC%9D%B4%EC%95%BC%EA%B8%B0\" aria-label=\"결과 해석 숫자 뒤에 숨은 이야기 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>결과 해석: 숫자 뒤에 숨은 이야기</h2>\n<p>같은 p95 지표라도 <strong>요청 분포</strong>가 다르면 의미가 달라진다. 소수의 극단적 아웃라이어가 p95를 밀어 올린 것인지, 전체가 고르게 나빠진 것인지 확인하라. 전자라면 <strong>특정 쿼리·특정 테넌트</strong> 문제일 수 있고, 후자라면 <strong>전체 용량</strong> 문제일 가능성이 크다.</p>\n<p>히스토그램을 Grafana에 보낼 수 있다면, <strong>시간대별로</strong> 겹쳐 보는 것이 좋다. 배포 직후에만 지연이 튀었다면, <strong>캐시 콜드 스타트</strong>, <strong>JIT 컴파일</strong>, <strong>커넥션 풀 워밍업</strong> 같은 일시적 이유일 수 있다.</p>\n<h2 id=\"부하-테스트-전에-확인하는-체크리스트\" style=\"position:relative;\"><a href=\"#%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A0%84%EC%97%90-%ED%99%95%EC%9D%B8%ED%95%98%EB%8A%94-%EC%B2%B4%ED%81%AC%EB%A6%AC%EC%8A%A4%ED%8A%B8\" aria-label=\"부하 테스트 전에 확인하는 체크리스트 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>부하 테스트 전에 확인하는 체크리스트</h2>\n<ul>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled> 대상 환경이 <strong>프로덕션 데이터</strong>를 오염시키지 않는가</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled> 알림·결제·SMS 같은 <strong>부작용 경로</strong>가 차단됐는가</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled> <strong>동시성</strong>이 실제와 비슷한가 (think time, 시나리오 다양성)</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled> <strong>임계값</strong>이 SLO와 연결돼 있는가</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" disabled> 실패 시 <strong>롤백·원인 분석 담당</strong>이 정해져 있는가</li>\n</ul>\n<p>체크리스트가 형식이 아니라 <strong>회의록 한 줄</strong>이라도 남으면, 부하 테스트는 팀의 자산이 된다.</p>\n<p>또 한 가지, <strong>지역(latency) 차이</strong>를 기억하라. 부하 생성기가 서울에 있고 API가 버지니아에 있으면 RTT만으로도 p95가 커진다. CI 러너 위치와 스테이징 리전을 맞추거나, 결과를 비교할 때 <strong>항상 같은 네트워크 경로</strong>를 전제로 문서화하라.</p>\n<h2 id=\"마지막으로\" style=\"position:relative;\"><a href=\"#%EB%A7%88%EC%A7%80%EB%A7%89%EC%9C%BC%EB%A1%9C\" aria-label=\"마지막으로 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>마지막으로</h2>\n<p>부하 테스트는 한 번의 이벤트가 아니라 <strong>제품의 지속적인 질문</strong>이다. “우리는 얼마나 버틸 수 있는가?”에 대한 답은 코드 한 줄이 아니라, <strong>시나리오·임계값·관측</strong>이 합쳐졌을 때 비로소 신뢰를 얻는다. k6는 그 답을 자동화하기 좋은 도구다. 이 글이 스테이징에서의 첫 <code class=\"language-text\">thresholds</code> 실패를, <strong>프로덕션의 미래의 성공</strong>으로 바꾸는 데 도움이 되기를 바란다.</p>","tableOfContents":"<ul>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EC%99%9C-k6%EC%9D%B8%EA%B0%80\">왜 k6인가</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EC%B5%9C%EC%86%8C-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%99%80-%EC%8B%A4%ED%96%89\">최소 스크립트와 실행</a></li>\n<li>\n<p><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EB%B6%80%ED%95%98-%EB%AA%A8%EB%8D%B8-vu-rps-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80\">부하 모델: VU, RPS, 스테이지</a></p>\n<ul>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%98%88%EC%8B%9C\">현실적인 패턴 예시</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EC%9E%84%EA%B3%84%EA%B0%92thresholds%EA%B3%BC-slo-%EC%97%B0%EA%B2%B0\">임계값(thresholds)과 SLO 연결</a></p>\n<ul>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#check%EC%99%80-%EC%9E%84%EA%B3%84%EA%B0%92%EC%9D%98-%EC%B0%A8%EC%9D%B4\">check()와 임계값의 차이</a></li>\n</ul>\n</li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A4%80%EB%B9%84%EC%99%80-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B2%A9%EB%A6%AC\">데이터 준비와 테스트 격리</a></li>\n<li>\n<p><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EB%A9%94%ED%8A%B8%EB%A6%AD-%ED%95%B4%EC%84%9D-%EB%AC%B4%EC%97%87%EC%9D%84-%EB%B4%90%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80\">메트릭 해석: 무엇을 봐야 하는가</a></p>\n<ul>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EB%B3%91%EB%AA%A9-%ED%9B%84%EB%B3%B4-%EC%B2%B4%ED%81%AC%EB%A6%AC%EC%8A%A4%ED%8A%B8\">병목 후보 체크리스트</a></li>\n</ul>\n</li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#grpcwebsocket\">gRPC·WebSocket</a></li>\n<li>\n<p><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#github-actions%EC%97%90-%EB%B6%99%EC%9D%B4%EA%B8%B0--ci-%EA%B2%8C%EC%9D%B4%ED%8A%B8\">GitHub Actions에 붙이기 — CI 게이트</a></p>\n<ul>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EC%9B%8C%ED%81%AC%ED%94%8C%EB%A1%9C-%EA%B0%9C%EB%85%90\">워크플로 개념</a></li>\n</ul>\n</li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EC%84%B1%EB%8A%A5-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A1%B0%EC%A7%81-%EB%AC%B8%ED%99%94\">성능 테스트 조직 문화</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%ED%9D%94%ED%95%9C-%EC%8B%A4%EC%88%98\">흔한 실수</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EC%9E%A5%EA%B8%B0-%EC%9A%B4%EC%98%81-%EA%B2%B0%EA%B3%BC%EB%A5%BC-%EC%B6%95%EC%A0%81%ED%95%98%EB%9D%BC\">장기 운영: 결과를 축적하라</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EC%A0%95%EB%A6%AC\">정리</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EB%B6%80%EB%A1%9D-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%92%88%EC%A7%88%EC%9D%84-%EC%98%AC%EB%A6%AC%EB%8A%94-%EC%9E%91%EC%9D%80-%EC%8A%B5%EA%B4%80\">부록: 스크립트 품질을 올리는 작은 습관</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4scenarios%EB%A1%9C-%EC%97%AC%EB%9F%AC-%ED%8C%A8%ED%84%B4-%EB%8F%99%EC%8B%9C%EC%97%90-%EB%8F%8C%EB%A6%AC%EA%B8%B0\">시나리오(scenarios)로 여러 패턴 동시에 돌리기</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EB%AA%A9%ED%91%9C-rps%EB%A5%BC-%EC%93%B0%EB%8A%94-%EA%B2%BD%EC%9A%B0-arrival-rate-%EC%8B%A4%ED%96%89%EA%B8%B0\">목표 RPS를 쓰는 경우: arrival-rate 실행기</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EC%BB%A4%EC%8A%A4%ED%85%80-%EB%A9%94%ED%8A%B8%EB%A6%AD%EA%B3%BC-%EB%B9%84%EC%A6%88%EB%8B%88%EC%8A%A4-%EA%B2%80%EC%A6%9D\">커스텀 메트릭과 비즈니스 검증</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EC%86%8C%EC%BC%93-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%B6%84%EC%82%B0-%EC%8B%A4%ED%96%89%EA%B3%BC-%ED%95%9C%EA%B3%84\">소켓 테스트: 분산 실행과 한계</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EC%86%8C%ED%81%AC-%ED%85%8C%EC%8A%A4%ED%8A%B8soak-%EC%98%A4%EB%9E%98-%EB%88%8C%EB%9F%AC%EB%B3%B4%EA%B8%B0\">소크 테스트(Soak): 오래 눌러보기</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#github-actions-%EC%98%88%EC%8B%9C-%EC%8A%A4%EC%BC%80%EC%B9%98\">GitHub Actions 예시 스케치</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%ED%8A%B8%EB%A0%88%EC%9D%B4%EC%8A%A4%EB%A1%9C%EA%B7%B8%EC%99%80-%EC%83%81%EA%B4%80%EA%B4%80%EA%B3%84\">트레이스·로그와 상관관계</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EB%B9%84%EC%9A%A9%EA%B3%BC-%EC%9C%A4%EB%A6%AC\">비용과 윤리</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EC%9E%A5%EC%95%A0-%ED%9B%84-%EC%9E%AC%ED%98%84\">장애 후 재현</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EA%B2%B0%EA%B3%BC-%ED%95%B4%EC%84%9D-%EC%88%AB%EC%9E%90-%EB%92%A4%EC%97%90-%EC%88%A8%EC%9D%80-%EC%9D%B4%EC%95%BC%EA%B8%B0\">결과 해석: 숫자 뒤에 숨은 이야기</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A0%84%EC%97%90-%ED%99%95%EC%9D%B8%ED%95%98%EB%8A%94-%EC%B2%B4%ED%81%AC%EB%A6%AC%EC%8A%A4%ED%8A%B8\">부하 테스트 전에 확인하는 체크리스트</a></li>\n<li><a href=\"/etc/k6-%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%84%A4%EA%B3%84%EC%99%80-GitHub-Actions-CI-%EA%B2%8C%EC%9D%B4%ED%8A%B8/#%EB%A7%88%EC%A7%80%EB%A7%89%EC%9C%BC%EB%A1%9C\">마지막으로</a></li>\n</ul>","frontmatter":{"title":"k6 부하 테스트 — 시나리오 설계와 GitHub Actions CI 게이트까지","date":"April 15, 2026","description":"Grafana k6로 부하 테스트 스크립트를 작성하는 법, RPS·스테이지·임계값 설계, 메트릭 해석, 그리고 GitHub Actions에서 성능 회귀를 막는 CI 게이트 패턴까지 실무 중심으로 정리한다.","tags":["k6","부하 테스트","성능","GitHub Actions","CI","SRE","관측 가능성"]}}},"pageContext":{"slug":"/etc/k6-부하-테스트-시나리오-설계와-GitHub-Actions-CI-게이트/","previous":{"fields":{"slug":"/etc/기술-부채와-의-전쟁-2년간-쌓인-기술-부채를-해결한-여정/"},"frontmatter":{"title":"기술 부채와의 전쟁 - 2년간 쌓인 기술 부채를 해결한 여정","tags":["기술 부채","리팩토링","프로젝트 경험","개발자 경험","회고","코드 품질"]}},"next":{"fields":{"slug":"/etc/MCP-Model-Context-Protocol-실전-가이드-AI-에디터에-도구를-붙이는-방법/"},"frontmatter":{"title":"MCP(Model Context Protocol) 실전 가이드 — AI 에디터에 도구를 붙이는 방법","tags":["MCP","Model Context Protocol","Cursor","AI","TypeScript","개발 생산성","도구 통합"]}}}},"staticQueryHashes":["3262363727","4027999303"]}