ASHD Dev_Blog

asyncio.gather (return_exception)

이재룡
이재룡 Oct 28, 2025

1. asyncio.gather는 어떤 역할을 하나요?

ocrs/enhance_img.py 파일의 이 코드를 보시죠.

asyncio.gather는 여러 개의 비동기 작업(코루틴)을 동시에 실행하고, 모든 작업이 끝날 때까지 기다리는 역할을 합니다.

  • 동시 실행: HTML 문서 안에 이미지가 여러 개 있을 수 있습니다. 각 이미지에 대해 OCR(광학 문자 인식)을 요청하는 것은 네트워크 통신이 필요한 작업이라 시간이 걸립니다. 만약 이미지를 하나씩 순서대로 처리한다면, 첫 번째 이미지의 OCR이 끝날 때까지 기다렸다가 두 번째 이미지 처리를 시작해야 해서 총 시간이 매우 길어집니다.
  • asyncio.gather는 이 OCR 요청들을 동시에 보냅니다. 그리고 파이썬의 이벤트 루프가 각 요청의 응답을 효율적으로 관리하여, 여러 작업을 거의 동시에 처리하는 것처럼 만들어 줍니다. 이를 통해 전체 OCR 처리 시간을 크게 단축할 수 있습니다.
  • ocr_tasksocr_tasks는 [task1, task2, task3]와 같은 리스트입니다. 는 이 리스트를 풀어서 gather(task1, task2, task3)처럼 여러 개의 인자로 전달하는 역할을 합니다.
  • return_exceptions=True: 이 옵션이 매우 중요합니다. 만약 여러 OCR 작업 중 하나가 실패해서 예외(Exception)가 발생했을 때,
    • False (기본값): gather는 즉시 실행을 중단하고 그 예외를 발생시킵니다. 성공한 다른 작업의 결과도 얻을 수 없습니다.
    • Truegather는 중단하지 않고 모든 작업이 끝날 때까지 기다립니다. 그리고 실패한 작업의 결과는 예외 객체(Exception object) 자체로 돌려줍니다. 덕분에 아래 코드처럼 각 결과가 성공했는지, 실패했는지 확인하며 안정적으로 후처리를 할 수 있습니다.

요약: asyncio.gather는 여러 개의 시간이 걸리는 비동기 작업을 동시에 처리하여 프로그램의 전체 실행 속도를 높이는 역할을 합니다.

result에 예외를 넣는 이유: 안정성과 전체 작업 완료 보장

결론부터 말씀드리면, 여러 개의 작업을 동시에 처리할 때, 그중 일부가 실패하더라도 전체 프로그램이 중단되지 않고 나머지 성공한 작업들의 결과를 모두 얻기 위함입니다.

ocrs/enhance_img.py 파일의 이 코드를 다시 보겠습니다.

여기서 return_exceptions=True 옵션이 핵심입니다.

만약 return_exceptions=True가 없다면? (기본 동작)

  • HTML 문서에 이미지가 5개 있다고 가정해 봅시다. asyncio.gather는 5개의 OCR 작업을 동시에 시작합니다.
  • 만약 3번째 이미지의 URL이 잘못되었거나 네트워크 오류가 발생하여 예외(Exception)가 발생하면, gather는 그 즉시 실행을 중단하고 해당 예외를 발생시킵니다.
  • 결과: 프로그램 전체가 멈추게 됩니다. 성공적으로 처리되었을 수도 있는 1, 2, 4, 5번째 이미지의 OCR 결과마저 모두 잃어버리게 됩니다. 단 하나의 실패 때문에 전체 작업이 실패로 돌아가는 것이죠.

return_exceptions=True를 사용하면?

  • 똑같이 5개의 OCR 작업을 동시에 시작합니다.
  • 3번째 이미지에서 예외가 발생하더라도, gather는 실행을 중단하지 않습니다. 모든 작업(성공하든 실패하든)이 끝날 때까지 계속 기다립니다.
  • 모든 작업이 완료되면, gather는 각 작업의 결과를 리스트로 반환합니다.
    • 성공한 작업의 위치에는 결과값(OCR 딕셔셔리)이 들어갑니다.
    • 실패한 작업의 위치에는 **예외 객체(Exception object)**가 들어갑니다.
  • 결과: ocr_results 리스트는 다음과 같은 형태가 됩니다.

"없는게 실제 OCR 결과를 저장할 때는 더 유용한거 아니야?"에 대한 답변

아주 날카로운 지적이십니다. 최종적으로 저장하고 싶은 것은 성공한 OCR 결과뿐인 것이 맞습니다. 하지만 "실패한 결과를 버리는 과정" 을 안정적으로 수행하려면, 일단 성공과 실패를 모두 받아야 합니다.

"예외를 결과에 포함시키지 않는다"는 것은 "실패하면 그냥 무시한다"는 뜻이 아니라, "실패하면 프로그램 전체를 멈춘다"는 의미이기 때문입니다.

 
 
 
 
 
 

[ 1. Variable & Constant ]

Variable

  • var <변수명> <type> = <초기값>
  • 타입 추론 <변수명> := <값>
 

Constant

  • iota : 0번 부터 시작하는 상수 열거형 at (enum을 만들 거나 할 때)
 
 

[ 2. Datatype ]

String

  • backQuote(’’)
  • DoubleQuote(’’)에서만 /n 과 같은 문자열 해석이 가능
Callout icon'
go에서는 type conversion 문제가 발생했을 때,
compile error가 아닌 runtime error가 발생 ( 오류 체크 할 때 주의 필요! )
 

Pointer

  • &k - 주소 할당
  • *p - 해당 주소의 실제 내용
 
 

[ 3. Collection ]

Array

  • zero-base 초기화가 자동
  • var <변수명> [크기]<type>

< 응용 : Slice >

  • python처럼 sub slice 이용
  • slice[start idx : end idx +1 ]

< 응용 : append 활용 >

 

Map

  • 기본형 map [key_type] value_type
  • 키 값 확인 key_val, exists := map["value"]
 
 

[ 4. 조건문 / 반복문 ]

if / else if / else

  • ( )는 안 쓰지만 { }는 필수
  • else if/else를 쓸 때는 반드시 전 조건의 마지막 ‘}’ 와 같은 라인에 써준다.
  • 다중 조건 ‘;’ 로 가능
 

for (no while)

  • 반복문에서 while이 없음
  • ( )는 사용하지 않음

< 응용 : range >

  • python처럼 range 사용 가능 (단 index까지 반드시 포함)
  • 기본적으로 break 사용가능

⇒ 즉 index와 리스트에서 뽑은 데이터 총 2개의 값을 for range에서 이용

< 응용 : 중첩 루프 탈출 : break[Label] >

  • label을 이용하여 for문을 건너뛰고 반복문 탈출 가능
  • label은 for문 앞에 선언되며, 여러 개의 for문을 한번에 탈출 가능
 
 

추천 글

BlogPro logo