Redis๋ฅผ ํ์ฉํ ์บ์ฑ ๊ตฌํ! (์งง๊ณ ๊ฐ๋จํ ์์)
์ญ์๋ ๋ ๋์ค ๊ณต๋ถํ ๊ฒ ๊น๋จน๊ธฐ ์ซ์ด ๊ธฐ๋ก์ผ๋ก ๋จ๊ธด๋ค.
๋์์
์์ค
application.yml
server:
port: 9090
servlet:
context-path: /
encoding:
charset: UTF-8
enabled: true
force: true
spring:
cache:
type: redis
devtools:
livereload.enabled: true
restart.enabled: true
datasource:
driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
url: jdbc:log4jdbc:mysql://localhost:3309/db๋ช
hikari:
username: ์ฌ์ฉ์
password: ๋น๋ฐ๋ฒํธ
connectionTimeout: 10000
maximum-pool-size: 15
max-lifetime: 600000
readOnly: false
connection-test-query: SELECT 1
jpa:
hibernate:
ddl-auto: create #create update none
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
show-sql: true
# Redis ์ธ์
์ค์
session:
store-type: redis # ์ธ์
์ Redis์ ์ ์ฅ
redis:
namespace: session # Redis์ ์ ์ฅํ ์ธ์
๋ฐ์ดํฐ์ ๋ค์์คํ์ด์ค
timeout: 1800 # ์ธ์
ํ์์์ (์ด ๋จ์)
# Redis ์ค์ ์ถ๊ฐ
data:
redis:
host: localhost # Redis ์๋ฒ ์ฃผ์
port: 6379 # Redis ํฌํธ
password: # (ํ์ํ๋ฉด ๋น๋ฐ๋ฒํธ ์ถ๊ฐ)
timeout: 6000 # ์ฐ๊ฒฐ ํ์์์ (ms)
lettuce:
pool:
max-active: 8 # ์ต๋ ํ์ฑ ์ฐ๊ฒฐ ์
max-idle: 8 # ์ต๋ ์ ํด ์ฐ๊ฒฐ ์
min-idle: 0 # ์ต์ ์ ํด ์ฐ๊ฒฐ ์
max-wait: -1 # ์ต๋ ๋๊ธฐ ์๊ฐ (๋ฌด์ ํ)
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.9'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'com.lsy'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// Logging
implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16'
// redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
// swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
// cache
implementation 'org.springframework.boot:spring-boot-starter-cache'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
RedisConfig
package com.lsy.rediscashing.user.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@EnableCaching
public class RedisConfig {
@Value("${data.redis.host}")
private String host;
@Value("${data.redis.port}")
private int port;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
}
controller
package com.lsy.rediscashing.user.controller;
import com.lsy.rediscashing.user.model.User;
import com.lsy.rediscashing.user.service.UserService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
private final UserService service;
public UserController(@Qualifier("userService1") UserService service) {
this.service = service;
}
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return service.getUserData(id);
}
}
service
package com.lsy.rediscashing.user.service;
import com.lsy.rediscashing.user.model.User;
import com.lsy.rediscashing.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service("userService1")
@Slf4j
@RequiredArgsConstructor
public class UserService {
private final UserRepository repository;
private final RedisTemplate redisTemplate;
public User getUserData(Long id) {
long startTime = System.currentTimeMillis();
String cacheKey = "user:" + id;
// ์บ์๊ฐ ์กฐํ
User cachedData = (User) redisTemplate.opsForValue().get(cacheKey);
// ์บ์๊ฐ์ด ์์ผ๋ฉด ์ผ์๊ฐ ๋ฐํ
if (cachedData != null) {
log.info("redis data exist~");
long endTime = System.currentTimeMillis(); // ๋ ์๊ฐ ์ธก์
long elapsedTime = endTime - startTime; // ์๊ฐ ์ฐจ์ด (๋ฐ๋ฆฌ์ด ๋จ์)
log.info("Cache retrieval time: {} ms", elapsedTime);
return cachedData;
}
// ์บ์๊ฐ์ด ์์ผ๋ฉด db์์ ์กฐํ
log.info("redis data not exist~");
long dbStartTime = System.currentTimeMillis(); // DB ์กฐํ ์์ ์๊ฐ
User findUser = repository.findById(id).orElse(null);
// db์กฐํ ๊ฐ์ด ์์ผ๋ฉด ๋ ๋์ค์ key, value ์ ์ฅ(๋ง๋ฃ์๊ฐ์ 400์ผ๋ก)
if (findUser != null) {
redisTemplate.opsForValue().set(cacheKey, findUser, 400, TimeUnit.SECONDS);
}
long dbEndTime = System.currentTimeMillis(); // DB ์กฐํ ๋ ์๊ฐ
long dbElapsedTime = dbEndTime - dbStartTime; // DB ์กฐํ ์๊ฐ
long endTime = System.currentTimeMillis(); // ์ ์ฒด ์ข
๋ฃ ์๊ฐ
long totalElapsedTime = endTime - startTime; // ์ ์ฒด ์๊ฐ
log.info("DB retrieval time: {} ms", dbElapsedTime);
log.info("Total time: {} ms", totalElapsedTime);
return findUser;
}
}
repository
package com.lsy.rediscashing.user.repository;
import com.lsy.rediscashing.user.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}
User
package com.lsy.rediscashing.user.model;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import java.sql.Timestamp;
@Entity
@Table(name = "user_test")
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
@CreationTimestamp
private Timestamp created_at;
@Builder
public User(String name, String email) {
this.name = name;
this.email = email;
}
}
๊ฐ์ธ ์คํฐ๋ ๊ธฐ๋ก์ ๋ฉ๋ชจํ๋ ๊ณต๊ฐ์ด๋ผ ํ๋ฆฐ์ ์ด ์์ ์ ์์ต๋๋ค.
ํ๋ฆฐ ์ ์์ ๊ฒฝ์ฐ ๋๊ธ ๋ถํ๋๋ฆฝ๋๋ค.
Spring Boot + Redis๋ก ์ธ์ ๊ณต์ ํ๊ธฐ (ํ ์คํธ ์์ & ์์ค ์ฝ๋ ํฌํจ)
Redis๋ฅผ ํ์ฉํ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ ์ธ์ ๊ณต์ ์คํ! (์งง๊ณ ๊ฐ๋จํ ์์)๋ ๋์ค๋ฅผ ๊ณต๋ถํ์ง ์ผ๋ง ์๋์ด์ ๊น๋จน๊ธฐ ์ซ์ด ๊ธฐ๋ก์ผ๋ก ๋จ๊ธด๋ค.๋์์์์คapplication.ymlserver: port: 9090 servlet: context-path: / encodin
yaga.tistory.com
Spring Boot + Redis๋ก ์ค์๊ฐ ๋ญํน ๊ตฌํ (ํ ์คํธ ์์ & ์์ค ์ฝ๋ ํฌํจ)
Redis๋ฅผ ํ์ฉํ ์ค์๊ฐ ๋ญํน ๊ตฌํ! (์งง๊ณ ๊ฐ๋จํ ์์)์ญ์๋ ๋ ๋์ค ๊ณต๋ถํ ๊ฒ ๊น๋จน๊ธฐ ์ซ์ด ๊ธฐ๋ก์ผ๋ก ๋จ๊ธด๋ค.๋์์์์คapplication.ymlserver: port: 8081 servlet: context-path: / encoding: charset: UTF-8 enabled: true
yaga.tistory.com
๋๊ธ