[๐Ÿ”ฅTroubleShooting - TicToc๐Ÿ”ฅ] 1์ดˆ๋งˆ๋‹ค ์Šค์ผ€์ค„๋Ÿฌ ๋Œ๋ ค์„œ ๊ฒฝ๋งค ์ข…๋ฃŒ ํ™•์ธ..โ“ ์ด๊ฑฐ ๊ดœ์ฐฎ์„๊นŒ..โ“

._mungยท2025๋…„ 3์›” 8์ผ
0

TicToc

๋ชฉ๋ก ๋ณด๊ธฐ
4/6

๐Ÿ“Œ ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ

ํŒ€์› : ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ
๊ธฐ๊ฐ„ : 2025.01 ~ ์ง„ํ–‰ ์ค‘
๋งํฌ : https://github.com/M-ung/TicToc_Server
์„œ๋น„์Šค ๋‚ด์šฉ : ๋‹น์‹ ์˜ ์‹œ๊ฐ„์— ๊ฐ€์น˜๋ฅผ ๋งค๊ธฐ๋‹ค, ์‹œ๊ฐ„ ๊ฑฐ๋ž˜ ๊ฒฝ๋งค ํ”Œ๋žซํผ


๐Ÿ”ฅTroubleShooting๐Ÿ”ฅ

Problems

๊ฒฝ๋งค ๋“ฑ๋ก์„ ๊ตฌํ˜„ํ–ˆ๋‹ค๋ฉด ๊ฒฝ๋งค ์ข…๋ฃŒ ์‹œ๊ฐ„์— ๋งž๊ฒŒ ๊ฒฝ๋งค ์ข…๋ฃŒ๋„ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.
์ฒ˜์Œ ๊ฒฝ๋งค ์ข…๋ฃŒ ๊ตฌํ˜„์€ ์Šค์ผ€์ค„๋Ÿฌ๋ฅผ 1๋ถ„๋งˆ๋‹ค ๋Œ๋ฆฌ๊ณ  1๋ถ„๋งˆ๋‹ค ํ˜„์žฌ ์‹œ๊ฐ„๊ณผ ๊ฒฝ๋งค ์ข…๋ฃŒ ์‹œ๊ฐ„์ด ์ผ์น˜ํ•˜๋Š” ๊ฒฝ๋งค๋“ค์„ ์ฐพ์•„ ์ข…๋ฃŒ ์ฒ˜๋ฆฌํ–ˆ๋‹ค.

์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

๐Ÿ“ AuctionProgressScheduler.java

@Component
@RequiredArgsConstructor
public class AuctionProgressScheduler {

    private final AuctionRepository auctionRepository;

    @Scheduled(fixedRate = 60000) // 1๋ถ„ ๊ฐ„๊ฒฉ์œผ๋กœ ์‹คํ–‰
    @Transactional
    public void updateAuctionProgress() {
        LocalDateTime now = LocalDateTime.now();

        List<Auction> finishedAuctions = auctionRepository.findByProgressNotAndAuctionCloseTime(AuctionProgress.FINISHED, now);

        finishedAuctions.forEach(Auction::finished);
    }
}

ํ•˜์ง€๋งŒ 1๋ถ„๋งˆ๋‹ค ์Šค์ผ€์ค„๋Ÿฌ๋ฅผ ๋Œ๋ฆฌ๊ณ  DB ์ ‘๊ทผ ๋ฐ ์กฐํšŒ๋ฅผ ํ•˜๋Š” ๊ฑด ๋„ˆ๋ฌด ๋น„ํšจ์œจ์ ์ด๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ด๋ฅผ ํ•ด๊ฒฐํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

1. 1๋ถ„๋งˆ๋‹ค ๋Œ์•„๊ฐ€๋Š” ์Šค์ผ€์ค„๋Ÿฌ ํ•ด๊ฒฐ
2. 1๋ถ„๋งˆ๋‹ค DB ์ ‘๊ทผ ๋ฐ ์กฐํšŒ ํ•ด๊ฒฐ


How

์œ„ ๋ฌธ์ œ๋“ค์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ฒ˜์Œ ์ดˆ์ ์„ ๋‘” ๊ฑด "DB ์ ‘๊ทผ ๋ฐ ์กฐํšŒ"๋ฅผ ์ตœ์†Œํ™”ํ•˜์ž์˜€๋‹ค.

์ตœ์†Œํ™”ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” DB ๋ง๊ณ  ๊ฒฝ๋งค ์ข…๋ฃŒ ์‹œ๊ฐ„์„ ์ €์žฅํ•  "๋ฌด์–ธ๊ฐ€"๊ฐ€ ํ•„์š”ํ–ˆ๋‹ค..

์ฐพ๊ณ  ์ฐพ๊ณ  ์ฐพ๊ณ .... ์ฐพ์€ ๊ฒฐ๋ก ์ด "Redis"์˜€๋‹ค.

๊ทธ๋ž˜์„œ ์œ„ ๋ฌธ์ œ๋ฅผ "Redis"๋ฅผ ํ™œ์šฉํ•ด ํ’€์–ด๋‚˜๊ฐˆ ์ƒ๊ฐ์ด๋‹ค.


Process

์ฒ˜์Œ์—๋Š” ๋‹จ์ˆœํ•˜๊ฒŒ Auction๋ฅผ ๋“ฑ๋กํ•  ๋•Œ DB ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ Redis์—๋„ ๊ทธ๋Œ€๋กœ ์ €์žฅํ•˜์—ฌ ๊ด€๋ฆฌํ•  ์ƒ๊ฐ์ด์—ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด ๋ฐฉ์‹์€ DB ์ ‘๊ทผ ๋ฐ ์กฐํšŒ๋ฅผ ์ค„์ด๊ธด ํ•˜์ง€๋งŒ, Redis์— ๋ถ€ํ•˜๊ฐ€ ์ปค์ง€๊ณ  ๊ฒฐ๊ตญ์—” ์Šค์ผ€์ค„๋Ÿฌ๋ฅผ ๋Œ๋ ค์„œ ๋งค๋ฒˆ Redis์— ์ ‘๊ทผ ๋ฐ ์กฐํšŒ๋ฅผ ํ•ด์•ผํ–ˆ๋‹ค.

๊ทธ๋ž˜์„œ Redis์˜ TTL๊ณผ ์ด๋ฒคํŠธ๋ฅผ ํ™œ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

Redis์— Auction์˜ id์™€ ๊ฒฝ๋งค ์ข…๋ฃŒ ์‹œ๊ฐ„์„ ์ €์žฅํ•  ๋•Œ, TTL์„ ๊ฒฝ๋งค ์ข…๋ฃŒ ์‹œ๊ฐ„๊นŒ์ง€ ์œ ์ง€ํ•˜๊ฒŒ ์„ค์ •ํ–ˆ๋‹ค.
์ด ๊ฒฐ๊ณผ TTL์ด ๋งŒ๋ฃŒ๋˜๋ฉด Redis์—์„œ ์ž๋™์œผ๋กœ ์‚ญ์ œ๋˜๋ฉฐ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.

์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

๐Ÿ“ CloseAuctionAdapter.java

@Component
@RequiredArgsConstructor
public class CloseAuctionAdapter implements CloseAuctionUseCase {
    private final StringRedisTemplate redisTemplate;

    @Override
    public void save(final Long auctionId, LocalDateTime auctionCloseTime) {
        long secondsUntilExpire = Duration.between(LocalDateTime.now(), auctionCloseTime).getSeconds();
        String key = AuctionConstants.AUCTION_CLOSE_KEY_PREFIX + auctionId;
        redisTemplate.opsForValue().set(key, AuctionConstants.EXPIRED_STATUS, secondsUntilExpire, TimeUnit.SECONDS);
    }

    @Override
    public void delete(final Long auctionId) {
        redisTemplate.delete(AuctionConstants.AUCTION_CLOSE_KEY_PREFIX + auctionId);
    }
}

๐Ÿ“ CloseAuctionListener.java

@Component
@RequiredArgsConstructor
public class CloseAuctionListener implements MessageListener {
    private final AuctionRepositoryPort auctionRepositoryPort;
    private final BidRepositoryPort bidRepositoryPort;
    private final WinningBidRepositoryPort winningBidRepositoryPort;
    private final UserScheduleRepositoryPort userScheduleRepositoryPort;
    private final CloseAuctionUseCase closeAuctionUseCase;

    @Override
    public void onMessage(Message message, byte[] pattern) {
        String expiredKey = new String(message.getBody(), StandardCharsets.UTF_8);
        if (expiredKey.startsWith(AuctionConstants.AUCTION_CLOSE_KEY_PREFIX)) {
            Long auctionId = Long.parseLong(expiredKey.replace(AuctionConstants.AUCTION_CLOSE_KEY_PREFIX, ""));
            Auction findAuction = auctionRepositoryPort.findAuctionById(auctionId);
            close(findAuction);
            closeAuctionUseCase.delete(auctionId);
        }
    }

    private void close(Auction findAuction) {
        if (findAuction.getProgress().equals(AuctionProgress.NOT_STARTED)) {
            findAuction.notBid();
        } else {
            findAuction.bid();
            Bid findBid = bidRepositoryPort.findBidByAuctionId(findAuction.getId());
            processWinningBid(findAuction, findBid);
            processUserSchedule(findAuction, findBid);
        }
        auctionRepositoryPort.saveAuction(findAuction);
    }

    private void processWinningBid(Auction auction, Bid bid) {
        bid.win();
        bidRepositoryPort.saveBid(bid);
        winningBidRepositoryPort.saveWinningBid(WinningBid.of(auction, bid));
    }

    private void processUserSchedule(Auction auction, Bid bid) {
        userScheduleRepositoryPort.saveUserSchedule(UserSchedule.of(auction, bid));
    }
}

Result

๋ฌธ์ œ ํ•ด๊ฒฐ๋กœ ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์—ˆ๋‹ค.
1. ๊ธฐ์กด : 1๋ถ„๋งˆ๋‹ค ์Šค์ผ€์ค„๋Ÿฌ ์‹คํ–‰ โ†’ ย ํ•˜๋ฃจ ์•ฝ 1,440 ์ฟผ๋ฆฌ ๋ฐœ์ƒ.
2. ๊ฐœ์„  ํ›„ : TTL ๊ธฐ๋ฐ˜ ์‹ค์‹œ๊ฐ„ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌย  โ†’ ์ข…๋ฃŒ ๊ฒฝ๋งค ์ˆ˜๋งŒํผ ์ฟผ๋ฆฌ ๋ฐœ์ƒ.
3. ํ•˜๋ฃจ 100๊ฑด ๊ธฐ์ค€, ์ข…๋ฃŒํ•œ๋‹ค๋ฉด ์•ฝ 1,340 ์ฟผ๋ฆฌ ๊ฐ์†Œ.

Redis๋ฅผ ํ™œ์šฉํ•œ ๊ฒฝ๋งค ์ข…๋ฃŒ ๊ธฐ๋Šฅ์˜ ์‹œ๋‚˜๋ฆฌ์˜ค๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.


Thoughts

์ฒ˜์Œ์—๋Š” Redis๋ฅผ ๋‹จ์ˆœํžˆ ์บ์‹ฑ์„ ์œ„ํ•œ ์šฉ๋„๋กœ๋งŒ ์ƒ๊ฐํ–ˆ์ง€๋งŒ, ์ด๋ฒˆ ๊ตฌํ˜„์„ ํ†ตํ•ด TTL๋ฅผ ํ™œ์šฉํ•œ ์ด๋ฒคํŠธ ๊ฐ์ง€ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒฝํ—˜์„ ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด DB ๋ถ€ํ•˜๋ฅผ ์ตœ์†Œํ™”ํ•˜๋ฉด์„œ๋„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋งŒ๋ฃŒ ์ด๋ฒคํŠธ๋ฅผ ๊ฐ์ง€ํ•˜๋Š” ๋ฐฉ์‹์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

ํŠนํžˆ, ๊ธฐ์กด์˜ ์Šค์ผ€์ค„๋ง ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ถˆํ•„์š”ํ•œ ์กฐํšŒ์™€ DB ๋ถ€ํ•˜๊ฐ€ ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์ปธ์ง€๋งŒ, Redis TTL ๊ธฐ๋ฐ˜ ์ด๋ฒคํŠธ ๊ฐ์ง€ ๋ฐฉ์‹์€ ์ •ํ™•ํ•œ ์‹œ์ ์— ํ•„์š”ํ•œ ๋กœ์ง๋งŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์–ด ๋”์šฑ ํšจ์œจ์ ์ด์—ˆ๋‹ค.

์ด๋ฒˆ ๊ณผ์ •์„ ๊ฒช์œผ๋ฉด์„œ ๋ถ€ํ•˜๋ฅผ ์ตœ๋Œ€ํ•œ ๋œ ์ฃผ๋Š” ์„ค๊ณ„๋ฅผ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•ด์•ผ๊ฒ ๋‹ค๊ณ  ๋‹ค์งํ–ˆ๋‹ค.


profile
๐Ÿ’ป ๐Ÿ’ป ๐Ÿ’ป

0๊ฐœ์˜ ๋Œ“๊ธ€

๊ด€๋ จ ์ฑ„์šฉ ์ •๋ณด