해당 포스팅은 유튜브 영화&드라마 리뷰 영상 큐레이션 플랫폼
Plotz
를 개발하면서 도입된 기술 및 방법론에 대한 내용을 다루고 있습니다.
다운로드 링크 : 앱스토어 / 플레이스토어
여러분들은 보통 원시 타입의 데이터
(String, Int ...)를 특정 형태로 포맷팅하는 기능을 어떻게 구현하시나요?
정수를 천 단위마다 콤마로 구분하고 '원'이라는 단위를 표시하는 형태로 변환해본다고 가정해봅시다.
먼저 간단하게 정수를 인자로 받고 반환 타입이 문자열인 함수를 만들어 볼 수 있겠죠.
formatNumbWithWonSuffix
함수에서는 정규식을 이용해서 천 단위로 콤마(,)를 삽입하고 '원' 단위로 붙이는 기능을 수행하고 있습니다.
하지만 만약 해당 포맷팅 로직이 여러 곳에서 자주 사용
된다면 어떻게 할까요?
물론 매번 함수를 작성하거나, 해당 함수를 전역
으로 관리하는 것도 하나의 방법일 수 있겠지만
조금 더 Fancy한 방법이 있을 것 같습니다.
Dart 2.6버전에 추가된 Extension
키워드를 이용하면 보다 좀 더 직관적으로 포맷팅 로직을 구현할 수 있습니다.
그럼 동일하게 정수를 천 단위로 나누고 '원' 단위를 붙이는 로직의 extension 활용 버전을 확인해봅시다.
extension NumForammter on int {
String get numWithWonSuffix {
String numberStr = '$this';
RegExp regExp = RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))');
final foramattedNum = numberStr.replaceAllMapped(
regExp,
(Match match) => '${match[1]},',
);
return foramattedNum + '원';
}
}
void main() {
int number = 1000000;
print(number.numWithWonSuffix); // 출력: 1,000,000
}
실행 함수 부분을 보시면 정수에 접근 연산자
구문으로 쉽게 포맷팅 기능을 실행하고 있습니다.
더 직관적인 형태죠.
이처럼 Dart에서 일반 메서드와 함께 확장 메소드를 제안
하는 Extension
에서 키워를 이용하면 조금 더 가독성 고려해 데이터를 포맷팅 로직을 구성할 수 있고 전역
으로 사용할 수 있기 때문에 편리함을 제공하기도 합니다.
또한 단순히 포맷팅 로직뿐만 아니라 다양한 Operating로직
도 구현이 가능합니다.
extension
사용 유무에 따른 예시를 통해 알아보죠.
유저의 아이디의 최소 글자 수(최소 7자 이상)를 제한하는 클래스를 구현한다고 가정합니다.
String Extension 사용 여부에 따라 아래와 같이 구분할 수 있습니다.
class UserIdValidation {
static bool isValidLenght(String str) {
return str.length >= 7 ? true : false;
}
}
class UserIdValidation {
static bool isValidLenght(String str) {
return str.length >= 7 ? true : false;
}
}
void main() {
UserIdValidation.isValidLenght("vkdl370528") // true
UserIdValidation.isValidLenght("Ximya") // false
}
extension UserIdValidation on String {
bool get isIdValidLength {
return this.length >= 7 ? true : false;
}
}
void main() {
"vkdl370528".isValidLenght // true
"Ximya".isValidLength // false
}
on
키워드를 통해 Extend 할 타입을 설정이번에는 두 개의 단어를 더하는 기능을 제공하는 Extension에 대한 예시입니다.
extension StringExtension on String {
String concatWithSpace(String other) {
return '$this $other';
}
}
void main () {
"XimYa".concatWithSpace("Kim") // Ximya Kim
}
$
(dollarSign) 의 키워드로 참조하여 구분함.extension StringExtension on String {
String operator &(String other) => '$this $other';
}
void main() {
"Ximya" & "Kim" // Ximya Kim
}
operator
를 이용하여 좀 더 직관적으로 표현이 가능함.그럼 순삭
앱에서는 어떻게 extension이 활용되었을까요?
순삭에서 Tmdb API를 이용해서 영화 & 드라마 콘텐츠의 이미지 제목 등의 정보를 불러옵니다.
"backdrops": [
{
"aspect_ratio": 1.778,
"height": 2160,
"iso_639_1": null,
"file_path": "/8ZTVqvKDQ8emSGUEMjsS4yHAwrp.jpg", <-- image url
"vote_average": 5.522,
"vote_count": 4,
"width": 3840
},
{
"aspect_ratio": 1.778,
"height": 1080,
"iso_639_1": null,
"file_path": "/s3TBrRGB1iav7gFOCNx3H31MoES.jpg",
"vote_average": 5.396,
"vote_count": 12,
"width": 1920
},
...
위 TMDB API JSON 응답 코드를 보시면 url 이미지 주소가 "./{....}.jpg"로 구성되어 있는데요.
Flutter 이미지 위젯 속성에 값을 전달하기 위해서는 네트워크 이미지 형태가 되어야 합니다.
즉 https://image.tmdb.org/t/p/original
문자열과 서버에서 받아온 image_path
데이터가 결합되어야 하는거죠.
extension TmdbFormatter on String {
String get prefixTmdbImgPath {
return 'https://image.tmdb.org/t/p/original$this';
}
}
CachedNetworkImage(imageUrl: vm.imgUrl.prefixTmdbImgPath)
그래서 저는 TmdbFormatter
extension을 통해 보다 쉽게 image_path
에 필요한 문자열을 쉽게 prefix할 수 있도록 했습니다.
또한 Extension은 Enum
과 같이 사용할 때 굉장히 편리합니다.
순삭에서는 ContentType
이라는 Enum 클래스를 정의하여 콘텐츠의 영화
& 드라마
타입을 구분하고 있습니다.
enum ContentType {
tv('드라마'),
movie('영화');
const ContentType(this.name);
final String name;
factory ContentType.fromString(String originStr) {
switch (originStr) {
case 'tv':
return ContentType.tv;
case 't':
return ContentType.tv;
case 'movie':
return ContentType.movie;
case 'm':
return ContentType.movie;
default:
throw Exception('enum not found');
}
}
}
순삭앱의 여러 핵심 도메인 로직에서 컨텐츠 타입을 판단하는 로직
이 사용됩니다.
final ContentType contentA;
예를 들어, contentA
변수의 타입이 movie인지 tv인지 판단하기 위해서 비교연산자를 이용할 수 있겠지만,
if(contentA == ContentType.movie) {
... some method
}
ContentType
의 조건을 판별하는 extension을 만들어준다면
extension DeterminContentType on ContentType {
bool get isMovie {
return this == ContentType.movie ? true : false;
}
bool get isTv {
return this == ContentType.tv ? true : false;
}
}
아래와 같이 조금 더 직관적으로 비교 연산 구문을 작성할 수 있습니다.
if(contentA.isMovie) {
... some method
}
이번 글에서 extension 키워드를 활용해서 조금 더 직관적인 operating, formatting 등의 로직을 작성하는 방법에 대해 다루어봤습니다.
사실 extension이 없더라도 기능 명세를 구현하는데 큰 제약이 없지만,
조금 더 코드의 가독성과 효율적인 구조를 위해 extension
을 적극 도입하는 게 좋다고 생각합니다😉
좋은 글 잘 보고 갑니다~ 정리를 잘해주셔서 도움이 되네요 extension은 잘 사용 중이었는데 operator 새로 알아갑니다!