전체 글 77

@ObservableState와 @BindingState 충돌 트러블 슈팅

안녕하세요! 오늘은 SwiftUI와 TCA(The Composable Architecture)로 영화 검색 기능을 구현하던 중 만났던 컴파일 에러와 해결 과정을 공유해 보려고 합니다. 문제 발생영화 검색을 위한 MovieSearchFeature를 만들고 있었습니다. 검색어를 입력받을 searchText 상태를 만들고, SwiftUI View에서 양방향으로 데이터를 주고받기 위해 TCA에서 제공하는 @BindingState를 사용했습니다. @Reducerstruct MovieSearchFeature { @ObservableState struct State: Equatable { // ... @BindingState var searchText: String = "" } enum Ac..

iOS

Swift 6에 새로 등장한 InlineArray

몇주 전에 포스팅에서 Swift의 Array가 어떻게 동적으로 크기를 조절하며, 그 과정에서 발생하는 메모리 재할당과 성능 비용에 대해 알아봤었습니다. Array의 편리함 이면에는 힙(Heap) 메모리 할당과 복사라는 숨은 비용이 있다는 점을 기억하실 겁니다.그 숨겨진 비용을 줄여줄 수 있는 또다른 방법인 InlineArray에 대해 이야기해보려 합니다!InlineArray가 뭔가요?InlineArray는 이름 그대로, 데이터를 인라인(inline)으로 저장하는, 컴파일 시간에 크기가 고정된 배열입니다.가장 큰 특징은 기존 Array처럼 데이터를 힙 메모리에 별도로 할당하는 것이 아니라, InlineArray를 감싸고 있는 타입의 메모리 공간 안에 직접 값을 저장한다는 점입니다. 만약 함수 내의 지역 변..

iOS

Alamofire, Sendable 에러 트러블 슈팅

안녕하세요 오늘은 Alamofire를 함께 사용하면서 겪은 Main actor-isolated conformance... 에러를 해결해 나간 과정을 공유해 보려고 합니다. 1. 에러 발생 및 첫 시도Alamofire를 통해 데이터 통신을 시도하던 중 에러가 발생했습니다.func fetchExchangeRates(completion: @escaping (Result) -> Void) { // Alamofire로 GET 요청 AF.request(url, method: .get) .validate() // 상태 코드 200~299가 아니면 실패 처리 .responseDecodable(of: ExchangeRateResponse.self) { response in switch respon..

iOS

WWDC22: Eliminate data races using Swift Concurrency

이번 영상의 핵심 주제는 '데이터 레이스(Data Race)를 어떻게 없앨 것인가' 입니다. 이 문제를 해결하기위해 Swift Concurrency를 어떻게 활용할 수 있는지 알아봅시다.1. 기본 개념: 보트와 바다 바다: 동시성의 세계. 여러 일이 동시에 일어나는 예측 불가능한 공간입니다.보트: Task를 의미합니다. 각 보트는 자신만의 일을 처리하는 독립적인 일꾼입니다. 문제점: 보트들이 서로 소통하고 무언가를 주고받아야하는 상황이 생깁니다. 이때 주고받는 것이 안전하게 전달되는지가 중요한 문제입니다.2. 안전한 데이터 공유: 파인애플 vs. 닭 보트끼리 물건을 주고받는 상황을 생각해봅시다.파인애플 (Struct, Value Type)파인애플을 다른 보트에 주면, 그 보트는 완전히 새로운 복사본을 받..

Array의 메모리 재할당과 reserveCapacity(_:)

안녕하세요. 최근에 코딩 테스트 문제를 풀다가 시간초과 때문에 헤맨 경험이 있습니다. 분명 알고리즘의 시간 복잡도는 문제의 제한 사항을 충분히 만족한 것 같은데 계속해서 시간 초과가 뜨는 상황이었죠. 문제는 너비 우선 탐색(BFS)을 사용하는 그래프 문제였고, 수만 개의 노드를 큐에 넣고 빼는 로직이었습니다."내 BFS 로직이 잘못됐나? 포인터 관리가 비효율적인가?" 등등 고민을 하다가 해결방법을 찾아냈습니다. 원인은 알고리즘이 아니라, 너무나도 당연하게 사용하던 Array의 append 때문이였습니다. 그리고 그 해결책은 reserveCapacity라는 메서드 였습니다.Swift 배열의 메모리 재할당우리는 보통 Swift에서 가변 크기의 배열이 필요할 때 아무 생각 없이 빈 배열을 선언하고 append..

iOS

배열을 순회하며 수정할 때 for-in으로 배열을 직접 순회하면 안되는 이유

안녕하세요. 오늘은 알고리즘 코딩 테스트 문제를 풀면서 겪은 문제상황과 그 이유를 알아보는 과정에서 알게된 내용을 이야기해보려고합니다. DFS 문제를 풀고 예제 케이스를 돌려보는데 예상과 다르게 계속 실패가 떠서 원인을 찾기 위해 print를 찍어보며 디버깅을 했습니다. 분명히 visited[1] = true로 값을 바꿨는데 바로 다음 반복문에서 visited의 1번 인덱스가 여전히 false로 읽히는 것을 보았습니다.문제의 코드는 그래프의 모든 노드를 방문했는지 확인하고 방문 여부에 따라 함수를 실행하는 코드였습니다.var visited = Array(repeating: false, count: n)func dfs(computer: Int) { visited[computer] = true for (..

iOS

Core Data 사용해보기

안녕하세요! 오늘은 앱 내부에 데이터를 저장해야 할 때 체계적으로 데이터 관리를 할 수 있는 Core Data를 사용해본 경험을 공유해보겠습니다. 시작하기 전에: CRUD란?개발을 하다 보면 CRUD라는 용어를 정말 자주 듣게 되죠. 데이터베이스의 기본 개념으로, 각각 다음을 의미합니다.Create: 데이터 생성Read: 데이터 읽기Update: 데이터 수정Delete: 데이터 삭제간단히 '전화번호부 앱'을 예로 들면, 새로운 연락처를 추가(Create)하고, 저장된 연락처를 조회(Read)하며, 번호나 이름을 수정(Update)하고, 필요 없는 연락처를 삭제(Delete)하는 모든 과정이 바로 CRUD입니다.이번 글에서는 Apple의 프레임워크인 Core Data를 사용해서 기기 내부에 데이터를 저장하..

iOS

그래프 탐색 BFS와 DFS

오늘은 그래프를 탐색하는 대표적인 두 가지 방법, BFS(너비 우선 탐색)와 DFS(깊이 우선 탐색)를 Swift 코드로 함께 살펴보겠습니다. DFS는 재귀와 스택 두 가지 방식으로 구현했는데 어떤 상황에 어떤 방식을 쓰는 게 유리한지 장단점까지 이야기해보도록 하겠습니다!BFS (너비 우선 탐색)BFS는 이름 그대로 넓게 탐색하는 방식입니다. 시작점에서 가장 가까운 노드들부터 방문하고 그다음 가까운 노드들 순서로 퍼져나가듯 탐색을 진행합니다.BFS의 핵심은 "먼저 찾은 노드를 먼저 탐색한다"는 것이고, 이걸 가능하게 해주는 자료구조가 바로 큐(Queue)입니다. 선입선출(FIFO) 구조인 큐 덕분에 탐색 순서가 보장되죠.그래서 BFS는 주로 두 노드 사이의 최단 경로를 찾거나, 연결된 모든 노드를 순회할..

iOS

WWDC21: Protect mutable state with Swift actors

WWDC21 세션 Protect mutable state with Swift actors에서는 왜 데이터 레이스가 발생하는지, 그리고 Swift에 새로 등장한 actor 타입과 Sendable, @MainActor를 활용해 어떻게 안전한 비동기 코드를 만들 수 있는지에 대해서 설명합니다.데이터 레이스는 왜 위험한가데이터 레이스(Data race)는 두 개 이상의 스레드가 같은 가변 상태를 동시에 읽거나 쓸 때 발생합니다. 하나라도 쓰기 연산이 개입한다면 결과는 예측할 수 없기 때문에 디버깅도 어려워집니다.세션에서는 Counter 예제를 통해 단순한 클래스로도 데이터 레이스가 얼마나 쉽게 만들어지는지를 보여줍니다. 아래와 같이 두 개의 Task.detached가 동일한 Counter 인스턴스를 건드리면 결..

TCA 사용해서 장바구니 동작 완성하기

어제는 Cart 버튼을 누르면 CartView가 시트로 열리도록 TCA 흐름을 정리했습니다. 오늘은 그 시트에 데이터를 넣고 사용자가 원하는 대로 장바구니를 조작할 수 있게 만드는 과정을 기록해 보겠습니다. 버튼에서 넘겨준 선택 상품을 장바구니에서 확인하고 수량 증감/단품 삭제/전체 비우기/결제 완료 알림까지 연결하는 것이 목표입니다.1. CartFeature로 상태 넘기기1) @Shared로 ProductList와 Cart 상태 공유선택된 상품을 담은 selectedProducts는 이미 @Shared로 선언되어 있어서 Cart 시트에서도 같은 selectedProducts배열을 바라볼 수 있었다. CartFeature의 state에 selectedProducts 값을 그대로 주입하면, 변경된 항목이 ..

iOS

TCA 사용해서 버튼으로 시트 띄우기

새로운 프로젝트를 TCA(Composable Architecture)로 진행하면서 장바구니 버튼을 누르면 장바구니 화면을 시트로 띄우는 요구사항을 만나게 되었다. SwiftUI만 쓸 때는 @State랑 sheet(isPresented:) 정도면 끝났는데, TCA를 처음 쓰다 보니 상태는 어디에 두고, 액션은 어떻게 전달하고, 시트는 또 어떻게 열어야 하는지 막막했다. 이 글은 Cart 버튼 → CartView를 시트로 띄우기 흐름을 만들기까지의 시행착오와 해결 과정을 적어둔 기록이다.일단 시트로 열리는 것은 확인했는데 당연히 구조적으로 개선해야할 부분은 있을거라고 생각해서 이 글을 작성해두고 나중에 다시 보고 수정을 하면서 어떻게 생각했었는지, 어떤 부분을 바꾸면 좋을지 고민해볼 수 있을 것 같다. (..

iOS

TCA사용해서 앱 만들기

오늘은 어제 익힌 TCA 개념을 바탕으로 간단한 카운터 앱을 만들어보면서 사용법을 익히고 정리해보겠습니다.아래 TCA 튜토리얼 문서를 보면서 따라서 진행했습니다!https://pointfreeco.github.io/swift-composable-architecture/main/tutorials/composablearchitecture/01-01-yourfirstfeature Documentation pointfreeco.github.io ReducerReducer는 특정 기능(Feature)에 대한 모든 비지니스 로직, 상태변화, 사이드 이펙트를 관리하는 곳입니다.//CounterFeature.swiftimport ComposableArchitecture@Reducerstruct CounterFeatur..

iOS

TCA란?

TCA(The Composable Architecture)는 앱의 상태를 일관되고 예측 가능하며 테스트하기 쉽게 관리하기 위한 Swift 라이브러리입니다. 복잡하게 얽힌 데이터 흐름을 명확한 단방향 흐름으로 정리하여, 앱이 커져도 유지보수하기 쉬운 구조를 만들어줍니다.TCA의 핵심 개념TCA는 5가지 주요 구성 요소로 이루어져 있습니다. State (상태)특정 기능을 표현하는 데 필요한 모든 데이터가 유일하게 저장되는 곳입니다. 예를 들어, 카운터 화면이라면 현재 숫자, 로딩 중인지 여부 등이 모두 State에 포함됩니다.비유: 건물의 설계도와 같습니다. 설계도만 보면 건물의 모든 구조와 상태를 알 수 있듯, State만 보면 현재 화면이 어떤 상태인지 완벽하게 파악할 수 있습니다.Action (액션)앱..

iOS

동적으로 UI 추가하기

오늘은 서버에서 받아온 데이터의 개수에 따라 버튼이나 뷰를 다르게 보여줘야 할 때, 어떻게 UI를 구성해야 할지 알아보도록 하겠습니다.UIStackView를 활용해서 동적 UI를 어떻게 구현할 수 있는지 알아보겠습니다.동적으로 버튼 생성하기아래 코드는 특정 count 값을 받아서 그 개수만큼 번호가 매겨진 버튼을 생성하고, UIStackView에 추가하는 함수입니다. 하나씩 뜯어보며 이해해 봅시다.// 버튼들을 담을 UIStackViewlet seriesButtonStackView: UIStackView = { let stackView = UIStackView() stackView.axis = .horizontal // 가로 정렬 stackView.spacing = 8.0 stack..

iOS

WWDC21: Meet AsyncSequence

오늘은 Swift Concurrency의 AsyncSequence에 대해 알아보려고 합니다. WWDC 세션 내용을 바탕으로 AsyncSequence가 무엇인지, 어떻게 사용하는지, 그리고 직접 만드는 방법까지 정리해보았습니다.AsyncSequence란 무엇일까? *Sequence는 Swift에서 순서를 가진 값들의 목록을 나타내는 프로토콜 입니다. (Array, Dictionary, Set)AsyncSequence는 비동기적으로 동작하는 Sequence입니다. AsyncSequence는 시간이 지남에 따라 값을 하나씩 비동기적으로 생성하고 전달합니다. async와 AsyncSequence의 차이async 함수: await을 통해 하나의 값을 비동기적으로 반환하고 종료됩니다.AsyncSequence: a..

UIStackView의 타입과 case let 패턴

오늘은 iOS 개발에서 UIStackView 내부에 동적으로 추가된 여러개의 버튼들을 제어해야할 때 마주한 문제와 그 과정에서 사용했던 case let이라는 패턴에 대해서 공유해보려고 합니다. 당시의 문제 상황은 아래와 같습니다. UIStackView가 하나 있습니다.viewDidLoad()나 특정 시점에서 API로부터 받은 데이터 개수만큼 UIButton을 만들어 이 스택뷰에 addArrangedSubview()로 추가합니다.사용자가 여러 버튼 중 하나를 탭하면, 탭한 버튼은 활성화 상태 (파란색 배경)로 변경됩니다.동시에 나머지 모든 버튼은 비활성화 상태 (회색 배경)로 돌아가야 합니다.동적으로 생성된 버튼들을 탭할 때마다 활성 / 비활성화를 해주기 위해서 스택뷰를 for문으로 탐색하면서 버튼의 태그..

iOS

Swift Concurrency: Sendable 프로토콜

오늘은 Sendable이 무엇인지, 왜 중요한지에 대해서 알아보도록 하겠습니다.Sendable 프로토콜, 왜 필요할까요?동시성 프로그래밍의 가장 큰 문제는 바로 데이터 레이스(Data Race)입니다. 데이터 레이스는 여러 스레드가 공유된 데이터에 동시에 접근하고, 그중 하나 이상이 데이터를 수정하려고 할 때 발생합니다. 이로 인해 앱이 비정상적으로 종료되거나 예측 불가능한 결과를 초래할 수 있게됩니다.이전에는 이러한 문제를 해결하기 위해 rock이나 세마포어와 같은 메커니즘을 사용했지만, 코드가 복잡해지고 데드락의 위험이 있었습니다.Sendable 프로토콜은 바로 이 데이터 레이스 문제를 컴파일 시점에 방지하기 위해 탄생했습니다. 어떤 타입이 Sendable을 준수한다는 것은, 해당 타입의 값을 동시성..

iOS

Repository Pattern

오늘은 Repository Pattern으로 데이터 로직 분리하는 방법을 알아보겠습니다.UIKit으로 개발하다 보면 UIViewController 하나에 네트워크 통신, 데이터 파싱, UI 업데이트 등 온갖 코드가 뒤섞여 코드가 수백 줄이 넘어가는 경우가 종종 생깁니다. 이런 코드는 유지보수도 어렵고, 테스트는 더더욱 힘들어집니다.이 문제를 해결할 수 있는 방법중 하나인 리포지토리 패턴(Repository Pattern)에 대해 알아보고, 실제 코드에 적용해 보겠습니다.Repository Pattern리포지토리 패턴을 한마디로 정의하면 '데이터 소스에 대한 추상화 계층'입니다.쉽게 비유해 볼게요. 우리가 도서관에서 책을 찾는다고 생각해 봅시다. 우리는 사서에게 가서 "해리포터 책 주세요"라고 요청합니다...

iOS

링크드리스트 만들기

오늘은 자료구조중에 링크드리스트를 만들어 보겠습니다. 먼저 링크드 리스트(Linked List)는 여러 개의 노드가 순차적으로 연결된 형태를 띠는 자료구조입니다. 각 노드는 데이터와 다음 노드를 가리키는 포인터로 구성됩니다. 배열처럼 데이터를 순서대로 저장하지만, 메모리상에 연속적으로 위치하지 않는다는 차이점이 있습니다.먼저 링크드리스트를 만들기 위해 노드를 생성해줍니다.class Node { var value: Element var next: Node? init(value: Element, next: Node? = nil) { self.value = value self.next = next }}어떤 타입의 데이터를 받을지 모르기 때문에 제네릭으로 타입을 지정하였습니다. 그리고 링크드리..

CS

WWDC21: Explore structured concurrency in Swift

이번 글에서는 Swift의 동시성 개념 중에서도 구조적 동시성에 대한 wwdc 세션의 내용을 살펴보겠습니다.구조적 동시성(Structured Concurrency)이란?우리는 if-else, for, while 같은 구조적 프로그래밍(Structured Programming) 구문에 익숙해져있고 구조적 프로그래밍은 코드를 작성하고 읽기 쉽게 도와줍니다. 이 구문들은 예측 가능한 제어 흐름과 명확한 스코프를 가지고있습니다. 코드는 위에서 아래로 흐르고, 변수의 생명주기는 스코프를 벗어나지 않는 것이죠.구조적 프로그래밍의 장점을 비동기 프로그래밍에서 사용할 수 있도록 만들어준 것이 async/await입니다. async/await의 핵심: 태스크는 그대로, 실행만 잠시 멈춤여기서 매우 중요한 점이 있습니다..