개요
친구랑 작은 토이프로젝트를 기획하면서, Redis 를 사용할 일이 생겼다.
일단 Redis 환경을 어떻게 구성할지에 대해 고민했다.
1. AWS 환경에서 사용
2. 로컬에서 Docker 환경으로 구성
3. 로컬에서 Docker 없이 직접 설치
일단 토이 프로젝트이고, 친구와 굳이 'Redis만을 위해' 배포를 할 필요성을 느끼지 못했기 때문에 1은 기각.
로컬에서 사용하는 것에 대해 고민하다가 친구가 'Embedded Redis'라는 것을 찾아왔다.
Embedded Redis란?
애플리케이션 내부에서 내장형 Redis 서버를 실행할 수 있도록 도와주는 방식이다.
보통 테스트환경에서 Redis를 별도로 설치하지 않고 애플리케이션과 함께 실행하고 싶을 때 사용한다.
Embedded Redis 의 장점은 다음과 같다.
로컬에서 개발할 때 Redis를 따로 설치하지 않아도 실행 가능하다. Docker를 사용할 필요도 없다.
Redis가 필요한 테스트 코드를 작성할 때, 실제 Redis서버를 사용하지 않고도 테스트가 가능하다.
이러한 이유로, Docker 사용도 필요 없어? 직접 서버 실행도 아냐? 당장 진행하자! 하였고,
잘 실행 되는지 확인하기 위해 다음과 같이 구성했다.
Embedded Redis 구성
1. 의존성 추가
build.gradle의 dependencies에 Embedded Redis 구성을 위한 의존성을 추가해줘야한다.
build.gradle
dependencies { // ... // embedded redis implementation 'it.ozimov:embedded-redis:0.7.2' implementation 'org.springframework.boot:spring-boot-starter-data-redis' // ... }
ozimov의 0.7.2버전이 자료를 찾아봤을 때 가장 많이 나오길래 해당 embedded redis를 추가해주었다.
2. Config 파일 만들기
EmbeddedRedisConfig
Embedded Redis 설정에 대한 파일을 만들어줘야 한다.
local 환경에서만 실행되도록 profile을 설정했다.
실행 중인 Redis가 있으면 빈 포트를 찾아 실행시킨다.
start, stop 메서드로 Redis 서버 실행, 중지한다.
@Slf4j @Profile({"local"}) @Configuration public class EmbeddedRedisConfig { @Value("${spring.data.redis.port}") private int redisPort; private RedisServer redisServer; @PostConstruct public void start() throws IOException { int port = isRedisRunning() ? findAvailablePort() : redisPort; redisServer = new RedisServer(port); redisServer.start(); } @PreDestroy public void stop() { redisServer.stop(); } public int findAvailablePort() throws IOException { for (int port = 10000; port <= 65535; port++) { Process process = executeGrepProcessCommand(port); if (!isRunning(process)) { return port; } } throw new RuntimeException("No available ports found."); } /** * Embedded Redis가 현재 실행중인지 확인 */ private boolean isRedisRunning() throws IOException { return isRunning(executeGrepProcessCommand(redisPort)); } /** * 해당 port를 사용중인 프로세스를 확인하는 sh 실행 */ private Process executeGrepProcessCommand(int redisPort) throws IOException { String command = String.format("netstat -nat | grep LISTEN|grep %d", redisPort); 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 e) { log.error(e.getMessage()); } return !pidInfo.isEmpty(); } }
RedisRepositoryConfig
Spring Boot에서 Redis를 연결하는 설정 파일이다.
Spring Data Redis를 사용하기 위한 기본적인 설정을 제공한다.
@Configuration @EnableRedisRepositories public class RedisRepositoryConfig { @Value("${spring.data.redis.host}") private String host; @Value("${spring.data.redis.port}") private int port; @Bean public RedisConnectionFactory redisConnectionFactory() { return new LettuceConnectionFactory(host, port); } @Bean public RedisTemplate<byte[], byte[]> redisTemplate() { RedisTemplate<byte[], byte[]> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory()); return redisTemplate; } }
테스트 해보기
RedisHash를 작성해보자
Token 클래스
@Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @RedisHash(value = "token", timeToLive = 60) public class Token implements Serializable { @Id private String id; private String value; public Token(String value) { this.value = value; } }
TokenRepository 인터페이스
public interface TokenRepository extends CrudRepository<Token, String> { }
EmbeddedRedisTest 테스트코드
@ActiveProfiles("local") @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) public class EmbeddedRedisTest { @Autowired private TokenRepository tokenRepository; // @Autowired 적용 @DisplayName("Token Save 테스트 성공") @org.junit.jupiter.api.Test void saveTest(){ // given Token token = tokenRepository.save(new Token("token")); // when Token savedToken = tokenRepository.save(token); // then assertThat(savedToken).isNotNull(); } }
Mac에서 문제 발생
Windows를 사용하는 친구는 해당 구성으로 테스트가 원활히 진행 되었다. 하지만 Mac을 사용하는 나는 계속 테스트가 실패했다. 오류 내용은 대충 Redis 서버가 실행되지 않았을 때 나는 오류였다.
결론적으로, Redis-Server 바이너리 파일을 실행시키도록 했다. 🥲
Embedded Redis 를 Mac에서 지원하지 않기 때문에 TestContainers를 사용하라는 해결책이 대부분이었으나, 나는 애초에 Embedded Redis를 테스트 용도로 사용하는 것이 아니기 때문에 나에게 적용되지 않았다.
Mac M칩도 아닌 intel칩으로 작업을 하는 나는 Embedded Redis의 해당 오류에 대한 해결방법을 찾기 너무 어려웠다. M칩을 사용하는 사람들도 Redis 설치 파일로 작업하기로 했다는 글을 보았고, 해당 방법을 적용하였다.
Docker를 쓸까? 하는 고민도 살짝 했다. 하지만 내 OS의 문제였고, 친구의 OS에서는 정상 작동이 되었기 때문에 굳이 Docker를 사용해서 친구에게도 번거로운 작업을 추가하는 것보다, OS별로 Embedded Redis를 사용할지, Redis 서버를 사용할지 나누는 코드를 추가하는게 훨씬 간편하고 빠르고... 그렇다고 생각했다.
아래 블로그를 참고하였다.
https://develoyummer.tistory.com/101
1. Redis 바이너리 파일을 가져오자
터미널에서 아래 명령어들을 실행하자
1. 레디스 압축파일 다운
wget https://download.redis.io/releases/redis-6.0.10.tar.gz
2. 레디스 압축파일 압축 풀기
tar -xzf redis-6.0.10.tar.gz
3. 압축 해제한 디렉토리로 이동
cd redis-6.0.10
4. 레디스 컴파일
make
이 과정을 거치면 redis-6.0.10/src
경로에 redis-server
파일이 생길 것이다.
해당 파일을 Spring Boot 프로젝트의 src/main/resource/binary/redis/
경로로 옮겨준다.
2. Config 파일 수정
EmbeddedRedisConfig
먼저 Mac OS인지 확인하는 isMac() 메소드를 추가해준다.
private boolean isMac() { return Objects.equals(System.getProperty("os.name"), "Mac OS X"); }
이제 start() 메소드에 아래 내용으로 바꿔준다.
@PostConstruct public void start() throws IOException { int port = isRedisRunning() ? findAvailablePort() : redisPort; // redisServer = new RedisServer(port); // 기존 코드 if(isMac()){ redisServer = new RedisServer(Objects.requireNonNull(getRedisFileForArcMac()), port); }else{ redisServer = new RedisServer(port); } redisServer.start(); }
친구의 도움을 많이 받았고... 결국 Redis 설치 엔딩이었지만... 그래도 Embedded Redis라는 것을 알게되었으니... 좋다..👍
'Backend > Spring' 카테고리의 다른 글
[Spring] 로그 레벨 설정 (1) | 2024.12.31 |
---|---|
[Spring Boot] 싱글톤 컨테이너(Singleton Container) (0) | 2024.12.30 |
[Spring Boot] Test Code 작성하기 (0) | 2024.12.17 |
[Spring Boot] Application 클래스 (@SpringBootApplication) (0) | 2024.12.16 |