서버가 실제로 접수한 결과 표시하기

JavaScriptBeginner
지금 연습하기

소개

Still & Spoke의 레이블, 조건부 좌석 메모와 반응형 배치는 준비되어 있습니다. 현재 처리기는 신청을 보내지 않고도 데모 확인을 표시할 수 있습니다. 이를 실제 비동기 요청과 스튜디오가 실제로 접수한 결과를 나타내는 피드백으로 바꿉니다. 페이지가 맞아 보여도 준비된 서비스가 확인을 돌려주기 전에는 신청이 아닙니다.

HTML 폼, 기본 CSS 배치, JavaScript 객체·함수·DOM 선택·이벤트가 필요합니다. JSON, async/await, fetch, HTTP 결과, 대기 상태와 복구 가능한 피드백을 배웁니다. 폼 의미와 조건부 제약은 준비되어 있으며 이전 실습은 유용하지만 그 VM 파일은 필요 없습니다. /home/labex/project/stillspoke/request.js와 feedback.js만 바꿉니다. 다른 프런트엔드 파일, API, 정원과 방문자는 보존합니다. 모든 신원은 합성이며 메일·결제는 없습니다. 60분은 저자 추정치이지 초보자 실측이 아닙니다.

전체 파일 교체는 Workspace, 사이트는 Studio입니다. Study form은 독립된 완성 예시이며 신청이 작업 성과가 되지 않습니다. Studio records는 접수 기록을 읽습니다. Practice room은 접수 전에 한 요청을 거부할 수 있는 별도 훈련 페이지입니다. 각 실습은 독립 데이터로 시작합니다. 준비된 백엔드, 운영 배포, 임의 요청 멱등성, 완전한 접근성 준수는 가르치지 않습니다.

실제 요청 하나를 보내고 응답 기다리기

데모 처리기를 진짜 POST로 바꾸고 대기 중 중복 실행을 막아 서버의 진짜 확인을 표시합니다.

Studio에서 Study form을 선택합니다. Tab으로 Your name에 가서 Example Guest, Tab으로 Email에 가서 sample@example.test, Tab으로 Session에 가서 C를 입력하고 Clay, slowly / 10:00을 읽습니다. 선택하지 않은 지원과 선택 사항인 메모를 Tab으로 지나 Reserve my place에 갑니다. Enter를 한 번 누릅니다. 실제 Confirmation SS-… 뒤 Studio records에서 이름·메일·시간을 비교합니다. Back to signup, Work form으로 돌아옵니다. 예시는 별도 데이터베이스입니다. 작업에는 기존 방문자만 있으며 데모 문구는 접수를 증명하지 않습니다.

예시는 별도 제목과 읽기 쉬운 시간을 가진 예상 결과입니다. 발급 번호는 달라집니다.

실제 신청 후 독립 예시 폼

준비된 API는 로컬 환경 일부입니다. 공개 필드와 규칙은 다음과 같습니다.

JSON 필드 의미
name 앞뒤 공백 제거 후 두 글자 이상.
email @example.test로 끝나는 합성 주소. 브라우저가 기본 메일 문법을 먼저 검사.
session 나열된 안정 ID: 10:00은 clay-morning, 14:00은 clay-afternoon. 오후는 만석.
support 좌석 지원 선택 여부의 불리언.
note 지원 시 다섯 글자 이상 문자열, 아니면 선택 사항.

POST는 api/bookings로 새 신청 제안을 보냅니다. 현재 작업/예시 페이지 아래 상대 URL이므로 각 업무 공간은 분리됩니다. API는 객체·문자열·숫자·불리언을 나타내는 텍스트 형식 JSON을 돌려줍니다. 프로퍼티 이름과 JSON 문자열은 큰따옴표를 씁니다. JSON.stringify(data)는 JavaScript 객체를 JSON 텍스트로, response.json()은 응답 텍스트를 다시 객체로 해석합니다. Content-Type: application/json 헤더는 본문의 종류를 서비스에 알립니다. 프런트엔드 문구나 비활성화 버튼은 서비스의 정원 규칙을 구현하지 않습니다.

HTTP 상태는 결과를 구분합니다.

상태 응답과 확정 사실
201 booking에는 접수 필드, 읽기 쉬운 sessionTitle, 서버 생성 id가 있습니다. 접수되었고 번호는 SS-로 시작합니다. 만들지 마세요.
422 errors가 필드명을 설명에 연결합니다. 접수 없음, 표시한 필드 수정.
409 선택 회차 만석. 접수 없음, 가능한 회차 선택.
503 통제된 훈련 정지가 접수 전에 거부. 접수 없음, 안전한 재시도 가능.

fetch는 나중의 결과인 Promise를 반환합니다. async 이벤트 처리기는 await로 Promise가 성공하거나 실패할 때까지 자신만 기다리며 브라우저는 계속 쓸 수 있습니다. HTTP 응답 뒤 JSON 해석을 기다립니다. 422·503 같은 HTTP 거부도 fetch가 정상 완료하며 자동으로 catch에 가지 않습니다. 상태를 명시적으로 확인합니다. 네트워크·해석 실패는 접수 여부를 확정하지 않은 채 catch에 올 수 있습니다. 메시지는 접수 여부가 불명확함과 Studio records 복구 경로를 안내해야 합니다.

submit 이벤트는 버튼과 필드 내 Enter 모두를 처리합니다. event.preventDefault()는 기본 페이지 이동 제출을 막아 JSON을 보냅니다. 기본 검사에 실패한 필드는 처리기 전에 제출을 막습니다. form.elements는 위치나 HTML id가 아닌 name으로 찾습니다. 텍스트는 .value, 체크는 .checked입니다.

기다리기 전에 pending을 설정합니다. 보호 조건은 대기 중 다른 제출이 오면 즉시 반환하며 .disabled로 기본 버튼을 보이게 비활성화합니다. 같은 페이지의 진행 중 요청 동안 반복 실행만 막으며 다른 페이지나 임의 동시 클라이언트까지 보장하지 않습니다. try는 요청, catch는 실패 시 접수 불명 안내, finally는 모든 결과에서 버튼 복원과 수동적 알림 제거를 합니다. 폼을 초기화하지 말고 값을 보존합니다.

clearErrors, showMessage, showResponse는 feedback.js 함수입니다. 준비된 페이지는 HTML 해석 후 defer로 scenario.js, validity.js, feedback.js, request.js를 이 순서로 읽습니다. 첫 파일은 데이터, 둘째는 기본 조건 규칙, 마지막 둘은 요청과 피드백을 협력 처리합니다. 현재 피드백은 정상 201을 지원하며 다음에 필드 거부를 개선합니다.

Workspace에서 cd로 디렉터리를 고릅니다. cat > request.js는 그 파일만 바꿉니다. 따옴표 here-document 마커 JS는 단독 JS 줄까지 그대로 저장하므로 Markdown 백틱 없이 전체를 붙입니다. 프롬프트가 돌아오면 저장 완료입니다.

cd /home/labex/project/stillspoke
cat > request.js <<'JS'
const form = document.querySelector('form');
const reserveButton = form.querySelector('[type="submit"]');
const progress = document.querySelector('#progress');
let pending = false;
form.addEventListener('submit', async (event) => {
  event.preventDefault();
  if (pending) return;
  clearErrors();
  document.querySelector('#result').hidden = true;
  const data = {
    name: form.elements.name.value,
    email: form.elements.email.value,
    session: form.elements.session.value,
    support: form.elements.support.checked,
    note: form.elements.note.value
  };
  pending = true;
  reserveButton.disabled = true;
  progress.textContent = 'Sending your request...';
  try {
    const response = await fetch('api/bookings', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });
    const body = await response.json();
    showResponse(response.status, body);
  } catch (error) {
    showMessage('No confirmation received. Acceptance is unknown. Check Studio records; wait for the original attempt to be resolved before a new submission.');
  } finally {
    pending = false;
    reserveButton.disabled = false;
    progress.textContent = '';
  }
});
JS

Studio로 돌아가 작업을 새로 고치고 Morgan Vale, morgan@example.test, Clay, slowly / 10:00을 설정하며 지원은 해제합니다. Tab으로 버튼에서 Enter를 한 번 누릅니다. Sending your request... 동안 버튼은 비활성화입니다. Submission result에 Reserved for Morgan Vale, 진짜 Confirmation SS-…, 읽기 쉬운 10:00 제목을 기다립니다. 수동적 role="status"에는 초점을 주지 않습니다. 일반 결과 영역은 제목과 내용을 찾기 쉽게 초점을 받을 수 있습니다.

Studio records에서 전체 새 기록을 비교하고 Existing Visitor, Avery Reed를 남깁니다. 예전 데모 ID는 접수 기록에 없어야 합니다. HTTP 전달 성공만으로 부족하며 실제 201이어야 하고 확인은 접수된 booking에서 와야 합니다. 자체 확인: 진짜 기록이 있고 대기 중 중복 조작이 막히며 완료 후 버튼이 돌아옵니다.

서버 거부를 올바른 필드에 연결하기

다른 입력을 지우지 않고 실제 서버 검증 거부를 읽을 수 있는 필드 오류로 만듭니다.

브라우저는 잘못된 메일 문법을 거부하지만 서비스에는 연습 주소가 @example.test로 끝나야 한다는 업무 규칙도 있습니다. morgan@other.test는 문법상 유효하여 서버에 도달하고 HTTP 422를 만듭니다. 기본 필수 입력 팝업과 다른 실제 요청·거부입니다.

오류 응답 모양은 다음과 같습니다. 명령이 아니라 읽는 데이터입니다.

{
  "message": "Please correct the marked fields. Nothing was booked.",
  "errors": {
    "email": "Use an address ending in @example.test for this practice."
  }
}

errors.email은 API 키 email을 뜻합니다. 순서나 첫 오류 문단이 아닌 form.elements[name]으로 대응합니다. 대괄호 안 문자열 키는 해당 프로퍼티를 선택합니다. Object.keys(body.errors)는 프로퍼티 이름 배열이며 for (const name of ...)는 각각 순회합니다. let 변수는 재할당 가능하므로 first가 첫 표시 필드를 기억합니다.

textContent는 서버 문구를 HTML로 해석하지 않고 글로 보입니다. aria-invalid="true"는 무효 필드를 나타내며 관련 email-error가 기존 aria-describedby로 보이는 이유를 제공합니다. 상시 도움말을 색만으로 대체하지 마세요. 새 요청 전 옛 오류를 지워 수정된 필드에 오래된 설명이 남지 않게 합니다. 422는 전체 결과를 먼저 보이고 첫 무효 필드로 이동합니다. 다른 결과는 일반 결과 영역에 초점을 줄 수 있습니다. 수동적인 라이브 상태 알림은 별도 요소로 강제 초점 대상이 아닙니다.

성공 문구는 템플릿 리터럴입니다. 백틱이 글을 감싸고 ${booking.id}가 현재 프로퍼티 값을 넣습니다. 만든 문자열을 textContent가 그대로 보입니다. 번호는 응답 객체에서 오며 로컬에서 꾸미지 않습니다.

Workspace에서 아래 함수로 feedback.js를 바꿉니다.

cd /home/labex/project/stillspoke
cat > feedback.js <<'JS'
function clearErrors() {
  for (const field of document.querySelectorAll('form [name]')) {
    field.removeAttribute('aria-invalid');
    const error = document.getElementById(field.name + '-error');
    if (error) error.textContent = '';
  }
}
function showMessage(text) {
  const panel = document.querySelector('#result');
  document.querySelector('#message').textContent = text;
  panel.hidden = false;
  panel.focus();
}
function showResponse(status, body) {
  if (status === 201) {
    const booking = body.booking;
    showMessage(`Reserved for ${booking.name}. Confirmation ${booking.id}. ${booking.sessionTitle}.`);
  } else if (status === 422) {
    showMessage(body.message);
    let first;
    for (const name of Object.keys(body.errors)) {
      const field = document.querySelector('form').elements[name];
      document.getElementById(name + '-error').textContent = body.errors[name];
      field.setAttribute('aria-invalid', 'true');
      if (!first) first = field;
    }
    first.focus();
  } else {
    showMessage(body.message);
  }
}
JS

Studio를 새로 고쳐 Morgan Vale, morgan@other.test, Clay, slowly / 10:00, 지원 선택, A seat near the entrance로 한 번 보냅니다. 전체 Nothing was booked와 새 메일 오류가 보여야 합니다. Email로 초점이 가고 레이블·도움말·이름·회차·체크·메모는 남습니다. Studio records에 거부 주소 기록이 생기면 안 됩니다. 초안을 떠나지 않고 보려면 브라우저 Open Link in New Tab으로 Studio records를 다른 페이지에 열고 원래 폼에 돌아옵니다.

주소만 morgan@example.test로 고쳐 보냅니다. 옛 오류가 지워지고 진짜 확인이 나타나며 메모가 기록에 도달합니다. 다른 필드 거부도 시험합니다. 나머지가 유효할 때 이름을 정확히 두 공백으로 바꿉니다. 기본 minlength="2"는 입력 글자를 세지만 공개된 서버 규칙은 앞뒤 공백을 없애고 두 글자를 요구합니다. 실제 422는 늘 Email이 아니라 Your name을 표시하고 초점을 주며 메일·회차·메모를 보존해야 합니다. Morgan Vale를 복원합니다. 같은 합성 이름의 두 번째 접수는 허용되며 이메일 유일성 시스템이 아닙니다. 기존 방문자는 보존합니다.

@ 없이 morgan.example.test를 잘못 입력하면 브라우저가 POST 전에 거부합니다. 예상된 기본 메시지지만 422 분기는 시험하지 않습니다. 기본 문법은 유효한 morgan@other.test로 서버 규칙을 봅니다. 코드 수정 후 갱신이 없다면 Workspace의 cat feedback.js를 전체 블록과 비교해 철자를 고치고 Studio를 새로 고칩니다. JavaScript 오타 때문에 데이터 삭제나 서비스 재시작을 하지 않습니다. 자체 확인: 실제 서버 키가 해당 입력 및 읽는 오류에 연결되고 다른 입력이 남으며 수정 후 재전송됩니다.

만석과 안전한 서비스 정지에서 복구하기

만석과 접수 전 서비스 거부를 구분하고 초안을 유지하며 안전한 재시도를 마칩니다.

Studio를 새로 고쳐 Casey Brook, casey@example.test, Clay, slowly / 14:00, 지원 선택, A seat near the entrance를 설정합니다. 보내기 전에 14:00 제목을 읽습니다. 첫 C는 첫 Clay 항목이고 한 번 더 누르면 다음입니다. 키가 늘 같은 위치를 뜻한다고 가정하지 말고 실제 표시를 읽습니다.

한 번 보냅니다. 만석이라 HTTP 409를 돌려줍니다. **That session is full. Nothing was booked; choose another session.**를 읽습니다. 입력은 남고 버튼이 돌아옵니다. 자리를 만들려고 기존 14:00 방문자를 지우면 안 됩니다. 409는 잘못된 메일·형식이 필요 없는, 올바른 제안에 대한 업무 거부입니다.

회차만 Clay, slowly / 10:00으로 바꿉니다. Practice room 링크는 원래 폼을 유지한 채 다른 페이지를 엽니다. 고객 신청과 분리된 훈련 공간입니다. Pause next submission을 선택하고 **Next submission will be refused before a booking is accepted.**를 기다립니다. 연습 페이지를 닫거나 원래 Studio 폼으로 돌아옵니다. 연습 페이지 Back to signup으로 열린 초안을 바꾸지 마세요.

별도 훈련 페이지는 접수 전 거부를 약속합니다. 이 조작 중 원래 폼을 열어 둡니다.

별도 Practice room의 안전한 거부 제어

원래 폼을 한 번 보냅니다. 이번 HTTP 503은 **Nothing was booked. Service paused; please retry.**라고 명시합니다. 이름·주소·10:00·체크·메모는 남습니다. 이 요청이 정지 한 번을 소비합니다. 같은 값으로 Reserve my place에서 Enter를 한 번 더 누릅니다. 진짜 201 후 Studio records에서 Casey Brook, 메일, 10:00, 좌석 메모를 비교합니다. 기존 방문자와 이전 접수도 남깁니다.

503 계약은 좁습니다. 접수 전에 거부하므로 이 재시도가 안전합니다. 연결 끊김이나 읽을 수 없는 응답은 다릅니다. catch는 접수 불명이지 Nothing was booked가 아닙니다. 그런 경우 Studio records를 다른 페이지에 엽니다. 일치 기록이 확인을 찾는 데 도움은 되지만 같은 이름·값이 반복되면 어느 시도의 결과인지 늘 알 수 없습니다. 원래 시도가 미해결이면 다시 보내지 않습니다. 기록을 성공적으로 읽고 일치가 없어도 거부 증명이 아닙니다. 원래 요청이 아직 진행 중일 수 있습니다. 해결을 기다리거나 스튜디오 운영자(실습 강사)에게 문의합니다. 기록도 못 읽으면 서비스도 기다립니다. 여기서 안전한 재시도를 확정하는 것은 명시된 접수 전 503뿐입니다. 이 실습은 멱등성을 약속하거나 분산 결제 흐름을 가르치지 않습니다.

보통 편집 실수는 Workspace로 전환해 저장 요청 파일을 봅니다.

cd /home/labex/project/stillspoke
cat request.js

정확한 상대 URL api/bookings, JSON.stringify 철자, 두 await, finally를 확인합니다. 오타면 1단계 전체로 바꿔 Studio를 새로 고칩니다. 새 제출 전 위 경계에 따라 이전 불명 시도를 해결합니다. 철자 수정만으로 이전 요청의 거부가 확정되지 않습니다. 해결되었음을 알 때 새 합성 이름으로 해당 정상 시험을 반복합니다. 읽기 전용 검증은 대체 처리기를 설치하거나 실제 신청으로 작업을 고치지 않습니다.

자체 확인: 만석과 정지는 대기 종료 뒤 보이는 결과를 내고 초안을 보존하며 수정이나 안전한 재시도를 허용하고 가짜 번호를 표시하지 않습니다. 이해할 수 있는 동등 문구도 유효합니다. 정상 접수 기록이 있으며 준비된 전체 정원과 방문자가 그대로여야 합니다.

요약

응답을 기다리는 POST로 JSON을 보내고 HTTP 거부와 연결 불확실성을 구분했으며 대기 중 조작을 보호하고 실제 확인을 표시했습니다. 서버 오류를 필드에 연결하고 만석·정지에도 입력을 보존했으며 모든 연결 실패를 미접수라고 단정하지 않고 기록으로 복구를 안내했습니다.