# 세션 다시 시작 및 지속성

이 가이드에서는 SDK의 세션 지속성 기능(작업을 일시 중지하고, 나중에 다시 시작하고, 프로덕션 환경에서 세션을 관리하는 방법)을 안내합니다.

<!-- markdownlint-disable GHD046 GHD005 -->

<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->

## 세션 작동 방식

세션을 만들 때 Copilot CLI는 대화 기록, 도구 상태 및 계획 컨텍스트를 유지 관리합니다. 기본적으로 이 상태는 메모리에 있으며 세션이 종료되면 사라집니다. 지속성을 사용하도록 설정하면 다시 시작, 컨테이너 마이그레이션 또는 다른 클라이언트 인스턴스 간에 세션을 다시 시작할 수 있습니다.

![다이어그램: 설명된 프로세스를 보여 주는 순서도입니다.](/assets/images/help/copilot/copilot-sdk/features-session-persistence-diagram-0.png)

| 상태              | 어떻게 되나요?            |
| --------------- | ------------------- |
| **창조하다**        |                     |
| `session_id` 할당 |                     |
| **Active**      | 프롬프트 보내기, 도구 호출, 응답 |
| **일시 중지된**      | 디스크에 저장된 상태         |
| **다시 시작**       | 디스크에서 로드된 상태        |

## 빠른 시작: 다시 시작 가능한 세션 만들기

재개 가능한 세션의 핵심은 자체 `session_id`를 제공하는 것입니다. 이 ID가 없으면 SDK는 임의 ID를 생성하고 세션은 나중에 다시 시작될 수 없습니다.

### TypeScript

```typescript
import { CopilotClient } from "@github/copilot-sdk";

const client = new CopilotClient();

// Create a session with a meaningful ID
const session = await client.createSession({
  sessionId: "user-123-task-456",
  model: "gpt-5.2-codex",
});

// Do some work...
await session.sendAndWait({ prompt: "Analyze my codebase" });

// Session state is automatically persisted
// You can safely close the client
```

### Python

```python
from copilot import CopilotClient
from copilot.session import PermissionHandler

client = CopilotClient()
await client.start()

# Create a session with a meaningful ID
session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-5.2-codex", session_id="user-123-task-456")

# Do some work...
await session.send_and_wait("Analyze my codebase")

# Session state is automatically persisted
```

### Go

```golang
ctx := context.Background()
client := copilot.NewClient(nil)

// Create a session with a meaningful ID
session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
    SessionID: "user-123-task-456",
    Model:     "gpt-5.2-codex",
})

// Do some work...
session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "Analyze my codebase"})

// Session state is automatically persisted
```

### C#(.NET)

```csharp
using GitHub.Copilot;

var client = new CopilotClient();

// Create a session with a meaningful ID
var session = await client.CreateSessionAsync(new SessionConfig
{
    SessionId = "user-123-task-456",
    Model = "gpt-5.2-codex",
});

// Do some work...
await session.SendAndWaitAsync(new MessageOptions { Prompt = "Analyze my codebase" });

// Session state is automatically persisted
```

## 세션 다시 열기

나중에(분, 시간 또는 며칠) 중단된 위치에서 세션을 다시 시작할 수 있습니다.

![다이어그램: 설명된 프로세스를 보여 주는 순서도입니다.](/assets/images/help/copilot/copilot-sdk/features-session-persistence-diagram-1.png)

### TypeScript

```typescript
// Resume from a different client instance (or after restart)
const session = await client.resumeSession("user-123-task-456");

// Continue where you left off
await session.sendAndWait({ prompt: "What did we discuss earlier?" });
```

### Python

```python
# Resume from a different client instance (or after restart)
session = await client.resume_session("user-123-task-456", on_permission_request=PermissionHandler.approve_all)

# Continue where you left off
await session.send_and_wait("What did we discuss earlier?")
```

### Go

```golang
ctx := context.Background()

// Resume from a different client instance (or after restart)
session, _ := client.ResumeSession(ctx, "user-123-task-456", nil)

// Continue where you left off
session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "What did we discuss earlier?"})
```

### C#(.NET)

```csharp
// Resume from a different client instance (or after restart)
var session = await client.ResumeSessionAsync("user-123-task-456");

// Continue where you left off
await session.SendAndWaitAsync(new MessageOptions { Prompt = "What did we discuss earlier?" });
```

## 다시 시작 옵션

세션을 다시 시작하면 필요에 따라 여러 설정을 다시 구성할 수 있습니다. 이 기능은 모델을 변경하거나, 도구 구성을 업데이트하거나, 동작을 수정해야 하는 경우에 유용합니다.

| Option             | Description                   |
| ------------------ | ----------------------------- |
| `model`            | 다시 시작된 세션의 모델 변경              |
| `systemMessage`    | 시스템 프롬프트 재정의 또는 확장            |
| `availableTools`   | 사용할 수 있는 도구 제한                |
| `excludedTools`    | 특정 도구 사용 안 함                  |
| `provider`         | BYOK 자격 증명 다시 제공(BYOK 세션에 필요) |
| `reasoningEffort`  | 추론 노력 수준 조정                   |
| `streaming`        | 스트리밍 응답 사용/사용 안 함             |
| `workingDirectory` | 작업 디렉터리 변경                    |
| `configDir`        | 구성 디렉터리를 재정의                  |
| `mcpServers`       | MCP 서버 구성                     |
| `customAgents`     | 사용자 지정 에이전트 구성                |
| `agent`            | 이름으로 사용자 지정 에이전트 사전 선택        |
| `skillDirectories` | 기술을 로드하기 위한 디렉터리              |
| `disabledSkills`   | 사용하지 않도록 설정하는 기술              |
| `infiniteSessions` | 무한 세션 동작 구성                   |

### 예: 다시 시작할 때 모델 변경

```typescript
// Resume with a different model
const session = await client.resumeSession("user-123-task-456", {
  model: "claude-sonnet-4",  // Switch to a different model
  reasoningEffort: "high",   // Increase reasoning effort
});
```

## 다시 시작된 세션과 함께 BYOK(사용자 고유의 키 가져오기) 사용

사용자 고유의 API 키를 사용하는 경우 다시 시작 시 공급자 구성을 다시 제공해야 합니다. API 키는 보안상의 이유로 디스크에 유지되지 않습니다.

```typescript
// Original session with BYOK
const session = await client.createSession({
  sessionId: "user-123-task-456",
  model: "gpt-5.2-codex",
  provider: {
    type: "azure",
    endpoint: "https://my-resource.openai.azure.com",
    apiKey: process.env.AZURE_OPENAI_KEY,
    deploymentId: "my-gpt-deployment",
  },
});

// When resuming, you MUST re-provide the provider config
const resumed = await client.resumeSession("user-123-task-456", {
  provider: {
    type: "azure",
    endpoint: "https://my-resource.openai.azure.com",
    apiKey: process.env.AZURE_OPENAI_KEY,  // Required again
    deploymentId: "my-gpt-deployment",
  },
});
```

## 지속되는 항목은?

세션 상태는 다음으로 저장됩니다 `~/.copilot/session-state/{sessionId}/`.

```text
~/.copilot/session-state/
└── user-123-task-456/
    ├── checkpoints/           # Conversation history snapshots
    │   ├── 001.json          # Initial state
    │   ├── 002.json          # After first interaction
    │   └── ...               # Incremental checkpoints
    ├── plan.md               # Agent's planning state (if any)
    └── files/                # Session artifacts
        ├── analysis.md       # Files the agent created
        └── notes.txt         # Working documents
```

| 데이터              | 보존되었습니까?         | 비고 |
| ---------------- | ---------------- | -- |
| 대화 기록            |                  |    |
| ✅ 예              | 전체 메시지 스레드       |    |
| 도구 호출 결과         |                  |    |
| ✅ 예              | 컨텍스트용으로 캐시됨      |    |
| 에이전트 계획 상태       |                  |    |
| ✅ 예              |                  |    |
| `plan.md` 파일     |                  |    |
| 세션 아티팩트          |                  |    |
| ✅ 예              |                  |    |
| `files/` 디렉터리 안에 |                  |    |
| 공급자/API 키        |                  |    |
| ❌ 아니요            | 보안: 다시 제공해야 합니다. |    |
| 메모리 내 도구 상태      |                  |    |
| ❌ 아니요            | 도구는 무상태여야 합니다.   |    |

## 세션 ID 모범 사례

소유권 및 용도를 인코딩하는 세션 ID를 선택합니다. 이렇게 하면 감사 및 정리가 훨씬 쉬워집니다.

| Pattern                         | Example            | 사용 사례 |
| ------------------------------- | ------------------ | ----- |
| ❌                               |                    |       |
| `abc123`                        |                    |       |
| 임의 ID                           | 감사하기 어렵고 소유권 정보 없음 |       |
| ✅                               |                    |       |
| `user-{userId}-{taskId}`        |                    |       |
| `user-alice-pr-review-42`       | 다중 사용자 앱           |       |
| ✅                               |                    |       |
| `tenant-{tenantId}-{workflow}`  |                    |       |
| `tenant-acme-onboarding`        | 다중 사용자 SaaS        |       |
| ✅                               |                    |       |
| `{userId}-{taskId}-{timestamp}` |                    |       |
| `alice-deploy-1706932800`       | 시간 기반 정리 작업        |       |

**구조적 ID의 이점:**

* 감사가 용이합니다: "사용자 alice의 모든 세션 표시"
* 정리하기 쉽습니다. "X보다 오래된 모든 세션 삭제"
* 자연 액세스 제어: 세션 ID에서 사용자 ID 추출

### 예: 세션 ID 생성

```typescript
function createSessionId(userId: string, taskType: string): string {
  const timestamp = Date.now();
  return `${userId}-${taskType}-${timestamp}`;
}

const sessionId = createSessionId("alice", "code-review");
// → "alice-code-review-1706932800000"
```

```python
import time

def create_session_id(user_id: str, task_type: str) -> str:
    timestamp = int(time.time())
    return f"{user_id}-{task_type}-{timestamp}"

session_id = create_session_id("alice", "code-review")
# → "alice-code-review-1706932800"
```

## 세션 수명 주기 관리

### 활성 세션 나열

```typescript
// List all sessions
const sessions = await client.listSessions();
console.log(`Found ${sessions.length} sessions`);

for (const session of sessions) {
  console.log(`- ${session.sessionId} (created: ${session.createdAt})`);
}

// Filter sessions by repository
const repoSessions = await client.listSessions({ repository: "owner/repo" });
```

### 이전 세션 정리

```typescript
async function cleanupExpiredSessions(maxAgeMs: number) {
  const sessions = await client.listSessions();
  const now = Date.now();
  
  for (const session of sessions) {
    const age = now - new Date(session.createdAt).getTime();
    if (age > maxAgeMs) {
      await client.deleteSession(session.sessionId);
      console.log(`Deleted expired session: ${session.sessionId}`);
    }
  }
}

// Clean up sessions older than 24 hours
await cleanupExpiredSessions(24 * 60 * 60 * 1000);
```

### 세션에서 연결 끊기(`disconnect`)

작업이 완료되면 시간 제한을 기다리지 않고 세션에서 명시적으로 연결을 끊습니다. 이렇게 하면 메모리 내 리소스가 해제되지만 **디스크에 세션 데이터가 유지**되므로 나중에 세션을 다시 시작해도 됩니다.

```typescript
try {
  // Do work...
  await session.sendAndWait({ prompt: "Complete the task" });
  
  // Task complete — release in-memory resources (session can be resumed later)
  await session.disconnect();
} catch (error) {
  // Clean up even on error
  await session.disconnect();
  throw error;
}
```

또한 각 SDK는 해당 언어에 맞는 자동 정리 패턴도 제공합니다:

| 언어                    | Pattern                                                                             | Example                                                              |
| --------------------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| **TypeScript**        | `Symbol.asyncDispose`                                                               | `await using session = await client.createSession(config);`          |
| **Python**            |                                                                                     |                                                                      |
| `async with` 컨텍스트 관리자 | `async with await client.create_session(on_permission_request=handler) as session:` |                                                                      |
| **C#**                | `IAsyncDisposable`                                                                  | `await using var session = await client.CreateSessionAsync(config);` |
| **Go**                | `defer`                                                                             | `defer session.Disconnect()`                                         |

> \[!NOTE]
> `destroy()`은 더 이상 사용되지 않으며, 대신 `disconnect()` 사용이 권장됩니다. 사용하는 `destroy()` 기존 코드는 계속 작동하지만 마이그레이션해야 합니다.

### 세션 영구 삭제(`deleteSession`)

디스크에서 세션과 그 모든 데이터(대화 기록, 계획 상태, 아티팩트)를 영구적으로 제거하려면 `deleteSession`를 사용하십시오. 이는 되돌릴 수 없습니다. 삭제 후 세션을 다시 시작 **하지 못할 수 있습니다** .

```typescript
// Permanently remove session data
await client.deleteSession("user-123-task-456");
```

> **`disconnect()` vs `deleteSession()`:**`disconnect()` 메모리 내 리소스를 해제하지만 나중에 다시 시작하려면 세션 데이터를 디스크에 유지합니다. `deleteSession()` 는 디스크의 파일을 포함하여 모든 항목을 영구적으로 제거합니다.

## 자동 정리: 유휴 시간 제한

기본적으로 세션은 **유휴 시간 제한이 없으며** 명시적으로 연결이 끊어지거나 삭제될 때까지 무기한으로 라이브됩니다. 필요에 따라 다음을 통해 `CopilotClientOptions.sessionIdleTimeoutSeconds`서버 전체 유휴 시간 제한을 구성할 수 있습니다.

```typescript
const client = new CopilotClient({
  sessionIdleTimeoutSeconds: 30 * 60, // 30 minutes
});
```

시간 제한이 구성되면 해당 기간 동안 활동이 없는 세션이 자동으로 정리됩니다. 비활성화하려면 `0`로 설정하거나 생략합니다.

> \[!NOTE]
> 이 옵션은 SDK가 런타임 프로세스를 생성하는 경우에만 적용됩니다. 를 통해 `cliUrl`기존 서버에 연결할 때 서버의 자체 시간 제한 구성이 적용됩니다.

![다이어그램: 설명된 프로세스를 보여 주는 순서도입니다.](/assets/images/help/copilot/copilot-sdk/features-session-persistence-diagram-2.png)

활성 작업(실행 중인 명령, 백그라운드 에이전트)이 있는 세션은 제한 시간 설정에 관계없이 항상 유휴 정리로부터 보호됩니다.

유휴 이벤트를 수신 대기하여 세션 비활성 상태에 반응합니다.

```typescript
session.on("session.idle", (event) => {
  console.log(`Session idle for ${event.idleDurationMs}ms`);
});
```

## 배포 패턴

### 패턴 1: 사용자당 하나의 CLI 서버(권장)

적합 대상: 강력한 격리, 다중 테넌트 환경, Azure 동적 세션.

![다이어그램: 설명된 프로세스를 보여 주는 순서도입니다.](/assets/images/help/copilot/copilot-sdk/features-session-persistence-diagram-3.png)

\*\*혜택:\*\*✅ 완전한 격리 | ✅ 단순 보안 | ✅ 간편한 크기 조정

### 패턴 2: 공유 CLI 서버(리소스 효율적)

적합 대상: 내부 도구, 신뢰할 수 있는 환경, 리소스 제한 설정.

![다이어그램: 설명된 프로세스를 보여 주는 순서도입니다.](/assets/images/help/copilot/copilot-sdk/features-session-persistence-diagram-4.png)

**Requirements:**

* ⚠️ 사용자당 고유한 세션 ID
* ⚠️ 애플리케이션 수준 액세스 제어
* ⚠️ 작업 전 세션 ID 유효성 검사

```typescript
// Application-level access control for shared CLI
async function resumeSessionWithAuth(
  client: CopilotClient,
  sessionId: string,
  currentUserId: string
): Promise<Session> {
  // Parse user from session ID
  const [sessionUserId] = sessionId.split("-");
  
  if (sessionUserId !== currentUserId) {
    throw new Error("Access denied: session belongs to another user");
  }
  
  return client.resumeSession(sessionId);
}
```

## Azure 동적 세션

컨테이너를 다시 시작하거나 마이그레이션할 수 있는 서버리스/컨테이너 배포의 경우:

### 영구 스토리지 탑재

세션 상태 디렉터리를 영구 스토리지에 탑재해야 합니다.

```yaml
# Azure Container Instance example
containers:
  - name: copilot-agent
    image: my-agent:latest
    volumeMounts:
      - name: session-storage
        mountPath: /home/app/.copilot/session-state

volumes:
  - name: session-storage
    azureFile:
      shareName: copilot-sessions
      storageAccountName: myaccount
```

![다이어그램: 설명된 프로세스를 보여 주는 순서도입니다.](/assets/images/help/copilot/copilot-sdk/features-session-persistence-diagram-5.png)

**컨테이너를 다시 시작해도 세션이 유지됩니다!**

## 장기 실행 워크플로를 위한 무한 세션

컨텍스트 제한을 초과할 수 있는 워크플로의 경우 자동 압축을 사용하여 무한 세션을 사용하도록 설정합니다.

```typescript
const session = await client.createSession({
  sessionId: "long-workflow-123",
  infiniteSessions: {
    enabled: true,
    backgroundCompactionThreshold: 0.80,  // Start compaction at 80% context
    bufferExhaustionThreshold: 0.95,      // Block at 95% if needed
  },
});
```

> \[!NOTE]
> 임계값은 절대 토큰 수가 아닌 컨텍스트 사용률(0.0-1.0)입니다. 자세한 내용은 [SDK 및 CLI 호환성](/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/troubleshooting/compatibility) 을 참조하세요.

## 제한 사항 및 고려 사항

| Limitation                               | Description                    | 완화 방법                                |
| ---------------------------------------- | ------------------------------ | ------------------------------------ |
| **BYOK 다시 인증**                           | API 키는 유지되지 않습니다.              | 비밀 관리자에 키를 저장합니다. 이력서에 제공            |
| **쓰기 가능한 스토리지**                          |                                |                                      |
| `~/.copilot/session-state/` 쓰기 가능해야 합니다. | 컨테이너에 영구 볼륨 탑재                 |                                      |
| **세션 잠금 없음**                             | 동일한 세션에 대한 동시 액세스가 정의되지 않았습니다. | 애플리케이션 레벨 잠금 또는 큐 시스템 구현             |
| **도구 상태가 유지되지 않음**                       | 메모리 내 도구 상태가 손실됨               | 상태 비저장이 되거나, 자체 상태를 유지하도록 도구를 설계하세요. |

### 동시 액세스 처리

SDK는 기본 제공 세션 잠금을 제공하지 않습니다. 여러 클라이언트가 동일한 세션에 액세스할 수 있는 경우:

```typescript
// Option 1: Application-level locking with Redis
import Redis from "ioredis";

const redis = new Redis();

async function withSessionLock<T>(
  sessionId: string,
  fn: () => Promise<T>
): Promise<T> {
  const lockKey = `session-lock:${sessionId}`;
  const acquired = await redis.set(lockKey, "locked", "NX", "EX", 300);
  
  if (!acquired) {
    throw new Error("Session is in use by another client");
  }
  
  try {
    return await fn();
  } finally {
    await redis.del(lockKey);
  }
}

// Usage
await withSessionLock("user-123-task-456", async () => {
  const session = await client.resumeSession("user-123-task-456");
  await session.sendAndWait({ prompt: "Continue the task" });
});
```

## 요약

| 특징                                                                                | 사용 방법                                   |
| --------------------------------------------------------------------------------- | --------------------------------------- |
| **다시 열 수 있는 세션 만들기**                                                              | 사용자 고유의 것을 제공하십시오 `sessionId`           |
| **세션 다시 시작**                                                                      | `client.resumeSession(sessionId)`       |
| **BYOK 다시 시작**                                                                    |                                         |
| `provider` 구성 다시 제공                                                               |                                         |
| **세션 목록**                                                                         | `client.listSessions(filter?)`          |
| **활성 세션에서 연결 끊기**                                                                 |                                         |
| `session.disconnect()`- 메모리 내 리소스를 해제합니다. 디스크의 세션 데이터는 재개를 위해 유지됩니다.              |                                         |
| **세션을 영구적으로 삭제**                                                                  |                                         |
| `client.deleteSession(sessionId)`- 디스크에서 모든 세션 데이터를 영구적으로 제거합니다. 을(를) 다시 시작하지 못 함 |                                         |
| **컨테이너화된 배포**                                                                     | 영구 스토리지에 탑재 `~/.copilot/session-state/` |

## 다음 단계

* [세션 후크](/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/hooks-overview) - 후크를 사용하여 세션 동작 사용자 지정
* [SDK 및 CLI 호환성](/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/troubleshooting/compatibility) - SDK 및 CLI 기능 비교
* [디버깅 가이드](/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/troubleshooting/debugging) - 세션 문제 해결