오늘 완료된 프로젝트에서 오류가 하나 발생하여 수정에 들어간 일이 있다.

웹페이지에서 계층 Tree으로 보여주는 메뉴 구성 부분이 있었는데 자바스크립트 스택 오버 플로우 오류가 발행한 것이다. DB의 테이블을 열어보니 자기 자신을 부모로 갖고있는 ID값이 원인이었다.

Menu ID Menu Parent
1 0
2 0
10 1
10 1
10 2

왜 이런 문제가 발생한 것인가. 위 내용을 보면 ID 값이 계속적으로 같은 값으로 추가되는 것을 볼 수 있다. 일차적으로 테이블 생성시 PK라는 제약조건을 설정하지 않은 것이 문제였다.

다음으로 볼 것은 왜 같은 10이라는 값을 ID로하여 메뉴가 추가되느냐 하는 것이다. 해답은 다음 질의에서 찾을 수 있었다.

select nvl(max(menu_item_id) + 1, 0)
  from tb_menu

현재 있는 최대값에 1을 더해서 새로운 ID 값을 만드는 부분이다. 위 질의에서는 문제될 것이 없었지만 계속 10이라는 값만을 출력하고 있다. 11이 아니고 왜 10인가? Menu ID 컬럼의 데이터 타입(menu_id varchar2(10))을 살펴보면 답이 나온다. 숫자가 아닌 문자열이기 때문에, 또 오라클의 자동 형변환에 의해 신규 ID가 항상 9로 출력되는 바보같은 상황이 온것이다. 처음 테이블 생성시 다음과 같은 구조로 생성하였다면 아무 문제가 없었을 것이다.

create table tb_menu (
  menu_id number,
  menu_id_p number not null,
  ...,
  constraint pk_tb_menu primary key (menu_id)
);

이와 같은 경우의 에러는 ID 값이 10이 넘어가기 전까지는 알 수가 없다. 하지만 제대로된 테이블 생성만으로 피해갈 수 있는 문제이지 않았나 싶다.

추가적으로 오라클에서 max()와 데이터 타입 관련된 간과하기 쉬운 버그성 코드는 다음과 같은 것이 있다.

select max(decode(usage, 'N', null, num_col))
  from tbl

위 예에서 tbl 테이블에서 usage가 'N'이 아닌 num_col에 [7, 10, 100] 이라는 값이 있을 경우 최종 출력되는 값은 7이 된다. 이렇게 되는 원인은 null 값이 기본적으로 char 형태라는 것과 decode() 문에서 처음 나오는 데이터 타입에 따라 자동 형변환 되는 구조에 있다. 원래 의도대로 100이 나오도록 하려면 아래처럼 수정한다.

select max(decode(usage, 'N', to_number(null), num_col))
  from tbl