UML 설계를 자바 구현으로 연결하는 모델 변환 방법

UML 클래스·관계·시퀀스·상태 다이어그램을 자바 코드로 옮기고, 모델과 구현의 일치성을 관리하는 방법

2026-08-14 · 최초 발행 2025-05-23

UML 모델은 구현 구조를 결정하는 설계 청사진이다

UML(Unified Modeling Language)은 소프트웨어 시스템의 설계를 시각적으로 표현하는 표준 모델링 언어다. 객체지향 설계의 청사진을 자바 코드로 옮기면, 추상적인 모델은 실제로 동작하는 소프트웨어의 구조가 된다.

이 작업은 수동으로 진행할 수도 있고 자동화 도구를 통해 수행할 수도 있다. 어느 방식을 택하든 UML 다이어그램별 의미와 자바 문법의 대응 관계를 함께 이해해야 설계 의도가 구현 과정에서 흐려지지 않는다.

클래스의 구조를 자바 선언으로 옮기기

클래스 다이어그램은 자바 코드와 가장 직접적으로 이어진다. UML의 클래스는 자바 클래스가 되고, 속성은 필드, 메서드는 메서드 선언으로 변환된다. 가시성(visibility)은 public, private, protected 같은 접근 제어자로 표현한다.

// UML 클래스: Customer
// 속성: -id: int, -name: String
// 메서드: +getId(): int, +setName(name: String): void

public class Customer {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setName(String name) {
        this.name = name;
    }
}

관계의 의미는 참조 방식과 생명주기까지 포함한다

UML 관계를 자바 문법으로 바꾸는 일은 화살표를 키워드로 치환하는 것보다 넓다. 관계가 나타내는 책임과 객체 생명주기를 코드에서 어떻게 관리할지 결정해야 한다.

일반화(Generalization)는 extends로 구현한다.

Person-name String+getName() : StringEmployee-employeeId int+getEmployeeId() : int
public class Person {
    private String name;

    public String getName() {
        return name;
    }
}

public class Employee extends Person {
    private int employeeId;

    public int getEmployeeId() {
        return employeeId;
    }
}

실현(Realization)은 인터페이스와 implements 키워드의 관계로 이어진다.

«interface»Printable+print() : : voidDocument+print() : void
public interface Printable {
    void print();
}

public class Document implements Printable {
    @Override
    public void print() {
        // 구현 코드
    }
}

연관(Association)은 다른 객체를 가리키는 참조 변수로 구현한다.

11..*Student-name StringCourse-title String
public class Student {
    private String name;
    private List<Course> courses;  // 연관 관계
}

public class Course {
    private String title;
}

집합(Aggregation)과 합성(Composition)은 모두 전체-부분 관계를 참조로 표현하지만, 구성 요소의 생명주기 관리가 다르다.

DepartmentEmployeeCarEngine
// 집합(Aggregation) - 약한 결합
public class Department {
    private List<Employee> employees;  // 직원들은 부서 없이도 존재 가능

    public void addEmployee(Employee employee) {
        employees.add(employee);
    }
}

// 합성(Composition) - 강한 결합
public class Car {
    private Engine engine = new Engine();  // 엔진은 자동차의 일부로 생성, 자동차 없이 독립적으로 존재 불가
}

메시지 흐름은 메서드 호출 순서로 구체화된다

시퀀스 다이어그램은 객체 사이에서 오가는 메시지와 상호작용 순서를 보여준다. 자바 구현에서는 각 메시지를 메서드 호출로 옮기고, 그 호출의 제어 흐름을 코드에 반영한다.

PaymentInventoryOrderSystemClientPaymentInventoryOrderSystemClientplaceOrder(items)checkAvailability(items)availabilityStatusprocessPayment(amount)paymentStatusorderConfirmation
public class Client {
    private OrderSystem orderSystem;

    public void buyItems(List<Item> items) {
        OrderConfirmation confirmation = orderSystem.placeOrder(items);
        // 확인 처리
    }
}

public class OrderSystem {
    private Inventory inventory;
    private Payment payment;

    public OrderConfirmation placeOrder(List<Item> items) {
        boolean available = inventory.checkAvailability(items);
        if (available) {
            double amount = calculateTotal(items);
            boolean paymentSuccessful = payment.processPayment(amount);
            if (paymentSuccessful) {
                return new OrderConfirmation(/* ... */);
            }
        }
        return null; // 주문 실패
    }
}

상태 전이는 열거형이나 상태 패턴으로 관리한다

상태 다이어그램은 객체가 어떤 상태를 거쳐 변화하는지와 전이 조건을 다룬다. 자바에서는 상태 패턴(State Pattern) 또는 열거형(Enum)을 이용해 이를 구현할 수 있다.

process()ship()cancel()deliver()CreatedProcessingShippedCancelledDelivered
public enum OrderState {
    CREATED, PROCESSING, SHIPPED, DELIVERED, CANCELLED
}

public class Order {
    private OrderState state = OrderState.CREATED;

    public void process() {
        if (state == OrderState.CREATED) {
            // 처리 로직
            state = OrderState.PROCESSING;
        } else {
            throw new IllegalStateException("Cannot process order in " + state + " state");
        }
    }

    public void ship() {
        if (state == OrderState.PROCESSING) {
            // 배송 로직
            state = OrderState.SHIPPED;
        } else {
            throw new IllegalStateException("Cannot ship order in " + state + " state");
        }
    }

    // 다른 상태 전이 메서드들
}

코드 생성 도구는 설계와 구현 사이의 간극을 줄인다

CASE(Computer-Aided Software Engineering) 도구는 UML 모델에서 자바 코드를 자동 생성하는 기능을 제공한다. Enterprise Architect는 포괄적인 UML 모델링과 코드 생성을 지원하고, IBM Rational Software Architect는 엔터프라이즈급 모델링 및 코드 생성 도구다. StarUML은 경량화된 UML 모델링 도구로 코드 생성 기능을 제공하며, Eclipse Papyrus는 오픈소스 UML 모델링 도구다.

자동 생성은 개발 시간을 줄이고 일관된 코드 생성을 도울 수 있다. 설계와 구현의 간극을 줄이며 리팩토링과 유지보수에도 유리하다. 다만 생성 코드가 충분히 최적화되지 않을 수 있고, 맞춤형 로직 구현에는 제한이 있으며, 특정 도구에 대한 의존성이 생길 수 있다.

모델을 코드와 함께 관리하는 방식

설계 단계에서는 자바 언어의 특성을 고려해 UML을 작성해야 한다. 클래스, 인터페이스, 열거형 같은 자바의 구조적 요소를 구분하고, 접근 제어자와 정적·비정적 멤버도 모델에 명시한다. 유지보수성을 고려한 설계 패턴 선택 역시 이 단계의 판단이다.

변환할 때는 UML 요소명과 자바 식별자를 연결하는 네이밍 컨벤션을 정하고, 주석을 통해 모델과 코드의 연관성을 남긴다. 수동 변환은 클래스 구조, 관계, 메서드 구현 순서로 진행할 수 있다. 자동 생성된 코드도 품질을 검증하고 필요하면 리팩토링해야 한다.

코드가 바뀌면 UML 모델도 함께 동기화한다. 코드에서 UML로 역공학하는 양방향 엔지니어링 도구, 버전 관리 시스템을 통한 변경 이력 추적, 정기적인 설계-구현 일치성 검토가 이 간극을 관리하는 수단이 된다.

온라인 쇼핑몰 모델을 코드로 구현한 예

전자상거래 플랫폼의 주요 구성 요소를 모델링하면 사용자, 주문, 상품, 결제, 재고 사이의 상속과 연관 관계를 드러낼 수 있다.

1*11**11User-userId: String-password: String+authenticate() : : booleanCustomer-shippingAddress: Address-paymentInfo: PaymentInfo+placeOrder(items: List<Item>) : : OrderAdmin+manageProducts()+viewReports()Order-orderId: String-orderDate: Date-status: OrderStatus+calculateTotal() : : double+processOrder() : : booleanProduct-productId: String-name: String-price: double-description: String+isAvailable() : : booleanPayment-amount: double-paymentMethod: PaymentMethod-status: PaymentStatus+processPayment() : : booleanInventory-quantity: int+updateStock(change: int) : : void+isInStock() : : boolean

이 모델은 상속 구조, 주문과 상품의 관계, 결제와 재고에 대한 참조를 자바 클래스로 구현할 수 있게 한다.

// User 클래스
public abstract class User {
    private String userId;
    private String password;

    public boolean authenticate() {
        // 인증 로직 구현
        return true;
    }
}

// Customer 클래스
public class Customer extends User {
    private Address shippingAddress;
    private PaymentInfo paymentInfo;

    public Order placeOrder(List<Item> items) {
        Order newOrder = new Order();
        // 주문 생성 로직
        return newOrder;
    }
}

// Order 클래스
public class Order {
    private String orderId;
    private Date orderDate;
    private OrderStatus status;
    private List<Product> products;
    private Payment payment;

    public double calculateTotal() {
        return products.stream()
                .mapToDouble(Product::getPrice)
                .sum();
    }

    public boolean processOrder() {
        // 주문 처리 비즈니스 로직
        boolean paymentSuccessful = payment.processPayment();
        if (paymentSuccessful) {
            updateProductInventory();
            status = OrderStatus.PROCESSING;
            return true;
        }
        return false;
    }

    private void updateProductInventory() {
        for (Product product : products) {
            product.getInventory().updateStock(-1);
        }
    }
}

// Product와 Inventory 클래스간 합성 관계 구현
public class Product {
    private String productId;
    private String name;
    private double price;
    private String description;
    private Inventory inventory = new Inventory();  // 합성 관계

    public boolean isAvailable() {
        return inventory.isInStock();
    }

    public Inventory getInventory() {
        return inventory;
    }

    // getter, setter 메서드들
}

// 상태 열거형 구현
public enum OrderStatus {
    CREATED, PROCESSING, SHIPPED, DELIVERED, CANCELLED
}

모델과 구현 사이에서 생기는 괴리 다루기

UML은 추상 모델이므로 실제 구현에서는 세부사항을 결정해야 한다. 다양한 다이어그램의 일관성을 유지하는 일, 비즈니스 로직과 기술적 세부사항의 균형, 레거시 시스템 통합 과정에서 발생하는 모델-코드 괴리도 함께 다뤄야 한다.

고수준 UML 모델에서 시작해 구현 세부사항으로 점진적으로 상세화하면 이 간극을 관리할 수 있다. 모델 중심 개발(MDD)은 모델을 개발 프로세스 중심에 두는 접근법이며, 테스트 주도 개발(TDD)과 연결하면 모델에서 테스트 케이스를 도출한 뒤 구현할 수 있다. CI 파이프라인에 모델-코드 일치성 검증을 포함하는 방식도 지속적인 관리에 활용된다.

UML에서 자바 코드로의 변환은 설계와 구현을 잇는 과정이다. 자동화 도구는 이를 효율화할 수 있지만, 최종 코드 품질은 설계자와 개발자의 전문성에 달려 있다. 모델 기반 개발은 복잡한 시스템에서 구조적 명확성과 개발 효율성을 제공하며, 모델과 코드의 일치성을 계속 유지할 때 그 효과가 이어진다.

UML자바객체지향 설계모델 기반 개발소프트웨어 아키텍처