개념
레슨 2에서 배운 리스크가 "얼마나 깊게 볼 것인가"를 정한다면, 이 레슨은 "변경 규모가 얼마나 넓게 볼 것인가에 영향을 준다" 는 걸 다룬다. 둘은 함께 작동한다.
깊이와 넓이를 나눠서 생각하는 게 이 레슨의 출발점이다. 같은 "테스트를 많이 한다"는 말도 두 방향이 있다. 하나는 한 기능을 파고드는 깊이 — 경계값을 더 촘촘히, 예외 경로를 더 많이 보는 것이다. 다른 하나는 건드리지도 않은 기능까지 훑는 넓이 — 이번에 손대지 않은 로그인, 검색, 마이페이지까지 다시 돌려보는 것이다. 리스크는 깊이를 정하고, 변경 규모는 넓이를 정한다.
규모는 "코드 줄 수"가 아니다
가장 먼저 깨야 할 오해가 있다. 변경 규모를 diff의 줄 수로 재는 습관이다.
규모 = 바뀐 코드의 양이 아니라, 그 변경이 닿는 기능의 수다.
500줄을 고쳤어도 그 500줄이 관리자 화면 한 곳에만 쓰이면 영향 범위는 좁다. 반대로 공통 날짜 포맷 함수의 한 줄을 고치면 그 함수를 쓰는 화면 전부가 영향권이다. 줄 수는 개발 난이도를 재고, 영향 범위는 테스트 범위를 잰다. QA가 봐야 하는 건 뒤쪽이다. 모듈 9 레슨 8("PR diff에서 리스크 읽어내기")에서 "변경 크기와 리스크는 비례하지 않는다"고 배운 것이 계획 단계에서는 이 형태로 나타난다.
변경 유형 다섯 가지와 테스트 범위
실무에서 마주치는 변경을 다섯 가지로 나눠두면 판단이 빨라진다. 앞의 셋은 익숙한 소·중·대규모이고, 뒤의 둘이 사고가 자주 나는 자리다.
| 유형 | 전형적인 예 | 코드 변경량 | 영향 범위 | 테스트 범위 |
|---|---|---|---|---|
| 핫픽스 | 문구 한 줄, 색상 값, 오타 | 1~10줄 | 대개 좁음 (단, 아래 주의) | 확인 테스트 + 해당 화면 스모크 |
| 마이너 — 기존 기능 수정 | 정렬 옵션 추가, 유효성 규칙 변경 | 수십 줄 | 그 기능 + 직접 호출부 | 기능 전체 테스트 + 인접 기능 새니티 |
| 메이저 — 기능 추가 | 쿠폰 기능 신설, 결제 수단 추가 | 수백 줄 | 새 기능 + 얽히는 기존 기능 | 신규 기능 전수 + 연관 영역 회귀 |
| 신규 모듈 | 정산 시스템 신설, 앱 전면 개편 | 수천 줄 | 시스템 전반 | 테스트 레벨 전체 동원 + 전면 회귀 |
| 인프라·라이브러리 업그레이드 | Node 버전 업, 결제 SDK 마이너 업, DB 마이그레이션 | 거의 0줄 | 전체 | 전면 스모크 + 핵심 흐름 전면 회귀 |
마지막 줄이 이 표의 핵심이다. 애플리케이션 코드가 한 줄도 안 바뀌었는데
영향 범위는 가장 넓은 유형 이 존재한다. package.json의 버전 숫자 하나,
서버 설정 파일의 값 하나, DB 인덱스 하나가 시스템 전체의 동작을 바꿀 수 있다.
그런데 PR을 열어보면 diff가 두 줄뿐이라 리뷰도 테스트도 가볍게 지나간다.
이 불일치가 실무 사고의 단골 원인이다.
인프라·라이브러리 변경에서 특히 확인해야 할 것들이다.
| 변경 | 조용히 깨지는 것 |
|---|---|
| 런타임 버전 업 (Node, JDK, Python) | 날짜·숫자 포맷, 정렬 순서, 암호화 기본값 |
| 라이브러리 마이너 업그레이드 | 기본 옵션값 변경, 경고가 오류로 승격, 타임아웃 기본값 |
| 서버 설정값 변경 | 세션 만료 시간, 파일 업로드 한도, 커넥션 풀 크기 |
| DB 스키마 마이그레이션 | 기존 데이터의 NULL 처리, 인덱스 사라져 느려진 조회 |
| 인증서·도메인·CDN 교체 | 캐시된 옛 자원, 모바일 앱의 인증서 고정(pinning) |
규모 × 리스크 매트릭스
레슨 2의 리스크와 이 레슨의 규모를 함께 놓으면 더 정교하게 판단할 수 있다.
| 리스크 낮음 | 리스크 높음 | |
|---|---|---|
| 규모 작음 | 최소한의 확인만 | 작지만 신중하게 — 좁고 깊게 |
| 규모 큼 | 넓지만 얕게 — 전체 스모크 위주 | 넓고 깊게 — 가장 많은 시간 배정 |
예를 들어 "결제 로직 한 줄 수정"은 규모는 작지만 리스크는 높다 — 이 경우 "좁지만 깊게" 본다(관련된 결제 시나리오는 꼼꼼히, 그 외 영역은 안 봐도 됨). 반대로 "다국어 지원 국가를 5개 늘리는" 변경은 규모는 크지만 리스크는 상대적으로 낮을 수 있다 — "넓지만 얕게"(각 언어별로 기본 동작만 확인) 접근이 적절하다.
네 칸 중 실무에서 가장 자주 틀리는 칸은 오른쪽 위 다. 규모가 작으면 반사적으로 "가볍게"라고 판단해버리기 때문이다. 이 칸을 놓치지 않으려면 규모를 먼저 보지 말고 영향 범위를 먼저 확인하는 순서 로 습관을 바꿔야 한다. 그 순서가 다음에 볼 영향 분석이다.
영향 분석 — 변경이 어디까지 닿는지 추적하기
영향 분석(impact analysis)은 거창한 기법이 아니라 "이 변경이 쓰이는 곳을 끝까지 따라가 보는" 절차 다. QA가 개발자 없이 혼자서도 상당 부분 할 수 있다.
| 단계 | 무엇을 하나 | QA가 던지는 질문 |
|---|---|---|
| 1. 변경 지점 확인 | PR diff에서 바뀐 파일 목록을 본다 | 어떤 파일이 바뀌었나? 화면인가 공통 코드인가? |
| 2. 호출 관계 추적 | 바뀐 함수·컴포넌트를 코드 검색으로 찾는다 | 이걸 쓰는 곳이 몇 군데인가? |
| 3. 데이터 경로 추적 | API·DB 스키마 변경 여부를 본다 | 같은 API를 쓰는 다른 화면이 있나? |
| 4. 실행 환경 확인 | 설정·라이브러리·배포 대상이 바뀌었나 | 앱·웹·배치 중 어디에 영향이 가나? |
| 5. 범위 확정 | 1~4에서 나온 목록에 리스크를 매긴다 | 여기까지가 이번 회귀 범위다 |
2단계가 핵심인데, 방법은 생각보다 단순하다. 바뀐 함수나 컴포넌트의 이름을
그대로 저장소 전체에서 검색 하면 된다. GitHub 검색창이나 에디터의 전체
검색으로 formatPrice 를 찾으면 그 함수를 쓰는 파일이 전부 나온다. 결과가
3개면 좁은 변경이고, 40개면 그 자체로 "이건 공통 코드다"라는 신호다.
모듈 9 레슨 7에서 배운 Git·PR 구조와 레슨 8의 diff 읽기가 여기서 계획
문서로 이어진다.
diff에서 영향 범위를 읽는 신호 를 정리하면 이렇다.
| diff에서 보이는 것 | 해석 | 회귀 범위 |
|---|---|---|
| 한 화면 파일만 수정 | 국소 변경 | 그 화면 + 진입 경로 |
common/, shared/, utils/ 경로 | 공통 코드 | 그것을 쓰는 모든 화면 |
| API 응답 필드 추가·이름 변경 | 계약 변경 | 그 API를 쓰는 모든 클라이언트(앱 포함) |
| DB 마이그레이션 파일 포함 | 데이터 구조 변경 | 조회·저장·통계·배치 전부 |
package.json / lock 파일만 변경 | 의존성 변경 | 전면 스모크 |
설정 파일(.env, config) 변경 | 런타임 동작 변경 | 환경별 차이 확인 필수 |
| 테스트 코드가 함께 삭제됨 | 위험 신호 | 왜 지웠는지 반드시 질문 |
마지막 줄은 코드 변경이 아니라 사람에 대한 신호다. 기존 테스트가 실패해서 지웠다면, 그 실패가 바로 이번 변경의 부작용일 수 있다.
개발자에게 물어보는 것도 영향 분석이다. 코드를 다 읽을 수 없을 때 가장 효율적인 질문은 "이 변경이 영향 주는 다른 기능이 있나요?"가 아니라 더 구체적인 형태다 — "이 함수 쓰는 데가 여기 말고 또 있나요?", "API 응답 형태가 바뀌면 앱도 같이 배포해야 하나요?", "이 설정, 스테이징이랑 운영이 값이 다른가요?" 답이 "아마 없을 거예요"면 그건 확인이 아니라 추측이므로, 범위에 넣어두는 편이 안전하다.
스모크·새니티·회귀 — 셋은 다른 것이다
세 용어를 섞어 쓰는 팀이 많은데, 목적도 시점도 범위도 다르다.
| 스모크(Smoke) | 새니티(Sanity) | 회귀(Regression) | |
|---|---|---|---|
| 목적 | 빌드가 테스트할 만한 상태인가 | 이번 수정이 의도대로 됐나 | 기존 기능이 안 깨졌나 |
| 범위 | 전체를 얕게 (핵심 흐름) | 변경된 부분 주변만 좁게 | 넓게, 반복 실행 |
| 시점 | 배포 직후 가장 먼저 | 수정본 전달 직후 | 릴리즈 전 |
| 실패하면 | 테스트 중단, 빌드 반려 | 재수정 요청 | 결함 보고 후 판단 |
| 자동화 | 거의 항상 (가장 먼저 자동화) | 대개 수동 | 자동화 우선순위 높음 |
| 비유 | 전원이 켜지나 | 고친 데가 고쳐졌나 | 다른 데가 망가졌나 |
스모크의 실패는 다른 둘과 성격이 다르다. 스모크가 깨지면 결함 하나를 보고하고 넘어가는 게 아니라 그날의 테스트 자체를 멈춘다. 로그인이 안 되는 빌드에서 결제 테스트를 진행하는 건 시간 낭비이기 때문이다. 이 "중단 권한"이 스모크 테스트를 별도로 정의해두는 실질적인 이유다.
새니티는 회귀의 축소판처럼 보이지만 방향이 반대다. 회귀는 "안 건드린 곳"을 보고, 새니티는 "건드린 곳" 을 본다. 모듈 1에서 배운 확인 테스트 (confirmation test)를 그 주변까지 조금 넓힌 것이 새니티라고 이해하면 쉽다.
변경 규모별로 셋을 어떻게 조합하는지가 이 레슨의 실무 결론이다.
| 변경 유형 | 스모크 | 새니티 | 회귀 |
|---|---|---|---|
| 핫픽스 | 해당 화면만 | 필수 | 직접 연관 케이스 몇 건 |
| 마이너 | 핵심 흐름 | 필수 | 해당 기능 + 인접 기능 |
| 메이저 | 전체 | 필수 | 연관 영역 전면 |
| 신규 모듈 | 전체 | 필수 | 전면 회귀 + 비기능 테스트 |
| 인프라·라이브러리 | 전체 (최우선) | 해당 없음 | 핵심 흐름 전면 |
인프라 변경 줄을 보자. 고친 기능이 없으니 새니티는 대상이 없는데, 스모크와 회귀는 최대 범위다. "수정한 게 없으니 테스트할 것도 없다"는 판단이 정확히 거꾸로 라는 게 이 줄이 말하는 바다.
"문구만 바꿨는데" 터지는 사고들
작은 변경이 큰 사고가 되는 경로에는 반복되는 패턴이 있다. 미리 알아두면 diff를 볼 때 신호로 잡힌다.
| 패턴 | 무슨 일이 일어나나 | 미리 볼 곳 |
|---|---|---|
| 공통 컴포넌트 수정 | 버튼 하나 고쳤는데 그 버튼을 쓰는 30개 화면의 레이아웃이 밀린다 | 컴포넌트 사용처 검색 |
| 문구 키 변경 | 다국어 키 이름을 바꿔 다른 언어에서 빈 문자열·키 노출 | 전 언어 화면 확인 |
| 문구 길이 변화 | 한국어는 맞는데 독일어에서 버튼 밖으로 넘친다 | 긴 언어·좁은 화면 |
| 설정값 변경 | 세션 만료를 30분→10분으로 줄여 결제 중 로그아웃 | 긴 작업 흐름 |
| 라이브러리 마이너 업 | 날짜 라이브러리가 타임존 기본값을 바꿔 하루씩 밀림 | 날짜 표시 전 구간 |
| 정규식·유효성 규칙 수정 | 이메일 규칙을 조이자 기존 회원 일부가 로그인 불가 | 기존 데이터로 확인 |
| 로그·분석 코드 제거 | 기능은 멀쩡한데 매출 대시보드가 0이 된다 | 분석 이벤트 |
| 널 처리 한 줄 추가 | 예외는 막았는데 화면에 "undefined"가 노출된다 | 빈 값·신규 계정 |
여기서 얻을 결론은 하나다. 작은 변경을 크게 테스트하라는 말이 아니라, 크기가 아니라 영향 범위로 판단하라는 말이다. 위 여덟 줄은 전부 diff가 10줄 미만인 변경들이다.
사례 연구 1 — "오타 수정"이 결제를 멈춘 날
금요일 오후, 개발자가 PR을 하나 올린다. 제목은 "결제 버튼 문구 오타 수정".
diff는 두 줄이다. 결제하기 → 결제 하기. QA는 규모가 작다고 판단하고
결제 화면에서 버튼 문구만 확인한 뒤 통과시킨다.
월요일, 앱에서 결제가 안 된다는 문의가 쏟아진다. 원인은 이랬다. 앱의 자동화 테스트와 일부 화면 전환 로직이 버튼 텍스트로 요소를 찾고 있었다. 문구가 바뀌자 앱은 그 버튼을 못 찾았고, 특정 기종에서 결제 화면이 멈췄다.
영향 분석을 했다면 어디서 잡혔을까. 2단계에서 결제하기 라는 문자열을
저장소 전체에서 검색했다면, 웹 화면 코드 외에 앱 저장소와 자동화 스크립트
에서 같은 문자열이 나왔을 것이다. 검색 결과가 한 곳이 아니라는 사실 하나가
"이건 웹만의 문구가 아니다"라는 신호였다.
교훈은 두 가지다. 첫째, 사용자에게 보이는 문자열도 인터페이스 다. 둘째, "문구 변경"이라는 제목을 그대로 규모 판단에 쓰면 안 된다. 제목은 개발자가 자기 관점에서 붙인 것이고, QA는 그 변경이 닿는 곳 을 따로 확인해야 한다.
사례 연구 2 — 숨은 영향을 미리 찾아낸 경우
같은 팀에서 몇 달 뒤. 이번 PR은 "주문 목록 API 응답에 discountAmount 필드
추가"다. 필드를 더하기만 했으니 기존 동작에는 영향이 없어 보인다.
QA가 영향 분석 3단계(데이터 경로 추적)를 돌린다. 이 API를 쓰는 곳을 검색하니 웹 주문 목록 외에 모바일 앱과 정산 배치 가 나왔다. 앱 개발자에게 물으니 구버전 앱이 응답을 엄격하게 파싱해서 모르는 필드가 오면 파싱에 실패 한다는 답이 돌아온다. 배포 전에 발견됐고, 앱 강제 업데이트 대상 버전을 확인해 서버에서 버전별 응답을 분기하는 것으로 정리됐다.
여기서 QA가 한 일은 코드를 깊이 읽은 게 아니다. "이 API를 누가 쓰나"를 검색 한 번으로 확인하고, 답을 아는 사람에게 물어본 것 이 전부다. 영향 분석의 대부분은 이 수준에서 끝난다. 그리고 이 사례처럼 배포 전에 잡히면 비용은 질문 한 번이고, 배포 후에 잡히면 앱 강제 업데이트다.
커버리지 계획을 한 장으로 적기
지금까지의 판단을 릴리즈마다 다시 논쟁하지 않으려면 짧은 문서 하나로 남긴다. 레슨 1의 테스트 계획서 안에 들어가는 실행 단위 문서다.
| 항목 | 이번 릴리즈 |
|---|---|
| 변경 유형 | 마이너 (쿠폰 최소 금액 조건 변경) |
| 영향 범위 | 결제 화면, 장바구니, 주문 API, 정산 배치 |
| 근거 | applyCoupon 사용처 4곳 검색 결과 |
| 스모크 | 핵심 흐름 8건 (자동) |
| 새니티 | 쿠폰 적용·해제 12건 |
| 회귀 | 결제·주문 영역 40건, 정산 배치 5건 |
| 제외한 것 | 회원가입·검색·리뷰 — 변경 경로와 무관 |
| 남은 리스크 | 구버전 앱 미검증 (기기 미보유) |
"제외한 것"과 "남은 리스크" 두 줄이 이 표의 값어치 다. 무엇을 했는지는 누구나 적지만, 무엇을 일부러 안 했는지를 근거와 함께 적어두면 나중에 문제가 생겨도 "판단"이 되고 안 적어두면 "누락"이 된다. 레슨 5의 Go/No-Go 회의에서 그대로 쓰이는 자료이기도 하다.
이 표가 "어디까지 넓게 볼 것인가" 를 정한다면, 레슨 7 은 "각 영역에서 어디까지 깊게 볼 것인가" 를 정한다 — UI 동작부터 정답·권한·접근성·데이터 정합성·비기능·보안까지 7개 계층으로 훑는 체계다. 둘을 함께 쓰면 범위와 깊이가 모두 근거를 갖는다.
실무에서 왜 필요한가
"이번 변경, 얼마나 테스트해야 하나요?"라는 질문에 팀마다 정해진 답이 없으면 매번 같은 논쟁이 반복된다. 규모와 리스크를 기준으로 미리 원칙을 세워두면 ("소규모+저리스크는 확인 테스트만", "대규모+고리스크는 전체 레벨 동원") 이 논쟁이 훨씬 빨리 끝난다. 특히 핫픽스처럼 시간이 촉박한 상황에서, 이 원칙이 없으면 "일단 다 돌려보자"거나 반대로 "시간 없으니 확인만 하자"는 극단으로 치우치기 쉽다.
첫째, 배포 빈도가 높을수록 이 판단이 매일 필요해진다. 한 달에 한 번 배포하던 시절에는 매번 전면 회귀를 돌려도 됐다. 하루에 여러 번 배포하는 팀에서는 불가능하다. 변경마다 범위를 다르게 잡는 능력이 없으면 QA가 배포의 병목이 되고, 그러면 팀은 QA를 우회하기 시작한다.
둘째, 범위를 좁힌 근거를 남기면 QA의 판단이 방어된다. 사고가 났을 때 "왜 못 잡았냐"는 질문은 반드시 나온다. 이때 "영향 범위를 이렇게 판단했고, 이 근거로 이 영역을 제외했다"고 답할 수 있는 것과 "시간이 없어서 못 봤다"고 답하는 것은 완전히 다르다. 전자는 판단의 개선으로 이어지고, 후자는 개인의 책임으로 끝난다. 모듈 3 마지막 레슨에서 배운 설명 가능성 이 릴리즈 범위 차원에서 반복되는 것이다.
셋째, 개발자와 대화하는 언어가 생긴다. "이거 회귀 돌려야 해요"는 협상이 안 되지만, "이 변경이 공통 컴포넌트라 30개 화면에 닿아서 스모크 전면 + 결제 회귀가 필요합니다. 컴포넌트를 새로 만들고 이 화면만 쓰게 하면 범위가 1개로 줄어듭니다"는 협상이 된다. 후자는 개발 방식이 테스트 비용을 바꾼다 는 사실을 눈에 보이게 만든다.
넷째, 산정과 종료 조건의 입력값이 된다. 다음 레슨(레슨 4)에서 일정을 산정할 때, 근거가 되는 것은 "이번에 몇 건을 돌리는가"이고 그 건수는 이 레슨의 범위 판단에서 나온다. 범위를 못 정하면 산정도 못 하고, 산정을 못 하면 종료 조건도 정할 수 없다.