완제품을 버리고 공개 소스로 다시 빌드한 사내 워크스페이스
350명이 쓰던 노션을 대체하려다 완제품 무료판의 좌석 잠금과 유료판의 개조 금지 약관에 동시에 막혀, 공개 AGPL 소스를 포크해 직접 빌드하는 경로로 갈아탄 프로젝트
Problem
350명 기준 연 5,800만~1억 2천만 원의 노션 구독료와 벤더 서버에 있는 데이터를 대체하려 오픈소스를 실사했는데, 요건을 채우는 유일한 후보의 완제품 무료판에는 고지되지 않은 좌석 잠금(정식 1명·게스트 3명)이 있었고 유료판 약관은 수정과 리브랜딩을 금지했다
Hypothesis
좌석 잠금 장치는 비공개 코드가 섞인 완제품 빌드에만 있고 공개 설계도에는 없다고 보았다. 공개 저장소 전체에서 max_users가 점검 스크립트 한 건뿐이라는 코드 검색이 근거였고, AGPL이 포크·수정·자체 빌드를 허용하므로 후보를 바꾸는 대신 후보에 접근하는 경로를 바꾸기로 했다
Solution
전부 소스로 빌드한다는 목표를 내리고 좌석 잠금이 있는 서버 본체만 필수로, 같은 모노레포라 추가 비용이 없는 워커·관리자 콘솔은 권장으로, 좌석과 무관한 인증·검색은 업스트림 유지로 계층화했다. 상표는 바꾸되 저작권 고지와 내부 식별자는 건드리지 않는다는 절대 규칙을 먼저 세우고 계정 6개로 좌석 제한 부재를 실증했다
Result
상용 이미지 의존 3개를 0개로 내리고 좌석 오류 0건을 실측했으며, 사용자 화면의 원본 제품명을 로케일 33종에서 없앴다. 프로덕션이 가동됐고 운영비도 월 약 47만 8천 원에서 약 22만 8천 원으로 줄었다. 다만 350명 부하 테스트는 실시하지 않았고 확산도 보류 상태라, 구독료 절감은 아직 실현된 절감이 아니라 설계된 절감이다
Lesson Learned
목표는 선언만으로 존재할 수 있다. 스파이크를 돌리기 전까지 완제품을 쓰지 않는다는 전제는 이미 지켜지고 있는 것으로 다뤄졌지만 실제 스택은 상용 이미지 세 개 위에 있었다. 그 뒤로 계획 문서의 현재 상태 칸을 문장이 아니라 실측 명령의 출력으로 채운다
문제 정의
구독료보다 먼저 걸린 것은 좌석 잠금이었다
회사는 사내 협업 도구로 노션을 쓰고 있었다. 350명 기준 구독료는 인당 월 10~20달러 환산으로 연 5,800만 원에서 1억 2천만 원 사이였고, 문서와 데이터베이스와 첨부파일이 전부 벤더 서버에 있었으며, 화면과 기능을 회사 요구에 맞게 고칠 수 없었다. 비용만으로도 대체를 검토할 이유는 충분했다. 그런데 실제로 걸린 것은 그다음이었다.
2026년 7월, 데이터 주권·커스텀 자유·라이선스·비용·AI 연결이라는 다섯 기준으로 오픈소스 대안을 제로베이스에서 다시 실사했다. 노션식 데이터베이스(표·칸반·캘린더)와 AI를 함께 갖춘 후보는 AppFlowy 하나뿐이었다. 그런데 개발사가 미리 조립해 배포하는 완제품 도커 이미지를 그대로 받아쓰는 쉬운 길에 문이 두 개 잠겨 있었다. 무료판은 라이선스 키가 없으면 정식 사용자 1명에 게스트 3명으로 제한된다. 이 제한은 2025년 하반기에 들어왔는데 공식 문서에는 고지가 없었고, 저장소 이슈 두 건의 로그 원문에서 max_users: 1, max_guests: 3을 직접 찾아야 확인됐다. 유료판은 셀프호스트 약관이 수정과 리브랜딩을 명시적으로 금지했고, 가격은 영업 문의였다.
350명 조직에 정식 사용자 1명은 사실상 사용 불가이고, 개조 금지 약관은 "우리 디자인과 기능으로 개발한다"는 목표와 정면으로 충돌한다. 완제품 경로는 무료판이든 유료판이든 이 프로젝트의 목적을 통과시키지 못했다. 노션을 떠나려는 이유가 벤더 종속인데, 대체품에서 다시 벤더의 약관에 묶이는 셈이었다.
제약도 분명했다. 구독료를 0으로 만드는 대신 서버비와 운영 인력 시간을 치러야 했고, 그 치환을 조직이 승인해야 착수할 수 있었다. 라이선스 판단이 틀리면 비용 문제가 아니라 회사의 법적 리스크가 된다. 그리고 원본은 2~3주마다 업데이트되므로, 포크해 온 뒤에도 변경분을 우리 수정본에 합치는 일이 상시로 남는다. 이것들은 프로젝트가 끝나면 사라지는 비용이 아니라 계속 내는 비용이었다.
정식 1 + 게스트 3
해당 없음
0원
제한 없음
약관이 금지
비공개
잠금 로직 부재
라이선스가 허용
서버비 + 운영 시간
가설 수립
후보를 바꾸는 대신 경로를 바꿨다
물론 다른 오픈소스로 갈아타는 선택지도 있었다. 같은 다섯 기준으로 재검토했을 때 정공법으로 우회 없이 요건을 채운 유일한 대안은 Docmost였고, 운영도 더 단순했다. 그런데 노션식 데이터베이스가 아예 없었다. AFFiNE은 3인 제한에 리브랜딩이 유료라 요건과 정면으로 부딪혔고, Outline은 위키 완성도가 최고 수준이지만 자체 AI가 없었으며, Anytype은 비상업 라이선스라 탈락했다. 우리가 옮겨야 할 노션 문서 상당수가 데이터베이스 위에 얹혀 있었으므로, 데이터베이스를 포기하는 선택은 이관 자체를 실패로 만든다. 그래서 후보를 바꾸는 대신 후보에 접근하는 경로를 바꾸기로 했다.
가설은 이것이었다. 좌석 잠금 장치는 비공개 코드가 섞인 완제품 빌드에만 들어 있고, 공개된 설계도에는 없다. 근거는 추정이 아니라 코드 검색이었다. 공개 저장소 전체에서 max_users는 단 한 건 나왔고 그것도 점검 스크립트였다. AGPL은 포크와 수정과 자체 빌드를 명시적으로 허용하고, 350명 전원이 우리 직원인 사내 사용에는 소스를 대중에 공개할 의무가 붙지 않는다. 가장 보수적으로 해석해도 사내 저장소 링크 하나를 제공하는 선에서 충족된다.
이 가설이 맞으면 두 가지가 따라온다. 인원이 늘어도 라이선스 키를 요구받지 않고, 상용 약관의 개조 금지가 적용되지 않으므로 이름·로고·색·기능을 소스 레벨에서 바꿔 우리 제품으로 만들 수 있다. 목표 지표는 단순하게 잡았다. 수용 인원의 상한을 없애고, 상용 이미지 의존을 0으로 내리고, 사용자 화면에서 원본 제품명을 0건으로 만든다. 구독료가 사라진 자리에는 서버비와 운영 시간이 들어온다. 이 프로젝트는 비용을 없애는 프로젝트가 아니라 비용의 종류를 바꾸는 프로젝트라는 것을 검토 단계에서 명시했다.
AI 자체 키
데이터베이스 없음
리브랜딩 유료
자체 AI 없음
솔루션 도출
전부 소스로 빌드하는 대신, 잠금이 있는 곳만 필수로 걸었다
착수하고 선행 스파이크를 돌리자 계획의 전제 하나가 먼저 깨졌다. 당시 돌고 있던 스택이 이미 비공개 상용 이미지 세 개에 의존하고 있었다. 다시 말해 "완제품을 쓰지 않는다"는 목표는 선언으로만 존재했고, 실제 상태는 정반대였다. 이 발견은 프로젝트의 취지를 약화시키는 대신 착수 근거를 강화했지만, 무엇을 걷어내야 하는지의 목록을 통째로 바꿨다.
여기서 내린 핵심 판단은 "전부 소스로 빌드한다"를 목표에서 내린 것이다. 좌석 잠금이 들어 있는 곳은 서버 본체 하나였고, 그것만 필수였다. 워커와 관리자 콘솔은 같은 모노레포 산출물이라 포크 하나에서 도커 타깃만 분기하면 되므로 추가 비용이 사실상 0이었다. 반면 인증 서버와 검색 서버는 좌석 잠금과 무관한 보조 서비스여서, 업스트림 이미지를 digest로 고정해 남겨도 목표 미달이 아니었다. 이 계층을 세우지 않고 순수주의로 밀었다면 인증 스키마를 소유한 서비스를 건드리다 전원 로그인 불능을 만들 수 있었다. 걷어내야 할 것과 걷어내면 멋있는 것을 구분하는 일이 이 단계에서 가장 중요한 판단이었다.
유일한 필수 소스 빌드
도커 타깃만 분기
digest 로 고정해 유지
리브랜딩에는 절대 규칙 세 개를 먼저 세워 두고 착수했다. 화면에 보이는 상표는 바꾸되 소스 안의 저작권 고지와 라이선스 원문은 절대 지우지 않는다. 오히려 수정 고지를 추가해야 한다. 환경변수 이름이나 딥링크 스킴 같은 내부 식별자는 서비스 사이의 계약이므로 사용자에게 안 보이는 배관으로 취급해 유지한다. 그리고 리버스 프록시에서 제품명을 통짜로 치환하지 않는다. 치환 필터가 대소문자를 무시하기 때문에 설정 문자열까지 함께 바뀌어 앱이 부팅에 실패한다. 이 세 줄이 없었다면 브랜드를 지우는 작업이 라이선스 위반이 되거나 앱을 죽였을 것이다.
서버를 회사 클라우드로 옮길 때도 같은 종류의 판단이 한 번 더 필요했다. 사내 인프라 표준 저장소는 컨테이너를 관리형 서비스로 쪼개 올리는 방식을 권장하고 있었다. 문의해서 받은 답은 그것이 권장이지 원칙은 아니라는 것이었고, 그 답을 근거로 서버 한 대에 지금 구성을 그대로 올리는 쪽을 골랐다. 근거는 셋이었다. 남은 로드맵 항목은 서버 이전과 인원 확산이지 클라우드 네이티브 재설계가 아니고, 쪼개는 순간 이미 끝낸 SSO와 게스트 공유와 보안 하드닝 검증이 전부 다시 실행돼야 하며, 끝까지 쪼개도 로컬 디스크 인덱스를 쓰는 검색 서비스 때문에 서버가 0대가 되지 않는다. 비용 차이는 월 약 1,200달러와 약 220달러로 다섯 배 넘게 벌어졌는데, 강제가 아니라는 답을 받아 두지 않았다면 그 다섯 배를 감수 대상으로 착각했을 것이다.
내가 이 프로젝트에서 맡은 자리는 설계와 검증과 판정이었다. 요구를 인터뷰로 정제해 계획 문서를 직접 쓰고, 그 문서를 서로 다른 관점의 리뷰에 태워 반박하게 한 뒤, 구현은 에이전트에 위임하고 완료 여부를 인수 조건으로 직접 채점했다. 이 계획도 아키텍트 관점 리뷰가 조건부 승인과 함께 수정 7건을 냈고, 비평 관점 리뷰가 사전 부검 시나리오를 반영한 뒤에야 실행에 들어갔다.
가설의 최종 검증은 계정으로 했다. 6개 계정을 만들어 워크스페이스 하나에 멤버 4명과 게스트 1명을 초대하고 수락까지 전부 통과시켰다. 좌석 초과를 뜻하는 응답 코드 다섯 종이 한 건도 나오지 않았고, 구독과 결제 라우트는 물론 subscription 테이블 자체가 없다는 것까지 확인했다. 잠금이 공개 소스에 없다는 가설이 실측으로 닫힌 지점이다. 라이선스는 게이트로 남겼다. 포크 저장소는 사내 사용 동안만 비공개로 두되, 협력사나 외주처럼 외부 사용자가 접속하는 순간 공개 전환을 필수 조건으로 명문화했고, 앱 화면 하단에는 소스 저장소 링크를 상시 노출한다.
결과 & 배운 점
잠금은 사라졌고, 350명은 아직 오지 않았다
상용 이미지 의존은 세 개에서 0개가 됐다. 백엔드 세 서비스를 전부 우리 포크에서 멀티아키텍처 CI로 빌드해 digest로 고정했고, 사용자에게 보이는 원본 제품명은 로케일 33종에서 사라졌다. 외부 클라이언트를 지칭하는 6개 문구만 자체 데스크톱 앱이 나올 때 교체하기로 남겼다. 데이터가 밖으로 나가던 폰트 CDN 한 곳도 자체 호스팅으로 닫아 외부로 향하는 연결을 없앴다. 7월 30일에 프로덕션이 올라갔고, 로그인은 구글 SSO 전용으로 회사 도메인 제한을 웹 화면이 아니라 인증 서버 훅이 강제한다. 운영비는 실측 사용률을 근거로 VM 사양을 한 단계 내려 월 약 47만 8천 원에서 약 22만 8천 원으로 절반 넘게 줄였다. 이 값은 아직 모델 추정이고 다음 청구서로 대조해야 한다.
미달인 것도 분명하다. 350명 부하 테스트는 하지 않았다. 도입 검토에서 프로덕션 전환 앞에 걸어 둔 게이트 두 개 중 하나가 그것이었는데, 배포를 막고 있던 사유가 다른 이유로 해소되면서 게이트를 통과한 게 아니라 게이트를 지나친 채 프로덕션이 먼저 올라갔다. 350명 프로비저닝은 이관이 자리 잡는 것을 보고 열기로 미뤄 둔 상태이고, 실사용은 워크스페이스 14곳에 대부분 멤버 한 명이다. 동시 편집 데이터 유실률도 재지 않았다. 그러니 연 5,800만 원에서 1억 2천만 원이라는 절감은 아직 실현된 절감이 아니라 설계된 절감이다. 이 케이스에서 내가 가장 조심하는 문장이 그것이다.
구독료를 운영 인력 시간으로 치환한다는 말의 실체도 이 기간에 숫자가 아니라 사건으로 드러났다. 8월 20일 잔여 점검에서 인프라 코드가 프로덕션보다 22일 뒤처져 있고, 그 사이 손으로 얹은 수정 세 건이 살아 있는 서버 위에서만 돌고 있다는 것을 발견했다. 부팅 스크립트가 재부팅마다 그 파일들을 무조건 덮어쓰므로, 다음 재부팅 한 번에 브랜드 자산이 접근 거부로 죽고 내장 AI가 전면 실패하는 잠복 장애였다. 22일 동안 아무 일도 없었던 이유는 그동안 재부팅이 한 번도 없었기 때문이다. 더 곤란한 것은 그 코드를 따라잡는 적용 자체가 서버를 파괴하는 구조였다는 점이다. 부팅 스크립트를 담은 필드가 한 글자만 바뀌어도 인스턴스를 교체하도록 돼 있었다. 상태 파일에서 같은 값을 교체를 유발하지 않는 자리로 옮겨, 인스턴스 식별자와 생성 시각을 그대로 둔 채 적용을 끝냈다.
지나친 채 올라갔다
대부분 멤버 1명
가장 오래 남은 배움은 목표가 선언만으로 존재할 수 있다는 사실이다. 스파이크를 돌리기 전까지 우리는 "완제품을 쓰지 않는다"를 이미 지켜지고 있는 전제로 다뤘고, 실제로는 상용 이미지 세 개 위에서 돌고 있었다. 나는 그 뒤로 계획 문서의 현재 상태 칸을 문장이 아니라 실측 명령의 출력으로 채운다. 그리고 순수주의를 계층으로 바꾸는 것이 후퇴가 아니라는 것도 여기서 배웠다. 좌석 잠금이 없는 서비스까지 소스로 빌드하는 일은 목표가 아니라 취향이었고, 그 취향을 필수로 걸었다면 로그인 전체를 위험에 빠뜨릴 수 있었다. 실제로 나는 이 프로젝트에서 인증 서버에 공개 소스가 없다고 한 번 잘못 판정했다가 뒤늦게 정정했다. 이미지 이름과 소스 저장소 이름이 다를 뿐이었는데, 이미지 이름으로만 찾고 없다고 결론지은 것이다. 판정을 문서에 적는 순간 그것은 다음 사람의 전제가 된다는 걸 그때 알았다. 셀프호스팅으로 옮긴다는 것은 벤더가 대신 안 터지게 해 주던 일을 우리가 떠맡는다는 뜻이고, 그 몫은 구축이 끝나는 날 사라지지 않는다.
Supercent
에서의 다른 경험도 살펴보기
완성 파일이 아니라 편집 상태를 내놓은 AIDRA 팟캐스트 스튜디오
프리미어 프로에서 소스를 하나씩 얹던 오디오 드라마 제작 공정을 웹으로 옮기고, 회차 본문이 낳은 대본을 유일한 입구로 삼아 가편집된 타임라인까지 자동으로 채운 프로젝트
옮겼다가 아니라 잃은 게 없다로 기준을 바꾼 노션 이관
문서 5,148개와 댓글 2,131건을 자체 빌드한 사내 워크스페이스로 옮기면서, 계수 게이트가 초록불인데 화면이 깨져 있던 1차 실패를 기계 전수 대조와 실브라우저 페어 대조 두 레인으로 다시 판정한 프로젝트
말해줄 때까지 기다리지 않는 AIDRA 피드백 트리아지 루프
물어봐야 조금씩 말해주던 파트너팀 피드백을, 인앱 티켓과 회신이 한 바퀴 도는 루프로 바꾸고 그 운영 절차를 스킬로 코드화해 다섯 주간 열세 번 반복한 프로젝트