페이지 목차
이 페이지에서 검색
AI 드리븐
이 페이지에 묻기
AI 드리븐 · 현재 차시와 목차 본문에서만 찾아 답합니다. 답을 지어내지 않고 해당하는 대목을 그대로 보여 주며, 여기에 없으면 없다고 알려 드립니다.
생성형 AI가 아니라 이 페이지 본문을 찾아 그대로 보여 주는 방식입니다. 외부로 나가는 요청이 없습니다.
AI · DEVELOPMENT
AI 드리븐
생성 수정
요구사항과 역할 정의부터 구현·검증·배포·운영까지, AI와 함께 개발하며 사람이 판단할 기준을 정리합니다.
26개 문서 · 2일차 · 11 / 26
10차시 · 백엔드 역할: 서버 로직
지도 위치 : 2일차 · 구현과 검증 · 구현 | 이전: 9차시 서비스 뼈대 세우기 | 다음: 11차시 프론트엔드 역할: 화면과 경계 키워드 : 설계 기반 구현, 서버의 판정·검증 원칙, 인터페이스 시험
이 차시를 다 읽으면
- 서버 판정 원칙 을 유와 종차로 말하고, 요청에 담겨 와도 믿으면 안 되는 값 네 가지 를 댈 수 있다.
- 인증과 인가 를 구별하고, 각각이 빠졌을 때의 응답 숫자(401 · 403)와 사고 모양을 클래스온 예로 설명할 수 있다.
- 구현 중에 명세가 이상할 때 명세를 고치지 않고 보고하게 하는 이유를 추상 ↔ 구상 축으로 설명할 수 있다.
- 서버 안의 세 계층(라우터 · 서비스 · 저장소) 이 각각 무엇을 맡는지 말하고, 그것이 "어디서?" 질문을 쉽게 만드는 이유를 설명할 수 있다.
- 인터페이스 시험 을 정상 1개 + 실패 여러 개로 설계하고, 결과를 표로 받아 판정할 수 있다.
0. 이 차시의 판별 축
| # | 축 | 왼쪽 (0) | 오른쪽 (1) | 이 축으로 갈리는 것 | 큰 축 (README 5장) |
|---|---|---|---|---|---|
| 1 | 누가 판정하는가 | 화면 — 사용자 기기 (조작 가능) | 서버 — 신뢰할 수 있는 곳 | 금액 · 남은 자리 · 권한을 어디서 정하는가 | 화면이 판정 ↔ 서버가 판정 (②의 파생) |
| 2 | 무엇을 묻는가 | 인증 — 누구인가 | 인가 — 이걸 해도 되는가 | 401 ↔ 403, 로그인 ↔ "본인 것인가" | 이 차시의 축 |
| 3 | 약속인가, 구현인가 | API 명세 (docs/api.md) | 서버 코드 | 구현이 따라야 할 것 ↔ 고쳐도 되는 것 | ② 추상 ↔ 구상 |
| 4 | 지난 요청을 기억하는가 | 무상태 — 요청마다 로그인 증표를 다시 확인 | 상태 — "아까 로그인했으니" | 매 요청마다 인증 · 인가를 하는가 | ⑦ 무상태 ↔ 상태 |
이 차시가 답하는 질문은 "서버는 무엇을 믿고, 무엇을 의심해야 하는가" 입니다. 앞에서 배운 API 명세(4차시)가 약속(축 3의 왼쪽)이라면, 이 차시는 그 약속을 지키는 실제 부품을 만듭니다. 그리고 그 부품이 지킬 가장 중요한 원칙이 축 1의 오른쪽, 판정은 서버가 한다 입니다.
1. 설계 기반 구현 — 명세가 먼저
1.1 백엔드 역할의 맥락
docs/api.md (약속) ─▶ 시험 목록 먼저 또는 함께 ─▶ 구현 ─▶ 명세와 대조 (5절)
▲
docs/requirements.md의 인수 기준 (3차시) · docs/adr/ (8차시)
앞에서 배운 역할 정의(5차시)대로, 백엔드 에이전트가 읽을 맥락은 명세 · 인수 기준 · 결정 기록 세 가지입니다. 이 셋이 없으면 에이전트는 "그럴듯한 예약 서버"를 상상해서 만듭니다.
1.2 명세가 이상하면 — 고치지 말고 보고
구현하다 보면 "이 응답에 필드 하나가 더 있으면 좋겠다" 같은 생각이 듭니다. 이때 백엔드 에이전트가 명세를 직접 고치지 않고 보고하게 합니다.
| 명세를 직접 고침 | 보고하고 사람이 결정 | |
|---|---|---|
| 프론트엔드 역할 | 모르는 채로 옛 명세대로 만든다 | 바뀐 명세를 함께 받는다 |
| 0절 축 3 | 구현이 약속을 끌고 간다 | 약속이 구현을 끌고 간다 |
명세는 화면과 서버가 함께 보는 약속 이기 때문입니다(⑤ 여럿으로 나눈 대가). 한쪽이 혼자 바꾸면 9차시의 "데이터 모양" 이음매가 깨집니다.
2. ⭐ 서버 판정 원칙
2.1 왜 화면은 판정할 수 없는가
클라이언트 — [요청을 보내는 프로그램]인데, [ 사용자 기기에서 돌아가서 사용자가 마음대로 조작할 수 있다 ]는 점에서 우리가 통제하는 서버와 구별된다. 브라우저 화면이 대표적입니다.
화면을 거치지 않고도 서버에 요청을 보낼 방법은 많습니다.
| 방법 | 누가 할 수 있나 |
|---|---|
| 브라우저 개발자 도구로 보낼 내용을 바꿔서 다시 보내기 | 브라우저가 있는 누구나 |
curl 같은 명령으로 화면 없이 직접 호출 | 주소를 아는 누구나 |
| 화면 코드를 고쳐 버튼 숨김 · 입력 제한을 풀기 | 브라우저가 있는 누구나 |
서버 판정 원칙 — [보안 설계 원칙]인데, [ 금액 · 남은 자리 · 권한처럼 결과를 바꾸는 판단을 서버가 자기 데이터로만 내리고, 요청에 담긴 판단 결과는 무시한다 ]는 점에서 화면에서 하는 입력 검사(편의용, 11차시)와 구별된다.
2.2 원칙 다섯 가지
| 원칙 | 의미 | 클래스온 예 |
|---|---|---|
| 클라이언트를 믿지 않는다 | 받은 값은 모두 다시 검사 | headcount가 1~4 사이 정수인지 |
| 판정은 서버가 한다 | 가격 · 재고 · 권한은 서버의 데이터로 계산 | 금액 = DB의 클래스 가격 × 인원. 요청에 들어 있는 금액은 무시 |
| 인증과 인가를 구분한다 | 3절 | 로그인했어도 남의 예약 은 취소 불가 |
| 실패는 명확하게 | 명세의 상태 코드와 에러 형식대로 | 자리 없음 → 409 SOLD_OUT |
| 함께 성공하거나 함께 실패 | 여러 변경을 트랜잭션 (모두 성공 또는 모두 취소로 묶는 단위, 12차시에서 정의)으로 | 자리 차감 + 예약 생성 |
상태 코드 는 서버가 응답과 함께 돌려주는 세 자리 숫자입니다(4차시). 이 노트에서는 200 · 201(성공), 400(입력이 잘못됨), 401(누구인지 모름), 403(알지만 권한 없음), 404(없음), 409(현재 상태와 충돌), 500(서버 내부 오류)만 알면 됩니다.
2.3 ⭐ 요청에 담겨 와도 믿지 않는 값
| 값 | 요청에 들어오면 | 서버는 어디서 가져오나 |
|---|---|---|
| 금액 | 무시 | DB의 클래스 가격 × 인원 |
| 사용자 ID | 무시 | 요청 본문이 아니라 로그인 증표(토큰) 에서 |
역할 (role: "admin") | 무시 | DB의 사용자 정보에서 |
상태 (status: "CONFIRMED") | 무시 | 서버의 처리 결과로만 바뀜 (16차시) |
⚠️ AI는 "화면에서 이미 검사했으니까" 서버 검사를 생략한 코드를 자주 만듭니다. 그래서 이 원칙은 백엔드 역할 정의와 지침 파일에 문장으로 적어 둡니다.
2.4 반례 — 이건 서버 판정이 아닙니다
| 대상 | 왜 아닌가 |
|---|---|
| 화면이 금액을 계산해 보내고, 서버는 "0보다 큰지"만 확인 | 형식만 검사했을 뿐 판정은 화면이 했다 |
| 화면에서 "취소" 버튼을 남의 예약에는 숨김 | 버튼을 숨겨도 주소를 알면 호출할 수 있다. 화면 편의일 뿐 |
서버가 요청 본문의 userId로 "본인 것인가"를 확인 | 그 userId를 바꿔 보내면 된다. 비교 기준 자체가 조작 가능 |
💡 비유는 마지막에 — 서버는 경기의 심판 입니다. 선수(화면)가 "골인입니다"라고 외쳐도 점수는 심판이 자기 눈으로 본 것으로만 올라갑니다.
3. 인증과 인가
3.1 정의와 경계
인증 (Authentication) — [접근 통제 단계]인데, [ 요청을 보낸 사람이 누구인지 확인한다 ]는 점에서 그 사람이 무엇을 해도 되는지 판단하는 인가와 구별된다.
인가 (Authorization) — [접근 통제 단계]인데, [ 확인된 사람이 이 대상에 이 행동을 해도 되는지 판단한다 ]는 점에서 신원만 확인하는 인증과 구별된다.
| 인증 | 인가 | |
|---|---|---|
| 묻는 것 | 누구인가? | 이 예약을 이 사람이 취소해도 되는가? |
| 판단 재료 | 로그인 증표(토큰) | 증표에서 꺼낸 사용자 + DB의 대상 데이터 |
| 실패 응답 | 401 | 403 (또는 남의 것이 있다는 사실도 숨기려면 404) |
| 빠지면 | 아무나 예약 생성 | 로그인한 누구나 남의 예약 을 보거나 취소 |
| 클래스온 예 | 로그인 안 하고 예약 → 거부 | 수강생 A가 B의 예약 취소 → 거부 |
⚠️ 인증은 인가의 전제 이지 대체물이 아닙니다. "로그인한 사람만 들어올 수 있으니 안전하다"는 인가가 빠진 서버의 전형적인 변명입니다.
3.2 인가 누락 — 가장 흔한 보안 사고
GET /reservations/345 ← 내 예약
GET /reservations/346 ← 주소의 숫자만 1 바꿈 → 남의 예약이 보인다면 인가 누락
🔍 읽는 포인트 : 주소의 숫자 하나 만 바꿔 보는 시험입니다. 코드를 몰라도 브라우저 주소창에서 할 수 있습니다.
3.3 서버는 매번 다시 확인한다 — 무상태
0절 축 4입니다. 서버는 "아까 이 사람이 로그인했으니"를 기억하지 않습니다. 요청마다 로그인 증표를 다시 확인하고(인증), 대상이 그 사람 것인지 다시 확인합니다(인가). 그래서 예약 조회 · 수정 · 취소 모든 경로 에 인가가 들어가야 합니다. 하나라도 빠지면 그 경로가 3.2의 구멍이 됩니다.
앞으로 다시 만납니다 — 14차시에서 인가 누락을 흔한 보안 사고 목록의 맨 앞에 놓고, 계정 두 개로 하는 행동 시험을 정식 점검 항목으로 만듭니다.
💡 비유는 마지막에 — 인증은 건물 출입증 , 인가는 방마다 다른 열쇠 권한 입니다. 출입증이 있다고 사장실에 들어갈 수 있는 것은 아닙니다.
4. 코드 계층 — 판정이 한 곳에 있게
계층 (Layer) — [코드 구조화 방식]인데, [ 책임별로 코드를 나누고 위층이 바로 아래층만 부르게 한다 ]는 점에서 기능별로 파일을 나누는 방식과 구별된다.
라우터 (경로 · 입력 모양 검사) ─▶ 서비스 (규칙 · 판정) ─▶ 저장소 (DB 읽기·쓰기)
🔍 읽는 포인트 : 판정은 가운데(서비스)에만 있어야 합니다. 금액 계산이 라우터나 저장소에도 있으면 판정이 여러 곳에 흩어진 것입니다.
| 계층 | 맡는 것 | 맡지 않는 것 |
|---|---|---|
| 라우터 | 요청 모양 검사(필수 필드, 타입), 응답 형식 변환 | "자리가 있는가" 같은 판단 |
| 서비스 | "자리가 있는가?", "취소 기한 안인가?", 금액 계산 | SQL 직접 작성 |
| 저장소 | DB 질의 실행만 | 규칙 판단 |
이 구조가 코드를 읽지 않는 사람에게 특히 중요한 이유 가 있습니다. "금액은 어디서 계산돼?"라고 물었을 때 답이 서비스 계층의 한 곳 으로 나와야 정상입니다. 답이 모호하거나 "여러 곳에서"가 나오면 그 자체가 위험 신호입니다. 이 구조는 지침 파일에 적어 두어 모든 에이전트가 같은 모양으로 만들게 합니다.
5. 인터페이스 시험 — 실제로 호출해 본다
구현이 명세와 맞는지는 읽어서가 아니라 호출해서 확인합니다.
# 정상
curl -X POST localhost:4000/reservations \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"classId":12,"date":"2026-11-01","headcount":1}'
# 기대: 201 {"id":..., "status":"PENDING_PAYMENT", "amount":55000}
# 금액 위조 시도 → 서버가 무시해야 함
-d '{"classId":12,"date":"2026-11-01","headcount":1,"amount":100}'
# 기대: 201, amount는 여전히 55000
# 잘못된 입력
-d '{"classId":12,"headcount":-1}'
# 기대: 400 {"error":{"code":"INVALID_INPUT",...}}
# 로그인 없이 (Authorization 줄을 빼고)
# 기대: 401
🔍 읽는 포인트 : 주소(/reservations)와 # 기대: 뒤의 숫자(201 · 400 · 401) , 그리고 위조 시도에서 amount가 55000으로 남는지 만 보면 됩니다.
| 시험 | 기대 | 확인하는 원칙 |
|---|---|---|
| 정상 예약 | 201, 서버가 계산한 금액 | 명세 일치 |
| 금액 100원으로 위조 | 201, 금액은 55000 | 판정은 서버가 |
| 인원 -1, 0, 5 | 400 | 클라이언트를 믿지 않는다 |
| 로그인 없이 | 401 | 인증 |
| 남의 예약 ID로 조회 | 403 또는 404 | 인가 |
⚠️ 정상 경우만 호출해 보고 "완료"라고 보고하는 것 이 AI 보고의 가장 흔한 빈틈입니다. 백엔드 역할의 완료 조건에 "정상 1개 + 실패 3개 이상의 호출 결과 원문" 을 넣어 둡니다.
앞으로 다시 만납니다 — 이 표는 그대로 17차시의 자동 테스트 목록이 됩니다. 사람이 한 번 손으로 확인한 것을 기계가 매번 확인하게 바꾸는 것입니다.
6. 클래스온에 적용하면
백엔드 에이전트에게 맡기는 순서입니다. 쉬운 것에서 판정이 많은 것으로 갑니다.
| 순서 | 대상 | 이 단계에서 새로 생기는 것 |
|---|---|---|
| 1 | GET /classes, GET /classes/{id} | 읽기만. 9차시 뼈대 위의 첫 기능 |
| 2 | 인증 (간단한 방식 또는 외부 인증 서비스) | 로그인 증표 |
| 3 | POST /reservations | 판정 · 트랜잭션 — 이 차시의 핵심 |
| 4 | GET /me/reservations, 예약 취소 | 인가 |
| 5 | 결제 승인 | 16차시 |
7. 실습 — 맡기고 검수하기
서버 코드를 읽는 대신 공격자처럼 시험 합니다. 이 차시의 원칙은 모두 행동으로 확인할 수 있습니다.
지시
docs/api.md의 POST /reservations와 GET /reservations/{id}에 대해 아래 시험을 하는 스크립트를
scripts/attack-test.sh로 만들고 실행해줘.
결과는 [시험 | 기대 | 실제 응답 숫자 | 실제 금액 | 통과?] 표로 보고하고, 실행 출력 원문을 붙여줘.
1. 정상 예약 2. 금액을 100원으로 바꿔 보내기 3. 인원 -1, 0, 5
4. 로그인 없이 5. 사용자 B의 토큰으로 사용자 A의 예약 조회
그리고 아래 두 질문에 [파일 | 줄 | 그 줄이 하는 일 한 문장]으로 답해줘.
- 금액은 어디서 계산돼? 화면에서 보낸 값이 그 계산에 들어가는 경로가 하나라도 있어?
- 사용자 ID는 어디서 가져와? 요청 본문에서 가져오는 곳이 하나라도 있어?
받을 증거
- 시험 결과 표와 스크립트 실행 출력 원문
- 금액 계산 · 사용자 ID 출처의 위치(파일 · 줄)
내가 직접 확인할 것
| 볼 것 | 이걸로 아는 것 / 왜 내가 보는가 |
|---|---|
| 결과 표의 금액 위조 줄 : 실제 금액이 55000인가 | 판정이 서버에 있는지. 100이 보이면 즉시 멈춤 |
| 계정 두 개를 만들고, A로 예약한 뒤 B로 로그인해 그 예약 주소를 연다 . 주소의 숫자도 1씩 바꿔 본다 | 인가 누락. AI의 표가 아니라 내 눈으로 보는 행동 시험 |
| "어디서?" 답이 서비스 계층의 한 곳 인가 | 모호하거나 여러 곳이면, 13차시의 리뷰 역할에게 같은 질문을 따로 해서 답을 비교 (README 9.1의 교차 검증) |
| 보고에 "아마", "~해야 합니다"가 있는가 | 실행하지 않았다는 신호 (README 9.4) |
💡 curl이 낯설다면 API 시험 도구(Postman, Bruno 등)로 화면을 클릭하는 방식으로 같은 요청을 직접 보내 볼 수도 있습니다.
부록 A. 스스로 확인하기
| # | 질문 | 답이 있는 곳 |
|---|---|---|
| 1 | 구현 중 명세가 이상할 때 고치지 말고 보고하게 하는 이유는? | 1.2 |
| 2 | 클라이언트를 믿을 수 없는 이유를 세 가지 방법으로 말하면? | 2.1 |
| 3 | 서버 판정 원칙을 유와 종차로 정의하면? | 2.1 |
| 4 | 요청에 담겨 와도 믿지 않는 값 네 가지와 각각의 출처는? | 2.3 |
| 5 | 인증과 인가가 빠지면 각각 어떤 사고가 나고, 응답 숫자는? | 3.1 |
| 6 | 주소의 숫자 하나로 인가 누락을 시험하는 방법은? | 3.2 |
| 7 | 서버가 요청마다 다시 확인하는 것이 어느 축과 관련되는가? | 0, 3.3 |
| 8 | 판정이 서비스 계층 한 곳에 있어야 하는 이유는? | 4 |
| 9 | 인터페이스 시험에서 정상 외에 넣을 경우는? | 5 |
부록 B. 자주 하는 실수
| 실수 | 바로잡기 |
|---|---|
| 요청의 금액 · 사용자 ID · 역할을 그대로 쓴다 | ❌ 금액은 DB, 사용자 ID는 로그인 증표에서 (2.3) |
| "로그인한 사람만 들어오니 안전하다" | ❌ 인증은 인가를 대신하지 않습니다 (3.1) |
| 에러를 전부 500으로 돌려준다 | ⚠️ 명세의 상태 코드와 에러 형식대로 (2.2) |
| 에러 메시지에 SQL이나 내부 경로를 노출한다 | ❌ 일반 메시지만 (14차시) |
| 정상 경우만 호출해 보고 완료 처리 | ❌ 정상 1 + 실패 3 이상 (5) |
| 백엔드 에이전트가 명세를 직접 고친다 | ⚠️ 보고하고 사람이 결정 (1.2) |
| 금액 계산이 여러 파일에 있다 | ⭕ 서비스 계층 한 곳으로 모으게 합니다 (4) |
부록 C. NotebookLM 복습 프롬프트
이 10차시 노트만 근거로 5문제를 내줘. 비전공자 기준으로.
"서버 판정 원칙과 믿지 않는 값", "인증과 인가의 구별(401/403)", "인터페이스 시험 설계"는 반드시 포함해줘.
답은 내가 답한 다음에 채점하고, 틀린 이유를 노트의 절 번호를 근거로 설명해줘.
LangTailor