로컬에서 벗어나려면 실행 환경이 필요했다

1편에서 쓴 것처럼, 처음 문제는 꽤 단순했다.

관련 글내 OS를 만들고 싶었다ATLAS는 처음부터 거대한 지식 엔진으로 시작한 프로젝트가 아니었다. 맥북 안에 갇힌 작업 환경을 클라우드 위로 옮기고, AI를 내가 원하는 방식으로 더 편하게 쓰기 위한 개인 OS를 만들고 싶다는 생각에서 출발했다.

내 작업 환경이 맥북 안에 갇혀 있었다.
파일도 로컬에 있고, 메모도 로컬에 있고, AI에게 넘겨줄 맥락도 로컬에 있었다.
맥북을 열지 않으면 아무것도 이어지지 않았다.

처음에는 이게 단순히 “파일 동기화”의 문제처럼 보이기도 했다. iCloud나 GitHub에 파일을 올려두면 되는 것 아닌가 싶었다. 하지만 내가 원했던 건 파일 보관만이 아니었다.

나는 AI와 대화하고, 프로젝트 기록을 보고, 필요한 파일을 읽고, 내가 하던 작업을 이어갈 수 있는 환경을 원했다. 그건 단순한 저장소라기보다 실행 환경에 가까웠다.

어딘가에 항상 켜져 있는 공간이 필요했다.
맥북이 꺼져 있어도 접속할 수 있고, 폰에서도 열 수 있고, 내가 만든 앱과 데이터가 계속 살아 있는 공간.

그래서 자연스럽게 클라우드 위에 올리는 방향으로 갔다.

Storage wasn't enough — I needed a runtime

당시 상태: 작은 Cloud Run 서비스들이 흩어져 있었다

ATLAS 때문에 처음부터 서버를 만든 것은 아니었다.

그때 나는 여러 프로젝트를 동시에 굴리고 있었다. Digging Music, Campus Koe, 포트폴리오, 실험용 앱들이 있었고, 각각의 서비스가 따로 배포되어 있었다. 어떤 것은 Cloud Run에 있고, 어떤 것은 별도 VM에 있고, 데이터베이스도 따로 있었다.

처음에는 Cloud Run이 편했다. 서버를 직접 관리하지 않아도 되고, 배포도 단순하고, 서비스별로 독립적으로 관리할 수 있었기 때문이다.

하지만 실제 상태는 점점 복잡해졌다.

대부분의 프로젝트가 각각의 Cloud Run 서비스로 올라가 있었다. 프로젝트 안에서도 web, api, admin, cms처럼 서비스가 나뉘는 경우가 있었다. 나는 응답속도를 꽤 신경 썼기 때문에, 자주 여는 서비스나 사람들에게 보여줘야 하는 서비스에는 min instance를 1로 작게라도 켜두는 경우가 많았다.

이렇게 하면 사용 경험은 좋아졌다. 서비스가 잠들어 있다가 첫 요청에서 느리게 뜨는 느낌이 줄어들었기 때문이다. 하지만 그 대신 작은 서비스라도 완전히 싸게 유지되지는 않았다.

서비스 하나만 보면 큰 비용은 아니었다.
그런데 여러 프로젝트가 있고, 각 프로젝트 안에 여러 서비스가 생기면 이야기가 달라졌다.

비용은 트래픽이 많아서 나가는 게 아니었다. 언제든 바로 열려야 하는 작은 서비스들이 많아서 나가는 구조였다.

Many small services, always awake

문제는 비용만이 아니었다

비용도 신경 쓰였지만, 더 큰 문제는 심리적 마찰이었다.

뭔가를 하나 실험할 때마다 Cloud Run 서비스를 새로 만들고, 도메인을 붙이고, 환경 변수를 넣고, 시크릿을 연결하고, 배포 설정을 추가해야 했다.

이건 별것 아닌 일처럼 보이지만, 작업 방식에는 꽤 큰 영향을 준다.

작은 아이디어는 가볍게 올라가야 한다.
써보고 별로면 부담 없이 내려갈 수 있어야 한다.
그런데 실험 하나를 시작할 때마다 새로운 배포 단위를 만들어야 하면, 만들기도 전에 실험이 무거워진다.

나는 계속 새로운 걸 작게 만들고, 써보고, 아니면 버리고, 괜찮으면 키우는 식으로 일하고 싶었다.

작은 앱, 내부 도구, 포트폴리오 기능, AI 실험, 프로젝트별 어드민 페이지 같은 것들이 계속 생기는 작업 방식. 말하자면 작은 프로젝트를 계속 만들고 실패하는 공장처럼 움직이고 싶었다.

이런 방식으로 일하려면 실패 비용이 낮아야 한다.

아이디어 하나가 또 다른 Cloud Run 서비스 하나가 되고, 그 서비스가 또 비용과 설정과 관리 포인트가 되는 구조는 점점 부담스러웠다.

The experiment loop: make, use, decide, keep or kill

통합 실험 환경이 필요했다

그래서 하나의 VM이 점점 더 자연스럽게 느껴졌다.

매번 새로운 Cloud Run 서비스를 늘리는 대신, GCE VM 하나를 계속 켜두고 그 위에 여러 앱을 올릴 수 있다. Docker Compose로 서비스들을 띄우고, Caddy로 도메인과 HTTPS를 처리하면 된다.

머릿속 모델이 훨씬 단순해졌다.

하나의 바닥이 있다.
그 위에 컨테이너를 하나 더 올린다.
실패하면 내린다.
괜찮으면 남긴다.

이건 단순한 서버 비용 절감이라기보다, 내가 일하는 방식에 맞는 인프라를 만드는 일이었다.

나는 계속 새로운 걸 작게 만들고 실패하는 공장처럼 일하고 싶었다.
그 공장 바닥이 되는 환경이 필요했다.

ATLAS는 그 위에 올라온 개인 AI 실험 중 하나였다.

Scattered Cloud Run services consolidated into one GCE VM

Cloud Run이 아니라 VM 위에 둔 이유

Cloud Run을 계속 쓸 수도 있었다.

실제로 Cloud Run은 좋은 도구다. 배포도 단순하고, 스케일링도 관리되고, 서버를 직접 관리하지 않아도 된다. 일반적인 웹서비스라면 충분히 좋은 선택이다.

하지만 ATLAS는 조금 달랐다.

ATLAS는 단순히 요청이 들어오면 응답하고 끝나는 서비스가 아니라, 내 작업 환경 전체와 붙어 있어야 했다. 파일을 읽고, 여러 프로젝트와 연결되고, 대화 기록을 저장하고, 나중에는 더 긴 작업 흐름까지 붙을 가능성이 있었다.

그리고 이미 여러 프로젝트를 하나의 VM으로 모으는 흐름이 있었다. 그 위에서 Docker Compose와 Caddy를 쓰고 있었기 때문에, ATLAS도 같은 방식으로 올리는 게 자연스러웠다.

물론 이 방식이 모든 상황에서 정답은 아니다. 트래픽이 커지고 안정성이 중요해지면 Cloud Run 같은 관리형 환경이 더 나을 수 있다. 하지만 당시의 나에게 필요했던 건 대규모 운영 환경이 아니라, 작은 실험을 빠르게 만들고 버릴 수 있는 작업장이었다.

VM은 그 작업장에 가까웠다.

Cloud Run vs. one VM for ATLAS

첫 구조

이때의 ATLAS는 아직 복잡한 지식 엔진이 아니었다.

목표는 훨씬 단순했다.
맥북 밖에서 접속할 수 있는 개인 AI 앱을 만드는 것.

처음 생각한 구조는 대략 이랬다.

Next.js로 웹앱을 만든다.
PWA처럼 모바일에서도 열 수 있게 한다.
GCE VM 위에 배포한다.
Caddy로 도메인을 연결하고 HTTPS를 처리한다.
Claude API를 붙여서 대화할 수 있게 한다.
Firestore에 대화 기록을 저장한다.
Secret Manager로 API 키를 관리한다.
Face ID나 WebAuthn으로 인증을 붙인다.

이 정도만 되어도 로컬에서 벗어나는 느낌은 꽤 컸다.

맥북 안에 있는 폴더를 열고, Claude 앱을 켜고, 파일을 복사해서 붙여넣는 방식이 아니라, 그냥 내 개인 웹앱에 들어가서 대화를 시작하는 구조. 폰에서도 들어갈 수 있고, 서버는 계속 켜져 있고, 대화 기록도 남아 있는 구조.

그때는 이것만으로도 꽤 의미가 있다고 느꼈다.

물론 지금 보면 이 구조는 굉장히 초기적이다. 하지만 당시에는 중요한 첫 단계였다. 로컬 파일과 로컬 앱 중심으로 쓰던 AI 환경을, 내가 직접 배포한 클라우드 앱으로 옮기기 시작한 것이기 때문이다.

The first ATLAS stack on a single VM

Next.js PWA를 선택한 이유

처음부터 네이티브 앱을 만들 생각은 없었다.

내가 필요했던 건 앱스토어에 올릴 제품이 아니라, 내가 바로 쓸 수 있는 개인 도구였다. 그래서 웹앱이 가장 현실적이었다. Next.js로 만들면 프론트엔드와 API 라우트를 한 프로젝트 안에서 다룰 수 있고, 배포도 익숙했다.

PWA로 만들면 모바일에서도 앱처럼 열 수 있었다. 엄밀히 말하면 네이티브 앱은 아니지만, 홈 화면에 추가해두고 쓰면 충분히 앱처럼 느껴졌다.

이 선택은 ATLAS의 성격과도 맞았다.
ATLAS는 처음부터 완성된 제품이라기보다, 내가 쓰면서 계속 바꿔갈 실험 도구에 가까웠다. 빠르게 만들고, 직접 써보고, 불편한 부분을 고치고, 다시 구조를 바꾸는 게 중요했다.

그래서 무겁게 시작하고 싶지 않았다.
웹앱이면 충분했다.

Why a Next.js PWA

인증이 필요했다

개인 AI 앱이라는 말은 생각보다 민감하다.

단순한 메모 앱이면 몰라도, 여기에 프로젝트 기록, 대화 내용, API 키, 개인적인 메모, 공부 기록 같은 것들이 들어갈 수 있다. 아무나 접근할 수 있으면 안 됐다.

그래서 처음부터 인증을 붙이려고 했다.

일반적인 아이디/비밀번호 방식도 가능했지만, 내가 주로 쓰는 기기에서는 Face ID로 여는 게 훨씬 자연스러웠다. 그래서 WebAuthn 기반으로 Face ID 인증을 붙였다.

이건 기능적으로도 중요했지만, 느낌상으로도 중요했다.
내가 원했던 건 공개 웹서비스가 아니라, 내 개인 작업 공간이었다.
브라우저에서 열리지만, 아무나 들어오는 페이지는 아니어야 했다.

Face ID로 들어가는 순간, 이 앱이 그냥 테스트 페이지가 아니라 내 개인 OS의 입구처럼 느껴졌다.

Locking a personal AI app

대화 기록을 남기기 시작했다

클라우드 앱으로 옮기면서 가장 먼저 필요했던 데이터는 대화 기록이었다.

AI와 대화는 한 번 쓰고 사라지는 것처럼 보이지만, 실제로는 많은 작업의 흔적이 들어 있다. 어떤 문제를 물어봤는지, 어떤 방향을 검토했는지, 어떤 표현을 고쳤는지, 어떤 판단을 했는지가 대화 안에 남는다.

그래서 Firestore에 대화 기록을 저장하기 시작했다.

처음부터 완벽한 메모리 구조를 만들었던 건 아니다. 그때는 그냥 “대화가 날아가지 않았으면 좋겠다”에 가까웠다. 그래도 대화가 서버에 남는다는 것은 꽤 큰 차이였다.

로컬에서 임시로 쓰는 채팅과 달리, 이 대화들은 나중에 다시 읽을 수 있었다. 그리고 나중에는 이 기록에서 중요한 부분을 뽑아 노트로 만들거나, 프로젝트 문맥으로 연결할 수도 있겠다는 생각이 들었다.

하지만 이 단계에서는 아직 가능성에 가까웠다.
우선은 기록이 남는 것 자체가 중요했다.

Conversation history as the first memory

파일과 지식베이스에 접근하고 싶었다

대화 기록만으로는 부족했다.

내가 AI에게 자주 넘겨주는 것은 결국 파일이었다. 프로젝트 문서, 기획 메모, 공부 기록, 코드 조각, 포트폴리오 초안 같은 것들. 이 파일들이 맥북 안에만 있으면, 클라우드 앱을 만들어도 반쪽짜리였다.

그래서 ATLAS에는 파일과 지식베이스를 읽는 기능이 필요했다.

처음에는 복잡한 데이터베이스보다 flat Markdown 구조가 더 편해 보였다. 마크다운은 사람이 읽기도 쉽고, Git으로 관리하기도 쉽고, 나중에 다른 도구로 옮기기도 쉽다. 특정 앱의 포맷에 갇히지 않는다는 점도 좋았다.

이때부터 ATLAS는 단순히 대화 기록을 저장하는 앱이 아니라, 내가 가진 문서들을 AI가 읽을 수 있게 하는 환경으로 조금씩 바뀌기 시작했다.

아직 지식 구조를 깊게 설계한 단계는 아니었다.
하지만 방향은 보였다.

AI와 대화하는 화면이 있고, 그 뒤에 내 파일과 기록이 있고, 그 둘이 연결될 수 있다면, 적어도 맥북 안에 갇힌 환경보다는 훨씬 나아질 것 같았다.

A chat surface over your files

첫 번째 버전의 의미

초기 ATLAS는 완성도가 높은 앱은 아니었다.

채팅 UI도 단순했고, 파일 접근도 제한적이었고, 지식 구조도 아직 명확하지 않았다. 지금 생각하는 노드, 관계, lesson, decision 같은 개념이 제대로 정리된 것도 아니었다.

하지만 중요한 변화는 있었다.

내 AI 작업 환경이 처음으로 맥북 밖에서 돌아가기 시작했다.

폰에서도 접근할 수 있고, 서버는 계속 켜져 있고, 대화는 저장되고, 내 파일과 연결될 가능성이 생겼다. 아직 엉성했지만, 적어도 방향은 분명했다.

로컬에 있던 AI 사용 습관을 클라우드 위의 개인 작업 환경으로 옮기는 것.

이게 ATLAS의 첫 번째 구현이었다.

The first version: rough, but outside the laptop

다음 문제

그런데 직접 만들어서 쓰려고 하니 바로 다음 문제가 보였다.

단순히 Claude API와 대화하는 화면만으로는 부족했다.
내가 원했던 기능들은 채팅창 안에만 머물지 않았다.

특히 영어 학습 기능이 그랬다.

나는 AI와 대화하는 과정에서 자연스럽게 영어 표현을 배우고 싶었다. 내가 쓴 한국어를 영어로 바꿔보고, AI 답변 속 표현을 하이라이트하고, 모르는 표현을 저장하고, 나중에 다시 복습하는 식의 레이어를 붙이고 싶었다.

그런 기능을 만들려면 단순한 채팅창으로는 부족했다.
메인 답변 옆에 별도의 분석 결과가 붙어야 했고, 저장 버튼과 하이라이트 UI가 필요했고, 대화와 단어장이 연결되어야 했다.

결국 ATLAS는 단순한 개인 AI 앱에서 조금 더 복잡한 구조로 넘어가게 됐다.

다음 단계는 영어 레이어였다.

Why a chat window wasn't enough