grep

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 테스트 자동화 가 선택이 아닌 필수라는 걸 깨달았어요. 단순히 테스트를 빨리 끝내는 것을 넘어, 더 높은 품질과 안정적인 개발 프로세스를 위한 결단이었죠. 물론 자동화 도입에 따르는 초기 학습 비용이나 시스템 구축의 어려움 같은 단점도 고려했지만, 장기적으로 얻을 수 있는 이점이 훨씬 크다고 판단했습니다.

자동화, 저희에게 이런 마법 같은 변화를 선물해 주었답니다!

이런 직접적인 효과 외에도, NOL QA 팀의 포지션이 확 바뀌었어요. 예전엔 ‘버그 잡는 사람 ’이었다면, 이제는 ‘개발 프로세스 전반의 품질을 책임지는 전략가’가 된 느낌이랄까요? 개발 초기부터 품질을 챙기니, 개발팀도 저희를 더 든든하게 생각하는 것 같아요!

API 테스트 자동화의 주요 이점

2. 포스트맨: API 테스트를 위한 최고의 선택

NOL QA 팀의 API 테스트 자동화 여정에서 빼놓을 수 없는 일등 공신은 바로 포스트맨(Postman)입니다. 직관적인 UI와 강력한 기능 덕분에 API 요청을 만들고, 응답 확인하고, 테스트 스크립트 짜는 게 너무나 쉬웠어요. 그야말로 저희의 ‘최애’ 도구가 되었죠.

2.1. ‘왜 포스트맨인가?’ 심층 분석: 과거의 아픔으로부터 배우다

사실 포스트맨을 만나기 전, 저희는 다른 도구들도 기웃거렸어요. 특히 SoapUI는 조건 분기, 루프, 변수 처리 같은 고급 기능 덕분에 복잡한 테스트 시나리오를 GUI 상에서 시각적으로 설계할 수 있어서 꽤 매력적이었죠. 자동화된 통합 테스트에도 나름 잘 어울렸고요.

하지만… (여기서 잠시 눈물을 닦습니다) 😭

SoapUI는 너무 무겁고, 동작도 불안하고, 심지어는 갑자기 종료되는 일도 잦아서 QA팀을 정말 힘들게 했어요. 생산성은 바닥을 쳤고, 스트레스는 하늘을 찔렀죠. 그러다 포스트맨을 만났고, 운명임을 직감했습니다. 포스트맨으로 ‘갈아타자!’고 결정한 데에는 여러 가지 이유가 있었어요.

포스트맨을 선택한 건 단순히 더 좋은 도구를 찾은 게 아니었어요. 이건 저희 팀이 ‘협업’과 ‘생산성’이라는 두 마리 토끼를 잡기 위한 전략적인 결정이었죠. 덕분에 QA가 더 이상 외딴섬이 아니라, 개발팀과 PM팀을 잇는 든든한 다리 역할을 하게 되었답니다.

2.2. 포스트맨에서 API 테스트 시나리오 구성: 구조가 곧 생명

저희는 포스트맨 안에서 API 엔드포인트들을 체계적으로 정리하는 데 공을 많이 들였어요. 각 서비스 버티컬(예: 사용자 API, 상품 API, 주문 API)마다 전용 컬렉션 을 만들고, 그 안에는 기능별로 폴더를 만들어서 API 엔드포인트를 분류했죠. 마치 잘 정리된 도서관처럼요. 📚

명확성을 위한 예시 구조 (이렇게 정리하면 찾기 쉬워요!)

├── Member API

│ ├── 회원 가입

│ ├── 로그인

│ └── 회원 정보 수정

├── Product API

│ ├── 상품 목록 조회

│ ├── 상품 상세 조회

│ └── 상품 등록

└── Order API

├── 주문 생성

├── 주문 조회

└── 주문 취소

그리고 테스트 전략의 핵심은 바로 포스트맨의 ‘환경(Environment)’ 기능이었어요. 개발, 스테이징, 운영 등 다양한 환경에 따라 API 엔드포인트나 인증 정보 같은 변수들을 유연하게 관리할 수 있었죠. 덕분에 환경만 쓱 바꾸면 똑같은 테스트 컬렉션을 여러 환경에서 돌릴 수 있어서, 일일이 수동으로 수정하는 번거로움이 싹 사라졌답니다.

Postman 컬렉션 및 폴더 구조화 예시 (우리 팀의 정리 비법)

2.3. 지능적인 테스트 케이스 작성: 사용자 흐름부터 엣지 케이스까지 꼼꼼하게

QA팀은 테스트 케이스를 만들 때, 실제 사용자가 서비스를 이용하는 흐름을 최대한 따라가려고 노력했어요. 마치 사용자가 된 것처럼요! 🕵️‍♀️

시나리오 기반 테스트: 실제 사용자처럼, 문서처럼

포괄적인 케이스 구성: 돌다리도 두드려보고 건너자

강력한 테스트 스크립트 작성

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. 변화무쌍한 서비스에 발맞춰 춤추는 테스트 코드

저희는 서비스 개선이 멈추지 않는 한, 테스트 코드 관리도 영원한 숙제라고 생각해요. 그래서 아래와 같은 방법들을 쓰고 있답니다.

3.2. 우리 팀만의 테스트 코드 관리 비법(깃허브는 우리의 보물창고)

효율적으로 테스트 코드를 관리하기 위해 저희는 나름의 규칙을 세우고, GitHub라는 든든한 지원군을 활용하고 있어요.

[파트별 맞춤 관리]

NOL QA 팀은 크게 파트별로 담당 영역을 나눠서 테스트 코드를 관리하고 있어요.

이렇게 파트를 나누니 각자 자기 영역의 전문성을 쑥쑥 키울 수 있고, 해당 도메인의 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 파이프라인에 딱이었죠.

젠킨스 구성 단계: 모든 것을 하나로

  1. Node.js와 Newman 설치 💻: 젠킨스 서버에 Node.js 환경을 구축하고 Newman을 설치하는 게 첫 단계였어요. 그래야 젠킨스에서 Newman 명령어를 쓸 수 있으니까요!
  2. 파이프라인 프로젝트 설정 🏗️: 젠킨스에 새로운 파이프라인 프로젝트를 만들고, 포스트맨 컬렉션과 환경 파일이 있는 Git 저장소를 연결했어요. 덕분에 젠킨스는 항상 최신 버전의 테스트를 가지고 작업할 수 있었죠.
  3. 빌드 스텝 구성 ⚙️: 젠킨스 파이프라인의 빌드 스텝에 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 상태를 항상 투명하게 확인할 수 있고, 혹시라도 문제가 생기면 바로 알아차릴 수 있죠.

주요 모니터링 활동 (우리는 늘 지켜보고 있다!)

슬랙을 통한 투명한 보고와 꼼꼼한 모니터링 덕분에, API 자동화는 단순한 테스트 도구를 넘어 능동적인 품질 정보 시스템 이 되었어요. 덕분에 애플리케이션의 무결성을 항상 지키고, 모든 팀원들에게 실시간으로 유용한 정보를 제공해서 ‘품질은 모두의 책임’이라는 문화를 만들어가고 있답니다.

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

5.2. 가시적인 이점 & 예상치 못한 성과: 자동화, 그 이상의 감동

5.3. 젠킨스, 정기 실행을 넘어: 개발팀도 함께 성장

6. 우리의 미래 비전: API 자동화를 위한 다음 Step!

NOL QA 팀은 중요한 이정표를 달성했지만, API 테스트 자동화 여정은 아직 끝나지 않았어요. NOL QA 팀은 끊임없이 더 나은 방법을 찾고, 역량을 강화하며, 소프트웨어 개발의 모든 단계에 ‘품질’을 더욱 깊숙이 녹여내기 위해 노력하고 있답니다.

주요 미래 Initiative (다음 목표는 이것)

우리 팀은 언제나 열려있어요

포스트맨으로 테스트를 꼼꼼하게 만들고, 젠킨스로 이 모든 과정을 자동화한 건 NOL QA 팀에게 정말 혁신적인 경험이었어요. API 안정성을 엄청나게 높이고, 버그를 초기에 잡아서 개발 비용을 확 줄였을 뿐만 아니라, 놀유니버스 안에서 NOL QA 팀의 역할도 훨씬 더 중요해졌죠.

이런 경험이 API 테스트 자동화를 고민하거나 이미 시작하신 분들께 조금이나마 도움이 되었으면 좋겠어요. NOL QA 팀은 항상 배우고 성장하는 것에 진심이고, QA 및 자동화 분야의 동료들과 언제나 소통하고 싶답니다. 궁금한 점이나 더 자세한 내용이 필요하시면 언제든지 댓글로 문의해주세요. 함께 배우고 성장하는 즐거움을 누려봐요.

다음 블로그 게시물에서는 NOL QA 팀이 또 다른 흥미로운 도전을 하고 있는 ‘라이브 환경에서의 UI 모니터링 자동화’에 대해 자세히 소개해 드릴게요. 기대하셔도 좋습니다.

혹시 ‘자동화’에 대한 열정이 넘치고, 끊임없이 성장하고 싶고, 새로운 도전을 즐기며, 제품 품질에 직접적인 영향을 미치고 싶은 분이 계신가요? NOL QA 팀은 항상 가능한 것의 한계를 뛰어넘고, 재미있는 문제들을 해결하며, 서비스 성공에 직접적으로 기여하고 있답니다. 혁신을 장려하고, 전문성 개발 기회가 넘치는 역동적인 QA 팀을 찾고 있다면, 주저하지 말고 지원해 주세요. NOL QA 팀과 함께라면 품질 보증의 새로운 가능성을 경험할 수 있을 거예요.

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

지금 채용 중인 포지션 보러가기 >