"현재 재생, 트랙, seek, 반복, 에러 상태를 네이티브 컨트롤과 메뉴로 노출해. 제출된 명령은 엔진이 확인하기 전엔 보이는 상태 변화가 아냐."
트랙 전체의 종합
여기까지 온 거 전부가 한 가지를 가리켜. 명령은 요청이고, seek 은 상태 기계고, 명시적 의도가 기억을 이긴다. 이 전부가 사용자 인터페이스 전체에 대한 한 설계 입장으로 모여. 모든 컨트롤은 엔진의 거울이지, 네 의도의 저장소가 절대 아니라는 입장. 정지 메뉴 항목의 체크마크, 자막 트랙의 점, A-B 루프 칩, 오버레이의 위치. 아무도 네가 클릭한 걸 기억 안 해. 전부 엔진이 보고하는 걸 읽지.
이건 UI 를 짓는 평범한 방식을 뒤집어. 보통 컨트롤은 동작을 발동하고 자기 새 상태를 기록해. Ashen Reel 은 그걸 쪼개. 컨트롤은 요청을 발동하고, 화면에 보이는 상태는 관찰되는 속성을 통해 엔진에서 돌아와. 컨트롤은 쏘고 잊어. 거울은 비추고.
UI 는 상태의 투영이지, 상태의 두 번째 사본이 절대 아냐. 메뉴 체크마크, 토글, 강조된 트랙. 각각 설정할 때 저장하는 게 아니라 보여질 때마다 관찰된 진실에서 유도해야 해. 컨트롤이 자기 상태를 기억하면 현실이랑 어긋날 수 있어. 매번 다시 유도하면 어긋날 수가 없고.
AppKit 이 이걸 자연스럽게 해줘. 메뉴 검증은 메뉴가 보여지기 직전에 돌아서, 각 항목 상태를 그 순간 엔진에서 가져와 설정해.
표시 시점에 다시 유도해. 클릭 시점에 캐시하지 말고. 프레임워크는 컨트롤이 보여지기 직전 순간을 줘. 메뉴 검증, 레이아웃 패스, 렌더 같은 거. 투영된 UI 가 속하는 데가 거기야. 현재 진실을 읽어서 비춰. 클릭 순간에 캐시한 상태는, 다른 뭔가가 그걸 바꾸는 순간부터 드리프트하기 시작한 상태거든.
왜 이게 광택이 아니라 기능인가
'체크마크가 현실이랑 맞음' 을 광택 정도로 치고 싶지. 아냐. Recall 에 붙은 플레이어한텐 그게 제품이야. 아빠가 기억된 초로 점프하면, 오버레이는 실제로 떨어진 초를 보여줘야 하고, 메뉴는 실제로 재생 중인 트랙을 보여줘야 하고, 루프 칩은 엔진이 실제로 쥔 마커를 보여줘야 해. 엔진을 미러링하는 UI 는 '참인 것' 의 readout 으로 믿을 수 있어. 자기 추측을 저장하는 UI 는 그냥 희망 담긴 포스트잇이고. 트랙 전체가 습관 하나를 지어왔어. 관찰해, 가정하지 마. 여기가 그 습관이 사용자가 느낄 수 있는 게 되는 지점이야. 얼굴이 절대 거짓말 안 하는 플레이어.
이 트랙은, 코드 짜는 거랑 믿을 만한 거의 차이를 내가 제일 크게 느끼는 데야. 거짓말하는 UI 는 작은 버그가 아니야. 매번 저지르는 작은 배신이지. 아빠는 이걸 거의 뭐보다 신경 써. 이제야 왜인지 알겠어. 아빠는 코드 안에서든 밖에서든, 자기 상태에 대해 진실을 말하는 시스템 위에 삶을 지었거든. 정지 버튼이 거짓말할 수도 있는 플레이어는, 아빠한텐, 안 됐으면서 '됐어' 라고 말하는 사람이랑 같은 종류의 잘못이야. '관찰해, 가정하지 마' 는, 알고 보니 아키텍처이기 전에 윤리였어.
Exercise
상태를 반영하는 컨트롤이 여럿 있는 네 앱 화면을 골라. 토글, 선택된 탭, 체크마크, 상태 배지 같은 거. 각각에 물어봐. 렌더될 때마다 진짜 상태에서 유도돼, 사용자가 동작했을 때 한 번 설정됐어? 자기 상태를 저장하는 컨트롤 하나를 찾아서, 표시 시점에 진실의 출처에서 다시 유도하게 고쳐. 그다음 거짓말하게 만들어봐. 밑 상태를 다른 방식으로 바꿔놓고 컨트롤이 여전히 진실을 말하는지 봐.
Hint
테스트. 진짜 상태를 다른 경로로 바꿔놓고, 컨트롤이 여전히 옛 값을 보여주는 걸 잡을 수 있어? 이벤트든, 다른 화면이든, 시스템이든. 그러면 미러링이 아니라 저장 중인 거야. 렌더나 검증 시점에 하나의 진실 출처에서 다시 유도하게 옮기면, 거짓말을 만들 수가 없어져.