목록분류 전체보기 (115)
기술 블로그
반응형 UI를 만들다 보면 데스크톱에서는 멀쩡한데 실기기에서만 어긋나는 문제를 마주하곤 합니다. 최근 메인 페이지의 sticky 탭바가 실기기 아이폰에서만 스크롤을 한 박자 늦게 따라오는 현상을 겪었습니다. 크롬 개발자도구의 모바일 에뮬레이터에서도 재현되지 않아 원인을 좁히기가 쉽지 않았고, 가설을 두 번 세워 두 번 다 기각당한 끝에 전혀 다른 파일의 공통 mixin 한 줄에서 범인을 찾았습니다.이번 포스트에서는 그 과정을 브라우저 렌더링 엔진의 관점에서 정리합니다.1. 현상 및 재현 조건메인 페이지의 '성공 레퍼런스' 섹션에는 모바일에서 상단에 고정되는 탭바가 있습니다. 사이트 헤더(높이 60px) 바로 아래에 붙고, 스크롤로 헤더가 숨으면 그 빈자리로 올라오는 구조입니다..tabBarSticky {..
[React] 대규모 데이터 편집 시 발생하는 입력 지연 분석: Fiber 트리 순회와 브라우저 ReflowReact 환경에서 수천 건의 데이터를 동시에 다루는 편집 UI를 구현하다 보면, 단순한 최적화만으로는 해결되지 않는 성능 병목을 마주하곤 합니다. 최근 약 3,800건의 매출 데이터를 수정하는 모달에서 입력 시 약 5초간의 지연(Input Lag)이 발생했던 사례를 통해, React 내부 동작과 브라우저 렌더링 엔진의 관점에서 그 원인을 분석하고 해결 과정을 기록합니다.1. 현상 및 초기 진단규모: 약 3,822행 (행당 Input 및 SelectBox 등 10여 개의 컴포넌트 포함)문제: 특정 입력 필드에 타이핑 시 화면이 수 초간 프리징됨.초기 가설: 검색 필터링 로직의 부하 혹은 부모 컴포넌..
정산 시스템의 데이터 수정 모달에서 최대 5,000건의 행을 테이블로 보여줘야 하는 기능을 구현했습니다. 개별 체크박스 토글은 문제가 없었으나, 헤더의 '전체 선택'을 클릭할 때마다 화면이 수 초간 멈추는 성능 저하가 발생했습니다.지난 포스트에서 다룬 Set 기반 최적화와 React.memo만으로는 해결되지 않았던 이 문제를 가상화(Virtualization)를 통해 해결한 과정을 정리합니다.1. 병목 지점 분석: React.memo가 무력화되는 순간개별 체크박스 토글은 React.memo로 최적화가 가능합니다. 5,000개 행 중 상태가 변하는 행은 단 1개뿐이기 때문입니다. 하지만 '전체 선택'은 이야기가 다릅니다.전체 선택 시 발생하는 일selectedSeqs 상태가 0개에서 5,000개로 변경됩니..
[react] 대량의 체크박스(5,000건) 렌더링 최적화하기프로젝트를 진행하다 보면 대량의 데이터를 다뤄야 하는 상황을 마주하곤 합니다. 최근 정산 데이터 수정 모달에서 약 5,000건의 데이터를 테이블로 렌더링하고, 각 행을 체크박스로 선택하는 기능을 구현했습니다.하지만 구현 후 테스트 과정에서 체크박스를 클릭할 때마다 1~2초 수준의 심각한 지연이 발생했습니다. 이번 포스트에서는 배열 기반 상태 관리의 함정과 이를 Set 및 React.memo로 해결한 과정을 정리합니다.1. 문제 상황과 원인 분석처음에는 일반적인 방식인 배열(Array)을 사용하여 선택된 아이템의 상태를 관리했습니다.const [selectedItems, setSelectedItems] = useState([]);// 개별 선택 ..
프로젝트 규모가 커짐에 따라 서버 부하를 줄이기 위해 전체 데이터를 클라이언트에서 관리하며 필터링해야 하는 경우가 많아집니다. 특히 한국어 서비스에서는 사용자의 검색 경험을 결정짓는 '초성 검색'과 UI의 안정성을 보장하는 '포탈(Portal) 렌더링'이 필수적입니다.이번 포스트에서는 hangul-js를 활용하여 검색 효율을 높이고, 외부 환경에 영향을 받지 않는 독립적인 LocalAsyncSelectBox를 구축한 과정을 정리합니다.1. 커스텀 컴포넌트 구현의 필요성기존 오픈소스 라이브러리들은 범용성을 위해 설계되었으나, 다음과 같은 기술적 제약이 있었습니다.한글 검색 최적화 미흡: 단순 텍스트 매칭만 지원하여 초성 검색 등 한국어 특화 기능을 별도로 구현해야 함.레이아웃 간섭: 드롭다운 UI가 부모 ..
1. 상태가 다른 상태를 바꾸는 패턴 (useEffect 안에서 setState)const [a, setA] = useState(0);const [b, setB] = useState(0);useEffect(() => { setB(a * 2);}, [a]);문제: 다른 값으로부터 계산 가능한 값을 굳이 상태로 두면 무한 루프 위험 + 중복 관리 필요해결: 계산은 상태로 두지 않고 직접 계산const [a, setA] = useState(0);const b = a * 2; // 혹은 useMemo2. 상태를 지나치게 잘게 나눈 경우const [email, setEmail] = useState('');const [isValid, setIsValid] = useState(false);문제: 서로 연결된 값이 ..
expo의 장점과 eas 지출회피를 같이하기 위해 prebuild를 사용하고 있었으나prebuild 시 ios 폴더가 초기화되는 현상 떄문에 fastlane을 적용하기 어려웠다.방법 1. expo config plugin방법 2. app.json 설정위 방법들은 prebuild시 유지해야할 항목을 보존하는데 도움을 줄 순 있으나 fastlane 관련 파일을 보존하긴 어려워prebuild와 복구 명령어를 통합하기로 했다.fastlane 폴더를 루트에서 백업bash/project-root┣ /fastlane-template/┃ ┣ Appfile┃ ┗ Fastfile2. expo prebuild 이후에 복사bashnpx expo prebuild --cleancp -R ../fastlane-template io..
npx expo prebuild --cleancd iospod installopen ios/nocotine.xcworkspace왼쪽 상단에서 "nocotine" → "nocotine" (프로젝트, 파란색 아이콘) 클릭TARGETS > nocotine 선택Signing & Capabilities 탭에서Team: Apple Developer 계정 선택Bundle Identifier: com.jaegwan.nocotine 맞는지 확인Provisioning Profile: 자동으로 맞춰주거나, 직접 선택(Ad Hoc 등)Build Settings > Versioning에서Current Project Version: 빌드 넘버(예: 3)Version: 마케팅 버전(예: 1.0.2)상단 Scheme에서 "Any i..
0. eas build는 github repo를 기반으로 빌드app.json 파일 점검하여 ios:{buildNumber} 등 체크1. ipa 생성npm install -g eas-clieas logineas build:configure # (최초 1회)eas build -p ios --profile production # 필요시 --clear-cache 위 과정을 따른 후 빌드가 완료되면 EAS 에 접속 해 ipa 를 내려받는다.2. apple connect에 전송 3. 심사 제출
문제 상황: Type Guard와 every 메서드의 한계TypeScript는 코드의 타입 안전성을 보장하기 위해 컴파일 시점에 타입을 검사한다. 이 과정에서 타입 가드와 타입 단언을 활용해 컴파일러가 타입을 정확하게 추론하도록 돕는다. 하지만 때로는 타입 가드를 사용했음에도 TypeScript가 배열의 타입을 확신하지 못하는 경우가 생긴다.예시 코드다음 예제에서 result 배열이 FileSystemFileEntry 또는 FileSystemDirectoryEntry로만 구성되어 있는지를 every 메서드를 통해 확인하고자 한다.if ( result.length > 0 && result.every(entry => isFileSystemFileEntry(entry) || isFileSystemD..