본문 바로가기
ABAP 문법 & 기법

[SAP ABAP] SAP에서 메일 보내는 방법 — CL_BCS · SO_NEW_DOCUMENT_SEND_API1 (HTML 예제)

by Song.sh 2026. 5. 12.

SAP에서 알림 메일을 보내는 일은 의외로 자주 등장합니다. 인터페이스 오류 발생 시 담당자 알림, 결재 요청 통보, 일일 배치 결과 리포트 등 — ABAP 코드에서 직접 SMTP를 다루지 않고 SAP 내부 API로 메일을 발송하는 패턴이 표준입니다.

문제는 SAP에서 메일을 보내는 방법이 하나가 아니라 여러 개 라는 점입니다.

옛날부터 쓰던 SO_NEW_DOCUMENT_SEND_API1 펑션 모듈 방식,

클래스 기반의 cl_bcs 방식,

HTML 본문까지 처리 가능한 cl_document_bcs 패턴까지.

어느 방식을 쓰느냐에 따라 코드 길이도 다르고 HTML 지원 여부도 다릅니다.

 

이 글은 SAP 메일 발송 세 가지 패턴의 코드·차이·발송 결과 확인 방법 을 정리한 메모입니다.


핵심 원리

SAP에서 메일 발송은 결국 SAP 자체 메일 큐(SO 시스템) 에 메시지를 적재하는 작업입니다. 적재된 메시지는 SCOT(메일 라우팅 설정)에 따라 외부 SMTP 서버로 발송됩니다.

방식 HTML 지원 언제 쓰는가
FunctionSO_NEW_DOCUMENT_SEND_API1 기본은 RAW 텍스트만 레거시 코드 · 단순 텍스트 본문 · 빠른 작성
Classcl_bcs + cl_document_bcs HTML / 첨부 / Express 가능 신규 개발 · HTML 본문 · 첨부 파일 · 예외 처리 필요 시
Class (HTML 인라인) 완전 HTML 표·서식이 있는 리포트 메일 · 일일 배치 결과

신규 개발이면 거의 Class 방식(cl_bcs)을 선택합니다. Function 방식은 오래된 프로그램 유지 보수 시에만 마주치는 패턴입니다.


1단계 — Function 방식 (SO_NEW_DOCUMENT_SEND_API1)

가장 오래된 표준 방식입니다. 본문은 SOLISTI1 라인 단위 테이블에 채우고, 수신자는 SOMLRECI1 테이블에 추가합니다.

DATA: lt_content   TYPE TABLE OF solisti1  WITH HEADER LINE,
      lt_receivers TYPE TABLE OF somlreci1 WITH HEADER LINE,
      ls_doc_data  TYPE sodocchgi1,
      l_descr      TYPE so_obj_des.

" 1) 메일 헤더(메타데이터)
ls_doc_data-obj_name   = 'E-MAIL'.
ls_doc_data-obj_descr  = l_descr.       " 제목
ls_doc_data-obj_langu  = sy-langu.
ls_doc_data-sensitivty = 'F'.
ls_doc_data-obj_prio   = '1'.
ls_doc_data-no_change  = 'X'.
ls_doc_data-priority   = '1'.

" 2) 본문 — 라인 단위로 APPEND
CONCATENATE '  ' ' ' INTO lt_content-line.
APPEND lt_content.

CONCATENATE '아래의 오류 내역을 확인하시기 바랍니다.' '' INTO lt_content-line.
APPEND lt_content.

CONCATENATE ' 의뢰서 관리 번호 : ' lv_field1 INTO lt_content-line RESPECTING BLANKS.
APPEND lt_content.

CONCATENATE ' 구매자 상호 : '  lv_field2 INTO lt_content-line RESPECTING BLANKS.
APPEND lt_content.

CONCATENATE ' 추심(매입) 금액 : ' lv_field3 INTO lt_content-line RESPECTING BLANKS.
APPEND lt_content.

" 3) 수신자
lt_receivers-receiver = 'recipient@example.com'.
lt_receivers-rec_type = 'U'.             " U = Internet address
lt_receivers-express  = 'X'.             " 즉시 발송
lt_receivers-com_type = 'INT'.           " 인터넷 메일
APPEND lt_receivers.

" 4) 제목
l_descr = '[수출] LC NEGO 요청 오류 발생 알림'.

" 5) 발송
CALL FUNCTION 'SO_NEW_DOCUMENT_SEND_API1'
  EXPORTING
    document_data              = ls_doc_data
    document_type              = 'RAW'    " RAW / HTM
    put_in_outbox              = 'X'
    commit_work                = 'X'
  TABLES
    object_content             = lt_content
    receivers                  = lt_receivers
  EXCEPTIONS
    too_many_receivers         = 1
    document_not_sent          = 2
    document_type_not_exist    = 3
    operation_no_authorization = 4
    parameter_error            = 5
    x_error                    = 6
    enqueue_error              = 7
    OTHERS                     = 8.

핵심 포인트:

  • 본문은 한 줄씩 CONCATENATE ... INTO lt_content-lineAPPEND 패턴
  • RESPECTING BLANKS — 공백을 유지하지 않으면 항목 사이가 한 칸으로 줄어듭니다
  • commit_work = 'X' — 펑션이 내부적으로 COMMIT까지 처리. 외부에서 추가 COMMIT 불필요
  • rec_type = 'U', com_type = 'INT' — 외부 이메일 주소 발송 시 표준 조합

2단계 — Class 방식 기본 (cl_bcs + cl_document_bcs)

객체지향 방식. 가독성 좋고 예외 처리가 자연스럽습니다.

DATA: send_request  TYPE REF TO cl_bcs,
      document      TYPE REF TO cl_document_bcs,
      sender        TYPE REF TO cl_sapuser_bcs,
      recipient     TYPE REF TO if_recipient_bcs,
      bcs_exception TYPE REF TO cx_bcs,
      t_text        TYPE soli_tab,
      w_text        LIKE LINE OF t_text,
      l_subject     TYPE so_obj_des.

TRY.
    " 1) 발송 요청 객체 생성
    send_request = cl_bcs=>create_persistent( ).

    " 2) 본문 + 제목
    l_subject = '테스트'.
    w_text    = 'test'.
    APPEND w_text TO t_text.

    document = cl_document_bcs=>create_document(
                 i_type    = 'HTM'
                 i_text    = t_text
                 i_subject = l_subject ).

    send_request->set_document( document ).

    " 3) 발신자 — 로그인 사용자
    sender = cl_sapuser_bcs=>create( sy-uname ).
    send_request->set_sender( i_sender = sender ).

    " 4) 수신자
    recipient = cl_cam_address_bcs=>create_internet_address( 'recipient@example.com' ).
    send_request->add_recipient( i_recipient = recipient ).

    " 5) 즉시 발송 옵션
    send_request->set_send_immediately( 'X' ).

    " 6) 발송
    send_request->send( ).
    COMMIT WORK.

  CATCH cx_bcs INTO bcs_exception.
    DATA(lv_msg) = bcs_exception->get_text( ).
    " 에러 처리
ENDTRY.

핵심 포인트:

  • cl_bcs=>create_persistent( ) — 영구 저장되는 발송 요청 생성
  • cl_sapuser_bcs=>create( sy-uname ) — 현재 로그인 SAP 사용자를 발신자로
  • cl_cam_address_bcs=>create_internet_address — 외부 이메일 주소 객체 생성
  • cx_bcs 예외 처리 — Function 방식의 SY-SUBRC보다 명확한 에러 추적

3단계 — Class 방식 HTML 발송

리포트성 메일은 HTML로 표·서식을 넣어 보내면 가독성이 크게 올라갑니다.

DATA: lv_recipient TYPE adr6-smtp_addr,
      lv_subject   TYPE string.

lv_recipient = 'recipient@example.com'.
lv_subject   = 'Testmail'.

" 1) HTML 본문 — 문자열로 작성 후 SOLI 테이블로 변환
DATA(lv_html_text) = VALUE string( ).
lv_html_text = |<BODY>|
            && |<P><B>TEST</B></P>|
            && |</BODY>|.

DATA(it_body_text) = cl_document_bcs=>string_to_soli( ip_string = lv_html_text ).

TRY.
    " 2) HTML 타입 도큐먼트 생성
    DATA(o_document) = cl_document_bcs=>create_document(
                         i_type    = 'HTM'
                         i_text    = it_body_text
                         i_subject = CONV so_obj_des( lv_subject ) ).

    " 3) 발송 요청
    DATA(o_send_request) = cl_bcs=>create_persistent( ).
    o_send_request->set_message_subject( ip_subject = lv_subject ).
    o_send_request->set_document( o_document ).

    " 4) 발신자 — 인터넷 주소 직접 지정
    DATA(o_sender) = cl_cam_address_bcs=>create_internet_address( i_address_string = 'sender@example.com' ).
    o_send_request->set_sender( o_sender ).

    " 5) 수신자 + Express
    DATA(o_recipient) = cl_cam_address_bcs=>create_internet_address( lv_recipient ).
    o_send_request->add_recipient( i_recipient = o_recipient
                                   i_express   = abap_true ).

    o_send_request->set_send_immediately( abap_true ).
    o_send_request->send( i_with_error_screen = abap_true ).

    " 6) 업데이트 태스크 안에 있으면 COMMIT 생략
    DATA: lv_in_update_task TYPE sy-subrc.
    CALL FUNCTION 'TH_IN_UPDATE_TASK'
      IMPORTING in_update_task = lv_in_update_task.

    IF lv_in_update_task = 0.
      COMMIT WORK.
    ENDIF.

  CATCH cx_root INTO DATA(e_text).
    WRITE: / e_text->get_text( ).
ENDTRY.

핵심 포인트:

  • i_type = 'HTM' — HTML 타입 도큐먼트 지정. RAW로 만들면 <BODY> 태그가 텍스트로 보임
  • cl_document_bcs=>string_to_soli — string을 SOLI 테이블로 변환
  • 인라인 선언(DATA(...)) 활용 가능 — ABAP 7.40 이상
  • TH_IN_UPDATE_TASK 체크 — 업데이트 태스크 내부에서는 COMMIT WORK 중복 호출 방지

4단계 — 발송 결과 확인 (SBWP / SOSG)

메일 발송 코드를 호출했다고 해서 즉시 외부로 나가는 건 아닙니다. SAP 메일 큐에 적재된 후 SCOT 라우팅에 따라 전송됩니다. 발송 상태는 다음 T-Code로 확인합니다.

T-Code 정식 명칭 용도
SBWP SAP Business Workplace 로그인 사용자 기준 수신함 / 발신함 / 휴지통 확인. 본인이 보낸 메일 추적
SOSG Send Requests Monitor 전체 발송 요청 모니터. 상태(WAITING / SENT / ERROR) · 재발송 가능
SCOT SAPconnect 메일 라우팅 설정. SMTP 노드·도메인별 발송 규칙(BC 담당자 영역)

코드는 정상인데 메일이 도착하지 않으면, SOSG 에서 발송 요청 상태가 ERROR인지부터 확인합니다. 대부분 SCOT 라우팅 누락이거나 외부 SMTP 거부가 원인입니다.


흔히 빠뜨리는 함정

COMMIT WORK 누락 또는 중복

Function 방식에서 commit_work = 'X' 옵션을 안 쓰고 외부에서 COMMIT을 빠뜨리면 메일이 큐에 들어가도 발송되지 않습니다. 반대로 업데이트 태스크 안에서 COMMIT을 또 부르면 시스템 에러가 발생하므로 TH_IN_UPDATE_TASK 체크가 필요합니다.

HTML 타입인데 BODY 태그 누락

i_type = 'HTM' 으로 보냈는데 본문에 <BODY> 태그가 없으면 메일 클라이언트에 따라 HTML로 렌더되지 않고 plain text로 표시됩니다. 최소 <BODY>...</BODY> 구조는 갖춰야 합니다.

한글 깨짐

본문에 한글이 들어가는데 SCOT 설정의 캐릭터셋이 한글을 지원하지 않으면 수신 측에서 깨집니다. SCOT → Settings → Default Domain 의 캐릭터셋이 UTF-8 또는 적절한 인코딩으로 설정되어 있어야 합니다(BC 담당자 영역).

외부 이메일 검증 누락

create_internet_address 에 잘못된 이메일 형식을 넘기면 발송 시점에 예외가 발생합니다. 사용자 입력을 수신자로 받는 경우는 코드 레벨에서 형식 검증(@ 포함 · 길이 등) 필수.

Express 옵션 오해

express = 'X' 는 "수신자가 SAP 로그인 시 즉시 알림 팝업" 옵션입니다. 외부 이메일에는 의미 없음. 외부 발송을 빨리 하고 싶으면 set_send_immediately( 'X' ) 사용.


요약

단계 방법 핵심 객체 / 파라미터
1 Function 방식 SO_NEW_DOCUMENT_SEND_API1 + SOLISTI1 + SOMLRECI1
2 Class 방식 기본 cl_bcs + cl_document_bcs + cl_cam_address_bcs
3 Class 방식 HTML i_type = 'HTM' + string_to_soli + TH_IN_UPDATE_TASK
4 결과 확인 SBWP(본인 수발신함) / SOSG(전체 큐) / SCOT(라우팅)

신규 개발이면 Class 방식(cl_bcs)을 기본으로 잡고, 단순 텍스트 알림은 기본 형태로, 리포트성 메일은 HTML 인라인으로 구성하면 거의 모든 케이스를 커버할 수 있습니다. 메일이 보내졌는데 안 오면 SOSG 부터 열어보는 습관을 들이면 트러블슈팅 시간이 크게 줄어듭니다.


Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다. SAP 버전·SCOT 라우팅 설정·BC 영역 설정에 따라 동작이 달라질 수 있으므로 운영 환경 적용 전 개발 시스템에서 발송 흐름을 확인하시기 바랍니다.