Skip to content

Single Session Model

Woojin Kim edited this page Jun 20, 2026 · 2 revisions

단일 세션 모델을 채용한 이유

pytmux 는 tmux 와 달리 이름 붙은 여러 세션을 두지 않는다. 하나의 서버(데몬)가 탭·윈도우·패널로 이뤄진 단 하나의 세션을 호스팅하고, 여러 클라이언트가 그 동일한 화면에 붙어 공유한다. 이 페이지는 그 모델이 무엇이고, tmux 와 어떻게 다르며, 무엇을 얻고 무엇을 포기했는지를 솔직하게 정리한다.

한눈에 보는 계층

flowchart TD
    D["서버(데몬, 단일 asyncio 루프)"]
    S["Session — 항상 하나. 사용자 표면에서 이름/전환 개념 없음"]
    T["Tab — 최상위 전환 단위 (tmux 의 '윈도우' 역할)"]
    W["Window — 탭마다 1:1 로 종속되는 렌더 영역"]
    P["Pane — 이진 분할 트리의 잎. 셸 1개 = PTY + 화면 버퍼"]
    D --> S
    S --> T
    T --> W
    W --> P
Loading

Session → Tab → Window → Pane 의 4계층이지만, 사용자가 실제로 다루는 전환 단위는 이다. 세션은 항상 하나뿐이라 배경으로 물러나 있다.

다중 탭

tmux 와의 차이

tmux 는 여러 개의 이름 붙은 세션을 만들고(new-session -s work), 그 사이를 오가며(switch-client, choose-tree) 쓰는 모델이다. 각 세션은 자기만의 윈도우 목록을 가진다. 강력하지만, 그만큼 사용자가 머릿속에 "세션 → 윈도우 → 패널" 3계층을 늘 들고 있어야 하고, "지금 어느 세션에 붙어 있는가"를 신경 써야 한다.

tmux pytmux
최상위 전환 단위 이름 붙은 세션 여러 개 탭 하나의 줄 (세션은 하나)
작업 공간 분리 세션 + 윈도우 으로 일원화
"어디에 붙어 있나" 세션 선택 필요 항상 그 하나의 세션
attach attach -t <name> 이름 없이 단일 세션에 붙음

pytmux 는 tmux 의 세션 + 윈도우 두 층이 하던 일을 "탭" 한 층으로 합쳤다. 여러 작업 공간이 필요하면 세션을 새로 파는 대신 탭을 연다. 탭마다 단일 윈도우가 종속되고, 그 윈도우를 패널로 분할한다.

좌우 분할

분할은 이진 트리라서 얼마든지 중첩할 수 있다. 한 탭 안에서 좌우·상하 분할을 섞어 원하는 레이아웃을 만든다.

중첩 분할

왜 이렇게 정했나 — 동기

1. 단순한 멘탈 모델

대상 사용 시나리오(원격 개발 머신에서 셸 몇 개 + 빌드/AI 작업 패널을 띄워두는 용도)에서 세션을 여러 개 굴릴 일은 드물었다. 거의 모든 사용자가 세션 하나에 윈도우(탭) 몇 개로 끝났고, 그렇다면 "세션"이라는 한 계층은 사용자에게 외워야 할 개념만 늘리고 실익은 적은 층이었다. 그래서 세션 전환 UI 를 사용자 표면에서 통째로 들어내고, 탭으로 단일화했다. 이 결정은 프로젝트 초기(탭 모델 도입기)에 내려졌고, 이후 모델의 뼈대로 굳었다.

2. 단일 스레드 asyncio 서버와의 궁합

서버는 단일 스레드 asyncio 이벤트 루프 하나로, 모든 패널의 PTY 읽기 → 터미널 에뮬레이션 → dirty 표시 → 클라이언트로의 화면 diff push 를 처리한다. 상태가 하나의 세션 트리로 응집되어 있으면 이 루프가 다뤄야 하는 권위 상태가 단일해진다. 여러 독립 세션을 동시에 들고 그 사이 라우팅·격리를 관리하는 복잡도를 애초에 들이지 않는다. 서버가 레이아웃의 단일 권위자이고 클라이언트는 받은 좌표를 그리기만 하는 구조와도 잘 맞는다.

3. 공유가 단순해진다

세션이 하나뿐이면 "여러 클라이언트가 같은 것을 본다"가 기본값이 된다. 별도의 세션 선택·매칭 협상이 없다.

다중 클라이언트 attach/detach — 하나의 세션을 공유하는 법

서버(데몬)가 셸 PTY 를 소유하고, 클라이언트(터미널 앱)는 거기에 붙어 화면을 그린다.

  • attach: 클라이언트는 이름 없이 그 하나의 세션에 붙는다. 붙는 순간 누적 스트림이 아니라 현재 화면 전체 스냅샷을 받아 즉시 복원한다.
  • 다중 클라이언트 미러링: 여러 클라이언트가 동시에 같은 세션에 붙을 수 있다. 모두 같은 탭·패널·레이아웃을 본다(미러링). 레이아웃은 붙은 클라이언트들의 최소 크기에 맞춰 동기화된다.
  • detach: 클라이언트가 빠져나가도 서버와 셸은 그대로 남는다. 클라이언트가 0개여도 셸은 계속 돈다(긴 빌드·원격 작업 유지). 다시 실행하면 같은 세션에 이어 붙는다.
  • 터미널 창을 닫아도(SIGHUP) 클라이언트만 죽고 데몬은 영향을 받지 않는다.

세션이 하나라서, "어느 세션을 미러링할지"를 고를 필요 없이 붙으면 곧 같은 화면이다.

탭 + 레이아웃 슬롯이 세션/윈도우를 대체하는 방식

tmux 에서 세션·윈도우로 하던 일을 pytmux 는 탭과 레이아웃 기능으로 흡수한다.

  • = tmux 의 윈도우 역할. new-tab(=new-windowrename-tab·kill-tab, 다음/이전/번호 선택, 마지막 탭 토글, 재정렬/이동을 제공한다. 상단 탭바(tmux 엔 없는 UI)로 마우스 클릭·드래그·키보드로 다룬다.
  • 탭별 레이아웃 슬롯: 각 탭의 윈도우+패널 트리를 이름 슬롯으로 저장하고 (layout-save) 현재 탭에 덮어쓰거나 새 탭으로 불러온다(layout-load). tmux 에서 "특정 세션을 특정 레이아웃으로 세팅해 두던" 작업을 저장된 레이아웃을 탭에 불러오는 방식으로 대신한다.
  • 패널 분리/합치기: 패널을 새 탭으로 떼거나(break) 다른 탭의 패널을 끌어오는(join) 조작으로, 세션 간 윈도우 이동 같은 재배치 욕구를 탭 단위로 충족한다.

트레이드오프 — 포기한 것과 얻은 것

솔직히 말하면, 이 모델은 만능이 아니다. 명시적으로 포기한 것이 있다.

포기한 것

  • 이름 붙은 다중 세션과 세션 전환: new-session/switch-client/세션 단위 choose-tree 가 없다. 완전히 격리된 별개 작업 공간 여러 벌을 세션 이름으로 오가는 워크플로를 쓰던 사용자에겐 직접적인 대체가 없다(탭으로 풀어야 한다).
  • 세션 단위 격리: 모든 탭이 한 세션 아래 있으므로, 세션 경계로 작업을 칸막이하는 습관은 탭 그룹핑·레이아웃 슬롯으로 우회해야 한다.

대량의 독립 세션을 세션 이름으로 관리하는 헤비 유저라면, 이 단순화가 제약으로 느껴질 수 있다. 그 점은 의도된 트레이드오프다.

얻은 것

  • 단순한 멘탈 모델: 외울 계층이 하나 줄었다(세션 선택 불필요).
  • 단순한 공유: 다중 클라이언트 미러링이 협상 없이 기본 동작이 된다.
  • 단순한 서버: 단일 세션 트리 = 단일 권위 상태라 단일 스레드 asyncio 루프가 다루기 쉽고, 레이아웃 권위가 한 곳에 모인다.
  • 세션 보존 재시작: 살아 있는 셸·실행 중 프로그램·스크롤백을 보존한 채 서버 코드만 제자리에서 교체하는 재시작(restart-server)이, 직렬화·복원 대상이 하나의 세션 트리 라서 더 다루기 쉽다.
  • 원격 페더레이션과의 궁합: 원격 pytmux 의 탭을 로컬 탭바에 흡수하는 페더레이션 (remote-attach)이, "여러 세션 중 어느 것을 가져올지"가 아니라 그 하나의 세션을 탭으로 가져오는 단순한 합성이 된다. 중첩 tmux-in-tmux 대신 탭 흡수로 푼다.

요컨대 pytmux 는 "세션을 여러 개 두는 일반성"을 포기하는 대신, 공유·재시작·페더레이션이 한결 단순해지는 쪽을 택했다. 대상 사용 시나리오에서 세션 다중화가 드물다는 관찰이 이 선택을 정당화한다.

관련 문서

Clone this wiki locally