본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
AI Generated
2025. 12. 4. · 56 Views
Config 시스템과 설정 관리 완벽 가이드
Claude Code의 설정 시스템을 깊이 있게 살펴봅니다. Config 네임스페이스부터 Zod 스키마 검증, MCP/LSP 서버 설정까지 체계적으로 이해할 수 있습니다.
목차
1. Config 네임스페이스 구조
김개발 씨는 오픈소스 CLI 도구의 소스 코드를 분석하던 중 머리가 복잡해졌습니다. 설정 관련 코드가 여기저기 흩어져 있어서 전체 구조를 파악하기 어려웠기 때문입니다.
그때 선배 박시니어 씨가 다가와 말했습니다. "설정 시스템은 네임스페이스로 체계적으로 정리되어 있어요.
구조만 이해하면 금방 파악할 수 있습니다."
Config 네임스페이스는 애플리케이션의 모든 설정을 논리적으로 그룹화한 구조입니다. 마치 잘 정리된 서랍장처럼 각각의 설정이 제자리에 있어서 필요한 것을 쉽게 찾을 수 있습니다.
이 구조를 이해하면 설정 관련 코드를 빠르게 탐색하고 수정할 수 있습니다.
다음 코드를 살펴봅시다.
// Config 네임스페이스 정의 - 모든 설정의 중심점
export namespace Config {
// 핵심 설정 타입들을 그룹화합니다
export interface Settings {
model: ModelConfig;
permissions: PermissionConfig;
ui: UIConfig;
}
// 모델 관련 설정
export interface ModelConfig {
defaultModel: string;
maxTokens: number;
temperature: number;
}
// 권한 관련 설정
export interface PermissionConfig {
allowedTools: string[];
dangerousMode: boolean;
}
}
김개발 씨는 입사한 지 얼마 되지 않은 주니어 개발자입니다. 새로운 프로젝트에 투입되어 기존 코드를 분석하던 중, 설정 파일들이 어떻게 구성되어 있는지 도무지 감이 잡히지 않았습니다.
"설정이 대체 어디에 있는 거지?" 김개발 씨는 혼잣말을 하며 파일들을 뒤적였습니다. config.ts, settings.ts, options.ts...
비슷해 보이는 파일들이 여러 폴더에 흩어져 있었습니다. 마침 옆자리 박시니어 씨가 이 광경을 보고 다가왔습니다.
"설정 시스템 구조가 헷갈리죠? 제가 핵심만 짚어드릴게요." 그렇다면 네임스페이스란 정확히 무엇일까요?
쉽게 비유하자면, 네임스페이스는 마치 대형 마트의 코너별 구획과 같습니다. 마트에 들어가면 식품 코너, 생활용품 코너, 전자제품 코너가 구분되어 있어서 원하는 물건을 쉽게 찾을 수 있습니다.
네임스페이스도 마찬가지로 관련된 코드들을 논리적인 그룹으로 묶어줍니다. 네임스페이스가 없던 시절에는 어땠을까요?
개발자들은 모든 설정 타입과 함수를 전역 공간에 선언해야 했습니다. 프로젝트가 커지면 이름 충돌이 빈번하게 발생했습니다.
ModelConfig라는 이름이 이미 다른 곳에서 사용되고 있다면, ModelConfig2나 MyModelConfig 같은 어색한 이름을 만들어야 했습니다. 바로 이런 문제를 해결하기 위해 네임스페이스가 등장했습니다.
Config 네임스페이스 안에 정의된 ModelConfig는 다른 곳의 ModelConfig와 충돌하지 않습니다. Config.ModelConfig라는 명확한 경로로 접근하기 때문입니다.
또한 관련된 설정들이 한 곳에 모여 있어 코드를 이해하기 훨씬 쉬워집니다. 위의 코드를 살펴보겠습니다.
먼저 Settings 인터페이스가 최상위 구조를 정의합니다. 이 안에 model, permissions, ui라는 세 가지 큰 카테고리가 있습니다.
각 카테고리는 다시 세부 인터페이스로 정의되어 있어서 계층 구조가 명확합니다. 실제 현업에서는 이런 구조가 빛을 발합니다.
새로운 팀원이 합류했을 때, 네임스페이스 구조만 보면 전체 설정 시스템을 한눈에 파악할 수 있습니다. 문서를 뒤지지 않아도 코드 자체가 문서 역할을 하는 것입니다.
주의할 점도 있습니다. 네임스페이스를 너무 깊게 중첩하면 오히려 복잡해질 수 있습니다.
Config.Model.Advanced.Experimental.Settings 같은 구조는 피해야 합니다. 보통 2-3단계 정도가 적당합니다.
박시니어 씨의 설명을 들은 김개발 씨는 눈이 밝아졌습니다. "아, 그래서 Config.을 붙이면 자동완성으로 관련 타입들이 쭉 나오는 거군요!" 네임스페이스를 제대로 활용하면 설정 코드가 체계적으로 정리되고, 팀원들과의 협업도 훨씬 수월해집니다.
실전 팁
💡 - 네임스페이스는 2-3단계까지만 중첩하세요
- 관련된 인터페이스와 타입을 함께 그룹화하면 자동완성이 편리해집니다
2. 설정 파일 로딩 순서
김개발 씨가 로컬에서 테스트할 때는 잘 작동하던 설정이 서버에 배포하니 전혀 다르게 동작했습니다. "분명히 설정을 바꿨는데 왜 적용이 안 되지?" 당황한 김개발 씨에게 박시니어 씨가 물었습니다.
"혹시 설정 파일 로딩 순서를 알고 계신가요?"
설정 파일 로딩 순서는 여러 설정 파일이 존재할 때 어떤 파일이 먼저 읽히고 어떤 파일이 나중에 덮어쓰는지를 결정합니다. 마치 레이어를 쌓는 것처럼 기본 설정 위에 환경별 설정이 올라가고, 그 위에 사용자 설정이 올라갑니다.
이 순서를 이해하면 설정 충돌 문제를 쉽게 해결할 수 있습니다.
다음 코드를 살펴봅시다.
// 설정 파일 로딩 순서 구현
async function loadConfig(): Promise<Config> {
// 1단계: 기본 설정 (가장 먼저 로드)
const defaults = await loadDefaults();
// 2단계: 시스템 전역 설정
const system = await loadSystemConfig('/etc/app/config.json');
// 3단계: 사용자 홈 디렉토리 설정
const user = await loadUserConfig('~/.config/app/settings.json');
// 4단계: 프로젝트 로컬 설정 (가장 높은 우선순위)
const local = await loadLocalConfig('./.app/config.json');
// 나중에 로드된 설정이 이전 설정을 덮어씁니다
return deepMerge(defaults, system, user, local);
}
김개발 씨는 분명히 프로젝트 폴더에 설정 파일을 만들어두었습니다. 그런데 서버에서는 완전히 다른 설정값이 적용되고 있었습니다.
대체 무슨 일이 벌어진 걸까요? 박시니어 씨는 화이트보드에 그림을 그리기 시작했습니다.
"설정 파일 로딩은 마치 옷을 겹쳐 입는 것과 같아요." 비유를 이어가자면, 먼저 기본 속옷을 입습니다. 이것이 **기본 설정(defaults)**입니다.
프로그램에 하드코딩된 기본값으로, 아무 설정 파일이 없어도 프로그램이 동작할 수 있게 해줍니다. 그 위에 티셔츠를 입습니다.
이것이 시스템 전역 설정입니다. 서버 관리자가 모든 사용자에게 공통으로 적용하고 싶은 설정을 여기에 넣습니다.
보통 /etc 디렉토리 아래에 위치합니다. 다음으로 재킷을 입습니다.
이것이 사용자 홈 설정입니다. 각 사용자의 홈 디렉토리에 있는 설정 파일로, 사용자 개인의 선호도를 반영합니다.
마지막으로 외투를 입습니다. 이것이 프로젝트 로컬 설정입니다.
특정 프로젝트에서만 사용하는 설정으로, 가장 바깥에 있어서 가장 먼저 보이고, 가장 높은 우선순위를 가집니다. 이제 김개발 씨의 문제가 이해됩니다.
로컬에서는 프로젝트 폴더의 설정이 적용되었지만, 서버에서는 해당 파일이 없어서 시스템 설정이 적용된 것입니다. 코드를 보면 deepMerge 함수가 핵심입니다.
이 함수는 여러 객체를 깊은 병합으로 합칩니다. 단순히 덮어쓰는 것이 아니라, 중첩된 객체 내부까지 똑똑하게 병합합니다.
실무에서 이 로딩 순서를 활용하는 좋은 패턴이 있습니다. 팀 공통 설정은 프로젝트 저장소에 커밋하고, 개인 설정은 홈 디렉토리에 둡니다.
그러면 팀원 모두가 기본 설정을 공유하면서도 각자의 취향을 반영할 수 있습니다. 흔히 하는 실수 중 하나는 설정 파일의 위치를 혼동하는 것입니다.
분명히 설정을 바꿨는데 적용이 안 된다면, 더 높은 우선순위의 설정 파일이 값을 덮어쓰고 있는지 확인해보세요. 김개발 씨는 서버에도 프로젝트 폴더에 설정 파일을 추가한 후 문제를 해결했습니다.
"로딩 순서만 알았어도 삽질을 안 했을 텐데요." 설정 파일 로딩 순서를 명확히 이해하면, 환경별로 다른 설정을 적용하는 것이 훨씬 쉬워집니다.
실전 팁
💡 - 디버깅할 때는 실제 로드된 최종 설정값을 출력해보세요
- 환경별 설정은 환경 변수와 조합하면 더욱 유연해집니다
3. Zod 스키마 기반 검증
김개발 씨가 작성한 설정 파일에 오타가 있었습니다. maxToken이라고 써야 할 것을 maxTokn이라고 썼는데, 프로그램은 아무 에러 없이 실행되었습니다.
문제는 한참 뒤에야 발견되었고, 원인을 찾느라 반나절을 허비했습니다. "설정 파일 오류를 미리 잡을 방법은 없나요?"
Zod 스키마 기반 검증은 설정 파일의 구조와 타입을 런타임에 검증하는 방법입니다. TypeScript의 타입 체크는 컴파일 타임에만 동작하지만, Zod는 실제 실행 시점에 데이터를 검증합니다.
마치 공항 보안 검색대처럼 설정이 로드될 때 모든 값을 꼼꼼히 확인합니다.
다음 코드를 살펴봅시다.
import { z } from 'zod';
// Zod 스키마로 설정 구조 정의
const ConfigSchema = z.object({
model: z.object({
defaultModel: z.string().min(1),
maxTokens: z.number().int().positive().max(100000),
temperature: z.number().min(0).max(2).default(1),
}),
permissions: z.object({
allowedTools: z.array(z.string()),
dangerousMode: z.boolean().default(false),
}),
});
// 설정 로드 시 검증 수행
function loadAndValidate(rawConfig: unknown) {
const result = ConfigSchema.safeParse(rawConfig);
if (!result.success) {
throw new Error(`설정 오류: ${result.error.message}`);
}
return result.data;
}
김개발 씨의 오타 사건 이후, 팀에서는 설정 검증 시스템을 도입하기로 했습니다. 단순히 TypeScript 타입을 정의하는 것만으로는 부족했습니다.
JSON 파일에서 읽어온 데이터는 TypeScript 타입 체크를 통과하지 않기 때문입니다. 박시니어 씨가 제안했습니다.
"Zod를 써보는 게 어떨까요? 런타임 검증의 끝판왕입니다." Zod란 무엇일까요?
쉽게 말해 데이터의 모양을 정의하고 검증하는 라이브러리입니다. 마치 쿠키틀과 같습니다.
쿠키틀에 반죽을 찍으면 정해진 모양이 나오듯, Zod 스키마에 데이터를 넣으면 정해진 구조만 통과합니다. 기존에는 어떻게 검증했을까요?
if문으로 하나하나 체크하거나, JSON Schema를 사용했습니다. 하지만 if문은 너무 번거롭고, JSON Schema는 TypeScript와 따로 놀았습니다.
스키마를 변경하면 타입 정의도 따로 수정해야 했습니다. Zod의 강점은 스키마와 타입이 하나라는 점입니다.
ConfigSchema를 정의하면 z.infer<typeof ConfigSchema>로 TypeScript 타입을 자동으로 추출할 수 있습니다. 스키마 하나로 검증과 타입 정의를 동시에 해결합니다.
코드를 살펴보겠습니다. z.object로 객체 구조를 정의합니다.
z.string().min(1)은 최소 1글자 이상의 문자열을 의미합니다. z.number().int().positive().max(100000)은 양의 정수이면서 100000 이하라는 조건을 체이닝으로 표현합니다.
safeParse 메서드가 핵심입니다. 이 메서드는 검증에 실패해도 예외를 던지지 않고, success 플래그와 함께 결과를 반환합니다.
덕분에 에러 처리를 유연하게 할 수 있습니다. 실무에서 Zod는 설정 파일뿐 아니라 API 응답 검증, 폼 데이터 검증 등 다양한 곳에서 활용됩니다.
한 번 익혀두면 여러 상황에서 유용하게 쓸 수 있습니다. 주의할 점은 default 값의 위치입니다.
스키마에서 default를 설정하면, 해당 필드가 없을 때 기본값이 채워집니다. 하지만 null이나 undefined를 명시적으로 전달하면 default가 적용되지 않을 수 있으니 주의해야 합니다.
김개발 씨는 Zod를 도입한 후 설정 오류를 프로그램 시작 시점에 바로 잡을 수 있게 되었습니다. "이제 오타 때문에 반나절 삽질하는 일은 없겠네요!" Zod 스키마 검증은 안전한 설정 관리의 필수 도구입니다.
실전 팁
💡 - z.infer를 활용하면 스키마에서 타입을 자동 추출할 수 있습니다
- 복잡한 검증 로직은 refine이나 transform 메서드로 구현하세요
4. JSONC 파싱과 환경 변수 치환
김개발 씨는 설정 파일에 주석을 달고 싶었습니다. "이 옵션은 왜 이 값인지 설명을 남기고 싶은데, JSON은 주석을 지원하지 않잖아요." 또한 API 키 같은 민감한 정보를 설정 파일에 직접 쓰기도 꺼려졌습니다.
박시니어 씨가 해법을 알려주었습니다. "JSONC와 환경 변수 치환을 써보세요."
JSONC는 JSON with Comments의 약자로, 주석을 허용하는 JSON 확장 형식입니다. 환경 변수 치환은 설정 파일 안에 ${변수명} 형태의 플레이스홀더를 두고, 로드 시점에 실제 환경 변수 값으로 교체하는 기법입니다.
이 두 가지를 조합하면 설명이 풍부하고 보안도 챙긴 설정 파일을 만들 수 있습니다.
다음 코드를 살펴봅시다.
import { parse as parseJSONC } from 'jsonc-parser';
// 환경 변수 치환 패턴: ${VAR_NAME} 또는 ${VAR_NAME:-default}
const ENV_PATTERN = /\$\{([^}:-]+)(?::-([^}]*))?\}/g;
function substituteEnvVars(content: string): string {
return content.replace(ENV_PATTERN, (_, varName, defaultVal) => {
const value = process.env[varName];
if (value !== undefined) return value;
if (defaultVal !== undefined) return defaultVal;
throw new Error(`환경 변수 ${varName}가 정의되지 않았습니다`);
});
}
function loadJSONCWithEnv(filePath: string): unknown {
const raw = fs.readFileSync(filePath, 'utf-8');
const substituted = substituteEnvVars(raw);
// JSONC 파서가 주석을 무시하고 파싱합니다
return parseJSONC(substituted);
}
김개발 씨는 팀에 새로 합류한 후배에게 프로젝트 설정을 인수인계하고 있었습니다. "이 설정은 왜 이 값인가요?" 후배의 질문에 김개발 씨는 당황했습니다.
6개월 전 자신이 설정한 값인데 이유가 기억나지 않았습니다. "그때 주석으로 남겨뒀으면 좋았을 텐데..." JSON은 주석을 지원하지 않습니다.
이것은 JSON의 설계 철학이지만, 실무에서는 불편한 점이 많습니다. 이런 문제를 해결하기 위해 JSONC가 등장했습니다.
JSON with Comments, 말 그대로 주석이 달린 JSON입니다. VS Code의 settings.json이 바로 JSONC 형식입니다.
// 한 줄 주석과 /* 여러 줄 주석 */ 모두 사용할 수 있습니다. 환경 변수 치환은 또 다른 문제를 해결합니다.
API 키, 데이터베이스 비밀번호 같은 민감한 정보를 어떻게 관리할까요? 설정 파일에 직접 쓰면 git에 커밋될 위험이 있습니다.
.gitignore에 추가하면 팀원들과 공유가 안 됩니다. 해결책은 플레이스홀더입니다.
설정 파일에는 ${API_KEY}라고 쓰고, 실제 값은 환경 변수로 주입합니다. 설정 파일은 안전하게 커밋하고, 민감한 값은 각자의 환경에서 관리합니다.
코드를 보면 정규표현식 패턴이 눈에 띕니다. ${VAR_NAME} 기본 형태와 ${VAR_NAME:-default} 기본값 형태를 모두 지원합니다.
환경 변수가 없을 때 기본값을 사용할 수 있어서 개발 환경 설정이 편리해집니다. parseJSONC 함수는 jsonc-parser 라이브러리에서 제공됩니다.
주석을 자동으로 무시하고 순수한 JSON 데이터만 추출합니다. VS Code 팀이 만든 라이브러리라 신뢰할 수 있습니다.
실무에서는 이렇게 활용합니다. 개발 환경용 .env.development, 운영 환경용 .env.production을 분리합니다.
설정 파일은 동일하되, 환경 변수만 다르게 주입하면 환경별 설정이 완성됩니다. 주의할 점은 환경 변수 누락입니다.
${API_KEY}를 썼는데 환경 변수가 없으면 에러가 발생합니다. 코드에서는 명시적으로 에러를 던지도록 했지만, 빈 문자열로 대체하는 방식도 있습니다.
상황에 맞게 선택하세요. 김개발 씨는 이제 설정 파일에 주석을 달고, 민감한 정보는 환경 변수로 분리했습니다.
후배에게 인수인계할 때도 주석을 보면서 설명하니 훨씬 수월했습니다.
실전 팁
💡 - 기본값 문법 ${VAR:-default}를 활용하면 개발 환경 설정이 편리해집니다
- 민감한 환경 변수는 반드시 .env 파일을 .gitignore에 추가하세요
5. 커맨드 에이전트 모드 설정
김개발 씨는 CLI 도구를 만들면서 고민에 빠졌습니다. 사용자마다 원하는 기능이 다르고, 상황에 따라 동작 방식도 달라져야 했습니다.
"어떻게 하면 유연하게 확장 가능한 구조를 만들 수 있을까요?" 박시니어 씨가 커맨드, 에이전트, 모드라는 세 가지 개념을 소개해주었습니다.
커맨드는 사용자가 실행할 수 있는 개별 명령입니다. 에이전트는 특정 작업을 수행하는 독립적인 처리 단위입니다.
모드는 전체적인 동작 방식을 결정하는 상태입니다. 이 세 가지를 설정으로 관리하면 프로그램의 동작을 코드 수정 없이 유연하게 변경할 수 있습니다.
다음 코드를 살펴봅시다.
// 커맨드, 에이전트, 모드 설정 스키마
const CommandSchema = z.object({
name: z.string(),
description: z.string(),
handler: z.string(), // 핸들러 함수 경로
options: z.array(z.object({
flag: z.string(),
description: z.string(),
required: z.boolean().default(false),
})).default([]),
});
const AgentSchema = z.object({
name: z.string(),
type: z.enum(['general', 'specialized', 'background']),
tools: z.array(z.string()), // 사용 가능한 도구 목록
maxConcurrency: z.number().default(1),
});
const ModeSchema = z.object({
name: z.string(),
enabledCommands: z.array(z.string()),
enabledAgents: z.array(z.string()),
restrictions: z.record(z.boolean()).default({}),
});
김개발 씨는 회사에서 내부용 CLI 도구를 개발하고 있었습니다. 처음에는 단순했지만, 요구사항이 늘어나면서 코드가 복잡해졌습니다.
새 기능을 추가할 때마다 여러 파일을 수정해야 했고, 실수로 기존 기능이 망가지기도 했습니다. 박시니어 씨가 조언했습니다.
"설정 기반 아키텍처로 바꿔보세요. 커맨드, 에이전트, 모드를 설정 파일로 관리하면 훨씬 유연해집니다." 먼저 커맨드부터 살펴보겠습니다.
마치 레스토랑 메뉴판과 같습니다. 메뉴판에 음식 이름, 설명, 가격이 적혀 있듯이, 커맨드 설정에는 명령어 이름, 설명, 실행할 핸들러가 정의됩니다.
새 메뉴를 추가하려면 메뉴판만 수정하면 되듯이, 새 커맨드도 설정만 추가하면 됩니다. 에이전트는 식당 직원에 비유할 수 있습니다.
요리사, 서빙 담당, 계산원이 각자 맡은 일이 있듯이, 에이전트도 특정 작업을 전문으로 처리합니다. tools 배열은 해당 에이전트가 사용할 수 있는 도구 목록입니다.
요리사에게는 칼과 불이 필요하듯이, 코드 분석 에이전트에게는 파일 읽기와 검색 도구가 필요합니다. 모드는 영업 시간대와 같습니다.
점심에는 런치 메뉴만, 저녁에는 풀 메뉴를 제공하듯이, 모드에 따라 사용 가능한 커맨드와 에이전트가 달라집니다. 예를 들어 '안전 모드'에서는 파일 삭제 커맨드를 비활성화할 수 있습니다.
코드를 보면 각 스키마가 독립적으로 정의되어 있습니다. CommandSchema의 handler는 문자열로 함수 경로를 저장합니다.
런타임에 동적으로 핸들러를 로드할 수 있어서, 새 커맨드를 추가할 때 코드 수정이 최소화됩니다. AgentSchema의 type은 enum으로 정의되어 세 가지 유형만 허용합니다.
general은 범용, specialized는 특화된 작업용, background는 백그라운드 실행용입니다. maxConcurrency는 동시에 몇 개의 인스턴스를 실행할 수 있는지 제한합니다.
ModeSchema의 restrictions는 record 타입으로, 동적인 제한 사항을 키-값 쌍으로 저장합니다. 어떤 기능이든 불리언 플래그로 제어할 수 있어서 확장성이 뛰어납니다.
실무에서 이 패턴은 권한 관리와 잘 어울립니다. 관리자 모드, 일반 사용자 모드, 읽기 전용 모드 등을 설정 파일로 정의하면, 권한별 기능 제어가 깔끔해집니다.
주의할 점은 설정과 코드의 동기화입니다. handler에 존재하지 않는 함수 경로를 쓰면 런타임 에러가 발생합니다.
설정 변경 후에는 반드시 테스트를 수행하세요. 김개발 씨는 설정 기반 아키텍처로 전환한 후, 새 기능 추가가 훨씬 쉬워졌습니다.
"이제 설정 파일만 건드리면 되니까 실수할 여지가 줄었어요."
실전 팁
💡 - 핸들러 경로는 문자열보다 심볼이나 타입 안전한 방식을 고려해보세요
- 모드 전환 시 현재 진행 중인 작업 처리를 잊지 마세요
6. MCP와 LSP 서버 설정
김개발 씨는 자신이 만든 도구가 외부 서비스와 연동되어야 한다는 요구사항을 받았습니다. "다른 프로그램과 어떻게 통신해야 하지?" 특히 에디터와의 연동이 필요했는데, 어디서부터 시작해야 할지 막막했습니다.
박시니어 씨가 MCP와 LSP라는 두 가지 프로토콜을 소개해주었습니다.
**MCP(Model Context Protocol)**는 AI 모델과 외부 도구 사이의 통신 규약입니다. **LSP(Language Server Protocol)**는 에디터와 언어 서버 사이의 통신 규약입니다.
둘 다 서버 설정을 통해 연결하며, 표준화된 프로토콜 덕분에 한 번 구현하면 다양한 클라이언트와 연동할 수 있습니다.
다음 코드를 살펴봅시다.
// MCP 서버 설정
const MCPServerSchema = z.object({
name: z.string(),
command: z.string(), // 실행 명령어
args: z.array(z.string()).default([]),
env: z.record(z.string()).default({}),
transport: z.enum(['stdio', 'http']).default('stdio'),
});
// LSP 서버 설정
const LSPServerSchema = z.object({
languageId: z.string(), // "typescript", "python" 등
command: z.string(),
args: z.array(z.string()).default([]),
rootUri: z.string().optional(),
initializationOptions: z.record(z.unknown()).default({}),
});
// 통합 서버 설정
const ServersConfig = z.object({
mcp: z.array(MCPServerSchema).default([]),
lsp: z.array(LSPServerSchema).default([]),
});
김개발 씨의 도구는 점점 커지고 있었습니다. 이제는 혼자 모든 것을 처리하기보다 외부 서비스와 협력해야 했습니다.
AI 모델에게 코드 분석을 맡기고, 에디터와 연동해서 실시간 피드백을 제공하고 싶었습니다. 박시니어 씨가 화이트보드에 두 개의 상자를 그렸습니다.
"외부 서비스와 소통하려면 언어가 통해야 해요. 그 언어가 바로 프로토콜입니다." MCP를 먼저 살펴보겠습니다.
MCP는 마치 통역사와 같습니다. AI 모델이 "파일 목록을 알려줘"라고 요청하면, MCP 서버가 이를 이해하고 실제로 파일 시스템을 조회한 뒤 결과를 AI가 이해할 수 있는 형태로 전달합니다.
표준화된 프로토콜 덕분에 다양한 AI 모델과 다양한 도구가 서로 소통할 수 있습니다. LSP도 비슷한 역할을 합니다.
에디터가 "이 변수의 타입이 뭐야?"라고 물으면, LSP 서버가 코드를 분석해서 답변합니다. VS Code, Neovim, Sublime Text 등 LSP를 지원하는 모든 에디터에서 같은 언어 서버를 사용할 수 있습니다.
한 번 만들면 어디서든 동작하는 것입니다. 설정을 보면 두 프로토콜의 공통점이 보입니다.
command와 args로 서버 실행 방법을 지정합니다. 서버는 별도의 프로세스로 실행되고, 메인 프로그램과 통신합니다.
MCP의 transport 옵션은 통신 방식을 결정합니다. stdio는 표준 입출력을 사용하는 간단한 방식이고, http는 네트워크를 통한 통신입니다.
로컬 도구는 stdio가 적합하고, 원격 서비스는 http를 사용합니다. LSP의 rootUri는 프로젝트 루트 경로입니다.
언어 서버가 어떤 프로젝트를 분석해야 하는지 알려줍니다. initializationOptions는 서버 시작 시 전달할 추가 설정입니다.
실무에서 이 설정들은 사용자별로 다를 수 있습니다. 어떤 사용자는 TypeScript LSP만 필요하고, 어떤 사용자는 Python과 Go도 필요합니다.
설정 파일로 관리하면 각자 원하는 서버만 활성화할 수 있습니다. 주의할 점은 서버 프로세스 관리입니다.
메인 프로그램이 종료될 때 서버 프로세스도 정리해야 합니다. 좀비 프로세스가 남으면 시스템 리소스를 낭비합니다.
김개발 씨는 MCP 서버를 연결해서 AI 모델이 파일을 읽고 검색할 수 있게 만들었습니다. LSP 서버도 연결해서 코드 자동완성과 오류 진단을 제공했습니다.
"프로토콜만 맞추면 이렇게 많은 기능을 쉽게 붙일 수 있군요!" MCP와 LSP 서버 설정을 이해하면 도구의 확장성이 크게 향상됩니다.
실전 팁
💡 - 서버 프로세스 종료 처리를 반드시 구현하세요
- 개발 중에는 서버 로그를 활성화해서 통신 내용을 확인하세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
UX와 협업 패턴 완벽 가이드
AI 에이전트와 사용자 간의 효과적인 협업을 위한 UX 패턴을 다룹니다. 프롬프트 핸드오프부터 인터럽트 처리까지, 현대적인 에이전트 시스템 설계의 핵심을 배웁니다.
자가 치유 및 재시도 패턴 완벽 가이드
AI 에이전트와 분산 시스템에서 필수적인 자가 치유 패턴을 다룹니다. 에러 감지부터 서킷 브레이커까지, 시스템을 스스로 복구하는 탄력적인 코드 작성법을 배워봅니다.
Feedback Loops 컴파일러와 CI/CD 완벽 가이드
컴파일러 피드백 루프부터 CI/CD 파이프라인, 테스트 자동화, 자가 치유 빌드까지 현대 개발 워크플로우의 핵심을 다룹니다. 초급 개발자도 쉽게 이해할 수 있도록 실무 예제와 함께 설명합니다.
실전 MCP 통합 프로젝트 완벽 가이드
Model Context Protocol을 활용한 실전 통합 프로젝트를 처음부터 끝까지 구축하는 방법을 다룹니다. 아키텍처 설계부터 멀티 서버 통합, 모니터링, 배포까지 운영 레벨의 MCP 시스템을 구축하는 노하우를 담았습니다.
MCP 동적 도구 업데이트 완벽 가이드
AI 에이전트의 도구를 런타임에 동적으로 로딩하고 관리하는 방법을 알아봅니다. 플러그인 시스템 설계부터 핫 리로딩, 보안까지 실무에서 바로 적용할 수 있는 내용을 다룹니다.