/ TYPESCRIPT

TypeScript 강좌(4) - 변수 선언

TypeScript 강좌는 여러 절로 구성되어 있습니다.


TypeScript Variable Declarations

이번 포스트에서는 var, let, const에 대해서 알아보겠습니다. let은 var과 유사하지만 var이 가지고 있는 우리에게 익숙치 않은 몇가지 사항들을 피하도록 도와줍니다. const는 이름에서 의미하다시피 변수에 재할당을 금지하는 목적으로 사용하는 것이구요.

결론을 미리 말하자면 TypeScriptletconst를 이용합니다.

먼저 var에 대해서 알아보고 그 다음에 let, const에 대해서 알아보도록 하겠습니다.


var

코드로 설명하자면 다음과 같이 변수를 선언할 수 있습니다.

var myVar = 100;

당연한 말이지만 함수내에서도 선언할 수 있습니다.

function myFunc() {
    var message = "Hello World";

    return message
}

JavaScript는 함수안에 다른 함수를 선언할 수 있습니다. 그러한 경우 내부 함수가 외부 함수가 가지고 있는 변수를 참조할 수 있습니다. 또한 JavaScript는 1급 함수(first-class function)를 지원합니다. 쉽게 말하면 JavaScript의 함수는 값의 의미로 사용될 수 있다는 것입니다. 함수가 값의 역할을 하다보니 다음과 같은 형태의 코드가 가능합니다.

  • 함수를 변수에 저장.
  • 함수를 다른 함수의 인자로 전달.
  • 함수를 다른 함수의 리턴값으로 사용.

위와 같은 형태의 함수를 이용할 때는 함수의 이름이 특별히 필요하지 않기 때문에 이런 함수는 특별히 이름을 명시하지 않고 사용합니다. 이런 함수를 우리는 익명함수( Anonymous function )라고 하죠. 다른 표현으로는 람다( Lambda )라고도 합니다. ( 사실 Lambda에 대한 내용은 조금 더 파고 들어야 합니다. 나중에 다른 포스트에서 정리해볼께요 )

위의 내용으로 간단한 예를 들자면 아래와 같습니다.

function outerFunc() {

    var a = 100;

    return function() {
        var b = a + 100;
        return b;
    }
}

var myFunc = outerFunc();
console.log(myFunc());    // 200 출력

사실 위의 예제는 좀 이상합니다. 아니 많이 이상하죠.

다음과 같은

var myFunc = outerFunc();

코드에서 outerFunc()함수는 수행이 종료되었기 때문에 그 안에 선언된 변수 a는 사실 사용할 수 없어야 합니다. 우리가 익히 알고 있는 다른 프로그래밍 언어의 변수 scope 개념으로 본다면 말이죠.

하지만 JavaScript의 var 변수 scope는 좀 다릅니다. outerFunc() 함수의 호출이 끝났음에도 불구하고 a 변수값이 유지됩니다. 즉, outerFunc()에 대한 참조가 남아있는 한 메모리에 계속 유지하게 되는 구조입니다. ( outerFunc() 함수가 실행중인 상태로 간주한다는 의미이기도 합니다. ) 그렇기 때문에 위에서 myFunc()를 호출했을 때 a 변수를 이용할 수 있는 것입니다.

조금 다른 예제를 살펴보겠습니다.

function myFunc(init) {
    if (init) {
        var x = 10;
    }

    return x;
}

console.log(myFunc(true));  // 10 출력

위의 코드는 JavaScript 코드입니다. var 변수는 if block 안에서 선언되었음에도 불구하고 block 외부에서도 사용이 가능합니다. 위에서 언급했듯이 JavaScript의 var 변수 scope가 좀 독특해서 그렇습니다. var로 선언된 변수는 block에 상관없이 function 내에서 사용이 가능합니다.

이런 특성을 var은 function-scoping을 가진다 라고 표현합니다.

var에 대해서 마지막 하나만 더 살펴보고 정리하겠습니다.

다음과 같은 setTimeout을 이용한 코드가 있을 때 실행결과가 어떻게 출력될까요?

for (var i = 0; i < 10; i++) {
    setTimeout(
        function() { console.log(i); },
        1000
    );
}   

결과는 다음과 같이 출력됩니다.

10
10
10
10
10
10
10
10
10
10

JavaScript를 알고 있는 사람들에게는 꽤나 익숙한 코드형태이자 결과일겁니다. setTimeout은 두번째 인자로 들어가는 시간을 delay로 첫번째 인자의 함수를 호출해 주는 역할을 하는 함수입니다. (내부 Thread로 동작하겠죠)

for문은 순식간에 수행될 것이고 1초뒤에 다음 코드가

function() { console.log(i); }

10번 호출되게 됩니다. 이 때 i변수는 상위 for문의 scope를 참조하게 되므로 실제 console.log(i)가 수행될 때 i값을 참조하면 10이라는 값을 가지고 있을테니 화면에 10이 10번 출력되게 됩니다.

그러면 0부터 9까지 순차적으로 출력하려면 어떻게 해야 할까요? 이 작업을 수행하기 위해 IIFE을 이용합니다.

IIFEImmediately Invoked Function Expression을 의미합니다.

코드를 아래와 같이 수정합니다.

for (var i = 0; i < 10; i++) {
    (function(tmp) {
        setTimeout(
            function() { console.log(tmp); },
            1000
        );
    })(i);
}

즉시 실행되는 함수 표현법(IIFE)를 이용하여 새로운 function scope를 생성해서 i값을 capture하는 방식으로 처리하면 될 듯 합니다.

이 부분을 정확히 이해하기 위해서는 free variable, scope chain, Closure에 대한 이해가 있어야 합니다. 너무 이야기가 산으로 가니 이 부분은 JavaScript 관련 포스트에서 따로 정리해 보도록 하겠습니다.


let

위에서 언급한 var의 모호성을 let을 이용해 해결할 수 있습니다. 변수를 선언할 때 단순히 var대신 let을 이용하면 됩니다.

let myVar: string = "Hello World!!";

let은 우리에게 친숙한 block-scoping을 가집니다. ( lexical-scoping 이라고도 합니다. )

function myFunc(input: boolean) {
    
    let a = 100;

    if (input) {
        let b = a + 1;   // a에 접근이 가능합니다.
        return b;
    }

    return b;            // 코드 에러( b에 접근할 수 없습니다. )
}

let의 또 다른 특징은 변수의 중복 선언이 안된다는 것입니다. 사실 프로그래밍 언어에서는 거의 당연시 여겨지는 것이지만 var는 중복선언이 가능합니다. 다음과 같이 말이죠.

function f() {
    
    var x;
    var x;          // JavaScript의 var은 같은 변수를 중복선언할 수 있습니다. 
    
    if (true) {
        var x;
    }
}

자 그럼 아까 var로 선언했을 때 이상하게 동작했던 setTimeout을 이번에는 let을 이용해서 처리하면 어떻게 될까요?

for (let i = 0; i < 10; i++) {
    setTimeout(
        function() { console.log(i); },
        1000
    );
}  
0
1
2
3
4
5
6
7
8
9

var로 선언했을 때와 다른 결과를 볼 수 있습니다.

결론적으로 let은 우리가 익히 알던 (다른 언어의) 변수 특징을 가지는
변수를 선언하기 위한 keyword라고 생각하시면 됩니다.


const

변수를 선언하기 위한 또 다른 방법은 const를 이용하는 것입니다. 하지만 const로 선언된 변수에는 재 할당(re-assign)이 불가능 합니다. 아래의 예처럼 말이죠.

const myName: string = "홍길동";

myName = "강감찬";   // 코드 에러

const를 사용할 때 한가지만 주의하시면 됩니다. 만약 const 변수가 객체를 지칭하게 되면 다른 객체로 reference를 바꾸지는 못하지만 현재 reference하고 있는 객체의 속성에 대해서는 값을 변경할 수 있습니다. 아래의 예처럼 말이죠.

const count: number = 100;

const myProfile = {
    myName: "홍길동",
    myAddress: "서울",
    myCount: count
};

myProfile = {                  // 코드 에러 ( re-assign 안됨 )
    myName: "강감찬",
    myAddress: "인천",
};                   

myProfile.myName = "강감찬";   // 가능
myProfile.myAddress = "인천";
myProfile.myCount = 10;

변수 선언에 대한 keyword에 대해서는 이정도 알아두시면 될 듯 보입니다. 다음 주제는 Destructuring(디스트럭쳐링)입니다.

End.


이 포스트의 내용은 아래의 사이트를 참조했습니다. 조금 더 자세한 사항을 알고 싶으시면 해당 사이트를 방문하세요!!