| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
- java
- iOS Class Chain
- appium
- 포그라운드
- WDA
- XCode Console
- 푸시알림
- XCUITest
- foreground
- Appium Inspector
- 자동화 테스트
- XCUIElementStaticText
- xpath
- push notification
- IOS
- Web Driver Agent
- Today
- Total
성장기의 히동
[Appium, iOS] 푸시 알림 배너를 OCR로 검증하는 방법 - tesseract, tess4j / OCR 자동화 본문
🌼
이전 글에서 푸시 알림이 동작했는가?를 검증하기 위해 시스템 로그를 직접 열어보는 과정에 대해 포스팅 했다.
이번에는 앱이 백그라운드에서 동작할 때 푸시 알림이 동작했는지에 대해 검증하기 위해 OCR을 활용한다.
OCR 작업을 위해 tesseract를 활용했고, Java를 이용해 자동화 하고 있어 tess4j 래퍼 라이브러리를 사용했다.
💭 OCR 도입 배경
푸시 알림이 동작했는가? 를 검증하기 위해 다양한 방법을 시도했다.
푸시 알림이 동작했음을 로그로 검증할 수 있었지만, 조금 불완전한 방법이라는 생각이 들었다.
시스템 로그로 검증함으로써 "푸시 알림 배너가 UI에 등장했다"는 검증할 수 있었지만 몇 가지 아쉬운 점이 있었다.
- 만약 테스트 도중 다른 앱에서 보낸 푸시 알림이 도착하고, UI에 표시된 경우 내가 테스트 하고자 하는 앱에서 온 푸시 알림인지 확인할 방법이 없다.
- 개발자가 의도한 대로 메시지가 정상적으로 출력됐는가? 에 대한 검증이 불가능하다.
즉, 시스템 로그로 검증하는 것 만으로는 많은 허점이 존재할 수밖에 없다는 것이다.
따라서, 시스템 로그와 더불어 실제로 원하는 메시지가 푸시 알림 배너로 등장했는지 확인하기 위해 OCR 도입을 결정했다.
🌱 Tesseract란?
OCR 자동화를 위해 구글링을 하면서 tesseract에 대해 알게 됐다.
Tesseract OCR은 이미지나 스캔된 문서에서 텍스트를 추출하여 컴퓨터가 읽을 수 있는 디지털 텍스트로 변환해주는 오픈소스 광학 문자 인식(Optical Character Recognition) 엔진이다.
Tesseact 설치법에 대해서는 자세히 다루지 않을 예정이니, 설치 방법을 원한다면 다른 글을 참고하는 것이 좋겠다.
만약 하다가 비슷한 오류를 만났다면 언제든 찾아와서 봐주시라!
💦 Tesseract OCR 시도 과정
1. Tesseract를 처음으로 실행하고 라이브러리 파일을 찾지 못한다는 에러를 마주했다.
static Tesseract tesseract;
public void setupOCR() {
tesseract = new Tesseract();
// data 경로 지정
tesseract.setDatapath("/opt/homebrew/Cellar/tesseract/5.5.1/share/tessdata");
// 인식할 언어 설정
tesseract.setLanguage("kor+eng");
}
먼저 Tesseract 활용을 위해 Setup 메서드를 구현했다.
tessdata는 tesseract가 텍스트를 인식하는 데 필요한 핵심 데이터들을 담고 있는 디렉토리라고 한다.
언어 훈련 데이터, traineddata 등 사용자가 데이터를 추가해서 tesseract를 보다 효율적으로 활용할 수 있는 핵심! 이다.
그리고 인식할 언어는 한글과 영어로 설정했다.
< 초기 화면 캡쳐 후 크롭, OCR 과정 구현 코드 >
private String getTextFromPushNotificationBanner(String imageSavePath) throws IOException, TesseractException {
File screenshot = driver.getScreenshotAs(OutputType.FILE);
BufferedImage fullScreenshot = ImageIO.read(screenshot);
// 배너 이미지를 제외한 테스트가 필요한 텍스트 영역 비율값으로 crop
int bannerX = 0;
int bannerY = 0;
int bannerWidth = fullScreenshot.getWidth();
int bannerHeight = (int) (fullScreenshot.getHeight() * 0.15);
BufferedImage croppedImage = fullScreenshot.getSubimage(bannerX, bannerY, bannerWidth, bannerHeight);
// 이미지 저장 경로와 이름 지정
String fileName = "push_notification_cropped_+" + LocalDateTime.now() + ".png";
File croppedFile = new File(imageSavePath + "/" + fileName);
// 메모리에 있는 croppedImage를 가져와서 png 형식으로 변환한 뒤 croppedFile이 지정하는 경로에 저장해라
ImageIO.write(croppedImage, "png", croppedFile);
System.out.println("잘라낸 배너 이미지 저장: " + croppedFile.getAbsolutePath());
// OCR 텍스트 인식
String recognizedText = tesseract.doOCR(croppedImage);
System.out.println("인식된 텍스트:\n" + recognizedText);
return recognizedText;
}
@Test
public void 알람_ON_푸시_알림이_백그라운드에서_정상적으로_동작하는지_확인() throws InterruptedException, TesseractException, IOException {
// OCR 실행 setup
setupOCR();
String routineTitle = "액션 알람 테스트 - 할 일이 한 개일 때";
String toDoTitle = "푸시 알림 테스트";
enterRoutineTabAndClickRoutine(routineTitle);
WebElement playButton = driver.findElement(accessibilityId("play.fill"));
playButton.click();
alarmOnOff();
try {
driver.findElement(accessibilityId("bell"));
} catch (Exception e) {
System.out.println("알람 초기 설정을 꺼진 상태로 설정 후 재시도 하십시오");
fail();
}
driver.runAppInBackground(Duration.ofSeconds(65));
String recognizedText = getTextFromPushNotificationBanner("/Users/heecong/QA/PlanBuddyTest/OCR");
if (recognizedText.contains(toDoTitle) && recognizedText.contains("종료!")) {
System.out.println("✅" + recognizedText + " 테스트 성공");
} else {
System.out.println("❌" + recognizedText + " 테스트 실패");
}
}
}
homebrew로 설치한 tesseract를 위와 같은 코드로 실행했다.
그리고 아래와 같은 에러를 만날 수 있었다. SLF4J는 로깅할 때 사용하는 라이브러리라서 치명적인 오류는 아니었다.
그 아래, tesseract 라이브러리를 로드할 수 없다는 것이 가장 큰 오류였다. 왜냐면 OCR이 안 되니까요..

에러 메시지를 잘 읽어보면, 해당 경로에서 libtesseract.dylib 이라는 파일을 찾지 못하고 있는 것을 파악할 수 있다.
이 에러를 해결하기 위해, 두 가지 방법을 시도했다.
1. Mac 시스템 환경 변수 추가
* 이 방법으로는 해결되지 않아서, 과정만 기술하고 자세히 설명하지 않겠다.
Tesseract는 Java가 아닌 C++로 작성된 네이티브 프로그램이다.
그리고 Tess4J는 Tesseract를 Java에서 사용하기 위한 Wrapper 라이브러리이므로, Tesseract는 로컬 환경에 직접 설치해야 한다. (설치는 이미 해둔 상태)
자바가 Tesseract의 기능을 사용하려면, JVM이 OS의 네이티브 라이브러리 파일(libtesseract.dylib 등)을 찾아서 메모리에 로드해야 한다.
따라서 네이티브 라이브러리 파일을 찾게 해주는 것이 문제 해결 방법이 될 것이라고 생각했다.
env | grep 'tess' 를 터미널에서 실행했고, 아무 것도 조회되지 않는 상황임을 인지했다. -> 시스템 환경 변수에 tesseract 관련 변수가 없다.
따라서 vim으로 ~/.zshrc 파일을 편집했으며, tesseract 실행 파일의 경로를 삽입한 후 저장했다.
- Tesseract 실행 파일 경로 추가
- Tesseract 라이브러리 파일 경로 추가
위와 같은 작업을 완료했음에도 불구하고 같은 에러가 발생했다.

위와 같은 이유로 Mac 시스템에 설정한 환경 변수가 IntelliJ IDE에 전달되지 않았을 것으로 추측했다.
2. IDE(IntelliJ) 에 환경 변수 추가

위와 같이 IDE에 환경 변수를 추가해줬다.
DYLD_LIBRARY_PATH는 네이티브 라이브러리 파일이 실제로 존재하는 위치다.
TESSDATA_PREFIX는 실제로 tessdata 폴더가 존재하는 경로이다.
tesseract 경로가 헷갈려서 정리해봤다
- /opt/homebrew/bin : tesseract 실행 파일 경로
which tesseract 명령어를 Z Shell에서 실행해서 찾을 수 있다.

- /opt/homebrew/Cellar/tesseract/버전/tessdata : tesseract 데이터 파일 경로
명시적으로 추가해주는게 좋다고 한다. 못 찾는 것보단 낫잖아요...
- /opt/homewbrew/lib : 프로그램이 사용해야 하는 동적 라이브러리가 물리적으로 저장된 위치
환경변수에 이 경로를 추가해서 라이브러리를 찾지 못하는 에러를 해결할 수 있었다.
일반적으로는 작성하지 않아도 된다고 하는데, 나는 무엇 때문인지 라이브러리 파일을 찾지 못하는 상황이라서 라이브러리를 찾을 수 있도록 환경변수를 추가해주는 작업이 필수적이었다.
tesseract가 실행되지 않는 문제는 이 방법으로 해결했다. (IDE 환경변수에 라이브러리 파일 위치 경로 추가)
2. 분명 한글과 영어를 사용하겠다고 했는데, 외계어를 마주했다.
분명 tesseract를 설치할 때 kor과 eng를 설치하기 위해 tess-lang을 실행한 것 같았는데 왜 외계어가 나오는지 알 수 없었다..!
외계어를 마주했을 때 너무 당황했는지 캡쳐해둔 이미지가 없어서 재현해봤다. (정성)

캡쳐는 잘 됐고, 잘 잘렸는데 문제가 발생했다.

이게 무슨 외계어인지 알 수 없었다.
그런데 그냥 보자마자 한글을 인식하지 못하고 있다는 건 파악할 수 있었다.
그래서 다시 시도해봤다.

tesseract github에서 한글 훈련 데이터를 다운 받아서, tessdata 폴더에 복사해줬다.
그리고 다시 돌려보면?


많이 발전된 모습을 볼 수 있다.
근데 이제 정확도를 어떻게 높이느냐가 문제라서 일단 kor_vert.traineddata도 추가해줬다. (세로로 써진 한국어 인식)


도대체 뭐가 문젠지 모르겠다.
OCR을 하긴 했는데, 예상치 못한 공백이 너무 많다.
그리고 정확도가 많이 떨어진다. 나한테 가장 중요한 건 "푸시 알림 테스트 종료!" 라는 문구인데, 패턴은 [할 일 제목] 종료! 이다.
해당 할 일을 실행했을 때 정확히 해당 할 일이 종료됐음을 알리는지 검증하고 싶어서 OCR을 진행한 건데, 정확도가 많이 떨어진다.
GMa
ofr 의 영역은 배너 영역이라서 그렇다고 쳐도, 푸시와 종료라는 한글을 명확하게 인식하지 못하는 것은 문제가 크다.
따라서 푸시 알림 자동화에 OCR을 활용할 수 없겠다고 생각한다.
다만, 이 문제를 해결하기 위해 도전한 또 다른 방법은 다음 글에 기술하겠다.
이 경험을 통해 물리적, 논리적 픽셀에 대해 학습할 수 있는 좋은 계기를 마련할 수 있었다.
🥲 후기
OCR을 도전해본 건 진짜 좋은 선택이었다고 생각한다. 많이 아쉽지만.
자동화에 OCR을 사용해보겠다고 생각했던 것도, 나에게는 꽤나 도움이 되는 도전이었던 것 같다.
푸시 알림 자동화를 위해서 OCR을 도입했을 때 괜찮은 결과가 나오지 않아서 많이 아쉽다.
하지만 이 경험을 통해 새로운 기술을 접하고, 적용해보는 도전을 할 수 있어서 재밌었다.
그 과정에서 정확도 향상을 위해 노력한 부분은 다음 글에 기술할테지만, 이 경험을 통해 새로운 지식도 학습할 수 있었다.
그리고 OCR을 지금 상황에서는 활용하기 어렵지만, 이미지가 없는 상태이거나 정말 텍스트로 이뤄진 데이터를 검증할 때는 사용할 수 있을 것 같다.
나중에 이런 경험을 통해 어딘가에서는 써볼 수 있지 않을까? 하는 기대와 도전에 쏟은 내 노력과 시간이 뿌듯하다😂
'☁️ QA' 카테고리의 다른 글
| [Appium, iOS] 포그라운드에서 iOS 앱 푸시 알림 테스트 - 알림센터에 등록되지 않는 경우 (0) | 2025.10.23 |
|---|---|
| [Appium, iOS] 같은 로케이터를 사용하는 버튼을 눌렀을 때, 요소를 찾지 못한다는 에러가 발생하는 이유 (0) | 2025.10.17 |
| [Appium, iOS] TypePickerWheel을 조작할 때 발생한 에러 직면기 및 Locator 리팩토링 (0) | 2025.10.15 |
| [QA/TC] 앱 테스트 중 기능 및 UI 변경 반영 TC 추가 및 변경 (2) | 2025.09.26 |
| [Appium] 같은 계층에 속해있으면서 같은 조건을 만족하는 StaticText를 어떻게 구분해낼 수 있을까? (1) | 2025.09.22 |