Skip to content

Conversation

@KimDongGyun23
Copy link
Collaborator

@KimDongGyun23 KimDongGyun23 commented Jan 10, 2026

#️⃣ 연관된 이슈 번호

관련된 이슈 번호를 작성하고, 자동으로 이슈를 닫으려면 Closes #이슈번호 형식으로 작성해주세요.


⏰ 작업 시간

예상 작업 시간과 실제 작업 시간을 작성해주세요.
두 시간이 다를 경우, 그 이유를 함께 설명해주세요.

  • 예상 작업 시간 : 1 h
  • 실제 작업 시간 : 5 h

구조에 대한 고민과 폼 유효성 검사 방식에 대한 고민으로 인해 시간이 지체되었습니다.


📝 작업 내용

이번 PR(작업)을 통해 수행한 주요 내용을 구체적으로 작성해주세요.

Important

아직 merge 되지 않은 PR에서 구현된 훅과 컴포넌트를 임시로 common.tsx 에 모아 사용하고 있으며, 해당 PR들이 merge된 이후에는 실제 공용 파일들로 교체할 예정입니다. 따라서 리뷰 시에는 common.tsx를 제외하고, 테스트 파일는 선택적으로 확인하시면 될 것 같습니다.


투표 모달/옵션 UI 변경

  • “투표 생성”과 “투표 수정”을 위한 모달 컴포넌트를 새로 구현하여, 제목/선택지/제한 시간 입력과 제출 기능을 포합하도록 했습니다.

  • 모달 내에서 폼 제출 후에는 모달을 닫고, 기존 입력값을 초기화하도록 동작을 정의했습니다.

    • ​투표 생성 폼을 제출할 때에는 폼을 초기화합니다.
    • 투표 수정 폼을 제출할 때에는 폼을 초기화 하지 않습니다. 새롭게 모달이 열릴 때 넘겨받은 초기값으로 초기화가 되기 때문입니다.
    • 폼 제출이 아닌 닫기 버튼 클릭 시에는, 기존 입력 내용이 그대로 남아있도록 하였습니다. 추후 초기화 버튼을 만든다거나, 기존 입력 내용을 제거하는 방식 중에 고려해보면 좋을 것 같습니다.

투표 옵션 리스트 구조 재구성

  • PollOptionList, PollOptionItem 컴포넌트를 폼과 직접 연결되도록 수정하여, react-hook-form의 필드 배열을 통해 옵션 추가/삭제/수정을 처리하도록 바꿨습니다.​

  • 이전에 옵션 상태를 관리하던 훅인 usePollOptions는 react-hook-form의 도입으로 더 이상 필요 없어 삭제했습니다.


공용 폼 UI 컴포넌트 추가 (FormField 컴포넌트)

  • 폼 섹션을 구성하기 위한 FormField 컴포넌트를 추가하고, 내부에 Label, Input, HelpText, Error 등을 컴파운드 패턴으로 제공하도록 구현했습니다.

  • 이 컴포넌트들을 조합해서 각 화면(모달, 페이지 등)에서 레이아웃/간격을 자유롭게 구성할 수 있습니다. CreatePollModal 등 기존 폼 UI 일부를 해당 구조로 리팩토링 하였습니다.


모달 공통 컴포넌트 개선

  • 공통 모달 컴포넌트에 모달 제목과 닫기 버튼을 마찬가지로 컴파운드 패턴으로 제공하였습니다. 반복된다는 점과 또 다른 확장성을 고려한다면 효과적일 것이라 판단했습니다.

  • role="dialog", aria-modal="true"를 실제 다이얼로그 요소에 부여하고, 오버레이와 콘텐츠 레이아웃을 수정하여 잘못된 접근성을 개선하였습니다.


관련된 스크린샷이나 캡처 화면을 첨부해주세요.

2026-01-11.1.35.23.mov

변경된 부분은 추가된 테스트를 포함해 모두 통과했습니다.

스크린샷 2026-01-11 오후 1 33 39

🪏 주요 고민과 해결 과정

작업 중 겪은 문제나 고민, 그리고 그에 대한 해결 과정을 정리해주세요.
관련 트러블슈팅 문서가 있다면 링크로 연결해주세요.

폼 유효성 검사 로직

(deprecated)

react-hook-form 이나 zod 같은 라이브러리를 도입하지 않고도, 간단하면서도 재사용 가능한 폼 유효성 검사 방식을 설계하는 방법을 고민했습니다.

필드별 규칙과 에러 메시지는 CreatePollFormValidator 내부에서 관리하고, 모달에서는 isValid / validate 인터페이스만 사용하도록 분리해 추후 제약 조건 변경이나 메시지 변경 시 영향 범위를 최소화했습니다.


react-hook-form과 zod를 도입했습니다. 기존에는 폼 수가 많지 않고, 직접 유효성 검사 로직을 구현해도 충분히 대체 가능하다고 판단했습니다. 하지만 실제로는 기존에 구현된 공통 컴포넌트들과 폼을 연결 시키는 과정, 검증 로직을 자연스럽게 녹여내는 과정에서 어려움을 겪었습니다.

초기 폼 구조 설계를 잘못한 부분도 있었고, 컴포넌트를 잘못 구현한 부분도 있었다고 생각합니다. 이를 깔끔하게 재구성할 더 나은 구조를 떠올리지 못했습니다. 그래서 검증과 상태 관리를 검증된 라이브러리에 위임하고자했습니다.

react-hook-form에서 직접 폼의 상태를 관리하고, 기존 작성된 컴포넌트를 수정하지 않고도 폼의 관리가 가능했습니다. 이를 통해 복잡한 상태 관리 로직을 제거할 수 있었고, 코드가 더욱 직관적으로 변하였습니다. zod를 통하여 폼의 스키마를 더욱 간결하게 관리할 수 있으며, 직관적으로 파악할 수 있었습니다. 또한, 스키마 기반으로 검증과 타입 추론이 가능해 편리해졌습니다.

컴포넌트의 어떤 부분에서 설계가 잘못되었을지 다시 한번 정리해보는 시간을 가져보려합니다. 또한, 라이브러리 없이 어떻게 구현할 수 있었을지 고민해본 후 다시 자료를 공유해보겠습니다.


FormField 컴포넌트 구현

기존 폼에서는 section 태그와 라벨 구조가 반복되어서, 이를 공통으로 사용하기 위해 FormSection 컴포넌트를 구현하여 사용했습니다.

/**
 * 폼 섹션 컴포넌트
 * @param required 필수 입력 여부
 * @param title 섹션 제목
 */
function FormSection({ required, title, children }: FormSectionProps) {
  return (
    <section>
      <Label
        required={required}
        className="font-extrabold"
      >
        {title}
      </Label>
      {children}
    </section>
  );
}

하지만, 모달이 아닌 강의 생성/입장 페이지에서는 섹션 간 간격이 다르고 HelpText가 필요한 등 요구사항이 달랐습니다. 경우마다 다른 컴포넌트를 선언하거나 props를 추가해야 했고, 컴포넌트가 비대해지는 문제가 있었습니다. 이 컴포넌트가 무엇을 하는지 알기 힘들었습니다.

또한 한 section에서만 스타일이 달라지거나, 라벨 옆에 HelpText 를 붙여야 하는 변경이 생길 경우에도 유연하게 대응하기 어려운 구조라고 판단했습니다. ​

이를 개선하기 위해 FormField 를 도입하고, Label, HelpText, Error 를 컴파운드 컴포넌트 패턴으로 분리했습니다. 필요한 조합만 선택적으로 사용하도록 설계함으로써, 레이아웃, 간격, 표현 방식은 각 사용처에서 유연하게 구성하면서도 의미 단위는 일관되게 유지할 수 있도록 했습니다.

각 서브 컴포넌트가 어떤 역할을 하는지 명확해지고, 이후 스타일 변경이나 기능 확장에도 좀 더 유연하게 대응할 수 있게 되었습니다.

스크린샷 2026-01-11 오전 12 57 06

forwardRef

리액트 19부터는 forwardRef를 별도로 선언하지 않아도 함수형 컴포넌트에서 ref를 prop처럼 직접 전달해 사용할 수 있는 것으로 알고 있습니다. 그런데 현재 프로젝트의 리액트 버전이 18.3.1이라 이 방식이 적용되지 않는 상황이었습니다.

react-hook-form을 사용하면 ref가 자동으로 넘어가지만, 일반 함수형 컴포넌트에서는 이 ref가 바로 전달되지 않는 문제가 있었습니다. 그래서 Input 컴포넌트를 화살표 함수 형태로 변경한 뒤, forwardRef를 사용해 ref를 명시적으로 전달하는 방식으로 수정했습니다.


💬 리뷰 요구사항

리뷰어가 중점적으로 확인해주길 바라는 부분이 있다면 작성해주세요.

  1. 현재 버튼이 비활성화 상태에서 활성화 상태로 전환될 때 튀는 느낌의 문제가 발생하고 있습니다. 스타일이나 전환 방식을 조정하는 개선이 필요할 것 같습니다.

  1. 현재는 FormField 에서 Label, Input, HelpText를 각각 import 해서 조합해 쓰고 있습ㄴ디ㅏ. 나중에는 이들을 하나의 FormField 컴포넌트 안으로 통합하는 것도 좋을 것 같습니다.

    우선은 도기의 작업에 영향이 갈 수 있고, 아직 확정되지 않은 내용이기에 분리된 컴포넌트를 불러와 사용하는 방식으로 유지했습니다. 리뷰를 통해 보완해나가면 좋을 것 같습니다.


  1. 현재 작업 중인 폴더명은 feature/create-poll 입니다. 처음에는 투표 생성 기능만 담당할 예정이어서 이렇게 이름을 지었지만, 지금은 투표 생성뿐만 아니라 수정까지 포함해 전반적인 투표 관련 로직을 다루고 있습니다. 폴더가 담당하는 책임 범위를 고려하면, 이름을 어떻게 수정하면 좋을지 조언을 구하고싶습니다.

📘 참고 자료

참고한 문서, 링크, 또는 외부 리소스를 작성해주세요.

@KimDongGyun23 KimDongGyun23 self-assigned this Jan 10, 2026
@KimDongGyun23 KimDongGyun23 linked an issue Jan 10, 2026 that may be closed by this pull request
2 tasks
@KimDongGyun23 KimDongGyun23 marked this pull request as ready for review January 10, 2026 13:46
@KimDongGyun23 KimDongGyun23 marked this pull request as draft January 11, 2026 01:43
@KimDongGyun23 KimDongGyun23 marked this pull request as ready for review January 11, 2026 05:09
Copy link
Collaborator

@YunDo-Gi YunDo-Gi left a comment

Choose a reason for hiding this comment

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

feature/create-poll 폴더가 담당하는 책임 범위를 고려하면, 이름을 어떻게 수정하면 좋을지 조언을 구하고 싶습니다.

투표를 관리한다는 의미에서 manage-poll 어떤가요?

@YunDo-Gi
Copy link
Collaborator

현재는 FormField 에서 Label, Input, HelpText를 각각 import 해서 조합해 쓰고 있습니다. 나중에는 이들을 하나의 FormField 컴포넌트 안으로 통합하는 것도 좋을 것 같습니다.

현재 디자인상 해당 컴포넌트들이 Form 내부에서만 사용될 것 같아서 (Input 같은 경우에는 채팅에 사용될 수도 있을 것 같기는 해요) 통합하는 방향으로 가도 괜찮을 것 같아요. 제가 작업하고 있는 부분에 영향을 주는건 없어서 로키의 판단대로 진행해도 좋을 것 같아요!

Copy link
Collaborator

@YunDo-Gi YunDo-Gi left a comment

Choose a reason for hiding this comment

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

고생하셨습니다!!

- FormFieldRoot의 폼을 감싸는 부분을 section에서 fieldset으로 수정
- 기존 FormFieldLabel을 FormFieldLegend로 수정
- HelpText 제거 후, FormFieldHelpText에 직접 사용
- FormFieldLabel 추가 (input과 연결되는 label)
Copy link
Collaborator

@dami0806 dami0806 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다!!👍 👍

@@ -0,0 +1,214 @@
import { RefObject, useEffect } from 'react';
Copy link
Collaborator

Choose a reason for hiding this comment

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

이 두 import from 'react' 합쳐도 좋을거 같아요!!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

요거 이전 PR 병합 전, 임시용이어서 삭제될 예정이에요!!

확인 감사합니다~~~

@KimDongGyun23
Copy link
Collaborator Author

KimDongGyun23 commented Jan 12, 2026

FormField.Label -> FormField.Legend 로 이름 수정

  • 폼 필드의 제목을 담당하기에 Legend 가 적절하다고 생각했습니다.
  • 실제로,FormField.Label 컴포넌트가 필요한 부분 (ex. 체크 박스와 연결되는 텍스트)이 있어서 구분하고자 했습니다.
스크린샷 2026-01-17 오전 11 01 37



FormField.Label 구현

  • 체크 박스와 라벨 연결을 위해서 해당 부분을 구현했습니다.
스크린샷 2026-01-17 오전 10 55 58



사용되지 않는 HelpText, Label 공통 컴포넌트는 제거했습니다.

  • 해당 부분은 FormField 내에 직접 선언했습니다.
  • Input 컴포넌트는 도기가 사용할 수도 있다해서 그대로 남겨놨어요!

@KimDongGyun23 KimDongGyun23 merged commit 2629150 into develop Jan 12, 2026
1 check passed
@KimDongGyun23 KimDongGyun23 deleted the feat/#61-poll-modal branch January 12, 2026 07:46
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.

사전투표 모달 구현

4 participants