-
[Javascript/lib.] LodashFront-End(Web)/Javascript 2021. 8. 17. 21:45반응형
🧐 서론
lodash 역시 회사 실무를 접하면서, 처음으로 경험해본 Javascript 라이브러리이다.
사실, Javascript에서 제공하는 왠만한 메서드들을 표방한 것이다보니 필요성이 있을까 싶지만,
_cloneDeep() 으로 객체를 깊은 복사하거나, _has() 로 객체의 필드 포함여부를 확인하는 등 다양한 기능을 수행할 수 있다.
현재 실무에서는, 더 다양한 Lodash 메서드들을 사용하고 있기 때문에, 이의 기원과 대표적인 메서드들을 정리해보고자 이 글을 포스팅한다.
📒 Lodash 란?
Lodash는 Javascript 유틸리티 라이브러리의 일종이다. 즉, 모듈성, 성능 등을 제공하기 위한 라이브러리인 것이다.
* Lodash는, node.js 에서 많이 쓰이는 underscore 라이브러리에서 영감을 받았다.
(참고링크 : https://m.blog.naver.com/sssang97/222014670813 )
Lodash의 가장 큰 의의는 Array, Object, String 등 데이터 내 값들을 Handling 하는 것을 용이하게 해준다는 점이다.
Lodash는 코드가 간결해진다는 장점 외에도, 아래와 같은 다양한 강점을 보이기에 React를 포함한 JS환경에서 환영받고 있다.
- 기존 Javascript 메서드로 작성하는 것에 비해 코드가 간결해짐
- Javascript에서 지원하지 않는 다양한 메서드들을 가지고 잇음
- 퍼포먼스 측면에서 native 메서드보다 더 나은 성능을 보임
- 설치
마찬가지로, CDN과 npm 환경 2가지 방법으로 설치할 수 있다.
// CDN <script src="https://raw.githubusercontent.com/lodash/lodash/4.17.15-npm/lodash.min.js"></script>
// npm npm i --save lodash yarn add lodash
- 사용
lodash를 import 하거나, 필요한 메서드만 가져올수도 있다.
import _ from "lodash"; import _get from "lodash/get";
// node.js var _ = require('lodash'); var _ = require('lodash/core');
📒 Lodash 주요 메서드
Lodash 에서는 Array, Object, Collection(배열, 객체 모두), Util(유틸리티 관련) 등 카테고리들로 분류한다.
1. Array
_.chunk (array, [size = 1])
1차원 배열을 2차원 배열로 수정할 때 유용하다. 첫 번째 인자는 타겟 배열, 두 번째 인자는 각 2차원 배열의 원소개수가 된다.
_.chunk(['a', 'b', 'c', 'd'], 2); // => [['a', 'b'], ['c', 'd']] _.chunk(['a', 'b', 'c', 'd'], 3); // => [['a', 'b', 'c'], ['d']]
_.findIndex(array, [predicate=_.identity(function)], [fromIndex = 0(number)]) / _.findLastIndex
조건을 만족하는 첫 번째 요소의 인덱스를 반환한다.
첫 번째 인자는 타겟 배열, 두 번째 인자는 조건 함수, 세 번째 인자는 시작 인덱스이다.
var users = [ { 'user': 'barney', 'active': false }, { 'user': 'fred', 'active': false }, { 'user': 'pebbles', 'active': true } ]; _.findIndex(users, function(o) { return o.user == 'barney'; }); // => 0 // The `_.matches` iteratee shorthand. _.findIndex(users, { 'user': 'fred', 'active': false }); // => 1
_.uniq(array) / _.uniqBy(array, [iteratee=_.identity(function)])
uniq는 배열 요소들의 중복값을 제거한 배열을, uniqBy는 배열의 객체 요소들의 중복값을 제거할 때 사용한다.
_.uniq([2, 1, 2]); // => [2, 1] _.uniqBy([2.1, 1.2, 2.3], Math.floor); // => [2.1, 1.2] // The `_.property` iteratee shorthand. _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); // => [{ 'x': 1 }, { 'x': 2 }]
2. Object
_.get(object, path, [defaultValue]) / _.has(object, path)
get은 해당하는 경로의 값을 가져오는 것이다. has는 객체에 해당 경로가 존재하는지 여부를 반환한다.
var object = { 'a': [{ 'b': { 'c': 3 } }] }; _.get(object, 'a[0].b.c'); // => 3 _.get(object, ['a', '0', 'b', 'c']); // => 3 var object = { 'a': { 'b': 2 } }; var other = _.create({ 'a': _.create({ 'b': 2 }) }); _.has(object, 'a'); // => true _.has(object, 'a.b'); // => true _.has(object, ['a', 'b']); // => true _.has(other, 'a'); // => false
_.omit(object, [paths]) / _pick(object, [paths])
객체의 filter() 메서드라고 생각하면 되겠다. 객체에 해당하는 키를 제외하는 omit()과 키만 선택하는 pick()이 있다.
var object = { 'a': 1, 'b': '2', 'c': 3 }; _.omit(object, ['a', 'c']); // => { 'b': '2' } _.pick(object, ['a', 'c']); // => { 'a': 1, 'c': 3 }
이 역시도, [paths] 대신 value를 판정하는 함수를 인자로 받는 _.omitBy(), _.pickBy() 등이 있다.
_.unset(object, path)
해당 경로의 value를 삭제하는 메서드이다. 독특하게 반환값은 boolean 이며, 메서드 적용 후 원본객체는 수정되어있다.
var object = { 'a': [{ 'b': { 'c': 7 } }] }; _.unset(object, 'a[0].b.c'); // => true console.log(object); // => { 'a': [{ 'b': {} }] }; _.unset(object, ['a', '0', 'b', 'c']); // => true console.log(object); // => { 'a': [{ 'b': {} }] };
3. Collection
_.forEach, _.filter, _.includes, _.map, _.reduce 등 우리가 흔히 사용해온 Javascript 메서드들을 포함하고 있다.
4. Date
_.now() : 현재 시간을 milliseconds 단위로 반환한다.
5. Function
_.bind(function, thisArg, [partials])
함수에 매개변수를 this 바인딩하는 메서드이다. thisArg를 통해 객체의 각 키값들을 this로 넘길 수 있으며, partials는 함수의 추가적인 매개변수들에 해당한다.
function greet(greeting, punctuation) { return greeting + ' ' + this.user + punctuation; } var object = { 'user': 'fred' }; var bound = _.bind(greet, object, 'hi'); bound('!'); // => 'hi fred!' // Bound with placeholders. var bound = _.bind(greet, object, _, '!'); bound('hi'); // => 'hi fred!'
_.debounce(function, [wait=0], [options={ }])
디바운스 메서드를 통해, 일정시간(wait) 동안 연이어 호출되는 함수들 중 마지막 함수만 호출되도록 하는 것이다.
* 디바운싱(Debouncing) : 연이어 호출되는 함수들 중 마지막 함수만 호출되도록 하는 것 (최종결과가 중요할 때 사용)
- function(Function) : 디바운스가 걸리는 동작. 콜백함수이다.
- [wait](Number) : 디바운스를 제한하기 위한 시간. 숫자 타입으로, millisecond 단위로 입력해주면 된다.
- [options](Object) : 디바운스 옵션 객체. (leading, maxWait, trailing)
const sendQuery = async (query: string) => { if (query.length === 0) return; try { const res = await apiCall.get(`url/search?query=${query}`); setData(res?.data); } catch(err) console.log(err); }; const dbcQuery = useRef(_.debounce((q) => sendQuery(q), 500)).current; const handleChangeSearch = (event: React.ChangeEvent<any>) => { setSearchValue(event.target.value); dbcQuery(event.target.value); if (searchValue.trim() === '') { setData({ stores: [], nearbys: [], }); } };
단순 API 예시보다는 블로그의 좋은 예시가 있어 차용해와봤다. _.debounce() 를 통해 0.5초마다 데이터를 최신화하는 로직이었다.
[searchValue, setSearchValue]는 input 값의 최신화가 목적인듯하며, [data, setData] 를 다루는 부분이 중점이다.
sendQuery() 메서드가 비동기 요청을 전담하며, dbcQuery() 의 경우에는 쿼리요청을 0.5초 간격으로만 보내도록 디바운싱해준다.
_.throttle(function, [wait=0], [options={ }])
쓰로틀링 메서드를 통해, 마지막 함수가 호출되고나서 일정시간(wait)이 지나기 전까지 재호출하지 않는다.
* 쓰로틀링(Throttling) : 마지막 함수가 호출된 후 일정시간이 지나기 전까지 다시 호출되지 않도록 하는것(스크롤, 드래그 등 즉각적임)
- function(Function) : 디바운스가 걸리는 동작. 콜백함수이다.
- [wait](Number) : 디바운스를 제한하기 위한 시간. 숫자 타입으로, millisecond 단위로 입력해주면 된다.
- [options](Object) : 디바운스 옵션 객체. (leading, maxWait, trailing)
kako.maps.event.addListener( _map, 'bounds_changed', _.throttle(() => { const bounds = _map.getBounds(); const swLatLng = bounds.getSouthWest(); const neLatLng = bounds.getNorthEast(); setMapBounds({ swLatLng, neLatLng }); }, 1000) );
역시, _.throttle() 을 통해 1초마다 카카오맵 API를 통해 좌표값을 최신화하는 로직을 가져와봤다.
* 디바운싱은 아무리 많은 이벤트가 발생해도 특정시간 내에서는 딱 한번만 실행을 시킨다.
반면, 쓰로틀링은 한 번 실행된 뒤 일정한 시간간격으로 호출이 되기 때문에 중간과정이 갱신되어야 할 경우 주로 사용된다.
이외에도, 결과를 저장하는 _.memoize(), 콜스택이 빈 뒤 함수를 실행하는 _.defer(), 한번만 실행하는 _.once() 등이 있다.
6. Lang
_.clone(value) / _.cloneDeep(value)
객체를 복제하는데 통용되는 메서드다. _.clone()은 얕은 복사, _.cloneDeep()은 깊은 복사를 하게 된다.
var objects = [{ 'a': 1 }, { 'b': 2 }]; var shallow = _.clone(objects); console.log(shallow[0] === objects[0]); // => true var deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false
이외에도, 타입을 검증하는 isNumber, isNull 등과, 타입을 변환하는 toNumber, toSring 등과, 숫자를 비교하는 gt, lt 등이 있다.
7. ETC
- Math : 사칙연산, 각종 연산, 소수점 다루는 메서드들
- Number / String : 숫자와 문자열을 다루는 각종 메서드들 (Number: _.random(), String: _.trim())
- Seq : 이터레이터를 다루는 메서드들 (_.chain, _.prototype.toJSON ...)
- Util : 범위를 구하는 _.range, 함수명을 키값으로 재활용하는 _.mixin, 거듭 실행하는 _.times 등 다양한 기능의 메서드들
금방 끝날 줄 알았던 내용이지만 생각보다 포스팅에 할애된 시간이 길어졌다.
다양한 메서드를 효율적으로 가져다쓰는건 숙련도를 요하겠지만, _.findIndex, _.cloneDeep 같은 경우엔 상당히 유용할 것 같다.
또한, 고성능의 기능을 구현하기 위해 디바운스와 쓰로틀링의 개념과 메서드(_.debounce, _.throttle)에 대해서도 새로이 알게되었다.
[출처]
- lodash 공식문서 : https://lodash.com/docs/
- kysung05 님의 블로그 : https://velog.io/@kysung95/%EC%A7%A4%EB%A7%89%EA%B8%80-lodash-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90
- harry the great 님의 블로그 : https://harrythegreat.tistory.com/entry/%EC%96%B8%EB%8D%94%EC%8A%A4%EC%BD%94%EC%96%B4-%EC%A0%95%EB%A6%AC#delay
- edie_ko 님의 블로그(debounce, throttle) : https://velog.io/@edie_ko/React-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%84%B1%EB%8A%A5-%ED%96%A5%EC%83%81-%EC%8B%9C%ED%82%A4%EA%B8%B0-feat.-Lodash-throttle-debounce
반응형'Front-End(Web) > Javascript' 카테고리의 다른 글
[Javascript] ES6 이후 - (2) ES2019, ES2020, ES2021 (0) 2022.02.09 [Javascript] ES6 이후 - (1) ES2016, ES2017, ES2018 (0) 2022.02.08 [Javascript] ES6(ES2015) (0) 2021.04.09 [Javascript] 이벤트(Event) (0) 2021.03.16 [Javascript] 객체의 복사 (깊은 복사, 얕은 복사) (0) 2021.03.12