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
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,6 @@
## 2025-01-24 - 단일 readAttributes 호출로 파일 속성 조회 최적화
**학습:** `isDirectory`, `!it.isDirectory()`, `isSymbolicLink` 3개의 개별적인 파일 시스템 I/O 호출을 수행하면 성능 저하가 큽니다. 이를 단일 `Files.readAttributes` 호출로 변경하여 메타데이터를 한 번에 조회함으로써 I/O 오버헤드를 대폭 줄일 수 있음을 확인했습니다.
**조치:** 디렉토리 순회 시 파일의 여러 속성을 확인할 때는 개별적인 stat 호출보다 `Files.readAttributes`를 사용하여 필요한 모든 속성을 한 번에 가져오는 방식을 우선적으로 고려해야 합니다.
## 2026-08-02 - 정적 문자열 호이스팅을 통한 할당 및 연산 최적화
**학습:** `process_dir`과 같이 자주 호출되는 함수 내부에 불변의 정적 문자열(CSS)이나 무거운 연산(SHA-256 해시)이 존재하면 함수가 호출될 때마다 불필요한 객체 할당과 해시 연산 오버헤드가 발생합니다.
**조치:** 불변 정적 문자열 및 해시값은 파일 최상단에 `private val`로 호이스팅하여 초기화 블록에서 한 번만 연산되게 하고, 메서드 반복 호출 시 재할당되지 않도록 최적화했습니다. `private` 접근 제어자를 명시하여 커버리지 저하를 방지했습니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

private의 효과를 정확히 설명하세요.

private 접근 제어자는 파일 외부 노출을 제한합니다. private 지정만으로 테스트 커버리지 저하를 방지하지는 않습니다. 두 효과를 분리해서 기록하세요.

수정 예시
-**조치:** 불변 정적 문자열 및 해시값은 파일 최상단에 `private val`로 호이스팅하여 초기화 블록에서 한 번만 연산되게 하고, 메서드 반복 호출 시 재할당되지 않도록 최적화했습니다. `private` 접근 제어자를 명시하여 커버리지 저하를 방지했습니다.
+**조치:** 불변 정적 문자열 및 해시값은 파일 최상단에 `private val`로 호이스팅하여 초기화 블록에서 한 번만 연산되게 하고, 메서드 반복 호출 시 재할당되지 않도록 최적화했습니다. `private` 접근 제어자는 파일 외부 노출을 제한합니다.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
**조치:** 불변 정적 문자열 및 해시값은 파일 최상단에 `private val`로 호이스팅하여 초기화 블록에서 한 번만 연산되게 하고, 메서드 반복 호출 시 재할당되지 않도록 최적화했습니다. `private` 접근 제어자를 명시하여 커버리지 저하를 방지했습니다.
**조치:** 불변 정적 문자열 및 해시값은 파일 최상단에 `private val`로 호이스팅하여 초기화 블록에서 한 번만 연산되게 하고, 메서드 반복 호출 시 재할당되지 않도록 최적화했습니다. `private` 접근 제어자는 파일 외부 노출을 제한합니다.
🤖 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 @.jules/bolt.md at line 48, Update the explanatory text in the optimization
note to separate private’s actual effect—restricting access outside the
file—from initialization and coverage behavior. Remove the claim that declaring
constants private prevents coverage degradation, and describe the one-time
top-level computation independently.

145 changes: 72 additions & 73 deletions src/main/kotlin/html4tree/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,78 @@ fun main(args: Array<String>) = Html4tree().main(args)

internal data class FileIdentity(val key: Any?, val readable: Boolean)

private val cssContent = """
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
line-height: 1.5;
padding: 1rem;
color: #1f2328;
}
main {
max-width: 800px;
margin: 0 auto;
}
ul {
list-style-type: none;
padding-left: 0;
}
a.dir-link {
display: flex;
align-items: flex-start;
gap: 0.5rem;
width: 100%;
overflow-wrap: anywhere;
box-sizing: border-box;
}
.icon {
flex-shrink: 0;
width: 1.25rem;
text-align: center;
}
a {
padding: 0.5rem;
text-decoration: none;
color: #0969da;
border-radius: 4px;
transition: background-color 0.2s ease, outline-color 0.2s ease;
}
a:hover, a:focus-visible {
background-color: #f6f8fa;
text-decoration: underline;
outline: 2px solid #0969da;
outline-offset: -2px;
}
@media (prefers-reduced-motion: reduce) {
a {
transition: none;
}
}
@media (prefers-color-scheme: dark) {
body {
background-color: #0d1117;
color: #c9d1d9;
}
a {
color: #58a6ff;
}
a:hover, a:focus-visible {
background-color: #161b22;
outline-color: #58a6ff;
}
}
.empty-dir {
padding: 0.5rem;
opacity: 0.7;
font-style: italic;
}
"""

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

private val css = """
<style>
${cssContent} </style>
"""

internal fun read_file_identity(file: File): FileIdentity {
return try {
Expand Down Expand Up @@ -244,79 +316,6 @@ fun process_dir(curr_dir: File, excludeSet: Set<String>? = null, dirFiles: Array

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

val cssContent = """
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
line-height: 1.5;
padding: 1rem;
color: #1f2328;
}
main {
max-width: 800px;
margin: 0 auto;
}
ul {
list-style-type: none;
padding-left: 0;
}
a.dir-link {
display: flex;
align-items: flex-start;
gap: 0.5rem;
width: 100%;
overflow-wrap: anywhere;
box-sizing: border-box;
}
.icon {
flex-shrink: 0;
width: 1.25rem;
text-align: center;
}
a {
padding: 0.5rem;
text-decoration: none;
color: #0969da;
border-radius: 4px;
transition: background-color 0.2s ease, outline-color 0.2s ease;
}
a:hover, a:focus-visible {
background-color: #f6f8fa;
text-decoration: underline;
outline: 2px solid #0969da;
outline-offset: -2px;
}
@media (prefers-reduced-motion: reduce) {
a {
transition: none;
}
}
@media (prefers-color-scheme: dark) {
body {
background-color: #0d1117;
color: #c9d1d9;
}
a {
color: #58a6ff;
}
a:hover, a:focus-visible {
background-color: #161b22;
outline-color: #58a6ff;
}
}
.empty-dir {
padding: 0.5rem;
opacity: 0.7;
font-style: italic;
}
"""

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

val css = """
<style>
${cssContent} </style>
"""

val index_top = """<!doctype html>
<html lang="ko">
<head>
Expand Down
Loading