Lazy Developer8 min

구독자한테 MCP를 열 계획이다

2026년 4월 · 귀찮은개발자 EP.15

2026년 4월 · 귀찮은개발자 EP.15

EP.10에서 나만 쓰는 MCP를 만들었다. EP.11에서 내 SaaS 데이터를 연결했다. 그다음 생각은 자연스러웠다. “이걸 구독자한테도 열어줄 수 있지 않나?”

결론부터 말하면, 아직 계획이다. 구조만 만들어놨고, 구독자한테 공개는 안 했다. 근데 npm에 패키지는 올렸고, API 키 인증도 돌아간다. 여기까지 뭘 했는지 기록해둔다.

빠르게 보기

– @lazydeveloper/mcp-apsity, @lazydeveloper/mcp-feedmission npm 배포 (0.1.0)
– API 키 인증: ask_ (Apsity) / fmk_ (FeedMission) + UUID
– 플랜별 도구 제한: Starter 3개 / Pro 6개
– 모든 DB 쿼리에 userId 필터 (데이터 격리)
– 아직 초기 단계. 구독자 공개 전. 구조만 잡아놓은 상태.

대시보드 대신 MCP를 다는 아이디어

보통 SaaS를 만들면 대시보드를 준다. 웹 브라우저에서 그래프 보고, 표 보고, 필터 걸고. 나도 Apsity랑 FeedMission에 대시보드가 있다. 근데 EP.11에서 내 데이터를 MCP로 연결해보니까 느낀 게 있었다.

대시보드는 내가 찾아가야 한다. 브라우저 열고, 로그인하고, 메뉴 클릭하고. MCP는 반대다. “이번 주 매출 어때?”하고 물어보면 AI가 알아서 데이터를 갖다 준다. 데이터가 나한테 온다. 이 방향이 맞다고 생각했다.

그러면 구독자한테도 이걸 줄 수 있다. 구독자가 Claude에 내 MCP를 연결하면, 자기 앱 데이터를 자연어로 조회할 수 있다. 대시보드를 안 열어도 된다.

npm이 뭔데

개발 이야기가 나오니까 용어부터 정리한다.

npm은 프로그래머들의 앱스토어 같은 곳이다. “npm install 뭐뭐”하면 남이 만든 코드를 내 프로젝트에 가져다 쓸 수 있다. 앱스토어에서 앱을 깔듯이, npm에서 코드를 깔아 쓴다. 전 세계 개발자 300만 개 넘는 패키지를 올려놓고 있다.

npx는 npm에 올라간 프로그램을 설치 없이 바로 실행하는 명령어다. “npx @lazydeveloper/mcp-apsity”하면 그 자리에서 MCP 서버가 뜬다. 따로 설치할 것 없다. 항상 최신 버전이 실행된다.

API 키는 출입증이다. 건물에 들어가려면 출입증이 필요하다. 내 데이터에 접근하려면 API 키가 필요하다. 키마다 “이 사람은 Starter다” “이 사람은 Pro다”가 적혀 있다. 출입 등급에 따라 갈 수 있는 층이 다른 것처럼.

만든 것: npm 패키지 2개

패키지를 2개 만들었다. @lazydeveloper/mcp-apsity@lazydeveloper/mcp-feedmission. 각각 내 SaaS의 MCP 서버다. 둘 다 0.1.0으로 npm에 올렸다.

처음에 @lazydev라는 이름으로 올리려 했다. 근데 npm에 이미 그 이름의 조직이 있었다. 선점당한 거다. 그래서 @lazydeveloper로 바꿨다. 이름 짓기가 제일 어렵다.

구독자 입장에서 쓰는 법은 간단하다. Claude 설정에 몇 줄만 넣으면 된다.

// Claude Desktop 설정 (.mcp.json)
{
  “mcpServers”: {
    “apsity”: {
      “command”: “npx”,
      “args”: [“@lazydeveloper/mcp-apsity”],
      “env”: {
        “APSITY_API_KEY”: “ask_너의키를여기에”
      }
    }
  }
}

이게 전부다. 설정 저장하고 Claude를 열면 된다. “이번 주 매출 보여줘”하면 Claude가 apsity_revenue를 불러서 답해준다. EP.10에서 내가 쓰던 방식 그대로다. 차이는 구독자 본인 데이터만 나온다는 것뿐이다.

인증: 시작할 때 출입증 검사

MCP 서버가 뜨는 순간 첫 번째로 하는 일이 있다. API 키 검증이다.

npx로 MCP 서버 시작 시 Starter는 3개, Pro는 6개 도구가 등록되는 터미널 화면
같은 서버인데 키에 따라 등록되는 도구가 다르다 / GoCodeLab

서버가 뜨면 환경변수에서 API 키를 읽는다. ask_로 시작하면 Apsity 키고, fmk_로 시작하면 FeedMission 키다. 뒤에 UUID가 붙는다. 키가 없으면 안내 메시지만 뜨고 끝이다.

키가 있으면 SaaS 서버에 POST를 보낸다. “/api/mcp-keys/verify”라는 주소다. 서버가 키를 확인하고 세 가지를 돌려준다. 이 키가 유효한지, 누구 키인지(userId), 어떤 요금제인지(plan).

인증이 끝나면 플랜에 따라 도구를 등록한다. Starter면 3개, Pro면 6개. 등록 안 된 도구는 아예 보이지 않는다. Claude가 호출할 수도 없다.

// 플랜별 도구 접근 권한
const PLAN_ACCESS = {
  apsity_revenue:      [‘STARTER’, ‘PRO’],
  apsity_reviews:      [‘STARTER’, ‘PRO’],
  apsity_category_rank: [‘STARTER’, ‘PRO’],
  apsity_keywords:     [‘PRO’],
  apsity_competitors:  [‘PRO’],
  apsity_insights:     [‘PRO’],
}

코드가 짧다. 배열 안에 플랜 이름이 있으면 쓸 수 있고, 없으면 못 쓴다. 이게 권한 시스템의 전부다. 복잡한 걸 만들 이유가 없었다.

플랜: Starter vs Pro

API 키 검증 흐름과 Starter vs Pro 플랜별 도구 비교 다이어그램
키 검증부터 도구 등록까지의 전체 흐름 / GoCodeLab

Apsity 기준으로 설명한다. 전체 도구가 6개다.

Starter (3개 도구)
– apsity_revenue: 매출 합계만 나온다. 앱별 상세는 안 나온다.
– apsity_reviews: 앱 리뷰 조회.
– apsity_category_rank: 카테고리 순위 조회.

기본적인 숫자를 확인하는 용도다. “이번 주 매출 얼마야?” 정도는 된다.
Pro (6개 도구 전체)
– Starter 3개 전부 포함. 매출은 앱별 상세로 나온다.
– apsity_keywords: 키워드 순위 + 변동 추적.
– apsity_competitors: 경쟁앱 메타데이터 변동 감지.
– apsity_insights: AI가 분석한 성장 인사이트.

분석과 인사이트까지 쓸 수 있다. “경쟁앱이 뭘 바꿨어?” 같은 질문이 가능하다.

같은 도구라도 플랜에 따라 결과가 다르다. 매출 조회를 예로 들면, Starter는 전체 합계만 보여준다. “이번 주 총 매출 $120” 이런 식이다. Pro는 앱별로 나눠서 보여준다. “앱A $80, 앱B $25, 앱C $15”. 코드 안에서 plan 값을 보고 쿼리를 다르게 날린다.

데이터 격리: 남의 통장을 못 보게

구독자 패키지에서 제일 신경 쓴 부분이다. A 구독자는 자기 데이터만, B 구독자는 자기 데이터만 봐야 한다. 은행에서 내 통장만 볼 수 있는 것과 같다.

구조는 단순하다. API 키에서 userId를 꺼내고, 모든 DB 쿼리의 WHERE 조건에 넣는다.

SELECTFROM “SalesData” sd
JOIN “App” a ON a.“id” = sd.“appId”
WHERE a.“userId” = ${userId}  — 이 한 줄이 핵심이다

도구가 6개니까 쿼리도 6개. 하나하나 userId 필터가 제대로 들어가 있는지 확인했다.

handler 함수의 두 번째 인자로 userId가 들어온다. 도구 핸들러 안에서 이 값을 빠트릴 수 없는 구조다. 쿼리를 짤 때 userId를 안 넣으면 타입 에러가 난다. 실수를 구조로 막는 거다.

npm 배포: 안 된 것들

npm에 패키지를 올리는 건 처음이었다. 몇 군데서 걸렸다.

2FA 문제. npm 계정에 이중 인증을 켜놨더니 CLI에서 publish할 때 OTP를 계속 물어봤다. 자동화 스크립트에서는 이게 불편하다. 결국 Granular Access Token이라는 걸로 우회했다. 토큰에 publish 권한만 주고, 2FA 대신 토큰으로 인증하는 방식이다.

scope 이름. 아까 말한 @lazydev 선점 건이다. npm에서는 @뒤에 오는 게 조직 이름이다. 다른 사람이 이미 @lazydev를 만들어놨으면 내가 못 쓴다. 미리 확인 안 하고 코드부터 짰다가 이름을 전부 바꿔야 했다.

bin 필드. npx로 실행하려면 package.json에 bin 필드가 있어야 한다. 여기에 진입점을 적어주면 npx가 그 파일을 실행한다. 빠트리면 npx가 뭘 실행할지 모른다. 처음에 이거 빠트려서 아무것도 안 됐다.

// package.json 핵심 부분
{
  “name”: “@lazydeveloper/mcp-apsity”,
  “version”: “0.1.0”,
  “bin”: {
    “mcp-apsity”: “dist/server.js”  // npx가 이걸 실행한다
  },
  “publishConfig”: {
    “access”: “public”  // scoped 패키지는 기본이 private다
  }
}

scoped 패키지(@뭐뭐/이름 형태)는 기본이 private다. publishConfig에 access: public을 안 넣으면 npm이 유료 플랜을 요구한다. 이것도 처음에 몰라서 한 번 헤맸다.

아직 안 한 것들

솔직하게 쓴다. 지금은 구조만 있다. 실제로 구독자한테 열려면 이것들이 더 필요하다.

남은 할 일
– API 키 발급 UI: 대시보드에서 키를 만들고 관리하는 화면
– 문서: 설치법, 도구 설명, 트러블슈팅 가이드
– Rate limiting: 요청 횟수 제한 (남용 방지)
– 에러 핸들링: 키 만료, DB 연결 실패 등 예외 처리
– 안정성 테스트: 여러 구독자가 동시에 쓸 때 문제없는지
– 가격 정책: Starter와 Pro의 월 요금 결정

패키지는 npm에 올라가 있다. 인증 코드도 돌아간다. 근데 구독자가 직접 키를 발급받을 UI가 없다. 지금은 내가 수동으로 키를 만들어줘야 한다. 이러면 서비스가 아니다.

Rate limiting도 빠져 있다. 누군가 도구를 1초에 100번 호출하면 DB가 죽는다. 아직 나 혼자 테스트하는 단계라 괜찮지만, 공개 전에는 반드시 넣어야 한다.

왜 미리 만들었나

공개도 안 할 거면서 왜 만들었냐고 물을 수 있다.

구조를 먼저 잡아놓고 싶었다. 나중에 “이제 열어야지”하고 시작하면 인증, 격리, 플랜 이런 걸 한꺼번에 짜야 한다. 지금 미리 만들어두면 나중에 UI랑 문서만 붙이면 된다. 뼈대가 서 있으면 살 붙이기는 빠르다.

그리고 내가 쓰면서 검증하고 싶었다. 내 계정으로 API 키를 만들고, npx로 실행해보고, 도구가 제대로 동작하는지 확인하는 거다. EP.11에서 했던 것처럼 내가 첫 번째 사용자가 되는 방식이다.

아직 초기 단계다. 언제 열지도 정하지 않았다. 근데 구조는 있다. 준비가 됐을 때 바로 움직일 수 있도록.

자주 묻는 질문

Q. npm이 뭔가요?

프로그래머들이 코드를 공유하는 앱스토어 같은 곳이다. “npm install 뭐뭐”하면 남이 만든 코드를 가져다 쓸 수 있다. 전 세계 300만 개 넘는 패키지가 올라가 있다.

Q. npx는 뭔가요?

npm에 올라간 프로그램을 설치 없이 바로 실행하는 명령어다. “npx @lazydeveloper/mcp-apsity”하면 바로 MCP 서버가 뜬다. 항상 최신 버전이 실행된다.

Q. API 키가 뭔가요?

출입증이다. 이 키가 있어야 내 데이터에 접근할 수 있다. 키마다 요금제 정보가 들어 있어서 Starter인지 Pro인지에 따라 쓸 수 있는 도구가 달라진다.

Q. 데이터 격리가 뭔가요?

A 사용자가 B 사용자의 데이터를 못 보게 막는 거다. 은행에서 내 통장만 볼 수 있는 것과 같다. 모든 DB 쿼리에 userId 필터가 들어가서 본인 데이터만 나온다.

Q. 지금 쓸 수 있나요?

아직이다. npm에 패키지는 올렸고, 인증도 돌아간다. 근데 키 발급 UI, 문서, 안정성 테스트가 빠져 있다. 구조만 잡아놓은 상태라고 보면 된다.

관련 글