-
Notifications
You must be signed in to change notification settings - Fork 0
feat: 투표 모달 구현 #68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: 투표 모달 구현 #68
Conversation
- common.tsx: pr 머지 전 임시 컴포넌트 모음
YunDo-Gi
left a comment
There was a problem hiding this 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 어떤가요?
현재 디자인상 해당 컴포넌트들이 Form 내부에서만 사용될 것 같아서 (Input 같은 경우에는 채팅에 사용될 수도 있을 것 같기는 해요) 통합하는 방향으로 가도 괜찮을 것 같아요. 제가 작업하고 있는 부분에 영향을 주는건 없어서 로키의 판단대로 진행해도 좋을 것 같아요! |
YunDo-Gi
left a comment
There was a problem hiding this 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)
dami0806
left a comment
There was a problem hiding this 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'; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 두 import from 'react' 합쳐도 좋을거 같아요!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요거 이전 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
변경된 부분은 추가된 테스트를 포함해 모두 통과했습니다.
주요 고민과 해결 과정
폼 유효성 검사 로직
react-hook-form과 zod를 도입했습니다. 기존에는 폼 수가 많지 않고, 직접 유효성 검사 로직을 구현해도 충분히 대체 가능하다고 판단했습니다. 하지만 실제로는 기존에 구현된 공통 컴포넌트들과 폼을 연결 시키는 과정, 검증 로직을 자연스럽게 녹여내는 과정에서 어려움을 겪었습니다.
초기 폼 구조 설계를 잘못한 부분도 있었고, 컴포넌트를 잘못 구현한 부분도 있었다고 생각합니다. 이를 깔끔하게 재구성할 더 나은 구조를 떠올리지 못했습니다. 그래서 검증과 상태 관리를 검증된 라이브러리에 위임하고자했습니다.
react-hook-form에서 직접 폼의 상태를 관리하고, 기존 작성된 컴포넌트를 수정하지 않고도 폼의 관리가 가능했습니다. 이를 통해 복잡한 상태 관리 로직을 제거할 수 있었고, 코드가 더욱 직관적으로 변하였습니다. zod를 통하여 폼의 스키마를 더욱 간결하게 관리할 수 있으며, 직관적으로 파악할 수 있었습니다. 또한, 스키마 기반으로 검증과 타입 추론이 가능해 편리해졌습니다.
컴포넌트의 어떤 부분에서 설계가 잘못되었을지 다시 한번 정리해보는 시간을 가져보려합니다. 또한, 라이브러리 없이 어떻게 구현할 수 있었을지 고민해본 후 다시 자료를 공유해보겠습니다.
FormField 컴포넌트 구현
기존 폼에서는 section 태그와 라벨 구조가 반복되어서, 이를 공통으로 사용하기 위해
FormSection컴포넌트를 구현하여 사용했습니다.하지만, 모달이 아닌 강의 생성/입장 페이지에서는 섹션 간 간격이 다르고 HelpText가 필요한 등 요구사항이 달랐습니다. 경우마다 다른 컴포넌트를 선언하거나 props를 추가해야 했고, 컴포넌트가 비대해지는 문제가 있었습니다. 이 컴포넌트가 무엇을 하는지 알기 힘들었습니다.
또한 한 section에서만 스타일이 달라지거나, 라벨 옆에
HelpText를 붙여야 하는 변경이 생길 경우에도 유연하게 대응하기 어려운 구조라고 판단했습니다. 이를 개선하기 위해
FormField를 도입하고, Label, HelpText, Error 를 컴파운드 컴포넌트 패턴으로 분리했습니다. 필요한 조합만 선택적으로 사용하도록 설계함으로써, 레이아웃, 간격, 표현 방식은 각 사용처에서 유연하게 구성하면서도 의미 단위는 일관되게 유지할 수 있도록 했습니다.각 서브 컴포넌트가 어떤 역할을 하는지 명확해지고, 이후 스타일 변경이나 기능 확장에도 좀 더 유연하게 대응할 수 있게 되었습니다.
forwardRef
리액트 19부터는 forwardRef를 별도로 선언하지 않아도 함수형 컴포넌트에서 ref를 prop처럼 직접 전달해 사용할 수 있는 것으로 알고 있습니다. 그런데 현재 프로젝트의 리액트 버전이 18.3.1이라 이 방식이 적용되지 않는 상황이었습니다.
react-hook-form을 사용하면 ref가 자동으로 넘어가지만, 일반 함수형 컴포넌트에서는 이 ref가 바로 전달되지 않는 문제가 있었습니다. 그래서 Input 컴포넌트를 화살표 함수 형태로 변경한 뒤, forwardRef를 사용해 ref를 명시적으로 전달하는 방식으로 수정했습니다.
💬 리뷰 요구사항
현재는
FormField에서Label,Input,HelpText를 각각 import 해서 조합해 쓰고 있습ㄴ디ㅏ. 나중에는 이들을 하나의FormField컴포넌트 안으로 통합하는 것도 좋을 것 같습니다.우선은 도기의 작업에 영향이 갈 수 있고, 아직 확정되지 않은 내용이기에 분리된 컴포넌트를 불러와 사용하는 방식으로 유지했습니다. 리뷰를 통해 보완해나가면 좋을 것 같습니다.
feature/create-poll입니다. 처음에는 투표 생성 기능만 담당할 예정이어서 이렇게 이름을 지었지만, 지금은 투표 생성뿐만 아니라 수정까지 포함해 전반적인 투표 관련 로직을 다루고 있습니다. 폴더가 담당하는 책임 범위를 고려하면, 이름을 어떻게 수정하면 좋을지 조언을 구하고싶습니다.📘 참고 자료