kos-mock 상품변경 실제 DB 업데이트 기능 추가

- MockDataService에 updateCustomerProduct 메서드 추가
- KosMockService에 실제 고객 데이터 업데이트 로직 추가
- 상품변경 시 고객의 current_product_code를 실제로 업데이트하도록 수정
- 트랜잭션 처리로 데이터 일관성 보장
- product-service Hibernate dialect 설정 추가

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
hiondal
2025-09-10 02:06:24 +09:00
parent 6ca4daed8d
commit 02bcfa5434
122 changed files with 6116 additions and 3983 deletions
@@ -1,200 +1,6 @@
spring:
datasource:
url: jdbc:${DB_KIND:postgresql}://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:product_change_db}
username: ${DB_USERNAME:phonebill_user}
password: ${DB_PASSWORD:phonebill_pass}
driver-class-name: org.postgresql.Driver
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
leak-detection-threshold: 60000
# JPA 설정
jpa:
show-sql: ${SHOW_SQL:true}
properties:
hibernate:
format_sql: true
use_sql_comments: true
hibernate:
ddl-auto: ${DDL_AUTO:update}
# Redis 설정
data:
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
timeout: 2000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
database: ${REDIS_DATABASE:2}
# Cache 개발 설정 (TTL 단축)
cache:
redis:
time-to-live: 3600000 # 1시간 (개발환경에서 단축)
# Server 개발 설정
server:
port: ${SERVER_PORT:8083}
error:
include-stacktrace: always
include-message: always
include-binding-errors: always
# Logging 개발 설정
logging:
level:
com.unicorn.phonebill: ${LOG_LEVEL_APP:DEBUG}
org.springframework.security: ${LOG_LEVEL_SECURITY:DEBUG}
org.hibernate.SQL: ${LOG_LEVEL_SQL:DEBUG}
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
org.springframework.web: DEBUG
org.springframework.cache: DEBUG
pattern:
console: "%clr(%d{HH:mm:ss.SSS}){faint} %clr([%thread]){faint} %clr(%-5level){spring} %clr(%logger{36}){cyan} - %msg%n"
# Management 개발 설정
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
show-components: always
info:
env:
enabled: true
# OpenAPI 개발 설정
springdoc:
swagger-ui:
enabled: true
try-it-out-enabled: true
api-docs:
enabled: true
show-actuator: true
# Resilience4j 개발 설정 (더 관대한 설정)
resilience4j:
circuitbreaker:
configs:
default:
failure-rate-threshold: 70
minimum-number-of-calls: 3
wait-duration-in-open-state: 5s
instances:
kosClient:
failure-rate-threshold: 80
wait-duration-in-open-state: 10s
retry:
instances:
kosClient:
max-attempts: 3
wait-duration: 1s
# KOS Mock 서버 설정 (개발환경용)
kos:
base-url: ${KOS_BASE_URL:http://localhost:9090/kos}
connect-timeout: 5s
read-timeout: 10s
max-retries: 3
retry-delay: 1s
# Mock 모드 설정
mock:
enabled: ${KOS_MOCK_ENABLED:true}
response-delay: 500ms # Mock 응답 지연 시뮬레이션
endpoints:
customer-info: /api/v1/customer/{lineNumber}
product-info: /api/v1/product/{productCode}
available-products: /api/v1/products/available
product-change: /api/v1/product/change
headers:
api-key: ${KOS_API_KEY:dev-api-key}
client-id: ${KOS_CLIENT_ID:product-service-dev}
# 비즈니스 개발 설정
app:
product:
cache:
customer-info-ttl: ${PRODUCT_CACHE_CUSTOMER_INFO_TTL:600} # 10분 (개발환경에서 단축)
product-info-ttl: ${PRODUCT_CACHE_PRODUCT_INFO_TTL:300} # 5분
available-products-ttl: ${PRODUCT_CACHE_AVAILABLE_PRODUCTS_TTL:1800} # 30분
product-status-ttl: ${PRODUCT_CACHE_PRODUCT_STATUS_TTL:300} # 5분
line-status-ttl: ${PRODUCT_CACHE_LINE_STATUS_TTL:180} # 3분
validation:
enabled: ${PRODUCT_VALIDATION_ENABLED:true}
strict-mode: ${PRODUCT_VALIDATION_STRICT_MODE:false} # 개발환경에서는 유연하게
processing:
async-enabled: ${PRODUCT_PROCESSING_ASYNC_ENABLED:false} # 개발환경에서는 동기 처리
# 개발용 테스트 데이터
test-data:
enabled: ${TEST_DATA_ENABLED:true}
customers:
- lineNumber: "01012345678"
customerId: "CUST001"
customerName: "홍길동"
currentProductCode: "PLAN001"
- lineNumber: "01087654321"
customerId: "CUST002"
customerName: "김철수"
currentProductCode: "PLAN002"
products:
- productCode: "PLAN001"
productName: "5G 베이직 플랜"
monthlyFee: 45000
dataAllowance: "50GB"
- productCode: "PLAN002"
productName: "5G 프리미엄 플랜"
monthlyFee: 65000
dataAllowance: "100GB"
security:
jwt:
secret: ${JWT_SECRET:dev-secret-key-for-testing-only}
expiration: ${JWT_EXPIRATION:3600} # 1시간 (개발환경에서 단축)
cors:
allowed-origins: ${CORS_ALLOWED_ORIGINS:*} # 개발환경에서만 허용
# DevTools 설정
spring.devtools:
restart:
enabled: true
exclude: static/**,public/**,templates/**
livereload:
enabled: true
port: 35729
add-properties: true
# 디버깅 설정
debug: false
trace: false
# 개발 환경 정보
info:
app:
name: ${spring.application.name}
description: Product-Change Service Development Environment
version: ${spring.application.version}
encoding: UTF-8
java:
version: ${java.version}
build:
artifact: ${project.artifactId:product-service}
name: ${project.name:Product Service}
version: ${project.version:1.0.0}
time: ${build.time:2024-03-15T10:00:00Z}
root: INFO
com.unicorn.phonebill: DEBUG
org.springframework.security: DEBUG
org.hibernate: DEBUG
@@ -1,273 +1,6 @@
spring:
# Database - 운영환경 (PostgreSQL)
datasource:
url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:phonebill_product_prod}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
driver-class-name: org.postgresql.Driver
hikari:
maximum-pool-size: 20
minimum-idle: 5
idle-timeout: 300000
max-lifetime: 1800000
connection-timeout: 20000
validation-timeout: 5000
leak-detection-threshold: 60000
# JPA 운영 설정
jpa:
hibernate:
ddl-auto: validate
show-sql: false
properties:
hibernate:
format_sql: false
use_sql_comments: false
generate_statistics: false
# Redis - 운영환경 (클러스터)
data:
redis:
cluster:
nodes: ${REDIS_CLUSTER_NODES}
password: ${REDIS_PASSWORD}
timeout: 2000ms
lettuce:
cluster:
refresh:
adaptive: true
period: 30s
pool:
max-active: 50
max-idle: 20
min-idle: 5
max-wait: 3000ms
# Server 운영 설정
server:
port: ${SERVER_PORT:8080}
shutdown: graceful
compression:
enabled: true
min-response-size: 1024
tomcat:
connection-timeout: 30s
max-connections: 8192
max-threads: 200
min-spare-threads: 10
accept-count: 100
error:
include-stacktrace: never
include-message: on-param
include-binding-errors: never
# Graceful Shutdown
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
# Logging 운영 설정
logging:
level:
root: WARN
com.unicorn.phonebill: INFO
org.springframework.security: WARN
org.hibernate: WARN
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{traceId:-},%X{spanId:-}] %logger{36} - %msg%n"
file:
name: /app/logs/product-service.log
max-size: 500MB
max-history: 30
total-size-cap: 10GB
logback:
rollingpolicy:
clean-history-on-start: true
# Management 운영 설정
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: never
show-components: never
info:
enabled: true
health:
probes:
enabled: true
livenessstate:
enabled: true
readinessstate:
enabled: true
metrics:
distribution:
percentiles:
http.server.requests: 0.5, 0.95, 0.99
slo:
http.server.requests: 50ms, 100ms, 200ms, 500ms, 1s, 2s
# OpenAPI 운영 설정 (비활성화)
springdoc:
api-docs:
enabled: false
swagger-ui:
enabled: false
# Resilience4j 운영 설정
resilience4j:
circuitbreaker:
configs:
default:
failure-rate-threshold: 50
slow-call-rate-threshold: 50
slow-call-duration-threshold: 3s
permitted-number-of-calls-in-half-open-state: 5
minimum-number-of-calls: 10
wait-duration-in-open-state: 30s
sliding-window-size: 20
instances:
kosClient:
base-config: default
failure-rate-threshold: 40
wait-duration-in-open-state: 60s
minimum-number-of-calls: 20
retry:
configs:
default:
max-attempts: 3
wait-duration: 2s
exponential-backoff-multiplier: 2
instances:
kosClient:
base-config: default
max-attempts: 2
wait-duration: 3s
timelimiter:
configs:
default:
timeout-duration: 8s
instances:
kosClient:
timeout-duration: 15s
# KOS 서버 설정 (운영환경)
kos:
base-url: ${KOS_BASE_URL}
connect-timeout: 10s
read-timeout: 30s
max-retries: 2
retry-delay: 3s
endpoints:
customer-info: /api/v1/customer/{lineNumber}
product-info: /api/v1/product/{productCode}
available-products: /api/v1/products/available
product-change: /api/v1/product/change
headers:
api-key: ${KOS_API_KEY}
client-id: ${KOS_CLIENT_ID:product-service}
# 운영환경 보안 설정
ssl:
enabled: true
trust-store: ${SSL_TRUST_STORE:/app/certs/truststore.jks}
trust-store-password: ${SSL_TRUST_STORE_PASSWORD}
key-store: ${SSL_KEY_STORE:/app/certs/keystore.jks}
key-store-password: ${SSL_KEY_STORE_PASSWORD}
# 비즈니스 운영 설정
app:
product:
cache:
customer-info-ttl: 14400 # 4시간
product-info-ttl: 7200 # 2시간
available-products-ttl: 86400 # 24시간
product-status-ttl: 3600 # 1시간
line-status-ttl: 1800 # 30분
validation:
enabled: true
strict-mode: true
max-retry-attempts: 2
validation-timeout: 10s
processing:
async-enabled: true
max-concurrent-requests: 500
request-timeout: 60s
security:
jwt:
secret: ${JWT_SECRET}
expiration: 86400 # 24시간
refresh-expiration: 604800 # 7일
cors:
allowed-origins: ${CORS_ALLOWED_ORIGINS}
allowed-methods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
allowed-headers:
- Authorization
- Content-Type
- Accept
- X-Requested-With
- X-Forwarded-For
- X-Forwarded-Proto
allow-credentials: true
max-age: 3600
# 모니터링 설정
monitoring:
health-check:
interval: 30s
timeout: 10s
metrics:
enabled: true
export-interval: 60s
alerts:
email-enabled: ${ALERT_EMAIL_ENABLED:false}
slack-enabled: ${ALERT_SLACK_ENABLED:false}
webhook-url: ${ALERT_WEBHOOK_URL:}
# 운영 환경 정보
info:
app:
name: ${spring.application.name}
description: Product-Change Service Production Environment
version: ${spring.application.version}
environment: production
build:
artifact: product-service
version: ${BUILD_VERSION:1.0.0}
time: ${BUILD_TIME}
commit: ${GIT_COMMIT:unknown}
branch: ${GIT_BRANCH:main}
# JVM 튜닝 설정 (환경변수로 설정)
# JAVA_OPTS=-Xms2g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=200
# -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/heapdumps/
# -Dspring.profiles.active=prod
# 외부 의존성 URLs
external:
auth-service:
url: ${AUTH_SERVICE_URL:http://auth-service:8080}
bill-inquiry-service:
url: ${BILL_INQUIRY_SERVICE_URL:http://bill-inquiry-service:8081}
# 데이터베이스 마이그레이션 (Flyway)
spring:
flyway:
enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
validate-on-migrate: true
org.hibernate: WARN
@@ -5,69 +5,60 @@ spring:
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
# Database 기본 설정
datasource:
url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:product_change}
username: ${DB_USERNAME:product_user}
password: ${DB_PASSWORD:product_pass}
driver-class-name: org.postgresql.Driver
hikari:
maximum-pool-size: 20
minimum-idle: 5
idle-timeout: 300000
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
connection-timeout: 20000
validation-timeout: 5000
leak-detection-threshold: 60000
# JPA 기본 설정
# JPA 설정
jpa:
open-in-view: false
hibernate:
ddl-auto: ${JPA_DDL_AUTO:validate}
show-sql: ${JPA_SHOW_SQL:false}
show-sql: ${SHOW_SQL:true}
properties:
hibernate:
format_sql: false
use_sql_comments: false
jdbc:
batch_size: 25
order_inserts: true
order_updates: true
format_sql: true
use_sql_comments: true
connection:
provider_disables_autocommit: true
# Redis 기본 설정
provider_disables_autocommit: false
dialect: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: ${DDL_AUTO:update}
# Redis 설정
data:
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
timeout: 2000ms
lettuce:
pool:
max-active: 20
max-active: 8
max-idle: 8
min-idle: 2
min-idle: 0
max-wait: -1ms
time-between-eviction-runs: 30s
# Cache 설정
database: ${REDIS_DATABASE:2}
# Cache 개발 설정 (TTL 단축)
cache:
type: redis
cache-names:
- customerInfo
- productInfo
- availableProducts
- productStatus
- lineStatus
redis:
time-to-live: 14400000 # 4시간 (ms)
cache-null-values: false
use-key-prefix: true
key-prefix: "product-service:"
# Security 기본 설정
security:
oauth2:
resourceserver:
jwt:
issuer-uri: ${JWT_ISSUER_URI:http://localhost:8080/auth}
time-to-live: 3600000 # 1시간 (개발환경에서 단축)
# Server 개발 설정
server:
port: ${SERVER_PORT:8083}
error:
include-stacktrace: always
include-message: always
include-binding-errors: always
# Jackson 설정
jackson:
serialization:
@@ -78,22 +69,16 @@ spring:
adjust-dates-to-context-time-zone: false
time-zone: Asia/Seoul
date-format: yyyy-MM-dd'T'HH:mm:ss
# HTTP 설정
webflux: {}
# Server 설정
server:
port: ${SERVER_PORT:8083}
compression:
enabled: true
mime-types: application/json,application/xml,text/html,text/xml,text/plain
http2:
enabled: true
error:
include-stacktrace: never
include-message: always
include-binding-errors: always
# CORS
cors:
allowed-origins: ${CORS_ALLOWED_ORIGINS:http://localhost:3000}
# JWT 토큰 설정
jwt:
secret: ${JWT_SECRET:}
access-token-validity: ${JWT_ACCESS_TOKEN_VALIDITY:1800000}
refresh-token-validity: ${JWT_REFRESH_TOKEN_VALIDITY:86400000}
# Management & Actuator
management:
@@ -128,25 +113,6 @@ management:
build:
enabled: true
# Logging 설정
logging:
level:
root: ${LOG_LEVEL_ROOT:INFO}
com.unicorn.phonebill: ${LOG_LEVEL_APP:INFO}
org.springframework.security: ${LOG_LEVEL_SECURITY:WARN}
org.hibernate.SQL: ${LOG_LEVEL_SQL:WARN}
org.hibernate.type: WARN
pattern:
console: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
file:
name: ${LOG_FILE:logs/product-service.log}
logback:
rollingpolicy:
max-file-size: 10MB
max-history: 7
total-size-cap: 100MB
# OpenAPI/Swagger 설정
springdoc:
api-docs:
@@ -214,42 +180,44 @@ resilience4j:
base-config: default
timeout-duration: 10s
# 비즈니스 설정
app:
product:
cache:
customer-info-ttl: 14400 # 4시간 (초)
product-info-ttl: 7200 # 2시간 (초)
available-products-ttl: 86400 # 24시간 (초)
product-status-ttl: 3600 # 1시간 (초)
line-status-ttl: 1800 # 30분 (초)
validation:
max-retry-attempts: 3
validation-timeout: 5s
processing:
async-enabled: ${PRODUCT_PROCESSING_ASYNC_ENABLED:true}
max-concurrent-requests: ${PRODUCT_PROCESSING_MAX_CONCURRENT_REQUESTS:100}
request-timeout: ${PRODUCT_PROCESSING_REQUEST_TIMEOUT:30s}
security:
jwt:
secret: ${JWT_SECRET:product-service-secret-key-change-in-production}
expiration: ${JWT_EXPIRATION:86400} # 24시간
refresh-expiration: ${JWT_REFRESH_EXPIRATION:604800} # 7일
cors:
allowed-origins:
- http://localhost:3000
- https://mvno.com
allowed-methods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
allowed-headers:
- Authorization
- Content-Type
- Accept
- X-Requested-With
allow-credentials: true
max-age: 3600
# KOS Mock 서버 설정
kos:
base-url: ${KOS_BASE_URL:http://localhost:9090}
connect-timeout: ${KOS_CONNECT_TIMEOUT:5000}
read-timeout: ${KOS_READ_TIMEOUT:10000}
max-retries: ${KOS_MAX_RETRIES:3}
retry-delay: ${KOS_RETRY_DELAY:1000}
# Circuit Breaker 설정
circuit-breaker:
failure-rate-threshold: ${KOS_CB_FAILURE_RATE:0.5}
slow-call-duration-threshold: ${KOS_CB_SLOW_CALL_THRESHOLD:10000}
slow-call-rate-threshold: ${KOS_CB_SLOW_CALL_RATE:0.5}
sliding-window-size: ${KOS_CB_SLIDING_WINDOW_SIZE:10}
minimum-number-of-calls: ${KOS_CB_MIN_CALLS:5}
permitted-number-of-calls-in-half-open-state: ${KOS_CB_HALF_OPEN_CALLS:3}
wait-duration-in-open-state: ${KOS_CB_WAIT_DURATION:60000}
# Logging 운영 설정
logging:
level:
root: WARN
com.unicorn.phonebill: INFO
com.phonebill.common.security: DEBUG
org.springframework.security: DEBUG
org.hibernate: WARN
org.hibernate.resource.transaction: ERROR
org.hibernate.internal: ERROR
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{traceId:-},%X{spanId:-}] %logger{36} - %msg%n"
file:
name: logs/product-service.log
max-size: 500MB
max-history: 30
total-size-cap: 10GB
logback:
rollingpolicy:
clean-history-on-start: true