
게시판 API 를 만들기로 했을 때 Express 도 Prisma 도 GraphQL 도 매일 쓰는 스택이 아니었다.
그래도 설계는 해야 했다. 스택을 다 알지 못하는 상태에서 설계로 정할 수 있는 것은 어디까지인가?
아홉 단계로 나눠 배포까지 갔다. 직접 정한 것과 에이전트에게 넘긴 것을 나눠 남긴다.
1. 스택을 모를 때 먼저 정할 것
설계라고 하면 보통 레이어 그림부터 그린다. 그건 스택을 알 때 되는 일이다. Prisma 가 어떤 모양인지 모르는 채로 저장소 레이어를 그리면 그림만 그럴듯해진다.
대신 순서를 정한다. 단계마다 문제 하나와 통과 조건 하나만 적는다.
| 단계 | 문제 | 통과 조건 |
|---|---|---|
| 3 | 저장소 없이 흐름부터 | 서버를 껐다 켜면 글이 사라지는 것을 확인한다 |
| 4 | 껐다 켜도 남아야 한다 | 같은 글이 그대로 있다 |
| 5 | 잘못된 입력이 500 을 낸다 | 400 과 422 가 값으로 나뉜다 |
| 7 | 글이 1만 건이면 느리다 | 인덱스 전후를 같은 질의로 잰다 |
표 1. 아홉 단계 중 넷
3단계에 DB 가 없는 것이 핵심이다. 글을 배열에 담아 두고 서버를 껐다 켜면 전부 사라진다. 그것을 본 다음에 4단계로 간다.
순서가 설계다. 도구를 몰라도 무엇이 먼저 필요한지는 정할 수 있다. 도구는 그 자리에서 고르면 된다.
2. 직접 정할 결정과 넘길 결정의 구분
클라이언트를 무엇으로 할지, API 를 어떻게 이을지에서 에이전트가 멈추고 물었다. 되돌리기 어려운 결정이라 직접 답했다.
| 무엇을 | 어떻게 정했나 |
|---|---|
| 클라이언트 | Next.js |
| API 연동 | GraphQL. 단, 서버는 REST 로 두고 Next 안에 BFF 를 얹는다 |
| 클라이언트 구조 | DDD |
| 명세 공개 | OpenAPI 를 Swagger UI 로 띄운다 |
표 2. 직접 내린 결정 넷
파일을 어디에 둘지, 함수 이름을 뭐로 할지, 에러 코드를 몇 개로 나눌지, 인덱스 컬럼을 어떤 순서로 넣을지는 넘겼다.
기준은 한 줄이다. 되돌리기 어려운 것은 직접 정한다. 되돌릴 수 있는 것은 넘긴다. GraphQL 을 서버에 넣었다가 빼는 것은 큰일이다. 함수 이름은 나중에 바꾸면 된다.
3. 라이브러리가 없을 때의 처리
4단계로 넘어갈 때 답이 흐릿하게 왔다. "PostgreSQL 을 쓴다" 까지만 있고 어디에 어떻게 띄울지가 없었다. 그 자리에서 되물었더니 그다음에 도커 컴포즈와 볼륨 이야기가 나왔다.
모르는 것을 묻지 않고 넘어가면 대체품이 나온다. 규약이 그 자리에서 멈추게 한다.
- 라이브러리가 없으면 묻는다. 대체품을 직접 만들지 않는다
- 무엇이 없고, 무엇을 설치하면 되고, 안 쓰면 무엇이 달라지는지 셋을 적고 멈춘다
이 규칙에는 근거가 있다. 2026-09-11 에 three.js 로 만든 아티팩트를 옮기라고 했더니, 라이브러리가 없다는 말 없이 셰이더를 직접 써서 비슷한 것을 만들어 왔다. 설치 승인을 우회하는 가장 흔한 길이 직접 만드는 것이다.
설치·삭제·git push·외부 발행은 아예 승인 경계로 묶는다. 예고만으로는 부족하고 명시적인 승인이 있어야 한다.
4. 검증 결과를 수치로 받기
"통과했습니다" 는 보고가 아니다. 무엇을 어떻게 돌려서 어떤 값이 나왔는지가 보고다.
그래서 단계마다 게이트를 만든다. 셸 스크립트로 돌리고 통과·실패를 센다.
| 검사 | 무엇을 | 개수 |
|---|---|---|
| 검증·에러 | 상태 코드, 에러 형태, 내부 유출 | 16 |
| 인증·권한 | 완성 시나리오, 해시, 토큰, 쿠키 | 27 |
| 배포본 | 공개 URL 의 완성 시나리오 | 12 |
| 스모크 | 배포 직후 전 구간, 읽기만 | 5 |
표 3. 레포에 넣은 검사
측정도 같은 방식이다. 인덱스를 붙이기 전이 1.922 ms, 붙인 뒤가 0.049 ms 다. "빨라졌다" 가 아니라 두 값을 나란히 받는다.
확인하지 못한 값은 확인 불가로 남긴다. openapi.yaml 문법을 파서 없이 확인 못 했을 때가 그랬다. 나중에 Swagger UI 가 파싱해서 화면을 그리면서 닫혔다.
5. 반복되는 지시를 스킬로 옮기기
같은 지시를 세 번째 쓰게 되면 규약으로 옮긴다. 매번 프롬프트에 적는 것은 그때만 듣는다.
| 스킬 | 무엇을 고정하나 |
|---|---|
curriculum-step | 한 단계를 여는 절차와 통과 조건 |
code-doc-pairing | 코드 파일마다 짝 문서를 두고 양방향으로 맞춘다 |
verification-scripts | 게이트 스크립트의 골격과 종료 코드 |
browser-verify-traps | 브라우저로 확인할 때 걸리는 함정 |
표 4. 이 프로젝트에서 뽑아낸 스킬
문서를 여러 개 동시에 고치는 일은 에이전트 정의로 따로 뺐다. 담당 파일과 실측값만 브리핑에 넣고 나머지는 정의가 들고 있다.
검사 패턴이 없는 규칙은 다시 샌다. 금지어를 문서에만 적어 두었더니 31편을 윤문하는 동안 13곳으로 되돌아온 적이 있다. 그래서 규칙을 적을 때 검사도 같이 붙인다.
6. 확인하지 않은 값이 들어오는 자리
버전을 추측했다. README 배지에 GraphQL 16.11.0 이라고 적었는데 실제로는 17.0.2 였다. 같은 배지에 Docker 29.8.0 도 들어갔다. 그건 노트북에 깔린 CLI 버전이지 프로젝트 스택이 아니다.
검사 스크립트 자체에 버그가 있었다. 값을 꺼내는 헬퍼가 없는 값에 undefined 라는 다섯 글자 문자열을 찍었다. 받는 쪽은 그걸 토큰으로 읽었다. 레포로 옮긴 첫 실행에서 16개 중 8개가 실패했다. 서버는 멀쩡했다.
셋의 공통점은 같다. 확인하지 않고 그럴듯하게 쓴 것이다. 그래서 규약에 "확인 못 한 값은 확인 불가로 남긴다" 를 넣었다. 버전은 package.json 에서 다시 읽게 했다.
7. 이 방식의 비용과 한계
순서를 먼저 정하면 느리다. 3단계에서 배열에 담았다가 4단계에서 다시 짜는 것은 한 번 더 쓰는 일이다. 대신 인덱스가 무엇을 해결하는지 알고 붙이게 된다.
결정을 직접 하면 사람이 병목이 된다. 에이전트가 멈춰서 묻는 자리마다 답이 있어야 진행된다. 그 대신 되돌리기 어려운 결정이 조용히 내려가는 일이 없다.
수치 보고를 요구하면 시간이 든다. 게이트 43가지를 도는 데 수십 초가 걸린다. 그동안 DB 가 비워져 시드를 다시 넣어야 한다. 대신 "통과했다" 를 믿지 않아도 된다.
스킬로 굳히면 새 상황에서 방해가 된다. 규약이 늘수록 예외를 설명해야 할 자리도 는다.
8. 스택을 몰라도 정할 수 있는 것
설계를 도구 선택이라고 보면 못 한다. Express 와 Fastify 중 무엇이 나은지는 써 본 사람이 안다.
그런데 아홉 단계를 지나며 정한 것은 도구가 아니었다. 무엇이 먼저 필요한가, 그리고 이 단계가 끝났다는 것을 무엇으로 확인하나. 둘 다 스택을 몰라도 정할 수 있다.
도구는 그 자리에서 고르면 된다. 문제를 먼저 보고 나면 선택지가 줄어 있다. 글 1만 건이 느린 것을 본 다음에는 인덱스를 붙일지 말지가 취향 문제가 아니다.
추가로 확인할 사항
배포까지 갔고 검사 43가지가 main push 마다 돈다. 다만 되돌리기는 아직 자동이 아니다. 스테이징이 없어서 검사가 못 보는 것은 그대로 운영으로 나간다.
게시판 API 하나를 만드는 동안 Express 도 Prisma 도 끝까지 낯설었다. 그래도 설계에서 정할 것은 아홉 단계 내내 같은 둘이었다. 같은 방식으로 다시 한다면 그 둘을 정하는 데 시간을 더 쓰겠다.