1. 변환 루틴이란 무엇인가? '저장'과 '표시'의 분리
SAP 시스템을 사용하면서 데이터베이스에는 '0000000000123456'처럼 18자리로 저장된 품목 번호가 화면에는 '123456'으로 표시되거나, 'KG'를 입력하면 '킬로그램'으로 자동 변환되어 나타나는 경험을 해보셨을 것입니다. 이러한 변환을 담당하는 핵심 메커니즘이 바로 '변환 루틴(Conversion Routine)'입니다.
변환 루틴은 SAP의 표준화된 데이터 처리 기능 중 하나로, 데이터가 시스템 내부적으로 저장되는 방식(내부 형식)과 사용자에게 보여지거나 입력받는 방식(외부 형식)이 다를 때 이 둘 사이를 매끄럽게 연결해주는 다리 역할을 합니다. 즉, '내부와 외부의 분리'라는 핵심 원칙에 기반하여 데이터를 효율적으로 관리할 수 있게 합니다.
이 메커니즘은 비즈니스 요구사항의 고전적인 충돌 지점, 즉 '효율적인 저장과 사용자 친화적인 표시'라는 두 마리 토끼를 잡는 데 탁월합니다. 예를 들어, 품목 번호는 데이터베이스에서 검색 및 색인 효율성을 위해 18자리의 선행 0을 포함한 문자열로 저장될 수 있습니다. 그러나 최종 사용자는 보고서에서 이 긴 0들을 보고 싶어 하지 않으며, '123456'과 같은 간결한 표현을 선호합니다. 변환 루틴은 데이터를 사용자에게 보여줄 때는 불필요한 0을 제거하고, 사용자의 입력을 받아 데이터베이스에 저장할 때는 다시 필요한 0을 채워주는 역할을 수행합니다.
변환 루틴은 두 가지 상호 보완적인 함수로 구성됩니다:
- 출력 함수 (Output Function): 일반적으로
CONVERSION_EXIT_XXXX_OUTPUT패턴을 따릅니다. 이 함수는 데이터가 데이터베이스에서 화면, 보고서 또는 인쇄물로 표시될 때 호출되며, 내부 형식을 사용자 친화적인 외부 형식으로 변환합니다. - 입력 함수 (Input Function): 일반적으로
CONVERSION_EXIT_XXXX_INPUT패턴을 따릅니다. 사용자가 화면에 데이터를 입력하거나 외부 시스템에서 데이터를 수신할 때 호출되며, 외부 형식을 정규화된 내부 형식으로 다시 변환하여 데이터베이스에 저장될 수 있도록 합니다.
이 변환 메커니즘은 SAP 데이터 딕셔너리(Data Dictionary)에 통합되어 있습니다. 특정 데이터 요소(Data Element)의 도메인(Domain)에 변환 루틴을 지정하면, 해당 도메인을 사용하거나 해당 도메인을 기반으로 하는 모든 필드(화면 필드, ALV 그리드, 심지어 일부 API 인터페이스)에 이 변환 로직이 자동으로 적용됩니다. 이로써 개발자는 각 프로그램마다 반복적인 변환 로직을 작성할 필요 없이, 한 번의 정의로 시스템 전반에 걸쳐 일관된 데이터 표시를 보장하고 유지보수 편의성을 크게 향상시킬 수 있습니다.
ABAP 개발을 처음 접할 때는 이 기능이 사소하게 느껴질 수 있습니다. 하지만 실제 프로젝트에서 여러 난관을 겪으면서, 사용자 정의 변환 루틴의 올바른 활용이 복잡한 사용자 인터페이스 문제들을 해결하고 애플리케이션의 유용성을 크게 증진시킨다는 것을 깨달았습니다. 이제 실무에서 유용하게 적용할 수 있는 세 가지 대표적인 시나리오를 통해, 직접 맞춤형 변환 루틴을 구현하는 방법을 살펴보겠습니다.
2. 시나리오 1: 사용자 ID를 자동으로 이름으로 변환하기
첫 번째 시나리오는 사용자 관리가 필요한 거의 모든 시스템에서 흔히 마주치는 상황입니다. 데이터베이스에는 사용자 ID(예: KIM_CHULSU)가 저장되어 있지만, 목록이나 화면에는 해당 사용자의 한글 이름(예: '김철수')을 표시해야 하는 경우가 많습니다. 만약 모든 보고서나 화면에서 USER_ADDR 테이블을 매번 조회하여 이름을 가져온다면, 코드의 중복성이 높아지고 N+1 쿼리 문제로 인해 성능 저하가 발생할 수 있습니다.
이러한 문제를 해결하기 위해 '사용자(USER)'를 위한 맞춤형 변환 루틴을 구현하는 것이 효과적입니다. 기본적인 아이디어는 다음과 같습니다. 출력 시에는 사용자 ID를 이름으로 변환하고, 입력 시에는 사용자 ID와 이름 모두를 허용하여 최종적으로는 사용자 ID로 변환하여 저장합니다.
2.1 핵심 구현 로직 및 코드 분석
이 변환 루틴의 구현은 캐싱(Caching)과 오류 처리(Error Handling)에 중점을 둡니다. 먼저, 동일한 프로그램 실행 주기 동안 같은 사용자에 대한 데이터베이스 반복 조회를 피하기 위해 전역 내부 테이블을 캐시로 활용합니다. 이는 수백 또는 수천 건의 데이터를 ALV 그리드에 표시할 때 성능 향상에 크게 기여합니다.
* 사용자 정보 캐시를 위한 전역 내부 테이블 선언
DATA: BEGIN OF gs_user_data,
usr_id TYPE bname, " 사용자 ID
usr_name TYPE name_textc, " 사용자 이름
END OF gs_user_data.
DATA: gt_user_cache TYPE STANDARD TABLE OF gs_user_data.
출력 함수 (CONVERSION_EXIT_USER_OUTPUT) 로직:
이 함수는 내부 사용자 ID를 받아 사용자 이름으로 변환합니다. 주요 단계는 다음과 같습니다:
- 입력된 사용자 ID를 캐시 테이블 (
gt_user_cache)에서 먼저 검색합니다. - 캐시에서 해당 사용자 ID를 찾으면, 캐시된 이름을 즉시 반환합니다.
- 캐시에 없으면,
USER_ADDR테이블을 조회하여 사용자 이름을 가져옵니다. - 조회된 결과를 캐시에 저장한 후, 해당 이름을 반환합니다.
- 만약 데이터베이스에서도 사용자 ID를 찾을 수 없는 경우 (예: 유효하지 않은 ID), 입력값을 그대로 반환하여 오류 발생을 방지합니다.
FUNCTION conversion_exit_user_output.
*----------------------------------------------------------------------
* IMPORTING
* VALUE(INPUT) TYPE BNAME
* EXPORTING
* VALUE(OUTPUT) TYPE NAME_TEXTC
*----------------------------------------------------------------------
DATA: lv_user_name TYPE name_textc.
" 캐시에서 사용자 이름 조회
READ TABLE gt_user_cache INTO gs_user_data WITH KEY usr_id = input.
IF sy-subrc = 0.
lv_user_name = gs_user_data-usr_name.
ELSE.
" 캐시에 없으면 DB 조회
SELECT SINGLE name_textc INTO lv_user_name
FROM user_addr
WHERE bname = input.
IF sy-subrc = 0.
" DB에서 찾으면 캐시에 추가
gs_user_data-usr_id = input.
gs_user_data-usr_name = lv_user_name.
APPEND gs_user_data TO gt_user_cache.
ELSE.
" DB에서도 못 찾으면 입력값 그대로 반환 (오류 처리)
lv_user_name = input.
ENDIF.
ENDIF.
output = lv_user_name.
ENDFUNCTION.