본문 바로가기
C.W.K.
Stream
Lesson 01 of 05 · published

보낸 명령이 곧 바뀐 상태는 아냐

~12 min · observed-state, async, optimistic-ui, state

Level 0릴 입문자
0 XP0/39 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"제출된 명령은 엔진이 확인하기 전엔 보이는 상태 변화가 아냐."

속도처럼 느껴지는 작은 거짓말

정지 버튼을 제일 빠르게-느껴지게 만드는 방법은, 사용자가 클릭하는 순간 UI 를 뒤집는 거야. isPaused = true 로 놓고, 그다음 엔진한테 정지하라고 하는 거지. 즉각적으로 느껴져. 진짜로 즉각이니까. UI 가 아무것도 안 기다렸거든. 근데 방금 작은 거짓말을 했어. 엔진은 아직 정지 안 했어. 보통은 몇 밀리초 뒤에 하고, 아무도 못 알아채. 근데 가끔은 안 해. 느린 프레임, 에러, race 때문에. 그럼 이제 버튼은 이 말을 하는데 영상은 저 짓을 하고 있어.

그걸 정지, seek, 트랙 선택, 속도, 전체화면에 걸쳐 곱해봐. 컨트롤이 일어난 것의 보고가 아니라 네가 요청한 것에 대한 이야기인 플레이어가 돼. 그 틈이 바로 사람 미치게 하는 버그가 사는 데야.

명령은 요청, 현실은 보고

고침은 코드이기 전에 사고방식이야. 네가 보내는 명령은 요청이고, 엔진의 관찰된 상태가 유일한 보고야. 보고 채널은 Track 2 에서 이미 깔았어. 관찰되는 속성. 이제 진짜로 쓰는 거야. 요청을 보내고, 엔진 값이 실제로 바뀔 때 관찰되는 속성이 UI 를 업데이트하게 둬. UI 는 예측을 멈추고 반영을 시작하고.

명령은 요청이지 결과가 절대 아냐. 상태를 참으로 만드는 유일한 건 시스템이 그걸 되돌려 보고하는 거야. 네가 보낸 것 말고 돌아온 것에서 UI 를 업데이트해. 둘은 다른 이벤트고, 하나로 취급하는 게 모든 '버튼이 틀렸어' 버그의 뿌리야.

같은 기능을 거짓말로, 그리고 진실로 보면.

낙관적 UI 도 자리가 있어. 근데 여긴 아냐. 싸고, 되돌릴 수 있고, 거의 확실한 동작이면 성공을 예측하고 실패하면 고치는 게 좋은 UX 야. 포스트 좋아요나 로컬 설정 토글 같은 거. 근데 재생 진실은 달라. 정지됐나, 그림이 어디 있나. 여기선 틀린 예측이 사용자가 눈앞에서 지켜보는 거짓말이야. 낙관은 틀려도 싼 것에만 남겨.

Pending 은 정직한 상태야

요청이랑 보고 사이엔 진짜, 이름 붙일 수 있는 순간이 있어. pending. Ashen Reel 은 그걸 보여주는 데 거리낌이 없어. 동작이 진행 중이라는 짧은 힌트지. pending 은 정직하고, 거짓 '완료' 는 안 정직하니까. 중간 상태에 이름을 붙이는 게 UI 가 진실을 끊김 없이 말하게 해줘. 요청됨, 그다음 pending, 그다음 엔진이 실제로 보고하는 뭐든. 어떤 단계도 나중 단계인 척 안 하고.

Code

같은 정지 버튼, 거짓말로 그리고 진실로·swift
// BAD: optimistic. We declare the outcome before the engine acts.
func pauseNaive() {
    isPaused = true                              // UI now CLAIMS paused...
    try? mpvCommand(mpv, ["set", "pause", "yes"]) // ...but did it? we never checked.
}

// GOOD: send a request; let the observed 'pause' property report the truth.
func pause() {
    try? mpvCommand(mpv, ["set", "pause", "yes"])  // a REQUEST, nothing more
    // isPaused is updated ONLY by the observed-property handler (lesson 2.4),
    // when the ENGINE's pause flag actually flips. Until then the UI may show
    // a 'pending' hint -- never a false 'paused'.
}

// The difference isn't style. In the BAD version, isPaused and the engine
// can disagree forever. In the GOOD version, they cannot -- there's one source.

External links

Exercise

네 UI 나 API 클라이언트에서 명령 보낸 직후 상태를 바로 업데이트하는 곳을 찾아. 토글, 상태 라벨, 진행 표시 같은 거. 물어봐. 명령이 느리거나, 실패하거나, 시스템이 덮어쓰면 어떻게 돼? 그 동작이 싸고 되돌릴 수 있는 거야(낙관 괜찮음), 사용자가 의존하는 진실이야(진짜 결과를 반영해)? 한 케이스를 확인된 결과에서 업데이트하고, 그 사이엔 정직한 'pending' 을 두게 다시 써.
Hint
모든 낙관적 업데이트에 던질 질문. '내 예측이 틀리면 얼마나 나쁘고, 사용자가 알아챌까?' 싸고 안 보이면 낙관 괜찮아. 비싸거나 사용자가 보는 진실이면, 요청 보내고, pending 보여주고, 현실이 보고할 때만 업데이트해.

Progress

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

댓글 0

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

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