목록전체 글 (185)
seong
알림 목록에 조회 기간 필터를 붙였다. 시트에서 시작일, 종료일을 고르고 [적용]을 누르면 그 기간의 알림만 다시 불러온다. 처음엔 [적용] 버튼에서 Task { await vm.load() }를 부르면 끝이라고 생각했다. 그런데 기간을 빠르게 두 번 바꾸면 요청 두 개가 동시에 나가고, 늦게 도착한 옛 응답이 최신 결과를 덮어쓴다. 같은 작업에서 목록을 2026-10-15 (목) 같은 날짜 헤더로 묶는 일도 했다. 재조회는 버튼이 아니라 .task(id:)에 맡겼고, 날짜 섹션은 저장하지 않고 alerts에서 계산했다. 1. 버튼의 Task 대신 .task(id:)버튼에서 Task { }를 띄우는 것과 .task(id:)에 맡기는 것은 이전 요청을 다루는 방식이 다르다. 구분버튼에서 Task { }...
앱을 다시 켤 때마다 서비스부터 골라야 했다. 담당자는 보통 서비스 한두 개만 보는데, 켤 때마다 같은 서비스를 다시 고르는 한 단계가 끼었다. 그래서 앱이 마지막으로 본 서비스의 알림 목록에서 바로 시작하게 만들기로 했다. 첫 실행이거나 저장된 서비스가 사라졌으면 서비스 선택 화면을, 서비스가 1개뿐이면 선택 화면 없이 바로 목록을 보여준다. 지금까지 선택된 서비스는 RootView의 @State private var selectedServiceId였다. 화면 하나의 지역 상태다. 그런데 저장도 해야 하고, 나중에 푸시를 눌러 들어오면 화면 밖에서 선택 서비스를 바꿔야 한다. 이번에 정해야 했던 것은 이 값을 어디에 두고 어떻게 화면에 내려줄지였다. 1. 선택 서비스는 화면이 아니라 App 계층에가른..
알림 목록 화면을 처음 만들었을 때는 데이터가 잘 올 때만 화면이 멀쩡했다. 로딩 중에도 빈 화면, 알림이 0개여도 빈 화면, 네트워크가 끊겨 요청이 실패해도 빈 화면이었다. 세 경우가 똑같이 생겨서, 화면만 봐서는 기다려야 하는지 다시 시도해야 하는지 알 수 없었다. PRD에는 이미 상태별 표시 규칙이 있었다. 첫 로드 중에는 스켈레톤, 실패하면 전체 화면 에러와 [다시 시도], 새로고침이 실패하면 기존 목록을 유지하고 상단 배너, 다음 페이지가 실패하면 목록 끝에 [다시 시도] 행, 상세 조회가 404면 "삭제되었거나 없는 알림입니다". 이 규칙을 화면마다 따로 만들면 금방 어긋난다. 그래서 공통 상태를 enum 하나로 묶었는데, 막상 묶으려고 보니 "새로고침 실패"나 "404"도 같은 enum에 넣..
처음 기획에는 "알림 끄기는 iOS 설정에 맡긴다"였다. 그런데 iOS 설정으로는 앱 전체만 끌 수 있다. 특정 서비스 알림만 끄고 싶다는 요구는 채울 수 없었다. 그래서 하단 탭을 홈, 설정 두 개로 나누고, 설정 탭에 서비스별 토글을 두기로 했다. 만들면서 걸린 건 세 가지였다. 이 값은 어디에 저장하나, 기본값을 켜짐으로 하려면 어떻게 읽나, 그리고 private(set) 배열에 Toggle을 어떻게 묶나. 이 글은 그 고민을 정리한 것이다. 1. 결론부터서버의 서비스 목록과 기기의 on/off 값은 UseCase에서 합치고, Toggle은 Binding(get:set:)으로 ViewModel 메서드에 연결한다. 구분서비스 목록on/off 값출처서버 (GET api/services)기기 (User..
더미 데이터로 그리던 화면에 서버 데이터를 붙이기로 했다. 서비스 목록 API 하나를 붙였을 뿐인데 파일이 7~8개 생겼다. Endpoint, APIClient, DTO, Entity, Mapper, Repository 프로토콜과 구현, UseCase, 그리고 이걸 조립하는 AppContainer까지. Flutter에서는 dio로 받아서 fromJson 하고 바로 화면에 넘기던 일이다. 처음엔 과하다고 느꼈다. 그럼 이 계층은 각각 무슨 일을 하고, 왜 나눠야 할까? 이 글은 요청 한 번이 지나가는 길을 따라가며 그 고민을 정리한 것이다. 1. 결론부터파일이 많아진 이유는 "바뀌는 이유"마다 한 곳씩 맡겼기 때문이다. 서버 JSON이 바뀌면 DTO와 Mapper만, 저장소를 바꾸면 Repository ..
설계를 정하고 나서 화면을 만들 차례가 됐다. 그런데 SwiftUI가 처음이었다. API,ViewModel,UseCase를 한꺼번에 붙이면 문제가 생겼을 때 어디가 원인인지 알기 어렵다. 그래서 서버 연동 없이 더미 데이터로 탭,목록,상세,바텀 시트의 뼈대만 먼저 세웠다. 만들면서 계속 "Flutter에서는 이걸 뭘로 했더라?"를 떠올렸다. 이 글은 Flutter 개발자가 SwiftUI로 화면 골격을 잡을 때 대응되는 것들을 정리한 것이다. 1. 결론부터위젯 이름은 거의 일대일로 대응된다. 가장 다른 점은 "함수를 부르던 자리를 상태 변경이 대신한다"는 것이다. 하고 싶은 것FlutterSwiftUI하단 탭bottomNavigationBar + IndexedStackTabView + Tab탭별 화면 이..
Flutter로만 앱 개발을 해왔고 메서드 채널을 통해서 각 Swift,Kotlin을 사용해 본 경험은 있다. 하지만 0부터 Native를 개발 해본 적이 없기도 하며 Claude랑 함께 하면 스터디도 하면서 쉽게 개발 할 수 있을 것 같아서 이번 프로젝트부터 해보기로 했다. Flutter로 할 땐 하네스를 설계 해서 자동화를 하면서 썻지만 SwiftUI는 스터디가 목적이라서 "코드를 대신 짜 주는 도구"가 아니라 "옆자리 선배"처럼 쓰는 것을 원칙으로 정했다. 구분Claude가 한 일내가 한 일설계PRD·아키텍처 문서 보완, 선택지와 장단점 제시PRD, 아키텍처 문서 초안 작성, 선택지 중 선택구현단계별 가이드(줄 번호, 바꾸기 전→후 코드, 이유)코드를 작성 및 피드백 요청검증내 코드를 다시 읽고 빌..
SwiftUI로 앱을 만들다 보면 반복되는 UI를 따로 빼게 된다. 그런데 빼는 방법이 두 가지다.struct XxxView: View로 새 뷰를 만든다.ViewModifier를 만들어 .xxx() 형태의 modifier로 붙인다.나는 복사 완료 토스트를 ViewModifier로 만들었고, 서비스 아이콘은 View로 만들었다. 둘 다 "재사용 가능한 UI"인데, 그럼 아이콘도 Modifier로 만들어야 하는 게 아닐까? 이 글은 그 고민을 정리한 것이다.1. 결론부터혼자서 무언가를 그리는 부품은 View, 다른 뷰에 붙어서 꾸미거나 기능을 더하는 것은 ViewModifier.둘 다 재사용 가능한 UI지만, 무엇을 재사용하느냐가 다르다.View (struct)ViewModifier하는 일새로운 UI 부품을..
