🔷 1. SAP의 3계층 아키텍처 구조

SAP 시스템은 보통 다음 3가지 계층으로 구성됨 client, app, database server.

계층 설명
Presentation (Client) 사용자가 SAP GUI 등으로 작업
Application Server ABAP 프로그램 실행, 로직 처리
Database Server 데이터 저장, 조회, 수정 담당

📌 즉, 사용자는 SAP GUI에서 프로그램을 실행하면, Application 서버가 그걸 받아서 DB와 통신하게 된다.


🔷 2. 여러 사용자가 동시에 접근하는 상황

  • 기업에서는 수백, 수천 명의 사용자가 동시에 같은 데이터를 조회/수정할 수 있음
  • 따라서 ABAP 프로그램을 만들 땐, "동시성" 을 고려해야함!
  • 예: A가 수정 중일 때 B가 읽으면, 최신 데이터가 아닐 수 있음 → 주의 필요

🔷 3. Open SQL이란?

구분 설명
일반 SQL 데이터베이스에 직접 명령
Open SQL ABAP 프로그래밍에서 SAP가 제공하는 DB 인터페이스 언어
  • SELECT, INSERT, UPDATE, DELETE 같은 SQL 문장을 ABAP에서 사용 가능
  • 하지만 실제로는 직접 DB에 접근하는 게 아닌, ABAP Dictionary를 통해 간접적으로 DB 접근

📌 즉, ABAP에서 직접 DB를 건드리지 않고, SAP가 제공한 안전한 경로로 접근하는 것 → 안정성과 보안 보장


🔷 4. Buffering (버퍼링)

개념설명
버퍼링 DB 접근을 줄이기 위해, 최근 데이터를 메모리에 잠깐 저장해두는 기술
장점 속도 향상
단점 실시간 데이터가 아닐 수 있음
  • 예: 마스터 데이터(MARA, 고객정보)는 자주 바뀌지 않아서 버퍼링 O
  • 예: 거래 데이터(주문, 재고 등)는 자주 바뀌므로 버퍼링 X

✅ 테이블을 버퍼링 사용 안 함으로 설정하면 그래서 항상 최신 데이터를 DB에서 직접 가져오게됨

버퍼링되어있다면, 데이터가 최신데이터가 아닐 수 있음!


🔷 5. 요약 정리

 

핵심 포인트설명
💡 Open SQL ABAP에서 DB에 안전하게 접근하는 수단 (ABAP Dictionary 통해 간접 접근)
💡 버퍼링 읽기 속도는 빠르지만 최신 데이터가 아닐 수 있음
💡 트랜잭션성 여러 사용자가 동시에 작업해도 안정적으로 작동해야 함
💡 실시간 데이터 필요시 버퍼링 끄고 항상 최신 DB에서 직접 가져오기

 

 

 

 

✅ Include Structure란?

Include Structure는 여러 테이블이나 프로그램에서 재사용 가능한 필드 묶음(구조체) 
Append Structure와 비슷하지만 재사용성과 관리성에서 차이가있음


🔍 Append Structure vs Include Structure

항목 Append Structure Include Structure
목적 테이블 확장 (보통 1회성) 재사용 가능한 구조 정의
적용 위치 테이블 끝에만 추가 가능 테이블 중간에 원하는 위치에 삽입 가능
재사용 가능성 ❌ (하나의 테이블에만 사용) ✅ 여러 테이블이나 프로그램에서 사용 가능
일반적으로 사용되는 경우 표준 테이블 확장 (SAP 보호 목적) 여러 테이블에 동일한 필드 세트가 필요할 때
SAP 표준과의 충돌 위험 없음 없음

📌 Include Structure의 특징

  • 반드시 flat 구조여야 함 (다른 구조체 포함 불가)
  • 각 필드의 길이는 16자 이하여야 함
  • 테이블 중간에 삽입 가능 (커서 위치 바로 위에 삽입됨)
  • 키 필드로 사용하고 싶다면 테이블 상단에 위치해야 함
  • 삽입 후 테이블에서 실제 컬럼처럼 보이고 사용됨

🧪 예시: Include Structure 만들기

  1. SE11에서 테이블을 수정 모드로 엶
  2. 삽입할 위치에 커서 올려둠
  3. Edit > Include > Insert 메뉴 클릭
  4. 구조체 이름 예: ZEMPL_INC
  5. 구조체 안에 ZZLOCATION, ZZDIVISION 같은 필드를 생성
  6. 저장 & 활성화

결과

EMP_IDNAMEZZLOCATIONZZDEPARTMENT
1001 Alice Seoul IT
1002 Bob Busan HR
  • ZZLOCATION → Include Structure에서 추가된 필드
  • ZZDEPARTMENT → Append Structure에서 추가된 필드

💡 팁

  • 표준 테이블에 동일한 필드를 여러 번 확장해야 할 때는 Include Structure를 만들어두고 재사용하는 것이 좋다
  • 실습에서는 우리가 만든 Z 테이블에 Include를 붙여봤지만, 표준 테이블(MARA, KNA1 등) 에도 동일하게 적용 가능
  • 변경 모드에서 필드가 안 보일 때는 Expand Include 버튼을 눌러야 보임!

Append Structure란?

- 테이블에 필드를 직접 추가하지 않고, 옆에 붙이는 구조.

장점

1. 테이블 구조를 직접 수정하지 않아도 됨

2. 테이블을 업데이트해도 문제 없음 (특히, 스탠다드 테이블!)

3. 하나의 테이블에 여러 팀이 각각 Append Structure 추가 가능함

 

하지만, 실습을 해도 약간 이해가 안갔다..🚨🚨🚨

뭔소리여

문제를 파악하면서 내가 궁금했던 2가지가 있었다.

첫번째로 append structure를 쓰면 왜 안전하다는 것인가?와,

두번째로는 append structure은 c의 구조체와 비슷한것인가?다. 데이터가 필드화되는 것인지 가 궁금했다.

 

방식 특징 위험 
직접 필드 추가 테이블 안에 직접 추가함 🚨업데이트 충돌 가능
Append Structure 사용 테이블은 그대로, 외부에서 확장함 ✅안전하게 유지 가능

 

 

 스탠다드 테이블

SAP가 기본적으로 제공하는 테이블

- 사용자가 만든 게 아니라, SAP 시스템 안에 기본 내장되어 있는 테이블

- FI, MM, CO모듈은 이 테이블을 사용해서 관리를 한다.

예시로 자재관리 모듈의 대표적 스탠다드 테이블은 아래와 같다

테이블명 설명
MARA 자재의 일반 정보 (General material data)
MARC 자재의 플랜트 단위 정보 (Plant data for material)
MARD 자재의 저장 위치 정보 (Storage location data)
EKKO 구매 오더 헤더 정보 (Purchase Order Header)
EKPO 구매 오더 항목 정보 (Purchase Order Item)

 

☑️ 특징

특징 설명
SAP가 제공 기본적으로 SAP 안에 포함되어 있음
수정 불가 테이블 구조를 직접 수정할 수 없음
확장은 가능 → 그래서 Append Structure를 사용함
클라이언트 구분 필드 있음 대부분 MANDT 필드 포함

 

📦 MARA란?

MARA는 SAP 시스템에서 모든 자재(Material)에 대한 일반 정보를 담고 있는 기본 테이블

  • MATNR: 자재 번호
  • ERSDA: 생성 일자
  • ERNAM: 생성자
  • MTART: 자재 유형
  • MEINS: 기본 단위
  • …등 자재에 대한 공통 정보들이 들어있다.

Mara 테이블에서 필요한 자재의 정보를 회사에 맞춰 쓰면 된다.

 

그런데, Mara테이블에 없는 필드를 추가하려고 할 때, 그냥 필드로 테이블안에 직접 추가한다면,

추후 SAP의 업데이트로 인해 업데이트 충돌 기능이 생겨날 수 있다...!

Append Strucure을 쓰면, SAP에서 테이블을 업데이트하더라도,

-기존 SAP필드만 갱신

-Append Structure는 따로 관리되기에,

추후 업데이트가 있어도 사라지지 않고 유지보수관점에서 안전한다

 

-> 이로써 "왜 appened structure가 안전한가?"에 대한 첫번째 질문 해결.

 

 

Append Structure은 C의 구조체와 비슷한가? 어떤 형식으로 되는지?

비슷한 점 - "필드 묶음"이라는 점

C의 structure처럼 append structure도 여러 개의 필드를 묶어서 테이블에 추가하는 단위

다른 점 - append structure은 테이블에 직접 확장되는 방식 => 테이블에 붙여서 사용하는 용도

 

Append Structure은 하나의 필드가 아니라, 여러개의 필드를 담은 묶음을 테이블에 확장하는 용도이다.

 

Append Structure ZZCUSTOMER_EXTRA
- ZZSALES_REGION    TYPE ZSALES_REGION   " 영업 구역
- ZZBIRTHDATE       TYPE D               " 생년월일
- ZZMEMBERSHIP_LVL  TYPE CHAR2           " 멤버십 등급

 

 

고객번호 (KUNNR)고객명 (NAME1)지역 (ORT01)마케팅코드 (ZZMARKETING_CODE)생일 (ZZBIRTH)포인트 (ZZPOINT)

100001 홍길동 서울 MKT-001 1990-05-12 1200
100002 김영희 부산 MKT-002 1985-11-02 800

 

Append Structure을 쓰면 이런식으로 행에 추가적으로 붙어서 나온다. == 테이블 확장!

 

문제 설명

방금그곡

라디오를 자주 듣는 네오는 라디오에서 방금 나왔던 음악이 무슨 음악인지 궁금해질 때가 많다. 그럴 때 네오는 다음 포털의 '방금그곡' 서비스를 이용하곤 한다. 방금그곡에서는 TV, 라디오 등에서 나온 음악에 관해 제목 등의 정보를 제공하는 서비스이다.

네오는 자신이 기억한 멜로디를 가지고 방금그곡을 이용해 음악을 찾는다. 그런데 라디오 방송에서는 한 음악을 반복해서 재생할 때도 있어서 네오가 기억하고 있는 멜로디는 음악 끝부분과 처음 부분이 이어서 재생된 멜로디일 수도 있다. 반대로, 한 음악을 중간에 끊을 경우 원본 음악에는 네오가 기억한 멜로디가 들어있다 해도 그 곡이 네오가 들은 곡이 아닐 수도 있다. 그렇기 때문에 네오는 기억한 멜로디를 재생 시간과 제공된 악보를 직접 보면서 비교하려고 한다. 다음과 같은 가정을 할 때 네오가 찾으려는 음악의 제목을 구하여라.

  • 방금그곡 서비스에서는 음악 제목, 재생이 시작되고 끝난 시각, 악보를 제공한다.
  • 네오가 기억한 멜로디와 악보에 사용되는 음은 C, C#, D, D#, E, F, F#, G, G#, A, A#, B 12개이다.
  • 각 음은 1분에 1개씩 재생된다. 음악은 반드시 처음부터 재생되며 음악 길이보다 재생된 시간이 길 때는 음악이 끊김 없이 처음부터 반복해서 재생된다. 음악 길이보다 재생된 시간이 짧을 때는 처음부터 재생 시간만큼만 재생된다.
  • 음악이 00:00를 넘겨서까지 재생되는 일은 없다.
  • 조건이 일치하는 음악이 여러 개일 때에는 라디오에서 재생된 시간이 제일 긴 음악 제목을 반환한다. 재생된 시간도 같을 경우 먼저 입력된 음악 제목을 반환한다.
  • 조건이 일치하는 음악이 없을 때에는 “(None)”을 반환한다.

입력 형식

입력으로 네오가 기억한 멜로디를 담은 문자열 m과 방송된 곡의 정보를 담고 있는 배열 musicinfos가 주어진다.

  • m은 음 1개 이상 1439개 이하로 구성되어 있다.
  • musicinfos는 100개 이하의 곡 정보를 담고 있는 배열로, 각각의 곡 정보는 음악이 시작한 시각, 끝난 시각, 음악 제목, 악보 정보가 ','로 구분된 문자열이다.
  • 음악의 시작 시각과 끝난 시각은 24시간 HH:MM 형식이다.
  • 음악 제목은 ',' 이외의 출력 가능한 문자로 표현된 길이 1 이상 64 이하의 문자열이다.
  • 악보 정보는 음 1개 이상 1439개 이하로 구성되어 있다.

출력 형식

조건과 일치하는 음악 제목을 출력한다.

 

입출력 예시

 

문제 풀이


1. 문제를 잘게잘게 쪼개서 어떤 것 부터 풀어야할 지 생각했다.

2. 먼저 주어진 musicinfos에 있는 String 배열을 쪼개야겠다는 생각을 했다.

3. 12:00-12:14 분 차이를 구하고, 그 분동안 곡들이 반복하는 것을 구상했다.

4. 배열 속에 n분 만큼 곡들을 반복하고 입력값이 같은 지 확인하면 된다.

5. contains함수를 사용한다.

첫 풀이 => 실패

import java.io.*;
import java.util.*;

class Solution {
    
    public int remainTime(String start, String end)
    {
        String[] s = start.split(":");
        String[] e = end.split(":");
        int startMin = Integer.parseInt(s[0] * 60) + Integer.parseInt(s[1]);
        int endMin = Integer.parseInt(e[0] * 60) + Integer.parseInt(e[1]);
        
        return endMin - startMin;   
    }
    
    
    
    public String solution(String m, String[] musicinfos) {
        String answer = "(None)";
        for (String info : musicinfos)
        {
            String[] parts = info.split(",");
            int times = 0;
            String title;
            String lyrics;
            times = remainTime(parts[0], parts[1]);
            title = parts[2];
            lyrics = parts[3];
            StringBuilder sb = new StringBuilder();
             for (int i=0; i<times; i++)
                sb.append(lyrics.charAt(i % lyrics.length()));
            String fullLyrics = sb.toString();
            if (fullLyrics.contains(m))
                answer = title;
        }
                
        
        
        return answer;
    }
}

 

C#, D#을 생각안하고 contains비교했더니 실패가 떴다.

정답풀이

import java.io.*;
import java.util.*;

class Solution {
    
    public int remainTime(String start, String end)
    {
        String[] s = start.split(":");
        String[] e = end.split(":");
        int startMin = Integer.parseInt(s[0]) * 60 + Integer.parseInt(s[1]);
        int endMin = Integer.parseInt(e[0]) * 60 + Integer.parseInt(e[1]);
        
        return endMin - startMin;   
    }
    
    private String convertMelody(String s)
    {
        return s.replaceAll("C#", "c")
            .replaceAll("D#", "d")
            .replaceAll("F#", "f")
            .replaceAll("G#", "g")
            .replaceAll("A#", "a")
            .replaceAll("B#", "b");
    }
    
    
    public String solution(String m, String[] musicinfos) {
        String answer = "(None)";
        m = convertMelody(m);
        int maxTime = -1;
        for (String info : musicinfos)
        {
            String[] parts = info.split(",");
            int times = remainTime(parts[0], parts[1]);
            String title = parts[2];
            String lyrics = convertMelody(parts[3]);
            
            StringBuilder sb = new StringBuilder();
            for (int i=0; i<times; i++)
                sb.append(lyrics.charAt(i % lyrics.length()));
            String fullLyrics = sb.toString();
            if (fullLyrics.contains(m) && times > maxTime)
            {
                answer = title;
                maxTime = times;
            }
        }
        return answer;
    }
}

C#과 같은 음악을 다른 수로 치환해주며, 비교를 하니까 답이 됐다.

시간 같은 경우도 생각안하고 제출했는데 20, 27,28에서 오류가 나서 times maxTime을 비교했다.

 

현재 직원관리 table에서 직원의 이름, 나이, 성별, 월급, 통화종류등이 관리되고 있다.

Salary의 월급과 통화를 연결시켜주기 위해서는 외래키 등록이 필요하다.

       +--------------------------+
       |       EMPLOYEE           |
       +--------------------------+
       | EmpID (PK)               |
       | Name                     |
       | Salary                   |
       | CurrencyKey (FK)  <------|--------------------+
       +--------------------------+                    |
                                                       |
                                                       v
       +--------------------------+          +--------------------------+
       |       CURRENCY           |          |   (참조 테이블)          |
       +--------------------------+          +--------------------------+
       | CurrencyKey (PK)         |          | CurrencyKey (PK)         |
       | CurrencyName             |          | CurrencyName             |
       +--------------------------+          +--------------------------+

 

Employee 테이블:

- 직원 정보를 저장하고, Salary급여와 함꼐 CURRENCY KEY 통화키를 보유함

CURRENCY 테이블:

- 통화정보를 저장함

- CurrencyKey는 기본키(PK)이고, Employee 테이블의 currencyKey 외래키가 이 값을 참조함

=> 이렇게 외래키를 설정하면 통화필드에 잘못된 값이 들어가는 것을 방지할 수 있고 드롭다운 목록을 통해 사용자가 미리 정의된 통화갑만 선택할 수 있도록 유도가능

 

 

이제 통화를 선택할 수 있는 드롭다운이 생긴다.

 

 

로제(직원이름)는 USD로 통화를 받는다.

📌 투명테이블 수정

 

 

1️⃣ 기존 테이블 확인 (ZEMPLOYEES)

  • SE11 (ABAP Dictionary) 트랜잭션 실행
  • ZEMPLOYEES 테이블을 조회
  • 테이블에 존재하는 6개의 필드 확인:
  • MANDT, EMPLOYEE, SURNAME, FORENAME, TITLE, DOB

🔹 MANDT는 SAP 시스템이 자동으로 관리하는 클라이언트 필드다.

 

2️⃣ 주의사항: 테이블 수정 시 위험

 

테이블을 수정할 때, 특히 Key 필드를 수정하거나 제거하면 데이터 손실이 발생할 수 있음!

  • SAP 시스템은 가능한 경우 기존 테이블을 수정하지만,
  • 구조가 크게 바뀌면 기존 테이블을 삭제하고 새로 만들기도 함.
  • 중요: 테이블을 바꾸기 전에 반드시 백업 또는 복제!

 

3️⃣ 안전한 방법: 테이블 복사

  • ZEMPLOYEES 테이블을 복사하여 ZEMPLOYEES2 테이블 생성 (Ctrl+F5)
  • 이때 복사되는 건 구조만이며, 데이터는 복사되지 않음
  • 복사 후에는 테이블 활성화 필요
  • ZEMPLOYEES2 테이블에 몇 개의 더미 데이터 입력 (직원 3명)

 

 

4️⃣ 필드 추가: ZINITIALS, ZGENDER, ZSALARY

 

 

5️⃣ 오류 발생: 통화 필드 없음

통화 필드 없이 ZSALARY 필드를 활성화하려고 하면 오류 발생

 

6️⃣ 통화 필드 추가:  ZECURRENCY

  • 필드명: ZECURRENCY
  • 기존에 존재하는 데이터 엘리먼트 CURRENCY(CURCY) 사용
  • 길이: 5자리 문자
  • 이후 ZSALARY 필드의 “통화 필드 참조” 탭에서 다음을 지정:
    • 참조 필드: ZECURRENCY
    • 참조 테이블: 현재 테이블명 (ZEMPLOYEES2)

ZSALARY 필드가 ZECURRENCY 필드를 통화 키로 사용함을 명시

 

 

단계설명

✅ 테이블 복사 구조만 복사되고, 데이터는 복사되지 않음
⚠️ 수정 전 주의 키 필드 변경 시 데이터 손실 가능성 있음
➕ 필드 추가 데이터 엘리먼트/도메인과 함께 추가
💰 화폐 필드 CURR 타입은 반드시 CURRENCY 키 필드가 필요
✔️ 참조 설정 화폐 필드에는 반드시 참조 필드/테이블 지정 필요

 

디버깅 버튼(shift+f5)를 누르면, 프로그램의 첫줄부터 디버거가 실행된다.

- 코드내에서 BreakPoint를 설정할 수도 있다.

- Place the cursor on the line and click the stop icon.

=> 멈추고 싶은 코드 줄에 커서를 두고 정지 아이콘(🛑) 을 클릭한다.

- When we execute the program, it will stop at that line and enter the debugger.

프로그램을 실행하면 해당 줄에서 자동으로 멈추며 디버깅 모드로 진입한다.

=✅ 이 방법이 가장 많이 사용됨: 처음부터 디버깅할 필요 없이, 문제가 의심되는 위치부터 추적 가능

 


 

✅ 디버깅 단축키 & 기능

 

F5 Single Step 한 줄씩 실행 (함수 안으로 들어감)
F6 Execute 함수/폼 등 블록 단위 실행 (들어가지 않음)
F7 Return 현재 함수/폼 나머지 실행 후 복귀
F8 Continue 전체 프로그램 실행 또는 커서 위치까지 

Sub-Fields : 필드안의 특정 문자위치를 지정해서 사용

DATA : int_tel_num(17) TYPE c,
country_code(3) TYPE c,
telephone_num(14) TYPE c.

int_tel_num = '+82-(0)207-123456'.
WRITE int_tel_num.
SKIP.

country_code = int_tel_num(3).
telphone_num = int_telphone_num+4(13).
WRITE / country_code.
WRITE / telephone_num.

country_code+1(2) = '01'.
WRITE / country_code.

 

 

 

int_tel_num+4(13)은 4번째 인덱스부터 13개의 문자열을 쓰겠다는 의미!

SPLIT 함수  : 여러 필드로 문자열을 구분자를 기준으로 나누는 문

: 하나의 문자열을 여러개의 필드로 분리하고 싶을 때 사용


DATA: mystring(30),
      a1(10),
      a2(10),
      a3(10),
      sep2(2) VALUE '**'.

 

- mystring: 전체 문자열 (30자)

- a1, a2, a3: 나눠 담을 각 필드 (10자씩)

- sep2: 구분자로 사용할 문자열, '**'

 

예제 - 기본 나누기

mystring = ' 01234**ABCD**6789'.
SPLIT mystring AT sep2 INTO a1 a2 a3.

 

변수 내용

a1 ' 01234'
a2 'ABCD'
a3 '6789'

모든 필드의 길이는 10이기 때문에 남은 공간은 자동으로 공백으로 채워짐

 

 

만약 필드 수보다 많은 분할을 한다면?

 

mystring = ' 01234**ABCD**6789**XYZ'.
SPLIT mystring AT sep2 INTO a1 a2 a3.

- **가 3번 등장하기 때문에 4개로 나뉘어야 하지만, a1, a2, a3 3개의 필드만 선언되어있음

 

  • 분리할 항목 수 > 필드 수인 경우 → 마지막 필드에 나머지 전부가 저장됨
  • 마지막 필드의 크기를 초과할 경우 → 잘림(Truncated)

💡 SHIFT : 문자열을 왼쪽 or 오른쪽으로 이동시키는 함수

문자열 앞의 0을 제거하거나, 문자들을 한 글자씩 회전시킬 수 있음 

 


1. 왼쪽으로 이동하면서 0 제거 : LEFT DELETING LEADING '0';

DATA: employee_num(10).
SHIFT employee_num LEFT DELETING LEADING '0'.
=> "654321"

 

2. 기본 SHIFT 

SHIFT empl_num .
WRITE empl_num .
=> 000654321

 

3. CIRCULAR 옵션

SHIFT empl_num CIRCULAR.
WRITE empl_num .
=> 0006543210

+ Recent posts