데이터베이스 구조 설계

2024년 8월 30일 금요일

Today I Learned

날짜

2024년 8월 30일 금요일

내용

데이터베이스 구조 짜기

새로운 스프린트를 들어가면서 서버를 파야한다. 데이터베이스 구조를 고민하는데 참 여러모로 쉽지 않다. 네이버 플랫폼 확장이 이뤄질 것이다 보니.. 중요한 포인트는

  1. 플랫폼은 네이버와 쇼피파이가 될 예정
  2. 가져올 인스타그램 계정 종류는 두가지다. 인스타그램 비즈니스 계정과 일반 계정
  3. 인스타그램 비즈니스 계정은 필수적으로 페이스북 페이지와 연결되어있다.
  4. 네이버에서 어떤 데이터를 어떻게 사용할지 모른다.

가장 중요한건 확장성이라고 판단했고, 기존 알파플러스를 유지보수하고 개선하면서 가장 힘든게 최상위 데이터가 플랫폼에 종속되어 있다는 점이었다. 그땐 그게 당연헀으니까… 그래서 이번엔 최상위 테이블을 외부 데이터와 전혀 관련되지 않게 만들었다.

사실 쇼피파이와 네이버 스마트스토어가 같은 인스타그램 계정을 사용할 일은 극히 드물거라고 판단해서 두 테이블을 만들어도 크게 상관은 없지 않을까 생각했다. 쇼피파이 쪽에서 사용하는 인스타그램 비즈니스 계정 테이블과 네이버에서 사용하는 인스타그램 비즈니스 계정 테이블. 근데 뭔가 굳이.. 굳이.. 싶어서 열심히 고민하다가 그냥 다대다 관계를 만들수 있는 secondary 테이블을 만들었다.

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
37
38
39
40
41
42
43
44
45
46
47
48
class Store(Base):
    """
    Store 정보
    유저당 하나의 스토어 정보를 가집니다.
    """

    __tablename__ = "store"
    service_id = Column(
        String,
        ForeignKey("user.service_id"),
        primary_key=True,
        index=True,
        unique=True,
    )
    
    instagram_business_accounts = relationship(
        "InstagramBusinessAccount",
        secondary="store_instagram_business_account_association",
        back_populates="store",
    )

class InstagramBusinessAccount(Base):
    """
    인스타그램 비즈니스 계정 정보
    """

    __tablename__ = "instagram_business_account"

    id = Column(Integer, primary_key=True, index=True, unique=True)
    facebook_id = Column(TEXT)
    facebook_username = Column(TEXT)
    facebook_access_token = Column(TEXT)
    facebook_access_token_expires_at = Column(DateTime)
    app_id = Column(TEXT)

    store = relationship(
        "Store",
        secondary="store_instagram_business_account_association",
        back_populates="instagram_business_accounts",
    )
    
# Store와 InstagramBusinessAccount의 관계를 나타내는 테이블
StoreInstagramBusinessAccountAssociation = Table(
    "store_instagram_business_account_association",
    Base.metadata,
    Column("service_id", String, ForeignKey("store.service_id"), primary_key=True),
    Column("instagram_business_account_id", Integer, ForeignKey("instagram_business_account.id"), primary_key=True),
)

중간 테이블을 둬서 다대다 관계가 용이하게 만들어줬다.

한 가지 더 추가한 점은 연관된 스토어 관련 테이블들의 primary_key를 service_id로 통일했다. 최상단 테이블인 User 테이블에 있는 데이터는 각자 고유한 값을 service_id라는 8글자짜리 랜덤 문자열로 가진다. 각 유저는 하나의 스토어 데이터를 가지는데 이 스토어 테이블도 primary_key를 service_id라는 foreign_key 로 설정해줬다. 이전에는 id 라는 숫자를 고윳값으로 각자 가졌는데, 각 테이블마다 다른 id를 가지니 여러모로 중구난방이라는 느낌이 들어서 이렇게 처리했다. 코드로 보자면

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
class User(Base):
    """
    최상위 사용자 정보
    """

    __tablename__ = "user"

    service_id = Column(String, primary_key=True, index=True, unique=True)

    store = relationship(
        "Store",
        back_populates="user",
        passive_deletes=True,
    )

class Store(Base):
    """
    Store 정보
    유저당 하나의 스토어 정보를 가집니다.
    """

    __tablename__ = "store"
    service_id = Column(
        String,
        ForeignKey("user.service_id"),
        primary_key=True,
        index=True,
        unique=True,
    )

    user = relationship("User", back_populates="store", passive_deletes=True)

    shopify_store = relationship(
        "ShopifyStore",
        back_populates="store",
        passive_deletes=True,
    )

    naver_store = relationship(
        "NaverStore",
        back_populates="store",
        passive_deletes=True,
    )
    
class ShopifyStore(Base):
    """
    Shopify에서 받는 스토어 정보를 담는 테이블
    Shopify에서 최상단 테이블
    """

    __tablename__ = "shopify_store"

    service_id = Column(
        String,
        ForeignKey("store.service_id", ondelete="CASCADE"),
        index=True,
        primary_key=True,
        unique=True,
    )
    
    ...

    store = relationship(
        "Store",
        back_populates="shopify_store",
        passive_deletes=True,
    )
    
class NaverStore(Base):
    """
    Naver에서 받는 스토어 정보를 담는 테이블
    Naver에서 최상단 테이블
    """

    __tablename__ = "naver_store"

    service_id = Column(
        String,
        ForeignKey("store.service_id", ondelete="CASCADE"),
        index=True,
        primary_key=True,
        unique=True,
    )

    store = relationship(
        "Store",
        back_populates="naver_store",
        passive_deletes=True,
    )

이런식이다. 결국 완성한 전체 모양새는

Screenshot 2024-08-30 at 6.00.02 PM.png

테이블 이름 참 길다.

회고

클린 아키텍처!