B-3: 관계와 식별자 — 선을 잇는 법과, 한 줄을 가리키는 법
목차 29
안녕하세요, 데이터베이스의 세계를 함께 걷는 홍순구 튜터입니다. 다시 만나서 반가워요.
지난 시간엔 다섯 박스 — Member·Post·Comment·Like·Follow — 의 속살을 종류·이름·속성·도메인까지 채웠죠. 회원은 유형·기본·독립 엔터티고, 속성은 기본·설계·파생으로 나뉘며, 각 속성엔 자료형·길이·제약이라는 값의 범위가 붙는다는 데까지 왔어요. 종이 위 인스타그램 설계도가 제법 또렷해졌습니다.
그런데 마지막에 제가 두 가지를 "다음 시간"으로 슬쩍 미뤄뒀어요. 하나는 Like와 Follow의 식별자가 한 속성이 아니라 두 속성을 묶은 모습이었던 것. 또 하나는 독립·종속을 가르면서 "식별자를 스스로 갖느냐, 부모에게서 빌려오느냐"가 기준이라고만 말하고 넘어간 것. 오늘은 미뤄둔 이 둘을 정면으로 마주합니다.
오늘의 주제는 딱 두 단어예요. 관계와 식별자. 관계는 박스와 박스를 잇는 '선'을 어떻게 긋느냐의 이야기고, 식별자는 한 박스 안에서 행 하나를 콕 집어 가리키는 '한 줄'을 무엇으로 정하느냐의 이야기예요. 선을 긋는 법과, 행을 가리키는 법. 이 둘이 맞물리면 종이 설계도가 곧장 진짜 표로 내려갈 준비를 마칩니다.
미리 귀띔하면, 오늘은 SQLD 1과목에서 매 회차 빠지지 않는 ★빈출 영역이에요. 특히 주식별자가 갖춰야 할 네 가지 조건과, 식별 관계·비식별 관계를 가르는 문제는 단골 중의 단골이죠. 그러니 오늘은 시험을 염두에 두고 조금 더 단단히 짚어갈게요.
오늘의 여정 — 선을 잇고, 한 줄을 가리키기
① 선을 어떻게 긋나 — 관계와 차수(1:1 · 1:N · M:N)
② 꼭 있어야 하나 — 선택성(필수 참여 · 선택 참여)
③ 그물을 푸는 법 — M:N 관계를 푸는 법(교차 엔터티)
④ 무엇으로 가리키나 — 주식별자와 네 가지 조건 ★빈출
⑤ 타고난 키, 만든 키 — 본질 식별자 · 인조 식별자
⑥ 하나로, 둘을 묶어 — 단일 식별자 · 복합 식별자
⑦ 빌려와 내 키로 — 식별 관계 · 비식별 관계 ★빈출
⑧ 인스타그램 다시 — 관계와 식별자로 ERD 완성
│
▼
다섯 박스를 잇는 '선'과, 각 박스를 가리키는 '한 줄' 확정
종이와 펜은 지난 시간 그대로 들고 오시면 됩니다. 오늘도 SQL을 새로 짜는 시간이 아니라, 표로 내려가기 직전의 설계를 마지막으로 다듬는 시간이에요.
💡 오늘 수업의 핵심 — "엔터티를 잇는 관계를 차수·선택성으로 읽고, 한 행을 가리키는 주식별자의 네 조건과 종류를 익히며, 식별·비식별 관계로 둘을 맞물린다"
🎯 학습 목표
- 관계의 차수(1:1·1:N·M:N)와 선택성(필수·선택 참여)을 읽고, M:N 관계를 교차 엔터티로 풀 수 있다.
- 주식별자의 네 가지 조건(유일·최소·불변·존재)을 설명하고, 식별자를 본질·인조 / 단일·복합으로 구분할 수 있다.
- 식별 관계와 비식별 관계의 차이를 이해하고, SQLD 1과목 관계·식별자 출제 포인트를 정리할 수 있다.
Step 1: "선을 어떻게 긋나 — 관계와 차수(1:1·1:N·M:N)"
지난 시간 ERD를 그릴 때 박스끼리 선으로 이었던 것, 기억나시죠? 그 선이 바로 관계예요. 관계는 보통 동사로 읽어요. "회원이 게시물을 작성한다", "게시물이 댓글을 가진다"처럼요. 박스가 등장인물이라면, 관계는 그 인물들이 주고받는 행동인 셈이에요.
그런데 선을 하나 그었다고 끝이 아니에요. "회원 한 명이 게시물을 몇 개나 가질 수 있나?"를 함께 정해야 하죠. 이걸 차수(cardinality, 관계 차수)라고 불러요. 지난 시간엔 1:N, M:N 같은 이름만 슬쩍 보여드렸는데, 오늘 정식으로 셋을 구분합니다.
관계의 차수 — 한쪽 1행이 반대쪽 몇 행과 이어지나
1 : 1 회원 1명 ── 상세설정 1개 (드묾 — 보통 한 표로 합침)
1 : N 회원 1명 ──< 게시물 여럿 (가장 흔함)
M : N 회원 여럿 >──< 게시물 여럿 (그물 — 그대로는 표로 못 만듦)
곧은 선(─) = '1(하나)' 쪽 끝 · 까마귀발(<) = 'N(여럿)' 쪽 끝
선 끝의 모양으로 '하나'인지 '여럿'인지를 표시하는데, 새 발가락처럼 갈라진 이 모양(<)을 까마귀발이라고 불러요. 발이 닿은 쪽이 '여럿(N)'이라는 뜻이에요. 셋을 하나씩 볼게요.
- 1:1 — 한쪽 1행이 반대쪽 딱 1행과 이어져요. 회원 한 명에 상세설정 하나처럼요. 그런데 1:1은 실제로 드물어요. 둘이 늘 붙어 다니면 보통 한 표로 합쳐버리거든요.
- 1:N — 한쪽 1행이 반대쪽 여러 행과 이어져요. 회원 한 명이 게시물을 여러 개 올리는 게 딱 이거예요. 엄마 한 명에 자녀 여럿인 부모-자식 관계를 떠올리면 쉬워요. 인스타에서 가장 흔한 관계죠.
- M:N — 양쪽 다 여러 행과 얽혀요. 회원 여러 명이 게시물 여러 개에 좋아요를 누르는, 그물처럼 얽힌 관계요. 이건 그대로는 표로 못 만들어서 따로 푸는 법이 있는데, Step 3에서 다룹니다.
⚠️ 함정 — 차수는 "최대 몇 행과 이어지나"를 봅니다. 회원이 지금 게시물을 한 개만 올렸어도, 더 올릴 수 있으니 Member–Post는 1:N이에요. 지금 당장의 개수가 아니라 '가능한 최대'로 읽으세요.
💡 한 줄 정리
관계는 박스를 잇는 선이고, 차수(1:1·1:N·M:N)는 한쪽 1행이 반대쪽 몇 행과 이어지는지를 말한다. 인스타는 1:N(회원–게시물)이 흔하고, M:N(좋아요·팔로우)은 Step 3에서 따로 푼다.
🙋 학생 질문 — "튜터님, 1:1 관계면 그냥 한 테이블로 합치면 되지 않나요?"
날카로운 질문이에요. 대부분은 맞아요 — 그래서 1:1이 드문 거예요. 회원과 상세설정이 항상 1:1로 붙어 다닌다면 굳이 둘로 나눌 이유가 없죠.
그래도 일부러 1:1로 나누는 경우가 있어요. 첫째, 한쪽 정보가 극소수 행에만 있을 때. 예를 들어 회원 중 1%만 갖는 특별한 정보를 본 표에 넣으면 나머지 99% 칸이 비어버리니, 따로 떼는 게 깔끔하죠. 둘째, 민감한 정보(비밀번호 같은)를 보안상 분리하고 싶을 때예요. 이런 특별한 이유가 없으면 합치는 게 보통이에요.
Step 2: "꼭 있어야 하나 — 선택성(필수 참여·선택 참여)"
차수로 "몇 행과 이어지나"는 정했어요. 그런데 한 가지가 더 남았어요. "꼭 이어져야 하나, 안 이어져도 되나?"예요. 지난 시간에 더미 데이터를 보다가, 게시물을 한 개도 안 올린 회원(7번·8번)이 있었던 것 기억하세요? 그 회원은 게시물과 이어진 선이 아예 없었죠. 이렇게 "참여가 필수냐 선택이냐"를 따지는 게 선택성(optionality)이에요.
선택성 — 그 관계에 '꼭' 참여해야 하나
Member ──○──< Post
회원 → 게시물 : 선택 참여(○) — 게시물 0개인 회원도 회원이다
게시물 → 회원 : 필수 참여(│) — 작성자 없는 게시물은 존재할 수 없다
표기: ○(동그라미) = 선택(최소 0) · │(곧은 선) = 필수(최소 1)
재미있는 건, 선택성은 양쪽이 따로따로 정해진다는 거예요. 같은 Member–Post 선 하나를 두고도, 회원 쪽에서 보면 게시물은 선택(0개여도 됨)이고, 게시물 쪽에서 보면 회원은 필수(작성자가 반드시 있어야 함)예요. 한 선의 양 끝이 서로 다른 성격일 수 있는 거죠.
동호회로 비유해 볼게요. 회원은 모임에 한 번도 안 나가도 여전히 회원이에요(모임 참여는 선택). 하지만 모임 사진에 찍힌 사람은 반드시 실제 인물이어야 하죠(사람 없는 사진은 없음, 필수). 같은 '회원-모임' 관계인데 방향에 따라 필수와 선택이 갈려요.
⚠️ 함정 — 차수와 선택성은 다른 축이에요. "1:N"은 개수(몇 개)를 말하고, 선택성은 "최소 0개냐 최소 1개냐"를 말해요. 둘 다 1:N이어도 한쪽은 "최소 0", 다른 쪽은 "최소 1"일 수 있어요. 시험에서 이 둘을 섞어 묻습니다.
💡 한 줄 정리
선택성은 관계에 꼭 참여해야 하는지(필수, 최소 1)를, 안 해도 되는지(선택, 최소 0)를 가른다. 차수(개수)와는 별개의 축이며, 한 선의 양 끝이 서로 다를 수 있다.
🙋 학생 질문 — "튜터님, 게시물 0개인 회원도 회원인가요? 그럼 관계가 없는 거 아닌가요?"
맞아요, 지금은 이어진 선이 없죠. 하지만 그건 "관계가 없다"가 아니라 "선택 참여라서 아직 0개일 뿐"이에요. 관계의 정의(회원은 게시물을 올릴 수 있다)는 그대로 살아 있고, 그 회원이 나중에 게시물을 올리면 곧장 1:N 관계로 채워져요.
설계할 때 이게 중요해요. 만약 회원 쪽을 '필수'로 잘못 정해버리면, 게시물을 아직 안 올린 신규 회원은 가입조차 못 하게 막혀요. "최소 0개를 허용하느냐"는 이렇게 실제 동작을 바꾸는 결정이에요.
Step 3: "그물을 푸는 법 — M:N 해소(교차 엔터티)"
Step 1에서 M:N은 "그대로는 표로 못 만든다"고 했죠. 왜 그런지부터 짚을게요. 지난 시간에 한 칸에는 값 하나만 넣는다고 배웠어요(다중값은 쪼갠다고 했던 것 기억하시죠?). 그런데 "이 게시물에 좋아요 누른 회원 목록"을 게시물 표의 한 칸에 다 적으려면, 한 칸에 회원 여러 명을 욱여넣어야 해요. 규칙 위반이죠.
그래서 M:N은 가운데에 다리를 놓아 풀어요. 두 박스 사이에 새 박스(연결 엔터티, 교차 엔터티)를 세우고, M:N 하나를 1:N 둘로 쪼개는 거예요.
M:N 을 그대로 두면 — 표로 만들 수 없다
Member >────< Post "회원 여럿이 게시물 여럿을 좋아요"
가운데에 연결 엔터티 Like 를 세운다
Member ──< Like >── Post
Member ── Like : 1:N (회원 1명이 좋아요를 여러 번)
Post ── Like : 1:N (게시물 1개에 좋아요가 여러 개)
이제 좋아요 하나하나가 Like 표의 한 행이 돼요. "3번 회원이 7번 게시물에 좋아요"는 (3, 7) 한 행, "5번 회원이 7번 게시물에 좋아요"는 (5, 7) 또 한 행. 한 칸에 욱여넣을 필요가 사라졌죠. 지난 시간 미뤄둔 좋아요·팔로우의 M:N이 이렇게 풀립니다.
팔로우도 똑같아요. 다만 팔로우는 회원과 회원 사이의 M:N이라 좀 특별해요(같은 엔터티끼리 얽히는 자기참조). Member ──< Follow >── Member로, 가운데 Follow가 "누가(follower) 누구를(following) 팔로우하는가" 한 쌍씩을 담아요.
비유하면 동창회 참석 기록 같아요. "누가 어느 모임에 갔는지"는 사람 명단의 한 칸에도, 모임 명단의 한 칸에도 못 적어요. 사람과 모임을 잇는 별도의 '참석 기록' 장부가 필요하죠. Like와 Follow가 바로 그 장부예요.
🌟 연결 엔터티는 보통 두 부모의 식별자를 한데 묶어서 자기 식별자로 삼아요. Like라면
(member_id, post_id)처럼요. 이 "두 개를 묶은 식별자"가 곧 Step 6에서 다룰 복합 식별자예요.
💡 한 줄 정리
M:N은 한 칸에 여러 값을 넣을 수 없어 그대로는 표가 안 된다. 가운데 연결(교차) 엔터티를 세워 1:N + 1:N으로 풀고, 그 엔터티의 한 행이 "누가 무엇을" 한 쌍을 담는다. 좋아요·팔로우가 이렇게 풀린다.
🙋 학생 질문 — "튜터님, 왜 한 칸에 여러 개를 못 넣나요? 좋아요 누른 사람 목록을 게시물에 적으면 안 되나요?"
넣을 수야 있어요. "3,5,9,12" 처럼 문자열로 욱여넣는 거죠. 그런데 그 순간 표가 표답지 않아져요. "9번 회원이 좋아요를 취소했다"를 처리하려면 그 긴 문자열을 뜯어고쳐야 하고, "좋아요를 가장 많이 받은 게시물"을 세려면 칸 안의 쉼표를 일일이 세야 해요. 회원이 늘면 칸이 끝없이 길어지고요.
연결 엔터티로 풀면 좋아요 하나가 한 행이라, 추가는 한 행 삽입, 취소는 한 행 삭제, 집계는 행 세기로 깔끔하게 끝나요. "한 칸엔 값 하나"라는 규칙을 지키는 게 결국 우리를 편하게 해주는 거예요.
Step 4: "무엇으로 가리키나 — 주식별자와 네 가지 조건" ★빈출
여기서부터는 선이 아니라 박스 안으로 들어가요. 한 박스(표) 안에 행이 수십, 수백 개 쌓였을 때, 그중 딱 한 행을 콕 집어 가리키려면 무엇이 필요할까요? 지난 시간 기본키 이야기로 살짝 맛봤던 그것, 바로 식별자예요.
식별자가 여러 개일 수도 있어요. 회원을 가리키는 데 member_id도 쓸 수 있고, 겹치지 않는다면 username으로도 가리킬 수 있죠. 이때 대표로 내세워 쓰는 하나를 주식별자(primary identifier), 나머지 후보를 보조식별자라고 해요. 우리는 member_id를 주식별자로 정합니다.
그럼 아무 속성이나 주식별자가 될 수 있을까요? 아니에요. 주식별자가 되려면 네 가지 조건을 모두 만족해야 해요. 이게 SQLD 1과목 단골 출제 포인트라 하나씩 또렷이 짚을게요.
| 조건 | 뜻 | ✅ 좋은 예 | ❌ 나쁜 예 |
|---|---|---|---|
| 유일성 | 값이 겹치지 않는다 | member_id 1, 2, 3… |
이름(동명이인 가능) |
| 최소성 | 꼭 필요한 속성만 묶는다 | member_id 하나로 충분 |
member_id+nickname |
| 불변성 | 한 번 정하면 안 바뀐다 | member_id 평생 고정 |
휴대폰번호(바뀜) |
| 존재성 | 비어 있으면(NULL) 안 된다 | 항상 값이 있음 | 선택 입력 속성 |
네 글자로 유일·최소·불변·존재, 입에 붙여두면 시험장에서 바로 떠올라요. 하나씩 풀어볼게요.
- 유일성 — 같은 값이 두 행에 있으면 둘을 구분 못 해요. 이름은 동명이인이 있으니 탈락이죠.
- 최소성 — 꼭 필요한 속성만 묶어요.
member_id하나로 충분한데nickname까지 묶으면 군더더기예요. 복합 식별자(Step 6)에서 특히 중요해요. - 불변성 — 값이 바뀌면 그 행을 가리키던 다른 모든 곳이 한꺼번에 어긋나요. 그래서 휴대폰번호·이메일처럼 바뀔 수 있는 값은 주식별자로 위험해요.
- 존재성 — 비어 있는(NULL) 값으로는 행을 가리킬 수 없어요. 주식별자는 언제나 채워져 있어야 해요.
비유하면 학번이나 사번 같아요. 안 겹치고(유일), 군더더기 없이 그것 하나로 학생을 가리키고(최소), 졸업할 때까지 안 바뀌고(불변), 모든 학생이 반드시 갖고 있죠(존재).
⚠️ 함정 — "이메일을 주식별자로 쓰면?"이라는 문제가 자주 나와요. 이메일은 유일·존재는 만족해도 바뀔 수 있어서 불변성에서 탈락이에요. 휴대폰번호도 마찬가지죠. 네 조건 중 하나라도 어기면 주식별자가 될 수 없어요. 바로 이 약점 때문에 다음 Step의 인조 식별자가 등장합니다.
💡 한 줄 정리
주식별자는 한 행을 대표로 가리키는 식별자이며, 유일·최소·불변·존재 네 조건을 모두 만족해야 한다. 하나라도 어기면(이름=유일성 ✗, 이메일=불변성 ✗) 주식별자가 될 수 없다.
🙋 학생 질문 — "튜터님, 이름은 왜 주식별자가 안 되나요?"
이름은 유일성에서 막혀요. '김민지'라는 회원이 둘 이상 있으면, 이름만 보고는 어느 김민지인지 가릴 수 없죠. 한 명만 있을 때조차도 "지금 한 명뿐"인 거지 "앞으로도 한 명"이라는 보장이 없어요. 식별자는 '지금'이 아니라 '언제나' 유일해야 해요.
게다가 이름은 바뀔 수도 있어요(개명). 그러면 불변성까지 위반이죠. 유일성·불변성 두 군데서 막히니, 이름은 주식별자 후보에서 일찌감치 빠집니다.
Step 5: "타고난 키, 만든 키 — 본질 식별자·인조 식별자"
방금 봤듯이 이메일·휴대폰번호는 바뀔 수 있어서 주식별자로 위험했어요. 그런데 인스타에서 회원을 가리키는 데 쓸 만한 '자연스러운' 값은 대부분 이런 약점을 안고 있어요. 그래서 우리는 슬쩍 다른 방법을 써왔죠. member_id 1, 2, 3… 처럼 그냥 번호를 새로 매기는 거예요. 식별자에는 이렇게 출신이 다른 두 종류가 있어요.
같은 회원을 가리키는 두 종류의 키
본질 식별자 업무에서 자연스럽게 생긴 값으로 가리킴
예) username 'jaehoon', 이메일, 주민번호
길거나 · 바뀔 수 있거나 · 여러 개를 묶어야 할 때가 있음
인조 식별자 오로지 식별하려고 인위적으로 매긴 일련번호
예) member_id 1, 2, 3 … (자동으로 1씩 늘려가며)
짧고 · 안 바뀌고 · 그 대신 값 자체엔 의미가 없음
본질 식별자(natural identifier)는 업무에 원래 존재하던 값이에요. 주민번호, 이메일, username 같은 것들요. 사람이 만들어낸 게 아니라 업무 세계에 자연스럽게 있던 값이죠. 지난 시간 배운 분류로 치면 '기본 속성'에 가까워요.
인조 식별자(artificial identifier)는 오로지 식별하려고 우리가 인위적으로 붙인 번호예요. member_id 1, 2, 3처럼요. 지난 시간에 봤던 '설계 속성'(member_id 같은 일련번호)이 바로 이거예요. 업무엔 원래 없던, 우리가 만든 키죠. 비유하면 본질 식별자는 타고난 지문이고, 인조 식별자는 회사에서 발급받은 사번이에요.
왜 굳이 번호를 또 만들까요? 본질 식별자는 길거나(주민번호 13자리), 바뀔 수 있거나(이메일), 여러 속성을 묶어야 해서 다루기 불편할 때가 많거든요. 인조 식별자는 짧고 절대 안 바뀌니 주식별자의 네 조건을 거뜬히 통과해요. 다만 번호만 봐선 누구인지 알 수 없다는 게 약점이죠.
🌟 식별자를 만든 위치로도 나눠요.
member_id처럼 자기 박스 안에서 직접 만든 건 내부 식별자, 다른 박스의 식별자를 받아와 쓰는 건 외부 식별자예요. 좋아요·팔로우가 부모의 키를 받아오는데, 이건 Step 7에서 정식으로 다룹니다.
💡 한 줄 정리
본질 식별자는 업무에 원래 있던 값(이메일·username)으로, 인조 식별자는 식별하려고 인위적으로 매긴 일련번호(member_id)로 가리킨다. 본질은 의미가 있지만 약점이 있고, 인조는 짧고 안 바뀌는 대신 값 자체엔 의미가 없다.
🙋 학생 질문 — "튜터님, 그냥 본질 식별자(이메일)를 쓰면 안 되나요? 왜 굳이 번호를 또 만들어요?"
쓸 수는 있어요. 하지만 이메일을 주식별자로 삼으면, 회원이 이메일을 바꾸는 순간 그 회원을 가리키던 모든 곳(좋아요·댓글·팔로우 기록)이 한꺼번에 어긋나요. 불변성을 어긴 대가죠.
그래서 보통은 절대 안 바뀌는 인조 번호(member_id)를 주식별자로 두고, 이메일에는 "겹치면 안 된다"는 약속만 따로 걸어둬요. 이렇게 하면 이메일이 바뀌어도 member_id는 그대로라 아무 데도 안 흔들리죠. 본질 식별자를 버리는 게 아니라, 역할을 나눠 맡기는 거예요.
Step 6: "하나로, 둘을 묶어 — 단일 식별자·복합 식별자"
지난 시간 마지막에 제가 남겨둔 수수께끼 하나, 기억하세요? Member·Post·Comment는 식별자가 member_id·post_id처럼 하나였는데, Like와 Follow만 식별자가 두 속성을 묶은 모습이었죠. 오늘 그 수수께끼를 풉니다. 식별자는 속성을 몇 개 묶느냐로도 나뉘어요.
한 행을 가리키는 데 속성이 몇 개 필요한가
단일 식별자 속성 하나로 충분
Member → member_id
복합 식별자 둘 이상을 묶어야 가리킴
Like → (member_id, post_id) "회원 3번이 게시물 7번을"
Follow → (follower_id, following_id) "회원 3번이 회원 9번을"
단일 식별자는 속성 하나로 행을 가리켜요. member_id 하나면 회원 한 명이 정해지죠. 가장 흔한 형태예요.
복합 식별자는 둘 이상을 묶어야 비로소 한 행이 정해져요. Like를 볼까요? member_id만으론 그 회원이 누른 좋아요가 전부 걸려요. post_id만으론 그 게시물이 받은 좋아요가 전부 걸리고요. (member_id, post_id) 둘을 묶어야 "3번 회원이 7번 게시물에 누른 그 좋아요" 하나가 콕 집혀요. Step 3에서 연결 엔터티가 두 부모의 키를 묶어 쓴다고 했던 게 바로 이거예요.
비유하면 단일 식별자는 학번 하나로 학생을 찾는 거고, 복합 식별자는 "몇 학년 몇 반 몇 번"처럼 여러 개를 묶어야 한 사람이 정해지는 거예요. 어느 하나만으론 부족하죠.
⚠️ 함정 — 복합 식별자는 묶인 속성 전부가 있어야 유일해요.
(member_id, post_id)중member_id하나만 떼면 그 회원의 좋아요가 줄줄이 걸려 유일성이 깨져요. 그리고 Step 4의 최소성 기억하시죠? 복합키엔 꼭 필요한 속성만 묶어요.(member_id, post_id)로 충분한데created_at까지 끼워 넣으면 최소성 위반이에요.
💡 한 줄 정리
단일 식별자는 속성 하나로(member_id), 복합 식별자는 둘 이상을 묶어야((member_id, post_id)) 한 행을 가리킨다. 복합키는 묶인 속성 전부가 있어야 유일하며, 최소성에 따라 꼭 필요한 속성만 묶는다. 좋아요·팔로우가 복합 식별자다.
🙋 학생 질문 — "튜터님, 복합 식별자는 속성이 많아 불편할 것 같은데, 왜 번호 하나로 안 만들고요?"
좋은 직감이에요. 실제로 그렇게도 해요. Like 대신 like_id 1, 2, 3 같은 인조 번호 하나를 새로 매겨 단일 식별자로 쓸 수도 있죠. 그럼 다루기는 간편해져요.
대신 그렇게 하면 "한 회원이 같은 게시물에 좋아요를 두 번" 누르는 걸 막아주는 장치가 사라져요. 복합키 (member_id, post_id)는 같은 쌍이 두 번 들어올 수 없으니 중복이 저절로 막히는데, like_id 번호만 두면 (3, 7)이 여러 번 들어와도 번호만 다르니 통과해버리거든요. 이 "복합키 vs 인조키" 줄다리기는 Step 8에서 팔로우를 예로 더 자세히 비교할게요.
Step 7: "빌려와 내 키로 — 식별 관계·비식별 관계" ★빈출
이제 오늘의 두 주제가 만나요. Step 1~3의 관계와 Step 4~6의 식별자가 맞물리는 지점이거든요. 그리고 여기가 지난 시간 제가 미뤄둔 마지막 수수께끼 — "독립이냐 종속이냐가 식별자를 스스로 갖느냐, 부모에게서 빌려오느냐로 갈린다"고 했던 그 이야기예요. 이것도 SQLD 단골이라 천천히 갑니다.
자식 엔터티가 부모의 식별자를 받아올 때(이렇게 받아온 키를 외래키, FK라고 해요), 그 받아온 키를 어디에 넣느냐로 관계가 둘로 갈려요.
부모 키를 받아와 — 어디에 넣느냐로 갈린다
비식별 — Post (부모 키를 '일반 칸'에)
post_id ← 주식별자 (자기 것)
member_id ← 외래키, 일반 칸 (작성자 참조)
caption
→ Post 는 작성자가 누구든 post_id 로 스스로 가리킨다 (약한 종속, 점선)
식별 — Like (부모 키를 '주식별자 안으로')
member_id ┐
post_id ┘ ← 둘을 묶은 주식별자 (Member·Post 에게서 빌려옴)
created_at
→ Like 는 회원·게시물 없이는 식별 자체가 불가능하다 (강한 종속, 실선)
비식별 관계부터 볼게요. Post는 작성자인 member_id를 부모 Member에게서 받아와요. 하지만 그건 그냥 '일반 칸'(외래키)에 둘 뿐이고, Post의 주식별자는 따로 가진 post_id예요. 그래서 Post는 작성자가 누구든 post_id 하나로 스스로를 가리킬 수 있어요. 부모에 기대긴 하지만 식별은 독립적이죠. 이런 약한 종속을 비식별 관계라 하고, ERD에서 보통 점선으로 그려요.
식별 관계는 달라요. Like는 부모 Member·Post에게서 받아온 member_id·post_id를 일반 칸이 아니라 자기 주식별자 안으로 끌어들여요. 그래서 Like는 부모 둘이 없으면 식별 자체가 안 돼요. 부모에게 완전히 기댄 강한 종속이고, ERD에서 보통 실선으로 그려요. 지난 시간 말한 "식별자를 빌려와 내 키로 쓴다"가 바로 이거예요.
비유해 볼게요. 비식별은 자식이 자기 주민번호가 따로 있고 부모는 비상연락처로만 적어두는 것과 같아요. 부모가 바뀌어도 자식은 자기 번호로 멀쩡히 존재하죠. 식별은 자식 이름표가 "김씨 집 셋째"인 것과 같아요. 부모(김씨 집)를 빼면 '셋째'만으론 누구인지 가릴 수 없어요.
⚠️ 함정 — 핵심은 "부모 키를 받아오느냐"가 아니라 "받아온 키를 주식별자에 넣었느냐"예요. Post도
member_id를 받아오지만 일반 칸에 두니 비식별, Like는 주식별자 안에 넣으니 식별이에요. 받아온 것만 보지 말고, 그게 주식별자의 일부인지를 보세요. 시험에서 ERD의 실선/점선이 이걸 묻습니다.
💡 한 줄 정리
부모 키를 받아와 일반 칸(외래키)에만 두면 비식별 관계(자식이 자기 주식별자를 따로 가짐, 약한 종속, 점선), 주식별자 안으로 끌어들이면 식별 관계(부모 없이는 식별 불가, 강한 종속, 실선)다. Post=비식별, Like·Follow=식별.
🙋 학생 질문 — "튜터님, Post도 member_id를 가져오는데 왜 식별 관계가 아닌가요?"
바로 그 점이 이 Step의 핵심이에요. 가져오느냐가 아니라, 가져온 걸 어디에 두느냐가 기준이거든요.
Post는 member_id를 받아오지만 그건 일반 칸일 뿐이고, Post의 주식별자는 따로 가진 post_id예요. 그래서 작성자 정보 없이도 post_id만으로 게시물을 가리킬 수 있죠. 반면 Like는 받아온 member_id·post_id가 곧 자기 주식별자라, 부모를 빼면 가리킬 방법이 없어요. 같은 "받아오기"라도 주식별자에 넣었는지 아닌지가 식별과 비식별을 가릅니다.
Step 8: "인스타그램 다시 — 관계와 식별자로 ERD 완성"
오늘 배운 걸 다섯 박스에 전부 적용해 볼 차례예요. 관계엔 차수·선택성·식별/비식별을, 박스엔 주식별자의 종류를 채우면, 지난 시간의 설계도가 한 단계 더 단단해져요.
다섯 박스를 잇는 선 — 차수 · 식별/비식별
Member 1 ──< Post 1:N · 비식별 (post_id 가 PK, member_id 는 일반 FK)
Post 1 ──< Comment 1:N · 비식별 (comment_id 가 PK, post_id 는 일반 FK)
Member >──< Post M:N → Like · 식별 (PK = member_id + post_id)
Member >──< Member M:N → Follow · 식별 (PK = follower_id + following_id)
규칙이 보이죠? Member에 그냥 매달린 Post·Comment는 자기 번호(post_id·comment_id)를 갖는 비식별이고, M:N을 풀어 만든 Like·Follow는 부모 키를 묶어 쓰는 식별이에요. 이걸 박스 안 식별자 종류까지 적으면 지난 시간 명세표가 이렇게 자라요.
| 엔터티 | 주식별자 | 식별자 특성 |
|---|---|---|
| Member | member_id |
인조 · 단일 |
| Post | post_id |
인조 · 단일 (member_id는 비식별 FK) |
| Comment | comment_id |
인조 · 단일 (post_id는 비식별 FK) |
| Like | (member_id, post_id) |
복합 · 식별(부모 키를 빌려옴) |
| Follow | (follower_id, following_id) |
복합 · 식별(자기참조) |
마지막으로 설계 결정 하나를 같이 고민해 볼게요. 팔로우를 가리키는 방법은 사실 두 가지예요. Step 6 끝의 질문에서 살짝 예고했던 그 줄다리기죠.
Follow 를 어떻게 가리킬까 — 두 가지 설계
(가) 복합키 주식별자 = (follower_id, following_id)
· "누가 누구를" 한 쌍이 곧 키 → 같은 팔로우 중복이 원천 차단
· 식별 관계, 의미가 그대로 드러남
· SQLD 가 보는 정석
(나) 인조키 주식별자 = follow_id (1, 2, 3… 새로 매긴 단일 번호)
· 참조가 짧고 단순해짐
· 단, 중복 팔로우 차단은 (follower_id, following_id) 에
유일 약속을 따로 걸어야 함
정답이 하나로 정해진 건 아니에요. 모델링 관점(그리고 SQLD)에선 (가) 복합키·식별 관계가 의미가 또렷해 정석으로 봐요. 실무에선 다루기 편한 (나) 인조키도 흔히 쓰는데, 대신 중복 방지 약속을 잊지 말아야 하죠. 중요한 건 "왜 그렇게 골랐는지" 트레이드오프를 설명할 수 있는 거예요.
이렇게 관계와 식별자까지 채우고 나면, 종이 설계도는 거의 완성이에요. 식별 관계로 묶인 이 키들이 나중에 두 표를 맞춰보는 조인(JOIN)으로 이어지는데, 그건 SQL을 배우는 C 카테고리에서 만나요.
💡 한 줄 정리
다섯 박스의 관계를 차수·식별/비식별로, 박스를 주식별자 종류로 채우면 설계도가 완성된다. Member–Post·Comment는 비식별, Like·Follow는 식별이며, Follow는 복합키(정석)와 인조키(편의) 사이에서 트레이드오프를 따져 고른다.
🙋 학생 질문 — "튜터님, 그래서 실무에선 복합키랑 인조키 중 뭘 더 많이 쓰나요?"
솔직히 말하면 둘 다 써요. 그래서 "무조건 이게 맞다"고 외우는 것보다, 상황에 맞게 고르고 그 이유를 대는 게 더 중요해요.
연결 엔터티가 단순하고 그 자체를 다른 곳에서 참조할 일이 거의 없으면, 복합키가 의미도 또렷하고 중복도 저절로 막혀 깔끔해요. 반대로 그 연결 엔터티에 또 다른 자식이 줄줄이 매달리거나 참조가 많아지면, 긴 복합키를 여기저기 끌고 다니는 게 번거로워서 짧은 인조키를 얹기도 하죠. 시험에선 (가) 복합키·식별 관계를 정석으로 보니 그쪽을 기준으로 잡아두면 됩니다.
마무리
오늘 수고 많으셨어요! 지난 시간까지는 박스 하나하나를 들여다봤다면, 오늘은 그 박스들을 잇는 선을 긋고, 박스마다 행을 가리키는 한 줄을 확정했어요. 종이 위 인스타그램 설계도가 이제 "어떤 박스가 어떻게 이어지고, 각 행을 무엇으로 가리키는가"까지 또렷해졌습니다.
오늘 배운 핵심 세 가지
- 💡 하나 — 관계는 차수(1:1·1:N·M:N)와 선택성(필수·선택 참여)으로 읽는다. M:N은 그대로 표가 안 되니 가운데 연결 엔터티를 세워 1:N + 1:N으로 푼다(좋아요·팔로우).
- 💡 둘 — 주식별자는 유일·최소·불변·존재 네 조건을 모두 만족해야 한다. 식별자는 본질·인조(타고난 값이냐 만든 번호냐), 단일·복합(속성 하나냐 여럿을 묶느냐)으로 나뉜다.
- 💡 셋 — 부모 키를 일반 칸에만 두면 비식별(점선), 주식별자 안으로 끌어들이면 식별(실선)이다. Member–Post·Comment는 비식별, Like·Follow는 식별이다.
다음 시간 예고
오늘로 표 뼈대는 거의 다 섰어요. 관계로 선을 긋고 식별자로 각 행을 가리킬 수 있게 됐으니까요. 그런데 한 가지 함정이 남아 있어요. 만약 욕심을 부려서 회원·게시물·댓글을 한 표에 몽땅 욱여넣으면 어떻게 될까요? 같은 회원 이름이 그 회원의 게시물 수만큼 줄줄이 중복되고, 회원이 닉네임을 한 번 바꾸면 그 중복된 칸을 전부 고쳐야 해요. 한 군데라도 놓치면 같은 회원이 두 이름을 갖는 모순이 생기고요.
이렇게 중복 때문에 생기는 골칫거리를 이상현상(삽입·갱신·삭제 이상)이라 불러요. 다음 시간엔 이 중복을 속성 사이의 의존 관계(함수 종속)를 따라 단계별(1NF·2NF·3NF·BCNF)로 풀어내는 정규화를 배웁니다. 식별자 4조건이 거기서 또 핵심 열쇠로 등장하니, 오늘 배운 유일·최소·불변·존재를 잘 챙겨두세요. 정규화 역시 ★빈출이라 단단히 준비하고 오시면 좋아요.
과제
오늘 배운 관계 차수·선택성·식별자를 직접 종이에 그려보는 과제예요. SQL을 짜는 게 아니라, ERD 표기와 식별자 설계를 손으로 익히는 게 목적이에요.
[기초] 새 관계를 차수·선택성으로 표기하기
인스타에 '스토리(Story)' 엔터티를 새로 추가한다고 해봐요. 스토리는 회원이 올리는, 24시간 뒤 사라지는 게시물이에요. Member와 Story의 관계를 (1) 차수(1:1·1:N·M:N 중 무엇인지), (2) 양쪽의 선택성(각 끝이 필수인지 선택인지)으로 적어보세요. 그리고 왜 그렇게 봤는지 이유를 한 줄씩 붙여보세요.
[응용] M:N을 연결 엔터티로 풀고 복합 식별자 적기
인스타 해시태그를 생각해 봐요. 게시물 하나에 해시태그가 여러 개 달리고, 같은 해시태그가 여러 게시물에 쓰여요. 즉 Post와 Hashtag는 M:N이죠. 이 관계를 (1) 가운데 연결 엔터티를 세워 1:N + 1:N으로 풀고, (2) 그 연결 엔터티의 주식별자(복합 식별자)를 어떤 속성들로 묶을지 적어보세요. (3) 이 관계가 식별 관계인지 비식별 관계인지도 판단해 보세요.
[심화] 같은 엔터티를 복합키 vs 인조키 두 안으로 설계하기
위 [응용]에서 만든 해시태그 연결 엔터티를 두 가지 방식으로 설계해 보세요. (가) 두 부모 키를 묶은 복합키를 주식별자로 쓰는 안, (나) 새 인조 번호 하나를 주식별자로 얹는 안. 두 안의 장단점을 주식별자 4조건과 '중복 방지'·'참조 편의' 관점에서 비교하고, 어느 쪽을 고를지 이유와 함께 정해보세요.
생각해볼 주제
1. 인조 식별자를 쓰면, 본질 식별자의 유일성은 누가 지키나
회원을 member_id 인조 번호로 가리키기로 했어요. 편하긴 한데, 그러면 이메일이나 username이 겹치는 걸 막아주던 장치가 사라지는 건 아닐까요? member_id만 다르면 같은 이메일을 가진 회원이 둘 들어올 수도 있잖아요. 인조 식별자를 주식별자로 쓸 때, 원래 본질 식별자가 갖던 '겹치면 안 된다'는 성질을 어떻게 지켜줘야 할지 생각해 보세요.
2. 식별 관계가 손자까지 내려가면 주식별자는 어디까지 길어질까
식별 관계는 부모 키를 자식 주식별자 안으로 끌어들이죠. 그런데 그 자식이 또 다른 자식(손자)의 부모가 되어 식별 관계로 이어지면, 손자의 주식별자는 조부모·부모의 키까지 전부 물려받아 점점 길어져요. 이렇게 복합키가 계속 자라는 게 왜 부담이 될지, 그리고 어느 지점에서 인조키로 끊어주는 게 나을지 생각해 보세요.
3. M:N을 꼭 연결 엔터티로 풀어야 할까
오늘 M:N은 한 칸에 여러 값을 못 넣어서 연결 엔터티로 푼다고 했어요. 그런데 만약 "그냥 한 칸에 목록을 넣으면(예: 좋아요 누른 회원 번호를 쉼표로 죽 나열) 안 되나?"라고 누가 묻는다면 어떨까요? 그렇게 했을 때 당장은 무엇이 편하고, 나중에 무엇을 잃게 될지를 따져보세요. 이 고민은 다음 시간 정규화로 곧장 이어져요.
✅ 예시 답안정답 보기
과제와 생각해볼 주제의 예시답안입니다. 오늘 과제도 SQL을 짜는 게 아니라 종이 위에서 관계를 표기하고 식별자를 설계하는 데 초점이 있고, 생각해볼 주제는 자격증·실무 양쪽 관점을 함께 담았어요. 정답을 맞히는 것보다 "왜 그렇게 골랐는지"를 설명할 수 있는지가 핵심이라, 아래 예시는 여러 모범 답 중 하나일 뿐입니다.
🎯 [과제 1 예시답안] 새 관계를 차수·선택성으로 표기하기
채점 포인트
| 채점 포인트 | 확인 |
|---|---|
| (1) Member–Story 차수(1:1·1:N·M:N)를 골랐는가 | ✅ |
| (2) 양쪽 끝의 선택성(필수/선택)을 각각 판단했는가 | ✅ |
| 두 판단의 이유를 각각 한 줄씩 적었는가 | ✅ |
풀이 예시
Member와 Story의 관계는 1:N이에요. 회원 한 명이 스토리를 여러 개 올릴 수 있고, 스토리 하나는 올린 회원 한 명의 것이니까요. Member–Post와 똑같은 모양이죠.
Member ──○──< Story 1:N
회원 → 스토리 : 선택(○) — 스토리를 한 번도 안 올린 회원도 회원이다
스토리 → 회원 : 필수(│) — 올린 사람 없는 스토리는 존재할 수 없다
선택성은 양쪽을 따로 봐요. 회원 쪽에서 보면 스토리는 선택이에요. 스토리를 한 개도 안 올린 회원도 멀쩡한 회원이니까요(최소 0). 반대로 스토리 쪽에서 보면 회원은 필수예요. 올린 사람 없는 스토리란 있을 수 없으니까요(최소 1). 같은 선인데 양 끝의 성격이 다른 거죠.
덧붙이면 Story도 게시물처럼 자기 번호(story_id)를 주식별자로 가지니, Member–Story는 비식별 관계예요.
💡 튜터의 한마디
이 과제의 함정은 차수와 선택성을 섞는 거예요. "1:N"은 개수(몇 개와 이어지나)를 말하고, 선택성은 "최소 0개냐 1개냐"를 말해요. 둘은 다른 축이에요. 본인 답에서 차수 한 줄, 양쪽 선택성 두 줄이 따로 적혔는지, 그리고 "스토리를 안 올린 회원"처럼 선택성을 0개 사례로 설명했는지 확인해 보세요.
🎯 [과제 2 예시답안] M:N을 연결 엔터티로 풀고 복합 식별자 적기
채점 포인트
| 채점 포인트 | 확인 |
|---|---|
| (1) 가운데 연결 엔터티를 세워 1:N + 1:N으로 풀었는가 | ✅ |
| (2) 연결 엔터티의 주식별자(복합 식별자)를 적었는가 | ✅ |
| (3) 식별 관계인지 비식별 관계인지 판단했는가 | ✅ |
풀이 예시
Post와 Hashtag는 M:N이에요. 게시물 하나에 해시태그가 여러 개 달리고, 같은 해시태그가 여러 게시물에 쓰이니까요. 그대로는 표가 안 되니 가운데 연결 엔터티(여기선 PostHashtag라 부를게요)를 세워 풀어요.
Post >────< Hashtag M:N
Post ──< PostHashtag >── Hashtag
Post ── PostHashtag : 1:N
Hashtag ── PostHashtag : 1:N
PostHashtag 주식별자 = (post_id, hashtag_id) 복합 · 식별
(2) 연결 엔터티의 주식별자는 두 부모의 키를 묶은 복합 식별자 (post_id, hashtag_id)예요. "7번 게시물에 3번 해시태그가 달렸다"는 (7, 3) 한 쌍으로 콕 집히죠.
(3) 이건 식별 관계예요. 받아온 post_id·hashtag_id를 일반 칸이 아니라 주식별자 안으로 끌어들였으니까요. 좋아요(Like)와 완전히 똑같은 구조예요.
💡 튜터의 한마디
좋아요·해시태그·팔로우가 다 같은 패턴이라는 걸 느꼈다면 성공이에요. M:N이 보이면 → 연결 엔터티를 세우고 → 두 부모 키를 묶어 복합 주식별자로 → 식별 관계. 이 흐름이 한 번에 떠오르면 시험에서 M:N 문제는 거의 다 잡은 거예요.
🎯 [과제 3 예시답안] 같은 엔터티를 복합키 vs 인조키 두 안으로 설계하기
채점 포인트
| 채점 포인트 | 확인 |
|---|---|
| (가) 복합키 안과 (나) 인조키 안을 둘 다 설계했는가 | ✅ |
| 주식별자 4조건·중복 방지·참조 편의 관점에서 비교했는가 | ✅ |
| 어느 안을 고를지 이유와 함께 정했는가 | ✅ |
풀이 예시
[응용]에서 만든 PostHashtag를 두 방식으로 설계해 볼게요.
(가) 복합키 주식별자 = (post_id, hashtag_id)
· 같은 (게시물, 태그) 쌍이 두 번 못 들어옴 → 중복 원천 차단
· 식별 관계, "어느 게시물에 어느 태그"가 키에 그대로 드러남
· 단, 키가 두 칸이라 참조가 조금 길어짐
(나) 인조키 주식별자 = post_hashtag_id (1, 2, 3… 새 번호)
· 키가 한 칸이라 참조가 짧고 단순
· 단, (post_id, hashtag_id)에 '겹치면 안 된다'는 약속을
따로 걸어야 같은 태그가 두 번 붙는 걸 막을 수 있음
4조건으로 보면 둘 다 유일·불변·존재는 만족해요. 최소성에선 (나)가 한 칸이라 더 단순하죠. 하지만 (나)는 인조 번호만으론 중복을 못 막아서, 본질 조합 (post_id, hashtag_id)에 유일 약속을 별도로 걸어줘야 비로소 (가)와 같은 안전성이 나와요.
저라면 (가) 복합키를 고르겠어요. 연결 엔터티가 단순하고 다른 곳에서 이 행을 따로 참조할 일이 거의 없으니, 의미도 또렷하고 중복도 저절로 막히는 복합키가 깔끔하거든요. 모델링·SQLD 관점에서도 이쪽이 정석이고요.
💡 튜터의 한마디
이 과제엔 정답이 하나로 정해져 있지 않아요. 핵심은 "어느 쪽이든 고르고, 그 트레이드오프를 설명하는 것"이에요. (나)를 골랐어도 "참조가 단순해지는 대신 중복 방지 약속을 따로 걸어야 한다"를 짚었다면 훌륭한 답이에요. 반대로 (가)를 골랐는데 "키가 길어지는 단점"을 언급 안 했다면 한쪽만 본 거고요.
🤔 [생각해볼 주제 1] 인조 식별자를 쓰면, 본질 식별자의 유일성은 누가 지키나
문제 상황 요약
회원을 member_id 인조 번호로 가리키기로 했어요. 편하긴 한데, 그러면 이메일이나 username이 겹치는 걸 막아주던 장치가 사라지는 게 아니냐는 의문이에요. member_id만 다르면 같은 이메일을 가진 회원이 둘 들어올 수도 있으니까요.
튜터의 가이드 및 해설
핵심은 역할을 나눠 맡긴다는 거예요. 인조 식별자(member_id)는 '행을 가리키는' 일만 담당해요. 본질 식별자가 갖던 '겹치면 안 된다'는 성질은 그냥 사라지는 게 아니라, 따로 챙겨줘야 하는 거죠.
방법은 이래요. member_id를 주식별자로 두고, username·이메일은 보조식별자로 남겨서 "이 값은 겹치면 안 된다"는 유일 약속을 따로 걸어요. 그러면 member_id로는 행을 가리키고, 보조식별자로는 중복을 막아서 둘 다 챙겨지죠.
즉 인조 식별자를 도입한다고 본질 식별자를 버리는 게 아니에요. 주식별자 역할만 인조에게 내주고, 본질 식별자는 보조식별자로 옮겨 앉아 유일성을 계속 지키는 거예요.
🎯 SQLD는 이렇게 나온다
주식별자와 보조식별자의 구분이 단골이에요. "인조 식별자를 주식별자로 쓸 때 기존 본질 식별자는 어떻게 되는가?"라는 물음의 답이 바로 "보조식별자로 두고 유일성을 보장한다"예요. 인조 식별자만 두면 본질 값의 중복을 못 막는다는 함정도 자주 나오니, 보조식별자에 유일 약속을 거는 그림을 같이 기억해 두세요.
💡 실무에선
인조 번호를 주식별자로 두고, username·이메일 같은 본질 값엔 "겹치면 안 된다"는 제약을 따로 거는 조합이 가장 흔해요. 가리키기는 안 바뀌는 번호로, 중복 방지는 본질 값으로 — 두 마리 토끼를 이렇게 나눠 잡습니다.
🤔 [생각해볼 주제 2] 식별 관계가 손자까지 내려가면 주식별자는 어디까지 길어질까
문제 상황 요약
식별 관계는 부모 키를 자식 주식별자 안으로 끌어들이죠. 그런데 그 자식이 또 다른 자식(손자)의 부모가 되어 식별 관계로 이어지면, 손자의 주식별자는 부모·조부모의 키까지 전부 물려받아 점점 길어져요. 이 복합키 비대를 어떻게 봐야 할까요.
튜터의 가이드 및 해설
식별 관계를 한 줄로 이으면 주식별자가 줄줄이 상속돼요. 조부모 키 1개 → 부모는 그걸 포함해 2개 → 손자는 또 포함해 3개… 이렇게 깊어질수록 복합키의 칸 수가 불어나요.
이게 왜 부담이냐면, 그 긴 키를 자식이 생길 때마다 통째로 끌고 내려가야 하거든요. 키가 길면 값을 비교하고 찾는 비용도 커지고, 다른 곳에서 이 행을 참조할 때 적어야 할 칸도 많아져요. 의미는 또렷해지지만 다루기는 무거워지는 거죠.
그래서 어느 깊이부터는 인조키로 끊어주는 선택을 해요. 손자 엔터티에 새 인조 번호 하나를 주식별자로 얹고, 부모와의 관계를 식별에서 비식별로 바꾸는 거예요. 그러면 긴 상속 사슬이 거기서 끊겨요. "의미의 또렷함(식별)"과 "다루기 편함(인조키로 끊기)" 사이에서 균형점을 찾는 판단이에요.
🎯 SQLD는 이렇게 나온다
식별 관계가 주식별자를 상속·전파시킨다는 성질이 출제 포인트예요. "식별 관계로 이어지면 자식의 주식별자에 부모 주식별자가 포함된다"가 핵심 문장이죠. 반대로 비식별 관계는 부모 키가 일반 속성에만 머물러 상속을 끊는다는 대비도 함께 묻습니다.
💡 실무에선
식별 관계가 두세 단계만 깊어져도 복합키가 부담스러워져서, 적당한 깊이에서 인조키로 끊는 게 일반적이에요. 연결 엔터티가 또 다른 자식을 거느리기 시작하면, 그때가 인조키 도입을 고민할 신호예요.
🤔 [생각해볼 주제 3] M:N을 꼭 연결 엔터티로 풀어야 할까
문제 상황 요약
오늘 M:N은 한 칸에 여러 값을 못 넣어서 연결 엔터티로 푼다고 했어요. 그런데 누가 "그냥 한 칸에 목록을 넣으면(좋아요 누른 회원 번호를 쉼표로 죽 나열) 안 되나?"라고 묻는다면, 그게 당장은 무엇이 편하고 나중에 무엇을 잃게 될까요.
튜터의 가이드 및 해설
당장은 편해 보여요. 표를 하나 더 안 만들어도 되고, "3,5,9,12"처럼 한 칸에 적으면 게시물 행 하나만 봐도 누가 좋아요를 눌렀는지 한눈에 보이죠.
그런데 잃는 게 훨씬 커요. "9번 회원이 좋아요를 취소"하려면 그 긴 문자열을 뜯어서 9만 빼내 다시 합쳐야 하고, "좋아요를 가장 많이 받은 게시물"을 세려면 칸 안의 쉼표를 일일이 세야 해요. 회원이 늘면 칸이 끝없이 길어지고, 실수로 "3,,5"처럼 망가진 값이 들어가도 막을 길이 없어요. 한 칸에 값 하나라는 기본 약속이 깨지면, 표가 주는 모든 편리함이 함께 무너지는 거예요.
연결 엔터티로 풀면 좋아요 하나가 한 행이라, 추가는 한 행 삽입, 취소는 한 행 삭제, 집계는 행 세기로 깔끔하게 끝나요. 표를 하나 더 만드는 수고를 들이는 대신, 그 뒤의 모든 작업이 단순해지는 거죠.
🎯 SQLD는 이렇게 나온다
"한 칸에 여러 값을 넣는다"는 건 제1정규형(1NF)을 어기는 대표 사례예요. 속성은 더 쪼갤 수 없는 하나의 값(원자값)을 가져야 한다는 규칙이죠. 이 한 칸 목록 문제가 곧 다음 시간 정규화의 출발점이라, "M:N을 한 칸에 넣으면 1NF 위반"을 연결 지어 기억해 두면 좋아요.
💡 실무에선
한 칸에 목록을 욱여넣는 방식은 거의 안티패턴으로 봐요. 검색·집계·수정이 다 어려워지거든요. 연결 엔터티로 정규화해 두는 게 기본이에요. 이 고민이 다음 시간 정규화로 곧장 이어집니다.