프로젝트 코드리뷰/CosMos

React 트리구조 (without library)

SooHw 2024. 3. 25. 15:12

그룹웨어 만드는 과정중에 조직도 페이지를 위해 트리구조 화면단이 필요했다

라이브러리를 사용한다면 쉽게 만들순 있겠지만 라이브러리 없이도 만들수 있을 거 같아 그냥 만들어봤다

 

일단 트리구조를 위해선 당연하게도 트리구조 data가 필요하다

    const initialTreeData = [
        {
            id: '1',
            label: '(주)코스모스오피스',
            depth: 0,
            type: 'root',
            expand: true,
            children: [
                {
                    id: '2',
                    label: '대표',
                    depth: 1,
                    type: 'department',
                    expand: false,
                    children: [
                        {
                            id: '3',
                            label: '김철수 대표',
                            depth: 2,
                            type: 'employee',
                            expand: false,
                            children: []
                        }
                    ]
                },
                {
                    id: '4',
                    label: 'A 유닛',
                    depth: 1,
                    type: 'department',
                    expand: false,
                    children: []
                },
                {
                    id: '5',
                    label: 'B 유닛',
                    depth: 1,
                    type: 'department',
                    expand: false,
                    children: [
                        {
                            id: '6',
                            label: 'B1 팀',
                            depth: 2,
                            type: 'department',
                            expand: false,
                            children: [
                                {
                                    id: '7',
                                    label: '홍길동 팀장',
                                    depth: 3,
                                    type: 'employee',
                                    expand: false,
                                    children: []
                                },
                                {
                                    id: '8',
                                    label: '이순신 팀원',
                                    depth: 3,
                                    type: 'employee',
                                    expand: false,
                                    children: []
                                },
                            ]
                        }
                    ]
                },
            ]
        }
    ];

 

label에는 화면에 보여지게 될 이름,

depth는 계층형으로 css를 만들어 주기 위한 속성이 될것

type은 타입별로 label 옆에 보여지게 될 이미지를 조절하기 위해

expand는 초기 랜더링 시 펼쳐져있을지, 접혀있을지 결정하는 속성

 

 

 

Organization.jsx

import React, { useState } from 'react';
import style from '../SCSS/components/Organization.module.scss';

const TreeItem = ({ label, id, depth, type, expand, children }) => {
    const [isExpanded, setIsExpanded] = useState(expand);

    const toggleExpand = () => {
        setIsExpanded(!isExpanded);
    };

    const getImagePath = () => {
        if (type === 'department') {
            return '/org/folder.svg';
        } else if (type === 'employee') {
            return '/org/human.svg';
        } else {
            return ''; // 다른 유형에 대한 이미지가 필요한 경우 여기에 추가
        }
    };

    // root일때만 label 값만 출력하도록 수정
    if (type === 'root') {
        return (
            <div style={{ marginLeft: `${depth * 20}px` }}>
                {label}
                {isExpanded && children && children.map(child => (
                    <TreeItem key={child.id} {...child} />
                ))}
            </div>
        );
    }

    // employee인 경우 클릭 이벤트 처리하지 않음
    const handleClick = type === 'employee' ? null : toggleExpand;

    return (
        <div className={style.tree_item_wrapper} style={{ marginLeft: `${depth * 20}px` }}>
               <div className={style.tree_item_box} onClick={handleClick}>
                {type !== 'employee' && (isExpanded ? <img src='/org/minus.svg' /> : <img src='/org/plus.svg' />)} <img src={getImagePath()} alt={type} /> {label}
            </div>
            {isExpanded && children && children.map(child => (
                <TreeItem key={child.id} {...child} />
            ))}
        </div>
    );
};

const Organization = ({ initialTreeData }) => {
    return (
        <div className={style.org_main}>
            {initialTreeData.map(data => (
                <TreeItem key={data.id} {...data} />
            ))}
        </div>
    );
};

export default Organization;

 

 

Organization이라는 컴포넌트를 구성하고

 

{departmentTree && <Organization initialTreeData={departmentTree} />}

 

미리 정의해둔 data 혹은 back단에서 받아온 데이터를 넘겨주는 식으로 구현

 

*db에는 계층형 데이터로 상위id 컬럼을 추가

 

 

결과물