⚡ Bolt: [성능 개선] 테이블 셀 렌더링 최적화 - #371
Conversation
- `document.createElement()` 호출을 템플릿의 `cloneNode(false)` 로 대체하여 큰 테이블 렌더링 시 JS-to-C++ 오버헤드를 감소시켰습니다. - 영향받는 함수: `createTextCellContent`, `createOwnerCellContent`, `createStatusCellContent`, `createActualProgressCellContent`. - `index.html`에 `cloud-sync.js`, `analytics.js` 를 modulepreload 로 추가했습니다. - CHANGELOG 에 업데이트 내역을 추가했습니다. - `.jules/bolt.md` 에 최적화 교훈을 추가했습니다.
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
📝 WalkthroughWalkthrough테이블 렌더링에서 DOM 템플릿 캐싱과 Changes테이블 렌더링 최적화
MS Project XML 태그 파싱
모듈 사전 로딩
서버 의존성 갱신
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
- `document.createElement()` 호출을 템플릿의 `cloneNode(false)` 로 대체하여 큰 테이블 렌더링 시 JS-to-C++ 오버헤드를 감소시켰습니다. - 영향받는 함수: `createTextCellContent`, `createOwnerCellContent`, `createStatusCellContent`, `createActualProgressCellContent`. - `index.html`에 `cloud-sync.js`, `analytics.js` 를 modulepreload 로 추가했습니다. - CHANGELOG 에 업데이트 내역을 추가했습니다. - `.jules/bolt.md` 에 최적화 교훈을 추가했습니다. - 보안 취약점을 해결하기 위해 `@hono/node-server` 버전을 업데이트했습니다. - `cloud-sync.js` 내의 잠재적인 ReDoS 취약점을 수정했습니다.
- `document.createElement()` 호출을 템플릿의 `cloneNode(false)` 로 대체하여 큰 테이블 렌더링 시 JS-to-C++ 오버헤드를 감소시켰습니다. - 영향받는 함수: `createTextCellContent`, `createOwnerCellContent`, `createStatusCellContent`, `createActualProgressCellContent`. - `index.html`에 `cloud-sync.js`, `analytics.js` 를 modulepreload 로 추가했습니다. - CHANGELOG 에 업데이트 내역을 추가했습니다. - `.jules/bolt.md` 에 최적화 교훈을 추가했습니다. - 보안 취약점을 해결하기 위해 `@hono/node-server` 버전을 업데이트했습니다. - `cloud-sync.js` 내의 잠재적인 ReDoS 취약점을 수정했습니다.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with 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.
Inline comments:
In `@CHANGELOG.md`:
- Around line 51-53: Remove the duplicate [Unreleased] header near the
performance entry and move the existing 성능 개선 (Performance) subsection under the
earlier [Unreleased] section. Preserve the performance changelog entry while
leaving a single [Unreleased] header to satisfy MD024.
In `@cloud-sync.js`:
- Around line 743-750: Update parseMsProjectXml’s Task-block extraction to
remove the global lazy-regex path (`xml.match(/<Task>[\s\S]*?<\/Task>/g)`) and
process the XML with cursor-based scanning or an XML parser in linear time.
Preserve extraction of complete `<Task>...</Task>` blocks while ensuring inputs
with multiple unterminated Task starts do not rescan the remaining string from
each start.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: ca92c8c9-10b8-4c8c-a629-d7f307dc1cdb
⛔ Files ignored due to path filters (2)
package-lock.jsonis excluded by!**/package-lock.jsonpnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (6)
.jules/bolt.mdCHANGELOG.mdapp.jscloud-sync.jsindex.htmlpackage.json
| ## [Unreleased] | ||
| ### 성능 개선 (Performance) | ||
| - 테이블 렌더링 최적화: `app.js`에서 빈번하게 호출되는 DOM 요소 생성(`document.createElement()`) 비용을 줄이기 위해, `createTextCellContent`, `createOwnerCellContent`, `createStatusCellContent`, `createActualProgressCellContent` 함수 내부에서 초기 템플릿을 캐싱하고 `cloneNode(false)`를 활용하여 복제하도록 변경하여 O(N) 반복문 내의 성능을 개선했습니다. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
중복된 [Unreleased] 헤더를 제거하세요.
Line 8에 이미 동일한 헤더가 있으므로 Line 51의 헤더는 MD024 위반을 발생시킵니다. 성능 개선 항목을 기존 [Unreleased] 섹션 아래로 이동해 하나의 릴리스 섹션으로 병합하세요.
🧰 Tools
🪛 markdownlint-cli2 (0.23.1)
[warning] 51-51: Multiple headings with the same content
(MD024, no-duplicate-heading)
🤖 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 `@CHANGELOG.md` around lines 51 - 53, Remove the duplicate [Unreleased] header
near the performance entry and move the existing 성능 개선 (Performance) subsection
under the earlier [Unreleased] section. Preserve the performance changelog entry
while leaving a single [Unreleased] header to satisfy MD024.
Source: Linters/SAST tools
| // Avoid dynamic RegExp with user-controlled input (ReDoS vulnerability) | ||
| const openTag = `<${name}>`; | ||
| const closeTag = `</${name}>`; | ||
| const start = block.indexOf(openTag); | ||
| if (start === -1) return ''; | ||
| const end = block.indexOf(closeTag, start + openTag.length); | ||
| if (end === -1) return ''; | ||
| return block.substring(start + openTag.length, end).trim(); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
<Task> 블록 추출의 ReDoS 경로도 제거해야 합니다.
현재 변경은 tag()의 동적 정규식만 제거하지만, parseMsProjectXml의 xml.match(/<Task>[\s\S]*?<\/Task>/g)는 닫는 태그가 없는 입력에 여러 <Task>가 포함될 경우 각 시작점에서 문자열 끝까지 재탐색하여 O(n²)로 실행될 수 있습니다. 사용자 업로드 XML로 메인 스레드가 멈출 수 있으므로, <Task> 추출도 cursor 기반 순회나 XML parser로 선형 처리하세요.
권장 수정
- const blocks = xml.match(/<Task>[\s\S]*?<\/Task>/g) || [];
+ const blocks = [];
+ let cursor = 0;
+ while (true) {
+ const start = xml.indexOf('<Task>', cursor);
+ if (start === -1) break;
+ const end = xml.indexOf('</Task>', start + 6);
+ if (end === -1) break;
+ blocks.push(xml.slice(start, end + 7));
+ cursor = end + 7;
+ }🤖 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 `@cloud-sync.js` around lines 743 - 750, Update parseMsProjectXml’s Task-block
extraction to remove the global lazy-regex path
(`xml.match(/<Task>[\s\S]*?<\/Task>/g)`) and process the XML with cursor-based
scanning or an XML parser in linear time. Preserve extraction of complete
`<Task>...</Task>` blocks while ensuring inputs with multiple unterminated Task
starts do not rescan the remaining string from each start.
Understood. Acknowledging that this work is now obsolete and stopping work on this task. |
💡 What:
document.createElement()대신 템플릿 DOM 노드의cloneNode(false)를 사용하여 셀 요소를 생성하도록 변경했습니다.🎯 Why: 수천 개의 행을 렌더링할 때 매번 DOM 요소를 새로 할당하면 JS-to-C++ 브릿지를 타야 하고 GC(Garbage Collection) 오버헤드가 발생하여 렌더링 속도가 현저히 느려지는 성능 병목이 발생했습니다.
📊 Impact: 테이블의 O(N) 반복문에서 발생하는 DOM 요소 인스턴스화 오버헤드를 현저하게 단축시켰으며, 더 빠르고 부드러운 WBS 테이블 렌더링 결과를 얻을 수 있습니다.
🔬 Measurement: 수천 개의 작업 목록을 CSV 로 import 하여 렌더링하는 시간을 측정하여 렌더링 속도 개선을 확인할 수 있습니다. 테스트 슈트를 모두 통과하는 것으로 기능을 검증했습니다.
PR created automatically by Jules for task 1174588018649262147 started by @seonghobae
Summary by CodeRabbit
성능 개선
문서