python-oracledb 접속 확립: thin/thick, Instant Client, libaio, NLS
개요
이관의 첫 관문은 소스 Oracle에 파이썬으로 접속하는 것이다. 겉보기엔 oracledb.connect() 한 줄이지만, 폐쇄망의 오래된 서버 앞에서는 드라이버 모드(thin/thick), 네이티브 클라이언트 라이브러리(Instant Client, libaio), 문자셋(NLS)이 얽혀 좀처럼 한 번에 붙지 않는다.
접속 문제를 진단할 때 가장 중요한 원칙은 “네트워크로 닿는가”와 “인증이 되는가”를 분리하는 것이다. 리스너·방화벽 문제(ORA-12170, ORA-12514, ORA-12505)와 인증·프로토콜 문제(DPY-3010, DPY-3015)는 원인도 해법도 완전히 다르다. 이 둘을 섞으면 방화벽을 뚫어 놓고 패스워드만 계속 바꾸는 헛수고를 하게 된다.
핵심 개념
thin vs thick 모드
python-oracledb는 두 가지 모드로 동작한다.
| 항목 | thin 모드 | thick 모드 |
|---|---|---|
| 네이티브 클라이언트 | 불필요 (순수 파이썬) | Oracle Instant Client 필요 |
| 지원 서버 버전 | ≥ 12.1 | 11g 등 구버전 포함 |
| 배포 편의성 | 높음 (pip만으로) | 낮음 (.so/.dll 배치 필요) |
| 구형 password verifier | 미지원 | 지원 |
| NLS 처리 | 파이썬 레벨 | 클라이언트 라이브러리 레벨 |
기본은 thin이다. 배포가 간단하고 의존성이 없기 때문이다. 그러나 소스가 오래된 서버라면 thin이 접속을 거부한다. 이 경우 thick으로 fallback해야 한다.
Instant Client와 버전 선택
thick 모드는 Oracle Instant Client 라이브러리를 로드한다. 구서버(11g)에 붙어야 하는 상황에서도 클라이언트는 19c(LTS) 를 쓰는 것이 안전하다. 19c 클라이언트는 11.2 이상 서버와 호환되며 장기 지원(LTS)이라 폐쇄망에 오래 두기 적합하다. 패키지는 NLS 데이터를 포함하는 Basic 배포를 쓴다(Basic Light는 일부 문자셋이 빠져 한글이 깨질 수 있다).
실전
thin → thick fallback 접속 코드
thin을 먼저 시도하고, 실패하면 thick을 초기화해 재시도하는 패턴이 실전에서 가장 견고하다.
import oracledb
def connect(user: str, password: str, dsn: str):
try:
return oracledb.connect(user=user, password=password, dsn=dsn) # thin 우선
except oracledb.DatabaseError as e:
(err,) = e.args
# 구서버/구형 verifier면 thick으로 전환
if err.full_code in ("DPY-3010", "DPY-3015"):
oracledb.init_oracle_client(lib_dir="/opt/oracle/instantclient_19")
return oracledb.connect(user=user, password=password, dsn=dsn)
raiseinit_oracle_client()는 프로세스당 한 번만 호출할 수 있으므로, 예외 처리 안에서 상태를 관리하거나 최초에 조건적으로 초기화한다.
네트워크 도달성부터 확인
접속 코드를 짜기 전에 리스너에 TCP로 닿는지부터 본다. 인증 이전 단계다.
# 리스너 포트 도달성 (인증 이전)
nc -vz oracle-host 1521
# tnsping으로 서비스 등록 여부 확인 (Instant Client 포함 도구)
tnsping "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=oracle-host)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=orclpdb)))"여기서 막히면 인증 문제가 아니라 리스너·방화벽·서비스명 문제다.
함정·트러블슈팅
| 코드/증상 | 원인 | 해법 |
|---|---|---|
DPY-3010 | thin 모드가 서버 버전(예: 11g)을 미지원 | thick + Instant Client 19c로 전환. 패스워드 재설정으로는 해결 안 됨 |
DPY-3015 | 계정이 구형(11g) password verifier 사용 | DBA가 ALTER USER ... IDENTIFIED BY ...로 verifier 재생성, 또는 thick fallback |
libaio.so.1 로드 실패 | Instant Client가 libaio 의존 | 패키지 설치 + 심볼릭 링크(아래) |
한글이 ?/모지바케로 깨짐 | 클라이언트 charset 불일치 | Basic 배포 사용, KO16MSWIN949 ↔ AL32UTF8 확인 |
ORA-12170/12514/12505 | 리스너·타임아웃·서비스명 문제 | 인증이 아니라 네트워크 계층 → tnsping/방화벽 점검 |
DPY-3010 — thin이 구서버를 거부
이 오류는 “서버가 너무 오래됐다”는 명확한 신호다. 흔한 오해가 패스워드 문제로 착각하고 계정을 리셋하는 것인데, 아무 소용이 없다. thick으로 전환하고 Instant Client 19c를 붙이는 것이 정답이다.
DPY-3015 — 구형 password verifier
계정의 패스워드가 11g 시절 해시(구형 verifier)로만 저장돼 있으면 thin 모드가 인증을 거부한다. 두 가지 길이 있다.
-- (DBA 권한) 새 verifier를 생성하도록 패스워드 재설정
ALTER USER app_reader IDENTIFIED BY "<new-password>";DBA 협조가 어렵다면 thick fallback으로 우회한다. thick은 구형 verifier를 처리할 수 있기 때문이다.
libaio 누락과 Ubuntu 24.04 리네임
Instant Client는 리눅스에서 libaio 없이 초기화에 실패한다. 문제는 Ubuntu 24.04에서 패키지가 libaio1t64로 리네임되면서, Instant Client가 찾는 libaio.so.1이 존재하지 않는다는 점이다. 심볼릭 링크로 이어준다.
sudo apt-get install -y libaio1t64
# Instant Client가 기대하는 이름으로 링크
sudo ln -sf /usr/lib/x86_64-linux-gnu/libaio.so.1t64 \
/usr/lib/x86_64-linux-gnu/libaio.so.1NLS와 인코딩
한글 멀티바이트는 클라이언트 charset이 소스와 어긋나면 곧장 깨진다. 레거시 Oracle은 흔히 KO16MSWIN949(CP949 계열)를 쓰고, PostgreSQL 대상은 AL32UTF8(UTF-8)이다. 접속 시 NLS를 명시해 두면 예측 가능하다.
import os
os.environ["NLS_LANG"] = "KOREAN_KOREA.AL32UTF8" # 드라이버가 UTF-8로 받도록핵심은 인코딩을 추출 단계에서 확정하는 것이다. 잘못 디코딩된 한글은 적재 후 되돌리기 어렵다(→ 04.데이터 정제 (NUL 문자·인코딩·암호화 컬럼)).
정리
접속은 “네트워크 도달”과 “인증”을 분리해 진단하는 것이 절반이다. 나머지 절반은 구서버 대응이다. thin을 기본으로 하되 DPY-3010/DPY-3015를 만나면 Instant Client 19c로 thick fallback하고, 리눅스에서는 libaio 링크를, 한글에서는 NLS charset을 반드시 챙긴다. 접속이 안정되면 다음은 대용량 테이블을 안전하게 끌어오는 추출 단계다.