Engineering
포스트맨에서 젠킨스까지: QA 팀의 API 테스트 자동화, 파란만장 성장기
NOL Tech Blog야놀자
2025년 6월 13일
원문에서 보기 ↗급변하는 환경 속, 품질을 유지하기 위해 고군분투 했던 저희 팀의 경험을 공유합니다.

이 이미지는 Gemini 생성형 AI 를 활용해 생성하였습니다.
안녕하세요, NOL QA 팀은 매일매일이 새로운 도전의 연속이랍니다. 특히 요즘처럼 서비스가 눈 깜짝할 새에 커지고, API들이 거미줄처럼 복잡해지는 세상에서는 수동 테스트만으로는 한계에 부딪히기 일쑤이죠. 솔직히 말하면, 예전엔 단순 반복 테스트에 지쳐 “이게 맞나…?” 싶을 때도 많았어요. 😅
하지만 QA 팀은 포기하지 않았습니다. 오히려 이 위기를 기회 삼아, 포스트맨(Postman)으로 API 테스트를 꼼꼼하게 만들고, 이걸 또 젠킨스(Jenkins)랑 찰떡같이 붙여서 지속적 통합(CI) 환경을 구축하는 대장정을 시작했죠. 오늘은 그 파란만장했던 여정과, 그 과정에서 얻은 팁들을 여러분과 함께 나누고자 합니다.
자, 그럼 저희의 API 테스트 자동화 성장기 속으로 풍덩 빠져볼까요?
1. 왜 API 테스트 자동화인가? ‘더 빠른 테스트’ 그 이상의 감동
NOL QA팀은 다양한 서비스(NOL은 이걸 ‘버티컬’이라고 불러요!)를 담당하고 있는데, 각 버티컬마다 API 스펙도 다르고, 특징도 제각각이에요. 서비스가 커질수록 API는 점점 더 복잡해지고, 이걸 일일이 손으로 테스트하려니… 휴, 상상만 해도 아찔하죠? 😱
수동 API 테스트, 이대로 괜찮을까?
[우리의 Pain Point]
- 시간 잡아먹는 하마 ⏳: 새로운 기능이 추가되거나 기존 기능이 변경될 때마다 수많은 API를 하나하나 확인해야 했어요. 이 작업에 너무 많은 시간이 소요되면서, 다른 중요한 테스트나 품질 향상 활동에 집중하기 어려웠죠.
- 반복 작업의 지루함과 오류 😴: 똑같은 API를 수없이 반복해서 테스트하는 건 정말 지루한 일이었어요. 그러다 보니 집중력이 흐트러져 사람이 놓치는 실수도 생길 수밖에 없었죠.
- 배포 전 불안감 😨: 복잡한 API 구조에서 놓친 버그가 있을까봐 배포 전에는 늘 불안했어요. 혹시라도 라이브 환경에서 문제가 생기면 어쩌나 하는 걱정이 컸죠.
여러 고민 끝에 저희는 API 테스트 자동화 가 선택이 아닌 필수라는 걸 깨달았어요. 단순히 테스트를 빨리 끝내는 것을 넘어, 더 높은 품질과 안정적인 개발 프로세스를 위한 결단이었죠. 물론 자동화 도입에 따르는 초기 학습 비용이나 시스템 구축의 어려움 같은 단점도 고려했지만, 장기적으로 얻을 수 있는 이점이 훨씬 크다고 판단했습니다.
자동화, 저희에게 이런 마법 같은 변화를 선물해 주었답니다!
- 테스트 효율성 대폭 상승(feat. 팀워크와 집중의 시너지) 🚀: 반복적인 테스트는 이제 자동화에게 맡기고, QA 팀은 더 중요하고 창의적인 테스트에 집중할 수 있게 되었어요. 덕분에 리소스 활용도는 물론 업무 집중도까지 높아졌고, 하루를 기분 좋게 마무리하는 날이 많아졌어요.
- 피드백 속도 5G급(버그, 어디 숨었니?) ⚡: QA 과정에서 API에 문제가 생기면, 자동화가 빛의 속도로 찾아내서 알려줘요. 덕분에 버그를 빠르게 발견하고 수정할 수 있어, 검증 기간이 짧아지고 배포 주기까지 확! 당겨졌답니다.
- 테스트 신뢰성 200% 달성(사람은 실수해도 자동화는 안 해) : 사람이 하는 테스트는 아무래도 실수가 생길 수 있잖아요? 하지만 자동화는 언제나 일관된 기준으로 테스트를 수행하니, 결과에 대한 신뢰도가 하늘을 찔러요.
- 지속적 통합 환경 구축 (배포는 이제 안심하고!) 🏗️: 젠킨스와 연동해서 코드가 변경될 때마다 자동으로 테스트가 돌아가게 만들었어요. 덕분에 배포 전에 미리미리 문제를 걸러내니, 안정적인 배포 환경이 구축되었죠.
이런 직접적인 효과 외에도, NOL QA 팀의 포지션이 확 바뀌었어요. 예전엔 ‘버그 잡는 사람 ’이었다면, 이제는 ‘개발 프로세스 전반의 품질을 책임지는 전략가’가 된 느낌이랄까요? 개발 초기부터 품질을 챙기니, 개발팀도 저희를 더 든든하게 생각하는 것 같아요!
API 테스트 자동화의 주요 이점

2. 포스트맨: API 테스트를 위한 최고의 선택
NOL QA 팀의 API 테스트 자동화 여정에서 빼놓을 수 없는 일등 공신은 바로 포스트맨(Postman)입니다. 직관적인 UI와 강력한 기능 덕분에 API 요청을 만들고, 응답 확인하고, 테스트 스크립트 짜는 게 너무나 쉬웠어요. 그야말로 저희의 ‘최애’ 도구가 되었죠.
2.1. ‘왜 포스트맨인가?’ 심층 분석: 과거의 아픔으로부터 배우다
사실 포스트맨을 만나기 전, 저희는 다른 도구들도 기웃거렸어요. 특히 SoapUI는 조건 분기, 루프, 변수 처리 같은 고급 기능 덕분에 복잡한 테스트 시나리오를 GUI 상에서 시각적으로 설계할 수 있어서 꽤 매력적이었죠. 자동화된 통합 테스트에도 나름 잘 어울렸고요.
하지만… (여기서 잠시 눈물을 닦습니다) 😭
SoapUI는 너무 무겁고, 동작도 불안하고, 심지어는 갑자기 종료되는 일도 잦아서 QA팀을 정말 힘들게 했어요. 생산성은 바닥을 쳤고, 스트레스는 하늘을 찔렀죠. 그러다 포스트맨을 만났고, 운명임을 직감했습니다. 포스트맨으로 ‘갈아타자!’고 결정한 데에는 여러 가지 이유가 있었어요.
- 낮은 learning curve 🥳: 포스트맨은 개발자들 사이에서도 워낙 유명하고 많이 쓰는 도구잖아요? 덕분에 저희 팀원들이나 새로 오신 분들도 금방 익숙해질 수 있었어요. 배우는 데 시간을 낭비할 필요 없이 바로 실전에 투입될 수 있었죠.
- 개발/PM과의 환상적인 협업 🧑🤝🧑: 이게 정말 중요했어요! 포스트맨은 UI가 워낙 직관적이라 개발팀이나 PM분들도 API 요청이나 응답을 쉽게 이해하고, 심지어는 기본적인 테스트까지 함께 볼 수 있었어요. “내 컴퓨터에서는 되는데?” 같은 불필요한 논쟁이 확 줄었죠. 포스트맨의 무제한 공유 작업 공간(Shared Workspaces)은 저희 팀원들이 실시간으로 컬렉션, API, 환경 등을 공유하며 공동 작업할 수 있게 해주어 협업의 시너지를 극대화했어요. 변경 사항이 즉시 동기화되니 모두가 항상 최신 버전에 접근할 수 있었고요. 또한, 역할 기반 접근 제어(Role-based Access Control)를 통해 특정 작업 공간에 대한 권한을 부여하여 보안을 강화하고 체계적인 작업 관리가 가능했습니다.
- 엔터프라이즈급 기능 & 눈이 즐거운 UI 🏆: NOL이 Basic Plan의 소프트웨어 계정을 공식적으로 제공한다는 점도 큰 장점이었어요. 무료 버전에 비해 훨씬 확장된 API 호출 및 모니터링 한도 덕분에 대규모 테스트를 마음껏 진행할 수 있었고, 컬렉션 복구 기간이 30일로 연장되어 혹시 모를 실수에도 데이터 유실 걱정을 덜 수 있었죠. Postman은 API의 정의부터 설계, 개발, 테스트, 배포, 모니터링까지 API 수명 주기의 모든 단계를 지원하는 엔터프라이즈급 기능을 제공해서 QA 팀의 API 개발 프로세스를 더욱 효율적으로 관리할 수 있게 도왔습니다. 게다가 디자인도 깔끔하고 보기 좋으니, 테스트하는 내내 눈도 즐거웠답니다.
포스트맨을 선택한 건 단순히 더 좋은 도구를 찾은 게 아니었어요. 이건 저희 팀이 ‘협업’과 ‘생산성’이라는 두 마리 토끼를 잡기 위한 전략적인 결정이었죠. 덕분에 QA가 더 이상 외딴섬이 아니라, 개발팀과 PM팀을 잇는 든든한 다리 역할을 하게 되었답니다.

2.2. 포스트맨에서 API 테스트 시나리오 구성: 구조가 곧 생명
저희는 포스트맨 안에서 API 엔드포인트들을 체계적으로 정리하는 데 공을 많이 들였어요. 각 서비스 버티컬(예: 사용자 API, 상품 API, 주문 API)마다 전용 컬렉션 을 만들고, 그 안에는 기능별로 폴더를 만들어서 API 엔드포인트를 분류했죠. 마치 잘 정리된 도서관처럼요. 📚
명확성을 위한 예시 구조 (이렇게 정리하면 찾기 쉬워요!)
├── Member API
│ ├── 회원 가입
│ ├── 로그인
│ └── 회원 정보 수정
├── Product API
│ ├── 상품 목록 조회
│ ├── 상품 상세 조회
│ └── 상품 등록
└── Order API
├── 주문 생성
├── 주문 조회
└── 주문 취소
그리고 테스트 전략의 핵심은 바로 포스트맨의 ‘환경(Environment)’ 기능이었어요. 개발, 스테이징, 운영 등 다양한 환경에 따라 API 엔드포인트나 인증 정보 같은 변수들을 유연하게 관리할 수 있었죠. 덕분에 환경만 쓱 바꾸면 똑같은 테스트 컬렉션을 여러 환경에서 돌릴 수 있어서, 일일이 수동으로 수정하는 번거로움이 싹 사라졌답니다.
Postman 컬렉션 및 폴더 구조화 예시 (우리 팀의 정리 비법)

2.3. 지능적인 테스트 케이스 작성: 사용자 흐름부터 엣지 케이스까지 꼼꼼하게
QA팀은 테스트 케이스를 만들 때, 실제 사용자가 서비스를 이용하는 흐름을 최대한 따라가려고 노력했어요. 마치 사용자가 된 것처럼요! 🕵️♀️
시나리오 기반 테스트: 실제 사용자처럼, 문서처럼
- 사용자 흐름 기반 시나리오 🚶♀️: 실제 사용자 여정을 모방하는 시나리오를 만들었어요. 인수 기준(AC) 문서나 기존 수동 테스트 케이스를 꼼꼼히 분석해서, 자동화된 테스트가 실제 사용자 행동을 제대로 검증하도록 했죠.
- API 중심 시나리오 (Swagger와 발맞춰) 🔗: 사용자 흐름과 직접 관련 없는 API들은 Swagger 문서와 테스트 케이스 이름을 최대한 맞춰서 만들었어요. 이렇게 하니 개발자나 다른 팀원들이 나중에 필요한 API 테스트를 훨씬 쉽게 찾을 수 있더라고요.
포괄적인 케이스 구성: 돌다리도 두드려보고 건너자
- 유효하지 않은 케이스 테스트 (만약의 경우를 대비) ⚠️: API가 튼튼하려면 ‘예외 케이스’ 가 필수죠. 요청 본문을 빼먹거나, 이상한 값을 넣거나, 범위를 벗어난 값을 입력하는 등 ‘만약의 경우’ 시나리오들을 꼼꼼하게 테스트했어요. 덕분에 API가 어떤 상황에서도 끄떡없고, 에러 처리도 잘하는지 확인할 수 있었죠.
- 유효한 케이스 다양성 (현실의 복잡성을 담아) 🌈: 유효한 입력값이라고 해서 다 똑같은 게 아니잖아요? 예를 들어 숙박서비스를 담당하는 ‘Stay API’ 같은 경우, ‘대실’, ‘단박’, ‘연박’ 예약처럼 다양한 시나리오를 만들어서 API가 모든 경우를 제대로 처리하는지 확인했답니다.
강력한 테스트 스크립트 작성
API 응답을 꼼꼼하게 검증하기 위해 포스트맨에 내장된 JavaScript 기반 테스트 스크립트를 활용했어요. HTTP 상태 코드가 제대로 오는지, JSON 데이터에 필요한 필드가 다 있는지, 특정 값이 맞는지 등 중요한 부분들을 다 확인했죠.
pm.test() 함수로 각 검증 단계를 명확하게 정의하니, 테스트 코드가 마치 설명서처럼 읽기 쉬워졌어요. 그리고 pm.expect() 덕분에 예상되는 결과를 딱! 명시할 수 있어서 정말 편리했답니다.
초기 검증 단계를 넘어서는 발전된 테스트 전략
처음에는 아래처럼 단순한 코드 스니펫으로 시작했어요.
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
pm.test("Response has 'id' and 'name' fields", function () {
const responseJson = pm.response.json();
pm.expect(responseJson).to.have.property('id');
pm.expect(responseJson).to.have.property('name');
});
pm.test("Name field is not empty", function () {
const responseJson = pm.response.json();
pm.expect(responseJson.name).to.not.be.empty;
});
하지만, 이 정도로는 복잡한 API 응답을 효과적으로 검증하기엔 역부족이었어요. 그래서 저희는 여기서 한 단계 더 나아가, Pre-request에 응답을 저장하고 Post-response에서 재귀 함수까지 활용하며, 지속 가능하고 심플하며 유지보수가 쉬운 코드를 구현하고 발전시켜 나갔답니다.
Pre-request와 Post-response를 활용한 고급 테스트 구현 (이게 바로 우리의 마법 주문)
저희의 핵심 전략은 Pre-request에서 API 응답을 저장해두고, Post-response에서 이 응답과 미리 정의해둔 예상 값을 비교하는 거예요. 특히 Post-response에서는 재귀 함수를 써서 중첩된 JSON 객체나 배열까지도 유연하게 비교하고 검증할 수 있게 만들었죠. 단순하게 필드 존재 여부만 확인하는 걸 넘어, 실제 데이터의 일관성과 정확성까지 꽉 잡았답니다.
아래는 저희가 실제로 적용한 Post-response 코드 스니펫이에요. 이 ‘마법 주문’ 덕분에 API 응답의 깊숙한 곳까지 꼼꼼하게 검증하며, 테스트 자동화 수준을 한 단계 더 끌어올릴 수 있었어요.
// 필요한 데이터와 설정
var actualJson = pm.response.json(); // 실제 API 응답 JSON
var expectedJson = JSON.parse(pm.environment.get("expectedJson")); // 환경 변수에서 가져온 예상 JSON
var excludeFields = [
"iconComponents",
"useTypeSubTitle",
"styles"
]; // 비교에서 제외할 필드 배열
var testDetails = {}; // 각 필드별 테스트 결과를 저장할 객체
/**
* JSON 객체를 정렬하여 문자열로 변환하는 함수
* 배열 내 객체의 순서나 객체 내 필드의 순서에 관계없이 값만을 비교할 수 있도록 합니다.
* @param {Object} obj - 정렬할 JSON 객체
* @returns {string} - 정렬된 JSON 문자열
*/
function sortAndStringify(obj) {
return JSON.stringify(obj, (key, value) => {
if (Array.isArray(value)) {
return sortArray(value); // 배열인 경우 정렬 함수 호출
}
return value;
});
}
/**
* 배열을 정렬하는 함수
* 특정 필드(name, value, order)를 기준으로 배열 내 객체를 정렬하여 비교의 일관성을 유지합니다.
* @param {Array} array - 정렬할 배열
* @returns {Array} - 정렬된 배열
*/
function sortArray(array) {
return array.sort((a, b) => {
if (a.name && b.name) {
return a.name.localeCompare(b.name);
} else if (a.value && b.value) {
return a.value.localeCompare(b.value);
} else if (a.order && b.order) {
return a.order - b.order;
}
return 0;
});
}
/**
* 두 JSON 객체를 전체적으로 비교하는 함수 (값 일치 여부)
* JSON 전체의 동일성을 보장합니다.
* @param {Object} obj1 - 비교할 첫 번째 객체
* @param {Object} obj2 - 비교할 두 번째 객체
* @param {Array} path - 현재 JSON 경로 (테스트 이름 생성을 위함)
*/
function compareJsonObjects(obj1, obj2, path = []) {
var sortedObj1 = JSON.parse(sortAndStringify(obj1)); // 정렬 후 파싱
var sortedObj2 = JSON.parse(sortAndStringify(obj2)); // 정렬 후 파싱
pm.test("JSON 객체 전체 비교: " + path.join('.'), function () {
pm.expect(sortedObj1).to.eql(sortedObj2); // 깊은 비교를 통해 두 객체의 모든 값이 동일한지 확인
});
}
/**
* JSON 객체 내의 필드를 재귀적으로 비교하는 함수
* 실제 응답과 예상 응답의 각 필드를 비교하여 일치 여부를 확인하고, 불일치 시 상세 정보를 저장합니다.
* @param {Object} obj1 - 실제 응답 객체
* @param {Object} obj2 - 예상 응답 객체
* @param {Array} path - 현재 JSON 경로 (디버깅 및 결과 보고를 위함)
*/
function compareObjects(obj1, obj2, path = []) {
// obj1 (실제 응답)의 키들을 순회하며 비교
Object.keys(obj1).forEach(function(key) {
// 제외할 필드인 경우 건너뛰기
if (excludeFields.includes(key)) return;
var fullPath = path.concat(key).join('.'); // 현재 필드의 전체 경로 생성
// 두 객체 모두에 키가 존재하고, 값이 객체인 경우 재귀적으로 비교 (중첩 객체 처리)
if (typeof obj1[key] === 'object' && obj1[key] !== null &&
typeof obj2[key] === 'object' && obj2[key] !== null) {
// 'filters'와 같이 특정 배열의 경우 정렬하여 비교 (순서 무관 비교)
if (key === 'filters' && Array.isArray(obj1[key]) && Array.isArray(obj2[key])) {
obj1[key] = sortArray(obj1[key]);
obj2[key] = sortArray(obj2[key]);
}
// 재귀 호출을 통해 하위 객체 또는 배열 비교
compareObjects(obj1[key], obj2[key], path.concat(key));
}
// 기본적인 값 비교 (객체가 아닌 경우)
else {
if (obj1[key] !== obj2[key]) {
// 값이 일치하지 않는 경우 실패 정보 저장
testDetails[fullPath] = { result: "FAIL", actual: obj1[key], expected: obj2[key] };
} else {
// 값이 일치하는 경우 성공 정보 저장
testDetails[fullPath] = { result: "PASS" };
}
}
});
// 추가: 예상에는 없고 실제 응답에만 있는 필드 검증
Object.keys(obj2).forEach(function(key) {
if (!obj1.hasOwnProperty(key) && !excludeFields.includes(key)) {
var fullPath = path.concat(key).join('.');
testDetails[fullPath] = { result: "FAIL", actual: "존재", expected: "없음" };
}
});
// 추가: 실제 응답에는 없고 예상에만 있는 필드 검증
Object.keys(obj1).forEach(function(key) {
if (!obj2.hasOwnProperty(key) && !excludeFields.includes(key)) {
var fullPath = path.concat(key).join('.');
testDetails[fullPath] = { result: "FAIL", actual: "없음", expected: "존재" };
}
});
}
/**
* 최종 결과를 출력하는 함수
* testDetails에 저장된 결과를 바탕으로 최종 테스트 보고서를 생성합니다.
*/
function outputFinalResults() {
var finalResults = aggregateFinalResults(testDetails); // 상세 결과를 최상위 필드별로 집계
Object.keys(finalResults).forEach(function (topLevelField) {
pm.test(topLevelField + " 필드 항목 데이터 검증", function () {
if (finalResults[topLevelField].length === 0) {
pm.expect(true).to.be.true; // 해당 최상위 필드에 실패한 항목이 없으면 성공
} else {
// 실패한 항목이 있으면 에러 메시지와 함께 테스트 실패
pm.expect.fail("실패한 필드: \n" + finalResults[topLevelField].join("\n"));
}
});
});
}
/**
* 최상위 필드별 상세 결과를 집계하는 함수
* @param {Object} details - 테스트 상세 결과 객체
* @returns {Object} - 최상위 필드를 키로 하고, 해당 필드 내 실패한 상세 경로 및 정보를 값으로 하는 객체
*/
function aggregateFinalResults(details) {
var results = {};
Object.keys(details).forEach(function (key) {
var topLevelField = key.split('.')[0]; // 최상위 필드 추출
if (!results[topLevelField]) {
results[topLevelField] = []; // 해당 최상위 필드가 없으면 배열 초기화
}
if (details[key].result === "FAIL") {
// 실패한 경우 상세 정보와 함께 배열에 추가
results[topLevelField].push(key + " (실제: " + JSON.stringify(details[key].actual) + ", 기대: " + JSON.stringify(details[key].expected) + ")");
}
});
return results;
}
// 함수 호출: 실제 응답과 예상 응답을 비교하여 테스트 시작
compareObjects(actualJson, expectedJson);
// 결과 출력: 모든 비교가 완료된 후 최종 테스트 결과 보고
outputFinalResults();
이처럼 저희는 단순한 코드 스니펫을 넘어, Pre-request에 데이터를 저장하고 Post-response에서 재귀 함수 같은 프로그래밍 기법을 활용해서 API 응답을 심층적으로 검증하는 프레임워크를 구축했어요. 덕분에 테스트의 지속 가능성이 높아지고, 복잡한 API 변경에도 유연하게 대응할 수 있게 되었답니다.
결국, 유지보수가 용이한 강력한 테스트 코드를 구현하는 데 크게 기여했죠. 이런 접근 방식이 단순한 테스트를 넘어, 견고한 소프트웨어 품질 보증을 위한 ‘마법 주문’이 되어주고 있다고 생각해요.
3. 테스트 코드, 변화에 발맞춰 진화하다! (feat. 살아있는 코드의 비밀)
NOL QA 팀은 서비스가 눈 깜짝할 새에 성장하는 만큼, 저희의 테스트 코드도 그냥 놔두면 안 된다는 걸 일찍이 깨달았어요. 마치 살아있는 생명체처럼, 서비스의 변화에 맞춰 테스트 코드도 꾸준히 숨 쉬고 진화해야 한다는 걸 말이죠. 단순히 한 번 만들어두고 “자, 끝!” 하는 게 아니라, 계속해서 돌보고 가꿔줘야 한답니다.
3.1. 변화무쌍한 서비스에 발맞춰 춤추는 테스트 코드
저희는 서비스 개선이 멈추지 않는 한, 테스트 코드 관리도 영원한 숙제라고 생각해요. 그래서 아래와 같은 방법들을 쓰고 있답니다.
- 촉각을 곤두세우고 변경 사항 감지 👁️🗨️: QA가 직접 테스트를 수행했던 작업은 물론이고, 미처 손대지 못했던 작업까지도 모든 변경 내역을 저희가 자체적으로 꼼꼼하게 모니터링하고 있어요. 놓치는 부분이 없도록요.
- 개발팀과의 찰떡궁합 호흡 🤝 : 가 끔은 개발팀에서 중요한 코드 변경이 있을 때, 커밋 메시지에 QA 팀을 직접 멘션하며 “이 부분 바뀌었어요!” 하고 알려주시기도 해요. 또, 자동화 채널에 변경되는 내용을 상세하게 공유해주시는 분들도 있고요. 덕분에 변경 사항을 빛의 속도로 알아차리고, 필요한 테스트 코드를 바로바로 업데이트할 수 있죠. 이 투명하고 적극적인 소통 덕분에 저희의 살아있는 코드는 늘 최신 상태를 유지할 수 있답니다.
3.2. 우리 팀만의 테스트 코드 관리 비법(깃허브는 우리의 보물창고)
효율적으로 테스트 코드를 관리하기 위해 저희는 나름의 규칙을 세우고, GitHub라는 든든한 지원군을 활용하고 있어요.
[파트별 맞춤 관리]
NOL QA 팀은 크게 파트별로 담당 영역을 나눠서 테스트 코드를 관리하고 있어요.
- 1파트: 숙박, 레저, 항공 API는 1파트가 담당해요.
- 2파트: 주문, 결제, 회원 API는 2파트가 책임지죠.
- 3파트: 프로모션, 기획전, 찜, 장바구니 API는 3파트의 몫이랍니다.
이렇게 파트를 나누니 각자 자기 영역의 전문성을 쑥쑥 키울 수 있고, 해당 도메인의 API가 바뀌어도 더 빠르게 대처할 수 있어요.
[GitHub에 모여라! 우리의 자동화 컬렉션]
모든 포스트맨 테스트 컬렉션과 환경 파일은 GitHub 저장소에 모여 있어요. 젠킨스와 연동할 때도 정말 편하고, 무엇보다 팀원들끼리 코드를 쉽게 공유하고 함께 작업할 수 있다는 게 최고죠. 변경 이력도 한눈에 볼 수 있고, 문제가 생기면 이전 버전으로 싹 돌릴 수도 있어서 테스트 코드의 품질과 안정성을 꽉 잡을 수 있답니다.
이처럼 NOL QA 팀은 단순히 자동화 파이프라인을 구축하는 것을 넘어, 끊임없이 변화하는 서비스 속에서 테스트 코드가 살아 숨 쉬도록 유지하고 관리하는 데 진심이에요. 덕분에 서비스가 아무리 빠르게 성장해도, 저희의 든든한 자동화 방패는 늘 굳건하게 지켜주고 있답니다. 😊
4. 젠킨스: 지속적인 품질을 위한 CI/CD의 심장
포스트맨으로 테스트 컬렉션을 튼튼하게 만들었으니, 이제 다음 단계는 이 테스트들을 자동으로 돌려줄 젠킨스와 연결하는 거였어요. 이 연결이야말로 저희 API 테스트 자동화 파이프라인의 핵심이자, 개발팀이 코드를 푸시할 때마다 자동으로 테스트가 실행되는 마법을 가능하게 했죠. ✨
4.1. 인프라 진화: iMac에서 AWS EC2로 (우리 팀의 ‘레벨업’ 스토리)
iMac 시절(그때는 그랬지…) 😥
자동화 여정의 초창기에는 젠킨스를 iMac에서 돌렸어요. 시작은 좋았지만, 문제는 iMac이 종종 연결이 끊기거나, 유지보수가 너무 힘들었다는 거예요. 테스트가 불안정하게 돌아가니, 지속적 통합은커녕 스트레스만 쌓였죠.
AWS EC2로 이전: 드디어 안정적인 보금자리를 찾다🏡
더 튼튼하고, 확장 가능하고, 안정적인 솔루션이 필요하다는 걸 깨달았어요. 그래서 과감하게 젠킨스 환경을 AWS EC2 인스턴스로 옮기기로 결정했죠! 이 결정은 정말 신의 한 수였 어요. 이전의 물리적인 환경 구성이나 유지보수 부담이 싹 사라지고, 자동화 파이프라인이 언제나 든든하게 돌아가게 되었답니다. 이제야 비로소 진정한 지속적 통합의 꿈을 이룰 수 있게 된 거죠.
불안정한 iMac에서 안정적인 AWS EC2로의 전환은 단순한 기술 업그레이드를 넘어섰어요. 이건 저희 팀이 진정으로 믿을 수 있고, 확장 가능한 품질 보증 시스템을 구축하기 위한 전략적 투자였죠. 덕분에 저희의 자동화 노력은 ‘실험’ 단계를 넘어 ‘전문적인 CI/CD 구성 요소’로 한 단계 더 성장할 수 있었답니다.

4.2. Newman & Jenkins: 자동화의 환상적인 듀오
Newman의 역할: 포스트맨의 든든한 조력자
젠킨스에서 포스트맨 컬렉션을 매끄럽게 실행하기 위해 저희는 포스트맨의 명령줄 인터페이스(CLI) 도구인 Newman을 활용했어요. Node.js 기반의 Newman은 포스트맨 컬렉션과 환경 파일을 코드로 실행하고, 테스트 결과를 다양한 형식으로 뽑아낼 수 있어서 CI/CD 파이프라인에 딱이었죠.
젠킨스 구성 단계: 모든 것을 하나로
- Node.js와 Newman 설치 💻: 젠킨스 서버에 Node.js 환경을 구축하고 Newman을 설치하는 게 첫 단계였어요. 그래야 젠킨스에서 Newman 명령어를 쓸 수 있으니까요!
- 파이프라인 프로젝트 설정 🏗️: 젠킨스에 새로운 파이프라인 프로젝트를 만들고, 포스트맨 컬렉션과 환경 파일이 있는 Git 저장소를 연결했어요. 덕분에 젠킨스는 항상 최신 버전의 테스트를 가지고 작업할 수 있었죠.
- 빌드 스텝 구성 ⚙️: 젠킨스 파이프라인의 빌드 스텝에 Newman 명령어를 추가해서 테스트를 실행하도록 설정했어요. 어떤 컬렉션과 환경 파일을 쓸지 지정해서, 테스트가 정확한 API에 대해 돌아가도록 했답니다.
실행을 위한 예시 명령어 (이게 바로 핵심)
newman run 'Leisure_postman.json' --delay-request 200 --verbose -r htmlextra,cli --reporter-htmlextra-showFolderDescription --reporter-htmlextra-export '${REPORT_DIR}/LeisureAPI-Result-${BUILD_NUMBER}.html' | tee newman_output.log
이 간단한 명령어가 젠킨스 안에서 API 자동화를 움직이는 심장이랍니다.
Jenkins 파이프라인 주요 구성 단계 (젠킨스 세팅 비법)

4.3. 실시간 피드백 및 알림: 문제 발생? 바로 알려줄게
Newman은 CLI, HTML, JUnit 등 다양한 형식으로 테스트 결과를 출력할 수 있어요. 저희는 가독성이 뛰어난 htmlextra 리포트 를 생성하고, 필요한 통계는 CLI 로그에서 직접 파싱 하는 방식을 사용하고 있어요. 테스트 결과는 Jenkins에서 자동으로 HTML 리포트로 게시되고, 슬랙(Slack)으로는 요약된 통계와 링크가 전송되죠.
무엇보다 중요한 건 즉각적인 알림 이에요. 테스트가 실패하면 Jenkins가 곧바로 저희 팀 슬랙 채널에 메시지를 보내줘요. 총 실행 시간, 통과/실패한 Assertion 수, 그리고 상세 리포트 링크까지 포함되어 있어서, 누가 먼저랄 것도 없이 문제를 빠르게 인지하고 대응할 수 있어요.
덕분에 이슈 대응 속도는 업! 디버깅 시간은 다운! 실제로 장애 대응 시간이 크게 단축 되었답니다 😊


5. 혁신적인 영향: 자동화가 QA 워크플로우를 어떻게 뒤집어 놓았나
포스트맨과 젠킨스를 활용해서 API 테스트 자동화 파이프라인을 구축하는 건 정말 야심 찬 프로젝트였어요. 물론 중간중간 어려움도 많았지만, 그 결과는 정말이지 기대 이상이었습니다. 이 자동화 파이프라인은 NOL QA 팀의 일하는 방식을 완전히 바꿔놓았고, API와 서비스의 안정성을 엄청나게 끌어올렸죠.
5.1. 일일 모니터링 & 회귀 테스트 마스터: 우리 팀의 든든한 방패
슬랙을 통한 맞춤형 소통 💬
각 서비스 버티컬(예: stay-api, order-api, member-api..)마다 전용 슬랙 채널을 만들었어요. 관련 Product PM과 개발팀원들을 모두 초대해서, 중요한 품질 정보가 필요한 분들에게 바로바로 전달되도록 했죠.
매일매일 정기 보고 ⏰
자동화된 API 테스트 결과는 매일 오전 11시, 오후 3시에 슬랙 채널에 꼬박꼬박 공유됩니다. 덕분에 API 상태를 항상 투명하게 확인할 수 있고, 혹시라도 문제가 생기면 바로 알아차릴 수 있죠.
주요 모니터링 활동 (우리는 늘 지켜보고 있다!)
- 신규 프로젝트 회귀 테스트 🔄: 새로운 프로젝트나 중요한 기능이 QA 환경에 배포되면, 자동화가 기존 기능에 대한 회귀 테스트를 싹 다 돌려줘요. 덕분에 새로운 개발 때문에 생길 수 있는 예상치 못한 문제들을 미리미리 잡아낼 수 있죠.
- QA 미수행 작업 모니터링 🔍: 이게 정말 중요한데요. 리소스나 일정 때문에 QA 팀이 직접 테스트하지 못했던 작업들도, 자동화가 QA 환경에 배포된 변경 사항들을 꼼꼼하게 모니터링해 줘요. 덕분에 잠재적인 오류가 라이브 환경으로 넘어가기 전에 미리 발견해서, 나중에 큰 사고가 터지는 걸 막을 수 있답니다.
슬랙을 통한 투명한 보고와 꼼꼼한 모니터링 덕분에, API 자동화는 단순한 테스트 도구를 넘어 능동적인 품질 정보 시스템 이 되었어요. 덕분에 애플리케이션의 무결성을 항상 지키고, 모든 팀원들에게 실시간으로 유용한 정보를 제공해서 ‘품질은 모두의 책임’이라는 문화를 만들어가고 있답니다.


일일 QA 모니터링 및 커뮤니케이션 전략 (우리 팀의 소통 노하우)

5.2. 가시적인 이점 & 예상치 못한 성과: 자동화, 그 이상의 감동
- 리소스 효율성 대폭 상승 📈: 가장 눈에 띄는 변화는 반복적인 수동 API 테스트가 사라졌다는 거예요. 덕분에 NOL QA 팀은 귀한 시간과 전문성을 더 복잡하고 창의적인 테스트, 전략 기획, 심층 분석에 쏟을 수 있게 되었죠.
- 안정성 UP! 믿음직한 검증 ✅: 테스트가 자동으로 꾸준히 돌아가니, API의 전반적인 안정성이 엄청나게 좋아졌어요. 이제는 릴리스 품질에 대한 확신이 훨씬 커졌답니다.
- 백엔드 이해도 UP! 프론트엔드 시너지 폭발 🔥: 자동화 덕분에 새로운 API 스펙에 대한 피드백을 바로바로 받으면서, NOL QA 팀은 백엔드 서비스에 대한 이해도가 엄청나게 깊어졌어요. 이 깊은 지식은 문제를 미리 예측하고, 프론트엔드 테스트에도 큰 도움을 주었죠. 그야말로 ‘전천후 QA’가 된 느낌이랄까요?
- 사전적인 사이드 이펙트 발견(숨은 버그도 놓치지 않아) 🚨: 자동화 파이프라인은 이제 NOL QA팀의 없어서는 안 될 ‘조기 경보 시스템’이 되었어요. 코드 변경으로 인한 예상치 못한 사이드 이펙트들을 꾸준히, 그리고 선제적으로 찾아내죠. 수동 테스트로는 놓칠 수 있었던 문제들도 척척 잡아내니, 나중에 큰 비용이 드는 수동 회귀 테스트 시간과 노력을 엄청나게 줄일 수 있었답니다.
5.3. 젠킨스, 정기 실행을 넘어: 개발팀도 함께 성장
- 정기 자동화 (크론의 마법) 🪄: 젠킨스 작업은 ‘크론(cron)’ 설정으로 꼼꼼하게 구성되어, 주기적으로 자동으로 실행돼요. 덕분에 저희가 신경 쓰지 않아도 꾸준히 모니터링하고, 정기적으로 테스트 결과를 공유할 수 있죠.
- 개발자 셀프 서비스 (이제 개발팀도 직접) 🧑💻: 개발팀이 자체 빌드 후에 API 자동화 작업을 직접 돌려볼 수 있도록 권한을 주었어요. 덕분에 개발자분들도 본인들이 만든 코드가 안전하고 안정적인지 직접 확인할 수 있게 되었죠. 품질에 대한 책임감을 함께 나누고, 피드백 속도도 더 빨라졌답니다.
- CI/CD 파이프라인 통합 (배포는 이제 자동문) 🚪: 일부 서비스의 경우, API 자동화가 더 큰 CI/CD 파이프라인에 완전히 통합되어 있어요. 백엔드 서비스가 배포되면 QA 팀의 API 자동화 잡(job)이 자동으로 트리거되는 거죠. 그야말로 배포 프로세스 안에 자동 품질 검증 게이트가 생긴 셈이랍니다.
6. 우리의 미래 비전: API 자동화를 위한 다음 Step!
NOL QA 팀은 중요한 이정표를 달성했지만, API 테스트 자동화 여정은 아직 끝나지 않았어요. NOL QA 팀은 끊임없이 더 나은 방법을 찾고, 역량을 강화하며, 소프트웨어 개발의 모든 단계에 ‘품질’을 더욱 깊숙이 녹여내기 위해 노력하고 있답니다.
주요 미래 Initiative (다음 목표는 이것)
- AI 기반 테스트 자동화 도입 🤖: 단순 반복적인 테스트는 이제 AI에게 맡길 시간! AI를 활용해 테스트 케이스를 자동으로 생성하고, 버그를 예측하며, 테스트 결과 분석까지 자동화하여 QA 효율을 극대화할 거예요. 이를 통해 QA 엔지니어는 더 복잡하고 창의적인 문제 해결에 집중할 수 있게 된답니다.
- Shift-Left Testing 강화 ➡️: 버그는 일찍 발견할수록 고치기 쉽고 비용도 적게 들어요. 개발 초기 단계부터 테스트를 더 많이 수행하는 ‘Shift-Left Testing’ 전략을 강화하여, 개발 과정 전반에 걸쳐 품질을 내재화하고 문제 발생을 최소화할 거예요. 개발팀과의 협업을 더욱 긴밀히 하고, 테스트 활동을 더욱 앞당길 계획이랍니다.
- 테스트 데이터 관리 시스템 구축 🗃️: 테스트의 품질은 테스트 데이터의 품질에 달려있어요. 실제와 유사하고 다양한 시나리오를 커버할 수 있는 테스트 데이터를 효율적으로 생성하고 관리하는 시스템을 구축할 거예요. 이를 통해 테스트의 신뢰성을 높이고, 필요한 데이터를 빠르게 확보하여 테스트 시간을 단축할 수 있을 거예요.
우리 팀은 언제나 열려있어요
포스트맨으로 테스트를 꼼꼼하게 만들고, 젠킨스로 이 모든 과정을 자동화한 건 NOL QA 팀에게 정말 혁신적인 경험이었어요. API 안정성을 엄청나게 높이고, 버그를 초기에 잡아서 개발 비용을 확 줄였을 뿐만 아니라, 놀유니버스 안에서 NOL QA 팀의 역할도 훨씬 더 중요해졌죠.
이런 경험이 API 테스트 자동화를 고민하거나 이미 시작하신 분들께 조금이나마 도움이 되었으면 좋겠어요. NOL QA 팀은 항상 배우고 성장하는 것에 진심이고, QA 및 자동화 분야의 동료들과 언제나 소통하고 싶답니다. 궁금한 점이나 더 자세한 내용이 필요하시면 언제든지 댓글로 문의해주세요. 함께 배우고 성장하는 즐거움을 누려봐요.
다음 블로그 게시물에서는 NOL QA 팀이 또 다른 흥미로운 도전을 하고 있는 ‘라이브 환경에서의 UI 모니터링 자동화’에 대해 자세히 소개해 드릴게요. 기대하셔도 좋습니다.
혹시 ‘자동화’에 대한 열정이 넘치고, 끊임없이 성장하고 싶고, 새로운 도전을 즐기며, 제품 품질에 직접적인 영향을 미치고 싶은 분이 계신가요? NOL QA 팀은 항상 가능한 것의 한계를 뛰어넘고, 재미있는 문제들을 해결하며, 서비스 성공에 직접적으로 기여하고 있답니다. 혁신을 장려하고, 전문성 개발 기회가 넘치는 역동적인 QA 팀을 찾고 있다면, 주저하지 말고 지원해 주세요. NOL QA 팀과 함께라면 품질 보증의 새로운 가능성을 경험할 수 있을 거예요.

누구나 마음 편히 놀 수 있는 세상을 함께 만들어갈 동료를 찾고 있어요 :)
