Database ‐ 논리적 모델링 - woojin-playground/Backend-PlayGround GitHub Wiki
다양한 종류의 키
기본키(Primay Key)
- 테이블의 모든 행을 유일하게 식별하는 대표 키다.
- 기본 키가 반드시 지켜야 하는 3가지 규칙
- NULL값을 가질 수 없다.(NULL)
- 반드시 유일해야만 한다.(UNIQUE)
- 값이 변하지 않아야 한다.(불변성)
후보키(Candidate Key)
- 후보키는 말 그대로 기본키가 될 수 있는 후보 선수들을 말한다.
- 기본키가 될 수 있는 자격, 즉 유일성과 최소성을 모두 만족하는 모든 키를 말한다.
- 유일성 : 모든 행을 서로 구분할 수 있어야 한다.
- 최소성 : 행을 유일하게 식별하는 데 꼭 필요한 최소한의 컬럼만 포함해야 한다.
최소성이란?
- 유일성을 만족하더라도 불필요한 컬럼을 포함해 최소성을 만족하지 못하는 키는 후보 키가 될 수 없다.
- 참고로 키를 구성할 때, 여러 칼럼을 묶어서 키를 구성할 수 있는데 이것을 복합키라고 한다.
대체키(Alternate Key)
- 후보키 중에서 기본키로 선택되지 않은 나머지 키들을 말한다.
외래키(Foreign Key)
- 테이블 간의 관계를 연결하는 역할을 한다.
- 한 테이블의 컬럼이 다른 테이블의 기본 키를 참조하는 것이다.
자연 키
- 자연키란, 이름 그대로 비즈니스 로직 안에서 자연스럽게 발생하는 의미를 가진 데이터를 기본 키로 사용하는 것을 말한다.
- 장점
- 직관성 : 키 값 자체에 의미가 있어 값만 봐도 어떤 데이터를 가리키는지 쉽게 이해할 수 있다.
- 중복 방지 : 비즈니스 규칙상 고유해야 하는 값을 PK로 지정하므로, 데이터베이스 차원에서 값의 중복을 원천적으로 막을 수 있다.
- 단점
- 비즈니스 로직에 종속되는 자연키는 변경 불가능이라는 원칙을 지키기 어렵다.
대리 키
- 대리 키의 존재 이유는 단 하나, 절대 변하지 않는다는 것이다.
- 대리 키는 비즈니스와 아무런 관련이 없는 시스템이 자동으로 생성해주는 값이다.
- 보통 1, 2, 3 ...과 같이 순차적으로 증가하는 정수나 전역적으로 고유함이 보장되는 UUID(Universally Unique Identifier)를 사용한다.
UUID(Universally Unique Identifier)
- 이름 그대로 전 세계에서 유일무이할 것으로 기대되는 값을 만들어내는 표준이다.
- 약 1초에 10억 개의 UUID를 100년 동안 계속 만들다보면 한 번 정도 충돌할 수 있다.
복합 키 설계
- 경우에 따라서는 하나의 컬럼만으로 행을 고유하게 식별할 수 없는 상황이 발생할 수 있는데, 이 때 등장하는 것이 바로 복합키(Composite Key)이다.
- 하지만 이런 복합키는 테이블의 크기를 불필요하게 크게 만들며, 조인 쿼리가 복잡해지게 된다.
- 여러 타입의 조합은 저장 공간을 더 많이 차지하며, 인덱스 효율성을 떨어뜨려 쓰기/조회 성능 저하의 원인이 될 수 있다.
- 조회와 관리의 어려움 : 복합키는 직관적이지 않다. 단일 키처럼
WHERE id = 1과 같이 조회할 수가 없어, 쿼리가 길어지고 오류가 발생하기 쉽다. 또한, 실무에서 자주 사용하는 ORM 도구에서 복합키를 다루려면 별도의 식별자 클래스를 만드는 등 추가적인 매핑 로직이 필요해 개발 복잡도가 증가한다. - 확장성 문제 : 시스템이 성장하며 새로운 요구사항 조건이 추가된다면, 기본 키의 구성이 바뀌어야 한다. 이는 테이블 구조의 대대적인 변경을 의미하며, 관련된 모든 테이블과 쿼리에 영향을 미치는 큰 작업이 된다.
- 조회와 관리의 어려움 : 복합키는 직관적이지 않다. 단일 키처럼
복합 키 정리
- 자연 키를 기본으로 사용하려면, 비즈니스 로직상 하나의 컬럼만으로는 부족해 여러 컬럼을 묶은 복합키를 사용해야 하는 경우가 많다.
- 복합키는 데이터를 식별하고 다른 테이블과 관계를 맺을 때, 여러 컬럼을 한 묶음으로 다뤄야 하므로 복잡하고 비효율적이다.
- 반면 대리 키는 비즈니스와 무관한 단 하나의 컬럼으로 데이터를 고유하게 식별한다. 덕분에 다른 테이블과의 관계 역시 이 단일 컬럼 하나로 매우 단순하고 명확하게 표현할 수 있다.
참여도와 카디널리티
- 참여도 : 한 테이블의 행이 관계를 맺고 있는 다른 테이블에 반드시 대응되는 행을 가져야 하는지 아니면 갖지 않아도 되는지를 나타낸다.
- 카디널리티 : 한 테이블의 행이 다른 테이블의 행과 몇 개나 연결될 수 있는지를 나타낸다.
일대다(1:N), 다대일(N:1) 관계에서의 외래 키 위치
- 관계형 데이터베이스는 외래 키를 사용해서 관계를 표현한다.
- 외래 키는 항상 다(N)쪽에 존재해야 한다.
- 만약 일(1)쪽에 외래 키를 두게 되면 여러 행에 나눠 보관하게 되면 구조적으로 문제가 생기며 하나의 컬럼에 모두 보관해야하는데 이렇게 되면 데이터베이스의 원칙을 위배하게 된다.
- 제1정규형 위반 : 관계형 데이터베이스 컬럼은 원자성을 가져야 한다. 즉, 컬럼 하나에는 단 하나의 값만 저장되어야 한다는 원칙이다. 여러 값을 하나의 문자열에 보관한다는 것은 이 원칙을 정면으로 위배한다.
- 데이터 검색의 어려움
- 데이터 수정의 복잡성
- 참조 무결성 제약 불가 : 여러 값을 보관하게 되면서 외래 키 자체를 설정할 수가 없다.
- 그렇다면 하나의 컬럼에 모두를 보관하는 것이 문제가 된다고 했는데 그럼 여러 컬럼에 나누어 보관하면 되지 않을까 생각하겠지만 이 설계 역시 다음과 같은 문제들을 야기한다.
- 확장성 부재
- 공간적 낭비
- 데이터 검색의 어려움
- 반면 다(N)쪽에 외래 키를 두는 이유는 단 하나의 행만 차지하며 데이터 구조가 매우 안정적이고 확장성있게 유지된다.
- 원자성 준수
- 유연성
- 확장성
- 부모 테이블 : 관계에서 일(1)에 해당하는 테이블로, 다른 테이블에 의해 참조되는 쪽이다.
- 자식 테이블 : 관계에서 다(N)에 해당하는 테이블로, 다른 테이블을 참조하는 쪽이다.
- 여기서 자식은 부모에게 의존한다.
- 데이터베이스는 외래 키 제약조건을 통해 부모 테이블에 존재하지 않는 데이터가 자식 테이블에 입력되는 것을 막아 참조 무결성을 지켜준다.
일대다(1:N), 다대일(N:1)에서의 조인 주의점
- 다대일 조인의 경우 다(N)쪽인 테이블을 기준으로 일(1)쪽인 테이블을 조인하면 다(N)쪽을 기준으로 계산된다.
- 일대다 조인의 경우 일(1)쪽인 테이블을 기준으로 다(N)쪽인 테이블을 조인하면 일(1)쪽을 기준으로 계산되기 때문에 N개만큼 늘어나게 되는 것이다.
일대일(1:1) 관계 조인
- 일대일 관계를 사용하는 이유는 여러가지가 있다.
- 성능 최적화를 위한 분리
- 보안 강화를 위한 분리
- 선택적 정보(0 or 1) 표현 및 확장
- 비즈니스 모델에 따른 분리
- 이렇게 테이블을 분리하면 다음과 같은 장점들이 있다.
- 관심사의 분리 : 예를 들어, '주문'과 '배송'이라는 서로 다른 비즈니스 도메인의 데이터를 명확하게 분리해 모델을 더 깔끔하게 이해하고 관리할 수 있다.
- 독립적인 확장성 : 고도화 시 추가 테이블과 관련 로직만 수정하면 된다.
- 진정한 1 : 1 관계를 데이터베이스 레벨에서 강제하려면,
UNIQUE제약조건을 추가하면 된다. - 일대일 관계를 설계할 때, 외래 키를 두는 방법은 향후 요구사항의 변화 시 다(N)로 변할 가능성이 높은 측에 외래 키를 두는 것이 좋다.
다대다(N:M) 관계 조인
- 관계형 데이터베이스에서 테이블 2개만으로 다대다(N:M) 관계를 표현할 수 없다.
- 왜냐하면 관계형 데이터베이스가 외래 키 하나로 단일 행을 가리키는 방식으로 관계를 맺기 때문에 발생하는 구조적인 한계가 있기 때문이다.
다대다 관계 - 연결 테이블
- 다대다 관계를 푸는 표준적인 방법은 중간에 새로운 테이블을 하나 더 만들어서 기존의 다대다 관계를 두 개의 일대다 관계로 풀어내는 것이다.
- 이런 중간 테이블을 연결 테이블 또는 개념적 모델링에서는 연관 엔티티라고 부른다.
- 하나의 주문이 여러 주문 상품들을 가질 수 있다.
- 하나의 상품이 여러 주문 상품에 포함될 수 있다.