Skip to content

[REFACTOR] 지원폼 제출 관심사 분리 적용#164

Merged
aaaaaattt merged 21 commits intodevelopfrom
refacgtor/apply-soc#143
Oct 15, 2025
Merged

[REFACTOR] 지원폼 제출 관심사 분리 적용#164
aaaaaattt merged 21 commits intodevelopfrom
refacgtor/apply-soc#143

Conversation

@aaaaaattt
Copy link
Collaborator

@aaaaaattt aaaaaattt commented Oct 13, 2025

#️⃣연관된 이슈

📝작업 내용

feat:

  • 인터뷰 일정 업데이트 및 인덱스 차이 계산을 위한 유틸 함수 추가
  • formData에 interviewDateAnswer 필드 전송 조건 적용
  • selectedState 업데이트 유틸 함수 추가
  • mouseMove 이벤트 발생 시 드래그 상태를 관리하는 도메인 함수 추가
  • 일정 폼 값 업데이트 훅 개선

refactor:

  • 중복된 formData 변경 훅 제거
  • useReducer 사용으로 응집도 향상
  • 도메인 로직과 유틸 함수 분리
  • useReducer 초기 상태 반환 함수 생성
  • 드래그 상태 관련 도메인 로직 추출

버그 수정:

  • 마우스 클릭 시 indexDiffSign 초기화

스타일 변경:

  • timeSelector UI 변경
  • hover 애니메이션 반영 시간 조정

스크린샷 (선택)

화면 기록 2025-10-14 오후 3 20 24

💬리뷰 요구사항(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요
domain 로직을 util 부분과 분리해서 따로 폴더를 생성했습니다.
판단 기준은 범용성과 특정 도메인에 종속적인지 기준으로 판단했는데 추가 리뷰 남겨주시면 반영해보겠습니다.

Summary by CodeRabbit

  • New Features

    • Improved drag-to-select for interview time slots with more reliable multi-slot selection and immediate schedule updates after dragging.
  • Bug Fixes

    • Prevents creating empty interview-date entries when no times are selected.
  • Style

    • Enhanced time-slot visuals: clearer borders, hover feedback, transitions, and slightly smaller date/time text for readability.
  • Refactor

    • Internal drag-selection state simplified for more consistent selection behavior.

aaaaaattt added 17 commits October 13, 2025 16:34
- 입력값 : 현재 인터뷰 일정(날짜/시간) , 날짜, 선택된 시간
- 업데이트된 인터뷰 일정(날짜/시간)
- updateSchedule util 함수 적용
- hover 반응, border, color
- 기존 : 값이 없어도 [] 전송
- 현재 : 값이 존재하지 않으면 아예 전송X
- 마우스 상태에 따른 state를 일괄적으로 관리
- object -> state length 매개 변수function
- 범용성 & 특정 도메인에 종속적 여부로 판단
@aaaaaattt aaaaaattt self-assigned this Oct 13, 2025
@aaaaaattt aaaaaattt added 🐛 bug 버그 이슈 🛠️ Refactor 리팩터링 이슈 labels Oct 13, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 13, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Introduces reducer-driven drag-selection for interview scheduling: new drag types, utilities, and initial state; replaces legacy form updater with a hook that persists merged time intervals to react-hook-form; updates component usage and styles; adjusts DTO to omit empty interviewDateAnswer; renames useApplicationForm parameter.

Changes

Cohort / File(s) Summary
Drag types & initial state
src/pages/user/Apply/type/apply.ts, src/pages/user/Apply/constant/initialDragState.ts
Adds DragState/DragAction types and generateInitialDragState(statesLength) to produce default drag state arrays.
Drag domain & utils
src/pages/user/Apply/domain/drag.ts, src/pages/user/Apply/utils/drag.ts, src/pages/user/Apply/utils/math.ts
Adds updateDragState logic for start/isSelectionMode changes, updateSelectedState to compute inclusive selections, and index-diff helpers (getDiff/getIndexDiffSign).
Drag hook (reducer) & integration
src/pages/user/Apply/hook/useDragSelection.ts, src/pages/user/Apply/components/ApplicationForm/InterviewScheduleSelector.tsx
Replaces imperative selection with reducer-based useDragSelection(date, timeSlotsArray) returning states/dispatch; component switched to consume states.isSelectedStates and new handlers.
Form updater hook
src/pages/user/Apply/hook/useFormDataUpdate.ts, src/pages/user/Apply/hook/useUpdateFormData.ts
Adds useInterviewScheduleUpdater(date, timeSlotsArray) that merges intervals and sets form value; removes legacy useUpdateFormValue hook.
Schedule domain helper
src/pages/user/Apply/domain/schedule.ts
Adds updateSchedule(...) to upsert a date’s selectedTimes immutably.
Application form hook param rename
src/pages/user/Apply/hook/useApplicationForm.ts
Renames parameter from Id to clubId and updates internal refs and effect deps.
Styles & component tweaks
src/pages/user/Apply/components/ApplicationForm/index.styled.ts
TimeSpan styling: explicit border, color, borderRadius, transitions, hover behavior; DateText font size adjusted.
API DTO change
src/pages/user/Apply/api/apply.ts
applicationFormDto now conditionally omits interviewDateAnswer when empty (only append when interviewDateAnswer.length > 0).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Selector as InterviewScheduleSelector
  participant DragHook as useDragSelection (reducer)
  participant FormHook as useInterviewScheduleUpdater
  participant Domain as updateSchedule
  participant RHF as react-hook-form

  User->>Selector: mousedown / mousemove / mouseup
  Selector->>DragHook: handleMouseDown/Move/Up(event index)
  DragHook->>DragHook: reducer updates DragState\n(startIndex, hover, mode, isSelectedStates)
  Note over DragHook: on mouseup, final isSelectedStates produced
  DragHook->>FormHook: updateInterviewSchedule(isSelectedStates)
  FormHook->>Domain: updateSchedule(currentSchedules, date, mergedTimes)
  Domain-->>FormHook: newSchedules
  FormHook->>RHF: setValue('selectedInterviewSchedule', newSchedules)
  RHF-->>Selector: form state updated
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • ganimjeong

Poem

A rabbit clicks and drags with glee,
Hops through slots from A to B.
Reducers stitch the chosen time,
Form fields hum a tidy rhyme.
New hooks nibble bugs away—🥕

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The pull request includes style adjustments such as color, border, hover behavior, and font sizes in styled components, which are not part of the logic refactoring goals defined in the linked issue and thus fall outside the specified refactoring scope. To isolate concerns, consider extracting these UI style changes into a separate PR or updating the linked issue description to explicitly include these style enhancements.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title concisely summarizes the primary refactoring objective of separating concerns in the application form submission logic, using clear and relevant wording without extraneous details.
Linked Issues Check ✅ Passed The pull request addresses the linked issue’s objectives by integrating useReducer for drag selection cohesion, improving hook-based logic through a new schedule updater hook and cleaning duplicate hooks, and refactoring schedule state management into domain and utility modules.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refacgtor/apply-soc#143

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.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @aaaaaattt, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 지원 폼 제출 기능의 코드 구조를 리팩토링하고 사용자 경험을 개선하는 데 중점을 둡니다. 특히 인터뷰 일정 선택 로직을 도메인과 유틸리티 계층으로 분리하고 useReducer를 활용하여 상태 관리를 중앙화함으로써 코드의 응집도와 유지보수성을 크게 향상시켰습니다. 또한, 폼 데이터 전송 로직을 최적화하고 시간 선택기 UI를 개선하여 기능적 안정성과 시각적 완성도를 높였습니다.

Highlights

  • 관심사 분리 및 useReducer 도입: 드래그 선택 로직과 일정 업데이트 로직을 도메인 및 유틸리티 함수로 분리하고, 복잡한 드래그 선택 상태 관리를 위해 useReducer 훅을 도입하여 코드의 응집도와 유지보수성을 높였습니다.
  • 폼 데이터 처리 개선: 인터뷰 일정 답변 필드를 조건부로 전송하도록 applicationFormDto를 수정하고, 중복된 폼 데이터 업데이트 훅을 제거하여 폼 데이터 처리 로직을 최적화했습니다.
  • UI/UX 개선: 시간 선택기(timeSelector) UI를 변경하고 호버 애니메이션 시간을 조정하여 사용자 경험을 향상시켰습니다.
  • 버그 수정: 마우스 클릭 시 indexDiffSign이 올바르게 초기화되도록 수정하여 드래그 선택 기능의 안정성을 확보했습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

전반적으로 매우 훌륭한 리팩터링입니다. 지원서 제출 관련 로직의 관심사를 성공적으로 분리하셨습니다. 복잡한 드래그 상태 관리를 useReducer로 이전하여 응집도를 높이고, 도메인 로직과 유틸리티 함수를 분리하여 코드의 구조를 명확하게 만든 점이 인상적입니다. 특히 도메인 로직 분리 기준에 대해 고민하신 부분이 코드에 잘 드러나며, 유지보수성과 가독성이 크게 향상되었습니다. 몇 가지 추가적인 개선 제안을 포함했으니 확인 부탁드립니다.

onMouseUp={handleMouseUp}
>
<Text>{e[0] + '~' + e[1]}</Text>
<Text size='xs'>{e[0] + '~' + e[1]}</Text>
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

문자열을 연결할 때 + 연산자 대신 템플릿 리터럴(`${...}`)을 사용하면 코드가 더 깔끔하고 가독성이 좋아집니다.

Suggested change
<Text size='xs'>{e[0] + '~' + e[1]}</Text>
<Text size='xs'>{`${e[0]}~${e[1]}`}</Text>

Comment on lines 21 to 27
if (idxDiffSign < 0) {
newStartIndex = curtIdx + 1;
} else if (idxDiffSign > 0) {
newStartIndex = curtIdx - 1;
} else {
newStartIndex = curtIdx;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

newStartIndex를 계산하는 if/else if/else 블록은 newStartIndex = curtIdx - idxDiffSign; 한 줄로 간단하게 표현할 수 있습니다. idxDiffSign1, -1, 0인 모든 경우를 처리할 수 있어 코드가 더 간결해집니다.

    newStartIndex = curtIdx - idxDiffSign;

| { type: 'mouseDown'; index: number; isSelectionMode: boolean }
| { type: 'mouseMove'; index: number; indexDiffSign: number }
| { type: 'mouseUp' }
| { type: 'setInitialDragDirection'; indexDiffSign: number };
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

DragAction 타입에 정의된 setInitialDragDirection 액션 타입이 dragReducer에서 사용되지 않고 있습니다. 불필요한 코드는 혼란을 줄 수 있으므로 제거하는 것이 좋습니다.

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: 3

🧹 Nitpick comments (7)
src/pages/user/Apply/utils/math.ts (1)

9-15: Helpers are correct but consider simplifying.

The implementations are correct and compose well with the existing getSign function. However, getDiff is a thin wrapper around subtraction.

Consider simplifying getIndexDiffSign to remove the intermediate getDiff:

-export function getDiff(A: number, B: number): number {
-  return A - B;
-}
-
 export function getIndexDiffSign(currentSelectedIndex: number, lastHoveredIndex: number): Sign {
-  return getSign(getDiff(currentSelectedIndex, lastHoveredIndex));
+  return getSign(currentSelectedIndex - lastHoveredIndex);
 }

This reduces indirection while maintaining clarity. Keep the change if you prefer the explicit naming of getDiff for semantic clarity.

src/pages/user/Apply/hook/useApplicationForm.ts (1)

5-5: Rename fetchApplicationForm parameter to clubId
In src/pages/user/Apply/api/apply.ts (line 8), update the function signature from (Id: number) to (clubId: number) and adjust all internal references to match.

src/pages/user/Apply/domain/schedule.ts (2)

6-15: Param naming + redundant coalescing

  • Parameter name selectedTime (singular) holds an array; prefer selectedTimes for clarity.
  • selectedTime ?? [] is redundant because the param is typed as string[].

Consider this small cleanup:

 export const updateSchedule = (
   currentInterviewSchedule: PostInterviewSchedule[],
   date: string,
-  selectedTime: string[],
+  selectedTimes: string[],
 ): PostInterviewSchedule[] => {
@@
   const selectedInterviewSchedule: PostInterviewSchedule = {
-    date: date,
-    selectedTimes: selectedTime ?? [],
+    date,
+    selectedTimes,
   };

18-25: Option: drop empty selections instead of storing empty entries

If mergedTimes is empty, you may want to remove that date from the schedule to reduce payload and simplify DTO handling. Verify product intent.

   if (sameDateIndex !== -1) {
-    updatedSchedule = [...currentInterviewSchedule];
-    updatedSchedule[sameDateIndex] = selectedInterviewSchedule;
+    if (selectedInterviewSchedule.selectedTimes.length === 0) {
+      updatedSchedule = currentInterviewSchedule.filter((s) => s.date !== date);
+    } else {
+      updatedSchedule = [...currentInterviewSchedule];
+      updatedSchedule[sameDateIndex] = selectedInterviewSchedule;
+    }
   } else {
-    updatedSchedule = [...currentInterviewSchedule, selectedInterviewSchedule];
+    updatedSchedule =
+      selectedInterviewSchedule.selectedTimes.length === 0
+        ? currentInterviewSchedule
+        : [...currentInterviewSchedule, selectedInterviewSchedule];
   }
src/pages/user/Apply/hook/useFormDataUpdate.ts (2)

15-16: Mark form field dirty when updating schedules

Ensure formState reflects the update.

-    setValue('selectedInterviewSchedule', updatedSchedules);
+    setValue('selectedInterviewSchedule', updatedSchedules, { shouldDirty: true });

12-16: Optional: filter out empty dates before persisting

To avoid storing empty selections:

-    const updatedSchedules = updateSchedule(currentSchedules, date, mergedTimes);
-    setValue('selectedInterviewSchedule', updatedSchedules, { shouldDirty: true });
+    const updatedSchedules = updateSchedule(currentSchedules, date, mergedTimes);
+    const nonEmpty = updatedSchedules.filter((s) => s.selectedTimes.length > 0);
+    setValue('selectedInterviewSchedule', nonEmpty, { shouldDirty: true });
src/pages/user/Apply/domain/drag.ts (1)

9-13: Narrow idxDiffSign type and rename curtIdx for clarity

Constrain to -1 | 0 | 1 and use a clearer name.

-export const updateDragState = (
-  state: DragState,
-  curtIdx: number,
-  idxDiffSign: number,
-): UpdatedDragState => {
+export const updateDragState = (
+  state: DragState,
+  currIdx: number,
+  idxDiffSign: -1 | 0 | 1,
+): UpdatedDragState => {
-    if (idxDiffSign < 0) {
-      newStartIndex = curtIdx + 1;
-    } else if (idxDiffSign > 0) {
-      newStartIndex = curtIdx - 1;
-    } else {
-      newStartIndex = curtIdx;
+    if (idxDiffSign < 0) {
+      newStartIndex = currIdx + 1;
+    } else if (idxDiffSign > 0) {
+      newStartIndex = currIdx - 1;
+    } else {
+      newStartIndex = currIdx;
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce21771 and bbbdae9.

📒 Files selected for processing (13)
  • src/pages/user/Apply/api/apply.ts (2 hunks)
  • src/pages/user/Apply/components/ApplicationForm/InterviewScheduleSelector.tsx (2 hunks)
  • src/pages/user/Apply/components/ApplicationForm/index.styled.ts (2 hunks)
  • src/pages/user/Apply/constant/initialDragState.ts (1 hunks)
  • src/pages/user/Apply/domain/drag.ts (1 hunks)
  • src/pages/user/Apply/domain/schedule.ts (1 hunks)
  • src/pages/user/Apply/hook/useApplicationForm.ts (1 hunks)
  • src/pages/user/Apply/hook/useDragSelection.ts (1 hunks)
  • src/pages/user/Apply/hook/useFormDataUpdate.ts (1 hunks)
  • src/pages/user/Apply/hook/useUpdateFormData.ts (0 hunks)
  • src/pages/user/Apply/type/apply.ts (1 hunks)
  • src/pages/user/Apply/utils/drag.ts (1 hunks)
  • src/pages/user/Apply/utils/math.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/pages/user/Apply/hook/useUpdateFormData.ts
🧰 Additional context used
🧬 Code graph analysis (8)
src/pages/user/Apply/constant/initialDragState.ts (1)
src/pages/user/Apply/type/apply.ts (1)
  • DragState (71-80)
src/pages/user/Apply/hook/useApplicationForm.ts (2)
src/pages/user/Apply/components/ApplicationForm/index.tsx (1)
  • ApplicationForm (18-208)
src/pages/user/Apply/api/apply.ts (1)
  • fetchApplicationForm (8-14)
src/pages/user/Apply/domain/schedule.ts (1)
src/pages/user/Apply/type/apply.ts (1)
  • PostInterviewSchedule (56-59)
src/pages/user/Apply/hook/useFormDataUpdate.ts (3)
src/pages/user/Apply/type/apply.ts (2)
  • FormInputs (61-69)
  • PostInterviewSchedule (56-59)
src/pages/user/Apply/utils/time.ts (2)
  • convertSelectionToTimeInterval (58-74)
  • mergeContinuousTimeInterval (76-106)
src/pages/user/Apply/domain/schedule.ts (1)
  • updateSchedule (3-26)
src/pages/user/Apply/components/ApplicationForm/InterviewScheduleSelector.tsx (2)
src/pages/user/Apply/hook/useDragSelection.ts (1)
  • useDragSelection (71-118)
src/shared/components/Text/index.tsx (1)
  • Text (12-23)
src/pages/user/Apply/hook/useDragSelection.ts (6)
src/pages/user/Apply/type/apply.ts (2)
  • DragState (71-80)
  • DragAction (82-86)
src/pages/user/Apply/domain/drag.ts (1)
  • updateDragState (9-33)
src/pages/user/Apply/utils/drag.ts (1)
  • updateSelectedState (1-13)
src/pages/user/Apply/hook/useFormDataUpdate.ts (1)
  • useInterviewScheduleUpdater (6-19)
src/pages/user/Apply/constant/initialDragState.ts (1)
  • generateInitialDragState (3-14)
src/pages/user/Apply/utils/math.ts (1)
  • getIndexDiffSign (13-15)
src/pages/user/Apply/components/ApplicationForm/index.styled.ts (1)
src/styles/theme.ts (1)
  • theme (3-10)
src/pages/user/Apply/domain/drag.ts (1)
src/pages/user/Apply/type/apply.ts (1)
  • DragState (71-80)
🪛 GitHub Actions: Dongarium FE CI/CD
src/pages/user/Apply/hook/useDragSelection.ts

[error] 2-2: ESLint: '../utils/math import should occur after import of ../utils/drag' import/order

🪛 GitHub Check: lint
src/pages/user/Apply/hook/useFormDataUpdate.ts

[failure] 4-4:
../domain/schedule import should occur before type import of ../type/apply


[failure] 3-3:
../utils/time import should occur before type import of ../type/apply

src/pages/user/Apply/hook/useDragSelection.ts

[failure] 4-4:
../type/apply type import should occur after import of ../utils/drag


[failure] 2-2:
../utils/math import should occur after import of ../utils/drag

🔇 Additional comments (10)
src/pages/user/Apply/components/ApplicationForm/index.styled.ts (2)

99-107: LGTM! Enhanced drag selection UI feedback.

The styling improvements appropriately support the drag-and-select interface:

  • Proper border definition and radius for visual clarity
  • Smooth transitions for selection state changes
  • Interactive hover feedback that respects selection state
  • Color contrast improvement when selected

120-120: Font size reduction aligns with UI scale.

The change from base to sm appropriately scales the date text to match the time slot UI proportions.

src/pages/user/Apply/type/apply.ts (1)

71-86: LGTM! Well-structured drag state types.

The type definitions appropriately support reducer-driven drag handling:

  • DragState captures all necessary drag interaction state
  • DragAction uses discriminated union pattern for type-safe action handling
  • Properties are well-named and comprehensive
src/pages/user/Apply/constant/initialDragState.ts (1)

3-14: LGTM! Clean initial state generator.

The function correctly initializes all drag state properties with appropriate defaults:

  • Index values at -1 indicate no selection
  • Boolean flags properly initialized to false
  • Array length matches the provided statesLength
src/pages/user/Apply/utils/drag.ts (1)

1-13: LGTM! Clean range selection utility.

The implementation correctly handles bidirectional selection:

  • Math.min/Math.max normalize the range direction
  • Immutable update via map
  • Inclusive range logic is appropriate for UI selection
src/pages/user/Apply/components/ApplicationForm/InterviewScheduleSelector.tsx (3)

10-13: LGTM! Clean integration with refactored drag hook.

The component correctly integrates with the new useDragSelection hook, receiving both state and handlers for the drag-and-select interaction.


23-23: Selection state properly sourced from hook.

The change from selectedTime[idx] to states.isSelectedStates[idx] correctly uses the state from the refactored drag selection hook.


28-28: Text size aligns with styling updates.

The size='xs' prop matches the font size reduction in the styled component (Line 120 of index.styled.ts).

src/pages/user/Apply/api/apply.ts (1)

38-42: Good bug fix: avoid sending empty interview dates.

The conditional logic correctly prevents appending an empty interviewDateAnswer to the form data, avoiding unnecessary empty fields in the API request.

src/pages/user/Apply/hook/useDragSelection.ts (1)

109-110: Order of operations on mouseUp is fine; state used is stable

Calling updateInterviewSchedule after dispatch('mouseUp') is safe since mouseUp doesn’t modify isSelectedStates. LGTM.

Comment on lines 1 to 9
import { useReducer } from 'react';
import { getIndexDiffSign } from '../utils/math';
import { useInterviewScheduleUpdater } from './useFormDataUpdate';
import type { DragAction, DragState } from '../type/apply';

export function useDragSelection(
updateScheduleData: (data: string, mergedInterviewTime: string[]) => void,
date: string,
timeIntervalArray: [string, string][],
) {
const [prevDiffSign, setPrevDiffSign] = useState<Sign>(0);
const startIndex = useRef<string | undefined>('');
const lastHoveredIndex = useRef<string | null>(null);
const mode = useRef<boolean>(false);
const [isDragging, setIsDragging] = useState<boolean>(false);
const [isMouseDown, setIsMouseDown] = useState<boolean>(false);
const selectedIndex = useRef<string | undefined>('');
const [selectedTime, setSelectedTime] = useState<boolean[]>(() =>
new Array(timeIntervalArray.length).fill(false),
);

function handleIndexChange(newIndex: number) {
const diff = newIndex - Number(lastHoveredIndex.current);
const currentSign = getSign(diff);
import { generateInitialDragState } from '../constant/initialDragState';
import { updateDragState } from '../domain/drag';
import { updateSelectedState } from '../utils/drag';

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix import order (ESLint import/order) and add MouseEvent type

Reorder imports to place ../utils/drag before ../utils/math and keep type-only imports after value imports. Also import MouseEvent type to avoid relying on global React namespace.

-import { useReducer } from 'react';
-import { getIndexDiffSign } from '../utils/math';
-import { useInterviewScheduleUpdater } from './useFormDataUpdate';
-import type { DragAction, DragState } from '../type/apply';
-
-import { generateInitialDragState } from '../constant/initialDragState';
-import { updateDragState } from '../domain/drag';
-import { updateSelectedState } from '../utils/drag';
+import { useReducer } from 'react';
+import type { MouseEvent } from 'react';
+import { updateSelectedState } from '../utils/drag';
+import { getIndexDiffSign } from '../utils/math';
+import { useInterviewScheduleUpdater } from './useFormDataUpdate';
+import { generateInitialDragState } from '../constant/initialDragState';
+import { updateDragState } from '../domain/drag';
+import type { DragAction, DragState } from '../type/apply';
📝 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
import { useReducer } from 'react';
import { getIndexDiffSign } from '../utils/math';
import { useInterviewScheduleUpdater } from './useFormDataUpdate';
import type { DragAction, DragState } from '../type/apply';
export function useDragSelection(
updateScheduleData: (data: string, mergedInterviewTime: string[]) => void,
date: string,
timeIntervalArray: [string, string][],
) {
const [prevDiffSign, setPrevDiffSign] = useState<Sign>(0);
const startIndex = useRef<string | undefined>('');
const lastHoveredIndex = useRef<string | null>(null);
const mode = useRef<boolean>(false);
const [isDragging, setIsDragging] = useState<boolean>(false);
const [isMouseDown, setIsMouseDown] = useState<boolean>(false);
const selectedIndex = useRef<string | undefined>('');
const [selectedTime, setSelectedTime] = useState<boolean[]>(() =>
new Array(timeIntervalArray.length).fill(false),
);
function handleIndexChange(newIndex: number) {
const diff = newIndex - Number(lastHoveredIndex.current);
const currentSign = getSign(diff);
import { generateInitialDragState } from '../constant/initialDragState';
import { updateDragState } from '../domain/drag';
import { updateSelectedState } from '../utils/drag';
// src/pages/user/Apply/hook/useDragSelection.ts
import { useReducer } from 'react';
import type { MouseEvent } from 'react';
import { updateSelectedState } from '../utils/drag';
import { getIndexDiffSign } from '../utils/math';
import { useInterviewScheduleUpdater } from './useFormDataUpdate';
import { generateInitialDragState } from '../constant/initialDragState';
import { updateDragState } from '../domain/drag';
import type { DragAction, DragState } from '../type/apply';
// …rest of the file…
🧰 Tools
🪛 GitHub Actions: Dongarium FE CI/CD

[error] 2-2: ESLint: '../utils/math import should occur after import of ../utils/drag' import/order

🪛 GitHub Check: lint

[failure] 4-4:
../type/apply type import should occur after import of ../utils/drag


[failure] 2-2:
../utils/math import should occur after import of ../utils/drag

🤖 Prompt for AI Agents
In src/pages/user/Apply/hook/useDragSelection.ts around lines 1 to 9, the
imports are misordered and the MouseEvent type is missing; reorder so value
imports come first, move "../utils/drag" before "../utils/math", place type-only
imports (like DragAction, DragState) after the value imports, and add an
explicit type import for MouseEvent from 'react' (import type { MouseEvent }
from 'react') to avoid relying on the global React namespace.

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: 3

♻️ Duplicate comments (5)
src/pages/user/Apply/domain/drag.ts (1)

21-27: Simplify start-index calculation

Equivalent, shorter, and less error-prone.

-    if (idxDiffSign < 0) {
-      newStartIndex = currentIdx + 1;
-    } else if (idxDiffSign > 0) {
-      newStartIndex = currentIdx - 1;
-    } else {
-      newStartIndex = currentIdx;
-    }
+    newStartIndex = currentIdx - idxDiffSign;
src/pages/user/Apply/hook/useDragSelection.ts (4)

1-9: Import MouseEvent type and useEffect; keep import order sane

Prevents reliance on global React namespace and enables reset effect.

-import { useReducer } from 'react';
+import { useEffect, useReducer } from 'react';
+import type { MouseEvent } from 'react';

1-9: Import order now looks correct; ensure lint passes

Looks aligned with prior lint feedback; adding the type-only import may require keeping it after value imports as shown.


88-101: De-duplicate index lookups and guard invalid state on mouseMove

Prevents unnecessary work and invalid dispatch.

-  const handleMouseMove = (e: React.MouseEvent<HTMLSpanElement>) => {
-    if (
-      !e.currentTarget.dataset.index ||
-      getSelectedIndex(e) === states.lastHoveredIndex ||
-      !states.isMouseDown
-    )
-      return;
-    dispatch({
-      type: 'mouseMove',
-      index: getSelectedIndex(e),
-      indexDiffSign: getIndexDiffSign(getSelectedIndex(e), states.lastHoveredIndex),
-    });
+  const handleMouseMove = (e: MouseEvent<HTMLSpanElement>) => {
+    const index = getSelectedIndex(e);
+    if (!e.currentTarget.dataset.index || index === states.lastHoveredIndex || !states.isMouseDown)
+      return;
+    dispatch({
+      type: 'mouseMove',
+      index,
+      indexDiffSign: getIndexDiffSign(index, states.lastHoveredIndex),
+    });
   };

79-86: Fix NaN risk on mouseDown and avoid duplicate lookups

Single parse, validate, then dispatch.

-  const handleMouseDown = (e: React.MouseEvent<HTMLSpanElement>) => {
+  const handleMouseDown = (e: MouseEvent<HTMLSpanElement>) => {
     e.preventDefault();
-    dispatch({
-      type: 'mouseDown',
-      index: getSelectedIndex(e),
-      isSelectionMode: !states.isSelectedStates[getSelectedIndex(e)],
-    });
+    const index = getSelectedIndex(e);
+    if (index < 0) return;
+    dispatch({
+      type: 'mouseDown',
+      index,
+      isSelectionMode: !states.isSelectedStates[index],
+    });
   };
🧹 Nitpick comments (4)
src/pages/user/Apply/type/apply.ts (2)

71-80: Narrow previousIndexDiffSign to a sign union

Constrain to -1 | 0 | 1 for type-safety and to match getIndexDiffSign.

+export type IndexDiffSign = -1 | 0 | 1;
 export type DragState = {
   startIndex: number;
   lastHoveredIndex: number;
   currentSelectedIndex: number;
   isSelectionMode: boolean;
   isSelectedStates: boolean[];
   isMouseDown: boolean;
   isDragging: boolean;
-  previousIndexDiffSign: null | number;
+  previousIndexDiffSign: null | IndexDiffSign;
 };

82-85: Constrain indexDiffSign and add reset action to support re-init

Keeps action payloads precise and enables state reset when date/slots change.

-export type DragAction =
-  | { type: 'mouseDown'; index: number; isSelectionMode: boolean }
-  | { type: 'mouseMove'; index: number; indexDiffSign: number }
-  | { type: 'mouseUp' };
+export type DragAction =
+  | { type: 'mouseDown'; index: number; isSelectionMode: boolean }
+  | { type: 'mouseMove'; index: number; indexDiffSign: IndexDiffSign }
+  | { type: 'mouseUp' }
+  | { type: 'reset'; statesLength: number };
src/pages/user/Apply/domain/drag.ts (2)

1-1: Import the sign type for consistency

Aligns with DragAction/State narrowed types.

-import type { DragState } from '../type/apply';
+import type { DragState, IndexDiffSign } from '../type/apply';

9-17: Narrow idxDiffSign and return type fields to the sign union

Improves correctness guarantees.

 export const updateDragState = (
   state: DragState,
   currentIdx: number,
-  idxDiffSign: number,
+  idxDiffSign: IndexDiffSign,
 ): UpdatedDragState => {
   let newStartIndex = state.startIndex;
   let newIsSelectionMode = state.isSelectionMode;
-  let newPreviousIndexDiffSign = state.previousIndexDiffSign;
+  let newPreviousIndexDiffSign: IndexDiffSign | null = state.previousIndexDiffSign;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bbbdae9 and f8f51a2.

📒 Files selected for processing (5)
  • src/pages/user/Apply/components/ApplicationForm/InterviewScheduleSelector.tsx (2 hunks)
  • src/pages/user/Apply/domain/drag.ts (1 hunks)
  • src/pages/user/Apply/hook/useDragSelection.ts (1 hunks)
  • src/pages/user/Apply/hook/useFormDataUpdate.ts (1 hunks)
  • src/pages/user/Apply/type/apply.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/pages/user/Apply/hook/useFormDataUpdate.ts
  • src/pages/user/Apply/components/ApplicationForm/InterviewScheduleSelector.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
src/pages/user/Apply/domain/drag.ts (1)
src/pages/user/Apply/type/apply.ts (1)
  • DragState (71-80)
src/pages/user/Apply/hook/useDragSelection.ts (6)
src/pages/user/Apply/type/apply.ts (2)
  • DragState (71-80)
  • DragAction (82-85)
src/pages/user/Apply/domain/drag.ts (1)
  • updateDragState (9-33)
src/pages/user/Apply/utils/drag.ts (1)
  • updateSelectedState (1-13)
src/pages/user/Apply/hook/useFormDataUpdate.ts (1)
  • useInterviewScheduleUpdater (6-19)
src/pages/user/Apply/constant/initialDragState.ts (1)
  • generateInitialDragState (3-14)
src/pages/user/Apply/utils/math.ts (1)
  • getIndexDiffSign (13-15)

Comment on lines +10 to +12
function getSelectedIndex(e: React.MouseEvent<HTMLSpanElement>) {
return Number(e.currentTarget.dataset.index);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Guard invalid index in getSelectedIndex

Avoids NaN array indexing and dispatch with bad payloads.

-function getSelectedIndex(e: React.MouseEvent<HTMLSpanElement>) {
-  return Number(e.currentTarget.dataset.index);
-}
+function getSelectedIndex(e: MouseEvent<HTMLSpanElement>) {
+  const raw = e.currentTarget.dataset.index;
+  const idx = raw === undefined ? NaN : Number.parseInt(raw, 10);
+  return Number.isFinite(idx) ? idx : -1;
+}
📝 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
function getSelectedIndex(e: React.MouseEvent<HTMLSpanElement>) {
return Number(e.currentTarget.dataset.index);
}
function getSelectedIndex(e: MouseEvent<HTMLSpanElement>) {
const raw = e.currentTarget.dataset.index;
const idx = raw === undefined ? NaN : Number.parseInt(raw, 10);
return Number.isFinite(idx) ? idx : -1;
}
🤖 Prompt for AI Agents
In src/pages/user/Apply/hook/useDragSelection.ts around lines 10–12,
getSelectedIndex currently does Number(...) on dataset.index which can produce
NaN and lead to invalid array indexing/dispatch; change it to parse the value
(e.g. const idx = parseInt(e.currentTarget.dataset.index ?? "", 10)), validate
with Number.isFinite(idx) / !Number.isNaN(idx) (or Number.isInteger), and return
a safe sentinel (null or -1) when invalid; also update any callers to check for
the sentinel before indexing or dispatching to avoid using NaN as an index.

Comment on lines +14 to +69
function dragReducer(state: DragState, action: DragAction) {
switch (action.type) {
case 'mouseDown': {
const newSelectedStates: boolean[] = [...state.isSelectedStates];
newSelectedStates[action.index] = action.isSelectionMode;

return {
...state,
startIndex: action.index,
currentSelectedIndex: action.index,
lastHoveredIndex: action.index,
isSelectionMode: action.isSelectionMode,
isSelectedStates: newSelectedStates,
isMouseDown: true,
previousIndexDiffSign: null,
};
}
case 'mouseMove': {
const currentIndex = action.index;
const indexDiffSign = action.indexDiffSign;

const { newStartIndex, newIsSelectionMode, newPreviousIndexDiffSign } = updateDragState(
state,
currentIndex,
indexDiffSign,
);

const newSelectedStates = updateSelectedState(
state.isSelectedStates,
newStartIndex,
currentIndex,
newIsSelectionMode,
);

return {
...state,
isDragging: true,
currentSelectedIndex: currentIndex,
lastHoveredIndex: currentIndex,
isSelectedStates: newSelectedStates,
isSelectionMode: newIsSelectionMode,
startIndex: newStartIndex,
previousIndexDiffSign: newPreviousIndexDiffSign,
};
}
case 'mouseUp': {
return {
...state,
isMouseDown: false,
isDragging: false,
};
}
setPrevDiffSign(currentSign);
default:
return state;
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add reset action handling to reducer

Supports safe re-init when date/slots change.

 function dragReducer(state: DragState, action: DragAction) {
   switch (action.type) {
+    case 'reset': {
+      return generateInitialDragState(action.statesLength);
+    }
📝 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
function dragReducer(state: DragState, action: DragAction) {
switch (action.type) {
case 'mouseDown': {
const newSelectedStates: boolean[] = [...state.isSelectedStates];
newSelectedStates[action.index] = action.isSelectionMode;
return {
...state,
startIndex: action.index,
currentSelectedIndex: action.index,
lastHoveredIndex: action.index,
isSelectionMode: action.isSelectionMode,
isSelectedStates: newSelectedStates,
isMouseDown: true,
previousIndexDiffSign: null,
};
}
case 'mouseMove': {
const currentIndex = action.index;
const indexDiffSign = action.indexDiffSign;
const { newStartIndex, newIsSelectionMode, newPreviousIndexDiffSign } = updateDragState(
state,
currentIndex,
indexDiffSign,
);
const newSelectedStates = updateSelectedState(
state.isSelectedStates,
newStartIndex,
currentIndex,
newIsSelectionMode,
);
return {
...state,
isDragging: true,
currentSelectedIndex: currentIndex,
lastHoveredIndex: currentIndex,
isSelectedStates: newSelectedStates,
isSelectionMode: newIsSelectionMode,
startIndex: newStartIndex,
previousIndexDiffSign: newPreviousIndexDiffSign,
};
}
case 'mouseUp': {
return {
...state,
isMouseDown: false,
isDragging: false,
};
}
setPrevDiffSign(currentSign);
default:
return state;
}
}
function dragReducer(state: DragState, action: DragAction) {
switch (action.type) {
case 'reset': {
return generateInitialDragState(action.statesLength);
}
case 'mouseDown': {
const newSelectedStates: boolean[] = [...state.isSelectedStates];
newSelectedStates[action.index] = action.isSelectionMode;
return {
...state,
startIndex: action.index,
currentSelectedIndex: action.index,
lastHoveredIndex: action.index,
isSelectionMode: action.isSelectionMode,
isSelectedStates: newSelectedStates,
isMouseDown: true,
previousIndexDiffSign: null,
};
}
case 'mouseMove': {
const currentIndex = action.index;
const indexDiffSign = action.indexDiffSign;
const { newStartIndex, newIsSelectionMode, newPreviousIndexDiffSign } = updateDragState(
state,
currentIndex,
indexDiffSign,
);
const newSelectedStates = updateSelectedState(
state.isSelectedStates,
newStartIndex,
currentIndex,
newIsSelectionMode,
);
return {
...state,
isDragging: true,
currentSelectedIndex: currentIndex,
lastHoveredIndex: currentIndex,
isSelectedStates: newSelectedStates,
isSelectionMode: newIsSelectionMode,
startIndex: newStartIndex,
previousIndexDiffSign: newPreviousIndexDiffSign,
};
}
case 'mouseUp': {
return {
...state,
isMouseDown: false,
isDragging: false,
};
}
default:
return state;
}
}
🤖 Prompt for AI Agents
In src/pages/user/Apply/hook/useDragSelection.ts around lines 14 to 69, the
reducer lacks a 'reset' action to safely reinitialize drag state when dates or
slots change; add a 'case "reset"' that returns a fresh state (either from
action.payload or by constructing an initial state) resetting startIndex,
currentSelectedIndex, lastHoveredIndex to null,
isSelectionMode/isMouseDown/isDragging to false, previousIndexDiffSign to null,
and isSelectedStates to the new selection array provided in the payload (or
recomputed), ensuring you return a new object (no in-place mutation) so the hook
fully re-initializes on date/slot changes.

Comment on lines +71 to +77
export function useDragSelection(date: string, timeIntervalArray: [string, string][]) {
const { updateInterviewSchedule } = useInterviewScheduleUpdater(date, timeIntervalArray);

const newValue = [...selectedTime];
newValue[Number(startIndex.current)] = mode.current;
const [states, dispatch] = useReducer(
dragReducer,
generateInitialDragState(timeIntervalArray.length),
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Reset reducer when date or slots length change

Prevents length mismatch and out-of-bounds updates after switching days or slot lists.

 export function useDragSelection(date: string, timeIntervalArray: [string, string][]) {
   const { updateInterviewSchedule } = useInterviewScheduleUpdater(date, timeIntervalArray);

   const [states, dispatch] = useReducer(
     dragReducer,
     generateInitialDragState(timeIntervalArray.length),
   );
+
+  useEffect(() => {
+    dispatch({ type: 'reset', statesLength: timeIntervalArray.length });
+  }, [date, timeIntervalArray.length]);
📝 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
export function useDragSelection(date: string, timeIntervalArray: [string, string][]) {
const { updateInterviewSchedule } = useInterviewScheduleUpdater(date, timeIntervalArray);
const newValue = [...selectedTime];
newValue[Number(startIndex.current)] = mode.current;
const [states, dispatch] = useReducer(
dragReducer,
generateInitialDragState(timeIntervalArray.length),
);
export function useDragSelection(date: string, timeIntervalArray: [string, string][]) {
const { updateInterviewSchedule } = useInterviewScheduleUpdater(date, timeIntervalArray);
const [states, dispatch] = useReducer(
dragReducer,
generateInitialDragState(timeIntervalArray.length),
);
useEffect(() => {
dispatch({ type: 'reset', statesLength: timeIntervalArray.length });
}, [date, timeIntervalArray.length]);
// …rest of your hook…
}
🤖 Prompt for AI Agents
In src/pages/user/Apply/hook/useDragSelection.ts around lines 71-77, the reducer
state can get out-of-sync when the date or the number of time slots changes; add
logic to reset the reducer whenever date or timeIntervalArray.length changes.
Implement a RESET (or REINITIALIZE) action in dragReducer that replaces state
with generateInitialDragState(timeIntervalArray.length), then add a useEffect
that watches [date, timeIntervalArray.length] and dispatches that RESET action
with the freshly generated initial state; alternatively, if dragReducer already
supports a reset action name, dispatch that action from the effect. Ensure the
effect runs on mount and when those dependencies change so state length always
matches slots.

Copy link
Contributor

Choose a reason for hiding this comment

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

updateDragState랑 utils의 updateSelectedState 함수들은 useDragSelection훅 파일 내부에만 사용이 될 것 같고 함께 두면 응집도도 높아지고 나중에 수정할 때도 한 파일에 모여 있어 코드를 찾기 쉬울 것 같은데 분리하신 이유가 무엇인지 궁금합니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

말씀하신대로 훅 내부에서만 사용되는 함수이고, 같이 두면 찾기 편한 측면은 분명히 존재하는 것 같습니다!
그런데 제가 생각하기에는 응집도는 사용되는 로직들이 함께 위치한다고 높아지는 것은 아니고 수행하는 기능 측면에서 생각하는게 맞다고 생각했습니다.

updateSelectedState는 배열, 시작인덱스, 종료 인덱스만 매개변수로 넣으면 상태 변환된 배열을 출력해주는 함수이고, 드래그 뿐만 아니라 배열의 특정 범위를 바꾸는 영역에서 추가로 사용할 수 있으므로 util 영역이 적절하다고 판단했고,

useDragSelection은 "드래그 방향이 바뀌면 선택/해제를 반대로 한다"는 면접 일정을 선택할 때만 반영되는 어떤 규칙이 반영된 부분이라 util이 아닌 곳에 두어야 한다고 판단했습니다. 지난번에 멘토님이 보내주신 FSD 관련 구조 뿐만 아니라 다수의 프로젝트에서 도메인/비즈니스 로직 등을 따로 관리하는 게 일반적이라 현재 프로젝트에도 domain 폴더가 적합해 보여서 추가했습니다.

결과적으로 useDragSelection 훅 내부에서 util 함수와 domain 함수를 불러서 사용해도 기능 측면에서 응집도는 유지된다고 생각했습니다.

Comment on lines +9 to +19
const updateInterviewSchedule = (isSelectedStates: boolean[]) => {
const selectedTimes = convertSelectionToTimeInterval(isSelectedStates, timeSlotsArray);
const mergedTimes = mergeContinuousTimeInterval(selectedTimes);
const currentSchedules: PostInterviewSchedule[] = getValues('selectedInterviewSchedule') || [];

const updatedSchedules = updateSchedule(currentSchedules, date, mergedTimes);
setValue('selectedInterviewSchedule', updatedSchedules);
};

return { updateInterviewSchedule };
}
Copy link
Contributor

Choose a reason for hiding this comment

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

updateInterviewSchedule을 [date, timeSlotsArray, getValues, setValue] 의존성 배열로 useCallback으로 감싸는 것도 괜찮을 것 같은데 어떠신가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

이 부분이 조금 헷갈리는데, useCallback으로 내부를 감싸도


export const InterviewScheduleSelector = ({ availableTime, date }: InterviewSchedule) => {
  const timeSlotsArray: [string, string][] = getTimeSlotsArray(availableTime);

  const { handleMouseDown, handleMouseMove, handleMouseUp, states } = useDragSelection(
    date,
    timeSlotsArray,
  );

상위 component가 렌더링 되면 -> hook이 계속 호출되는 구조 -> useCallback에 기대했던 역할을 수행 불가라고 생각했는데,
그래서 리팩토링 과정에서 상위 컴포넌트의 props를 오히려 memo를 통해 매개변수로 넣어야되지 않을까 생각했는데 어떤 게 맞을까요?

Copy link
Contributor

Choose a reason for hiding this comment

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

오 그렇네요!
말씀하신 대로 상위 컴포넌트가 리렌더링되면서 매번 새로운 props를 생성하니까 useCallback을 해도 효과가 별로 없네요
먼저 props나 timeSlotsArray를 useMemo로 고정한 뒤 useCallback을 함께 적용하는 게 더 나은 방법인 것 같아요

@aaaaaattt aaaaaattt merged commit e448b55 into develop Oct 15, 2025
11 checks passed
@aaaaaattt aaaaaattt deleted the refacgtor/apply-soc#143 branch October 15, 2025 01:17
ganimjeong pushed a commit that referenced this pull request Oct 27, 2025
ganimjeong pushed a commit that referenced this pull request Oct 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 bug 버그 이슈 🛠️ Refactor 리팩터링 이슈

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REFACTOR] 지원폼 제출 컴포넌트 관심사 분리

3 participants