데이터베이스 감리 점검: ERD·무결성·접근 권한·백업 복원의 검증 증거
1장. 점검표는 모두 완료인데 실제로는 아무것도 증명되지 않았다#
프로젝트 종료를 앞두고 데이터베이스 점검표가 제출됐습니다.
모든 항목에:
완료가 표시되어 있습니다.
예를 들면 다음과 같습니다.
ERD 작성
완료
외래키 설정
완료
접근 권한 적용
완료
백업 구성
완료
성능 점검
완료보기에는 완벽합니다.
하지만 감리 담당자가 질문합니다.
없는 고객을 참조하는 주문을 실제로 넣어봤습니까?
답:
아니요.보고 계정으로 고객 원본 테이블을 직접 조회하면 실제로 실패합니까?
답:
정책상 안 됩니다.백업 파일을 실제로 복원해서 주문 API까지 연결해 봤습니까?
답:
백업 성공 로그는 있습니다.이 상황에서 완료라는 체크 표시는 실제 시스템 동작을 충분히 설명하지 못합니다.
감리에서 중요한 것은:
설정이 있다고 주장하는 것
이 아니라:
그 설정이 실제로 의도한 결과를 만드는지 재현 가능한 증거로 확인하는 것
입니다.
2장. 데이터베이스 감리는 문서 검사만으로 끝나지 않는다#
데이터베이스 감리에서는 여러 종류의 증거가 필요합니다.
설계 문서
DDL
실제 데이터
실행 결과
권한 시험
성능 측정
복원 시험한 종류의 증거만으로 모든 것을 설명할 수 없습니다.
예를 들어 ERD에:
Customer
1:N
Order가 그려져 있어도 운영 DB에 외래키가 없을 수 있습니다.
반대로 DB에 외래키가 있어도 과거 적재 과정에서 제약을 비활성화해 이미 고아 행이 들어가 있을 수도 있습니다.
3장. 감리의 기본 구조는 주장·검사·관찰·판정이다#
각 점검 항목을 다음 네 단계로 정리하면 명확해집니다.
주장
↓
검사 방법
↓
실제 관찰 결과
↓
판정예:
주장
모든 주문은 존재하는 고객을 참조한다.
검사
고아 주문 조회 + 잘못된 주문 INSERT 시험
관찰
고아 주문 0건
잘못된 INSERT는 FK 오류로 거부
판정
통과이 구조가 있어야 다른 사람이 다시 검사할 수 있습니다.
4장. “정상”이라는 결과에는 반드시 범위가 붙어야 한다#
다음 결과:
고아 주문
0건은 좋아 보입니다.
하지만 실제 검사 범위가:
2026년 9월 주문만이었다면:
전체 주문에 고아 행이 없다.고 결론 내릴 수 없습니다.
또 검사 계정이 일부 행만 볼 수 있다면 0건의 의미는 더 제한됩니다.
따라서 결과에는 최소한 다음 정보가 필요합니다.
대상 DB
스키마
테이블
기간
계정
실행 시각5장. 감리 증거의 최소 단위를 정해 보자#
하나의 점검 결과에는 다음을 남기는 것이 좋습니다.
검사 목적
대상 환경
스키마 버전
실행 계정
실행 시각
검사 SQL 또는 절차
기대 결과
실제 결과
판정
조치 담당자
재검증 결과이 정도가 있어야 완료 표시를 다시 확인할 수 있습니다.
6장. 설계 감리 — ERD가 업무 사건을 실제로 표현하는가#
새 주문 시스템을 검토한다고 하겠습니다.
ERD에는:
Customer
Order
Product
OrderItem이 있습니다.
상자와 관계선은 잘 그려져 있습니다.
하지만 실제 업무 질문을 던져야 합니다.
한 주문에서 같은 상품을 두 줄로 주문할 수 있는가?
부분 환불은 품목 단위인가?
한 주문 품목을 여러 번 나눠 환불할 수 있는가?
주문 당시 상품 가격은 어디에 저장하는가?ERD가 이런 사건을 저장하지 못하면 그림이 깔끔해도 모델은 부족합니다.
7장. 주문 품목의 키는 업무 규칙을 보여준다#
다음 키를 사용한다고 하겠습니다.
PRIMARY KEY (
order_id,
product_id
)이 구조에서는 동일 주문 안에서 같은 상품을 두 행으로 넣을 수 없습니다.
예:
주문 100
상품 P1
수량 1
주문 100
상품 P1
수량 2를 서로 다른 행으로 저장해야 하는 업무라면 문제가 됩니다.
8장. order_line_no 같은 식별자가 필요할 수 있다#
예:
order_id
line_no
product_id
quantity
unit_price키:
PRIMARY KEY (
order_id,
line_no
)로 만들 수 있습니다.
이제 같은 상품도:
주문 100
1번 줄
P1
주문 100
2번 줄
P1처럼 구분할 수 있습니다.
9장. 환불 모델도 실제 사건으로 검증해야 한다#
부분 환불이 가능한 서비스라면 다음 질문이 필요합니다.
주문 전체만 환불 가능한가?
품목별 환불이 가능한가?
수량 일부 환불이 가능한가?
한 품목을 여러 번 나눠 환불할 수 있는가?환불 테이블이:
order_id만 참조한다면 품목 단위 이력을 충분히 표현하지 못할 수 있습니다.
10장. ERD 검증에는 예제 데이터가 강력하다#
업무 담당자에게 최소한 다음 사례를 받습니다.
정상 주문
같은 상품 두 줄 주문
부분 환불 주문이 세 사례를 실제 테이블에 넣어봅니다.
ERD가 업무를 표현하는지 설명하기가 훨씬 쉬워집니다.
11장. ERD의 관계선과 실제 외래키는 별개의 증거다#
ERD:
Customer
1:N
Order라고 그려져 있습니다.
운영 DB를 확인합니다.
SELECT ...메타데이터를 조회해 실제 FK가 있는지 확인합니다.
문서가 맞다고 DB가 자동으로 맞는 것은 아닙니다.
12장. 참조 무결성 감리 — 현재 데이터와 미래 입력을 따로 확인한다#
고객:
customer주문:
orders가 있다고 하겠습니다.
업무 규칙:
모든 주문의 customer_id는 실제 고객을 참조해야 한다.
입니다.
이 규칙을 검증하는 데는 최소 두 가지 증거가 필요합니다.
현재 데이터에 위반이 없는가?
새로운 위반 입력이 차단되는가?13장. 현재 고아 주문을 찾는 SQL#
SELECT
o.id,
o.customer_id
FROM orders AS o
LEFT JOIN customer AS c
ON c.id = o.customer_id
WHERE o.customer_id IS NOT NULL
AND c.id IS NULL;결과:
0행이면 현재 검사 범위에서는 참조되지 않는 고객 ID가 발견되지 않았다는 뜻입니다.
14장. 0행 결과는 외래키 존재를 증명하지 않는다#
고아 데이터가 현재 없다고 해서 앞으로도 잘못된 주문이 들어오지 않는 것은 아닙니다.
예를 들어 FK가 없어도 우연히 현재 데이터가 깨끗할 수 있습니다.
따라서:
현재 상태 검증과:
입력 통제 검증을 구분합니다.
15장. 잘못된 주문을 실제로 넣어본다#
테스트 환경에서:
INSERT INTO orders (
id,
customer_id
)
VALUES (
'O999',
'MISSING_CUSTOMER'
);를 실행합니다.
기대 결과:
외래키 위반으로 INSERT 실패입니다.
실제 결과도 같은지 확인합니다.
16장. “실패했다”는 사실만으로도 부족하다#
INSERT가 실패했습니다.
하지만 이유가:
컬럼 이름 오류였다면 외래키 검증 증거가 아닙니다.
예:
column does not exist로 실패했는데:
FK가 잘 작동했다.고 판정하면 안 됩니다.
반드시 의도한 원인으로 실패했는지 확인해야 합니다.
17장. 실패 메시지와 제약 이름까지 남기면 좋다#
예:
constraint
orders_customer_id_fkey
result
foreign key violation을 기록합니다.
그러면 후속 담당자가 어떤 제약이 동작했는지 확인할 수 있습니다.
18장. 제약을 비활성화한 적재 경로도 확인해야 한다#
대량 적재 작업에서:
외래키 검사 우회
임시 테이블 적재
ETL 후 일괄 반영같은 경로가 있을 수 있습니다.
일반 애플리케이션 INSERT는 안전해도 배치 적재가 무결성을 깨뜨릴 수 있습니다.
따라서 운영 경로 전체를 봐야 합니다.
19장. 현재 위반 0건과 입력 차단 성공을 함께 기록하자#
좋은 결과 예:
검사 대상
production.orders
검사 시각
2026-10-01 03:00 UTC
현재 고아 주문
0건
잘못된 customer_id INSERT
FK 오류로 거부
적재 배치
FK 검사 후 반영
판정
통과이 정도가 되어야 현재 상태와 미래 통제를 함께 설명할 수 있습니다.
20장. 정규화 감리 — “3NF 완료”라는 문구만으로 충분하지 않다#
설계 문서에:
3NF 적용 완료라고 적혀 있습니다.
하지만 감리에서는 질문해야 합니다.
후보키는 무엇인가?
함수 종속은 무엇인가?
왜 이 테이블로 분리했는가?
분해 후 원래 정보를 복원할 수 있는가?
필요한 종속이 보존되는가?정규화 단계 이름만 적는 것은 근거가 아닙니다.
21장. 주문 당시 단가는 현재 상품 가격과 다른 사실이다#
상품 마스터:
product
current_price = 1200주문 당시:
unit_price = 1000이었다고 하겠습니다.
주문 금액을 현재 상품 가격으로 다시 계산하면 과거 매출이 바뀝니다.
따라서 주문 품목에:
unit_price를 저장하는 것은 단순 중복이 아니라 거래 사실 보존일 수 있습니다.
22장. 반정규화라면 동기화 책임을 확인해야 한다#
예를 들어 주문 테이블에:
customer_name을 중복 저장했다고 하겠습니다.
이유가:
조회 성능이라면 감리에서는 다음을 확인합니다.
어떤 SQL이 빨라졌는가?
고객 이름 변경 시 어떻게 동기화하는가?
과거 이름을 보존하려는 것인가?
불일치 감지 SQL이 있는가?중복 저장 자체보다 그 의도를 확인합니다.
23장. 이력 감리 — updated_at 하나가 이력 관리를 의미하지 않는다#
테이블에:
updated_at열이 있습니다.
그래서:
이력 관리 완료라고 적혀 있다고 하겠습니다.
하지만 updated_at은 보통:
마지막으로 수정된 시각만 알려줍니다.
이전 값이 무엇이었는지 알 수 없습니다.
24장. 유효 시각과 기록 시각을 구분해야 할 수도 있다#
예:
급여는 3월 1일부터 340
시스템 정정 입력은 4월 10일입니다.
이 경우:
업무상 유효 시각
3월 1일
시스템이 알게 된 시각
4월 10일이 다릅니다.
감사·정산 시스템이라면 두 시간축이 필요한지 확인해야 합니다.
25장. 기간 이력은 경계와 겹침을 검증해야 한다#
예:
[1월 1일, 3월 1일)
300
[3월 1일, 6월 1일)
330은 자연스럽습니다.
하지만:
[2월 1일, 4월 1일)
350이 추가되면 기간이 겹칩니다.
업무 규칙이 한 시점에 하나의 값만 허용한다면 중복 기간이 거부되어야 합니다.
26장. 표준 코드 감리 — 코드 목록만 있다고 표준화된 것은 아니다#
상태 코드:
01
02
03이 있습니다.
감리에서는 다음을 묻습니다.
각 코드의 의미는 무엇인가?
언제부터 유효했는가?
기관별 원천값은 어떻게 매핑되는가?
코드 의미 변경 이력은 있는가?
미확인 값은 어떻게 처리하는가?값 목록만으로는 충분하지 않습니다.
27장. 코드 변경의 소급 적용 여부도 확인해야 한다#
2026년:
01 = 도서관2027년:
01 = 문화시설이라면 2026년 데이터의 01을 2027년 코드표로 해석하면 안 됩니다.
따라서:
valid_from
valid_to
mapping_version같은 변경 근거가 필요할 수 있습니다.
28장. 접근 권한 감리 — 권한표가 아니라 실제 계정으로 시험한다#
정책 문서:
report_user
매출 집계 조회만 가능이라고 하겠습니다.
실제 시험을 합니다.
허용:
SELECT *
FROM analytics.daily_sales
LIMIT 1;기대:
성공금지:
SELECT *
FROM customer
LIMIT 1;기대:
권한 오류입니다.
29장. 허용 테스트와 거부 테스트를 모두 해야 한다#
금지 SQL만 실패했다고 하겠습니다.
하지만 report_user가:
daily_sales도 읽지 못한다면 최소 권한이 아니라 기능 장애입니다.
따라서:
허용할 것은 성공
금지할 것은 실패해야 합니다.
30장. 직접 권한 외에 역할 상속도 확인한다#
계정:
report_user에게 직접 customer SELECT를 주지 않았습니다.
하지만:
legacy_reader역할을 상속받고 있고 이 역할이 고객 테이블을 읽을 수 있다면 실제 권한은 더 넓습니다.
감리에서는 최종 유효 권한을 확인해야 합니다.
31장. PUBLIC 권한도 빠뜨리면 안 된다#
특정 계정에 권한이 없어도:
PUBLIC에 SELECT가 부여되어 있다면 접근이 가능할 수 있습니다.
확인 범위:
직접 GRANT
역할 상속
PUBLIC
소유자 권한
뷰·함수 간접 접근입니다.
32장. 조회 실패 원인이 권한인지 확인해야 한다#
다음 SQL:
SELECT *
FROM wrong_table_name;이 실패했습니다.
이를:
report_user의 접근이 차단됐다.고 증거로 쓰면 안 됩니다.
실패 원인이:
테이블 없음이기 때문입니다.
권한 시험에서는 정확한 객체와 정확한 권한 오류를 확인해야 합니다.
33장. 개인정보 접근 감리에서는 행·열 범위도 확인한다#
고객센터 역할이:
orders SELECT권한을 가지고 있습니다.
하지만 실제 업무상 필요한 것은:
order_id
status뿐이라고 하겠습니다.
원본 테이블 전체 SELECT는 과도할 수 있습니다.
34장. 뷰를 통한 열 제한을 검증할 수 있다#
예:
CREATE VIEW support_order AS
SELECT
order_id,
customer_id,
status
FROM orders;고객센터 역할:
support_order
조회 가능
orders
직접 조회 불가가 정책이라고 하겠습니다.
실제 두 SQL을 모두 시험합니다.
35장. 행 수준 범위도 별도다#
상담 직원 A는 자신에게 배정된 고객만 볼 수 있다고 하겠습니다.
그렇다면 다음을 시험해야 합니다.
담당 고객 주문
조회 성공
다른 담당자 고객 주문
조회 실패열 제한이 맞다고 행 제한까지 맞는 것은 아닙니다.
36장. 성능 감리 — “인덱스 있음”보다 실제 실행 계획을 본다#
다음 문서가 제출됐습니다.
customer_id 인덱스 적용 완료감리에서는 질문합니다.
어떤 SQL을 개선하려고 만든 인덱스인가?
실제 쿼리는 인덱스를 사용하는가?
읽은 블록 수는 줄었는가?
쓰기 비용은 얼마나 증가했는가?인덱스 존재 여부만으로 성능 개선을 판단할 수 없습니다.
37장. 예상 행과 실제 행의 차이를 확인하자#
예:
Estimated Rows
100
Actual Rows
200,000이라면 옵티마이저의 추정이 크게 빗나갔습니다.
가능한 원인:
통계 부족
데이터 편향
상관관계
조건 선택도 오판등입니다.
감리 기록에는 어느 실행 계획 노드의 숫자인지 명시해야 합니다.
38장. 카디널리티라는 말도 문맥을 적어야 한다#
카디널리티는 상황에 따라:
관계의 전체 행 수를 의미할 수도 있고:
실행 계획 노드의 예상 출력 행 수를 의미할 수도 있습니다.
따라서:
카디널리티가 10만이다.라는 문장만으로는 부족합니다.
무엇의 행 수인지 적어야 합니다.
39장. 선택도도 분모를 명시해야 한다#
전체 후보 행:
1,000,000조건 만족:
1,000이라면 선택도는:
1,000 / 1,000,000
=
0.1%입니다.
하지만 전체 테이블인지, 특정 파티션인지, 조인 중간 결과인지에 따라 분모가 달라집니다.
40장. 전체 스캔은 항상 나쁜 것이 아니다#
테이블의 80%를 읽어야 하는 분석 쿼리라면 순차 스캔이 합리적일 수 있습니다.
반대로 고객 한 명의 주문 10건을 찾는데 전체 테이블을 읽고 있다면 비효율일 가능성이 큽니다.
따라서:
Full Scan 발견
=
지적이라는 단순 규칙은 피해야 합니다.
41장. 실행 계획과 실제 작업량을 함께 기록한다#
예:
반환 행
12
읽은 블록
80,000
Actual Rows
12
Rows Removed by Filter
2,000,000이라면 결과는 적지만 내부 작업량은 큽니다.
성능 판정에는 결과 건수와 실제 접근량을 함께 봐야 합니다.
42장. 느린 SQL 감리 — 사용자 응답시간을 쪼개야 한다#
사용자가:
2초를 기다렸습니다.
내부 측정:
연결 풀 대기
1.5초
DB SQL
0.3초
응답 처리
0.2초라고 하겠습니다.
이 경우:
DB 인덱스 부족을 주원인으로 지적하면 잘못된 판단일 수 있습니다.
43장. 성능 용어는 실제 관찰 위치와 연결해야 한다#
예:
커넥션 풀
→ 연결 획득 대기
락
→ 트랜잭션 충돌
I/O
→ 페이지 읽기
실행 계획
→ 접근·조인 경로
옵티마이저
→ 계획 선택같은 용어를 구체적인 관찰값과 연결해야 원인을 설명할 수 있습니다.
44장. DBMS별 용어를 다른 제품에 그대로 옮기지 말자#
다음은 대표적인 Oracle 문맥의 용어입니다.
AWR
Statspack
SGA
PGA
V$SESSION
래치
언두 세그먼트
INDEX FAST FULL SCANPostgreSQL이나 MySQL에도 비슷한 목적의 구조가 있을 수 있지만 이름과 내부 구현은 다릅니다.
감리 표준에서 제품별 도구명을 섞어 쓰면 실제 검증이 어려워집니다.
45장. SGA와 PGA는 Oracle에서 서로 다른 메모리 영역이다#
개념적으로:
SGA
→ 인스턴스 공유 메모리
PGA
→ 프로세스별 작업 메모리입니다.
정렬·해시 작업의 메모리 문제를 SGA 문제라고 부르면 원인 분석이 어긋날 수 있습니다.
46장. 락과 래치도 같은 것이 아니다#
락:
업무 데이터의 동시 접근·변경 충돌 제어래치:
Oracle 내부 공유 구조의 짧은 동기화맥락으로 이해할 수 있습니다.
행 잠금 대기와 내부 메모리 구조 경합을 같은 문제처럼 표현하면 안 됩니다.
47장. 인덱스 종류에 따라 설명도 달라진다#
다음 설명:
인덱스는 정렬된 자료구조다.는 B-tree 계열을 설명하는 데는 어느 정도 유용합니다.
하지만:
해시 인덱스
비트맵 인덱스
공간 인덱스까지 모두 정확하게 설명하지는 못합니다.
감리 용어집도 제품과 인덱스 유형을 구분해야 합니다.
48장. 백업 감리 — 성공 로그보다 실제 복원을 본다#
백업 시스템:
매일 02:00
BACKUP SUCCESS를 기록합니다.
감리표:
백업 정상이라고 되어 있습니다.
하지만 이 정보만으로 다음을 알 수 없습니다.
실제 파일이 읽히는가?
암호화 키가 있는가?
복원 시간이 얼마나 걸리는가?
어느 시점까지 복구되는가?
애플리케이션이 연결 가능한가?49장. 백업 시험은 분리된 환경에 실제로 복원한다#
예:
운영 백업
↓
격리 복구 환경
↓
DB 복원
↓
마지막 주문 확인
↓
애플리케이션 연결 시험이렇게 해야 실제 복구 가능성을 확인할 수 있습니다.
50장. 복구 시험의 예#
장애 가정:
14:07복원 가능한 마지막 데이터:
14:05서비스 재개:
14:45라고 하겠습니다.
51장. 측정된 데이터 손실 시간#
장애:
14:07마지막 복구:
14:05차이:
2분입니다.
목표 RPO:
1분이라면 목표 미달입니다.
52장. 측정된 업무 복구 시간#
장애:
14:07서비스 정상:
14:45차이:
38분입니다.
목표 RTO:
30분이라면 역시 목표 미달입니다.
53장. DB 복원 완료 시각과 RTO는 같지 않을 수 있다#
DB 자체는:
14:30에 복원됐다고 하겠습니다.
하지만:
권한 확인
애플리케이션 연결
캐시 초기화
주문 API 검사를 거쳐 14:45에 서비스가 정상화됐습니다.
업무 RTO를 본다면 14:45가 더 중요한 시각입니다.
54장. RPO는 실제 거래로 검증하는 것이 좋다#
로그 파일 시각만 확인하지 말고 장애 전에 테스트 주문을 기록합니다.
예:
O100
14:04:20
성공
O101
14:05:40
성공
O102
14:06:30
성공복원 후:
O100 존재
O101 존재
O102 없음을 확인합니다.
그러면 실제 손실 거래를 설명할 수 있습니다.
55장. 마지막 거래 시각과 실제 손실량은 다른 지표다#
마지막 복구 거래:
14:05장애:
14:07이라서:
2분 손실이라고 표현할 수 있습니다.
하지만 그 2분 동안 실제 거래가 하나도 없었다면 누락 건수는:
0건일 수도 있습니다.
따라서:
시간 기반 RPO
실제 누락 거래 수를 함께 기록하는 것이 좋습니다.
56장. 복제본이 있다고 백업 검증을 생략하면 안 된다#
잘못된 DELETE가 주 DB에 커밋되었습니다.
정상 복제는 이를 복제본에도 전달할 수 있습니다.
따라서:
복제본 존재은:
과거 상태 복구 가능과 같은 의미가 아닙니다.
장애 전환과 논리 오류 복구는 별도 시험이 필요합니다.
57장. 데이터 품질 감리 — “품질이 낮다”를 숫자로 바꾸자#
다음 지적:
고객 연락처 데이터 품질이 좋지 않다.는 너무 모호합니다.
다음과 같이 구체화해 보겠습니다.
활성 고객
1,000명
연락처 필수 대상
900명
연락처 입력
873명완전성:
873 / 900
=
97%입니다.
58장. 왜 전체 1,000명을 분모로 사용하지 않는가#
100명은 업무적으로 연락처가 필수가 아니라고 하겠습니다.
이들을 분모에 넣으면:
873 / 1000
=
87.3%가 됩니다.
하지만 실제 필수 대상 완전성을 측정하려는 지표와 맞지 않습니다.
품질 지표는 분모 정의가 핵심입니다.
59장. 개선 후 99%라도 완료가 아닐 수 있다#
수정 후:
891 / 900
=
99%가 되었습니다.
하지만 남은 9명이:
긴급 알림 대상 VIP 고객이라면 영향은 클 수 있습니다.
단순 비율 외에:
치명 예외
중요 대상
업무 영향을 함께 봐야 합니다.
60장. 100%를 만들기 위해 임의값을 채우면 더 나빠질 수 있다#
연락처가 없는 고객에게 다른 필드의 번호를 복사해서:
완전성 100%을 만들었다고 하겠습니다.
형식상 값은 채워졌지만 정확성은 떨어집니다.
완전성 상승
≠
데이터 품질 전체 상승입니다.
61장. 0건이라는 숫자는 언제나 검사 범위와 함께 읽는다#
고아 주문:
0건접근 위반:
0건오류 데이터:
0건이라고 하겠습니다.
항상 물어야 합니다.
어떤 기간인가?
어떤 테이블인가?
전체인가 표본인가?
어떤 권한으로 봤는가?
언제 조회했는가?0은 범위 없는 절대적인 품질 보증이 아닙니다.
62장. 읽기 권한이 제한된 계정으로 검사하면 일부 행이 보이지 않을 수 있다#
점검 계정이:
자기 부서 데이터만 조회 가능이라고 하겠습니다.
이 계정으로:
고아 주문 0건을 얻었다면 전체 회사 데이터에 대한 결과가 아닙니다.
감리 계정의 데이터 가시성도 증거에 포함해야 합니다.
63장. 표본 검사와 전체 검사를 구분하자#
100만 건 중:
1,000건 표본을 검사해 오류가 0건이었습니다.
이 결과는:
검사한 1,000건에서 오류를 발견하지 못함을 의미합니다.
전체 100만 건 오류 0을 의미하지 않습니다.
보고서 표현도 달라야 합니다.
64장. 실행 계획 감리에서도 현재 한 번의 스냅샷이라는 사실을 기억하자#
현재 실행 계획:
Index Scan입니다.
하지만 데이터 분포나 통계가 변하면 다음 달:
Seq Scan이 선택될 수 있습니다.
따라서:
실행 계획 확인 완료만 적기보다 기준 부하와 통계 상태를 남기는 것이 좋습니다.
65장. 계획 이름보다 실제 행·반복·버퍼를 본다#
예:
Nested Loop이 있다고 하겠습니다.
내부 조회는 한 번에 0.1ms입니다.
하지만:
loops
100,000이라면 전체 비용은 커집니다.
연산자 이름만으로 좋은 계획인지 판단하지 않습니다.
66장. 패치 감리 — 버전 번호 하나만 확인하지 않는다#
엔진 버전이 최신이라고 하겠습니다.
감리에서는 다음도 확인할 수 있습니다.
현재 지원 상태
보안 패치 적용 범위
확장 모듈 호환성
애플리케이션 테스트
롤백 절차
유지보수 창버전 업그레이드가 완료됐다고 애플리케이션 호환성까지 자동으로 보장되는 것은 아닙니다.
67장. 장애 대응 감리 — 락 문제도 실제 차단 관계를 확인한다#
느린 SQL이 발견됐습니다.
단순히:
DB 느림으로 기록하기보다:
waiting session
blocking session
transaction age
wait event를 확인합니다.
원인이 긴 트랜잭션이라면 인덱스 조정보다 트랜잭션 범위 개선이 필요할 수 있습니다.
68장. 현재 차단 상태와 전체 지연 시간을 혼동하지 말자#
현재 세션이:
Lock 대기중이라고 하겠습니다.
전체 쿼리 경과:
40초입니다.
그렇다고:
40초 전체가 락 대기였다고 단정하면 안 됩니다.
실행과 I/O 후 마지막 몇 초만 락을 기다렸을 수도 있습니다.
69장. 감리 지적 사항은 재현 절차까지 남겨야 한다#
나쁜 지적:
주문 무결성 문제 있음좋은 지적:
대상
production.orders
검사 시각
2026-10-01 03:00 UTC
재현 SQL
고아 주문 조회
기대 결과
0행
실제 결과
1행
위반 데이터
O9 → M99
조치 책임
주문 시스템 팀
재검증 조건
고아 행 0건 + FK 위반 INSERT 거부이렇게 해야 수정 완료 여부를 객관적으로 판단할 수 있습니다.
70장. 수정 전후 같은 검사 조건을 유지해야 한다#
수정 전:
전체 2026년 데이터를 검사했습니다.
수정 후:
최근 7일만 검사하고 오류 0건이라고 하면 두 결과를 비교할 수 없습니다.
가능하면:
같은 범위
같은 SQL
같은 판정 기준을 유지합니다.
71장. 기준이 바뀌었다면 변경 이유를 기록한다#
업무 규칙 변경으로 연락처 필수 대상이:
900명
→
850명으로 줄었다고 하겠습니다.
완전성이 상승해도 실제 데이터가 좋아진 것이 아니라 분모가 바뀐 영향일 수 있습니다.
따라서 지표 기준 변경을 별도로 기록해야 합니다.
72장. 감리 증거를 유형별로 나누면 이해하기 쉽다#
설계 증거#
ERD
키 정의
함수 종속
데이터 사전구현 증거#
DDL
제약 조건
인덱스
역할상태 증거#
위반 데이터 조회
활성 세션
실행 계획
품질 지표행동 증거#
잘못된 입력 거부
금지 조회 거부
Failover
실제 복원각각 다른 질문에 답합니다.
73장. 가장 강한 증거는 여러 층이 일치하는 것이다#
예:
ERD
고객-주문 FK 표시
DDL
FK 존재
현재 데이터
고아 주문 0
시험 입력
없는 고객 주문 거부네 가지가 모두 일치한다면 참조 무결성에 대한 설명력이 높아집니다.
하나만 확인했을 때보다 훨씬 강한 증거입니다.
74장. 데이터베이스 감리 핵심 용어를 실제 관찰과 연결하자#
| 용어 | 의미 | 확인할 것 |
|---|---|---|
| 카디널리티 | 행 수 또는 계획 노드 출력 행 수 | 어느 단계의 행 수인지 |
| 선택도 | 후보 중 조건을 만족하는 비율 | 분모와 만족 행 수 |
| 실행 계획 | DBMS가 선택한 연산 경로 | 실제 행·loops·버퍼 |
| 인덱스 | 검색·정렬을 돕는 구조 | 조건·유지 비용 |
| MVCC | 버전 기반 가시성 관리 | 격리·장기 트랜잭션 |
| RPO | 허용 가능한 데이터 손실 목표 | 마지막 복구 거래 |
| RTO | 허용 가능한 복구 시간 목표 | 업무 기능 재개 |
| 커넥션 풀 | DB 연결 재사용 계층 | 획득 대기·점유 시간 |
용어 자체보다 어떤 관찰값으로 확인할 수 있는지가 중요합니다.
75장. 옵티마이저#
옵티마이저는 가능한 실행 방법을 비교해 계획을 선택합니다.
감리에서 볼 것은:
옵티마이저가 있다.가 아닙니다.
예:
예상
100행
실제
200,000행처럼 입력 통계와 실제 실행이 크게 다른지 확인합니다.
76장. 클러스터링 팩터#
Oracle의 클러스터링 팩터는 인덱스 순서로 테이블 행을 따라갈 때 테이블 블록 방문 패턴과 관련된 통계입니다.
일반적으로:
테이블 블록 수에 가까움
→ 비교적 모여 있는 경향
행 수에 가까움
→ 비교적 흩어진 경향으로 해석할 수 있지만 절대적인 성능 점수로 사용해서는 안 됩니다.
77장. 바인드 변수#
바인드 변수는 SQL 구조와 입력값을 분리합니다.
예:
customer_id = ?감리에서는:
실제로 값이 바인딩되는가?
문자열 연결 SQL이 남아 있는가?를 확인할 수 있습니다.
보안과 SQL 재사용 측면에서 모두 중요한 항목입니다.
78장. 리두 로그와 언두#
Oracle 문맥에서:
Redo
→ 변경 복구에 필요한 기록
Undo
→ 이전 값 복원·롤백·읽기 일관성 등에 사용할 수 있습니다.
감리에서는 개념 이름만 확인하지 않고:
보존 기간
장애 복구 요구
긴 쿼리
백업 정책과 연결해서 봅니다.
79장. 파티셔닝과 샤딩#
파티셔닝:
하나의 논리 테이블을
DB 내부에서 분할샤딩:
여러 DB 노드에 데이터 분산입니다.
둘 다 데이터를 나누지만 장애·백업·집계 책임은 다릅니다.
감리 문서에서 두 용어를 같은 의미로 쓰지 않도록 확인합니다.
80장. 설계 점검표#
- 주요 업무 사건을 실제 데이터 사례로 검증했는가?
- 후보키와 기본키의 근거가 있는가?
- M:N 관계를 적절한 연결 엔터티로 해소했는가?
- 동일 상품 여러 줄 같은 실제 업무 사례를 저장할 수 있는가?
- 부분 환불 구조가 실제 업무와 맞는가?
- 외래키가 ERD뿐 아니라 DB에 구현됐는가?
- 함수 종속과 정규화 근거가 있는가?
- 반정규화 시 동기화 책임이 있는가?
- 이력의 유효시간과 변경시간을 구분해야 하는가?
- 코드의 의미·유효기간·매핑 이력이 있는가?
81장. 데이터 무결성 점검표#
- PK가 실제 업무 식별 규칙과 맞는가?
- FK 위반 데이터가 존재하는가?
- 없는 부모를 참조하는 INSERT가 거부되는가?
- UNIQUE 위반이 실제로 차단되는가?
- CHECK 조건이 실제 입력에서 작동하는가?
- NULL 허용 여부가 업무 규칙과 맞는가?
- 제약을 우회하는 적재 경로가 있는가?
- 과거 데이터에 이미 위반이 없는가?
- 위반 0건의 검사 범위가 명확한가?
- 수정 후 같은 조건으로 재검사했는가?
82장. 접근 통제 점검표#
- 계정별 업무 역할이 정의되어 있는가?
- 직접 권한을 확인했는가?
- 역할 상속 권한을 확인했는가?
- PUBLIC 권한을 확인했는가?
- 허용 조회가 실제로 성공하는가?
- 금지 조회가 실제 권한 오류로 실패하는가?
- 원본 테이블 대신 제한 뷰를 사용할 수 있는가?
- 민감 열 접근이 제한되는가?
- 행 수준 접근 범위도 검증했는가?
- 감사 로그가 실제 접근을 남기는가?
83장. 성능 점검표#
- 실제 느린 SQL을 기준으로 검사하는가?
- 실행 계획의 예상·실제 행을 비교하는가?
- 반복 횟수 loops를 보는가?
- 버퍼와 물리 읽기를 확인하는가?
- 결과 행 대비 읽은 행이 과도하지 않은가?
- 락 대기와 SQL 계산시간을 분리하는가?
- 연결 풀 대기를 확인하는가?
- 전체 스캔이 업무 특성상 합리적인지 판단하는가?
- 인덱스 쓰기 비용도 확인하는가?
- 개선 전후 같은 조건으로 측정하는가?
84장. 백업·복구 점검표#
- 자동 백업 성공 상태만 확인하고 있지 않은가?
- 실제 격리 환경에 복원했는가?
- 마지막 복구 가능 거래를 확인했는가?
- 실제 누락 거래를 확인했는가?
- 측정 RPO가 목표를 만족하는가?
- DB 복구와 업무 서비스 복구를 구분하는가?
- 측정 RTO가 목표를 만족하는가?
- 애플리케이션 재연결을 시험했는가?
- 권한과 비밀정보가 복원 환경에서도 정상인가?
- 복제본과 백업의 역할을 구분하는가?
- 논리 삭제 사고도 시험했는가?
- 복원 절차를 다른 담당자가 재현할 수 있는가?
85장. 데이터 품질 점검표#
- 품질 지표의 분모가 정의되어 있는가?
- 완전성·정확성·유효성을 구분하는가?
- NULL의 업무 의미를 구분하는가?
- 미수집과 0을 구분하는가?
- 표본 결과를 전체 결과로 확대 해석하지 않는가?
- 검사 계정의 가시성 범위를 알고 있는가?
- 치명적인 예외를 비율과 별도로 보는가?
- 임의값 입력으로 품질 수치를 올리고 있지 않은가?
- 기준일을 남기는가?
- 수정 후 동일 기준으로 재측정하는가?
86장. 감리 지적서에 필요한 최소 항목#
좋은 감리 지적서는 다음 구조를 가질 수 있습니다.
항목
업무 규칙
대상 환경
검사 범위
재현 절차
기대 결과
실제 결과
위험
조치 책임자
수정 기한
재검증 방법
최종 판정단순히:
미흡이라고만 적는 것보다 훨씬 실용적입니다.
87장. 재검증 가능성이 중요한 이유#
감리 담당자가 문제를 발견했습니다.
하지만 한 달 뒤 다른 담당자가 수정 여부를 확인합니다.
원래 검사 방법이 남아 있지 않다면:
무엇이 문제였는가?
어느 데이터였는가?
어떤 조건으로 실패했는가?를 다시 조사해야 합니다.
재현 가능한 기록은 검수 비용도 줄여줍니다.
88장. 자동화할 수 있는 검사는 자동화하는 것이 좋다#
예:
고아 FK 검사
중복 키 검사
NULL 필수값 검사
금지 권한 검사
백업 상태 확인
실행 계획 회귀 검사같은 항목은 일정 부분 자동화할 수 있습니다.
하지만 자동화 결과도 범위와 기준이 필요합니다.
89장. “0건” 자동 검사도 기준이 바뀌면 의미가 달라진다#
어제:
퇴직자 제외오늘:
퇴직자 포함으로 검사 조건이 바뀌었습니다.
둘 다 오류 0건이라도 같은 지표가 아닙니다.
검사 SQL과 버전을 함께 관리하면 이런 차이를 추적하기 쉽습니다.
90장. 감리는 시스템의 완벽함을 증명하는 절차가 아니다#
특정 시점에:
검사 범위 내 위반 없음을 확인했다고 하겠습니다.
이것이:
앞으로 절대 문제가 발생하지 않는다.는 뜻은 아닙니다.
감리는 일정한 범위와 기준 아래에서 시스템의 설계·통제·운영 상태를 검증하는 과정입니다.
91장. 그래서 운영 통제와 반복 검증이 필요하다#
예:
FK 정의
입력 거부
매일 고아 데이터 검사
오류 알림을 함께 운영하면 한 번의 점검보다 지속적인 통제가 됩니다.
감리의 결과를 운영 모니터링으로 연결할 수 있습니다.
92장. 완료 판정을 위한 가장 중요한 질문#
각 점검 항목에서 마지막으로 다음을 묻습니다.
무엇을 확인했는가?
어떤 범위에서 확인했는가?
어떤 계정으로 확인했는가?
언제 확인했는가?
기대 결과는 무엇이었는가?
실제로 무엇이 나왔는가?
다른 사람이 다시 실행할 수 있는가?이 질문에 답하지 못하면 완료 표시의 신뢰도가 낮습니다.
93장. 핵심 정리#
데이터베이스 감리는 체크리스트에 표시를 채우는 작업이 아닙니다.
ERD에서:
고객
1:N
주문이라고 그려져 있다는 사실만으로 참조 무결성이 보장되는 것은 아닙니다.
실제 DB에서:
외래키가 존재하는가?
현재 고아 주문이 없는가?
없는 고객 주문을 입력하면 거부되는가?를 각각 확인해야 합니다.
이 세 가지는 서로 다른 증거입니다.
접근 권한도 마찬가지입니다.
문서에:
report_user
매출 조회만 가능이라고 적혀 있어도 실제로:
daily_sales 조회
→ 성공
customer 원본 조회
→ 권한 오류가 되는지 시험해야 합니다.
성능 점검 역시:
인덱스 있음이라는 사실보다:
실제 SQL
예상 행
실제 행
loops
버퍼
대기 시간을 확인하는 것이 중요합니다.
백업도:
BACKUP SUCCESS만으로 복구 능력을 증명하지 못합니다.
실제로 복원해:
마지막 보존 주문
실제 누락 데이터
DB 복원 시각
애플리케이션 정상화 시각을 확인해야 RPO와 RTO를 평가할 수 있습니다.
데이터 품질 지표 역시 숫자만 보면 안 됩니다.
완전성 97%라고 할 때는:
분모가 누구인가?
필수 대상은 어떻게 정했는가?
치명적인 누락은 없는가?
검사 시점은 언제인가?가 함께 있어야 합니다.
결국 데이터베이스 감리에서 가장 중요한 원칙은 이것입니다.
정책과 설정을 믿는 것이 아니라, 시스템이 실제로 어떻게 행동하는지를 증거로 확인한다.
그리고 그 증거는 다음과 같이 남아야 합니다.
대상
범위
시각
계정
실행 절차
기대 결과
실제 결과
판정그래야 다른 사람이 같은 조건에서 다시 실행해 같은 결론에 도달할 수 있습니다.
좋은 감리 결과는 단순히:
완료라고 적힌 문서가 아닙니다.
왜 완료라고 판정했는지 다시 실행해서 증명할 수 있는 기록입니다.