코딩테스트 JS 스택 풀이: 크레인 인형뽑기 처리법

2026.01.05·수정 2026.09.13·약 12분·작성: 해비·블로그 소개

주요 포인트 한눈에 보기

카카오 기출로 자주 등장하는 크레인 인형뽑기 문제를 통해, 단순 구현과 스택 기반 사고의 차이를 정리합니다. 내가 작성한 코드와 정답 코드의 구조를 비교하며, 왜 스택이 핵심 자료구조인지 흐름 중심으로 설명합니다.

문제 설명

게임개발자인 죠르디는 크레인 인형뽑기 기계를 모바일 게임으로 만들려고 합니다.죠르디는 게임의 재미를 높이기 위해 화면 구성과 규칙을 다음과 같이 게임 로직에 반영하려고 합니다.

크레인 인형뽑기 JS 풀이: 스택으로 사라지는 인형 처리 핵심 개념을 설명하는 첫 번째 본문 이미지

게임 화면은 1 x 1 크기의 칸들로 이루어진 N x N 크기의 정사각 격자이며 위쪽에는 크레인이 있고 오른쪽에는 바구니가 있습니다. (위 그림은 5 x 5 크기의 예시입니다). 각 격자 칸에는 다양한 인형이 들어 있으며 인형이 없는 칸은 빈칸입니다. 모든 인형은 1 x 1 크기의 격자 한 칸을 차지하며 격자의 가장 아래 칸부터 차곡차곡 쌓여 있습니다. 게임 사용자는 크레인을 좌우로 움직여서 멈춘 위치에서 가장 위에 있는 인형을 집어 올릴 수 있습니다. 집어 올린 인형은 바구니에 쌓이게 되는 데, 이때 바구니의 가장 아래 칸부터 인형이 순서대로 쌓이게 됩니다. 다음 그림은 [1번, 5번, 3번] 위치에서 순서대로 인형을 집어 올려 바구니에 담은 모습입니다.

크레인 인형뽑기 JS 풀이: 스택으로 사라지는 인형 처리 적용 흐름을 설명하는 두 번째 본문 이미지

만약 같은 모양의 인형 두 개가 바구니에 연속해서 쌓이게 되면 두 인형은 터뜨려지면서 바구니에서 사라지게 됩니다. 위 상태에서 이어서 [5번] 위치에서 인형을 집어 바구니에 쌓으면 같은 모양 인형 두 개가 없어집니다.

크레인 인형뽑기 JS 풀이: 스택으로 사라지는 인형 처리 참고 내용을 설명하는 본문 이미지 3

크레인 작동 시 인형이 집어지지 않는 경우는 없으나 만약 인형이 없는 곳에서 크레인을 작동시키는 경우에는 아무런 일도 일어나지 않습니다. 또한 바구니는 모든 인형이 들어갈 수 있을 만큼 충분히 크다고 가정합니다. (그림에서는 화면표시 제약으로 5칸만으로 표현하였음)

게임 화면의 격자의 상태가 담긴 2차원 배열 board와 인형을 집기 위해 크레인을 작동시킨 위치가 담긴 배열 moves가 매개변수로 주어질 때, 크레인을 모두 작동시킨 후 터트려져 사라진 인형의 개수를 return 하도록 solution 함수를 완성해주세요

[제한사항]
board 배열은 2차원 배열로 크기는 5 x 5 이상 30 x 30 이하입니다. board의 각 칸에는 0 이상 100 이하인 정수가 담겨있습니다. 0은 빈 칸을 나타냅니다. 1 ~ 100의 각 숫자는 각기 다른 인형의 모양을 의미하며 같은 숫자는 같은 모양의 인형을 나타냅니다. moves 배열의 크기는 1 이상 1,000 이하입니다. moves 배열 각 원소들의 값은 1 이상이며 board 배열의 가로 크기 이하인 자연수입니다.

[[0,0,0,0,0],
 [0,0,1,0,3],
 [0,2,5,0,1],
 [4,2,4,4,2],
 [3,5,1,3,1]]

[1,5,3,5,1,2,1,4]

내가 푼 풀이

아래는 내가 처음 작성한 전체 코드입니다. 이후 단계별 설명에서는 이 코드를 기준으로 각 부분이 어떤 역할을 하는지 하나씩 살펴봅니다.

function solution(board, moves) {
  let arr = [];
  let answer = 0;
  let moveIndex = 0;

  for (let index of moves) {
    index--;
    while (moveIndex < board.length && board[moveIndex][index] === 0) {
      moveIndex++;
    }

    if (moveIndex < board.length && board[moveIndex][index] !== 0) {
      if (board[moveIndex][index] === arr.at(-1)) {
        answer += 2;
        arr.pop();
      } else {
        arr.push(board[moveIndex][index]);
      }
      board[moveIndex][index] = 0;
    }
    moveIndex = 0;
  }

  return answer;
}

먼저 내가 작성한 풀이는 문제를 있는 그대로 구현하는 방식에 가깝습니다. 크레인이 특정 열을 선택하면, 해당 열의 가장 위에서부터 인형을 하나 찾고, 그 인형을 바구니에 담는 흐름을 그대로 코드로 옮겼습니다.

이 풀이의 전체 흐름은 다음과 같이 나눌 수 있습니다.

1단계는 크레인이 이동한 열에서 집을 인형을 찾는 과정입니다. moves 배열의 각 값은 열 번호이므로, 1을 빼서 실제 인덱스로 변환한 뒤 위에서부터 탐색합니다.

for (let index of moves) {
  index--;
  while (moveIndex < board.length && board[moveIndex][index] === 0) {
    moveIndex++;
  }

이 코드는 선택된 열에서 값이 0이 아닌 지점을 찾을 때까지 위에서 아래로 내려가는 역할을 합니다. 즉, 크레인이 집을 수 있는 가장 위 인형을 찾는 단계입니다.

2단계는 인형을 바구니에 담으면서 바로 이전 인형과 비교하는 과정입니다. 바구니 역할을 하는 arr 배열의 마지막 값과 현재 집은 인형을 비교합니다.

if (moveIndex < board.length && board[moveIndex][index] !== 0) {
  if (board[moveIndex][index] === arr.at(-1)) {
    answer += 2;
    arr.pop();
  } else {
    arr.push(board[moveIndex][index]);
  }
  board[moveIndex][index] = 0;
}

여기서 같은 인형이 연속으로 들어오는 경우를 직접 처리합니다. 같은 경우에는 바구니에서 이전 인형을 제거하고, 제거된 인형의 개수를 answer에 더합니다.

마지막으로 각 move가 끝날 때마다 moveIndex를 다시 0으로 초기화하여, 다음 열 탐색이 항상 위에서부터 시작되도록 합니다.

이 풀이는 문제를 정확히 해결할 수 있지만, 크레인의 동작과 바구니의 상태를 모두 직접 관리해야 하기 때문에 코드 흐름이 다소 길어지고 복잡해지는 특징이 있습니다.

정답 풀이

다음은 문제에서 제시된 정답 풀이의 전체 코드입니다. 이 코드는 바구니를 스택으로 모델링하여 문제의 규칙을 가장 자연스럽게 표현합니다.

function solution(board, moves) {
  let answer = 0;
  let stack = [];

  moves.forEach(pos => {
    for (let i = 0; i < board.length; i++) {
      if (board[i][pos - 1] !== 0) {
        let tmp = board[i][pos - 1];
        board[i][pos - 1] = 0;

        if (tmp === stack[stack.length - 1]) {
          stack.pop();
          answer += 2;
        } else {
          stack.push(tmp);
        }
        break;
      }
    }
  });

  return answer;
}

정답 풀이는 문제를 한 단계 추상화하여 바라봅니다. 핵심은 크레인이 아니라, 바구니에서 어떤 일이 반복적으로 발생하는지입니다.

바구니에서는 항상 가장 마지막에 들어온 인형과 새로 들어온 인형을 비교합니다. 이 구조는 후입선출(LIFO) 특성을 가지는 스택과 완전히 동일합니다.

같은 스택의 상호작용 규칙은 JavaScript 올바른 괄호 스택 검증에서도 동일하게 확인할 수 있어, 스택 기반 사고를 비교하기 좋습니다.

정답 풀이는 이 점을 명확히 인식하고, 바구니를 스택으로 정의합니다.

let answer = 0;
let stack = [];

이후 각 move에 대해 크레인이 집을 인형을 찾는 부분은 비교적 단순하게 처리합니다. 선택된 열을 위에서부터 순회하며 첫 번째 인형만 집습니다.

moves.forEach(pos => {
  for (let i = 0; i < board.length; i++) {
    if (board[i][pos - 1] !== 0) {
      let tmp = board[i][pos - 1];
      board[i][pos - 1] = 0;

인형을 집는 순간, 즉시 스택의 마지막 값과 비교합니다. 이때 조건 분기는 문제의 규칙과 1:1로 대응됩니다.

if (tmp === stack[stack.length - 1]) {
  stack.pop();
  answer += 2;
} else {
  stack.push(tmp);
}

같은 인형이면 제거하고 점수를 올리고, 다른 인형이면 그대로 스택에 쌓습니다. 이 과정이 반복되면서 바구니의 상태가 자연스럽게 유지됩니다.

최근 인형을 꺼내고 비교한 뒤 제거하는 동작은 JS 쇠막대기 스택 레이저 절단에서도 동일하게 쓰이는 패턴이므로 함께 보면 pop/push 기준을 빠르게 정리할 수 있습니다.

이 풀이는 문제의 규칙을 자료구조에 그대로 위임했기 때문에, 별도의 상태 변수나 복잡한 조건 처리가 필요하지 않습니다.

내 풀이와 정답 풀이의 차이점

이제 두 풀이를 나란히 놓고 보면, 결과보다 더 중요한 차이점이 보입니다. 두 코드 모두 정답을 반환하지만, 문제를 바라보는 관점과 코드 구조에는 분명한 차이가 있습니다.

두 풀이 모두 동일한 결과를 만들지만, 문제를 바라보는 관점에는 분명한 차이가 있습니다. 내가 푼 풀이는 문제의 조건을 하나씩 코드로 옮기는 방식이었다면, 정답 풀이는 문제의 구조 자체를 자료구조로 모델링한 방식입니다.

특히 바구니를 어떻게 해석하느냐에서 차이가 발생합니다. 바구니는 단순히 인형을 담는 배열이 아니라, 가장 마지막에 들어온 인형과의 비교가 반복적으로 발생하는 공간입니다. 이 특성은 스택의 LIFO 구조와 정확히 일치합니다.

또한 내가 푼 풀이에서는 moveIndex와 같은 보조 변수를 사용해 상태를 직접 관리해야 했지만, 정답 풀이는 반복문과 스택만으로 흐름이 자연스럽게 이어집니다. 이는 코드의 가독성과 유지보수 측면에서도 큰 차이를 만듭니다.

코딩 테스트에서는 정답 여부뿐만 아니라, 문제를 얼마나 구조적으로 이해했는지도 함께 평가됩니다. 같은 문제를 풀더라도, 문제의 핵심 개념을 정확히 짚어낸 풀이가 더 좋은 코드로 평가받게 됩니다.

결론

크레인 인형뽑기 문제는 구현 문제처럼 보이지만, 실제로는 스택의 개념을 정확히 이해하고 있는지를 묻는 문제입니다. 문제의 규칙을 그대로 자료구조로 옮길 수 있을 때, 코드도 자연스럽고 간결해집니다.

FAQ

Q. 왜 배열이 아니라 스택을 사용해야 하나요?
바구니의 맨 위 인형과 새로 들어온 인형을 비교해야 하기 때문에, 마지막 상태를 바로 확인할 수 있는 스택 구조가 적합합니다.

Q. board 배열을 직접 수정해도 괜찮나요?
문제 조건상 인형이 사라진 자리를 0으로 처리해야 하므로, board를 직접 수정하는 방식이 오히려 직관적입니다.

풀이 전에 확인할 순서

  1. 입력값과 출력값을 한 문장으로 다시 적습니다.
  2. 반복할 대상과 비교·저장할 값을 정합니다.
  3. 필요한 자료구조와 시간복잡도를 예상합니다.
  4. 코드를 보기 전에 손으로 작은 예제를 계산합니다.
힌트: 코딩테스트 JS 스택 풀이: 크레인 인형뽑기 처리법

정답 코드를 바로 따라 쓰기보다, 본문에서 값이 갱신되는 조건과 반복 범위를 먼저 찾으세요. 반복 한 번마다 반드시 유지되어야 하는 값이 무엇인지 적으면 풀이의 중심 변수를 고르기 쉽습니다.

테스트 확인

  • 가능한 가장 작은 입력
  • 같은 값이나 문자가 반복되는 입력
  • 정답이 처음 또는 마지막 위치에서 결정되는 입력
  • 입력 제한에 가까운 경우의 실행 시간

확인 결과: 본문의 예제뿐 아니라 위 경계 사례에서도 예상값과 실제 출력이 같아야 풀이가 완료됩니다.

이 글이 도움이 되었나요?

조회 중

코딩테스트 JavaScript 학습 순서

필수 49개 · 전체 49개

읽음 기록 관리

전체 과정 목차 (49개)
  1. 필수 길잡이 · 코딩테스트 JS 자료구조 로드맵: 배열, 해시, 스택, 투 포인터 순서
  2. 필수 학습 · 세 수 중 최솟값 JavaScript 조건문 풀이 정리
  3. 필수 학습 · 삼각형 판별하기 JavaScript 풀이
  4. 필수 학습 · 연필 개수 JavaScript 풀이
  5. 필수 학습 · 1부터 N까지 합 출력하기 JavaScript 풀이
  6. 필수 학습 · 최솟값 구하기 JavaScript 풀이|배열 순회와 비교 갱신 원리
  7. 필수 학습 · 홀수 JavaScript 풀이: 조건 판별과 결과 처리 정리
  8. 필수 학습 · 10부제 JavaScript 풀이: 끝자리 비교로 위반 차량 수 세기
  9. 필수 학습 · A를 #으로 JavaScript 풀이: 문자열 순회와 치환
  10. 필수 학습 · 문자 찾기 JavaScript 풀이: 문자열 순회로 개수 세기
  11. 필수 학습 · 대문자 찾기 JavaScript 풀이
  12. 필수 학습 · 대문자로 통일 JavaScript 풀이
  13. 필수 학습 · 대소문자 변환 JavaScript 풀이
  14. 필수 학습 · 일곱 난쟁이 JavaScript 풀이: 두 명을 제외하는 완전탐색
  15. 필수 학습 · 코딩테스트 JS Map 풀이: 학급 회장 득표수 세기
  16. 필수 학습 · 코딩테스트 JS 스택 풀이: 올바른 괄호 검증하기
  17. 필수 학습 · 코딩테스트 JS 스택 풀이: 괄호문자 제거하기
  18. 필수 학습 · 코딩테스트 JS 스택 풀이: 크레인 인형뽑기 처리법 현재 글
  19. 필수 학습 · 코딩테스트 JS 스택 풀이: 후위식 연산 계산하기
  20. 필수 학습 · 코딩테스트 JS 스택 풀이: 쇠막대기 레이저 절단 개수 세기
  21. 필수 학습 · 코딩테스트 JS 투 포인터 풀이: 두 정렬 배열 합치기
  22. 필수 학습 · 코딩테스트 JS 투 포인터 풀이: 공통 원소 추출하기
  23. 필수 학습 · 코딩테스트 JS 슬라이딩 윈도우 풀이: 최대 매출 구간 합 계산하기
  24. 필수 학습 · JavaScript 투 포인터: 합이 M인 연속 부분수열 개수
  25. 필수 학습 · 코딩테스트 JS 해시 풀이: 모든 아나그램 찾기
  26. 필수 학습 · 가장 긴 문자열 JavaScript 풀이
  27. 필수 학습 · 가운데 문자 출력 JavaScript 풀이
  28. 필수 학습 · 중복문자제거 JavaScript 풀이
  29. 필수 학습 · 코딩테스트 JS 고급: 최소 힙으로 다익스트라 최단 경로 구하기
  30. 필수 학습 · 코딩테스트 JS Union-Find: 연결 성분 수와 크기 구하기
  31. 필수 학습 · 코딩테스트 JS Trie: 접두사에 맞는 단어 수 세기
  32. 필수 학습 · 코딩테스트 JS Fenwick Tree: 값 갱신과 구간 합 처리
  33. 필수 학습 · 코딩테스트 JS 세그먼트 트리: 단일 대입과 구간 합
  34. 필수 학습 · 코딩테스트 JS LRU 캐시: 지도 타일 재사용 기록
  35. 필수 학습 · 코딩테스트 JS AVL 트리: 기준 이상 최솟값 찾기
  36. 필수 학습 · 코딩테스트 JS 큐: 상담 창구 대기열 명령 처리
  37. 필수 학습 · 코딩테스트 JS 연결 리스트: 재생 대기 목록 관리
  38. 필수 학습 · JavaScript 원형 덱 연습: 최근 기록 창과 되돌리기
  39. 필수 학습 · JavaScript 해시 테이블 연습: 정규화 문자열 빈도와 등장 순서
  40. 필수 학습 · JavaScript 트리 순회 연습: 깊이별 노드 묶기
  41. 필수 학습 · JavaScript BST 연습: 닫힌 구간의 중복 키 보고서
  42. 필수 학습 · JavaScript 최소 힙 연습: 동률 순서를 지키는 작업 스케줄러
  43. 필수 학습 · JavaScript 그래프 연습: 연결 구역 크기를 작은 순서로 출력하기
  44. 필수 학습 · 중복단어제거 JavaScript 풀이
  45. 필수 학습 · TypeScript 이진 탐색 연습: 숫자 카드 존재 여부 확인
  46. 필수 학습 · 큰 수 출력하기 JavaScript 풀이
  47. 필수 학습 · 보이는 학생 JavaScript 풀이
  48. 필수 학습 · 가위바위보 JavaScript 풀이
  49. 필수 학습 · 점수계산 JavaScript 풀이

새 글 받아보기

RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.

RSS 피드 구독하기

댓글 남기기