Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
## 2024-08-01 - URL 인코딩 빌더 지연 생성
**학습:** URL 인코딩이 필요 없는 안전한 경로 문자열에서도 항상 `StringBuilder`를 생성하면 hot path에서 불필요한 할당이 발생합니다.
**조치:** 예약 바이트를 처음 만났을 때만 `StringBuilder`를 만들고, 그 전까지는 원본 문자열을 그대로 반환하는 지연 생성 패턴을 사용합니다.
## $(date +%Y-%m-%d) - Optimize OS stat calls in file listing
## 2025-01-24 - Optimize OS stat calls in file listing
**Learning:** Replaced three separate OS stat calls (`Files.isDirectory(it.toPath(), LinkOption.NOFOLLOW_LINKS)`, `!it.isDirectory()`, and `!Files.isSymbolicLink(it.toPath())`) with a single `Files.readAttributes` call. The original code caused significant I/O overhead. This reduces file metadata fetching time significantly.
**Action:** Always consider using `Files.readAttributes` to fetch multiple file attributes at once rather than calling separate boolean checks like `isDirectory` or `isSymbolicLink` on individual files when iterating directories.
## 2025-01-24 - 단일 readAttributes 호출로 파일 속성 조회 최적화
Expand All @@ -43,3 +43,6 @@
## 2025-01-24 - 단일 readAttributes 호출로 파일 속성 조회 최적화
**학습:** `isDirectory`, `!it.isDirectory()`, `isSymbolicLink` 3개의 개별적인 파일 시스템 I/O 호출을 수행하면 성능 저하가 큽니다. 이를 단일 `Files.readAttributes` 호출로 변경하여 메타데이터를 한 번에 조회함으로써 I/O 오버헤드를 대폭 줄일 수 있음을 확인했습니다.
**조치:** 디렉토리 순회 시 파일의 여러 속성을 확인할 때는 개별적인 stat 호출보다 `Files.readAttributes`를 사용하여 필요한 모든 속성을 한 번에 가져오는 방식을 우선적으로 고려해야 합니다.
## 2025-01-24 - CSS 및 SHA-256 해시 계산 최상위(Top-level) 이동
**학습:** `html4tree/main.kt`의 `process_dir` 함수 내에서 디렉토리를 순회할 때마다 고정된 문자열인 `cssContent`와 이를 기반으로 하는 `styleHash`(SHA-256 해시 계산)를 반복적으로 할당하고 계산하는 것은 심각한 성능 병목을 유발합니다. 암호화 해시 계산(`MessageDigest.getInstance("SHA-256")`)은 상대적으로 비용이 큰 연산입니다.
**조치:** 불필요한 중복 계산과 객체 할당을 피하기 위해 `cssContent`, `styleHash`, `css` 변수를 최상위 프로퍼티(top-level property)로 추출하여 애플리케이션 실행 당 한 번만 계산되도록 최적화했습니다. (이러한 최적화 후에는 Jacoco가 해당 프로퍼티의 암묵적 getter에 대한 커버리지를 요구하므로 관련된 테스트 코드를 추가해야 합니다.)
16 changes: 9 additions & 7 deletions src/main/kotlin/html4tree/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,9 @@ fun write_index_file(curr_dir: File, content: String) {
}
}

fun process_dir(curr_dir: File, excludeSet: Set<String>? = null, dirFiles: Array<File>? = null){

val exclude: Set<String> = excludeSet ?: process_ignore_file(curr_dir)

val cssContent = """
// ⚡ Bolt Performance Optimization: 정적 CSS 및 비용이 큰 SHA-256 해싱 계산을 최상위(top-level)
// 프로퍼티로 이동하여 매 디렉토리 순회마다 재계산하지 않고 애플리케이션 실행 시 단 한 번만 계산하도록 최적화합니다.
val cssContent = """
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
line-height: 1.5;
Expand Down Expand Up @@ -310,13 +308,17 @@ fun process_dir(curr_dir: File, excludeSet: Set<String>? = null, dirFiles: Array
}
"""

val styleHash = "sha256-" + Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-256").digest(cssContent.toByteArray(Charsets.UTF_8)))
val styleHash = "sha256-" + Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-256").digest(cssContent.toByteArray(Charsets.UTF_8)))

val css = """
val css = """
<style>
${cssContent} </style>
"""
Comment on lines +313 to 316

fun process_dir(curr_dir: File, excludeSet: Set<String>? = null, dirFiles: Array<File>? = null){

val exclude: Set<String> = excludeSet ?: process_ignore_file(curr_dir)

val index_top = """<!doctype html>
<html lang="ko">
<head>
Expand Down
9 changes: 9 additions & 0 deletions src/test/kotlin/html4tree/MainTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -705,4 +705,13 @@ class MainTest {
assertFalse(processed, "fileKey mismatch should skip directory processing")
assertFalse(listed, "fileKey mismatch should skip child listing")
}

@Test
fun testCssContentProperties() {
org.junit.Assert.assertNotNull(cssContent)
org.junit.Assert.assertNotNull(styleHash)
org.junit.Assert.assertNotNull(css)
Comment on lines +711 to +713
assertTrue(css.contains(cssContent))
assertTrue(styleHash.startsWith("sha256-"))
}
}
Loading