구글 시트로 Gemini Spark 커스텀 앱 만들기 (Apps Script·MCP)

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 설치

  1. https://nodejs.org 에서 LTS 버전을 받아 설치합니다. (다음, 다음, 설치만 누르면 됩니다)
  2. PowerShell을 새로 열고 확인합니다.
node -v
npm -v

버전 숫자가 나오면 성공입니다.

5-2. clasp 설치

npm i -g @google/clasp
clasp --version

5-3. Apps Script API 켜기 (중요, 자주 빠뜨림)

  1. 브라우저에서 https://script.google.com/home/usersettings 에 들어갑니다.
  2. Google Apps Script API를 On으로 바꿉니다.

이걸 안 켜면 clasp가 프로젝트를 만들거나 올리지 못합니다.

5-4. clasp 로그인

clasp login

브라우저가 열리면 시트를 가진 구글 계정으로 로그인하고 허용을 누릅니다. “Logged in” 같은 메시지가 나오면 끝입니다.

6. 2단계: Apps Script 프로젝트 만들기

작업할 폴더를 만들고, 그 안에서 프로젝트를 만듭니다.

mkdir $HOME\moonlight-ops-mcp
cd $HOME\moonlight-ops-mcp
clasp create --type standalone --title "Moonlight Ops MCP"

성공하면 이렇게 나옵니다.

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 오류를 냅니다. (저희도 여기서 한 번 막혔습니다.)

아래 명령을 폴더 안에서 그대로 실행하면 안전하게 저장됩니다.

$m = @'
{
  "timeZone": "Asia/Seoul",
  "dependencies": {
    "enabledAdvancedServices": [
      { "userSymbol": "Sheets", "version": "v4", "serviceId": "sheets" }
    ]
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "webapp": {
    "executeAs": "USER_DEPLOYING",
    "access": "ANYONE_ANONYMOUS"
  },
  "oauthScopes": [
    "https://www.googleapis.com/auth/spreadsheets.readonly"
  ]
}
'@
[IO.File]::WriteAllText("$PWD\appsscript.json", $m, (New-Object Text.UTF8Encoding $false))

각 줄의 의미:

설정 의미
enabledAdvancedServices: Sheets 시트를 Sheets API로 읽습니다. 읽기 전용 권한만으로 읽기 위해 필요합니다. (15장 문제 2 참고)
webapp.executeAs: USER_DEPLOYING 웹앱이 내 권한으로 시트를 읽습니다.
webapp.access: ANYONE_ANONYMOUS Spark가 로그인 없이 주소로 접근할 수 있게 합니다. 대신 코드가 비밀 접근 키로 막습니다.
oauthScopes: spreadsheets.readonly 이 프로그램은 시트를 읽기만 할 수 있습니다. 수정/삭제 불가.

8. 4단계: 올리기(push)와 배포(deploy)

clasp push -f

이렇게 나오면 성공입니다.

Pushed 2 files.
└─ appsscript.json
└─ Code.gs

Pushed 2 files가 안 나오고 오류가 나면 절대 다음 단계로 넘어가지 마세요. (15장 참고)

이어서 배포합니다.

clasp deploy --description "v1"
Deployed YOUR_DEPLOYMENT_ID @2

YOUR_DEPLOYMENT_ID 자리에 나오는 긴 문자열(AKfycb…로 시작)이 배포 ID입니다. 메모해 두세요. 웹앱 주소는 이렇게 됩니다.

https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec

팁: 나중에 코드를 고치고 다시 배포할 때는 clasp deploy -i YOUR_DEPLOYMENT_ID -d "v2"처럼 -i를 붙이세요. 그래야 주소가 바뀌지 않아서 Spark 연결을 다시 할 필요가 없습니다.

9. 5단계: 접근 키 저장하기

웹앱은 누구나 주소로 접근할 수 있게 열려 있으므로, 비밀 키를 아는 요청만 받도록 합니다.

9-1. 랜덤 키 만들기

PowerShell에서 실행하면 안전한 랜덤 문자열이 나옵니다.

$b = New-Object byte[] 24
[Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b)
[Convert]::ToBase64String($b).Replace('+','-').Replace('/','_').TrimEnd('=')

나온 문자열을 복사해 둡니다. 비밀번호처럼 다루세요. 채팅, 스크린샷, 공개 저장소에 올리면 안 됩니다.

9-2. Script Properties에 저장

  1. https://script.google.com 에서 프로젝트를 엽니다.
  2. 왼쪽 톱니바퀴 **Project Settings(프로젝트 설정)**를 누릅니다.
  3. 맨 아래 Script Properties 에서 Add script property를 누릅니다.
  4. Property: ACCESS_KEY, Value: 방금 만든 키
  5. Save script properties를 누릅니다.

코드는 이 값을 읽어서, 요청 주소의 ?accessKey= 값과 같을 때만 응답합니다. 키를 코드에 직접 쓰지 않아서, 나중에 코드를 공유해도 키가 새지 않습니다.

10. 6단계: 권한 승인하기

웹앱이 “내 권한으로” 시트를 읽으려면, 처음 한 번 내가 직접 허락해야 합니다. 이걸 안 하면 웹앱 주소가 Authorization needed 페이지만 돌려줍니다.

  1. 프로젝트 편집기(Editor)에서 Code.gs를 엽니다.
  2. 위쪽 함수 선택 칸에서 **testLocal**을 고릅니다.
  3. **Run(실행)**을 누릅니다.
  4. “Authorization required” 창이 뜨면 Review permissions를 누릅니다.
  5. 계정을 고르면 “Google hasn’t verified this app” 경고가 나옵니다.
    • 놀라지 마세요. 개발자가 나 자신이라서 나오는 정상 경고입니다.
    • Advanced(고급) → Go to Moonlight Ops MCP (unsafe) 를 누릅니다.
  6. 요청 권한이 “See all your Google Sheets spreadsheets”(시트 보기) 하나뿐인지 확인하고 **Continue(계속)**를 누릅니다.
  7. 아래 Execution log에 도구 목록과 시트 요약이 찍히면 성공입니다.

11. 7단계: 제대로 동작하는지 확인하기

Spark에 붙이기 전에 직접 호출해 보면 문제를 빨리 찾을 수 있습니다. PowerShell에서 실행합니다. (YOUR_DEPLOYMENT_ID, YOUR_ACCESS_KEY를 바꾸세요)

$url = "https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec?accessKey=YOUR_ACCESS_KEY"
$body = '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Invoke-RestMethod -Uri $url -Method Post -ContentType "application/json" -Body $body | ConvertTo-Json -Depth 6

ops_overview, ops_read_tab, ops_search, ops_count_by 4개가 보이면 성공입니다.

시트를 실제로 읽는지도 확인합니다.

$body = '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"ops_overview","arguments":{}}}'
Invoke-RestMethod -Uri $url -Method Post -ContentType "application/json" -Body $body | ConvertTo-Json -Depth 6

틀린 키로도 한 번 해 보세요. Unauthorized 오류가 나와야 정상입니다.

저희가 실제로 확인한 결과:

  • initialize, tools/list 정상 응답
  • ops_overview가 실제 시트 값 반환 (01_Catalog 812행, 03_Listing_QA 774행, 02_Series 73행 등)
  • 틀린 키는 Unauthorized: missing or wrong accessKey로 거부

12. 8단계: Gemini Spark에 연결하기

  1. https://gemini.google.com/spark/apps 로 갑니다. (또는 Gemini 왼쪽 메뉴 Spark → Connected Apps)
  2. Custom apps 섹션에서 Add a custom app을 누릅니다.
  3. MCP Server URL 칸에 키까지 포함한 주소를 넣습니다.
    https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec?accessKey=YOUR_ACCESS_KEY
  4. Next → 안내문을 읽고 “I understand and accept…” 체크 → Connect
  5. “Save your custom app” 창이 뜨고 Available actions에 도구 4개가 보이면 성공입니다.
  6. 앱 이름을 확인(예: 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)만 줬기 때문에 막혔습니다.
  • 선택지:
    1. 권한을 전체(spreadsheets)로 넓힌다: 쉽지만, 인터넷에 열린 웹앱이 시트 수정 권한까지 갖게 됩니다.
    2. 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는 다시 연결할 필요가 없습니다.

도구 하나 추가하는 방법 (예: 파이프라인 마감 임박 목록)

  1. TOOLS 배열에 도구 설명을 추가합니다. (이름, 설명, 입력값 형식)
  2. callTool_ 함수의 switch에 case '새도구이름': data = 새함수_(args); break;를 추가합니다.
  3. 새함수_를 만듭니다. table_('05_Pipeline')로 행을 받아 원하는 조건으로 거르면 됩니다.
  4. 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' } })));
}

appsscript.json

반드시 BOM 없이 저장하세요 (7-2의 PowerShell 명령 사용).

{
  "timeZone": "Asia/Seoul",
  "dependencies": {
    "enabledAdvancedServices": [
      {
        "userSymbol": "Sheets",
        "version": "v4",
        "serviceId": "sheets"
      }
    ]
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "webapp": {
    "executeAs": "USER_DEPLOYING",
    "access": "ANYONE_ANONYMOUS"
  },
  "oauthScopes": [
    "https://www.googleapis.com/auth/spreadsheets.readonly"
  ]
}

작성: 2026-09-28. Moonlight Ops v1.1 (읽기 전용 도구 4개) 기준.

Gemini Spark에 Exa 연결하기

AI용 검색엔진 Exa를 Spark 도구로 붙이는 초보자 튜토리얼

Exa는 AI가 쓰기 좋게 만든 웹 검색엔진입니다. 검색 결과를 링크 목록이 아니라 바로 읽을 수 있는 깔끔한 본문으로 돌려줍니다. Gemini Spark에 연결하면 조사, 트렌드 추적, 경쟁 분석 같은 일을 더 깊고 정확하게 맡길 수 있습니다.

난이도: 쉬움 (코드 없음, Exa 가입 1회). 걸리는 시간: 약 5분.

1. 완성하면 무엇이 달라지나

전에는

  • Spark가 웹을 찾아볼 수는 있지만, 검색 결과의 질이나 깊이를 조절하기 어려웠습니다.
  • 여러 사이트를 비교하는 조사는 검색 → 링크 열기 → 읽기 → 정리를 사람이 반복해야 했습니다.

이제는

  • @Exa로 AI용 검색을 직접 시킬 수 있습니다. 결과가 본문 형태로 와서 Spark가 바로 읽고 정리합니다.
  • 특정 URL 여러 개를 한 번에 읽혀서 비교하게 할 수 있습니다.
  • 로그인 방식으로 연결했기 때문에 **여러 단계 조사를 알아서 해 주는 agent_run**까지 쓸 수 있습니다.

2. 먼저 알아둘 개념

용어 쉬운 설명
Exa AI를 위한 검색엔진. 키워드가 아니라 “이런 페이지를 찾아줘”처럼 설명으로 검색하는 데 강합니다.
Exa MCP Exa 검색을 AI 도구로 쓸 수 있게 열어 둔 창구. 주소는 https://mcp.exa.ai/mcp
OAuth(로그인 연결) 비밀번호를 넘기지 않고 “이 앱이 내 계정을 써도 된다”고 허락하는 방식. 우리가 흔히 보는 “Google로 로그인”이 이 방식입니다.
크레딧 Exa 사용량을 계산하는 단위. 새 계정에는 무료 크레딧이 들어 있습니다.

3. 준비물

  • ☐ Gemini Spark를 쓸 수 있는 개인 구글 계정 (Google AI Pro/Ultra)
  • ☐ Gemini 설정에서 Keep Activity(활동 기록 유지) 켜기
  • ☐ Exa 계정 (없으면 연결 과정에서 Google 계정으로 바로 가입할 수 있습니다)

4. 연결 방식 3가지 이해하기

Exa MCP는 3가지 방식으로 쓸 수 있습니다.

방식 특징 쓸 수 있는 도구
키 없이(Keyless) 가입 없이 무료. 대신 사용량 제한이 있음 web_search_exa, web_fetch_exa
로그인(OAuth) Exa 계정으로 로그인. 사용량은 내 Exa 계정에 기록 위 2개 + agent_run
API 키 로그인 연결을 지원하지 않는 프로그램용 위와 같음

Spark에서는 로그인(OAuth) 방식으로 연결됩니다. 주소만 넣어도 Spark가 Exa의 로그인 절차를 자동으로 시작합니다. 결과적으로 가장 기능이 많은 형태로 연결됩니다.

5. 1단계: Spark에 주소 넣기

  1. https://gemini.google.com/spark/apps 로 갑니다. (Gemini 왼쪽 메뉴 Spark → Connected Apps)
  2. Custom apps 섹션에서 Add a custom app을 누릅니다.
  3. MCP Server URL에 아래 주소를 넣고 Next를 누릅니다.
    https://mcp.exa.ai/mcp
  4. “Connect this MCP server” 안내문을 읽고 “I understand and accept…” 체크 → Connect

6. 2단계: 구글 계정 연결 동의

Connect를 누르면 한국어로 이런 창이 뜹니다.

Google 계정과 [Custom] Exa 계정 연결하기 Google에서 다음 작업을 할 수 있습니다.

  • 모델 컨텍스트 프로토콜(MCP) 서버 도구 실행
  • [Custom] Exa에서 데이터 검색 및 관리
  • [Custom] Exa에 데이터 공유

내용을 확인하고 동의 및 계속을 누릅니다.

이 창은 “Gemini(Google)가 Exa 계정을 대신 써도 되는가”를 묻는 것입니다. Exa에 구글 비밀번호가 넘어가지 않습니다.

7. 3단계: Exa 로그인(가입)

페이지가 **Exa 로그인 화면(“Get started with Exa”)**으로 바뀝니다.

  1. Continue with Google을 누릅니다. (이메일로 가입해도 됩니다)
  2. 구글 계정 선택 화면에서 Spark와 같은 계정을 고릅니다.
  3. “Google will allow Exa to access this info about you”(이름, 프로필 사진, 이메일) 화면에서 Continue를 누릅니다.
  4. Exa 계정이 없었다면 이 과정에서 자동으로 가입되고 무료 크레딧이 들어갑니다.

로그인이 끝나면 자동으로 Gemini Spark 페이지로 돌아옵니다. 주소창에 custom_mcp_action=auth_complete가 보이면 인증이 끝난 것입니다.

8. 4단계: 저장하고 확인하기

  1. “Save your custom app” 창이 뜹니다.
    • App name: Exa
    • Available actions: web_search_exa, web_fetch_exa, agent_run
  2. Connect를 누릅니다.
  3. Custom apps 목록에 Exa가 켜진 상태로 보이면 성공입니다.
도구 하는 일 주로 쓰는 입력
web_search_exa 웹 검색 후 결과 본문을 깔끔한 텍스트로 가져옴 검색어(설명형), 결과 수
web_fetch_exa 알고 있는 URL(여러 개 가능)의 본문을 마크다운으로 읽음 URL 목록
agent_run 여러 번 검색하고 확인하는 다단계 조사를 알아서 수행. 목록 만들기, 조건 검증, 표 형태 결과에 강함 조사 요청

9. 5단계: 실제로 써보기

Spark 대화창에서 @Exa를 붙여 물어봅니다.

검색 요령: 키워드보다 “원하는 페이지를 설명”하세요.

  • 약함: AI 에이전트 교육
  • 강함: @Exa 초등 영어 교사가 수업에 AI 에이전트를 쓴 실제 사례를 소개한 블로그 글

조사

  • @Exa 이번 주에 공개된 오픈소스 AI 코딩 에이전트 출시 소식 찾아서 5줄 요약
  • @Exa Teachers Pay Teachers에서 잘 팔리는 novel study 상품의 공통 특징을 다룬 글 찾아줘

URL 읽기

  • @Exa 이 3개 페이지 읽고 가격과 기능 비교표 만들어줘: (URL1) (URL2) (URL3)

다단계 조사 (agent_run)

  • @Exa 한국 학원용 AI 도입 컨설팅 서비스를 하는 회사 10곳을 찾아서 이름, 사이트, 가격 공개 여부를 표로 정리해줘

다른 앱과 섞어 쓰기

  • @Exa 로 오늘 AI 뉴스 3개 찾아서 요약한 뒤 구글 문서로 저장해줘
  • 매일 오후 12시에 @Exa 로 새 AI 모델 출시 소식을 찾아서 내 Gmail로 보내줘 (Spark 예약 작업)

10. 이걸로 무엇을 할 수 있게 되었나

10-1. 더 정확한 조사

설명형 검색이라 “딱 이런 글”을 찾는 데 강합니다. 결과를 본문으로 받아서 Spark가 출처를 읽고 정리하므로, 링크만 나열하는 답보다 쓸모 있습니다.

10-2. 트렌드와 신규 출시 추적

AI 도구, 모델 출시처럼 빠르게 바뀌는 주제를 Spark 예약 작업과 묶으면 매일 자동 브리핑이 됩니다. SNS에 올릴 인사이트 글의 재료 모으기에도 좋습니다.

10-3. 시장과 경쟁 조사

TPT 같은 마켓의 경쟁 상품, 비슷한 서비스의 가격 정책, 사례 수집을 agent_run으로 표 형태까지 한 번에 받을 수 있습니다.

10-4. 여러 페이지 비교

web_fetch_exa로 URL 여러 개를 한 번에 읽혀 비교표를 만들 수 있습니다. 도구 카탈로그 정리, 서비스 비교 글 작성에 유용합니다.

10-5. 다른 앱과 조합

조합 할 수 있는 일
Exa + Moonlight Ops 내 시트의 시리즈 목록과 웹의 경쟁 상품을 비교
Exa + Hugging Face 뉴스로 새 모델을 찾고, HF에서 모델 카드 확인
Exa + DeepWiki 뉴스에 나온 오픈소스 레포의 실제 구조 확인
Exa + Google Docs/Gmail 조사 결과를 문서로 저장하거나 메일로 발송

11. 실제로 겪은 문제와 해결법

문제 1. “로그인 없이 된다더니 로그인 화면이 떠요”

  • 이유: Exa 서버는 키 없이도 동작하지만, 로그인 방식도 함께 알려 줍니다. Spark는 이걸 보고 자동으로 로그인 절차를 진행합니다.
  • 정리: Spark에서는 Exa 가입(또는 로그인)이 필요하다고 생각하면 됩니다. 대신 agent_run까지 쓸 수 있게 됩니다.

문제 2. 연결 도중 엉뚱한 확인창(“Google Workspace를 Gemini에서 연결 해제하시겠어요?”)이 떴어요

  • 상황: 저희 작업 중 한 번 Exa 연결 창과 겹쳐서 이 확인창이 떴습니다.
  • 해결: **Cancel(취소)**을 눌렀고, Google Workspace 연결은 그대로 유지됐습니다.
  • 교훈: 연결 중에 예상하지 못한 “연결 해제”, “Disconnect” 창이 뜨면 무조건 취소하세요.

문제 3. 로그인 후 원래 화면으로 안 돌아온 것 같아요

  • 확인: 주소창에 custom_mcp_action=auth_complete가 있으면 인증은 끝난 상태입니다. 잠시 기다리면 “Save your custom app” 창이 뜹니다.
  • 해결: 창이 안 뜨면 Connected Apps 페이지를 새로고침해서 Exa가 목록에 있는지 확인하세요. 없으면 5단계부터 다시 합니다.

그 밖에

증상 해결
Custom apps 메뉴가 없음 개인 계정인지, Spark 권한과 Keep Activity가 켜져 있는지 확인
agent_run이 안 보임 로그인 없이 연결된 경우입니다. 앱을 지우고 다시 연결해서 Exa 로그인까지 마치세요
결과가 엉뚱함 키워드 대신 “원하는 페이지를 설명”하는 문장으로 검색

12. 비용과 주의할 점

  • 무료 크레딧: 새 Exa 계정에는 무료 크레딧이 들어 있습니다. 로그인 방식에서는 사용량이 내 Exa 계정에 기록됩니다.
  • agent_run은 사용량 기반 과금입니다. 여러 번 검색하는 만큼 크레딧을 더 씁니다. 큰 조사를 자주 맡긴다면 https://dashboard.exa.ai 에서 남은 크레딧을 가끔 확인하세요.
  • 예약 작업 주의: 매일 도는 Spark 예약 작업에 agent_run을 넣으면 크레딧이 꾸준히 나갑니다. 일상 브리핑은 web_search_exa 위주로 요청하는 것이 경제적입니다.
  • 보내는 검색어는 외부 서버로 갑니다. Google은 맞춤 앱으로 연결한 외부 서버를 검토하거나 보호하지 않는다고 안내합니다. 개인정보나 내부 기밀을 검색어에 넣지 마세요.
  • 연결 해제: Connected Apps → Exa → More details → Disconnect 또는 Remove app

13. 더 나아가기: 고급 검색 도구 켜기

Exa에는 기본으로 꺼져 있는 **web_search_advanced_exa**가 있습니다. 도메인 지정, 날짜 범위, 카테고리, 지역 같은 세밀한 필터를 쓸 수 있습니다.

주소 뒤에 tools 값을 붙여서 켤 수 있습니다.

https://mcp.exa.ai/mcp?tools=web_search_exa,web_fetch_exa,web_search_advanced_exa,agent_run
  • tools를 적으면 기본 목록을 대체하므로, 쓰고 싶은 도구를 전부 적어야 합니다.
  • 적용하려면 Spark에서 기존 Exa 앱을 지우고 이 주소로 다시 연결합니다.
  • 참고: 이 확장 주소로 Spark에 연결하는 과정은 이번 작업에서 직접 테스트하지 않았습니다. 기본 연결이 잘 되고 있다면 필요할 때만 시도하세요.

14. 자주 묻는 질문

Q. Gemini에도 검색 기능이 있는데 왜 Exa를 붙이나요? Exa는 “설명형 검색”과 “본문 그대로 받기”에 특화돼 있어서, 깊은 조사나 여러 페이지 비교에서 차이가 납니다. 두 가지를 같이 두고 목적에 따라 @Exa를 지정해 쓰면 됩니다.

Q. Claude나 Cursor에서도 쓸 수 있나요? 네. 같은 주소 https://mcp.exa.ai/mcp를 각 도구의 MCP 설정에 넣으면 됩니다. 로그인을 원하면 https://mcp.exa.ai/mcp?login 주소를 씁니다.

Q. 가입한 Exa 계정은 어디서 관리하나요? https://dashboard.exa.ai 에서 사용량, 크레딧, API 키를 관리합니다.

작성: 2026-09-28. 실제 Spark 연결 과정(구글 계정 연결 동의, Exa Google 로그인)과 Exa 공식 문서 기준.

Gemini Spark에 DeepWiki 연결하기

깃허브 레포를 "읽어주는 AI 위키"를 Spark 도구로 붙이는 초보자 튜토리얼

DeepWiki는 공개 깃허브 레포를 자동으로 분석해서 위키(구조 설명, 문서, 다이어그램)를 만들어 주는 서비스입니다. 이걸 Gemini Spark에 연결하면, 코드를 직접 열지 않고도 “이 레포 어떻게 돌아가?”라고 물어볼 수 있습니다.

난이도: 가장 쉬움 (로그인, 가입, 코드 없음). 걸리는 시간: 약 3분.

1. 완성하면 무엇이 달라지나

전에는

  • 오래 안 건드린 레포를 다시 보려면 README를 읽고, 폴더를 뒤지고, 파일을 하나씩 열어야 했습니다.
  • 남의 오픈소스를 쓰려면 문서가 부족할 때 코드를 직접 읽어야 했습니다.

이제는

  • Spark에 “@DeepWiki 이 레포 구조 요약해줘”라고 물으면, DeepWiki가 미리 만들어 둔 위키를 근거로 답합니다.
  • “이 기능은 어느 파일에서 처리해?” 같은 질문에도 코드 기반으로 답을 받습니다.
  • Spark의 다른 앱(Docs, Gmail 등)과 묶어서 “레포 요약 문서 만들어줘” 같은 일도 맡길 수 있습니다.

2. 먼저 알아둘 개념

용어 쉬운 설명
DeepWiki Devin을 만든 Cognition이 운영하는 서비스. 공개 깃허브 레포를 분석해 위키를 자동으로 만들어 줍니다. deepwiki.com에서 볼 수 있습니다.
DeepWiki MCP 그 위키를 AI가 읽을 수 있게 열어 둔 “도구 창구”입니다. 무료이고 로그인이 필요 없습니다.
MCP AI와 외부 도구를 연결하는 표준 규격입니다. “AI용 USB-C 포트”라고 생각하면 됩니다.
Spark 맞춤 앱(Custom apps) MCP 서버 주소를 넣어 Spark에 도구를 추가하는 메뉴입니다.
색인(인덱싱) DeepWiki가 레포를 처음 분석해서 위키를 만드는 과정입니다. 색인이 안 된 레포는 질문해도 답이 안 나옵니다.

3. 준비물

  • ☐ Gemini Spark를 쓸 수 있는 개인 구글 계정 (Google AI Pro/Ultra)
  • ☐ Gemini 설정에서 Keep Activity(활동 기록 유지) 켜기
  • ☐ 끝. DeepWiki 쪽 가입이나 키는 필요 없습니다.

4. 1단계: 주소 확인하기

DeepWiki MCP 주소는 이것입니다.

https://mcp.deepwiki.com/mcp
  • 끝이 /mcp인 주소가 권장 방식(Streamable HTTP)입니다.
  • /sse로 끝나는 옛 주소도 있지만 곧 없어질 방식이라 쓰지 않습니다.

5. 2단계: Spark에 추가하기

  1. https://gemini.google.com/spark/apps 로 갑니다. (Gemini 왼쪽 메뉴 Spark → Connected Apps)
  2. 아래쪽 Custom apps 섹션에서 Add a custom app을 누릅니다.
  3. MCP Server URL 칸에 https://mcp.deepwiki.com/mcp를 붙여넣고 Next를 누릅니다.
  4. “Connect this MCP server” 안내문이 나옵니다.
    • Gemini가 이 서버의 도구를 실행하고, 데이터를 주고받을 수 있다는 내용입니다.
    • 맨 아래 “I understand and accept…” 체크박스를 누르고 Connect를 누릅니다.
  5. 잠시 로딩 후 “Save your custom app” 창이 뜹니다.
    • App name: DeepWiki (자동 입력)
    • Available actions: ask_wiki_question, read_wiki_contents, read_wiki_structure
  6. Connect를 누르면 끝입니다.

로그인 화면이 따로 뜨지 않는 게 정상입니다. DeepWiki는 인증 없는 공개 서버라서 연결이 가장 간단합니다.

6. 3단계: 연결 확인하기

Connected Apps 페이지의 Custom apps 목록에 이렇게 보이면 성공입니다.

DeepWiki   @DeepWiki   Last sync (연결한 날짜와 시간)   [켜짐]

More details를 누르면 도구 3개를 볼 수 있습니다.

도구 하는 일 필요한 입력
read_wiki_structure 레포 위키의 목차(주제 목록)를 가져옴 레포 이름 (예: facebook/react)
read_wiki_contents 위키 본문을 읽음 레포 이름
ask_wiki_question 레포에 대해 질문하면 위키 근거로 답함 레포 이름, 질문

레포 이름은 항상 소유자/레포 형식입니다. 예: Reasonofmoon/gov-scan-daily

7. 4단계: 내 레포를 DeepWiki에 등록(색인)하기

유명한 오픈소스(React, LangChain 등)는 이미 색인돼 있어서 바로 됩니다. 하지만 내 레포는 대부분 처음엔 색인이 안 돼 있습니다.

실제로 Reasonofmoon/gov-scan-daily를 조회해 보면 이런 답이 옵니다.

Repository not found. Visit https://deepwiki.com/Reasonofmoon/gov-scan-daily to index it.

색인하는 방법

  1. 브라우저에서 https://deepwiki.com/소유자/레포로 갑니다.
    • 예: https://deepwiki.com/Reasonofmoon/gov-scan-daily
  2. 색인(위키 생성) 요청 버튼이 나오면 누르고 안내를 따릅니다.
  3. 생성이 끝날 때까지 기다립니다. 레포 크기에 따라 몇 분 이상 걸릴 수 있습니다.
  4. 페이지에 위키가 보이면, 이제 Spark에서도 그 레포를 물어볼 수 있습니다.

큰 레포에서 중요한 폴더가 위키에서 빠진다면, 레포 루트에 .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

작성: 2026-09-28. 실제 연결 과정과 도구 목록 확인 결과 기준.

달빛이 머무는 마당 · 한가위 — 브라우저 Three.js 미니어처

한 줄로. 브라우저 안에서 직접 그린 인터랙티브 Three.js 한가위 마당이다. 한옥 마당·황금 들판·감나무·송편 상·한복을 입은 가족을 코드로 구성했고, 사진을 이어 붙이거나 외부 3D 모델을 영상으로 배치한 것이 아니다.

달빛이 머무는 마당 · 한가위

보름달처럼 넉넉하게, 고향집처럼 따뜻하게.

인터랙티브 마당 열기
HTML 직접 열기

미리보기 높이 약 640px · 전체 화면·조작은 전용 페이지를 권장합니다.

무엇이 다른가

이 작품은 오프라인 단일 HTML이다. npm 설치나 외부 API 호출 없이 브라우저에서 바로 연다. Three.js 장면·UI·합성 바람·풀벌레 소리(Web Audio)가 한 파일에 들어가 있다. 메타 설명은 「보름달 아래 따뜻한 한옥 마당. 황금빛 들판과 감나무, 송편을 나누는 가족이 있는 인터랙티브 Three.js 한가위 풍경」이다.

장면과 조작

  • 시점(VIEW): 01 고향집 · 02 송편 마당 · 03 황금 들녘 · 04 달맞이
  • 슬라이더: 빛의 온도(저녁 등) · 가을바람(산들 등)
  • 단축: 드래그로 둘러보기 · 스크롤(또는 두 손가락)로 가까이 · Space 일시정지 · H 풍경만 보기 · 처음 자리로 돌아가기
  • 핫스팟: 감나무에 바람 한 줌 · 송편 드시고 가세요 · 낮잠 한숨(마당 지킴이)
  • 기타: 합성 가을 소리 켜기 · 지금 풍경 PNG 저장 · 전체화면(데스크톱)

브랜드 카피: 한가위, 작은 고향 — A LITTLE PIECE OF HOME · 에디션 표기: AUTUMN, IN MINIATURE — A warm homecoming.

파일

  • 인터랙티브 HTML(ASCII 파일명): chuseok-moonlight-yard.html
  • 미리보기 PNG: chuseok-moonlight-yard.png(원본 한글명 한가위_달빛마당.png)

QA · 한계

먼저 읽는 면책

  • 창작용 미니어처다. 건축 측량·제례 재현·정확한 유체 물리 시뮬레이션이 아니다.
  • QA 기준일: 2026-09-23, Chromium + SwiftShader 환경에서 확인했다. 기기·드라이버·브라우저에 따라 성능·표현이 달라질 수 있다.
  • 성적·입시·수익 보장 없음 · 제품·도구·창작 설명

FAQ

사진이나 미리 만든 3D 모델을 영상으로 붙인 건가요?

아니다. 브라우저에서 Three.js로 장면을 코드로 구성한 인터랙티브 애니메이션이다.

인터넷·npm이 필요한가요?

배포본은 단일 HTML로 오프라인에서 열 수 있도록 묶었다. npm 설치나 별도 API 서버는 없다.

모바일에서도 되나요?

반응형 UI(슬라이더 접기·터치 드래그 등)가 들어 있다. 다만 QA는 2026-09-23 Chromium+SwiftShader 기준이므로, 저전력 기기에서는 프레임이 낮아질 수 있다.

제례·한옥 고증이 정확한가요?

아니다. 고향집처럼 따뜻한 분위기의 창작 미니어처이며, 건축 측량이나 의식 재현을 목표로 하지 않는다.

작성: moonlang · KST 2026-09-24 · 성적·입시 보장 없음 · 창작 미니어처

메타 뮤즈: ‘답하는 AI’에서 ‘일하는 AI’로 — 앱스토어 1위 뉴스 정리

한 줄로. 메타의 AI 에이전트 뮤즈(Muse)가 출시 열흘 만에 미국 애플 앱스토어 무료 앱 1위에 올랐다는 한국경제TV 뉴스를, 영상·기사 원문 기준으로 정리한다. 핵심 서술은 “답하는 AI”에서 “일하는 AI”로의 전환이다.

먼저 읽는 면책

  • 이 글은 한국경제TV 뉴스 유튜브 세그먼트와 동일 제목 기사 원문을 확인·미확인으로 나눈 AI 소식 메모다. moonlang이 뮤즈를 직접 설치·시연·측정한 결과가 아니다.
  • 다운로드·순위·요금·목표가 숫자는 해당 기사·영상이 인용한 값이다. 앱스토어·증권사 원문을 이 글에서 재검증하지 않았다.
  • 한국 출시 일정·수익 보장·투자 권유는 하지 않는다. 기사 기준 현재 이용은 미국이다.

이 글의 범위

흐름 한눈에 (시연 서술)

기자가 메타 공개 영상을 인용해 설명한 흐름이다. moonlang 재현이 아니다.

챗봇형 질문-답변과 뮤즈 에이전트형 할 일 요청·화면 밖 작업·결제 승인 흐름 비교 다이어그램
한국경제TV 시연 서술 기준 흐름 · moonlang 재현 아님

무엇이 다르다고 말했나

  • 기존 챗GPT 서술은 챗봇에 가깝다. 질문을 던져야 답한다.
  • 뮤즈는 일을 시키면 직접 처리하는 쪽으로 소개된다. 기사 표현은 ‘답하는 AI’에서 ‘일하는 AI’로의 진화.
  • 기사 시점 기준 미국에서만 이용 가능하다고 한다.

시연에서 강조한 동작

  1. 사용자가 “이번 주가 정신없이 바쁘다”고 하면, AI가 먼저 “어떤 일을 대신 처리해드릴까요?”라고 묻는다.
  2. 학교 관련 이메일을 챙겨달라고 하면 준비물 목록을 찾아준다. (여기까지는 기존 AI와 크게 다르지 않다고 기자가 말한다.)
  3. 화면을 꺼도 뮤즈가 작업을 이어간다고 시연한다. 사용자가 과정을 지켜보지 않아도 된다.
  4. 구매 목록을 정리해 결제까지 진행한다고 한다.
  5. 다만 결제 같은 민감 행동에는 사용자 승인이 필요하고, 인증 번호 등은 사람이 직접 입력해야 한다고 명시한다.

초기 흥행·요금 (기사 인용 숫자)

  • 뮤즈는 무료 앱으로 소개된다.
  • 지난 8일 출시 직후 5일 동안 다운로드 73만 건을 기록했고, 챗GPT·클로드·그록을 모두 앞질렀다고 한다.
  • 출시 열흘 만에 챗GPT를 제치고 미국 애플 앱스토어 무료 앱 1위에 올랐다고 한다.
  • 유료 요금제로 월 20달러·100달러 구독이 함께 나왔다고 한다. 관건은 무료→유료 전환이라고 기자는 정리한다.

이 숫자들은 기사·영상 서술이며, moonlang이 App Store·메타 공식 대시보드를 재집계한 값이 아니다.

시장·전략 쪽 서술

  • 에버코어ISI를 인용해 “메타가 AI에 투입한 2천억 달러가 헛되지 않았다”는 분석이 나왔다고 한다.
  • 시장의 관심이 ‘AI에 얼마나 쓰느냐’에서 ‘AI로 얼마나 버느냐’로 옮겨간다고 기자는 말한다.
  • 웰스파고가 메타 목표주가를 796달러로 상향했고, 간밤 대형 반도체주가 급등했다고 전한다. (투자 권유가 아니다.)
  • 메타 강점으로 페이스북·인스타그램·왓츠앱·스레드 등 소셜 생태계 연동을 든다. 웹 검색·학습 데이터 중심 답변과 달리, 사용자가 메타 서비스에서 접한 콘텐츠를 바탕으로 맞춤형 답변이 가능하다는 서술이다.
  • 마크 저커버그가 강조해 온 ‘개인 초지능’ 전략으로 묶는다.
  • 현지시간 23일 메타 커넥트에서 AI 전략 공개가 주목되며, 뮤즈 기능이 신규 AI 글라스에 탑재된다는 전망이 나온다고 전한다. (미확정 전망으로만 적는다.)

아직 이 글에서 단정하지 않는 것

  • 한국·아시아 출시 일정
  • 무료→유료 전환율, 실제 매출
  • 결제·계정 보안의 장기 안전성 (시연은 승인·직접 입력을 전제한다고만 밝힘)
  • 메타 커넥트에서 실제로 발표될 기능 목록

moonlang에서 읽을 때

  • “챗GPT를 제쳤다”는 미국 앱스토어 무료 순위·초기 다운로드 서술이지, 모델 벤치마크 승패가 아니다.
  • 에이전트가 화면 밖에서 일하는 흐름과, 결제·인증은 사람 승인이 남는다는 점을 같이 본다.
  • 학원 수업·투자와 직접 연결하지 않는다.

FAQ

Q. 뮤즈가 ChatGPT보다 “더 똑똑하다”는 뜻인가요?
A. 이 글 기준으로는 아닙니다. 한국경제TV·와우TV가 전한 것은 미국 앱스토어 무료 순위·초기 다운로드와 “일하는 AI(에이전트)” 프레이밍이지, 모델 벤치마크 승패가 아닙니다.

Q. 화면을 꺼도 일하고 결제까지 하나요?
A. 시연 서술상 화면 밖 작업 이어가기가 강조되지만, 결제 등 민감 행동은 사용자 승인이 필요하고 인증 번호 등은 사람이 직접 입력한다고 기사·영상이 말합니다. moonlang 재현이 아닙니다.

Q. 한국에서도 바로 쓸 수 있나요?
A. 기사 시점 기준으로는 미국 전용으로 전합니다. 한국·아시아 출시 일정은 이 글에서 단정하지 않습니다.

Q. 요금·목표가 숫자를 사실로 써도 되나요?
A. 안 됩니다. 다운로드·순위·요금·목표가 숫자는 해당 기사·영상이 인용한 값이며, 이 글에서 앱스토어·증권사 원문을 재검증하지 않았습니다.

출처

  1. YouTube — 한국경제TV뉴스, 메타 ‘뮤즈’, 챗GPT도 제쳤다…일하는 AI로 반격 (2026-09-22)
  2. 와우TV 기사 동일 제목, A202609220101 (김대연 · 2026-09-22)

작성: moonlang AI 소식 · KST 2026-09-23 · polish 2026-09-23 · 수치·출시·투자 보장 없음

Argon 유출과 Arena 3.8 Flash 라벨 논란: 공식 발표 전 기록 (제미나이 4 아르곤 공개로 업데이트)

업데이트 2026-10-01 (한국시간): 구글이 한국 시간 10월 1일 오전 5시(미국 9월 30일) ‘제미나이 4 아르곤(Gemini 4 Argon)’을 공식 발표했습니다. 공식 모델명은 ‘Gemini 4 Pro’가 아니라 ‘아르곤’이며, Argon은 코드네임이 아니라 정식 모델 이름입니다. 공식 출력 한도는 100만 토큰으로 아래 유출 스펙(25만 6천)과 다릅니다. 지금은 구글 내부와 페어윈드 프로그램 기관만 쓸 수 있고, 일반 공개 날짜는 발표되지 않았습니다. Arena의 3.8 Flash 라벨 시연이 아르곤이었는지는 공식 발표에 언급이 없어 여전히 미확정입니다. 공식 발표 내용과 벤치마크 검증은 제미나이 4 아르곤 정리에 정리했습니다. 아래 본문은 2026년 9월 23일 시점의 기록입니다.

한 줄로. Arena에 Gemini 3.8 Flash 라벨로 올라온 고품질 SVG·미니게임 출력이 “숨겨진 Gemini 4 Pro(Argon)”인지(Argon은 9월 30일 제미나이 4 아르곤으로 공식 발표됨), 아니면 이미 공개된 Flash를 더 세게 돌린 것인지 아직 확정되지 않았다. 작성 당시 Google이 이름으로 붙인 사실은 Gemini 4 사전학습이 시작됐다는 수준까지였다. 이후 9월 30일(미국) 구글이 제미나이 4 아르곤을 공식 발표하면서 모델명·출력 한도·발표 가격·벤치마크가 공개됐다.

먼저 읽는 면책

  • 이 글은 YouTube 채널 BitBiasedAI의 정리 영상을 확인·미확인·반박으로 나눈 AI 소식 메모다. moonlang이 Arena·유출물을 직접 재현·측정한 결과가 아니다.
  • X 계정 유출 스펙·벤치마크 시트는 독립 검증 전제로만 적는다. 확인되지 않은 숫자를 사실처럼 쓰지 않는다.
  • 모델 출시일·등급·성능 보장을 하지 않는다.

이 글의 범위

Gemini 4 Pro 주장 확인 사다리: L1 회사 공개 기록부터 L4 유출 스펙까지
확인 사다리 — 위로 갈수록 근거가 약함 · moonlang 재현 아님

확인된 것 (회사·공개 기록 쪽)

  • 영상은 DeepMind/관련 발언을 인용하며, Sundar Pichai가 Gemini 4의 가장 야심 찬 사전학습 런을 이미 시작했다고 말한 수준까지를 “확인”으로 둔다. 출시일·Pro 티어·코드네임·벤치마크는 그 인용에 없다. (작성 당시 기준. 9월 30일 공식 발표로 모델명과 출력 한도·벤치마크가 공개됐다.)
  • 영상이 정리한 공개 제품 쪽 숫자는 Gemini 3.8 Flash 기준 컨텍스트 약 100만 토큰 · 출력 상한 약 6만 4천 토큰이다. (영상·공개 스펙 시트 서술. moonlang API 재측정 아님.)
  • 영상 타임라인: Flash 계열이 2026년 들어 자주 올라왔고, 3.8 Flash는 9월 2일 전후로 서술된다.

Arena에서 무엇이 보였나 (라벨은 3.8 Flash)

영상·포럼 보고에 따르면 9월 17일 전후 Arena 익명 배틀에서, 인터페이스 라벨이 Gemini 3.8 Flash인 응답이 단순 그림이 아니라 조작 가능한 SVG·HTML을 냈다.

  • 자전거 타는 펠리컨 — 색·낮/밤 토글이 동작했다고 보고
  • 자전거 타는 주전자 — 같은 식의 인터랙션
  • ウルトラマン×ゴジラ SVG/HTML 미니게임 — 레이저·점수 카운터 등이 동작했다고 보고 (점수 예: 234,350은 시연 화면 숫자이며 벤치마크가 아님)

요점은 “출력이 인상적이었다”와 “라벨이 이미 공개된 Flash였다”가 동시에 붙었다는 점이다. 링크가 Arena 도메인이라는 점까지는 영상이 “진짜 세션”으로 보지만, 세션이 진짜라고 해서 모델이 4 Pro라는 뜻은 아니다.

Argon 유출 주장 (미검증)

9월 14일 X 계정 Lentils80이 당시 ‘Gemini 4 Pro’의 코드네임으로 주장된 Argon(9월 30일 실제 모델명으로 공개)의 첫 출력이라고 주장하는 영상을 올렸다고 한다. 주장 스펙은 대략 컨텍스트 200만 · 출력 25만 6천이고, 고 thinking 설정에서 한 장면이 약 2.4분 걸렸다는 서술이다. (공식 출력 한도: 100만 토큰. 유출 스펙의 25만 6천과 다르며, 컨텍스트 200만은 공식 확인이 없다.)

작성 당시에는 독립 모델 카드·공식 API 목록·코드 안 지문으로 확인되지 않았다고 영상도 밝혔다. 이 영상이 실제 아르곤 출력이었는지는 공식 발표 뒤에도 확인되지 않았다. Arena 시연과 날짜가 가깝다는 이유로 커뮤니티가 점을 이었을 뿐, 동일 체크포인트 증명은 아니다.

이미 한 번 깨진 신호

8월 12일경 X에 돌던 “Gemini 4 Pro 벤치마크 시트”(예: 컨텍스트 150만 등)는 독립 분석이 다른 회사 공개 점수와 숫자가 같다고 보고 재라벨 조작으로 반박됐다고 영상은 말한다. 유출처럼 보이는 표가 금방 퍼질 수 있다는 배경으로만 적는다.

4 Pro 가설을 약하게 만드는 관찰

  • linux.do 테스터 중 일부가 Arena에서 Gemini 3.8 Flash high(같은 공개 모델의 높은 thinking)로 바꾸면 같은 품질이 재현된다고 보고했다. 영상은 이를 “숨은 플래그십”보다 “공개 Flash를 더 세게” 쪽으로 기울이는 가장 강한 단일 관찰로 둔다.
  • 주장된 Argon 생성은 분 단위로 느린데, Arena 펠리컨·게임 출력은 초 단위로 빨랐다는 속도 불일치가 있다. (양쪽 모두 사용자 보고.)
  • 생성물 안에 Argon/Gemini 4를 스스로 밝히는 문자열·토큰 지문은 보고되지 않았다.
  • 점수형 Arena 리더보드에는 Gemini 4/Argon 항목이 없고 3.x만 보인다고 한다. 미공개 체크포인트는 보통 의미 없는 임시키로 가려지는데, 이미 출시된 Flash 이름으로 위장한다는 가정은 어색하다는 회의도 같은 스레드에 있다.

아직 모르는 것

  • 9월 14일 유출 영상이 실제 아르곤 출력이었는지
  • Arena 출력이 지금도 같은 설정으로 재현되는지, Google이 빼 갔는지
  • 수학·추론 등 창작 SVG 밖 벤치
  • 다음이 3.9인지 4인지, 언제 공개인지 → 해소(10월 1일 업데이트): 제미나이 4(아르곤)로 공개됐다. 지금은 구글 내부와 페어윈드 프로그램 기관에만 제한 공개 중이고, 일반 공개 날짜는 미정이다.

영상 진행자 개인 판단(확정이 아님): 오늘 시점에서 가장 강한 데이터는 Flash high 재현 쪽이며, 아직 유출 플래그십이라고 단정하지 않는다. Arena 배틀에서 해당 출력이 사라지거나 리더보드에 별도 항목으로 뜨면 그때 다시 보면 된다고 말한다.

moonlang에서 읽을 때

  • 스크린샷·유출 숫자보다 라벨·재현 조건·속도·공식 문장을 같이 적는다.
  • “Insane / Leaked” 제목은 클릭용이며, 본문은 확인 계층을 나눈다.
  • 제품 도입·학원 수업과 직접 연결하지 않는다. 필요하면 별도 실험 글을 쓴다.

FAQ

Q. Arena SVG 시연이 Gemini 4 Pro라는 뜻인가요?
A. 아닙니다. 보고된 인터페이스 라벨은 Gemini 3.8 Flash였고, 출력이 인상적이라는 점과 모델이 4 Pro라는 점은 별개입니다. Flash high로 같은 품질이 재현됐다는 커뮤니티 보고가 4 Pro 가설을 약화합니다. 9월 30일 공식 발표에도 Arena 건은 언급되지 않아, 이 시연이 아르곤이었는지는 지금도 미확정입니다.

Q. Argon 유출 스펙은 확인됐나요?
A. 이 글·참고 영상 기준으로는 독립 모델 카드·공식 API 목록·코드 지문으로 확인되지 않았습니다. 과거 벤치마크 시트 위조 선례도 같이 적었습니다. 9월 30일 공식 발표로 ‘Argon’이라는 이름은 실제 모델명으로 확인됐지만, 공식 출력 한도는 100만 토큰으로 유출 스펙(25만 6천)과 다르고 컨텍스트 200만은 공식 확인이 없습니다.

Q. Argon은 Gemini 4 Pro의 코드네임인가요?
A. 아닙니다. 구글 공식 글에 나온 정식 모델명이 ‘제미나이 4 아르곤(Gemini 4 Argon)’입니다. 구글은 최상위 모델에 ‘Pro’ 대신 이 이름을 붙였습니다.

Q. Google이 확인한 범위는 어디까지인가요?
A. 이 글을 쓴 9월 23일에는 영상 인용 기준으로 Gemini 4의 사전학습 런이 시작됐다는 수준까지였습니다. 이후 구글은 한국 시간 10월 1일 오전 5시(미국 9월 30일) 제미나이 4 아르곤을 공식 발표하고 출력 한도(100만 토큰), 발표 가격, 벤치마크를 공개했습니다. 10월 1일(한국시간) 기준으로는 구글 내부와 페어윈드 프로그램 기관만 쓸 수 있고, API·일반 공개 날짜는 발표되지 않았습니다.

Q. 제품 도입·수업과 바로 연결해도 되나요?
A. 이 글은 확인 계층을 나눈 AI 소식 메모입니다. 제품 도입·학원 수업과 직접 연결하지 않습니다.

출처

  1. YouTube — BitBiasedAI, Gemini 4 Pro Just Leaked: The Results Are Insane (2026-09-22 · 챕터: Arena 시연 / Argon / 반박 / 미확인)
  2. 영상 안 인용·서술로만 다루는 Google/DeepMind 측 Gemini 4 사전학습 언급, Gemini 3.8 Flash 공개 스펙 숫자
  3. 영상에서 언급한 커뮤니티 보고: Arena · linux.do · X(Lentils80 등) — 1차 원문은 각 플랫폼에서 재확인 권장
  4. 업데이트 출처: 구글 공식 블로그, Gemini 4 Argon: our next era of frontier intelligence (2026-09-30 미국 · 한국시간 10월 1일 05:00) · 구글 코리아 블로그

작성: moonlang AI 소식 · KST 2026-09-23 · 업데이트 2026-10-01 KST · 수치·출시 보장 없음

숀다 라임스 TED: 공부 사이에 놀이와 여유를 남기는 이유

명연설로 읽는 공부 · 20 / 20

숀다 라임스 | My year of saying yes to everything · 2016

공부가 생활의 모든 자리를 채우고 있다면 해야 할 일과 쉬는 시간을 함께 살펴보세요. 숀다 라임스의 TED 강연은 도전의 예스에서 시작해 일, 놀이, 가족과의 시간으로 이어집니다. 이 글은 잠깐 놀면 모든 어려움이 해결된다는 처방 대신 일상의 균형을 확인하는 질문을 건넵니다.

강연 내용은 요약했으며, 공부 예시와 실천 과제는 이 글에서 제안한 적용입니다. 연사의 직접 인용문이 아닙니다.

각 영상은 표시된 대목부터 살펴보도록 연결했습니다. 자동 정지는 설정하지 않았습니다. 재생이 제한되면 아래 TED 링크를 이용해 주세요.

1. 몰입의 활기가 사라진 순간

TED 해당 대목 · 06:53부터

라임스는 일에서 느끼던 활기를 소리의 비유로 설명하고, 그 감각이 사라졌던 경험을 이야기합니다. 이는 개인적인 회고입니다.

전에는 흥미롭던 책이 숙제처럼만 느껴진다면 최근의 일정을 먼저 펼쳐 보세요. 학습량이 갑자기 늘었는지, 선택할 여유가 사라졌는지 기록합니다. 스스로 열정이 없다고 평가하기 전에 달라진 조건을 구체적으로 살피는 것이 다음 대화의 출발점이 됩니다.

생각해 볼 질문
최근 내 일정에서 실제로 달라진 것은 무엇인가요?

2. 아이의 놀이 제안을 받아들입니다

TED 해당 대목 · 09:07부터

강연의 전환점은 함께 놀자는 아이의 요청에 응한 경험입니다. 라임스는 일 밖의 시간을 통해 느낀 변화를 설명합니다.

학생에게도 결과를 제출하지 않아도 되는 시간이 있을 수 있습니다. 짧게 산책하거나 그림을 그리거나 친구와 이야기를 나눌 수 있습니다. 그 시간마저 성적 향상을 위한 과제로 채우기보다 어떤 활동이 부담 없이 가능한지 직접 골라 보세요.

생각해 볼 질문
평가받지 않고 해 보고 싶은 활동은 무엇인가요?

3. 기쁨과 일의 관계를 다시 정합니다

TED 해당 대목 · 11:26부터

후반부에서 라임스는 활기의 근원을 일만으로 생각했던 관점을 돌아봅니다. 강연에 나오는 짧은 놀이 시간은 자신의 경험 속 예시입니다.

쉬는 시간을 정했다면 다시 시작할 학습 행동도 작게 남겨 보세요. 휴식 뒤 한꺼번에 밀린 일을 보충하려 하면 부담이 다시 커질 수 있습니다. 감당하기 어려운 일정은 선생님이나 보호자와 함께 조정할 수 있도록 실제 할 일의 목록을 보여 줍니다.

생각해 볼 질문
쉬는 시간 뒤 돌아올 첫 과제는 얼마나 작게 정할 수 있나요?

오늘 해 볼 작은 실천

오늘 일정에 부담 없이 선택한 여유 시간 하나를 넣으세요. 이어서 돌아왔을 때 할 첫 학습 행동도 적습니다. 하루를 마치면 생산적이었는지 평가하기 전에 일정이 실제로 감당 가능했는지 살피고, 줄이거나 도움받을 항목을 표시하세요.

자주 묻는 질문

Q. 강연처럼 15분만 놀면 지친 상태가 해결되나요?

A. 그 시간을 보편적인 회복 공식으로 볼 수는 없습니다. 강연자의 경험을 참고하되 자신의 생활 조건과 필요한 지원을 함께 살펴야 합니다.

읽고, 근거를 찾고, 자기 말로 표현하기

리드마스터 영어전문 국어논술 학원은 읽기에서 근거 찾기, 판단, 자기 언어로 표현하기까지 연결하는 교육을 지향합니다. 이 글을 읽은 뒤에도 인상적인 조언 하나를 고르고, 그 조언을 뒷받침하는 강연의 대목과 자신의 적용을 나누어 말해 보세요. 유명한 말에 동의하는 데서 한 걸음 더 나아가는 읽기 연습입니다.

상담 문의: 010-3368-7873
경기도 부천시 소사구 옥길로 113 상림프라자 5층 501호

출처: TED 공식 영상·자막 — My year of saying yes to everything · 2016

작성·출처 확인: 2026-09-22

시리즈: 명연설로 읽는 공부 20/20

에이미 커디 TED 다시 읽기: 자신감과 연구 근거를 구별하기

명연설로 읽는 공부 · 19 / 20

에이미 커디 | Your body language may shape who you are · 2012

자세 하나로 호르몬이나 성적이 달라진다고 단정하기는 어렵습니다. 에이미 커디의 TED 강연에는 비언어적 행동과 참여에 관한 이야기, 이후 논쟁이 된 연구 결과가 함께 들어 있습니다. 이 글에서는 강연의 메시지와 검증이 필요한 주장을 나누어 읽고, 학생에게는 구체적인 발표 준비를 제안합니다.

강연 내용은 요약했으며, 공부 예시와 실천 과제는 이 글에서 제안한 적용입니다. 연사의 직접 인용문이 아닙니다.

각 영상은 표시된 대목부터 살펴보도록 연결했습니다. 자동 정지는 설정하지 않았습니다. 재생이 제한되면 아래 TED 링크를 이용해 주세요.

1. 교실에서 보이는 행동과 참여 기회

TED 해당 대목 · 05:13부터

커디는 수업에서 학생들의 비언어적 행동과 참여를 관찰한 경험을 이야기합니다. 겉으로 드러난 자신감과 실제 이해가 같은지는 따로 물어야 합니다.

목소리가 작다고 생각이 없는 것은 아닙니다. 발표 전에 핵심 문장을 적어 두고 자료의 어느 부분을 가리킬지 정해 보세요. 듣는 사람도 큰 목소리만 평가하기보다 주장과 근거가 연결되는지 확인해야 합니다. 참여의 방식을 다양하게 열어 둘 수 있습니다.

생각해 볼 질문
내 생각이 전달되도록 준비할 문장과 자료는 무엇인가요?

2. 강연의 연구 주장에는 후속 검토를 붙입니다

TED 해당 대목 · 10:08부터

강연에 소개된 파워 포즈 연구의 일부 결과에는 재현성과 방법론에 관한 논쟁이 있습니다. TED도 관련 안내를 붙였고, 원 연구의 공동저자는 해당 효과를 더 이상 믿지 않는다는 입장을 공개했습니다.

근거 확인: TED의 연구 관련 안내 · 공동저자 Dana Carney의 입장문

유명한 강연에서 들었다는 사실은 주장 전체의 검증을 대신하지 못합니다. 자료를 읽을 때 누가, 누구를 대상으로, 무엇을 측정했는지 확인해 보세요. 자신감이 느껴졌다는 보고와 호르몬 변화, 실제 성과는 서로 다른 결과입니다.

생각해 볼 질문
지금 읽은 자료가 실제로 측정한 것은 무엇인가요?

3. 참여의 첫 시도는 구체적으로 준비할 수 있습니다

TED 해당 대목 · 17:56부터

강연 후반에는 스스로 어울리지 않는다고 느끼던 학생이 수업에 참여하는 이야기가 나옵니다. 여기서는 연구 효과의 증거와 개인적인 일화를 구분해 읽습니다.

발표가 걱정된다면 자세를 크게 만드는 과제보다 첫 문장을 또렷하게 읽고 잠깐 멈추는 연습을 해 보세요. 근거를 찾을 위치를 표시하고 예상 질문 하나에 답을 준비합니다. 시도 뒤에는 자신감 점수만 묻지 말고 실제로 전달된 내용도 확인합니다.

생각해 볼 질문
이번 발표에서 이전보다 분명히 전달한 부분은 무엇인가요?

오늘 해 볼 작은 실천

발표의 주장 한 문장과 근거 한 문장을 적고 한 번 녹음해 보세요. 다시 들으며 내용이 연결되는지 확인합니다. 자세는 편안하게 조절하되, 검토의 기준은 전달된 주장과 근거에 두세요. 녹음은 개인 연습용으로만 보관해도 됩니다.

자주 묻는 질문

Q. 그렇다면 몸짓이나 자세는 아무 의미가 없나요?

A. 그렇게 결론 내릴 수도 없습니다. 특정 연구 주장이 논쟁적이라는 사실과 모든 비언어적 소통이 무의미하다는 주장은 다릅니다.

읽고, 근거를 찾고, 자기 말로 표현하기

리드마스터 영어전문 국어논술 학원은 읽기에서 근거 찾기, 판단, 자기 언어로 표현하기까지 연결하는 교육을 지향합니다. 이 글을 읽은 뒤에도 인상적인 조언 하나를 고르고, 그 조언을 뒷받침하는 강연의 대목과 자신의 적용을 나누어 말해 보세요. 유명한 말에 동의하는 데서 한 걸음 더 나아가는 읽기 연습입니다.

상담 문의: 010-3368-7873
경기도 부천시 소사구 옥길로 113 상림프라자 5층 501호

출처: TED 공식 영상·자막 — Your body language may shape who you are · 2012

작성·출처 확인: 2026-09-22

시리즈: 명연설로 읽는 공부 19/20

덴절 워싱턴 졸업 연설: 꿈을 오늘의 목표로 바꾸는 법

명연설로 읽는 공부 · 18 / 20

덴절 워싱턴 | 딜러드대학교 졸업 연설 · 2015

공부 시간과 함께 무엇을 설명할 수 있게 됐는지 확인해 보세요. 덴절 워싱턴의 2015년 딜러드대학교 졸업 연설은 꿈과 목표, 꾸준한 실행, 다른 사람을 돕는 일을 이야기합니다. 종교적 신념이 담긴 연설이라는 맥락을 밝히고, 여기서는 학생의 목표와 행동에 관한 대목을 읽습니다.

강연 내용은 요약했으며, 공부 예시와 실천 과제는 이 글에서 제안한 적용입니다. 연사의 직접 인용문이 아닙니다.

함께 볼 연설 영상

전체 연설 영상 열기

본문의 세 대목은 아래 원문 링크로 연결했습니다. 정확한 영상 시작 시점은 확인되지 않아 임의의 시간을 넣지 않았습니다. 영상은 딜러드대학교 공식 채널이며, 대목 찾기에는 보조 전사문을 함께 사용했습니다.

1. 꿈 옆에 구체적인 목표를 둡니다

해당 대목 읽기 · 원문의 해당 대목

위치 이동이 지원되지 않으면 원문에서 dreams without goals를 찾아보세요.

워싱턴은 꿈을 가지되 목표도 세워야 한다고 말합니다. 큰 바람을 매일의 행동과 연결하는 대목입니다.

영어를 잘하고 싶다는 바람을 이번 주에는 인물의 변화 이유를 근거와 함께 설명하기로 바꾸어 보세요. 목표가 작아지면 오늘 어떤 장면을 읽어야 할지 결정할 수 있습니다. 목표를 세운 뒤 필요한 자료와 도움까지 확인하면 실행의 조건도 분명해집니다.

생각해 볼 질문
이번 주말에 무엇을 직접 보여 줄 수 있나요?

2. 움직인 양과 실제 진전을 구별합니다

해당 대목 읽기 · 원문의 해당 대목

위치 이동이 지원되지 않으면 원문에서 movement with progress를 찾아보세요.

연설은 바쁘게 움직이는 것과 진전하는 것을 혼동하지 말라고 조언합니다. 시간보다 남은 결과를 돌아보게 하는 부분입니다.

공부 앱을 여러 개 켜고 필기를 많이 했어도 책을 덮었을 때 설명이 막힐 수 있습니다. 오늘 배운 내용을 자료 없이 한 번 말해 보세요. 막힌 대목을 찾았다면 그 부분만 다시 확인합니다. 예쁘게 정리한 분량과 실제 이해를 구분하는 연습입니다.

생각해 볼 질문
오늘 어제보다 더 분명하게 설명할 수 있는 것은 무엇인가요?

3. 배운 것을 다른 사람에게 건넵니다

해당 대목 읽기 · 원문의 해당 대목

위치 이동이 지원되지 않으면 원문에서 help someone else를 찾아보세요.

워싱턴은 자신의 재능과 성취를 타인을 돕는 데 쓰는 의미를 강조합니다. 목표의 방향을 함께 생각하게 하는 메시지입니다.

친구에게 정답을 보내는 대신 스스로 풀 수 있는 힌트를 만들어 보세요. 어떤 조건을 먼저 봐야 하는지 질문하고, 친구가 설명한 뒤 빠진 근거를 함께 찾습니다. 도움을 주면서 자신의 이해도 다시 점검할 수 있습니다.

생각해 볼 질문
내가 줄 수 있는 도움은 답인가요, 생각할 실마리인가요?

오늘 해 볼 작은 실천

오늘 공부가 끝나면 보지 않고 설명할 내용 하나를 정하세요. 설명이 막힌 부분을 확인한 뒤 다시 말합니다. 마지막에는 그 내용을 배우는 친구에게 건넬 질문 하나를 만드세요. 시간 기록 옆에 실제로 달라진 설명을 남기는 것이 핵심입니다.

자주 묻는 질문

Q. 실패하라는 조언은 큰 위험을 감수하라는 뜻인가요?

A. 학생에게는 안전한 학습 시도와 수정으로 적용하는 편이 적절합니다. 무리한 결정을 해야 꿈에 가까워진다는 보장으로 읽지 않습니다.

읽고, 근거를 찾고, 자기 말로 표현하기

리드마스터 영어전문 국어논술 학원은 읽기에서 근거 찾기, 판단, 자기 언어로 표현하기까지 연결하는 교육을 지향합니다. 이 글을 읽은 뒤에도 인상적인 조언 하나를 고르고, 그 조언을 뒷받침하는 강연의 대목과 자신의 적용을 나누어 말해 보세요. 유명한 말에 동의하는 데서 한 걸음 더 나아가는 읽기 연습입니다.

상담 문의: 010-3368-7873
경기도 부천시 소사구 옥길로 113 상림프라자 5층 501호

출처: Speakola 연설 전사문 · 보조 출처 — 딜러드대학교 졸업 연설 · 2015

작성·출처 확인: 2026-09-22

시리즈: 명연설로 읽는 공부 18/20

숀 에이커 TED: 성공한 뒤로 미루지 않는 오늘의 공부

명연설로 읽는 공부 · 17 / 20

숀 에이커 | The happy secret to better work · 2011

목표를 향해 공부하면서도 오늘 작동한 방법과 도움을 기록할 수 있습니다. 숀 에이커의 강연은 성공해야 행복해진다는 익숙한 순서를 되묻습니다. 늘 긍정적이어야 한다는 요구 대신, 실패와 성과를 함께 살펴볼 기록 방법을 제안합니다.

강연 내용은 요약했으며, 공부 예시와 실천 과제는 이 글에서 제안한 적용입니다. 연사의 직접 인용문이 아닙니다.

각 영상은 표시된 대목부터 살펴보도록 연결했습니다. 자동 정지는 설정하지 않았습니다. 재생이 제한되면 아래 TED 링크를 이용해 주세요.

1. 성공 뒤로 계속 밀리는 만족

TED 해당 대목 · 08:57부터

에이커는 목표를 달성하면 기준을 다시 높이는 현상을 지적하며 성공과 행복의 관계를 질문합니다. 더 좋은 결과를 기다리는 동안 현재가 사라질 수 있다는 문제 제기입니다.

이번 시험이 끝나면 쉬겠다고 해 놓고 곧바로 다음 기준을 세웠던 적이 있나요? 목표를 유지하더라도 오늘 완료한 일을 확인할 시간을 정해 보세요. 만족을 느꼈다는 이유로 성장을 멈추는 것은 아닙니다. 다음 과제와 지금의 완료를 함께 인정할 수 있습니다.

생각해 볼 질문
이미 끝냈는데도 스스로 인정하지 않은 일은 무엇인가요?

2. 관점의 변화는 만능 성과 공식이 아닙니다

TED 해당 대목 · 09:51부터

강연은 긍정적인 상태와 일의 수행을 연결해 설명합니다. 그 수치를 학생 개인의 지능이나 성적 상승률로 그대로 옮길 수는 없습니다.

오답만 기록하던 노트에 이해한 문제도 하나 넣어 보세요. 무엇을 했기에 해결됐는지 풀이 과정을 확인합니다. 기분을 억지로 좋게 만들려는 작업이 아니라, 다음에도 사용할 수 있는 방법을 빠뜨리지 않고 수집하는 연습입니다.

생각해 볼 질문
잘 풀린 문제에서 다시 쓸 수 있는 전략은 무엇인가요?

3. 좋았던 경험을 구체적으로 기록합니다

TED 해당 대목 · 10:51부터

강연 후반은 감사한 일과 긍정적인 경험을 적는 등의 습관을 제안합니다. 강연자의 제안 중 선택해서 시험해 볼 수 있는 부분입니다.

누군가 도와줘서 좋았다고만 쓰기보다 어떤 질문에 어떤 설명이 도움이 됐는지 적어 보세요. 좋은 일이 떠오르지 않는 날에는 어려웠던 사실을 그대로 써도 됩니다. 기록의 목적은 감정을 평가하는 것이 아니라 경험을 더 정확히 보는 데 있습니다.

생각해 볼 질문
오늘의 도움이나 작은 진전을 사실로 적으면 어떻게 되나요?

오늘 해 볼 작은 실천

공부를 마친 뒤 어려웠던 일 하나, 작동한 방법 하나, 다음에 요청할 도움 하나를 적으세요. 어느 날은 잘된 일이 적어도 괜찮습니다. 며칠 뒤 기록을 읽으며 반복해서 도움이 된 조건을 다음 계획에 넣어 보세요.

자주 묻는 질문

Q. 부정적인 감정은 공부에 방해만 되나요?

A. 그렇게 단정하지 않습니다. 불편함은 필요한 변화나 도움을 알려 줄 수 있습니다. 이 글은 감정을 억누르기보다 학습 기록의 범위를 넓히자는 제안입니다.

읽고, 근거를 찾고, 자기 말로 표현하기

리드마스터 영어전문 국어논술 학원은 읽기에서 근거 찾기, 판단, 자기 언어로 표현하기까지 연결하는 교육을 지향합니다. 이 글을 읽은 뒤에도 인상적인 조언 하나를 고르고, 그 조언을 뒷받침하는 강연의 대목과 자신의 적용을 나누어 말해 보세요. 유명한 말에 동의하는 데서 한 걸음 더 나아가는 읽기 연습입니다.

상담 문의: 010-3368-7873
경기도 부천시 소사구 옥길로 113 상림프라자 5층 501호

출처: TED 공식 영상·자막 — The happy secret to better work · 2011

작성·출처 확인: 2026-09-22

시리즈: 명연설로 읽는 공부 17/20