추상화는 현재의 생산성을 올리지만,
과하면 미래의 장애 분석 비용을 폭발시킨다.
1️⃣ “추상화는 힘이다” — 왜 맞는 말인가
추상화의 실질적 힘
- 복잡성 은닉
- 변경 지점 최소화
- 코드 재사용
- 협업 가능
자바/스프링에서의 예
- 인터페이스
- 서비스 레이어
- 공통 모듈
- 프레임워크(eGovFrame, Spring)
👉 지금 당장 개발 속도 + 팀 생산성은 추상화가 만든다.
2️⃣ “하지만 과하면” — 언제부터 독이 되는가
추상화가 과해지는 시점
① 레이어가 문제를 숨길 때
Controller → Service → Facade → Manager → Helper → Util
- 장애 시 스택트레이스 50줄
- “어디서 터졌는지” 바로 안 보임
② 행위보다 구조 설명이 더 어려울 때
processContext.execute( new AbstractProcessor() { @Override protected void doInternal(Context ctx) { ... } } );
- 실제 비즈니스 로직: 3줄
- 추상화 코드: 300줄
👉 코드는 DRY하지만, 사고는 WET
③ 디버깅 시 “개념 점프”가 많을 때
- 인터페이스 → 구현체 찾기
- 프록시 → 실제 클래스 추적
- AOP → 실제 호출 흐름 복원
👉 디버깅 = 추상화 해체 작업
3️⃣ “미래의 디버깅 비용”이란 정확히 뭘 말하나
디버깅 비용 = 시간 × 인지 부하 × 불확실성
추상화 과다 시스템의 특징:
- 로그가 추상 레벨에서 끊김
- 실제 데이터 흐름이 안 보임
- 호출 스택이 “프레임워크 코드”로 덮임
결과:
- 원인 파악에 몇 시간
- 수정은 5분
4️⃣ GC / 성능 이야기와 정확히 연결됨
이 문장은 GC 명언과 같은 계열입니다.
예시
- 객체 생명주기 추상화
- 컬렉션 감싸기
- 범용 DTO
- 이벤트 기반 추상화
👉 실제로는:
- 객체 수 폭증
- 메모리 사용 패턴 불투명
- GC 로그 보고도 원인 추적 어려움
5️⃣ 실무에서 “과한 추상화”의 전형
❌ SI에서 자주 보는 패턴
- 모든 걸 CommonService
- 모든 응답을 ResultWrapper<T>
- 모든 로직을 Strategy + Factory
👉 미래 유지보수자는 전부 역추적
6️⃣ 좋은 추상화 vs 나쁜 추상화
구분좋은 추상화나쁜 추상화
| 기준 | 변경 이유 | 디자인 패턴 |
| 깊이 | 얕고 명확 | 깊고 일반화 |
| 디버깅 | 따라가기 쉬움 | 추적 지옥 |
| 로그 | 구체적 | 추상적 |
7️⃣ 실무 기준 판단 문장 (중요)
“이 추상화가 없어지면
디버깅이 더 쉬워지는가?”
- YES → 과한 추상화
- NO → 유지할 가치 있음
8️⃣ 그래서 이 문장을 실무 언어로 번역하면
“지금의 깔끔함은
나중에 반드시 추적 비용으로 청구된다.”
9️⃣ 개발자 레벨 구분 명언으로 정리
- 초급: 추상화를 믿는다
- 중급: 추상화를 의심한다
- 고급: 필요한 만큼만 남긴다
1) Spring에서 디버깅 지옥 만드는 추상화 TOP 5
TOP1. 과도한 AOP (특히 @Around 체인 + 예외 삼키기)
징후
- 장애 났는데 스택트레이스가 CglibAopProxy, ReflectiveMethodInvocation으로 도배
- 실제 비즈니스 예외가 래핑/변형되어 원인 소실
- AOP에서 catch(Exception) { return default; } 같은 방어코드
왜 지옥이냐
- 호출 흐름이 코드에 안 보임(런타임에 엮임)
- “언제/어디서” 값이 바뀌는지 추적이 어렵다
가이드
- AOP는 “로깅/트랜잭션/보안” 같은 횡단 관심사만
- @Around는 최소화, 예외는 원형 유지(추가 정보만 붙여라)
TOP2. “범용” 응답/예외 래퍼 (ResultWrapper, BizExceptionFactory)
징후
- 모든 API가 CommonResponse<T> / ApiResult<T>로 감싸짐
- 예외가 항상 BusinessException(code=XXXX)로 치환됨
- 로그에 진짜 예외가 안 남고 에러코드만 남음
왜 지옥이냐
- 원인 분석이 “코드표” 읽기로 바뀜
- 예외의 stacktrace 정보 가치가 급락
가이드
- 래퍼는 최소 필드만(요청ID/에러코드/메시지)
- 서버 로그에는 원본 예외 stacktrace 반드시 남기기
- 예외 변환은 컨트롤러 어드바이스 단에서 끝내기
TOP3. “공통 서비스/매니저” 만능 계층 (CommonService, BaseManager)
징후
- 모든 서비스가 BaseService 상속
- 핵심 로직이 공통 레이어에 숨어 있음
- override/extension hook이 난무
왜 지옥이냐
- 소스 따라가다 상속 트리/훅 포인트에서 길 잃음
- “이 기능은 어디서 결정되지?”가 매번 발생
가이드
- 상속보다 조합(Composition) 우선
- 공통은 유틸/컴포넌트로 빼고, 비즈니스는 서비스에 두기
TOP4. 동적 DI/전략 패턴 과잉 (Factory + Strategy + 문자열 key)
징후
- strategyMap.get(type) 같은 분기
- type 값이 DB/설정/요청에서 와서 런타임에만 결정
- 케이스 누락 시 NPE/기본전략으로 조용히 흘림
왜 지옥이냐
- 코드 리딩만으로 실행 경로가 확정되지 않음
- 재현이 어려움(특정 데이터에서만 터짐)
가이드
- 전략은 “진짜로 독립적인 변형”일 때만
- key는 enum 고정 + default 금지(누락이면 바로 fail)
TOP5. 프록시/리플렉션 기반 인프라 추상화 (Proxy, Reflection, Generic DAO)
징후
- “공통 DAO 한 방”으로 모든 테이블 처리
- 리플렉션으로 VO 맵핑/검증
- 런타임 타입 에러가 늦게 터짐
왜 지옥이냐
- 컴파일 타임에 잡을 오류를 런타임으로 미룸
- 장애는 늦게, 메시지는 모호하게 뜬다
가이드
- 공통 DAO는 CRUD 정도까지만
- 도메인별 Repository/Mapper를 명시적으로 둬라
2) eGovFrame 공통모듈이 위험한 이유 (공공 SI 관점)
eGovFrame 자체가 위험이라기보다, “공통화”를 명분으로 SI 조직이 남용하는 방식이 위험합니다.
위험 포인트 5개
① 프레임워크/공통모듈이 “표준”이라서 손대기 어려움
- “표준이라서”라는 말로 개선이 막힘
- 레거시가 성역화됨
② 디버깅 경로가 길어짐
- 공통모듈 → 공통서비스 → 공통DAO → 공통로깅 → …
- 장애 시 분석 범위가 폭발
③ 버전/종속성 락인
- Java/Spring 업그레이드의 병목이 공통모듈에서 발생
- “이거 올리면 다른 사업도 깨짐”으로 기술부채 고착
④ 예외/로그 정책이 공통에서 결정돼서 관측성이 떨어짐
- 예외 래핑, 메시지 치환, 로깅 필터링이 공통에 박혀 있으면
- 현장 서비스에서 필요한 증거가 안 남는다
⑤ “재사용”이 아니라 “재생산”이 된다
- 공통모듈을 이해 못 해서 프로젝트마다 복붙/커스터마이징
- 결국 공통이 여러 갈래로 포크됨(진짜 최악)
한 줄 요약
eGov 공통모듈의 위험은 “기술”보다 조직 운영 방식에서 온다.
공통이 표준이 되는 순간, 디버깅/개선이 막힌다.
3) ‘이 추상화는 지워야 한다’ 판단 체크리스트
아래 10개 중 3개 이상이면 제거 후보,
5개 이상이면 제거 우선순위 높음으로 보시면 됩니다.
제거 후보 체크 10
- 디버깅할 때 항상 그 추상화 안으로 들어간다 (매번 따라감)
- 그 추상화의 목적이 “재사용”이 아니라 **“멋” 또는 “패턴 적용”**이다
- 호출 흐름이 코드에 안 보이고 런타임에서만 결정된다(AOP/Map Strategy)
- 예외가 래핑되며 원본 stacktrace 가치가 떨어진다
- 로그가 “추상 레벨”에서 끊겨서 데이터/조건이 안 남는다
- 새 요구사항이 올 때마다 그 추상화에 if/flag가 늘어난다
- 그 추상화를 이해하려면 문서/구두 지식이 필수다 (코드만으론 불가)
- 팀원이 그 추상화를 피해서 우회 구현을 한다
- 테스트 작성이 어렵고, 대부분 통합테스트로만 검증한다
- 성능/메모리 이슈가 생기면 원인 추적이 안 되고 “대충” 옵션 튜닝으로 간다
LIST
'Spring & Backend' 카테고리의 다른 글
| 좋은 자바 코드는 JVM을 신뢰하되, 감시한다 (0) | 2026.02.02 |
|---|---|
| 동시성 버그는 재현이 아니라 운의 문제다 (0) | 2026.02.02 |
| JVM 이해 (0) | 2026.02.02 |
| GC를 모르면 성능을 논하지 말고, GC를 알면 성능을 과신하지 마라 (0) | 2026.02.02 |
| try-with-resources에 대해 설명해 주세요. (0) | 2026.02.02 |
