> 작성 및 명령어 확인 기준: 2026년 7월 26일  
> 예제 환경: Windows, Node.js, TypeScript, Claude Code, Codex CLI

최근 개발자 채용공고를 보다 보면 MCP 서버 개발 경험을 우대한다는 문구가 종종 보인다.

Claude Code나 Codex 같은 AI 코딩 도구를 사용하면서도 MCP라는 이름은 계속 접하게 된다.

처음에는 AI가 외부 API를 호출하게 만드는 방식과 크게 다르지 않은 것처럼 보였다.

어차피 서버에 요청을 보내고 결과를 받는 구조라면, 기존 REST API를 연결하는 것과 무엇이 다른지 궁금했다.

그래서 이번 글에서는 외부 API 키가 필요 없는 간단한 MCP 서버를 직접 만들어보고, 같은 서버를 Claude Code와 Codex에 각각 연결해보는 흐름으로 정리해보려고 한다.

결론부터 말하면 MCP는 API를 대체하는 기술이 아니다.

API가 서비스의 기능과 데이터를 외부에 제공하는 통로라면, MCP는 AI가 그런 기능을 발견하고 일정한 형식으로 사용할 수 있게 만드는 연결 규격에 가깝다.

쉽게 말하면 API가 실제 기능을 제공하는 엔진이라면, MCP는 AI가 그 엔진의 사용법을 이해하고 작동시킬 수 있도록 만들어주는 공통 어댑터라고 보면 된다.

---

1. MCP란 무엇인가

MCP는 Model Context Protocol의 약자다.

AI 애플리케이션이 외부 데이터, 도구, 업무 흐름과 연결될 수 있도록 만든 공개 표준이다.

예를 들어 Claude Code나 Codex가 다음과 같은 일을 해야 한다고 생각해보자.

1) 사내 데이터베이스에서 주문 정보를 조회한다.

2) GitHub 이슈를 검색한다.

3) 로컬에 저장된 문서를 읽는다.

4) 브라우저를 열어 화면을 확인한다.

5) 특정 API를 호출해 결과를 가져온다.

AI 모델 자체는 이런 시스템에 마음대로 접근할 수 없다.

외부 기능을 사용하려면 어떤 기능이 있는지, 어떤 값을 넘겨야 하는지, 결과는 어떤 형식으로 돌아오는지를 알아야 한다.

MCP 서버는 이런 정보를 AI 도구에 일정한 규격으로 제공한다.

MCP 서버가 제공하는 대표 기능은 다음 세 가지다.

1) Tools

AI가 실행할 수 있는 기능이다.

예를 들어 프로젝트 검색, 데이터베이스 조회, 파일 생성, 배포 실행 같은 기능을 Tool로 만들 수 있다.

2) Resources

AI가 읽을 수 있는 데이터다.

파일 내용, 문서, 데이터베이스 결과, API 응답 같은 정보를 Resource로 제공할 수 있다.

3) Prompts

반복해서 사용할 수 있는 작업 지침이나 프롬프트 템플릿이다.

이번 글에서는 가장 이해하기 쉬운 Tools를 사용한다.

---

2. MCP와 API는 무엇이 다른가

MCP를 처음 보면 가장 먼저 드는 생각은 이것이다.

“이것도 결국 서버에 요청해서 결과를 받는 것 아닌가?”

맞다.

MCP 서버 내부에서도 기존 REST API를 호출할 수 있고, 데이터베이스를 조회할 수도 있다.

다만 API와 MCP는 담당하는 역할이 조금 다르다.

1) API는 애플리케이션을 위한 인터페이스다

일반적인 REST API는 개발자가 정해진 URL과 요청 형식을 알고 직접 호출한다.

예를 들어 프로젝트를 검색하는 API가 다음과 같다고 해보자.

```http
GET /api/projects?keyword=flutter
```

이 API를 사용하는 클라이언트는 미리 다음 내용을 알고 있어야 한다.

- 호출할 주소
- HTTP 메서드
- 전달할 파라미터
- 인증 방식
- 응답 데이터 구조

프론트엔드나 모바일 앱에서는 개발자가 이 규격에 맞춰 호출 코드를 작성한다.

2) MCP는 AI 도구를 위한 연결 규격이다

MCP 서버는 AI에게 사용할 수 있는 Tool의 이름, 설명, 입력값 구조를 알려준다.

예를 들어 다음과 같은 Tool이 있다고 해보자.

```text
도구명: search_projects
설명: 기술 스택, 프로젝트 이름, 설명에서 프로젝트를 검색한다.
입력값: keyword 문자열
```

Claude Code나 Codex는 이 설명을 보고 사용자의 요청에 필요한 Tool인지 판단한다.

사용자가 다음처럼 자연어로 질문할 수 있다.

```text
Flutter와 AWS를 같이 사용한 프로젝트를 찾아줘.
```

AI는 등록된 Tool 가운데 `search_projects`가 적절하다고 판단하면 필요한 검색어를 만들어 MCP 서버를 호출한다.

개발자가 매번 AI에게 API 주소와 JSON 형식을 설명하지 않아도 된다.

3) MCP 서버 안에서 기존 API를 그대로 사용할 수 있다

이 부분이 생각보다 중요했다.

MCP와 API는 서로 경쟁하는 구조가 아니다.

실제 운영 환경에서는 다음과 같이 연결되는 경우가 많다.

```text
Claude Code 또는 Codex
        ↓
     MCP 서버
        ↓
기존 REST API 또는 데이터베이스
```

기존 서비스에 이미 REST API가 있다면 그것을 없애고 MCP로 다시 만드는 것이 아니다.

기존 API를 호출하는 MCP Tool을 추가해서 AI 도구가 사용할 수 있도록 연결하면 된다.

개인적으로는 MCP를 “AI용 API”라고만 설명하는 것보다 “기존 API와 시스템을 AI가 사용할 수 있게 만드는 표준 어댑터”라고 설명하는 편이 더 정확하게 느껴졌다.

---

3. 이번에 만들 MCP 서버의 구조

이번 예제에서는 개발자의 프로젝트 목록을 검색하는 간단한 MCP 서버를 만든다.

외부 API나 데이터베이스는 연결하지 않는다.

프로젝트 배열을 서버 코드 안에 넣고, AI가 검색어를 전달하면 일치하는 프로젝트를 반환하는 방식이다.

등록할 Tool은 두 개다.

1) `search_projects`

프로젝트 이름, 설명, 기술 스택에서 키워드를 검색한다.

2) `get_project_detail`

정확한 프로젝트 이름을 전달하면 해당 프로젝트의 상세 정보를 반환한다.

전체 구조는 다음과 같다.

```text
사용자
  ↓ 자연어 질문
Claude Code 또는 Codex
  ↓ Tool 선택 및 입력값 생성
project-search MCP 서버
  ↓ 프로젝트 데이터 검색
검색 결과 반환
  ↓
Claude Code 또는 Codex가 자연어로 정리
```

이 정도만 만들어도 MCP의 전체 흐름을 확인하기에는 충분하다.

---

4. 프로젝트 생성하기

먼저 PowerShell에서 프로젝트 폴더를 만든다.

```powershell
mkdir project-search-mcp
cd project-search-mcp

npm init -y

npm install @modelcontextprotocol/sdk zod@3
npm install -D typescript @types/node

mkdir src
New-Item src/index.ts
New-Item tsconfig.json
```

Node.js는 현재 설치 가능한 LTS 버전을 사용하는 것이 좋다.

설치 여부는 다음 명령으로 확인할 수 있다.

```powershell
node --version
npm --version
```

프로젝트 구조는 다음처럼 만들어진다.

```text
project-search-mcp
├─ src
│  └─ index.ts
├─ package.json
└─ tsconfig.json
```

---

5. package.json 설정하기

`package.json`을 다음처럼 정리한다.

```json
{
  "name": "project-search-mcp",
  "version": "1.0.0",
  "description": "개발 프로젝트를 검색하는 간단한 MCP 서버",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node build/index.js"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.0",
    "zod": "^3.0.0"
  },
  "devDependencies": {
    "@types/node": "^22.0.0",
    "typescript": "^5.0.0"
  }
}
```

여기서 버전 번호는 예시다.

실제로 `npm install`을 실행하면 설치 시점에 맞는 버전이 `package.json`에 기록된다.

따라서 직접 설치한 뒤 생성된 버전을 그대로 사용하는 편이 안전하다.

`"type": "module"`은 ES Module 방식의 `import` 문을 사용하기 위한 설정이다.

`npm run build`를 실행하면 TypeScript 파일이 `build/index.js`로 컴파일된다.

---

6. tsconfig.json 설정하기

`tsconfig.json`은 다음처럼 작성한다.

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}
```

복잡한 설정은 아니다.

`src` 안의 TypeScript 파일을 읽어서 `build` 폴더에 JavaScript 파일을 생성하는 설정이라고 보면 된다.

---

7. MCP 서버 코드 작성하기

`src/index.ts`에 다음 코드를 넣는다.

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

type Project = {
  name: string;
  description: string;
  stacks: string[];
  role: string;
};

const projects: Project[] = [
  {
    name: "Super App",
    description: "사용자 인증과 상품 조회 기능을 포함한 Flutter 모바일 앱",
    stacks: ["Flutter", "Dart", "REST API", "AWS"],
    role: "모바일 앱과 서버 연동 개발",
  },
  {
    name: "Market Service",
    description: "상품 등록과 주문 관리를 제공하는 웹 서비스",
    stacks: ["Next.js", "Node.js", "PostgreSQL", "AWS"],
    role: "웹 프론트엔드와 백엔드 개발",
  },
  {
    name: "Booking Service",
    description: "예약 일정과 관리자 기능을 제공하는 서비스",
    stacks: ["React", "Express", "MySQL"],
    role: "예약 API와 관리자 화면 개발",
  },
  {
    name: "Document Translator",
    description: "문서를 업로드하고 AI로 번역하는 웹 서비스",
    stacks: ["Next.js", "Supabase", "OpenAI API", "Vercel"],
    role: "전체 서비스 설계와 구현",
  },
];

const server = new McpServer({
  name: "project-search",
  version: "1.0.0",
});

server.registerTool(
  "search_projects",
  {
    description:
      "프로젝트 이름, 설명, 역할, 기술 스택에서 입력한 키워드와 관련된 프로젝트를 검색합니다.",
    inputSchema: {
      keyword: z
        .string()
        .min(1)
        .describe("검색할 기술명이나 프로젝트 관련 키워드"),
    },
  },
  async ({ keyword }) => {
    const normalizedKeyword = keyword.toLowerCase();

    const matchedProjects = projects.filter((project) => {
      const searchableText = [
        project.name,
        project.description,
        project.role,
        ...project.stacks,
      ]
        .join(" ")
        .toLowerCase();

      return searchableText.includes(normalizedKeyword);
    });

    if (matchedProjects.length === 0) {
      return {
        content: [
          {
            type: "text",
            text: `"${keyword}"와 관련된 프로젝트를 찾지 못했습니다.`,
          },
        ],
      };
    }

    const result = matchedProjects
      .map(
        (project) =>
          [
            `프로젝트명: ${project.name}`,
            `설명: ${project.description}`,
            `기술 스택: ${project.stacks.join(", ")}`,
            `담당 역할: ${project.role}`,
          ].join("\n"),
      )
      .join("\n\n---\n\n");

    return {
      content: [
        {
          type: "text",
          text: result,
        },
      ],
    };
  },
);

server.registerTool(
  "get_project_detail",
  {
    description: "정확한 프로젝트 이름을 기준으로 상세 정보를 조회합니다.",
    inputSchema: {
      name: z.string().min(1).describe("조회할 프로젝트 이름"),
    },
  },
  async ({ name }) => {
    const project = projects.find(
      (item) => item.name.toLowerCase() === name.toLowerCase(),
    );

    if (!project) {
      return {
        content: [
          {
            type: "text",
            text: `"${name}" 프로젝트를 찾지 못했습니다.`,
          },
        ],
      };
    }

    return {
      content: [
        {
          type: "text",
          text: [
            `프로젝트명: ${project.name}`,
            `설명: ${project.description}`,
            `기술 스택: ${project.stacks.join(", ")}`,
            `담당 역할: ${project.role}`,
          ].join("\n"),
        },
      ],
    };
  },
);

async function main() {
  const transport = new StdioServerTransport();

  await server.connect(transport);

  console.error("project-search MCP 서버가 stdio 방식으로 실행되었습니다.");
}

main().catch((error) => {
  console.error("MCP 서버 실행 중 오류가 발생했습니다.", error);
  process.exit(1);
});
```

코드를 보면 일반적인 Node.js 서버와 조금 다른 점이 있다.

Express처럼 포트를 열거나 URL을 등록하지 않았다.

대신 `McpServer`를 만들고 `registerTool`로 AI가 사용할 기능을 등록했다.

마지막에는 `StdioServerTransport`를 사용해 표준 입출력 방식으로 클라이언트와 통신한다.

---

8. 코드에서 중요한 부분 살펴보기

1) Tool의 설명이 중요하다

다음 부분은 단순한 주석이 아니다.

```typescript
description:
  "프로젝트 이름, 설명, 역할, 기술 스택에서 입력한 키워드와 관련된 프로젝트를 검색합니다."
```

Claude Code나 Codex는 이 설명을 보고 어떤 상황에서 Tool을 사용할지 판단한다.

설명이 너무 짧거나 애매하면 AI가 Tool을 사용하지 않거나 엉뚱한 상황에서 호출할 수 있다.

MCP에서는 함수 이름만큼 설명과 입력값 정의가 중요하다.

2) inputSchema가 호출 규격이 된다

다음 부분은 Tool이 어떤 값을 받아야 하는지 정의한다.

```typescript
inputSchema: {
  keyword: z
    .string()
    .min(1)
    .describe("검색할 기술명이나 프로젝트 관련 키워드"),
}
```

기존 REST API의 요청 파라미터나 JSON 스키마와 비슷한 역할이다.

AI는 이 스키마에 맞춰 `keyword` 값을 만들어 전달한다.

3) 결과는 content 형식으로 반환한다

Tool 실행 결과는 다음 형태로 반환한다.

```typescript
return {
  content: [
    {
      type: "text",
      text: result,
    },
  ],
};
```

MCP 서버는 결과를 사용자 화면에 직접 출력하는 것이 아니다.

결과를 AI 클라이언트에 전달하고, Claude Code나 Codex가 이를 다시 자연어로 정리한다.

4) stdio에서는 console.log를 사용하면 안 된다

이번 서버는 표준 입출력으로 MCP 메시지를 주고받는다.

이 상태에서 `console.log()`를 사용하면 MCP 통신에 사용하는 표준 출력에 일반 문자열이 섞일 수 있다.

그 결과 JSON-RPC 메시지가 깨지고 서버 연결이 실패할 수 있다.

로그가 필요하면 다음처럼 `console.error()`를 사용하는 편이 안전하다.

```typescript
console.error("서버 로그");
```

직접 만들어볼 때 이 부분이 생각보다 중요하다.

일반 Node.js 프로그램에서는 `console.log()`가 너무 자연스럽기 때문에 무심코 넣기 쉽다.

---

9. TypeScript 빌드하기

코드 작성이 끝났으면 빌드한다.

```powershell
npm run build
```

빌드가 성공하면 다음 파일이 생성된다.

```text
build/index.js
```

MCP 클라이언트는 TypeScript 원본이 아니라 이 JavaScript 파일을 Node.js로 실행한다.

코드를 수정한 뒤 빌드를 다시 하지 않으면 이전 코드가 실행된다.

MCP Tool을 수정했는데 결과가 바뀌지 않는다면 먼저 `npm run build`를 다시 실행했는지 확인하는 것이 좋다.

---

10. Claude Code에 MCP 서버 연결하기

Claude Code는 `claude mcp add` 명령으로 로컬 stdio MCP 서버를 추가할 수 있다.

먼저 `build/index.js`의 절대 경로를 확인한다.

예를 들어 프로젝트 위치가 다음과 같다고 해보자.

```text
C:\dev\project-search-mcp
```

Windows에서도 설정 경로에는 역슬래시보다 슬래시를 사용하면 따옴표와 이스케이프 문제를 줄일 수 있다.

```text
C:/dev/project-search-mcp/build/index.js
```

1) 사용자 범위로 연결하기

모든 프로젝트에서 사용하려면 다음처럼 등록한다.

```powershell
claude mcp add --transport stdio --scope user project-search -- node "C:/dev/project-search-mcp/build/index.js"
```

명령어 중간의 `--`는 Claude Code 자체 옵션과 실제 MCP 서버 실행 명령을 구분한다.

쉽게 말하면 `--` 뒤의 내용은 Claude Code 옵션이 아니라 서버를 실행하기 위한 명령이다.

위 설정은 내부적으로 다음 명령을 실행한다.

```powershell
node "C:/dev/project-search-mcp/build/index.js"
```

2) 현재 프로젝트에서만 사용하기

팀과 공유하거나 특정 프로젝트에서만 사용하려면 `project` 범위를 사용할 수 있다.

```powershell
claude mcp add --transport stdio --scope project project-search -- node "C:/dev/project-search-mcp/build/index.js"
```

이 경우 프로젝트 루트에 `.mcp.json` 설정이 만들어진다.

프로젝트 범위 MCP 서버는 보안상 처음 연결할 때 사용 승인을 요구할 수 있다.

개인적으로는 처음 테스트할 때는 `user` 범위로 연결하고, 실제 프로젝트에 필요한 서버만 `project` 범위로 관리하는 편이 이해하기 쉬웠다.

3) 연결 상태 확인하기

다음 명령으로 등록된 MCP 서버를 확인한다.

```powershell
claude mcp list
```

특정 서버의 상세 설정을 확인하려면 다음 명령을 사용한다.

```powershell
claude mcp get project-search
```

Claude Code를 실행한 상태에서는 다음 명령으로 연결 상태를 확인할 수 있다.

```text
/mcp
```

서버가 정상적으로 연결되면 `project-search`가 목록에 표시된다.

---

11. Claude Code에서 Tool 호출해보기

Claude Code를 실행한다.

```powershell
claude
```

다음과 같이 질문해본다.

```text
등록된 프로젝트 중 Flutter를 사용한 프로젝트를 찾아줘.
```

Claude Code가 MCP Tool을 사용하도록 조금 더 명확하게 요청할 수도 있다.

```text
project-search MCP 도구를 사용해서 Flutter 관련 프로젝트를 찾아줘.
```

정상적으로 연결됐다면 Claude Code는 `search_projects` Tool을 선택하고, 다음과 비슷한 입력값을 전달한다.

```json
{
  "keyword": "Flutter"
}
```

서버는 검색 결과를 반환하고, Claude Code는 결과를 읽기 쉬운 문장으로 정리한다.

다음 요청도 테스트해볼 수 있다.

```text
Super App 프로젝트의 상세 정보를 MCP 도구로 조회해줘.
```

이 경우에는 `get_project_detail` Tool이 호출될 가능성이 높다.

---

12. Codex에 MCP 서버 연결하기

같은 MCP 서버를 Codex에도 연결할 수 있다.

MCP의 장점은 한 번 만든 서버를 특정 AI 도구에만 묶어두지 않고, MCP를 지원하는 여러 클라이언트에서 재사용할 수 있다는 점이다.

1) Codex CLI 명령으로 추가하기

다음 명령을 실행한다.

```powershell
codex mcp add project-search -- node "C:/dev/project-search-mcp/build/index.js"
```

Claude Code와 마찬가지로 `--` 뒤에는 MCP 서버를 실행할 실제 명령이 들어간다.

등록 결과는 다음 명령으로 확인한다.

```powershell
codex mcp list
```

Codex의 MCP 관련 명령을 모두 보고 싶다면 다음 명령을 사용한다.

```powershell
codex mcp --help
```

2) config.toml에 직접 설정하기

Codex는 기본적으로 다음 파일에 설정을 저장한다.

```text
~/.codex/config.toml
```

Windows에서는 보통 사용자 홈 폴더 아래의 `.codex/config.toml`이다.

다음 내용을 직접 추가할 수도 있다.

```toml
[mcp_servers.project-search]
command = "node"
args = ["C:/dev/project-search-mcp/build/index.js"]
```

특정 프로젝트에서만 사용하려면 프로젝트 안에 다음 파일을 만들 수 있다.

```text
.codex/config.toml
```

프로젝트 범위 설정은 신뢰된 프로젝트에서만 적용되는 점에 주의가 필요하다.

3) Codex에서 연결 상태 확인하기

Codex를 실행한다.

```powershell
codex
```

Codex 터미널 화면에서 다음 명령을 입력한다.

```text
/mcp
```

`project-search` 서버와 제공되는 Tool이 표시되면 연결된 것이다.

---

13. Codex에서 Tool 호출해보기

Codex에서 다음처럼 요청한다.

```text
project-search MCP 서버를 사용해서 AWS 관련 프로젝트를 찾아줘.
```

또는 자연스럽게 질문해도 된다.

```text
내 프로젝트 데이터 중 AWS를 사용한 프로젝트가 무엇인지 확인해줘.
```

Codex가 Tool 설명을 보고 MCP 서버 호출이 필요하다고 판단하면 `search_projects`를 실행한다.

다음 질문도 가능하다.

```text
Document Translator 프로젝트의 상세 정보를 조회하고 사용 기술을 정리해줘.
```

Claude Code와 Codex는 서로 다른 AI 코딩 도구지만, MCP 서버 입장에서는 같은 규격으로 요청을 처리한다.

이것이 MCP를 사용하는 가장 큰 이유 가운데 하나다.

---

14. 실제 호출 과정은 어떻게 진행되는가

사용자가 다음과 같이 질문했다고 해보자.

```text
Flutter를 사용한 프로젝트를 찾아줘.
```

내부 흐름은 대략 다음과 같다.

1) Claude Code나 Codex가 사용자의 문장을 분석한다.

2) 현재 연결된 MCP 서버의 Tool 목록과 설명을 확인한다.

3) `search_projects` Tool이 적절하다고 판단한다.

4) 입력 스키마에 맞춰 `keyword: "Flutter"`를 만든다.

5) MCP 서버에 Tool 실행을 요청한다.

6) MCP 서버가 프로젝트 배열을 검색한다.

7) 검색 결과를 Claude Code나 Codex에 반환한다.

8) AI가 결과를 사용자에게 자연어로 설명한다.

일반 REST API에서는 개발자가 API 호출 코드를 직접 작성한다.

MCP에서는 AI 클라이언트가 Tool의 설명과 스키마를 읽고 호출 여부와 입력값을 결정한다.

물론 MCP가 모든 것을 알아서 해결해주는 것은 아니다.

Tool 설명, 입력 스키마, 권한, 오류 처리, 반환 데이터는 결국 개발자가 제대로 설계해야 한다.

---

15. API와 MCP를 실무 기준으로 다시 비교해보기

1) API가 더 적합한 경우

- 웹이나 모바일 앱에서 서버 기능을 호출할 때
- 외부 개발자에게 서비스 기능을 공개할 때
- 명확한 요청과 응답 규격이 필요한 서비스 간 통신
- 대규모 트래픽과 버전 관리가 필요한 운영 시스템
- 브라우저나 앱에서 직접 사용해야 할 때

2) MCP가 더 적합한 경우

- Claude Code나 Codex가 사내 도구를 사용해야 할 때
- AI가 로컬 파일이나 개발 도구에 접근해야 할 때
- 여러 AI 클라이언트에서 같은 기능을 재사용할 때
- 자연어 요청에 따라 AI가 적절한 기능을 선택해야 할 때
- 기존 API나 데이터베이스를 AI 작업 흐름에 연결할 때

3) 실제로는 같이 사용하는 경우가 많다

예를 들어 회사에 주문 조회 REST API가 이미 있다고 해보자.

웹 관리자 화면에서는 기존 REST API를 그대로 사용한다.

Claude Code나 사내 AI 에이전트가 주문을 조회해야 한다면, REST API를 호출하는 MCP Tool을 추가할 수 있다.

```text
웹 관리자
  ↓
REST API

Claude Code 또는 Codex
  ↓
MCP 서버
  ↓
같은 REST API
```

기존 API를 재사용하면서 AI 연결만 MCP로 표준화하는 구조다.

따라서 MCP를 도입한다고 기존 API 개발 경험이 필요 없어지는 것은 아니다.

오히려 실제 MCP 서버 개발에서는 API, 인증, 데이터베이스, 오류 처리, 네트워크에 대한 기존 백엔드 경험이 그대로 필요하다.

---

16. 직접 만들 때 자주 막힐 수 있는 부분

1) 절대 경로를 사용해야 한다

MCP 클라이언트가 어느 폴더에서 실행될지 항상 같지는 않다.

다음처럼 상대 경로를 사용하면 실행 위치에 따라 파일을 찾지 못할 수 있다.

```text
build/index.js
```

처음에는 다음처럼 절대 경로를 사용하는 것이 안전하다.

```text
C:/dev/project-search-mcp/build/index.js
```

2) TypeScript를 수정한 뒤 다시 빌드해야 한다

`src/index.ts`를 수정했더라도 MCP 클라이언트는 `build/index.js`를 실행한다.

수정 후에는 다시 빌드한다.

```powershell
npm run build
```

3) stdio 서버에서 console.log를 사용하지 않는다

표준 출력은 MCP 통신에 사용된다.

디버깅 로그는 다음처럼 표준 오류로 출력한다.

```typescript
console.error("디버깅 메시지");
```

4) Tool 이름과 설명을 구체적으로 작성한다

다음처럼 이름과 설명이 너무 추상적이면 AI가 언제 호출해야 할지 판단하기 어렵다.

```text
tool1
data 조회
```

다음처럼 목적과 검색 범위를 명확하게 쓰는 편이 좋다.

```text
search_projects
프로젝트 이름, 설명, 역할, 기술 스택에서 관련 프로젝트를 검색한다.
```

5) 쓰기 기능은 읽기 기능보다 더 신중해야 한다

이번 예제는 프로젝트 정보를 읽기만 한다.

하지만 실제 MCP 서버에서는 다음 기능도 만들 수 있다.

- 파일 수정
- Git 커밋
- 데이터베이스 변경
- 이슈 생성
- 배포 실행
- 이메일 전송

이런 Tool은 잘못 호출되면 실제 데이터가 바뀐다.

처음에는 조회 기능과 수정 기능을 분리하고, 수정이나 삭제 기능은 사용자 승인을 거치도록 설계하는 것이 좋다.

6) API 키를 코드에 직접 넣지 않는다

외부 API를 연결할 때는 API 키를 소스 코드에 넣지 않는 것이 기본이다.

환경변수로 전달하고, Git 저장소에 올라가지 않도록 주의해야 한다.

Claude Code와 Codex 모두 MCP 서버를 등록할 때 환경변수를 전달하는 방식을 지원한다.

7) AI의 호출 결과를 그대로 믿지 않는다

MCP를 연결했다고 결과가 항상 정확한 것은 아니다.

Tool에 전달된 입력값이 적절한지, 조회 결과가 완전한지, 쓰기 작업이 예상 범위를 넘지 않았는지 확인해야 한다.

AI 코딩 도구를 사용할 때와 마찬가지로 로그, 테스트, Git diff, 빌드 결과를 개발자가 직접 검증해야 한다.

---

17. 이 예제를 실제 프로젝트로 확장한다면

이번 예제는 배열에서 프로젝트를 검색하는 수준이다.

하지만 구조는 실제 업무용 MCP 서버로 그대로 확장할 수 있다.

1) 포트폴리오 MCP 서버

프로젝트 경력과 기술 스택을 저장해두고 채용공고와 맞는 프로젝트를 찾게 할 수 있다.

예를 들어 다음 Tool을 추가할 수 있다.

```text
search_projects
get_project_detail
find_projects_by_stack
match_job_posting
get_developer_profile
```

2) 사내 문서 검색 MCP 서버

Markdown, Notion, Google Drive, 사내 위키 같은 문서를 검색해 Claude Code나 Codex에 전달할 수 있다.

3) 데이터베이스 조회 MCP 서버

읽기 전용 계정을 사용해 주문, 통계, 장애 이력 등을 자연어로 조회할 수 있다.

운영 데이터베이스에 직접 쓰기 권한을 주는 것은 주의가 필요하다.

4) 유지보수 MCP 서버

서버 로그 검색, 배포 이력 확인, GitHub 이슈 조회, 모니터링 시스템 연결 기능을 하나의 MCP 서버에 묶을 수 있다.

5) 기존 REST API를 감싸는 MCP 서버

이미 운영 중인 API가 있다면 MCP 서버가 해당 API를 호출하고 결과를 AI가 사용하기 좋은 형태로 정리할 수 있다.

개인적으로는 이 방식이 실제 회사에서 MCP를 도입할 때 가장 현실적인 형태라고 본다.

---

18. MCP 서버 개발 경험이 채용공고에 등장하는 이유

MCP 서버의 예제 코드는 생각보다 짧다.

하지만 회사가 채용공고에서 원하는 MCP 경험은 단순히 Tool 하나를 등록해본 경험만을 뜻하지 않을 가능성이 높다.

실제로는 다음 능력을 함께 볼 수 있다.

1) 기존 API와 데이터베이스 구조를 이해할 수 있는가

2) AI가 사용하기 좋은 Tool 단위로 기능을 나눌 수 있는가

3) 입력 스키마와 설명을 명확하게 설계할 수 있는가

4) 인증과 권한을 안전하게 처리할 수 있는가

5) 오류, 타임아웃, 로그를 운영 수준으로 관리할 수 있는가

6) Claude Code나 Codex 같은 MCP 클라이언트에 연결할 수 있는가

7) AI가 잘못 호출할 가능성까지 고려할 수 있는가

MCP 자체만 보면 새로운 프로토콜이지만, 실제 서버를 안정적으로 만들려면 기존 백엔드 개발 경험이 중요하다.

AI가 코드를 대신 만들어주는 시대일수록, 어떤 기능을 열어줘도 되는지 판단하고 결과를 검증하는 개발자의 역할은 오히려 더 중요해진다.

---

19. 결론: MCP는 API를 없애는 기술이 아니다

간단한 프로젝트 검색 MCP 서버를 만들어 Claude Code와 Codex에 연결하는 흐름을 보면 MCP의 역할이 비교적 명확해진다.

REST API는 웹, 앱, 다른 서버가 기능과 데이터를 사용하도록 제공하는 인터페이스다.

MCP는 Claude Code나 Codex 같은 AI 클라이언트가 외부 기능을 발견하고, 입력값을 만들고, 결과를 받아 사용할 수 있도록 연결하는 공통 규격이다.

MCP 서버 안에서는 기존 API, 데이터베이스, 파일, 명령어를 그대로 사용할 수 있다.

따라서 MCP와 API 가운데 하나를 선택하는 문제가 아니다.

기존 시스템은 API로 운영하고, AI가 그 시스템을 사용해야 할 때 MCP를 연결하는 방식으로 보면 된다.

직접 간단한 서버를 만들어보니 MCP는 완전히 새로운 백엔드 기술이라기보다, 기존 개발 기능을 AI 작업 흐름 안으로 가져오는 연결 계층에 가까웠다.

한 줄로 정리하면 이렇다.

API가 기능을 제공하는 통로라면, MCP는 그 기능을 Claude Code와 Codex가 이해하고 사용할 수 있게 만드는 공통 어댑터다.

---

20. 참고한 공식 문서

- [Model Context Protocol 공식 소개](https://modelcontextprotocol.io/docs/getting-started/intro)
- [MCP 공식 TypeScript 서버 구축 가이드](https://modelcontextprotocol.io/docs/develop/build-server)
- [Claude Code MCP 연결 공식 문서](https://code.claude.com/docs/en/mcp)
- [Codex MCP 연결 공식 문서](https://developers.openai.com/codex/mcp)

Posted by 모과이IT
,

ChatGPT로 하나의 주제를 오래 다루다 보면 대화가 지나치게 길어진다.

처음에는 앞의 내용을 잘 기억하는 것처럼 보이지만, 대화가 계속 쌓이면 초기 검토안과 최종 결정이 섞이거나 이미 수정한 내용을 다시 기준으로 사용하는 경우가 생길 수 있다.

새 채팅을 시작하고 싶어도 지금까지 설명한 내용을 다시 입력해야 할 것 같아 기존 채팅을 계속 사용하는 경우도 많다.

결론부터 말하면 해결 방법은 간단하다.

긴 대화에서 현재 필요한 내용만 구조화한다.

그 내용을 Markdown 파일로 만든다.

Markdown 파일을 ChatGPT 프로젝트 소스로 올린다.

같은 프로젝트 안에서 새 채팅을 시작한다.

쉽게 말하면 수백 개의 메시지를 새 채팅으로 옮기는 것이 아니다.

기존 대화에서 확정된 내용, 아직 결정하지 않은 내용, 중간에 변경된 결정, 앞으로 지켜야 할 기준만 하나의 문서로 만든 뒤 새 채팅이 그 문서를 참고하도록 하는 방식이다.

1. 왜 긴 대화를 계속 사용하는 것이 불편한가

대화가 길어진다고 해서 이전 내용을 모두 잃어버리는 것은 아니다.

하지만 검토 과정에서 나온 여러 버전의 내용이 한 채팅 안에 계속 남아 있기 때문에, 어느 내용이 현재 기준인지 구분하기 어려워질 수 있다.

1) 초기안과 최종 결정이 섞일 수 있다

예를 들어 가상의 쇼핑몰 개발 프로젝트를 오랫동안 논의했다고 가정해 보자.

처음에는 PHP와 MySQL을 유지하고 자체 회원가입과 A사 결제 모듈을 사용하는 방향을 검토했다.

하지만 논의를 진행하면서 최종 방향이 바뀌었다.

프론트엔드는 Next.js로 리뉴얼하고, 데이터베이스는 PostgreSQL을 사용하며, 로그인은 소셜 로그인 중심으로 바꾸고, 결제 모듈은 B사를 사용하기로 결정했다.

긴 대화 안에는 초기안과 최종안이 모두 남아 있다.

이 상태에서 이전 내용을 다시 질문하면 초기 검토안이 현재 결정처럼 답변에 포함될 가능성이 있다.

2) 수정 전 내용이 다시 등장할 수 있다

처음 작성한 문서나 계획을 여러 차례 수정했더라도, 수정 전 내용이 채팅 안에서 사라지는 것은 아니다.

그래서 새 작업을 요청했을 때 이전 표현이나 폐기한 기준이 다시 섞여 나올 수 있다.

3) 새 채팅에서 처음부터 설명해야 할 것처럼 느껴진다

대화가 길어질수록 새 채팅으로 이동하기가 부담스러워진다.

지금까지의 배경, 결정 과정, 주의사항을 다시 설명해야 할 것 같기 때문이다.

하지만 핵심 내용을 기준 문서로 만들어두면 과거 대화를 처음부터 다시 설명할 필요가 없다.

2. 단순 요약보다 기준 문서가 필요하다

긴 대화를 새 채팅에서 이어가기 위해 “지금까지 대화한 내용을 요약해줘”라고 요청할 수도 있다.

하지만 단순 요약만으로는 부족할 수 있다.

1) 최종 결론만 남기면 변경 이유가 사라진다

쇼핑몰을 Next.js로 개발하기로 했다는 결론만 남기면, 왜 기존 PHP 유지안에서 Next.js 리뉴얼로 변경했는지 알 수 없다.

나중에 기술 선택을 다시 검토할 때 같은 논의를 반복할 수 있다.

2) 아직 결정하지 않은 내용이 빠질 수 있다

검색엔진 도입 여부나 다국어 지원 시점처럼 아직 결정하지 않은 항목도 중요하다.

이런 항목이 요약에서 빠지면 새 채팅이 이미 확정된 기능처럼 가정할 수 있다.

3) 정보의 상태를 구분해야 한다

새 채팅에 넘길 문서는 단순한 줄거리보다 현재 상태를 알려주는 문서에 가까워야 한다.

다음 내용을 구분해서 넣는 것이 좋다.

확정된 내용

검토 중인 내용

보류한 내용

초기안에서 변경된 내용

원본 확인이 필요한 내용

앞으로 반복하면 안 되는 잘못된 답변

3. 프로젝트 소스용 Markdown 파일 만들기

기존의 긴 채팅에서 ChatGPT에게 프로젝트 소스용 문서를 만들어달라고 요청한다.

내 기준으로는 다음과 같이 요청하는 방식이 가장 이해하기 쉽다.

“이 채팅의 처음부터 현재까지 내용을 새 채팅에서 이어갈 수 있도록 프로젝트 소스용 Markdown 파일로 정리해 주세요.

확정사항, 검토 중인 사항, 보류한 사항, 변경된 결정, 충돌 해결 기준, 원본 확인이 필요한 내용, 앞으로 하지 말아야 할 답변을 구분해 주세요.

추정한 내용은 확정하지 말고 별도로 표시해 주세요.”

1) Markdown 파일은 프로젝트 소스용으로 사용한다

ChatGPT 프로젝트에서 참고할 기준 문서라면 Markdown 파일이 편하다.

제목과 항목 구조가 명확하고, 내용 수정도 쉽다.

파일명은 문서의 역할이 바로 보이도록 만드는 것이 좋다.

예를 들면 다음과 같다.

쇼핑몰_리뉴얼_전체대화_최종본.md

자격증공부_진행상황_기준문서.md

여행계획_확정사항_최종본.md

2) Word 파일은 사람이 읽는 보관용으로 사용할 수 있다

같은 내용을 사람이 자주 읽거나 직접 수정해야 한다면 Word 파일도 함께 만들 수 있다.

역할은 다음처럼 나눌 수 있다.

Markdown 파일은 ChatGPT 프로젝트 소스용이다.

Word 파일은 사람이 읽고 수정하는 보관용이다.

내용이 같다면 프로젝트에는 Markdown 최종본 하나만 올리는 편이 관리하기 쉽다.

4. Markdown 문서는 상태 중심으로 구성한다

프로젝트 소스 문서는 과거 대화를 시간순으로 전부 옮기는 방식보다 현재 상태를 중심으로 구성하는 것이 좋다.

1) 문서 기본정보

문서 앞부분에는 문서의 역할과 기준 시점을 적는다.

예시는 다음과 같다.

제목: 쇼핑몰 리뉴얼 전체 대화 최종본

원본 채팅: 쇼핑몰 리뉴얼 개발 계획

기준일: 2026년 6월 20일

문서 용도: 프로젝트 안의 새 채팅에서 기존 논의를 이어가기 위한 기준 자료

기준 날짜를 넣어두면 나중에 문서가 최신 상태인지 판단하기 쉽다.

2) 항목마다 정보 상태를 표시한다

각 내용이 얼마나 확실한지 표시하는 것도 중요하다.

다음과 같은 상태값을 사용할 수 있다.

[확정]

[검토 중]

[보류]

[대화 재구성]

[원본 확인 필요]

예를 들면 다음과 같다.

[확정] 프론트엔드는 Next.js를 사용한다.

[확정] 데이터베이스는 PostgreSQL을 사용한다.

[검토 중] 검색엔진 도입 여부는 아직 결정하지 않았다.

[보류] 다국어 지원은 2차 개발로 미뤘다.

[원본 확인 필요] 결제 모듈의 정확한 요금제는 공식 계약서를 확인해야 한다.

이렇게 구분하면 새 채팅이 검토 중인 내용을 확정사항처럼 답하는 일을 줄일 수 있다.

3) 원본과 추정 내용을 구분한다

오랫동안 이어진 대화에는 실제 자료를 보고 확인한 내용과 대화만으로 추정한 내용이 섞일 수 있다.

실제 코드, 계약서, 검사 결과지, 설정 파일처럼 원본이 존재하는 내용은 원본을 가장 높은 기준으로 두는 것이 안전하다.

대화 내용만으로 재구성한 정보는 확정하지 말고 다음처럼 표시한다.

[대화 재구성] 당시 대화 흐름으로 보면 이 기능은 2차 개발로 미룬 것으로 보인다.

[원본 확인 필요] 실제 적용 여부는 최신 저장소에서 확인해야 한다.

5. 변경된 결정은 따로 남긴다

긴 대화에서는 처음 검토한 내용과 최종 결정이 달라지는 경우가 많다.

이전 내용을 모두 지우기보다 무엇이 어떻게 바뀌었는지 따로 정리하는 것이 좋다.

1) 기술 변경사항

예를 들면 다음과 같다.

프론트엔드

초기 검토안: 기존 PHP 화면 유지

최종 결정: Next.js로 리뉴얼

데이터베이스

초기 검토안: MySQL 유지

최종 결정: PostgreSQL 사용

로그인

초기 검토안: 자체 회원가입

최종 결정: 소셜 로그인 중심

결제 모듈

초기 검토안: A사

최종 결정: B사

이미지 저장

초기 검토안: 서버 디스크

최종 결정: 오브젝트 스토리지

2) 변경 이유도 함께 적는다

최종 결과만 적는 것보다 변경 이유를 짧게 남기는 것이 좋다.

예를 들어 “기존 PHP가 나빠서 변경했다”가 아니라 다음처럼 적는다.

[확정] 신규 화면과 관리자 기능의 확장성을 고려해 Next.js 리뉴얼로 변경했다.

[확정] 배포 환경과 백업 구조를 고려해 이미지 저장 위치를 오브젝트 스토리지로 변경했다.

이렇게 하면 나중에 같은 결정을 다시 검토할 때 과거 논의를 반복하지 않아도 된다.

6. 프로젝트에 파일과 지침을 넣는다

Markdown 파일을 만들었다면 ChatGPT 프로젝트 안에 추가한다.

1) 프로젝트 소스에 Markdown 파일을 올린다

ChatGPT에서 사용할 프로젝트를 연다.

프로젝트의 소스 영역에서 파일 추가를 선택한다.

생성한 Markdown 파일을 올린다.

파일명이 프로젝트 소스 목록에 표시되는지 확인한다.

프로젝트에는 최신 Markdown 최종본을 중심으로 두는 것이 좋다.

최초본, 중간본, 최종본을 모두 올려두면 서로 다른 내용이 함께 검색될 수 있다.

2) 기존 채팅을 프로젝트로 이동할 수도 있다

이동 가능한 채팅이라면 채팅 메뉴에서 프로젝트로 이동을 선택할 수 있다.

프로젝트로 이동한 채팅은 해당 프로젝트의 지침과 파일 문맥을 함께 사용하게 된다.

채팅에 프로젝트로 이동 메뉴가 없거나 이동할 수 없는 유형이라면, 프로젝트 안에서 새 채팅을 시작하고 Markdown 파일을 기준으로 이어가면 된다.

3) 프로젝트 지침에는 핵심 원칙만 넣는다

상세한 기록은 Markdown 파일에 넣고, 프로젝트 지침에는 모든 답변에 적용할 핵심 규칙만 넣는다.

예시는 다음과 같다.

“쇼핑몰 관련 질문에는 프로젝트 소스의 최신 전체 대화 최종본을 우선 참고한다.

실제 저장소의 최신 코드와 설정 파일을 가장 높은 근거로 사용한다.

초기 검토안과 최종 결정을 구분한다.

아직 결정하지 않은 기능은 구현 완료로 가정하지 않는다.

확인되지 않은 버전이나 설정은 추정하지 말고 확인 필요로 표시한다.”

프로젝트 지침을 너무 길게 작성하면 나중에 관리하기가 어려워진다.

세부 내용은 소스 파일에 넣고, 프로젝트 지침에는 우선순위와 금지사항만 남기는 편이 좋다.

7. 새 채팅에서 기존 맥락 이어가기

파일과 프로젝트 지침을 설정했다면 같은 프로젝트 안에서 새 채팅을 시작한다.

새 채팅의 첫 질문에서는 어떤 소스를 기준으로 답해야 하는지 한 번 알려주는 것이 좋다.

예를 들면 다음과 같다.

“프로젝트 소스의 ‘쇼핑몰 리뉴얼 전체 대화 최종본’과 실제 저장소 자료를 먼저 참고해 기존 대화를 이어가 주세요.

초기 검토안과 최종 결정을 구분하고, 확인되지 않은 구현 상태는 추정하지 말아 주세요.”

이후부터는 과거 내용을 다시 길게 설명하지 않고 바로 질문할 수 있다.

예를 들면 다음과 같다.

“현재 데이터베이스 구조를 기준으로 재고 이력 테이블을 설계해 주세요.”

“지금까지 보류된 기능만 정리해 주세요.”

“B사 결제 모듈에 부분 취소 기능을 추가하는 방법을 검토해 주세요.”

8. 제대로 연결됐는지 검증해야 한다

프로젝트 소스를 올렸다고 해서 바로 모든 내용이 원하는 기준으로 적용됐다고 가정하면 안 된다.

처음에는 간단한 검증 질문을 해보는 것이 좋다.

1) 확정사항과 미정사항을 구분하게 한다

예를 들면 다음과 같이 질문한다.

“프로젝트 소스를 기준으로 확정된 기술 스택, 변경된 결정, 아직 검토 중인 항목을 구분해 주세요.”

답변이 Markdown 최종본과 일치한다면 정상적으로 연결된 것으로 볼 수 있다.

2) 초기안이 다시 나오는지 확인한다

이미 폐기한 기술이나 계획이 현재 기준처럼 답변에 등장하지 않는지 확인한다.

잘못된 초기안이 다시 나온다면 다음 사항을 점검한다.

구형 소스 파일이 함께 올라가 있는지

프로젝트 밖에서 채팅을 시작했는지

프로젝트 지침이 저장됐는지

최신 Markdown 파일 업로드가 완료됐는지

파일명이 비슷한 중간본과 최종본이 함께 있는지

3) 실제 원본과 충돌하면 원본을 우선한다

기준 문서도 사람이 정리한 자료이기 때문에 실제 원본과 다를 수 있다.

최신 저장소 코드, 실제 설정 파일, 공식 결과지처럼 원본 자료가 있다면 원본을 우선해야 한다.

Markdown 문서에는 이런 우선순위도 적어두는 것이 좋다.

“기준 문서와 실제 저장소의 최신 코드가 충돌하면 최신 코드를 우선한다.”

“대화 재구성 내용과 공식 결과지가 충돌하면 공식 결과지를 우선한다.”

9. 개인정보와 민감정보를 확인한다

긴 대화를 문서로 만들 때는 불필요한 개인정보가 들어가지 않았는지 확인해야 한다.

특히 외부에 공개하거나 블로그 사례로 사용할 문서라면 더 조심해야 한다.

1) 삭제하거나 가상 정보로 바꿀 내용

실명과 연락처

주소와 계좌번호

병원 진료정보

회사 내부자료

고객 개인정보

서버 접속정보

API 키와 비밀번호

실제 계약 금액

2) 블로그 사례는 가상 상황으로 바꾼다

실제 가족의 건강 상태나 회사 내부 프로젝트를 그대로 사례로 쓰면 불필요한 정보가 공개될 수 있다.

사용 방법을 설명할 때는 쇼핑몰 개발, 여행 계획, 자격증 공부, 이사 준비 같은 가상 사례로 바꾸는 편이 안전하다.

핵심 원리만 전달되면 실제 개인정보를 사례로 사용할 필요는 없다.

10. 결론

ChatGPT 대화가 너무 길어졌을 때는 기존 채팅을 무작정 계속 사용하는 것보다 현재 상태를 기준 문서로 정리해 새 채팅으로 넘어가는 편이 효율적이다.

전체 과정은 다음과 같다.

긴 대화에서 확정사항과 미정사항을 구분한다.

프로젝트용 Markdown 파일을 만든다.

변경된 결정과 원본 확인이 필요한 내용을 표시한다.

Markdown 파일을 프로젝트 소스로 올린다.

프로젝트 지침에 우선순위와 금지사항을 넣는다.

같은 프로젝트 안에서 새 채팅을 시작한다.

검증 질문으로 제대로 연결됐는지 확인한다.

기존의 긴 채팅은 전체 논의 과정을 담은 기록이다.

Markdown 파일은 현재의 최종 상태를 정리한 기준 문서다.

새 채팅은 그 기준 문서를 바탕으로 다음 작업을 이어가는 공간이다.

한 줄로 정리하면 이렇다.

긴 대화를 그대로 옮기려고 하지 말고, 현재 필요한 내용을 하나의 기준 문서로 만든 뒤 프로젝트 안의 새 채팅에서 이어가면 된다.

Posted by 모과이IT
,

최근 AI 코딩 도구를 이것저것 써보면서 가장 많이 비교하게 되는 것이 Claude Code와 Codex다.

처음에는 Codex도 결국 터미널에서 실행하는 CLI 도구라고 생각했다.

하지만 실제로 Codex CLI와 Codex App을 같이 써보니 역할이 조금 다르게 느껴졌다.

결론부터 말하면, Codex CLI는 “개발자가 터미널 안에서 빠르게 작업 지시를 내리는 도구”에 가깝고, Codex App은 “여러 작업 흐름을 한눈에 관리하고 검토하는 작업 공간”에 가깝다.

둘 중 하나만 써야 하는 관계라기보다는, 같은 프로젝트를 다루더라도 상황에 따라 서로 보완하는 관계에 가깝다고 느꼈다.

OpenAI 공식 문서에서도 Codex CLI는 로컬 터미널에서 실행되며, 선택한 디렉터리의 코드를 읽고 수정하고 실행할 수 있는 코딩 에이전트로 설명된다.

반면 Codex App은 여러 Codex 작업 스레드를 병렬로 다루고, worktree, 자동화, Git 기능 등을 포함한 데스크톱 작업 환경으로 소개된다.

1. 처음에는 CLI만 있으면 충분하다고 생각했다

개발자 입장에서 가장 익숙한 환경은 역시 터미널이다.

VS Code를 열고, 프로젝트 폴더로 이동한 다음, 터미널에서 Codex CLI를 실행하면 바로 현재 프로젝트 기준으로 작업을 시킬 수 있다.

예를 들어 Next.js 프로젝트에서 특정 페이지 수정, 에러 분석, 컴포넌트 리팩토링, README 작성, 문서 정리 같은 작업은 CLI 방식이 편하다.

이미 내가 프로젝트 구조를 알고 있고, 수정할 범위도 어느 정도 알고 있다면 굳이 별도의 앱 화면을 열 필요 없이 터미널에서 바로 지시하는 것이 빠르다.

이 점은 Claude Code를 쓸 때와도 비슷하다.

개발자가 터미널 안에서 AI에게 작업을 맡기고, 결과를 확인하고, 다시 지시하는 흐름이다.

그래서 처음 Codex를 접했을 때도 “그냥 CLI만 있으면 되는 것 아닌가?”라는 생각이 들었다.

하지만 Codex App을 써보니 CLI와는 다른 장점이 있었다.

2. Codex CLI의 장점은 빠른 진입과 개발자 친화성이다

Codex CLI의 가장 큰 장점은 현재 작업 중인 폴더와 바로 연결된다는 점이다.

개발자는 이미 VS Code나 터미널에서 프로젝트를 열어놓고 있기 때문에, 그 상태에서 바로 명령을 내릴 수 있다.

개인적으로는 다음과 같은 작업에 CLI가 잘 맞았다.

1) 현재 프로젝트의 에러를 빠르게 분석할 때

개발하다 보면 터미널에 에러 로그가 뜨는 경우가 많다.

이때 CLI 환경에서는 바로 로그를 보고, 이어서 Codex에게 원인 분석을 시킬 수 있다.

예를 들어 빌드 실패, 타입 에러, API 호출 실패, 패키지 설치 문제처럼 터미널 로그를 기반으로 원인을 찾아야 하는 경우에는 CLI가 편하다.

왜냐하면 개발자가 이미 터미널을 보고 있는 상황이기 때문이다.

2) 특정 파일이나 폴더 기준으로 수정할 때

특정 컴포넌트 하나를 수정하거나, 특정 API 라우트만 고치거나, README만 정리하는 작업도 CLI가 잘 맞는다.

작업 범위가 작고 명확할수록 CLI가 빠르다.

예를 들어 Header 컴포넌트 모바일 레이아웃 수정, API 라우트 에러 처리 보완, README 로컬 실행 방법 추가, docs 폴더 문서 정리 같은 작업은 App까지 열지 않아도 된다.

터미널에서 바로 지시하고 결과를 확인하면 충분하다.

3) 터미널 로그를 보면서 바로 추가 지시를 내릴 때

CLI는 터미널 안에서 작업이 이어지기 때문에, 로그를 확인하고 바로 다음 지시를 내리기 좋다.

예를 들어 빌드가 실패하면 실패 로그를 보고 다시 수정 지시를 내릴 수 있다.

테스트가 실패하면 실패한 테스트 기준으로 다시 원인을 찾게 할 수 있다.

이런 흐름은 기존 개발 방식과 크게 다르지 않다.

4) Git 상태를 확인하면서 작은 단위로 변경할 때

AI 코딩 도구를 쓸 때는 Git 변경 사항 확인이 중요하다.

CLI 환경에서는 git status, git diff 같은 명령어를 바로 확인하면서 작업 범위를 좁혀갈 수 있다.

작은 단위로 수정하고, diff를 확인하고, 다시 지시하는 방식에서는 CLI가 자연스럽다.

5) 기존 개발 습관을 크게 바꾸지 않고 AI를 붙이고 싶을 때

CLI는 개발자의 기존 작업 흐름을 거의 방해하지 않는다.

기존처럼 VS Code를 열고, 터미널을 보고, Git 상태를 확인하면서 그 안에 AI 에이전트를 하나 붙이는 느낌이다.

그래서 “나는 이미 터미널 중심으로 개발하는 데 익숙하다”면 Codex CLI가 더 자연스럽다.

특히 빠르게 명령하고, 결과를 확인하고, 바로 다음 작업을 이어가는 방식에서는 CLI가 편하다.

3. Codex App의 장점은 작업 관리와 시각적 검토다

반면 Codex App은 조금 다르다.

CLI가 “터미널 안의 에이전트”라면, Codex App은 “Codex 작업들을 관리하는 데스크톱 작업 공간”에 가깝다.

공식 문서에서는 Codex App을 여러 스레드를 병렬로 다루고, worktree와 Git 기능을 포함한 command center라고 설명한다.

특히 Windows용 Codex App도 프로젝트 간 이동, 병렬 에이전트 스레드, Git 기능, artifact preview, plugin, skill 등을 지원하는 방향으로 소개되고 있다.

이 부분이 생각보다 중요했다.

1) 여러 작업 흐름을 한눈에 보기 좋다

CLI에서는 한 터미널 세션 안에서 작업하는 느낌이 강하다.

물론 여러 터미널을 열 수도 있다.

하지만 작업이 많아질수록 어떤 터미널에서 무슨 작업을 시켰는지 헷갈릴 수 있다.

반면 App은 여러 작업 흐름을 UI에서 확인하기 쉽다.

프로젝트를 여러 개 다루거나, 같은 프로젝트 안에서도 기능 A, 문서 B, 버그 C처럼 작업을 나눠서 진행할 때 관리하기가 더 편하다.

2) 변경 사항 검토 흐름이 더 자연스럽다

AI가 코드를 수정했을 때 개발자는 결국 diff를 보고 판단해야 한다.

Codex App은 이런 검토 흐름을 더 시각적으로 다루기 좋다.

CLI에서도 확인은 가능하지만, App은 “AI가 무슨 작업을 했고, 어떤 변경이 생겼는지”를 한 공간에서 보는 느낌이 강하다.

특히 여러 작업을 동시에 맡겼을 때는 어떤 작업에서 어떤 변경이 생겼는지 분리해서 보는 것이 중요하다.

3) 여러 프로젝트를 동시에 다룰 때 유리하다

하나의 프로젝트만 집중해서 작업한다면 CLI만으로도 충분할 수 있다.

하지만 포트폴리오 사이트, 쇼핑몰 프로젝트, 중고거래 플랫폼, 문서 번역 도구처럼 여러 프로젝트를 동시에 관리한다면 이야기가 달라진다.

이때는 단순히 코드를 수정하는 것보다 작업 흐름을 관리하는 능력이 중요해진다.

어느 프로젝트에서 어떤 작업을 시켰는지, 어떤 변경은 검토가 끝났는지, 어떤 작업은 다시 지시해야 하는지 계속 판단해야 한다.

이런 상황에서는 Codex App이 더 편하게 느껴질 수 있다.

4. 이미지나 자료를 같이 다룰 때는 App이 더 편하게 느껴질 수 있다

CLI는 텍스트 기반 작업에 강하다.

코드, 로그, 명령어, 파일 수정처럼 텍스트 중심의 작업은 CLI가 빠르다.

하지만 실제 개발을 하다 보면 텍스트만으로 설명하기 애매한 경우가 많다.

1) UI 문제를 설명할 때

예를 들어 화면 캡처를 보여주면서 “여기 레이아웃이 깨졌다”, “이 버튼 간격이 이상하다”, “이 UI를 이런 식으로 바꾸고 싶다”라고 설명하고 싶을 때가 있다.

이럴 때는 앱 형태의 인터페이스가 더 편하게 느껴질 수 있다.

개발자는 코드를 설명하는 것보다 화면을 보여주는 것이 더 빠를 때가 많다.

특히 프론트엔드, 관리자 페이지, 포트폴리오 사이트, 랜딩페이지처럼 UI가 중요한 프로젝트에서는 이런 차이가 꽤 크게 느껴진다.

2) 이미지나 참고 자료를 같이 볼 때

기획 화면, 참고 이미지, 디자인 캡처, 에러 화면 같은 자료를 같이 보면서 설명해야 할 때도 App 형태가 더 편할 수 있다.

CLI는 텍스트 중심이라 빠르지만, 화면이나 이미지를 함께 다루는 흐름에서는 App이 더 직관적이다.

말로 길게 설명하는 것보다 이미지를 보여주는 것이 빠른 경우가 있기 때문이다.

3) 최종 수정은 코드에서 검증해야 한다

물론 최종 수정은 여전히 코드에서 검증해야 한다.

AI가 화면을 보고 제안한 내용이 실제 코드에 제대로 반영됐는지도 개발자가 확인해야 한다.

하지만 문제를 설명하고 방향을 잡는 단계에서는 App 쪽이 더 직관적일 수 있다.

5. CLI는 작업 실행, App은 작업 조율에 가깝다

내가 느낀 차이를 한 문장으로 정리하면 이렇다.

Codex CLI는 작업을 바로 실행하기 좋은 도구이고, Codex App은 여러 작업을 조율하고 검토하기 좋은 도구다.

1) CLI는 빠른 실행에 강하다

CLI는 빠르다.

개발자가 이미 알고 있는 프로젝트에서 “이거 고쳐줘”, “이 파일 정리해줘”, “에러 원인 찾아줘” 같은 지시를 내릴 때 좋다.

작업 범위가 명확하고, 바로 수정하고, 바로 결과를 확인해야 하는 경우에는 CLI가 편하다.

2) App은 넓게 보는 데 강하다

App은 넓게 보기 좋다.

여러 프로젝트를 동시에 다루거나, 여러 에이전트 작업을 병렬로 돌리거나, 변경 사항을 UI에서 확인하고 싶을 때 장점이 있다.

특히 작업이 많아질수록 단순 실행보다 조율과 검토가 중요해진다.

3) 둘 중 하나만 고를 문제는 아니다

그래서 둘 중 하나를 선택하는 문제라기보다, 작업 성격에 따라 나눠 쓰는 것이 좋다고 느꼈다.

CLI는 손에 익은 실행 도구이고, App은 시야를 넓혀주는 관리 도구에 가깝다.

6. Claude Code 사용자 관점에서 본 Codex의 인상

나는 Claude Code를 먼저 많이 사용해 왔다.

Claude Code는 터미널 중심의 개발 흐름에 상당히 잘 맞는다.

프로젝트 폴더 안에서 명령하고, 파일을 수정하고, 세션을 이어가는 방식이 익숙하다.

그런데 Codex는 CLI뿐 아니라 App까지 같이 제공되면서, 조금 더 UI 친화적인 방향도 함께 가져가는 느낌이 있었다.

1) Claude Code는 터미널 안의 강력한 개발 에이전트 느낌이다

Claude Code는 터미널 중심 개발자에게 상당히 자연스럽다.

프로젝트 폴더 안에서 명령하고, 파일을 수정하고, 결과를 확인하는 방식이 익숙하다.

이런 점에서 Codex CLI와 사용 감각이 비슷한 부분이 있다.

2) Codex App은 별도의 AI 작업 공간 느낌이 있다

Codex App은 AI 개발 작업을 관리하는 별도의 워크스페이스를 제공하는 느낌에 가깝다.

개발자에게 CLI는 익숙하지만, 모든 작업을 터미널에서만 관리하는 것이 항상 편한 것은 아니다.

특히 여러 프로젝트를 동시에 열거나, 여러 작업을 병렬로 진행하거나, 작업 결과를 한눈에 검토하려면 UI가 있는 쪽이 편할 수 있다.

3) Codex는 CLI와 App을 같이 가져가는 점이 인상적이었다

Claude Code가 터미널 중심의 강력한 흐름이라면, Codex는 CLI와 App을 함께 가져가면서 사용 선택지를 넓혀주는 느낌이 있었다.

이 차이는 생각보다 크다.

개발자가 AI 코딩 도구를 어떻게 쓰느냐에 따라 CLI가 더 편할 수도 있고, App이 더 편할 수도 있다.

7. 실제로는 이렇게 나눠 쓰는 것이 좋아 보인다

내 기준으로는 다음과 같이 나눠 쓰는 방식이 가장 현실적이다.

1) 빠른 코드 수정과 에러 분석은 Codex CLI

이미 VS Code를 열고 있고, 터미널에서 에러를 보고 있다면 CLI에서 바로 처리하는 것이 빠르다.

작업 범위가 명확하고 바로 결과를 확인해야 하는 경우에는 CLI가 잘 맞는다.

2) 여러 작업 스레드 관리와 변경 검토는 Codex App

기능 A, 문서 B, 버그 C처럼 작업을 나눠서 진행할 때는 App 쪽이 관리하기 편하다.

작업이 많아질수록 중요한 것은 단순한 코드 생성이 아니라 작업 흐름을 놓치지 않는 것이다.

3) UI 관련 설명이나 자료 기반 요청은 App

화면 캡처나 이미지 자료를 같이 보면서 설명해야 하는 경우에는 앱 형태가 더 자연스럽다.

특히 프론트엔드나 관리자 페이지처럼 화면 결과물이 중요한 작업에서는 App이 더 편하게 느껴질 수 있다.

4) 기존 VS Code 터미널 중심 작업은 CLI

기존 개발 습관을 크게 바꾸지 않고 AI를 붙이고 싶다면 CLI부터 쓰는 것이 좋다.

터미널 중심으로 개발하는 사람에게는 이 방식이 가장 진입장벽이 낮다.

5) 장기적으로 여러 프로젝트를 동시에 관리할 때는 App

여러 프로젝트를 동시에 다루는 경우에는 App의 장점이 커진다.

작업이 많아질수록 “어느 프로젝트에서 어떤 작업을 하고 있는지”를 관리하는 것이 중요하기 때문이다.

즉, CLI는 손에 익은 도구이고, App은 시야를 넓혀주는 도구다.

개발자가 혼자 하나의 프로젝트만 집중해서 작업한다면 CLI만으로도 충분할 수 있다.

하지만 여러 프로젝트를 동시에 다루거나, AI 에이전트에게 여러 작업을 나눠 맡기는 방식으로 간다면 App의 필요성이 커진다.

8. AI 코딩 도구는 이제 코드 생성기만은 아니다

Codex CLI와 Codex App을 같이 써보면서 다시 느낀 점은, AI 코딩 도구를 단순히 코드 생성기로 보면 안 된다는 것이다.

예전에는 ChatGPT에게 코드를 물어보고, 복사해서 붙여넣고, 직접 수정하는 방식이 많았다.

하지만 지금은 AI가 프로젝트 폴더를 읽고, 파일을 수정하고, 테스트를 실행하고, Git 변경 사항까지 다룰 수 있는 방향으로 가고 있다.

1) AI가 프로젝트 단위로 작업하기 시작했다

요즘 AI 코딩 도구는 단순히 코드 한 조각을 만들어주는 수준을 넘어가고 있다.

프로젝트 폴더를 읽고, 파일을 수정하고, 테스트를 실행하고, Git 변경 사항까지 확인하는 방향으로 가고 있다.

이제 AI는 단순한 코드 답변기가 아니라 실제 개발 흐름 안으로 들어오고 있다.

2) 중요한 것은 작업 단위와 검토 방식이다

이제 중요한 것은 “AI가 코드를 짜주느냐”가 아니다.

더 중요한 것은 개발자가 AI에게 어떤 단위로 일을 맡기고, 결과를 어떻게 검토하고, 어디까지 신뢰할 것인가다.

일을 너무 크게 맡기면 결과를 검토하기 어렵다.

반대로 일을 적절히 나누면 AI의 속도와 개발자의 검증 능력을 같이 활용할 수 있다.

3) AI 코딩 도구의 중심은 작업 관리와 검증으로 이동하고 있다

CLI는 개발자가 직접 명령하고 확인하는 흐름에 가깝다.

App은 여러 AI 작업을 관리하는 흐름에 가깝다.

결국 AI 코딩 도구의 중심은 단순한 질문과 답변이 아니라, 작업 관리와 검증으로 이동하고 있다고 본다.

9. 아직은 개발자의 검증 능력이 더 중요하다

Codex CLI든 Codex App이든, AI가 만들어낸 결과를 그대로 믿으면 안 된다.

특히 실제 서비스나 포트폴리오 프로젝트에서는 더 그렇다.

AI는 빠르게 코드를 작성할 수 있지만, 다음과 같은 부분은 개발자가 직접 확인해야 한다.

1) 요구사항을 제대로 반영했는가

AI가 만든 결과가 겉으로는 그럴듯해 보여도 실제 요구사항과 다를 수 있다.

기능이 동작하는 것처럼 보여도 세부 조건을 놓치는 경우가 있다.

그래서 요구사항을 제대로 반영했는지는 개발자가 직접 확인해야 한다.

2) 기존 기능을 깨뜨리지 않았는가

AI가 한 파일을 고치면서 다른 기능에 영향을 줄 수도 있다.

특히 공통 컴포넌트, 공통 유틸, API 응답 구조를 수정할 때는 주의가 필요하다.

작은 수정처럼 보여도 전체 서비스에서는 영향 범위가 클 수 있다.

3) 보안상 위험한 코드는 없는가

인증, 권한 체크, 서버 API, 관리자 기능 같은 부분은 특히 조심해야 한다.

AI가 빠르게 코드를 만들어도 보안상 위험한 코드가 섞일 수 있다.

겉으로 동작한다고 해서 안전한 코드는 아니다.

4) 환경변수나 API 키가 노출되지 않았는가

환경변수, API 키, 토큰, 비밀값이 코드에 직접 들어가 있으면 안 된다.

AI가 예시 코드를 만들다가 실수로 하드코딩 형태를 제안할 수도 있다.

이런 부분은 반드시 개발자가 직접 확인해야 한다.

5) 빌드와 배포가 정상적으로 되는가

코드가 보기에는 괜찮아도 실제 빌드에서 실패할 수 있다.

로컬 실행, 타입 체크, 빌드, 배포까지 확인해야 실제로 사용할 수 있는 결과물이라고 볼 수 있다.

6) 사용자가 실제로 쓸 수 있는 UX인가

AI가 만든 화면이 기능적으로는 동작해도 사용성이 부족할 수 있다.

버튼 위치, 안내 문구, 에러 메시지, 모바일 화면, 로딩 상태 같은 부분은 개발자가 직접 확인해야 한다.

특히 포트폴리오 프로젝트나 실제 서비스에서는 UX 완성도가 중요하다.

7) Git 변경 사항이 의도한 범위 안에 있는가

AI가 의도하지 않은 파일까지 수정했을 가능성도 있다.

그래서 Git diff를 보고 변경 사항이 내가 지시한 범위 안에 있는지 확인하는 과정이 필요하다.

AI 코딩 도구를 잘 쓴다는 것은 단순히 명령을 많이 내리는 것이 아니다.

일을 작은 단위로 쪼개고, 결과를 검토하고, 필요하면 다시 지시하는 능력이 중요하다.

이 점에서 경력 개발자의 역할은 오히려 더 중요해진다.

AI가 초안을 빠르게 만들수록, 그 결과가 맞는지 판단하는 사람의 실력이 더 드러난다.

10. 결론: CLI와 App은 경쟁 관계가 아니라 보완 관계다

Codex CLI와 Codex App을 같이 써보니, 둘은 같은 Codex를 쓰지만 사용 감각은 꽤 다르다.

CLI는 빠르고 직접적이다.

개발자가 이미 작업 중인 프로젝트 안에서 바로 AI에게 지시하고 결과를 확인하기 좋다.

App은 넓고 관리적이다.

여러 작업을 병렬로 보고, 변경 사항을 검토하고, 프로젝트 단위로 작업 흐름을 관리하기 좋다.

그래서 내 결론은 이렇다.

1) CLI는 개발자의 손에 가까운 도구다

Codex CLI는 바로 명령하고 바로 결과를 확인하는 데 좋다.

터미널 중심 개발자라면 CLI가 먼저 편하게 느껴질 가능성이 높다.

작업 범위가 명확하고 빠른 실행이 필요할 때는 CLI가 잘 맞는다.

2) App은 개발자의 작업판에 가까운 도구다

Codex App은 여러 작업을 펼쳐놓고 관리하기 좋다.

AI 에이전트를 여러 작업에 나눠 쓰고, 프로젝트를 병렬로 관리하고, 결과물을 시각적으로 검토하고 싶다면 App도 충분히 의미가 있다.

3) 둘을 같이 쓰면 개발 워크플로우가 넓어진다

앞으로 AI 코딩 도구는 더 이상 “코드를 대신 짜주는 도구”에 머물지 않을 것이다.

개발자의 작업 흐름 전체를 함께 관리하는 방향으로 발전할 가능성이 크다.

그런 관점에서 Codex CLI와 Codex App을 같이 써보는 것은 꽤 의미 있는 경험이었다.

CLI는 빠른 실행력을 주고, App은 작업 관리의 시야를 넓혀준다.

둘을 함께 쓰면 AI 코딩 도구를 단순한 보조 수단이 아니라, 실제 개발 워크플로우의 일부로 가져갈 수 있다.

Posted by 모과이IT
,