From 974830846bd031e403e003e8b02cba768318c0d4 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Fri, 5 Dec 2025 10:39:20 +0900 Subject: [PATCH] =?UTF-8?q?OBP=20=EA=B3=BC=EA=B8=88=EC=9A=A9=20gw=20metric?= =?UTF-8?q?=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- CLAUDE.md | 51 ++++++++++++- GEMINI.md | 4 + build.gradle | 11 ++- .../credential/CredentialUIMapper.java | 2 +- .../onl/audit/adapter/map/AuditUIMapper.java | 2 +- .../service/TransactionReportHourJob.java | 11 ++- .../rms/onl/common/util/FileSystemUtil.java | 73 +++++++++++++++++++ .../apim/mapping/ApiInterfaceUIMapper.java | 2 +- .../onl/common/util/FileSystemUtilTest.java | 50 +++++++++++++ 10 files changed, 197 insertions(+), 12 deletions(-) create mode 100644 GEMINI.md create mode 100644 src/main/java/com/eactive/eai/rms/onl/common/util/FileSystemUtil.java create mode 100644 src/test/java/com/eactive/eai/rms/onl/common/util/FileSystemUtilTest.java diff --git a/.gitignore b/.gitignore index 5214320..2adda4b 100644 --- a/.gitignore +++ b/.gitignore @@ -243,4 +243,5 @@ gradle-gf63.sh eapim-admin-kjb/ .claude -*.report.md \ No newline at end of file +*.report.md +*.claude.md \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 1d04763..f99420b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,17 @@ -# CLAUDE.md +# AI-Assisted Development Guide -이 파일은 Claude Code (claude.ai/code)가 이 저장소의 코드를 작업할 때 참고할 가이드를 제공합니다. +이 문서는 AI 어시스턴트가 이 저장소의 코드를 이해하고 기여하는 데 필요한 가이드를 제공합니다. + +## 프로젝트 주요 특징 + +AI 어시스턴트가 작업을 시작하기 전에 반드시 이해해야 할 핵심적인 특징들입니다. + +- **하이브리드 데이터 액세스**: 이 프로젝트는 **JPA(Hibernate)와 iBATIS를 함께 사용**합니다. + - **신규 기능**: 항상 **JPA**와 Spring Data 리포지토리를 사용해야 합니다. + - **레거시 코드**: 기존 iBATIS (`*.xml` 매퍼) 코드를 수정할 때는 해당 패턴을 따라야 합니다. +- **멀티 모듈 Gradle 프로젝트**: `settings.gradle`에 정의된 것처럼 여러 하위 모듈(`elink-online-*`, `kjb-*` 등)로 구성되어 있습니다. 특정 기능은 다른 모듈에 위치할 수 있습니다. +- **QueryDSL 코드 생성**: 빌드 시 `QueryDSL`을 사용하여 Q-class가 자동으로 생성됩니다. IDE에서 엔티티를 찾을 수 없다고 표시되면, `gradle compileJava`를 먼저 실행하여 코드를 생성해야 합니다. +- **고객사별 커스터마이징**: `ext.kjb` 또는 `custom` 패키지는 특정 고객사(광주은행)를 위한 코드를 포함하므로, 일반적인 기능 수정 시에는 주의가 필요합니다. ## 프로젝트 개요 @@ -69,6 +80,39 @@ gradle dependencies gradle tasks --all ``` +## 로컬 개발 빠른 시작 + +새로운 환경에서 프로젝트를 처음 설정하고 실행하는 권장 절차입니다. + +1. **IDE 설정 파일 생성 (최초 1회)** + ```bash + # IntelliJ IDEA 사용 시 + gradle idea + + # Eclipse 사용 시 + gradle eclipse + ``` + +2. **QueryDSL 등 소스 코드 생성** + ```bash + gradle compileJava + ``` + +3. **전체 빌드 및 테스트 실행** + ```bash + gradle build + ``` + +4. **IDE에서 프로젝트 열기** + - `gradle idea` 또는 `gradle eclipse` 실행 후, IDE에서 프로젝트를 엽니다. + +5. **실행 구성(Run Configuration) 설정** + - 사용하는 WAS(Tomcat 등)에 `eapim-admin` 프로젝트를 배포하도록 설정합니다. + - **VM Options**에 위에 명시된 "필수 환경 변수 (Tomcat)"를 모두 추가합니다. + +6. **서버 실행** + - IDE에서 설정한 실행 구성을 시작하여 서버를 실행합니다. + ## 아키텍처 및 모듈 의존성 ### 멀티 모듈 구조 @@ -132,6 +176,9 @@ eapim-admin (root project) ### 필수 환경 변수 (Tomcat) +다음은 로컬 Tomcat 환경에서 개발 시 필요한 시스템 프로퍼티(-D)입니다. +**설정 방법**: IntelliJ, Eclipse 등 IDE의 실행 구성(Run Configuration)에서 'VM options'에 추가하거나, 사용하는 Tomcat의 `bin` 폴더에 `setenv.bat` (Windows) 또는 `setenv.sh` (Linux/macOS) 파일을 생성하여 아래 내용들을 `CATALINA_OPTS` 또는 `JAVA_OPTS` 환경 변수에 추가합니다. + ``` -Deai.datasource.type=DEV -Dinst.Name=emsSvr99 diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..60e6004 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,4 @@ +# GEMINI.md + +이 프로젝트에 대한 주요 정보 및 지침은 `CLAUDE.md` 파일을 참조하십시오. +(For key information and guidelines regarding this project, please refer to the `CLAUDE.md` file.) \ No newline at end of file diff --git a/build.gradle b/build.gradle index 77d3530..39c105a 100644 --- a/build.gradle +++ b/build.gradle @@ -36,10 +36,17 @@ java { } } +sourceSets { + main { + java { + srcDirs generatedJavaDir + } + } +} + compileJava { options.encoding = 'UTF-8' options.compilerArgs = ['-parameters'] - sourceSets.main.java { srcDir generatedJavaDir } options.generatedSourceOutputDirectory = project.file(generatedJavaDir) aptOptions { @@ -64,7 +71,7 @@ war { tasks.withType(JavaCompile) { options.encoding = 'UTF-8' - options.compilerArgs = ['-parameters'] + options.compilerArgs += ['-parameters', "-Xlint:unchecked", "-Xlint:deprecation"] } diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/approval/credential/CredentialUIMapper.java b/src/main/java/com/eactive/eai/rms/onl/apim/approval/credential/CredentialUIMapper.java index e3c7ff8..208dfa8 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/approval/credential/CredentialUIMapper.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/approval/credential/CredentialUIMapper.java @@ -28,7 +28,7 @@ public abstract class CredentialUIMapper implements GenericMapper -1 ) { + if (trTime.contains("EVERY_HOUR")) { // go on } else if (!StringUtils.contains(trTime, hour)) { return; @@ -93,9 +94,11 @@ public class TransactionReportHourJob implements Job { + CommonUtil.getToday("yyyy-MM-dd HH:mm") + ")"); } - String pathDir = monitoringContext.getStringProperty( MonitoringContext.RMS_TRANSACTION_LOG_PATH,"/fslog/eai/") - + monitoringContext.getStringProperty( Keys.SERVER_KEY) - + "/trlog"; + String pathDir = FileSystemUtil.pathNormalize( + monitoringContext.getStringProperty( MonitoringContext.RMS_TRANSACTION_LOG_PATH,"/fslog/eai/") + , monitoringContext.getStringProperty( Keys.SERVER_KEY) + , "trlog" + ); /////////////////////////////////////////////// // 로컬테스트용 diff --git a/src/main/java/com/eactive/eai/rms/onl/common/util/FileSystemUtil.java b/src/main/java/com/eactive/eai/rms/onl/common/util/FileSystemUtil.java new file mode 100644 index 0000000..7026ed7 --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/onl/common/util/FileSystemUtil.java @@ -0,0 +1,73 @@ +package com.eactive.eai.rms.onl.common.util; + +import java.io.File; +import java.util.StringJoiner; + +public class FileSystemUtil { + + /** + * Normalizes a path by joining segments and applying the correct file separator for the current operating system. + * It handles redundant separators between segments. + * + *

Examples for Linux/macOS:

+ * + * + *

Examples for Windows:

+ * + * + * @param args A variable number of path segments to join. + * @return A normalized path string for the current operating system. + */ + public static String pathNormalize(String... args) { + if (args == null || args.length == 0) { + return ""; + } + + StringJoiner joiner = new StringJoiner(File.separator); + for (String arg : args) { + if (arg != null && !arg.isEmpty()) { + String segment = arg; + // Remove leading separators from the segment + while (segment.startsWith("/") || segment.startsWith("\\")) { + segment = segment.substring(1); + } + // Remove trailing separators from the segment + while (segment.endsWith("/") || segment.endsWith("\\")) { + segment = segment.substring(0, segment.length() - 1); + } + if (!segment.isEmpty()) { + joiner.add(segment); + } + } + } + + String joinedPath = joiner.toString(); + + // Handle the root path for the first argument + String firstArg = args[0]; + if (firstArg != null && !firstArg.isEmpty()) { + if (firstArg.startsWith("/")) { + joinedPath = File.separator + joinedPath; + } else if (firstArg.matches("^[a-zA-Z]:\\\\") || firstArg.matches("^[a-zA-Z]:/.*")) { // Handle Windows drive letters, e.g., "C:\" or "C:/" + String drive = firstArg.substring(0, 2); + return drive + File.separator + joinedPath.substring(joinedPath.indexOf(File.separator) + 1); + } + } + + // Final normalization for Windows if the path starts with a separator (e.g. from a UNC path) + if (System.getProperty("os.name").toLowerCase().contains("win")) { + if (args[0] != null && args[0].startsWith("//") || args[0].startsWith("\\\\")) { + return "\\\\" + joinedPath; + } + return joinedPath.replace("/", "\\"); + } + + return joinedPath.replace("\\", "/"); + } +} diff --git a/src/main/java/com/eactive/eai/rms/onl/transaction/apim/mapping/ApiInterfaceUIMapper.java b/src/main/java/com/eactive/eai/rms/onl/transaction/apim/mapping/ApiInterfaceUIMapper.java index c933b63..8d4c762 100644 --- a/src/main/java/com/eactive/eai/rms/onl/transaction/apim/mapping/ApiInterfaceUIMapper.java +++ b/src/main/java/com/eactive/eai/rms/onl/transaction/apim/mapping/ApiInterfaceUIMapper.java @@ -148,7 +148,7 @@ public interface ApiInterfaceUIMapper { @Mapping(target = "eaisendrecv", source = "sendRecvType") @Mapping(target = "eaidirection", source = "inOutType") @Mapping(target = "standardMessageItems", source = "standardMessageItems") - @Mapping(target = "apiFullPath", source = "apiFullPath") + StandardMessageInfo toStandardMessageInfo(ApiInterfaceUI vo); @AfterMapping diff --git a/src/test/java/com/eactive/eai/rms/onl/common/util/FileSystemUtilTest.java b/src/test/java/com/eactive/eai/rms/onl/common/util/FileSystemUtilTest.java new file mode 100644 index 0000000..0d2e19b --- /dev/null +++ b/src/test/java/com/eactive/eai/rms/onl/common/util/FileSystemUtilTest.java @@ -0,0 +1,50 @@ +package com.eactive.eai.rms.onl.common.util; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class FileSystemUtilTest { + + @Test + @EnabledOnOs(OS.LINUX) + void testPathNormalize_Linux() { + assertEquals("/asd1/asd2/asd3", FileSystemUtil.pathNormalize("/asd1/asd2", "asd3")); + assertEquals("/asd1/asd2/asd3", FileSystemUtil.pathNormalize("/asd1/asd2/", "/asd3")); + assertEquals("/asd1/asd2/asd3", FileSystemUtil.pathNormalize("/asd1/asd2", "/asd3")); + assertEquals("/asd1/asd2/asd3", FileSystemUtil.pathNormalize("/asd1/asd2/", "asd3/")); + assertEquals("/a/b/c", FileSystemUtil.pathNormalize("/a/", "/b/", "/c/")); + assertEquals("a/b/c", FileSystemUtil.pathNormalize("a", "b", "c")); + assertEquals("/a", FileSystemUtil.pathNormalize("/a")); + } + + @Test + @EnabledOnOs(OS.WINDOWS) + void testPathNormalize_Windows() { + assertEquals("\\asd1\\asd2\\asd3", FileSystemUtil.pathNormalize("/asd1/asd2", "asd3")); + assertEquals("\\asd1\\asd2\\asd3", FileSystemUtil.pathNormalize("/asd1/asd2/", "/asd3")); + assertEquals("\\asd1\\asd2\\asd3", FileSystemUtil.pathNormalize("/asd1/asd2", "/asd3")); + assertEquals("\\asd1\\asd2\\asd3", FileSystemUtil.pathNormalize("/asd1/asd2/", "asd3/")); + assertEquals("C:\\Users\\Test", FileSystemUtil.pathNormalize("C:/Users", "Test")); + assertEquals("C:\\Users\\Test", FileSystemUtil.pathNormalize("C:\\Users", "Test")); + assertEquals("\\a\\b\\c", FileSystemUtil.pathNormalize("/a/", "/b/", "/c/")); + assertEquals("a\\b\\c", FileSystemUtil.pathNormalize("a", "b", "c")); + assertEquals("\\a", FileSystemUtil.pathNormalize("/a")); + } + + @Test + void testPathNormalize_General() { + String expected = "a" + File.separator + "b" + File.separator + "c"; + assertEquals(expected, FileSystemUtil.pathNormalize("a", "b", "c")); + assertEquals("", FileSystemUtil.pathNormalize("")); + assertEquals("", FileSystemUtil.pathNormalize(null)); + assertEquals("", FileSystemUtil.pathNormalize()); + assertEquals("a", FileSystemUtil.pathNormalize("a")); + assertEquals("a", FileSystemUtil.pathNormalize("a/")); + assertEquals("a", FileSystemUtil.pathNormalize("/a")); + } +}