React 재귀함수

£€€.T.$·2023년 12월 7일
post-thumbnail

tree방식으로 목차를 작성하던 중
prntKey를 기준으로 depth(혹은 lvl)를 체크하여 각각의 depth 맞는 넘버링을 해주면 어떨까 싶어서 해당 사항 적용 및 방법을 작성하였다!

대분류(Chapter // lvl : 1)는 로마 숫자
중분류(Section // lvl : 2)는 아라비아 숫자
소분류는(Sub Section // lvl : 3)는 한글

순으로 진행 하였다.

최종


📀 구조

설계 구조는 아래와 같다

Edit.js
┣ numbering-helger.js

Edit에서는 여러 기능이 있지만 tree구조를 변경하면서 원래 state정보를 계속해서 변경하는 기능을 사용할 예정이다.

📂 Edit.js

✔️ Edit.js
┣ numbering-helger.js

import update from 'immutability-helper';
const Edit = () => {
//리스트를 받을 state
const [mContent, setContent] = useState(init);

//rc-tree Api를 이용하여 tree값을 변경할 때 실행될 함수
//결과 값으로 sort된 tree값인 sortTree를 받는다.
 const onChangeTree = (sortTree) => {
   //mContent에서 직접 수정할 오브젝트
   const { MLC_TOCLIST } = mContent;
   //sortTree를 map으로 돌려서 각각의 요소를 비교하여 state값의 리스트에서 INDENT를 재정의 한다.
   const nTocList = sortTree.map((s) => {
     const nToc = MLC_TOCLIST[tabKey].find((f) => String(f.TOCID) === s.key);
     return Object.assign(nToc, { PRNT_TOCID: s.prntId, INDENT: s.lvl });
   });
    
   // 재정의한 값을 immutability-helper 사용하여 변경한다.
   setContent((prev) =>
     update(prev, {
       MLC_TOCLIST: {
         [tabKey]: { $set: [...nTocList] },
       },
     })
   );
  };
 //(생략)//

해당 사항을 통해 mContent가 변경 될 때 넘버링 값을 넣어주면 어떨까 해서 useEffect를 추가하였다. (적당한 위치에 넣어주자)

➕Edit.js

import { numberingData } from './panel/numbering-helper';

//(생략)//

  
  useEffect(() => {
    const list = numberingData({ mContent, tabKey });
    setIndexList(list);
  }, [mContent]);

이제 준비는 끝났다. util로 만들 numbering-helper.js를 만들고 설정해보자!


📂 numbering-helper.js

리스트의 INDENT값은 위의 Edit.js에서 sortTree 값의 lvl을 지정해주었다.
이제부터 INDENT값은 depth가 되시겠다.

INDENT 값을 체크 하여 리스트를 새로 만들었고 해당 list의 index순서에 따라서 Numbering을 하였다

예시
{ TOCID : 1 , INDENT : 1 , Numbering : 1 }
{ TOCID : 2 , INDENT : 1 , Numbering : 2 }
{ TOCID : 3 , INDENT : 1 , Numbering : 3 }
{ TOCID : 4 , INDENT : 1 , Numbering : 4 }

해당 코드는 아래와 같다.

Edit.js
┣ ✔️numbering-helper.js

export const numberingData = ({ mContent, tabKey }) => {
  const { MLC_TOCLIST } = mContent;

  const nTocList = MLC_TOCLIST[tabKey].map((s, index) => {
    //INDENT가 1일 때 ROMAN_NUM key값을 출력한다.
    let filterIndex = '';
    if (s.INDENT === 1) {
      //INDNET가 1인 값의 배열
      const indentOneArray = MLC_TOCLIST[tabKey].filter((f) => f.INDENT === 1);
      //각 배열 값의 index값(Numbering에서 가져올 key값)
      filterIndex = indentOneArray.findIndex((f) => f.TOCID === s.TOCID);
      return Object.assign(s, { Numbering: ROMAN_NUM[filterIndex + 1] });
      //INDENT가 2일 때 일반 index값을 출력한다.
    } else if (s.INDENT === 2) {
      //INDNET가 2인 값의 배열
      const indentOtherArray = MLC_TOCLIST[tabKey].filter((f) => f.PRNT_TOCID === s.PRNT_TOCID);
        //각 배열 값의 index값(Numbering에서 가져올 key값)
      filterIndex = indentOtherArray.findIndex((f) => f.TOCID === s.TOCID);
      return Object.assign(s, { Numbering: filterIndex + 1 });
      //INDENT가 3일 때 HANGLE_NUM key값을 출력한다.
    } else {
      //INDNET가 3인 값의 배열
      const indentOtherArray = MLC_TOCLIST[tabKey].filter((f) => f.PRNT_TOCID === s.PRNT_TOCID);
      //각 배열 값의 index값(Numbering에서 가져올 key값)
      filterIndex = indentOtherArray.findIndex((f) => f.TOCID === s.TOCID);
      return Object.assign(s, { Numbering: HANGLE_NUM[filterIndex + 1] });
    }
  });

잘 작동하였다. 그치만 여기서 문제가 발생
lvl값이 계속 늘어난다면 INDENT 값을 임시로 지정해준 1 2 그리고 else까지만 찾을 수 있어 매 케이스마다 계속 if문을 설정해주어야 한다... (이럴거면 코드 왜짬?)

그래서 재귀함수로 새롭게 작성하였다.


재귀함수

🔧 numbering-helper.js

findChildrenList라는 함수를 만들어 계속해서 자기를 호출하게 하였다.
findChildrenList함수는 (PrntKey와 Numbering숫자)를 입력 받아 PrntKey의 자식 List인 childernList를 만들어 각 배열을 새롭게 만든 indexList에 삽입하도록 만들었다.
탈출 조건으로는 새롭게 만들어진 childrenList의 길이인 length 가 없다면 더이상의 자식은 없는 것으로 판단하여 해당 함수를 탈출 할 수 있게 작성하였다.

Edit.js
┣ ✔️numbering-helper.js

export const numberingData = ({ mContent, tabKey }) => {
  const { MLC_TOCLIST } = mContent;
  
    let indexList = {};

    //자식 List를 찾을 함수 TOCID 받아서 indexList에 넣는다.
    const findChildrenList = (TOCID) => {
      //전체 리스트에서 TOCID와 PRNT_TOCID를 filter하여 자기 자식list를 찾는다
      const childrenList = MLC_TOCLIST[tabKey].filter((f) => f.PRNT_TOCID === TOCID);
      //찾은 리스트를 map으로 돌려 하나하나 Numbering을 해준다.
      childrenList.map((item, index) => {
        // indexList에 새로운 객체를 넣는다.
        indexList = Object.assign(indexList, { [item.TOCID]: { Numbering: index + 1, TITLE: item.TITLE } });
        //childrenList 각각의 배열의 TOCID값이 자식 List를 가졌는지 확인한다.
        //자식이 없다면 [] 값을 넘겨 재귀함수를 탈출한다.
        return MLC_TOCLIST[tabKey].filter((f) => f.PRNT_TOCID === item.TOCID).length > 0
          ? findChildrenList(item.TOCID)
          : [];
      });
    };
    findChildrenList('root');
    return indexList;
};

결과



잘 들어간 것을 확인 할 수 있다.

근데 왜 sorting이 안되는 것 같지... 해당 사항은 차후 수정 하는 걸로...


📑 ROMAN_NUM , HANGEUL_NUM

아스키 코드 조합으로 하려 했지만 변수가 많아 어쩔 수 없이 각각 key value값으로 선언하였다...

export const HANGLE_NUM = {
  1: '가',
  2: '나',
  3: '다',
  4: '라',
  5: '마',
  6: '바',
  7: '사',
  8: '아',
  9: '자',
  10: '차',
  11: '카',
  12: '타',
  13: '파',
  14: '하',
  15: '거',
  16: '너',
  17: '더',
  18: '러',
  19: '머',
  20: '버',
  21: '서',
  22: '어',
  23: '저',
  24: '처',
  25: '커',
  26: '터',
  27: '퍼',
  28: '허',
  29: '고',
  30: '노',
  31: '도',
  32: '로',
  33: '모',
  34: '보',
  35: '소',
  36: '오',
  37: '조',
  38: '초',
  39: '코',
  40: '토',
  41: '포',
  42: '호',
};

export const ROMAN_NUM = {
  1: 'Ⅰ',
  2: 'Ⅱ',
  3: 'Ⅲ',
  4: 'Ⅳ',
  5: 'Ⅴ',
  6: 'Ⅵ',
  7: 'Ⅶ',
  8: 'Ⅷ',
  9: 'Ⅸ',
  10: 'Ⅹ',
  11: 'ⅩⅠ',
  12: 'ⅩⅡ',
  13: 'ⅩⅢ',
  14: 'ⅩⅣ',
  15: 'ⅩⅤ',
  16: 'ⅩⅥ',
  17: 'ⅩⅦ',
  18: 'ⅩⅧ',
  19: 'ⅩⅨ',
  20: 'ⅩⅩ',
  21: 'ⅩⅩⅠ',
  22: 'ⅩⅩⅡ',
  23: 'ⅩⅩⅢ',
  24: 'ⅩⅩⅣ',
  25: 'ⅩⅩⅤ',
  26: 'ⅩⅩⅥ',
  27: 'ⅩⅩⅦ',
  28: 'ⅩⅩⅧ',
  29: 'ⅩⅩⅨ',
  30: 'ⅩⅩⅩ',
  31: 'ⅩⅩⅩⅠ',
  32: 'ⅩⅩⅩⅡ',
  33: 'ⅩⅩⅩⅢ',
  34: 'ⅩⅩⅩⅣ',
  35: 'ⅩⅩⅩⅤ',
  36: 'ⅩⅩⅩⅥ',
  37: 'ⅩⅩⅩⅦ',
  38: 'ⅩⅩⅩⅧ',
  39: 'ⅩⅩⅩⅨ',
  40: 'ⅩⅩⅩⅩ',
  41: 'ⅩⅩⅩⅩⅠ',
  42: 'ⅩⅩⅩⅩⅡ',
};
profile
Be {Nice} Be {Kind}

0개의 댓글