# πŸ”₯ Java-Service-Tree-Framework-Engine-Fire ## ν”„λ‘œμ νŠΈ 기동 κ°€μ΄λ“œ ### 1. Github Repository clone ```bash git clone -b dev https://github.com/313DEVGRP/Java-Service-Tree-Framework-Engine-Fire.git cd Java-Service-Tree-Framework-Engine-Fire ``` ### 2. ν™˜κ²½ μ„€μ • - Java 21 - languageVersion = JavaLanguageVersion.of(21) ### 3. μ„€μ • 파일 ꡬ성 ### ν”„λ‘œνŒŒμΌ μ„€μ • ```bash -Dspring.profiles.active=dev ``` ### μ„€μ • 확인 경둜 ```bash http://www.313.co.kr:33133/javaServiceTreeFrameworkEngineFire/dev ``` > A-RMS(Requirements Management System)의 핡심 λ°±μ—”λ“œ μ—”μ§„ μ„œλΉ„μŠ€ [![Java](https://img.shields.io/badge/Java-21-orange.svg)](https://openjdk.java.net/) [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.4.4-brightgreen.svg)](https://spring.io/projects/spring-boot) [![Gradle](https://img.shields.io/badge/Gradle-8.13-blue.svg)](https://gradle.org/) [![License](https://img.shields.io/badge/License-Proprietary-red.svg)]() ## πŸ“‹ λͺ©μ°¨ - [ν”„λ‘œμ νŠΈ κ°œμš”](#-ν”„λ‘œμ νŠΈ-κ°œμš”) - [μ£Όμš” κΈ°λŠ₯](#-μ£Όμš”-κΈ°λŠ₯) - [기술 μŠ€νƒ](#-기술-μŠ€νƒ) - [μ‹œμŠ€ν…œ μ•„ν‚€ν…μ²˜](#-μ‹œμŠ€ν…œ-μ•„ν‚€ν…μ²˜) - [ν”„λ‘œμ νŠΈ ꡬ쑰](#-ν”„λ‘œμ νŠΈ-ꡬ쑰) - [μ‹œμž‘ν•˜κΈ°](#-μ‹œμž‘ν•˜κΈ°) - [API λ¬Έμ„œ](#-api-λ¬Έμ„œ) - [배포](#-배포) - [개발 κ°€μ΄λ“œ](#-개발-κ°€μ΄λ“œ) - [λΌμ΄μ„ μŠ€](#-λΌμ΄μ„ μŠ€) --- ## 🎯 ν”„λ‘œμ νŠΈ κ°œμš” **Engine-Fire**λŠ” A-RMS의 λ°±μ—”λ“œ λ§ˆμ΄ν¬λ‘œμ„œλΉ„μŠ€ 쀑 ν•˜λ‚˜λ‘œ, ALM(Application Lifecycle Management) 톡합, μš”κ΅¬μ‚¬ν•­ 관리, 검색 μ—”μ§„, 그리고 이슈 νŠΈλž˜ν‚Ή κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€. ### 핡심 μ—­ν•  - πŸ”— **ALM 톡합**: Jira Cloud/Server, Redmine 연동 - πŸ“Š **μš”κ΅¬μ‚¬ν•­ 관리**: 계측 ꡬ쑰 기반 μš”κ΅¬μ‚¬ν•­ CRUD - πŸ” **검색 μ—”μ§„**: OpenSearch/Elasticsearch 기반 μ „λ¬Έ 검색 - πŸ“ˆ **뢄석 & λŒ€μ‹œλ³΄λ“œ**: λΉ„μš©/μ‹œκ°„/λ²”μœ„/λ¦¬μ†ŒμŠ€ 뢄석 ### λ§ˆμ΄ν¬λ‘œμ„œλΉ„μŠ€ μ•„ν‚€ν…μ²˜ ``` [Frontend: Java-Service-Tree-Framework-Frontend-Web] ↓ [Middleware-Proxy (API Gateway)] ↙ ↓ β†˜ Backend-Core Engine-Fire ⭐ Engine-Search ``` --- ## ✨ μ£Όμš” κΈ°λŠ₯ ### 1. μš”κ΅¬μ‚¬ν•­ 관리 (Requirement Management) - βœ… 계측적 μš”κ΅¬μ‚¬ν•­ ꡬ쑰 관리 - βœ… 버전별/μ œν’ˆλ³„ 필터링 - βœ… λ‹΄λ‹Ήμžλ³„ 집계 및 좔적 - βœ… 진척도 λͺ¨λ‹ˆν„°λ§ - βœ… μ‚­μ œ/볡ꡬ 관리 **μ£Όμš” API μ—”λ“œν¬μΈνŠΈ:** ```http POST /engine/jira/dashboard/req-assignees # λ‹΄λ‹Ήμž λͺ©λ‘ GET /engine/reqStatus/reqIssues-created-together # 묢음 쑰회 GET /engine/reqStatus/deletedIssueList # μ‚­μ œ λͺ©λ‘ PUT /engine/reqStatus/deleteWithdrawal # μ‚­μ œ 철회 POST /engine/req-gantt/resolution # κ°„νŠΈ 차트용 검색 POST /engine/reqStatus/issue-list-by-updated # μ—…λ°μ΄νŠΈ κΈ°μ€€ 쑰회 POST /engine/reqStatus/subtasks-and-linked-issues # μ„œλΈŒνƒœμŠ€ν¬ 쑰회 ``` ### 2. ALM 이슈 톡합 **지원 ALM μ‹œμŠ€ν…œ:** - Jira Cloud (Atlassian REST API v3) - Jira Server/Data Center (Atlassian REST API v2) - Redmine (Redmine REST API) **μ£Όμš” API μ—”λ“œν¬μΈνŠΈ:** ```http POST /{connectId}/jira/issue # 이슈 생성 PUT /{connectId}/jira/issue/{issueKeyOrId} # 이슈 μˆ˜μ • PUT /{connectId}/jira/issue/{issueKeyOrId}/{statusId} # μƒνƒœ λ³€κ²½ DELETE /{connectId}/jira/issue/{issueKeyOrId} # 이슈 μ‚­μ œ GET /{connectId}/jira/issue/{issueKeyOrId} # 이슈 쑰회 ``` ### 3. 검색 μ—”μ§„ - πŸ” OpenSearch/Elasticsearch 기반 μ „λ¬Έ 검색 - πŸ“Š 집계 쿼리 (Aggregation) - ⚑ μ‹€μ‹œκ°„ 인덱싱 - πŸ”— 컀λ„₯μ…˜ 풀링 및 Keep-Alive 관리 ### 4. 뢄석 & λŒ€μ‹œλ³΄λ“œ - πŸ“Š **λΉ„μš© 뢄석** (Cost Analysis) - ⏱️ **μ‹œκ°„ 뢄석** (Time Analysis) - πŸ“ **λ²”μœ„ 뢄석** (Scope Analysis) - πŸ‘₯ **λ¦¬μ†ŒμŠ€ 뢄석** (Resource Analysis) - πŸ“ˆ **λŒ€μ‹œλ³΄λ“œ 데이터** 제곡 ### 5. 리포트 생성 - πŸ“„ Full Data Report - 🎯 SWOT Analysis - πŸ“Š ALM Project Report - πŸ“… κ°„νŠΈ 차트 리포트 --- ## πŸ”§ 기술 μŠ€νƒ ### 핡심 ν”„λ ˆμž„μ›Œν¬ | μΉ΄ν…Œκ³ λ¦¬ | 기술 | 버전 | |---------|------|------| | **Language** | Java | 21 | | **Framework** | Spring Boot | 3.4.4 | | **Cloud Config** | Spring Cloud Config | 2024.0.1 | | **Build Tool** | Gradle | 8.13 | | **Container** | Docker | Latest | ### μ£Όμš” 라이브러리 #### ALM 톡합 ```gradle // Jira REST API Client implementation 'com.atlassian.jira:jira-rest-java-client-core:5.1.6' implementation 'com.atlassian.jira:jira-rest-java-client-api:5.1.6' // Redmine REST API Client implementation 'com.taskadapter:redmine-java-api:4.0.0.rc4' ``` #### 검색 μ—”μ§„ ```gradle // OpenSearch implementation 'org.opensearch.client:spring-data-opensearch:1.6.4' implementation 'org.opensearch.client:opensearch-rest-client:2.11.0' // Elasticsearch (Legacy Support) implementation 'co.elastic.clients:elasticsearch-java:8.11.1' ``` #### HTTP 톡신 ```gradle // OpenFeign implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' implementation 'io.github.openfeign:feign-hc5' // Apache HTTP Components implementation 'org.apache.httpcomponents:httpclient:4.5.14' ``` #### μœ ν‹Έλ¦¬ν‹° ```gradle // ModelMapper for DTO-Entity conversion implementation 'org.modelmapper:modelmapper:3.1.1' // Lombok compileOnly 'org.projectlombok:lombok' // Jackson Joda Time implementation 'com.fasterxml.jackson.datatype:jackson-datatype-joda:2.12.5' ``` #### λͺ¨λ‹ˆν„°λ§ & λ¬Έμ„œν™” ```gradle // Actuator implementation 'org.springframework.boot:spring-boot-starter-actuator' // SpringDoc OpenAPI (Swagger) implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0' // Slack API implementation 'com.slack.api:slack-api-client:1.25.1' ``` --- ## πŸ—οΈ μ‹œμŠ€ν…œ μ•„ν‚€ν…μ²˜ ### λ””μžμΈ νŒ¨ν„΄ #### 1. Strategy Pattern (μ „λž΅ νŒ¨ν„΄) ALM별 κ΅¬ν˜„μ²΄λ₯Ό λŸ°νƒ€μž„μ— 선택 ```java interface AlmStrategy { AlmIssue createIssue(AlmIssueDTO dto); AlmIssue updateIssue(AlmIssueDTO dto); AlmIssue getIssue(String issueKey); void deleteIssue(String issueKey); } // Jira κ΅¬ν˜„μ²΄ class JiraStrategy implements AlmStrategy { ... } // Redmine κ΅¬ν˜„μ²΄ class RedmineStrategy implements AlmStrategy { ... } ``` #### 2. DTO-VO-Entity Pattern 계측별 데이터 객체 뢄리 - **DTO** (Data Transfer Object): API μš”μ²­/응닡 - **VO** (Value Object): 읽기 μ „μš© κ°’ 객체 - **Entity**: μ˜μ†μ„± 객체 #### 3. Repository Pattern OpenSearch/Elasticsearch 좔상화 ```java @EnableElasticsearchRepositories( basePackages = {"com.arms"}, repositoryBaseClass = EsCommonRepositoryImpl.class ) ``` #### 4. AOP (Aspect-Oriented Programming) νš‘λ‹¨ 관심사 뢄리 - λ‘œκΉ… - νŠΈλžœμž­μ…˜ 관리 - μ˜ˆμ™Έ 처리 --- ## πŸ“‚ ν”„λ‘œμ νŠΈ ꡬ쑰 ``` src/main/java/com/arms/ β”œβ”€β”€ Application.java # 메인 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ—”νŠΈλ¦¬ν¬μΈνŠΈ β”‚ β”œβ”€β”€ config/ # μ„€μ • 클래슀 β”‚ β”œβ”€β”€ OpensearchClientConfig.java # OpenSearch ν΄λΌμ΄μ–ΈνŠΈ μ„€μ • β”‚ β”œβ”€β”€ ElasticsearchProperties.java # ES μ—°κ²° 정보 β”‚ β”œβ”€β”€ OpenFeignConfig.java # Feign ν΄λΌμ΄μ–ΈνŠΈ μ„€μ • β”‚ β”œβ”€β”€ SlackConfig.java # Slack μ•Œλ¦Ό μ„€μ • β”‚ β”œβ”€β”€ SwaggerUIConfiguration.java # API λ¬Έμ„œν™” β”‚ └── ... β”‚ β”œβ”€β”€ api/ β”‚ β”œβ”€β”€ requirement/ # ⭐ μš”κ΅¬μ‚¬ν•­ 관리 β”‚ β”‚ β”œβ”€β”€ controller/ β”‚ β”‚ β”‚ └── RequirementController.java β”‚ β”‚ β”œβ”€β”€ model/ β”‚ β”‚ β”‚ β”œβ”€β”€ dto/ # RequirementDTO, RequirementAggrDTO β”‚ β”‚ β”‚ └── vo/ # RequirementVO, ReqProgressVO β”‚ β”‚ └── service/ β”‚ β”‚ └── RequirementService.java β”‚ β”‚ β”‚ β”œβ”€β”€ issue/ # ⭐ ALM 이슈 톡합 β”‚ β”‚ β”œβ”€β”€ almapi/ # Jira/Redmine API 톡합 β”‚ β”‚ β”‚ β”œβ”€β”€ controller/ β”‚ β”‚ β”‚ β”‚ └── IssueController.java β”‚ β”‚ β”‚ β”œβ”€β”€ model/ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ dto/ # AlmIssueDTO β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ entity/ # AlmIssueEntity β”‚ β”‚ β”‚ β”‚ └── vo/ # AlmIssueVOResponse β”‚ β”‚ β”‚ β”œβ”€β”€ service/ β”‚ β”‚ β”‚ β”‚ └── IssueService.java β”‚ β”‚ β”‚ β”œβ”€β”€ strategy/ # Strategy νŒ¨ν„΄ (Jira/Redmine) β”‚ β”‚ β”‚ └── repository/ β”‚ β”‚ β”œβ”€β”€ priority/ # 이슈 μš°μ„ μˆœμœ„ β”‚ β”‚ β”œβ”€β”€ status/ # 이슈 μƒνƒœ β”‚ β”‚ β”œβ”€β”€ type/ # 이슈 νƒ€μž… β”‚ β”‚ └── resolution/ # 이슈 ν•΄κ²°μ±… β”‚ β”‚ β”‚ β”œβ”€β”€ search_engine/ # ⭐ 검색 μ—”μ§„ β”‚ β”‚ β”œβ”€β”€ controller/ β”‚ β”‚ β”œβ”€β”€ service/ β”‚ β”‚ β”‚ β”œβ”€β”€ AlmIssueSearch.java β”‚ β”‚ β”‚ └── AlmIssueSearchImpl.java β”‚ β”‚ β”œβ”€β”€ dto/ β”‚ β”‚ └── vo/ β”‚ β”‚ β”‚ β”œβ”€β”€ project/ # ν”„λ‘œμ νŠΈ 관리 β”‚ β”œβ”€β”€ dashboard/ # λŒ€μ‹œλ³΄λ“œ β”‚ β”œβ”€β”€ analysis/ # 뢄석 (λΉ„μš©/μ‹œκ°„/λ²”μœ„/λ¦¬μ†ŒμŠ€) β”‚ β”‚ β”œβ”€β”€ cost/ β”‚ β”‚ β”œβ”€β”€ time/ β”‚ β”‚ β”œβ”€β”€ scope/ β”‚ β”‚ └── resource/ β”‚ β”œβ”€β”€ report/ # 리포트 생성 β”‚ β”œβ”€β”€ account/ # 계정 관리 β”‚ └── util/ # μœ ν‹Έλ¦¬ν‹° β”‚ β”œβ”€β”€ alm/ # ALM μœ ν‹Έλ¦¬ν‹° β”‚ β”œβ”€β”€ aes/ # μ•”ν˜Έν™” β”‚ β”œβ”€β”€ slack/ # Slack μ•Œλ¦Ό β”‚ └── aspect/ # AOP β”‚ └── egovframework/ # μ „μžμ •λΆ€ ν”„λ ˆμž„μ›Œν¬ └── javaservice/ └── esframework/ # Elasticsearch Framework ``` --- ## πŸš€ μ‹œμž‘ν•˜κΈ° ### ν•„μˆ˜ μš”κ΅¬μ‚¬ν•­ - β˜• **Java 21** 이상 - 🐘 **Gradle 8.13** 이상 - 🐳 **Docker** (선택사항) - πŸ” **OpenSearch** λ˜λŠ” **Elasticsearch** ### 1. Repository Clone ```bash git clone -b dev https://github.com/313DEVGRP/Java-Service-Tree-Framework-Engine-Fire.git cd Java-Service-Tree-Framework-Engine-Fire ``` ### 2. ν™˜κ²½ μ„€μ • #### Spring Cloud Config μ„€μ • ```yaml # src/main/resources/application.yml spring: application: name: javaServiceTreeFrameworkEngineFire cloud: config: uri: http://www.313.co.kr:33133 name: javaServiceTreeFrameworkEngineFire profile: dev # dev, prod, local ``` #### ν”„λ‘œνŒŒμΌ ν™œμ„±ν™” ```bash # VM Options -Dspring.profiles.active=dev # λ˜λŠ” ν™˜κ²½ λ³€μˆ˜ export SPRING_PROFILES_ACTIVE=dev ``` #### μ„€μ • 확인 ```bash # Config Serverμ—μ„œ μ„€μ • 확인 curl http://www.313.co.kr:33133/javaServiceTreeFrameworkEngineFire/dev ``` ### 3. λΉŒλ“œ ```bash # Gradle Wrapper μ‹€ν–‰ κΆŒν•œ λΆ€μ—¬ (Linux/Mac) chmod +x gradlew # λΉŒλ“œ ./gradlew clean build # ν…ŒμŠ€νŠΈ μ œμ™Έ λΉŒλ“œ ./gradlew clean build -x test ``` ### 4. μ‹€ν–‰ #### 둜컬 μ‹€ν–‰ ```bash # Gradle둜 μ‹€ν–‰ ./gradlew bootRun # JAR둜 μ‹€ν–‰ java -jar build/libs/Java-Service-Tree-Framework-Engine-Fire-25.10.x.jar ``` #### Docker둜 μ‹€ν–‰ ```bash # Docker 이미지 λΉŒλ“œ ./gradlew buildDockerImage # Docker μ»¨ν…Œμ΄λ„ˆ μ‹€ν–‰ docker run -d \ -p 8080:8080 \ --name engine-fire \ 313.co.kr:5550/313devgrp/java-service-tree-framework-engine-fire:25.10.x ``` ### 5. ν—¬μŠ€ 체크 ```bash # μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μƒνƒœ 확인 curl http://localhost:8080/actuator/health # 응닡 μ˜ˆμ‹œ { "status": "UP" } ``` --- ## πŸ“– API λ¬Έμ„œ ### Swagger UI μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰ ν›„ λ‹€μŒ URLμ—μ„œ API λ¬Έμ„œλ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€: ``` http://localhost:8080/swagger-ui.html ``` ### μ£Όμš” API μ—”λ“œν¬μΈνŠΈ #### μš”κ΅¬μ‚¬ν•­ 관리 | Method | Endpoint | μ„€λͺ… | |--------|----------|------| | POST | `/engine/jira/dashboard/req-assignees` | λ‹΄λ‹Ήμž λͺ©λ‘ 쑰회 | | GET | `/engine/reqStatus/reqIssues-created-together` | 묢음 쑰회 | | GET | `/engine/reqStatus/deletedIssueList` | μ‚­μ œλœ 이슈 λͺ©λ‘ | | PUT | `/engine/reqStatus/deleteWithdrawal` | 이슈 μ‚­μ œ 철회 | | POST | `/engine/req-gantt/resolution` | κ°„νŠΈ 차트용 검색 | | POST | `/engine/reqStatus/issue-list-by-updated` | μ—…λ°μ΄νŠΈ κΈ°μ€€ 쑰회 | #### ALM 이슈 톡합 | Method | Endpoint | μ„€λͺ… | |--------|----------|------| | POST | `/{connectId}/jira/issue` | 이슈 생성 | | PUT | `/{connectId}/jira/issue/{issueKeyOrId}` | 이슈 μˆ˜μ • | | PUT | `/{connectId}/jira/issue/{issueKeyOrId}/{statusId}` | μƒνƒœ λ³€κ²½ | | DELETE | `/{connectId}/jira/issue/{issueKeyOrId}` | 이슈 μ‚­μ œ | | GET | `/{connectId}/jira/issue/{issueKeyOrId}` | 이슈 쑰회 | ### API μ‚¬μš© μ˜ˆμ‹œ #### 이슈 생성 ```bash curl -X POST "http://localhost:8080/{connectId}/jira/issue" \ -H "Content-Type: application/json" \ -d '{ "projectKey": "PROJ", "issueType": "Task", "summary": "New Issue", "description": "Issue description" }' ``` #### μš”κ΅¬μ‚¬ν•­ λ‹΄λ‹Ήμž λͺ©λ‘ 쑰회 ```bash curl -X POST "http://localhost:8080/engine/jira/dashboard/req-assignees" \ -H "Content-Type: application/json" \ -d '{ "pdServiceId": 1, "pdServiceVersions": [1, 2, 3] }' ``` --- ## 🐳 배포 ### Docker 배포 #### 1. 이미지 λΉŒλ“œ ```bash # Gradle Task둜 λΉŒλ“œ ./gradlew buildDockerImage # λ˜λŠ” Docker 직접 λΉŒλ“œ docker build -t 313devgrp/java-service-tree-framework-engine-fire:25.10.x . ``` #### 2. 이미지 ν‘Έμ‹œ ```bash # Private Registry에 ν‘Έμ‹œ ./gradlew pushDockerImage # λ˜λŠ” docker push 313.co.kr:5550/313devgrp/java-service-tree-framework-engine-fire:25.10.x ``` #### 3. μ»¨ν…Œμ΄λ„ˆ μ‹€ν–‰ ```bash docker run -d \ --name engine-fire \ -p 8080:8080 \ -e SPRING_PROFILES_ACTIVE=prod \ -e JAVA_OPTS="-Xms512m -Xmx2048m" \ 313.co.kr:5550/313devgrp/java-service-tree-framework-engine-fire:25.10.x ``` ### Maven Repository 배포 ```bash # Maven μ €μž₯μ†Œμ— 배포 ./gradlew publish # 버전 확인 ./gradlew printVersion ``` **배포 정보:** - **Repository**: http://www.313.co.kr/nexus/repository/ple-releases/ - **GroupId**: 313devgrp - **ArtifactId**: Java-Service-Tree-Framework-Engine-Fire - **Version**: 25.10.x (μžλ™ 증가) ### 버전 관리 버전은 `Major.Minor.Patch` ν˜•μ‹μœΌλ‘œ μžλ™ κ΄€λ¦¬λ©λ‹ˆλ‹€: ```gradle ext { majorVersion = 25 // μˆ˜λ™ 관리 minorVersion = 10 // μˆ˜λ™ 관리 // patchVersion은 Nexusμ—μ„œ μžλ™ 증가 } ``` **μžλ™ 증가 둜직:** 1. Nexusμ—μ„œ μ΅œμ‹  버전 쑰회 (maven-metadata.xml) 2. Major 버전 증가 μ‹œ β†’ Minor, Patch 리셋 (0.0) 3. Minor 버전 증가 μ‹œ β†’ Patch 리셋 (0) 4. 동일 Major.Minor β†’ Patch μžλ™ 증가 --- ## πŸ‘¨β€πŸ’» 개발 κ°€μ΄λ“œ ### 개발 ν™˜κ²½ μ„€μ • #### 1. IDE μ„€μ • (IntelliJ IDEA) 1. **ν”„λ‘œμ νŠΈ Import** - `File > Open` β†’ `build.gradle` 선택 - Gradle ν”„λ‘œμ νŠΈλ‘œ Import 2. **Java SDK μ„€μ •** - `File > Project Structure > Project` - SDK: Java 21 - Language Level: 21 3. **Lombok ν”ŒλŸ¬κ·ΈμΈ μ„€μΉ˜** - `Settings > Plugins` - "Lombok" 검색 및 μ„€μΉ˜ - `Settings > Build > Compiler > Annotation Processors` - "Enable annotation processing" 체크 4. **Code Style μ„€μ •** - `Settings > Editor > Code Style > Java` - Indent: 4 spaces - Tab size: 4 #### 2. 둜컬 개발 μ„€μ • ##### application-local.yml 생성 ```yaml # src/main/resources/application-local.yml spring: profiles: active: local server: port: 8080 # OpenSearch μ„€μ • elasticsearch: url: localhost:9200 # Slack μ•Œλ¦Ό (선택사항) slack: webhook-url: https://hooks.slack.com/services/YOUR/WEBHOOK/URL ``` ##### 둜컬 ν”„λ‘œνŒŒμΌλ‘œ μ‹€ν–‰ ```bash ./gradlew bootRun --args='--spring.profiles.active=local' ``` ### μ½”λ”© μ»¨λ²€μ…˜ #### 넀이밍 κ·œμΉ™ - **클래슀**: PascalCase (예: `RequirementController`) - **λ©”μ„œλ“œ**: camelCase (예: `createIssue`) - **μƒμˆ˜**: UPPER_SNAKE_CASE (예: `MAX_RETRY_COUNT`) - **νŒ¨ν‚€μ§€**: lowercase (예: `com.arms.api.requirement`) #### 주석 κ·œμΉ™ ```java /** * μš”κ΅¬μ‚¬ν•­ 생성 API * * @param requirementDTO μš”κ΅¬μ‚¬ν•­ 데이터 * @return μƒμ„±λœ μš”κ΅¬μ‚¬ν•­ 정보 * @throws IllegalArgumentException ν•„μˆ˜ νŒŒλΌλ―Έν„° λˆ„λ½ μ‹œ */ public RequirementVO createRequirement(RequirementDTO requirementDTO) { // κ΅¬ν˜„ } ``` #### μ˜ˆμ™Έ 처리 ```java @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(IllegalArgumentException.class) public ResponseEntity handleIllegalArgument( IllegalArgumentException ex) { log.error("Invalid argument: {}", ex.getMessage()); return ResponseEntity .badRequest() .body(new ErrorResponse(ex.getMessage())); } } ``` ### ν…ŒμŠ€νŠΈ μž‘μ„± #### λ‹¨μœ„ ν…ŒμŠ€νŠΈ ```java @SpringBootTest class RequirementServiceTest { @Autowired private RequirementService requirementService; @Test @DisplayName("μš”κ΅¬μ‚¬ν•­ 생성 ν…ŒμŠ€νŠΈ") void createRequirement() { // Given RequirementDTO dto = new RequirementDTO(); dto.setTitle("Test Requirement"); // When RequirementVO result = requirementService.createRequirement(dto); // Then assertThat(result).isNotNull(); assertThat(result.getTitle()).isEqualTo("Test Requirement"); } } ``` #### 톡합 ν…ŒμŠ€νŠΈ (WireMock) ```java @SpringBootTest @Import(WireMockJiraConfig.class) class IssueServiceIntegrationTest { @Autowired private IssueService issueService; @Test @DisplayName("Jira 이슈 생성 톡합 ν…ŒμŠ€νŠΈ") void createJiraIssue() { // Given AlmIssueDTO dto = new AlmIssueDTO(); dto.setServerId("jira-001"); dto.setProjectKey("PROJ"); // When AlmIssueVOResponse response = issueService.createIssue(dto); // Then assertThat(response).isNotNull(); assertThat(response.getKey()).startsWith("PROJ-"); } } ``` ### λ‘œκΉ… κ°€μ΄λ“œ ```java @Slf4j @Service public class RequirementService { public void processRequirement(Long id) { log.debug("Processing requirement: {}", id); try { // λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 log.info("Successfully processed requirement: {}", id); } catch (Exception e) { log.error("Failed to process requirement: {}", id, e); throw e; } } } ``` **둜그 레벨:** - `ERROR`: μ‹œμŠ€ν…œ 였λ₯˜, μ˜ˆμ™Έ λ°œμƒ - `WARN`: κ²½κ³ , 잠재적 문제 - `INFO`: μ€‘μš”ν•œ λΉ„μ¦ˆλ‹ˆμŠ€ 이벀트 - `DEBUG`: μƒμ„Έν•œ 디버깅 정보 - `TRACE`: 맀우 μƒμ„Έν•œ 좔적 정보 --- ## πŸ” 문제 ν•΄κ²° (Troubleshooting) ### 자주 λ°œμƒν•˜λŠ” 문제 #### 1. OpenSearch μ—°κ²° μ‹€νŒ¨ **증상:** ``` Connection refused: localhost:9200 ``` **ν•΄κ²°:** ```bash # OpenSearch μ‹€ν–‰ μƒνƒœ 확인 curl http://localhost:9200 # OpenSearch μ‹œμž‘ docker run -d -p 9200:9200 -e "discovery.type=single-node" opensearchproject/opensearch:2.11.0 ``` #### 2. Spring Cloud Config μ—°κ²° μ‹€νŒ¨ **증상:** ``` Could not resolve placeholder 'elasticsearch.url' ``` **ν•΄κ²°:** - Config Server μ‹€ν–‰ μƒνƒœ 확인 - `application.yml`에 fallback μ„€μ • μΆ”κ°€ ```yaml spring: cloud: config: fail-fast: false # Config Server 없어도 μ‹œμž‘ ``` #### 3. Jira API 인증 μ‹€νŒ¨ **증상:** ``` 401 Unauthorized ``` **ν•΄κ²°:** - API Token 확인 - Base64 인코딩 검증 - Jira κΆŒν•œ 확인 #### 4. λ©”λͺ¨λ¦¬ λΆ€μ‘± **증상:** ``` java.lang.OutOfMemoryError: Java heap space ``` **ν•΄κ²°:** ```bash # JVM λ©”λͺ¨λ¦¬ 증가 java -Xms512m -Xmx2048m -jar app.jar # Docker ν™˜κ²½ docker run -e "JAVA_OPTS=-Xmx2048m" ... ``` --- ## πŸ“Š μ„±λŠ₯ μ΅œμ ν™” ### Connection Pooling ```java @Bean public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() { return (response, context) -> 180 * 1000; // 3λΆ„ Keep-Alive } ``` ### 비동기 처리 ```java @EnableAsync @Configuration public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(100); executor.setThreadNamePrefix("async-"); executor.initialize(); return executor; } } ``` ### 캐싱 μ „λž΅ ```java @Cacheable(value = "issues", key = "#issueKey") public AlmIssueEntity getIssue(String issueKey) { // λ°μ΄ν„°λ² μ΄μŠ€ 쑰회 } @CacheEvict(value = "issues", key = "#issueKey") public void updateIssue(String issueKey, AlmIssueDTO dto) { // μ—…λ°μ΄νŠΈ 둜직 } ``` --- ## πŸ” λ³΄μ•ˆ ### Actuator λ³΄μ•ˆ ```yaml management: endpoints: web: exposure: include: health,info # μ΅œμ†Œν•œμ˜ μ—”λ“œν¬μΈνŠΈλ§Œ λ…ΈμΆœ endpoint: health: show-details: when-authorized # 인증된 μ‚¬μš©μžμ—κ²Œλ§Œ 상세 정보 ``` ### API λ³΄μ•ˆ (ν–₯ν›„ κ°œμ„  사항) - Spring Security 톡합 - OAuth2/JWT 인증 - API Rate Limiting - CORS μ •μ±… μ„€μ • --- ## πŸ“ˆ λͺ¨λ‹ˆν„°λ§ ### Actuator μ—”λ“œν¬μΈνŠΈ | Endpoint | μ„€λͺ… | |----------|------| | `/actuator/health` | μ• ν”Œλ¦¬μΌ€μ΄μ…˜ ν—¬μŠ€ 체크 | | `/actuator/info` | μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 정보 | | `/actuator/env` | ν™˜κ²½ λ³€μˆ˜ 확인 | | `/actuator/beans` | Spring Bean λͺ©λ‘ | | `/actuator/httptrace` | HTTP μš”μ²­ 좔적 | ### μ»€μŠ€ν…€ Health Indicator ```java @Component public class CustomHealthIndicator implements HealthIndicator { @Override public Health health() { // μ»€μŠ€ν…€ ν—¬μŠ€ 체크 둜직 boolean isHealthy = checkSystemHealth(); if (isHealthy) { return Health.up() .withDetail("custom", "All systems operational") .build(); } else { return Health.down() .withDetail("custom", "System degraded") .build(); } } } ``` --- ## 🀝 κΈ°μ—¬ κ°€μ΄λ“œ ### 브랜치 μ „λž΅ - `main`: ν”„λ‘œλ•μ…˜ 릴리즈 - `dev`: 개발 톡합 브랜치 - `feature/*`: κΈ°λŠ₯ 개발 - `bugfix/*`: 버그 μˆ˜μ • - `hotfix/*`: κΈ΄κΈ‰ μˆ˜μ • ### Pull Request ν”„λ‘œμ„ΈμŠ€ 1. `dev` λΈŒλžœμΉ˜μ—μ„œ feature 브랜치 생성 2. κΈ°λŠ₯ 개발 및 ν…ŒμŠ€νŠΈ 3. PR 생성 (dev ← feature) 4. μ½”λ“œ 리뷰 5. λ¨Έμ§€ ### 컀밋 λ©”μ‹œμ§€ μ»¨λ²€μ…˜ ``` feat: μƒˆλ‘œμš΄ κΈ°λŠ₯ μΆ”κ°€ fix: 버그 μˆ˜μ • docs: λ¬Έμ„œ μˆ˜μ • style: μ½”λ“œ ν¬λ§·νŒ… refactor: μ½”λ“œ λ¦¬νŒ©ν† λ§ test: ν…ŒμŠ€νŠΈ μ½”λ“œ chore: λΉŒλ“œ 업무, νŒ¨ν‚€μ§€ λ§€λ‹ˆμ € μ„€μ • ``` **μ˜ˆμ‹œ:** ``` feat: Redmine API 톡합 μΆ”κ°€ - Redmine REST API ν΄λΌμ΄μ–ΈνŠΈ κ΅¬ν˜„ - RedmineStrategy κ΅¬ν˜„ - 톡합 ν…ŒμŠ€νŠΈ μž‘μ„± Closes #123 ``` --- ## πŸ“ λ³€κ²½ 이λ ₯ ### v25.10.x (2025-11-03) - ✨ OpenSearch 2.11.0 지원 - ✨ Redmine API 4.0 μ—…κ·Έλ ˆμ΄λ“œ - πŸ› Jira 이슈 생성 μ‹œ νƒ€μž„μ•„μ›ƒ 문제 μˆ˜μ • - πŸ“ API λ¬Έμ„œ κ°œμ„  ### v25.9.x (2025-10-15) - ✨ 계측적 μš”κ΅¬μ‚¬ν•­ ꡬ쑰 지원 - ✨ Slack μ•Œλ¦Ό κΈ°λŠ₯ μΆ”κ°€ - πŸ”§ OpenFeign HTTP Client 5 적용 - πŸ“Š λŒ€μ‹œλ³΄λ“œ μ„±λŠ₯ κ°œμ„  ### v25.8.x (2025-09-20) - πŸŽ‰ Initial Release - ✨ Jira Cloud/Server 톡합 - ✨ μš”κ΅¬μ‚¬ν•­ 관리 κΈ°λŠ₯ - ✨ OpenSearch 검색 μ—”μ§„ --- ## πŸ“ž μ—°λ½μ²˜ - **Maintainer**: 313DEVGRP - **Email**: 313@313.co.kr - **Website**: http://www.313.co.kr --- ## πŸ“„ λΌμ΄μ„ μŠ€ Copyright Β© 2025 313DEVGRP. All rights reserved. 이 μ†Œν”„νŠΈμ›¨μ–΄λŠ” 독점 λΌμ΄μ„ μŠ€λ‘œ 제곡되며, 무단 볡제 및 배포λ₯Ό κΈˆμ§€ν•©λ‹ˆλ‹€. --- ## πŸ™ κ°μ‚¬μ˜ κΈ€ 이 ν”„λ‘œμ νŠΈλŠ” λ‹€μŒ μ˜€ν”ˆμ†ŒμŠ€ ν”„λ‘œμ νŠΈλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€: - [Spring Boot](https://spring.io/projects/spring-boot) - [Spring Cloud](https://spring.io/projects/spring-cloud) - [OpenSearch](https://opensearch.org/) - [Atlassian Jira REST Java Client](https://ecosystem.atlassian.net/wiki/spaces/JRJC/overview) - [Redmine Java API](https://github.com/taskadapter/redmine-java-api) --- **Made with ❀️ by 313DEVGRP**