---
title: "Claude Code의 터미널 화면은 UI가 아니라 Runtime Shell이었다"
slug: "03-session-shell"
canonicalUrl: "https://moonshotnotes.com/posts/03-session-shell/"
sourceUrl: "https://moonshotnotes.com/posts/03-session-shell/"
markdownUrl: "https://moonshotnotes.com/agent/posts/03-session-shell.md"
language: "ko"
category: "AI Agent"
updatedAt: "2026-04-30"
agentTokenEstimate: 1436
---

# Claude Code의 터미널 화면은 UI가 아니라 Runtime Shell이었다

Claude Code CLI 분석을 통해 터미널 화면이 단순 출력 UI가 아니라 메시지, 입력, 승인, 실행 상태를 묶는 runtime shell임을 설명합니다.

## Agent metadata

- Source: https://moonshotnotes.com/posts/03-session-shell/
- Markdown: https://moonshotnotes.com/agent/posts/03-session-shell.md
- Language: ko
- Category: AI Agent
- Tags: Claude Code, AI Agent, Runtime, CLI
- Updated: 2026-04-30
- Estimated tokens: 1436

## 핵심 요약

- 터미널 agent의 화면은 단순 renderer가 아니라 runtime state container에 가깝다.
- 화면에 보이는 메시지, 모델에게 보내는 메시지, transcript에 저장하는 메시지는 다르다.
- 승인 UI, 입력 큐, partial stream, overlay 상태를 한곳에서 조율하되 정책 판단은 분리해야 한다.
- AI 도구를 쓰는 개발자는 화면이 멈춘 것처럼 보일 때 어떤 상태가 대기 중인지 이해할 수 있어야 한다.

이번 글에서는 Claude Code류의 터미널 화면을 UI가 아니라 **runtime shell**로 보겠습니다.

겉으로는 입력창과 메시지 목록이 전부처럼 보입니다. 하지만 제품 수준 agent의 화면은 진행 중인 모델 스트림, tool approval, 입력 큐, local command overlay, 비용 알림, 취소 상태, 세션 복구 정보를 동시에 다룹니다.

이 지점에서 많은 agent 데모가 제품으로 넘어가지 못합니다. 화면에 보이는 상태와 모델에게 전달되는 상태를 같은 리스트로 관리하기 때문입니다.

## 1. 왜 화면이 runtime shell인가

터미널 agent에서 화면은 단순 출력창이 아닙니다. 사용자는 같은 화면에서 질문을 입력하고, 모델의 partial response를 보고, 도구 실행 승인을 결정하고, 취소를 누르고, 다시 새로운 입력을 넣습니다.

즉 화면은 여러 흐름이 만나는 표면입니다.

| 상태 | 화면에는 필요한가 | 모델에는 필요한가 | 기록에는 필요한가 |
|---|---:|---:|---:|
| 사용자 입력 | 예 | 예 | 예 |
| assistant partial text | 예 | 최종 조립 후 필요 | 예, 보통 요약 또는 최종값 |
| 승인 dialog 문구 | 예 | 보통 아니오 | 예, 승인 결과 중심 |
| local command overlay | 예 | 경우에 따라 아니오 | 예 |
| 비용 알림 | 예 | 아니오 | 예 |
| tool result | 예, 요약 형태 | 예, 구조화 형태 | 예 |

화면의 모든 텍스트를 모델에게 보내면 context가 오염됩니다. 반대로 모델에게 필요한 tool result를 화면용 문자열로만 저장하면 다음 추론이 이어지지 않습니다.

## 2. 세 가지 메시지 표현

제품형 agent에서는 같은 사건을 세 가지 방식으로 투영해야 합니다.

1. **UI projection**: 사람이 읽기 좋은 메시지, 접힘/펼침, 진행 표시.
2. **Model projection**: role, content block, tool result, system context.
3. **Ledger projection**: timestamp, turn id, cost, approval, restore metadata.

이 구분을 처음부터 해두면 이후 기능이 쉬워집니다. 예를 들어 tool result는 화면에는 “파일 3개 읽음”으로 보일 수 있지만, 모델에게는 각 파일의 요약 또는 관찰값이 구조화되어 들어가야 합니다. transcript에는 언제 읽었고 어떤 권한으로 읽었는지가 남아야 합니다.

## 3. Session shell의 상태 구조

session shell은 다음 상태를 가질 수 있습니다.

| 상태 그룹 | 예시 |
|---|---|
| conversation state | 사용자 메시지, assistant 메시지, tool result |
| input state | 현재 입력값, paste buffer, attachment, editor selection |
| execution state | active turn, abort signal, pending input queue |
| approval state | tool permission prompt, sandbox confirmation |
| overlay state | 설정 화면, help, cost notice, local command result |
| integration state | remote prompt, bridge event, session restore data |

중요한 것은 shell이 모든 정책을 결정하지 않는다는 점입니다. shell은 상태를 보관하고 사용자 interaction을 받지만, 권한 판단이나 도구 실행 여부는 별도 계층이 결정해야 합니다.

> **실무 팁**
> 화면 계층은 “무엇을 보여줄지”를 담당하고, 정책 계층은 “무엇을 허용할지”를 담당하게 만드세요. 이 둘이 섞이면 headless batch 모드나 remote session을 만들 때 다시 뜯어고치게 됩니다.

## 4. 승인 UI와 정책 엔진 분리

agent가 파일을 쓰거나 shell 명령을 실행하려고 할 때 화면에는 승인 dialog가 떠야 합니다. 하지만 dialog 자체가 “이 작업이 위험한가”를 판단하면 안 됩니다.

정책 엔진이 먼저 판단합니다.

```text
# 읽는 법: 아래 항목은 동작 흐름을 빠르게 확인하기 위한 요약 예시입니다.
도구 요청
→ policy engine: 허용 / 거부 / 질문 필요
→ session shell: 질문 필요 상태를 UI로 표시
→ 사용자 선택
→ policy decision 기록
→ tool runtime 실행 또는 거절 결과 생성
```

이 구조는 테스트에도 좋습니다. UI 없이도 정책 엔진을 테스트할 수 있고, interactive shell이 아닌 batch 모드에서는 “질문 필요” 상태를 자동 거절하거나 사전 정책으로 처리할 수 있습니다.

## 5. 개념 코드로 보는 shell 설계

아래 코드는 새로 작성한 개념 코드입니다.

```python
# 읽는 법: 실제 구현 복제가 아니라 runtime 경계를 설명하는 개념 코드입니다.
class SessionShell:
    # 객체가 이후 단계에서 참조할 runtime 의존성과 상태 저장소를 초기화합니다.
    def __init__(self):
        self.visible_timeline = []
        self.model_history = []
        self.ledger_buffer = []
        self.input_buffer = ""
        self.pending_inputs = []
        self.approval_stack = []
        self.current_overlay = None
        self.active_turn_id = None

    # 하나의 runtime event를 화면용, 모델용, 기록용 표현으로 각각 라우팅합니다.
    def publish(self, event):
        if event.show_to_user:
            self.visible_timeline.append(render_for_screen(event))

        if event.send_to_model:
            self.model_history.append(render_for_model(event))

        if event.recordable:
            self.ledger_buffer.append(render_for_ledger(event))

    # 도구 실행 전 사용자 승인이 필요한 요청을 overlay stack에 올립니다.
    def open_approval(self, approval_request):
        self.approval_stack.append(approval_request)
        self.current_overlay = "approval"

    # 승인 결과를 event로 기록하고 다음 overlay 상태를 복구합니다.
    def close_approval(self, decision):
        request = self.approval_stack.pop()
        self.publish(make_approval_event(request, decision))
        self.current_overlay = "approval" if self.approval_stack else None
```

핵심은 `publish()`입니다. runtime event 하나가 UI, model, ledger에 서로 다른 방식으로 반영됩니다. 이 구조가 없으면 나중에 “사용자에게는 보여야 하지만 모델에게는 보여주면 안 되는 것”을 처리하기 어렵습니다.

## 6. AI 활용 개발자가 읽어야 할 신호

AI 도구를 사용하는 개발자는 화면을 보면서 runtime 상태를 추론할 수 있어야 합니다.

| 화면 신호 | 가능한 runtime 상태 |
|---|---|
| 답변이 stream 중인데 새 입력이 대기 표시됨 | active turn이 있고 input queue에 들어감 |
| 승인창이 떠 있고 답변이 멈춤 | tool runtime이 permission decision을 기다림 |
| local command 결과만 나오고 모델 답변이 없음 | 입력이 local-only action으로 처리됨 |
| 비용 또는 토큰 경고가 표시됨 | ledger/accounting이 budget threshold에 접근함 |
| 취소 후 “결과 없음”이 아니라 실패 결과가 표시됨 | tool request/result pair를 맞추기 위한 중단 result 생성 |

이런 신호가 명확한 도구일수록 실무에서 신뢰하기 쉽습니다.

## 7. Agent 개발자가 피해야 할 실수

첫 번째 실수는 UI message list를 model history로 그대로 쓰는 것입니다. 이러면 비용 알림, help 문구, 승인 dialog 설명까지 모델 context에 섞일 수 있습니다.

두 번째 실수는 partial stream을 final message처럼 저장하는 것입니다. streaming text는 중간 상태입니다. 최종 응답이 닫힌 뒤 조립된 assistant message와 구분해야 합니다.

세 번째 실수는 overlay가 queue processing을 영구적으로 막게 만드는 것입니다. 설정 화면이 열려 있어도 background event나 취소 신호는 처리되어야 합니다.

## 8. 실전 체크리스트

```text
# 읽는 법: 아래 항목은 동작 흐름을 빠르게 확인하기 위한 요약 예시입니다.
Session Shell 체크리스트

[ ] UI timeline과 model history가 분리되어 있다.
[ ] ledger 기록은 UI/model projection과 별도로 만들어진다.
[ ] partial stream과 final assistant message가 구분된다.
[ ] approval UI는 정책 판단을 직접 하지 않는다.
[ ] pending input queue와 approval queue가 독립적으로 관리된다.
[ ] overlay가 열려 있어도 cancel/background event를 처리할 수 있다.
[ ] remote input과 local input이 같은 submit boundary로 들어간다.
```

## 마무리

Claude Code류의 terminal UI를 runtime shell로 보면, AI agent 개발의 난이도가 더 선명해집니다. 화면은 단순히 예쁘게 보여주는 곳이 아니라, 여러 runtime 상태가 충돌하지 않도록 투영하는 곳입니다.

다음 글에서는 사용자가 Enter를 누른 순간을 다룹니다. 한 줄 prompt는 곧바로 모델 호출이 아니라, submit boundary를 통과해 하나의 agent turn이 됩니다.
