CodeGym /행동 /SQL SELF /외래 키 사용할 때 흔히 하는 실수들

외래 키 사용할 때 흔히 하는 실수들

SQL SELF
레벨 20, 레슨 4
사용 가능

우리 모두 사람이니까 실수할 수밖에 없어. 특히 DB에서 외래 키 다룰 때는 더더욱 그렇지. 이번 시간엔 내가 자주 나오는 실수랑 함정들 피하는 방법을 알려줄게. 좋은 데이터베이스는 튼튼한 다리랑 비슷해: 어딘가에서 실수하면 전체 구조가 무너질 수도 있거든. 자, 이제 "데이터 다리"를 잘 관리하는 방법을 알아보자.

실수 1: 외래 키에 인덱스가 없음

외래 키를 추가하면 DB한테 "이 테이블들이 서로 연결되어 있어"라고 알려주는 거야. 근데 외래 키 컬럼에 인덱스를 따로 안 만들어주면, 관련 테이블끼리 복잡한 쿼리 돌릴 때 성능이 확 떨어질 수 있어.

문제 예시:

CREATE TABLE customers (
    customer_id SERIAL PRIMARY KEY,
    name TEXT NOT NULL
);

CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    customer_id INT REFERENCES customers(customer_id)
);

겉보기엔 완벽해 보여: 테이블도 만들었고, 외래 키도 있어. 근데 이런 쿼리를 실행하면:

SELECT * 
FROM orders 
JOIN customers ON orders.customer_id = customers.customer_id;

데이터가 많아지면 이런 쿼리가 엄청 느려질 수 있어. PostgreSQL이 조인 최적화에 쓸만한 인덱스를 못 찾거든.

피하는 방법:

외래 키가 참조하는 컬럼엔 항상 인덱스를 만들어줘. PostgreSQL이 자동으로 해줄 때도 있지만, 직접 해두는 게 안전해.

CREATE INDEX idx_customer_id ON orders(customer_id);

실수 2: 테이블 생성 순서가 잘못됨

테이블을 만들 때, 참조하는 테이블이 아직 없는데 외래 키부터 추가하려고 하면 PostgreSQL이 에러를 뿜어. 왜냐면 참조할 테이블을 못 찾으니까.

문제 예시:

CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    customer_id INT REFERENCES customers(customer_id)
);

-- 어라, customers 테이블이 어디 갔지?..
CREATE TABLE customers (
    customer_id SERIAL PRIMARY KEY,
    name TEXT NOT NULL
);

결과: PostgreSQL이 에러를 내. customers 테이블이 아직 없으니까.

피하는 방법:

먼저 참조할 테이블부터 만들고, 그 다음에 외래 키를 추가해. 순서가 중요해. 이게 올바른 방법이야:

CREATE TABLE customers (
    customer_id SERIAL PRIMARY KEY,
    name TEXT NOT NULL
);

CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    customer_id INT REFERENCES customers(customer_id)
);

실수 3: CASCADE 옵션 문법 실수

외래 키에는 ON DELETE CASCADE나 ON UPDATE RESTRICT 같은 옵션을 자주 써. 근데 이런 규칙을 제대로 안 쓰거나 오타 내면, DB가 예상치 못한 행동을 할 수 있어. 예를 들어, 한 테이블에서 데이터를 지워도 연결된 테이블엔 아무 영향이 없을 수도 있지.

문제 예시:

CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    customer_id INT REFERENCES customers(customer_id) ON DELETE CASCADEE
);

눈썰미 있는 사람은 CASCADEE가 오타라는 걸 바로 알 수 있지. PostgreSQL은 이런 실수를 그냥 넘어가지 않아.

피하는 방법:

정확하게 쓰는 게 반은 먹고 들어가는 거야. 헷갈리면 항상 PostgreSQL 공식 문서를 참고해.

실수 4: 데이터 무결성 깨짐

데이터 무결성은 DB에서 제일 중요한 거야. 외래 키가 그걸 지켜주는 역할을 해. 근데 가끔 외래 키를 빼먹으면, 모든 게 꼬이기 시작하지.

문제 예시:

CREATE TABLE customers (
    customer_id SERIAL PRIMARY KEY,
    name TEXT NOT NULL
);

CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    customer_id INT
);

-- 데이터 삽입
INSERT INTO orders (customer_id) VALUES (999);

여기선 존재하지도 않는 고객한테 주문을 추가했어. 이러면 데이터 무결성이 깨지고, 주문이 "떠돌이"가 돼버려.

피하는 방법:

항상 외래 키를 써서, 한 테이블이 없는 데이터를 참조하지 못하게 막아. 예시를 이렇게 고치면 돼:

CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    customer_id INT REFERENCES customers(customer_id)
);

이제 "떠돌이" 데이터 넣으려고 하면 에러가 나와.

실수 5: 외래 키 에러 무시하기

가끔 개발자들이 INSERT ... ON CONFLICT로 무리하게 안 맞는 데이터를 넣으려고 해. 뭔가 좋아 보이지만, 외래 키랑 같이 쓰면 예상 못한 결과가 나올 수 있어.

문제 예시:

INSERT INTO orders (order_id, customer_id)
VALUES (1, 999)
ON CONFLICT DO NOTHING;

결과: 데이터가 안 들어가는데, DB가 왜 그런지 알려주지도 않아. 이러면 상황을 통제 못하게 돼.

피하는 방법:

ON CONFLICT를 쓸 땐, 항상 데이터를 미리 체크해. 예를 들면:

INSERT INTO orders (order_id, customer_id)
SELECT 1, 999
WHERE EXISTS (
    SELECT 1 FROM customers WHERE customer_id = 999
);

실수 6: ON DELETE 없이 연결된 데이터 삭제

외래 키가 참조하는 테이블에서 데이터를 지웠는데 ON DELETE CASCADE를 안 썼다면, 연결된 데이터가 DB에 남아서 관계가 꼬여버려.

문제 예시:

DELETE FROM customers WHERE customer_id = 1;
-- orders에 customer_id = 1인 데이터는 그대로 남아있음.

피하는 방법:

ON DELETE CASCADE 옵션을 추가해서, 연결된 데이터도 같이 지워지게 해:

CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    customer_id INT REFERENCES customers(customer_id) ON DELETE CASCADE
);

이제 고객을 지우면, 관련 주문도 같이 사라져.

실수 7: MANY-TO-MANY 관계에서의 문제

MANY-TO-MANY 관계를 만들 때, 복합 primary key나 인덱스를 빼먹는 경우가 있어.

문제 예시:

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

-- 어이쿠! PRIMARY KEY를 빼먹었네.

피하는 방법:

복합 primary key나 unique 인덱스를 꼭 추가해:

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

실수 8: 순환 참조

순환 참조는 두 테이블이 서로를 외래 키로 참조할 때 생겨. 이러면 데이터 넣을 때 꼬이고, 골치 아픈 문제가 생겨.

문제 예시:

CREATE TABLE table_a (
    id SERIAL PRIMARY KEY,
    table_b_id INT REFERENCES table_b(id)
);

CREATE TABLE table_b (
    id SERIAL PRIMARY KEY,
    table_a_id INT REFERENCES table_a(id)
);

피하는 방법:

DEFERRABLE INITIALLY DEFERRED 옵션을 써서, PostgreSQL이 트랜잭션 끝나고 무결성 체크하게 해:

CREATE TABLE table_a (
    id SERIAL PRIMARY KEY,
    table_b_id INT REFERENCES table_b(id) DEFERRABLE INITIALLY DEFERRED
);

외래 키 다루면서 생기는 실수들은 개발 속도만 느려지는 게 아니라, 데이터에 심각한 문제를 만들 수도 있어. 이 리스트를 치트키처럼 써서 흔한 "지뢰"를 피하자. 기억해: 외래 키는 적이 아니라 든든한 친구야. 제대로만 쓰면, 네 데이터베이스는 장기 프로젝트의 튼튼한 기반이 될 거야.

2
과제
SQL SELF, 레벨 20, 레슨 4
잠금
CASCADE 업데이트
CASCADE 업데이트
2
과제
SQL SELF, 레벨 20, 레슨 4
잠금
ON DELETE CASCADE 사용하기
ON DELETE CASCADE 사용하기
1
설문조사/퀴즈
데이터 무결성 체크, 레벨 20, 레슨 4
사용 불가능
데이터 무결성 체크
데이터 무결성 체크
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION