노트

재귀

Recursion

CS#algorithm · 연결된 개념 7개

쉽게 말하면

재귀는 마트료시카 인형을 여는 것과 같아요. 인형을 열면 더 작은 인형이 나오고 더는 열 게 없는 가장 작은 인형에서 멈추니까, 같은 방법으로 큰 문제를 작게 줄여 풀 수 있어요.

비유가 깨지는 곳 인형은 언젠가 바닥이 나지만 코드의 재귀는 기저 조건을 빼먹거나 입력이 줄지 않으면 끝없이 불려요. 호출은 콜 스택에 쌓여서 너무 깊어지면 스택 오버플로가 나요.

함수가 자기 자신을 다시 호출해 문제를 푸는 방식. 같은 함수를 점점 작은 입력으로 부르다가 더 나눌 필요가 없는 지점(기저 조건, Base Case)에서 멈춘다.

두 가지 필수 조건

  • 기저 조건: 재귀가 끝나는 조건
  • 매번 다른 입력: 호출할 때마다 기저 조건에 가까워져야 한다

둘 중 하나가 빠지면 끝없이 호출되다가 스택 오버플로(Stack Overflow)가 난다. 결과를 return하지 않거나 엉뚱한 값을 돌려주는 것도 흔한 실수다. 호출이 쌓이는 곳이 콜 스택(Call Stack)이기 때문이다.

const sum = (arr: number[]): number => (arr.length === 0 ? 0 : arr[0] + sum(arr.slice(1)))

두 가지 모양

  • 헬퍼 메서드 재귀(Helper Method Recursion): 바깥 함수는 재귀가 아니고, 결과를 모을 변수를 둔 채 안쪽 헬퍼 함수가 재귀한다. 이해하기 쉽다
  • 순수 재귀(Pure Recursion): 매번 새 값을 만들어 반환값으로만 결과를 쌓는다. 배열은 slice·스프레드·concat으로 복사해서 원본을 바꾸지 않는다. 위 sum이 그 예다(다만 slice가 매번 복사하므로 큰 배열엔 비효율적이다)

어디에 쓰나

트리와 그래프 순회(트리 순회, 깊이 우선 탐색 (DFS)), DOM 탐색, 중첩 객체 복사(얕은 복사와 깊은 복사), JSON 직렬화, 분할 정복 알고리즘. 같은 부분 문제를 반복 계산한다면 결과를 저장하는 동적 프로그래밍으로 넘어간다. 깊이가 아주 깊어질 수 있다면 명시적 스택을 쓰는 반복문으로 바꾸는 것이 안전하다.

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 리팩터링 기법 카탈로그

    리팩터링 기법을 무엇을 정리하는지에 따라 묶어 본 지도. 기법마다 거의 항상 반대 방향 기법이 짝으로 있어서(추출↔인라인, 올리기↔내리기) 상황에 따라 양쪽으로 오간다. 아래 묶음은 Refactoring.Guru 카탈로그(1판 기반)의 분류를 따랐고, 기법 이름은 2판 기준으로 적었다.

  • 구성요소 줄이기

    셀 수 있는 모든 구성요소(파일, 폴더 깊이, 클래스, 함수, 분기, 변수, 테이블, 필드, 테스트 케이스…)는 비용이라는 관점. 심플 디자인의 네 번째 규칙을 실무 기준으로 풀어낸 것이다.

  • 최소 재현

    버그가 여전히 일어나는 가장 작은 코드와 조건을 만드는 것. 원인을 좁히고, 테스트로 고정하고, 남에게 묻기 쉬워진다.

  • 정렬 알고리즘

    컬렉션의 원소를 어떤 순서(크기, 사전순, 날짜…)로 다시 배치하는 알고리즘. 같은 일을 하는 방법이 여럿이고 각자 강점이 달라, 트레이드오프를 익히기 좋은 주제다.

  • 인지 부하

    작업 기억이 한 번에 처리해야 하는 양. 문제 자체의 복잡함과 표현 방식에서 오는 복잡함으로 나뉜다.

보기 옵션