미래 지향적인 웹 앱 구축: The Codest의 전문가 팀이 제공하는 인사이트
The Codest가 최첨단 기술로 확장 가능한 대화형 웹 애플리케이션을 제작하고 모든 플랫폼에서 원활한 사용자 경험을 제공하는 데 탁월한 성능을 발휘하는 방법을 알아보세요. Adobe의 전문성이 어떻게 디지털 혁신과 비즈니스를 촉진하는지 알아보세요...
지난 몇 년 동안 웹 개발이 변화하고 있다는 것을 알 수 있었습니다. 브라우저에 많은 기능과 API가 추가되면서 올바른 방식으로 이를 사용해야 했습니다. 이 영광을 안겨준 언어는 JavaScript였습니다.
처음에 개발자들은 이 스크립트가 어떻게 설계되었는지 확신하지 못했고 이 스크립트를 사용하는 동안 대부분 부정적인 인상을 받았습니다. 시간이 지남에 따라 이 언어가 큰 잠재력을 가지고 있다는 것이 밝혀졌고, 이후 ECMAScript 표준은 일부 메커니즘을 더 인간적이고 간단하게 개선했습니다. 이 글에서는 그 중 몇 가지를 살펴봅니다.
잘 알려진 진실 JavaScript 의 핵심은 모든 것이 객체라는 점입니다. 배열, 함수, 문자열, 숫자, 심지어 부울까지 모든 것이 객체입니다. 모든 유형의 값은 객체로 표현되며 고유한 메서드와 필드가 있습니다. 하지만 이를 기본값과 구조체의 두 가지 범주로 나눌 수 있습니다. 첫 번째 범주의 값은 불변이므로 새 값으로 일부 변수를 재할당할 수는 있지만 기존 값 자체는 수정할 수 없습니다. 두 번째는 변경할 수 있는 값을 나타내므로 대체할 수 있는 속성 컬렉션으로 해석하거나 이를 수행하도록 설계된 메서드를 호출해야 합니다.
더 자세히 알아보기 전에 범위의 의미에 대해 설명하겠습니다. 범위는 선언된 변수를 사용할 수 있는 유일한 영역이라고 할 수 있습니다. ES6 표준 이전에는 var 문을 사용하여 변수를 선언하고 전역 또는 로컬 범위를 지정할 수 있었습니다. 첫 번째는 모든 애플리케이션에서 일부 변수에 액세스할 수 있는 영역이고, 두 번째는 특정 영역(주로 함수)에만 전용으로 사용할 수 있는 영역입니다.
표준 ES2015 이후 JavaScript 에는 키워드에 따라 변수를 선언하는 세 가지 방법이 있습니다. 첫 번째는 앞서 설명한 것처럼 var 키워드로 선언된 변수는 현재 함수 본문으로 범위가 지정됩니다. ES6 표준에서는 var 문과 반대로 const 및 let 문으로 선언된 변수는 블록으로만 범위가 지정되는 좀 더 인간적인 방법으로 변수를 선언할 수 있도록 허용했습니다. 그러나 JS는 다른 언어와 비교했을 때 const 문을 매우 비정상적으로 취급합니다. 프로그래밍 언어 - 를 사용하면 지속되는 값 대신 값에 대한 지속 참조를 유지합니다. 즉, const 문으로 선언된 객체의 프로퍼티는 수정할 수 있지만 이 변수의 참조를 덮어쓸 수는 없습니다. 어떤 사람들은 ES6에서 var를 대체할 수 있는 문이 실제로는 let 문이라고 말합니다. 아니요, 그렇지 않으며 var 문은 철회되지도 않을 것입니다. 좋은 관행은 var 문을 사용하지 않는 것입니다. 왜냐하면 대부분 문제가 더 많이 발생하기 때문입니다. 결국, 참조를 수정해야 할 때까지는 const 문을 남용해야 하고, 그 다음에는 let 문을 사용해야 합니다.
다음부터 시작하겠습니다. 코드:
(() => {
for (var i = 0; i {
console.log(`"i"의 값: ${i}`);
}, 1000);
}
})();
이를 살펴보면, for 루프가 i 값을 반복하고 1초 후에 반복자의 값을 기록하는 것처럼 보입니다: 1, 2, 3, 4, 5. 그렇지 않습니다. 위에서 언급했듯이 var 문은 모든 함수 본문에 대해 변수 값을 유지하는 것이므로 두 번째, 세 번째 등의 반복에서 i 변수의 값이 다음 값으로 대체됩니다. 마지막으로 루프가 종료되고 시간 초과 틱이 5, 5, 5, 5, 5로 표시됩니다. 반복기의 현재 값을 유지하는 가장 좋은 방법은 대신 let 문을 사용하는 것입니다:
(() => {
for (let i = 0; i {
console.log(`"i"의 값: ${i}`);
}, 1000);
}
})();
위의 예제에서는 현재 반복 블록에서 i 값의 범위를 유지하며, 이 변수를 사용할 수 있는 유일한 영역이며 이 영역 외부에서 이 변수를 재정의할 수 없습니다. 이 경우 결과는 예상대로 1 2 3 4 5입니다. var 문을 사용하여 이 상황을 처리하는 방법을 살펴보겠습니다:
(() => {
for (var i = 0; i {
setTimeout(() => {
console.log(`"j"의 값: ${j}`);
}, 1000);
})(i);
}
})();
var 문은 함수 블록 안에 값을 유지하는 것이므로 인자(반복기의 현재 상태 값)를 받는 정의된 함수를 호출한 다음 무언가를 수행해야 합니다. 선언된 함수 외부의 어떤 것도 j 값을 재정의하지 않습니다.
제가 발견한 가장 자주 저지르는 범죄는 구조체의 힘을 무시하고 다른 코드에서도 수정되는 구조체의 속성을 변경하는 것과 관련이 있습니다. 간단히 살펴보겠습니다:
const DEFAULT_VALUE = {
favoriteBand: 'The Weeknd'
};
const currentValue = DEFAULT_VALUE;
const bandInput = document.querySelector('#favorite-band');
const restoreDefaultButton = document.querySelector('#restore-button');
bandInput.addEventListener('input', () => {.
currentValue.favoriteBand = bandInput.value;
}, false);
restoreDefaultButton.addEventListener('click', () => {.
currentValue = DEFAULT_VALUE;
}, false);
처음부터 기본 속성을 가진 모델이 객체로 저장되어 있다고 가정해 봅시다. 입력 값을 기본값으로 복원하는 버튼을 만들고 싶습니다. 입력을 몇 가지 값으로 채운 후 모델을 업데이트합니다. 잠시 후 기본 선택이 더 낫다고 생각하여 기본값으로 복원하려고 합니다. 하지만 버튼을 클릭해도 아무 일도 일어나지 않습니다. 왜 그럴까요? 참조된 값의 힘을 무시했기 때문입니다.
이 부분: const currentValue = DEFAULTVALUE는 JS에 다음을 알려줍니다: DEFAULT에 대한 참조를 가져옵니다.VALUE 값과 함께 currentValue 변수를 할당합니다. 실제 값은 메모리에 한 번만 저장되며 두 변수 모두 이를 가리키고 있습니다. 한 곳에서 일부 프로퍼티를 수정하면 다른 곳에서도 수정해야 합니다. 이런 상황을 피할 수 있는 몇 가지 방법이 있습니다. 그 중 하나가 스프레드 연산자입니다. 코드를 수정해 봅시다:
const DEFAULT_VALUE = {
favoriteBand: 'The Weeknd'
};
const currentValue = { ...DEFAULT_VALUE };
const bandInput = document.querySelector('#favorite-band');
const restoreDefaultButton = document.querySelector('#restore-button');
bandInput.addEventListener('input', () => {.
currentValue.favoriteBand = bandInput.value;
}, false);
restoreDefaultButton.addEventListener('click', () => {.
currentValue = { ...DEFAULT_VALUE };
}, false);
이 경우 스프레드 연산자는 다음과 같이 작동합니다. 객체에서 모든 속성을 가져와서 그 속성으로 채워진 새 객체를 만듭니다. 덕분에 currentValue와 DEFAULT_VALUE의 값이 더 이상 메모리에서 같은 위치를 가리키지 않으며, 둘 중 하나에 적용된 모든 변경 사항이 다른 값에 영향을 미치지 않습니다.
자, 이제 질문은 매직 스프레드 연산자를 사용하는 것이 전부인가요? 이 경우에는 그렇습니다. 하지만 우리의 모델은 이 예시보다 더 복잡할 수 있습니다. 중첩된 객체, 배열 또는 기타 구조체를 사용하는 경우 최상위 참조 값의 확산 연산자는 최상위 레벨에만 영향을 미치며 참조된 속성은 여전히 메모리에서 같은 위치를 공유하게 됩니다. 이 문제를 처리하는 솔루션은 여러 가지가 있으며, 모두 필요에 따라 다릅니다. 모든 깊이 수준에서 객체를 복제하거나, 더 복잡한 작업에서는 거의 고통 없이 불변 코드를 작성할 수 있는 immer와 같은 도구를 사용할 수 있습니다.
범위와 값 유형에 대한 지식을 혼합하여 사용할 수 있나요? 물론 가능합니다! 이 두 가지를 모두 사용하는 무언가를 만들어 봅시다:
const useValue = (defaultValue) => {
const value = [...defaultValue];
const setValue = (newValue) => {
value.length = 0; // 배열을 지우는 까다로운 방법
newValue.forEach((item, index) => { {
value[index] = item;
});
// 다른 작업 수행
};
반환 [value, setValue];
};
const [animals, setAnimals] = useValue(['cat', 'dog']);
console.log(animals); // ['cat', 'dog']
setAnimals(['말', '소']);
console.log(animals); // ['말', '소']);
이 코드가 어떻게 작동하는지 한 줄씩 설명해 보겠습니다. 사용 값 함수는 기본값 인수를 기반으로 배열을 생성하고, 변수와 또 다른 함수인 수정자를 생성합니다. 이 수정자는 기존 값에 까다로운 방식으로 적용되는 새로운 값을 취합니다. 함수가 끝나면 값과 그 수정자를 배열 값으로 반환합니다. 다음으로, 생성된 함수를 사용하여 반환 값으로 animals와 setAnimals를 선언합니다. 수정자를 사용하여 함수가 동물 변수에 영향을 미치는지 확인합니다 - 네, 작동합니다!
하지만 잠깐만요, 이 코드에서 정확히 무엇이 그렇게 멋진가요? 참조는 모든 새 값을 유지하며 다음과 같이 이 수정자에 자신만의 로직을 삽입할 수 있습니다. 일부 API 또는 생태계의 일부 를 사용하여 데이터 흐름을 강화할 수 있습니다. 이러한 까다로운 패턴은 프로그래밍의 함수형 패러다임이 코드를 덜 복잡하고 다른 프로그래머가 쉽게 읽을 수 있도록 하는 최신 JS 라이브러리에서 자주 사용됩니다.
언어 메커니즘이 내부에서 어떻게 작동하는지 이해하면 보다 의식적이고 가벼운 코드를 작성할 수 있습니다. JS가 저수준 언어가 아니고 메모리가 할당되고 저장되는 방식에 대해 어느 정도 지식이 있더라도 객체를 수정할 때 예기치 않은 동작을 주시해야 합니다. 반면에 값의 복제를 남용하는 것이 항상 올바른 방법은 아니며 잘못된 사용법은 장점보다 단점이 더 많습니다. 데이터 흐름을 계획하는 올바른 방법은 애플리케이션의 로직을 구현할 때 필요한 것이 무엇이고 어떤 장애물이 발생할 수 있는지 고려하는 것입니다.
자세히 읽어보세요: