Home [Spring] 시간표 기능 ERD 구상하기
Post
Cancel

[Spring] 시간표 기능 ERD 구상하기

데이터 형태

우선 시간표를 웹상에서 어떤 방식으로 주고받을지 생각해보았습니다.

시간표는 아래와 같은 조건들이 있습니다.

  • 시간표는 이름을 가지고 있다.
  • 각 시간표는 여러 수업을 포함한다.
  • 수업은 이름, 장소, 강사, 수업 시간, 색상을 포함한다.
  • 수업 시간은 시작 시각, 종료 시각, 요일이 포함된다.
  • 수업 하나에 여러 수업 시간이 있을 수 있다. (1주에 2회이상 수업)

이 조건들을 만족하는 데이터 구조를 짜보았습니다.

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
{
    "name": "내 시간표",
    "lectures": [
    {
        "name": "강의1",
        "place": "장소1",
        "professor": "교수1",
        "color": "#ff0000",
        "times": [
        {
            "from": "10:00",
            "to": "13:00",
            "week": 0
        },
        {
            "from": "11:00",
            "to": "14:00",
            "week": 3
        }
        ]
    },
    {
        "name": "강의2",
        "place": "장소2",
        "professor": "교수2",
        "color": "#ffff00",
        "times": [
        {
            "from": "10:00",
            "to": "13:00",
            "week": 0
        }
        ]
    }
    ]
}

이런 형태로 API를 구성합니다.

ERD 구상

Domain Model

ERD

위는 구상한 Domain Model과 ERD입니다.

사실 ERD 구조 자체는 매우 단순합니다. 하지만 고민이 필요한 포인트가 있습니다.

ERD를 위와 같이 구성하면 시간표를 조회할 때 3중 join이 필요합니다. 왜냐하면 시간표를 불러올 때는 반드시 수업정보와 수업 시간들도 불러와야합니다. 그래서 필연적으로 조회할 때마다 3중 join이 들어갑니다. JPA를 사용하는 경우에는 join/fetch join을 사용하지 않으면 3회 쿼리가 나갑니다.

이 문제를 해결하기 위해 반정규화를 해볼 수 있습니다. LectureTime테이블만 Lecture에 문자열로 저장해볼 수 있지 않을까요?

반정규화 ERD

이런식으로 LectureTime을 바로 String으로 저장해서 사용할수도 있습니다. 이렇게 구성하면 몇 가지 장점이 있습니다.

  • 테이블을 하나 덜 쓰므로 ERD가 비교적 간결해집니다.
  • Join도 1번만 쓰면 됩니다. 조회 속도가 비교적 빨라질 겁니다.

하지만 이렇게 하면 몇 가지 문제점이 있어요.

  • 강의 시작 시각, 종료 시각, 요일을 통한 SQL을 구성하기 어려워집니다. 이게 좀 큰 문제점입니다. 강의 시각과 관련된 정보들이 모두 String으로 인코딩되어있기 때문에 SQL로 이 정보들을 조건문에 사용하기 어렵습니다. 이 정보들을 연산이나 projection에서 사용할 수 없어서 SQL을 짜기 힘들어집니다. 문자열 탐색이므로 속도도 느려집니다.
  • 필드를 직접 추가적으로 Deserialize하는 과정이 필요합니다. 강의 시각 정보가 문자열로 저장되므로 이를 활용하려면 저희가 문자열을 직접 변환해야합니다. JPA에서 제공하는 Converter를 사용하면 특정 클래스로 변환할 수는 있지만, 설정상 번거로운 과정이 포함됩니다. 그리고 저장할때도 마찬가지로 serialize해주어야 합니다.

그래서 Trade-off를 해야합니다. 저는 결국 맨 처음에 소개한 정규화된 ERD를 선택했습니다. 판단 근거는 아래와 같습니다.

  • 제 프로젝트에서는 강의 시작 시각, 종료 시각, 요일을 조건에 사용할 일이 종종 있어서 반정규화하면 SQL이 복잡해집니다.
  • 하나의 Lecture당 최대 LectureTime는 2개입니다. 즉 3중 join을 하든, 2중 join을 하든 반정규화를 통한 성능향상은 미미합니다.
  • 성능차이가 미미하면 정규화된 형태로 두는 것이 좋다고 생각했습니다. DB단에서 LectureTime의 데이터 무결성을 보장해줄 수 있습니다.

결론

3개의 테이블을 연관지어 시간표 정보를 저장합니다. 반정규화를 통해 성능 향상을 노려보았지만… 강의 시각 정보를 통한 SQL문 구성이 어려워지고 성능 향상이 미미해서 포기했습니다.

무엇보다 반정규화말고도 다른 방법으로 성능을 향상시킬 수 있습니다. 반정규화는 최후의 방법으로 사용해야한다고 생각합니다.

시간표 테이블 TimeTable은 다른 2개 테이블과 생명 주기가 동일합니다. 이를 맞춰주기 위해 JPA단에서 Cascade, orphanRemoval를 설정해서 적절하게 생명주기를 관리해주어야 할 것입니다.

This post is licensed under CC BY 4.0 by the author.

[Spring] 스프링 부트 각종 테스트 방법 (2) - Controller

[Spring] Redis 사용하기 (1) - 좋아요 기능 성능 개선