HTML 강좌 / 여러 페이지로 된 작은 사이트 만들기

작은 다중 페이지 사이트를 만들면 문서 구조, 의미 요소, 링크, 양식을 한 번에 연결할 수 있습니다. 이 실습에서는 홈·소개·문의 세 페이지를 같은 폴더에 만들고 메뉴로 오가며 확인합니다. HTML 양식은 값을 전송할 수 있지만, 이 예제에는 문의를 수신·저장하는 서버 기능이 없으므로 입력과 GET 값 확인까지만 구현합니다.

파일 구성과 준비물

새 폴더를 하나 만들고 아래 세 파일을 같은 위치에 저장하세요. 파일명과 링크의 href가 정확히 일치해야 합니다. 상대경로를 파일 위치에서 계산하는 방법은 링크(link)에서 먼저 비교할 수 있습니다.

small-site/
  index.html
  about.html
  contact.html

각 파일은 UTF-8 인코딩의 일반 텍스트로 저장합니다. 우선 이미지 없이 완성한 뒤, 사용할 권리가 있는 이미지를 준비해 img와 대체 텍스트를 추가해도 됩니다.

텍스트 편집기에서 파일을 저장할 때 index.html.txt처럼 확장자가 하나 더 붙지 않았는지 확인하세요. 같은 폴더에 세 파일을 두면 href="about.html" 같은 상대경로로 서로 연결할 수 있습니다. 다른 폴더로 옮긴다면 링크도 함께 수정해야 합니다.

홈 페이지 만들기

index.html에는 사이트의 주제와 각 페이지로 가는 메뉴를 넣습니다. 세 파일에서 메뉴의 링크 주소를 동일하게 유지합니다. header·nav·main의 역할은 시맨틱 요소로 페이지 영역 나누기의 구조 예제를 적용합니다.

<!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>홈 | 작은 정원</title>
  <meta name="description" content="작은 정원 실습 사이트의 홈입니다. 소개와 문의 입력 연습으로 이동할 수 있습니다.">
  <style>
    body { margin: 1rem; font-family: sans-serif; line-height: 1.6; }
    nav a { display: inline-block; margin: 0 .5rem .5rem 0; }
    nav a[aria-current="page"] { font-weight: bold; }
    textarea, input { max-width: 100%; box-sizing: border-box; font: inherit; }
    textarea { width: 24rem; }
    :focus-visible { outline: 3px solid #005fcc; outline-offset: 3px; }
  </style>
</head>
<body>
  <header>
    <nav aria-label="주요 메뉴">
      <a href="index.html" aria-current="page">홈</a>
      <a href="about.html">소개</a>
      <a href="contact.html">문의</a>
    </nav>
  </header>
  <main>
    <h1>작은 정원</h1>
    <p>식물 가꾸기를 시작하는 사람을 위한 작은 안내입니다.</p>
  </main>
  <footer><p>작은 정원 실습</p></footer>
</body>
</html>

브라우저에서 index.html을 열면 상단 메뉴와 ‘작은 정원’ 제목이 보입니다. 아직 나머지 파일을 만들지 않았다면 소개·문의 링크가 열리지 않는 것이 정상입니다. 홈 파일을 먼저 완성하고 같은 폴더에서 다음 두 파일을 만든 뒤 다시 확인하세요.

소개와 문의 페이지 만들기

about.html에는 다음 전체 문서를 저장합니다. 홈과 같은 메뉴 주소를 유지하되 제목·설명·현재 메뉴 항목을 소개 페이지에 맞게 작성했습니다. GET 값 확인과 실제 수신 기능의 차이는 form의 구조와 GET·POST 제출 방식의 요청 방식 설명과 같습니다.

<!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>소개 | 작은 정원</title>
  <meta name="description" content="처음 식물을 키우는 사람을 위한 작은 정원 실습 사이트를 소개합니다.">
  <style>
    body { margin: 1rem; font-family: sans-serif; line-height: 1.6; }
    nav a { display: inline-block; margin: 0 .5rem .5rem 0; }
    nav a[aria-current="page"] { font-weight: bold; }
    textarea, input { max-width: 100%; box-sizing: border-box; font: inherit; }
    textarea { width: 24rem; }
    :focus-visible { outline: 3px solid #005fcc; outline-offset: 3px; }
  </style>
</head>
<body>
  <header>
    <nav aria-label="주요 메뉴">
      <a href="index.html">홈</a>
      <a href="about.html" aria-current="page">소개</a>
      <a href="contact.html">문의</a>
    </nav>
  </header>
  <main>
    <h1>소개</h1>
    <p>작은 정원은 처음 식물을 키우는 사람을 위한 연습 사이트입니다.</p>
  </main>
  <footer><p>작은 정원 실습</p></footer>
</body>
</html>

contact.html에는 다음 전체 문서를 저장합니다. 입력값은 현재 문서로 GET 제출하는 실습이며 실제 문의를 발송하지 않습니다.

<!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>문의 | 작은 정원</title>
  <meta name="description" content="실제 문의 발송 없이 이름과 테스트 메시지의 입력·검증을 연습합니다.">
  <style>
    body { margin: 1rem; font-family: sans-serif; line-height: 1.6; }
    nav a { display: inline-block; margin: 0 .5rem .5rem 0; }
    nav a[aria-current="page"] { font-weight: bold; }
    textarea, input { max-width: 100%; box-sizing: border-box; font: inherit; }
    textarea { width: 24rem; }
    :focus-visible { outline: 3px solid #005fcc; outline-offset: 3px; }
  </style>
</head>
<body>
  <header>
    <nav aria-label="주요 메뉴">
      <a href="index.html">홈</a>
      <a href="about.html">소개</a>
      <a href="contact.html" aria-current="page">문의</a>
    </nav>
  </header>
  <main>
    <h1>문의 입력 연습</h1>
    <p>이 예제는 문의를 발송하지 않습니다. 테스트 문구만 사용하세요.</p>
    <form method="get">
      <p><label for="visitor-name">이름</label>
        <input id="visitor-name" name="name" required></p>
      <p><label for="message">메시지</label>
        <textarea id="message" name="message" rows="4" required></textarea></p>
      <button type="submit">입력값 확인</button>
    </form>
  </main>
  <footer><p>작은 정원 실습</p></footer>
</body>
</html>

세 문서는 각각 완성된 파일이므로 기존 파일에 새 html이나 main을 덧붙이지 말고 전체 내용으로 저장합니다. 공통 메뉴 주소는 같지만 aria-current="page"는 각 문서의 현재 항목에만 지정합니다.

전체 파일을 저장한 뒤 브라우저 탭의 제목과 본문의 h1을 비교하세요. 페이지마다 주제는 달라지고 공통 메뉴 주소는 같아야 합니다.

문의 양식의 ‘입력값 확인’은 실제 발송 버튼이 아닙니다. 브라우저가 필수 입력을 확인한 뒤 현재 문서 주소에 값을 붙이는 연습용입니다. 로컬 파일에서의 URL 표시는 브라우저마다 다를 수 있고, 주소창에 메시지가 보일 수 있으므로 테스트 문구만 입력하세요. 실제 문의를 받으려면 서버 처리, 개인정보 보호, 스팸 방지와 완료 안내까지 별도로 설계해야 합니다.

완성 페이지 화면에 보이는 내용 현재 메뉴 표시
index.html 작은 정원 제목과 소개 문장 홈
about.html 소개 제목과 사이트 설명 소개
contact.html 문의 입력 연습 제목, 이름·메시지·확인 버튼 문의

위 표는 세 파일을 완성했을 때 비교할 화면 구성입니다. 각 현재 메뉴의 글자가 굵게 보이는지 확인하되, 굵은 모양만으로 현재 위치를 표현하는 대신 코드의 aria-current도 유지합니다.

완성 기준과 확장 과제 확인하기

확인 항목 완료 기준 어긋날 때 확인할 것
메뉴 이동 세 페이지에서 다른 두 페이지로 모두 이동 파일명·폴더·각 파일의 href
현재 위치 현재 페이지 링크에만 aria-current="page" 복사한 홈 상태가 다른 페이지에 남았는지
문서 정보 페이지별 title과 description, 올바른 언어·인코딩 head와 html의 역할 구분
문의 입력 빈 필수 값은 차단되고 테스트 값의 GET 항목을 확인 name·required·입력 조건
읽기·조작 키보드·좁은 화면·확대에서 핵심 내용 이용 초점·넘침·잘못된 중첩

먼저 이 기준으로 기본 사이트를 완성한 뒤 확장을 하나씩 적용합니다. 안내 페이지에 목록을 넣거나, 사용 권한이 있는 이미지를 alt와 함께 넣거나, 선택적 보충 설명을 details로 만들 수 있습니다. 한 가지를 추가할 때마다 링크·문법·키보드 검사를 반복하면 새 문제가 생긴 지점을 찾기 쉽습니다.

공통 메뉴를 수정했다면 세 파일 모두 반영하되 aria-current는 각 페이지에서 다른 항목에 지정합니다. 파일을 서버로 올린 뒤에도 상대경로를 다시 확인하세요. 서버의 대소문자 처리나 배포 폴더가 로컬 환경과 다를 수 있습니다.

완성 결과 점검하기

  1. index.html을 열고 메뉴 세 항목이 각각 맞는 페이지로 이동하는지 확인합니다.
  2. 각 페이지의 탭 제목과 보이는 h1이 그 페이지를 설명하는지 확인합니다.
  3. Tab 키만으로 메뉴와 문의 양식을 사용할 수 있는지 확인합니다.
  4. 문의 페이지에서 빈 필드 제출과 값 입력 후 제출을 각각 시험합니다.
  5. 파일을 Nu HTML Checker에 넣어 오류를 확인하고 고친 뒤 다시 검사합니다.

링크가 열리지 않으면 파일명과 상대경로부터 확인하세요. 한 페이지의 메뉴만 수정한 경우 다른 두 파일에도 같은 변경을 반영해야 합니다. 페이지 수가 늘어날수록 공통 부분을 일관되게 관리하는 방법이 중요해집니다. 검사기의 문법 결과와 실제 동작을 나누는 기준은 HTML 오류 찾기와 문서 검사하기에서 확인합니다.

검사를 마친 뒤에는 소개 페이지에서 홈과 문의로, 문의 페이지에서 홈과 소개로도 이동해 보세요. 홈에서만 확인하면 다른 메뉴의 오타를 놓칠 수 있습니다. 양식은 빈 값 제출이 막히고 테스트 값이 GET 항목으로 구성되는지 확인합니다. 이 예제에는 문의 수신·저장 기능이 없습니다.