2026년 07월 26일 | DBMS Error 가이드
이 글에서 다루는 내용
ORA-01700 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-01700 duplicate username in list 는?
ORA-01700 에러는 Oracle에서 GRANT 문을 사용하여 권한을 부여할 때 대상 사용자 목록에 동일한 사용자명이 두 번 이상 중복으로 지정되었을 때 발생하는 에러입니다. Oracle 데이터베이스는 하나의 GRANT 문에서 동일한 사용자에게 동일한 권한을 중복으로 부여하는 것을 허용하지 않으며, 이를 감지하는 즉시 해당 에러를 발생시킵니다. 주로 대규모 권한 관리 작업이나 자동화 스크립트에서 사용자 목록을 관리할 때 실수로 중복 항목이 포함되는 경우에 자주 발생합니다.
주요 발생 원인
1. GRANT 문에서 동일 사용자명 중복 기재
가장 흔한 원인으로, GRANT 문의 사용자 목록에 동일한 사용자명을 실수로 두 번 이상 입력하는 경우입니다. 특히 여러 명의 사용자에게 권한을 한 번에 부여하는 긴 스크립트 작업 시, 수작업으로 편집하다가 복사-붙여넣기 실수로 인해 발생하는 경우가 많습니다.
-- 에러 발생 예시: SCOTT이 두 번 등장
GRANT SELECT ON hr.employees TO scott, james, scott;
-- ORA-01700: duplicate username in list
2. 동적 SQL 또는 자동화 스크립트에서의 중복 생성
PL/SQL이나 셸 스크립트를 이용한 자동화된 권한 부여 과정에서 사용자 목록을 동적으로 생성할 때 중복 제거 로직이 없는 경우 발생합니다. 여러 소스에서 사용자 목록을 가져와 합치는 과정에서 중복 항목이 포함되는 경우가 특히 많으며, 이는 운영 환경에서 예기치 않은 장애를 일으킬 수 있습니다.
-- 잘못된 동적 SQL 예시
DECLARE
v_sql VARCHAR2(1000);
BEGIN
-- 여러 쿼리에서 합쳐진 사용자 목록에 중복이 포함될 수 있음
v_sql := 'GRANT SELECT ON hr.employees TO ' ||
'user_a, user_b, user_a'; -- user_a 중복!
EXECUTE IMMEDIATE v_sql;
END;
/
-- ORA-01700: duplicate username in list
3. 대소문자 혼용으로 인한 논리적 중복
Oracle에서 일반적으로 사용자명은 대소문자를 구별하지 않지만(따옴표 없는 경우), 스크립트 작성자가 대소문자가 다르면 다른 사용자라고 오인하여 동일 사용자를 다르게 표기하는 경우가 발생합니다. 실제로 Oracle 내부적으로는 동일한 사용자로 처리되어 ORA-01700 에러가 발생합니다.
-- 대소문자 혼용으로 인한 중복 예시
GRANT SELECT ON hr.departments TO Scott, SCOTT, james;
-- Oracle은 SCOTT과 Scott을 동일 사용자로 처리
-- ORA-01700: duplicate username in list
해결 방법
원인 1 해결: 중복 사용자명 직접 제거
GRANT 문에서 중복된 사용자명을 직접 찾아 제거하는 것이 가장 직접적인 해결 방법입니다.
-- 수정 전 (에러 발생)
GRANT SELECT ON hr.employees TO scott, james, scott;
-- 수정 후 (중복 제거)
GRANT SELECT ON hr.employees TO scott, james;
-- 또는 개별 GRANT 문으로 분리
GRANT SELECT ON hr.employees TO scott;
GRANT SELECT ON hr.employees TO james;
원인 2 해결: 동적 SQL에서 중복 제거 로직 추가
자동화 스크립트에서는 사용자 목록을 처리하기 전에 반드시 중복을 제거하는 로직을 추가해야 합니다.
-- 안전한 동적 권한 부여 PL/SQL 예시
DECLARE
TYPE t_users IS TABLE OF VARCHAR2(30);
v_users t_users := t_users('USER_A', 'USER_B', 'USER_A', 'USER_C');
v_unique t_users := t_users();
v_found BOOLEAN;
v_sql VARCHAR2(4000);
v_list VARCHAR2(4000);
BEGIN
-- 중복 제거 로직
FOR i IN 1 .. v_users.COUNT LOOP
v_found := FALSE;
FOR j IN 1 .. v_unique.COUNT LOOP
IF v_unique(j) = UPPER(v_users(i)) THEN
v_found := TRUE;
EXIT;
END IF;
END LOOP;
IF NOT v_found THEN
v_unique.EXTEND;
v_unique(v_unique.COUNT) := UPPER(v_users(i));
END IF;
END LOOP;
-- 중복 제거된 목록으로 GRANT 수행
FOR i IN 1 .. v_unique.COUNT LOOP
v_sql := 'GRANT SELECT ON hr.employees TO ' || v_unique(i);
EXECUTE IMMEDIATE v_sql;
DBMS_OUTPUT.PUT_LINE('Granted to: ' || v_unique(i));
END LOOP;
END;
/
또한 사전에 딕셔너리 뷰를 활용하여 현재 권한 현황을 확인하고 중복 부여를 사전에 방지할 수 있습니다.
-- 특정 객체에 대해 이미 권한이 부여된 사용자 목록 확인
SELECT GRANTEE, PRIVILEGE, GRANT_OPTION
FROM DBA_TAB_PRIVS
WHERE TABLE_NAME = 'EMPLOYEES'
AND OWNER = 'HR'
ORDER BY GRANTEE;
-- 시스템 권한이 부여된 사용자 목록 확인
SELECT GRANTEE, PRIVILEGE
FROM DBA_SYS_PRIVS
WHERE PRIVILEGE = 'CREATE SESSION'
ORDER BY GRANTEE;
원인 3 해결: 사용자명 대소문자 통일
스크립트 작성 시 사용자명을 항상 UPPER() 함수 또는 대문자로 통일하여 처리합니다.
-- 사용자명 대문자 통일 후 GRANT
GRANT SELECT ON hr.departments TO SCOTT, JAMES;
-- PL/SQL에서 사용자명 정규화
DECLARE
v_username VARCHAR2(30) := 'scott'; -- 소문자 입력
BEGIN
EXECUTE IMMEDIATE
'GRANT SELECT ON hr.departments TO ' || UPPER(v_username);
DBMS_OUTPUT.PUT_LINE('권한 부여 완료: ' || UPPER(v_username));
END;
/
-- DBA_USERS에서 정확한 사용자명 확인
SELECT USERNAME FROM DBA_USERS
WHERE USERNAME LIKE '%SCOTT%'
ORDER BY USERNAME;
예방 방법
Best Practice 1: 권한 부여 전 사전 검증 스크립트 운영
권한 부여 스크립트를 실행하기 전에 반드시 대상 사용자 목록에 중복이 없는지 검증하는 사전 점검 단계를 도입하세요. 아래와 같이 SQL을 활용하여 현재 권한 현황과 대상 사용자 목록을 교차 확인하는 프로세스를 표준화하면 실수를 크게 줄일 수 있습니다.
-- 권한 부여 대상 사용자 목록에서 중복 확인 쿼리
-- (권한 부여 전 사전 점검용)
WITH target_users AS (
SELECT 'SCOTT' AS username FROM DUAL UNION ALL
SELECT 'JAMES' AS username FROM DUAL UNION ALL
SELECT 'SCOTT' AS username FROM DUAL -- 중복!
)
SELECT username,
COUNT(*) AS cnt
FROM target_users
GROUP BY username
HAVING COUNT(*) > 1;
-- 위 쿼리 결과가 행을 반환하면 중복이 존재하는 것이므로 GRANT 실행 금지
Best Practice 2: 개별 GRANT 문 사용 및 예외 처리 로직 구현
한 번에 여러 사용자에게 권한을 부여하는 방식 대신, 사용자별 개별 GRANT 문을 사용하고 PL/SQL의 예외 처리를 함께 구현하면 에러 발생 시에도 나머지 작업이 중단되지 않습니다. 이 방식은 감사(Audit) 로그 기록과 롤백 처리를 명확하게 할 수 있어 대규모 권한 관리 작업에 특히 권장됩니다.
-- 개별 GRANT + 예외 처리 패턴
DECLARE
TYPE t_users IS TABLE OF VARCHAR2(30);
v_users t_users := t_users('SCOTT', 'JAMES', 'ADAMS');
BEGIN
FOR i IN 1 .. v_users.COUNT LOOP
BEGIN
EXECUTE IMMEDIATE
'GRANT SELECT ON hr.employees TO ' || v_users(i);
DBMS_OUTPUT.PUT_LINE('[SUCCESS] Granted to ' || v_users(i));
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('[ERROR] ' || v_users(i) ||
': ' || SQLERRM);
-- 에러가 발생해도 다음 사용자 처리 계속
END;
END LOOP;
END;
/
관련 에러
- ORA-01917:
user or role 'string' does not exist— GRANT 대상 사용자 또는 롤이 데이터베이스에 존재하지 않을 때 발생하며, ORA-01700과 함께 GRANT 문 작성 오류 시 자주 마주칩니다. - ORA-01919:
role 'string' does not exist— 존재하지 않는 롤에 권한을 부여하려 할 때 발생합니다. - ORA-01924:
role 'string' not granted or does not exist— 롤 관련 권한 부여 오류로, 권한 관리 작업 중 함께 나타날 수 있습니다. - ORA-00990:
missing or invalid privilege— 유효하지 않은 권한을 GRANT하려 할 때 발생하며, 잘못된 GRANT 문 작성 시 ORA-01700과 비슷한 맥락에서 발생합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.