본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 15. 14:55

AI는 몇 분 만에 코드를 배포하지만, 팀은 몇 달 동안 그 대가를 치릅니다. 그 이유를 알아봅시다.

요약

AI 코딩 어시스턴트가 생성한 코드는 속도는 빠르지만 강한 결합도로 인해 유지보수가 어렵습니다. 이를 해결하기 위해 컨트롤러, 서비스, 데이터 계층으로 역할을 분리하는 계층형 아키텍처 도입이 필수적입니다.

핵심 포인트

  • AI가 생성한 코드는 기능 구현은 빠르나 결합도가 높아 유지보수가 어려움
  • 강한 결합은 코드베이스를 복잡하게 만들고 변경 시 사이드 이펙트를 유발함
  • 계층형 아키텍처를 통해 HTTP 처리, 비즈니스 로직, 데이터 조작을 분리해야 함
  • 역할과 경계를 명확히 분리해야 코드의 확장성과 안정성을 확보할 수 있음

AI는 코드를 빠르게 작성합니다. 그것이 바로 문제입니다.

속도 자체가 적은 아닙니다. 유지보수 불가능한 속도가 문제입니다.

AI 코딩 어시스턴트(AI coding assistants)는 몇 분 만에 작동하는 엔드포인트(endpoint)를 배포할 수 있습니다. 하지만 기본적으로 그들이 할 수 없는 것은, 6개월 후에도 여전히 안전하게 수정할 수 있는 엔드포인트를 배포하는 것입니다.

저는 이 패턴을 반복해서 보았습니다. 팀은 빠르게 움직이고, 빠르게 배포하며, 빠르게 축하합니다. 그러다 코드베이스(codebase)는 사람들이 두려워하는 장소가 됩니다. 모든 변경 사항이 관련 없는 무언가를 망가뜨립니다. 아무도 마지막으로 그 코드를 건드린 사람이 되고 싶어 하지 않습니다.

원인은 거의 항상 복잡성(complexity) 때문이 아닙니다. 바로 결합도(coupling) 때문입니다.

AI가 실제로 당신에게 전달하는 것

어떤 AI 어시스턴트에게 주문 생성 엔드포인트를 만들어 달라고 요청해 보십시오. 결과는 다음과 같습니다:

app.post('/orders', async (req, res) => {
  const customer = await db.query(
    'SELECT * FROM customers WHERE id = ?',
...

작동합니다. 데모는 통과할 것입니다. PM(Product Manager)은 기뻐할 것입니다.

이제 하나의 함수에 쑤셔 넣어둔 것들을 보십시오: HTTP 핸들링(handling), 로우 SQL(raw SQL), 비즈니스 규칙 검증(business rule validation), 그리고 응답 포맷팅(response formatting)까지. 파일 하나에 경계도 없고, 분리도 없습니다.

이것이 강한 결합(tight coupling)입니다. 그리고 강한 결합은 도화선이 느린 시한폭탄과 같습니다.

모든 것을 다 하는 웨이터

웨이터가 주문을 받고, 식료품 저장실로 달려가 음식을 요리하고, 설거지를 하며, 재고까지 관리하는 식당을 상상해 보십시오.

테이블이 5개라면, 겨우겨우 버텨낼 수 있습니다.

테이블이 50개가 되면, 주문은 누락됩니다. 실수는 눈덩이처럼 불어납니다. 아무도 누가 책임자인지 모릅니다. 한 사람이 모든 것을 소유하고 있기 때문에 새로운 사람을 교육하는 것은 거의 불가능합니다.

이제 잘 운영되는 주방을 상상해 보십시오. 웨이터는 테이블을 담당합니다. 셰프는 주방을 운영합니다. 식료품 담당자는 식재료를 관리합니다. 각 역할은 명확한 경계가 있습니다. 각 사람은 독립적으로 교체될 수 있고, 교육될 수 있으며, 규모를 확장(scale)할 수 있습니다.

이것이 계층형 아키텍처(layered architecture)가 당신의 코드베이스에 해주는 역할입니다. 원리는 같지만, 매체가 다를 뿐입니다.

세 개의 계층. 세 개의 책임.

계층 1: 컨트롤러 (The Controller)

컨트롤러는 HTTP를 처리합니다. 그것이 유일한 업무입니다.

// controllers/orderController.js
async function createOrder(req, res) {
  const result = await orderService.createOrder(req.body);
...

컨트롤러는 요청을 받습니다. 서비스를 호출합니다. 응답을 반환합니다.

컨트롤러가 절대 하지 않는 일: SQL 작성, 비즈니스 규칙 (Business Rules) 강제, 또는 데이터베이스 (Database) 직접 조작. 컨트롤러가 주문을 승인해야 하는지 여부를 결정하기 시작하는 순간, 그것은 자신이 소유하지 않은 경계를 넘어선 것입니다.

컨트롤러는 번역기입니다. HTTP 입력, HTTP 출력. 그 외에는 아무것도 하지 않습니다.

계층 2: 서비스 계층 (The Service Layer)

이곳은 비즈니스 로직 (Business Logic)이 거주하는 곳입니다. 가격 책정 규칙, 신용 확인, 할인 로직, 승인 워크플로 (Workflows) 등 모든 것이 여기에 속합니다.

// services/orderService.js
async function createOrder(orderData) {
  const customer = await customerRepository.getById(orderData.customerId);
...

이 계층을 움직이는 하나의 질문은 이것입니다: 비즈니스는 어떻게 동작해야 하는가?

'데이터베이스는 어떻게 작동하는가?'가 아닙니다. 그것은 다른 누군가의 일입니다.

신용 한도 확인이 HTTP 상태 코드가 아닌 도메인 에러 (Domain Error)를 던지는 것에 주목하세요. 서비스 계층은 HTTP가 무엇인지 전혀 모릅니다. 그것은 설계 의도입니다.

계층 3: 리포지토리 (The Repository)

리포지토리는 데이터 액세스 (Data Access)를 소유합니다. SQL 쿼리, ORM 호출, 데이터베이스 특정 로직 등 이 모든 것은 오직 이곳에만 존재합니다.

// repositories/customerRepository.js
async function getById(customerId) {
  return db.query(
...

이 계층을 움직이는 하나의 질문은 이것입니다: 데이터를 어떻게 검색하거나 저장할 것인가?

'이 주문을 승인해야 하는가?'가 아닙니다. 그 답변은 두 계층 위에서 결정되어야 합니다.

실제로 얻게 되는 것

느슨한 결합 (Loose Coupling)은 계층이 구현 (Implementation)이 아닌 계약 (Contracts)에 의존함을 의미합니다. 호출 체인은 다음과 같습니다:

Controller
    ↓
Service
...

각 계층은 독립적으로 교체 가능합니다. 이것이 실무에서 여러분에게 제공하는 이점은 다음과 같습니다:

테스트 (Testing). 데이터베이스 없이도 비즈니스 로직을 테스트할 수 있습니다. 비즈니스 규칙을 모킹 (Mocking)하지 않고도 HTTP 동작을 테스트할 수 있습니다. 테스트는 빨라지고 목적이 명확해집니다.

리팩터링 (Refactoring). MySQL에서 PostgreSQL로 마이그레이션한다는 것은 리포지토리 (Repository) 계층 하나만을 수정한다는 의미입니다. 비즈니스 로직 (Business logic)은 건드리지 않습니다. 실수로 무언가 망가지는 일은 발생하지 않습니다.

온보딩 (Onboarding). 새로운 엔지니어는 비즈니스가 무엇을 하는지 이해하기 위해 서비스 계층 (Service layer)을 읽습니다. 데이터 액세스 (Data access)를 이해하기 위해 리포지토리를 읽습니다. 어떤 계층도 다른 계층으로 침범하지 않습니다.

AI 지원 개발 (AI-assisted development). 이 부분은 과소평가되어 있습니다. AI에게 리포지토리를 재생성해 달라고 요청할 때, AI는 비즈니스 로직을 건드리지 않고 이를 수행할 수 있습니다. 엔드포인트 (Endpoint)를 업데이트할 때 데이터베이스 코드를 다시 작성할 필요가 없습니다. 정의된 계층 구조는 AI 도구를 훨씬 더 정밀하게 만들고 위험성을 낮춥니다.

왜 AI는 난장판을 만드는가

AI 코딩 어시스턴트 (AI coding assistants)는 튜토리얼, 퀵스타트 가이드, 그리고 Stack Overflow의 답변들을 학습합니다. 그러한 코드들은 개념을 빠르게 보여주기 위해 작성된 것이지, 프로덕션 아키텍처 (Production architecture)를 모델링하기 위해 작성된 것이 아닙니다.

이러한 도구들은 눈에 보이는 결과를 얻기 위한 최단 경로를 최적화합니다. 출력 결과는 다음과 같은 모습일 것입니다:

Route
 ├─ Validation
 ├─ Business Rules
...

첫날은 생산적인 것처럼 느껴집니다. 결과물을 배포하고 있고, 잘 작동하니까요.

6개월 뒤: 모든 기능이 모든 파일을 건드립니다. 버그 수정이 부작용 (Side effects)을 일으킵니다. 무엇이 또 망가질지 아무도 모르기 때문에 아무도 리팩터링을 원하지 않습니다.

초기에 얻은 속도는 미래 팀원들의 정신 건강을 담보로 빌려온 것입니다.

난장판 없이 AI를 사용하는 방법

먼저 아키텍처를 정의하십시오. 그런 다음 AI에게 각 계층을 채워달라고 요청하십시오.

프롬프트 (Prompts)를 명확하게 작성하십시오:

"서비스 계층 (Service layer)만 생성해줘. 리포지토리 인터페이스 (Repository interface)가 존재한다고 가정해. 데이터베이스 쿼리는 제외하고, HTTP 핸들링도 제외해."

"orders 테이블을 위한 리포지토리를 작성해줘. 가공되지 않은 데이터 객체 (Raw data objects)를 반환해. 비즈니스 로직은 포함하지 마."

경계가 명확할 때 AI 도구는 패턴을 구현하는 데 매우 탁월합니다. 하지만 그 경계를 설정하는 것은 여러분의 몫입니다. 그리고 그것은 조만간 변하지 않을 것입니다.

결론

AI는 코드를 생성합니다. 아키텍처는 그 코드가 실제 사용 환경에서 살아남을 수 있을지를 결정합니다.

계층형 아키텍처 (Layered architecture)는 대규모 팀만을 위한 사치가 아닙니다. 이는 AI가 생성한 애플리케이션이 부채 (liability)가 되지 않고 성장할 수 있게 해주는 구조입니다.

우리가 더 빠르게 구축할수록, 관심사의 분리 (separation of concerns)는 더욱 중요해집니다. 구조 없는 속도는 그저 마케팅만 잘 된 부채 (debt)일 뿐입니다.

계층을 정의하십시오. 경계 (boundaries)를 강제하십시오. 그런 다음 AI가 그 안을 채우도록 하십시오.

AI가 생성한 코드를 구조화하는 여러분만의 접근 방식은 무엇인가요? 다양한 기술 스택과 팀 규모에서 어떤 방식이 효과적인지 궁금합니다. 댓글로 남겨주세요.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0