MMeshOps
바이브코딩을 위한 완벽한 방어선: SQL과 PostgreSQL 기초
7예상 10입문

데이터의 주민등록번호와 연결 고리

고아 데이터를 방지하기 위한 데이터베이스 필수 개념인 기본키(Primary Key), 외래키(Foreign Key), NOT NULL 조건에 대해 알아봅니다.

Part 1에서 우리는 SQL을 이용해 데이터를 어떻게 꺼내오고 수정하는지에 대한 '행동'을 배웠습니다. Part 2부터는 본격적으로 AI가 짜준 코드의 빈틈을 막아내는 데이터베이스의 핵심 역할, '제약 조건(Constraints)'에 대해 알아봅니다. 데이터베이스는 아무 정보나 다 받아주는 쓰레기통이 아닙니다. 규칙에 어긋나는 데이터가 들어오려 하면 단호하게 튕겨내어 데이터의 순수성을 지켜내는 것이 가장 중요한 임무입니다. 그 첫 번째 관문이자 뼈대인 기본키(PK)와 외래키(FK)를 이해해 봅시다.

동명이인을 구별하는 유일한 방법: 기본키(Primary Key, PK)

만약 우리 웹사이트에 '홍길동'이라는 이름을 가진 회원이 두 명 가입했다고 칩시다. 비밀번호를 변경하거나 회원 탈퇴를 시킬 때, 이름이나 이메일(메일도 바뀔 수 있으므로)만으로 사람을 찾으려고 하면 엉뚱한 사람의 계정이 날아가는 대참사가 발생합니다.

엑셀과 달리, 관계형 데이터베이스의 모든 테이블은 이처럼 데이터가 꼬이는 것을 막기 위해 각 줄(Row)마다 절대로 중복되지 않고, 절대로 변하지 않는 '주민등록번호'를 의무적으로 가져야 합니다. 이를 기본키(Primary Key, 줄여서 PK)라고 부릅니다.

테이블을 만들 때 PK 지정하기

sql
```sql
CREATE TABLE users (
  id SERIAL PRIMARY KEY, -- 이것이 바로 절대 변하지 않는 고유 번호입니다.
  name VARCHAR(50),
  email VARCHAR(100)
);
```
  1. 1테이블을 처음 설계할 때 id라는 컬럼을 만들고 PRIMARY KEY라는 훈장을 달아줍니다.
  2. 2PostgreSQL에서는 SERIAL(또는 BIGSERIAL)이라는 타입을 쓰면, 내가 굳이 숫자를 지정하지 않아도 가입할 때마다 알아서 1, 2, 3... 순서대로 고유 번호를 1씩 증가시키며 부여해 줍니다.

우리가 앞서 `UPDATE users SET age=20 WHERE id=5;` 처럼 `WHERE` 조건에 `id`를 넣었던 이유가 바로, PK를 기준으로 수정을 지시해야 단 한 명의 타겟만 정확하게 조준할 수 있기 때문입니다.

고아(Orphan) 데이터를 막는 생명줄: 외래키(Foreign Key, FK)

기본키가 자신의 주민등록번호라면, 외래키(Foreign Key, 줄여서 FK)는 다른 테이블에 있는 사람의 주민등록번호를 가리키는 '화살표'입니다. 이전 시간에 `JOIN`을 배울 때 "이 게시글은 1번 유저(id=1)가 썼다"라고 증거(`user_id=1`)를 남겼던 것 기억하시나요? 그 `user_id` 컬럼이 바로 외래키입니다.

그런데 여기서 무서운 상황을 가정해 봅시다. 1번 유저가 남긴 게시글이 100개가 있는데, 관리자가 1번 유저를 홧김에 강제 탈퇴시켜서 `users` 테이블에서 삭제해 버렸습니다. 그러면 `posts` 테이블에 남아있는 100개의 게시글은 `user_id=1`이라는 꼬리표를 달고 있지만, 1번 유저는 세상에 존재하지 않는 유령이 되어버립니다. 주인이 없는 붕 뜬 데이터, 전문 용어로 고아 데이터(Orphan Data)가 발생한 것입니다. 나중에 누군가 이 게시글을 조회하려고 하면, 글쓴이의 이름을 찾을 수 없어 웹사이트에 치명적인 에러 화면이 뜹니다.

이런 상황을 시스템이 스스로 원천 차단하도록 테이블에 거는 강력한 자물쇠가 바로 '외래키 제약조건'입니다.

외래키 제약조건의 강력한 방패막이

데이터베이스에 "이 게시글의 `user_id`는 반드시 `users` 테이블에 실존하는 `id`여야만 해!"라고 외래키 제약조건을 걸어두면, 1번 유저를 삭제하려고 시도하는 순간 데이터베이스가 에러를 뿜으며 삭제를 거부(Reject)합니다. "안 돼! 이 유저가 쓴 글이 아직 100개나 남아있잖아! 글을 먼저 지우든지, 아니면 나한테 한꺼번에 연쇄 삭제하라고 지시하든지 해!" 라며 시스템 붕괴를 막아줍니다.

바이브코딩 AI는 종종 코드를 짤 때 귀찮다는 이유로 이 외래키 제약조건을 빼먹고 껍데기 컬럼만 덜렁 만들어두는 실수를 저지릅니다. 반드시 AI에게 "모든 연관된 테이블에 Foreign Key 제약조건을 확실하게 걸어줘"라고 당부해야 데이터베이스가 안전해집니다.

절대 비워둘 수 없는 칸: `NOT NULL`

게시글을 쓰려면 반드시 '제목'이 있어야 하고, 회원가입을 하려면 반드시 '이메일'이 있어야 합니다. 만약 코딩 실수로 제목이 텅 빈(Null) 채 데이터베이스에 들어가 버리면 나중에 화면에 그릴 때 제목을 클릭할 수가 없어서 사이트가 망가집니다.

"이 칸은 무슨 일이 있어도 절대 비워둔 채로 저장할 수 없다"라고 데이터베이스에게 멱살을 잡고 신신당부하는 제약조건이 `NOT NULL`입니다. 프론트엔드 화면에서 "제목을 입력해 주세요"라고 막는 것은 너무 얕은 방어입니다. 나쁜 해커나 버그 난 코드가 화면을 우회해서 데이터를 꽂아 넣으려 할 때, 최후의 보루인 데이터베이스가 `NOT NULL` 방패로 에러를 내며 튕겨내야만 진짜 안전한 서버가 됩니다.

개념 퀴즈

퀴즈 답을 맞춰야 학습 완료가 됩니다.

우리 서비스의 '주문 내역' 테이블에는 누가 주문했는지 알기 위해 회원 테이블의 주민등록번호를 가리키는 'user_id' 컬럼이 있습니다. 만약 실수로 데이터베이스에서 특정 회원을 삭제하려 할 때, 해당 회원이 이미 주문한 내역이 존재한다면 삭제가 거부되도록 시스템적으로 강제 방어선을 구축해 주는 제약 조건의 이름은 무엇인가요?

3줄 요약

  1. 1기본키(Primary Key, PK)는 각 테이블의 데이터 한 줄 한 줄을 절대 헷갈리지 않게 고유하게 식별해 주는 핵심 번호(주민등록번호)입니다.
  2. 2외래키(Foreign Key, FK)는 다른 테이블의 기본키를 가리키며 데이터를 연결하고, 고아 데이터가 발생하지 않도록 삭제나 수정을 엄격하게 통제합니다.
  3. 3바이브코딩 시 AI에게 PK, FK, 그리고 값이 비어있음을 허락하지 않는 NOT NULL 제약조건을 확실히 걸어달라고 지시하는 것이 무결성 보호의 첫걸음입니다.