이 도구가 하는 일
직장 송년회, 가족 모임, 동호회 연말 파티처럼 한 사람이 한 사람에게만 선물하는 방식을 시크릿 산타 또는 마니또라고 부릅니다. 규칙은 단순하지만 실제로 하려면 "자기 이름을 뽑으면 다시 뽑기", "부부끼리는 서로에게 걸리지 않게 하기" 같은 예외가 계속 생깁니다. 이 도구는 참가자와 제외 조합을 입력받아 그 조건을 모두 만족하는 배정표를 한 번에 만들고, 각자 자기 이름의 버튼을 눌러 자기 결과만 확인하게 합니다.
사용 순서
- 참가자 이름을 한 명씩 추가합니다. 같은 이름은 추가되지 않으므로 동명이인은 "민수A", "민수B"처럼 구분하세요.
- 서로 걸리면 곤란한 조합이 있으면 제외 조합 칸에 "이름1-이름2"를 한 줄에 한 쌍씩 적습니다. 어느 쪽이 주는 쪽이든 두 방향 모두 막습니다. 참가자 목록에 없는 이름이 들어간 줄은 무시되고 결과 화면에 어떤 줄이 무시됐는지 알려 줍니다.
- 필요하면 맞교환 금지를 체크합니다. A가 B에게 주고 B가 A에게 주는 짝이 생기지 않게 합니다.
- 제비뽑기 실행을 누릅니다. 이름 옆에 "내 결과 보기" 버튼이 나타납니다.
- 화면을 한 사람씩 넘겨 가며 각자 자기 이름의 버튼만 눌러 확인하고 "숨기기"로 닫은 뒤 다음 사람에게 넘깁니다.
제외 조합의 구분자는 하이픈(-)뿐 아니라 쉼표, 슬래시, 물결표도 인식합니다. 이름 안에 하이픈이 들어 있어도(예: "김-민수") 양쪽이 모두 참가자 이름과 일치하도록 나눌 수 있는 위치를 찾습니다.
배정 방법: 자기 자신이 걸리지 않게 하는 방식
코드는 두 단계로 동작합니다. 먼저 참가자 이름을 피셔-예이츠 방식으로 무작위로 섞은 목록을 "받는 사람" 줄로 두고, 주는 사람 줄과 위치별로 짝을 짓습니다. 이 짝에서 자기 자신이 걸렸거나, 제외 조합이 걸렸거나, 맞교환 금지를 켰는데 맞교환이 생겼으면 그 배정표는 버리고 처음부터 다시 섞습니다. 이 시도를 최대 20,000번 반복합니다. 조건을 만족하는 모든 배정은 이 단계에서 같은 확률로 뽑힙니다. 한 가지 규칙이 정해져 있기 때문에 어느 이름이 특별히 유리하거나 불리하지 않습니다.
제약이 매우 빡빡하면 20,000번 안에 운이 나빠 못 찾을 수도 있습니다. 그 경우 마지막으로 역추적(백트래킹) 탐색을 한 번 더 합니다. 이 탐색은 해가 존재하면 거의 항상 찾아 주지만, 모든 해가 같은 확률로 나오는 보장은 없습니다. 탐색 한도를 넘거나 해가 없으면 "조건에 맞는 조합을 찾지 못했다"고 알립니다. 해가 없는 대표적인 경우는 참가자가 둘뿐인데 맞교환을 금지한 경우처럼 구조적으로 불가능한 조건입니다.
가능한 배정은 몇 가지인가: 검산한 표
자기 자신에게 걸리지 않는 배정을 수학에서 완전순열(derangement)이라고 하며 개수는 D(n) = (n−1)·(D(n−1)+D(n−2))로 구합니다. 아래 표는 이 점화식을 BigInt로 계산하고, 8명 이하는 n!개의 모든 배정을 직접 세어 같은 값이 나오는지 빌드 시 확인한 결과입니다. 마지막 열은 맞교환까지 금지했을 때의 개수입니다.
| 참가자 n | 모든 순열 n! | 자기 자신 제외 D(n) | 비율 | 맞교환도 금지 |
|---|---|---|---|---|
| 2명 | 2 | 1 | 50.00% | 0 |
| 3명 | 6 | 2 | 33.33% | 2 |
| 4명 | 24 | 9 | 37.50% | 6 |
| 5명 | 120 | 44 | 36.66% | 24 |
| 6명 | 720 | 265 | 36.80% | 160 |
| 7명 | 5040 | 1854 | 36.78% | 1140 |
| 8명 | 40320 | 14833 | 36.78% | 8988 |
| 10명 | 3,628,800 | 1,334,961 | 36.78% | - |
| 12명 | 479,001,600 | 176,214,841 | 36.78% | - |
D(n)/n!은 n이 4만 넘어도 약 36.8% 근처로 수렴합니다(수학에서 1/e로 알려진 값). 그래서 제외 조합이 없으면 무작위로 섞어 보기만 해도 평균 약 2.7번 만에 조건을 만족하는 배정이 나옵니다. 2명일 때 D(2)=1이라 결과는 정해져 있고, 맞교환 금지를 켜면 2명은 불가능해집니다(표에서 0). 3명이면 맞교환 금지 상태에서도 한 바퀴 도는 배정 2가지가 남습니다.
제외 조합이 늘면 선택지가 얼마나 줄어드나
6명이 모이고 1·2번, 3·4번, 5·6번이 부부라고 하고 부부끼리는 서로에게 걸리지 않게 한다고 합시다. 전수 탐색으로 센 결과는 다음과 같습니다. 마지막 열은 무작위로 섞어서 조건에 맞는 배정을 만날 때까지 평균 몇 번 시도해야 하는지(720÷개수)입니다.
| 제외한 부부 | 가능한 배정 수 | 전체 720가지 중 비율 | 평균 시도 횟수 |
|---|---|---|---|
| 0쌍 | 265 | 36.8% | 2.7회 |
| 1쌍 | 168 | 23.3% | 4.3회 |
| 2쌍 | 116 | 16.1% | 6.2회 |
| 3쌍 | 80 | 11.1% | 9.0회 |
세 쌍을 모두 제외해도 80가지가 남아 충분하지만, 8~10명 규모에 제외 조합을 절반 이상으로 늘리면 가능한 배정이 급격히 줄어듭니다. 실패 메시지가 뜨면 제외 쌍을 줄이거나 참가자를 늘려 보세요.
상황별 활용 예시
8명 팀 송년 선물 교환. 제외 조합 없이 8명을 입력하면 14,833가지 중 하나가 나옵니다. 팀장과 직속 팀원이 서로 걸리는 것이 부담스러우면 한 쌍만 제외하고, 선물 금액 상한은 모임에서 먼저 합의하세요. 도구는 금액을 정하지 않습니다.
세 쌍의 부부와 함께하는 6인 가족 모임. 부부 세 쌍을 제외하면 위 표처럼 80가지가 남습니다. 이때 맞교환 금지를 같이 켜면 선택지가 48가지(전수 탐색으로 센 값)로 더 줄어들지만 해는 남습니다.
12명 동아리. 완전순열이 176,214,841가지(12!은 479,001,600)이므로 제외 조합을 몇 쌍 두어도 충분히 다양합니다. 사람이 많으면 한 화면에서 돌려 가며 확인하기 번거로우니, 각자 자기 휴대폰으로 이 페이지를 열 수 없다는 점(아래 저장 설명 참고)을 고려해 진행 방식을 정하세요.
맞교환을 막는 옵션이 필요한 이유
완전순열이라도 A→B, B→A가 생길 수 있습니다. 둘이 서로에게 선물하는 것이 허용된 모임이면 문제없지만, "내가 선물하는 사람이 나에게 선물한다면 비밀이 드러난다"고 느끼는 모임에서는 이 옵션을 켜는 편이 낫습니다. 단, 켜면 가능한 배정이 줄어듭니다(표의 마지막 열). 모든 사람이 한 바퀴로 이어지는 단일 순환만 허용하는 기능은 아닙니다.
무작위성과 한계
무작위는 브라우저의 Math.random으로 만들며 추첨 보증용이나 보안용이 아닙니다. 상금이 걸린 공식 추첨이나 감사가 필요한 상황에는 맞지 않지만, 친목용 선물 배정에는 충분합니다. 같은 입력으로 다시 뽑으면 매번 다른 결과가 나오고 이전 결과는 사라집니다. 참가자 추가·삭제도 결과를 초기화합니다. 배정 알고리즘은 공정하지만, 화면을 돌려 보는 방식 자체는 신뢰가 필요합니다. 진행자가 개발자 도구를 열면 저장된 배정표를 볼 수 있기 때문입니다. 진행자도 결과를 모르게 해야 하는 모임이라면 이 도구보다 종이 뽑기나 각자 따로 결과를 받는 방식이 맞습니다.
저장과 개인정보
참가자 명단, 제외 조합, 배정 결과, "내 결과 보기" 상태는 이 브라우저의 localStorage에 키 pib-gift-exchange-tool-v1로 평문 JSON으로 저장됩니다. 이 페이지의 코드에는 이름이나 결과를 서버로 보내는 요청이 없습니다. 같은 기기·같은 브라우저를 다시 열면 그대로 남아 있고, 다른 기기나 다른 브라우저, 시크릿 창에서는 보이지 않으며 기기 간 동기화도 되지 않습니다. 삭제하려면 "전체 초기화" 버튼을 누르세요(키는 남지만 내용은 빈 값으로 덮어씁니다). 브라우저의 사이트 데이터 삭제를 쓰면 키 자체가 지워집니다. 공용 컴퓨터에서 썼다면 반드시 초기화하세요. 사이트에는 광고 스크립트가 로드될 수 있으나 이 도구의 입력칸을 읽어 가는 코드는 없습니다.
흔한 실수
- 결과를 확인하자마자 "숨기기"를 누르지 않고 화면을 다음 사람에게 넘기는 경우
- 제외 조합에 오타가 있어 규칙이 적용되지 않는 경우. 결과 화면의 "무시된 줄" 안내를 확인하세요.
- 뽑은 뒤에 참가자를 추가해 결과가 초기화되어 이미 본 사람이 혼란을 겪는 경우
- 제외 조합을 너무 많이 두어 실패 메시지를 반복해서 보는 경우
- 선물 금액·마감일·전달 방식을 정하지 않고 뽑기부터 하는 경우. 세 가지를 먼저 합의하세요.
뽑은 뒤에 할 일
결과가 정해지면 선물 전달일과 금액 상한, 선물 힌트(위시리스트) 공유 방식을 정하세요. 모임 전체 준비는 생일 파티 준비 체크리스트 생성기를 응용할 수 있고, 다른 방식의 추첨은 이름 뽑기 생성기와 사다리 게임, 숫자 추첨은 난수 생성기를 쓸 수 있습니다.
자주 묻는 질문
참가자를 추가한 뒤 다시 제비뽑기를 할 수 있나요?
네, "제비뽑기 실행" 버튼을 다시 누르면 새로운 무작위 조합으로 다시 배정됩니다. 이전 결과는 사라지니 이미 확인한 사람이 있다면 다시 뽑기 전에 알리세요.
참가자가 2명뿐이어도 되나요?
2명이면 A가 B에게, B가 A에게 주는 결과 하나뿐이라 무작위가 의미 없습니다. 맞교환 금지를 켜면 불가능으로 처리됩니다. 3명 이상을 권합니다.
정말 자기 자신이 걸리지 않나요?
네. 배정표를 만들 때마다 모든 참가자에 대해 자기 자신 배정 여부를 검사하고, 하나라도 있으면 그 표를 버리고 다시 만듭니다. 역추적 단계에서도 자기 자신은 후보에서 제외됩니다.
참가자 이름이 서버로 가나요?
이 도구의 코드는 이름·제외 조합·결과를 이 브라우저의 localStorage에만 저장하며 서버로 전송하는 요청이 없습니다. 다만 같은 기기를 쓰는 다른 사람은 저장된 내용을 열어 볼 수 있으니 공용 기기에서는 초기화하세요.
휴대폰마다 각자 결과를 볼 수 있나요?
아니요. 결과는 뽑은 기기의 브라우저에만 있고 링크로 공유되지 않습니다. 한 기기를 돌려 보거나, 진행자가 각 참가자에게 결과를 따로 전달하는 방식을 쓰세요.