이 글의 내용은 DB 설계하는 법 (feat. 데이터 모델링) 의 글을 참고해서 작성했다.
해당 내용을 기반으로 내가 다른 예시로 설계를 하고,설계 중 느낀점들에 대해서 남겼다.
결국, 상향식 설계를 해야만 요구 사항의 발전 & 변경에 대처가 가능하다.
블로거 영운님은 맥도날드를 예시로 들었다.
나는 내가 공부도 할겸 스타벅스를 예시로 설계를 해볼것이다.
( 참고 그림 )
- 메뉴를 CRUD 할 수 있다. ( 메뉴는 또 음료 / 푸드 / 상품 그룹을 가진다. ) - 여기서 메뉴는 음료로만 가정한다.
- 메뉴마다 이름 / 설명 / 가격 / ICED(HOT) ONLY / 제품 영양 정보를 가진다.
- 메뉴마다 퍼스널 옵션이 존재한다. ( 각 메뉴마다 매우 다양하다. - 사실상 공통화 X )
- 메뉴는 하나 이상의 메뉴 그룹에 매핑된다. ( 추천/NEW 에 포함될 수 있으며, 기본 티바나/피지오 등에 포함될 수 있다. )
- 매장을 CRUD 할 수 있다.
- 매장은 주소, 운영 시간, 오시는길, 등을 가진다.
- 몇명 매장은 특별 상품을 가진다.
- 장바구니에 1개 이상의 메뉴를 담을 수 있고 매장을 선택한다.
- 메뉴들은 선택한 퍼스널 옵션 / 사이즈 등을 가진다.
- 선택한 매장과 메뉴 수령 방법을 가진다.
기획안을 통해 모든 키워드를 뽑아보자. ( 불필요한 정보성 칼럼들은 생략 )
메뉴는 음료 / 푸드 / 상품 그룹을 가지나, 얘네를 하나의 테이블로 관리할 것 같지는 않다.
( 공통점 : 설명,가격이 끝, 차이점 : ICED ONLY 선택, 알레르기, 등등 대부분이 다름 )
ICED 와 HOT 은 두개를 다른것으로 구분한다 ( 영문명, 설명,그림 등이 달라지므로 다른것으로 판단 )
메뉴
- 이름
- 설명
- 가격
- 가능한 사이즈
- 온도 ( ICED, HOT )
- 퍼스널 정보
- 알레르기 유발 요인
- 제품 영양 정보 ( 칼로리, 탄수화물, 당류 등등 )
퍼스널 정보
- 시럽 ( 그룹 )
- 바닐라
- 헤이즐넛
- 카라멜
- 우유
- 일반
- 저지방
- 무지방
- 두유
- 오트
- 얼음
- 휘핑 크림
- 드리즐 ...
매장
- 매장 주소
- 전화번호
- 가능 서비스들
- 배달
- 개인컵
- 야간
- 운영 시간
- 오시는 길
장바구니
- 주문 메뉴들
- 매장 정보
- 픽업 방법
우선, 실체 엔티티 와 행위 엔티티를 분리해보겠다.
실체 엔티티는 결국 모델의 뼈대이기에, 모델의 근간이 튼튼해진다.
여기서, 내가 생각하기에 실체 엔티티는
매장, 메뉴, 옵션, 메뉴 그룹 이라고 생각한다.
( 느끼는 점으론 정적인 값들이라고 생각하고 실체 엔티티를 접근해도 괜찮은 것 같다. )
이때 되게 생각을 많이 했는데 퍼스널 정보라는 테이블 자체가 필요하지 않다.
옵션을 통해서 그룹화가 가능하다!
그러면 이제 요구사항과 엔티티들을 통해 다대다 관계를 생각해보자
퍼스널 옵션들을 빼고 다대다는 단순히 테이블만 더 만들면 된다.
그후, 관계를 설정하면?
기본적인 설계 및 프론트에 보여주기 위한 엔티티 설계는 마무리가 되었다.
하단에는 이번에 설계하며 생각한 부분들에 대해 작성하며 마무리 하려고 한다.
추가적으로, DB 가 가져야 하는 데이터인지 가질 필요 없는 데이터인지를 잘 구별 해내야 한다.
스타벅스에서 퍼스널 옵션을 선택할때는 각각 옵션마다 선택지가 다양하다.
예를 들어
1. 시럽에서는 바닐라 시럽,헤이즐넛 시럽, 카라멜 시럽 등이 있는데 횟수를 선택할 수 있다.
2. 휘핑 크림은 작게 / 보통 / 많이를 선택할 수 있다.
3. 드리즐은 카라멜 드리즐, 초콜렛 드리즐이 있는데 적게/보통/많이를 선택할 수 있다.
이때 적게 / 보통 / 많이를 굳이 DB에 저장할 필요가 있을까?
class Enum Amount {
LESS("적게"),
NORMAL("보통"),
MORE("많이");
}
Drizzle 에 대한 정보를 받으면 해당 값 + Enum 을 포함시켜줘서 프론트로 반환하면 칼럼이 없어도 칼럼이 있는것처럼 기대할 수 있다.
@Entity
class Drizzle{
@Id
private Long id;
private String name;
private BigDecimal price;
}
public DrizzleRespone toResponse(Drizzle drizzle){
return new DrizzleResponse(
drizzle.getId(),
drizzle.getName(),
drizzle.getPrice(),
Set.of(Amount.LESS,Amount.NORMAL)
);
}
물론, 특정 커피는 적게&보통만 가능하고, 어떤 커피는 많이만 가능하는 등의 빈도가 자주 보인다면 테이블이 가지는 것도 고려할 수 있다.
-> 그룹화와 유연한 확장에 대해 생각을 해야 한다.
그리고 Depth가 깊어지면
해당 내용에서의 일환으로도
옵션을 저장할 필요가 있을까? 에 대해서도 판단할 수 있다.
-> 사실 조회 자체도 필요없을수도, 저장될 필요가 없을수도 있다.저장될 필요가 없다고 판단 하면 로그성 데이터로 이동후 로그성 데이터로만 사용되게 의도할 수 있다.
정보를 DB에 저장할 수도, 애플리케이션이 가질 수도 있다.
이 부분은 현 인프랩 CTO 향로님이 배민에서 작성하신 Java Enum 활용기 에서도 잘 설명되어 있다.
내용을 인용하자면
특히나 카테고리의 경우 6개월에 1~2개가 추가될까말까한 영역인데 굳이 테이블로 관리하는 것은 장점보다 단점이 더 많다고 생각하였습니다.
Enum을 사용하는데 있어 가장 큰 허들은 "변경이 어렵다"란 의견입니다.
정말 코드를 추가하거나 변경해야 하는 일이 빈번하다면, 매번 Enum 코드를 변경하고 배포하는것보다 관리자 페이지에서
관리자가 직접 변경하는 것이 훨씬 편리할 수 있다고 생각합니다.
적정선에서 Enum으로 관리할 부분과 테이블로 관리할 부분을 잘 나누어야 된다고 생각합니다.)
Enum의 장점으로는 쿼리문을 날릴 필요 없는 점 + 메모리 내 상주 + 데이터 값과 로직을 같은곳에 붙힐 수 있는게 장점이다. ( 가장 큰 장점은 날리는 CRUD API 구현 할 필요가 없다는 점도 큰 장점 )
Enum의 변경으로 애플리케이션이 하루에도 2~3번씩 재시동&배포 해야하거나 ( 즉, 데이터 변동성을 잘 파악 )
너무 많은 데이터거나, 너무 용량이 큰 데이터가 아니라면 Enum으로 관리를 하는게 맞다고 판단했다.
원래 해당 내용은
퍼스널 옵션들에 대한 장바구니와 결제 내역 저장들에 대해서도 남기려고 했으나
내용이 길어져서 다음편에서 다룬다.