티스토리 뷰

Fire-and-Forget

"실행하고 잊어버린다" — 작업을 시작한 뒤 결과를 기다리지 않고 바로 다음 코드로 넘어가는 패턴입니다.


핵심 개념

// 일반 await: 완료될 때까지 기다림
await DoSomethingAsync();

// Fire-and-forget: 시작만 하고 기다리지 않음
_ = DoSomethingAsync();  // 또는 그냥 DoSomethingAsync();

호출자는 작업이 언제 끝나는지, 성공했는지, 예외가 났는지 전혀 신경 쓰지 않습니다.


언제 쓰나?

  • 로그 전송, 알림, 통계 수집 같이 실패해도 무방한 부수 작업
  • 캐시 갱신, 이메일 발송 등 응답 속도가 중요하고 결과는 불필요한 경우
  • IoT에서 센서 데이터를 큐에 던지고 바로 다음 측정으로 넘어갈 때

문제점

// ❌ 위험한 fire-and-forget
async void SendTelemetry(Data d) {  // async void는 예외가 그냥 터짐
    await httpClient.PostAsync(...);
}

// ❌ Task를 그냥 버리면 예외가 조용히 사라짐
Task.Run(() => DoWorkAsync());  // 예외 삼킴
  1. 예외 무시 — 실패해도 아무도 모름
  2. async void — 예외가 앱 전체를 죽일 수 있음
  3. 생명주기 문제 — 앱 종료 시 작업이 중간에 끊김

올바른 사용법

// ✅ 예외를 잡아서 로그라도 남기기
_ = Task.Run(async () => {
    try {
        await SendTelemetryAsync(data);
    }
    catch (Exception ex) {
        logger.LogError(ex, "Telemetry 전송 실패");
    }
});

// ✅ 확장 메서드로 패턴화
public static void FireAndForget(this Task task, ILogger logger = null) {
    task.ContinueWith(t => {
        if (t.IsFaulted)
            logger?.LogError(t.Exception, "Fire-and-forget 실패");
    }, TaskContinuationOptions.OnlyOnFaulted);
}

// 사용
SendTelemetryAsync(data).FireAndForget(logger);

비동기 루프에서의 활용

// 센서 폴링 루프에서 전송은 신경 안 쓰는 패턴
while (!cancellationToken.IsCancellationRequested)
{
    var reading = await sensor.ReadAsync();
    
    // 전송 결과 기다리지 않고 바로 다음 측정
    _ = eventHubClient.SendAsync(reading).ContinueWith(t => {
        if (t.IsFaulted) logger.LogWarning("전송 실패");
    });
    
    await Task.Delay(100, cancellationToken);
}

고빈도 센서 데이터를 Event Hub로 보내는 상황에서 측정 주기를 전송 지연에 묶어두지 않으려 할 때 유용한 패턴입니다.

 

 

 

반대되는 패턴

 

1. Await (직접 대기) — 가장 기본

// 완료될 때까지 현재 흐름을 멈춤
var result = await SendTelemetryAsync(data);
Console.WriteLine($"전송 완료: {result.StatusCode}");

결과를 받고, 예외도 정상적으로 전파됩니다. 가장 단순하고 안전합니다.


2. Request-Reply 패턴

// 요청 → 응답을 반드시 받아야 다음으로 진행
var response = await httpClient.PostAsync("/api/data", content);
if (!response.IsSuccessStatusCode)
    throw new Exception("전송 실패");

fire-and-forget과 정반대 — 응답이 올 때까지 진행 불가.


3. Acknowledgement (ACK) 패턴

메시지 큐, IoT에서 흔한 패턴입니다.

// 메시지를 보내고, 상대방이 "받았어" 신호를 줄 때까지 대기
var ackReceived = await mqttClient.PublishWithAckAsync(topic, payload);
if (!ackReceived)
    await RetryAsync();

Event Hub / MQTT QoS 1~2가 이 방식입니다.


4. Saga / Compensating Transaction

분산 시스템에서 "실패하면 되돌린다"는 보장.

try {
    await step1.ExecuteAsync();  // DB 저장
    await step2.ExecuteAsync();  // 외부 API 호출
    await step3.ExecuteAsync();  // 알림 발송
}
catch {
    await step2.CompensateAsync();  // 롤백
    await step1.CompensateAsync();
}

5. Outbox 패턴

"보냈다"가 아니라 "반드시 보내진다"를 DB 수준에서 보장.

[비즈니스 로직] → DB 트랜잭션에 메시지도 같이 저장 (Outbox 테이블)
                    ↓
              [별도 워커]가 Outbox를 폴링해서 실제 전송
                    ↓
              성공하면 Outbox에서 삭제

fire-and-forget의 완전한 반대 — 전송 실패해도 재시도가 보장됩니다.


스펙트럼으로 보면

[보장 약함] ←————————————————————→ [보장 강함]

Fire-and-Forget → Await → ACK → Saga → Outbox
   "몰라도 됨"    "기다림"  "확인"  "보상"  "반드시 됨"

 

데이터 종류적합한 패턴
고빈도 센서 Raw 데이터 Fire-and-Forget (유실 허용)
알람/임계값 초과 이벤트 ACK 패턴 (유실 불가)
설비 제어 명령 Request-Reply or Saga
청구/정산 관련 Outbox (반드시 보장)

센서 데이터는 fire-and-forget이 맞지만, 알람처럼 놓치면 안 되는 이벤트는 ACK나 Outbox로 분리하는 게 실무 설계입니다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/07   »
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 29 30 31
글 보관함