변경 위험과 테스트 선택
110분 안팎
학습 목표
사용자 피해와 영향 경로로 회귀 우선순위를 정합니다.
개념
변경 파일 수보다 사용자 피해를 봅니다
가입 비밀번호 최소 길이가 바뀌었는데 프로필 저장은 그대로라고 해서 프로필 검사를 모두 빼면 세션 전달 문제를 놓칠 수 있습니다. 반대로 닉네임 안내 문구만 바뀌었는데 모든 저장 조합을 먼저 실행하면 시간이 부족해 중요한 권한 검사를 뒤로 밀 수 있습니다. 위험 기반 회귀는 사용자 피해와 영향 경로를 근거로 실행 순서를 정하고 제외 이유를 설명하는 작업입니다.
위험은 실패 가능성과 실패했을 때의 영향을 함께 생각합니다. 발생 확률을 계산할 자료가 없다면 정확한 수치인 것처럼 쓰지 않습니다. 이 레슨은 P0와 P1을 교육용 우선순위로 사용합니다. 만료 뒤 수정이나 타인 정보 노출처럼 피해가 큰 경로는 P0, 직접 변경된 기능의 일반 동작은 P1로 분류합니다. 실제 조직의 등급 정책은 별도 합의가 필요합니다.
시간을 줄인다는 이유로 이미 실패한 검사를 자동 제외하지 않습니다. 불안정한 P0 검사가 있다면 원인 수정, 대체 수동 확인 또는 릴리스 보류가 검토 대상입니다. 선택표에 검사를 넣었다는 사실과 실제 실행했다는 사실도 나눕니다. 이번 브라우저 실습의 출력은 선택 ID와 우선순위이며 앱이 통과했다는 증거가 아닙니다.
직접 영향과 연결 영향을 추적합니다
기능 그래프의 방향은 변경된 원인에서 영향을 받을 소비자로 향합니다. signup→login은 가입 결과가 로그인에서 사용될 수 있다는 뜻이고 login→profile은 인증 결과가 프로필 접근의 전제라는 뜻입니다. 화살표 이름 없이 관계를 저장하면 반대 방향으로 전개할 위험이 있습니다. 먼저 그래프가 어떤 영향 가정을 나타내는지 문장으로 확인하고 전개합니다.
변경 features를 시작점으로 links의 from에서 to로 도달 가능한 모든 기능을 찾습니다. 직접 기능뿐 아니라 여러 단계를 거친 기능도 포함합니다. 예를 들어 signup이 변경되면 signup, login, profile까지 살펴봅니다. 도달한다는 사실은 후보 선정 근거입니다. 해당 기능의 모든 검사 실행이 충분한지는 기대 결과와 테스트 유형을 읽어 추가 판단합니다.
login→profile과 profile→login처럼 순환이 있어도 탐색이 끝나야 합니다. Set에 이미 방문한 기능을 기록하고 새 기능만 큐에 넣으면 같은 노드를 반복 방문하지 않습니다. 같은 기능을 여러 변경에서 시작하거나 같은 링크가 중복되어도 영향을 받은 기능은 한 번만 취급합니다. 이 처리 원칙은 중복 출력과 무한 반복을 동시에 막아 줍니다.
가입 길이 변경의 기대를 다시 씁니다
미션 change.json은 ASCII 비밀번호 길이를 10~64로 정한 교육용 변경 합의입니다. 이전 프로젝트의 8~64 테스트를 조용히 고치지 않고 새 RG 사례를 추가합니다. 옛 TC 기대는 과거 버전의 설계 증거로 유지합니다. 선택표에는 어떤 계약을 대체하는지 적고 앱 변경이 아직 적용되지 않았다는 상태도 남깁니다. 계획을 실제 실행 통과로 포장하지 않습니다.
길이 9와 10은 새 최소값 바로 아래와 경계이고 64와 65는 최대값과 바로 위입니다. 다른 문자 조건은 유효하도록 고정하여 길이 규칙만 바꿉니다. 길이 12의 정상 가입과 중복 이메일 오류를 함께 포함합니다. 오류 응답에서는 회원 수가 늘지 않았는지, 기존 회원이 유지되는지도 기대 결과에 넣습니다. 길이 거절 문구만으로 데이터 무변경을 대신하지 않습니다.
가입이 허용된 회원이 정상 로그인할 수 있는 연결 검사도 선택합니다. 입력 검증과 저장, 인증에 같은 길이 정책이 적용되는지를 분리해 봅니다. 선택표는 새 경계 사례와 기존 로그인 흐름 ID를 연결합니다. 반복되는 정상 UI 조합을 뒤로 미룰 수는 있지만 신규 길이의 API·저장 효과와 연결 기대를 빠뜨렸다면 선택 근거를 보완해야 합니다.
세션 만료 변경은 권한으로 이어집니다
미션의 두 번째 변경은 주입 시계 now가 expiresAt 이상이면 접근을 거절하는 계약입니다. 현재 시각 99, 만료 시각 100은 허용이고 100과 101은 거절입니다. 정확한 경계를 >로만 비교하면 만료 시각에 접근이 살아 있을 수 있습니다. 실제 시간을 기다리지 않고 동일 단위의 시각을 주입하는 설계를 사용하여 경계를 반복 가능하게 만듭니다.
만료 후 프로필 조회 거절만 확인하면 저장 요청의 권한 처리를 놓칠 수 있습니다. 만료된 세션의 수정 요청에서 저장 무변경을 기대하고 타인 프로필 접근에서는 정보 비노출을 봅니다. 세션 유효 여부와 소유 여부는 서로 다른 조건입니다. 유효한 본인 로그인 통과가 타인 접근 거절을 증명하지 않습니다. 권한 사례는 피해를 근거로 우선순위를 높입니다.
세션 전이 흐름은 정상 로그인→접근→만료→다시 접근으로 구성할 수 있습니다. 만료 안내 후 재로그인 동작과 본인 정보 유지도 후속 확인 후보입니다. 시간이 부족할 때 만료 경계와 거절 저장 검사를 먼저 완료하고 드문 표시 조합은 잔여 위험으로 기록합니다. 후보 전개와 최종 선택을 분리하면 무엇을 알고 보류했는지 설명하기 쉽습니다.
브라우저 선택 계약을 구현합니다
표준 입력은 changes, links, tests 배열을 가진 JSON 객체입니다. changes의 각 행은 feature이고 links는 from과 to입니다. tests의 id는 유일한 문자열이며 feature는 검사 대상 기능입니다. critical과 kind를 읽어 우선순위를 정합니다. 유효한 입력을 제공하므로 명세에 없는 타입 변환이나 자동 보정은 추가하지 않습니다. 빈 changes는 실행 후보 없음으로 출력합니다.
영향을 받은 기능의 검사만 선택합니다. critical이 true이거나 kind가 permission이면 P0, 나머지는 P1입니다. 이 규칙은 수업에서 정한 작은 정책이며 보안 검사가 항상 같은 등급이라는 일반 법칙은 아닙니다. 출력은 P0 먼저, 같은 우선순위에서는 ID 사전식 순서입니다. 숫자 접미 ID가 자연 숫자 순서와 다를 수 있으므로 입력 계약의 정렬 규칙을 지킵니다.
선택된 검사가 없으면 NONE 한 줄을 출력합니다. 후보 기능은 있어도 그 기능에 연결된 테스트가 없다면 NONE일 수 있습니다. 이 상황을 위험 없음이라고 해석하지 않고 추적 누락으로 조사합니다. 실무 선택표에는 요구사항 ID, 인수 기준, 테스트 ID, 우선순위, 사용자 영향, 선택 근거, 실행 상태와 증거 위치를 함께 둡니다.
결과를 리뷰 가능한 표로 남깁니다
선택 근거가 중요한 검사라서라는 문장뿐이면 다른 사람이 우선순위를 검토하기 어렵습니다. 세션 만료 변경→인증 판정→프로필 수정 거절→저장 무변경처럼 영향 경로를 적고 관련 ID를 연결합니다. reviewer는 경로가 실제 코드와 계약에 맞는지 확인합니다. 자동 그래프 탐색은 연결된 후보를 찾을 뿐 그 연결의 타당성을 판정하지 못합니다.
브라우저 테스트에서 예상보다 검사가 적으면 직접 기능만 선택했는지 확인합니다. 실행이 끝나지 않으면 visited Set을 넣은 위치와 큐 추가 조건을 봅니다. 같은 ID가 반복 출력되면 기능 목록을 돌며 검사 배열을 중복 추가했는지 읽습니다. P0와 P1 순서가 뒤집히면 문자열 정렬 전에 등급 순서를 비교했는지 확인합니다. 오류를 영향 모델과 구현으로 나누어 조사합니다.
완료 산출물에는 선택 결과와 변경 하나를 제거했을 때의 차이가 포함됩니다. CH-SESSION이 없더라도 가입에서 로그인과 프로필로 이어지는 영향이 남을 수 있습니다. 그 선택이 너무 넓다면 관계의 근거를 리뷰하여 그래프를 좁힙니다. 단순히 출력 ID를 삭제하면 모델과 결과가 달라집니다. 기능 사이의 연결을 읽는 관점은 더 읽기로 확장합니다.
따라하기
새 최소 경계를 정합니다
새 변경 계약은 ASCII 길이 10~64입니다. 길이 이외 조건은 유효하다고 고정합니다.
for(const n of [9,10,64,65])console.log(n,n>=10&&n<=64?'ALLOW':'REJECT');실행 결과
9 REJECT 10 ALLOW 64 ALLOW 65 REJECT
만료 시각 포함 여부를 확인합니다
now와 expiresAt은 동일한 가상 단위입니다. 정확한 만료 시각은 거절입니다.
const expiresAt=100;for(const now of [99,100,101])console.log(now,now>=expiresAt?'DENY':'ALLOW');실행 결과
99 ALLOW 100 DENY 101 DENY
연결 후보를 펼칩니다
가입 결과를 소비하는 로그인과 프로필까지 영향 후보를 찾습니다.
const links=[['signup','login'],['login','profile']];const seen=new Set(['signup']);const queue=[...seen];for(let i=0;i<queue.length;i++)for(const [a,b] of links)if(a===queue[i]&&!seen.has(b)){seen.add(b);queue.push(b);}console.log([...seen].join(' '));실행 결과
signup login profile
선택 근거를 리뷰합니다
브라우저 선택기를 완성한 뒤 미션 regression/selection.json을 작성합니다. CH-LENGTH에 RG-01~06·TC-17, CH-SESSION에 RG-07~11·TC-18을 연결하고 P0 사용자 영향과 선택 이유를 적습니다. 선택은 실행 계획이며 새 RG 사례의 상태 planned를 유지합니다.
확인 문제
실습
유효 JSON 객체의 changes[{feature}], links[{from,to}], tests[{id,feature,critical,kind}]를 읽습니다. id는 유일합니다. changes 기능에서 from→to 방향으로 도달 가능한 모든 기능의 테스트를 선택합니다. critical=true 또는 kind=permission이면 P0, 나머지는 P1입니다. 우선순위 오름차순, 같은 등급은 ID 사전식 순서로 P0 ID처럼 한 줄씩 출력합니다. 순환·중복 링크를 처리하고 선택 없음은 NONE입니다. 이 작은 정책은 실행 계획만 계산합니다.
모범 답안
const fs=require('node:fs');const d=JSON.parse(fs.readFileSync(0,'utf8'));
const seen=new Set(d.changes.map(c=>c.feature));const queue=[...seen];
for(let i=0;i<queue.length;i++)for(const link of d.links)if(link.from===queue[i]&&!seen.has(link.to)){seen.add(link.to);queue.push(link.to);}
const picked=d.tests.filter(t=>seen.has(t.feature)).map(t=>({id:t.id,p:t.critical===true||t.kind==='permission'?'P0':'P1'}));
picked.sort((a,b)=>a.p<b.p?-1:a.p>b.p?1:a.id<b.id?-1:a.id>b.id?1:0);
if(!picked.length)console.log('NONE');else for(const t of picked)console.log(t.p+' '+t.id);
더 읽기
면접 질문
- 가입 폼의 테스트 조건을 정하는 과정을 설명해 주시면 됩니다.
- 릴리스 시간이 부족할 때 테스트 범위를 정한 경험을 설명해 주시면 됩니다.