SWR 및 기타 등등

이민석·2023년 2월 5일
1

Frontend

목록 보기
4/5
post-thumbnail

SWR

가장 많이 사용되었던 상태관리 라이브러리 Redux는 비동기 로직을 처리하기위해서 미들웨어를 사용해야한다. redux-thunk나 redux-saga같은 라이브러리는 리덕스가 비동기 로직을 실행할 수 있도록 도와줬다. 그러나, 비동기 요청을 처리하는 과정이 매끄럽지 못하고, 동일한 API가 여러번 호출되어 효율성이 좋지 못하고, 상태가 반영되는 정확한 시점을 알 수 없다는 등의 단점이 존재했다.

SWR은 위의 단점을 해결하기 위해 탄생했다.

SWR : stale - while - revalidate 의 약자로, 캐시로부터 데이터를 먼저 얻은 후에, fetch 요청을 통해 재검증을 하고, 최종적으로 최신화된 데이터를 가져오는 전략이다!

import useSWR from 'swr'

function Profile() {
  const { data, error, isValidating, mutate } = useSWR('/api/user', fetcher)

  if (error) return <div>failed to load</div>
  if (isLoading) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}

SWR은 useSWR hook을 사용하는데, 첫번째 인자로 key문자열과 두번째 인자로 fetcher 함수, 세번째인자로 options를 받는다.
key는 가져오고자 하는 데이터를 구분하는 식별자이고, 동일한 data를 가져오고 싶은 경우 같은 key를 사용하면 된다. 또한, key는 fetcher함수의 인자로 전달되는데, 그렇기 때문에 대부분의 비동기 로직에서 key는 fetching할 url이 된다.
fetcher함수가 꼭 비동기 로직일 필요는 없으며, 비동기 로직으로 반환된 data를 가공해서 return 하는 함수여도 된다!

useSWR의 반환 값
1. data는 fetcher함수의 return 값이 된다.(아직 수행되지 않았다면 undefined)
2. error는 fetcher가 실패할경우 반환된다.
3. isValidating은 요청이나 갱신로딩의 여부를 반환한다.
4. mutate(data? , shouldRevalidate?) 수동으로 캐시된 데이터를 업데이트 하기위한 함수이다.

useSWR은 한번 fetch한 원격 저장소의 데이터를 클라이언트 측에서 캐시로 가지고 있는다. 그리고 동일한 데이터에 대한 요청을 받을 경우, 새로운 요청을 보내서 원격 저장소에서 데이터를 불러오는것이 아니라! 이전에 캐시로 가지고 있던 데이터를 리턴해준다.
SWR은 주기적으로 캐시데이터를 업데이트하여, 거의 최신의 데이터를 사용할수 있도록 해준다.
클라이언트 측에서는 데이터가 원할때 마다 요청을 보내지만, SWR은 내부적으로 캐시된 데이터만 제공하기 때문에, 많은 요청을 보내도 서버에는 주기적으로 SWR이 fetch하는 요청만 전달되게 되어 서버측의 부하가 적어진다는 장점이 있다.

예를 들어서, 서로 다른 두 파일에서 아래의 코드를 사용했다고 가정하자

const {data, error, mutate} = useSWR('/api/users', url => axios.get(url).then(res => res.json()));

두 파일은 항상 같은 데이터를 공유하게되는 것이다.

useSWR에는 여러가지 옵션이 있다. 이 링크에서 확인할 수 있다.

위에서는 캐시된 데이터를 SWR이 주기적으로 fetch한다고 설명했는데, 클라이언트 측에서 빠른 업데이트가 필요할 경우, mutate 함수를 사용해서 캐시된 데이터를 직접 업데이트 할 수 있다.
mutate(data? , shouldRevalidate?)
mutate를 파라미터 없이 호출할 경우, useSWR훅이 원격저장소에서 data를 fetch하여 클라이언트에 저장된 캐시 데이터를 업데이트 한다.
mutate(data, true)를 호출할 경우, 캐시데이터가 data로 업데이트된 후, SWR에서 원격저장소에서 data를 fetch하고, 캐시데이터와 fetch한 data가 다를경우, fetch한 값으로 캐시데이터를 변경한다.
mutate(data, false)를 호출할 경우, 캐시데이터가 data로 업데이트 되고, 재검증 과정은 생략한다. (그러나, 주기적으로 SWR은 원격 저장소에서 data를 fetch하기 때문에, 올바르지 않은 data로 바꿨을 경우, 올바른 data로 일정 시간 뒤에 바뀌게 될 것이다)

css-transition

css에 transition이라는 속성이 있는지 이제야 알게 되었다;;

transition은 한 컴포넌트의 CSS 속성이 변경될때 나타나는 애니메이션을 조절하는 속성이다.

애니메이션이 가능한 속성은 여기서 확인할 수 있다.

div {
	transition : <property> <duration> <timing-function> <delay>;
}

transition은 위와같이 적용할 property, transition이 완료되기까지 걸리는 duration, transition의 timing function, transition이 일정 시간 뒤에 시작하게 만들고 싶을때 사용하는 delay로 구성된다.

https://jsfiddle.net/kkyo/3jhr2x9e/4/
css transition을 활용하면, 위 사이트처럼, 막대그래프가 점점 차오르는 애니메이션을 확인할 수 있다.

event bubbling과 stopPropagation

다들 잘 알고 있겠지만, event bubbling이란, 하위 컴포넌트에서 발생한 이벤트가 상위 컴포넌트까지 전달되는 것이다. 반대의 개념으로 상위 컴포넌트에서 발생한 이벤트가 하위 컴포넌트로 전달되는 캡쳐링이라는 현상이 있다.

자바스크립트에서 거의 모든 이벤트는 버블링된다!
마찬가지로, click 이벤트 또한 버블링되는 이벤트 중 하나이다.

또한, event 객체의 메서드인 event.stopPropagation()을 사용하면, 이벤트의 버블링과 캡쳐링을 중단시킬 수 있다.

<body onclick="alert(`버블링은 여기까지 도달하지 못합니다.`)">
  <button>클릭해 주세요.</button>
</body>

예를 들어서, 위와 같은 코드에서 button을 클릭하면, 일반적인 상황에서는 body 컴포넌트까지 click 이벤트가 버블링되어 전달되기에, alert가 실행된다.

<body onclick="alert(`버블링은 여기까지 도달하지 못합니다.`)">
  <buttontoken tag"></button>
</body>

그러나, 위와 같이 button 컴포넌트에 event.stopPropagation을 적용하면, button을 아무리 클릭해도, 이벤트 버블링이 일어나지 않기 때문에, alert문이 실행되지 않는다.

click 이벤트와 stopPropagation()을 사용하면, 페이지에 modal이 띄워졌을때, modal의 바깥쪽을 클릭하면, modal이 닫히는 기능을 구현할 수 있다.

예시 코드

gravatar

github에서, 계정에 이름에 따라서, 자동으로 프로필 이미지가 설정된 것을 본적이 있을 것이다.
gravatar는 문자열을 parameter로 받아서, 무작위 프로필 이미지를 생성시켜준다.

npm i gravatar

userData에 email이 저장되어있다고 가정해보자.
<img src={gravatar.url(userData.email, { s: '28px', d: 'retro' })}/>
다음과 같이 작성하면, 크기가 28px 정사각형이고, retro 타입의 email에 기반한 프로필 이미지를 얻을 수 있다.

위 이미지는 userData.emaila@a.com 일때 생성된 이미지이다.

위 이미지는 userData.emailtest@gmail.com 일때 생성된 이미지이다.

d 옵션은 identicon , monsterid , wavatar , retro , robohash , mp가 있다.

cross-origin cookie

기본적으로 브라우저에서는 다른 도메인의 서버와 데이터를 주고 받을 수 없는데, 이것이 CORS이다.
CORS는 프론트엔드에서 프록시를 이용하거나, 백엔드에서 CORS를 허용하는 라이브러리를 설치하는 등으로 해결할 수 있다.

마찬가지로, cookie도 기본적으로 다른 도메인과 주고 받을 수 없지만,
withCredentials : true옵션을 주게되면, 다른 도메인과 쿠키를 주고 받는게 가능해진다!!

그러나, 필자는 withCredentials : true를 주었지만, 서로 다른 도메인에서 쿠키를 주고 받을 수 없었고, 약 3시간의 구글링을 통해 쿠키에 대해서 좀 더 알게 되었다.
출처 : https://beomy.github.io/tech/browser/cookie/

secure 속성이 정의된 쿠키는 HTTPS 요청에만 쿠키를 전송한다!
HttpOnly 속성이 정의된 쿠키는
SameSite 속성은 None, Lax, Static 중 하나를 설정할 수 있다.
None일 경우 모든 크로스 사이트 요청에 쿠키가 전송된다.
Lax 일 경우 대체로 서드 파티 쿠키를 전송하지 않지만, 몇가지 예외 적인 요청에서 전송한다.
Strict 일 경우 서드 파티 쿠키를 전송하지 않는다.

서드파티 쿠키란?

쿠키는 설정된 도메인을 기준으로 퍼스트 파티 쿠키와 서드 파티 쿠키로 나뉘어진다.
사용자가 naver.com에 접속했을 경우, naver.com에서 로그인 인증 쿠키를 발행하는 것은 퍼스트 파티 쿠키에 해당된다.

그런데, abc.com의 스크립트가 심어져 있는 naver.com에서 사용자가 A 상품을 클릭했다면, abc.com은 naver.com에서 사용자가 A 상품을 클릭했다는 정보를 abc.com으로 보낸다. 이렇게 방문한 웹사이트가 아닌 다른 웹사이트에서 발행한 쿠키를 서드파티 쿠키라고 한다.

서드파티쿠키를 사용한 CSRF 문제를 해결하기 위해 쿠키의 SameSite 속성이 추가되었고, 현재 SameSite 속성의 기본값은 Lax라고 한다.

그러나 3시간 끝에 알아낸 쿠키 전송의 오류는 단지 벡엔드 주소를 잘못 적었기 때문이었다.
벡엔드 주소가 .app으로 끝나는데, .io로 설정해 놓고 있었고, 다른 요청에는 올바르게 동작했는지는 아직도 의문이다...

코드 리팩토링

  1. 단일 책임 원칙 - 한가지 컴포넌트는 한가지 역할만 수행하도록
  2. input이 포함된 component는 따로 분리하자. (안하면, input에 데이터 변경될때마다 같은 컴포넌트에 있는 모든 요소가 리렌더링됨)
  3. styled 컴포넌트는 너무 많이 만들면 안좋다. & > 와 같은 자식 컴포넌트 css 선택자를 이용해서, 한가지 styled component안에 속해있는 다른 요소들은 styled 컴포넌트 안만들고 처리해도 된다.
  4. 코드 리팩토링하면 같은 기능을 하는 코드들이 가깝게 위치하게되어 읽기 편하다.

1월 회고 및 2월 계획

12.25일에 velog를 시작해서, 일주일에 글 하나씩 꼭 쓰자고 마음먹었는데, 1월동안 잘 지켜서 뿌듯하다. 2월에는 현재 듣고있는 프론트 강좌와, Nest.js 강좌를 마무리할 계획이다.
SWR과 비슷한 라이브러리로 ReactQuery가 있다는데, 둘을 비교해봐야겠다.
최근 여행유튜브를 많이 봤는데, 언어가 왜 중요한지 이제야 알게된 것 같다. 영어 공부도 틈틈이 해야겠다.
3월부터는 AI 공부도 시작해볼까 한다.
3월에 군입대를 해서, 5월에 자대에 왔고, 5월에 생활코딩으로 웹 공부를 했고, 6~8월은 3달간 알고리즘 공부를 했다. (종만북, 백준 platinum)
9월부터 군장병 공개SW 온라인 해커톤을 시작으로 본격적으로 개발공부를 했다.
9,10,11,12,1 약 5달간 개발공부를 진행했는데, 초반에 막막했던거에 비해서 차근차근 하나씩 공부하다보니 점점 막연했던게 약간은 두렵지 않아진 느낌이다.
지금 하는 개발공부가 정말로 나중에 도움이 될지는 미지수이지만, 최선을 다해보려한다.

0개의 댓글