본문 바로가기

SAP

[참고] 그룹웨어 메일 발송 Function Module (검증전)

반응형

검증 전

Function Module: ZFI_GROUPWARE_SEND_MAIL

FUNCTION ZFI_GROUPWARE_SEND_MAIL.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     VALUE(I_MAIL_API_URL) TYPE STRING
*"     VALUE(I_EMP_ID) TYPE STRING           " 발신자 사번
*"     VALUE(I_SUBJECT) TYPE STRING          " 메일 제목
*"     VALUE(I_BODYTEXT) TYPE STRING         " 메일 본문
*"     VALUE(I_CERT_KEY) TYPE STRING DEFAULT 'ekdphandy'
*"  TABLES
*"      T_RECIPIENTS STRUCTURE ZSTFI_MAIL_RECIPIENT  " 수신자 목록
*"      T_ATTACHMENTS STRUCTURE ZSTFI_MAIL_ATTACH OPTIONAL  " 첨부파일
*"  EXPORTING
*"     VALUE(E_SUCCESS) TYPE ABAP_BOOL
*"     VALUE(E_MESSAGE) TYPE STRING
*"     VALUE(E_RESPONSE) TYPE STRING
*"  EXCEPTIONS
*"      HTTP_COMMUNICATION_FAILURE
*"      HTTP_INVALID_STATE
*"----------------------------------------------------------------------
*& PROGRAM ID  : ZFI_GROUPWARE_SEND_MAIL                              &*
*& Title       : [FI] 그룹웨어 메일 발송 API 연동                     &*
*& Created By  : [Your Name]                                          &*
*& Created On  : 2025.10.31                                           &*
*& Description : 그룹웨어 API를 통한 메일 발송                        &*
*& Reference   : GroupMailSender.java                                 &*
*----------------------------------------------------------------------*

  DATA: lo_http_client TYPE REF TO if_http_client,
        lv_url         TYPE string,
        lv_json        TYPE string,
        lv_response    TYPE string,
        lv_code        TYPE i,
        lv_reason      TYPE string,
        lv_bodytext_b64 TYPE string,
        lv_length      TYPE i.

  DATA: BEGIN OF ls_recipient,
          emp_id TYPE string,
          email  TYPE string,
          name   TYPE string,
        END OF ls_recipient.

  DATA: lt_recipients_json TYPE TABLE OF string,
        lv_recipients_str  TYPE string.

  " ✅ 1. HTTP Client 생성
  TRY.
      cl_http_client=>create_by_url(
        EXPORTING
          url                = i_mail_api_url
          ssl_id             = 'ANONYM'  " 또는 'DFAULT'
        IMPORTING
          client             = lo_http_client
        EXCEPTIONS
          argument_not_found = 1
          plugin_not_active  = 2
          internal_error     = 3
          OTHERS             = 4 ).

      IF sy-subrc <> 0.
        e_success = abap_false.
        e_message = '메일 API URL 연결 실패'.
        RAISE http_communication_failure.
      ENDIF.

    CATCH cx_root INTO DATA(lx_error).
      e_success = abap_false.
      e_message = lx_error->get_text( ).
      RAISE http_communication_failure.
  ENDTRY.

  " ✅ 2. HTTP 메소드 및 헤더 설정
  lo_http_client->request->set_method( 'POST' ).
  lo_http_client->request->set_content_type( 'application/x-www-form-urlencoded' ).
  lo_http_client->request->set_header_field(
    name  = 'charset'
    value = 'UTF-8' ).

  " ✅ 3. Body Text Base64 인코딩 (한글 깨짐 방지)
  TRY.
      DATA(lv_bodytext_raw) TYPE xstring.
      
      " String → XSTRING (UTF-8)
      CALL FUNCTION 'SCMS_STRING_TO_XSTRING'
        EXPORTING
          text   = i_bodytext
        IMPORTING
          buffer = lv_bodytext_raw
        EXCEPTIONS
          OTHERS = 1.

      " XSTRING → Base64
      CALL FUNCTION 'SCMS_BASE64_ENCODE_STR'
        EXPORTING
          input  = lv_bodytext_raw
        IMPORTING
          output = lv_bodytext_b64.

    CATCH cx_root INTO lx_error.
      e_success = abap_false.
      e_message = 'Body 인코딩 실패: ' && lx_error->get_text( ).
      RETURN.
  ENDTRY.

  " ✅ 4. 수신자 목록 JSON 변환
  LOOP AT t_recipients INTO DATA(ls_recip).
    DATA(lv_recip_json) = |"{{ "emp_id":"{ls_recip-emp_id}", "email":"{ls_recip-email}", "name":"{ls_recip-name}" }}"| .
    APPEND lv_recip_json TO lt_recipients_json.
  ENDLOOP.

  " ✅ 5. POST 파라미터 생성 (application/x-www-form-urlencoded)
  DATA: lt_form_data TYPE tihttpnvp,
        ls_form_data TYPE ihttpnvp.

  " openapi=on
  ls_form_data-name  = 'openapi'.
  ls_form_data-value = 'on'.
  APPEND ls_form_data TO lt_form_data.

  " empcode=admin1
  ls_form_data-name  = 'empcode'.
  ls_form_data-value = 'admin1'.  " 고정값 또는 파라미터로 변경
  APPEND ls_form_data TO lt_form_data.

  " to=수신자 사번
  LOOP AT t_recipients INTO ls_recip.
    ls_form_data-name  = 'to'.
    ls_form_data-value = ls_recip-emp_id.
    APPEND ls_form_data TO lt_form_data.
  ENDLOOP.

  " subject=메일제목
  ls_form_data-name  = 'subject'.
  ls_form_data-value = i_subject.
  APPEND ls_form_data TO lt_form_data.

  " bodytext=메일본문(Base64)
  ls_form_data-name  = 'bodytext'.
  ls_form_data-value = lv_bodytext_b64.
  APPEND ls_form_data TO lt_form_data.

  " encodetype=UTF-8
  ls_form_data-name  = 'encodetype'.
  ls_form_data-value = 'UTF-8'.
  APPEND ls_form_data TO lt_form_data.

  " certKey=ekdphandy
  ls_form_data-name  = 'certKey'.
  ls_form_data-value = i_cert_key.
  APPEND ls_form_data TO lt_form_data.

  " autosubmit=Y
  ls_form_data-name  = 'autosubmit'.
  ls_form_data-value = 'Y'.
  APPEND ls_form_data TO lt_form_data.

  " ✅ 6. Form Data를 URL-Encoded String으로 변환
  DATA: lv_form_string TYPE string.
  LOOP AT lt_form_data INTO ls_form_data.
    IF sy-tabix = 1.
      lv_form_string = |{ ls_form_data-name }={ cl_http_utility=>escape_url( ls_form_data-value ) }|.
    ELSE.
      lv_form_string = |{ lv_form_string }&{ ls_form_data-name }={ cl_http_utility=>escape_url( ls_form_data-value ) }|.
    ENDIF.
  ENDLOOP.

  " ✅ 7. HTTP Request Body 설정
  lo_http_client->request->set_cdata( lv_form_string ).

  " ✅ 8. HTTP 요청 전송
  TRY.
      lo_http_client->send(
        EXCEPTIONS
          http_communication_failure = 1
          http_invalid_state         = 2
          http_processing_failed     = 3
          OTHERS                     = 4 ).

      IF sy-subrc <> 0.
        e_success = abap_false.
        e_message = '메일 발송 요청 실패'.
        RAISE http_communication_failure.
      ENDIF.

      " ✅ 9. HTTP 응답 수신
      lo_http_client->receive(
        EXCEPTIONS
          http_communication_failure = 1
          http_invalid_state         = 2
          http_processing_failed     = 3
          OTHERS                     = 4 ).

      IF sy-subrc <> 0.
        e_success = abap_false.
        e_message = '메일 발송 응답 수신 실패'.
        RAISE http_communication_failure.
      ENDIF.

    CATCH cx_root INTO lx_error.
      e_success = abap_false.
      e_message = lx_error->get_text( ).
      RAISE http_communication_failure.
  ENDTRY.

  " ✅ 10. 응답 상태 확인
  lo_http_client->response->get_status(
    IMPORTING
      code   = lv_code
      reason = lv_reason ).

  lv_response = lo_http_client->response->get_cdata( ).
  e_response = lv_response.

  " ✅ 11. 성공 여부 판단
  IF lv_code = 200 OR lv_code = 201.
    " 응답 JSON 파싱 (RET_MSG 확인)
    IF lv_response CS 'RET_MSG'.
      e_success = abap_true.
      e_message = '메일 발송 성공'.
    ELSE.
      e_success = abap_false.
      e_message = '메일 발송 실패 - 응답: ' && lv_response.
    ENDIF.
  ELSE.
    e_success = abap_false.
    e_message = |메일 발송 실패 - HTTP { lv_code }: { lv_reason }|.
  ENDIF.

  " ✅ 12. HTTP 연결 종료
  lo_http_client->close( ).

ENDFUNCTION.

📋 필수 구조체 정의 (SE11)

1. ZSTFI_MAIL_RECIPIENT (수신자 정보)

Structure: ZSTFI_MAIL_RECIPIENT

Component      Type         Length  Description
-----------------------------------------------
EMP_ID         CHAR         20      사원번호
EMAIL          CHAR         100     이메일 주소
NAME           CHAR         50      이름
RECIP_TYPE     CHAR         2       TO/CC/BCC

2. ZSTFI_MAIL_ATTACH (첨부파일)

Structure: ZSTFI_MAIL_ATTACH

Component      Type         Length  Description
-----------------------------------------------
FILENAME       CHAR         255     파일명
FILEDATA       XSTRING              파일 데이터(Binary)
FILESIZE       INT4                 파일 크기(Bytes)
MIMETYPE       CHAR         100     MIME Type

🧪 테스트 프로그램

REPORT z_test_groupware_mail.

DATA: lt_recipients  TYPE TABLE OF zstfi_mail_recipient,
      ls_recipient   TYPE zstfi_mail_recipient,
      lv_success     TYPE abap_bool,
      lv_message     TYPE string,
      lv_response    TYPE string.

" ✅ 수신자 추가
ls_recipient-emp_id = '1001'.
ls_recipient-email = 'user1@company.com'.
ls_recipient-name = '홍길동'.
ls_recipient-recip_type = 'TO'.
APPEND ls_recipient TO lt_recipients.

ls_recipient-emp_id = '1002'.
ls_recipient-email = 'user2@company.com'.
ls_recipient-name = '김철수'.
ls_recipient-recip_type = 'CC'.
APPEND ls_recipient TO lt_recipients.

" ✅ 메일 발송
CALL FUNCTION 'ZFI_GROUPWARE_SEND_MAIL'
  EXPORTING
    i_mail_api_url = 'http://groupware.company.com/api/sendmail'
    i_emp_id       = 'admin1'
    i_subject      = '테스트 메일입니다'
    i_bodytext     = '안녕하세요. 이것은 테스트 메일입니다.'
    i_cert_key     = 'ekdphandy'
  IMPORTING
    e_success      = lv_success
    e_message      = lv_message
    e_response     = lv_response
  TABLES
    t_recipients   = lt_recipients
  EXCEPTIONS
    http_communication_failure = 1
    http_invalid_state         = 2
    OTHERS                     = 3.

IF sy-subrc = 0.
  IF lv_success = abap_true.
    WRITE: / '✅ 메일 발송 성공!'.
    WRITE: / '응답:', lv_response.
  ELSE.
    WRITE: / '❌ 메일 발송 실패:', lv_message.
    WRITE: / '응답:', lv_response.
  ENDIF.
ELSE.
  WRITE: / '❌ Function 호출 실패:', sy-subrc.
  WRITE: / '메시지:', lv_message.
ENDIF.

🔧 고급 기능: 첨부파일 지원

첨부파일을 추가하려면 Function Module을 확장해야 합니다.

" ✅ 첨부파일 처리 추가 (Function Module 내부)

" 첨부파일이 있는 경우
IF t_attachments[] IS NOT INITIAL.
  
  " Multipart/form-data 방식으로 변경
  DATA: lv_boundary TYPE string VALUE 'ABAP_BOUNDARY_12345',
        lv_body     TYPE string.

  " Boundary 설정
  lo_http_client->request->set_content_type( 
    |multipart/form-data; boundary={ lv_boundary }| ).

  " 기본 필드 추가
  lv_body = |--{ lv_boundary }{ cl_abap_char_utilities=>cr_lf }|.
  lv_body = |{ lv_body }Content-Disposition: form-data; name="openapi"{ cl_abap_char_utilities=>cr_lf }|.
  lv_body = |{ lv_body }{ cl_abap_char_utilities=>cr_lf }on{ cl_abap_char_utilities=>cr_lf }|.

  " 첨부파일 추가
  LOOP AT t_attachments INTO DATA(ls_attach).
    DATA(lv_attach_b64) TYPE string.
    
    " Binary → Base64
    CALL FUNCTION 'SCMS_BASE64_ENCODE_STR'
      EXPORTING
        input  = ls_attach-filedata
      IMPORTING
        output = lv_attach_b64.

    lv_body = |{ lv_body }--{ lv_boundary }{ cl_abap_char_utilities=>cr_lf }|.
    lv_body = |{ lv_body }Content-Disposition: form-data; name="attach"; filename="{ ls_attach-filename }"{ cl_abap_char_utilities=>cr_lf }|.
    lv_body = |{ lv_body }Content-Type: { ls_attach-mimetype }{ cl_abap_char_utilities=>cr_lf }|.
    lv_body = |{ lv_body }Content-Transfer-Encoding: base64{ cl_abap_char_utilities=>cr_lf }|.
    lv_body = |{ lv_body }{ cl_abap_char_utilities=>cr_lf }{ lv_attach_b64 }{ cl_abap_char_utilities=>cr_lf }|.
  ENDLOOP.

  " 마지막 Boundary
  lv_body = |{ lv_body }--{ lv_boundary }--{ cl_abap_char_utilities=>cr_lf }|.

  lo_http_client->request->set_cdata( lv_body ).
ENDIF.

🔐 보안 강화: HTTPS + 인증서

1. SM59 설정

Transaction: SM59
Create → HTTP Connections to External Server

RFC Destination: Z_GROUPWARE_API
Target Host: groupware.company.com
Port: 443 (HTTPS)
Path Prefix: /api

Logon & Security:
  ☑ SSL (Active)
  SSL Certificate: DFAULT
  Authentication: Basic / OAuth 2.0

2. 코드 수정 (RFC Destination 사용)

" HTTP Client 생성 (RFC Destination 사용)
CALL METHOD cl_http_client=>create_by_destination
  EXPORTING
    destination              = 'Z_GROUPWARE_API'
  IMPORTING
    client                   = lo_http_client
  EXCEPTIONS
    argument_not_found       = 1
    destination_not_found    = 2
    destination_no_authority = 3
    plugin_not_active        = 4
    internal_error           = 5
    OTHERS                   = 6.

📊 Java vs ABAP 기능 매핑

Java 코드ABAP 구현
HttpClient httpClient = new DefaultHttpClient() cl_http_client=>create_by_url()
HttpPost httpPost = new HttpPost(url) set_method( 'POST' )
params.add(new BasicNameValuePair(...)) ls_form_data-name/value
httpPost.setEntity(new UrlEncodedFormEntity(...)) set_cdata( lv_form_string )
HttpResponse response = httpClient.execute(httpPost) send() / receive()
EntityUtils.toString(respEntity) get_cdata()
Hashtable<String, String> ret e_response (JSON String)

⚠️ 주의사항

  1. 한글 인코딩: Body는 반드시 Base64 인코딩 필요
  2. URL 파라미터: cl_http_utility=>escape_url() 사용
  3. 타임아웃: 기본 60초, 필요 시 set_timeout() 조정
  4. 에러 처리: 응답 JSON에서 RET_MSG 확인
  5. 인증키: certKey는 그룹웨어 관리자에게 발급 요청

 체크리스트

  •  SE11에서 구조체 생성 (ZSTFI_MAIL_RECIPIENT)
  •  SE37에서 Function Module 생성
  •  SM59에서 RFC Destination 설정 (HTTPS)
  •  그룹웨어 API URL 및 인증키 확인
  •  테스트 프로그램 실행
  •  메일 수신 확인
반응형