From c88fd2145b31d6fd47b419127c3f0ef730c36a81 Mon Sep 17 00:00:00 2001 From: z3rotig4r Date: Fri, 12 Jun 2026 20:41:32 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20EgovStringUtil.search=20=EB=B9=88=20targ?= =?UTF-8?q?et=20=EB=AC=B4=ED=95=9C=20=EB=A3=A8=ED=94=84=20=EB=B0=A9?= =?UTF-8?q?=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit search(source, target)에서 target이 빈 문자열이면 indexOf("")가 항상 0을 반환하고, 루프 인덱스 i가 0에 고정되며 strCheck도 줄어들지 않아 for 루프가 끝나지 않습니다(source가 비어 있지 않은 경우). 공개 정적 유틸이라 호출 측 입력에 따라 CPU 점유·스레드 행으로 이어질 수 있습니다. 또한 source나 target이 null이면 NullPointerException이 발생합니다. source 또는 target이 null이거나 빈 문자열이면 셀 대상이 없으므로 0을 반환하도록 진입부 가드를 추가했습니다(commons-lang StringUtils.countMatches와 동일한 규약). 빈 target 무한 루프 방지(타임아웃), null 입력, 정상 카운트를 포함한 단위 테스트 3건을 추가했습니다. --- .../rte/fdl/string/EgovStringUtil.java | 7 +++++ .../rte/fdl/string/EgovStringUtilTest.java | 31 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/Foundation/org.egovframe.rte.fdl.string/src/main/java/org/egovframe/rte/fdl/string/EgovStringUtil.java b/Foundation/org.egovframe.rte.fdl.string/src/main/java/org/egovframe/rte/fdl/string/EgovStringUtil.java index d3db1630..0498ba31 100755 --- a/Foundation/org.egovframe.rte.fdl.string/src/main/java/org/egovframe/rte/fdl/string/EgovStringUtil.java +++ b/Foundation/org.egovframe.rte.fdl.string/src/main/java/org/egovframe/rte/fdl/string/EgovStringUtil.java @@ -282,6 +282,13 @@ public static String toSubString(String source, int beginIndex) { * search */ public static int search(String source, String target) { + // source 또는 target이 null/빈 문자열이면 셀 대상이 없으므로 0을 반환한다. + // 특히 target이 빈 문자열이면 indexOf("")가 항상 0을 반환해 + // i가 0에 고정되어 무한 루프에 빠지므로 반드시 먼저 걸러낸다. + if (source == null || source.isEmpty() || target == null || target.isEmpty()) { + return 0; + } + int result = 0; String strCheck = source; for (int i = 0; i < source.length(); ) { diff --git a/Foundation/org.egovframe.rte.fdl.string/src/test/java/org/egovframe/rte/fdl/string/EgovStringUtilTest.java b/Foundation/org.egovframe.rte.fdl.string/src/test/java/org/egovframe/rte/fdl/string/EgovStringUtilTest.java index 6ce10bee..7090a8f9 100755 --- a/Foundation/org.egovframe.rte.fdl.string/src/test/java/org/egovframe/rte/fdl/string/EgovStringUtilTest.java +++ b/Foundation/org.egovframe.rte.fdl.string/src/test/java/org/egovframe/rte/fdl/string/EgovStringUtilTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.junit.jupiter.SpringExtension; +import java.time.Duration; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -406,4 +407,34 @@ public void testGetTokens() { assertEquals(4, EgovStringUtil.getTokens(str).size()); } + /** + * search는 source 안에서 target이 나타나는 횟수를 센다. + */ + @Test + public void testSearchCount() { + assertEquals(2, EgovStringUtil.search("aXbXc", "X")); + assertEquals(0, EgovStringUtil.search("abc", "z")); + assertEquals(2, EgovStringUtil.search("aaaa", "aa")); + } + + /** + * target이 빈 문자열이면 indexOf("")가 항상 0을 반환해 + * 기존 구현은 무한 루프에 빠졌다. 이제는 0을 반환해야 한다. + */ + @Test + public void testSearchEmptyTargetDoesNotLoop() { + assertTimeoutPreemptively(Duration.ofSeconds(2), + () -> assertEquals(0, EgovStringUtil.search("abc", ""))); + } + + /** + * null 입력은 NPE 없이 0을 반환해야 한다. + */ + @Test + public void testSearchNullInputs() { + assertEquals(0, EgovStringUtil.search(null, "a")); + assertEquals(0, EgovStringUtil.search("abc", null)); + assertEquals(0, EgovStringUtil.search("", "a")); + } + }