본문 바로가기

Programing/OpenSource

[JobRunr] 지연된 작업 기능으로 문자 메시지 상태 조회하기

2022년 8월 진행했던 프로젝트 중에 문자 메시지 발송하는 기능이 있었다.

백오피스를 이용하는 직원들은 문자로 고객과 커뮤니케이션을 하는 일이 있었다.

자주 사용하는 문구 기능과 더불어 시스템을 통한 메시지 발송 기능으로 시간 비용을 줄일 수 있었다.

상태 조회

문제는 문자 메시지 발송 후 상태 조회를 하는 방식이었다.

 

문자 메시지는 서드파티를 통해 수행했는데 1) 폴링 방식과 2) 콜백을 받는 방법 의 크게 두 가지 모두 제공하고 있었다.

문자 메시지는 발송 즉시 상태는 곧바로 알 수 없었고 수초 이상 걸릴 수 있었다.

따라서 언제 상태가 제대로 되는지는 알 수 없기에 후자 방식이 바람직했다.

 

하지만 콜백을 받기 위해서는 외부에서 훅(hook)을 줄 수 있는 인터페이스를 준비해야 했다.

인프라적인 지원이 필요했는데 프로젝트에 요구하는 시간이 길지 않았기에 폴링하는 전자를 선택하였다.

폴링 방식의 구현 방법들

상태를 매번 가져오는 방법을 사용한다면 언제 가져오고 반복 주기종료 조건 등이 필요하다.

별도의 상태를 조회하는 기능의 버튼이 있었으면 명료했지만 기획자는 알아서 상태가 최신화 되기를 희망했다.

 

최초 설계는 문자를 보내고 상태를 보여주는 화면을 요청시 상태 조회를 하도록 구현을 했다.

문자 발송 내역 조회(GET)에 일종에 부수 효과(side-effect)를 가지는 안티패턴이었다.

조회시 상태 갱신의 단점

2년 정도 사용을 했는데 주기적으로 수작업으로 조회를 해주어야 하는 단점이 생겼다.

발송을 하지만 실제로 잘 보내졌는지 조회하는 화면을 사용자가 보는 비중이 약 30.8% 뿐이었기 때문이다.

심지어 발송 후 특정 기간이 지나면 상태 조회가 안되었다.
따라서 상태 갱신이 안된 69.2%는 조회가 가능한 시기가 지나기 전에 상태 갱신을 해야 했다.

JobRunr by Josh Long

그러던 중 2024년 4월경 조쉬 롱의 Spring Tips 영상에서 JobRunr이라는 오픈소스 라이브러리를 소개받았다.

내가 딱 필요로 하는 지연된 작업(delayed jobs)이라는 부분을 제공하고 있었다.

 

Scheduling jobs이란 특정 시점을 기점으로 임의의 시점에 딱 한번 수행되는 작업을 이야기했다.

 

의존 라이브러리

JobRunr은 스프링 부트 스타터를 제공하고 있었다. Gradle Kotlin DSL로는 dependencies에 아래의 문장을 추가하면 된다.

implementation("org.jobrunr:jobrunr-spring-boot-3-starter:7.3.1")

이 스타터를 프로젝트에 추가하면 아래와 같은 두 jar 파일이 의존성에 추가된다.

 

스프링 부트 스타터 추가 후 애플리케이션 설정으로 JobScheduler, 백그라운드 작업 서버, 대시보드 등을 켜거나 끌 수 있다.

application.yml

org:
  jobrunr:
    background-job-server:
      enabled: false
    dashboard:
      enabled: false
    database:
      skip-create: true

공통 설정 파일에는 모든 설정이 동작하지 않도록 하였다.

이유는 필요한 환경의 프로파일(profile)에서만 켜는 것이 좋겠다 판단했기 때문이다.

 

또한 database 생성 및 업데이트가 켜져있는 것이 기본값인데 애플리케이션은 CRUD 정도의 권한만 가지고 table 생성, 수정, 삭제 권한은 주지 않기 때문에 수동으로 관리를 위해 org.jobrunr.database.skip-create는 true로 동작하지 않도록 설정을 했다.

 

참고로 테이블은 flyway 같이 증분적인 스크립트를 라이브러리 소스에 포함하고 있었다.

jar 명령으로 마이그레이션 스크립트를 생성할 수 있다고 한다.

java -cp jobrunr-${jobrunr.version}.jar:slf4j-api.jar org.jobrunr.storage.sql.common.DatabaseSqlMigrationFileProvider {databaseType} ({tablePrefix})

 

 

databaseType에는 'db2', 'h2', 'mariadb', 'mysql', 'oracle', 'postgres', 'sqlite', 'sqlserver'를 넣을 수 있다.

$ java -cp jobrunr-7.3.1.jar:slf4j-api-2.0.16.jar org.jobrunr.storage.sql.common.DatabaseSqlMigrationFileProvider mysql
==========================================================
======== JobRunr Database SQL Migration Provider =========
==========================================================
Successfully created all SQL scripts for mysql!

mysql 타입으로 생성을 해보면 7.3.1 버전 기준으로 v000 ~ v015의 스크립트 파일이 만들어졌다.

이 파일을 이용하여 수동으로 테이블을 생성 및 업데이트가 가능하다.

 

핵심 비즈니스 로직은 상태의 갱신이 필요한지 확인하고 필요하다면 갱신해주는 부분인데 이부분은 핵심 비즈니스 로직이다.

JobRunr은 비즈니스 보다는 인프라적인 요소, 클린 아키텍처의 표현에서는 세부사항(detail)이다.

따라서 예약하는 부분은 port.out 의 대상으로 생각했다.

 

헥사고날 아키텍처의 그림으로 표현해보면 아래와 같다.

Port And Adapter Architecture에 적용

 

1. 애플리케이션 서비스인 TextMessageService는 문자 메시지 발송 후 TextMessageStatusScheduler에 예약 메시지를 전달한다.

2. TextMessageStatusScheduler의 구현체인 JobRunrTextMessageStatusScheduler는 JobRunr 라이브러리를 이용하여TextMessageStatusUpdater의 로직을 특정 시간 이후 업데이트를 수행하도록 에약(schedule)한다.

3. JobRunr의 백그라운드 잡 서버(BackgroundJobServer)는 처리할 작업을 Polling한다.

4~5. TextMessageStatusUpdater는 리포지토리 및 문자 메시지 상태 조회를 하여 갱신한다.