개발 시 요구사항은 항상 바뀝니다.
DB가 MySQL에서 PostgreSQL로 바뀌고, 캐시가 Redis로 붙고, API가 교체되고, 트래픽이 늘면 MQ를 붙이거나 샤딩을 고려합니다.
이때 코드가 특정 구현에 “딱 붙어있으면” 변화가 올 때마다 연쇄 수정이 발생합니다.
그래서 추상화는 단순 개념이 아니라 유지보수/확장성/테스트 용이성을 만드는 핵심 도구입니다.
동시에, 잘못된 추상화는 오히려 복잡도를 키워서 팀을 괴롭힐 수 도 있습니다.
추상화의 핵심은 “대충 숨기기”가 아니라 경계를 세우고(어디까지가 한 덩어리인지), 계약을 만들기(무엇을 보장하는지)입니다.
이 계약은 보통 인터페이스 / 메서드 시그니처/HTTP API 스펙 같은 형태로 드러납니다.
“좋은 추상화는 구현 변경을 ‘내부 사건’으로 만들고,
나쁜 추상화는 구현 변경을 ‘전체 수정’으로 만든다.”
💡 TIP / 참고사항
초보일수록 “인터페이스부터 만들자”로 가기 쉬운데, 실무에서는 반대로 “변경이 실제로 생기는 지점”에만 추상화를 두는 편이 안전합니다.
바뀌지 않는 것까지 감싸면 코드가 늘고, 이해 비용만 올라갑니다.
// 1) 계약(추상화): "저장"이라는 능력만 정의 (What) interface FileStorage { String save(String path, byte[] bytes); // 저장 후 접근 키 반환 byte[] load(String key); } // 2) 구현 A: 로컬 디스크 class LocalFileStorage implements FileStorage { public String save(String path, byte[] bytes) { // 디스크 저장 로직 (생략) return "local:" + path; } public byte[] load(String key) { return null; } } // 3) 구현 B: S3 (또는 다른 오브젝트 스토리지) class S3FileStorage implements FileStorage { public String save(String path, byte[] bytes) { // S3 putObject 로직 (생략) return "s3:" + path; } public byte[] load(String key) { return null; } } // 4) 서비스는 구현에 의존하지 않고 계약에 의존한다. class ProfileService { private final FileStorage storage; public ProfileService(FileStorage storage) { this.storage = storage; } public String uploadAvatar(String userId, byte[] imageBytes) { return storage.save("avatar/" + userId + ".png", imageBytes); } }
👍 GOOD
- 이름이 도메인/의도를 말한다 (FileStorage, PaymentGateway)
- 메서드 수가 적고 꼭 필요한 기능만 제공한다
- 호출자가 내부 구현을 몰라도 사용 가능하다
- 테스트에서 가짜 구현(Fake/Stub)으로 교체하기 쉽다
👎 BAD
- Manager/Util처럼 의도가 모호한 이름
- 너무 많은 기능을 한 인터페이스에 몰아넣는다
- 결국 호출자가 내부 구현을 알아야 한다(구현 누수)
- 교체하려고 만들었는데 교체가 더 어려워진다
“추상화했는데도 내부를 알아야만 제대로 쓸 수 있는 상태”가 구현 누수입니다. 대표적으로 DB 추상화가 그렇습니다. ORM을 쓰더라도 N+1, 트랜잭션 경계, 인덱스 같은 개념을 모르고는 성능/정합성을 보장하기 어렵습니다.
따라서 중요한 태도는 이겁니다: 추상화는 디테일을 “완전히 제거”하는 게 아니라, “관리 가능한 수준으로 제한”하는 것입니다.
💡 TIP / 백엔드 관점 정리
DB/네트워크/분산환경은 본질적으로 누수가 있는 영역입니다.
그래서 “추상화로 몰라도 된다”가 아니라, 서비스 계층은 단순하게 유지하되, 핵심 디테일(DB 튜닝/트랜잭션/재시도/타임아웃)은 팀의 공통 지식으로 관리하는 방향이 안정적입니다.
✅ 핵심 요약
- ✔️ 추상화는 “무엇(What)”만 드러내고 “어떻게(How)”를 숨겨 변경 비용을 줄인다.
- ✔️ 백엔드에서는 스토리지/외부 연동/캐시/메시징처럼 바뀔 수 있는 지점이 추상화 효율이 높다.
- ✔️ 실패 원인은 보통 너무 이른 추상화와 구현 누수다.
- ✔️ 좋은 추상화는 계약이 작고 명확하며, 서비스 코드를 단순하게 만들고 테스트를 쉽게 만든다.