분류 전체보기 141

상세 페이지에서 뒤로가기 클릭시 원래 있던 목록 페이지로 돌아가기

목록 페이지에 페이지네이션이 있을 때, 상세 페이지를 갔다가 뒤로 가기를 눌렀을 때 항상 1페이지로 돌아가는 것이 아니라 원래 있던 페이지를 기억해서 해당 페이지로 돌아오게 하는 기능을 구현하는 방법을 간략하게 정리해 본다. 위의 기능을 구현하기 위해서는 현재 페이지를 어딘가에 저장해두어야 하는데 이때 다음의 세 가지 방식을 생각해 볼 수 있을 것 같다. 1. 브라우저의 sessionStorage 세션 스토리지는 탭이 열려 있는 동안 정보를 저장할 수 있어, 사용자가 페이지를 떠났다가 돌아왔을 때 이전 페이지 정보를 유지하는 데 유용하다. 2. URL의 쿼리 스트링 URL로부터 페이지 정보를 읽어들일 수 있다. URL을 가지고 있으면 탭을 닫았다가 다시 열 때도 이전 페이지로 돌아갈 수 있고, 해당 UR..

JSX에서 null, false, React.Fragment의 차이

1. React.Fragment () 추가 HTML 요소 없이 여러 요소를 함께 렌더링하려는 경우에 사용한다. react legacy docs의 JSX in Depth 파트를 보면 JSX는 React.createElement의 syntatic sugar임을 상기해 보면 왜 그루핑이 필요한 지 알 수 있다. import React from 'react'; import CustomButton from './CustomButton'; function WarningButton() { // return React.createElement(CustomButton, {color: 'red'}, null); return ; } // node_modules/@types/react/index.d.ts function cr..

무한 스크롤과의 사투 (2) - tanstack/react-virtual 적용

앞의 포스팅에서 언급했던 것처럼 무한스크롤 관련 이슈를 해결하고 나니 성능 이슈가 부각되었다. 원문과 번역문을 두고 사용자가 편집할 수 있는 서비스인데 아이템이 500개 정도만 되어도 키보드 입력 이벤트가 발생하면 사용자가 키보드를 누르고 화면에 해당 글자가 뜨는 시간 사이에 지연이 체감되는 정도였다. list virtualization 또는 windowing 기법을 적용해서 뷰포트에 보이는 아이템만 DOM 노드에 가지게 하면 성능 면에서 큰 개선을 할 수 있을 거라고 생각했다. react-virtualized가 대표적인 windowing 라이브러리로 지금은 legacy가 된 이전 버전 react 공식 문서의 성능 최적화 파트에서도 추천하는 라이브러리를 먼저 고려했다. 프로젝트의 요구 사항 중 목록 가상..

무한 스크롤과의 사투 (1)

딱 1년 전 이맘때 부트캠프 파이널 프로젝트를 마친 후 회고를 작성하면서 아쉬운 점으로 "무한스크롤도 써보고 싶었던 ui인데 시간 문제로 도입하지 못했다"고 꼽은 적이 있다. 그리고 그로부터 약 10개월 뒤 무한 스크롤을 원없이 사용하게 된다 😇 지난달 다른 팀원이 맡았던 파트를 인계 받았는데 그 중 메인 feature가 무한 스크롤이었다. 인계 받을 당시 프로젝트의 가장 큰 이슈는 다음과 같았다. 1. 스크롤을 위아래로 이동함에 따라 nextPage와 previousPage를 fetch하고, 서버에서 응답으로 받은 데이터를 UI를 그리는데 사용하는 배열 안에 순서에 맞게 삽입하는 게 때때로 올바르게 동작하지 않고 있었다. 가령 [... 6, 7, 8, ...] 페이지가 렌더링되어야 하는데, [... 7..

tanstack react-virtual을 사용하여 무한 스크롤 성능 최적화 하기

windowing 기법 또는 list virtualization 기법을 적용해 무한 스크롤 성능을 최적화하기 위해 tanstack의 react-virtual 라이브러리를 사용하면서 위의 YouTube 클립의 도움을 많이 받았다. 위의 클립에서 faker-js로 랜덤한 길이를 가진 문장을 리스트로 만들어 virtualization을 통해 최적화하는 구현 방법을 코드로 옮겨 보았다. 랜덤한 길이를 가졌기 때문에 리스트 아이템의 height가 일정하지 않다. 즉 dynamic height를 가진 아이템을 구현하는 방법이다. 먼저 virtual을 적용하기 전 코드이다. 10만 개가 아니라 100만 개의 아이템을 렌더링한다면? 어지간히 성능 좋은 컴퓨터에서도 100만개를 렌더링하는 순간 브라우저가 죽어버릴 것이다..

React Query를 활용한 테이블에서 데이터 정렬하기

기존 테이블 컴포넌트에 정렬 API를 연동하는 작업을 하던 중 다음과 같은 상황을 만났다. (간단한 예제 코드를 만들어 보고자 했으나 공개 API로 같은 현상을 재현하기가 어려웠다 😢) 먼저 테이블은 antd의 table 컴포넌트로 테이블의 각 열마다 정렬 버튼이 있다. 정렬 버튼을 처음 누르면 오름차순 정렬(ascend), 두번째로 누르면 내림차순 정렬(descend), 세번째로 누르면 정렬 취소 순서로 동작하고, 사용자가 특정 열의 정렬 버튼을 누를 때마다 해당 열이 ascend인지 descend인지 알 수 있다. 이 정보를 바탕으로 서버에 쿼리 스트링으로 정렬된 데이터를 요청한다. 그러자 특정 열의 정렬 버튼을 누를 때마다 화면이 full refresh가 되었다...! 😰 정렬 버튼을 한번씩 눌러서..

React ErrorBoundary와 react-query를 사용하여 예외 처리 설계하기

예외 처리를 설계하며 고민한 내용들을 간략하게 정리해 보았다. 프로젝트마다 필요한 예외 처리가 다르고, 구현 방식도 여러 가지가 있기에 개발의 많은 부분이 그렇듯 정답이 없는 것 같다. 나중에 시간이 흐른 뒤 이 포스팅을 보면 분명 '왜 이렇게 했지?'라고 생각할 것 같다. 😇 하지만 같은 고민을 하는 누군가에게 조금이나마 도움이 될 수 있기를 바란다. 1. 에러의 분류 먼저 위의 도식을 참고하여 프로젝트 상황에 맞게 에러를 분류해 보았다. 사용자에게 에러가 발생한 이유를 설명하고 사용자의 액션을 유도하는 장치를 제공하는 것을 예외 처리의 핵심으로 두었다. 1. 예상 가능한 에러 a. 사용자 입력 값에 대한 에러 - 에러 발생 상황 예시: 로그인 시 틀린 비밀번호를 입력하거나 회원가입시 중복 아이디 또는..

React의 key에 index를 사용하면 안 되는 이유 (feat. 무한스크롤)

지난주부터 회사 프로젝트에서 다른 팀원 분이 담당하던 feature를 내가 맡게 되었다. 인계를 받으면서 이슈 중 하나가 무한스크롤 기능 중 스크롤을 위로 올려 이전 페이지 아이템을 가져올 때 사용자가 현재 보고 있는 아이템이 화면에 유지되지 않고 새로 받아온 페이지의 첫번째 아이템으로 화면이 튀는 현상이었다. 반면 스크롤을 아래쪽으로 내려서 다음 페이지 아이템을 가져올 때는 현재 보고 있는 아이템이 화면에 그대로 유지되었다. 인계를 해준 팀원 분은 이 이슈를 스크롤 앵커링 문제라고 생각했고, 바닐라 JS를 사용하거나 React에서도 useRef를 사용해서 직접 DOM 조작을 할 때는 위쪽이나 아래쪽 어디로 스크롤을 하든지 스크롤 앵커링이 정상적으로 동작하며, React의 상태를 UI에 렌더링하는 경우 ..

[Git] 커밋은 diff가 아니라 스냅샷이다

들어가기에 앞서 Derrick Stolee가 작성한 Commits are snapshots, not diffs를 번역한 포스팅임을 밝힙니다. 번역에 오류가 있는 경우 댓글로 알려주시면 감사하겠습니다. Git은 뭐가 뭔지 헷갈리기로 악명이 높다. 사용자들은 기대와 어긋나는 용어와 문구를 접하며 곤혹스러워 한다. 이러한 현상은 git cherry-pick이나 git rebase와 같이 '히스토리를 다시 작성'하는 명령에서 가장 두드러진다. 내 경험상 이러한 혼란의 근본 원인은 커밋을 여기저기에서 무엇이 어떻게 바뀌었는지 보여주는 diff로 해석하기 때문이다. 하지만 커밋은 diff가 아니라 스냅샷이다! Git을 감싸고 있는 베일을 걷어내고 레포지토리에 데이터를 저장하는 방식을 살펴보면 Git을 이해할 수 있..

번역 2024.02.18

디자인 패턴 - Command 패턴

들어가기에 앞서 https://www.patterns.dev/vanilla/command-pattern 에 있는 설명과 예시를 바탕으로 하여 관련 주제에 대해 이제까지 공부한 내용을 개인적으로 정리한 포스팅임을 밝힙니다. 명령을 처리하는 객체를 통해 메소드와 실행되는 동작의 결합도를 낮출 수 있다. 특정 작업을 실행하는 객체과 메소드를 호출하는 객체를 분리할 수 있다. 주요 세 가지 클래스: invoker, receiver, command invoker : command를 생성하고 실행하는 역할 나중에 실행할 수 있도록 명령을 대기열(queue)에 넣거나 이미 실행된 명령을 실행 취소하는 데 사용할 수도 있다. receiver : 특정 command를 수신하고 처리 애플리케이션의 다른 클래스나 메소드에 ..