확인한 환경과 범위

Chromium 156.0.8078.48. 본문의 HTML·CSS·JavaScript를 실제 브라우저로 렌더링하고 현재 화면을 캡처했습니다. 모든 조작·레이아웃·접근성을 검증한 것은 아닙니다.

이 글의 순서 4개 단락

이름을 입력하고 버튼을 누르면 화면의 문장이 바뀌는 작은 페이지를 만들어 보겠습니다. 콘솔에 값을 출력하는 대신 HTML 요소를 선택하고, 클릭이 일어났을 때 그 요소의 내용을 바꾸는 실습입니다. 실습 환경은 Chromium입니다.

화면과 클릭 동작 만들기

핵심은 버튼을 선택한 뒤 addEventListener('click', 함수)로 클릭할 때 실행할 함수를 등록하는 것입니다. 입력값이 비어 있거나 공백뿐이면 이름을 요청하고, 이름이 있으면 인사 문장을 표시합니다.

먼저 화면을 구성하는 HTML입니다. 아래 JavaScript와 함께 사용합니다.

<!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>인사 버튼 실습</title>
</head>
<body>
  <main>
    <h1>인사 문장 만들기</h1>
    <label for="name">이름</label>
    <input id="name" type="text" placeholder="이름을 입력하세요">
    <button id="greet" type="button">인사하기</button>
    <p id="result" aria-live="polite">이름을 입력하고 버튼을 눌러 보세요.</p>
  </main>
</body>
</html>

JavaScript 버튼 클릭이 작동하지 않을 때: 요소 선택과 실행 시점 확인하기 — 첫 예제의 실제 실행 결과

다음은 보조 JavaScript입니다. 직접 하나의 HTML 문서로 실습한다면 아래 코드를 <script>와 </script> 사이에 넣어 </body> 바로 앞에 배치하세요. 그러면 입력창, 버튼, 결과 문단이 만들어진 뒤 코드가 실행됩니다.

const nameInput = document.querySelector('#name');
const greetButton = document.querySelector('#greet');
const result = document.querySelector('#result');

greetButton.addEventListener('click', () => {
  const name = nameInput.value.trim();

  if (name === '') {
    result.textContent = '이름을 입력해 주세요.';
    return;
  }

  result.textContent = `안녕하세요, ${name}님!`;
});

입력과 예상 화면 결과는 다음과 같습니다.

입력과 동작예상 결과 문장
처음 페이지를 열었을 때이름을 입력하고 버튼을 눌러 보세요.
지민을 입력하고 인사하기 클릭안녕하세요, 지민님!
입력을 지우고 인사하기 클릭이름을 입력해 주세요.
공백만 입력하고 인사하기 클릭이름을 입력해 주세요.

클릭하면 왜 문장이 바뀔까?

document.querySelector('#greet')는 ID가 greet인 첫 요소를 찾습니다. #은 ID 선택자입니다. 같은 방식으로 입력창과 결과 문단도 선택합니다. 선택한 버튼의 addEventListener에 이벤트 이름 click과 함수를 전달하면, 버튼을 클릭할 때 그 함수가 호출됩니다. 함수를 등록하는 시점에는 함수 본문이 실행되지 않습니다. MDN의 addEventListener 설명을 참고하세요.

함수 안에서는 클릭할 때마다 입력창의 현재 value를 읽습니다. trim()은 문자열 양끝의 공백을 제거하므로 지민 은 지민으로, 공백뿐인 입력은 빈 문자열로 처리됩니다. textContent에 새 문자열을 대입하면 결과 문단의 기존 내용이 바뀝니다. 입력한 HTML 태그도 화면에 텍스트로 표시됩니다.

버튼에 type="button"을 지정한 것은 이 버튼의 역할을 일반 버튼으로 명확히 하기 위해서입니다. 나중에 폼 안으로 옮겨도 제출 버튼으로 동작하지 않습니다.

요소를 찾지 못할 때 비교하기

querySelector는 유효한 선택자에 일치하는 요소가 없으면 null을 반환합니다. 문법 자체가 잘못된 선택자는 null 대신 예외를 발생시킵니다. 다음 두 경우는 결과가 같아 보여도 확인할 곳이 다릅니다. 각각 독립적으로 재현한 뒤 원래 코드로 되돌려 보세요.

선택자가 다른 경우

완성 예제의 버튼 선택 줄만 다음처럼 바꾸세요. HTML의 버튼 ID는 여전히 greet입니다.

const greetButton = document.querySelector('#greeting');
console.log(greetButton); // 예상: null

나머지 코드를 그대로 두고 새로고침하면 greetButton.addEventListener(...)에서 null의 속성을 읽을 수 없다는 TypeError가 발생할 것으로 예상됩니다. 클릭 함수가 등록되지 않으므로 버튼을 눌러도 처음 안내 문장이 유지됩니다. Chromium 개발자 도구의 Console에서 null과 오류를 확인하고, 선택자를 #greet로 고치세요. 오류 문구의 정확한 표현은 브라우저에 따라 다를 수 있습니다.

코드가 너무 일찍 실행되는 경우

다음은 별도의 최소 HTML입니다. script가 실행될 때 버튼은 아직 문서에 만들어지지 않았습니다.

<!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <title>실행 시점 확인</title>
  <script>
    const button = document.querySelector('#greet');
    console.log(button); // 예상: null
    button.addEventListener('click', () => console.log('클릭'));
  </script>
</head>
<body>
  <button id="greet" type="button">인사하기</button>
</body>
</html>

예상되는 콘솔 결과는 null 다음의 TypeError입니다. 이후 버튼이 화면에 나타나더라도 실패한 이벤트 등록이 자동으로 다시 실행되지는 않습니다. 클릭해도 클릭은 출력되지 않습니다.

해결하려면 이 예제의 <script>...</script> 전체를 버튼 다음, </body> 바로 앞으로 옮겨 새로고침하세요. 이제 버튼 선택 결과는 해당 버튼 요소이고 클릭할 때마다 콘솔에 클릭이 출력될 것으로 예상됩니다. 이처럼 일반 인라인 스크립트의 위치를 요소 뒤로 옮기는 방법을 사용할 수 있습니다. 스크립트 요소와 실행 방식은 MDN script 문서를 참고하세요.

선택자가 맞는데도 null이 나온다면 철자뿐 아니라 코드가 실행되는 시점도 살펴보세요. greetButton.addEventListener('click', 인사하기())처럼 함수를 호출해 결과를 전달하는 실수도 피해야 합니다. 이벤트가 일어났을 때 실행할 함수 자체를 전달해야 합니다.

연습: 긴 이름에 안내하기

완성 예제에서 공백을 제거한 이름이 10자를 초과하면 인사 대신 이름은 10자 이하로 입력해 주세요.를 표시해 보세요. 빈 입력 처리는 그대로 유지합니다. 예를 들어 가나다라마바사아자차카를 입력하면 안내 문장이 나와야 합니다.

풀이에서는 빈 입력 검사 뒤, 인사 문장 대입 전에 다음 조건을 추가합니다.

if (name.length > 10) {
  result.textContent = '이름은 10자 이하로 입력해 주세요.';
  return;
}

return은 현재 클릭 함수의 실행을 끝내므로 뒤의 인사 문장이 안내 문장을 덮어쓰지 않습니다. 이 연습의 length는 JavaScript 문자열의 UTF-16 코드 단위 수를 세므로 일부 이모지처럼 눈에 보이는 글자 수와 다를 수 있습니다.

CHECK THE SOURCE

참고한 공식 문서

기능 설명은 위 문서를 참고했으며, 예제의 입력·결과와 한계는 본문에 따로 기록했습니다.

한 줄씩 이해하고, 차곡차곡 기록합니다.이 글의 오류 제보하기 ↗