[리팩] Entity 설계하기 1

ZEDY·2024년 12월 27일
0

[리팩토링] GASOMANN

목록 보기
2/2

문제점 파악 & 개선 방향

기존 코드 트리


입점 기업 Entity

모호한 이름 수정

우선 이름 부터가 잘못 되었다. IR 장표에서 강조하는 '가치소비'라는 단어를 쓰고 싶었고, 팀 대표가 '가치소비'라고 하라고 해서 Worthy Consumption 이라고 했다. 이게 맞다고 옆에서 하도 그러니까 그러려니 했는데 데이터 관리하려면 데이터의 이름을 잘 지어야 한다는 걸 최근에서야 뼈저리게 깨달았다. 진심 코드랑 테이블 보고 가치소비가 도대체 뭐야... 이러면서 기획 문서를 한참을 들여다 봤다.
결론: 입점기업이라는 이름이 적절하다.
worthy_consumption -> value_company

과도한 결합 수정, 단순한 설계

입점 기업은 다음과 같은 요소가 필요하다.

  • 해시태그(hashtags): 입점 기업의 특징을 간단히 표현하는 키워드로, 유저가 기업의 성격을 빠르게 이해할 수 있도록 제공.
  • 추천 이유(recommendation reasons): 해당 입점 기업이 특별한 이유를 간단히 설명하는 정보.

기존 문제점

근데 나는 기존에 이거를 따로 엔티티로 빼서 연관을 시켰다. 이러면 다음과 같은 문제가 발생한다.

  1. 과도한 테이블 분리
    해시태그와 추천 이유를 각각 별도의 테이블로 분리하여 저장.
  • 문제: 데이터가 단순한데도 불구하고, 불필요한 테이블과 관계 설정으로 인해 설계 복잡성이 증가.
  • 결과: 조회 시 조인이 필요해 성능 저하 발생.
  1. 비효율적인 데이터 구조
  • 해시태그와 추천 이유는 대부분 조회만 사용되며, 검색이나 필터링이 필요하지 않음.
  • 문제: 검색 기능이 없는데도 테이블 분리로 인해 비효율적인 데이터 모델이 생성됨.
  1. 관리 및 유지보수 문제
  • 별도의 테이블로 인해 데이터 변경 시 추가적인 관리 작업 발생.

새로운 설계

  1. 단일 필드(JSON 형태)로 통합
    해시태그와 추천 이유를 각각 단일 필드에 JSON 문자열로 저장.
hashtags: ["친환경", "지속 가능", "로컬 기업"]
recommendation_reasons: [{"title": "최고의 품질", "description": "친환경 제품을 최고의 품질로 제공합니다."}]
  1. 데이터 처리 방식
  • 데이터 저장: 애플리케이션에서 리스트나 객체를 JSON으로 직렬화하여 저장.
  • 데이터 조회: JSON 데이터를 디코딩하여 유저에게 제공.

왜 이렇게 설계했는가?

  1. 단순성과 효율성
  • 단일 필드 통합으로 데이터베이스 구조를 간단하게 유지:
    • 테이블과 관계 설정 불필요.
    • 데이터 조회 시 조인이 필요 없어 성능 최적화.
  1. 유저 요구사항에 적합
  • 해시태그와 추천 이유는 검색이나 필터링 기능이 없으며, 단순히 조회 목적:
    • 유저가 입점 기업을 둘러볼 때 필요한 정보를 빠르게 제공.
    • 조회 API에서 기업의 주요 정보와 함께 해시태그와 추천 이유를 한번에 반환.
  1. 확장성과 유지보수성
  • JSON 필드의 구조를 변경하거나 확장하기 쉬움:
    • 새로운 필드 추가 시, 데이터베이스 스키마를 변경할 필요 없음.
    • 추천 이유에 이미지나 링크를 추가하고 싶다면 JSON 구조를 업데이트하면 충분.
      즉, 단일 필드(JSON)방식으로 설계를 하면, 요구사항을 만족하며 빠르게 데이터를 제공할 수 있다.

기획과 맞지 않는 구조 이동

기존에는 쿠폰 발급 기준인 Condition 엔티티와 가격 관련 정보(original Price, sale Price)를 Worthy Consumption에다가 두었다. What~~

기존 문제점

  1. Condition의 비효율성:
  • Condition 엔티티는 발급 조건을 관리했지만, 이것이 입점 기업의 속성이라기보다는 쿠폰 발급의 로직과 연관됨.
  • 입점 기업(WorthyConsumption)이 조건 데이터를 직접 보유할 필요가 없음.
  1. 가격 정보의 잘못된 위치:
  • originalPricesalePrice는 입점 기업 자체의 속성이 아니며, 특정 쿠폰이나 상품 할인과 관련된 정보.
  • 입점 기업은 할인 정보를 직접 보유하지 않고, 쿠폰이나 챌린지 등과 연관 지어야 더 명확함.

Coupon Entity

위에 문제를 해결하기 위해 쿠폰 엔티티에다가 통합을 했다.

Condition → Coupon
Condition 데이터(발급 조건)는 쿠폰 엔티티로 통합되었으며, 다음과 같은 방식으로 관리된다:

  • 쿠폰 발급 조건(max_issue_count, valid_from, valid_to)과 같은 정보는 Coupon 엔티티에서 관리.
  • 기존의 Condition 엔티티에서 maxIssuance, startDate, endDate 같은 필드는 Coupon 엔티티로 이동.

Price → Coupon
가격 정보는 특정 쿠폰의 할인 정책과 관련되므로 Coupon으로 이동:

  • originalPrice: 원래 가격.
  • discount 또는 salePrice: 할인 금액이나 할인율.

@ElementCollection으로 인한 복잡성

투머치였다.

기존 문제점

  • summary와 caution 필드가 @ElementCollection으로 설정되어, 각각 별도의 테이블로 관리됨.
  • 조회 시 조인이 필요해 성능 저하를 유발하고, 데이터 관리가 복잡해짐.

새로운 설계

  • summary와 caution을 JSON 문자열로 저장.
  • 단일 TEXT 컬럼에 JSON 데이터를 직렬화하여 저장.

왜 이렇게 설계했는가?:

  • 단순한 리스트 데이터를 위해 별도 테이블을 생성하지 않음으로써 테이블 복잡성을 줄임.
  • 조인 없이도 데이터를 조회할 수 있어 성능이 향상됨.
  • JSON 형태로 저장하면 동적 데이터를 유연하게 관리 가능.

날짜 필드 관리의 중복과 복잡성

기존 문제점

  • 발급 가능 기간(issuableStartDate, issuableEndDate)과 사용 가능 기간(limitStartDate, limitEndDate)이 각각 별도 필드로 관리.
  • 날짜 간의 논리적 관계를 파악하기 어렵고, 관리가 복잡함.

새로운 설계

  • 날짜 필드를 @Embeddable 클래스로 재구성하여 Period 클래스로 관리.
  • issuablePeriod와 usablePeriod로 구분.

왜 이렇게 설계했는가?

  • 발급 기간과 사용 가능 기간의 논리적 관계를 명확히 표현.
  • 코드와 데이터 모델의 가독성이 향상되고, 날짜 관련 관리 로직의 중복을 줄임.

앗!! 뭔가!! 알겠다.

유저 입장에서 사용하는 필터링이라던가 그런 기능상으로 검색이 되어야 하는 기준이 명확한게 있으면 클래스의 필드로 빼도 되지만, 그냥 싹 조회 같은 summary나 hashtags 들 같은거, 즉, 객체별로 엄청 다양하게 있는거고 이거는 그냥 객체의 요소로 조회만 하는 거는 text로 처리해 json으로 저장하는게 DB상으로 더 좋은거 같다.

정리하자면..

  • 유저가 필터링하거나 검색의 기준으로 삼아야 하는 데이터:
    • 예: title, price, category 등.
    • 클래스의 필드로 분리해서 저장. 이는 DB 상에서 인덱싱 및 검색에 유리.
  • 단순 조회 목적의 데이터(필터링/검색의 대상이 아닌 데이터):
    • 예: summary, hashtags, recommendation reasons 등.
    • JSON으로 TEXT 필드에 통합 저장. 이는 DB 스키마를 간소화하고 저장 공간 및 성능을 최적화.

이렇게 JSON으로 저장을 하면 객체마다 데이터 구조가 다르더라도 별도의 테이블을 생성할 필요가 없음. 조인 연산도 줄이고, 한 번의 조회로 데이터를 가져올 수 있음. 근데 자주 변경이 되지 않고 단순히 조회만 되는 데이터에 적합하다.

때로는 가독성을 위해 모듈화를 하기도 한다.

기존 문제점

  • 기존에 Coupon 필드에는 issuableStartDate, issuableEndDate, limitStartDate, limitEndDate와 같이 날짜 관련 필드가 따로 떨어져 있음.
  • 필드가 분리되어 있으면, 데이터 간 논리적 연관성을 코드나 데이터베이스 레벨에서 보장하기 어려움.

이러한 문제를 해결하기 위해 논리적 그룹화를 함

새로운 설계

  • 발급 가능 기간(issuable)과 사용 가능 기간(limit)을 Period라는 클래스로 그룹화.
  • 각 기간에 대해 시작일(startDate)과 종료일(endDate) 필드를 하나의 클래스로 묶음.

그런데 이렇게 Period 클래스를 따로 두면 코드 가독성과 구조가 개선되겠지만, 그냥 단순 저장 및 조회가 목적인 지금 단계에서는 오히려 불필요한 복잡성을 유발할 수도 있다.
즉, Over-Engineering이 될 수도 있다.
그래서 필드 명을 좀 더 명확히 하기로 했다.

value_company, coupon 최종 ERD

profile
Spring Boot 백엔드 주니어 개발자

0개의 댓글