-
Notifications
You must be signed in to change notification settings - Fork 1
프로그래스바 공통 컴포넌트로 분리
막대 부분만 따로 공통 컴포넌트로 분리하고자 했다. 해당 카드 자체를 분리하려고 했는데 생각해보니 각각 필요한 데이터가 너무 다르고 구조가 조금씩 다 달라서 Props로 데이터를 전부 내려주고 내부에서 또 두 개의 카드를 분기 처리하는 방식이 너무 지저분하고 복잡할 것 같았다.
막대 부분은 거의 로직이 동일한데, 한 가지 다른 점이 있다면 결과 화면에서는 각 비율 중 더 작은 쪽에 비율 색상을 회색으로 즉, “text-n-neutral-500”으로 처리해주어야 한다. 그 외에는 동일하다.
어차피 프로그래스바 컴포넌트를 분리할 때 비율을 받아야하므로 내부에서 결과 창일 때만 처리해주면 되는데, 결과창인지 게임 진행 중인지에 대한 분기 처리를 어떻게 해야할지 고민이었다.
생각해보니 useRushGameContext() 훅에서 게임 진행 상태를 저장해두었는데, 각각 “IN_PROGRESS”, “COMPLETED”에 해당하기 때문에 굳이 Props로 각각을 분기처리할 필요 없이 프로그래스바 컴포넌트 내부에서 해당 상태를 받아 처리해주면 된다.
import { VariantProps, cva } from "class-variance-authority";
import { useRushGameContext } from "@/hooks/useRushGameContext.ts";
interface RushProgressBarProps {
leftOptionRatio: number;
rightOptionRatio: number;
}
const barVariants = cva(`flex items-center`, {
variants: {
color: {
green: "bg-gradient-green",
red: "bg-gradient-red",
},
status: {
winning: "text-n-neutral-950",
losing: "text-n-neutral-500",
},
textAlign: {
left: "justify-start",
right: "justify-end",
},
},
defaultVariants: {
status: "winning",
},
});
type BarVariantsProps = VariantProps<typeof barVariants>;
interface BarProps extends BarVariantsProps {
ratio: number;
}
function Bar({ ratio, color, status, textAlign }: BarProps) {
return (
<span className={barVariants({ color, status, textAlign })} style={{ width: `${ratio}%` }}>
<p className="px-3">{ratio}%</p>
</span>
);
}
export default function RushProgressBar({
leftOptionRatio,
rightOptionRatio,
}: RushProgressBarProps) {
const { gameState } = useRushGameContext();
const isCompleted = gameState.phase === "COMPLETED";
const leftStatus = isCompleted && leftOptionRatio < rightOptionRatio ? "losing" : "winning";
const rightStatus = isCompleted && rightOptionRatio < leftOptionRatio ? "losing" : "winning";
return (
<div className="h-heading-3-bold h-[66px] flex justify-between">
<Bar ratio={leftOptionRatio} color="green" status={leftStatus} textAlign="left" />
<Bar ratio={rightOptionRatio} color="red" status={rightStatus} textAlign="right" />
</div>
);
}
위 코드가 프로그래스바 관련 컴포넌트인데, 우선 leftOptionRatio, rightOptionRatio를 Props로 받아서 넘겨줬다. 각 섹션별로 실시간 비율과 최종 비율을 받아서 보여줘야 하기 때문이다.
그리고 게임 진행 상태에 대한 gameState를 useRushGameContext에서 내려받아서 현재 게임이 종료됐는지, 즉 gameState.phase가 "COMPLETED"인지 판단하고, "COMPLETED"인 경우에는 각 비율의 상태에 따라서 losing과 winning을 설정해주었다. 해당 비율이 더 작은 경우 losing으로 설정해주었고, 비율이 둘 다 동일한 경우에는 그냥 winning으로 처리해주었다. 그래야 비율이 동일할 경우 둘 다 “text-n-neutral-950”로 처리할 수 있기 때문이다.
새로운 Bar 컴포넌트를 분리해주었는데, 각 프로그래스바의 공통 로직을 분리하기 위해서였다. Props로 각각의 비율(ratio)과 프로그래스바 색상(color), losing/winning 상태(status), 프로그래스바 내부에서 비율을 표시할 좌우 위치(textAlign)를 받도록 했다.
Props로 받은 비율대로 프로그래스바의 width를 설정해주고 text로 표기해주었다.
그리고 나머지 color, status, textAlign들은 cva를 사용하여 각 상태에 따라 tailwindCSS Variants를 다르게 해주었다.
나중에 테스트하면서 발견했는데, 게임 참여자가 0명인 경우 프로그래스바가 나타나지 않았다. 따라서, 0명인 경우에도 회색 바가 뜨도록 isAllZero라는 Props를 따로 넘겨주어 임의로 가로 width를 넣어주고 색상도 회색으로 설정해주었다. 그리고 해당 Bar 컴포넌트가 길어진다고 판단하여 프로그래스바 컴포넌트에서 Bar만 따로 분리해주었다.
import { CARD_COLOR, CARD_PHASE } from "@/constants/Rush/rushCard.ts";
import RushBar from "@/features/RushGame/RushGameComponents/RushBar.tsx";
import useRushGameStateContext from "@/hooks/Contexts/useRushGameStateContext.ts";
interface RushProgressBarProps {
leftOptionRatio: number;
rightOptionRatio: number;
}
export default function RushProgressBar({
leftOptionRatio,
rightOptionRatio,
}: RushProgressBarProps) {
const gameState = useRushGameStateContext();
const isCompleted = gameState.phase === CARD_PHASE.COMPLETED;
const isAllZero = leftOptionRatio === 0 && rightOptionRatio === 0;
const leftStatus = isCompleted && leftOptionRatio < rightOptionRatio ? "losing" : "winning";
const rightStatus = isCompleted && rightOptionRatio < leftOptionRatio ? "losing" : "winning";
return (
<div className="h-heading-3-bold h-[66px] flex justify-between">
<RushBar
ratio={leftOptionRatio}
color={CARD_COLOR.GREEN}
status={leftStatus}
textAlign="left"
isAllZero={isAllZero}
/>
<RushBar
ratio={rightOptionRatio}
color={CARD_COLOR.RED}
status={rightStatus}
textAlign="right"
isAllZero={isAllZero}
/>
</div>
);
}
import { VariantProps, cva } from "class-variance-authority";
const barVariants = cva(`flex items-center`, {
variants: {
color: {
green: "bg-gradient-green",
red: "bg-gradient-red",
blue: "bg-gradient-blue",
yellow: "bg-gradient-yellow",
gray: "bg-n-neutral-100",
},
status: {
winning: "text-n-neutral-950",
losing: "text-n-neutral-500",
},
textAlign: {
left: "justify-start",
right: "justify-end",
},
},
defaultVariants: {
status: "winning",
},
});
type BarVariantsProps = VariantProps<typeof barVariants>;
interface BarProps extends BarVariantsProps {
ratio: number;
isAllZero: boolean;
}
export default function RushBar({ ratio, color, status, textAlign, isAllZero }: BarProps) {
const barColor = isAllZero ? "gray" : color;
const barStatus = isAllZero ? "losing" : status;
const barWidth = isAllZero ? "50%" : `${ratio}%`;
return (
<span
className={barVariants({ color: barColor, status: barStatus, textAlign })}
style={{ width: barWidth }}
>
<p className="px-3 z-40">{ratio}%</p>
</span>
);
}
- 페이지가 렌더링 된 후 스크롤을 최상단으로 올리기
- DOM을 이미지로 저장하기
- Context API에서 불필요한 리렌더링을 줄이는 방법
- 무한 Transition 애니메이션
- Github Action 워크플로우 정의하기
- 무한 Transition 애니메이션 최적화하기
- 무한 Transition 애니메이션 최적화하기 2
- 무한 Transition 애니메이션 최적화하기 3
- fetch timeout 구현하기
- ErrorBoundary 구현하기
- 뒤로가기 confirm 로직 구현하기
- 선착순 밸런스 게임 상태 관리
- Modal 내부 컴포넌트에서 무한 스크롤이 제대로 동작 안되는 문제
- useToggleContents 훅 기본값 설정 및 조건부 사용법
- 폰트 굵기 적용 이슈
- SVG 내부 stroke 속성 값 제어를 위한 SVGIcon Util 함수 및 SVGR 사용 과정
- tailwindCSS의 @apply를 cva로 바꾸기
- 스크롤 내려갈 때 해당 섹션의 요소들 인터렉션 동작
- 공통 컴포넌트 내부에 애니메이션을 넣는 것에 대한 고민
- 특정 컴포넌트 위치에서 헤더 스타일 다르게 적용하는 방법
- 스크롤 거꾸로 올릴 때 IntersectionObserver가 뷰포트 감지 못하는 현상
- 선착순 밸런스 게임 최종 결과 계산에 대한 고민 (08.14)
- 프로그래스바 공통 컴포넌트로 분리
- (08.23 기준) 선착순 서버 시간 연동 문제
- 게임 종료된 상태에서 사용자 게임 참여 여부에 따른 FinalResult 분기 처리
- FinalResult 컴포넌트의 “당신의 선택” 카테고리 설정 이슈
- 게임 접속 시 게임 현재 진행 상태 초기화 및 카운트 다운 설정
- 선착순 밸런스 게임 UX 개선