UML과 객체지향 설계를 연결하는 모델링 방법
UML과 객체지향 설계의 연결 방식을 정적·동적 모델, 코드 동기화, 테스트 추적성 관점에서 정리한다.
2026-08-14 · 최초 발행 2025-10-31
모델은 객체지향 설계의 의도를 보존한다
객체지향 프로그래밍은 객체와 메시지를 중심으로 프로그램을 구성한다. 도메인 개념은 클래스, 인터페이스, 객체 그리고 이들 사이의 관계로 구현된다. UML(Uniform Modeling Language)은 이 구조와 동작을 시각화하고 명세화하며 문서화하기 위한 표준 모델링 언어다.
UML은 클래스, 컴포넌트, 시퀀스, 상태, 활동 등을 포함한 14종 다이어그램 집합을 제공한다. 객체지향에만 한정된 언어는 아니지만, 클래스·인터페이스·상속·합성처럼 OOP의 핵심 요소와 대응하는 표기법을 갖추고 있어 설계와 구현을 연결하는 데 적합하다.
클래스의 속성과 연관은 코드의 클래스, 필드, 참조로 이어진다. 일반화는 상속을, 실체화는 인터페이스 구현을 나타낸다. 합성 및 집합 관계는 객체의 수명 주기와 결합 방식을 드러내며, 패키지와 컴포넌트는 모듈·네임스페이스·배포 단위에 대응한다. 가시성 표기는 캡슐화의 경계를 검토하는 기준이 된다.
구조와 흐름을 분리해 읽기
클래스·컴포넌트·배치 다이어그램은 시스템의 정적 구조를 다룬다. 책임이 어디에 놓이는지, 아키텍처 경계가 어떻게 나뉘는지, 배포 단위가 무엇인지 확인할 때 유용하다.
반대로 시퀀스·상태·활동 다이어그램은 시간에 따라 변하는 행위를 표현한다. API 호출 계약과 예외 흐름, 상태 변화, 동시성 포인트를 설계 단계에서 확인할 수 있다. 구조만으로 파악하기 어려운 경계 조건도 이 모델에서 드러난다.
이 두 관점은 요구사항에서 유스케이스, 분석 클래스, 설계 클래스, 코드로 이어지는 추적성을 구성한다. 시퀀스와 상태 모델은 테스트 시나리오의 출발점이 되며, 테스트 케이스가 설계 의도와 맞는지도 검증할 수 있다.
모델과 코드가 어긋나지 않게 관리하는 방식
모델을 코드와 분리된 산출물로 두면 시간이 지날수록 실제 구현과의 차이가 커진다. Forward, Reverse, Round-trip 엔지니어링은 이런 모델-코드 드리프트를 감지하고 정렬하는 수단이다.
분석 단계에서는 엔티티·값 객체·서비스를 식별하고 관계와 제약을 정한다. 설계 단계에서는 인터페이스와 추상화를 정련하고 Strategy, Factory 같은 패턴을 적용한다. 이후 스켈레톤을 생성하거나 수기로 구현하면서 표준 명명과 패키징을 따른다.
검토 시에는 다중성, 가시성, 순환 의존성, 스레드 안전성을 확인한다. 시퀀스와 상태 모델에서 테스트 시나리오를 도출하고, 모델 산출물·동기화된 코드·추적 매트릭스·테스트 리포트를 버전과 함께 관리한다.
모델-코드 드리프트가 발견되면 역공학으로 차이를 찾고 설계 리뷰와 라운드트립 동기화를 수행한다. 동시 편집 충돌은 모델 저장소의 낙관적 락과 3-way 머지, 사전에 정의한 충돌 규칙 우선순위로 다룬다. OCL 또는 다중성 제약 위반은 빌드 실패로 처리하고 스키마와 도메인 규칙을 다시 검토한다.
설계 모델을 코드로 옮긴 예
JDK 17, Maven/Gradle, 단일 모듈 프로젝트를 가정한다. 아래 코드는 서비스에서 리포지토리를 참조하고, 주문 엔티티가 상태 전이를 관리하는 구조를 표현한다.
// Java 17
import java.util.Optional;
import java.util.UUID;
record OrderId(UUID value) {}
enum OrderState { CREATED, APPROVED, CANCELLED }
final class Order {
private OrderState state = OrderState.CREATED;
public OrderState state() { return state; }
public void approve() {
if (state != OrderState.CREATED)
throw new IllegalStateException("승인 불가 상태: " + state);
state = OrderState.APPROVED;
}
public void cancel() {
if (state == OrderState.CANCELLED)
return;
if (state == OrderState.APPROVED)
throw new IllegalStateException("승인 후 취소 불가");
state = OrderState.CANCELLED;
}
}
interface OrderRepository {
Optional<Order> findById(OrderId id);
void save(Order order);
}
final class OrderService {
private final OrderRepository repo;
OrderService(OrderRepository repo) { this.repo = repo; }
public void approve(OrderId id) {
Order order = repo.findById(id).orElseThrow(() -> new IllegalArgumentException("주문 없음"));
order.approve();
repo.save(order);
}
}
OrderRepository 인터페이스와 구현 클래스를 분리하는 방식은 실체화 관계를 코드에 반영한다. OrderService가 OrderRepository에 의존하는 관계는 다중성 1..1로 볼 수 있다. 주문 상태는 CREATED에서 APPROVED 또는 CANCELLED로 전이하며, 가드 조건과 예외 흐름은 메서드 안에 구현된다.
문서 부담을 통제하면서 얻는 효과
도메인 모델과 API 계약을 정리할 때 클래스·패키지 다이어그램은 도메인 경계와 어그리게잇을 식별하는 데 쓰인다. 시퀀스 다이어그램은 API 호출 계약과 예외 흐름을 명세하는 수단이 된다.
레거시 시스템에서는 코드 역공학으로 클래스와 의존성 그래프를 추출해 순환 참조 제거 계획을 세울 수 있다. 상태 다이어그램으로 비즈니스 규칙을 재구성한 뒤 서비스 경량화 방향을 검토할 수도 있다. 마이크로서비스 환경에서는 컴포넌트·배치 다이어그램으로 서비스 경계와 배포 토폴로지를 표현하고, 시퀀스·활동 다이어그램으로 동기·비동기 호출 및 서킷 브레이커 조건을 정의한다.
결합도와 응집도가 개선되면 리팩토링 비용은 2035% 절감 예상이며, 영향 분석 시간은 30% 내외 단축될 수 있다. 신규 인력이 투입될 때 생산성은 1525% 향상될 수 있고, 요구사항·설계·테스트의 추적성은 해석 차이로 인한 오해를 미리 줄인다. 상태·시퀀스 기반으로 테스트를 파생하면 누락 시나리오는 20% 이상 감소할 수 있으며, 모델 검증 자동화는 빌드 단계에서 결함을 조기에 검출하는 기반이 된다.
수치는 조직·도메인·도구 성숙도에 따라 변동 가능하다.
리스크가 큰 부분에만 모델을 남긴다
모델링의 정밀도를 높일수록 문서화 비용은 커지고 민첩성은 떨어질 수 있다. 빅디자인업프론트(BDUF) 방식보다, 리스크가 높은 영역부터 점진적이고 진화적으로 설계하는 편이 낫다.
모든 다이어그램을 유지하려 하기보다 클래스·시퀀스·상태 다이어그램을 중심으로 최소 유용 세트를 선택한다. 모델 저장소는 형상관리와 CI에 연결하고, 라운드트립 정책을 문서화한다. 용어 사전과 스테레오타입을 표준화하면 팀이 같은 모델을 다르게 해석하는 문제도 줄일 수 있다.
UML은 정적 구조와 동적 행위를 함께 표현해, 객체지향 설계의 개념을 요구사항에서 코드와 테스트까지 이어 준다. 핵심은 산출물의 양이 아니라 설계상 중요한 경계와 변경 위험을 지속해서 검증할 수 있는 모델을 유지하는 데 있다.