FI / FIELD NOTES

SAP GUI를 에이전트에게 맡기면 어디를 사람이 지켜야 할까

로그인은 사람이 하고, 에이전트는 열린 SAP GUI 세션에만 붙습니다. 자동지급 문의에서 조회와 QA 재현으로 틀린 안내와 가드의 구멍을 고친 기록입니다.

평소 SAP 개발은 Claude Code에 ADT를 붙여 소스를 읽고 고치고, SQL로 테이블을 조회한다. ADT가 닿는 곳은 오브젝트까지다. 트랜잭션 화면을 열고 값을 넣고, F110 같은 자동지급을 돌리는 일은 여전히 SAP GUI에서 사람이 해야 했다.

그래서 SAP GUI Scripting을 붙여 봤다. 처음 목적은 화면 순서대로 현업 매뉴얼을 만드는 것이었다. 설정을 보던 날, 해외 담당자의 자동지급 문의가 이미 와 있었다. 첫 실전은 운영 조회, QA 재현, 지급 실행, 안내 문서까지 이어진 하루가 됐다. 회사명, 시스템 ID, 사람 이름, 전표 번호, 금액은 빼 두었다.

  • 로그인은 사람이 하고, 에이전트는 이미 열린 SAP GUI 세션에만 붙는다. 비밀번호는 에이전트를 거치지 않는다.
  • 운영은 조회만 했고, 쓰기는 미리 정한 QA 클라이언트에서만 했다.
  • 다섯 번의 분석이 낸 첫 답은 원인은 맞혔지만 따라 할 수 없는 절차였다. QA에서 화면을 열어 본 뒤에야 안내가 맞았다.

에이전트는 사람이 열어 둔 GUI에 붙는다

사람이 로그인한 뒤 스크립트가 열린 SAP GUI 세션을 찾는 그림. 비밀번호는 에이전트에 없고, 운영은 조회만, 쓰기는 정한 QA 클라이언트만 허용한다.
사람이 로그인한 세션을 스크립트가 찾습니다. 운영은 조회만, 쓰기는 정한 QA 클라이언트만 허용합니다.

구성은 단순하다. Claude Code가 PC에서 cscript로 VBScript를 실행하고, 스크립트는 실행 중인 SAP Logon에 COM으로 붙는다. 객체는 GuiApplication, GuiConnection, GuiSession이고, 화면 요소는 findById로 찾는다.

Set app = GetObject("SAPGUI").GetScriptingEngine
For Each conn In app.Children
  For Each s In conn.Children
    WScript.Echo s.Info.SystemName & "/" & s.Info.Client
  Next
Next
  • 로그인은 내가 한다. 에이전트는 열어 둔 세션을 찾을 뿐이고, 계정 정보는 넘기지 않는다.
  • 이 PC에는 Python pywin32가 없어서 VBScript를 썼다. 설치 없이 바로 실행할 수 있었다.
  • cscript 콘솔은 한글 Windows에서 CP949라 한글이 깨진다. 결과는 ADODB.Stream으로 UTF-8 파일에 쓰고, 에이전트가 그 파일을 읽게 했다.
  • GUI에서 바꾼 값은 ADT의 SQL로 다시 확인했다. 화면에 저장됐다고 나와도 그대로 믿지 않았다.

스크립팅은 서버와 클라이언트 둘 다 켜져 있어야 한다

스크립팅 확인을 나눈 그림. 클라이언트 UserScripting, PAHI 이력의 개발 FALSE와 QA TRUE, 운영 세션의 DisabledByServer 확인, 운영은 조회만 하는 규칙.
클라이언트 옵션, PAHI 이력, 접속한 세션은 서로 다른 확인입니다. 이 사례의 운영 세션도 스크립팅이 켜져 있었습니다.

클라이언트는 SAP GUI 옵션의 접근성 및 스크립팅에서 켠다. 이 PC는 레지스트리 UserScripting=1로 이미 켜져 있었다. 스크립트가 붙을 때마다 뜨는 알림 팝업 두 개는 내가 직접 껐다. 보안 옵션이라 에이전트에게 레지스트리를 건드리게 하지 않았다. 팝업이 떠 있으면 스크립트는 엉뚱한 창을 보고 The control could not be found by id로 죽는다. 첫 실패가 나면 팝업부터 보는 편이 빠르다.

서버 스위치는 sapgui/user_scripting이다. ADT로는 RZ11 화면의 현재 값을 직접 읽지 못했다. 파라미터 변경 이력 테이블 PAHI는 SQL로 볼 수 있다.

SELECT hostname, pardate, parname, parvalue
  FROM pahi
 WHERE parname LIKE 'sapgui/user_scripting%'

가장 최근 행만 보면 개발은 FALSE, QA는 TRUE였다. 이력은 현재 값이 아니다. 마지막에는 로그인한 세션의 GuiConnection.DisabledByServer를 읽었다. 그렇게 보니 운영 세션도 스크립팅이 열려 있었다. 읽기 전용으로만 두는 sapgui/user_scripting_set_readonly 같은 파라미터는 이번 확인에 넣지 않았다.

운영에서 스크립트가 된다는 것은 편하면서 위험하다. 그래서 규칙을 먼저 적었다. 운영에서는 SE16N, FB03 같은 조회만 하고 저장이나 실행은 누르지 않는다. 쓰기는 미리 정한 QA 클라이언트에서만 한다.

조회만으로 지급 묶음이 갈라진 자리를 좁혔다

스크립팅을 보던 날, 해외 담당자가 자동지급(F110)을 돌리면 거래처 대변메모(Credit)가 반영되지 않는다고 했다. 회신에는 Credit 한 건과 송장 두 건이 적혀 있었다.

운영에서는 조회만으로 원인을 좁혔다. 에이전트가 SE16N으로 세 전표의 지급 관련 필드를 읽었다. FB03의 추가 데이터 화면은 첫 답을 검증할 때 봤고, 그 화면이 첫 답의 해결 절차를 뒤집었다. 그 사이에 GUI 자동화의 함정을 여러 번 밟았다.

SE16N이 컬럼을 숨긴다. BSEG를 열었더니 300개가 넘는 컬럼 중 32개만 보였다. 계정에 걸린 기본 ALV 레이아웃 때문이었고, 레이아웃 칸을 비워도 기본 레이아웃이 남았다. 기본 레이아웃이 없는 BSIK, BSAK로 우회했다. 이후에는 ColumnCount를 먼저 본다.

선택조건의 행 번호는 고정이 아니다. SE16N 선택조건은 화면에 21행만 보이고, 필드 순서도 테이블마다 다르다. 행 번호나 설명에 기대지 않고 기술명 열 GS_SELFIELDS-FIELDNAME에서 필드를 찾아 그 행에 값을 넣었다.

셀 단위 읽기는 느리다. ALV를 GetCellValue로 한 칸씩 읽으면 칸마다 COM 왕복이 생긴다. 행과 열이 조금만 많아도 10분을 넘겼고, 같은 세션의 다른 스크립트는 타임아웃이 났다. 필요한 컬럼만 읽고 조건을 좁혔다. F110 로그 같은 일반 목록은 %pc로 파일을 저장한 뒤 그 파일을 읽게 바꿨다.

팝업은 다른 창이다. FB03 추가 데이터는 wnd[1]이다. 처음에는 wnd[0]만 찍어 뒤에 깔린 화면만 남았다.

F110 묶음이 갈라진 사례. 송장에는 파트너은행유형이 있고 주거래은행은 비어 있다. Credit은 반대이며 전기키 21의 차변이다. Credit만 남은 묶음은 메시지 FZ501로 예외에 남는다.
송장과 Credit의 은행 필드가 달라 묶음이 갈라집니다. Credit만 남은 묶음은 차변 잔액이라 메시지 FZ501로 예외에 남습니다.

조회 결과는 분석 셋과 검토 둘에게 나눠 맡겼다. F110은 지급 프로그램이 묶음으로 보는 값이 같은 미결 항목만 한 번에 지급한다. 그 값에는 파트너은행유형(BVTYP), 주거래은행(HBKID), 통화, 지급 방법이 들어 있다. 거래처의 그룹핑 키가 달라도 묶음은 갈라진다. 이 사례에서 실제로 갈라진 자리는 BVTYP과 HBKID였다.

송장 두 건에는 BVTYP이 있고 Credit에는 비어 있었다. Credit에는 HBKID가 있고 송장에는 없었다. Credit은 전기키 21이라 공급업체 계정에서는 차변이다. Credit만 남은 묶음은 차변 잔액이 되어 지급할 수 없다. 그때 남는 메시지는 FZ501, “No pymt possible because items with a debit bal. still exist; see job log”이다. 송장 묶음은 Credit 없이 지급 대상이 된다. 운영의 지급 실행 기록에는 이 거래처가 없어서, 운영에서 이 증상을 다시 돌리지는 못했다.

서버 시간이 한국 시간(KST)이면 입력일(CPUDT)은 한국 날짜다. 입력자가 미국 동부일 때, 서머타임(EDT)은 현지 11:00부터, 표준시(EST)는 현지 10:00부터 CPUDT가 현지 날짜의 다음 날이 된다. F110의 Docs entered up to는 전기일이 아니라 이 입력일과 비교한다. 그래서 그 시각부터 입력한 전표는, 현지 날짜로 돌린 당일 지급에서 빠질 수 있다.

첫 답은 단계마다 다른 곳에서 틀렸다

  1. 초안은 FB02로 Credit에 파트너은행유형을 넣고 주거래은행을 지우라고 했다. 운영 FB03의 추가 데이터에는 그 칸이 없었다. 전기키 21의 필드 상태와 조정계정의 필드 상태를 보니, 그 시스템에서는 파트너은행유형이 숨김이었다. FB65와 FB02에도 칸이 없었다. 전기키 21이 항상 그 칸을 숨기는 것은 아니다. 따라 할 수 없는 안내였고, 1차 문서를 쓰기 전에 뺐다.
  2. 에이전트는 QA에 데이터가 없다고 보고했다. ADT SQL은 연결의 로그온 클라이언트를 본다. 데이터는 다른 클라이언트에 있었다. USING CLIENT를 붙이니 그대로 나왔다.
  3. 1차 안내에는 주거래은행은 원인이 아니라고 썼고, 잔여 항목은 전기키 31이니 FB02로 파트너은행유형을 넣으면 된다고 썼다. 둘 다 QA 재현에서 뒤집혔다.

다섯이 교차 검토해도, 그때의 결론은 운영 데이터를 읽고 만든 추론이었다. 원인은 맞았고, 사람이 따라 할 절차는 화면을 열어 보기 전이었다.

QA에서 세 번 돌려 원인을 갈랐다

QA 재현 네 단계. 1차에서 Credit이 FZ501로 빠지고, 2차에서 BVTYP과 HBKID가 각각 묶음을 가르고, 잔여 항목은 전기키 34이며, 3차에서 지급전표 한 건이 생기고 은행 파일은 만들지 않는다.
1차는 증상을 재현하고, 2차는 두 필드가 각각 묶음을 가르는 것을 확인했습니다. 잔여 항목은 전기키 34였고, 3차에서 지급전표 한 건이 생겼습니다.

쓰기부터는 막을 곳이 필요하다. QA용 헬퍼는 불러올 때 정한 QA 클라이언트의 세션만 잡고, 없으면 그 자리에서 끝난다. 작업을 시작할 때와 저장 직전에 Guard로 시스템과 클라이언트를 한 번 더 본다. 이 Guard에는 구멍이 있었고, 뒤에서 다시 말한다.

Set session = GetSession(QA_SID, QA_CLIENT)

Sub Guard()
  If session.Info.SystemName <> QA_SID _
    Or session.Info.Client <> QA_CLIENT Then
    Err.Raise vbObjectError + 9, "Guard", "not QA"
  End If
End Sub

1차 실행은 증상 재현이었다. 송장 두 건을 FB02로 운영과 같은 상태로 맞추고, FB65로 전기키 21 Credit을 입력했다. 지급 탭에 파트너은행유형 칸이 없는 것을 봤고, 전기 뒤 데이터베이스에서도 그 값이 비어 있었다. F110 제안은 담당자가 말한 대로 Credit을 반영하지 않았다. Credit만 따로 묶여 예외로 빠졌고, 메시지는 FZ501이었다.

제안 로그를 %pc로 저장하려다 SAP GUI 보안 창이 떠 스크립트가 멈췄다. 파일 쓰기를 허용할지 묻는 창이라 에이전트가 대신 누르지 않았다. 이유를 들은 뒤 내가 허용을 눌렀다.

2차 실행은 원인을 갈랐다. Credit의 주거래은행을 비워 Credit과 송장 A는 BVTYP만 다르게 했다. 송장 B에는 주거래은행을 넣어 송장 A와 B는 HBKID만 다르게 했다. Credit은 여전히 예외였고, 송장 A와 B도 서로 다른 지급 묶음이 됐다. 두 필드 중 하나만 달라도 묶음이 갈라졌다. 앞선 분석도 두 필드를 원인으로 봤지만, 1차 안내를 쓰면서 주거래은행을 뺐고 그 문장이 틀렸다.

조치는 F-44 반제였다. Credit에 값을 넣을 칸이 없으니 Credit과 송장 한 건을 먼저 반제해 잔여 항목을 만들었다. 잔여 항목의 전기키는 1차 문서의 31이 아니라 34였다. 많이 쓰이는 반제 설정에서 잔여 항목 대변은 34인 경우가 많지만, 여기 적은 34는 그 QA에서 본 값이다. 그 화면에서도 파트너은행유형 칸은 숨겨져 있었고, 값은 원 송장에서 복사됐다. 그 시스템의 대체 규칙이 새 항목에 지급보류를 걸어서, FB02로 보류를 푸는 단계가 하나 더 필요했다.

3차 실행은 지급 확인이었다. 잔여 항목과 나머지 송장이 한 묶음이 됐고, 지급 실행까지 돌려 지급전표가 한 건 생겼다. 은행 파일이나 출력물은 만들지 않았다. 지급 실행 팝업에 즉시 시작 말고 다른 체크박스가 보이면 스크립트가 취소하도록 해 두었다.

조건이 오류 나면 Then으로 들어간다

VBScript 가드의 구멍. And는 양쪽을 평가하고, On Error Resume Next 아래에서 조건 오류는 Then을 실행한다. 호출부가 오류를 무시하면 Guard의 Err.Raise도 멈추지 못한다. 다시 쓴 가드는 Quit으로 끝내고, 이번 실행을 막은 것은 세션 선택이다.
조건이 오류 나면 Then이 실행됩니다. Guard의 오류도 호출부에서 무시됐고, 이번 실행을 막은 것은 세션 선택이었습니다.

지급 실행 스크립트를 처음 돌렸을 때 스크립트가 스스로 취소하고 멈췄다. 팝업의 자식을 돌며 지급매체 체크박스가 켜져 있으면 중단하는 조건을 한 줄로 썼기 때문이다. VBScript의 And는 양쪽을 모두 평가한다. 체크박스가 아닌 요소에서 Selected를 읽다 오류가 난다. On Error Resume Next 아래에서 그 조건이 오류 나면 If는 Then으로 들어간다. 이번 Then은 취소라서 멈췄다. 진행 쪽 조건이었다면 검사를 지나갔을 것이다.

그 뒤 안전 가드는 중첩 If로 바꿨다. 위험한 것을 찾으면 중단하는 대신, 예상한 것 외에 하나라도 보이면 중단하는 허용 목록으로 썼다.

글을 쓰려고 스크립트를 다시 읽다가 Guard도 같은 구멍인 것을 알았다. 쓰기 스크립트는 On Error Resume Next를 켠 뒤에 Guard를 불렀다. VBScript의 오류 처리는 프로시저 단위라, Guard 안의 Err.Raise는 호출한 쪽에서 무시되고 다음 줄인 저장이 실행된다. 이번에 시스템을 지켜 준 것은 Guard가 아니라, 헬퍼를 부를 때의 세션 선택이었다. 그 시점에는 오류 무시가 아직 꺼져 있어서 에러가 스크립트를 멈춘다.

다시 쓴다면 가드는 오류를 던지지 않고 스스로 끝낸다. 판정이 실패해도 멈추는 쪽으로 떨어지게 한다. 아래는 그날 실행한 코드가 아니라, 글을 쓰며 고친 형태다.

Sub Guard()
  Dim ok : ok = False
  On Error Resume Next
  ok = (session.Info.SystemName = QA_SID _
    And session.Info.Client = QA_CLIENT)
  On Error GoTo 0
  If Not ok Then WScript.Quit 1
End Sub

사람이 지킨 자리와 다음에 먼저 볼 것

사람에이전트
SAP 로그인세션 찾기, 화면 조작, 캡처
스크립팅 알림 팝업 끄기레지스트리, PAHI, DisabledByServer 확인
SAP GUI 보안 창에서 허용막힌 이유 설명 후 대기
QA에서 재현하자는 판단재현 설계, 전기, F110 세 번, F-44 반제
담당자에게 회신원인 분석, 문서, 데이터베이스 대조

사람이 한 일은 적지만 로그인, 보안 창, 그리고 QA에서 같은 데이터를 만들어 보여 주자는 판단이었다. 에이전트는 QA에 데이터가 없다고 잘못 보고 거기서 멈춰 있었다. 그 판단이 없었다면 1차 문서의 틀린 문장은 그대로 남았을 것이다. 담당자용으로 자른 화면 캡처는 이름과 계좌를 가렸고, 이 글에는 넣지 않았다.

막힌 곳다음에 먼저 볼 것
컨트롤을 찾지 못함알림 팝업과 옵션 창
SE16N 컬럼이 일부만 보임ColumnCount, 기본 레이아웃
셀 읽기가 수 분 이상필요한 컬럼만, 목록은 %pc
스크립트가 끝없이 대기SAP GUI 보안 창은 사람이 클릭
한글 출력 깨짐UTF-8 파일로 쓰고 파일을 읽기
조건 오류인데 Then 실행중첩 If, 허용 목록
Guard가 저장을 막지 못함가드 안에서 Quit, 세션 선택이 먼저
QA 데이터가 없어 보임로그온 클라이언트와 USING CLIENT
당일 입력 전표가 지급에서 빠짐미국 동부 11:00 또는 10:00부터의 CPUDT

이번에 지킨 원칙은 네 가지다. 로그인은 사람이 한다. 운영은 조회만 한다. 쓰기 스크립트는 정한 시스템과 클라이언트의 세션만 잡는다. GUI에서 바꾼 값은 데이터베이스로 다시 확인한다. 하나 더 있다. 안전장치도 코드라서, 일부러 실패시켜 멈추는지 한 번은 본다.

매끄럽지는 않았다. 사용 한도에 걸려 몇 시간씩 멈춘 적이 있고, 첫 안내는 틀렸다. 그래도 운영 조회, 원인 정리, QA 재현, 지급 실행, 안내 문서까지 하루 안에 끝났다. 다음 목표는 원래 계획했던 현업 매뉴얼이다. QA에서 업무 순서를 돌리며 화면과 조작을 문서로 남기는 일은 이번과 모양이 같다.

END OF NOTE목록으로
COMMENTS BOX

이 기록에 대화를 더해 주세요.

궁금한 점, 다른 접근, 직접 해 본 결과를 나눠 주세요.

최신순

로그인 상태 확인 중…

댓글을 불러오는 중…