페이지 목차
이 페이지에서 검색
AI 드리븐
이 페이지에 묻기
AI 드리븐 · 현재 차시와 목차 본문에서만 찾아 답합니다. 답을 지어내지 않고 해당하는 대목을 그대로 보여 주며, 여기에 없으면 없다고 알려 드립니다.
생성형 AI가 아니라 이 페이지 본문을 찾아 그대로 보여 주는 방식입니다. 외부로 나가는 요청이 없습니다.
AI · DEVELOPMENT
AI 드리븐
생성 수정
요구사항과 역할 정의부터 구현·검증·배포·운영까지, AI와 함께 개발하며 사람이 판단할 기준을 정리합니다.
26개 문서 · 2일차 · 12 / 26
11차시 · 프론트엔드 역할: 화면과 경계
지도 위치 : 2일차 · 구현과 검증 · 구현 | 이전: 10차시 백엔드 역할: 서버 로직 | 다음: 12차시 데이터 관리 역할 키워드 : 화면과 서버의 역할 경계, 데이터 연동, 연동 시 주의점
이 차시를 다 읽으면
- 화면과 서버가 맡는 일을 표로 가르고, "화면의 검사는 편의를 위한 것" 이라는 말을 예로 설명할 수 있다.
- 공개 환경변수 를 유와 종차로 정의하고, 환경변수 이름만 보고 비밀값 노출을 판별할 수 있다.
- CORS 가 무엇을 막고 무엇을 막지 않는지 말할 수 있다.
- API 호출을 한 곳에 모으기 · 요청과 응답 모양 공유 · 네 가지 화면 상태가 각각 어떤 문제를 막는지 말할 수 있다.
- 브라우저 개발자 도구의 Network 탭 으로 화면이 서버에 보낸 것과 받은 것을 직접 확인할 수 있다.
0. 이 차시의 판별 축
| # | 축 | 왼쪽 (0) | 오른쪽 (1) | 이 축으로 갈리는 것 | 큰 축 (README 5장) |
|---|---|---|---|---|---|
| 1 | 누가 판정하는가 | 화면 — 보여 주고 입력받고 전달 | 서버 — 금액 · 자리 · 권한 판정 | 같은 규칙이 두 군데 생기는가 | 화면이 판정 ↔ 서버가 판정 (②의 파생) |
| 2 | 사용자 브라우저로 전달되는가 | 공개 — 화면 코드에 박혀 누구나 봄 | 비밀 — 서버 안에만 있음 | 공개 키 ↔ 비밀 키, 공개 환경변수 ↔ 일반 환경변수 | 이 차시의 축 |
| 3 | 어디서 돌아가는가 | 로컬 — localhost | 원격 — 배포된 주소 | 서버 주소 · 허용 출처가 환경마다 달라짐 | ④ 로컬 ↔ 원격 |
| 4 | 응답을 기다려서 표시하는가 | 기다린다 — 서버 응답 후 "예약 완료" | 기다리지 않는다 — 누르자마자 "예약 완료" | 실패했는데 성공으로 보이는 사고 | ⑥ 동기 ↔ 비동기 |
이 차시가 답하는 질문은 "화면은 어디까지 하고, 어디서 멈춰야 하는가" 입니다. 10차시에서 판정을 서버에 두었으니, 이번에는 그 반대편에서 같은 경계를 봅니다. 입력은 앞에서 배운 디자인 규칙과 검증을 통과한 시안(6 · 7차시), 그리고 API 명세(4차시)입니다.
1. ⭐ 화면과 서버의 경계
1.1 정의와 경계표
프론트엔드 (화면) — [서비스의 한 층]인데, [ 사용자 기기에서 돌아가며 보여 주고, 입력받고, 서버의 판정을 전달한다 ]는 점에서 판정을 내리는 서버(백엔드)와 구별된다.
| 일 | 화면 | 서버 |
|---|---|---|
| 입력 형식 검사 (빈칸, 숫자) | ⭕ 사용자 편의용 | ⭕ 진짜 검사 |
| 금액 계산 | 표시만 (서버가 준 값) | ⭕ |
| 남은 자리 판단 | 표시만 | ⭕ |
| 권한 판단 | 버튼 숨김 정도 (편의용) | ⭕ 진짜 차단 |
| 로딩 · 에러 표시 | ⭕ | — |
| 비밀값 사용 | ❌ 절대 안 됨 | ⭕ |
화면의 검사는 편의를 위한 것이고, 보안을 위한 것이 아니다.
화면에서 버튼을 숨겨도 API는 직접 호출할 수 있습니다(10차시). 화면의 검사는 사용자가 실수를 서버에 보내기 전에 알려 주는 친절일 뿐입니다.
1.2 경계가 흐려지면 — 같은 규칙이 두 군데
| 상황 | 결과 |
|---|---|
| 취소 가능 기한을 화면은 "3일 전까지", 서버는 "2일 전까지"로 판단 | 화면엔 "취소" 버튼이 보이는데 누르면 거절당함 |
⭕ 서버가 응답에 "cancellable": true를 담아 주고 화면은 그 값만 따름 | 규칙은 서버 한 곳에만 있음 |
규칙이 두 군데 있으면 언젠가 반드시 달라집니다. 한쪽만 고쳐지기 때문입니다. 그래서 화면은 판정 결과를 받아서 보여 주기만 합니다.
1.3 반례 — 이건 화면의 일이 아닙니다
| 대상 | 왜 아닌가 |
|---|---|
| 화면이 "가격 × 인원"을 계산해 서버로 보냄 | 판정입니다. 화면은 서버가 준 금액을 표시만 |
| 남은 자리 0이면 화면이 예약 요청을 아예 안 보냄 (그리고 서버는 검사 안 함) | 편의로는 좋지만, 서버 검사를 대신할 수 없음 |
| 서버가 아직 없어서 화면에 가짜 데이터를 박아 둠 → 잊음 | 연동이 됐는지 안 됐는지 화면만 봐서는 모름. 연동 뒤 반드시 제거 |
2. 데이터 연동
2.1 API 호출을 한 곳에 모으기
apps/web/src/api/
├── client.ts # 서버 주소, 로그인 증표 붙이기, 에러 형식 처리를 한 곳에서
├── classes.ts # getClasses(), getClass(id)
└── reservations.ts # createReservation(), getMyReservations()
🔍 읽는 포인트 : 서버와 통신하는 코드가 api/ 폴더 한 곳 에 있는지만 보면 됩니다. 화면 조각마다 서버 호출이 흩어져 있으면 에러 처리가 제각각이 됩니다.
2.2 요청 · 응답 모양을 공유하기
9차시 뼈대에서 만든 packages/shared에 요청 · 응답의 모양(필드 이름과 타입)을 두고 화면과 서버가 함께 씁니다. 한쪽이 모양을 바꾸면 다른 쪽에서 타입 오류로 바로 드러납니다. 9차시의 "데이터 모양" 이음매를 기계가 지켜 주는 장치입니다.
2.3 네 가지 화면 상태
앞에서 배운 디자인 규칙(6차시)의 네 상태를 실제 데이터와 연결합니다.
| 상태 | 화면 | 빠지면 |
|---|---|---|
| 로딩 중 | 스켈레톤(회색 자리 표시) | 빈 화면에서 사용자가 버튼을 여러 번 누름 |
| 성공 · 데이터 있음 | 목록 | — |
| 성공 · 데이터 없음 | "아직 클래스가 없어요" 안내 | 고장 난 것처럼 보임 |
| 실패 | 에러 코드별 안내 (SOLD_OUT → "방금 마감됐어요") | alert("에러 발생") 하나로 끝남 |
3. ⭐ 공개 환경변수와 비밀값
3.1 정의
앞에서 배운 환경변수(9차시)에는 두 종류가 있습니다.
공개 환경변수 — [환경변수]인데, [ 화면을 만들(빌드할) 때 코드 속에 그대로 박혀 사용자 브라우저로 전달된다 ]는 점에서 서버 안에서만 읽히는 일반 환경변수와 구별된다.
화면 도구들은 이름 앞에 정해진 접두사 가 붙은 환경변수만 화면 코드에 넣어 줍니다.
NEXT_PUBLIC_... VITE_... REACT_APP_... PUBLIC_...
🔍 읽는 포인트 : 이 접두사가 붙은 이름은 "누구나 볼 수 있음" 표시입니다. 뒤에 SECRET, PRIVATE, PASSWORD, DATABASE_URL이 붙어 있으면 코드를 몰라도 즉시 사고 라고 판단할 수 있습니다.
3.2 경계 — 공개해도 되는 것과 안 되는 것
| 공개 (화면에 둬도 됨) | 비밀 (서버에만) | |
|---|---|---|
| 예 | 서버 주소, 결제 대행사의 공개 키 (클라이언트 키) | 결제 비밀 키 , DB 비밀번호, 로그인 증표 서명 키 |
| 노출되면 | 문제없음 — 원래 보이라고 만든 값 | 남이 내 권한으로 결제 승인 · 취소, DB 접속 |
| 0절 축 2 | 왼쪽 | 오른쪽 |
⚠️ "화면 코드는 난독화(읽기 어렵게 압축)되니까 괜찮다"는 틀린 말입니다. 브라우저에 도착한 것은 전부 사용자 손에 있습니다.
앞으로 다시 만납니다 — 14차시에서 비밀값을 정식으로 정의하고 "두지 않을 곳"을 정리합니다. 16차시 결제에서는 공개 키(결제창 띄우기)와 비밀 키(승인)가 실제로 나뉘어 쓰이는 모습을 봅니다.
4. CORS — 브라우저가 지키는 규칙
4.1 정의
출처 (Origin) — 주소의 앞부분(https://classon.kr처럼 방식 + 도메인 + 포트)입니다. 화면이 localhost:3000, 서버가 localhost:4000이면 포트가 달라서 다른 출처 입니다.
CORS (교차 출처 리소스 공유) — [브라우저 보안 규칙]인데, [ 다른 출처의 서버 응답을 화면 코드가 읽으려면 그 서버가 "이 출처는 허락한다"는 표시를 응답에 담아야 한다 ]는 점에서 서버가 요청자를 확인하는 인증 · 인가와 구별된다.
4.2 경계 — 무엇을 막고 무엇을 막지 않는가
| CORS | 인증 · 인가 (10차시) | |
|---|---|---|
| 누가 검사하나 | 브라우저 | 서버 |
| 막는 것 | 남의 사이트 화면이 사용자 브라우저를 통해 우리 서버 응답을 읽는 것 | 권한 없는 요청 자체 |
curl · 서버끼리 호출 | ❌ 막지 않음 | ⭕ 막음 |
⚠️ CORS는 서버를 지키는 장치가 아닙니다. CORS 설정이 아무리 엄격해도 curl로 직접 부르는 요청은 그대로 서버에 도착합니다. 서버의 보호는 여전히 10차시의 인증 · 인가가 합니다.
| 증상 | 대응 |
|---|---|
브라우저 콘솔에 빨간 CORS 에러, 그런데 curl로는 잘 됨 | 서버의 허용 출처 목록 에 화면 주소를 추가 |
AI가 "CORS 에러를 고쳤습니다" → 허용 목록이 *(전부 허용) | ⚠️ 피합니다. 화면 주소를 명시 |
| 로컬에선 되는데 배포 후 CORS 에러 | 0절 축 3. 허용 출처를 환경변수 로 두고 환경마다 값을 바꿈 (15차시) |
💡 비유는 마지막에 — CORS는 건물 입구가 아니라 방문객 휴대폰에 깔린 규칙 입니다. 휴대폰(브라우저)이 "이 건물이 허락한 앱에만 내용을 보여 준다"고 지킬 뿐, 휴대폰 없이 걸어 들어오는 사람은 막지 못합니다.
5. 연동 시 주의점
| 문제 | 증상 | 대응 |
|---|---|---|
| 환경별 주소 | 로컬에선 되는데 배포하면 안 됨 | 서버 주소를 공개 환경변수로 (VITE_API_URL 등) |
| 중복 요청 | 결제 버튼 두 번 클릭 → 예약 두 개 | 요청 중 버튼 비활성화 + 서버 측 방지 (16차시) — 화면만으로는 부족 |
| 낙관적 표시 | 응답 전에 "예약 완료" → 실제론 실패 | 0절 축 4. 예약 · 결제처럼 중요한 동작은 서버 응답 후 표시 |
| 모양 불일치 | 화면에 undefined | 공유 타입(2.2), 명세 대조 |
| 비밀값 노출 | 비밀 키가 화면 코드에 | 3절. 공개 환경변수에는 공개 키만 |
6. 클래스온에 적용하면
프론트엔드 에이전트 정의의 핵심 줄입니다.
## 맥락
- 시안: design/*.html (디자인 검증 역할을 통과한 것만)
- 규칙: docs/design.md
- 약속: docs/api.md, packages/shared의 타입
## 하지 않는 일
- 금액 · 남은 자리 · 권한 · 취소 가능 여부를 화면에서 계산하거나 판단하지 않는다
- 서버 코드와 API 명세를 수정하지 않는다
- 비밀 키를 화면 코드나 공개 환경변수에 넣지 않는다
## 완료 조건
- 각 화면에서 로딩 / 빈 / 에러 상태가 동작한다
- 로컬 서버와 실제로 연동해 예약 흐름을 끝까지 눌러 봤다 (가짜 데이터 없음)
🔍 읽는 포인트 : "하지 않는 일"의 첫 줄이 0절 축 1, 셋째 줄이 축 2입니다. 이 두 줄이 이 차시 전체입니다.
7. 실습 — 맡기고 검수하기
지시
예약 흐름(목록 → 상세 → 예약하기)을 로컬 서버와 연동해서 완성해줘. 6절의 에이전트 정의를 따른다.
끝나면 아래를 보고해줘.
1. [화면 | 부르는 API | 로딩/빈/에러 처리 여부] 표
2. 화면 코드 전체에서 금액·인원·자리 계산을 하는 곳 [파일 | 줄 | 하는 일] — 없으면 "없음"
3. 공개 환경변수 목록 전체 [이름 | 값의 종류]. 값 자체는 쓰지 말고 종류만.
4. 서버의 CORS 허용 출처 설정값
가짜(하드코딩) 데이터가 남아 있는지 검색한 명령과 출력 원문도 붙여줘.
받을 증거
- 화면별 연동 표, 계산 위치 목록( "없음"이어야 정상 ), 공개 환경변수 이름 목록, CORS 허용 출처 값
- 가짜 데이터 검색 명령과 출력 원문
내가 직접 확인할 것
브라우저 개발자 도구의 Network 탭 하나만 익혀 두면, 코드를 읽지 않고도 화면과 서버 사이에서 실제로 무엇이 오가는지 볼 수 있습니다. 열기: F12(Mac은 Cmd+Option+I) → Network 탭 → 화면에서 "예약하기" → 목록에 생긴 요청을 클릭.
| 볼 것 | 이걸로 아는 것 / 왜 내가 보는가 |
|---|---|
| Network 탭 요청의 Payload (화면이 보낸 것 )에 금액이 있는가 | 있으면 화면이 판정하려는 신호. 서버가 무시하는지 10차시 시험으로 재확인 |
| Response (서버가 돌려준 것 )의 숫자와 내용 | 연동이 실제로 됐는지. 빨간 줄은 실패한 요청 — 캡처해서 AI에게 주면 원인 찾기가 빨라짐 |
보고받은 공개 환경변수 이름에 SECRET · PRIVATE · PASSWORD · DATABASE_URL이 붙은 것이 있는가 | 코드 없이 잡는 비밀값 노출 (3.1) |
CORS 허용 출처가 *인가 | 4.2의 ⚠️ |
| 결제 · 예약 버튼을 빠르게 두 번 누른다 → 내 예약 목록에 몇 건? | 중복 요청 방지 (5절, 16차시) |
| 와이파이를 끄고 버튼을 누른다 | 에러 상태 화면이 나오는가, 아니면 멈추는가 (2.3) |
| 예약 직후 브라우저 뒤로 가기 · 새로고침 | 같은 예약이 다시 만들어지지 않는가 |
부록 A. 스스로 확인하기
| # | 질문 | 답이 있는 곳 |
|---|---|---|
| 1 | "화면의 검사는 편의용"이라는 말을 예로 설명하면? | 1.1 |
| 2 | 같은 규칙이 화면과 서버 두 군데 있으면 무슨 일이 생기는가? | 1.2 |
| 3 | API 호출을 한 곳에 모으고 모양을 공유하면 각각 무엇이 좋은가? | 2.1, 2.2 |
| 4 | 네 가지 화면 상태와, 빠졌을 때의 증상은? | 2.3 |
| 5 | 공개 환경변수를 유와 종차로 정의하면? 대표 접두사는? | 3.1 |
| 6 | 공개 키와 비밀 키는 노출되면 각각 어떻게 되는가? | 3.2 |
| 7 | CORS는 누가 검사하고, curl 요청을 막는가? | 4.1, 4.2 |
| 8 | CORS 에러를 *로 고치면 왜 안 되는가? | 4.2 |
| 9 | 낙관적 표시가 예약 · 결제에서 위험한 이유는? | 0, 5 |
부록 B. 자주 하는 실수
| 실수 | 바로잡기 |
|---|---|
| 화면에서 금액을 계산해 서버로 보낸다 | ❌ 화면은 서버가 준 금액을 표시만 (1.1) |
| 공개 환경변수에 비밀 키를 넣는다 | ❌ 사용자 브라우저로 그대로 전달됩니다 (3.1) |
| "CORS를 설정했으니 서버는 안전하다" | ❌ CORS는 브라우저 규칙. curl은 막지 않습니다 (4.2) |
CORS 에러를 * 전체 허용으로 해결 | ⚠️ 화면 주소를 명시 (4.2) |
| 가짜 데이터를 박아 두고 잊는다 | ⚠️ 연동 후 검색해서 제거 확인 (1.3) |
에러를 alert("에러 발생") 하나로 처리 | ⭕ 에러 코드별 안내 (2.3) |
| 버튼 비활성화로 중복 결제를 막았다고 끝냄 | ⚠️ 서버 측 방지가 함께 있어야 합니다 (5, 16차시) |
부록 C. NotebookLM 복습 프롬프트
이 11차시 노트만 근거로 5문제를 내줘. 비전공자 기준으로.
"화면의 검사는 편의용", "공개 환경변수와 비밀값", "CORS가 막는 것과 막지 않는 것"은 반드시 포함해줘.
답은 내가 답한 다음에 채점하고, 틀린 이유를 노트의 절 번호를 근거로 설명해줘.
LangTailor