SAP BOX

'26/11/12 UPDATE

ABAP / FIELD NOTES

READ TABLE을 itab[ ]로 바꿨더니, 없는 행에서 왜 덤프가 날까

READ TABLE은 못 찾으면 sy-subrc 4를 남기고 다음 줄로 가지만, 같은 읽기를 itab[ ] 테이블 식으로 바꾸면 CX_SY_ITAB_LINE_NOT_FOUND 예외가 나고 잡지 않으면 덤프로 끝납니다. 옛 sy-subrc·sy-tabix 검사가 무엇을 보게 되는지, 예외 잡기·OPTIONAL/DEFAULT·line_exists·ASSIGN 중 무엇을 고를지 가상 예로 정리합니다.

오래된 프로그램을 정리하면서 READ TABLE ... INTO와 그 아래 IF sy-subrc를 ls_price = lt_prices[ item = lv_item ]. 한 줄로 바꾸면, 없는 품목을 찾는 순간 프로그램이 덤프로 끝날 수 있습니다. 아래의 IF sy-subrc <> 0. 검사를 그대로 두었더라도 마찬가지입니다. 이 글을 읽고 나면 READ TABLE과 테이블 식이 '못 찾음'을 어떻게 다르게 알리는지 구분하고, 바꾸는 자리마다 예외 잡기, 기본값, 존재 확인, ASSIGN 중 하나를 골라 새 덤프 없이 코드를 줄일 수 있습니다.

예시는 가상 표준 테이블 lt_prices(열 item과 price)와 가상 프로그램 ZDEMO_ITAB_EXPR입니다. 테이블에는 A-100, A-200, A-300 세 품목이 있고, 없는 품목 A-250을 찾습니다. 코드는 실제 시스템에서 실행하지 않았습니다. 동작은 ABAP 키워드 문서 7.58판의 테이블 식 설명(영어)과 관련 페이지에 적힌 대로 옮겼습니다.

READ TABLE은 못 찾아도 정상으로 끝난다

READ TABLE은 행 하나를 읽고 시스템 필드 sy-subrc와 sy-tabix를 채웁니다. 찾으면 sy-subrc는 0이고 sy-tabix는 사용한 테이블 인덱스에서 그 행의 번호입니다. 해시 키로 읽었다면 sy-tabix는 0입니다. 못 찾으면 sy-subrc는 4입니다. 이진 검색으로 찾다가 테이블 끝에 닿은 경우에는 8이고, 이때 sy-tabix는 행 수에 1을 더한 값입니다. 이진 검색이 아닌 읽기에서 못 찾으면 sy-tabix는 정해지지 않습니다. COMPARING을 쓸 때만 나오는 2는 '찾음'의 한 갈래입니다.

핵심은 못 찾았을 때도 문장이 정상으로 끝난다는 점입니다. 작업 영역 ls_price는 그대로 있거나 초기 상태이고, 프로그램은 다음 줄로 갑니다. 그래서 오래된 코드는 READ TABLE 바로 뒤에 IF sy-subrc <> 0.을 두고 '없음'을 그 안에서 처리합니다. 찾은 행을 고친 뒤 MODIFY ... INDEX sy-tabix로 되돌려 쓰는 패턴도 같은 시스템 필드에 기댑니다. 이 패턴은 문서의 READ TABLE 예제에도 나옵니다.

SAP GUI ABAP 편집기 화면. 가상 표준 테이블 lt_prices에서 없는 품목 A-250을 READ TABLE INTO ls_price WITH KEY로 읽고 IF sy-subrc <> 0으로 검사한 뒤 sy-tabix를 lv_row에 담는 옛 코드와, 같은 읽기를 ls_price = lt_prices[ item = lv_item ]로 바꾸고 같은 sy-subrc 검사와 sy-tabix 대입을 그대로 둔 새 코드에 번호 네 개가 표시되어 있다.
READ TABLE과 같은 뜻으로 바꾼 테이블 식을 한 화면에 놓은 예시. ZDEMO_ITAB_EXPR와 품목 값은 가상이며 코드는 실행하지 않았다.

화면의 번호를 순서대로 읽으면 이 글의 흐름이 됩니다. 1번은 READ TABLE이 sy-subrc와 sy-tabix를 남기는 자리, 2번은 같은 읽기를 테이블 식으로 바꾼 줄, 3번은 그대로 남은 옛 sy-subrc 검사, 4번은 sy-tabix를 옮겨 담는 줄입니다.

테이블 식은 못 찾으면 예외를 던진다

테이블 식 itab[ ... ]는 대괄호 안의 조건으로 행 하나를 찾아 그 자리에서 피연산자로 씁니다. 문서는 테이블 식을 READ TABLE의 한 변형을 줄여 쓴 형태로 볼 수 있다고 설명하지만, 못 찾았을 때의 동작은 다릅니다. 지정한 행이 없으면 모든 피연산자 위치에서 잡을 수 있는 예외 CX_SY_ITAB_LINE_NOT_FOUND가 발생합니다. item = lv_item 같은 키 조건이든 lt_prices[ 5 ] 같은 행 번호든 같습니다. TRY ... CATCH로 잡지 않으면 프로그램은 런타임 오류 ITAB_LINE_NOT_FOUND로 끝나고, 이것이 ST22에서 보는 덤프입니다.

예외가 나지 않는 경우는 문서에 네 가지로 정해져 있습니다. 결과를 VALUE나 REF로 정의하면서 기본값을 줄 때, ASSIGN 문에 쓸 때(sy-subrc가 4), 술어 함수 line_exists 안에 쓸 때(거짓), 테이블 함수 line_index 안에 쓸 때(0)입니다. 이 네 자리 밖의 itab[ ]는 '없음'을 예외로만 알립니다.

SAP GUI ST22 런타임 오류 화면 예시. 런타임 오류 ITAB_LINE_NOT_FOUND, 예외 CX_SY_ITAB_LINE_NOT_FOUND, 가상 프로그램 ZDEMO_ITAB_EXPR, 28행이 표시되고, 오류 분석에 예외가 발생했지만 잡히지 않았다고 적혀 있다. 소스 코드 발췌의 28행 ls_price = lt_prices[ item = lv_item ]에 번호가 붙어 있고 그 아래에 IF sy-subrc <> 0 검사가 있다.
ST22에서 덤프를 연 화면 예시. 프로그램, 줄 번호와 값은 가상이며 실제 시스템에서 나온 덤프가 아니다.

화면 예시에서 덤프가 가리키는 줄(3번)은 테이블 식이 있는 28행이고, 바로 아래 IF sy-subrc는 실행되지 않았습니다. 예외 클래스에는 실패한 접근의 행 번호나 키를 보여 주는 속성이 있습니다. 다만 한 문장에 행 번호로 읽는 테이블 식을 여러 개 쓰면 어느 것이 실패했는지 구분할 수 없다고 문서에 적혀 있습니다.

옛 sy-subrc·sy-tabix 검사는 무엇을 보고 있나

READ TABLE을 테이블 식으로 바꾸면서 뒤의 검사를 그대로 두면, 그 검사는 더 이상 같은 뜻이 아닙니다. 먼저 sy-tabix입니다. 문서는 READ TABLE과 달리 테이블 식이 ASSIGN에 쓰일 때를 빼고는 sy-tabix를 바꾸지 않는다고 적습니다. 그러면 화면 4번의 lv_row = sy-tabix.에는 그보다 앞서 sy-tabix를 바꾼 문장의 값이 들어갑니다. LOOP AT 안이라면 LOOP가 넣은 현재 행 번호가 남아 있을 수 있습니다. 그 값으로 MODIFY lt_prices FROM ls_price INDEX sy-tabix를 하면, 방금 읽은 행과 관계없는 번호의 행을 고치게 됩니다.

다음은 sy-subrc입니다. 문서가 테이블 식의 sy-subrc 값을 정해 둔 곳은 ASSIGN뿐입니다. 일반 피연산자 위치에서는 행이 없으면 예외가 먼저 나므로, 화면 3번의 IF sy-subrc <> 0. 블록은 '없음'을 처리할 기회를 얻지 못합니다. 행이 있을 때 그 IF가 읽는 값도 이 테이블 식이 정한 값이라는 근거가 문서에 없습니다. 옛 검사는 덤프를 막지도, 결과를 확인하지도 못하는 줄로 남습니다.

SAP 노트

같은 읽기, 다른 시스템 필드

가상 표준 테이블 lt_prices에서 없는 품목 A-250을 찾을 때

  • READ TABLE … INTO ls_price

    sy-subrc = 4

    • 찾으면 sy-subrc 0, sy-tabix = 행 번호
    • 못 찾으면 sy-subrc 4 · 예외 없음
    • 이진 검색이 끝에 닿으면 8
    • ls_price는 그대로 또는 초기 상태
  • ls_price = lt_prices[ … ]

    CX_SY_ITAB_LINE_NOT_FOUND

    • 못 찾으면 이 예외가 발생
    • 다음 줄의 IF sy-subrc에 닿지 않음
    • sy-tabix를 바꾸지 않음
    • 남는 값은 앞 문장의 것
  • ASSIGN lt_prices[ … ] TO <fs>

    sy-subrc = 4

    • 못 찾으면 sy-subrc 4 · 예외 없음
    • 정렬 테이블 이진 검색이 끝에 닿으면 8
    • sy-tabix도 READ TABLE처럼 설정
    • <fs>는 이전 상태 유지 → sy-subrc로 검사

문서가 테이블 식의 sy-subrc 값을 정해 둔 곳은 ASSIGN뿐입니다. LOOP 안이라면 남아 있는 sy-tabix는 LOOP가 넣은 현재 행 번호일 수 있습니다. 값과 이름은 가상입니다.

READ TABLE은 못 찾음을 sy-subrc로 알리고, 테이블 식은 예외로 알립니다. 테이블 식이 시스템 필드를 READ TABLE처럼 채우는 것은 ASSIGN에 쓸 때뿐입니다.

'없음'의 뜻에 따라 고른다

문서가 제시하는 방법은 네 가지이고, 무엇을 고를지는 그 코드에서 '없음'이 무슨 뜻인지에 달려 있습니다.

  • 행이 보통 있어야 한다면 테이블 식을 필요한 자리에 그대로 쓰고 TRY ... CATCH cx_sy_itab_line_not_found로 잡습니다. 없는 것이 데이터 오류라면 CATCH 블록에서 메시지를 남기거나 처리를 멈춥니다. line_exists 문서도 행이 대개 있는 경우에 이 방법을 제시합니다.
  • 없을 때 초기값이나 기본값이면 충분하다면 VALUE #( lt_prices[ item = lv_item ] OPTIONAL )로 씁니다. 행이 없으면 예외 대신 초기 상태의 행이 결과가 됩니다. OPTIONAL 자리에 DEFAULT VALUE #( item = lv_item )처럼 쓰면 직접 정한 값이 결과입니다. 기본값은 결과를 VALUE나 REF로 정의할 때만 붙일 수 있습니다. 못 찾았을 때 작업 영역을 그대로 둘 수 있던 READ TABLE과 달리, 결과가 매번 새로 정해진다는 점도 다릅니다.
  • 있는지만 알면 된다면 IF line_exists( lt_prices[ item = lv_item ] ).를 씁니다. 없으면 거짓이고 예외는 없습니다. 행 번호가 필요하면 line_index( )가 찾은 행의 번호를, 없으면 0을 돌려줍니다. line_exists는 sy-tabix를 채우지 않습니다. 문서는 line_exists로 확인한 뒤 같은 행을 다시 읽는 코드를 피하라고 합니다. 같은 검색을 두 번 하기 때문입니다.
  • 읽고 바로 분기하려면 ASSIGN lt_prices[ item = lv_item ] TO FIELD-SYMBOL(<ls_price>). 다음에 IF sy-subrc = 0.을 둡니다. 문서는 이 형태를 READ TABLE ... ASSIGNING의 다른 형태로 봅니다. 못 찾으면 예외 없이 sy-subrc가 4이고(정렬 테이블 이진 검색에서 끝에 닿으면 8), sy-tabix도 READ TABLE과 같은 방식으로 설정됩니다. 필드 심볼로 행을 바로 고칠 수 있어 MODIFY ... INDEX sy-tabix도 필요 없습니다. 못 찾았을 때 필드 심볼은 이전 상태를 유지하므로 IS ASSIGNED가 아니라 sy-subrc로 검사합니다.

SAP 노트

'없음'을 무엇으로 처리할까

문서가 정한 네 가지 방법, 가상 테이블 lt_prices 기준

  • 행이 있어야 정상일 때

    CATCH cx_sy_itab_line_not_found

    • TRY. 안에서 lt_prices[ … ]를 그대로 쓴다
    • 없으면 CATCH 블록에서 처리
    • 잡지 않으면 ITAB_LINE_NOT_FOUND 덤프
  • 초기값·기본값이면 충분할 때

    VALUE #( lt_prices[ … ] OPTIONAL )

    • OPTIONAL: 초기 상태의 행
    • DEFAULT def: 직접 정한 값
    • VALUE나 REF로 결과를 정할 때만
    • 7.40 SP08부터
  • 있는지만 알면 될 때

    line_exists( lt_prices[ … ] )

    • 없으면 false · 예외 없음
    • 행 번호는 line_index( ) · 없으면 0
    • 확인한 뒤 같은 행을 다시 읽지 않는다
  • 읽고 바로 분기할 때

    ASSIGN lt_prices[ … ] TO <fs>

    • 없으면 sy-subrc 4 · 예외 없음
    • sy-tabix도 READ TABLE처럼 설정
    • IS ASSIGNED가 아니라 sy-subrc를 검사

line_exists로 확인한 뒤 같은 행을 다시 읽으면 같은 검색을 두 번 합니다. 7.57부터는 ASSIGN에 ELSE UNASSIGN을 붙이면 IS ASSIGNED로도 검사할 수 있습니다. 코드는 가상 예시입니다.

바꾸기 전에 '없음'이 버그인지, 기본값이면 되는지, 분기할 일인지 정하면 네 방법 중 하나가 정해집니다.

릴리스와 BINARY SEARCH

릴리스 노트로 보면 테이블 식과 line_exists, line_index는 7.40 SP02에서 들어왔고, OPTIONAL과 DEFAULT는 7.40 SP08에서 추가되었습니다. 그 사이 지원 패키지의 시스템에서는 테이블 식은 쓸 수 있어도 기본값은 쓸 수 없으므로 TRY ... CATCH나 ASSIGN으로 처리합니다. 못 찾았을 때 필드 심볼을 비워 주는 ELSE UNASSIGN은 7.57에서 테이블 식 ASSIGN에도 붙일 수 있게 되었고, 이 옵션을 쓰면 IS ASSIGNED로 검사해도 됩니다.

BINARY SEARCH를 붙인 READ TABLE을 바꿀 때는 한 가지를 더 봅니다. 자유 키로 쓴 테이블 식에는 이진 검색을 강제하는 방법이 없습니다. 이진 검색이 필요하면 정렬 보조 키를 선언하고 테이블 식에 KEY 이름을 적어 그 키로 읽습니다. 문서상 정렬 키로 읽을 때는 이진 검색이 일어납니다.

줄이기 전에 정할 것

READ TABLE을 테이블 식으로 바꾸는 일은 문법 정리가 아니라 '못 찾음'의 처리 방식을 바꾸는 일입니다. 바꾸는 자리마다 바로 아래의 sy-subrc와 sy-tabix 검사를 찾아 두고, 한 가지 질문에 먼저 답하세요. 이 행이 없으면 버그인가(예외를 잡는다), 기본값이면 되는가(OPTIONAL/DEFAULT), 바로 갈라져야 하는가(ASSIGN과 sy-subrc). 답이 정해지면 옛 검사는 지우거나 그 방법에 맞게 다시 씁니다.

끝 · END OF NOTE
RSS 구독목록으로
COMMENTS BOX

이 기록에 대화를 더해 주세요.

궁금한 점, 다른 접근, 직접 해 본 결과를 나눠 주세요.

최신순

로그인 상태 확인 중…

댓글을 불러오는 중…