# 코드리뷰 - Flutter (2022.09.07)
# 메인 이슈
# 독립적인 모듈로 복잡한 시스템을 조각내기
# 스파게티 코드를 부축이는 데이터 공유
# 메시지가 아닌 속성으로 대화하는 객체들
# 상속보다는 위임을 선택해야 하는 이유
- 무조건 나쁘다고는 할 수 없지만 단순히 중복코드를 제거하기 위해서 상속을 사용하지 마세요.
# 어디까지 코드를 감춰야 할까요?
# 기타 이슈
# 문자열 분기 및 중복 코드
# as is
if (ok) {
// 정상 처리 코드
} else if (authResult.message == '권한이 부여되지 않았습니다. 모든 권한을 부여해주세요.') {
CommonUtils.toast(authResult.message!);
Get.offNamed(AppRoutes.insta_pro_access_error);
} else if (authResult.message == '해당 인스타그램 프로페셔널 계정에...'){
CommonUtils.toast(authResult.message!);
Get.offNamed(AppRoutes.insta_pro_facebook_account_error);
} else if (authResult.message == '이미 가입된 인스타그램 프로페셔널 계정입니다.') {
CommonUtils.toast(authResult.message!);
Get.back();
} else {
CommonUtils.toast('알 수 없는 오류가 발생했습니다. 다시 시도해주시거나 고객 센터에...');
Get.offNamed(AppRoutes.insta_pro_unknown_error);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# to be
if (authResult.errorCode != 0) {
CommonUtils.toast(authResult.message);
Get.offNamed(_getRouterPathFromErrorCode(authResult.errorCode));
return;
}
// 정상 처리 코드
1
2
3
4
5
6
7
2
3
4
5
6
7
# 분기문의 블럭이 너무 길어서 코드 흐름을 알기 어려움
# as is
return showContent ? Container(
child: Column(
children: <Widget>[
_divider(),
Container(
margin: EdgeInsets.only(top: h(20), right: w(32)),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
InkWell(
child: Row(
children: [
Text('컨텐츠 확인하기', style: ...),
Icon(LineAwesomeIcons.angle_right, ...)
],
),
onTap: () async {
if (...) {
launchUrlString(...);
}
},
),
],
),
),
],
),
) : Container();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# to be
if (!showContent) return Container();
return Container(
child: Column(
children: [
_divider(),
Container(
margin: EdgeInsets.only(top: h(20), right: w(32)),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
InkWell(
child: Row(
children: [
Text('컨텐츠 확인하기', style: ...),
Icon(LineAwesomeIcons.angle_right, ...)
],
),
onTap: () async {
if (...) {
launchUrlString(...);
}
},
),
],
),
),
],
),
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 비효율적인 코드 #1
# as is
Widget _getPage(int index) {
if (index == 0) {
return _page1();
} else if (index == 1) {
return _page2();
} else if (index == 2) {
return _page3();
} else {
return Container();
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# to be
Widget _getPage(int index) {
if (index > 2) retun Container();
const _pages = [_page1(), _page2(), _page3()];
return _pages[index];
}
1
2
3
4
5
6
2
3
4
5
6
# 실제 적용된 코드
Widget _getPage(int index) {
const titles = [
"<b>인스타그램\n메뉴 > 설정 > 계정 > 프로페셔널...",
"<b>프로필 편집 > 페이지</b>를\n눌러 주세요.",
"<b>기존 페이지 연결</b>을 눌러주세요.\n페이지가...",
"연결이 잘 되었다면 <b>프로필 편집</b> 화면에\n다음과...",
"<b>연동하러 가기</b>를 눌러 안내에 맞춰\n진행해 주세요."
];
return Column(
children: [
Align(
alignment: Alignment.centerLeft,
child: StyledText(text: titles[index],
style: TextStyle(fontSize: sp(40)),
tags: {"b": StyledTextTag(...))}
),
),
SizedBox(height: h(32)),
Image.asset('.../{index + 1}.png'),
],
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 비효율적인 코드 #2
# as is
Widget _signinButton() {
if (_signinEmailEnabled && _signinPasswordEnabled) {
return mainButton(
title: "로그인",
onPressed: () async {
final String email = this._emailController.text.trim();
final String password = this._passwordController.text.trim();
DeviceUtils.closeKeyboardLayer();
await this._model.signin(email: email, password: password);
}
);
} else {
return mainButton(
title: "로그인",
backgroundColor: UiConfig.primaryDisabledColor,
onPressed: () {
}
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# to be
Widget _signinButton() {
const canSignin = _signinEmailEnabled && _signinPasswordEnabled;
return mainButton(
title: "로그인",
backgroundColor: canSignin ? null : UiConfig.primaryDisabledColor,
onPressed: () {
if (!canSignin) return;
DeviceUtils.closeKeyboardLayer();
final String email = _emailController.text.trim();
final String password = _passwordController.text.trim();
_model.signin(email: email, password: password);
}
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# null safe issue 그리고 ...
# as is
Text('${this._campaign!.brand!.brand_name}',
style: TextStyle(color: UiConfig.secondaryFontColor, fontSize: sp(24))
);
var str = _campaignApply?.posting_url as String;
1
2
3
4
5
2
3
4
5
# to be 1
Text(_campaign?.brand?.brand_name ?? "",
style: TextStyle(color: UiConfig.secondaryFontColor, fontSize: sp(24))
);
var str = _campaignApply?.posting_url ?? "";
1
2
3
4
5
2
3
4
5
# to be 2
Text(_campaign.brand.brand_name,
style: TextStyle(color: UiConfig.secondaryFontColor, fontSize: sp(24))
)
1
2
3
2
3
- 상황에 따라서 객체 내부에서 필요한 조치를 하여 외부에서는 신경쓰지 않도록 한다.
# 필요없는 코드
# as is
onPressed: () async {
Get.dialog(Center(child: CircularProgressIndicator()));
await _model.connectInstagramProfessional(_fromWhere == 'fromSignup');
}
1
2
3
4
2
3
4
# to be
onPressed: () async {
Get.dialog(Center(child: CircularProgressIndicator()));
_model.connectInstagramProfessional(_fromWhere == 'fromSignup');
}
1
2
3
4
2
3
4