기본 콘텐츠로 건너뛰기

10월, 2023의 게시물 표시

async / await 사용 해도 될까?

  제목이 어그로성이긴 하지만... 이번 포스팅의 내용은 async / await을 사용하는 경우 놓칠 만한 오류가 있으니, 주의하자라는 내용을 담고자 하는 내용이니, 참고하자.  Array.protoype.*   // server api const checkValidation = async (v) => v % 2 const filteredList = [ 1 , 2 , 3 , 4 ]. filter ( async (v) => checkValidation (v) ) console . log ( 'filteredList' , filteredList ) // [1, 2, 3, 4]  위의 결과를 생각을 한 번 해보도록 하자, checkValidation이라는 서버 api가 있을 때, 이 처럼 개발되어있다는 것은 filteredList에 [1, 3]과 같이 홀수만 남아 있기를 원하는 것일 것이다.  하지만, filter입장에서는 async 키워드가 달려있는 순간, Promise를 리턴 받았으니, trucy값으로 판단 하여, [1, 2, 3, 4] 값을 리턴 하게 된다. Promise . resolve ([ 1 , 2 , 3 , 4 ]) . then ( list => list. reduce ( async (acc, v) => { acc = await acc if ( await checkValidation (v) ) acc. push ( v ) return acc }, Promise . resolve ( [] ) ) ) . then ( console . log ) // [1, 3]  정상적으로 홀수만 반환 받기를 원한다면, 위 처럼 Promise객체 기반으로 작동 할 수 있도록 작성하는 것이 맞다.  filter함수는 promise를 리턴 받는 순간, trucy로 인식하기 때문에 사용을 하는 순간 재기능을 할 수가 없다.  언젠가는  AsyncIterator 기반의 컬렉션

Object.groupBy (배열 그륩핑)

 배열을 다루다 보면, 그륩핑을 해야하는 경우가 있다.  뭐 그러다 보면 아래처럼 불편한 코드가 생성 될 것이다. const list = [ { class : 'a' , score : 1 }, { class : 'a' , score : 2 }, { class : 'a' , score : 3 }, { class : 'a' , score : 4 }, { class : 'b' , score : 1 }, { class : 'b' , score : 1 }, ] const group = {}; for ( const item of list ) { group [ item . class ] = group [ item . class ] || [] group [ item . class ]. push ( item ) } console . log ( 'group' , group )  뭐 reduce를 사용 하게 되면 아래 코드 처럼 되겟지... const list = [ { class : 'a' , score : 1 }, { class : 'a' , score : 2 }, { class : 'a' , score : 3 }, { class : 'a' , score : 4 }, { class : 'b' , score : 1 }, { class : 'b' , score : 1 }, ] const group = list . reduce ( (groupRes, item) => { groupRes[item. class ] = groupRes[item. class ] || [] groupRes[item. class ]. push (item) return groupRes }, {} ) co

array.with

 요즘, 자바스크립트 array에 추가되는 함수들을 보면, 변경 메서드를 대체하는 함수들이 추가 되고 있다. 예를 들면, sort메서드와 toSorted를 확인해보면 아래와 같다.  위 처럼 기존의 데이터를 변경하지 않는 것은 개발자들이라면, 얼마나 큰 의미가 있는지 알고 있을 것이다. 아마 조만간 sort함수를 쓰지말라는 개발자들이 나오겠지... 뭐 물론, 이미 데이터를 다루는 라이브러리를 쓰고 있다면, sort함수가 기존의 데이터를 변경 하지 않는 다는 것을 알고 있을 것이다.  이러한 array.with는 가장 기본적인 배열 내 속성의 할당에 관한 함수이다. 우리는 여태까지 배열내의 값을 수정을 하기 위하여, arr[1] = 3 이와 같이 진행 했을 것이다. 그렇다면, arr의 1번째 값은 3으로 변경 될 것이다.  하지만, 여러 곳에서 사용하는 데이터의 경우 해당 데이터의 변경이 어떻게 영향을 줄지 모르기에, 데이터를 변경하기 전, 복사를 하고 사용 하는 경우가 많다.  이럴때 사용 할 수 있는 함수가 array.with함수인 것이다.  위와 같은 방식이다.  뭐 물론 대괄호 표기법의 대체수단으로써만 생각하면, 큰 위화감은 없으나... 아래와 같이 여러 값을 변경 하는 경우가 문제가 된다.  코드 스타일이 계속된 함수 실행으로 인하여, 비용이 올라가며, with함수를 실행 할 때 마다, 새로운 레퍼런스를 제공받기 때문에 이 비용도 꽤 클 것으로 생각 된다.  변경 -> 복사로  뭐 물론 해당 함수가 추가 된 것은 원본 객체의 오염을 막기 위한 것이라는 것을 알 수 있다. 같은 시기에 추가된 함수가 아래의 함수들처럼 기존 객체의 값을 막고, 복사 방식이라는 것을 보면 말이다. Array.prototype.toReversed() = Array.prototype.reverse() Array.prototype.toSorted() = Array.prototype.sort() Array.prototype.toSpliced() = Array.prototype.

공허참 (feat.javascript)

 지금 숫자 몇 개만 생각해보자.  두개의 질문을 던저보자.   1. 모든 수가 양수인가? 2. 모든 수가 음수인가?  이 질문에, 답변 될 수 있는 케이스는 아래와 같다.     모든 수가 양수이다. 모든 수가 음수이다. 1 false false 2 false true 3 true false 4 true true  1번 케이스의 경우에는 양수와 음수가 섞여있을 것이고,  2, 3번 케이스는 상상하기 쉬울 것이다.  4번 케이스는 무엇일까? 대부분은 이 케이스겠지만......  자 잠깐만, 고등학생 때로 돌아가 보도록 하자. 수학의 정석을 1장을 열시히 읽던 그때로 말이다. 아마 내 생각에는 집합과 명제부분이였던걸로 기억한다.  x < 0인 음수만 있는 집합 N  x >= 0인 양수만 있는 집합 P  두 집합이 있다고 하였을 때, 방금 전에 떠올린 집합 Q가 있다고 하였을 때,  위의 케이스들은 아래처럼 표현 할 수 있는 것이다.  1. N ∩ Q = Q - P이고, P ∩ Q = Q - N이다.  2. N ∩ Q = ∅이고, P ∩ Q = Q이다.  3. N ∩ Q = Q이고, P ∩ Q = ∅이다.  4. N ∩ Q = Q이고, P ∩ Q = Q이다.  자 이렇게 표현해보니, 4번의 케이스의 경우 Q = ∅인 것을 눈치를 챘을 것이다. 즉 아무런 숫자도 생각을 안 했다면, 4번의 케이스가 된다는 것이다.  자 간단하게, 이제 수학 말고, 코드로 따져 보도록 하자. console . log ( []. every (v => v >= 0 ) ) // true console . log ( []. every (v => v < 0 ) ) // true  우리는 every를 사용 할 때, 빈 배열인 경우 true 값이 나온다는 것을 알아야 한다.  => 배열이 비어있는 경우 실행 하면 안되는 코드를 실행하면 안된다! https://tc39.es/ecma262/multipage/indexed-collections.html#sec-ar

Set.has vs Array.includes

  개발을 하다보면, 특정데이터가 배열 안에 있는지 확인해야 하는 경우가 있다. 그것도 자주... 그런경우 당연하게도, arr.includes를 이용 하는 경우가 많은데, set.has가 더 빠르다 이런식의 인터넷글들을 확인 해보았을 것이다.  특히나, 뭐 면접에서 set.has의 시간복잡도가 얼마냐? 이런식의 질문도 받아본 사람도 있을 것이다.  set.has  set의 경우 해시 테이블 구조로, 동일한 값을 같지 못한다. 따라서, set.has는 해당 속성이 있는가 없는가 수준을 O(1) 수준의 시간복잡도를 갖는다. 뭐 물론, 데이터가 많은 경우나 충돌이 나는 경우 처리 등등 뭐 문제가 일어나는 경우 그것보단 더 높아질 수도 있겠지... 일단 O(1)로 생각 하면 된다.  Array.includes  Array.includes의 스펙은 위와 같다. 딱 봐도, 로직이 길고, 배열의 길이만큼 순회를 하는 것을 볼 수 있다. 그래서 일반적으로 O(N)이라고 생각하는 것이 편하겠다.  자 문제는 여기서 발생한다. 당연하게도, set.has가 월등히 빠르다고 느껴지고 빠르다. 하지만, 실제 업무를 하는 경우에는 어떠한가? 생각을 해보아야 할 것이다.  뭐 알고리즘을 푸는 경우라면, Set을 이용하여, 값의 포함여부를 판단하는 것이 대부분의 상황에서 이득이 될 것이다. 하지만.... 서비스를 제작하는 경우 백엔드와 프런트엔드의 연동이 필요할 것이다. 이런 상황에서 문제가 되는 것은 api의 응답으로 Set을 줄 수 없다.  일반적으로 JSON형태로 데이터를 전달을 할 것이다. 즉 초기값이 배열일 것이다 라는 말이다. 이때 특정값의 포함여부를 확인 하기위하여, Set으로 데이터를 한 번 변경 하는 것은 꽤나 큰 작업이다.  위의 경우 같은 상황에서 Set으로 한 번 데이터 형태를 변환하는 것이 얼마나 큰 리소스를 사용하는지 볼 수 있을 것이다. 즉 상황에 따라서 잘 사용해야지, 무조건 빠르다고 해서 사용하는 것은 문제가 될 수 있다.