본문으로 건너뛰기
입문약 25분

코드 읽기의 기초: 변수·조건·반복·함수

코드를 짜는 것과 읽는 것은 다른 기술이다. QA에게 필요한 건 후자다. 변수·조건문·반복문·함수 네 가지 블록만으로 대부분의 코드를 읽어낼 수 있다.

  • #코드읽기
  • #프로그래밍기초

개념

짜는 것과 읽는 것은 다른 기술이다

"저는 개발자가 아니라서 코드를 못 봐요"라는 말을 QA에게서 자주 듣는다. 하지만 코드를 처음부터 짜는 능력과 이미 짜여진 코드를 읽고 무엇을 하는지 이해하는 능력은 다른 기술이다. QA에게 필요한 건 대부분 후자다 — 그리고 후자는 생각보다 배우기 쉽다.

외국어를 떠올리면 이해가 빠르다. 영어로 유창하게 말하는 것은 몇 년이 걸리지만, 영어로 된 표지판이나 메뉴판을 읽고 뜻을 파악하는 것은 훨씬 빨리 된다. 단어 몇 개를 모르더라도 문맥으로 메꿔가며 "대략 무슨 뜻인지" 알아낼 수 있기 때문이다. 코드도 똑같다. 우리 목표는 코드를 쓰는 것이 아니라 무슨 일을 하는지 알아내는 것이다.

한 가지 더 안심할 점이 있다. 개발자도 자기가 안 쓴 코드는 처음 볼 때 바로 이해하지 못한다. 개발자가 코드를 읽는 모습을 옆에서 보면, 위에서 아래로 정독하는 게 아니라 이름을 훑고, 조건문을 세고, 필요한 부분만 파고든다. 이 레슨에서 배우는 것이 정확히 그 방법이다.

QA가 코드를 읽으면 무엇이 달라지는가

구체적인 상황으로 보자. "쿠폰을 적용했는데 할인이 안 되는 것 같다"는 제보를 받았다.

코드를 못 읽을 때코드를 읽을 때
첫 행동여러 쿠폰으로 계속 눌러보며 재현 시도할인 계산 함수를 열어 조건을 확인
발견"가끔 안 되는 것 같아요""쿠폰율이 0보다 클 때만 적용되는데, 정액 할인 쿠폰은 율이 0이라 안 걸린다"
리포트재현 절차만 나열원인 가설 + 정확한 재현 조건 + 놓친 테스트 케이스
소요 시간1~2시간15분

코드를 읽는다는 건 개발자의 일을 대신하는 게 아니다. 추측을 사실로 바꾸는 것이다. 이게 이 모듈 전체의 목표이고, 스테이지 4(자동화)와 스테이지 6(AI 교차검증)이 전부 이 능력 위에 서 있다.

어디서 코드를 보게 되는가

"코드를 읽으라"고 하면 어디를 열어야 할지부터 막막할 수 있다. QA가 실제로 코드를 마주치는 지점은 정해져 있다.

상황어디서무엇을 보나
개발자가 "고쳤어요"라고 할 때GitHub/GitLab의 PR(Pull Request) 화면바뀐 줄만 색으로 표시된 diff
특정 기능이 왜 저렇게 동작하는지 궁금할 때저장소에서 파일명·함수명 검색해당 함수 전체
화면이 깨지거나 버튼이 안 먹을 때브라우저 개발자 도구 → Console에러 메시지와 스택 트레이스
API 응답이 이상할 때브라우저 개발자 도구 → Network요청·응답 JSON

이 중 PR diff가 가장 자주 쓰인다. 전체 코드를 읽을 필요 없이 바뀐 부분만 보면 되기 때문에 부담이 훨씬 적다. 이건 이 모듈 뒷부분에서 따로 다룬다.

코드를 이루는 네 가지 블록

거의 모든 프로그래밍 언어는 이 네 가지 개념의 조합으로 되어 있다. 언어가 달라도 이 뼈대는 같다.

1. 변수(Variable) — 값을 담아두는 이름표 붙은 상자

let discount = 0.1        // "discount"라는 상자에 0.1을 담아둔다
const MAX_ITEMS = 100     // const는 "한 번 정하면 안 바뀐다"는 뜻

QA가 변수에서 볼 것은 두 가지다.

  • 이름: 좋은 코드에서 변수 이름은 그 자체로 문서다. d 대신 discountRate라고 쓰여 있으면 무슨 값인지 바로 알 수 있다. 반대로 이름과 실제 담긴 값이 어긋나 있으면 그 자체가 버그의 냄새다.
  • 어디서 값이 들어오는가: 사용자 입력인지, DB에서 읽어온 값인지, 코드에 박아둔 상수인지. 사용자 입력이라면 그 지점이 곧 테스트 대상이다.

코드에 숫자가 그대로 박혀 있는 것(if (total > 30000)의 30000 같은)을 매직 넘버라고 부른다. 매직 넘버를 발견하면 반사적으로 물어야 한다 — "이 값이 기획서와 같은가?", "이 값이 바뀌면 어디를 다 고쳐야 하는가?" 같은 숫자가 여러 파일에 흩어져 있다면, 정책이 바뀔 때 일부만 고쳐지는 버그가 거의 확실히 생긴다.

2. 조건문(Conditional) — "만약 ~라면"으로 갈라지는 분기

if (discount > 0) {
  // 할인이 있을 때만 실행되는 코드
} else if (isFirstPurchase) {
  // 할인은 없지만 첫 구매일 때
} else {
  // 그 외 모든 경우
}

조건문은 QA가 코드에서 가장 주목해야 할 부분이다. 조건문 하나하나가 곧 테스트해야 할 갈래이기 때문이다. 자주 보게 될 비교 표현은 이 정도다.

표현뜻주의할 점
===, ==같다===는 타입까지 같아야 함. ==는 "1" == 1이 참이 되는 함정이 있다
!==, !=다르다
>, <크다, 작다경계값을 포함하지 않는다
>=, <=크거나 같다경계값을 포함한다
&&그리고둘 다 참이어야 함
||또는하나만 참이어도 됨
!아니다참/거짓을 뒤집음

>와 >=의 차이가 사소해 보이지만, 실무 버그의 상당수가 정확히 여기서 나온다. "3만원 이상 무료배송"이라고 기획서에 써 있는데 코드가 if (total > 30000)이면 딱 3만원인 주문은 무료배송이 안 된다. 이게 모듈 3에서 배운 경계값 분석이 코드 레벨에서 나타나는 모습이다.

조건이 여러 개 엮여 있으면 갈래가 곱셈으로 늘어난다.

if (isMember && cartTotal >= 30000) {
  // 회원이면서 3만원 이상
}

이 한 줄에서 확인해야 할 조합은 네 가지다 — 회원×3만원이상, 회원×3만원미만, 비회원×3만원이상, 비회원×3만원미만. &&이므로 첫 번째 조합만 참이고 나머지 셋은 전부 거짓이 되어야 한다. 조건이 세 개면 여덟 가지가 된다. 이렇게 조합이 폭발할 때 쓰는 것이 모듈 3의 결정 테이블과 페어와이즈 기법이다.

3. 반복문(Loop) — 같은 동작을 여러 번 되풀이

for (let item of cartItems) {
  // 장바구니의 각 상품에 대해 반복 실행
}

반복문을 볼 때 QA가 던질 질문은 네 가지다.

  • 0번 도는 경우는? 장바구니가 비어 있으면 이 반복문은 한 번도 실행되지 않는다. 그때 total은 무엇이 되는가? — 실무에서 가장 자주 터지는 케이스다.
  • 1번만 도는 경우는? 상품이 하나뿐일 때 의도대로 동작하는가? "목록"을 가정하고 만든 화면이 1개짜리에서 어색해지는 경우가 많다.
  • 여러 번 도는 경우는? 정상 경로.
  • 아주 많이 도는 경우는? 상품이 1,000개면 느려지지 않는가? 화면이 멈추지는 않는가?

이 "0개 / 1개 / 여러 개 / 아주 많이"는 모듈 3의 동등분할을 반복문에 적용한 것이다. 반복문을 발견했다면 이 네 가지는 거의 항상 테스트 케이스가 된다.

4. 함수(Function) — 이름 붙은 작업 단위

function applyDiscount(price, rate) {
  return price - (price * rate)
}

함수는 입력을 받아 출력을 내는 상자다. 위 함수는 price와 rate를 받아서(입력) 할인된 금액을 돌려준다(출력, return).

QA에게 함수가 중요한 이유는, 함수의 입력과 출력이 곧 테스트 케이스의 입력과 기대 결과이기 때문이다. applyDiscount(10000, 0.1)을 넣으면 9000이 나와야 한다 — 이건 그대로 하나의 테스트 케이스다. 스테이지 3 뒷부분에서 배울 단위 테스트가 정확히 이 일을 자동으로 한다.

함수를 볼 때 하나 더 확인할 것은 부수 효과(side effect) 다. 값만 돌려주는 함수도 있지만, DB에 저장하거나 이메일을 보내거나 화면을 바꾸는 함수도 있다.

function completeOrder(order) {
  saveToDatabase(order)      // DB에 저장 — 부수 효과
  sendConfirmationEmail(order)  // 이메일 발송 — 부수 효과
  return order.id
}

부수 효과가 있는 함수는 중간에 실패하면 어떻게 되는지가 항상 질문거리다. DB 저장은 됐는데 이메일 발송이 실패하면? 주문은 완료된 건가, 아닌가? 사용자에게는 뭐라고 보이는가? — 코드에 답이 없다면 그건 결함일 가능성이 높다.

다섯 번째 블록 — 객체와 배열

네 블록에 하나만 더하면 실무 코드의 거의 전부가 읽힌다. 데이터를 담는 모양인 객체와 배열이다. QA에게 특히 중요한 이유는, API 응답(JSON)이 정확히 이 모양이기 때문이다.

// 객체 — 이름표가 붙은 값들의 묶음
const user = {
  id: 101,
  name: "김테스트",
  isVip: true
}
 
// 배열 — 순서가 있는 값들의 목록
const cartItems = [
  { name: "키보드", price: 30000 },
  { name: "마우스", price: 15000 }
]

읽는 법은 점(.)과 대괄호([])를 따라가면 된다.

표현읽는 법
user.nameuser 안의 name → "김테스트"
cartItems[0]목록의 첫 번째(0부터 센다) → 키보드 객체
cartItems[0].price첫 번째 상품의 가격 → 30000
cartItems.length목록의 길이 → 2

배열이 0부터 시작한다는 점이 중요하다. 상품이 2개면 유효한 번호는 [0]과 [1]이고, [2]는 없는 값이다. "마지막 항목"을 다루는 코드에서 경계 실수가 자주 나는 이유가 여기 있다.

브라우저 개발자 도구 Network 탭에서 API 응답을 열면 정확히 이 모양이 보인다. 응답 JSON을 읽을 수 있으면 화면에 안 보이는 데이터까지 확인할 수 있다 — 화면에는 "배송 준비중"이라고 나오는데 응답에는 status: "PAID"로 와 있다면, 화면 변환 로직에 문제가 있다는 뜻이다.

읽는 순서 — 위에서 아래로 읽지 마라

초보자가 가장 많이 하는 실수는 코드를 소설처럼 첫 줄부터 끝까지 읽는 것이다. 그러면 금방 길을 잃는다. 실무에서 쓰는 순서는 이렇다.

  1. 함수 이름부터 본다. calculateTotal이라면 "합계를 계산하는구나" — 이것만으로 이미 절반은 파악한 것이다.
  2. 입력(파라미터)과 출력(return)을 본다. 무엇을 받아 무엇을 내놓는가.
  3. 조건문만 훑는다. 갈래가 몇 개인지, 각 갈래의 조건이 무엇인지. 여기서 테스트 케이스 목록이 거의 완성된다.
  4. 그다음에야 세부 계산을 본다. 필요할 때만.

세부 문법에서 막히면 3번까지만 해도 충분한 경우가 많다.

조합해서 읽어보기

function calculateTotal(cartItems, couponRate) {
  let total = 0
  for (let item of cartItems) {
    total = total + item.price
  }
  if (couponRate > 0) {
    total = applyDiscount(total, couponRate)
  }
  return total
}

위 순서대로 읽어보자.

  1. 이름: calculateTotal — 합계를 구한다.
  2. 입출력: 장바구니 목록(cartItems)과 쿠폰율(couponRate)을 받아, 최종 금액 하나를 돌려준다.
  3. 조건: if (couponRate > 0) 하나뿐이다. 갈래는 두 개 — 쿠폰이 있을 때와 없을 때.
  4. 세부: 반복문으로 상품 가격을 다 더한 뒤, 쿠폰이 있으면 할인을 적용한다.

문법을 몰라도 네 블록이 뭘 하는지만 알면 이 정도는 읽어낼 수 있다.

코드에서 테스트 케이스를 뽑아내기

여기가 QA가 코드를 읽는 진짜 이유다. 방금 읽은 코드에서 바로 테스트 케이스가 나온다.

근거가 된 코드뽑아낸 테스트 케이스
for 반복문장바구니가 빈 경우 / 1개인 경우 / 여러 개인 경우
if (couponRate > 0)쿠폰율이 0.1인 경우(참) / 0인 경우(거짓)
couponRate > 0의 경계쿠폰율이 정확히 0인 경우 — 할인이 적용되지 않는 게 맞는가?
total = total + item.priceprice가 없거나 음수인 상품이 섞이면?
return total반환값이 소수점이 되면 화면에 어떻게 표시되는가?

마지막 두 줄이 중요하다. 이건 코드에 쓰여 있지 않은 것을 묻는 질문이다. 코드는 "price가 항상 있다"고 가정하고 있는데, 그 가정이 깨지면 어떻게 되는지는 코드가 말해주지 않는다. 코드가 다루지 않는 경우를 찾아내는 것 — 이게 개발자가 짠 코드에 QA가 더하는 가치다.

사례 연구 — 제보에서 리포트까지

앞의 조각들을 하나로 이어보자. CS팀에서 이런 제보가 왔다.

"장바구니에 상품을 담고 쿠폰을 적용했는데 총액이 그대로예요."

1단계 — 어떤 함수를 볼지 정한다. 총액 계산이므로 calculateTotal 같은 이름을 저장소에서 검색한다.

2단계 — 읽는 순서대로 훑는다. 조건문이 if (couponRate > 0) 하나다. 즉 쿠폰율이 0이면 할인이 아예 적용되지 않는다.

3단계 — 가설을 세운다. "쿠폰율이 0으로 들어오는 쿠폰이 있는 것 아닐까?" 정액 할인 쿠폰(3,000원 할인)이라면 비율이 아니라 금액이므로 couponRate가 0일 수 있다.

4단계 — 확인한다. 개발자 도구 Network 탭에서 쿠폰 적용 API 응답을 연다.

{ "couponId": 77, "type": "FIXED", "amount": 3000, "rate": 0 }

가설이 맞았다. type이 FIXED인 쿠폰은 rate가 0이고 amount에 값이 들어 있는데, 총액 계산 코드는 rate만 보고 있다.

5단계 — 리포트를 쓴다. 모듈 6에서 배운 형식으로.

제목: 정액(FIXED) 쿠폰 적용 시 할인이 계산되지 않음

재현: 정액 쿠폰(예: 3,000원 할인, couponId 77)을 장바구니에 적용

결과: 총액이 변하지 않음 / 기대: 3,000원 차감

원인 추정: calculateTotal이 couponRate > 0만 검사하는데, FIXED 타입 쿠폰은 rate: 0, amount: 3000으로 내려옴(쿠폰 적용 API 응답 확인). 정률 쿠폰만 고려된 것으로 보임.

함께 확인 필요: 정액+정률 쿠폰 중복 적용, 정액 쿠폰이 총액보다 큰 경우

마지막 줄이 QA가 더한 가치다. 코드를 읽었기 때문에 이 버그를 고칠 때 같이 터질 수 있는 다른 경우까지 짚어줄 수 있었다.

주석과 TODO를 읽는 법

코드에는 사람이 남긴 메모가 섞여 있다. //나 /* */로 시작하는 부분이다.

// TODO: 정액 쿠폰 처리 추가 필요
// FIXME: 소수점 반올림이 가끔 틀림
// HACK: 임시로 하드코딩. 나중에 설정값으로 뺄 것

TODO, FIXME, HACK 같은 표시는 개발자가 스스로 남긴 미완성 지도다. QA에게는 보물이다 — 저장소에서 이 단어들을 검색하는 것만으로도 "아직 덜 된 곳" 목록이 나온다.

다만 주석을 그대로 믿으면 안 된다. 코드는 고쳐졌는데 주석은 예전 그대로 남아 있는 경우가 아주 흔하다. 주석과 코드가 다르게 말하고 있다면, 진실은 코드 쪽이고 그 불일치 자체가 보고할 만한 발견이다.

에러 메시지와 스택 트레이스 읽기

QA가 가장 자주 마주치는 코드는 사실 에러 메시지다. 브라우저 개발자 도구 Console에 이런 게 뜬다.

TypeError: Cannot read properties of undefined (reading 'price')
    at calculateTotal (cart.js:42:31)
    at handleCheckout (checkout.js:15:20)

겁먹을 필요 없다. 세 부분으로 나눠 읽는다.

부분내용읽는 법
에러 종류TypeError타입 문제 — 있어야 할 값이 없거나 모양이 다름
설명Cannot read properties of undefined (reading 'price')없는 것(undefined)에서 price를 꺼내려 했다
위치at calculateTotal (cart.js:42:31)cart.js 파일 42번째 줄

이 세 줄만 읽어도 리포트의 질이 완전히 달라진다. "결제 버튼 누르면 화면이 멈춰요" 대신 "cart.js 42번째 줄에서 price가 없는 상품을 만나 TypeError가 납니다. 재현: 품절 상품을 장바구니에 담고 결제 시도"라고 쓸 수 있다.

읽는 순서도 요령이 있다. 스택 트레이스는 위에서 아래로 "가장 최근 → 그 이전" 순서다. 맨 윗줄이 실제로 터진 지점이고, 아랫줄들은 거기까지 어떻게 왔는지의 경로다. 우리 회사 파일명이 나오는 첫 줄을 찾는 것이 보통 가장 빠르다 — 라이브러리 내부 줄은 대개 원인이 아니다.

모르는 문법을 만났을 때

모든 문법을 외울 필요는 없다. 처음 보는 표현을 만나면 "이게 변수인가, 조건인가, 반복인가, 함수인가"부터 판단하고, 필요하면 그 문법만 검색한다. 자주 만나는데 처음엔 낯선 것들을 미리 정리해두면 겁먹을 일이 줄어든다.

표현겁먹지 말 것 — 사실은 이런 뜻
items.map(x => x.price)반복문이다. "각 항목에서 price만 뽑아 새 목록을 만든다"
items.filter(x => x.price > 0)반복문 + 조건문이다. "조건에 맞는 것만 골라낸다"
items.find(x => x.id === 3)"조건에 맞는 첫 번째 하나를 찾는다". 없으면 undefined
a ? b : c조건문의 짧은 형태. "a가 참이면 b, 아니면 c"
x?.y"x가 없으면 그냥 넘어간다"는 안전장치
x ?? 0"x가 없으면 0을 대신 쓴다"는 기본값
async / await"시간이 걸리는 작업(서버 호출 등)을 기다린다"
try { } catch (e) { }"실패할 수 있는 작업 — 실패하면 catch로 간다"

마지막 두 개는 특히 주목할 만하다.

  • async/await가 보이면 시간이 걸리는 작업 = 실패할 수 있는 작업이라는 뜻이다. 네트워크가 끊기면? 응답이 10초 걸리면? 두 번 연속 누르면?
  • try/catch가 보이면 개발자가 여기서 실패를 예상했다는 뜻이다. catch 안을 읽어보면 실패했을 때 사용자에게 무엇이 보이는지 알 수 있다. 반대로 catch가 비어 있으면(catch (e) {}) 실패가 조용히 무시된다는 뜻 — 이건 거의 항상 보고할 가치가 있다.

언어가 달라도 같은 것

회사에서 쓰는 언어가 JavaScript가 아니어도 걱정할 필요 없다. 네 블록은 어느 언어에서나 거의 같은 모양이다.

JavaScriptPythonJava
변수let x = 1x = 1int x = 1;
조건if (x > 0) { }if x > 0:if (x > 0) { }
반복for (let i of list)for i in list:for (int i : list)
함수function f(a) { }def f(a):void f(int a) { }
주석// 메모# 메모// 메모

괄호와 세미콜론이 있고 없고의 차이일 뿐, 읽는 방법은 완전히 같다. Python은 중괄호 대신 들여쓰기로 범위를 나타낸다는 것만 알아두면 된다 — 같은 깊이로 들여쓴 줄들이 한 덩어리다.

실무에서 왜 필요한가

결함 리포트의 정확도가 달라진다. 모듈 6 레슨 2에서 "개발자가 되묻지 않는 결함 리포트"를 배웠다. 코드를 읽을 수 있으면 "이 조건에서 이런 결과가 나온다"를 추측이 아니라 사실로 쓸 수 있다. "3만원일 때 무료배송이 안 됩니다"보다 "total > 30000 조건이라 정확히 3만원은 제외됩니다"가 훨씬 빠르게 고쳐진다. 앞의 사례 연구가 그 차이를 통째로 보여준다.

재검증(retest)의 신뢰도가 올라간다. 개발자가 "고쳤어요"라고 할 때 실제 변경분(diff)을 열어 무엇이 바뀌었는지 읽을 수 있으면, 고친 부분만 확인하는 게 아니라 그 변경이 건드릴 수 있는 다른 곳까지 짚어낼 수 있다. 이건 모듈 9 뒷부분에서 PR diff 읽기로 이어진다.

테스트 설계의 빈틈이 보인다. 명세만 보고 만든 테스트 케이스는 명세에 없는 경우를 놓친다. 코드를 보면 "명세에는 없는데 코드에는 있는 분기"나 반대로 "코드가 아예 처리하지 않는 입력"이 드러난다. 이게 모듈 3의 명세 기반 기법과 구조 기반 기법을 함께 써야 하는 이유다.

개발자와의 대화가 짧아진다. "이거 왜 이래요?"에서 "여기 조건이 >인데 기획서는 '이상'이라 >=여야 하는 것 아닌가요?"로 바뀌면, 오가는 메시지 수가 확 줄어든다. 개발자 입장에서도 재현 시간을 아낄 수 있어 협업 관계가 좋아진다 — 모듈 6의 "비난 없는 결함 커뮤니케이션"과도 닿아 있다.

채용시장에서의 위치

QA 채용 공고에서 "개발 지식", "코드 이해도", "기본적인 프로그래밍 소양" 같은 표현은 대부분 코드를 짤 줄 아느냐가 아니라 읽을 줄 아느냐를 묻는 것이다. 신입 QA 면접에서 간단한 함수를 보여주고 "이 코드가 뭘 하는 것 같으세요?" 또는 "이 코드를 테스트한다면 어떤 케이스를 만드시겠어요?"를 묻는 경우가 흔하다. 후자가 특히 중요하다 — 코드를 읽는 것에서 끝나지 않고 테스트 케이스로 옮길 수 있는지를 보기 때문이다.

면접에서 잘 먹히는 답변 구조는 이 레슨에서 배운 순서 그대로다. "먼저 함수 이름과 입출력을 보고, 조건문이 몇 개인지 세서 갈래를 잡고, 각 갈래마다 경계값을 확인하고, 마지막으로 코드가 다루지 않는 입력(빈 값·음수·아주 큰 값)을 질문으로 만듭니다." — 이렇게 절차로 답하면 지식이 아니라 방법을 가진 사람으로 보인다.

자동화 QA·SDET 직군으로 가려면 이 능력은 선택이 아니라 전제 조건이다. 자동화 코드를 짜는 것도, 남이 짠 자동화 코드를 유지보수하는 것도 결국 읽기에서 시작한다.

실습 과제

과제 1 — 코드 설명해보기 (10분)

다음 코드를 앞서 배운 순서(이름 → 입출력 → 조건 → 세부)대로 읽고, 한국어 한 문장으로 요약해본다.

function isEligibleForFreeShipping(cartTotal, memberLevel) {
  if (memberLevel === "VIP") {
    return true
  }
  if (cartTotal >= 30000) {
    return true
  }
  return false
}
해설 보기

"VIP 회원이거나, 장바구니 총액이 3만원 이상이면 무료배송 대상이다."

조건문이 두 개지만 둘 다 return true로 끝나므로 사실상 OR 관계다. 둘 중 하나만 만족해도 참이 된다.

과제 2 — 결과 예측하기 (5분)

위 함수에 아래 값을 넣으면 결과가 무엇일지 코드를 따라가며 예측한다. 예측한 뒤 왜 그렇게 되는지 근거가 된 줄을 짚어본다.

cartTotalmemberLevel예측 결과
25000"일반"?
30000"일반"?
29999"VIP"?
해설 보기
cartTotalmemberLevel결과근거
25000"일반"falseVIP 아님, 3만원 미만 → 마지막 return false
30000"일반"true>=이므로 정확히 3만원도 포함
29999"VIP"true첫 조건에서 이미 참 — 금액은 아예 보지도 않음

세 번째가 중요하다. VIP면 금액 조건은 검사조차 되지 않는다. return을 만나면 함수가 그 자리에서 끝나기 때문이다.

과제 3 — 테스트 케이스 뽑아내기 (10분)

같은 함수에서 테스트 케이스를 최소 6개 뽑아본다. 힌트: 조건문이 두 개이고, 그중 하나에 >= 경계가 있다. 그리고 코드가 다루지 않는 입력도 생각해본다.

해설 보기
  • 경계값: 29,999 / 30,000 / 30,001 (일반 회원 기준)
  • 회원 등급: VIP / 일반 / 그 외 등급(예: "GOLD")이 들어오면?
  • 조합: VIP + 3만원 미만 (VIP 조건이 우선하는지 확인)
  • 코드가 다루지 않는 것: cartTotal이 음수·0, memberLevel이 빈 문자열· null, 대소문자 다름("vip") — ===는 대소문자를 구분하므로 "vip"는 VIP로 인정되지 않는다. 이게 의도인지 물어봐야 한다.

마지막 항목처럼 "이게 의도인가?"를 묻는 질문이 나왔다면 이 과제의 목적을 달성한 것이다.

과제 4 — 에러 메시지 해석하기 (5분)

아래 에러를 보고 ①어떤 종류의 문제인지 ②어느 파일 몇 번째 줄인지 ③리포트에 뭐라고 쓸지 정리해본다.

TypeError: Cannot read properties of null (reading 'name')
    at renderUserBadge (profile.js:88:14)
    at ProfilePage (page.js:23:8)
해설 보기

① 있어야 할 값이 null인데 거기서 name을 꺼내려다 실패했다. ② profile.js 88번째 줄. (page.js는 거기까지 온 경로일 뿐) ③ "프로필 화면에서 사용자 정보가 null일 때 profile.js:88의 renderUserBadge가 TypeError로 실패합니다. 탈퇴한 사용자나 로그인 만료 상태에서 재현되는지 확인이 필요합니다."

"사용자 정보가 없을 수 있는 상황"이 무엇인지 떠올리는 것까지가 QA의 몫이다.

과제 5 — 우리 회사 코드 열어보기 (선택, 15분)

실제 업무 저장소에서 이름이 이해되는 함수를 하나만 골라 같은 순서로 읽어본다. 전부 이해하려 하지 말고 조건문 갯수만 세어보는 것을 목표로 한다. 그 숫자가 곧 그 함수를 테스트할 때 최소한 확인해야 할 갈래의 수다.

여유가 있다면 저장소 전체에서 TODO와 FIXME를 검색해본다. 지금 팀이 알면서도 미뤄둔 것들의 목록이 나온다.

자가 체크리스트

  • 변수·조건문·반복문·함수 네 가지 블록의 역할을 설명할 수 있다
  • 객체와 배열을 읽고 user.name, items[0].price 같은 표현을 따라갈 수 있다
  • 코드를 첫 줄부터 읽지 않고, 이름 → 입출력 → 조건 → 세부 순서로 읽을 수 있다
  • >와 >=의 차이가 왜 버그로 이어지는지 예를 들어 설명할 수 있다
  • 반복문을 보면 0개·1개·여러 개·아주 많이를 테스트 케이스로 떠올릴 수 있다
  • 조건문에서 테스트해야 할 갈래를 세어낼 수 있다 (&&면 조합이 늘어난다는 것 포함)
  • 코드가 다루지 않는 입력을 질문으로 만들어낼 수 있다
  • 에러 메시지에서 종류·설명·위치(파일:줄)를 분리해 읽을 수 있다
  • TODO/FIXME 주석이 왜 QA에게 유용한지, 그리고 왜 주석을 그대로 믿으면 안 되는지 설명할 수 있다
  • 모르는 문법을 만났을 때 어떻게 접근해야 할지 설명할 수 있다

흔한 실수

  • "개발자가 아니니까 코드는 못 본다"고 지레 포기한다. 짜는 것과 읽는 것은 다른 기술이며, 읽는 건 생각보다 접근하기 쉽다. 개발자도 남의 코드는 처음에 못 읽는다.
  • 모든 문법을 완벽히 외우려 한다. 필요한 건 대략적인 흐름 파악 능력이지 문법 암기가 아니다. 막히는 문법은 그때그때 검색하면 된다.
  • 코드를 첫 줄부터 끝까지 순서대로 읽는다. 금방 길을 잃는다. 함수 이름과 조건문만 훑는 것으로 시작한다.
  • 코드를 읽고 "이해했다"에서 멈춘다. QA의 목표는 이해가 아니라 테스트 케이스를 뽑아내는 것이다. 읽은 뒤에는 반드시 "그래서 무엇을 확인해야 하는가"로 이어져야 한다.
  • 코드에 쓰여 있는 것만 본다. 코드가 처리하지 않는 경우가 더 중요한 버그일 때가 많다. 빈 값, 음수, 아주 큰 값, 예상 못 한 타입.
  • 주석을 사실로 믿는다. 코드는 바뀌었는데 주석만 남아 있는 경우가 흔하다. 둘이 다르면 진실은 코드 쪽이고, 그 불일치 자체가 보고 대상이다.
  • 에러 메시지를 "빨간 글씨"로만 보고 넘긴다. 파일명과 줄 번호만 옮겨 적어도 리포트의 가치가 크게 올라간다.
  • 코드를 읽었다는 이유로 원인을 단정한다. 코드 읽기는 가설을 만드는 도구지 결론이 아니다. 리포트에는 "원인 추정"이라고 쓰고 판단은 개발자에게 남긴다 — 틀린 단정은 신뢰를 깎는다.
  • 코드를 보지 않고 항상 개발자에게 구두로만 물어본다. 간단한 질문은 코드를 직접 열어 확인하는 게 더 빠를 때가 많고, 개발자의 시간도 아낀다.

참고 자료

낯선 말 풀이

이 레슨에 나온 약어입니다. 본문에서도 처음 나올 때 점선으로 표시해 두었습니다 (제목과 링크 글자는 그대로 둡니다).

QA — 품질보증
만든 것이 쓸 만한지 확인하고, 애초에 덜 망가지게 만드는 방법을 정하는 일. "테스트"는 그 일의 일부다.
AI — 인공지능
사람이 규칙을 일일이 적지 않아도 데이터에서 규칙을 찾아 판단하는 소프트웨어.
PR — 풀 리퀘스트
"내가 고친 코드를 본체에 합쳐 달라"고 올리는 요청. 합치기 전에 사람들이 보고 의견을 단다.
API — 응용 프로그램 인터페이스
프로그램끼리 주고받는 창구. 사람이 화면 버튼을 누르는 대신, 프로그램이 정해진 주소로 요청을 보내 답을 받는다.
JSON — 제이슨
프로그램끼리 값을 주고받을 때 쓰는 글자 형식. 괄호와 따옴표로 이름과 값을 짝지어 적는다.
DB — 데이터베이스
데이터를 넣어 두고 꺼내 쓰는 저장소. 보통 표 여러 개가 서로 연결된 형태다.
CS — 고객 지원 · 칩 선택 신호
이 커리큘럼에서 두 가지로 쓰인다. ① 고객 문의와 불만을 직접 받는 조직 ② 여러 부품이 한 선을 나눠 쓸 때 "지금은 네 차례" 라고 하나를 고르는 신호. 앞뒤에 문의·고객이 있으면 ①, 통신·배선 이야기면 ②다.
SDET — 테스트 개발 엔지니어
시험을 코드로 만드는 사람. 개발과 QA 양쪽 일을 한다.
JS — 자바스크립트
웹 화면을 움직이게 만드는 프로그래밍 언어. 버튼을 눌렀을 때 일어나는 일을 적는다.

용어집 전체 보기