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}]}