대상: 사내 DBA / DB 담당자 — 4개 DB 종류 별 사용자 권한 / 연결 정보 / 트러블슈팅 선행 조건: 커넥터 설치 + 툴킷 사용 가이드 숙지 (Toolkit 사용 가이드) 본 문서는 DB 측 작업 중심 — Studio 폼 입력은 툴킷 가이드 §5 참고
공통 보안 원칙
- 읽기 전용 사용자 별도 생성 — 기존 운영 계정 재사용 X
- 꼭 필요한 것만 권한 부여 — 조회 계정은
SELECT만, 그것도 필요한 테이블 / view 만. 함수·프로시저를 실행하는 권한(EXECUTE) 은 주지 마세요 (DB 종류별 설명은 아래 각 절 참고) - 노출 범위 제한 — 모든 스키마 X. 비즈니스 분석 필요한 view / 테이블만 권한
- 비밀번호는 secrets 파일 에만 — Studio / Hub 에 절대 평문 X
- 네트워크: 커넥터 호스트에서만 접근 가능하도록 DB 측 ACL 조정 권장
- 롤(role, 권한을 묶어둔 그룹) 밖의 전용 계정 — 권한은 롤을 거치지 말고 계정에 직접 주세요 (아래 "롤과 자동 권한 점검" 참고)
조회(읽기) 경로를 어떻게 봐야 하나 — 솔직한 설명
조회 도구는 AI 가 그때그때 필요한 SELECT 문을 직접 만들어 실행합니다. 그래서 조회 경로는 "설계상 위험이 0" 인 구조가 아닙니다 — 이 계정이 볼 수 있는 범위가 곧 문제가 생겼을 때의 영향 범위입니다.
제품에는 여러 겹의 안전장치가 있습니다 (조회 문장만 허용 · 여러 문장 붙여 실행 차단 · 결과 행수 상한 · 응답 크기 상한 · 쿼리 시간 제한). 하지만 가장 확실한 방법은 계정이 볼 수 있는 것 자체를 좁히는 것입니다. 스키마 전체가 아니라 업무에 필요한 테이블 / view 만 골라서 권한을 주세요.
반대로 입력(쓰기) 경로는 범위가 구조적으로 정해져 있습니다. AI 는 관리자가 미리 검토해 확정한 그 INSERT 문만, 그것도 뷰에 선언한 행 수 상한(기본 1행) 안에서만 실행할 수 있습니다. 상한을 넘으면 확정 전에 되돌립니다. 여기에 더해 쓰기 계정에는 그 테이블 INSERT 권한만 주시길 권장합니다 (아래 1-3 참고) — 이건 제품이 강제하는 것이 아니라 마지막 안전장치를 고객사에서 직접 세우는 부분입니다. 위 조회 쪽 주의사항이 쓰기 쪽까지 확대되는 것은 아닙니다.
가장 강력한 선택 — 읽기 전용 복제본 (read replica)
여건이 된다면 운영 DB 대신 읽기 전용 복제본 (운영 DB 의 내용을 실시간으로 따라 복사하는 읽기 전용 사본) 에 연결하는 것이 가장 안전합니다.
- 서버 자체가 쓰기를 받지 않으므로 어떤 경로로도 데이터가 바뀌지 않습니다
- 무거운 조회가 돌아도 운영 DB 성능에 영향을 주지 않습니다
필수는 아닙니다 — 복제본이 없어도 아래의 최소권한 계정 방식으로 충분히 운영할 수 있습니다. 다만 복제본을 쓸 수 있다면 그쪽을 권장합니다.
아주 큰 조회 결과 — 제품 상한만 믿지 마세요
커넥터는 조회 결과를 기본 8MB 까지만 전달합니다(환경변수 MCP_CONNECTOR_MAX_RESPONSE_BYTES 로 조정). 다만 이건 전달하는 양의 상한이지, 데이터베이스에서 받는 양의 상한이 아닙니다 — 데이터베이스 드라이버가 결과를 메모리에 먼저 올린 뒤 잘라내는 구조이기 때문입니다.
그래서 조회 한 건의 결과 자체가 수백 MB 인 경우(예: 아주 큰 파일·문서가 통째로 들어 있는 컬럼 한 칸) 이 상한은 커넥터를 지켜주지 못합니다. 이런 경우는 DB 쪽에서 미리 막아 주시는 것이 확실합니다.
- 조회 전용 복제본에 연결 (위 참고)
- 큰 값이 들어 있는 컬럼은 view 로 좁혀서 노출 — 원본 컬럼 대신 앞부분만 잘라낸 컬럼을 담은 view 를 만들고, 조회 계정에는 그 view 만
SELECT권한을 주세요 (아래 각 DB 절의 "노출 범위 좁히기" 참고) - 커넥터 호스트·컨테이너에 메모리 여유 확보 — 설치 안내대로
--restart unless-stopped로 실행하셨다면, 혹시 커넥터가 메모리 부족으로 종료돼도 도커가 곧바로 다시 띄웁니다
롤 (role) 과 자동 권한 점검
Studio 에 DB 접속 정보를 저장하면 그 계정의 실제 권한을 자동으로 점검합니다.
이때 계정이 여러 롤을 거쳐 권한을 물려받는 구조 — 특히 물려받지 않도록 설정된 롤 (PostgreSQL 의 NOINHERIT / WITH INHERIT FALSE) 이 중간에 끼어 있으면 — 실제 권한을 확정할 수 없습니다. 이 경우 점검은 추측하지 않고 "확인 불가" 로 정직하게 보고하며, 안전을 위해 쓰기 뷰는 막힙니다.
따라서 조회·쓰기 계정 모두 롤 구조 밖의 전용 계정으로 만들고, 권한을 계정에 직접 부여하는 것을 권장합니다. 그러면 점검이 확정되고 안내도 정확해집니다.
자동 점검은 참고용입니다 — 100% 확정이 아니니 DB 계정의 실제 권한은 꼭 직접 확인해 주세요.
Studio 폼 입력 — placeholder 사용 정책
| 필드 | 평문 입력 | placeholder 사용 |
|---|---|---|
| 비밀번호 | ✗ (금지) | ✅ 반드시 ${DB_PASSWORD} 등 |
| Host / Port / Database / User | ✅ 가능 | ✅ 가능 — ${DB_HOST}, ${DB_NAME}, ${DB_USER} 등 |
- 본 가이드의 각 dialect 연결 정보 표는 평문 예시 로 적었지만, 어떤 필드든
${VAR}placeholder 로 치환 가능합니다. 그 경우 secrets 파일 (/etc/mcp-connector/secrets/*.env) 에 같은 이름의 변수도 함께 추가하세요. - 사내 보안팀이 토폴로지 (호스트/DB 이름/계정명) 평문 저장도 금지 한다면 모두 placeholder 로, 그렇지 않으면 비밀번호만 placeholder — 두 방식 다 정상 동작.
- 자세한 정책은 Toolkit 사용 가이드 §3 참고.
⚠️ 비밀번호에
#@:/?가 있으면 그 자리만 아래처럼 바꿔 적어주세요 (secrets 파일에 넣을 때). 접속 주소에서 구분자로 쓰이는 문자들이라 그대로 두면 접속이 실패하는데, 오류 메시지 (Login failed,invalid db url)만으로는 비밀번호가 원인인 줄 알기 어렵습니다.
문자 바꿔 적을 값 문자 바꿔 적을 값 #%23/%2F@%40?%3F:%3A%%25예: 실제 비밀번호가
Pa55#word@2024라면 →DB_PASSWORD=Pa55%23word%402024데이터베이스의 비밀번호를 바꾸는 게 아니라, secrets 파일에 적는 표기만 바꾸는 것입니다. (따옴표로 감싸는 것으로는 해결되지 않습니다.)
1. PostgreSQL
1-1. 읽기 전용 사용자 생성
PostgreSQL 은 SELECT 만 주는 것으로 부족합니다. PostgreSQL 은 데이터베이스 안 함수의 실행 권한을 기본적으로 모든 사용자에게 열어둡니다. 그래서 조회 권한만 가진 계정도 사내에서 만든 함수를 전부 호출할 수 있고, 그 함수가 "만든 사람의 권한으로 동작" 하도록 정의돼 있으면 조회 계정을 통해 데이터가 바뀔 수 있습니다. 아래 REVOKE EXECUTE ... FROM PUBLIC 두 줄이 이 기본값을 닫아 줍니다. (실제 DB 로 확인했습니다 — count() · now() 같은 일반적인 조회 쿼리는 이 설정 후에도 그대로 동작합니다.)
CREATE ROLE mcp_reader LOGIN PASSWORD '<강한 비밀번호>';
-- 롤 멤버십은 주지 않습니다 (권한 자동 점검이 "확인 불가"로 막힙니다)
GRANT CONNECT ON DATABASE erp TO mcp_reader;
GRANT USAGE ON SCHEMA public TO mcp_reader;
GRANT SELECT ON TABLE orders, customers TO mcp_reader; -- 필요한 테이블만이 최선
REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA public FROM PUBLIC;
REVOKE EXECUTE ON ALL PROCEDURES IN SCHEMA public FROM PUBLIC; -- PG11+
REVOKE CREATE ON SCHEMA public FROM PUBLIC; -- PG14 이하만 (PG15+ 기본)
각 줄의 의미:
GRANT SELECT ON TABLE ...— 업무에 필요한 테이블만 나열합니다. 이 목록이 곧 AI 가 볼 수 있는 범위입니다REVOKE EXECUTE ...(2줄) — 모든 사용자에게 열려 있던 함수·프로시저 실행 기본값을 닫습니다. 프로시저 쪽은 PostgreSQL 11 이상에서만 있습니다REVOKE CREATE ON SCHEMA public ...— 아무나public스키마에 새 객체를 만들지 못하게 합니다. PostgreSQL 15 부터는 기본값이라 14 이하에서만 필요합니다- 롤 멤버십은 주지 않습니다 — 위 "롤과 자동 권한 점검" 참고
⚠️
REVOKE ... FROM PUBLIC은 이 계정만이 아니라 DB 전체에 적용됩니다.PUBLIC은 "모든 사용자" 를 뜻하는 특별한 대상이라, 위REVOKE EXECUTE두 줄을 실행하면 기존 사내 애플리케이션도 그 함수들을 못 쓰게 됩니다. 반드시 아래 순서로 진행하고, 가능하면 스테이징(테스트) 환경에서 먼저 시험해 보세요.
REVOKE EXECUTE ... FROM PUBLIC으로 기본값을 닫습니다- 그 함수가 정말로 필요한 롤에만 다시 열어 줍니다
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO app_role; -- 또는 함수 하나씩: GRANT EXECUTE ON FUNCTION public.calc_total(int) TO app_role;- 앞으로 새로 만들어질 함수도 같은 기본값이 되지 않도록 미리 정해 둡니다
(ALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA public REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;app_owner= 앞으로 함수를 만들 계정. 함수를 만드는 계정마다 한 번씩 지정해야 합니다.)
1-2. 조회 계정 권한 — 주지 말아야 할 것 · 더 좁히기
절대 주면 안 되는 권한
| 주지 말아야 할 것 | 이유 |
|---|---|
pg_read_all_data 롤 | 테이블별로 준 조회 권한을 통째로 무시하고 모든 데이터를 읽게 합니다 |
pg_write_all_data 롤 | 모든 테이블에 쓰기가 가능해집니다 |
SUPERUSER | 모든 제한이 사라집니다 |
CREATEROLE | 스스로 더 강한 계정을 만들 수 있습니다 |
함수 실행 권한(EXECUTE) | 1-1 참고 — 조회 계정으로 데이터가 바뀔 수 있는 경로가 열립니다 |
롤 멤버십 (GRANT <롤> TO ...) | 실제 권한을 자동 점검이 확정하지 못해 "확인 불가" 가 됩니다 (위 공통 절) |
설정 후 확인 — 조회 계정으로 접속해서 아래를 실행하면 둘 다 false 여야 정상입니다.
SELECT has_function_privilege(current_user, 'some_func()', 'EXECUTE'); -- false 여야 정상
SELECT has_table_privilege(current_user, 'orders', 'UPDATE'); -- false 여야 정상
노출 범위 더 좁히기 (권장) — 비즈니스 view 만 노출하는 방식이 가장 안전합니다.
CREATE VIEW analytics.v_orders_summary AS
SELECT id, customer_id, total, created_at FROM orders;
GRANT USAGE ON SCHEMA analytics TO mcp_reader;
GRANT SELECT ON analytics.v_orders_summary TO mcp_reader;
스키마 전체에
GRANT SELECT ON ALL TABLES를 주고 싶더라도, 그만큼 영향 범위가 넓어집니다. 업무에 필요한 테이블 / view 를 나열하는 쪽을 권장합니다.
1-3. 쓰기 전용 사용자 (쓰기 뷰를 쓸 때만)
쓰기 뷰(관리자가 미리 승인한 INSERT 문) 를 쓰는 경우, 조회 계정과 별도로 쓰기 계정을 만들고 대상 테이블 INSERT 권한만 주세요.
이 계정 권한이 AI 가 할 수 있는 일의 최종 한계선입니다. AI 는 관리자가 등록한 그 INSERT 문만, 정해둔 행 수 안에서만 실행하지만, 그 위의 어떤 안전장치보다 확실한 것은 계정에 아예 권한이 없는 것입니다. 수정·삭제 권한은 주지 마세요.
CREATE USER mcp_writer WITH PASSWORD 'Pa55word!2024';
GRANT CONNECT ON DATABASE erp TO mcp_writer;
\c erp
GRANT USAGE ON SCHEMA public TO mcp_writer;
GRANT INSERT ON public.order_notes TO mcp_writer;
-- id 컬럼이 serial 타입이면 시퀀스 사용 권한도 필요합니다 (아래 설명 참고)
GRANT USAGE ON SEQUENCE public.order_notes_id_seq TO mcp_writer;
표가
public이 아닌 스키마에 있으면 그 스키마 이름으로 바꿔주세요. 위 예시는 표가public스키마에 있는 경우입니다. 예를 들어 표가sales스키마에 있으면GRANT USAGE ON SCHEMA sales TO mcp_writer;와GRANT INSERT ON sales.order_notes TO mcp_writer;처럼 바꿔 주세요. 표에 INSERT 권한을 줘도 그 스키마에USAGE가 없으면 접근 자체가 막혀permission denied for schema sales로 실패합니다. 데이터베이스 이름과 스키마 이름이 같은 경우(예:salesDB 안의sales스키마) 특히 놓치기 쉽습니다.
serial컬럼이 있으면 INSERT 권한만으로는 실패합니다.id serial PRIMARY KEY처럼 번호를 자동으로 매기는 컬럼은 뒤에 숨은 시퀀스 객체에서 다음 번호를 받아옵니다. 이 시퀀스는 테이블과 권한이 따로라, INSERT 권한만 주면 입력이permission denied for sequence order_notes_id_seq로 실패합니다. 위 예시처럼GRANT USAGE ON SEQUENCE <시퀀스이름> TO <쓰기계정>;을 함께 주세요. 시퀀스 이름은 보통<테이블>_<컬럼>_seq이고, 정확한 이름은 이렇게 확인합니다:SELECT pg_get_serial_sequence('public.order_notes', 'id');PostgreSQL 10 이상의
IDENTITY컬럼(id int GENERATED BY DEFAULT AS IDENTITY)은 이 권한이 필요 없습니다 — INSERT 권한만으로 동작합니다.
생성된 id 를 돌려받는(RETURNING id) 뷰를 쓰려면 그 테이블의 SELECT 권한도 함께 필요합니다.
1-4. 연결 정보 (Studio 폼 입력값)
| 필드 | 값 |
|---|---|
| Host | pg-erp.internal |
| Port | 5432 |
| Database | erp |
| User | mcp_reader |
| 비밀번호 | ${DB_PASSWORD} |
| SSL | 사용 (CA 검증 없음) — 사내 자체서명일 때<br>사용 (시스템 CA) — Let's Encrypt 등 공인 CA |
1-5. secrets 파일
/etc/mcp-connector/secrets/pg.env:
DB_PASSWORD=Pa55word!2024
1-6. 사전 연결 테스트 (커넥터 호스트에서)
psql "postgres://mcp_reader:Pa55word!2024@pg-erp.internal:5432/erp" -c "SELECT 1"
# 출력: ?column? \n ---------- \n 1
1-7. 트러블슈팅
| 증상 | 원인 / 해결 |
|---|---|
password authentication failed | 사용자/비밀번호 재확인 — \password 로 재설정 가능 |
connection refused | PG 의 pg_hba.conf 에 커넥터 호스트 IP 허용 라인 추가 필요 |
permission denied for table X | GRANT SELECT 누락 — 위 1-1 절 다시 |
SSL connection failed | pg_hba.conf 의 method 가 hostssl 이면 SSL 필수. Studio 의 SSL 옵션 require 이상으로 |
permission denied for sequence X | 쓰기 계정에 serial 컬럼의 시퀀스 사용 권한 누락 — 위 1-3 절의 GRANT USAGE ON SEQUENCE 참고 |
permission denied for schema X | 표에 INSERT/SELECT 권한은 줬지만 그 표가 속한 스키마에 USAGE 누락 — GRANT USAGE ON SCHEMA X TO 계정; |
2. MySQL / MariaDB
2-1. 읽기 전용 사용자 생성
CREATE USER 'mcp_reader'@'%' IDENTIFIED BY 'Pa55word!2024';
GRANT SELECT ON appdb.* TO 'mcp_reader'@'%';
FLUSH PRIVILEGES;
⚠️ 조회 계정에
EXECUTE(함수·프로시저 실행 권한) 는 주지 마세요. MySQL 은 기본적으로 조회 계정에 실행 권한을 주지 않습니다 — 이 기본값이 조회 계정의 가장 중요한 안전장치이니 그대로 두세요. (실제 DB 로 확인했습니다:SELECT만 가진 계정은 모든 함수 호출이 DB 에서 거부됐습니다.) MySQL 은 함수를 만들 때 별도로 지정하지 않으면 "함수를 만든 사람의 권한으로 동작" 이 기본값입니다. 리포팅 편의를 위해GRANT EXECUTE를 한 번 주면, 그 함수 안의 입력·수정 작업이 조회 계정을 통해 실행될 수 있습니다.
2-2. 노출 범위 좁히기
-- 특정 테이블만
GRANT SELECT ON appdb.orders, appdb.customers TO 'mcp_reader'@'%';
-- view 만 (권장)
CREATE VIEW appdb.v_orders_summary AS
SELECT id, customer_id, total, created_at FROM orders;
GRANT SELECT ON appdb.v_orders_summary TO 'mcp_reader'@'%';
2-3. 연결 정보
| 필드 | 값 |
|---|---|
| Host | mysql-app.internal |
| Port | 3306 |
| Database | appdb |
| User | mcp_reader |
| 비밀번호 | ${DB_PASSWORD} |
| SSL | 사용 (CA 검증 없음) 권장 |
2-4. secrets 파일
/etc/mcp-connector/secrets/mysql.env:
DB_PASSWORD=Pa55word!2024
2-5. 사전 연결 테스트
mysql -h mysql-app.internal -P 3306 -u mcp_reader -p'Pa55word!2024' appdb -e "SELECT 1"
2-6. 트러블슈팅
| 증상 | 원인 / 해결 |
|---|---|
Access denied for user 'mcp_reader'@'커넥터호스트' | 'mcp_reader'@'%' 가 아닌 특정 호스트만 허용된 사용자. % 또는 커넥터 호스트 IP 명시 |
Unknown database 'appdb' | DB 이름 오타 — 대소문자 구분 (SHOW DATABASES; 확인) |
MySQL 5.7 + caching_sha2_password 인증 오류 | 커넥터의 mysql2 드라이버 호환. 비밀번호를 mysql_native_password 로 재설정 또는 MySQL 8 권장 |
| TLS handshake error | SSL 옵션 mismatch — MySQL 의 require_secure_transport=ON 이면 SSL require 필수 |
쓰기 뷰 실행이 write_unsupported_non_innodb 로 거절 | 대상 DB 에 MyISAM 등 InnoDB 가 아닌 테이블이 있습니다. 이런 엔진은 작업을 되돌리는 기능(롤백)이 없어 안전장치가 무효라 쓰기를 거절합니다. ALTER TABLE <표> ENGINE=InnoDB 로 전환 후 사용하세요 (조회는 엔진과 무관하게 동작) |
3. MS SQL Server
3-1. 읽기 전용 로그인 + 사용자 생성
USE master;
CREATE LOGIN mcp_reader WITH PASSWORD = 'Pa55word!2024';
USE erp;
CREATE USER mcp_reader FOR LOGIN mcp_reader;
-- 데이터베이스 전체 읽기 (SQL Server 2012 이상)
ALTER ROLE db_datareader ADD MEMBER mcp_reader;
-- SQL Server 2008 R2 이하: EXEC sp_addrolemember 'db_datareader', 'mcp_reader';
db_datareader는 그 데이터베이스의 모든 테이블을 열어 줍니다. 가능하면 아래 3-2 처럼 필요한 테이블 / view 만 권한을 주세요.
⚠️ 조회 계정이 링크드 서버를 쓸 수 없게 해 주세요. 링크드 서버(다른 SQL Server 나 외부 DB 를 미리 등록해 두고 참조하는 기능) 는 보통 더 강한 원격 계정으로 접속하도록 설정돼 있습니다. 그래서 조회 계정에 링크드 서버 접근을 열어 두면, 이 계정에 준 최소권한이 그대로 유지되지 않습니다 (실제 DB 로 확인했습니다). 사내에 링크드 서버가 구성돼 있다면 조회 계정은 그 목록에 매핑하지 마세요.
3-2. 노출 범위 좁히기
특정 스키마만:
USE erp;
-- db_datareader 롤에서 빼야 "DB 전체 읽기" 가 없어집니다 (SQL Server 2012 이상)
ALTER ROLE db_datareader DROP MEMBER mcp_reader;
-- SQL Server 2008 R2 이하: EXEC sp_droprolemember 'db_datareader', 'mcp_reader';
GRANT SELECT ON SCHEMA::dbo TO mcp_reader;
⚠️
REVOKE SELECT FROM mcp_reader;로는db_datareader가 빠지지 않습니다.db_datareader는 롤 멤버십이라REVOKE대상이 아닙니다. 위ALTER ROLE ... DROP MEMBER를 쓰지 않으면 계정은 계속 DB 전체를 읽을 수 있고, Studio 의 권한 자동 점검도 "전체 조회 가능" 으로 계속 표시합니다.확인 방법 — 조회 계정으로 접속해 아래를 실행하면
0이어야 정상입니다.SELECT IS_ROLEMEMBER('db_datareader'); -- 0 이어야 정상 (1 이면 아직 멤버)
특정 테이블 / view 만:
GRANT SELECT ON dbo.orders TO mcp_reader;
GRANT SELECT ON dbo.v_summary TO mcp_reader;
3-3. 연결 정보
| 필드 | 값 |
|---|---|
| Host | mssql-erp.internal |
| Port | 1433 |
| Database | erp |
| User | mcp_reader |
| 비밀번호 | ${MSSQL_PASS} |
| SSL | 사용 (CA 검증 없음) 또는 사용 안함 (사내 네트워크) |
3-4. secrets 파일
/etc/mcp-connector/secrets/mssql.env:
MSSQL_PASS=Pa55word!2024
비밀번호에
@가 들어가면 SQL Server 연결 문자열에서 따옴표 escape 가 까다로움. 가능하면@제외한 강력한 비밀번호 사용.
3-5. 사전 연결 테스트
# sqlcmd (microsoft/mssql-tools 이미지) 또는 사내 환경의 SQL 도구
docker run --rm -it mcr.microsoft.com/mssql-tools \
/opt/mssql-tools/bin/sqlcmd \
-S mssql-erp.internal,1433 -U mcp_reader -P 'Pa55word!2024' \
-d erp -Q "SELECT 1"
3-6. 트러블슈팅
| 증상 | 원인 / 해결 |
|---|---|
Login failed for user 'mcp_reader' | SQL Server 인증 모드 (Windows / Mixed) 확인 — Mixed Mode 필수 |
Cannot open database "erp" | 사용자가 그 DB 의 사용자로 추가 안 됨 — USE erp; CREATE USER 다시 |
| TLS handshake (encrypted connection) | SQL Server 2019+ 는 default 암호화. Studio SSL 옵션 require 권장 |
SELECT ... TOP 100 외 결과 잘림 | 정상 동작 — execute_query 가 100 행 제한 (안전 측면) |
4. Oracle DB
4-1. 읽기 전용 사용자 생성
-- DBA 로 접속
CREATE USER mcp_reader IDENTIFIED BY "Pa55word!2024"
DEFAULT TABLESPACE users
TEMPORARY TABLESPACE temp
QUOTA 0 ON users;
GRANT CREATE SESSION TO mcp_reader;
-- 특정 스키마 / 테이블 SELECT 권한
GRANT SELECT ON erp_owner.orders TO mcp_reader;
GRANT SELECT ON erp_owner.customers TO mcp_reader;
GRANT SELECT ON erp_owner.v_summary TO mcp_reader;
⚠️ 조회 계정에
EXECUTE(함수·프로시저 실행 권한) 는 주지 마세요. Oracle 도 기본적으로 조회 계정에 실행 권한을 주지 않습니다 — 이 기본값을 그대로 두세요. (실제 DB 로 확인했습니다:SELECT만 가진 계정은 함수의 존재조차 보이지 않았습니다.) Oracle 함수는 바깥 작업과 독립적으로 자기 변경만 따로 확정하는 기능 (PRAGMA AUTONOMOUS_TRANSACTION) 을 쓸 수 있습니다. 이런 함수에 실행 권한을 주면 조회 계정을 통해 데이터가 바뀔 수 있고, 이 경우 제품 쪽에서는 막을 방법이 없어 계정 권한이 유일한 안전장치입니다.
AI 에게 보이는 목록 = 위에서
GRANT SELECT해 주신 것뿐입니다. 스키마 정보(describe_schema)에는 이 계정이 조회 권한을 받은 테이블·view 만 나타납니다. 권한을 주지 않은 테이블은 목록에도 없고 조회도 되지 않으니, 위GRANT SELECT목록이 곧 AI 가 보는 범위입니다. 목록에 오라클이 스스로 관리하는 시스템 스키마는 나오지 않습니다. 이름은ERP_OWNER.ORDERS처럼 소유자 이름이 앞에 붙어 표시됩니다 (다른 DB 의스키마.테이블과 같은 형태). SQL 을 직접 쓰실 때도erp_owner.orders처럼 소유자를 붙여 주세요. 필요한 컬럼만 담은 view 를erp_owner쪽에 만들어 두고 그 view 에만 권한을 주시면 노출 범위를 더 좁힐 수 있습니다 (공통 원칙 3).
4-2. SERVICE_NAME 확인 (중요)
Oracle 은 SID 가 아닌 SERVICE_NAME 사용:
SELECT value FROM v$parameter WHERE name = 'service_names';
-- 또는
SHOW PARAMETER service_names;
출력 예: ERPSVC, XEPDB1, FREEPDB1 등
4-3. 연결 정보
| 필드 | 값 |
|---|---|
| Host | oracle-erp.internal |
| Port | 1521 |
| Database | SERVICE_NAME (위에서 확인한 값) |
| User | mcp_reader |
| 비밀번호 | ${ORACLE_PASS} |
| SSL | 사용 안함 (사내 네트워크) 또는 사용 (CA 검증 없음) (TCPS) |
미리보기에 표시되는 baseUrl:
oracle://mcp_reader:${ORACLE_PASS}@oracle-erp.internal:1521/ERPSVC
4-4. secrets 파일
/etc/mcp-connector/secrets/oracle.env:
ORACLE_PASS=Pa55word!2024
4-5. 사전 연결 테스트
# sqlplus 가 있을 때
sqlplus 'mcp_reader/"Pa55word!2024"@//oracle-erp.internal:1521/ERPSVC'
# 비밀번호의 특수문자는 따옴표로 감싸기
# 또는 docker 로
docker run --rm -it gvenzl/oracle-free:23-slim sqlplus \
'mcp_reader/Pa55word!2024@//oracle-erp.internal:1521/ERPSVC'
4-6. Oracle 버전 호환
| Oracle 버전 | 우리 커넥터 호환 |
|---|---|
| 12c (12.1+) / 18c / 19c / 21c / 23ai | ✅ 호환 (Thin 모드 자동) |
| 11g 이하 (2013 이전 release) | ❌ 별도 Thick 모드 이미지 필요 — 별도 문의 |
우리 커넥터는
oracledbv6 Thin 모드 사용 — Instant Client 설치 불필요. 단 Oracle 11g 이하는 미지원 (보안 패치 EOL 이라 사실상 거의 없음).
4-7. 트러블슈팅
| 증상 | 원인 / 해결 |
|---|---|
ORA-12154: TNS could not resolve | 4-3 의 baseUrl 형식 오류. //host:port/SERVICE_NAME 형식 확인 |
ORA-12541: TNS:no listener | 1521 포트 차단 — 사내 방화벽 / DB 호스트 listener 상태 확인 |
ORA-01017: invalid username/password | 비밀번호 특수문자. secrets 파일에 따옴표 없이 그대로 (escape X) |
ORA-00942: table or view does not exist | GRANT SELECT 누락. erp_owner schema 의 객체는 fully qualified (erp_owner.orders) 로 권한 부여 |
describe_schema 결과가 비어 있음 (tables: {}) | 이 계정에 아직 SELECT 권한을 준 테이블·view 가 없습니다 — 위 4-1 의 GRANT SELECT ON erp_owner.<테이블> TO mcp_reader; 를 확인해 주세요 |
| CLOB / BLOB 컬럼 결과가 중간에 끊김 | 정상 동작 — 아래 "CLOB / BLOB 컬럼 읽기" 참고 |
CLOB / BLOB 컬럼 읽기 — 빈 칸은 "비어 있음" 이 아닐 수 있습니다
CLOB(긴 글·메모) · BLOB(파일·이미지) 컬럼은 기본 8MB 까지만 읽어 옵니다. 중요한 점은 이 8MB 가 컬럼 한 칸마다가 아니라 조회 결과 전체를 합쳐서 한 번만 적용된다는 것입니다.
그래서 앞쪽 행의 큰 컬럼이 8MB 를 다 써버리면, 그 뒤 행의 큰 컬럼 값은 빈 값으로 옵니다. 응답에 "일부만 받았음" 표시가 있다면, 빈 칸을 실제로 비어 있는 데이터로 읽지 마세요 — 상한에 걸려 잘린 것입니다.
권장하는 조회 방법:
-- 필요한 앞부분만 잘라서 (예: 4,000바이트)
SELECT id, DBMS_LOB.SUBSTR(memo, 4000, 1) AS memo_head FROM erp_owner.notes;
-- 또는 한 번에 한 행만
SELECT memo FROM erp_owner.notes WHERE id = 123;
가장 안전한 방법은 아예 DBMS_LOB.SUBSTR(...) 로 잘라 둔 컬럼만 담은 view 를 erp_owner 쪽에 만들어 두고, 조회 계정에는 원본 테이블 대신 그 view 에만 SELECT 권한을 주는 것입니다.
5. DB 4종 공통 점검 체크리스트
새 DB 툴킷 만들기 전:
- 읽기 전용 사용자 생성 (DDL/DML 권한 없음)
- 노출할 스키마 / 테이블만 GRANT — 필요한 것만. 이 범위가 곧 영향 범위입니다
- 함수·프로시저 실행 권한(
EXECUTE) 을 주지 않음 — PostgreSQL 은 기본값이 열려 있으므로REVOKE ... FROM PUBLIC을 별도로 실행 (위 1-1) - 롤을 거치지 않는 전용 계정 — 권한은 계정에 직접 부여 (자동 점검이 "확인 불가" 로 막히는 것을 방지)
- PostgreSQL:
pg_read_all_data/pg_write_all_data/SUPERUSER/CREATEROLE미부여 (위 1-2) - MS SQL Server: 조회 계정에 링크드 서버 접근 미부여 (위 3-1). 범위를 좁혔다면
SELECT IS_ROLEMEMBER('db_datareader')가0인지 확인 (위 3-2) - Oracle: 조회에 필요한 테이블 / view 를
erp_owner.orders처럼 소유자까지 붙여GRANT SELECT— 이 목록이 곧 AI 에게 보이는 범위입니다 (위 4-1) - (권장) 운영 DB 대신 읽기 전용 복제본에 연결
- 비밀번호 강도 충분 (특수문자 포함,
@가급적 회피) - 커넥터 호스트에서 DB 호스트로 telnet / 사내 SQL 도구로 직접 연결 가능
- SSL 정책 결정 (사내 사설 인증서 → CA 검증 없음 / 공인 → 검증 사용)
- secrets 파일 권한 600, 폴더 700
- Studio 폼의 비밀번호 필드 =
${VAR}placeholder (평문 금지). Host / Database / User 는 정책에 따라 평문 또는 placeholder - 사용자 운영 모니터링 — DB 측 audit log 도 같이 기록 권장
6. AI 뷰 추천 (가상 뷰)
DB 툴킷은 기본으로 두 가지 도구 (describe_schema · execute_query) 를 제공합니다 — AI 가 스키마를 직접 보고 SQL 을 짜는 자유 질의 방식입니다. 여기에 더해, 자주 쓰는 조회를 이름 붙은 가상 뷰 도구로 만들 수 있습니다. 두 방식은 함께 쓸 수 있고, 뷰를 추가해도 자유 질의 도구는 그대로 남습니다.
가상 뷰의 장점
- 반복 업무에서 매번 SQL 을 새로 짜지 않아 안정적입니다
- "일별 매출 집계" 처럼 업무 언어로 대화할 수 있습니다
- 뷰에 적힌 컬럼만 AI 에 보여주므로 개인정보 노출이 줄어듭니다 (데이터베이스에는 아무것도 만들지 않습니다 — 뷰는 이름 붙은 SQL 일 뿐입니다)
사용 방법 (Studio → DB 툴킷 상세 → 도구 탭)
- AI 뷰 추천 받기 — 업무 설명 한 줄 (선택) 을 넣으면 AI 가 스키마를 분석해 뷰 후보를 제안합니다. 개인정보로 보이는 컬럼 (이메일 · 전화번호 등) 은 기본으로 제외되고, 제외된 컬럼과 사유가 카드에 표시됩니다. 계정에
SELECT권한을 준 테이블이 하나도 없으면 분석할 스키마 정보가 없어 제안할 후보도 나오지 않습니다. - 검증 실행 — 후보의 SQL 을 1행만 실제로 실행해 문법 · 컬럼 오류를 미리 확인합니다.
- 도구로 등록 — 클릭하면 이 뷰가 툴킷의 도구 목록에 추가됩니다.
- 바로 사용 — 등록하면 끝입니다. 서버의 설정 파일에 복사해 붙여넣는 단계는 없습니다 (커넥터 v0.2.10 부터).
커넥터 v0.2.10 이상이 필요합니다. 그보다 낮으면 Studio 에서 뷰 만들기 버튼이 비활성화되고 안내가 표시됩니다.
예전에
db-views.json으로 만드신 뷰가 있다면 — v0.2.10 으로 업데이트한 뒤에는 그 도구들이 동작하지 않습니다. 도구 목록에서 삭제한 뒤 화면에서 다시 만들어 주세요. 파일은 더 이상 읽지 않으므로 정리하셔도 됩니다.
등록된 뷰의 정의는 도구 목록에서 언제든 확인하고 삭제할 수 있습니다. 참고로 정의는 이런 모양입니다:
{
"daily_sales_summary": {
"description": "지정한 기간의 일별 주문 건수와 총 매출액 집계",
"sql": "SELECT date_trunc('day', ordered_at) AS order_date, count(*) AS order_count, sum(amount) AS total_amount FROM orders WHERE ordered_at BETWEEN $1 AND $2 GROUP BY 1 ORDER BY 1",
"params": [
{ "name": "start_date", "type": "string", "description": "집계 시작일 (ISO 8601)" },
{ "name": "end_date", "type": "string", "description": "집계 종료일 (ISO 8601)" }
]
}
}
권한 안내 — AI 는 계정에 SELECT 권한이 있는 테이블만 읽을 수 있습니다. 자유 질의든 가상 뷰든 계정 권한이 마지막 경계선이므로, 민감 테이블 (급여 · 인사 등) 은 계정 권한에서 아예 제외해 원천 차단하세요 (위 1-1 · 1-2 절 참고).
Oracle 은 목록에 소유자 이름이 함께 나옵니다 — 스키마 정보의 이름이
ERP_OWNER.ORDERS처럼 소유자.테이블 형태입니다 (위 4-1). 가상 뷰의 SQL 을 직접 쓰실 때도erp_owner.orders처럼 소유자를 붙여 주세요. 그리고 목록에 보이든 안 보이든 마지막 경계선은 여전히 계정 권한입니다.
7. 문의
- DB 권한 / 사내 정책 협의: 사내 DBA / 보안 담당자
- 도구 설명 정확도 / 호출 빈도 튜닝: WRKS 담당자
같이 보세요:
- 툴킷 사용 → Toolkit 사용 가이드
- 커넥터 설치 → Connector 설치 가이드