본문 바로가기
🏢 DB/1️⃣ Redis

레디스를 5가지 방법으로 설치/구축하기 : Embedded Redis, Testcontainers

by kukim 2022. 12. 9.

잘못된 내용이나 의견 있다면 편하게 말씀해주세요.🙏🏻

 

개발/테스트 목적 단일 노드 Redis(Remote Dictionary Server)를 구축하는 5가지 방법을 소개합니다.

(1. 로컬, 2. docker-compose, 3. Embedded Redis, 4. Testcontainers, 5. AWS ElastiCache, Terraform)

운영 목적의 아키텍처(Replication, Sentinel, Cluster)는 아닙니다.

 

글 목록

[Redis] 레디스를 5가지 방법으로 설치/구축하기 : 로컬, docker-compose

현재글 : [Redis] 레디스를 5가지 방법으로 설치/구축하기 : Embedded Redis, Testcontainers

[Redis] 레디스를 5가지 방법으로 설치/구축하기 : AWS ElastiCache + AWS Console

[Redis] 레디스를 5가지 방법으로 설치/구축하기 : Terraform + AWS ElastiCache

 

이번 글에서는 

Embedded Redis와 Testcontainers를 활용하고자 합니다.

전체 소스 코드는 이곳을 참고해주세요.


3. Embedded Redis (Java/Spring)

Embedded Redis는 내장 Redis입니다. 주로 Java/Spring 프로젝트에서 개발/테스트 환경에서 사용 목적으로 사용됩니다.

로컬/Docker-compose 구축 방법과 다르게 별도로 Redis 실행 없이 Java/Spring 프로젝트 실행 시 자동으로 실행되고 종료됩니다.

 

구축 방법은 아래와 같습니다. (Java/Spring + Gradle)

3.1. 의존성 추가

3.2. embedded redis 설정

3.3. Redis Client - Lettuce 사용

 

 

3.1. 의존성 추가

Embedde Redis DB는 it.ozimov 라이브러리를 사용합니다.

Redis Client는 Lettuce를 사용합니다. (Why is Lettuce the default Redis client used in Spring Session Redis?)

# build.gradle

dependencies {
	// ...
  		
	// Embedded Redis DB : it.ozimov
	testImplementation 'it.ozimov:embedded-redis:0.7.3' exclude group: 'org.slf4j', module: 'slf4j-simple'

  	// Spring Data Redis (Lettuce Client 포함)
	implementation 'org.springframework.boot:spring-boot-starter-data-redis'

}

application.yml 설정

// application.yml
spring:
  redis:
    host: localhost
    port: 6379

 

3.2. embedded redis 설정

전체 작동 방식은 RedisServer() 객체를 생성 -> 실행 -> 종료 합니다.

특이한 점은 통합 테스트 환경에서 Embedded Redis를 두 개 이상 띄울 경우 포트가 겹치는 문제가 발생하기 때문에 지정한 Redis port 사용 중이라면 RandomPort를 생성하여 Embedded Redis를 생성합니다. (ref : jojoldu - SpringBoot Data Redis 로컬/통합 테스트 환경 구축하기)

// https://github.com/ku-kim/springboot-redis-example/blob/main/src/test/java/kim/ku/redis/config/EmbeddedRedisConfig.java
@Configuration
// @TestConfiguration // 테스트에서만 사용할 때
public class EmbeddedRedisConfig {

   @Value("${spring.redis.port}")
   private int redisPort;

   private RedisServer redisServer;

   @PostConstruct
   public void redisServer() throws IOException {
      int port = isRedisRunning() ? getRandomPort() : redisPort; // 겹치지 않는 랜덤 포트 생성
      redisServer = new RedisServer(port); // 레디스 서버 생성
      redisServer.start(); // 레디스 서버 실행
   }

   @PreDestroy
   public void stopRedis() {
      redisServer.stop(); // 레디스 서버 종료
   }

   private boolean isRedisRunning() throws IOException {
      return isRunning(executeGrepProcessCommand(redisPort));
   }

   /**
    * 해당 port를 사용중인 프로세스 확인하는 sh 실행
    */
   private Process executeGrepProcessCommand(int port) throws IOException {
      String command = String.format("netstat -nat | grep LISTEN|grep %d", port);
      String[] shell = {"/bin/sh", "-c", command};
      return Runtime.getRuntime().exec(shell);
   }

   /**
    * 해당 Process가 현재 실행중인지 확인
    */
   private boolean isRunning(Process process) {
      String line;
      StringBuilder pidInfo = new StringBuilder();

      try (BufferedReader input = new BufferedReader(
         new InputStreamReader(process.getInputStream()))) {

         while ((line = input.readLine()) != null) {
            pidInfo.append(line);
         }

      } catch (Exception ignored) {
      }

      return StringUtils.hasLength(pidInfo.toString());
   }

   /**
    * 랜덤 포트를 할당받아서 리턴
	* ref : <a href="https://github.com/apache/curator/blob/7a148288603ae5db0c232142f7dc07d43e01bea3/curator-test/src/main/java/org/apache/curator/test/InstanceSpec.java#L86">...</a>
    */
   private int getRandomPort() {
      ServerSocket server = null;
      try {
         server = new ServerSocket(0);
         server.setReuseAddress(true);
         return server.getLocalPort();
      } catch (IOException e) {
         throw new Error(e);
      } finally {
         if (server != null) {
            try {
               server.close();
            } catch (IOException ignore) {
               // ignore
            }
         }
      }
   }
}

위와 같이 Configuration을 설정하면 실행될 때마다 Embedded Redis 가 실행됩니다. 이제 Redis Client를 사용하여 해당 Redis에 연결하여 사용하면 됩니다.

 

3.3. Redis Client - Lettuce 사용

Java/Spring을 사용하는 Redis Client 라이브러리로 Redis에 접근하는 방법은 크게 두 가지입니다.

RedisTemplate와 RedisRepository입니다.

아쉽게도 이 글의 취지는 Redis Server를 구축하는 방법이기에 해당 사용 방법은 다른 글에서 소개드리고자 합니다.

아래 코드는 RedisTemplate를 생성하고 있습니다.

@Configuration
public class RedisRepositoryConfig {

	@Value("${spring.redis.host}")
	private String redisHost;

	@Value("${spring.redis.port}")
	private int port;

	@Bean
	public RedisConnectionFactory redisConnectionFactory() {
		return new LettuceConnectionFactory(redisHost, port); // Redis Client 객체 생성
	}

	@Bean
	public RedisTemplate<?, ?> redisTemplate() {
		RedisTemplate<byte[], byte[]> redisTemplate = new RedisTemplate<>();
		redisTemplate.setConnectionFactory(redisConnectionFactory()); // RedisTempaltes에 Redis Client 연결
		return redisTemplate;
	}
}

4. Testcontainers 라이브러리를 활용 (Java/Spring)

Testcontainers 라이브러리를 사용하여 Docker-compose 구축 과정을 Java 코드로 사용할 수 있습니다. 자동으로 Docker 컨테이너가 실행되고 종료됩니다. 이는 CI 테스트 환경에서 유리하게 작동할 수 있습니다. (ref : [의사결정] Testcontainers 사용하기까지)

 

4.1. 의존성 추가

# build.gradle

dependencies {
	// ...
  
	// TestContainers
	testImplementation 'org.testcontainers:junit-jupiter:1.17.4'
    
    // Spring Data Redis
	implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}

4.2. Testcontainers컨테이너 설정

class TestContainersRedis {

   static {
      GenericContainer redis = new GenericContainer("redis:7.0.5")
         .withExposedPorts(6379);
      redis.start();
      // 테스트 환경에서 host, port가 랜덤으로 생성되기 때문에 property를 추가 설정해준다.
      System.setProperty("spring.redis.host", redis.getHost());
      System.setProperty("spring.redis.port", redis.getMappedPort(6379).toString());
   }

}

 

위와 같이 Testcontainers 라이브러리를 사용하여 Redis 도커를 띄울 수 있다. 이를 테스트에서 활용할 수 있다.

 

4.3. Redis Client - Lettuce 사용

3.3과 동일하여 넘어가고자 합니다.


마치며

이번 글을 통해 Java/Spring 프로젝트에서 Embedded Redis와 Testcontainers를 사용하여 Redis를 구축하였습니다.

다음 글에서는 AWS ElastiCache 서비스를 사용하여 Redis를 구축해보고자 합니다.

 

글 목록

[Redis] 레디스를 5가지 방법으로 설치/구축하기 : 로컬, docker-compose

[Redis] 레디스를 5가지 방법으로 설치/구축하기 : Embedded Redis, Testcontainers

다음글 : [Redis] 레디스를 5가지 방법으로 설치/구축하기 : AWS ElastiCache + AWS Console

[Redis] 레디스를 5가지 방법으로 설치/구축하기 : Terraform + AWS ElastiCache


Reference

Embedded Redis

https://jojoldu.tistory.com/297
https://github.com/ozimov/embedded-redis
https://github.com/kstyrc/embedded-redis
https://www.baeldung.com/spring-embedded-redis

Testcontainers Redis

https://www.baeldung.com/spring-boot-redis-testcontainers

https://github.com/testcontainers/testcontainers-java/tree/main/examples/redis-backed-cache

댓글