개발자혜수 홈으로 이동개발자혜수

Hyesu's Blog

테크블로그

공부하면서 기록을 남겨요

데이터베이스: RDB를 실제로 설계하기 전 알아야 하는 기본 개념들에 대해서 알아봅시다.

2026.08.12. DB

지난 글에서 데이터베이스가 왜 필요한지, RDB와 NoSQL이 어떻게 다른지 살펴봤어요.

이번엔 그중에서도 RDB를 실제로 설계할 때 꼭 알아야 하는 기본 개념인 테이블/키, 테이블 간 관계, 그리고 정규화에 대해서 정리해볼게요.

테이블(Table), 스키마(Schema), 그리고 키(Key)


테이블과 스키마

PK(기본키)

PK(Primary Key)는 테이블에서 각 행을 유일하게 식별하는 컬럼(들)이에요. 두 가지 조건을 반드시 만족해야 해요.

FK(외래키)

FK(Foreign Key)는 다른 테이블의 PK를 참조하는 컬럼이에요. 테이블 간의 “관계”를 만드는 역할을 해요.

CREATE TABLE enrollments (
  student_id INT REFERENCES students(student_id),
  course_id  INT REFERENCES courses(course_id),
  PRIMARY KEY (student_id, course_id)
);

이렇게 FK를 걸어두면 enrollments 테이블에 존재하지 않는 student_id가 들어오는 걸 DB가 막아줘요. 이런 성질을 참조 무결성(referential integrity)이라고 해요.

테이블 간 관계


테이블을 나누고 나면, 이 테이블들이 서로 어떤 식으로 연결되는지가 중요해져요. 관계는 크게 세 가지예요.

1:1 관계

한 행이 다른 테이블의 정확히 한 행과만 연결돼요.

1:N 관계

한쪽의 행 하나가 다른 쪽 여러 행과 연결돼요.

M:N 관계

양쪽 다 여러 개와 연결될 수 있어요.

정규화: 왜 테이블을 쪼갤까


하나로 합쳐두면 생기는 문제

학생·강좌 정보를 아래처럼 테이블 하나에 다 몰아넣었다고 해볼게요.


| student_id | student_name | course_id | course_name | professor |
|------------|--------------|-----------|-------------|-----------|
|     1      |    김철수      |    C1     |   알고리즘    |   박교수   |
|     2      |    이영희      |    C1     |   알고리즘    |   박교수   |
|     1      |    김철수      |    C2     |   DB개론     |   최교수   |

편해 보이지만, 이렇게 두면 **세 가지 이상 현상(anomaly)**이 생겨요.

  1. 삽입 이상(Insertion Anomaly): 아직 수강생이 없는 새 강좌(C3, 자료구조, 박교수)를 등록하려면 어떡하죠? student_id를 채울 방법이 없어요.
  2. 갱신 이상(Update Anomaly): DB개론 담당 교수가 최교수에서 김교수로 바뀌면, C2를 듣는 모든 행의 professor 값을 일일이 찾아 바꿔야 해요. 하나라도 빠뜨리면 데이터가 불일치해요.
  3. 삭제 이상(Deletion Anomaly): 김철수가 DB개론 수강을 취소해서 그 행을 지우면, “최교수가 DB개론을 가르친다”는 정보 자체가 통째로 사라져요.

정규화(Normalization)는 이런 문제를 줄이려고 테이블을 여러 개로 쪼개는 설계 기법이에요.

정규화 단계

1NF (제1정규형): 모든 컬럼 값이 원자값(더 쪼갤 수 없는 값)이어야 해요. 예를 들어 한 칸에 “국어, 영어, 수학”처럼 여러 값을 넣지 않는 거예요.

2NF (제2정규형): 1NF를 만족하면서, 기본키가 복합키(여러 컬럼으로 이루어진 키)일 때 그 일부에만 종속되는 컬럼(부분 함수 종속)이 없어야 해요.

enrollments_bad(student_id, course_id, student_name, course_name)
PK = (student_id, course_id)

여기서 student_name은 PK 전체가 아니라 student_id에만 종속되고, course_namecourse_id에만 종속돼요. 이게 부분 함수 종속이라 2NF 위반이에요. 그래서 앞서 본 것처럼 students, courses, enrollments 세 테이블로 쪼개는 거예요.

3NF (제3정규형): 2NF를 만족하면서, 기본키가 아닌 컬럼끼리 서로 종속하는 것(이행적 함수 종속)도 없어야 해요.

courses_bad(course_id, course_name, professor_id, professor_office)
PK = course_id

여기서 professor_officecourse_id가 아니라 professor_id에 종속돼요. course_id → professor_id → professor_office처럼 한 다리 건너 종속되는 거라 3NF 위반이에요. 이 경우엔 professors(professor_id, professor_office) 테이블을 따로 빼면 해결돼요.

1️⃣ 1NF: 모든 컬럼 값이 원자값, 한 칸에 여러 값 넣지 않기

2️⃣ 2NF: 1NF + 부분 함수 종속 제거, 복합키 중 일부에만 종속되는 컬럼 분리

3️⃣ 3NF: 2NF + 이행적 함수 종속 제거, 키가 아닌 컬럼끼리의 종속 관계 분리

트레이드오프: 무조건 쪼갠다고 좋은 게 아니에요

정규화를 많이 할수록 데이터 중복과 이상 현상은 줄어들지만, 테이블이 많아져서 조회할 때 JOIN이 늘어나고 그만큼 성능이 떨어질 수 있어요. 그래서 실무에서는 조회 성능이 중요한 곳엔 일부러 정규화를 되돌리는 반정규화(denormalization)를 하기도 해요. (이건 나중에 성능과 설계를 다루는 글에서 좀 더 파볼게요.)

정리하며


RDB는 테이블/스키마/PK·FK로 데이터를 구조화하고, FK로 테이블 간 1:1·1:N·M:N 관계를 표현해요. 그리고 테이블 하나에 여러 주제를 몰아넣으면 삽입·갱신·삭제 이상이 생기기 때문에, 정규화(1NF→2NF→3NF)로 테이블을 쪼개서 이를 줄여요. 다만 정규화가 늘어날수록 JOIN도 늘어나니, 상황에 따라 반정규화로 균형을 맞추기도 해요.

다음에는 기본적인 쿼리문에 대해서 살펴봅시다 !

목록으로 돌아가기

댓글