Spring - 한개가 아닌 여러세트(multi-row)의 파라미터를 모델에 맵핑하기
서론
- HTML에서 넘어오는 여러줄의 값들을 모델에 맵핑하기 위해서 방법을 검색하고 프로젝트에 직접 적용하며 알게된 방법을 소개한다.
본론
- HTML로 부터 넘어오는 파아미터값을 모델에 맵핑하기 위해서는 보통 메서드를 정의할 때 인자의 참조형 앞에 @ModelAttribute 어노테이션을 선언한다.
- 그렇다면, 하나의 객체에 맵핑되는게 아닌 배열의 형태 혹은 리스트의 형태로 여러줄을 맵핑시킬때는 어떻게 해야할까?
아래와 같이 같은 형태로 연속된 값이 있다고 하자.
<form>
<table>
<tr>
<td><input type="text" name="targetId" id="targetId"/></td>
<td><input type="text" name="targetName" id="targetName"/></td>
</tr>
<tr>
...
</tr>
...
</table>
</form>
아마도 이 값들을 전송하기 위해서 개발자는 form.submit 을 하거나 ajax를 이용해서 data에 form.serialiaze() 를 담아서 보낼것이다.
이 한 아이템(Row)를 모델에 맵핑하기 위해서는 먼저 넘어오는 데이터의 이름과 같은 멤버변수를 지닌 객체를 모델링 해줘야한다.
public class Target {
private String targetId;
private String targetName;
public String getTargetId() {
return targetId;
}
...
}
그리고 컨트롤러 클래스에서 다음과 같은 메서드를 작성할 것이다.
@RequestMapping(value = "/test/modelMap/")
public String getMultiRowToModel(@ModelAttribute Target target) {
...
}
여기까지는 한 개의 아이템이 넘어올때 모델객체에 맵핑시키는 방법이다. 하지만 여러 로우가 넘어올때는 하나의 모델 객체를 더 만든다.
public class MultiRowTarget {
private List<Target> targets;
public List<Target> getTargets() {
return targets;
}
public void setTargets(List<Target> targets) {
this.targets = targets;
}
}
그리고 HTML에서 넘겨줄때 해당 ROW들의 아이템 이름들을 변경해줘야 한다. 정적인 데이터인 경우엔 별 문제가 안되고, 삽입/삭제 버튼을 통한 가변 데이터를 다룰때 이슈가 있다. ( 사실 정적인 데이터엔 이 테크닉을 쓸 필요도 없다. )
<tr>
<td><input type="text" name="targets[0].targetId" id="targets[0].targetId"/></td>
<td><input type="text" name="targets[0].targetName" id="targets[0].targetName"/></td>
</tr>
<tr>
<td><input type="text" name="targets[1].targetId" id="targets[1].targetId"/></td>
<td><input type="text" name="targets[1].targetName" id="targets[1].targetName"/></td>
</tr>
물론, HTML 페이지에서 삽입/삭제 될 때마다 테이블의 로우를 계산해서 연결리스트 처럼 index를 먹인다는건 쉬운일이 아니다.
나는 form.submit 을 하거나 Ajax를 통해서 전송할 시 그때 다음과 같은 함수를 작성해서 해결했다.
function renameForModelAttribute() {
$("#form").each( function (index) {
$(this).find("input[name=targetId]").attr("name", "targets[" + index + "].targetId");
$(this).find("input[name=targetName]").attr("name", "targets[" + index + "].targetName");
}
}
이 함수를 이용하면 서버로 데이터로 전송할때 이름이 바뀌어서 전달하게 된다. 이 바뀐 양식의 모델을 받기 위해서는 아까 작성한 컨트롤러 메서드로 변경해야 한다. 생각나는대로 쓰고 있어서 두서가 없는점 양해 바란다.
@RequestMapping(value = "/test/modelMap/")
public String getMultiRowToModel(@ModelAttribute MultiRowTarget targets) {
...
}
이러면 이 메서드는 넘어온 값을 targets[0].targetName 을 MultiRowTarget.targets[0].targetName에 집어넣게 된다. 따라서 넘어온 모든줄의 데이터는 MultiRowTarget 객체의 targets 안에 들어가 있게된다.
자 모두 끝났다. 생각보다 심플하지 않지만 난 아직 이것보다 심플한 방법을 찾지 못했다. ( 물론, 이것을 작성할 때 꽤 고생했고 잘돌아가는것을 확인했기에 더이상 건드리지 않았다. )
일반적인 ModelAttribute를 이용하는 형태에서 추가적으로 해야할 사항을 다시 되짚어보면
- 해당 모델의 리스트형태를 가진 전용 모델 객체를 만든다.
public class MultiRowTarget
- 넘어올 데이터의 이름을 리스트의 이름과 인덱스로 맞춰춘다.
<input name="targets[0].targetName" />
- 저 값들을 넘겨준다. 어떻게든 Anyway.