앞서 첫번째 글에서는 Redux를 사용할 때 일반적으로 경험할 수 있는 몇 가지 문제들을 언급하였습니다. 이번 글에서는 SWR은 무엇이고 SWR이 어떻게 Redux를 대체할 수 있는지에 대해 설명해 보겠습니다.
SWR은 Nextjs로 유명한 vercel에서 만든 원격데이터 fetch를 위한 커스텀 훅 npm 모듈입니다. SWR은 원격서버의 상태를 가져와서 리액트 컴포넌트에 꽂아주는 기능을 제공합니다.
거두절미하고 코드부터 보겠습니다.
import useSWR from 'swr'
function Point(){
const {data, error} = useSWR('/api/points', url => {
return fetch(url).then(res => res.json())
})
if(error){
return <div>failed to load</div>
}
if(!data){
return <div>Loading..</div>
}
return <div>{data}</div>
}data 로 세팅되고 오류 발생시 해당 오류가 error 에 세팅됩니다.data 와 error 상태에 따라 알맞게 결과를 렌더링 해주면 됩니다.아주 직관적이고 사용하기도 편하죠😄
여기서 주목해야 할 부분은 useSWR 은 한번 fetch 한 원격상태의 데이터를 내부적으로 캐시하고 다른 컴포넌트에서 동일한 상태를 사용하고자 할 경우 이전에 캐시했던 상태를 그대로 리턴해 주기 때문에 서로 다른 컴포넌트가 동일한 상태를 공유할 수 있다는 점입니다. (여러 컴포넌트들에서 모두 동일하게 원격의 상태를 공유해야 하기 때문에 당연한 얘기겠지요)
서로 다른 컴포넌트가 동일한 상태를 공유한다? 이 부분은 기존의 상태관리 라이브러리들의 역할이었습니다. 그렇다면 우리는 SWR을 조금 응용해 기존 상태관리 라이브러리가 하던 역할을 완전히 대체할 수 있지 않을까 하는 기대를 걸어볼 수 있을 것 같습니다.
이제부터 그 기대가 어떻게 현실로 이루어지는 지 보여드리도록 하겠습니다.
프론트엔드 개발의 대부분의 작업은 아마도 원격 서버의 상태를 UI로 표현하는 일일 것입니다. 이 일을 하기 위해 우리는 먼저 로컬 스토어 상태를 정의하고 각 화면의 컴포넌트들이 상태에 따라 어떻게 반응할 지(react) 정의합니다.
그리고 적절한 시점에 원격 서버의 데이터를 fetch 하여 로컬 스토어 상태를 초기화하고 사용자 액션을 받아서 원격상태를 추가/수정/삭제합니다. 그리고 원격상태가 변경될 때마다 그에 맞게 로컬 스토어 상태도 동기화시키는 작업을 빼먹지 말아야 겠지요.
우리 대부분은 늘 이런 일들을 합니다. 이 중 특별히 귀찮은 작업 중 하나가 데이터의 추가/수정/삭제가 발생할 때마다 원격의 상태와 로컬의 상태를 동기화시키는 일이죠. 이 작업에 대한 현기증을 누구나 한번쯤은 느끼셨을 것입니다.
SWR이 해결하고자 하는 문제의 핵심은 바로 이 지점에 있습니다. SWR은 원격상태와 로컬상태를 하나로 통합합니다. SWR이 원격상태의 데이터 스트림을 리액트 컴포넌트에 그대로 연결한다고 생각하셔도 좋습니다. 위에 제시된 코드를 다시 한번 보세요. 정말로 SWR이 그렇게 동작하고 있는 것 같지 않습니까?
SWR은 해당 데이터를 마치 원격상태와 연결된 데이터 스트림으로서 바라볼 수 있도록 데이터 fetching 단계를 추상화합니다. 여기서 눈치빠른 개발자라면 SWR이 어떻게 HTTP 상에서 원격서버의 상태를 실시간 데이터 스트림으로 얻을 수 있단 말이냐 라고 의문을 가지실 수 있습니다. 네 당연한 질문입니다.
그 비밀은 SWR이 내부적으로 적절한 타이밍에 지속적으로 데이터를 폴링하기 때문입니다. 사실 실시간이라는 용어를 사용하기에 민망스러울 만큼 사실 실시간으로 데이터가 fetch 되는 것은 아닙니다. 하지만 충분히 용납할 수 있는 수준으로 데이터는 최신 데이터로 갱신됩니다. SWR은 브라우져 창이 focus를 얻을 때 또는 네트워크가 offline에서 online으로 바뀔 때 자동으로 데이터를 fetch 합니다. 그 밖에 polling 주기를 직접 설정하는 것도 가능합니다.

그럼 이제 SWR이 어떻게 기존 상태관리 라이브러리를 대체할 수 있는지 자세히 살펴보겠습니다. SWR이 기존 Redux를 대체할 수 있는 이유는 컴포넌트간 전역 상태를 공유할 수 있다는 특성 때문입니다.
많은 경우에 있어 로컬 스토어의 대부분 상태는 아마 원격 서버의 상태를 그대로 저장해 둔 것에 다르지 않을 것입니다.(Redux를 충분히 고도화해서 사용하고 있지 않다면😅)
우리가 상태관리 라이브러리를 사용할 때 절차는 보통 아래와 같을 것입니다.
SWR을 이용하면 위 3가지 과정을 하나로 통합할 수 있습니다. 앞서 제시된 SWR 예시 코드를 통해 이를 확인할 수 있습니다.
코드를 조금 발전시키면 원격서버와 연결된 각 데이터스트림을 상태에 따라 커스텀 훅으로 정의하여 마치 로컬의 상태를 다루듯 사용할 수 있습니다.
// usePoints.js
import useSWR from 'swr'
export default () => {
const {data, error} = useSWR('/api/points', url => {
return fetch(url).then(res => res.json())
})
return {data, error}
}// useUsers.js
import useSWR from 'swr'
export default () => {
const {data, error} = useSWR('/api/users', url => {
return fetch(url).then(res => res.json())
})
return {data, error}
}위와 같은 방식으로 SWR을 이용해 개별 상태들을 정의하면 여러 컴포넌트들에서 필요한 상태를 가져다 사용할 수 있습니다.
사용자가 사용자 정보를 수정할 경우에는 SWR의 내부 스케쥴링에 의한 데이터갱신을 기다리기 보다 수정 즉시 화면에 변경된 데이터가 보여져야 할 것입니다. 이럴 경우에는 mutate 함수를 이용할 수 있습니다. mutate 함수가 호출되면 해당 상태를 즉시 다시 fetch 하고 데이터를 갱신합니다.
import useSWR from 'swr'
function UserInfo(){
const {data, error, mutate} = useSWR('/api/users', url => {
return fetch(url).then(res => res.json())
})
const handleChange = async (user) => {
await updateUser(user)
mutate()
}
return <div>~생략~</div>
}예민한 개발자라면 서버의 상태를 갱신한 후 해당 사용자의 정보를 다시 fetch 하는 것은 효율적이지 못하다고 생각하실 수도 있습니다. 어짜피 로컬에서 해당 user의 데이터가 어떻게 갱신되었는 지를 알고 있기 때문이죠. 옳은 지적입니다. mutate 함수를 사용할 때 데이터 fetch 없이 로컬의 캐시되어있던 상태만 갱신하는 것도 가능합니다. 아래와 같이 말이죠.
const handleChange = async (user) => {
await updateUser(user)
mutate(user, false) // 첫번재 인자로 갱신할 데이터, 두번째 인자로 데이터 fetch 여부를 인자로 받습니다.
}
원격상태는 사용하지 않고 오직 로컬상태만 필요한 경우도 얼마든지 있습니다. 이런 경우라도 SWR을 사용할 수 있을까요? 네 가능합니다.
SWR은 기본적으로 원격서버의 상태를 fetch 하는 데에 적합한 도구입니다. 하지만 이를 조금 응용하면 로컬의 상태만을 필요로 하는 경우에도 충분히 SWR을 이용할 수 있습니다. fetch 함수가 접근하는 원격상태가 로컬 환경에 있다고 생각하면 되겠지요.🙂
로컬 환경의 원격상태는 필요에 따라 window, sessionStorage, localStorage, 클로져 변수 등을 적절하게 사용하실 수 있습니다.
앞선 글에서 처음 제시되었던 동기적 상황만을 고려하는 카운터를 Redux 없이 SWR만을 이용하면 아래와 같이 구현할 수 있습니다.
import useSWR from 'swr'
function useCounter(){
const {data, mutate} = useSWR('state', () => window.count)
return {data, mutate: (count) => {
window.count = count
return mutate()
}}
}
function Counter(){
const {data, mutate} = useCounter()
const handleInc = () => mutate(data + 1)
const handleDec = () => mutate(data - 1)
return (
<div>
<span>count: {data}</span>
<button onClick={handleInc}>inc</button>
<button onClick={handleDec}>dec</button>
</div>
)
}어떤가요? 리듀서를 정의하고 액션을 정의하고 상태변이를 위해 액션을 디스패치하는 번거로운 과정 없이 커스텀훅 하나로 이 모든 것이 간단하게 처리되었습니다!
물론 SWR이 Redux의 모든 기능을 대체할 수 있지는 않을 것입니다. 부족한 부분이 많이 있겠죠. Redux 와 SWR은 처음부터 해결하고자 했던 문제 자체가 달랐으니까요. 하지만 저의 짧은 경험에 비추어 볼 때 우리가 Redux를 사용하는 많은 경우에 있어서 번거로운 Redux 없이 SWR만으로도 보다 쉽게 원하는 결과를 얻을 수 있을 것이라 생각을 합니다.
이제 여러분도 SWR을 프로젝트에 적극적으로 도입해보는 것은 어떨까요? SWR을 적용하는데 어려운 부분에 언제든 피드백을 주신다면 저도 함께 관심을 가지고 고민해 보도록 하겠습니다. 😊
Keating님의 블로그에 게재된 글을 편집하여 매드업 테크블로그에서 다시 소개드립니다.
<매드업>은 개인정보보호법에 따라 이용자의 개인정보 보호 및 권익을 보호하고 개인정보와 관련한 이용자의 고충을 원활하게 처리할 수 있도록 다음과 같은 처리방침을 두고 있습니다.
<매드업>은 회사는 개인정보처리방침을 개정하는 경우 웹사이트 공지사항(또는 개별공지)을 통하여 공지할 것입니다.
○ 본 방침은 2020년 1월 15일부터 시행됩니다.
개인정보의 처리 목적 <매드업>은 개인정보를 다음의 목적을 위해 처리합니다. 처리한 개인정보는 다음의 목적 이외의 용도로는 사용되지 않으며 이용 목적이 변경될 시에는 사전동의를 구할 예정입니다.
가. 홈페이지 회원가입 및 관리 회원 가입의사 확인, 회원제 서비스 제공에 따른 본인 식별·인증, 회원자격 유지·관리, 제한적 본인확인제 시행에 따른 본인확인, 서비스 부정이용 방지, 만14세 미만 아동 개인정보 수집 시 법정대리인 동의 여부 확인, 각종 고지·통지, 고충처리, 분쟁 조정을 위한 기록 보존 등을 목적으로 개인정보를 처리합니다.
나. 민원사무 처리 민원인의 신원 확인, 민원사항 확인, 사실조사를 위한 연락·통지, 처리결과 통보 등을 목적으로 개인정보를 처리합니다.
다. 재화 또는 서비스 제공 물품배송, 서비스 제공, 청구서 발송, 콘텐츠 제공, 맞춤 서비스 제공, 본인인증, 연령인증, 요금결제·정산 등을 목적으로 개인정보를 처리합니다.
라. 마케팅 및 광고에의 활용 신규 서비스(제품) 개발 및 맞춤 서비스 제공, 이벤트 및 광고성 정보 제공 및 참여기회 제공 , 인구통계학적 특성에 따른 서비스 제공 및 광고 게재 , 서비스의 유효성 확인, 접속빈도 파악 또는 회원의 서비스 이용에 대한 통계 등을 목적으로 개인정보를 처리합니다.
개인정보 파일 현황
가. 개인정보 파일명 : 개인정보 처리방침
개인정보 항목 : 이메일, 휴대전화번호, 비밀번호, 이름, 업체명, 제목, 광고 예산, 내용, 사업자 등록 번호, 신용카드정보, 은행계좌정보, 예금주, 생년월일, 서비스 이용 기록, 접속 로그, 쿠키, 접속 IP 정보, 결제기록
수집방법 : 홈페이지, 계약서
보유근거 : 홈페이지 회원정보 수집 등에 관한 기록
보유기간 : 5년
관련법령 : 대금결제 및 재화 등의 공급에 관한 기록 : 5년
개인정보의 처리 및 보유 기간 ① <매드업>은 법령에 따른 개인정보 보유·이용기간 또는 정보주체로부터 개인정보를 수집 시에 동의 받은 개인정보 보유,이용기간 내에서 개인정보를 처리,보유합니다. ② 각각의 개인정보 처리 및 보유 기간은 다음과 같습니다.
가. <홈페이지 회원가입 및 관리> <홈페이지 회원가입 및 관리>와 관련한 개인정보는 수집.이용에 관한 동의일로부터<5년>까지 위 이용목적을 위하여 보유.이용됩니다.
보유근거 : 홈페이지 회원정보 수집 등에 관한 기록
관련법령 : 대금결제 및 재화 등의 공급에 관한 기록 : 5년
예외사유 :
나. <재화 또는 서비스 제공> <재화 또는 서비스 제공>와 관련한 개인정보는 수집.이용에 관한 동의일로부터<5년>까지 위 이용목적을 위하여 보유.이용됩니다.
보유근거 : 계약 또는 청약철회 등에 관한 기록
관련법령 : 계약 또는 청약철회 등에 관한 기록 : 5년
예외사유 :
다. <마케팅 및 광고에의 활용> <마케팅 및 광고에의 활용>와 관련한 개인정보는 수집.이용에 관한 동의일로부터<3년>까지 위 이용목적을 위하여 보유.이용됩니다.
보유근거 : 전자상거래등에서의 소비자보호에 관한 법률
관련법령 : 소비자의 불만 또는 분쟁처리에 관한 기록 : 3년
예외사유 :
정보 주체와 법정대리인의 권리·의무 및 그 행사방법 이용자는 개인정보주체로써 다음과 같은 권리를 행사할 수 있습니다.
① 정보주체는 매드업에 대해 언제든지 개인정보 열람,정정,삭제,처리 정지 요구 등의 권리를 행사할 수 있습니다.
② 제1항에 따른 권리 행사는 매드업에 대해 개인정보 보호법 시행령 제41조제1항에 따라 서면, 전자우편, 모사전송(FAX) 등을 통하여 하실 수 있으며 매드업은 이에 대해 지체 없이 조치하겠습니다.
③ 제1항에 따른 권리 행사는 정보주체의 법정대리인이나 위임을 받은 자 등 대리인을 통하여 하실 수 있습니다. 이 경우 개인정보 보호법 시행규칙 별지 제11호 서식에 따른 위임장을 제출하셔야 합니다.
④ 개인정보 열람 및 처리 정지 요구는 개인정보보호법 제35조 제5항, 제37조 제2항에 의하여 정보주체의 권리가 제한 될 수 있습니다.
⑤ 개인정보의 정정 및 삭제 요구는 다른 법령에서 그 개인정보가 수집 대상으로 명시되어 있는 경우에는 그 삭제를 요구할 수 없습니다.
⑥ 매드업은 정보주체 권리에 따른 열람의 요구, 정정·삭제의 요구, 처리 정지의 요구 시 열람 등 요구를 한 자가 본인이거나 정당한 대리인인지를 확인합니다.
처리하는 개인정보의 항목 작성
① <매드업>은 다음의 개인정보 항목을 처리하고 있습니다.
가. <홈페이지 문의 및 관리>
필수항목 : 업체명, 이메일, 제목, 연락처, 광고 예산, 내용, 휴대전화번호, 비밀번호, 이름, 서비스 이용 기록, 접속 로그, 쿠키, 접속 IP 정보
선택항목 : 프로필 사진
나. <재화 또는 서비스 제공>
필수항목 : 업체명, 이메일, 제목, 연락처, 광고 예산, 내용, 비밀번호, 이름, 회사명, 사업자 등록 번호, 신용카드정보, 은행계좌정보, 예금주, 생년월일, 서비스 이용 기록, 접속 로그, 쿠키, 접속 IP 정보, 결제기록
선택항목 :
다. <마케팅 및 광고에의 활용>
필수항목 : 업체명, 이메일, 제목, 연락처, 광고 예산, 내용, 휴대전화번호, 이름, 서비스 이용 기록, 접속 로그, 쿠키, 접속 IP 정보
선택항목 :
개인정보의 파기<매드업>은 원칙적으로 개인정보 처리 목적이 달성된 경우에는 지체없이 해당 개인정보를 파기합니다. 파기의 절차, 기한 및 방법은 다음과 같습니다.
파기절차
이용자가 입력한 정보는 목적 달성 후 별도의 DB에 옮겨져(종이의 경우 별도의 서류) 내부 방침 및 기타 관련 법령에 따라 일정기간 저장된 후 혹은 즉시 파기됩니다. 이 때, DB로 옮겨진 개인정보는 법률에 의한 경우가 아니고서는 다른 목적으로 이용되지 않습니다.
파기기한
이용자의 개인정보는 개인정보의 보유기간이 경과된 경우에는 보유기간의 종료일로부터 5일 이내에, 개인정보의 처리 목적 달성, 해당 서비스의 폐지, 사업의 종료 등 그 개인정보가 불필요하게 되었을 때에는 개인정보의 처리가 불필요한 것으로 인정되는 날로부터 5일 이내에 그 개인정보를 파기합니다.
파기방법
전자적 파일 형태의 정보는 기록을 재생할 수 없는 기술적 방법을 사용합니다. 종이에 출력된 개인정보는 분쇄기로 분쇄하거나 소각을 통하여 파기합니다.
개인정보 자동 수집 장치의 설치•운영 및 거부에 관한 사항
① 매드업은 개별적인 맞춤서비스를 제공하기 위해 이용 정보를 저장하고 수시로 불러오는 ‘쿠키(cookie)’를 사용합니다.
② 쿠키는 웹사이트를 운영하는데 이용되는 서버(http)가 이용자의 컴퓨터 브라우저에게 보내는 소량의 정보이며 이용자들의 PC 컴퓨터 내의 하드디스크에 저장되기도 합니다.
가. 쿠키의 사용 목적 : 이용자가 방문한 각 서비스와 웹 사이트들에 대한 방문 및 이용형태, 인기 검색어, 보안접속 여부, 등을 파악하여 이용자에게 최적화된 정보 제공을 위해 사용됩니다.
나. 쿠키의 설치•운영 및 거부 : 웹브라우저 상단의 도구>인터넷 옵션>개인정보 메뉴의 옵션 설정을 통해 쿠키 저장을 거부 할 수 있습니다.
다. 쿠키 저장을 거부할 경우 맞춤형 서비스 이용에 어려움이 발생할 수 있습니다.
개인정보 보호책임자 작성
① 매드업은 개인정보 처리에 관한 업무를 총괄해서 책임지고, 개인정보 처리와 관련한 정보주체의 불만처리 및 피해구제 등을 위하여 아래와 같이 개인정보 보호책임자를 지정하고 있습니다.
▶ 개인정보 보호책임자
성명 : 강남석
직책 : CFO
직급 : CFO
연락처 : 02-1522-1060, contact@madup.com
※ 개인정보 보호 담당부서로 연결됩니다.
▶ 개인정보 보호 담당부서
부서명 : General Affairs Team
담당자 : 민상준
연락처 : 02-1522-1060, contact@madup.com
② 정보 주체께서는 매드업의 서비스(또는 사업)을 이용하시면서 발생한 모든 개인정보 보호 관련 문의, 불만처리, 피해구제 등에 관한 사항을 개인정보 보호책임자 및 담당부서로 문의하실 수 있습니다. 매드업은 정보주체의 문의에 대해 지체 없이 답변 및 처리해드릴 것입니다.
개인정보 처리방침 변경
① 이 개인정보처리방침은 시행일로부터 적용되며, 법령 및 방침에 따른 변경 내용의 추가, 삭제 및 정정이 있는 경우에는 변경사항의 시행 7일 전부터 공지사항을 통하여 고지할 것입니다.
개인정보의 안전성 확보 조치 <매드업>은 개인정보보호법 제29조에 따라 다음과 같이 안전성 확보에 필요한 기술적/관리적 및 물리적 조치를 하고 있습니다.
가. 정기적인 자체 감사 실시 개인정보 취급 관련 안정성 확보를 위해 정기적(분기 1회)으로 자체 감사를 실시하고 있습니다.
나. 개인정보 취급 직원의 최소화 및 교육 개인정보를 취급하는 직원을 지정하고 담당자에 한정시켜 최소화 하여 개인정보를 관리하는 대책을 시행하고 있습니다.
다. 내부관리계획의 수립 및 시행 개인정보의 안전한 처리를 위하여 내부관리계획을 수립하고 시행하고 있습니다.
라. 해킹 등에 대비한 기술적 대책 <매드업>은 해킹이나 컴퓨터 바이러스 등에 의한 개인정보 유출 및 훼손을 막기 위하여 보안프로그램을 설치하고 주기적인 갱신·점검을 하며 외부로부터 접근이 통제된 구역에 시스템을 설치하고 기술적/물리적으로 감시 및 차단하고 있습니다.
마. 개인정보의 암호화 이용자의 개인정보는 비밀번호는 암호화 되어 저장 및 관리되고 있어, 본인만이 알 수 있으며 중요한 데이터는 파일 및 전송 데이터를 암호화 하거나 파일 잠금 기능을 사용하는 등의 별도 보안기능을 사용하고 있습니다.
바. 접속 기록의 보관 및 위변조 방지 개인정보처리시스템에 접속한 기록을 최소 6개월 이상 보관, 관리하고 있으며, 접속 기록이 위변조 및 도난, 분실되지 않도록 보안기능 사용하고 있습니다.
사. 개인정보에 대한 접근 제한 개인정보를 처리하는 데이터베이스시스템에 대한 접근 권한의 부여,변경,말소를 통하여 개인정보에 대한 접근통제를 위하여 필요한 조치를 하고 있으며 침입차단시스템을 이용하여 외부로부터의 무단 접근을 통제하고 있습니다.
아. 문서 보안을 위한 잠금 장치 사용 개인정보가 포함된 서류, 보조저장매체 등을 잠금 장치가 있는 안전한 장소에 보관하고 있습니다.
자. 비인가자에 대한 출입 통제 개인정보를 보관하고 있는 물리적 보관 장소를 별도로 두고 이에 대해 출입통제 절차를 수립, 운영하고 있습니다.