Showing Posts From

테스트

테스트 피라미드: 왜 E2E 테스트만 100개 돌리는 팀은 실패하는가

테스트 피라미드: 왜 E2E 테스트만 100개 돌리는 팀은 실패하는가

테스트 피라미드: 왜 E2E 테스트만 100개 돌리는 팀은 실패하는가 월요일 아침, 빌드가 깨졌다 출근했다. 슬랙에 알림 37개. "빌드 실패", "테스트 타임아웃", "CI 30분째 돌아가는 중". 아침 9시. 젠킨스 보니까 E2E 테스트가 2시간째 돌고 있다. 103개 테스트 중 47번째에서 멈췄다. 셀레니움이 로딩 스피너를 못 찾는다. 또. 옆자리 개발자가 말했다. "배포 언제 돼요? 오전 회의에서 발표해야 하는데." 나도 모른다. 테스트가 끝나봐야 안다. 이게 3일째다.작년 이맘때, 우리는 E2E를 사랑했다 입사 1년 전 이야기다. 그때 우리 팀은 E2E 테스트를 막 도입했다. PM이 말했다. "실제 사용자 시나리오를 테스트해야죠." 맞는 말이었다. 첫 E2E 테스트를 짰다. 로그인 → 상품 검색 → 장바구니 → 결제. 완벽했다. 실제 브라우저에서 돌아갔다. 버그를 3개 찾았다. 개발팀이 감탄했다. "역시 E2E네요!" 그래서 더 짰다. 회원가입 시나리오. 10개 케이스. 결제 시나리오. 15개 케이스. 마이페이지. 8개 케이스. 6개월 후, E2E 테스트가 103개였다. 실행 시간은 2시간 30분. 이때부터 문제가 시작됐다. 금요일 오후, 아무도 배포 안 하려는 이유 금요일 4시. 개발자가 PR 올렸다. "간단한 버그 픽스입니다. 머지 부탁드려요." 나도 간단해 보였다. CSS 한 줄 수정. 그런데 규칙이 있었다. "모든 PR은 E2E 테스트 통과 후 머지." 버튼 눌렀다. 젠킨스가 103개 테스트를 돌리기 시작했다. 2시간 기다렸다. 92번째 테스트에서 실패. "Element not found: 로그아웃 버튼." CSS 수정인데 왜 로그아웃 테스트가 깨지나. 로그 봤다. 타임아웃이었다. 페이지 로딩이 평소보다 2초 늦었다. 재실행했다. 이번엔 통과했다. 시계 봤다. 저녁 7시. 개발자는 퇴근했다. 금요일 배포는 없었다. 다음 주 월요일에 머지했다. CSS 한 줄 때문에 3일 걸렸다.화요일 점심, CTO가 물었다 회의실. CTO가 들어왔다. "배포 속도가 너무 느립니다. 경쟁사는 하루 10번 배포하는데." 개발 리드가 말했다. "테스트 때문입니다. 2시간 넘게 걸려요." CTO가 나를 봤다. "테스트를 줄일 수 있나요?" 나는 답했다. "어떤 테스트를요? 다 중요합니다." CTO가 물었다. "정말 103개가 다 필요한가요?" 솔직히 모르겠다. 6개월 동안 쌓다 보니 103개가 됐다. 어느 게 중요한지 구분 못 했다. 회의 끝나고 책상 돌아왔다. 구글링했다. "E2E test too slow". 테스트 피라미드가 나왔다. 마틴 파울러의 글이었다. 그때 깨달았다. 우리는 피라미드를 거꾸로 세우고 있었다. 테스트 피라미드, 내가 이해한 것 그림을 그렸다. 삼각형. 피라미드. 밑에서부터:유닛 테스트 (70%) 통합 테스트 (20%) E2E 테스트 (10%)우리 팀 현실:유닛 테스트 (5%) 통합 테스트 (5%) E2E 테스트 (90%)완전 거꾸로였다. 왜 피라미드 모양이어야 하나. 이유는 간단했다. 속도. 유닛 테스트: 0.1초. 통합 테스트: 2초. E2E 테스트: 90초. 안정성. 유닛 테스트: 깨지면 코드 문제. 통합 테스트: 깨지면 연동 문제. E2E 테스트: 깨지면... 네트워크? 타임아웃? 셀레니움? 모름. 유지보수. 유닛 테스트: 함수 이름 바꾸면 끝. 통합 테스트: API 스펙 바꾸면 수정. E2E 테스트: UI 바뀌면 셀렉터 다 깨짐. 계산해봤다. 103개 E2E를 70개 유닛 + 20개 통합 + 13개 E2E로 바꾸면. 실행 시간:기존: 2시간 30분 변경 후: 12분10배 빨라진다.수요일 오전, 설득 시작 개발팀 회의. 화이트보드에 피라미드 그렸다. "우리는 지금 이렇게 테스트합니다." 거꾸로 된 피라미드. "이래야 합니다." 정상 피라미드. 개발자 한 명이 물었다. "유닛 테스트로 사용자 시나리오를 어떻게 검증해요?" 좋은 질문이었다. "검증 안 합니다. 유닛 테스트는 함수를 테스트해요." 예시 들었다. 로그인 기능. 기존 E2E 테스트:브라우저 열기 로그인 페이지 가기 아이디 입력 비밀번호 입력 로그인 버튼 클릭 메인 페이지 확인 프로필 메뉴 확인 로그아웃 버튼 확인90초 걸렸다. 새로운 방식: 유닛 테스트 (3개):비밀번호 검증 함수 테스트 (0.1초) 토큰 생성 함수 테스트 (0.1초) 세션 저장 함수 테스트 (0.1초)통합 테스트 (2개):로그인 API 테스트 (2초) 토큰 검증 API 테스트 (2초)E2E 테스트 (1개):실제 로그인 시나리오 (30초)총 실행 시간: 34.3초. 기존 대비 62% 빠르다. 더 중요한 건 안정성이었다. E2E가 깨지면 원인 찾기 어렵다. 유닛이 깨지면 정확히 어느 함수가 문제인지 안다. 개발 리드가 말했다. "좋은데, 누가 유닛 테스트 짜죠?" "개발자들이요." 분위기가 싸해졌다. 목요일 오후, 반발 개발자 세 명이 찾아왔다. "유닛 테스트 짜는 시간 있으면 기능 개발하겠습니다." 예상했던 반응이다. 물었다. "지난주 버그 기억나요? 결제 금액 계산 오류." 기억한다는 표정이었다. "그거 유닛 테스트 있었으면 5분 만에 찾았어요. 우리는 2시간 걸렸죠." 계산해줬다. 한 달 버그 수정 시간: 40시간. 유닛 테스트 작성 시간: 주당 2시간, 한 달 8시간. 이득: 32시간. "기능 개발 시간 더 생깁니다." 한 명이 고개 끄덕였다. 다른 개발자가 물었다. "QA가 도와줄 건가요?" "유닛은 어렵습니다. 코드를 제가 다 모르니까. 대신 통합 테스트는 제가 짜겠습니다." 역할 분담했다.개발자: 유닛 테스트 (함수, 클래스) 나: 통합 테스트 (API, 데이터베이스) 나: E2E 테스트 (핵심 시나리오만)일주일 시범 운영 제안했다. CTO가 승인했다. 2주 후, 숫자로 말하기 스프레드시트 만들었다. 비교표. 기존 방식 (2주):총 테스트: 103개 (E2E 100개, 통합 3개) 실행 시간: 평균 2시간 18분 빌드 실패율: 23% (Flaky 테스트) 평균 배포 시간: PR 머지부터 4.2시간 버그 발견: 17개 프로덕션 버그: 5개새 방식 (2주):총 테스트: 187개 (유닛 132개, 통합 38개, E2E 17개) 실행 시간: 평균 14분 빌드 실패율: 8% 평균 배포 시간: 28분 버그 발견: 31개 프로덕션 버그: 2개숫자가 말해줬다. 테스트는 81% 늘었다. 시간은 90% 줄었다. 버그는 더 많이 찾았다. 프로덕션 버그는 60% 줄었다. 개발자들이 놀랐다. "유닛 테스트가 버그를 이렇게 많이 찾네요." 당연했다. 유닛 테스트는 엣지 케이스를 확인한다. E2E는 해피 패스만 확인한다. 예시. 결제 금액 계산 함수. E2E로는 1개 케이스만 확인했다. "10,000원 상품 + 2,500원 배송비 = 12,500원" 유닛으로는 12개 케이스 확인했다.음수 금액 0원 천억 원 소수점 null 값 할인 적용 쿠폰 중복 등등실제로 버그 3개 찾았다. 음수 금액 처리 안 됨. 쿠폰 중복 적용됨. 천억 원 넘으면 오버플로우. E2E로는 절대 못 찾을 버그들이었다. 한 달 후, E2E 17개 선택 기준 회의. "E2E 103개 중 17개만 남기기." 기준 세웠다. 남길 E2E:매출 직결 시나리오 (결제, 환불) 유닛/통합으로 커버 안 되는 것 (브라우저 특성) 여러 시스템 연동 필수 (결제사, 배송사) 사용자 경험 크리티컬 (회원가입, 로그인) 법적 필수 기능 (개인정보 처리)버릴 E2E:유닛으로 커버 가능 (계산, 검증) 통합으로 충분 (API 호출) UI만 다른 중복 케이스 (버튼 위치만 다름) Flaky한 테스트 (타임아웃 자주 남) 비즈니스 임팩트 낮음 (어드민 페이지 필터)103개 리스트 출력했다. 하나씩 분류했다. 결과:유닛으로 이동: 61개 통합으로 이동: 25개 E2E 유지: 17개17개 E2E 리스트:회원가입 (이메일 인증 포함) 로그인 (소셜 로그인 포함) 상품 검색 → 상세 → 장바구니 결제 (카드, 계좌이체, 카카오페이) 주문 조회 환불 신청 리뷰 작성 1:1 문의 배송지 변경 쿠폰 적용 포인트 사용 정기 구독 신청 구독 해지 비밀번호 찾기 회원 탈퇴 장바구니 비우기 최근 본 상품이 17개만 돌리면 35분 걸렸다. 충분했다. 핵심 사용자 플로우는 다 커버했다. 3개월 후, 새로운 문제 평화로웠다. 배포는 빨라졌다. 하루 평균 8번 배포했다. 그런데 새로운 문제가 생겼다. 유닛 테스트가 너무 많아졌다. 418개. 개발자들이 열심히 짰다. 너무 열심히 짰다. 실행 시간이 3분으로 늘었다. 예전엔 30초였다. 왜? 봤더니 중복 테스트가 많았다. 예시. 회원가입 API. 유닛 테스트:이메일 형식 검증 (유닛) 비밀번호 길이 검증 (유닛) 닉네임 특수문자 검증 (유닛)통합 테스트:이메일 형식 검증 (통합) 비밀번호 길이 검증 (통합) 닉네임 특수문자 검증 (통합)중복이었다. 개발자한테 말했다. "유닛에서 검증했으면 통합에서 또 할 필요 없어요." "그럼 통합에서는 뭘 테스트해요?" "연동이요. 데이터베이스에 제대로 저장되는지. 이메일이 발송되는지." 정리했다. 유닛 테스트 역할:비즈니스 로직 계산, 검증, 변환 빠른 피드백통합 테스트 역할:시스템 연동 데이터베이스, API, 외부 서비스 실제 환경 검증E2E 테스트 역할:사용자 시나리오 여러 시스템 통합 최종 확인역할이 명확해지니까 중복이 줄었다. 418개 유닛 → 312개. 실행 시간 3분 → 1분 20초. 6개월 후, 숫자 보고 CTO 보고. 반년 데이터. 테스트 현황:유닛: 312개 통합: 94개 E2E: 17개 총: 423개실행 시간:전체: 평균 16분 유닛만: 1분 20초 통합까지: 9분 E2E 포함: 16분배포 속도:하루 평균: 11.3번 6개월 전: 1.4번 증가율: 707%품질:프로덕션 버그: 월 2.1개 6개월 전: 월 8.7개 감소율: 76%개발 생산성:기능 개발 시간: 37% 감소 (버그 수정 시간 줄어서) 핫픽스: 월 1.2회 (기존 5.8회)CTO가 만족했다. "다른 팀에도 적용합시다." 복잡했다. 지금, 다른 팀 코칭 중 지금은 백엔드팀 코칭 중이다. 그들도 비슷했다. 통합 테스트 200개. 실행 시간 40분. 피라미드 그려줬다. "유닛 테스트 먼저 짜세요." 그들이 물었다. "API 테스트가 있는데 왜 유닛이 필요해요?" 설명했다. API 테스트는 느리다. 서버 띄우고, DB 연결하고, 요청 보내고. 한 테스트에 2초. 유닛 테스트는 빠르다. 함수만 호출. 한 테스트에 0.01초. 200배 차이. 더 중요한 건 디버깅이다. API 테스트가 깨지면:라우팅 문제? DB 문제? 비즈니스 로직 문제? 권한 문제? 네트워크 문제?모른다. 다 확인해야 한다. 유닛 테스트가 깨지면:정확히 이 함수가 문제.끝. 백엔드팀이 2주째 시도 중이다. 유닛 78개 추가했다. 통합 200개 → 143개로 줄였다. 실행 시간 40분 → 18분. 잘되고 있다. 내가 배운 것들 6개월간 배웠다. 1. E2E는 마지막 방어선이다. 모든 걸 E2E로 테스트하려고 하면 망한다. E2E는 비싸다. 느리고, 불안정하고, 유지보수 어렵다. 핵심만 남겨라. 2. 유닛 테스트가 제일 중요하다. 처음엔 안 믿었다. "사용자 시나리오를 어떻게 유닛으로 테스트해?" 하지만 버그의 80%는 로직 문제다. 로직은 유닛으로 잡는다. 3. 피라미드는 속도다. 10분 안에 피드백 받아야 한다. 그래야 개발자가 컨텍스트 유지한다. 2시간 후 피드백은 의미 없다. 4. 테스트도 코드다. 중복 제거해야 한다. 리팩토링 해야 한다. 유지보수 해야 한다. 테스트가 많다고 좋은 게 아니다. 5. 역할 분담이 명확해야 한다. 유닛, 통합, E2E의 역할을 모두가 이해해야 한다. 안 그러면 중복 테스트 천지가 된다. 6. 숫자로 말해야 한다. "E2E 줄입시다"는 설득 안 된다. "실행 시간 90% 줄고, 버그 60% 줄어듭니다"는 설득된다. 7. 점진적으로 바꿔야 한다. 한 번에 103개 다 못 바꾼다. 일주일 시범, 한 달 확대, 석 달 정착. 이게 현실이다. 여전히 어려운 것 완벽하진 않다. E2E 선택 기준이 주관적이다. "이건 E2E로 해야 해요" vs "통합으로 충분해요" 여전히 의견 갈린다. 내 기준:돈이 오가는가? → E2E UI 인터랙션이 복잡한가? → E2E API만 호출하는가? → 통합 함수 호출인가? → 유닛하지만 애매한 경우가 많다. 개발자 설득이 계속 필요하다. 신입이 들어온다. 또 설명해야 한다. "왜 이런 걸 유닛으로 짜요?" 피라미드 그림 그리는 게 일상이 됐다. Flaky 테스트는 여전하다. E2E 17개 중 3개는 가끔 깨진다. 네트워크 타임아웃. 타이밍 이슈. 재시도 로직 넣었다. 3번 재시도. 2번 성공하면 통과. 완벽한 해결은 아니다. 커버리지 압박. PM이 물어본다. "커버리지 몇 퍼센트예요?" "유닛 78%, 통합까지 합치면 86%." "왜 100% 아니에요?" 설명한다. "100% 커버리지가 목표가 아니에요. 중요한 로직 커버가 목표예요." 이해 안 할 때도 있다. E2E 유지보수는 여전히 힘들다. UI 바뀌면 E2E 깨진다. 버튼 ID 바뀜. 셀렉터 수정. CSS 클래스 바뀜. 다시 수정. 17개로 줄었지만 여전히 일이다. 금요일 저녁, 배포 금요일 5시. 개발자가 PR 올렸다. "새 기능: 위시리스트 공유" 리뷰했다. 머지 승인. CI 돌아간다. 유닛 312개: 1분 20초. 통과. 통합 94개: 7분 40초. 통과. E2E 17개: 6분 30초. 통과. 총 15분 30초. 배포 버튼 눌렀다. 프로덕션 올라갔다. 5시 17분에 시작해서 5시 33분에 끝났다. 개발자한테 슬랙 보냈다. "배포 완료. 확인해보세요." "벌써요? 감사합니다!" 퇴근했다. 6시 10분. 작년 같았으면 9시까지 E2E 돌아가는 걸 지켜봤을 것이다. 지금은 정시 퇴근한다. 테스트 피라미드 덕분이다.E2E 100개 돌리는 팀은 느리고, 불안정하고, 결국 무너진다. 피라미드는 거꾸로 세우는 게 아니다. 밑에서부터 차곡차곡 쌓는 거다. 그게 빠르고, 안정적이고, 지속 가능한 방법이다.