(하.. 방금 토스 코테 보고 왔는데 알고리즘 문제 쪽에서 코드 저장안해서 알고리즘 문제 제출 안됨.. 개멘붕.....
나 왜 당연히 저장된 걸로 알고 있었지?... 나 진짜 바보다ㅜㅜ 테스트 통과 까지 확인 했는데 코드 저장을 안함 ㅜㅡㅜ ..)
아무튼 이번주 향해 회고를 하자면
처음 과제 받았을 떄 나의 상태...
보기만 해도 어렵다 .. 뭔소리지..
문제 "이번 주차를 지나며 겪었던 문제가 무엇이었나요?"
처음에는 기본 과제부터 "왜 이 함수를 사용해야 하는지" 또는 "이 로직이 어떤 역할을 하는지" 정확하게 이해하지 못하고, 일단 주어진 방향대로 구현하려 했습니다. 그 결과, 심화 과제의 단계에 들어서면서 어디서부터 어떻게 접근해야 할지 막막함을 느꼈습니다.
시도 "문제를 해결하기 위해 어떤 시도를 하셨나요?"내가 정확히 무엇을 알고 있고, 어떤 부분을 모르고 있는지 명확하지 않았기 때문에 내가 작성한 코드를 팀원분들께 설명했습니다. 그 과정을 통해 논리적으로 생각하며 문제를 분석하고, 무엇을 제대로 이해하지 못했는지 확인했습니다.
해결 "문제를 어떻게 해결하셨나요?"
코드에 대해 설명하면서 나의 이해가 부족한 부분을 더 잘 파악할 수 있었고, 팀원분의 피드백을 통해 문제 해결 접근 방식을 조금씩 터득할 수 있었습니다. 조언대로 디버깅을 진행하니 코드의 흐름을 하나씩 파악하며 구조를 이해하게 되었습니다.
알게된 것 "문제를 해결하기 위해 시도하며 새롭게 알게 된 것은 무엇인가요?"
직접 vNode를 콘솔에 찍어보면서 그 구조를 파악하고, 데이터가 어떻게 흘러가는지 확인할 수 있었습니다. 이 과정을 통해 로직이 어떻게 동작하는지 이해하고, Virtual DOM의 변환 과정을 체감할 수 있었습니다.
지난 목표 회고 "지난 주에 설정해 두었던 목표는 달성하셨나요? 잘 된 것은 무엇이고 안 된 것은 무엇인가요?"
이전에는 혼자서 문제를 해결하려는 경향이 있었지만, 이번 주에는 모르는 부분이 있으면 설명하고 피드백을 받으면서 문제를 해결해 나갈 수 있었습니다.
클로저를 이해하려면 먼저 자바스크립트가 변수의 유효범위를 어떻게 지정하는지, 즉 렉시컬 스코핑(Lexical Scoping) 개념을 이해해야 해요.
실행 컨텍스트(Execution Context)
실행 컨텍스트는 자바스크립트 코드가 평가되고 실행되는 환경을 추상적으로 설명한 개념이에요. 모든 자바스크립트 코드는 실행 컨텍스트 내에서 실행돼요.
☑️ 실행 컨텍스트 생성 과정
자바스크립트 코드가 실행되면, 그 코드가 어떤 스코프에서 실행되는지를 확인하고 그에 맞는 실행 컨텍스트가 생성돼요.
전역 코드가 실행되면 전역 실행 컨텍스트가 생성되고,
함수가 실행되면 그 함수만의 함수 실행 컨텍스트가 생성돼요.
각 실행 컨텍스트에는 해당 코드 블록에서 접근할 수 있는 변수, 함수, 상위 스코프에 대한 정보가 기록돼요.
let globalVar = 10;
function outer() {
let outerVar = 20;
function inner() {
let innerVar = 30;
console.log(globalVar); // 전역 스코프에 접근 (전역 실행 컨텍스트)
console.log(outerVar); // 외부 함수 스코프에 접근 (outer의 실행 컨텍스트)
console.log(innerVar); // 현재 함수 스코프에 접근 (inner의 실행 컨텍스트)
}
inner();
}
outer();
☑️ 실행 컨텍스트 주요 구성 요소
렉시컬 환경(Lexical Environment): 변수나 함수가 어디에 정의되었는지에 따라 변수나 함수에 접근할 수 있는 범위를 결정하며, 코드 실행 중 변수가 실시간으로 변경되는지를 관리해요.
변수 환경(Variable Environment): 함수나 블록이 실행될 때, 변수들이 처음 선언된 상태를 저장해요.
VE(변수 환경)와 LE(렉시컬 환경)는 실행 컨텍스트가 처음 생성될 때는 동일한 내용을 담지만, 이후에는 VE는 스냅샷을 유지하고 LE는 실시간으로 변경사항을 반영해요.
결국, 실행 컨텍스트를 생성할 때, VE에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LE를 만들고 이후에는 주로 LE를 활용한답니다.
렉시컬 환경은 크게 두 가지 정보를 담고 있어요:
record (environmentRecord): 현재 스코프에서 선언된 변수나 함수 같은 식별자들을 저장하고 관리해요. 호이스팅(hoisting)이 일어나는 것도 이 기록 과정에서 발생
outer (outerEnvironmentReference): 현재 스코프보다 상위 스코프를 참조해요. 이 속성을 통해 현재 스코프에서 변수를 찾지 못하면 상위 스코프에서 변수를 검색할 수 있어요.
클로저
클로저는 함수가 선언될 때의 렉시컬 환경을 기억하는 기능이에요. 어떤 함수가 실행된 후에도 그 함수가 선언된 당시의 변수를 기억하고, 나중에 그 변수를 사용할 수 있는 걸 클로저라고 해요.
☑️ 클로저의 주요 특징:
데이터 은닉과 캡슐화 구현 가능
함수 팩토리 생성
비동기 처리에서의 활용
1) 클로저의 활용: 데이터 은닉과 상태 관리
let num = 0;
// 카운트 상태 변경 함수
const increase = function () {
// 카운트 상태를 1만큼 증가시킨다.
return ++num;
};
console.log(increase());
// num = 100; // 치명적인 단점이 있어요.
console.log(increase());
console.log(increase());
위 코드에서는 num 변수가 외부에서 접근 가능해, 직접 수정할 수 있는 위험이 있어요.
따라서 상태를 안전하게 변경하고 유지하기 위해 외부에서는 직접 접근할 수 없게 해야한다.
// 카운트 상태 변경 함수 #2
const increase = function () {
// 카운트 상태 변수
let num = 0;
// 카운트 상태를 1만큼 증가시킨다.
return ++num;
};
// 이전 상태값을 유지 못함
console.log(increase()); //1
console.log(increase()); //1
console.log(increase()); //1
이 코드에서는 num 변수가 함수 내부에 선언되어 있지만, 매번 함수가 호출될 때마다 새로운 num이 생성되기 때문에 이전 상태를 유지할 수 없어요.
이 문제를 해결하기 위해 클로저를 사용하면, 함수가 종료된 이후에도 상태를 유지할 수 있어요:
// 카운트 상태 변경 함수 #3
const increase = (function () {
// 카운트 상태 변수
let num = 0;
// 클로저
return function () {
return ++num;
};
})();
// 이전 상태값을 유지
console.log(increase()); //1
console.log(increase()); //2
console.log(increase()); //3
여기서 num 변수는 외부에서 직접 접근할 수 없고, 오직 클로저인 increase 함수에서만 값이 변경돼요. 이를 통해 상태를 안전하게 관리할 수 있어요
따라서 클로저는 상태(state)가 의도치 않게 변경되지 않도록 안전하게 은닉(information hiding) 하고 특정 함수에게만 상태 변경을 허용하여 상태를 안전하게 변경하고 유지하기 위해 사용돼요.
이제는 세금 비율을 한 번만 설정하고, 다른 금액들을 계속 계산할 수 있게 됐어요. 이렇게 하면 코드를 더 효율적으로 관리할 수 있어요.
3) 비동기 처리에서의 클로저 활용
function fetchData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`Data from ${url}`);
}, 1000);
});
}
function createDataFetcher(baseUrl) {
const cache = new Map();
return function(endpoint) {
const url = `${baseUrl}/${endpoint}`;
if (cache.has(url)) {
console.log('Serving from cache');
return Promise.resolve(cache.get(url));
}
return fetchData(url).then(data => {
cache.set(url, data);
return data;
});
};
}
const fetchFromAPI = createDataFetcher('<https://api.example.com>');
fetchFromAPI('users')
.then(data => console.log(data))
.then(() => fetchFromAPI('users')) // 두 번째 호출은 캐시에서 제공됨
.then(data => console.log(data));
위 코드는 클로저를 이용해 API 데이터를 캐시하고, 같은 데이터를 다시 요청할 때는 캐시된 데이터를 제공하는 방식이에요.
결론
실행 컨텍스트, 렉시컬 환경, 그리고 클로저는 자바스크립트에서 변수를 어떻게 관리하고, 함수가 어떤 스코프에서 실행되는지를 이해하는 데 필수적인 개념이에요. 클로저는 특히 상태 관리나 비동기 처리에서 강력한 도구로 사용될 수 있고, 코드의 안정성을 높이는 데 중요한 역할을 해요.