AI 계정은 연수·자료·질문할 지원망과 함께 운영 구조로 설계해야 합니다. Anthropic과 아이슬란드 교육아동부가 2025년 11월 4일 발표한 국가 단위 시범사업도 전국 수백 명의 교사에게 Claude와 교육 자료, 연수 자료, 전담 지원 네트워크를 함께 제공하는 방식이었습니다. 국내 학원에 그대로 적용할 사업은 아니지만, 계정 이후 지원 구조를 설계할 때 참고할 수 있습니다.
아이슬란드 시범사업은 무엇을 묶었나
사업의 목적은 AI가 학교와 교사의 수업 준비, 수업 지도, 학생 학습에 어떻게 도움이 되는지 탐색하는 것입니다. 특정 성적 향상을 약속하기보다 교사의 필요를 길잡이로 삼겠다는 방향을 내세웠고, 교육아동부 장관도 교사의 필요를 중심에 두겠다는 취지로 말했습니다.
언어와 문화의 조건도 발표에 들어 있습니다. Anthropic 측은 아이슬란드어 보호를 함께 언급했고, 아이슬란드어를 인식하는 환경을 고려했습니다. 계정만 나눠 주는 방식이 아니라 교사가 쓸 자료와 연수, 막힐 때 물어볼 지원망을 한 번에 준비한 점이 이 사업의 설계입니다.
검색 사례와 학원 도입의 간격
같은 발표에는 유럽의회 기록보관 부서가 210만 건 이상의 문서를 다루며 검색 시간을 80% 단축했다는 사례도 나옵니다. 이 수치는 아이슬란드 시범사업의 성과가 아니라 별도의 활용 사례입니다. 서로 다른 사례를 한 사업의 결과처럼 합치지 않아야 합니다.
학원에서 참고할 대목은 계정 발급 이후의 운영입니다. 수업 준비에서 반복되는 업무를 고르고, 연수에서 직접 자료를 만들어 보고, 잘 된 양식을 공유하고, 막히면 도움을 요청할 담당자를 정하는 순서입니다. 실제 학생 정보와 수업 자료를 다룰 때는 접근 권한과 보관 방식도 함께 정해야 합니다.
AI 도입 뒤에 붙일 지원 구조
계정 발급과 함께 첫 실습에서 만들 결과물을 정합니다.
강사들이 공유할 수업 자료와 프롬프트의 위치를 정합니다.
AI가 읽을 자료의 범위와 개인정보 처리 기준을 적습니다.
사용 중 막혔을 때 질문할 담당자와 답변 방식을 정합니다.
한 달 뒤 실제로 쓰인 업무와 남은 불편을 검토합니다.
자주 묻는 질문
아이슬란드 사업을 한국 학원에 그대로 적용할 수 있나요?
그 사업은 아이슬란드 교육아동부와의 국가 시범사업입니다. 국내 학원은 국가 제도나 제공 조건을 복사하기보다 계정, 연수, 자료, 지원을 자신의 규모에 맞게 설계해야 합니다.
아이슬란드어 보호가 학원에 어떤 의미가 있나요?
AI를 쓸 때도 수업의 언어와 지역 맥락을 지키는 조건을 먼저 정하라는 뜻으로 읽을 수 있습니다. 한국 학원이라면 학생에게 맞는 표현과 교사의 수업 의도를 검토 기준에 넣을 수 있습니다.
지원 담당자가 꼭 필요한가요?
도입 초기에는 질문이 어디로 가는지 정해 두는 편이 좋습니다. 담당자는 모든 문제를 대신 해결하기보다 자료와 권한, 재현 절차를 함께 정리하는 역할을 맡으면 됩니다.
AI가 푼 숙제에서 배움을 확인하려면 결과물만 보지 말고 근거·풀이·설명 과정을 함께 받으세요. Anthropic이 2025년 4월 8일 발표한 Education Report는 전 세계 대학 이메일 계정의 익명 대화 약 100만 건 가운데 학업 관련 574,740건을 분석한 자료로, 한국 초중등 교실의 부정행위를 직접 판정하는 조사는 아닙니다.
대화는 네 가지 사용법으로 나뉘었다
보고서는 학생과 AI의 상호작용을 직접 문제 해결, 직접 결과물 생성, 협력 문제 해결, 협력 결과물 생성으로 나눕니다. 각 패턴은 대략 23~29% 범위였고, 직접 문제 해결과 직접 결과물 생성을 합친 직접형은 약 47%였습니다. 협력형이라고 해서 항상 학습이 일어난다고 단정할 수도 없습니다.
Claude의 응답이 어떤 사고 수준을 대신했는지도 블룸 분류로 살폈습니다. 만들기가 39.8%, 분석하기가 30.2%, 적용이 10.9%, 이해가 10.0%, 기억이 1.8%로 제시됩니다. 숫자가 보여주는 것은 학생이 AI를 어려운 과제에도 사용한다는 점이지, 그 결과가 곧 학습이나 부정행위라는 뜻은 아닙니다.
숫자를 평가 규칙으로 바꾸지 않기
대화 기록만으로는 집에서 치르는 시험 답을 구한 것인지, 연습문제를 점검한 것인지 알 수 없습니다. 협력형 대화도 확률·통계 숙제를 여러 차례 주고받으며 결국 사고를 AI에 넘기는 경우가 있을 수 있다고 보고서는 지적합니다. 맥락과 과제 규칙을 함께 보지 않으면 분류가 판정으로 바뀝니다.
학원에서는 결과물만 제출받는 과제에 근거 설명을 붙일 수 있습니다. 독해라면 답의 근거 문장을 찾게 하고, 영작이라면 고친 이유를 말하게 하며, 필요하면 짧은 구두 확인을 둡니다. 이 방법은 AI 사용을 자동으로 적발한다는 약속이 아니라, 학생이 무엇을 이해했는지 확인하는 평가 설계입니다.
과정 중심 평가를 설계할 때
결과물과 함께 핵심 근거 또는 풀이 과정을 제출하게 합니다.
AI 사용 허용 범위와 학생이 직접 해야 할 부분을 미리 씁니다.
구두 설명, 초안 비교, 근거 표시 중 과목에 맞는 방법을 고릅니다.
대화 분류 수치를 한국 초중등 전체의 비율처럼 사용하지 않습니다.
평가 후 학생이 막힌 개념을 다음 수업 자료에 반영합니다.
자주 묻는 질문
이 보고서로 AI 부정행위를 판정할 수 있나요?
판정할 수 없습니다. 익명 대화의 목적과 과제 상황을 모두 알 수 없으므로, 개별 학생의 행동을 추정하는 근거로 쓰면 안 됩니다.
협력형 사용은 학습에 더 좋은가요?
협력형이라는 이름만으로 학습 효과를 보장하지 않습니다. 학생이 근거를 설명하고 자신의 판단을 남겼는지까지 확인해야 합니다.
한국 학원도 보고서 비율을 적용할 수 있나요?
보고서는 전 세계 대학 이메일 계정과 대학생 대화를 대상으로 했습니다. 학원은 같은 비율을 적용하기보다 자신의 과제와 학생 수준에 맞는 과정 확인 방법을 설계해야 합니다.
교사는 학생이 반복해서 막히는 지점을 바탕으로 작은 AI 학습 도구를 직접 설계해 볼 수 있습니다. Anthropic과 Teach For All이 2026년 1월 21일 발표한 글로벌 교육자 프로그램 사례를 학원에 그대로 적용하는 것이 아니라, 교사가 목표와 결과물을 검토하는 절차로 한정해 참고하세요.
교사를 공동 설계자로 부른 구조
이 프로그램의 AI Literacy & Creator Collective에는 63개국의 교사와 동문 10만 명 이상이 참여하고, 이들이 가르치는 학생은 150만 명 이상이라고 안내합니다. 교사를 단순한 사용자보다 공동 설계자(co-architects)로 부르는 것이 특징입니다. 교실을 아는 사람이 도구의 문제 정의에 들어와야 한다는 전제입니다.
활동은 세 갈래로 소개됩니다. AI Fluency Learning Series는 라이브 6회 학습 시리즈로, 2025년 11월 첫 시리즈에는 530명 이상이 참여했습니다. Claude Connect는 60개국 이상의 교사가 매일 사례를 나누는 공간이고, Claude Lab은 더 나아가고 싶은 교사에게 Claude Pro를 제공해 실험하게 하고 월별 오피스아워를 엽니다. 발표 나흘 만에 200건 이상 지원이 들어왔다는 수치도 프로그램의 초기 반응으로 제시됩니다.
사례가 학원에 주는 힌트
라이베리아의 한 교사는 AI를 처음 접한 뒤 몇 주 만에 기후 교육용 인터랙티브 교과과정을 만들었습니다. 방글라데시에서는 6·7학년 학생의 절반 이상이 기초 연산을 어려워한다는 교사의 관찰을 바탕으로 보스전, 리더보드, 경험치가 있는 게임형 수학 앱을 설계했습니다. 아르헨티나에서는 교육과정과 연결된 디지털 워크스페이스를 만드는 사례가 소개됩니다.
이 사례가 한국 학원에 보장하는 도구나 성과가 있는 것은 아닙니다. 다만 교사가 학생의 오답과 막히는 지점을 먼저 알고, 그 정보를 작은 활동으로 바꾸는 순서는 참고할 수 있습니다. 학원에서도 완성된 플랫폼부터 찾기보다 특정 수업의 복습 활동 하나를 정하고, 교사가 직접 시안을 검토하는 방식으로 시작할 수 있습니다.
교사가 만들 학습 활동을 고르는 순서
학생이 반복해서 막히는 개념을 수업 기록에서 찾습니다.
활동의 목표와 대상 학년을 한 문장으로 적습니다.
AI에게 만들게 할 부분과 교사가 판단할 부분을 나눕니다.
작은 퀴즈나 인터랙티브 자료로 먼저 시험합니다.
학생 반응을 보고 다음 자료에 반영할 기준을 남깁니다.
자주 묻는 질문
교사가 프로그램에 참여하지 않아도 같은 결과를 낼 수 있나요?
원문 사례는 특정 국제 프로그램 안에서 나온 결과입니다. 국내 학원이 같은 결과를 기대하기보다, 교사가 학생의 어려움을 정의하고 작은 자료를 시험하는 절차부터 가져오는 편이 안전합니다.
게임형 학습 도구가 모든 수업에 맞나요?
그렇지 않습니다. 게임 형식은 기초 연산처럼 반복 연습이 필요한 내용에서 검토할 수 있지만, 학습 목표와 학생의 나이에 맞아야 합니다.
무엇부터 AI에게 맡기면 좋을까요?
학생이 자주 틀리는 개념을 짧은 연습 활동으로 바꾸는 일부터 시작할 수 있습니다. 교사는 목표와 난이도를 정하고, AI가 만든 자료는 수업 전에 직접 확인해야 합니다.
수업에서 AI가 답을 대신 쓰지 않게 하려면 ‘답해줘’ 대신 학생에게 접근과 근거를 묻도록 지시하세요. Anthropic이 2025년 4월 2일 발표한 대학용 Claude for Education의 Learning mode 사례를, 국내 학원에서 바로 쓸 수 있는 기능이 아니라 질문 중심 수업 설계의 참고로 한정해 보세요.
Learning mode가 바꾸는 응답
학습 모드는 Projects 안에서 작동하며, “이 문제에 어떻게 접근하겠어?” 또는 “네 결론을 뒷받침하는 근거는 뭐야?”처럼 되묻는 응답을 지향합니다. 원문이 설명하는 방식은 네 가지입니다. 바로 답하는 대신 접근법을 묻고, 근거를 확인하고, 핵심 개념을 짚고, 보고서·학습 가이드·개요의 틀을 제공하는 것입니다.
이 기능은 Northeastern, LSE, Champlain College 같은 대학과의 전교 이용 계약 맥락에서 소개됐습니다. 따라서 대학용 제품과 국내 학원용 이용 조건을 혼동하지 않아야 합니다. 여기서 가져올 만한 점은 기능 이름보다 학생이 답을 만들기 전에 어떤 질문을 받아야 하는지 정하는 방식입니다.
학원 지시문을 질문형으로 바꾸기
강사에게 AI가 수업 자료를 바로 쓰게 하기보다 빈틈을 질문으로 찾게 하라고 지시할 수 있습니다. 학생의 독해 요약에는 “요약해 줘” 대신 “본문에서 근거가 되는 문장을 찾도록 질문해 줘”라고 요청합니다. 영작 첨삭도 완성 문장을 먼저 주기보다, 틀린 이유를 학생이 설명하게 하는 질문을 차례로 만들 수 있습니다.
이것은 특정 기능을 그대로 복제하는 작업이 아닙니다. 답변 속도보다 학생이 판단한 흔적을 남기는 것이 중요한 수업에서 쓸 수 있는 지시 방식입니다. 학년과 과목에 따라 질문의 난이도와 교사가 개입할 시점을 정하고, AI의 질문이 학습 목표에서 벗어나지 않는지 확인해야 합니다.
답 대신 질문을 설계할 수업
학생이 먼저 시도할 문제와 AI가 질문할 범위를 정합니다.
정답 대신 요구할 근거와 설명의 형태를 고릅니다.
핵심 개념을 짚는 질문과 보고서 틀을 구분합니다.
질문이 너무 어렵거나 넓을 때 교사가 개입할 시점을 둡니다.
결과물보다 학생의 판단 과정을 볼 기록 방식을 정합니다.
자주 묻는 질문
Learning mode는 학원에서도 바로 쓸 수 있나요?
원문은 대학용 Claude for Education 기능으로 설명합니다. 국내 학원에서의 제공 여부나 조건은 별도로 확인해야 하며, 이 글은 질문 중심 수업 설계를 참고하자는 뜻입니다.
질문만 하면 학생이 반드시 스스로 풀게 되나요?
그렇지는 않습니다. 질문의 범위와 난이도가 적절한지, 학생이 실제 근거를 사용했는지 교사가 확인해야 합니다.
강사 업무에도 이 방식을 적용할 수 있나요?
가능합니다. 수업 자료를 만들어 달라고만 하기보다 빠진 개념이나 확인할 근거를 질문으로 찾게 하면 검토 단계에 활용할 수 있습니다.
학원 자료를 AI에 연결하려면 먼저 자료의 목적·접근 권한·연결 방식을 나누어 정리하세요. Anthropic이 2025년 7월 9일 발표한 대학 교육 사례를 바탕으로 하되, Canvas의 LTI와 Wiley·Panopto의 MCP 사례를 국내 학원에 그대로 적용하지 않고 연결 원칙으로만 참고하세요.
대학 자료를 연결하는 방식은 도구마다 다르다
Panopto와 Wiley는 미리 만든 MCP 서버로 연결해 강의 녹화 자막과 동료 심사를 거친 학술 자료를 다루는 방식으로 소개됩니다. 반면 Canvas는 MCP가 아니라 LTI로 Canvas 강좌 안에서 Claude를 쓰도록 연결합니다. 연결 규격을 하나로 뭉뚱그리지 않고 자료의 위치와 제품의 권한에 맞춰 나눈 점이 중요합니다.
학생이 지난주 강의에서 어떤 내용이 나왔는지 묻고 자막에서 근거를 찾는 상황은 예시입니다. AI가 알고 있는 일반 지식이 아니라 기관이 가진 자료에서 답을 찾게 하려는 설계입니다. 학원에서도 수업 기록과 교재 진도, 복습 자료가 일정한 형식으로 쌓여 있어야 비슷한 질문을 검토할 수 있습니다.
개인정보와 승인 절차를 먼저 둔다
원문은 대화를 기본적으로 비공개로 두고 학습에서 제외한다고 설명합니다. 기관 자료를 요청하는 일에는 공식 승인이 필요하고, 사용자가 자료를 스스로 내보내는 기능도 기본 제한이 있다고 안내합니다. 이런 설정은 연결 기능보다 먼저 검토할 운영 조건입니다.
국내 학원에서는 출결, 상담, 성적 자료를 모두 연결하기보다 목적과 범위를 나누는 편이 낫습니다. 기록 양식을 통일하고, 누가 읽을 수 있는지 정한 뒤, 필요한 자료만 검색 대상으로 둡니다. 대학 발표 당시의 연결 방식이 한국 학원 프로그램에서도 작동한다고 단정하지 말고, 현재 사용하는 시스템의 연동 방법을 따로 확인해야 합니다.
학원 자료를 연결하기 전 정리할 것
수업 기록과 교재 자료를 목적별 폴더와 양식으로 나눕니다.
Canvas식 LTI와 MCP를 같은 연결 방식으로 부르지 않습니다.
학생 자료의 접근자와 공식 승인 절차를 정합니다.
AI가 답할 때 근거로 보여줄 원본 위치를 남깁니다.
연결 전후에 자료가 학습이나 외부 공유에 쓰이는 조건을 확인합니다.
자주 묻는 질문
학원 자료도 MCP로 연결하면 되나요?
원문은 도구에 따라 연결 방식이 다르다고 설명합니다. Canvas는 LTI, Wiley와 Panopto는 미리 만든 MCP 서버 사례이므로 현재 학원 시스템에 맞는 방법을 별도로 확인해야 합니다.
학생 자료를 연결하면 개인정보 우려가 사라지나요?
그렇지 않습니다. 비공개와 학습 제외 설정이 있어도 누가 어떤 자료를 읽는지, 기관 승인이 필요한지 운영 규칙을 정해야 합니다.
자료를 먼저 정리해야 하는 이유는 무엇인가요?
AI가 답할 근거를 찾으려면 기록의 위치와 형식이 일정해야 합니다. 흩어진 메시지와 파일을 먼저 목적별로 정리하면 연결 범위를 좁히고 검토하기도 쉬워집니다.
AI에게 긴 일을 맡길 때는 무엇이 끝인지, 언제 멈춰 사람에게 물을지를 먼저 정해야 합니다. Opus 5.5 가이드는 전체 일을 한 번에 맡기고 완료 기준을 말한 뒤, 결과를 확인하는 절차를 따로 두라고 권합니다.
학원에서도 “이거 좀 해줘”보다 “어디까지 끝나면 되는지”를 알려주는 편이 낫습니다. 숙제 피드백, 학부모 안내문, 워크북 검수처럼 중간 과정이 긴 업무일수록 시작 전에 끝을 정해야 합니다.
가이드의 팁 가운데 골라 볼 여섯 가지
비개발자에게 쓸모 있는 것만 추리면 이렇습니다. 첫째, 일을 통째로 맡기되 완료 기준과 멈춰 물을 조건을 함께 말합니다. 둘째, “신중히 생각해”, “단계별로 생각해” 같은 문구를 습관처럼 덧붙이지 않습니다. Opus 5.5는 답변 전에 생각하므로, Anthropic이 채팅 제품에서 해 본 테스트에서는 이 문구를 지웠을 때 답변이 더 빨리 시작됐고 품질 저하는 뚜렷하지 않았습니다.
셋째, 디자인을 요청할 때는 싫은 스타일을 구체적으로 적습니다. 원문 예시처럼 크림색·오프화이트 배경, 제목 속 이탤릭 강조 단어, 01/02/03 번호 라벨, 모노스페이스 라벨, 알약 모양 버튼을 원하지 않는다면 처음부터 목록으로 씁니다. 넷째, 긴 작업은 TASKS.md 같은 파일에 체크리스트를 적고 진행하면서 갱신합니다.
다섯째, 찾지 못했거나 직접 확인하지 못한 항목을 따로 보고하게 합니다. 여섯째, 차트·다이어그램·스크린샷은 숫자와 모양을 다시 옮겨 적지 말고 파일로 첨부합니다. Opus 5.5 자체 소개는 devlog 글에 정리했습니다.
AI에게 일을 맡길 때 무엇부터 정할까요?
첫 메시지에 전체 업무, 완료선, 질문할 조건을 넣어보세요. 예를 들어 “숙제 피드백 초안을 작성하고, 학생별 누락 여부를 표로 정리해. 모든 항목을 확인하면 끝내고, 자료가 없거나 판단이 필요한 경우에만 질문해”처럼 쓸 수 있습니다. 파괴적인 작업이나 사람의 승인이 필요한 단계는 계속 진행하지 말고 멈추도록 따로 적습니다.
AI가 잘못 이해해서 엉뚱한 방향으로 갈 때도 있지만, 완료 기준이 없어서 서로 다른 결과를 내놓는 경우도 있습니다. 기준을 먼저 주면 결과를 읽을 때도 무엇을 확인해야 하는지 분명해집니다.
긴 업무의 완료 기준을 정하는 순서
숙제 피드백 지시문에 대상, 제출물 형식, 완료 조건, 질문할 조건을 한 문장씩 적습니다.
학부모 안내문에는 “초안을 만들고, 사실·날짜·대상 학생을 점검한 뒤, 판단이 필요한 문장만 질문해”처럼 끝나는 순서를 씁니다.
워크북 검수에는 오탈자·페이지 넘침·문항 연결처럼 검사할 항목과, 직접 확인하지 못한 항목의 보고 형식을 함께 넣습니다.
디자인 요청에는 원하지 않는 배경, 제목 강조, 번호 라벨, 글꼴 라벨, 버튼 모양을 구체적으로 적습니다.
오래 걸리는 교재 제작이나 리포트 작성은 TASKS.md에 할 일을 적고, 새로 생긴 항목도 같은 파일에 추가합니다.
차트나 학습 자료의 스크린샷은 다시 타이핑하지 말고 원본 파일을 첨부한 뒤, 확인할 질문을 한 문장으로 씁니다.
자주 묻는 질문
“단계별로 생각해”를 지워도 되나요?
Opus 5.5는 답변 전에 생각하므로 같은 지시를 반복할 필요가 없다는 것이 가이드의 설명입니다. Anthropic이 채팅 제품에서 해 본 테스트에서는 해당 문구를 지웠을 때 답변이 더 빨리 시작됐고 품질 저하는 뚜렷하지 않았습니다.
완료 기준은 어떻게 쓰나요?
“무엇을 만들고, 어떤 상태면 끝내며, 어떤 경우에만 질문할지”를 한 문장에 넣으면 됩니다. 예를 들면 “초안을 만들고 날짜와 대상만 점검한 뒤, 자료가 없는 항목만 따로 물어봐”처럼 업무의 끝과 중단 조건을 함께 씁니다.
AI가 확인하지 못한 것을 왜 따로 적게 하나요?
사람이 마지막으로 판단해야 할 위치를 바로 찾을 수 있기 때문입니다. 가이드도 찾지 못한 내용과 살펴본 범위를 보고하게 하면 결과를 검토하기 쉬워진다고 설명합니다.
긴 작업은 어디에 적어두나요?
Claude Code를 쓴다면 원문 예시처럼 TASKS.md에 체크리스트를 두고 진행하면서 갱신할 수 있습니다. 화면의 긴 대화 기록보다 파일을 보면 끝난 일과 남은 일을 빠르게 확인할 수 있습니다.
제미나이 4 아르곤은 구글이 한국 시간 2026년 10월 1일 오전 5시에 공개한 제미나이 4 첫 프런티어 모델로, 지금은 구글 내부와 페어윈드(Fairwind) 참여 기관만 쓸 수 있고 API·한국 일정은 발표되지 않았다.
미국 시간으로는 9월 30일 발표입니다. 발표 글은 구글 딥마인드의 코라이 카바쿨루(Koray Kavukcuoglu)가 썼습니다. 국내 보도자료에는 화려한 표현이 많이 붙었지만, 공식 글을 직접 읽어 보면 확인된 내용과 아직 정해지지 않은 내용이 꽤 분명하게 갈립니다. 이 글은 그 둘을 나눠서 정리합니다.
한눈에 보기
구글 공식 표현은 ‘새 프런티어 모델(new frontier model)’입니다. 소프트웨어 엔지니어링, 법률·금융 같은 기업 지식 업무, 사이버 보안 방어를 주요 용도로 꼽았습니다.
외부 리더보드에서 단독 1위가 확인된 지표는 Vals Index(68.90%)입니다. DeepSWE 77.9%는 구글이 직접 낸 수치이고, CWE-bench 68%는 세 모델이 공동 1위입니다.
지금은 구글 내부와 페어윈드 프로그램에 선정된 기관만 쓸 수 있습니다. API, 제미나이 앱, 한국 출시 일정은 아직 발표되지 않았습니다.
발표된 가격은 100만 토큰당 입력 2달러·출력 10달러(출시 기간)이고, 그 뒤에는 4달러·20달러입니다.
제미나이 4 아르곤은 어떤 모델인가
아르곤은 제미나이 4 세대의 첫 모델이고, 지금까지는 유일한 제미나이 4 모델입니다. 그동안 구글 최상위 모델에 붙던 ‘Pro’라는 이름 대신 새 이름 체계를 쓴 점이 눈에 띕니다. 로이터에 따르면 구글 대변인은 아르곤이 기존 ‘Pro’ 계열보다 크기가 큰 모델이라고 설명했습니다. 9to5Google은 이번 발표가 제미나이 3.5 Pro 계획이 취소된 뒤에 나왔다고 전했습니다. 공개 전 ‘Gemini 4 Pro’라는 이름으로 돌던 Argon 유출·Arena 논란은 이전 글에 정리해 두었습니다. 당시 유출 스펙(출력 25만 6천 토큰)은 공식 발표(출력 100만 토큰)와 다릅니다.
구글은 공식 글에서 아르곤이 실제 소프트웨어 개발, 법률·금융 같은 기업 지식 업무, 사이버 보안 방어처럼 단계가 많은 작업에서 최상위 수준의 성능을 낸다고 설명합니다. 한 번에 내보낼 수 있는 출력 토큰 한도는 이전 6만 4천 개에서 100만 개로 늘었습니다.
표현 하나는 짚고 넘어가겠습니다. 국내 보도자료에는 ‘역대 가장 지능적인 모델’이라는 문구가 있지만, 구글의 영문·한국어 공식 글에는 이런 표현이 없습니다. 공식 글은 ‘새로운 프론티어 모델(new frontier model)’이라고만 부르고, 로이터가 인용한 대변인 발언은 ‘지금까지 가장 성능이 좋은 모델(most performant model yet)’입니다. 어느 쪽이든 회사가 스스로 내린 평가라는 점은 같습니다.
벤치마크 수치, 누가 측정했나
모델 발표에서 가장 많이 인용되는 것이 벤치마크 점수입니다. 다만 같은 ‘1위’라도 회사가 직접 잰 수치인지, 외부 평가 기관이 공개한 수치인지에 따라 무게가 다릅니다. 아래 표는 그 구분을 기준으로 정리한 것입니다.
벤치마크
아르곤
비교
검증 상태
Vals Index
68.90% (1위)
Claude Sonnet 5.5 67.04%, Claude Opus 5.5 66.97%
외부 리더보드 게재 (vals.ai, 사이트 표기 9/30 갱신)
CWE-bench v1
68% (공동 1위)
Grok 4.7 68%, GPT-6 Astra 68%, Opus 5.5 67%
외부 리더보드 게재 (cwe-bench.com), 3개 모델 동률
DeepSWE v1.1
77.9%
Opus 5.5 74.2, GPT-6 Astra 74.1 (구글 표 기준)
구글 자체 수치, 공식 리더보드 미등재
LVBench / AutomationBench
91.7% / 51.3%
구글은 각각 최고 기록·1위라고 발표
구글 발표 기준
Vals Index 1위: 외부 리더보드에 오른 수치
Vals Index는 금융, 코딩, 법률, 세무 과제를 미국 GDP 비중에 따라 가중해 점수를 매기는 지표입니다(사이트 표기 날짜 기준 9월 25일 세무 항목이 추가된 v2.1). vals.ai 공식 페이지에서 아르곤은 68.90%로 41개 모델 중 1위, Claude Sonnet 5.5가 67.04%로 2위, Claude Opus 5.5가 66.97%로 3위입니다. 참고로 vals.ai는 아르곤을 출시 기간 이후 가격인 100만 토큰당 4달러·20달러 기준으로 표시했습니다. vals.ai의 별도 지표 Vals RSI Index에서는 23개 중 4위(30.55%)입니다. 일부 해외 매체가 말한 ‘4위’는 이 지표 기준입니다.
CWE-bench 68%: 단독 1위가 아니라 3개 모델 동률
보안 취약점 관련 과제를 평가하는 CWE-bench v1(Collinear AI가 Artificial Analysis와 함께 공개)에서 아르곤은 자동 채점 기준(programmatic pass@1) 68%를 받았습니다. 그런데 같은 68%에 Grok 4.7과 GPT-6 Astra도 있어 세 모델이 공동 1위입니다. 심사단 채점 기준(judge-panel pass@1)으로 보면 아르곤은 62%로, GPT-6 Astra(66%), Opus 5.5(67%), Grok 4.7(64%)보다 낮습니다. 구글이 공개한 비교표에는 Grok 4.7이 빠져 있다는 점도 함께 알아 두면 좋습니다.
DeepSWE 77.9%: 구글이 직접 측정한 수치
구글은 실전형 소프트웨어 엔지니어링 벤치마크 DeepSWE v1.1에서 77.9%로 최고 기록을 세웠다고 밝혔습니다. 다만 9월 22일(사이트 표기 날짜)에 마지막으로 갱신된 DeepSWE 공식 리더보드에는 아직 아르곤이 올라 있지 않습니다. 지금 시점에서는 구글이 자체 측정해 발표한 수치이고, 외부에서 다시 확인된 기록은 아닙니다. 구글의 평가 방법 문서에도 아르곤의 DeepSWE v1.1 점수는 ‘자체 계산(self computed)’이라고 적혀 있습니다. 같은 문서에 따르면 비교 대상 중 GPT-6 Astra 점수는 공식 공개 리더보드에서, Fable 5.1과 Opus 5.5 점수는 각 회사의 시스템 카드에서 가져왔습니다.
구글 표에서 아르곤이 뒤지는 항목
구글 딥마인드가 공개한 비교표에도 아르곤이 경쟁 모델보다 낮은 항목이 있습니다. FrontierSWE v2에서는 55.0으로 GPT-6 Astra(65.5)보다 낮고, Terminal-Bench 4.0에서는 57.4로 Opus 5.5(66.4)보다 낮습니다. Terminal-Bench Science, PostTrainBench, OSWorld-2.0에서도 다른 모델이 앞섭니다. 여러 지표를 종합하는 Artificial Analysis Intelligence Index에서는 53점으로 Fable 5.1, GPT-6 Astra와 함께 공동 3위라고 연합뉴스가 보도했습니다. 모든 분야에서 앞서는 모델이라기보다, 강점과 약점이 함께 있는 모델로 보는 편이 정확합니다.
구글 안에서는 어떻게 쓰였나
구글은 이미 수천 명의 직원이 업무에 아르곤을 쓰고 있다고 밝혔습니다. 대표 사례는 C/C++ 코드를 Rust로 옮기는 작업입니다. 정규식 라이브러리 re2, 동영상 디코더 libgav1처럼 수만 줄 규모의 코드부터, 80만 줄이 넘는 Fuchsia 운영체제의 Zircon 커널까지 전환을 시도했습니다. 이 결과물은 실제 서비스에 넣기 전에 감사를 받는 중입니다.
메모리 절감 효과도 소개했는데, 수치를 읽을 때 주의가 필요합니다. 구글은 전면 적용되면 300TiB가 넘는 메모리를 확보하고, 전체적으로는 500TiB에서 1PiB를 아낄 것으로 추정합니다. 이미 달성한 성과가 아니라 예상치입니다. 이 밖에 양자 컴퓨팅 최적화 과제에서 기준 대비 40% 개선된 결과를 냈다고 밝혔습니다.
지금 누가, 언제 쓸 수 있나
아르곤은 한 번에 모두에게 열리지 않고 단계적으로 공개됩니다. 지금 쓸 수 있는 곳은 구글 내부 팀과 페어윈드 프로그램 참여 기관, 그리고 일부 신뢰 테스터뿐입니다. 페어윈드는 사이버 보안 방어를 맡은 조직을 위한 프로그램으로, 개인이 아니라 기관 단위로 심사를 거쳐 참여합니다. 대상은 정부·국가 사이버 당국, 핵심 기반시설 운영 기관(의료·통신·에너지·금융 등), 핵심 기술 플랫폼이며, 구글이 신청 기관의 이력을 심사합니다. 구글은 이 신뢰받는 방어 조직과 내부 팀에는 사이버 관련 안전장치를 뺀 버전을 제공한다고 밝혔습니다. 또 미국 정부의 자발적 사전 모델 접근 절차에도 참여하고 있다고 했습니다. 글 맨 위 도식은 2026년 10월 1일(한국시간) 기준 공개 단계를 정리한 것입니다. 3·4단계 구분은 구글 발표 문장을 바탕으로 정리한 것이며, 날짜는 발표되지 않았습니다(출처: 구글 공식 블로그, 구글 딥마인드 페어윈드 프로그램 페이지).
구글이 밝힌 다음 순서는 유료 API 고객과 Google AI Ultra 구독자입니다. 구글은 개발자, 기업, 일반 사용자에게 ‘가능한 한 빨리’ 제공하겠다고 했지만 날짜는 밝히지 않았습니다. 10월 1일(한국시간) 확인 기준으로 아르곤은 Gemini API 문서의 모델 목록과 가격 페이지, Vertex AI 모델 목록에 없고, 제미나이 앱에도 들어 있지 않습니다. 영문·한국어 공식 글 어디에도 한국 출시 일정에 관한 언급은 없습니다.
구글은 광범위하게 공개하기 전에 네 가지 안전장치를 점검한다고 밝혔습니다. 사이버 공격이나 화학·생물·방사능·핵(CBRN) 분야 오용 방지, 프롬프트 인젝션 대응, 모델이 의도에서 벗어나는지 감시하는 정렬 모니터링, 샌드박스 강화입니다.
가격은 얼마로 발표됐나
구글이 발표한 API 가격은 출시 기간 동안 100만 토큰당 입력 2달러, 출력 10달러입니다. 캐시된 입력은 입력 가격에서 95% 할인됩니다. 영문 공식 글의 각주에 따르면 출시 기간이 끝나면 입력 4달러, 출력 20달러로 오릅니다. 출시 기간이 얼마나 이어지는지는 나와 있지 않습니다. 한국어 공식 글에는 출시 가격만 보이고, 이후 가격 각주는 확인되지 않았습니다. 아직 API가 열리지 않았기 때문에 이 가격은 실제 청구 가격이 아니라 발표된 가격입니다.
지금 한국 사용자가 할 수 있는 것
공개 여부는 공식 페이지에서 확인하기. 구글 AI 개발자 문서(ai.google.dev)의 모델 목록과 가격 페이지, 제미나이 앱에 아르곤이 올라오는지 확인하는 것이 가장 확실합니다. ‘국내 출시’ 같은 표현은 공식 발표가 나온 뒤에 믿어도 늦지 않습니다.
보안 조직이라면 페어윈드 페이지 읽어 보기. 기관 단위로 심사하는 프로그램이므로 개인 개발자가 신청하는 경로는 아닙니다. 참여 조건은 구글 딥마인드 페어윈드 프로그램 페이지에서 확인할 수 있습니다.
비용은 4달러·20달러 기준으로 잡아 두기. 출시 가격이 언제까지인지 정해지지 않았으니, 도입을 검토한다면 이후 가격을 기준으로 예산을 짜는 편이 안전합니다.
벤치마크는 외부 리더보드에 올라오는지 지켜보기. DeepSWE 공식 리더보드에 아르곤이 등재되는지, 구글 표에서 약했던 터미널 작업·에이전트 항목이 어떻게 평가되는지 보면 실제 성능을 가늠하는 데 도움이 됩니다.
평가용 과제를 미리 만들어 두기. 내 업무에서 자주 하는 코드 전환, 문서 검토 같은 작업을 몇 가지 정해 두면, 아르곤이 열렸을 때 지금 쓰는 모델과 바로 비교해 볼 수 있습니다.
자주 묻는 질문
Q. 한국에서 지금 제미나이 4 아르곤을 쓸 수 있나요?
A. 10월 1일(한국시간) 기준 쓸 수 없습니다. 지금은 구글 내부 팀과 심사를 거친 페어윈드 프로그램 참여 기관만 쓸 수 있습니다. API와 제미나이 앱에는 아직 없고, 한국 출시 일정도 발표되지 않았습니다.
Q. ‘역대 가장 지능적인 모델’이라는 말이 맞나요?
A. 이 표현은 국내 보도자료의 문구입니다. 구글 공식 글은 아르곤을 ‘새로운 프론티어 모델’이라고 부를 뿐, ‘가장 지능적인’이라는 표현은 쓰지 않았습니다. 외부 리더보드에서 단독 1위가 확인된 지표는 Vals Index이고, CWE-bench는 공동 1위입니다. 구글 표에서도 FrontierSWE v2, Terminal-Bench 4.0 등에서는 다른 모델이 앞섭니다.
Q. 제미나이 4 아르곤이 ‘Gemini 4 Pro’인가요?
A. 아닙니다. 구글 공식 글에 나온 정식 모델명은 ‘제미나이 4 아르곤(Gemini 4 Argon)’이고, 아르곤은 코드네임이 아니라 공식 모델 이름입니다. 구글은 최상위 모델에 ‘Pro’ 대신 이 이름을 붙였습니다. 로이터에 따르면 구글 대변인은 아르곤이 기존 ‘Pro’ 계열보다 큰 모델이라고 설명했습니다.
Q. 가격은 얼마인가요?
A. 발표 기준으로 출시 기간에는 100만 토큰당 입력 2달러·출력 10달러, 이후에는 4달러·20달러입니다. 캐시된 입력은 95% 할인됩니다. 출시 기간이 얼마나 되는지는 공개되지 않았습니다.
Q. DeepSWE 77.9%는 믿을 만한 수치인가요?
A. 구글이 직접 측정해 발표한 수치이고, 구글 평가 방법 문서에도 ‘자체 계산(self computed)’으로 적혀 있습니다. 10월 1일(한국시간) 기준 DeepSWE 공식 리더보드(사이트 표기 9월 22일 갱신)에는 아르곤이 아직 없어, 외부에서 다시 확인된 기록은 아닙니다.
이 글은 구글 공식 블로그(영문·한국어), 구글 딥마인드 모델 페이지, 구글 평가 방법 문서, 그리고 외부 벤치마크 사이트(vals.ai, CWE-bench, DeepSWE 리더보드)를 2026년 10월 1일(한국시간) 기준으로 직접 확인해 정리했습니다. 수치마다 누가 측정했는지 함께 적었고, 출처 링크는 본문과 글 끝에 모아 두었습니다.
Apps Script + clasp로 MCP 서버를 만들어 Spark에 연결하는 초보자 튜토리얼 (Moonlight Ops 제작기)
이 문서는 Moonlight English(TPT 스토어)의 운영 시트 Moonlight_English_Ops를 Gemini Spark에서 대화로 조회할 수 있게 만든 과정을 그대로 정리한 것입니다. 코딩 경험이 거의 없어도 순서대로 따라 하면 자기 시트로 같은 것을 만들 수 있도록 썼습니다. 실제로 만들면서 막혔던 부분과 해결 방법도 모두 넣었습니다.
1. 완성하면 무엇이 달라지나
전에는
시트를 열고, 탭을 찾고, 필터를 걸고, 원하는 행을 눈으로 찾았습니다.
812개 상품이 있는 카탈로그에서 “Easel이 아직 안 붙은 상품”을 찾으려면 직접 필터를 만들어야 했습니다.
이제는
Gemini Spark에 한국어로 물어보면 Spark가 시트를 직접 읽고 답합니다.
“이번 주 우선순위 알려줘”
“Easel 아직 안 붙은 상품 목록 뽑아줘”
“시리즈별 상품 수 세어줘”
Spark는 24시간 돌아가는 에이전트라서, “매주 월요일 아침에 파이프라인 상태를 요약해서 메일로 보내줘” 같은 예약 작업에도 이 시트를 쓸 수 있습니다.
시트 내용을 복사해서 붙여넣을 필요가 없습니다. 항상 지금 시트에 있는 최신 값을 읽습니다.
한 줄 요약: 내 구글 시트가 Spark가 쓸 수 있는 “도구”가 됐습니다.
2. 먼저 알아둘 개념 4가지
어려운 말이 몇 개 나오는데, 아래 비유만 기억하면 충분합니다.
용어
쉬운 설명
Gemini Spark
Google의 AI 에이전트입니다. 대화뿐 아니라 여러 앱을 써서 일을 대신 처리하고, 예약 작업도 합니다.
MCP (Model Context Protocol)
AI와 외부 도구를 잇는 “표준 콘센트”입니다. 이 규격에 맞춰 만들면 Spark, Claude, Cursor 같은 AI가 그 도구를 꽂아 쓸 수 있습니다.
Custom apps (맞춤 앱)
Spark의 설정 메뉴입니다. MCP 서버 주소(URL)를 넣으면 Spark가 그 도구를 쓸 수 있게 됩니다.
Google Apps Script
구글이 무료로 제공하는 작은 서버입니다. 코드를 올리면 인터넷 주소(웹앱 URL)가 생기고, 내 구글 시트도 읽을 수 있습니다.
clasp
내 컴퓨터에 있는 코드 파일을 Apps Script로 올려 주는 명령어 도구입니다. 브라우저에서 코드를 복사해 붙여넣는 수고를 줄여 줍니다.
3. 전체 구조 한눈에 보기
나 (한국어로 질문)
│
▼
Gemini Spark ──── "@Moonlight Ops 이번 주 우선순위 알려줘"
│ (MCP 규격으로 요청: tools/call ops_overview)
▼
Apps Script 웹앱 https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec?accessKey=YOUR_ACCESS_KEY
│ 1) 비밀키가 맞는지 확인
│ 2) 시트를 "읽기 전용"으로 읽음
▼
구글 시트 Moonlight_English_Ops (00_KPI, 01_Catalog, 05_Pipeline ...)
Spark가 쓸 수 있는 도구는 4개입니다.
도구 이름
하는 일
예시 질문
ops_overview
탭 목록, 탭별 행 수와 컬럼, 00_KPI 요약
“시트 전체 현황 알려줘”
ops_read_tab
한 탭을 행 단위로 읽기 (페이지 나눠 읽기 가능)
“05_Pipeline 처음 20행 보여줘”
ops_search
글자 검색, 또는 “컬럼 = 값” 조건 검색
“Junie 들어간 행 전부 찾아줘”
ops_count_by
컬럼 값별로 개수 세기
“status별 상품 수 세어줘”
4. 준비물 체크리스트
☐ 개인 구글 계정 (회사/학교 계정은 Spark 맞춤 앱이 안 됩니다)
☐ Gemini Spark 사용 권한 (Google AI Pro 또는 Ultra 구독)
☐ Gemini 설정에서 Keep Activity(활동 기록 유지) 켜기 (꺼져 있으면 맞춤 앱 연결이 안 됩니다)
☐ 연결할 구글 시트 1개
☐ Windows PC (이 튜토리얼은 PowerShell 기준입니다. Mac도 명령어만 조금 다르고 흐름은 같습니다)
참고: Google 안내 기준으로 Spark 맞춤 앱은 개인 계정, Keep Activity 켜짐, 영어 UI 등의 조건이 있습니다. 메뉴가 안 보이면 이 조건부터 확인하세요.
5. 1단계: 컴퓨터 준비 (Node.js, clasp)
5-1. Node.js 설치
https://nodejs.org 에서 LTS 버전을 받아 설치합니다. (다음, 다음, 설치만 누르면 됩니다)
PowerShell을 새로 열고 확인합니다.
node -v
npm -v
버전 숫자가 나오면 성공입니다.
5-2. clasp 설치
npm i -g @google/clasp
clasp --version
5-3. Apps Script API 켜기 (중요, 자주 빠뜨림)
브라우저에서 https://script.google.com/home/usersettings 에 들어갑니다.
Google Apps Script API를 On으로 바꿉니다.
이걸 안 켜면 clasp가 프로젝트를 만들거나 올리지 못합니다.
5-4. clasp 로그인
clasp login
브라우저가 열리면 시트를 가진 구글 계정으로 로그인하고 허용을 누릅니다. “Logged in” 같은 메시지가 나오면 끝입니다.
Created new script: https://script.google.com/d/YOUR_SCRIPT_ID/edit
└─ appsscript.json
폴더 안에 .clasp.json(어느 프로젝트와 연결됐는지 적힌 파일)과 appsscript.json(설정 파일)이 생깁니다.
7. 3단계: 코드와 설정 파일 넣기
폴더에 파일이 2개 있어야 합니다.
파일
역할
Code.gs
MCP 서버 코드 (Spark의 요청을 받아 시트를 읽고 답함)
appsscript.json
설정 파일 (웹앱으로 공개, 읽기 전용 권한, Sheets API 사용)
7-1. Code.gs
부록의 전체 코드를 Code.gs라는 이름으로 저장합니다. 메모장이나 VS Code에 붙여넣고 폴더에 저장하면 됩니다.
꼭 바꿔야 하는 곳은 딱 1줄입니다.
const SPREADSHEET_ID = 'YOUR_SPREADSHEET_ID';
시트 주소가 https://docs.google.com/spreadsheets/d/YOUR_SPREADSHEET_ID/edit라면, /d/와 /edit 사이의 YOUR_SPREADSHEET_ID 부분이 ID입니다.
내 시트에 맞게 바꾸면 좋은 곳: TOOLS 안의 description 문장들. 여기에 “이 시트가 무엇이고 탭이 어떤 의미인지”를 적어 두면 Spark가 도구를 더 똑똑하게 고릅니다.
7-2. appsscript.json
주의: 이 파일은 반드시 “BOM 없는 UTF-8″로 저장해야 합니다. Windows PowerShell 5.1의 Set-Content -Encoding utf8은 파일 맨 앞에 BOM이라는 보이지 않는 3바이트를 붙이는데, clasp가 이걸 읽지 못하고 Invalid manifest file 오류를 냅니다. (저희도 여기서 한 번 막혔습니다.)
Next → 안내문을 읽고 “I understand and accept…” 체크 → Connect
“Save your custom app” 창이 뜨고 Available actions에 도구 4개가 보이면 성공입니다.
앱 이름을 확인(예: Moonlight Ops)하고 Connect를 누릅니다.
이제 Custom apps 목록에 Moonlight Ops가 켜진 상태로 보입니다.
왜 이 방식이 잘 되나: Spark 맞춤 앱은 로그인(OAuth)이 필요한 서버와 연결할 때 실패 사례가 많이 보고돼 있습니다. 이 방식은 로그인 대신 주소에 든 키로 인증하므로, Spark 입장에서는 로그인 절차가 없는 단순한 서버라 안정적으로 연결됩니다.
13. 9단계: 실제로 써보기
Spark 대화창에서 @Moonlight Ops를 붙여 물어보면 됩니다. 처음 도구를 쓸 때 Spark가 “이 앱을 사용해도 될까요?”라고 물으면 허용합니다.
현황 파악
@Moonlight Ops 시트 전체 현황이랑 이번 주 우선순위 알려줘
찾기
@Moonlight Ops 01_Catalog에서 easel_status가 비어있는 상품 목록 보여줘
@Moonlight Ops Junie 들어간 행 전부 찾아서 탭별로 정리해줘
@Moonlight Ops 03_Listing_QA에서 listing_score 낮은 상품 10개
집계
@Moonlight Ops 01_Catalog를 series별로 개수 세줘
@Moonlight Ops 05_Pipeline stage별 개수
다른 앱과 섞어 쓰기 (Spark의 진짜 힘)
@Moonlight Ops 파이프라인에서 live 된 것들 뽑아서 구글 문서로 주간 리포트 만들어줘
매주 월요일 오전 9시에 @Moonlight Ops 로 00_KPI 요약해서 내 Gmail로 보내줘 (Spark 예약 작업)
14. 이걸로 무엇을 할 수 있게 되었나
14-1. 시트를 “대화로” 조회
필터나 수식을 몰라도 됩니다. “Easel 안 붙은 Digital 상품”처럼 말로 조건을 주면 Spark가 알맞은 도구와 조건을 골라 시트를 읽습니다.
14-2. 항상 최신 데이터
시트를 복사해서 AI에 붙여넣는 방식은 금방 옛날 자료가 됩니다. 이 방식은 물어보는 순간의 시트를 읽습니다. 시트를 고치면 바로 답이 바뀝니다.
14-3. 예약 작업과 자동화
Spark는 컴퓨터를 꺼도 클라우드에서 돌아갑니다. “매주”, “매일 아침” 같은 일정으로 시트 요약, 리포트 작성, 메일 발송을 맡길 수 있습니다.
14-4. 여러 앱을 한 번에 연결
Spark에 연결된 다른 앱과 조합할 수 있습니다.
조합
할 수 있는 일
Moonlight Ops + Google Docs/Gmail
시트 기반 주간 리포트 작성, 메일 발송
Moonlight Ops + Exa
카탈로그의 시리즈와 경쟁 상품을 웹에서 비교 조사
Moonlight Ops + Canva
우선순위 상품의 홍보 이미지 초안
Moonlight Ops + DeepWiki / GitHub
워크북 제작 도구(harness) 코드와 시트 상태를 함께 확인
14-5. 안전한 구조
읽기 전용이라 AI가 실수해도 시트가 망가지지 않습니다.
비밀 키가 없으면 아무도 못 씁니다.
Google 서버에서 무료로 돌아가서 따로 서버 비용이 없습니다.
14-6. 같은 틀로 계속 늘릴 수 있음
이번에 만든 코드는 “도구 목록(TOOLS) + 도구 실행 함수” 구조라서, 도구를 하나씩 더 붙이기 쉽습니다. 다른 시트(예: 공고 스캔 결과, 레포 대시보드)도 같은 방법으로 Spark 전용 앱으로 만들 수 있습니다. (17장 참고)
15. 실제로 겪은 문제와 해결법
직접 만들면서 부딪힌 문제 3가지입니다. 초보자가 가장 많이 막히는 곳이기도 합니다.
문제 1. clasp push에서 Invalid manifest file "appsscript.json"
증상: push가 실패했는데, 모르고 deploy까지 진행했습니다. deploy 자체는 “Deployed …”라고 성공처럼 나옵니다.
원인: PowerShell 5.1의 Set-Content -Encoding utf8이 파일 앞에 BOM을 붙였고, clasp가 이를 잘못된 JSON으로 봤습니다.
해결: 7-2의 [IO.File]::WriteAllText(..., UTF8Encoding $false) 방식으로 BOM 없이 저장한 뒤 다시 push했습니다.
교훈: push 결과에 Pushed 2 files가 나오는지 반드시 확인하세요.
문제 2. 웹앱 주소가 404를 반환
증상: 배포 ID로 주소를 열면 404가 나왔습니다. 배포 관리 화면을 보니 배포가 “Library(라이브러리)”로만 만들어져 있었습니다.
원인: 문제 1 때문에 코드와 웹앱 설정이 서버에 올라가지 않은 빈 프로젝트를 배포한 것이었습니다. 웹앱 설정(webapp)이 없는 프로젝트를 배포하면 웹앱 주소가 생기지 않습니다.
해결: push를 제대로 한 뒤 clasp deploy -i YOUR_DEPLOYMENT_ID로 같은 배포를 업데이트했습니다. 주소는 그대로 두고 웹앱으로 바뀌었습니다.
확인법: 편집기 왼쪽 파일 목록에 Code.gs가 있는지, appsscript.json 안에 webapp이 있는지 보세요.
문제 3. Specified permissions are not sufficient to call SpreadsheetApp.openById
증상: 도구 목록은 잘 나오는데, 시트를 읽는 도구만 오류가 났습니다.
원인: Apps Script의 기본 시트 도구인 SpreadsheetApp.openById는 시트 쓰기 권한 전체(spreadsheets)를 요구합니다. 저희는 안전을 위해 읽기 전용(spreadsheets.readonly)만 줬기 때문에 막혔습니다.
선택지:
권한을 전체(spreadsheets)로 넓힌다: 쉽지만, 인터넷에 열린 웹앱이 시트 수정 권한까지 갖게 됩니다.
Sheets API 고급 서비스로 읽는다 (선택한 방법): 읽기 전용 권한으로도 동작합니다.
해결: 코드를 Sheets.Spreadsheets.get, Sheets.Spreadsheets.Values.batchGet으로 바꾸고, 설정 파일에 enabledAdvancedServices로 Sheets를 추가했습니다.
그 밖에 알아두면 좋은 것
증상
원인과 해결
주소를 열면 Authorization needed
10장 권한 승인을 아직 안 함. testLocal을 한 번 실행해 승인
Spark에서 “We had trouble connecting”
일시적 오류일 수 있음. 11장 방법으로 서버가 응답하는지 확인 후 다시 시도 (저희도 다른 앱에서 두 번째 시도에 성공한 적이 있음)
Spark에 Custom apps 메뉴가 없음
개인 계정인지, Spark 권한이 있는지, Keep Activity가 켜져 있는지 확인
코드를 고쳤는데 Spark 답이 그대로
push만 하고 deploy를 안 함. clasp deploy -i YOUR_DEPLOYMENT_ID 실행
도구가 늘었는데 Spark가 모름
Connected Apps → 앱의 More details에서 동기화 시각 확인. 필요하면 앱을 다시 연결
16. 보안에 대해 꼭 알아둘 것
접근 키 = 비밀번호입니다. 주소 전체(...?accessKey=...)를 공개된 곳에 붙이지 마세요.
키가 새면: Script Properties에서 ACCESS_KEY를 새 값으로 바꾸고, Spark에서 앱을 지운 뒤 새 주소로 다시 연결하면 됩니다. 옛 키는 즉시 쓸모없어집니다.
읽기 전용 권한만 줬기 때문에, 키가 새도 시트를 고치거나 지울 수는 없습니다. 다만 시트 내용은 읽힐 수 있으니 키 관리가 중요합니다.
Google은 Spark 맞춤 앱으로 연결한 외부 서버를 검토하거나 보호하지 않는다고 안내합니다. 이 경우 서버가 내 소유이고 코드도 직접 확인할 수 있어서 그 걱정이 가장 적은 형태입니다.
나중에 “쓰기” 도구(행 추가, 상태 변경 등)를 만들고 싶다면, 권한이 넓어지는 만큼 별도 프로젝트로 분리하고 키도 따로 두는 것을 권합니다.
17. 나중에 고치고 늘리는 법
코드 수정 → 반영
cd $HOME\moonlight-ops-mcp
# Code.gs 수정 후
clasp push -f
clasp deploy -i YOUR_DEPLOYMENT_ID -d "v1.2 설명"
-i를 쓰면 주소가 그대로라서 Spark는 다시 연결할 필요가 없습니다.
도구 하나 추가하는 방법 (예: 파이프라인 마감 임박 목록)
TOOLS 배열에 도구 설명을 추가합니다. (이름, 설명, 입력값 형식)
callTool_ 함수의 switch에 case '새도구이름': data = 새함수_(args); break;를 추가합니다.
새함수_를 만듭니다. table_('05_Pipeline')로 행을 받아 원하는 조건으로 거르면 됩니다.
push, deploy 후 Spark의 앱 상세에서 도구가 늘었는지 확인합니다.
다른 시트로 새 앱 만들기
이 폴더를 통째로 복사해 SPREADSHEET_ID와 도구 설명만 바꾸고, 2단계부터 다시 하면 됩니다. 앱마다 키는 새로 만드세요.
18. 자주 묻는 질문
Q. 돈이 드나요?
Apps Script는 무료이고 Google 계정의 일일 사용량 한도 안에서 동작합니다. 개인 업무용 조회로는 넉넉합니다. Spark 자체는 AI Pro/Ultra 구독이 필요합니다.
Q. clasp 없이 브라우저만으로도 되나요?
됩니다. script.google.com에서 새 프로젝트를 만들고, 편집기에 코드를 붙여넣고, Project Settings에서 “Show appsscript.json”을 켜서 설정을 붙여넣은 뒤, Deploy → New deployment → Web app(Execute as: Me, Who has access: Anyone)으로 배포하면 됩니다. clasp는 이 과정을 명령어로 빠르게, 실수 없이 반복하게 해 줍니다.
Q. Claude나 Cursor에서도 쓸 수 있나요?
MCP 표준을 따르므로 같은 주소를 원격 MCP 서버로 등록하는 도구라면 대부분 쓸 수 있습니다. 다만 클라이언트마다 연결 방식이 조금씩 달라서 직접 확인이 필요합니다.
Q. 시트가 아주 크면 느리지 않나요?
한 번 호출할 때 시트 전체를 한 번에 읽고(batchGet), 결과는 기본 50행(최대 200행)으로 잘라 보냅니다. 셀 하나가 500자를 넘으면 잘라서 보냅니다. 수천 행 규모까지는 문제없이 쓸 수 있습니다.
Q. 컴퓨터를 꺼도 되나요?
됩니다. 서버는 Google에서, Spark는 클라우드에서 돌아갑니다. 내 컴퓨터는 코드를 고칠 때만 필요합니다.
19. 부록: 전체 코드
Code.gs
SPREADSHEET_ID를 내 시트 ID로 바꾸세요. TOOLS의 설명 문장은 내 시트 내용에 맞게 고치면 Spark가 더 정확하게 도구를 씁니다.
/**
* Moonlight Ops MCP server (read-only) for Gemini Spark custom apps.
*
* Deploy: Deploy > New deployment > Web app
* Execute as: Me | Who has access: Anyone
* Script property ACCESS_KEY must be set (Project Settings > Script properties).
* Spark URL: https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec?accessKey=YOUR_ACCESS_KEY
*/
const SPREADSHEET_ID = 'YOUR_SPREADSHEET_ID'; // <- 내 시트 ID로 바꾸기
const SERVER_INFO = { name: 'moonlight-ops', version: '1.0.0' };
const SUPPORTED_PROTOCOLS = ['2025-06-18', '2025-03-26', '2024-11-05'];
const DEFAULT_LIMIT = 50;
const MAX_LIMIT = 200;
const MAX_CELL_CHARS = 500;
const FILTERS_SCHEMA = {
type: 'object',
description: 'Exact, case-insensitive column matches, e.g. {"status":"active","easel_status":""}. Empty string means the cell is blank.',
additionalProperties: { type: 'string' }
};
const COLUMNS_SCHEMA = {
type: 'array',
items: { type: 'string' },
description: 'Optional. Only return these columns.'
};
const TOOLS = [
{
name: 'ops_overview',
description: 'Overview of the Moonlight English (Teachers Pay Teachers store) ops spreadsheet Moonlight_English_Ops: every tab with data row count and column headers, plus the 00_KPI snapshot and this-week priorities. Call this first.',
inputSchema: { type: 'object', properties: {} },
annotations: { title: 'Ops overview', readOnlyHint: true }
},
{
name: 'ops_read_tab',
description: 'Read rows from one tab as objects keyed by header. Tabs: 00_KPI, 01_Catalog (master SKU list), 02_Series, 03_Listing_QA, 04_Content_QA, 05_Pipeline, 06_Easel, 07_Marketing, 08_Hygiene. Partial names like "catalog" work.',
inputSchema: {
type: 'object',
properties: {
tab: { type: 'string', description: 'Tab name or partial name, e.g. "01_Catalog" or "pipeline".' },
offset: { type: 'integer', minimum: 0, description: 'Data rows to skip. Default 0.' },
limit: { type: 'integer', minimum: 1, maximum: MAX_LIMIT, description: 'Max rows to return. Default 50.' },
columns: COLUMNS_SCHEMA
},
required: ['tab']
},
annotations: { title: 'Read tab', readOnlyHint: true }
},
{
name: 'ops_search',
description: 'Find rows by free text and/or exact column filters, in one tab or across all tabs. Examples: query "Junie"; tab "01_Catalog" with filters {"easel_status":""}; tab "05_Pipeline" with filters {"stage":"live"}.',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Case-insensitive substring matched against every cell.' },
tab: { type: 'string', description: 'Optional tab name or partial name. Omit to search all tabs.' },
filters: FILTERS_SCHEMA,
limit: { type: 'integer', minimum: 1, maximum: MAX_LIMIT, description: 'Max rows to return. Default 50.' },
columns: COLUMNS_SCHEMA
}
},
annotations: { title: 'Search rows', readOnlyHint: true }
},
{
name: 'ops_count_by',
description: 'Count rows in a tab grouped by one column. Examples: 01_Catalog by status, line, series, easel_status, harness_status, edition; 05_Pipeline by stage. Optional filters.',
inputSchema: {
type: 'object',
properties: {
tab: { type: 'string', description: 'Tab name or partial name. Default 01_Catalog.' },
column: { type: 'string', description: 'Column header to group by.' },
filters: FILTERS_SCHEMA
},
required: ['column']
},
annotations: { title: 'Count by column', readOnlyHint: true }
}
];
// ---------- HTTP entry points ----------
function doGet(e) {
if (!isAuthorized_(e)) return json_({ error: 'unauthorized' });
return json_({ ok: true, server: SERVER_INFO, transport: 'POST JSON-RPC 2.0 (MCP streamable HTTP, JSON responses)' });
}
function doPost(e) {
if (!isAuthorized_(e)) return json_(rpcError_(null, -32001, 'Unauthorized: missing or wrong accessKey'));
let msg;
try {
msg = JSON.parse((e.postData && e.postData.contents) || '');
} catch (err) {
return json_(rpcError_(null, -32700, 'Parse error'));
}
if (Array.isArray(msg)) {
const out = msg.map(handle_).filter(function (r) { return r; });
return out.length ? json_(out) : empty_();
}
const res = handle_(msg);
return res ? json_(res) : empty_();
}
// ---------- JSON-RPC / MCP ----------
function handle_(msg) {
if (!msg || typeof msg !== 'object') return rpcError_(null, -32600, 'Invalid Request');
const id = msg.id;
const isNotification = id === undefined || id === null;
const method = msg.method;
if (!method) return null; // a client response; nothing to do
try {
switch (method) {
case 'initialize': {
const requested = msg.params && msg.params.protocolVersion;
const version = SUPPORTED_PROTOCOLS.indexOf(requested) >= 0 ? requested : SUPPORTED_PROTOCOLS[0];
return rpcResult_(id, {
protocolVersion: version,
capabilities: { tools: { listChanged: false } },
serverInfo: SERVER_INFO,
instructions: 'Read-only access to Moonlight_English_Ops, the ops sheet for the Moonlight English TPT store. Start with ops_overview, then use ops_search / ops_read_tab / ops_count_by.'
});
}
case 'ping':
return rpcResult_(id, {});
case 'tools/list':
return rpcResult_(id, { tools: TOOLS });
case 'tools/call':
return rpcResult_(id, callTool_(msg.params || {}));
case 'resources/list':
return rpcResult_(id, { resources: [] });
case 'resources/templates/list':
return rpcResult_(id, { resourceTemplates: [] });
case 'prompts/list':
return rpcResult_(id, { prompts: [] });
default:
if (isNotification) return null; // notifications/initialized etc.
return rpcError_(id, -32601, 'Method not found: ' + method);
}
} catch (err) {
return isNotification ? null : rpcError_(id, -32603, String((err && err.message) || err));
}
}
function callTool_(params) {
const name = params.name;
const args = params.arguments || {};
let data;
try {
switch (name) {
case 'ops_overview': data = overview_(); break;
case 'ops_read_tab': data = readTab_(args); break;
case 'ops_search': data = search_(args); break;
case 'ops_count_by': data = countBy_(args); break;
default:
return { content: [{ type: 'text', text: 'Unknown tool: ' + name }], isError: true };
}
} catch (err) {
return { content: [{ type: 'text', text: 'Error: ' + ((err && err.message) || err) }], isError: true };
}
return { content: [{ type: 'text', text: JSON.stringify(data) }] };
}
// ---------- Tools ----------
function overview_() {
const meta = meta_();
const all = allValues_();
const tabs = sheetNames_().map(function (name) {
const values = all[name] || [];
const header = (values[0] || []).filter(String);
const dataRows = values.slice(1).filter(function (r) { return r.some(String); }).length;
return { name: name, data_rows: dataRows, header: header };
});
const kpiName = sheetNames_().filter(function (n) { return /kpi/i.test(n); })[0];
const kpi = kpiName ? rawRows_(kpiName, 60) : [];
return { spreadsheet: meta.properties.title, url: meta.spreadsheetUrl, tabs: tabs, kpi_rows: kpi };
}
function readTab_(a) {
const sheet = resolveSheet_(a.tab);
if (/kpi/i.test(sheet)) {
return { tab: sheet, note: 'KPI tab has no header row; raw rows returned.', rows: rawRows_(sheet, clampLimit_(a.limit)) };
}
const t = table_(sheet);
const offset = Math.max(0, parseInt(a.offset, 10) || 0);
const limit = clampLimit_(a.limit);
const rows = t.rows.slice(offset, offset + limit).map(function (r) { return pick_(r, a.columns); });
return { tab: sheet, header: t.header, total_rows: t.rows.length, offset: offset, returned: rows.length, rows: rows };
}
function search_(a) {
const q = a.query ? String(a.query).toLowerCase().trim() : '';
const filters = a.filters || {};
if (!q && !Object.keys(filters).length) throw new Error('Give a query and/or filters.');
const sheets = a.tab ? [resolveSheet_(a.tab)] : sheetNames_().filter(function (n) { return !/kpi/i.test(n); });
const limit = clampLimit_(a.limit);
const out = [];
let total = 0;
sheets.forEach(function (sheet) {
const t = table_(sheet);
t.rows.forEach(function (r) {
if (!matches_(r, q, filters)) return;
total++;
if (out.length < limit) out.push(Object.assign({ _tab: sheet }, pick_(r, a.columns)));
});
});
return { query: a.query || null, filters: filters, total_matches: total, returned: out.length, rows: out };
}
function countBy_(a) {
const sheet = resolveSheet_(a.tab || '01_Catalog');
const t = table_(sheet);
const want = String(a.column || '').toLowerCase().trim();
const col = t.header.filter(function (h) { return h.toLowerCase() === want; })[0];
if (!col) throw new Error('Unknown column "' + a.column + '". Columns: ' + t.header.join(', '));
const counts = {};
t.rows.forEach(function (r) {
if (!matches_(r, '', a.filters || {})) return;
const v = r[col] === undefined ? '(blank)' : String(r[col]);
counts[v] = (counts[v] || 0) + 1;
});
const groups = Object.keys(counts)
.map(function (k) { return { value: k, count: counts[k] }; })
.sort(function (x, y) { return y.count - x.count; });
const total = groups.reduce(function (s, g) { return s + g.count; }, 0);
return { tab: sheet, column: col, total: total, groups: groups };
}
// ---------- Sheet helpers ----------
// Uses the Sheets advanced service (read-only scope). Sheets are addressed by tab name.
let META_, ALL_;
function meta_() {
if (!META_) {
META_ = Sheets.Spreadsheets.get(SPREADSHEET_ID, { fields: 'properties.title,spreadsheetUrl,sheets.properties.title' });
}
return META_;
}
function sheetNames_() {
return (meta_().sheets || []).map(function (s) { return s.properties.title; });
}
function a1_(name) {
return "'" + String(name).replace(/'/g, "''") + "'";
}
function allValues_() {
if (!ALL_) {
const names = sheetNames_();
const res = Sheets.Spreadsheets.Values.batchGet(SPREADSHEET_ID, { ranges: names.map(a1_), valueRenderOption: 'FORMATTED_VALUE' });
ALL_ = {};
(res.valueRanges || []).forEach(function (vr, i) { ALL_[names[i]] = vr.values || []; });
}
return ALL_;
}
function values_(name) {
const rows = allValues_()[name] || [];
let width = 0;
rows.forEach(function (r) { width = Math.max(width, r.length); });
return rows.map(function (r) {
const out = [];
for (let i = 0; i < width; i++) out.push(r[i] === undefined || r[i] === null ? '' : String(r[i]));
return out;
});
}
function resolveSheet_(tab) {
if (!tab) throw new Error('tab is required.');
const names = sheetNames_();
const q = String(tab).toLowerCase().trim();
const hit =
names.filter(function (n) { return n.toLowerCase() === q; })[0] ||
names.filter(function (n) { return n.toLowerCase().indexOf(q) >= 0; })[0];
if (!hit) throw new Error('No tab matches "' + tab + '". Tabs: ' + names.join(', '));
return hit;
}
function table_(sheet) {
const values = values_(sheet);
if (!values.length) return { header: [], rows: [] };
const header = values[0].map(function (h, i) { return String(h).trim() || 'col_' + (i + 1); });
const rows = [];
for (let r = 1; r < values.length; r++) {
const row = values[r];
if (row.every(function (v) { return v === ''; })) continue;
const obj = { _row: r + 1 };
header.forEach(function (h, i) {
if (row[i] !== '') obj[h] = clip_(row[i]);
});
rows.push(obj);
}
return { header: header, rows: rows };
}
function rawRows_(sheet, limit) {
return values_(sheet)
.filter(function (r) { return r.some(String); })
.slice(0, limit)
.map(function (r) {
let end = r.length;
while (end > 0 && r[end - 1] === '') end--;
return r.slice(0, end).map(clip_);
});
}
function matches_(row, q, filters) {
const keys = Object.keys(row);
for (const k in filters) {
const want = String(filters[k]).toLowerCase().trim();
const key = keys.filter(function (h) { return h.toLowerCase() === String(k).toLowerCase().trim(); })[0];
const have = key ? String(row[key]).toLowerCase().trim() : '';
if (have !== want) return false;
}
if (q) {
return keys.some(function (k) { return k !== '_row' && String(row[k]).toLowerCase().indexOf(q) >= 0; });
}
return true;
}
function pick_(row, columns) {
if (!columns || !columns.length) return row;
const out = { _row: row._row };
const wanted = columns.map(function (c) { return String(c).toLowerCase().trim(); });
Object.keys(row).forEach(function (k) {
if (wanted.indexOf(k.toLowerCase()) >= 0) out[k] = row[k];
});
return out;
}
function clip_(v) {
const s = String(v);
return s.length > MAX_CELL_CHARS ? s.slice(0, MAX_CELL_CHARS) + '…' : s;
}
function clampLimit_(n) {
const v = parseInt(n, 10);
if (!v || v < 1) return DEFAULT_LIMIT;
return Math.min(v, MAX_LIMIT);
}
// ---------- Auth / output ----------
function isAuthorized_(e) {
const key = PropertiesService.getScriptProperties().getProperty('ACCESS_KEY');
if (!key) return false;
const given = e && e.parameter && e.parameter.accessKey;
return given === key;
}
function rpcResult_(id, result) {
return { jsonrpc: '2.0', id: id, result: result };
}
function rpcError_(id, code, message) {
return { jsonrpc: '2.0', id: id === undefined ? null : id, error: { code: code, message: message } };
}
function json_(obj) {
return ContentService.createTextOutput(JSON.stringify(obj)).setMimeType(ContentService.MimeType.JSON);
}
function empty_() {
return ContentService.createTextOutput('').setMimeType(ContentService.MimeType.JSON);
}
// ---------- Manual test (run from the editor) ----------
function testLocal() {
console.log(JSON.stringify(handle_({ jsonrpc: '2.0', id: 1, method: 'tools/list' })).slice(0, 500));
console.log(JSON.stringify(callTool_({ name: 'ops_overview', arguments: {} })).slice(0, 1500));
console.log(JSON.stringify(callTool_({ name: 'ops_count_by', arguments: { column: 'status' } })));
}
큰 레포에서 중요한 폴더가 위키에서 빠진다면, 레포 루트에 .devin/wiki.json 파일을 두어 어떤 페이지를 만들지 직접 지정할 수 있습니다. (공식 문서의 “Steering DeepWiki” 참고)
8. 5단계: 실제로 써보기
Spark 대화창에서 @DeepWiki를 붙여 물어봅니다. 처음 도구를 쓸 때 Spark가 사용 허락을 물으면 허용합니다.
구조 파악
@DeepWiki facebook/react 위키 목차 보여줘
@DeepWiki Reasonofmoon/gov-scan-daily 전체 구조를 초보자 눈높이로 요약해줘
특정 질문
@DeepWiki langchain-ai/langchain 에서 retriever는 어떤 흐름으로 동작해?
@DeepWiki 이 레포에서 스케줄 실행은 어느 파일이 담당해? (Reasonofmoon/gov-scan-daily)
다른 앱과 섞어 쓰기
@DeepWiki 로 Reasonofmoon/gov-scan-daily 구조를 정리해서 구글 문서로 만들어줘
@DeepWiki 로 레포 3개(A, B, C)를 비교해서 표로 정리해줘
9. 이걸로 무엇을 할 수 있게 되었나
9-1. 오래된 레포 빠르게 복습
레포가 많을수록 “이거 뭐였지?”가 자주 생깁니다. 색인만 해 두면 README보다 자세한 구조 설명을 바로 받을 수 있습니다. 공개 레포 정리(아카이브 후보 고르기, 재활용할 코드 찾기)에 특히 좋습니다.
9-2. 오픈소스 도입 전 조사
새 라이브러리나 AI 도구를 쓰기 전에 “핵심 구조”, “설정 방법”, “확장 포인트”를 먼저 물어보면 문서만 볼 때보다 빠르게 판단할 수 있습니다. AI 도구 카탈로그에 새 도구를 소개할 때 근거 자료로도 쓸 수 있습니다.
9-3. 트렌드 레포 해설
깃허브 급상승 레포를 볼 때, 이름만 보고 넘기지 않고 “이 레포가 실제로 뭘 하는지 3줄 요약”을 받을 수 있습니다. (색인된 레포 기준)
9-4. 다른 도구와 역할 나누기
도구
잘하는 것
DeepWiki
공개 레포의 구조와 동작 원리 설명
Context7
라이브러리의 최신 공식 문서와 코드 예시
Github Personal Bridge
비공개 레포 포함 내 깃허브 파일과 이슈 직접 조회
같은 질문도 목적에 따라 알맞은 도구를 지정(@)하면 답이 더 정확해집니다.
10. 실제로 겪은 문제와 해결법
문제 1. 첫 연결에서 “We had trouble connecting to this server”
증상: 주소를 넣고 Connect까지 눌렀는데 오류가 나며 입력 화면으로 돌아왔습니다.
확인: 서버에 직접 요청을 보내 보니 DeepWiki는 정상 응답하고 있었습니다.
해결: 같은 주소로 한 번 더 시도하니 바로 연결됐습니다. 일시적인 문제였습니다.
교훈: 이 오류가 한 번 나왔다고 바로 포기하지 말고, 잠시 후 다시 시도해 보세요.
문제 2. 내 레포를 물어보면 “Repository not found”
원인: DeepWiki에 아직 색인되지 않은 레포입니다.
해결: 7장 방법으로 deepwiki.com에서 먼저 색인합니다.
그 밖에
증상
해결
Custom apps 메뉴가 없음
개인 계정인지, Spark 권한과 Keep Activity가 켜져 있는지 확인
비공개 레포가 안 됨
정상입니다. DeepWiki MCP는 공개 레포 전용 (11장 참고)
Spark가 DeepWiki를 안 씀
질문 앞에 @DeepWiki를 붙이고 레포 이름을 소유자/레포로 정확히 쓰기
11. 한계와 주의할 점
공개 레포만 됩니다. 비공개 레포까지 쓰려면 Devin 계정을 만들고 API 키로 연결하는 별도의 Devin MCP가 필요합니다. 비공개 레포는 Github Personal Bridge 같은 다른 도구로 보는 편이 현실적입니다.
위키는 색인 시점 기준입니다. 레포를 크게 바꿨다면 deepwiki.com에서 위키를 새로 만들게 해야 최신 내용이 반영됩니다.
AI가 만든 설명입니다. 중요한 결정(보안, 배포 설정 등)은 답에 나온 파일을 직접 열어 확인하세요.
보내는 질문은 외부 서버로 갑니다. Google은 맞춤 앱으로 연결한 외부 서버를 검토하거나 보호하지 않는다고 안내합니다. 민감한 내부 정보를 질문에 넣지 마세요.
12. 자주 묻는 질문
Q. 돈이 드나요?
공개 레포용 DeepWiki와 DeepWiki MCP는 무료입니다.
Q. 연결을 끊고 싶어요.
Connected Apps → DeepWiki의 More details → Disconnect 또는 Remove app을 누릅니다. 잠깐만 끄고 싶으면 목록의 스위치만 끄면 됩니다.
Q. Claude나 Cursor에서도 쓸 수 있나요?
네. 같은 주소 https://mcp.deepwiki.com/mcp를 각 도구의 MCP 설정에 넣으면 됩니다. 예: Claude Code는 claude mcp add -s user -t http deepwiki https://mcp.deepwiki.com/mcp