배경vscode의 탭 바처럼 동작하는 대시보드를 만들고 있었어요. 파일을 열면 탭이 생기고, 클릭하면 전환되고, X로 닫는 그 구조요.대시보드에는 몇 가지 종류의 화면이 있어요.home — 홈 화면. 항상 존재하는 기본 탭이에요.new — 새 인사이트를 작성하는 페이지. 사이드바에서 "새 페이지 만들기"를 누르면 탭이 열립니다.insight-{id} — 작성이 완료된 개별 인사이트 페이지. new에서 글을 저장하면 insight-42 같은 탭으로 교체돼요.tag-{id} — 특정 태그에 속한 인사이트 목록.trash — 휴지통.이 탭 상태를 어디에 저장할까요? React state에 넣으면 간단하지만, 새로고침하면 날아가요. 그래서 공유 기능도 넣을 겸 URL query string을 single sou..
들어가며지난 글에서 Branded Type으로 유틸 함수 사용을 강제하는 방법을 다뤘어요. getSnvAnchorId, getCnvAnchorId 같은 개별 함수들을 만들어서 각 variant type마다 올바른 ID 포맷을 보장했죠. 근데 variant type이 6개가 넘어가면서 문제가 생기기 시작했어요. 함수가 너무 많아져서 어떤 게 있는지 찾기가 힘들었어요. 자동완성으로 get만 쳐도 수십 개가 뜨는데 그 중에서 anchor id 관련 함수만 골라내는 것도 일이었죠. 마침 예전에 사용하던 쿼리 키 팩토리 패턴이 떠올랐어요. 쿼리 키처럼 일괄 invalidate할 일은 없지만, 관련 함수를 하나의 객체로 묶는다는 아이디어 자체가 딱 맞았어요.// 기존 - 흩어진 개별 함수들export const g..
들어가며유전체 분석 플랫폼인 GEBRA에서 variant(변이)를 클릭하면 해당 row로 스크롤되는 기능이 있어요. URL 해시 기반 네비게이션인데, 사용자 입장에서의 흐름은 이래요.테이블 row에 id="SNV----" 속성이 부여되어 있다 (strategy)variant card에서 코멘트 버튼을 누르면, "SNV----"이 포함된 메시지가 채팅에 추가된다 (message)나중에 채팅에서 해당 메시지의 "Go to Variant" 링크를 클릭하면, #SNV---- 해시로 이동하여 row로 스크롤된다 (navigation)row에 부여한 ID를 메시지에 심어두고, 나중에 그걸로 찾아가는 구조죠. 세 단계가 정확히 같은 문자열을 만들어야 이 흐름이 동작하며 한 글자라도 다르면 row를 찾지 못해요.하드코..
들어가며이전 글에서 로직 계층을 내려서 단일 관심사를 챙기는 이야기를 했었죠. 여러 주제가 섞인 컴포넌트를 분리하고, 각 컴포넌트가 자기 로직만 가지도록 만드는 거였어요. 이번엔 반대 방향입니다. 분기를 상위로 올려서 단일 관심사를 챙기는 이야기를 해볼게요.컴포넌트는 "단일 책임이 아니라 단일 관심사"로 생각해야 합니다.여기서 단일 관심사란 곧 높은 응집도를 의미합니다. 하나의 컴포넌트가 하나의 맥락에 필요한 것들만 모아서 가지고 있는 상태죠. 근데 컴포넌트 안에서 강하게 결합된 로직들을 어떻게 분리할 수 있을까요?트리 구조의 컴포넌트상위에서 복잡도를 해소하기React 컴포넌트는 단방향 트리 구조입니다. 데이터와 제어 흐름이 위에서 아래로만 흐르죠. // 여기서 결정하면 /..
들어가며이전 글에서 훅 추상화를 아토믹하게 쪼개는 이야기를 했었는데요. 몬스터 훅 대신 URL 필터 동기화, 날짜 범위 처리 같은 로직을 각각 분리해서 조합해 쓰는 방식이었죠. 덕분에 각 로직이 한 가지 일만 해서 파악하기 쉬웠어요.근데 막상 컴포넌트를 보면 좀 달랐어요. 예를 들어 상품 상세 페이지를 만든다고 해볼게요function ProductPage({ productId }) { // 상품 정보 관련 const { data: product } = useProductQuery(productId); // 장바구니 관련 const [quantity, setQuantity] = useState(1); const { mutate: addToCart } = useAddToCart(); // 리뷰 관..