const data = [1, 2, [3, 4, [5, [6]]]];
위와 같은 중첩 배열을 아래와 같이 정보를 담아 트리 구조로 출력하려면 어떻게 해야할까?
나는 이 문제를 해결하기 위해 재귀함수를 사용했다.
재귀함수의 사전적 정의는 정의 단계에서 자신을 재참조하는 함수
이다.
재귀함수에 대하여 이해도가 높지 않았기에, 콘솔에 중간 결과값을 찍어가고 시각화하며 이해하려고 노력하였다.
일단 코드부터 살펴보자.
parseArray라는 함수 안에 동일한 parseArray 함수가 재귀호출되고 있다.
최종적으로 stack이라는 빈 배열에 출력하는 값을 모두 담으려고 한다. 그리고 stack에 담을 값들을 저장하는 변수로 tempStack을 정의했다.
인수로 전달된 data라는 중첩 배열에 대하여 forEach 메서드가 요소 하나 하나에 대하여 정해진 프로세스를 진행한다.
그 프로세스는 전달된 요소의 데이터 타입이 number인지 array인지 구별하는 것이며, number라면 tempStack에 { type: 'number', value: i, child: [] }
를 저장하고, array라면 tempStack에 { type: 'array', child: parseArray(i) }
를 저장한다. 그러나 array에 저장하는 tempStack의 값은 인자로 해당 요소를 전달하여 호출된 재귀함수로부터 반환된 값을 받아서 완성된 것이다.
재귀함수에 대한 이야기는 조금 이따가 다시 하기로 하고, 일단 if문 또는 else if 문을 거쳐 새로이 값을 받은 tempStack은 stack 배열에 push되고, data의 모든 요소가 forEach문을 돌 때까지 위 프로세스가 반복된다. 마침내 forEach문을 다 돌고 나서 stack 값이 return 된다.
하지만 위 설명만으로는 중간에 재귀함수의 작동 과정을 이해하기 어렵다. 위 설명에서 중간에 있는 그러나 array에 저장하는 tempStack의 값은 인자로 해당 요소를 전달하여 호출된 재귀함수로부터 반환된 값을 받아서 완성된 것이다.
이 내용을 심층적으로 확인하기 위해, 콘솔을 세 군데에 찍어서 보았다.
@1@ 콘솔내용은 요소의 타입이 number일 때 저장되는 tempStack이고, #2# 콘솔내용은 요소의 타입이 array일 때 저장된 tempStack이다. 은 조건문 밖에서 tempStack 값이 stack에 push되고 나서 stack 값이다.
콘솔을 찍은 결과 위와 같이 출력되었는데, 그 위에 재귀함수 차원에 따라 시각화를 해보았다. 함수가 여러 번 재귀 호출되었으므로 구분을 위해 차원이라고 표현해보겠다. 1차원인 메인함수(회색) 안에서 2차원 재귀함수(초록색)가 호출되었고, 그 2차원의 재귀함수 안에서 다시 3차원의 재귀함수(빨간색)가 호출되고, 3차원의 재귀함수 안에서 4차원의 재귀함수(보라색)가 호출된 것이다. 재귀함수가 호출되는 경우는 else if 문 조건인 Array인 경우이므로, 우리가 원하는 결과값인 Array 밑에 number가 들어오는 트리 구조를 만들어 낼 수 있을 것이라고 예상할 수 있다.
<호출순서>
1차원(메인함수)->2차원(재귀함수)->3차원(재귀함수)->4차원(재귀함수)
위와 같은 순서로 호출하였고, 각 함수의 실행 및 결과값은 제일 마지막에 불린 4차원부터 반환하여 자신을 호출한 직전 차원에 넘겨준다.
<실행순서>
4차원 값->3차원 값->2차원 값->1차원
적절한 비유가 아닐 수도 있지만, 마치 공상과학소설처럼 1차원의 내가 무언가 필요한 것이 있어 나와 똑같은 나를 복사하여 다른 차원에 보내고, 이후 2차원의 나로부터 1차원의 내가 값을 전달받는 것이라고 상상해보았다. 그리고 2차원의 나도 막상 2차원에 가보니 다시 또 필요한 것이 있어 3차원의 나를 부르고 그로부터 값을 받는 것이다.
또 재귀함수에 대하여 이런 상상을 해봤는데, 엘리베이터에서 거울이 마주본 곳 사이에 서서 한쪽 거울을 자세히 보면 거울 속 내가 무한하게 재귀되어 반복되고 있는 것을 볼 수 있다. 즉, 거울의 호출을 통해 내가 무한하게 재귀되는 것과 같다.프로그래밍에서 이렇게 무한히 재귀된다면 stack over flow가 나기 때문에, 재귀가 끝나는 조건을 설정해줘야 한다.
위에서 살펴봤던 재귀함수 콘솔을 다시 시각화해본다면 아래와 같다.
재귀함수 호출순서와 재귀함수 실행순서는 반대다.
재귀는 스택과 같은 LIFO(Last In, First Out)이다.