TAGS
주제로 글을 찾습니다
Tech, Domain, Journal 글에 붙은 공통 태그를 모았습니다.

후기 탐색을 주문 시작으로 잇기 위한 운송후기 탭 실험
운송후기 탭 실험에서 체크아웃 시작은 늘었지만 구매 전환 차이는 확인되지 않았습니다. 지도·검색·사진 필터의 효과를 어디까지 해석할 수 있었는지 첫 DRI의 판단과 함께 정리했습니다.
푸시 알림 생명주기
"FCM을 쓰면 APNs가 필요 없다"는 오해가 흔하다. iOS 푸시는 어떤 경우에도 APNs를 거친다. FCM은 서버에서 APNs까지의 구간을 감싸 주는 편의 레이어이고, 기기에 메시지를 꽂는 건 언제나 APNs다.
딥링크와 앱 라우팅
웹에서 https://shop.example.com/product/42를 누르면 상품 화면으로 간다. 딥링크는 같은 일을 앱에서 하는 링크다. 푸시·알림톡·광고를 눌렀을 때 홈이 아니라 그 화면으로 곧장 보낸다. 중간 화면을 하나 더 거치게 두면 그 사이에서 이탈이 난다.
차주 위치를 실시간으로 공유하는 운송 추적
배민·우버처럼 차주 위치를 지도에서 실시간으로 공유하는 운송 추적을, 폴링·SSE·WebSocket 중 무엇을 어디에 쓸지 고르고, 끊겨도 화주가 못 느끼게 재연결과 경로 이탈·지도 렌더까지 설계한 기록.
WWDC26 워치파티 후기
6월 8일 밤 시작해 다음 날 새벽까지 이어진 WWDC26 워치파티 기록.
react-native-bootsplash 내부 동작 원리
OS LaunchScreen과 RN 첫 paint 사이의 공백을 메우는 방법.
WKWebView와 Android WebView가 화면을 그리는 방식의 차이
같은 페이지가 iOS WKWebView에서는 fragment 앵커 점프 후 위쪽이 빈 화면으로 보이고 Android WebView에서는 멀쩡히 그려진다. 두 엔진의 tile/painting 모델을 따라가며 왜 한쪽에서만 일어나는지 정리한다.
Data Essentials in SwiftUI (WWDC20 10040)
Curt Clifton·Luca Bernardi·Raj Ramamurthy가 SwiftUI의 데이터 소유권·source of truth·@State/@StateObject/@ObservedObject·view life cycle을 한 번에 정리한 세션. 내용·코드·맥락을 한국어로 재구성한다.
Demystify SwiftUI (WWDC21 10022)
Matt Ricketson·Luca Bernardi·Raj Ramamurthy의 대표 세션. identity·lifetime·dependencies 세 축으로 SwiftUI의 업데이트 규범을 체계화한다. dependency graph가 곧 SwiftUI의 render tree라는 것까지.
Demystify SwiftUI Performance (WWDC23 10160)
SwiftUI 앱이 느려지는 원인을 진단하는 법. printChanges로 의존성 추적, body 안의 work 제거, List/Table의 identity 수집 메커니즘까지 — 2021 Demystify가 이론이라면 이 세션은 최적화 매뉴얼이다.
SwiftUI의 Identity · Lifetime · Dependencies
Omar Radwan의 SwiftUI Under the Hood 번역. Demystify SwiftUI 세션(WWDC21 #10022 · WWDC23 #10160)의 세 기둥인 identity·lifetime·dependencies를 코드 예제와 함께 정리한다.
Discover Observation in SwiftUI (WWDC23 10149)
Swift 5.9의 @Observable 매크로는 ObservableObject의 구조적 한계를 뒤집는다. body가 실제로 읽은 property만 추적하는 granular tracking 메커니즘과, @State·@Environment·@Bindable 세 개로 단순화된 property wrapper까지.
A Day in the Life of a SwiftUI View
SwiftUI의 view tree → render tree 변환, .task 재실행 문제와 .id/.task(id:)의 트레이드오프, proposal-reporting 레이아웃 시스템, environment와 preferences의 방향성을 그림 100여 장으로 풀어낸 Chris Eidhof의 강연 정리.
Variadic Views in SwiftUI
VStack 안에 여러 뷰를 넣으면 SwiftUI는 그걸 어떻게 들고 있을까. TupleView·_VariadicView.Tree·UnaryViewRoot/MultiViewRoot의 구분, 그리고 List가 행 사이에 Divider를 끼워 넣을 수 있는 이유까지.
RxSwift에서 Combine으로 넘어가며 정리한 것들
- 값의 변화를 시간 축 위의 스트림으로 다루고, 스트림에 변환을 연쇄 적용해 결과 스트림을 얻는 방식 - 명령형: "지금의 값을 꺼내 계산한다" - 반응형: "값이 바뀔 때마다 어떻게 이어지는지 미리 정의해 둔다" - 목적: 수동 구독·콜백·상태 동기화가 중첩되며 생기는 복잡도를 값의 변화 자체에 위임
SwiftUI의 내부 동작 원리
View tree, Attribute Graph, _UIHostingView까지 — SwiftUI가 선언형 뷰를 실제 UIKit·Core Animation 위에 매핑하는 내부 구조를 따라간다.
iOS 앱 샌드박스와 Extension 정리
- iOS 앱은 모두 샌드박스 안에서 동작. 다른 앱·시스템 자원에 임의 접근 불가 - 목적: 보안 경계 + 리소스 사용 규제 - 격리 범위 - 파일 시스템: 앱마다 독립된 홈 디렉토리 - 네트워크: 자체 URLSession·캐시 - 메모리: 프로세스 단위 격리 - Keychain: 기본은 앱 전용. access group 명시 시에만 공유 - UserDefaults: 앱 번들 단위
WKWebView의 내부 동작 원리
WKWebView를 쓰다 보면 비슷한 순간이 반복됩니다. 로그인 쿠키를 넣었는데 첫 요청에는 빠지고, JS 브리징은 언제나 completion이나 callback으로 돌아오고, 메모리 압박이 오면 앱은 살아 있는데 웹 화면만 하얗게 비어 버립니다.
UIKit의 내부 동작 원리
Runloop, CATransaction, Core Animation layer tree 등 UIKit이 화면을 그리기까지의 내부 동작을 파이프라인 단계별로 정리한다.
Alamofire 제대로 쓰기
Alamofire는 Apple URLSession 위에 올라가는 Swift 네트워킹 프레임워크다. 기본 사용법(AF.request, responseDecodable)은 단순하지만, 실제 프로덕션 앱에서는 엔드포인트 관리, 인증 헤더 주입, 토큰 자동 갱신, 스트리밍, 업로드 등 고급 기능이 필요해진다.
Kingfisher Under the Hood
Kingfisher는 URLSession 위에 올라가는 Swift 이미지 로딩 라이브러리다. imageView.kf.setImage(with: url) 한 줄로 동작하지만, 그 한 줄 아래에는 다중 레이어 캐시, 비동기 다운로드 파이프라인, 프로세서 체인, 프리패칭, 재시도 전략이 맞물려 돌아간다.
TCA Under the Hood
TCA(The Composable Architecture)를 쓰면서 "왜 이렇게 동작하지?" 싶었던 순간들이 있었다. send가 동기인 것 같으면서도 Effect가 비동기고, 같은 액션을 두 번 보내면 경고가 뜨고, scope를 호출할 때마다 새 Store가 생기지 않는 것처럼 느껴지고.
URLProtocol과 Bonjour로 직접 만든 iOS 실시간 네트워크 디버거
센디 앱을 개발하면서 네트워크부터 의심해야 하는 순간이 자주 있었습니다. QA에서 올라오는 "API가 느려요" 리포트, 특정 화면에서만 재현되는 빈 응답 같은 문제는 대부분 네트워크 구간을 봐야 답이 나옵니다. 그런데 iOS에서 개발 중인 실기기의 요청을 바로 들여다보는 흐름이 생각보다 매끄럽지 않았습니다. Alamofire나 URLSession에 print 로깅을 달아 놔도 Xcode 콘솔에서 필요한 요청만 골라 읽기…
iOS 코드 서명 정리
- 앱의 출처(등록된 개발자·조직)를 검증 - 서명 이후 코드 변조 여부 검증 - Push Notification · App Groups 같은 권한 사용 가능 앱 제한
GitHub Actions + Fastlane 기반 iOS 배포 자동화
기능을 만드는 시간보다 빌드를 기다리는 시간이 더 길게 느껴질 때가 있습니다. 코드 한 줄을 고쳐도 확인을 받으려면 앱을 새로 빌드해 전달해야 하고, 이 과정은 수정할 때마다 처음부터 다시 반복됩니다.
센디 iOS 앱에 React Native 얹기
센디는 iOS와 Android 앱을 각각 네이티브로 개발해 왔습니다. 새로운 기능이 추가되면 두 플랫폼에서 동일한 요구사항을 바탕으로 화면과 로직을 구현했습니다.
Letus Go! 2025 가을 모임 후기
마루360에서 열린 Letus Go! 가을 모임에 다녀온 기록.
React Native New Architecture의 내부 동작 원리
React Native에서 버튼을 탭하면 무슨 일이 일어날까?
React Native 브라운필드 통합
기존 앱에 RN 얹기.
CRM 캠페인에서 시작한 앱 진입 시스템 구축기 (1): 푸시 알림 수신과 클릭
앱 진입 시스템 구축기 1편. CRM 캠페인에서 사용한 Notification Service Extension 코드와 2025년 7월 기기 화면 녹화로 이미지 푸시 처리와 탭 이후 흐름을 정리.
CRM 캠페인에서 시작한 앱 진입 시스템 구축기 (2): 여러 경로를 모으는 딥링크 라우팅
앱 진입 시스템 구축기 2편. CRM 캠페인에서 사용한 푸시·Universal Link·OneLink 세 진입 경로를 하나의 매칭 테이블로 모으고, 해석·매칭·내비게이션을 타입별로 분리해 새 캠페인을 테이블 한 줄로 끝낸 라우팅 설계.
CRM 캠페인에서 시작한 앱 진입 시스템 구축기 (3): 웹뷰와 네이티브를 잇는 브릿지
앱 진입 시스템 구축기 3편. CRM 이벤트 웹뷰 CTA를 네이티브 화면으로 잇는 단일 브릿지 채널과 enum 강제 디코딩으로 무반응을 없애고, 2-depth 웹뷰 스택을 네이티브 네비게이션으로 관리한 기록.
React Native iOS 앱 부트스트랩
@main에서 첫 화면까지.
WWDC25 워치파티 후기
부산에서 올라와 WWDC25 워치파티에 참석하고 새벽 기차로 돌아온 기록.
센디 결제카드 스캐너 개발기 (2): 카메라 없이 검증하는 모듈 설계
카메라와 촬영 화면, 카드 인식 기능을 하나의 모듈에 담고 영상과 합성 이미지로 검증하는 구조를 정리합니다. 입력을 교체하는 지점과 테스트별 확인 범위를 나누고, 실제 기기에 남겨 둔 검증도 설명합니다.
센디 결제카드 스캐너 개발기 (1): 카메라 프레임에서 카드 정보 추출
카드 번호를 직접 입력하는 부담을 줄이기 위해 구현한 카메라 스캐너입니다. 프레임 수집과 안내 영역 크롭, Vision 텍스트 인식부터 사용자 확인 화면까지의 처리 과정을 설명합니다.
제대로 알고 싶었던 한 해 (2024 회고)
혼자 공부해 온 iOS 지식을 돌아보고, 새싹 과정에서 배우고 설명하며 빈틈을 채운 2024년 회고.
AttributeGraph 풀어 보기
Apple 이 숨겨 놓은 AttributeGraph 프레임워크를 lldb 로 직접 찔러 본 기록. AG::Graph::print() 심볼을 GraphViz 로 덤프해 state 변경 → dirty 전파 → lazy evaluation → RootDisplayList 갱신까지 실제 그래프로 확인한다. subgraph 가 view tree 의 동적 구조를 어떻게 포장하는지까지.
컬렉션과 바인딩: forEach와 IdentifiedArray, 바인딩 변수
앞 장까지로 화면 하나와 화면 사이 전환을 다뤘다. 남은 건 화면 안쪽에서 자주 나오는 두 패턴이다. 하나는 목록이고, 하나는 폼이다.
화면 전환: iOS 15 대응부터 iOS 16 Stack까지
화면이 하나일 때는 1장의 단방향 흐름으로 충분하다. 문제는 화면이 여러 개가 되면서 시작된다. 목록에서 상세로 들어가고, 거기서 편집 시트를 띄우고, 저장하면 두 단계를 한 번에 닫는다. 이런 전환을 뷰 코드에서 명령형으로 밀고 당기면, 지금 어느 화면이 떠 있는지가 다시 여기저기 흩어진다. 1장에서 겨우 좁혀 놓은 추적 가능성이 화면 전환에서 무너지는 것이다.
배포의 원리와 패키지 관리
앱을 만드는 내내 Xcode의 실행 버튼 하나로 빌드하고 설치했다. 그런데 이 버튼이 실제로 무슨 일을 하는지는 오래 몰랐다. 소스 코드가 어떻게 실제 기기에서 도는 앱이 되고, 왜 아무 기기에나 깔리지 않는지가 불투명했다. 이걸 이해하려면 버튼 한 번으로 뭉뚱그려진 과정을 명령줄에서 손으로 밟아 보는 게 가장 빠르다.
의존성 관리와 테스트
1장의 Effect 안에서 apiClient.fetchItems()를 불렀다. 문제는 이 한 줄이 테스트를 통째로 어렵게 만든다는 데 있다. 테스트가 진짜 서버를 부르면 네트워크 상태에 따라 결과가 달라지고, 느리고, 서버가 특정 상황을 돌려주게 만들 수도 없다. 현재 시간, 랜덤 값, 파일 시스템도 같은 문제를 갖는다. 결과가 바깥 환경에 따라 달라지는 코드는 그대로는 테스트할 수 없다.
로컬 데이터 저장의 선택지
앱을 만들다 보면 데이터를 기기에 남겨야 하는 순간이 온다. 다크 모드 설정, 마지막으로 본 화면, 로그인 토큰, 다운로드한 이미지, 오프라인에서도 봐야 할 목록까지 종류가 제각각이다. 처음엔 이 모든 걸 UserDefaults에 넣으려다 문제를 겪었다. 이미지를 통째로 넣었더니 앱이 켜질 때마다 느려졌고, 목록을 넣었더니 일부만 골라 읽는 게 불가능했다.
State·Action·Reducer·Store와 단방향 흐름
SwiftUI로 화면 몇 개를 만들어 보면 상태 관리가 곧 벽이 된다. 작은 뷰는 @State로 충분하지만, 화면이 커지면 여러 뷰가 같은 값을 공유해야 하고 @ObservedObject, @EnvironmentObject가 섞여 들어온다. 문제는 값을 바꾸는 경로가 정해져 있지 않다는 데 있다. 어떤 버튼이 어디의 상태를 직접 고치고, 그 변화가 또 다른 상태를 건드리기 시작하면 "지금 이 값이 왜 이렇게 됐는지"를 되…
실전 구성 팁
기본기를 익히고 실제 화면을 만들기 시작하면 곧 다른 종류의 어려움을 만난다. 문법이 아니라 구조의 문제다. body 안에 뷰가 쌓이면서 수십, 수백 줄이 되고, 어디를 고쳐야 할지 눈에 안 들어온다. 조건 분기와 스택이 겹치면 들여쓰기만 깊어진다.
SwiftUI 의 diffing algorithm
SwiftUI 가 React 처럼 identifier 나 shortest-edit-script 없이도 대부분의 경우 view 변화를 모호함 없이 판별하는 이유. 비결은 정적 타입. TupleView<(Text, _ConditionalView<A, B>)> 자체가 구조를 드러내므로 같은 타입끼리만 비교하면 된다. Swift 코드로 직접 재구성해 본다.
상태로 화면을 제어한다
UIKit에서 얼럿을 띄우려면 present(alert, animated: true)를 호출했다. 화면 전환도, 액션 시트도 전부 "지금 띄워라"라고 명령하는 함수 호출이었다. SwiftUI로 넘어와 처음 얼럿을 띄우려 할 때 이 함수를 찾다가 막힌다. 그런 함수가 없기 때문이다.
SwiftUI 의 render loop
body 평가와 실제 화면 렌더링은 같지 않다. onAppear 가 왜 깜빡임을 만들지 않는지, preference key 기반 커스텀 layout 에서 body 가 두 번 평가돼도 왜 첫 번째는 화면에 닿지 않는지 — CFRunLoop 의 beforeWaiting observer 와 암묵적 CATransaction 의 관계까지 내려가 설명한다.
데이터 흐름의 두 원칙
선언형 감각을 잡고 나면 곧바로 막히는 지점이 있다. 상태를 담는 방법이 하나가 아니라는 것이다. @State, @Binding, @ObservedObject, @StateObject, @EnvironmentObject가 줄줄이 나오는데, 처음에는 무엇을 언제 써야 하는지 감이 안 온다. 아무거나 골라 쓰다가 화면이 갱신되지 않거나, 반대로 값이 자꾸 초기화되는 문제를 겪는다.
SwiftUI 에서 escaping closure 를 쓰지 말아야 하는 이유
커스텀 container view 를 만들 때 content 를 @escaping closure 로 받을지, 초기화 때 바로 평가해서 view 로 저장할지. 겉보기엔 closure 쪽이 더 효율적이지만 실제로는 @State 최적화와 memoization 이 동시에 깨진다. Rens Breur 가 왜 view 는 이미 함수인지부터 짚고 시작한다.
아키텍처 패턴과 선택 (MVC와 MVVM)
처음 몇 개 화면은 아키텍처를 몰라도 만들어진다. ViewController 하나에 화면 그리는 코드, 네트워크 호출, 데이터 가공, 화면 전환을 다 몰아넣어도 돌아가긴 한다. 문제는 그 화면이 커질 때 생긴다. 목록을 불러오고, 로딩 상태를 보여 주고, 검색을 걸고, 에러를 처리하고, 셀을 탭하면 상세로 넘어가는 화면 하나가 금세 수백 줄이 된다. 어디를 고쳐야 할지 찾는 데만 한참 걸리고, 한 곳을 고치면 엉뚱한 데가…
타깃액션과 네트워크 데이터로 화면 채우기
지금까지 화면을 그렸지만, 아직 사용자 입력을 받지 못했다. 버튼을 눌러도 아무 일이 안 일어난다. 입력을 코드에 연결하는 UIKit의 방식이 타깃액션(target-action)이다.
명령형에서 선언형으로
UIKit을 쓰다 SwiftUI로 넘어오면 가장 먼저 찾게 되는 것이 "화면을 다시 그리는 함수"다. UIKit에서는 그게 일이었기 때문이다. 카운트가 하나 늘면 countLabel.text를 새로 대입하고, 버튼을 잠가야 하면 button.isEnabled = false를 부른다. 데이터가 바뀔 때마다 그 변화를 화면에 반영하는 코드를 사람이 직접 써 준다. 이게 명령형 방식이다.
주요 컴포넌트와 MVC
지금까지 뷰를 붙이고, 화면을 넘기고, 배치를 잡는 뼈대를 봤다. 이제 그 위에 올릴 실제 부품을 다룬다. iOS 화면 대부분은 몇 가지 표준 컴포넌트의 조합이다. 글자를 보여 주는 UILabel, 탭을 받는 UIButton, 입력을 받는 UITextField, 그림을 그리는 UIImageView, 그리고 목록을 그리는 UITableView다.
앱은 어디서 시작하는가
C 프로그램에는 main 함수가 있고 거기서 실행이 시작된다는 걸 학부에서 배웠다. 그런데 처음 만든 iOS 프로젝트를 아무리 뒤져도 main이라 부를 만한 곳이 없었다. AppDelegate가 있고 SceneDelegate가 있는데, 둘 중 무엇이 먼저 도는지, 애초에 이 코드를 누가 부르는지가 불투명했다.
옵셔널과 안전한 nil 처리
다른 언어에서 null은 그냥 아무 참조에나 들어갈 수 있는 값이었다. 그래서 값이 없는데 접근하려다 터지는 사고를 늘 조심해야 했다. Swift를 처음 잡았을 때 변수 타입 뒤에 붙는 물음표가 낯설었다. String과 String?이 왜 다른 타입인지, nil을 넣으려면 왜 굳이 물음표를 붙여야 하는지 이해가 안 갔다.
메모리 관리와 ARC
가비지 컬렉터가 도는 언어를 먼저 접했던 터라, Swift에서 메모리를 언제 어떻게 지우는지가 처음엔 막막했다. free를 직접 부르는 것도 아닌데 메모리가 새지 않는다니 신기했고, 반대로 분명히 화면을 닫았는데 그 화면 객체의 deinit이 안 불리는 걸 보고는 당황했다. 다 쓴 객체가 메모리에 그대로 남아 있었다.
오토레이아웃과 코드 기반 UI
앞 장까지는 뷰의 위치를 frame에 좌표 숫자로 직접 넣었다. 작은 예제에선 잘 되지만, 실제 앱에서는 금방 깨진다. iPhone SE의 좁은 화면과 큰 Pro Max의 넓은 화면은 폭이 다르고, 가로로 돌리면 또 달라진다. "왼쪽에서 320만큼" 같은 절대 좌표는 특정 기기에서만 맞는다.
값 타입과 참조 타입
입문기에 가장 크게 데였던 지점이다. 데이터 묶음을 만들 때 struct로 만들지 class로 만들지 감이 없었다. 둘 다 프로퍼티와 메서드를 담을 수 있으니 겉보기엔 비슷했다. 그런데 복사해서 한쪽을 고쳤더니, 구조체는 다른 쪽이 멀쩡한데 클래스는 다른 쪽도 같이 바뀌었다. 같은 문법으로 만든 것 같은데 결과가 정반대라 한참을 헤맸다.
뷰컨트롤러 생명주기와 화면 전환
앞 장에서 화면은 뷰의 트리라고 했다. 그런데 그 트리를 누가 만들고, 데이터를 채우고, 화면이 나타나고 사라질 때 뒷정리를 하는가. 그 담당자가 UIViewController다. iOS에서는 화면 하나에 뷰컨트롤러 하나를 대응시키는 것이 기본이다. 로그인 화면, 목록 화면, 상세 화면이 각각 별도의 뷰컨트롤러다.
뷰와 뷰 계층
iOS 화면을 처음 뜯어볼 때 가장 도움이 됐던 관점은 이거였다. 눈에 보이는 모든 것은 결국 사각형 하나다. 버튼도 사각형, 레이블도 사각형, 이미지도 사각형이다. 이 사각형 하나하나가 UIView이고, 화면은 이 뷰들이 서로 겹치고 포개진 결과다.
함수는 1급 객체다
Swift를 배우기 전에 다뤘던 언어에서 함수는 그냥 "호출하는 것"이었다. 그런데 Swift 코드를 읽다 보니 함수를 변수에 대입하고, 다른 함수의 인자로 넘기고, 함수가 함수를 돌려주는 장면이 계속 나왔다. sorted(by:)에 정렬 기준을 통째로 넘기는 코드를 처음 봤을 때, 저기 들어가는 게 값인지 코드인지 헷갈렸다.