이번 시간에는 styled-components를 이용하여 날짜별로 색상을 지정할 것이다.
여태 styled-components를 많이 사용하기는 했지만, props를 이용해 조건 분기는 한번도 해본 적이 없는 듯하다. 여태 props 넘겨서 조건 분기하는 게 크리티컬하게 필요한 적은 없었지만, '하면 더 나을 것 같은' 그런 상황에도 굳이 적용을 안해보고 넘겼던 것 같다. 오늘 한 번 손에 익혀두면 앞으로도 아주 활용도가 높을 것이고 쓸 일도 계속 생길 것이다. 달력은 특히 props 넘겨서 조건 분기하는 게 핵심적으로 필요한 기능 중 하나이기 때문에 이를 적용해보는 게 아주 좋은 연습이 될 것이다.
일단 난이도가 더 낮을 것 같은 오늘 날짜부터 배경색을 바꿔보자.
today라는 변수에 이번 달과 날짜가 key/value인 객체를 담아서 EachDate에 prop으로 넘겨주었다.
const now = Date.now();
// today라는 변수에 이번달과 날짜 정보를 객체로 생성
const today = { year, month, date: date.getDate() };
const showDates = daysOfThisMonth.map((week, i) => (
<tr key={now + i}>
{week.map((date, j) => (
// today를 prop으로 넘겨주기!!
<EachDate key={now + j} today={today}>
{date ? date : null}
</EachDate>
))}
</tr>
));
background-color 속성에서 prop으로 객체를 받아와서 EachDatesProps라는 type을 지정해주었다.
type EachDatesProps = {
today: {
year: number;
month: number;
date: number;
};
};
//
const EachDate = styled.th<EachDatesProps>`
background-color: ${(props) => {
console.log(props);
return props.today.date === props.children ? "red" : "null";
}};
.
.
// 다른 속성들 생략
.
.
`;
그리고 props를 console 출력해보면
이렇게 날짜 별로 출력된다.
오늘 날짜만 열어보았는데, 내가 만들어서 props로 전달한 {month: 4, date: 5} 객체도 보이고, children이라는 속성도 보인다. children이라는 속성은 내가 화면에 띄워주고 있는 date에 담긴 각각의 날짜값이다.
즉, date에 담긴 날짜값과, props.date이 같고, 이번 달이 props.month와 같다면 배경색을 다르게 지정하면 될 것이다!!
도우전!
background-color: ${(props) => {
const date = new Date();
// 이번 연도와 이번 달을 각각 thisYear와 thisMonth에 담음
const thisYear = date.getFullYear();
const thisMonth = date.getMonth();
// props에 담긴 달이 thisMonth이고
return props.today.month === thisMonth &&
// props에 담긴 날짜가 props.children이라면
props.today.date === props.children
// 배경 red로
? "red"
// 그 외에는 배경 없도록
: "null";
}};
오예쓰 성공!! 이번 달, 오늘 날짜에 해당하면 배경이 빨간색으로 바뀐다. ㅎㅎㅎ
근데 빨간색 너무 무섭다... 나중에 예쁘게 바꾸도록 하고 일단은 기능 구현에 초점을 두자 ㅎㅎㅎ
다음으로는 저번 달, 다음 달 날짜들은 흐리게 출력해봐야겠다.
일단은 저번달 부터!!
배열에서 행 번호를 받아와서 행 번호가 0일 때에 한해 styled-components 내부에서 조건 분기를 하려고 한다.
const showDates = daysOfThisMonth.map((week, i) => (
<tr key={now + i}>
{week.map((date, j) => (
<EachDate key={now + j} today={today} row={i}>
{date ? date : null}
</EachDate>
))}
</tr>
));
row를 새로 받아왔으니 type에도 추가해주고!!
type EachDatesProps = {
today: {
year: number;
month: number;
date: number;
};
row: number;
};
처음에 이렇게 시도해보았는데 type error가 나는 것이었다.
일단 이 코드의 로직을 잠시 보자면, 행 번호가 0이고(첫째주) 첫째주 중에서도 props.children이 7보다 크면 저번 달 날짜라는 의미이므로 이에 따라 조건 분기를 하려고 한다.
그런데 props.children > 7이 3항연산자에서 조건에 들어가야 하는데
color: ${(props) => {
console.log(props.children);
return props.row === 0 && props.children > 7
? "rgb(0, 0, 0, 0.1)"
: null;
}};
이런 에러가 나는 것이었다.
이 에러에서 Object는 props.children을 가리킨다.
props.children이 숫자가 아니면 7이랑 비교를 못하잖아!
그래서 해결을 시도해보았다.
props.children as number > 7
물론 컴파일러는 통과를 했다.
하지만 이 방법은 쓰지 않기로 결정했다.
typescript를 배우면서 type assertion은 웬만하면 쓰지 말라고 배웠기 때문이다. 컴파일러가 아니라 나의 의지로 type을 지정해버리면 타입스크립트 컴파일러가 있는 의미가 없으니까!
그래서 다른 대안을 찾아보았고,
Number(props.children) > 7
이렇게 바꿨다.
만약 props.children이 number type이 아니더라도, Number로 감싸면 NaN이 될 것이다. NaN의 type은 number이므로 7과 비교하는 연산자를 사용할 수 있게 되는 것이다.
여기까지 화면을 보자면
저번 달 날짜 흐리게 나오게 하기 성공!!
아이 재밌다
다음 달 날짜는 더 어려워보이기는 하지만 또 시도해보자 흐흐흐흐
마지막 주라는 정보가 필요하므로 row라는 prop을
row={{
week: i,
lastWeek: daysOfThisMonth.findIndex(
(week, index) => index !== 0 && week.includes(1)
),
}}
이렇게 변경했다. 각 날짜가 몇번째 주에 속해 있는지, 이번달 lastWeek의 index는 몇인지(첫번째 행은 아니지만 1이라는 날짜가 포함된 주 === 마지막주)를 담아서 전달하려는 것이다.
color: ${(props) => {
console.log(props.children);
return (props.row.week === 0 && Number(props.children) > 7) ||
(props.row.week === props.row.lastWeek && Number(props.children) < 8)
? "rgb(0, 0, 0, 0.1)"
: null;
}};
row를 변경했으니 type도 변경해주구~~
type EachDatesProps = {
today: {
year: number;
month: number;
date: number;
};
row: {
week: number;
lastWeek: number;
};
};
이렇게 조건을 추가했다.
해당 주가 마지막주이면서, props.children(날짜)는 8보다 작다면? 다음 달이라는 의미이다.
color: ${(props) => {
console.log(props.children);
// 지난 달이거나
return (props.row.week === 0 && Number(props.children) > 7) ||
// 다음 달 날짜면
(props.row.week === props.row.lastWeek && Number(props.children) < 8)
// 흐린 색 적용
? "rgb(0, 0, 0, 0.1)"
: null;
}};
다음 달 날짜도 흐리게 출력된다!!
아싸라비야 콜롬비야~~
너무 뿌듯하고 재밌어서 땐스떈스가 나온다~~
이제 목표하던 건 다 구현을 했고, 좀 못생긴 것 같으니 스타일을 좀 만져보자.
짜잔! 좀 더 낫다 ㅎㅎㅎㅎ
오늘의 styled-components는 여기까지!
아니 진짜 prop 넘겨서 style 주는 거 너무 재밌잖아 ㅠㅠ 재미있어서 춤추는 중 ㅠ 조건 분기에 필요한 key/value들을 골라내서 prop으로 넘겨주는 과정이 아주 흥미롭다. 이제 styled-components에 prop 넘기는 것도 어느 정도 손에 익어서 자주 활용할 수 있을 것 같다.
typescript를 생각해보면 아쉬운 점도 있긴 하다. 물론 typescript를 본격적으로 배운 이후부터 점점 type 주는게 손에 익기도 하고 재미있기도 하다.
하지만 여전히 type을 잘 활용하고 있는 것 같다는 생각이 크게 들지는 않는다. 여전히 compiler 통과에 의의를 두고 있는 것 같달까? 그래도 점점 손에 익는 만큼 좀 더 적극적으로 type을 활용해보고 typescript의 이점을 누릴 수 있으면 좋겠다!
다음 시간부터는 날짜별로 CRUD 구현을 해보려고 한다. 달력의 특정 날짜를 클릭하면 그 날짜에 해당하는 메모만 달력의 오른쪽에 뜨도록 구현해볼 것이다. 어려울 것 같지만...나 제법 도전적이야 할 수 있어!!!
이제 오늘은 알고리둠 풀러가쟝 ~~ 화이팅!!