설계 리뷰 를 하다 보면, 발표 자료 에선 멋진데 6 개월 뒤 운영 에서 피 를 흘리는 패턴 들 이 있다. 공통점 은 “만들 때 는 좋아 보이고, 운영 할 때 아프다” 는 것. 그래서 RFC 첫 장 에서 잡으면 한 분기 를 아낀다. 이 글 은 그 6 가지 를 정리 한다.

좋아 보이는데 운영에서 아픈 설계 6가지 — Resume-driven / Premature abstraction / Distributed monolith / Shared mutable DB / Silent failure / No rollback path


01. Resume-driven design — 이력서 를 위한 기술 선택

“이력서 에 쓰고 싶어서” Kubernetes / Kafka / Rust 를 도입.

증상: 문제 가 도구 를 부르는 게 아니라, 도구 가 문제 를 찾아 다닌다. “Kafka 써보고 싶어서” 트래픽 도 없는 서비스 에 이벤트 브로커 를 얹는다.

왜 아픈가: 안 써도 될 복잡도 를 운영 이 떠안는다. Kafka 하나 = 브로커 운영·파티션·컨슈머 랙·순서 보장 이라는 영구 세금.

처방: “이 도구 가 어떤 통증 을 없애나?” 를 못 답하면 빼라. 도구 는 통증 에 대한 응답 이어야 한다. (→ MSA 의 기준 과 같은 논리 — 유행 이 아니라 신호.)


02. Premature abstraction — 조기 추상화

“나중에 결제 가 바뀔 수도 있으니” 인터페이스 를 7 층 으로.

증상: 아직 오지 않은 요구 를 위해 추상 계층 을 미리 쌓는다. Factory 의 Strategy 의 Adapter 의…

왜 아픈가: 정작 판 을 바꿀 때, 그 7 층 을 다 헤치고 들어가야 해서 디버깅 이 5 배 어려워진다. 추상화 는 유연성 을 준다지만, 잘못된 추상화 는 잘못된 방향 으로만 유연하다.

처방: 백엔드 설계 세 축“의심 되면 단순 쪽”. 추상화 는 중복 이 3 번 나타난 뒤에 도 늦지 않다(Rule of Three). 미래 를 추측 하지 말고, 현재 를 단순 하게.


03. Distributed monolith — 분산 된 모놀리스

MSA 로 포장 됐는데, 서로 동기 로 강결합.

증상: 서비스 는 쪼갰는데 A→B→C 를 동기 호출 로 줄줄이 엮는다. 하나 죽으면 다 죽는다.

왜 아픈가: 모놀리스 의 단점(강결합) 은 다 가지고, MSA 의 장점(독립성) 은 하나 도 못 가진다. 최악 의 조합. 배포 는 7 번 하는데, 실제 로는 한 덩어리.

처방: 경계 를 넘는 호출 은 비동기·이벤트 로. 정산 에서 나 는 결제→정산 을 Outbox 이벤트 로 풀어 서비스 가 서로 를 직접 기다리지 않게 했다. (→ 정산 시스템 을 KPI 로)


04. Shared mutable database — 공유 가변 DB

서비스 는 쪼개졌는데 DB 는 하나. 한쪽 이 컬럼 을 바꾸면 다른 쪽 이 깨진다.

증상: 여러 서비스 가 같은 테이블 을 마음대로 쓰기. 스키마 가 숨은 공유 계약 이 되어, 한쪽 의 변경 이 다른 쪽 을 조용히 부순다.

왜 아픈가: 코드 는 분리 됐는데 데이터 결합 은 그대로. 사실상 같은 시스템 인데 경계 만 그린 척.

처방 — 내가 쓴 방법: 정산 은 order 의 테이블 을 공유 하되, Read-only Projection 으로 접근 한다. @Immutable JPA 엔티티 로 읽기 전용 매핑 → 쓰기 는 소유 서비스 만, 나머지 는 읽기 만. ArchUnit 으로 이 규칙 을 자동 검증. 즉 “공유 하되 가변 하지 않게” 만들면 이 안티패턴 을 피한다.


05. Silent failure — 조용한 실패

try/catch 로 다 삼키고 로그 도 안 남긴다.

증상: 예외 를 잡아서 아무 일 없는 척 넘어간다. 에러 는 안 뜨는데, 데이터 는 조용히 망가진다.

왜 아픈가: “에러 가 안 뜬다” 가 가장 위험한 상태 다. 터지는 버그 는 고칠 수 있지만, 조용히 틀린 버그 는 몇 달 뒤 정합성 사고 로 돌아온다. 특히 돈 에선 치명적.

처방: 실패 는 시끄럽게 만들어라. 삼킬 거면 최소 로그 + 메트릭. 나 는 이걸 관측 으로 강제 한다 — ELK 로 전 로그 색인, Prometheus 알림, Outbox 발행 실패 를 메트릭 으로. 조용한 실패 는 관측 가능성 으로만 잡는다. (→ 쿠버네티스 를 KPI 로 의 관측 3 층)


06. No rollback path — 되돌릴 길 이 없음

“잘 될 겁니다” 시나리오 만 있고, “잘 안 되면 어떻게 되돌리나” 가 없다.

증상: 배포·마이그레이션 계획 에 성공 경로 만 있다. 실패 하면? — 침묵.

왜 아픈가: 운영 은 실패 를 전제 로 해야 한다. 되돌릴 수 없는 배포 는, 터지는 순간 선택지 가 0 이 된다.

처방: 모든 변경 에 롤백 경로 를 먼저 설계. 나 는 가역성↔속도 축 으로 접근 한다 — 되돌리기 쉬운 건 GitOps git revert·feature flag 로, DB 파괴적 변경 은 단계적(add → backfill → switch → drop)으로 각 단계 를 되돌릴 수 있게. “롤백 어떻게?” 에 답 못 하는 RFC 는 통과 시키지 않는다.


관통 하는 진단 — 운영 을 상상 했는가

6 가지 는 하나 의 질문 으로 수렴 한다: “이걸 만든 나 말고, 6 개월 뒤 새벽 3 시 에 장애 로 깨어난 사람 을 상상 했는가?”

  • Resume-driven / Premature abstraction → 지금 필요 없는 복잡도 를 미래 에 떠넘김
  • Distributed monolith / Shared mutable DB → 경계 를 그린 척 만 하고 결합 은 그대로
  • Silent failure / No rollback path → 실패 를 설계 에서 지워버림

좋은 설계 는 잘 될 때 가 아니라 안 될 때 를 상상 한 설계다. RFC 첫 장 에서 이 6 개 를 체크리스트 로 돌리면, 한 분기 어치 의 통증 을 미리 지불 하지 않아도 된다.

아름다운 아키텍처 가 아니라, 새벽 에 안 깨우는 아키텍처 를 만들자.