HackerRank React-Basic

Jemin·2023년 5월 19일
0

개발 지식

목록 보기
8/51
post-thumbnail

과제 테스트 준비를 위해 해커랭크에서 제공하는 React-Basic 과제 테스트를 진행했다.
2문제 90분이었는데 1번 문제는 쉽게 통과하고 2번 문제에서 막혀있다가 시간이 끝나버렸다.
그래도 마무리는 하고 싶어서 깃허브에서 해당 테스트 프로젝트를 찾아서 끝까지 진행했다.

과제테스트 링크: HackerRank React-Basic

요구사항

React: Sorting Articles

Create a basic article sorting application, as shown below. Some core functionalities have already been implemented, but the application is not complete. Application requirements are given below, and the finished application must pass all of the unit tests.

The app has one component named Articles. The list of articles to be displayed is already provided in the app.

The app must have the following functionalities:

  • The list of articles is passed to the App component as a prop in the form of an array of objects.

  • Each article has the following three properties:
    - title: The title of the article [STRING]

    	- upvotes: The number of upvotes for an article [NUMBER]
    	- date: The publish date for the article in the format YYYY-MM-DD [STRING]
  • By default, the articles should be displayed in the table ordered by the number of upvotes in descending order.

  • Clicking on the "Most Upvoted" button should reorder and display the articles by the number of upvotes in descending order.

  • Clicking on the "Most Recent" button should reorder and display the articles by date in descending order.
    You can assume that each article has a unique publish date and number of upvotes.

  • Your task is to complete the implementation of src/App.js and src/components/Articles.js.

The following data-testid attributes are required in the component for the tests to pass:

  • The button to reorder and display the most upvoted articles must have the data-testid attribute "most-upvoted-link".

  • The button to reorder and display the most recent articles must have the data-testid attribute "most-recent-link".
    Each article must be rendered inside a element that has the data-testid attribute "article".

  • The title of each article must be rendered in a element that has the data-testid attribute "article-title".

  • The number of upvotes of each article must be rendered in a element that has the data-testid attribute "article-upvotes".

  • The publish date of each article must be rendered in a element that has the data-testid attribute "article-date".

간단하게 얘기해서 App.js파일과 Articles.js파일을 수정하면 되는데, 주어진 데이터를 화면에 리스트로 나타내고 버튼을 눌렀을 때 요구하는 정렬 조건대로 리스트를 정렬하면 된다.

기본 코드

App.js

import React from "react";
import "./App.css";
import "h8k-components";

import Articles from "./components/Articles";

const title = "Sorting Articles";

function App(articles) {
  return (
    <div className="App">
      <h8k-navbar header={title} />
      <div className="layout-row align-items-center justify-content-center my-20 navigation">
        <label className="form-hint mb-0 text-uppercase font-weight-light">
          Sort By
        </label>
        <button data-testid="most-upvoted-link" className="small">
          Most Upvoted
        </button>
        <button data-testid="most-recent-link" className="small">
          Most Recent
        </button>
      </div>
      <Articles articles={articles} />
    </div>
  );
}

export default App;

components/Articles.js

import React from "react";

function Articles() {
  return (
    <div className="card w-50 mx-auto">
      <table>
        <thead>
          <tr>
            <th>Title</th>
            <th>Upvotes</th>
            <th>Date</th>
          </tr>
        </thead>
        <tbody>
          <tr data-testid="article" key="article-idx">
            <td data-testid="article-title">article 1 title</td>
            <td data-testid="article-upvotes">article 1 upvotes</td>
            <td data-testid="article-date">article 1 date</td>
          </tr>
        </tbody>
      </table>
    </div>
  );
}

export default Articles;

Articles.js 구현하기

일단 Articles.js 파일은 props로 넘어온 데이터를 출력해주는 것이 전부이기 때문에, 먼저 구현했다.

import React from "react";

function Articles({ articles }) { // props로 articles 받아오기
  return (
    <div className="card w-50 mx-auto">
      <table>
        <thead>
          <tr>
            <th>Title</th>
            <th>Upvotes</th>
            <th>Date</th>
          </tr>
        </thead>
        <tbody>
    	  // map()으로 articles 배열의 각 요소를 렌더링한다.
          {articles.map((article, idx) => (
            <tr data-testid="article" key={`article-${idx}`}>
              <td data-testid="article-title">{article.title}</td>
              <td data-testid="article-upvotes">{article.upvotes}</td>
              <td data-testid="article-date">{article.date}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

export default Articles;

map()함수를 사용해서 각 요소들을 리스트로 만들 때는 각각의 요소들에게 고유한 key값이 필요하다.

참고자료: 리스트와 Key

주어진 데이터안에 고유한 값이 없기 때문에 index를 사용해서 고유한 키값을 지정해준다.

App.js 구현하기

버튼이 Articles.js가 아니라 App.js에 있기 때문에 App.js 파일에서 데이터를 정렬시켜줘야한다.

1차 시도

일단 정렬된 배열 두개를 먼저 만들어두고 버튼을 눌렀을 때 상태에 따라서 props를 다르게 넘겨주는 방식을 생각했다.

import React, { useState } from "react";
import "./App.css";
import "h8k-components";

import Articles from "./components/Articles";

const title = "Sorting Articles";

function App(articles) {
  const [mostUpvoted, setMostUpvoted] = useState(true); // 정렬 기준
  const MostUpvotedArticles = articles.articles.sort( // Upvoted 기준 내림차순 정렬
    (a, b) => b.upvotes - a.upvotes
  );
  const MostRecentArticles = articles.articles.sort( // Recent 기준 내림차순 정렬
    (a, b) => new Date(a.date) - new Date(b.date)
  );
  return (
    <div className="App">
      <h8k-navbar header={title} />
      <div className="layout-row align-items-center justify-content-center my-20 navigation">
        <label className="form-hint mb-0 text-uppercase font-weight-light">
          Sort By
        </label>
        <button
          data-testid="most-upvoted-link"
          className="small"
          onClick={() => setMostUpvoted(true)}
        >
          Most Upvoted
        </button>
        <button
          data-testid="most-recent-link"
          className="small"
          onClick={() => setMostUpvoted(false)}
        >
          Most Recent
        </button>
      </div>
      <Articles
		// mostUpvoted을 기준으로 props 넘겨주기
        articles={mostUpvoted ? MostUpvotedArticles : MostRecentArticles}
      />
    </div>
  );
}

export default App;

정렬은 됐지만 버튼을 눌렀을 때 동작하지 않았다.
아무래도 sort()함수가 원본배열을 변경하기 때문에 MostUpvotedArticlesMostRecentArticles값이 동일하게 바뀐 것 같다.

2차 시도

이번에는 버튼을 눌렀을 때 원본 리스트를 정렬하는 방식을 사용했다. 정렬된 리스트는 useState로 관리해서 props로 넘겨줬다.

import React, { useState } from "react";
import "./App.css";
import "h8k-components";

import Articles from "./components/Articles";

const title = "Sorting Articles";

function App(articles) {
  const MostUpvoted = () => { // Upvoted 기준 정렬 함수
    articles.articles.sort((a, b) => b.upvotes - a.upvotes);
  };
  const MostRecent = () => { // Recent 기준 정렬 함수
    articles.articles.sort((a, b) => new Date(a.date) - new Date(b.date));
  };
  const [newArticles, setNawArticles] = useState(MostUpvoted());
  return (
    <div className="App">
      <h8k-navbar header={title} />
      <div className="layout-row align-items-center justify-content-center my-20 navigation">
        <label className="form-hint mb-0 text-uppercase font-weight-light">
          Sort By
        </label>
        <button
          data-testid="most-upvoted-link"
          className="small"
          onClick={setNawArticles(MostUpvoted())}
        >
          Most Upvoted
        </button>
        <button
          data-testid="most-recent-link"
          className="small"
          onClick={setNawArticles(MostRecent())}
        >
          Most Recent
        </button>
      </div>
      <Articles articles={newArticles} />
    </div>
  );
}

export default App;

결과는 무한루프에 빠져버렸다.😭
어디서 문제가 발생했는지 찾아봤는데 onClick속성에서 함수를 넘겨주는 방법이 잘못된 것이다. 그동안 알고리즘 문제만 주구장창 풀면서 과제를 등한시한 결과라고 생각한다.

함수를 그냥 적는 것이 아니라 onClick={() => function()}으로 수정했다.

그 이후에도 에러의 향연이었는데 props로 넘겨준 값이 undefined로 나와서 발생하는 문제였다.
useState에서 반환하는 값도 없이 그저 정렬함수를 호출하는 멍청한 행동이 원인이었다.

3차 시도

import React, { useState } from "react";
import "./App.css";
import "h8k-components";

import Articles from "./components/Articles";

const title = "Sorting Articles";

// 정렬 함수들을 App함수 밖으로 빼줬다
const MostUpvoted = (articles) => {
  [...articles].sort((a, b) => b.upvotes - a.upvotes);
};
const MostRecent = (articles) => {
  [...articles].sort((a, b) => new Date(a.date) - new Date(b.date));
};

function App(articles) {
  const [newArticles, setNawArticles] = useState(
    MostUpvoted(articles.articles)
  );
  return (
    <div className="App">
      <h8k-navbar header={title} />
      <div className="layout-row align-items-center justify-content-center my-20 navigation">
        <label className="form-hint mb-0 text-uppercase font-weight-light">
          Sort By
        </label>
        <button
          data-testid="most-upvoted-link"
          className="small"
          onClick={() => setNawArticles(MostUpvoted(articles.articles))}
        >
          Most Upvoted
        </button>
        <button
          data-testid="most-recent-link"
          className="small"
          onClick={() => setNawArticles(MostRecent(articles.articles))}
        >
          Most Recent
        </button>
      </div>
      <Articles articles={newArticles} />
    </div>
  );
}

export default App;

많은 수정을 거치고 이제는 분명 동작하리라 믿고 브라우저를 봤는데,

또 같은 에러가 발생했다. undefinedmap()함수를 사용할 수 없다는데 그렇다면 아직도 props로 undefined가 넘어간다는 뜻이다.

눈 빠지게 디버깅을 하다가 습관의 문제라는 것을 깨달았다. 평소 화살표 함수 뒤에 습관적으로 {}를 붙여주는 행동 때문에, 정렬하는 함수들에서 정렬한 값이 반환되지 않았다.

문제의 함수들..

const MostUpvoted = (articles) => {
  [...articles].sort((a, b) => b.upvotes - a.upvotes);
};
const MostRecent = (articles) => {
  [...articles].sort((a, b) => new Date(a.date) - new Date(b.date));
}; 

최종 코드

import React, { useState } from "react";
import "./App.css";
import "h8k-components";

import Articles from "./components/Articles";

const title = "Sorting Articles";

// 정렬 함수들, 각각 정렬된 리스트를 반환한다.
const MostUpvoted = (articles) =>
  [...articles].sort((a, b) => b.upvotes - a.upvotes);
const MostRecent = (articles) =>
  [...articles].sort((a, b) => new Date(a.date) - new Date(b.date));
function App(articles) {
  // 정렬된 리스트를 useState에 저장해서 관리한다.
  const [newArticles, setNawArticles] = useState(
    MostUpvoted(articles.articles)
  );
  return (
    <div className="App">
      <h8k-navbar header={title} />
      <div className="layout-row align-items-center justify-content-center my-20 navigation">
        <label className="form-hint mb-0 text-uppercase font-weight-light">
          Sort By
        </label>
        <button
          data-testid="most-upvoted-link"
          className="small"
		  // 클릭 시 정렬된 리스트로 상태값을 변경한다.
          onClick={() => setNawArticles(MostUpvoted(articles.articles))}
        >
          Most Upvoted
        </button>
        <button
          data-testid="most-recent-link"
          className="small"
		  // 클릭 시 정렬된 리스트로 상태값을 변경한다.
          onClick={() => setNawArticles(MostRecent(articles.articles))}
        >
          Most Recent
        </button>
      </div>
      <Articles articles={newArticles} />
    </div>
  );
}

export default App;

마무리

그동안 과제테스트보다는 알고리즘에 비중을 높게 두고 공부했어서 이렇게 간단한 Basic 문제도 못푸는 지경에 이르렀다고 생각한다. 간간히 개인 프로젝트를 진행하고 있긴 하지만 앞으로 과제테스트에도 시간을 더 투자해서 상태관리나 이벤트처리같은 기본기를 더 학습해야겠다.

과제 제출 이후 시간이 조금 지나면 이렇게 귀여운 자격증(?)을 준다. 어디에 사용할 수 있는지 잘 모르겠는데 일단 공짜로 주니까..🎉

profile
경험은 일어난 무엇이 아니라, 그 일어난 일로 무엇을 하느냐이다.

0개의 댓글