세션 관리

  • Claude 5시간 한도 72% 찍힌 이유는 세션 길이였다

    Claude 5시간 한도 72% 찍힌 이유는 세션 길이였다

    블로그 예약 발행 캡처 규칙을 막 정리했을 때였다. 뭔가 마무리된 느낌이 들어서 Claude 사용량 한도를 열어봤다.

    5시간 한도 72%. 주간 한도 50%.

    잠깐, 이게 벌써?

    사용량 계기판에서 72%를 발견한 순간

    Claude Code에는 사용량 계기판(usage dashboard, 내가 얼마나 썼는지 보여주는 화면)이 있다. 5시간 단위로 사용량을 제한하는 방식인데, 확인했더니 이미 72%가 찍혀 있었다.

    이해가 안 됐다. 딱히 엄청난 작업을 한 것도 아니었다. 블로그 글 몇 편 만들고, 이것저것 설정 잡은 게 전부였다. 그런데 72%라니.

    그래서 Claude한테 직접 물어봤다. 왜 이렇게 됐냐고.

    돌아온 답이 좀 뜻밖이었다.

    Claude 사용량 한도, 세션을 208MB까지 키운 게 진짜 범인이었다

    세션(session, 대화창 하나를 열고 나서 닫기 전까지의 전체 대화 묶음)이 208MB, 줄 수로는 23,829줄까지 쌓여 있었다.

    하루 종일 같은 대화창을 끊지 않고 계속 이어서 썼다. 작업이 바뀌어도 그냥 계속 입력했다. 새 창을 열 생각을 못 했다.

    문제는 Claude가 대화하는 방식에 있다. 매 턴(turn, 내가 한 번 입력하고 Claude가 한 번 답하는 한 쌍)마다 지금까지 나눈 대화 전체를 처음부터 다시 읽는다. 토큰(token, AI가 글을 읽고 쓸 때 세는 단위. 대략 글자 두세 개 분량)으로 환산해서 전부 처리한다는 뜻이다.

    세션 초반에는 괜찮다. 대화가 짧으니까. 그런데 세션이 쌓일수록, 한마디 할 때마다 처리해야 하는 양이 눈덩이처럼 불어난다. 나는 그 세션을 208MB까지 키웠다. 한마디 할 때마다 208MB짜리 대화를 통째로 들고 시작한 셈이다.

    Claude 사용량 한도 초과 원인을 직접 확인한 터미널 화면 — 세션 파일 wc -l 23829줄, du -h 208M, grep -c image 1420건

    직접 명령을 돌려서 확인한 숫자다. 줄 수 23,829줄, 용량 208MB, 그 안에 이미지가 들어간 메시지만 1,420건. 이게 근본 원인이었다.

    이미지 토큰(페이지 스냅샷)은 텍스트보다 훨씬 비싸다

    두 번째 원인은 Playwright(플레이라이트, 브라우저를 코드로 자동 조작하는 도구)였다.

    랭크매스 점수를 확인하려고 워드프레스 편집기를 열었다 닫았다를 반복했다. 포스트 하나 점수 보는 데 편집기를 6번 넘게 열었다.

    문제는 브라우저를 열 때마다 딸려오는 것이 있다는 점이다. 페이지 스냅샷이 자동으로 첨부된다. Claude가 화면 상태를 파악하려면 이게 필요하다.

    이미지 토큰은 텍스트 토큰보다 훨씬 무겁다. 글자 몇 백 개짜리 텍스트와, 화면 전체 이미지 한 장은 비교가 안 된다. 그걸 6번 넘게 반복했다. 거기에 208MB짜리 세션 문맥까지 매번 붙었다.

    단순 계산이다. 비쌀 수밖에 없었다.

    이 글을 올리는 과정에서도 똑같은 일이 반복됐다. REST API로 랭크매스 키워드를 저장했는데, 응답을 확인해보니 값이 안 찍혀 있었다. 저장이 씹힌 거다. 결국 편집기를 다시 열어서 UI에 직접 입력하고 저장 버튼을 눌러야 했다.

    Claude 사용량 한도와 관련된 랭크매스 REST 메타 저장 씹힘을 직접 확인하고 에디터 UI로 우회한 터미널 화면

    REST로 넣은 값은 저장 응답에 안 찍히는데, 편집기에서 직접 입력하고 저장하면 “글 업데이트 되었습니다” 토스트가 뜨고 새로고침해도 유지됐다. 편집기를 또 열어야 했다는 뜻이다. 방금 원인으로 지목한 그 습관을, 그 글을 쓰면서 또 반복한 셈이다.

    Opus 모델을 너무 많이 쓴 것도 한몫했지만

    세 번째는 모델 선택이었다. 그날 모델을 여러 번 바꿨는데, Opus(오퍼스, Claude 모델 중 가장 고성능이지만 가장 무거운 버전)를 꽤 자주 썼다.

    Opus는 Sonnet(소네트, Opus보다 가볍고 빠른 중간 성능 모델)보다 5시간 한도를 훨씬 빨리 깎아먹는다. 같은 작업을 해도 Opus로 하면 한도 소모가 더 크다.

    이것도 분명히 한몫했다. 하지만 솔직히 말하면, 세션 길이 앞에서는 부차적인 문제였다. 세션이 짧았다면 Opus를 좀 썼어도 이 정도는 아니었을 것이다.

    블로그 생성 파이프라인은 별개였다, 한도 안 깎았음

    처음엔 블로그 글 여러 편 만든 게 원인이라고 생각했다. 틀렸다.

    블로그 글을 만드는 파이프라인은 API 키로 따로 돌아간다. 종량제(사용한 만큼만 별도로 돈 내는 방식) 결제라서 Claude Code의 5시간 사용량 한도와는 완전히 별개다.

    글 열 편을 만들어도 이 계기판 숫자는 안 움직인다. 계기판에 찍힌 72%는 순전히 대화 자체에서 나왔다. 파이프라인 탓이 아니었다.

    이건 좀 중요한 포인트다. 자동화 작업과 대화 사용량은 별도로 추적해야 한다. 같은 숫자로 묶어서 보면 원인을 잘못 짚는다.

    지금부터 /clear로 세션을 끝내기로 결심한 이유

    해결책은 단순하다. /clear를 쓰면 된다.

    /clear는 Claude Code에서 현재 세션을 완전히 초기화하는 명령어다. 대화 기록을 싹 지우고 새 세션으로 시작한다. 208MB짜리 짐을 내려놓고 가볍게 다시 출발하는 것이다. 공식 문서에도 세션이 길어질수록 토큰 사용량이 늘어난다고 나와 있다.

    왜 지금까지 안 썼냐면, 솔직히 필요성을 몰랐다. 대화가 길어지면 Claude가 문맥을 더 잘 기억하니까 오히려 좋은 거 아닌가 싶었다. 착각이었다.

    문맥이 길어지면 Claude가 더 많이 읽어야 한다. 비용이 는다. 속도도 느려진다. 문맥을 오래 유지하는 건 장점만 있는 게 아니다.

    사용량 한도는 어디서 확인하나

    Claude Code에서 /usage를 입력하면 사용량 화면이 열린다. 여기서 Current session의 사용률과 리셋 시각을 볼 수 있다. Current week(모든 모델)의 사용률과 리셋 시각도 함께 나온다.

    나는 이 화면에서 사용률이 17%, 28%, 36%로 올라가는 것을 직접 확인했다. 화면 아래에는 사용량이 많았던 이유도 나온다. 내 경우에는 150k 넘는 긴 문맥이 가장 큰 비중이었고, Playwright 같은 MCP 서버도 13~16%를 차지했다고 나왔다.

    사용량 초기화는 언제 되나

    /usage 화면에는 Current session 옆에 리셋 시각이 찍힌다. 내 계정에서는 Asia/Tokyo 기준 11:19pm이었다.

    같은 화면을 두 번 열어봤다. 두 번째는 몇 분 뒤였고, 사이에 대화를 몇 번 주고받았다. 그래도 사용률 17%와 리셋 11:19pm이 그대로였다. 리셋 시각이 메시지를 보낼 때마다 밀리지는 않았다.

    첫 사용 시점과 맞춰 보면

    이 세션 기록에서 오늘 첫 활동은 6시 25분 무렵이다. 화면의 리셋 11:19pm에서 5시간을 빼면 6시 19분이라 6분 차이가 난다. 같은 계정으로 열어 둔 다른 창이 6시 19분쯤 먼저 쓰였을 수도 있다. 그래서 “처음 사용한 시점부터 5시간”이라고 단정하지는 않는다.

    확인하지 못한 것도 있다. Pro 요금제의 리셋 규칙은 공식 문서에서 찾지 못했다. 위 내용은 내 화면에서 본 관찰이고, 다른 날 같은 시각에 리셋되는지는 아직 보지 못했다.

    이제 작업 하나가 끝나면 /clear를 친다. 새 작업은 새 세션에서 시작한다. 이걸 습관으로 만들어야 한다. 모르고 넘어갔다면 72%가 아니라 100%를 진작에 찍었을 것이다.

    계기판을 더 자주 봐야겠다는 생각도 든다. 이상한 걸 발견했을 때 그냥 넘기지 않고 물어본 덕분에 이 구조를 이해했다. 눈치채지 못했으면 다음 번에도 같은 실수를 반복했을 것이다.

  • 세션 ID 문제, Claude Code에서 MCP가 안 보이는 이유

    세션 ID 문제, Claude Code에서 MCP가 안 보이는 이유

    ujinify.com 워드프레스에 MCP Adapter가 코어에 내장돼 있다는 걸 알게 됐다. 나중에 밝혀졌지만, 이 이야기의 결론은 결국 세션 ID 하나였다.

    거기에 랭크매스(워드프레스 SEO 플러그인)가 ability(어빌리티, 서버가 외부에 제공하는 기능 단위)를 25개나 등록해뒀다. get-seo-scores로 여러 글 점수를 한 번에 조회하거나, audit-site-seo/fix-site-seo로 사이트 전체를 진단하고 자동 수정하거나, set-homepage-seo로 설정까지 바꿀 수 있는 것들이었다.

    그전까지는 SEO 점수 하나 확인하려면 Playwright(플레이라이트, 브라우저를 자동으로 조종하는 도구)로 wp-admin 에디터를 직접 열어야 했다. 이걸 Claude Code에 MCP 서버로 등록하면 API 호출 하나로 끝낼 수 있겠다 싶었다. 그래서 등록했다. 그리고 아무것도 안 됐다.

    문제: MCP가 ‘연결됨’인데 tool이 하나도 안 보여요

    등록 자체는 간단했다.

    claude mcp add --transport http ujinify-wp <MCP 서버 URL> --header "Authorization: Basic ..."

    claude mcp list를 돌리면 “✔ Connected”가 떴다. 연결됐다는 뜻이다. 그런데 ToolSearch(툴서치, Claude Code에서 사용 가능한 도구를 찾는 기능)로 아무리 찾아봐도 랭크매스 tool(툴, 도구)이 하나도 잡히지 않았다. 매번 “No matching deferred tools found”였다. 세션을 새로 시작해봤다. 재시작도 해봤다. 결과는 똑같았다.

    서버는 연결됐다는데, 실제로 쓸 수 있는 tool은 하나도 없는 상태. 이건 좀 웃겼다.

    curl로 직접 테스트해보니 세션 ID가 필요했다

    Claude Code를 믿지 않기로 했다. curl(컬, 터미널에서 HTTP 요청을 직접 보내는 도구)로 MCP 서버에 직접 말을 걸어보기로 했다.

    먼저 initialize(초기화 요청, MCP 서버와 처음 악수하는 단계)를 호출했다. 서버는 정상 응답했다. 그런데 응답 헤더(header, 서버가 응답과 함께 보내는 부가 정보)를 보니 mcp-session-id라는 값이 딸려 내려왔다. 이 서버는 Streamable HTTP(스트리머블 HTTP, 요청마다 세션 ID를 주고받는 MCP 통신 방식)를 쓰는 타입이었다.

    그 상태에서 세션 ID 없이 바로 tools/list를 호출했다. 결과는 “Missing Mcp-Session-Id header” 에러였다. 반대로, initialize에서 받은 세션 ID를 Mcp-Session-Id 헤더에 실어서 tools/list를 다시 호출했더니 — 랭크매스가 등록한 tool 3개의 스키마(schema, 도구의 구조와 사용법을 정의한 명세)가 정상으로 내려왔다.

    서버는 100% 정상이었다. 문제는 Claude Code 쪽이었다.

    왜 Claude Code는 세션 ID를 못 챙겼을까

    MCP 통신은 핸드셰이크(handshake, 통신 시작 전에 서로 규칙을 맞추는 절차)가 필요하다. MCP 공식 스펙 문서에도 명시돼 있는 내용인데, 순서는 이렇다.

    • initialize 요청을 보낸다.
    • 서버가 응답 헤더로 세션 ID를 내려준다.
    • 이후 모든 요청에 그 세션 ID를 Mcp-Session-Id 헤더에 실어서 보낸다.

    Claude Code의 HTTP MCP 클라이언트가 이 두 번째와 세 번째 단계를 처리하지 못하고 있었다. initialize는 성공했지만, 거기서 받은 세션 ID를 이후 요청에 다시 실어 보내지 않는 것이다.

    claude mcp list의 “Connected” 표시는 initialize 성공만 확인한다. 그러니 이 핸드셰이크 실패를 잡아내지 못하고, 겉으로는 정상처럼 보인다. 연결은 됐지만 실제로 아무것도 못 쓰는 상태가 “Connected”로 표시되는 것이다. 별로였다.

    해결책: 직접 만든 함수로 세션 핸드셰이크 처리하기

    MCP tool 등록에 기대는 걸 포기했다. 대신 이 핸드셰이크 절차를 그대로 코드로 옮겼다.

    scripts/rankmath-mcp.ts에 callRankMathAbility 함수를 만들었다. 이 함수는 호출될 때마다 아래 순서를 자동으로 처리한다.

    • initialize 요청을 보내서 세션 ID를 받는다.
    • 받은 세션 ID를 헤더에 실어서 tools/call을 호출한다.
    • 결과를 반환한다.

    CLI(커맨드 라인 인터페이스, 터미널에서 명령어로 프로그램을 실행하는 방식)에서 바로 쓸 수 있게 npm run rankmath -- <ability-name> '<json-params>' 형태로도 만들었다. 기존 upload.ts의 getWpConfig()를 그대로 재사용해서 인증 정보를 따로 관리할 필요도 없게 했다.

    Claude Code가 못 하는 걸 직접 구현한 것이다. 우아하진 않지만 동작한다.

    실제로 점수를 가져왔을 때 검증 결과

    get-seo-scores ability(어빌리티, 여기서는 랭크매스가 MCP를 통해 제공하는 기능)를 호출해서 여러 글의 점수를 한 번에 받아봤다. 이전에 Playwright로 wp-admin 에디터를 직접 열어서 확인했던 점수와 정확히 일치했다. 132번 글 81점, 109번 글 83점, 42번 글 79점. 숫자가 맞았다.

    그다음엔 실제로 사이트 전체를 점검했다. seo_filter를 bad(50점 이하), empty-fk(포커스 키워드 없음), unscored(채점 안 됨)로 각각 돌렸다. 결과는 깔끔했다. 문제되는 글은 하나도 없었다. 포커스 키워드가 없는 건 애초에 필요 없는 고정 페이지 3개뿐이었다.

    브라우저를 한 번도 열지 않았다. 몇 초 만에 끝났다.

    이 방법의 한계, 읽기만 되고 쓰기는 안 된다

    명확히 해둬야 할 게 있다.

    get-seo-scores는 데이터베이스에 이미 저장된 점수를 ‘읽기’만 한다. 랭크매스의 점수 계산 엔진 자체는 여전히 브라우저, 구체적으로는 wp-admin 에디터의 자바스크립트에서만 돌아간다. 새 글을 올리거나 콘텐츠를 수정한 직후에 점수를 새로 계산하려면 여전히 에디터를 열어야 한다. REST나 MCP로는 재계산을 트리거할 방법이 없다.

    analyze-post-content라는 ability도 있다. 이건 점수를 주지 않는다. 원본 콘텐츠와 24개 테스트 목록, 채점 기준 문서를 던져주고 AI가 직접 채점하라는 방식이다. 손이 많이 가서 아직 시도하지 않았다.

    결국 이 방법으로 해결된 건 ‘이미 채점된 점수를 빠르게 읽는 것’이다. 채점 자체는 여전히 브라우저 의존이다. 한계를 알고 쓰면 된다. 모르고 쓰면 나중에 헷갈린다.

    다음에는 analyze-post-content를 실제로 써볼 생각이다. AI가 직접 채점한 결과가 랭크매스 공식 점수와 얼마나 차이 나는지 비교해보면 쓸만한지 아닌지 판단할 수 있을 것이다.

    “설정 화면에 값이 있다고 실제로 작동한다는 보장은 없다”는 패턴은 이번에도 반복됐다. 워드프레스 SEO 버그 5개 총정리 글에서 다룬 다섯 가지 사례와 정확히 같은 결이다. 이번엔 랭크매스 설정이 아니라 Claude Code의 MCP 연결 상태였다는 것만 다르다.

    세션 쪽에서 겪은 또 다른 문제는 Claude 5시간 한도 72% 찍힌 이유는 세션 길이였다에도 있다.