본문 바로가기

공부기록용

[ javascript ] 끝말잇기 게임 프로그래밍

slice()

원본 배열은 바뀌지 않고, 지정한 배열에서 시작 인덱스부터 끝 인덱스에 해당하는 얕은 복사본을 배열로 반환한다.

The slice() method of Array. instances returns a shallow copy of a portion of an array into a new array object selected from start to end(end not included) where start and end represents index of items in that array. The origin array will not be modified. 

***얕은 복사: 참조(주소) 값을 복사하기 때문에 메모리를 공유한다 → 원본 배열 또는 얕은 복사본의 배열중 하나의 객체를 변경한다면(두 배열이 가리키는 참조값이 동일한 객체를 참조하므로[가리키므로]) 두 배열이 가리키는 객체가 지니는 값이 동일하게 된다.

--> 객체 참조의 경우(객체 참조는 heap영역에 만들어진 객체를 가리키는 주소 값을 stack영역의 변수에 보관하고 있고, 그 stack영역의 변수의 값을 참조하는[가리키는] 것) slice()는 객체 참조를 새 배열로 복사한다. 원본 배열과 새 배열은 모두 동일한 객체를 참조한다. 참조된 객체가 변경되면 변경 내용은 새 배열과 원본 배열 모두에서 볼 수 있다.

- 배열의 인덱스는 0부터 시작, 시작 인덱스는 0부터 지정이 가능, 시작 인덱스는 포함, 끝 인덱스는 포함하지 않음

- slice() 함수에 음수를 매개 값으로 전달할 수 있는데 -1은 해당 배열의 마지막 인덱스에 해당하는 요소를 반환한다.

--> 배열(명).slice(-2)는 해당 배열에서 마지막 두 개의 엘리먼트(요소)를 추출한다[반환한다]

- slice() 매개 값으로 1. 아무것도 전달하지 않을 경우 2. 0을 전달할 경우 3. begin이 undefined인 경우에

0번 인덱스부터 끝 인덱스에 해당하는 모든 요소를 배열로 반환한다.

***모든 함수는 return undefined; 가 기본 값이다.

- bigin이 배열의 길이보다 긴 경우에 빈 배열을 반환한다.

- end 생략시 배열의 끝까지(arr.length) 추출하여 반환한다.

- slice() 함수는 몇 번째 인덱스(시작 인덱스)부터 몇 번째 인덱스까지(끝 인덱스, 끝 인덱스는 포함하지 않음)에 해당하는 요소들을 반환하는 것밖에 못하는 것 같음. 예를 들어 몇 번째 인덱스를 시작점으로 몇 개의 요소를 반환하는 기능은 없는 것 같다.

- const word = '제로초다'; word변수에 담긴 문자열의 뒤에서 세 번째 글자를 가져오려면 slice() 함수를 이용할 때

뒤에서 세 번째 글자부터(시작 인덱스) 문자열의 끝까지 모든 요소를 반환한뒤 해당 배열의 첫 번째 요소인 0번째 인덱스를 반환하면 '로'를 반환받을 수 있다 → 1. slice(-3)[0]; 또는 2.  word[word.length-3]

slice() 함수는 문자열과 배열에 사용할 수 있는데, splice()함수는 배열에만 사용이 가능하다 → String.prototype.slice(), Array.prototype.slice(), Array.prototype.splice(), String.prototype.substring()

***String.prototype.substring(): String 객체의 시작 인덱스부터 끝 인덱스까지(끝 인덱스는 포함하지 않음)에 해당하는 문자열의 부분을 반환한다. The substring() and slice() methods are almost identical, but there are a couple of subtle differences between the two, especially in the way negative arguments are dealt with.

substring() 메서드는 만약 시작 인덱스가 끝 인덱스보다 크다면 두 인자를 바꾼다. slice()는 그렇지 않다.

예를 들어 const text = 'hello'; text.substring(4, 2); 의 결과는 ll이다. 시작 인덱스가(4) 끝 인덱스보다(2) 크기 때문에 두 인자를 내부적으로 바꾸어서 text.substring(2,4); 가 되고 이 메서드가 동작되어 ll을 반환하는 것이다. slice의 경우 빈 문자열을 반환한다.

매개변수로 전달한 인자중 다른 하나 또는 둘다 negative 또는 NaN일 경우 substring() 메서드는 그것들을(-2, -1 등 음수) 0으로 다룬다. slice() 메서드 또한 NaN 인자를 0처럼 다룬다. 그런데 slice() 메서드는 음의 정수 인자를 받으므로 만약 음의 정수를 통해 인덱스를 찾는다면 그 인자로부터 해당하는 인덱스들의 요소를 반환한다. 

- substring() 메서드 또한 원본(String) 값은 바뀌지 않는다.

 

형식

arr.slice([begin[, end]])

 

------------------------------------------------------------------------

 

문자열에서 n번째(ex. 3번째)에 해당하는 글자를 가져오기

- 문자열에서 n번째에 해당하는 글자를 가져오는 실습에는 slice(), substring()메서드를 사용할 수 있다.

→ String.prototype.slice(), String.prototype.substring()

- 예를 들어 const text = 'hello goodmorning'; 이라고 변수 선언(text) 및 값을 초기화('hello goodmorning') 했을 때

text가 지니는 문자열중 5번째에 해당하는 문자를 추출하는 실습을 해보자면

1. text.slice(4, 5);

2. text.substring(4, 5);

3. text.slice(0, 5).slice(-1);

여러 가지 방법으로 접근할 수 있다.

 

------------------------------------------------------------------------

 

끝말잇기 게임 순서도 작성하기

- 시작 → 참가자 수를 정한다 → 참가자 순서를 정한다 → 대기

- 입력(이벤트 발생) → 제시어가 비어있는가? ( 첫 번째 참가자인가? ) 또는 입력이 올바른가?

   --- 예 → 입력받은 단어가 제시어가 된다 → 다음 사람에게 순서를 넘긴다 → 대기

   --- 아니오 → 틀렸다고 알려준다 → 끝   

 

입력버튼을 클릭한 것과 입력 창에 단어를 입력한 것은 별개

- 입력 창에 단어를 입력 → 입력한 단어를 저장한다 → 대기

 

------------------------------------------------------------------------

 

쿵쿵따 게임 순서도 작성하기

 

조건

- 세글자만 입력할 수 있다.

- 몇 명이 참가할지 사용자의 입력을 받을 때(prompt 함수) 사용자가 취소를 누르면 다음 코드가 실행되지 않게 처리한다.

 

순서도 작성하면서 복잡했던 여러 경우의 수를 정리

 

값(단어)을 입력했는가?

 └ ---아니오→ 입력한 값이 비었다고 알림한다.

 └ ---예

 

제시어가 비어있는가? 

 └ ---예 → 첫 번째 참가자로 순번을 정한다 그리고 입력한 값이 3글자가 맞는지 ---아니오→ 3글자를 입력해야 한다고 알림한다

 └ ---예 → 첫 번째 참가자로 순번을 정한다 그리고 입력한 값이 3글자가 맞는지 ---예 → 제시어로 등록한다

 └ 아니오 → 입력한 값이 3글자인지를 확인 ---아니오 → 3글자를 입력해야 한다고 알림한다

 └ 아니오 → 입력한 값이 3글자인지를 확인 ---예 → 기존 제시어의 끝 글자와 입력한 첫 글자가 일치한지 ---아니오 → 기존 제시어의 끝 글자와 입력한 첫 글자가 일치하지 않는다고 알림한다

 └ 아니오 → 입력한 값이 3글자인지를 확인 ---예 → 기존 제시어의 끝 글자와 입력한 첫 글자가 일치한지 ---예 → 제시어로 등록한다

제로초님이 작성한 것은 → 제시어가 비어 있는가? OR ( 단어가 올바른가 AND 단어가 세 글자인가 )

*** OR보다 AND의 우선순위가 높다

 

순서

제시어가 비어있을 때 입력한 사람이 첫 번째 참가자가 된다

제시어가 등록됐을 때 현재 순번에 +1을 더한 값이 총 참가자 수보다 많다면

1로 리셋해주고, 아니라면 현재 순번에 +1을 더한 값이 다음 참가자의 순번이 된다.

 

 

 

 

 

 

                                                                                                                  입력창에 입력 → 입력값을 변수에 대입 ┐

시작  →  참가자를 입력했는가? && 참가자가 1명 이상인가?   --예 → 참가자 수를 상수에 대입한다 → 대기

                └아니오 → 참가자 수는 1명 이상이어야 한다고 알림한다                 

 

버튼 클릭 → 제시어가 존재하는가? ---예 → 입력한 값이 3글자인가? ---예→ 제시어 끝 글자와 입력값의 첫 글자가 일치하는가?

버튼 클릭 → 제시어 존재 ---예→ 입력값이 3글자인가? && 제시어 끝 글자와 입력값의 첫 글자가 일치 --예--- 

                             │                          └아니오→ 3글자로 작성하라고 알림          └아니오→ 불일치 알림

                             │제시어가 존재하지 않는데 입력값이 3글자 → 입력값이 제시어가 된다 

                                   └아니오(→ 첫 번째 참가자) → 입력값이 3글자인가? ---예 → 입력값이 제시어가 된다

                                                                                                       └아니오→ 3글자로 작성해야 한다고 알림한다

 

→ 입력한 값이 제시어가 된다 → 다음 참가자에게 순서를 넘겨준다 → 대기

                                                            └현재 순번에 +1 연산을 했을 때 총 참가자의 수보다 큰가? ---예→ 다음 참가자의 순번을 1로 리셋

                                                                 └아니오→ 현재 순번에 +1을 연산한 값이 다음 참가자의 순번이 된다

 

*제시어의 존재유무에 상관없이 입력한 값이 3글자인지는 확인해야함

 

------------------------------------------------------------------------

 

일단 한 번은 동작해야하는 경우라면 do~while문을 이용하는 것이 좋다.

do~while문으로 총 몇 명이 참가할 것인지 결과를 전달받고,조건식: 만약 입력받은 수 또는 +prompt() 창이 떴을 때 취소를 눌렀다면, 0이하라면→ 참여자가 1명 이상이어야 합니다 라고 알림한 뒤 참여자 수 입력받기를 반복한다.

 

------------------------------------------------------------------------

 

끝말잇기 게임 프로그래밍

 

1. const number = prompt('몇 명이 참가하나요?');

사용자로부터 입력 값을 받을 때 prompt()함수를 사용한다. prompt()함수에 전달한 문자열이 사용자에게 보여지는 메시지다.

메시지는 사용자가 무슨 값을 입력해야하는지 판단할 수 있는 기준이 된다. prompt()함수는 기본적으로 문자열을 반환하는데

숫자를 반환받으려면 prompt()함수로부터 반환받은 값의 타입을 숫자로 형변환 해주면 된다. 문자열을 숫자형으로 변환하는 방법은

3가지가 있다.

- parseInt(prompt()): 문자열이 숫자로 구성되거나(ex. '1234') 첫 글자가 숫자로 시작한다면(ex. 3명) 숫자타입으로 값을 반환한다.

- Number(prompt()): 문자열이 숫자로만 구성되어야 숫자타입의 값을 반환한다. 만약 '3명'이라는 값을 가진 문자열이라면 NaN을 반환한다.

- +prompt(): prompt()함수를 Number()함수로 감싸서 형변환을 한 결과와 같다. 숫자로만 이루어진 문자열을 숫자타입으로 변환하여 값을 반환한다. 만약 '3명'이라는 문자열인 경우 NaN이 반환된다.

parseInt(), Number(), + 모두 1개 이상의 빈 공백 다음에 숫자를 입력하면(단, 숫자로만 구성된 문자열일 경우) 공백에 상관 없이 숫자를 반환한다. 그런데 숫자 사이에 공백이 있어선 안된다. parseInt()의 경우 숫자 사이에 공백이 존재할 경우 첫 번째 숫자를 반환하고, Number()와 +의 경우 NaN을 반환한다. parseInt() 함수는 매개변수로 전달된 문자열이 앞에 빈 공백을 제외하고 시작 값이 숫자라면 첫 숫자 값만 반환한다. ex. parseInt('                                                    4명             '); → 숫자타입 4가 반환된다.

취소버튼을 눌렀을 때 parseInt()는 NaN을 반환하고, Number()와 +는 0을 반환한다.

 

2. 알림창을 띄우는 함수

- prompt()는 사용자로부터 입력값을 전달받을 때 사용하고, 확인과 취소버튼을 제공한다.

- alert()는 사용자에게 경고 메시지를 표시할 때 사용하는 함수로 확인버튼만 제공한다.

- confirm()은 사용자에게 확인을 요구하는 창으로(ex. ~ 작성하셨는데 맞는지 한 번 더 확인하는) 확인과 취소 버튼을 제공하고,

확인을 눌렀다면 true를 취소를 눌렀다면 false를 반환한다.

 

3. html태그 선택하기 → html태그의 선택은 javascript언어를 사용해서 화면의 변화를 주기 위한 밑작업

document.querySelector('선택자'); → 선택자는 html태그를 선택할 수 있는 특수한 문자열이다.

예를 들어 const $input = document.querySelector('input');

- html에서 태그를 선택해서 변수에 대입할 때는 변수명 앞에 $(달러기호)를 붙인다.

- 위의 코드는 input요소를 $input이라는 변수명에 대입하는 것이다.

그래서 console.log($input); 이렇게 콘솔에 찍으면 대입되어 있는(선택한) input태그를 알 수 있다.

 

여러 개의 태그를 동시에 선택할 수 있는데 변수명 앞에는 달러기호를 두 개($$) 붙여주고,

querySelectorAll() 함수를 사용하여 const $$input = document.querySelectorAll('input'); 이렇게 작성해준다.

html문서 내에 있는 여러 개의 input 요소들이 $$input 변수에 대입되어 있어 $$input 변수를 통해

모든 input 요소들을 조작할 수 있다.

→ $$input변수를 콘솔에 찍으면 여러 개의 input 요소가 찍히는데, 이는 배열처럼 보이지만 배열이 아닌 유사배열이다.

유사배열인 객체 NodeList가 반환되는 것. ***유사배열인 NodeList와 arguments

 

const $input = document.querySelector('input');

html문서에서 여러 개의 input요소 중 가장 첫 번째로 만나는 input 요소가 $input 변수에 대입되는데

만약 html문서에서 3번 째의 input요소를 담고 싶다면 해당 태그에 id라는 속성을 작성하면 해당 id값으로

특정 input요소를 지목할 수 있다. 예를 들면

 

id는 자바스크립트의 입장에서 고유한 값으로 인정함(html 문서에 동일한 id값을 작성했다면 html문서에서는 문제가 없지만

자바스크립트에서는 에러가 발생함으로 id의 값은 고유해야 하며 동일한 값을 입력하지 않아야 한다)

<span id="word"> </span>

위 태그를 자바스크립트에서 딱 지목하려면 id값으로 지목할 수 있다.

const $span = document.querySelector('#word');

id를 선택자로 작성하는 경우 #(샵기호) 뒤에 id의 값을 작성해야 한다.

선택자를 작성할 때 'span#word' 이렇게 작성할 수 있는데, 이는 id가 word인 span태그를 지목하는 것이다.

 

class라는 속성도 있는데 class값을 선택자로 작성할 때는 .뒤에 클래스명을 작성한다.

예를 들어

<button class="btn">버튼1</button>

<button class="btn">버튼2</button>

<button class="btn">버튼3</button>

 

버튼1, 버튼2, 버튼3의 요소를 모두 선택하려면 class값을 이용할 수 있다.

요소를 여러 개 선택하려면 querySelectorAll(), 그리고 그 요소들을 한 번에 지목할 수 있는 class값을 이용하면 된다.

const $$buttons = document.querySelectorAll('.btn'); // 또는 선택자를 'button.btn' 이렇게 작성해도 된다.

 

id를 가진 태그를 선택할 때: document.querySelector('#아이디값');

class를 가진 태그를 선택할 때: document.querySelector('.클래스값');

여러 개의 태그를 지목할 때 document.querySelectorAll('선택자') 함수를 사용하고,

지목할 복수의 태그들에 class를 부여해서 동일한 class의 값을 준다. 그리고 선택자에 동일한 class의 값을 작성해준다.

 

선택자에 태그와 태그 사이에 공백이 있다면 자손 태그를 의미한다.

예를 들어 const $span = document.querySelector('div span');

이렇게 작성한 코드는 div의 자손인 span태그를 찾아서 $span 변수에 대입하라는 것이다.

자손과 자식의 차이점은 태그 바로 안에 있는 것이 자식이고, 자손은 지목한 해당 태그 아래에 있는 지목한 모든 태그들을 의미한다.

div의 바로 자식인 span만 찾는 것은 'div > span' 이렇게 작성하면 되고,

'div span' 이렇게 작성했다면 div 아래에 있는 모든 span 태그를 찾는 것을 의미한다.

 

*** 선택자가 너무 길어진다면 id를 부여하자

 

a 태그 안에 id가 b인 태그 안에 class가 c인 태그를 선택하려고 할 때 선택자 작성하기 → 'a #id .c'

 

4. 이벤트 리스너 함수

사용자가 보여지는 화면에서 입력창(input태그)에 입력을 하거나 버튼을 클릭하는 등을 이벤트가 발생했다 혹은 사건이 일어났다라고 표현한다 → (프로그래밍한 시스템에서 일어나는) 프로그램에서 일어나는 action(사건) 또는 occurrence(발생)

사용자가 입력 창에 입력을 하거나 버튼을 클릭했을 때 그 다음 어떤 일을 발생하게 할지, 어떻게 응답할지에 대해 프로그래밍할 수 있다.

 

입력 창에 입력이 발생한다면 어떤 동작을 발생하도록 설계할 때

1) 먼저 동작이 어디에서 발생했는지에 대해 파악을 해야하니 해당 요소를 얻는다.

const $input = document.querySelector('#word');

id가 word인 요소를 $input변수에 대입한다.

그리고 $input변수에 대입된 요소에 입력이벤트가 발생한다면 어떤 함수를 작동하겠다는 코드를 작성한다.

$input.addEventListener('input', function() {

 // input요소에 입력이 발생한 뒤에 사용자에게 어떤 응답을 할지를 적는다.

 // 여기서 function은 이름이 없어서 익명함수라고도 하고,

 // addEventListener()함수의 두 번째 인자로 전달하는 리스너 함수라고 한다.

 // 또 input이라는 이벤트가 발생한 후에 작동하는 함수로써 이벤트 함수라고도 한다.

});

 

addEventListener() 함수에 두 번째 매개변수로 전달한 함수는 이렇게 익명함수로도 작성할 수 있고 function() { },

화살표 함수로도 작성할 수 있고 () => {}, 혹은 외부에 작성한 함수 표현식을 사용해도 된다.

함수 표현식은 변수에 함수를 대입함으로써 해당 변수명을 적기만 하면 된다.

주의할 점은 변수명 뒤에 ()괄호를 적게되면 모든 함수의 반환 값은 undefined이므로

return값이 addEventListener() 함수의 두 번째 인자로 전달되면서 함수가 작동하지 않게된다.

이것이 주의할 점이다.

 

5. 태그가 가지는 내용물 얻기 그리고 대입하기

input요소의 값을 얻어서 input에 입력된 값을 확인하기

const $input = document.querySelector('input');

$input.value; → input요소의 경우 속성인 value로 접근해야 한다.

 

const $span = document.querySelector('span');

$span.textContent;

span요소의 경우 textConte 속성으로 접근해야 한다.

 

span요소가 가지는 내용물에 값을 대입하기 → word라는 변수에 저장된 값을 $span.textContent에 대입하면

$span.textContent = word;

<span>이자리에 word가 저장하고 있는 값이 들어온다</span>

 

6. input요소의 값을 비우고 커서를 깜빡거리게 하기

const $input = document.querySelector('input');

 

// input창에 입력된 값을 비우고

$input.value = '';

 

// input창에 커서 깜빡거리게 하기

$input.focus();

 

*** 태그들의 내부 값을 가져올 때 사용하는 속성

뭔가를 입력하는 태그들은, 예를 들면 input, textarea, select → value 속성을 사용하여 해당 태그들의 내부의 값에 접근한다.

span, div, button 태그들의 내부의 값에 접근할 때는 textContent 속성을 사용한다.

 

-------------------------------------------------------------------------------------------------------------------

 

끝말잇기 게임 순서도

 

                                                                                                                                            단어 입력(input이벤트 발생) → 입력 값을 변수에 저장

시작 → 몇 명이 참여할지 입력했는가 --예→ 1명 이상을 입력했는가? ---예→ 참가자 순서 정하기 → 제시어 저장할 변수 선언 → 대기 

              │                                                              └아니오→1명 이상을 입력해야 한다고 알림한다

              └ 아니오→ 참여자 수를 입력하지 않았다고 알림한다

 

입력버튼(이벤트 발생) → 제시어가 비어있는가? ---예→ 입력된 단어가 제시어가 된다 → 다음 참가자에게 순서를 넘긴다

                                               │                                                                       ↑

                                               │                                                                       예

                                               │                                                                       │

                                               └ 아니오 → 입력 값이 올바른가? (기존 제시어의 끝 글자와 입력 값의 첫 글자가 일치하는가?)

                                                                     └ 아니오 → 입력 값이 올바르지 않다고 알림한다                                                

 

 

다음 참가자에게 순서를 넘겨준다.

현재 순서를 파악한다 → 현재 순서값에 +1 연산한 값이 총 참가자수보다 많은가? ---예→ 다음 참가자 순번을 1로 리셋한다

                                            └ 아니오→ 기존 순서값에 +1 연산한 값이 다음 참가자의 순번이 된다 

 

 

```

 

현재 순서 파악하기

// id가 order인 요소를 얻기

// 만약 얻은 요소가 span이라면 span태그가 감싸는 내용물은 textContent로 접근할 수 있다.

const $order = document.querySelector('#order'); 

 

// $order.textContent로 얻은 값은 문자열이다. 그래서 문자열을 숫자타입으로 변환해주어야 한다.

const order = parseInt($order.textContent)

 

// 현재 순서에 1을 더한 값이 number가 가지는 값보다 큰가?

// 여기서 number는 총 참가자 수를 의미한다.

if(order+1 > number) {

 // 현재 순서에 1을 더한 값이 총 참가자 수보다 크다면

 // 다음 참가자의 순번은 1로 리셋한다

 $order.textContent = 1;

} else {

 $order.textContent = order+1;

}

 

```

-------------------------------------------------------------------------------------------------------------------

 

오늘 제로초님 자바스크립트를 보며 코딩한 끝말잇기 게임 실습

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
</head>

<body>
	<div><span id="order">1</span>번째 참가자</div>
	<div>제시어: <span id="word"></span></div>
	<input type="text" />
	<button id="btn">입력</button>


	<script>
		const number = parseInt(prompt('몇 명이 참가하나요?'), 10);
		const $button = document.querySelector('button'); // 입력버튼
		const $input = document.querySelector('input'); // 입력요소
		const $word = document.querySelector('#word'); // 제시어를 띄울 span 요소
		const $order = document.querySelector('#order');


		let word; // 제시어를 담을 변수를 선언
		let newWord; // 입력 값을 담을 변수 선언

		const onClickButton = () => {
			if (!word) {
				// 제시어가 비어있다면

				word = newWord;
				$word.textContent = word;
				$input.value = '';
				// 입력창을 비운뒤 커서를 둔다
				$input.focus();

				const order = Number($order.textContent);
				if (order + 1 > number) {
					// 현재 순번에 1을 더한 값이 총 참가자 수보다 많다면
					// 다음 참가자의 순번에는 1을 대입한다
					$order.textContent = 1;
				} else {
					$order.textContent = order + 1;
				}
			} else {
				// 제시어가 비어있지 않다면
				// 문자열의 마지막 글자와 첫 글자를 length로 접근할 수 있다.
				// 
				if (word[word.length - 1] === newWord[0]) {

					word = newWord
					$word.textContent = newWord;
					$input.value = '';
					$input.focus();

					const order = Number($order.textContent);
					if (order + 1 > number) {
						// 현재 순번에 1을 더한 값이 총 참가자 수보다 많다면
						// 다음 참가자의 순번에는 1을 대입한다
						$order.textContent = 1;
					} else {
						$order.textContent = order + 1;
					}

				} else {
					alert(`기존 제시어: ${word}\n입력한 값: ${newWord}\n기존 제시어의 끝 글자인 '${word[word.length-1]}'과\n입력한 값의 첫 글자인 '${newWord[0]}'가 일치하지 않습니다.`);
				}


			}



		}
		const onInput = (event) => {
			newWord = event.target.value;
		}

		$button.addEventListener('click', onClickButton);
		$input.addEventListener('input', onInput);
	</script>


</body>

</html>

 

-------------------------------------------------------------------------------------------------------------------

 

참고 강의

[유튜브] ZeroCho TV - 자바스크립트 강좌