1. Access Token과 Refresh Token (react-cookie, token 갱신) - Oauth2.0과 일반 공통 - O

    react-cookie와 axios

    https://velog.io/@0307kwon/JWT는-어디에-저장해야할까-localStorage-vs-cookie

    안녕하세요, 프론트 엔드 박정우입니다.

    저는 총 5가지의 기술들에 대해서 말씀드리고자 합니다.

    먼저 첫 번째로 서버와 주고 받는 Jason Web Token을 관리함에 있어 React-Cookies와 Axios 메서드를 활용했습니다.

    사용자가 일반 로그인 혹은 SNS 로그인을 하면, 서버로부터 전달받은 Refresh Token은 cookie에 저장합니다.

    // Access Token은 따로 저장하지 않은 채 Axios의 Default Header 설정을 통해 모든 Ajax 요청 헤더에 토큰이 담기도록 했습니다.

    // 페이지가 리로드되거나 사용자가 앱을 종료한 후 다시 시작할 때, Access Token이 사라지는 문제는 처음 앱이 실행되었을 때, cookie에 저장된 Refresh Token의 존재 여부를 확인한 후 존재한다면 Access Token을 서버에 요청하여 받아오도록 구현했습니다.

    또한 Backend와 함께 각 토큰들의 만료기간을 맞춤으로써 Access Token과 Refresh Token이 만료되기 전에 서버에 요청하여 재발급을 받도록 구현하여 사용자가 로그아웃을 하지 않는 이상 로그인이 유지되도록 하였습니다.

    JWT를 Local Storage가 아닌 Cookie에 저장한 이유는 XSS 공격에 비교적 안전하며, 백엔드와의 소통을 통해 refresh token을 httpOnly 쿠키로 설정하고, url이 새로고침 될 때마다 refresh token을 request에 담아 새로운 Access Token을 발급 받음으로써 CSRF 공격 또한 막을 수 있기 때문입니다.

  2. Infinite Scroll (Observer API vs. Scroll Event) - O

    http://blog.hyeyoonjung.com/2019/01/09/intersectionobserver-tutorial/

    다음으로 두 번째는 Observer API 사용하여 무한 스크롤을 구현했습니다.

    가장 처음 무한 스크롤 구현할 때는 순수한 Scroll Event를 적용했었습니다. 왼쪽 영상에서 확인하실 수 있듯이 페이지 하단에 도달했을 때, 비동기 요청으로 데이터가 로드되기 전에 scroll Event Handler가 여러번 실행되는 문제가 발생했습니다.

    이를 방지하기 위해 Throttle을 적용했으나, 이 또한 Throttle 자체의 특성으로 데이터 요청 및 로드에 지연이 발생하는 문제가 있었습니다.

    이후 Intersection Observer API를 활용하여 무한스크롤을 다시 구현함으로써 이전에 발생했던 문제들을 해결할 수 있었습니다.

    Observer API를 사용한 이유는 Throttle이나 debounce 등을 활용한 최적화 작업이 필요하지 않고, viewport와 target 요소 간 교차 여부를 비동기적으로 감시함으로써 메인 스레드에 부하를 주지 않기 때문입니다. 또한 reflow와 repain 현상을 방지하여 앱 성능 최적화를 도모할 수 있다는 점에서 Observer API를 사용했씁니다.

  3. Next-Image - 이미지 최적화, 비율 맞추기(aspect ratio와 object-fit) 등

    외부 이미지를 가져오는 경우 next.config.js 파일에 CDN의 host를 명시해줘야 하는 불편함도 존재 (해당 서버가 안전한 서버임을 Next.js에 알려줘야 함)

    세 번째는 Next-Image입니다. 기존의 img 태그를 사용했을 때, 이미지의 해상도가 높은 경우 페이지가 로드되는데 시간이 꽤 걸려 UX를 저해하는 현상이 발생했었습니다.

    이를 Nextjs에서 제공해주는 next-image API를 통해 해결할 수 있었습니다. 화면에서도 보실 수 있듯이, 적용 전후로 페이지 로드 시간에 약 4배의 차이가 있음을 알 수 있습니다.

    Next-Image를 통해 이렇게 빠른 이미지 로드가 가능한 이유는 디바이스 크기 별로 srcSet을 미리 지정해두고, 사용자의 디바이스에 맞는 이미지를 다운로드할 수 있으며, 이미지를 webp와 같은 용량이 작은 포맷으로 이미지를 변환해서 제공하기 때문입니다.

    사진에서 보실 수 있듯이, 원본 이미지 크기의 약 40분의 1로 크기가 줄어들었음을 알 수 있습니다.

    이외에도 Next-image API는 default로 lazy-loading을 지원해주며, placeholder를 통해 이미지가 로드되기 전에도 이미지 높이만큼 영역을 표시해서 이미지가 로드된 후에 레이아웃이 흔들리지 않도록 하여 Cumulative Layout Shift 현상을 방지할 수 있다는 점에서 큰 이점이 있습니다.

  4. SSR → router.isReady와 optional Chaining 활용

    다음 네 번째는 UseRouter와 Optional Chaining입니다. Server Side Rendering이 이루어지는 Next.js 특성상 서버로부터 비동기적으로 데이터를 받아오기 전에 pre-rendering이 발생하여 첫 번째 사진과 같은 문제가 발생했었고, 이를 Optional Chaining을 활용하여 해결했습니다.

    이후 사용자가 게시물을 Post하면, 해당 게시물의 상세 페이지로 이동시키는 과정에서 두 번째 사진과 같이 정상적으로 이동되지 않는 문제가 발생했었습니다. useRouter Hook을 통해 이동된 uri 내 게시물의 id를 가져와서 서버에 요청을 보낸 후 데이터를 받아와 화면을 구성하는 방식이었는데요. useRouter Hook의 LifeCycle을 고려하지 않아서 발생했던 문제임을 파악하고, useRouter Hook의 isReady 메소와 useEffect Hook을 통해 해당 문제를 해결하였습니다.

  5. Atomic Design Pattern과 next-layout

    마지막으로는 Atomic Design Pattern과 next-layout입니다. 이전 프로젝트에서는 CRA React App을 만들고 Atomic Design Pattern을 도입했었습니다. CRA에서는 nested routing을 통해 공통적으로 적용되는 컴포넌트나 위치는 동일하지만 page에 따라서 조금씩 달라지는 컴포넌트들을 손쉽게 처리할 수 있었고, Atomic Design Pattern을 적용하여 Component 구조를 구성하였기에 효율적으로 구현할 수 있었습니다.

    반면, 이번 프로젝트에서는 Atomic Design Pattern을 도입하지 않았고, 프로젝트 초중반까지 next-layout을 알지 못하였기에 Header, Top Navigation, Bottom Navigation을 구현할 때, 각 컴포넌트에서 url을 기준으로 다르게 보이도록 했었습니다.

    이후에 layout을 달리 가져가는 페이지들을 추가할 때, 비효율적으로 작업하고 있어 next-layout API를 도입하려 했으나, Atomic Design Pattern을 적용하지 않았다 보니, 제한된 시간 내에 refactoring 하는 작업을 수행하지 못한 아쉬움이 있습니다.

    프로젝트 기간이 종료된 이후에 Refactoring 작업을 진행할 예정입니다.

이상 발표 마치겠습니다. 다음 동규님 나와주세요!

개인기술영상.key