기본 콘텐츠로 건너뛰기

추천 가젯

리액트 + 비트(Vite)로 모바일 청첩장 만들기 — 2편

모바일 청첩장 시리즈 2편 · R2 갤러리 & Firebase 방명록 Cloudflare R2 · Functions Firebase Firestore 클라우드플레어 R2로 갤러리 저장하고, Firebase로 방명록 달기 안녕하세요, 병민입니다 🙌 1편에서 전체 흐름을 잡았고, 이번엔 사진 업로드/보관 과 방명록 을 연결합니다. 서버는 따로 없고 Cloudflare Pages 를 쓰고 있으니, Pages Functions (= 워커)로 R2에 사전서명 URL을 만들어주고, 프론트에서 그 URL로 바로 업로드하는 구조예요. 방명록은 Firebase DB로 간단·안전하게! 전체 그림 프론트(React) → /api/r2/upload 로 업로드용 URL 요청 → R2에 파일 PUT 프론트(React) → /api/r2/list 로 목록 요청 → 갤러리 렌더 프론트(React) → Firebase SDK로 방명록 작성/조회 1) R2 버킷 & Pages Functions 준비 Cloudflare 대시보드 > R2 > Create bucket (예: wedding-gallery ) 버킷 > Settings > CORS 에서 사이트 도메인 허용(예: https://*.pages.dev , 커스텀 도메인) Pages 프로젝트 > Settings > Functions 에서 R2 바인딩 추가: ...

⚡ concurrent.futures로 쓰레드·프로세스 동시 처리 간편하게!

Concurrent Futures icon

⚡ concurrent.futures로 쓰레드·프로세스 동시 처리 간편하게!

concurrent.futures는 Python의 고수준 병렬·동시성 모듈로, 스레드 기반 또는 프로세스 기반 작업을 `Executor` 추상화로 간단하게 처리할 수 있습니다. 복잡한 스레드 제어 없이 Future 객체만으로 비동기 작업을 직관적으로 관리할 수 있어요.

✅ 1. ThreadPoolExecutor 기본 사용 예

import concurrent.futures
import time

def io_task(n):
    time.sleep(1)
    return f"I/O 결과 {n}"

with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    futures = [executor.submit(io_task, i) for i in range(10)]
    for future in concurrent.futures.as_completed(futures):
        print(future.result())
  • I/O 바운드 작업에서 쓰레드를 효과적으로 활용. `as_completed()`로 완료 순서대로 결과 처리 가능.

✅ 2. ProcessPoolExecutor로 CPU 집약 작업 병렬화

import concurrent.futures

def cpu_task(n):
    return sum(i*i for i in range(n))

with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor:
    results = list(executor.map(cpu_task, [1000000, 2000000, 3000000]))
print(results)
  • CPU 바운드 작업 시 `ProcessPoolExecutor`를 사용하면 GIL 제약 없이 병렬 속도 높일 수 있음.

✅ 3. timeout 설정, 취소, 예외 처리

import concurrent.futures, time

def slow(n):
    time.sleep(n)
    return n

with concurrent.futures.ThreadPoolExecutor() as exec:
    f = exec.submit(slow, 5)
    try:
        print(f.result(timeout=2))
    except concurrent.futures.TimeoutError:
        print("타임아웃 발생")
        f.cancel()
  • Future의 `result(timeout=…)`로 타임아웃 제어 가능하며, `cancel()`을 사용해 작업 중단도 가능합니다.

✅ 4. `executor.map()` vs `submit()` 차이 및 예외 처리 전략

with concurrent.futures.ProcessPoolExecutor() as executor:
    for result in executor.map(cpu_task, [1000000, 2000000, 3000000]):
        print(result)

# submit + as_completed 사용 시:
futures = [executor.submit(cpu_task, n) for n in [1000000,2000000,3000000]]
for f in concurrent.futures.as_completed(futures):
    try:
        print(f.result())
    except Exception as e:
        print("에러:", e)
  • `map()`은 순서 보장, 예외 발생 시 자동 전파. `submit()`+`as_completed()`는 유연하지만 예외를 직접 처리해야 함.

✅ 5. 실제 벤치마크 비교 – 싱글 vs Thread vs Process

import timeit

setup = """
def cpu(x):
    return sum(i*i for i in range(x))
data = [1000000]*4
"""

baseline = timeit.timeit("list(map(cpu, data))", setup=setup, number=5)
threaded = timeit.timeit("""
import concurrent.futures
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as e:
    list(e.map(cpu, data))
""", setup=setup + "\nimport concurrent.futures", number=5)
processed = timeit.timeit("""
import concurrent.futures
with concurrent.futures.ProcessPoolExecutor(max_workers=4) as e:
    list(e.map(cpu, data))
""", setup=setup + "\nimport concurrent.futures", number=5)

print("싱글:", baseline)
print("Thread:", threaded)
print("Process:", processed)
  • CPU 작업에선 `ProcessPoolExecutor`가 가장 빠르지만, 작은 작업일 경우 스레드가 더 유리할 수 있음.

✨ 요약 & 활용 팁 요약

  • I/O 작업엔 **ThreadPoolExecutor**, CPU 집중 작업엔 **ProcessPoolExecutor** 추천
  • `submit()` + `as_completed()`는 유동적인 결과 처리에, `map()`은 순차적 결과에 적합
  • 타임아웃과 취소 기능으로 안정성 고려
  • 작업 크기에 따라 Executor 유형 선택 및 `max_workers` 조절 필요

댓글

가장 많이 본 글

Icons by Flaticon