putree 19. 퍼블리싱 독 통합과 리로드 후 실행 중 작업 복원
쿼리로 갈리는 화면을 캔버스에서 바로 추가하게 됐다
?publicId= 없이는 목록으로 튕기는 편집 페이지가 있었다. 캔버스 카드는 그 페이지를 열 때마다 "다른 페이지가 떴어요"만 반복해 보여줬다. 실제 주소를 알려줄 칸이 없었기 때문이다.
막힌 곳이 두 겹이었다. 쿼리가 붙은 주소를 붙여넣으면 마지막 세그먼트가 edit?publicId=…가 되어 자기 자신의 패턴에도 안 맞아 거절됐다. 쿼리로 화면을 가르는 길도 config 파일에 직접 쓰는 variants뿐이었다.
segmentsOf가 query와 hash를 떼고 판정하도록 바꿨다. variant 경로는 다른 라우트가 아니라 같은 라우트의 다른 화면이다. 그리고 설정 모달에 쿼리 칸을 만들어 publicId=1처럼 짧게 써도, 주소를 통째로 붙여넣어도 받아 variants에 적게 했다. variant 카드는 자기 쿼리를 채워 보여준다.
이름과 쿼리는 한 번에 저장한다. 따로 쓰면 두 번째 패치가 첫 번째 결과를 못 본 채 배열을 되돌린다. 로그인이 아닌 리다이렉트 카드에는 그 칸으로 가는 입구를 뒀다.
README에 절을 더하고 0.10.3으로 냈다.
독을 캔버스 밖으로 올리고 두 독을 하나로 합쳤다
퍼블리싱 현황 독은 진행 중인 퍼블리싱 작업 목록을 항상 띄워두는 패널이다. 이게 캔버스 안에 살아 있어서 뷰어(z-index 2147483200)가 열리면 통째로 가려졌다. 뷰어에는 그 페이지만 보는 PublishDock이 따로 있었다. A 페이지 작업을 걸어두고 B 페이지를 열면 돌고 있는 일을 볼 방법이 없었다.
FAB과 같은 자리인 PageMapInner로 올렸다. 앱 화면이든 맵이든 뷰어든 그대로 남는 최상위 컴포넌트라, 어디서 보든 같은 목록이 뜬다. .putree-dock은 position: fixed에 z-index 2147483250을 줬다. 충돌 모달과 FAB에는 양보하는 값이다.
PublishDock은 지웠다. 그 몫이던 두 가지는 MapDock이 잇는다. 지금 보고 있는 페이지의 행은 뷰어를 다시 열지 않고 화면에서 그 블록을 바로 짚고(viewer-block-select 창구), 찾을 수 없는 블록은 누르기 전에 알린다.
헤더는 언제나 접기/펼치기다. 행이 하나일 때 곧바로 이동하게 두면 접을 방법이 사라지고, 같은 자리가 누를 때마다 다른 일을 한다. 앞서 편의로 넣어뒀던 동작을 이번에 되돌렸다.
클릭 뒤 남는 브라우저 기본 포커스 링도 껐다. overflow: hidden에 한 변만 잘려 보여서 상자 테두리처럼 읽혔기 때문이다. 키보드 초점은 배경색으로 알린다. IA에 없는 경로도 지금 보고 있는 화면이면 행을 남기게 했고, 쓰는 곳이 없어진 dockRowsForPage는 제거하면서 커버리지는 dockRowsAll로 옮겼다.
올린 대가 둘, 가려진 모달과 옛 페이지에 남은 목록
독을 최상위로 올리면서 z-index가 오버레이(2147483100) 위로 올라갔다. 페이지 추가 모달이나 IA 주소 입력 모달은 오버레이 안에서 z 10을 쓰는데, 열면 독이 그 위에 겹쳐 지금 답해야 하는 것을 가렸다.
z를 낮추는 대신 모달이 열리면 독을 감췄다. 낮추면 뷰어 위에 있으라는 이 컴포넌트의 존재 이유가 함께 사라진다.
작업완료 목록은 옛 페이지에 남아 있었다. "지금 페이지"를 가리키는 값이 둘이기 때문이다. viewerPath는 처음 연 카드의 경로고, activePagePath는 프레임 안에서 링크를 눌러 실제로 도달한 경로다.
뷰어 안에서 이동하면 블록 오버레이와 독은 activePagePath를 따라가는데 이 목록만 viewerPath에 남아, 지금 화면에 없는 블록의 좋아요와 되돌리기가 떠 있었다. activePagePath로 맞췄다.
리로드해도 돌고 있는 퍼블리싱이 돌아온다
비대칭이 남아 있었다. 실행 중 상태는 클라이언트 store에만 살아서, 퍼블리싱이 유발한 full reload나 앱 페이지 이동 한 번에 "퍼블리싱 중" 행이 화면에서 사라졌다. 에이전트는 서버에서 계속 일하고 있는데도.
완료된 일은 reviews.json이 근거를 들고 있어 /api/reviews로 되살아나는데, 실행 중인 일에는 그런 근거가 없었다. 끝난 건 보이는데 돌고 있는 건 안 보이는 상태였다.
PublishJobManager가 job의 review 서술자를 보관하고 activeJobs()로 내려주게 했다. 블록이 없는 job(구조 변경)은 제외했다. 독에 올릴 행도 없고 만들 블록 키도 없기 때문이다. 그 위에 GET /api/publish/active와 api.listActiveJobs()를 얹었다.
클라이언트에서는 store.hydrateActiveJobs가 서버 값으로 store를 채운다. 이 세션에서 돌리고 있는 job은 건드리지 않는다. 로그와 입력값이 이미 붙어 있어서다. 이름과 마커도 세션 값이 이긴다. hydrateReviews와 같은 규칙이다.
채운 뒤에는 각 job의 이벤트 스트림에 다시 붙는다. 서버 subscribe가 과거 이벤트를 replay하므로 로그와 확인 질문이 함께 돌아오고, 그 사이 끝난 job은 곧바로 done을 받는다.
flowchart TD
A["서버 PublishJobManager: 실행 중 job 보관"] --> B["GET /api/publish/active"]
B --> C["store.hydrateActiveJobs"]
C --> D["이벤트 스트림 재구독"]
D --> E["과거 이벤트 replay"]
E --> F["로그·확인 질문 복원"]
E --> G["그 사이 끝난 job은 곧바로 done"]
구독 구현은 publish-subscription으로 분리했다. 패널과 하이드레이션이 각자 terminal 처리(done·실패를 마무리하는 코드)를 따로 갖고 있으면 "리로드하면 완료 처리가 안 된다" 같은 차이가 조용히 생긴다.
실제로 돌려서 확인했다. job이 도는 중에 페이지를 새로 열면 "퍼블리싱 중" 행이 돌아오고, 완료되면 "작업완료"로 넘어가며 좋아요와 되돌리기가 뜬다.
카드 색과 playground 픽스처를 정리했다
카드 상태를 배지 말고 카드 자체의 색으로도 읽히게 했다. 색 글로우와 헤더 상태 레일을 붙이고 0.10.4로 냈다.
playground에서는 my 페이지를 러닝 크루 시안에서 기본 프로필 페이지로 되돌렸다. my-run.css는 대부분 지웠다. 픽스처가 특정 시안에 묶여 있으면 다른 검증에 못 쓴다. 모델 탭은 세그먼트 컨트롤로 바꾸고 카드를 3열로 폈다.
어디서 막혔고 무엇으로 풀었나
| 문제 | 원인 | 해결 |
|---|---|---|
| 쿼리 붙은 주소를 붙여넣으면 거절됨 | segmentsOf가 query를 세그먼트에 포함해 edit?publicId=…가 자기 패턴에도 안 맞았다 | query와 hash를 떼고 판정 |
| A 페이지 작업을 걸고 B를 열면 진행 상황이 안 보임 | 독이 캔버스 안에 살아 뷰어에 가려지고, 뷰어에는 그 페이지만 보는 별도 독이 있었다 | 최상위로 올려 하나로 합침 |
| 모달이 독에 가려짐 | 독 z-index가 오버레이 위로 올라갔다 | z를 낮추지 않고 모달이 열리면 독을 감춤 |
| 프레임 안 이동 후 없는 블록의 좋아요가 떠 있음 | 작업완료 목록만 viewerPath를 보고 있었다 | activePagePath로 맞춤 |
| 리로드하면 "퍼블리싱 중"이 사라짐 | 실행 중 상태가 클라이언트 store에만 있고 서버에 근거가 없었다 | GET /api/publish/active와 hydrateActiveJobs, 스트림 재구독 |
z를 낮추지 말고 그 순간만 감춘다
화면에 상시 떠 있는 것은 z-index를 올린 만큼 가릴 것도 늘어난다. 독을 올린 것과 그 대가를 갚은 것이 같은 날 안에 있었다. "위에 있으라"는 요구와 "지금 답할 것을 가리지 마라"는 요구는 z 하나로 동시에 만족되지 않았다.
"현재 페이지"를 가리키는 값이 둘이면 언젠가 갈라진다. viewerPath와 activePagePath가 공존하는 한, 새로 붙는 UI마다 어느 것을 볼지 틀릴 기회가 생긴다. 또 한 번 같은 곳에서 틀렸다.
클라이언트 store에만 있는 상태는 리로드에서 없던 일이 된다. 완료는 파일에 근거가 있어 되살아나는데 실행 중은 아니었다. 그 비대칭은 기능 설계가 아니라 저장 위치의 우연이었다. 서버가 계속 일하고 있다는 사실을 화면이 잊는다면 그건 화면의 버그다.
좋은 아이디어도 며칠 만에 되돌리게 된다. "행이 하나면 헤더 클릭이 곧바로 이동"은 그것만 보면 편했는데, 독이 상시 표시가 되자 접을 방법이 사라지는 대가가 커졌다. 같은 자리가 상황에 따라 다른 일을 하면 그건 편의가 아니다.
코드는 줄었는데 기능은 늘었다. PublishDock을 없애고 그 몫을 MapDock에 잇는 것이 "뷰어에서도 다른 페이지 작업이 보인다"는 새 기능을 만들었다. 중복 구현이 있는 자리에서는 합치는 것이 곧 기능이다.
아직 남은 것들이 있다. 독 폭이 접힘과 펼침에서 벌어진다(min 240 / max 340). 상시 표시물이니 고정해야 한다.
헤더 요약 문구도 다시 봐야 한다. 작업완료 2가 건수가 아니라 배지로 읽힌다. "확인 필요" 테두리가 뜬 카드를 눌러도 확인 질문이 열리지 않고, 독 행을 눌러도 마찬가지다. 확인 대기 목록에서 긴 블록 이름이 오른쪽으로 잘리는 것도 그대로다.