Embedded Mongo/Redis ์ ์šฉํ•˜๊ธฐ

2024. 11. 26. 16:02ยทProject

๋“ค์–ด๊ฐ€๋ฉฐ

์ด๋ฒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์˜ ์ผ๊ด€์„ฑ๊ณผ ํšจ์œจ์„ฑ์— ๋Œ€ํ•ด ๊ณ ๋ฏผ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํŒ€ ํ”„๋กœ์ ํŠธ๊ฐ€ ์ง„ํ–‰๋จ์— ๋”ฐ๋ผ, ์—ฌ๋Ÿฌ ๋ช…์˜ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋™์‹œ์— ์ž‘์—…์„ ์‹œ์ž‘ํ•˜๊ฒŒ ๋˜์—ˆ๊ณ , ํ”„๋กœ์ ํŠธ๋ฅผ 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๋ฅผ ์†์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

Embedded ์„ค์ •์„ ๋งˆ์นœํ›„ ํ…Œ์ŠคํŠธ ์‹คํ–‰
Embedded ์„ค์ •์„ ๋งˆ์นœ ํ›„ ํ”„๋กœ์ ํŠธ ์‹คํ–‰

 

'Project' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋กœ ์ธํ•œ API ์„ฑ๋Šฅ ๋ฌธ์ œ ํ•ด๊ฒฐ: ์ปค๋ฒ„๋ง ์ธ๋ฑ์Šค ๋ฐ ์ธ๋ฑ์Šค ํŠœ๋‹์„ ํ†ตํ•œ ์ตœ์ ํ™”  (1) 2025.05.29
GitHub Actions + Docker + NGINX๋ฅผ ํ™œ์šฉํ•œ Blue/Green ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ํ™˜๊ฒฝ ๊ตฌ์ถ•  (1) 2025.05.04
์ฑ„ํŒ… ๋ฉ”์„ธ์ง€ ์ฝ์Œ ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ ๊ตฌ์กฐ ๊ฐœ์„ ๊ธฐ  (0) 2024.12.18
๋ณ„์  ์ค‘๋ณต ์ƒ์„ฑ ๋™์‹œ์„ฑ ์ด์Šˆ ํ•ด๊ฒฐ  (0) 2024.10.27
CompletableFuture๋ฅผ ํ™œ์šฉํ•œ ์„ฑ๋Šฅ ๊ฐœ์„ ๊ธฐ  (0) 2024.08.01
'Project' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • GitHub Actions + Docker + NGINX๋ฅผ ํ™œ์šฉํ•œ Blue/Green ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ํ™˜๊ฒฝ ๊ตฌ์ถ•
  • ์ฑ„ํŒ… ๋ฉ”์„ธ์ง€ ์ฝ์Œ ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ ๊ตฌ์กฐ ๊ฐœ์„ ๊ธฐ
  • ๋ณ„์  ์ค‘๋ณต ์ƒ์„ฑ ๋™์‹œ์„ฑ ์ด์Šˆ ํ•ด๊ฒฐ
  • CompletableFuture๋ฅผ ํ™œ์šฉํ•œ ์„ฑ๋Šฅ ๊ฐœ์„ ๊ธฐ
jwooo๐ŸŒฑ
jwooo๐ŸŒฑ
jwooo's log ์ž…๋‹ˆ๋‹ค.
  • jwooo๐ŸŒฑ
    jwooo's log
    jwooo๐ŸŒฑ
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (12)
      • Java (4)
      • Project (6)
      • Computer Science (2)
        • Network (1)
        • Security (1)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.3
jwooo๐ŸŒฑ
Embedded Mongo/Redis ์ ์šฉํ•˜๊ธฐ
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”