ABAP

아직도 MOVE? CONV 타입 변환 실수 3가지 #shorts #SAP #ABAP

▶ YouTube에서 보기

개요 및 이 글에서 얻어갈 것

ABAP에서 타입 변환은 가장 빈번하게 일어나는 작업이지만, 동시에 가장 많은 런타임 오류(CX_SY_CONVERSION_NO_NUMBER, CX_SY_CONVERSION_OVERFLOW)를 일으키는 영역입니다. 7.40 이전에는 MOVE 또는 단순 대입(=)으로 처리했지만, 7.40 SP02부터 도입된 CONV 생성자 연산자(constructor operator)는 인라인 선언과 결합되어 코드 안정성과 가독성을 동시에 끌어올립니다. 이 글에서는 CONV의 내부 동작, MOVE 대비 장점, 그리고 실무에서 자주 마주치는 함정을 다룹니다.

  • CONV 연산자의 문법과 컴파일 타임 타입 추론 방식 이해
  • MOVE/직접 대입과 CONV의 동작 차이 비교
  • 판매오더, 자재마스터 등 실무 시나리오에 CONV 적용
  • 변환 실패 시 예외 처리(CONV ... ( ... ) + TRY-CATCH) 패턴 정착
  • 흔한 오버플로/문자→숫자 변환 함정 회피

이 글을 읽기 전 알아두면 좋은 것

ABAP 7.40 이상의 인라인 선언(DATA(...)), 기본 데이터 타입(C, N, P, I, STRING, DECFLOAT), 클래스 기반 예외 처리(TRY/CATCH/CX_ROOT) 개념을 익혀두시기 바랍니다. RAP나 CDS 작업 중 BAPI/RFC 반환값을 ABAP 내부 타입으로 옮길 때 CONV가 자주 등장하므로, ABAP Dictionary의 도메인·데이터 엘리먼트 구조도 함께 알면 도움이 됩니다.

환경·버전·준비물

CONV 연산자는 ABAP 7.40 SP02 이상에서 사용할 수 있으며, 본 글의 예제는 SAP S/4HANA 2022 FPS02(ABAP Platform 2022) 환경의 ADT(ABAP Development Tools for Eclipse 2025-03)에서 검증되었습니다. SAP BTP ABAP Environment(Steampunk)와 on-premise NetWeaver 7.50 이상에서도 동일하게 동작합니다. 실습용으로는 사용자 정의 패키지($TMP 또는 로컬 객체)와 SE38 클래식 리포트 또는 ADT의 Class/Report 객체가 필요합니다. 컴파일 옵션은 Unicode 활성화 상태가 일반적으로 권장됩니다.

참고: 7.40 미만 시스템에서는 CONV가 컴파일 오류를 내므로, 마이그레이션 전 시스템 코드 점검(ATC)에서 SLIN_ABAP_RELEASE_CHECK를 활성화해 두는 것이 안전합니다.

CONV는 어떻게 동작하는가

CONV는 "타입을 명시적으로 지정해 임시 변수를 만든 뒤 거기에 값을 변환해 담는" 생성자 연산자입니다. 문법은 CONV dtype|#( expression ) 형태로, dtype 자리에는 데이터 타입명이나 데이터 엘리먼트가 오고, #은 컨텍스트에서 타입을 자동 추론하라는 의미입니다.

비유하자면 MOVE/대입은 "값을 그냥 옮겨라"고 시스템에 맡기는 방식이고, CONV는 "이 값을 정확히 이 타입의 임시 컨테이너에 담아서 넘겨라"고 명시하는 방식입니다. 차이는 다음에서 두드러집니다.

  • 인라인 선언과의 결합: DATA(x) = some_string.은 x가 자동으로 STRING이 됩니다. 하지만 함수 시그니처가 C(10)을 요구하면 타입 충돌이 발생합니다. CONV는 DATA(x) = CONV char10( some_string ). 한 줄로 의도된 타입을 부여합니다.
  • 메서드 파라미터 전달: IMPORTING 파라미터가 P DECIMALS 2인데 호출부에 STRING을 가지고 있을 때, MOVE는 별도 변수 선언이 필요하지만 CONV는 호출식 안에서 즉시 변환 가능합니다.
  • 표현식 평가 시점: CONV는 컴파일 타임에 타입이 결정되므로, 코드를 읽는 사람이 변환 의도를 즉시 파악할 수 있습니다. MOVE는 런타임에 좌변 타입을 보고 변환하므로 의도가 묻힙니다.

내부적으로 CONV는 일반 대입(=) 규칙과 동일한 변환 표를 따릅니다. 즉 C → N 변환 시 비숫자 문자가 있으면 CX_SY_CONVERSION_NO_NUMBER, P → I 변환 시 범위를 넘으면 CX_SY_CONVERSION_OVERFLOW가 발생합니다. CONV가 "안전한 변환"을 보장하는 것이 아니라, "변환 지점을 명확히 드러내 예외 처리를 강제"하는 도구라는 점이 핵심입니다.

1단계 — 기본 형태 익히기

판매오더 번호 문자열을 숫자형 도메인에 담아야 하는 가장 단순한 케이스부터 보겠습니다.

REPORT zr_conv_basic.

DATA: lv_order_text TYPE string VALUE '0000045231',
      lv_order_num  TYPE n LENGTH 10.

" 7.40 이전 방식
MOVE lv_order_text TO lv_order_num.
WRITE: / 'MOVE 결과 :', lv_order_num.

" CONV 방식 — 동일 결과, 의도 명확
DATA(lv_converted) = CONV n( lv_order_text ).
WRITE: / 'CONV 결과 :', lv_converted.

" 메서드 호출부에서 인라인 변환
DATA(lv_amount_str) = '1234.56'.
DATA(lv_amount)     = CONV p( lv_amount_str ).  " 타입 추론 대신 명시
WRITE: / 'Amount :', lv_amount.

위 코드에서 핵심은 마지막 두 줄입니다. DATA(lv_amount) = lv_amount_str로 썼다면 lv_amount는 STRING으로 추론되었을 것입니다. CONV로 감싸는 순간 P 타입으로 고정되며, 이후 산술 연산에서 의도하지 않은 문자열 연결 같은 사고를 막을 수 있습니다.

2단계 — 실무 시나리오와 예외 처리

판매오더 헤더 BAPI를 호출한 뒤, 반환된 문자열 금액을 내부 P 타입 필드로 옮기는 상황입니다. 외부 데이터는 신뢰할 수 없으므로 반드시 예외 처리를 동반해야 합니다.

CLASS zcl_so_amount_parser DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    TYPES: BEGIN OF ty_so_input,
             order_id     TYPE string,
             net_amount   TYPE string,
             currency     TYPE string,
           END OF ty_so_input.

    TYPES: BEGIN OF ty_so_parsed,
             order_id     TYPE vbeln_va,
             net_amount   TYPE netwr,
             currency     TYPE waers,
           END OF ty_so_parsed.

    METHODS parse
      IMPORTING is_input         TYPE ty_so_input
      RETURNING VALUE(rs_parsed) TYPE ty_so_parsed
      RAISING   cx_sy_conversion_no_number
                cx_sy_conversion_overflow.
ENDCLASS.

CLASS zcl_so_amount_parser IMPLEMENTATION.
  METHOD parse.
    rs_parsed-order_id   = CONV vbeln_va( is_input-order_id ).
    rs_parsed-net_amount = CONV netwr( is_input-net_amount ).
    rs_parsed-currency   = CONV waers( is_input-currency ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  DATA(lo_parser) = NEW zcl_so_amount_parser( ).
  TRY.
      DATA(ls_result) = lo_parser->parse(
        VALUE #( order_id   = '0000045231'
                 net_amount = '15000.75'
                 currency   = 'EUR' ) ).
      WRITE: / ls_result-order_id, ls_result-net_amount, ls_result-currency.

    CATCH cx_sy_conversion_no_number INTO DATA(lx_no_num).
      MESSAGE |숫자 변환 실패: { lx_no_num->get_text( ) }| TYPE 'E'.
    CATCH cx_sy_conversion_overflow INTO DATA(lx_ovf).
      MESSAGE |오버플로: { lx_ovf->get_text( ) }| TYPE 'E'.
  ENDTRY.

MOVE 기반 코드와 비교하면, 어느 라인에서 어떤 타입으로 변환이 일어나는지가 한눈에 드러납니다. 트러블슈팅 시 ST22 덤프의 호출 스택과 코드 라인을 1:1로 맞추기가 쉬워집니다.

3단계 — 프로덕션 품질: 로깅, 단위 테스트, 안전 변환

실제 운영 코드에서는 변환 실패를 단순 덤프가 아닌 애플리케이션 로그로 남기고, ABAP Unit으로 회귀 테스트까지 작성합니다.

CLASS zcl_safe_converter DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    CLASS-METHODS to_amount
      IMPORTING iv_text          TYPE string
                iv_default       TYPE netwr DEFAULT 0
      RETURNING VALUE(rv_amount) TYPE netwr.
ENDCLASS.

CLASS zcl_safe_converter IMPLEMENTATION.
  METHOD to_amount.
    TRY.
        rv_amount = CONV netwr( iv_text ).
      CATCH cx_sy_conversion_no_number
            cx_sy_conversion_overflow INTO DATA(lx_conv).
        cl_bali_log_db=>get_instance(
          )->save_log( EXPORTING io_log = NEW cl_bali_log(
                                    iv_subobject = 'CONV_FAIL' ) ).
        rv_amount = iv_default.
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

CLASS ltcl_converter DEFINITION FOR TESTING
  DURATION SHORT RISK LEVEL HARMLESS.
  PRIVATE SECTION.
    METHODS:
      valid_decimal   FOR TESTING,
      invalid_text    FOR TESTING,
      overflow_value  FOR TESTING.
ENDCLASS.

CLASS ltcl_converter IMPLEMENTATION.
  METHOD valid_decimal.
    cl_abap_unit_assert=>assert_equals(
      act = zcl_safe_converter=>to_amount( '999.99' )
      exp = CONV netwr( '999.99' ) ).
  ENDMETHOD.

  METHOD invalid_text.
    cl_abap_unit_assert=>assert_equals(
      act = zcl_safe_converter=>to_amount( 'ABC' )
      exp = CONV netwr( 0 ) ).
  ENDMETHOD.

  METHOD overflow_value.
    cl_abap_unit_assert=>assert_equals(
      act = zcl_safe_converter=>to_amount(
              '99999999999999999999' iv_default = 0 )
      exp = CONV netwr( 0 ) ).
  ENDMETHOD.
ENDCLASS.

이 패턴의 장점은 (1) 호출부가 예외 처리 없이도 안전한 기본값을 받을 수 있고, (2) 변환 실패 통계를 BAL로 집계해 데이터 품질 모니터링이 가능하며, (3) 단위 테스트로 회귀를 막을 수 있다는 점입니다. 성능 측면에서 CONV는 컴파일 시점 타입 결정이라 런타임 오버헤드가 사실상 없습니다.

실무에서 자주 밟는 지뢰와 FAQ

Q1. DATA(x) = CONV i( '12.7' ).의 결과는? — 12가 아닌 13입니다. P → I 변환은 반올림(commercial rounding) 규칙을 따릅니다. 절사하고 싶다면 FLOOR( ) 같은 내장 함수를 함께 사용해야 합니다.

Q2. CONV로 감쌌는데도 변환 오류가 안 잡힙니다. — CONV는 변환 규칙 자체를 바꾸지 않습니다. C → STRING처럼 손실 없는 변환은 절대 예외를 던지지 않습니다. 반대로 STRING → I, STRING → P처럼 데이터 손실 가능성이 있는 경로에서만 예외가 발생합니다.

Q3. CONV #( ... )를 어디서나 써도 되나요? — 컨텍스트에서 타입 추론이 가능한 자리(메서드 파라미터, RETURNING, ASSIGN 대상)에서만 작동합니다. 인라인 DATA(...) 우변에 CONV #( ... )를 쓰면 추론 컨텍스트가 없어 컴파일 오류가 납니다.

그 외 자주 발생하는 실수: ① 통화 필드(WAERS)에 소문자를 넣는 경우 — CONV는 변환만 할 뿐 도메인 변환 루틴(CONVERSION_EXIT)은 자동 호출되지 않으므로 별도 함수 모듈을 거쳐야 합니다. ② 날짜 변환 — STRING '20260619'을 DATS로 CONV하면 잘 되지만, '2026-06-19'는 실패합니다. 외부 포맷은 cl_abap_datfm 또는 cl_abap_tstmp를 먼저 거치는 것이 권장됩니다. ③ AMDP나 CDS에서 받은 결과를 CONV로 좁히는 경우, HANA 측 타입(NVARCHAR vs ABAP STRING) 차이로 길이 잘림이 발생할 수 있습니다.

여기서 더 나아가려면

CONV를 익혔다면 자연스럽게 다른 생성자 연산자도 함께 다루게 됩니다. 값 생성에는 VALUE, 참조 변환에는 CAST(다운캐스팅, CX_SY_MOVE_CAST_ERROR), 객체 생성에는 NEW, 조건 평가에는 COND·SWITCH가 있습니다. 특히 CAST는 CONV와 이름이 비슷하지만 참조 타입 변환이라 동작이 완전히 다르므로 분리해서 학습하시기 바랍니다.

댓글 0

아직 댓글이 없습니다.