프로그래밍에서의 프랑켄슈타인 — 정의, 원인 및 예방

저자: IT Sectr 게시일: 2026-07-26 읽는 시간: 10 분

프랑켄슈타인 은 프로그래밍에서 서로 다른 기술, 스타일 및 아키텍처의 호환되지 않는 부분에서 조립된 코드입니다. ThoughtWorks Technology Radar (2024) 연구에 따르면 대규모 프로젝트의 28%가 프랑켄슈타인 증후군의 징후를 보입니다 — 통일된 기술적 비전의 부재로 인한 아키텍처 절충주의입니다. 메리 셸리의 소설과 유사하게, 그러한 코드는 작동하지만 유지보수는 악몽으로 변합니다.

주요 내용

  • 프랑켄슈타인 은 시스템이 이질적이고 호환성이 낮은 구성 요소에서 조립되는 안티패턴입니다
  • 주요 원인: 아키텍트 부재, 프로젝트 통합, “경계 없는 창의성”
  • 문제 — 각 구성 요소는 자체 기술에 대한 지식이 필요하며 상호 작용은 예측 불가능합니다
  • 리팩터링 프랑켄슈타인은 기술 스택의 통일과 명확한 경계 설정이 필요합니다
  • Architecture Decision Records 및 RFC는 최고의 예방 도구입니다

프로그래밍에서 프랑켄슈타인이란

프랑켄슈타인 (프랑켄슈타인 코드, 프랑켄슈타인 패턴)은 소프트웨어 시스템이 함께 작동하도록 설계되지 않은 부품에서 조립되는 안티패턴입니다. 프랑켄슈타인의 괴물처럼, 그러한 코드는 작동할 수 있지만 추하고 예측 불가능하며 약간의 변경에도 위험합니다.

이 용어는 문학에서 유래했습니다: 메리 셸리의 소설 “프랑켄슈타인, 또는 현대의 프로메테우스” (1818)에서 과학자가 여러 다른 시체의 조각으로 살아있는 생명체를 만들었습니다. 프로그래밍에서 비유는 정확합니다 — 개발자는 다양한 프레임워크, 라이브러리, 언어의 조각을 가져와 “살아서” 붙여 작동하지만 끔찍한 결과를 얻습니다.

프랑켄슈타인과 스파게티 코드의 차이는 규모와 성질에 있습니다. 스파게티 코드는 단일 기술 스택 내의 얽힌 구조입니다. 프랑켄슈타인은 아키텍처 수준의 절충주의입니다: 동일한 시스템 내에서 서로 다른 기술, 호환되지 않는 패러다임, 충돌하는 접근 방식.

프랑켄슈타인 vs 마이크로서비스

마이크로서비스 아키텍처는 서로 다른 서비스에 다른 기술을 사용할 수 있지만 명확한 경계와 표준화된 상호 작용 프로토콜을 조건으로 합니다. 프랑켄슈타인은 경계 없는 혼란스러운 혼합입니다: 동일한 컨트롤러에 REST와 GraphQL, 동일한 모듈에 두 개의 ORM, 동일한 엔터티에 SQL과 NoSQL.

프랑켄슈타인 증후군이 발생하는 이유

기술 리더 또는 아키텍트의 부재가 근본 원인입니다. 프로젝트에 아키텍처 무결성을 담당하는 사람이 없으면 각 개발자가 “자기를 위해” 도구를 선택합니다. 한 명은 Spring을 좋아하고, 다른 명은 Guice를, 세 번째는 맞춤형 DI를 사용합니다. 결과는 아키텍처 뒤죽박죽입니다.

프로젝트 통합은 두 번째로 흔한 원인입니다. 두 팀이 서로 다른 스택을 사용하여 독립적으로 모듈을 개발했습니다. 모듈을 하나의 애플리케이션으로 결합해야 할 때 어댑터와 미들웨어로 단순히 “붙입니다”. 결과는 프랑켄슈타인입니다.

기업 인수는 세 번째 시나리오입니다. 회사 A가 회사 B를 인수하고 그 제품을 자사 제품에 통합하려고 합니다. 재작성 대신 — API, 공유 데이터베이스 및 패치를 통한 접착. 1년 후 시스템은 아무도 이해할 수 없는 괴물이 됩니다.

원인설명일반적인 결과
아키텍트 없음각 개발자가 자신의 스택을 선택하나의 모듈에 3개의 다른 HTTP 클라이언트
프로젝트 통합두 제품이 하나로 붙여짐두 개의 ORM, 두 개의 로깅 방식
M&A제품과 함께 회사 인수다른 아키텍처와 스타일의 혼합
실험전략 없이 새 기술 도입하나의 파일에 Java 8 + Java 21 기능
정치적 결정맥락 없이 위에서 기술 강요간단한 스크립트에 엔터프라이즈 프레임워크

“창의성” 요인

경험 많은 개발자가 프로덕션에서 새 기술을 시도하려고 하며 종종 프랑켄슈타인의 원천이 됩니다. 실험을 격리된 모듈로 제한하는 대신 시스템의 중요한 부분에 실험적 코드를 도입합니다.

실제 프로젝트의 프랑켄슈타인 예

고전적인 예는 하나의 애플리케이션에서 여러 ORM을 사용하는 것입니다. 일부 모듈은 Hibernate를 사용하고, 일부는 MyBatis를, 일부는 직접 JDBC 쿼리를 사용합니다. 트랜잭션은 관리 불가능해지고, 캐시는 일관성이 없어지며, 새 개발자는 새 기능에 어떤 접근 방식을 사용해야 할지 모릅니다.

두 번째 예는 아키텍처 스타일의 혼합입니다. REST API 컨트롤러에서 SOAP 서비스 호출, 직접 SQL 쿼리, 파일 시스템 액세스 및 HTML 생성을 찾을 수 있습니다. 이러한 애플리케이션은 테스트, 확장 또는 문서화가 불가능합니다.

세 번째 예는 백엔드에 Python, 마이크로서비스에 Node.js, 데스크톱 클라이언트에 C#, Android 앱에 Java가 사용되는 기술 스택이며, 모든 비즈니스 로직이 명확한 책임 분리 없이 그들 사이에 흩어져 있습니다.

javascript
// 프랑켄슈타인 — 혼합 스타일과 기술
// callbacks, Promises 및 async/await 결합

// callbacks
db.query("SELECT * FROM users", function(err, rows) {
  if (err) handleError(err);
  // callback 내부의 Promise
  fetch("/api/data").then(function(data) {
    // then 내부의 async/await
    (async () => {
      const result = await processData(data);
      sendResponse(result);
    })();
  });
});

// 깔끔한 코드 — 통일된 async/await 스타일
async function getUserData(userId) {
  const user = await db.query("SELECT * FROM users WHERE id = ?", [userId]);
  const data = await fetch("/api/data/" + userId);
  return await processData(user, data);
}

데이터 수준의 프랑켄슈타인

하나의 데이터베이스가 SQL 관계형 (정규화 포함) 및 NoSQL 문서 지향 (JSON 열 포함)으로 동시에 사용됩니다. 일부 쿼리는 ORM을 통해, 일부는 저장 프로시저를 통해, 일부는 코드에서 직접 SQL을 통해 실행됩니다. DB 스키마는 문서화되지 않았으며 마이그레이션이 충돌합니다.

프랑켄슈타인 코드의 결과

온보딩 복잡성이 첫 번째 결과입니다. 새 개발자는 시스템 작동 방식을 이해하기 위해 5개 언어, 3개 프레임워크, 2개 아키텍처 스타일을 알아야 합니다. 온보딩은 몇 주에서 몇 달로 늘어납니다. LinkedIn (2023)에 따르면 기술 절충주의가 있는 프로젝트는 새 직원을 2배 더 자주 잃습니다.

동작의 예측 불가능성이 두 번째 결과입니다. Python 마이크로서비스의 변경이 명확한 계약 없이 데이터베이스를 공유하기 때문에 예기치 않게 Java 모듈을 손상시킬 수 있습니다. 이러한 문제를 디버깅하려면 스택의 모든 기술에 대한 동시 지식이 필요합니다.

보안이 세 번째 결과입니다. 스택의 각 기술은 자체 보안 구성, 자체 패치, 자체 모니터링이 필요합니다. 5-6개의 이기종 기술에 대해 허용 가능한 수준의 보안을 유지하는 것은 사실상 불가능합니다. 그중 하나는 필연적으로 취약해집니다.

프랑켄슈타인의 기술 부채

SonarQube는 기술 부채를 측정할 수 있지만 “아키텍처 부채”는 측정할 수 없습니다 — 구성 요소 비호환성. 이 부채는 린터 경고가 아니라 다른 기술로 작성된 세 개의 다른 모듈을 수정하지 않고는 새 기능을 추가할 수 없는 데서 나타납니다.

괴물 생성을 피하는 방법

번째이자 주요 단계는 기술 스택의 무결성을 책임지는 아키텍트 또는 테크 리드를 임명하는 것입니다. 이 사람은 아키텍처 검토 없이 새 기술 도입에 대한 거부권을 가집니다. 민주주의가 아니라 핵심 기술에 대한 책임 있는 단독 결정입니다.

번째는 Architecture Decision Record (ADR) 프로세스를 도입하는 것입니다. 중요한 아키텍처 결정 (DB, 프레임워크, 프로토콜 선택)은 짧은 텍스트로 문서화됩니다: 맥락, 고려된 대안, 내린 결정, 결과. ADR은 저장소에 저장되며 전체 팀이 사용할 수 있습니다.

번째는 “하나의 작업 — 하나의 도구” 원칙을 확립하는 것입니다. HTTP 요청에는 하나의 클라이언트. ORM에는 하나의 라이브러리. 로깅에는 하나의 프레임워크. 예외는 정당성과 함께 ADR을 통해서만 허용됩니다. 프로젝트에 이미 Axios가 있다면 fetch를 추가하지 마세요. SLF4J가 있다면 System.out을 통해 작성하지 마세요.

  • 아키텍트 새 기술에 대한 거부권 보유
  • Architecture Decision Records 중요한 선택마다
  • 통일된 스택 각 작업에 — 하나의 HTTP 클라이언트, 하나의 ORM
  • RFC 팀 전체 토론이 필요한 큰 변경에
  • 기술 레이더 채택 가능한 것을 추적하기 위해

실험적 기술 정책

실험은 허용되지만 격리된 환경에서. 시스템의 나머지에 영향을 주지 않고 새 기술로 다시 작성할 수 있는 모듈이나 서비스를 할당하세요. 실험이 성공하면 ADR을 통해 표준화하세요. 실패하면 결과 없이 제거하세요.

기존 프랑켄슈타인 리팩터링 방법

인벤토리 — 첫 번째 단계. 기술 스택의 완전한 지도를 만드세요: 어떤 프레임워크, 라이브러리, 언어, 프로토콜이 어떤 모듈에서 어떤 작업에 사용되는지. 문제의 규모를 보게 될 것입니다: 도구 중복, 충돌하는 기술, 사용되지 않는 의존성.

표준화 — 두 번째 단계. 각 작업에 하나의 도구를 선택하세요. 예: ORM에는 Hibernate만, 로깅에는 SLF4J + Logback만, API에는 REST만. ADR에 표준을 문서화하세요. 절충주의가 가장 많은 문제를 일으키는 모듈부터 교체를 시작하세요.

Parallel Run 전략 — 세 번째 단계. 새 도구가 신뢰성을 입증할 때까지 이전 도구와 새 도구가 병렬로 작동합니다. 예를 들어, 이전 HTTP 클라이언트와 새 것이 동시에 작동하지만 새 것은 요청의 일부만 처리합니다. 안정화 기간 후 이전 것이 제거됩니다.

java
// 프랑켄슈타인 — 하나의 프로젝트에 세 가지 HTTP 접근 방식
// 모듈 A: OkHttp
OkHttpClient client = new OkHttpClient().newCall(request);

// 모듈 B: RestTemplate (Spring)
restTemplate.getForObject(url, String.class);

// 모듈 C: java.net.HttpURLConnection
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();

// 통일된 접근 방식: 동기에는 RestTemplate, 리액티브에는 WebClient
@Autowired
private RestTemplate restTemplate;

public String callApi(String url) {
    return restTemplate.getForObject(url, String.class);
}

예방에서 기술 리더의 역할

기술 리더는 프랑켄슈타인과 싸우는 주요 도구입니다. 관리자가 아니라, 상아탑의 아키텍트가 아니라, 코드를 작성하고 PR을 검토하며 아키텍처 결정을 내리는 실천하는 개발자입니다. 그러한 사람 없이 프로젝트는 필연적으로 기술 절충주의로 미끄러집니다.

RFC (Request for Comments)는 오픈 소스 커뮤니티에서 차용한 프로세스입니다. 중요한 기술을 도입하기 전에 작성자가 RFC를 작성합니다: 문제, 제안된 해결책, 대안, 구현 계획. 팀이 토론하고, 투표하고, 승인하거나 거부합니다. RFC는 투명성을 만들고 “조용한” 아키텍처 결정을 방지합니다.

기술 레이더 (ThoughtWorks Technology Radar)는 분류 도구입니다: Adopt, Trial, Assess, Hold. 팀은 정기적으로 레이더를 검토하고 상태를 업데이트합니다. 이는 “유행”과 “유용”을 구분하고 검증되지 않은 기술이 중요한 코드에 도입되는 것을 방지하는 데 도움이 됩니다.

일관성 원칙

아키텍처의 가장 중요한 특성은 일관성입니다. 프로젝트 전체에서 사용되는 아주 좋지 않은 도구도 하나의 모듈에서만 사용되는 최고의 도구보다 낫습니다. 일관성은 인지 부하를 줄이고 온보딩을 간소화하며 코드를 예측 가능하게 만듭니다.

자주 묻는 질문

프랑켄슈타인이 polyglot persistence 사용과 어떻게 다른가요?

Polyglot persistence는 다른 작업에 다른 DB를 의식적으로 사용하는 것입니다 (PostgreSQL은 트랜잭션, Redis는 캐시, Elasticsearch는 검색). 프랑켄슈타인은 전략 없는 혼란스러운 혼합입니다. 차이는 아키텍처 결정의 존재에 있습니다: polyglot은 계획이고 프랑켄슈타인은 그 부재입니다.

마이크로서비스 아키텍처가 프랑켄슈타인으로 변할 수 있나요?

, 그리고 이것은 흔한 문제입니다. 각 마이크로서비스가 중앙 집중식 표준 없이 자체 언어, 자체 DB, 자체 프로토콜 및 자체 배포 방식을 사용할 때 분산 프랑켄슈타인이 생깁니다. 마이크로서비스에는 공통 표준이 중요합니다: 통일된 프로토콜 (REST/gRPC), 공통 로그 형식, 중앙 집중식 관찰 가능성.

팀이 새 기술을 사용하지 않도록 어떻게 설득하나요?

금지하지 말고 — 안내하세요. 작성자에게 RFC를 작성하도록 제안하세요: 기존 솔루션이 왜 적합하지 않은지, 어떤 대안을 고려했는지, 마이그레이션을 어떻게 할 것인지 설명합니다. 종종 RFC를 작성하는 과정에서 개발자 스스로 새 기술이 필요하지 않다는 것을 깨닫습니다. RFC가 설득력 있으면 구현하되 계획과 제약 조건을 가지고.

레거시 프로젝트에서 프랑켄슈타인을 어떻게 처리하나요?

먼저 인벤토리, 그런 다음 표준화. 한 번에 모든 것을 다시 작성하려고 하지 마세요. 하나의 레이어 (예: HTTP 클라이언트 또는 로깅)를 선택하고, 하나의 도구를 고르고, ADR을 작성하고, 점진적으로 마이그레이션하세요. Strangler Fig 패턴 — 애플리케이션을 중단하지 않고 이전 구성 요소를 하나씩 새 것으로 교체하세요.

하나의 프로젝트에 최적의 기술 수는?

적을수록 좋습니다. 이상적으로는 하나의 언어, 하나의 프레임워크, 하나의 DB, 하나의 로깅 방식. 현실적으로는 2-3개 언어 (명확한 분할), 1-2개 DB, 1-2개 프레임워크. 추가 기술마다 팀의 인지 부하와 유지보수 비용이 증가합니다.

요약

  • 프랑켄슈타인 은 시스템이 이질적인 호환되지 않는 구성 요소에서 조립되는 안티패턴입니다
  • 주요 원인: 아키텍트 부재, 프로젝트 통합, 통제되지 않은 실험
  • 결과 — 복잡한 온보딩, 예측 불가능한 동작, 보안 문제
  • ADR 및 RFC는 아키텍처 절충주의를 방지하는 핵심 프로세스입니다
  • 원칙 “작업당 하나의 도구”는 예방의 기초입니다
  • 리팩터링은 기술 스택의 인벤토리와 표준화로 시작됩니다
  • 아키텍처 일관성은 하위 작업을 위한 “최고의 도구”보다 더 중요합니다

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기