하루스페이스 랩
하나의 저장소를 넘어 그룹이 자기 프로젝트를 갖기까지
하나의 앱에 기능을 더하던 방식에서 벗어나, 그룹별 저장소·공개 사이트·AI 작업공간을 독립시키고도 사람 승인과 운영 경계를 유지한 3일의 기록입니다.

Summary
한눈에 보기
- 공개 사이트를 비공개 그룹 앱과 분리하고 정적 빌드·Git 승인 이력 중심으로 운영했습니다.
- 저장소 연결, AI 실행기, 저장소 반영과 공개 배포를 서로 다른 연결로 보고 단계별로 검증했습니다.
- 그룹별 프로세스를 늘리는 대신 공용 스케줄러와 격리된 프로젝트 정책으로 비용과 복구 가능성을 함께 지켰습니다.
이 글은 2026년 7월 29–31일의 Git 기록, 설계 문서와 운영 이슈를 바탕으로 다시 쓴 Haru Space 개발 회고다. 공개에 필요하지 않은 그룹 식별자, 로컬 경로, 내부 주소, 인증 정보와 개인 대화는 제외했다.
처음의 Haru Space는 하나의 제품과 하나의 저장소를 잘 운영하는 문제에 가까웠다. 하지만 그룹이 자기 프로젝트를 시작하고, 자기 공개 사이트를 만들고, 자기 AI 펫에게 작업을 맡기기 시작하자 질문이 달라졌다.
한 앱에 메뉴를 하나 더 붙이는 것으로 충분한가. 아니면 그룹마다 코드와 배포, 공개 범위와 승인 책임까지 실제로 나눠야 하는가.
우리는 후자를 택했다. 다만 저장소를 여러 개 만드는 것만으로 독립 프로젝트가 생기는 것은 아니었다.
그룹의 프로젝트가 독립적이라는 말은 코드가 떨어져 있다는 뜻이 아니라, 누가 읽고 바꾸고 공개할 수 있는지가 끝까지 섞이지 않는다는 뜻이었다.
공개 사이트부터 비공개 앱과 떼어 냈다
첫 실험은 Haru Journal이었다. 그룹의 글을 공개하는 사이트를 Haru Space 내부 페이지가 아니라 별도 저장소와 별도 배포 프로젝트로 만들었다.
이 선택은 화면 디자인보다 데이터 경계를 먼저 바꿨다.
- 공개 사이트는 로그인 없이 읽는 정적 화면으로 만들었다.
- 글과 이미지는 공개 저장소 안의 검토된 파일만 사용했다.
- 비공개 채팅, 회원, 계정과 그룹 내부 문서는 공개 빌드가 직접 읽지 않게 했다.
- 초안은 작업 브랜치에서 만들고, 세 언어·스키마·권리·공개 범위를 확인한 뒤 사람이 PR을 병합해야 게시되게 했다.
- 공개 사이트에는 저장소 쓰기 자격 증명이나 관리자 세션을 두지 않았다.
댓글과 실시간 AI 기능을 처음부터 붙이지 않은 것도 같은 이유였다. 입력을 받는 순간 스팸, 신고, 개인정보, 모델 비용과 보존 정책이 함께 생긴다. 먼저 읽기 전용 공개면을 안정적으로 열고, 상호작용은 책임 있게 운영할 준비가 된 뒤 추가하기로 했다.
정적 사이트는 비용 면에서도 맞았다. 방문할 때마다 서버 함수와 데이터베이스를 깨우지 않고 CDN에서 제공할 수 있었다. 이미지도 저장 원본과 실제 전송 크기를 나누었다. 그룹의 독립성을 위해 별도 프로젝트를 만들되, 유휴 비용까지 그룹 수만큼 늘릴 필요는 없었다.
저장소 연결은 작업 가능 상태의 일부일 뿐이었다
다음 문제는 AI 작업이었다. GitHub나 GitLab 저장소 연결 화면에서 “검증 완료”가 보인다고 해서 펫이 그 코드를 읽고 수정하고 저장소에 반영할 수 있는 것은 아니었다.
실제 흐름에는 서로 다른 네 가지 연결이 있었다.
- Haru Space가 현재 그룹과 저장소·기준 브랜치의 관계를 확인한다.
- 게이트웨이가 그 그룹의 대화용·작업용 프로젝트 정책을 안다.
- 설치형 실행기가 승인된 저장소를 정해진 격리 경계 안에서 준비한다.
- 별도의 전달 실행기가 사람이 승인한 변경만 작업 브랜치에 반영한다.
한 단계만 빠져도 연결 화면은 초록색인데 실제 작업은 실패할 수 있었다. 반대로 새 그룹 설정 하나가 잘못됐다고 기존 그룹 전체가 멈춰서도 안 됐다.
그래서 잘못되거나 충돌하는 연결은 해당 그룹만 닫히게 했다. 모호한 연결을 다른 프로젝트로 자동 대체하지 않았고, 대화 프로젝트는 읽기만, 작업 프로젝트는 승인된 편집만 허용했다. 저장소를 쓰는 자격 증명은 AI 실행 프로세스에 주지 않고 전달 단계에서만 짧게 사용했다.
이 구조는 설정 항목이 많아 보였다. 그러나 그룹의 프로젝트가 늘어날수록 “알아서 비슷한 저장소를 선택”하는 편의가 가장 위험한 동작이 된다. 연결 실패는 사용자가 고칠 수 있지만, 다른 그룹의 코드를 읽거나 바꾸는 성공은 복구하기 어렵다.
수동 등록은 오래 버티지 못했다
초기에는 새 프로젝트가 생길 때마다 운영자가 실행기와 게이트웨이의 허용 목록을 맞췄다. 몇 개의 프로젝트까지는 가능했다. 하지만 실제로는 프로젝트 이름, 작업용·대화용 구분, 저장소 위치, 기준 브랜치와 각 실행 신원의 권한을 여러 곳에 반복 등록해야 했다.
한 곳의 값이 다르면 작업은 모델을 호출하기도 전에 실패했다. Windows의 긴 경로 제한처럼 저장소 내용과 관계없는 환경 차이도 준비 단계를 막았다. 실행기 프로세스를 갱신할 때 이전 인스턴스가 남으면, 화면상 재시작과 실제 단일 실행 상태도 달라질 수 있었다.
우리는 수동 문서를 더 길게 만드는 대신 승인형 자동 준비로 방향을 바꿨다.
그룹관리자가 검증된 저장소에 대해 연결을 요청하면, 권한 있는 관리자가 그룹과 저장소 경계를 확인한다. 승인 뒤에만 실행기가 저장소를 준비하고 정책을 동기화한다. 저장소나 기준 브랜치가 바뀌면 과거 승인을 그대로 재사용하지 않는다. 준비 실패는 기존 그룹·공간 데이터를 되돌리지 않고 재시도 가능한 상태로 남긴다.
자동화가 늘었지만 자동 승인 범위는 늘리지 않았다. 연결을 자동으로 준비하는 것과, 그 저장소에 만들어진 변경을 자동으로 병합·배포하는 것은 다른 결정이었다.
여러 저장소의 성공은 한 번의 데모로 확인할 수 없었다
다중 저장소 연결을 확인할 때는 대표 프로젝트 하나의 성공만 보지 않았다. 서로 다른 저장소에서 읽기, 격리된 수정, 커밋·푸시, PR과 배포 연결이 같은 경계로 작동하는지 순서대로 확인했다.
여기서 중요한 것은 작업 결과보다 실패의 격리였다.
- 한 그룹의 연결 오류가 다른 그룹 작업을 막지 않는가.
- 대화가 쓰기 가능한 작업으로 승격되지 않는가.
- 작업 실행기와 저장소 전달 실행기가 서로의 자격 증명을 공유하지 않는가.
- 기준 브랜치 직접 푸시와 강제 푸시가 차단되는가.
- PR과 공개 배포는 여전히 사람이 최종 범위를 확인하는가.
운영 확인 결과를 남길 때도 토큰, 내부 주소와 로컬 경로를 기록하지 않았다. 어떤 프로젝트에서 어느 단계가 성공하거나 실패했는지, 어떤 정책 버전과 변경 지문을 검증했는지만으로 복구에 충분해야 했다.
예약 작업도 그룹별 프로세스로 만들지 않았다
그룹 프로젝트가 늘자 “매주 이 글을 준비해 줘”, “정해진 시간에 점검해 줘” 같은 반복 작업이 자연스럽게 필요해졌다. 가장 단순한 구현은 그룹마다 별도 Cron과 별도 AI 프로세스를 두는 것이었다.
그 방법은 작은 PoC의 자원 구조와 맞지 않았다. 그룹 수만큼 상시 프로세스, 설정, 로그와 장애 지점이 늘어난다. 대신 하나의 공용 스케줄러가 기한이 된 소수 작업만 선점해 직렬 처리하도록 했다.
예약도 두 종류로 나눴다.
- 단순 알림·일정·날씨는 AI를 호출하지 않는다.
- 실제 코드나 콘텐츠 작업만 구조화된 작업 요청을 만든다.
예약 작업의 자동 승인은 작업 요청 수락과 최초 편집 시작까지만 허용했다. 커밋·푸시, PR, 병합, 운영 배포와 데이터베이스·권한 변경은 기존 사람 승인 경계를 그대로 통과했다. 반복 작업이라는 이유로 결과까지 자동 게시하지 않았다.
재시도에서 같은 메시지와 작업이 중복되지 않게 실행마다 고유한 이력을 두었고, 장시간 중단 뒤 지나간 예약을 한꺼번에 실행하지 않았다. 실패 기록은 남기되 다음 반복은 다음 시각으로 넘겼다.
긴 작업 하나가 예약의 한계를 보여 줬다
실제 운영에서는 조사, 이미지, 세 언어 글, 검증과 게시 준비를 한 요청에 모두 묶은 예약 작업이 실행 시간 한도를 넘었다. 스케줄러와 자동 승인은 정상인데 후속 AI 작업만 실패했다.
처음에는 제한 시간을 더 늘리면 될 것처럼 보였다. 일부 범위에서는 제한형 확장이 필요했다. 그러나 실행 시간을 계속 늘리는 것은 단일 작업 슬롯의 대기 시간을 함께 늘리는 선택이었다.
더 중요한 해결은 작업을 산출물 단위로 나누는 것이었다. 조사와 원고, 언어별 검토와 이미지, 빌드 검증을 한 번에 모두 맡기기보다 중간 결과를 저장소에 남기고 다음 작업이 검증된 결과에서 이어지게 하는 편이 안정적이었다.
시간이 끝난 부분 산출물은 “거의 완료”라는 이유로 전달하지 않았다. 한국어 원고와 이미지가 남아 있어도 세 언어 검토와 최종 검증이 끝나지 않았다면 게시 가능한 결과가 아니었다.
그룹 주권은 프로젝트 수가 늘 때 드러났다
이 3일 동안 만든 것은 다중 저장소 기능 하나가 아니었다. 그룹, 저장소, AI 프로젝트, 작업공간, 전달과 공개 사이트가 서로 어떤 관계를 가져야 하는지에 대한 기준이었다.
다시 시작한다면 다음 순서를 지킬 것이다.
1. 공개면과 비공개 원본을 먼저 분리한다
공개 사이트는 승인된 공개 파일이나 projection만 읽게 한다. 편집 권한과 방문자 읽기 권한을 같은 런타임에 넣지 않는다.
2. 연결 완료를 단계별 상태로 보여 준다
SCM 연결, 실행기 준비, 저장소 전달과 배포 연결을 하나의 초록색 표시로 합치지 않는다. 실패한 계층과 다음 복구 행동을 구분한다.
3. 자동 준비와 자동 결정을 혼동하지 않는다
반복 설정은 자동화하되, 코드 수정·저장소 반영·병합·공개에는 기존 사람 승인을 유지한다.
4. 그룹 수만큼 상시 자원을 늘리지 않는다
공용 스케줄러, 조건부 동기화와 검증 캐시를 사용한다. 새 그룹 하나의 유휴 비용이 새 서버 하나가 되지 않게 한다.
5. 실패 이력을 성공으로 덮지 않는다
부분 산출물, 실패 예약과 거부된 결정은 복구 근거로 남긴다. 다음 작업은 새 이력으로 이어 간다.
하나의 플랫폼 안에 여러 기준선을 두는 일
7월 31일이 끝났을 때 Haru Space는 더 이상 자기 저장소만 관리하는 앱이 아니었다. 그룹이 별도 프로젝트와 공개 사이트를 가지고, 자기 펫에게 제한된 작업을 맡기며, 결과를 자기 승인 흐름으로 반영할 수 있는 작은 플랫폼이 되어 가고 있었다.
하지만 플랫폼이 된다는 것은 모든 것을 중앙에서 통제한다는 뜻이 아니었다.
중앙은 연결 계약, 최소 권한, 감사와 비용 경계를 제공한다. 각 그룹은 자기 저장소, 공개 범위와 최종 결정을 가진다. 실패한 연결은 그 그룹 안에서 닫히고, 성공한 자동화도 사람의 마지막 문을 대신 열지 않는다.
하나의 저장소를 넘어선 날, 우리가 얻은 것은 더 많은 프로젝트가 아니라 서로 다른 프로젝트가 서로의 경계를 침범하지 않고 움직이는 방법이었다.
반응을 남기면 중복 방지를 위한 무작위 식별자가 이 브라우저에 저장될 수 있습니다.

