트리 구조 사이드바 만들기 (1) Server-side


이번에는 현재 블로그 우측에 보이는 트리 구조의 카테고리 사이드바를 만들겁니다. 이 글에서는 서버사이드의 작업을 다룰 겁니다.

트리 구조 데이터 모델링

우선 트리 구조를 사용하기 위한 데이터를 저장하는 모델링 패턴을 결정해야 합니다. MongoDB 공식문서를 보면 트리 구조의 다양한 모델링 패턴이 나와있습니다. 저는 이 중에서 자식 참조를 갖고 있는 방식을 선택했습니다. 이제 해당 패턴을 사용하기 위해 아래와 같이 스키마를 정의해줍니다.

import { Document, Schema } from 'mongoose';

const CategorySchema: Schema = new Schema();

CategorySchema.add({
    name: {
        type: String,
        required: true,
        unique: true,
    },
    //  Tree structures with children references
    isTopLevel: Boolean,
    children: [String],
});

export default CategorySchema;

위 코드에서 isTopLevel필드는 이후 그래프를 만드는데 쉽도록 정의했습니다.

Child Reference모델에서의 CRUD

Create

Category를 생성할 때는 요청의 category를 만들어주고 parent의 children에 새 category를 push해주면 됩니다.

Read

Flat한 카테고리들을 트리 구조로 만들어 준 뒤 json으로 응답해줍니다. 아래 함수가 Flat한 category 배열을 트리 구조로 만들어줍니다.

function structureCategories(categories: CategoryObject[]): Category[] {
    function appendChildren(category: CategoryObject) {
        if (category.children.length === 0) return category.name;

        category.children = category.children.map(child => {
            const childCategory = categories.find(c => c.name === child);
            if (childCategory) return appendChildren(childCategory);
            else throw new Error('Something wrong in category data');
        });
        return category;
    }

    return categories.filter(category => category.isTopLevel).map(appendChildren);
}

function CategoryObjectFromICategory(icat: ICategory): CategoryObject {
    return {
        name: icat.name,
        isTopLevel: icat.isTopLevel as boolean,
        children: icat.children,
    };
}

type CategoryObject = {
    name: string;
    isTopLevel: boolean;
    children: Array<Category>;
};

type Category = CategoryObject | string;

Delete

Child가 있는 category를 삭제하면 category의 child들을 재귀적으로 삭제한 뒤 마지막으로 삭제하려 했던 항목을 삭제해야 합니다. 저는 그냥 Child가 있는 category는 삭제가 되지 않게 했습니다. 이런 경우에는 parent의 children에서 category를 삭제해주고 해당 category document를 삭제해주면 됩니다.

Update

단순한 카테고리의 이름 수정은 쉽지만 구조가 바뀌는 경우는 조금 복잡합니다. 하지만 위에서 생성과 삭제를 만들었기 때문에 구조를 수정하는 경우 삭제 후 생성을 해주면 기존 코드를 활용해서 쉽게 구조를 변경할 수 있습니다.


이렇게 하면 서버에서 필요한 작업은 다 끝났습니다. 다음 글에서는 프론트에서 받은 데이터를 이용해 현재 보이는 사이드바를 만드든 방법을 다루겠습니다. 이번 글의 전체 소스코드는 여기에서 확인할 수 있습니다.