HTML

이미지와 하이퍼링크 & 폼 관련 태그

1. 이미지와 하이퍼링크

웹 페이지의 기본적인 구성 요소인 이미지와 다른 페이지로 연결되는 하이퍼링크를 만드는 방법을 학습한다.

<img> 태그: 이미지 삽입

웹 페이지에 이미지를 표시할 때 사용하는 태그이다. <img> 태그는 닫는 태그가 없는 단일 태그(empty tag)이다.

주요 속성

  • src (source): 표시할 이미지의 경로를 지정하는 필수 속성이다. 웹 URL이나 파일 경로를 값으로 가진다.

  • alt (alternative text): 이미지를 불러올 수 없을 때 나타나는 대체 텍스트이며 HTML 표준상 필수 속성이다. 시각장애인을 위한 스크린 리더가 이 텍스트를 읽어주고, 검색 엔진도 이미지를 색인할 때 활용한다. 장식용 이미지라면 alt="" (빈 문자열)로 명시한다.

  • width: 이미지의 가로 너비를 지정한다.

  • height: 이미지의 세로 높이를 지정한다.

  • title: 이미지에 마우스 커서를 올렸을 때 나타나는 툴팁 텍스트를 지정한다.

예시 코드

<!DOCTYPE html>
<html lang="ko">
<head>
    <title>이미지 태그</title>
</head>
<body>
    <h1>페이지 로고</h1>
    <img src="logo.png" alt="회사 로고 이미지" width="200" title="우리 회사 로고입니다.">
</body>
</html>

정상적으로 불러온 이미지:

이미지를 못 불러왔을 때 — alt 텍스트가 대신 표시됨:

이미지 경로(src) 지정 방법

종류

예시

설명

상대 경로

src="images/logo.png"

현재 HTML 파일을 기준으로 한 경로. 같은 폴더면 파일명만, 하위 폴더면 폴더/파일, 상위 폴더는 ../

절대 경로(웹)

src="https://.../logo.png"

인터넷 주소 전체를 지정. 외부 이미지에 사용

width만 지정하면 비율은?

width 또는 height 중 하나만 지정하면 나머지는 원본 비율에 맞춰 자동 조정된다. 둘 다 지정하면 비율이 무시되어 이미지가 찌그러질 수 있다.

<a> 태그: 하이퍼링크 생성

텍스트나 이미지를 클릭했을 때 지정된 다른 페이지로 이동시키는 하이퍼링크를 생성한다.

주요 속성

  • href (hypertext reference): 이동할 페이지의 URL 주소를 지정하는 필수 속성이다.

  • target: 링크를 열 창을 지정한다.

    • _self: 현재 창에서 페이지를 연다. (기본값)

    • _blank: 새로운 탭이나 창에서 페이지를 연다.

  • rel: 링크 관계를 지정한다. target="_blank"와 함께 rel="noopener noreferrer"를 쓰는 것이 권장됨 (보안/성능 이슈 방지).

  • title: 링크에 마우스 커서를 올렸을 때 나타나는 툴팁 텍스트를 지정한다.

rel=\"noopener noreferrer\"는 왜 필요한가?

target="_blank"로 열린 페이지는 자바스크립트로 원래 페이지를 조작할 수 있어서 (Reverse Tabnabbing 공격) noopener로 차단한다. noreferrer는 대상 사이트에 출처 정보를 보내지 않는다.

예시 코드

<!DOCTYPE html>
<html lang="ko">
<head>
    <title>하이퍼링크 태그</title>
</head>
<body>
    <p>
        자세한 정보를 원하시면
        <a href="https://www.google.com" target="_blank" rel="noopener noreferrer" title="새 탭에서 구글 열기">구글</a>을 방문하세요.
    </p>

    <a href="https://www.google.com">
        <img src="google_logo.png" alt="구글 로고" width="150">
    </a>
</body>
</html>

자세한 정보를 원하시면 구글을 방문하세요.

다양한 링크 대상 (href 값)

용도

href 값 예시

동작

외부 페이지

https://google.com

다른 웹사이트로 이동

내 사이트 다른 페이지

about.html

같은 사이트의 다른 파일로 이동 (상대 경로)

페이지 안 특정 위치

#section2

같은 페이지의 id="section2" 위치로 점프 (앵커)

이메일 보내기

mailto:hi@example.com

메일 작성 프로그램 실행

전화 걸기

tel:01012345678

모바일에서 전화 앱 실행


2. 폼(Form) 관련 태그

사용자로부터 데이터를 입력받기 위해 사용하는 요소들의 집합이다. 로그인, 회원가입, 검색창 등 웹에서의 상호작용에 필수적이다.

  • <form>: 모든 폼 요소를 감싸는 컨테이너이다. action 속성으로 데이터를 보낼 서버 주소를, method 속성으로 데이터 전송 방식을 지정한다. 파일 업로드를 포함하는 경우 enctype="multipart/form-data"를 추가로 지정해야 한다.

  • <label>: input, select 등 폼 컨트롤 요소의 제목을 나타낸다. for 속성을 사용해 id가 일치하는 폼 컨트롤과 연결하면 사용성과 접근성이 향상된다. <label>로 폼 컨트롤을 직접 감싸는 방식도 있다 (이 경우 for와 id가 필요 없음).

  • <input>: 가장 많이 사용되는 폼 태그로, 사용자의 입력을 받는다. type 속성에 따라 다양한 형태로 변경된다.

  • <select>: 여러 옵션 중 하나를 선택할 수 있는 드롭다운 목록을 생성한다. 내부에 <option> 태그를 사용하여 각 항목을 만든다. 옵션을 그룹화하려면 <optgroup label="그룹명">으로 감싼다.

  • <textarea>: 여러 줄의 텍스트를 입력받을 때 사용한다. rows와 cols 속성으로 초기 크기를 지정할 수 있다.

  • <button>: 클릭 가능한 버튼을 생성한다. type 속성을 "submit"으로 지정하면 form 데이터를 서버로 전송하는 역할을 한다.

  • <fieldset> 과 <legend>: 관련된 폼 요소들(예: 라디오 그룹)을 의미적으로 묶을 때 사용한다. <legend>는 <fieldset>의 제목 역할.

<label> 사용 패턴

<label>은 입력 칸이 무엇을 위한 것인지 알려주는 제목표다. 두 가지 작성 방식이 있고 효과는 동일하다.

방식 1 — for + id로 연결

<label for="username">아이디:</label>
<input type="text" id="username" name="username">

방식 2 — <label>로 입력 칸을 감싸기 (for/id 불필요)

<label>
    아이디: <input type="text" name="username">
</label>

방식 1: for + id 연결

아이디:

방식 2: 감싸기

아이디:

→ "아이디:" 글자를 클릭해 보세요. 입력 칸으로 포커스가 이동합니다.

label을 꼭 써야 하는 이유

글자를 클릭해도 입력 칸이 포커스되어 클릭 영역이 넓어지고, 스크린 리더는 입력 칸을 읽을 때 label을 함께 읽어 준다. 체크박스·라디오에서는 효과가 특히 크다.


3. <input> 태그의 주요 유형 (type)

<input>은 type 속성 값에 따라 형태와 동작이 완전히 달라진다. 자주 쓰는 type을 하나씩 살펴본다. 어떤 type을 쓰든 name 속성으로 식별 이름을 정하는 것이 공통 규칙이다.

대표적인 type들이 실제로 어떻게 보이는지 한눈에 비교해 보자.

text password email number date color range file checkbox 동의 radio A B

type="text"

가장 기본적인 형태로, 한 줄의 텍스트를 입력받는다.

주요 속성

  • name: 폼 데이터 제출 시 서버에서 값을 식별하기 위한 이름

  • placeholder: 입력할 값에 대한 힌트를 흐리게 표시

  • value: 미리 입력되어 있을 기본값

  • maxlength: 최대 글자 수

  • minlength: 최소 글자 수

  • readonly: 값을 읽기 전용으로 설정 (값은 서버로 전송됨)

  • disabled: 필드를 비활성화. 데이터가 서버로 전송되지 않음 (readonly와의 핵심 차이)

  • required: 필수 입력 필드

<form action="/search">
    <label for="search-query">검색어:</label>
    <input
        type="text"
        id="search-query"
        name="query"
        placeholder="검색어를 입력하세요"
        maxlength="20"
        required
    >
    <button type="submit">검색</button>
</form>

type="password"

비밀번호처럼 노출되면 안 되는 값을 입력받을 때 쓴다. 입력한 글자가 화면에 점(●)이나 별표(*)로 가려진다. 단, HTTPS가 아니면 네트워크에서는 평문으로 전송된다 — 화면 가림은 어깨너머 시선을 막는 용도일 뿐이라는 점에 주의.

<form action="/login" method="post">
    <label for="login-pw">비밀번호:</label>
    <input
        type="password"
        id="login-pw"
        name="user_pw"
        minlength="8"
        autocomplete="current-password"
        required
    >
    <button type="submit">로그인</button>
</form>

비밀번호:

→ 입력 칸 안의 글자가 ●로 가려진다.

autocomplete로 자동완성 정확도 높이기

autocomplete 속성은 브라우저의 자동완성·비밀번호 매니저에게 "이 칸이 어떤 종류의 값을 받는지" 알려주는 힌트다. 기본값은 on(브라우저가 추측해서 자동완성)이며, 비밀번호 칸에서는 다음 두 값을 구분해서 쓰는 것이 정석이다.

값

사용처

동작

current-password

로그인 폼

저장된 비밀번호를 자동 입력해 줌

new-password

회원가입 / 비번 변경 폼

저장된 값으로 채우지 않고, 강력한 비밀번호 생성을 제안

아이디·이메일 칸도 autocomplete="username", autocomplete="email"처럼 명시하면 정확도가 올라간다. 참고로 비밀번호 칸에 autocomplete="off"를 적어도 요즘 브라우저는 대부분 무시한다 (비밀번호 매니저 사용을 막지 않는 게 더 안전하다는 판단).

type="email"

이메일 형식인지 브라우저가 1차 검증한다. @가 없거나 형식이 잘못되면 제출 시 거부된다. 모바일에서는 @이 있는 키패드가 자동으로 뜬다.

<form>
    <label for="user-email">이메일:</label>
    <input
        type="email"
        id="user-email"
        name="email"
        placeholder="id@example.com"
        required
    >
    <button type="submit">전송</button>
</form>

이메일: 전송

→ "abc"처럼 형식이 틀린 값으로 전송을 시도하면 브라우저가 막아 준다.

브라우저 검증은 보조 수단일 뿐

브라우저의 형식 검증은 편의 기능이다. JavaScript를 끄거나 직접 HTTP 요청을 보내면 우회할 수 있으므로, 실제 검증은 반드시 서버에서 다시 해야 한다.

type="number"

숫자 값만 입력받기 위한 필드이다.

주요 속성

  • min: 최솟값

  • max: 최댓값

  • step: 값이 변경되는 간격. step="any"를 쓰면 소수점 제한 없이 입력 가능.

<form>
    <label for="product-quantity">상품 수량 (1~10개):</label>
    <input type="number" id="product-quantity" name="quantity"
           value="1" min="1" max="10" step="1">
</form>

<form>
    <label for="item-score">평점 (0.0 ~ 5.0):</label>
    <input type="number" id="item-score" name="score"
           placeholder="1.5" min="0" max="5" step="0.5">
</form>

type="date"

날짜를 고르는 달력 위젯이 뜬다. 값의 형식은 YYYY-MM-DD로 통일된다.

주요 속성

  • min, max: 선택 가능한 날짜 범위

  • value: 기본 날짜

<form>
    <label for="reservation-date">예약 날짜:</label>
    <input
        type="date"
        id="reservation-date"
        name="reservation_date"
        min="2026-01-01"
        max="2026-12-31"
        required
    >
</form>

예약 날짜:

→ 입력 칸을 클릭하면 달력이 열린다. 비슷한 type으로 time(시간), datetime-local(날짜+시간)이 있다.

type="range"

값을 슬라이더로 조절하게 한다. 정확한 숫자보다 "낮음/중간/높음" 같은 상대적 강도를 입력받기에 좋다.

주요 속성: min (기본 0), max (기본 100), step (기본 1), value (기본 위치)

<form>
    <label for="volume">볼륨 (0~100):</label>
    <input type="range" id="volume" name="volume"
           min="0" max="100" step="5" value="40">
</form>

볼륨 (0~100):

→ 슬라이더를 움직여 값을 조절. 현재 값을 화면에 함께 보여주려면 JavaScript가 필요하다.

type="file"

사용자의 컴퓨터에서 파일을 선택할 수 있는 칸을 만든다. 파일 업로드를 포함하면 <form>에 반드시 enctype="multipart/form-data"를 추가해야 한다.

주요 속성

  • accept: 허용할 파일 종류 제한. 확장자(.pdf,.docx) 또는 MIME 타입(image/*, image/png)

  • multiple: 여러 파일 동시 선택 허용

<form action="/upload" method="post" enctype="multipart/form-data">
    <label for="profile-photo">프로필 사진 (이미지만):</label>
    <input type="file" id="profile-photo" name="photo" accept="image/*">

    <label for="docs">제출 서류 (여러 개):</label>
    <input type="file" id="docs" name="docs" accept=".pdf,.docx" multiple>

    <button type="submit">업로드</button>
</form>

프로필 사진 (이미지만):
제출 서류 (여러 개):
업로드

type="radio"

여러 개의 선택지 중에서 단 하나만 선택할 수 있는 라디오 버튼을 생성한다.

중요 특징

  • 동일한 name 속성: 하나의 그룹으로 묶고 싶은 라디오 버튼들은 반드시 동일한 name 값을 공유해야 한다. 이것이 단일 선택을 가능하게 하는 핵심이다.

  • value: 각 항목이 선택되었을 때 서버로 전송될 실제 값. 사용자에게는 보이지 않는다.

  • checked: 페이지 로딩 시 기본적으로 선택되어 있을 항목 지정

<form>
    <p><b>배송 속도를 선택해주세요:</b></p>
    <div>
        <input type="radio" id="delivery-standard" name="delivery_speed" value="standard" checked>
        <label for="delivery-standard">일반 배송 (3-5일)</label>
    </div>
    <div>
        <input type="radio" id="delivery-express" name="delivery_speed" value="express">
        <label for="delivery-express">빠른 배송 (1-2일)</label>
    </div>
    <div>
        <input type="radio" id="delivery-rocket" name="delivery_speed" value="rocket">
        <label for="delivery-rocket">새벽 배송</label>
    </div>
</form>

배송 속도를 선택해주세요:

일반 배송 (3-5일)

빠른 배송 (1-2일)

새벽 배송

type="checkbox"

여러 개의 선택지 중에서 원하는 모든 항목을 선택(다중 선택)하거나, 단일 항목에 대한 동의 여부를 체크할 때 사용한다.

중요 특징

  • 독립적인 name 또는 동일한 name:

    • 동의 여부처럼 단일 항목일 경우, 고유한 name을 사용한다.

    • 관심사 선택처럼 여러 항목을 하나의 그룹으로 묶을 경우, 동일한 name을 사용하여 서버에서 배열 형태로 데이터를 처리하기 쉽게 만든다.

  • value: 해당 체크박스가 선택되었을 때 서버로 전송될 값

  • checked: 기본 선택 상태

1. 다중 선택 예시 (관심사 선택)

<form>
    <p><b>관심 있는 프로그래밍 언어를 모두 선택해주세요:</b></p>
    <div>
        <input type="checkbox" id="lang-html" name="interests" value="HTML">
        <label for="lang-html">HTML</label>
    </div>
    <div>
        <input type="checkbox" id="lang-css" name="interests" value="CSS" checked>
        <label for="lang-css">CSS</label>
    </div>
    <div>
        <input type="checkbox" id="lang-js" name="interests" value="JavaScript" checked>
        <label for="lang-js">JavaScript</label>
    </div>
</form>

관심 있는 프로그래밍 언어를 모두 선택해주세요:

HTML

CSS

JavaScript

2. 단일 선택 예시 (약관 동의)

<form>
    <div>
        <input type="checkbox" id="agree" name="agree_policy" value="yes" required>
        <label for="agree">[필수] 이용약관에 동의합니다.</label>
    </div>
</form>

type="hidden"

화면에는 보이지 않지만 폼이 제출될 때 함께 서버로 전송되는 값을 담는다. 사용자가 직접 입력하지 않고 페이지가 미리 알고 있는 값을 함께 보낼 때 쓴다.

대표적인 활용

  • 게시글 수정 폼에서 어떤 글을 수정하는지 알려주는 글 ID

  • 다중 페이지 폼에서 진행 중인 단계 번호

  • 보안 토큰(CSRF 토큰)

<form action="/posts/update" method="post">
    <input type="hidden" name="post_id" value="42">
    <input type="hidden" name="csrf_token" value="a7f5...e91c">

    <label for="post-title">제목:</label>
    <input type="text" id="post-title" name="title" value="기존 글 제목">

    <button type="submit">수정 완료</button>
</form>

위 폼을 제출하면 서버에는 post_id=42&csrf_token=a7f5...&title=... 처럼 사용자가 본 적 없는 값(post_id, csrf_token)도 함께 전달된다.

민감한 정보는 hidden에 넣지 말 것

화면에는 안 보여도 HTML 소스 보기로 누구나 볼 수 있다. 비밀번호·관리자 권한 정보 같은 민감 값은 절대 hidden에 담으면 안 된다.


4. 그 외 폼 요소

<input> 외의 입력 요소들을 살펴본다.

<textarea> — 여러 줄 텍스트

자기소개·후기·게시글 본문처럼 여러 줄의 텍스트를 받을 때 쓴다. <input>과 달리 여는 태그와 닫는 태그 사이에 기본값을 넣는다.

주요 속성

  • rows: 보이는 줄 수

  • cols: 보이는 글자 너비

  • placeholder, required, maxlength: <input type="text">와 동일하게 사용

<form>
    <label for="review">상품 후기:</label><br>
    <textarea
        id="review"
        name="review"
        rows="5"
        cols="40"
        maxlength="300"
        placeholder="구입하신 상품에 대한 솔직한 후기를 남겨 주세요."
        required
    ></textarea>
</form>

상품 후기:

→ 우하단 모서리를 드래그하면 사용자가 크기를 조절할 수 있다.

<button> 타입과 기본값

<button>은 type 속성에 따라 동작이 완전히 달라진다.

type

동작

submit (생략 시 기본값)

폼 데이터를 action으로 전송

reset

같은 폼 안의 모든 입력을 초깃값으로 복원

button

아무 동작도 없음 (JavaScript로 직접 처리할 때)

<form action="/contact" method="post">
    <input type="text" name="name" placeholder="이름">
    <input type="email" name="email" placeholder="이메일">

    <button type="submit">전송하기</button>
    <button type="reset">입력값 초기화</button>
    <button type="button" onclick="alert('도움말!')">도움말</button>
</form>

전송하기 입력값 초기화 도움말

type을 생략하면 무조건 submit

<form> 안에서 <button>의 type을 적지 않으면 기본값이 "submit"이다. 계산기·토글 버튼처럼 폼 제출과 무관한 버튼이라면 반드시 type="button"을 명시해야 의도치 않은 폼 제출(페이지 새로고침)을 막을 수 있다.

<select> — 드롭다운 목록

여러 항목 중 하나(또는 여러 개)를 고를 때 쓰는 드롭다운이다. 사용자에게 보이는 글자는 <option> 태그 안의 내용이고, 서버로 전송되는 값은 <option value="...">에 적힌 값이다. 이 둘은 다를 수 있다는 점이 핵심.

<form action="/survey" method="post">
    <label for="city">살고 있는 도시:</label>
    <select id="city" name="city">
        <option value="">-- 선택하세요 --</option>
        <optgroup label="수도권">
            <option value="seoul" selected>서울</option>
            <option value="incheon">인천</option>
            <option value="suwon">수원</option>
        </optgroup>
        <optgroup label="영남권">
            <option value="busan">부산</option>
            <option value="daegu">대구</option>
        </optgroup>
        <optgroup label="호남권">
            <option value="gwangju">광주</option>
            <option value="jeonju">전주</option>
        </optgroup>
    </select>
</form>

살고 있는 도시: -- 선택하세요 -- 서울 인천 수원 부산 대구 광주 전주

주요 속성과 활용 패턴

  • name은 <select> 태그에, value는 각 <option> 태그에 붙인다. 위 예시에서 서울을 선택해 제출하면 서버에는 city=seoul로 전달된다.

  • <option>에 selected를 붙이면 페이지 로딩 시 기본 선택값이 된다. (위 예시: 서울)

  • <optgroup label="...">로 옵션을 묶으면 그룹 제목과 함께 시각적으로 구분된다. (그룹 자체는 선택 불가)

  • 첫 항목을 <option value="">-- 선택하세요 --</option>처럼 두면 사용자가 직접 선택하도록 유도할 수 있다. required와 함께 쓰면 빈 값 제출을 막을 수 있다.

여러 개를 한꺼번에 고르게 하려면 multiple

<select multiple> 로 쓰면 드롭다운이 목록 박스로 바뀌고, Ctrl(맥에서는 Cmd)을 누른 채 클릭해 여러 항목을 선택할 수 있다. 이 경우 서버에는 같은 name으로 여러 값이 전송되므로, 백엔드에서는 배열로 받아 처리해야 한다.

<fieldset> / <legend> — 폼 묶기

회원가입 폼에서 "기본 정보"와 "관심 분야"처럼 성격이 다른 항목들을 시각·의미적으로 구분해 주는 용도로 자주 쓴다. 스크린 리더는 <legend>를 그 그룹 안 입력칸의 제목으로 함께 읽어 준다.

<form action="/register" method="post">
    <fieldset>
        <legend>기본 정보</legend>
        <label for="user-name">이름:</label>
        <input type="text" id="user-name" name="user_name">
        <label for="user-email">이메일:</label>
        <input type="email" id="user-email" name="user_email">
    </fieldset>

    <fieldset>
        <legend>관심 분야 (복수 선택)</legend>
        <label><input type="checkbox" name="interests" value="frontend"> 프론트엔드</label>
        <label><input type="checkbox" name="interests" value="backend"> 백엔드</label>
        <label><input type="checkbox" name="interests" value="design"> 디자인</label>
    </fieldset>

    <button type="submit">가입하기</button>
</form>

기본 정보

이름: 이메일:

관심 분야 (복수 선택) 프론트엔드
백엔드
디자인

가입하기


5. 공통 폼 속성

<input>을 비롯한 거의 모든 폼 컨트롤에 공통으로 적용되는 중요한 속성들을 정리한다.

  • name: 폼 데이터를 서버로 전송할 때 각 데이터를 식별하는 이름으로 사용된다. 서버 프로그래밍에서 매우 중요하다.

  • value: 미리 입력되어 있을 기본값. (체크박스·라디오·<option>의 value는 "선택 시 전송되는 값"이라는 의미로 약간 다르다)

  • placeholder: 입력 필드에 어떤 값을 입력해야 하는지 알려주는 안내 문구. 입력을 시작하면 사라진다. 주의: placeholder는 <label>을 대체할 수 없다 (포커스 시 사라져 접근성이 떨어짐).

  • required: 해당 필드가 필수 입력 항목임을 지정한다.

  • readonly vs disabled:

    • readonly: 값을 읽기 전용으로 만든다. 값은 서버로 전송됨.

    • disabled: 필드를 비활성화한다. 값이 서버로 전송되지 않음.

  • autofocus: 페이지 로딩 시 자동으로 포커스를 가져간다. (한 페이지에 하나만)

  • maxlength / minlength: 입력 글자 수 제한

  • min / max / step: type="number", range, date 등의 값 범위·간격 제한


6. 폼 데이터 전송과 HTTP

폼에 입력한 값이 서버로 전달되는 과정은 다음과 같다. 각 입력칸의 name이 데이터의 "이름표", 입력값이 "내용"이 되어 함께 전송된다.

flowchart LR
    A["📝 사용자 입력<br/>아이디·비밀번호 등"] --> B["📦 form 수집<br/>name=값 쌍으로 묶음"]
    B --> C{"method?"}
    C -->|GET| D["🔗 URL에 붙여 전송<br/>?id=hong&amp;age=20"]
    C -->|POST| E["✉️ 본문(body)에 담아 전송<br/>URL에 안 보임"]
    D --> F["🖥️ action 주소의 서버"]
    E --> F
    style A fill:#e3f2fd,stroke:#1e88e5
    style F fill:#e8f5e9,stroke:#43a047

GET vs POST (form의 method)

  • GET: 데이터를 URL 뒤에 붙여서 보냄 (예: ?id=hong&age=20). 검색, 북마크 가능한 조회용에 적합. 용량 제한 있고 민감한 데이터에는 부적절.

  • POST: 데이터를 요청 본문(body)에 실어 보냄. URL에 노출되지 않아 로그인, 회원가입, 파일 업로드 등 생성/수정용에 사용.

HTTP 요청 메시지의 구조

form이 제출될 때 브라우저가 실제로 서버에 보내는 것은 HTTP 요청 메시지라는 텍스트이다. 메시지는 항상 다음 네 부분으로 이뤄진다.

flowchart TD
    M["📨 HTTP 요청 메시지"]
    M --> L1["① 시작 줄 (Start Line)<br/>METHOD&nbsp;&nbsp;경로&nbsp;&nbsp;HTTP/버전"]
    M --> L2["② 헤더 (Headers)<br/>Host, Content-Type, ... <br/>한 줄에 하나씩"]
    M --> L3["③ 빈 줄 (CRLF)<br/>헤더와 본문을 구분"]
    M --> L4["④ 본문 (Body)<br/>실제로 보낼 데이터<br/>(GET 요청은 비어 있음)"]
    style M fill:#ede7f6,stroke:#5e35b1
    style L1 fill:#e3f2fd,stroke:#1e88e5
    style L2 fill:#fff3e0,stroke:#fb8c00
    style L3 fill:#eceff1,stroke:#607d8b
    style L4 fill:#e8f5e9,stroke:#43a047

각 부분의 역할은 다음과 같다.

부분

들어가는 내용

예시

시작 줄

메소드 + 요청 경로 + HTTP 버전

POST /register HTTP/1.1

헤더

요청에 대한 부가 정보(어디로 가는지, 본문 형식, 본문 크기 등)

Host: example.com, Content-Type: ...

빈 줄

헤더가 끝났음을 알리는 구분선 (반드시 한 줄)

(내용 없음)

본문

실제로 전송할 데이터. POST·PUT 등에서 사용

user_id=hong&user_pw=1234

메소드(METHOD)는 "무엇을 하려는지"를 나타낸다

같은 주소라도 어떤 메소드를 쓰느냐에 따라 요청의 의미가 달라진다. GET(조회), POST(생성), PUT(수정), DELETE(삭제) — HTML <form>이 직접 지원하는 건 GET과 POST 두 가지다.

<form method="get"> 으로 보낸 요청 (검색창)

GET /search?q=html&page=2 HTTP/1.1        ← ① 시작 줄: 메소드 + 경로(쿼리 포함) + 버전
Host: example.com                          ← ② 헤더 시작
User-Agent: Mozilla/5.0
Accept: text/html
                                            ← ③ 빈 줄 (헤더 끝)
                                            ← ④ 본문 없음 — 데이터는 이미 URL에 들어 있음

→ 데이터가 URL 뒤(?q=html&page=2)에 그대로 노출되므로 북마크·공유에 유리하지만 비밀번호처럼 민감한 정보에는 부적절.

<form method="post"> 으로 보낸 요청 (회원가입)

POST /register HTTP/1.1                              ← ① 시작 줄
Host: example.com                                     ← ② 헤더
Content-Type: application/x-www-form-urlencoded       ← 본문 형식
Content-Length: 27                                    ← 본문 크기(바이트)
                                                       ← ③ 빈 줄
user_id=hong&user_pw=1234                              ← ④ 본문 (form 데이터)

→ 데이터가 본문에 담겨 URL에 보이지 않고, 용량 제한도 사실상 없다. 단, HTTPS가 아니면 중간에서 들여다볼 수 있으니 보안은 HTTPS가 담당한다.

그럼 응답은?

서버가 보내는 HTTP 응답 메시지도 구조는 거의 같다. 첫 줄만 "메소드+경로" 대신 상태 코드(예: HTTP/1.1 200 OK, 404 Not Found)가 오는 점이 다르다.

모든 요소가 들어간 종합 예시

<form action="/register" method="post">
    <label for="user-id">아이디:</label>
    <input type="text" id="user-id" name="user_id">

    <label for="user-job">직업:</label>
    <select id="user-job" name="job">
        <option value="student">학생</option>
        <option value="developer">개발자</option>
    </select>

    <label for="intro">자기소개:</label>
    <textarea id="intro" name="intro" rows="4" cols="50"></textarea>

    <button type="submit">가입하기</button>
</form>

아이디: 직업: 학생 개발자 자기소개:
가입하기


7. 종합 실습: 회원가입 페이지 만들기

지금까지 학습한 <img>, <a>, 그리고 다양한 폼 태그와 속성을 활용하여 간단한 회원가입 페이지를 만들어 본다.

요구사항

  1. 페이지 상단에 사이트 로고(이미지)를 표시한다.

  2. 아이디, 비밀번호, 이메일, 이름 필드를 만든다.

  3. 관심 분야를 체크박스로 여러 개 선택할 수 있도록 한다. (HTML, CSS, JavaScript)

  4. 가입 경로를 라디오 버튼으로 하나만 선택할 수 있도록 한다. (블로그, 추천, 기타)

  5. 개인정보처리방침에 동의하는 링크와 체크박스를 만든다.

  6. placeholder, required 속성을 적절히 활용한다.

  7. '가입하기' 버튼을 만든다.

완성 코드

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>IT 커뮤니티 회원가입</title>
    <style>
        body { font-family: sans-serif; line-height: 1.6; }
        .form-group { margin-bottom: 15px; }
        label { display: block; margin-bottom: 5px; font-weight: bold; }
        input[type="text"], input[type="password"], input[type="email"] {
            width: 300px;
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        button {
            padding: 10px 20px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
    </style>
</head>
<body>

    <img src="logo.png" alt="IT 커뮤니티 로고" width="180">
    <h1>회원가입</h1>

    <form action="/register-user" method="post">
        <div class="form-group">
            <label for="userId">아이디</label>
            <input type="text" id="userId" name="user_id" placeholder="6자 이상 영문 또는 숫자" required>
        </div>
        <div class="form-group">
            <label for="userPw">비밀번호</label>
            <input type="password" id="userPw" name="user_pw" placeholder="8자 이상, 특수문자 포함" required>
        </div>
        <div class="form-group">
            <label for="userName">이름</label>
            <input type="text" id="userName" name="user_name" required>
        </div>
        <div class="form-group">
            <label for="userEmail">이메일</label>
            <input type="email" id="userEmail" name="user_email" placeholder="id@example.com" required>
        </div>
        <hr>
        <div class="form-group">
            <p><b>관심 분야 (다중 선택 가능)</b></p>
            <label><input type="checkbox" name="interests" value="html"> HTML</label>
            <label><input type="checkbox" name="interests" value="css"> CSS</label>
            <label><input type="checkbox" name="interests" value="javascript"> JavaScript</label>
        </div>
        <div class="form-group">
            <p><b>가입 경로 (하나만 선택)</b></p>
            <label><input type="radio" name="join_path" value="blog" checked> 블로그</label>
            <label><input type="radio" name="join_path" value="recommend"> 지인 추천</label>
            <label><input type="radio" name="join_path" value="etc"> 기타</label>
        </div>
        <hr>
        <div class="form-group">
            <label>
                <input type="checkbox" name="agree_terms" required>
                <a href="/privacy-policy.html" target="_blank" rel="noopener noreferrer">개인정보처리방침</a>에 동의합니다.
            </label>
        </div>
        <button type="submit">가입하기</button>
    </form>

</body>
</html>

마지막 수정 2026년 10월 22일