생성형 AI와 Vibe Coding이 보편화되면서 소프트웨어 개발 방식은 빠르게 변하고 있다. 이제 코드를 작성하는 것 자체는 더 이상 어려운 일이 아니다. 프롬프트 몇 줄만으로 CRUD 화면, API, 데이터베이스 스키마, 테스트 코드까지 생성할 수 있다. 하지만 개발 속도가 빨라질수록 오히려 더 중요해지는 것이 있다. 바로 설계 판단(Design Decision) 이다. AI는 대안을 제시할 수 있고, 장단점을 설명할 수 있으며, 심지어 상당히 괜찮은 코드까지 생성한다. 그러나 지금 어떤 판단을 해야 하는지, 무엇을 우선해야 하는지, 어떤 위험을 감수해야 하는지는 결정하지 않는다.
실제로 AI 도입 이후 현장에서 관찰되는 많은 문제들은 새로운 문제가 아니다. 과거에도 존재했던 설계 실패 패턴들이 AI의 생산성과 결합하면서 훨씬 더 빠른 속도로 증폭되고 있을 뿐이다. 이 글에서는 Decision Quality(DQ)라는 의사결정 프레임워크를 기반으로 21가지 설계 실패 패턴을 체계적으로 정리해본다.
좋은 결과와 좋은 의사결정은 다르다
Decision Quality(DQ)는 SDG에서 체계화한 의사결정의 품질을 다음 여섯 요소로 설명한다.
- Frame (무엇이 문제인가 - 결정의 목적(Purpose), 범위(Scope), 관점(Perspective)을 명확히 하는 것. “우리가 풀고자 하는 진짜 문제는 무엇인가?“)
- Alternatives (어떤 선택지가 있는가 - 의미 있게 다른, 실행 가능한 여러 옵션을 충분히 생성하는 것. “우리가 선택할 수 있는 선택지는 무엇인가?“)
- Information (정보는 충분한가 - 관련성 있고, 신뢰할 수 있으며, 시의적절한 정보(사실 + 판단 + 불확실성)를 수집하는 것. “우리가 무엇을 알고, 무엇을 모르는가?“)
- Values (무엇을 기준으로 평가하는가 - 이해 관계자들이 진짜 중요하게 여기는 가치(Values)를 명확히 하고, 그 사이의 트레이드오프를 이해하는 것. “우리가 무엇을 중요하게 여기는가?“)
- Sound Reasoning (논리는 타당한가 - Alternatives + Information + Values를 논리적으로 결합해 최선의 선택을 도출하는 과정. “이 대안을 선택하는 이유는 무엇인가?”를 명확히 설명할 수 있어야 한다.)
- Commitment to Action (실행 가능한가 - 결정 후 실행을 위한 자원 배분, 이해관계자 정렬(Alignment), 실행 의지(Commitment)를 확보하는 것)
DQ의 가장 중요한 주장은 다음 한 문장으로 요약된다. 의사결정의 품질은 가장 약한 요소에 의해 결정된다. 좋은 의사결정을 했더라도 결과가 나쁠 수 있다. 반대로 운 좋게 좋은 결과가 나왔다고 해서 좋은 의사결정을 한 것은 아니다. 설계 역시 마찬가지다. 많은 설계 실패는 사실 두 가지 문제로 귀결된다.
- 문제를 잘못 정의했다 (Frame 오류)
- 평가 기준을 잘못 잡았다 (Values 오류)
그래서 Values는 ‘평가축’으로 8개를 정리해 평가를 하고 Frame의 문제는 ‘지배축의 문제’로 해결해보고자 한다. 나머지 4개 요소도 부차적으로 얽힐 수 있다. ‘성급한 결론’은 Information의 신뢰성 문제, ‘명목상 이름뿐인 완료’는 Commitment to Action의 누락, ‘바퀴의 재발명’과 ‘너무 이른 추상화’는 Alternatives의 빈곤, ‘임시방편의 영구화’나 ‘계층 침범 구현’은 Sound Reasoning의 논리 비약으로 읽을 수 있다. 가장 약한 요소가 전체의 품질을 결정한다는 DQ의 주장은 그대로 설계 판단에도 적용할 수 있다.
설계 판단의 8가지 평가축
설계를 평가할 때 사용하는 기준은 분야마다 이름이 다르다.
- Decision Analysis에서는 Criteria
- ATAM에서는 Quality Attributes
- ISO/IEC 25010에서는 Quality Characteristics
여기서는 이들을 통합하여 평가축(Evaluation Axis) 이라고 부르겠다. 설계 판단에 필요한 핵심 평가축은 다음 여덟 가지로 정리할 수 있다.
- 목적 적합성: 현재 해결하려는 문제를 얼마나 잘 해결하는가.
- 제약 적합성: 요구사항과 제약사항을 만족하는가.
- 실현 가능성: 기술적으로 가능하고, 조직이 실제로 운영할 수 있는가.
- 품질 영향: 유지보수성, 운영성, 확장성, 테스트 용이성 등에 미치는 영향이다.
- 시간 효과: 단기적인 효과와 장기적인 효과를 함께 고려한다.
- 위험과 불확실성: 예상 가능한 위험과 불확실성을 얼마나 통제할 수 있는가.
- 정합성: 기존 아키텍처와 얼마나 일관되는가.
- 합의 가능성: 팀과 조직이 실제로 받아들이고 실행할 수 있는가.
대부분의 실패는 지배축을 잘못 선택해서 발생한다
모든 평가축이 항상 동일하게 중요하지는 않다. 상황에 따라 가장 중요한 축이 존재한다. 이를 지배축(Dominant Criterion) 이라고 부른다. 예를 들어 장애 대응 상황이라면 단기 시간 효과가 지배축이 된다. 반대로 플랫폼 재설계라면 장기 품질, 유지보수성, 확장성 이 지배축이 된다.
문제는 대부분의 실패가 다음 구조를 가진다는 점이다.
실제 지배축 ≠ 선택한 지배축. 예를 들면,
- 장애 대응인데 이상적인 아키텍 처를 고민한다.
- PoC인데 운영 품질을 요구한다.
- 보안 문제인데 UX만 고려한다.
- 장기 플랫폼인데 개발 속도만 본다.
이러한 지배축의 착오가 다양한 실패 패턴으로 나타난다. 그리고 여기에서 사용하는 용어를 다시 한번 요약해서 이해를 돕고자 한다.
- 평가축: 설계 판단에 사용하는 평가 기준(Criteria).
- 지배축: 지금의 판단에서 가장 중시하는 평가축(Dominant Criterion).
- 2차 조건: 지배축만큼 중요하지 않지만, 채워야 할 수준을 결정해 두는 축. 예를 들어 지배축을 ‘시간 효과(단기)‘로 해도 ‘제약 적합성’을 2차 조건으로 최저 수준은 확보한다는 형태로 사용할 수 있다. 또한, 실현가능성(기술)/실현가능성(조직)을 2차 조건으로 걸어서 리스크를 완화할 수도 있다.
그래서 설계 판단을 할때 평가축, 지배축, 2차 조건 등을 고려하는 습관을 가진다면 아래와 같은 설계 실패 패턴들을 줄일 수 있을 것이다.
설계 실패 패턴들
재사용과 중복 오판
1. 바퀴의 재발명
이미 검증된 표준 해법이 있는데도 직접 구현하는 경우다. 대표적 사례는,
- 자체 인증 시스템을 만드는 것
- 자체 권한 시스템을 만드는 것
- 자체 스케줄러를 만드는 것
- 자체 에러 프로토콜을 만드는 것
Spring Security가 있는데 인증을 만들고, Quartz가 있는데 스케줄러를 만들고, HTTP 상태 코드가 있는데 자체 에러 체계를 만든다. 대개 “우리 서비스는 조금 특별하다”는 생각에서 시작된다. 그러나 대부분은 특별하지 않다. AI가 생성한 코드에서도 자주 나타나는 패턴이다. 코드는 동작하지만 프레임워크가 제공하는 정석적인 방법을 벗어나 있는 경우가 많다. 평가축의 편향으로는 목적 적합성은 과대평가하고 정합성, 실현 가능성(기술), 품질 영향(유지보수성)을 고려하지 않으면 일어나는 현상이다.
2. 주변을 보지 않는 재구현
이미 존재하는 기능을 모르고 다시 구현한다. 대표 사 례로,
- 날짜 유틸리티가 있지만 보지 않고 함수를 중복 생성하는 것
- 유효성 검사 규칙이 공통화 되어 있지만 동일 규칙을 로컬함수나 개별 컨트롤러에 중복 구현하는 것
- 권한 체크가 Decorator나 Interceptor에 구현되어 있으나 Controller에 개별적으로 구현하는 것
- 동일한 도메인 상수가 있지만 Constants.java 파일에 재정의하는 것
이 패턴은 Addy Osmani가 말한 Comprehension Debt의 전형적인 사례다. 평가축의 편향으로는 정합성, 품질영향(유지보수성), 합의 가능성 등을 고려하지 않아서 일어나는 현상이다. AI를 사용한다면 “신규 함수를 만들기 전에 기존 코드를 검색한다”는 규칙을 명시해 두면 중복 빈도를 낮출 수 있다.
3. 황금 망치
익숙하거나 최근에 도입한 특정 기술을 문제의 적합성을 따지지 않고 모든 곳에 적용하는 경우다. 예를 들어,
- Redis를 도입한 팀이 캐시가 필요하지 않은 곳에도 Redis를 추가하는 것
- Kafka를 도입한 이후 모든 서비스 간 통신을 이벤트 기반으로 바꾸려는 것
- 특정 디자인 패턴(Observer, Strategy 등)을 배운 직후 모든 코드에 적용하려는 것
- RDB로 충분한 데이터에도 최신 유행 NoSQL을 선택하는 것
“바퀴의 재발명”이 이미 있는 표준 해법을 무시하고 새로 만드는 문제라면, 황금 망치는 반대로 익숙한 도구를 적합성 검토 없이 적용하는 문제다. AI도 학습 데이터에 자주 등장한 기술 조합을 문맥 없이 그대로 제안하는 경향이 있어 이 패턴을 강화한다. 평가축의 편향으로는 목적 적합성과 제약 적합성을 고려하지 않고 실현 가능성(기술)에만 편향되어 판단할 때 일어나는 현상이다.
범위와 복잡도 오판
4. 닭 잡는데 소 잡는 칼 쓰기
과도하게 복잡한 기술을 선택하는 경우다. 예를 들어,
- 30명 규모 시스템에 마이크로서비스 도입하는 것
- 간단한 입력 검증(필수, 자리수, 정규식)이나 승인프로세스에 Drools 같은 룰 엔진을 도입하는 것
- 하루 500건 정도의 CSV 처리를 Kafka + Consumer Group으로 구성하는 것
- 월 1회 실행하는 보고서 생성 배치를 Spark 클러스터와 Kubernetes CronJob으로 운영하는 것
등이 있다. AI는 일반적으로 알려진 베스트 프랙티스를 맥락 없이 적용하는 경향이 있다. 그래서 실제 문제보다 훨씬 큰 해법을 제안하는 경우가 많다. 평가축의 편향으로는 목적 적합성은 과대평가하고, 실현 가능성(기술), 시간 효과(단기), 품질 영향(운용성-운영팀 역량), 합의 가능성 등을 고려하지 않으면 일어나는 현상이다.
5. 너무 이른 추상화
변화가 관찰되지 않았는데 추상화를 도입한다. 예를 들어,
- 구현이 아직 하나밖에 없는데 Strategy 패턴을 도입하는 것.
- 검색 조건이 name과 status 두 개뿐인데 독자 DSL을 설계하는 것.
- 설정 항목이 거의 변하지 않을 것 같은데 미리 DB 기반 설정 관리 화면을 만드는 것.
대응하는 대표적인 원칙은 Rule of Three다. 같은 변화가 최소 세 번은 발생한 후 추상화하라. 평가축의 편향으로는 품질 영향(변경 용이성)만 과대평가하고 목적 적합성이나 실현 가능성(기술), 시간 효과(단기)를 무시하면 일어나는 현상이다.
6. 잡히지도 않은 미래를 위한 확장점
“너무 이른 추상화”와 비슷한 면이 있지만, “너무 이른 추상화”는 변화가 관찰되지 않은 상태에서 추상화이고, 여기에서는 미래의 연결고리가 없는 상황에서 미리 만들어 버리는 것이다. “언젠가 필요할 것 같다”는 이유로 확장 구조를 만드는 경우다. 예를 들어,
- 언젠가 DB가 여러 개 될지도 몰라서 멀티 데이터소스 환경을 구성하는 것
- 언젠가 SMS를 보낼지도 몰라서 SMS 발송 기능을 구현하는 것
- 언젠가 다국어 지원할지도 몰라서 다국어 버전을 구성하는 것
- 다른 PG사를 사용할 지 몰라서 결제 추상 클래스를 만드는 것
문제는 그 미래는 일정이 없고, 책임자도 없고, 비즈니스 가치도 명확하지 않다는 점이다. 미래는 계획이 아니라 가정일 뿐이다. 평가축의 편향으로는 시간 효과(장기)만 과대평가하고 시간 효과(단기), 실현 가능성(기술), 위험·불확실성은 고려하지 않아서 일어나는 현상이다.
조직 역량 오판
7. 조직 수준을 무시한 설계
기술적으로는 맞지만 조직이 운영할 수 없는 경우다. 예를 들어,
- 24시간 온콜 체계가 없는 팀인데 멀티 리전 고가용성 아키텍처를 도입하는 것
- Kubernetes나 Kafka 운영 경험이 거의 없는 팀이 본격 도입하는 것
- DBA가 없는 팀에 복잡한 샤딩 + 멀티마스터 DB 구성을 처음부터 설계하는 것
- SRE 전담 인력이 없는 팀에 SLO, Error Budget, Chaos Engineering을 도입하는 것
그런데도 복잡한 분산 시스템을 도입한다. 기술적 가능성과 조직적 가능성은 다르다. 실무에서는 후자가 더 중요할 때도 많다. 평가축의 편향으로는 실현 가능성(기술)만 과대평가하고 조직의 실현 가능성, 품질영향(운용성-운영팀의 역량), 위험·불확실성을 고려하지 않으면 일어나는 현상이다.

