본문 바로가기

전체 글233

Swift: 동시성 코드 작성 노하우 몇 가지의 동시성 관련 코드 작성 노하우 비동기 작업 데이터와 UI 데이터 분리아래와 같은 개별 파일을 압축하는 코드가 가진 문제점. Published 데이터는 메인 스레드에서 갱신되어야 하는 데이터이고, logs는 동시성 문제를 방지하기 위해 한 번에 한 스레드만 접근해야만 한다.하지만 logs를 생성하는 작업은 메인 스레드에서 수행될 필요가 없다. 아래 코드의 문제는 무거운 작업의 메인 스레드 수행으로 UI 업데이트가 블로킹됨 @MainActorclass CompressionState: ObservableObject { @Published var files: [FileStatus] = [] var logs: [String] = [] func update(url: URL, progres.. 2025. 12. 2.
WWDC25: Swift 동시성 사용하기 링크https://developer.apple.com/kr/videos/play/wwdc2025/268/ 서론앱은 모든 코드를 메인 스레드에서 실행.Swift 동시성을 통해 앱은 여러 작업을 동시에 수행할 수 있음동시성 모델은 actor, task 등 메인 액터@MainActor을 사용해 메인 액터 지정.코드가 메인 스레드에서만 실행됨을 보장, 메인 액터 데이터는 해당 스레드에서만 액세스 가능메인 스레드 코드 보호를 통해 공유 상태에서 자유로운 액세스를 보장함Xcode 26.0 부터는 기본적으로 @MainActor 격리성을 사용앱 모듈과 UI 상호 작용에 초점을 맞춘 모듈에서 사용함 동시 함수@concurrent 속성을 적용하면 Swift가 백그라운드에서 함수를 실행하도록 지시 비격리 코드@concur.. 2025. 11. 30.
Swift: CORS Error 개요CORS(Cross-Origin Resource Sharing)란 출처가 다른 리소스 요청/응답을 허용/거부할 수 있도록 하는 정책브라우저는 기본적으로 다른 서버를 신뢰하지 않는다. 그렇기에 브라우저가 내 서버가 아닌 다른 서버에 대한 요청/응답을 차단한다. 예시 상황내 서버 "https://myshop.com/page1"가 있을 때,"https://myshop.com/page2"는 내 서버이기 때문에 신뢰함"https://othershop.com/page2"는 다른 서버로 신뢰하지 않음 -> 응답/요청 차단 CORS 사용 이유토큰, 쿠키와 같은 민감 사용자 정보는 브라우저에 저장되는데, 이 정보를 탈취하면(CSRF, XSS) 보안 이슈가 생긴다. 그렇기에 다른 서버에 대한 요청/응답을 브라우저 단.. 2025. 11. 17.
UIKit: 중첩 스크롤 뷰 구현하기 개요중첩 스크롤 뷰는 인스타그램 프로필이나 웹툰, 웹소설 앱과 같은 화면에서 흔히 볼 수 있는 UI 패턴이다.상단의 정보 뷰가 스크롤에 따라 사라지고, 탭 바가 상단에 고정되며, 그 아래에 컬렉션 뷰가 표시되는 구조다. (카카오 페이지 웹소설 상세 페이지)구현 영상 구조 설계계층 구조UIViewController└── mainScrollView (메인 스크롤 뷰) └── contentStackView (수직 스택 뷰) ├── infoView (정보 뷰) ├── stickyTabContainer (탭 컨테이너) └── collectionView (컬렉션 뷰) 메인 스크롤 뷰 안에 모든 컨텐츠가 들어가며, 스택 뷰를 사용해 수직으로 배치한다.핵심은 collecti.. 2025. 10. 29.
UIKit: 원형 컨텍스트 메뉴 라이브러리 링크GitHub: https://github.com/songmoro/CircularContextMenuCocoaPods: https://cocoapods.org/pods/CircularContextMenu샘플 프로젝트: https://github.com/songmoro/CircularContextMenuExample 개요프로젝트에서 사용하는 원형 컨텍스트 메뉴를 기반으로 배포한 라이브러리Long Press와 Tap 두 가지 제스처를 지원 프로젝트 내에서는 SnapKit을 사용하긴 하나, 외부 의존성을 제거한 버전으로 교체 특징커스터마이징 가능한 뷰 하이라이팅 효과버튼 원형 배치요구사항iOS 16.0+Swift 5.9+ 설치SPMdependencies: [ .package(url: "https://.. 2025. 10. 21.
RxSwift와 Diffable DataSource에서의 ModelSelected 구현 서론Rx에서는 선택한 셀의 IndexPath를 반환하는 ItemSelected, 선택한 셀의 IndexPath를 기반으로 데이터 모델을 반환하는 ModelSelected 메서드를 제공한다. ItemSelected는 collectionView(:didSelectItemAtIndexPath:), tableView(:didSelectRowAtIndexPath:)를 delegate를 래핑 한 메서드이고, ModelSelected는 그러한 ItemSelected를 한 번 더 래핑 한 메서드이다. 이 두 메서드를 사용해서 뷰 컨트롤러에 Delegate를 채택하지 않아도, 셀이 선택되면 해당 셀에 대한 정보(인덱스패스, 데이터)를 조작할 수 있게 된다. 기본적인 itemSelected, modelSelected 사.. 2025. 9. 10.
UIKit: DataSource, DiffableDataSource 학습 이유컬렉션 뷰/테이블 뷰를 데이터 기반으로 동작하고자 했었음예. item 변수에 프로퍼티 옵저버를 통해 데이터가 바뀌면 자동으로 reloadData() 수행 하지만, 프로퍼티 옵저버를 통해 뷰를 관리하니 애니메이션이 존재하는 상황에서 크래시가 발생이유는 제스쳐를 통해 셀 삭제 등을 수행할 때사용자가 셀 삭제()인덱스패스를 기반으로 데이터 remove데이터가 변경되니 reloadData() 트리거데이터에는 존재하지 않는데, 뷰에는 데이터가 존재하는 상태에서 인덱스 오류가 발생앱 크래시 이러한 이유에서 인덱스 기반의 DataSource가 아닌 식별자 기반의 DiffableDataSource를 학습하기로 함 DataSource데이터를 관리하고, CollectionView/TableView에 셀을 제공하기.. 2025. 8. 30.
정렬과 중복 제거가 가능한 모델 검색 기록과 같은 모델은 시간 순에 따라 정렬이 가능해야 하며, 동시에 중복 검색을 방지를 위한 Hashable이 필요할 수 있다.struct Keyword: Codable { let text: String let date: Date init(text: String, date: Date = Date()) { self.text = text self.date = date }} Keyword가 정렬, Set을 지원하기 위해선 Hashable과 Comparable을 채택할 수 있다.extension Keyword: Hashable { func hash(into hasher: inout Hasher) { hasher.combine(text) .. 2025. 8. 30.
UIKit: UITextView UITextView커스텀해서 Text 표시/편집 가능여러 줄의 텍스트 표시 가능attributedText 속성으로 여러 텍스트 스타일 지원 키보드 관리텍스트 뷰를 탭하면 첫 번째 responder가 되어 키보드 표시resignFirstResponder() 메시지를 첫 번째 responder인 텍스트 뷰로 전달해 키보드 숨기기키보드 모양은 UITextInputTraits 프로토콜 키보드 노티피케이션keyboardWillShowNotificationkeyboardDidShowNotificationkeyboardWillHideNotificationkeyboardDidHideNotification 키보드 숨기기 옵션keyboardDismissModeDo not dismissDismiss on dragDismis.. 2025. 8. 30.
UIControl 이벤트를 Combine 퍼블리셔로 만들기 문제 상황처음에는 UITextField().publisher(for:) 같은 방식으로 Combine과 연결하려 했지만, 실제로는 값이 한 번만 방출되고 실시간으로 텍스트 변화가 반영되지 않음 이를 해결하기 위해 NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification)와 같은 방법을 쓸 수도 있지만, 데이터 흐름이 분산되어 추적하기 어렵다고 판단 결국, UIControl 자체를 Combine 퍼블리셔로 래핑하는 방법을 선택 참고 링크의 코드를 참고해 사용했지만, 실 사용시 아래와 같이 UIControl 타입을 UIButton, UITextField로 타입 캐스팅해줘야 하는 불편함이 존재nicknameTextField.p.. 2025. 8. 20.
struct Error vs. enum Error 기존 패턴보통 error를 반환하기 위해 아래와 같이 enum에 error를 채택하여 사용한다. 이 방식은 간단하고 가독성이 좋지만, 케이스마다 연관값을 추가하다 보면 복잡해진다.더해서, 상황별로 메타데이터를 표현하기엔 한계가 있다. ErrorError는 단순히 Sendable을 상속하는 프로토콜이다.즉, 열거형 뿐 아니라 struct, class도 채택할 수 있어, 상황에 맞게 유연하게 에러를 모델링할 수 있음protocol Error : Sendable 아래는 메타데이터(line, column)을 포함하는 에러의 예struct XMLParsingError: Error { enum Kind { case invalidCharacter case mismatchedTag .. 2025. 8. 18.
버튼을 통해 IndexPath 전달하기 테이블뷰 안의 컬렉션뷰, 그리고 IndexPath개발을 하다 보면 테이블 뷰 안에 또 다른 뷰가 들어가는 경우가 왕왕 있다.대표적인 예시가 테이블 뷰 셀 안에 컬렉션 뷰를 넣는 것인데, 가로 스크롤 리스트 UI를 위해 사용했다.이 경우에 생긴 문제가 하나 있는데, 컬렉션 뷰 셀을 선택했을 때, 원래 어떤 테이블 뷰 섹션/셀에 속하는지를 알아내기 어렵다는 점이다 왜 IndexPath가 꼬일까?테이블 뷰는 IndexPath(row:section:)를 가지고 있고, 컬렉션 뷰도 IndexPath(item:section:)를 가지고 있다. 즉, 테이블 뷰 기준의 IndexPath, 컬렉션 뷰 기준의 IndexPath이렇게 두 개의 계층이 겹치면서 “내가 선택한 셀이 테이블 뷰 몇 번째 섹션에 속하는지”를 알기가.. 2025. 8. 18.
UIKit: TableView, CollectionView register, dequeue 셀 식별자 문제점테이블 뷰/컬렉션 뷰에서 셀을 사용할 때 register, dequeue 하는 과정이 필요하다.이때 셀 식별자를 사용하는데, String 값으로 관리하다 보니 "Cell"이라는 식별자를 "CEll"로 입력하거나 "Cel"로 입력하는 등 단순 실수로 인해 문제가 발생할 수 있다. import UIKitimport SnapKitclass Cell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) backgroundColor = .systemBrown.. 2025. 7. 31.
UIKit: 테이블 뷰 셀 내부에 원형 뷰 만드는 방법 뷰의 cornerRadius 값을 너비 혹은 높이의 절반으로 설정하면 원형으로 만들 수 있다. class ViewController: UIViewController { lazy var view1: UIView = { let view = UIView() view.backgroundColor = .brown view.bounds = CGRect(x: 0, y: 0, width: 100, height: 100) self.view.addSubview(view) return view }() override func viewDidLoad() { super.viewDidLoad() view1.layer.corner.. 2025. 7. 28.
UIKit: Layout Cycle Layout Cycle: View Controller를 표시할 때 내부의 View가 그려지는 과정Layout CycleConstraintsAutolayout의 제약을 갱신순서: Subview -> SuperviewLayout제약을 바탕으로 레이아웃 수행순서: Superview -> SubviewDrawCore Graphics를 사용해 View를 drawLayout Cycle 단계 별 수행 메서드ConstraintsupdateConstraints()LayoutlayoutSubviews()Drawdraw(:)updateConstraints(), layoutSubviews(), draw(:)는 호출 비용이 비싸기 때문에 직접 호출하면 안 됨그렇기에 업데이트를 명령하기 위한 메서드를 사용해야 함Constrain.. 2025. 7. 27.
UIKit: Render Loop Render Loop뷰가 그려지는 세부 단계렌더 루프는 병렬적으로 수행크게 3단계App, Render server, On the display세부적으로 5단계AppEventCommitRender serverRender prepareRender executeOn the displayDisplayAppEvent: 화면 변경을 요구하는 이벤트 처리Commit: 레이아웃 배치, drawRender serverRender prepare: UI를 GPU에 그릴 준비Render execute: 실제 화면을 그리는 단계On the displayDisplay: 화면에 나타내는 단계 참고https://developer.apple.com/kr/videos/play/tech-talks/10855https://luke-kon.. 2025. 7. 27.
UIKit: Frame, Bounds 뷰 객체는 frame, bounds, center 속성을 사용해 크기와 위치를 추적frame: 슈퍼 뷰를 기준으로 뷰의 크기와 위치를 지정하는 프레임 사각형bounds: 본인을 기준으로 뷰의 크기와 위치를 지정하는 경계 사각형center: 슈퍼 뷰 좌표계에서의 뷰의 중심점 사용 목적center, frame: 현재 뷰의 지오메트리 조작런타임 중 뷰 계층 구축뷰의 위치/크기 변경-> frame은 회전하면 크기 변함-> center는 위치만 변경하는 경우 사용bounds: drawing을 위해 사용-> 회전해도 본인 크기 (보라색 사각형 기준) 참고https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/ViewPG_.. 2025. 7. 20.
UIKit: Corner Radius 설정 전체 둥근 모서리 설정 위와 같이 rectangle이라는 뷰가 있을 때 둥근 모서리로 설정하고 싶다면, cornerRadius를 설정// 둥근 모서리를 그릴 때 사용하는 지름 값var cornerRadius: CGFloat { get set }rectangle.layer.cornerRadius = 24 특정 모서리만 둥글게 설정특정 모서리만 둥글게 하고 싶다면, cornerRadius & maskedCorners 설정rectangle.layer.cornerRadius = 24rectangle.layer.maskedCorners = [.layerMinXMinYCorner]// .layerMinXMinYCorner: minX, minY 좌상단 // .layerMaxXMinYCorner: maxX, min.. 2025. 7. 20.
UIKit: clipsToBounds vs. masksToBounds clipsToBounds서브 뷰가 뷰보다 크면, 뷰를 넘어서는 범위까지 표현된다.ex)부모 뷰: width = 50, height = 50자식 뷰: width = 100, height = 100 이때, 뷰의 clipsToBounds를 true로 설정하면 서브 뷰를 뷰의 크기에 맞춰 자를 수 있음// 서브 뷰를 뷰의 bounds에 국한되게 설정var clipsToBounds: Bool { get set }rectangle.layer.cornerRadius = 12rectangle.clipsToBounds = true masksToBoundsclipsToBouds는 서브 "뷰"에 대해 동작하는 반면, masksToBounds는 서브 "레이어"에 대해 동작하는 속성rectangle.layer.cornerRa.. 2025. 7. 20.
Swift: class func, static func, func class func, static func: 클래스의 타입 메서드func: 클래스의 인스턴스 메서드class func vs. static func- 상속하여 override가 필요한 경우: class func- override가 필요하지 않은 경우: static funcfinal class func vs. static func- 취향 차이 https://sujinnaljin.medium.com/swift-class-func-vs-static-func-7e6feb264147 2025. 7. 20.
UIKit: 레이아웃 우선순위 contentHuggingPriority뷰가 고유 크기(intrinsic size) 보다 커지는 걸 거부하는 우선순위우선순위와(UILayoutPriority) 축을(NSLayoutConstraint.Axis) 지정 가능제약 조건 기반 레이아웃(constraint-based layout) 시스템에서 고유 크기보다 커져야 하는 상황에 이 우선순위를 사용// 뷰의 contentHuggingPriority 반환func contentHuggingPriority(for axis: NSLayoutConstraint.Axis) -> UILayoutPriority// 뷰의 contentHuggingPriority 설정func setContentHuggingPriority(_ priority: UILayoutPriorit.. 2025. 7. 20.
Swift: print가 값을 출력하는 방법 func print( _ items: Any..., separator: String = " ", terminator: String = "\n")print(Character("G"))print("S")print(3.14) 어떤 값이든 print를 사용하면 값이 출력된다.모든 타입의 항목을 받아 텍스트 표현으로 출력한다고 하는데,커스텀 구조체/클래스에서도 CustomStringConvertible을 채택하지 않았는데, 값이 출력되는 원리가 궁금해서 찾아봤다. print는 내부적으로 String(describing:)을 호출한 결과와 같다고 한다.그렇다면 String(describing:)은 무엇이냐? struct Point { let x: Int, y: Int}let p = Point.. 2025. 7. 13.
UIKit: Nib, Xib init(coder:)스토리보드/Nib을 통해 뷰를 로드할 때, 커스텀을 추가해서 초기화하고 싶을 때 사용 init(frame: CGRect)코드 기반으로 뷰를 로드할 때 사용하는 초기화기 ArchiveObject Graph를 저장하기 위한 방법 중 하나 XibXML Interface BuilderIB를 통해 구성한 정보를 단순 나열한 파일아무런 구조적 상호관계 없는 레코드들이 모인 파일XML 형식 NibXib가 컴파일하여 메모리에 로드할 때 사용하는 파일Object Graph가 아카이빙된 형태바이너리 형식 Object Graph뷰 요소와 같은 객체들의 관계를 나타내는 그래프 Placeholders빠져있는 요소를 연결해주는 역할 Placeholders - File's Owner앱 코드와 Nib 파일을 연.. 2025. 7. 13.
UIKit: View, View Controller Lifecycle UIKit은 뷰를 표시하기 전에 뷰 컨트롤러와 뷰를 구성할 수 있는 몇 가지 기회를 제공스토리보드에서 뷰 컨트롤러의 인스턴스를 생성하면 init(coder:) 메서드를 사용하여 해당 객체 생성 뷰 컨트롤러를 표시하는 단계(이때 뷰 컨트롤러를 사용하기 위한 일회성 설정 수행)init(coder:)으로 각 뷰 생성뷰를 뷰 컨트롤러의 액션 및 아울렛에 연결각 뷰와 뷰 컨트롤러의 awakeFromNib() 호출뷰 컨트롤러의 뷰 속성에 뷰 계층 할당뷰 컨트롤러의 viewDidLoad() 호출이때 스토리보드의 일부가 아닌 추가 뷰를 생성 및 구성 뷰가 화면에 나타날 때(이때 뷰의 레이아웃 업데이트)viewWillAppear 호출하며 전환 시작 알림계층 구조에 뷰 추가뷰 컨트롤러, 뷰의 trait collecti.. 2025. 7. 13.