1. 🖥️ Redis란 무엇인가?
Redis는 Remote Dictionary Server의 약자로, 데이터를 메모리에 저장하여 빠르게 처리하는 오픈 소스 인메모리 데이터베이스입니다. Key-Value 구조로 데이터를 저장하고, 다양한 데이터 구조를 지원하는 것이 특징입니다. 일반적인 데이터베이스와 달리 데이터를 디스크가 아닌 메모리에 저장하기 때문에 읽기/쓰기 속도가 매우 빠릅니다.
Redis의 특징
- 인메모리 데이터베이스: Redis는 모든 데이터를 메모리에 저장하고, 필요에 따라 데이터를 디스크에 백업할 수 있습니다. 이로 인해 데이터 처리 속도가 매우 빠르며, 특히 실시간 데이터 처리에 유리합니다.
- 다양한 데이터 구조 지원: Redis는 단순한 Key-Value 저장소일 뿐만 아니라, 다양한 데이터 타입을 지원합니다. 그 예로 다음과 같은 타입이 있습니다:
- String: 가장 기본적인 형태로, 문자열 데이터를 저장합니다.
- List: 연결 리스트 형태로, 순서가 있는 여러 값을 저장할 수 있습니다.
- Set: 중복되지 않는 값들의 집합을 저장할 수 있습니다.
- Hash: 필드와 값의 쌍으로 데이터를 저장할 수 있습니다. 흔히 자바의 Map과 비슷한 구조입니다.
- Sorted Set: 점수를 기반으로 정렬된 집합을 관리할 수 있습니다.
- 지속성 옵션: Redis는 메모리에 저장된 데이터를 디스크에 백업할 수 있는 옵션을 제공합니다. 이를 통해 RDB 스냅샷과 AOF(Append-Only File)와 같은 방식으로 데이터를 영구적으로 저장하거나 복구할 수 있습니다.
- 스케일링과 확장성: Redis는 클러스터링을 지원하여 여러 노드에 데이터를 분산시켜 저장하고 처리할 수 있습니다. 이를 통해 수평 확장이 가능하며, 큰 데이터를 분산 저장할 수 있습니다.
- Pub/Sub 기능: Redis는 발행-구독(Pub/Sub) 패턴을 지원해, 메시지 브로커로도 사용할 수 있습니다. 실시간 채팅, 알림 서비스 같은 시스템에서 사용됩니다.
- Atomic Operation: Redis는 모든 명령이 원자적(atomic)으로 실행되기 때문에, 복수의 클라이언트가 동시에 동일한 데이터에 접근해도 안전하게 처리됩니다.
Redis의 주요 용도
- 캐싱: Redis는 주로 캐시로 사용됩니다. 서버의 데이터를 Redis에 캐싱함으로써, 데이터베이스에 대한 접근을 줄이고 성능을 향상시킬 수 있습니다.
- 세션 저장소: 사용자의 세션을 Redis에 저장해 빠르게 접근하고, 여러 서버에서 공유할 수 있습니다.
- 순위 시스템: Sorted Set을 사용하여 점수 기반의 순위를 관리할 수 있습니다. 예를 들어, 게임 리더보드나 인기 콘텐츠 순위를 만들 때 유용합니다.
- 실시간 분석: 실시간 데이터를 빠르게 처리할 수 있는 특징 때문에, 로그 처리나 실시간 모니터링 시스템에서도 Redis를 많이 사용합니다.
2. 🧠 Redis의 동작 원리
Redis는 데이터를 메모리에 저장하기 때문에, 매우 빠른 읽기 및 쓰기 성능을 제공합니다. 모든 데이터는 메모리에 상주하며, 필요에 따라 비동기적으로 디스크에 저장하여 데이터를 영구적으로 유지할 수 있습니다. Redis의 동작 원리는 다음과 같습니다:
- Key-Value 데이터 저장: Redis는 Key-Value 방식으로 데이터를 저장하며, 이를 통해 빠르게 데이터를 찾고 처리할 수 있습니다.
- 비동기적 데이터 영속성: Redis는 메모리에 데이터를 저장하지만, RDB(Redis Database Backup) 방식이나 AOF(Append-Only File) 방식을 사용해 데이터를 디스크에 저장하여 영구성을 제공합니다. RDB 방식은 일정 주기로 데이터를 저장하고, AOF 방식은 명령어 단위로 로그를 기록해 복구 시 이 명령을 다시 실행합니다.
- 원자적 작업: Redis의 모든 명령은 원자적으로 처리됩니다. 즉, 동시에 여러 클라이언트가 같은 데이터에 접근하더라도 데이터 손상이 없으며, 항상 일관된 상태를 유지합니다.
- Pub/Sub 동작 원리: Redis의 Pub/Sub 기능은 메시지를 발행자와 구독자가 직접적으로 연결해주는 방식입니다. 발행자는 특정 채널에 메시지를 보내고, 구독자는 해당 채널에 연결된 모든 메시지를 수신합니다. 실시간 알림이나 채팅 같은 시스템에 적합합니다.
3. 📋 Spring에서 Redis 사용 방법 (Maven)
이제 Redis의 동작 원리를 이해했다면, Spring에서 Redis를 어떻게 사용하는지 살펴보겠습니다. Spring에서는 Spring Data Redis를 통해 Redis와 쉽게 연동할 수 있습니다.
📌 Maven 설정
pom.xml 파일에 아래와 같이 의존성을 추가합니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
그리고, application.yml 파일에 Redis 서버 정보를 설정합니다.
spring:
redis:
host: localhost
port: 6379
이제 Spring에서 Redis를 사용할 준비가 완료되었습니다.
📋 캐시 어노테이션 사용
Spring은 Redis와의 캐시 통합을 돕기 위해 몇 가지 유용한 어노테이션을 제공합니다:
- @Cacheable: 메서드 실행 전에 캐시를 먼저 확인하고, 데이터가 없으면 메서드를 실행해 결과를 캐시에 저장합니다.
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
// 메서드가 실행되고, 그 결과가 캐시에 저장됨
return userRepository.save(user);
}
- @CachePut: 메서드를 항상 실행하고 그 결과를 캐시에 업데이트합니다.
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
// 메서드가 실행되고, 그 결과가 캐시에 저장됨
return userRepository.save(user);
}
- @CacheEvict: 캐시에서 데이터를 삭제할 때 사용됩니다.
@CacheEvict(value = "users", key = "#userId")
public void deleteUser(String userId) {
// 캐시에서 해당 유저 데이터가 삭제됨
userRepository.deleteById(userId);
}
4. 🔄 직렬화와 역직렬화 방식 (Java, Jackson)
Redis는 데이터를 저장할 때 직렬화 방식으로 데이터를 변환하여 메모리에 저장합니다. Spring에서 Redis와 데이터를 주고받을 때는 Java 직렬화 또는 JSON 직렬화(Jackson)를 주로 사용합니다.
Java 직렬화
- Java 직렬화는 클래스가 Serializable 인터페이스를 구현해야 합니다.
- Java 직렬화는 데이터가 클 수 있다는 단점이 있으며, 클래스 구조가 변경되면 이전에 직렬화된 데이터를 읽을 때 문제가 발생할 수 있습니다. 이때 serialVersionUID를 설정해주면 클래스의 변경 여부를 체크해 호환성을 보장할 수 있습니다.
Jackson을 사용한 JSON 직렬화
- Jackson을 사용하면 데이터를 JSON 형식으로 직렬화해 저장할 수 있으며, 다른 시스템과의 호환성이 좋습니다.
- Jackson 직렬화에서는 serialVersionUID가 필요하지 않으며, 필드 이름과 구조를 기반으로 직렬화 및 역직렬화가 진행됩니다.
Jackson 직렬화 설정 예시:
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
return template;
}
}
5. 🧑🔧 Redis 사용 시 발생할 수 있는 오류와 처리 방법
Redis를 사용하다 보면 다양한 오류가 발생할 수 있습니다. 그중 자주 발생하는 오류를 살펴보고, 그 처리 방법을 제시합니다.
1. 직렬화/역직렬화 오류 (SerializationException)
발생 상황
Redis에 데이터를 직렬화하여 저장하는 과정에서 직렬화 포맷이 잘못되면 데이터를 읽을 때 문제가 발생할 수 있습니다. 이런 경우, SerializationException과 같은 예외가 발생할 수 있습니다.
처리 방법
직렬화 오류는 Redis 캐시 관련 메서드에서 발생할 수 있으며, 이를 트라이-캐치 블록으로 감싸서 오류를 처리하고, 캐시에서 데이터를 읽지 못하면 DB에서 데이터를 불러오는 방식으로 처리할 수 있습니다.
@Cacheable(value = "users", key = "#userId")
public User getUserFromCache(String userId) {
try {
// Redis에서 데이터를 가져옴
return (User) redisTemplate.opsForValue().get(userId);
} catch (SerializationException e) {
// 직렬화 오류 처리
logger.error("Serialization error when getting user from cache", e);
// 캐시에서 데이터를 읽을 수 없을 경우, DB에서 조회
return userRepository.findById(userId).orElse(null);
}
}
2. Connection 오류 (RedisConnectionFailureException)
발생 상황
Redis 서버와의 네트워크 문제로 인해 발생하는 오류입니다. Redis 서버가 중지되거나, 네트워크 연결이 끊어진 경우 발생할 수 있습니다.
처리 방법
Redis 연결 문제는 LettuceConnectionFactory와 같은 Redis 연결 설정에서 처리할 수 있습니다. 연결 상태를 주기적으로 확인하여 문제가 발생할 경우 다시 연결을 시도할 수 있도록 구성할 수 있습니다.
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory();
connectionFactory.setValidateConnection(true); // 연결 상태를 주기적으로 확인
return connectionFactory;
}
3. 모델 클래스와 캐시 데이터의 불일치 (ClassCastException)
발생 상황
모델 클래스의 필드가 변경되거나 삭제된 경우, Redis에 저장된 데이터와의 불일치로 인해 ClassCastException이 발생할 수 있습니다.
처리 방법
모델 클래스에 @JsonIgnoreProperties(ignoreUnknown = true) 어노테이션을 사용하여, 알 수 없는 필드를 무시하고 역직렬화를 안전하게 수행할 수 있습니다.
@JsonIgnoreProperties(ignoreUnknown = true)
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String name;
private int age;
// 기본 생성자, getter, setter
}
트라이-캐치와 메서드 실행
Spring에서 Redis 캐시를 사용할 때 @Cacheable이나 @CachePut을 사용하는 경우, Redis에서 오류가 발생하더라도 메서드 자체는 계속 실행됩니다. 캐시에서 데이터를 찾지 못하면 메서드의 비즈니스 로직이 실행되고, 그 결과가 캐시에 저장됩니다. 그러나 Redis 오류는 로그에 남게 되므로, 트라이-캐치 블록을 사용해 오류 처리를 통해 안정성을 높일 수 있습니다.
📝요약
- Redis는 빠른 성능의 인메모리 데이터베이스로, 다양한 데이터 구조와 캐시, 세션 관리, 실시간 데이터 처리에 적합합니다.
- Spring Data Redis를 통해 쉽게 Redis와 연동할 수 있으며, @Cacheable, @CachePut, @CacheEvict 등의 어노테이션을 사용해 캐시 관리를 할 수 있습니다.
- Redis 오류는 직렬화/역직렬화 오류, 연결 문제, 타입 불일치 등의 문제가 있을 수 있으며, 이를 적절한 예외 처리와 어노테이션으로 관리해야 합니다.
- 트라이-캐치 블록을 통해 Redis 오류가 발생하더라도 서비스가 정상적으로 동작하도록 처리할 수 있으며, 캐시와 비즈니스 로직이 원활하게 연결됩니다.
'Java' 카테고리의 다른 글
[JAVA] 프로젝트에서 PMD를 이용한 소스 품질 검사 (0) | 2024.11.11 |
---|---|
[JAVA] Spring 돌아보기 Part.3 #JDBC #MyBatis #Multiple DataSources (4) | 2024.10.10 |
[JAVA] Spring 돌아보기 Part.2 #AOP (0) | 2024.09.12 |
[JAVA] Spring 돌아보기 Part.1 #IoC #DI #싱글톤 (0) | 2024.09.10 |
[JAVA] 객체 비교와 Integer 클래스 (0) | 2024.09.09 |