
UiPath 개발 중 직접 겪었던 문제와 해결 방식을 정리하는 문서입니다.
단순 문법 정리가 아니라, 문제 상황 → 내가 적용한 해결 방식 → 주의한 점 → 정리한 기준 순서로 계속 업데이트합니다.
이 글은 공개 블로그용이므로 실제 운영 정보는 일반화해서 작성한다.
- 실제 업무 시스템명 → "업무 시스템", "대상 시스템"으로 표현
- 실제 URL, IP, 도메인 → example.com 또는 <도메인> 형태로 표현
- 계정명, 서버명, PC명 → <계정명>, <서버명>, <Robot PC> 형태로 표현
- 내부 패키지 경로 → <로컬 패키지 경로>, <내부 패키지 저장소>로 표현
- 방화벽 허용 정보 → 출발지, 목적지, 프로토콜, 포트, 용도 기준으로 일반화
- RDP 세션 명령어 → 실제 세션 ID, 계정명 없이 템플릿 형태로 작성
보안 관련 내용을 무조건 제외하기보다는, 실제 환경을 특정할 수 있는 값만 제거하고 구조와 판단 기준은 남긴다.
콤마로 구분된 문자열을 배열로 나누고 싶었다.
단순히 Split만 하면 값 앞뒤에 공백이 남는 문제가 있었다.
예시 문자열:
" A, B , C "
원하는 결과:
{"A", "B", "C"}
strText.Split(","c).Select(Function(x) x.Trim()).ToArray()
List 타입으로 받아야 할 때는 마지막만 ToList()로 바꿨다.
strText.Split(","c).Select(Function(x) x.Trim()).ToList()
문자열을 나누기만 한다 → Split
나눈 뒤 각 항목을 가공한다 → Split + Select
배열로 사용한다 → ToArray()
List로 사용한다 → ToList()
Select는 반드시 필요한 것은 아니다.
하지만 나눈 값마다 Trim, Replace, ToUpper 같은 후처리가 필요하면 Select를 쓰는 게 맞다.
여러 줄 텍스트를 줄 단위로 나눠 배열로 만들 필요가 있었다.
예시:
A
B
C
원하는 결과:
{"A", "B", "C"}
Windows 줄바꿈만 고려하면 아래 방식으로 충분하다.
strText.Split({Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
다만 외부에서 가져온 텍스트는 줄바꿈 형식이 섞일 수 있어서, 실무에서는 아래 방식이 더 안전했다.
strText.Split({vbCrLf, vbLf}, StringSplitOptions.RemoveEmptyEntries)
각 줄의 앞뒤 공백까지 제거해야 하면 다음처럼 처리했다.
strText.Split({vbCrLf, vbLf}, StringSplitOptions.RemoveEmptyEntries).
Select(Function(x) x.Trim()).
ToArray()
줄바꿈 형식이 확실하다 → Environment.NewLine
외부 텍스트라 줄바꿈이 섞일 수 있다 → vbCrLf, vbLf 모두 처리
빈 줄 제거가 필요하다 → RemoveEmptyEntries
각 줄 공백 제거가 필요하다 → Select + Trim
문자열 안에서 "인증번호:" 뒤에 오는 6자리 숫자만 추출해야 했다.
예시:
인증번호: 123456
원하는 결과:
123456
System.Text.RegularExpressions.Regex.Match(strText, "인증번호:\s*(\d{6})").Groups(1).Value
실제 자동화에서는 매칭 실패 가능성도 있기 때문에 Success를 확인하는 방식이 더 안전하다.
Dim match = System.Text.RegularExpressions.Regex.Match(strText, "인증번호:\s*(\d{6})")
If match.Success Then
strCode = match.Groups(1).Value
Else
Throw New Exception("인증번호 6자리를 찾을 수 없습니다.")
End If
패턴이 반드시 있다고 확신한다 → Match().Groups(1).Value
패턴이 없을 수도 있다 → Match.Success 확인 후 처리
실패 시 업무 오류로 봐야 한다 → Throw
텍스트가 아래처럼 숫자와 공백으로 시작하는지 확인할 필요가 있었다.
1 항목명
System.Text.RegularExpressions.Regex.IsMatch(strText, "^\d+\s")
^ 문자열 시작
\d+ 숫자 1개 이상
\s 공백 1개
이 패턴은 목록형 텍스트를 구분할 때 유용했다.
UiPath에서 Array를 써야 할지, List를 써야 할지 헷갈렸다.
개수가 고정되어 있다 → Array
실행 중 계속 추가하거나 제거한다 → List
New String() {}
New List(Of String)()
listData.Add("값")
listData.Count
listData.Last()
단, 리스트가 비어 있으면 Last()에서 예외가 발생한다.
그래서 실제로는 아래처럼 방어 조건을 넣었다.
If(listData.Count > 0, listData.Last(), "")
처음부터 값 목록이 정해져 있다 → Array
반복 중 값을 계속 쌓는다 → List
마지막 값을 가져온다 → Last()
비어 있을 수 있다 → Count 체크 후 Last()
List(Of String)에서 중복 값을 제거해야 했다.
처음에는 Python의 set 같은 타입을 찾았지만, VB.NET에서는 Distinct()를 쓰는 방식이 일반적이었다.
listData.Distinct().ToList()
대소문자를 무시해야 할 때는 아래처럼 처리했다.
listData.Distinct(StringComparer.OrdinalIgnoreCase).ToList()
Distinct()는 중복 제거용으로 사용하고, 결과 순서가 중요하면 이후에 명시적으로 정렬하는 편이 안전하다.
listData.Distinct().OrderBy(Function(x) x).ToList()
중복 제거만 필요하다 → Distinct()
결과를 List로 계속 사용한다 → Distinct().ToList()
대소문자를 같은 값으로 본다 → StringComparer.OrdinalIgnoreCase
결과 순서도 중요하다 → OrderBy 추가
두 개의 List가 같은 값을 가지고 있는지 비교해야 했다.
listA.SequenceEqual(listB)
예시:
{"A", "B", "C"}
{"A", "B", "C"}
결과:
True
하지만 아래처럼 순서가 다르면 False다.
{"A", "B", "C"}
{"C", "B", "A"}
listA.OrderBy(Function(x) x).SequenceEqual(listB.OrderBy(Function(x) x))
이 방식은 중복 개수까지 비교한다.
예시:
{"A", "A", "B"}
{"A", "B", "B"}
결과:
False
listA.Distinct().OrderBy(Function(x) x).
SequenceEqual(listB.Distinct().OrderBy(Function(x) x))
순서까지 같아야 한다 → SequenceEqual
순서는 무시하지만 중복 개수는 비교한다 → OrderBy 후 SequenceEqual
중복 자체를 무시한다 → Distinct 후 OrderBy 후 SequenceEqual
하위 Workflow에서 Throw로 예외를 발생시키고, 상위 Workflow에서 Try-Catch로 잡는 구조였다.
이때 Throw 아래에 Return을 넣어야 하는지 헷갈렸다.
대부분 필요 없다.
Throw
Return
Throw가 실행되면 예외가 발생하고 현재 흐름은 중단된다.
따라서 바로 아래의 Return은 실행되지 않는다.
정상적으로 현재 Workflow를 끝낸다 → Return
실패를 상위 Workflow에 알린다 → Throw
반복문만 빠져나간다 → Break
예외 상황인데 Return으로 조용히 종료하면 상위 Workflow에서는 정상 종료처럼 보일 수 있다.
운영 자동화에서는 실패를 숨기는 구조가 더 위험하다.
For Each 반복 도중 특정 조건에서 멈춰야 했다.
이때 Break를 써야 할지, Return을 써야 할지 구분이 필요했다.
Break는 현재 반복문만 빠져나간다.
For Each
조건 만족
Break
Next
아래 로직 계속 실행
Return은 현재 Workflow 자체를 종료한다.
For Each
조건 만족
Return
Next
아래 로직 실행 안 됨
반복만 멈추고 다음 로직은 계속한다 → Break
현재 Workflow를 정상 종료한다 → Return
실패 상황을 상위에 알린다 → Throw
조건이 여러 개 있고, 중간에 하나라도 실패하면 예외로 중단해야 했다.
처음에는 If 안에 If가 계속 들어가는 구조가 되었다.
If 조건1 Then
If 조건2 Then
If 조건3 Then
처리
End If
End If
End If
실패 조건을 먼저 검사하고, 실패하면 바로 Throw했다.
If Not 조건1 Then
Throw New Exception("조건1 실패")
End If
If Not 조건2 Then
Throw New Exception("조건2 실패")
End If
If Not 조건3 Then
Throw New Exception("조건3 실패")
End If
' 정상 처리
실패 조건이 위에 모인다
정상 로직이 깊게 들어가지 않는다
예외 메시지를 명확히 남기기 쉽다
유지보수가 쉬워진다
로그인 여부 확인
필수 데이터 존재 여부 확인
조회 결과 존재 여부 확인
화면 상태 확인
파일 존재 여부 확인
권한 여부 확인
String, Integer 같은 값은 하위 Workflow에 In으로 넘기고 내부에서 바꿔도 외부 값이 바뀌지 않았다.
그런데 DataTable은 In으로 넘겼는데도 내부에서 수정한 내용이 외부에 반영됐다.
DataTable은 참조 타입이다.
하위 Workflow로 객체의 참조가 전달되기 때문에, 같은 객체의 내부 내용을 수정하면 외부에서도 변경된 내용이 보인다.
예시:
dtData.Rows.Add(...)
또는:
dtData.Rows(0)("Status") = "완료"
아래처럼 새 객체를 다시 할당하는 것은 다르다.
dtData = New DataTable()
이 경우 외부 변수가 자동으로 새 DataTable을 바라보지는 않는다.
외부에도 새 객체를 전달해야 한다면 Out 또는 In/Out 인수를 써야 한다.
DataTable 내부 행/컬럼/값을 수정한다 → In으로도 외부 반영 가능
DataTable 변수에 새 객체를 할당한다 → Out 또는 In/Out 필요
VB.NET에서 String은 참조 타입이라고 알고 있었다.
그런데 UiPath Workflow에서 String을 In으로 넘기고 내부에서 값을 바꿔도 외부 값은 바뀌지 않았다.
문자열은 불변 객체다.
즉, 기존 문자열 자체를 수정하는 것이 아니라 변경 결과로 새 문자열을 만든다.
예시:
strText = strText.Replace("A", "B")
이 코드는 기존 문자열을 직접 수정하는 것이 아니다.
Replace 결과로 새 문자열을 만들고, 그 새 문자열을 strText 변수에 다시 할당하는 구조다.
String 값을 하위 Workflow에서 바꾸고 외부에도 반영해야 한다 → Out 또는 In/Out
String을 읽기만 한다 → In
String은 참조 타입이지만 불변 객체라 값처럼 다루는 편이 안전하다
클래식 액티비티에서는 Browser 변수를 사용했는데, 모던 UI 자동화에서는 같은 방식이 잘 맞지 않았다.
클래식 방식은 보통 아래 액티비티 중심으로 구성된다.
Open Browser
Attach Browser
Classic Click
Classic Type Into
이 구조에서는 Browser 타입 변수를 직접 다루는 방식이 자연스럽다.
반면 모던 방식은 아래 구조를 중심으로 사용한다.
Use Application/Browser
Check App State
Modern Click
Modern Type Into
Table Extraction
모던에서는 Browser 변수를 직접 넘겨가며 제어하기보다, Use Application/Browser Scope 안에서 대상 애플리케이션을 잡고 처리하는 방식이 더 자연스럽다.
기존 Browser 변수 기반 구조다 → 클래식 중심으로 유지
Use Application/Browser, Check App State, Modern Table Extraction을 쓴다 → 모던 중심으로 구성
한 흐름에서 섞어야 한다 → Scope 경계와 셀렉터 기준을 명확히 분리
클래식과 모던을 섞는 것이 불가능한 것은 아니다.
다만 유지보수 난이도가 올라가므로, 한 업무 흐름 안에서는 가능하면 한 방식을 기준으로 잡는 편이 낫다.
URL에 매번 바뀌는 값이 들어가서 셀렉터가 불안정해졌다.
예시:
https://example.com/search?sessionId=ABC123&page=1
sessionId 값은 실행마다 바뀔 수 있으므로 셀렉터에 그대로 넣으면 다음 실행에서 깨질 가능성이 있다.
동적으로 바뀌는 부분은 와일드카드로 처리했다.
<html app='chrome.exe' url='https://example.com/search?sessionId=*&page=1' />
page 값도 바뀌면 다음처럼 처리할 수 있다.
<html app='chrome.exe' url='https://example.com/search?sessionId=*&page=*' />
셀렉터는 XML 형식이므로 URL의 &는 그대로 쓰지 않고 &로 작성해야 한다.
잘못된 표현: &
올바른 표현: &
sessionId, token, timestamp처럼 매번 바뀐다 → 와일드카드 처리
업무적으로 의미 있는 값이다 → 필요한 경우만 유지
URL 전체가 불안정하다 → url 속성 의존도를 낮추고 다른 속성 검토
화면에 특정 엘리먼트가 몇 개 있는지 확인해야 했다.
Find Children 계열 액티비티로 조건에 맞는 자식 엘리먼트를 찾고, 결과 개수를 확인했다.
결과 변수가 List나 배열이면 다음처럼 확인할 수 있다.
uiElements.Count
결과가 IEnumerable(Of UiElement) 형태라면 아래처럼 쓰는 편이 안전하다.
uiElements.Count()
또는 List로 변환해서 확인한다.
uiElements.ToList().Count
부모 엘리먼트 범위를 잘 잡는 것이 핵심이었다.
부모 범위가 너무 넓다 → 불필요한 요소까지 잡힌다
부모 범위가 너무 좁다 → 원하는 요소가 누락된다
목록, 테이블, 반복 UI 개수 확인 → Find Children
개수가 예상보다 많다 → 부모 범위가 너무 넓은지 확인
개수가 0이다 → 로딩, iframe, 셀렉터 조건 확인
화면 로딩이 느리다 → Element Exists 또는 Check App State 후 Count
UiPath 실행 중 아래 오류가 발생했다.
입력 내용이 화면 경계를 벗어나므로 UI 엘리먼트에 보낼 수 없습니다.
이 오류는 단순히 입력값이 잘못됐다는 의미가 아니었다.
대부분 화면 상태나 UI 렌더링 문제와 관련이 있었다.
가능성이 높았던 원인은 다음이다.
대상 엘리먼트가 화면 밖에 있음
창이 최소화되어 있음
브라우저 또는 프로그램이 비활성 상태임
해상도 또는 배율이 다름
셀렉터는 잡혔지만 실제 좌표가 비정상임
스크롤이 필요한 위치에 대상이 있음
원격 세션 상태가 UI 자동화에 적합하지 않음
1. 창을 Maximize 처리
2. 대상 창 Activate
3. 필요한 경우 Scroll Into View
4. Element Exists 또는 Check App State로 대상 확인
5. 입력 방식 변경 검토
6. 운영 PC의 해상도와 배율 확인
7. 원격 세션 종료 방식 확인
개발 PC에서는 되는데 운영 PC에서 실패한다 → 해상도, 배율, 화면 상태 확인
가끔 실패한다 → 로딩 대기, Activate, Scroll 처리 추가
항상 실패한다 → 셀렉터 또는 대상 컨트롤 상태 확인
원격 접속 종료 후 실패한다 → 세션 유지 방식 확인
Excel 자동화 중 아래 오류가 발생했다.
호출된 개체가 해당 클라이언트로부터 연결이 끊겼습니다.
HRESULT: 0x80010108 (RPC_E_DISCONNECTED)
이 오류는 UiPath와 Excel 사이의 연결이 불안정해졌을 때 발생했다.
특히 Copy/Paste 작업이 많거나 Excel Scope 구조가 복잡할 때 더 자주 나타났다.
가능한 원인은 다음으로 정리했다.
Excel 프로세스 비정상 상태
파일 잠김
Excel 창 응답 없음
Copy/Paste 작업이 너무 빠르게 이어짐
Use Excel File 범위가 복잡함
클립보드 사용 중 충돌
가능하면 화면 기반 Copy/Paste를 줄이고 DataTable 기반 처리로 바꿨다.
Read Range
Write Range
Append Range
DataTable 조작
Workbook 기반 처리
대량 데이터 복사/붙여넣기 중 오류 → DataTable 방식 검토
간헐적 오류 → Delay, Save/Close 순서, Scope 정리
항상 같은 위치에서 오류 → 파일 잠금, 시트 보호, 범위 문제 확인
운영 PC에서만 오류 → Office 버전, 권한, 실행 환경 확인
다른 PC에서 가져온 UiPath 프로젝트를 열었더니 패키지 의존성 오류가 발생했다.
패키지 복구를 눌러도 제한된 네트워크 환경에서는 복구가 되지 않았다.
project.json의 dependencies
Studio의 Manage Packages 설정
로컬 패키지 경로
기존 개발 PC의 패키지 캐시
사내 라이브러리 패키지 존재 여부
패키지 소스 우선순위
실제 내부 경로나 저장소 정보는 공개하지 않고, 해결 흐름만 정리하면 다음과 같다.
1. 기존 개발 PC에서 필요한 패키지 파일 확인
2. 대상 PC에서 참조 가능한 로컬 패키지 경로 구성
3. Studio 패키지 소스에 로컬 경로 추가
4. project.json의 dependencies와 실제 패키지 버전 확인
5. Restore 재시도
인터넷이 가능한 개발 환경 → 공식 피드에서 Restore 가능
제한된 네트워크 환경 → 로컬 패키지 또는 내부 패키지 저장소 필요
프로젝트만 복사했다 → 패키지 누락 가능성 높음
사내 라이브러리를 쓴다 → 해당 라이브러리 패키지도 같이 이관 필요
패키지 버전이 다르다 → project.json 기준으로 맞춰야 함
UiPath 프로젝트를 열었을 때 패키지 복구가 필요했지만, 네트워크 제한 때문에 패키지를 다운로드하지 못했다.
처음에는 단순히 “인터넷이 안 된다” 정도로 봤지만, 실제로는 Studio, Robot, Orchestrator, 패키지 피드 간 통신 경로를 구분해서 봐야 했다.
방화벽 요청은 단순히 “열어주세요”가 아니라 아래 항목으로 정리하는 게 좋았다.
| 구분 | 작성 예시 |
|---|---|
| 출발지 | <개발 PC>, <Robot PC> |
| 목적지 | <Orchestrator URL>, <패키지 피드 URL>, <Automation Cloud 관련 URL> |
| 프로토콜 | HTTPS |
| 포트 | 443 |
| 용도 | Studio 로그인, 패키지 복구, Robot-Orchestrator 통신, 라이선스 확인 등 |
| 대상 프로그램 | UiPath Studio, UiPath Robot |
| 비고 | 프록시, 인증서 검사, 내부망 정책 확인 필요 |
공개 글에서는 실제 도메인이나 내부 URL을 쓰지 않고 아래처럼 일반화한다.
출발지: <개발 PC 또는 Robot PC>
목적지: <UiPath Orchestrator 또는 패키지 피드>
프로토콜: HTTPS
포트: TCP 443
용도: 패키지 복구, Robot 연결, Studio 로그인
Studio에서 패키지 Restore가 실패한다
Manage Packages에서 피드 목록은 보이지만 다운로드가 실패한다
개발 PC에서는 되는데 운영 PC에서는 안 된다
Robot이 Orchestrator에 연결되지 않는다
로그인 또는 라이선스 확인 단계에서 실패한다
1. 패키지 복구 실패인지, Orchestrator 연결 실패인지 구분
2. Studio PC와 Robot PC의 네트워크 정책이 같은지 확인
3. 공식 피드 사용인지 내부 패키지 저장소 사용인지 확인
4. 폐쇄망이면 로컬 패키지 또는 내부 패키지 저장소 방식 검토
5. 방화벽 요청 시 출발지, 목적지, 포트, 용도를 명확히 정리
패키지 다운로드만 실패한다 → 패키지 피드 접근 확인
Robot 연결만 실패한다 → Orchestrator 접근 확인
Studio 로그인만 실패한다 → 인증/라이선스 관련 통신 확인
개발 PC만 정상이다 → 운영 PC의 방화벽, 프록시, 인증서 정책 확인
폐쇄망이다 → 외부 다운로드 전제 구조를 버리고 로컬 패키지 전략 필요
방화벽 관련 내용은 보안상 삭제할 것이 아니라, 실제 도메인과 내부 경로를 제거하고 통신 구조 중심으로 일반화해서 정리하는 것이 적절하다.
짧은 주기로 스케줄을 실행하려고 했는데, 이전 Job이 아직 실행 중일 때 다음 Job이 또 쌓이면 문제가 될 수 있었다.
Orchestrator Trigger 설정에서 중복 실행 관련 옵션을 먼저 확인했다.
추가로 프로세스 내부에서도 2차 방어를 두는 구조가 안전하다고 판단했다.
Start
- 실행 중 여부 확인
- 실행 중이면 종료
- 실행 중이 아니면 실행 상태 설정
Process
- 실제 업무 처리
Finally
- 실행 상태 해제
짧은 주기로 Trigger 실행 → 중복 실행 방지 필수
한 번에 하나만 돌아야 하는 업무 → Trigger 설정 + 내부 실행 상태 체크
비정상 종료 가능성이 있다 → 실행 상태 해제 기준 필요
공개 글에서는 구체적인 Orchestrator Tenant명, Folder명, Robot명은 쓰지 않는 편이 안전하다.
대신 “중복 실행 방지 옵션”과 “프로세스 내부 실행 상태 체크”라는 구조를 설명하면 된다.
Queue가 단순히 List를 반복하는 것과 무엇이 다른지 헷갈렸다.
Queue는 단순 반복문이 아니다.
Orchestrator에서 처리 건을 상태로 관리하는 구조다.
대표적으로 아래와 같은 상태 흐름을 가진다.
New
In Progress
Successful
Failed
Retried
Abandoned
Deleted
상태 종류는 UiPath 버전과 설정에 따라 표현이 달라질 수 있으므로, 글에서는 “대표적으로”라고 표현하는 것이 안전하다.
처리 대상이 건 단위로 명확함
실패 건을 재처리해야 함
성공/실패 상태를 Orchestrator에서 보고해야 함
여러 Robot이 나눠 처리할 수 있음
트랜잭션 단위 로그가 중요함
웹사이트에서 실시간으로 조회함
조회 결과가 없으면 바로 종료함
Input 데이터가 사전에 쌓여 있지 않음
단순 상태 확인성 프로세스임
건별 재처리 필요성이 낮음
건별 상태 관리가 필요하다 → Queue
재처리와 Retry가 중요하다 → Queue
단순 조회 후 있으면 처리, 없으면 종료 → While 반복 구조 검토
결과만 취합하면 된다 → Queue가 과할 수 있음
REFramework를 사용하려고 했지만, Input 데이터가 Queue에 미리 쌓여 있는 구조가 아니었다.
웹에서 실시간으로 조회한 뒤, 처리할 건이 있으면 진행하고 없으면 종료하는 업무였다.
기본 REFramework 구조는 다음과 같다.
Init
Get Transaction Data
Process Transaction
End Process
하지만 모든 업무가 Queue 기반에 맞는 것은 아니다.
실시간 조회형 업무는 아래 구조가 더 자연스러웠다.
Init
- 브라우저 실행
- 로그인
- 설정 로딩
Process
- 처리 대상 조회
- 없으면 종료
- 있으면 반복 처리
End
- 로그아웃
- 브라우저 종료
- 결과 정리
처리할 건이 없는데 후속 시스템까지 접근하면 실패 지점만 늘어난다.
그래서 초반에 처리 대상 존재 여부를 먼저 판단하는 방식으로 정리했다.
처리 건이 Queue에 쌓여 있다 → 기본 REFramework 구조 적합
처리 건을 웹에서 실시간 조회한다 → Process 안의 While 반복 검토
건이 없으면 바로 끝난다 → 초반 조회 후 종료
후속 시스템 접근이 조건부다 → 사전 조건 검증 후 접근
원격 환경에서 UiPath를 실행할 때, RDP 접속을 종료한 뒤 UI 자동화가 실패하는 경우가 있었다.
특히 화면 클릭, 입력, 엘리먼트 인식 같은 작업은 원격 세션 상태에 영향을 받았다.
UI 자동화는 실제 화면 상태에 의존하는 경우가 많다.
따라서 원격 세션을 그냥 닫거나 화면이 잠기면 아래 문제가 생길 수 있다.
화면 렌더링 중단
해상도 변경
창 비활성화
엘리먼트 좌표 계산 오류
입력 대상이 화면 밖으로 인식됨
운영 환경에서는 가능하면 Unattended Robot 구성을 우선 검토한다.
불가피하게 원격 세션 기반으로 실행해야 한다면, RDP 세션 종료 방식도 운영 절차에 포함해야 한다.
일반적으로 확인할 항목은 다음과 같다.
1. Robot 실행 계정으로 정상 로그인되어 있는가
2. 실행 중 화면 잠금이 발생하지 않는가
3. 해상도와 배율이 고정되어 있는가
4. RDP 종료 후에도 UI 자동화가 가능한 상태로 세션이 유지되는가
5. 관리자 권한이 필요한 명령을 쓰고 있지는 않은가
6. 작업 스케줄러 실행 계정과 실제 로그인 계정이 일치하는가
실제 계정명이나 세션 ID를 공개하지 않고, 개념만 정리하면 아래와 같다.
query user <계정명>
현재 접속된 사용자 세션 정보를 확인한다.
tscon <세션ID> /dest:console
특정 RDP 세션을 콘솔 세션으로 전환한다.
배치 파일 형태로 만들 경우에는 실제 환경의 query user 출력 형식에 맞춰 세션 ID 위치를 파싱해야 한다.
@echo off
for /f "skip=1 tokens=<세션ID_토큰위치>" %%s in ('query user <계정명>') do (
%windir%\System32\tscon.exe %%s /dest:console
)
query user 출력 형식은 환경마다 다를 수 있다.
따라서 tokens=3 같은 값이 항상 맞는 것은 아니다.
또한 tscon은 관리자 권한이 필요할 수 있다.
운영 환경에서는 보안 정책과 계정 권한 정책을 먼저 확인해야 한다.
운영 자동화다 → Unattended Robot 구성이 우선
RDP 종료 후 실패한다 → 세션 종료 방식 확인
CMD 수동 실행은 되는데 BAT는 실패한다 → 권한, 계정, 세션 ID 파싱 확인
tokens 값이 맞지 않는다 → query user 출력 결과 기준으로 조정
관리자 권한이 필요하다 → 운영 정책에 맞게 승인된 방식으로 처리
RDP 관련 내용도 무조건 숨길 필요는 없다.
다만 실제 계정명, 서버명, 세션 ID, 내부 접속 방식은 제거하고, “원격 세션 상태가 UI 자동화에 영향을 준다”는 구조 중심으로 작성하는 것이 안전하다.
개발 PC에서는 정상 동작했지만 운영 PC 또는 원격 환경에서는 UI 자동화가 실패하는 경우가 있었다.
해상도 고정 여부
디스플레이 배율
절전 모드 여부
화면 잠금 정책
브라우저 업데이트 영향
UiPath 브라우저 확장 프로그램 설치 여부
Windows 업데이트 후 재부팅 영향
Robot 실행 계정 권한
다운로드/임시 폴더 접근 권한
Office 버전
Outlook 프로필 설정
원격 세션 종료 방식
UI 자동화가 간헐적으로 실패한다 → 해상도, 배율, 화면 잠금 확인
브라우저 셀렉터가 갑자기 깨진다 → 브라우저 업데이트 영향 확인
운영 시간 중 실패한다 → 재부팅 또는 세션 상태 확인
Excel/Outlook 오류가 난다 → Office 버전, 프로필, 권한 확인
원격 환경에서만 실패한다 → RDP 세션 상태와 실행 계정 확인
운영 PC 문제는 소스 코드 문제가 아니라 실행 환경 문제인 경우가 많았다.
따라서 개발 완료 후에는 운영 PC 기준으로 별도 점검표를 두는 것이 좋다.
UiPath에서 HTML 메일을 작성해 보냈는데, Outlook에서 테이블 서식이 깨지는 경우가 있었다.
메일 클라이언트는 일반 브라우저처럼 HTML/CSS를 완전하게 처리하지 않는다.
특히 <style> 태그나 복잡한 CSS는 클라이언트에 따라 다르게 보일 수 있다.
중요한 스타일은 인라인으로 넣었다.
<table style='width:800px; border-collapse:collapse;'>
<tr>
<td style='border:1px solid #999; padding:4px;'>값</td>
</tr>
</table>
VB.NET 문자열 안에서 HTML을 만들 때는 큰따옴표보다 작은따옴표를 쓰는 편이 편했다.
<td style='padding:4px;'>값</td>
동적으로 행을 추가할 때는 새로 생성되는 모든 td에 스타일을 직접 넣었다.
strRows = strRows &
"<tr>" &
"<td style='border:1px solid #999; padding:4px;'>" & value1 & "</td>" &
"<td style='border:1px solid #999; padding:4px;'>" & value2 & "</td>" &
"</tr>"
메일 서식이 깨진다 → 인라인 CSS 우선
브라우저에서는 정상인데 Outlook에서 깨진다 → 메일 클라이언트 CSS 제한 의심
동적 테이블 일부만 깨진다 → 생성된 td/tr에 스타일 누락 확인
HTML이 텍스트로 보인다 → HTML Body 옵션 확인
문자열을 나누기만 한다 → Split
나눈 뒤 각 항목을 가공한다 → Split + Select
계속 추가할 데이터다 → Array보다 List
List 개수 확인 → Count
List 마지막 값 → Last(), 단 Count 체크 필요
중복 제거 → Distinct
순서까지 비교 → SequenceEqual
순서 무시 비교 → OrderBy 후 SequenceEqual
중복 무시 비교 → Distinct 후 OrderBy 후 SequenceEqual
정상 종료 → Return
실패를 상위에 전달 → Throw
반복문만 탈출 → Break
Throw 아래 Return → 대부분 불필요
중첩 If가 깊다 → Guard Clause로 분리
String을 하위에서 바꾸고 외부에도 반영 → Out 또는 In/Out
DataTable 내부 내용 수정 → In으로도 반영 가능
DataTable 새 객체 할당 → Out 또는 In/Out 필요
String은 참조 타입이지만 불변 객체 → 값처럼 다루는 편이 안전
동적 URL → 와일드카드 처리
URL 셀렉터의 & → &로 작성
브라우저 변수 중심 → 클래식 방식
Use Application/Browser 중심 → 모던 방식
엘리먼트 개수 확인 → 부모 범위와 로딩 상태 확인
화면 경계 오류 → 해상도, 배율, Activate, Scroll 확인
셀렉터 불안정 → 동적 속성 제거, 안정 속성 위주로 재구성
원격 환경 실패 → 세션 상태와 화면 렌더링 확인
Excel COM 오류 → Excel 프로세스/파일 잠금/Scope 구조 확인
Copy/Paste 불안정 → DataTable 기반 처리 검토
간헐적 오류 → Delay, Save/Close 순서, Scope 단순화
운영 PC에서만 실패 → Office 버전, 권한, 실행 환경 확인
패키지 Restore 실패 → 패키지 소스와 버전 확인
제한된 네트워크 환경 → 로컬 패키지 또는 내부 패키지 저장소 필요
방화벽 요청 → 출발지, 목적지, 프로토콜, 포트, 용도 기준으로 정리
Robot 연결 실패 → Orchestrator 접근 확인
개발 PC만 정상 → 운영 PC의 네트워크, 프록시, 인증서 정책 확인
건별 상태 관리 필요 → Queue
실패 건 재처리 필요 → Queue
단순 실시간 조회 후 있으면 처리 → While 반복 검토
REFramework 사용 → Transaction 단위부터 정의
처리 대상이 없으면 후속 시스템 접근 불필요
UI 자동화 운영 PC → 해상도, 배율, 절전, 잠금 정책 확인
브라우저 자동화 → 브라우저 업데이트 영향 확인
스케줄 실행 → 중복 실행 방지 필요
RDP 종료 후 실패 → 세션 유지 방식 확인
Attended를 Unattended처럼 사용 → 운영 리스크 큼
UiPath 개발에서 자주 발생하는 문제는 대부분 다음 범주로 정리된다.
1. VB.NET 표현식과 데이터 타입 이해 부족
2. Workflow 흐름 제어와 예외 처리 기준 부족
3. UI 자동화 셀렉터와 화면 상태 문제
4. Excel, Outlook, Browser 같은 외부 애플리케이션 의존성
5. 패키지, 방화벽, 운영 환경 차이
6. Queue, REFramework, Trigger 설계 판단 문제
7. 원격 세션과 화면 렌더링 문제
RPA는 개발 PC에서 한 번 성공했다고 끝나는 작업이 아니다.
운영 환경에서 반복 실행되기 때문에, 작은 변수 타입 하나나 셀렉터 조건 하나도 장애 원인이 될 수 있다.
그래서 트러블슈팅은 단순히 “이렇게 하면 됨”으로 끝내기보다 아래 형식으로 정리하는 것이 좋다.
문제 상황
내가 적용한 해결 방식
주의한 점
정리한 기준
이 문서는 그런 기준으로 계속 업데이트한다.