레벨1 영화 리뷰 - step1

개요

미션
기간
Repository
PR & Review
github page

영화 리뷰 1단계

23-03-14 - 23-03-16


🚀 미션 회고

드디어 마지막 미션이 시작되었다. 지금까지 시간이 어떻게 흘러갔는지 모르겠다. 하루하루가 너무 빠르고 일주일은 금방 지나갔다. 벌써 마지막 미션이라는 것이 아쉬웠다. 물론 레벨2에 가면 또 다른 미션이 기다리고 있지만 빠르게 흘러가는 시간은 애석하게만 느껴진다.

영화 리뷰 1단계 미션의 목표는 다음과 같다.

  • API 연동을 위한 테스트 환경 경험

  • 실제 동작하는 API를 통한 비동기 통신

  • UX 경험 개선을 위한 더 보기(페이징) 구현

  • 역할과 책임에 따라 관심사를 분리할 수 있는 마지막 기회

앞으로 실제 현업에 나가서 백엔드과의 협업을 통해 웹페이지를 만들어야 하는 프론트엔드의 입장에선 API의 통신은 필수라고 본다. 이를 위한 미션이 바로 영화 리뷰였다. 데이터를 받아오고 받아온 데이터를 바탕으로 사용자에게 친절한 UI를 만들어야 했다. UI를 만드는 과정에서 처음 접하는 개념이 있었다. 바로 스켈레톤 UI였다. 지금껏 사용해보지 않았던 개념이라 무엇인지 궁금했다. 하지만 이는 평소에 많이 접했던 것이었다. 유튜브에서 영상이 로드되기 전 회색 배경이 바로 스켈레톤 UI이다. 이것이 있느냐 없느냐에 따라 사용자들은 지루할 수도 있고 지루하지 않을 수 있다. 이러한 것들을 직접 구현하면서 개발자로서가 아닌 사용자로서 웹 페이지를 생각할 수 있었다.

API 통신은 기본적으로 비동기이다. 미션을 시작하기 전까지만 해도 동기, 비동기에 대해 잘 알지 못했다. 동기를 살펴보면 동시에 일어난다.라는 뜻을 가진 단어로 알고 있었고, 이는 자바스크립트의 함수가 동시에 일어난다고 생각하였다. 즉, 시간이 필요한 함수조차 동시에 실행되는 것이 동기라고 이해하고 있었다. 하지만 이는 완전히 틀린 개념이었다. 시간이 필요한 함수, 예를 들어 setTimeout, Promise와 같은 함수는 비동기 성격을 가지는 함수이다. 이들은 콜스텍에서 실행되지 않고 task queue에 담겨져(queue의 종류는 3가지가 있다.) 콜스텍에 비어졌을 때 실행된다. 이것이 바로 프로그래밍에서 말하는 비동기이다. 이를 동기로 바꿔주는 것이 콜백함수, async/await, Promise의 then이다. 앞선 함수의 사용은 알고 있었는데 개념을 정확히 알고 있지 않아 처음엔 혼란이 있었다. 하지만 여러 크루들의 이야기를 들으며 나도 서서히 이해가 되었다.

아무튼!! API 통신과 더불어 사용자에게 친절한 UI를 만드는 것이 이번 미션의 중요한 목표였다. 결론적으론 만족한 결과물이 나왔지만, 프로그래밍 요구 사항엔 도달하지 못한 듯 하여 아쉬움이 남았다. 추가적으로 배포가 매우.. 어려웠다ㅠㅠ


👬 페어프로그래밍 진행 과정

레벨1 마지막 미션의 페어는 우코였다. 평소에 우코와 친해지고 싶었기 때문에 정말 좋은 기회라고 생각했다. 많은 이야기를 나누면서 페어프로그래밍을 진행하고 싶었다. 하지만 문제가 발생하였다. 우코가 미션 시작 다음날인 수요일에 예비군에 간다는 것이었다. 때문에 수요일 오전과 오후는 혼자서 시간을 보내야 했다. 다른 크루들은 페어프로그래밍을 통해 미션을 완성해 가는데, 난 그러지 못해서 마음은 점점 초조해졌다. 마음이 초초해져서 그런지 코드의 구조에 대해서는 생각을 하지 않고 나의 생각을 코드로 옮기기 바빴다. 돌이켜 생각해보면 페어프로그래밍이 아니라 혼자서하는 프로그래밍이었다. 나의 생각을 페어에게 전달하지 않았을 뿐더러 코드의 구조 또한 나만 생각하고 있는 것이었다. 페어프로그래밍의 본질에서 벗어난 느낌이 들었다.

다행히 우코가 먼저 이야기를 해야할 것 같다고 말을 했고, 나도 정신을 차리게 되었다. 아마 이야기 없이 각자 코딩을 했다면 시간은 더 많이 걸렸을 것이다. 급한 마음을 잠시 접어두고 이야기를 통해 코드의 구조를 생각했다. 다행히 금요일 미션 제출 시간 전까지 어느 정도 완성할 수 있었다. 보다 시간이 충분해 이야기를 더 많이 나누었으면 어땠을까 하는 생각이 든다. 그래도 우코와 이전보다 더 친해졌으니 앞으로 더 많이 이야기를 나눌 수 있을 것이다. 지금은 그걸로 만족한다!


👍 잘한 점

  • 웹 컴포넌트가 아닌 새로운 시도를 해보았다.

  • 시간이 부족한 만큼 더 집중하여 미션을 진행하였다.

  • 이전보다 타입스크립트를 더 알차게 사용했다.


👎 아쉬운 점

  • 초초한 마음에 내 생각을 잘 전달하지 않았다.

  • 코드 구조와 설계에 대한 이야기가 부족했다.


👊 앞으로의 각오

  • 아무리 시간이 없다해도 본질을 잊지말자.

  • 마음의 여유를 가질 수 있도록 훈련하자.


🛠️ 리팩터링

import 순서

import 순서에 대해 고민을 하였다. 아무런 규칙없이 import를 하게 된다면 어떤 문제가 있을까? 당장 떠오르는 것은 협업에서의 효율성이다. 지금은 많은 라이브러리를 사용하지 않지만 큰 프로잭트에 들어가게 된다면 다양한 라이브러리를 사용하게 될 것이다. 이때, import 순서가 뒤죽박죽이라면 분명 혼란이 올 때가 있을 것이다. 이를 방지하기 위해 리팩터링을 진행하였다.

eslint에 "import/order" role를 적용하여 순서를 강제하였다. 해당 리팩터링을 진행하면서 좋았던 점은 스스로 eslint를 설정했다는 것이다. 지금까진 누군가가 설정해 둔 eslint를 사용했었다. 그래서 그런지 설정하는 것조차 순탄치 않았다. 분명 잘 성정했다고 생각했는데, role이 적용되지 않아서 무엇이 문제인지 계속 찾았다. 혼자 해결책을 찾지 못해 주변 솔로스타에게 물어보았다. 설정에 관해 문제가 있을 경우, 무엇이 문제인지 확인하기 위해선 터미널의 OUTPUT를 보면 된다고 해서 봤더니.. 세상에 무엇이 설치되지 않았다는 것이 보였다. 역시.. 혼자 해결하는 것도 좋지만 주변에 도움을 요청하는 것도 좋은 방법이다..! 아래는 내가 이번에 적용한 "import/order" role이다.

{
  // ...
  "rules": {
    "import/order": [
      "error",
      {
        "groups": ["builtin", "external", "internal", "type"],
        "newlines-between": "always",
        "pathGroups": [],
        "alphabetize": {
          "order": "asc",
          "caseInsensitive": true
        }
      }
    ],
    "no-unused-vars": "off"
  },
  // ...
}

URL 구조에 따른 상수화

이번 미션의 첫 제출은 이전 미션처럼 상수화까지 고려하지 못했다. 때문에 상수화도 리팩터링 목록에 있었다. 그 중 이전과 다르게 특별히 신경쓴 것이 있는데 바로 URL 구조에 따른 상수화였다. api는 URL주소를 가지고 있고 이를 구조에 따라 나눌 필요가 있어다.

나는 다음과 같이 URL주소를 나누고 상수화를 진행하였다.

const BASE_API_PROTOCOL = 'https';
const BASE_API_DOMAIN = 'api.themoviedb.org';
const POPULAR_API_PATH = '3/movie/popular';
const SEARCH_API_PATH = '3/search/movie';

객체로 메서드 관리하기

같은 성격의 도메인 객체를 만들어야 할 때 어떻게 해야할까? 새로운 파일을 만들어야 할까? 같은 파일 아래에 새로운 함수를 만들어야 할까? 잉? 그러면 파일명을 바꿔야겠네...

새로운 도메인 객체가 추가될 때, 어떻게 해야 수고를 덜 수 있을끼? 정답인지는 모르겠지만, 같은 성격의 도메인 객체를 하나의 객체로 묶어 관리를 하기로 하였다.

이번 미션에는 다음과 같은 도메인 객체를 만들었었다.

export const processMovieData = ({
  page,
  results,
  total_pages: totalPages,
}: FetchedMovieJson) => {
  const movies: MovieItem[] = results.map((result) => ({
    title: result.title,
    posterPath: `${MOVIE_IMAGE_URL}${result.poster_path}`,
    voteAverage: result.vote_average,
  }));

  return { page, movies, totalPages };
};

api를 통해 전달 받은 데이터를 가공하는 함수이다. 해당 함수가 있는 파일은 이 함수가 export를 하고 있기 때문에 비슷한 성격의 객체를 추가하기 위해선 구조를 바꿔야 했다.

이를 대비하여 전체 구조를 다음과 같이 바꾸었다.

export const dataProcessors = {
  processMovieData: ({
    page,
    results,
    total_pages: totalPages,
  }: FetchedMovieJson) => {
    const movies: MovieItem[] = results.map((result) => ({
      title: result.title,
      posterPath: `${MOVIE_IMAGE_URL}${result.poster_path}`,
      voteAverage: result.vote_average,
    }));

    return { page, movies, totalPages };
  },
};

실제로 위와 같이 관리를 하니, 2단계 미션에서 새로운 도메인 객체를 추가할 때 큰 어려움이 없었다. 어려움이라기 보단 수고가 덜 했다.


📅 2023-03-26

Last updated