pubuilder 07. 로컬 서버 보안 강화와 Design IR 확장
로컬에서만 도는 서버라는 이유로 인증을 미뤄두고 있었다. 이날은 거기부터 손댔다. 세션 토큰과 origin 검증을 넣고, 그 때문에 깨진 연결을 자동 탐색으로 다시 잇고, 화면에서 어긋나 보이던 것들을 고치고, Design IR에 시각 효과 정보를 채워 v0.9 계열을 세 번에 나눠 냈다.
localhost라서 안전하다고 믿고 있었다
pubuilder의 로컬 컴패니언 서버는 브라우저와 에이전트 사이를 잇는 중계 역할을 한다. 로컬호스트에만 떠 있으니 외부에서 못 건드린다고 생각했는데, 같은 머신에서 도는 다른 페이지나 프로세스는 얼마든지 붙을 수 있었다. 인증도 없고 실행 환경 경계도 느슨했다.
경계를 네 겹으로 나눠 그었다. 요청에 세션 토큰을 요구하고, origin(요청을 보낸 페이지의 출처. 프로토콜·호스트·포트 조합이다)을 지정한 값으로 고정하고, 서버가 띄우는 Claude 프로세스에 넘길 환경 변수를 허용 목록 방식으로 제한하고, Figma 접근 권한도 필요한 범위만 열었다. 자식 프로세스에 셸 환경 변수를 통째로 물려주면 토큰이나 키가 의도치 않게 따라간다.
사용법 문서와 플레이그라운드 예제를 새 인증 흐름에 맞춰 같이 고치고 v0.9.0으로 배포했다. 그런데 macOS에서 Team 인증이 통과되지 않았다. USER와 LOGNAME을 허용 목록에 추가하니 통과됐다.
origin을 고정했더니 연결이 끊겼다
origin을 고정하고 나니 localhost로 접속한 페이지와 127.0.0.1로 접속한 페이지가 서로 다른 출처로 걸렸다. 같은 서버를 보고 있는데 한쪽만 통과했다. 둘을 동등한 origin으로 처리하도록 검증 로직을 고쳤다.
포트에서도 비슷하게 걸렸다. 기본 포트가 이미 다른 프로세스에 물려 있으면 그대로 연결에 실패했다. 호출부가 포트와 세션 정보를 직접 가정하고 있던 게 문제였다. CLI가 시작할 때 빈 포트를 자동으로 고르게 하고, 세션 탐색 책임은 PubuilderApi로 옮겼다. 사용자 입장에서는 편의 기능이지만, 실제로 얻은 건 연결 정보가 한곳에서만 관리된다는 구조 쪽이었다.
중간 범위는 아무도 고르지 않았다
요소를 검사할 때 고를 수 있는 범위가 세 단계였다. 페이지 전체, 중간 묶음, 개별 요소. 중간 단계는 어디까지 잡히는지 예측이 어려워서 결국 전체 아니면 개별로 다시 고르게 됐다. 중간 단계를 없애고 페이지 전체와 개별 요소 둘만 남겼다. 타입, 프롬프트, 테스트, 문서를 함께 고치고 v0.9.3으로 배포했다.
퍼블리싱 중일 때 어느 블록이 실행 중인지도 화면에서 잘 안 보였다. 상태 칩을 실행 중인 블록 위에 직접 올렸다. 그리고 휠을 빠르게 굴리면 하이라이트 박스가 실제 요소보다 뒤처져 어긋났다. 위치 갱신이 스크롤 속도를 못 따라간 것이라 측정과 오버레이 갱신 흐름을 보강했다.
PageMap 오버레이는 별도 React root로 분리했다. 호스트 앱의 렌더 트리와 섞이지 않게 하려는 것이었다. 캔버스 쪽에서는 Controls를 MiniMap으로 바꾸고, 선택 라벨이 화면 밖이나 우측 패널 뒤로 잘려나가지 않도록 위치를 보정했다.
원본을 더 많이 넘기는 대신 의미를 더 담았다
Design IR(Intermediate Representation. Figma 원본 JSON과 모델 사이에 두는 중간 표현이다)은 구현 판단에 필요한 필드만 추려 담는다. 그런데 초기 스키마가 레이아웃과 색상 위주라, 실제로 디자인을 재현하려니 정보가 비었다. 테두리 하나 그리려는데 stroke 값이 없는 식이었다.
stroke, gradient, blur 같은 시각 효과와 sizing, bindings, 그리고 제외 노드까지 IR에 포함시켰다. 디자인 정확도는 원본을 더 크게 통째로 넘겨서 나오는 게 아니라, 구현 판단에 쓰이는 의미를 빠짐없이 보존하는 데서 나온다.
퍼블리싱 절차를 담은 publish 스킬과 프롬프트에도 게이트를 더 넣었다. 제외 노드를 실제로 확인하는 단계, 지침이 충돌할 때의 우선순위, 구현 후 디자인 시스템 검증 단계다. 작업이 끝나면 Figma 렌더 임시 이미지를 지우도록 수명 주기도 손봤다. 변경 내역을 정리해 v0.9.5로 배포했다.
막힌 지점과 원인
| 문제 | 원인 | 해결 |
|---|---|---|
| 로컬 서버라도 다른 origin이나 프로세스에서 접근 가능 | 인증과 실행 환경 경계가 느슨함 | 세션 토큰, origin 고정, env·Figma 권한 허용 목록 적용 |
| 기본 포트 충돌 시 연결 실패 | 포트와 세션 정보를 호출부가 직접 가정 | CLI의 빈 포트 선택과 API의 세션 자동 탐색 도입 |
| 빠른 스크롤에서 하이라이트가 요소와 어긋남 | 위치 갱신이 스크롤 속도를 따라가지 못함 | 측정과 오버레이 갱신 흐름 보강 |
| 디자인 재현에 필요한 효과 정보가 누락 | 초기 IR이 핵심 레이아웃·색상 위주 | 시각 효과·크기·바인딩·제외 노드까지 스키마 확장 |
세 번의 작은 릴리스로 나눠 낸 덕에 어떤 변경이 어디까지 영향을 줬는지 경계가 흐려지지 않았다.
아직 남은 건 있다. 세션 토큰에 만료와 재발급 정책이 없어서 그대로 두고 쓰는 상태고, gradient와 effect가 복잡하게 겹친 노드에서 Design IR이 제대로 나오는지 확인할 회귀 사례도 더 필요하다. 별도 root로 띄운 오버레이가 호스트 CSS 환경에 따라 어떻게 보이는지도 확인이 남았다.