AI가 SaaS 코드를 작성할 수 있지만, 코드의 위치를 결정하는 것은 여전히 어렵다.
요약
AI 코딩 에이전트가 SaaS 개발 속도를 혁신적으로 높이고 있지만, 개별 기능 구현을 넘어 전체 코드베이스의 아키텍처를 설계하는 것이 핵심 과제로 남아있습니다. 성공적인 시스템은 모듈 간의 경계(seams)와 구조화된 컨텍스트를 명확히 정의하여 확장성과 유지보수성을 확보해야 합니다.
핵심 포인트
- AI 에이전트는 개별 기능 구현 속도를 높이지만, 아키텍처 설계는 여전히 인간의 영역이다.
- 핵심은 '어디에' 코드를 배치할지 결정하는 구조적 문제다 (비즈니스 로직 위치 등).
- 명확한 경계(boundaries)가 없으면 제공업체별 코드가 비즈니스 로직에 스며들어 유지보수 비용이 증가한다.
- SaaS 아키텍처에서는 모듈, 어댑터, 경계를 명시적으로 정의하는 것이 중요하다.
AI 코딩 에이전트가 SaaS 아키텍처에 어떤 변화를 가져오는지, 그리고 모듈(modules), 어댑터(adapters), 경계(boundaries), 구조화된 컨텍스트(structured context)가 왜 여전히 중요한지.
AI는 여러분이 SaaS 프로젝트를 시작할 수 있는 속도를 바꿨습니다.
인증(Authentication), 결제(billing), 대시보드(dashboards), 이메일, 백그라운드 작업(background jobs), AI 기능, 배포(deployment) 등은 예전에는 초기 개발 주기에서 큰 부분을 차지했습니다. 오늘날 AI 코딩 에이전트는 짧은 시간 안에 그 작업의 놀라울 정도로 많은 부분을 생성할 수 있습니다.
이는 유용합니다.
하지만 이것은 어려운 결정들이 어디에 있는지를 바꿉니다.
문제는 개별적인 조각들을 작성하는 것보다, 코드베이스를 변경하기 비싸지 않게 만들기 전에 그 조각들이 어떻게 함께 맞물려 돌아갈지 결정하는 것에 더 가까워지고 있습니다.
에이전트는 Stripe를 추가할 수 있습니다.
인증을 추가할 수 있습니다.
AI 채팅 엔드포인트(endpoint)를 추가할 수 있습니다.
백그라운드 워커(background worker)를 추가할 수 있습니다.
또 다른 데이터베이스 테이블을 추가할 수 있습니다.
각 변화는 그 자체로는 합리적일 수 있습니다.
문제는 보통 '이음매(seams)'에서 시작됩니다.
비즈니스 로직은 어디에 존재해야 할까요?
어떤 레이어가 데이터베이스에 접근할 수 있을까요?
제공업체별 코드(provider-specific code)는 어디에 속해야 할까요?
결제가 애플리케이션의 나머지 부분과 어떻게 격리되어 유지될 수 있을까요?
제공업체를 교체하게 되면 무슨 일이 발생할까요?
이 모든 것을 변경하기 전에 에이전트가 무엇을 알아야 할까요?
이것들은 아키텍처 질문들입니다. 더 빠른 코드 생성은 이 질문들을 사라지게 만들지 않습니다.

이음매(seams)에서 비용이 발생한다
라우트(route)가 서비스(service)를 호출하고, 서비스는 리포지토리(repository)와 통신하며, 리포지토리는 데이터베이스에 통신하는 SaaS 애플리케이션을 상상해 보세요.
그 구조는 이해하기 충분히 쉽습니다.
여기에 결제 제공업체(payment provider)를 추가합니다.
그리고 인증 제공업체를 추가합니다.
다음으로 오브젝트 스토리지(object storage)를 추가합니다.
다음으로 백그라운드 작업을 추가합니다.
다음으로 임베딩(embeddings) 및 벡터 검색(vector search)을 추가합니다.
명확한 경계가 없으면, 제공업체별 코드가 제품 계층으로 새어 나오는 경향이 있습니다. 비즈니스 로직에 Stripe 호출이 나타납니다. 스토리지 SDK를 기능에서 직접 가져옵니다. 큐 구현이 애플리케이션 서비스의 일부가 됩니다.
애플리케이션은 여전히 작동합니다.
하지만 다음 변경 사항은 더 비용이 많이 들게 됩니다.
이것이 바로 아키텍처가 초반에는 괜찮다고 느껴지지만 나중에는 고통스러워지는 이유입니다. 첫 번째 구현은 저렴합니다. 그 주변에 만든 경계(seams)는 그렇지 않습니다.
저는 이 문제와 관련하여 Codapult를 생각하기 시작했습니다.
모듈이 경계를 가질 때 더 유용하다
SaaS 기반은 방대한 기능 목록을 노출할 수 있습니다.
그 목록 자체가 흥미로운 부분은 아닙니다.
유용한 질문은 해당 기능들이 전체 애플리케이션을 하나의 상호 연결된 기능 세트로 만들지 않으면서 존재할 수 있는지 여부입니다.
Codapult는 자체 라우트, 컴포넌트, 코드 및 데이터베이스 조각이 있는 모듈로 기능을 구성합니다. 설정 마법사(setup wizard)를 사용하면 필요하지 않은 모듈을 그 파일, 라우트, 스키마 조각, 임포트와 함께 영구적으로 제거할 수 있습니다. 남아있는 모듈은 코드는 유지하되 아직 기능을 노출하고 싶지 않을 때 구성(configuration)을 통해 비활성화할 수도 있습니다. Codapult 모듈 아키텍처
이는 프로젝트에 두 가지 유용한 속성을 제공합니다:
- 유연한 시작 표면 (Flexible Starting Surface): 광범위한 기반으로 시작할 수 있습니다.
- 선택적 복잡성 (Selective Complexity): 제품 결정이 명확해짐에 따라 결과 애플리케이션을 더 작게 만들 수 있습니다.
예를 들어, 한 제품은 팀(teams), 결제(billing), 관리자 기능(admin features)이 필요하지만 블로그는 필요하지 않을 수 있습니다. 다른 제품은 AI와 파일 업로드가 필요하지만 멀티테넌시(multi-tenancy)는 필요하지 않을 수 있습니다. 또 다른 제품은 간단한 공개 애플리케이션으로 시작하여 나중에 계정 기능을 추가할 수도 있습니다.
핵심은 모든 모듈을 배포하는 것이 아닙니다.
핵심은 그것과 함께 온 모든 것에 영구적인 의존성을 만들지 않으면서 구조화된 시작점을 갖는 것입니다.
어댑터가 인프라를 엣지에 유지하다
같은 아이디어가 제공업체에도 적용됩니다.
비즈니스 로직은 어떤 공급업체가 인증, 결제, 스토리지 또는 다른 인프라 관련 작업을 처리하는지 알 필요가 없습니다.
공급업체는 구현 세부 사항(implementation detail)일 뿐입니다.
이것이 바로 어댑터 경계(adapter boundaries)의 이유입니다.
제품 코드가 애플리케이션 전체에서 Stripe API를 호출하도록 허용하는 대신, 제품은 내부 계약(internal contract)과 통신합니다. Stripe 구현체는 그 계약의 가장자리(edge)에 존재합니다.
이러한 모델은 인증, 스토리지, 백그라운드 작업(background jobs), 알림(notifications), 임베딩(embeddings), 벡터 스토리지(vector storage) 등에도 사용될 수 있습니다.
Codapult는 현재 Better Auth 및 Kinde를 통한 인증, Stripe, Polar, Lemon Squeezy를 통한 결제, PostgreSQL 및 Turso를 통한 데이터베이스, 그리고 파일용 S3, R2, 로컬 스토리지를 포함하여 여러 경계에 대한 교체 가능한 구현체를 제공합니다. Codapult 아키텍처
이점은 환경 변수를 변경할 수 있다는 것보다 훨씬 큽니다.
중요한 결정은 공급업체가 존재할 장소(somewhere to live)를 갖는 것입니다.
만약 공급업체별 API가 제품 코드에 새어 들어가면, 인프라 교체는 애플리케이션 전체에서 검색하고 재작성하는 작업이 됩니다.
하지만 처음부터 경계(seam)가 존재한다면, 인프라를 변경하는 것은 훨씬 더 국한된 변화입니다.
저는 필요하기 전에 그 경계를 명시적으로 만드는 것을 선호합니다.
비활성화된 기능과 제거된 기능은 같지 않다
코드베이스가 커질수록 더 중요해지는 또 다른 아키텍처적 구분이 있습니다.
무언가를 끄는 것은 되돌릴 수 있습니다(reversible).
그것을 제거하는 것은 프로젝트의 형태를 바꿉니다.
Codapult는 이 두 가지를 모두 지원합니다.
모듈은 환경 플래그를 통해 비활성화된 상태로 리포지토리에 남아 있을 수 있습니다. 또는 설정 마법사(setup wizard)가 그것을 완전히 제거할 수도 있습니다.
이는 기반 구조가 사용자 대신 영구적인 결정을 내릴 필요가 없다는 것을 의미합니다.
불확실할 때는 기능을 유지할 수 있습니다.
하지만 제품의 범위를 벗어난 것이 확실하다면 제거할 수 있습니다.
이것은 사소한 구현 세부 사항처럼 들리지만, 실제적인 영향이 있습니다. 즉, 제품이 더 구체화됨에 따라 코드베이스가 단순해질 수 있다는 것입니다.
기반 구조는 이러한 변화를 돕는 역할을 해야 합니다.
AI 역시 아키텍처 인터페이스가 필요하다
에이전트에게 파일에서 레포지토리를 역공학(reverse-engineer)하도록 맡기는 방식으로 AI 지원 개발을 위한 기반 구조를 구축하는 것에는 명백한 문제가 있습니다.
새 프로젝트에 합류한 인간 개발자는 그 구조를 파악하는 데 며칠이 걸릴 수 있습니다.
반면, 에이전트는 일반적으로 주어진 컨텍스트에서 시작합니다.
따라서 구조화된 컨텍스트가 훨씬 더 중요해집니다.
Codapult는 AGENTS.md, CLAUDE.md, GEMINI.md와 Cursor 규칙, 그리고 Codapult MCP 서버를 포함하고 있습니다. MCP 레이어는 29개의 도구(tools), 8개의 리소스(resources), 2개의 프롬프트 템플릿을 통해 프로젝트의 아키텍처, 데이터베이스 스키마, 환경, 설치된 모듈, 배포 상태, 컨벤션 및 유효성 검사 등에 대한 구조화된 정보를 노출합니다. Codapult MCP
목표는 에이전트를 더 똑똑하게 만드는 것이 아닙니다.
코드를 변경하기 전에 에이전트에게 더 나은 지도를 제공하는 것입니다.
이러한 구분 자체가 중요합니다.
에이전트는 불완전한 컨텍스트로부터도 올바른 코드를 생성할 수 있습니다.
하지만 주변 시스템에는 완전히 틀리지만, 그 자체로는 완벽하게 유효한 코드도 생성할 수 있습니다.
컨텍스트는 유용하지만, 강제력은 아니다
여기에는 중요한 한계가 존재합니다.
문서화나 에이전트 지침은 프로젝트가 무엇을 기대하는지 설명해 줄 수는 있지만,
미래의 변경 사항이 그 기대를 따를 것이라고 보장하지는 않습니다.
그렇기 때문에 저는 아키텍처를 단순히 프롬프트 지침들의 집합으로 축소할 수 없다고 생각합니다.
에이전트는 컨벤션을 정확하게 읽으면서도 잘못된 의존성을 구현할 수 있습니다.
특정 경계(boundary)에서 도달해서는 안 되는 컴포넌트를 호출하면서도 레이어의 일반적인 방향을 따를 수 있습니다.
새로운 기능에 대해서는 잘못되었지만 우연히 맞는 기존 패턴을 재사용할 수도 있습니다.
따라서 기반 구조는 단순한 지침 이상의 것이 필요합니다.
인간과 에이전트 모두에게 의도된 구조가 이해될 수 있도록 명시적인 경계(seams)가 필요합니다.
그리고 프로젝트 위에는 여전히 검증 과정이 필요합니다.
프로덕션 아키텍처는 결정의 일부입니다
같은 원칙이 배포에도 적용됩니다.
하나의 노트북에서 실행되는 스타터만으로 코딩을 시작하기에 충분합니다.
SaaS 기반 구조는 다른 질문에 답해야 합니다. 즉, 애플리케이션이 실제 시스템이 되어야 할 때 무슨 일이 발생하는가?
Codapult은 Vercel을 지원할 뿐만 아니라 Docker, AWS Terraform 및 Pulumi 템플릿, Kubernetes/Helm 배포 경로, 외부 데이터베이스, 백그라운드 워커, 스토리지 어댑터, 관측 가능성(observability) 구성을 포함합니다. Codapult 자체 호스팅
이것이 모든 프로젝트가 Kubernetes를 필요로 한다는 의미는 아닙니다.
이는 Kubernetes가 시작점의 제약 사항이라기보다는 선택지라는 것을 의미합니다.
같은 추론은 데이터베이스, 스토리지 및 배포 제공업체에도 적용됩니다.
아키텍처가 경계(seam)를 정의해야 합니다.
제품이 그 뒤에 무엇을 둘지 결정해야 합니다.
테스트는 하나의 질문에 답합니다. 아키텍처는 다른 질문에 답합니다.
테스트는 여전히 필수적입니다.
단위(Unit) 및 통합(Integration) 테스트는 특정 동작이 작동하는지 알려줍니다.
엔드투엔드(End-to-end) 테스트는 더 큰 사용자 흐름이 작동하는지 알려줍니다.
타입 검사(Type checking)와 린팅(Linting)은 다른 종류의 문제를 포착합니다.
하지만 이들 중 어느 것도 다음과 같은 다른 질문에 자동으로 답하지 못합니다:
이 변경 사항이 여전히 시스템에 적합한가?
변경 사항은 아키텍처를 악화시키면서도 동작을 보존할 수 있습니다.
서비스는 자신이 알 필요가 없었던 레이어에서 무언가를 가져오기 시작할 수 있습니다.
특정 기능은 기존의 애플리케이션 경계를 우회할 수 있습니다.
프로바이더 SDK는 점차 제품 코드 내부로 퍼져나갈 수 있습니다.
애플리케이션은 여전히 컴파일되고 테스트도 통과할 수 있습니다.
이것은 테스트에 반대하는 주장이 아닙니다.
이는 아키텍처 제약(architectural constraints)을 별도의 관심사로 다루어야 한다는 주장입니다.
Codapult에게 바라는 점
이것이 제가 Codapult가 첫날 포함되는 파일 수에 초점을 맞춘 또 다른 스타터가 되기를 원하지 않았던 이유입니다.
유용한 부분은 그 파일들을 둘러싼 구조입니다:
- **모듈(Modules)**은 기능에 정의된 위치를 부여합니다.
- **어댑터(Adapters)**는 인프라 결정이 경계면(edge)에 머무르게 합니다.
- **모듈 제거(Module Removal)**는 모든 것을 영원히 유지하는 대신 사용하지 않는 기능을 정리할 수 있게 합니다.
- **에이전트 컨텍스트 (Agent Context, MCP)**는 AI 어시스턴트가 아키텍처와 규칙을 더 쉽게 검사하도록 만듭니다.
- **배포 인프라(Deployment Infrastructure)**는 사후 고려 사항이 아니라 기반의 일부로서 확장 가능한 경로를 제공합니다.
- **테스트 스위트(Testing Suite)**는 검증을 개발 루프 내에 직접 유지시킵니다.
현재 기반에는 70개 이상의 프로덕션 준비가 된 모듈이 포함되어 있지만, 진정한 가치는 그 개수가 아닙니다. 그것은 이러한 기능들이 제품과 분리되지 않으면서 이미 통합되어 있다는 점입니다. Codapult modules
AI는 아키텍처를 더 눈에 띄게 만들었다
저는 AI가 소프트웨어 아키텍처를 덜 중요하게 만든다고 생각하지 않습니다.
오히려 잘못된 경계(bad boundaries)들이 쌓이도록 더 쉽게 만듭니다.
구현 비용이 저렴해지면서, 잘못된 결정의 비용이 명확해지기 전에 코드를 더 많이 만들 수 있습니다.
이는 초기 프로젝트의 경제성을 변화시킵니다.
더 이상 비싼 것은 반드시 기능을 작성하는 것이 아닙니다.
그것은 기능이 어디에 속해야 할지 결정하고, 시스템의 나머지 부분이 당신이 추가하는 모든 구현 세부 사항에 대해 학습할 필요가 없도록 보장하는 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
