진행하던 프로젝트에서 실시간 인기 게시글을 조회하는 기능을 redis를 통해 구현하였다.
하지만 팀원분중에 로컬에 redis가 설치되어 있지 않았던 팀원 분이 계셔서 처음에는 도커를 이용하여
환경을 구성할까 하였지만 다른 팀원분이 Embedded Redis라는게 있다는걸 알려주셔서 적용해보았다.
Embedded Redis란?
로컬에 별도의 redis를 설치하지 않고 애플리케이션 내에서 Redis를 실행할 수 있다.
보통은 로컬 개발 환경이나 테스트 환경에서 많이 사용된다.
프로젝트에서 팀원이 redis가 설치되어 있지 않더라도 Embedded Redis를 사용하면
clone 받아 실행하기만 하면 redis가 애플리케이션 내에서 실행된다.
build.gradle
// redis 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
// embedded-redis 의존성 추가
implementation group: 'it.ozimov', name: 'embedded-redis', version: '0.7.2'
우선 build.gradle에 의존성을 추가 해준다. 이때 0.7.3버전에서는 Slf4J와 관련된 에러가 있다고 하여 0.7.2버전으로 설정했다.
이슈! Mac OS(m1)에서 Embedded Redis 실행하기
기본적인 설정을 하고 실행하면 윈도우에서는 동작하지만 맥OS(M1)에서는 동작하지 않는것을 발견했다.
이후 열심히 찾아본 결과 embedded redis 라이브러리에 mac_arm64용 바이너리가 없는것이 원인이라는것을
알게되었다. 아래부터는 Apple Silicon을 사용한 Mac OS와 Window 모두에서 embedded redis를 실행할 수 있도록
설정한 방법을 정리했다.
mac_arm64용 바이너리 파일 프로젝트에 추가하기
먼저 바이너리 파일을 추출하기 위해 Redis의 소스 코드를 다운받아 컴파일 해야한다.
// 소스 코드 다운로드
$ wget https://download.redis.io/releases/redis-7.2.6.tar.gz
// 압축 해제
$ tar xzf redis-7.2.6.tar.gz
// redis-7.2.6 디렉토리 이동
$ cd redis-7.2.6
// 컴파일
$ make
여기서 다운받는 버전은 설치되어있는 redis의 버전을 확인한 후 해당 버전과 동일하게 맞춰주면 된다.
위의 단계를 성공적으로 거치면 /redis-7.2.6/src/redis-server 라는 바이너리 파일이 생성된다.
redis-server를 실행시켜보고 위와 같이 정상적으로 동작하는지 확인한다.
정상적으로 동작하는것을 확인한 후 해당 파일의 이름을 redis-server-7.2.6-mac-arm64 으로 변경하여
프로젝트 /resources 에 redis라는 디렉토리를 만들어서 넣어준다.
이때도 역시 버전은 소스코드의 버전으로 변경해야한다.
application.yml
spring:
profiles:
active: local
data:
redis:
host: localhost
port: 6379
maxmemory: 128M
실제 배포할때는 Embedded Redis를 사용하지않고 개발 단계에서만 사용하기 때문에
profiles를 local로 설정한다.
이 이외에 redis를 사용하는데 필요한 설정들과 함께
embedded redis를 window에서 사용할때의 메모리를 확보를 위한 maxmemory와 같은 설정을 추가한다.
EmbeddedRedisConfig
@Slf4j
@Profile("local")
@Configuration
public class EmbeddedRedisConfig {
@Value("${spring.data.redis.port}")
private int redisPort;
// 윈도우를 위한 maxmemory 설정
@Value("${spring.data.redis.maxmemory}")
private String redisMaxMemory;
private RedisServer redisServer;
@PostConstruct
public void startRedis() throws IOException {
int port = isRedisRunning() ? findAvailablePort() : redisPort;
if (isArmArchitecture()) {
log.info("ARM Architecture");
redisServer = new RedisServer(Objects.requireNonNull(getRedisServerExecutable()), port);
}else{
redisServer = RedisServer.builder()
.port(port)
.setting("maxmemory " + redisMaxMemory)
.build();
}
redisServer.start();
}
@PreDestroy
public void stopRedis() {
redisServer.stop();
}
private int findAvailablePort() throws IOException {
for (int port = 10000; port < 65535; port++) {
Process process = executeGrepProcessCommand(port);
if (!isRunning(process)) {
return port;
}
}
throw new GeneralException(ErrorStatus.AVAILABLE_PORT_NOT_FOUND);
}
private boolean isRedisRunning() throws IOException {
return isRunning(executeGrepProcessCommand(redisPort));
}
private Process executeGrepProcessCommand(int port) throws IOException {
String os = System.getProperty("os.name").toLowerCase();
String command;
if (os.contains("win")) {
// 윈도우인 경우
command = String.format("netstat -nao | find \"LISTENING\" | find \":%d\"", port);
String[] cmd = {"cmd.exe", "/y", "/c", command};
return Runtime.getRuntime().exec(cmd);
}
// Unix 계열인 경우 (맥OS, 리눅스)
command = String.format("netstat -nat | grep LISTEN | grep %d", port);
String[] shell = {"/bin/sh", "-c", command};
return Runtime.getRuntime().exec(shell);
}
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) {
throw new GeneralException(ErrorStatus.ERROR_EXECUTING_EMBEDDED_REDIS);
}
return StringUtils.hasText(pidInfo.toString());
}
// mac os용 redis 바이너리 파일
private File getRedisServerExecutable() {
try {
return new File("src/main/resources/redis/redis-server-7.2.6-mac-arm64");
} catch (Exception e) {
throw new GeneralException(ErrorStatus.REDIS_SERVER_EXECUTABLE_NOT_FOUND);
}
}
// mac os 인지 확인
private boolean isArmArchitecture() {
return System.getProperty("os.arch").contains("aarch64");
}
}
mac os인지 확인하고 맞다면 redis 바이너리 파일을 찾아 적용하도록 코드를 작성한다.
또한 운영체제마다 명령어가 다르기 때문에 각각에 맞는 명령어로 처리할 수 있도록 플랫폼 의존성 처리를 한다.
이렇게 한뒤 테스트 해보니 window와 apple slilcon을 사용한 mac os 모두에서 잘 동작하는 것을 확인했다.
참고
https://velog.io/@hakjong/ARM-Mac-M1-%EC%97%90%EC%84%9C-embedded-redis-%EC%82%AC%EC%9A%A9
https://green-bin.tistory.com/77
https://da-nyee.github.io/posts/how-to-use-embedded-redis-on-m1-arm/
'Project' 카테고리의 다른 글
WebSocket HTTP Connection 문제 해결하기 (0) | 2025.03.05 |
---|---|
Redis를 이용하여 최근 조회수 기반 인기공연 제공하기 (0) | 2025.02.27 |
Quartz 스케줄러 이용하여 경매 마감 관리하기 (0) | 2025.01.10 |