[달력 만들기 toy project] styled-components에 prop 넘겨서 날짜별, 요일별 색상 지정하기

이민선(Jasmine)·2023년 5월 5일
0
post-thumbnail

이번 시간에는 styled-components를 이용하여 날짜별로 색상을 지정할 것이다.

  • 오늘 날짜는 배경 지정
  • 저번달, 다음달 날짜는 흐린색으로 지정

여태 styled-components를 많이 사용하기는 했지만, props를 이용해 조건 분기는 한번도 해본 적이 없는 듯하다. 여태 props 넘겨서 조건 분기하는 게 크리티컬하게 필요한 적은 없었지만, '하면 더 나을 것 같은' 그런 상황에도 굳이 적용을 안해보고 넘겼던 것 같다. 오늘 한 번 손에 익혀두면 앞으로도 아주 활용도가 높을 것이고 쓸 일도 계속 생길 것이다. 달력은 특히 props 넘겨서 조건 분기하는 게 핵심적으로 필요한 기능 중 하나이기 때문에 이를 적용해보는 게 아주 좋은 연습이 될 것이다.

일단 난이도가 더 낮을 것 같은 오늘 날짜부터 배경색을 바꿔보자.

1. 오늘 날짜 배경색 지정

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";
  }};

오예쓰 성공!! 이번 달, 오늘 날짜에 해당하면 배경이 빨간색으로 바뀐다. ㅎㅎㅎ
근데 빨간색 너무 무섭다... 나중에 예쁘게 바꾸도록 하고 일단은 기능 구현에 초점을 두자 ㅎㅎㅎ

다음으로는 저번 달, 다음 달 날짜들은 흐리게 출력해봐야겠다.

일단은 저번달 부터!!

2. 저번 달 날짜들 글자색 흐리게

배열에서 행 번호를 받아와서 행 번호가 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가 나는 것이었다.

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

이렇게 바꿨다.

type error 해결 (assertion 지양)

만약 props.children이 number type이 아니더라도, Number로 감싸면 NaN이 될 것이다. NaN의 type은 number이므로 7과 비교하는 연산자를 사용할 수 있게 되는 것이다.

여기까지 화면을 보자면

저번 달 날짜 흐리게 나오게 하기 성공!!
아이 재밌다
다음 달 날짜는 더 어려워보이기는 하지만 또 시도해보자 흐흐흐흐

3. 다음 달 날짜들 글자색 흐리게

마지막 주라는 정보가 필요하므로 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는 여기까지!

4. 짧은 회고

아니 진짜 prop 넘겨서 style 주는 거 너무 재밌잖아 ㅠㅠ 재미있어서 춤추는 중 ㅠ 조건 분기에 필요한 key/value들을 골라내서 prop으로 넘겨주는 과정이 아주 흥미롭다. 이제 styled-components에 prop 넘기는 것도 어느 정도 손에 익어서 자주 활용할 수 있을 것 같다.

typescript를 생각해보면 아쉬운 점도 있긴 하다. 물론 typescript를 본격적으로 배운 이후부터 점점 type 주는게 손에 익기도 하고 재미있기도 하다.
하지만 여전히 type을 잘 활용하고 있는 것 같다는 생각이 크게 들지는 않는다. 여전히 compiler 통과에 의의를 두고 있는 것 같달까? 그래도 점점 손에 익는 만큼 좀 더 적극적으로 type을 활용해보고 typescript의 이점을 누릴 수 있으면 좋겠다!

다음 시간부터는 날짜별로 CRUD 구현을 해보려고 한다. 달력의 특정 날짜를 클릭하면 그 날짜에 해당하는 메모만 달력의 오른쪽에 뜨도록 구현해볼 것이다. 어려울 것 같지만...나 제법 도전적이야 할 수 있어!!!

이제 오늘은 알고리둠 풀러가쟝 ~~ 화이팅!!

profile
기록에 진심인 개발자 🌿

0개의 댓글