무한 반복 피하기
깊이 제한과 순환 감지를 사용합니다.
무한 반복 피하기은(는) CoddyKit의 무료 SQL Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 SQL Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. SQL Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
무한 반복 문제
재귀 CTE는 강력하지만 심각한 위험도 따릅니다. 쿼리가 기본 사례에 도달하지 못하면 영원히 반복되면서 사용 가능한 메모리를 모두 소모하고 데이터베이스 세션이 중단될 수 있습니다.
무한 반복이 발생하는 이유를 이해하는 것이 이를 방지하는 첫 단계입니다.
반복이 끝나지 않는 경우
재귀 CTE는 재귀 항이 새로운 행을 계속 생성하고 더 이상 새로운 행이 생성되지 않는 상태에 도달하지 않을 때 무한히 반복됩니다.
이는 일반적으로 두 가지 상황에서 발생합니다. 종료 조건이 없거나 잘못된 경우, 또는 노드 A가 B를 가리키고 B가 다시 A를 가리키는 순환 데이터가 있는 경우입니다.
-- Simple recursive CTE that WOULD loop forever
-- (do NOT run this as-is; illustration only)
WITH RECURSIVE counter AS (
SELECT 1 AS n -- base case
UNION ALL
SELECT n + 1 -- recursive term
FROM counter
-- no WHERE clause to stop it!
)
SELECT n FROM counter;깊이 제한 추가하기
가장 간단한 보호 방법은 깊이 카운터입니다. 모든 재귀 단계에서 1씩 증가하는 열을 추가한 다음 최대 깊이를 초과하면 중지합니다.
이렇게 하면 데이터와 관계없이 종료가 보장되며, 선택한 제한값이 안전 상한선 역할을 합니다.
WITH RECURSIVE counter AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1
FROM counter
WHERE n < 10 -- stop at depth 10
)
SELECT n FROM counter;계층 구조 쿼리에서 깊이 제한 사용하기
직원 계층 구조를 순회할 때 경로와 함께 깊이도 추적할 수 있습니다. WHERE depth < 5 절을 사용하면 데이터에 더 깊은 연결이나 순환 연결이 있더라도 5개 수준을 넘어 순회하지 않습니다.
CREATE TEMP TABLE employees (
id INT PRIMARY KEY,
name TEXT,
manager_id INT
);
INSERT INTO employees VALUES
(1, 'Alice', NULL),
(2, 'Bob', 1),
(3, 'Carol', 2),
(4, 'Dave', 3);
WITH RECURSIVE hierarchy AS (
SELECT id, name, manager_id, 1 AS depth
FROM employees
WHERE manager_id IS NULL -- root
UNION ALL
SELECT e.id, e.name, e.manager_id, h.depth + 1
FROM employees e
JOIN hierarchy h ON e.manager_id = h.id
WHERE h.depth < 5 -- depth limit
)
SELECT id, name, depth FROM hierarchy ORDER BY depth, id;순환 감지란 무엇인가
그래프 데이터에서 간선을 따라가다가 이미 방문한 노드로 다시 이어지면 순환이 발생합니다. 예를 들면 A → B → C → A와 같습니다.
깊이 제한을 사용하면 순환 데이터에서도 쿼리를 종료할 수 있지만, 순환이 어디에서 발생했는지는 알려 주지 않습니다. 명시적인 순환 감지는 그 위치까지 알려 줍니다.
CREATE TEMP TABLE edges (
from_node INT,
to_node INT
);
-- Introduce a cycle: 1->2->3->1
INSERT INTO edges VALUES
(1, 2),
(2, 3),
(3, 1), -- cycle back to 1
(1, 4); -- also a non-cyclic branch
SELECT * FROM edges;배열로 방문한 노드 추적하기
견고한 순환 감지 방법은 재귀 과정에서 방문한 노드 ID의 배열을 함께 전달하는 것입니다. 다음 노드를 방문하기 전에 해당 노드가 이미 배열에 있는지 확인합니다. 이미 있다면 건너뜁니다.
PostgreSQL에서는 ANY(array) 연산자와 || 배열 추가 연산자를 사용하여 이를 쉽게 구현할 수 있습니다.
WITH RECURSIVE traverse AS (
-- Start from node 1
SELECT from_node,
to_node,
ARRAY[from_node] AS visited
FROM edges
WHERE from_node = 1
UNION ALL
SELECT e.from_node,
e.to_node,
t.visited || e.from_node
FROM edges e
JOIN traverse t ON e.from_node = t.to_node
WHERE NOT (e.from_node = ANY(t.visited)) -- skip visited nodes
)
SELECT from_node, to_node, visited
FROM traverse;CYCLE 절(PostgreSQL 14 이상)
PostgreSQL 14에서는 재귀 CTE를 위한 기본 제공 CYCLE 절이 도입되었습니다. 이 절은 순환이 감지되었을 때 true가 되는 부울 플래그와 이동한 경로를 기록하는 배열이라는 두 개의 열을 자동으로 추가합니다.
배열을 직접 관리하는 것보다 더 깔끔한 방법입니다.
WITH RECURSIVE traverse AS (
SELECT from_node, to_node
FROM edges
WHERE from_node = 1
UNION ALL
SELECT e.from_node, e.to_node
FROM edges e
JOIN traverse t ON e.from_node = t.to_node
)
CYCLE from_node SET is_cycle USING path
SELECT from_node, to_node, is_cycle, path
FROM traverse;깊이 제한과 순환 감지 함께 사용하기
깊이 제한과 순환 감지를 함께 사용하면 가장 강력한 안전성을 확보할 수 있습니다:
- 깊이 제한은 데이터 품질과 관계없이 엄격한 상한선으로 작동합니다.
- 순환 감지는 반복이 발견되는 즉시 중지하여 불필요한 반복 실행을 줄입니다.
운영 쿼리에는 항상 이 보호 방법 중 하나 이상을 적용해야 합니다.
WITH RECURSIVE traverse AS (
SELECT from_node,
to_node,
1 AS depth,
ARRAY[from_node] AS visited
FROM edges
WHERE from_node = 1
UNION ALL
SELECT e.from_node,
e.to_node,
t.depth + 1,
t.visited || e.from_node
FROM edges e
JOIN traverse t ON e.from_node = t.to_node
WHERE t.depth < 10 -- depth limit
AND NOT (e.from_node = ANY(t.visited)) -- cycle guard
)
SELECT from_node, to_node, depth, visited
FROM traverse;문자열로 전체 경로 만들기
순환 감지와 함께 사람이 읽을 수 있는 전체 순회 경로를 문자열로 기록하면 유용합니다. 노드 ID를 -> 로 구분하여 연결하면 그래프에서 거친 경로를 쉽게 표시하거나 디버깅할 수 있습니다.
WITH RECURSIVE traverse AS (
SELECT from_node,
to_node,
1 AS depth,
ARRAY[from_node] AS visited,
from_node::TEXT AS path_str
FROM edges
WHERE from_node = 1
UNION ALL
SELECT e.from_node,
e.to_node,
t.depth + 1,
t.visited || e.from_node,
t.path_str || ' -> ' || e.from_node::TEXT
FROM edges e
JOIN traverse t ON e.from_node = t.to_node
WHERE t.depth < 10
AND NOT (e.from_node = ANY(t.visited))
)
SELECT from_node, to_node, path_str, depth
FROM traverse
ORDER BY depth;최대 재귀 반복 횟수 설정하기
일부 데이터베이스(MariaDB, 이전 버전의 MySQL)는 재귀 횟수를 제한하는 세션 변수를 사용합니다. PostgreSQL에서는 직접 작성한 깊이 카운터에 의존하거나 문 수준 시간 제한을 사용하는 방식이 이에 해당합니다.
statement_timeout을 설정하는 것은 마지막 수단으로 사용할 수 있는 안전장치이며, 일정 시간이 지나면 제어되지 않는 쿼리를 종료합니다.
-- PostgreSQL: set a statement timeout as a safety net
SET statement_timeout = '5s';
-- Now any query that runs longer than 5 seconds is cancelled
WITH RECURSIVE counter AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM counter WHERE n < 1000000
)
SELECT MAX(n) FROM counter;
-- Reset to default when done
SET statement_timeout = '0';적절한 깊이 제한 선택하기
모든 상황에 적용되는 깊이 제한은 없습니다. 데이터에서 현실적으로 가능한 최대 깊이를 기준으로 선택해야 합니다:
- 조직도는 10~15개 수준을 넘는 경우가 드뭅니다. 여유 있는 완충값으로
depth < 20을 사용하십시오. - 파일 시스템 트리는 50~100개 수준까지 깊어질 수 있습니다.
- 소셜 네트워크 그래프 탐색은 대개 3~6개의 연결 단계로 제한합니다.
유효한 데이터를 포함할 수 있을 만큼 높게 설정하되, 제어되지 않는 쿼리를 조기에 발견할 수 있을 만큼 낮게 설정하십시오.
-- Example: org chart with a generous but safe depth cap
WITH RECURSIVE org AS (
SELECT id, name, manager_id, 1 AS depth
FROM employees
WHERE manager_id IS NULL
UNION ALL
SELECT e.id, e.name, e.manager_id, o.depth + 1
FROM employees e
JOIN org o ON e.manager_id = o.id
WHERE o.depth < 20 -- realistic upper bound for an org chart
)
SELECT id, name, depth
FROM org
ORDER BY depth, name;깊이 제한과 순환 감지 비교
어떤 기법을 사용해야 할까요?
정리: 재귀 쿼리 안전하게 유지하기
재귀 CTE에서 무한 반복을 방지하는 방법을 정리하면 다음과 같습니다:
- 깊이 제한 — 카운터 열을 추가하고
WHERE depth < N으로 중지합니다. 항상 효과적이며 구현하기 쉽습니다. - 배열 기반 순환 감지 — 방문한 노드 ID를 배열에 전달하고 이미 배열에 있는 노드는 건너뜁니다. 첫 번째 순환에서 조기에 중지합니다.
- CYCLE 절(PostgreSQL 14 이상) —
is_cycle및path열을 사용하여 순환 추적을 자동화하는 기본 제공 구문입니다. - 문 실행 시간 제한 — 제어되지 않는 쿼리를 위한 데이터베이스 수준의 안전장치이며, 올바른 논리를 대신할 수는 없습니다.
- 운영 환경에서는 가장 강력한 보장을 위해 깊이 제한과 순환 감지를 함께 사용하십시오.
이러한 기법을 사용하면 데이터베이스가 중단될 위험 없이 계층 구조와 그래프를 자신 있게 순회할 수 있습니다.
자주 묻는 질문
“무한 반복 피하기” 강의는 무료인가요?
네 — “무한 반복 피하기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 SQL Academy 강의 전체를 잠금 해제할 수 있습니다. SQL Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“무한 반복 피하기”에서 뭘 배우나요?
깊이 제한과 순환 감지를 사용합니다. 브라우저에서 직접 실행하는 실습 코드로 SQL Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
SQL Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 SQL Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“무한 반복 피하기” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 SQL Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 SQL Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 재귀 CTE의 작동 원리
- 범주 트리 탐색하기
- 계열과 시퀀스 생성
- 무한 반복 피하기