---
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: 1760
---

# 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: 1760

## 핵심 요약

- 터미널 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이 됩니다.

## 독자 적용 노트

이 글은 AI agent나 코딩 도구를 실제 개발 업무에 적용하려는 개발자가 `Claude Code의 터미널 화면은 UI가 아니라 Runtime Shell이었다`이라는 주제를 단순 개념 설명이 아니라 AI 도구를 실제 작업 흐름에 붙일 때의 실행 경계로 점검하도록 작성했습니다. 본문에서 다룬 구조는 새 도구를 도입하거나 기존 워크플로우를 바꿀 때 "무엇을 먼저 확인해야 하는가"를 정하는 데 쓰는 것이 좋습니다.

### 원본 판단 근거

이 글의 판단은 1. 왜 화면이 runtime shell인가에서 설명한 구조와 본문 안의 예시, 표, 체크리스트를 함께 놓고 정리한 것입니다. 특히 독자가 그대로 복사할 수 있는 결론보다, 자신의 코드베이스나 팀 규칙에 맞춰 확인해야 할 경계와 실패 조건을 분리하는 데 초점을 둡니다.

### 실패 사례 또는 edge case

가장 흔한 실패는 AI 도구를 실제 작업 흐름에 붙일 때의 실행 경계를 문서나 명칭만 맞춘 상태로 끝내는 것입니다. 예를 들어 실제 입력, 권한, 로그, 배포, 검증 기준이 연결되지 않으면 겉으로는 도입이 끝난 것처럼 보여도 다음 작업에서 같은 문제가 반복됩니다. 따라서 본문을 적용할 때는 "누가 실행하는가", "무엇을 기록하는가", "실패하면 어디서 멈추는가"를 같이 확인해야 합니다.

### 실무 체크리스트

- [ ] 이 글의 핵심 개념을 현재 프로젝트의 실제 파일, API, 로그, 문서 중 하나에 연결했다.
- [ ] 본문 예시를 그대로 쓰지 않고 우리 환경의 제약 조건으로 다시 검토했다.
- [ ] 실패 사례나 edge case를 하나 이상 정하고, 재현 또는 확인 방법을 적었다.
- [ ] 적용 후 바뀌어야 할 완료 기준과 검증 명령을 분리했다.

### Q&A

#### Q1. 이 글의 내용을 바로 표준으로 써도 되나요?

바로 표준으로 고정하기보다 작은 작업 하나에 먼저 적용해 보는 편이 안전합니다. 실제 로그, 리뷰, 테스트에서 같은 판단이 반복해서 맞을 때 팀 표준으로 올리는 것이 좋습니다.

#### Q2. 본문 예시와 내 프로젝트가 다르면 어떻게 해야 하나요?

예시의 이름보다 경계를 보세요. 입력, 상태, 권한, 검증, 기록 중 어떤 경계를 다루는 글인지 먼저 찾고, 그 경계를 현재 프로젝트의 구조에 맞게 옮기면 됩니다.

### 참고자료와 불확실성

이 보강 노트는 본문에 이미 포함된 참고자료와 작성 시점의 공개 문서를 기준으로 합니다. 도구 버전, API 정책, 제품 UI는 바뀔 수 있으므로 실제 적용 전에는 공식 문서와 현재 런타임 동작을 다시 확인해야 합니다.
