Design Pattern/행위 패턴

Strategy 전략 프로젝트에 적용 해보기

 문제사항

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class MemberApiControllerTest {
	...
}

스프링 부트에서 통합 테스를 위해서 위처럼 랜덤한 포트를 배정하게 하면서 내장 redis를 사용하게 되면 

여러 스프링 테스트 컨텍스트가 실행되면 EmbeddedRedis가 포트충돌

 

이를 위해서 레디스를 실행할때 사용가능한 포트를 찾고 이를 레디스에 할당하게 해야 합니다.

여기서 문제는 저의 컴퓨터는 윈도우이고 서버는 리눅스이기 때문에 포트를 찾는 로직이 달라집니다.

 

window

public Process executeGrepProcessCommand(int port) throws IOException {
        String command = String.format("netstat -nao | find \"LISTEN\" | find \"%d\"", port);
        String[] shell = {"cmd.exe", "/y", "/c", command};
        return Runtime.getRuntime().exec(shell);
    }

 

linux

public 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);
    }

 

 

os에 따라서 메서드를 다르게 적용해야하는데 스프링 IOC의 도움을 받아서 전략패턴을 적용해 보려고 합니다.

 

1.전략 인터페이스

public interface OSStrategy {
    public Process executeGrepProcessCommand(int port) throws IOException;
}

 

2. 인터페이스 구현

public class WindowStrategy implements OSStrategy{
    @Override
    public Process executeGrepProcessCommand(int port) throws IOException {
        String command = String.format("netstat -nao | find \"LISTEN\" | find \"%d\"", port);
        String[] shell = {"cmd.exe", "/y", "/c", command};
        return Runtime.getRuntime().exec(shell);
    }
}
public class LinuxStrategy implements OSStrategy{
    @Override
    public 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);
    }
}

 

 

3. Profile 어노테이션을 통한 Bean을 등록

@Profile("dev")
@Bean
public OSStrategy windowStrategy() {
	return new WindowStrategy();
}

@Profile("test")
@Bean
public OSStrategy linuxStrategy() {
	return new LinuxStrategy();
}

 

4. 인터페이스를 통해서 DI를 받아서 사용

스프링 IOC는 DI를 통해서 의존성을 주입 해주고 앞서 Profile에 의해서 인터페이스를 상속받은 빈이 하나만 등록되어서아래의 코드에서 처럼 주입 하게 됩니다.

@Slf4j
@Configuration
public class EmbeddedRedisConfig {

    ...

    @Autowired
    OSStrategy osStrategy;

    ...

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

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

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

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

    /**
     * 해당 Process가 현재 실행중인지 확인
     */
    private boolean isRunning(Process process) {
        ...
    }
}

 

이제 아래 처럼 설정 파일에서 profile를 수정하면 적용 끝

spring:
  #profiles
  profiles:
    include: test, redis

 

Reference

https://jojoldu.tistory.com/297

'Design Pattern > 행위 패턴' 카테고리의 다른 글

Template Method Pattern  (0) 2021.06.24
State Pattern  (0) 2021.05.13
Command Pattern  (0) 2021.05.12
Strategy Pattern  (0) 2021.05.06