ABOUT

성능과 운영 안정성을 함께 끌어올리는 개발자입니다.

92% Positional Error Reduction
79% p95 Latency Improvement
90%+ Long Tasks Reduction

2022.02 · 한국장학재단

우수 멘티

한국장학재단 사회 리더 대학생 멘토링 IT

2022.10 · 동작구청

우수 인재상

동작구청 우수 SW 인재

2025.05 · (주) 그랩

프로그래밍 우수상

(주) 그랩 우수 프로그램 개발

2025.05 · AWSKRUG

AWS한국사용자모임 발표

AI agent 스크립트 튜닝 관련 발표

ComputerScience

Development

Engineering

Trouble Shooting

GUESTBOOK

첫 마음부터
함께 나누는 온기

방명록 작성하러 가기

SUBSCRIBE

최신소식을
편하게 만나보세요.

추상화 (Abstraction)

도입
“복잡한 구현을 감추고, 변경 비용을 줄이는 기술”입니다.

개발 시 요구사항은 항상 바뀝니다.

DB가 MySQL에서 PostgreSQL로 바뀌고, 캐시가 Redis로 붙고,  API가 교체되고, 트래픽이 늘면 MQ를 붙이거나 샤딩을 고려합니다.

이때 코드가 특정 구현에 “딱 붙어있으면” 변화가 올 때마다 연쇄 수정이 발생합니다.

그래서 추상화는 단순 개념이 아니라 유지보수/확장성/테스트 용이성을 만드는 핵심 도구입니다.

동시에, 잘못된 추상화는 오히려 복잡도를 키워서 팀을 괴롭힐 수 도 있습니다.

정의
필요한 관점만 남기고 나머지 복잡함을 숨겨, 외부가 “무엇(What)”만 알고 사용하게 만드는 설계입니다.

추상화의 핵심은 “대충 숨기기”가 아니라 경계를 세우고(어디까지가 한 덩어리인지), 계약을 만들기(무엇을 보장하는지)입니다.

이 계약은 보통 인터페이스 / 메서드 시그니처/HTTP API 스펙 같은 형태로 드러납니다.

핵심 메시지

“좋은 추상화는 구현 변경을 ‘내부 사건’으로 만들고,
나쁜 추상화는 구현 변경을 ‘전체 수정’으로 만든다.”

- 변경을 누가 감당할지 정하는 기술 -
백엔드에서 자주 쓰는 추상화
대부분의 백엔드 추상화는 “바뀔 수 있는 것”을 감싸는 데서 시작합니다.
영역 추상화 예시 왜 추상화하나?
DB 접근 Repository / DAO SQL/스토리지 변경을 서비스에서 분리
외부 연동 Client / Adapter 벤더 교체/실패 처리 전략을 캡슐화
캐시 CacheStore In-memory ↔ Redis 전환, TTL 정책 분리
메시징 EventPublisher Kafka/RabbitMQ 교체, 재시도/순서/중복처리 통합

💡 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처럼 의도가 모호한 이름
  • 너무 많은 기능을 한 인터페이스에 몰아넣는다
  • 결국 호출자가 내부 구현을 알아야 한다(구현 누수)
  • 교체하려고 만들었는데 교체가 더 어려워진다
주의
백엔드에서 자주 터지는 함정: 구현 누수(Leaky Abstraction)

“추상화했는데도 내부를 알아야만 제대로 쓸 수 있는 상태”가 구현 누수입니다. 대표적으로 DB 추상화가 그렇습니다. ORM을 쓰더라도 N+1, 트랜잭션 경계, 인덱스 같은 개념을 모르고는 성능/정합성을 보장하기 어렵습니다.

따라서 중요한 태도는 이겁니다: 추상화는 디테일을 “완전히 제거”하는 게 아니라, “관리 가능한 수준으로 제한”하는 것입니다.

💡 TIP / 백엔드 관점 정리

DB/네트워크/분산환경은 본질적으로 누수가 있는 영역입니다.
그래서 “추상화로 몰라도 된다”가 아니라, 서비스 계층은 단순하게 유지하되, 핵심 디테일(DB 튜닝/트랜잭션/재시도/타임아웃)은 팀의 공통 지식으로 관리하는 방향이 안정적입니다.

 

가이드
“추상화할지 말지” 판단하는 흐름
1
이 부분은 바뀔 가능성이 높은가? 외부 연동/스토리지/정책/벤더 등
2
변경이 생기면 수정 범위가 크게 퍼지는가? 여러 서비스/도메인이 동시에 터지는 지점
3
계약을 최소로 만들 수 있는가? 메서드가 많아질수록 추상화는 약해짐
4
테스트가 쉬워지는가? Mock/Fake 교체로 도메인 테스트가 빨라지는가

✅ 핵심 요약

  • ✔️ 추상화는 “무엇(What)”만 드러내고 “어떻게(How)”를 숨겨 변경 비용을 줄인다.
  • ✔️ 백엔드에서는 스토리지/외부 연동/캐시/메시징처럼 바뀔 수 있는 지점이 추상화 효율이 높다.
  • ✔️ 실패 원인은 보통 너무 이른 추상화구현 누수다.
  • ✔️ 좋은 추상화는 계약이 작고 명확하며, 서비스 코드를 단순하게 만들고 테스트를 쉽게 만든다.
728x90