우리는 소프트웨어 엔지니어링 문제가 아니라, 플랫폼 엔지니어링 문제를 겪고 있습니다
요약
새로운 제품 개발 과정에서 비즈니스 로직 작성보다 인프라 및 플랫폼 설정에 더 많은 시간이 소요되는 플랫폼 엔지니어링의 문제를 다룹니다. 개발자가 코드 구현에 집중하지 못하게 만드는 기술적 부채와 결정 지연의 병목 현상을 지적합니다.
핵심 포인트
- 비즈니스 로직 구현 전 플랫폼 설정 단계에서 발생하는 병목 현상
- 디자인 시스템과 컴포넌트 라이브러리의 차이로 인한 일관성 문제
- 마이크로 프론트엔드 도입 시 발생하는 복잡한 환경 설정 이슈
- AI 도구(Claude Code)가 있어도 기반 인프라가 없으면 생산성 향상에 한계가 있음
지난달 나는 새로운 제품을 만들기 위해 착수했지만, 일주일이 꼬박 지나도록 단 하나의 기능도 출시하지 못했다.
내가 느려서가 아니다. 작업이 어려워서도 아니다. 누군가 비즈니스 로직 (business logic)을 단 한 줄이라도 쓰기 전에, 우리 팀은 회사가 수년 전에 결정했어야 할 수십 가지 사항들을 다시 결정해야만 했기 때문이다.
나는 거의 5년 동안 풀스택 개발자 (full-stack developer)로 일해 왔다. WordPress 개발자, QA, 프론트엔드 (frontend), 백엔드 (backend), 솔루션 아키텍트 (solution architect), 파운딩 엔지니어 (founding engineer) 등 대부분의 역할을 수행하기에 충분한 시간이었다. 나는 20개 이상의 웹 애플리케이션과 몇 개의 모바일 애플리케이션을 구축했다. 그중 일부는 진심으로 자랑스럽다. 5초마다 PLC를 폴링 (poll)하여 산업 장비를 감시하는 시스템, 트리 기반 그래프를 사용하여 전 세계의 공격을 실시간으로 매핑하는 사이버 보안 대시보드, FIFA를 포함한 라이브 이벤트를 수백만 명의 동시 시청자에게 스트리밍하는 OTT 플랫폼 등이 그것이다. 현재 나는 엔터프라이즈 공급망 금융 소프트웨어를 다루고 있다.
따라서 그 새로운 제품에서 가장 힘들었던 부분이 코드와 아무런 관련이 없다고 말할 때, 내 말이 어떻게 들릴지 잘 알고 있다. 설명해 보겠다.
사라져 버린 일주일
이 실험은 의도적으로 야심 차게 계획되었다. 나는 새로운 애플리케이션 — 궁극적으로는 더 큰 엔터프라이즈 플랫폼 내부의 마이크로 프론트엔드 (microfrontend) — 을 구축하고 싶었지만, 그 대부분을 직접 작성하고 싶지는 않았다. 나는 Claude Code가 구현을 담당하는 동안 나는 아키텍트 (architect)로서 검토, 테스트, 도전, 개선, 반복하는 역할을 하고 싶었다.
그 부분은 잘 작동했다. AI가 병목 현상 (bottleneck)의 원인은 아니었다. 병목 현상은 첫 번째 기능이 나오기 전의 모든 과정이었다.
React를 사용해야 할까? Vite를 쓸까? 부모 앱이 여전히 사용 중이니 Create React App을 유지해야 할까, 아니면 둘 다 마이그레이션해야 할까? 부모 앱은 자식 앱을 어떻게 소비하며, 로컬 개발 (local development)은 여전히 작동할까? 인증 (authentication)은 여전히 작동할까? 라우팅 (routing)은? 기존 앱이 사용하지 않는데, 하나의 제품을 두 가지 표준으로 분리하게 될 것을 알면서도 TypeScript를 도입해야 할까? 또한 앱은 기존 제품과 네이티브하게 느껴져야 했다. 동일한 간격 (spacing), 타이포그래피 (typography), 색상 (colors), 상호작용 (interactions)을 유지해야 했지만, 문제는 회사가 디자인 시스템 (design system)이 아닌 컴포넌트 라이브러리 (component library)를 가지고 있었다는 점이다. 이 둘은 같은 것이 아니다. 컴포넌트 라이브러리는 버튼을 제공하지만, 디자인 시스템은 일관성 (consistency)을 제공한다.
그다음 설정 (setup) 단계가 이어졌다: 저장소 (repositories), 브랜치 전략 (branch strategy), 폴더 구조 (folder structure), ESLint, Prettier, Husky, 커밋 훅 (commit hooks), 모듈 페더레이션 (Module Federation), 인증 통합 (auth integration), 환경 설정 (environment config), 빌드 스크립트 (build scripts), 공유 패키지 (shared packages), 권한 처리 (permission handling), 보일러플레이트 (boilerplate).
거의 일주일이 지났지만, 우리는 아직 단 한 줄의 비즈니스 로직 (business logic)도 작성하지 못했다. 고객은 얻은 것이 없었다. 제품도 얻은 것이 없었다. 우리는 단순히 기반을 다졌을 뿐이었다. 그리고 전 과정에서 AI의 도움을 받았음에도 불구하고, 고객의 문제를 해결하는 대신 인프라 (infrastructure)에 대해 논쟁하느라 수천 개의 토큰을 낭비했다. 아직 CI/CD, 모니터링 (monitoring), 로깅 (logging), 배포 (deployment), 또는 프로덕션 (production)은 건드리지도 못한 상태였다.
가장 뼈아픈 부분은 이것이다: 그 결정들 중 거의 대부분은 수년 전, 회사 전체를 위해 단 한 번만 내려졌어야 했다. 대신, 모든 새로운 프로젝트가 그것들을 처음부터 다시 재심리 (re-litigates)하고 있다.
이것은 소프트웨어 엔지니어링 문제가 아니다. 플랫폼 엔지니어링 (platform engineering) 문제다.
"이미 해결된 것"이 느껴져야 하는 모습
저장소를 생성하는 것이 당신의 전체 설정 작업인 회사에 합류한다고 상상해 보라.
프레임워크는 이미 선택되어 있습니다. TypeScript는 필수이며 설정도 완료되어 있습니다. Linting(린팅)과 formatting(포매팅) 규칙이 존재합니다. Git hooks(Git 훅)가 설치되어 있습니다. Testing(테스팅)이 연결되어 있습니다. CI/CD 파이프라인, Docker(도커) 이미지, 그리고 deployment(배포) 워크플로우가 스스로 결합됩니다. Monitoring(모니터링), logging(로깅), 그리고 security scanning(보안 스캐닝)이 기본적으로 실행됩니다. Secrets(비밀 정보)가 관리되고, branch protection(브랜치 보호)이 활성화되어 있으며, code owners(코드 소유자)가 설정되어 있고, preview environment(프리뷰 환경)가 자동으로 생성됩니다. Local development(로컬 개발)는 명령어 하나로 시작됩니다.
이 중 어느 것도 생소한 것이 아닙니다. 모든 요소는 이미 조직 내 어딘가에서 해결된 문제로서 존재하고 있습니다. 문제는 이것이 패키징(packaged)되어 있지 않다는 점입니다. 그래서 각 팀은 이를 조금씩 다른 방식으로 영원히 다시 구축하고 있습니다.
두 가지 원칙이 차이를 만듭니다.
1. 의도적으로 기술을 표준화하라 (Standardize your technology, on purpose)
하나 이상의 스택을 지원하는 것 자체는 잘못된 것이 아닙니다. 하지만 두 개나 세 개를 지원해야지, 열 개를 지원해서는 안 됩니다. 모든 계층에서 짧고 의도적인 메뉴를 만드십시오. 프론트엔드에서는 Vite, React, 또는 Angular; 백엔드에서는 Node, Spring Boot, 또는 Python; CI/CD 경로는 팀마다 하나씩이 아니라, 승인된 몇 가지 경로만을 두십시오. 그런 다음 유행이 아닌 적합성에 따라 선택하십시오. 이 중 우리가 이미 실행 중인 것은 무엇이며, 이 프로젝트의 규모, 대상, 속도에 적합한 것은 무엇입니까? 이 질문에 답할 수 있다면 프레임워크 논쟁은 시작되기도 전에 끝납니다. 세 가지로 구성된 메뉴는 감옥이 아닙니다. 그것은 논쟁을 멈추고 구축을 시작해도 좋다는 허가입니다.
엔지니어와 DevOps(데브옵스)가 이미 가장 잘 알고 있는 도구들을 중심으로 메뉴를 구성하고, 왜 각 도구가 선정되었는지 문서화하며, 이를 고수하십시오. 팀이 "React인가 Vue인가, Node인가 Spring인가"를 두고 다시 논쟁하며 보내는 모든 시간은 기능을 만드는 데 쓰이지 못한 시간입니다. 그리고 메뉴에 없는 선택은 내일이면 단 한 사람만이 배포할 수 있는 고립된 서비스(orphaned service)가 됩니다. 옵션을 좁히는 것은 단순히 개발 시간을 절약하는 것만이 아닙니다. 그것은 비용을 절감하며, 여러분이 구매할 수 있는 가장 저렴한 기술 부채(technical debt) 보험입니다.
2. 컴포넌트뿐만 아니라 인터페이스를 소유하라 (Own your interface, not just your components)
많은 팀이 "우리는 컴포넌트 라이브러리(component library)를 가지고 있다"라고 자랑스럽게 말합니다. 좋습니다. 하지만 개발자들이 Ant Design, Material UI, 또는 Bootstrap을 직접 임포트(import)하나요? 만약 그렇다면, 당신은 디자인 시스템(design system)을 가진 것이 아니라 공유 의존성(shared dependencies)을 가진 것입니다. 모든 컴포넌트는 자체적인 추상화 계층(abstraction layer)을 거쳐야 하며, 그래야만 하단의 서드파티 라이브러리(third-party library)를 교체할 수 있고 사용자 경험(experience)이 변하지 않습니다. 오늘은 Ant Design을 감싸고(wrap) 있더라도, 내일은 다른 것을 감싸게 될 것입니다. 당신의 앱은 이를 알아차려서는 안 됩니다. 이를 규율(discipline)이 아닌 린팅(linting)으로 강제하세요. 규율은 확장(scale)되지 않습니다.
동일한 논리가 스택(stack) 전체에 적용됩니다. 데이터베이스(databases), 자격 증명(credentials), 그리고 비밀값(secrets)은 매 프로젝트가 시작될 때마다 새로 협상해야 하는 대상이 되어서는 안 됩니다. 플랫폼이 이를 프로비저닝(provision)하고 앱에 임시 자격 증명을 전달해야 합니다. 도메인(domains)과 SSL은 제품 엔지니어(product engineer)의 업무가 되어서는 안 됩니다. 하지만 저는 보안이 취약한 HTTP나, 아무도 실제 도메인을 아직 프로비저닝하지 않아 가공되지 않은 AWS 로드 밸런서(load-balancer) URL을 통해 제품을 데모한 적이 있습니다. 유효한 HTTPS 엔드포인트(endpoint)를 기다리느라 며칠을 허비한 적도 있습니다. 그중 어느 것도 제품을 개선하지 못했습니다. 오직 출시를 지연시켰을 뿐입니다. 새로운 애플리케이션을 요청하면, 누군가 리포지토리(repo)를 클론(clone)하기 전에 도메인, 인증서(certificates), DNS, 그리고 로드 밸런서(load balancer)가 프로비저닝되어야 합니다.
모든 엔지니어가 계속해서 다시 만드는 것
제가 가장 많이 목격하는 단 하나의 안티 패턴(anti-pattern)이 있다면, 그것은 인증(authentication)입니다. 모든 새로운 앱은 로그인(login), 로그아웃(logout), 회원가입(registration), 비밀번호 재설정(password reset), 이메일 인증(email verification), JWT, 세션(sessions), MFA, 리프레시 토큰(refresh tokens), RBAC, 감사 로그(audit logs), 사용자 관리(user management)와 같은 동일한 흐름을 다시 만드는 것으로 시작합니다. 이러한 문제들은 이미 수년 전에 해결되었습니다. 그럼에도 불구하고 우리는 그것들을 다시 해결하고 있습니다.
한 제품에서는 멈췄습니다. 우리는 IIE라고 부르는 내부 플랫폼을 구축했습니다. 이것은 비즈니스 제품이 아니라 엔지니어링 제품입니다. 이 플랫폼의 전체 임무는 모든 애플리케이션이 결국 필요로 하는 것을 중앙 집중화하는 것입니다: 인증(authentication), 권한 부여(authorization), 사용자 관리(user management), 승인 워크플로우(approval workflows), 알림(notifications), 감사 추적(audit trails), 권한(permissions), SSO. 각 제품이 이 모든 것을 독립적으로 구현하는 대신, 앱들은 IIE와 한 번 통합합니다. 그 후에는 제품 팀은 신원(identity)에 대해 전혀 생각하지 않습니다. 비즈니스 문제에 대해서만 생각합니다.
내부 플랫폼의 진정한 가치는 바로 여기에 있습니다: 차별화되지 않은 노력(undifferentiated effort)을 제거하기 때문입니다. 따라서 모든 새로운 애플리케이션은 가장 어려운 기반이 이미 존재하기 때문에 결승선에 더 가까운 상태에서 시작할 수 있습니다. 관측 가능성(Observability)도 같은 방식으로 작동해야 합니다. 로깅(logging), 메트릭(metrics), 트레이싱(tracing), 헬스 체크(health checks), 오류 보고(error reporting), 대시보드(dashboards), 알림(alerts)이 프로젝트가 생성되는 순간부터 존재해야 하며, 개발자가 나중에 추가할 것을 기억해야 하는 것이 아닙니다.
새로운 문제: AI 역시 온보딩이 필요하다
이 문제에는 신선한 버전이 있으며, 빠르게 확산되고 있습니다.
예전에 Stack Overflow에서 한 번 차단당한 적이 있습니다. 너무 많은 '뻔한' 질문을 했기 때문입니다. 그때는 막힌 엔지니어가 선임의 책상으로 걸어갔고, 그것이 실패하면 밤새 디버거 안에서 시간을 보냈습니다. 오늘날 이 모든 질문은 프롬프트(prompt) 하나만 떨어져도 해결됩니다. 접근 가능한 지식의 양은 정말 놀랍습니다.
하지만 이제 모든 엔지니어는 다른 AI 스택을 실행합니다: Claude Code, Cursor, Copilot, Gemini, ChatGPT 등, 종종 여러 개를 동시에 사용합니다. 각 에이전트(agent)는 서로 다른 것을 알고 있습니다. 각 세션은 컨텍스트(context)를 잃습니다. 각 엔지니어는 다르게 프롬프트를 입력하므로, 각 AI는 다른 아키텍처 가정을 합니다. 결국 모순점들이 드러납니다: 중복 구현, 환각된 아키텍처(hallucinated architecture), AI가 만든 스파게티 코드입니다.
해답은 AI를 금지하는 것이 아닙니다. AI에게 컨텍스트 (Context)를 제공하는 것입니다. 모든 프로젝트는 아키텍처 결정 기록 (Architecture Decision Records, ADR), 살아있는 문서 (living documentation), 코딩 표준 (coding standards), 명시적인 에이전트 규칙 (explicit agent rules), 그리고 공유 용어집 (shared glossary)을 갖추고 있어야 합니다. AI 에이전트를 회사에 새로 합류한 신입 엔지니어처럼 대하십시오. 신입 사원이 온보딩 (onboarding)을 거치기도 전에 코드를 커밋 (commit)하게 두지 않듯이, AI에게도 그런 기대를 멈춰야 합니다.
플랫폼이 팀 전체의 가치를 지니는 이유
사람들은 왜 회사가 더 많은 기능을 출시하는 대신 플랫폼 팀에 자금을 지원해야 하는지 묻습니다. 그 답은 한 문장으로 요약됩니다: 프론트엔드 엔지니어는 한 팀을 더 빠르게 만들지만, 플랫폼 엔지니어는 모든 팀을 더 빠르게 만듭니다.
이것이 바로 레버리지 (leverage)입니다. 모든 프로젝트가 더 빨리 시작되고, 모든 배포 (deployment)가 더 쉬워지며, 모든 애플리케이션이 더 일관되게 성장하고, 모든 개발자가 보일러플레이트 (boilerplate) 대신 비즈니스 로직에 더 많은 시간을 할애할 수 있게 됩니다. 이는 엔지니어링 조직이 얻을 수 있는 가장 높은 수익 중 하나입니다.
물론 공짜는 아니며, 항상 최우선으로 취해야 할 조치도 아닙니다. 하나의 제품을 출시하는 5인 규모의 스타트업에는 내부 개발자 플랫폼 (internal developer platform)이 필요하지 않습니다. 그들에게 필요한 것은 고객이며, 그 상황에서의 성급한 플랫폼 팀 구축은 상상 속의 규모를 위해 오버헤드 (overhead)를 만드는 것일 뿐입니다. 하지만 여러 제품과 여러 팀을 병렬로 운영하는 순간 계산법은 달라집니다. 왜냐하면 그때부터 동일한 설정 비용을 반복해서 지불하게 되며, 거의 아무도 이를 측정하지 않기 때문입니다. 기업들은 스토리 포인트 (story points), 속도 (velocity), 버그 수를 추적합니다. 하지만 첫 번째 비즈니스 기능 구현까지 걸리는 시간 (time-to-first-business-feature), 반복적인 기술적 결정으로 인해 낭비되는 시간, 또는 인프라와 배포를 기다리며 소비되는 시간을 추적하는 기업은 거의 없습니다. 이 수치들은 엄청나지만, 거의 눈에 보이지 않습니다.
그 보이지 않는다는 점이 바로 함정입니다. 코드를 작성하는 것은 극적으로 쉬워졌습니다. 하지만 소프트웨어 엔지니어링은 그렇지 않았습니다. 병목 현상 (bottleneck)의 위치가 이동했기 때문입니다. 이제 병목은 알고리즘이나 구문 (syntax), 프레임워크 (frameworks)가 아닙니다. 그것은 조직 공학 (organizational engineering)입니다. 즉, 좋은 아이디어가 실제 출시된 기능이 되기까지의 속도를 결정하는 시스템, 표준, 그리고 기반 시설입니다.
최고의 내부 플랫폼 (Internal Platform)을 구축하는 기업은 단순히 엔지니어들의 만족도만 높이는 것이 아닙니다. 그들은 더 적은 버그와 더 높은 일관성을 유지하며 더 빠르게 제품을 출시할 것이며, 엔지니어들은 매번 새로운 프로젝트마다 동일한 기반 시설을 재구축하는 대신 마침내 자신들이 채용된 본연의 업무에 시간을 쏟을 수 있게 될 것입니다.
따라서 이 글을 읽고 있는 엔지니어분들에게 진짜 질문을 하나 던지겠습니다. 여러분의 회사에서는 "신규 제품 승인" 단계에서 "첫 비즈니스 기능 머지 (Merge)" 단계까지 얼마나 걸리나요? 하루인가요? 일주일인가요? 한 달인가요? 댓글로 그 수치를 남겨주세요. 그 범위가 어느 정도인지 알고 싶습니다. 왜냐하면 저는 그 수치가 우리 중 누구도 인정하고 싶어 하는 것보다 더 심각할 것이라고 의심하기 때문입니다.
저의 개인적인 경험을 바탕으로 직접 작성했습니다. 저는 구조를 다듬고 반복을 줄이기 위해 AI를 편집자로 활용했습니다. 이는 제가 동료에게 초안을 전달하는 방식과 동일합니다. 논거, 실전 경험담(War stories), 그리고 우리가 구축한 플랫폼은 모두 실제 사실입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기