하루의 기록을 준비하고 있어요

잠시만 기다려 주세요.

목록으로

운영 도메인을 여는 일은 화면 뒤에서 시작됐다

새 운영 주소를 여는 날, 화면보다 먼저 데이터베이스 승인·인증 경계·백그라운드 작업자와 외부 의존성 복구를 확인한 이유를 기록했습니다.

책처럼 보기
보통
새 문을 여는 열쇠 옆에 데이터베이스 원장, 인증 방패, 작업자 시계와 빈 점검 카드가 놓인 차분한 운영 작업대

주제

하루스페이스 랩

운영 도메인을 여는 일은 화면 뒤에서 시작됐다

새 운영 주소를 여는 날, 화면보다 먼저 데이터베이스 승인·인증 경계·백그라운드 작업자와 외부 의존성 복구를 확인한 이유를 기록했습니다.

요약

요약

  1. 코드 배포 중 조용히 실행되던 DB 변경을 검토된 파일·변경 지문·사람 승인 흐름으로 옮겼습니다.
  2. 플랫폼 운영 권한과 비공개 그룹 참여 권한을 분리하고 공개 인증의 남용·계정 수명주기를 보완했습니다.
  3. 도메인과 인증서뿐 아니라 예약 호출·설치형 실행기·저장소 전달·공개 사이트까지 한 흐름으로 확인했습니다.
12페이지
새 문을 여는 열쇠 옆에 데이터베이스 원장, 인증 방패, 작업자 시계와 빈 점검 카드가 놓인 차분한 운영 작업대

Summary

한눈에 보기

  • 코드 배포 중 조용히 실행되던 DB 변경을 검토된 파일·변경 지문·사람 승인 흐름으로 옮겼습니다.
  • 플랫폼 운영 권한과 비공개 그룹 참여 권한을 분리하고 공개 인증의 남용·계정 수명주기를 보완했습니다.
  • 도메인과 인증서뿐 아니라 예약 호출·설치형 실행기·저장소 전달·공개 사이트까지 한 흐름으로 확인했습니다.

이 글은 2026년 8월 1–2일의 설계 문서, Git 기록과 운영 이슈를 바탕으로 다시 쓴 Haru Space 개발 회고다. 방어에 악용될 수 있는 세부 설정, 내부 주소, 계정·그룹 정보, 비밀값과 개인 대화는 제외했다.

새 운영 도메인이 연결되던 날, 가장 눈에 띄는 변화는 주소표시줄이었다. 기존 배포 주소 대신 기억하기 쉬운 서비스 주소가 열리고, 공개 사이트들도 같은 이름 아래 정리됐다.

하지만 실제 작업 시간의 대부분은 로고나 첫 화면에 쓰이지 않았다. 데이터베이스가 언제 바뀌는지, 플랫폼 관리자가 무엇을 볼 수 있는지, 예약 호출과 설치형 실행기가 새 주소를 따라오는지, 외부 패키지가 필요할 때 누가 어떤 범위를 승인하는지를 확인하는 데 쓰였다.

처음에는 도메인 전환을 배포 작업으로 생각했다. 끝나고 보니 운영 체계 전체를 시험하는 날에 더 가까웠다.

운영 도메인을 연다는 것은 사람들이 들어올 문을 다는 일이 아니라, 그 문 뒤의 데이터·권한·백그라운드 작업이 같은 규칙으로 움직인다는 것을 증명하는 일이었다.

가장 먼저 본 것은 데이터베이스였다

당시 Haru Space에는 서버 요청 중 필요한 테이블이나 컬럼을 확인하고 없으면 만드는 코드가 여러 곳에 남아 있었다. 작은 PoC에서는 편리했다. 새 기능을 배포하면 필요한 구조가 자연스럽게 준비됐다.

운영 단계에서는 의미가 달랐다. 코드 배포가 곧 데이터베이스 변경 권한 행사일 수 있는데, 사람이 그 변경 내용을 검토하고 승인할 지점이 없었다. 서버리스 인스턴스가 동시에 시작되면 같은 구조 확인이 반복되고 잠금이나 콜드 스타트 비용도 생길 수 있었다.

그래서 DB 변경을 코드 작업의 부속 효과에서 명시적인 운영 산출물로 옮겼다.

  • 변경은 저장소의 버전 파일과 기계가 읽을 수 있는 설명 파일로만 전달한다.
  • 승인 대상은 정확한 소스 revision과 변경 파일 지문에 묶는다.
  • 내용이나 기준 revision이 바뀌면 과거 승인을 재사용하지 않는다.
  • 작업 카드에는 영향 범위와 검증 결과를 보여 주되 연결 정보와 비밀값은 넣지 않는다.
  • 적용, 검증, 병합과 배포는 내부적으로 구분해 기록하되 사람은 고정된 전체 범위를 한 번에 판단한다.

초기 자동 범위도 추가형 변경으로 좁혔다. 되돌리기 어렵거나 데이터 의미를 바꾸는 작업, 권한·보안 정책에 영향을 주는 작업은 같은 자동 흐름에 섞지 않았다. 코드와 새 구조가 함께 살아 있는 기간을 먼저 만든 뒤 별도 작업으로 다루는 편을 택했다.

이 구조를 만드는 과정에서 서버리스 패키징이 로컬 검증과 다르게 실패하기도 했다. 로컬에서 읽히던 파서 자산이 배포 산출물에는 원하는 형태로 포함되지 않았다. 파일 경로에 기대던 방식을 빌드가 추적할 수 있는 단일 런타임으로 바꾸고 나서야 운영 함수가 정상화됐다.

“빌드 통과”와 “서버리스에서 실제 실행 가능”이 다른 검증이라는 첫 번째 신호였다.

최고관리자도 그룹의 자동 구성원이 아니었다

운영 안정화에서 다음으로 고친 것은 권한의 의미였다.

플랫폼 최고관리자는 인프라와 전체 설정을 점검해야 한다. 그렇다고 모든 비공개 그룹의 채팅, 문서와 활동을 자동으로 볼 수 있어야 하는 것은 아니다. 운영 권한과 그룹 참여 권한을 같은 역할 안에 넣으면, 관리 편의가 곧 과도한 데이터 접근이 된다.

우리는 다음 기준을 중앙 정책으로 고정했다.

  • 플랫폼 권한은 그룹 멤버십을 자동 부여하지 않는다.
  • 비공개 콘텐츠는 현재 활성 그룹 구성원인지 다시 확인한다.
  • 그룹 설정은 해당 그룹의 현재 관리자만 바꾼다.
  • 비활성·탈퇴 상태는 과거 역할과 관계없이 콘텐츠 접근 근거가 되지 않는다.

API도 같은 기준으로 점검했다. 각 경로가 인증을 요구하는지, 공개 예외라면 왜 공개인지 명시했는지를 자동 검사했다. 공개 홈과 약관은 열어 두되 그룹, 채팅, 작업과 운영 데이터는 로그인·멤버십 경계 안에 남겼다.

이때 만든 운영 기록은 “누가 무엇을 읽었는가”를 모두 복제하는 감시 로그가 아니었다. 작업, 승인, 저장소, DB와 배포에서 일어난 상태 변화를 시간순으로 연결하되, 프롬프트와 토큰, 사용자 원문과 연결 지문은 제외했다. 설명 가능성을 얻기 위해 더 많은 비공개 데이터를 모으지 않으려 했다.

공개 주소는 인증 남용도 함께 데려왔다

서비스가 공개 주소를 갖는 순간 로그인과 가입은 내부 테스트 기능이 아니라 인터넷에 노출된 입구가 된다.

반복 로그인과 자동 가입을 줄이는 방어를 추가했다. 다만 공격을 줄이겠다는 이유로 원본 네트워크 주소를 장기 저장하거나, 정상 사용자를 영구 차단하지 않도록 했다.

  • 반복 실패에는 시간이 지나면 풀리는 제한을 적용했다.
  • 가입 출처는 서버 전용 키로 비가역 처리한 짧은 기록으로만 비교했다.
  • 오래된 시도 기록은 장기 보관하지 않았다.
  • 그룹 참여 이력이 있는 계정은 자동 정리 대상에서 보수적으로 제외했다.
  • 운영자는 신규 가입을 일시 중단할 수 있지만 기존 로그인과 그룹 참여 상태는 유지되게 했다.

정확한 제한값보다 중요한 것은 인증 방어 상태와 계정 상태를 분리한 점이었다. 비밀번호 실패가 쌓였다고 그룹 승인이나 탈퇴 기록을 바꾸지 않았다. 잠금은 잠금이고, 멤버십은 멤버십이었다.

주소 하나를 바꾸면 숨은 호출자가 드러났다

운영 주소를 전환한 뒤 브라우저에서 첫 화면이 열렸다. 그것만으로는 끝나지 않았다.

예약 스케줄러, 설치형 AI 실행기, 저장소 전달 프로세스, OAuth 복귀, 웹 푸시, 공개 사이트의 canonical과 외부 요청 허용 범위도 이전 주소를 기억하고 있을 수 있었다. 일부는 브라우저처럼 자동 이동을 따르지만, 보안을 위해 redirect를 거부하는 서버 클라이언트도 있었다.

그래서 전환을 의존 관계 순서로 확인했다.

  1. DNS와 인증서가 새 주소를 올바르게 가리키는지 확인한다.
  2. 미등록 공개 호스트가 메인 앱으로 잘못 열리지 않는지 확인한다.
  3. 로그인 세션이 공개 서브도메인에 공유되지 않는지 확인한다.
  4. 예약 호출과 실행기 레지스트리가 새 주소에서 인증되는지 확인한다.
  5. 코드 작업과 저장소 전달이 실제 한 사이클을 끝내는지 확인한다.
  6. 공개 사이트의 검색 메타데이터와 외부 연결 범위가 새 주소 하나로 정규화됐는지 확인한다.

주소 변경은 숨은 의존성을 찾는 좋은 시험이었다. 사용자가 보는 링크만 새 주소로 바꾸고 백그라운드 호출을 놓치면, 사이트는 정상처럼 보이면서 예약과 작업만 조용히 멈춘다.

“실행 중”인 작업자가 실제로 두 개였다

도메인 전환 과정에서는 설치형 브리지의 재시작 문제도 드러났다. 예약 작업을 중지했다가 다시 시작했는데, 부모 감독 프로세스가 멈춘 뒤에도 자식 실행 프로세스가 남을 수 있었다. 새 감독 프로세스는 시작됐지만 기존 인스턴스와 충돌해 재시도만 반복했다.

작업 스케줄러 화면의 “실행 중” 표시는 실제 애플리케이션 인스턴스 수를 보장하지 않았다.

복구는 모든 같은 종류의 프로세스를 강제로 종료하는 방식이 아니었다. 정확한 실행 대상과 부모 관계를 확인하고, 소유자가 사라진 고아 인스턴스만 정리했다. 정상 감독자가 있으면 중복 시작은 즉시 멈췄고, 조회 권한이 부족하면 기존 단일 인스턴스 잠금이 마지막 방어로 남았다.

여기서도 상태 표시보다 관계 검증이 중요했다. 예약 작업, 감독 프로세스, 실제 실행 프로세스와 마지막 정상 heartbeat를 따로 봐야 했다.

예약은 성공했지만 AI 작업은 끝나지 않았다

같은 시기 예약 콘텐츠 작업 하나가 실패했다. 예약 메시지는 제시간에 만들어졌고 자동 승인도 정상 동작했다. 설치형 실행기도 작업을 가져갔다. 문제는 그 뒤였다.

조사, 이미지, 세 언어 원고와 검증을 한 요청에 묶으면서 실행 한도를 넘었다. 일부 파일이 격리 작업공간에 남았지만 완성 결과로 전달되지 않았다.

우리는 부분 결과를 자동으로 커밋하거나 게시하지 않았다. 실행 한도를 제한형으로 조정하되, 반복해서 한도에 가까워지는 작업은 글이나 산출물 단위로 나누기로 했다. 스케줄러 성공과 AI 작업 성공을 서로 다른 상태로 보여 줘야 한다는 점도 명확해졌다.

한 시스템의 앞단이 성공했다는 사실로 뒤 단계의 실패를 가리지 않는 것. 이후 Haru Space 운영 점검에서 반복해서 등장할 원칙이었다.

인터넷을 열지 않고 의존성을 받는 방법

외부 패키지가 필요한 AI 작업도 같은 경계를 시험했다. AI 실행 환경의 네트워크를 통째로 열면 구현은 쉬워진다. 대신 임의 다운로드, 공급망 공격, 내부망 접근과 자격 증명 유출 위험도 함께 열린다.

우리는 일반 인터넷 허용 대신 구조화된 의존성 승인을 만들었다.

AI는 기존 코드로 해결할 수 없을 때 정확한 패키지와 고정 버전, 용도를 요청한다. 사람은 작업보기에서 그 대상을 확인한다. 승인된 요청만 제한된 설치기가 공식 공급 경로에서 실행하고, 설치 스크립트를 끈 채 변경 파일과 고위험 취약점을 검사한다. 성공하면 같은 격리 작업공간과 작업 문맥에서 원 요청을 이어 간다.

첫 운영에서는 패키지 관리 도구 자체를 프로젝트 의존성처럼 처리해 복원 작업이 시간 안에 끝나지 않는 문제가 생겼다. 실행기 도구 복원과 프로젝트 패키지 추가가 다른 작업임을 분리하고, 저장소가 선언한 버전과 잠금파일이 일치할 때만 제한된 복원을 허용했다.

보안 경계를 완화하지 않고도 실패 원인을 더 정확히 분류할 수 있었다.

운영 시작일 체크리스트

이틀을 다시 시작한다면 화면을 열기 전에 다음을 확인할 것이다.

1. 데이터 변경은 소스 revision과 함께 승인되는가

요청 중 조용히 DDL을 실행하지 않는다. 검토된 변경 파일, 영향 요약, 지문과 실행 기록을 묶는다.

2. 플랫폼 권한과 콘텐츠 권한이 분리되어 있는가

인프라 운영자가 된 사실만으로 모든 비공개 그룹을 읽을 수 없게 한다.

3. 공개 주소의 모든 숨은 소비자를 찾았는가

브라우저 링크 외에 예약 호출, OAuth, 실행기, 웹훅, 검색 메타데이터와 공개 사이트 연결을 함께 확인한다.

4. 프로세스 상태가 실제 단일 실행을 뜻하는가

스케줄러 표시, 부모·자식 프로세스, readiness와 마지막 정상 작업을 따로 관찰한다.

5. 앞 단계 성공이 전체 성공으로 보이지 않는가

예약, AI 실행, 저장소 반영, 병합과 배포를 분리한다. 부분 결과는 완료로 발행하지 않는다.

6. 외부 의존성 예외가 일반 네트워크 권한이 되지 않는가

정확한 대상, 일회성 승인, 제한된 설치기와 취약점 검사를 사용한다.

화면이 열렸을 때 비로소 시작된 운영

새 주소가 정상 응답하고 첫 화면이 뜬 순간은 분명 기뻤다. 하지만 그날의 완료 기준은 브라우저 한 장면이 아니었다.

DB 변경이 승인된 revision과 일치하고, 그룹 권한이 다시 확인되며, 예약 호출과 실행기가 새 주소에서 한 번의 실제 흐름을 끝내고, 실패한 작업은 부분 결과를 퍼뜨리지 않아야 했다. 공개와 수익화도 기술적 스위치가 준비됐다는 이유만으로 바로 켜지 않았다. 권리, 법적 고지와 사람 검토가 별도의 문으로 남았다.

운영 도메인은 서비스를 완성해 주지 않았다. 대신 그동안 따로 만든 경계들이 실제로 서로 이어져 있는지 한꺼번에 보여 줬다.

주소표시줄이 바뀐 것은 마지막에 보인 결과였다. 운영은 그보다 먼저, 화면 뒤의 모든 관계를 다시 확인하면서 시작됐다.

이어 읽기

이전 글 · 다음 글

이전 글하나의 저장소를 넘어 그룹이 자기 프로젝트를 갖기까지다음 글 AI 작업이 멈춰도 다시 시작하려면 무엇을 남겨야 할까