게시판 그룹 리스트의 핵심은 데이터를 트리 구조로 표현하는 것이다.
즉, 리스트 기능을 표 형태가 아닌 트리 구조의 계층형으로 구현한다.
부서별 게시판들(공지사항, 토론방, QNA 등)로 구성하거나
하위 토론방을 주제별로 구분해서 사용할 수 있기 때문이다.
리스트를 트리로 구현하면서 신규, 읽기, 수정, 삭제를 하나의 페이지로 구현하고
신규, 읽기, 수정, 삭제가 발생할때 마다 트리를 매번 다시 생성하는 것은
비효율적이고 복잡해서
신규, 읽기, 수정, 삭제를 Ajax로 구현한다.
따라서 멀티 게시판 관리자 기능의 핵심은 트리 사용과 Ajax 활용이 된다.
개인적으로 사용해본 트리 라이브러리 중
dynatree가 무료(MIT) 중에서 가장 무난하다고 생각해서 사용한다.
자세한 사용법은 찾아보길 바라고 여기서는 관련된 내용만 다룬다.
http://wwwendt.de/tech/dynatree/doc/sample-select.html
http://jwgye.tistory.com/13
이외에 jstree나 bootstrap-treeview도 좋다고 생각한다.
https://www.jstree.com/demo/
https://github.com/jonmiles/bootstrap-treeview
다음 그림과 같이 트리 구조로 게시판 그룹이 있다고 하면
![](data:image/png;base64,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)
dynatree는 다음과 같이 JSON을 이용하여 트리 구조를 표현해서 넘겨만 주면 된다.
{“key”: “1”, “title”: “공지사항”}, {"key": "2", "title": "동아리", "isFolder": true, "children": [ {"key": "3", "title": "기타"}, {"key": "4", "title": "축구"} ] }, {"key": "5", "title": "토론방"} |
Key는 그룹번호(bgno), title는 그룹 명(bgname)을 의미하고
2번 동아리는 자식(children)으로 기타(3)와 축구(4)를 가지는 트리 구조이다.
1과 5는 출력만 하면 된다.
이상과 같이 JSON 데이터를 생성하는 것이 트리 표현의 핵심인데
Java의 ObjectMapper를 이용하면 쉽게 처리할 수 있다.
ObjectMapper는 리스트(List)등의 구조체를 문자열로 변환하는 클래스인데
리스트의 항목이 클래스(VO)로 되어 있기 때문에
변환을 하면 JSON형태가 되게 된다.
클래스의 변수(메소드) 명이 JSON의 키로 생성되는 것이다.
변수의 값은 JSON의 키 값으로 생성된다.
따라서, 다음과 같이 TreeVO를 생성하면 쉽게 해결 된다.
dynatree에서 사용하는 key, title, isFolder, children로 클래스 멤버를 구성하고
데이터베이스에서 데이터를 가지고 올 때에도 이와 같이 가지고 오는 것이다.
public class TreeVO { private String key, title, parent; private boolean isFolder; private List children; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } ~~ 생략 ~~
|
TreeVO.java
List로 지정된 children이 트리 변환의 핵심인데
ObjectMapper에 의해
List는 하위 JSON 집합으로 변환된다.
데이터 베이스에서 danatree의 JSON 키 생성구조(TreeVO)로 데이터 리스트를 조회하고
이 리스트를 계층 구조로 정리하고(children에 넣어주고)
ObjectMapper를 이용해서 JSON으로 변환하는 것이
게시판 그룹 리스트의 핵심 사항이다.
이 개념을 가지고 하나씩 구현해 본다.
먼저, 메인 페이지이자 게시판 그룹 리스트를 구현한다.
하나의 게시판 그룹이 추가될 때 마다
게시판 그룹 리스트 페이지를 새로 실행하고
게시판 그룹 트리를 다시 생성하는 것은 비효율적이고 직관성이 떨어져
이상의 그림과 같이
왼쪽에 리스트(트리), 오른쪽에는 입력/수정 화면이 하나로 묶어서 구현한다.
왼쪽의 트리에서 게시판을 선택하면
오른쪽에 해당 정보가 나오고 수정/삭제 할 수 있는 방식이다.
게시판에서는 각각의 화면이 다른 페이지로 있었지만
여기서는 모든 페이지가 하나에 구현된다.
따라서 Ajax가 많이 사용된다.
먼저, 게시판 그룹 리스트를 가지고 오는(selectBoardGroupList) 컨트롤을 작성한다.
@RequestMapping(value = "/boardGroupList") public String boardList(ModelMap modelMap){ List<?> listview = boardSvc.selectBoardGroupList();
TreeMaker tm = new TreeMaker(); String treeStr = tm.makeTreeByHierarchy(listview); modelMap.addAttribute("treeStr", treeStr); return "board9/BoardGroupList"; } |
BoardGroupCtr.java
public List<?> selectBoardGroupList(){ return sqlSession.selectList("selectBoardGroupList"); } |
BoardGroupSvc.java
<select id="selectBoardGroupList" resultType="gu.common.TreeVO" > SELECT BGNO 'KEY', BGNAME TITLE, BGPARENT PARENT FROM TBL_BOARDGROUP WHERE BGDELETEFLAG='N' ORDER BY BGNO </select> |
board9.xml
selectBoardGroupList를 이용해 가지고 온 데이터는
dynatree에 필요한 데이터 구조(KEY, TITLE)로 맞춘 TreeVO에 담겨 있다.
게시판 그룹의 부모(BGPARENT) 정보를 이용하여 계층 구조를 생성하게 된다.
Listview의 개수만큼(size) 하나씩(item=TreeVO) 반복하며
새로운 리스트(rootlist)에 하나씩 추가한다.
이때, 하나의 데이터(TreeVO)의 부모 정보(bgparent)가 NULL이 아니면
부모가 있다는 의미로
부모 데이터를 찾아서 (for ~~~ mtDO.getParent().equals(ptDO.getKey()))
children(List)에 내가 너의 자식이라고 추가(add)해 주는 방식으로 계층을 형성한다.
부모가 없는 경우는 최상위 노드(Root)를 의미하며
새로운 리스트(rootlist)에 추가하고 다음으로 넘어간다.
public class TreeMaker { public String makeTreeByHierarchy(List<?> listview){ List<TreeVO> rootlist= new ArrayList<TreeVO>(); for (Integer i=0; i<listview.size(); i++) { TreeVO mtDO = (TreeVO)listview.get(i); if (mtDO.getParent()==null) { rootlist.add(mtDO); continue; } for (Integer j=0; j<listview.size(); j++) { TreeVO ptDO = (TreeVO) listview.get(j); if (mtDO.getParent().equals(ptDO.getKey())) { if (ptDO.getChildren()==null) ptDO.setChildren(new ArrayList<Object>() ); List<TreeVO> a = ptDO.getChildren(); a.add(mtDO); ptDO.setIsFolder(true); break; } } }
ObjectMapper mapper = new ObjectMapper(); String str=""; try { str = mapper.writeValueAsString(rootlist); } catch (IOException e) { e.printStackTrace(); } return str; } } |
TreeMaker.java
계층 구조로 데이터가 정리되면
ObjectMapper로 JSON형태의 문자열로 변환하면 된다.
일반 리스트 데이터를 위와 같은 방식으로 변환하는 것은
자주 사용하는 것이라 common폴더에 생성하였다.
설명이 어려워서 이해되지 않는 경우
boardGroupList 컨트롤에서 사용하는 것처럼
makeTreeByHierarchy를 호출하는 것만 기억해도 된다.
이렇게 생성한 정보는 modelMap을 이용하여 JSP로 보낸다.
JSP 파일에서는 넘겨 받은 treeStr의 값을
다음과 같이 dynatree의 문법에 맞춰 넣어주면 트리가 생성된다.
#tree는 트리를 만들 대상 DIV의 ID이다.
$(function(){ $("#tree").dynatree({ children: <c:out value="${treeStr}" escapeXml="false"/>, onActivate: TreenodeActivate }); fn_groupNew(); }); ~~ 생략 ~~ <div id="tree"></div> |
BoardGroupList.jsp
Dynatree를 사용하기 위해서는
다음과 같이 CSS와 JS 파일을 포함해야 한다.
<link rel="stylesheet" href="js/dynatree/ui.dynatree.css" id="skinSheet"/> <script src="js/jquery-2.2.3.min.js"></script> <script src="js/jquery-ui.js"></script> <script src="js/dynatree/jquery.dynatree.js"></script> |
BoardGroupList.jsp
Dynatree는 JQuery와 JQueryUI를 기반으로 제작되었기 때문에
위와 같이 JQuery도 지정해야 한다.
웹 페이지를 실행해 보면 이상의 그림과 같이 트리가 생성된 것을 볼 수 있다.
(데이터가 없어서 못 볼 수도 있다.)
onActivate는 트리의 노드가 선택되면 발생하는 이벤트로
TreenodeActivate를 아직 구현하지 않아서 노드를 클릭하면 오류가 발생한다.