파일과 브라우저 실행
90분 안팎
학습 목표
상대 경로와 개발자 도구를 사용해 문서를 엽니다.
개념
왜 실행 위치부터 확인하나요
처음 웹 작업을 맡으면 코드를 많이 쓰는 것보다 동료가 같은 화면을 열 수 있게 만드는 일이 먼저입니다. 지역 행사 사이트의 소개 문서를 바꿨는데 화면이 그대로라면 문법보다 다른 index.html을 열었을 가능성을 살핍니다. 편집기의 파일 경로와 브라우저 주소 표시줄을 비교합니다. 같은 이름의 파일이 다운로드 폴더와 작업 폴더에 각각 있으면 수정한 파일과 실행한 파일이 달라질 수 있습니다. 한 폴더를 실습의 기준점으로 정하고 그 안에서 명령을 실행합니다.
파일과 주소의 관계
index.html은 문서, assets/site.css는 표현 규칙, results.html은 검색 제출 목적지, event.html은 예시 상세 문서입니다. href에는 상대 주소나 절대 주소를 쓸 수 있습니다. 이 예제의 상대 주소는 별도의 base 요소가 없으므로 현재 문서 주소를 기준으로 해석됩니다. index.html 옆의 assets 폴더를 가리킬 때 assets/site.css라고 씁니다. asset처럼 한 글자를 빼면 실제 폴더와 일치하지 않습니다. 컴퓨터의 절대 경로를 넣으면 작성자의 장치에서는 열려도 다른 사람이 압축을 풀었을 때 깨집니다. 파일명 대소문자가 다르면 실행 환경에 따라 실패할 수 있으므로 실제 이름과 똑같이 씁니다.
주소 앞의 /는 사이트의 루트를 기준으로 합니다. 파일을 직접 여는 file 주소에서는 웹 서버 루트와 같은 의미로 기대하기 어렵습니다. 이 단계는 서버 없이 실행하므로 내부 파일에는 상대 경로를 사용합니다. #intro는 다른 파일이 아니라 현재 문서 안의 id가 intro인 요소로 이동합니다. 이동 링크에 #events를 쓰려면 대상 id가 있어야 하고 문서 안에서 유일해야 합니다. 파일 경로 문제와 문서 내부 목적지 문제를 분리하면 찾는 시간이 줄어듭니다.
문서의 최소 시작점
doctype은 현대 HTML 문서라는 선언입니다. html의 lang을 ko로 쓰면 문서 기본 언어 정보를 제공합니다. head 안의 charset은 한국어 문자를 UTF-8로 읽도록 지정하고 title은 브라우저 탭과 문서 이름에 사용됩니다. viewport는 모바일에서 레이아웃 뷰포트를 장치 너비에 맞추는 시작점입니다. 이것만으로 반응형 구성이 완성되지는 않습니다. 보이는 내용은 body에 배치합니다. title을 고쳤다고 페이지의 큰 제목이 바뀌는 것은 아니며 본문 제목은 따로 작성합니다.
개발자 도구로 원인을 좁힙니다
브라우저 메뉴에서 개발자 도구를 열고 Elements 또는 요소 탭에서 main을 찾습니다. 이 탭은 브라우저가 파싱한 DOM을 보여 주므로 파일 원문과 항상 같은 문자열은 아닙니다. 브라우저는 잘못된 중첩을 보정할 수 있습니다. Console 또는 콘솔의 오류를 읽을 때 오류 종류, 경로, 줄 번호를 먼저 봅니다. 파일 로드 실패는 JavaScript 문법 오류와 다른 문제입니다. Network 또는 네트워크에서 새로고침하고 site.css 요청의 경로를 확인합니다. file 실행에서는 브라우저별로 표시 방식이 다를 수 있어 원문 경로와 실제 파일 존재도 함께 확인합니다.
이 실습에는 JavaScript가 없으므로 스크립트 로그가 없는 것은 자연스럽습니다. 콘솔에 아무 메시지도 없다는 사실만으로 모든 파일이 정상이라고 결론 내리지는 않습니다. 스타일 경로가 틀리면 문서는 열리지만 여백과 글꼴이 달라집니다. 브라우저에서 임시로 바꾼 스타일은 원본 파일에 저장되지 않으므로 수정은 편집기에서 합니다. 다른 페이지로 이동했다 돌아온 뒤에도 같은 화면이 보이는지 확인하면 임시 수정과 파일 수정을 구분할 수 있습니다.
재현 가능한 인계
새 폴더에 압축을 풀어 index.html을 열 수 있는 것이 첫 완료 기준입니다. 설치 명령과 검사 명령은 폴더 기준으로 기록하고 개인 사용자 이름이 들어간 경로는 안내에 넣지 않습니다. ENOENT는 해당 위치에 파일이 없다는 단서입니다. npm이 package.json을 찾지 못하면 실습 폴더에서 실행했는지 확인하고 Python 실행 파일을 못 찾으면 Python 3 설치 상태를 확인합니다. 환경 오류와 HTML 계약 실패를 분리합니다.
검증 결과를 읽는 습관
실습 폴더 안에서 npm test를 실행하면 PASS와 FAIL 뒤에 검사의 목적이 표시됩니다. FAIL은 편집할 파일과 기대한 계약을 알려 주는 단서입니다. 마지막 실패 수만 보고 파일을 다시 만들지 말고 첫 FAIL을 읽어 한 항목씩 고칩니다. 검사 파일을 수정하거나 실패한 검사를 제거하면 사용자 문제는 그대로 남습니다. 수정한 문서를 저장하고 다시 실행한 뒤 브라우저를 새로고침합니다. 저장 전 화면과 저장 후 파일을 혼동하면 같은 수정이 반복됩니다.
자동 검사는 정해진 파일과 태그 속성을 읽는 계약 검사입니다. 실제 브라우저의 접근 가능한 이름 계산, 스크린 리더 발화, 포커스 표시의 시인성까지 재현하지 않습니다. PASS를 받은 뒤에도 링크를 눌러 목적지가 열리는지, 키보드로 모든 조작이 가능한지 확인합니다. 테스트가 보장하는 범위를 설명할 수 있어야 결과를 동료에게 전달할 수 있습니다. 예를 들어 레이블 속성은 맞지만 문구가 모호한 상황은 사람이 사용자의 목적을 기준으로 판단합니다.
이번 프로젝트의 행사 이름과 날짜는 교육용 예시입니다. 실제 행사 공고를 가져오거나 개인정보를 입력할 필요가 없습니다. 결과 페이지는 검색 조건을 주소로 전달받는 정적 문서이며 목록을 필터링하지 않습니다. 입력값으로 목록을 바꾸는 기능은 데이터와 이벤트를 익힌 뒤 구현합니다. 화면에 이 한계를 표시하면 사용자가 구현되지 않은 기능을 기대하지 않습니다. 이전 모듈이 없는 첫 단계이므로 제공된 파일부터 시작하고, 미션 결과 폴더 전체를 다음 모듈의 출발점으로 보관합니다.
완료 기준을 말로 설명합니다
검사 결과와 브라우저 관찰을 함께 보고 어떤 요구를 만족했는지 한 문장으로 씁니다. 예를 들어 검색어가 주소에 전달되었다는 관찰은 제출 계약의 증거이며 목록 필터의 증거는 아닙니다. 동료가 같은 폴더로 따라 할 수 있도록 파일 이름과 조작 순서를 기록합니다. 수정 전 실패 항목과 수정 후 통과 항목을 비교하면 변경 이유가 분명해집니다.
따라하기
실습 폴더 준비
starter 압축을 풀고 그 폴더를 편집기로 엽니다. 터미널도 package.json이 있는 폴더에서 시작합니다. 다음 명령은 외부 의존 패키지가 없는 잠금 파일을 사용합니다.
npm ci
npm teststarter의 FAIL 항목을 먼저 읽습니다. npm 설치나 Python 실행 실패는 환경 문제이므로 HTML 수정 전에 해결합니다.
계약에 맞게 문서 수정
index.html에서 다음 구조를 기준으로 해당 레슨의 FAIL 항목을 고칩니다. 아래는 최종 문서의 참고 코드이며 그대로 저장하면 같은 파일 구성에서 사용할 수 있습니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>우리 동네 행사 찾기</title>
<link rel="stylesheet" href="assets/site.css">
</head>
<body>
<header><p>우리 동네 행사</p><nav aria-label="주요 메뉴"><a href="#intro">소개</a> <a href="#events">행사 목록</a></nav></header>
<main>
<h1>우리 동네 행사 찾기</h1>
<section id="intro" aria-labelledby="intro-title">
<h2 id="intro-title">가까운 곳에서 함께 만나요</h2>
<p>지역과 검색어로 관심 있는 행사를 찾아보세요. 현재는 예시 행사만 제공합니다.</p>
</section>
<section aria-labelledby="search-title">
<h2 id="search-title">행사 검색</h2>
<p id="query-help">검색어를 한 글자 이상 입력하세요. 예: 책</p>
<form action="results.html" method="get">
<label for="query">검색어</label>
<input id="query" name="q" type="search" required aria-describedby="query-help">
<label for="region">지역</label>
<select id="region" name="region">
<option value="all">전체 지역</option>
<option value="seoul">서울</option>
<option value="busan">부산</option>
</select>
<button type="submit">검색</button>
</form>
</section>
<section id="events" aria-labelledby="events-title">
<h2 id="events-title">예시 행사 목록</h2>
<article><h3>동네 책 나눔</h3><p>서울 · 10월 20일 · 무료</p><a href="event.html">동네 책 나눔 상세 보기</a></article>
</section>
</main>
<footer><p>교육용 행사 정보이며 실제 모집 공고가 아닙니다.</p></footer>
</body>
</html>
파일 검사 재실행
저장한 뒤 실습 폴더에서 검사합니다. 마지막 실패 수가 0인지 확인합니다. 출력은 solution을 실행한 실제 검사 결과입니다.
npm test실행 결과
> frontend-m01-workspace@1.0.0 test > python3 check.py PASS 한국어 문서와 제목 PASS UTF-8 및 작은 화면 설정 PASS 스타일 파일 상대 경로 PASS 목록 및 상세 파일 존재 PASS 중복 id 없음 PASS 모든 내부 링크 목적지 존재 검사 6개, 실패 0개
브라우저에서 사용자 흐름 확인
index.html을 브라우저의 파일 열기로 엽니다. 소개와 목록 링크, 상세 페이지와 돌아가기를 확인합니다. 검색어에 책을 넣고 지역을 서울로 골라 Enter를 누릅니다. results.html 주소의 q와 region을 확인합니다. 다시 돌아와 빈 입력 제출과 Tab·Shift+Tab을 시험하고 KEYBOARD.md의 미확인을 실제 관찰로 바꿉니다. 브라우저 결과는 직접 관찰하므로 고정 출력이 없습니다.
확인 문제
실습
starter의 index.html을 수정해 이 레슨의 검사 계약을 통과시키세요. package.json 폴더에서 npm ci 후 npm test를 실행합니다. check.py는 수정하지 않습니다. 브라우저에서 링크·검색·키보드 흐름을 확인하고 KEYBOARD.md에 관찰 결과를 기록합니다. solution은 비교용이며 수동 검증 결과를 대신하지 않습니다.
실행 명령
npm test
기대 결과
모든 PASS 항목과 마지막 실패 0개를 확인합니다. 수동 점검 기록도 제출합니다.
모범 답안
모범 답안 내려받기더 읽기
면접 질문
- 상대 경로로 스타일 파일을 연결하면 동료에게 폴더를 전달할 때 어떤 이점이 있나요?
- 개발자 도구의 DOM과 HTML 원문이 다를 때 무엇을 확인하나요?