본문 바로가기
C.W.K.
Stream
Lesson 02 of 04 · published

시계 셋, 그리고 기본값이 없는 이유

~12 min · capture, timing, async, readiness

Level 0불 꺼진 작업장
0 XP0/35 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

쉬워 보이는 속임수 같은 작업

돌고 있는 앱의 스크린샷을 찍어. 연산 하나처럼 들리는데, 실제로는 보편적인 정답이 없는 타이밍 문제야. 어려운 질문이 딱 하나거든. 이 화면은 언제 다 된 거지? 그리고 앱마다 그 답이 서로 양립 안 하는 방식으로 달라.

시계 1 — 실시간

정해진 초만큼 기다렸다가 찍어. 조잡한데, 특정 부류엔 맞아. 계속 폴링하는 앱들. 일을 영원히 예약해 넣는 화면은 잠잠해지는 순간이 없어서 감지할 게 없어. 더 영리한 방법은 뭐든 무한정 기다려. 이런 앱한테는 뭉툭한 실시간 타임아웃만이 끝나는 유일한 방법이야.

시계 2 — 가상 시간

실제 초로 기다리는 대신, 브라우저가 자기 시계를 최대한 빨리 돌려서 가상 시간 예산을 다 쓸 때까지 두는 거야. 한 번 로드하는 앱한테 맞아. 특히 시작할 때 상태를 비동기로 복원하는 것들. 실제로 걸리는 시간은 들쭉날쭉한데 예약된 일의 양은 유한한 경우지. 고정된 실시간 대기는 이런 걸 복원 중에 끊어버리거나, 최악의 경우를 감당하려고 매 실행마다 시간을 낭비해.

이걸 폴링하는 앱한테 겨누면 영원히 안 끝나. 예약된 일이 항상 더 있어서 가상 시계가 절대 안 마르거든.

시계 3 — 화면한테 물어보기

둘 다 이기는 앱도 있어. 물결처럼 차오르는 화면은 어떤 의미에선 준비가 됐어. 예약된 일도 없고 실제 시간도 흘렀지. 근데 눈으로 보면 미완성이야. 반쯤 채워진 줄이나 뼈대만 보여주고 있어. 눈먼 시계 둘 다 이걸 못 봐. 둘 다 시간을 재고 있는데 진짜 질문은 상태에 관한 거니까.

답은 화면 안에서 준비 조건을 물어보고, 참이라고 답할 때 찍는 거야. 그럼 앱마다 자기만의 '준비됐다' 정의가 필요해지고. 일이 더 많아지는데, 던져진 질문에 실제로 답하는 건 그것뿐이야.

보편적인 준비 신호가 없는 자원은, 준비의 정의를 부르는 쪽이 갖고 있어야 해. 어디서나 통하는 타임아웃 하나를 찾고 싶어지는데, 그런 건 없고 그 탐색 값이 앱별 지식을 받아들이는 것보다 비싸. 대상마다 어느 시계가 필요한지 적어두고, 항목이 없는 새 대상은 기본값이 아니라 질문으로 다뤄.

Code

전략 셋, 그리고 그중 하나를 고르는 성질·python
class Clock(Enum):
    REAL = "real"        # polls forever -> only a blunt timeout terminates
    VIRTUAL = "virtual"  # loads once -> let its own clock run out
    HYDRATED = "ready"   # loads in waves -> ask the page directly


def capture(url: str, out: Path, clock: Clock, ready_js: str | None = None):
    if clock is Clock.REAL:
        args = ["--timeout=8000"]

    elif clock is Clock.VIRTUAL:
        # never point this at a polling app: the budget never drains
        args = ["--virtual-time-budget=20000"]

    elif clock is Clock.HYDRATED:
        # neither blind clock can see a half-populated list
        assert ready_js, "hydration capture needs a readiness predicate"
        wait_until(url, predicate=ready_js, timeout=30)
        args = ["--timeout=500"]

    shoot(url, out, args)


# The clock is a property of the TARGET, not a setting to standardize.
# A new target with no entry here is a question, not a default.

External links

Exercise

네 코드베이스에서 어떤 상태를 읽기 전에 고정 시간 sleep 을 거는 자리를 찾아봐. 그 sleep 이 사실 무슨 조건을 근사하고 있는지 알아내고, 그 조건에 대한 폴링 + 예비용 타임아웃으로 바꿔. 그다음 실제로 폴링이 걸리는 시간을 sleep 이랑 비교해봐. 보통 훨씬 짧고, 가끔 훨씬 길어. 그 가끔이 sleep 이 조용히 틀리고 있던 경우고.
Hint
고정 sleep 은 추측 둘을 한꺼번에 부호화해. 그때쯤이면 조건이 참일 거라는 것, 그리고 그만큼 기다리는 게 괜찮다는 것. 둘 다 보통 반대 방향으로 틀려. 부하가 걸리면 너무 짧고, 나머지 시간엔 낭비스럽게 길고. 조건이랑 예비 장치를 분리하면 둘 다 고쳐져.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.