🐢🐢꼬부기 LV.2 | 실습•에러/🛡️껍질에 숨기(에러해결)

🛠️ 이클립스 SQL 트리거 에러해결하기 🛠️

서화 2025. 12. 11. 23:55

🐞 트리거 생성오류 🐞

트리거를 만들려고 실행할때 생기는 오류이다 오류메세지가 안뜨는데 실패한 상황이어서 이유를 찾기가 어려웠다

🎯  내가 만들려고한 트리거

CREATE OR REPLACE TRIGGER TRG_ADDRESS_DEFAULT 
BEFORE INSERT OR UPDATE ON ADDRESS 
FOR EACH ROW 
WHEN(:NEW.ADDRESS_IS_DEFAULT = 1) 
BEGIN UPDATE ADDRESS SET ADDRESS_IS_DEFAULT = 0
WHERE ACCOUNT_PK = :NEW.ACCOUNT_PK 
AND ADDRESS_PK <> :NEW.ADDRESS_PK;
END; 
/

🔍  원인으로 볼수있는 경우 5가지

  • WHEN절 괄호앞뒤 공백없음
  • 이클립스실행창이 /줄을 인식하지 못하는경우
  • ADDRESS_PK컬럼이 존재하지 않는경우
  • 같은테이블에 대한 UPDATE 트리거 안에서 UPDATE 하려고해서
  • 트리거 생성권한이 없는경우

📌 확인결과 WHEN절 공백없음과 트리거에서 사용할 INSERT 실행시  :NEW.ADDRESS_PK가 없어서 비교할수 없기때문이다 

WHEN(:NEW.ADDRESS_IS_DEFAULT = 1) 
AND ADDRESS_PK <> :NEW.ADDRESS_PK;

🛠️ 해결방법

WHEN절에 공백을 추가하면서 NEW앞에있던 콜론도 삭제해줬다 그리고 ADDRESS_PK컬럼의  PK를 자동으로 만들어주기 위해 시퀀스를 추가하고 트리거를 INSERT시 실행과 UPDATE시 실행을 나눴다 이제 INSERT시 ADDRESS_PK가 새로만들어질 PK라서 비교할 필요가 없다

CREATE OR REPLACE TRIGGER TRG_ADDRESS_DEFAULT
BEFORE INSERT OR UPDATE OF ADDRESS_IS_DEFAULT ON ADDRESS
FOR EACH ROW
WHEN (NEW.ADDRESS_IS_DEFAULT = 1)
BEGIN
    -- UPDATE일 때만 자기 주소 제외
    IF INSERTING THEN
        UPDATE ADDRESS
        SET ADDRESS_IS_DEFAULT = 0
        WHERE ACCOUNT_PK = :NEW.ACCOUNT_PK;
    ELSIF UPDATING THEN
        UPDATE ADDRESS
        SET ADDRESS_IS_DEFAULT = 0
        WHERE ACCOUNT_PK = :NEW.ACCOUNT_PK
          AND ADDRESS_PK <> :NEW.ADDRESS_PK;
    END IF;
END;
/

이제 트리거를 만들기위해 실행하면 또 실패가뜨긴하는데 화살표 눌러보면 파란체크표시가 보이면서 트리거가 생성됐다고 표시된다 

즉 트리거는 정상적으로 사용가능하지만 에러가 난것처럼 보이는 상황이다 이클립스가 END;와  / 부분을  하나의 SQL문으로 인식해서 실행하려다 실패해서 에러처럼 표시한것으로 무시해도 된다
트리거가 정상인지 확인하기위해서 USER_TRIGGERS를조회하는게 제일 확실한 방법이다 여기서 STATUS가 ENABLED라면 성공이다

-- 트리거 상태확인
SELECT TRIGGER_NAME, STATUS
FROM USER_TRIGGERS
WHERE TRIGGER_NAME = 'TRG_ADDRESS_DEFAULT';

🐞ORA-04098 : 트리거가 유효하지 않아 재검증에 실패했습니다🐞

트리거 생성오류를 해결하고 INSERT문을 실행시켰을때 만난 에러다 트리거가 유효하지 않아서 실행할수 없다는 에러문구다

🔍  원인분석

위에서 트리거가 정상적으로 만들어진것을 확인했는데 왜 이런 에러가 발생했냐하면 실제로는 트리거가 INVALID 즉 유효하지 않은 상태이기 때문이다
그러면 트리거 확인할때 왜 정상적으로 만들어졌다고 알려줬냐면 이클립스는 컴파일에 실패해도 ENABLED 라고 보여주기때문이다 그래서 꼭 오류내용을 직접 조회 해봐야 정확하게 알수 있다

--트리거 에러내용확인
SELECT * FROM USER_ERRORS
WHERE NAME = 'TRG_ADDRESS_DEFAULT';

실행시키면 이렇게 나오는데 여기서 중요한게 LINE,POSITION,TEXT다 차례대로 문제가 발생한 줄, 해당줄에서 에러위치,에러내용 전체다 따라서 TEXT컬럼의 문구를 봐야 정확한 에러 내용을 알수 있다

PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: ;

➡ 이 오류는 트리거의 BEGIN~END 블록이 비정상적으로 끝났다는 뜻으로 구문이 완성되지 않았다는 것이다
원인으로는 END; 뒤에 / 가 제대로 인식되지 않아서 발생했거나 IF/ELSIF/END IF; 맞지 않음 등의 IF문 문법에러가 있다

PLS-00049: bad bind variable ""

➡ 이 오류는 이클립스가 :NEW.address_pk 또는 :NEW.account_pk 같은 변수이름을 제대로 인식하지 못해서 생긴것이다
원인으로 오타가 났거나,위치가 잘못됬거나 ADDRESS_PK가 NULL이 가능한데 비교하려고 시도했을때 생긴다
결론적으로 두 에러 다 트리거의 문법이 깨졌기 때문이다

🛠️ 해결방법

기존 트리거를 삭제하고 트리거 문법에 맞게 수정해서 다시 실행했다

CREATE OR REPLACE TRIGGER TRG_ADDRESS_DEFAULT
-- 기본배송지를 설정하려는 상황(ADDRESS_IS_DEFAULT = 1)에서
-- 같은 회원의 기존 기본배송지를 자동으로 해제해주는 트리거

BEFORE INSERT OR UPDATE OF ADDRESS_IS_DEFAULT ON ADDRESS
-- ADDRESS 테이블에서 INSERT 또는 ADDRESS_IS_DEFAULT 컬럼 UPDATE 전에 작동함

FOR EACH ROW
-- 한 행(row) 단위로 트리거 실행

WHEN (NEW.ADDRESS_IS_DEFAULT = 1)
-- 새로 입력되거나 수정되는 레코드가 "기본배송지로 설정되었을 때"만 실행됨

BEGIN
    -- [1] INSERT 상황 (배송지 신규 등록)
    -- 새 배송지를 기본배송지로 체크하여 등록하려는 경우
    -- 아직 새 주소의 PK값(:NEW.ADDRESS_PK)이 없거나 안정적이지 않으므로
    -- 기존 기본배송지 해제만 처리하고 "자기 자신 제외" 조건은 쓰지 않음.

    IF INSERTING THEN
        UPDATE ADDRESS
        SET ADDRESS_IS_DEFAULT = 0
        WHERE ACCOUNT_PK = :NEW.ACCOUNT_PK;
        -- 같은 회원의 모든 기존 배송지 기본값을 0으로 초기화함.
        -- 이후 새로 INSERT된 주소가 기본배송지가 됨.

    -- [2] UPDATE 상황 (기존 배송지를 기본배송지로 변경할 때)
    -- 이미 PK가 존재하는 주소이므로 자기 자신을 제외하고 기존 배송지를 해제해야 함.
    ELSIF UPDATING THEN
        UPDATE ADDRESS
        SET ADDRESS_IS_DEFAULT = 0
        WHERE ACCOUNT_PK = :NEW.ACCOUNT_PK
          AND ADDRESS_PK <> :NEW.ADDRESS_PK;
        -- 같은 회원이 가진 다른 배송지들의 기본값만 해제함.
        -- UPDATE되는 해당 주소만 기본배송지로 유지됨.
    END IF;
END;
/

🐞UPDATE 실행시 트리거가 유효하지 않아 재검증에 실패했습니다 🐞

위에서 다 해결했다고 생각했는데 UPDATE문을 실행하니 INSERT를 실행할때랑 똑같은 에러가 발생했다

🔍 원인분석

트리거가 작성된 코드와 실제 DB에 저정된 코드가 다르거나 컬럼명/변수명에 오타가있을때 발생한다
따라서 현재 DB에 저장된 트리거의 내용을 확인해야한다

SELECT TEXT
FROM USER_SOURCE
WHERE NAME = 'TRG_ADDRESS_DEFAULT'
ORDER BY LINE;

확인결과를 보면 DB의 트리거의 내용이 절반만 저장되어 있는것을 볼수 있다 그래서 에러가 계속 발생했던 것이었다
DB에 왜 절반만 저장이 되었냐면 이클립스의 SQL실행기가 PL/SQL 블록을 쪼개서 실행했기때문에 절반만 저장된것이다

🛠️ 해결방법

이클립스 말고 SQL Developer에서 실행해서 새로운 트리거를 만들어야한다 SQL Developer는 오라클 DB전용 GUI 툴로 별도의 프로그램을 다운 받아서 실행해야한다
SQL Developer 다운로드 링크 https://www.oracle.com/database/sqldeveloper/

SQL Developer

Oracle SQL Developer is a free, development environment that simplifies the management of Oracle Database in both traditional and Cloud deployments. It offers development of your PL/SQL applications, query tools, a DBA console, a reports interface, and mor

www.oracle.com

링크로 들어가서 메인화면에 다운로드를 눌러준다

다음으로 다운 받을 버전을 선택하면 되는데 아래 사진에 있는 17 included 버전을 다운 받으면 된다

파일을 받았으면 압축을 풀고 

exe를 눌러 실행하면 이런화면이 나온다 이화면은 예전 SQL Developer 설정가져올래? 물어보는 거라서 아니오 누르고 넘어가면 된다

아니오를 누르면 이렇게 메인화면이 나오는데

여기서 맨 왼쪽 접속에 보면 초록색 + 표시가 있다 이걸 누르면 현재 내가 이클립스에서 사용하고 있는 DB를 연결할수 있다

새로만들기를 눌러 DB연결을 해준다

이름과 DB 아이디와 비밀번호를 입력하고 호스트이름/포트/SID까지 현재 이클립스에서 사용하는 DB설정과 같다면 테스트를 눌러서 연결이 잘됫는지 테스트해본다 성공이 뜨면 접속을 눌러서 창을 닫는다 그러면 아래사진과 같이 DB가 잘 추가된것을 확인할수 있다

연결에 성공하면 자동으로 SQL Worksheet가 열리는데 자동으로 안열린다면 Control+Shift+N을 누르면 열린다 이제 여기서 새로운 트리거를 만들면 된다 새로운 트리거를 만들기전에 기존 트리거 삭제를 먼저 진행한다음에 새로만들어야한다

DROP TRIGGER TRG_ADDRESS_DEFAULT;

그러면 이렇게 결과를 알려준다 위의 트리거 코드를 수정한 새로운 트리거를 SQL Worksheet안에서 만들어준다 SQL Developer에서는 /을 블록끝으로 인식하기때문에 이클립스처럼 트리거가 깨지지 않는다

CREATE OR REPLACE TRIGGER TRG_ADDRESS_DEFAULT
BEFORE INSERT OR UPDATE ON ADDRESS
FOR EACH ROW
BEGIN
    -- 기본배송지로 체크된 경우만 트리거 실행
    IF :NEW.ADDRESS_IS_DEFAULT = 1 THEN

        -- 신규 등록(INSERT)일 경우: 기존 기본배송지를 모두 0으로 초기화
        IF INSERTING THEN
            UPDATE ADDRESS
            SET ADDRESS_IS_DEFAULT = 0
            WHERE ACCOUNT_PK = :NEW.ACCOUNT_PK;

        -- 수정(UPDATE)일 경우: 본인을 제외한 기존 배송지를 0으로 초기화
        ELSIF UPDATING THEN
            UPDATE ADDRESS
            SET ADDRESS_IS_DEFAULT = 0
            WHERE ACCOUNT_PK = :NEW.ACCOUNT_PK
              AND ADDRESS_PK <> :NEW.ADDRESS_PK;
        END IF;

    END IF;
END;
/

실행결과를 보면 새로운 트리거가 잘 만들어진것을 볼수있다 이제 INSERT와 UPDATE 할때 에러가 없는지 확인하기 위해서 INSERT와 UPDATE를 실행해보았다 

INSERT INTO ADDRESS (
    ADDRESS_PK, ACCOUNT_PK, ADDRESS_NAME, ADDRESS_POSTAL_CODE,
    ADDRESS_IS_DEFAULT, ADDRESS_LINE1, ADDRESS_LINE2
) VALUES (
    ADDRESS_SEQ.NEXTVAL, 2, '집', '12345', 1, '서울시 강남구', '4층'
);
UPDATE ADDRESS
SET ADDRESS_IS_DEFAULT = 1
WHERE ADDRESS_PK = 2;

INSETRT와  UPDATE가 문제없이 잘 실행된것을 볼수있다 마지막으로 트리거가 잘 저장됐는지 확인하기 위해 USER_SOURCE로 저장 여부를 확인해야한다

SELECT TEXT
FROM USER_SOURCE
WHERE NAME = 'TRG_ADDRESS_DEFAULT'
ORDER BY LINE;

그러면 이렇게 결과를 볼수 있고 문제없이 잘 저장된 모습이다 이제 트리거는 이클립스에서 사용하는게 아니고 SQL Developer에서 사용해야한다 따라서 이클립스에 있던 기존 트리거 코드를 모두 삭제한 다음에 INSERT를 실행했을때 문제없이 DB에 저장된것을 확인할수 있다

📌 이클립스에 트리거 코드가 없어도 되는이유

트리거는 DB안에 저장되는 객체이기 때문이다 따라서 이클립스에 코드가 없어도 자동으로 동작한다
SQL Developer에서 한번만 만들면되고 개발자는 INSERT와 UPDATE를 실행시켜주기만하면된다

✅ 결론

트리거 생성오류는 문법문제와 ADDRESS_PK가 없어서 생긴문제였고 나머지 에러들은 이클립스가 SQL문을 인식하지 못하는 버그가 있었기때문이다 그래서 SQL Developer를 설치했고 정상작동까지 확인했다