๋ค์ด๊ฐ๋ฉฐ
์ด๋ฒ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ์ ์ผ๊ด์ฑ๊ณผ ํจ์จ์ฑ์ ๋ํด ๊ณ ๋ฏผ์ด ์์์ต๋๋ค. ํ ํ๋ก์ ํธ๊ฐ ์งํ๋จ์ ๋ฐ๋ผ, ์ฌ๋ฌ ๋ช ์ ๊ฐ๋ฐ์๊ฐ ๋์์ ์์ ์ ์์ํ๊ฒ ๋์๊ณ , ํ๋ก์ ํธ๋ฅผ GitHub์์ ํด๋ก ํ ๋ค, ๋น ๋ฅด๊ฒ ๊ฐ๋ฐ์ ์์ํ ์ ์๋ ํ๊ฒฝ์ ์ ๊ณตํ๋ ๊ฒ์ด ์ค์ํ๋ค๋ ์ ์ ๊นจ๋ฌ์์ต๋๋ค.
์ด๊ธฐ์๋ ๊ฐ ๊ฐ๋ฐ์๊ฐ Redis, MySQL, MongoDB์ ๊ฐ์ ์ธ๋ถ ๋ฐ๋ชฌ์ ๋ณ๋๋ก ์ค์นํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์๊ณ , ๊ทธ๋ก ์ธํด ์ค์ ์๋ง ์ค๋ ์๊ฐ์ด ๊ฑธ๋ฆฌ๊ฑฐ๋, ํ๊ฒฝ ์ธํ ์ ํ์ง ๋ชปํ ์ฑ๋ก ์งํ๋๋ ๊ฒฝ์ฐ๋ ์์์ต๋๋ค. ๋ฐ๋ผ์, ํ๋ก์ ํธ๋ฅผ ํด๋ก ํ ํ ๋ฐ๋ก ์คํ ๊ฐ๋ฅํ ํ๊ฒฝ์ ๊ตฌ์ฑํด์ผ ํ๋ค๋ ์๊ฐ์ด ๋ค์๊ณ , ์ด๋ฅผ ์ํด ๋ก์ปฌ์์ ๋ ๋ฆฝ์ ์ผ๋ก ์คํํ ์ ์๋ ๊ธฐ์ ์ด ํ์ํ๋ค๊ณ ํ๋จํ์ต๋๋ค.
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๋ฐฉ๋ฒ์ผ๋ก Docker์ TestContainer๋ฅผ ๊ณ ๋ คํ์ง๋ง, ๋ด์ฅํ ๋ฐฉ์์ ์ฌ์ฉํ๋ Embedded Mongo์ Redis๋ฅผ ์ ํํ๊ฒ ๋์์ต๋๋ค.
์ ์ ์ด์
Docker์ TestContainer๋ฅผ ์ฌ์ฉํ์ฌ๋ ์ํ๋ ํ๊ฒฝ์ ๊ตฌ์ถํ ์ ์์ง๋ง ๊ฐ ๋ฐฉ์์๋ ํน์ ํ ์ ์ฝ์ด ์์๊ณ , ์ด๋ฅผ ๊ณ ๋ คํ ์ต์ ์ ์ ํ์ด ํ์ํ์ต๋๋ค.
Docker๋ฅผ ์ฌ์ฉํ๋ฉด ์ปจํ ์ด๋ํ๋ ์๋น์ค๋ฅผ ๋ก์ปฌ์์ ์์ฝ๊ฒ ์คํํ ์ ์์์ง๋ง, ์ปจํ ์ด๋๋ฅผ ์คํํ๋ ๋ฐ ์์ด ์ถ๊ฐ์ ์ธ ์ค์ ๊ณผ ๊ด๋ฆฌ๊ฐ ํ์ํฉ๋๋ค. ๊ฐ๋ฐ์๋ ์ง์ ์ปจํ ์ด๋๋ฅผ ์์ํ๊ณ ๊ด๋ฆฌํด์ผ ํ๋ฏ๋ก ๋ก์ปฌ ๊ฐ๋ฐํ๊ฒฝ์ ์ ์ํ๊ฒ ์์ํ๋ ๋ฐ ๋ฒ๊ฑฐ๋ก์์ด ์์ ์ ์์ต๋๋ค.
TestContainer๋ ์ฃผ๋ก ํ ์คํธ ํ๊ฒฝ์์ ์ปจํ ์ด๋๋ฅผ ๋์ ์ผ๋ก ์์ฑํ๊ณ ๊ด๋ฆฌํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ๋๋ค. ์ด๋ฅผ ํตํด ๋ค์ํ ์ธ๋ถ ์๋น์ค๋ฅผ ํ ์คํธ์ฉ ์ปจํ ์ด๋๋ก ์คํํ ์ ์์ง๋ง, ํ ์คํธ ์ฝ๋ ์คํ ์์๋ง ๋์ํฉ๋๋ค. ์ด๋ ํ ์คํธ ์๋ํ์๋ ๋งค์ฐ ์ ์ฉํ์ง๋ง, ์ผ์์ ์ธ ๋ก์ปฌ ๊ฐ๋ฐํ๊ฒฝ์์ ์ง์์ ์ผ๋ก Redis๋ MongoDB์ ๊ฐ์ ์๋น์ค๋ฅผ ์ฌ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ์๋ ๋ถํธํจ์ด ๋ฐ๋ฆ ๋๋ค. ๊ฐ๋ฐ์๊ฐ ์ผ๋ฐ์ ์ธ ์์ ์ ํ๋ ๋์ค์๋ ์ง์์ ์ผ๋ก ํ ์คํธ ํ๊ฒฝ์ด ํ์ํ ์ ์๋๋ฐ, TestContainer๋ ์ด์ ๊ฐ์ ์ง์์ ์ธ ์ฌ์ฉ์ ์ ํฉํ์ง ์๋ค๊ณ ํ๋จํ์์ต๋๋ค.
๋ฐ๋ผ์, ์ ํฌ ํ๋ก์ ํธ์์๋ Embedded Mongo์ Redis๋ฅผ ์ ํํ๊ฒ ๋์์ต๋๋ค. ์ด ๋ฐฉ์์ ์ธ๋ถ ์์กด์ฑ ์์ด ์ ํ๋ฆฌ์ผ์ด์ ๋ด์์ MongoDB์ Redis๋ฅผ ์ง์ ๋ด์ฅํ์ฌ ์คํํ ์ ์์ด, ์ฒ์ ์ค์ ํ์๋ ๋ณ๋์ ์ค์ ์์ด ๋ฐ๋ก ์คํํ ์ ์๊ธฐ ๋๋ฌธ์ ์ฆ์ ํ ์คํธ์ ๊ฐ๋ฐ์ ์์ํ ์ ์๋ค๋ ์ฅ์ ์ด ์์์ต๋๋ค.
๋ผ์ด๋ธ๋ฌ๋ฆฌ
์ผ๋จ ๋ก์ปฌ ๊ฐ๋ฐํ๊ฒฝ์์ Embedded Mongo์ Redis๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ build.gradle์ ์ถ๊ฐํฉ๋๋ค.
// embedded-mongo
implementation 'de.bwaldvogel:mongo-java-server:1.45.0'
// embedded-redis
implementation 'com.github.codemonstur:embedded-redis:1.4.3'
PortInspector ์ถ๊ฐ
๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ์์ Embedded Mongo์ Redis๋ ๋ณ๋์ ์ธ๋ถ ์๋ฒ ์์ด ๋ก์ปฌ์์ ์คํ๋๋ ์ฅ์ ์ด ์์ง๋ง, ํตํฉ ํ ์คํธ๋ฅผ ์ํํ ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ํนํ, ์๋ก ๋ค๋ฅธ ์์ฑ์ ๊ฐ์ง ํ ์คํธ๋ค์ด ๋์์ ์คํ๋๋ฉด, ๋ด์ฅ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ๋์ผํ ํฌํธ๋ฅผ ์ฌ์ฉํ๋ ค๊ณ ์๋ํ ์ ์์ด ์ถฉ๋์ด ๋ฐ์ํ ๊ฒ ๋ฉ๋๋ค. ์ด๋ก ์ธํด ํฌํธ ์ถฉ๋์ด ์ผ์ด๋ ํ ์คํธ๊ฐ ์คํจํ๊ณ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค.
๋ฌธ์ ์ํฉ
์ฌ๋ฌ ํ ์คํธ๊ฐ ์คํ๋ ๋ ์๋ก์ด ์ปจํ ์คํธ๊ฐ ์์ฑ๋๋ฉด, ์ด์ ์ ์คํ์ค์ธ ํฌํธ๋ฅผ ์ฌ์ฌ์ฉํ๋ ค๊ณ ์๋ํฉ๋๋ค. ๋ง์ฝ ํด๋น ํฌํธ๊ฐ ์ด๋ฏธ ์ฌ์ฉ์ค์ด๋ผ๋ฉด, ๋ค๋ฅธ ํ ์คํธ๊ฐ ์งํ๋์ง ์๊ฑฐ๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค. ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด, ํ ์คํธ๊ฐ ์ํ๋๋ ๋ํ ํฌํธ ์ถฉ๋์ ๋ง๊ณ ์์ฑ์ด ๋ณ๊ฒฝ๋๋ ํ ์คํธ๋ง๋ค ๋ ๋ฆฝ์ ์ธ ํฌํธ๋ฅผ ํ ๋นํ ํ์๊ฐ ์์์ต๋๋ค.
ํด๊ฒฐ ๋ฐฉ๋ฒ
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด PortInspector ํด๋์ค๋ฅผ ์ถ๊ฐํ์ต๋๋ค. ์ด ํด๋์ค๋ ํ์ฌ ์ฌ์ฉ์ค์ธ ํฌํธ๋ฅผ ๊ฒ์ฌํ์ฌ, ํน์ ํฌํธ๊ฐ ์ด๋ฏธ ์ฌ์ฉ์ค์ธ์ง๋ฅผ ํ์ธํฉ๋๋ค. ๋ง์ฝ ํด๋น ํฌํธ๊ฐ ์ฌ์ฉ์ค์ด๋ผ๋ฉด, ์๋ก์ด ํฌํธ๋ฅผ ์๋์ผ๋ก ํ ๋นํ์ฌ ํฌํธ ์ถฉ๋์ ๋ฐฉ์งํฉ๋๋ค.
@Component("portInspector")
@Profile("local")
public class PortInspector {
private static final int MIN_PORT = 10000;
private static final int MAX_PORT = 65535;
public boolean isPortRunning(int port) throws IOException {
return isRunning(executeGrepProcessCommand(port));
}
public int findAvailablePort() throws IOException {
for (int port = MIN_PORT; port <= MAX_PORT; port++) {
Process process = executeGrepProcessCommand(port);
if (!isRunning(process)) {
return port;
}
}
throw new IllegalArgumentException("Not Found Available Port: 10000 ~ 65535");
}
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);
}
private boolean isRunning(Process process) throws IOException {
String line;
StringBuilder pid = new StringBuilder();
try (BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
while ((line = input.readLine()) != null) {
pid.append(line);
}
} catch (Exception e) {
}
return !isEmpty(pid.toString());
}
}
Embedded ์ค์
๋ด์ฅ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค์ ์ฝ๋์์ @DependsOn("portInspector") ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ ์ด์ ๋ portInspector ๋น์ด ๋ฒ์ ์ด๊ธฐํ๋ ํ์ ๋ด์ฅ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค์ ์ด ์คํ๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ์ด๋ฅผ ๋ณด์ฅํ๊ธฐ ์ํด @DependsOn์ ์ฌ์ฉํ์ฌ, portInspector ๋น์ด ๋จผ์ ์ด๊ธฐํ๋ ํ์ ๋ค๋ฅธ ๋น๋ค์ด ์ด๊ธฐํ๋๋๋ก ์ค์ ํ์ต๋๋ค.
EmbeddedMongoConfig
@Configuration
@RequiredArgsConstructor
@DependsOn("portInspector")
@Profile("local")
public class EmbeddedMongoConfig {
@Value("${spring.data.mongodb.port}")
private int port;
@Value("${spring.data.mongodb.host}")
private String host;
@Value("${spring.data.mongodb.database}")
private String database;
private final PortInspector portInspector;
// MongoTemplate Bean ์์ฑ
@Bean
public MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDatabaseFactory) {
return new MongoTemplate(mongoDatabaseFactory);
}
// MongoDatabaseFactory ์์ฑ
@Bean
public MongoDatabaseFactory mongoDatabaseFactory(MongoServer mongoServer) {
String connection = mongoServer.getConnectionString();
return new SimpleMongoClientDatabaseFactory(appendDatabaseName(connection));
}
// MongoServer ์คํ
@Bean(destroyMethod = "shutdown")
public MongoServer mongoServer() throws IOException {
MongoServer mongoServer = new MongoServer(new MemoryBackend());
int availablePort = portInspector.isPortRunning(port) ? portInspector.findAvailablePort() : port;
mongoServer.bind(host, availablePort);
return mongoServer;
}
// connection String์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๋ฆ ์ถ๊ฐ
private String appendDatabaseName(String baseConnection) {
return String.format("%s/%s", baseConnection, database);
}
}
EmbeddedRedisConfig
@Configuration
@RequiredArgsConstructor
@DependsOn("portInspector")
@Profile("local")
public class EmbeddedRedisConfig {
private final RedisProperties redisProperties;
private final PortInspector portInspector;
private RedisServer redisServer;
// Redis ์๋ฒ ์์
@PostConstruct
private void start() throws IOException {
int redisPort = portInspector.isPortRunning(redisProperties.getPort()) ? portInspector.findAvailablePort()
: redisProperties.getPort();
redisProperties.setPort(redisPort);
redisServer = new RedisServer(redisPort);
redisServer.start();
}
// Redis ์๋ฒ ์ข
๋ฃ
@PreDestroy
private void stop() throws IOException {
if (redisServer != null) {
redisServer.stop();
}
}
}
์์ ๊ฐ์ด ์ค์ ์ ๋๋ง์ณค๋ค๋ฉด, ํ ์คํธ ์คํ์ด๋ ํ๋ก์ ํธ๋ฅผ ์คํ ์ ๋ด์ฅ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐฉ์์ผ๋ก MongoDB์ Redis๋ฅผ ์์ฝ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
'Project' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์ฑํ ๋ฉ์ธ์ง ์ฝ์ ์ฒ๋ฆฌ ๊ธฐ๋ฅ ๊ตฌ์กฐ ๊ฐ์ ๊ธฐ (0) | 2024.12.18 |
---|---|
๋ณ์ ์ค๋ณต ์์ฑ ๋์์ฑ ์ด์ ํด๊ฒฐ (0) | 2024.10.27 |
CompletableFuture๋ฅผ ํ์ฉํ ์ฑ๋ฅ ๊ฐ์ ๊ธฐ (0) | 2024.08.01 |