묻고 답해요
163만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨앨런 iOS 앱 개발 (15개의 앱을 만들면서 근본원리부터 배우는 UIKit) - MVVM까지
활용 앱 57강 질문입니다.
영상 1분경에 보면드래그해서 폴더랑 파일 순서 변경하시는데 저는 현재 xcode 26.2 버전 사용하고있는데요.최신버전에서는 폴더, 파일순서 변경이 안되는건가요?
-
미해결Flutter 중급 2편 - 실전 앱 개발 - 미국 주식 앱 (with 클린 아키텍처)
그래프 그리기 위한 API가 프리미엄 요금제를 구독해야만 가능하다고 합니다...ㅜㅜ
{ "Information": "Thank you for using Alpha Vantage! This is a premium endpoint. You may subscribe to any of the premium plans at https://www.alphavantage.co/premium/ to instantly unlock all premium endpoints"}선생님, 그래프 그리기 위한 API가 프리미엄 요금제를 구독해야만 가능하다고 하는데 어떻게 하면 좋을까요? ㅜㅜ
-
미해결Flutter 입문 - 안드로이드, iOS 개발을 한 번에 (with Firebase)
과거 ai없을때 듣고 다시 듣는 중인데
허허 돌겟.. 에이전트 붙여서강의 따라가니깐 금방금방이네요 게다가 주석으로 설명까지.. 크...
-
해결됨앨런 iOS 앱 개발 (15개의 앱을 만들면서 근본원리부터 배우는 UIKit) - MVVM까지
활용 앱 30강 에러 질문입니다
let backButton: UIButton = { let button = UIButton(type: .custom) button.setTitle("Back", for: .normal) button.setTitleColor(.white, for: .normal) button.backgroundColor = UIColor.blue button.titleLabel?.font = .boldSystemFont(ofSize: 20) button.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside) return button }()backButton 설정시에 addTarget 첫번째 인자로 self 로 전달하니까 'self' refers to the method 'FirstViewController.self', which may be unexpected라는 warning 문구가 xcode 내에 표시되어서 xcode 에서 추천하는 방향대로 아래 코드블록 처럼 FirstViewController.self 로 전달하니까 backButton 클릭시 Exception 이 발생하는데요. button.addTarget(FirstViewController.self, action: #selector(backButtonTapped), for: .touchUpInside) 이 부분 왜 Excpetion 이 발생하는건지 궁금합니다.self 로 전달하면 문제는 없는데 경고문구가 나오는게 신경쓰입니다.경고문구를 없애고 버튼 클릭에도 문제가 없으려면 어떻게 하는게 좋을까요?Exception 문구는 아래와 같습니다.*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[NextVC.FirstViewController backButtonTapped]: unrecognized selector sent to class 0x100e4a3d8'*** First throw call stack:(0x19df92964 0x19ae81814 0x19e02d140 0x19df1235c 0x19df1a200 0x1a4c49f58 0x1a4501154 0x1a4501498 0x1a44fdf08 0x1a450001c 0x1a4c7a2d4 0x1a4c7b734 0x1a4c5e170 0x1a3892d38 0x1a38a1e28 0x1a3894c50 0x1a38a2ee4 0x1a38a2374 0x28cd15560 0x19dee34cc 0x19df130b0 0x19df12fd8 0x19deeac1c 0x19dee9a6c 0x23fb18498 0x1a38c2df8 0x1a386be54 0x1a3997820 0x100e365cc 0x100e3653c 0x100e36648 0x19aed6e28)libc++abi: terminating due to uncaught exception of type NSException
-
미해결SwiftUI + TCA: 실전 프로젝트로 완성하는 차세대 iOS 아키텍처
예제 빌드시 The compiler is unable to type-check this expression in reasonable time 에러 발생
// // EditImageReducer.swift // AppStore // // Created by Langpeu on 12/24/25. // import ComposableArchitecture import SwiftUI import SwiftData import Photos @Reducer struct EditImageReducer { @ObservableState struct State { var userImage: Image? var assets: [PHAsset] = [] var selectedPhoto: (id: String, data: Data)? @Presents var alert: AlertState<Action>? } enum Action { case onAppear(image: Data?) case setUserImageData(Data?) case setUserImage(Image) case authResult(Bool) case onSelectPhoto(id: String, data: Data) } var body: some Reducer<State, Action> { Reduce { state, action in switch action { case let .onAppear(imageData): return Effect.run { send in let isAuth = await PhotoManager.requestAuthorization() await send(.authResult(isAuth)) await send(.setUserImageData(imageData)) } case let .authResult(isAuth): if isAuth { let assets = PhotoManager.getAssets() state.assets = assets } else { state.alert = AlertState.creatAlert(type: .error(message: "권한이 없습니다")) } case let .setUserImageData(data): guard let data, let uiImage = UIImage(data: data) else { return .none } return .send(.setUserImage(Image(uiImage: uiImage))) case let .setUserImage(image): state.userImage = image return .none case let .onSelectPhoto(id, data): state.selectedPhoto = (id: id, data: data) } return .none } } } struct EditImageView: View { @Bindable var store: StoreOf<EditImageReducer> let colums: [GridItem] = .init(repeating: .init(.flexible()), count: 3) @Query private var users: [User] private var user: User? { users.first } var body: some View { ScrollView { VStack { Text("선택된 이미지") // 선택된 이미지 Group { if let image = store.userImage { image .resizable() .scaledToFit() } else { Color.gray.opacity(0.2) } } .frame(width: 100, height: 100) .clipped() .cornerRadius(8) } LazyVGrid(columns: colums, spacing: 10) { ForEach(store.assets, id: \.localIdentifier) { asset in let isSelectedImage = store.selectedPhoto.id == asset.localIdentifier AssetImageView(asset: asset, isSelected: isSelectedImage, onTap: { data in //TODO: onTap store.send(.onSelectPhoto(id: asset.localIdentifier, data: data)) }) .clipped() .clipShape(RoundedRectangle(cornerRadius: 8)) } } .padding(8) } .onAppear() { store.send(.onAppear(image: user?.imageData)) } } } private struct AssetImageView: View { let asset: PHAsset let isSelected: Bool let onTap: (Data) -> Void let imageWidth: CGFloat = (UIScreen.currentWidth - 16 - 20) / 3 @State private var uiImage: UIImage? = nil var body: some View { Group { if let uiImage = uiImage { Image(uiImage: uiImage) .resizable() .scaledToFill() .onTapGesture { if let data = uiImage.jpegData(compressionQuality: 1.0) { onTap(data) } } } else { Color.gray.opacity(0.2) } } .frame(width: imageWidth, height: imageWidth) .overlay(alignment: .topTrailing, content: { if isSelected { Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) .frame(width: 20, height: 20) } }) .onAppear() { PhotoManager.fetchImage(asset: asset) { uiImage in self.uiImage = uiImage } } } }75번라인var body: some View 에서 아래 빌드에러 발생The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions----위와 같은 에러로 빌드가 안되서아래 처럼 처리 했습니다.(Xcode26.2 + M1Pro Macbook) 포인트: let isSelectedImage = ... 같은 로컬 let + 복잡한 클로저를 ForEach 내부에서 직접 쓰면 타입체커가 더 힘들어해. 함수로 빼면 거의 항상 해결됨. LazyVGrid(columns: colums, spacing: 10) { ForEach(store.assets, id: \.localIdentifier) { asset in assetCell(asset) } } .padding(8) } .onAppear() { store.send(.onAppear(image: user?.imageData)) } } @ViewBuilder private func assetCell(_ asset: PHAsset) -> some View { let isSelectedImage = (store.selectedPhoto?.id == asset.localIdentifier) AssetImageView( asset: asset, isSelected: isSelectedImage, onTap: { data in store.send(.onSelectPhoto(id: asset.localIdentifier, data: data)) }) .clipped() .clipShape(RoundedRectangle(cornerRadius: 8)) }
-
미해결SwiftUI + TCA: 실전 프로젝트로 완성하는 차세대 iOS 아키텍처
TextField 에 Binding 으로 연결하면 에러 발생
강의 내용중에아래 2가지가 가능하다고 했는데1번TextField("이메일을 입력해주세요", text: $store.email.sending(\.inputEmail))2번 TextField("이메일을 입력해주세요", text: Binding(get: { store.email }, set: { email in store.send(.inputEmail(email)) }))2번 코드로 작성후 이메일주소를 수정후저장 하니깐 아래와 같은 에러가 발생했습니다.A "forEach" at "AppStore/MyPageReducer.swift:96" received an action for a missing element. Action: MyPageStackReducer.Action.email(.inputEmail) This is generally considered an application logic error, and can happen for a few reasons: A parent reducer removed an element with this ID before this reducer ran. This reducer must run before any other reducer removes an element, which ensures that element reducers can handle their actions while their state is still available. An in-flight effect emitted this action when state contained no element at this ID. While it may be perfectly reasonable to ignore this action, consider canceling the associated effect before an element is removed, especially if it is a long-living effect. This action was sent to the store while its state contained no element at this ID. To fix this make sure that actions for this reducer can only be sent from a store when its state contains an element at this id. In SwiftUI applications, use "NavigationStack.init(path:)" with a binding to a store.확인해 보니 pop시 Binding의 set 이 호출되서이미 path에 없는 element를 호출하게 되서해당 에러가 발생한다고 합니다.그럼 TCA 환경에서는 1번을 꼭 사용해야 하는 건지요 ?
-
미해결SwiftUI + TCA: 실전 프로젝트로 완성하는 차세대 iOS 아키텍처
xcode 26.2 에서 ReducerOf<Self> 이슈
강의 내용처럼EditNameReducer 에서ReducerOf<Self> 를 사용하면아래처럼 에러가 발생합니다.@Reducer 매크로 타입은 빌드시점이 되야EditNameReducer 가 Reducer 타입인걸알기 때문에 사용 못한다고 하는데 강의는 에러 없이 되서 xcode26.2 에서왜 안되는지 알고 싶습니다.결국 아래처럼 변경해서 사용 했습니다.var body: some Reducer <State, Action> { Reduce { state, action in return .none } }
-
해결됨앨런 Swift Concurrency for Swift 6 (Part-2)
20강 Task 내부에서 nonisolated async 호출 예제 질문드립니다
18분 55초 쯤 예시를 보여주시면서/// 2) (액터 외부) 동기 코드가 (일반) 비동기 코드에서 불려지면.. 어떤 (컨텍스트에 해당하는) 작업(Task) 내부에서 실행됨 ===> Task 격리 func readAny(with books: [Book]) async { if let book = books.randomElement() { readBook(with: book) } } Task { let account = LibraryAccount(idNumber: 1) /// 액터 let books = await account.booksOnLoan /// 액터내부의 (상태)값 가져오기 (Sendable타입) await readAny(with: books) /// 액터 외부에서 실행 (일반적으로 이런 구현은 옳지 않음) }위와 같은 코드에서 Task 가 3번 스레드에서 돌아가고 있었다고 치면, account.booksOnLoan 을 가져오는건 2번 스레드로 갈 수 있다고 쳐도, await readAny(with:) 는 다시 Task 와 동일한 3번 스레드에서 돌아가게 될거다! 라고 말씀을 주셨는데요사실 nonisolated async func 인 readAny 를 await 로 호출을 하는 순간, 해당 함수는 Task 의 스레드는 무관한게 아무 스레드에서나 돌아갈 수 있다고 생각을 했습니다 (실제로 Task 를 mainActor context 로 두고 해당 코드를 돌려봐도 readyAny 는 main 에서 돌아가지 않았구여!)이해 편의상 돌려서 설명을 주시건가??? 생각이 들지만 혹시 정확히 어떤 의미로 요렇게 말씀을 주신건지 궁금합니다~감사합니다저장
-
해결됨앨런 Swift Concurrency for Swift 6 (Part-2)
19강 Actor 의 동작 관련해 질문드립니다
안녕하세요 앨런님! 19강에서 actor 를 이해 편의상 "특정한 하나의 스레드에서만 실행"되는 것처럼 그림을 첨부해주셨는데요 (물론 설명으로는 한번에 하나씩 실행되는건 맞지만 스레드를 특정할 수는 없다" 라고 말씀주셨지만요)그러면 이해 편의상이 아니라, 좀 더 정확하게는 제가 이해한 내용이 맞을지 한번 확인 부탁드려도 될까요?항상 좋은 강의 감사합니다. 기본적으로 actor 의 실행 스레드는 고정되어있지 않음Actor 의 메서드나 데이터에 접근하면 serial executor 에 작업이 쌓일 것이때 actor 는 선입 선출로 동작하지 않으므로, 우선 순위에 따라 실행 순서는 달라질 수 있음Actor 의 작업이 실행될때 어느 스레드에서 실행할건지는 상관없고 중요하지도 않음. 한 작업이 끝나고 나면, serial executor 에서 대기하고 있던 작업은 어느 스레드에서든 heap 에 저장된 데이터 읽어서, 그 데이터 기반으로 작업하면 되는 것.중요한건 데이터 경쟁이 발생하지 않게 serial executor 에 넣어서 actor 관련 작업은 한번에 하나의 스레드에서만 돌게 하는 매커니즘
-
미해결앨런 iOS 앱 개발 (15개의 앱을 만들면서 근본원리부터 배우는 UIKit) - MVVM까지
기초앱 17강 Contraints 설정 질문입니다.
6:09 쯤에 UIView 에 대해서 Add New Contraints 하실때따라해보면 Top alignment 가 맞지않는게 좀 다르게 설정이 되는것 같습니다.Inspector 패널에서 확인해보면Top Alignment Constraint 가First Item : View.topSecond Item : Safe Area.top이런식으로 적용되고있는데요.이부분 설정을 어떻게해야 강의에 나오는것 처럼 수직정렬이 반듯하게 적용이 되는걸까요?
-
미해결[Lv.1] iOS 17 앱 개발 기초 - SwiftUI로 시작하기
소리가 ㅠㅠ
한쪽에서만 들려요 ㅠㅠ
-
해결됨Flutter 초입문 왕초보편
실로폰 음원 재생 오류 해결 (do.1.wav)
안녕하세요. 강의 듣다가 테스트 중에 문제가 발생하여 해당 내용에 대해 공유를 드리고자 이렇게 글 남깁니다.문제가 발생한 부분은 음원을 audioPlayer 객체로 읽어 들이는 과정에서 제대로 처리가 되지 않아 _isLoading State 값이 변경되지 않고 무한 로딩이 걸리는 문제가 있습니다.원인을 찾고자 try ~ catch 문을 통해 에러 메세지를 출력해 보았다가 _isLoading State가 정상적으로 업데이트 되어 화면 UI가 나오기 시작했습니다.for (final note in notes) { final player = AudioPlayer(); try { await player.setAsset('assets/$note'); } catch (e) { print(e); } _audioPlayers.add(player); }다만 이렇게 수정한 후에는 맨 첫 번째 '도' 건반의 소리가 나지 않았습니다. 다른 음원은 모두 정상적으로 로드했지만, 첫 번째 'do1.wav' 만 로드가 되지 않고 에러가 발생합니다.Playback error androidx.media3.exoplayer.ExcoPlaybackException: Source Error여러 고민을 해 보다가 혹시나 싶어 'do1.wav'의 음원의 이름을 'do.wav'로 바꾸니 정상적으로 작동하였습니다.정확한 원인은 모르겠지만, 혹시 같은 문제를 겪는 분은 이름을 바꿔보는 걸 시도해 보시면 좋을 거 같습니다!
-
미해결Flutter 초입문 왕초보편
Emulate Device Frame 관련 질문입니다.
안녕하세요. 앱 개발에도 관심이 생겨 Flutter에 도전을 하고 있습니다. 초입문편이 있길래 구매하여 강의 시청 중에 있는데요. 강의에서 보여지는 Android Studio 버전이 저와 달라서 일단 헷갈리는 부분이 있습니다. 현재 다운로드하는 시점에서는 Android Studio Otter로 되어 있는데, 현재 보여주시는 UI와 상당히 달라져 있어서 약간 진행하는데 텀이 발생하고 있습니다. 제가 설치한 Android Studio Otter에서는 Device Manager > device > Edit 을 클릭하여 들어가 보아도 Device Frame 관련 체크박스가 보이지 않습니다. Otter에서는 따로 지원하지 않는 것인가요?참고로 밖으로 꺼내지 않고 사이드바에 있을 경우엔 따로 Device Frame을 제거할 수 있더라구요 .. 밖으로 빼냈을 경우에만 Device Frame 체크박스를 찾을 수 없는 상황입니다 ..!
-
해결됨앨런 Swift Concurrency for Swift 6 (Part-2)
Actor에서 Task vs Task.detached 사용 시 재진입 문제 질문
actor TaskImageDownloader { /// (다운로드) 상태를 저장하기 위한 열거형 정의 enum DownloadState { case completed(UIImage) case loading(Task<UIImage, Error>) case failed } private(set) var cache: [String: DownloadState] = [:] func image(from url: String) async throws -> UIImage { /// 기존에 저장된 상태가 있는 경우 (캐시 먼저 확인) if let cachedState = cache[url] { switch cachedState { case .completed(let image): /// 이미지 리턴 return image case .loading(let task): /// 작업(Task)을 기다렸다가 ===> 이미지 리턴 return try await task.value case .failed: throw "이미지 다운로드 실패" } } /// 작업(Task)을 생성 let task = Task.detached<UIImage, Error> { let image = try await downloadImage(from: url) return image } /// 일단 (완료되지 않은) 작업 상태를 보관 cache[url] = .loading(task) do { /// 작업의 완료를 기다렸다가 ===> 완료되면 ===> 완료상태(이미지)로 바꿔서 보관 let image = try await task.value cache[url] = .completed(image) return image } catch { cache[url] = .failed // 에러 발생의 경우 throw "이미지 다운로드 실패" } } } Actor에서 Task vs Task.detached 사용 시 재진입 문제 질문안녕하세요.위 코드에서 Task.detached 대신 일반 Task {}를 사용하면 재진입 문제가 발생할 수 있을 것 같아 질문드립니다.제가 이해한 바로는:Task {}를 사용하면 생성된 작업이 actor의 context를 상속받아 serial executor에서 실행됩니다다운로드 작업 중 await를 만나면 actor가 suspension되고, 이 시점에 다른 스레드에서 해당 actor에 재진입할 수 있습니다따라서 동일한 URL에 대해 중복으로 다운로드 작업이 생성될 가능성이 있습니다반면 Task.detached를 사용하면 actor context와 독립적으로 실행되어 이러한 재진입 문제를 방지할 수 있는 것으로 이해했습니다.제가 이해한 내용이 맞는지 확인 부탁드립니다. 감사합니다.
-
미해결Flutter 중급 1편 - 클린 아키텍처
MVVM, 클린 아키텍처 관련 질문 있습니다.
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 안녕하세요. 좋은 강의 정말 잘 듣고 있습니다.강의를 통해 MVVM 구조와 클린 아키텍처에 대해 배우고, 진행 중인 프로젝트에 적용해보고 있는데, 어려움을 겪고 있어 조언을 구하고자 질문 글을 올리게 되었습니다. 회원 정보 관련api - repository - use_case - view_model -view 구조에서,로그인 화면에서 로그인 후 받아오는 회원 정보를 어디에 저장해 두어야 되는지 잘 모르겠습니다.회원정보는 마이페이지, 피드 등 여러 view에 사용되는데,그렇다면 각각의 view_model은 회원정보를 어디에서 읽어오는게 좋은지 궁금합니다. 새로 고침 관련note_app 강의 예제 에서는 노트 목록과 삭제가 하나의 화면(notes_view)에 있습니다.제가 구현하는 구조에서는 두 개의 view로 분리되어 있습니다.1번 view: 노트 목록 화면2번 view: 노트 상세 + 삭제 화면각 화면은 서로 다른 view_model을 사용합니다.노트 상세 화면에서 노트를 삭제했을 때, 노트 목록 화면을 어떻게 다시 업데이트 하는 것이 적절한지 고민입니다.생각나는 방법을 아래에 작성해보았는데, 어떤 방법을 사용하는 게 깔끔할지, 이외에 더 좋은 방법이 있는지 궁금합니다.두 개의 view가 하나의 view_model을 공용으로 사용 노트 상세 화면에서 노트 목록 view_model를 read 하여 _loadNotes()를 직접 호출노트 목록도 회원 정보처럼 별도로 저장해두고, view_model은 읽어오기 감사합니다!
-
미해결SwiftUI + TCA: 실전 프로젝트로 완성하는 차세대 iOS 아키텍처
해당 강의에서 나오는 노션 링크는 따로 제공안되나요 ?
해당 강의에서 나오는 노션 링크는 따로 제공안되나요 ?
-
해결됨Combine - iOS의 Reactive Programming(2025)
강의자료 문의
안녕하세요, 강의자료는 코드 외에 pdf 파일은 제공받을 수 있을까요?
-
해결됨[Lv.1] iOS 17 앱 개발 기초 - SwiftUI로 시작하기
index와 indexSet
fruitArray 를 delete하는 함수에서index가 아닌 indexSet과 remove(at:)이 아닌 remove(atOffsets:)를 사용하셨는데 왜 그렇게 하셨는지 궁금합니다!
-
해결됨앨런 Swift Concurrency for Swift 6 (Part-2)
18강 NSCache 예시 질문
안녕하세요!18강 전체적으로 cache 에 값을 세팅하기 전후로 lock 이나 semaphore 등을 이용해서 GCD 에서 thread safe 를 구현하는 방법에 대한 예시를 들어준것 확인했습니다이때 removeAll() 의 앞뒤로는 따로 처리가 되어있지않아서, 정말 필요가 없어서 작성되어있지 않은건지, 아니면 단순 예시라서 여기까지는 적용하지 않은건지 궁금합니다func clearAll() { cache.removeAll() } 두번째로, 이건 주제와 좀 동떨어진 질문이긴한데요..! (혹시 강의 성격과 너무 벗어났다 싶으면 무시해주세요!)예시를 NSCache 로 들어주셨는데, NSCache 문서에 보면 캐시를 직접 잠그지 않고도 다양한 스레드에서 캐시에 항목을 추가, 제거 및 쿼리할 수 있다고 나와있는걸 확인했습니다.You can add, remove, and query items in the cache from different threads without having to lock the cache yourself즉, 문서를 통해 자체적으로 thread safe 하게 뭔가를 하고 있구나..를 유추할 수 있기 때문에 (내부적으로는 NSLock 을 사용하고 있긴하지만요) 이럴때는 저희쪽에서 @unchecked Sendable 만 사용해도 무방할까요?하지만 이런 문서 등을 확인하지 않는 이상 정확히 NSCache 가 thread safe 하게 뭔가를 처리한다는걸 확인할 수 없기 때문에, 예시처럼 @uncheked Sendable 명시와 동시에 자체적으로 semaphore 나 lock 을 걸어줘야하는건지..? 앨런님은 어떻게 생각하시는지 궁금합니다!감사합니다~
-
해결됨앨런 Swift Concurrency for Swift 6 (Part-2)
Task 클로저 내 `non-Sendable` 값 타입 접근 시, 캡처 리스트가 정의된 Task 순서에 따른 컴파일러 에러 차이
12강 16분 즘에, Task 의 클로저에 value type 의 프로퍼티를 캡처 리스트로 명시하면 아래와 같은 코드에서는 에러가 나지 않는다고 되어있습니다.struct ValueCounter { var value = 0 mutating func increment() -> Int { value = value + 1 return value } } func test() { var valueCounter = ValueCounter() Task { print(valueCounter.increment()) print(valueCounter.value) } Task { [valueCounter] in var newValueCounter = valueCounter print(newValueCounter.increment()) print(newValueCounter.value) } }하지만 제가 Xcode 26.0.1 에서 확인했을때는 해당 코드의 첫번째 Task 에서 다음과 같은 컴파일 에러가 발생했습니다Sending value of non-Sendable type '() async -> ()' risks causing data races이에 추가로 이것 저것 확인해보다가, 아래와 같이 캡처 리스트를 사용하는 Task 를 먼저 작성하면, 에러가 발생하지 않는것을 확인했습니다.// 캡처 리스트 사용하는 Task 순서 변경하니 정상 func test2() { var valueCounter = ValueCounter() Task { [valueCounter] in var newValueCounter = valueCounter print(newValueCounter.increment()) print(newValueCounter.value) } Task { print(valueCounter.increment()) print(valueCounter.value) } }이와 같은 현상을 어떻게 설명할 수 있을지 궁금합니다.첫번째 예시의 두번째 Task 에서는 [valueCounter] in 으로 현재 값을 캡처하려고해도, 이미 첫번째로 정의된 Task 에서 valueCounter.increment() 를 호출하면서 다른 스레드 (편의상) 에서 값을 변경하고 있기 때문에, 동일 시점에 딱 한개의 쓰레드에서의 접근이 깨져서 이런 에러가 발생하는 걸까요? (그렇다기엔 에러 위치는 첫번째 Task 정의에서 떠서... 아닌가 싶기도하고요..)두번째 예시의 첫번째 Task 에서는 캡처 리스트로 값을 캡처해서 valueCounter.increment() 를 호출하고, 두번째 Task 는 valueCounter.increment() 를 하려고해도 이 시점에서 valueCounter 를 참조하고 있는건 이곳 뿐이기 때문에 (첫번째 Task 에서는 캡처해서 사용), 동일 시점에 딱 한개의 쓰레드에서의 접근이 보장되어서 에러가 발생하지 않는걸까요? 결과를 기준으로 나름대로 고민을 해봤는데, 어쨌든 다 추측이라서.. 혹시 이와 같은 현상을 어떻게 이해하면 될지 궁금합니다감사합니다.