💻 프로그래밍

Variable

변수

프로그램에서 데이터를 저장하는 메모리 공간에 붙이는 이름. let, const, var의 차이와 스코프를 이해하면 안정적이고 예측 가능한 코드를 작성할 수 있습니다.

📖 상세 설명

변수(Variable)는 프로그램에서 데이터를 저장하고 참조하기 위한 메모리 공간에 붙이는 이름입니다. "변할 수 있는 값"이라는 의미로, 프로그램 실행 중에 값이 변경될 수 있습니다. 예를 들어 let count = 0;이라고 선언하면, 메모리 어딘가에 0이라는 값이 저장되고, 우리는 "count"라는 이름으로 그 값에 접근하고 변경할 수 있습니다. 변수는 프로그래밍의 가장 기본적인 개념이며, 모든 언어에서 필수적으로 사용됩니다.

스코프(Scope)는 변수가 접근 가능한 범위를 의미합니다. 전역 스코프(Global Scope)의 변수는 프로그램 어디서든 접근 가능하고, 지역 스코프(Local Scope)의 변수는 선언된 함수 내부에서만 접근 가능합니다. ES6부터 도입된 블록 스코프(Block Scope){ } 중괄호 내부에서만 유효합니다. JavaScript의 letconst는 블록 스코프를 따르지만, var는 함수 스코프를 따라서 의도치 않은 버그가 발생할 수 있습니다.

JavaScript에서 let, const, var의 차이는 매우 중요합니다. var는 ES6 이전의 변수 선언 방식으로, 함수 스코프를 가지며 호이스팅(Hoisting) 시 undefined로 초기화됩니다. let은 블록 스코프를 가지며, 호이스팅되지만 초기화 전에 접근하면 TDZ(Temporal Dead Zone) 에러가 발생합니다. constlet과 동일한 스코프 규칙을 따르지만, 선언 시 반드시 초기화해야 하고 재할당이 불가능합니다. 단, 객체나 배열의 내부 값은 변경할 수 있습니다.

네이밍 컨벤션(Naming Convention)은 코드의 가독성과 유지보수성에 직접적인 영향을 미칩니다. JavaScript에서는 camelCase를 사용하고, Python에서는 snake_case를 사용합니다. 변수 이름은 그 역할을 명확히 나타내야 합니다. d보다 elapsedTimeInDays가, temp보다 currentTemperature가 좋습니다. 약어는 피하고, 불린 변수는 isActive, hasPermission처럼 is/has/can으로 시작하면 의미가 명확해집니다.

💻 코드 예제

JavaScript - let, const, var 차이
// 1. var - 함수 스코프, 호이스팅 시 undefined로 초기화
console.log(varVariable);  // undefined (에러 아님!)
var varVariable = "var로 선언";

// 2. let - 블록 스코프, TDZ 존재
// console.log(letVariable);  // ReferenceError: TDZ
let letVariable = "let으로 선언";

// 3. const - 재할당 불가, 객체 내부는 변경 가능
const PI = 3.14159;
// PI = 3.14;  // TypeError: 재할당 불가

const user = { name: "김개발", age: 25 };
user.age = 26;  // 객체 내부 변경은 가능!
console.log(user);  // { name: "김개발", age: 26 }

// 4. 스코프 비교
function scopeExample() {
    if (true) {
        var functionScoped = "var: 함수 스코프";
        let blockScoped = "let: 블록 스코프";
    }
    console.log(functionScoped);  // "var: 함수 스코프"
    // console.log(blockScoped);  // ReferenceError
}

// 5. for 루프에서의 차이 (클로저 문제)
for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log("var:", i), 100);
}  // 출력: 3, 3, 3 (의도치 않은 결과!)

for (let j = 0; j < 3; j++) {
    setTimeout(() => console.log("let:", j), 100);
}  // 출력: 0, 1, 2 (의도한 결과)
JavaScript - 스코프 체인과 클로저
// 1. 전역 변수 (가능하면 피하기)
let globalCount = 0;  // 어디서든 접근 가능 - 위험!

// 2. 지역 변수와 스코프 체인
const outerValue = "외부";

function outer() {
    const middleValue = "중간";

    function inner() {
        const innerValue = "내부";
        // 스코프 체인: inner -> outer -> global
        console.log(innerValue);   // "내부" (자신의 스코프)
        console.log(middleValue);  // "중간" (외부 스코프)
        console.log(outerValue);   // "외부" (전역 스코프)
    }

    inner();
}

// 3. 클로저를 활용한 private 변수
function createCounter() {
    let count = 0;  // private 변수 (외부에서 직접 접근 불가)

    return {
        increment() {
            count += 1;
            return count;
        },
        decrement() {
            count -= 1;
            return count;
        },
        getCount() {
            return count;
        }
    };
}

const counter = createCounter();
console.log(counter.increment());  // 1
console.log(counter.increment());  // 2
console.log(counter.getCount());   // 2
// counter.count;  // undefined (직접 접근 불가)

🗣 실무 대화 예시

🔍 코드 리뷰: 변수 네이밍
👨‍💻
시니어 개발자 (리뷰어)
이 코드에서 let d = new Date() - startTime;이라고 되어있는데, d가 뭘 의미하는지 바로 알기 어렵네요. 변수 이름을 좀 더 명확하게 바꿔주실 수 있을까요?
👩‍💻
주니어 개발자
아, 경과 시간을 밀리초 단위로 계산한 건데요. const elapsedTimeMs = Date.now() - startTime;으로 바꾸면 될까요? const로 바꾼 이유는 이 값을 나중에 변경하지 않아서요.
👨‍💻
시니어 개발자 (리뷰어)
좋습니다! 변수명에서 단위(Ms)까지 표현한 것도 좋네요. 그리고 재할당하지 않는 값은 const로 선언하는 습관도 훌륭해요. 한 가지 더, 아래 let flag = true;isProcessing이나 shouldContinue처럼 의미를 담은 이름이 좋겠어요.

🎓 기술 면접: 호이스팅 설명
👤
면접관
JavaScript에서 호이스팅(Hoisting)이 뭔지 설명해주시고, var와 let의 호이스팅 동작 차이를 말씀해주세요.
👩
지원자
호이스팅은 JavaScript 엔진이 코드 실행 전에 변수와 함수 선언을 스코프 최상단으로 끌어올리는 것처럼 동작하는 것을 말합니다. 실제로 코드가 이동하는 건 아니고, 컴파일 단계에서 선언부만 먼저 메모리에 등록됩니다.
👩
지원자
var는 호이스팅될 때 undefined로 초기화되어서, 선언 전에 접근해도 에러 없이 undefined가 나옵니다. 반면 let과 const는 호이스팅되지만 초기화되지 않아서, 선언 전에 접근하면 TDZ(Temporal Dead Zone) 에러가 발생합니다. 이런 차이 때문에 let/const가 더 안전하고, 요즘은 var 대신 let/const를 사용합니다.
👤
면접관
좋아요. 그럼 실제 프로젝트에서 var 때문에 버그가 발생했던 경험이 있나요?
👩
지원자
네, for 루프에서 var로 선언한 인덱스 변수를 setTimeout 콜백에서 사용했을 때 문제가 있었어요. 루프가 끝난 후의 값만 출력되어서 원인을 찾느라 시간이 걸렸는데, let으로 바꾸니까 바로 해결됐습니다. 그 후로 var는 아예 사용하지 않고, ESLint에서 no-var 규칙을 활성화해서 쓰고 있어요.

주의사항

🚨 변수 관련 실무 주의사항
  • var 사용 금지: ES6 이후에는 let과 const만 사용하세요. var의 함수 스코프와 호이스팅 동작은 예측하기 어려운 버그를 만듭니다. ESLint의 no-var 규칙을 활성화하세요.
  • const를 기본으로: 재할당이 필요한 경우에만 let을 사용하고, 그 외에는 const를 사용하세요. 불변성(Immutability)을 유지하면 코드 추론이 쉬워집니다.
  • 전역 변수 최소화: 전역 변수는 어디서든 수정될 수 있어 추적이 어렵습니다. 모듈 시스템, 클로저, 클래스 등을 활용해 스코프를 제한하세요.
  • 의미 있는 이름 사용: temp, data, result 같은 모호한 이름 대신 userProfile, filteredProducts처럼 구체적인 이름을 사용하세요.
  • 선언과 초기화를 함께: 변수 선언 후 나중에 초기화하면 undefined 상태로 존재하는 시간이 생깁니다. 가능하면 선언과 동시에 초기화하세요.

🔗 관련 용어

📚 더 배우기