본문 바로가기
나의 개발/데이터베이스

DB 스키마와 ERD를 설계·확장·운영하려면 어떻게 해야 할까? (요약)

by Parkej 2025. 12. 20.

백엔드 개발에서 스키마와 ERD는
한 번 그리고 끝나는 문서가 아니라, 계속 변경되고 운영되는 구조다.
따라서 “정답 구조”보다 어떤 기준으로 선택했는지가 중요하다.

이 글에서는 실무에서 자주 마주치는 고민들을 기준으로
설계 → 확장 → 마이그레이션 → 인덱스 → 동시성 → 삭제 전략까지 정리한다.


1. 도메인에 종속되지 않는 공용 테이블을 설계하려면 어떻게 해야 할까?

문제

  • 이미지, 첨부파일 같은 리소스는
    유저, 게시글, 작가 등 여러 도메인에서 사용될 수 있다.
  • 특정 도메인에 종속되면 재사용과 확장이 어려워진다.

접근 방법

  • 리소스 자체를 표현하는 테이블은 독립적으로 둔다
    • 예: image, s3_image
  • 도메인 테이블에서 해당 리소스를 참조(FK) 한다.
 
user
- id
- profile_image_id (FK → image.id)

image
- id
- s3_key
- url

나은 선택

  • 도메인이 이미지를 1개만 가진다면 → FK 컬럼
  • 여러 개 / 역할 / 정렬 / 이력이 필요하면 → 연결 테이블
user_image
- user_id
- image_id
- role
- sort_order
 

결론

리소스는 독립적으로,
관계는 도메인 쪽에서 참조하는 방향이 확장에 유리하다.


2. ORM에서 N:M 관계를 바로 쓰지 않으려면 어떻게 판단해야 할까?

문제

ORM의 ManyToMany는 편하지만, 실무에서는 잘 쓰이지 않는다.

이유

  • 관계 자체에 속성이 생긴다
    • 생성 시점, 상태, 역할, 정렬
  • 쿼리 제어가 어렵다
  • eager / cascade로 인한 성능·운영 리스크

해결 방식: 연결 엔티티

N:M을 두 개의 1:N으로 풀어낸다.

 
post_like
- user_id
- post_id
- created_at
- status

결론

관계가 단순 매핑을 넘어 “의미를 갖는 순간”,
연결 테이블은 엔티티가 된다.


3. 확장 요구사항이 생겼을 때 마이그레이션은 어떻게 해야 할까?

상황

  • user.profile_image_id → 유저가 여러 이미지를 가질 수 있게 변경

단계적 접근 (Expand → Migrate → Contract)

  1. 기존 구조 유지 + 신규 테이블 추가
  2. 기존 데이터를 신규 구조로 이관(backfill)
  3. 일정 기간 함께 유지하며 검증
  4. 완전 전환 후 기존 컬럼 제거

중요한 포인트

  • 한 번에 구조를 바꾸지 않는다
  • 데이터 이관은 배치/스크립트로 수행
  • 재시작 가능한 구조(멱등성)를 만든다

결론

마이그레이션은 “한 번에 끝내는 작업”이 아니라
단계적으로 안전하게 옮기는 과정이다.


4. dual-write는 어떻게 구현하고 왜 필요한가?

언제 필요한가

  • 신규 구조로 전환 중
  • 기존 데이터와 신규 데이터의 정합성을 유지해야 할 때

개념

  • 전환 기간 동안 기존 컬럼 + 신규 테이블에 동시에 기록
  • 같은 트랜잭션으로 묶어 원자성 보장

장점

  • 데이터 누락 방지
  • 점진적 전환 가능

결론

dual-write는 임시 전략이지만,
무중단 전환을 가능하게 하는 안전장치다.


5. “트랜잭션을 짧게 유지한다”는 건 무슨 의미일까?

오해

  • timeout 설정
  • 트랜잭션 옵션 조절

실제 의미

  • 한 트랜잭션이 처리하는 데이터 양을 줄인다
  • 오래 걸리는 작업은 트랜잭션 밖으로 분리

배치에서의 적용

  • 대량 데이터 → chunk 단위로 처리
  • N건 처리 → commit → 다음 N건

결론

트랜잭션을 짧게 유지한다는 건
작게 나누고 자주 커밋한다는 뜻이다.


6. 중복 데이터 문제를 락으로 해결해야 할까?

결론부터

락은 최선이 아니라 최후의 수단이다.

우선순위

  1. DB 무결성 제약(UNIQUE)
  2. 멱등성 설계(upsert)
  3. 배치 분할 + 재시도
  4. 그 다음에 락

락이 필요한 경우

  • 재고 차감
  • 포인트 사용
  • 상태 전이처럼 경쟁이 치명적인 경우

결론

중복 insert 문제는
락의 문제가 아니라 설계의 문제다.


7. 인덱스는 언제, 어떤 기준으로 추가해야 할까?

기본 원칙

  • 실제 쿼리 패턴 기준
    • WHERE
    • JOIN
    • ORDER BY
    • LIMIT

외래키 인덱스

  • JOIN 조건에 자주 쓰이면 필수
  • FK 제약과 인덱스는 별개

인덱스가 효과 없는 경우

  • 조건 없는 전체 정렬
  • 낮은 카디널리티 컬럼
  • 쓰기 비중이 매우 높은 테이블

결론

인덱스는 “있으면 좋은 것”이 아니라
필요할 때만 추가하는 도구다.


8. soft delete와 UNIQUE 제약은 어떻게 함께 써야 할까?

문제

  • soft delete 시 기존 row가 남아 UNIQUE 충돌 발생

해결

  • 활성 데이터만 유니크하게 보장
    • (user_id, post_id, deleted_at)
    • 또는 deleted_at IS NULL partial index

철학

무결성은 애플리케이션이 아니라
DB가 최종적으로 보장해야 한다.


9. soft delete 대신 hard delete는 언제 쓸까?

hard delete가 적합한 경우

  • 임시 데이터
  • 보안/개인정보
  • 재생성 가능한 데이터

soft delete가 적합한 경우

  • 주문/결제
  • 이력/감사
  • 통계에 필요한 데이터

결론

삭제 전략은 기술 문제가 아니라
데이터 생명주기 문제다.


전체 결론

  • 스키마와 ERD 설계에는 “정답 구조”가 없다
  • 중요한 건:
    • 요구사항
    • 변경 가능성
    • 운영 시나리오
  • DB는 단순 저장소가 아니라 규칙을 강제하는 계층
  • 락, 인덱스, soft delete는 최후의 선택지

좋은 설계란
“지금 요구사항에 맞으면서,
바뀔 때 무너지지 않는 구조”다.

반응형

댓글