테이블 관계란? 1:1, 1:다, 다:다 구분하기
어드민에 메모 기능을 붙이면 AI가 표를 새로 만들고 id로 연결해요. 한 칸에 여러 개를 욱여넣으면 무엇이 막히는지, 1:1·1:다·다:다를 어떻게 구분하는지, 부모 행을 지울 때 자식 행이 어떻게 되는지까지 예시로 설명합니다.
핵심 요약
- 관계는 "A 하나에 B가 몇 개까지 붙는가"를 양쪽에서 물어 정합니다.
- 가장 자주 만나는 1:다는 "다" 쪽 표에 상대 id를 한 칸 넣어 연결합니다.
- 관계를 만들 때는 부모 행을 지울 때 자식 행을 어떻게 할지도 함께 정해야 합니다.
어드민에 메모 기능을 붙여달라고 했더니 AI가 이렇게 답합니다.
inquiry_notes테이블을 새로 만들고inquiries테이블과 연결하겠습니다.
문의 표에 메모 칸 하나 더 늘리면 될 것 같은데, 표를 새로 만든다고 하네요. 스키마와 마이그레이션까지는 따라왔는데 여기서 한 번 더 막힙니다.
AI가 표를 나눈 이유는 메모가 문의 하나에 여러 개 붙기 때문이에요. 관계형 데이터베이스에서 이걸 관계라고 부릅니다.
한 칸에 여러 개를 넣으면 무엇이 막히나요
문의 표에 memo 칸을 하나 만들고 메모를 적는다고 해볼게요.
| id | 이름 | memo |
|---|---|---|
| i1 | 민지 | 8/20 통화함, 8/22 견적 보냄, 예산 검토중 |
메모 하나일 때는 멀쩡합니다. 두 개째부터 이런 일이 생겨요.
- 8월 22일에 쓴 메모가 어느 부분인지 알 수 없습니다. 날짜를 앞에 손으로 적는 수밖에 없어요.
- "견적 보냄"만 지우려면 문장 가운데를 잘라내야 합니다. 쉼표를 하나 잘못 지우면 앞뒤 메모가 붙어버려요.
- "최근 일주일 안에 메모가 달린 문의"를 찾을 방법이 없습니다. 날짜가 글자로 섞여 있으니까요.
- 두 사람이 동시에 메모를 추가하면 나중에 저장한 쪽이 앞사람 메모를 덮어씁니다.
따로 세어야 하고, 따로 지워야 하고, 따로 찾아야 하는 데이터라면 표를 나눌 때가 된 겁니다. 메모는 셋 다 해당하죠.
관계는 양쪽에서 두 번 물어봅니다
관계 종류를 외울 필요는 없어요. 두 데이터를 놓고 질문을 두 번 하면 답이 나옵니다.
- A 하나에 B가 몇 개까지 붙나요?
- B 하나에 A가 몇 개까지 붙나요?
문의와 메모로 물어볼게요.
- 문의 하나에 메모가 몇 개까지 붙나요? → 여러 개
- 메모 하나가 문의 몇 건에 붙나요? → 한 건
한쪽이 여럿, 한쪽이 하나. 이게 1:다입니다. 답 조합에 따라 이름이 정해져요.
| 문의 → 메모 | 메모 → 문의 | 관계 이름 |
|---|---|---|
| 하나 | 하나 | 1:1 |
| 여러 개 | 하나 | 1:다 |
| 여러 개 | 여러 개 | 다:다 |
1:다 — 가장 자주 만나는 관계
회원과 주문, 게시글과 댓글, 문의와 메모가 모두 1:다입니다. 바이브코딩으로 만드는 앱에서 대부분의 관계가 여기에 들어가요.
연결 방법은 간단합니다. "다" 쪽 표에 상대 id를 넣을 칸을 하나 만듭니다.
문의는 inquiries 표에 그대로 둡니다.
| id | 이름 | 이메일 |
|---|---|---|
| i1 | 민지 | minji@example.com |
| i2 | 준호 | junho@example.com |
메모는 inquiry_notes 표로 따로 나가고, 어느 문의의 메모인지를 inquiry_id에 적어요.
| id | inquiry_id | 내용 | 생성일 |
|---|---|---|---|
| n1 | i1 | 통화함 | 2026-08-20 |
| n2 | i1 | 견적 보냄 | 2026-08-22 |
| n3 | i2 | 메일 회신 없음 | 2026-08-22 |
inquiry_notes의 inquiry_id처럼 다른 표의 id를 가리키는 칸을 외래키(foreign key)라고 합니다. 외래키를 걸어두면 데이터베이스가 규칙 하나를 대신 지켜줘요. i9처럼 존재하지 않는 문의의 메모는 저장 자체가 거부됩니다.
Drizzle이나 Prisma 같은 ORM을 쓰고 있다면 이 연결도 스키마 파일에 코드로 적습니다.
메모를 100개 남겨도 문의 표는 그대로예요. 날짜로 정렬하는 것도, 메모 하나만 지우는 것도 이제 한 줄 단위로 됩니다.
반대 방향으로 넣으면 안 되는지 궁금할 수 있어요. inquiries 표에 note_id 칸을 만들면 문의 하나가 메모 하나만 가리킬 수 있으니 처음 문제로 돌아갑니다. id를 넣는 쪽은 항상 "여러 개"인 표입니다.
1:1 — 나눌 수도, 안 나눌 수도 있어요
회원과 프로필처럼 양쪽 다 하나씩만 붙는 관계입니다. 1:1은 애초에 한 표에 다 넣어도 되기 때문에, 나눌지 말지가 선택의 문제예요.
표를 나누는 쪽이 나을 때가 있습니다.
- 회원 100명 중 3명만 채우는 사업자 정보처럼, 대부분 빈칸으로 남는 데이터
- 주민등록번호나 계좌번호처럼 접근 권한을 따로 걸고 싶은 데이터
- 프로필 사진과 자기소개처럼 회원 목록을 조회할 때는 전혀 필요 없는 데이터
이 중 어디에도 해당하지 않으면 그냥 한 표에 두세요. 표를 나누면 조회할 때마다 두 표를 합쳐야 해서 코드가 늘어납니다. 1:1이 보이면 "정말 나눠야 하나"부터 의심하는 게 맞아요.
다:다 — 중간 표가 하나 더 필요합니다
학생과 강의를 볼게요.
- 학생 하나가 강의를 몇 개 듣나요? → 여러 개
- 강의 하나를 학생 몇 명이 듣나요? → 여러 명
양쪽 다 여럿이면 어느 쪽에 id를 넣어도 담기지 않습니다. 이때는 관계를 담는 표를 하나 더 만들어요. 여기서는 수강신청이 그 표입니다.
| id | student_id | course_id | 신청일 |
|---|---|---|---|
| e1 | s1 | c1 | 2026-08-01 |
| e2 | s1 | c2 | 2026-08-01 |
| e3 | s2 | c1 | 2026-08-03 |
다:다 하나가 1:다 두 개로 바뀌었습니다. 학생 하나에 수강신청이 여럿, 강의 하나에 수강신청이 여럿이에요.
중간 표를 만들 때 놓치기 쉬운 게 있어요. 관계 자체에 붙는 정보는 중간 표에 들어갑니다. 신청일은 학생의 정보도 강의의 정보도 아니고 "이 학생이 이 강의를 신청한 일"의 정보니까요. 출석률, 수료 여부, 결제 금액도 마찬가지입니다.
중간 표를 만들 때 조건 하나를 같이 걸어두면 좋아요. 같은 학생이 같은 강의를 두 번 신청하지 못하도록 (student_id, course_id) 조합을 유일하게 만드는 겁니다. 이걸 빼놓으면 사용자가 버튼을 두 번 눌렀을 때 중복 신청이 그대로 쌓입니다.
부모를 지우면 자식은 어떻게 되나요
관계를 만들었으면 반드시 같이 정해야 하는 게 하나 있어요. 문의를 삭제하면 그 문의의 메모는 어떻게 되나요?
선택지는 세 가지입니다.
| 설정 | 동작 | 어울리는 곳 |
|---|---|---|
cascade |
문의를 지우면 메모도 같이 지웁니다 | 문의와 메모, 게시글과 댓글 |
restrict |
메모가 남아 있으면 문의 삭제를 막습니다 | 회원과 결제 기록 |
set null |
메모는 남기고 inquiry_id를 비웁니다 |
카테고리와 상품 |
문의에 딸린 메모는 문의 없이 의미가 없으니 cascade가 맞아요. 반대로 결제 기록은 회원이 탈퇴해도 남아야 하니 restrict로 막고, 대신 회원 행에 탈퇴 표시를 남기는 편이 안전합니다.
AI는 이걸 물어보지 않고 정합니다. 보통 cascade나 아무것도 걸지 않은 상태를 고르는데, 결제나 주문처럼 지워지면 안 되는 데이터에 cascade가 걸려 있으면 회원 한 명 삭제로 매출 기록이 통째로 사라져요. 관계를 만들 때마다 삭제 규칙을 직접 확인하세요.
AI에게는 관계를 말로 풀어서 알려주세요
"메모 기능 만들어줘"라고만 하면 AI가 문의 표에 칸 하나를 추가할 수도 있습니다. 개수를 문장에 넣으면 달라져요.
어드민에 메모 기능을 더해줘. 문의 하나에 메모를 여러 개 남길 수 있어야 하고, 메모는 내용과 생성 날짜로 구성해줘. 문의를 삭제하면 그 문의의 메모도 같이 삭제되게 해줘.
이미 만들어진 스키마를 확인할 때는 이렇게 물어보세요.
지금 데이터베이스에 있는 테이블들의 관계를 표로 정리해줘. 각 관계가 1:1인지 1:다인지 다:다인지, 외래키가 어느 표에 있는지, 부모를 삭제하면 자식이 어떻게 되는지 함께 알려줘.
답을 받으면 세 가지를 직접 확인하면 됩니다.
- "여러 개"라고 말한 쪽이 정말 여러 줄로 쌓이나요?
- 지워지면 안 되는 데이터에
cascade가 걸려 있지 않나요? - 중복으로 들어가면 안 되는 조합에 유일 조건이 걸려 있나요?
어드민에 어떤 기능이 필요한지 정리해두면 관계도 같이 보입니다. 화면에 목록으로 쌓이는 것이 보통 "다" 쪽이에요.
정리
- 따로 세고 따로 지우고 따로 찾아야 하는 데이터라면 표를 나눌 때입니다.
- 관계는 "A 하나에 B가 몇 개, B 하나에 A가 몇 개"를 양쪽에서 물어 정합니다.
- 1:다는 "다" 쪽 표에 상대 id를 넣는 외래키로 연결합니다.
- 1:1은 대부분 한 표에 두고, 빈칸이 많거나 권한을 나눠야 할 때만 분리합니다.
- 다:다는 중간 표를 만들고, 관계에 붙는 정보는 중간 표에 넣습니다.
- 중간 표에는 같은 조합이 두 번 들어가지 않도록 유일 조건을 겁니다.
- 관계를 만들 때마다 부모를 지웠을 때 자식을 어떻게 할지 함께 정합니다.
관계를 알아보는 눈이 생기면 AI가 만든 스키마를 읽을 수 있게 됩니다. 화면을 만들기 전에 표 목록을 놓고 화살표를 그려보세요. 어느 표에서 어느 표로 여러 줄이 뻗어나가나요? 그 화살표가 앱의 구조입니다.
인스타그램 @ddukddak.build · 페이스북 뚝딱







