본문 바로가기

Web Development/Front-end

[Zerocho-11] 틱택토 심화 ( 스코프 문제를 해결하는 3가지 방법 )

안녕하세요 !

틱택토에 컴퓨터 턴을 만들고, 무승부를 처리하는 코드를 추가했습니다.

그리고 이 과정에서 지난 시간에 다룬 스코프 문제를 해결하는 방법 3가지를 알게되었는데요.

이 부분에 대해 다뤄보고자 합니다.

 

1. 변수를 위로 빼는 방법 (스코프 문제 해결법 1)

우선 아래와 같은 코드가 있다고 가정합시다.

const scope = () => {
	let a = 'monkey';
}
scope();
console.log(a);

당연히 scope함수 내에 있기에, 스코프 문제가 발생할 것이고, console.log(a)함수는 a변수를 읽지 못할 것 입니다.

그럼 어떻게 해결할 수 있을까요?

let a;
const scope = () => {
    a = 'lion';
}

scope();
console.log(a);

이런식으로, 변수를 위에서 선언해주고, 그럼 a는 undefined라는 디폴트값(초기값)이 자동으로 설정됩니다.

함수를 실행하면, a = 'lion'이라는 값으로 재선언됩니다.

그리고, console.log(a);를 해본다면, 첫번째 줄의 let a; << 이 변수를 읽어올 것이고,

let a << 변수는 scope함수를 실행함으로써 lion으로 변했겠죠?

그래서, a의 값은 "lion"으로 출력되게 됩니다.

 

즉, 함수내 선언한 변수(함수스코프 내 변수)를 외부에서 사용하는 방법은,

이런식으로, 변수를 외부에서 선언하고, scope()함수 내에서 재선언하는 방식으로 사용할 수 있습니다.

*단 함수를 실행하지않으면, 재선언과정이 없으므로, a값을 출력했을 때, 디폴트값인 undefined로 출력되게 됩니다.

 

2. 변수를 return 값으로 (스코프 문제 해결법 2)

위와 같은 문제의 코드를 가져와봅시다.

const scope = () => {
	let a = 'monkey';
}
scope();
console.log(a);

역시나, a를 읽을 수 없을 것입니다.

그럼 return값을 활용해 해결해봅시다.

const scope = () => {
    let a = 'lion';
    return a; //함수의 리턴값으로 a를 부여함.
}

let a = scope(); //함수를 호출할 때 동시에 이를 부를 변수의 값으로 할당해줌.
console.log(a);

우선 함수의 리턴값으로, a변수를 줍니다.

그리고 함수를 호출할 때, 함수의 리턴값으로 준 변수명을 그대로 변수로 선언하면서 동시에 함수를 호출합니다.

그럼, 함수에서 리턴해준 변수가 있으니, 그 변수가 아래에 변수로 할당되게 되겠죠?

그리고나서 console.log()함수에서 a를 호출하게되면, 리턴값으로 받아진 a값이 그대로, 출력될 수 있겠죠?

 

3. 매개변수 (스코프 문제 해결법 3)

const one = () => {
    return a + b;
}

const two = () => {
    let a = 1;
    let b = 2;
}

 

이렇게 함수 두개가 있다고 가정합시다.

one 함수는 a와 b값을 더해주는 더하기 함수입니다. 그리고 two함수는 a변수와 b변수값을 가진 함수죠.

그럼 one 함수에서 a + b 값을 두개 다 가져가서 더하고, 콘솔에 출력하려면 어떻게 해야할까요?

 

 

const one = (a, b) => {
    return a + b;
}

const two = () => {
    let a = 1;
    let b = 2;
    return one(a, b);
}

console.log(two());

two에서 리턴으로 one 함수를 호출하고, 인자값으로 a, b값을 전달한 뒤에, one함수에서 매개변수로 이를 받으면 가능합니다.

그럼 two함수를 호출하면, a변수와 b변수를 만들고, one함수에 a, b를 담아서 호출합니다. 그럼, one()함수는 더하기를 실행하고 다시 two()함수에게 돌려줘서 이 더한 값을 리턴하게 되겟죠? 이런식으로 해결하는 방법도 있습니다.

 

 

 

 

4. 틱택토 게임 영상 (컴퓨터 턴 & 무승부 구현)

틱택토 게임 개량판

 

 

5. 틱택토 게임(심화) 코드

tic.js

var body = document.body;
var table = document.createElement('table');
var blanks = [];
var lines = [];
var turn = 'X';

//스코프를 해결하는 세번째 방법. 매개변수로 잡아준다.
function 결과체크(which_line, which_blank){
    //가로줄 검사
    var finished = false;
    if(blanks[which_line][0].textContent === turn &&
    blanks[which_line][1].textContent === turn &&
    blanks[which_line][2].textContent === turn
    ){
        finished = true;
    }
    //세로줄 검사
    if(blanks[0][which_blank].textContent === turn &&
    blanks[1][which_blank].textContent === turn &&
    blanks[2][which_blank].textContent === turn 
    ){
        finished = true;  
    }
    //좌상->우하 대각선 검사
    if(blanks[0][0].textContent === turn &&
    blanks[1][1].textContent === turn &&
    blanks[2][2].textContent === turn  
    ){
        finished = true;
    }
    //우상->좌하 대각선 검사
    if(blanks[0][2].textContent === turn &&
        blanks[1][1].textContent === turn &&
        blanks[2][0].textContent === turn  
        ){
        finished = true;
    }
    return finished; //스코프를 해결하는 두번째 방법. (return으로 새로 등장한 변수를 넣어줌.)
}

function 초기화(무승부){
    //무승부일 때, 구현.
    if(무승부){
        finished_result.textContent = `무승부입니다 !`;
    } else {
        finished_result.textContent = `${turn} 님이 승리했습니다 !`;
    }
    setTimeout(()=>{
        finished_result.textContent = '';
        blanks.forEach((line)=>{
            line.forEach((blank)=>{
                blank.textContent = '';
            })
        })
        turn = 'X';
    }, 1500);
}

//게임완료 문구를 만들 태그생성 후, 바디에 넣어줌
var finished_result = document.createElement('h3');
document.body.append(finished_result);

var back = (e) => {
    //컴퓨터 턴일 때 노터치.
    if(turn === 'O'){
        return;
    }
    var which_line = lines.indexOf(e.target.parentNode);
    var which_blank = blanks[which_line].indexOf(event.target);
    console.log(`몇줄 => ${which_line} && 몇칸 => ${which_blank}`);
    
    //빈칸일 때, 칸을 X or O 를 기입함.
    if(blanks[which_line][which_blank].textContent === ''){
    blanks[which_line][which_blank].textContent = turn;
    //스코프를 해결하는 세번째 방법. 매개변수로 잡아준다. 그리고, 호출하는 부분에서 인자를 줘서 해결.
    var finished = 결과체크(which_line, which_blank); //스코프를 해결하는 두번째 방법. (return으로 새로 등장한 변수를 넣어줌.)
    //게임이 끝났는지 확인하는 코드, 여기에 턴을 넘기는 코드를 포함했습니다.
    //게임이 끝났다면 승리문구와, 초기화함.
    var 후보칸 = [];
    blanks.forEach((line)=>{
        line.forEach((blank)=>{
            후보칸.push(blank);
        });
    });
    후보칸 = 후보칸.filter((blank) => !blank.textContent);
    if(finished){
        초기화(); // 비워두면 undefined 처리됨 =>> false 
    } else if(후보칸.length === 0){ //칸이 더이상 없으면, 무승부처리.
        초기화(true);
    } else { //턴을 넘기는 코드.
        if(turn === 'X'){
            turn = 'O';
        }
        //컴퓨터의 턴
        setTimeout(()=>{
            console.log(`컴퓨터의 턴입니다!`);
            //비어있는 칸 중 하나를 고른다.
            var 선택칸 = 후보칸[Math.floor(Math.random() * 후보칸.length)];
            선택칸.textContent = 'O';
            //컴퓨터가 승리했는지 체크
            var which_line = lines.indexOf(선택칸.parentNode);
            var which_blank = blanks[which_line].indexOf(선택칸);
            var finished = 결과체크(which_line, which_blank);//스코프를 해결하는 세번째 방법. 매개변수로 잡아준다. 그리고, 호출하는 부분에서 인자를 줘서 해결.
            //다 찻으면;
            //게임이 끝났는지 확인하는 코드, 여기에 턴을 넘기는 코드를 포함했습니다.
            //게임이 끝났다면 승리문구와, 초기화함.
            if(finished){
                초기화();
            }
            //턴을 나한테 넘긴다.
            turn = 'X';
        }, 1000);
    }
} else {
    console.log('칸이 이미 차있습니다.');
}
}


//3 X 3 칸을 생성하고, 이벤트리스너를 달아, 클릭이벤트 지정.(back함수)
for(var i=1; i<=3; i+=1){
    var line = document.createElement('tr');
    lines.push(line);
    blanks.push([]);
    for(var j=1; j<=3; j+=1){
        var blank = document.createElement('td');
        blanks[i-1].push(blank);
        blank.addEventListener('click', back);
        line.appendChild(blank);
    }
    table.appendChild(line);
}
body.appendChild(table);

컴퓨터의 턴 코드를 작성하고, 무승부 시 동작할 코드를 추가했습니다.

그리고 반복되는 코드는 함수로 빼서 리팩토링 해준 뒤,

리팩토링시 발생하는 스코프 문제를 해결하기 위해 위 3가지 스코프 문제해결방법을 사용해 해결했습니다.