작업을 시작해 놓고 다른 프로젝트를 보고 싶을 때가 있다. 그런데 실행 코드가 매번 화면의 현재 탭을 읽으면 사용자 이동이 업무 대상의 변경이 된다. 나는 보이는 선택과 프로젝트 실행의 cursor를 따로 두어 이 두 의미를 나눴다.

현재라는 말에 두 가지 시간이 있었다

화면의 현재 탭은 사람이 지금 보고 있는 위치다. 실행의 현재 탭은 그 작업이 이어서 읽거나 입력할 위치다. 둘이 같을 때만 가정하면 백그라운드 업무와 사람의 탐색이 서로 영향을 준다. 같은 변수 하나로 표현하기 어려운 상태였다.

ProjectCursor는 workspaceId·profileId·tabId를 묶는다. projectHost는 기존 BrowserHost를 두 개 만드는 대신 그 실행에서 읽는 현재 값을 cursor에 연결한다. 실제 세션과 탭 저장소는 같은 호스트에 남는다. 화면 복제와 문맥 분리를 같은 구조로 해결하지 않은 이유다.

선택은 실행 cursor를 먼저 바꾼다

projectHost의 select는 탭이 해당 프로젝트에 있는지 확인하고 cursor의 탭·프로필을 갱신한다. 필요한 탭을 깨우고 프로젝트 메모리와 저장도 이어 간다. 이것만으로 사용자가 보는 다른 프로젝트로 화면을 전환하지 않는다.

명시적인 interactive 경로이며 같은 프로젝트를 보고 있을 때에만 실제 화면 선택을 이어 주는 조건이 있다. 백그라운드 실행의 편의를 위해 사람의 포커스를 가져오는 방식으로 통일하지 않았다. 화면 반영이 필요한 관리 동작과 조용히 이어가야 하는 실행을 구별하는 선택이었다.

프로젝트에 영향을 주는 메서드의 수신자도 본다

Proxy에서 현재 값 몇 개만 바꾸고 나머지 메서드를 원래 호스트에 무조건 bind하면 내부 this가 다시 화면의 프로젝트를 볼 수 있다. 현재 코드는 프로젝트 범위 메서드 묶음을 proxy에 연결한다. 읽는 속성뿐 아니라 실제 메서드가 어느 문맥에서 실행되는지도 중요했다.

이 방식을 운영체제 프로세스 격리나 보안 샌드박스라고 부르지 않는다. 하나의 호스트에서 실행 문맥을 나누는 코드 구조다. 메서드를 추가할 때 범위와 호출부를 확인해야 하며 모든 새 기능이 자동으로 올바르게 분리된다고 가정할 수는 없다. 작은 proxy도 유지보수 계약이 필요하다.

요청한 탭과 다른 cursor를 같은 것으로 보고하지 않는다

외부 context 처리에는 target.tabId가 실제 실행 cursor와 다르면 MCP_TARGET_TAB_BUSY로 거절하는 검사가 있다. 바쁜 실행이 A를 가리키는데 호출자가 B를 지정했을 때 A의 내용을 B의 응답처럼 돌려주지 않는 것이다.

관련 통합 시험은 B 요청을 거절하고 activeTabId가 A로 유지되는지, A 요청과 기본 조회가 A를 정확히 돌려주는지 본다. 무조건 화면을 바꾸어 요청을 맞추는 대신 현재 상태와 요청이 충돌한다는 사실을 알려 준다. 조회 응답의 정체성도 실제 입력만큼 중요했다.

화면 선택만으로 계정과 결과를 추측하지 않는다

같은 프로젝트 안에도 서로 다른 계정의 탭이 있을 수 있다. 실행 문맥을 보존한다고 실제 로그인 유효성까지 확인한 것은 아니다. 계정 표시명과 현재 페이지의 인증 상태, 원래 업무의 대상은 각각 대조해야 한다.

이 글은 현재 projectHost와 context 처리, 관련 시험을 읽어 구성했다. 새 실행을 백그라운드에서 돌리거나 사용자 창을 전환한 검수는 아니다. 일반 화면 이동·프로젝트별 프로그램 실행·외부 호출의 문맥 지정은 입구가 다를 수 있다. 모든 경로를 새로 전수 시험했다고 쓰지 않고 확인한 계약을 구체적으로 남긴다.

대표 화면을 보며 직원 작업을 이어가는 것과 닮았다

회사 업무 도구에서도 대표가 현황 화면을 열었다고 직원의 처리 대상이 바뀌면 안 된다. 무엇을 보고 있는지와 어떤 업무를 실행하는지는 다른 상태다. 나는 브라우저에서도 이 구분을 명시적인 값으로 만들었다. 결과를 확인할 때도 화면에 보이는 프로젝트를 추측하지 않고 실행 기록의 프로젝트와 탭을 대조해야 한다.

보이는 화면을 하나로 유지하면서도 실행 대상은 고정할 수 있어야 사람이 다른 자료를 확인하기 편하다. 다만 실행 중인 작업과 중지 범위, 현재 결과를 읽는 입구도 함께 분리해야 한다. cursor 하나를 뒀다는 설명보다 그 값이 어떤 호출의 실제 대상을 정하는지 남기는 것이 제작자의 역할이라고 본다.