-
2. 추상화(Abstraction)소프트웨어 설계 원칙/2. 기본 원칙 2026. 5. 20. 15:08
추상화란?
복잡한 자료, 모듈, 시스템 등으로부터 핵심적인 개념 또는 기능을 간추려 내는 것을 말한다.
본질만 노출하고, 세부 디테일은 숨기는 것이다.
예를 들어 자동차 운전자는 운전대를 돌리고 페달을 밟으면 된다.
그 안에서 엔진이 어떻게 작동하는지, 변속기가 어떻게 움직이는지 몰라도 운전할 수 있다.운전대와 페달이라는 단순한 인터페이스 뒤에 복잡한 시스템이 숨어 있다.
이게 추상화다.
코드에서도 마찬가지다. 함수 이름만 보고 사용할 수 있고, 내부 구현은 몰라도 되는 형태가 추상화의 결과이다.추상화는 SoC 원칙을 실현하는 수단 중 하나다.
SoC가 '관심사를 분리하라'는 큰 방향이라면, 추상화는 '본질만 노출하고 세부는 숨긴다'는 SoC 원칙을 위한 보다 구체적인 행동 지침이다.
최종 목적은 같다. 유지보수가 용이한 코드를 짜는 것
사실 추상화/정보 은닉/캡슐화는 한 사고(모듈화)의 세 측면이다.
한 단위에 이름 붙이기(추상화), 무엇을 묶을지 결정(캡슐화), 무엇을 숨길지 결정(정보 은닉) 이 셋이 동시에 일어난다.
이 글에선 추상화 중심으로, 추상화가 무엇이고 어떤 종류와 방식이 있는지 알아볼 예정이다.종류와 방식을 외우고 바이블처럼 매번 따져보며 적용할 필요는 없다.
그저 추상화가 무엇이고 지켜야할 기본 원칙만 알면 된다.
이후 정보 은닉과 캡슐화는 별도 글로 다루고, 마지막 글에서 세 원칙을 통합해서 정리할 예정이다
추상화의 종류 - "어떤 것을 추상화 할 수 있는가"
1. 데이터 추상화: 데이터를 어떻게 저장/표현하는지 숨기고, 그 데이터로 할 수 있는 동작만 외부에 노출.
(예: Stack, Set, Cart 클래스)
2. 절차 추상화: 복잡한 절차를 함수로 묶어서, 호출자는 "어떻게"는 몰라도 됨. (예: validateSignup)
3. 제어 추상화: if/for 같은 제어 흐름을 함수 안에 숨김. (예: map, filter, find)
4. 다형성 추상화: 같은 인터페이스(약속) 위에서 여러 다른 구현이 가능. (자바의 abstract와 interface, 상속과 확장)
5. 함수형 추상화: 함수를 값처럼 다룸. 함수를 인자로 받거나, 반환하거나, 조합. (예: 고차 함수, HOC, 데코레이터)더보기// 함수형 추상화 예시
// 1. 함수를 인자로 받음 — 본인이 잡은 거
[1, 2, 3].map(x => x * 2);
// 2. 함수를 반환 — 함수형 추상화의 핵심
function multiplier(n: number) {
return (x: number) => x * n;
}
const double = multiplier(2);
const triple = multiplier(3);
// 3. 함수를 변수에 저장
const handler = () => console.log('hi');
// 4. 함수 조합
const process = compose(validate, transform, save);무엇을 추상화 했느냐에 따라 위와 같이 나눌 수 있다.
표로 정리하면
추상화 정의 핵심 데이터 추상화 데이터의 내부 표현을 숨기고 동작만 노출 "데이터의 구조"를 숨김 절차 추상화 복잡한 절차를 함수로 묶어 호출로 수행 "내부 동작을 어떻게 하는지"를 숨김 제어 추상화 제어 흐름(if/for)을 함수에 숨김 "흐름의 과정을 숨김"를 숨김 다형성 추상화 같은 인터페이스로 여러 다른 동작 "타입별 구현 차이"를 숨김 함수 추상화 함수를 값처럼 다룸(인자/반환/조합) "함수 자체"를 추상화함 하지만 이것은 어떤 것을 어떻게 추상화 할 수 있는지를 분류한 것일 뿐 굳이 외울 필요는 없다.
자바에서 쓰는 abstract와 interface도 추상화(다형성 추상화)이고 리액트에서의 공통 컴포넌트 분리나 함수를 만들어 쓰는 것도 추상화이다.
실제로 알게모르게 다 쓰고 있고 여러 방식을 복합적으로 사용하고 있을 것이다.
이런 방법들이 있다는 것만 알고 넘어가자.
추상화 vs 자료의 상속대부분의 자료가 추상화를 설명할 때 자동차나 동물의 상속 예시를 든다.
abstract class Animal { abstract void makeSound(); } class Dog extends Animal { void makeSound() { System.out.println("멍멍"); } } class Cat extends Animal { void makeSound() { System.out.println("야옹"); } }
이건 추상화의 한 종류(다형성 추상화)로 "여러 구체의 공통점을 부모로 묶고, 차이를 자식으로 분리"하는 추상화이다.다만 추상화는 상속만이 아니다.
- Cart 클래스가 내부 배열을 숨기는 것 → 데이터 추상화
- validateSignup 함수가 검증 절차를 숨기는 것 → 절차 추상화
- map이 반복 흐름을 숨기는 것 → 제어 추상화
이 모두가 추상화다.
상속 예시는 추상화의 일반화를 강조하지만 추상화의 본질은 더 넓다.처음에 설명 했듯 '본질만 노출, 세부 숨김' 이다.
추상화의 방향 - "추상화가 만들어지는 방향"
추상화는 두 방향으로 만들어진다.
방향 1: Bottom-Up (일반화)구체적인 사례들을 먼저 만들고, 공통점을 뽑아내 추상화 만들기.
// 1단계: 구체적 코드 먼저 작성 async function getUser(id: string) { const res = await fetch(`/api/users/${id}`); return res.json(); } async function getPost(id: string) { const res = await fetch(`/api/posts/${id}`); return res.json(); } async function getComment(id: string) { const res = await fetch(`/api/comments/${id}`); return res.json(); } // 2단계: 패턴 발견 → 공통점 뽑기 async function fetchResource<T>(path: string): Promise<T> { const res = await fetch(path); return res.json(); }구체 코드 작성 → 패턴 발견 → 추상화
방향 2: Top-Down (특수화)
먼저 추상적 개념을 정의하고, 그것의 구체적 사례를 만들기.
// 1단계: 추상적 개념 정의 interface PaymentProvider { charge(amount: number): Promise<void>; refund(chargeId: string): Promise<void>; } // 2단계: 구체적 구현 만들기 class StripeProvider implements PaymentProvider { ... } class TossProvider implements PaymentProvider { ... }추상 정의 → 구체 구현 채움
일반적인 흐름
- 1. 구체적 코드 작성 (Bottom)
- 2. 비슷한 패턴 발견
- 3. 추상화 시도 (Up)
- 4. 새 사례 추가
- 5. 한계 발견
- 6. 추상화 수정 (다시 Up)
실무에선 양방향 왔다 갔다 추상화는 한 번에 완벽하게 만들기 어렵기 때문에 계속 왔다 갔다 하면서 추상화가 정제된다.
- Bottom-Up이 좋은 경우: 도메인 잘 모를 때 / 패턴 예측 어려울 때 / 작은 프로젝트 / 빠른 검증
- Top-Down이 좋은 경우: 도메인 명확할 때 / 외부 라이브러리 설계 / 큰 시스템 / API 계약 먼저 정해질 때
일반적으로는 Bottom-Up이 안전하다.
너무 일찍 추상화하면 잘못된 일반화 위험 발생할 확률이 높다.
이 또한, 그냥 알고 넘어가면 된다
추상화의 일반 원칙 - "추상화를 어떻게 할 것인가"추상화의 본질은 한 문장으로 요약된다.
"본질만 노출하고, 세부는 숨긴다."
이 한 문장을 실행하기 위해 추상화는 세 가지 행위를 한다.
1. 묶기 (일반화)
공통적인 것들을 한 단위로 묶는다. 무엇을 한 단위로 부를지 결정하는 행위다만 "무엇을 기준으로 추상화 할 것인지" 구체적 결정 (객체로 볼지, 기능으로 볼지)은 추상화 원칙이 정해주지 않는다.
이것은 패러다임/도메인 분석의 영역이다.
2. 버리기 (세부 무시)
묶을 것을 정했다면, 나머지는 자동으로 버릴 것이 된다.
본질이 아닌 디테일은 다른 곳으로 옮기거나 숨긴다.
"무엇을 묶을까"와 "무엇을 버릴까"는 같은 결정의 두 측면이다.
3. 노출 (인터페이스)
함수 이름, 메서드 시그니처, 클래스 인터페이스로 본질을 알 수 있어야 한다.호출자는 인터페이스만 보고 사용할 수 있어야 한다.
내부 디테일은 알 필요 없다.
이 세 행위가 동시에 일어난 결과가 추상화이다.
결론
추상화는 단순히 '본질만 노출'이 아닌 '라벨링'이라고 생각한다.
무엇을 한 단위로 부를지 결정하는 사고다. 무엇을 묶고, 무엇을 버릴지 정하는 순간 추상화가 완성된다.
이 결정이 캡슐화(묶기)와 정보 은닉(차단)으로 자연스럽게 이어진다.'소프트웨어 설계 원칙 > 2. 기본 원칙' 카테고리의 다른 글
4. 캡슐화(Encapsulation) (0) 2026.05.22 3. 정보 은닉(Information Hiding) (0) 2026.05.22 1. 관심사의 분리(SOC: Seperation Of Concern) 원칙 (0) 2026.05.19