SDK 수준 옵션 및 패턴은 다중 테넌트 및 서버 배포을 참조하세요.
최적 대상: 플랫폼 개발자, SaaS 작성기, 소수의 동시 사용자를 제공하는 모든 배포.
핵심 개념
패턴을 선택하기 전에 세 가지 크기 조정 차원을 이해합니다.

세션 격리 패턴
패턴 1: 사용자당 격리된 CLI
각 사용자는 자체 CLI 서버 인스턴스를 가져옵니다. 가장 강력한 격리 - 사용자의 세션, 메모리 및 프로세스가 완전히 분리됩니다.

사용 시기:
- 데이터 격리가 중요한 다중 테넌트 SaaS
- 다른 인증 자격 증명을 가진 사용자
- 규정 준수 요구 사항(SOC 2, HIPAA)
// CLI pool manager — one CLI per user
class CLIPool {
private instances = new Map<string, { client: CopilotClient; port: number }>();
private nextPort = 5000;
async getClientForUser(userId: string, token?: string): Promise<CopilotClient> {
if (this.instances.has(userId)) {
return this.instances.get(userId)!.client;
}
const port = this.nextPort++;
// Spawn a dedicated CLI for this user
await spawnCLI(port, token);
const client = new CopilotClient({
cliUrl: `localhost:${port}`,
});
this.instances.set(userId, { client, port });
return client;
}
async releaseUser(userId: string): Promise<void> {
const instance = this.instances.get(userId);
if (instance) {
await instance.client.stop();
this.instances.delete(userId);
}
}
}
패턴 2: 세션 격리를 사용하는 공유 CLI
여러 사용자가 하나의 CLI 서버를 공유하지만 고유한 세션 ID를 통해 격리된 세션이 있습니다. 리소스가 더 가벼우지만 격리가 약합니다.

사용 시기:
- 신뢰할 수 있는 사용자가 있는 내부 도구
- 리소스가 제한된 환경
- 낮은 격리 요구 사항
const sharedClient = new CopilotClient({
cliUrl: "localhost:4321",
});
// Enforce session isolation through naming conventions
function getSessionId(userId: string, purpose: string): string {
return `${userId}-${purpose}-${Date.now()}`;
}
// Access control: ensure users can only access their own sessions
async function resumeSessionWithAuth(
sessionId: string,
currentUserId: string
): Promise<Session> {
const [sessionUserId] = sessionId.split("-");
if (sessionUserId !== currentUserId) {
throw new Error("Access denied: session belongs to another user");
}
return sharedClient.resumeSession(sessionId);
}
패턴 3: 공유 세션(공동 작업)
여러 사용자가 코필로트와 공유 채팅방과 같은 동일한 세션과 상호 작용합니다.

사용 시기:
- 팀 공동 작업 도구
- 공유 코드 검토 세션
- 프로그래밍 도우미 페어링
⚠️중요: SDK는 기본 제공 세션 잠금을 제공하지 않습니다. 동일한 세션에 대한 동시 쓰기를 방지하려면 액세스를 직렬화 해야 합니다 .
import Redis from "ioredis";
const redis = new Redis();
async function withSessionLock<T>(
sessionId: string,
fn: () => Promise<T>,
timeoutSec = 300
): Promise<T> {
const lockKey = `session-lock:${sessionId}`;
const lockId = crypto.randomUUID();
// Acquire lock
const acquired = await redis.set(lockKey, lockId, "NX", "EX", timeoutSec);
if (!acquired) {
throw new Error("Session is in use by another user");
}
try {
return await fn();
} finally {
// Release lock (only if we still own it)
const currentLock = await redis.get(lockKey);
if (currentLock === lockId) {
await redis.del(lockKey);
}
}
}
// Usage: serialize access to shared session
app.post("/team-chat", authMiddleware, async (req, res) => {
const result = await withSessionLock("team-project-review", async () => {
const session = await client.resumeSession("team-project-review");
return session.sendAndWait({ prompt: req.body.message });
});
res.json({ content: result?.data.content });
});
격리 패턴 비교
| 사용자별로 분리된 CLI | 공유 CLI 및 세션 격리 | 공유 세션 | |
|---|---|---|---|
| Isolation | |||
| ✅ 완료 | |||
| ⚠️ 논리 | |||
| ❌ 공유 | |||
| 리소스 사용량 | 높음(사용자당 CLI) | 낮음(하나의 명령줄 인터페이스) | 낮음(하나의 CLI + 세션) |
| 복잡성 | 중간 | 낮음 | 높음(잠금) |
| 인증 유연성 | |||
| ✅ 사용자별 토큰 | |||
| ⚠️ 서비스 토큰 | |||
| ⚠️ 서비스 토큰 | |||
| 최적입니다 | 다중 사용자 SaaS | 내부 도구 | Collaboration |
수평 크기 조정
부하 분산 장치 뒤에 여러 CLI 서버

주요 요구 사항: 모든 CLI 서버가 세션을 다시 시작할 수 있도록 세션 상태는 공유 스토리지 에 있어야 합니다.
// Route sessions to CLI servers
class CLILoadBalancer {
private servers: string[];
private currentIndex = 0;
constructor(servers: string[]) {
this.servers = servers;
}
// Round-robin selection
getNextServer(): string {
const server = this.servers[this.currentIndex];
this.currentIndex = (this.currentIndex + 1) % this.servers.length;
return server;
}
// Sticky sessions: same user always hits same server
getServerForUser(userId: string): string {
const hash = this.hashCode(userId);
return this.servers[hash % this.servers.length];
}
private hashCode(str: string): number {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = (hash << 5) - hash + str.charCodeAt(i);
hash |= 0;
}
return Math.abs(hash);
}
}
const lb = new CLILoadBalancer([
"cli-1:4321",
"cli-2:4321",
"cli-3:4321",
]);
app.post("/chat", async (req, res) => {
const server = lb.getServerForUser(req.user.id);
const client = new CopilotClient({ cliUrl: server });
const session = await client.createSession({
sessionId: `user-${req.user.id}-chat`,
model: "gpt-4.1",
});
const response = await session.sendAndWait({ prompt: req.body.message });
res.json({ content: response?.data.content });
});
고정 세션 및 공유 스토리지

스티키 세션은 더 간단합니다. 즉, 사용자가 특정 CLI 서버에 계속 연결되도록 합니다. 공유 스토리지는 필요하지 않지만 부하 분산은 고르지 않습니다.
공유 스토리지 를 사용하면 모든 CLI에서 모든 세션을 처리할 수 있습니다. 부하 분산은 더 우수하지만, ~/.copilot/session-state/에는 네트워크 스토리지가 필요합니다.
수직 크기 조정
단일 CLI 서버 튜닝
단일 CLI 서버는 여러 동시 세션을 처리할 수 있습니다. 주요 고려 사항:

세션 수명 주기 관리는 수직적 크기 조정의 핵심입니다.
// Limit concurrent active sessions
class SessionManager {
private activeSessions = new Map<string, Session>();
private maxConcurrent: number;
constructor(maxConcurrent = 50) {
this.maxConcurrent = maxConcurrent;
}
async getSession(sessionId: string): Promise<Session> {
// Return existing active session
if (this.activeSessions.has(sessionId)) {
return this.activeSessions.get(sessionId)!;
}
// Enforce concurrency limit
if (this.activeSessions.size >= this.maxConcurrent) {
await this.evictOldestSession();
}
// Create or resume
const session = await client.createSession({
sessionId,
model: "gpt-4.1",
});
this.activeSessions.set(sessionId, session);
return session;
}
private async evictOldestSession(): Promise<void> {
const [oldestId] = this.activeSessions.keys();
const session = this.activeSessions.get(oldestId)!;
// Session state is persisted automatically — safe to disconnect
await session.disconnect();
this.activeSessions.delete(oldestId);
}
}
임시 세션과 영구 세션 비교

임시 세션
각 요청이 독립적인 상태 비스테이스 API 엔드포인트의 경우:
app.post("/api/analyze", async (req, res) => {
const session = await client.createSession({
model: "gpt-4.1",
});
try {
const response = await session.sendAndWait({
prompt: req.body.prompt,
});
res.json({ result: response?.data.content });
} finally {
await session.disconnect(); // Clean up immediately
}
});
영구 세션
대화형 인터페이스 또는 장기 실행 워크플로의 경우:
// Create a resumable session
app.post("/api/chat/start", async (req, res) => {
const sessionId = `user-${req.user.id}-${Date.now()}`;
const session = await client.createSession({
sessionId,
model: "gpt-4.1",
infiniteSessions: {
enabled: true,
backgroundCompactionThreshold: 0.80,
},
});
res.json({ sessionId });
});
// Continue the conversation
app.post("/api/chat/message", async (req, res) => {
const session = await client.resumeSession(req.body.sessionId);
const response = await session.sendAndWait({ prompt: req.body.message });
res.json({ content: response?.data.content });
});
// Clean up when done
app.post("/api/chat/end", async (req, res) => {
await client.deleteSession(req.body.sessionId);
res.json({ success: true });
});
컨테이너 배포
영구 스토리지가 있는 Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
name: copilot-cli
spec:
replicas: 3
selector:
matchLabels:
app: copilot-cli
template:
metadata:
labels:
app: copilot-cli
spec:
containers:
- name: copilot-cli
image: your-registry/copilot-cli:latest # See backend-services.md for how to build and push this image
args: ["--headless", "--host", "0.0.0.0", "--port", "4321"]
env:
- name: COPILOT_GITHUB_TOKEN
valueFrom:
secretKeyRef:
name: copilot-secrets
key: github-token
ports:
- containerPort: 4321
volumeMounts:
- name: session-state
mountPath: /root/.copilot/session-state
volumes:
- name: session-state
persistentVolumeClaim:
claimName: copilot-sessions-pvc
---
apiVersion: v1
kind: Service
metadata:
name: copilot-cli
spec:
selector:
app: copilot-cli
ports:
- port: 4321
targetPort: 4321

Azure 컨테이너 인스턴스
containers:
- name: copilot-cli
image: your-registry/copilot-cli:latest # See backend-services.md for how to build and push this image
command: ["copilot", "--headless", "--host", "0.0.0.0", "--port", "4321"]
volumeMounts:
- name: session-storage
mountPath: /root/.copilot/session-state
volumes:
- name: session-storage
azureFile:
shareName: copilot-sessions
storageAccountName: myaccount
프로덕션 체크리스트

| 관심사 | Recommendation |
|---|---|
| 세션 정리 | 주기적 정리를 실행하여 TTL보다 오래된 세션을 삭제합니다. |
| 상태 검사 | 주기적으로 CLI 서버 Ping; 응답하지 않는 경우 다시 시작 |
| 스토리지 | |
~/.copilot/session-state/에 영구 볼륨 탑재 | |
| 비밀 | 플랫폼의 시크릿 관리 도구(Vault, K8s Secrets 등)를 사용하세요. |
| Monitoring | 활성 세션 수, 응답 대기 시간, 오류 비율 추적 |
| Locking | 공유 세션 액세스에 Redis 또는 이와 유사한 항목 사용 |
| 종료 | CLI 서버를 중지하기 전에 활성 세션 드레이닝 |
Limitations
| Limitation | Details |
|---|---|
| 기본 제공 세션 잠금 없음 | 동시 액세스를 위한 애플리케이션 수준 잠금 구현 |
| 기본 제공 부하 분산 없음 | 외부 로드 밸런서 또는 서비스 메시 사용 |
| 세션 상태는 파일 기반입니다. | 다중 서버 설치를 위한 공유 파일 시스템 필요 |
| 30분 유휴 시간 제한 | 작업이 없는 세션은 CLI에 의해 자동으로 정리됩니다. |
| CLI는 단일 프로세스입니다. | 스레드가 아닌 CLI 서버 인스턴스를 더 추가하여 크기 조정 |
다음 단계
- 세션 다시 시작 및 지속성: 다시 실행 가능한 세션에 대한 심층 분석
- 백 엔드 서비스 설정: 핵심 서버 쪽 설정
- GitHub OAuth 설정: 다중 사용자 인증
- BYOK(사용자 고유의 키 가져오기): 사용자 고유의 모델 공급자 사용