최근 작업하던 프로젝트 폴더들을 한번 훑어 봤다. 블로그도 있고, 영상 제작 도구도 있고, 작은 웹서비스와 데스크톱 앱도 있다. 하나씩 만들 때는 그저 당장 필요한 것을 고른다고 생각했는데, 모아 놓으니 반복되는 선택이 꽤 보였다.
TypeScript를 공통 언어로 쓰고, 화면은 가능한 가볍게 배포한다. 서버가 꼭 필요해지면 Cloudflare Workers와 D1을 붙인다. React는 모든 곳에 깔지 않고, 복잡한 화면이나 영상처럼 이유가 분명할 때만 쓴다. 그리고 이제는 AI와 함께 개발하기 좋은가도 선택 기준에 들어왔다.
그러니까 이 글은 “요즘은 이 스택이 정답이다” 같은 이야기는 아니다. 지금의 내가 몇 개 프로젝트를 만들면서 어느 정도까지의 복잡도를 감당하고 싶은지 정리한 기록에 가깝다.
공통 언어는 TypeScript가 됐다
웹사이트, CLI, Cloudflare Functions, 영상 렌더링 파이프라인까지 TypeScript가 등장한다. 물론 모든 것을 TypeScript로 만들 수 있다는 뜻은 아니다. ssagal 프로젝트처럼 Rust와 Tauri가 더 자연스러운 경우도 있다.
그런데 작은 서비스를 여럿 만들다 보니, 언어를 하나 더 고르는 비용이 생각보다 컸다. 문법을 다시 익히는 것보다 패키지 관리, 타입, 배포 스크립트, 에러를 찾는 방식이 제각각이 되는 쪽이 더 귀찮았다. TypeScript는 웹의 언어이기도 하지만, 나에게는 여러 프로젝트를 오갈 때의 기본 작업 언어가 된 것 같다.
그래서 CLI와 영상 파이프라인도 TypeScript로 묶었고, Remotion 작업도 같은 흐름 위에 있다. 프론트엔드와 그 주변의 자동화 코드를 다른 세계처럼 느끼지 않아도 되는 점이 좋다.
AI와 같이 만들 수 있는가
요즘 기술을 고를 때 AI를 빼고 생각하기는 어렵다. 코드를 처음부터 다 맡긴다는 뜻은 아니다. 구현을 시작하고, 문서를 찾고, 테스트를 만들고, 막힌 에러를 풀어 가는 과정에 이미 AI가 깊게 들어와 있다.
그래서 프로젝트의 목표가 같은 선상에 있다면, AI가 비교적 안정적으로 읽고 쓰고 고칠 수 있는 언어와 도구를 먼저 보게 된다. 문서와 예제가 충분하고, 널리 쓰이는 패키지 생태계가 있는가도 중요해졌다. 내가 모르는 기술을 배워 가는 시간뿐 아니라, AI가 헛다리를 짚은 결과를 검증하고 바로잡는 시간도 결국 내 몫이기 때문이다.
신생 언어나 아직 AI가 다루기 어려운 도구가 나쁘다는 이야기는 아니다. 그 도구가 아니면 만들 수 없는 것이 있다면 당연히 써야 한다. 다만 프로젝트를 완성하는 것이 목표라면, 새로운 기술이라는 이유만으로 선택 순위를 올리지는 않게 됐다. 지금은 내가 이해할 수 있고 AI와 함께 끝까지 밀고 갈 수 있는 쪽을 우선으로 둔다.
화면은 먼저 가볍게 만든다
콘텐츠를 보여주는 화면에는 거대한 애플리케이션이 꼭 필요하지 않은 경우가 많았다.
이 블로그는 Hugo로 정적 페이지를 만든다. 여러 기록 사이트는 Astro로 만들고, wedding 모바일 청첩장 프로젝트는 바닐라 HTML, CSS, JavaScript로 시작했다. 만드는 방식은 조금씩 달라도 공통점은 있다. 글이나 정보가 중심인 페이지는 먼저 정적으로 만들고, 배포도 최대한 단순하게 가져간다는 점이다.
이 선택이 멋있어서라기보다, 운영해야 할 것이 줄어들기 때문이다. 서버 프로세스가 계속 살아 있는지 신경 쓰지 않아도 되고, 콘텐츠를 고친 뒤 결과를 예측하기도 쉽다. 블로그를 Ghost에서 다시 정적 사이트로 옮긴 것도 비슷한 이유였다. 기능이 많은 도구가 항상 글을 더 자주 쓰게 해 주지는 않았다.
물론 정적 사이트가 모든 문제를 해결하지는 않는다. 로그인한 사용자의 상태를 길게 들고 가거나, 화면이 계속 바뀌는 서비스라면 다른 선택을 해야 할 것이다. 다만 처음부터 그 복잡도를 들고 시작할 이유는 많지 않았다.
서버는 필요한 만큼만 둔다
정적 페이지로 시작해도 결국 데이터가 필요한 순간은 온다. wedding 모바일 청첩장 프로젝트의 방명록에는 저장할 곳이 필요했고, 보고서 작성 웹 앱에는 데이터와 파일, 비동기 작업이 필요했다.
이런 경우에는 Cloudflare Pages와 Workers, D1 같은 도구가 잘 맞았다. 사이트를 배포하는 흐름에서 API를 붙일 수 있고, 작은 데이터베이스나 큐도 같은 곳에서 다룰 수 있다. 별도 서버 한 대를 먼저 열어 두고 관리하는 것보다 시작의 문턱이 낮다.
여기에도 기준은 있다. “서버가 없다"가 목표는 아니다. 서버를 운영해야 할 이유가 생기기 전까지는, 운영할 서버를 만들지 않는 쪽에 가깝다. 데이터량이나 처리 시간이 커지면 이 선택도 다시 봐야 한다. 지금 규모에서는 필요한 만큼의 서버리스 기능이 가장 편했다.
React는 필요한 곳에만 쓴다
한동안 React는 웹 프로젝트의 기본값처럼 느껴졌다. 그런데 모든 페이지가 같은 정도의 상태 관리와 컴포넌트 조합을 요구하지는 않는다.
React와 Remotion은 영상 작업에서 확실히 값을 한다. 프레임, 자막, 오디오 타이밍, 시뮬레이션 결과를 컴포지션으로 조합해야 하는 영상이 있다. 또 다른 영상 파이프라인은 나레이션 길이에 맞춰 씬의 길이를 계산하고, 자막과 BGM을 맞춘다. 이 정도가 되면 UI를 선언적으로 쪼개고 재사용하는 방식이 오히려 단순해진다.
반대로 ssagal 프로젝트는 화면 위에 작은 버튼 하나를 띄우고 소리를 재생하는 데스크톱 앱이다. 여기에는 React를 올리지 않았다. 순수 HTML, CSS, JavaScript로 화면과 재생 로직을 끝내고, Rust/Tauri는 윈도우를 만드는 일에 집중하게 했다. 프레임워크를 쓰지 않은 것도 기술을 포기한 것이 아니라, 필요한 경계를 작게 잡은 선택이었다.
모노레포도 늘 기본값은 아니다
여러 앱이 같은 디자인 시스템이나 타입을 공유하면 모노레포는 편하다. 여러 사이트가 하나의 디자인 시스템을 공유하는 경우나, CLI·공용 스키마·외부 서비스 연동·영상 패키지가 함께 움직이는 경우에는 특히 그렇다.
pnpm workspace로 의존성을 한 번에 관리하고, 공용 코드를 패키지로 나눠 두면 바꿀 곳을 찾기가 쉬워진다. 대신 아주 작은 단일 프로젝트까지 굳이 모노레포로 만들 필요는 없다고 생각한다. 구조는 나중에 필요해졌을 때 옮길 수 있지만, 처음부터 늘어난 폴더와 설정은 계속 관리해야 하기 때문이다.
결국은 운영할 수 있는 크기
프로젝트를 훑어 보고 나서 남은 것은 특정 프레임워크의 승패가 아니었다. 나는 TypeScript를 자주 쓰지만 Rust가 더 맞는 앱도 만들었다. 정적 사이트를 선호하지만, 데이터가 필요할 때는 Workers와 D1을 붙였다. React를 쓰기도 하고, 화면이 단순하면 쓰지 않기도 했다.
결국 기술 스택은 기능 목록보다 내가 운영할 수 있는 복잡도의 크기로 고르게 되는 것 같다. 이제 그 안에는 AI와 함께 개발하고 검증할 수 있는가도 들어간다. 지금의 기준이 몇 년 뒤에도 그대로일지는 모르겠다. 그래도 적어도 지금은, 새 도구를 하나 더 넣기 전에 그 도구가 없으면 정말 풀리지 않는 문제가 무엇인지 먼저 보려고 한다.
다음 글에서는 이 기준이 실제 선택으로 이어진 두 사례를 더 자세히 적어 보려고 한다. Remotion을 고른 이유와, Tauri 앱에서 React를 쓰지 않은 이유다.