외부 프로그램에서 DB 데이터 입력받기
SDK로 제한된 PostgreSQL 계정을 발급하고 접속 IP를 등록해 외부 프로그램의 INSERT를 검증합니다.
외부 프로그램에서 DB 데이터 입력받기
고객 PC나 서버의 프로그램이 App 소유 PostgreSQL 테이블에 이벤트를 입력하도록 연결합니다. App은 PHP SDK로 계정 발급과 접속 IP 등록을 요청하고, Nexia가 DB 권한과 방화벽을 관리합니다. 이 기능은 외부 프로그램이 Nexia에 데이터를 넣는 용도입니다. Nexia에서 임의의 외부 DB를 조회하거나 범용 SQL 계정을 만드는 기능은 아닙니다.
계정 발급 요청 → 전송 PC·서버의 IP 등록 → 적용 완료 확인 → 비밀번호 1회 표시 → INSERT 확인 순서로 진행합니다. 사용자가 AWS에 접속해 설정할 필요는 없습니다.
App 준비하기
PHP SDK 0.9.1 이상, 외부 DB 접속 IP 기능을 지원하는 Core 0.8.14 이상을 사용하고 App의 호환성 메타데이터에 최소 버전을 선언하세요. 버전만 맞춘다고 접속이 열리지는 않습니다. 해당 환경의 게이트웨이가 활성화되어 있고 App 실행 환경에도 대응 SDK가 있어야 합니다. network()의 enabled가 false이면 사용 불가 상태를 표시하고 플랫폼 운영자에게 활성화를 요청합니다. 사설 DB 주소를 외부 접속정보로 대신 제공하지 마세요.
데이터 모델과 마이그레이션에 따라 입력받을 App 소유 테이블을 준비하고, 보호된 API 추가하기에 따라 조회·관리 권한을 선언합니다. 선택한 테넌트의 인증된 App 경로에서 호출하세요. SDK가 그 요청의 App·테넌트·사용자·DB 할당을 식별합니다. 브라우저에서 직접 호출하는 공개 API나 인증 없는 스크립트용 인터페이스가 아닙니다.
아래 코드는 Workshop App에 다음 항목이 준비되어 있다는 전제의 발췌 예제입니다.
wsp_external_events테이블: 자동 생성되는id, JSONBpayload, DB 현재 시각을 기본값으로 쓰는received_at. 그 밖의 필수 칼럼에도 기본값이 있어야 합니다.- 선언하고 사용자에게 부여한
workshop.external_events.manage,workshop.external_events.read권한. - 이 테넌트의 발급 결과 handle을 저장할 App 소유 연동 레코드. 이후 요청에서는 해당 레코드의 접근 권한을 확인한 뒤 handle을 읽습니다.
직접 실행한 SQL에는 App의 Controller 검증, 모델 이벤트, API 처리가 적용되지 않습니다. 테이블 제약조건으로 입력 범위를 제한하고, 받은 원시 이벤트는 App에서 검증·처리한 뒤 업무 데이터로 사용하세요. 내부 승인·소유자·처리 상태처럼 외부 프로그램이 결정해서는 안 되는 칼럼은 허용하지 않습니다.
계정 발급 요청하기
인증된 연동 서비스에서 입력 대상과 권한을 코드로 정의합니다. 클라이언트가 테이블·칼럼 목록·권한명을 지정하게 하지 마세요. 아래는 App 소유 연동 레코드를 먼저 저장한 뒤 실행할 서비스 내부 코드입니다.
use Nexia\Laravel\Database\Contracts\ExternalDatabaseWriterProvisioner;
use Nexia\Laravel\Database\ExternalDatabaseWriterDefinition;
$writers = app(ExternalDatabaseWriterProvisioner::class);
$definition = new ExternalDatabaseWriterDefinition(
key: 'external_events',
table: 'wsp_external_events',
insertColumns: ['payload'],
permission: 'workshop.external_events.manage',
readPermission: 'workshop.external_events.read',
);
$provisioning = $writers->request($definition);
$handle = $provisioning->handle;$handle을 연동 레코드에 저장합니다. App 상태로 DB 역할명이나 비밀번호를 보관하지 마세요. 계정 준비는 비동기로 진행됩니다. App DB 트랜잭션이 Core에서 발생한 효과까지 되돌리지는 못하므로, 연동 레코드 저장과 Core 발급 요청을 별도 단계로 처리합니다. 같은 정의로 request()를 다시 호출하면 pending/provisioning/ready 상태의 기존 계정을 재사용합니다. 이미 요청한 계정의 대상이나 권한을 바꾸는 경우에는 검토가 필요합니다.
permission은 발급·비밀번호 교체·표시·IP 변경에, readPermission은 상태·IP 조회에 사용됩니다. App 경로에도 대응 권한 검사를 유지하세요. handle을 알고 있다는 이유로 다른 테넌트·App·사용자의 계정에 접근할 수는 없습니다.
실제 전송할 PC·서버의 IP 등록하기
권한을 검사한 App 조회 경로에서 정책을 읽습니다. 아래 $handle은 검증하지 않은 요청값이 아니라 현재 연동 레코드에서 읽은 값입니다.
use Nexia\Laravel\Database\Contracts\ExternalDatabaseWriterNetworkAccess;
$network = app(ExternalDatabaseWriterNetworkAccess::class);
$policy = $network->network($handle);| 필드 | 화면에서 사용할 방법 |
|---|---|
enabled | 게이트웨이를 지원하는 환경에서만 등록 기능을 제공합니다. |
addresses | 저장된 공인 IPv4 목록을 보여줍니다. |
revision | 사용자가 보고 있는 목록과 함께 보관하고 다음 변경 때 전달합니다. |
applied | 저장한 설정과 게이트웨이가 실제 반영한 설정을 구분합니다. |
observed_ip | null이 아니면 ‘현재 IP 사용’으로 제안하고, 실제 전송 PC도 그 IP를 쓰는지 사용자가 확인하게 합니다. |
브라우저를 연 PC와 데이터를 보내는 PC의 IP는 다를 수 있습니다. 회사 공유기에 연결된 노트북은 보통 공유기의 외부 공인 IP를 사용하며, 별도 연동 서버라면 그 서버의 외부 통신 IP를 등록해야 합니다. Wi-Fi·VPN·네트워크가 바뀌면 다시 등록해야 할 수 있습니다. 웹페이지에 접속했다는 이유만으로 IP를 자동 허용하지 마세요.
변경 경로에서 사용자가 제출한 전체 목록과 화면에 표시했던 revision을 검증한 뒤 호출합니다.
// $addresses: 폼에서 받은 값을 검증한 list<string>.
// $expectedRevision: 표시했던 정책의 revision, 검증한 0 이상의 정수.
$saved = $network->setNetwork($handle, $addresses, $expectedRevision);이 호출은 IP 하나를 덧붙이는 방식이 아니라 전체 목록 교체입니다. 사용자가 삭제하지 않은 IP도 함께 전달하세요. 개별 공인 IPv4만 허용하며 CIDR 범위·사설/예약 주소·IPv6·0.0.0.0/0은 받지 않습니다. 현재 한 계정에 최대 5개, 환경 전체에 서로 다른 IP 50개까지 등록할 수 있습니다. 여러 계정이 같은 IP를 쓰면 환경 방화벽에서는 하나의 IP로 관리합니다. 등록 후 AWS와 서버 방화벽에는 자동으로 반영됩니다.
revision이 오래됐으면 HTTP 409가 반환됩니다. 현재 정책을 다시 읽어 사용자가 변경 내용을 확인하게 하세요. 새 revision을 몰래 가져와 다른 사용자의 변경을 덮어쓰면 안 됩니다. IP 형식이나 등록 한도에 문제가 있으면 HTTP 422가 반환됩니다.
적용 확인 후 비밀번호 한 번 표시하기
화면이 열려 있는 동안 status($handle)과 network($handle)을 제한된 간격으로 조회합니다. 예를 들어 5초마다 확인하되, 오래 걸리면 새로고침·복구 안내를 제공하세요. ‘적용 중’과 ‘사용 가능’을 구분해야 합니다. IP 등록 요청이 성공했다는 것만으로 실제 접속이 가능한 것은 아닙니다.
아래 코드는 상태 조회가 아니라, 권한을 가진 사용자가 누른 접속정보 표시 동작의 예제입니다. $writers, $network는 위에서 사용한 SDK 서비스입니다.
$provisioning = $writers->status($handle);
$policy = $network->network($handle);
abort_unless(
$policy['enabled'] && $policy['addresses'] !== [] && $policy['applied']
&& $provisioning->status === 'ready' && $provisioning->connection !== null,
409,
);
$credential = $writers->reveal($handle);
return response()->json([
'host' => $credential->host,
'port' => $credential->port,
'database' => $credential->database,
'schema' => $credential->schema,
'username' => $credential->username,
'password' => $credential->password,
'ssl_mode' => $credential->sslMode,
])->header('Cache-Control', 'no-store');현재 비밀번호의 발급을 요청한 사용자만 관리 권한을 유지한 상태에서 표시할 수 있습니다. DB 계정 준비가 성공한 시점부터 10분 안에 한 번만 표시할 수 있으므로, 상태 조회 중 자동으로 reveal()을 호출하지 마세요. 로그·브라우저 저장소·query cache·App 테이블에 비밀번호를 남기지 않습니다. 외부 프로그램의 자격증명 저장소에 입력할 수 있도록 화면에 잠시 표시하세요. 응답을 받지 못했거나 이미 표시했거나 만료됐다면 표시 요청을 반복하지 말고 사용자의 명시적인 동작으로 비밀번호를 교체합니다.
호스트·포트·DB·스키마·사용자명·SSL 모드는 반환된 값을 사용합니다. 테넌트 ID로 조합하거나 도메인·AWS RDS 주소를 하드코딩하지 마세요. 현재 관리형 환경의 주소는 개발 db-connect.dev.nexia.to, 운영 db-connect.nexia.to입니다. 고정된 이름을 통해 플랫폼이 뒤쪽 서버를 바꿔도 클라이언트 설정을 유지할 수 있습니다. 이 주소는 현재 플랫폼 설정의 예시이지 App 코드에 넣을 상수가 아닙니다.
외부 프로그램에서 입력 확인하기
SCRAM-SHA-256 인증과 TLS 인증서·호스트명 검증을 지원하는 PostgreSQL 드라이버를 사용합니다. 게이트웨이가 반환한 verify-full과 해당 드라이버의 신뢰 CA를 설정하세요. 외부 프로그램이 32비트 ODBC를 요구한다면 드라이버도 맞춰야 합니다. 접속이 안 된다고 TLS 검증을 끄지 마세요.
등록한 공인 IP로 통신하는 PC·서버에서 반환된 접속정보를 설정한 뒤 예제 테이블에 입력합니다.
INSERT INTO wsp_external_events (payload)
VALUES ('{"event":"docs-check"}'::jsonb);계정의 검색 경로는 반환된 스키마로 설정됩니다. 외부 프로그램이 스키마까지 지정한 테이블명을 요구하면 그 값을 사용하세요. 허용한 칼럼의 INSERT와 필요한 시퀀스 접근 권한만 부여하며, 일반적인 SELECT·UPDATE·DELETE는 허용하지 않습니다. RETURNING이나 테이블 조회를 요구하는 전송 방식도 피해야 합니다. 입력된 결과는 권한이 있는 App 화면에서 확인하고, 확인용으로 외부 계정에 조회 권한을 추가하지 마세요.
연습용 테넌트에서 권한 없는 사용자의 등록·비밀번호 표시가 거부되는지, 미등록 IP의 접속이 막히는지, 다른 테이블·보호된 칼럼에 쓸 수 없는지, 마지막 IP를 지우면 접속이 종료되는지도 확인합니다. TCP 연결 성공만으로 로그인이나 INSERT 권한까지 검증되지는 않습니다. 실제 업체 프로그램·ODBC에서의 동작은 해당 프로그램으로 별도 확인해야 합니다.
접속 변경·해제하기
남겨둘 전체 IP 목록과 마지막으로 읽은 revision으로 setNetwork()를 호출합니다. 빈 목록을 보내면 반영 후 해당 계정의 게이트웨이 접속이 차단됩니다. 데이터나 DB 역할을 삭제하는 기능은 아닙니다. 다른 계정이 같은 IP를 사용하면 네트워크 규칙은 유지될 수 있지만, 게이트웨이는 DB·계정·IP 조합별 권한을 계속 검사합니다.
IP만 변경할 때는 비밀번호를 재발급하지 않습니다. 비밀번호를 바꾸려면 같은 서버 정의로 request($definition, rotate: true)를 명시적으로 호출하고, ready/applied 확인 후 새 값을 한 번 표시해 외부 프로그램에 입력하세요. 호스트명이 그대로여도 비밀번호 교체 시에는 클라이언트 값을 갱신해야 합니다.
차단은 비동기입니다. 직전 반영 작업이 끝난 뒤 보통 15초의 주기와 실제 실행 시간이 필요합니다. IP·비밀번호 변경 시 관련 게이트웨이 세션이 종료되며, 초기 구현에서는 같은 DB를 사용하는 다른 입력 계정의 연결도 끊길 수 있습니다. 클라이언트에 횟수·간격을 제한한 재접속을 적용하고, 즉시 차단이나 무중단 교체를 약속하지 마세요.
실패 상태 처리하기
| 확인한 상태 | 조치 |
|---|---|
pending / provisioning 또는 applied: false | 준비·적용 중임을 표시합니다. 제한된 시간 동안 확인한 뒤 새로고침과 플랫폼 문의를 안내하며 이전 접속정보를 보여주지 않습니다. |
enabled: false | 선택한 환경에서 플랫폼 운영자가 기능을 활성화해야 합니다. |
needs_review 또는 external_writer.review_required | 자동 재시도를 중단하고 운영자에게 검토를 요청합니다. DB 할당·계정 정의가 바뀌었거나 기존 계정에 게이트웨이 인증 정보가 없는 경우도 포함됩니다. |
external_writer.allocation_unavailable | 선택한 App의 배포·DB 할당 준비 상태를 확인합니다. 다른 테넌트나 환경의 DB로 우회하지 않습니다. |
external_writer.credential_unavailable | 준비 상태·요청 사용자·표시 기한을 확인합니다. 비밀번호를 더 이상 표시할 수 없으면 명시적으로 교체합니다. |
| HTTP 403 / 404 | 현재 사용자 권한과 테넌트·App 소유 연동 handle을 확인합니다. |
| 정책 교체 시 HTTP 409 | 정책·revision을 다시 읽습니다. 게이트웨이가 꺼진 경우도 있으며 모든 409에 external_writer.* 코드가 붙지는 않습니다. |
| HTTP 422 | IP 목록을 수정하거나 환경 등록 한도를 운영자에게 문의합니다. |
| Timeout | 실제 전송 PC의 외부 IP, 저장·적용 상태와 환경 가용성을 확인합니다. 차단된 네트워크 경로는 비밀번호 변경으로 해결되지 않습니다. |
| TLS 또는 로그인 실패 | 반환된 호스트·SSL 모드, 드라이버의 신뢰 CA·SCRAM 지원, 현재 비밀번호를 확인합니다. 검증을 끄지 않습니다. |
현재 설계의 기준은 공개 SDK의 외부 DB 입력 계약과 Core의 계정 발급·게이트웨이 구현입니다. App 개발자는 이 계약을 사용하며 서버 설치·AWS 권한·게이트웨이 배포는 플랫폼이 담당합니다.