개발

재귀 호출을 통한 데이터 구조 변환

사낙 2025. 3. 9. 20:59

 

문제 상황

사용자 지역 정보를 입력받는 Form을 개발하면서, 서버에서 전달하는 평면 데이터(flat data)를 Cascader 컴포넌트에서 요구하는 트리 구조로 변환해야 하는 문제가 있었습니다.


예를 들어, 서버에서는 아래와 같이 데이터가 전달됩니다.

{
  "data": [
    { "name": "서울시", "last": false, "id": 1, "parentId": null },
    { "name": "중구", "last": true, "id": 2, "parentId": 1 }
  ]
}

 

하지만 실제로는 다음과 같은 트리 형태가 필요했습니다.

{
  "data": [
    {
      "name": "서울시",
      "id": 1,
      "children": [
        {
          "name": "중구",
          "id": 2,
          "children": [ /* 추가 하위 지역이 있을 경우 */ ]
        }
      ]
    }
  ]
}

 

이러한 문제는 크게 세 가지로 요약됩니다.

  1. 프론트엔드와 백엔드 데이터의 타입 불일치
  2. 컴포넌트를 재사용하기 위해 부모 노드만 표기, Multiple 선택, 선택 경로(breadcrumb) 표시 등의 옵션 구현 필요
  3. 최종 Form 제출 시, 최상위 노드 선택 시 부모 노드를 제거하고 자식 노드들만 반환해야 함

이를 해결하기 위해 재귀적 데이터 변환자식 노드 추출을 담당하는 클래스를 작성했습니다. 재귀 호출을 활용함으로써 복잡한 계층 구조를 간결하게 변환할 수 있었으며, 관련 로직을 한 곳에 모아 유지보수성과 재사용성을 크게 향상시켰습니다.

 

예시 코드 (AreaHelper.ts)

class AreaHelper {
  // 평면 데이터를 재귀적으로 트리 형태로 변환
  static buildTree(flatData: any[], parentId: number | null = null, maxDepth?: number) {
    const recursiveBuild = (parent: number | null, depth: number): any[] => {
      return flatData
        .filter(item => item.parentId === parent)
        .map(item => {
          const children = (!maxDepth || depth < maxDepth)
            ? recursiveBuild(item.id, depth + 1)
            : [];
          return { ...item, children: children.length ? children : undefined };
        });
    };
    return recursiveBuild(parentId, 0);
  }
  
  // 부모 노드로부터 최종 자식(Leaf) 노드들의 id만 추출
  static getLeafIds(parentId: number, flatData: any[]): number[] {
    const children = flatData.filter(item => item.parentId === parentId);
    let leafIds: number[] = [];
    children.forEach(child => {
      if (child.isLeaf) {
        leafIds.push(child.id);
      } else {
        leafIds.push(...this.getLeafIds(child.id, flatData));
      }
    });
    return leafIds;
  }
}

설계 근거 및 추가 대안

  • 재귀 호출 활용:
    재귀 함수를 사용하면 데이터의 깊이에 상관없이 간단한 코드로 트리 구조를 생성할 수 있어 코드의 간결성과 유지보수성이 높아집니다.
  • 모듈화:
    AreaHelper 클래스로 기능을 모듈화함으로써, Custom Cascader 내부에서 API로 받은 데이터를 쉽게 트리 구조로 변환하고, onChange 이벤트 발생 시 선택 경로의 최종 노드 ID를 추출해 Form에 전달할 수 있습니다.
  • 추가 대안:
    1. 서버 사이드 변환: 서버에서 미리 트리 구조로 데이터를 가공하여 클라이언트로 전달하는 방법
    2. 라이브러리 사용: 이미 검증된 데이터 변환 라이브러리를 도입하여 자동으로 원하는 형태로 데이터를 변환하는 방법

이와 같이 구현하면, 사용자에게 계층 구조의 데이터를 효과적으로 제공하면서 동시에 다양한 선택 옵션 및 상태 관리 기능을 유연하게 처리할 수 있습니다.