feat: Embedded Redis 적용 — Docker 없이 Redis 자동 구동
- EmbeddedRedisConfig: 외부 Redis 없으면 Embedded Redis 자동 시작 - RedisCacheAutoConfig: Redis 연결 실패 시 인메모리 캐시 fallback - install.bat/sh: Docker/Redis 필수 의존성 제거, 인메모리 전환 안내 - application yml: Redis 설정을 선택 사항으로 변경 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
이 Commit은 다음에 포함되어 있습니다:
@@ -34,6 +34,12 @@ dependencies {
|
|||||||
compileOnly 'com.oracle.database.jdbc:ojdbc11:23.6.0.24.10'
|
compileOnly 'com.oracle.database.jdbc:ojdbc11:23.6.0.24.10'
|
||||||
compileOnly 'com.microsoft.sqlserver:mssql-jdbc:12.8.1.jre11'
|
compileOnly 'com.microsoft.sqlserver:mssql-jdbc:12.8.1.jre11'
|
||||||
|
|
||||||
|
// Embedded Redis (Docker 없이 Redis 자동 구동)
|
||||||
|
api('it.ozimov:embedded-redis:0.7.3') {
|
||||||
|
exclude group: 'org.slf4j'
|
||||||
|
exclude group: 'ch.qos.logback'
|
||||||
|
}
|
||||||
|
|
||||||
// Micrometer
|
// Micrometer
|
||||||
runtimeOnly 'io.micrometer:micrometer-registry-prometheus'
|
runtimeOnly 'io.micrometer:micrometer-registry-prometheus'
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ where docker >nul 2>&1
|
|||||||
if !ERRORLEVEL! equ 0 (
|
if !ERRORLEVEL! equ 0 (
|
||||||
for /f "delims=" %%d in ('docker --version') do echo [OK] %%d
|
for /f "delims=" %%d in ('docker --version') do echo [OK] %%d
|
||||||
) else (
|
) else (
|
||||||
echo [WARN] Docker 미설치 — DB/Redis를 직접 설치해야 합니다
|
echo [INFO] Docker 미설치 — DB를 직접 설치해야 합니다
|
||||||
)
|
)
|
||||||
|
|
||||||
:: ---------- 4. Redis 확인 ----------
|
:: ---------- 4. Redis 확인 ----------
|
||||||
@@ -100,23 +100,7 @@ if !ERRORLEVEL! equ 0 (
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
if !REDIS_OK! equ 0 (
|
if !REDIS_OK! equ 0 (
|
||||||
where docker >nul 2>&1
|
echo [INFO] Redis 미실행 — 인메모리 캐시로 자동 전환됩니다 (운영 환경에서는 Redis 권장^)
|
||||||
if !ERRORLEVEL! equ 0 (
|
|
||||||
docker ps --format "{{.Names}}" 2>nul | findstr /i "redis" >nul 2>&1
|
|
||||||
if !ERRORLEVEL! equ 0 (
|
|
||||||
echo [OK] Redis Docker 컨테이너 실행 중
|
|
||||||
) else (
|
|
||||||
echo [INFO] Redis 미실행 — Docker로 자동 시작...
|
|
||||||
docker run -d --name redis -p 6379:6379 redis:7-alpine >nul 2>&1
|
|
||||||
if !ERRORLEVEL! equ 0 (
|
|
||||||
echo [OK] Redis 컨테이너 시작 완료
|
|
||||||
) else (
|
|
||||||
echo [WARN] Redis 자동 시작 실패 — 수동으로 Redis를 실행하세요
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) else (
|
|
||||||
echo [WARN] Redis 연결 불가 — Redis를 설치/실행하세요
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
:: ---------- 5. MySQL 연결 확인 ----------
|
:: ---------- 5. MySQL 연결 확인 ----------
|
||||||
@@ -170,8 +154,8 @@ if not exist "!PROJECT_ROOT!\.env" (
|
|||||||
echo DB_USER=jsh
|
echo DB_USER=jsh
|
||||||
echo DB_PASS=jsh@
|
echo DB_PASS=jsh@
|
||||||
echo.
|
echo.
|
||||||
echo # --- Redis ---
|
echo # --- Redis (선택 — 미설정 시 인메모리 캐시 자동 사용) ---
|
||||||
echo SPRING_DATA_REDIS_HOST=localhost
|
echo # SPRING_DATA_REDIS_HOST=localhost
|
||||||
echo.
|
echo.
|
||||||
echo # --- CORS ---
|
echo # --- CORS ---
|
||||||
echo CORS_ORIGINS=https://app.company.com
|
echo CORS_ORIGINS=https://app.company.com
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ echo "3. Docker 확인 (선택)"
|
|||||||
if command -v docker &>/dev/null; then
|
if command -v docker &>/dev/null; then
|
||||||
ok "$(docker --version | head -1)"
|
ok "$(docker --version | head -1)"
|
||||||
else
|
else
|
||||||
warn "Docker 미설치 — DB/Redis를 직접 설치해야 합니다"
|
info "Docker 미설치 — DB를 직접 설치해야 합니다"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ---------- 4. Redis 확인 ----------
|
# ---------- 4. Redis 확인 ----------
|
||||||
@@ -145,19 +145,8 @@ REDIS_HOST="${SPRING_DATA_REDIS_HOST:-localhost}"
|
|||||||
REDIS_PORT="${SPRING_DATA_REDIS_PORT:-6379}"
|
REDIS_PORT="${SPRING_DATA_REDIS_PORT:-6379}"
|
||||||
if command -v redis-cli &>/dev/null && redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" ping 2>/dev/null | grep -q PONG; then
|
if command -v redis-cli &>/dev/null && redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" ping 2>/dev/null | grep -q PONG; then
|
||||||
ok "Redis 응답 OK ($REDIS_HOST:$REDIS_PORT)"
|
ok "Redis 응답 OK ($REDIS_HOST:$REDIS_PORT)"
|
||||||
elif command -v docker &>/dev/null; then
|
|
||||||
if docker ps --format '{{.Names}}' 2>/dev/null | grep -q redis; then
|
|
||||||
ok "Redis Docker 컨테이너 실행 중"
|
|
||||||
else
|
else
|
||||||
info "Redis 미실행 — Docker로 자동 시작..."
|
info "Redis 미실행 — 인메모리 캐시로 자동 전환됩니다 (운영 환경에서는 Redis 권장)"
|
||||||
if docker run -d --name redis -p "${REDIS_PORT}:6379" redis:7-alpine &>/dev/null; then
|
|
||||||
ok "Redis 컨테이너 시작 완료"
|
|
||||||
else
|
|
||||||
warn "Redis 자동 시작 실패 — 수동으로 Redis를 실행하세요"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
warn "Redis 연결 불가 ($REDIS_HOST:$REDIS_PORT) — Redis를 설치/실행하세요"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ---------- 5. MySQL 연결 확인 ----------
|
# ---------- 5. MySQL 연결 확인 ----------
|
||||||
@@ -213,8 +202,8 @@ DB_NAME=mos
|
|||||||
DB_USER=jsh
|
DB_USER=jsh
|
||||||
DB_PASS=jsh@
|
DB_PASS=jsh@
|
||||||
|
|
||||||
# --- Redis ---
|
# --- Redis (선택 — 미설정 시 인메모리 캐시 자동 사용) ---
|
||||||
SPRING_DATA_REDIS_HOST=localhost
|
# SPRING_DATA_REDIS_HOST=localhost
|
||||||
|
|
||||||
# --- CORS ---
|
# --- CORS ---
|
||||||
CORS_ORIGINS=https://app.company.com
|
CORS_ORIGINS=https://app.company.com
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package kr.co.accura.wbx.spring.config;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import jakarta.annotation.PreDestroy;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import redis.embedded.RedisServer;
|
||||||
|
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Embedded Redis 자동 구성.
|
||||||
|
*
|
||||||
|
* 동작 순서:
|
||||||
|
* 1. 외부 Redis가 이미 실행 중이면 → 그대로 사용 (운영 환경)
|
||||||
|
* 2. 외부 Redis 없으면 → Embedded Redis 자동 시작 (개발 환경)
|
||||||
|
* 3. Embedded Redis도 실패하면 → RedisCacheAutoConfig에서 인메모리 캐시 전환
|
||||||
|
*
|
||||||
|
* Docker Desktop 없이 개발 PC에서 Redis가 자동 구동됩니다.
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class EmbeddedRedisConfig {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(EmbeddedRedisConfig.class);
|
||||||
|
|
||||||
|
@Value("${spring.data.redis.port:6379}")
|
||||||
|
private int redisPort;
|
||||||
|
|
||||||
|
private RedisServer redisServer;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void start() {
|
||||||
|
if (isRedisRunning()) {
|
||||||
|
log.info("[WBX] 외부 Redis 감지 — Embedded Redis 건너뜀 (port: {})", redisPort);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
redisServer = RedisServer.builder()
|
||||||
|
.port(redisPort)
|
||||||
|
.setting("maxmemory 128mb")
|
||||||
|
.setting("maxmemory-policy allkeys-lru")
|
||||||
|
.build();
|
||||||
|
redisServer.start();
|
||||||
|
log.info("[WBX] Embedded Redis 시작 (port: {})", redisPort);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("[WBX] Embedded Redis 시작 실패 — 인메모리 캐시로 전환됩니다 ({})", e.getMessage());
|
||||||
|
redisServer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreDestroy
|
||||||
|
public void stop() {
|
||||||
|
if (redisServer != null && redisServer.isActive()) {
|
||||||
|
redisServer.stop();
|
||||||
|
log.info("[WBX] Embedded Redis 종료");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isRedisRunning() {
|
||||||
|
try (Socket socket = new Socket("localhost", redisPort)) {
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package kr.co.accura.wbx.spring.config;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||||
|
import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
|
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redis/인메모리 캐시 자동 구성.
|
||||||
|
*
|
||||||
|
* 동작 방식:
|
||||||
|
* 1. Redis 연결 가능 → RedisCacheManager 사용
|
||||||
|
* 2. Redis 연결 불가 → ConcurrentMapCacheManager 자동 전환 (인메모리)
|
||||||
|
*
|
||||||
|
* 개발 PC에서 Docker/Redis 없이도 애플리케이션이 정상 구동됩니다.
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@AutoConfigureBefore(CacheAutoConfiguration.class)
|
||||||
|
public class RedisCacheAutoConfig {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(RedisCacheAutoConfig.class);
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
|
||||||
|
try {
|
||||||
|
redisConnectionFactory.getConnection().close();
|
||||||
|
log.info("[WBX] Redis 캐시 사용 (연결 성공)");
|
||||||
|
return RedisCacheManager.builder(redisConnectionFactory).build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("[WBX] Redis 연결 실패 — 인메모리 캐시로 전환 ({})", e.getMessage());
|
||||||
|
return new ConcurrentMapCacheManager("permissions", "deptScopes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1,4 @@
|
|||||||
|
kr.co.accura.wbx.spring.config.EmbeddedRedisConfig
|
||||||
|
kr.co.accura.wbx.spring.config.RedisCacheAutoConfig
|
||||||
kr.co.accura.wbx.spring.config.WbxAutoConfiguration
|
kr.co.accura.wbx.spring.config.WbxAutoConfiguration
|
||||||
kr.co.accura.wbx.spring.admin.AdminAutoConfiguration
|
kr.co.accura.wbx.spring.admin.AdminAutoConfiguration
|
||||||
|
|||||||
@@ -26,10 +26,11 @@ spring:
|
|||||||
minimum-idle: 5
|
minimum-idle: 5
|
||||||
connection-timeout: 30000
|
connection-timeout: 30000
|
||||||
|
|
||||||
data:
|
# Redis (선택 — 미설정 시 인메모리 캐시 자동 사용)
|
||||||
redis:
|
# data:
|
||||||
host: localhost
|
# redis:
|
||||||
port: 6379
|
# host: localhost
|
||||||
|
# port: 6379
|
||||||
|
|
||||||
server:
|
server:
|
||||||
port: 8080
|
port: 8080
|
||||||
|
|||||||
@@ -16,11 +16,6 @@ spring:
|
|||||||
flyway:
|
flyway:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
data:
|
|
||||||
redis:
|
|
||||||
host: localhost
|
|
||||||
port: 6379
|
|
||||||
|
|
||||||
wbx:
|
wbx:
|
||||||
spring:
|
spring:
|
||||||
jwt:
|
jwt:
|
||||||
|
|||||||
새 Issue에서 참조
사용자 차단