SRP?
SRP(Single Responsibility Principle), 즉 단일 책임 원칙은 객제지향 프로그래밍과 설계의 SOLID 원칙 중 하나로, 각 클래스나 모듈은 오직 하나의 책임만을 가져야 한다는 원칙이다.
SRP를 사용했을 때 기대할 수 있는 효과
SRP를 지키는 경우에 따라오는 장점은 어마무시하다.
- 유지 보수성 향상: 한 클래스가 한 가지 책임만을 가지면, 그 클래스를 수정하는 것은 해당 책임과 관련된 변경 때문이다. 따라서 예기치 않은 부작용을 최소화할 수 있는 것이다.
- 재사용성 향상: 특정 기능만을 수행하는 클래스나 모듈은 다른 프로젝트나 부분에서도 쉽게 재사용할 수 있다.
- 테스트 용이성: 한 가지 책임만을 가진 클래스는 테스트하기가 더 쉽다.
Flutter로 SRP 알아보기
간단한 Flutter 앱으로 SRP의 중요성을 확인해 보자
아래 코드는 +, - 를 지원하는 간단한 카운터 앱이다.
코드의 문제점
- UI와 비즈니스 로직이 결합되어 있다 : 클린한코드를 작성하기 위해서는 UI와 비즈니스로직은 철저하게 분리가 되어야 한다.
class CounterApp extends StatefulWidget {
@override
_CounterAppState createState() => _CounterAppState();
}
class _CounterAppState extends State<CounterApp> {
int _counter = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Count: $_counter'),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(Icons.add),
onPressed: () {
setState(() {
_counter++;
});
},
),
IconButton(
icon: Icon(Icons.remove),
onPressed: () {
setState(() {
_counter--;
});
},
),
],
)
],
),
),
);
}
}
개선된 코드
위의 코드의 문제점을 수정한 코드는 아래와 같다.
달라진 부분은 IconButton안에 있던 비즈니스 코드가 함수로 빠져 관리가 되고 있는 점이다.
이렇게 구현을 할 경우 +와 관련된 기능의 수정이 필요할 경우 다른 위젯의 간섭 없이 수정이 가능하기 때문에
오류발생 가능성이 낮으며 같은 방법으로 개발한 경우 기능의 추가, 삭제가 간단해지는 장점이 생긴다.
class CounterApp extends StatefulWidget {
@override
_CounterAppState createState() => _CounterAppState();
}
class _CounterAppState extends State<CounterApp> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
void _decrementCounter() {
setState(() {
_counter--;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CounterDisplay(counter: _counter),
CounterControls(
onIncrement: _incrementCounter,
onDecrement: _decrementCounter,
),
],
),
),
);
}
}
class CounterDisplay extends StatelessWidget {
final int counter;
CounterDisplay({required this.counter});
@override
Widget build(BuildContext context) {
return Text('Count: $counter');
}
}
class CounterControls extends StatelessWidget {
final VoidCallback onIncrement;
final VoidCallback onDecrement;
CounterControls({required this.onIncrement, required this.onDecrement});
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(Icons.add),
onPressed: onIncrement,
),
IconButton(
icon: Icon(Icons.remove),
onPressed: onDecrement,
),
],
);
}
}
간단한 기능을 예로 들었지만 기능이 거대하고 복잡해질수록 SRP의 중요성은 더 와닿게 될 것이다.