프로퍼티 드릴링이란?

Uno·2022년 11월 30일
0

Tip-Swift

목록 보기
23/26
post-custom-banner

단위 테스트를 작성하기 위해서, 특히 Mock 혹은 Fake 를 사용하기 위해 초기화 메소드에 의존성을 전달받는 경우가 있었습니다. 그럴 때, 해당 값을 하위에 클래스(내부에 초기화된 클래스) 에서 사용하게 되면, 이것을 초기화 함수나 멤버변수로 계속 전달해줘야 합니다. 이 부분을 프로그래밍하다가, 이것을 부르는 용어나 어떤식으로 해결했는지 궁금해서 이 글을 작성합니다.

프로퍼티 드릴링이란?

정의

특정 객체가 있고, 그것의 하위 객체가 있습니다. 하위 객체에게 필요한 값을 전달해주는 행위를 프로퍼티 드릴링이라고 부릅니다.

예시 - SwiftUI

struct ContentView: View {
    var welcomeMessage = "Hello SwiftUI~!!"
    var body: some View {
        VStack {
            Text(welcomeMessage)
        }
        .padding()
    }
}
  • 위 코드는 SwiftUI 의 예시 코드입니다.
  • body 부분이 Rendering 될 UI 코드 입니다.
  • 이 부분에 welcomMessage 라는 프로퍼티를 입력받아서 전달받고 있습니다.
  • 여기서 body 부분에 새로운 Component 로 분리해서 주입하면 아래와 같이 코드가 변경이 될 겁니다.
struct ContentView: View {
    var welcomeMessage = "Hello SwiftUI~!!"
    var body: some View {
        VStack {
            BodyView(message: welcomeMessage)
        }
        .padding()
    }
}

struct BodyView: View {
    var message: String
    var body: some View {
        TextView(text: message)
    }
}

struct TextView: View {
	var text: String
	var body: some View {
		VStack {
			Text(text)
		}
	}
}
  • ContentView > BodyView > TextView 로 구성된 View 입니다.
  • 최상위 ContentView 에 있는 welcomeMessage 가 message -> text 로 하위 뷰로 전달되고 있습니다.

위 상황에서 하위뷰가 더 복잡해지면, 전달해줘야하는 프로퍼티가 정말 많아질 것 같습니다.

예시 - Flutter

최초에는 텍스트가 최상위 위젯에 위치합니다.

void main() {
	runpApp(const MyApp());
}

class MyApp extends StatelessWidget {
	const MyApp({Key? key}) : super(key: key);

	
	Wdiget build(BuildContext context) {
		return Scaffold(
			body: Text('이곳은 최상위 위젯임')
		);
	}
}

그런데 하위에 SubWidget 들이 추가된다면, 다음과 같이 프로퍼티를 전달해줘야 하는 상황은 동일합니다.

class MyApp extends StatelessWidget {  
  const MyApp({Key? key}) : super(key: key);  
  
    
  Widget build(BuildContext context) {  
    return Scaffold(  
      body: Text(''),  
    );  
  }  
}  
  
class SecondWidget extends StatelessWidget {  
  SecondWidget({  
    Key? key,  
    required String this.message,  
  }) : super(key: key);  
  
  String message;  
  
    
  Widget build(BuildContext context) {  
    return Text(message);  
  }  
}

프로퍼티 드릴링을 줄이는 방법

프로퍼티를 전달하게 되면, 초기화 메소드만 봐도 어떤 값들에 의존하고 있는지 확실히 알 수 있습니다.
단점으로는 프로퍼티 하나의 코드 변경 시, 하위 뷰를 모두 변경해야 한다는 단점이 있습니다.
이를 예방하는 방법으로 ServiceLocator(서비스 로케이터) 패턴을 사용하기도 합니다.
(SwiftUI / Flutter)

SwiftUI - EnvironmentObject

// ObservableObject 만들기
class Message: ObservableObject {
	var content: String = "Hell wolrd SwiftUI~!"
}

@main
struct Property_DrillingApp: App {
	var body: some Scene {
		WindowGroup {
			ContentView()
				.environmentObject(Message())
		}
	}
}

struct ContentView: View {
	var body: some View {
		VStack {
			BodyView()
		}
		.padding()
	}
}

struct BodyView: View {
	var body: some View {
		TextView()
	}
}

struct TextView: View {
	@EnvironmentObject var globalMessage: Message
	var body: some View {
		VStack {
			Text(globalMessage.content)
		}
	}
}
  • 이전 같으면, App 에서 TextView 까지 4 번의 프로퍼티 드릴링일 해줘야했습니다.
  • @EnvironmentObject 를 사용해서, 필요한 곳에서만 정의해서 접근하고 있습니다.

Flutter - InheritedWidget

  • 최상위 위젯
class MyApp extends InheritedWidget {  
  MyApp({  
    super.key,  
    required super.child,  
  });  
  
  String message = 'Hell world Flutter~!';  
  
  static MyApp of(BuildContext context) {  
    final MyApp? result = context.dependOnInheritedWidgetOfExactType<MyApp>();  
    assert(result != null, "Not found...");  
    return result!;  
  }  
  
    
  Widget build(BuildContext context) {  
    return Scaffold(  
      body: SecondWidget(),  
    );  
  }  
  
    
  bool updateShouldNotify(covariant InheritedWidget oldWidget) {  
    throw UnimplementedError();  
  }  
}
  • static MyApp of(BuildContext context) {...}이 부분을 전역으로 정의했기 때문에, 하위 위젯에서도 상위 위젯에 접근가능합니다.

  • 다만 Flutter 프레임워크 특징 중 현재 Context 를 입력받아서, 상위 위젯을 조회해야합니다. 그러므로 BuildContext 를 파라미터로 전달해줍니다.

  • 최하위 위젯

class ThridWidget extends StatelessWidget {  
  ThridWidget({  
    Key? key,  
  }) : super(key: key);  
  
    
  Widget build(BuildContext context) {  
    return Text(MyApp.of(context).message);  
  }  
}
  • MyApp.of(context).message 이 코드가 상위 위젯으로 접근하는 부분입니다.

정리

  • 프로퍼티 드릴링이 명시적으로 어떤 값을 전달해줘야하는지 한 눈에 보이기에 나쁜 코드는 아닙니다.
  • 단위 테스트 진행 시, 해당 객체를 초기화메소드에 전달해주면 되기에 프로퍼티 드릴링을 해도 괜찮습니다.
  • 다만, UI 담당 객체에서는 단위테스트가 힘들기도 합니다. 그런 곳에는 프로퍼티 드릴링을 줄이는 것도 생산성을 늘리는 하나의 방법이라고 봅니다.

참고자료

profile
iOS & Flutter
post-custom-banner

0개의 댓글