NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 학습
  • 매뉴얼

    학습


    Web Javascript 의 변수인 const와 let 그리고 var 차이점.

    페이지 정보

    본문

    안녕하세요. 엔지엠소프트웨어입니다. 이번 년도에는 본업을 프로젝트 매니저(PM)로 하고 있어서 크게 개발에 관여하지는 않았습니다. 주로 고객과 개발 아이템 및 일정 협의와 내부 개발자 인력 관리, 스케줄 관리가 전부였거든요. 그렇다보니 스스로 이것 저것 공부하지 않으면 안되겠더라구요. 하찮은 실력에 나이만 먹고, 옛날 기술로만 버티려니 입지도 점점 줄어들고, 여러모로 힘든 시기입니다-_-; 다음 프로젝트는 웹(node, angular, typescript)인데요. 요즘 웹 트렌드를 찾아보다 아주 기초적인 내용이지만~ 명확하게 const와 let 그리고 var의 차이점에 대해 잘 모르는듯하여 정리하는 차원에서 글을 남겨봅니다.

     

    Javascript에서 변수를 선언할 때 사용하는 const와 let, var를 사용합니다. 오래전~ 웹프로젝트를 할 때는 var밖에 없었는데 어느덧 이상한 아이들이 생겨 났더라구요. 이들의 관계를 정확하게 이해하려면 변수의 선언과 할당 그리고, 호이스팅과 스코프를 알아야 합니다. 일단, const는 다른 언어들(C++, C#, Java...)도 const를 사용하기 때문에 알아두면 좋죠~ const는 상수를 정의할 때 사용하고, 상수라는건 변하지 않는 값들을 만들 때 사용합니다. 예를들어서 어떤 시스템에 접속할 때 사용자 아이디를 상수로 초기화하고, 이후 서비스를 이용하는 동안 이 값이 내부 또는 외부의 영향으로 값이 변경되면 안된다고 하면 상수로 선언하고, 아이디로 초기화하면 됩니다. 아래와 같은 간단한 테스트 코드를 만들고 실행 해보세요.

    const id = "NGMsoftware";
    id = "Guest";
    console.log(id);
    [Running] node "d:\NewWebSite\third-app\src\javascript.js"
    d:\NewWebSite\third-app\src\javascript.js:2
    id = "Guest";
       ^
    
    TypeError: Assignment to constant variable.
        at Object.<anonymous> (d:\NewWebSite\third-app\src\javascript.js:2:4)
        at Module._compile (node:internal/modules/cjs/loader:1108:14)
        at Object.Module._extensions..js (node:internal/modules/cjs/loader:1137:10)
        at Module.load (node:internal/modules/cjs/loader:973:32)
        at Function.Module._load (node:internal/modules/cjs/loader:813:14)
        at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:12)
        at node:internal/main/run_main_module:17:47
    
    [Done] exited with code=1 in 0.053 seconds

     

    "Assignment to constant variable."와 같이 상수 변수에 값을 할당해서 에러가 발생한 것을 확인할 수 있습니다. 대부분의 언어가 동일하고, 상수는 코딩에 있어서 용도가 명확하다보니 크게 설명할 부분이 없습니다. 근데 let과 var의 차이는 명확하게 이해하기 난해한 부분이 있습니다. 프로젝트 시작 단계에서 어떤 기준을 가지고 팀원들이 코딩을 시작하면 아마도~ 균일한 품질의 코드가 만들어질겁니다. 그런데 보통은 여러 개발자를 거쳐 조금씩 프로그램에 기능이 추가되고, 최적화 및 안정화를 거치게 됩니다. 물론, 이렇게 이상향을 따라가지는 못합니다. 일정에 쫓기다보면 스파게티 소스가 되기 마련이고, 리펙토링 프로젝트를 다시 하기도 하죠. 아무튼, 이미 만들어진 시스템을 유지보수 하거나 추가 기능을 개발할 때 비슷한 코드라도 var를 쓰거나 let을 씁니다.

    var id = "NGMsoftware";
    id = "Guest";
    console.log(id);
    [Running] node "d:\NewWebSite\third-app\src\javascript.js"
    Guest
    
    [Done] exited with code=0 in 0.051 seconds

     

    let을 사용해도 결과는 동일합니다.

    let id = "NGMsoftware";
    id = "Guest";
    console.log(id);
    [Running] node "d:\NewWebSite\third-app\src\javascript.js"
    Guest
    
    [Done] exited with code=0 in 0.052 seconds

     

    오래전부터 웹프로젝트를 해왔던 개발자분들은 var에 대해서는 충분히 이해하고 있을겁니다. let은 ES6(2015)가 세상에 나오면서 추가되었습니다. ES6는 기존의 Javascript의 문제점으로 지적되어온 내용들이 대폭 개선되었으며, 수많은 개념과 기능이 추가 되었습니다. 그래서, 저처럼 2000년 초반에 웹개발을 하다가 중간에 CS(클라이언트, 서버) 쪽으로 이동한 개발자는 잘 모를수도 있습니다. 물론, 저도 모릅니다만~ 중간 중간 node, react, angular, typescript를 이용한 프로젝트를 몇번 했기에 그나마 약간 알고 있는 수준입니다. 아무튼 ES6가 발표되면서 var 말고 let을 사용하라고 홍보를 많이 했었습니다. 그리고, var를 사용함으로써 발생되는 여러가지 문제점들을 let으로 해결하려고 한거죠.

     

    Javascript는 실행될 때 V8 엔진이 어떤 변수들이 코드에 존재하는지 미리 조사를 합니다. 그리고, 선언된 변수들을 메모리에 저장 시킵니다. 그리고, 누군가가 해당 변수를 호출하면 메모리에서 가져오게 되는데 이렇게 동작하는 메커니즘을 호이스팅(Hoisting)이라고 부릅니다. 호이스팅은 코드가 실행되기 전 변수 및 함수 선언이 해당 스코프의 최상단으로 끌어 올려줍니다. 왜 이렇게 해야 하는지는 고전적인 Javascript를 하다보면 이해할 수 있습니다. 자바스크립트는 인터프리터 언어로 위에서 아래로 한줄씩 읽으면서 코드를 처리하는 언어였습니다. 이로인해 선언과 할당이 문제를 일으키곤 했죠. 이 당시에는 자바스크립트를 디버깅할 수 있는 변변한 도구조차 없었으니 말이죠^^;

     

    호이스팅으로 인해 코드 실행전에 변수가 메모리에 저장되어 있기 때문에 선언문보다 참조나 호출이 먼저 나와도 오류없이 잘 동작합니다. 정확히는 var 키워드로 선언한 변수와 함수 선언문일 경우 오류 없이 동작하죠. 이는 선언이 파일의 맨 위로 끌어 올려진것처럼 만들어줍니다. 실제로 끌어 올린건 아닙니다. 위 예제 코드를 아래와 같이 변경하고 실행 해보세요. 물론, 잘 동작할겁니다^^

    var id = "NGMsoftware";
    console.log(id);

     

    코드를 아래와 같이 변경 해보세요. C나 C#, Java와 같은 컴파일 언어를 하시는 분들이 보면 말도 안되는 코드인걸 금방 알 수 있습니다. 언어는 항상 선언, 할당, 사용의 순서를 가지게 되는데요. 아래 코드는 사용, 선언, 할당으로 되어 있습니다. 당연히 이 코드는 디자인 타임(코드 작성중...)에서 이미 에러를 표시할테고, 컴파일이 안됩니다.

    console.log(id);
    var id = "NGMsoftware";
    console.log(id);

     

    그래도, 일단 실행 해봅시다. 에러가 발생하지않고, undefined를 출력해줍니다. 왠지 익숙하네요^^; 자주 보던거니까요-_-;

    [Running] node "d:\NewWebSite\third-app\src\javascript.js"
    undefined
    NGMsoftware
    
    [Done] exited with code=0 in 0.056 seconds

     

    그럼 변수를 제거하면 어떻게 될까요? 당연히 에러를 출력 해줍니다. 변수 선언이 없어졌기 때문이죠. 호이스팅으로 변수 선언이 끌어 올려진것과 변수가 존재하지 않는것은 다르니까요.

    console.log(id);
    [Running] node "d:\NewWebSite\third-app\src\javascript.js"
    d:\NewWebSite\third-app\src\javascript.js:1
    console.log(id);
                ^
    
    ReferenceError: id is not defined
        at Object.<anonymous> (d:\NewWebSite\third-app\src\javascript.js:1:13)
        at Module._compile (node:internal/modules/cjs/loader:1108:14)
        at Object.Module._extensions..js (node:internal/modules/cjs/loader:1137:10)
        at Module.load (node:internal/modules/cjs/loader:973:32)
        at Function.Module._load (node:internal/modules/cjs/loader:813:14)
        at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:12)
        at node:internal/main/run_main_module:17:47
    
    [Done] exited with code=1 in 0.047 seconds
    
    

     

    호이스팅은 코드를 실행하기 전 변수를 메모리에 담지만, 자기 마음데로 초기화 시켜 버립니다. 그렇기 때문에 id 변수에 undefined가 들어가 있게 되고, 처음 콘솔에서 이 값을 출력 해주게 됩니다. 물론, 두번째 줄에서 id 변수에 값을 할당했기 때문에 3번째 줄에서는 우리가 의도한 값이 출력되었습니다. 전통적으로 오래된 자바스크립트는 사실 var 키워드를 사용하지 않아도 정상 동작했었습니다. 물론, 지금은 안됩니다. 하지만 아래와 같은 코드는 동작합니다. C나 Java 개발자는 이게 말이돼나 싶을 겁니다 ㅎㅎ;

    console.log(id);
    id = "NGMsoftware";
    var id;
    console.log(id);
    [Running] node "d:\NewWebSite\third-app\src\javascript.js"
    undefined
    NGMsoftware
    
    [Done] exited with code=0 in 0.055 seconds

     

    var의 가장 큰 문제점은 전역 변수와 지역 변수를 구분하지 못한다는 것입니다. 아래와 같이 코드를 작성하고, 실행 해보세요.

    var id = "NGMsoftware"; // 전역 변수 (Global variable)
    
    function ngm() {
        var pw = "********"; // 지역 변수 (Local variable)
        console.log(id);
    }
    
    console.log(pw);
    [Running] node "d:\NewWebSite\third-app\src\javascript.js"
    d:\NewWebSite\third-app\src\javascript.js:8
    console.log(pw);
                ^
    
    ReferenceError: pw is not defined
        at Object.<anonymous> (d:\NewWebSite\third-app\src\javascript.js:8:13)
        at Module._compile (node:internal/modules/cjs/loader:1108:14)
        at Object.Module._extensions..js (node:internal/modules/cjs/loader:1137:10)
        at Module.load (node:internal/modules/cjs/loader:973:32)
        at Function.Module._load (node:internal/modules/cjs/loader:813:14)
        at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:12)
        at node:internal/main/run_main_module:17:47
    
    [Done] exited with code=1 in 0.052 seconds
    
    

     

    함수 스코프 안에 선언된 지역 변수인 pw는 함수 밖에서 사용할 수 없기 때문에 에러가 발생됩니다. 당연한 결과죠? 그러나 아래 내용은 좀 다릅니다. 이 코드 자체가 일단 말이 안되지만~ for 문 스코프 밖에 i 변수를 사용했기 때문이죠. 하지만, 실행하면 결과가 정상적으로 표시됩니다. 변수 i가 0부터 5보다 작을 때 까지 반복하는 루틴입니다. 결과는 예상대로 0부터 4까지 찍혀야 합니다. 그러나, for 문 밖에 i를 찍어주고 있기 때문에 실제로는 0부터 5까지 찍히게 됩니다.

    for (var i=0;i<5;i++){
        console.log(i);
    }
    console.log(i);
    [Running] node "d:\NewWebSite\third-app\src\javascript.js"
    0
    1
    2
    3
    4
    5
    
    [Done] exited with code=0 in 0.055 seconds

     

    var 키워드로 선언한 변수는 특이하게도 함수만 지역 변수로 호이스팅이 되고, 나머지는 다 전역 변수로 올려버립니다. 아래 코드는 더 심각합니다-_-; 물론, 컴파일 언어를 먼저 하신 분들에게만 해당되겠지만요. 변수 선언 키워드로 동일한 이름을 사용한다는건... 음... 그렇습니다. 자바스크립트는 이렇게해도 용서가 됩니다.

    var id = "NGM Macro";
    console.log(id);
    var id = "NGMsoftware";
    console.log(id);
    [Running] node "d:\NewWebSite\third-app\src\javascript.js"
    NGM Macro
    NGMsoftware
    
    [Done] exited with code=0 in 0.054 seconds

     

    이게 날코딩할 때는 여러모로 편한 부분이 있습니다. 특히나 디버거 도구가 제대로 없던 시절에는 수백줄, 수천줄에 해당하는 코드를 모두 따라다니면서 분석하기란 정말 뼈를 깍는 고통이었거든요. 그렇다보니 이렇게 느슨하게 어느정도 알아서 처리해주는 부분들이 좋았습니다. 그러나, 프로젝트 규모가 점점 커지고 복잡해짐에 따라 어쩔 수 없이 제약이 필요해지게 됩니다. typescript가 나온 배경이기도 하구요. 이 부분은 다른 얘기이니 여기에서 언급하지는 않겠습니다. 아무튼, 그래서 나온게 let입니다. var를 let으로 변경해보세요.

    let id = "NGM Macro";
    console.log(id);
    let id = "NGMsoftware";
    console.log(id);
    [Running] node "d:\NewWebSite\third-app\src\javascript.js"
    d:\NewWebSite\third-app\src\javascript.js:3
    let id = "NGMsoftware";
        ^
    
    SyntaxError: Identifier 'id' has already been declared
        at wrapSafe (node:internal/modules/cjs/loader:1024:16)
        at Module._compile (node:internal/modules/cjs/loader:1072:27)
        at Object.Module._extensions..js (node:internal/modules/cjs/loader:1137:10)
        at Module.load (node:internal/modules/cjs/loader:973:32)
        at Function.Module._load (node:internal/modules/cjs/loader:813:14)
        at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:12)
        at node:internal/main/run_main_module:17:47
    
    [Done] exited with code=1 in 0.064 seconds
    
    

     

    이번에는 우리가 원하던(?) 에러를 표시 해줍니다. "Identifier 'id' has already been declared., 식별자 'id'가 이미 선언되었습니다." var에서 정상적으로 처리되던 코드들도 let으로 변경하고 다시 실행 해보세요. 에러를 발생시키게 되어, 좀 더 명확하게 변수를 사용할 수 있도록 해줍니다. 그렇다면 "let은 호이스팅이 안되냐?" 그건 아닙니다. let으로 선언된 변수는 호이스팅되는 시점에 Temporal Death Zone(TDZ)을 만들고, 이곳에 넣어줍니다. 선언이 나오기 전까지 이곳에 넣어두는거죠. 이렇게해서 const와 var, let에 대해 간단하게 알아 봤습니다. 사실은 function도 호이스팅이 되기 때문에 위나 아래에 함수가 있더라도 호출이 가능하다는걸 알고 있을겁니다. 그리고, 블록과 스코프에 따라 전역과 지역인지 다른 레벨로 인지하죠.

     

    개발 방법론에는 가능하면 스코프를 좁게 만드는걸 권장합니다. 극단으로 함수 또는 메소드의 코드 범위를 30줄이 가장 좋다고 하기도 하니까요. 하지만, 정적 코드 분석 도구(소나 큐브와 같은...)가 코드 부채를 계산 할 때 라인도 중요하지만 뎁스도 중요하게 생각합니다. 여러 함수 또는 메소드에 걸쳐서 하나의 프로시저를 처리한다면 이것또한 그리 좋은 코딩은 아닙니다. 제가 뭐~ 아는 지식이 많지 않다보니 어떤게 좋은 코딩이고 어떤게 나쁜 코딩이라고 명확하게 답변 드릴 수 있는건 아니지만, 항상 하는 말은 Simple is best라는 겁니다. 신기술이 나온다고 해서 새로운 문법으로 코딩하는것, 새로운 개념, 새로운 방식을 무조건 도입하는게 좋은건 아닙니다. 혼자 학습은 괜찮지만 협업 환경에서 다른 개발자들이 코드를 보고 분석할 수 없거나 러닝 커브가 높다면 이 또한 시간과 비용이니까요~ 뭐 충분한 주석을 달아놓으면 괜찮겠습니다만... 판단은 여러분들 몫입니다^^

     

    개발자에게 후원하기

    MGtdv7r.png

     

    추천, 구독, 홍보 꼭~ 부탁드립니다.

    여러분의 후원이 빠른 귀농을 가능하게 해줍니다~ 답답한 도시를 벗어나 귀농하고 싶은 개발자~

    감사합니다~

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천0 비추천0

    댓글목록

    등록된 댓글이 없습니다.