redis test 환경 추가

최준호·2022년 5월 29일
0

redis

목록 보기
3/3
post-thumbnail

참고 이동욱님 [Redis] SpringBoot Data Redis 로컬/통합 테스트 환경 구축하기

💭Redis test 환경?

redis를 붙이며 가장 큰 문제가 생긴건 build 할 경우 해당 서버에 무조건 redis가 켜져있어야한다는 것이다. 물론 테스트 서버와 jenkins 서버에 모두 redis가 설치되어 있어야하며 만약에 우리 회사에 새로운 개발자 분이 오셨을 때 코드가 잘 실행되는지 테스트를 실행했을 때 redis를 설정해두지 않으면 테스트 조차 실행되지 않을 것이다.

또한 이동욱님의 글을 참고하면 최소한 테스트 환경은 아무런 설정없이 실행될 수 있도록 설정해두는 것을 지향한다고 하셨다. 그래서 나도 redis를 테스트 환경에 맞게 돌아갈 수 있게 진행해보려 한다.

🔨yml 설정

server:
  port: 0

spring:
  profiles:
    active: local #테스트 환경 구축후에는 test로 결과 보면서 테스트 진행
  application:
    name: login-service
eureka:
  instance:
    instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: ${server.eureka.url:http://localhost:8761}/eureka

management:
  endpoints:
    web:
      exposure:
        include: refresh, health, beans, httptrace
token:
  access:
    expiration_time: 15 #15분
  refresh:
    expiration_time: 1440 #24시간
  secret: test_secret
--- #local
spring:
  config:
    activate:
      on-profile: local
  h2:
    console:
      enabled: true
      settings:
        web-allow-others: true
      path: /h2-console
  jpa:
    open-in-view: false #OSIV(Open Session In View) false일 경우 영속성 컨텍스트가 서비스까지만 존재
    hibernate:
      ddl-auto: create  #절대 수정 금지
    show-sql: true
  datasource:
    hikari:
      driver-class-name: org.h2.Driver
      jdbc-url: jdbc:h2:mem:testdb  #실제 서버에서는 내장 h2 db 서버로 연결해야함! ex) jdbc:h2:mem:test-db
      username: sa
      password:
  redis:
    host: localhost
    port: 6379
--- #test
spring:
  config:
    activate:
      on-profile: test
  h2:
    console:
      enabled: true
      settings:
        web-allow-others: true
      path: /h2-console
  jpa:
    open-in-view: false #OSIV(Open Session In View) false일 경우 영속성 컨텍스트가 서비스까지만 존재
    hibernate:
      ddl-auto: create  #테스트 방법에 맞게 수정해서 사용
    show-sql: true
  datasource:
    hikari:
      driver-class-name: org.h2.Driver
      jdbc-url: jdbc:h2:tcp://localhost/~/testdb  #실제 서버에서는 내장 h2 db 서버로 연결해야함! ex) jdbc:h2:mem:test-db
      username: sa
      password:
  redis:
    host: localhost
    port: 6379

/test/resources/application.yml 파일에 다음과 같이 작성했다. local은 h2와 redis를 모두 내장으로 돌리려는 profile이고 test는 혹시나 실제 데이터가 잘 들어가고 redis도 실제 데이터를 확인하고 싶다면 실행하도록 test라는 profile로 빼두었다. 실제 서버를 운영할 때도 test라는 profile을 추가해서 로컬에서 내장 테스트와 서버를 연결해서 하는 테스트로 나눠서 진행하려고 한다.

📙Embedded Redis 설정

<!-- test 내장 redis 추가 -->
<dependency>
    <groupId>it.ozimov</groupId>
    <artifactId>embedded-redis</artifactId>
    <version>0.7.3</version>
    <exclusions>
        <!-- slf4j simple이 기존 boot 설정과 충돌로 인해 제외 -->
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
        </exclusion>
    </exclusions>
</dependency>

embedded redis의 경우 slf4j simple 설정이 추가로 잡혀 있어서 충돌 에러 문구가 나오게 된다. exclusion을 통해 해당 설정 부분을 제외하고 의존성을 추가하면 해당 에러를 해결할 수 있다.

@Configuration
@RequiredArgsConstructor
@Profile(value = "local")
public class EmbeddedRedisConfig {
    private RedisServer redisServer;
    @Value("${spring.redis.port}")
    private int redisPort;

    @PostConstruct
    public void redisServer() throws IOException {
        int port = isRedisRunning()? findAvailablePort() : redisPort;
        redisServer = new RedisServer(port);
        redisServer.start();
    }

    @PreDestroy
    public void stopRedis(){
        if(redisServer != null){
            redisServer.stop();
        }
    }

    /**
     * Embedded Redis가 현재 실행중인지 확인
     */
    private boolean isRedisRunning() throws IOException {
        return isRunning(executeGrepProcessCommand(redisPort));
    }

    /**
     * 현재 PC/서버에서 사용가능한 포트 조회
     */
    public int findAvailablePort() throws IOException {

        for (int port = 10000; port <= 65535; port++) {
            Process process = executeGrepProcessCommand(port);
            if (!isRunning(process)) {
                return port;
            }
        }

        throw new IllegalArgumentException("Not Found Available port: 10000 ~ 65535");
    }

    /**
     * 해당 port를 사용중인 프로세스 확인하는 sh 실행
     */
    private Process executeGrepProcessCommand(int port) throws IOException {
        String os = System.getProperty("os.name").toLowerCase(Locale.ROOT);
        if(os.contains("win")){
            String command = String.format("netstat -nao | find \"LISTEN\" | find \"%d\"", port);
            String[] shell = {"cmd.exe", "/y", "/c", command};
            return Runtime.getRuntime().exec(shell);
        }else if(os.contains("linux")) {
            String command = String.format("netstat -nat | grep LISTEN|grep %d", port);
            String[] shell = {"/bin/sh", "-c", command};
            return Runtime.getRuntime().exec(shell);
        }else if(os.contains("mac")){

        }else{
            throw new RuntimeException("해결되지 않은 OS 입니다. executeGrepProcessCommand() os 명령어 코드를 추가해주세요.");
        }
        return null;
    }


    /**
     * 해당 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) {
        }

        return !StringUtils.isEmpty(pidInfo.toString());
    }
}

가장 애를 먹었던 부분인데. 우리 회사는 개발은 window에서 진행하고 실제 서버는 linux 서버로 진행된다. mac을 통한 해결 방법은 많이 나와있었지만 window에 대한 해결방법을 못 찾고 있던 중 이동욱님 블로그 글 댓글에

해당 분의 댓글에 해결방법이 적혀져 있었다ㅜㅜ widnow 명령어로 변경해서 사용하면 되는데 그 부분을 몰라서 애를 먹다가 해당 분 덕분에 바로 해결할 수 있었다. 그래서 내 코드를 보면

/**
 * 해당 port를 사용중인 프로세스 확인하는 sh 실행
 */
private Process executeGrepProcessCommand(int port) throws IOException {
    String os = System.getProperty("os.name").toLowerCase(Locale.ROOT);
    if(os.contains("win")){
        String command = String.format("netstat -nao | find \"LISTEN\" | find \"%d\"", port);
        String[] shell = {"cmd.exe", "/y", "/c", command};
        return Runtime.getRuntime().exec(shell);
    }else if(os.contains("linux")) {
        String command = String.format("netstat -nat | grep LISTEN|grep %d", port);
        String[] shell = {"/bin/sh", "-c", command};
        return Runtime.getRuntime().exec(shell);
    }else if(os.contains("mac")){

    }else{
        throw new RuntimeException("해결되지 않은 OS 입니다. executeGrepProcessCommand() os 명령어 코드를 추가해주세요.");
    }
    return null;
}

해당 메서드 부분만 수정해서 사용했다. window 경우 해당 포트가 실행중인지 확인하는 sh 부분을 다르게 작성해야하기 때문에 다음과 같이 분기처리를 해두었다. 회사에 mac 사용하시는 분들도 있는데... mac은 또 intell용과 m1용이 나눠서 설정해야하더라... 나중에 꼭 작성해야겠다ㅜㅜ

👊테스트

그리고 나서 테스트를 진행해보면
local로 package를 돌리게 되면

따로 h2 서버나 redis 서버를 실행시키지 않더라도 테스트가 잘 진행된다. 만약 redis에 내용을 보고 싶다면 redis 서버를 포트번호를 맞게 설정해서 실행시키면 된다. 위 부분은 테스트를 진행할 때 여러 redis가 동시에 올락면서 충돌하는 문제를 해결하기 위한 코드였다. 다른 더 쉬운 설정이 있었다면 좋았겠지만 인터넷 상에는 이게 최선인거 같다!

0개의 댓글

관련 채용 정보