Skip to content

[FE] 출석조회 세션 선택 전 중복 발생 해결#286

Merged
discipline24 merged 2 commits intomainfrom
20260307_#282-버그출석-출석-조회시-회차-인덱스-오류-수정
Mar 7, 2026

Hidden character warning

The head ref may contain hidden characters: "20260307_#282-\ubc84\uadf8\ucd9c\uc11d-\ucd9c\uc11d-\uc870\ud68c\uc2dc-\ud68c\ucc28-\uc778\ub371\uc2a4-\uc624\ub958-\uc218\uc815"
Merged

[FE] 출석조회 세션 선택 전 중복 발생 해결#286
discipline24 merged 2 commits intomainfrom
20260307_#282-버그출석-출석-조회시-회차-인덱스-오류-수정

Conversation

@sangkyu39
Copy link
Contributor

@sangkyu39 sangkyu39 commented Mar 7, 2026

Summary by CodeRabbit

릴리스 노트

  • 새 기능

    • 세션 제목 정규화 및 기본값 도입으로 일관된 세션 표시 제공
    • 출석 데이터에 라운드 메타데이터(세션명, 라운드일시) 병합
  • 버그 수정

    • 세션 선택 드롭다운에서 빈 옵션 제거 및 선택값 동기화 개선
    • 중복 세션 항목 필터링 및 정렬 정확도 향상

Copilot AI review requested due to automatic review settings March 7, 2026 12:08
@sangkyu39 sangkyu39 requested a review from DongEun02 as a code owner March 7, 2026 12:08
@sangkyu39 sangkyu39 linked an issue Mar 7, 2026 that may be closed by this pull request
@sangkyu39 sangkyu39 requested a review from gxuoo as a code owner March 7, 2026 12:08
@coderabbitai
Copy link

coderabbitai bot commented Mar 7, 2026

Walkthrough

세션 제목 정규화 유틸을 추가하고 이를 SessionSelectBox, SessionManage, Attendance 등에서 사용하도록 통합했습니다. 출석 데이터는 라운드 메타를 조회해 기록에 sessionTitle·roundDate·roundStartAt를 보강하도록 변경되었습니다.

Changes

Cohort / File(s) Summary
세션 컴포넌트 변경
frontend/src/components/attendance/SessionManage.jsx, frontend/src/components/attendance/SessionSelectBox.jsx
normalizeSessionTitle 도입 및 적용. SessionManage: roundIndexMap, visibleSessions 필터/정렬/중복 제거, 테이블 렌더링 시 정규화 적용. SessionSelectBox: 옵션 매핑에서 정규화 적용, 기본 빈 옵션 제거, currentValue 로직 추가.
출석 페이지 동기화
frontend/src/pages/Attendance.jsx
sessionOptions를 useMemo로 생성하고 useEffect로 selectedSession을 옵션과 동기화. SessionSelectBox의 disabled 조건에 옵션 존재 여부 반영.
출석 데이터 보강 유틸
frontend/src/utils/attendanceList.js
attendance 레코드에 대해 getRounds 호출로 라운드 메타를 구축하는 buildRoundMetaMap 추가. 각 레코드에 sessionTitle, roundDate, roundStartAt을 보강하고 실패 시 원본 유지하는 방어적 처리 추가.
정규화 유틸 추가
frontend/src/utils/normalizeSessionTitle.js
SESSION_TITLE_FALLBACK('기타') 및 normalizeSessionTitle(sessionTitle), getSessionTitleFallback() 함수 추가.

Sequence Diagram(s)

sequenceDiagram
    participant UI as Frontend UI
    participant Utils as attendanceList utils
    participant API as attendanceManage.getRounds
    participant Renderer as Renderer

    UI->>Utils: 요청 (attendance 조회 후 enrichment)
    Utils->>API: getRounds(sessionIds)
    API-->>Utils: rounds list (roundId, sessionTitle, startAt)
    Utils->>Utils: buildRoundMetaMap(rounds) -> map by roundId
    Utils-->>UI: enriched attendance records (sessionTitle, roundDate, roundStartAt)
    UI->>Renderer: 렌더링 (SessionManage/SessionSelectBox 사용)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • DongEun02
  • gxuoo

Poem

🐰
제목을 다듬어 깔끔히 묶고,
공백은 '기타'로 포근히 채워요.
라운드도 찾아와 기록을 빛내니,
출석판 위에 이야기들이 춤추네.
홧팅, 당근 케이크는 내가 한 조각! 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 변경 사항의 핵심과 명확히 일치합니다. 세션 선택 전 중복 발생을 해결하는 것이 주요 목표이며, 제목은 간결하고 구체적입니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 20260307_#282-버그출석-출석-조회시-회차-인덱스-오류-수정

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

출석조회 화면에서 세션 선택 전(혹은 세션 데이터 중복/공백 처리로 인해) 발생하던 중복 표시 및 세션 선택 상태 꼬임을 줄이기 위해, 세션 타이틀 정규화/중복 제거 및 기본 선택값 동기화를 추가한 PR입니다.

Changes:

  • Attendance에서 세션 옵션을 메모이즈하고, 유효한 기본 세션이 선택되도록 상태를 동기화
  • SessionSelectBox에서 세션 타이틀을 trim 기반으로 정규화하고 중복 옵션을 제거
  • SessionManage에서 세션 타이틀 정규화 및 attendanceId 기반 중복 제거 로직 추가

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
frontend/src/pages/Attendance.jsx 세션 옵션 계산/기본 선택 세션 동기화 및 옵션 없을 때 선택 비활성화 처리
frontend/src/components/attendance/SessionSelectBox.jsx 세션 타이틀 정규화/중복 제거 및 select value 안정화
frontend/src/components/attendance/SessionManage.jsx 세션 타이틀 정규화 및 목록 중복 제거(표시 중복 완화)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/components/attendance/SessionSelectBox.jsx`:
- Around line 4-5: normalizeSessionTitle currently returns '' for
non-string/empty titles which removes the "기타" option upstream; make it return
the same placeholder label used elsewhere (e.g. '기타') or use a shared
normalization util so SessionManage.jsx and Attendance.jsx see identical values.
Update normalizeSessionTitle (and the other occurrences at the 13-17 block) to
map empty or non-string sessionTitle values to the canonical "기타" token, or
refactor to import and use a shared function so the dropdown and auto-select
logic refer to the same normalized value.

In `@frontend/src/pages/Attendance.jsx`:
- Around line 20-29: sessionOptions is computed from attendanceSessions by
reading session.sessionTitle which doesn't exist in the real API shape (see
frontend/src/utils/attendanceList.js), so sessionOptions becomes empty and
disables <SessionSelectBox>; fix by deriving options from the actual field(s)
the API provides (or synthesize sessionTitle earlier) instead of
session.sessionTitle, update the useMemo that builds sessionOptions to use the
correct property (or map attendanceSessions to include a sessionTitle before
this component runs), and ensure the effect that clears selectedSession only
runs when there truly are no options so the SessionSelectBox isn't erroneously
disabled.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d8e65172-e888-4142-b2a6-e6f04c4e1abf

📥 Commits

Reviewing files that changed from the base of the PR and between 28a826c and 0c0771a.

📒 Files selected for processing (3)
  • frontend/src/components/attendance/SessionManage.jsx
  • frontend/src/components/attendance/SessionSelectBox.jsx
  • frontend/src/pages/Attendance.jsx

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
frontend/src/components/attendance/SessionSelectBox.jsx (2)

25-37: 빈 세션 목록 시 select 값 불일치 가능성

sessionList가 빈 배열인 경우 currentValue는 빈 문자열이 되지만, 렌더링되는 <option>이 없어 React controlled component 경고가 발생할 수 있습니다.

♻️ 제안: 빈 목록 처리 추가
       <select
         id="session-select"
         className={styles.session}
         value={currentValue}
         onChange={(event) => onChange?.(event.target.value)}
-        disabled={disabled}
+        disabled={disabled || sessionList.length === 0}
       >
+        {sessionList.length === 0 && (
+          <option value="">세션 없음</option>
+        )}
         {sessionList.map((item) => (
           <option key={item} value={item}>
             {item}
           </option>
         ))}
       </select>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/attendance/SessionSelectBox.jsx` around lines 25 -
37, The select can become a controlled component with no options when
sessionList is empty, causing React warnings; update the SessionSelectBox
rendering to handle empty sessionList by ensuring an option always exists (e.g.,
render a placeholder "<option value=\"\">No sessions</option>" when
sessionList.length === 0) and/or normalize currentValue before passing to the
select (use "" if currentValue is undefined/null or not in sessionList). Modify
the JSX that maps sessionList (and the value prop usage of currentValue) so the
select always has at least one option and the value is kept in sync.

17-18: 부모 컴포넌트와의 자동 선택 로직 중복

currentValuesessionList[0]으로 폴백하는 로직이 Attendance.jsxuseEffect(Line 31-42)에서도 동일하게 수행됩니다. 두 곳에서 자동 선택을 처리하면 초기 렌더링 시 불필요한 상태 업데이트가 발생할 수 있습니다.

현재 구조에서는 SessionSelectBoxcurrentValue를 내부적으로 계산하지만 onChange를 통해 부모에게 알리지 않으므로, 부모의 selectedSession과 실제 표시되는 값이 일시적으로 불일치할 수 있습니다.

♻️ 제안: 자동 선택 로직을 한 곳에서만 관리

부모 컴포넌트(Attendance.jsx)에서 자동 선택을 담당하고 있으므로, 여기서는 단순히 selectedSession을 그대로 사용하거나, 폴백 시 onChange를 호출하여 부모 상태를 동기화하는 것이 좋습니다:

- const currentValue = sessionList.includes(selectedSession) ? selectedSession : sessionList[0] || '';
+ // 부모에서 자동 선택을 처리하므로 여기서는 selectedSession 직접 사용
+ // sessionList에 없는 경우 빈 문자열로 처리 (부모 useEffect가 동기화)
+ const currentValue = sessionList.includes(selectedSession) ? selectedSession : '';

또는 useEffect를 추가하여 폴백 시 부모에게 알림:

useEffect(() => {
  if (sessionList.length > 0 && !sessionList.includes(selectedSession)) {
    onChange?.(sessionList[0]);
  }
}, [sessionList, selectedSession, onChange]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/attendance/SessionSelectBox.jsx` around lines 17 -
18, SessionSelectBox computes currentValue =
sessionList.includes(selectedSession) ? selectedSession : sessionList[0] || ''
causing duplicate auto-selection with Attendance.jsx; remove internal fallback
logic and either use selectedSession directly or, if you must fallback in the
child, notify the parent by calling onChange(sessionList[0]) so parent state
stays in sync. Update SessionSelectBox to stop mutating displayed value
independently (remove the fallback assignment to currentValue) and instead rely
on the parent's selectedSession or trigger onChange inside a useEffect when
sessionList changes and selectedSession is not present; reference the symbols
SessionSelectBox, currentValue, selectedSession, sessionList, onChange and the
parent's useEffect in Attendance.jsx when making the change.
frontend/src/utils/attendanceList.js (1)

10-41: 세션별 회차 조회 실패 시 로깅 부재

Promise.all 내부의 catch 블록에서 에러를 완전히 무시하고 있습니다. 특정 세션의 회차 조회가 실패해도 전체 기능은 동작하지만, 디버깅이 어려워질 수 있습니다.

♻️ 제안: 최소한의 에러 로깅 추가
       try {
         const rounds = await getRounds(sessionId);
         if (!Array.isArray(rounds)) return [];

         const sessionTitle = getSessionTitle(session);
         return rounds
           .filter((round) => !!round?.roundId)
           .map((round) => [
             round.roundId,
             {
               sessionTitle,
               roundDate: round.roundDate,
               roundStartAt: round.startAt || round.roundStartAt,
             },
           ]);
-      } catch {
+      } catch (err) {
+        console.warn(`세션 ${sessionId} 회차 조회 실패:`, err);
         return [];
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/utils/attendanceList.js` around lines 10 - 41, In
buildRoundMetaMap, do not silently swallow errors in the sessions.map catch for
getRounds; update that catch inside the async callback (the function passed to
sessions.map) to log the error along with identifying info (e.g., sessionId
and/or sessionTitle from getSessionId/getSessionTitle) before returning []; use
the existing logging mechanism (console.error or the app logger) so failures in
getRounds are recorded while preserving the current behavior of returning [] on
error.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/components/attendance/SessionManage.jsx`:
- Around line 78-105: The deduplication logic in visibleSessions skips sessions
missing attendanceId, causing duplicates and undefined React keys; update the
filter to compute a stable dedupe key for each session (e.g., const dedupeKey =
session.attendanceId ?? `${session.roundId ?? ""}-${session.userId ??
""}-${session.roundDate ?? ""}`) and use that key with seenAttendanceIds to
decide whether to push into deduplicated; ensure you add the original session
object (or attach the dedupeKey as a new property) to the deduplicated array so
the UI can use a stable key instead of undefined, keeping the rest of the
visibleSessions logic (normalizeSessionTitle, getTimestamp sorting) intact.

---

Nitpick comments:
In `@frontend/src/components/attendance/SessionSelectBox.jsx`:
- Around line 25-37: The select can become a controlled component with no
options when sessionList is empty, causing React warnings; update the
SessionSelectBox rendering to handle empty sessionList by ensuring an option
always exists (e.g., render a placeholder "<option value=\"\">No
sessions</option>" when sessionList.length === 0) and/or normalize currentValue
before passing to the select (use "" if currentValue is undefined/null or not in
sessionList). Modify the JSX that maps sessionList (and the value prop usage of
currentValue) so the select always has at least one option and the value is kept
in sync.
- Around line 17-18: SessionSelectBox computes currentValue =
sessionList.includes(selectedSession) ? selectedSession : sessionList[0] || ''
causing duplicate auto-selection with Attendance.jsx; remove internal fallback
logic and either use selectedSession directly or, if you must fallback in the
child, notify the parent by calling onChange(sessionList[0]) so parent state
stays in sync. Update SessionSelectBox to stop mutating displayed value
independently (remove the fallback assignment to currentValue) and instead rely
on the parent's selectedSession or trigger onChange inside a useEffect when
sessionList changes and selectedSession is not present; reference the symbols
SessionSelectBox, currentValue, selectedSession, sessionList, onChange and the
parent's useEffect in Attendance.jsx when making the change.

In `@frontend/src/utils/attendanceList.js`:
- Around line 10-41: In buildRoundMetaMap, do not silently swallow errors in the
sessions.map catch for getRounds; update that catch inside the async callback
(the function passed to sessions.map) to log the error along with identifying
info (e.g., sessionId and/or sessionTitle from getSessionId/getSessionTitle)
before returning []; use the existing logging mechanism (console.error or the
app logger) so failures in getRounds are recorded while preserving the current
behavior of returning [] on error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0e4b2ffd-706b-4df0-9a3a-6cd3187fa1db

📥 Commits

Reviewing files that changed from the base of the PR and between 0c0771a and f5bfbdf.

📒 Files selected for processing (5)
  • frontend/src/components/attendance/SessionManage.jsx
  • frontend/src/components/attendance/SessionSelectBox.jsx
  • frontend/src/pages/Attendance.jsx
  • frontend/src/utils/attendanceList.js
  • frontend/src/utils/normalizeSessionTitle.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/pages/Attendance.jsx

Copy link
Contributor

@discipline24 discipline24 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다~

@discipline24 discipline24 merged commit 80a562b into main Mar 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

❗ [버그][출석] 출석 조회시 회차 인덱스 오류 수정

3 participants