해커톤 164개 제출작을 전부 빌드해봤다 — 아무도 물어볼 시간이 없던 질문
해커톤 심사에서 늘 빠지던 질문 '이거 빌드는 되나요?'를 일회용 리눅스 머신 164대로 검증한 기록. 55분, 1달러, 그리고 69%라는 숫자.

164 disposable computers, one judging afternoon, and a question nobody had time to ask
Does it build? I have run hackathon judging the careful way. By that I mean: rubrics...
개요 #
해커톤 심사표에는 혁신성, 발표력, 사용자 경험, 스폰서 기술 활용도가 들어간다. 정작 "이 프로젝트가 빌드는 되나요"라는 항목은 거의 없다. 심사위원이 게을러서가 아니다. 60개 팀의 저장소를 클론하고 의존성을 깔고 컴파일해볼 시간이 오후 한나절에는 없기 때문이다.
해커톤 운영 경험이 있는 개발자 Lauren Lee가 이 문제를 정면으로 다뤘다. Fly.io의 Sprites로 일회용 리눅스 머신을 164대 띄워 단일 해커톤의 제출작 전부를 빌드해본 것이다. 소요 시간 약 55분, 비용은 1달러 남짓이었다.
결과는 이랬다. 164개 중 113개, 69%가 끝까지 빌드됐다. 나머지 51개 중 41개는 제출자 본인 노트북 말고는 어디서도 빌드되지 않는 코드였다. 그리고 심사 테이블에서 그 41개는 나머지와 구분되지 않았다.
심사 오후의 산수 #
주말 해커톤의 표준 형태를 떠올려보자. 48시간, 4080개 팀, 마지막 날 부스 형태의 데모 행사. 심사는 걸어 다니며 이뤄진다. 심사위원 한 명이 행사장 한 구역을 맡아 테이블을 옮겨 다니며 팀당 45분을 쓴다. 발표를 듣고, 데모를 보고, 질문 하나를 던지고, 휴대폰으로 점수를 입력하고, 다음 테이블로 간다.
5분은 이야기를 듣고 판단을 내리기에 충분한 시간이다. 하지만 저장소를 클론하고 의존성을 설치해서 방금 본 그 물건이 발표자 노트북 밖에서도 돌아가는지 확인하기에는 턱없이 모자란다. 그걸 하겠다고 덤볐다가는 피자가 다 떨어진 뒤에야 담당 구역을 끝내게 된다.
데모가 잘 돌아간다는 건 그 노트북이 잘 돌아간다는 증거일 뿐이다.
그래서 제약에 맞춰 형식이 만들어졌고, 시간이 지나면서 제약 자체는 눈에 띄지 않게 됐다. 일부 생태계에서는 스마트 컨트랙트만이라도 컴파일해보기 시작했는데, 이건 실질적인 진전이다. 하지만 프로젝트 전체를 빌드해보는 곳은 거의 없다. 그렇게 하는 소수는 낯선 사람 60명의 설치 스크립트를 자기 노트북에서 돌리고 있다. 그 스크립트 중 하나만 악의적이어도 파일과 키, 브라우저 세션이 통째로 읽힌다.
그러다 보니 해커톤은 소프트웨어를 설명하는 능력을 재는 대회가 됐다. 그것도 분명 실력이다. 다만 소프트웨어를 만드는 능력과는 다른 실력이고, 해커톤 역사 내내 전자가 후자를 대신해왔다. 팀들도 이걸 누구보다 잘 안다. 일요일 오후에 빌드보다 발표 리허설에 시간을 더 쓰는 이유다.
바뀐 건 컴퓨터 한 대의 가격 #
필자는 Fly.io의 Sprites를 만지작거리던 중이었다. API 호출 한 번에 깨끗한 리눅스 머신을 내주고, 초 단위로 과금하고, 다 쓰면 버리면 되는 서비스다. 깨끗한 머신 한 대가 분당 1센트의 몇 분의 일이고 몇 초 만에 뜬다면, "모든 제출작을 빌드해본다"는 건 공상이 아니라 for 루프가 된다.
그래서 for 루프를 짰다. 저장소 목록을 넘기면 각각에 대해 머신을 만들고, 클론하고, 어떤 종류의 프로젝트인지 판별하고, 의존성을 설치하고, 컴파일할 게 있으면 컴파일하고, 테스트가 있으면 돌리고, 어디까지 갔는지를 정확히 기록한다.
npm run judge -- --input submissions.csv --executor sprites --concurrency 4도구의 전부다. 흥미로운 건 이 도구가 하지 않는 것들이다.
이 도구가 거부하는 네 가지 #
점수를 매기지 않는다 #
모든 제출작은 사다리의 한 칸에 놓인다. 각 칸은 주관적 의견이 아니라 사실이다.
| 단계 | 의미 |
|---|---|
| Unreachable | 저장소가 사라졌거나, 비공개이거나, 애초에 저장소가 아니었다 |
| Empty | 존재하지만 의미 있는 코드가 없다 |
| Not evaluable | 코드는 있으나 필요한 툴체인을 확보하지 못했다. 팀이 아니라 도구 쪽 실패다 |
| Install failed | 의존성 설치 단계에서 멈췄다 |
| Build failed | 설치는 됐지만 컴파일/빌드가 안 됐다 |
| Installed, no build | 설치됐고, 빌드할 대상이 없었다 |
| Built, no tests | 빌드됐고, 돌릴 테스트가 없었다 |
| Tests failed | 빌드됐고, 테스트가 있으며 실패했다 |
| Tests passed | 빌드됐고, 테스트가 통과했다 |
심사위원은 점수를 두고 다툴 수 있다. 타입 체커 40번째 줄의 exit code 2를 두고는 다툴 수 없다.
머신을 공유하지 않는다 #
해커톤 설치 스크립트는 무슨 짓이든 할 수 있다. 홈 디렉터리에 파일을 쓰기도 하고, 전역 패키지를 설치해 다음 프로젝트의 동작을 바꿔놓기도 한다. 러너 한 대에서 60개를 돌리면 40번 프로젝트의 결과가 1~39번이 남긴 찌꺼기에 좌우된다. 매번 새 머신에서 돌리면 모든 결과가 독립적으로 성립한다. 같은 격리 덕분에 낯선 사람의 코드를 빌드하는 일도 안전해진다. 설치 스크립트가 무슨 짓을 하든, 그 대상은 어차피 버릴 머신이지 내 노트북이 아니다.
도구의 잘못을 팀에게 떠넘기지 않는다 #
저장소가 사라져서 클론이 실패하면 그건 팀 책임이다. 도구 쪽 머신이 GitHub에 닿지 못했거나 컴파일러가 없어서 실패했다면 그건 도구 책임이고, 프로젝트를 "망가짐"으로 표시하는 대신 별도 카테고리로 기록한다.
실패를 버리지 않는다 #
빌드가 깨지면 그 머신을 깨진 순간 그대로 체크포인트로 저장해 보관한다. 접근 권한이 있는 사람은 그 머신을 열어 정확한 상태를 볼 수 있다. 통과한 프로젝트의 머신은 삭제된다. 볼 게 없기 때문이다. 머신 이름은 해시로 붙기 때문에 대시보드조차 익명 빌드의 목록일 뿐, 팀 목록이 아니다.

실패한 프로젝트의 머신은 팀이 직접 열어볼 수 있는 피드백이다.
실행 자체는 시시하다. 그게 핵심이다. 164개 제출작을 동시 4개씩, 점심 준비하는 동안 약 55분 만에 끝냈다. 프로젝트당 중앙값 58초. 이 글에 나온 모든 실행은, 잘못해서 다시 돌린 것까지 포함해 사용료가 1달러를 조금 넘었다. README 164개를 읽는 데 걸릴 시간을 생각해보라.

Fly의 Cost Explorer 기준 이틀간 $1.01.
164개를 전부 빌드하면 보이는 것 #
제출작은 전부 한 행사에서 나온 것이고, 하나도 빼놓지 않고 돌렸다.

164개 중 113개가 끝까지 빌드됐다. 69%다. 33개는 의존성 설치까지 성공한 뒤 컴파일/빌드에서 실패했다. 8개는 설치조차 되지 않았다. 9개는 설치는 됐지만 빌드할 대상이 없었다. 그것도 나름의 답이다. 1개는 첫 실행 때는 접근됐다가 하루 뒤 두 번째 실행 때 사라져 있었다.
여기서 "빌드됐다"의 의미를 분명히 해두자. 컴파일러가 코드를 받아들였고 빌드 스크립트가 0으로 종료했다는 뜻이다. 프로젝트가 동작한다거나, README가 말하는 일을 한다거나, 실제 사용자를 견딘다는 뜻이 아니다. 빌드는 소프트웨어가 할 수 있는 최소한이다.
69%를 "프로젝트들이 괜찮았다"로 읽기 쉬운데, 그건 오독이다. 빌드되지 않은 51개 중 10개는 빌드할 게 없거나 클론할 게 없는 경우였다. 나머지 41개, 전체의 4분의 1은 제작자 본인 머신 외에는 어디서도 빌드되지 않았을 코드다. 그런데 심사 테이블에서 이 41개는 빌드되는 것들과 똑같아 보였다. 모든 판단이 "돌아가는 소프트웨어"와 "돌아가는 데모"를 가르는 유일한 사실 없이 내려졌다는 뜻이다.
실패에는 유형이 있었다.
- 가장 흔한 건 컨트랙트는 깨끗하게 컴파일되는데 그걸 감싼 애플리케이션이 깨지는 경우였다. 어려운 부분은 잘 돌아가고, 평범한 부분(TypeScript 에러, 누락된 빌드 산출물, 끝내 해소되지 않은 패키지)이 발목을 잡았다.
- 두 번째는 모노레포에서 패키지 하나가 실패하며 전체 빌드를 끌고 내려간 경우다.
- 세 번째이자 가장 드문 건 저장소에 README와 다이어그램만 있던 경우다.
이건 유형이지 특정 팀 이야기가 아니다. 이 글에서 식별되는 프로젝트는 없고, 앞으로도 없다.
스폰서 기술을 쓴 건가, 로고만 쓴 건가 #
스폰서가 붙은 해커톤에는 늘 두 번째 질문이 깔려 있다. 스폰서는 자기 기술이 실제로 쓰이는 걸 보려고 상금을 건다. 그런데 확인 방법은 README를 읽고 발표 슬라이드에 로고가 있는지 보는 것뿐이다. 팀들도 이걸 안다.
이 질문의 일부는 기계로 확인할 수 있고, 일부는 안 된다.
확인 가능한 것: 저장소에 스폰서 플랫폼용 컨트랙트가 아예 있는가? 그 컨트랙트가 스타터 킷 예제에 이름만 바꾼 건 아닌가? 플랫폼의 차별적 기능을 쓰는가, 아니면 어떤 데이터베이스로도 가능한 부분만 쓰는가? 컴파일은 되는가?
이번 코호트에서는 164개 중 157개에 컨트랙트가 있었고, 정확히 1개가 예제 템플릿에 새 페인트를 칠한 수준이었으며, 157개 중 114개가 컨트랙트 자신이 선언한 컴파일러 버전으로 컴파일됐다. 마지막 조건이 중요한데, 뒤에서 다시 다룬다.
확인 불가능한 것: 그 사용이 의미 있었는지 여부다. 컨트랙트가 private input을 선언해놓고 아무 흥미로운 일도 하지 않을 수 있다. 도구는 심사위원에게 어디를 보라고 알려줄 수 있을 뿐, 무엇을 결론 내리라고 말할 수는 없다. 그렇게 주장하는 도구가 있다면 이 글이 반대하는 바로 그 일을 하고 있는 셈이다.
도구가 말해줄 수 없는 것, 그리고 필자가 틀린 것 #
빌드됨은 정확함과 다르다. "Tests passed"에도 단서가 붙는다. 대부분의 팀이 테스트가 이미 작성된 스타터 킷에서 출발했기 때문에, 통과한 테스트 스위트가 손대지 않은 스타터의 테스트일 수 있다. 도구는 아직 그 둘을 구분하지 못한다. 이 글에 테스트 통과율 수치가 없는 이유다.
도구는 "빌드되는가?" 하나만 답한다. 소프트웨어가 넘을 수 있는 가장 낮은 문턱이다. 독창성이나 난이도, 결과물의 질에 대한 의견은 없다. 야심도 보지 못한다. 어려운 걸 시도하다 컴파일 에러 하나로 못 넘긴 팀과 할 일 목록 앱을 낸 팀이 같은 칸에 놓인다. 그 차이는 심사위원이 본다. 도구는 그 심사위원이 "어느 쪽이 빌드되는지"까지 보게 만드는 역할이다. 심사 패널을 대체하는 게 아니라 옆에 두는 물건인 이유다.
그리고 실수 이야기다. 첫 버전은 저장소 최종 수정 시점 기준으로 가장 최신 컴파일러를 골랐다. 하지만 컨트랙트는 자기가 어떤 언어 버전으로 작성됐는지를 선언하고, 행사와 실행 사이에 새 컴파일러가 출시된 상태였다. 정상 동작하는 컨트랙트 42개가 실패로 돌아왔고, 그 책임은 고스란히 작성자 몫이 됐다. 도구가 선언부를 읽어 각 컨트랙트가 요구하는 컴파일러를 설치하도록 고치고 나서야 해결됐다.
자신만만한 오답을 잡아내려고 만든 도구가 자신만만한 오답을 냈다. 에러 메시지가 전부 똑같았던 덕분에 겨우 알아챘다. 다 끝났다고 생각한 뒤에 한 번 더 그랬다. 빌드 실패 6개는 팀이 자기 스크립트에 고정해둔 컴파일러 버전이 샌드박스에 없던 탓이었다. 6개 모두 빌드된다. 위 숫자는 수정된 값이다.
컴파일러 수정 과정에서 알아둘 만한 숫자가 하나 더 나왔다. 최신 릴리스로 컴파일했더니 정상 컨트랙트의 3분의 1이 빌드되지 않았다. 빠르게 움직이는 플랫폼은 초기 사용자에게 늘 이런 식이다. "동작하는가?"를 물을 때 지난주에 나온 도구가 아니라 팀이 실제로 쓴 도구로 물어야 하는 이유다.
다음 행사 전에 해볼 것 #
- 심사 시작 전에 모든 제출작을 돌리고, 심사위원에게 사다리를 건네라. 점수가 아니라 칸이다. 무엇이 중요한지는 여전히 패널이 정한다. 다만 어떤 프로젝트가 빌드되는지를 알고 정한다.
- 실패한 팀에게 머신을 줘라. 도구가 빌드가 깨진 순간을 저장해두므로, 팀은 그걸 열어 정확한 에러를 보고 고칠 수 있다. 대부분의 해커톤 팀은 이만큼 구체적인 피드백을 받아본 적이 없고, 머신 하나를 보관하는 비용은 사실상 0이다.
- "빌드되는가?"를 심사표에 넣어라. 그동안 빠져 있던 건 60개 팀분을 오후 한나절에 정직하게 채울 방법이 없었기 때문이다. 이제는 피자가 배달되는 시간이면 된다.
필자는 다음 단계로 자기 조언을 따르고 있다. 깨진 빌드들을 셸 권한과 시간 제한, 지시 하나를 가진 에이전트에게 넘기는 것이다. 빌드되게 만들고, 무엇이 문제였는지 팀에게 알려줘라. 자세한 내용은 다음 글에 있다.
도구는 GitHub github.com/laurenelee/hackjudge에 공개돼 있다. 작고 고집 있는 도구이며, 각자의 제출작에 직접 돌려봐도 된다.
이 글은 위 출처를 바탕으로 한국 독자를 위해 재작성한 기사입니다. 원문의 사실과 수치에 근거하며, 별도의 견해를 포함하지 않습니다.

Photo by