KimMinJun
Coding Note
KimMinJun
전체 방문자
오늘
어제
  • 분류 전체보기 (487)
    • ALGORITHM (11)
      • 정렬 (6)
      • 최단경로 (1)
      • 자료구조 (1)
      • 슬라이딩 윈도우 (1)
      • etc (2)
    • Git (5)
    • Web (24)
      • Vanilla JS (13)
      • TS (2)
      • React (7)
      • ETC (1)
    • React 공식문서 (번역, 공부) (11)
      • Quick Start (2)
      • Installation (0)
      • Describing the UI (9)
      • Adding Interactivity (0)
      • Managing State (0)
      • Escape Hatches (0)
    • Next.js 공식문서 (번역, 공부) (3)
      • Getting Started (2)
      • Building Your Application (1)
    • PS (432)
      • 백준 (187)
      • Programmers (105)
      • CodeUp (21)
      • STL (3)
      • 제코베 JS 100제 (50)
      • SWEA (0)
      • LeetCode (65)
    • IT (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록
  • 관리

공지사항

인기 글

태그

  • 제코베 JS 100제
  • 수학
  • Level 0
  • Level 1
  • js
  • programmers
  • Level1
  • codeup
  • 정렬
  • 문자열
  • recursion
  • tree
  • string
  • C++
  • Level 2
  • C
  • 백준
  • 그래프
  • 다이나믹 프로그래밍
  • LeetCode

최근 댓글

최근 글

hELLO · Designed By 정상우.
KimMinJun

Coding Note

Programmers / 과제테스트 / [실무 역량 과제] 게시물 레이아웃 재구성하기 (FE)
PS/Programmers

Programmers / 과제테스트 / [실무 역량 과제] 게시물 레이아웃 재구성하기 (FE)

2025. 5. 30. 14:53

게시물 레이아웃 재구성하기

type02-97.gif

📌 문제

테이블 형태로 나열되어 있는 게시물 조금 더 보기 좋게 카드 형태로 레이아웃을 수정하고, 정렬 및 북마크 기능을 추가하려고 합니다.

아래의 요구사항을 읽고 레이아웃 및 추가 기능을 완성해 주세요.

✅ 레이아웃

  • 주어진 Card 컴포넌트를 활용하여 카드 형태로 레이아웃을 수정해 주세요.
    • 이때, 카드 요소 마다 특정 id 값을 지정해 주어야 합니다.

✅ 정렬

  • 우측 상단의 셀렉트 버튼을 사용하여 게시물의 정렬 형태를 변경할 수 있습니다.
  • 게시물을 최근등록순과 조회순으로 정렬할 수 있으며, 게시물은 기본으로 최근등록순으로 정렬되어야 합니다.

✅ 북마크

  • 카드 우측 상단의 북마크 아이콘 버튼을 사용하여 게시물을 북마크 할 수 있습니다.
  • 게시물이 북마크 되었음을 나타내기 위해서 북마크 된 게시물의 경우 북마크 아이콘이 빨간색으로 표시되어야 합니다.
  • 북마크된 게시물의 경우에는 제일 우선으로 정렬되어야 합니다.

 

1. 카드 형태로 레이아웃 수정하기

주어진 Card 컴포넌트를 활용하여 카드 형태로 레이아웃을 수정해 주세요.
이때, 카드 요소 마다 특정 id 값을 지정해 주어야 합니다.

 

초기엔 Table 컴포넌트로 렌더링이 되어있는데, 이미 주어진 Card 컴포넌트로 바꾸면 된다.

나는 여기에서 Card 리스트를 보여주는 CardList 컴포넌트를 따로 만들었다.

import { useState } from 'react';
import { ulid } from 'ulid';
import posts from '../data/posts.json';
import Card from './Card';
import './Card.css';

const initPostList = posts.map((post) => ({ id: ulid(), ...post }));

function CardList() {
  const [postList, setPostList] = useState(initPostList);

  return (
    <div className="card-list">
      {postList.map((post) => (
        <Card key={post.id} {...post} />
      ))}
    </div>
  );
}

export default CardList;

 

"카드 요소 마다 특정 id를 지정해 주어야 합니다." 라는 요구사항이 있다.

그래서 일단 주어진 posts.json 에서 데이터를 불러오고,

배열의 각 요소마다 id를 ulid로 넣어주었다.

 

대부분의 사람들은 ulid보다 uuid가 더 익숙할 것이다.

둘의 차이점은 뭘까?

 

가장 큰 차이점은 ulid는 정렬이 가능하다.

타임스탬프에 랜덤값이 붙기 때문에,

시간순 정렬, 사전식 정렬이 가능하다.

 

물론, 이 테스트엔 포스트 생성기능도 없고,

upload_date가 따로 주어지므로 굳이 사용할 필요는 없다.

 

그리고 가장 바깥의 div 태그에 .card-list 라는 클래스를 넣어주었는데,

다음과 같이 카드를 나열하는 css를 적용하기 위해서 넣어주었다.

.card-list {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
}

 

이렇게 해주면, 다음과 같은 레이아웃을 구성할 수 있을것이다.

 

만약 flex-wrap을 wrap으로 안해주면,

카드들이 가로로 쭉 늘어져서 나열되게 되므로 주의하자.

 

2. 게시물 정렬 기능 구현하기

게시물을 최근등록순과 조회순으로 정렬할 수 있으며, 게시물은 기본으로 최근등록순으로 정렬되어야 합니다.

 

일단 기능을 구현하기 전에 타입부터 따로 정의해주었다.

나는 types 폴더에 따로 구현해놨다.

export type Post = {
  id: string;
  title: string;
  views: number;
  upload_date: string;
  bookmark: boolean;
};

 

그리고 드롭다운에서 선택하는 정렬 방법을 상태값으로 갖고 있어야 한다.

게시물에 관련한 정보는 CardList 컴포넌트에서 갖고 오기 때문에,

App 컴포넌트에서 변화하는 상태값을 props로 내려주어야 한다.

/* 여기에 주어진 요구 사항을 충족 시키기 위한 코드를 작성 및 수정해 주세요. */
import { useState } from 'react';
import './App.css';
import CardList from './components/CardList';

const NEWEST = '1';
const VIEWS = '2';

function App() {
  const [orderType, setOrderType] = useState(NEWEST);

  const handleOrderChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setOrderType(e.target.value);
  };

  return (
    <div className="container">
      <div className="section __order">
        <select id="order_type" value={orderType} onChange={handleOrderChange}>
          <option value={NEWEST}>최근등록순</option>
          <option value={VIEWS}>조회순</option>
        </select>
      </div>
      <div className="section">
        <CardList orderType={orderType} />
      </div>
    </div>
  );
}

export default App;

 

NEWEST와 VIEWS라는 상수 변수를 정의해주었는데,

각각 option 태그의 value 속성의 값으로 쓰이는 변수이다.

그냥 '1', '2'로 쓰이는 것 보다는 확실하게 보이도록 따로 선언해주었다.

 

그리고 정렬 방법을 변경하는 함수인 handleOrderChange를 구현해주었다.

select 태그의 onChange 이벤트 핸들러로 달아주고,

정렬 방법인 orderType 상태값을 CardList 컴포넌트에 props로 내려주었다.

 

다음으로는 정렬 기능 구현이다.

import { useState } from 'react';
import { ulid } from 'ulid';
import posts from '../data/posts.json';
import type { Post } from '../types';
import Card from './Card';
import './Card.css';

const NEWEST = '1';
const VIEWS = '2';

const initPostList: Post[] = posts.map((post) => ({ id: ulid(), ...post }));

interface CardListProps {
  orderType: string;
}

function CardList({ orderType }: CardListProps) {
  const [postList, setPostList] = useState<Post[]>(initPostList);

  const sortedPostList = [...postList].sort((a, b) => {
    if (orderType === NEWEST) {
      return (
        new Date(b.upload_date).getTime() - new Date(a.upload_date).getTime()
      );
    } else {
      return b.views - a.views;
    }
  });

  return (
    <div className="card-list">
      {sortedPostList.map((post) => (
        <Card key={post.id} {...post} />
      ))}
    </div>
  );
}

export default CardList;

 

크게 어려울 것은 없었다.

정렬 방법에 맞게 최신순, 조회순으로 sort()를 이용해 정렬해주면 된다.

최신순 정렬을 할 때는 Date()의 getTime()을 이용하면 1970년 1월 1일 00:00:00 UTC 기준으로,

그 후로 몇 밀리초가 지났는지 숫자로 반환해준다.

클수록 오래지났다는 의미이니까, 최신순이 된다.

 

3. 북마크 토글 기능 구현하기

카드 우측 상단의 북마크 아이콘 버튼을 사용하여 게시물을 북마크 할 수 있습니다.
게시물이 북마크 되었음을 나타내기 위해서 북마크 된 게시물의 경우 북마크 아이콘이 빨간색으로 표시되어야 합니다.

 

먼저, 색을 변하게 하기 전에 북마크 상태를 토글하는 기능을 만들어주어야 한다.

const handleBookmarkToggle = (id: string) => {
  setPostList((prevPostList) =>
    prevPostList.map((post) =>
      post.id === id ? { ...post, bookmark: !post.bookmark } : post
    )
  );
};

 

모든 포스트를 순회하면서,

만약 북마크 아이콘을 클릭한 포스트의 아이디와 같다면 해당 포스트의 bookmark를 바꿔주면 된다.

이전 상태값에 의존해서 상태 변경이 일어나기 때문에,

함수형 업데이트를 사용해주어야 한다.

이렇게 만든 함수를 onBookmarkToggle 이라는 이름의 prop으로 넘겨주었다.

<Card key={post.id} {...post} onBookmarkToggle={handleBookmarkToggle} />

 

이제 이 넘겨준 함수를 북마크 아이콘에 달아주면 된다.

<span className="icon bookmark" onClick={() => onBookmarkToggle(id)}>
  <i className="fa fa-bookmark" style={{ color: bookmark ? 'red' : '#fff' }}></i>
</span>

 

그리고 북마크 상태에 따라서,

style에 color를 red로 주거나, 기본값인 흰색으로 주면 된다.

 

4. 북마크 우선 정렬 기능 구현하기

북마크된 게시물의 경우에는 제일 우선으로 정렬되어야 합니다.

 

마지막으로, 북마크된 게시물은 가장 앞으로 와야한다.

그리고 북마크된 게시물들끼리도 설정한 정렬 방법에 따라 정렬이 이루어져야 한다.

간단하게 정렬 조건에 하나만 추가해주면 된다.

const sortedPostList = [...postList].sort((a, b) => {
  if (a.bookmark !== b.bookmark) {
    return a.bookmark ? -1 : 1;
  }

  if (orderType === NEWEST) {
    return (
      new Date(b.upload_date).getTime() - new Date(a.upload_date).getTime()
    );
  } else {
    return b.views - a.views;
  }
});

 

앞, 뒤 게시물끼리 북마크 여부를 판단해서,

북마크 여부 상태가 다를 경우에 북마크 된 것을 앞으로 보내주면 된다.

 

만약, 둘 다 북마크가 되어있거나, 되어있지 않을 경우에는

기존에 설정해준 정렬 방법에 따른 조건에따라 정렬을 수행할 것이다.

 

이렇게 완성!

 

주의!
초기에 설정한 값이 아니라, 데이터대로 다 표시되어 있는지 확인하자.

특히, Card 컴포넌트에 id값 잘 확인하자!

 

저작자표시 (새창열림)

'PS > Programmers' 카테고리의 다른 글

Programmers / Level 2 / 우박수열 정적분 / JS  (0) 2025.04.02
Programers / Level 3 / 양과 늑대 / JS  (0) 2025.02.28
Programmers / Level 2 / 리코쳇 로봇 / JS  (0) 2024.07.18
Programmers / Level 2 / 테이블 해시 함수 / JS  (0) 2024.07.18
    'PS/Programmers' 카테고리의 다른 글
    • Programmers / Level 2 / 우박수열 정적분 / JS
    • Programers / Level 3 / 양과 늑대 / JS
    • Programmers / Level 2 / 리코쳇 로봇 / JS
    • Programmers / Level 2 / 테이블 해시 함수 / JS
    KimMinJun
    KimMinJun

    티스토리툴바