ํ์ : ๊ฐ์ธ ํ๋ก์ ํธ
๊ธฐ๊ฐ : 2025.01 ~ ์งํ ์ค
๋งํฌ : https://github.com/M-ung/TicToc_Server
์๋น์ค ๋ด์ฉ : ๋น์ ์ ์๊ฐ์ ๊ฐ์น๋ฅผ ๋งค๊ธฐ๋ค, ์๊ฐ ๊ฑฐ๋ ๊ฒฝ๋งค ํ๋ซํผ
๊ฒฝ๋งค ๋ฑ๋ก์ ๊ตฌํํ๋ค๋ฉด ๊ฒฝ๋งค ์ข
๋ฃ ์๊ฐ์ ๋ง๊ฒ ๊ฒฝ๋งค ์ข
๋ฃ๋ ๊ตฌํํด์ผ ํ๋ค.
์ฒ์ ๊ฒฝ๋งค ์ข
๋ฃ ๊ตฌํ์ ์ค์ผ์ค๋ฌ๋ฅผ 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 ์ ๊ทผ ๋ฐ ์กฐํ ํด๊ฒฐ
์ ๋ฌธ์ ๋ค์ ํด๊ฒฐํ๊ธฐ ์ํด์ ์ฒ์ ์ด์ ์ ๋ ๊ฑด "DB ์ ๊ทผ ๋ฐ ์กฐํ"๋ฅผ ์ต์ํํ์์๋ค.
์ต์ํํ๊ธฐ ์ํด์๋ DB ๋ง๊ณ ๊ฒฝ๋งค ์ข ๋ฃ ์๊ฐ์ ์ ์ฅํ "๋ฌด์ธ๊ฐ"๊ฐ ํ์ํ๋ค..
์ฐพ๊ณ ์ฐพ๊ณ ์ฐพ๊ณ .... ์ฐพ์ ๊ฒฐ๋ก ์ด "Redis"์๋ค.
๊ทธ๋์ ์ ๋ฌธ์ ๋ฅผ "Redis"๋ฅผ ํ์ฉํด ํ์ด๋๊ฐ ์๊ฐ์ด๋ค.
์ฒ์์๋ ๋จ์ํ๊ฒ 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));
}
}
๋ฌธ์ ํด๊ฒฐ๋ก ์๋์ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ์ป์๋ค.
1. ๊ธฐ์กด : 1๋ถ๋ง๋ค ์ค์ผ์ค๋ฌ ์คํ โ ย ํ๋ฃจ ์ฝ 1,440 ์ฟผ๋ฆฌ ๋ฐ์.
2. ๊ฐ์ ํ : TTL ๊ธฐ๋ฐ ์ค์๊ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌย โ ์ข
๋ฃ ๊ฒฝ๋งค ์๋งํผ ์ฟผ๋ฆฌ ๋ฐ์.
3. ํ๋ฃจ 100๊ฑด ๊ธฐ์ค, ์ข
๋ฃํ๋ค๋ฉด ์ฝ 1,340 ์ฟผ๋ฆฌ ๊ฐ์.
Redis๋ฅผ ํ์ฉํ ๊ฒฝ๋งค ์ข
๋ฃ ๊ธฐ๋ฅ์ ์๋๋ฆฌ์ค๋ ์๋์ ๊ฐ๋ค.
์ฒ์์๋ Redis๋ฅผ ๋จ์ํ ์บ์ฑ์ ์ํ ์ฉ๋๋ก๋ง ์๊ฐํ์ง๋ง, ์ด๋ฒ ๊ตฌํ์ ํตํด TTL๋ฅผ ํ์ฉํ ์ด๋ฒคํธ ๊ฐ์ง ๊ธฐ๋ฅ์ ๊ตฌํํ๋ ๊ฒฝํ์ ํ ์ ์์๋ค. ์ด๋ฅผ ํตํด DB ๋ถํ๋ฅผ ์ต์ํํ๋ฉด์๋ ์ค์๊ฐ์ผ๋ก ๋ง๋ฃ ์ด๋ฒคํธ๋ฅผ ๊ฐ์งํ๋ ๋ฐฉ์์ ๊ตฌํํ ์ ์์๋ค.
ํนํ, ๊ธฐ์กด์ ์ค์ผ์ค๋ง ๋ฐฉ์์ ์ฌ์ฉํ๋ฉด ๋ถํ์ํ ์กฐํ์ DB ๋ถํ๊ฐ ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ด ์ปธ์ง๋ง, Redis TTL ๊ธฐ๋ฐ ์ด๋ฒคํธ ๊ฐ์ง ๋ฐฉ์์ ์ ํํ ์์ ์ ํ์ํ ๋ก์ง๋ง ์คํํ ์ ์์ด ๋์ฑ ํจ์จ์ ์ด์๋ค.
์ด๋ฒ ๊ณผ์ ์ ๊ฒช์ผ๋ฉด์ ๋ถํ๋ฅผ ์ต๋ํ ๋ ์ฃผ๋ ์ค๊ณ๋ฅผ ์๊ฐํ ์ ์๋ ๊ฐ๋ฐ์๋ก ์ฑ์ฅํด์ผ๊ฒ ๋ค๊ณ ๋ค์งํ๋ค.