ExtJs의 Tree Panel구조를 이용하여 메뉴나 선택항목을 구성하는 경우 데이터를 Ajax 등을 통해 Data Model로 내려받아 Panel에 뿌려야 한다(그렇지 않으면 Tree를 갱신하기도 어려워지고 공통모듈을 사용하기 어려워 진다).
이 때 서버쪽에서 Tree구조의 json 데이터를 내려주기 위해서는 결국 서버쪽에서 json데이터를 만들기 전 먼저 tree 구조로 데이터를 재정렬해서 json데이터 형태로 전환해야 한다.
많은 검색 끝에 찾은 팁은 다음과 같다.
데이터 조회(php)
...
db조회
...
$data = array();
$i = 0;
while ( $row = dbnext($result) ) { // 쿼리 결과를 loop 돌면서 배열에 채우는 부분
$data[$i] = array();
$data[$i]['id'] = $row[0]; // 정렬을 위한 키값을 읽음. 이를 id라고 하는 field에 저장
$data[$i]['parent_id] = $row[4]; // 정렬을 위해 부모(parent) 키값을 읽음
$data[$i]['leaf'] = true; // 모든 노드를 잠정적으로 leaf로 저장
for ( $idx = 1; $idx <= 3; $idx++ ) { // 조회한 field값을 배열에 입력 받음
$field_name = "field" . $idx;
$data[$i][$field_name] = $row[$idx];
}
$i++;
}
...
db close
정렬하는 부분(php)
$itemsByReference = array();
foreach($data as $key => &$item) {
$itemsByReference[$item['id']] = &apm;$item;
}
foreach($data as $key => &$item)
if($item['parent_id'] && isset($itemsByRefence[$item['parent_id']])) {
$itemsByReference[$item['parent_id']['children'][] = &$item;
$itemsByReference[$item[parent_id']['expanded'] = true;
unset($itemsByReference[$item['parent_id']]['leaf']);
}
foreach($data as $key => &$item) {
if($item['parent_id'] && isset($itemsByReference[$item['parent_id']]))
unset($data[$key]);
}
if ($i > 0) {
print json_encode($data);
}
php에서 지원하는 json과 배열의 call by reference가 jsp에서는 지원되지 않는다.
그래서 json은 별도 class를 사용해서 해결했고(org.json.*), 데이터의 정렬은 stack 구조를 이용하여 구현했다.
데이터 조회(jsp)
JSONArray jData = new JSONArray();
HashMap subData;
...
데이터 조회
...
i = 0;
while ( row.next() ) {
subData = new HashMap();
subData.put("id", row.getString(2)); // 키값을 입력한다.
if ( i == 0 ) thisTermAmount = Double.valueOf(row.getString(13));
for ( idx = 1; idx <= 3; n++ ){
subData.put("field" + String.valueOf(idx), row.getString(idx));
}
subData.put("seq", row.getString(4)); // seq와 depth 필드가 필수다
subData.put("depth", row.getString(5));
subData.put("parent_id", row.getString(6));
subData.put("leaf", true);
jData.put(i, subData);
i++;
}
if ( i > 0 )
out.println(Utils.TreeJson(jData).toString());
배열을 그대로 이용하여서는 json으로 변환하기 힘들어 JSONArray와 HashMap()을 이용하여 데이터를 저장하여 이를 다시 재정렬하는 구조로 작성하였다.
정렬하는 부분(jsp)
import java.util.*;
import org.json.*;
public class Utils {
Utils() {}
public static JSONArray TreeJson(JSONArray jData)
{
int len, m;
Stack s1 = new Stack();
Stack s2 = new Stack();
JSONObject j1, j2;
try {
j1 = jData.getJSONObject(0);
s1.push(j1);
for ( m = 1; m < jData.length(); m++) {
j2 = jData.getJSONObject(m);
if ( Integer.parseInt(j1.get("depth").toString()) <= Integer.parseInt(j2.get("depth").toString()) ) {
s1.push(j2);
j1 = j2;
}
else if ( Integer.parseInt(j1.get("depth").toString()) > Integer.parseInt(j2.get("depth").toString()) ) {
while ( Integer.parseInt((s1.peek()).get("depth").toString()) > Integer.parseInt(j2.get("depth").toString()) ) {
JSONArray children = new JSONArray();
int lvl = Integer.parseInt((s1.peek()).get("depth").toString());
while ( Integer.parseInt((s1.peek()).get("depth").toString()) == lvl ) {
s2.push(s1.pop());
}
while(s2.size() > 0 ) {
children.put(s2.pop());
}
j1 = s1.pop();
j1.put("children", children);
j1.remove("leaf");
j1.put("leaf", false);
s1.push(j1);
}
s1.push(j2);
j1 = j2;
}
}
while ( s1.size() > 0 && Integer.parseInt((s1.peek()).get("depth").toString()) > 1 ) {// clear stack
JSONArray children = new JSONArray();
int lvl = Integer.parseInt((s1.peek()).get("depth").toString());
while ( Integer.parseInt((s1.peek()).get("depth").toString()) == lvl ) {
s2.push(s1.pop());
}
while(s2.size() > 0 ) {
children.put(s2.pop());
}
j1 = s1.pop();
j1.put("children", children);
j1.remove("leaf");
j1.put("leaf", false);
s1.push(j1);
}
}
catch( JSONException je) {
}
return new JSONArray(s1);
}
}
java expert가 아니라 코드가 비효율적으로 구성되었을 수 있으나 일단 php에 없는 json함수와 tree 정렬 함수를 구현했다.
실행 결과는 tree 구조의 데이터를 json 형태로 보내게 된다.
실행 결과 예
{id:001,field0:root,parent_id:NULL,leaf:false,child:[{id:002,field0:'노드1',parent_id:001,leaf:true},{id:003,field0:'노드100',parent_id:001,leaf:true}]}