문서/설치·운영/DB 연동 가이드
GUIDE인프라/DBAdb-connection-guide.md·읽는 시간 약 58

DB 연동 가이드

PostgreSQL / MySQL / MS SQL / Oracle 별 read-only 계정 + 연결 정보.

대상: 사내 DBA / DB 담당자 — 4개 DB 종류 별 사용자 권한 / 연결 정보 / 트러블슈팅 선행 조건: 커넥터 설치 + 툴킷 사용 가이드 숙지 (Toolkit 사용 가이드) 본 문서는 DB 측 작업 중심 — Studio 폼 입력은 툴킷 가이드 §5 참고


공통 보안 원칙

  1. 읽기 전용 사용자 별도 생성 — 기존 운영 계정 재사용 X
  2. 꼭 필요한 것만 권한 부여 — 조회 계정은 SELECT 만, 그것도 필요한 테이블 / view 만. 함수·프로시저를 실행하는 권한(EXECUTE) 은 주지 마세요 (DB 종류별 설명은 아래 각 절 참고)
  3. 노출 범위 제한 — 모든 스키마 X. 비즈니스 분석 필요한 view / 테이블만 권한
  4. 비밀번호는 secrets 파일 에만 — Studio / Hub 에 절대 평문 X
  5. 네트워크: 커넥터 호스트에서만 접근 가능하도록 DB 측 ACL 조정 권장
  6. 롤(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 두 줄을 실행하면 기존 사내 애플리케이션도 그 함수들을 못 쓰게 됩니다. 반드시 아래 순서로 진행하고, 가능하면 스테이징(테스트) 환경에서 먼저 시험해 보세요.

  1. REVOKE EXECUTE ... FROM PUBLIC 으로 기본값을 닫습니다
  2. 그 함수가 정말로 필요한 롤에만 다시 열어 줍니다
    GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO app_role;
    -- 또는 함수 하나씩: GRANT EXECUTE ON FUNCTION public.calc_total(int) TO app_role;
    
  3. 앞으로 새로 만들어질 함수도 같은 기본값이 되지 않도록 미리 정해 둡니다
    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 로 실패합니다. 데이터베이스 이름과 스키마 이름이 같은 경우(예: sales DB 안의 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 폼 입력값)

필드
Hostpg-erp.internal
Port5432
Databaseerp
Usermcp_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 refusedPG 의 pg_hba.conf 에 커넥터 호스트 IP 허용 라인 추가 필요
permission denied for table XGRANT SELECT 누락 — 위 1-1 절 다시
SSL connection failedpg_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. 연결 정보

필드
Hostmysql-app.internal
Port3306
Databaseappdb
Usermcp_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 errorSSL 옵션 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. 연결 정보

필드
Hostmssql-erp.internal
Port1433
Databaseerp
Usermcp_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. 연결 정보

필드
Hostoracle-erp.internal
Port1521
DatabaseSERVICE_NAME (위에서 확인한 값)
Usermcp_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 모드 이미지 필요 — 별도 문의

우리 커넥터는 oracledb v6 Thin 모드 사용 — Instant Client 설치 불필요. 단 Oracle 11g 이하는 미지원 (보안 패치 EOL 이라 사실상 거의 없음).

4-7. 트러블슈팅

증상원인 / 해결
ORA-12154: TNS could not resolve4-3 의 baseUrl 형식 오류. //host:port/SERVICE_NAME 형식 확인
ORA-12541: TNS:no listener1521 포트 차단 — 사내 방화벽 / DB 호스트 listener 상태 확인
ORA-01017: invalid username/password비밀번호 특수문자. secrets 파일에 따옴표 없이 그대로 (escape X)
ORA-00942: table or view does not existGRANT SELECT 누락. erp_owner schema 의 객체는 fully qualified (erp_owner.orders) 로 권한 부여
describe_schema 결과가 비어 있음 (tables: {})이 계정에 아직 SELECT 권한을 준 테이블·view 가 없습니다 — 위 4-1GRANT 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 툴킷 상세 → 도구 탭)

  1. AI 뷰 추천 받기 — 업무 설명 한 줄 (선택) 을 넣으면 AI 가 스키마를 분석해 뷰 후보를 제안합니다. 개인정보로 보이는 컬럼 (이메일 · 전화번호 등) 은 기본으로 제외되고, 제외된 컬럼과 사유가 카드에 표시됩니다. 계정에 SELECT 권한을 준 테이블이 하나도 없으면 분석할 스키마 정보가 없어 제안할 후보도 나오지 않습니다.
  2. 검증 실행 — 후보의 SQL 을 1행만 실제로 실행해 문법 · 컬럼 오류를 미리 확인합니다.
  3. 도구로 등록 — 클릭하면 이 뷰가 툴킷의 도구 목록에 추가됩니다.
  4. 바로 사용 — 등록하면 끝입니다. 서버의 설정 파일에 복사해 붙여넣는 단계는 없습니다 (커넥터 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 담당자

같이 보세요: