← 홈으로 돌아가기
2026-07-20 (월) — 스크롤 렌더 심화
학습 주제 — 셀 높이(테이블 vs 컬렉션)·오프스크린·블렌딩 심화
- 📄 스크롤 렌더 심화 → 스크롤/테이블/컬렉션뷰 문서 §12 후속
- 셀 높이: 테이블=행별(automaticDimension+estimatedRowHeight), 컬렉션=레이아웃 객체가 크기 결정(estimatedItemSize/self-sizing)
- CAShapeLayer=벡터 path single-pass 직접 래스터화라 오프스크린 아님(masksToBounds는 합성 결과 오려서 오프스크린)
- shadowPath=그림자 모양 미리 명시 → 콘텐츠 오프스크린 렌더로 모양 추출 단계 생략 → 방지
- shouldRasterize=레이어 렌더 캐싱(정적=이득, 동적=매 프레임 재래스터화 역효과)
- 블렌딩 줄이기=반투명 픽셀 섞기(GPU 연산)를 isOpaque로 덮어쓰기로 전환 → GPU 부하↓
오답노트
- ❌ "오프스크린(GPU)은 CPU draw보다 항상 나음" → 메인 안 막는 장점은 있으나 여전히 비싼 추가 패스. 목표는 single-pass(shadowPath·미리 둥근 이미지)
- ❌ "shouldRasterize는 항상 최적화" → 자주 바뀌면 매 프레임 재래스터화로 역효과
- ❌ "블렌딩은 CPU 문제" → GPU 픽셀 연산량. 불투명화(isOpaque)로 감소
2026-07-20 (월) — 네트워크·아키텍처·스크롤 파이프라인 통합노트 정리
학습 주제 — 네트워크 레이어·견고한 파싱·토큰 리프레시·스크롤 dedup·테스트 아키텍처
- 📄 네트워크 레이어·견고한 파싱·토큰 리프레시(신설) → 문서
- 네트워크 계층: Endpoint(값으로 요청 정의)/Encoding/Client(공통 인증·재시도)/Decoding/Error Mapping
- DTO↔도메인 분리: 변경 격리·모양 다름·지저분함 차단·테스트
- 토큰 리프레시 동시성: 동시 401 → in-flight dedup actor로 리프레시 딱 1회(refreshTask await 전 박기·"이미 갱신됨" 체크·재시도 1회)
- Codable 견고 파싱: Failable(lossy)·decodeIfPresent 기본값·convertFromSnakeCase/iso8601·타입 오락가락·깨진 것 로깅
- 📄 스크롤 성능 파이프라인 → 스크롤 문서 §13
- 셀 재사용 황금규칙: 모든 분기 상태 명시 설정(else 없으면 유령 배지)·prepareForReuse 리셋
- prefetch=힌트(보장 아님) → cellForItemAt에서도 로딩·취소 콜백 짝·같은 캐시/in-flight 공유
- in-flight dedup ImageLoader actor: inProgress를 await 전 박기, 다운로드+디코딩 한 task, 실패 시 캐시 제거
- 📄 테스트 아키텍처 보강 → Clean/MVVM 문서
- 테스트 3원칙(DI·프로토콜 추상화·명확한 입출력), DIP 화살표(데이터→도메인), 레이어별 Mock 표, MVVM+DI Stub 테스트, Input/Output 패턴
오답노트
- ❌ "동시 401은 각자 리프레시" → 리프레시 5번 중복→rotation이면 로그아웃 대참사. in-flight dedup으로 정확히 1회
- ❌ "in-flight dedup은 다운로드 task만 캐시하면 됨" → 디코딩을 별도 task로 빼면 미디코딩/중복. 다운로드+디코딩 한 task
- ❌ "배열 하나 깨지면 어쩔 수 없이 전체 실패" → Failable 래퍼(lossy)로 정상 항목만 살림 + 깨진 개수 로깅
- ❌ "prefetch만 하면 됨" → 힌트라 안 불릴 수 있음. cellForItemAt에서도 반드시 로딩
- ❌ "DIP는 그냥 프로토콜 쓰는 것" → 프로토콜을 상위(도메인)가 소유, 하위(데이터)가 구현 → 의존 화살표 안쪽으로 역전
2026-07-20 (월) — Instruments 성능 측정 기본
학습 주제 — Instruments 기본(다운샘플 메모리·피크 측정)
- 📄 Instruments 성능 측정 기본(신설) → 문서
- 기본 5개: Time Profiler(CPU 병목)·Allocations(메모리)·Leaks(순환참조)·Core Animation(오프스크린 노랑/블렌딩 빨강)·Memory Graph
- Call Tree 옵션: Separate by Thread·Hide System Libraries·Invert Call Tree
- 실전: Allocations Call Tree로 다운샘플 코드 할당 확인, 작업 구간 그래프 최고점으로 peak 비교
오답노트
- ❌ "Growth로 피크 측정" → Growth(Generations)는 '남는 지속 증가분'(누수/abandoned)용. 순간 피크는 Allocations 그래프 최고점/Debug 게이지로. 재는 게 다름
- ❌ "다운샘플 효과는 growth로 증명" → 풀디코딩 순간 스파이크(peak)를 안 만드는 게 핵심 → peak로 봐야 정확
2026-07-20 (월) — 웹뷰 Tier 2 ⑦
학습 주제 — 웹뷰 보안 (App-Bound Domains·origin 검증)
- 📄 웹뷰 보안 → 웹뷰 학습 문서 §7
- 브리지 노출 기준 = 프로세스 아니라 configuration(userContentController). 한 페이지의 모든 프레임(메인+iframe)이 접근
- 제3자 iframe = 새 창 아니라 페이지 HTML 안에 박힌 서브 프레임(광고·결제위젯). 같은 웹뷰라 브리지 호출 시도 가능
- App-Bound Domains(iOS14+): evaluateJavaScript·메시지핸들러·쿠키조작을 내 도메인만 잠금(브라우저형엔 부적합)
- origin 검증: frameInfo.isMainFrame + securityOrigin(host/https) + 액션 화이트리스트
- forMainFrameOnly(iframe 주입 배제)·WKContentRuleList·ATS·비밀값 JS 금지
오답노트
- ❌ "브리지는 프로세스 단위로 노출" → configuration(userContentController) 단위. 같은 config 웹뷰·모든 프레임이 접근
- ❌ "제3자 iframe = 새로 열린 웹뷰" → 페이지 안에 박힌 서브 프레임(같은 웹뷰). 새 창은 createWebViewWith(§4)
- ❌ "App-Bound Domains는 그냥 켜면 좋음" → 지정 도메인 외엔 강력 API 막힘. 브라우저형(임의 URL) 앱엔 부적합
- ❌ "메시지 오면 바로 처리" → isMainFrame·origin·액션 화이트리스트·입력 검증 필수
2026-07-20 (월) — 통합노트 신규분(UIKit 렌더링·런루프·생명주기 + GCD)
학습 주제 — UIKit 렌더링 파이프라인·런루프·생명주기 + GCD 보강
- 📄 UIKit 렌더링·런루프·생명주기(신설) → 문서
- 래스터화(벡터→픽셀) vs 디코딩(압축→픽셀), layer.contents 3갈래(draw CPU/이미지 디코딩/없음)
- CPU=텍스처 만들기(래스터화·디코딩) / GPU=합성. 도형·색은 GPU 대체 가능, 이미지 디코딩·텍스트는 CPU 몫. Core Graphics≠Core Animation(형제)
- 업데이트 사이클: 제약→레이아웃→디스플레이(draw)→CATransaction commit→렌더 서버 합성. "모인 변경"=레이어 트리 전반
- 런루프: 스레드당 하나, 백그라운드는 직접 run(). Timer 모드 함정(.tracking→.common). perform 계열
- hitTest=누가 받을지(≠first responder) / responder chain=안 받으면 누구에게. UIScene 계층(프로세스→앱→씬→윈도우)
- 📄 GCD 보강 → GCD vs Swift Concurrency 문서
- QoS 등급·우선순위 역전 방지(QoS promotion), main.sync 데드락(런루프 관점), 동시 큐+barrier(읽기 sync/쓰기 async barrier), actor가 barrier큐/DispatchGroup보다 안전한 4이유
오답노트
- ❌ "GPU 합성이 이미지 디코딩도 대체" → 디코딩은 CPU 몫(GPU 샘플링엔 디코딩된 비트맵 필요). 도형·색만 대체 가능
- ❌ "Core Graphics는 Core Animation 안에 있음" → 별개 형제 프레임워크(CG=래스터화, CA=합성/애니메이션)
- ❌ "hitTest가 first responder를 찾음" → hit-test view를 찾음. first responder는 키보드/텍스트 포커스로 별개
- ❌ "Timer는 항상 fire" → 스크롤 시 .tracking 모드로 .default 타이머 멈춤 → .common 모드 등록 필요
- ❌ "barrier 큐가 actor만큼 안전" → actor는 컴파일 타임 데이터레이스 검사, barrier는 우회 접근 못 막음
2026-07-20 (월) — 실전 통합 ImageLoader 코딩
학습 주제 — 배운 것 전부를 한 파이프라인으로(캐시+다운샘플+dedup+취소)
- 📄 실전 통합 ImageLoader(신설) → 문서
- 흐름: 메모리캐시 히트→즉시 / in-flight 진행중→Task 공유 / 새 Task(디스크→없으면 네트워크→디스크저장)→백그라운드 디코딩+다운샘플→메모리 승격
- actor로 in-flight dedup(inFlight를 await 전 박기), 다운로드+디코딩 한 Task, 실패 시 캐시 제거
- 다운샘플(ShouldCache:false 피크 절약), 2단 캐시(NSCache 디코딩/디스크 압축), Task.checkCancellation
- 셀: cancel + representedURL==url identity 검증 + prepareForReuse 리셋
오답노트
- ❌ "다운로드 Task만 캐시하면 dedup 됨" → 디코딩 별도 Task면 미디코딩/중복. 다운로드+디코딩 한 Task 필수
- ❌ "실패도 캐시" → 실패는 inFlight 제거해야 재시도 가능
- ❌ "inFlight를 await 후 박아도 됨" → await 전(동기 구간)에 박아야 그 사이 들어온 호출이 공유(dedup 성립)
2026-07-20 (월) — 웹뷰 Tier 3 ⑩
학습 주제 — SwiftUI 통합 (UIViewRepresentable·Coordinator)
- 📄 SwiftUI 통합 → 웹뷰 학습 문서 §10
- UIKit 뷰(WKWebView)를 UIViewRepresentable로 감쌈. makeUIView(1번 생성=@State처럼)·updateUIView(매 갱신=body처럼)·makeCoordinator·dismantleUIView
- Coordinator(class)=delegate·메시지핸들러 안정적 소유자·SwiftUI @Binding 브릿지(Representable은 struct라 재생성)
- Representable struct 재생성돼도 WKWebView·Coordinator는 SwiftUI가 identity로 유지(§11 개념)
- 함정: updateUIView 값 같으면 스킵(불필요 reload), @Binding은 메인에서, retain cycle은 WeakScriptMessageHandler
오답노트
- ❌ "updateUIView에서 매번 load" → 리렌더마다 재로딩. 값 같으면 스킵
- ❌ "Representable(struct)를 delegate로" → 재생성돼 부적합. Coordinator(class)가 안정적 소유자
- ❌ "makeUIView가 매번 호출" → 딱 1번(@State처럼). 갱신은 updateUIView
2026-07-20 (월) — SwiftUI 레이아웃 시스템
학습 주제 — 부모 제안→자식 결정→부모 배치
- 📄 SwiftUI 레이아웃 시스템(신설) → 문서
- 3단 협상: 부모가 크기 제안 → 자식이 결정(부모 강제 못 함) → 부모가 배치
- .frame도 뷰(자식에 제안, 자기는 부모에 보고). Text=필요한 만큼/Color·Spacer=flexible 다 차지/Image=고정(resizable 전)
- Stack: 고정 자식 먼저→남은 공간 유연 자식에. layoutPriority
- frame 고정(width) vs 유연(maxWidth:.infinity), GeometryReader=greedy, fixedSize=ideal 크기, Layout 프로토콜(iOS16)
오답노트
- ❌ "부모가 자식 크기를 정한다" → 제안만. 최종 크기는 자식이 결정
- ❌ ".frame은 크기 설정" → frame도 뷰. 자식에 제안하고 자기 크기를 부모에 보고(자식이 더 작을 수 있음)
- ❌ "GeometryReader는 그냥 크기 읽기" → greedy라 제안 공간 다 차지해 레이아웃 흐트러뜨림
2026-07-20 (월) — TableView/CollectionView 노트(Diffable·간격)
학습 주제 — Diffable Data Source 심화 · FlowLayout 간격 구분
- 📄 Diffable Data Source 심화 → 스크롤/테이블/컬렉션뷰 문서 §14
- 전통(numberOfItems/cellForItemAt 수동 동기화, 개수 안 맞으면 크래시) vs Diffable(스냅샷 선언)
- "apply는 수동, diff는 자동"(자동 감지 아님). applyInitialSnapshot은 내장 API 아니라 직접 헬퍼
- Section·Item 둘 다 Hashable 필수. 정체성=안정적 고유 ID, 내용 변경은 reconfigureItems(가변 내용 Hashable에 섞으면 삭제+삽입)
- 대체 표(numberOf→스냅샷/cellForItem→셀 제공자 클로저/reloadData→apply). dataSource는 viewDidLoad에서 1번 생성
- FlowLayout: lineSpacing=스크롤 방향 줄 사이, interitemSpacing=한 줄 안 아이템 사이(가로 스크롤이면 뒤집힘)
오답노트
- ❌ "Diffable은 데이터 변화를 자동 감지" → apply는 내가 수동 호출, diff·애니메이션만 프레임워크
- ❌ "Hashable에 title(내용) 넣어도 됨" → 내용 바뀌면 다른 아이템으로 착각·전체 리로드. 정체성=고유 ID만
- ❌ "dataSource를 cellForItemAt에서 생성" → viewDidLoad에서 1번 생성·보관, 생성자 클로저가 cellForItemAt 대체
- ❌ "lineSpacing=가로 간격" → 스크롤 방향에 따라 다름. 세로 스크롤이면 line=행 사이 세로, interitem=행 안 가로
2026-07-20 (월) — CollectionView 심화(reconfigure·CellRegistration·orthogonal)
학습 주제 — reload vs reconfigure · CellRegistration · orthogonal · 피드 설계
- 📄 CollectionView 심화 → 스크롤/테이블/컬렉션뷰 문서 §15
- reloadItems(재사용 큐로 보내 새 dequeue) vs reconfigureItems(iOS15+, 기존 셀 그대로 제공자만 재실행·싸다)
- CellRegistration(iOS14+): 식별자 문자열 제거·타입 안전·dequeueConfiguredReusableCell
- orthogonalScrollingBehavior(.continuous/.paging): 중첩 컬렉션뷰 없이 가로 스크롤 섹션·같은 Diffable 공유
- 피드 설계 요약: Compositional+Diffable+self-sizing+이미지 파이프라인+prefetch/취소/identity+페이지네이션
오답노트
- ❌ "내용만 바꿀 때도 reloadItems" → 깜빡임·비쌈. iOS15+ reconfigureItems가 기존 셀 유지·저렴
- ❌ "가로 스크롤 섹션은 중첩 컬렉션뷰" → orthogonalScrollingBehavior 한 줄로 해결(dataSource 공유)
2026-07-21 (화)
학습 주제 — CollectionView Self-Sizing Cell 전체 동작 흐름
- 📄 Self-Sizing Cell 흐름 → 스크롤/테이블/컬렉션뷰 문서 §16
- 순서: numberOfItems → Layout.prepare(estimated) → layoutAttributesForElements → 보이는 셀만 cellForItem → preferredLayoutAttributesFitting → systemLayoutSizeFitting → 비교 → shouldInvalidateLayout → invalidationContext(부분) → prepare 재수행
- Layout Attributes가 먼저 만들어지고 그 위치의 셀만 cellForItem(반대 아님)
- preferredLayoutAttributesFitting 호출 주체 = CollectionView(Layout 아님)
- invalidationContext(forPreferredLayoutAttributes:)로 바뀐 셀 이후만 재계산(전체 아님)
- TableView는 preferred 훅 없이 테이블뷰가 contentView에 systemLayoutSizeFitting 직접 호출(automaticDimension)
오답노트
- ❌ "cellForItem이 prepare보다 먼저" → Layout.prepare/attributes가 먼저, 그 위치의 보이는 셀만 cellForItem
- ❌ "items 수만큼 cellForItem 호출" → 개수 파악은 numberOfItems, 실제 셀은 보이는 것만(1만개여도 10~15)
- ❌ "preferredLayoutAttributesFitting은 Layout이 호출" → CollectionView 시스템이 호출
- ❌ "invalidate하면 전체 재계산" → invalidationContext로 바뀐 셀 이후만