MCP로 PostgreSQL을 Claude Code에 연결하고 읽기 전용 계정과 권한 제어를 설정한다. Claude가 schema를 직접 읽고 쿼리를 작성해 분석하므로 수동으로 테이블 구조를 붙여넣을 필요가 없다.
데이터베이스를 연결하면 Claude Code의 작동 방식이 달라진다. 테이블 구조를 프롬프트에 복사할 필요도, 필드 이름을 설명할 필요도 없다. Claude가 직접 연결해서 스키마를 읽고, 쿼리를 작성하고, 결과를 분석해준다.
이 글은 PostgreSQL을 예시로 설정부터 실제 사용까지 전 과정을 다룬다.
공식 @modelcontextprotocol/server-postgres가 지원하는 기능:
읽기 전용. 이 Server는 조회만 하도록 설계되어 있고 INSERT/UPDATE/DELETE는 실행하지 않는다.
npm install -g @modelcontextprotocol/server-postgres
.claude/settings.json (프로젝트 단위) 편집:
{
"mcpServers": {
"db": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgresql://readonly_user:password@localhost:5432/myapp_production"
]
}
}
}
연결 문자열 형식: postgresql://USER:PASSWORD@HOST:PORT/DATABASE
비밀번호를 설정 파일에 넣고 싶지 않다면 환경 변수를 사용:
{
"mcpServers": {
"db": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"DATABASE_URL": "postgresql://readonly_user:password@localhost:5432/myapp_production"
}
}
}
}
.claude/settings.json을 git에 커밋한다면 평문 비밀번호가 없는지 확인할 것. 민감한 설정은 .claude/settings.local.json에 두고 .gitignore에 추가한다.
쓰기 권한이 있는 계정을 Claude에게 주면 안 된다. PostgreSQL에서 읽기 전용 사용자를 만든다:
-- 읽기 전용 사용자 생성
CREATE USER claude_readonly WITH PASSWORD 'your_password';
-- 연결 권한 부여
GRANT CONNECT ON DATABASE myapp_production TO claude_readonly;
-- 스키마 사용 권한 부여
GRANT USAGE ON SCHEMA public TO claude_readonly;
-- 모든 테이블에 SELECT 권한 부여
GRANT SELECT ON ALL TABLES IN SCHEMA public TO claude_readonly;
-- 앞으로 생성될 테이블에도 동일한 권한 적용
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO claude_readonly;
sessions, audit_logs 같이 Claude가 보면 안 되는 테이블은 권한을 부여하지 않거나 별도 스키마로 분리한다.
Claude Code를 재시작하고 물어본다:
데이터베이스에 있는 테이블 목록을 알려줘
Claude가 "데이터베이스에 접근할 수 없습니다" 대신 테이블 목록을 반환하면 MCP 연결이 성공한 것이다.
설정이 완료되면 Claude가 할 수 있는 일이 크게 늘어난다. 실제 시나리오 몇 가지를 살펴보자.
시나리오 1: 데이터 구조 파악
users 테이블과 orders 테이블의 관계가 어떻게 돼?
외래 키 제약 조건이 있어?
Claude가 information_schema를 직접 조회해서 테이블 구조와 제약 조건을 읽고 명확하게 설명해준다. 스키마를 붙여넣을 필요가 없다.
시나리오 2: 데이터 분석
최근 30일 내에 가입한 사용자 중에서
주문을 1건 이상 한 비율이 얼마나 돼?
가입 채널별로 나눠서 보여줘
Claude가 SQL을 작성하고 실행한 후 결과를 분석한다. 쿼리에 오류가 있으면(필드명 불일치 등) 에러를 보고 스스로 수정한다.
시나리오 3: 문제 조사
orders 테이블에서 status = 'pending'인데
created_at이 7일 넘은 건이 몇 개야?
가장 최근 것은 언제야?
이런 즉석 조회는 예전엔 직접 SQL을 작성하거나 DB 툴에서 수동으로 확인해야 했다. 이제 필요한 것만 말하면 된다.
시나리오 4: 개발 지원
users 테이블에 last_login_at 컬럼을 추가하고 싶어.
마이그레이션 SQL 작성해줘.
기존 인덱스가 충분한지도 확인해줘.
Claude가 현재 테이블 구조를 먼저 확인하고 마이그레이션을 작성하면서 인덱스 상황도 분석해서 제안한다.
기본적으로 Claude는 권한을 부여한 모든 테이블의 완전한 스키마와 데이터에 접근할 수 있다. 필요에 따라 다음과 같이 제한한다.
테이블 단위 제어
특정 테이블에만 권한 부여:
-- 전체가 아닌 특정 테이블만 허용
GRANT SELECT ON TABLE users, orders, products TO claude_readonly;
-- sessions, payment_tokens, audit_logs 등 민감한 테이블은 제외
뷰로 민감한 컬럼 필터링
테이블에 민감한 필드(비밀번호 해시, API 키, 전화번호)가 있다면 해당 필드를 제외한 뷰를 만든다:
CREATE VIEW users_safe AS
SELECT id, email, created_at, plan, status
FROM users;
-- password_digest, phone, payment_method_id 제외
GRANT SELECT ON users_safe TO claude_readonly;
-- 원본 테이블에는 권한 부여하지 않음
Claude는 뷰만 조회할 수 있고 원본 필드는 볼 수 없다.
행 수 제한
@modelcontextprotocol/server-postgres는 기본적으로 쿼리 결과에 행 수 제한(보통 1,000행)을 적용해서 테이블 전체를 가져오는 것을 방지한다. 이 제한은 합리적이므로 늘릴 필요 없다.
프로덕션 데이터베이스가 원격 서버에 있다면 보통 포트를 직접 노출하지 않는다. SSH 터널을 사용한다:
# localhost:5433을 원격 서버의 5432로 포워딩하는 터널 생성
ssh -L 5433:localhost:5432 [email protected] -N
설정 파일에서는 로컬 포트를 사용:
"postgresql://readonly_user:password@localhost:5433/myapp_production"
pgBouncer 같은 커넥션 풀러를 사용하는 방법도 있다.
프로젝트에서 여러 데이터베이스(메인 DB + 분석 DB + 캐시 DB 등)를 사용한다면 여러 MCP Server를 설정할 수 있다:
{
"mcpServers": {
"main-db": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "postgresql://...메인DB..."]
},
"analytics-db": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "postgresql://...분석DB..."]
}
}
}
Claude가 두 DB 모두에 연결한다. 어느 DB를 쓸지 말해주면 된다.
데이터베이스를 연결하면 가장 크게 달라지는 점은 데이터 관련 대화에 사전 설명이 필요 없어진다는 것이다. 테이블 구조 설명도, 필드 타입 설명도 필요 없다. 필요한 것만 말하면 Claude가 직접 조회한다.
버그를 추적할 때 특히 유용하다. 예전 흐름: DB 조회 → 결과 복사 → Claude에 붙여넣기 → 분석. 새 흐름: 문제 설명 → Claude가 직접 조회 → 바로 결론.