Program Against Abstractions
Program Against Abstractions(추상화에 대하여 프로그래밍하라) 는 소프트웨어 아키텍처 및 설계에서 핵심 원칙 중 하나로, 구체적인 구현체가 아닌 추상화 계층 (인터페이스, 추상 클래스 등) 에 의존해 코드를 작성하는 것을 의미한다. 이 원칙은 시스템의 결합도를 낮추고, 변화에 유연하게 대응할 수 있도록 하며, 유지보수와 테스트, 확장성을 크게 향상시킨다. 객체지향 설계의 DIP(의존성 역전 원칙) 와도 밀접하게 연관되어 있다.
핵심 개념
Program Against Abstractions란 코드를 작성할 때 구체적인 구현체가 아니라, 인터페이스나 추상 클래스 등 추상화 계층에 의존하도록 설계하는 것이다.
Program Against Abstractions의 핵심 개념들:
- 추상화 (Abstraction): 구현 세부사항을 숨기고 본질적인 특성만을 노출하는 개념
- 구체화 (Concretion): 실제 구현을 포함한 구체적인 클래스나 모듈
- 의존성 역전 (Dependency Inversion): 고수준 모듈이 저수준 모듈에 의존하지 않고 추상화에 의존하는 원칙
- 인터페이스 (Interface): 구현을 강제하는 계약을 정의하는 추상화 도구
- 느슨한 결합 (Loose Coupling): 컴포넌트 간의 의존성을 최소화하는 설계 방식
배경
역사적 배경:
- 1990 년대 객체지향 프로그래밍의 발전과 함께 등장
- Robert C. Martin(Uncle Bob) 이 SOLID 원칙의 일부로 체계화
- 소프트웨어 복잡성 증가에 따른 유지보수성 문제 해결을 위해 발달
등장 배경:
- 전통적인 하향식 설계에서 발생하는 강한 결합 문제
- 코드 변경 시 연쇄적인 수정이 필요한 상황
- 테스트 가능성과 재사용성의 필요성 증대
목적 및 필요성
주요 목적:
- 유연성 확보: 구현체 변경 시 클라이언트 코드 수정 최소화
- 확장성 향상: 새로운 구현체 추가 시 기존 코드 영향 최소화
- 테스트 용이성: Mock 객체를 활용한 단위 테스트 가능
- 재사용성 증대: 다양한 구현체와 함께 사용 가능한 코드 작성
필요성:
- 대규모 소프트웨어 시스템의 복잡성 관리
- 지속적인 요구사항 변경에 대응
- 팀 개발 환경에서의 모듈 독립성 확보
- 소프트웨어 품질 및 유지보수성 향상
주요 기능 및 역할
- 의존성 관리: 컴포넌트 간 의존성을 추상화 계층으로 관리
- 변경 격리: 구현 변경이 다른 컴포넌트에 미치는 영향 차단
- 인터페이스 정의: 명확한 계약을 통한 컴포넌트 간 상호작용 정의
- 확장성 제공: 새로운 기능 추가 시 기존 코드 변경 최소화
핵심 원칙
- 추상화 의존 원칙: 구체적인 클래스가 아닌 추상화에 의존
- 인터페이스 분리 원칙: 클라이언트가 사용하지 않는 메서드에 의존하지 않음
- 대체 가능성 원칙: 추상화를 구현한 모든 객체는 상호 교체 가능
- 단일 책임 원칙: 각 인터페이스는 하나의 명확한 책임만 가짐
주요 원리
graph TD A[클라이언트 코드] --> B[추상화 계층] B --> C[구현체 A] B --> D[구현체 B] B --> E[구현체 C] F[고수준 모듈] --> G[인터페이스] H[저수준 모듈] --> G style B fill:#e1f5fe style G fill:#e1f5fe
핵심 원리:
- 의존성 역전: 고수준과 저수준 모듈 모두 추상화에 의존
- 추상화 안정성: 추상화는 구체화보다 안정적
- 계약 우선: 구현보다 인터페이스 계약을 우선시
- 느슨한 결합: 컴포넌트 간 직접적인 의존성 제거
작동 원리
sequenceDiagram participant Client as 클라이언트 participant Interface as 인터페이스 participant Implementation as 구현체 Client->>Interface: 메서드 호출 Interface->>Implementation: 실제 구현 실행 Implementation-->>Interface: 결과 반환 Interface-->>Client: 결과 전달
작동 메커니즘:
- 인터페이스 정의: 필요한 기능을 추상 메서드로 정의
- 구현체 작성: 인터페이스를 구현하는 구체적인 클래스 작성
- 의존성 주입: 클라이언트에 인터페이스 타입으로 구현체 제공
- 런타임 바인딩: 실행 시점에 실제 구현체 메서드 호출
작동 원리 및 구조
- 클라이언트 (사용자) 는 인터페이스 (추상화) 에 의존.
- 실제 구현체는 인터페이스를 구현 (implements) 하여 제공.
- 클라이언트는 구현체의 세부사항을 몰라도 인터페이스를 통해 기능 사용.
classDiagram class IService { <<interface>> +execute() : Result +validate() : boolean } class ServiceA { +execute() : Result +validate() : boolean } class ServiceB { +execute() : Result +validate() : boolean } class Client { -service : IService +Client(service: IService) +performAction() : void } IService <|-- ServiceA IService <|-- ServiceB Client --> IService
구분 | 구성요소 | 기능 | 역할 | 특징 |
---|---|---|---|---|
필수 | 추상화 계층 (Interface/Abstract Class) | 계약 정의 | 구현체와 클라이언트 간 중개 | 안정적, 변경 빈도 낮음 |
구현체 (Concrete Implementation) | 실제 로직 구현 | 비즈니스 로직 처리 | 변경 가능, 교체 가능 | |
클라이언트 (Client) | 서비스 사용 | 추상화를 통한 기능 호출 | 구현체에 독립적 | |
선택 | 의존성 주입 컨테이너 (DI Container) | 객체 생성 및 주입 | 의존성 관리 자동화 | 설정 기반, 런타임 관리 |
팩토리 패턴 (Factory Pattern) | 객체 생성 추상화 | 구체적인 생성 로직 캡슐화 | 생성 복잡성 숨김 | |
어댑터 패턴 (Adapter Pattern) | 인터페이스 변환 | 기존 코드와 새 인터페이스 연결 | 레거시 코드 통합 |
구현 기법
인터페이스 기반 설계
정의: 모든 의존성을 인터페이스로 정의하는 기법
구성: 인터페이스 + 구현체 + 클라이언트
목적: 강한 결합 제거 및 유연성 확보
실제 예시:
|
|
의존성 주입 (Dependency Injection)
정의: 외부에서 의존성을 주입하는 기법
구성: 생성자 주입, 세터 주입, 인터페이스 주입
목적: 객체 생성과 사용의 분리
시스템 구성:
graph LR A[DI Container] --> B[Service Registration] A --> C[Dependency Resolution] A --> D[Lifecycle Management] E[Constructor Injection] --> F[Client Object] G[Interface] --> F H[Implementation] --> G
전략 패턴 (Strategy Pattern)
정의: 알고리즘을 캡슐화하고 교체 가능하게 만드는 패턴
구성: Context + Strategy Interface + Concrete Strategies
목적: 런타임에 알고리즘 변경 가능
시나리오: 다양한 할인 정책을 적용하는 쇼핑몰 시스템
장점과 단점
구분 | 항목 | 설명 |
---|---|---|
✅ 장점 | 유연성 | 구현체 변경 시 클라이언트 코드 수정 불필요 |
확장성 | 새로운 구현체 추가가 용이함 | |
테스트 용이성 | Mock 객체를 활용한 단위 테스트 가능 | |
재사용성 | 동일한 인터페이스로 다양한 구현체 사용 | |
유지보수성 | 변경 영향 범위 최소화 | |
⚠ 단점 | 복잡성 증가 | 추가적인 추상화 계층으로 인한 복잡성 |
성능 오버헤드 | 간접 호출로 인한 미미한 성능 저하 | |
과도한 추상화 | 불필요한 인터페이스 생성 가능성 | |
학습 비용 | 초기 설계 시 높은 이해도 요구 |
도전 과제
도전 과제 | 설명 | 해결책 |
---|---|---|
기존 코드베이스 적용 어려움 | 레거시 코드에 추상화 적용이 어렵고, 직접 의존이 많음 | 점진적 리팩토링, 추상화된 인터페이스 도입으로 결합도 감소 |
과도한 추상화 | 단순 기능에도 불필요한 인터페이스를 생성하여 오히려 복잡도 증가 | YAGNI 원칙 적용, 실제 사용 시점 기준으로 추상화 적용 결정 |
인터페이스 설계의 어려움 | 적절한 책임 분리와 추상화 수준 정의가 어려움 | 도메인 전문가와 협업, 반복적 설계 검토 및 리팩토링 |
런타임 성능 영향 | 추상화 계층 증가로 호출 성능 저하 발생 가능 | 프로파일링 기반 성능 측정, 성능 중요 구간은 직접 구현 고려 |
분류에 따른 종류 및 유형
분류 기준 | 분류/유형 | 설명 | 장점 | 단점 |
---|---|---|---|---|
추상화 수준 | 고수준 추상화 | 비즈니스 로직 중심 인터페이스 | 변경에 유연, 테스트 용이 | 구현과 거리감 있음 |
저수준 추상화 | 기술적 세부사항 캡슐화 | 모듈 교체 용이, 유지보수 편리 | 과도시 설계 복잡도 증가 | |
구현 범위 | 단일 책임 인터페이스 | 하나의 역할만 수행 | SRP(단일 책임 원칙) 준수 | 인터페이스 수 증가 |
복합 인터페이스 | 관련된 여러 기능 통합 | 관련 기능 일괄 관리 | 응집도 낮으면 결합도 증가 | |
생명 주기 | 정적 인터페이스 | 컴파일 시점에 타입 결정 | 타입 안정성 보장, IDE 지원 | 유연성 낮음 |
동적 인터페이스 | 런타임 시점에 구조 해석 | 높은 유연성, 빠른 프로토타이핑 | 런타임 오류 위험 | |
타입 정의 | 명시적 인터페이스 | 명확한 계약 정의 | 문서화 및 검증 용이 | 구현 코드 많아짐 |
제네릭 인터페이스 | 타입 매개변수로 동작 추상화 | 재사용성, 타입 안전성 | 설계 복잡도 증가 | |
타입 시스템 | 덕 타이핑 | 이름이 아닌 구조에 기반한 인터페이스 | 유연한 의존성, 빠른 개발 가능 | 일관성 부족, 추적 어려움 |
실무 적용 예시
분야 | 적용 사례 | 추상화 | 구현체 | 효과 |
---|---|---|---|---|
데이터 액세스 | Repository Pattern | IUserRepository | MySQLUserRepository, MongoUserRepository | DB 변경 시 비즈니스 로직 보호 |
외부 서비스 연동 | Gateway Pattern | IPaymentGateway | StripeGateway, PayPalGateway | 결제 서비스 변경 용이 |
알림 시스템 | 알림 서비스 | INotificationService | EmailNotifier, SMSNotifier, PushNotifier | 다중 알림 채널 지원 |
로깅 시스템 | 로깅 추상화 | ILogger | FileLogger, DatabaseLogger, CloudLogger | 로깅 전략 변경 자유도 |
캐싱 시스템 | 캐시 추상화 | ICacheService | MemoryCache, RedisCache, DistributedCache | 캐싱 전략 최적화 |
활용 사례
사례 1: 전자상거래 플랫폼의 주문 처리 시스템
시나리오: 대형 온라인 쇼핑몰에서 다양한 결제 방식 (신용카드, PayPal, 가상계좌, 암호화폐) 과 배송 방식 (일반배송, 택배, 드론배송) 을 지원하는 주문 처리 시스템을 구축
시스템 구성:
graph TB subgraph "클라이언트 계층" A[OrderController] end subgraph "서비스 계층" B[OrderService] end subgraph "추상화 계층" C[IPaymentProcessor] D[IShippingService] E[IInventoryService] end subgraph "구현 계층" F[CreditCardProcessor] G[PayPalProcessor] H[StandardShipping] I[ExpressShipping] J[DatabaseInventory] K[CacheInventory] end A --> B B --> C B --> D B --> E C --> F C --> G D --> H D --> I E --> J E --> K
Workflow:
sequenceDiagram participant Customer as 고객 participant OrderController as 주문컨트롤러 participant OrderService as 주문서비스 participant PaymentProcessor as 결제처리기 participant ShippingService as 배송서비스 participant InventoryService as 재고서비스 Customer->>OrderController: 주문 요청 OrderController->>OrderService: processOrder() OrderService->>InventoryService: checkAvailability() InventoryService-->>OrderService: 재고 확인 결과 OrderService->>PaymentProcessor: processPayment() PaymentProcessor-->>OrderService: 결제 결과 OrderService->>ShippingService: arrangeShipping() ShippingService-->>OrderService: 배송 안내 OrderService-->>OrderController: 주문 완료 결과 OrderController-->>Customer: 주문 확인
역할 및 책임:
- OrderService: 주문 비즈니스 로직 조율, 추상화된 인터페이스만 사용
- IPaymentProcessor: 결제 처리 추상화, 다양한 결제 방식 지원
- IShippingService: 배송 서비스 추상화, 배송 방식 선택 유연성
- IInventoryService: 재고 관리 추상화, 다양한 데이터 소스 지원
구현 코드 예시:
|
|
실무에서 효과적으로 적용하기 위한 고려사항 및 주의할 점
구분 | 고려사항 | 설명 | 권장사항 |
---|---|---|---|
설계 단계 | 책임 분리 | 각 모듈이 고유한 책임을 가지도록 설계 | 단일 책임 원칙 (SRP) 및 인터페이스 분리 원칙 (ISP) 적용 |
적절한 추상화 수준 | 과도하거나 부족한 추상화는 설계 복잡성을 증가시킴 | 실제 변경 가능성이 높은 부분에만 추상화 도입 | |
인터페이스 명확성 | 추상화된 메서드는 구체성과 일반성의 균형이 필요 | 도메인 용어 기반의 직관적인 인터페이스 정의 | |
인터페이스 안정성 | 인터페이스 변경 시 영향 범위가 커질 수 있음 | 충분한 도메인 분석 및 의존성 최소화 전략 수립 | |
구현 단계 | 의존성 주입 전략 | 추상 타입에 의존하도록 구성하며, 주입 방식에 따라 유연성이 달라짐 | 생성자 주입을 기본으로 하며, 필요 시 세터/인터페이스 주입 활용 |
구현체 수 증가 관리 | 다양한 구현체로 인해 관리 포인트 증가 가능 | DI(Dependency Injection) 컨테이너 및 설정 기반 주입 활용 | |
생명주기 및 스코프 관리 | 인터페이스에 주입된 객체의 생명주기 고려 필요 | 싱글톤, 프로토타입 등 컨테이너 스코프 활용 | |
예외 처리 전략 | 추상화 계층에서 발생하는 예외의 처리 일관성 유지 | 도메인별 커스텀 예외 정의 및 매핑 | |
테스트 단계 | 테스트 용이성 확보 | 추상화는 테스트 대상 분리를 용이하게 함 | Mock, Stub, Spy 등 테스트 더블 패턴 활용 |
통합 테스트 전략 | 추상화된 계층을 실제 구현체와 검증 필요 | TestContainers, Spring Profile 등으로 통합 테스트 구성 | |
운영 단계 | 성능 영향 모니터링 | 추상화로 인한 간접 호출이 성능에 미치는 영향 | APM(Application Performance Monitoring) 도구로 병목 식별 |
설정 기반 유연성 확보 | 런타임에 구현체 교체 등 유연한 구성 필요 | 외부 설정 파일, 환경 변수 활용 |
최적화하기 위한 고려사항 및 주의할 점
구분 | 고려사항 | 설명 | 권장사항 |
---|---|---|---|
메모리 최적화 | 객체 생성 비용 | 인터페이스 구현체 생성 및 의존성 주입 시 메모리 사용량 증가 | 객체 풀링, 싱글턴 패턴, Lazy Initialization 적용 |
가비지 컬렉션 영향 | 불필요한 객체가 과도하게 생성되어 GC(Garbage Collection) 압박 유발 | 불변 객체 (Immutable Object), 재사용 전략 도입 | |
실행 성능 | 다형성 및 간접 호출 비용 | 인터페이스/추상 클래스 기반 호출은 직접 호출보다 성능 저하 발생 | 핫스팟 분석 후 성능 민감한 경로에 직접 구현체 사용 |
인라인 최적화 제한 | 컴파일러 최적화 (인라이닝) 가 추상화로 인해 제한될 수 있음 | JIT 프로파일 기반 최적화 (PGO: Profile-Guided Optimization) 활용 | |
추상화 구조 | 과도한 추상화 | 불필요한 추상화 계층은 복잡성과 호출 비용 증가 | KISS 원칙 적용, 필요한 경우에만 추상화 도입 |
디버깅/추적 어려움 | 추상화가 깊어질수록 문제 원인 분석이 어려움 | 명확한 계층 구조 문서화 및 책임 구분 | |
의존성 관리 | DI 프레임워크 초기화 비용 | 의존성 주입 (DI: Dependency Injection) 시 생성 및 주입 비용 발생 | 싱글턴 활용, 필요한 경우 Lazy 주입 적용 |
캐싱 전략 | 구현체별 캐싱 | 각기 다른 구현체가 다른 방식으로 캐싱 처리 | 데코레이터 패턴 활용, 캐싱 로직 분리 |
리플렉션/메타데이터 캐싱 | 런타임에 발생하는 리플렉션 비용 증가 | 타입 정보 캐싱 등 메타데이터 최적화 적용 | |
네트워크 최적화 | 원격 호출/직렬화 비용 | 분산 시스템 내 인터페이스 호출은 네트워크 오버헤드 발생 | 배치 처리, 비동기 패턴, 효율적 직렬화 (예: Protocol Buffers 등) 적용 |
주목할 내용 정리
주제 | 항목 | 설명 |
---|---|---|
추상화 | 인터페이스 / 추상 클래스 | 구현을 분리하고 유연한 구조를 제공하며, 변경에 강한 설계를 가능하게 함 |
설계 원칙 | DIP (Dependency Inversion Principle) | 고수준 모듈과 저수준 모듈 모두가 추상화에 의존하도록 설계하는 SOLID 원칙 |
ISP (Interface Segregation Principle) | 클라이언트가 사용하지 않는 메서드에 의존하지 않도록 인터페이스를 분리하는 원칙 | |
OCP (Open-Closed Principle) | 시스템은 확장에는 열려 있고 수정에는 닫혀 있어야 함 | |
디자인 패턴 | 전략 패턴 (Strategy) | 알고리즘을 캡슐화하여 런타임에 교체 가능하게 만드는 패턴 |
팩토리 패턴 (Factory) | 객체 생성 로직을 추상화하여 캡슐화 | |
어댑터 패턴 (Adapter) | 서로 다른 인터페이스 간의 호환성을 제공하는 연결 패턴 | |
데코레이터 패턴 (Decorator) | 객체에 기능을 동적으로 추가 가능 | |
아키텍처 패턴 | 계층형 아키텍처 | 계층 간 추상화를 통한 모듈 간 결합도 감소 |
헥사고날 아키텍처 (Hexagonal) | 포트와 어댑터를 통해 외부 의존성과 내부 도메인을 분리 | |
클린 아키텍처 (Clean Architecture) | 의존성의 방향을 내부 비즈니스 로직으로만 향하도록 설계 | |
기술적 구현 | DI (Dependency Injection) | 런타임에 외부에서 객체를 주입하여 결합도를 낮추는 기법 |
IoC (Inversion of Control) | 객체 생성 및 제어 책임을 프레임워크 또는 컨테이너로 위임 | |
AOP (Aspect-Oriented Programming) | 로깅, 트랜잭션 등 횡단 관심사를 모듈화 | |
코드 유지보수 | 인프라 분리 | 구현 클래스 교체가 쉬워 테스트 및 유지보수 용이 |
객체 캡슐화 | 인터페이스 중심 설계 | 클라이언트는 구체 구현이 아닌 추상에 의존하여 시스템의 유연성과 확장성이 증가 |
적용 분야 | 결제, 로깅, 데이터 접근 | 외부 변화에 강한 시스템 구현에 활용 가능 |
하위 주제로 분류한 추가 학습 내용
카테고리 | 주제 | 설명 |
---|---|---|
설계 원칙 | Dependency Inversion Principle | 고수준 모듈이 저수준 모듈에 의존하지 않도록 추상화 중심으로 설계 |
GRASP 원칙 | 객체지향 설계를 위한 9 가지 핵심 원칙 (Information Expert, Creator 등) | |
Law of Demeter | 최소 지식 원칙으로 객체 간 직접적인 결합 최소화 | |
Hollywood Principle | " 우리가 당신을 호출하겠다 " 는 제어 흐름의 역전 원칙 | |
디자인 패턴 | Factory Pattern | 객체 생성 책임을 추상화하여 구체 클래스를 감춤 |
Service Locator Pattern | 런타임 시점에 서비스 인스턴스를 찾아주는 중앙 레지스트리 패턴 | |
Strategy Pattern | 알고리즘을 캡슐화하여 런타임에 교체 가능하게 만드는 행동 패턴 | |
Abstract Factory Pattern | 관련된 객체군을 일관성 있게 생성할 수 있도록 하는 생성 패턴 | |
Proxy Pattern | 객체 접근을 제어하는 대리자 제공 패턴 | |
구현 기법 | Dependency Injection (DI) | 객체 간 의존성을 외부에서 주입받아 결합도를 낮추는 구현 기법 |
DI 컨테이너 | Spring IoC, Unity, Autofac 등의 의존성 주입 프레임워크 | |
테스팅 | 테스트 더블 (Test Double) | Mock, Stub, Spy, Fake 등을 이용한 테스트 전략 |
계약 테스트 (Contract Testing) | 소비자 - 제공자 간 API 계약을 자동 검증하는 테스트 기법 | |
통합 테스트 전략 | 실제 구현체와 외부 시스템을 함께 테스트하는 방법론 | |
성능 최적화 | 객체 풀링 (Object Pooling) | 객체 생성을 최소화하고 재사용하여 성능을 향상 |
지연 로딩 (Lazy Loading) | 실제 필요 시점까지 객체 생성을 지연시켜 리소스 절약 |
관련 분야별 추가 학습 내용
관련 분야 | 카테고리 | 주제 | 설명 |
---|---|---|---|
소프트웨어 아키텍처 | 마이크로서비스 | 서비스 간 인터페이스 설계 | API Gateway, 서비스 메시 등을 통한 추상화 |
이벤트 기반 아키텍처 | 이벤트 인터페이스 설계 | 이벤트 스키마와 계약 정의 | |
도메인 주도 설계 (DDD) | 바운디드 컨텍스트 | 도메인별 인터페이스 경계 정의 | |
데이터베이스 | Repository 패턴 | 데이터 액세스 추상화 | ORM 과 데이터베이스 독립성 |
Unit of Work 패턴 | 트랜잭션 추상화 | 비즈니스 트랜잭션과 기술적 트랜잭션 분리 | |
웹 개발 | MVC 패턴 | 계층 간 인터페이스 | 컨트롤러, 서비스, 레포지토리 계층 분리 |
RESTful API 설계 | HTTP 인터페이스 설계 | 리소스 중심의 인터페이스 설계 원칙 | |
클라우드 네이티브 | 서비스 메시 | 서비스 간 통신 추상화 | Istio, Linkerd 등을 통한 네트워크 추상화 |
컨테이너 오케스트레이션 | 인프라 추상화 | Kubernetes API 를 통한 인프라 추상화 |
주제와 관련하여 추가로 알아야 하거나 학습해야 할 내용들
관련 분야 | 카테고리 | 주제 | 설명 |
---|---|---|---|
소프트웨어 아키텍처 | 마이크로서비스 | 서비스 간 인터페이스 설계 | API Gateway, 서비스 메시, 컨트랙트 기반 통신 등 |
이벤트 기반 아키텍처 | 이벤트 인터페이스 설계 | 이벤트 스키마 정의와 발행/구독 구조 설계 | |
도메인 주도 설계 (DDD) | 바운디드 컨텍스트 | 도메인별 인터페이스 및 책임 경계 정의 | |
Onion Architecture | 계층 간 의존성 방향의 제한 | 핵심 도메인을 중심으로 한 계층 분리 | |
Clean Architecture | 핵심 로직과 인프라 분리 | 의존성 방향을 내부 도메인으로 향하게 함 | |
Microkernel Architecture | 플러그인 기반 설계 | 유연한 기능 확장을 위한 핵심 - 확장 구조 | |
데이터베이스 | Repository 패턴 | 데이터 액세스 추상화 | DB 기술과 비즈니스 로직의 분리 |
Unit of Work 패턴 | 트랜잭션 추상화 | 여러 DB 연산의 원자성 보장을 위한 단위 처리 | |
웹 개발 | MVC 패턴 | 계층 간 인터페이스 분리 | Controller-Service-Repository 계층 구조 |
RESTful API 설계 | HTTP 기반 인터페이스 설계 | 리소스 지향 설계, URI, 상태 코드, 메서드 표준화 | |
클라우드 네이티브 | 서비스 메시 | 서비스 간 통신 추상화 | Istio, Linkerd 등으로 관찰 가능성 및 보안 제어 |
컨테이너 오케스트레이션 | 인프라 추상화 | Kubernetes API 및 배포 전략 중심 설계 | |
프레임워크/도구 | Spring DI | 의존성 주입 프레임워크 | IoC 컨테이너를 통한 구성 및 주입 자동화 |
IoC Container | 제어의 역전 구현 도구 | 런타임 객체 생성 및 의존성 주입 관리 | |
객체지향 설계 | Interface-based Design | 인터페이스 중심 설계 | 객체 간 결합도 최소화 및 테스트 용이성 확보 |
SOLID 원칙 | 객체지향 설계 원칙 | 단일 책임, 개방 - 폐쇄, 리스코프 치환, 인터페이스 분리, 의존성 역전 | |
테스트/품질보증 | 테스트 더블 | Mock, Stub, Spy, Fake 등 | 단위 테스트 및 계약 테스트 용 객체 |
계약 테스트 | Interface 기반 상호작용 검증 | 생산자 - 소비자 간 API 계약 자동화 테스트 | |
유지보수/리팩토링 | 코드 리팩토링 | 추상화 적용 및 코드 구조 개선 | SRP 및 DIP 원칙 기반 구조화 |
성능 최적화 | 추상화 계층 최적화 | 불필요한 계층 제거 | 호출 오버헤드 분석 및 성능 병목 제거 |
용어 정리
용어 | 설명 |
---|---|
추상화 (Abstraction) | 복잡한 구현 세부사항을 감추고, 필요한 기능만 외부에 노출하는 설계 원칙 |
구체화 (Concretion) | 실제 동작을 구현한 클래스나 객체 |
인터페이스 (Interface) | 구현과 무관하게 동작의 명세만 정의하는 추상 계층 |
추상 클래스 (Abstract Class) | 일부 구현을 포함할 수 있는 추상화 계층, 상속 기반 구조에 주로 사용 |
계약 (Contract) | 인터페이스가 제공해야 하는 메서드와 그에 대한 동작 규약 |
DIP (Dependency Inversion Principle) | 고수준과 저수준 모듈 모두 추상화에 의존해야 한다는 설계 원칙 |
DI (Dependency Injection) | 객체의 의존성을 외부에서 주입하여 결합도를 낮추는 설계 기법 |
IoC (Inversion of Control) | 객체 생성과 제어 책임을 외부 (컨테이너 등) 로 위임하는 설계 원칙 |
팩토리 패턴 (Factory Pattern) | 객체 생성 책임을 팩토리 클래스로 위임해, 클라이언트가 구체 클래스에 의존하지 않도록 하는 패턴 |
서비스 로케이터 패턴 (Service Locator Pattern) | 클라이언트가 서비스의 구체 구현을 알지 않고도 접근할 수 있도록 레지스트리 역할 수행 |
Low Coupling (낮은 결합도) | 구성 요소 간 상호 의존성이 낮은 상태로, 변경 영향 최소화에 유리 |
High Cohesion (높은 응집도) | 구성 요소 내 책임이 밀접하게 연관된 상태로, 책임 명확화 및 유지보수 용이 |
결합도 (Coupling) | 모듈 간의 연결 정도를 나타내며, 낮을수록 유연한 구조 |
응집도 (Cohesion) | 모듈 내부 구성 요소들이 얼마나 관련성 있게 응집되어 있는지를 나타내는 지표 |
다형성 (Polymorphism) | 동일한 인터페이스를 통해 여러 다른 구현을 사용할 수 있는 객체지향 특성 |
런타임 바인딩 (Runtime Binding) | 실행 시점에 어떤 구체 구현이 호출될지 결정되는 메커니즘 |
제어 역전 (Inversion of Control) | 애플리케이션이 아닌 프레임워크가 객체의 흐름을 제어하는 설계 패턴 |
서비스 로케이터 (Service Locator) | 런타임에 서비스 인스턴스를 찾아 제공하는 레지스트리 |
테스트 더블 (Test Double) | 테스트 용으로 실제 객체를 대신하는 객체 (Mock, Stub, Spy, Fake 등) |
참고 및 출처
원칙 및 개념
- Program Against Abstractions - Roadmap.sh
- Abstraction principle (Wikipedia)
- Dependency inversion principle - Wikipedia
- Interface segregation principle - Wikipedia
- SOLID: The First 5 Principles of Object Oriented Design - DigitalOcean
- Chapter 5: Design Principles – Software Engineering: A Modern Approach
- What Are Abstractions in Software Engineering with Examples - The Valuable Dev
- 6 Software Design Principles Used by Successful Engineers - Swimm
DIP 및 인터페이스 설계
- Dependency Inversion Principle - Embedded Artistry
- Understanding Interfaces and Dependency Inversion - GoForGoldman
- Interface Segregation Principle in Java - Baeldung
- Dependency Inversion Principle Explained - Stackify
- Interface Segregation with Code Examples - Stackify
아키텍처 및 프레임워크
- Martin Fowler - Inversion of Control Containers
- Spring Framework - Dependency Injection (공식 문서)
- Clean Architecture 설명서 - Uncle Bob
- 10 Software Architecture Patterns You Must Know About - Simform
- 14 Software Architecture Design Patterns to Know - Red Hat