[Sunny Braille] 외부 API 호출, 비동기적으로 처리하기! (ft. Spring Event): 트랜잭션은 어떻게 해야할까,,?

2024. 3. 7. 12:13· PROJECT/Sunny Braille
목차
  1. 💋 인트로
  2. 💋 Before
  3. ✔️ 상황
  4. ✔️ 피드백
  5. 💋 After
  6. ✔️ 개선안
  7. ✔️ 구현 방법
  8. ✔️ 외부 API 호출 시 트랜잭션을 어떻게 해야할까,,?
  9. 💋 참고자료
반응형
반응형

 

 

 

시각장애인을 위한 빠르고 정확한 수학교재 인공지능 점자 변환 서비스, Sunny Braille

어떤 교재든 텍스트 뿐만 아니라 수식도 점자로 변환해주는 교육용 AI 점역 소프트웨어

 

 

 

해바라기 팀 소개 영상 ⇒ https://www.youtube.com/watch?v=cuYIEpQx2po&t=8s

 

Sunny Braille 소스 코드 ⇒ https://github.com/sunnybraille

 

시각장애인을 위한 빠르고 정확한 수학교재 인공지능 점자 변환 서비스, Sunny Braille

어떤 교재든 텍스트 뿐만 아니라 수식도 점자로 변환해주는 교육용 AI 점역 소프트웨어. 시각장애인을 위한 빠르고 정확한 수학교재 인공지능 점자 변환 서비스, Sunny Braille has 4 repositories available

github.com

 

 

 

💋 인트로

지난 포스팅에 이어서 Sunny Braille 서비스의 가장 핵심이자 (사실 모든 것이라고도 할 수 있는) 점자번역 API를 개발중에 있다.

 

팀은 현재 1년 가량 유지되고 있었고, 1년 정도 즈음 내가 합류하게 되었다.

기존 팀에서 사용하던 API가 장고 코드로 이루어져 있는데, 점역 알고리즘과 섞여 있어서 유지보수가 극도로 어려워졌고, 나는 본질적인 기능은 같지만 언어를 자바+스프링으로 바꾸어 재구현을 하고 있다.

 

그중 받았던 피드백을 바탕으로 API 구조를 개선한 내용에 대해 포스팅하려고 한다.

 

💋 Before

✔️ 상황

우리팀에서는 점자 번역 처리를 위해서 4번 이상의 외부 API를 호출하고 있는데, 그 과정이 동기적으로 이루어져 클라이언트가 무한 대기해야 하는 상황이었다.

 

Before

 

✔️ 피드백

문제가 될 것 같다고는 생각했는데,

역시나 서울맹학교 선생님과 User Test에서, 점자 번역할 수학문제의 Pdf를 업로드하고 기다리는 약 1분의 시간동안 아무런 progress bar도 없고, 어떻게 진행되고 있는지 깜깜무소식이라 답답하다는 피드백을 받았다.

 

 

💋 After

✔️ 개선안

새롭게 API를 짜서 제안했다.

그냥 외부 api 4번 호출을 4번의 비동기적인 동작으로 분리하고, 클라이언트에게는 즉시 id 값을 반환한다.

3번의 작업이 진행되는 상황을 체크할 수 있는 status 확인 API만을 제공하고,

작업이 완료되면 클라이언트가 알아서 서버에 결과물을 호출하게 해서 사용자를 무한 대기로부터 해방시켜주기로 함.

 

After

 

✔️ 구현 방법

 

자바에서 비동기로 처리하는 방식에 롱폴링, 소켓, 이벤트 발행 등등이 있다고 하는데,

지난 우아콘에서 접해서 가장 익숙한 스프링 이벤트 발행으로 구현하기로 결심했다. 나머지는 좀더 학습비용이 필요할 것 같아서!

 

 

아래는 서비스 계층 코드인데, 저렇게 최대한 간단히 받은 파일만을 어딘가에 저장해 놓고, 데이터베이스에 간단한 정보만을 저장한 뒤 (외부 API 호출은 하나도 안하고) 클라이언트에게 곧바로 id를 반환한다.

 

@Transactional
public Long register(final MultipartFile file) {
    final String originalFileName = file.getOriginalFilename();
    final String fileName = FileUtil.createRandomFileName(file);

    final String pdfPath = FileUtil.saveFile(file, fileName, Paths.get("src", "main", "pdf"));
    final Translations translations = translationsRepository.save(Translations.of(pdfPath, originalFileName));
    log.info("Saved pdf File in Server. File URI: {}", pdfPath);

    eventPublisher.publishEvent(new OcrRegisterEvent(this, translations));
    log.info("pdf file 저장 이벤트를 발행했습니다!");
    log.info("현재 스레드: {}", Thread.currentThread().getName());

    return translations.getId();
}

 

 

그리고 보면 OcrRegisterEvent를 발행한다. 

그러면 OcrRegisterEventListener가 깨어나서 첫번째 외부 API를 호출하게 된다. 

 

@Slf4j
@NoArgsConstructor
@Component
public class OcrRegisterEventListener {

    private TranslationsRepository translationsRepository;
    private OcrRegisterClient ocrRegisterClient;
    private ApplicationEventPublisher eventPublisher;

    @Autowired
    public OcrRegisterEventListener(
            final TranslationsRepository translationsRepository,
            final OcrRegisterClient ocrRegisterClient,
            final ApplicationEventPublisher eventPublisher
    ) {
        this.translationsRepository = translationsRepository;
        this.ocrRegisterClient = ocrRegisterClient;
        this.eventPublisher = eventPublisher;
    }

    @Async
    @TransactionalEventListener
    @Transactional(propagation = REQUIRES_NEW)
    public void registerOcr(final OcrRegisterEvent event) {
        log.info("현재 스레드: {}", Thread.currentThread().getName());

        final Long id = event.getTranslations().getId();
        final Translations translations = translationsRepository.getById(id);
        final File file = FileUtil.findFile(translations.getPdfPath());

        translations.startOcr();
        final String pdfId = ocrRegisterClient.requestPdfId(file);
        translations.registerPdfId(pdfId);

        eventPublisher.publishEvent(new OcrStatusEvent(this, id));
    }
}

 

 

앞선 방법과 유사하게 마지막에 또 이벤트를 발행한다. 

저렇게 해서 계속해서 이벤트가 연쇄적으로 발행되어 클라이언트는 즉시 반환받은 아이디 값을 가지고 있다가,

진행 상태가 어떤지 확인하거나, 언젠가 점역 과정이  완료되었다면 파일을 다운로드 받을 수 있다. 

 

관련 코드 더보기

 

 

✔️ 외부 API 호출 시 트랜잭션을 어떻게 해야할까,,?

개선된 코드에서 각각 작업을 아래와 같은 트랜잭션으로 묶고 있다.

 

  • 트랜잭션 1: 클라이언트가 파일을 보내자마자, 그냥 일단 데이터베이스에 저장하고 id 즉시 반환
  • 트랜잭션 2: 1번 외부 API 호출 후 데이터베이스에 상태 update
  • 트랜잭션 3: 2번 외부 API 호출 후 데이터베이스에 상태 update
  • 트랜잭션 4: 3번 외부 API 호출 후 데이터베이스에 상태 update
  • 트랜잭션 5: 4번 외부 API 호출 후 데이터베이스에 상태 update

 

고민이,, 트랜잭션 2~5에서 외부 API 호출에 대한 부분도 트랜잭션에 포함되고 있다는 것이다. 

물론 아직 서비스 규모가 작아서 문제가 되지는 않겠지만, 트랜잭션 유지에는 비용이 들기 때문에 필수가 아니면 제거하는게 좋다.

 

고려해봤을 때, 각각의 API에 대해서 하나를 호출한 후에 그 결과에 따라서 우리 데이터베이스에 상황을 업데이트한다. (클라이언트가 현재 진행도에 대해서 조회하는 API를 호출했을 때는 자체 데이터베이스만 찔러봐도 반환할 수 있도록)

진행 상황이 업데이트가 교착될 일은 딱히 없을 것 같다.

 

트랜잭션을 제거하려고 했는데 그렇게 하니, 업데이트가 반영이 되지 않는데 이건 아마도 코드 상의 이슈일 것 같아서 JPA를 암전히 좀더 공부해봐야겠다. (충분히 공부했다 생각했는데 아직도 섣부른거였니...!)

 

값을 업데이트 하는데 트랜잭션이 필수인건 아니어 보이니, 현재 흐름에서 트랜잭션은 중요한 부분이 아니라 제거하게 된다면 아예 없게 만들 수 있을 것 같다는 생각이 들며,,, 이건 성능개선이니 또 우선순위에서 밀리지 않을까 싶다,,킼키

 

💋 참고자료

  • https://tecoble.techcourse.co.kr/post/2022-11-14-spring-event/
  • https://cheese10yun.github.io/event-transaction
  • https://velog.io/@heoseungyeon/REST-API-서버에서-비동기가-필요할-때-1
  • https://www.youtube.com/watch?v=b65zIH7sDug&t=523s

 

도움이 되었다면, 공감/댓글을 달아주면 깃짱에게 큰 힘이 됩니다!🌟
비밀댓글과 메일을 통해 오는 개인적인 질문은 받지 않고 있습니다. 꼭 공개댓글로 남겨주세요!

 

반응형
저작자표시 비영리 변경금지 (새창열림)

'PROJECT > Sunny Braille' 카테고리의 다른 글

[Sunny Braille] 교내 세션 발표인줄 알았는데 반기문 옆자리인 건에 대하여 (GEEF 2024 초청받음)  (23) 2024.03.14
[Sunny Braille] Swagger로 더러워진 내 컨트롤러 코드 인공호흡: 인터페이스로 스웨거 코드 분리하는 방법  (0) 2024.03.04
[Sunny Braille] 팀 합류와 동시에 타이거 팀이 되다..! 장고 코드를 스프링 코드로 바꿔주세요!: Mathpix API 사용해서 수학문제 사진에서 OCR 처리하기 (수식 인식하기)  (2) 2024.02.14
[Sunny Braille] 시각장애인을 위한 수학교재 인공지능 점자변환 서비스, 해바라기 팀에 합류하다  (3) 2024.02.07
  1. 💋 인트로
  2. 💋 Before
  3. ✔️ 상황
  4. ✔️ 피드백
  5. 💋 After
  6. ✔️ 개선안
  7. ✔️ 구현 방법
  8. ✔️ 외부 API 호출 시 트랜잭션을 어떻게 해야할까,,?
  9. 💋 참고자료
'PROJECT/Sunny Braille' 카테고리의 다른 글
  • [Sunny Braille] 교내 세션 발표인줄 알았는데 반기문 옆자리인 건에 대하여 (GEEF 2024 초청받음)
  • [Sunny Braille] Swagger로 더러워진 내 컨트롤러 코드 인공호흡: 인터페이스로 스웨거 코드 분리하는 방법
  • [Sunny Braille] 팀 합류와 동시에 타이거 팀이 되다..! 장고 코드를 스프링 코드로 바꿔주세요!: Mathpix API 사용해서 수학문제 사진에서 OCR 처리하기 (수식 인식하기)
  • [Sunny Braille] 시각장애인을 위한 수학교재 인공지능 점자변환 서비스, 해바라기 팀에 합류하다
깃짱
깃짱
연새데학교 컴퓨터과학과 & 우아한테크코스 5기 백엔드 스타라이토 깃짱
반응형
깃짱
깃짱코딩
깃짱
전체
오늘
어제
  • 분류 전체보기
    • About. 깃짱
    • Weekly Momentum
      • 2024
    • PROJECT
      • AIGOYA LABS
      • Stamp Crush
      • Sunny Braille
    • 우아한테크코스5기
    • 회고+후기
    • Computer Science
      • Operating System
      • Computer Architecture
      • Network
      • Data Structure
      • Database
      • Algorithm
      • Automata
      • Data Privacy
      • Graphics
      • ETC
    • WEB
      • HTTP
      • Application
    • C, C++
    • JAVA
    • Spring
      • JPA
      • MVC
    • AI
    • MySQL
    • PostgreSQL
    • DevOps
      • AWS
      • 대규모 시스템 설계
    • frontend
      • HTML+CSS
    • NextJS
    • TEST
    • Industrial Engineering
    • Soft Skill
    • TIL
      • 2023
      • 2024
    • Linux
    • Git
    • IntelliJ
    • ETC
      • 日本語

블로그 메뉴

  • 홈
  • 깃허브

인기 글

최근 글

태그

  • 함수형프로그래밍
  • 상속
  • 람다와스트림
  • 우테코
  • TDD
  • 스트림
  • 조합
  • Composition
  • Stream
  • lamda
  • 우아한테크코스5기
  • 우아한테크코스
  • OOP
  • 예외
  • 우테코5기
  • Java
  • 람다
  • 상속과조합
  • 컴포지션
  • 레벨로그
hELLO · Designed By 정상우.v4.2.0
깃짱
[Sunny Braille] 외부 API 호출, 비동기적으로 처리하기! (ft. Spring Event): 트랜잭션은 어떻게 해야할까,,?
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.