Error Channel

Error Channel์˜ ์—ญํ• 

์žฅ์•  ๊ฒฉ๋ฆฌ:

  • ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ „์ฒด ์‹œ์Šคํ…œ์ด ์ค‘๋‹จ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ณ , ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” ๋ฉ”์‹œ์ง€๋งŒ ๋ถ„๋ฆฌํ•˜์—ฌ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ์ „์ฒด ์„œ๋น„์Šค์˜ ๊ฐ€์šฉ์„ฑ๊ณผ ์•ˆ์ •์„ฑ์ด ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.

์ง„๋‹จ ๋ฐ ๋””๋ฒ„๊น…:

  • ์˜ค๋ฅ˜ ํ† ํ”ฝ์— ์ „์†ก๋œ ๋ฉ”์‹œ์ง€๋ฅผ ๋ถ„์„ํ•˜์—ฌ ์–ด๋–ค ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ์‰ฝ๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ค๋ฅ˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(์˜ˆ: ์˜ค๋ฅ˜ ์›์ธ, ์Šคํƒ ํŠธ๋ ˆ์ด์Šค)์™€ ํ•จ๊ป˜ ์ €์žฅ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ œ์˜ ์›์ธ์„ ์ฐพ๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

์žฌ์ฒ˜๋ฆฌ:

  • ์˜ค๋ฅ˜์˜ ์›์ธ์„ ํ•ด๊ฒฐํ•œ ํ›„ ์˜ค๋ฅ˜ ํ† ํ”ฝ์— ์žˆ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ๋‹ค์‹œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ผ์‹œ์ ์ธ ์˜ค๋ฅ˜๋กœ ์ธํ•ด ์‹คํŒจํ•œ ๋ฉ”์‹œ์ง€๋„ ๋‚˜์ค‘์— ์„ฑ๊ณต์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ์•Œ๋ฆผ:

  • Error Channel์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜์—ฌ ์˜ค๋ฅ˜ ๋ฐœ์ƒ ๋น„์œจ์ด๋‚˜ ํŠน์ • ์ž„๊ณ„๊ฐ’์„ ์ดˆ๊ณผํ•˜๋Š” ๊ฒฝ์šฐ ์•Œ๋ฆผ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ๊ฑด๊ฐ• ์ƒํƒœ๋ฅผ ์ง€์†์ ์œผ๋กœ ํ™•์ธํ•˜๊ณ  ํ•„์š”ํ•œ ์กฐ์น˜๋ฅผ ์ทจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹œ์Šคํ…œ ๋ณต์›๋ ฅ ํ–ฅ์ƒ:

  • Error Channel์„ ํ™œ์šฉํ•˜๋ฉด ์‹œ์Šคํ…œ์€ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์˜ค๋ฅ˜์— ๋Œ€ํ•ด ๋”์šฑ ํƒ„๋ ฅ์ ์œผ๋กœ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ํ•ด๋‹น ๋ฉ”์‹œ์ง€๋งŒ ๋”ฐ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ , ์ •์ƒ ๋ฉ”์‹œ์ง€๋Š” ๊ณ„์† ์ฒ˜๋ฆฌ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค

When user Error Channel ?

์žฌ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ

์˜ˆ์‹œ: ์™ธ๋ถ€ ๊ฒฐ์ œ ์„œ๋น„์Šค ํ˜ธ์ถœ

  • E-commerce ์‹œ์Šคํ…œ์—์„œ๋Š” ์ฃผ๋ฌธ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์™ธ๋ถ€์˜ ๊ฒฐ์ œ ์„œ๋น„์Šค๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ด ์™ธ๋ถ€ ์„œ๋น„์Šค๋Š” ๋•Œ๋•Œ๋กœ ์ผ์‹œ์ ์ธ ์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

public class TemporaryPaymentException extends RuntimeException {
    public TemporaryPaymentException(String message) {
        super(message);
    }
}

@KafkaListener(topics = "order-topic")
public void processOrder(String order) {
    try {
        // ... ์ฃผ๋ฌธ ๋กœ์ง ...
        paymentService.charge(order);
    } catch (TemporaryPaymentException e) {
        // ์ผ์‹œ์ ์ธ ๊ฒฐ์ œ ์˜ค๋ฅ˜: Error Channel๋กœ ์ „์†กํ•˜์—ฌ ๋‚˜์ค‘์— ์žฌ์ฒ˜๋ฆฌ
        errorKafkaTemplate.send("payment-retry-topic", order);
    }
}

๋ถ„์„์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ

์˜ˆ์‹œ: ์œ ํšจํ•˜์ง€ ์•Š์€ ์ฃผ๋ฌธ ํ˜•์‹

  • ์ฃผ๋ฌธ ๋ฉ”์‹œ์ง€์˜ ํ˜•์‹์ด ์œ ํšจํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ํ•„๋“œ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ.

public class InvalidOrderFormatException extends RuntimeException {
    public InvalidOrderFormatException(String message) {
        super(message);
    }
}

@KafkaListener(topics = "order-topic")
public void processOrder(String order) {
    try {
        // ... ์ฃผ๋ฌธ ๋กœ์ง ...
        orderValidator.validate(order);
    } catch (InvalidOrderFormatException e) {
        // ์œ ํšจํ•˜์ง€ ์•Š์€ ์ฃผ๋ฌธ ํ˜•์‹: Error Channel๋กœ ์ „์†กํ•˜์—ฌ ๋ถ„์„์„ ์œ„ํ•œ ์ €์žฅ
        errorKafkaTemplate.send("invalid-order-format-topic", order);
    }
}

ํŠน๋ณ„ํ•œ ๋Œ€์‘์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ

์˜ˆ์‹œ: ํฐ ๊ธˆ์•ก์˜ ์ฃผ๋ฌธ ๊ฑฐ์ ˆ

  • ์ƒ์ ์€ ์ผ์ • ๊ธˆ์•ก ์ด์ƒ์˜ ์ฃผ๋ฌธ์— ๋Œ€ํ•ด ์ถ”๊ฐ€ ๊ฒ€ํ† ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์ฃผ๋ฌธ๋“ค์€ ์ž๋™์œผ๋กœ ๊ฑฐ์ ˆ๋˜๊ณ  ๊ด€๋ฆฌ์ž์—๊ฒŒ ์•Œ๋ฆผ์ด ์ „์†ก๋ฉ๋‹ˆ๋‹ค.

public class LargeOrderException extends RuntimeException {
    public LargeOrderException(String message) {
        super(message);
    }
}

@KafkaListener(topics = "order-topic")
public void processOrder(String order) {
    try {
        // ... ์ฃผ๋ฌธ ๋กœ์ง ...
        if (orderService.isLargeOrder(order)) {
            throw new LargeOrderException("Order amount exceeds the limit");
        }
    } catch (LargeOrderException e) {
        // ํฐ ๊ธˆ์•ก์˜ ์ฃผ๋ฌธ ๊ฑฐ์ ˆ: Error Channel๋กœ ์ „์†กํ•˜๊ณ  ๊ด€๋ฆฌ์ž์—๊ฒŒ ์•Œ๋ฆผ
        errorKafkaTemplate.send("large-order-topic", order);
        notificationService.notifyAdmin(e.getMessage());
    }
}

์žฌ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•

์ˆ˜๋™ ์žฌ์ฒ˜๋ฆฌ:

  • ๊ด€๋ฆฌ์ž ๋˜๋Š” ์—ฐ์‚ฐ์ž๊ฐ€ ํŠน์ • ํˆด ๋˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ๊ฒ€ํ† ํ•˜๊ณ  ์ˆ˜๋™์œผ๋กœ ์žฌ์ฒ˜๋ฆฌ๋ฅผ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž๋™ ์žฌ์‹œ๋„:

  • Kafka ๊ฐ™์€ ๋ฉ”์‹œ์ง• ์‹œ์Šคํ…œ์€ ๋ฉ”์‹œ์ง€ ์†Œ๋น„ ์‹คํŒจ์‹œ ์žฌ์‹œ๋„ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ž์ฒด์ ์œผ๋กœ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์„ค์ •์€ Kafka consumer์˜ ์„ค์ •์„ ํ†ตํ•ด ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์žฌ์‹œ๋„ ํšŸ์ˆ˜, ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ ๋“ฑ์„ ์„ค์ •ํ•˜์—ฌ ์ž๋™์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์žฌ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์Šค์ผ€์ฅด๋ง๋œ ์žฌ์ฒ˜๋ฆฌ:

  • "payment-retry-topic"์™€ ๊ฐ™์€ ํŠน์ • ์žฌ์‹œ๋„ ํ† ํ”ฝ์— ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋Š” ๊ฒƒ ์™ธ์—๋„, ์Šค์ผ€์ฅด๋ง๋œ ์ž‘์—…(์˜ˆ: Spring Scheduled Task, Quartz Scheduler ๋“ฑ)์„ ์‚ฌ์šฉํ•˜์—ฌ ์ •๊ธฐ์ ์œผ๋กœ ์ด ํ† ํ”ฝ์„ ํด๋งํ•˜๊ณ  ์žฌ์ฒ˜๋ฆฌํ•  ๋ฉ”์‹œ์ง€๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Dead Letter Queue (DLQ):

  • ์žฌ์‹œ๋„ ํšŸ์ˆ˜๊ฐ€ ํŠน์ • ํšŸ์ˆ˜๋ฅผ ์ดˆ๊ณผํ•  ๊ฒฝ์šฐ ๋ฉ”์‹œ์ง€๋ฅผ Dead Letter Queue(DLQ)๋ผ๋Š” ๋ณ„๋„์˜ ํ† ํ”ฝ์œผ๋กœ ์ „์†กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. DLQ์— ์žˆ๋Š” ๋ฉ”์‹œ์ง€๋Š” ์ˆ˜๋™์œผ๋กœ ๊ฒ€ํ† ๋˜๊ณ , ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•œ ํ›„ ์žฌ์ฒ˜๋ฆฌ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Last updated

Was this helpful?