일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- IntelliJ
- PostgreSQL
- 명령어
- MariaDB
- codeium
- DBMS
- error
- mysql
- Kibana
- SQL
- pandas
- 쉘스크립트
- oracle
- Linux
- 자바
- github
- git
- mssql
- 리눅스
- Python
- iBatis
- java
- spring
- cursorai
- 오블완
- zset
- docker
- 티스토리챌린지
- analytics4
- Javascript
- Today
- Total
hanker
Redis - Spring Boot + Redis를 사용하여 실시간 검색어 순위 만들기 (3) 화면에 데이터 넘겨주기 본문
Redis - Spring Boot + Redis를 사용하여 실시간 검색어 순위 만들기 (2) Spring Redis 검색어 저장
https://hanke-r.tistory.com/entry/Redis-Spring-Boot-Redis%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EA%B2%80%EC%83%89%EC%96%B4-%EC%88%9C%EC%9C%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-1-%ED%99%94%EB%A9%B4-%EA%B5%AC%EC%84%B1-%EB%B0%8F
hanke-r.tistory.com
이전 글에서 redis에 저장시키는 방법에 대해서 알아봤다.
이번 글에서는 저장된 redis 데이터를 화면으로 가져와서 보여주자.
1. View 단 Script 추가
새로고침과 새로고침이 되었을 때 업데이트 시간을 변경하기 위해 이전에 작성했던 rate.html에 script를 추가해 준다.
<script>
refreshTrending();
// 새로고침 아이콘 클릭 시 실행
function refreshTrending() {
// 여기에 실시간 검색어 갱신 로직 추가
$.ajax({
url: "/api/v1/getTopKeywords",
type: "POST",
dataType: "json",
success: function(data) {
const keywords = data.list;
console.log(keywords)
const trendingList = document.getElementById('trending-list');
trendingList.innerHTML = '';
for (let i = 0; i < keywords.length; i++) {
const keyword = keywords[i];
// 변동 상태에 따른 클래스와 텍스트 설정
let changeClass = '';
let changeText = '';
if (keyword.change == 'UP') {
changeClass = 'up';
changeText = `▲ ${keyword.change}`;
} else if (keyword.change == 'DOWN') {
changeClass = 'down';
changeText = `▼ ${keyword.change}`;
} else if (keyword.change == 'NEW') {
changeClass = 'new';
changeText = 'NEW';
} else {
changeText = '-';
}
// 리스트 아이템 생성
const listItem = document.createElement('li');
listItem.className = 'trending-item';
listItem.onclick = function() { searchTrending(keyword.keyword); };
// 항목 내용 구성
listItem.innerHTML = `
<span class="rank">${i + 1}</span>
<span class="keyword">${keyword.keyword}</span>
<span class="change ${changeClass}">${changeText}</span>
`;
// 리스트에 추가
trendingList.appendChild(listItem);
updateCurrentTime();
}
}
})
// 시간 업데이트
updateCurrentTime();
}
// 현재 시간 업데이트
function updateCurrentTime() {
const now = new Date();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const formattedDate = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${hours}:${minutes}`;
$('.trending-footer').text(`업데이트: ${formattedDate}`);
}
</script>
2. Controller / Service 추가
* KeywordRank.java (VO Class)
public class KeywordRank {
private static final long serialVersionUID = 1L;
private Object keyword;
private int rank;
private RankChange change; // 상승, 하락, 유지, 신규
// getter / setter / Constructor 생성
// ...
}
* Controller
@PostMapping("/api/v1/getTopKeywords")
public String getTopKeywords(Model model) throws IOException {
List<KeywordRank> keywordRanks = redisService.getKeywordRankingWithChanges(10);
model.addAttribute("list", keywordRanks);
return "jsonView";
}
* Service (ZSET이용)
- Redis ZSet(정렬된 집합)을 이용하여 검색어 순위(랭킹)를 가져오고, 이전 순위와 비교하여 순위 변화(상승, 하락, 신규 등)를 판단하는 메서드이다.
private static final String HOT_KEYWORDS = "hot_keywords";
public List<KeywordRank> getKeywordRankingWithChanges(int topN) {
// HOT_KEYWORDS 기준으로 현재 상위 검색어 추출 (ZSET은 점수 높은 순)
// Redis는 점수가 같을 경우 알파벳 순 정렬
Set<Object> currentTop = redisTemplate.opsForZSet()
.reverseRange(HOT_KEYWORDS, 0, topN - 1);
List<KeywordRank> result = new ArrayList<>();
int currentRank = 1;
for (Object keyword : currentTop) {
// 이전 순위(hot_keywords:previous) 찾기
Long prevRank = redisTemplate.opsForZSet()
.reverseRank("hot_keywords:previous", keyword);
RankChange change;
if (prevRank == null) {
change = RankChange.NEW;
} else if (prevRank + 1 > currentRank) {
change = RankChange.UP;
} else if (prevRank + 1 < currentRank) {
change = RankChange.DOWN;
} else {
change = RankChange.NO_CHANGE;
}
result.add(new KeywordRank(keyword, currentRank++, change));
}
return result;
}
- int topN : 상위 몇 개의 검색어를 가져올지 정한다.
- KeywordRank 객체 리스트 (검색어, 현재 순위, 순위 변화 상태 포함)
- HOT_KEYWORDS라는 Redis ZSet에서 점수가 높은 순으로 상위 topN개의 검색어를 가져온다.
- ZSet은 (검색어, 점수) 쌍으로 구성되어 있음
- 점수가 같을 경우 Redis는 사전순 정렬
- hot_keywords:previous라는 이전 검색어 ZSet에서 현재 키워드의 순위 조회 후 순위 지정 (이전 순위는 스케쥴러에 구현)
JobScheduler
@Component
public class Job {
// Redis에 저장된 현재 검색어 순위의 key
private static final String HOT_KEYWORDS = "hot_keywords";
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 2시간마다 실행되는 스케줄러 메서드
@Scheduled(cron = "0 0 0/2 * * *") // cron 표현식: 매 2시간마다 실행
public void backupPreviousRanking() {
// 현재 HOT_KEYWORDS의 모든 요소와 해당 점수를 가져옵니다.
Set<ZSetOperations.TypedTuple<Object>> currentTop =
redisTemplate.opsForZSet().reverseRangeWithScores(HOT_KEYWORDS, 0, -1);
// 이전 순위 데이터를 저장하는 key("hot_keywords:previous")를 삭제하여 초기화합니다.
redisTemplate.delete("hot_keywords:previous");
// currentTop이 null이 아닌 경우(즉, HOT_KEYWORDS에 데이터가 존재하면)
if (currentTop != null) {
// 현재 순위의 각 항목을 순회하면서 hot_keywords:previous에 동일하게 추가합니다.
// 이로써 현재 순위를 이전 순위로 백업하는 효과를 얻습니다.
for (ZSetOperations.TypedTuple<Object> entry : currentTop) {
redisTemplate.opsForZSet().add("hot_keywords:previous", entry.getValue(), entry.getScore());
}
}
}
}
* 스케쥴러를 사용하려면 @EnableScheduling을 추가해야 한다.
@SpringBootApplication
@EnableScheduling // 추가
public class HkApp {
public static void main(String[] args) {
SpringApplication.run(HkApp.class, args);
}
}
여기까지 구현이 완료되면, 구현이 끝이 났다.
화면을 보자
UI 적으로 보완해야 할 부분이 보이긴 하지만 간단하게 구현이 완료됐다.
'SPRING' 카테고리의 다른 글
Spring boot - 스프링 부트 프로젝트 jar파일 생성 방법 (0) | 2025.04.07 |
---|---|
Spring boot - thymeleaf layout 사용 시 설정 (layout 적용 안될 때) (0) | 2025.04.02 |
iBatis - isEmpty, isNotEmpty / isNull, isNotNull 뭘 써야 할까? (빈 값 / null 체크) (0) | 2025.03.18 |
iBatis - isEmpty / isNotEmpty 사용 방법 (null or 빈 값 체크) (0) | 2025.03.17 |
iBatis - isEqual / isNotEqual (문자열 비교) (0) | 2025.03.12 |