๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ–ฅ๏ธ ๋ญ”๊ฐ€๋ญ”๊ฐ€ํ”„๋กœ์ ํŠธ/โœ๏ธ TIL

[TIL] ํ”„๋กœ์ ํŠธ 25์ผ์ฐจ.. ๊ตฌํ˜„ํ–ˆ๋˜ ๊ธฐ๋Šฅ๋“ค ๋งˆ๋ฌด๋ฆฌํ•˜๊ธฐ!!

by carrot0911 2025. 6. 12.

๐ŸŒž ์˜ค๋Š˜์€ ์–ด๋–ค ํ•˜๋ฃจ์˜€์ง€..

์˜ค์ „์—๋Š” ์ƒํ’ˆ ๊ฐ€๊ฒฉ ๋ณ€๋™ ๋กœ์ง์„ ๋งˆ๋ฌด๋ฆฌํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์„ ์Ÿ์•˜๋‹ค.

// ๊ฐ€๊ฒฉ ๊ด€๋ จ Kafka Listener
@KafkaListener(topics = "${kafka.topic.price}", groupId = "${spring.kafka.consumer.product-combination.group-id}")
public void listenPriceChange(@Header(KafkaHeaders.RECEIVED_KEY) String key, String value) {
    log.info("๐Ÿ“ฅ ๊ฐ€๊ฒฉ๋ณ€๋™ ๋ฉ”์‹œ์ง€ ์ˆ˜์‹ : key={}, value={}", key, value);

    try {
        OperationWrapperDto wrapperDto = JsonHelper.fromJson(value, OperationWrapperDto.class);

        // PRICE_CHANGE ํƒ€์ž…์ด ์•„๋‹Œ ๋ฉ”์‹œ์ง€๊ฐ€ ์ž˜๋ชป ๋“ค์–ด์™”์„ ๊ฒฝ์šฐ ๋ฌด์‹œ
        if (!wrapperDto.operationType().equals(OperationType.PRICE_CHANGE)) {
            log.info("โš ๏ธ PRICE_CHANGE ํƒ€์ž…์ด ์•„๋‹Œ ๋ฉ”์‹œ์ง€ ์ˆ˜์‹ : {}", wrapperDto.operationType());
            return;
        }

        ProductOptionCombinationPriceDto priceDto = JsonHelper.fromJson(wrapperDto.payload(), ProductOptionCombinationPriceDto.class);

        productOptionCombinationService.changePrice(priceDto, wrapperDto, key);
    } catch (NotFoundException e) {
        log.warn("โš ๏ธ ํ•„์ˆ˜ ๋ฐ์ดํ„ฐ ๋ˆ„๋ฝ์œผ๋กœ ๊ฐ€๊ฒฉ ๋ณ€๊ฒฝ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ์‹คํŒจ ({}): {}", e.getErrorCode(), e.getMessage());
    } catch (Exception e) {
        log.error("โŒ ์˜ˆ๊ธฐ์น˜ ๋ชปํ•œ ์˜ค๋ฅ˜๋กœ ๊ฐ€๊ฒฉ ๋ณ€๊ฒฝ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ์‹คํŒจ: {}", e.getMessage());
    }
}

๊ฐ€๊ฒฉ ๊ด€๋ จํ•ด์„œ KafkaListener๋ฅผ ์ƒ์„ฑํ–ˆ๊ณ , ์ด๋ฏธ ๋งŒ๋“ค์–ด์ง„ ํ‹€์— ๋‚ด๊ฐ€ ๋งŒ๋“ค ๋กœ์ง์ด ์ •์ƒ์ ์œผ๋กœ ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒŒ ์ƒ๊ฐ๋ณด๋‹ค ์–ด๋ ค์› ๋‹ค..

@Transactional
public void changePrice(ProductOptionCombinationPriceDto priceDto, OperationWrapperDto wrapperDto, String key) {
    Long combinationId = priceDto.productOptionCombinationId();
    Integer newPrice = priceDto.price();
    String lockKey = "softLock:priceChange:combination:" + combinationId;

    ProductOptionCombination combination = productOptionCombinationRepository.findById(combinationId)
        .orElseThrow(() -> new NotFoundException(ErrorCode.PRODUCT_OPTION_COMBINATION_NOT_FOUND));
    Integer currentPrice = combination.getPrice();

    // ๊ฐ€๊ฒฉ ๋ณ€๊ฒฝ
    if (!newPrice.equals(currentPrice)) {
        combination.updatePrice(newPrice);
        log.info("โœ… ๊ฐ€๊ฒฉ ๋ณ€๊ฒฝ ๊ฐ์ง€ ๋ฐ ๋ฐ˜์˜ ์™„๋ฃŒ: combinationId={}", combinationId);
    } else {
        log.info("โŒ ๊ฐ€๊ฒฉ ๋ณ€๋™ ์—†์Œ: combinationId={}", combinationId);
    }

    // soft Lock ํ•ด์ œ
    redisTemplate.delete(lockKey);
    log.info("๐Ÿ”“ soft lock ํ•ด์ œ ์™„๋ฃŒ: {}", lockKey);

    redisTemplate.opsForValue().set("status:" + priceDto.operationId(), true, Duration.ofMinutes(5));
}

๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‹ค๋ฅธ ํŒ€์›๋“ค์ด ๊ฐœ๋ฐœํ•œ ํ‹€์— ๋งž์ถฐ์„œ ๋กœ์ง์„ ๊ตฌํ˜„ํ–ˆ๊ณ  ์„ฑ๊ณต์ ์œผ๋กœ ๊ฐ€๊ฒฉ ๋ณ€๊ฒฝ์ด ์ด๋ฃจ์–ด์ง€๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์˜คํ›„์—๋Š” ์ง€๊ธˆ๊นŒ์ง€ ๊ฐœ๋ฐœํ•œ ๋‚ด์šฉ์— ๋งž์ถฐ์„œ API ๋ช…์„ธ์„œ๋ฅผ ์ˆ˜์ •ํ–ˆ๋‹ค. ํ•˜๋‚˜ํ•˜๋‚˜ ์ˆ˜์ •ํ•˜๋ฉด์„œ ํ•„์š”ํ•œ ๋‚ด์šฉ๋„ ์ž‘์„ฑํ•˜๋А๋ผ ์‹œ๊ฐ„์„ ๊ฝค ๋งŽ์ด ์žก์•„๋จน์—ˆ๋‹ค.

 

๋งˆ์ง€๋ง‰์œผ๋กœ ํ”„๋กœ์ ํŠธ ๋งˆ๋ฌด๋ฆฌ ๊ฒธ ๊ฐ์ž ๊ฐœ๋ฐœํ•œ ๋‚ด์šฉ ๋ฐœํ‘œํ•˜๊ธฐ๋กœ ํ•ด์„œ ๋‚ด๊ฐ€ ๊ตฌํ˜„ํ•œ ๋‚ด์šฉ๋“ค์— ๋Œ€ํ•ด ์–ด๋–ป๊ฒŒ ๋ณด์—ฌ์ฃผ๋ฉด ์ข‹์„์ง€ ์ƒ๊ฐํ•˜๊ณ  ์ค€๋น„๋ฅผ ํ•˜๋ฉด์„œ ํ•˜๋ฃจ๋ฅผ ๋งˆ๋ฌด๋ฆฌํ–ˆ๋‹ค.

๋‚ด์ผ ๋ฐœํ‘œ ์ˆœ์„œ ์ƒ๊ฐ

๋ฐฐ์†ก, ํƒ๋ฐฐ์‚ฌ, ์†ก์žฅ, ์ถœ๊ณ ์ง€(๋ฌผ๋ฅ˜์„ผํ„ฐ), ์žฌ๊ณ  ๊ฐ์†Œ, ๊ฐ€๊ฒฉ ๋ณ€๊ฒฝ

  1. ํƒ๋ฐฐ์‚ฌ ๊ด€๋ จ ๊ธฐ๋Šฅ ๊ตฌํ˜„
    1. ํƒ๋ฐฐ์‚ฌ ์ƒ์„ฑ
    2. ํƒ๋ฐฐ์‚ฌ ์ƒ์„ฑ
    3. ํƒ๋ฐฐ์‚ฌ ์กฐํšŒ
    4. ํƒ๋ฐฐ์‚ฌ ์ˆ˜์ •
  2. ์ถœ๊ณ ์ง€ ๊ด€๋ จ ๊ธฐ๋Šฅ ๊ตฌํ˜„
    1. ์ถœ๊ณ ์ง€ ์ƒ์„ฑ
    2. ์ถœ๊ณ ์ง€ ์ƒ์„ฑ
    3. ํƒ๋ฐฐ์‚ฌ ์‚ญ์ œ → ์ถœ๊ณ ์ง€์— ๋“ฑ๋ก๋˜์–ด ์žˆ๋Š” ํƒ๋ฐฐ์‚ฌ ์‚ญ์ œ X
    4. ์ถœ๊ณ ์ง€ ์กฐํšŒ
    5. ์ถœ๊ณ ์ง€ ์ˆ˜์ •
    6. ์ถœ๊ณ ์ง€ ์‚ญ์ œ
  3. ์žฌ๊ณ  ๊ฐ์†Œ → ๋ฐฐ์†ก ์ƒ์„ฑ ๊ตฌํ˜„ → ์Šฌ๋ž™ ์•Œ๋ฆผ ๋ฉ”์‹œ์ง€
    1. ๊ฒฐ์ œ ํ›„ ์žฌ๊ณ  ๊ฐ์†Œ ๋กœ์ง ์‹คํ–‰
    2. ์žฌ๊ณ  ๊ฐ์†Œ ์™„๋ฃŒ ํ›„ ๋ฐฐ์†ก ์ƒ์„ฑ ํ† ํ”ฝ์œผ๋กœ ๋ฉ”์‹œ์ง€ ์ „์†ก
    3. ๋ฐฐ์†ก ์ƒ์„ฑ ์„ฑ๊ณต ์‹œ ์Šฌ๋ž™ ์•Œ๋ฆผ ๋ฉ”์‹œ์ง€ ์ „์†ก
    4. ๋ฐฐ์†ก ์ƒ์„ฑ ์‹คํŒจ ์‹œ ์Šฌ๋ž™ ์•Œ๋ฆผ ๋ฉ”์‹œ์ง€ ์ „์†ก + ์žฌ๊ณ  ๋ณต์› ๋กœ์ง ์ง„ํ–‰
  4. ์†ก์žฅ ๊ด€๋ จ ๊ธฐ๋Šฅ ๊ตฌํ˜„
    1. ์†ก์žฅ ์ƒ์„ฑ
  5. ์ถœ๊ณ  → ์นด์นด์˜คํ†ก ์•Œ๋ฆผ ๋ฉ”์‹œ์ง€
    1. ๋ฐฐ์†ก ์ˆ˜์ • → ์†ก์žฅ ๋ฒˆํ˜ธ ๋“ฑ๋ก + ์ƒํƒœ ์ถœ๊ณ ๋กœ ๋ณ€๊ฒฝ ํ›„ ์นด์นด์˜คํ†ก ์•Œ๋ฆผ ๋ฉ”์‹œ์ง€ ์ „์†ก
  6. ๋ฐฐ์†ก์ค‘, ๋ฐฐ์†ก ์™„๋ฃŒ → ์นด์นด์˜คํ†ก ์•Œ๋ฆผ ๋ฉ”์‹œ์ง€
    1. ๋ฐฐ์†ก ์ƒํƒœ ์ˆ˜์ • ํ›„ ์นด์นด์˜คํ†ก ์•Œ๋ฆผ ๋ฉ”์‹œ์ง€ ์ „์†ก
  7. ๊ฐ€๊ฒฉ ๋ณ€๊ฒฝ ๊ตฌํ˜„
    1. ๊ฐ€๊ฒฉ ๋ณ€๊ฒฝ ๋กœ์ง ์ง„ํ–‰

 

๐Ÿ—“๏ธ ๋‚ด์ผ์€ ๋ญ ํ•˜์ง€?!

โœ”๏ธ ๋‚ด๊ฐ€ ๊ตฌํ˜„ํ•œ ๋‚ด์šฉ ๋ฐœํ‘œํ•˜๊ธฐ