Search
Duplicate

Maximum call stack size exceeded 오류

생성일
2023/02/09 06:14
태그
JS

Maximum call stack size exceeded 오류

Maximum call stack size exceeded 오류는 재귀 함수를 사용하다 보면 자주 볼 수 있는 오류이다.
호출 스택의 최대 크기를 초과했을 때, 발생하는 오류인데 왜 이런 오류가 발생할까?
function countMine(rowIndex, cellIndex) { const mines = [CODE.MINE, CODE.QUESTION_MINE, CODE.FLAG_MINE]; let i = 0; mines.includes(data[rowIndex - 1]?.[cellIndex - 1]) && i++; mines.includes(data[rowIndex - 1]?.[cellIndex]) && i++; mines.includes(data[rowIndex - 1]?.[cellIndex + 1]) && i++; mines.includes(data[rowIndex][cellIndex - 1]) && i++; mines.includes(data[rowIndex][cellIndex + 1]) && i++; mines.includes(data[rowIndex + 1]?.[cellIndex - 1]) && i++; mines.includes(data[rowIndex + 1]?.[cellIndex]) && i++; mines.includes(data[rowIndex + 1]?.[cellIndex + 1]) && i++; return i; } function open(rowIndex, cellIndex) { const target = $tbody.children[rowIndex]?.children[cellIndex]; if (!target) { return; } const count = countMine(rowIndex, cellIndex); target.textContent = count || ''; target.className = 'opened'; data[rowIndex][cellIndex] = count; return count; } function openAround(rI, cI) { const count = open(rI, cI); if (count === 0) { openAround(rI - 1, cI - 1); openAround(rI - 1, cI); openAround(rI - 1, cI + 1); openAround(rI, cI - 1); openAround(rI, cI + 1); openAround(rI + 1, cI - 1); openAround(rI + 1, cI); openAround(rI + 1, cI + 1); } } function onLeftLclick(event) { const target = event.target; // td 태그 const rowIndex = target.parentNode.rowIndex; const cellIndex = target.cellIndex; const cellData = data[rowIndex][cellIndex]; if (cellData === CODE.NORML) { // 닫힌 칸이면 openAround(rowIndex, cellIndex); /* const count = countMine(rowIndex, cellIndex); target.textContent = count || ''; // 있으면 앞에꺼, 없으면 뒤에꺼 target.className = 'opened'; data[rowIndex][cellIndex] = count; */ } else if (cellData === CODE.MINE) { // 지뢰 칸이면 // 펑 target.textContent = '펑!'; target.className = 'opened'; $body.removeEventListener('contextmenu', onRightClick); $body.removeEventListener('click', onLeftClick); } // 나머지는 무시 }
JavaScript
복사
onLeftClick 함수가 제일 먼저 호출되고, 그 안에서 openAround 함수가 호출된다. 따라서 호출 스택에서는 onLeftClick 함수 위에 openAround 함수가 올라가 있다. 그 다음에 open 함수가 호출 스택으로 들어간다.
open 함수는 openAround 함수보다 먼저 종료되므로 호출 스택에서 빠져 나오지만, openAround 함수는 종료되기 전에 openAround 함수를 호출하므로 빠져 나오지 못한다.
openAround 안의 openAround 함수는 또다른 openAround 함수를 호출하게 되고, 이렇게 끊임없이 위로 쌓이다가 호출 스택 최대 크기를 초과하게 된다.
브라우저별로 호출 스택의 최대 크기가 다르다. 다음 코드를 복사해 붙여 넣으면 브라우저의 최대 호출 스택 크기를 알 수 있다.
let i = 0; function recurse() { i++; recurse(); } try { recurse(); } catch(ex) { alert('최대 크기는 ' + i + '\nerror: ' + ex); }
JavaScript
복사
이 문제를 해결하는 방법은 많지만, 가장 간단한 방법은 비동기 함수를 사용하는 것이다. 재귀 함수의 내부를 가장 대표적인 비동기 함수인 setTimeout으로 감는 것. 시간은 0초로 해서 즉시 호출되게 한다.
→ 호출 스택과 이벤트 루프를 잘 이해하면 이 에러를 해결할 수 있다.
호출 스택 함수를 백그라운드와 태스크 큐로 넘겨주자! (부담을 덜어준다)
function openAround(rI, cI) { setTimeout(() => { const count = open(rI, cI); if (count === 0) { openAround(rI - 1, cI - 1); openAround(rI - 1, cI); openAround(rI - 1, cI + 1); openAround(rI, cI - 1); openAround(rI, cI + 1); openAround(rI + 1, cI - 1); openAround(rI + 1, cI); openAround(rI + 1, cI + 1); } }, 0); }
JavaScript
복사
→ 이 함수가 태스크 큐로 넘어가는 함수이다