스프링 레디스 사용하기 (1)

Hwalyo·2024년 2월 1일
0
post-thumbnail
post-custom-banner

개발 환경

  • Spring-boot 3.2.2
  • spring-boot-starter-data-redis 3.2.2
  • testcontainers 1.19.3
  • junit-jupiter 1.19.3
  • JDK 17

1. Redis 설치

https://github.com/microsoftarchive/redis -> Rleases에서 .msi 설치 폴더로 설치

  • Redis 서버 실행 파일 만들기
	cd "Redis가 설치된 폴더 경로"
	redis-server.exe redis.windows.conf

1. 텍스트 파일을 만들고 위의 코드를 복사하여 만든다.
2. txt 확장자를 bat 확장자로 바꾼다.

1 - 1. Redis.bat 파일 실행시

  • "TCP 리스너가 이미 사용 되어 있다"는 에러가 발생했다.
  • redis-cli를 통해 강제로 종료하고 다시 실행하여 해결했다.
  • [Can't bind TCP listener *:6379]

Redis 설정 및 실행

RedisProperties 클래스

import org.springframework.beans.factory.annotation.Value;  
import org.springframework.context.annotation.Configuration;  
  
@Configuration  
public class RedisProperties {  
  
    private int redisPort;  
    private String redisHost;  
  
    public RedisProperties(  
		    // appliaction.properties에 포트 번호와 아이피 주소 설정이 가능하다.
            @Value("${spring.data.redis.port}") int redisPort,  
            @Value("${spring.data.redis.host}") String redisHost) {  
        this.redisHost = redisHost;  
        this.redisPort = redisPort;  
    }  
  
    public int getRedisPort() {  
        return redisPort;  
    }  
  
    public String getRedisHost() {  
        return redisHost;  
    }  
}

RedisConfiguration 클래스

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.StringRedisTemplate;  
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;  
  
@Configuration  
@EnableRedisRepositories  
public class RedisConfiguration {  
  
    @Bean  
    public RedisConnectionFactory redisConnectionFactory(RedisProperties properties){  
        return new LettuceConnectionFactory(properties.getRedisHost(), properties.getRedisPort());  
    }  
  
    @Bean  
    public StringRedisTemplate redisTemplate(RedisConnectionFactory connectionFactory){  
        StringRedisTemplate redisTemplate = new StringRedisTemplate();  
        redisTemplate.setConnectionFactory(connectionFactory);  
        return redisTemplate;  
    }  
  
}

Redis repository 및 domain

import org.springframework.data.repository.CrudRepository;  
  
public interface UserRepository extends CrudRepository<User, String> {  
  
}
import java.util.UUID;  
import org.springframework.data.annotation.Id;  
import org.springframework.data.redis.core.RedisHash;  
  
@RedisHash("user")  
public class User {  

    @Id  
    UUID uuid;  
    String name;  
  
    public User(UUID uuid, String name) {  
        this.uuid = uuid;  
        this.name = name;  
    }  
}

저장 되는지 테스트하기 위한 간단한 코드

//레디스 저장 테스트
import java.util.UUID;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.boot.CommandLineRunner;  
import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Profile;  
  
@SpringBootApplication  
public class RedisSpringBootTestApplication {  
  
    @Bean  
    CommandLineRunner runner(UserRepository repository) {  
        return args -> {  
            UUID uud = UUID.randomUUID();  
            User user = new User(uud, "name");  
            repository.save(user);  
  
        };  
    }  
  
    public static void main(String[] args) {  
        SpringApplication.run(RedisSpringBootTestApplication.class, args);  
    }  
  
}

redis-cli 에서 확인

3. Redis 테스트

  • Redis 테스트 방법에는 두가지가 존재한다.
  1. embedded-redis
    • TestContainers 등장 이전에 자주 사용하던 기술
    • 현재 업데이트가 더디다.
    • Redis 서버를 임의로 만들어 테스트 가능하다.
  2. TestContainers
    • 도커 컨테이너에서 실행할 수 있는 경량 인스턴스를 제공한다.
    • 통합 테스트 환경에서 유용하다
    • 실제 환경과 가까운 테스트 환경을 구현할 수 있다.
    • 첫 빌드 및 실행시 도커 이미지 생성에 큰 시간이 걸린다.

3 - 1. Embedded Redis

	// build.gradle에 의존성 추가
	implementation("it.ozimov:embedded-redis:0.7.2")

테스트용 레디스 환경설정 클래스 (TestRedisConfiguration)

package com.example.redisspringboottest;  
  
import jakarta.annotation.PostConstruct;  
import jakarta.annotation.PreDestroy;  
import java.io.IOException;  
import java.io.OutputStreamWriter;  
import java.io.PrintWriter;  
import org.springframework.boot.test.context.TestConfiguration;  
import redis.embedded.RedisExecProvider;  
import redis.embedded.RedisServer;  
import redis.embedded.util.Architecture;  
import redis.embedded.util.OS;  
  
  
@TestConfiguration  
public class TestRedisConfiguration {  
  
    private static RedisServer redisServer;  
  
    public TestRedisConfiguration(RedisProperties redisProperties) throws IOException {  
        this.redisServer = new RedisServer(/*  
        운영 체제별 RedisExecProvider로 설정할 수 있다.  
        getRedisPath()         */redisProperties.getRedisPort());  
    }  
  
//    private RedisExecProvider getRedisPath() {  
//        RedisExecProvider customProvider = null;  
//        customProvider = RedisExecProvider.defaultProvider()  
//                .override(OS.UNIX, "/path/unix/redis")  
//                .override(OS.WINDOWS, Architecture.x86_64, "C:/Redis")  
//                .override(OS.MAC_OS_X, Architecture.x86_64, "/path/macosx/redis");  
//  
//        return customProvider;  
//    }  
  
    @PostConstruct  
    public void redisServer() {  
        redisServer.start();  
    }  
  
    @PreDestroy  
    public void stopServer() {  
        redisServer.stop();  
    }  
  
  
}

레디스 저장 관련 테스트 코드

package com.example.redisspringboottest;  
  
import static org.junit.jupiter.api.Assertions.assertEquals;  
import static org.junit.jupiter.api.Assertions.assertNotNull;  
  
import java.util.UUID;  
import org.junit.jupiter.api.DisplayName;  
import org.junit.jupiter.api.Test;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.boot.test.context.SpringBootTest;  
  
@SpringBootTest(classes = TestRedisConfiguration.class)  
public class UserRepositoryIntegerationTest {  
  
    @Autowired  
    private UserRepository userRepository;  
  
    @Test  
    @DisplayName("멤버 저장 확인")  
    void shouldSaveUser_toRedis(){  
        UUID uuid = UUID.randomUUID();  
        User user = new User(uuid, "name");  
        User saved = userRepository.save(user);  
        assertNotNull(saved);  
  
        User user1 = userRepository.findAll().iterator().next();  
        assertNotNull(user1);  
    }  
}

3 - 2. TestContainers

	// build.gradle에 의존성 추가
	testImplementation 'org.testcontainers:testcontainers:1.19.3'
    testImplementation "org.testcontainers:junit-jupiter:1.19.3"

테스트 환경 Redis 환경 설정클래스

  
import static org.junit.jupiter.api.Assertions.assertTrue;  
  
import org.junit.jupiter.api.Test;  
import org.springframework.test.context.DynamicPropertyRegistry;  
import org.springframework.test.context.DynamicPropertySource;  
import org.testcontainers.containers.GenericContainer;  
import org.testcontainers.junit.jupiter.Container;  
import org.testcontainers.junit.jupiter.Testcontainers;  
import org.testcontainers.utility.DockerImageName;  
  
@Testcontainers  
public class TestRedisContainerConfiguration {  
	// 도커에서 다운받은 redis 이미지의 overview 페이지에서 지원하는
	// Redis 이미지 버전을 적어주면 된다 redis: 이미지 버전 명
    private static final String REDIS_IAMEGE = "redis:7.2.4-alpine";  
    private static final int REDIS_PORT = 6379;  
  
  
    @Container  
    private static final GenericContainer REDIS_CONTAINER =  
            new GenericContainer(DockerImageName.parse(REDIS_IAMEGE)).withExposedPorts(REDIS_PORT);  
  
    @DynamicPropertySource  
    private static void registerRedisProperties(DynamicPropertyRegistry registry) {  
        registry.add("spring.data.redis.host", REDIS_CONTAINER::getHost);  
        registry.add("spring.data.redis.port", () -> REDIS_CONTAINER.getMappedPort(REDIS_PORT).toString());  
    }  
  
    @Test  
    void givenRedisContainerConfiguredWithDynamicProperties_whenCheckingRunningStatus_thenStatusIsRunning() {  
        assertTrue(REDIS_CONTAINER.isRunning());  
    }  
  
}

TestCode

import static org.junit.jupiter.api.Assertions.assertEquals;  
import static org.junit.jupiter.api.Assertions.assertNotNull;  
  
import java.util.UUID;  
import org.junit.jupiter.api.DisplayName;  
import org.junit.jupiter.api.Test;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.boot.test.context.SpringBootTest;  
  
@SpringBootTest()  
public class UserRepositoryIntegerationTest {  
  
    @Autowired  
    private UserRepository userRepository;  
  
    @Test  
    @DisplayName("멤버 저장 확인")  
    void shouldSaveUser_toRedis(){  
        UUID uuid = UUID.randomUUID();  
        User user = new User(uuid, "name");  
        User saved = userRepository.save(user);  
        assertNotNull(saved);  
  
        User user1 = userRepository.findAll().iterator().next();  
        assertNotNull(user1);  
    }  
}

테스트 결과

  • 결과는 잘 나오지만, 첫 통합 테스트 이후에 다시 동작하려니 문제가 발생했다.

  • 다시 gradle build를 하니 테스트가 정상적으로 동작하는 것을 확인 했다.

  • 이유를 찾아보니 Gradle이 테스트코드의 변화가 없다면 통합 테스트를 캐싱하여 저장한다.

	//linux and mac
	./gradlew clean test --no-build-cache
    //window
    gradlew clean test --no-build-cache

도커 이미지 생성

  • 테스트 실행시 도커 컨테이너가 실행되고 테스트가 끝나면 도커 컨테이너가 끝난다.

언제 사용되면 좋은가?

  • 자주 호출되어 사용 되는 데이터
  • 저장, 수정, 삭제 등등으로 자주 바뀌는 데이터는 예외
  • 예시를 들자면 이벤트 배너, 카테고리, 메인 배너 데이터 등등.

출처

깃허브 소스코드
Redis SpringBoot Data Redis 로컬/통합 테스트 환경 구축하기
Embedded Redis Server with Spring Boot Test
embedded-redis 깃허브 주소
레디스객체
Strategy 전략 프로젝트
Test events were not received" when run tests using Intellij

profile
꾸준하게글을작성해보자
post-custom-banner

0개의 댓글