Chapter 15

타임라인 격리하기

  • 15.1 버그가 있다 — 타임라인 이야기
  • 15.2 타임라인 다이어그램
  • 15.3 타임라인 다이어그램 그리기
  • 15.4 전역변수를 지역변수로
  • 15.5 전역변수를 인자로

여러 타임라인이 자원을 공유하면 버그가 생긴다 — 이게 경쟁 조건의 본질이야.

파트 2의 후반부가 시작돼. 1014장이 "일급 함수로 추상화하기"였다면, 1517장은 타임라인과 동시성을 다뤄. MegaMart에 버그가 생겼거든 — "장바구니에 담기" 버튼을 빠르게 두 번 클릭하면, 합계 금액이 잘못 계산되는 경우가 있어.

var total = 0;

function add_item_to_cart(item) {
  cart = add_item(cart, item);  // 1. 전역변수 읽기/쓰기
  total = calc_total(cart);      // 2. 전역변수 읽기/쓰기
  update_total_dom(total);       // 3. DOM 업데이트
}

한 번 클릭하면 문제없어. 하지만 두 번 빠르게 클릭하면? 첫 번째 클릭의 2번 단계가 끝나기 전에 두 번째 클릭의 1번 단계가 시작될 수 있어. 전역변수 carttotal을 두 실행이 공유하고 있으니, 경쟁 조건(race condition)이 발생하는 거야.

저자가 이 문제를 분석하기 위해 타임라인 다이어그램을 소개해. 세로 선 하나가 하나의 타임라인(실행 흐름)이고, 각 액션은 타임라인 위에 점으로 표시해(계산과 데이터는 표시 안 해 — 타이밍에 의존하지 않으니까). 두 타임라인이 동시에 실행될 수 있으면 나란히 그리고, 점선으로 연결하면 순서 보장을 나타내.

타임라인 다이어그램을 그리는 단계는 이래. 먼저 코드에서 모든 액션(전역변수 읽기/쓰기, DOM 변경, API 호출 등)을 식별하고, 각 액션을 순서대로 타임라인에 배치하고, 동시에 실행 가능한 타임라인을 나란히 그려. 핵심 통찰은 가능한 실행 순서의 수를 세는 거야. 타임라인이 2개이고 각각 액션이 3개면, 가능한 순서 조합이 꽤 많아. 그 중 하나라도 잘못된 결과를 만들면 버그거든.

첫 번째 해결법 — 전역변수를 지역변수로 바꾸기. total을 지역변수로 만들면, 두 타임라인이 total을 공유하지 않게 돼. 공유하는 자원이 줄어들면 가능한 실행 순서 중 문제가 되는 경우도 줄어들지. 원칙은 타임라인 사이에 공유하는 자원이 적을수록 안전하다는 거야.

두 번째 해결법 — 전역변수를 함수의 인자로 바꾸기. 전역변수 읽기/쓰기가 인자 받기/리턴하기로 바뀌면, 인자와 리턴값은 타임라인 사이에 공유되지 않아 — 각 호출이 자기만의 복사본을 가지니까. 물론 어딘가에서는 여전히 전역 cart를 업데이트해야 하지만, 그 지점을 최소화하는 게 핵심이야. 가능한 많은 코드를 계산으로 바꾸고, 액션(전역변수 읽기/쓰기)은 가장 바깥에 몰아놓는 거지. 이건 결국 파트 1에서 배운 원칙의 연장이야 — 액션에서 계산을 빼내라. 동시성 맥락에서 보면 그 원칙이 왜 중요한지가 더 명확해져.


정리

15장 읽고 기억할 거 세 가지:

  1. 타임라인 = 실행 흐름. 여러 타임라인이 자원을 공유하면 버그가 생긴다. 이게 경쟁 조건의 본질
  2. 타임라인 다이어그램으로 가능한 실행 순서를 시각화하라. 액션만 표시하고, 가능한 순서 조합 중 잘못된 결과가 있는지 확인
  3. 전역변수를 지역변수/인자로 바꿔서 공유 자원을 줄여라. 공유가 없으면 동시성 문제도 없다