From c30aa276f8ce41125dbf10726489a5d5be031e78 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 6 Jul 2026 21:20:56 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20String.format=EC=9D=84=20He?= =?UTF-8?q?xFormat=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=EC=84=B1=EB=8A=A5=20=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 반복문 내에서 `String.format`을 사용하여 16진수 문자열을 생성하는 것은 구문 분석 및 중간 객체 생성으로 인해 성능 저하를 유발합니다. 이를 Java 17+의 표준인 불변 및 스레드 안전 `java.util.HexFormat`을 사용하도록 변경하여 성능을 최적화하고 중간 객체 할당을 제거했습니다. 영향: - `DefaultDocumentConversionService.contentHash()` 및 `ArtifactLinkService.sha256Hex()`에서 문자열 생성 비용 감소 - 객체 생성 감소 및 불필요한 가비지 컬렉터 부하 방지 --- .jules/bolt.md | 3 +++ .../clearfolio/viewer/artifact/ArtifactLinkService.java | 8 +++----- .../viewer/service/DefaultDocumentConversionService.java | 9 +++------ 3 files changed, 9 insertions(+), 11 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..108b134f --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2026-07-02 - String.format in loops +**Learning:** `String.format` is slow for hex conversion inside loops. +**Action:** Replace it with `HexFormat.of().formatHex(bytes)` which is significantly faster and doesn't do intermediate string creations. diff --git a/src/main/java/com/clearfolio/viewer/artifact/ArtifactLinkService.java b/src/main/java/com/clearfolio/viewer/artifact/ArtifactLinkService.java index 454b0448..33e5fe4e 100644 --- a/src/main/java/com/clearfolio/viewer/artifact/ArtifactLinkService.java +++ b/src/main/java/com/clearfolio/viewer/artifact/ArtifactLinkService.java @@ -38,6 +38,8 @@ @Service public class ArtifactLinkService { + private static final java.util.HexFormat HEX_FORMAT = java.util.HexFormat.of(); + /** * Query parameter used by PDF.js-compatible artifact URLs. */ @@ -423,11 +425,7 @@ private static String sha256Hex(byte[] bytes) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] raw = digest.digest(bytes); - StringBuilder hex = new StringBuilder(raw.length * 2); - for (byte b : raw) { - hex.append(String.format("%02x", b)); - } - return hex.toString(); + return HEX_FORMAT.formatHex(raw); } catch (GeneralSecurityException ex) { throw new IllegalStateException("SHA-256 digest unavailable", ex); } diff --git a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentConversionService.java b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentConversionService.java index b8787f35..7ef568b5 100644 --- a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentConversionService.java +++ b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentConversionService.java @@ -26,6 +26,8 @@ @Service public class DefaultDocumentConversionService implements DocumentConversionService { + private static final java.util.HexFormat HEX_FORMAT = java.util.HexFormat.of(); + private final ConversionJobRepository repository; private final ConversionJobStateStore stateStore; private final DocumentValidationService validationService; @@ -160,12 +162,7 @@ private String contentHash(MultipartFile file) { } byte[] raw = digest.digest(); - StringBuilder hex = new StringBuilder(raw.length * 2); - for (byte b : raw) { - hex.append(String.format("%02x", b)); - } - - return hex.toString(); + return HEX_FORMAT.formatHex(raw); } catch (NoSuchAlgorithmException ex) { throw new IllegalStateException("SHA-256 digest unavailable", ex); } catch (IOException ex) {