Apache Wicket에서 개발 시 고려해야 할 가장 중요한 모델 3가지는 다음과 같습니다. Wicket에서 Model은 컴포넌트와 실제 데이터 객체 사이를 연결하고, 상태 저장 및 관리 방식을 결정하는 핵심 개념입니다.
1. Model (정적 모델)
- 특징: 가장 기본적인 모델 구현체로, 모델 객체(
T)를 직접 가지고 있습니다. - 사용 시점: 모델 객체가 작고, 세션에 저장되어도 메모리 부담이 적은 경우, 또는 컴포넌트의 값이 요청(Request) 사이에서 변경되지 않고 정적으로 유지되어도 되는 경우에 사용합니다. (예: 간단한 문자열, 상수 값)
- 주의 사항: 모델이 세션에 직렬화되어 저장되므로, 모델 객체가 클 경우(예: 큰 리스트) 세션 메모리 사용량이 증가할 수 있습니다.
2. LoadableDetachableModel (LDM, 로드/분리 가능 모델)
- 특징: Wicket의 세션 메모리 절약 및 데이터베이스 접근 최적화를 위한 가장 중요한 모델입니다.
- Load: 요청이 시작될 때(
onAttach()) 필요하면 실제 모델 객체(데이터)를 로드합니다. (예: 데이터베이스에서 조회) - Detach: 요청이 끝날 때(
onDetach()) 실제 모델 객체를null로 만들거나 분리합니다. 이로 인해 세션에는 **모델 객체를 로드하기 위한 식별자(ID)**만 직렬화되어 저장됩니다.
- Load: 요청이 시작될 때(
- 사용 시점: 데이터베이스에서 조회해야 하는 객체나 크기가 큰 객체(예: 엔티티 객체, 대량의 리스트)를 컴포넌트 모델로 사용할 때 반드시 사용해야 합니다.
- 효과: 실제 데이터 객체가 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 모델 바인딩 예시

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와 모델 객체의 속성 이름이 일치해야 하며, 코드 작성이 간결해져 폼 개발의 생산성이 높아집니다.