ABAP 에서 메일을 보낼 때 가장 오래 쓰여온 표준 함수가 SO_DOCUMENT_SEND_API1 과 SO_NEW_DOCUMENT_SEND_API1 입니다. 이름이 비슷해서 "둘 중 뭘 써야 하나" 헷갈리기 쉬운데, 실제로는 역할과 기능이 분명히 다릅니다.
결론부터 말하면 SO_NEW_DOCUMENT_SEND_API1 은 내부에서 SO_DOCUMENT_SEND_API1 을 그대로 호출하는 간편 래퍼(wrapper) 입니다. 대신 두 가지를 포기합니다 — (1) 발신인 지정 기능과 (2) PACKING_LIST 세밀 제어. 그 대가로 코드가 짧아지죠.
이 글에서는 두 함수의 관계(NEW = 래퍼) → 발신인 지정 차이 → PACKING_LIST 수동 vs 자동 → 전체 시그니처 비교 → 언제 무엇을 쓸지까지 정리합니다. 예시 메일 주소는 가상값(example.com) 으로 표기.
핵심 — NEW 는 OLD 의 간편 래퍼
SO_NEW_DOCUMENT_SEND_API1 의 함수 본문을 열어보면, 결국 마지막에 SO_DOCUMENT_SEND_API1 을 호출합니다. 즉 NEW 는 "PACKING_LIST 를 자동으로 만들어준 뒤 OLD 를 대신 불러주는" 편의 함수입니다.
| 구분 | SO_DOCUMENT_SEND_API1 | SO_NEW_DOCUMENT_SEND_API1 |
|---|---|---|
| 관계 | 실제 전송 로직 (원본) | OLD 를 호출하는 간편 래퍼 |
| 발신인 지정 | 가능 (SENDER_ADDRESS) |
불가 (항상 로그인 사용자) |
| PACKING_LIST | 직접 작성 (수동) | 자동 생성 (DOCUMENT_TYPE 기반) |
| 본문 테이블 | CONTENTS_TXT + CONTENTS_BIN |
OBJECT_CONTENT |
| 코드 길이 | 길다 (PACKING_LIST 채워야 함) | 짧다 |
| 적합한 경우 | 발신인 변경·첨부 세밀 제어 필요 | 단순 텍스트 메일 빠르게 발송 |
두 함수 모두 SAPoffice(SO) 패키지의 함수 그룹 SOI1 에 속합니다. 더 새로운 방식인 객체지향 CL_BCS 클래스 기반 송신도 있지만, 이 글은 두 클래식 FM 의 차이에 집중합니다.
차이 1 — 발신인(Sender) 지정 가능 여부
가장 실무적으로 중요한 차이입니다. SO_DOCUMENT_SEND_API1 에는 발신인을 지정하는 IMPORTING 파라미터가 있습니다.
* SO_DOCUMENT_SEND_API1 의 발신인 관련 파라미터
IMPORTING
VALUE(SENDER_ADDRESS) LIKE SOEXTRECI1-RECEIVER DEFAULT SY-UNAME
VALUE(SENDER_ADDRESS_TYPE) LIKE SOEXTRECI1-ADR_TYP DEFAULT 'B'
EXPORTING
VALUE(SENDER_ID) LIKE SOUDK
SENDER_ADDRESS 에 보내는 사람을 직접 넣을 수 있고, SENDER_ADDRESS_TYPE 으로 그 주소가 어떤 형식인지(SAP 사용자 / 외부 이메일 등) 지정합니다. 기본값은 SY-UNAME(현재 로그인 사용자) 와 타입 'B'.
반면 SO_NEW_DOCUMENT_SEND_API1 의 시그니처에는 SENDER_ADDRESS 자체가 없습니다. 내부에서 OLD 를 호출할 때 발신인 인자를 넘기지 않기 때문에, 발신인은 항상 메일을 발송하는 현재 사용자(SY-UNAME) 로 고정됩니다.
| 파라미터 | OLD | NEW | 의미 |
|---|---|---|---|
SENDER_ADDRESS |
O | X | 발신인 주소 |
SENDER_ADDRESS_TYPE |
O | X | 발신인 주소 타입 |
SENDER_ID |
O | X | 생성된 발신인 ID 반환 |
배치 잡이나 시스템 계정으로 메일을 보내되 발신인 표시는 특정 담당자/대표 주소로 보이게 하고 싶다면 반드시 SO_DOCUMENT_SEND_API1 을 써야 합니다.
차이 2 — PACKING_LIST 수동 작성 vs 자동 생성
두 번째 차이는 메일 본문/첨부를 구성하는 PACKING_LIST 의 처리 방식입니다.
SO_DOCUMENT_SEND_API1 은 PACKING_LIST (타입 SOPCKLSTI1) 를 호출자가 직접 채워야 합니다. 본문이 몇 번째 줄부터 시작하는지(body_start), 몇 줄인지(body_num), 문서 타입은 무엇인지(doc_type) 등을 일일이 세팅합니다. 본문 + 첨부파일 여러 개를 정교하게 묶을 수 있는 대신 코드가 길어집니다.
SO_NEW_DOCUMENT_SEND_API1 은 이 PACKING_LIST 를 내부에서 자동으로 한 줄 생성합니다. 함수 본문을 보면 이렇게 동작합니다.
* SO_NEW_DOCUMENT_SEND_API1 내부 — PACKING_LIST 자동 생성
DATA pack_wa TYPE sopcklsti1.
DATA packing_list TYPE TABLE OF sopcklsti1.
pack_wa-doc_type = document_type. " IMPORTING DOCUMENT_TYPE (DEFAULT 'RAW')
pack_wa-head_start = 1.
pack_wa-head_num = lines( object_header ).
pack_wa-body_start = 1.
IF contents_hex[] IS NOT INITIAL.
pack_wa-body_num = lines( contents_hex ).
pack_wa-transf_bin = 'X'.
ELSE.
pack_wa-body_num = lines( object_content ).
ENDIF.
APPEND pack_wa TO packing_list.
* 그 다음 SO_DOCUMENT_SEND_API1 을 대신 호출
CALL FUNCTION 'SO_DOCUMENT_SEND_API1'
EXPORTING document_data = document_data
...
TABLES packing_list = packing_list
contents_txt = object_content
...
호출자는 본문을 OBJECT_CONTENT 테이블에 채우고 DOCUMENT_TYPE 만 지정하면, PACKING_LIST 작성은 함수가 알아서 해줍니다. 단순 텍스트/HTML 메일 한 통을 빠르게 보낼 때 훨씬 간편합니다.
차이 3 — 전체 시그니처 비교
ADT 로 확인한 두 함수의 IMPORTING / TABLES 파라미터를 나란히 비교하면 차이가 한눈에 들어옵니다.
| 구역 | SO_DOCUMENT_SEND_API1 | SO_NEW_DOCUMENT_SEND_API1 |
|---|---|---|
| IMPORTING | DOCUMENT_DATA · PUT_IN_OUTBOX · SENDER_ADDRESS · SENDER_ADDRESS_TYPE · COMMIT_WORK · IP_ENCRYPT · IP_SIGN · IV_VSI_PROFILE | DOCUMENT_DATA · DOCUMENT_TYPE · PUT_IN_OUTBOX · COMMIT_WORK · IP_ENCRYPT · IP_SIGN |
| EXPORTING | SENT_TO_ALL · NEW_OBJECT_ID · SENDER_ID | SENT_TO_ALL · NEW_OBJECT_ID |
| TABLES | PACKING_LIST · OBJECT_HEADER · CONTENTS_BIN · CONTENTS_TXT · CONTENTS_HEX · OBJECT_PARA · OBJECT_PARB · RECEIVERS · ET_VSI_ERROR | OBJECT_HEADER · OBJECT_CONTENT · CONTENTS_HEX · OBJECT_PARA · OBJECT_PARB · RECEIVERS |
핵심만 추리면 — OLD 에만 있는 것: SENDER_ADDRESS · SENDER_ID · PACKING_LIST.
NEW 에만 있는 것: DOCUMENT_TYPE · OBJECT_CONTENT. NEW 의 OBJECT_CONTENT 는 내부에서 OLD 의 CONTENTS_TXT 로 연결됩니다.
언제 무엇을 쓸까
| 상황 | 권장 함수 |
|---|---|
| 단순 텍스트/HTML 메일 한 통 빠르게 | SO_NEW_DOCUMENT_SEND_API1 |
| 발신인을 특정 주소로 표시해야 함 | SO_DOCUMENT_SEND_API1 |
| 본문 + 첨부 여러 개를 정교하게 묶음 | SO_DOCUMENT_SEND_API1 |
| 신규 개발 — 더 현대적인 방식 원함 | CL_BCS 클래스 (별도 주제) |
요약하면 간편함이 우선이면 NEW, 발신인·첨부 제어가 필요하면 OLD. 그리고 신규 개발이라면 두 클래식 FM 대신 객체지향 CL_BCS 기반 송신이 SAP 의 권장 방향입니다.
자주 빠뜨리는 함정
COMMIT_WORK 누락 — 메일이 안 나감
두 함수 모두 COMMIT_WORK 기본값이 SPACE. 호출 후 직접 COMMIT WORK 을 하거나 파라미터를 'X' 로 넘기지 않으면 송신 큐에 들어가지 않습니다. 전송 후 SOST 에서 메일이 안 보이면 이걸 먼저 의심.
NEW 로 발신인 바꾸려다 막힘
SO_NEW_DOCUMENT_SEND_API1 에는 발신인 파라미터가 아예 없습니다. 발신인을 바꾸려고 NEW 의 파라미터를 뒤지지 말고, 처음부터 OLD 를 쓰거나 CL_BCS 의 set_sender 를 쓰세요.
DOCUMENT_TYPE 과 본문 형식 불일치
NEW 에서 HTML 메일을 보내려면 DOCUMENT_TYPE = 'HTM' 으로 지정해야 합니다. 기본값 'RAW' 그대로 두고 HTML 태그를 본문에 넣으면 태그가 그대로 텍스트로 보입니다.
제목 길이 제한
DOCUMENT_DATA-OBJ_DESCR(제목) 은 50자(byte) 제한. 더 긴 제목이 필요하면 본문 첫 줄이나 별도 처리로 보완해야 합니다.
수신자 RECEIVERS 의 REC_TYPE
RECEIVERS 테이블의 REC_TYPE 을 외부 이메일은 'U'(인터넷 주소) 로 지정해야 합니다. 비워두면 SAP 내부 사용자로 해석되어 외부 메일이 안 나갑니다.
전체 코드 — 복사용 통합본
두 함수의 호출 방식을 한 프로그램에 담은 예시입니다. 둘 다 SAP 표준 함수이므로 시그니처 그대로 호출하면 됩니다. 발신인 지정이 필요한 경우(OLD) 와 간편 발송(NEW) 두 가지를 나란히 비교.
*&---------------------------------------------------------------------*
*& Report ZRXX_MAIL_SEND_COMPARE (예시)
*&---------------------------------------------------------------------*
*& SO_DOCUMENT_SEND_API1 (발신인 지정) vs SO_NEW_DOCUMENT_SEND_API1 (간편)
*&---------------------------------------------------------------------*
REPORT zrxx_mail_send_compare.
DATA: gs_docdata TYPE sodocchgi1,
gt_receiver TYPE TABLE OF somlreci1,
gs_receiver TYPE somlreci1,
gt_content TYPE TABLE OF solisti1,
gs_content TYPE solisti1.
* 공통 — 제목 + 수신자 + 본문
START-OF-SELECTION.
gs_docdata-obj_descr = '메일 제목 예시'. " 50자 제한
gs_receiver-receiver = 'someone@example.com'. " 수신자 (가상)
gs_receiver-rec_type = 'U'. " U = 인터넷 이메일
APPEND gs_receiver TO gt_receiver.
gs_content-line = '안녕하세요. 메일 본문입니다.'.
APPEND gs_content TO gt_content.
* ───────────────────────────────────────────────
* 방법 A) SO_NEW_DOCUMENT_SEND_API1 — 간편 (발신인 = 현재 사용자)
* ───────────────────────────────────────────────
CALL FUNCTION 'SO_NEW_DOCUMENT_SEND_API1'
EXPORTING
document_data = gs_docdata
document_type = 'RAW' " HTML 이면 'HTM'
commit_work = 'X' " ★ 필수
TABLES
object_content = gt_content " 본문 (PACKING_LIST 자동)
receivers = gt_receiver
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.
IF sy-subrc <> 0.
MESSAGE 'NEW 송신 실패' TYPE 'I'.
ENDIF.
* ───────────────────────────────────────────────
* 방법 B) SO_DOCUMENT_SEND_API1 — 발신인 지정 + PACKING_LIST 수동
* ───────────────────────────────────────────────
DATA: gt_packing TYPE TABLE OF sopcklsti1,
gs_packing TYPE sopcklsti1.
gs_packing-transf_bin = space.
gs_packing-head_start = 1.
gs_packing-head_num = 0.
gs_packing-body_start = 1.
gs_packing-body_num = lines( gt_content ).
gs_packing-doc_type = 'RAW'.
APPEND gs_packing TO gt_packing.
CALL FUNCTION 'SO_DOCUMENT_SEND_API1'
EXPORTING
document_data = gs_docdata
put_in_outbox = 'X'
sender_address = 'noreply@example.com' " ★ 발신인 지정
sender_address_type = 'INT' " INT = 인터넷 주소
commit_work = 'X' " ★ 필수
TABLES
packing_list = gt_packing " ★ 수동 작성
contents_txt = gt_content
receivers = gt_receiver
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.
IF sy-subrc <> 0.
MESSAGE 'OLD 송신 실패' TYPE 'I'.
ENDIF.
요약
| 관점 | 정리 |
|---|---|
| 관계 | NEW 는 내부에서 OLD 를 호출하는 간편 래퍼 |
| 발신인 | OLD = SENDER_ADDRESS 지정 가능 · NEW = 현재 사용자 고정 |
| PACKING_LIST | OLD = 수동 작성 · NEW = DOCUMENT_TYPE 기반 자동 생성 |
| 본문 테이블 | OLD = CONTENTS_TXT · NEW = OBJECT_CONTENT |
| 선택 기준 | 간편 = NEW · 발신인/첨부 제어 = OLD · 신규 개발 = CL_BCS |
두 함수의 차이는 결국 "얼마나 세밀하게 제어할 것인가" 한 가지로 수렴합니다. SO_NEW_DOCUMENT_SEND_API1 은 PACKING_LIST 를 자동 생성해 코드를 줄여주는 대신 발신인 지정을 포기했고, SO_DOCUMENT_SEND_API1 은 손이 더 가는 대신 발신인·첨부를 자유롭게 다룹니다. 발신인을 바꿔야 하는 요구가 나오면 NEW 로는 답이 없으니 처음부터 OLD(또는 CL_BCS) 로 설계하는 게 안전합니다.
Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다.
SO_DOCUMENT_SEND_API1 · SO_NEW_DOCUMENT_SEND_API1 의 시그니처(파라미터·테이블·예외) 는 NetWeaver 표준 함수 그룹 SOI1 기준(ECC 6.0 / S/4HANA on-premise) 입니다. 적용 환경 버전에 따라 일부 파라미터가 다를 수 있으니 실제 적용 시 SE37 의 함수 정의를 확인하시기 바랍니다. 예시 메일 주소는 모두 가상값입니다.