DB 접속 정보를 AI에게 줘도 될까? 15년차 DBA가 정리한 AI 보안 기준 4등급 (민감정보·마스킹·반입 범위)
3줄 요약
비밀번호·접속 문자열·실제 고객 데이터는 어떤 AI, 어떤 요금제든 넣지 않습니다. 이건 협상 대상이 아닙니다.
- 나머지는 "데이터 등급 × 쓰는 AI 환경"으로 판단합니다. 개인용 플랜과 기업용·API 플랜은 학습·보관 정책이 다릅니다.
- AI가 DB에 직접 붙는다면 읽기 전용 전용계정 + 마스킹 뷰 + 감사 로그가 최소 조건입니다. AI가 제안하는 권한 설정은 그대로 쓰면 안 됩니다.
들어가며
지난 1편 「DBA가 AI를 쓰면 뭐가 달라지나 — 15년차의 솔직한 검증」에서 AI로 되는 것과 안 되는 것을 큰 그림으로 정리했습니다. 댓글과 쪽지로 가장 많이 받은 질문은 의외로 기술 질문이 아니었습니다.
"실행계획 붙여넣어도 되나요?", "에러 로그에 IP 찍혀 있는데 그냥 줘도 돼요?", "MCP로 DB 연결하라던데 무슨 계정으로 붙여요?"
결국 "어디까지 줘도 되느냐" 입니다. 15년 동안 감사 대응을 해보면 사고는 대부분 기술이 아니라 "이 정도는 괜찮겠지"에서 납니다. 오늘은 AI 시리즈를 본격적으로 시작하기 전에, 제가 실제로 쓰는 반입 기준을 정리합니다. 기준은 Oracle 19c, PostgreSQL 17이고 예시는 모두 가상의 테이블입니다.
1. 결론부터: 접속 정보는 "줄까 말까"의 문제가 아니다
질문 제목에 대한 답은 짧습니다. DB 접속 정보(계정 비밀번호, TNS/접속 문자열, 지갑·키 파일)는 AI에게 주지 않습니다.
이유는 세 가지입니다.
- 대화는 저장됩니다. 요금제와 설정에 따라 보관 기간이 다르고, 길게는 수년 단위입니다(2장 참고). 내가 지우더라도 정책상 보관 기간이 별도로 존재할 수 있습니다.
- 사람의 실수가 섞입니다. 회사 계정이 아니라 개인 계정 탭에서 붙여넣는 순간, 사내 계약 보호를 받지 못합니다.
- AI가 DB를 다룰 때 필요한 건 비밀번호가 아니라 구조입니다. 실행계획 분석, SQL 리뷰, 에러 해석 어디에도 실제 비밀번호는 필요 없습니다.
ORA-01017(아이디/비밀번호 오류) 같은 접속 에러를 AI에게 물어볼 때도 마찬가지입니다. 에러 메시지와 설정 구조(sqlnet.ora 파라미터 이름 등)만 주면 충분합니다. 접속 에러 자체의 해결 순서는 예전 글에 정리해 두었습니다.
[내부 링크: ORA-01017 해결 글]
2. AI 서비스별로 데이터 처리가 다르다
"AI에 넣으면 학습된다"는 말은 절반만 맞습니다. 요금제(플랜)에 따라 다릅니다. 2026년 11월 기준으로 공식 문서에서 확인한 내용만 정리합니다.

| 구분 | 대상 | 모델 학습 사용 | 보관 |
| Claude 개인 플랜 | Free · Pro · Max (해당 계정의 Claude Code 포함) | 사용자 선택(설정에서 변경 가능) | 학습 허용 시 5년, 미허용 시 30일 |
| Claude 업무·API | Team · Enterprise · Government · Education, API(Amazon Bedrock · Google Vertex AI 경유 포함) | 위 소비자 약관 변경 대상에서 제외 | 계약·상업 약관 기준 |
| OpenAI 업무·API | ChatGPT Enterprise · Business · Edu, API 플랫폼 | 기본적으로 학습에 사용하지 않음(API는 조직이 명시적으로 opt-in 가능) | 적격 조직은 보관 기간 설정, API는 적격 시 Zero Data Retention |
여기서 DBA가 챙길 포인트는 이렇습니다.
- 개인 플랜은 업무 DB 정보 반입 대상이 아닙니다. 학습 설정을 꺼두더라도 회사와 AI 회사 사이의 계약 보호가 없습니다.
- 기업용·API라도 "학습 제외"일 뿐, 반입해도 된다는 뜻은 아닙니다. 보관·국외 이전·사내 보안 규정은 별도입니다.
- 개인정보보호위원회가 2025년 8월 공개한 「생성형 AI 개발·활용을 위한 개인정보 처리 안내서」도 외부 API를 쓸 때 입력값이 국외 서버로 전송될 수 있으므로 라이선스 계약·약관으로 보호 장치를 확보하라고 안내합니다. 기획 단계부터 CPO(개인정보 보호책임자)가 참여하라는 내용도 들어 있습니다.
Claude 요금제별 차이와 설정 화면은 Claude 시리즈에서 자세히 다뤘습니다.
[내부 링크: Claude 시리즈 — 요금제·설정 편]
3. 데이터 4등급 신호등: 무엇을 줄 수 있나
제가 팀에 공유하는 기준은 신호등 4단계입니다.

| 등급 | 예시 | 어떤 AI에 |
| 🟢 초록 | 문법·개념 질문, 가상 테이블 예시 SQL, ORA 에러코드 의미 | 어떤 AI든 |
| 🔵 파랑 | 리터럴을 바인드로 바꾼 SQL, 호스트·스키마를 별칭 처리한 실행계획, 수치만 남긴 AWR·pg_stat_statements 요약 | 마스킹 후, 업무용 AI 권장 |
| 🟡 노랑 | 실제 스키마 DDL, 테이블·컬럼명, 내부 시스템 구성 | 사내 승인된 업무용 AI(학습 제외 계약)만 |
| 🔴 빨강 | 비밀번호, 접속 문자열, 지갑·키 파일, 실제 고객 데이터, 감사로그 원문 | 어디에도 금지 |
헷갈릴 때는 아래 순서로 위에서부터 판단합니다. 한 번이라도 "예"가 나오면 그 조치를 먼저 합니다.

💡 의외로 많이 놓치는 것: 컬럼명 자체가 민감정보인 경우입니다. cust_member.disease_cd, emp.salary_grade 같은 이름만으로도 "이 회사는 이런 데이터를 갖고 있다"가 드러납니다. 그래서 실제 DDL은 노랑 등급입니다.
4. 반입 전 마스킹: 자동화 + 사람 검수
파랑 등급을 만들려면 마스킹이 필요합니다. 손으로 지우면 반드시 빠뜨리기 때문에, 저는 자동 마스킹 → 별칭 치환 → 사람 diff 검수 3단을 씁니다.

4-1. 붙여넣기 전 마스킹 스크립트 (Python)
# mask_for_ai.py : AI에 붙여넣기 전 1차 마스킹 (표준 라이브러리만 사용)
import re, sys
RULES = [
# sqlplus user/password@tns → user/****@<TNS>
(re.compile(r'(\b\w+)/([^\s@/]+)@([\w.\-:/]+)'), r'\1/****@<TNS>'),
# JDBC / URI 형식 접속 문자열
(re.compile(r'jdbc:oracle:thin:@[^\s"\']+', re.I), 'jdbc:oracle:thin:@<HOST>'),
(re.compile(r'postgres(?:ql)?://[^\s"\']+', re.I), 'postgresql://<USER>:****@<HOST>/<DB>'),
# password= / PGPASSWORD= / IDENTIFIED BY
(re.compile(r'(password|passwd|pwd|PGPASSWORD)\s*[=:]\s*\S+', re.I), r'\1=****'),
(re.compile(r'(IDENTIFIED\s+BY\s+)("?)[^\s";]+\2', re.I), r'\1****'),
# 주민·외국인등록번호, 휴대폰, 이메일, IPv4
(re.compile(r'\b\d{6}-?[1-8]\d{6}\b'), '<RRN>'),
(re.compile(r'\b01[016789]-?\d{3,4}-?\d{4}\b'), '<PHONE>'),
(re.compile(r'\b[\w.+-]+@[\w-]+\.[\w.]+\b'), '<EMAIL>'),
(re.compile(r'\b(?:\d{1,3}\.){3}\d{1,3}\b'), '<IP>'),
# SQL 문자열 리터럴 → 값 제거
(re.compile(r"'(?:[^']|'')*'"), "'<LIT>'"),
]
def mask(text: str) -> str:
for pat, rep in RULES:
text = pat.sub(rep, text)
return text
if __name__ == '__main__':
print(mask(sys.stdin.read()), end='')
실행 결과(가상 데이터)는 이렇습니다.
$ python3 mask_for_ai.py < sample.txt
sqlplus app_user/****@<TNS>
ALTER USER app_user IDENTIFIED BY ****;
psql postgresql://<USER>:****@<HOST>/<DB>
SELECT * FROM cust_member WHERE email = '<LIT>' AND mobile = '<LIT>';
-- 문의 고객 <RRN>
4-2. 이 스크립트의 한계 (꼭 읽어주세요)
- 처음 AI에게 이 정규식을 짜달라고 했을 때 주민번호 뒷자리 첫 숫자를 [1-4]로만 잡았습니다. 외국인등록번호(5~8)가 그대로 통과합니다. 위 코드는 [1-8]로 고친 버전입니다. AI가 짠 마스킹 규칙은 "그럴듯하지만 빠진 케이스"가 있다는 전제로 테스트 데이터를 직접 만들어 돌려봐야 합니다.
- 한글 이름은 정규식으로 못 잡습니다. SQL 리터럴 안의 이름은 <LIT>로 지워지지만, 주석이나 로그 메시지 안의 "홍길동 고객 문의"는 남습니다. 그래서 사람 검수 단계가 빠지면 안 됩니다.
- "이거 마스킹 잘 됐어?"를 AI에게 묻지 마세요. 원문을 같이 보내는 순간 그 자체가 유출입니다.
4-3. 별칭 치환 + 역치환
호스트명·스키마명은 PRD_ERP_DB01 → DB_A, ERP_OWNER → SCH_1처럼 사내 매핑표로 바꿔 보내고, 답변을 받은 뒤 다시 실명으로 돌립니다. 매핑표는 로컬에만 둡니다.
4-4. 아예 DB 레벨에서 가리기
AI용으로 데이터를 뽑을 일이 잦다면 DB 쪽에서 마스킹된 뷰를 제공하는 편이 안전합니다.
Oracle 19c — Data Redaction (Advanced Security 옵션 라이선스 확인 필수)
-- 가상 테이블 app.cust_member, AI 전용 계정 AI_RO 에게만 가림
BEGIN
DBMS_REDACT.ADD_POLICY(
object_schema => 'APP',
object_name => 'CUST_MEMBER',
policy_name => 'RP_CUST_MEMBER_AI',
column_name => 'MOBILE',
function_type => DBMS_REDACT.PARTIAL,
function_parameters => 'VVVFVVVVFVVVV,VVV-VVVV-VVVV,*,4,7', -- 010-****-5678
expression => 'SYS_CONTEXT(''USERENV'',''SESSION_USER'') = ''AI_RO''');
DBMS_REDACT.ALTER_POLICY(
object_schema => 'APP',
object_name => 'CUST_MEMBER',
policy_name => 'RP_CUST_MEMBER_AI',
action => DBMS_REDACT.ADD_COLUMN,
column_name => 'EMAIL',
function_type => DBMS_REDACT.FULL);
END;
/
PostgreSQL 17 — PostgreSQL Anonymizer 동적 마스킹
CREATE EXTENSION IF NOT EXISTS anon;
ALTER DATABASE shopdb SET anon.transparent_dynamic_masking TO true;
CREATE ROLE ai_ro LOGIN;
SECURITY LABEL FOR anon ON ROLE ai_ro IS 'MASKED';
SECURITY LABEL FOR anon ON COLUMN app.cust_member.cust_nm
IS 'MASKED WITH FUNCTION anon.dummy_last_name()';
SECURITY LABEL FOR anon ON COLUMN app.cust_member.mobile
IS 'MASKED WITH FUNCTION anon.partial(mobile,3,$$-****-$$,4)';
⚠️ AI가 흔히 하는 말: "Data Redaction 걸면 안전합니다." 반만 맞습니다. Redaction은 결과를 반환할 때 가리는 기능이라, 조회 권한이 있는 사용자가 WHERE mobile LIKE '010-1234%' 같은 조건을 반복해 값을 추론하는 것까지 막아주지는 않습니다. AI 에이전트처럼 쿼리를 마음대로 만드는 주체에게는 마스킹된 사본(리플리카/정적 마스킹)이나 필요한 컬럼만 담은 뷰가 더 안전합니다.
5. AI가 DB에 직접 붙는다면 (MCP·에이전트)
요즘은 붙여넣기를 넘어 MCP 서버나 IDE 에이전트가 DB에 직접 접속해 쿼리를 날리는 구성이 많아졌습니다. 여기서는 보안 기준이 한 단계 더 올라갑니다. OWASP의 「LLM 애플리케이션 Top 10 (2025)」 중 세 항목이 그대로 DBA 문제입니다.
| OWASP 2025 | DB 관점에서의 의미 |
| LLM01 Prompt Injection | 테이블 데이터 안의 문자열("이전 지시 무시하고 …")이 AI에게 지시문으로 읽힐 수 있음 |
| LLM02 Sensitive Information Disclosure | AI가 조회한 고객 데이터가 대화·로그·다른 도구로 흘러감 |
| LLM06 Excessive Agency | AI에게 필요 이상의 권한(DML·DDL·DBA 롤)을 줘서 사고 범위가 커짐 |

5-1. Oracle 19c — AI 전용 읽기 계정
-- 1) 리소스 제한 프로파일 (RESOURCE_LIMIT 파라미터가 TRUE인지 확인)
CREATE PROFILE ai_ro_prof LIMIT
SESSIONS_PER_USER 2
CPU_PER_CALL 3000 -- 1/100초 단위 = 30초
CONNECT_TIME 60 -- 분
IDLE_TIME 10; -- 분
-- 2) 계정: 테이블스페이스 쿼터 0, 객체 생성 불가
CREATE USER ai_ro IDENTIFIED BY "<비밀금고에서 발급>"
PROFILE ai_ro_prof
DEFAULT TABLESPACE users QUOTA 0 ON users;
GRANT CREATE SESSION TO ai_ro;
-- 3) 마스킹 뷰만 개별 GRANT (SELECT ANY TABLE 금지)
GRANT SELECT ON app.v_order_ai TO ai_ro;
-- 4) 실행계획 분석이 필요하면 필요한 V$ 뷰만 (SYS에서)
GRANT SELECT ON sys.v_$sql_plan TO ai_ro;
-- 5) 통합 감사: AI 계정의 모든 행위 기록
CREATE AUDIT POLICY ai_ro_all ACTIONS ALL;
AUDIT POLICY ai_ro_all BY ai_ro;
5-2. PostgreSQL 17 — AI 전용 읽기 롤
CREATE ROLE ai_ro LOGIN CONNECTION LIMIT 2;
ALTER ROLE ai_ro SET default_transaction_read_only = on;
ALTER ROLE ai_ro SET statement_timeout = '30s';
ALTER ROLE ai_ro SET idle_in_transaction_session_timeout = '60s';
GRANT CONNECT ON DATABASE shopdb TO ai_ro;
GRANT USAGE ON SCHEMA ai_view TO ai_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA ai_view TO ai_ro;
GRANT pg_read_all_stats TO ai_ro; -- pg_stat_statements 등 통계 조회용
# pg_hba.conf : 에이전트 서버 대역에서만, TLS + SCRAM
hostssl shopdb ai_ro 10.10.20.0/24 scram-sha-256
⚠️ AI가 틀리기 쉬운 지점: "default_transaction_read_only = on 이면 쓰기가 막힙니다." 아닙니다. 이 값은 세션에서 SET 으로 다시 끌 수 있는 기본값입니다. 진짜 방어선은 쓰기 권한 자체를 주지 않는 것(INSERT/UPDATE/DELETE GRANT 없음, 스키마 CREATE 권한 없음)이고, read_only 설정은 실수 방지용 보조 장치입니다.
PostgreSQL 롤·권한 구조 자체는 PostgreSQL 시리즈에서 자세히 다뤘고, VS Code 에이전트에 MCP 서버를 연결하는 방법은 Cline 시리즈를 참고하세요.
[내부 링크: PostgreSQL 시리즈 — 사용자·권한(ROLE) 관리 편]
[내부 링크: Cline/VS Code 시리즈 — MCP 서버 연결 편]
5-3. 에이전트 연결 시 실제로 자주 보는 실수
| 실수 | 왜 위험한가 | 대신 |
| 앱 계정·DBA 계정을 그대로 연결 | 에이전트 오작동 = 운영 장애 | AI 전용 계정 분리 |
| 권한 오류가 나자 AI 제안대로 SELECT ANY TABLE, superuser 부여 | AI는 "에러를 없애는" 방향으로 권한을 넓힘 | 필요한 객체만 개별 GRANT |
| DML 도구를 연 채 자동 승인 모드 | 확인 없이 UPDATE가 실행될 수 있음 | 조회 도구만 노출, 실행 전 사람 승인 |
| 접속 정보를 설정 파일 평문으로 두고 Git 커밋 | 저장소 유출 = DB 유출 | 환경변수·비밀금고, .gitignore |
6. AI에게 보낼 때 쓰는 안전한 프롬프트 형태
같은 질문도 형태를 바꾸면 노랑 → 파랑으로 내려갑니다.
[역할] 너는 Oracle 19c 성능 튜닝을 돕는 보조자다.
[주의] 아래 데이터는 마스킹되어 있다. <LIT>, <IP>, DB_A, SCH_1 같은
표기는 실제 값을 추측하거나 복원하려 하지 말고 그대로 사용하라.
[입력] 바인드 처리된 SQL 1개 + DBMS_XPLAN.DISPLAY_CURSOR 결과(별칭 처리)
[요청] 1) 비용이 큰 단계 3개 2) 의심되는 원인 3) 검증용 쿼리
※ 인덱스 생성·통계 수집 같은 변경 작업은 "제안"만 하고,
운영 적용 여부는 판단하지 말 것.
실행계획을 실제로 AI에게 읽혀보는 과정은 4편에서 본격적으로 다룹니다.
7. 운영 적용 전 체크리스트

| 영역 | 확인 항목 |
| 정책 | 사내 AI 사용 지침에 해당 도구가 승인되어 있는가 / 플랜이 학습 제외·보관 조건을 충족하는가 |
| 데이터 | 마스킹 결과를 사람이 diff로 확인했는가 / 주석·로그 안 이름·번호까지 봤는가 |
| 권한 | AI 계정이 읽기 전용·최소 권한인가 / 타임아웃·세션 수 제한이 있는가 |
| 감사 | AI 계정 세션이 감사 대상인가 / 누가 무엇을 반입했는지 기록이 남는가 |
| 사고 대응 | 비밀번호를 실수로 붙여넣으면 즉시 변경하는가 / 대화 삭제 + 보안 담당 보고 절차가 있는가 |
하나라도 "아니오"면 실DB 정보는 아직 반입하지 않습니다. 특히 마지막 항목, "실수로 붙여넣었다면 대화 삭제가 아니라 비밀번호 변경이 먼저" 라는 점은 팀원 모두가 알고 있어야 합니다.
마치며
핵심 3줄
- 비밀번호·접속 문자열·실데이터는 어떤 AI에도 주지 않는다. AI에게 필요한 건 비밀번호가 아니라 구조다.
- 나머지는 4등급 신호등 × AI 환경(개인/기업/API)으로 판단하고, 마스킹은 자동화 + 사람 검수로 한다.
- AI가 DB에 직접 붙으면 전용 읽기 계정·마스킹 뷰·감사 로그가 최소 조건이다. AI가 제안하는 권한 설정은 검증 대상이다.
다음 편 예고 — 3편 「AI에게 내 DB 구조를 이해시키는 법」에서는 오늘 정한 기준 안에서, 스키마를 어떻게 요약하고 DDL을 어떤 형태로 넘겨야 AI가 엉뚱한 SQL을 덜 만드는지 컨텍스트 구성법을 다룹니다.
이 글이 도움이 되셨다면 공감과 구독 부탁드립니다.