인쇄하면 호스트가 같이 나온다
이날은 수료증 기능의 기술적 난관을 해결한 날이다.
인쇄할 때 호스트 UI가 같이 나오는 문제를 DOM을 옮기는 것으로 해결했다.
그리고 모듈 연합 환경에서 각 앱이 따로 만드는 캐시를 하나로 통합했다.
수료증을 인쇄할 때 문제가 있었다.
인쇄 버튼을 누르면 수료증만 나와야 하는데, 호스트의 헤더랑 사이드바가 같이 나왔다.
원인은 DOM 구조였다.
수료증 앱은 모듈 연합 호스트 안에 내장되어 있다.
window.print()를 호출하면 브라우저가 현재 DOM 트리 전체를 캡처한다.
수료증 노드가 호스트 레이아웃 안에 깊이 중첩되어 있으니까, 호스트 CSS가 인쇄에 반영되는 거다.
DOM 텔레포트
해결은 인쇄 직전에 DOM을 옮기는 거였다.
beforeprint 이벤트에서 수료증 노드를 body 직계 자식으로 옮긴다.
호스트 레이아웃 밖으로 빼내는 거다.
그러면 인쇄할 때 호스트 CSS가 안 먹는다.
인쇄가 끝나면 afterprint에서 원래 위치로 되돌린다.
100밀리초 지연을 줘서 DOM 이동이 확실히 반영된 후에 인쇄가 시작되게 한다.
이건 “DOM 텔레포트” 패턴이다.
인쇄 컨텍스트에서 노드를 잠시 다른 곳으로 옮겼다가 되돌린다.
인쇄는 DOM 스냅샷을 찍으니까, 찍히는 순간에 노드가 어디 있느냐가 결정적이다.
Module Federation 공유 캐시
두 번째 문제는 캐시 분리였다.
모듈 연합 환경에서 호스트 셸이랑 서브앱이 각자 QueryClient를 만들고 있었다.
각자 캐시를 가진다.
호스트에서 수료 상태를 갱신해도 서브앱은 옛날 캐시를 쓴다.
해결은 QueryClient를 하나로 통합하는 거였다.
전역 싱글톤 팩토리를 만들었다.
호스트랑 서브앱이 같은 인스턴스를 공유한다.
갱신하면 양쪽이 다
반영된다.
서버 사이드 렌더링 가드도 넣었다.
서버에서는 window가 없으니까, window가 있을 때만 싱글톤을 만든다.
개발 환경에서 서브앱을 단독으로 실행할 때는 폴백으로 새 인스턴스를 만든다.
dev 오케스트레이터 함정
개발 서버 오케스트레이터에서도 세 가지 함정을 잡았다.
첫째, 고아 프로세스다.
오케스트레이터가 강제로 죽으면, 서브앱 프로세스가 좀비로 남는다.
포트를 잡고 있어서 다음 실행이 실패한다.
다음 실행 시 죽은 앱을 정리하는 로직을 넣었다.
둘째, 포트 충돌이다.
포트가 각 앱의 웹팩 설정에 하드코딩되어 있다.
한 앱만 스캔하면 다른 네임스페이스 앱이랑 충돌한다.
전체 패키지를 스캔해야 포트 충돌을 잡을 수 있다.
셋째, 상태 파일 레이스다.
상태 파일을 읽고 수정하고 쓰는 과정에서 종료 이벤트가 끼어들면 경쟁이 생긴다.
프로세스별 임시 파일을 쓰고 정리 순서를 보장해서 해결했다.
배포 경로 추론
배포 대상 추론에서도 함정이 있었다.
diff 기준이 origin/main이면 머지 커밋의 변경까지 섞여서 배포 범위가 왜곡된다.
소스 브랜치만의 변경을 정확히 추리려면 diff 기준을 소스 브랜치로 잡아야 한다.
그리고 삼점 diff랑 이점 diff가 다르다.
삼점은 대칭 차이라 main 쪽 커밋까지 섞인다.
배포 대상 검증에는 이점 diff가 정확하다.
마무리
내가 이날 배운 핵심은 다음과 같다.
이날의 핵심은 “모듈 연합 환경의 격리 문제를 해결하는 것”이었다.
인쇄 DOM 텔레포트는 시각적 격리다.
인쇄할 때 호스트 UI가 섞이는 문제를 DOM을 옮겨서 해결했다.
인쇄는 DOM 스냅샷이니까, 스냅샷 찍히는 순간의 위치가 중요하다.
공유 QueryClient는 데이터 격리다.
각 앱이 따로 캐시를 가지면 갱신이 전파 안 된다.
하나로 통합하면 갱신이 양쪽에 다
반영된다.
모듈 연합은 앱을 독립적으로 만들지만 동시에 공유해야 하는 것이 생긴다.
DOM, 캐시, 포트. 이걸 언제 독립시키고 언제 공유할지 결정하는 게 모듈 연합 환경의 핵심 과제다.