포스트

"절대 흘려듣지 마세요": AI 시대에 더욱 중요해진 아키텍처 원칙

"절대 흘려듣지 마세요": AI 시대에 더욱 중요해진 아키텍처 원칙

개발자 분들 절대 흘려듣지 마세요

컨트롤러가 리포지토리를 사용하는 것이

왜 안티패턴으로 규정되는지

당신은 알아야합니다.

서비스가 RequestDTO를 사용하는 것이

왜 안티패턴으로 규정되는지

당신은 알아야합니다.

컨트롤러가 리포지토리를 직접 사용하는 것이 안티패턴인 이유는 컨트롤러가 본래 담당해야 할 HTTP 요청·응답 처리의 역할을 넘어 데이터 조회와 저장, 비즈니스 흐름 판단까지 떠안게 되어 계층 간 책임 분리가 붕괴되기 때문이다. 이 구조에서는 비즈니스 로직이 컨트롤러 곳곳에 흩어지며 재사용이 불가능해지고, 트랜잭션 경계가 요청 계층에 종속되어 일관성과 원자성을 보장하기 어려워진다. 또한 데이터 저장 방식이나 도메인 규칙이 변경될 때 웹 계층까지 연쇄 수정이 발생해 변경 비용이 급격히 증가하며, 결과적으로 시스템은 “웹 요청 하나당 임시 로직을 쌓아 올리는 구조”로 퇴화한다.

서비스가 Request DTO를 사용하는 것이 안티패턴인 이유는 비즈니스 계층이 HTTP 요청이라는 외부 인터페이스 형태에 종속되면서 도메인의 독립성이 깨지기 때문이다. Request DTO는 검증 규칙, JSON 구조, 프론트엔드 요구사항을 담은 웹 계약 객체인데, 이를 서비스 계층에서 그대로 사용하면 서비스 메서드의 의미가 비즈니스 행위가 아니라 특정 API 요청 처리로 변질된다. 그 결과 배치, 이벤트, 테스트 등 다른 진입점에서 재사용이 어려워지고, 프론트 요구로 DTO가 변경될 때마다 서비스 시그니처와 내부 로직까지 함께 흔들리는 구조적 취약성이 발생한다.

https://www.threads.com/@obi_dobi_dev/post/DTyrJKykx4k

두 가지 안티패턴의 본질

“컨트롤러가 리포지토리를 사용하는 것이 왜 안티패턴으로 규정되는지 당신은 알아야합니다. 서비스가 RequestDTO를 사용하는 것이 왜 안티패턴으로 규정되는지 당신은 알아야합니다.”

이 경고는 2026년 1월 현재, “Hello World도 못하지만 공부 안 할 것”이라는 선언과 정확히 대조되는 지점을 드러낸다. AI가 코드를 작성해준다면 구문을 몰라도 된다. 그러나 AI는 당신의 아키텍처를 알지 못한다. 그리고 그것이 문제의 핵심이다.

안티패턴 1: 컨트롤러가 리포지토리를 직접 사용

컨트롤러가 리포지토리를 직접 사용하는 것은 계층형 아키텍처의 근본적 붕괴다. 컨트롤러는 HTTP 요청과 응답을 처리해야 한다. 데이터 조회와 저장, 비즈니스 흐름 판단은 서비스 계층의 책임이다. 이 경계가 무너지면 비즈니스 로직이 컨트롤러 곳곳에 흩어진다. 재사용이 불가능해진다. 트랜잭션 경계가 웹 계층에 종속된다. 일관성과 원자성을 보장하기 어려워진다.

더 심각한 것은 변경의 파급 효과다. 데이터 저장 방식이 바뀌면? JPA에서 MongoDB로 전환한다면? 리포지토리 인터페이스가 변경될 때마다 모든 컨트롤러를 수정해야 한다. 웹 계층까지 연쇄 수정이 발생한다. 결과적으로 시스템은 “웹 요청 하나당 임시 로직을 쌓아 올리는 구조”로 퇴화한다.

안티패턴 2: 서비스가 Request DTO를 사용

서비스가 Request DTO를 사용하는 것은 비즈니스 계층이 HTTP 인터페이스에 종속되는 것이다. Request DTO는 웹 계약 객체다. JSON 구조, 검증 규칙, 프론트엔드 요구사항을 담는다. 이것을 서비스 계층에서 그대로 사용하면 서비스 메서드의 의미가 비즈니스 행위가 아니라 “특정 API 요청 처리”로 변질된다.

그 결과는 무엇인가? 배치에서 이 서비스를 호출하려면? 이벤트 핸들러에서 사용하려면? 테스트를 작성하려면? 모두 불가능하거나 어색해진다. Request DTO를 만들어서 전달해야 하는데, 그것은 HTTP 요청이 없는 맥락에서는 의미가 없다. 프론트 요구로 DTO가 변경될 때마다 서비스 시그니처와 내부 로직까지 함께 흔들린다. 도메인은 UI의 노예가 된다.

2026년의 충격적 발견: AI는 이 원칙을 모른다

IT Brief Asia의 2026년 1월 보고서는 충격적인 현실을 드러낸다. “AI 코드 생성 도구는 2025년 내내 열풍이었다. 수백만 명의 사용자를 확보했다. 일부 회사(Cursor, Lovable, Replit, v0 by Vercel)는 이미 수억 달러의 수익을 올리며 새로운 지수적 확장 기록을 세웠다. 그러나 초기 열광이 시들해지고 있다. 코드 품질, 리뷰, 유지보수성, 비결정적 결과가 여러 온라인 포럼에서 개발자 불만으로 등장했다.”

WaveMaker 경영진은 직설적이다. “2026년에 기업의 AI 개발 도구 채택은 제품이 내부 아키텍처 규칙과 통제에 얼마나 잘 맞는지에 달려있을 것이다.” 왜 이것이 중요한가? “대기업은 수년간 구축된 추상화, 프레임워크, 디자인 패턴의 레이어 위에서 운영된다. 이러한 구조는 중요한 시스템을 보호하고, 컴플라이언스를 강제하고, 대규모 팀 전체에 걸쳐 신뢰성을 유지한다. 이러한 구조를 우회하는 새로운 도구는 기술 부채, 보안 격차, 팀 간 불일치를 만들 수 있다.”

Architectural Drift: AI가 조용히 아키텍처를 구부린다

vFunction의 2025년 11월 분석은 “vibe coding”의 어두운 면을 폭로한다. 제목부터 명확하다. “The rise of vibe coding: Why architecture still matters in the age of AI agents.”

핵심 발견들:

  • AI는 안티패턴을 주입한다: “한때 인기 있었지만 이제는 구식으로 간주되는 패턴을 채택한다. 모듈성보다 단순함이나 친숙함을 선호한다. 특별히 지시하지 않으면 중요한 추상화 레이어(서비스, 리포지토리, 데이터 전송 객체)를 건너뛴다.”
  • Happy-path implementation: “대부분의 에이전트는 모든 서비스가 항상 작동하고 모든 요청이 성공한다고 가정하는 happy-path 구현을 작성한다. retry logic, circuit breakers, graceful fallbacks, timeouts, structured error propagation이 포함되지 않는다.”
  • 확장성 무시: “잘 설계된 애플리케이션과 달리, 에이전트는 비동기 처리, 캐싱 레이어, 수평 확장 고려사항 같은 전략을 거의 통합하지 않는다.”

간단한 프롬프트를 제시한다. “사용자가 블로그 게시물을 만들고 볼 수 있는 Node.js REST API를 구축하라.” AI가 생성할 것은 무엇인가? 모든 것이 하나의 파일에 있고, 컨트롤러가 직접 데이터베이스를 호출하고, 검증이 없고, 에러 핸들링이 없는 코드. “이 앱은 의도한 대로 작동할 것이다. 뚜껑을 열어보지 않으면 모든 것이 괜찮아 보일 수 있다. 나중에 기능을 확장하거나 앱을 확장할 때 이 단순한 디자인의 한계에 부딪힐 때까지.”

AIT의 2025년 9월 분석은 더 구체적이다. “AI가 생성한 코드는 종종 작동한다. 그리고 그것이 정확히 문제다. 코드가 컴파일되고, 테스트를 통과하고, 일을 해내면 더 큰 질문을 묻지 않고 병합하고 싶은 유혹이 있다.”

Architectural drift의 징후들:

  • 일관성 없는 패턴: AI는 유사한 문제에 대해 다른 솔루션을 생성하여 동일한 코드베이스 내에서 단편화된 접근을 초래한다.
  • 강하게 결합된 컴포넌트: 제안은 관심사의 깔끔한 분리보다 즉시성을 우선시한다.
  • 중복되거나 복제된 로직: AI는 다른 곳에 이미 작성된 것을 추적하지 않는다. 각 프롬프트를 고립된 상태로 해결한다.
  • 기본값에 대한 과도한 의존: Copilot 같은 도구는 신중하게 안내하지 않으면 일반적 패턴이나 안티패턴을 과도하게 사용할 수 있다.

“AI는 당신의 아키텍처를 깨뜨리지 않는다. 조용히 구부려서 형태를 잃게 만든다. 적극적인 리팩토링과 아키텍처 리뷰 없이 이 drift는 눈덩이처럼 커진다. 결과는? 부풀어 오르고, 깨지기 쉬우며, 진화시키기 더 어렵고, 신뢰하기 더욱 어려운 시스템. 그리고 정리를 기다리면 기다릴수록 그 정리 비용은 더 높아진다.”

실제 사례: AI가 생성하는 정확히 그 안티패턴들

vFunction의 예제 코드를 보자. 간단한 프롬프트: “사용자가 블로그 게시물을 만들고 볼 수 있는 Node.js REST API를 구축하라.”

AI가 생성할 가능성이 높은 코드:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const express = require('express');
const sqlite3 = require('sqlite3').verbose();

const app = express();
const db = new sqlite3.Database(':memory:');

app.use(express.json());

// 직접 데이터베이스 초기화
db.serialize(() => {
  db.run("CREATE TABLE posts (id INTEGER PRIMARY KEY, title TEXT, content TEXT)");
});

// 컨트롤러가 직접 데이터베이스 호출 - 안티패턴 1
app.post('/posts', (req, res) => {
  const { title, content } = req.body;
  db.run(`INSERT INTO posts (title, content) VALUES (?, ?)`, [title, content], function(err) {
    if (err) return res.status(500).send(err);
    res.json({ id: this.lastID });
  });
});

app.get('/posts', (req, res) => {
  db.all("SELECT * FROM posts", [], (err, rows) => {
    if (err) return res.status(500).send(err);
    res.json(rows);
  });
});

이 코드의 문제점:

  1. 컨트롤러가 데이터베이스를 직접 호출 - 서비스 레이어 없음
  2. Request body를 직접 사용 - DTO 변환 없음
  3. 검증 없음 - title이나 content가 없어도 삽입 시도
  4. 트랜잭션 관리 없음 - 복잡한 작업에서 일관성 보장 불가
  5. 에러 핸들링 부족 - 일반적 500 에러만 반환
  6. 테스트 불가능한 구조 - 모든 것이 강하게 결합됨
  7. 재사용 불가능 - 배치나 이벤트에서 같은 로직을 사용할 수 없음

그리고 이것은 정확히 경고한 두 가지 안티패턴이다. 컨트롤러가 리포지토리(여기서는 직접 데이터베이스)를 사용하고, 서비스가 없어서 Request body(DTO)를 직접 처리한다.

올바른 아키텍처: 계층의 존재 이유

같은 기능을 올바르게 구현하면 어떻게 되는가?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Domain Entity
class Post {
  constructor(id, title, content) {
    this.id = id;
    this.title = title;
    this.content = content;
  }
}

// Repository (Data Access Layer)
class PostRepository {
  constructor(db) {
    this.db = db;
  }

  async create(post) {
    return new Promise((resolve, reject) => {
      this.db.run(
        `INSERT INTO posts (title, content) VALUES (?, ?)`,
        [post.title, post.content],
        function(err) {
          if (err) reject(err);
          else resolve(this.lastID);
        }
      );
    });
  }

  async findAll() {
    return new Promise((resolve, reject) => {
      this.db.all("SELECT * FROM posts", [], (err, rows) => {
        if (err) reject(err);
        else resolve(rows.map(row => new Post(row.id, row.title, row.content)));
      });
    });
  }
}

// Service Layer (Business Logic)
class PostService {
  constructor(postRepository) {
    this.postRepository = postRepository;
  }

  async createPost(title, content) {
    // 비즈니스 규칙 검증
    if (!title || title.trim().length === 0) {
      throw new Error('Title is required');
    }
    if (!content || content.trim().length === 0) {
      throw new Error('Content is required');
    }
    if (title.length > 200) {
      throw new Error('Title too long');
    }

    const post = new Post(null, title.trim(), content.trim());
    return await this.postRepository.create(post);
  }

  async getAllPosts() {
    return await this.postRepository.findAll();
  }
}

// DTOs (Web Layer)
class CreatePostRequest {
  constructor(body) {
    this.title = body.title;
    this.content = body.content;
  }
}

class PostResponse {
  constructor(post) {
    this.id = post.id;
    this.title = post.title;
    this.content = post.content;
    this.createdAt = new Date().toISOString(); // 추가 웹 계층 정보
  }
}

// Controller (Presentation Layer)
class PostController {
  constructor(postService) {
    this.postService = postService;
  }

  async createPost(req, res) {
    try {
      // Request DTO로 변환
      const requestDto = new CreatePostRequest(req.body);
      
      // 서비스 호출 시 도메인 파라미터 사용
      const postId = await this.postService.createPost(
        requestDto.title,
        requestDto.content
      );
      
      res.status(201).json({ id: postId });
    } catch (error) {
      if (error.message.includes('required') || error.message.includes('too long')) {
        res.status(400).json({ error: error.message });
      } else {
        res.status(500).json({ error: 'Internal server error' });
      }
    }
  }

  async getAllPosts(req, res) {
    try {
      const posts = await this.postService.getAllPosts();
      
      // Response DTO로 변환
      const response = posts.map(post => new PostResponse(post));
      
      res.json(response);
    } catch (error) {
      res.status(500).json({ error: 'Internal server error' });
    }
  }
}

차이가 보이는가?

  1. 명확한 책임 분리: 각 계층이 자신의 역할만 수행
  2. 도메인 독립성: 서비스는 HTTP에 대해 모른다
  3. 재사용 가능: 배치, 이벤트, 테스트에서 동일한 서비스 사용 가능
  4. 변경 격리: 데이터베이스를 MongoDB로 바꿔도 서비스 계층은 영향 없음
  5. 테스트 용이: 각 계층을 독립적으로 테스트 가능
  6. 비즈니스 로직 집중화: 검증 규칙이 서비스에 집중됨

2026년 Reset: 아키텍처로의 회귀

WaveMaker의 AI 제품 엔지니어링 책임자는 명확하다. “2026년에 AI 기반 개발 도구는 vibe coding이나 기본적 개념 증명 지원을 훨씬 넘어 성숙할 것이다.”

실제로 무슨 일이 일어나고 있는가?

  • 문서 우선 워크플로우: Amazon 같은 벤더들이 코드 생성 전에 요구사항과 아키텍처 정의를 장려하는 기능을 통합하기 시작했다.
  • 아키텍처 규칙 인코딩: 도구들이 아키텍처 규칙을 인코딩하고, 리뷰 프로세스를 강제하고, 엔지니어가 공식 사양에서 작업하도록 프롬프트하는 기능을 홍보한다.
  • 감사 추적: 규제 부문의 엔지니어링 리더들은 프로덕션 시스템에 접촉하는 모든 도구에서 감사 추적, 일관된 동작, 컴플라이언스 통제와의 정렬을 원한다.

IJERT의 2025년 6월 논문은 미래 방향을 보여준다. “아키텍처 사양에서 실행 가능한 코드를 생성하는 AI 기반 솔루션”. 핵심은 LLM과 RAG를 활용해 아키텍처 문서에서 직접 프로덕션 준비 코드를 스캐폴드하는 자동화된 파이프라인이다.

그들의 접근법:

  1. 패턴 인덱싱: 아키텍처 패턴의 임베딩 생성, 메타데이터와 함께 벡터 데이터베이스에 저장
  2. 컨텍스트 검색: RAG 파이프라인을 사용해 유사한 아키텍처 패턴 쿼리, 관련 코드 템플릿 식별
  3. 코드 합성: 검색된 컨텍스트와 아키텍처 제약으로 LLM을 사용해 프로덕션 준비 코드 생성
  4. 품질 검증: 구문, 스타일, 보안 컴플라이언스, 아키텍처 준수 검증

이것은 “vibe coding”과 정반대다. 구조화되고, 검증되고, 아키텍처에 정렬된다.

Claude Code Skills와의 연결: 아키텍처를 인코딩하라

앞서 분석한 Claude Code의 Skills와 Sub-agents로 돌아가자. 이것들이 바로 해결책이다. Skills는 단순히 “코드를 빨리 작성”하는 것이 아니라, 아키텍처 원칙을 인코딩하는 것이다.

실제 Skill 예제를 보자:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
---
name: layered-architecture-enforcer
description: Enforces layered architecture principles in Spring Boot applications
allowed-tools: "Read, Write, Grep"
version: 1.0.0
---

# Layered Architecture Enforcement Skill

## Purpose
Ensure all code follows proper layered architecture with clear separation of concerns.

## Layer Definitions

### Controller Layer (Presentation)
**Responsibilities:**
- Handle HTTP requests/responses
- Input validation (format, not business rules)
- DTO conversion
- Delegate to Service layer

**Prohibitions:**
- ❌ NEVER directly use Repository
- ❌ NEVER contain business logic
- ❌ NEVER pass Request DTOs to Service

### Service Layer (Business Logic)
**Responsibilities:**
- Implement business rules
- Transaction management
- Coordinate between repositories
- Return domain objects

**Prohibitions:**
- ❌ NEVER use Request/Response DTOs
- ❌ NEVER know about HTTP
- ❌ Accept domain parameters only

### Repository Layer (Data Access)
**Responsibilities:**
- Database operations only
- Return domain entities

**Prohibitions:**
- ❌ NEVER contain business logic
- ❌ NEVER know about web layer

## Anti-Pattern Detection

Before writing code, check:
1. Is Controller calling Repository directly? → REJECT
2. Is Service using RequestDTO? → REJECT
3. Is business logic in Controller? → REJECT
4. Is HTTP knowledge in Service? → REJECT

## Code Generation Rules

When generating a new endpoint:
1. Create RequestDTO in web.dto package
2. Create domain entity in domain package
3. Create Service method accepting domain parameters
4. Create Repository interface
5. Controller converts DTO → calls Service → converts to ResponseDTO
6. Service implements business logic → calls Repository
7. Repository performs data access only

## Validation Checklist
- [ ] Each layer only depends on layer below
- [ ] No circular dependencies
- [ ] DTOs only in web layer
- [ ] Business logic only in service layer
- [ ] Data access only in repository layer
- [ ] Proper exception handling at each layer

이 Skill을 로드하면 Claude Code는 더 이상 안티패턴을 생성하지 않는다. 컨트롤러가 리포지토리를 호출하려고 하면? Skill이 거부한다. 서비스가 RequestDTO를 받으려고 하면? Skill이 수정한다.

“Hello World도 못함” vs “아키텍처는 알아야 함”: 진짜 균형

여기서 흥미로운 역설이 나타난다. “Hello World도 못하지만 공부 안 할 것”이라는 선언과 “이 안티패턴을 절대 흘려듣지 마세요”는 어떻게 공존하는가?

답은 추상화 레벨의 전환이다:

더 이상 중요하지 않은 것:

  • print() 구문
  • 루프 작성법
  • 배열 인덱싱
  • 기본 자료구조 구현

여전히 (오히려 더) 중요한 것:

  • 계층형 아키텍처 원칙
  • 관심사의 분리
  • 의존성 방향
  • SOLID 원칙
  • 도메인 주도 설계

AI가 구문을 작성해주지만, 아키텍처는 작성해주지 않는다. 더 정확히는, AI는 당신이 정의한 아키텍처를 따를 수 있지만, 아키텍처 자체를 설계하지는 못한다.

NetCorp의 2026 AI 통계는 명확하다. “AI는 생산성을 향상시키지만 대체재가 아니다. 숙련된 개발자들은 압도적으로 AI 도구를 보일러플레이트, 테스팅, 단조로운 작업에 도움이 된다고 보지만, 깊은 문제 해결, 디자인, 아키텍처를 대체할 수 있다고는 보지 않는다.”

World Economic Forum의 Future of Jobs Report 2025에 따르면, 2030년까지 직무 스킬의 39%가 전환될 것이며, 기술 인재는 “AI 유창성, 시스템 사고, 소프트 스킬의 더 강한 조합”이 필요할 것이다. 가장 가치 있는 개발자를 차별화하는 것은 “코드 리뷰, 문서화, 아키텍처 계획, 팀 협업”이다.

실천적 조언: AI 시대의 아키텍처 원칙 강제

그렇다면 어떻게 AI가 올바른 아키텍처를 생성하도록 보장하는가?

1. Skills로 아키텍처를 인코딩하라

Claude Code, Cursor, 기타 도구들이 Skills를 지원한다. 프로젝트별 또는 조직별 Skill을 만들어라. 아키텍처 원칙, 금지된 패턴, 필수 구조를 명시하라. AI가 코드를 생성하기 전에 이 규칙들을 컨텍스트에 주입한다.

2. Architecture-First 워크플로우를 채택하라

코드 생성 전에 아키텍처를 정의하라. PlantUML, ArchiMate, Structurizr 같은 도구로 아키텍처를 문서화하고, 이를 기계 판독 가능한 형식으로 내보내라. AI에게 “코드를 만들어”가 아니라 “이 아키텍처 다이어그램을 구현해”라고 요청하라.

3. 자동화된 아키텍처 검증을 구축하라

ArchUnit 같은 도구로 아키텍처 규칙을 코드로 표현하고, CI/CD에 통합하라. 컨트롤러가 리포지토리를 호출하면 빌드가 실패하도록 만들라. AI가 생성한 코드든 사람이 쓴 코드든, 아키텍처 위반은 자동으로 거부된다.

1
2
3
4
5
6
7
8
9
static final ArchRule controllers_should_not_access_repositories =
    noClasses()
        .that().resideInAPackage("..controller..")
        .should().accessClassesThat().resideInAPackage("..repository..");

static final ArchRule services_should_not_depend_on_web_dtos =
    noClasses()
        .that().resideInAPackage("..service..")
        .should().dependOnClassesThat().resideInAPackage("..web.dto..");

4. Code Review에서 아키텍처를 최우선으로

AI 생성 코드를 리뷰할 때 “작동하는가?”만 묻지 마라. “올바른 계층에 있는가?”, “의존성 방향이 맞는가?”, “도메인이 독립적인가?”를 먼저 물어라. 작동하지만 아키텍처가 잘못된 코드는 거부하라.

5. 교육과 문서화에 투자하라

팀 전체가 왜 이 원칙들이 중요한지 이해해야 한다. “컨트롤러가 리포지토리를 쓰면 안 되는 이유”를 설명하는 문서를 작성하라. 실제 프로젝트에서 안티패턴이 어떤 문제를 일으켰는지 케이스 스터디를 공유하라.

6. AI를 아키텍처 검증에 활용하라

역설적으로 AI를 사용해 AI 생성 코드를 검증할 수 있다. Neueda의 제안처럼, 아키텍처 다이어그램을 LLM에 제공하고 “이 코드에 안티패턴이 있는가?”를 물어라. AI는 패턴 인식에 뛰어나다. 올바른 컨텍스트를 주면 안티패턴도 식별할 수 있다.

기업 환경의 현실: 레거시와 AI의 충돌

vFunction의 경고는 특히 중요하다. “대부분의 기업 개발은 빛나는 새 프로젝트의 greenfield 작업이 아니라 5년, 10년, 심지어 20년 된 기존 코드베이스와 함께 작업하는 것이다. 이러한 시스템 내에는 기술 부채, 문서화되지 않은 비즈니스 로직, 복잡한 상호의존성의 레이어가 있어 에이전트 주도 개발을 훨씬 더 어렵게 만든다.”

AI는 greenfield에서 빠르다. 그러나 기존 시스템을 이해하는 것은? 기존 아키텍처를 존중하는 것은? 수십 년간 축적된 비즈니스 규칙을 파악하는 것은? 이것은 근본적으로 다른 위험 프로필이다.

기업 환경에서 “컨트롤러가 리포지토리를 직접 호출”하는 안티패턴이 한 번 들어가면 어떻게 되는가? 다른 개발자들이 그것을 패턴으로 인식하고 복제한다. AI는 기존 코드베이스에서 학습하므로, 안티패턴을 “정상적인” 패턴으로 학습하고 전파한다. 눈덩이처럼 커진다.

결론: 절대 흘려듣지 마세요

“컨트롤러가 리포지토리를 사용하는 것이 왜 안티패턴으로 규정되는지 당신은 알아야합니다. 서비스가 RequestDTO를 사용하는 것이 왜 안티패턴으로 규정되는지 당신은 알아야합니다.”

이 경고는 2026년에 그 어느 때보다 중요하다. AI가 코드를 작성할 수 있게 되었지만, 그것은 아키텍처 원칙을 더욱 중요하게 만든다. AI는 빠르다. 그리고 잘못된 방향으로 빠르면 재앙이다.

WaveMaker의 말처럼, “2026년은 AI 코드 생성 도구가 vibe coding에서 아키텍처, 거버넌스, 장기 유지보수성으로 초점을 이동하는 reset의 해다.” 성공하는 팀과 조직은 AI의 속도와 아키텍처의 원칙을 결합하는 방법을 찾을 것이다.

print("Hello, World!")를 몰라도 괜찮을 수 있다. AI가 그것을 작성해준다. 그러나 계층형 아키텍처, 의존성 역전, 관심사의 분리를 모르면? AI는 당신의 코드베이스를 기술 부채의 늪으로 만들 것이다. 그리고 당신은 왜 “작동하는” 코드가 몇 달 후 유지보수 악몽이 되는지 이해하지 못할 것이다.

Skills로 아키텍처를 인코딩하라. 검증을 자동화하라. 리뷰에서 원칙을 강제하라. 팀을 교육하라. 그리고 무엇보다, 이 원칙들을 절대 흘려듣지 마라. AI 시대의 진짜 개발자는 코드를 작성하는 사람이 아니라, 올바른 아키텍처를 설계하고 강제하는 사람이다.


작성일자: 2026-01-22

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.