본문으로 바로가기

728x90

메뉴관리를 구현하면서 forEach를 통해 하위 메뉴를 찾는 로직을 수행하려고 하였는데 원하는 결과값이 나오지 않는 문제가 발생하였습니다. 아래의 글은 이 문제를 해결하는 방법에 대한 정리 합니다.

 

아래와 같은 메뉴가 있을 때 [시스템] 메뉴를 수정하는 경우 상위 메뉴 선택 시 [시스템]의 하위 메뉴는 제외하는 로직을 추가하려고 메뉴 항목을 forEach 하여 [시스템] 메뉴의 하위 항목을 검색하여 상위 메뉴 선택에서 제외하는 로직을 구현하려고 하였습니다. 원하는 결과는 [시스템] 메뉴의 4개 항목만 제외되어야 하는데 [환경설정]의 하위 메뉴까지 제외되는 문제가 발생하였습니다.

 

forEach 문제

 

인터넷을 조회한 결과 javascript 나 Node.js에서 배열에 대한 forEach를 사용하여 값을 처리하는 경우 break가 오작동된다고 합니다. 그리고 break의 처리를 위해서는 some을 사용하여 구현이 가능합니다.

 

그냥 for문을 사용하여 처리가 가능한데 보다 깔끔한 코드는 forEach, some이 더 표현이 좋습니다.

 

forEach 함수와 some함수

아래와 같은 컬렉션 배열이 있고 원하는 구현 로직은 값(value)이 40 미만의 값을 추출하고 40 이상일 경우에는 break문을 수행하고 싶습니다. (1차원으로 처리된 트리구조를 생성한 뒤 특정 트리의 하위 항목을 찾는 로직을 구현하려고 했는데 오작동이 발생하여 forEach에 대한 오작동을 발견하였습니다.)

      const collection = [
        {name: "Name #1", value: 10},
        {name: "Name #2", value: 20},
        {name: "Name #3", value: 30},
        {name: "Name #4", value: 40},
        {name: "Name #5", value: 50},
        {name: "Name #6", value: 40},
        {name: "Name #7", value: 30},
        {name: "Name #8", value: 20},
        {name: "Name #9", value: 10},
      ];

 

forEach 함수

forEach를 사용하여 아래와 같이 구현해 보았습니다. return false;로 함수를 벗어나는 로직을 구현하려고 하였는데(jQuery의 each는 return false;가 break 기능을 수행합니다.) 원하는 결과가 아니었습니다. 배열 요소에서 40 미만의 모든 값을 추출하였습니다.

      let ret1 = [];
      collection.forEach((item) => {
        if(item.value < 40) ret1.push(item);
        if(item.value >= 40) {
          return false;
        }
      });
      console.log("forEach Result : " + JSON.stringify(ret1));

      forEach Result : [
        {"name":"Name #1","value":10},
        {"name":"Name #2","value":20},
        {"name":"Name #3","value":30},
        {"name":"Name #7","value":30},
        {"name":"Name #8","value":20},
        {"name":"Name #9","value":10}
      ]

 

some 함수

컬렉션에서 break를 수행하기 위해서는 some함수를 사용하여 아래와 같이 처리가 가능합니다.

      let ret2 = [];
      collection.some((item) => {
        if(item.value < 40) ret2.push(item);
        if(item.value >= 40) {
          return true;
        }
      });
      console.log("some Result : " + JSON.stringify(ret2));

      some Result : [
        {"name":"Name #1","value":10},
        {"name":"Name #2","value":20},
        {"name":"Name #3","value":30}
      ]

 

메뉴의 하위 항목 찾는 예시

다음은 특정 메뉴를 검색한 후에 하위 메뉴를 검색하는 로직을 비교한 코드입니다. 원하는 로직은 2번 메뉴를 찾고 그 하위 메뉴를 반환하도록 구현하려 합니다. 아래의 코드처럼 2번 메뉴를 찾고 그 하위 메뉴를 검색하도록 구현한 로직인데 forEach의 경우에는 3번대의 메뉴까지 출력합니다. some의 return true를 사용하여 break를 수행합니다. return false는 continue 역할을 수행합니다.

      const menutable = [
        {key: "1", name: "Menu #1", depth: 0},
        {key: "1-1", name: "Menu #1-1", depth: 1},
        {key: "1-1-1", name: "Menu #1-1-1", depth: 2},
        {key: "1-2", name: "Menu #1-2", depth: 1},
        {key: "1-3", name: "Menu #1-3", depth: 1},
        {key: "2", name: "Menu #2", depth: 0},
        {key: "2-1", name: "Menu #2-1", depth: 1},
        {key: "2-1-1", name: "Menu #2-1-1", depth: 2},
        {key: "2-1-2", name: "Menu #2-1-2", depth: 2},
        {key: "2-2", name: "Menu #2-2", depth: 1},
        {key: "3", name: "Menu #3", depth: 0},
        {key: "3-1", name: "Menu #3-1", depth: 1},
        {key: "3-1-1", name: "Menu #3-1-1", depth: 2},
        {key: "3-1-2", name: "Menu #3-1-2", depth: 2},
        {key: "3-2", name: "Menu #3-2", depth: 1},
      ];
/*
      let submenu1 = [];
      let menu1 = null;
      menutable.forEach((item) => {
        if(item.key === "2") {
          menu1 = item;
          return true;
        }
        if(menu1) {
          if(menu1.depth < item.depth) {
            submenu1.push(item.name);
          } else {
            return false;
          }
        }
      });
      console.log("forEach : " + JSON.stringify(submenu1));
      // forEach : ["Menu #2-1","Menu #2-1-1","Menu #2-1-2","Menu #2-2","Menu #3-1","Menu #3-1-1","Menu #3-1-2","Menu #3-2"]
*/
      let submenu2 = [];
      let menu2 = null;
      menutable.some((item) => {
        if(item.key === "2") {
          menu2 = item;
          return false;			// continue
        }
        if(menu2) {
          if(menu2.depth < item.depth) {
            submenu2.push(item.name);
          } else {
            return true;		// break
          }
        }
      });
      console.log("some : " + JSON.stringify(submenu2));
      // some : ["Menu #2-1","Menu #2-1-1","Menu #2-1-2","Menu #2-2"]

 

참고 자료

 

forEach에 break문 대신 some 사용하기 :: Outsider's Dev Story

프로그래밍 언어에는 컬렉션의 요소를 순회하는 forEach문이 존재한다. 자바스크립트에서는 forEach이고 자바에서 for이고 jQuery에서는 each이다. 언어마다 모양은 약간이 달라도 사용방법은 다 동일하다. forEa...

blog.outsider.ne.kr

 

728x90