์กฐํ์ ์ค๋ณต ์ฆ๊ฐ ๋ฐฉ์ง ๋ฐ ์ผ๊ด ์ ๋ฐ์ดํธ
๐์ ๋ฆฌ
- ์ฌ์ฉ์ ๊ตฌ๋ถ์ viewCount๋ผ๋ ์ด๋ฆ์ผ๋ก ๋ธ๋ผ์ฐ์ ๋ณ random ๊ฐ์ ๊ฐ์ง ์ฟ ํค๋ฅผ ์ด์ฉ
- ์ํ์ ๋ํ ์ค๋ณต ์ ๊ทผ ์ฒดํน์ ๋ธ๋ผ์ฐ์ ๋ณ ๋๋ค ๊ฐ์ Key๋ก Redis์ SET์ด์ฉ(itemId์ ์ฅ)
- ์ฌ์ฉ์ ๊ตฌ๋ถ์ ์ํ ์ฟ ํค์ ์ค๋ณต ์ ๊ทผ์ ๋ํ ๋ด์ฉ์ ์ ์ฅํ๋ set์ Key๊ฐ์ ์์ ์ ๋ง๋ฃ
Redis์์ itemId๋ก ์กฐํ์๋ฅผ ์ ์ฅ -> ์ผ์ ์๊ฐ ํ DB๋ฐ์
1. ์ ๋ ํน์ ๋ชจ๋ฅผ ์ํฉ์ ๋ํด ๊ตฌํ์์์ ํธ์์ฑ์ ๊ฐ์ ธ๊ฐ๊ธฐ ์ํด Redis๋ก ์ค๋ณต์ ๊ทผ์ ์ฒดํนํ์ต๋๋ค.
ํ๋์ ์ฟ ํค์ ์ค๋ณต์ ๊ทผ์ ๋ํ ์ ๋ณด๋ฅผ ๋ด์์ ๋ ์ฌ์ฉ์๊ฐ ๊ฒ์๊ธ 1000๊ฐ ์ด์์ ์ฝ๋๋ค๋ฉด ๋ฌธ์ ๋ฐ์
-> ์ฟ ํค๋ฅผ ์ฌ๋ฌ ๊ฐ ์ด์ฉํ๋ ๋ฐฉ์์ผ๋ก ํ์ฅํด์ผ ํจ
๊ทธ๋ฌ๋ ํ๋ฃจ์ 1000๊ฐ ์ด์์ ๊ฒ์๊ธ์ ์ฝ๋ ๊ฒฝ์ฐ๊ฐ ์๊ฐ๋ณด๋ค ๋ง์ด ๋ฐ์ํ์ง ์์๊ฒ์ด๊ณ ๊ทธ๋ ๋ค๋ฉด ์๋ฒ์์ ๊ตณ์ด ์์์ ์๋ชจํ ํ์๊ฐ ์๊ธดํฉ๋๋ค..!!
2. ๋ง์ ์ฌ์ฉ์๋ค์ด ์ด์ฉํ ๊ฒ์ผ๋ก ์์ํ๊ณ ์ผ๊ด ์ ๋ฐ์ดํธ๋ฅผ ์งํํ์ต๋๋ค.
10000๋ช ์ ์ฌ์ฉ์๊ฐ 10๋ถ ๋์ ๊ฒ์๊ธ์ 100๊ฐ์ฉ ์กฐํํ๋ฉด 10๋ถ๋์ 100๋ง๋ฒ์ ์ ๋ฐ์ดํธ ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๊ฒ ๋ค!?
์ฌ์ฉ์๊ฐ ์ ๋ค๋ฉด ์ค๋ณต ์ ๊ทผ์ ์ ๊ฑฐํ ๋ง๋น์ ํฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์์ ์ ์์ต๋๋ค. ํนํ ์ค๋ณต ์กฐํ๋ฅผ ์ ํํ ์๊ฐ๋ถํฐ๋ ๋ถํ์ํ ๊ณผ์ ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค Redis์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ก์๋จน๊ธฐ๋ ํ ๊ฑฐ๊ตฌ์
๐๋ชฉํ
์ ๊ฐ ์ด๋ฃจ๊ณ ์ ํ๋ ๋ชฉํ๋ ์๋์ ๊ฐ์์ต๋๋ค.
1. 00:00 ๋ถ์ ๊ธฐ์ค์ผ๋ก ํ๋ฃจ ๋์ ํ ๋ช ์ ์ฌ์ฉ์๋ ํ๋์ ์กฐํ์๋ง ์ฌ๋ฆฐ๋ค.
2. ์ํ ๋ณ ์กฐํ์๋ ํน์ ์๊ฐ๋ง๋ค ์ผ๊ด ๋ฐ์ํ๋ค.
๐์ฌ์ฉ์๋ ์ด๋ป๊ฒ ๊ตฌ๋ถํ์ง?
๊ตณ์ด ์๋ฒ ์์์ ์ฐ๋ฉด์ ์ ์งํ ๋ฐ์ดํฐ๋ ์๋๋ผ๊ณ ํ๋จํ๊ณ IP์ ์ฟ ํค ์ค์ ๊ณ ๋ฏผํ๊ณ ์กฐ์์ ์ํ์ฑ ๋ณด๋ค๋ ์ ํํ ์๋ณ์ ๋ ์ค์ ์ ๋์ด ์ฟ ํค๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ํ์ต๋๋ค.
์ฟ ํค | IP |
์กฐ์ ๊ฐ๋ฅ | ์ฌ์ฉ์๊ฐ ๊ฐ์ IP์ฃผ์ ๊ณต์ ํ๋ ๊ฒฝ์ฐ(ํ๊ต, ๊ณต๊ณต์ฅ์, ํ์ฌ ๋ฑ) ์๋ณ์ด ์ด๋ ค์ |
๐์ฌ์ฉ์๋ง ๊ตฌ๋ณํ๋๊ฒ ์๋๋ผ ์ฌ์ฉ์์ ์ํ ๋ณ ์ ๊ทผ์ ๊ตฌ๋ถํด์ผ ํ๋ค!?
IP๋ฅผ ์ด์ฉ - IP + itemId์ ๋ณด๋ฅผ ์ ์ฅ(์๋ฒ์ ์ ์ฅํด์ผํจ)
cookie์ด์ฉ - Random๊ฐ + itemId์ ํํ๋ก ์ ์ฅ
๐์ฟ ํค...๋ ์ข์ ๋ฐฉ๋ฒ ๋ง๋๊ณ
IP๋ ์๋ฒ์ ์ ์ฅ์ด ๋ถ๊ฐํผํ์ง๋ง ์ฟ ํค๋ ์ ํ์ง๊ฐ ์กด์ฌํ์ต๋๋ค. ๊ณ ๋ฏผ ๊ณผ์ ์ ์ ๋ฆฌํ์๋ฉด ์๋์ ๊ฐ์ต๋๋ค.
ํ๋์ ๋ธ๋ผ์ฐ์ ๋ 300๊ฐ์ ์ฟ ํค๋ฅผ ์ ์ฅํ ์ ์๊ณ ํ๋์ ๋๋ฉ์ธ ๋น 20๊ฐ์ ๊ฐ๋ง ๊ฐ์ง ์ ์๋ค.
-> ์ฆ, cookie_item_1๊ณผ ๊ฐ์ด ์ฌ๋ฌ ๊ฐ์ ์ฟ ํค๋ก ๋๋ ์ ๊ด๋ฆฌํ๋ ๊ฒ์ ๋ถ๊ฐ๋ฅํ ๋ฟ๋๋ฌ ๊ฐ๋ฅํด๋ดค์ ๋นํจ์จ์
-> ๊ทธ๋ ๋ค๋ฉด ํ๋์ ์ฟ ํค๋ก ์ฌ๋ฌ๊ฐ์ id๋ฅผ ๊ด๋ฆฌํด์ผํ๋ฉด ๋๊ฒ ๋ค!
-> ์ฟ ํค์ ์ฉ๋์ 4KB์ด๊ธฐ ๋๋ฌธ์ 1/2/3/4/5/6/7/8/9/ ์ ๊ฐ์ ํํ๋ก ์ ์ฅํ๋ค๋ฉด ์ซ์๋ ์๋ฆฌ์๊ฐ ๋์ด๋๊ณ / ๋ ํฌํจํด์ผํด์ 1500๊ฐ์ ๋..?
์ค์ ๋ก ๋ก์ง์ ์์ฑํด๋ณด์ง๋ ์์์ง๋ง ๊ตฌํ ๊ณผ์ ์์ ๊ณ ๋ คํด์ผ ํ ๋ถ๋ถ๋ค์ด ๊ฝค ๋ณด์๊ณ ์ฑ๋ฅ์ ์ธ ๋ถ๋ถ์ด๋ ์กฐ๊ธ ๋ ๊ฐ๋จํ ๊ตฌํ์ด ๊ฐ๋ฅํ Redis์ set์ ์ด์ฉํ๊ธฐ๋ก ๊ฒฐ์ ํ์ต๋๋ค.
- ๋งค๋ฒ ์ฟ ํค ๊ฐ์ splitํ List์ ์ ๊ทผํ๋ ค๋ ์ํ์ id๊ฐ ์๋์ง ํ์ธ ๋ฐ ์์ผ๋ฉด ์ฟ ํค์ ์ ๋ ฅ
- ํ ์ฌ์ฉ์๊ฐ 1500๊ฐ ์ด์์ ๋ณผ ์ผ์ ๊ฑฐ์ ์๊ฒ ์ง๋ง ๋์ด๊ฐ๋ค๋ฉด ์ฟ ํค๋ฅผ ๋๋ ค์ผ ํ๊ณ ์ด ๋๋ถํฐ ์ด์ ๋ํ ์ถ๊ฐ์ ์ธ ํ์ธ์ด ํ์
๐๊ตฌํ ๋ฐฉ์
์ฌ์ฉ์์ ๊ตฌ๋ถ๋ง ์ฟ ํค๋ก ์งํํ๊ณ ์ดํ ์ค๋ณต์ ๋ํ ์ฒดํน์ Redis์ Set์ ์ด์ฉํ์ต๋๋ค. ๊ตฌํ ๋ฐฉ์์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
Controller
@GetMapping("/{itemId}")
public Response<ItemDetailResponse> getItemDetail(@Valid @PathVariable(value = "itemId") Long itemId, HttpServletRequest request, HttpServletResponse response) {
ItemDetailResponse itemDetailResponse = itemService.getItemDetail(itemId);
/**
* @Description
* 1. ์ฟ ํค๊ฐ ์๋ค๋ฉด ๋ง๋ค๊ณ ์๋ค๋ฉด value Return
* 2. ์ฌ์ฉ์์ ์์ดํ
์ ๋ํ ์ ๊ทผ ํ์ ์ด ์๋ค๋ฉด ์กฐํ์ ์ฆ๊ฐ
*/
String viewCountCookieValue = CookieUtils.getViewCountCookieValue(request, response);
itemService.handleViewCount(itemId, viewCountCookieValue);
return Response.success(HttpStatus.OK, String.format("%d๋ฒ ์ํ ์ ๋ณด์
๋๋ค.",itemId), itemDetailResponse);
}
- ์ฌ์ฉ์๊ฐ ์ฒซ ์ ๊ทผํ์ ๋ viewCount ์ฟ ํค ๋ฐํํด ์ค๋๋ค(์ฌ์ฉ์ ๊ตฌ๋ถ์ ์ํจ)
CookieUtils, TimeUtils
@Component
public class CookieUtils {
public static String getViewCountCookieValue(HttpServletRequest request, HttpServletResponse response) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
Optional<Cookie> viewCountCookie = Arrays.stream(cookies)
.filter(cookie -> cookie.getName().equals("viewCountCookie"))
.findFirst();
if(viewCountCookie.isPresent())
return viewCountCookie.get().getValue();
}
return createAndAddViewCountCookie(response);
}
private static String createAndAddViewCountCookie(HttpServletResponse response) {
String randomCookieValue = UUID.randomUUID().toString();
ResponseCookie cookie = ResponseCookie.from("viewCountCookie", randomCookieValue)
.path("/")
.sameSite("None")
.httpOnly(true)
.secure(true)
.maxAge(60 * 60 * 24)
.build();
response.addHeader("Set-Cookie", cookie.toString());
return randomCookieValue;
}
}
public class TimeUtils {
public static long getRemainingTimeUntilMidnight() {
LocalDateTime now = LocalDateTime.now();
// ํ์ฌ ์๊ฐ๋ถํฐ ๋ด์ผ 00:00:00๊น์ง์ ๋จ์ ์๊ฐ ๊ณ์ฐ
Duration duration = Duration.between(now, now.plusDays(1).withHour(0).withMinute(0).withSecond(0));
// ๊ณ์ฐ๋ ์๊ฐ์ ์ด๋ก ๋ณํ
return duration.getSeconds();
}
}
- ์ฌ์ฉ์ ๊ตฌ๋ถ์ ์ํ ์ฟ ํค๋ 00:00 ๋ง๋ค ๋ง๋ฃ๋๋๋ก ๊ตฌํ(์ฌ์ฉ์์ ์ ๊ทผ ๊ธฐ๋ก ์ด๊ธฐํ)
-> ์์ ์ด ๋์ด๊ฐ๋๋ฐ ์ฟ ํค๊ฐ ๋จ์์๋ค๋ฉด ์กฐํ์ ์ค๋ณต์ฒดํน์ ์ํฅ์ ์ฃผ๊ธฐ ๋๋ฌธ์ ์์ ๋ง๋ค ๋ง๋ฃ๋๋๋ก ๊ตฌํํ์ต๋๋ค!
ItemRedisService, RedisService
@Service
@RequiredArgsConstructor
public class ItemRedisService {
private final RedisService redisService;
// @Description ์ค๋ณต๋ ์ ๊ทผ์ด ์๋๋ผ๋ฉด ์กฐํ์๋ฅผ ์ฆ๊ฐ์ํค๊ณ ์ ๊ทผ ์ฒ๋ฆฌ
public void handleViewCount(String cookieValueOfViewer, Long itemId) {
if (!redisService.isExistInSet(cookieValueOfViewer, itemId))
{
redisService.increaseData("viewCount_item_" + itemId);
redisService.addToSet(cookieValueOfViewer, itemId);
}
}
}
@RequiredArgsConstructor
@Service
public class RedisService {
private final RedisTemplate<String, Object> redisTemplate;
public void increaseData(String key) {
redisTemplate.opsForValue().increment(key);
}
public void addToSet(String key, Long itemId){
if(!redisTemplate.hasKey(key)) {// ํค๊ฐ ์๋ค๋ฉด(set์ด ์๋ค๋ฉด)
redisTemplate.opsForSet().add(key, String.valueOf(itemId)); // set์์ฑ
redisTemplate.expire(key, TimeUtils.getRemainingTimeUntilMidnight(), TimeUnit.SECONDS); // ๋ง๋ฃ๊ธฐ๊ฐ ์ค์
}
else // ๊ธฐ์กด ํค ๊ฐ์ผ๋ก ๋ set์ ์ถ๊ฐ
redisTemplate.opsForSet().add(key,String.valueOf(itemId));
}
public boolean isExistInSet(String key, Long itemId){
return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, String.valueOf(itemId)));
}
}
-> ์ฟ ํค ๊ฐ์ ํค ๊ฐ์ผ๋ก Set์ itemId๋ฅผ ์ ์ฅ ์ดํ ์ฟ ํค๋ฅผ ์ด์ฉํด ์ค๋ณต ์ ๊ทผ ์ฒดํน ๋ฐ ์กฐํ์ ์ฆ๊ฐ ์งํ
- ์๋ฒ์ ์ ์ฅ๋์๋ ์ฟ ํค์ ๋ํ set ์ด๊ธฐํ(์ฌ์ฉ์์ ์์ดํ ๋ณ ์ ๊ทผ ๊ธฐ๋ก ์ด๊ธฐํ)
๐์กฐํ์ ๋งค๋ฒ ์ ๋ฐ์ดํธ ์์ผ์ผํ ๊น์?
์ค๋ณต ์ ๊ทผ์ ์ ๊ฑฐํ์ง๋ง ์ด์ ์ฑ๋ฅ์ ์ธ ๋ฌธ์ ๋ฅผ ์ผ๊ธฐํ ์ ์๋ ๊ฒ์๊ธ์ด ์กฐํ๋ ๋๋ง๋ค ์ ๋ฐ์ดํธ ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋ค๋ ๋ฌธ์ ์ ์ด ๋จ์์์ต๋๋ค. ์ ๊ฐ ์ ํํ ๋ฐฉ๋ฒ์ ์๋์ ๊ฐ์ต๋๋ค.
- Redis์์ itemId๋ก ์กฐํ์๋ฅผ ์ ์ฅ -> ์ผ์ ์๊ฐ ํ DB๋ฐ์