SECTION 1 데이터베이스의 기본
SECTION 2 ERD와 정규화 과정
SECTION 3 트랜잭션과 무결성
SECTION 4 데이터베이스의 종류
SECTION 5 인덱스
SECTION 6 조인의 종류
SECTION 7 조인의 원리
4.1 데이터베이스의 기본
데이터베이스 : 일정한 규칙을 통해 구조화되어 저장되는 데이터의 모음
DBMS (DataBase Management System) : DB를 제어, 관리하는 통합시스템
DB 안에 있는 데이터들은 DBMS마다 정의된 쿼리 언어를 통해 삽입, 삭제, 수정, 조회 가능
DB의 특징
- 실시간 접근 / 동시 공유 / 데이터의 독립성 / 무결성 / 일관성 등
4.1.1 엔터티
엔터티 : 여러 개의 속성을 지닌 명사
ex) 엔터티가 '회원'일 경우, 이름, 아이디, 주소, 전화번호 등의 속성을 가진다
약한 엔터티와 강한 엔터티
- 약한 엔터티 : 혼자서 존재하지 못하고, 다른 엔터티(B)의 존재 여부에 따라 종속적
- 이때, 다른 엔터티(B)는 강한 엔터티가 된다.
4.1.2 릴레이션
릴레이션 : DB에서 정보를 구분하여 저장하는 기본 단위
엔터티에 관한 데이터를 릴레이션 하나에 담아서 관리

릴레이션은,
관계형 데이터베이스 --> 테이블
NoSQL 데이터베이스 --> 컬렉션
| 특징 | 관계형 데이터베이스 (RDB) | NoSQL 데이터베이스 |
| 릴레이션 개념 | 테이블 | 컬렉션, 컬럼 패밀리, 노드 등 |
| 데이터 저장 방식 | 행과 열 구조 | JSON, Key-Value, Wide Column 등 다양함 |
| 데이터 관계 표현 | JOIN을 통해 여러 테이블 연결 | 중첩 구조 사용하여 관계 표현 |
| 정규화 여부 | 정규화로 중복 최소화 | 비정규화로 성능 최적화 |
| 확장성 | 수직 확장 중심 | 수평 확장 중심 |
| 사용 사례 | 금융, ERP, 전통적인 웹 애플리케이션 | 빅데이터, IoT 소셜 미디어 |
테이블과 컬렉션
- MySQL(관계형 데이터베이스)의 구조는 '레코드-테이블-데이터베이스'
- MongoDB(NoSQL)의 구조는 '도큐먼트-컬렉션-데이터베이스'

| 개념 | MySQL (관계형 DB) | MongoDB (NoSQL) |
| 데이터베이스 | 여러 개의 테이블을 포함 | 여러 개의 컬렉션을 포함 |
| 테이블 vs 컬렉션 | 테이블 | 컬렉션 |
| 행 vs 도큐먼트 | 행 | 도큐먼트 |
| 열 vs 필드 | 고정된 컬럼 구조 | 유연한 필드 구조 |
| 스키마 | 고정됨 (스키마 필요) | 유연함 (스키마 필요 없음) |
| 데이터 저장 형식 | 정형 데이터 | 비정형 데이터 가능 |
| 관계 표현 방식 | JOIN을 통해 관계 설정 | 중첩 문서로 관계 표현 |
4.1.3 속성
속성 : 릴레이션에서 관리하는 구체적이며 고유한 이름을 갖는 정보
ex) 엔터티 : 차 / 속성 : 차 넘버, 바퀴 수, 차 색깔, 차종 등
But, 이 중에서 서비스의 요구 사항을 기반으로 관리해야 할 필요가 있는 속성들만
'엔터티의 속성'이 된다.
4.1.4 도메인
도메인 : 릴레이션에 포함된 각각의 속성들이 가질 수 있는 값의 집합
ex) 속성 : 성별 / 도메인 : {남, 여}

4.1.5 필드와 레코드

엔터티 : 회원
테이블 : member
속성 : 이름, 아이디, 주소, 전화번호
필드 : name, ID, address, phonenumber
레코드 : 테이블에 쌓이는 행 단위의 데이터
레코드=튜플
엔터티를 데이터베이스에 넣어 테이블로 만들려면?
--> 속성들에 맞는 타입을 정의해야 한다.
--> 데이터베이스마다 다름
필드 타입
- DBMS마다 타입이 다르다.
- 이 책에서는 MySQL을 기준으로 대표적인 타입을 설명하고 있음
- 숫자 타입 / 날짜타입 / 문자 타입 에 대해서 알아보자!
1) 숫자 타입

2) 날짜 타입
- DATE : 날짜 부분 O, 시간 부분 X, 2025-03-05, 3바이트
- DATETIME : 날짜 부분 O, 시간 부분 X, 2025-03-05 23:00:00, 8바이트
- TIMESTAMP : 날짜 부분 O, 시간 부분 X, 2025-03-05 23:00:00, 4바이트
** DATETIME vs TIMESTAMP
| 특징 | DATETIME | TIMESTAMP |
| 저장 범위 | 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 | 1970-01-01 00:00:01 UTC ~ 2038-01-19 03:14:07 UTC |
| 저장 크기 | 8 바이트 | 4 바이트 |
| 자동 업데이트 | 없음 | CURRENT_TIMESTAMP로 자동 업데이트 가능 |
| 타임존 영향 | 서버 타임존 영향 없음 | 서버의 타임존 영향을 받 |
| 형 | YYYY-MM-DD HH:MI:SS | YYYY-MM-DD HH:MI:SS |
3) 문자 타입
- CHAR : 고정 길이 문자열(0~255)
- VARCHAR : 가변 길이 문자열(0~65,535), 유동성 GOOD!
- TEXT : 큰 문자열 저장에 사용(주로 게시판의 본문을 저장할 때)
주로 게시판의 본문을 저장할 때?? 이게 무슨 소리
- BLOB : 큰 데이터 저장에 사용 (이미지, 동영상 등)
** But, 서버(AWS의 S3)에 파일을 올리고 파일에 관한 경로를 VARCHAR로 저장
- ENUM : ENUM('x-small', 'small', 'medium', 'large', 'x-large') 형태로 사용됨.
단일 선택만 가능
잘못된 값 삽입하면, 빈 문자열이 대신 삽입됨
CREATE TABLE TShirts (
id INT PRIMARY KEY,
size ENUM('x-small', 'small', 'medium', 'large', 'x-large')
);
INSERT INTO TShirts (id, size) VALUES (1, 'medium'); //정상 삽입
INSERT INTO TShirts (id, size) VALUES (2, 'extra-large'); //오류 발생, ''로 저장됨
- SET : 여러 개의 데이터 선택 가능
CREATE TABLE Features (
id INT PRIMARY KEY,
options SET('WiFi', 'Bluetooth', 'GPS', 'NFC')
);
INSERT INTO Features (id, options) VALUES (1, 'WiFi'); // 단일 선택 가능
INSERT INTO Features (id, options) VALUES (2, 'WiFi,Bluetooth'); // 다중 선택 가능
INSERT INTO Features (id, options) VALUES (3, 'WiFi,Bluetooth,GPS,NFC'); // 최대 4개까지 가능
INSERT INTO Features (id, options) VALUES (4, 'USB'); // 오류 발생, ''로 저장됨
4.1.6 관계
DB에는 여러 개의 테이블이 있고, 서로의 관계가 정의되어 있음.

- 1:1 관계

- 1: N 관계

- N:M 관계

N:M은 테이블 2개를 직접적으로 연결하는 것이 아니라, 1:N, 1:M이라는 관계를 갖는 테이블로 나눠서 설정
4.1.7 키
키 : 테이블 간의 관계를 명확하게하고, 테이블 자체의 인덱스를 위한 장치
기본키, 외래키, 후보키, 슈퍼키, 대체키

유일성 : 중복되는 값 X
최소성 : 최소한의 속성만 써서 유일성을 만족해야함
기본키(Primary Key = PK)
- 유일성과 최소성 만족
- 자연키 또는 인조키 중에 고른다
*자연키 : 중복된 값들을 제외하며 중복되지 않는 것을 뽑다가 나오는 키, 언젠가는 변함
*인조키 : 인위적으로 생성한 키, 변하지 않음
외래키 (Foreign Key = FK)
- 다른 테이블의 PK를 그대로 참조하는 값
- 개체와의 관계를 식별하는 데 사용
- 중복 가능

후보키 (candidate key)
- 기본키의 후보 + 유일성과 최소성을 동시에 만족하는 키
대체키 (alternate key)
- 후보키가 2개 이상일 경우, 어느 하나를 기본키로 지정하고 남은 후보키들
슈퍼키 (super key)
- 각 레코드를 유일하게 식별할 수 있는 유일성을 갖춘 키
4.2 ERD와 정규화 과정
ERD (Entity Relationship Diagram) : 릴레이션 간의 관계들을 정의한 것. 매우 중요!!
4.2.1 ERD의 중요성
ERD는 시스템의 요구 사항을 기반으로 작성됨
작성된 ERD를 기반으로 DB 구축
디버깅 또는 재설계가 필요한 경우, 설계도 역할을 담당
But, 비정형 데이터를 충분히 표현할 수 없음
4.2.2 예제로 배우는 ERD

4.2.3 정규화 과정
- 릴레이션 간의 잘못된 종속 관계(DB 이상 현상이 발생할 수 있음)를 해결하기 위해
- 저장 공간을 효율적으로 사용하기 위해
* DB 이상 현상이란?
하나의 값을 가져야 하는데, 여러개의 값을 가지거나
삭제할 때 필요한 데이터가 같이 삭제되거나
삽입해야 하는데 하나의 필드 값이 NULL이 되면 안되어서 삽입하기 어려운 현상
정규화 과정 : 정규형 원칙을 기반으로 정규형을 만들어가는 과정
정규화된 정도 : 정규형(NF, Normal Form)으로 표현
기본 정규형 : 제1정규형, 제2정규형, 제3정규형, 보이스/코드 정규형
고급 정규형 : 제4정규형, 제 5정규형 (여기 책에서 배우지는 않음)
정규형 원칙
같은 의미를 표현하는 릴레이션이지만,
1) 좀 더 좋은 구조여야 하고
2) 자료의 중복성은 감소해야 하고
3) 독립적인 관계는 별개의 릴레이션으로 표현해야하고
4) 각각의 릴레이션은 독립적인 표현이 가능해야 함을 의미
제1정규형
릴레이션의 모든 도메인이 원자 값 만으로 구성되어야 함
*원자 값 : 더 이상 분해될 수 없는 값

제2정규형
일단, 릴레이션이 제1정규형이고
여기에서 부분 함수의 종속성을 제거한 것
* 부분 함수의 종속성 제거 : 기본키가 아닌 모든 속성이 기본키에 완전 함수 종속적인 것

기본키 : 유저번호 + 수강명
부분 함수 종속 문제
- 유저ID는 유저번호에만 종속 (수강명과 무관)
- 즉, 유저번호만 알면 유저ID를 알 수 있음 --> 부분적 종속 발생
주의할 점 : 동등한 릴레이션으로 분해해야 하고, 정보 손실이 발생하지 않아야 함
제3정규형
제2정규형의 상태에서 기본키가 아닌 모든 속성이 이행적 함수 종속을 만족하지 않는 상태
(기본 키가 아닌 속성이 다른 속성을 결정하지 않도록 해야 함!)
* 이행적 함수 종속
A->B 와 B->C가 존재하면 논리적으로 A->C가 성립하는데
이때, 집합 C가 집합 A에 '이행적으로 함수 종속이 되었다'고 한다


기본키 : 유저 ID
이행적 함수 종속 문제
- 할인율은 유저ID가 아닌 등급에 종속됨
- 즉, 등급을 알면 할인율을 알 수 있음 --> 이행적 종속 발생
보이스/코드 정규형(BCNF)
제3정규형이고
결정자가 후보키가 아닌 함수 종속 관계를 제거하여
릴레이션의 함수 종속 관계에서
모든 결정자가 후보키인 상태
* 결정자
x->y일 때, x는 결정자, y는 종속자

기본키 후보(후보키) : (학번, 수강명)
- 수강명 -> 강사 의 함수 종속이 존재함 (수강명만 알면 강사를 결정할 수 있음)
- 수강명이 강사를 결정하는데, 수강명은 후보키가 아니므로 보이스/코드 정규형 위반
정규형 과정을 거친다 해서 성능이 무조건 좋아지는 것은 아니다!!
서비스에 따라 정규화 또는 비정규화 과정을 진행해야 한다.
4.3 트랜잭션과 무결성
4.3.1 트랜잭션
트랜잭션 : DB에서 하나의 논리적 기능을 수행하기 위한 작업의 단위 / 여러 개의 퀴리들을 하나로 묶는 단위
ACID 특징 : 원자성, 일관성, 독립성, 지속성
원자성(atomicity)
- "all or nothing"
- 여러 로직들을 묶을 때, 외부 API를 호출하면 안됨.
* 외부 API는 트랜잭션 롤백이 불가능 (원자성 보장 어려움)
* 외부 API는 응답 시간이 예측 불가능 (트랜잭션 지연)
- 만약 있다면, 롤백이 일어났을 때에 대한 해결 방법이 있어야 하고, 트랜잭션 전파를 신경 써야함
* 보상 트랜잭션
* 트랜잭션 전파 관리
* 비동기 처리 활용하여 외부 API와 트랜잭션을 분리

커밋 : 여러 쿼리가 성공적으로 처리되었다고 확정하는 명령어
커밋이 수행되었다 = 하나의 트랜잭션이 성공적으로 수행되었다
롤백 : 트랜잭션으로 처리한 하나의 묶음 과정을 일어나기 전으로 돌리는 일
커밋과 롤백을 하면 좋은 점?
데이터의 무결성이 보장됨
데이터 변경 전에 변경사항을 쉽게 확인 가능함
해당 작업을 그룹화할 수 있음
트랜잭션 전파 : 여러 트랜잭션 관련 메서드의 호출을 하나의 트랜잭션에 묶이도록 하는 것
다시 ACID 특징으로 돌아와서,
일관성(consistency)
'허용된 방식'으로만 데이터를 변경해야 하는 것
격리성(isolation)
트랜잭션 수행 시 서로 끼어들지 못하는 것

* 격리 수준에 따라 발생하는 현상
팬텀 리드, 반복 가능하지 않은 조회, 더티 리드
- 팬텀 리드 : 같은 조건으로 여러 번 조회했을 때, 새로운 행이 추가되거나 삭제되는 현상
ex) 트랜잭션 A가 특정 조건에 맞는 데이터를 조회한 후, 트랜잭션 B가 그 조건에 맞는 새로운 데이터를 추가하면, A가 다시 조회했을 때 결과가 달라질 수 있음.
- 반복 가능하지 않은 조회 : 같은 행을 두 번 조회했을 때, 그 값이 변경되는 현상
ex) 트랜잭션 A가 특정 데이터를 조회한 후, 트랜잭션 B가 그 데이터를 수정하고 커밋하면, A가 다시 조회했을 때 값이 달라질 수 있음.
- 더티 리드 : 아직 커밋되지 않은 데이터를 다른 트랜잭션이 읽는 현상
ex) 트랜잭션 A가 데이터를 변경했지만 아직 커밋하지 않았을 때, 트랜잭션 B가 그 데이터를 읽으면, 이후 A가 롤백하면 B는 잘못된 데이터를 보게 됨.
팬텀 리드와 반복 가능하지 않은 조회의 차이점은?
팬텀 리드 : 새로운 행이 추가되거나 삭제됨 (INSERT / DELETE) -> 조건을 만족하는 데이터의 개수가 달라지는 것
반복 가능하지 않은 조회 : 기존 행이 수정됨 (UPDATE) -> 특정 행의 값 자체가변경되는 것
* 격리 수준
SERIALIZABLE
- 트랜잭션을 순차적으로 진행시키는 것
- 팬텀 리드까지 완벽하게 방지
- 가장 성능이 떨어짐
REPEATABLE_READ
- 트랜잭션이 실행되는 동안 조회한 행의 값이 변하지 않도록 보장(반복 가능한 조회 보장)
- 팬텀 리드 발생 가능
READ_COMMITTED (커밋된 데이터만 읽기 가능)
- 가장 많이 사용되는 격리 수준
- 더티 리드 발생 하지 않음
- 반복 가능하지 않은 조회와 팬텀 리드는 발생 가능
READ_UNCOMMITTED (읽기 허용, 커밋 전 데이터 읽기 가능)
- 가장 낮은 격리 수준, 성능은 가장 좋음
- 더티 리드, 반복 가능하지 않은 조회, 팬텀 리드 발생 가능
chatGPT가 알려주는 실생활 비유,,,, (판단은 여러분이)
- READ UNCOMMITTED : 남이 적고 있는 시험지를 옆에서 몰래 훔쳐보는 것 (검토 중인 답안지를 읽음).
- READ COMMITTED : 다른 사람이 답을 제출한 후에야 볼 수 있음.
- REPEATABLE READ : 다른 사람이 답안을 바꿀 수는 없지만, 새로운 답안을 추가할 수 있음.
- SERIALIZABLE : 한 사람씩 시험을 치르게 해서 절대 영향을 주지 않도록 함.
다시 ACID 특징으로 돌아와서,,
지속성(durability)
성공적으로 수행된 트랜잭션은 영윈히 반영되어야 하는 것
DB에 시스템 장애가 발생해도, 회복 기능이 있어야 함 (체크섬, 저널링, 롤백)
*체크섬 : 중복 검사
*저널링 : 변경 사항을 커밋하기 전에 변경 사항에 대한 로그를 남기는 것
4.3.2 무결성
데이터의 정확성, 일관성, 유효성을 유지하는 것
- 개체 무결성 : 기본키로 선택된 필드는 빈 값 X
- 참조 무결성 : 서로 참조 관계에 있는 두 테이블의 데이터는 항상 일관된 값을 유지
- 고유 무결성 : 특정 속성에 대해 고유한 값을 가지도록 조건이 주어진 경우 그 속성 값은 모두 고유한 값
- NULL 무결성 : 특정 속성 값에 NULL이 올 수 없다는 조건이 주어진 경우 그 속성 값은 NULL이 될 수 없다는 제약 조건
4.4 데이터베이스의 종류
4.4.1 관계형 데이터베이스
관게형 데이터베이스(RDBMS) : 표 형식 데이터를 저장하는 DB
ex)MySQL, PostgreSQL, 오라클 등
MySQL
- C/C++ 기반
- MyISAM 인덱스 압축 기술
- B-트리 기반의 인덱스
- 스레드 기반의 메모리 할당 시스템
- 매우 빠른 조인
- 최대 64개의 인덱스
- 대용량 데이터베이스를 위해 설계됨
- 롤백, 커밋, 이중 암호 지원 보안 등의 기능 제공
- 쿼리 캐시 지원
PostgreSQL
- VACUUM 장치가 특징
- 최대 32TB의 테이블 크기
- SQL 뿐만 아니라 JSON 이용해서 데이터 접근 가능
- 지정 시간에 복구하는 기능, 로깅, 접근 제어, 중첩된 트랜잭션, 백업 등 가능
| 선택 기준 | MySQL | PostgreSQL |
| 빠른 읽기 성능이 필요한 경우 | o | x |
| 대규모 트랜잭션, 데이터 일관성 | x | o |
| JSON 및 복잡한 데이터 처리 | x | o |
| GIS(지리정보) 데이터 활용 | x | o |
| 확장성과 동시성 제어 필요 | x | o |
| 간단한 데이터 모델 & 웹사이트 | o | x |
💡 간단한 웹 서비스나 빠른 읽기 성능이 필요하면 MySQL, 복잡한 데이터 처리 및 트랜잭션이 중요하면 PostgreSQL을 선택하는 것이 좋다!
4.4.2 NoSQL 데이터베이스
SQL을 사용하지 않는 DB
ex)MongoDB, redis
MongoDB
- JSON 통해 데이터 접근 가능
- Binary JSON 형태로 데이터가 저장됨
- 도큐먼트 기반의 데이터베이스
- 확장성이 뛰어남 (빅데이터에 굳)
- 스키마를 정하지 않아도 됨
- ObjectID가 생성됨 (도큐먼트를 생성할 때)
redis
- 인메모리 데이터베이스
- 키-값 데이터 모델 기반의 데이터베이스
- 기본 데이터 타임은 문자열, 최대 512MB까지 저장 가능
| 선택 기준 | MongoDB | redis |
| 대량의 데이터 저장 | o | ox |
| 빠른 데이터 조회 | o | x |
| 데이터 분석 및 복잡한 쿼리 | o | o |
| API 응답 속도 개선 | x | o |
| 사용자 세션 저장 | x | o |
| 메시지 큐 / 실시간 처리 | x | o |
| 관계형 데이터 대체 | o | x |
💡 MongoDB는 데이터 저장이 중요한 경우, Redis는 빠른 처리와 캐싱이 중요한 경우에 사용하면 된다.
4.5 인덱스
4.5.1 인덱스의 필요성
테이블 안에서 데이터를 빠르게 찾기 위한 장치
4.5.2 B-트리
인덱스는 B-트리라는 자료 구조로 이루어져 있음
루트 노드 / 브랜치 노드 / 리프 노드

인덱스가 효율적인 이유
균형 잡힌 트리 구조 + 트리 깊이의 대수확장성 때문
*대수확장성 : 트리 깊이가 리프 노드 수에 비해 느리게 성장하는 것
4.5.3 인덱스 만드는 방법
DB마다 다르다! (이 책에서는 MySQL 과 MongoDB를 설명함)
MySQL
- 클러스터형 인덱스 / 세컨더리 인덱스
- 클러스터형 인덱스 : 테이블당 하나
- primary key 옵션
- unique not null 옵션
- 세컨더리 인덱스 : 보조 인덱스 / 여러 개의 필드 값을 기반으로 쿼리해야할 때
- create index 명령어 기반
MongoDB
자동으로 ObjectID가 형성되고 해당 키가 기본키로 설정됨
4.5.4 인덱스 최적화 기법
- 인덱스는 비용이다
* 필드에 인덱스를 다 설정하는 것이 답은 아니다!
- 항상 테스팅하라
* 테스팅을 하며 걸리는 시간을 최소화 해야 함
- 복합 인덱스는 같음, 정렬, 다중 값, 카디널리티 순이다
* 카디널리티란? 유니크한 값의 정도
4.6 조인의 종류
조인 : 두 개 이상의 테이블을 묶어서 하나의 결과물을 만드는 것
MySQL : JOIN / MongoDB : lookup (사용 X)

4.6.1 내부 조인(inner join) : 두 행이 모두 일치하는 행만을 표기

4.6.2 왼쪽 조인 (left outer join) : 왼쪽 테이블의 모든 행 표기

4.6.3 오른쪽 조인 (right outer join) : 오른쪽 테이블의 모든 행 표기

4.6.4 합집합 조인 (full outer join) : 모든 행 표기

4.7 조인의 원리
중첩 루프 조인 / 정렬 병합 조인 / 해시 조인
4.7.1 중첩 루프 조인 (NLJ, Nested Loop Join)
- 중첩 for 문과 같은 원리
- 대용량의 테이블에서 사용 안함 (비용 문제)

4.7.2 정렬 병합 조인
- 각각의 테이블을 조인할 필드 기준으로 정렬
- 정렬 이후에 조인 작업을 수행
- 인덱스가 없을 때
- 대용량의 테이블들을 조인할 때
- 조인 조건으로 범위 비교 연산자가 있을 때
4.7.3 해시 조인
- 해시 테이블을 기반으로 조인
- MySQL의 해시 조인 단계 : 빌드 단계 / 프로브 단계
빌드 단계
- 작은 테이블의 조인 키를 해시 테이블에 저장한다
- 조인 키를 해시 함수로 변환해 특정 버킷에 저장한다

프로브 단계
- 큰 테이블의 데이터를 하나씩 보면서, 해시 테이블에서 같은 키를 가진 값이 있는지 확인한다
