Spring Boot에서 Redis 사용해보기

말하는 감자·2025년 4월 23일

내일배움캠프

목록 보기
48/73

오늘 에 목표....
장바구니를 만들어야 한다.!!!

redis는 Key:Value 형태를 지닌 NoSQL이니
조금 더 똑똒한 json저장소라고 생각을 해보겠다..

생각중인 장바구니 형태

Key : cart:{userId} <- cart:4213

Value : {
  "storeId": 88,
  "items": [
    {
      "menuId": 1,
      "optionIds": [10, 11], <보류
      "quantity": 2
    },
    {
      "menuId": 3,
      "optionIds": [],  <보ㅓ류라서뺌
      "quantity": 1
    }
  ]
}

보면 알겠지만 일단 제약조건

  • 유저당 딱 하나의 장바구니만 가질 수 있음
  • 유저는 딱 하나의 가게에 대한 메뉴만 담을 수 있음.
  • 메뉴를 여러개 담을 수 있음
  • 메뉴에 대한 옵션이 붙어있음
  • 메뉴 개수를 더하고 뺄 수 있음.

도커위에 레디스 올리기는 여차저차 완료했다

참고 : https://velog.io/@j3beom/SpringBoot-Redis-Redis%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-Caching-%EA%B8%B0%EB%B3%B8-%EB%B0%8F-%ED%99%9C%EC%9A%A9-%EC%98%88%EC%A0%9C





템플릿

@Autowired
private RedisTemplate<String, Object> redisTemplate;

public void example() {
    // String 자료구조
    ValueOperations<String, Object> valueOps = redisTemplate.opsForValue();
    valueOps.set("key", "value");

    // List 자료구조
    ListOperations<String, Object> listOps = redisTemplate.opsForList();
    listOps.leftPush("myList", "item1");
    listOps.leftPush("myList", "item2");

    // Set 자료구조
    SetOperations<String, Object> setOps = redisTemplate.opsForSet();
    setOps.add("mySet", "item1", "item2");
}

처음에 이거보고 RedisTemplate<String, Cart> 하면되겠네!! 라는 생각ㅇ르 햇는데
https://jforj.tistory.com/424

여기보면 String을 다시 오브젝트로 변환해주고 (역직렬화)
오브젝트를 String으로 변환하는 (직렬화) 함수가 따로있길래

그냥 json 써서 String,String하기로했음.

cart = objectMapper.readValue(ops.get(key), Cart.class);
쓰면 String값 cart로 바꿔주는거아니냐 는 생각..





ValueOperations

ValueOperations는 주로 Redis의 간단한 값(또는 문자열)과 관련된 작업을 처리하는 데 사용된다.

ValueOperations<K, V>는 Redis에서 "key" -> "value" 형태의 데이터를 다룰 때 사용하는 인터페이스

📌 Redis 자료구조 중에서 “String” 타입을 다루는 기능만 제공
opsForValue()로 얻을 수 있음.

내부적으로는 Redis의 SET, GET, INCR, APPEND 등의 명령어로 매핑됨.

사용사례

  • 캐싱
    Redis는 자주 액세스하는 데이터를 메모리에 저장하여 데이터베이스 부하를 줄이기 위한 캐싱 솔루션으로 자주 사용된다. ValueOperations를 사용하면 이러한 캐시 된 값을 신속하게 검색하고 업데이트할 수 있으므로 성능이 중요하고 데이터 읽기가 많은 애플리케이션에 적합하다.
  • 세션 저장소
    웹 애플리케이션에서 ValueOperations를 사용하여 세션 데이터를 Redis에 저장할 수 있다. 이를 통해 기존 세션관리(로컬 또는 관계형 데이터베이스)가 너무 느리거나 번거로울 수 있는 분산 환경에서 사용자 상태를 유지하는데 필수적인 빠른 세션 검색 및 업데이트가 가능하다.
  • 카운터
    Redis는 좋아요, 조회수 또는 동시 업데이트가 필요한 기타 측정항목과 같은 카운터를 처리하는데 자주 사용된다.ValueOperations는 이러한 사용 사례에 필수적인 증가 및 감소와 같은 원자적 연산을 제공한다.
  • 실시간 데이터
    채팅 애플리케이션이나 라이브 이벤트 모니터링과 같이 실시간 데이터 처리가 필요한 애플리케이션의 경우 ValueOperations는 메시지 또는 이벤트 데이터를 빠르게 저장하고 검색하는데 도움이 된다.
  • 임시 데이터 저장소
    OTP(일회용 비밀번호) 또는 임시 액세스 토큰과 같이 데이터를 장기간 유지할 필요가 없는 시나리오의 경우 ValueOperations를 사용하여 이러한 임시 데이터를 효율적으로 관리할 수 있다. Redis에 내장된 만료 기능을 사용하면 이러한 키에 TTL 설정이 간단해진다.



메서드

간단한 예제

// ValueOperations 객체 얻기
ValueOperations<String, String> valueOps = redisTemplate.opsForValue();

// 저장
valueOps.set("key", "value");

// 조회
String value = valueOps.get("key");

// 만료 시간 설정 (예: 1분)
valueOps.set("tempKey", "value", Duration.ofMinutes(1));



장바구니에 메뉴 추가하기

public void addCartItem(Long userId, Long storeId, MenuItem value) {
//기존 장바구니 확인
//가게 아이디 다르면 덮어쓰기
//아니면 뒤로 붙이기
// 붙이기전에 메뉴 중복아이디 있으면 quantity 값만 조절
// 없으면 menus 에 추가
// 다시 Redis에 저장
}

해야할 목록이다
갈길이 겁나머니 이모지같은거 다뺌

1. 기존 장바구니 확인
위에서 redisTemplate.opsForValue().get(key);을 쓰면 해당 키값에 맞는 밸류를 가져오는 것으로 확인

<String, String> 이니깐 String으로 받아주고
null이면 값없는거니깐 새로 장바구니만들고 아니면 장바구니 객체로 만들어서 반환하면 되겠지예

return objectMapper.readValue(json, Cart.class);
오브젝트 매퍼가 참 최고다

    public Cart getCartData(Long userId) {
        String key = "cart:" + userId;
        String json = redisTemplate.opsForValue().get(key);

        if (json == null) {
            return null;
        }

        try {
            return objectMapper.readValue(json, Cart.class);
        } catch (JsonProcessingException e) {
            throw new CartException(CartExceptionCode.CART_READ_FAILED);
        }
    }

이거 >기존 장바구니 확인 하는 함수< 임
그... 장바구니 그냥 읽기에서도 쓸거같아서 따로뺏음

3. 다시 redis에 저장

        // 다시 Redis에 저장
        String updatedJson;
        try {
            updatedJson = objectMapper.writeValueAsString(cart);
        } catch (JsonProcessingException e) {
            throw new CartException(CartExceptionCode.CART_SAVE_FAILED);
        }
        ops.set(key, updatedJson, Duration.ofHours(24));
        redisTemplate.opsForValue().set(key, String.valueOf(value));

저장하려면 직렬화 해줘야하니깐 오브젝터 매퍼써서 cart를 String으로 바꿔줌

참고
많이 쓰는 오브젝트 매퍼 메서드

ㅊㅊ
https://devel-repository.tistory.com/84
https://velog.io/@kimsei1124/Spring-Data-Redis-%EB%A1%9C%EC%BB%AC%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0
https://jforj.tistory.com/424

CrudRepository

위에서 뻘짓한거보다 엄청 좋은 방법을 알게됐다
팀원 분이 알려줫다..

이걸 JpaRepository 쓰는것처럼 extends 하면 JPA하는 것 처럼 기본 CRUD를 쉽게할 수 있다고함.............................

사용방법

@Getter
@NoArgsConstructor
@RedisHash("cart")
public class Cart implements Serializable {

	private static final long serialVersionUID = 1L;

	@Id
	private Long userid;

	private Long storeId;

	// TODO 고쳐야 됨
	private List<MenuItem> menus = new ArrayList<>();

	public Cart(Long userid, Long storeId, MenuItem menuItem){
		this.userid = userid;
		this.storeId = storeId;
		this.menus.add(menuItem);
	}
}

@RedisHash("cart") 어노테이션 추가해줬음
userId 기준으로 장바구니가 딱 하나니깐 ....
implements Serializable 적어주면 쌩으로 오브젝트 매퍼써서 jsonString으로 바꿔주던거 아ㅓㄴ해ㅜ저도된다..

private static final long serialVersionUID = 1L;를 해주면
자바가 일관적으로 직렬화/역직렬화를 하는데에 도움이됨..(직렬화 안정성 챙기기용)
1L많이 한다고하니깐 써주기

public interface CartRedisRepository extends CrudRepository<Cart, Long> {

그리고 레퍼지토리 형태좀 바꿔주었다............................
그러면 JPA에서 사용하던 기본적인 함수 다 쓸수있음
정렬 조건 같은건 챙기기 어렵다고한다..

save는 꼬박꼬박써줘야함 끗

ㅊㅊ
https://jydlove.tistory.com/69

profile
대충 데굴데굴 굴러가는 개발?자

2개의 댓글

comment-user-thumbnail
2025년 4월 25일

레디스 그냥 막 쓰고 있었는데 덕분에 많이 배워가요

1개의 답글