ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 조직도를 만들어보자
    개발/Spring(boot) 2024. 12. 8. 23:42

    회사에서 기존 조직 데이터가 유연하지 못해 이를 개편하고자 했고 내가 담당하게 됬다.

    그래서 이번 글에서는 기존 조직 데이터의 문제, 요구사항, 내가 만든 조직 데이터, 한계점에 대해 작성해보고자 한다.


    1. 기존 조직 데이터의 문제

    먼저 사내에서 사용하던 조직 데이터는 아래 데이터와 유사하게 생겼다.

     

    유저 테이블의 컬럼(요약)

    • id
    • 유저 이름
    • 기타 정보
    • 조직 - 대분류
    • 조직 - 중분류
    • 조직 - 소분류

    내가 생각한 것 이상의 문제가 있을 수 있지만, 내가 이 데이터를 봤을 때 느낀 가장 큰 문제조직의 계층이 3계층으로 고정되어 있다는 점 이다.

    그래서 부가적으로 생기는 문제는 세 가지가 있었다.

     

    1. 어떤 유저는 소분류 조직에 속하지 않을 수 있다.

    XX그룹 라는 조직이 있다고 가정하고 이는 대분류에 속한다고 생각해보자.

    그렇다면 이 유저 데이터는 중분류와 소분류는 비어있게 되는 문제가 생기게 된다.

     

    2. 조직의 계층이 더이상 늘어날 수 없다.

    대분류 조직 상위에 XX사업부 라는 조직을 만들고 싶다고 가정해보자.

    그렇다면 이 데이터 구조상으로는 불가능하고 유저 테이블에 "사업부"라는 컬럼을 하나 만들고 모든 데이터를 마이그레이션 해야 하는 문제가 생긴다.

     

    3. 조직의 수정이 유저 테이블에 영향을 미치게 된다.

    A그룹이라는 조직을 A-1이라는 조직으로 변경하고 싶다면 단순히 조직명을 바꾸는 것 뿐만 아니라 A그룹에 속해있는 유저들을 모두 A-1그룹으로 변경해야 하는 문제가 생긴다.

     

    기획자 역시 해당 내용에 대해 인지하고 계셨고 그래서 나에게 해당 업무를 맡기신 것 같다.

    그래서 요구사항은 아래와 같다.

     

    2. 요구사항

    1. 조직은 계층을 가질 수 있다.

    2. 조직 계층의 깊이는 제한을 두지 않는다.(개발 시에는 5로 제한을 두지만 확장성을 고려)

    3. 조직은 자유롭게 이동할 수 있다.

       - 예를 들어 A - a - 1이라는 조직과 B - b - 2라는 조직이 있을 때 B 조직이 1이라는 조직 밑으로 자유롭게 이동할 수 있다.

    4. 조직 수정 페이지에서는 Create/Update/Delete가 발생할 수 있지만 저장 버튼을 눌러야 DB에 반영된다.

    5. 조직도에 속한 모든 유저 데이터를 조회할 때 조직이라는 트리 기준으로 preorder 순으로 조회해야 한다

     

    이 내용 말고도 다른 요구사항이 있었지만 2번 내용이 테이블을 설계하는데 고민을 많이 하게 만들었고 4번 내용은 springboot에서 코드를 짤 때, 5번  내용은 db 조회 쿼리를 만들 때 꽤 많은 고민을 하게 만들었다.

    그럼 이제 고민했던 내용에 대해 작성해보고자 한다.

     

    3. 내가 만든 조직 데이터

    먼저 조직(team)이라는 table을 하나 만들었고 데이터 구조는 다음과 같다.

    • id
    • 조직 이름(name)
    • 부모 조직(parent)
    • 형제간 순서(sibling_order)
    • 깊이(depth)
    • ...

    먼저 부모 조직 컬럼에 대해 작성해보겠다.

    조직 테이블의 구조가 유연하기 위해서는 LinkedList의 형태와 유사해야 한다고 결론을 내렸고 이는 단방향 연결 리스트의 구조로 만들기로 결정했다. 이렇게 된다면 조직의 계층이 한없이 깊어질 수 있기 때문에 이 방법을 채택했다.

     

    다음은 형제간 순서 데이터이다.

    조직에 속한 계정을 조회하고 order by 쿼리에 넣기 위해서는 기준이 될만한 데이터가 필요하다.

    그리고 조직도를 기준으로 최상위 조직부터 최하위 조직까지 순서를 하나씩 매긴다면 조직의 순서가 변경되었을 때 문제가 생길 수 있어 보였다.

    예를들면 아래와 같은 문제가 발생한다.

     

    기존 조직도

    • A(1번)
      • a(2번)
      • b(3번)
    • B(4번)
      • c(5번)
      • d(6번)
    • C(7번)
      • e(8번)
    • D(9번)
      • f(10번)

    여기서 조직도를 아래와 같이 변경한다고 하자.

    • f(1번)
    • A(2번)
      • a(3번)
      • b(4번)
    • B(5번)
      • c(6번)
      • d(7번)
    • C(8번)
      • e(9번)
    • D(10번)

    최상위 조직부터 최하위 조직까지 순서를 하나씩 매긴다면 

    f조직만 변경했지만 모든 조직의 순서 데이터가 전부 변경되야 하는 문제가 생긴다.


    그래서 형제간 순서 라는 데이터를 사용해 최대한 변경되는 데이터를 줄이고자 노력했다.

    참고로 형제간 순서라는 데이터는 동일한 깊이와 동일한 부모를 가지는 데이터를 의미하는 컬럼이다.

    형제간 순서 데이터를 가지고 동일하게 조직의 순서를 변경한다고 가정해보자.

    기존 조직도

    기존 조직도

    • A(깊이1 / 부모 null / 1번)
      • a(깊이 2 / 부모 A / 1번)
      • b(깊이 2 / 부모 A / 1번)
    • B(깊이1 / 부모 null / 2번)
      • c(깊이 2 / 부모 B / 1번)
      • d(깊이 2 / 부모 B / 2번)
    • C(깊이1 / 부모 null / 3번)
      • e(깊이 2 / 부모 C / 1번)
    • D(깊이1 / 부모 null / 4번)
      • f(깊이 2 / 부모 D / 1번)

    여기서 조직도를 아래와 같이 변경한다고 하자.

    • f(깊이 1 / 부모 null / 1번)
    • A(깊이1 / 부모 null / 2번)
      • a(깊이 2 / 부모 A / 1번)
      • b(깊이 2 / 부모 A / 1번)
    • B(깊이1 / 부모 null / 3번)
      • c(깊이 2 / 부모 B / 1번)
      • d(깊이 2 / 부모 B / 2번)
    • C(깊이1 / 부모 null / 4번)
      • e(깊이 2 / 부모 C / 1번)
    • D(깊이1 / 부모 null / 5번)

    이와 같이 깊이와 부모가 동일한 데이터만 순서 변경에 영향을 받게 된다.

    이정도가 내가 현재 조직 데이터에서 할 수 있는 최선이라고 느꼈고 그래서 이와 같이 형제간 순서 라는 데이터를 조직 테이블에 넣게 되었다.


    물론 이렇게 조직 데이터를 만들고 난 뒤에 조직에 속한 유저들을 순서대로 조회하는데도 꽤나 어려움을 겪었다.

    예를들어 아래와 같이 유저가 있다고 가정하면 조직도라는 트리 기준으로 preorder순으로 조회해야 하는 것이 요구사항이었다.

     

    조직도와 조직에 포함된 유저

    - 조직 1 -> a, b유저가 속한 조직

          - 조직 2 -> c, d유저가 속한 조직

             - 조직 3 -> e 유저가 속한 조직

         - 조직 4

       - 조직 5-> f 유저가 속한 조직

          - 조직 6 -> g, h유저가 속한 조직

    - 조직 7 -> i 유저가 속한 조직

     

    조직도에 속한 유저를 preorder 순으로 조회한다는 내용은

    위 조직도 기준 a, b, c, .. i 유저와 같이 알파벳 순으로 조회해야 한다는 요구사항이었다.

     

    이는 열심히 구글링을 하고 GPT에게 도움을 얻은 결과 CTE(Common Table Expression) 쿼리를 통해 조회할 수 있도록 기능을 구현했다.

    간략하게 설명하자면 임시 테이블을 만들어 기존 테이블의 데이터를 재귀적으로 조회할 수 있도록 도와주는 기능이다.

    그렇게 요구사항에 맞게 기능 구현을 마치게 되었고, 마지막으로 한계점에 대해 작성해보고자 한다.

     

    4. 한계점

    일단 가장 아쉬웠던 점은 그럼에도 불구하고 하나의 조직 순서가 수정되었을 때 여러 개의 조직 순서가 함께 영향을 받는다는 점 이었다.

    즉 형제간 순서라는 데이터를 사용해도 형제들은 하나만 수정되도 전부 변경 될 가능성이 있다는 점이다.

     

    두번째로 프론트에서 주는 형제간 순서 데이터를 어느정도는 신뢰해야 한다는 점 이다.

    형제간 순서 데이터는 사실 1부터 무한히 늘어날 수 있는 데이터이고

    유효성 검증은 같은 깊이/같은 부모를 가지는 동일한 형제 순서를 가지는 데이터가 이미 존재하는지? 정도만 하고 있다.

    물론 더 빡빡하게 유효성을 검증한다면 부모가 자식의 숫자를 관리한다면 가능해 보인다. 하지만 어느정도 구현이 마무리되었을 때 해당 내용이 떠오르게 되었고 치명적인 이슈는 아니라고 판단해 백로그로 남겨두는 정도로 그쳤다. 


    오늘은 조직 데이터를 개편하면서 했던 고민에 대해 작성해보았다.

    사실 작성된 내용 중 CTE쿼리를 작성하며 했던 삽질과 저장 버튼을 누르기 전에 일어날 수 있는 많은 예외상황들과 이를 잡기 위해 꽤나 많은 노력을 했지만 딱히 적을만한 내용은 아니라고 느껴 여기서 글을 마치고자 한다.

    반응형

    '개발 > Spring(boot)' 카테고리의 다른 글

    ApplicationEventPublisher와 EventListener  (0) 2024.12.01
    Springboot Redis 세션 만료 감지하기  (0) 2024.11.24

    댓글

Designed by Tistory.