오래된 프로그램을 정리하면서 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 예제에도 나옵니다.
화면의 번호를 순서대로 읽으면 이 글의 흐름이 됩니다. 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[ ]는 '없음'을 예외로만 알립니다.
화면 예시에서 덤프가 가리키는 줄(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가 넣은 현재 행 번호일 수 있습니다. 값과 이름은 가상입니다.
'없음'의 뜻에 따라 고른다
문서가 제시하는 방법은 네 가지이고, 무엇을 고를지는 그 코드에서 '없음'이 무슨 뜻인지에 달려 있습니다.
- 행이 보통 있어야 한다면 테이블 식을 필요한 자리에 그대로 쓰고
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). 답이 정해지면 옛 검사는 지우거나 그 방법에 맞게 다시 씁니다.
이 기록에 대화를 더해 주세요.
궁금한 점, 다른 접근, 직접 해 본 결과를 나눠 주세요.
로그인 상태 확인 중…
댓글을 불러오는 중…