이 글에는 제휴 링크가 없고 글에 나오는 제품을 만든 곳들(Electron 프로젝트·마이크로소프트·구글·앤트로픽)과 아무 관계가 없습니다. 제가 직접 만든 중계기의 개발 기록을 바탕으로 썼습니다. 삽화는 GPT 이미지 생성, 도식은 직접 제작했습니다.
무거운 껍데기를 걷어내다 — 대형 SUV에서 경차로 (LLM 중계기 개발기)
혼자 동네 마트에 가는데 7인승 대형 SUV를 몰고 간다고 해 볼게요. 장바구니 하나 실을 자리면 충분한데, 차는 매번 빈 좌석 여섯 개와 넓은 짐칸까지 통째로 끌고 다닙니다. 기름도 그만큼 먹고, 주차 칸도 그만큼 차지하고요. 차가 나쁜 건 아니에요. 하는 일에 비해 너무 클 뿐이죠.
제 LLM 중계기의 겉껍데기가 꼭 그랬습니다. 중계기는 화면 구석에 작게 떠 있는 앱인데, 그 껍데기 안에 웹 브라우저 한 벌을 통째로 싣고 다녔어요. 재 보니 앱이 쓰는 메모리 329MB 가운데 274MB가 껍데기 몫이었습니다.
이 글은 LLM 중계기 개발기의 25편이자, 5부 ‘만들고 나서가 더 어렵다 — 설치·업데이트’의 마지막 편입니다. 지난 24편 「조용한 업데이트는 멈춘 줄 안다」에서는 진행 창 없이 돌던 업데이트가 멈춘 것처럼 보여서 진행률 막대를 붙인 이야기를 했어요. 오늘은 20편과 21편에서 “25편에서 다룹니다”라고 미뤄 둔 이야기, 8월에 앱의 겉껍데기를 통째로 바꾼 일입니다.

결론 요약
- 측정 — 앱 전체는 프로세스 4개에 329MB를 썼는데, 중계기 서버 혼자서는 55MB였어요. 나머지 274MB(서버의 약 6배)가 브라우저를 품은 껍데기 몫이었고, 그 껍데기가 하던 일은 네 가지뿐이었어요
- 해결 — 서버는 다시 짜지 않고 껍데기 속 브라우저만 뺐어요. 전용 창은 이미 깔린 브라우저의 앱 모드를 빌려 61MB(81% 감소)가 됐고, 이틀 뒤에는 작은 트레이 프로그램으로 바꿔 Electron을 완전히 걷어냈어요. 윈도용 압축 파일은 112MB에서 41.6MB로 줄었고요
- 교훈 — 자르기 전에 잰다. 탓하기 쉬운 곳이 아니라 무거운 곳을 자른다. 끄는 방식과 검사 같은 약속은 그대로 옮긴다
🚙 1. Electron — 브라우저 한 벌을 싣고 다니는 앱 껍데기
중계기의 관리 화면은 처음부터 웹 페이지였어요. 웹 페이지를 만드는 기술(HTML과 자바스크립트)로 화면을 짰고, 중계기 서버가 내 컴퓨터 안에서 그 페이지를 보여 줍니다.
그런데 웹 페이지를 ‘앱’처럼 쓰려면 껍데기가 필요해요. 아이콘을 눌러 켜고, 화면 구석에 작은 아이콘이 떠 있고, 주소창 없는 창이 열리는 것 말이에요. 7월에 첫 버전을 만들 때 고른 껍데기가 Electron(일렉트론)이었습니다.
Electron은 웹 기술로 만든 화면을 데스크톱 앱으로 포장해 주는 도구예요. 널리 쓰이는 방식인데, 비결이 하나 있어요. 앱마다 자기 전용 웹 브라우저를 한 벌씩 싣고 다닌다는 거예요. 크롬·엣지와 뿌리가 같은 브라우저 엔진(Chromium, 크로미엄)이 앱 안에 통째로 들어 있어서, 어느 컴퓨터에서 켜도 화면이 똑같이 그려집니다.
편한 대신 무거워요.
- 내려받는 파일이 커진다 — 중계기 개발 폴더에서 Electron이 깔린 자리만 242MB였어요. 압축하기 전 크기지만, 그만큼이 배포판에 함께 실립니다
- 켜 두는 동안 메모리를 많이 쓴다 — 브라우저처럼 여러 프로세스로 나뉘어 돌아요. 6편에서 실행 중인 프로그램 하나를 프로세스라고 했죠. 중계기 앱은 Electron의 도우미 프로세스까지 합쳐 4개였어요. 21편에서 “화면을 그리려고 함께 띄운 도우미 프로세스”라고 한 것이 이것들이에요

📏 2. 먼저 재 봤다 — 329MB 중 274MB가 껍데기
계기는 함께 쓰던 사람의 한마디였어요. “네이티브가 성능이 좋다.” 네이티브(native) 앱은 브라우저를 빌리지 않고 운영체제의 기본 부품으로 직접 만든 앱을 말해요. 8월 6일 새벽의 저장 기록(커밋)과 그날 쓴 계획서에는 이 말을 그대로 따르지 않고 실측으로 검증했다고 적혀 있습니다.
- Electron 앱 전체 (프로세스 4개) — 329MB
- 중계기 서버만 따로 켰을 때 — 55MB
- → 껍데기만의 몫 — 274MB, 서버의 약 6배
- 윈도용 배포 압축 파일 (v0.1.31) — 112MB, 그중 상당 부분이 Chromium
여기서 메모리는 프로그램이 켜져 있는 동안 실제로 차지하고 있는 크기예요.
그다음 그 274MB가 무슨 일을 하는지 봤어요. 껍데기 코드는 302줄이었고, 하는 일은 네 가지뿐이었습니다.
① 관리 화면을 가리키는 창 하나 ② 화면 구석의 트레이 아이콘 ③ 중계기가 두 번 켜지지 않게 막는 잠금 ④ 지난번에 남은 좀비를 치우고 문 번호(포트)를 되찾는 일 (21편)
창 하나와 아이콘 하나를 띄우려고 브라우저 한 벌을 싣고 다닌 셈이에요. 장바구니 하나 사러 7인승을 끌고 나간 것처럼요.

반대로 서버는 병목이 아니었어요. 55MB에 CPU 사용률 0.2%였거든요. 그래서 기록은 선을 분명히 그었습니다. 걷어낼 것은 껍데기이고, 서버는 다시 짜지 않는다. 이유도 함께 적혀 있어요.
- 서버는 무겁지 않다 — 1만 줄이 넘는 서버를 다른 언어로 다시 짜도 얻을 게 없다
- 다시 짜면 잃는 게 있다 — Claude를 붙이는 공식 부품(Claude Agent SDK)은 자바스크립트용이라, 서버를 다른 언어로 옮기면 Claude 연결을 통째로 잃는다
- 진짜 무거운 건 따로 있다 — 가장 무거운 건 서버가 띄우는 AI 프로그램들이다. Claude 쪽 실행 파일만 230MB이고 Codex 앱 서버는 켜 둔 채 쓴다(6편). 이건 어떤 언어, 어떤 껍데기로 가도 그대로다
“네이티브가 낫다”는 말은 반만 맞았던 거예요. 껍데기에 대해서는 맞았고, 서버에 대해서는 아니었죠.
🪟 3. 브라우저는 빌려 쓴다 — ‘앱 모드’ 창
남은 문제는 ‘전용 창’이었어요. Electron을 빼면 창을 그려 줄 브라우저가 없어집니다. 그런데 컴퓨터에는 이미 브라우저가 깔려 있죠. 그래서 그걸 빌리기로 했어요.
Edge나 Chrome 같은 브라우저에는 앱 모드라는 켜는 방법이 있어요. 브라우저를 부를 때 --app= 뒤에 주소를 붙이면, 주소창도 탭도 없는 독립된 창이 하나 열려요. 쓰는 사람 눈에는 데스크톱 앱 창과 다르지 않은데, 중계기가 싣고 다니는 브라우저는 0바이트가 됩니다. 집을 지고 다니는 대신, 이미 서 있는 집의 창문 하나를 여는 셈이에요.

같은 날 새벽에 새 시작 프로그램(런처)이 생겼어요. 하는 일은 세 단계예요.
① 문 번호를 확보한다 — 좀비 치우기와 빈 번호 찾기를 Electron 없이 그대로 옮겼어요. 이미 켜진 정상 중계기는 치우지 않고 그 화면을 쓰게 하고, 낯선 프로그램은 절대 끄지 않는다는 규칙도 그대로예요 ② 중계기 서버를 켠다 ③ 앱 모드로 전용 창을 연다
창을 여는 데는 순서를 정해 두었어요. ‘아무것도 안 열린 채 이유도 모르는’ 상태를 남기지 않으려고요.
- 먼저 앱 모드를 지원하는 브라우저(Edge, Chrome 같은)를 찾아 앱 모드 창으로 연다
- 그런 브라우저가 없으면 평소 쓰는 기본 브라우저의 탭으로 연다. Firefox나 Safari에는 앱 모드가 없어서 이쪽으로 온다
- 그것도 안 되면 주소를 알려 주고 직접 열어 달라고 안내한다
일부러 하지 않은 일도 하나 있어요. 브라우저에는 설정과 방문 기록을 담는 프로필이라는 폴더가 있는데, 전용 창에 별도 프로필을 주면 깔끔하게 분리되긴 해요. 하지만 그러면 브라우저가 통째로 하나 더 떠요. 기록은 이걸 “가벼우려고 Electron을 걷어냈는데 같은 비용을 다시 내는 꼴”이라고 적었습니다. 그래서 프로필을 따로 주지 않고, 이미 켜져 있는 브라우저에 창만 하나 붙게 했어요.
결과는 저장 기록에 이렇게 남았어요. 런처 61MB, Electron 329MB, 81% 감소. 기록은 이 길을 ‘서버 하나 + 브라우저 창 하나’라고 적었어요. 창은 이미 켜져 있던 브라우저에 붙기 때문에, 창이 쓰는 몫은 런처가 아니라 브라우저 쪽에 잡힌다는 뜻이에요. 쓰는 사람에게 진짜로 줄어든 건 ‘브라우저 한 벌을 하나 더 띄우던 비용’입니다.
⚖️ 4. 빌려 쓰는 값 — 컴퓨터마다 달라지는 것들
남의 집 창문을 빌리면 그 집 사정을 따라야 해요. 브라우저를 빌린 대가도 있었습니다.
① 어떤 브라우저가 깔려 있는지 컴퓨터마다 다르다 윈도에는 대개 Edge가 들어 있어서 첫 후보에서 거의 열리지만, 다른 컴퓨터는 사정이 제각각이에요. 3절의 순서(앱 모드 → 기본 브라우저 탭 → 주소 안내)가 그래서 필요했어요. Electron 시절에는 앱에 실린 같은 브라우저 엔진이 화면을 그렸는데, 이제는 그 컴퓨터에 깔린 브라우저가 그려 줍니다.
② 브라우저의 버릇을 따라야 한다 런처를 저장하고 7분 뒤의 저장 기록에는 맥에서 확인하다 찾은 고장이 적혀 있어요. 20편 끝에서 잠깐 말한 그 일이에요. 브라우저가 이미 켜져 있으면, 맥의 ‘앱 열기’ 명령은 “앱 모드로 열어 달라”는 부탁을 조용히 버리고 브라우저만 앞으로 가져왔어요. 런처는 “전용 창을 열었다”고 기록했지만 실제 창은 어디에도 없었죠. 기록은 이걸 성공을 위장하는 실패라고 불렀어요.
브라우저 앱 안에 든 실행 파일을 직접 부르게 바꾸자, 이미 켜진 브라우저가 부탁을 넘겨받아 창을 열었어요. 브라우저를 새로 하나 더 띄우는 방법은 쓰지 않았어요. 이번에도 가벼워지려던 의미가 사라지니까요.
③ 창이 닫혀도 런처는 모른다 창은 브라우저의 것이라, 사용자가 창을 닫아도 런처에게는 소식이 오지 않아요. Electron에서는 ‘창이 모두 닫힘’을 알아채 정리 담당을 불렀는데(21편), 그 손잡이가 사라진 거예요. 창만 닫고 AI 프로그램은 남기는 일은 기록에 따르면 “이 저장소가 반복해서 겪은 좀비 사고의 재발 경로”였어요.
그래서 런처가 생기고 20분 남짓 뒤, 관리 화면 위쪽에 ⏻ 종료 버튼을 달고 중계기에 ‘종료 창구’를 따로 만들었어요. 응답부터 보내고 정리하는 순서와 11초 상한은 21편 5절에서 이야기했어요.
④ Electron이 잠가 둔 열쇠 Electron에는 운영체제의 금고를 빌려 내용을 잠가 주는 기능이 있어요. 중계기는 사용자가 넣어 둔 API 키 같은 열쇠를 이 기능으로 잠가 두었고요. Electron을 빼면 그 금고를 여는 손잡이도 함께 사라집니다.
조사해 보니 계획서에 적은 것보다 위험이 컸어요. 예전 코드는 열쇠를 열지 못하면 아무 말 없이 ‘빈 금고’를 돌려줬거든요. 사용자 눈에는 열쇠가 사라진 것처럼 보이고, 다시 입력하면 그 저장이 원래 파일을 덮어써서 정말로 영영 사라지는 길이었어요. 기록은 이걸 “조용한 데이터 손실 경로”라고 적었어요. 그날 새벽부터 이렇게 막았습니다.
- 열지 못하면 조용히 비우지 않고, 왜 잠겼는지 화면 위 알림 띠(배너)로 알렸어요
- 잠긴 파일을 덮어쓰기 전에 원본을 따로 보관했어요
- Electron판이 켜질 때 한 번, 열쇠를 Electron 없이도 열 수 있는 방식으로 옮겨 담았어요(v0.1.49부터)
- 읽을 수 없는 환경이면 아무것도 건드리지 않았어요. 못 읽는 파일을 건드리는 순간 그게 소실이니까요
공짜는 아니었어요. 새로 저장하는 열쇠는 운영체제의 금고 대신 중계기가 직접 잠그게 됐고, 그만큼 운영체제가 해 주던 보호 한 겹을 잃었어요. 기록은 이걸 Electron을 없애는 이상 피할 수 없는 교환이라며 ‘의도된 선택’으로 문서에 남겼고, 지금도 새 열쇠는 이 방식으로 저장돼요.
🧰 5. 이틀 뒤, 껍데기를 통째로 바꾸다 — 작은 트레이 프로그램
계획서에는 네 단계가 적혀 있었어요.
① Electron 없는 런처를 Electron 옆에 나란히 둔다 (8월 6일 새벽 완료) ② 종료 창구를 만들고, 잠긴 열쇠를 옮겨 담는다 (같은 날 새벽 완료) ③ 배포판 포장 방식을 바꾼다 — 목표는 112MB에서 50MB 이하 ④ Electron을 지운다 — 단, 검사는 지우지 않고 검사할 대상만 옮긴다
③과 ④는 8월 8일 새벽 한 번의 저장에 함께 들어갔어요. 그런데 이때 바뀐 모양은 계획서의 ‘앱 모드 창’보다 한 걸음 더 나갔습니다. 전용 창마저 내려놓았거든요.
- 트레이 프로그램 — 윈도에서는 윈도에 들어 있는 .NET Framework 위에서 도는 작은 트레이 프로그램이 중계기를 켜는 실행 파일이 됐어요. 윈도에 기본으로 딸린 C# 번역기(컴파일러)로 만듭니다. 세 시간 뒤 나온 v0.1.57에는 맥판(맥에 기본으로 들어 있는 스크립트 기능으로 만든 상태 막대 아이콘)과 리눅스판(파이썬으로 만든 트레이 아이콘)도 함께 나왔어요
- 서버는 그대로 — 트레이가 공식 Node.js 실행 파일로 중계기 서버를 켜고, 끝까지 책임져요. 압축 파일 안에는 트레이 프로그램, Node.js, 서버와 화면 파일만 있고 브라우저도, AI 프로그램 실행 파일도 들어 있지 않아요
- 관리 화면은 필요할 때만 — 처음 켜도 창이 뜨지 않고 트레이에 조용히 자리 잡아요. 트레이 메뉴에서 ‘설정 열기’를 누르면 평소 쓰는 브라우저의 탭으로 관리 화면이 열려요. 문서에는 “관리 화면은 평범한 웹 페이지라 열어도, 닫아도, 아예 열지 않아도 중계에는 영향이 없다”고 적혀 있어요
껍데기가 하던 네 가지 일은 이렇게 자리를 옮겼어요.
① 관리 화면 창 → 필요할 때만 여는 기본 브라우저 탭 ② 트레이 아이콘 → 운영체제마다 따로 만든 작은 트레이 프로그램 ③ 두 번 켜지지 않게 잠금 → 트레이가 직접 막고, 두 번째로 실행하면 이미 켜진 트레이에게 설정 화면을 열어 달라고 부탁 ④ 좀비 치우기 → 런처에 옮겨 둔 그대로
트레이 프로그램의 코드는 첫날 889줄이었어요. 업데이트를 맡는 도우미까지 들어 있어서 Electron 껍데기(302줄)보다 오히려 길어요. 23·24편의 자동 업데이트도 이 트레이가 맡은 일이에요. 그래도 이 889줄은 브라우저를 싣고 다니지 않아요. 가벼움은 코드 줄 수가 아니라 싣고 다니는 짐에서 온다는 걸 보여 준 셈이에요.
대신 내려놓은 편리함도 있어요. Electron은 하나의 코드로 세 운영체제를 덮었지만, 이제는 트레이 프로그램을 운영체제마다 따로 만들어야 했어요. 기록에도 맥판과 리눅스판은 윈도에서 묶고 구조만 검증했으니, 그 운영체제에서 실제로 켜 보는 확인이 아직 필요하다고 적혀 있어요.
쓰는 사람에게 달라진 점은 이래요.
- 내려받는 크기 — 윈도용 압축 파일이 112MB(v0.1.31, Electron판)에서 41.6MB(v0.1.57, 41,614,257바이트)로 약 63% 줄었어요. 20편에서 첫날 밤 180MB가 113MB로 줄었다고 했는데, 거기서 한 번 더 줄어든 거예요. v0.1.63부터는 40MB 남짓을 유지했어요
- 켜 두는 동안의 메모리 — 윈도에서 잰 값으로, 쉬고 있는 서버(50.1MiB) 옆에서 트레이 프로그램은 6.4MiB까지 내려갔어요. 둘을 합쳐 56.5MiB예요(MiB는 MB와 거의 같은 크기 단위예요)
- 끄는 법 — 브라우저 탭을 닫아도 중계기는 트레이에서 계속 일해요. 끌 때는 트레이 메뉴의 ‘종료’나 관리 화면의 ⏻ 버튼을 써요(21편)
- 켜는 시점 — 로그인할 때 자동으로 시작하는 기능은 기본으로 꺼져 있고, 원하는 사람만 트레이 메뉴나 설정 화면에서 켜요
- AI 프로그램도 필요할 때만 — 같은 v0.1.57부터 AI 프로그램은 실제 요청이 올 때 켜고, 5분 동안 일이 없으면 내려놓아요. 기다리는 시간은 1분·15분으로 바꾸거나 아예 끌 수도 있어요
📐 6. 남긴 규칙 — 재고, 무거운 곳을 자르고, 약속은 그대로
① 자르기 전에 잰다 “네이티브가 낫다”는 말을 그대로 따랐다면 서버까지 다시 짜는 쪽으로 갈 수도 있었어요. 숫자 네 개(329·55·274·112)가 어디를 잘라야 하는지 먼저 알려 줬어요.
② 탓하기 쉬운 곳이 아니라 무거운 곳을 자른다 1만 줄이 넘는 서버는 가장 큰 덩어리라 탓하기 쉬웠어요. 하지만 잰 숫자로는 302줄짜리 껍데기와 그 안에 실린 브라우저가 무거웠어요. 그래서 서버는 남기고 껍데기 속 브라우저만 뺐어요. 껍데기를 바꿔도 그대로인 AI 프로그램들에는 다른 방법이 쓰였고요. 5절에서 본 것처럼 필요할 때만 켜고, 쉬면 내려놓는 방식이에요.
③ 약속은 그대로 옮긴다 껍데기는 바뀌어도 바깥에서 보는 약속은 같아야 해요.
- 끄는 약속 — 첫 런처부터 ‘꺼라’ 신호를 받으면 같은 정리 담당을 부르고, 정리가 매달리면 일정 시간 뒤 강제로 끝냈어요. 저장 기록은 이걸 “Electron판과 동일한 종료 계약”이라고 불렀어요
- 종료 경로 검사(21편) — 지우지 않고 새 트레이 프로그램과 런처를 보도록 다시 썼어요. 종료 창구, 11초 상한, 트레이가 서버를 나무째 끄는 마지막 수단이 실제로 연결돼 있는지 확인해요. Electron 코드를 지우기 전까지 Electron판과 런처를 나란히 둔 것도 이 검사가 Electron 코드를 보고 있었기 때문이에요
- 첫날의 재현 시험(20편) — 윈도의
D:를 주소로 착각한 고장을 재현하던 시험은 지키는 대상을 Electron 시작 파일에서 새 시작 파일(런처)로 옮겼어요. 런처도 서버를 부를 때 파일 위치를 주소 모양으로 바꿔서 건네요 - 새로 생긴 검사 — 윈도용 배포판에 Electron이나 Chromium이 들어 있으면 실패하고, 압축 파일이 60MiB를 넘어도 실패해요. 다시 무거워지지 않게 문 앞에 체중계를 둔 셈이에요
정리
① 앱 전체는 329MB를 썼는데 중계기 서버는 55MB였어요. 나머지 274MB는 브라우저 한 벌을 품은 Electron 껍데기 몫이었고, 그 껍데기가 하던 일은 창 하나·트레이·두 번 켜기 잠금·좀비 치우기, 네 가지뿐이었어요 ② 서버는 다시 짜지 않고 브라우저만 걷어냈어요. 이미 깔린 브라우저의 앱 모드를 빌려 61MB가 됐고, 브라우저가 컴퓨터마다 다르다는 것·맥의 버릇·창 닫힘을 모른다는 것·잠긴 열쇠라는 값을 치렀어요. 이틀 뒤에는 작은 트레이 프로그램으로 바꿔 윈도용 압축 파일이 112MB에서 41.6MB로 줄었어요 ③ 자르기 전에 재고, 무거운 곳을 자르고, 끄는 약속과 검사는 그대로 옮겼어요
이렇게 5부 ‘만들고 나서가 더 어렵다’를 마칩니다. 다른 컴퓨터에서 켜고, 끄고, 준비 상태를 알리고, 새 판으로 바꾸고, 가볍게 만드는 일까지, 만든 다음의 일이 만드는 일만큼 많았어요.
다음 26편은 「한 질문, 여러 AI의 답」이에요. 6부 ‘중계기 위에서, 그리고 회고’를 시작하며, 같은 질문을 여러 AI에게 한꺼번에 던지는 이야기를 할게요.
2026년 10월 1일 기준입니다. 제가 만든 중계기의 개발 기록(저장 기록의 시각과 메모, 계획서와 설계 문서, 배포 기록)을 바탕으로 썼고, 메모리와 파일 크기는 그 기록에 남은 실측값입니다. 특정 기업·서비스의 공식 입장이 아닙니다.