새벽 두 시에 장애 알림이 울린다. 이미지 메타데이터를 붙여 주는 마이크로서비스가 죽었고 그 여파로 검색 결과 화면이 통째로 깨졌다. 모니터링 알람은 여러 군데서 동시에 울려 대는데 정작 어느 서비스가 제일 먼저 주저앉았는지는 아무도 알려 주지 않는다. 졸린 눈을 비비며 대시보드를 하나씩 열어 보지만 차트 제목만 봐서는 도무지 무엇을 재는 그래프인지 알 길이 없다. 사실 과부하에 걸린 범인을 정확히 가리키는 차트는 밤새 그 자리에 떠 있었다. 다만 당직자가 그 차트를 찾지 못했을 뿐이다.

날이 밝은 뒤 재발 방지 문서를 정리하려 해도 답답하기는 마찬가지다. 실시간 차트는 Grafana에 떠 있고 그 차트를 어떻게 읽어야 하는지 적어 둔 설명서는 Notion에 따로 살며 장애 대응 코드는 GitHub에 흩어져 있다. 처음 문서를 만들 때는 양쪽에 부지런히 링크를 걸어 두지만 몇 달만 지나도 차트에는 새 라벨이 붙는데 Notion 문서는 옛날 그대로 방치된다. 둘이 여전히 같은 지표를 말하고 있는지 확인하려면 브라우저 탭을 수도 없이 왔다 갔다 해야 한다. 그러다 보면 결국 팀 안에서 가장 믿을 만한 최신 문서는 나 혼자 메모장으로 쓰는 Slack 개인 채팅방이 되고 만다.

오픈소스 기술 위키 ‘Oxynote’는 바로 이 한밤중의 삽질에서 출발한 프로젝트다. 실시간으로 움직이는 지표 차트와 그 차트를 해설하는 줄글 문서, 그리고 두 가지를 한꺼번에 고치고 검토하는 코드 리뷰 흐름을 아예 한 페이지 안에 합쳐 버렸다. 왜 기존의 쟁쟁한 도구들을 두고 이런 합체 도구를 새로 만들었는지 그 구조를 뜯어본다.

세 도구가 각자 한 조각씩만 쥐고 있다

Grafana는 차트, Notion은 설명, GitHub는 리뷰를 각각 맡는 상황과 Oxynote가 셋을 한 페이지에 모은 모습을 비교한 그림
도구마다 한 조각씩 들고 있으면 세 조각을 한 번에 고칠 수 없다.

지금 우리가 쓰는 도구들은 저마다 자기 영역 한 가지는 기막히게 잘 해내지만 다른 영역과는 좀처럼 섞이지 않는다. Grafana에서 차트 패널 하나에 설명을 덧붙일 수 있는 공간은 작은 정보 아이콘(i) 뒤에 숨은 툴팁 칸뿐이고 심지어 공식 문서조차 그 칸은 최대한 짧게 쓰라고 권한다. 그래서 긴 배경 설명을 Notion으로 옮겨 적으면 이번에는 차트 쪽이 말썽을 부린다. Notion 페이지에 Grafana 패널을 iframe으로 끼워 넣을 수는 있지만 Grafana에 이미 로그인한 사람에게만 보일뿐더러 Grafana Cloud 환경에서는 아예 차단되어 뜨지 않는다. 무엇보다 두 도구는 수정 이력을 각자 따로 관리하기 때문에 차트 쿼리와 설명 문단을 하나의 세트로 묶어서 리뷰하고 승인받는 일은 애초에 불가능하다.

이 도구를 만든 두 개발자의 경험담을 들어 보면 문제가 더 생생하게 와닿는다. 한 사람은 금융 시장 데이터를 웹소켓으로 실시간 수집하는 서버를 맡았는데 트래픽이 몰릴 때마다 메모리가 새면서 서버가 죽었고 그때마다 누락된 이벤트를 손으로 다시 메워야 했다. 사내 대시보드 어디에도 이 위험 신호를 똑바로 보여 주는 곳이 없어서 결국 개인적으로 cron 스케줄러를 돌려 상태 확인 주소로 curl 요청을 쏘고 그 결과를 자기 Slack으로 받아 보며 버텼다. 다른 한 사람은 글 첫머리에서 말한 새벽 두 시 장애를 겪은 장본인이다. 흥미로운 건 그날 밤 지친 사람은 끝내 못 찾았던 문제의 차트를 Datadog의 MCP 서버에 연결해 둔 AI 에이전트는 단숨에 찾아냈다는 사실이다.

결국 두 사람이 간절히 원했던 건 하나로 모인다. 실시간 그래프, 배경 설명, 복구용 명령어, 기능 플래그가 전부 한 페이지에 모여 있고 함께 편집되며 코드처럼 깐깐하게 리뷰를 거치는 문서다. 새벽에 잠이 덜 깬 당직자가 읽어도 단번에 이해되고 AI 에이전트가 읽어도 막힘이 없어야 한다. Oxynote는 세상에 없던 기발한 기능을 발명하기보다 세 군데로 찢어져 있던 조각들을 한 화면에 나란히 올려놓는 데 집중한 도구다.

차트가 외부 화면을 빌려 오지 않고 직접 쿼리를 돌린다

한 페이지 안에 설명 문단과 쿼리를 직접 실행하는 Live Metrics 차트 블록과 임계치 설명이 위아래로 놓인 구조를 보여주는 그림
차트는 임베드가 아니라 페이지의 한 블록이다.

이 위키의 핵심 부품은 ‘Live Metrics’라는 블록이다. 문서 중간에 이 블록을 하나 만들고 Prometheus 쿼리나 SQL 쿼리를 적어 넣으면 그 자리에서 바로 시계열 꺾은선 차트나 막대 차트, 게이지 형태로 데이터를 그려 준다. SQL 데이터 원본으로는 PostgreSQL, MySQL, MariaDB를 곧바로 연결할 수 있다. 이런 차트 블록 여러 개를 바둑판처럼 나란히 배치하면 ‘메트릭 그리드(metric grid)’가 되는데 Grafana로 치면 어엿한 대시보드 하나가 문서 한가운데에 자리 잡는 셈이다. 외부 대시보드 화면을 액자처럼 빌려 오는 게 아니라 문서 블록 자체가 직접 쿼리를 실행하기 때문에 차트가 무엇을 뜻하는지, 빨간 위험선(임계치)이 왜 그 높이에 그어져 있는지, 선을 넘었을 때 당직자가 어떤 명령어를 쳐야 하는지가 차트 바로 위아래에 일반 문단으로 자연스럽게 이어진다.

저장소 첫머리에 실린 예시 화면을 보면 이 조합의 위력이 한눈에 들어온다. 예약 API 중 배송(Shipments) 엔드포인트를 설명하는 문서인데 화면 왼쪽에는 응답 데이터 필드 설명이 적혀 있고 오른쪽에는 curl 호출 예시 코드와 실제 운영 서버의 상위 99%(p99) 응답 지연 시간 차트가 위아래로 나란히 떠 있다. 차트 안에는 ‘Degraded(성능 저하)’와 ‘Heavy delay(심각한 지연)’라는 두 개의 기준선이 그어져 있다. 흔히 보는 Stripe 스타일의 좌우 분할 API 문서 안에 실시간 상태 계기판까지 합쳐 놓은 모양이다. 개발자가 API 명세를 읽다가 “어, 지금 이 엔드포인트가 평소보다 느린가?” 싶으면 시선을 옆으로 살짝 옮기기만 하면 된다.

물론 일반 기술 위키가 갖춰야 할 기본기도 빠짐없이 챙겼다. 제목, 목록, 체크리스트, 인용문, 파일명이 달린 코드 블록, 강조 박스(콜아웃), 이미지, 첨부 파일, Figma 디자인 임베드, 텍스트로 그리는 Mermaid 다이어그램까지 블록 단위로 조립할 수 있고 폴더 트리와 태그, 전체 본문 검색, 단축키, 다크 모드도 지원한다. 하지만 다른 수많은 위키들과 갈라지는 결정적인 차이는 역시 차트가 단순한 그림이나 외부 링크가 아니라 살아 움직이는 쿼리 블록이라는 점 하나에 있다.

문서 수정도 코드처럼 초안과 diff를 거쳐 병합된다

초안에서 블록 단위 diff를 거쳐 승인되면 보호된 main에 병합되는 네 단계 흐름을 보여주는 그림
문장과 엔드포인트 이름과 응답 필드와 차트가 한 diff에 들어간다.

중요한 운영 문서는 아무나 바로 고치지 못하도록 ‘리뷰 필수 페이지’로 잠가 둘 수 있다. 이렇게 지정된 페이지는 누군가 내용을 고치면 곧바로 반영되지 않고 초안(draft) 상태로 따로 저장된다. 여기서 빛을 발하는 게 블록 구조를 이해하는 전용 비교 화면(diff)이다. 단순히 글자 몇 개가 바뀐 것만 보여 주는 게 아니라 API 엔드포인트 주소가 바뀐 것, 응답 필드 표의 한 칸이 수정된 것, 본문 설명 문장이 다듬어진 것을 각 블록 모양 그대로 유지한 채 빨간색(삭제)과 초록색(추가)으로 한 화면에 나란히 보여 준다. 동료 리뷰어가 변경 내역을 확인하고 승인 버튼을 눌러야만 비로소 보호된 main 문서에 합쳐진다. 평소 GitHub에서 풀 리퀘스트(PR)로 코드를 다루던 작업 방식을 기술 문서에 그대로 이식한 것이다.

초안을 함께 고치는 과정은 구글 독스처럼 실시간으로 돌아간다. 지금 이 페이지에 누가 들어와 있는지, 누가 어느 블록을 붙잡고 고치는 중인지 커서 위치까지 실시간으로 보여서 두 사람이 같은 문단을 동시에 덮어쓰는 사고를 막아 준다. 피드백 댓글도 아주 세밀하게 달 수 있다. 일반 본문의 특정 단어 하나는 물론이고 Mermaid 다이어그램을 그리는 소스 코드의 특정 줄이나 실시간 차트 블록 전체에도 댓글 스레드를 열 수 있다. 실제로 공개된 예시 화면에는 상태 다이어그램 코드 안의 8s timeout이라는 구절에 동료가 댓글을 달고 수정이 끝난 뒤 해결(resolve) 버튼을 누르는 장면이 나온다. 나를 부른 멘션과 답글 알림은 전용 받은 편지함으로 깔끔하게 모인다.

굳이 위키에 이렇게 깐깐한 코드 리뷰 절차를 넣은 이유도 앞서 말한 개발자들의 고생담과 맞닿아 있다. 실무에서는 관리자용 복구 작업이 곧 특정 curl 명령어 한 줄이고 장애 대응 절차가 곧 차트의 임계치 숫자인데 기존 위키에서는 누가 그 숫자를 슬쩍 바꿔 놓아도 앞뒤 맥락을 비교해 검토할 방법이 마땅치 않았다. 문서를 정말로 운영 코드만큼 중요하게 취급하려면 문서의 변경 비교 화면부터 코드의 diff만큼 정교해야 한다는 생각이 이 구조를 만들었다.

참조하던 파일이 바뀌면 문서가 먼저 손을 든다

아무리 꼼꼼하게 리뷰를 거쳐 작성한 문서라도 코드가 바뀌고 시간이 흐르면 소리 없이 낡기 마련이다. Oxynote는 이 해묵은 문제를 풀려고 ‘신선도 훅(freshness hook)’이라는 장치를 도입했다. 특정 문서 페이지나 개별 블록에다가 “이 설명은 GitHub의 특정 소스 파일, 특정 도커 컨테이너 이미지, 외부 웹페이지, 혹은 지정한 날짜에 의존한다”고 미리 연결 고리를 걸어 두는 기능이다. 나중에 그 원본 코드나 컨테이너 이미지가 업데이트되면 위키가 이를 감지하고 문서 담당자(maintainer)에게 “원본이 바뀌었으니 문서를 확인하라”고 알림을 보낸다. 사람이 일일이 기억해서 문서를 고치는 대신 문서 쪽에서 먼저 자기가 낡았다고 손을 드는 구조다.

이 문서를 읽고 고치는 주체는 사람만이 아니다. 위키 안에 기본 내장된 ‘Rubber Duck’이라는 AI 도우미는 사람과 똑같이 페이지 구조를 읽고 편집할 뿐 아니라 차트 블록에 걸린 Prometheus나 SQL 쿼리를 직접 실행해 현재 수치까지 확인한다. 뒤에서 돌아가는 언어 모델은 Anthropic, OpenAI, Google, OpenRouter 같은 상용 API 중에서 고르거나 Ollama를 연결해 사내 로컬 모델로 돌릴 수 있다. 또한 OAuth 인증을 거쳐 Claude Code나 Codex 같은 외부 MCP 클라이언트에게 똑같은 문서 읽기·쓰기 도구를 내어줄 수도 있다. 새벽 장애 때 사람은 못 찾은 차트를 AI 에이전트가 먼저 찾아냈던 경험을 떠올려 보면 왜 처음부터 에이전트를 일급 독자로 대우해 설계했는지 고개가 끄덕여진다.

직접 서버에 띄워 보는 방법도 도커 컨테이너 한 줄로 단출하게 묶여 있다.

docker run -d --name oxynote \
  -p 8080:8080 \
  -v oxynote_data:/oxynote/data \
  ghcr.io/oxynote/oxynote:latest

단, 처음 띄웠을 때 쓰이는 기본 관리자 아이디와 비밀번호가 공개 문서에 그대로 적혀 있으므로 사내망이나 외부에 포트를 열기 전에 비밀번호부터 반드시 바꿔야 한다. 백엔드는 Go 언어로, 프런트엔드는 Vue와 Nuxt로 작성되었으며 전체 코드가 Apache 2.0 오픈소스 라이선스로 풀려 있다.

Grafana를 당장 버릴 순 없어도 빈틈은 메울 수 있다

개발팀은 문서 한구석에 ‘의도적인 단순함(Simple on purpose)’이라는 제목을 달고 이 도구가 누구에게 맞고 누구에게 안 맞는지를 솔직하게 밝혀 두었다. 동료 엔지니어에게 복잡한 시스템 구조를 설명해야 하는 사람, 거창한 문서 자동 생성기 없이 깔끔한 내부 API 문서를 손수 꾸미고 싶은 사람, 문서 변경을 초안과 diff와 승인 절차로 단단하게 관리하고 싶은 팀, 그리고 사람과 AI 에이전트가 똑같은 운영 문서를 보며 일하길 바라는 셀프 호스팅 환경에는 안성맞춤이다. 반대로 가입만 하면 바로 쓸 수 있는 클라우드 구독형(SaaS) 서비스를 찾는 팀이나 비개발 직군까지 모두 쓰는 전사 일반 위키, 혹은 깨알 같은 설정 옵션이 수백 개씩 달린 만능 도구를 원하는 곳에는 맞지 않는다.

Does it replace Grafana? That is the goal.

자주 묻는 질문(FAQ)에 적힌 이 짧은 문답에 개발팀의 진짜 속내가 담겨 있다. 물론 지금 당장 수년째 써 오던 Grafana를 걷어내고 이 위키 하나로 갈아탈 수는 없다. 현재 버전은 Prometheus와 SQL 기반의 지표 차트와 대시보드 그리드까지만 지원할 뿐 장애 대응의 나머지 절반인 로그 검색과 실시간 알림(alerting) 기능은 아직 없기 때문이다. 하지만 Grafana 패널 설명란이 너무 좁아서 Notion으로 쫓겨났던 운영 매뉴얼과 그 문서가 가리키던 핵심 차트들을 한자리에 다시 합쳐 놓는 용도로는 지금 당장도 충분히 제 몫을 해낸다.

결국 이 오픈소스가 던지는 가장 묵직한 질문은 도구 바깥에 있다. 지금 우리 팀의 핵심 지표 차트와 그 차트를 읽는 법, 그리고 두 가지를 고친 변경 이력은 몇 개의 서비스에 조각나 흩어져 있는가. 만약 오늘 밤 새벽 두 시에 알람이 울린다면 잠에서 덜 깬 동료가 브라우저 탭을 헤매지 않고 한 화면에서 원인과 대응법을 곧바로 찾을 수 있는가. 이 질문에 선뜻 그렇다고 답하기 어렵다면 우리 팀의 가장 믿을 만한 문서 역시 누군가의 개인 채팅방으로 흘러 들어가고 있을지 모른다.

출처: Oxynote/Oxynote