C.W.K.
Stream
Lesson 02 of 05 · published

seek 은 상태 기계야, boolean 이 아니라

~14 min · observed-state, state-machine, seek, tolerance

Level 0릴 입문자
0 XP0/39 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"명령 큐잉은 pending 이지 성공이 아냐; 엔진이 보고한 시간이 applied, clamped, failed 를 결정해."

참도 거짓도 되기를 거부하는 seek

순진한 플레이어한테 "seek 됐어?" 물으면 true 라고 답해 — 명령 보냈잖아, 안 그래? 근데 진짜 seek 은 boolean 이 담을 수 있는 것보다 많은 결말을 가져. 네가 요청한 데 정확히 떨어질 수도. duration 을 넘겨 요청해서 끝에 떨어질 수도. 파일이 멈춰서 영영 확인 안 될 수도. 완전히 실패할 수도. 그걸 전부 true/false 로 짜넣는 게, 플레이어가 엉뚱한 초에 앉은 채 성공을 주장하는 방식이야.

Ashen Reel 은 seek 을 작은 상태 기계로 모델링해: pending 으로 시작하고, 엔진이 보고한 위치가 그걸 정확히 하나의 정직한 결말로 옮겨.

  • applied — 관찰된 위치가 타겟의 tolerance 안. 진짜 성공.
  • clamped — 타겟이 끝을 넘어서, duration 에 snap 됨. UI 는 요청한 것과 한 것을 둘 다 보여줘.
  • timeout — 엔진이 제때 위치를 확인 안 함. 요청 시간은 유지되고; 앱은 exact 열기를 주장 안 해.
  • failed — seek 이 에러남. 보이고, 이유가 보존됨.

정직한 정확함: 프레임 하나 또는 80밀리초

뭘 '요청한 데 떨어짐' 으로 쳐? 0밀리초 아냐 — 그건 매체가 못 지키는 환상이야. 영상은 프레임으로 이뤄지고, 화면에 존재하는 제일 작은 건 프레임 하나야. 그래서 tolerance 는 정직하게 디코드된 프레임 하나 또는 80 ms 중 큰 쪽으로 정의돼. 'exact' 는 어떤 포맷도 표현 못 하는 sub-frame 정밀도가 아니라 '맞는 프레임' 을 뜻해. 매체의 결에 맞춰 성공을 정의하는 게 주장을 진실되게 지켜.

결말을 boolean 이 아니라 이름 붙은 상태로 모델링해. 결과가 의미 있는 결말을 둘 넘게 갖는 순간, bool 은 안 맞는 것들에 대해 거짓말하게 강제해. 정직한 결말의 enum 은 UI, 로그, 테스트가 전부 같은 현실을 말하게 해줘.

해석 로직은 작고, 모든 분기가 실제로 일어날 수 있는 진짜야:

clamp 은 그래도 정직하게 보여줘야 하는 성공이야. 끝을 넘겨 점프하라고 요청하면, duration 에 snap 하는 게 맞는 동작이야 — 근데 UI 는 요청 시간이랑 적용 시간을 둘 다 보여줘, 사용자가 그림이 왜 타이핑한 데 없는지 이해하게. 조용한 clamp 은 버그처럼 보이고; 라벨 붙은 clamp 은 배려처럼 보여.

왜 이 기계가 남는 장사인가

seek 이 이름 붙은 상태를 가지면, downstream 전부가 공짜로 정직해져. 오버레이는 pending 동안 "seeking…" 을, 그 뒤 진짜 위치를 말할 수 있어. 로그는 수수께끼 같은 틀린 숫자 대신 clamped 를 기록해. 테스트는 추측 대신 appliedtimeout 을 단언해. 그리고 맞는 초에 떨어지느냐에 사활이 걸린 Recall handoff 는 마침내 "네가 기억한 그 순간에 있어""시도했고 실제로 이렇게 됐어" 를 구분할 수 있어.

Code

seek 결말을 정직한 상태로, 매체에 맞춰 해석·swift
enum SeekResult {
    case pending                       // sent, not yet confirmed
    case applied(observed: Double)     // landed within tolerance of target
    case clamped(to: Double)           // target was past the end -> snapped
    case timeout(target: Double)       // engine never confirmed in time
    case failed(reason: String)
}

/// Compare the engine's OBSERVED position to the target, honestly.
func resolveSeek(target: Double,
                 observed: Double,
                 duration: Double,
                 frameDuration: Double) -> SeekResult {
    let tolerance = max(frameDuration, 0.080)   // one frame OR 80 ms
    if target > duration { return .clamped(to: duration) }
    if abs(observed - target) <= tolerance { return .applied(observed: observed) }
    return .pending   // still off-target: keep observing, or time out elsewhere
}
// Nowhere does this return `true`. 'It worked' is one of five honest states.

External links

Exercise

네 코드에서 boolean '됐어?' 를 돌려주지만 실제론 결말이 둘 넘는 연산을 찾아 — 결제(승인 / 거절 / pending / 3DS-필요), 저장(저장됨 / 충돌 / 오프라인-큐잉), fetch(ok / not-modified / rate-limited / 실패). 반환을 이름 붙은 상태의 enum 으로 다시 설계해. 그다음 tolerance 질문을 골라: '충분히 가까움' 개념이 있는 뭐든에 대해, 도메인의 진짜 결에 맞춰 정의한 정직한 임계값이 뭐야?
Hint
bool 을 넘어섰다는 두 신호: (1) true 를 돌려주면서 어떤 종류의 true 였는지 warning 을 로깅하는 자신을 발견함; (2) 호출자가 진짜 무슨 일이 났는지 알려고 두 번째 필드를 봐야 함. 둘 다 결말이 이름을 원한다는 뜻이야. 그리고 '성공 tolerance' 를 이상화된 0 이 아니라 매체의 진짜 해상도에 맞춰 정의해.

Progress

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

댓글 0

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

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