[회고] vue.js 과제 회고

imloopy·2022년 5월 19일
0

Today I Learned

목록 보기
26/56
post-custom-banner

[회고] Vue.js 회고

Vue.js 회고

이번 과제는 주어진 API를 활용하여 영화 검색 사이트를 만드는 것이었다. 이번 과제에서는 기존과는 달리 좀 더 체계적으로 접근하기 위하여 과제를 시작하기 전에 스케치를 사전에 진행하여 전체적인 구조를 잡고 과제를 진행하였다.

구조 잡기

기본 요구사항과 특별 요구사항 중, 기본 요구사항을 먼저 만족하기 위한 구조를 잡았다.

  • 검색어를 입력해 영화를 검색
    • v-model을 이용하여 양방향 데이터 바인딩 진행

페이지 구성

  • / : 구글처럼 home 화면으로 제목과 단 하나의 검색 바로만 구성
  • /search: 검색 결과를 보여주는 페이지
  • /movie/:id: id를 이용하여 해당 영화의 detail data에 접근하는 페이지

페이지간 이동

이 부분은 가장 고민이었던 부분 중 하나이다. SPA의 장점을 극대화 하기 위하여 쿼리 정보를 내부에 저장하여 쿼리가 변경되면 데이터를 다시 fetch 하는 방식으로 구성을 할 지, 아니면 RouterLink를 이용하여 직접 페이지를 변경하는 방식을 사용할 지 고민했다.

해당 프로젝트에서는 쿼리가 포함된 주소를 통해 접근했을 때, 해당 데이터에 바로 접근이 가능하고, bot이 데이터를 수집했을 때 알맞은 데이터를 보여줄 수 있도록 검색 엔진 최적화를 하고자 페이지를 직접 이동시키는 방식을 사용했다.

해당 페이지를 직접 이동시킬 때, 컴포넌트(Search)의 재렌더링이 발생할 줄 알고 우려하였다. 따라서 created를 통해 처음 컴포넌트가 렌더링 시 data를 fetch하도록 하였고, 페이지를 이동할 때 자동으로 created hook이 재실행 될 줄 알았는데 실행되지 않았다. (컴포넌트의 재렌더링 발생 X) 따라서 이 부분은 안심하고 적용할 수 있었다. 다만, watch 옵션을 통해 쿼리의 변경을 감지하여 data를 fetch하려고 할 때, 찾기 어려운 버그가 발생했었는데 이 부분은 하단부에 서술하도록 한다.

pagination

  • pagination을 단순 1~5 방식으로 구현할 지, 아니면 무한 스크롤 방식으로 구현할 지 → 이 부분은 일반적인 1~5방식의 페이지네이션으로 구현하였다. 무한 스크롤 방식은 footer에 접근이 어렵고, 검색 엔진 최적화에 불리하다고 판단했기 때문
  • pagination 로직과 컴포넌트의 완전한 분리의 시도
    • 로직은 클래스로 할 지, 아니면 함수 모듈로 구현할 지 고민하였음
    • 이전에는 클래스로 구현하는 것을 선호해서 클래스로 구현할 까 고민했으나, 구현을 해보다 보니 constructor가 굳이 필요가 없을 듯 하여 function module 형식으로 변경하였다.
    • 자바스크립트의 class는 syntatic sugar이므로 이번에는 사용하지 않음
    • 실무에서는 어떤 방식이 더 선호되는지 몰라서 이 부분은 PR에 질문 사항으로 남겨 놓음

Loading

  • 전역 store로 구성하여 data를 fetch 할 때마다 isLoading의 변화가 일어나도록 함
  • 노출되는 로딩 컴포넌트의 구성은 어떻게 할 지?? 이 부분은 구현하면서 바뀔 수 있으니 생각하지 않았음

이슈

  • route.push 하지만 같은 페이지 내에서 쿼리만 바꾸는 경우 refresh가 일어나지 않는 문제 있음 (path가 바뀌지 않아 페이지 이동으로 처리하지 않음)
    • vue 자체가 한번 mount 된 후 다시 렌더링을 하지 않도록 최적화 되어 있었다.
    • 따라서 watch 옵션을 주어 query가 변경될 때 다시 데이터를 fetch하도록 구성
<script>
export default {
	computed: {
		query() {
			return this.$router.query
		}
	},
	watch: {
		query() {
			this.fetchData()
		}
}
</script>
  • /search에서 /movie/:id 이동시, 불필요한 watch가 발생하여 movie page에서 데이터를 fetch하기 전에 잘못된 쿼리로 데이터를 요청하고, isLoading 옵션이 오작동하는 문제
    • 이 부분은 과제를 전부 마친 후 테스트를 하면서 발견하였다.

    • movie detail 페이지로 이동할 때 path가 변경됨

    • created 가 화면에 컴포넌트가 구성되기 전에 호출되지만, fetchData는 비동기 함수이므로 데이터를 받아올 때 까지 소요시간이 필요

    • 이전 페이지의 watch 옵션으로 걸어 놓은 데이터 요청이 fail되고, isLoading = false가 됨

    • 따라서 화면에 데이터 없이 나머지 부분만 잠시 렌더링되고, 그 이후에 정상적으로 화면이 구성됨

    • 요청에 대한 응답이 빠르면 해당 현상이 잘 확인되지 않지만 요청이 느리다면 발견하기 쉽다.

      이 부분을 해결하기 위하여 여러 방식을 시도하였다.

    • 전역 store의 isLoading을 처리하기 위하여 actions에 fetchData 함수를 구성하였는데, 이 부분의 로직에 console.log 찍어서 확인 → 아무런 문제가 없었음. 너무 단순했기 때문에

    • list → detail로 path 변경될 때 나타났던 문제점이기 때문에 해당 라우터의 fetchData에 console.log를 찍어보았는데 역시 문제가 발생하지 않았다.

    • 가장 마지막으로 watch또한 fetchData 함수를 호출하기 때문에 이 부분에서 console.log를 찍어보았는데, 여기서 페이지 이동시에 fetchData를 호출하는 현상을 확인할 수 있었다. search?s=title&page=1에서 movie/:id로 이동하면서 this.$route.query가 변경되고, 이에 따라서 페이지를 이동하기 전에 fetchData 함수가 호출되는 문제였다.

    • 이 부분을 해결하기 위하여, path는 바뀌지 않기 때문에 watch 감지가 일어나도 해당 path가 아니면 데이터를 요청하지 않도록 처리하였다.

      글로 쓰니까 금방 해결한 것 같은데 이거 처리하느라 거의 한시간 걸렸다….

  • paginator 구성시 totalResult에 NaN이 들어가버리는 문제 → error 발생
    • 비동기적으로 데이터를 불러오기 전에 paginator가 먼저 로딩되서 발생하는 문제

    • validation 코드를 잘 짜놔서 그런지 다행히도 금방 버그를 발견할 수 있었음

    • 해결:

      <Paginator
            v-if="!isNaN(Number(apiData.totalResults))"
            :total-items="Number(apiData.totalResults)"
            :offset="5"
            :post-per-page="10"
            :base-url="`/search?&s=${query.s}&page=`"
          />
    • 원래 생각으로는 데이터가 변경되면 자동으로 재렌더링이 발생하여 paginator가 다시 렌더링 되어야 하는데 그렇지 않았던 것 같다. 이 부분은 다시 한번 확인해야겠다.

  • webpack 환경으로 netlify 배포시, process.env로 만들어 놓은 환경 변수들을 읽지 못한 문제
    • 정확히는 process 객체를 읽어들이지 못하는 문제가 있었다.
    • 구글 검색을 통해 기존에 사용했던 플러그인의 사용 방식에 문제가 있었음을 깨닫고, webpack에서 사용하는 dotenv-webpack 플러그인을 설치한 뒤, netlify에서 설정한 환경 변수를 읽을 수 있도록 systemvars: true 옵션을 주어 문제 해결이 가능했다. DotenvPlugin({ systemvars: true })
    • CSR의 단점인 다른 페이지에서 새로고침 시 404Error를 해결하기 위하여 _redirects 파일을 구성하였다.
    • 다만, 클라이언트에서는 여전히 API_KEY를 읽을 수 있었다. 이 부분은 다른 방법이 있는지 한 번 확인해 봐야겠다.

내 과제의 차별점(이 부분은 맘에든다)?

Rating 컴포넌트

https://user-images.githubusercontent.com/56826914/169028097-457d44af-0458-48e5-9d84-c5507237ec9f.png

뭔가 가독성 좋게, 그리고 해당 회사의 시그니처 컬러를 따서 만들어 보고자 하였는데, 꽤 괜찮게 뽑혀서 기분이 좋다.

반응형 페이지 구현

이번 과제가 CSS에 중점을 둔 과제는 아니기 때문에 굳이 반응형 페이지를 구현할 필요는 없었지만, 저번 과제를 통해 CSS에 굉장히 약하다는 것을 깨닫고 다시 연습을 하고자 CSS에 신경을 썼다. 특히 flex 처리가 미흡했어서, 이 부분을 연습하면서 반응형 페이지 역시 구현하였다.

  • 사진 처리가 제일 어려웠다. 사진의 비율이 제각각이라서 컴포넌트의 높이 역시 제각각인 문제가 발생했고, 이는 list의 movie-card 컴포넌트의 높이를 200px로 고정하는 것으로 해결하였다.

isLoading 데이터 처리

사실 react나 vue나 props로 데이터를 내려주는 것은 프로젝트의 크기가 커질 수록 버그의 발생 가능성을 높이기 때문에 모든 데이터를 전역 스토어로 관리하려고 하였으나, 해당 데이터는 현재 각 컴포넌트에서만 사용하기 때문에 굳이 전역 스토어를 구성하지 않았다. 다만, 모든 컴포넌트에서 공통적으로 사용하는 isLoading 식별자만 전역 스토어에서 관리하도록 하였다.

actions에 각 REST API의 주소마다 메서드를 만드는 것이 아닌, 해당 부분은 컴포넌트단에서 처리하도록 하고, actions는 fetchData 하나의 메서드만 두고, 콜백함수를 받도록하였다. fetchData를 통해 api를 요청하게 되면, 자동으로 isLoading을 처리할 수 있도록 하였고, 다른 컴포넌트에서는 isLoaindg에 대하여 신경 쓸 필요가 없도록 하였다. 이 부분이 클래스 설계 원칙(?) 은닉화? 그런 부분을 잘 지켰다고 생각했다. (오직 나만의 생각이다)

Loading 컴포넌트가 출현 시마다 랜덤한 색깔이 설정되도록 함

부트스트랩의 primary, secondary, danger… 등등의 옵션이 담긴 배열을 만들고, 랜덤한 인덱스가 호출되도록 하였다. 결과적으로는 이쁘게 잘 나왔다고 생각한다.

느낀 점

스케치 관련

사전 스케치를 처음 해보았는데, 효율도 좋고 설계에 투자하는 시간 및 변경 시간을 줄일 수 있어서 결과적으로 일찍 과제를 끝낼 수 있었던 것 같다.

  • 스케치 과정에서 굵직한 구현 부분을 고민하고 결정함 → 추후에 뒤엎는 경우의 수 감소
  • 완전 세세한 부분까지 스케치를 하는 것이 아니라, 스케치 말 그대로 이러 이러한 방식으로 진행을 해 나가자고 적어두는 부분만 하더라도 의사 소통 비용이 감소하였고, 마음의 평화를 얻을 수 있었음
  • 프로젝트의 shape이 드러나면서 어디까지 끝내야겠다는 목표가 생기고 동기 부여가 됨

앞으로도 사전 스케치를 꼼꼼히 진행하여 더 능률을 높이는 방식으로 프로젝트를 진행해야겠다.

post-custom-banner

0개의 댓글