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 @@ -15,3 +15,7 @@

**Learning:** `dict.setdefault(key, []).append(value)` evaluates the empty-list default on every iteration, including when the key already exists. In grouping loops, `defaultdict(list)` avoids those transient unused list allocations while preserving insertion order.
**Action:** Use `defaultdict(list)` when missing keys are intentionally initialized with lists. Keep `setdefault` when its eager-default behavior or an ordinary `dict` is part of the required contract, and benchmark before claiming a material end-to-end improvement.
## 2025-02-12 - Wrap array rendering loops with useMemo

**Learning:** Mapping over arrays of properties inside a React functional component's render execution directly causes O(N) evaluations when unrelated state (like input queries or tab switches) updates the component.
**Action:** Always wrap `.map()` calls that output elements in a `useMemo` block with exact prop array dependencies in large lists, especially if they are passed as props to avoid blocking the main thread on every re-render.
33 changes: 21 additions & 12 deletions frontend/src/components/calendar/CalendarCandidateView.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
import { useMemo } from 'react';
import type { CalendarCandidateEvent } from './types';

type Props = {
visibleCandidateEvents: CalendarCandidateEvent[];
};

export function CalendarCandidateView({ visibleCandidateEvents }: Props) {

// ⚡ Bolt: Wrap candidate events map in useMemo to prevent O(N) re-renders
// 🎯 Why: Mapping over event lists during unrelated parent renders (e.g. state changes) blocks the main thread.
const candidateEventsList = useMemo(() => (
<>
{visibleCandidateEvents.map((event) => (
<article key={event.id} className="rounded-xl border border-border bg-background p-4">
<h4 className="text-sm font-bold">{event.title}</h4>
<p className="mt-2 text-xs text-muted-foreground">{event.source}</p>
<p className="mt-3 rounded-full bg-primary/10 px-3 py-1 text-xs font-bold text-primary">{event.mode}</p>
</article>
))}
{visibleCandidateEvents.length === 0 && (
<p className="rounded-xl border border-border bg-background p-4 text-sm font-bold text-muted-foreground">
표시 중인 캘린더 후보가 없습니다.
</p>
)}
</>
), [visibleCandidateEvents]);
return (
<section aria-label="일정 후보" className="rounded-2xl border border-border bg-card p-5 shadow-sm">
<h3 className="text-lg font-bold">일정 후보</h3>
<div className="mt-4 grid gap-3 lg:grid-cols-3">
{visibleCandidateEvents.map((event) => (
<article key={event.id} className="rounded-xl border border-border bg-background p-4">
<h4 className="text-sm font-bold">{event.title}</h4>
<p className="mt-2 text-xs text-muted-foreground">{event.source}</p>
<p className="mt-3 rounded-full bg-primary/10 px-3 py-1 text-xs font-bold text-primary">{event.mode}</p>
</article>
))}
{visibleCandidateEvents.length === 0 && (
<p className="rounded-xl border border-border bg-background p-4 text-sm font-bold text-muted-foreground">
표시 중인 캘린더 후보가 없습니다.
</p>
)}
{candidateEventsList}
</div>
</section>
);
Expand Down
33 changes: 21 additions & 12 deletions frontend/src/components/calendar/CalendarWeekView.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
import { useMemo } from 'react';
import type { CalendarWeekEvent } from './types';

type Props = {
visibleWeekEvents: CalendarWeekEvent[];
};

export function CalendarWeekView({ visibleWeekEvents }: Props) {

// ⚡ Bolt: Wrap week events map in useMemo to prevent O(N) re-renders
// 🎯 Why: Mapping over event lists during unrelated parent renders (e.g. state changes) blocks the main thread.
const weekEventsList = useMemo(() => (
<>
{visibleWeekEvents.map((event) => (
<article key={event.id} className="rounded-xl border border-border bg-background p-4">
<p className="text-xs font-black text-primary">{event.day}</p>
<h4 className="mt-2 text-sm font-bold">{event.title}</h4>
<p className="mt-2 text-xs font-semibold text-muted-foreground">{event.source}</p>
</article>
))}
{visibleWeekEvents.length === 0 && (
<p className="rounded-xl border border-border bg-background p-4 text-sm font-bold text-muted-foreground">
표시 중인 캘린더 일정이 없습니다.
</p>
)}
</>
), [visibleWeekEvents]);
return (
<section aria-label="주간 캘린더" className="rounded-2xl border border-border bg-card p-5 shadow-sm">
<h3 className="text-lg font-bold">주간 캘린더</h3>
<div className="mt-4 grid gap-3 md:grid-cols-5">
{visibleWeekEvents.map((event) => (
<article key={event.id} className="rounded-xl border border-border bg-background p-4">
<p className="text-xs font-black text-primary">{event.day}</p>
<h4 className="mt-2 text-sm font-bold">{event.title}</h4>
<p className="mt-2 text-xs font-semibold text-muted-foreground">{event.source}</p>
</article>
))}
{visibleWeekEvents.length === 0 && (
<p className="rounded-xl border border-border bg-background p-4 text-sm font-bold text-muted-foreground">
표시 중인 캘린더 일정이 없습니다.
</p>
)}
{weekEventsList}
</div>
</section>
);
Expand Down
Loading