feat: cicd pipeline
This commit is contained in:
36
.gitignore
vendored
36
.gitignore
vendored
@@ -35,3 +35,39 @@ out/
|
|||||||
|
|
||||||
### VS Code ###
|
### VS Code ###
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
|
### QueryDSL ###
|
||||||
|
/src/main/generated/
|
||||||
|
**/generated/
|
||||||
|
|
||||||
|
### Logs ###
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
*.log.*
|
||||||
|
|
||||||
|
### OS ###
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
### Environment ###
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
application-local.yml
|
||||||
|
application-secret.yml
|
||||||
|
|
||||||
|
### Docker (local testing) ###
|
||||||
|
.dockerignore
|
||||||
|
docker-compose.override.yml
|
||||||
|
|
||||||
|
### Temporary ###
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|||||||
15
Dockerfile-dev
Normal file
15
Dockerfile-dev
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Stage 1: Build stage (gradle build는 Jenkins에서 이미 수행)
|
||||||
|
FROM eclipse-temurin:21-jre-jammy
|
||||||
|
|
||||||
|
# 작업 디렉토리 설정
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# JAR 파일 복사 (Jenkins에서 빌드된 ROOT.jar)
|
||||||
|
COPY build/libs/ROOT.jar app.jar
|
||||||
|
|
||||||
|
# 포트 노출
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# 애플리케이션 실행
|
||||||
|
# dev 프로파일로 실행
|
||||||
|
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=dev", "app.jar"]
|
||||||
94
Jenkinsfile-dev
Normal file
94
Jenkinsfile-dev
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
tools {
|
||||||
|
jdk 'jdk21'
|
||||||
|
}
|
||||||
|
environment {
|
||||||
|
BRANCH = 'develop'
|
||||||
|
GIT_REPO = 'https://10.100.0.10:3210/dabeeo/kamco-dabeeo-backoffice.git'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Checkout') {
|
||||||
|
steps {
|
||||||
|
checkout([
|
||||||
|
$class: 'GitSCM',
|
||||||
|
branches: [[name: "${env.BRANCH}"]],
|
||||||
|
userRemoteConfigs: [[
|
||||||
|
url: "${env.GIT_REPO}",
|
||||||
|
credentialsId: 'jenkins-dev-token'
|
||||||
|
]]
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Get Commit Hash') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
env.COMMIT_HASH = sh(script: "git rev-parse --short HEAD", returnStdout: true).trim()
|
||||||
|
echo "Current commit hash: ${env.COMMIT_HASH}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Build') {
|
||||||
|
steps {
|
||||||
|
sh "./gradlew clean build -x test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Docker Build & Deploy') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
echo "Building Docker image with tag: ${env.COMMIT_HASH}"
|
||||||
|
|
||||||
|
// IMAGE_TAG 환경변수 설정 후 docker-compose로 빌드 및 배포
|
||||||
|
sh """
|
||||||
|
export IMAGE_TAG=${env.COMMIT_HASH}
|
||||||
|
|
||||||
|
# 기존 컨테이너 중지 및 제거
|
||||||
|
docker-compose -f docker-compose-dev.yml down || true
|
||||||
|
|
||||||
|
# 새 이미지 빌드
|
||||||
|
docker-compose -f docker-compose-dev.yml build
|
||||||
|
|
||||||
|
# latest 태그도 추가
|
||||||
|
docker tag kamco-changedetection-api:${env.COMMIT_HASH} kamco-changedetection-api:latest
|
||||||
|
|
||||||
|
# 컨테이너 시작
|
||||||
|
docker-compose -f docker-compose-dev.yml up -d
|
||||||
|
"""
|
||||||
|
|
||||||
|
// 헬스체크 대기
|
||||||
|
echo "Waiting for application to be ready..."
|
||||||
|
sh """
|
||||||
|
for i in {1..30}; do
|
||||||
|
if docker exec kamco-changedetection-api curl -f http://localhost:8080/monitor/health > /dev/null 2>&1; then
|
||||||
|
echo "✅ Application is healthy!"
|
||||||
|
docker-compose -f docker-compose-dev.yml ps
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "⏳ Waiting for application... (\$i/30)"
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
echo "⚠️ Warning: Health check timeout, checking container status..."
|
||||||
|
docker-compose -f docker-compose-dev.yml ps
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Cleanup Old Images') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
echo "Cleaning up old Docker images..."
|
||||||
|
sh """
|
||||||
|
# Keep latest 5 images, remove older ones
|
||||||
|
docker images kamco-changedetection-api --format "{{.ID}} {{.Tag}}" | \
|
||||||
|
grep -v latest | tail -n +6 | awk '{print \$1}' | xargs -r docker rmi || true
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
393
README.md
Normal file
393
README.md
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
# KAMCO Change Detection API
|
||||||
|
|
||||||
|
> KAMCO 변화 탐지 시스템을 위한 백엔드 API 서버
|
||||||
|
|
||||||
|
## 📋 프로젝트 소개
|
||||||
|
|
||||||
|
**kamco-change-detection-api**는 공간 데이터의 변화를 탐지하고 관리하기 위한 RESTful API 서버입니다.
|
||||||
|
JTS(Java Topology Suite)를 활용한 지오메트리 데이터 처리와 PostgreSQL을 통한 공간 데이터 저장을 지원합니다.
|
||||||
|
|
||||||
|
## 🛠️ 기술 스택
|
||||||
|
|
||||||
|
| Category | Technology |
|
||||||
|
|----------|------------|
|
||||||
|
| **Language** | Java 21 |
|
||||||
|
| **Framework** | Spring Boot 3.5.7 |
|
||||||
|
| **Database** | PostgreSQL (with PostGIS) |
|
||||||
|
| **ORM** | Spring Data JPA + Hibernate |
|
||||||
|
| **Query** | QueryDSL 5.0.0 (Jakarta) |
|
||||||
|
| **Geospatial** | JTS (Java Topology Suite) + GeoJSON |
|
||||||
|
| **Connection Pool** | HikariCP |
|
||||||
|
| **Build Tool** | Gradle 8.x |
|
||||||
|
| **Monitoring** | Spring Boot Actuator |
|
||||||
|
| **Container** | Docker + Docker Compose |
|
||||||
|
| **CI/CD** | Jenkins |
|
||||||
|
|
||||||
|
## 🚀 시작하기
|
||||||
|
|
||||||
|
### 필수 요구사항
|
||||||
|
|
||||||
|
- Java 21 (JDK 21)
|
||||||
|
- PostgreSQL 12+ (PostGIS 확장 필요)
|
||||||
|
- Gradle 8.x (또는 Gradle Wrapper 사용)
|
||||||
|
- Docker & Docker Compose (선택사항)
|
||||||
|
|
||||||
|
### 로컬 환경 설정
|
||||||
|
|
||||||
|
1. **저장소 클론**
|
||||||
|
```bash
|
||||||
|
git clone https://10.100.0.10:3210/dabeeo/kamco-dabeeo-backoffice.git
|
||||||
|
cd kamco-back
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **데이터베이스 설정**
|
||||||
|
|
||||||
|
PostgreSQL 데이터베이스를 준비하고 `src/main/resources/application-local.yml`을 생성:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
spring:
|
||||||
|
config:
|
||||||
|
activate:
|
||||||
|
on-profile: local
|
||||||
|
datasource:
|
||||||
|
url: jdbc:postgresql://localhost:5432/your_database
|
||||||
|
username: your_username
|
||||||
|
password: your_password
|
||||||
|
```
|
||||||
|
|
||||||
|
> **참고**: `application-local.yml`은 `.gitignore`에 포함되어 있어 Git에 커밋되지 않습니다.
|
||||||
|
|
||||||
|
3. **빌드 및 실행**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 빌드
|
||||||
|
./gradlew build
|
||||||
|
|
||||||
|
# 실행 (local 프로파일)
|
||||||
|
./gradlew bootRun
|
||||||
|
|
||||||
|
# 또는 JAR 파일로 실행
|
||||||
|
java -jar build/libs/ROOT.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
서버가 시작되면 http://localhost:8080 에서 접근 가능합니다.
|
||||||
|
|
||||||
|
## ⚙️ 프로파일 설정
|
||||||
|
|
||||||
|
애플리케이션은 환경별로 다른 설정을 사용합니다:
|
||||||
|
|
||||||
|
| 프로파일 | 환경 | 포트 | 설정 파일 |
|
||||||
|
|---------|------|------|-----------|
|
||||||
|
| `local` | 로컬 개발 | 8080 | `application.yml` (기본) |
|
||||||
|
| `dev` | 개발 서버 | 7100 | `application-dev.yml` |
|
||||||
|
| `prod` | 운영 서버 | 8080 | `application-prod.yml` |
|
||||||
|
|
||||||
|
### 프로파일 활성화
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 개발 환경으로 실행
|
||||||
|
./gradlew bootRun --args='--spring.profiles.active=dev'
|
||||||
|
|
||||||
|
# JAR 실행 시
|
||||||
|
java -jar build/libs/ROOT.jar --spring.profiles.active=dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🧪 테스트
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 전체 테스트 실행
|
||||||
|
./gradlew test
|
||||||
|
|
||||||
|
# 특정 테스트 클래스 실행
|
||||||
|
./gradlew test --tests com.kamco.cd.kamcoback.KamcoBackApplicationTests
|
||||||
|
|
||||||
|
# 테스트 리포트 확인
|
||||||
|
open build/reports/tests/test/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📦 빌드
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 전체 빌드 (테스트 포함)
|
||||||
|
./gradlew clean build
|
||||||
|
|
||||||
|
# 테스트 제외 빌드 (CI/CD에서 사용)
|
||||||
|
./gradlew clean build -x test
|
||||||
|
|
||||||
|
# JAR 파일만 생성
|
||||||
|
./gradlew bootJar
|
||||||
|
```
|
||||||
|
|
||||||
|
빌드된 JAR 파일: `build/libs/ROOT.jar`
|
||||||
|
|
||||||
|
## 🐳 Docker로 실행하기
|
||||||
|
|
||||||
|
### Docker Compose 사용 (권장)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 빌드 및 실행
|
||||||
|
docker-compose -f docker-compose-dev.yml up -d
|
||||||
|
|
||||||
|
# 로그 확인
|
||||||
|
docker-compose -f docker-compose-dev.yml logs -f
|
||||||
|
|
||||||
|
# 중지
|
||||||
|
docker-compose -f docker-compose-dev.yml down
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker만 사용
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 이미지 빌드
|
||||||
|
docker build -f Dockerfile-dev -t kamco-changedetection-api:latest .
|
||||||
|
|
||||||
|
# 컨테이너 실행
|
||||||
|
docker run -d \
|
||||||
|
--name kamco-changedetection-api \
|
||||||
|
-p 7100:8080 \
|
||||||
|
kamco-changedetection-api:latest
|
||||||
|
|
||||||
|
# 로그 확인
|
||||||
|
docker logs -f kamco-changedetection-api
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📡 API 및 모니터링
|
||||||
|
|
||||||
|
### Actuator 헬스체크
|
||||||
|
|
||||||
|
애플리케이션 상태를 확인할 수 있는 엔드포인트가 제공됩니다:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 헬스체크
|
||||||
|
curl http://localhost:8080/monitor/health
|
||||||
|
|
||||||
|
# 응답 예시
|
||||||
|
{
|
||||||
|
"status": "UP",
|
||||||
|
"components": {
|
||||||
|
"db": {
|
||||||
|
"status": "UP"
|
||||||
|
},
|
||||||
|
"diskSpace": {
|
||||||
|
"status": "UP"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Actuator 엔드포인트**:
|
||||||
|
- `/monitor/health` - 애플리케이션 헬스 체크
|
||||||
|
- `/monitor/health/readiness` - Kubernetes Readiness Probe
|
||||||
|
- `/monitor/health/liveness` - Kubernetes Liveness Probe
|
||||||
|
|
||||||
|
## 🏗️ 프로젝트 구조
|
||||||
|
|
||||||
|
```
|
||||||
|
kamco-back/
|
||||||
|
├── src/
|
||||||
|
│ ├── main/
|
||||||
|
│ │ ├── java/com/kamco/cd/kamcoback/
|
||||||
|
│ │ │ ├── KamcoBackApplication.java # 메인 애플리케이션
|
||||||
|
│ │ │ ├── config/ # 설정 클래스
|
||||||
|
│ │ │ │ ├── WebConfig.java # Jackson, CORS 설정
|
||||||
|
│ │ │ │ └── StartupLogger.java # 시작 로그
|
||||||
|
│ │ │ ├── common/ # 공통 유틸리티
|
||||||
|
│ │ │ │ └── utils/geometry/ # GeoJSON 직렬화
|
||||||
|
│ │ │ ├── domain/ # 도메인 엔티티 (예정)
|
||||||
|
│ │ │ ├── repository/ # 리포지토리 (예정)
|
||||||
|
│ │ │ ├── service/ # 비즈니스 로직 (예정)
|
||||||
|
│ │ │ └── controller/ # REST 컨트롤러 (예정)
|
||||||
|
│ │ └── resources/
|
||||||
|
│ │ ├── application.yml # 기본 설정 + local/prod
|
||||||
|
│ │ └── application-dev.yml # 개발 환경 설정
|
||||||
|
│ └── test/ # 테스트 코드
|
||||||
|
├── build.gradle # Gradle 빌드 설정
|
||||||
|
├── Dockerfile-dev # Docker 이미지 정의
|
||||||
|
├── docker-compose-dev.yml # Docker Compose 설정
|
||||||
|
├── Jenkinsfile-dev # Jenkins CI/CD 파이프라인
|
||||||
|
├── .editorconfig # 코드 스타일 설정
|
||||||
|
├── intellij-java-google-style.xml # IntelliJ 코드 스타일
|
||||||
|
└── README.md # 이 문서
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎨 코드 스타일
|
||||||
|
|
||||||
|
프로젝트는 **Google Java Style Guide**를 따르며, 일부 커스터마이징이 적용되어 있습니다:
|
||||||
|
|
||||||
|
- **들여쓰기**: 탭 (표시 너비: 2칸)
|
||||||
|
- **최대 줄 길이**: 100자
|
||||||
|
- **줄 바꿈**: LF (Unix 스타일)
|
||||||
|
- **인코딩**: UTF-8
|
||||||
|
|
||||||
|
### IntelliJ IDEA 설정
|
||||||
|
|
||||||
|
1. `Preferences` → `Editor` → `Code Style` → `Java`
|
||||||
|
2. `Import Scheme` → `intellij-java-google-style.xml` 선택
|
||||||
|
|
||||||
|
## 📐 주요 기능
|
||||||
|
|
||||||
|
### GeoJSON 지원
|
||||||
|
|
||||||
|
JTS Geometry 타입이 자동으로 GeoJSON 형식으로 직렬화/역직렬화됩니다:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Entity
|
||||||
|
public class SpatialEntity {
|
||||||
|
@Column(columnDefinition = "geometry(Point,4326)")
|
||||||
|
private Point location;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "geometry(Polygon,4326)")
|
||||||
|
private Polygon boundary;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**특징**:
|
||||||
|
- 16자리 정밀도 (기본 8자리에서 증가)
|
||||||
|
- Point, Polygon, Geometry 타입 지원
|
||||||
|
- 자동 JSON 변환 (Jackson ObjectMapper 설정)
|
||||||
|
|
||||||
|
### QueryDSL 설정
|
||||||
|
|
||||||
|
타입 안전한 쿼리를 위해 QueryDSL이 설정되어 있습니다:
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Q-class는 빌드 시 자동 생성됨
|
||||||
|
QSpatialEntity entity = QSpatialEntity.spatialEntity;
|
||||||
|
|
||||||
|
List<SpatialEntity> results = queryFactory
|
||||||
|
.selectFrom(entity)
|
||||||
|
.where(entity.name.eq("example"))
|
||||||
|
.fetch();
|
||||||
|
```
|
||||||
|
|
||||||
|
**Q-class 생성 위치**: `build/generated/sources/annotationProcessor/java/main`
|
||||||
|
|
||||||
|
## 🚢 배포
|
||||||
|
|
||||||
|
### Jenkins CI/CD
|
||||||
|
|
||||||
|
Jenkins를 통한 자동 배포가 설정되어 있습니다 (`Jenkinsfile-dev`):
|
||||||
|
|
||||||
|
**파이프라인 단계**:
|
||||||
|
1. **Checkout**: Git 저장소에서 코드 가져오기 (develop 브랜치)
|
||||||
|
2. **Get Commit Hash**: 버전 태깅용 커밋 해시 추출
|
||||||
|
3. **Build**: Gradle로 JAR 빌드 (테스트 제외)
|
||||||
|
4. **Docker Build & Deploy**: Docker 이미지 빌드 및 배포
|
||||||
|
5. **Cleanup**: 오래된 이미지 정리
|
||||||
|
|
||||||
|
**자동 기능**:
|
||||||
|
- Git 커밋 해시로 이미지 태깅
|
||||||
|
- 기존 컨테이너 자동 교체
|
||||||
|
- 헬스체크로 배포 검증
|
||||||
|
- 오래된 이미지 자동 정리 (최신 5개 유지)
|
||||||
|
|
||||||
|
### 수동 배포
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 빌드
|
||||||
|
./gradlew clean build -x test
|
||||||
|
|
||||||
|
# 2. Docker 이미지 빌드
|
||||||
|
docker-compose -f docker-compose-dev.yml build
|
||||||
|
|
||||||
|
# 3. 배포
|
||||||
|
docker-compose -f docker-compose-dev.yml up -d
|
||||||
|
|
||||||
|
# 4. 헬스체크
|
||||||
|
curl http://localhost:7100/monitor/health
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 개발 가이드
|
||||||
|
|
||||||
|
### 새로운 엔티티 추가
|
||||||
|
|
||||||
|
1. `domain` 패키지에 엔티티 클래스 생성
|
||||||
|
2. JPA 어노테이션 설정
|
||||||
|
3. 빌드하여 Q-class 자동 생성
|
||||||
|
4. Repository 인터페이스 작성 (QueryDSL custom 메서드 포함)
|
||||||
|
|
||||||
|
### GeoJSON 데이터 다루기
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Controller에서 GeoJSON 수신/반환
|
||||||
|
@PostMapping("/spatial")
|
||||||
|
public ResponseEntity<SpatialEntity> create(@RequestBody SpatialEntity entity) {
|
||||||
|
// Point, Polygon이 자동으로 GeoJSON에서 변환됨
|
||||||
|
return ResponseEntity.ok(spatialService.save(entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 응답 예시
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"location": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [126.9779692, 37.5662952]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 데이터베이스 마이그레이션
|
||||||
|
|
||||||
|
현재 설정은 `ddl-auto: validate`이므로 스키마 변경 시:
|
||||||
|
|
||||||
|
1. 수동으로 마이그레이션 스크립트 작성
|
||||||
|
2. 또는 개발 중에는 `ddl-auto: update` 사용 (주의 필요)
|
||||||
|
3. 운영 환경에서는 반드시 `validate` 유지
|
||||||
|
|
||||||
|
## 📝 환경 변수
|
||||||
|
|
||||||
|
Docker Compose 또는 시스템 환경 변수로 설정 가능:
|
||||||
|
|
||||||
|
| 변수명 | 설명 | 기본값 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| `SPRING_PROFILES_ACTIVE` | 활성 프로파일 | `local` |
|
||||||
|
| `IMAGE_TAG` | Docker 이미지 태그 | `latest` |
|
||||||
|
| `TZ` | 타임존 | `Asia/Seoul` |
|
||||||
|
|
||||||
|
## 🐛 트러블슈팅
|
||||||
|
|
||||||
|
### DataSource 설정 오류
|
||||||
|
|
||||||
|
```
|
||||||
|
Failed to configure a DataSource: 'url' attribute is not specified
|
||||||
|
```
|
||||||
|
|
||||||
|
**해결 방법**:
|
||||||
|
1. 프로파일 이름 확인: `dev`, `prod`, `local` (철자 정확히)
|
||||||
|
2. 해당 프로파일의 `application-{profile}.yml`에 datasource 설정 존재 확인
|
||||||
|
3. PostgreSQL 접속 정보 확인
|
||||||
|
|
||||||
|
### Q-class가 생성되지 않음
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 강제로 Q-class 재생성
|
||||||
|
./gradlew clean compileJava
|
||||||
|
```
|
||||||
|
|
||||||
|
생성 위치: `build/generated/sources/annotationProcessor/java/main`
|
||||||
|
|
||||||
|
### Docker 컨테이너 헬스체크 실패
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 컨테이너 로그 확인
|
||||||
|
docker logs kamco-changedetection-api
|
||||||
|
|
||||||
|
# 직접 헬스체크
|
||||||
|
docker exec kamco-changedetection-api curl http://localhost:8080/monitor/health
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📚 참고 문서
|
||||||
|
|
||||||
|
- [Spring Boot 3.5.7 Documentation](https://docs.spring.io/spring-boot/3.5.7/reference/)
|
||||||
|
- [Spring Data JPA](https://docs.spring.io/spring-data/jpa/reference/)
|
||||||
|
- [QueryDSL](https://querydsl.com/static/querydsl/latest/reference/html/)
|
||||||
|
- [JTS Topology Suite](https://github.com/locationtech/jts)
|
||||||
|
- [GeoJSON Specification](https://geojson.org/)
|
||||||
|
|
||||||
|
## 📄 라이선스
|
||||||
|
|
||||||
|
Copyright © 2024 KAMCO. All rights reserved.
|
||||||
|
|
||||||
|
## 👥 기여자
|
||||||
|
|
||||||
|
- Development Team at KAMCO
|
||||||
@@ -45,8 +45,16 @@ dependencies {
|
|||||||
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
|
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
|
||||||
annotationProcessor 'jakarta.annotation:jakarta.annotation-api'
|
annotationProcessor 'jakarta.annotation:jakarta.annotation-api'
|
||||||
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
|
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
|
||||||
|
|
||||||
|
// actuator
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('test') {
|
tasks.named('test') {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bootJar {
|
||||||
|
archiveFileName = 'ROOT.jar'
|
||||||
|
}
|
||||||
|
|||||||
27
docker-compose-dev.yml
Normal file
27
docker-compose-dev.yml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
kamco-changedetection-api:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile-dev
|
||||||
|
image: kamco-changedetection-api:${IMAGE_TAG:-latest}
|
||||||
|
container_name: kamco-changedetection-api
|
||||||
|
ports:
|
||||||
|
- "7100:8080"
|
||||||
|
environment:
|
||||||
|
- SPRING_PROFILES_ACTIVE=dev
|
||||||
|
- TZ=Asia/Seoul
|
||||||
|
networks:
|
||||||
|
- plantation-network
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:8080/monitor/health"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
networks:
|
||||||
|
plantation-network:
|
||||||
|
external: true
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package com.kamco.cd.kamcoback.config;
|
||||||
|
|
||||||
|
|
||||||
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class StartupLogger {
|
||||||
|
|
||||||
|
private final Environment environment;
|
||||||
|
private final DataSource dataSource;
|
||||||
|
|
||||||
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
|
public void logStartupInfo() {
|
||||||
|
String[] activeProfiles = environment.getActiveProfiles();
|
||||||
|
String profileInfo =
|
||||||
|
activeProfiles.length > 0 ? String.join(", ", activeProfiles) : "default";
|
||||||
|
|
||||||
|
// Database connection information
|
||||||
|
String dbUrl = environment.getProperty("spring.datasource.url");
|
||||||
|
String dbUsername = environment.getProperty("spring.datasource.username");
|
||||||
|
String dbDriver = environment.getProperty("spring.datasource.driver-class-name");
|
||||||
|
|
||||||
|
// HikariCP pool settings
|
||||||
|
String poolInfo = "";
|
||||||
|
if (dataSource instanceof HikariDataSource hikariDs) {
|
||||||
|
poolInfo =
|
||||||
|
String.format(
|
||||||
|
"""
|
||||||
|
│ Pool Size : min=%d, max=%d
|
||||||
|
│ Connection Timeout: %dms
|
||||||
|
│ Idle Timeout : %dms
|
||||||
|
│ Max Lifetime : %dms""",
|
||||||
|
hikariDs.getMinimumIdle(),
|
||||||
|
hikariDs.getMaximumPoolSize(),
|
||||||
|
hikariDs.getConnectionTimeout(),
|
||||||
|
hikariDs.getIdleTimeout(),
|
||||||
|
hikariDs.getMaxLifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
// JPA/Hibernate settings
|
||||||
|
String showSql = environment.getProperty("spring.jpa.show-sql", "false");
|
||||||
|
String ddlAuto = environment.getProperty("spring.jpa.hibernate.ddl-auto", "none");
|
||||||
|
String batchSize =
|
||||||
|
environment.getProperty("spring.jpa.properties.hibernate.jdbc.batch_size", "N/A");
|
||||||
|
String batchFetchSize =
|
||||||
|
environment.getProperty(
|
||||||
|
"spring.jpa.properties.hibernate.default_batch_fetch_size", "N/A");
|
||||||
|
|
||||||
|
String startupMessage =
|
||||||
|
String.format(
|
||||||
|
"""
|
||||||
|
|
||||||
|
╔════════════════════════════════════════════════════════════════════════════════╗
|
||||||
|
║ 🚀 APPLICATION STARTUP INFORMATION ║
|
||||||
|
╠════════════════════════════════════════════════════════════════════════════════╣
|
||||||
|
║ PROFILE CONFIGURATION ║
|
||||||
|
╠────────────────────────────────────────────────────────────────────────────────╣
|
||||||
|
│ Active Profile(s): %s
|
||||||
|
╠════════════════════════════════════════════════════════════════════════════════╣
|
||||||
|
║ DATABASE CONFIGURATION ║
|
||||||
|
╠────────────────────────────────────────────────────────────────────────────────╣
|
||||||
|
│ Database URL : %s
|
||||||
|
│ Username : %s
|
||||||
|
│ Driver : %s
|
||||||
|
╠════════════════════════════════════════════════════════════════════════════════╣
|
||||||
|
║ HIKARICP CONNECTION POOL ║
|
||||||
|
╠────────────────────────────────────────────────────────────────────────────────╣
|
||||||
|
%s
|
||||||
|
╠════════════════════════════════════════════════════════════════════════════════╣
|
||||||
|
║ JPA/HIBERNATE CONFIGURATION ║
|
||||||
|
╠────────────────────────────────────────────────────────────────────────────────╣
|
||||||
|
│ Show SQL : %s
|
||||||
|
│ DDL Auto : %s
|
||||||
|
│ JDBC Batch Size : %s
|
||||||
|
│ Fetch Batch Size : %s
|
||||||
|
╚════════════════════════════════════════════════════════════════════════════════╝
|
||||||
|
""",
|
||||||
|
profileInfo,
|
||||||
|
dbUrl != null ? dbUrl : "N/A",
|
||||||
|
dbUsername != null ? dbUsername : "N/A",
|
||||||
|
dbDriver != null ? dbDriver : "PostgreSQL JDBC Driver (auto-detected)",
|
||||||
|
poolInfo,
|
||||||
|
showSql,
|
||||||
|
ddlAuto,
|
||||||
|
batchSize,
|
||||||
|
batchFetchSize);
|
||||||
|
|
||||||
|
log.info(startupMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/main/resources/application-dev.yml
Normal file
25
src/main/resources/application-dev.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
spring:
|
||||||
|
config:
|
||||||
|
activate:
|
||||||
|
on-profile: dev
|
||||||
|
|
||||||
|
jpa:
|
||||||
|
show-sql: true
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: validate
|
||||||
|
properties:
|
||||||
|
hibernate:
|
||||||
|
default_batch_fetch_size: 100 # ✅ 성능 - N+1 쿼리 방지
|
||||||
|
order_updates: true # ✅ 성능 - 업데이트 순서 정렬로 데드락 방지
|
||||||
|
use_sql_comments: true # ⚠️ 선택 - SQL에 주석 추가 (디버깅용)
|
||||||
|
format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성)
|
||||||
|
|
||||||
|
datasource:
|
||||||
|
url: jdbc:postgresql://10.100.0.10:25432/temp
|
||||||
|
username: temp
|
||||||
|
password: temp123!
|
||||||
|
hikari:
|
||||||
|
minimum-idle: 10
|
||||||
|
maximum-pool-size: 20
|
||||||
|
|
||||||
|
|
||||||
25
src/main/resources/application-local.yml
Normal file
25
src/main/resources/application-local.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
spring:
|
||||||
|
config:
|
||||||
|
activate:
|
||||||
|
on-profile: local
|
||||||
|
|
||||||
|
jpa:
|
||||||
|
show-sql: true
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: validate
|
||||||
|
properties:
|
||||||
|
hibernate:
|
||||||
|
default_batch_fetch_size: 100 # ✅ 성능 - N+1 쿼리 방지
|
||||||
|
order_updates: true # ✅ 성능 - 업데이트 순서 정렬로 데드락 방지
|
||||||
|
use_sql_comments: true # ⚠️ 선택 - SQL에 주석 추가 (디버깅용)
|
||||||
|
format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성)
|
||||||
|
|
||||||
|
datasource:
|
||||||
|
url: jdbc:postgresql://10.100.0.10:25432/temp
|
||||||
|
username: temp
|
||||||
|
password: temp123!
|
||||||
|
hikari:
|
||||||
|
minimum-idle: 1
|
||||||
|
maximum-pool-size: 5
|
||||||
|
|
||||||
|
|
||||||
24
src/main/resources/application-prod.yml
Normal file
24
src/main/resources/application-prod.yml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
spring:
|
||||||
|
config:
|
||||||
|
activate:
|
||||||
|
on-profile: dev
|
||||||
|
|
||||||
|
jpa:
|
||||||
|
show-sql: false
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: validate
|
||||||
|
properties:
|
||||||
|
hibernate:
|
||||||
|
default_batch_fetch_size: 100 # ✅ 성능 - N+1 쿼리 방지
|
||||||
|
order_updates: true # ✅ 성능 - 업데이트 순서 정렬로 데드락 방지
|
||||||
|
use_sql_comments: true # ⚠️ 선택 - SQL에 주석 추가 (디버깅용)
|
||||||
|
|
||||||
|
datasource:
|
||||||
|
url: jdbc:postgresql://10.100.0.10:25432/temp
|
||||||
|
username: temp
|
||||||
|
password: temp123!
|
||||||
|
hikari:
|
||||||
|
minimum-idle: 10
|
||||||
|
maximum-pool-size: 20
|
||||||
|
|
||||||
|
|
||||||
@@ -1,83 +1,59 @@
|
|||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: kamco-back
|
name: kamco-change-detection-api
|
||||||
profiles:
|
profiles:
|
||||||
default: local
|
active: local # 사용할 프로파일 지정 (ex. dev, prod, test)
|
||||||
|
|
||||||
server:
|
|
||||||
port: 8080
|
|
||||||
|
|
||||||
# -----------------------
|
|
||||||
# local
|
|
||||||
# -----------------------
|
|
||||||
---
|
|
||||||
server:
|
|
||||||
port: 8080
|
|
||||||
|
|
||||||
spring:
|
|
||||||
config:
|
|
||||||
activate:
|
|
||||||
on-profile: local
|
|
||||||
datasource:
|
datasource:
|
||||||
driver-class-name: org.postgresql.Driver
|
driver-class-name: org.postgresql.Driver
|
||||||
url: jdbc:postgresql://10.100.0.10:25432/temp
|
|
||||||
username: temp
|
|
||||||
password: temp123!
|
|
||||||
hikari:
|
hikari:
|
||||||
minimum-idle: 10
|
jdbc:
|
||||||
maximum-pool-size: 50
|
time_zone: UTC
|
||||||
|
batch_size: 50
|
||||||
|
# 권장 설정
|
||||||
|
minimum-idle: 2
|
||||||
|
maximum-pool-size: 2
|
||||||
|
connection-timeout: 20000
|
||||||
|
idle-timeout: 300000
|
||||||
|
max-lifetime: 1800000
|
||||||
|
leak-detection-threshold: 60000
|
||||||
jpa:
|
jpa:
|
||||||
hibernate:
|
hibernate:
|
||||||
ddl-auto: none # 또는 update/create
|
ddl-auto: validate
|
||||||
show-sql: true
|
|
||||||
properties:
|
properties:
|
||||||
hibernate:
|
hibernate:
|
||||||
format_sql: true
|
jdbc:
|
||||||
|
batch_size: 50
|
||||||
|
default_batch_fetch_size: 100
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
org:
|
||||||
|
springframework:
|
||||||
|
web: DEBUG
|
||||||
|
security: DEBUG
|
||||||
|
root: INFO
|
||||||
|
# actuator
|
||||||
|
management:
|
||||||
|
health:
|
||||||
|
readinessstate:
|
||||||
|
enabled: true
|
||||||
|
livenessstate:
|
||||||
|
enabled: true
|
||||||
|
endpoint:
|
||||||
|
health:
|
||||||
|
probes:
|
||||||
|
enabled: true
|
||||||
|
show-details: always
|
||||||
|
endpoints:
|
||||||
|
jmx:
|
||||||
|
exposure:
|
||||||
|
exclude: "*"
|
||||||
|
web:
|
||||||
|
base-path: /monitor
|
||||||
|
exposure:
|
||||||
|
include:
|
||||||
|
- "health"
|
||||||
|
|
||||||
# -----------------------
|
|
||||||
# dev
|
|
||||||
# -----------------------
|
|
||||||
---
|
|
||||||
server:
|
|
||||||
port: 3333
|
|
||||||
|
|
||||||
spring:
|
|
||||||
config:
|
|
||||||
activate:
|
|
||||||
on-profile: dev
|
|
||||||
datasource:
|
|
||||||
driver-class-name: org.postgresql.Driver
|
|
||||||
url: jdbc:postgresql://10.100.0.10:25432/temp
|
|
||||||
username: temp
|
|
||||||
password: temp123!
|
|
||||||
jpa:
|
|
||||||
hibernate:
|
|
||||||
ddl-auto: none # 또는 update/create
|
|
||||||
show-sql: true
|
|
||||||
properties:
|
|
||||||
hibernate:
|
|
||||||
format_sql: true
|
|
||||||
|
|
||||||
# -----------------------
|
|
||||||
# prod
|
|
||||||
# -----------------------
|
|
||||||
---
|
|
||||||
server:
|
|
||||||
port: 8080
|
|
||||||
|
|
||||||
spring:
|
|
||||||
config:
|
|
||||||
activate:
|
|
||||||
on-profile: prod
|
|
||||||
datasource:
|
|
||||||
driver-class-name: org.postgresql.Driver
|
|
||||||
url: jdbc:postgresql://10.100.0.10:25432/temp
|
|
||||||
username: temp
|
|
||||||
password: temp123!
|
|
||||||
jpa:
|
|
||||||
hibernate:
|
|
||||||
ddl-auto: validate # 또는 update/create
|
|
||||||
show-sql: true
|
|
||||||
properties:
|
|
||||||
hibernate:
|
|
||||||
format_sql: true
|
|
||||||
|
|||||||
Reference in New Issue
Block a user