무엇을 만들었나

조에서 매일 이끄미와 시간지키미를 공정하게 뽑고 그대로 학습 타이머로 이어지는 웹 서비스. 화면만 있던 목업에 서버와 데이터베이스를 붙여 실제로 쓰는 주소까지 올렸다.

5회차 수업의 “프론트와 백엔드를 붙인 서비스 만들기” 과제다. 앞선 두 작업물(2048, 벽돌깨기)은 파일 하나로 끝났지만, 이번엔 서버와 데이터베이스가 붙었다. 내 화면에서 누른 것이 다른 사람 화면에도 보여야 하니까.

직접 해보기rolepicker-pink.vercel.app

코드 보기github.com/songyee-ai/rolepicker

설치도 로그인도 없다. 조를 만들면 나오는 링크를 조 채팅방에 한 번 올리면 그날부터 쓴다.


1. 만들기 전 스케치

이번엔 손에 든 게 두 개였다. 요구사항 문서(PRD)화면 목업 HTML. 목업은 브라우저에서 열리는 실제 화면이고, 각 화면 아래에 그렇게 만든 이유가 적혀 있다. 색·서체·간격은 여기서 그대로 가져가게 했다.

첫 화면부터 조원 확인, 오늘의 역할, 학습 시간 정하기, 타이머, 지난 기록으로 이어지는 화면 흐름도
그림 1. 화면 여섯 개 — 링크 하나로 조원 전원이 같은 흐름을 탄다

핵심 가치는 랜덤이 아니라 “고루 돌아갔다는 것이 눈에 보이는 것”이었다. 그래서 마지막 화면이 지난 기록이다.


2. 만드는 과정 — 프롬프트 기록

시도 1 — 문서 두 개와 진행 규칙 넣기

파일 두 개를 첨부하고, 무엇을 만들지보다 어떻게 진행할지를 먼저 적었다.

  • 마일스톤 순서대로 간다. M1(뽑기) → M2(타이머) → M3(지난 기록) → M4(다듬기). M1을 끝내면 나에게 확인받고 넘어갈 것
  • M1의 첫 작업은 화면이 아니라 배정 알고리즘과 그 단위 테스트. 조용히 터지는 경우들을 각각 테스트로 만들 것 — 쿨다운 때문에 후보가 0명이 되는 경우, 인원이 역할 수보다 적은 경우, 한 번도 안 맡은 사람이 섞인 경우, 한 사람이 두 역할을 겸하지 않는지, 12명 조 2주 시뮬레이션에서 횟수 차이가 1 이하인지
  • 애매하면 설계 원칙 7개로 돌아갈 것
  • 문서에 없어서 판단이 필요하면 임의로 정하지 말고 물어볼 것. 목업과 다르게 만드는 게 낫다고 판단되면 이유를 말하고 물어볼 것
  • 코드를 쓰기 전에 프로젝트 구조와 파일 목록을 먼저 보여주고 승인받을 것

중간에 한 줄 더 붙였다.

내가 프로그래밍 관련 아예 아무것도 모르는 수준이라 조금 이해하기 쉽게 설명해줘.

이 한 줄 덕분에 이후 설명이 전부 “이건 왜 필요하냐면”으로 시작했다. 모른다고 먼저 말하는 게 손해가 아니었다.

알고리즘을 먼저 만들었더니 숫자가 나왔다

화면보다 먼저 배정 알고리즘을 만든 덕분에, 화면 없이 2주치를 200번 돌려보는 일이 가능했다. 그리고 거기서 문제가 하나 나왔다.

오늘 온 사람 걸러내기, 쿨다운, 가중치, 뽑기로 이어지는 배정 알고리즘과 다시 뽑기 수정 내용
그림 2. 공정한 랜덤 — 그리고 설계안을 고친 곳

요구사항에는 다시 뽑기가 “같은 방식으로 한 번 더 돌린다”고만 적혀 있었다. 그대로 만들어 돌려 보니 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. 완성 화면

새 조 만들기와 최근 조 목록이 있는 첫 화면
그림 3. 첫 화면 — 링크를 잃어버렸으면 코드로도 들어간다
오늘 온 조원과 빈자리를 확인하는 화면
그림 4. 누가 왔는지 확인 — 못 온 사람은 지우지 말고 빈자리로. 지우면 그 사람의 기록이 통째로 사라진다
이끄미와 시간지키미가 뽑힌 오늘의 역할 화면
그림 5. 오늘의 역할 — 역할마다 무엇을 하는지도 함께 적었다
학습 시간과 쉬는 시간을 정하고 시작 버튼 위에 시간지키미 안내가 있는 화면
그림 6. 학습 시간 정하기 — 시작 버튼 위의 안내는 '아무나 누르기 전에 한 번 읽히게' 고친 자리다
학습 시간이 흐르고 있는 타이머 화면
그림 7. 타이머 — 조원 전원의 화면이 같이 움직인다
조원별 이끄미·지키미·그루 횟수가 표로 정리된 지난 기록 화면
그림 8. 지난 기록 — 이 화면 하나 때문에 알고리즘을 먼저 만들었다

4. 코드에서 배운 부분

4-1. 브라우저는 데이터베이스를 직접 만지지 않는다

이번 회차에서 제일 크게 배운 것. 화면과 데이터베이스 사이에 서버가 한 겹 있고, 열쇠는 그 안에서만 산다.

브라우저와 서버와 데이터베이스가 나뉘고 비밀값이 서버 폴더 밖으로 나가지 않는 구조도
그림 9. 프론트와 백엔드를 붙인다는 건, 어디까지 내려가는지를 정하는 일이었다

폴더를 front / back / shared로 나눠 달라고 한 건 나였는데, 나눠 놓고 보니 “이 값이 브라우저까지 내려가도 되나”를 매번 생각하게 됐다. 경계가 지켜지는지 확인하는 검사까지 넣어 뒀다.

4-2. 남은 시간을 각자 세면 어긋난다

타이머에서 제일 헷갈렸던 부분이다.

서버에 시작 시각만 적고 각 조원 화면이 3초마다 확인해 같은 숫자를 보여주는 구조
그림 10. 시작한 시각만 적고, 남은 시간은 계산한다

각자의 화면이 1초씩 세면 화면마다 숫자가 어긋나고, 늦게 들어온 사람은 처음부터 다시 센다. 그래서 “언제 시작했나”만 서버에 적고 남은 시간은 그때그때 계산한다. 각 화면은 3초마다 서버에 물어보고, 누가 일시정지를 누르면 다음 확인 때 전원이 같이 멈춘다.

4-3. 테스트가 있으면 “돌려볼 수” 있다

배정 알고리즘은 데이터베이스도, 현재 시각도, 난수도 직접 만지지 않고 전부 인자로 받는다. 그래서 화면 없이 2주치를 200번 돌려 공정성 숫자를 뽑을 수 있었고, 49% 문제도 그래서 발견됐다.

“테스트를 왜 쓰나” 싶었는데, 이번엔 테스트가 검사지가 아니라 실험 도구였다.


5. 막혔던 점과 해결

막힌 지점 원인 해결 방법
다시 뽑기를 눌러도 같은 사람이 계속 나옴 요구사항대로 같은 방식을 재실행 — 후보가 평균 2.3명이라 49% 재당첨 그 역할에 한해 직전 당첨자 제외 (그림 2)
명단을 고쳐도 결과에 반영되지 않음 고친 명단으로 다시 뽑는 단계가 빠져 있었다 저장 → 다시 뽑기 → 결과 순서로 수정
Vercel 대시보드에서 어디를 눌러야 할지 모름 배포 도구를 처음 써 봄 화면에 보이는 그대로 단계를 다시 설명받아 진행
작업 중 사용량 한도에 걸려 중단 한 번에 다 만들기엔 큰 작업이었다 한도가 풀린 뒤 마일스톤 단위로 이어서 진행
공개해도 되는지 판단이 안 섬 로그인이 없어 링크만 알면 누구나 들어온다 요청 제한 + 검색 제외로 막고, 남는 위험은 안내 문구로 적음

6. 다음에 개선할 것

  • 2주 실제로 써 보고 지난 기록 숫자를 보기 — 공정성 규칙을 한 줄 더 조일지(가장 적게 맡은 사람만 후보) 그때 정한다
  • 조원이 직접 출결을 표시하게 하기 — 지금은 한 사람이 전원의 빈자리를 체크한다
  • 타이머가 끝났을 때 알림
  • 리뷰에서 받은 지적 반영하기
  • 다음엔 화면 하나라도 내가 직접 고쳐 보기

관련 용어

PRD · API · 스키마 · MVP · GitHub