[블로그만들기] 오늘, 이번 주, 이번 달 인기글 보여주기 (typescript)

seo young park·2022년 1월 19일
4

블로그 만들기

목록 보기
3/5
post-thumbnail
post-custom-banner

✨ 인기글 보여주기

오늘은 '좋아요'와 '작성 시간'을 기준으로 인기 글을 보여주는, 데이터 필터링을 구현할 것이다.
사용자가 셀렉트 박스에서 오늘 / 이번 주 / 이번 달 / 올 해 등 기간을 선택하면 그 기간동안 작성된 글을 인기순으로 재정렬한다.

자식요소에서 선택 값 받아오기

filter ---- selectbox
	|_ card container

selectbox에서 선택된 value를 기준으로 card container를 재정렬해야 하는데,
부모에서 자식으로 값을 내려주는 게 아니라 자식에서 부모로 값을 올려주는 것이 관건이었다.

그래서 부모요소에 useState와 state를 업데이트하는 함수를 만들어두고,

  const [filteredText, setFilteredText] = useState('이번 주');

  function handleClick(e: string) {
    setFilteredText((i) => (i = e));
  }

함수를 props로 내려줬다.

//onClick이벤트 아님 'onClicked' props임 
  <SelectBox onClicked={handleClick} route={route}></SelectBox>

그리고 자식요소에서 onClick이벤트로 선택된 value를 받아와, 아까 props로 내려줬던 함수에 파라미터로 넘겨주었다. 이제 셀렉트박스에서 기간을 고를 때마다 부모요소에서 state값이 업데이트된다.

export const SelectBox = ({ route, onClicked }: any) => ...
 const optionClickHandler = (e: React.MouseEvent<HTMLElement>) => {
    const text = (e.target as HTMLElement).textContent as string;
    onClicked(text);
  };
...

<List onClick={optionClickHandler}>
    {name}
 </List>

기간 필터링

Date()

이제 기간을 기준으로 데이터를 필터링해야한다. 현재시각을 객체로 반환하는 Date() 생성자를 이용할 것이다. Date() 생성자는 시간의 특정 지점을 나타내는 Date 객체를 반환한다.

파라미터를 전달하지 않으면 현재 날짜와 시각을 나타내는 인스턴스를 리턴하고

new Date();
// Wed Jan 19 2022 23:25:48 GMT+0900 (한국 표준시)

날짜외 시간을 문자열로 전달하면 그 날짜와 시각을 나타내는 인스턴스를 리턴한다.

new Date('Dec 31, 21 00:20:18')
Fri Dec 31 2021 00:20:18 GMT+0900 (한국 표준시)

참고로 데이터타입은 Date다.

Date객체 년/월/일로 변환


본격적인 기능 구현에 앞서,
데이터의 Date 객체를 ㅇㅇ년 ㅇㅇ월 ㅇㅇ일로 보기 좋게 변환시켜줄 것이다.
getFullYear, getDate, getMonth 메서드로 년월일을 구해준 다음 원하는 문자열 형식에 넣어준다. 이 중 getMonth 는 0부터 시작해서 +1 해줘야한다는 것을 유의하자.


export const PostCard = (date:{date:Date;}) => {
const formatDate = (date: Date) => {
    let d = new Date(date);
    let month = '' + (d.getMonth() + 1);
    let day = '' + d.getDate();
    let year = d.getFullYear();
    if (month.length < 2) {
      month = '0' + month;
    }
    if (day.length < 2) {
      day = '0' + day;
    }
    return year + '년 ' + month + '월 ' + day + '일';
  };

....

return(
 <span>{formatDate(date)}</span>
)

기간별로 끊어주기 : 하루동안, 일주일동안

이제 기간별로 데이터를 끊어줄 것이다.
현재시각으로부터 하루 전 / 일주일 전 시간 값을 구해주고 그 이상인 데이터들, 그러니까 그 시간 이후에 작성된 데이터들을 filter로 거르면 하루/일주일동안 작성된 게시물들 뽑아낼 수 있다. 뽑아낸 게시물들을 sort를 이용해 좋아요(count)순으로 정렬한다.

//일주일동안
CARD_DATA.filter(
  (post) => post.date >= new Date(Date.now() - 1000 * 3600 * 24 * 7))
  .sort((a,b)=>b.count - a.count);
//하루동안
CARD_DATA.filter(
  (post) => post.date >= new Date(Date.now() - 1000 * 3600 * 24 * 7))
  .sort((a,b)=>b.count - a.count);

기간별로 끊어주기 : 이번 달, 올해

이제는 이번 달(현재날짜와 같은 달), 올해(현재날짜와 같은 년도)를 기준으로 데이터를 끊어줄 것이다.
getMonth(), getYear()메서드로 월값과 년값을 받아 일치하는 데이터들을 뽑아낸다.

//이번 달 작성된 게시물
MAIN_CARD_DATA.filter(
        (post) =>
          post.date.getMonth() === new Date().getMonth()
      ).sort((a, b) => b.count - a.count);
//올해 작성된 게시물
MAIN_CARD_DATA.filter(
        (post) =>
          post.date.getFullYear() === new Date().getFullYear()
      ).sort((a, b) => b.count - a.count);

type 선언 : filter, sort

타입스크립트이기 때문에 filter에서 테스트를 통과할 요소와 sort의 비교 요소(a,b)도 타입을 지정해줘야한다. 앞서 작성한 코드를 타입스크립트로 다시 써보자면..
테스트를 통과할 요소도 , 비교할 요소도 모두 객체다. 그러니까 먼저 객체의 타입을 인터페이스로 정의해주고

interface CARD_DATA_PROPS {
  title: string;
  date: Date;
  count: number;
}

받아올 데이터, 객체 배열의 타입을 다음과 같이 선언해야한다.

function filterposts (CARD_DATA:Array<CARD_DATA_PROPS>){
  ...filter함수...sort함수...
}

그리고 각 요소의 타입으로 CARD_DATA_PROPS 인터페이스를 선언해야한다.

//자바스크립트
CARD_DATA.filter(
  (post) => post.date >= new Date(Date.now() - 1000 * 3600 * 24 * 7))
  .sort((a,b)=>b.count - a.count);

//타입스크립트
CARD_DATA.filter(
  (post:CARD_DATA_PROPS) => post.date >= new Date(Date.now() - 1000 * 3600 * 24 * 7))
  .sort((a:CARD_DATA_PROPS,b:CARD_DATA_PROPS)=>b.count - a.count);

이제 filter로 걸러지고 sort로 재정렬한 데이터들을 새로운 변수에 할당하고 뿌려준다.

const results = filterposts(MAIN_CARD_DATA,filter);

...
return(
result.map()=> {....}
  )

📸 기능 구현 화면

🦄 추가 구현 사항

type 선언

블로그를 쓰다가 any로 타입 선언한 코드를 발견했다. any를 모두 없앴다고 생각했는데 함수와 문자열을 같이 넘기는 부분에서 any로 타입 선언하고 넘어갔다. 다시 고쳐놓자.

export const SelectBox = ({ route, onClicked }: any) =>{};
post-custom-banner

1개의 댓글

comment-user-thumbnail
2022년 1월 25일

애니 없애주세여

답글 달기