UML로 표현하는 캡슐화·상속·다형성과 설계 관계

UML 클래스·시퀀스 다이어그램에서 캡슐화, 상속, 다형성을 가시성·일반화·실현 관계로 표현하는 설계 방법을 정리한다.

2026-08-14 · 최초 발행 2025-10-31

클래스 경계와 관계선에 설계 의도를 남기기

객체지향 설계에서 캡슐화, 상속, 다형성은 코드에만 머물지 않는다. UML 문서에서도 가시성, 일반화, 실현 관계를 정확히 써야 분석·설계 산출물과 구현의 연결이 흐려지지 않는다.

캡슐화는 데이터와 행위를 한 단위로 묶고 내부 구현을 숨기는 방식이다. 접근 제어자는 외부에 허용할 경계와 내부 상태를 구분하는 수단이 된다. 상속은 상위 타입의 속성과 행위를 하위 타입이 재사용하거나 확장하는 일반화·특수화 관계다. 다형성은 같은 인터페이스를 여러 구현체로 바꿔 쓸 수 있게 하며, 오버로딩에 의한 정적 다형성과 오버라이딩에 따른 실행 시 디스패치를 포함한다.

가시성은 인터페이스 경계를 드러낸다

클래스 다이어그램에서는 속성과 연산 앞의 기호로 가시성을 표현한다. +는 public, -는 private, #는 protected를 뜻한다. 파라미터와 반환 타입까지 명시하면 외부에 제공하는 인터페이스의 경계가 더 선명해진다.

공개 API는 필요한 범위로 제한하고 내부 속성은 private로 둔다. protected는 하위 클래스가 확장해야 하는 지점에만 제한적으로 사용하는 편이 낫다. {readOnly}, {abstract}, {redefines} 같은 제약은 모델의 의미를 보강하며, OCL 또는 간단한 제약식으로 무결성 규칙도 표현할 수 있다.

<<entity>>, <<service>>, <<valueObject>> 같은 스테레오타입은 클래스가 도메인에서 맡는 역할을 문서에 남긴다. 이런 표기는 코드 생성이나 검토 자동화와 연결하기도 쉽다.

일반화와 실현 관계를 혼동하지 않기

상속은 상위 클래스를 향하는 빈 삼각형의 일반화 화살표로 나타낸다. 다중 상속이 필요한 경우에는 주의점을 모델에 남기고, 공통 동작을 인터페이스로 분리하는 방식을 검토할 수 있다.

인터페이스는 <<interface>>로 표시하고, 구현 클래스와의 관계는 점선과 빈 삼각형으로 그린다. 오버라이딩 메서드는 시그니처가 일치해야 하며 필요한 제약도 함께 명시한다.

일반화와 실현은 같은 관계가 아니다. 클래스 상속은 <|--, 인터페이스 실현은 <|..로 구분한다. 이 구분은 리스코프 치환 원칙(LSP)을 검토할 지점과 호출자가 의존해야 할 계약을 드러내는 데 도움이 된다.

연관, 집합, 합성도 생명주기 결합 정도를 표현한다. o--는 집합, *--는 합성을 나타낸다. 캡슐화를 해치지 않도록 불필요한 양방향 연관은 피하고, 구현 결합은 use 관계의 점선 화살표와 인터페이스 기반 의존으로 낮춘다.

런타임에는 메시지 흐름으로 다형성을 확인한다

클래스 다이어그램만으로는 실제 수신자 객체에 따라 연산이 선택되는 흐름을 충분히 보여주기 어렵다. 시퀀스 다이어그램에서는 호출자와 수신자가 인터페이스를 기준으로 상호작용하는 모습을 표현해 런타임 다형성을 확인한다.

팩토리, 전략, DI는 구체 타입을 결정하는 시점을 늦추는 수단이다. 예외와 오류 경로는 alt 같은 대안 흐름으로 분리해 두면 복원력 설계의 근거가 된다.

계좌와 알림 인터페이스로 보는 표현 방식

다음 다이어그램은 가시성, 일반화, 인터페이스 실현, 구현체 교체를 한 모델에 담은 예다.

notifierAccount- balance: Money+ deposit(amount: Money) : : void+ withdraw(amount: Money) : : void# audit() : : voidSavingsAccountCheckingAccountMoney«interface»Notifier+ notify(msg: string) : : voidEmailNotifierSmsNotifier

balance는 private이고 audit은 protected이며, 외부에 노출되는 연산은 public으로 표시된다. SavingsAccountCheckingAccountAccount의 공통 행위를 재사용하거나 특수화한다. Account는 구체 알림 구현이 아니라 Notifier 인터페이스에 의존하므로 Email과 SMS 구현을 런타임에 교체할 수 있다.

설계 문서에서 쓰이는 장면

API를 설계하고 문서화할 때는 비즈니스 요구와 도메인 용어집을 바탕으로 공용 연산을 인터페이스로 추출한다. 구현 클래스와 가시성을 분리해 두면 클래스 다이어그램, 인터페이스 계약서, 변경 영향도 분석을 함께 관리할 수 있다.

레거시 리팩터링에서는 순환 의존, 과도한 상속, 낮은 응집을 가진 클래스를 대상으로 상속을 합성으로 바꾸거나 인터페이스를 도입한다. 불필요한 양방향 연관을 끊은 결과는 결합도 감소 다이어그램, 리팩터링 체크리스트, 회귀 테스트 케이스로 남길 수 있다.

알림 채널이나 결제 게이트웨이처럼 구현체 교체 요구가 있는 구조에서는 <<interface>>를 정의하고 구현 관계를 <|..로 모델링한다. DI 컨테이너와 결합하면 런타임 교체 가능 설계를 만들 수 있고, 시퀀스 다이어그램의 alt 경로로 분기 조건을 명세할 수 있다.

모델을 과도하게 키우지 않는 운영 원칙

공개 API를 최소화하고, 인터페이스를 먼저 설계하며, 상속보다 합성을 선호하는 방향이 기본이 된다. 제약과 스테레오타입을 통해 모델의 의미를 보강하는 것도 유효하다.

다만 모델이 지나치게 상세해지면 복잡도가 늘어난다. 인터페이스를 지나치게 잘게 나누면 관리 비용이 커지고, 상속 계층이 깊어질수록 변경 비용도 급증한다. 패키지 의존도로 변경 영향도를 표시하고, 시퀀스 다이어그램으로 다형성 경로를 검증하며, 모델과 코드를 동기화하는 자동화 도구를 연계하는 방식이 이를 다루는 방법이다.

이런 설계가 정착되면 클래스 의존 수 기준 변경 영향 범위는 2040% 감소하고, 코드 리뷰 시간은 1525% 단축되며, 클래스/천 라인 기준 결함 밀도는 10~20% 감소할 수 있다. 모델과 코드의 정합성, 팀의 공통 언어, 테스트 가능성과 회귀 안정성도 함께 높아진다.

UML객체지향캡슐화상속다형성