어떤 한 회사에서 라이브 코딩으로 면접을 보게 되었다.
과제는 정말 간단했다. 주어진 시간 동안 React를 이용한 상태 관리를 구현하면 끝이었다.
그러나 면접 질문은 그리 간단하지 않았다.
아니, 어쩌면 정말 간단한 함수들에 대한 질문이었는데 내가 제대로 답하지 못하였다.
결과는 탈락이었지만 그래도 회고겸 공부를 위해 글을 작성하였다.
 

JS 배열 내장 함수


탐색 및 검색

includes
특정 요소가 배열에 존재하는지 확인 (true/false 반환)
indexOf
요소의 첫 번째 인덱스를 반환 (없으면 -1)
lastIndexOf
요소의 마지막 인덱스를 반환
find
조건을 만족하는 첫 번째 요소를 반환
findIndex
조건을 만족하는 첫 번째 요소의 인덱스를 반환
some
조건을 만족하는 요소가 하나라도 있으면 true
every
모든 요소가 조건을 만족하면 true
at
음수 인데스로도 접근 가능 (새로운 ES 기능)
const arr = [1, 2, 3, 4, 5, 1]; // result = 0 arr.indexOf(1); // result = 5 arr.lastIndexOf(1); // result = 1 arr.at(-1); arr.at(0) == arr[0]
 
그렇다면 arr.at(0)과 arr[0] 중 어떤 것이 더 좋을까?
 
const arr = Array(1_000_000).fill(1); // 테스트: arr.at(0) console.time('at'); for (let i = 0; i < 1_000_000; i++) { const y = arr.at(0); } console.timeEnd('at'); // 테스트: arr[0] console.time('bracket'); for (let i = 0; i < 1_000_000; i++) { const x = arr[0]; } console.timeEnd('bracket');
위 코드처럼 테스트 코드를 만들어 퍼포먼스 시간을 비교해보았으나 둘 다 비슷했다!
하지만 at 함수는 ES2022부터 생긴 기능이므로 오래된 버전에서는 기능을 안할 수도 있다.
 
대체로 callback 인자들은 (element, index, array) 이다.
const scores = [30, 45, 55, 60, 40, 80]; // 맨 앞에 있으니깐 결국 score는 arr의 element 인 것이다. scores.find((score) => (score > 50)); scores.find((element, index, array) => { console.log(`index: ${index}, value: ${element}, array: [${array}]`); return index % 2 === 0 && element >= 50; }; // true scores.some((value) => value > 50) // false scores.every((value) => value > 50)
 

변형 및 가공

reduce 함수의 callback 인자는 (accumulator, currentValue, currentIndex, array)이다.
map
각 요소를 변형하여 새 배열을 생성
filter
조건을 만족하는 요소만으로 새 배열을 생성
reduce
누적 계산을 통해 하나의 값으로 축약
reduceRight
오른쪽에서 왼쪽으로 누적 계산
flat
다차원 배열을 평탄화
flatMap
map + flat(1)
sort
배열 정렬
reverse
배열 순서를 뒤집음
const arr1 = [1,2,3,4,5]; // arr2 = [2,3,4,5,6] const arr2 = arr1.map((element) => element + 1); // arr3 = [3,4] const arr3 = arr.filter((element, index) => index > 1 && element < 5); // result = 28 const result = arr.reduce((accumulator, element) => accumulator + element, 0);
 
const arr = [1, [2, [3, [4, 5]]]]; // = arr.flat() arr.flat(1); // [1, 2, [3, [4, 5]]] arr.flat(2); // [1, 2, 3, [4, 5]] arr.flat(3); // [1, 2, 3, 4, 5]
 

요소 추가 및 제거

push
마지막에 요소 추가
pop
마지막 요소 제거
unshift
처음에 요소 추가
shift
첫 요소 제거
splice
요소를 추가/제거/교체
slice
배열의 일부분을 잘라 새 배열 생성 (원본 변경 x)
 

결합 및 복사

concat
두 배열을 이어붙여 새 배열 생성
join
배열을 문자열로 변환
toString
배열을 쉼표로 구분된 문자열로 반환
copyWithin
배열 내 특정 위치로 값을 복사
fill
배열의 모든 요소를 특정 값으로 채움. fill 안에 인자가 없으면 undefinded로 할당
from
 배열이나 유사 배열(array-like)을 새 배열로 변환
const arr = new Array(10).fill(0). const str = "hello"; const chars = Array.from(str); // ['h', 'e', 'l', 'l', 'o']
new를 쓰지 않으면 어떻게 될까?
new를 사용하면 생성자 호출 방식, 안쓰면 함수 호출 방식이지만 동일한 역할을 한다.
 
const arr = Array.from({ length: 10 }, (_, i) => i); console.log(arr); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] const arr = Array(10).fill(0).map((_, i) => i);
이 둘은 성능 차이는 거의 없지만,
Array.from은 의도와 구조가 명확해서 협업 시 더 추천되는 경우가 많다고 한다.
 

반복

forEach
각 요소에 대해 함수를 실행
const arr = [1,2,3,4,5]; arr.forEach((element, index) => {arr[index] = element + 1}); arr.forEach((element, index, array) => {array[index] = element + 1};
위의 두 코드의 차이는 뭘까?
사실 성능의 차이는 없지만, 내가 생각했을 때는 아래 방법이 더 좋은 것 같다.
위의 방법의 경우 원하지 않았던 arr (이름이 같은 변수) 을 가져오거나 실수로 다른 이름을 적을 수도 있다.
 

정리하며,


지금 나는 이 글을 쓰면서 같은 기능을 하는 코드들의 차이를 분석해보았다.
실제로 이런 질문이 면접에서 나왔었고, 답은 없겠지만 각각의 장단점 또는 이유는 말할 수 있어야 한다.
같은 기능을 만드는데 정말 다양한 방법으로 코딩할 수 있을 것이다.
개발자는 이렇게 서로 다른 방법들을 알고 있어야하며 각각의 차이를 알아야 한다 생각한다.