제로초 JavaScript 강의 - 순서도 그리기, 대화창(prompt, alert, confirm), HTML 선택하기, EventListener 달기(콜백함수), 끝말잇기 게임 만들기
- 제로초 JavaScript 강의 - 순서도 그리기, 대화창(prompt, alert, confirm), HTML 선택하기, 끝말잇기 게임 만들기
Review
- 제로초 JavaScript 강의 - 순서도 그리기, 대화창(prompt, alert, confirm), HTML 선택하기, 끝말잇기 게임 만들기
<순서도 그리기>
: 프로그래밍에서는 절차가 매우 중요하다.
코드를 에디터에 입력하기 전에 내가 만들 프로그램이 어떤 절차로 돌아갈지 미리 생각해야 한다.
우리가 만들 게임 순서도로 작성해보자.
먼저 끝말잇기가 어떤 식으로 진행되는지 말로 설명해보자. 최대한 구체적으로 순서를 말할 수
있으면 더 좋다. 귀찮아서 다음 문단으로 바로 넘어가는 사람도 있겠지만, 이 과정은 프로그래밍
사고를 하는 데 필수 과정이다. 이 과정이 머릿속에 있지 않으면 여러분은 프로그램을 작성할 수
없다.
사람들은 보통 절차를 설명할 때 예시를 들어 설명하는 경우가 많다.
1. 세 명의 참가자가 있습니다. (여기서는 A, B, C라는 사람이 있다고 가정)
2. A가 ‘자바스크립트’라고 말했습니다.
3. B가 ‘트집’이라고 말했습니다.
4. C가 ‘집합’이라고 말했습니다.
5. 다시 A가 ‘합체’라고 말했습니다.
6. B가...
7. 무한 반복
절차 예시는 분명 끝말잇기 게임이 맞다. 하지만 이러한 절차로는 프로그램을 만들 수 없다.
절차 1부터 생각해보자.
참가자의 수가 항상 세 명일까? 아닐 것이다. 두 명일 수도 있고 네 명일 수도 있다.
집에서 혼자 끝말잇기를 할 수도 있다. 다양한 가능성을 닫아 둔 채 구체적으로 세 명이라고
명시하는 것은 좋지 않다.
만약 세 명이 참가하는 끝말잇기 게임을 만들었는데 어떤 친구가 와서 자신도 끼어 달라고 하면
어떻게 해야 할까? 새로운 게임을 만들어야 할까? 네 명이 참가하는 게임을 만들어 줄 수도 있다.
그렇다면 끝말잇기 절차는 다음과 같이 바뀔 것이다.
1. 네 명의 참가자가 있습니다. (여기서는 A, B, C, D라는 사람이 있다고 가정)
2. A가 ‘자바스크립트’라고 말했습니다.
3. B가 ‘트집’이라고 말했습니다.
4. C가 ‘집합’이라고 말했습니다.
5. D가 ‘합체’라고 말했습니다.
6. 다시 A가 ‘체육’이라고 말했습니다.
7. B가...
8. 무한 반복
7단계였던 절차가 8단계로 늘어났다. 그런데 게임을 한다는 소문을 듣고 다섯 번째 친구가
온다면? 여섯 번째 친구는? 사람 수가 늘어날 때마다 절차가 계속 늘어날 것이다.
이번에는 절차 2부터 절차 6까지를 살펴보자.
A가 항상 ‘자바스크립트’라고 말한다는 보장이 있나? A는 자기가 원하는 어떤 단어든 말할 수 있다.
B는 A가 말하는 단어에 따라 대답이 달라진다. 즉, 절차 2부터 절차 6은 게임마다 달라진다.
마지막으로 절차 3을 살펴보자. A가 ‘자바스크립트’라고 말했는데 B가 ‘배고파’라고 말한다면 어떻게
될까? 엉뚱한 대답을 했으므로 게임이 진행되지 않아야 한다. 그런데 앞의 절차에서는 절차 4로
넘어가게 된다. 틀린 대답을 한 경우를 고려하지 않은 것이다.
프로그램은 고정된 절차로 돌아가야 한다.
몇 명이 참가하든 절차의 수가 같은 프로그램을 만들어야 한다. 또한, 각 절차는 항상 내용이 같아야
한다. 어떤 사람이 무엇을 말하든 프로그램은 그것을 받아들일 준비가 되어 있어야 한다.
따라서 예시를 들어 절차를 설명하는 행동은 좋지 않다. 예시는 언제든지 바뀔 수 있으니까.
예시는 절차를 세우고 나서 절차를 검증할 때 사용하는 게 좋다.
절차를 세울 때는 모든 가능성을 고려해야 한다.
‘자바스크립트’에 이어서 ‘트집’이라고 말하는 올바른 경우도 있겠지만, ‘자바스크립트’에 이어서
‘배고파’라고 말하는 틀린 경우도 발생할 수 있다. 틀린 경우에는 어떻게 설명할지도 절차에서 언급해야
한다. 다만, 처음부터 모든 가능성을 고려하기는 어렵다.
이런 경우에는 절차를 생각나는 대로 만들어 놓고 차차 보완해 나가는 것이 좋다.
* 프로그램 절차를 만들 때의 원칙
1. 프로그램 절차의 개수는 정해져 있어야 한다.
2. 각 절차는 항상 같은 내용이어야 한다.
3. 모든 가능성을 고려해야 한다.
4. 예시는 절차를 검증하는 데 사용한다.
원칙을 지키면서 절차를 만들어 나가기는 생각보다 어렵다. 한 번에 완성하려고 하지 말고 차근차근
보완해 나가면 된다.
다음과 같이 절차를 수정해 보겠다.
1. 게임에 몇 명이 참가할지를 선택한다.
2. 참가자 순서를 정한다(편의상 숫자로 한다).
3. 첫 번째 사람이 어떤 단어를 말한다.
4. 다음 사람이 어떤 단어를 말한다.
5. 절차 4에서 말한 단어가 올바른지 판단한다.
6. 올바르다면 그다음 사람이 어떤 단어를 말한다.
7. 올바르지 않다면 틀렸다고 표시한다.
8. 게임을 계속 진행(절차 4로 이동)한다.
절차 1이나 절차 3, 절차 4처럼 ‘몇’이나 ‘어떤’이라는 용어를 사용하니 다양한 경우에 대비할 수 있다.
다양한 경우를 대비할 수 있으니 원칙 1과 원칙 2를 위배할 일이 없어진다.
이렇게 절차를 만든 후에는 예시를 들어 검증해 보면 좋다.
세 명이 끝말잇기에 참가한다고 생각해보자(절차 1).
각자에게 1, 2, 3이라는 숫자를 부여하고(절차 2),
첫 번째 사람이 ‘자바스크립트’라는 단어를 말했다(절차 3).
다음 사람(2번 사람)이 ‘트집’이라는 단어를 말한다(절차 4).
‘자바스크립트’에 ‘트집’으로 이어지는 것은 맞다(절차 5).
따라서 그다음 사람(3번 사람)이 ‘배고파’라는 단어를 말했다(절차 6).
‘트집’ 다음에 ‘배고파’가 나오는 것은 올바르지 않으므로 틀렸다고 표시한다(절차 7).
게임을 계속 진행한다(절차 8).

가장 눈에 거슬리는 점은 절차 8이다. 절차 7에서 틀렸으므로 게임이 중단되거나 새로 시작돼야 할 텐데
게임을 계속 진행하다니 말이 안 된다.
또한, 절차를 검증할 때는 하나의 예시만 드는 게 아니라 다양한 예시를 들어야 한다. 원칙 3에서 ‘모든
가능성을 고려해야 한다’라고 했으니까.
절차 8을 제외하고 다른 예시를 들어 보겠다.
이번에는 두 명이 참가한다(절차 1).
참가자 순서를 1, 2로 정하고(절차 2),
첫 번째 사람이 ‘프로그래밍’이라고 말했다(절차 3).
다음 사람(2번 사람)이 ‘배고파’라고 말했다(절차 4).
‘프로그래밍’ 다음에 ‘배고파’가 나오는 것은 맞지 않다(절차 5).
따라서 틀렸다고 표시한다(절차 7).

여기서 다시 한번 이상한 것이 눈에 띈다. 절차 5 다음에 절차 6이 나오는 게 아니라 절차 7이 나온다.
이러한 현상이 발생하는 이유는 절차 5가 판단하는 절차이기 때문이다.
절차 5에 대한 대답이 ‘예’인지 ‘아니요’인지에 따라 절차 6으로 갈지 절차 7로 갈지 방향이 달라진다.
즉, 프로그램의 절차를 세울 때 절차는 항상 일직선 모양이 아니다.
판단 결과에 따라 절차가 갈라지는데, 이렇게 갈라지는 점을 분기점이라고 한다.
갈라지는 절차를 표현할 때는 1, 2, 3, 4... 순서대로 표현하는 것보다는 순서도를 그려서 시각적으로
표현하는 것이 편리하다.

일반적인 절차는 타원형으로, 판단을 요구하는 절차는 마름모로 표현한다.
판단 결과가 ‘예’이면 절차를 거슬러 올라가는 모습이 보인다.
절차를 거슬러 올라간다는 것은 ‘반복’을 의미한다. 단어가 올바르다면 게임은 멈추지 않고 무한 반복
하기 때문이다.
추가로 순서도에서 어디가 시작이고 끝인지도 두 겹의 원 기호로 표시했다.
순서도에서 절차가 일직선으로 진행되지 않으므로 시작과 끝을 시각적으로 표시하는 것이 좋다.
순서도를 검증하기 위해 앞서 들었던 예시를 적용해보면, 원칙을 위반하지 않으면서 모든 예시에
부합하고 있다.
이 정도면 훌륭한 순서도다 ! 완벽하지는 않다. 조금 더 보완해야 할 점이 있지만, 프로그래밍하지
않으면 생각조차 할 수 없는 부분이라 지금은 건너뛴다.
순서도만 그리면 재미없으니까 프로그램을 만들어 가면서 차차 보완해 나가도록 하겠다 !
<끝말잇기 게임 만들기 : 기본 HTML 작성>
: 먼저 우리가 만들 웹 게임의 특성을 알아보자.
웹 게임은 브라우저에서 돌아가는 프로그램으로 자바스크립트, HTML과 CSS라는 언어를 사용한다.
보통 HTML은 화면 요소(입력창, 버튼, 글자 등)를 담당하고, CSS는 요소의 디자인을 담당하며,
자바스크립트는 프로그램의 작동을 담당한다.
여기서 필수 언어는 HTML과 자바스크립트이다. 웹 게임을 실행할 수 있는 파일의 확장자가
HTML이므로 HTML은 어쩔 수 없이 사용해야 하고, 자바스크립트는 실제 웹 게임을 프로그래밍하는
데 사용한다.
HTML과 CSS의 사용을 최소화하고 자바스크립트만으로 화면 요소를 배치하고 디자인할 수 있긴 하다.
하지만 프로그램이 상당히 복잡해진다. 따라서 화면 요소는 HTML로 배치하고 디자인은 CSS로 한다.
HTML과 CSS에 화면 업무를 분담하면 자바스크립트가 담당해야 하는 부분이 줄어든다.
자바스크립트는 화면을 신경 쓰지 않고 프로그램의 작동만 담당하면 되니까.
<끝말잇기 게임 만들기 : 값 입력받아 변수에 저장하기>
프로그래밍할 때는 항상 순서도를 옆에 띄워 놓고 보면서 한다.
첫 번째 절차는 ‘몇 명이 참가할지 선택한다’이다.
프로그램은 몇 명이 참가할지를 스스로 알아내지 못하므로 직접 알려 줘야 한다.
방법은 크게 두 가지인데, 입력창(input 태그)을 사용하는 방법과 prompt 함수를 사용하는 방법이다.
이번에는 prompt 함수를 사용하겠다. prompt 함수로 직접 프로그램에 값을 전달할 수 있다.
자바스크립트 코드도 HTML처럼 기본적으로는 위에서 아래로 실행되므로 첫 번째 절차인 prompt
함수는 맨 위에 적는다. HTML에서 다른 부분은 변하지 않으므로 script 태그 내부만 수정하면 된다.
prompt('사용자에게 표시할 메시지');
<script>
prompt('몇 명이 참가하나요?');
</script>
HTML 파일을 저장하고 실행하면 다음과 같은 창이 뜨면서 입력을 기다린다.
이 창을 대화 상자라고 부른다.
세 명이 참가한다고 가정하고 3을 입력해보자.
이렇게 프로그램이 여러분으로부터 3이라는 값을 전달받았다. 참고로 취소를 누르면 프로그램에
null 값이 전달된다.
3을 입력하면 아무 일도 일어나지 않는다. 전달받은 3으로 아무것도 하지 않았으니까.
또한, 여러분이 전달한 값을 어딘가에 저장하지 않으면 프로그램은 금방 그 값을 잊어버린다.
그러면 값을 어떻게 저장할 수 있을까? 변수를 사용하면 된다.
<script>
const number = prompt('몇 명이 참가하나요?');
</script>
값이 변수에 제대로 저장됐는지 확인해보자. 이럴 때는 console.log라는 함수를 사용하면 좋다.
<script>
const number = prompt('몇 명이 참가하나요?');
console.log('number', number);
</script>
number 변수에 3이 제대로 저장됐다는 것을 콘솔 창에서 확인할 수 있다.

prompt 함수를 사용하면 브라우저에서 대화 상자가 뜬다.
이런 창이 뜨는 동안은 그다음 코드가 실행되지 않고 멈춰 있다. 비슷한 함수로 alert과 confirm이 있다.
alert 함수는 주로 어떠한 메시지를 사용자에게 알릴 때 사용한다. console.log 대신에 변수의 값을 확인
하는 용도로도 사용할 수 있다.
alert('사용자에게 표시할 메시지');
<script>
const number = prompt('몇 명이 참가하나요?');
alert(number);
</script>
다만, console.log 함수로 값을 확인하는 것을 추천한다.
alert 함수를 사용하면 여러 값을 동시에 확인하기 어려울 뿐만 아니라(alert('number', number) 같은 것을
할 수 없다) 실제 사용자들에게도 alert 대화 상자가 보이기 때문이다.

confirm 함수는 사용자에게 의사를 물어볼 때 사용한다.
confirm('사용자에게 표시할 메시지')
<script>
const yesOrNo = confirm('확인이나 취소를 눌러보세요');
console.log(yesOrNo);
</script>

[확인]을 누르면 true가 콘솔 창에 기록되고 [취소]를 누르면 false가 콘솔 창에 기록된다.
이렇게 나온 true나 false 값을 프로그래밍할 때 사용하면 된다.
콘솔은 오른쪽 마우스를 클릭해 '검사'로 들어가 확인하면 된다.
다시 원래 코드로 돌아가 보자. 입력한 값은 어떤 자료형일까?
3을 입력했으니 숫자일까?
<script>
const number = prompt('몇 명이 참가하나요?');
console.log(typeof number);
</script>

놀랍게도 문자열이다. 사실 prompt 함수로 입력받은 값은 모두 문자열이 된다.
3을 입력해도 '3'이라는 문자열이 되고, true를 입력해도 'true'라는 문자열이 된다.
따라서 문자열인 3을 숫자로 변환해야 한다.
<script>
const number = prompt('몇 명이 참가하나요?');
const realNumber = Number(number);
console.log(typeof realNumber);
</script>

아니면 realNumber 같은 변수를 사용하지 않고 다음과 같이 바로 숫자로 바꿔도 된다.
<script>
const number = Number(prompt('몇 명이 참가하나요?'));
console.log(typeof number);
</script>
Number를 사용해도 되고 parseInt를 사용해도 된다.
<1분 퀴즈>
다음 중 함수 설명이 올바른 것을 고르세요.
① prompt 함수는 사용자로부터 값을 전달받는다.
② alert 함수는 사용자의 확인을 요구한다.
③ confirm 함수는 사용자에게 경고 메시지를 표시한다.
정답은 1번이다.
2번과 3번은 설명이 서로 바뀌었다.
<HTML 태그 선택하기>
: 몇 명이 참가할지를 전달했고 그 값을 변수에 저장했으니, 이제 다음 절차인 ‘첫 번째 사람이 어떤
단어를 말한다’를 프로그래밍해 보자.
이전에는 값을 prompt 함수로 입력받았다면 이번에는 입력창(input 태그)으로 입력받겠다.
다만, 입력창은 HTML 태그이므로 자바스크립트에서 이 입력창을 가져와야 한다.
보통 자바스크립트에서 HTML 태그를 가져오는 것을 선택한다고 표현한다. 선택하기 위해서는 특별한
함수와 특별한 방법을 사용한다. 특별한 함수는 document.querySelector이고, 사용법은 다음과 같다.
document.querySelector('선택자')
여기서 ‘선택자’라는 용어가 나온다. 선택자는 HTML 태그를 선택할 수 있게 도와주는 문자열이다.
선택자에 관해 좀 더 알아보자. script 태그 내부를 다음과 같이 바다.
<script>
const $input = document.querySelector('input');
console.log($input);
</script>
선택자로 input을 넣어서 HTML을 실행해 보겠다.

콘솔에 HTML로 작성한 input 태그가 표시된다.
input 태그에 마우스를 올려보면 왼쪽의 실제 화면에서 input 태그가 하이라이트된다.
이렇게 선택자를 태그 이름으로 두면 해당 태그가 선택된다.
$input 변수는 우리가 선택한 input 태그를 저장한다. 태그를 저장하는 변수명이 $로 시작하도록 규칙을
정하겠다. 여러 개의 태그를 동시에 선택한다면 변수명이 $$로 시작하도록 규칙을 설정하겠다.
이번에 button 태그를 선택해보자.
<script>
const $button = document.querySelector('button');
console.log($button);
</script>

'검사' 창을 띄워 콘솔 창에서 확인할 수 있다.
태그가 여러 개 있는 경우도 있다. 이때 태그를 모두 선택하고 싶다면 document.querySelectorAll 함수를
사용하면 된다.
<body>
<div><span id="order">1</span>번째 참가자</div>
<div>제시어: <span id="word"></span></div>
<input type="text">
<button>입력</button>
<button>버튼2</button>
<button>버튼3</button>
<script>
const $$buttons = document.querySelectorAll('button');
console.log($$buttons);
</script>
</body>

콘솔창에 뜨는 NodeList는 배열처럼 생긴 객체인 유사배열이다.
배열에서 배웠던 arguments도 역시 유사배열이다.
이번에는 콘솔창에 버튼이 세 개이므로 콘솔 창에 세 개의 태그가 동시에 표시된다.
여러 개를 한 번에 표시하려고 배열을 사용한 것처럼 보이지만, 실제로는 배열이 아니다.
NodeList라는 특수한 객체, 즉 배열처럼 생긴 객체인 유사배열이다. NodeList와 배열의 차이점은
나중에 설명하겠다.
이렇게 버튼이 여러 개인데 document.querySelectorAll 대신 document.querySelector를
사용한다면 어떻게 될까?
<script>
const $button = document.querySelector('button');
console.log($button);
</script>

HTML에 나오는 첫 번째 버튼만 선택된다. 따라서 여러 개의 태그를 선택하고 싶다면 document.querySelector
대신 document.querySelectorAll을 써야 한다는 점을 기억하자 !
그럼 여러 개의 태그 중에서 특정한 태그만 선택하려면 어떻게 해야 할까?
document.querySelector를 쓰면 첫 번째 태그만 선택된다. 이럴 때를 대비해 다른 선택자들이 존재한다.
<body>
<div><span id="order">1</span>번째 참가자</div>
<div>제시어: <span id="word"></span></div>
<input type="text">
<button>입력</button>
<button>버튼2</button>
<button>버튼3</button>
<script>
const $order = document.querySelector('#order');
console.log($order);
</script>
</body>
span 태그에 미리 id 속성을 달아 둔 이유가 있다. id 값은 태그에 달 수 있는 고유한 값이다.
한번 사용한 id 값은 다른 태그에 재사용할 수 없다. 예를 들어, id="gilbut"이라는 값을 사용했으면 다른
태그는 id="gilbut"을 사용할 수 없다(자바스크립트 기준이며 HTML에서는 여러 번 사용할 수 있다).
해당 id를 가진 태그는 유일하므로 태그에 id를 달아 두면 선택자로 선택하기 쉬워진다.
order라는 id가 있으면 앞에 #을 붙여 #order라는 선택자를 사용하면 된다.
document.querySelector('#아이디');

두 번째와 세 번째 버튼을 동시에 선택하고 싶다면 어떻게 해야 할까?
id는 하나의 태그에만 쓸 수 있으므로 두 개를 동시에 선택할 때는 사용할 수 없다.
이럴 때는 class를 사용한다. 자바스크립트 문법에 있는 class와는 다르다.
<body>
<div><span id="order">1</span>번째 참가자</div>
<div>제시어: <span id="word"></span></div>
<input type="text">
<button>입력</button>
<button class="hello">버튼2</button>
<button class="hello">버튼3</button>
<script>
const $buttons = document.querySelectorAll('.hello');
console.log($buttons);
</script>
</body>
두 번째와 세 번째 버튼에 hello라는 클래스를 달았다. 이렇게 클래스는 여러 번 사용할 수 있다.
hello 클래스를 선택할 때는 클래스 앞에 .을 붙여 .hello로 선택하면 됩니다.
document.querySelectorAll('.클래스');

이때도 document.querySelectorAll 대신 document.querySelector를 사용하면 하나의 태그만 선택되니
주의하자 !
마지막으로 어떤 태그 안에 들어 있는 다른 태그를 선택해 보자.
<body>
<div><span id="order">1</span>번째 참가자</div>
<div>제시어: <span id="word"></span></div>
<input type="text">
<button>입력</button>
<script>
const $span = document.querySelector('div span');
console.log($span);
</script>
</body>
선택자를 여러 개 동시에 적으면 되는데, 선택자 사이에 공백(띄어쓰기)을 주어 구분한다.
가장 먼저 나오는 선택자가 기준 태그이고, 다음에 나오는 선택자는 기준 태그 안에 들어 있는 태그이다.
예를 들어, 선택자가 div span일 때는 div 태그 안에 들어 있는 span 태그를 찾는다.
다른 예로, 선택자가 body #target button일 때는 body 태그 안에 들어 있는 id가 target인 태그에서 다시
그 안에 들어 있는 button 태그를 찾는다.
document.querySelector('선택자 내부선택자 내부선택자...');

앞에서는 div 태그 안에 들어 있는 span 태그를 찾아봤다. 다만, 실무에서는 div나 span 태그가 흔하게
쓰이므로 div 안에 들어 있는 span 태그가 상당히 많을 수밖에 없다. 그래서 span 태그에 id를 붙여
선택하기 쉽게 만들어 둔다.
이외에도 다양한 선택자가 있지만, 이 정도면 충분히 HTML 태그들을 선택할 수 있다.
<1분 퀴즈>
a 태그 안에 id가 b인 태그 안에 class가 c인 태그를 선택하려면 어떤 선택자를 사용해야 할까요?
① a b c
② a .b #c
③ a #b .c
④ a .b .c
정답은 3번이다.
<태그에 이벤트 달기>
: 사용자들은 웹 게임을 플레이하면서 화면과 다양한 상호 작용을 한다.
이는 HTML 태그와 상호 작용을 하는 것인데, 끝말잇기 게임만 하더라도 사용자는 input 태그에 글자를
입력하고 button 태그를 누른다.
사용자가 태그와 상호 작용을 할 때 이벤트라는 것이 발생한다.
예를 들어, input 태그에 글자를 입력하면 input이라는 이벤트가 발생하고, 버튼을 클릭하면 click 이벤트가
발생하는 식이다. 이처럼 다양한 이벤트가 발생하지만, 자바스크립트는 이벤트를 자동으로 감지할 수 없다.
그래서 이벤트 리스너라는 것을 직접 추가해 자바스크립트가 HTML에서 발생하는 이벤트를 감지할 수 있게
만들겠다. 앞 절에서 배웠던 태그 선택 함수(document.querySelector)를 사용해 HTML 태그들에 이벤트
리스너를 달아 보자.
먼저 이벤트가 발생할 때 실행할 함수를 하나 만들고 이 함수를 태그에 연결한다.
<script>
const onClickButton = () => {
console.log('버튼 클릭');
};
const $button = document.querySelector('button');
$button.addEventListener('click', onClickButton);
</script>
이때 addEventListener라는 함수를 사용합니다.
태그.addEventListener('이벤트 이름', 리스너함수);
여기서는 태그를 document.querySelector('button')으로 선택했다. 이 태그에 addEventListener를 붙여
이벤트를 연결할 수 있다. 클릭 이벤트의 이름은 click이다.
버튼을 클릭하면 onClickButton 함수가 실행됩니다. 이때 onClickButton 대신 onClickButton()를 넣으면
안 된다. ()를 붙이면 클릭과 상관없이 함수가 실행된다. 함수 자체를 넣어야 하고, ()를 붙여 함수를
실행해서는 안 된다는 점에 주의하자 !
이때 onClickButton 같은 함수를 콜백 함수(callback function)라고 한다.
콜백 함수는 특정 작업이 실행되고 난 뒤에 추가로 실행되는 함수를 의미한다. 버튼을 클릭한 후에
onClickButton이 추가적으로 실행되므로 콜백 함수라고 볼 수 있다.
변수를 사용하는 대신 다음과 같이 한 번에 코딩할 수도 있다. 다만, 변수 사용을 추천한다.
읽기가 더 쉽고 나중에 변수를 재사용할 수 있기 때문이다.
<script>
document.querySelector('button').addEventListener('click', () => {
console.log('버튼 클릭');
});
</script>
이제 버튼을 클릭하면 콘솔 창에 기록된다. 즉, 등록한 이벤트 리스너가 제대로 작동하고 있다는 뜻이다 !

다른 이벤트도 알아보자. input 태그에 글자를 입력하면 input 이벤트가 발생한다.
<script>
const onClickButton = () => {
console.log('버튼 클릭');
};
const $button = document.querySelector('button');
$button.addEventListener('click', onClickButton);
const onInput = (event) => {
console.log('글자 입력', event.target.value);
};
const $input = document.querySelector('input');
$input.addEventListener('input', onInput);
</script>
버튼과 비슷하게 input 태그를 선택한 뒤 input 이벤트를 연결한다. 여기서 onInput 함수에 조금 독특한 점이
있는데, 함수의 매개변수로 event가 존재한다는 점이다.
이벤트 리스너에 넣는 함수에는 매개변수로 이벤트에 관한 정보가 제공된다(addEventListener가 제공, https://developer.mozilla.org/ko/docs/Web/API/Event). 여기서 event.target.value로 input 태그에 입력한
값을 알아낼 수 있다.
참고로 event는 매개변수이므로 다른 이름으로 지어도 된다. 아래처럼 e로 해도 되고 전혀 관련 없는 hello로
지어도 된다. 다만, hello로 지으면 코드를 보는 사람들이 헷갈리니 보통 event라고 이름을 짓는다.
const onInput = (e) => {
console.log('글자 입력', e.target.value);
};
const onInput = (hello) => {
console.log('글자 입력', hello.target.value);
};
작성한 HTML을 실행해 입력창에 글자를 입력하면 콘솔 창에 기록된다.

여기서 직접 실행해보고 콘솔 창에서 확인할 수 있다.
변수를 사용해서 이렇게 표현할 수도 있다.
function 함수 대신 화살표 함수를 이용해도 되는데 결과는 같다.
하지만 둘의 차이점이 있어 주의해야 하는데 이는 나중에 다루도록 하겠다.
<1분 퀴즈>
이벤트를 달 때 사용하는 메서드는 무엇인가요?
① querySelector
② addListener
③ addEventListener
④ removeListener
정답은 3번이다.
removeListener는 이벤트를 지울 때 사용하는 메소드이고, querySelector는 태그를 선택할 때 사용하는
메소드이다.
<첫 단어를 입력한 사람인지 판단하기>
: 이벤트 리스너가 작동한다는 사실을 확인했으니 필요 없는 코드는 지우고 절차 3을 프로그래밍해보자.
첫 번째 사람한테 1, 두 번째 사람한테 2, 세 번째 사람한테 3, 이렇게 순서를 부여했으니 절차 2는 따로
코딩할 필요가 없다.
코드를 순서도대로 보기 좋게 정렬한다. 기본적으로 위에서 아래로 순서대로 실행된다는 것을 기억하자.
<script>
const number = Number(prompt('몇 명이 참가하나요?'));
const $button = document.querySelector('button');
const $input = document.querySelector('input');
const onClickButton = () => {
};
const onInput = (event) => {
};
$button.addEventListener('click', onClickButton);
$input.addEventListener('input', onInput);
</script>
이제 단어를 입력한 사람이 첫 단어를 입력한 참가자인지 아닌지를 판단해야 하는데, 여기서 순서도를
수정해야 한다.

절차 2와 절차 3 사이에는 참가자가 단어를 입력하는 데 걸리는 시간이 조금 필요하다.
참가자가 단어를 입력하지 않으면 영원히 절차 3은 수행되지 않는다.
input 이벤트나 click 이벤트 모두 이러한 특성이 있다. 사용자가 이벤트를 발생시키지 않으면 다음
절차로 넘어가지 않는다. 이럴 때는 순서도를 한 번 끊어 주는 것이 좋다.
프로그램에 대기 상태가 발생하고 이벤트가 발생하면 끊어주고, 두 개로 겹쳐진 네모 기호로 쓴다.

사용자가 단어를 입력하고 입력 버튼을 클릭하면 오른쪽 부분이 실행된다. 여기서 첫 단어를 입력한
참가자인지를 판단하면 된다. ‘판단’이라고 했으므로 사실 순서도에서 타원이 아니라 마름모를
써야 한다.
입력 버튼을 클릭한 사람이 첫 단어를 입력한 참가자라면 그 사람이 입력한 단어가 제시어가 된다.
그 후에는 다음 참가자에게 순서를 넘기고 대기하면 된다. 반대로 첫 단어를 입력한 참가자가 아니라면
끝말잇기를 진행하는 상황이므로 방금 입력한 단어가 올바른지 판단하면 된다.
올바르다면 입력한 단어를 제시어로 만든 후 다음 참가자에게 순서를 넘기고 대기하면 되고, 올바르지
않다면 올바르지 않다고 표시하고 프로그램을 종료한다.
이를 토대로 다시 순서도를 수정해 보자.

순서도가 상당히 복잡해졌다. 순서도는 일직선이 아니므로 중간에 갈라지기도 하고 다시 합쳐지기도 한다.
이제 입력 버튼을 클릭한 사람이 첫 단어를 입력한 참가자인지 코드로 판단할 시간이다.
어떻게 판단할까?
첫 단어를 말한 참가자인지 아닌지를 알려면 첫 단어가 무엇인지 기억하고 있어야 한다.
이렇게 무언가를 기억하고 있으려면 변수가 필요하다. 단어를 기억하는 변수를 만들어보자.
그래야 첫 단어도 저장하고, 그다음 단어와도 비교해서 올바른 단어인지 판단할 수 있을 테니까.
따라서 처음 시작할 때 단어를 저장할 변수를 만들고 이 변수를 빈 값으로 놔둔다.
그런데 이미 코드에 빈 값을 가진 태그가 있다. 바로 #word 태그이다.
제시어가 들어갈 자리인데 제시어가 없으므로 빈 값으로 있다. 이 값을 가져와서 변수에 넣으면 된다.
따라서 판단하려 했던 ‘첫 번째 참가자인가?’라는 절차는 ‘제시어가 비어 있는가?’로 바꿔도 된다.
제시어가 비어 있다면 아직 아무도 단어를 입력하지 않았다는 것이고, 이는 값을 입력하는 사람이 첫 번째
참가자라는 의미이기 때문이다.

지금까지 순서도를 수정한 것을 바탕으로 코드를 짜보면 이러하다.
<script>
const number = Number(prompt('몇 명이 참가하나요?'));
const $button = document.querySelector('button');
const $input = document.querySelector('input');
let word; // 제시어
const onClickButton = () => {
if (!word) { // 제시어가 비어 있는가?
// 비어 있다.
} else {
// 비어 있지 않다.
}
};
const onInput = (event) => {
console.log('글자 입력', event.target.value);
};
$button.addEventListener('click', onClickButton);
$input.addEventListener('input', onInput);
</script>
제시어를 저장할 word 변수를 선언했다. word는 사람들이 단어를 입력할 때마다 바뀌므로 const 대신
let을 사용했다.
선언할 때 아무런 값을 넣지 않았으니 word는 undefined가 된다. undefined는 if 문 안에 들어가면
false로 취급되고, !word는 true로 취급됩니다. 그래서 ‘단어가 비어 있는가?’를 !word로 표현했다.
이제 ‘입력된 단어가 제시어가 된다’를 코딩하려면 참가자가 입력한 단어를 저장해야 한다.
따라서 변수가 필요하다. 이 변수는 newWord로 선언한다. 입력한 단어를 input 이벤트 안에서
newWord 변수에 저장하면 된다.

<script>
const number = Number(prompt('몇 명이 참가하나요?'));
const $button = document.querySelector('button');
const $input = document.querySelector('input');
const $word = document.querySelector('#word');
let word; // 제시어
let newWord; // 현재 단어
const onClickButton = () => {
if (!word) { // 제시어가 비어 있는가?
word = newWord; // 입력한 단어가 제시어가 된다.
$word.textContent = word; // 화면에 제시어 표시
} else {
// 비어있지않다.
}
};
const onInput = (event) => {
newWord = event.target.value; // 입력하는 단어를 현재 단어로
};
$button.addEventListener('click', onClickButton);
$input.addEventListener('input', onInput);
</script>
#word 태그를 선택해서 $word 변수에 대입했고, 현재 단어를 저장할 newWord 변수를 선언했다.
제시어가 비어 있다면 입력한 단어를 제시어로 만든다.
그 다음 줄에 $word.textContent가 나오는데 이는 태그 내부의 값을 얻거나 수정할 때 사용한다.
처음에 #word는 빈 값이므로 $word.textContent를 하면 ' '을 얻을 수 있다. textContent는 무조건 문자열로
나오므로 빈 값인 null이나 undefined가 아니라 ' '이 나온다.
반대로 $word.textContent에 다른 값을 넣으면 그 값이 화면에 설정된다.
$word.textContent = '자바스크립트'를 하면 #word 내부의 값이 ‘자바스크립트’로 설정됩니다.
태그.textContent // 태그 내부의 문자열을 가져옴
태그.textContent = >값 // 태그 내부의 문자열을 해당 값으로 설정함
<올바른 단어인지 판단하기>
: 이번에는 올바른 단어인지 판단하는 코드를 작성해보자.
제시어가 비어 있지 않은 경우인데, 두 번째 사람이 단어를 입력할 때부터 이 코드가 실행된다.
끝말잇기에서 단어가 올바른지 판단하려면 제시어의 끝 글자와 현재 단어의 첫 글자를 비교하면 된다.
어떻게 비교할까?
문자열은 ‘문자’의 나‘열’이다. 문자들을 이어 놓은 것이므로 각각의 문자를 분리할 수도 있다.
이때 다음과 같이 하면 된다.
문자열[자릿수]
단, 자릿수는 0부터 시작하므로 첫 번째 자리가 0이고 두 번째 자리가 1이다.
따라서 hello라는 단어가 있을 때 hello[0]을 하면 h라는 문자가 나오고 hello[1]을 하면 e가 나온다.
마지막 문자는 어떻게 가져올까?
자릿수가 0부터 시작하니 hello의 마지막 문자는 hello[4]를 하면 된다. 하지만 문자열들은 길이가 다를 수
있으므로 마지막 문자가 항상 [4]라고 할 수 없다.
예를 들어, javascript라는 문자열은 javascript[9]를 해야 마지막 문자인 t가 나온다.
그렇다면 어떻게 해야 문자열의 길이와 상관없이 항상 마지막 문자를 가져올까?
자세히 보면 문자열의 길이와 마지막 자릿수 사이에 규칙이 보인다. hello는 5글자이면서 마지막 자릿수가
4이고, javascript는 10글자이면서 마지막 자릿수가 9이다.
webgame은 7글자이면서 마지막 자릿수가 6이다. 즉, 글자 수에서 1을 빼면 마지막 자릿수가 나온다.
따라서 문자열의 글자 수(길이)만 구하면 마지막 자릿수도 구할 수 있다.
문자열의 길이는 다음과 같이 구한다.
문자열.length
hello.length를 하면 5가 나오고, javascript.length를 하면 10이 나온다.
여기서 1을 빼면 마지막 자릿수가 나온다. word의 길이는 word.length로, 마지막 자릿수는 word.length - 1로
구할 수 있다.
word의 마지막 문자는 문자열[마지막 자릿수]이므로 word[word.length - 1]을 하면 된다.
제시어인 word의 마지막 문자를 구했으니 현재 단어인 newWord의 첫 번째 문자와 비교하면 된다.
word[word.length - 1] === newWord[0]으로 비교하면 된다.
<script>
const number = Number(prompt('몇 명이 참가하나요?'));
const $button = document.querySelector('button');
const $input = document.querySelector('input');
const $word = document.querySelector('#word');
let word; // 제시어
let newWord; // 현재 단어
const onClickButton = () => {
if (!word) { // 제시어가 비어 있는가?
word = newWord; // 입력한 단어가 제시어가 된다.
$word.textContent = word; // 화면에 제시어 표시
} else { // 비어 있지 않다.
if (word[word.length - 1] === newWord[0]) { // 입력한 단어가 올바른가?
// 올바르다.
word = newWord; // 현재 단어를 제시어에 저장한다.
$word.textContent = word; // 화면에 제시어 표시
} else {
// 올바르지 않다.
}
}
};
const onInput = (event) => {
newWord = event.target.value; // 입력한 단어를 현재 단어로
};
$button.addEventListener('click', onClickButton);
$input.addEventListener('input', onInput);
</script>
이제 다음 사람에게 순서를 넘긴다. number 변수에 참가자가 몇 명인지 저장하고 있다.
그리고 #order 태그 안에는 몇 번째 참가자인지 순서가 나와 있다. 참가자가 세 명일 때 1번 참가자부터
시작하면 다음은 2번, 그다음은 3번이 됐다가 다시 1번 참가자로 순서가 돌아갈 것이다.
순서도에 단순히 ‘다음 사람에게 순서를 넘긴다’라고 적었지만, 이 절차도 실제 프로그램으로 표현하려면
세부 절차로 나뉘게 되고 판단도 필요하다.

예를 들어, 세 명의 참가자가 있으면 number는 3이 된다.
현재 순서가 1이라면(#order 내부의 값 1) 여기에 1을 더한 값은 2로 number보다 작다.
이때는 현재 순서에 1을 더한 값인 2를 다음 순서로 설정하면 된다.
현재 순서가 3이라면(#order 내부의 값 3) 여기에 1을 더한 값은 4로 number보다 크다.
이때는 다음 순서가 1로 다시 돌아가야 한다.
이 절차를 코드로 표현하면 다음과 같다.
<script>
const number = Number(prompt('몇 명이 참가하나요?'));
const $button = document.querySelector('button');
const $input = document.querySelector('input');
const $word = document.querySelector('#word');
const $order = document.querySelector('#order');
let word; // 제시어
let newWord; // 현재 단어
const onClickButton = () => {
if (!word) { // 제시어가 비어 있는가?
word = newWord; // 입력한 단어가 제시어가 된다.
$word.textContent = word; // 화면에 제시어 표시
const order = Number($order.textContent);
if (order + 1 > number) {
$order.textContent = 1;
} else {
$order.textContent = order + 1;
}
} else { // 비어 있지 않다.
if (word[word.length - 1] === newWord[0]) { // 입력한 단어가 올바른가?
// 올바르다.
word = newWord; // 현재 단어를 제시어에 저장한다.
$word.textContent = word; // 화면에 제시어 표시
const order = Number($order.textContent);
if (order + 1 > number) {
$order.textContent = 1;
} else {
$order.textContent = order + 1;
}
} else {
// 올바르지 않다.
}
}
};
const onInput = (event) => {
newWord = event.target.value; // 입력한 단어를 현재 단어로
};
$button.addEventListener('click', onClickButton);
$input.addEventListener('input', onInput);
</script>
#order 태그를 선택해 $order 변수에 저장했다. 그리고 #order 태그 내부의 값을 꺼내 숫자로 변환하고
이를 order 변수에 저장했다. 그 값에 1을 더한 값이 number보다 크면 화면에 순서를 1로 표시하고,
작으면 화면에 order + 1을 순서로 표시한다.
중복된 부분이 찜찜할 수 있지만, 이 부분은 나중에 해결하겠다.
<1분 퀴즈>
세 글자 이상의 단어를 저장하고 있는 word라는 변수가 있을 때 뒤에서 세 번째 글자를 가져오는
코드를 작성하세요.
정답은 word[word.length - 3];
<틀렸을 때 오류 표시하기>
: 지금까지 수정한 순서도는 다음과 같다.

이제 올바르지 않은 단어를 입력한 경우만 처리하면 된다. alert 함수를 사용해 틀렸다는 것을 알려주도록
하겠다.
<script>
const number = Number(prompt('몇 명이 참가하나요?'));
const $button = document.querySelector('button');
const $input = document.querySelector('input');
const $word = document.querySelector('#word');
const $order = document.querySelector('#order');
let word; // 제시어
let newWord; // 현재 단어
const onClickButton = () => {
if (!word) { // 제시어가 비어 있는가?
word = newWord; // 입력한 단어가 제시어가 된다.
$word.textContent = word; // 화면에 제시어 표시
const order = Number($order.textContent);
if (order + 1 > number) {
$order.textContent = 1;
} else {
$order.textContent = order + 1;
}
} else { // 비어 있지 않다.
if (word[word.length - 1] === newWord[0]) { // 입력한 단어가 올바른가?
// 올바르다.
word = newWord; // 현재 단어를 제시어에 저장한다.
$word.textContent = word; // 화면에 제시어 표시
const order = Number($order.textContent);
if (order + 1 > number) {
$order.textContent = 1;
} else {
$order.textContent = order + 1;
}
} else { // 올바르지 않다.
alert('올바르지 않은 단어입니다!');
}
}
};
const onInput = (event) => {
newWord = event.target.value; // 입력한 단어를 현재 단어로
};
$button.addEventListener('click', onClickButton);
$input.addEventListener('input', onInput);
</script>
이제 HTML 파일을 실행해 끝말잇기 게임이 올바르게 작동하는지 확인해 보자.
순서도를 올바르게 그렸다면 제대로 작동하고, 올바르게 그리지 않았다면 어딘가에서 오류가 발생할 것이다.
또한, 순서도를 검사하기 위해 가능한 모든 경우의 수를 테스트해 보는 것이 좋다.



테스트하다 보면 부족한 점을 몇 가지 느낄 수 있다.
단어를 입력하고 나서 입력 버튼을 누르면 다음 참가자로 순서가 넘어가게 된다. 이때 이전 사람이
input 태그에 입력한 단어가 그대로 남아 있어서 지우고 다시 입력해야 한다. 단어를 지우려고 input 태그도
한 번 더 눌러서 커서를 표시해야 하니 귀찮을 것이다.
이렇게 사소한 것까지 사용자의 편의를 신경 쓰면 더 좋을 것이다.
순서도부터 수정해보자.

이제 수정한 순서도를 코드로 옮기면 다음과 같다.
<script>
const number = Number(prompt('몇 명이 참가하나요?'));
const $button = document.querySelector('button');
const $input = document.querySelector('input');
const $word = document.querySelector('#word');
const $order = document.querySelector('#order');
let word; // 제시어
let newWord; // 현재 단어
const onClickButton = () => {
if (!word) { // 제시어가 비어 있는가?
word = newWord; // 입력한 단어가 제시어가 된다.
$word.textContent = word; // 화면에 제시어 표시
const order = Number($order.textContent);
if (order + 1 > number) {
$order.textContent = 1;
} else {
$order.textContent = order + 1;
}
$input.value = '';
$input.focus();
} else { // 비어 있지 않다.
if (word[word.length - 1] === newWord[0]) { // 입력한 단어가 올바른가?
// 올바르다.
word = newWord; // 현재 단어를 제시어에 저장한다.
$word.textContent = word; // 화면에 제시어 표시
const order = Number($order.textContent);
if (order + 1 > number) {
$order.textContent = 1;
} else {
$order.textContent = order + 1;
}
$input.value = '';
$input.focus();
} else { // 올바르지 않다.
alert('올바르지 않은 단어입니다!');
$input.value = '';
$input.focus();
}
}
};
const onInput = (event) => {
newWord = event.target.value; // 입력한 단어를 현재 단어로
};
$button.addEventListener('click', onClickButton);
$input.addEventListener('input', onInput);
</script>
$input은 input 태그를 선택하는 변수이다. input 태그 내부의 내용을 수정하려면 $input.value = 값을
하면 된다. 왜 textContent를 안 쓰냐면, 기본적으로 태그 내부의 값을 선택할 때는 textContent를
사용하는 게 맞지만, 입력 태그만 value를 사용한다. input은 대표적인 입력 태그이다.
다른 입력 태그로는 select와 textarea가 있다.
입력태그.value // 입력창의 값을 가져옴
입력태그.value = 값 // 입력창에 값을 넣음
또한, 입력 태그를 선택하게 하려면 focus라는 메서드를 사용한다. focus는 입력 태그 내부에 커서를
위치하게 해서 다음 사용자가 입력하기 편하게 도와준다.
입력태그.focus() // 입력창을 하이라이트
<1분 퀴즈>
다음 태그들의 내부 값을 가져올 때 둘 중 어떤 속성을 사용해야 하는지 표시해 보세요.
① input (value / textContent)
② button (value / textContent)
③ select (value / textContent)
④ div (value / textContent)
⑤ textarea (value / textContent)
⑥ span (value / textContent)
정답
① input (value / textContent)
② button (value / textContent)
③ select (value / textContent) // input과 비슷
④ div (value / textContent)
⑤ textarea (value / textContent) // input과 비슷
⑥ span (value / textContent)
<순서도 최적화하기>
: 순서도를 완성했으니 마지막으로 순서도와 코드를 점검해 보자. 가장 기본은 중복되는 부분을 찾아
최적화하는 것이다. 순서도를 보면 ‘입력창을 비우고 커서를 둔다’ 절차가 중복된다.
코드에서는 다음 부분이다.
$input.value = '';
$input.focus();
순서도에서는 중복되지 않고 코드에서만 중복되는 부분도 있다.
‘입력한 단어가 제시어가 된다’와 ‘다음 사람에게 순서를 넘긴다’ 절차에 해당하는 코드이다.
word = newWord;
$word.textContent = word;
const order = Number($order.textContent);
if (order + 1 > number) {
$order.textContent = 1;
} else {
$order.textContent = order + 1;
}
순서도에서 중복되는 절차는 다음과 같이 줄일 수 있다. 이 게임은 무한 반복되는 게임이므로 끝을
나타내는 표시는 필요하지 않다. 단어가 틀렸더라도 다시 입력할 기회를 주는 관대한 게임이다.
게임을 끝내고 싶다면 브라우저를 끄면 된다.

또한, ‘제시어가 비어 있는가?’와 ‘단어가 올바른가?’라는 절차는 ‘제시어가 비어 있거나 단어가 올바른가?’
라는 절차로 줄일 수 있다. 이처럼 판단하는 절차가 연이어 나오고 ‘예’나 ‘아니요’가 공통된 절차로 이어질
때에는 절차를 하나로 만들 수 있다. 여기서는 두 판단 절차 모두 ‘예’인 경우에 ‘입력한 단어가 제시어가
된다’라는 절차로 합쳐진다. 따라서 다음과 같이 하나로 만들면 된다.

합칠 수 있는지 아닌지는 다음과 같이 표를 만들어서 분석해 보면 도움이 된다.
▼ 표 3-1 합칠 수 있는 절차인지 판단하기
판단1 | 판단2 | 결과 |
제시어가 비어 있다 | 단어가 올바르다 | 입력한 단어가 제시어가 된다 |
제시어가 비어 있다 | 단어가 올바르지 않다 | 입력한 단어가 제시어가 된다 |
제시어가 비어 있지 않다 | 단어가 올바르다 | 입력한 단어가 제시어가 된다 |
제시어가 비어 있지 않다 | 단어가 올바르지 않다 | 틀렸다고 표시한다 |
제시어가 비어 있다면 단어가 올바른지 아닌지를 볼 필요가 없으므로 삭제선을 그어 두었다.
이때는 OR(자바스크립트에서는 ||) 관계가 있음을 파악할 수 있다.
앞의 표와 다음 표는 구성이 일치한다.
▼ 표 3-2 OR의 관계
첫 번째 조건 | 두 번째 조건 | 최종 결과 |
true | true | true |
true | false | true |
false | true | true |
false | false | false |
반대로 표의 분석 결과가 다음과 같이 나왔다면 AND(자바스크립트에서는 &&) 관계가 있는 판단문이다.
▼ 표 3-3 AND의 관계
첫 번째 조건 | 두 번째 조건 | 최종 결과 |
true | true | true |
true | false | false |
false | true | false |
false | false | false |
순서도의 절차가 적을수록 효율적인 프로그램이 된다.
최적화한 순서도를 코드에 반영하려면 onClick 함수에서 if 문의 순서를 잘 조정하면 다.
<script>
const number = Number(prompt('몇 명이 참가하나요?'));
const $button = document.querySelector('button');
const $input = document.querySelector('input');
const $word = document.querySelector('#word');
const $order = document.querySelector('#order');
let word; // 제시어
let newWord; // 현재 단어
const onClickButton = () => {
if (!word || word[word.length - 1] === newWord[0]) { // 제시어가 비어 있거나 올바른 단어인가?
word = newWord; // 입력한 단어가 제시어가 된다.
$word.textContent = word; // 화면에 제시어 표시
const order = Number($order.textContent);
if (order + 1 > number) {
$order.textContent = 1;
} else {
$order.textContent = order + 1;
}
} else { // 올바르지 않다.
alert('올바르지 않은 단어입니다!');
}
$input.value = '';
$input.focus();
};
const onInput = (event) => {
newWord = event.target.value; // 입력한 단어를 현재 단어로
};
$button.addEventListener('click', onClickButton);
$input.addEventListener('input', onInput);
코드가 상당히 짧아졌다.
OR의 관계가 있는 판단 절차 두 개를 하나로 줄였고(!word || word[word.length - 1] === newWord[0]),
다른 부분은 순서만 조정했다.
이때 절차 1이었던 ‘제시어가 비어 있는가?’는 절차 2인 ‘단어가 올바른가?’보다 우선순위가 높으므로
(절차 1을 절차2보다 먼저 판단한다.) 절차 1이 || 앞에 나와야 한다.
그리고 $input.value = ' '; 와 $input.focus(); 부분도 중복이 되므로 뒤쪽으로 빼서 한 번만 작성하는
것으로 변경했다.
지금까지 첫 번째 웹 게임인 끝말잇기를 구현해 봤다. 코드를 작성하는 것은 부가적인 일이고 순서도
작성이 훨씬 더 중요함을 느꼈을 것이다. 순서도가 명확하게 정리되지 않으면 코드도 엉망이 된다.
생각하는 대로 바로 코딩하지 못해서 답답하겠지만, 반드시 순서도를 그리는 습관을 들여야 한다.
습관이 되면 머릿속으로 순서도를 그릴 수 있게 되고, 결과적으로 코딩 속도가 빨라진다.
<1분 퀴즈>
다음 논리 연산의 결과는 무엇일까요?
① true && false
② false && true
③ false || true
④ true || false
힌트 AND와 OR 관계표 참조
정답
① true && false // false
② false && true // false
③ false || true // true
④ true || false // true
<마무리>
1. 순서도 그리기
: 프로그래밍에서 가장 중요한 것은 코드를 작성하기 전에 올바른 순서도를 만드는 것이다.
물론 한 번에 순서도를 완성할 수는 없고 코딩하면서 계속 수정해야 한다.
하지만 다음 원칙을 지키면서 순서도를 설계한다면 수정하는 횟수와 전체 절차 수를 최소화할 수 있다
(코딩 속도가 빨라진다).
1) 프로그램 절차의 수는 정해져 있어야 한다.
2) 각 절차는 항상 같은 내용이어야 한다.
3) 모든 가능성을 고려해야 한다.
4) 예시는 절차를 검증하는 데 사용한다.
순서도를 만들 때 사용자의 이벤트(버튼 클릭, 입력창 글자 입력 등)가 필요한 곳에서 순서도를 끊어야
함을 잊지 말아야 한다 !
2. 대화 상자 띄우기
: 웹 브라우저에 대화 상자는 다음 세 가지로 띄울 수 있다.
• prompt 대화 상자에 사용자가 입력한 메시지가 문자열 형태로 전달되고, 입력하지 않고 취소를 누르면
null이 전달됩니다.
• alert 단순한 알림창으로, 호출하면 확인을 누르기 전까지 다음 스크립트 실행이 중단된다.
디버깅 용도로 사용할 때는 console.log를 사용한다.
• confirm 사용자에게 확인을 받을 때 사용한다. 사용자가 확인을 누르면 true가 전달되고, 취소를 누르면
false가 전달된다.
각각의 사용 방법은 다음과 같다.
prompt('사용자에게 표시할 메시지');
alert('사용자에게 표시할 메시지');
confirm('사용자에게 표시할 메시지')
3. HTML 태그 선택하기
: document.querySelector로는 하나의 태그만 선택할 수 있고, document.querySelectorAll로는 여러 개의
태그를 선택할 수 있다.
document.querySelector('선택자')
document.querySelectorAll('선택자')
단순한 태그일 때 선택자는 태그의 이름이 된다.
예를 들어, 버튼 태그는 button이 선택자이다. 하나의 태그만 콕 찝어서 선택하고 싶을 때는 id 속성을
사용한다. 선택자로는 #아이디를 사용한다. 여러 개의 태그를 동시에 선택하고 싶을 때는 class 속성을,
선택자로는 .클래스를 사용한다.
document.querySelector('#아이디')
document.querySelectorAll('.클래스')
어떤 태그 안에 들어 있는 다른 태그를 선택하고 싶다면 선택자 사이를 한 칸 띄면 된다.
그러면 앞 선택자 안에 들어 있는 태그가 선택된다.
document.querySelector('선택자 내부선택자 내부선택자...');
4. 태그에 이벤트 달기
: 태그를 선택한 후에 addEventListener 메서드를 사용해 이벤트를 연결한다.
첫 번째 인수로 이벤트 이름을 넣고, 두 번째 인수로 리스너 함수를 넣는다. 리스너 함수는 이벤트가
발생할 때 실행되는 함수라고 생각하면 된다.
태그.addEventListener('이벤트 이름', 리스너함수);
리스너 함수의 매개변수로 event 객체를 제공해서 이벤트와 관련된 정보를 얻을 수 있다.
예를 들어, input 태그에 입력된 값을 가져오려면 event.target.value를 넣으면 된다.
여기서 event.target은 이벤트가 발생한 대상 태그를 가리킵니다.
const 리스너함수 = (event) => {
console.log(event.target.value);
};
입력창에 입력된 값은 value 속성으로 가져온다. value에 값을 대입하면 대입한 값으로 변경된다.
입력창.value // 입력창의 값을 가져옴
입력창.value = 값 // 입력창에 값을 넣음
입력 태그(input, select, textarea 등)가 아닌 일반 태그들의 내부 값을 가져올 때는 value가 아니라
textContent 속성을 사용합니다.
태그.textContent // 태그 내부의 문자를 가져옴
태그.textContent = 값 // 태그 내부의 문자를 해당 값으로 설정함
입력창이나 버튼의 경우 focus 메서드를 호출하면 해당 태그가 하이라이트된다.
입력창.focus() // 입력창을 하이라이트
5. 순서도 최적화하기
: 여러 개의 if 문을 하나로 합치려면 진리표를 활용한다. 두 if 문의 관계가 OR(||, 또는)인지
AND(&&, 그리고)인지에 따라 진리표가 달라진다.
▼ 표 3-4 OR의 관계
첫 번째 조건 | 두 번째 조건 | 최종 결과 |
True | true | true |
True | false | true |
false | true | true |
false | false | false |
▼ 표 3-5 AND의 관계
첫 번째 조건 | 두 번째 조건 | 최종 결과 |
true | true | true |
true | false | false |
false | true | false |
false | false | false |
<쿵쿵따 게임 만들기>
: 입력할 수 있는 단어를 세 글자로 고정하면 된다. 다만, 세 글자가 아니라면 다시 입력하라고 표시하도록
만들어보자. 또한, 초반에 prompt 함수를 사용해 몇 명이 참가할지를 선택할 때 사용자가 취소를 누르면
다음 코드가 실행되지 않게 처리하면 좋을 것 같다.
사용자가 input 이벤트를 발생시킬 때 입력한 글자가 세 글자인지 확인한다.
끝말잇기 게임과 조금 달라진 점은 순서도인데, 다음과 같다.
제시어가 비어 있는가 OR (단어가 올바른가 AND 단어가 세 글자인가)
참가자가 단어를 입력한 후 입력 버튼을 누를 때, 먼저 세 글자인지 검사하고 나서 그 단어가 올바른
단어인지 추가로 검사하면 된다. 판단하는 절차가 하나 들어가서 순서도는 다음과 같이 바뀐다.

판단문이 조금 복잡해졌다. 현재 판단문은 세 개의 조건으로 구성된다.
(1) 단어가 세 글자인가?
(2) 제시어가 비어 있는가?
(3) 입력한 단어가 올바른가?
한 가지 조심할 것은 1번 조건은 2, 3번 조건과 판단 기준이 다르다는 점이다.
세 글자가 아니면 뒤의 두 조건을 검사할 필요 없이 바로 ‘아니요’로 넘어가지만, 세 글자이면 제시어가
비어 있거나 입력한 단어가 올바른가라는 조건 중 하나 이상을 만족해야 ‘예’로 넘어갈 수 있다.
둘 다 만족하지 않으면 ‘아니요’로 넘어간다.
즉, 2번 조건과 3번 조건은 OR 관계이고, 1번 조건과 2, 3번 조건은 AND 관계이다.
이를 그대로 코드로 옮기면 된다. 괄호가 없으면 AND가 OR보다 우선순위가 높아 결과가 달라진다.
괄호 유무에 따라 결과가 달라지는 경우가 많으니 우선순위가 헷갈린다면 괄호를 적극 활용하기를
권장한다.
<script>
...
const onClickButton = () => {
if (newWord.length === 3 && (!word || [word.length - 1] === newWord[0]))
{ // 세 글자이면서 제시어가 비어 있거나 입력한 단어가 올바른가?
word = newWord; // 입력한 단어가 제시어가 된다.
$word.textContent = word; // 화면에 제시어 표시
const order = Number($order.textContent);
if (order + 1 > number) {
$order.textContent = 1;
} else {
$order.textContent = order + 1;
}
} else { // 올바르지 않다.
alert('올바르지 않은 단어입니다!');
}
$input.value = '';
$input.focus();
};
...
</script>
마지막으로, prompt 함수에서 취소를 누르면 다음 코드가 실행되지 않게 해야 한다.
생각보다 간단하다.
<script>
const number = Number(prompt('몇 명이 참가하나요?'));
if (number) {
const $button = document.querySelector('button');
const $input = document.querySelector('input');
...
$button.addEventListener('click', onClickButton);
$input.addEventListener('input', onInput);
}
</script>
이처럼 number 값에 따라 if 문으로 나머지 코드를 감싸면 된다.
prompt 함수에서 취소를 눌렀다면 값이 null이 될 것이고, 그 값이 Number 함수에 들어가면 NaN이
된다. NaN은 if 문에 들어가면 항상 false로 취급되므로 number가 null이면 if 문 내부는 실행되지 않는다.