<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>j_wisdom_h.log</title>
        <link>https://velog.io/</link>
        <description>안녕하세요! j_wisdom_h의 개발기록 블로그입니다.</description>
        <lastBuildDate>Fri, 27 Feb 2026 14:07:40 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>j_wisdom_h.log</title>
            <url>https://velog.velcdn.com/images/j_wisdom_h/profile/9789f951-f6d4-4747-b6de-5a45b7a662ef/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. j_wisdom_h.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/j_wisdom_h" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[SeSAC] BDC 프로그램: 대량 데이터 업로드]]></title>
            <link>https://velog.io/@j_wisdom_h/SeSAC-BDC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EB%8C%80%EB%9F%89-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%97%85%EB%A1%9C%EB%93%9C</link>
            <guid>https://velog.io/@j_wisdom_h/SeSAC-BDC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EB%8C%80%EB%9F%89-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%97%85%EB%A1%9C%EB%93%9C</guid>
            <pubDate>Fri, 27 Feb 2026 14:07:40 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/0bc13f06-553c-46c3-a141-fae3a85f5f1b/image.png" alt=""></p>
<h2 id="1-excel-업로드-파일-선택">1. Excel 업로드 파일 선택</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/ba5dfebe-4248-49d1-ac25-e5cb736bc78e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/08f828f3-635d-411d-bf64-b0aafec13f05/image.png" alt=""></p>
<p>파일선택 클릭시 &gt; file open dialog</p>
<h2 id="2-excel-upload-내역확인">2. Excel Upload 내역확인</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/c4799fdf-c3ac-4c4d-a784-16f8bb54d213/image.png" alt=""></p>
<p>엑셀업로드 내역확인</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/91c3f4a9-7fff-46a0-adad-4ebe49403f21/image.png" alt=""></p>
<p>실제 엑셀파일의 데이터</p>
<h2 id="3-고객등록-bdc">3. 고객등록 (BDC)</h2>
<h3 id="3-1-bdc-실행-스닛핏">3-1. BDC 실행 스닛핏</h3>
<pre><code class="language-abap">
  PERFORM set_bdc_options.

  PERFORM bdc_data USING: &#39;X&#39; &#39;SAPMZSP1020&#39; &#39;0100&#39;,
                          &#39;&#39; &#39;BDC_CURSOR&#39; &#39;ZSSP1020-CUSTID_L&#39;,
                          &#39;&#39; &#39;BDC_OKCODE&#39; &#39;=INSERT&#39;.

  LOOP AT gt_data INTO gs_data.
    PERFORM bdc_data USING: &#39;X&#39; &#39;SAPMZSP1020&#39; &#39;0200&#39;,
                            &#39;&#39; &#39;ZSSP1021-CNAME&#39; gs_data-cname,
                            &#39;&#39; &#39;ZSSP1021-PHONE&#39; gs_data-phone,
                            &#39;&#39; &#39;ZSSP1021-EMAIL&#39; gs_data-email,
                            &#39;&#39; &#39;ZSSP1021-POSTCD&#39; gs_data-postcd,
                            &#39;&#39; &#39;ZSSP1021-ADDRS&#39; gs_data-addrs,
                            &#39;&#39; &#39;BDC_OKCODE&#39; &#39;=ADD&#39;.
    CLEAR gs_data.
  ENDLOOP.</code></pre>
<pre><code class="language-abap"> CALL TRANSACTION &#39;ZSP1020&#39; **USING gt_bdc** **OPTIONS FROM gs_opt MESSAGES INTO gt_bdcmsg.**</code></pre>
<pre><code class="language-abap">FORM set_bdc_options .
  gs_opt-dismode  = &#39;N&#39;.
  gs_opt-updmode  = &#39;S&#39;.
  **gs_opt-racommit = &#39;X&#39;.**
ENDFORM.</code></pre>
<ul>
<li><p><strong>racommit 속성</strong>을 사용해 insert에서 commit work를 만났을때 transaction이 종료되는 문제.</p>
<p>  (해결) racommit = ‘X’이면 commit work를 만나도 transaction이 종료되지 않음.</p>
</li>
<li><p><strong>gt_bdcmsg</strong>을 사용하여 에러메세지 처리</p>
<pre><code class="language-abap">      PERFORM get_msg_text USING ls_msg-msgid ls_msg-msgnr
                           ls_msg-msgv1 ls_msg-msgv2 ls_msg-msgv3 ls_msg-msgv4
                     CHANGING lv_msg_text. 
      ... 

      IF ls_msg-msgtyp EQ &#39;A&#39; OR ls_msg-msgtyp EQ &#39;E&#39;.
        gs_data-light = 1.
        gs_data-msg = |[실패] { lv_msg_text }|.
      ELSE.
        IF ls_msg-msgid EQ &#39;ZSPMSG&#39; AND ls_msg-msgnr EQ &#39;035&#39;.
          gs_data-light = 3.
        ELSE.
          gs_data-light = 1.
          gs_data-msg = |[실패] { lv_msg_text }|.
        ENDIF.
      ENDIF.</code></pre>
</li>
</ul>
<h3 id="3-2-bdc-실행결과">3-2. BDC 실행결과</h3>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/f8588f71-fcc7-46e7-a2bc-251e43889d8e/image.png" alt=""></p>
<p>BDC 실행결과 - 상태 및 에러메세지</p>
<p>실행 실패시, 에러메시지 표출됩니다.</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/9dc7e5bd-2e1d-401b-8a7b-b5b4035d82e6/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/de05d74c-85e9-41ca-8507-2895f1c13616/image.png" alt=""></p>
<p>BDC 실행결과 레코드가 신규 등록되는것 확인할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SeSAC] 트랜잭션 처리 전략 - Lock, 커밋/롤백, Audit Trail]]></title>
            <link>https://velog.io/@j_wisdom_h/SeSAC-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%B2%98%EB%A6%AC-%EC%A0%84%EB%9E%B5-Lock-%EC%BB%A4%EB%B0%8B%EB%A1%A4%EB%B0%B1</link>
            <guid>https://velog.io/@j_wisdom_h/SeSAC-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%B2%98%EB%A6%AC-%EC%A0%84%EB%9E%B5-Lock-%EC%BB%A4%EB%B0%8B%EB%A1%A4%EB%B0%B1</guid>
            <pubDate>Fri, 27 Feb 2026 14:06:39 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/d70019df-8d4b-4e99-94e8-eabae635673f/image.png" alt=""></p>
<h1 id="트랜잭션-처리-전략---lock-커밋롤백">트랜잭션 처리 전략 - Lock, 커밋/롤백</h1>
<h2 id="📌-배경why">📌 배경(Why?)</h2>
<p>입고(GR) 데이터는 재고·회계·후속 문서 흐름에 직접 연결되는 핵심 기준 데이터이기 때문에,
저장 시점의 작은 오류나 처리 충돌도 운영·재무 전반에 연쇄적인 영향을 미칠 수 있다.</p>
<p>이에 따라 다음과 같은 구조적 리스크를 통제할 필요가 있었다.</p>
<h3 id="1-부분-성공·부분-실패로-인한-트랜잭션-불일치">1) 부분 성공·부분 실패로 인한 트랜잭션 불일치</h3>
<ul>
<li>한 번의 저장안에 여러 품목이 함께 처리되는 구조로</li>
<li>일부 Item만 성공하고 일부는 실패하는 경우, 사용자 인식과 실제 반영 데이터 간 괴리가 발생</li>
</ul>
<p>➡ 입고 문서와 재고 테이블 간 불일치, 운영자 재확인·조정 업무 증가</p>
<h3 id="2-lock-전략-미흡으로-인한-통제-불명확성">2) Lock 전략 미흡으로 인한 통제 불명확성</h3>
<ul>
<li>동일 레코드에 대한 동시 수정 통제 기준이 명확하지 않을 경우</li>
<li>누가 먼저 수정했는지, 왜 저장이 실패했는지 파악이 어려움</li>
</ul>
<p>➡ 데이터 충돌 발생 가능성과 운영 부서의 시스템 신뢰도 저하의 문제 발생</p>
<h2 id="📌-설계-의도why-this-design">📌 설계 의도(Why this design?)</h2>
<p>입고(GR) 수정 및 저장 시점을 Logical Unit of Work (LUW) 정의하고,</p>
<p>동시성 제어와 Commit/Rollback 규칙을 명확히 설정함으로써</p>
<p>항상 일관된 처리 결과가 보장되도록 설계하였다.</p>
<h3 id="1-레코드-단위-lock으로-동시-수정-자체를-논리적으로-차단">1) 레코드 단위 Lock으로 ‘동시 수정’ 자체를 논리적으로 차단</h3>
<p>입고 품목은 <strong>입고문서 번호(GRNUM) + 품목 번호(GRPOS)</strong> 조합으로 식별된다.</p>
<p>이에 해당 키를 기준으로 <strong>ENQUEUE/DEQUEUE Lock을 적용</strong>하여</p>
<p>동일 데이터에 대한 동시 수정이 발생하지 않도록 통제하였다.</p>
<ul>
<li>선행 사용자가 Lock을 선점하면 수정 권한을 보유</li>
<li>후속 사용자는 조회만 가능하도록 제한하거나, 수정 시도 시 명확한 오류 메시지를 반환</li>
</ul>
<p>이를 통해 <strong>“First User Wins” 원칙</strong>을 적용하고,</p>
<p>데이터 충돌을 애플리케이션 레벨이 아닌 <strong>트랜잭션 레벨에서 구조적으로 차단</strong>하였다.</p>
<blockquote>
<p>“동시성 문제를 해결하는 가장 확실한 방법은 ‘동시에 같은 데이터를 못 건드리게 하는 것’이라고 판단했다.
동일 데이터에 대한 동시 수정 충돌을 SAP Lock(Enqueue) 메커니즘을 통해 구조적으로 방지하였다.</p>
</blockquote>
<h3 id="2-all-or-nothing-트랜잭션-원칙">2) <strong>All or Nothing 트랜잭션 원칙</strong></h3>
<p>입고 저장 과정에서 일부 품목 또는 일부 테이블만 반영되고 나머지가 실패하는 경우,</p>
<p><strong>GR 문서·PO 상태·재고 테이블·FI 문서 간 데이터 불일치가 발생할 가능성이 있었다.</strong></p>
<p>다음 네 영역을 단일 처리 단위로 묶어 관리하였다.</p>
<ul>
<li>GR 문서 생성</li>
<li>GR와 연계된 <strong>PO 상태 변경(완료 플래그)</strong></li>
<li>GR 기준으로 반영되는 <strong>재고 테이블 수량/레코드 생성·수정</strong></li>
<li>GR에 의해 발생하는 <strong>FI 문서(회계 전표) 생성</strong></li>
</ul>
<p>위 영역 중</p>
<p><strong>어느 한 단계라도 오류가 발생하면 <code>ROLLBACK WORK</code>를 수행하고</strong></p>
<p>모든 처리가 <strong>정상 완료된 경우에만 <code>COMMIT WORK</code></strong></p>
<p>를 실행하도록 설계하였다</p>
<p>즉,</p>
<blockquote>
<p>“한 번의 저장은 GR·PO·재고·FI가 모두 성공했을 때만 유효하다”</p>
</blockquote>
<p>라는 <strong>All-or-Nothing 트랜잭션 원칙</strong>을 적용하여,</p>
<p>문서·재고·회계 상태가 서로 어긋난 채로 남는 상황을 구조적으로 차단하였다.</p>
<h2 id="📌-구현-방식how">📌 구현 방식(How?)</h2>
<h2 id="1-enqueuedequeue-기반-동시성-제어-구현">1) ENQUEUE/DEQUEUE 기반 동시성 제어 구현</h2>
<h3 id="①-입고-품목-단위-lock-선점">① 입고 품목 단위 Lock 선점</h3>
<p><strong>입고 수정</strong> 저장이 시작될때,</p>
<p>변경 대상 품목을 기준으로 <strong>GRNUM(입고문서 번호) + GRPOS(품목 번호)</strong> 조합에 대해 Lock을 선점하도록 설계하였다.</p>
<ul>
<li>저장 로직 진입 시 각 품목별로 <strong><code>ENQUEUE</code></strong> 호출</li>
<li>Lock 선점 성공 시 → 해당 품목을 Validation 및 Update 대상으로 포함</li>
<li>Lock 선점 실패(<code>foreign_lock</code>) 시 → 즉시 저장을 중단하고 사용자에게 명확한 안내 메시지 제공</li>
</ul>
<blockquote>
<p>“해당 품목은 현재 다른 사용자에 의해 수정 중입니다.”</p>
</blockquote>
<p>이를 통해 동일 품목에 대한 동시 수정 자체를 사전에 차단하고,</p>
<p>사용자가 저장 실패 원인을 명확히 인지할 수 있도록 하였다.</p>
<pre><code class="language-abap">*&amp;---------------------------------------------------------------------*
*&amp;      Form  ENQUEUE_LOCK
*&amp;---------------------------------------------------------------------*
*       Lock Enqueue
*----------------------------------------------------------------------*
FORM enqueue_lock_item .
  DATA lv_numc LIKE ztspgri-grpos.

  CLEAR gv_subrc.
  PERFORM get_selected_rows USING go_alvb.

  LOOP AT gt_selected INTO DATA(ls_selected).
    CLEAR lv_numc.
    lv_numc = ls_selected-row_id.

    CALL FUNCTION &#39;ENQUEUE_EZ_ZTSPGRI&#39;
      EXPORTING
        mode_ztspgri   = &#39;E&#39;
        mandt          = sy-mandt
        grnum          = gs_hmain-grnum
        grpos          = lv_numc
      EXCEPTIONS
        foreign_lock   = 1
        system_failure = 2
        OTHERS         = 3.
    IF sy-subrc NE 0.
      gv_subrc = sy-subrc.
      MESSAGE i012. &quot; 해당 품목은 현재 다른 사용자에 의해 수정 중입니다.
      RETURN.
    ENDIF.
  ENDLOOP.
ENDFORM.</code></pre>
<h3 id="②--트랜잭션-종료-시점의-lock-해제-전략">②  트랜잭션 종료 시점의 Lock 해제 전략</h3>
<p>입고 저장 로직이 종료될 때,</p>
<p>처리 대상 품목에 대해 <code>DEQUEUE</code>를 호출하여 Lock을 명확히 해제하도록 설계하였다.</p>
<ul>
<li>모든 Validation 및 DB Update가 정상 완료된 경우 → <code>COMMIT WORK</code> 이후 <code>DEQUEUE</code> 수행</li>
<li>예외 또는 오류 발생 시 → <code>ROLLBACK WORK</code> 수행 후 반드시 <code>DEQUEUE</code> 실행</li>
</ul>
<p>특히 예외 상황에서도 Lock이 해제되지 않고 남는 일이 없도록,</p>
<p>예외 처리 블록에서 Lock 해제를 강제하는 구조로 설계하였다.</p>
<p>이를 통해</p>
<ul>
<li>Lock 잔존으로 인한 Dead Lock 발생 가능성 차단</li>
<li>특정 사용자가 데이터를 장시간 점유하는 상황 방지</li>
<li>동시 사용자 환경에서도 안정적인 운영 보장</li>
</ul>
<p>하도록 하였다.</p>
<pre><code class="language-abap">*&amp;---------------------------------------------------------------------*
*&amp;      Form  DEQUEUE_LOCK_ITEM
*&amp;---------------------------------------------------------------------*
*       Lock Dequeue
*----------------------------------------------------------------------*
FORM dequeue_lock_item.
  DATA lv_numc LIKE ztspgri-grpos.

  LOOP AT gt_selected INTO DATA(ls_selected).
    CLEAR lv_numc.
    lv_numc = ls_selected-row_id.

    CALL FUNCTION &#39;DEQUEUE_EZ_ZTSPGRI&#39;
      EXPORTING
        mode_ztspgri = &#39;E&#39;
        mandt        = sy-mandt
        grnum        = gs_hmain-grnum
        grpos        = lv_numc.
  ENDLOOP.

  CLEAR gt_selected.
ENDFORM.</code></pre>
<h2 id="2-all-or-nothing-트랜잭션-처리-구현-방식">2) All or Nothing 트랜잭션 처리 구현 방식</h2>
<p>‘한 번의 저장 = 한 번의 Commit 또는 Rollback’ 원칙을 실제로 적용하기 위해, 사용자가 GR을 저장하는 행위 전체를 하나의 업무 트랜잭션으로 보고 GR·재고·PO·FI가 함께 움직이도록 설계했다.</p>
<h3 id="①-gr-문서-생성이-전체-트랜잭션의-시작점">① <strong>GR 문서 생성이 전체 트랜잭션의 시작점</strong></h3>
<ul>
<li><p>사용자가 저장을 누르면 <strong>먼저 GR 헤더를 생성</strong>한다.</p>
</li>
<li><p>GR 자체가 생성되지 않으면</p>
<p>  → 재고 반영, PO 상태 변경, FI 생성 <strong>모두 수행되지 않도록 차단</strong>.</p>
</li>
<li><p>GR이 존재하지 않는데 재고나 회계가 움직이는 <strong>비정합 상황을 원천 방지</strong>했다.</p>
</li>
</ul>
<hr>
<h3 id="②-모든-gr-품목·재고-반영을-한-묶음으로-처리">② <strong>모든 GR 품목·재고 반영을 ‘한 묶음’으로 처리</strong></h3>
<ul>
<li><p>GR 헤더가 성공해야 그 다음에 <strong>각 GR Item 생성 + 재고 증가</strong>를 수행한다.</p>
</li>
<li><p>어떤 품목이라도 입력 오류·재고 제약·Validation 실패가 발생하면</p>
<p>  → 이번 저장 전체를 <strong>즉시 Rollback</strong>.</p>
</li>
<li><p>“몇 개 품목만 반영된 반쪽짜리 GR”이 남지 않도록 했다.</p>
</li>
</ul>
<hr>
<h3 id="③-po-품목·헤더-상태-자동-업데이트-성공-시에만-진행">③ <strong>PO 품목·헤더 상태 자동 업데이트 (성공 시에만 진행)</strong></h3>
<ul>
<li><p>모든 GR Item + 재고 반영이 정상 완료된 경우에만</p>
<p>  → <strong>PO Item 상태(진행/부분완료/완료) 갱신</strong></p>
<p>  → 잔여 진행 품목 수에 따라 <strong>PO 헤더 상태 자동 변경</strong>.</p>
</li>
<li><p>PO 상태 갱신 중 하나라도 실패하면</p>
<p>  → GR·재고·PO 변경 <strong>전체를 Rollback</strong>.</p>
</li>
</ul>
<p>→ “입고는 되었지만 PO는 예전 상태 그대로” 같은 <strong>문서 불일치가 발생하지 않도록</strong> 했다.</p>
<hr>
<h3 id="④-fi-문서까지-포함한-단일-트랜잭션으로-통합">④ <strong>FI 문서까지 포함한 ‘단일 트랜잭션’으로 통합</strong></h3>
<ul>
<li><p>FI 생성까지 성공해야만 <strong>최종 Commit</strong>.</p>
</li>
<li><p>FI에서 오류가 나면</p>
<p>  → GR, 재고, PO 변경까지 <strong>모두 Rollback</strong>.</p>
</li>
<li><p>MM(재고)과 FI(회계)가 <strong>서로 다른 상태로 갈라지는 현상</strong>을 근본적으로 차단했다.</p>
</li>
</ul>
<h2 id="3-생성수정-이력-필드-기반-audit-trail-확보">3) 생성/수정 이력 필드 기반 Audit Trail 확보</h2>
<p>입고(GR)와 연계된 재고·PO·FI 전 구간에 대해,</p>
<p>모든 테이블에 <strong>생성자·생성일·생성시각 / 수정자·수정일·수정시각</strong>을 갖는<strong>공통 Audit 필드 구조를 표준 규칙으로 설계했다.</strong></p>
<ul>
<li>GR 생성 시점에는 해당 필드에 <strong>생성 정보를 일괄 세팅</strong>하고, 
이후 재처리·정정 등으로 데이터가 변경될 경우에는 <strong>수정 정보만 업데이트</strong>하도록 일관된 플로우를 정의했다.</li>
</ul>
<p>이를 통해,</p>
<ul>
<li><p><strong>누가, 언제, 어떤 업무 흐름으로 데이터를 생성·수정했는지</strong>를 명확하게 추적할 수 있고</p>
<p>  단순 오류 분석뿐 아니라 <strong>감사(Audit), 내부통제, 운영 보고서 작성 시에도 신뢰도 높은 이력 기반</strong>을 제공할 수 있게 되었다.</p>
</li>
</ul>
<blockquote>
<p>생성/수정 이력은 단순히 “언제 바뀌었는지”를 남기는 용도를 넘어,</p>
<p>변경 주체(담당자)와 변경 타이밍을 기준으로 책임과 권한을 명확히 할 수 있는 근거 데이터로 활용된다.</p>
<p>이슈를 추적할 때, “어느 조직/사용자가 어떤 패턴으로 수정했는지”를 이력 기반으로 분석할 수 있어, 
운영 정책 수립, 권한 조정, 교육 대상 선정 같은 후속 프로세스에도 직접적인 인사이트를 제공한다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SeSAC] 입고 기반 재고 정합성 확보를 위한 Validation & 재고 반영 구조 설계]]></title>
            <link>https://velog.io/@j_wisdom_h/SeSAC-%EC%9E%85%EA%B3%A0-%EA%B8%B0%EB%B0%98-%EC%9E%AC%EA%B3%A0-%EC%A0%95%ED%95%A9%EC%84%B1-%ED%99%95%EB%B3%B4%EB%A5%BC-%EC%9C%84%ED%95%9C-Validation-%EC%9E%AC%EA%B3%A0-%EB%B0%98%EC%98%81-%EA%B5%AC%EC%A1%B0-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@j_wisdom_h/SeSAC-%EC%9E%85%EA%B3%A0-%EA%B8%B0%EB%B0%98-%EC%9E%AC%EA%B3%A0-%EC%A0%95%ED%95%A9%EC%84%B1-%ED%99%95%EB%B3%B4%EB%A5%BC-%EC%9C%84%ED%95%9C-Validation-%EC%9E%AC%EA%B3%A0-%EB%B0%98%EC%98%81-%EA%B5%AC%EC%A1%B0-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Fri, 27 Feb 2026 14:05:58 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/8020de87-f4f4-4181-96a7-304fa93e3260/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/18c74ec1-3a50-4861-8b36-21993d7d8c65/image.gif" alt="입고문서 생성
"></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/b7e54794-b400-4daa-bcc5-fe957bfcbc62/image.gif" alt="반품제품에 대한 입고문서 생성"></p>
<h2 id="📌-배경"><strong>📌 배경</strong></h2>
<p>입고(GR)는 재고·회계·PO 상태에 직접 영향을 주는 핵심 프로세스이기 때문에,
입력 오류나 처리 기준의 불명확성이 존재할 경우 연쇄적인 정합성 문제가 발생할 가능성이 있었다.</p>
<p>이에 따라 다음과 같은 운영 리스크를 사전에 통제할 필요가 있었다.</p>
<h3 id="1-잘못된-입력으로-인한-연쇄-오류-가능성">1) 잘못된 입력으로 인한 연쇄 오류 가능성</h3>
<ul>
<li>주말·미래일·마감일 입력</li>
<li>미운영/삭제된 창고·플랜트 선택</li>
<li>삭제·완료된 문서를 기반으로 GR 생성 시도</li>
</ul>
<p>➡ <strong>재고 수량·회계 반영·PO 진행 상태가 모두 틀어지는 문제 발생</strong></p>
<hr>
<h3 id="2-정상-입고·반품-입고가-동일하게-처리되는-구조"><strong>2) 정상 입고·반품 입고가 동일하게 처리되는 구조</strong></h3>
<ul>
<li><p>반품 사유(정상/불량)에 따라 재고 반영 여부가 달라야 함에도</p>
</li>
<li><p>하나의 로직으로 처리되어 <strong>재고가 과다 혹은 중복 반영</strong>되는 사례 발생</p>
<p>  ➡ <strong>반품 유형별</strong> 재고 처리 기준을 <strong>시스템적으로 강제하는 것이 필요</strong></p>
</li>
</ul>
<hr>
<h3 id="3-신규-품번창고-조합의-재고-테이블-누락-문제"><strong>3) 신규 품번/창고 조합의 재고 테이블 누락 문제</strong></h3>
<ul>
<li><p>재고 테이블에 “자재 × 플랜트 × 창고 × 사이즈” 조합이 없으면</p>
<p>  운영자가 직접 레코드를 생성해야 했다.</p>
</li>
<li><p>신규 플랜트 오픈 혹은 신규 라인 투입 시 <strong>누락·지연·운영 부담 증가</strong></p>
<p>  ➡ 입고 시점에서 <strong>재고 레코드가 자동 생성되는 구조</strong>가 필요</p>
</li>
</ul>
<h2 id="📌-설계-의도why-this-design"><strong>📌 설계 의도(Why this design?)</strong></h2>
<p>입고(GR) 생성 단계에서 발생하는 모든 오류와 재고 반영 문제를 <strong>입력 시점에서 바로 잡고</strong>,</p>
<p>정상 입고·반품 입고·재고 생성까지 <strong>하나의 일관된 흐름으로 처리</strong>하기 위해 다음 의도를 중심으로 설계를 진행했다.</p>
<hr>
<h3 id="1-gr-생성-전에-오류를-선제적으로-차단하는-validation-layer-구축"><strong>1) GR 생성 전에 오류를 ‘선제적으로 차단’하는 Validation Layer 구축</strong></h3>
<p>입고 화면 상단에 Validation 유틸리티 레이어를 두어,</p>
<ul>
<li>입고일(주말·미래일·마감일)</li>
<li>플랜트/창고 상태(운영/미운영/삭제)</li>
<li>참조 문서(삭제·완료·0수량 Item)</li>
</ul>
<p>등을 <strong>GR 생성 이전 단계에서 즉시 검증</strong>한다.</p>
<p>검증 결과는</p>
<ul>
<li><strong>메시지 클래스 기반</strong>의 명확한 안내문구,</li>
<li><strong>Function Group</strong> 의 스크린 팝업을 통한 상세 정보 확인</li>
</ul>
<p>으로 사용자에게 전달되어, 사용자 스스로 <strong>오류 원인을 빠르게 확인하고 수정</strong>할 수 있도록 했다.</p>
<hr>
<h3 id="2-donum-기반으로-정상-입고와-반품-입고를-구조적으로-분리"><strong>2) DONUM 기반으로 정상 입고와 반품 입고를 구조적으로 분리</strong></h3>
<p>입고 유형을 <strong>DONUM 규칙</strong>으로 명확히 구분하였다.</p>
<ul>
<li><strong>1xxxx → PO 기반 정상 입고</strong></li>
<li><strong>4xxxx → GI 기반 반품 입고</strong></li>
</ul>
<p>또한 <strong>반품 사유(정상/불량)에</strong> 따라</p>
<ul>
<li>재고 반영</li>
<li>재고 미반영</li>
</ul>
<p>을 자동 분기해, 기존에 반복적으로 발생하던 <strong>반품 재고 과반영 문제</strong>를 시스템 단에서 차단했다.</p>
<h4 id="✔-정상-반품불량-반품을-재고-수량만으로-구분할-수-없는-문제-해결">✔ <strong>정상 반품/불량 반품을 재고 수량만으로 구분할 수 없는 문제 해결</strong></h4>
<blockquote>
<p><em>“정상 반품과 불량 반품을 모두 재고 증가로 처리하면, 재고 수량만 보고는 ‘판매 가능한 수량’과 ‘불량 적치 수량’을 구분할 수 없었다.
이에 ‘<strong>판매 가능 여부’를 기준으로 재고 반영 기준을 분리해, 정상 반품만 재고로 복귀시키고 불량 반품은 별도 흐름으로 관리</strong>하도록 설계했다.”</em></p>
</blockquote>
<h4 id="✔-불량-반품의-재고-미반영-기준을-명확히-설정">✔ <strong>불량 반품의 재고 미반영 기준을 명확히 설정</strong></h4>
<blockquote>
<p><em>“불량 반품은 <strong>실제로 재판매·재출고가 불가능한 ‘폐기 예정 수량’이라는 점</strong>에 주목했다.
따라서 <strong>정상 재고로 편입하는 대신, 품질(QM)·폐기(Scrap) 프로세스에서 별도로 관리하는 것이 현업 운영 방식과 가장 일치한다고 판단</strong>해, 시스템에서도 재고를 ‘미반영’하도록 설계했다.”</em></p>
</blockquote>
<hr>
<hr>
<h3 id="3-신규-품번·창고·사이즈-조합도-gr-시점에-자동-생성"><strong>3) 신규 품번·창고·사이즈 조합도 GR 시점에 자동 생성</strong></h3>
<p>재고 테이블에</p>
<p>‘자재 × 플랜트 × 창고 × 사이즈’</p>
<p>조합이 존재하지 않더라도,</p>
<p>입고 처리 시 해당 조합을 재고 테이블의 레코드로 <strong>자동 생성</strong>하도록 설계하여</p>
<ul>
<li>신규 플랜트/창고 오픈</li>
<li>첫 입고 품번</li>
<li>신규 라인 투입</li>
</ul>
<p>상황에서도 운영자가 사전 세팅을 하지 않아도 <strong>재고 반영이 누락되지 않도록</strong> 했다.</p>
<h3 id="✔-재고-조합만-자동-생성하도록-한-이유">✔ 재고 조합만 자동 생성하도록 한 이유</h3>
<blockquote>
<p>‘자재 × 플랜트 × 창고 × 사이즈’ 조합은 입고 시점에 재고테이블에 자동 생성되도록 했지만, 
자재 마스터나 플랜트, 창고 마스터 자체는 여전히 표준 마스터 관리 프로세스를 통해서만 생성되도록 구분했다.
재고 구조는 운영 편의를 위해 자동화하되, 마스터 데이터 생성 권한까지 열어버리면 통제 리스크가 커진다고 판단했기 때문이다.</p>
</blockquote>
<h3 id="✔-자동화-범위를-재고-조합으로-제한한-설계-의도">✔ <strong>자동화 범위를 ‘재고 조합’으로 제한한 설계 의도</strong></h3>
<blockquote>
<p>자동 생성 대상을 재고 조합으로 한정함으로써, 가장 번거로워하던 사전 세팅(조합 추가) 작업만 제거하고, 나머지 마스터 관리 프로세스는 기존 거버넌스를 유지하도록 했다. 결과적으로 ‘자동화로 편해지지만, 데이터 거버넌스는 깨지지 않는’ 선을 찾는 것이 설계의 핵심 기준이었다.</p>
</blockquote>
<h2 id="📌-구현-방식how"><strong>📌 구현 방식(How?)</strong></h2>
<hr>
<h3 id="1-입고일-유효성-검사date-validation"><strong>1) 입고일 유효성 검사(Date Validation)</strong></h3>
<p>입고일 입력 시 다음 규칙을 <strong>즉시 검증</strong>하여 잘못된 날짜 입력을 사전 차단했다.</p>
<ul>
<li>오늘 기준 <strong>7일 이전까지만 허용</strong><ul>
<li>‘실제 업무에서 소급 처리가 필요한 최대 기간’을 기준으로 7일로 정의</li>
</ul>
</li>
<li>미래날짜 자동 차단</li>
<li>명확한 메시지로 오류 원인 안내</li>
</ul>
<p>→ 반복적으로 발생하던 <strong>입고일 및 회계 불일치 오류를 입력 단계에서 원천 방지</strong>했다.</p>
<hr>
<h3 id="2-플랜트·창고-운영-상태-validation"><strong>2) 플랜트·창고 운영 상태 Validation</strong></h3>
<p>입고 생성 시 선택된 플랜트과 창고<strong>의 운영 여부를 즉시 검증</strong>해 잘못된 저장위치 선택을 막았다.</p>
<p>검증 항목</p>
<ul>
<li>운영(Active)</li>
<li>미운영(Inactive)</li>
<li>삭제(Delete)</li>
<li>재고 반영 가능 여부</li>
</ul>
<p>오류 발견 시</p>
<ul>
<li>팝업 경고</li>
<li>GR 생성 차단</li>
<li>대체 창고/플랜트 서치헬프</li>
</ul>
<p>→ 잘못된 장소로 재고가 반영되는 사고를 <strong>시스템이 선제적으로 차단</strong>하도록 설계했다.</p>
<blockquote>
<p>창고/플랜트 상태를 엄격하게 검증한 이유는, 
현업에서 ‘테스트용’으로 열어둔 창고나 더 이상 사용하지 않는 창고에 입고가 들어가는 등 재고조정 업무가 과도하게 발생할 수 있기 때문이다.
운영 주체가 ‘사용 안 한다’고 판단한 저장 위치는 아예 입고 자체가 불가능하도록 시스템 통제 수준을 끌어올렸다.</p>
</blockquote>
<hr>
<h3 id="3-donum-기반-문서-validation--입고반품-자동-분기"><strong>3) DONUM 기반 문서 Validation + 입고/반품 자동 분기</strong></h3>
<p>GR 생성 시 DONUM 규칙으로 원본 문서를 자동으로 구분한다.</p>
<ul>
<li><strong>1xxxx → PO 기반 정상 입고</strong></li>
<li><strong>4xxxx → GI 기반 반품 입고</strong></li>
</ul>
<p>추가로 참조 문서 상태도 동시에 검증한다.</p>
<ul>
<li>삭제된 문서(LOEKZ)</li>
<li>입고 완료된 Item</li>
<li>0수량 Item</li>
</ul>
<p>→ 삭제·완료·무효 Item을 <strong>GR 생성 단계에서 자동 제외</strong>하여</p>
<p>정상 입고·반품 입고 로직을 <strong>정확하게 분리 처리</strong>할 수 있는 기반을 마련했다.</p>
<hr>
<h3 id="4-메시지-클래스-통합--popup-utility-정비"><strong>4) 메시지 클래스 통합 + Popup Utility 정비</strong></h3>
<p>모든 Validation 및 경고 메시지를 <strong>단일 메시지 클래스</strong>로 통합하여:</p>
<ul>
<li>규칙·문구의 일관성 확보</li>
<li>다국어 대응 용이</li>
<li>유지보수 비용 감소</li>
<li>사용자에게 이해 쉬운 메시지 제공</li>
</ul>
<blockquote>
<p>기존에는 유사한 오류라도 프로그램마다 다른 메시지와 코드가 섞여 있어서, 오류 로그만 보고는 정확한 원인을 추적하기 어려웠다. 
그래서 Validation 관련 메시지를 하나의 메시지 클래스로 일원화하고 코드 체계도 통합해, 장애 분석 시 ‘메시지 코드만으로도 어떤 레이어에서 막힌 건지’를 바로 인지할 수 있도록 하였다.</p>
</blockquote>
<p>또한 Function Group 팝업스크린으로 다음 정보를 <strong>화면 이동 없이 조회</strong>할 수 있도록 했다.</p>
<ul>
<li>자재 상세 정보</li>
<li>플랜트·창고 기본 정보</li>
<li>참조 문서 정보</li>
<li>담당자 정보</li>
</ul>
<p>동시에 <strong>여러 화면에서 동일한 조회 스크린을 공통으로 재사용</strong>할 수 있도록 했다.</p>
<blockquote>
<p>공용 FG 팝업을 두면서, 화면마다 비슷한 조회 로직과 UI를 중복 개발하지 않고 한 곳에서만 유지보수하면 되기 때문에, <strong>개발 및 운영 비용을 줄이면서도 사용자 경험(UI/메시지)을 일관되게 유지</strong>할 수 있도록 했다.</p>
</blockquote>
<hr>
<h4 id="5-일반-입고po-기반-재고-반영-로직"><strong>5) 일반 입고(PO 기반) 재고 반영 로직</strong></h4>
<p>일반 입고(1xxxx) 시:</p>
<ul>
<li>GR Item 수량만큼 <strong>재고(matqty) 증가</strong></li>
<li>동일 자재라도 <strong>플랜트/창고/사이즈/자재그룹 조합별로 분리 관리</strong></li>
</ul>
<p>→ 표준 재고 흐름을 유지하면서도</p>
<p>각 사업장·창고·라인의 재고 상태를 <strong>독립적으로 관리</strong>할 수 있게 했다.</p>
<hr>
<h3 id="6-반품-입고gi-기반-재고-반영-분기"><strong>6) 반품 입고(GI 기반) 재고 반영 분기</strong></h3>
<p>반품 입고(4xxxx)는 반품 사유에 따라 재고 반영을 자동 분기한다.</p>
<ul>
<li><strong>정상 반품</strong> → 재고 증가</li>
<li><strong>불량 반품</strong> → 재고 미반영 (GR 문서만 생성)</li>
</ul>
<p>→ 정상 입고와 반품 입고를 동일하게 처리해 발생하던</p>
<p><strong>재고 과반영·중복 반영 문제를 구조적으로 차단</strong>했다.</p>
<hr>
<h3 id="7-자재-×-플랜트-×-창고-×-사이즈-조합-자동-생성"><strong>7) ‘자재 × 플랜트 × 창고 × 사이즈’ 조합 자동 생성</strong></h3>
<p>입고(GR) 시점에 재고 테이블을 조회해 조합이 없으면:</p>
<ul>
<li>신규 레코드 <strong>자동 INSERT</strong></li>
<li>있으면 기존 matqty <strong>UPDATE</strong></li>
</ul>
<p>→ 운영자가 사전에 테이블을 세팅할 필요 없이,</p>
<p><strong>입고 프로세스 자체가 신규 조합을 자동 생성하는 기준점</strong>으로 동작하도록 했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SeSAC] 구매-반품-입고 구조 설계]]></title>
            <link>https://velog.io/@j_wisdom_h/SeSAC-%EA%B5%AC%EB%A7%A4-%EB%B0%98%ED%92%88-%EC%9E%85%EA%B3%A0-%EA%B5%AC%EC%A1%B0-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@j_wisdom_h/SeSAC-%EA%B5%AC%EB%A7%A4-%EB%B0%98%ED%92%88-%EC%9E%85%EA%B3%A0-%EA%B5%AC%EC%A1%B0-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Fri, 27 Feb 2026 14:04:03 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/925234ab-5e5c-4c94-80da-7ff61e94b276/image.png" alt=""></p>
<h2 id="📌-배경">📌 배경</h2>
<p>프로젝트의 기준상 입고(GR)는 <strong>두 가지 서로 다른 프로세스</strong>에서 발생한다.</p>
<ul>
<li><strong>일반 입고</strong> → <strong>구매오더(PO)</strong> 기반</li>
<li><strong>반품 입고</strong> → <strong>출고문서(GI)</strong> 기반</li>
</ul>
<p>그러나 GR을 PO나 GI에 <strong>FK로 연결하는 구조로는 두 흐름을 하나의 입고 프로세스 안에서 처리할 수 없었다.</strong></p>
<blockquote>
<p>PO와 GI를 GR에 <strong>직접 FK로 물리게 되면,</strong></p>
<p><strong>입고 유형마다 서로 다른 테이블과 관계를 관리해야 해서 구조가 지나치게 복잡</strong>해지고, 테이블 설계부터 손봐야 하는 문제가 있었다.</p>
<p>그래서 <strong>이미 사용 중이던 DONUM (관련문서번호 필드)규칙을 공통 식별자로 재사용해,</strong></p>
<p>두 프로세스를 하나의 GR 구조 안에서 통합하는 것을 설계 기준으로 삼았다.</p>
</blockquote>
<p>현업에서는 동일 거래처의 여러 PO를 한 번에 입고 처리하는 경우가 많았기 때문에,</p>
<p>이를 반영하여 <strong>여러 PO의 Item을 하나의 GR 문서에서 참조할 수 있는 구조로 목표로 하였다.</strong></p>
<p>또한, 하나의 PO에 하나의 GR이 대응되는 방식에서 발생하던</p>
<ul>
<li>반복 입력</li>
<li>상태 누락</li>
<li>문서 불일치</li>
<li>회계 반영 지연</li>
</ul>
<p>과 같은 문제를 개선하고자 하였다.</p>
<p>➡ 이에 따라<strong>, 정상입고와 반품입고를 하나의 처리 구조 안에서 통합 관리하면서,</strong></p>
<p><strong>여러 PO의 Item을 하나의 GR 문서에서 참조 및 집계할 수 있는 방식</strong>으로 설계하였다.</p>
<hr>
<h2 id="📌-donum-기반-문서-매핑-구조-core-logic">📌 DONUM 기반 문서 매핑 구조 (Core Logic)</h2>
<p>입고 문서의 <strong>Origin(생성 근거)</strong> 을 추적하기 위해</p>
<p>GR Item에 <strong>관련문서번호(DONUM)</strong> 를 공통 기준으로 설계했다.</p>
<aside>
💡

<p><strong>DONUM 규칙</strong></p>
<ul>
<li><strong>1로 시작</strong> → PO 기반 일반 입고</li>
<li><strong>4로 시작</strong> → GI 기반 반품 입고</li>
</ul>
</aside>

<p>이 규칙을 기반으로 시스템은 GR 생성 시 자동으로 문서 타입을 판별한다.</p>
<p>➡ <strong>정상입고/반품입고 흐름을 하나의 GR 구조에서 통합할 수 있는 구조적 기반 확보</strong></p>
<p>➡ GR을 특정 문서에 FK로 연결하지 않아도 문서 흐름을 정확히 추적 가능</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/ed98a181-863a-46f0-a923-6b64a43ad75d/image.png" alt=""></p>
<p>입고 라인아이템 테이블 - DONUM, DLNUM</p>
<hr>
<h2 id="📌-구조-설계-headeritem-구조-통일">📌 구조 설계 (Header–Item 구조 통일)</h2>
<p>PO, GI, GR 모두 <strong>Header–Item 구조</strong>로 통일하여</p>
<p>GR Item이 반드시 PO Item 또는 GI Item과 <strong>1:1로 매핑되도록</strong> 설계했다.</p>
<p>이를 통해</p>
<ul>
<li>한 GR 아래에 여러 PO/GI 문서를 병합해도 Item 단위 추적이 정확</li>
<li>향후 FI·재고 업데이트에서도 오류 없이 연계 가능</li>
<li>문서 흐름이 구조적으로 단순하고 관리가 쉬움</li>
</ul>
<p>등 입고 처리의 효율성과 문서 간 일관성을 확보했다.</p>
<hr>
<h2 id="📌-다건-po-→-gr-병합-처리-구조-핵심-설계">📌 다건 PO → GR 병합 처리 구조 (핵심 설계)</h2>
<p>같은 거래처이면</p>
<p>사용자가 선택한 여러 PO의 Item을 모두 수집하여 <strong>GR Header 1건</strong> 아래에 배치한다.</p>
<ul>
<li>PO 개수가 1개든 10개든 GR은 “1건” 생성</li>
<li>GR Item은 PO Item 수만큼 자동 생성</li>
<li>DONUM으로 원본 문서와 정확히 연결</li>
</ul>
<p>➡ <strong>입고 처리 속도 대폭 증가, 누락/중복 방지, 문서 수 감소</strong></p>
<blockquote>
<p>‘‘PO 한 건 → GR 한 건’ 구조에서는 동일 트럭에 실려 온 물량이라도 PO 개수만큼 나누어 반복 입력해야 했다. 이 과정에서 누락 및 중복 입력이나 상태 오류가 발생할 가능성이 높았다.</p>
<p>이에 따라, 기술 구조 역시 실제 업무 단위를 그대로 반영하여 여러 PO를 하나의 GR로 병합 처리할 수 있는 방향을 설계 기준으로 삼았다.</p>
</blockquote>
<hr>
<h2 id="📌-gr-생성-후-po-상태-자동-변경">📌 GR 생성 후 PO 상태 자동 변경</h2>
<p>PO 상태를 담당자가 수기로 관리하는 경우,</p>
<ul>
<li>부분 입고를 ‘완료’로 표시하거나</li>
<li>실제로는 일부만 입고되었음에도 상태가 갱신되지 않는 등</li>
</ul>
<p>부서별 해석 차이와 관리 기준 불일치로 인한 혼선이 발생할 가능성이 있었다.</p>
<p>이에 따라, <strong>PO 상태를 사람이 판단하는 것이 아니라 실제 입고 이력이 자동으로 정의하도록 설계하였다.</strong></p>
<p>GR 생성 시 각 PO Item의 입고 수량을 분석하여,</p>
<ul>
<li>모든 Item이 미입고일 경우 → <strong>A: 생성</strong></li>
<li>일부만 입고된 경우 → <strong>B: 부분입고</strong></li>
<li>모든 Item이 입고 완료된 경우 → <strong>C: 입고완료</strong></li>
</ul>
<p>로 PO header의 상태를 자동 판정하도록 로직을 구현하였다.</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/f2b91b7f-1e0d-4306-9ae1-c9c55dd2a0fe/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/4d977563-717a-4d5f-a0f0-f4c3e85ee091/image.png" alt=""></p>
<h3 id="✔-효과">✔ 효과</h3>
<ul>
<li>PO 진행 상황이 항상 “현재 기준”으로 정확히 유지됨</li>
<li>다건 병합입고에서도 PO 상태가 누락 없이 업데이트됨</li>
<li>구매·검수·회계팀이 PO 상태 판단을 수작업으로 할 필요가 사라짐</li>
</ul>
<p>➡ 이를 통해, <strong>PO 관리의 정합성을 운영 단계에서 안정적으로 확보하였다</strong></p>
<hr>
<h2 id="📌-gr-생성과-동시에-fi-문서-자동-생성">📌 GR 생성과 동시에 FI 문서 자동 생성</h2>
<p>입고 이후 회계(FI)를 별도로 생성하는 방식은 </p>
<p>문서 매칭 오류와 회계 반영 지연을 유발할 수 있었다.</p>
<p>이에 따라, 입고 시점(GR)을 회계 처리의 기준 시점으로 정의하고, </p>
<p>GR 생성과 동시에 FI 문서를 자동 생성하도록 설계하였다.</p>
<p>(※ Invoice 프로세스는 별도 흐름으로 운영되었으며, GR–FI 기준의 회계 반영 구조에 집중하였다.)</p>
<p>GR Header 정보를 기반으로 FI 문서를 생성하고,</p>
<p>GR–FI 간 참조 관계를 자동 설정함으로써 문서 연결이 즉시 확정되도록 구현하였다.</p>
<h3 id="✔-효과-1">✔ 효과</h3>
<ul>
<li>입고와 회계가 실시간으로 동기화</li>
<li>회계팀의 후처리 작업 대폭 감소</li>
<li>문서 불일치 및 매칭 오류 제거</li>
<li>감사/정산 단계에서도 문서 추적이 명확해짐</li>
</ul>
<p>➡ <strong>입고(GR) → 회계(FI) 프로세스가 한 번에 자동 완결되는 구조 확보</strong></p>
<blockquote>
<p>초기 운영 설계에서 별도의 Invoice 프로세스를 태우지 않기로 하면서, 실제 비용 인식의 기준 시점을 어디로 둘지에 대한 합의가 필요했다.</p>
</blockquote>
<p>논의 결과,</p>
<blockquote>
<p>물류 기준으로 재화가 실제 창고에 입고되는 시점(GR)을 재고 및 원가 등 회계 처리의 기준 시점으로 삼는 것이 가장 직관적이라고 판단해, GR 생성과 동시에 FI 문서를 함께 생성하는 구조를 채택했다. </p>
<p>이를 통해 Invoice 없이도 입고 기준으로 재고와 비용이 일관되게 반영되도록 했다.</p>
</blockquote>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SeSAC] 입고 프로세스 가시성 개선을 위한 통합 리포트 시스템 구축]]></title>
            <link>https://velog.io/@j_wisdom_h/SeSAC-%EC%9E%85%EA%B3%A0-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B0%80%EC%8B%9C%EC%84%B1-%EA%B0%9C%EC%84%A0%EC%9D%84-%EC%9C%84%ED%95%9C-%ED%86%B5%ED%95%A9-%EB%A6%AC%ED%8F%AC%ED%8A%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%EC%B6%95</link>
            <guid>https://velog.io/@j_wisdom_h/SeSAC-%EC%9E%85%EA%B3%A0-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B0%80%EC%8B%9C%EC%84%B1-%EA%B0%9C%EC%84%A0%EC%9D%84-%EC%9C%84%ED%95%9C-%ED%86%B5%ED%95%A9-%EB%A6%AC%ED%8F%AC%ED%8A%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%EC%B6%95</guid>
            <pubDate>Fri, 27 Feb 2026 13:53:15 GMT</pubDate>
            <description><![CDATA[<p><strong>SeSAC 클라우드 애플리케이션 개발자 양성과정 5기 - 우수상</strong></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/e926f6cc-3ba0-488b-be75-9524e7db7844/image.png" alt=""></p>
<h1 id="입고-프로세스-가시성-개선을-위한-통합-리포트-시스템-구축">입고 프로세스 가시성 개선을 위한 통합 리포트 시스템 구축</h1>
<h2 id="📌-배경">📌 배경</h2>
<p>기존 운영 환경에서는 입고(GR)와 관련된 정보가</p>
<ul>
<li><p>PO(구매),</p>
</li>
<li><p>GR(입고),</p>
</li>
<li><p>FI(회계),</p>
</li>
<li><p>INVOICE(송장)</p>
<p>  등 서로 다른 화면에 흩어져 있어 <strong>문서 흐름을 한눈에 추적하기 어렵고</strong>,</p>
<p>  일/주/월 단위 실적을 분석하는 데에도 <strong>수작업이 과도하게 필요했다.</strong></p>
</li>
</ul>
<p>이에 따라</p>
<p><strong>입고 프로세스 전체의 가시성을 개선</strong> 하고,</p>
<p><strong>문서 연결·기간별 분석·보고 자동화</strong> 를 지원하는 통합 리포트 시스템을 구축하였다.</p>
<hr>
<h2 id="📌-핵심-설계-포인트">📌 핵심 설계 포인트</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/7547b0ca-dd87-4b17-8628-d6da3e6f56e0/image.png" alt=""></p>
<h3 id="①-기간·조건-기반의-다차원-조회-구조-설계"><strong>① 기간·조건 기반의 다차원 조회 구조 설계</strong></h3>
<p>현업 분석 패턴을 반영하여 다음 단위 조회 기능을 제공:</p>
<ul>
<li><strong>일별 / 주차별 / 월별 / 분기별 / 연도별 조회</strong></li>
<li>자재, 사이즈, 플랜트, 창고, 담당자 등 상세 조건 필터링</li>
<li>DONUM 기반 문서 타입(PO/GI) 자동 판별</li>
</ul>
<p>기간 조건 선택 시 SAP가 자동으로 날짜 범위를 계산하여 사용자 입력 부담을 줄이고 조회 정확도를 높였다.</p>
<blockquote>
<p>기간 단위를 일/주/월/분기/연도로 나눈 이유는, 
<strong>구매·물류팀</strong>은 <strong>주/월 단위</strong>로, <strong>경영진</strong>은 <strong>분기/연 단위</strong>로 실적을 보는 등 부서별 분석 단위가 서로 달랐기 때문이다. 주요 리포트 패턴을 한 화면에 모두 수용해, 각 팀이 엑셀을 따로 가공하지 않고도 바로 사용할 수 있도록 했다.</p>
</blockquote>
<hr>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/e04528b8-041b-46da-a44a-a80b1e369584/image.png" alt=""></p>
<h3 id="②-문서-흐름pogrfi을-한-화면에서-연결하는-traceability-확보"><strong>② 문서 흐름(PO–GR–FI)을 한 화면에서 연결하는 Traceability 확보</strong></h3>
<p>입고문서(GR)를 중심으로 다음 문서를 자동 연계:</p>
<ul>
<li>GR → PO</li>
<li>GR → FI (회계문서)</li>
<li>GR → Invoice</li>
<li>(반품 시) GR → GI</li>
</ul>
<p><strong>원본 구매 단계부터 회계 처리까지의 전체 프로세스를 단일 화면에서 확인</strong>할 수 있게 되었다.</p>
<p>이는 기존에 화면을 여러 번 오가며 문서를 찾던 비효율을 제거한 개선이다.</p>
<blockquote>
<p>기존에는 PO·GR·FI·Invoice가 서로 다른 트랜잭션에 흩어져 있어, 문제 발생 시 <strong>‘어디에서 끊겼는지’를 찾는 데만 시간이 많이 걸렸다.</strong> 그래서 GR을 중심축으로 삼고 전후 문서를 한 번에 보여주면, 구매·물류·회계가 같은 화면을 기준으로 소통할 수 있다고 판단했다.</p>
</blockquote>
<p>특히 클레임이나 정산 이슈가 발생했을 때, 한 화면에서 흐름이 끊긴 지점을 바로 찾을 수 있어 <strong>원인 파악과 책임 소재 확인에 드는 시간을 크게 줄이는 것</strong>을 목표로 했다.</p>
<hr>
<h3 id="③-핫스팟hotspot-기반-트랜잭션-자동-이동"><strong>③ 핫스팟(Hotspot) 기반 트랜잭션 자동 이동</strong></h3>
<p>ALV에서 문서번호(PO, FI, GI, IVNUM 등)를 클릭하면</p>
<p>해당 문서의 상세 화면 또는 별도 트랜잭션으로 즉시 이동하도록 구현하였다.</p>
<ul>
<li>클릭 → 자동 SET/GET PARAMETER ID → CALL TRANSACTION</li>
<li>문서 추적 시간이 대폭 감소</li>
<li>구매/재고/회계팀의 협업 속도 상승</li>
</ul>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/d5dfd1ac-cb36-437d-ad88-e7e7df51b110/image.png" alt=""></p>
<p>➡ <strong>입고 리포트가 프로세스 네비게이션 역할</strong>까지 수행하도록 확장했다.</p>
<blockquote>
<p>문서번호를 복사해 다른 트랜잭션에 붙여넣는 기존 방식은 단순히 번거로운 수준을 넘어서, <strong>번호 오입력이나 잘못된 문서 조회로 이어지는 실제 장애 원인</strong>이 되기도 했다. 그래서 <strong>자주 조회하는 문서들은 클릭 한 번으로 이동하도록 해, 사용자의 수작업을 최대한 없애는 방향</strong>을 택했다.</p>
<p>이렇게 입고 리포트에서 바로 각 모듈 트랜잭션으로 네비게이션하게 함으로써, 보고서가 단순 조회용을 넘어 <strong>실제 업무 처리의 허브 역할</strong>을 하도록 설계했다.</p>
</blockquote>
<hr>
<h3 id="④-function-group-기반-상세-팝업-조회"><strong>④ Function Group 기반 상세 팝업 조회</strong></h3>
<p>자재, 창고, 담당자 등 특정 필드 클릭 시 즉시 세부 정보를 확인할 수 있도록</p>
<p>재사용 가능한 Function Group 기반 팝업을 구현하였다.</p>
<ul>
<li>품번 클릭 → 자재 정보 팝업</li>
<li>플랜트/창고 → 기본정보 팝업</li>
<li>GR Item → 라인아이템 상세 팝업</li>
</ul>
<p>팝업이 여러 프로그램에서 호출되도록 설계해 <strong>유지보수성과 확장성</strong>을 높였다.</p>
<blockquote>
<p>자재, 창고, 담당자 정보를 조회하는 팝업을 Function Group 하나로 통합한 이유는, <strong>입고 리포트뿐 아니라 추후 다른 리포트·트랜잭션에서도 동일한 UI와 로직을 그대로 재사용할 수 있게 하기 위함</strong>이었다. 
결과적으로 화면마다 유사한 조회 로직을 따로따로 만들지 않고, 한 곳만 수정해도 전 시스템에 반영되도록 유지보수 비용을 줄였다.</p>
</blockquote>
<hr>
<h3 id="⑤-ole2-기반-엑셀-다운로드-자동화"><strong>⑤ OLE2 기반 엑셀 다운로드 자동화</strong></h3>
<p>조회된 리포트를 <strong>원클릭으로 보고서 형태의 Excel로 변환</strong>하도록 구현하였다.</p>
<ul>
<li>컬럼 헤더 자동 매핑</li>
<li>기간 기반 파일명·시트명 자동 생성
<img src="https://velog.velcdn.com/images/j_wisdom_h/post/0e170cad-6565-4e28-8d17-cb40ad10a063/image.gif" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[GATEWAY 설정 ( SAP GUI )]]></title>
            <link>https://velog.io/@j_wisdom_h/GATEWAY-%EC%84%A4%EC%A0%95-SAP-GUI</link>
            <guid>https://velog.io/@j_wisdom_h/GATEWAY-%EC%84%A4%EC%A0%95-SAP-GUI</guid>
            <pubDate>Thu, 26 Feb 2026 07:50:52 GMT</pubDate>
            <description><![CDATA[<h2 id="gateway-생성">GATEWAY 생성</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/a8d6d166-f7f2-483b-92c7-6d3c167a7ae5/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/1a27436e-719a-482e-b50e-a389af2c6054/image.png" alt=""></p>
<h2 id="gateway---datamodel">GATEWAY - DATAMODEL</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/270f7804-125d-4c7a-aace-72e32d1bd3ca/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/4239f375-9a0b-4e1c-85fc-de07975bd6ea/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/ae2ed0b2-4a68-4916-8b56-fce374d0c7bf/image.png" alt=""></p>
<h2 id="gateway---entrytypes">GATEWAY - ENTRYTYPES</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/d63640ac-e73d-4587-ad31-eb765834515c/image.png" alt=""></p>
<h2 id="gateway---entry-set">GATEWAY - ENTRY SET</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/7aa55ec4-a6ef-48bf-b1be-f5a041f61e5c/image.png" alt=""></p>
<h2 id="gateway---service-maintance-확인">GATEWAY - SERVICE MAINTANCE 확인</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/6f0e829b-d4c8-4b8b-9bf2-f295479c5dfc/image.png" alt=""></p>
<h2 id="service-definition-생성">SERVICE DEFINITION 생성</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/3ac9f8a0-5d9d-49bf-abf6-1aeb6eab6e16/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/ceacc2f1-7ba5-4871-a755-8dc8bd875df2/image.png" alt=""></p>
<h2 id="service-생성추가">SERVICE 생성(추가)</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/e9d3ce8b-6812-46ec-8404-63734d7a3df7/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/ff08f13e-d6d6-43c7-950b-4d6f0f79cda5/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/5460b89b-27f4-4cf3-a744-c97e63c33d9d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/2759cad4-5a8c-4f30-af96-25d6f9922a9f/image.png" alt=""></p>
<h2 id="gateway-client-연결확인">GATEWAY CLIENT 연결확인</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/e9025a91-9731-4f97-8639-d557eb4405e4/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/1093e133-d163-41c8-a90d-56a719d23796/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/c5a85974-778f-4aa4-a6b1-abd6a1727620/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SAP Memory와 ABAP Memory - ABAP Memory실습]]></title>
            <link>https://velog.io/@j_wisdom_h/SAP-Memory%EC%99%80-ABAP-Memory-ABAP-Memory%EC%8B%A4%EC%8A%B5-nthwr1ik</link>
            <guid>https://velog.io/@j_wisdom_h/SAP-Memory%EC%99%80-ABAP-Memory-ABAP-Memory%EC%8B%A4%EC%8A%B5-nthwr1ik</guid>
            <pubDate>Thu, 26 Feb 2026 07:40:59 GMT</pubDate>
            <description><![CDATA[<p><strong>SAP Memory와 ABAP Memory - ABAP Memory실습</strong></p>
<h2 id="abap-memory-특징">ABAP Memory 특징</h2>
<ul>
<li><p>SAP MEMORY는 파라미터 ID를 생성해야하는 반면
ABAP MEMORY를 사용하는 경우는 메모리 ID이름을 _<strong>프로그램 수준</strong>_에서 임의로 지정가능하다.</p>
</li>
<li><p>ABAP MEMORY로 데이터를 넘겨주기 위해,
EXPORT/IMPORT구문을 사용하고 _<strong>동일한 파라미터 ID</strong>_이름을 사용해야한다.</p>
</li>
<li><p>다른 프로그램에 <strong>데이터오브젝트(필드, 스트럭처, 인터널 테이블)</strong>를 넘겨주기 자주 사용한다.</p>
</li>
<li><p>다른 프로그램을 호출할 때 명령어(CALL TRANSACTION, SUBMIT)을 사용하면 내부세션이 생성되고 ABAP MEMORY에 잇는 데이터가 공유된다.</p>
</li>
<li><p>같은 윈도우(동일 내부세션)에서만 메모리 영역을 공유하므로 *<em>새창을 띄워서 작업할 경우 메모리 영역이 사라진다. *</em></p>
</li>
</ul>
<h2 id="abap-memory--사용법">ABAP Memory  사용법</h2>
<h3 id="importexport">IMPORT/EXPORT</h3>
<pre><code>EXPORT OBJ1 …OBJN TO MEMORY ID KEY.

IMPORT OBJ… BOJN FROM MEMORY ID KEY.</code></pre><p><strong>Export 구문</strong>을 이용해서 <strong>데이터 오브젝트(필드, 스트럭처, 인터널 테이블)</strong>를 <strong>ABAP Memory</strong>에 로드할 수 있다.</p>
<h3 id="abap-memory삭제">ABAP MEMORY삭제</h3>
<pre><code>FREE MEMORY [ID KEY]</code></pre><p>ID KEY를 넣지 않으면 전체 메모리가 삭제되므로 주의가 필요하다!</p>
<h2 id="실습1">실습1</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/67a08067-795f-47c5-b20a-ae399d7db9ec/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/5ba5e716-6980-4ba6-bd81-47380aa19e06/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/c9723639-cfd7-42e8-8867-d4e5c4668e7c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/a0be257a-15c9-4a83-b0e4-5afa352a500f/image.png" alt=""></p>
<p>z11_05에서 EXPORT로 인터널테이블 gt_emplist가 ABAP Memory의 KEY &#39;TEST_ID&#39;로 저장됨.</p>
<p>z11_06에서 IMPORT로 KEY &#39;TEST_ID&#39;로 찾아와서 GT_EMPLIST값 가져옴.</p>
<h2 id="트랜잭션-또는-실행가능한-프로그램을-호출할-때-발생하는-상황-2가지">트랜잭션 또는 실행가능한 프로그램을 호출할 때 발생하는 상황 2가지</h2>
<ol>
<li>호출받은 프로그램이 종료되고 되돌아올 때. <strong>호출받은 프로그램이 삭제되지 않고 비활성화된 상태.</strong> 그리고 메모리에 저장된 값들은 스택으로 옮겨진다.(최대9개까지)</li>
</ol>
<p><strong>LEAVE TO PROGRAM.</strong></p>
<ol start="2">
<li><p><strong>호출받은 프로그램이 실행을 종료하고 호출한 프그램으로 복귀 하지 않고,</strong>
호출받은 프로그램은 내부 세션에서 자신이 호출한 프로그램을 대체해 버리고, 호출한 프로그램메모리 값은 삭제한다.</p>
<p>**  LEAVE TO TRANSACTION **
내부세션의 모든 스택을 삭제하고 새로운 내부 세션을 생성하여 트랜잭션을 호출. 그 결과 프로그램간에 데이터를 넘겨주지 못함.</p>
<p>※ SAP MEMORY는 여전히 데이터 전달된다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[SAP Memory와 ABAP Memory - SAP Memory실습]]></title>
            <link>https://velog.io/@j_wisdom_h/SAP-Memory%EC%99%80-ABAP-Memory-SAP-Memory%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@j_wisdom_h/SAP-Memory%EC%99%80-ABAP-Memory-SAP-Memory%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Thu, 26 Feb 2026 04:06:13 GMT</pubDate>
            <description><![CDATA[<h2 id="sap-gui-스탠다드-예시t-code-se11">SAP GUI 스탠다드 예시(T-code: SE11)</h2>
<p>SeSAC의 과정에서 배웠던 SAP Memory는 바로 모든 세션에서 
쓸 수 있는 메모리라는 것이다.</p>
<p>예시)
<img src="https://velog.velcdn.com/images/j_wisdom_h/post/3cda3780-47b8-4037-8604-dfa1af39eeae/image.png" alt=""></p>
<p>이렇게 T-code: SE11에서 SCARR을 입력하면</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/ad12bd2a-eb2f-4ff3-818d-fee1f224f76d/image.png" alt=""></p>
<p>다른 창을 열어서도 SCARR가 보인다.</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/d3751319-6385-4a8c-9f88-8ec424487304/image.png" alt=""></p>
<p>F1 &gt; Technical로 들어가면
이렇게 parameter ID가 있는데, 이걸 KEY로 SAP Memory에 저장된다.</p>
<h2 id="getset-parameter-id-설정하는법">GET/SET Parameter ID 설정하는법</h2>
<h3 id="방법1-t-code--se80">방법1. t-code : se80</h3>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/2e55385f-a827-4211-abf7-0e5574fd8d95/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/fb40744e-d18a-4022-b052-38c868dc0553/image.png" alt=""></p>
<p>SE80에서 </p>
<p>Workbench &gt; Edit Object &gt; Enhanced Options &gt; SET/GET Parameter ID</p>
<p>로 SAP Memory의 KEY를 설정한다. </p>
<h3 id="방법2-t-code--sm30">방법2. t-code : sm30</h3>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/825c8712-d248-4c82-9cf9-fdfca36e3006/image.png" alt=""></p>
<p>TPARA는 SAP 시스템 전체에서 사용하는 SPA/GPA Parameter ID 정의 테이블.</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/5eb24e24-0828-43ae-a562-b4f357945c68/image.png" alt=""></p>
<h3 id="방법3-더블클릭해서-생성하기">방법3. 더블클릭해서 생성하기</h3>
<p>이게 가장 직관적이다. 
SE38이나 SE80에서 코드를 작성하면서 생성한다.</p>
<h2 id="sap-memory할당">SAP Memory할당</h2>
<pre><code> SET PARAMETER ID &lt;pid&gt; FILD &lt;f&gt;
</code></pre><pre><code>필드 &lt;f&gt;를 SPA/GPA파라미터 &lt;pid&gt;에 저장
&lt;pid&gt; : 최재 20자. 값이 있다면 덮어쓴다.</code></pre><h2 id="sap-memory읽기">SAP Memory읽기</h2>
<pre><code> GET PARAMETER ID &lt;pid&gt; FILD &lt;f&gt;
</code></pre><pre><code>SAP Memory의 &lt;pid&gt;에 있는 값을 필드&lt;f&gt;에 저장한다.
만약 &lt;pid&gt;가 메모리 ID에 존재하지 않는다면, sy-subrc = 4.</code></pre><h2 id="실습-1">실습 1</h2>
<p>파라미터 또는 Select-option변수에 &#39;Memory ID&#39;를 사용해 필드와 파라미터를 연결시킨다.</p>
<pre><code>PARAMETERS P_1 TYPE C MEMORY ID &#39;PID&#39;</code></pre><p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/9cbff5a4-dd5c-4c4b-8454-ce43531197a7/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/d70e1024-b62d-4c0a-9075-41852ed94ecc/image.png" alt=""></p>
<p>레포트 프로그램에서 메모리 할당후 값을 엔터하면
ZMY에 SET A가 된다.</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/52290e9c-239e-47dc-a535-d0ff8e4c9746/image.png" alt=""></p>
<p>모듈풀 프로그램의 스크린페인터에서 </p>
<p>PARAMTER ID와 
GET/SET PAMRMETER 체크를 해주면</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/c3c7a27d-c9e8-446d-9b6a-bf2656a77cbf/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/9c8d7209-561d-4215-8070-30c283e31d56/image.png" alt="">
새창을 열어서 모듈풀 프로그램 실행하면
리포트프로그램에서 입력했던 값이 할당된 것을 확인할 수 있다. (GET ZMY)</p>
<h2 id="실습-2">실습 2</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/d6b4bd38-00a5-432e-8d8b-aa376fc12031/image.png" alt=""></p>
<ol>
<li><p>ZENM, ZENO를 더블클릭해서 Create Paramters해서 메모리할당</p>
</li>
<li><p>사용자가 더블클릭하면(BY AT LINE-SELECTION),
파라미터 값 저장되고, Z11_03트랜잭션 실행된다.</p>
</li>
</ol>
<p>※ AND SIP FIRST SCREEN: 첫화면을 건너뛰고(SKIP) 실행하라는 명령어.
※ HIDE: 필드의 값을 HIDE메모리 영역으로 저장하는 역할</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/d9c9a5ff-6d6f-4639-b114-4c7858a11f84/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/3f1e1c47-fa56-4c29-ae0a-731026e0f99e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/8973d411-e843-4fc3-baa7-38caa16530f8/image.png" alt=""></p>
<p>Z11_02에서 더블클릭하면 이벤트를 타게 되고,
파라미터 값이 SET되고 
Z11_03프로그램으로 넘어간다. (새로운 내부세션이 생성됨)
SAP Memory에서 GET PARAMTER ID로 동일한 값이 출력된다.</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/c1de386b-d479-4145-bbbd-5809f9721aa5/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/c048afc5-ab81-48f9-a033-c01f2cd186fc/image.png" alt=""></p>
<h2 id="생각해보기">생각해보기</h2>
<p>SAP MEMORY를 어디서 활용할 수 있을까?
초기설정값에 활용할 수 있다.</p>
<p>예를 들어 회사코드 1000, 플랜트1010에 대한 조회 권한만 가지고 있다면
T-CODE : SU03또는 SU01에서 
USER PROFILE의 PARAMETERS탭을 통해 값을 미리 설정할 수 있다.</p>
<p>이경우에 사용자가 로인하여 트랜잭션을 조회할때
회사코드, 플랜트 필드가 자도응로 값이 입력되어 효율적으로 사용할 수 있을 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SAP Fiori 환경설정 ]]></title>
            <link>https://velog.io/@j_wisdom_h/Cloud-Connector%EC%97%B0%EA%B2%B0</link>
            <guid>https://velog.io/@j_wisdom_h/Cloud-Connector%EC%97%B0%EA%B2%B0</guid>
            <pubDate>Wed, 25 Feb 2026 11:00:12 GMT</pubDate>
            <description><![CDATA[<h2 id="다운로드">다운로드</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/057de5c1-01bb-4413-9e8b-b8bcd3606215/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/4719fa35-e4b2-4f12-8b9b-bf2aabcfc9c9/image.png" alt=""></p>
<p>JDK 다운로드한 후 (환경변수 설정 필요),
Cloud Connector 다운로드</p>
<h2 id="cloud-connector-접속">Cloud Connector 접속</h2>
<p><a href="https://localhost:8443">https://localhost:8443</a>
(colud connector 다운로드시 포트번호를 8443으로 설정해둠)</p>
<blockquote>
</blockquote>
<p>초기 로그인 
UserName : Administrator
Password: manage</p>
<h2 id="subaccount-추가">Subaccount 추가</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/d94cb814-e221-4ad0-a8ec-dd56a59a2fd1/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/61bfee47-3d17-4278-9a9e-d639ec008567/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/1b7069b6-471a-4546-8b14-8ab556c2ba54/image.png" alt=""></p>
<h2 id="cloud-to-on-premise---system-mapping">Cloud to On-Premise - System Mapping</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/391fd8a2-ab86-445d-b5bb-78b637ea393a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/196d84c5-f225-4be4-b5e8-3fb3f4622fad/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/2e8c793f-bc1e-4f6b-a7be-d8321b8f1ab4/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/fa543daa-7cd9-4aca-94d6-6c691c72e4d6/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/da5a2eba-75ec-45da-9d64-196245096abe/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/53326103-709a-43da-8665-ba76a4efe370/image.png" alt=""></p>
<h2 id="cloud-to-on-premise---add-resource">Cloud to On-Premise - Add resource</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/a6bec339-0314-4fff-be08-8fd05ae6a9be/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/8b38f4e3-eba6-4471-8435-d4419f21fd6a/image.png" alt=""></p>
<h2 id="destination-설정">Destination 설정</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/46df5bb3-0dea-4825-910b-4f70150e7770/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/af04e643-976d-416c-b0fa-215f7659dc56/image.png" alt=""></p>
<h2 id="권한설정">권한설정</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/1df5b18b-1cf1-4d43-ba1e-3f1ae10b9219/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/207e7677-5b30-4a6b-8675-f0c4cb83b7f0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/f53fc806-1677-459f-b54b-5e950c0c1ed9/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/7937f07f-b6b1-4e29-bb62-9b14d1cc05c9/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/55365f5f-6418-46df-a527-9478e036189e/image.png" alt="">
<img src="https://velog.velcdn.com/images/j_wisdom_h/post/decc146a-4e71-493d-900a-569d051b4bca/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SAP Memory와 ABAP Memory - Overview]]></title>
            <link>https://velog.io/@j_wisdom_h/SAP-Memory%EC%99%80-ABAP-Memory-Overview</link>
            <guid>https://velog.io/@j_wisdom_h/SAP-Memory%EC%99%80-ABAP-Memory-Overview</guid>
            <pubDate>Wed, 25 Feb 2026 06:25:36 GMT</pubDate>
            <description><![CDATA[<p><strong>SAP Memory와 ABAP Memory</strong></p>
<h2 id="overview">Overview</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/33c1c8a7-1abc-4f3f-97c0-a4c67e110f33/image.svg" alt=""></p>
<p>SAP GUI에 로그인하면 하나의 사용자 터미널 세션이 생성된다.
이 터미널 세션에는 최대 6개의 GUI윈도우를 실행할 수 있다.</p>
<p>이때 6개의 윈도우를 _<strong>외부세션(External Session)</strong>_이라고 한다.</p>
<p>하나의 외부세션은 최대 20개의 _<strong>내부세션(Internal Session)</strong>_을 가질 수 있다.</p>
<p>내부세션은 하나의 외부세션 내에서 실행중인 프로그램이 다른 프로그램을 호출할때 성되는 세션이다.</p>
<ul>
<li><p>사용자 터미널 세션: SAP에 로그인하면 생기는 전체 작업 공간이야.</p>
</li>
<li><p><strong>외부 세션</strong>: 이 작업 공간 안에서 열 수 있는 최대 6개(또는 S/4 HANA의 경우 16개)의 창들 . 각 창은 독립적인 것처럼 보이지만, 사실 메모리를 공유하고 있다 .</p>
</li>
<li><p><strong>내부 세션</strong>: 하나의 외부 세션 안에서 프로그램이 다른 프로그램을 부를 때 생기는 작은 작업 공간. 최대 20개까지 만들 수 있다.</p>
</li>
</ul>
<br />

<h3 id="abap-memory와-sap-memory차이점">ABAP Memory와 SAP Memory차이점</h3>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/651bf578-2ec2-47b4-9697-b51583fc6d16/image.svg" alt=""></p>
<blockquote>
<p>ABAP 메모리:** 동일한 외부 세션 안에서 실행되는 프로그램들끼리 데이터를 공유하는 메모리. **</p>
</blockquote>
<p>각각의 외부세션에 하나만 존재한다. 
각각의 프로그램은 자신의 내부세션을 가지고, 내부세션은 스택에 쌓인다.
데이터를 주고받을 때는 <strong>EXPORT/IMPORT</strong> 구문을 사용한다. </p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/ed7ef637-4c5c-45a6-8d8f-8e9fbcec6db3/image.svg" alt=""></p>
<blockquote>
<p>SAP 메모리: <strong>사용자 터미널 세션 내의 모든 외부 세션에서 접근할 수 있는 Global Memory</strong></p>
</blockquote>
<ul>
<li>SAP에 로그인해서 띄운 모든 창에서 이 메모리에 접근할 수 있다 . </li>
<li>데이터를 주고받을 때는 SET PARAMETER / GET PARAMETER 구문을 사용한다.</li>
</ul>
<h3 id="정리">정리</h3>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/4e1788e2-e69e-4fb9-a77a-bbeb9643b8b3/image.png" alt=""></p>
<ul>
<li><p>User Terminal Session (최상위): 사용자가 SAP GUI에 로그인할 때 생성되는 가장 큰 단위. </p>
</li>
<li><p>External Session: 사용자가 새 창을 열 때 생성되는 메인 세션입니다. </p>
</li>
<li><p>Internal Session: 하나의 외부 세션 내에서 프로그램이 실행되거나 다른 프로그램을 호출할 때 생성되는 세션.</p>
</li>
<li><p>ABAP Memory: 동일한 외부 세션 내에 있는 내부 세션들끼리만 데이터를 주고받을 수 있는 메모리 .  External Session 레벨에 존재.</p>
</li>
<li><p>SAP Memory : 모든 외부 세션이 공통으로 접근할 수 있다. User Terminal Session 레벨에 존재.</p>
</li>
</ul>
<h2 id="프로그램-간의-데이터를-넘겨주는-법">프로그램 간의 데이터를 넘겨주는 법</h2>
<h3 id="1-interface">1. Interface</h3>
<p>TYPE-1(리포트 프로그램 프로그램을 호출할때, </p>
<p>Submit 구문을 이용해 Selection screen의 Input필드에 값을 입력하거나,</p>
<pre><code class="language-abap">Parameters (단일 값): WITH [필드명] = [값]
Select-options (범위 값): WITH [필드명] IN [Range 인터널 테이블]

DATA: lr_date TYPE RANGE OF sy-datum,
      ls_date LIKE LINE OF lr_date.

&quot; Select-option을 위한 Range 데이터 준비
ls_date-sign   = &#39;I&#39;.
ls_date-option = &#39;BT&#39;.
ls_date-low    = &#39;20240101&#39;.
ls_date-high   = &#39;20241231&#39;.
APPEND ls_date TO lr_date.

SUBMIT z_target_report
  WITH p_user = sy-uname          &quot; Parameter에 값 전달
  WITH s_date IN lr_date          &quot; Select-option에 Range 테이블 전달
  VIA SELECTION-SCREEN            &quot; (선택) 화면을 보여줄지 여부
  AND RETURN.                     &quot; 실행 후 다시 현재 프로그램으로 복귀</code></pre>
<p>Selection screen의 Variant를 호출하는 방법
(복잡한 입력 조건을 매번 넣기 힘들 때, 백그라운드 작업(Batch Job)을 돌릴 때 등 )</p>
<pre><code>&quot; 예시: &#39;MONTHLY_BATCH&#39;라는 이름의 Variant를 사용하여 호출
SUBMIT z_target_report
  USING SELECTION-SET &#39;MONTHLY_BATCH&#39;
  AND RETURN.</code></pre><h3 id="2-abap-memory">2. ABAP Memory</h3>
<p>ABAP Memory를 이용하여 EXPROT, IMPORT 구문을 사용한다. 동일한 파라미터 명을 사용한다.</p>
<ul>
<li>핵심 구문: EXPORT ... TO MEMORY ID / IMPORT ... FROM MEMORY ID</li>
<li>특징: 동일한 ID를 사용해야만 데이터를 찾을 수 있어요.</li>
</ul>
<pre><code>&quot; [프로그램 A] 데이터를 메모리에 저장 (보내기)
DATA: gv_text TYPE string VALUE &#39;j_wisdom_h&#39;.

EXPORT p1 = gv_text TO MEMORY ID &#39;ZMY_NICKNAME&#39;. 
&quot; &#39;ZMY_NICKNAME&#39;이라는 메모리id에 
&quot; gv_text를
&quot;&#39;p1&#39;이라는 이름으로 데이터를 넣음

&quot; --------------------------------------------------

&quot; [프로그램 B] 메모리에서 데이터를 읽기 (받기)
DATA: gv_received TYPE string.

IMPORT p1 = gv_received FROM MEMORY ID &#39;ZMY_MEM&#39;.
&quot; &#39;ZMY_NICKNAME&#39; 메모리id에서 
&quot; &#39;p1&#39;이라는 이름을 찾아 데이터를 꺼냄</code></pre><h3 id="3-sap-memory">3. SAP Memory</h3>
<p>SAP Memory영역의 파라미너 ID를 이용해 데이터를 넘겨준다.</p>
<ul>
<li>핵심 구문: SET PARAMETER ID / GET PARAMETER ID<pre><code>&quot; [창 1] 사용자가 입력한 회사 코드를 글로벌하게 저장
SET PARAMETER ID &#39;BUK&#39; FIELD &#39;1000&#39;. 
&quot; &#39;BUK&#39;라는 공용 게시판 위치에 &#39;1000&#39;이라는 값을 딱! 붙여둠
</code></pre></li>
</ul>
<p>&quot; --------------------------------------------------</p>
<p>&quot; [창 2] 다른 프로그램에서 그 값을 자동으로 가져옴
DATA: gv_bukrs TYPE bukrs.</p>
<p>GET PARAMETER ID &#39;BUK&#39; FIELD gv_bukrs.
&quot; 공용 게시판 &#39;BUK&#39; 위치에 있는 값을 읽어와서 변수에 담음</p>
<pre><code>

### 4. Database Table
가장 일반적인 방법. 
프로그램 A가 데이터를 테이블에 저장하고,
프로그램 B가 테이블에서 데이터를 조회

### 5. Presentation Server
SAP GUI가 실행 중인 로컬PC에 데이터를 파일로 다운로드했다가 업로드하는 방법 (GUI_DOWNLOAD, GUI_UPLOAD)

</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[SAP BTP] CAPM 가이드: 정의, 특징, 그리고 자동화]]></title>
            <link>https://velog.io/@j_wisdom_h/SAP-BTP-CAPM-%EA%B0%80%EC%9D%B4%EB%93%9C-%EC%A0%95%EC%9D%98-%ED%8A%B9%EC%A7%95-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%9E%90%EB%8F%99%ED%99%94%EC%9D%98-%EB%A7%88%EB%B2%95</link>
            <guid>https://velog.io/@j_wisdom_h/SAP-BTP-CAPM-%EA%B0%80%EC%9D%B4%EB%93%9C-%EC%A0%95%EC%9D%98-%ED%8A%B9%EC%A7%95-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%9E%90%EB%8F%99%ED%99%94%EC%9D%98-%EB%A7%88%EB%B2%95</guid>
            <pubDate>Tue, 24 Feb 2026 07:59:09 GMT</pubDate>
            <description><![CDATA[<p><strong>SAP BTP(Business Technology Platform)</strong>의 핵심 구성 요소 중 하나인 CAPM(SAP Cloud Application Programming Model)은 개발자들이 비즈니스 애플리케이션을 효율적으로 구축할 수 있도록 돕는 강력한 프레임워크입니다. </p>
<p>CAPM의 정의부터 주요 특징, 그리고 개발 과정을 혁신하는 자동화까지 자세히 살펴보겠습니다.</p>
<h2 id="1-capm이란-무엇인가요">1. CAPM이란 무엇인가요?</h2>
<p>CAPM은 <strong>SAP Cloud Application Programming Model</strong>의 약자로, 
개발자가 복잡한 인프라나 기술적인 세부 사항에 얽매이지 않고 비즈니스 로직 구현에 집중할 수 있도록 설계된 프레임워크입니다. </p>
<h2 id="2-capm의-주요-특징">2. CAPM의 주요 특징</h2>
<p>CAPM은 개발 생산성을 극대화하고 유연한 애플리케이션 개발을 지원하는 여러 가지 특징을 가지고 있습니다.</p>
<h3 id="21-cdscore-data-services-중심-모델링">2.1. CDS(Core Data Services) 중심 모델링</h3>
<p>CAPM은 <strong>CDS(Core Data Services)</strong>를 사용하여 <strong>데이터 모델과 서비스 정의를 통합</strong>합니다. </p>
<p>CDS는 데이터베이스 스키마, 서비스 인터페이스, 비즈니스 로직을 하나의 추상화된 모델로 정의할 수 있게 해주며, 이는 개발의 일관성과 효율성을 높이는 핵심 요소입니다.</p>
<h3 id="22-언어-유연성">2.2. 언어 유연성</h3>
<p>CAPM은 개발자들이 선호하는 프로그래밍 언어를 선택할 수 있도록 지원합니다.</p>
<ul>
<li><p>Node.js: JavaScript/TypeScript 기반의 백엔드 개발에 익숙한 개발자에게 적합합니다.</p>
</li>
<li><p>Java: 안정적이고 성능이 뛰어난 엔터프라이즈 애플리케이션 개발에 강점을 가집니다.</p>
</li>
</ul>
<h3 id="23-데이터베이스-독립성">2.3. 데이터베이스 독립성</h3>
<p>CAPM은 특정 데이터베이스에 종속되지 않습니다. 개발자는 프로젝트 요구사항에 따라 다양한 데이터베이스를 유연하게 선택하고 변경할 수 있습니다.</p>
<ul>
<li><p>SAP HANA: SAP 환경에 최적화된 인메모리 데이터베이스입니다.</p>
</li>
<li><p>SQLite: 개발 및 테스트 단계에서 가볍게 사용할 수 있는 파일 기반 데이터베이스입니다.</p>
</li>
</ul>
<p>그 외 다양한 관계형 데이터베이스를 지원합니다.</p>
<h2 id="3-capm의-자동화-마법">3. CAPM의 &#39;자동화 마법&#39;</h2>
<p>CAPM의 가장 강력한 장점은 개발자가 반복적이고 번거로운 작업을 직접 처리할 필요 없이, 프레임워크가 자동으로 많은 부분을 처리해준다는 점입니다.</p>
<h3 id="31-odata-api-자동-생성">3.1. OData API 자동 생성</h3>
<p>CDS 모델을 정의하면 CAPM은 해당 모델을 기반으로 <strong>OData(Open Data Protocol) API를 자동으로 생성</strong>합니다. </p>
<p>이는 데이터 생성(Create), 조회(Read), 업데이트(Update), 삭제(Delete)와 같은 기본적인 CRUD(Create, Read, Update, Delete) 엔드포인트를 별도의 코딩 없이 바로 사용할 수 있게 해줍니다.</p>
<h3 id="32-db-스키마-자동-배포-및-관리">3.2. DB 스키마 자동 배포 및 관리</h3>
<p>CDS 모델에 따라 <strong>데이터베이스 스키마가 자동으로 생성되고 배포</strong>됩니다. 모델이 변경되면 CAPM은 데이터베이스 스키마를 <strong>자동으로 업데이트</strong>하여 개발자가 수동으로 스키마를 관리하는 부담을 덜어줍니다.</p>
<h3 id="33-내부-통신-자동-연결">3.3. 내부 통신 자동 연결</h3>
<p>CAPM 프로젝트 내에서 여러 서비스 간의 통신은 <strong>별도의 Destination 설정 없이 자동으로 연결</strong>됩니다. 이는 마이크로서비스 아키텍처에서 서비스 간의 상호작용을 매우 간편하게 만들어줍니다.</p>
<h2 id="4-destination-설정-가이드">4. Destination 설정 가이드</h2>
<p>CAPM은 내부 서비스 간 통신을 자동화하지만, 외부 시스템과의 연결에는 Destination 설정이 필요합니다.</p>
<ul>
<li><p>내부 프로젝트 서비스 사용 시: CAPM 프로젝트 내에서 다른 서비스(예: 동일한 CAPM 프로젝트 내의 다른 마이크로서비스)를 호출할 때는 별도의 Destination 설정이 필요 없습니다.</p>
</li>
<li><p>외부 시스템 연결 시: Northwind OData 서비스, SAP S/4HANA 시스템, 또는 기타 외부 API와 같이 CAPM 프로젝트 외부에 있는 시스템에 연결할 때는 Destination 설정이 필수적입니다. 이는 보안 및 연결 정보를 중앙에서 관리하고 안전하게 통신하기 위함입니다.</p>
</li>
</ul>
<h2 id="5-capm-개발-프로세스-시각화">5. CAPM 개발 프로세스 시각화</h2>
<p>CAPM을 활용한 애플리케이션 개발 프로세스는 다음과 같이 시각화할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/42df6753-fedd-47ab-9e89-46742ae68fe9/image.png" alt=""></p>
<p>CAPM은 개발자가 비즈니스 가치 창출에 집중할 수 있도록 복잡한 기술적 장벽을 낮추고, 자동화를 통해 개발 생산성을 혁신하는 강력한 도구입니다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SAP BTP 계정구조 및 관계]]></title>
            <link>https://velog.io/@j_wisdom_h/SAP-BTP-%EA%B3%84%EC%A0%95%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@j_wisdom_h/SAP-BTP-%EA%B3%84%EC%A0%95%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Tue, 24 Feb 2026 06:07:35 GMT</pubDate>
            <description><![CDATA[<p><a href="https://help.sap.com/docs/btp/sap-business-technology-platform/account-model?locale=ko-KR#loioeeda449cf252418a97e0f7c9abd30b9a">글로벌 계정과 하위계정의 관계 공식문서</a></p>
<h2 id="1-sap-btp의-큰-그림-글로벌-계정">1. SAP BTP의 큰 그림: 글로벌 계정</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/a5d995a1-b39c-4ccb-af92-35ddb62d85bc/image.svg" alt=""></p>
<p>글로벌 계정(Global Account)은 SAP BTP를 사용하기 위한 가장 큰 단위(최상위)라고 보면 된다.</p>
<p>글로벌 계정은 여러 개의 하위 계정(Subaccount)을 관리하고, 
우리가 쓸 수 있는 자원(자격 및 할당량)을 총괄하는 역할을 한다. </p>
<ul>
<li>상업적 계약</li>
<li>자격 및 할당량</li>
<li>지역</li>
<li>전체 리스스 관리</li>
</ul>
<p>이 계정은 특정 지역에 묶여있지 않고, 전 세계 어디서든 관리할 수 있는 자유로운 계정이다. </p>
<p><strong>여기서는 앱을 직접 배포할 수 없다.</strong></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/ae926933-bb8d-490d-ae5f-1becedf63a49/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/e1efa911-41a0-446e-b702-5b96e3274360/image.png" alt=""></p>
<h2 id="2-실제-작업-공간-하위-계정">2. 실제 작업 공간: 하위 계정</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/24921c80-e61d-4579-8dbc-9fa830774e0c/image.svg" alt=""></p>
<p>하위 계정(Subaccount)은 *<em>실제로 앱을 만들고, 서비스를 사용하고, 구독을 관리하는 작업 공간이다. *</em></p>
<p>즉, 글로벌 계정내의 논리적 컨테이너다.</p>
<p>글로벌 계정 아래에 여러 개의 하위 계정을 만들 수 있다. </p>
<p><strong>각 하위 계정은 서로 독립적이라서,</strong> 마치 여러 개의 독립된 작업실을 가지고 있는 것과 같다. </p>
<p>하위 계정은 <strong>특정 지역(Region)에 연결</strong>되어 있다. </p>
<p>글로벌 계정에서 받은 자원(인타이틀먼트와 쿼터)은 이 하위 계정들에게 나눠줘야 실제로 사용할 수 있다. </p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/0636946c-7c9c-459f-97b7-4098f371b317/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/ed9bd3bb-d476-4958-8c9d-8e064cbf4c54/image.png" alt=""></p>
<h2 id="cloud-foundry-조직과-kyma-클러스터">Cloud Foundry 조직과 Kyma 클러스터</h2>
<h3 id="cloud-foundry">Cloud Foundry</h3>
<p>Cloud Foundry 환경을 활성화하면 
Cloud Foundry 조직이 자동으로 생긴다. (초기값으로 활성화되어있음)</p>
<p>Cloud Foundry는 SAP가 엑세스 할 수 있는 제3자 환경이다.
대부분의 클라이언트가 Cloud Foundray 환경을 사용한다.</p>
<p>이 조직 안에 <strong>공간(Space)</strong>을 만들어서 앱을 더 세부적으로 관리할 수 있다. </p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/5547cb9d-9aa3-41d5-9081-e423abd5c8d0/image.png" alt=""></p>
<h3 id="kyma">Kyma</h3>
<p>Kyma 환경을 활성화하면 
Kubernetes 클러스터가 생긴다.</p>
<p>Kyma는 SAP의 자체환경이다.</p>
<p>하나의 하위 계정 안에 여러 개의 Kyma 클러스터를 만들 수 있다.</p>
<p>각 클러스터 안에는 네임스페이스<strong>(Namespace)</strong>를 만들어서 작업 부하를 나누고 자원 접근을 제어할 수 있다. </p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/9632f3b9-60de-4fe2-8701-2ac179b1e298/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>구분</th>
<th>Cloud Foundry (CF)</th>
<th>Kyma (K8s 기반)</th>
</tr>
</thead>
<tbody><tr>
<td>기본 기술</td>
<td>오픈소스 PaaS (Buildpacks)</td>
<td>Kubernetes (Docker 컨테이너)</td>
</tr>
<tr>
<td>추상화 수준</td>
<td>높음 (코드만 올리면 됨)</td>
<td>낮음 (컨테이너/인프라 제어 가능)</td>
</tr>
<tr>
<td>배포 방식</td>
<td>cf push (매우 간편)</td>
<td>kubectl, Helm Chart (복잡하지만 정교함)</td>
</tr>
<tr>
<td>확장성</td>
<td>애플리케이션 단위 확장</td>
<td>마이크로서비스/컨테이너 단위 정교한 확장</td>
</tr>
<tr>
<td>주요 용도</td>
<td>표준 비즈니스 웹 앱, Java/Node.js</td>
<td>마이크로서비스, 이벤트 기반 아키텍처, 서버리스</td>
</tr>
<tr>
<td>학습 곡선</td>
<td>낮음 (빠른 시작 가능)</td>
<td>높음 (Kubernetes 지식 필요)</td>
</tr>
</tbody></table>
<ul>
<li><p>Cloud Foundry는 개발자가 인프라를 신경 쓰지 않게 한다. 
소스코드를 올리면 &#39;빌드팩(Buildpack)&#39;이 알아서 실행 환경을 구성한다. </p>
<blockquote>
<p>간단한 배포에 장점</p>
</blockquote>
</li>
<li><p>Kyma는 컨테이너 기술을 직접 다룬다. Docker 이미지를 사용하여 환경을 구성하므로, 특정 OS 설정이나 복잡한 네트워크 구성이 필요한 경우 훨씬 자유롭다.</p>
<blockquote>
<p>강력한 제어권에 장점</p>
</blockquote>
</li>
</ul>
<h2 id="디렉토리">디렉토리</h2>
<p>디렉토리(Directory)는 하위 계정들을 더 체계적으로 정리하고 관리할 수 있게 해준다.</p>
<blockquote>
<p>마치 컴퓨터에서 폴더 안에 또 다른 폴더를 만들어서 파일을 정리하듯이, 디렉토리 안에 다른 디렉토리나 하위 계정을 넣을 수 있다.</p>
</blockquote>
<p>최대 7단계까지 깊은 계층 구조를 만들 수 있다. </p>
<p>가장 위는 글로벌 계정, 
가장 아래는 하위 계정이고, 
그 사이에 최대 5개의 디렉토리 레벨을 가질 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/2ab0e788-5a13-4a05-8c69-3c112142ede5/image.png" alt=""></p>
<p>디렉토리를 사용하면 사용량과 비용을 모니터링하거나, 자원(인타이틀먼트)을 미리 할당해둘 수도 있다. </p>
<blockquote>
<p>예를 들어, 특정 부서의 디렉토리에 예산을 미리 배정해두는 것.</p>
</blockquote>
<table>
<thead>
<tr>
<th>기능</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>그룹화 및 필터링</td>
<td>하위 계정들을 묶어서 쉽게 찾고 관리할 수 있음</td>
</tr>
<tr>
<td>사용량 및 비용 모니터링</td>
<td>디렉토리별 자원 사용량과 비용을 한눈에 확인 가능</td>
</tr>
<tr>
<td>인타이틀먼트 관리(선택)</td>
<td>글로벌 계정에서 받은 자원을 디렉토리에 먼저 할당하고, 디렉토리 내부 하위 계정에 재분배 가능</td>
</tr>
<tr>
<td>권한 관리(선택)</td>
<td>특정 사용자에게 디렉토리 자원 관리 권한을 부여 가능</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[SAP BTP Global Account, Subaccount & Spaces]]></title>
            <link>https://velog.io/@j_wisdom_h/SAP-BTP-Global-Account-Subaccount-Spaces</link>
            <guid>https://velog.io/@j_wisdom_h/SAP-BTP-Global-Account-Subaccount-Spaces</guid>
            <pubDate>Tue, 24 Feb 2026 05:58:11 GMT</pubDate>
            <description><![CDATA[<p><strong>SAP BTP Global Account, Subaccount &amp; Spaces</strong></p>
<p>SAP BTP 3단계 계정구조를 가지고 있다. </p>
<ul>
<li>global account</li>
<li>sub-account</li>
<li>space (Cloud Foundry)</li>
</ul>
<h2 id="글로벌-계정-생성">글로벌 계정 생성</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/6a7513ca-7cf9-48b7-a8e3-7a37915fc369/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/366d504a-b7a5-4582-ad87-91cad1d611f0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/004393de-0540-4b87-90f7-a32f9065fce4/image.png" alt=""></p>
<p><a href="https://velog.io/@j_wisdom_h/SAP-BTP-%EA%B3%84%EC%A0%95%EA%B5%AC%EC%A1%B0">글로벌계정, 서브계정, 스페이스, 디렉토리 관계 블로그 글 </a></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/fc47c008-bbb7-4ab5-a0d0-743f11780274/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/d69853a6-1d26-483c-8d77-c6017fd2d30b/image.png" alt=""></p>
<h3 id="cloud-foundry-환경과-kyma-환경">Cloud Foundry 환경과 Kyma 환경.</h3>
<ul>
<li>Cloud Foundry는 SAP가 액세스할 수 있는 제3자 환경이다.<ul>
<li>space라는 공간을 갖는다. </li>
</ul>
</li>
<li>Kyma는 SAP 자체 환경이다.</li>
</ul>
<p>애플리케이션 배포시 서브계정과 환경을 확인하고 해당 공간에 배포된다.</p>
<h2 id="하위계정-생성">하위계정 생성</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/f09f8ea4-5540-4204-a694-1d9e4c9e76c9/image.png" alt=""></p>
<h2 id="destination">Destination</h2>
<p>OData서비스가 생성되는 도메인 또는 호스트가 포함된다.</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/c48a5ad6-ee06-4883-ac4a-531129aa61d8/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/f39f33ca-3272-4dd9-a0b0-b19caf7e5bb1/image.png" alt=""></p>
<h3 id="destination에-포함되는-주요-정보">Destination에 포함되는 주요 정보</h3>
<ul>
<li>Name: 앱에서 호출할 별명 (예: Northwind_Service)</li>
<li>URL: 실제 데이터가 있는 주소 (예: <a href="https://services.odata.org/">https://services.odata.org/</a>...)</li>
<li>Proxy Type: 인터넷을 통할 것인지(Cloud), 내부망을 통할 것인지(On-Premise)</li>
<li>Authentication: 어떤 방식으로 로그인할 것인지 (예: NoAuthentication, BasicAuthentication)</li>
</ul>
<h3 id="ufa-user-front-end-application">UFA (User Front-end Application)</h3>
<p><em><strong>SAP BTP 환경에서 사용자에게 보여지는 화면단(Front-end) 애플리케이션을 통칭하는 용어</strong></em></p>
<p>스스로 데이터를 가지고 있지 않기 때문에, 반드시 외부(클라우드나 온프레미스)에 있는 OData 서비스를 호출해서 데이터를 가져와야 한다. </p>
<p>이때 <strong>데이터를 어디서 가져올지 알려주는 이정표가 바로 &#39;Destination&#39;다</strong>.</p>
<h3 id="내부-통신-vs-외부-연결">&#39;내부 통신&#39; vs &#39;외부 연결&#39;</h3>
<ul>
<li>Destination이 필요한 이유: 주로 외부(External)에 있는 시스템과 연결할 때 사용한다.</li>
<li>Destination이 필요 없는 이유: <strong>CAPM 프로젝트 안</strong>에는 <strong>보통 화면(UFA)과 백엔드(CAPM)</strong>가 같이 들어있다. Destination를 찾아보지 않고도 상대 경로(Relative Path)를 통해 바로 데이터를 주고받을 수 있다.</li>
</ul>
<blockquote>
<p>CAPM은 SAP Cloud Application Programming Model의 약자로
SAP 클라우드 환경에 최적화된 애플리케이션을 빠르고 표준화된 방식으로 만들기 위한 프레임워크다.</p>
</blockquote>
<h2 id="trust-configuration">Trust Configuration</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/4e45ceae-4f53-4adb-8d1f-56dbce8ff84f/image.png" alt=""></p>
<p>비즈니스 사용자가 어플리케이션에 로그온할 수 있도록 IDP를 하위 계정에 연결한다.</p>
<ul>
<li>IAS (Identity Authentication Service, 신원 인증 서비스)</li>
<li>IDP (Identity Provider, 신원 제공자)</li>
</ul>
<h2 id="instance-and-subscriptions">Instance and Subscriptions</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/ba29d2c6-ba0c-44ad-b972-58024464220f/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[인트로: SAP BTP, Fiori, UI5 & CAPM]]></title>
            <link>https://velog.io/@j_wisdom_h/%EC%9D%B8%ED%8A%B8%EB%A1%9C-SAP-BTP-Fiori-UI5-CAPM</link>
            <guid>https://velog.io/@j_wisdom_h/%EC%9D%B8%ED%8A%B8%EB%A1%9C-SAP-BTP-Fiori-UI5-CAPM</guid>
            <pubDate>Tue, 24 Feb 2026 05:47:47 GMT</pubDate>
            <description><![CDATA[<h2 id="sap-btp">SAP BTP</h2>
<p>SAP 엔터프라이즈 앱을 구축, 확장, 통합하기 위한 클라우드 플랫폼이다.
애플리케이션 개발, 통합, 자동화, 보안 및 ID, 데이터 및 분석을 제공한다.
Colud Foundry, CAMA, ABAP환경을 지원한다.</p>
<h2 id="sap-fiori">SAP Fiori</h2>
<p><strong>SAP UI/UX 디자인 시스템</strong>  (디자인 원칙)
엔터프라이즈급 사용자 인터페이스를 만드는데 사용된다.</p>
<h3 id="5가지-원칙">5가지 원칙</h3>
<ul>
<li>Role-based</li>
<li>adaptive </li>
<li>Simple</li>
<li>Coherent </li>
<li>Delightful</li>
</ul>
<h3 id="sap-ui5-sap-ui-development-toolkit-for-html5-">SAP UI5 (SAP UI Development Toolkit for HTML5 )</h3>
<p>SAP UI5는 반응형 웹 애플리케이션을 구축하는 데 사용되는 <strong>JavaScript 프레임워크</strong>(기술 프레임워크)</p>
<ul>
<li>MVC 아키텍처</li>
<li>Data-binding (jasion, odata, resource)</li>
<li>라이브러리 컨트롤 지원(SAP.m,SAP.f, SAP.TNT 등)</li>
<li>반응형, 크로스 플랫폼</li>
</ul>
<h2 id="capm--cloud-application-programming-model">CAPM ( Cloud Application Programming Model)</h2>
<p>SAP 클라우드 애플리케이션 프로그래밍 모델</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/35046ddf-b37d-4edd-82d7-8d749fa1d7a1/image.png" alt=""></p>
<ul>
<li>Node.js 또는 Java기반 프레임워크</li>
<li>CDS 모델링 사용</li>
<li>OData v4서비스 사용</li>
<li>보안, 유효성 검사 및 권한 부여 기능 내장</li>
<li>BTP에 쉽게 배포가능</li>
</ul>
<h2 id="아키텍처-오버뷰">아키텍처 오버뷰</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/26e36a49-4103-4c83-974a-3178d10391ac/image.png" alt=""></p>
<p>SAP UI5 APP 은 결국 프론트엔드 즉, 사용자 인터페이스 개발이다.
그리고 이 개발을 위해 Fiori 프레임워크인 UI5을 사용한다.
JavaScript 컨트롤러를 사용해 이벤트와 그 처리 매커니즘을 이용한다.</p>
<p>그리고 CDS, handler, DB를 사용하여 CAMP 서비스에서 UI5로 데이터를 보낸다.</p>
<p>SAP BTP Cloud Foundary에 있는 HANA Cloud에 HANA DB를 구성한다. </p>
<ul>
<li>UI5앱은 CAPM Odata를 사용한다.</li>
<li>CAMP는 Colud Foundary의 BTP에 배포되었다.</li>
<li>UI5가 HTML5 앱저장소가 배포되었다. </li>
<li>Fiori 런치패드는 UI5를 실행하고 IAS(Identity Authentication Service) 및 XS UAA(User Account and Authentication)가 인증을 처리한다.</li>
</ul>
<h2 id="ui5앱-end-to-end-학습순서">UI5앱 end-to-end 학습순서</h2>
<ol>
<li>UI5앱 빌드</li>
<li>CAPM 서비스 생성</li>
<li>SAP BTP에 배포</li>
<li>대상(destination)을 구성</li>
<li>실시간 CRUD </li>
<li>Fiori 런치패드, TMS(테스트 &lt;-&gt; 프로덕션) 전송구조</li>
<li>프로젝트 </li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[PBO, PAI of Module Screen
(feat. screen, call screen)]]></title>
            <link>https://velog.io/@j_wisdom_h/PBO-PAI-of-Module-Screenfeat.-screen-call-screen</link>
            <guid>https://velog.io/@j_wisdom_h/PBO-PAI-of-Module-Screenfeat.-screen-call-screen</guid>
            <pubDate>Sun, 10 Aug 2025 03:39:46 GMT</pubDate>
            <description><![CDATA[<p>스크린의 생명주기는 </p>
<p><strong>PBO(Process Before Output)</strong>와 
<strong>PAI(Process After Input)</strong>라는 </p>
<p>두 가지 핵심 이벤트와 화면 흐름 제어 명령어로 구성된다. </p>
<blockquote>
</blockquote>
<p>PBO는 화면이 <strong><em>사용자에게 보이기 전에 데이터를 준비하고 화면 속성을 제어하는 단계</em></strong>이며, </p>
<blockquote>
</blockquote>
<p>PAI 는 _<strong>사용자의 입력 값을 처리하고 비즈니스 로직을 실행</strong>_하는 단계다.</p>
<blockquote>
<p>💡 <strong>PBO 시점</strong>에 스크린 만들어지고 있다.
 끝날시점 스크린으로 값이 넘어온다.</p>
</blockquote>
<blockquote>
<p>💡 <strong>PAI 시작시점</strong>에 스크린으로 값이 넘어온다. (작업을 해야하니까)
끝날시점에 스크린이 사라진다.</p>
</blockquote>
<hr>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/d2534d49-9c5b-47e5-9f07-7fea7798ddaa/image.png" alt=""></p>
<p>입력값을 주고 엔터를 친다. 그 후 화면은 이전의 화면과 같은 화면일까 ?</p>
<p>NO.</p>
<p>화면은 PAI를 거쳐 Next  Screen으로 이동하고, PBO로 새로운 스크린이 만들어진다. Next Screen이 같은 화면 번호여서 똑같아 보이지만 다른 스크린이 새로 생성된 것이다.</p>
<hr>
<h2 id="생명주기가-뭔데">생명주기가 뭔데?</h2>
<p>생물의 생명주기란 어떤 대상이 탄생해서 성장하고, 유지되다가 결국 소멸하는 일련의 과정을 의미한다.</p>
<p>ABAP에서 스크린 생명주기는 생물의 생명주기랑 비슷하다. 스크린 어떻게 만들어지고 유지되고 결국 소멸하고 또 다시 탄생하는 그 과정의 총칭이다.
그리고 대표적인 단계가 PBO, PAI다.</p>
<h2 id="pbopai가-정확히-뭔데">PBO/PAI가 정확히 뭔데?</h2>
<h3 id="pbo-process-before-output">PBO (Process Before Output)</h3>
<p>이 단계는 화면이 사용자에게 보이기 직전에 실행되는 부분이다.
화면에 표시될 데이터를 준비하고, 입력 필드를 활성화하거나 비활성화하는 등 화면의 모습을 설정하는 역할을 한다.</p>
<p>PBO가 끝나면 비로소 화면이 사용자에게 나타나게 된다.</p>
<h3 id="pai-process-after-input">PAI (Process After Input)</h3>
<p>이 단계는 사용자가 화면에 무언가를 입력하거나 버튼을 클릭하는 등 액션을 취했을 때 실행되는 부분이다.
사용자가 입력한 값을 프로그램으로 가져와서 유효한지 검사하고, 필요한 비즈니스 로직(예: 데이터 조회, 저장)을 수행하며, 다음 화면으로 이동할지 결정하는 등의 작업을 한다.
PAI가 끝나면 다음 PBO가 실행되어 새로운 화면이 나타나거나, 현재 화면이 다시 표시될 수 있다.</p>
<p>그리고 다음 화면을 결정짓는 명령어는** SET SCREEN**이다.</p>
<h2 id="set-screen-다음-스크린-설정과-생성">SET SCREEN? 다음 스크린 설정과 생성</h2>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/ca5ca81d-13c2-4d5a-b717-888840feb2ae/image.png" alt=""></p>
<p>이미지에서 하이라이트 표시된 <strong>Next Dynpro</strong>가 바로 넥스트스크린을 _<strong>정적</strong>_으로 결정짓는 부분이다. 
(참고. 아무것도 입력되지 않으면 0으로 인식한다)</p>
<p>넥스트 스크린은 엑션으로 pai가 실행되고 나고 현재화면이 사라진뒤 다음에 보여질 스크린을 결정짓는다.</p>
<p>스크린에서 지정하고 싶지 않고, 동적으로 지정하고 싶다면 <strong>SET SCREEN</strong>을 사용한다.</p>
<blockquote>
<p>이 말은 즉 슨, 기본적으로 <code>Next Dynpro</code>에 의해 정의된 스크린 번호가 넥스트 스크린을 결정하고,
<code>SET SCREEN</code> 명령어를 사용한다면, 이 명령어로 선언된 스크린 번호가 넥스트 스크린이 된다는 것이다.</p>
</blockquote>
<ul>
<li>** SET SCREEN    ** : &quot;다음에 보여줄 화면을 미리 설정&quot;하는 명령어</li>
</ul>
<br />

<h3 id="모호한가">모호한가?</h3>
<pre><code class="language-abap">MODULE user_command_0100 INPUT.
  CASE ok_code.
    WHEN &#39;EXIT&#39;.
      LEAVE PROGRAM.
    WHEN &#39;CANC&#39;.
      SET SCREEN 0.
    WHEN &#39;BACK&#39;.
      LEAVE TO SCREEN 0.
    WHEN &#39;S200&#39;.
      SET SCREEN 0200.&quot;dynamic screen
      MESSAGE i999(zmcb00) WITH gv_carrid.
      LEAVE SCREEN.&quot;현재스크린 떠남

    WHEN &#39;ENTER&#39;.

    WHEN OTHERS.
  ENDCASE.
ENDMODULE.
</code></pre>
<p>현재 스크린은 100. Next Dynpro은 100이다. (가장 일반적인 경우)</p>
<blockquote>
<p>예시1)  <strong><code>S200</code></strong>인 액션
100에서 pai를 탄다.
그리고 SET SCREEN에 의해서 next screen이 200으로 변한다.
현재화면 멈추고 메세지띄우고 (by type i)
<code>leave screen</code>으로 현재 스크린을 떠난다. 
pbo로 넥스트스크린 200으로 이동(생성)한다.(기존스크린삭제됨)</p>
</blockquote>
<blockquote>
<p>예시2) <code>ENTER</code>를 입력했다면 
100에서 pai를 탄다.
pbo로 Next Dynpro인 100으로 이동(생성)한다.(기존스크린삭제됨)</p>
</blockquote>
<p>이해가 될 것이다. 
화면에 대한 명령어가 없다면 스크린의 기본 설정 Next Dynpro에 따라서
다음 스크린이 결정되고,</p>
<p>SET SCREEN이 있다면 그것에 해당하는 스크린이 다음 스크린이 된다.</p>
<h3 id="주의할-점-leave-to-screen">주의할 점. LEAVE TO SCREEN.</h3>
<ul>
<li>** LEAVE TO SCREEN**: &quot;현재 화면을 즉시 종료하고 지정된 화면으로 이동&quot;하는 명령어&#39;</li>
</ul>
<p>SET SCREEN 0.
LEAVE SCREEN.</p>
<p>= LEAVE TO SCREEN 0.</p>
<p>이걸 왜 주의해야할까? SET SCREEN은 단순히 다음 스크린을 설정하는거지 현재 화면을 끝내(종료)는 것이 아니라는 것이다.</p>
<p><code>LEAVE SCREEN</code>이 있어야 즉시, 현재 화면이 끝나고 다음 스크린으로 넘어가는것이다. </p>
<p>즉, 이 명령어가 있다면 <strong>뒤에 정의된 함수 및 로직은 무시된다.</strong> </p>
<p><strong>왜냐? 현재 스크린이 제거되었는걸?</strong></p>
<h3 id="자-그럼-question-하나-더">자 그럼 question 하나 더</h3>
<pre><code>WHEN &#39;S200&#39;.
      SET SCREEN 0200.&quot;dynamic screen
      MESSAGE i999(zmcb00) WITH gv_carrid.</code></pre><p>여기서는 메세지는 보일까?
YES.</p>
<p>SET SCREEN은 다음스크린을 설정할 뿐,
현재 화면을 즉시 종료하지는 않는다.</p>
<h2 id="뒤로가기-스크린-삭제">뒤로가기? 스크린 삭제.</h2>
<p>자, 이제 
<code>넥스트 스크린으로 이동</code>. 
<code>스크린 100 -&gt; 스크린 200으로 이동</code>하기에 대한 
pbo/pai 간단한 설명을 끝냈다.</p>
<p>이번에는 뒤로가기다.</p>
<p>가장 흔히 쓰는 뒤로가기는 <code>LEAVE TO SCREEN 0</code> 명령어다.</p>
<pre><code> WHEN &#39;BACK&#39;.
      LEAVE TO SCREEN 0.</code></pre><p> 앞서 말했듯
 SET SCREEN 0, LEAVE TO SCREEN을 합친 형태다.</p>
<blockquote>
<p><code>넥스트 스크린을 0</code>으로 설정한다는건 어떤 의미일까?
 0은 없는 화면이다. 그래서 0으로 이동할려고하면 프로그램이 종료되거나 기존에 남아있던 화면으로 돌아간다.</p>
</blockquote>
<p> 지금 현재 화면은 200으로
 스크린은 100 -&gt; 200으로 왔다.</p>
<p> <strong>그럼 기존 100은 남아있는건가..?
 NO.</strong></p>
<p> 앞서 말했듯 기존 스크린은 삭제되고 넥스트 스크린(여기서는 set screen 설정에 의해)으로 200이 새로 생긴것이다.</p>
<p> 100은 사라지고, 200만 남았다.</p>
<p>그래서 <strong><code>SET SCREEN</code>은 다음 화면을 설정하고 현재 화면을 종료하여 메모리를 효율적으로 사용한다.</strong></p>
<blockquote>
<p>다시 돌아가서 그러면 200에서 뒤로가기
즉 <code>LEAVE TO SCREEN 0.</code>을 사용한다면?</p>
</blockquote>
<blockquote>
<p>넥스트 스크린을 0으로 설정하고 
현재 스크린을 제거한다.
그 결과 프로그램이 종료된다. 왜냐? 스크린 0은 없다.</p>
</blockquote>
<p>그럼 어떻게 해야 다시 100으로 돌아갈 수 있는가?
앞서 말한 SET SCREEN을 다시 써주면 된다.
<code>SET SCREEN 100</code>으로 말이다.</p>
<Br />

<p>한가지 더,
만약 아래처럼 아무것도 입력을 안한다면? 
nextnpro를 통해 넥스트 스크린으로 이동할 것이다. 그리고 nextnpro은 일반적으로 자기자신이다.</p>
<pre><code> WHEN &#39;BACK&#39;.
</code></pre><p>이제 반복에 반복을 했으니 다음 <code>Call Screen</code>으로 넘어가자. </p>
<h2 id="call-screen">Call Screen.</h2>
<ul>
<li>** CALL SCREEN **   : 현재 화면 위에 &quot;새로운 화면을 덮어씌우듯이 호출&quot;하는 방식</li>
</ul>
<p>전공생이나 이 개발직 공부를 해봤다면
사실 이게 더 이해하기가 쉬울 것이다. CALL STACK이 익숙할테니.</p>
<p>앞선 스크린은 
현재스크린을 삭제하고 넥스트 스크린을 생성하는 방식이었다.</p>
<p><strong><code>콜스크린</code></strong>은
현재스크린을 삭제하지 않고, 잠시 홀딩시킨 상태에서
넥스트 스크린을 그 위에 쌓는다. (STACK과 같은 구조)</p>
<p>그래서 로직도, 코드도 조금 다르다.</p>
<pre><code>MODULE user_command_0100 INPUT.
  CASE ok_code.
        ...
    WHEN &#39;BACK&#39;.
      LEAVE TO SCREEN 0.
      ...
    WHEN &#39;C200&#39;.
      CALL SCREEN 0200.
    WHEN OTHERS.
  ENDCASE.
ENDMODULE.</code></pre><pre><code>MODULE user_command_0200 INPUT.
  CASE sy-ucomm.
    WHEN &#39;BACK&#39;.
      LEAVE TO SCREEN 100.
    WHEN &#39;C100&#39;.
      CALL SCREEN 0100.
    WHEN OTHERS.
  ENDCASE.
ENDMODULE.</code></pre><p>자 다시 해보자
초기 스크린은 100이다.</p>
<p>Fuction code <code>C200</code> 액션이 발생한다.
그럼 <code>CALL SCREEN</code>으로 200을 호출한다.</p>
<p>그리고 스크린 200의 pbo을 통해
200스크린이 보이고 생성된다. (스크린 100 위에 쌓인다. 스크린 100이 제거되지 않음).</p>
<p>스크린 200에서 뒤로 가기를 하면? 
<code>LEAVE TO SCREEN 100.</code> 으로 
현재 스크린으로 종료하고 스크린 100으로 이동(생성한다).</p>
<br />

<p>=&gt; 여기서 주목할 점</p>
<blockquote>
<p>뒤로가기할때 <code>LEAVE TO SCREEN 100.</code>을 하면
<strong>이전 스크린 100으로 돌아오는 것이 아니고
100이 새로 생성된다는 것이다.</strong></p>
</blockquote>
<p>200에서 뒤로가기하며 <strong>next screen 100</strong>으로 이동</p>
<p>200의 pai가 종료되면서 새로운 스크린(100)으로 이동</p>
<p>(200이 100으로 대체된다고 생각하면됨)</p>
<br />

<p>빈약하지만 플로우를 도식화하면 아래와 같다.</p>
<blockquote>
</blockquote>
<p>200 → 뒤로가기(next 100) → 100 → 뒤로가기 (next 스크린 0)→사라짐
-&gt; 100 → 뒤로가기  (next 스크린 0)→사라짐</p>
<p>그래서 뒤로가기를 두번을 해야지 기존 스크린 100으로 돌아가야하는 상황이 생긴다. 불필요한 클릭을 한 번 더 해야하는것이다.</p>
<p>한번에 이전의 스크린 100으로 돌아갈려면?</p>
<br />

<pre><code>MODULE user_command_0100 INPUT.
  CASE ok_code.
        ...
    WHEN &#39;BACK&#39;.
      LEAVE TO SCREEN 0.
      ...
    WHEN &#39;C200&#39;.
      CALL SCREEN 0200.
    WHEN OTHERS.
  ENDCASE.
ENDMODULE.


MODULE user_command_0200 INPUT.
  CASE sy-ucomm.
    WHEN &#39;BACK&#39;.
      LEAVE TO SCREEN 0.
    WHEN &#39;C100&#39;.
      CALL SCREEN 0100.
    WHEN OTHERS.
  ENDCASE.
ENDMODULE.
</code></pre><p>100→200→100 ⇒ <strong>back을 한 번하면 초기화면으로 이동.</strong></p>
<p>바뀐 것은 
user_command_0200의 <code>BACK</code> 부분이다.
100이 0으로 바뀌었다.</p>
<pre><code>WHEN &#39;BACK&#39;.
      LEAVE TO SCREEN 0.</code></pre><blockquote>
<p>200에서 뒤로가기하며 스크린 삭제되고 next screen 0으로 이동한다..
pai가 끝나면서 새로운 스크린(0)으로 이동해야하는데 없다.
그러면 <strong>전에 있던 스크린 100으로 이동한다.</strong></p>
</blockquote>
<p>콜스크린에서는 이전 스크린으로 돌아오기 위해서는 넥스트스크린 설정을 0으로 해야한다.</p>
<br />

<pre><code>MODULE user_command_0100 INPUT.
  CASE ok_code.

    WHEN &#39;CANC&#39;.
      SET SCREEN 0.
*      LEAVE SCREEN.
    WHEN &#39;BACK&#39;.
      LEAVE TO SCREEN 0.

    WHEN &#39;C200&#39;.
      CALL SCREEN 0200.

      CLEAR gv_carrname.
      SELECT SINGLE carrname
        FROM scarr
        INTO gv_carrname
        WHERE carrid = gv_carrid.

    WHEN &#39;ENTER&#39;.

    WHEN OTHERS.
  ENDCASE.
ENDMODULE.</code></pre><p>100 → 200으로 콜스크린이 이동되면</p>
<p>그 밑에는 홀딩된다. </p>
<p>그리고 200에서 뒤로가기로 기존 100으로 돌아오면, select 구문 실행된다.</p>
<p>그래서 처음 100에서 200으로 콜스크린 이동시에는 gv_carrname 결과값이 보이지 않지만 (왜냐면 select 구문이 아직 실행안됨)</p>
<p>100으로 돌아온 후 콜스크린 이동시에, gv_carrname이 반영(적용)된다.</p>
<p>그래서 한발자국씩 늦게 보인다.</p>
<br />

<p>이 문제를 해결하는 법은 간단하다
select 구문을 먼저 실행시키고 콜 스크린을 하면 된다.</p>
<br />




<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/fa915e52-60b2-4fb2-9e9f-569423b1c0b9/image.png" alt=" CALL, SET, LEAVE의 활용 ">
[CALL, SET, LEAVE]</p>
<h2 id="한-번-더-꼬아서-보기">한 번 더 꼬아서 보기</h2>
<h3 id="스크린-100의-nextnpro-200으로-설정한다면--콜-스크린">스크린 100의 nextnpro 200으로 설정한다면, + 콜 스크린</h3>
<p>100의 PAI에서 콜스크린으로 200이 불러와지고,</p>
<p>200에서 뒤로가기로 leave to screen 0로 </p>
<p>200스크린이 삭제된다. </p>
<p>다시 100으로 돌아온다.</p>
<p>계속 100의 PAI구문 실행된다.</p>
<p>별다른 set screen 설정이 없기때문에 </p>
<p>next screen으로 200이 생성된다. (nextnpro 200. 기존 100은 삭제됨)</p>
<p>여기서 뒤로가기를 하면</p>
<p>스크린은 200밖에 없으므로 </p>
<p>leave to screen 0이 의해 현재 스크린 삭제되고, 초기화면으로 돌아간다.(프로그램 종료)</p>
<p>그래서 동작은 </p>
<p>100 → 200</p>
<p>200에서 뒤로가기 ⇒ 200</p>
<p>200에서 뒤로가기 ⇒ 프로그램 종료.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[📘 SAP ABAP 기술 정리 노트 (추가예정)]]></title>
            <link>https://velog.io/@j_wisdom_h/SAP-ABAP-%EA%B8%B0%EC%88%A0-%EC%A0%95%EB%A6%AC-%EB%85%B8%ED%8A%B8-%EC%B6%94%EA%B0%80%EC%98%88%EC%A0%95</link>
            <guid>https://velog.io/@j_wisdom_h/SAP-ABAP-%EA%B8%B0%EC%88%A0-%EC%A0%95%EB%A6%AC-%EB%85%B8%ED%8A%B8-%EC%B6%94%EA%B0%80%EC%98%88%EC%A0%95</guid>
            <pubDate>Wed, 18 Jun 2025 10:44:36 GMT</pubDate>
            <description><![CDATA[<h2 id="📘-sap-abap-기술-정리-노트-추가예정">📘 SAP ABAP 기술 정리 노트 (추가예정)</h2>
<p>SAP ABAP 개발 과정 및 예비 개발자들을 위해, ABAP 프로그램 구조, 시스템 설치, UI5/Fiori, SAP BTP, RFC/BAPI/CDS 기술까지 포함한 실무 대비용 기술 노트입니다.</p>
<hr>
<h2 id="❓-기술-질문-요약">❓ 기술 질문 요약</h2>
<ul>
<li><strong>도메인</strong>: 타입, 길이, 값 범위 등 기술적 속성</li>
<li><strong>데이터 엘리먼트</strong>: 설명, 레이블 등 의미적 속성</li>
</ul>
<hr>
<h2 id="🛠-sap-설치-환경-구성">🛠 SAP 설치 환경 구성</h2>
<ul>
<li><strong>Oracle VirtualBox</strong>: 가상머신</li>
<li><strong>openSUSE</strong>: SAP 서버 설치용 리눅스 배포판</li>
<li><strong>SAP Logon</strong>: SAP 접속용 클라이언트 프로그램</li>
<li><strong>su / sudo</strong>: 계정 전환 및 root 명령 실행</li>
<li><strong>npladm</strong>: SID가 NPL인 SAP 시스템 관리자 계정</li>
<li><strong>chmod</strong>: 파일 권한 변경 명령어</li>
<li><strong>포트 포워딩</strong>: 호스트 포트 ↔ 게스트 포트 연동 방식</li>
</ul>
<hr>
<h2 id="🌐-ui5-fiori-launchpad">🌐 UI5, Fiori, Launchpad</h2>
<ul>
<li><strong>SAPUI5</strong>: SAP 제공 웹 애플리케이션 개발 프레임워크</li>
<li><strong>Fiori</strong>: SAPUI5 기반 웹 앱 (모바일 친화적)</li>
<li><strong>Fiori Launchpad</strong>: Fiori 앱을 통합 제공하는 대시보드</li>
<li><strong>STC01 (TCODE)</strong>: SAP_GW_FIORI_ERP_ONE_CLNT_SETUP 수행 → 여러 권한·서비스 자동 세팅</li>
</ul>
<hr>
<h2 id="🧾-abap-리포트와-alv">🧾 ABAP 리포트와 ALV</h2>
<ul>
<li><strong>Report</strong>: ABAP에서 가장 기본적인 프로그램 유형 (조건에 따라 조회 및 결과 출력)</li>
<li><strong>ALV</strong>: 데이터를 표 형태로 출력하는 도구 (<code>REUSE_ALV_GRID_DISPLAY</code>, <code>CL_GUI_ALV_GRID</code> 등)</li>
<li><strong>Type Pool <code>SLIS</code></strong>: ALV용 타입/상수 정의 집합</li>
</ul>
<pre><code class="language-abap">TYPE-POOLS: SLIS.
DATA: fieldcatalog TYPE slis_t_fieldcat_alv WITH HEADER LINE.
fieldcatalog-fieldname = &#39;MANDT&#39;.
fieldcatalog-seltext_m = &#39;Client&#39;.
fieldcatalog-do_sum = &#39;X&#39;.</code></pre>
<h3 id="alv-레이아웃-예시">ALV 레이아웃 예시</h3>
<pre><code class="language-abap">DATA: gd_layout TYPE slis_layout_alv.
gd_layout-zebra = &#39;X&#39;.
gd_layout-colwidth_optimize = &#39;X&#39;.
gd_layout-info_fieldname = &#39;LINE_COLOR&#39;.</code></pre>
<h3 id="alv-정렬">ALV 정렬</h3>
<pre><code class="language-abap">DATA: gd_sort TYPE slis_t_sortinfo_alv WITH HEADER LINE.
gd_sort-spos = 1.
gd_sort-fieldname = &#39;CARRID&#39;.
gd_sort-up = &#39;X&#39;.
gd_sort-subtot = &#39;X&#39;.</code></pre>
<hr>
<h2 id="🧮-선택화면selection-screen-흐름-정리">🧮 선택화면(Selection-Screen) 흐름 정리</h2>
<aside>
**💡 선택화면 흐름**

<table>
<thead>
<tr>
<th>순서</th>
<th>이벤트 구문</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>①</td>
<td><code>INITIALIZATION.</code></td>
<td>화면 표시 전 1회 실행</td>
</tr>
<tr>
<td>②</td>
<td><code>AT SELECTION-SCREEN OUTPUT.</code></td>
<td>선택화면 필드 제어(숨김/비활성 등)</td>
</tr>
<tr>
<td>③</td>
<td>선택화면 표시</td>
<td>사용자 입력</td>
</tr>
<tr>
<td>④</td>
<td><code>AT SELECTION-SCREEN ON VALUE-REQUEST</code></td>
<td>F4 도움말 실행 시</td>
</tr>
<tr>
<td>⑤</td>
<td><code>AT SELECTION-SCREEN.</code></td>
<td>실행버튼 클릭 시 유효성 검사</td>
</tr>
<tr>
<td>⑥</td>
<td><code>START-OF-SELECTION.</code></td>
<td>본 로직 처리 시작</td>
</tr>
<tr>
<td>⑦</td>
<td><code>END-OF-SELECTION.</code> (선택)</td>
<td>데이터 출력, 화면 이동</td>
</tr>
</tbody></table>
</aside>

<pre><code class="language-abap">SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME.
  PARAMETERS: p_file TYPE rlgrap-filename.
SELECTION-SCREEN END OF BLOCK b1.</code></pre>
<hr>
<h2 id="📋-주요-문법-요약">📋 주요 문법 요약</h2>
<ul>
<li><code>TYPE STANDARD TABLE OF</code>: 특정 타입의 내부 테이블 선언</li>
<li><code>WITH HEADER LINE</code>: 테이블 + 동일 구조의 워크 에어리어 생성</li>
<li><code>LIKE</code>: 기존 오브젝트의 타입을 따라 선언</li>
</ul>
<hr>
<h2 id="📦-도움말-f4-제공-예시">📦 도움말 (F4) 제공 예시</h2>
<pre><code class="language-abap">AT SELECTION-SCREEN ON VALUE-REQUEST FOR s_cname-low.
  DATA: it_return_tab TYPE ddshretval OCCURS 0 WITH HEADER LINE.
  CALL FUNCTION &#39;F4IF_INT_TABLE_VALUE_REQUEST&#39;
    EXPORTING
      retfield = &#39;CARRNAME&#39;
      dynprofield = &#39;s_cname-low&#39;
    TABLES
      value_tab = it_f4help3
      return_tab = it_return_tab.</code></pre>
<hr>
<h2 id="🔽-사용자-정의-드롭다운-listbox">🔽 사용자 정의 드롭다운 (LISTBOX)</h2>
<pre><code class="language-abap">TYPE-POOLS: vrm.
DATA: lt_droplist TYPE vrm_values.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_carrid.
  SELECT carrid AS key, carrid &amp;&amp; &#39; &#39; &amp;&amp; carrname AS text
    FROM scarr INTO TABLE @lt_droplist.
  CALL FUNCTION &#39;VRM_SET_VALUES&#39;
    EXPORTING id = &#39;p_carrid&#39; values = lt_droplist.</code></pre>
<hr>
<h2 id="🔁-join-종류">🔁 JOIN 종류</h2>
<table>
<thead>
<tr>
<th>유형</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>INNER JOIN</td>
<td>양쪽 테이블에 모두 존재하는 값만</td>
</tr>
<tr>
<td>LEFT OUTER JOIN</td>
<td>왼쪽 + 일치하는 오른쪽 값</td>
</tr>
<tr>
<td>RIGHT OUTER JOIN</td>
<td>오른쪽 + 일치하는 왼쪽 값</td>
</tr>
<tr>
<td>CROSS JOIN</td>
<td>모든 조합 (데카르트 곱)</td>
</tr>
</tbody></table>
<hr>
<h2 id="➕-collect-문">➕ COLLECT 문</h2>
<ul>
<li>동일 키 존재 시: 숫자 필드 합산</li>
<li>키 미존재 시: 새로운 행 추가</li>
</ul>
<pre><code class="language-abap">DATA: lt_itab TYPE TABLE OF ty_itab WITH HEADER LINE.
COLLECT lt_itab.</code></pre>
<hr>
<h2 id="🔄-시스템-변수">🔄 시스템 변수</h2>
<table>
<thead>
<tr>
<th>변수</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>sy-repid</code></td>
<td>현재 프로그램 ID</td>
</tr>
<tr>
<td><code>sy-subrc</code></td>
<td>마지막 명령의 성공 여부 (0: 성공)</td>
</tr>
<tr>
<td><code>sy-index</code></td>
<td>DO, WHILE 반복 횟수</td>
</tr>
<tr>
<td><code>sy-tabix</code></td>
<td>LOOP, READ 수행 중 테이블 인덱스</td>
</tr>
</tbody></table>
<hr>
<h2 id="🧠-bdc-vs-bapi">🧠 BDC vs BAPI</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>BDC</th>
<th>BAPI</th>
</tr>
</thead>
<tbody><tr>
<td>처리 방식</td>
<td>트랜잭션 화면 모방 (SHDB)</td>
<td>비즈니스 객체 직접 호출</td>
</tr>
<tr>
<td>속도</td>
<td>느림</td>
<td>빠름</td>
</tr>
<tr>
<td>오류 처리</td>
<td>SM35에서 확인</td>
<td>RETURN 파라미터 활용</td>
</tr>
<tr>
<td>유지보수</td>
<td>화면 바뀌면 수정 필요</td>
<td>안정적 인터페이스 제공</td>
</tr>
<tr>
<td>실시간성</td>
<td>가능 (CALL TRANSACTION)</td>
<td>우수 (RFC 연동 포함)</td>
</tr>
</tbody></table>
<hr>
<h2 id="🔗-관련-개념">🔗 관련 개념</h2>
<h3 id="📌-btp-sap-business-technology-platform">📌 BTP (SAP Business Technology Platform)</h3>
<ul>
<li>SAP의 클라우드 PaaS</li>
<li>통합, 데이터, 앱, AI 기능 포함</li>
</ul>
<h3 id="📌-rap-restful-abap-programming">📌 RAP (RESTful ABAP Programming)</h3>
<ul>
<li>S/4HANA 기반 ABAP 개발 모델</li>
<li>CDS + Behavior + 서비스로 구성</li>
</ul>
<h3 id="📌-cds-core-data-services">📌 CDS (Core Data Services)</h3>
<ul>
<li>SQL 기반 View</li>
<li>JOIN, 집계, UI annotation 가능</li>
<li>고성능 + Fiori 연계 용이</li>
</ul>
<h3 id="📌-rfc-remote-function-call">📌 RFC (Remote Function Call)</h3>
<ul>
<li>시스템 간 함수 호출 표준 방식</li>
<li><code>CALL FUNCTION IN DESTINATION</code> 형태로 사용</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[ SELECTION-SCREEN 계열 이벤트 흐름]]></title>
            <link>https://velog.io/@j_wisdom_h/SELECTION-SCREEN-%EA%B3%84%EC%97%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%ED%9D%90%EB%A6%84</link>
            <guid>https://velog.io/@j_wisdom_h/SELECTION-SCREEN-%EA%B3%84%EC%97%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%ED%9D%90%EB%A6%84</guid>
            <pubDate>Wed, 18 Jun 2025 10:37:47 GMT</pubDate>
            <description><![CDATA[<h2 id="📊-정리">📊 정리</h2>
<table>
<thead>
<tr>
<th>구문</th>
<th>실행 시점</th>
<th>용도</th>
</tr>
</thead>
<tbody><tr>
<td><code>INITIALIZATION</code></td>
<td>선택화면 생성 전에 1회</td>
<td>제목, 기본값 지정</td>
</tr>
<tr>
<td><code>AT SELECTION-SCREEN OUTPUT</code></td>
<td>선택화면 표시 직전 매번</td>
<td>화면 요소 조작</td>
</tr>
<tr>
<td><code>AT SELECTION-SCREEN ON VALUE-REQUEST</code></td>
<td>사용자가 F4 등 요청 시</td>
<td>파일 탐색기, 도움말</td>
</tr>
<tr>
<td><code>AT SELECTION-SCREEN</code></td>
<td>실행 버튼 또는 FunctionKey 클릭 시</td>
<td>전체 유효성 체크</td>
</tr>
<tr>
<td><code>START-OF-SELECTION</code></td>
<td>선택화면 통과 후 실행</td>
<td>본 로직 처리 시작</td>
</tr>
<tr>
<td><code>END-OF-SELECTION</code></td>
<td>메인 로직 후 실행</td>
<td>화면 전환/출력 처리</td>
</tr>
</tbody></table>
<h2 id="🟦-initialization">🟦 INITIALIZATION</h2>
<p>프로그램 실행 직후, 선택화면 뜨기 전에 1회 실행</p>
<pre><code>INITIALIZATION.
  SY-TITLE = &#39;엑셀 업로드 프로그램&#39;.
  r1 = &#39;X&#39;. &quot; 기본값 설정</code></pre><h2 id="🟨-at-selection-screen-output">🟨 AT SELECTION-SCREEN OUTPUT</h2>
<p>선택화면이 사용자에게 보여지기 직전에 실행 (필드 제어용)</p>
<pre><code>AT SELECTION-SCREEN OUTPUT.
  LOOP AT SCREEN.
    IF screen-name = &#39;P_FILE&#39; AND r2 = &#39;X&#39;.
      screen-active = 0.
      MODIFY SCREEN.
    ENDIF.
  ENDLOOP.</code></pre><h2 id="🟩-at-selection-screen-on-value-request">🟩 AT SELECTION-SCREEN ON VALUE-REQUEST</h2>
<p>특정 필드에 F4 도움말 요청 시 실행</p>
<pre><code>AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.
  PERFORM get_file_path. &quot; 파일 탐색기 열기</code></pre><h2 id="🟧-at-selection-screen">🟧 AT SELECTION-SCREEN</h2>
<p>실행버튼 또는 Function Key 클릭 시 전체 유효성 검사</p>
<pre><code>AT SELECTION-SCREEN.
  PERFORM act_function_key. &quot; 양식다운로드 처리</code></pre><h2 id="🟥-start-of-selection">🟥 START-OF-SELECTION</h2>
<p>선택화면 통과 후 본 로직 시작</p>
<pre><code>START-OF-SELECTION.
  PERFORM upload_from_excel.</code></pre><h2 id="🟪-end-of-selection">🟪 END-OF-SELECTION</h2>
<p>데이터 처리 후 화면 출력 등</p>
<pre><code>END-OF-SELECTION.
  CALL SCREEN 100. &quot; ALV 출력</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[⭐프로젝트 : 항공사 예약 정보]]></title>
            <link>https://velog.io/@j_wisdom_h/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%95%AD%EA%B3%B5%EC%82%AC-%EC%98%88%EC%95%BD-%EC%A0%95%EB%B3%B4</link>
            <guid>https://velog.io/@j_wisdom_h/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%95%AD%EA%B3%B5%EC%82%AC-%EC%98%88%EC%95%BD-%EC%A0%95%EB%B3%B4</guid>
            <pubDate>Mon, 16 Jun 2025 01:48:30 GMT</pubDate>
            <description><![CDATA[<h1 id="🔍-개요">🔍 개요</h1>
<p>SAP ABAP을 활용하여 항공사 예약 정보를 조회하고, 고객 정보를 ALV로 시각화하는 프로그램을 개발했습니다. </p>
<h2 id="주요기능-">*<em>주요기능 *</em></h2>
<ul>
<li><p>항공편 조건에 따른 예약정보 조회</p>
</li>
<li><p>취소 여부 필터링</p>
</li>
<li><p>고객 상세 정보 팝업 조회</p>
</li>
<li><p>빠른 예약 고객 Top 5 추출 및 출력</p>
</li>
</ul>
<p>SAP 내부 테스트 테이블(ZSBOOK, SCUSTOM 등)을 기반으로 진행되었으며, 핫스팟 이벤트 및 사용자 정의 툴바, 이중 ALV 출력</p>
<h2 id="📋-사용-기술">📋 사용 기술</h2>
<table>
<thead>
<tr>
<th>분류</th>
<th>기술명</th>
</tr>
</thead>
<tbody><tr>
<td>언어</td>
<td>ABAP</td>
</tr>
<tr>
<td>UI 구성</td>
<td>SELECTION-SCREEN, ALV (CL_GUI_ALV_GRID)</td>
</tr>
<tr>
<td>이벤트 처리</td>
<td>핫스팟 이벤트, 툴바 버튼, 사용자 명령어</td>
</tr>
<tr>
<td>객체지향 요소</td>
<td>이벤트 핸들링 클래스 정의 및 구현</td>
</tr>
<tr>
<td>주요 테이블</td>
<td>ZSBOOK(예약), SCUSTOM(고객), SCARR(항공사)</td>
</tr>
<tr>
<td>## 🔧 주요 개발 목표</td>
<td></td>
</tr>
<tr>
<td>목표</td>
<td>설명</td>
</tr>
<tr>
<td>----------------</td>
<td>-------------------------------------</td>
</tr>
<tr>
<td><strong>조건별 예약 조회</strong></td>
<td>항공사 코드 / 항공편 / 출발일 기준으로 예약 정보 필터링</td>
</tr>
<tr>
<td><strong>ALV 상태 시각화</strong></td>
<td>남은 일수에 따라 상태 아이콘 및 셀 색상 출력</td>
</tr>
<tr>
<td><strong>고객 상세 팝업</strong></td>
<td>고객 ID 클릭 시 별도 팝업에서 고객정보 출력</td>
</tr>
<tr>
<td><strong>Top 5 고객 추출</strong></td>
<td>남은 일수 기준 Top 5 고객 추출 및 별도 ALV로 출력</td>
</tr>
<tr>
<td><strong>사용자 중심 인터페이스</strong></td>
<td>툴바 버튼 클릭 시 기능 이동 가능, 사용자 명령에 따라 화면 제어</td>
</tr>
</tbody></table>
<h2 id="프로그램-구조">프로그램 구조</h2>
<p>ZPORTFOLIO_ARS
├─ ZPORTPOLIO_ARS_TOP  : 전역변수 선언
├─ ZPORTPOLIO_ARS_SEL  : 선택화면(SELECTION-SCREEN)
├─ ZPORTPOLIO_ARS_C01  : ALV 이벤트 클래스
├─ ZPORTPOLIO_ARS_F01  : 데이터 조회, ALV 출력 Form들
├─ ZPORTPOLIO_ARS_I01  : 화면 입력 처리
└─ ZPORTPOLIO_ARS_O01  : ALV 출력 및 화면 구성</p>
<h2 id="💡-핵심-기능-설명">💡 핵심 기능 설명</h2>
<h3 id="📌-1-조건별-예약-데이터-조회-get_data">📌 1. 조건별 예약 데이터 조회 (GET_DATA)</h3>
<p>SELECTION-SCREEN에서 입력한 항공사, 항공편, 출발일 조건으로 예약 조회</p>
<pre><code class="language-abap">SELECTION-SCREEN BEGIN OF BLOCK BL01 WITH FRAME TITLE TEXT-001.
*   검색조건
PARAMETERS: p_air TYPE sbook-carrid OBLIGATORY,   
            p_conn   TYPE sbook-connid OBLIGATORY,    
            p_date   TYPE sbook-fldate.    
SELECTION-SCREEN END OF BLOCK BL01.

SELECTION-SCREEN BEGIN OF BLOCK BL02 WITH FRAME TITLE TEXT-002.
  SELECT-OPTIONS: s_cust FOR scustom-id NO-EXTENSION.
  PARAMETERS: r1 RADIOBUTTON GROUP rad DEFAULT &#39;X&#39;,
              r2 RADIOBUTTON GROUP rad,
              r3 RADIOBUTTON GROUP rad.
SELECTION-SCREEN END OF BLOCK BL02.

</code></pre>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/cb826333-9752-409c-b64c-3e0e460840ab/image.png" alt=""></p>
<p>r1/r2/r3 라디오버튼에 따라 전체 / 취소 / 미취소 필터링</p>
<br/>

<pre><code>START-OF-SELECTION.

  IF r1 = &#39;X&#39;.
    gv_cancelled = &#39;&#39;.
  ELSEIF r2 = &#39;X&#39;.
    gv_cancelled = space.  &quot; 취소되지 않은 예약
  ELSEIF r3 = &#39;X&#39;.
    gv_cancelled = &#39;X&#39;.    &quot; 취소된 예약
  ENDIF.

  PERFORM get_data.                      &quot; 1단계: 데이터 조회
  PERFORM apply_filter_condition.       &quot; 2단계: 필터 조건별 분류

END-OF-SELECTION.


IF GT_TABLE IS INITIAL.
  MESSAGE &#39;조회할 데이터가 없습니다.&#39; TYPE &#39;I&#39;.
ELSE.
  CALL SCREEN 100.
ENDIF.</code></pre><hr/>


<pre><code>FORM get_data.
  CLEAR gt_table.

  IF r1 = &#39;X&#39;.
   SELECT
    a~carrid, a~connid, a~fldate, a~bookid, a~customid, a~CUSTTYPE,
    a~loccuram, a~loccurkey, a~order_date, a~cancelled,
    b~carrname,
    c~name      AS custname,
    c~telephone
    INTO CORRESPONDING FIELDS OF TABLE @gt_table
    FROM  zsbook AS a
    INNER JOIN scarr AS b ON a~carrid = b~carrid
    INNER JOIN scustom AS c ON a~customid = c~id
    WHERE a~carrid   = @p_air
      AND a~connid   = @p_conn
      AND a~fldate   = @p_date
      AND a~customid IN @s_cust.

  ELSE.
    SELECT
      a~carrid, a~connid, a~fldate, a~bookid, a~customid, a~CUSTTYPE,
      a~loccuram, a~loccurkey, a~order_date, a~cancelled,
      b~carrname,
      c~name      AS custname,
      c~telephone
      INTO CORRESPONDING FIELDS OF TABLE @gt_table
      FROM  zsbook AS a
      INNER JOIN scarr AS b ON a~carrid = b~carrid
      INNER JOIN scustom AS c ON a~customid = c~id
      WHERE a~carrid   = @p_air
        AND a~connid   = @p_conn
        AND a~fldate   = @p_date
        AND a~customid IN @s_cust
        AND a~cancelled = @gv_cancelled.
   ENDIF.

ENDFORM.</code></pre><p>ZSBOOK, SCARR, SCUSTOM 조인 SELECT</p>
<h2 id="📌-2-남은-일수-계산--상태-아이콘-표시-set_status_icon">📌 2. 남은 일수 계산 + 상태 아이콘 표시 (SET_STATUS_ICON)</h2>
<p>예약일과 출발일 차이 계산</p>
<p>남은 일수 100 이상: 초록불, 50 이상: 노랑불, 그 외: 빨강불 표시</p>
<p>특정 조건(&gt;=150)은 셀 강조 색상까지 설정 (CELL COLOR)</p>
<p>전체조회 모드일때 취소된 예약은 빨간색 배경색 지정.
<img src="https://velog.velcdn.com/images/j_wisdom_h/post/8350f9e5-db4f-4dc1-bdc5-382ca9ad2f71/image.png" alt=""></p>
<pre><code class="language-abap">
&lt;== gt_table
...
DATA LINECOLOR(4)     TYPE C.   ==&gt; 라인 컬러
DATA CELLSCOL  TYPE LVC_T_SCOL. ==&gt; 셀 컬러
...
==&gt;
</code></pre>
<pre><code class="language-abap">FORM apply_filter_condition.
  LOOP AT gt_table INTO gs_table.
    IF r1 = &#39;X&#39;. &quot; 전체 조회일때
      IF gs_table-cancelled = &#39;X&#39;.
         gs_table-linecolor = &#39;C611&#39;.  ==&gt; 라인 컬러
      ENDIF.
    ENDIF.

    PERFORM set_status_icon CHANGING gs_table.
    MODIFY gt_table FROM gs_table.
  ENDLOOP.
ENDFORM.


FORM set_status_icon CHANGING gs_table LIKE gs_table.
  DATA: LS_COLOR TYPE LVC_S_SCOL. 
  DATA: lv_days_left TYPE i.

  lv_days_left =  gs_table-fldate - gs_table-order_date .
  gs_table-days = lv_days_left.

  IF lv_days_left &gt;= 150.
      LS_COLOR-FNAME = &#39;DAYS&#39;.
      LS_COLOR-COLOR-INT = 1.
      LS_COLOR-COLOR-INV = 1.
      LS_COLOR-COLOR-COL = 5.
      APPEND LS_COLOR TO gs_table-CELLSCOL. ==&gt; 셀 컬러
  ENDIF.

  IF lv_days_left &gt;= 100.
    gs_table-zstatus = ICON_LED_GREEN.  &quot; 초록불
  ELSEIF lv_days_left &gt;= 50.
    gs_table-zstatus = ICON_LED_YELLOW. &quot; 노랑불
  ELSE.
    gs_table-zstatus = ICON_LED_RED.    &quot; 빨강불
  ENDIF.
ENDFORM.

FORM set_layout .
  gs_layout-zebra = &#39;X&#39;.
  gs_layout-cwidth_opt = &#39;X&#39;.
  gs_layout-sel_mode   = &#39;D&#39;.
  gs_layout-info_fname =&#39;LINECOLOR&#39;. &quot; 라인 컬러
  gs_layout-ctab_fname = &#39;CELLSCOL&#39;. &quot; 셀 컬러
ENDFORM.</code></pre>
<h2 id="📌-3-고객-id-핫스팟-클릭-→-고객-상세-팝업-handle_hotspot_click">📌 3. 고객 ID 핫스팟 클릭 → 고객 상세 팝업 (handle_hotspot_click)</h2>
<p>ALV 내 CUSTOMID 필드에 Hotspot 설정</p>
<p>클릭 시 선택한 고객 ID를 gv_cid에 저장 후, SCREEN 200 호출</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/3415ac9d-a177-45fe-b879-dbf6f9cb737c/image.png" alt=""></p>
<pre><code>FORM set_fildcat .
  DEFINE _fcat.
    CLEAR gs_fcat.
    gs_fcat-fieldname  = &amp;1.
    gs_fcat-coltext    = &amp;2.
    gs_fcat-key        = &amp;3.
    gs_fcat-edit       = &amp;4.
    gs_fcat-outputlen  = &amp;5.
    gs_fcat-lowercase  = &amp;6.
    gs_fcat-col_pos    = &amp;7.
    APPEND gs_fcat TO gt_fcat.
  END-OF-DEFINITION.

  _fcat: &#39;ZSTATUS&#39;   &#39;상태&#39;             &#39;&#39;  &#39;&#39; &#39;3&#39;  &#39;&#39;  &#39;1&#39;,
         &#39;CARRID&#39;    &#39;Airline Code&#39;    &#39;X&#39; &#39;&#39; &#39;6&#39;  &#39;&#39;  &#39;2&#39;,
         &#39;CARRNAME&#39;  &#39;Airline Name&#39;    &#39;X&#39; &#39;&#39; &#39;20&#39; &#39;&#39;  &#39;3&#39;,
         &#39;CONNID&#39;    &#39;Flight No.&#39;      &#39;X&#39; &#39;&#39; &#39;6&#39;  &#39;&#39;  &#39;4&#39;,
         &#39;FLDATE&#39;    &#39;Flight Date&#39;     &#39;X&#39; &#39;&#39; &#39;10&#39; &#39;&#39;  &#39;5&#39;,
         &#39;BOOKID&#39;    &#39;Booking&#39;         &#39;X&#39; &#39;&#39; &#39;6&#39;  &#39;&#39;  &#39;6&#39;,
         &#39;CUSTOMID&#39;  &#39;Cust. No.&#39;       &#39;&#39;  &#39;&#39; &#39;10&#39; &#39;&#39;  &#39;7&#39;,
         &#39;CUSTNAME&#39;  &#39;Customer Name&#39;   &#39;&#39;  &#39;&#39; &#39;20&#39; &#39;&#39;  &#39;8&#39;,
         &#39;TELEPHONE&#39; &#39;Telephone No.&#39;   &#39;&#39;  &#39;&#39; &#39;15&#39; &#39;&#39;  &#39;9&#39;,
         &#39;CUSTTYPE&#39;  &#39;B/P Type&#39;        &#39;&#39;  &#39;&#39; &#39;4&#39;  &#39;&#39;  &#39;10&#39;,
         &#39;LOCCURAM&#39;  &#39;Amount&#39;          &#39;&#39;  &#39;&#39; &#39;10&#39; &#39;&#39;  &#39;11&#39;,
         &#39;LOCCURKEY&#39; &#39;Curr.&#39;           &#39;&#39;  &#39;&#39; &#39;5&#39;  &#39;&#39;  &#39;12&#39;,
         &#39;ORDER_DATE&#39; &#39;Booking Date&#39;   &#39;&#39;  &#39;&#39; &#39;10&#39; &#39;&#39;  &#39;13&#39;,
         &#39;DAYS&#39;      &#39;DAYS&#39;            &#39;&#39;  &#39;&#39; &#39;10&#39; &#39;&#39;  &#39;14&#39;,
         &#39;CANCELLED&#39; &#39;Cancelled&#39;       &#39;&#39;  &#39;&#39; &#39;1&#39;  &#39;&#39;  &#39;15&#39;.

* HOTSPOT 필드 설정
  LOOP AT gt_fcat INTO gs_fcat WHERE fieldname = &#39;CUSTOMID&#39;.
    gs_fcat-hotspot = &#39;X&#39;.
    MODIFY gt_fcat FROM gs_fcat.
  ENDLOOP.
ENDFORM.
</code></pre><hr />

<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/ecdbe584-47fc-4f18-bead-179260df79b2/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/794e174a-f4b0-4220-bf3c-4155a6e6e019/image.png" alt=""></p>
<p>스크린 200 
=&gt; Modal dialog box로 설정 
=&gt; 스크린페인터로 dictionary/program field 에서 scustom 필드 불러와서 그리기</p>
<hr>
<pre><code>FORM display_alv_0100 .
  CREATE OBJECT g_event_receiver.
  SET HANDLER g_event_receiver-&gt;handle_hotspot_click FOR go_grid.
  SET HANDLER g_event_receiver-&gt;HANDEL_TOOLBAR FOR go_grid.
  SET HANDLER g_event_receiver-&gt;HANDEL_USER_COMMAND FOR go_grid.

 CALL METHOD GO_GRID-&gt;SET_TABLE_FOR_FIRST_DISPLAY
    EXPORTING
      IS_LAYOUT                     = GS_LAYOUT
    CHANGING
      IT_OUTTAB                     = GT_TABLE
      IT_FIELDCATALOG               = GT_FCAT.

ENDFORM.</code></pre><p>GO_GRID 에 이벤트 리시버 등록</p>
<hr>
<pre><code>CLASS lcl_event_receiver DEFINITION DEFERRED.
DATA: g_event_receiver TYPE REF TO lcl_event_receiver.

*--------------------------------------------------
* 클래스 선언
*-------------------------------------------------
CLASS lcl_event_receiver DEFINITION.
  PUBLIC SECTION.

  &quot; 핫스팟 클릭 이벤트 
  METHODS : handle_hotspot_click
    FOR EVENT hotspot_click OF CL_GUI_ALV_GRID
    IMPORTING e_row_id e_column_id.

  &quot; 툴바 보여주는용 함수.
  METHODS : HANDEL_TOOLBAR
    FOR EVENT TOOLBAR OF CL_GUI_ALV_GRID
    IMPORTING E_OBJECT.

  &quot; 툴바 클릭시 발생하는 ucomm을 갖고 작동하는 함수.
  METHODS : HANDEL_USER_COMMAND
    FOR EVENT USER_COMMAND OF CL_GUI_ALV_GRID
    IMPORTING E_UCOMM.
ENDCLASS.
*--------------------------------------------
* 클래스 구현
*--------------------------------------------
CLASS lcl_event_receiver IMPLEMENTATION.

  METHOD handle_hotspot_click.
    DATA: tmp_gt_table LIKE LINE OF gt_table.
    READ TABLE gt_table INDEX e_row_id-index INTO tmp_gt_table.

    IF sy-subrc = 0.
      gv_cid = tmp_gt_table-customid. &quot;&gt; 선택된 고객id

      CALL SCREEN 200 STARTING AT 16   1
                      ENDING   AT 77  20.
    ENDIF.
  ENDMETHOD.

  METHOD HANDEL_TOOLBAR.
    PERFORM HANDEL_TOOLBAR USING E_OBJECT.
  ENDMETHOD.

  METHOD HANDEL_USER_COMMAND.
    PERFORM HANDEL_USER_COMM USING e_ucomm.
  ENDMETHOD.

ENDCLASS.
</code></pre><p>콜스크린 실행 결과 모달화면, PBO실행
<img src="https://velog.velcdn.com/images/j_wisdom_h/post/41ed0ea6-3782-440e-95e7-c7c991cfc21a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/dce17252-1800-4c8b-94df-9c9824b22b26/image.png" alt=""></p>
<hr>
<pre><code>MODULE status_0200 OUTPUT.

 SET TITLEBAR &#39;200&#39; WITH &#39;고객 정보&#39;.
 SET PF-STATUS &#39;ZSTAT&#39;.

ENDMODULE.

MODULE set_alv_0200 OUTPUT.

  PERFORM get_customer.

ENDMODULE.


FORM get_customer.

  SELECT SINGLE *
    FROM scustom
    WHERE id = gv_cid.

ENDFORM.</code></pre><p>선택된 id기준으로 scustom에서 정보 불러와서 자동으로 스크린에 SET.</p>
<h2 id="📌-4-툴바-버튼-→-빠른-예약-고객-top-5-조회-handle_toolbar">📌 4. 툴바 버튼 → 빠른 예약 고객 Top 5 조회 (handle_toolbar)</h2>
<p>사용자 정의 툴바 버튼에 SHOW 명령 연결</p>
<p>클릭 시 남은 일수 기준 Top 5 고객 정렬 → ALV 출력 (화면 250)</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/d7c8f35a-2fb1-402c-9c14-37bb4502f25c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/8eb97590-fd27-449c-a386-6f58b6f1dd64/image.png" alt=""></p>
<hr>
<pre><code>METHODS : HANDEL_TOOLBAR
  FOR EVENT TOOLBAR OF CL_GUI_ALV_GRID
  IMPORTING E_OBJECT.</code></pre><p>CL_GUI_ALV_GRID의 TOOLBAR 이벤트 발생 시 호출.
( e_object 객체를 전달받는 이벤트 핸들러 )</p>
<hr>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/7614b1d2-fb1a-4057-b11c-15bf52885095/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/b9d18d9f-dbe6-4c24-b383-3fbc31667ac9/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/b73b6ea7-814d-4715-b1c1-e6b40be5b252/image.png" alt=""></p>
<pre><code class="language-abap">
*&amp;---------------------------------------------------------------------*
*&amp; Form HANDEL_TOOLBAR
*&amp;---------------------------------------------------------------------*
*&amp; text
*&amp;---------------------------------------------------------------------*
*&amp;      --&gt; E_OBJECT
*&amp;---------------------------------------------------------------------*
FORM handel_toolbar  USING PO_OBJECT TYPE REF TO CL_ALV_EVENT_TOOLBAR_SET.
  DATA : LS_TOOLBAR TYPE STB_BUTTON. &quot; STB_BUTTON: 툴바 버튼의 속성

  LS_TOOLBAR-ICON = ICON_CUSTOMER.
  LS_TOOLBAR-FUNCTION = &#39;SHOW&#39;.
  LS_TOOLBAR-TEXT = &#39;Early Booked Top 5 Customer&#39;.
  APPEND LS_TOOLBAR TO PO_OBJECT-&gt;MT_TOOLBAR.

  CLEAR  ls_toolbar.
ENDFORM.</code></pre>
<table>
<thead>
<tr>
<th>항목</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>e_object</code></td>
<td><code>CL_ALV_EVENT_TOOLBAR_SET</code> 클래스의 참조 객체</td>
</tr>
<tr>
<td><code>-&gt;</code></td>
<td>해당 객체의 속성에 접근하는 참조 연산자</td>
</tr>
<tr>
<td><code>mt_toolbar</code></td>
<td><code>STB_BUTTON</code> 구조의 내부 테이블로 툴바 버튼 목록 저장소</td>
</tr>
<tr>
<td>접근 목적</td>
<td>ALV 툴바에 버튼을 추가하기 위해 사용됨</td>
</tr>
</tbody></table>
<p>SAP는 모든 이벤트마다 하나의 객체를 넘겨주고, 그 객체 내부 필드를 조작해서 원하는 기능을 추가하라는 구조를 채택함.
&quot;버튼 테이블만 채우면 알아서 툴바가 만들어지는&quot; 방식으로 일관성을 유지</p>
<hr>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/02901a49-96d9-4fe6-859c-7aac8312acf2/image.png" alt=""></p>
<pre><code>FORM handel_user_comm  USING    p_ucomm.
 CASE p_ucomm.
    WHEN &#39;SHOW&#39;.
      CALL SCREEN 250 STARTING AT 16   5
                      ENDING   AT 62  13.
  ENDCASE.
ENDFORM.</code></pre><p>ALV에서 사용자가 버튼을 클릭하거나 특정 동작을 할 때, SY-UCOMM 타입의 기능 코드를 E_UCOMM이라는 이름으로 이벤트 파라미터에 담아 전달.
(여기서는 툴바 클릭시)</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/cf9765db-3525-41c8-9614-059ca54a5b8f/image.png" alt="">
<img src="https://velog.velcdn.com/images/j_wisdom_h/post/5739e301-633e-4ab8-8bce-521e9dac4616/image.png" alt="">
<img src="https://velog.velcdn.com/images/j_wisdom_h/post/02feb25a-7fcb-43ce-ac77-769f1dd9fbbf/image.png" alt=""></p>
<pre><code class="language-abap">MODULE set_alv_0250 OUTPUT.

  IF GO_CUSTOM IS INITIAL.

    PERFORM create_object_instance_250.

    PERFORM get_customer_top5.

    PERFORM set_fildcat_250.

    PERFORM set_layout_250.

    PERFORM display_alv_0250.

  ENDIF.

ENDMODULE.</code></pre>
<pre><code>*&amp;---------------------------------------------------------------------*
*&amp; Form GET_CUSTOMER_TOP5
*&amp;---------------------------------------------------------------------*
*&amp; 전체 예약 데이터(GT_TABLE)에서 고객ID/이름/남은일수 정보 추출
*&amp; → 남은 일수 기준으로 정렬하여 Top 5 고객만 GT_CUST에 저장
*&amp;---------------------------------------------------------------------*
FORM get_customer_top5.

  &quot; 1. 초기화
  CLEAR gt_cust.

  &quot; 2. 예약 테이블 루프 → 고객 정보 구조에 매핑
  LOOP AT gt_table INTO gs_table.
    CLEAR gs_cust.
    MOVE-CORRESPONDING gs_table TO gs_cust.
    APPEND gs_cust TO gt_cust.
  ENDLOOP.

  &quot; 3. 남은 일수(DAYS) 기준 내림차순 정렬
  SORT gt_cust BY days DESCENDING.

  &quot; 4. 상위 5개 항목만 남기고 나머지 삭제
  DELETE gt_cust FROM 6 TO LINES( gt_cust ).

ENDFORM.

*&amp;---------------------------------------------------------------------*
*&amp; Form CREATE_OBJECT_INSTANCE_250
*&amp;---------------------------------------------------------------------*
FORM create_object_instance_250 .

  CREATE OBJECT GO_CUSTOM
    EXPORTING
      CONTAINER_NAME = &#39;CON1&#39;.   &quot;Custom Control 이름

  CREATE OBJECT GO_GRID2
    EXPORTING
      I_PARENT = GO_CUSTOM.
ENDFORM.</code></pre><p>커스텀 컨트롤러 생성 GO_CUSTOM</p>
<pre><code>
*&amp;---------------------------------------------------------------------*
*&amp; Form SET_FILDCAT_250
*&amp;---------------------------------------------------------------------*
*&amp; text
*&amp;---------------------------------------------------------------------*
*&amp; --&gt;  p1        text
*&amp; &lt;--  p2        text
*&amp;---------------------------------------------------------------------*
FORM set_fildcat_250 .

  DEFINE _fcat2.
    CLEAR GS_FCAT2.
    GS_FCAT2-FIELDNAME  = &amp;1.
    GS_FCAT2-COLTEXT    = &amp;2.
    GS_FCAT2-KEY        = &amp;3.
    GS_FCAT2-OUTPUTLEN  = &amp;4.
    GS_FCAT2-LOWERCASE  = &amp;5.
    APPEND GS_FCAT2 TO GT_FCAT2.
  END-OF-DEFINITION.

  _fcat2: &#39;CUSTOMID&#39;  &#39;고객ID&#39;    &#39;X&#39;  &#39;8&#39;   &#39;&#39;,
          &#39;CUSTNAME&#39;  &#39;고객명&#39;     &#39;&#39;  &#39;25&#39;  &#39;&#39;,
          &#39;DAYS&#39;      &#39;남은일수&#39;    &#39;&#39;  &#39;4&#39;   &#39;&#39;.

ENDFORM.</code></pre><pre><code>FORM display_alv_0250.
  APPEND cl_gui_alv_grid=&gt;mc_fc_excl_all TO GT_FUNCTIONS.

  CALL METHOD GO_GRID2-&gt;SET_TABLE_FOR_FIRST_DISPLAY
    EXPORTING
      IS_LAYOUT       = GS_LAYOUT2
      it_toolbar_excluding = GT_FUNCTIONS
    CHANGING
      IT_OUTTAB       = GT_CUST
      IT_FIELDCATALOG = GT_FCAT2.
ENDFORM.</code></pre><hr>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/11eb0dee-31f7-41b2-ab66-21b1239a7555/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>연산자</th>
<th>읽는 법</th>
<th>용도</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>-&gt;</code></td>
<td>인스턴스 연산자</td>
<td><strong>객체 인스턴스의 속성이나 메서드</strong> 접근</td>
<td><code>lo_grid-&gt;set_table_for_first_display</code></td>
</tr>
<tr>
<td><code>=&gt;</code></td>
<td>클래스 연산자</td>
<td><strong>클래스의 정적(static) 속성/메서드</strong> 접근</td>
<td><code>cl_gui_alv_grid=&gt;mc_fc_excl_all</code></td>
</tr>
</tbody></table>
<hr>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/7a8141e2-b7c4-4cc9-99f4-08970c9dd58d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/1c01721b-24c9-4a40-896f-c6ca73a8178b/image.png" alt="">
<img src="https://velog.velcdn.com/images/j_wisdom_h/post/6158a89b-f1a3-4b5b-9348-473c14ef2a70/image.png" alt="">
<img src="https://velog.velcdn.com/images/j_wisdom_h/post/e6c1961f-a8d0-443e-a1fb-2b02460504ea/image.png" alt=""></p>
<p>UI_FUNCTIONS : UI_FUNC(function code)로 이루어진 테이블</p>
<hr>
<p>MC_FC_EXCL_ALL은 SAP가 정의한 기능 코드 상수.</p>
<p>mc_fc_excl_all = &#39;&amp;EXCLALLFC&#39; (상수. 펑션코드).</p>
<p>이 값이 UI_FUNC 타입 테이블 (GT_FUNCTIONS)에 들어가면,
SAP는 “ALV 표준 기능 전부 비활성화”라고 인식</p>
<p><strong>it_toolbar_excluding</strong>: 특정 툴바 버튼을 제외하기 위한 목록 → 위에서 &#39;mc_fc_excl_all&#39;을 넣었으므로 모든 버튼이 제거됨</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/f8573fd2-2781-4d9d-afd8-2b72028fc3e0/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Fiori launch pad]]></title>
            <link>https://velog.io/@j_wisdom_h/Fiori-launch-pad</link>
            <guid>https://velog.io/@j_wisdom_h/Fiori-launch-pad</guid>
            <pubDate>Wed, 11 Jun 2025 06:32:39 GMT</pubDate>
            <description><![CDATA[<p>Fiori Launchpad. 웹 기반의 SAP 화면</p>
<p><strong>TCODE : STC01</strong></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/cefbe75f-7b9b-4fe1-b01d-41dafa1795ee/image.png" alt=""></p>
<p>Task list SAP-GATEWAY-BASIC-CONFIG does not exist</p>
<p>Message no. STC_TM013</p>
<p><a href="https://help.sap.com/docs/SAP_FIORI_OVERVIEW/17ca92fbe8f54d9d8dfbe830cbb0c8d2/bfd1b053a647e842e10000000a4450e5.html?version=4_S4H1909.01">
<img src="https://velog.velcdn.com/images/j_wisdom_h/post/c724f436-ce4a-454a-a047-104fa10608f7/image.png" alt="">
</a></p>
<p>대신 SAP_GW_FIORI_ERP_ONE_CLNT_SETUP 을 실행( 베이직 컨피그와 , 피오리 런치패드 초기화 셋업 포함되어있음)</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/3828b1ce-60ad-4faf-ba25-6d9f2cd8660d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/a4784392-8a7f-4dbb-971a-29d981223c69/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/8564efa9-95a0-46a5-a13a-d93bcd800f3c/image.png" alt="">
홈화면에 피오리 런치패드 즐겨찾기 추가됨</p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/817446de-02a9-4bcd-8eba-5c665b3ff94f/image.png" alt="">
지금 상태로는 연결안됨. 호스트 설정과 포트포워딩 설정 필요.</p>
<p><strong>호스트 설정</strong></p>
<p>C드라이버\ Windows\ System32\drivers\etc</p>
<p>127.0.0.1 vchcl.dummy.no-domain</p>
<p>127.0.0.1 localhost</p>
<p>내 컴퓨터에서 localhost 또는 vchcl.dummy.no-domain으로 접속 시 항상 127.0.0.1(즉, 내 PC 자신)로 연결됩니다.</p>
<p>HTTP 쓸 수 있는 게이트웨이
=&gt; 8000번, 34300번</p>
<p><strong>포트포워딩 설정</strong></p>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/c9457cf8-02a2-41f2-821c-86888e242074/image.png" alt=""></p>
<blockquote>
<p>호스트 IP/포트: 내 PC(호스트)에서 접근할 때 사용할 주소와 포트 (예: 127.0.0.1:3200)</p>
</blockquote>
<blockquote>
<p>게스트 IP/포트: 가상머신(게스트) 내부에서 실제 서비스가 동작하는 주소와 포트 (예: 10.0.2.15:3200)</p>
</blockquote>
<blockquote>
<p>예를 들어, 내 PC에서 127.0.0.1:3200으로 SAP GUI 클라이언트로 접속하면, 이 요청이 자동으로 가상머신의 10.0.2.15:3200으로 전달됩니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/j_wisdom_h/post/f2e492c3-05b5-4368-bada-ff903d65961a/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CDS 뷰(Core Data Services View)]]></title>
            <link>https://velog.io/@j_wisdom_h/CDS-%EB%B7%B0Core-Data-Services-View</link>
            <guid>https://velog.io/@j_wisdom_h/CDS-%EB%B7%B0Core-Data-Services-View</guid>
            <pubDate>Mon, 09 Jun 2025 10:42:09 GMT</pubDate>
            <description><![CDATA[<p>💡 <strong>SAP CDS 뷰(Core Data Services View)</strong></p>
<h2 id="1-cds-뷰란-무엇인가">1. CDS 뷰란 무엇인가?</h2>
<p>CDS 뷰는 <strong>SQL 기반의 뷰(View)</strong>를 보다 강력하게 확장한 SAP의 데이터 정의 언어입니다.<br>기존 ABAP SQL이 가진 한계를 극복하며, 성능과 가독성 모두를 갖춘 데이터 모델링 방식으로 각광받고 있습니다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>CDS 뷰</th>
<th>SQL 뷰</th>
</tr>
</thead>
<tbody><tr>
<td><strong>기능</strong></td>
<td>클라이언트 처리, 권한 관리, 시멘틱 정보 제공, 최신 SQL 기능 사용 가능</td>
<td>DB 개체 표현, 제한적 기능</td>
</tr>
<tr>
<td><strong>생성 위치</strong></td>
<td>ABAP 레포지토리에서 정의, DB 직접 생성 아님</td>
<td>DB 내부에 직접 생성</td>
</tr>
<tr>
<td><strong>ABAP Dictionary</strong></td>
<td>OpenSQL로 조회 가능</td>
<td>조회만 가능, 수정 불가</td>
</tr>
<tr>
<td><strong>클라이언트 필드</strong></td>
<td>사용 가능</td>
<td>일반적으로 미사용</td>
</tr>
<tr>
<td><strong>시멘틱 정보</strong></td>
<td>제공함</td>
<td>제공하지 않음</td>
</tr>
</tbody></table>
<p>✅ <strong>CDS 뷰는 성능, 구조화, 연동 측면에서 기존 SQL 뷰보다 유리합니다.</strong></p>
<hr>
<h2 id="2-cds-뷰-개발-환경-구축">2. CDS 뷰 개발 환경 구축</h2>
<p>CDS 뷰는 <strong>Eclipse 기반 개발환경</strong>에서 작성됩니다.</p>
<h3 id="✅-환경-구축-절차">✅ 환경 구축 절차</h3>
<ol>
<li>Eclipse 설치  </li>
<li><code>Help &gt; Install New Software</code> → SAP HANA 플러그인 설치  </li>
<li>SAP BTP 또는 ABAP 플랫폼 선택 후 설치 진행  </li>
<li><code>New &gt; ABAP Project</code> 생성 후 SAP 서버 접속 (ID, PW, Client 입력)  </li>
<li>ABAP 프로젝트 내에서 CDS 뷰 작성</li>
</ol>
<h3 id="✍️-cds-뷰-생성-위치">✍️ CDS 뷰 생성 위치</h3>
<ul>
<li>ABAP 프로젝트 내부의 패키지<br>→ <code>New &gt; Other &gt; Core Data Services &gt; Data Definition</code></li>
</ul>
<hr>
<h2 id="3-cds-뷰-문법-및-활용-예시">3. CDS 뷰 문법 및 활용 예시</h2>
<p>CDS 뷰는 SQL SELECT 구문과 유사하게 작성되며, 다양한 <strong>내장 함수 및 연산</strong>을 지원합니다.</p>
<h3 id="✅-기본-문법-예시">✅ 기본 문법 예시</h3>
<pre><code class="language-sql">@AbapCatalog.sqlViewName: &#39;ZVIEW_EXAMPLE&#39;
define view ZCDS_SALES as select from sflight
{
  carrid,
  connid,
  seatsocc,
  price,
  round( price / seatsocc, 2 ) as avg_price_per_seat
}</code></pre>
<h2 id="✅-기존-abap-sql의-한계-vs-cds-뷰의-개선-예시">✅ 기존 ABAP SQL의 한계 vs CDS 뷰의 개선 예시</h2>
<h3 id="📌-예시-1-다단계-조인--읽기-복잡성">📌 예시 1: 다단계 조인 / 읽기 복잡성</h3>
<h4 id="❌-abap-open-sql">❌ ABAP Open SQL</h4>
<p>기존 ABAP에서는 여러 테이블을 조인하려면 반복적으로 SELECT, LOOP, READ TABLE 등의 복잡한 구문을 사용해야 했습니다.</p>
<pre><code>SELECT * FROM sflight INTO TABLE @DATA(lt_sflight).

LOOP AT lt_sflight INTO DATA(ls_sflight).
  SELECT SINGLE carrname INTO @DATA(lv_carrname)
    FROM scarr
    WHERE carrid = @ls_sflight-carrid.
ENDLOOP.`</code></pre><h4 id="✅-cds-뷰">✅ CDS 뷰</h4>
<p>CDS 뷰에서는 조인을 뷰 정의에 한 번에 처리할 수 있어 간결하고 성능도 뛰어납니다.</p>
<pre><code>define view ZCDS_FLIGHT_INFO as
  select from sflight
    inner join scarr on sflight.carrid = scarr.carrid
{
  sflight.carrid,
  scarr.carrname,
  sflight.connid,
  sflight.price
}</code></pre><h3 id="📌-예시-2-계산-필드">📌 예시 2: 계산 필드</h3>
<h4 id="❌-abap-open-sql-1">❌ ABAP Open SQL</h4>
<p>ABAP에서는 계산 로직이 SQL에서 어렵고 보통 ABAP 코드에서 처리함:</p>
<pre><code>DATA(avg_price) = price / seatsocc.`</code></pre><h4 id="✅-cds-뷰-1">✅ CDS 뷰</h4>
<p>계산된 필드를 뷰 정의 안에서 바로 처리할 수 있습니다:</p>
<pre><code>round( price / seatsocc, 2 ) as avg_price_per_seat</code></pre><h3 id="📌-예시-3-시멘틱--권한-제어-내장">📌 예시 3: 시멘틱 &amp; 권한 제어 내장</h3>
<p>ABAP SQL은 단순한 데이터 읽기만 가능하며, 권한 제어나 필드 설명은 추가 코드로 구현해야 합니다.</p>
<p>CDS는 @Annotations를 통해 뷰에 권한(Authorization), 설명, UI 속성 등을 바로 부여할 수 있습니다.</p>
<pre><code>@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: &#39;항공편 요약 뷰&#39;</code></pre><p>✅ 요약</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>ABAP SQL</th>
<th>CDS 뷰</th>
</tr>
</thead>
<tbody><tr>
<td>다단계 조인</td>
<td>복잡한 중첩 SELECT &amp; LOOP</td>
<td>단일 SELECT 정의 가능</td>
</tr>
<tr>
<td>계산 필드</td>
<td>코드로 수동 계산</td>
<td>뷰 내장 수식으로 처리</td>
</tr>
<tr>
<td>시멘틱/권한 관리</td>
<td>구현 코드 별도 필요</td>
<td>Annotation으로 직접 설정</td>
</tr>
<tr>
<td>가독성</td>
<td>SQL + ABAP 섞임</td>
<td>깔끔한 SQL 기반 정의</td>
</tr>
<tr>
<td>퍼포먼스</td>
<td>런타임에서 처리</td>
<td>DB 레벨에서 HANA 최적화 실행</td>
</tr>
</tbody></table>
]]></description>
        </item>
    </channel>
</rss>