6-1. 해결책: 연결 테이블(Junction Table)

다대다 관계를 푸는 표준적인 해법은 중간에 새로운 테이블을 하나 만드는 것이다. 이 테이블을 보통 다음과 같이 부른다.

핵심 아이디어는 간단하다.

"다대다(M:N) 관계를 두 개의 일대다(1:N, N:1) 관계로 분해한다."

쇼핑몰 예시에서, orders와 product 사이에 order_product라는 연결 테이블을 만든다.

orders (M)  <----->  (N) product

==> 연결 테이블 추가 후

orders (1)  <-----> (N) order_product (N) <-----> (1) product

이제 의미는 이렇게 바뀐다.

  1. 하나의 주문(orders)은 여러 주문 상품(order_product)을 가진다. → 1:N
  2. 하나의 상품(product)은 여러 주문 상품(order_product)에 포함될 수 있다. → 1:N

6-2. 연결 테이블 예시 – 테이블 구조

다음은 productordersorder_product 세 테이블을 사용하는 예시다.

-- 상품 테이블
CREATE TABLE product (
  product_id   BIGINT PRIMARY KEY,
  name         VARCHAR(100) NOT NULL,
  price        INT          NOT NULL
);

-- 주문 테이블
CREATE TABLE orders (
  order_id     BIGINT PRIMARY KEY,
  orderer_id   BIGINT      NOT NULL,
  ordered_at   DATETIME    NOT NULL
);

-- 주문-상품 연결 테이블
CREATE TABLE order_product (
  order_product_id BIGINT PRIMARY KEY,
  order_id         BIGINT NOT NULL,
  product_id       BIGINT NOT NULL,
  order_price      INT    NOT NULL,  -- 주문 당시 가격
  quantity         INT    NOT NULL,  -- 주문 수량
  CONSTRAINT fk_order_product_order
    FOREIGN KEY (order_id)   REFERENCES orders(order_id),
  CONSTRAINT fk_order_product_product
    FOREIGN KEY (product_id) REFERENCES product(product_id)
);

샘플 데이터까지 넣어서 보면 더 직관적이다.

-- product
INSERT INTO product (product_id, name, price) VALUES
(101, '기계식 키보드', 135000),
(102, '무소음 마우스',  45000),
(103, '4K 모니터',    350000);

-- orders
INSERT INTO orders (order_id, orderer_id, ordered_at) VALUES
(1001, 1, '2025-08-06'),
(1002, 3, '2025-08-07'),
(1003, 1, '2025-08-08'),
(1004, 2, '2025-08-08');

-- order_product (연결 테이블)
INSERT INTO order_product
(order_product_id, order_id, product_id, order_price, quantity) VALUES
(201, 1001, 101, 120000, 2),
(202, 1001, 102,  45000, 1),
(203, 1002, 103, 350000, 1),
(204, 1003, 102,  45000, 1),
(205, 1004, 101, 135000, 1),
(206, 1004, 103, 350000, 1);

이를 표로 나타내면 다음과 같다.

product