“코드는 잘 돌아가는데요?” 웹개발 테스트가 배포를 살린다
내 컴퓨터에서는 멀쩡했던 기능이 배포 후에만 고장 나고, 작은 문구 수정이 결제 화면의 오류로 이어집니다. 이런 경험이 반복되면 개발자는 코드를 고치는 시간보다 변경을 두려워하는 데 더 많은 에너지를 쓰게 됩니다. 크림코드는 웹개발 테스트 자동화를 현업에 적용해 온 테스트 엔지니어 강이든을 만나, 어디까지 무엇을 검사해야 하는지 구체적으로 물었습니다.
“직접 눌러 봤는데요?” 수동 확인만으로 놓치는 것
Q. 화면을 직접 확인했다면 테스트 코드는 없어도 되지 않나요?
A. 직접 눌러 보는 탐색적 테스트는 꼭 필요하지만, 사람이 매번 같은 조건을 빠짐없이 재현하기는 어렵습니다. 로그인한 사용자와 비회원, 빈 장바구니와 품절 상품, 느린 네트워크와 중복 클릭처럼 조건이 갈라지기 시작하면 확인해야 할 조합이 급격히 늘어납니다. 기능 하나를 수정할 때마다 이 모든 경로를 기억에 의존한다면 누락은 능력보다 구조의 문제입니다.
테스트 코드는 개발자의 판단을 대신하는 장치가 아니라 이미 합의한 동작을 반복 확인하는 안전망입니다. 프로그래밍이 컴퓨터에 수행 절차를 표현하는 일이라는 기본 개념은 프로그래밍 용어 설명에서도 살펴볼 수 있습니다. 테스트 역시 입력과 기대 결과를 명확한 절차로 표현한다는 점에서 제품 코드와 같은 개발 작업입니다.
- 수동 확인이 유리한 영역: 낯선 사용 흐름 탐색, 문구의 자연스러움, 시각적 어색함, 새 기능의 초기 검증
- 자동화가 유리한 영역: 로그인 권한, 금액 계산, 데이터 변환, 폼 검증처럼 결과가 명확하고 반복되는 규칙
- 함께 써야 하는 이유: 자동화가 회귀 오류를 지키는 동안 개발자는 새로운 위험과 사용성을 집중해서 살필 수 있음
“직접 눌러 봤다”는 현재 한 번 성공했다는 뜻입니다. “다음 변경 뒤에도 같은 규칙이 유지된다”는 보장은 테스트 코드가 맡아야 합니다.
단위·통합·E2E 테스트는 무엇이 다른가요?
Q. 이름은 많이 들었지만 어느 기능에 어떤 테스트를 써야 할지 헷갈립니다
A. 구분 기준은 도구 이름이 아니라 한 번에 연결해 검사하는 범위입니다. 단위 테스트는 가격 계산 함수나 이메일 검증 규칙처럼 작은 로직을 빠르게 확인합니다. 통합 테스트는 API와 데이터베이스, 컴포넌트와 상태 관리처럼 둘 이상의 요소가 실제 계약대로 협력하는지 검증합니다. E2E 테스트는 브라우저에서 사용자가 로그인하고 상품을 선택해 주문을 완료하는 전체 흐름을 따라갑니다.
범위가 넓을수록 실제 사용자 경험에 가까워지지만 실행 시간이 길고 실패 원인을 찾기 어려워집니다. 반대로 단위 테스트만 많으면 개별 함수는 정상이어도 연결 지점에서 깨지는 문제를 놓칠 수 있습니다. 그래서 핵심 규칙은 단위 테스트로 촘촘하게 보호하고, 중요한 연결은 통합 테스트로 확인하며, 사업상 치명적인 사용자 경로만 E2E로 지키는 구성이 실용적입니다.
| 종류 | 적합한 대상 | 장점 | 주의점 |
|---|---|---|---|
| 단위 테스트 | 계산·변환·검증 함수 | 빠르고 원인 파악이 쉬움 | 구현 세부사항만 검사하지 않기 |
| 통합 테스트 | API·DB·컴포넌트 연결 | 실제 계약 오류를 잘 발견함 | 테스트 데이터 격리가 필요함 |
| E2E 테스트 | 가입·결제·예약 핵심 흐름 | 사용자 관점의 신뢰도가 높음 | 느리고 환경 변화에 민감함 |
- 할인율과 배송비 계산은 단위 테스트부터 작성합니다.
- 주문 API가 재고를 차감하고 결과를 저장하는 과정은 통합 테스트가 적합합니다.
- 회원이 실제 화면에서 주문을 끝내는 대표 경로는 E2E 테스트로 보호합니다.
테스트를 많이 쓰면 개발 속도가 느려지지 않나요?
Q. 일정이 촉박할수록 테스트부터 빼자는 말이 나옵니다
A. 첫 작업 시간만 보면 테스트를 생략한 코딩이 빠릅니다. 하지만 개발 속도는 코드를 처음 입력한 시간보다 요구사항 변경, 리뷰, 장애 대응, 재배포까지 포함해 측정해야 합니다. 테스트가 없으면 작은 수정에도 관련 화면을 전부 다시 확인해야 하고, 담당자가 바뀔 때 기존 동작을 설명하는 비용도 커집니다.
그렇다고 모든 줄에 테스트를 붙이는 방식도 효율적이지 않습니다. 버튼의 내부 변수명처럼 사용자가 관찰할 수 없는 구현보다, 클릭했을 때 어떤 요청이 발생하고 성공 또는 실패 메시지가 어떻게 나타나는지를 검사해야 합니다. 테스트 개수보다 실패했을 때 알려 주는 정보의 가치가 중요합니다. 코딩의 개념과 활용 범위를 짚을 때는 코딩 지식백과 설명도 기초 참고 자료로 활용할 수 있습니다.
- 장애 비용이 큰 기능을 먼저 고릅니다. 결제 금액, 권한, 개인정보 처리, 예약 수량이 대표적입니다.
- 최근 세 달 동안 자주 깨진 부분을 찾아 회귀 테스트로 바꿉니다. 같은 버그를 두 번 고치고 있다면 우선순위가 높습니다.
- 코드 리뷰에서 이해하기 어려운 조건문을 발견하면 입력과 기대 결과를 테스트 이름으로 남깁니다.
- 단순 스타일 변경이나 실험 단계의 화면은 무리하게 자동화하지 않고 수동 탐색 범위를 기록합니다.
작은 팀이라면 처음부터 높은 커버리지 수치를 목표로 잡지 않아도 됩니다. 배포를 막아야 할 핵심 시나리오 세 개를 선정하고, 오류가 반복될 때마다 테스트를 하나씩 추가해 보세요. 두세 번의 배포가 지나면 테스트 작성 시간이 보험료가 아니라 재확인 시간을 줄이는 개발 자산으로 보이기 시작합니다.
좋은 테스트 코드는 어떤 문장으로 시작하나요?
Q. 테스트가 자주 깨지고 읽기도 어렵다면 무엇부터 바꿔야 합니까?
A. 좋은 테스트는 준비, 실행, 검증의 흐름이 눈에 보여야 합니다. 예를 들어 “쿠폰을 적용한다”보다 “주문 금액이 5만 원이고 10% 쿠폰이 유효할 때 최종 결제액은 4만5천 원이다”처럼 조건과 결과를 이름에 담습니다. 실패 로그만 읽어도 어떤 사업 규칙이 무너졌는지 이해할 수 있어야 유지보수가 쉬워집니다.
테스트 데이터도 의미가 드러나야 합니다. 임의의 숫자와 문자열을 여러 줄 늘어놓기보다 정상 회원, 만료 쿠폰, 품절 상품을 만드는 작은 팩토리를 사용하면 의도가 선명해집니다. 다만 공통 함수를 지나치게 추상화하면 테스트를 이해하려고 여러 파일을 오가게 됩니다. 반복 제거보다 한 화면에서 시나리오가 읽히는 것을 우선해도 좋습니다.
- Given: 로그인 회원에게 사용 가능한 할인 쿠폰과 재고 1개의 상품을 준비합니다.
- When: 회원이 쿠폰을 선택하고 주문 요청을 한 번 전송합니다.
- Then: 할인된 금액으로 주문이 저장되고 재고가 0이 되었는지 확인합니다.
- And: 같은 요청이 다시 들어와도 주문과 결제가 중복 생성되지 않는지 검증합니다.
테스트가 제품 코드의 내부 구조를 따라가면 리팩터링할 때 함께 무너집니다. 사용자가 관찰할 수 있는 입력과 결과를 중심으로 쓰면 구현을 바꿔도 안전망은 남습니다.
외부 API를 모의 처리할 때도 주의가 필요합니다. 모든 응답을 성공으로 고정하면 실제 연동 규격이 바뀌어도 알아차리지 못합니다. 성공, 인증 만료, 요청 제한, 응답 지연처럼 현실적인 경우를 나누고, 핵심 연동에는 실제 규격과 맞는지 확인하는 계약 테스트를 추가해야 합니다.
실무 웹개발 프로젝트에는 어떻게 도입하나요?
Q. 테스트가 전혀 없는 서비스라면 첫 주에 무엇을 해야 할까요?
A. 기존 코드를 전부 테스트 가능한 형태로 바꾸려 하지 마세요. 먼저 최근 장애 기록과 고객 문의를 살펴보고 위험 지도를 만듭니다. 매출에 직접 영향을 주는 경로, 권한이 잘못되면 문제가 커지는 기능, 수정이 잦은 모듈을 표시하면 테스트를 어디서 시작해야 할지 자연스럽게 드러납니다.
첫 테스트는 이미 동작하는 기능의 현재 행동을 기록하는 특성 테스트로 작성할 수 있습니다. 기대했던 설계와 다소 다르더라도 현재 입력과 출력을 먼저 고정한 뒤 리팩터링합니다. 이후 바꾸려는 규칙을 새 테스트로 표현하고 코드를 수정하면, 오래된 시스템에서도 안전한 변경 범위를 조금씩 넓힐 수 있습니다.
- 1일 차: 장애가 나면 손실이 큰 사용자 흐름 세 개를 선정하고 성공·실패 조건을 문장으로 적습니다.
- 2일 차: 테스트 실행 명령과 데이터 초기화 방식을 통일해 누구나 같은 결과를 얻도록 합니다.
- 3일 차: 계산이나 권한 규칙 중 하나를 골라 빠른 단위 테스트를 작성합니다.
- 4일 차: API와 저장소가 연결되는 통합 테스트를 추가하고 외부 서비스는 통제 가능한 대역으로 분리합니다.
- 5일 차: 핵심 사용자 경로 하나를 E2E로 만들고 코드 저장소의 자동 검사 과정에 연결합니다.
자동 검사는 두 층으로 운영하면 기다림을 줄일 수 있습니다. 코드 변경마다 수 분 안에 끝나는 단위·통합 테스트를 실행하고, 시간이 오래 걸리는 전체 E2E 묶음은 병합 전이나 별도 주기로 돌립니다. 실패한 테스트를 무조건 재실행해 통과시키는 관행은 피해야 합니다. 불안정한 테스트는 담당자, 원인, 수정 기한을 기록하고 신뢰를 회복해야 합니다.
최근에는 AI가 테스트 초안과 경계값 후보를 만드는 데 도움을 주지만, 생성된 코드의 기대 결과가 실제 정책과 일치하는지는 사람이 확인해야 합니다. 기업이 AI 활용 역량을 업무 평가와 연결하는 흐름은 AI 업무 역량 관련 보도에서도 확인할 수 있습니다. AI에게 테스트를 맡긴다는 말은 검증 책임을 넘긴다는 뜻이 아니라, 반복 작성 시간을 줄이고 개발자가 위험 판단에 집중한다는 뜻에 가깝습니다.
“커버리지 100%면 버그가 없나요?”라는 질문에 답한다면
Q. 팀의 테스트 목표를 숫자로 세워도 괜찮습니까?
A. 코드 커버리지는 테스트가 실행한 코드의 비율이지, 올바른 결과를 충분히 검증했다는 보증서가 아닙니다. 조건문을 한 번 지나가기만 하고 결과를 확인하지 않아도 수치는 올라갈 수 있습니다. 반대로 외부 라이브러리 연결부나 단순 설정 코드는 커버리지가 낮아도 위험이 크지 않을 수 있습니다. 따라서 100%라는 숫자를 성과 목표로 걸면 의미 없는 테스트가 늘어날 가능성이 큽니다.
그렇다면 커버리지를 버릴 필요는 없습니다. 새로 변경한 핵심 로직이 아무 테스트도 거치지 않았는지 발견하는 경고등으로 사용하면 유용합니다. 전체 평균보다 변경 코드의 커버리지, 조건 분기 누락, 실제 장애를 재현하는 테스트의 존재 여부를 함께 보세요. 숫자가 떨어졌다는 이유만으로 병합을 막기보다 위험한 규칙이 검증되지 않았을 때 대화를 시작하는 기준으로 삼는 편이 낫습니다.
- 테스트가 실패하면 어떤 사용자 피해를 막는지 팀원이 설명할 수 있습니까?
- 정상 입력뿐 아니라 빈 값, 경계값, 중복 요청, 권한 없음도 검증합니까?
- 버그를 수정할 때 같은 문제가 다시 나타나지 않도록 재현 테스트를 먼저 추가합니까?
- 실행 시간이 길어 개발자가 검사를 건너뛰고 있지는 않습니까?
- 불안정한 테스트를 방치해 실패 알림 자체를 무시하게 만들고 있지는 않습니까?
현실적인 목표는 “몇 퍼센트를 채울까?”보다 “이번 변경에서 어떤 실패를 허용할 수 없는가?”로 정하는 것이 좋습니다. 결제 중복과 관리자 권한 노출은 작은 가능성도 막아야 하지만, 추천 문구의 줄바꿈은 배포 후 빠르게 고쳐도 될 수 있습니다. 위험의 크기와 발생 가능성을 함께 평가하면 테스트 범위가 선명해지고, 웹개발 팀은 속도와 안정성을 동시에 관리할 수 있습니다.
다음 기능을 개발할 때는 구현을 끝낸 뒤 커버리지 숫자를 올리려 하지 말고, 코드 작성 전에 실패하면 곤란한 사례 세 가지를 먼저 적어 보세요. 그중 하나는 정상 흐름, 하나는 경계값, 나머지 하나는 권한이나 중복 요청처럼 악용 가능한 흐름으로 고르면 좋습니다. 이 세 문장이 곧 테스트 이름이 되고, 리뷰어에게는 설계 의도를 전달하는 가장 짧은 문서가 됩니다.

- 이전글웹개발 입문은 HTML·CSS·자바스크립트 순서가 가장 빠르다 26.08.18
- 다음글월요일 배포 직전, AI 코딩 에이전트가 바꾸는 웹개발 현장 26.08.16
등록된 댓글이 없습니다.
