HTML 메뉴 렌더링 리팩토링하기
리팩토링할 결심
- 데이터베이스에서 메뉴데이터를 가져와 화면에 html로 보여주는 JSP 코드가 있었는데 매우 복잡하였다. 아래 사진은 코드의 일부분이다 ㅎㅎ
- Scriptlet과 HTML이 섞여있고, 메뉴뎁스가 깊어질수록 for문안에 for문 if문이 중첩적으로 계속 늘어나면서 총 200라인이 넘어갔다.
이러다보니 메뉴쪽 디자인을 변경할 때마다 매우 고통스러웠다.
보기만해도 다리가 후들거리고 숨이 탁탁 막힌다. 더 이상 고통받고 싶지 않아 리팩토링을 하기로 결심했다.
리팩토링
먼저 메뉴 정보를 담을 Menu 클래스를 생성한다.
1 | public class Menu { |
실무에서는 더 많은 클래스 필드가 있었지만 여기서는 구현방법만 보여주기 위해 최대한 간단히 구성했다.
다음으로 테스트 코드를 만든다.
1 | public class MenuTest { |
html() 메소드를 호출하면 위처럼 나오게하고 싶다. html() 메소드를 작성해보자.
1 | public String html() { |
위와같이 작성하고 다시 테스트를 돌리면 성공한다.
다음으로 하위메뉴가 들어갔을 때를 테스트해보자
1 |
|
하위 메뉴를 가지기 위해 Menu에 List<Menu> children 을 추가하고 addChild(..) 메소드도 추가한다.
또한 하위메뉴가 있으면 위처럼 ul 태그 안에 하위 리스트가 들어가게 html() 메서드도 수정해주자
1 | public class Menu { |
테스트를 돌리니 실패했다. 확인해보니 expected 텍스트 블록의 띄어쓰기 때문이다. 정규식을 이용해 아래처럼 한줄로 바꿔서 다시 테스트를하면 성공한다.
1 | String expected = """ |
하위 메뉴까지 성공했으니 이제 같은 depth의 이웃 메뉴 테스트를 해보자
1 |
|
홈메뉴와 같은 레벨의 게시판 메뉴를 만들어서 루트 메뉴에 추가했다.
위와 같은 expected 구조를 예상하여 테스트 했더니 실패했다.
루트메뉴는 링크 태그가 생성되면 안되는데 생성되어서 실패한다.
해결하기 위해서 아래와같이 html() 메소드를 수정했다.
1 | public String html() { |
루트메뉴가 아닐 때 (depth != 0) 만 <li> 태그를 생성하게 만들고 다시 테스트를 실행하면 성공한다.
메뉴 생성 로직에 중복이 보이니 이쯤에서 테스트코드를 리팩토링해준다.
메뉴생성 코드를 필드로 추출하면 다음과 같다.
1 | private final Menu menu1 = new Menu("홈", "/site/home", 1); |
변경 후 다시 테스트를 실행시켜보자. 성공한다면 일단 기본적인 HTML rendering은 끝났다.
다음으로 DB에서 가져온 메뉴 리스트로 Menu 를 생성할 수 있게 만들어야한다.
DB에서 계층쿼리를 사용해 가져오기 때문에 순서만 정렬된 인접리스트 형태일 것이다.
트리계층 구조로 바꾸려면 식별자와 부모식별자가 필요하다. Menu 클래스에 id와 parentId를 추가한다.
1 | public class Menu { |
인접리스트 메뉴를 생성자 인자로 받아서 트리 메뉴를 생성하는 테스트를 다음과 같이 만든다.
1 |
|
생성자를 구현해준다.
1 | class Menu { |
위 생성 알고리즘에서 중요한 점은 adjacentMenuList가 정렬이 되어있어야한다는 것이다.
부모메뉴가 먼저 있어야 제대로 생성된다.
트리메뉴는 생성시 메모리를 많이 쓰며, 메뉴는 자주 로딩되기때문에 캐시를 사용하는 것이 좋겠다.
끝~~ ^__^