Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions .env.prod.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# =============================================================================
# Production Environment Configuration
# =============================================================================
# 운영 환경용 설정 파일
#
# 사용법:
# 1. 이 파일을 .env.prod로 복사하세요
# 2. 필요한 값들을 채워넣으세요
# 3. docker-compose -f docker-compose-prod.yml --env-file .env.prod up -d 로 실행하세요
#
# 주의: .env.prod 파일은 민감한 정보를 포함하므로 절대 Git에 커밋하지 마세요!
# =============================================================================

# =============================================================================
# Application Configuration
# =============================================================================
SERVER_PORT=8080

# =============================================================================
# Docker Configuration
# =============================================================================
DOCKER_USERNAME=your_docker_username
DOCKER_REPO=your_docker_repo
DOCKER_IMAGE_TAG=prod

# =============================================================================
# Database Configuration
# =============================================================================
DB_IP=your_db_host
DB_PORT=3306
DB_SCHEMA=your_db_schema
DB_USER=your_db_user
DB_PASSWORD=your_db_password

# =============================================================================
# Security Configuration
# =============================================================================
# JWT 시크릿 키 (최소 256비트 권장, 운영 환경에서는 반드시 강력한 키 사용)
JWT_SECRET_KEY=your_production_secret_key_minimum_32_characters_long
JWT_ACCESS_TOKEN_VALIDITY=3600000 # 1시간 (밀리초)
JWT_REFRESH_TOKEN_VALIDITY=86400000 # 24시간 (밀리초)

# =============================================================================
# External API Configuration
# =============================================================================
# Nexon Open API 키 (https://openapi.nexon.com/에서 발급)
NEXON_OPEN_API_KEY=your_nexon_api_key_here

# 경매 데이터 수집 설정
AUCTION_HISTORY_DELAY_MS=1000 # API 호출 간 딜레이 (1초)
AUCTION_HISTORY_CRON=0 0 * * * * # 매시간 정각에 실행

# 통계 스케줄러 설정
STATISTICS_PREVIOUS_DAY_CRON=0 10 0 * * * # 매일 00:10에 전일 통계 확정

# =============================================================================
# Elasticsearch Configuration
# =============================================================================
# Elasticsearch 기능 활성화 여부
ELASTICSEARCH_ENABLED=true
ELASTICSEARCH_INDEX_ENABLED=true

# Elasticsearch 버전 (docker-compose-prod.yml에서 사용)
ELASTICSEARCH_VERSION=8.11.0

# Elasticsearch 포트 (기본값: 9200, 9300)
ELASTICSEARCH_PORT=9200
ELASTICSEARCH_TRANSPORT_PORT=9300

# Elasticsearch JVM Heap 크기 (최대 3GB 권장)
# 주의: 시스템 메모리의 50%를 넘지 않도록 설정
ELASTICSEARCH_HEAP_SIZE=1536m

# =============================================================================
# JVM Configuration (선택사항 - docker-compose에 기본값 있음)
# =============================================================================
# JAVA_OPTS_XMS=768m
# JAVA_OPTS_XMX=1536m
# JAVA_OPTS_MAX_METASPACE_SIZE=450m
# JAVA_OPTS_RESERVED_CODE_CACHE_SIZE=144m
# JAVA_OPTS_MAX_DIRECT_MEMORY_SIZE=192m
89 changes: 82 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,21 @@

## 주요 기능

### 경매장 데이터 수집
### 경매장 거래 내역 수집
- 매 1시간마다 마비노기 경매장 거래 내역을 Nexon Open API를 통해 수집
- 약 70개의 아이템 카테고리에 대한 커서 기반 페이지네이션으로 데이터 수집
- 카테고리별 요청 간 딜레이 설정으로 API Rate Limit 준수

### 실시간 경매장 데이터 수집
- 10분 간격으로 현재 판매 중인 아이템 정보 수집
- 만료된 아이템 자동 삭제
- 거래 내역과 분리된 별도 테이블로 관리

### 뿔피리 데이터 수집
- 5분마다 거대한 외침의 뿔피리 내역 수집
- 4개 서버 지원 (류트, 만돌린, 하프, 울프)
- 지수 백오프 기반 재시도 로직 구현
- **Elasticsearch 연동**: 한글 전문 검색 지원 (Ngram Parser)

### 통계 분석
- **일간 통계**: 경매 데이터 수집 완료 시 자동 트리거 (이벤트 기반)
Expand All @@ -28,7 +34,7 @@
### 데이터 조회
- 아이템별 최저가, 최고가, 평균가, 거래량 등 시세 조회
- 서버별/전체 뿔피리 내역 조회
- 아이템 옵션 필터링 (무기 공격력, 방어구 방어력 등)
- 아이템 옵션 필터링 (무기 공격력, 방어구 방어력, 세공 옵션 등)

<br>

Expand All @@ -39,9 +45,11 @@
| **Backend** | Java 21, Spring Boot 3.5.0, Spring Data JPA, QueryDSL |
| **HTTP Client** | Spring WebFlux (WebClient) |
| **Database** | MySQL 8, Flyway |
| **Search Engine** | Elasticsearch (한글 전문 검색) |
| **Security** | Spring Security, JWT |
| **Test** | JUnit5, Mockito, AssertJ, Testcontainers |
| **Code Quality** | Spotless (Google Java Format AOSP), Jacoco |
| **Documentation** | Swagger, Spring REST Docs |
| **Documentation** | Swagger (Springdoc OpenAPI), Spring REST Docs |
| **DevOps** | Docker Compose, GitHub Actions |
| **Deployment** | Oracle Cloud |

Expand All @@ -52,7 +60,10 @@
```
src/main/java/until/the/eternity/
├── auctionhistory/ # 경매장 거래 내역 수집 및 검색
├── hornBugle/ # 뿔피리 내역 수집 및 검색
├── auctionrealtime/ # 실시간 경매장 데이터 수집 (10분 간격)
├── auctionitem/ # 경매장 아이템 엔티티
├── auctionitemoption/ # 아이템 옵션 정보 (세공 옵션 포함)
├── hornBugle/ # 뿔피리 내역 수집 및 검색 (Elasticsearch 연동)
├── statistics/ # 일간/주간 통계 집계
├── iteminfo/ # 아이템 메타데이터
├── itemoptioninfo/ # 아이템 옵션 정보
Expand All @@ -76,19 +87,45 @@ infrastructure/ # Repository 구현체, JPA

## API 엔드포인트

### 경매장 거래 내역
| Endpoint | Method | 설명 |
|----------|--------|------|
| `/auction-history/search` | GET | 경매 내역 검색 (필터 및 페이징) |
| `/auction-history/{id}` | GET | 단일 거래 내역 조회 |
| `/auction-history/batch` | POST | 배치 수동 실행 |

### 실시간 경매장
| Endpoint | Method | 설명 |
|----------|--------|------|
| `/auction-realtime/search` | GET | 현재 판매 중인 아이템 검색 |
| `/auction-realtime/{id}` | GET | 단일 아이템 조회 |

### 뿔피리
| Endpoint | Method | 설명 |
|----------|--------|------|
| `/horn-bugle` | GET | 뿔피리 내역 검색 (서버별/전체) |
| `/horn-bugle/batch` | POST | 뿔피리 배치 수동 실행 |

### 통계
| Endpoint | Method | 설명 |
|----------|--------|------|
| `/statistics/daily/items` | GET | 일간 아이템 통계 |
| `/statistics/daily/subcategories` | GET | 일간 서브카테고리 통계 |
| `/statistics/daily/top-categories` | GET | 일간 상위카테고리 통계 |
| `/statistics/weekly/items` | GET | 주간 아이템 통계 |
| `/statistics/weekly/subcategories` | GET | 주간 서브카테고리 통계 |
| `/statistics/weekly/top-categories` | GET | 주간 상위카테고리 통계 |

### 메타데이터
| Endpoint | Method | 설명 |
|----------|--------|------|
| `/api/item-infos` | GET | 아이템 메타데이터 |
| `/api/v1/item-option-infos` | GET | 아이템 옵션 정보 |
| `/api/auction-search-options` | GET | 검색 옵션 메타데이터 |

### 시스템
| Endpoint | Method | 설명 |
|----------|--------|------|
| `/actuator/health` | GET | 헬스체크 |
| `/swagger-ui/index.html` | - | API 문서 |

Expand All @@ -99,6 +136,7 @@ infrastructure/ # Repository 구현체, JPA
| 스케줄러 | Cron 표현식 | 설명 |
|----------|-------------|------|
| 경매 내역 수집 | `0 0 * * * *` | 매 시 정각 |
| 실시간 경매장 수집 | `0 0/10 * * * *` | 10분마다 |
| 뿔피리 수집 | `0 */5 * * * *` | 5분마다 |
| 전일 통계 확정 | `0 10 0 * * *` | 매일 00:10 |
| 주간 통계 집계 | `5 0 4 * * MON` | 매주 월요일 04:00 |
Expand Down Expand Up @@ -126,6 +164,11 @@ JWT_REFRESH_TOKEN_VALIDITY=86400000

# Nexon Open API
NEXON_OPEN_API_KEY=your-api-key

# Elasticsearch
ELASTICSEARCH_URIS=http://localhost:9200
ELASTICSEARCH_USERNAME=
ELASTICSEARCH_PASSWORD=
```

### 선택 환경 변수
Expand All @@ -134,11 +177,19 @@ NEXON_OPEN_API_KEY=your-api-key
AUCTION_HISTORY_CRON=0 0 * * * *
AUCTION_HISTORY_DELAY_MS=1000

# 실시간 경매장 배치
AUCTION_REALTIME_CRON=0 0/10 * * * *
AUCTION_REALTIME_DELAY_MS=500

# 뿔피리 배치
HORN_BUGLE_CRON=0 */5 * * * *
HORN_BUGLE_MAX_RETRIES=3
HORN_BUGLE_RETRY_DELAY_MS=2000

# Elasticsearch 기능
ELASTICSEARCH_ENABLED=true
ELASTICSEARCH_INDEX_ENABLED=true

# 통계
STATISTICS_PREVIOUS_DAY_CRON=0 10 0 * * *
STATISTICS_WEEKLY_CRON=5 0 4 * * MON
Expand Down Expand Up @@ -179,9 +230,9 @@ docker-compose -f docker-compose-local.yml down

| 환경 | 파일 | 설명 |
|------|------|------|
| 로컬 개발 | `docker-compose-local.yml` | 로컬 빌드, 낮은 리소스 |
| 개발 서버 | `docker-compose-dev.yml` | 개발 환경 배포 |
| 운영 서버 | `docker-compose-prod.yml` | 운영 환경 배포 |
| 로컬 개발 | `docker-compose-local.yml` | 로컬 빌드, MySQL, Elasticsearch 포함 |
| 개발 서버 | `docker-compose-dev.yml` | 개발 환경 배포, Autoheal 컨테이너 포함 |
| 운영 서버 | `docker-compose-prod.yml` | 운영 환경 배포, 높은 리소스 할당 |

<br>

Expand Down Expand Up @@ -209,6 +260,30 @@ docker-compose -f docker-compose-local.yml down

<br>

## 데이터베이스 스키마

Flyway를 통한 마이그레이션 관리 (17개 버전)

### 주요 테이블
| 테이블 | 설명 |
|--------|------|
| `auction_history` | 경매장 거래 내역 |
| `auction_realtime_item` | 현재 판매 중인 아이템 |
| `auction_history_item_option` | 거래 아이템 옵션 (세공 포함) |
| `auction_realtime_item_option` | 실시간 아이템 옵션 |
| `horn_bugle_world_history` | 뿔피리 내역 (FULLTEXT 인덱스) |
| `item_daily_statistics` | 일간 아이템 통계 |
| `item_weekly_statistics` | 주간 아이템 통계 |
| `subcategory_daily_statistics` | 일간 서브카테고리 통계 |
| `subcategory_weekly_statistics` | 주간 서브카테고리 통계 |
| `top_category_daily_statistics` | 일간 상위카테고리 통계 |
| `top_category_weekly_statistics` | 주간 상위카테고리 통계 |
| `item_info` | 아이템 메타데이터 |
| `item_option_value_info` | 아이템 옵션 정보 |
| `metalware_info` | 금속류 정보 |

<br>

## API 응답 형식

```json
Expand Down
3 changes: 3 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ dependencies {
// P6Spy
implementation("com.github.gavlyukovskiy:p6spy-spring-boot-starter:${property("p6spyVersion")}")

// Elasticsearch
implementation("org.springframework.boot:spring-boot-starter-data-elasticsearch")

// QueryDSL (with Jakarta API)
implementation("com.querydsl:querydsl-core:5.1.0")
implementation("com.querydsl:querydsl-jpa:5.1.0:jakarta")
Expand Down
1 change: 1 addition & 0 deletions docker-compose-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ services:
-Xmx${JAVA_OPTS_XMX:-512m}
-XX:MaxMetaspaceSize=${JAVA_OPTS_MAX_METASPACE_SIZE:-150m}
-XX:ReservedCodeCacheSize=${JAVA_OPTS_RESERVED_CODE_CACHE_SIZE:-48m}
-XX:ReservedCodeCacheSize=${JAVA_OPTS_RESERVED_CODE_CACHE_SIZE:-48m}

Copilot AI Jan 28, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate line: -XX:ReservedCodeCacheSize=${JAVA_OPTS_RESERVED_CODE_CACHE_SIZE:-48m} appears on both line 59 and line 60. Remove the duplicate.

Suggested change
-XX:ReservedCodeCacheSize=${JAVA_OPTS_RESERVED_CODE_CACHE_SIZE:-48m}

Copilot uses AI. Check for mistakes.
-XX:MaxDirectMemorySize=${JAVA_OPTS_MAX_DIRECT_MEMORY_SIZE:-64m}
-Xss${JAVA_OPTS_XSS:-512k}
-XX:+UseG1GC
Expand Down
45 changes: 44 additions & 1 deletion docker-compose-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ services:
AUCTION_HISTORY_CRON: "${AUCTION_HISTORY_CRON:-0 0 * * * *}"
STATISTICS_PREVIOUS_DAY_CRON: "${STATISTICS_PREVIOUS_DAY_CRON:-0 0 * * * *}"

# === Elasticsearch Configuration ===
ELASTICSEARCH_ENABLED: ${ELASTICSEARCH_ENABLED:-true}
ELASTICSEARCH_INDEX_ENABLED: ${ELASTICSEARCH_INDEX_ENABLED:-true}
SPRING_ELASTICSEARCH_URIS: http://elasticsearch:9200

# === JVM Configuration (Local - 경량 개발용) ===
# Heap: 256m~512m, Non-Heap: 256m, Total: ~768m
JAVA_OPTS: >-
Expand Down Expand Up @@ -85,10 +90,12 @@ services:
- app-network
- my-network # MySQL 컨테이너와 통신을 위해 추가

# MySQL이 준비될 때까지 대기
# MySQL, Elasticsearch가 준비될 때까지 대기
depends_on:
mysql:
condition: service_healthy
elasticsearch:
condition: service_healthy

# === Health Check (Local - 표준) ===
healthcheck:
Expand Down Expand Up @@ -144,6 +151,41 @@ services:
- --default-time-zone=+09:00 # MySQL 레벨 타임존 설정
- --explicit_defaults_for_timestamp=1 # TIMESTAMP 기본값 명시 허용

# Elasticsearch
# Nori 플러그인 사용 시: build 섹션 주석 해제, image 주석 처리
# build:
# context: ./docker/elasticsearch
# dockerfile: Dockerfile
# image: open-api-batch-elasticsearch:local
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
container_name: open-api-batch-elasticsearch
restart: unless-stopped
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- cluster.name=devnogi-es-cluster
ports:
- "${ELASTICSEARCH_PORT:-9200}:9200"
- "${ELASTICSEARCH_TRANSPORT_PORT:-9300}:9300"
volumes:
- elasticsearch_data:/usr/share/elasticsearch/data
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health | grep -q '\"status\":\"green\"\\|\"status\":\"yellow\"'"]
interval: 10s
timeout: 10s
retries: 10
start_period: 60s
deploy:
resources:
limits:
memory: 1g
reservations:
memory: 512m

# === Autoheal (Local - 표준) ===
# unhealthy 컨테이너 자동 재시작 서비스
autoheal:
Expand All @@ -168,6 +210,7 @@ services:

volumes:
mysql_data:
elasticsearch_data:
app-logs:
driver: local

Expand Down
Loading