← 홈으로 돌아가기

📝 Daily Learning Log (2026-06-22 ~ 06-28)

주간 학습일지 · 2026-06-22 ~ 06-28

2026-06-22 (월)

✅ 학습 완료

💡 핵심

Custom Layout 4가지 필수 오버라이드:
메서드역할
prepare()모든 아이템 attributes 미리 계산·캐싱
collectionViewContentSize전체 콘텐츠 크기 (스크롤 범위)
layoutAttributesForElements(in:)해당 rect에 보이는 요소 반환
layoutAttributesForItem(at:)특정 아이템 attributes
Compositional Layout 가로 스크롤:
section.orthogonalScrollingBehavior = .continuous
// .paging / .groupPaging / .groupPagingCentered

📕 오답노트

contentInset=0이면 adjustedContentInset도 0?

  • ❌ 아니다. contentInsetAdjustmentBehavior = .automatic(기본값)이면 safe area가 자동 추가됨
  • contentInset = .zero여도 adjustedContentInset.top = 47(노치), .bottom = 34(홈 인디케이터)
  • 진짜 0 원하면: .never로 설정

스크롤 최하단 offset.y 계산에 .top 포함?

  • ❌ 포함 안 함. .top은 최상단 계산에만 관여
  • 최상단: offset.y = -adjustedContentInset.top
  • 최하단: offset.y = contentSize.height + adjustedContentInset.bottom - bounds.height
  • .top + .bottom은 "스크롤 가능 범위(거리)"를 구할 때 쓰는 것

inset 바꾸면 bounds/frame/contentSize 변함?

  • ❌ 안 변함. inset은 "스크롤 범위"만 바꿈
  • bounds.height = scrollView 자체 크기 (safe area 무관)
  • contentSize = 콘텐츠 크기 (inset과 별개 프로퍼티)
  • 스크롤 가능 범위 = top + contentSize + bottom이지만, 각 프로퍼티 값은 그대로

heightForRowAt이 셀 개수만큼 호출된다?

  • estimated 없으면 맞음. 100개 셀 → 100번 호출
  • estimated 있으면: 보이는 셀만 호출 (10개 정도), 나머지는 추정값으로 contentSize 계산
  • 스크롤하면 새로 보이는 셀에 대해 추가 호출

TableView에서 "높이만 알면 frame 자동" — 왜?

  • TableView = 세로 1열 누적으로 배치 규칙이 고정
  • x = 0 (항상), width = bounds.width (항상), y = 이전 셀들 높이 합
  • 그래서 "높이"만 알면 나머지(x, y, width)는 자동 계산
  • CollectionView는 자유 배치라 x, y, width, height 전부 알아야 함 → layoutAttributes 필요

layoutAttributesForElements에서 캐시된 attributes를 직접 수정?

  • ❌ 하지 마라. 다음 호출 때 오염된 상태로 시작
  • ✅ .copy() 후 수정해서 반환 → 원본 캐시 유지
// ❌
cache[i].transform = ...  // 원본 오염

// ✅
let attr = cache[i].copy() as! UICollectionViewLayoutAttributes
attr.transform = ...
return attr

2026-06-23 (화)

✅ 학습 완료

💡 핵심

frame 설정 시점 구분:
방식layer.frame 변경 시점
view.frame = ... (직접)즉시
Auto Layout (제약조건)레이아웃 패스 (layoutSubviews)
Auto Layout 흐름 (순서 중요!):
  1. 제약조건 변경 → setNeedsLayout()
  2. BeforeWaiting
  3. updateConstraints() — 바텀업 ↑
  4. layoutSubviews() — 탑다운 ↓ ← 여기서 frame 계산!

📕 오답노트

frame 직접 설정해도 레이아웃 패스 때 바뀐다?

  • ❌ 아니다. view.frame = ...즉시 layer.frame 변경
  • setNeedsLayout()은 호출되지만, 이건 서브뷰 재배치
  • 레이아웃 패스에서 frame 계산하는 건 Auto Layout일 때만

heightForRowAt 있으면 estimatedRowHeight 안 쓴다?

  • ❌ 같이 쓸 수 있다!
  • estimated 있음 → 보이는 셀만 heightForRowAt 호출
  • estimated 없음 → 모든 셀 heightForRowAt 호출 (1000개면 1000번!)
  • estimated는 "안 보이는 셀 높이 추정"용

heightForRowAt 구현하면 systemLayoutSizeFitting도 호출된다?

  • ❌ 별개다! heightForRowAt 구현하면 systemLayoutSizeFitting 호출 안 됨
  • systemLayoutSizeFittingrowHeight = automaticDimension + heightForRowAt 없을 때만

bounds.origin 변경하면 layoutSubviews 호출된다?

  • 일반 UIView: ❌ 호출 안 됨 (서브뷰 재배치 필요 없으니까)
  • UIScrollView/TableView/CollectionView: ✅ 호출됨 (오버라이드되어 있음)
  • 화면 렌더링은? layoutSubviews 없이 Core Animation이 처리

2026-06-24 (수)

✅ 학습 완료

💡 핵심

await 후 context 복귀:
Swift ConcurrencyGCD
완료 후원래 context 자동 복귀호출받은 쪽이 결정
메인 UI 업데이트그냥 됨 (@MainActor)DispatchQueue.main.async 필요
Continuation "머문다"의 의미:
await 만남 → continuation 객체 생성 → 스레드 반납 → 대기(메모리에 객체만 존재)
         ↓
   awaited 작업 완료 시 continuation.resume() → Task 재개

Low priority가 오래 머무는 이유: Executor가 높은 우선순위 Task를 먼저 resume → Low는 큐에서 계속 밀림

Swift Concurrency 스레드 할당 규칙:
  • Suspension point에서 높은 우선순위 Task가 스레드 할당받을 확률 높음
  • 가장 높은 우선순위 Task가 코어 수만큼 스레드 점유 중이면 → 낮은 우선순위용 별도 스레드 추가
  • 따라서 스레드 수는 코어 수로 고정 X, 상황에 따라 더 많을 수 있음
  • 추가된 스레드는 낮은 우선순위 작업만 실행 → 무한정 대기(starvation) 방지
예: 6코어 기기, High 6개 + Low 4개 Task
┌─────────────────────────────────────┐
│ Thread 1-6: High Task (코어 수만큼)  │
│ Thread 7:   Low Task (추가 스레드)   │ ← starvation 방지
└─────────────────────────────────────┘
자식 Task 생성 = Suspension point 역할:

자식 작업을 생성하면 OS가 스케줄링을 진행 → 스레드를 우선 할당받아야 하는 작업에 스레드 재할당

자식 Task 생성 방법 분류
async let child = ... Structured (자동 취소)
withTaskGroup { group.addTask { } } Structured (자동 취소)
withThrowingTaskGroup { ... } Structured (자동 취소)

Task { }, Task.detached { }는 새 Task 생성이지만 "자식"은 아님 (unstructured)

"자식(child)"의 의미:

  • 부모 취소 시 자동 취소
  • 부모 scope 끝나면 자동 await (암묵적 대기)
  • = Structured Concurrency의 핵심
⚠️ Swift Concurrency + GCD 혼용 시 성능 저하:

GCD가 스레드를 많이 생성하면 → 코어가 Concurrency 스레드에 할당할 CPU 시간 부족

Concurrency 단독 (정상):
Core 1: ████████ Concurrency Thread (100% 시간)
Core 2: ████████ Concurrency Thread (100% 시간)

GCD 혼용 (문제):
Core 1: █░█░█░█░ GCD 8개 + Concurrency 1개 경쟁
Core 2: █░█░█░█░ → Concurrency 받는 시간 ↓↓↓
Swift Concurrency GCD
스레드 수 코어 수 고정 (~6) 무제한 생성 가능
철학 "코어 독점" 가정 "필요하면 더 만들어"

결과: GCD가 스레드 폭발 → Concurrency의 "코어 독점" 가정 깨짐 → Task 느려짐

📚 참고 자료

GPU 렌더링 · 오프스크린 · VSync

Q&A 정리 문서

Clean Architecture + MVVM 학습

kudoleh/iOS-Clean-Architecture-MVVM 레포 분석

완벽 가이드 문서

2026-06-26 (목)

✅ 학습 완료

💡 핵심

DI vs DIP:
DI (Dependency Injection)DIP (Dependency Inversion)
의미외부에서 의존성 주입추상화(Protocol)에 의존
분류기법 (How)원칙 (Why)
Clean Architecture 파일 수정 범위:

기능 추가 시 비즈니스 로직도 바뀜. 핵심은 "바꿀 파일이 분리됨"

MVC:         한 파일에 UI + 로직 섞임 → 전부 수정
Clean Arch:  UseCase, ViewModel, View 각각 분리 → 역할별 수정

📝 오답노트

읽은 문서

Clean Architecture + MVVM 완벽 가이드 ✅ 완독

Clean Architecture + Dispatch Q&A (오늘 질문 정리)

2026-06-27 (토)

✅ 학습 완료

📕 오답노트

let animal: Animal = Dog()에서 Dog 고유 메서드(bark) 호출?

  • 못 한다. 가시성=선언 타입(Animal) → bark 안 보임(컴파일 에러). 다운캐스팅(as? Dog) 필요
  • 디스패치(어느 구현 실행)는 런타임 타입(Dog)=vtable. 가시성≠디스패치
  • existential heap box는 값 struct가 24B 초과일 때만 — Array는 8B라 inline(box 없음)

2026-06-28 (일)

✅ 학습 완료

📕 오답노트

shouldInvalidateLayout이 true면 무슨 일? hitTest가 panGesture를 찾나?

  • shouldInvalidate true=레이아웃 invalidate→prepare 재계산(캐시 갱신). false(그리드 기본)=캐시 재사용
  • hitTest는 panGesture가 아니라 터치 지점의 뷰를 찾음(1회). 버튼/스크롤뷰/셀에 따라 다른 처리
  • attributes 적용=cell.apply(_:), self-sizing은 크기 한 번 보정 후 수렴