> For the complete documentation index, see [llms.txt](https://kopens.gitbook.io/plantpulse-platform/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kopens.gitbook.io/plantpulse-platform/admin/properties-reference.md).

# 프로퍼티 레퍼런스

## 개요

PlantPulse 의 모든 설정은 두 단계로 관리됩니다.

```mermaid
flowchart LR
  ENV[plantpulse-startup/env.sh<br/>전역 단일 소스]
  TPL[template/*.template<br/>placeholder 가 포함된 템플릿]
  PROPS[모듈별 .properties / .xml / .yml<br/>실제 설정 파일]
  RUN[모듈 런타임]

  ENV -->|configure.sh<br/>치환| TPL
  TPL --> PROPS
  PROPS --> RUN
```

| 단계        | 위치                                  | 변경 방법                                                 |
| --------- | ----------------------------------- | ----------------------------------------------------- |
| **전역 설정** | `plantpulse-startup/env.sh`         | 직접 편집 후 `configure.sh && restart.sh`                  |
| **모듈 설정** | 각 모듈의 `conf/` 또는 `WEB-INF/classes/` | 직접 편집 후 해당 모듈 재시작 (env 와 동기화 깨질 수 있음 — 가급적 env.sh 경유) |

> **권장**: 운영 환경에서는 **env.sh 만 편집**하고 `configure.sh` 가 모든 모듈 설정을 일괄 생성하도록 하세요. 모듈 설정을 직접 편집하면 다음 `configure.sh` 실행 시 덮어쓰여집니다.

## env.sh 변수 인덱스

`plantpulse-startup/env.sh` 의 변수가 어느 모듈의 어느 설정 파일로 흘러가는지 한눈에 보여줍니다.

| 카테고리             | env.sh 변수                                                                                                                | 적용 위치                                                   |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------- |
| **Platform**     | `PP_HOME`, `PP_MODE`, `PP_SCHEME`, `PP_OPTIONS`                                                                          | 전 모듈                                                    |
| **Network**      | `PP_HOST_IP`, `PP_SERVICE_IP`, `PP_MASTER_IP`, `PP_PUBLIC_IP`                                                            | 전 모듈                                                    |
| **Data**         | `PP_DATA_DIR`, `PP_TEMP_DIR`, `PP_BACKUP_DIR`, `PP_DATA_DISK_NAME`                                                       | storage, warehouse, backup                              |
| **Cluster**      | `PP_CLUSTER_CORES`, `PP_CLUSTER_MEMORY_BY_CORE`                                                                          | analytics (Spark)                                       |
| **Locale**       | `PP_LANG`, `PP_TZ`, `JAVA_TOOL_OPTIONS`                                                                                  | 전 JVM                                                   |
| **Valkey**       | `PP_REDIS_HOST/PORT/USER/PASSWORD`                                                                                       | server, batch, cep, data-gateway                        |
| **PostgreSQL**   | `PP_POSTGRES_HOST/PORT`, `PP_PG_USER/PASSWORD`, `PP_DB_NAME`                                                             | server, batch, hive, kestra, temporal                   |
| **Cassandra**    | `PP_CASSANDRA_HOST/PORT/USER/PASSWORD`, `PP_KEYSPACE`, `PP_STORAGE_PORT`                                                 | server, batch, cep, data-gateway, timeseries, warehouse |
| **MinIO**        | `PP_MINIO_HOST/PORT/USER/PASSWORD/BUCKET`                                                                                | warehouse, server (첨부)                                  |
| **Kafka**        | `PP_KAFKA_HOST/PORT/TLS_PORT/CONTROLLER_PORT`, `PP_TOPIC_PREFIX`, `PP_MQ_USER/PASSWORD`, `PP_KAFKA_BROKER_USER/PASSWORD` | messaging, server, cep, timeseries                      |
| **MQTT**         | `PP_MQTT_HOST/PORT/TLS_PORT`                                                                                             | messaging, server, plugin                               |
| **STOMP**        | `PP_STOMP_HOST/PORT/TLS_PORT`                                                                                            | messaging, server                                       |
| **TSE**          | `PP_TSE_HOST/PORT/TLS_PORT/PROTOCOL/USER/PASSWORD`                                                                       | timeseries, server                                      |
| **Hive**         | `PP_HIVE_HOST/PORT/USER/PASSWORD`, `PP_HIVE_DB`                                                                          | analytics                                               |
| **Spark**        | `PP_SPARK_HOST/PORT`                                                                                                     | analytics, warehouse                                    |
| **Kyuubi**       | `PP_KYUUBI_PORT`                                                                                                         | analytics, sql                                          |
| **Gravitino**    | `PP_GRAVITINO_USER/PASSWORD`                                                                                             | analytics                                               |
| **Temporal**     | `PP_TEMPORAL_HOST/PORT/USER/PASSWORD`                                                                                    | workflow                                                |
| **Kestra**       | `PP_KESTRA_HOST/PORT`, `PP_KESTRA_DB_USER/PASSWORD`, `PP_KESTRA_ADMIN_EMAIL/PASSWORD`                                    | workflow                                                |
| **Grafana**      | `PP_GRAFANA_PORT`, `PP_GRAFANA_ADMIN_USER/PASSWORD`                                                                      | timeseries                                              |
| **Data Gateway** | `PP_DATA_GATEWAY_HOST/PORT`, `PP_DATA_GATEWAY_API_KEY`                                                                   | data-gateway, server                                    |
| **CEP**          | `PP_CEP_HOST/PORT/PROTOCOL`, `PP_CEP_API_KEY`                                                                            | cep, server                                             |
| **OPC-UA**       | `PP_OPCUA_PORT/TCP_PORT/TLS_PORT`                                                                                        | plugin                                                  |
| **AAS**          | `PP_AAS_SERVER_PORT`, `PP_AAS_REGISTRY_PORT`, `PP_AAS_V3_PORT`                                                           | plugin                                                  |
| **SMTP**         | `PP_MAIL_SMTP_PORT/AUTH/USER/PASSWORD/STARTTLS`                                                                          | server (mail)                                           |
| **WebSocket**    | `PP_WEBSOCKET_PORT/PORT_SSL/USER/SERVER_PASSWORD`                                                                        | server                                                  |
| **TLS**          | `PP_TLS_ENABLED/CERT_DIR/DOMAIN/KEYSTORE_PASSWORD/SAN_DNS/SAN_IPS/NODE_NAMES/VALID_DAYS/EC_GROUP/SIGALG/OPCUA_APP_URI`   | prepare-ssl.sh, 전 TLS 모듈                                |
| **API Keys**     | `PP_API_KEY`, `PP_FLOW_WEBHOOK_API_KEY`, `PP_DATA_GATEWAY_API_KEY`, `PP_CEP_API_KEY`                                     | 인증 모듈                                                   |
| **AI Gateway**   | `PP_AI_OPENAI_ENABLED/BASE_URL/API_KEY/MODEL`                                                                            | server (AI)                                             |

### env.sh 변경 표준 절차

```bash
cd /opt/kopens/plantpulse-platform/plantpulse-startup

# 1. env.sh 편집
vi env.sh

# 2. 검증 (선택)
./env-validate.sh

# 3. 템플릿 적용
./configure.sh

# 4. 재시작 (전체 또는 영향받는 모듈만)
./restart.sh                        # 전체 안전
./restart-<module>.sh               # 모듈만

# 5. 검증
./status.sh
```

***

## 모듈별 프로퍼티 (자동 생성됨)

아래 모듈별 프로퍼티는 `template/` 의 템플릿에서 `env.sh` 값을 치환해 생성됩니다. 직접 편집할 수도 있지만, 다음 `configure.sh` 실행 시 덮어쓰여지므로 **운영 환경에서는 env.sh 만 편집**하시기 바랍니다.

프로퍼티 파일은 `plantpulse-server/app/plantpulse-server-web/WEB-INF/classes/` 디렉토리에 위치하며, 변경 후 서버를 재시작해야 적용됩니다.

> **안내:** 기본값은 대부분의 환경에서 적절하게 동작하도록 설정되어 있습니다. 특별한 요구사항이 없다면 기본값을 유지하시는 것을 권장합니다.

***

## application.properties <a href="#application-properties" id="application-properties"></a>

경로: `WEB-INF/classes/application.properties`

플랫폼의 기본 동작, UI, 알람, 스토리지, API 관련 설정을 관리합니다.

### 테마 및 홈페이지 <a href="#theme" id="theme"></a>

| 프로퍼티              | 기본값                   | 설명                          |
| ----------------- | --------------------- | --------------------------- |
| `app.theme`       | `default`             | UI 테마 (default, dark, blue) |
| `app.homepage`    | `/summary/index`      | 로그인 후 이동할 페이지 경로            |
| `app.title`       | `PlantPulse`          | 브라우저 탭에 표시되는 타이틀            |
| `app.logo`        | `/images/logo.png`    | 로고 이미지 경로                   |
| `app.language`    | `ko`                  | 기본 언어 (ko, en, ja, zh)      |
| `app.timezone`    | `Asia/Seoul`          | 서버 타임존                      |
| `app.date.format` | `yyyy-MM-dd HH:mm:ss` | 날짜 표시 형식                    |

### 알람 <a href="#alarm" id="alarm"></a>

| 프로퍼티                            | 기본값    | 설명             |
| ------------------------------- | ------ | -------------- |
| `app.alarm.enable`              | `true` | 알람 기능 활성화 여부   |
| `app.alarm.refresh.interval`    | `5000` | 알람 갱신 주기 (ms)  |
| `app.alarm.max.count`           | `1000` | 알람 목록 최대 표시 건수 |
| `app.alarm.sound.enable`        | `true` | 알람 사운드 활성화     |
| `app.alarm.notification.enable` | `true` | 브라우저 알림 활성화    |

### 쿼리 <a href="#query" id="query"></a>

| 프로퍼티                        | 기본값      | 설명                    |
| --------------------------- | -------- | --------------------- |
| `app.query.max.rows`        | `10000`  | 쿼리 결과 최대 행 수          |
| `app.query.timeout`         | `30000`  | 쿼리 타임아웃 (ms)          |
| `app.query.export.max.rows` | `100000` | Excel/CSV 내보내기 최대 행 수 |
| `app.query.page.size`       | `20`     | 페이지당 표시 행 수           |

### 스토리지 <a href="#app-storage" id="app-storage"></a>

| 프로퍼티                          | 기본값            | 설명           |
| ----------------------------- | -------------- | ------------ |
| `app.storage.upload.path`     | `/data/upload` | 파일 업로드 경로    |
| `app.storage.upload.max.size` | `100MB`        | 업로드 파일 최대 크기 |
| `app.storage.temp.path`       | `/data/temp`   | 임시 파일 경로     |
| `app.storage.backup.path`     | `/data/backup` | 백업 파일 경로     |

### 태그 알람 메시지 <a href="#tag-alarm-message" id="tag-alarm-message"></a>

| 프로퍼티                             | 기본값      | 설명         |
| -------------------------------- | -------- | ---------- |
| `app.tag.alarm.message.high`     | `상한 초과`  | 상한 알람 메시지  |
| `app.tag.alarm.message.low`      | `하한 미만`  | 하한 알람 메시지  |
| `app.tag.alarm.message.highhigh` | `상상한 초과` | 상상한 알람 메시지 |
| `app.tag.alarm.message.lowlow`   | `하하한 미만` | 하하한 알람 메시지 |
| `app.tag.alarm.message.deadband` | `불감대 알람` | 불감대 알람 메시지 |

### OPC <a href="#opc" id="opc"></a>

| 프로퍼티                          | 기본값    | 설명               |
| ----------------------------- | ------ | ---------------- |
| `app.opc.polling.interval`    | `1000` | OPC 폴링 주기 (ms)   |
| `app.opc.reconnect.interval`  | `5000` | OPC 재연결 주기 (ms)  |
| `app.opc.reconnect.max.retry` | `10`   | 최대 재연결 시도 횟수     |
| `app.opc.timeout`             | `5000` | OPC 연결 타임아웃 (ms) |
| `app.opc.subscription.enable` | `true` | OPC 구독 모드 활성화    |

### 타이머 <a href="#timer" id="timer"></a>

| 프로퍼티                        | 기본값       | 설명                 |
| --------------------------- | --------- | ------------------ |
| `app.timer.asset.sync`      | `60000`   | 에셋 동기화 주기 (ms)     |
| `app.timer.cache.refresh`   | `300000`  | 캐시 갱신 주기 (ms), 5분  |
| `app.timer.session.cleanup` | `600000`  | 세션 정리 주기 (ms), 10분 |
| `app.timer.statistics`      | `3600000` | 통계 수집 주기 (ms), 1시간 |

### 진단 <a href="#diagnostic" id="diagnostic"></a>

| 프로퍼티                          | 기본값     | 설명                |
| ----------------------------- | ------- | ----------------- |
| `app.diagnostic.enable`       | `true`  | 진단 기능 활성화         |
| `app.diagnostic.interval`     | `60000` | 진단 실행 주기 (ms), 1분 |
| `app.diagnostic.history.days` | `30`    | 진단 이력 보존 기간 (일)   |

### API <a href="#api" id="api"></a>

| 프로퍼티                   | 기본값        | 설명                      |
| ---------------------- | ---------- | ----------------------- |
| `app.api.enable`       | `true`     | REST API 활성화            |
| `app.api.token.expire` | `86400000` | API 토큰 유효 시간 (ms), 24시간 |
| `app.api.rate.limit`   | `1000`     | 초당 API 호출 제한 횟수         |
| `app.api.cors.enable`  | `false`    | CORS 허용 여부              |
| `app.api.cors.origins` | `*`        | CORS 허용 도메인             |

***

## engine.properties <a href="#engine-properties" id="engine-properties"></a>

경로: `WEB-INF/classes/engine.properties`

데이터 처리 엔진의 핵심 동작을 제어하는 설정입니다.

### 비동기 처리 <a href="#async" id="async"></a>

| 프로퍼티                       | 기본값      | 설명                           |
| -------------------------- | -------- | ---------------------------- |
| `engine.async.parallelism` | `256`    | 비동기 병렬 처리 수. 범위: 16\~1024    |
| `engine.async.queue.size`  | `100000` | 비동기 큐 크기. 범위: 10000\~1000000 |
| `engine.async.timeout`     | `30000`  | 비동기 작업 타임아웃 (ms)             |

### 파이프라인 <a href="#pipeline" id="pipeline"></a>

| 프로퍼티                             | 기본값       | 설명                                     |
| -------------------------------- | --------- | -------------------------------------- |
| `engine.pipeline.threads`        | `36`      | 파이프라인 워커 스레드 수. 범위: 4\~128             |
| `engine.pipeline.ratelimit`      | `40000`   | 초당 최대 처리 메시지 수 (MPS). 범위: 1000\~500000 |
| `engine.pipeline.queue.size`     | `1200000` | 파이프라인 큐 최대 크기. 범위: 100000\~10000000    |
| `engine.pipeline.queue.o3.delay` | `50`      | Out-of-Order 지연 처리 (ms). 범위: 0\~1000   |
| `engine.pipeline.task.mode`      | `SINGLE`  | 파이프라인 태스크 모드 (SINGLE, MULTI)           |
| `engine.pipeline.batch.size`     | `1000`    | 배치 처리 크기. 범위: 100\~10000               |

### NTP <a href="#ntp" id="ntp"></a>

| 프로퍼티                  | 기본값            | 설명                   |
| --------------------- | -------------- | -------------------- |
| `engine.ntp.enable`   | `false`        | NTP 동기화 활성화          |
| `engine.ntp.server`   | `pool.ntp.org` | NTP 서버 주소            |
| `engine.ntp.interval` | `3600000`      | NTP 동기화 주기 (ms), 1시간 |

### 스트림 <a href="#stream" id="stream"></a>

| 프로퍼티                                               | 기본값          | 설명                                   |
| -------------------------------------------------- | ------------ | ------------------------------------ |
| `engine.stream.processor`                          | `DIRECT`     | 스트림 처리 모드 (DIRECT, KAFKA, MEMORY)    |
| `engine.streaming.messaging.warning.ms`            | `5000`       | 메시지 지연 경고 기준 (ms)                    |
| `engine.streaming.messaging.timeout.ms`            | `10000`      | 메시지 타임아웃 (ms)                        |
| `engine.streaming.messaging.timeout.store.type`    | `FILE_QUEUE` | 타임아웃 메시지 저장 방식 (FILE\_QUEUE, MEMORY) |
| `engine.streaming.messaging.timeout.recovery.type` | `DB`         | 타임아웃 복구 방식 (DB, FILE)                |

### CEP <a href="#cep" id="cep"></a>

| 프로퍼티                    | 기본값         | 설명           |
| ----------------------- | ----------- | ------------ |
| `engine.cep.enable`     | `true`      | CEP 엔진 활성화   |
| `engine.cep.host`       | `127.0.0.1` | CEP 서버 호스트   |
| `engine.cep.port`       | `7400`      | CEP 서버 포트    |
| `engine.cep.threads`    | `8`         | CEP 처리 스레드 수 |
| `engine.cep.queue.size` | `100000`    | CEP 이벤트 큐 크기 |

### 타임아웃 <a href="#timeout" id="timeout"></a>

| 프로퍼티                        | 기본값     | 설명           |
| --------------------------- | ------- | ------------ |
| `engine.timeout.connection` | `10000` | 연결 타임아웃 (ms) |
| `engine.timeout.read`       | `30000` | 읽기 타임아웃 (ms) |
| `engine.timeout.write`      | `30000` | 쓰기 타임아웃 (ms) |

### 작업 스레드 <a href="#job-thread" id="job-thread"></a>

| 프로퍼티                      | 기본값  | 설명                      |
| ------------------------- | ---- | ----------------------- |
| `engine.job.thread.asset` | `8`  | 에셋 작업 스레드 수. 범위: 1\~32  |
| `engine.job.thread.point` | `8`  | 포인트 작업 스레드 수. 범위: 1\~32 |
| `engine.job.thread.alarm` | `4`  | 알람 작업 스레드 수. 범위: 1\~16  |
| `engine.job.thread.event` | `4`  | 이벤트 작업 스레드 수. 범위: 1\~16 |
| `engine.job.thread.oee`   | `10` | OEE 계산 스레드 수. 범위: 1\~32 |
| `engine.job.thread.ram`   | `5`  | RAM 계산 스레드 수. 범위: 1\~16 |
| `engine.job.thread.ems`   | `5`  | EMS 계산 스레드 수. 범위: 1\~16 |

### OEE / 생산 분석 <a href="#oee" id="oee"></a>

| 프로퍼티                     | 기본값    | 설명                                                      |
| ------------------------ | ------ | ------------------------------------------------------- |
| `engine.oee.debounce.ms` | `100`  | 설비 상태 변경 디바운싱 (ms). 범위: 50\~500. 빠르게 연속되는 상태 변경을 하나로 합침 |
| `engine.plugin.handlers` | *(동적)* | 플러그인 핸들러 동적 등록. OEE/RAM/EMS 핸들러를 런타임에 추가/제거 가능          |

### DDS (데이터 분배 서비스) <a href="#dds" id="dds"></a>

| 프로퍼티                      | 기본값          | 설명         |
| ------------------------- | ------------ | ---------- |
| `engine.dds.enable`       | `false`      | DDS 활성화    |
| `engine.dds.domain.id`    | `0`          | DDS 도메인 ID |
| `engine.dds.topic.prefix` | `plantpulse` | DDS 토픽 접두사 |

### 이상 감지 <a href="#anomaly-detection" id="anomaly-detection"></a>

| 프로퍼티                         | 기본값      | 설명                                        |
| ---------------------------- | -------- | ----------------------------------------- |
| `engine.anomaly.enable`      | `false`  | 이상 감지 활성화                                 |
| `engine.anomaly.model`       | `ZSCORE` | 이상 감지 모델 (ZSCORE, IQR, ISOLATION\_FOREST) |
| `engine.anomaly.threshold`   | `3.0`    | 이상 감지 임계값                                 |
| `engine.anomaly.window.size` | `100`    | 분석 윈도우 크기 (데이터 포인트 수)                     |

### 데이터플로우 <a href="#dataflow" id="dataflow"></a>

| 프로퍼티                          | 기본값     | 설명              |
| ----------------------------- | ------- | --------------- |
| `engine.dataflow.enable`      | `true`  | 데이터플로우 엔진 활성화   |
| `engine.dataflow.threads`     | `4`     | 데이터플로우 처리 스레드 수 |
| `engine.dataflow.buffer.size` | `10000` | 데이터플로우 버퍼 크기    |

***

## storage.properties <a href="#storage-properties" id="storage-properties"></a>

경로: `WEB-INF/classes/storage.properties`

데이터베이스 및 스토리지 연결 설정을 관리합니다.

### PostgreSQL <a href="#postgresql" id="postgresql"></a>

| 프로퍼티                              | 기본값          | 설명               |
| --------------------------------- | ------------ | ---------------- |
| `storage.postgresql.host`         | `127.0.0.1`  | PostgreSQL 호스트   |
| `storage.postgresql.port`         | `5432`       | PostgreSQL 포트    |
| `storage.postgresql.database`     | `plantpulse` | 데이터베이스 이름        |
| `storage.postgresql.username`     | `plantpulse` | 접속 사용자           |
| `storage.postgresql.password`     | `plantpulse` | 접속 비밀번호          |
| `storage.postgresql.pool.min`     | `5`          | 커넥션 풀 최소 크기      |
| `storage.postgresql.pool.max`     | `20`         | 커넥션 풀 최대 크기      |
| `storage.postgresql.pool.timeout` | `30000`      | 커넥션 획득 타임아웃 (ms) |

### Redis (Valkey) <a href="#redis" id="redis"></a>

| 프로퍼티                     | 기본값         | 설명                      |
| ------------------------ | ----------- | ----------------------- |
| `storage.redis.host`     | `127.0.0.1` | Redis 호스트               |
| `storage.redis.port`     | `6379`      | Redis 포트                |
| `storage.redis.password` | *(빈 값)*     | Redis 비밀번호              |
| `storage.redis.database` | `0`         | Redis 데이터베이스 번호 (0\~15) |
| `storage.redis.pool.max` | `20`        | 커넥션 풀 최대 크기             |
| `storage.redis.timeout`  | `5000`      | 연결 타임아웃 (ms)            |

### TSE (시계열 엔진) <a href="#tse" id="tse"></a>

| 프로퍼티                   | 기본값         | 설명               |
| ---------------------- | ----------- | ---------------- |
| `storage.tse.host`     | `127.0.0.1` | TSE 호스트          |
| `storage.tse.port`     | `7800`      | TSE 포트           |
| `storage.tse.timeout`  | `10000`     | TSE 연결 타임아웃 (ms) |
| `storage.tse.pool.max` | `10`        | 커넥션 풀 최대 크기      |

### Cassandra <a href="#cassandra" id="cassandra"></a>

| 프로퍼티                                   | 기본값          | 설명                            |
| -------------------------------------- | ------------ | ----------------------------- |
| `storage.cassandra.host`               | `127.0.0.1`  | Cassandra 호스트 (쉼표로 다중 호스트 지정) |
| `storage.cassandra.port`               | `9042`       | Cassandra 포트                  |
| `storage.cassandra.keyspace`           | `plantpulse` | 키스페이스 이름                      |
| `storage.cassandra.username`           | `cassandra`  | 접속 사용자                        |
| `storage.cassandra.password`           | `cassandra`  | 접속 비밀번호                       |
| `storage.cassandra.pool.local`         | `4`          | 로컬 노드 커넥션 수                   |
| `storage.cassandra.pool.remote`        | `2`          | 원격 노드 커넥션 수                   |
| `storage.cassandra.consistency`        | `LOCAL_ONE`  | 읽기/쓰기 일관성 수준                  |
| `storage.cassandra.replication.factor` | `1`          | 복제 팩터. 클러스터 환경에서는 3 권장        |

### TTL (데이터 보존 기간) <a href="#ttl" id="ttl"></a>

| 프로퍼티                     | 기본값   | 설명                |
| ------------------------ | ----- | ----------------- |
| `storage.ttl.realtime`   | `7`   | 실시간 데이터 보존 기간 (일) |
| `storage.ttl.history`    | `365` | 이력 데이터 보존 기간 (일)  |
| `storage.ttl.alarm`      | `90`  | 알람 데이터 보존 기간 (일)  |
| `storage.ttl.event`      | `90`  | 이벤트 데이터 보존 기간 (일) |
| `storage.ttl.audit`      | `365` | 감사 로그 보존 기간 (일)   |
| `storage.ttl.diagnostic` | `30`  | 진단 데이터 보존 기간 (일)  |

### 메타스토어 <a href="#metastore" id="metastore"></a>

| 프로퍼티                             | 기본값          | 설명                        |
| -------------------------------- | ------------ | ------------------------- |
| `storage.metastore.type`         | `POSTGRESQL` | 메타스토어 유형 (POSTGRESQL, H2) |
| `storage.metastore.cache.enable` | `true`       | 메타스토어 캐시 활성화              |
| `storage.metastore.cache.ttl`    | `300`        | 캐시 TTL (초), 5분            |

### 캐시 <a href="#cache" id="cache"></a>

| 프로퍼티                     | 기본값     | 설명                         |
| ------------------------ | ------- | -------------------------- |
| `storage.cache.type`     | `REDIS` | 캐시 유형 (REDIS, LOCAL, NONE) |
| `storage.cache.ttl`      | `600`   | 기본 캐시 TTL (초), 10분         |
| `storage.cache.max.size` | `10000` | 로컬 캐시 최대 항목 수              |

### Compaction <a href="#compaction" id="compaction"></a>

| 프로퍼티                          | 기본값                            | 설명                                  |
| ----------------------------- | ------------------------------ | ----------------------------------- |
| `storage.compaction.enable`   | `true`                         | 자동 Compaction 활성화                   |
| `storage.compaction.schedule` | `0 0 2 * * ?`                  | Compaction 스케줄 (Cron 표현식), 매일 02:00 |
| `storage.compaction.strategy` | `TimeWindowCompactionStrategy` | Compaction 전략                       |

### 압축 <a href="#compression" id="compression"></a>

| 프로퍼티                             | 기본값    | 설명                                |
| -------------------------------- | ------ | --------------------------------- |
| `storage.compression.enable`     | `true` | 데이터 압축 활성화                        |
| `storage.compression.algorithm`  | `LZ4`  | 압축 알고리즘 (LZ4, SNAPPY, ZSTD, NONE) |
| `storage.compression.chunk.size` | `64KB` | 압축 청크 크기                          |

***

## mq.properties <a href="#mq-properties" id="mq-properties"></a>

경로: `WEB-INF/classes/mq.properties`

메시지 큐 연결 및 토픽 설정을 관리합니다.

### MQ 연결 <a href="#mq-connection" id="mq-connection"></a>

| 프로퍼티                         | 기본값                | 설명                                 |
| ---------------------------- | ------------------ | ---------------------------------- |
| `mq.type`                    | `KAFKA`            | MQ 유형 (KAFKA, MQTT, BOTH)          |
| `mq.kafka.bootstrap.servers` | `127.0.0.1:9092`   | Kafka 브로커 주소 (쉼표로 다중 지정)           |
| `mq.kafka.group.id`          | `plantpulse-group` | Kafka 컨슈머 그룹 ID                    |
| `mq.kafka.auto.offset.reset` | `latest`           | Kafka 오프셋 리셋 정책 (latest, earliest) |
| `mq.kafka.max.poll.records`  | `500`              | Kafka 한 번에 가져올 최대 레코드 수            |
| `mq.mqtt.host`               | `127.0.0.1`        | MQTT 브로커 호스트                       |
| `mq.mqtt.port`               | `1883`             | MQTT 브로커 포트                        |
| `mq.mqtt.username`           | *(빈 값)*            | MQTT 사용자 이름                        |
| `mq.mqtt.password`           | *(빈 값)*            | MQTT 비밀번호                          |
| `mq.mqtt.qos`                | `1`                | MQTT QoS 수준 (0, 1, 2)              |

### Kafka 토픽 <a href="#kafka-topics" id="kafka-topics"></a>

| 프로퍼티                                | 기본값                   | 설명                                                        |
| ----------------------------------- | --------------------- | --------------------------------------------------------- |
| `mq.kafka.topic.realtime`           | `plantpulse-realtime` | 실시간 데이터 토픽                                                |
| `mq.kafka.topic.alarm`              | `plantpulse-alarm`    | 알람 토픽                                                     |
| `mq.kafka.topic.event`              | `plantpulse-event`    | 이벤트 토픽                                                    |
| `mq.kafka.topic.command`            | `plantpulse-command`  | 명령 토픽                                                     |
| `mq.kafka.topic.production.oee`     | `pp-production-oee`   | OEE 생산 이벤트 토픽 (설비 상태, 양품/불량, 오더 시작/종료). asset\_id 기준 파티셔닝 |
| `mq.kafka.topic.production.ram`     | `pp-production-ram`   | RAM 생산 이벤트 토픽 (고장 시작/종료). asset\_id 기준 파티셔닝               |
| `mq.kafka.topic.production.ems`     | `pp-production-ems`   | EMS 생산 이벤트 토픽 (에너지 소비). asset\_id 기준 파티셔닝                 |
| `mq.kafka.topic.partitions`         | `3`                   | 토픽 파티션 수                                                  |
| `mq.kafka.topic.replication.factor` | `1`                   | 토픽 복제 팩터. 클러스터에서는 3 권장                                    |

### MQTT 토픽 <a href="#mqtt-topics" id="mqtt-topics"></a>

| 프로퍼티                     | 기본값                   | 설명         |
| ------------------------ | --------------------- | ---------- |
| `mq.mqtt.topic.realtime` | `plantpulse/realtime` | 실시간 데이터 토픽 |
| `mq.mqtt.topic.alarm`    | `plantpulse/alarm`    | 알람 토픽      |
| `mq.mqtt.topic.event`    | `plantpulse/event`    | 이벤트 토픽     |
| `mq.mqtt.topic.command`  | `plantpulse/command`  | 명령 토픽      |

***

## websocket.properties <a href="#websocket-properties" id="websocket-properties"></a>

경로: `WEB-INF/classes/websocket.properties`

실시간 UI 업데이트를 위한 WebSocket 설정입니다.

| 프로퍼티                           | 기본값      | 설명                      |
| ------------------------------ | -------- | ----------------------- |
| `websocket.enable`             | `true`   | WebSocket 활성화           |
| `websocket.port`               | `8080`   | WebSocket 포트            |
| `websocket.path`               | `/ws`    | WebSocket 엔드포인트 경로      |
| `websocket.max.sessions`       | `100`    | 최대 동시 세션 수              |
| `websocket.idle.timeout`       | `300000` | 유휴 세션 타임아웃 (ms), 5분     |
| `websocket.max.message.size`   | `65536`  | 최대 메시지 크기 (bytes), 64KB |
| `websocket.heartbeat.interval` | `30000`  | 하트비트 주기 (ms), 30초       |

***

## mail.properties <a href="#mail-properties" id="mail-properties"></a>

경로: `WEB-INF/classes/mail.properties`

이메일 발송 및 SMS 알림 설정입니다.

### SMTP <a href="#smtp" id="smtp"></a>

| 프로퍼티                        | 기본값                     | 설명            |
| --------------------------- | ----------------------- | ------------- |
| `mail.smtp.host`            | `smtp.gmail.com`        | SMTP 서버 호스트   |
| `mail.smtp.port`            | `587`                   | SMTP 서버 포트    |
| `mail.smtp.username`        | *(빈 값)*                 | SMTP 인증 사용자   |
| `mail.smtp.password`        | *(빈 값)*                 | SMTP 인증 비밀번호  |
| `mail.smtp.auth`            | `true`                  | SMTP 인증 사용 여부 |
| `mail.smtp.starttls.enable` | `true`                  | TLS 활성화       |
| `mail.smtp.from`            | `noreply@plantpulse.io` | 발신자 이메일 주소    |
| `mail.smtp.from.name`       | `PlantPulse`            | 발신자 이름        |

### SMS <a href="#sms" id="sms"></a>

| 프로퍼티                  | 기본값      | 설명                              |
| --------------------- | -------- | ------------------------------- |
| `mail.sms.enable`     | `false`  | SMS 알림 활성화                      |
| `mail.sms.provider`   | `TWILIO` | SMS 제공자 (TWILIO, ALIGO, CUSTOM) |
| `mail.sms.api.key`    | *(빈 값)*  | SMS API 키                       |
| `mail.sms.api.secret` | *(빈 값)*  | SMS API 시크릿                     |
| `mail.sms.from`       | *(빈 값)*  | SMS 발신 번호                       |

### 진단 알림 <a href="#diagnostic-notification" id="diagnostic-notification"></a>

| 프로퍼티                         | 기본값       | 설명                       |
| ---------------------------- | --------- | ------------------------ |
| `mail.diagnostic.enable`     | `false`   | 진단 이메일 알림 활성화            |
| `mail.diagnostic.recipients` | *(빈 값)*   | 수신자 이메일 (쉼표 구분)          |
| `mail.diagnostic.interval`   | `3600000` | 진단 알림 최소 발송 간격 (ms), 1시간 |

***

## quartz.properties <a href="#quartz-properties" id="quartz-properties"></a>

경로: `WEB-INF/classes/quartz.properties`

스케줄러 및 배치 작업 설정입니다.

### 스케줄러 <a href="#scheduler" id="scheduler"></a>

| 프로퍼티                                   | 기본값                   | 설명           |
| -------------------------------------- | --------------------- | ------------ |
| `org.quartz.scheduler.instanceName`    | `PlantPulseScheduler` | 스케줄러 인스턴스 이름 |
| `org.quartz.scheduler.instanceId`      | `AUTO`                | 스케줄러 인스턴스 ID |
| `org.quartz.scheduler.skipUpdateCheck` | `true`                | 업데이트 확인 건너뛰기 |

### 스레드풀 <a href="#threadpool" id="threadpool"></a>

| 프로퍼티                                                                         | 기본값                | 설명                    |
| ---------------------------------------------------------------------------- | ------------------ | --------------------- |
| `org.quartz.threadPool.class`                                                | `SimpleThreadPool` | 스레드풀 클래스              |
| `org.quartz.threadPool.threadCount`                                          | `10`               | 스레드풀 스레드 수. 범위: 1\~50 |
| `org.quartz.threadPool.threadPriority`                                       | `5`                | 스레드 우선순위. 범위: 1\~10   |
| `org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread` | `true`             | 컨텍스트 클래스로더 상속         |

***

## 환경별 권장 설정 <a href="#environment-settings" id="environment-settings"></a>

환경에 따라 프로퍼티 값을 조정하는 것을 권장합니다.

### 개발 환경 <a href="#dev-settings" id="dev-settings"></a>

| 프로퍼티                                   | 권장값    | 설명             |
| -------------------------------------- | ------ | -------------- |
| `engine.pipeline.threads`              | `4`    | 최소 스레드로 리소스 절약 |
| `engine.pipeline.ratelimit`            | `1000` | 낮은 처리량         |
| `storage.postgresql.pool.max`          | `5`    | 최소 커넥션         |
| `storage.cassandra.replication.factor` | `1`    | 단일 노드          |
| `mq.kafka.topic.partitions`            | `1`    | 단일 파티션         |
| `app.api.rate.limit`                   | `100`  | 낮은 제한          |

### 스테이징 환경 <a href="#staging-settings" id="staging-settings"></a>

| 프로퍼티                                   | 권장값     | 설명        |
| -------------------------------------- | ------- | --------- |
| `engine.pipeline.threads`              | `16`    | 운영의 절반 수준 |
| `engine.pipeline.ratelimit`            | `10000` | 중간 처리량    |
| `storage.postgresql.pool.max`          | `10`    | 중간 커넥션    |
| `storage.cassandra.replication.factor` | `2`     | 이중 복제     |
| `mq.kafka.topic.partitions`            | `3`     | 3파티션      |

### 프로덕션 환경 <a href="#production-settings" id="production-settings"></a>

| 프로퍼티                                   | 권장값     | 설명            |
| -------------------------------------- | ------- | ------------- |
| `engine.pipeline.threads`              | `36`    | CPU 코어의 2\~4배 |
| `engine.pipeline.ratelimit`            | `40000` | 높은 처리량        |
| `engine.async.parallelism`             | `256`   | 높은 병렬도        |
| `storage.postgresql.pool.max`          | `20`    | 충분한 커넥션       |
| `storage.cassandra.replication.factor` | `3`     | 3중 복제         |
| `mq.kafka.topic.partitions`            | `6`     | 충분한 파티션       |
| `mq.kafka.topic.replication.factor`    | `3`     | 3중 복제         |

### 대규모 환경 (10만 포인트 이상) <a href="#large-scale-settings" id="large-scale-settings"></a>

| 프로퍼티                           | 권장값       | 설명            |
| ------------------------------ | --------- | ------------- |
| `engine.pipeline.threads`      | `64`      | CPU 코어의 4배 이상 |
| `engine.pipeline.ratelimit`    | `100000`  | 초고속 처리        |
| `engine.pipeline.queue.size`   | `3000000` | 대형 큐          |
| `engine.async.parallelism`     | `512`     | 최대 병렬도        |
| `storage.postgresql.pool.max`  | `50`      | 대형 커넥션 풀      |
| `storage.cassandra.pool.local` | `8`       | 높은 동시성        |
| `mq.kafka.topic.partitions`    | `12`      | 높은 파티션 수      |

***

## 보안 주의사항 <a href="#security-notes" id="security-notes"></a>

### 비밀번호가 포함된 프로퍼티 <a href="#password-properties" id="password-properties"></a>

다음 프로퍼티에는 비밀번호가 포함되어 있으므로 보안에 주의해 주세요.

| 프로퍼티                          | 파일                 |
| ----------------------------- | ------------------ |
| `storage.postgresql.password` | storage.properties |
| `storage.redis.password`      | storage.properties |
| `storage.cassandra.password`  | storage.properties |
| `mq.mqtt.password`            | mq.properties      |
| `mail.smtp.password`          | mail.properties    |
| `mail.sms.api.secret`         | mail.properties    |

### 권장 보안 조치 <a href="#recommended-security-measures" id="recommended-security-measures"></a>

* **파일 권한 제한:** 프로퍼티 파일의 읽기 권한을 서비스 실행 계정으로 제한해 주세요.

```bash
# 프로퍼티 파일 권한 설정
chmod 600 /opt/kopens/plantpulse-platform/plantpulse-server/app/plantpulse-server-web/WEB-INF/classes/*.properties
chown kopens:kopens /opt/kopens/plantpulse-platform/plantpulse-server/app/plantpulse-server-web/WEB-INF/classes/*.properties
```

* **환경 변수 사용:** 비밀번호를 프로퍼티 파일에 직접 기재하는 대신 환경 변수로 관리하는 것을 권장합니다.

```properties
# 환경 변수 참조 방식 (지원되는 경우)
storage.postgresql.password=${PP_DB_PASSWORD}
storage.redis.password=${PP_REDIS_PASSWORD}
```

* **버전 관리 제외:** 프로퍼티 파일을 Git 등 버전 관리 시스템에 포함하지 않도록 `.gitignore`에 추가해 주세요.
* **백업 시 암호화:** 프로퍼티 파일이 포함된 백업 파일은 암호화하여 보관해 주세요.
* **정기 변경:** 비밀번호를 정기적으로 변경하고, 변경 시 관련 프로퍼티 파일도 함께 업데이트해 주세요.
