본문 바로가기

첫 웹 페이지 만들기

HTML 구조와 CSS 스타일, JavaScript 클릭 이벤트를 연결해 자기소개가 담긴 첫 웹 페이지를 완성합니다.

앞선 절에서 웹의 기본 원리와 핵심 언어, 그리고 개발 환경 설정까지 마쳤습니다.

이제 이 모든 지식과 도구를 활용하여 여러분의 첫 번째 웹 페이지를 직접 만들어 볼 시간입니다.

우리가 방금 설정한 개발 환경에서 HTML, CSS, 그리고 자바스크립트 코드를 직접 작성하고, 그 결과물을 웹 브라우저에서 바로 확인하게 됩니다.

이 실습에서는 HTML, CSS, JavaScript 파일을 연결해 브라우저에 표시되는 첫 페이지를 만듭니다.

이번 장에서는 여러분의 이름과 간단한 소개가 담긴 개인 웹 페이지를 만들어 볼 것입니다.

이를 통해 HTML로 구조를 만들고, CSS로 스타일을 적용하며, 자바스크립트로 간단한 상호작용을 추가하는 과정을 실제 예시로 경험하게 됩니다.

첫 페이지 파일 연결과 브라우저 실행 순서 — 상태 추적

HTML 파서·서버·일반 script의 실제 메시지 순서와 버튼 handler 준비 여부가 중요하다. 본문 contactButton 예제에 맞춰 파일 요청→스크립트 등록→DOMContentLoaded를 분리할 가치가 있다.

본문 끝 스크립트가 앞의 버튼에 동작을 연결합니다

FIRST PAGE INTEGRATION

본문 끝 스크립트가 앞의 버튼에 동작을 연결합니다

HTML 파싱 중 CSS·JS를 요청하고, 교재의 일반 script가 실행된 뒤 파싱 완료와 DOMContentLoaded에 도달합니다.

교재 세 파일의 연결과 실행HTML 파싱 중 CSS와 body 끝 일반 스크립트를 요청한다. 스크립트 응답을 받아 실행 준비가 되면 앞서 파싱된 버튼에 핸들러를 등록하고, 파싱을 마쳐 DOMContentLoaded가 발생한다. CSS 응답과 대기 시간은 생략했다.문서·HTML 파서DOM을 구성로컬 HTTP 서버HTML · CSS · JSJavaScript일반 script 실행my_profile.html 응답profile_style.css 요청profile_script.js 요청스크립트 응답 · 실행 준비*contactButton에 click 핸들러 등록문서 파싱 완료DOMContentLoaded
세 파일 요청과 본문 끝 스크립트의 모바일 시퀀스왼쪽 문서 파서, 가운데 로컬 HTTP 서버, 오른쪽 일반 JavaScript의 세 수명선을 따라 위에서 아래로 읽는다. 서버에서 문서로 HTML 응답, 문서에서 서버로 CSS 요청과 JS 요청, 서버에서 JS로 스크립트 응답, JS에서 문서로 contactButton click 핸들러 등록이 이어지고 문서 파싱을 마친다. CSS 응답과 대기는 생략했다. 데스크톱과 같은 참여자와 다섯 메시지다.HTML 응답CSS 요청JS 요청JS 응답*click 등록문서파서서버HTTPJS일반문서 파싱 완료DOMContentLoaded

본문 끝 script · 대상 요소를 먼저 구성

<button id="contactButton">
  문의하기
</button>
<script
  src="profile_script.js">
</script>

그림은 교재의 async·defer 없는 일반 script를 기준으로 합니다. 클릭 핸들러는 문의 기능 대신 alert를 보여줍니다.

* 응답 수신과 실행은 구분

일반 script는 먼저 발견된 blocking stylesheet를 기다릴 수 있습니다. CSS 응답은 그림에서 생략했습니다. 모바일의 HTML·CSS·JS는 각각 my_profile.html·profile_style.css·profile_script.js를 가리킵니다. 요청 간격·수명선 길이는 실제 소요 시간이 아닙니다.

DOMContentLoaded는 문서 파싱과 관련 스크립트 실행 완료를 알리는 시점입니다. 이미지 등 모든 자원 로드와는 구분합니다. Network에서 실제 세 파일 요청, Console에서 오류·load 메시지, 버튼 조작에서 alert를 확인합니다.

이 예의 조건

  • 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
  • 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.
관계도의 보충 설명
원도표의 전체 관계·조건·설명
구분내용
원도표 설명그림은 교재의 async·defer 없는 일반 script를 기준으로 합니다. 클릭 핸들러는 문의 기능 대신 alert를 보여 줍니다.
전체 상태와 변화 근거
장면현재 관찰값설명변화 이유
HTML 파싱 중문서: 아직 파싱 중 / 버튼: 핸들러 등록 전파서가 첫 문서를 읽으며 필요한 파일을 요청한다문서와 script는 별도 입력이다
스크립트 실행스크립트: 현재 실행 / 버튼: 이벤트 handler 등록해당 스크립트가 DOM 대상을 찾아 handler를 연결한다실제 DOM에 handler가 연결되어 후속 클릭을 받을 수 있다
파싱 완료를 알린다문서: 파싱 완료 / 이미지: 모두 완료라는 뜻은 아님DOMContentLoaded는 문서 파싱 및 해당 대기 스크립트 뒤의 사건이다DOMContentLoaded와 전체 자원 load는 서로 다른 완료 조건이다

첫 페이지 파일 연결과 브라우저 실행 순서 — 상태 추적

HTML 파서·서버·일반 script의 실제 메시지 순서와 버튼 handler 준비 여부가 중요하다. 본문 contactButton 예제에 맞춰 파일 요청→스크립트 등록→DOMContentLoaded를 분리할 가치가 있다.

본문 끝 스크립트가 앞의 버튼에 동작을 연결합니다

FIRST PAGE INTEGRATION

본문 끝 스크립트가 앞의 버튼에 동작을 연결합니다

HTML 파싱 중 CSS·JS를 요청하고, 교재의 일반 script가 실행된 뒤 파싱 완료와 DOMContentLoaded에 도달합니다.

교재 세 파일의 연결과 실행HTML 파싱 중 CSS와 body 끝 일반 스크립트를 요청한다. 스크립트 응답을 받아 실행 준비가 되면 앞서 파싱된 버튼에 핸들러를 등록하고, 파싱을 마쳐 DOMContentLoaded가 발생한다. CSS 응답과 대기 시간은 생략했다.문서·HTML 파서DOM을 구성로컬 HTTP 서버HTML · CSS · JSJavaScript일반 script 실행my_profile.html 응답profile_style.css 요청profile_script.js 요청스크립트 응답 · 실행 준비*contactButton에 click 핸들러 등록문서 파싱 완료DOMContentLoaded
세 파일 요청과 본문 끝 스크립트의 모바일 시퀀스왼쪽 문서 파서, 가운데 로컬 HTTP 서버, 오른쪽 일반 JavaScript의 세 수명선을 따라 위에서 아래로 읽는다. 서버에서 문서로 HTML 응답, 문서에서 서버로 CSS 요청과 JS 요청, 서버에서 JS로 스크립트 응답, JS에서 문서로 contactButton click 핸들러 등록이 이어지고 문서 파싱을 마친다. CSS 응답과 대기는 생략했다. 데스크톱과 같은 참여자와 다섯 메시지다.HTML 응답CSS 요청JS 요청JS 응답*click 등록문서파서서버HTTPJS일반문서 파싱 완료DOMContentLoaded

본문 끝 script · 대상 요소를 먼저 구성

<button id="contactButton">
  문의하기
</button>
<script
  src="profile_script.js">
</script>

그림은 교재의 async·defer 없는 일반 script를 기준으로 합니다. 클릭 핸들러는 문의 기능 대신 alert를 보여줍니다.

* 응답 수신과 실행은 구분

일반 script는 먼저 발견된 blocking stylesheet를 기다릴 수 있습니다. CSS 응답은 그림에서 생략했습니다. 모바일의 HTML·CSS·JS는 각각 my_profile.html·profile_style.css·profile_script.js를 가리킵니다. 요청 간격·수명선 길이는 실제 소요 시간이 아닙니다.

DOMContentLoaded는 문서 파싱과 관련 스크립트 실행 완료를 알리는 시점입니다. 이미지 등 모든 자원 로드와는 구분합니다. Network에서 실제 세 파일 요청, Console에서 오류·load 메시지, 버튼 조작에서 alert를 확인합니다.

이 예의 조건

  • 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
  • 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.

관계도의 보충 설명

원도표의 전체 관계·조건·설명
구분내용
원도표 설명그림은 교재의 async·defer 없는 일반 script를 기준으로 합니다. 클릭 핸들러는 문의 기능 대신 alert를 보여 줍니다.
전체 상태와 변화 근거
장면현재 관찰값설명변화 이유
HTML 파싱 중문서: 아직 파싱 중 / 버튼: 핸들러 등록 전파서가 첫 문서를 읽으며 필요한 파일을 요청한다문서와 script는 별도 입력이다
스크립트 실행스크립트: 현재 실행 / 버튼: 이벤트 handler 등록해당 스크립트가 DOM 대상을 찾아 handler를 연결한다실제 DOM에 handler가 연결되어 후속 클릭을 받을 수 있다
파싱 완료를 알린다문서: 파싱 완료 / 이미지: 모두 완료라는 뜻은 아님DOMContentLoaded는 문서 파싱 및 해당 대기 스크립트 뒤의 사건이다DOMContentLoaded와 전체 자원 load는 서로 다른 완료 조건이다

HTML: 웹 페이지의 뼈대 세우기

가장 먼저 HTML을 사용하여 웹 페이지의 기본 구조를 만듭니다.

나의 소개라는 제목과 함께 여러분의 이름, 간단한 자기소개 문구, 그리고 몇 가지 정보를 담을 공간을 마련할 것입니다.

새 파일 준비
  • 개발 환경 설정 절에서 만든 web-dev-practice 폴더를 VS Code에서 엽니다.
  • 폴더 안에 새로운 파일 my_profile.html을 생성합니다. (기존 index.html은 그대로 두고, 이번 실습을 위한 새 파일을 만듭니다.)
HTML 기본 구조 작성
  • my_profile.html 파일에 다음 코드를 입력합니다. (VS Code에서 html:5를 입력한 후 Tab 키를 누르면 기본 구조가 자동 완성됩니다.)
  • [여러분의 이름]은 예제의 자리 표시자입니다. title·h1·푸터에서 자신의 이름으로 바꾸세요. title은 브라우저 탭에, h1은 페이지 안에 보이므로 두 위치를 각각 확인합니다.
my_profile.html
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>나의 첫 프로필 페이지 - [여러분의 이름]</title>
    </head>
<body>
    <header>
        <h1>안녕하세요, [여러분의 이름]입니다!</h1>
        <p>웹 개발을 시작하는 초보 개발자입니다.</p>
    </header>

    <section>
        <h2>저를 소개합니다</h2>
        <p>
            저는 새로운 기술을 배우는 것을 즐기며, 특히 웹 개발에 깊은 흥미를 느끼고 있습니다.
            이 책과 함께 여러분과 함께 성장하고 싶습니다.
        </p>
        <ul>
            <li><strong>취미:</strong> 독서, 산책</li>
            <li><strong>좋아하는 것:</strong> 맛있는 커피, 새로운 지식</li>
            <li><strong>목표:</strong> 나만의 멋진 웹 서비스 만들기</li>
        </ul>
    </section>

    <footer>
        <p>&copy; 2025 [여러분의 이름]. All rights reserved.</p>
        <button id="contactButton">문의하기</button>
    </footer>

    </body>
</html>
코드 설명
  • <!DOCTYPE html>: 이 문서가 HTML5 표준을 따른다는 것을 웹 브라우저에 알려줍니다.
  • <html lang="ko">: 문서의 시작을 알리며, 문서의 기본 언어가 한국어임을 명시합니다. 이는 검색 엔진이나 스크린 리더 등에 유용합니다.
  • <head>: 웹 페이지 자체에 대한 정보(메타데이터)를 담는 부분입니다. 사용자에게 직접 보이지는 않지만, 웹 페이지의 설정과 관련된 중요한 내용이 들어갑니다.
    • <meta charset="UTF-8">: 문서의 문자 인코딩을 UTF-8로 설정합니다. 한글이 깨지지 않도록 하는 매우 중요한 설정입니다.
    • <meta name="viewport" ...>: 웹 페이지가 다양한 기기(모바일, 태블릿 등)에서 올바르게 표시되도록 뷰포트 설정을 정의합니다. 반응형 웹을 위한 필수 설정입니다.
    • <title>: 웹 브라우저 탭에 표시될 웹 페이지의 제목을 설정합니다.
  • <body>: 웹 브라우저 화면에 실제로 표시될 모든 콘텐츠가 들어가는 부분입니다.
    • <header>: 페이지의 머리글 영역을 나타냅니다. 주로 제목이나 로고, 내비게이션 바 등이 들어갑니다.
    • <h1>, <p>: 제목과 단락을 나타내는 태그입니다.
    • <section>: 문서의 독립적인 섹션을 나타냅니다.
    • <h2>: 두 번째 수준의 제목을 나타냅니다.
    • <ul>, <li>: 순서 없는 목록(Unordered List)과 목록의 각 항목(List Item)을 나타냅니다.
    • <strong>: 텍스트를 강조하는 태그입니다.
    • <footer>: 페이지의 바닥글 영역을 나타냅니다. 주로 저작권 정보나 연락처 등이 들어갑니다.
    • <button>: 클릭할 수 있는 버튼을 만듭니다. id="contactButton"은 나중에 자바스크립트에서 이 버튼을 쉽게 찾기 위한 고유한 이름입니다.
미리 보기
  • my_profile.html 파일을 저장합니다.
  • Live Server를 설치했다면 my_profile.html을 우클릭해 Open with Live Server로 여세요. Go Live가 다른 시작 파일을 열면 주소 끝이 my_profile.html인지 확인합니다. 확장 프로그램 없이 따라갈 때는 이 파일을 더블 클릭해 열고, 수정·저장 뒤 직접 새로고침해도 됩니다.
  • 웹 브라우저에 여러분이 작성한 HTML 내용이 표시될 것입니다. 아직 스타일이 적용되지 않아 다소 밋밋하게 보일 수 있습니다. 마치 건물의 뼈대만 완성된 상태입니다.

CSS: 웹 페이지에 스타일 입히기

이제 HTML로 만든 뼈대에 CSS를 사용하여 옷을 입히고 아름답게 꾸며줄 차례입니다.

색상, 글꼴, 여백 등을 조절하여 웹 페이지를 시각적으로 매력 있게 만들어 봅시다.

CSS 파일 생성
  • web-dev-practice 폴더 안에 새로운 파일 profile_style.css를 생성합니다. (개발 환경 설정 절에서 만든 style.css는 그대로 두고 새 파일을 만듭니다.)
HTML 파일에 CSS 연결
  • my_profile.html 파일로 돌아와 </head> 태그 바로 앞에 다음 코드를 추가합니다.
my_profile.html
<link rel="stylesheet" href="profile_style.css">
  • 이 코드는 my_profile.html 파일이 profile_style.css 파일의 스타일 규칙을 적용받도록 연결해 줍니다.
CSS 코드 작성
  • profile_style.css 파일에 다음 CSS 코드를 작성합니다.
profile_style.css
/* 전체 페이지에 적용될 기본 스타일 */
body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background-color: #f0f2f5; /* 연한 회색 배경 */
    color: #333;
    line-height: 1.6;
    margin: 0;
    padding: 0;
    display: flex; /* Flexbox를 사용하여 콘텐츠를 중앙에 배치 */
    flex-direction: column;
    align-items: center;
    min-height: 100vh; /* 최소 화면 높이 */
}

/* 헤더 스타일 */
header {
    background-color: #007bff; /* 파란색 배경 */
    color: white;
    padding: 40px 20px;
    width: 100%;
    box-sizing: border-box; /* padding을 지정한 너비 안에 포함 */
    text-align: center;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

header h1 {
    margin-bottom: 10px;
    font-size: 2.8em;
}

header p {
    font-size: 1.2em;
    opacity: 0.9;
}

/* 섹션 스타일 */
section {
    background-color: white;
    margin: 30px 20px;
    padding: 30px;
    border-radius: 10px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
    max-width: 800px;
    width: calc(100% - 40px); /* 좌우 margin 20px씩 제외 */
    box-sizing: border-box; /* padding을 지정한 너비 안에 포함 */
    text-align: left;
}

section h2 {
    color: #0056b3;
    margin-bottom: 20px;
    font-size: 2em;
    border-bottom: 2px solid #eee;
    padding-bottom: 10px;
}

section ul {
    list-style: none; /* 목록 마커 제거 */
    padding: 0;
    margin-top: 20px;
}

section ul li {
    background-color: #e9f7fe; /* 연한 하늘색 배경 */
    margin-bottom: 10px;
    padding: 12px 15px;
    border-left: 5px solid #007bff;
    border-radius: 5px;
    font-size: 1.1em;
}

section ul li strong {
    color: #004d66;
}

/* 푸터 스타일 */
footer {
    background-color: #333;
    color: white;
    text-align: center;
    padding: 20px;
    width: 100%;
    box-sizing: border-box; /* padding을 지정한 너비 안에 포함 */
    margin-top: auto; /* 하단에 고정 */
}

footer p {
    margin-bottom: 15px;
    font-size: 0.9em;
    opacity: 0.8;
}

/* 버튼 스타일 */
#contactButton { /* ID 선택자 사용 */
    background-color: #28a745; /* 녹색 버튼 */
    color: white;
    padding: 12px 25px;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    font-size: 1.1em;
    transition: background-color 0.3s ease, transform 0.2s ease; /* 호버 애니메이션 */
}

#contactButton:hover {
    background-color: #218838; /* 진한 녹색 */
    transform: translateY(-2px); /* 살짝 위로 */
}
변화 확인
  • profile_style.css와 my_profile.html 파일을 저장합니다. (Live Server를 사용 중이라면 자동으로 새로고침됩니다.)
  • 웹 브라우저에서 여러분의 프로필 페이지가 확연히 달라진 것을 확인할 수 있을 것입니다. 배경색이 바뀌고, 글꼴이 정돈되며, 각 영역이 깔끔하게 구분되어 보일 것입니다. 브라우저에서 배경색, 글꼴, 영역 구분이 적용되었는지 확인합니다.
  • 스타일이 적용되지 않으면 href의 파일 이름과 profile_style.css가 같은 폴더에 있는지 확인합니다.

JavaScript: 웹 페이지에 상호작용 추가하기

마지막으로 자바스크립트를 사용하여 웹 페이지에 동적인 기능을 추가해 봅시다.

문의하기 버튼을 클릭하면 경고창이 나타나도록 만들어 보겠습니다.

자바스크립트 파일 생성
  • web-dev-practice 폴더 안에 새로운 파일 profile_script.js를 생성합니다. (개발 환경 설정 절에서 만든 script.js는 그대로 두고 새 파일을 만듭니다.)
HTML 파일에 자바스크립트 연결
  • my_profile.html 파일로 돌아와 </body> 태그 바로 앞(푸터 아래)에 다음 코드를 추가합니다.
<script src="profile_script.js"></script>
  • 브라우저는 HTML을 읽으며 요소 구조인 DOM을 만듭니다. 이 예제는 script를 버튼 뒤, </body> 바로 앞에 두므로 스크립트가 실행될 때 contactButton 요소를 이미 찾을 수 있습니다. 스크립트를 버튼보다 앞에 옮기면 아직 없는 요소를 찾다가 null을 얻을 수 있으므로 지금은 이 위치를 유지하세요.
  • 그림의 DOMContentLoaded는 문서 파싱이 끝난 시점을 가리킵니다. 예제의 window.onload는 이미지 같은 추가 자원의 로드까지 기다리는 별도 시점입니다. 앞서 발견한 스타일시트가 일반 스크립트의 실행을 기다리게 할 수 있다는 세부 조건은 MDN의 DOMContentLoaded 설명에서 확인할 수 있습니다.
자바스크립트 코드 작성
  • profile_script.js 파일에 다음 자바스크립트 코드를 작성합니다.
profile_script.js
// '문의하기' 버튼 요소를 ID로 선택합니다.
const contactButton = document.getElementById('contactButton');

// 버튼에 클릭 이벤트 리스너를 추가합니다.
contactButton.addEventListener('click', function() {
    // 버튼이 클릭될 때 경고창(alert)을 띄웁니다.
    alert('아직 문의 기능은 개발 중입니다. 잠시만 기다려주세요! 😊');
});

// 페이지가 로드되면 실행되는 메시지 (선택 사항)
window.onload = function() {
    console.log('프로필 페이지가 성공적으로 로드되었습니다!');
    // 개발자 도구의 콘솔 탭에서 이 메시지를 확인할 수 있습니다.
};
변화 확인
  • profile_script.js와 my_profile.html 파일을 저장합니다.
  • 웹 브라우저에서 여러분의 프로필 페이지를 확인합니다.
  • 하단의 문의하기 버튼을 클릭해 보세요. 아직 문의 기능은 개발 중입니다...라는 경고창이 나타날 것입니다.
  • 경고창이 나타나지 않으면 src의 파일 이름과 profile_script.js의 저장 여부를 먼저 확인합니다.

이 예시는 자바스크립트가 사용자 입력에 반응해 화면을 바꾸는 기본 흐름을 보여줍니다.


정리 및 다음 단계 안내

이번 실습에서는 첫 웹 페이지를 만들며 다음 흐름을 확인했습니다.

  • HTML: 웹 페이지의 뼈대와 내용을 구조화하는 방법
  • CSS: 구조화된 내용에 시각적인 스타일을 적용하는 방법
  • 자바스크립트: 사용자와 상호작용하는 동적인 기능을 추가하는 방법
  • 개발 환경: VS Code와 Live Server를 활용하여 코드를 작성하고 실시간으로 확인하는 방법

지금 만든 페이지는 기초적인 수준이지만, HTML 구조, CSS 스타일, JavaScript 동작을 한 파일 흐름 안에서 확인했다는 점이 중요합니다.

다음 장부터는 이 세 요소를 더 세분화해 다룹니다.

자기소개 페이지를 스스로 한 번 바꿔 보기

예제 파일을 보관한 상태에서 취미 목록에 항목을 하나 추가하고, 헤더 배경을 자신이 고른 색으로 바꾸고, 문의 안내창의 문구를 바꿔 보세요. 수정 전에 세 변경을 각각 어느 파일에서 할지 적습니다. 저장·새로고침 뒤에는 목록 항목 수, 헤더 색, 버튼 클릭 시 안내 문구를 하나씩 확인하세요.

확인 기준과 연결 실패 구분

목록 항목은 my_profile.html의 ul 안 li, 헤더 배경은 profile_style.css의 header 규칙, 안내 문구는 profile_script.js의 클릭 함수 안 alert에서 바꿉니다. 취미만 추가되면 HTML은 반영되었지만 CSS·JS 저장이나 연결은 각각 더 확인해야 합니다. 문의 버튼은 Tab으로 이동해 Enter 또는 Space로도 눌러 보고 같은 안내창이 뜨는지 확인합니다. 이 예제는 안내창만 보여 주며 서버에 문의를 보내지는 않습니다.