{"meta":{"title":"세션 수명 주기 후크","intro":"세션 수명 주기 후크를 사용하면 세션 시작 및 종료 이벤트에 응답할 수 있습니다. 이를 사용하여 다음을 수행합니다.","product":"GitHub Copilot","breadcrumbs":[{"href":"/ko/enterprise-cloud@latest/copilot","title":"GitHub Copilot"},{"href":"/ko/enterprise-cloud@latest/copilot/how-tos","title":"방법"},{"href":"/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk","title":"코필로트 SDK"},{"href":"/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks","title":"후크 사용"},{"href":"/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/session-lifecycle","title":"세션 수명 주기"}],"documentType":"article"},"body":"# 세션 수명 주기 후크\n\n세션 수명 주기 후크를 사용하면 세션 시작 및 종료 이벤트에 응답할 수 있습니다. 이를 사용하여 다음을 수행합니다.\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n* 세션이 시작될 때 컨텍스트 초기화\n* 세션이 종료되면 리소스 정리\n* 세션 메트릭 및 분석 추적\n* 동적으로 세션 동작 구성\n\n## 세션 시작 후크 {#session-start}\n\n세션이 `onSessionStart` 시작될 때 후크가 호출됩니다(신규 또는 다시 시작).\n\n### 후크 서명\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\ntype SessionStartHandler = (\n  input: SessionStartHookInput,\n  invocation: HookInvocation\n) => Promise<SessionStartHookOutput | null | undefined>;\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nSessionStartHandler = Callable[\n    [SessionStartHookInput, dict[str, str]],\n    Awaitable[SessionStartHookOutput | None]\n]\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\ntype SessionStartHandler func(\n    input SessionStartHookInput,\n    invocation HookInvocation,\n) (*SessionStartHookOutput, error)\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\npublic delegate Task<SessionStartHookOutput?> SessionStartHandler(\n    SessionStartHookInput input,\n    HookInvocation invocation);\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n```java\n@FunctionalInterface\npublic interface SessionStartHandler {\n    CompletableFuture<SessionStartHookOutput> handle(\n        SessionStartHookInput input,\n        HookInvocation invocation);\n}\n```\n\n</div>\n\n</div>\n\n### 입력\n\n| Field           | Type           | 설명                     |\n| --------------- | -------------- | ---------------------- |\n| `timestamp`     | number         | 후크가 트리거될 때의 Unix 타임스탬프 |\n| `cwd`           | string         | 현재 작업 디렉터리             |\n| `source`        |                |                        |\n| `\"startup\"`     |                |                        |\n| \\|              |                |                        |\n| `\"resume\"`      |                |                        |\n| \\|              |                |                        |\n| `\"new\"`         |                |                        |\n| 세션이 시작된 방법      |                |                        |\n| `initialPrompt` | 정의되지 않은 문자열 \\| | 제공된 경우 초기 프롬프트         |\n\n### 출력\n\n| Field               | Type   | 설명               |\n| ------------------- | ------ | ---------------- |\n| `additionalContext` | string | 세션 시작 시 추가할 컨텍스트 |\n| `modifiedConfig`    | object | 세션 구성 재정의        |\n\n### 예제\n\n#### 시작 시 프로젝트 컨텍스트 추가\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onSessionStart: async (input, invocation) => {\n      console.log(`Session ${invocation.sessionId} started (${input.source})`);\n      \n      const projectInfo = await detectProjectType(input.cwd);\n      \n      return {\n        additionalContext: `\nThis is a ${projectInfo.type} project.\nMain language: ${projectInfo.language}\nPackage manager: ${projectInfo.packageManager}\n        `.trim(),\n      };\n    },\n  },\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nfrom copilot.session import PermissionHandler\n\nasync def on_session_start(input_data, invocation):\n    print(f\"Session {invocation['session_id']} started ({input_data['source']})\")\n    \n    project_info = await detect_project_type(input_data[\"cwd\"])\n    \n    return {\n        \"additionalContext\": f\"\"\"\nThis is a {project_info['type']} project.\nMain language: {project_info['language']}\nPackage manager: {project_info['packageManager']}\n        \"\"\".strip()\n    }\n\nsession = await client.create_session(on_permission_request=PermissionHandler.approve_all, hooks={\"on_session_start\": on_session_start})\n```\n\n</div>\n\n</div>\n\n#### 세션 재시작 처리\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onSessionStart: async (input, invocation) => {\n      if (input.source === \"resume\") {\n        // Load previous session state\n        const previousState = await loadSessionState(invocation.sessionId);\n        \n        return {\n          additionalContext: `\nSession resumed. Previous context:\n- Last topic: ${previousState.lastTopic}\n- Open files: ${previousState.openFiles.join(\", \")}\n          `.trim(),\n        };\n      }\n      return null;\n    },\n  },\n});\n```\n\n#### 사용자 기본 설정 로드\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onSessionStart: async () => {\n      const preferences = await loadUserPreferences();\n      \n      const contextParts = [];\n      \n      if (preferences.language) {\n        contextParts.push(`Preferred language: ${preferences.language}`);\n      }\n      if (preferences.codeStyle) {\n        contextParts.push(`Code style: ${preferences.codeStyle}`);\n      }\n      if (preferences.verbosity === \"concise\") {\n        contextParts.push(\"Keep responses brief and to the point.\");\n      }\n      \n      return {\n        additionalContext: contextParts.join(\"\\n\"),\n      };\n    },\n  },\n});\n```\n\n## 세션 종료 후크 {#session-end}\n\n세션이 `onSessionEnd` 종료되면 후크가 호출됩니다.\n\n### 후크 서명\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\ntype SessionEndHandler = (\n  input: SessionEndHookInput,\n  invocation: HookInvocation\n) => Promise<SessionEndHookOutput | null | undefined>;\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nSessionEndHandler = Callable[\n    [SessionEndHookInput, dict[str, str]],\n    Awaitable[SessionEndHookOutput | None]\n]\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\ntype SessionEndHandler func(\n    input SessionEndHookInput,\n    invocation HookInvocation,\n) (*SessionEndHookOutput, error)\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\npublic delegate Task<SessionEndHookOutput?> SessionEndHandler(\n    SessionEndHookInput input,\n    HookInvocation invocation);\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n```java\n@FunctionalInterface\npublic interface SessionEndHandler {\n    CompletableFuture<SessionEndHookOutput> handle(\n        SessionEndHookInput input,\n        HookInvocation invocation);\n}\n```\n\n</div>\n\n</div>\n\n### 입력\n\n| Field          | Type           | 설명                       |\n| -------------- | -------------- | ------------------------ |\n| `timestamp`    | number         | 후크가 트리거될 때의 Unix 타임스탬프   |\n| `cwd`          | string         | 현재 작업 디렉터리               |\n| `reason`       | string         | 세션이 종료된 이유(아래 참조)        |\n| `finalMessage` | 정의되지 않은 문자열 \\| | 세션의 마지막 메시지              |\n| `error`        | 정의되지 않은 문자열 \\| | 오류로 인해 세션이 종료된 경우 오류 메시지 |\n\n#### 종료 이유\n\n| Reason        | 설명                         |\n| ------------- | -------------------------- |\n| `\"complete\"`  | 세션이 정상적으로 완료됨              |\n| `\"error\"`     | 오류로 인해 세션이 종료됨             |\n| `\"abort\"`     | 사용자 또는 코드에 의해 세션이 중단되었습니다. |\n| `\"timeout\"`   | 세션 시간이 초과됨                 |\n| `\"user_exit\"` | 사용자가 세션을 명시적으로 종료했습니다.     |\n\n### 출력\n\n| Field            | Type      | 설명              |\n| ---------------- | --------- | --------------- |\n| `suppressOutput` | boolean   | 최종 세션 출력 표시 안 함 |\n| `cleanupActions` | string\\[] | 수행할 정리 작업 목록    |\n| `sessionSummary` | string    | 로깅/분석을 위한 세션 요약 |\n\n### 예제\n\n#### 세션 메트릭 추적\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nconst sessionStartTimes = new Map<string, number>();\n\nconst session = await client.createSession({\n  hooks: {\n    onSessionStart: async (input, invocation) => {\n      sessionStartTimes.set(invocation.sessionId, input.timestamp);\n      return null;\n    },\n    onSessionEnd: async (input, invocation) => {\n      const startTime = sessionStartTimes.get(invocation.sessionId);\n      const duration = startTime ? input.timestamp - startTime : 0;\n      \n      await recordMetrics({\n        sessionId: invocation.sessionId,\n        duration,\n        endReason: input.reason,\n      });\n      \n      sessionStartTimes.delete(invocation.sessionId);\n      return null;\n    },\n  },\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nfrom copilot.session import PermissionHandler\n\nsession_start_times = {}\n\nasync def on_session_start(input_data, invocation):\n    session_start_times[invocation[\"session_id\"]] = input_data[\"timestamp\"]\n    return None\n\nasync def on_session_end(input_data, invocation):\n    start_time = session_start_times.get(invocation[\"session_id\"])\n    duration = input_data[\"timestamp\"] - start_time if start_time else 0\n    \n    await record_metrics({\n        \"session_id\": invocation[\"session_id\"],\n        \"duration\": duration,\n        \"end_reason\": input_data[\"reason\"],\n    })\n    \n    session_start_times.pop(invocation[\"session_id\"], None)\n    return None\n\nsession = await client.create_session(on_permission_request=PermissionHandler.approve_all, hooks={\n        \"on_session_start\": on_session_start,\n        \"on_session_end\": on_session_end,\n    })\n```\n\n</div>\n\n</div>\n\n#### 자원을 정리하세요\n\n```typescript\nconst sessionResources = new Map<string, { tempFiles: string[] }>();\n\nconst session = await client.createSession({\n  hooks: {\n    onSessionStart: async (input, invocation) => {\n      sessionResources.set(invocation.sessionId, { tempFiles: [] });\n      return null;\n    },\n    onSessionEnd: async (input, invocation) => {\n      const resources = sessionResources.get(invocation.sessionId);\n      \n      if (resources) {\n        // Clean up temp files\n        for (const file of resources.tempFiles) {\n          await fs.unlink(file).catch(() => {});\n        }\n        sessionResources.delete(invocation.sessionId);\n      }\n      \n      console.log(`Session ${invocation.sessionId} ended: ${input.reason}`);\n      return null;\n    },\n  },\n});\n```\n\n#### 다시 시작에 대한 세션 상태 저장\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onSessionEnd: async (input, invocation) => {\n      if (input.reason !== \"error\") {\n        // Save state for potential resume\n        await saveSessionState(invocation.sessionId, {\n          endTime: input.timestamp,\n          cwd: input.cwd,\n          reason: input.reason,\n        });\n      }\n      return null;\n    },\n  },\n});\n```\n\n#### 로그 세션 요약\n\n```typescript\nconst sessionData: Record<string, { prompts: number; tools: number; startTime: number }> = {};\n\nconst session = await client.createSession({\n  hooks: {\n    onSessionStart: async (input, invocation) => {\n      sessionData[invocation.sessionId] = { \n        prompts: 0, \n        tools: 0, \n        startTime: input.timestamp \n      };\n      return null;\n    },\n    onUserPromptSubmitted: async (_, invocation) => {\n      sessionData[invocation.sessionId].prompts++;\n      return null;\n    },\n    onPreToolUse: async (_, invocation) => {\n      sessionData[invocation.sessionId].tools++;\n      return { permissionDecision: \"allow\" };\n    },\n    onSessionEnd: async (input, invocation) => {\n      const data = sessionData[invocation.sessionId];\n      console.log(`\nSession Summary:\n  ID: ${invocation.sessionId}\n  Duration: ${(input.timestamp - data.startTime) / 1000}s\n  Prompts: ${data.prompts}\n  Tool calls: ${data.tools}\n  End reason: ${input.reason}\n      `.trim());\n      \n      delete sessionData[invocation.sessionId];\n      return null;\n    },\n  },\n});\n```\n\n## 에이전트 중지 후크 {#agent-stop}\n\n에이전트 중지 후크는 최상위 에이전트가 자연스럽게 턴의 끝에 도달하면 실행됩니다. 이는 `onSessionEnd`와는 별개입니다: 세션은 계속 활성 상태로 유지되며, 후크는 다른 에이전트 턴을 요청할 수 있습니다.\n\n| 언어                  | 핸들러              |\n| ------------------- | ---------------- |\n| Node.js/ TypeScript | `onAgentStop`    |\n| Python              | `on_agent_stop`  |\n| Go                  | `OnAgentStop`    |\n| .NET                | `OnAgentStop`    |\n| 러스트                 | `on_agent_stop`  |\n| Java                | `setOnAgentStop` |\n\n### 입력\n\n공용 멤버 이름은 각 언어의 대/소문자 규칙을 따릅니다.\n\n| Meaning                        | Node.js/Python   | Go/.NET          | 러스트                | Java                  |\n| ------------------------------ | ---------------- | ---------------- | ------------------ | --------------------- |\n| 에이전트가 중지된 이유(예: `end_turn`     | `stopReason`     | `StopReason`     | `stop_reason`      | `getStopReason()`     |\n| 디스크 내 세션 기록 경로                 | `transcriptPath` | `TranscriptPath` | `transcript_path`  | `getTranscriptPath()` |\n| 이전 블록 결정이 이미 이 계속 진행을 강제했는지 여부 | `stopHookActive` | `StopHookActive` | `stop_hook_active` | `getStopHookActive()` |\n\n### 출력\n\n에이전트를 중지할 출력을 반환하지 않습니다. 다른 사용자 메시지를 큐에 넣기 위해 블록 결정을 반환하고 계속합니다.\n\n```json\n{\n  \"decision\": \"block\",\n  \"reason\": \"Run the final validation and fix any failures.\"\n}\n```\n\n이 후크로 인해 이미 계속된 에이전트를 반복적으로 차단하지 않도록 위에 나열된 활성 중지 멤버를 사용합니다. 또한 런타임은 연속 블록 결정의 한도를 지정합니다.\n\n## 모범 사례\n\n1. **빠르게 유지 `onSessionStart`** - 사용자가 세션이 준비되기를 기다리고 있습니다.\n\n2. **모든 끝 이유 처리** - 세션이 완전히 종료된다고 가정하지 마세요. 오류 및 중단을 처리합니다.\n\n3. **리소스 정리** - 세션 중에 할당된 모든 리소스를 해제하는 데 사용합니다 `onSessionEnd` .\n\n4. **최소 상태 저장** - 세션 데이터를 추적하는 경우 경량으로 유지합니다.\n\n5. **정리 작업이 멱등성을 갖도록 하세요** - `onSessionEnd` 프로세스가 충돌하면 호출되지 않을 수 있습니다.\n\n## 참고하십시오\n\n* [후크 사용](/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks)\n* [오류 처리 후크](/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/error-handling)\n* [디버깅 가이드](/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/troubleshooting/debugging)"}