본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 18. 19:04

OpenCode에서 GLM-5.2 테스트하기: 정말 인상적이었습니다!

요약

z.ai에서 출시한 오픈 웨이트 모델 GLM-5.2의 성능과 실무 적용 가능성을 테스트한 리뷰입니다. GLM-5.2는 이전 버전 대비 지능 지수가 크게 향상되었으며, 특히 컨텍스트 윈도우가 100만 토큰으로 확장되어 에이전틱 코딩에 최적화되었습니다.

핵심 포인트

  • GLM-5.2는 Intelligence Index가 40점에서 51점으로 상승하며 선두적인 오픈 웨이트 모델로 등극
  • 컨텍스트 윈도우가 200K에서 100만 토큰으로 대폭 확장됨
  • 동일한 비용으로 이전 버전보다 향상된 성능 제공
  • 대규모 저장소(Repo)를 활용하는 에이전틱 코딩 작업에 매우 유리함

고백하자면, 저는 AI 벤치마크(benchmarks)를 볼 때마다 눈을 치켜뜹니다. 격주로 누군가 Twitter에 새로운 모델이 갑자기 Opus와 GPT를 이기고 있다는 차트를 올리고, 답글창은 열광의 도가니가 되지만, 막상 실제로 사용해 보면 첫 번째 실제 작업에서 무너져 버리곤 하니까요. 숫자는 아름답지만, 코드는 엉망이죠.

그래서 z.aiGLM 5.2를 출시하고, 오픈 웨이트 (open-weights) 모델이 이제 프런티어 랩(frontier labs)의 뒤를 바짝 쫓고 있다는 소식이 들려오기 시작했을 때, 제 본능은 평소와 같았습니다. '그래, 어디 한번 증명해 봐.'

이 포스트는 제가 그것을 증명하는 과정입니다. 저는 GLM 5.2에게 거의 아무런 도움 없이 제 실제 운영 중인 웹사이트에 구축할 실제 기능을 맡겼고, 어떤 일이 벌어지는지 지켜보았습니다. 스포일러를 하자면, 제가 결국 쓰게 된 문장을 예상하지 못했습니다.

만약 제가 이 모든 과정을 라이브로 진행하는 것(제 도구들이 카메라 앞에서 충돌하는 부분 포함)을 보고 싶으시다면, 영상은 바로 여기에 있습니다:

제가 테스트하러 온 주장들

먼저 과장된 광고(hype)를 걷어내고 시작합시다. 왜냐하면 그 주장들이 정말 대단하기 때문입니다.

// Detect dark theme var iframe = document.getElementById('tweet-2066938937344495629-390'); if (document.body.className.includes('dark-theme')) { iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2066938937344495629&theme=dark" }

GLM 5.2는 GLM 5.1과 물리적 크기가 동일하지만 (총 파라미터 744B, 활성 파라미터 40B), Artificial Analysis Intelligence Index에서 40점에서 51점으로 11포인트 상승했습니다. 이 점수로 GLM 5.2는 MiniMax-M3 (44), DeepSeek V4 Pro (44), Kimi K2.6 (43)를 앞질러 선두적인 오픈 웨이트 (open-weights) 모델이 되었습니다. 전체 리더보드에서는 Claude Fable 5 (60), Claude Opus 4.8 (56), GPT-5.5 (55)의 뒤를 이어 위치하고 있습니다. 가중치(weights)를 다운로드할 수 있는 오픈 소스이자 MIT 라이선스 모델로서, 이는 정말 놀라운 위치입니다.

한눈에 보는 업그레이드 내용은 다음과 같습니다:

GLM 5.1GLM 5.2
Intelligence Index (v4.1)4051
...

두 가지가 눈에 띕니다. 첫째, 컨텍스트 윈도우 (Context Window)가 200K에서 무려 **100만 토큰 (1 million tokens)**으로 늘어났습니다. 이는 하네스 (Harness)가 전체 저장소 (Repo)를 프롬프트에 집어넣는 에이전틱 코딩 (Agentic Coding)에서 매우 중요한 요소입니다. 둘째, 가격은 전혀 변하지 않았습니다. 5.1과 동일한 비용으로 11포인트 더 똑똑해졌습니다.

// Detect dark theme var iframe = document.getElementById('tweet-2067135640249209175-416'); if (document.body.className.includes('dark-theme')) { iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2067135640249209175&theme=dark" }

표의 마지막 행은 온라인에서 보게 될 불만 사항에 대한 설명이기도 하며, 이에 대해서는 나중에 다시 다루겠습니다. 차트의 숫자는 숫자일 뿐입니다. 이제 직접 무언가를 만들어 봅시다.

나의 설정: OpenCode와 OpenRouter

내가 사용한 하네스 (Harness)로는 데스크톱 앱인 OpenCode를 사용했습니다. 이는 Claude가 아닌 모델들을 테스트하기 위한 나의 즐겨 찾는 놀이터가 되었습니다. 모델 자체는 OpenRouter를 통해 라우팅했습니다.

왜 OpenCode 자체 제공자가 아닌 OpenRouter를 사용했을까요? 솔직히 말해서 작은 불만 때문입니다. OpenCode Zen과 Go 구독 서비스는 새로운 오픈 웨이트 (Open-weights) 모델을 추가하는 속도가 느립니다. 내가 기록하던 날 기준으로 GLM 5.2는 두 곳 모두에 아직 올라와 있지 않았습니다. 반면 OpenRouter는 첫날부터 지원했기에 그쪽을 선택했습니다. 만약 이 모델을 사용하고 싶다면, 현재로서는 OpenRouter가 가장 저항이 적은 경로입니다.

여기 단골분들을 위해 한 가지 말씀드리자면: 저는 보통 Claude Code를 사용하는 사람입니다. 저는 Claude Max를 유료로 사용하고 있으며, 이 채널에서 Claude가 현존 최고의 에이전트라고 여러 번 말해왔습니다. 따라서 이 영상은 "Claude가 나쁘다"는 영상이 아닙니다. Claude의 충성 고객인 제가, 오픈 모델이 과연 대등하게 경쟁할 수 있는지 진심으로 궁금해서 진행하는 것입니다.

테스트: 내 실제 사이트의 실제 기능

단순한 연습용 할 일 관리(to-do) 앱이 아닙니다. 대상은 제 개인 웹사이트인 bergdaniel.com.br였습니다. 이 사이트는 빌드 타임(build time)에 dev.to API를 호출하여 블로그 페이지를 채우고, YouTube API를 호출하여 강의 페이지를 채우는, 완전히 서버 사이드 렌더링(Server-rendered)된 Next.js 앱입니다. 대단한 것은 아니지만, 실제로 배포되어 운영 중인 진짜 코드입니다.

제 블로그 목록이 충분히 늘어나서 검색창이 필요해졌습니다. 그것이 이번 과제였습니다. 그리고 모델 리뷰에서 중요한 부분은 다음과 같습니다: 저는 의도적으로 문맥(context)을 거의 제공하지 않았습니다. "참고로 우리는 데이터베이스가 없어요"라거나 "이것은 ISR(Incremental Static Regeneration)로 서버 사이드 렌더링된다는 점을 기억하세요" 같은 말은 하지 않았습니다. 모델이 스스로 제약 사항을 파악할지, 아니면 그 제약 사항에 걸려 넘어질지 확인하고 싶었습니다.

프롬프트 전체 내용은 다음과 같았습니다:

블로그 페이지에 검색 기능을 구현하는 것을 도와주세요. 가능하다면 검색을 위해 쿼리 파라미터(query params)와 URL 상태(URL state)를 사용하세요. 예시: ?q=. 사용자가 검색창에 무언가를 입력할 때마다 명시적인 제출(submit) 버튼을 만들 필요는 없으며, 약 300밀리초 정도 기다린 후 디바운스(debounced) 검색을 사용하세요.

그런 다음 저는 플랜 모드(plan mode)를 켜고 제출을 눌렀습니다. 에이전트(agent)를 정직하게 테스트하는 방법은 명세(spec)를 따를 수 있는지 여부가 아닙니다. 명세가 모호할 때 모델이 어떻게 행동하는지를 보는 것입니다.

플랜 모드가 실제로 저를 놀라게 했습니다

GLM-5.2는 다른 오픈 웨이트(open-weights) 옵션들보다 느리고 더 신중한 모델입니다. 전날 이미 그 점을 인지했습니다. 하지만 계속 논쟁해야 하는 빠른 모델보다는, 한 번에 기능을 제대로 구현하는 느린 모델을 택하겠기에 모델이 생각할 시간을 주었습니다.

먼저 모델은 코드베이스를 조사한 뒤, 제 블로그 페이지가 getArticles를 호출하고 12시간마다 ISR로 재생성되는 서버 컴포넌트(server component)라는 점을 정확하게 보고했습니다. 그다음 모델은 다음과 같은 계획을 제안했습니다: 페이지를 서버 컴포넌트로 유지하되, 검색을 위한 새로운 클라이언트 컴포넌트(client component)를 추가할 것, 각 키 입력에 대해 300ms의 디바운스(debounce)를 적용할 것, 그리고 매 글자마다 브라우저 히스토리가 오염되지 않도록 URL에 replace를 실행할 것. 초기화를 위한 명확한 X 버튼과 검색 결과가 없을 때의 빈 상태(empty state) 처리까지 포함되었습니다.

하지만 저를 놀라게 한 부분은 바로 추론(reasoning) 과정이었습니다. 모델은 요청하지 않았음에도 불구하고, 왜 클라이언트 사이드 필터링(client-side filtering)을 선택했는지 그 이유를 설명했습니다:

기사들은 이미 빌드 타임(build time)에 가져와집니다. 클라이언트 사이드(client side)에서 필터링을 수행하면 ISR을 온전하게 유지할 수 있고, 키스트로크(keystroke)마다 추가적인 페치(fetch)가 발생하는 것을 방지하며, URL 상태를 단일 진실 공급원(single source of truth)으로 둘 수 있습니다.

정확히 맞습니다. 그것이 바로 제가 모델이 빠지기를 기대했던 함정이었습니다. 런타임(runtime)에 키스트로크마다 dev.to API를 쿼리하는 상황 말이죠. 그런데 모델은 제가 ISR에 대해 한마디도 하지 않았음에도 이를 피해갔습니다. 심지어 Next.js에서 useSearchParams를 사용하려면 필요하다는 점을 알고 컴포넌트를 Suspense 경계(boundary)로 감싸기까지 했습니다. 이것은 제가 가중치(weights)를 다운로드한 모델이 아니라, Opus에게나 기대할 법한 모습입니다.

그러더니 모델은 보통 최첨단 모델(frontier models)들만이 하는 행동을 했습니다. 코드를 작성하기 전에 저에게 좋은 질문들을 던진 것입니다.

  • 검색이 활성화되었을 때, "New" 배지는 어떻게 동작해야 합니까? (검색 중에는 숨김 / 첫 번째 일치 항목에서 유지 / 항상 최신 항목에 표시)
  • 어떤 필드를 대상으로 검색을 수행해야 합니까? (제목, 설명, 그리고 태그 (권장))
  • 일치하는 항목이 없을 때는 무엇을 보여줘야 합니까? ("검색된 기사가 없습니다" 메시지 (권장))

옵션에 있는 (recommended)가 보이시나요? 이것은 Claude의 방식입니다. 제가 Claude 모델들과 대화할 때, 그들은 거의 항상 괄호 안에 권장 선택지를 표시하는데, 이는 정말 유용합니다. GLM이 합리적인 기본값과 함께 똑같이 행동하는 것을 보았을 때, 저는 정신이 번쩍 들었습니다.

mind blown

모델이 실제로 작성한 코드

말은 쉽죠, 그래서 실제 결과물을 보여드리겠습니다. 이것은 현재 제 사이트에 배포되어 있는 코드이며, 리포지토리(repo)에서 그대로 복사해 왔습니다.

먼저, 블로그 페이지의 변경 사항입니다. 서버 컴포넌트(server component) 상태를 유지하고, ISR 재검증(revalidation)을 유지하면서, 기사들을 새로운 검색 컴포넌트로 넘겨주기만 했습니다.

// src/app/blog/page.tsx
import type { Metadata } from "next"

...

그다음은 새로운 클라이언트 컴포넌트 (client component)입니다. 제가 요청한 것과 정확히 일치하게 제목, 설명, 태그를 검색하는 matches 헬퍼 (helper), 300ms 디바운스 (debounce), scroll: false 옵션이 포함된 router.replace, 그리고 하단의 Suspense 래퍼 (wrapper)를 확인해 보세요:

// src/components/blog-search.tsx
"use client"

...

가독성을 유지하기 위해 위 스니펫 (snippet)에서 SearchInput 서브 컴포넌트 (sub-component)와 몇 가지 이펙트 (effects)를 생략했지만, 여러분이 보고 있는 모든 것은 모델이 작성한 실제 코드이며 수정되지 않은 상태입니다. newPost={!isSearching && index === 0} 라인은 모델 스스로 던진 "New 배지 (badge)" 질문에 대한 해답입니다. 즉, 검색 중에는 배지를 숨기고, 그렇지 않으면 최신 포스트에 배지를 보여주는 방식입니다. 모델은 자신의 디자인 결정을 JSX에 직접 연결했습니다.

디자인 측면에서는 사이트가 어떻게 생겼는지 알려주지 않았음에도 제 사이트의 나머지 부분과 일치했습니다. 동일한 테두리 처리, 동일한 간격, 동일한 낮은 불투명도 (muted opacity)를 적용했습니다. 테스트를 위해 "Elixir", "React", "Fable"을 입력했을 때 즉시 필터링되었고, 주소창에 /blog?q=fable을 직접 붙여넣어도 작동했습니다. 계획했던 대로 URL 상태 (URL state)가 단일 진실 공급원 (single source of truth) 역할을 수행했습니다.

예상치 못하게 마음에 들었던 부분: 절제

작업이 끝난 후 npm run check를 실행했을 때 몇 가지 린트 (lint) 경고가 있었습니다. 하지만 여기서 중요한 점은, 그 경고들이 GLM이 전혀 건드리지 않은 제 Tidewave 프록시 (proxy) 파일들에 있었다는 것입니다. 그리고 모델의 응답은 기본적으로 "남아 있는 하나의 경고는 기존에 존재하던 것이며 제 변경 사항과 무관하므로 그대로 두었습니다"라는 식이었습니다.

어떤 개발자들은 이를 싫어할 수도 있습니다. 그들은 에이전트 (agent)가 보이는 모든 것을 수정하기를 원합니다. 저는 정반대입니다. 만약 모델이 제가 건드려 달라고 요청하지 않은 파일들을 선제적으로 다시 쓰기 시작한다면, 단 하나의 컴포넌트 기능을 구현하기 위해 40개 파일의 디프 (diff)가 발생하고, 직접 코드를 짜는 것보다 더 오래 걸리는 코드 리뷰를 해야 할 것입니다. GLM은 제가 선을 긋는 바로 그 지점에서 선을 그었습니다. 요청한 것을 변경하고, 포맷 (format)을 실행하고, 빌드 (build)를 확인한 뒤, 저장소의 나머지 부분은 그대로 두는 것입니다.

참고로, 이 저장소의 제 AGENTS.md에는 엄격한 규칙이 있습니다:

중요: 코드 변경을 수행한 후에는 항상 다음을 실행하십시오:

  1. npm run format - 코드 포맷팅 (Format)
  2. npm run check - 린팅 (Linting) 또는 타입 오류 (Type error)가 없는지 확인
    ...

GLM은 이를 글자 그대로 따랐습니다. 포맷팅을 하고, 체크를 하고, 빌드(Build)를 한 뒤 멈췄습니다. 이것은 에이전트(Agent)가 자신의 지침을 읽고 그 경계를 존중하는 모습이며, 이는 말하는 것보다 훨씬 어려운 일입니다.

그러다 OpenCode가 충돌했습니다

이제 솔직한 이야기를 해보겠습니다. 이 과정이 완벽했다고 거짓말하지는 않겠습니다. 제가 커밋(Commit)을 정리해달라고 요청한 직후, OpenCode가 멈추더니 메인 프로세스(Main process)에서 JavaScript 오류를 발생시켰습니다. 카메라 앞에서 앱 전체가 다운되었습니다.

facepalm

분명히 말씀드리자면, 이것은 OpenCode의 문제이지 GLM 5.2의 문제가 아닙니다. 모델의 작업은 괜찮았습니다. 모델을 둘러싼 하네스(Harness)가 넘어졌을 뿐입니다. 저는 다시 실행했고, 변경 사항은 그대로 남아 있었으며(아직 푸시(Push)되지는 않았지만), 이를 푸시하여 배포했습니다. 짜증 나는 일이었지만, 빠르게 발전하는 도구들에서 흔히 발생하는 일이며, 편집해서 없애기보다는 보여줄 가치가 있는 부분입니다.

내장된 리뷰 기능이 실제 문제를 잡아냈습니다

OpenCode에는 코드 리뷰어 서브 에이전트(Code-reviewer sub-agent)를 구동하는 /review 명령어가 포함되어 있습니다. 저는 이 명령어를 GLM이 생성한 커밋에 지정하고 실행했습니다.

리뷰어는 버그를 찾지는 못했는데, 이는 제가 코드를 읽은 결과와 일치하지만, 두 가지 사소한 사항을 지적했습니다. 하나는 타이핑 중 발생하는 미묘한 입력 드리프트(Input drift)였고, 다른 하나는 딥 링크(Deep links) 시 발생하는 허용 가능한 깜빡임(Flash)이었습니다. 입력 드리프트는 실제 문제였습니다. URL 동기화 효과로 인해 각 키 입력 약 300ms 후에 입력값이 다듬어진(Trimmed) URL 값으로 재설정되었는데, 이로 인해 커서 아래의 끝부분 공백(Trailing whitespace)이 삭제될 수 있었습니다. GLM은 이를 깔끔하게 수정했습니다:

// src/components/blog-search.tsx  (수정 사항)
// 뒤로 가기/앞으로 가기 탐색 시 입력을 동기화 상태로 유지합니다. URL이 이미
// 현재 입력을 반영하고 있는 경우(예: 쓰기 작업 시 끝부분 공백이 제거된 경우)에는 건너뜁니다...

이것을 시도할 때 주의해야 할 점이 한 가지 있습니다. GLM은 커밋(commit)하려는 의지가 매우 강합니다. 제가

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0