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
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,7 @@
## 2025-01-24 - 단일 readAttributes 호출로 파일 속성 조회 최적화
**학습:** `isDirectory`, `!it.isDirectory()`, `isSymbolicLink` 3개의 개별적인 파일 시스템 I/O 호출을 수행하면 성능 저하가 큽니다. 이를 단일 `Files.readAttributes` 호출로 변경하여 메타데이터를 한 번에 조회함으로써 I/O 오버헤드를 대폭 줄일 수 있음을 확인했습니다.
**조치:** 디렉토리 순회 시 파일의 여러 속성을 확인할 때는 개별적인 stat 호출보다 `Files.readAttributes`를 사용하여 필요한 모든 속성을 한 번에 가져오는 방식을 우선적으로 고려해야 합니다.

## 2025-01-25 - 디렉토리 순회 중복 해시 계산 최적화
**학습:** Kotlin에서 `process_dir` 내에 정적 문자열 `cssContent`와 무거운 SHA-256 해시 연산 `styleHash`를 정의하면, 각 디렉토리 처리 시마다 불필요한 할당 및 연산이 발생하여 전체 성능 저하를 일으킵니다.
**조치:** 성능 최적화를 위해, 변하지 않는 정적 문자열 및 해시 연산을 파일의 최상위(Top-level) 속성으로 분리하여 애플리케이션 수명 주기 동안 한 번만 계산되도록 합니다.
15 changes: 8 additions & 7 deletions src/main/kotlin/html4tree/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,8 @@ 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: 디렉토리 순회 시마다 발생하는 정적 문자열 할당과 무거운 SHA-256 해시 연산을 방지하기 위해 파일 최상위 속성으로 추출합니다.
internal val cssContent = """
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
line-height: 1.5;
Expand Down Expand Up @@ -313,13 +310,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)))
internal val styleHash = "sha256-" + Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-256").digest(cssContent.toByteArray(Charsets.UTF_8)))

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

CSP 해시와 실제 <style> 본문을 일치시키세요.

Line [313]은 cssContent만 SHA-256으로 계산합니다. 그러나 Line [315]-[318]은 <style> 태그 안에 cssContent 앞의 개행과 뒤의 공백을 추가합니다. CSP는 해시가 실제 인라인 스타일 블록과 일치할 때만 스타일을 허용합니다. 공백도 해시 입력에 포함됩니다. (w3.org)

현재 생성된 HTML에서는 CSS가 차단될 수 있습니다. css가 태그 사이에 cssContent만 포함하도록 구성하세요. src/test/kotlin/html4tree/MainTest.kt의 테스트도 태그 사이 본문을 해시하여 이 조건을 검증해야 합니다.

수정 예시
 internal val css = """
               <style>
-${cssContent}              </style>
+${cssContent}</style>
               """
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/html4tree/main.kt` around lines 313 - 318, Update the `css`
template and `styleHash` calculation in `main.kt` so the SHA-256 input exactly
matches the bytes between the `<style>` tags, with no added newline or
indentation around `cssContent`; then update the relevant `MainTest` assertion
to hash the extracted tag body and verify it matches `styleHash`.


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
8 changes: 8 additions & 0 deletions src/test/kotlin/html4tree/MainTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ class MainTest {
}
}

@Test
fun testCssTopLevelPropertiesCoverage() {
assertTrue(cssContent.contains("font-family:"))
assertTrue(styleHash.startsWith("sha256-"))
assertTrue(css.contains("<style>"))
assertTrue(css.contains(cssContent))
}

@Test
fun testEscapeHtml() {
assertEquals("&amp;", "&".escapeHtml())
Expand Down
Loading