📌 오늘의 작업 핵심 요약
👀 구현 배경 & 목적
- 왜 지역 선택과 후보 선택이 필요했는지
대선의 꽃은 바로 투표 아니겠는가?
이번 프로젝트에 재미 요소를 더하기 위해 모의 투표 기능을 제안했고, 직접 만들어보았다. (그래서 이 기능은 내가 꼭 구현해보고 싶었다!) 해당 컴포넌트(디자인 포함)를 구성하면서는 선거법에 저촉되지 않도록 문구 하나하나 신중하게 작성했다. 또 너무 긴 문장은 디자인 측면에서 가독성이 떨어지기 때문에, 핵심만 명확하게 전달하는 데 집중했다. 특히 실제 선거와 무관하다는 안내를 시각적으로도 눈에 띄게 배치하며 사용자 오해가 없도록 구성했다.
이 기능을 맡고 싶었던 또 하나의 이유는, React의 상태 관리와 백엔드와의 연결 흐름을 직접 구현하며 더 깊이 배우고 싶었기 때문이다.
- UX 측면에서 어떤 흐름을 만들고 싶었는지
UX 측면에서는 사용자에게 자연스러운 선택 흐름을 만드는 데 집중했다. 지역 선택이 왜 필요한지에 대한 설명도 함께 제공했고,
사용자가 이를 이해하고 체크할 수 있도록 체크박스 UI로 명확하게 표시해 주었다. 테일윈드는 이번에 처음 써보는 터라, 팀원들과 함께 만든 초기 시안만큼 완성도 있게 스타일을 구현하지는 못했다.
아직 아쉬운 부분이 많아, 내일 다시 디자인 디테일을 보완할 계획이다.
📁 주요 기능 설명
📃 vote.jsx
vote.jsx는 투표 페이지의 메인 컴포넌트로 헤더, 지역 선택 UI, 후보 리스트, 안내 문구, 투표 버튼, 하단 디스클레이머로 구성된 단일 컴포넌트이다.
주요 섹션별 설명
1.voteHeader
<VoteHeader />
상단 투표 제목/설명을 보여주는 UI컴포넌트
2. RegionSelect
const [selectedRegionId, setSelectedRegionId] = useState(null);
...
<RegionSelect onRegionSelect={handleRegionSelect} />
사용자의 지역 선택 드롭다운, 지역 ID를 상위로 전달하고 상태를 저장한다.
지역 ID를 전달하는 이유는 백엔드에 보내고 보낸 값을 지역 지지율에 표시하기 위함이다.
3. CandidateCard
const { selected, selectCandidate } = useVote();
...
{candidates.map((candidate) => (
<CandidateCard
key={candidate.id}
image={candidate.image}
name={candidate.name}
party={candidate.party}
slogan={candidate.slogan}
selected={selected === candidate.id}
onClick={() => selectCandidate(candidate.id)}
/>
))}
후보자 리스트를 candidates.map()으로 순회하며 렌더링했다. selected 상태와 onClick 핸들러로 사용자 선택을 반영한다.
onClick={() => selectCandidate(candidate.id)}
해당 후보 카드를 클릭되었을 때, 그 후보의 id를 selectCandidate() 함수에 즉시 전달해서 상태를 전달한다.
헷갈릴때 보기 🙈
1. 사용자가 후보 카드 클릭
2. selectCandidate(candidate.id) 호출
3. selected상태가 해당 후보의 id로 바뀜
4. React가 컴포넌트를 다시 렌더링함
5. 각 CandidateCard는 selected === candidate.id인지 비교해 선택된 후보만 표시되게 한다.
4. handleRegionSelect
const [agreedToNotice, setAgreedToNotice] = useState(false);
...
const handleToggleAcknowledge = () => {
setShowRegionPrompt(false);
};
...
<div className="flex items-center gap-2">
<input
type="checkbox"
checked={agreedToNotice}
onChange={(e) => setAgreedToNotice(e.target.checked)}
/>
<span className="text-sm text-gray-600">
지역 정보를 기반으로 지지율 분석이 진행됩니다.
</span>
</div>
...
if (!agreedToNotice) {
toast.error('안내 문구에 동의해주세요.');
return;
}
3가지 이유로 이 문구를 만들었다.
1. 지역 데이터를 수집하기 위함인 기능적 이유
2. 사용자 오해 방지 및 중립성을 유지하기 위한 정책적 이유
3. 그냥 지역만 선택하세요하면 사용자들이 궁금할 수 있으니 ux 설명
체크박스에 체크를 안하면..ㅎㅎ 토스트ui가 나온다.
🍞 토스트 UI
이번 프로젝트에서는 사용자 피드백을 시각적으로 빠르게 전달하기 위해 Toast UI 라이브러리를 활용했다.
특히 최근에 많이 사용되는 핫한 라이브러리인 react-hot-toast를 적용했다.
이 라이브러리를 사용한 이유는 toast.success(), toast.error()와 같이 간단한 함수 호출로 다양한 스타일 알림을 띄울 수 있다. 비동기 상태 표현도 자연스러운 알림으로 사용할 수 있는 장점이 있기에 적용해봤다. (아직 비동기까지는 사용하지 못해봤다)
5. VoteButton
<VoteButton
disabled={selected === null}
onClick={() => {
if (selectedRegionId === null) {
toast.error('지역을 먼저 선택해주세요.');
return;
}
if (!agreedToNotice) {
toast.error('안내 문구에 동의해주세요.');
return;
}
const selectedCandidate = candidates.find((candidate) => candidate.id === selected);
toast.success(`${selectedCandidate.name} 후보에게 투표 완료되었습니다.`);
console.log(`${selectedCandidate.name} 후보에게 투표 완료되었습니다.`);
}}
/>
disabled는 select === null을 작성해 onclick에 있는 문구들을 선택을 안하면 못하게해놨다.
📃 useVote 훅 활용 방식
useVote는 사용자가 선택한 후보의 ID를 상태로 저장하고 그 값을 다른 컴포넌트에서 쉽게 사용할 수 있게 해주는 커스텀 훅이다.
import React, { useState } from 'react';
const useVote = () => {
const [selected, setSelected] = useState(null);
const selectCandidate = (id) => {
setSelected(id);
};
return {
selected,
selectCandidate,
};
};
export default useVote;
이걸 외부에서 쓰이기 위해
const { selected, selectCandidate } = useVote();
이렇게 작성하면 된다.
이 훅을 통해서
상태 관리 로직을 컴포넌트 외부로 분리해 재사용과 코드 가독성을 높였다.
✨ 오늘 배운 새로운 기능
내 작업 결과를 내 브런치에만 저장할 수 있다! 다른 브랜치에는 영향을 안가게 말이다! (오늘 알았다. 팀원분께서 더 알려주셨다.)
1. 내 브랜치에서 작업 중인지 확인
git branch
- 현재 브랜치에 내 브랜치면 OK
- 만약 아니라면:
git checkout 내 브랜치 명 입력
2. 작업 파일을 커밋
git add .
git commit -m "커밋 메시지를 입력하시오"
3. 해당 브랜치에만 푸시
git push origin 내 브랜치
이렇게 하면 다른 브랜치에 영향을 주지 않는다! 그리고 메인에서도 업데이트된걸 동기화해 당겨올 수 있다는 사실!! 난 여태 내거 브랜치를 커밋하고 그 다음 메인에서 가져올 수 있는 줄 알았는데 이렇게 좋은 기능이 있었다니!!
🎠 회고
구현할 땐 어렵게 느껴졌던 기능들이, 블로그에 정리하면서 다시 보니 왜 이렇게 만들었는지, 어떤 흐름으로 흘러가는지 더 명확해졌다.
코드 정리는 단순한 기록이 아니라, 이해를 반복하고 내 것으로 만드는 과정이라는 걸 느꼈다. 백엔드와 연결도 고려해야 했기 때문에 최대한 빠르게 기능을 구현하려고 했고, 그 과정은 재밌으면서도 역시 어렵다고 느껴졌다. Tailwind 역시 익숙하지 않아서 여전히 어렵고, 혼자 하기엔 한계도 느꼈다. 하하.
그래도 사용자 입장에서 어떻게 하면 더 보기 좋을까를 고민하는 시간은 정말 즐거웠다.
궁금한 점이 생기면 팀원들에게 물어보는데, 언제나 친절하게 잘 알려주신다.
다들 정말 대단하고 배울 점이 많은 분들이다.
내일도 오늘처럼, 한 걸음 더 나아가보자.
+ 다이어트 실패와 홈트 실패 ^_ㅠ
프젝 끝나고 진짜 다이어트 한다.
'💡 URECA > 📽️ 프로젝트' 카테고리의 다른 글
[URECA] 픽미업: 복잡한 정보를 쉽게 전달하는 정치 참여 플랫폼 #6 (0) | 2025.05.14 |
---|---|
[URECA] 픽미업: 복잡한 정보를 쉽게 전달하는 정치 참여 플랫폼 #5 (0) | 2025.05.13 |
[URECA] 픽미업: 복잡한 정보를 쉽게 전달하는 정치 참여 플랫폼(기획_최최종일까요?) #3 (2) | 2025.05.09 |
[URECA] 픽미업: 복잡한 정보를 쉽게 전달하는 정치 참여 플랫폼 #2 (4) | 2025.05.08 |
[URECA] 픽미업 - 공감으로 뽑는 나의 대통령 후보 #1 (0) | 2025.05.07 |