Apache Wicket에서 개발 시 고려해야 할 가장 중요한 모델 3가지 개념 및 예제

Apache Wicket에서 개발 시 고려해야 할 가장 중요한 모델 3가지는 다음과 같습니다. Wicket에서 Model은 컴포넌트와 실제 데이터 객체 사이를 연결하고, 상태 저장 및 관리 방식을 결정하는 핵심 개념입니다.

 

1. Model (정적 모델)

 

  • 특징: 가장 기본적인 모델 구현체로, 모델 객체(T)를 직접 가지고 있습니다.
  • 사용 시점: 모델 객체가 작고, 세션에 저장되어도 메모리 부담이 적은 경우, 또는 컴포넌트의 값이 요청(Request) 사이에서 변경되지 않고 정적으로 유지되어도 되는 경우에 사용합니다. (예: 간단한 문자열, 상수 값)
  • 주의 사항: 모델이 세션에 직렬화되어 저장되므로, 모델 객체가 클 경우(예: 큰 리스트) 세션 메모리 사용량이 증가할 수 있습니다.

 

2. LoadableDetachableModel (LDM, 로드/분리 가능 모델)

 

  • 특징: Wicket의 세션 메모리 절약데이터베이스 접근 최적화를 위한 가장 중요한 모델입니다.
    • Load: 요청이 시작될 때(onAttach()) 필요하면 실제 모델 객체(데이터)를 로드합니다. (예: 데이터베이스에서 조회)
    • Detach: 요청이 끝날 때(onDetach()) 실제 모델 객체를 null로 만들거나 분리합니다. 이로 인해 세션에는 **모델 객체를 로드하기 위한 식별자(ID)**만 직렬화되어 저장됩니다.
  • 사용 시점: 데이터베이스에서 조회해야 하는 객체나 크기가 큰 객체(예: 엔티티 객체, 대량의 리스트)를 컴포넌트 모델로 사용할 때 반드시 사용해야 합니다.
  • 효과: 실제 데이터 객체가 HTTP 세션에 저장되지 않아 메모리 사용량을 크게 줄이고, 매 요청마다 최신 데이터를 가져올 수 있습니다.

 

3. CompoundPropertyModel (CPM, 복합 속성 모델)

 

  • 특징: 페이지나 패널 등의 컨테이너 컴포넌트에 설정하여, 해당 컨테이너의 하위 컴포넌트들이 모델 객체의 특정 속성(Property)에 쉽게 바인딩될 수 있도록 해주는 모델입니다.
    • 하위 컴포넌트는 자신의 ID를 속성 이름으로 사용하여 상위 컴포넌트에 설정된 모델 객체의 해당 속성과 자동으로 연결됩니다.
  • 사용 시점: Form과 같이 여러 입력 필드(TextField, CheckBox 등)가 하나의 Java Bean(DTO/VO) 객체에 바인딩되어야 할 때 매우 편리합니다. 폼 개발에 필수적으로 사용됩니다.
  • 작동 방식: Component("firstName").setModel(new PropertyModel(parentModel, "firstName"))와 같이 하위 컴포넌트마다 PropertyModel을 명시적으로 선언할 필요 없이, Component("firstName")만 선언하면 CompoundPropertyModel이 자동으로 속성을 찾아 바인딩합니다.

요약:

  • Model: 단순하고 작은 값에 사용 (정적, 직접 저장)
  • LoadableDetachableModel: DB 엔티티나 큰 객체에 사용 (메모리 최적화, 요청 시 로드/분리)
  • CompoundPropertyModel: 폼 개발 시 여러 컴포넌트의 속성 바인딩을 자동화

이 모델들을 적절히 활용하는 것이 Wicket 애플리케이션의 **성능(메모리)**과 개발 편의성을 결정하는 핵심입니다.

 

🚀 Wicket 모델 바인딩 예시

Apache Wicket에서 개발 시 고려해야 할 가장 중요한 모델 3가지 개념 및 예제

1. Model<T> (정적 모델) – 간단한 값 표시

 

가장 단순한 형태의 모델로, 컴포넌트 생성 시점에 값이 결정되고 변경되지 않는 경우에 주로 사용됩니다.

  • HTML (MyPage.html):

    HTML

    <span wicket:id="message">여기에 메시지가 표시됩니다</span>
    
  • Java (MyPage.java):

    Java

    public MyPage() {
        // Model은 "Hello, Wicket!" 문자열 객체를 직접 저장합니다.
        IModel<String> staticModel = new Model<>("Hello, Wicket!");
    
        // Label 컴포넌트에 모델을 바인딩
        add(new Label("message", staticModel)); 
    }
    
  • 특징: staticModel 객체 자체가 세션에 저장되며, 값이 고정되어 있습니다.

 

2. LoadableDetachableModel<T> (LDM) – DB 엔티티 바인딩

 

메모리 최적화를 위해 데이터베이스에서 가져오는 엔티티 객체에 반드시 사용해야 하는 모델입니다.

  • 시나리오: ID로 User 객체를 조회하여 표시
  • Java (UserPanel.java):

    Java

    // User 객체를 로드/분리할 LDM
    private class UserModel extends LoadableDetachableModel<User> {
        private final long userId; // 세션에 직렬화될 ID 값
    
        public UserModel(long userId) {
            this.userId = userId;
        }
    
        // 요청이 시작될 때 (onAttach) 호출되어 실제 User 객체를 로드
        @Override
        protected User load() {
            // DB 또는 서비스 레이어를 통해 실제 데이터를 조회
            return userService.loadUserById(userId); 
        }
    
        // onDetach 메소드는 명시적으로 오버라이드 할 필요 없음 (기본 구현이 객체를 분리함)
    }
    
    public UserPanel(long id) {
        super("panel");
        IModel<User> userLDM = new UserModel(id);
    
        // LDM을 Label에 바인딩
        add(new Label("userName", new PropertyModel<>(userLDM, "name")));
    }
    
  • 특징: HTTP 요청이 끝날 때 User 객체는 세션에서 분리되고, userId 값만 세션에 남습니다. 다음 요청 시 load()를 통해 다시 객체를 가져옵니다.

 

3. CompoundPropertyModel<T> (CPM) – 폼(Form) 바인딩

 

하나의 폼 안에 있는 여러 입력 필드를 하나의 POJO(Plain Old Java Object) 객체에 쉽게 바인딩할 때 사용합니다.

  • Java (UserEditForm.java):

    Java

    // 폼이 다룰 데이터 객체
    private User user;
    
    public UserEditForm(String id, User initialUser) {
        super(id);
        this.user = initialUser;
    
        // **폼 자체에 CompoundPropertyModel 설정**
        setDefaultModel(new CompoundPropertyModel<>(this.user));
    
        // 하위 컴포넌트들은 별도의 PropertyModel 지정 없이 자신의 ID만으로 
        // 폼 모델(user 객체)의 해당 속성에 바인딩됩니다.
        add(new TextField<>("name"));   // user.getName(), user.setName()에 바인딩
        add(new TextField<>("email"));  // user.getEmail(), user.setEmail()에 바인딩
    
        add(new Button("save") {
            @Override
            public void onSubmit() {
                // 저장 버튼 클릭 시 user 객체에 입력 값이 이미 반영되어 있습니다.
                userService.saveUser(user); 
            }
        });
    }
    
  • HTML (UserEditForm.html):

    HTML

    <form wicket:id="userForm">
        <input type="text" wicket:id="name" />
        <input type="text" wicket:id="email" />
        <button wicket:id="save">저장</button>
    </form>
    
  • 특징: 각 컴포넌트의 ID와 모델 객체의 속성 이름이 일치해야 하며, 코드 작성이 간결해져 폼 개발의 생산성이 높아집니다.