그루뽑기 — 프론트와 백엔드를 붙인 첫 서비스
무엇을 만들었나
조에서 매일 이끄미와 시간지키미를 공정하게 뽑고 그대로 학습 타이머로 이어지는 웹 서비스. 화면만 있던 목업에 서버와 데이터베이스를 붙여 실제로 쓰는 주소까지 올렸다.
5회차 수업의 “프론트와 백엔드를 붙인 서비스 만들기” 과제다. 앞선 두 작업물(2048, 벽돌깨기)은 파일 하나로 끝났지만, 이번엔 서버와 데이터베이스가 붙었다. 내 화면에서 누른 것이 다른 사람 화면에도 보여야 하니까.
직접 해보기 → rolepicker-pink.vercel.app
코드 보기 → github.com/songyee-ai/rolepicker
설치도 로그인도 없다. 조를 만들면 나오는 링크를 조 채팅방에 한 번 올리면 그날부터 쓴다.
1. 만들기 전 스케치
이번엔 손에 든 게 두 개였다. 요구사항 문서(PRD)와 화면 목업 HTML. 목업은 브라우저에서 열리는 실제 화면이고, 각 화면 아래에 그렇게 만든 이유가 적혀 있다. 색·서체·간격은 여기서 그대로 가져가게 했다.
핵심 가치는 랜덤이 아니라 “고루 돌아갔다는 것이 눈에 보이는 것”이었다. 그래서 마지막 화면이 지난 기록이다.
2. 만드는 과정 — 프롬프트 기록
시도 1 — 문서 두 개와 진행 규칙 넣기
파일 두 개를 첨부하고, 무엇을 만들지보다 어떻게 진행할지를 먼저 적었다.
- 마일스톤 순서대로 간다. M1(뽑기) → M2(타이머) → M3(지난 기록) → M4(다듬기). M1을 끝내면 나에게 확인받고 넘어갈 것
- M1의 첫 작업은 화면이 아니라 배정 알고리즘과 그 단위 테스트. 조용히 터지는 경우들을 각각 테스트로 만들 것 — 쿨다운 때문에 후보가 0명이 되는 경우, 인원이 역할 수보다 적은 경우, 한 번도 안 맡은 사람이 섞인 경우, 한 사람이 두 역할을 겸하지 않는지, 12명 조 2주 시뮬레이션에서 횟수 차이가 1 이하인지
- 애매하면 설계 원칙 7개로 돌아갈 것
- 문서에 없어서 판단이 필요하면 임의로 정하지 말고 물어볼 것. 목업과 다르게 만드는 게 낫다고 판단되면 이유를 말하고 물어볼 것
- 코드를 쓰기 전에 프로젝트 구조와 파일 목록을 먼저 보여주고 승인받을 것
중간에 한 줄 더 붙였다.
내가 프로그래밍 관련 아예 아무것도 모르는 수준이라 조금 이해하기 쉽게 설명해줘.
이 한 줄 덕분에 이후 설명이 전부 “이건 왜 필요하냐면”으로 시작했다. 모른다고 먼저 말하는 게 손해가 아니었다.
알고리즘을 먼저 만들었더니 숫자가 나왔다
화면보다 먼저 배정 알고리즘을 만든 덕분에, 화면 없이 2주치를 200번 돌려보는 일이 가능했다. 그리고 거기서 문제가 하나 나왔다.
요구사항에는 다시 뽑기가 “같은 방식으로 한 번 더 돌린다”고만 적혀 있었다. 그대로 만들어 돌려 보니 5명 조에서 49%가 같은 사람이 다시 나왔다. 쿨다운을 거치면 후보가 평균 2.3명뿐이라서다. 버튼을 눌러도 아무 일도 안 일어난 것처럼 보인다.
그래서 문서를 고쳤다 — 그 역할에 한해 직전 당첨자를 후보에서 뺀다. 후보가 0명이 되는 작은 조에서는 되돌린다.
화면이 나온 뒤 내가 찾은 것들
M1 확인 단계에서 직접 눌러 보고 문제를 찾아 돌려보냈다.
| 내가 찾은 것 | 어떻게 고쳤나 |
|---|---|
| 명단에 사람을 추가하고 저장해도 결과 화면에 반영되지 않음 | 명단을 고쳤으면 다시 뽑기를 거친 뒤 결과 화면으로 오도록 |
| “오늘은 이미 뽑았어요”가 아직 안 뽑은 날에도 떠 있음 | 그날 배정이 실제로 있는지 보고 표시 |
| 최근 조 목록을 고르자마자 바로 넘어감 | 고른 다음 ○○의 조 열기를 눌러야 넘어가도록 |
| 새 조 만들기로 잘못 들어가면 돌아갈 방법이 없음 | 왼쪽 위에 홈으로 가는 길 추가 |
| 빈자리가 없는데도 버튼에 “빈자리 반영해서 다시뽑기” | 상황에 맞는 문구로 분리 |
그리고 만들다 보니 궁금해져서 물어본 것.
그럼 어떤 사람이 수정을 할 수 있는거야? 링크를 가지고 있는 모두에게 권한이 있어?
답은 “링크를 아는 사람 모두”였다. 로그인을 넣지 않기로 한 대가다. 이건 숨길 게 아니라 적어야 할 것이라, 사용 안내에 “링크가 열쇠다. 조 채팅방 밖으로 내보내지 않는다”를 넣었다.
타이머 — 조 전체가 함께 쓰게
M2에서 요구한 건 하나였다. 한 명이 시작을 누르면 조원 전원의 화면이 같이 타이머로 바뀌고, 누가 일시정지해도 다 같이 멈춘다. 컨트롤만 시간지키미가 더 가진다.
만들면서 나온 질문에 그때그때 정했다 — 종료는 어떻게 하나(오늘 학습 끝내기 추가), 도중에 시간을 다시 정하고 싶으면(시간 다시 정하기 추가, 바뀐 시간은 바로 적용, 아무나 누르되 한 번 확인), 버튼은 1+1+2 배치로.
시도 2 — 배포
M3(지난 기록)과 M4(사용법 화면·설치형 앱 설정)를 끝내고 배포로 갔다. 이때 물어본 게 이 작업에서 제일 중요한 질문이었던 것 같다.
내가 배포하면 현재 내가 링크를 전달하는 우리 수업 그루들 외에 다른 사람들도 쓸 수 있어? 혹은 나쁜짓을 할 수도 있어?
그래서 공개 전에 두 곳을 막았다. 조 만들기 요청 제한(한 곳에서 한 시간에 20개 — 자동 프로그램이 조를 계속 만들어 데이터베이스를 채우는 걸 막는다)과 조 화면을 검색엔진에서 제외. 저장소를 공개로 바꾸기 전에 민감한 값이 남아 있는지 한 번 더 확인했다.
며칠 뒤 — 코드 리뷰를 받았다
9월 9일에 같은 과정 사람에게 코드 피어 리뷰를 받았다. 저장소를 내려받아 검사를 직접 돌려 본 결과였다.
npm test → 169개 통과
npm run lint → 경고·오류 없음
배포 주소 → 200
“공정한 랜덤”이 한 함수에 다 모여 있고 그게 실제 서버까지 연결돼 있다는 점을 짚어 줬다. 남이 내 저장소를 받아서 돌려 본다는 걸 알고 나니, README와 문서를 왜 남겨야 하는지가 그제야 체감됐다.
3. 완성 화면
4. 코드에서 배운 부분
4-1. 브라우저는 데이터베이스를 직접 만지지 않는다
이번 회차에서 제일 크게 배운 것. 화면과 데이터베이스 사이에 서버가 한 겹 있고, 열쇠는 그 안에서만 산다.
폴더를 front / back / shared로 나눠 달라고 한 건 나였는데, 나눠 놓고 보니 “이 값이 브라우저까지 내려가도 되나”를 매번 생각하게 됐다. 경계가 지켜지는지 확인하는 검사까지 넣어 뒀다.
4-2. 남은 시간을 각자 세면 어긋난다
타이머에서 제일 헷갈렸던 부분이다.
각자의 화면이 1초씩 세면 화면마다 숫자가 어긋나고, 늦게 들어온 사람은 처음부터 다시 센다. 그래서 “언제 시작했나”만 서버에 적고 남은 시간은 그때그때 계산한다. 각 화면은 3초마다 서버에 물어보고, 누가 일시정지를 누르면 다음 확인 때 전원이 같이 멈춘다.
4-3. 테스트가 있으면 “돌려볼 수” 있다
배정 알고리즘은 데이터베이스도, 현재 시각도, 난수도 직접 만지지 않고 전부 인자로 받는다. 그래서 화면 없이 2주치를 200번 돌려 공정성 숫자를 뽑을 수 있었고, 49% 문제도 그래서 발견됐다.
“테스트를 왜 쓰나” 싶었는데, 이번엔 테스트가 검사지가 아니라 실험 도구였다.
5. 막혔던 점과 해결
| 막힌 지점 | 원인 | 해결 방법 |
|---|---|---|
| 다시 뽑기를 눌러도 같은 사람이 계속 나옴 | 요구사항대로 같은 방식을 재실행 — 후보가 평균 2.3명이라 49% 재당첨 | 그 역할에 한해 직전 당첨자 제외 (그림 2) |
| 명단을 고쳐도 결과에 반영되지 않음 | 고친 명단으로 다시 뽑는 단계가 빠져 있었다 | 저장 → 다시 뽑기 → 결과 순서로 수정 |
| Vercel 대시보드에서 어디를 눌러야 할지 모름 | 배포 도구를 처음 써 봄 | 화면에 보이는 그대로 단계를 다시 설명받아 진행 |
| 작업 중 사용량 한도에 걸려 중단 | 한 번에 다 만들기엔 큰 작업이었다 | 한도가 풀린 뒤 마일스톤 단위로 이어서 진행 |
| 공개해도 되는지 판단이 안 섬 | 로그인이 없어 링크만 알면 누구나 들어온다 | 요청 제한 + 검색 제외로 막고, 남는 위험은 안내 문구로 적음 |
6. 다음에 개선할 것
- 2주 실제로 써 보고
지난 기록숫자를 보기 — 공정성 규칙을 한 줄 더 조일지(가장 적게 맡은 사람만 후보) 그때 정한다 - 조원이 직접 출결을 표시하게 하기 — 지금은 한 사람이 전원의 빈자리를 체크한다
- 타이머가 끝났을 때 알림
- 리뷰에서 받은 지적 반영하기
- 다음엔 화면 하나라도 내가 직접 고쳐 보기