SQL 캠프 광고 이미지
  • SQL
  • 로그인 전용

알아두면 쓸데있는 데이터 모델링 (5) 속성 톺아보기

SQL 캠프 광고 이미지
SQL 캠프 광고 이미지
지금 간편하게 로그인하고 전문성 있는 정보를 확인해보세요.

데이터 분석가들이 직접 쓰는 다른 로그인 전용 글들도 무제한으로 이용할 수 있습니다.

🔖
알아두면 쓸데있는 데이터 모델링 모아보기
  1. 정규화
  1. 여러 종류의 Key 이해하기 (feat. PK, FK는 무엇인가?)
  1. ER 모델의 구성 요소 - 엔티티, 관계, 속성 [✨ 로그인 전용]
  1. 엔티티 톺아보기 [✨ 로그인 전용]
  1. 속성 톺아보기 [✨ 로그인 전용]
 
 
데이터 모델링을 시작하려고 한다면 꼭 알아두어야 할 속성과 관련한 세 가지 토픽을 다뤄보려고 합니다.
 
  1. 데이터 표준화
  1. 식별자
  1. 유연함을 가진 속성 모델링
 

데이터 표준화

데이터 모델링을 시작하면 가장 먼저 벽에 부딪히는 것이 바로 작명입니다. 우스개소리로 ‘프로그래머가 가장 힘들어 하는 일은 변수명을 짓는 것이다’라는 말이 있는데요. 데이터 모델링도 크게 다르지 않습니다. 엔티티와 속성 이름을 짓는데 많은 고민을 하게 됩니다. 이렇게 이름을 지어줄 때에 표준화를 고려해야 하는데요. 간단하게는 이음동의어와, 동음이의어를 최소화한다고 생각하면 좋습니다. 이어서 자세하게 설명해보겠습니다.
 

이음동의어를 최소화: 일관성이 있어야 한다

데이터리안의 캠프는 데이터베이스에 어떻게 표현해야 할까요? ‘content’, ‘course’, ‘lecture’, ‘강의’ 등 여러가지로 표현할 수 있습니다. 예를 들어, A라는 테이블에는 ‘course’라고 속성 이름을 정해놓고 B 테이블에는 ‘lecture’라고 정했다고 생각해봅시다. 테이블이 두 개면 괜찮지만, 테이블이 많아지고 데이터 구조가 복잡해지면 나중에는 캠프라는 개념을 찾기 어려워질겁니다. 캠프를 ‘course’라고 정했다면 데이터 구조 전체에서 캠프는 ‘course’라는 이름으로 불려야 합니다.
또한 ‘course’라는 속성을 사용하는 것 뿐 아니라, 캠프와 관련된 모든 속성에서 ‘course’라는 이름을 일관적으로 사용해야 합니다. 아래와 같이 캠프 관련 속성은 여러 종류가 있을 수 있습니다.
 
  • course_id
  • course_title
  • course_type
  • course_description
 
이렇게 이음동의어를 최소화하는 것이 데이터 표준화의 핵심입니다.
 

동음이의어를 최소화: 구체적이어야 한다

속성명은 구체적이어야 합니다. 예를 들어, 주민등록번호는 ‘user_id’라고 부르는 것이 아니라 ‘resident_registration_number’ 또는 ‘rrn’이라고 써야 합니다. ‘대한민국의 모든 국민에게 부여되는 번호인데, 국내 서비스만 하면 ‘user_id’에 주민등록번호를 넣고 써도 되는거 아니야?’라고 생각할 수도 있겠지만 주민등록번호는 바뀔 수도 있고, 또 해외 사용자들에게 서비스를 확장하기 위해 진짜 ‘user_id’의 개념이 필요해질 수도 있거든요. A라는 테이블에서는 ‘user_id’가 주민등록번호를 의미하는데, B라는 테이블에서는 유저 고유 번호를 의미하는 상황이 충분히 벌어질 수 있습니다.
예시는 이해를 쉽게 하기 위해 극단적으로 들었지만, 속성명이 구체적이지 않아 동음이의어가 생기는 경우가 실무에서 굉장히 많습니다. ‘고객’이 아니라 ‘고객 고유 번호’, ‘매출’ 또는 ‘금액’이 아니라 ‘고객 당 매출 금액’ 또는 ‘연간 총 매출 금액’으로 속성 명만 읽었을 때에 이해가 되는 수준으로 더 구체적으로 정의해야 합니다.
 
 

식별자

식별자(Primary Key, PK)는 모델링에서 여러가지 역할을 하는 굉장히 특별한 속성이기 때문에 설계에 주의를 기울여야 합니다.
식별자는 정규화의 기준 역할을 합니다. 제2정규화 원칙 ‘후보 키가 아닌 속성이 후보 키 전체에 종속되어야 한다’에서 ‘후보 키’는 주 식별자가 될 수 있는 후보를 의미합니다. 제3정규화 원칙 ‘후보 키가 아닌 속성들은 모두 후보 키에 종속되어야 한다’에서도 마찬가지로 ‘후보 키’가 정규화 원칙의 핵심이 되어주고 있습니다. (정규화 원칙에 대해서 궁금하신 분들은 모델링 시리즈의 첫 번째 글 정규화를 읽어주세요.)
식별자는 개체를 고유하게 식별할 수 있어야 하기 때문에 유일한 값으로 관리가 되어야 합니다. 식별자는 곧 개체이기 때문에, 엔티티에 개체가 추가되는 규칙도 식별자에게 종속적입니다. 이 부분은 생소할 수 있어 아래에서 예시를 들어 자세하게 설명을 해보겠습니다. 대략 데이터리안의 테이블 구조가 아래와 같다고 생각해봅시다. <사용자>와 수강할 수 있는 <캠프>가 있고 어떤 사용자가 어떤 캠프를 듣는지에 대한 정보를 관리하는 <수강 내역>이 있습니다.
 
<사용자>
  • customer_id (PK)
  • customer_name
<수강 내역>
  • customer_id (PK, FK)
  • camp_id (PK, FK)
  • begin_at
<캠프>
  • camp_id (PK)
  • camp_name
 
위 구조에서 <수강 내역>은 customer_id, camp_id를 식별자로 가지는데요. 설명을 쉽게 하기 위해 가장의 <수강 내역> 테이블에 데이터를 넣어보면 아래와 같이 생겼을겁니다. 사용자 1114는 SQL 데이터 분석 캠프 입문반(sql-basic), 실전반(sql-advanced)을 모두 등록했고 사용자 1115는 입문반만 등록했네요.
 
<수강 내역>
customer_id
camp_id
begin_at
1114
sql-basic
2023-02-05 00:00:00
1114
sql-advanced
2024-03-04 00:00:00
1115
sql-basic
2024-02-05 00:00:00
 
이런 상황에서 사용자 1114가 입문반(sql-basic)을 다시 구매하려고 시도합니다. 실전반을 들으려고보니 1년 전에 들은 입문반 내용이 잘 생각이 안났나봐요. 데이터베이스 입장에서는 <수강 내역> 테이블에 (1114, sql-basic, 2024-02-05 00:00:00) 행을 입력하려고 시도하는건데요. <수강 내역> 테이블은 이 데이터를 받아주지 않습니다. (customer_id, camp_id)의 조합을 식별자로 등록했고, 식별자는 유일해야 하기 때문에 이미 존재하는 (1114, sql-basic)에 중복되는 새로운 데이터를 받지 않는거예요. 데이터베이스에서는 에러메시지를 줄겁니다.
이 상황은 언뜻보면 이상해보이지만 데이터리안의 서비스에는 꼭 맞는 설계입니다. 한 번 수강한 캠프라면 완강시 평생 학습권을 주고, 만약 기간 내에 완강하지 못했다면 수강료 전부를 다 내고 새로 등록하는게 아니라 수강 기한 연장권을 구매하여 들을 수 있도록 하고 있기 때문이에요.
 
  • SQL 데이터 분석 캠프 | 입문반 수강
    • 완강 O → 평생 학습권 지급
    • 완강 X → 수강기한 연장권 구매 → 완강 O → 평생 학습권 지급
 
오히려 같은 캠프를 여러 번 구매할 수 있도록 시스템 제약을 걸어놓지 않았다면 어떤 수강생은 10만원의 연장권으로 기존 강의를 연장하여 학습하고, 어떤 수강생은 수강료를 전부 다 내고 학습을 하는 상황이 벌어질 수 있을겁니다. 그런데 일반적인 커머스의 경우에도 이런 설계가 적합할까요?
 
<사용자>
  • customer_id (PK)
  • customer_name
<주문>
  • customer_id (PK, FK)
  • product_id (PK, FK)
  • ordered_at
<상품>
  • product_id (PK)
  • product_name
 
이해를 돕기 위해 일반 커머스의 주문 테이블을 아래와 같이 간략하게 적어보았습니다. 51689는 칫솔 세트라고 생각해볼게요. 사용자 1114는 2023년 2월에 구매한 칫솔 세트를 1년 동안 알차게 썼습니다. 이제 다 떨어져서 같은 상품으로 재구매를 하려고 해요. 예상하셨겠지만, 위와 같은 테이블 구조로는 같은 사용자가 같은 상품을 두 번 주문할 수 없습니다.
 
<주문>
customer_id
product_id
ordered_at
1114
51689
2023-02-05 12:59:40
1115
51689
2024-03-05 14:30:01
 
아래와 같이 <주문> 테이블의 식별자로 order_id를 만들어야 같은 사용자가 같은 상품을 여러번 주문할 수 있게 됩니다.
 
<사용자>
  • customer_id (PK)
  • customer_name
<주문>
  • order_id (PK)
  • customer_id (FK)
  • product_id (FK)
  • ordered_at
<상품>
  • product_id (PK)
  • product_name
 
사용자 1114가 새롭게 주문한 칫솔 세트 데이터는 아래와 같이 쌓이게 될거예요. 이제 식별자는 곧 개체이기 때문에, 엔티티에 개체가 추가되는 규칙도 식별자에게 달려있다는 말이 이해될거예요. 모델링에서 식별자의 중요성을 이제 아시겠죠? 식별자를 정의하는 것은 곧 엔티티를 정의하는 것과 같습니다.
 
<주문>
order_id
customer_id
product_id
ordered_at
1
1114
51689
2023-02-05 12:59:40
100
1115
51689
2024-03-05 14:30:01
101
1114
51689
2024-03-05 15:58:00
 
 

유연함을 가진 속성 모델링

속성과 관련해 유연성에 대해서 얘기하고 속성 모델링 파트를 마무리해보려고 합니다. 처음 한 모델링이 끝까지 변함없이 갔으면 하는 것이 모델러의 마음이지요. 그렇기 때문에 업무의 변화에도 모델은 쉽게 변하지 않도록 설계하려고 노력합니다. 하지만 현실적으로 요구사항과 모델은 조금씩 변합니다.
특히 컬럼을 추가할 필요가 생기는 것은 아주 흔한 요구사항입니다. 예를 들어 데이터리안의 웹사이트 사용자 테이블의 구조를 간략하게 살펴보겠습니다.
 
<사용자>
  • customer_id (PK)
  • name
  • email
  • phone_number
 
데이터리안 웹사이트는 카카오 로그인만 지원하기 때문에 email에 카카오 계정의 이메일이 들어갑니다. 그런데 설계 할 당시에는 생각하지 못했던 문제가 CS를 통해 들어오더라구요. 카카오 계정에 붙은 이메일은 잘 사용하지 않는 이메일이기 때문에, 안내 메일을 다른 메일 주소로 보내달라는겁니다. 현재 데이터베이스 구조상으로는 불가능한 요청이죠. 이 요구사항을 위해서는 ‘sub_email’라는 컬럼을 추가하는 방법 밖에 없을까요?
 
<사용자>
  • customer_id (PK)
  • name
  • email
  • sub_email — 이렇게 하는게 맞을까…?
  • phone_number
 
기존 테이블에 컬럼을 추가하는 것은 잘못 수행됐을 시 시스템 오류로도 이어질 수 있는 굉장히 까다로운 작업입니다. 모델이 만약 아래와 같이 생겼다면 새로운 컬럼을 추가하지 않고도 sub_email을 입력할 수 있게 됩니다.
 
<사용자>
  • customer_id (PK)
  • name
<사용자 연락처>
  • customer_id (PK, FK)
  • contact_type (PK)
  • contact_info
 
이해를 돕기 위해 테이블에 예시 데이터를 채워넣어보겠습니다. email, sub_email, phone_number를 컬럼이 아니라 행으로 관리할 수 있게 됩니다. 그러면 모델의 구조 수정 없이, 새로운 데이터를 넣어주는 것 만으로 문제를 해결할 수 있게 되죠.
 
<사용자>
customer_id
name
10987
윤선미
 
<사용자 연락처>
customer_id
contact_type
contact_info
10987
email_kakao_account
sunmi@datarian.io
10987
phone_number
070-4506-4566
10987
email_notification
rian.analyst@gmail.com
 
이 문제 해결 방식이 명약이라는 것은 아닙니다. 이렇게 데이터 구조를 만들게 되면 데이터 표준화를 하기도 어렵고(누구는 phone_number라고 데이터를 입력하고, 누구는 contact_number라고 입력하는 상황을 상상해보세요), 쿼리를 작성하는 것도 이전 모델보다 복잡해집니다. 결국 사람은 컬럼 단위로 데이터를 보고싶어하기 때문에 이 데이터를 컬럼으로 펼치는 테이블 피봇 작업을 해주어야 하거든요.
이외에도 모델링이 유연성을 가지기 위해서 할 수 있는 방법과 그에 따른 장단점은 굉장히 다양합니다. 좀 더 자세하게 알고 싶다면 『프로젝트 성패를 결정짓는 데이터 모델링 이야기』 15장을 추천합니다.
 

실무의 매운맛: JSON Data Type

사실 제가 실무에서 만났던 흔한 속성 유연성을 만드는 방법은 데이터를 JSON(JavaScript Object Notation)으로 넣는거였어요. MySQL 및 데이터베이스들이 JSON Type을 지원하기 때문입니다. (MySQL 공식 문서: 13.5 The JSON Data Type) 아래 테이블에서 event_params 컬럼은 특정 이벤트의 파라미터 값을 담고 있습니다. page_view라는 이벤트는 page_location이라는 파라미터를 가지고 있고, scroll이라는 이벤트는 page_location, scroll이라는 파라미터를 가지는 등 각 이벤트마다 가지는 파라미터가 다르기 때문에 이에 대처하기 위해 page_location 등 파라미터를 테이블의 컬럼으로 만들지 않고 JSON의 {key, value} 쌍으로 만들어 저장하는 것이지요.
 
event_date
event_timestamp
event_name
event_params
20231204
1701684701833010
page_view
{’page_location’: ‘https://datarian.io/lecture’}
20231204
1701684701833010
scroll
{’page_location’: ‘https://datarian.io/lecture’, ‘scroll’: 90}
 
값을 담을 때야 편할 수 있지만, 사실 분석가 입장에서 달가운 방법은 아니었습니다. event_params에 있는 값을 꺼내려면 json_extract() 같은 전용 처리 함수(MySQL 공식 문서: 14.17.3 Functions That Search JSON Values)를 사용해야 하기 때문입니다. 사실 모델링에 대해서 잘 모를 때에는 ‘뭐 이런 식으로 쌓아놓은 데이터가 다 있나’ 너무 답답하기만 했어요. 위에 예시를 들어놓은 것은 간단하지만 실제로 만난 JSON 데이터들은 데이터 한 개의 값이 몇 줄을 넘어가기도 했거든요. JSON Formatter를 사용하지 않고는 읽을 수 없을 정도로 복잡한 것들이 많았습니다. 지금은 분석을 하자니 답답한건 마찬가지이지만 데이터를 설계한 사람의 마음은 이해가 갑니다. 그래도 무작정 답답함보다는 이해가 가는 답답함이 조금 더 나은 것이 아닐까요 😊 이 방법은 자주 조회해서 보지 않지만 저장할 필요성이 있는 데이터에만 권장하는 방법입니다.
최악의 경우로 무늬만 JSON인 텍스트가 들어가 있는 것을 본 적이 있는데요. 그런 데이터가 대량으로 쌓여있다면 전처리를 하느라 데이터 분석은 못 한다고 봐야합니다. 사용하지 못 할 데이터가 저장 공간에 자리만 차지하고 있으니 쓰레기가 쌓여있는 것과 같습니다.
 

Google BigQuery의 속성 유연성

속성 유연성의 필요성을 잘 체감할 수 있는 데이터가 위에서 예시로 든 page_view, click 등 사용자 행동 데이터인데요. 이벤트마다 함께 봐야하는 파라미터가 다를 뿐더러 때에 따라 새로운 파라미터를 추가해서 볼 필요도 생기기 때문입니다.
Google BigQuery는 이런 사용자 행동 데이터 수집 및 분석 도구인 Google Analytics 로그를 잘 처리하기 위해 속성 유연성을 잘 대비해놓은 데이터 구조를 가지고 있습니다. Google의 방법이 궁금하신 분들은 아래 글을 참고해주세요.
 
 

마무리

이 글을 통해 JSON 데이터 타입에 대해 미운 마음을 조금 내려놓는 계기가 되었다면 더할나위 없이 뿌듯할 것 같습니다. 제가 데이터 분석가로써 모델링 공부를 하면서 가장 큰 깨달음을 얻은 부분이었거든요. ‘도대체 왜 이렇게 하는거지?’에 대해 알고나니 원망을 약간은 덜 하게 되었달까요.
이외에도 속성에 대해서 다양한 이야기들을 해보았는데 이 글을 계기로 속성에 대해 조금 더 넓은 시야를 가지게 되었길 바랍니다. 특히 모델링을 직접 하고 있다면 꼭 알아두어야하는 데이터 표준화, 식별자, 유연성에 대해 실무 이야기를 담았으니 도움이 되었으면 좋겠습니다. 글이 도움이 된 사연이나, 내용에 대해 정정할 사항이 있다면 contact@datarian.io로 메일 보내주세요.
윤선미데이터 분석가

어느새 7년차 데이터 분석가이고, 4년째 데이터 분석 교육을 하고 있습니다. 데이터리안 멤버들과 함께 일하면서 데이터의 힘을 더 믿게 되었습니다.

함께 읽어보면 좋은 글

주식회사 데이터리안