개발
코덱스와 함께하는 코덱스 사용량 조회 맥북 메뉴바 프로그램 만들기 (NodeJs, Swift + AppKit, zsh)
by
나는 백엔드 개발자다.
뭐 앞단도 할줄알고 뒷단도 할 줄아는 풀스택 개발자라고 가끔 소개하긴 하지만 근본은 백엔드라 생각한다.
예전에는 개발을 하려고하면 개발언어의 습득부터 개발환경 구축, 관련 프레임워크 학습, 수많은 사이드 프로젝트 테스트, 테스트, 테스트를 무진장 반복해야 1인분의 개발자가 되기마련이다.
그렇기 때문에 다들 자기들만의 주요 언어를 말하고 다녔고, 다들 그렇게 해당분야에서의 고수가 되어갔다.

하지만 지금은 어떤가?
요즘은 AI가 다 기획해주고 코딩해주는 세상이다.
그저 나는 그런 AI에게 명령과 구상만 잘 내려주면되는 그런 관리자가 되어가고 있는 실상이다.
이런 상황에서 여러가지 코딩 보조 에이전트가 있는데, 그 중 나는 코덱스를 가장 가볍게, 그리고 효율적으로 사용하고있다.
다루는 분야가 그렇게까지 복잡하지 않아서 클로드까지는 너무 과했고, 지금은 코덱스와 동고동락하고 살고있다.
하지만 이런류의 AI서비스가 으레 그렇듯, 사용량 한도라는게 각각 존재한다.
뭐 ai 회사도 돈을벌어야하니까 인정은 하는 부분이라 딱히 문제는없다.
다만 사용하는 입장에서는 이 한도라는걸 주기적으로 확인하고 싶은게 현실이다.
코덱스 사용량 조회코덱스의 경우엔 설정 -> 남은 요금 한도를 보면 얼마나 가용량이 남았는지 쉽게 볼수있다.
근데말이다.
매번 저 설정누르고 요금한도 누르고 이러는게 여긴 귀찮은게 아니다.
심지어나는 사용하는 IDE에서 직접 코덱스를 물려서 쓰는 경우가 많아서, 사용량을 알아보기 위해서는 매번 코덱스앱을 켜서 저 화면을 조회해봐야한다.
여간 귀찮은게 아니다.
그럼 이런 부분을 자동으로 풀링해서 맥북 상단 메뉴바에 띄워주면 어떨까?
1. 기술 스택
우선 이 목표에서 우리가 필요한 기술스택은 총 3개다.
환경은 개인이 쓰는 맥북에서 잘 구동하기 위한 로컬 프로그램이므로 아래와 같이 설계를 해볼 수 있겠다.
1) 한도 조회:
Node.js2) macOS UI:
Swift + AppKit3) 빌드/설치:
zshjs를 이용해 코덱스의 app-server에 JSON-RPC 방식으로 붙어서 한도조회를 하고,
이후 메뉴바에 앱형식으로 이를 표현하기위해 스위프트코드와 앱킷을 이용해 이를 구현한다.
이후 이렇게 만든 프로그램이 유효한 프로그램임을 인증하기위한 서명코드를 발급 후 이를 실행하게하는 구조이다.
2. Codex 한도 조회
한도 조회를 하는 파트에서 제일 핵심부분은 아래와 같다.
const child = spawn(codexPath, ["app-server"], {
stdio: ["pipe", "pipe", "pipe"],
});
child.stdin.write(
`${JSON.stringify({
id: 1,
method: "initialize",
params: {
clientInfo: {
name: "codex-limit-checker",
title: "Codex Limit Checker",
version: "0.1.0",
},
capabilities: {
experimentalApi: true,
},
},
})}\n`,
);
Copy해당 코드는 Codex 실행 파일을 별도의 프로세스로 띄운 뒤, 표준 입출력(stdio)을 통해 JSON-RPC 방식으로 통신하여 계정의 사용량 한도(rate limit)를 조회하는 구조로 동작한다.
먼저 spawn을 이용해 app-server 모드로 Codex를 실행하면, 일반 CLI가 아닌 요청을 받아 처리하는 서버 형태로 동작하게 된다.
이때 stdio를 모두 pipe로 열어 두어, stdin으로 요청을 보내고 stdout으로 응답을 받는 양방향 통신 채널을 구성한다.
이후 가장 먼저 수행해야 하는 단계는 initialize 요청이다.
이는 단순한 초기화가 아니라 클라이언트 정보를 전달하고 세션을 생성하는 일종의 핸드셰이크 과정으로, 이후의 모든 API 호출을 가능하게 하는 필수 절차다.
요청에는 클라이언트의 이름, 버전 등의 메타 정보와 함께 experimentalApi와 같은 capability 옵션을 포함할 수 있는데, 이 값을 활성화해야 일부 내부 API(예: rate limit 조회)가 정상적으로 동작한다.
child.stdin.write(
`${JSON.stringify({ id: 2, method: "account/rateLimits/read" })}\n`,
);
Copy초기화가 완료되면, 실제로 한도를 조회하기 위해
account/rateLimits/read 메서드를 호출한다.이 호출 역시 JSON 문자열 형태로 stdin에 전달되며, Codex 서버는 이를 해석한 뒤 결과를 stdout으로 반환한다.
응답은 요청 시 사용한 id 값을 기준으로 매칭되므로, 여러 요청을 동시에 처리하는 경우에도 각 응답을 구분할 수 있다.
결과적으로 이 방식은 HTTP API를 호출하는 것이 아니라, Codex 프로세스를 로컬 RPC 서버처럼 활용하여 내부 메서드를 직접 호출하는 구조이며, 모든 통신은 한 줄 단위의 JSON 메시지를 통해 이루어진다는 점이 핵심이라는 것이다.
3. 메뉴바 앱 구현
메뉴바를 구현하는건 스위프트 코드로 구현한다.
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
statusItem.isVisible = true
configureStatusButton(title: "..(..)")
Copy해당 코드는 macOS 메뉴바에 상주하는 상태 아이템을 생성하고, Codex에서 조회한 rate limit 정보를 간단한 문자열 형태로 표시하는 역할을 한다.
먼저
NSStatusBar.system.statusItem를 통해 가변 길이를 가진 메뉴바 아이템을 생성하면, 표시할 문자열 길이에 따라 자동으로 너비가 조정되는 상태 버튼이 만들어진다.이후
statusItem.isVisible = true로 활성화하고, 초기 상태에서는 "..(..)" 같은 플레이스홀더를 표시해 아직 데이터가 로드되지 않았음을 나타낸다.let primaryTitle = primaryRemaining.map(String.init) ?? "-"
let secondaryTitle = secondaryRemaining.map(String.init) ?? "-"
configureStatusButton(title: "\(primaryTitle)(\(secondaryTitle))")Copy실제 값이 들어오는 시점에는 남은 한도를 문자열로 변환하여 메뉴바에 렌더링한다.
primaryRemaining과 secondaryRemaining은 각각 서로 다른 시간 기준의 rate limit 잔여 비율을 의미하며, 값이 존재할 경우 숫자로 변환하고, 값이 없으면 "-"로 대체한다.이 처리를 통해 데이터가 아직 없거나 조회 실패 시에도 UI가 깨지지 않도록 방어하고 있다.
최종적으로 "
\(primaryTitle)(\(secondaryTitle))" 형태로 문자열을 구성해 configureStatusButton에 전달하면, 메뉴바에는 예를 들어 95(82)와 같은 형태로 표시된다.여기서 앞의 숫자는 단기(5시간 기준) 사용량 대비 남은 비율을 의미하고,
괄호 안의 숫자는 장기(7일 기준) 사용량 대비 남은 비율을 나타낸다.
즉, 사용자는 메뉴바의 짧은 텍스트만으로도 현재 단기/장기 한도 상태를 동시에 파악할 수 있게 된다는 것이다.
정리하면, 이 코드는 Codex에서 가져온 두 가지 시간 축의 rate limit 정보를 최소한의 UI로 압축해 표시하는 메뉴바 렌더링 로직이며, 가독성과 상태 전달에 초점을 둔 단순하지만 실용적인 구현이라고 볼 수 있다.
4. 초기 실행
이렇게 만든 것을 언제든 재사용할 수 있게 리패킹하는것이 필요해 절대경로는 모두 상대경로로 변경하고, 앱이 켜질때 최초로 설정화면이 뜨도록 설정했다.
struct AppSettings: Codable {
let refreshIntervalSeconds: Int
let targetLimitId: String
let showStatusWindowOnLaunch: Bool
}
enum SettingsStore {
static let fileURL = directoryURL.appendingPathComponent("settings.json")
static func load() -> AppSettings? {
guard let data = try? Data(contentsOf: fileURL) else { return nil }
return try? JSONDecoder().decode(AppSettings.self, from: data)
}
}
Copy이런 구조로 기존에 하드코딩으로 설정되어있던 코드내용을 사용자 설정 파일로 기록하도록 구현했다.
최초 실행시 설정화면5. 구동 화면
이렇게 구현한 프로그램을 sh 명령어로 실행해주면 아래와 같이 언제든 메뉴바에서 코덱스의 남은 용량을 조회할 수 있다.
메뉴바 코덱스 클릭시 상세정보가 보인다.기본적으론 우선 사용한도(5시간짜리)가 표기되며,
이후 괄호안에 세컨더리 사용한도(7일이상)가 표기된다.
처음에는 이 값을 1분에 한번씩 갱신하게 했는데, 설정을 분리하면서 이 값을 초기설정에서 300초(5분)으로 바꿔두었다.
그리고 해당 메뉴바에서 Show Window를 클릭하면...
코덱스 리미트 상세정보창이렇게 JSON으로 빼올 수 있는 모든 값과 더불어 내 설정값도 표기되게 해놓았다.
6. 마무리
생각보다 맥북앱도 앱킷을 이용해 자체 개발하기 편하다는걸 이번에 깨달았다.
스위프트 언어를 배워야 한다 생각했지만 이제 언어의 장벽은 AI로 인해 없는거나 마찬가지임을 새삼 깨닫기도한다.
(물론 이해도의 차이는 매우 다르지만요...)
글의 서두에서도 말했듯이 요즘은 참 개발의 판도가 많이 변했다.
누구나 개발을 할 수 있지만 누구나 같은 결과물을 내진 못한다.
실력이 있는 개발자들도, 노련한, 숙련한 개발자들도 AI를 환영하는 추세다.
왜냐하면 프로그램이란거는 어떤 구조물을 기획하고 어떻게 사용할것이며,
그 구조물을 사용하면서 발생할수있는 문제, 확장성등은 경험에 의거해서 만들어지기때문이다.
코덱스 최고!지금상황에서 코덱스같은 코딩 에이전트는 개발자의 훌륭한 무기라고 볼 수 있다.
그렇다, 더 강하게 보이기위한 그런 무기 말이다.
#NodeJs #Swift #AppKit #zsh
0
개의 댓글