묻고 답해요
164만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결Part2: 초중급 iOS 인스타그램 클론(SwiftUI, MVVM, Firebase, 2024)
searchable이 화면 하단에 위치해요
searchable이 화면 하단에 위치해요. 어떻게 하면 상단으로 이동시킬수가 있을까요?
-
미해결앨런 iOS 앱 개발 (15개의 앱을 만들면서 근본원리부터 배우는 UIKit) - MVVM까지
Drawing Cycle 관련 질문
안녕하세요, 강의 늘 잘 듣고 있습니다.다름이 아니라, 뷰의 레이아웃을 잡아주는 과정에서 Drawing Cycle & 생성자 중 어디에 무엇을 작성해야 할지 헷갈려서 개인적으로 알아보던 중 궁금증이 생겨서 질문드립니다! Drawing Cycle에 맞추어 뷰 업데이트하는 몇가지 예시init(생성자)subview 추가 (addSubview, stackView.addArrangedSubview)제약 생성 + NSLayoutConstraint.activate (1회)정적인 스타일(폰트, 색, axis, spacing 등)즉, ‘한번 바뀌고 안 바뀔 것들’을 세팅해 준다.updateConstraints()상태에 따라 레이아웃이 바뀔 때 (expanded/collapsed, 이미지 크기 토글, padding 변경, 어떤 뷰 숨김에 따른 높이 변경)constraint를 프로퍼티로 보관해두고updateConstraints()에서 constant 바꾸거나 isActive 토글필요 시 setNeedsUpdateConstraints() 호출즉, ‘동적으로 바뀔 레이아웃’을 세팅해 준다.layoutSubviews()frame이 계산된 뒤의 후처리를 수행해줘야 할 때 (그림자 경로, cornerRadius 등 frame의 정보가 필요한 것들) 먼저 제가 정리한 바는 다음과 같습니다.그런데, constraint를 updateConstraint() Drawing Cycle 내에서 지정해주는 강의 내 파트와 살짝 충돌이 발생하더라구요.GPT 및 개인적인 공부를 통해 확인했을 때에는,'constraint가 여러번 중복 적용될 수도 있으므로, 동적으로 constraint를 변경할 상황이 아니라면 생성자에서 초기화해주는 것이 좋다'라고 이해했습니다.동적으로 변경되지 않는 기본 레이아웃 제약은 생성자에서 설정하는 것이 원칙적으로 더 적절한지, 아니면 updateConstraints()에서 설정하는 방식도 올바른 패턴인지 궁금합니다.어떤 방식이 더 권장되는지 명확히 알고 싶습니다..!
-
해결됨앨런 iOS 앱 개발 (15개의 앱을 만들면서 근본원리부터 배우는 UIKit) - MVVM까지
앱 제작 시 주로 코드로 작성하시는 이유가 있을까요?
안녕하세요, 해당 강의 18:00분 경에 앨런님께서 말씀하신 '앱을 만들 때 주로 코드로 만든다'라는 말씀에 궁금증이 들어 질문 남깁니다.UIKit을 통해 앱을 제작하실 때, 스토리보드 대신 주로 이전 강의 섹션과 같이 주로 코드를 이용해 UI를 구성한다는 말씀이신가요?스토리보드와 코드 둘 다 짧게나마 겪어본 제 입장에서는, 세그웨이를 수행하는 것 제외 스토리보드가 더욱 많은 이점이 느껴져서요.대표적으로는 VC에 UI 관련한 코드가 없어 더욱 VC가 본인의 책임에 집중하게 된다는 생각도 들고, 코드로 UI를 짜다 보면 하나 둘 빼먹게 될 확률도 높아지는 것 같아서요(스토리보드에서는 이를 바로 에러로 잡아주지만, 코드는 잡아주지 않기도 하구요)어떤 부분에 의해, 주로 코드로 UI를 구성하는 것을 선호하는지가 궁금합니다!
-
해결됨앨런 iOS 앱 개발 (15개의 앱을 만들면서 근본원리부터 배우는 UIKit) - MVVM까지
델리게이트 패턴 관련 질문
영상 초반부(00:30 - 04:00)에 대한 의문점이 생겨 질문 남깁니다!텍스트필드의 경우 VC(뷰컨트롤러)와 '완전히 분리된 객체'이며 유저와 직접적인 커뮤니케이션을 하고, 버튼의 경우 VC와 '직접적인 커뮤니케이션을 한다' 라고 하셨는데, 이 부분에서 살짝 의문이 듭니다.텍스트필드와 버튼의 차이가 동작의 형태나 이벤트 흐름의 복잡성에서 발생한다는 점에는 공감하지만, 버튼 역시 UITextField와 마찬가지로 VC와는 별도의 객체로 존재하고, 이벤트 발생 시 target–action 메커니즘을 통해 결과를 VC로 전달하는 구조로 이해하고 있습니다.이런 관점에서 봤을 때, “버튼은 VC와 직접 커뮤니케이션한다”는 표현이 개념적으로 어떤 의미를 가지는지 조금 더 설명을 부탁드려도 될까요?
-
해결됨350개의 개인 앱을 만들어 월급의 7배 수익을 달성한 방법
ios 애드몹 광고
안녕하세요 프로그램 좀비님 좋은 점심입니다 :)저는 ios 앱 개발만 진행중입니다. ios 앱에 어떤 광고 유형들 추가하면 좋을지 고민이 되어 질문 올렸습니다. 1. ios의 경우 스플래시 이후 전면광고 대신 오프닝 광고를 추가하시나요? 스플래시 이후의 전면광고는 평점 관리나 이탈률에 취약할것같고 오프닝은 ecpm이 낮을것같아 고민됩니다. 오프닝 추가하고 특정 행동 이후 전면광고가 최선일까요?2. ios에서 스플래시 후 전면광고로 애드몹 정책에 문제가 생기신적이 있으신가요?3. 첫 앱 사용자에게는 이탈률을 줄이기 위해 광고를 가리거나 하시나요?
-
해결됨앨런 iOS 앱 개발 (15개의 앱을 만들면서 근본원리부터 배우는 UIKit) - MVVM까지
수강 기간 연장 부탁드릴 수 있을까요?
구매 후에 늦게나마 시작해서 기간이 얼마 안남았네요ㅠ혹시 기간 연장이 가능할까요?
-
해결됨앨런 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와 독립적으로 실행되어 이러한 재진입 문제를 방지할 수 있는 것으로 이해했습니다.제가 이해한 내용이 맞는지 확인 부탁드립니다. 감사합니다.