정보실

웹학교

정보실

javascript 자바 스크립트로 캡슐화

본문

캡슐화는 데이터의 번들링과 해당 데이터에 대한 액세스가 번들 외부에서 제한되거나 Alan Kay가 설명하는 것처럼 "지역적으로 유지 및 보호 및 상태 프로세스 숨기기"라는 데이터에 작용하는 방법입니다. 즉, 객체는 상태를 비공개로 저장하며 객체의 메서드만 객체를 변경할 수 있습니다.


https://medium.com/javascript-scene/encapsulation-in-javascript-26be60e325b4 


캡슐화 된 상태를 변경하려는 경우 일부 객체의 props에 손을 뻗어 직접 변경하지 않습니다. 대신 객체에서 메소드를 호출하면 객체가 상태를 업데이트하여 응답합니다. 예를 들어, 뷰의 데이터 모델을 직접 조작하는 대신 Redux를 사용하여 애플리케이션을 구축하는 경우 작업 객체라는 메시지를 데이터 저장소에 발송합니다. Redux는 해당 메시지가 언제 어떻게 처리되는지 타이밍을 제어합니다. 해당 메시지의 타이밍 및 처리는 예측 가능하고 결정적인 상태 업데이트를 생성하기 위해 정확하게 제어됩니다. 같은 순서로 같은 메시지가 주어지면 Redux는 항상 같은 상태를 생성합니다.


마찬가지로 useState 또는 setState로 React 구성 요소 상태를 업데이트 하려는 경우 이러한 변경 사항이 구성 요소 상태를 직접 조작하지 않습니다. 대신 렌더링 주기가 완료된 후 적용되는 잠재적인 상태 변경을 대기열에 넣을 수 있습니다. React의 구성 요소 상태를 직접 설정하지 마십시오. React does.


캡슐화가 필요한 이유 


1960 년대와 1970 년대에 프로그래머들은 비 결정적 순서로 실행되는 서로 다른 작업간에 동일한 메모리 리소스를 공유하려고 함으로써 발생하는 타이밍 종속성 및 리소스 충돌 문제에 시달리고 있었습니다. 또한 코드를 프로그램 상태의 특정 데이터 구조 표현에 결합해야 한다는 필요성 때문에 좌절했습니다.


1970 년대 Alan Kay는 MIT에서 1961 년에서 1963 년 사이에 개발 된 Ivan Sutherland의 Sketchpad 논문의 합성 데이터 구조에서 영감을 얻었으며 1960 년대에는 Ole-Johan Dahl과 Kristen Nygaard가 오슬로의 노르웨이 컴퓨팅 센터에서 개발 한 Simula에서 영감을 얻었습니다. Alan Kay는 ARPAnet 연구 및 디자인에 참여했으며 과학 및 수학에 대한 배경 지식이 있으며 특히 세포가 막으로 캡슐화 되어 메시지 전달을 통해 전달되는 방식에 영감을 받았습니다.


이러한 모든 아이디어는 OOP의 토대 인 캡슐화 및 메시지 전달의 토대를 형성하기 위해 함께 모였습니다.


공유 변경 가능 상태의 문제점은 입력 상태가 다른 명령어의 출력 상태에 의존하고 모든 종류의 동시성이 발생하면 경쟁 조건을 생성한다는 것입니다. 명령어 호출 순서를 변경하면 결과가 변경 될 수 있습니다. 시퀀싱에서 어떤 종류의 비결정론을 혼합하면 결과는 혼란입니다. 때로는 효과가 있습니다. 때로는 그렇지 않습니다.


캡슐화는이 문제를 해결하는 한 가지 방법입니다.


캡슐화는 또 다른 흥미로운 문제를 해결합니다. 처리해야 하는 데이터 집계가 있다고 가정하십시오. 이를 수행하는 한 가지 방법은 먼저 데이터를 나타내는 데이터 구조를 결정하는 것입니다. 구현 세부 사항 (배열이라고 합시다)으로 시작하고 이를 사용하는 모든 것이 구조를 인식하고 데이터 구조와 긴밀한 연결을 만들어 나중에 구현을 변경하기 어렵게 만들 수 있습니다. 결국 스트림, 트리 또는 다른 데이터 구조로 배열을 교체하려면 어떻게 해야 합니까? 모든 것이 구현을 알고 있으면 너무 늦을 수 있습니다.


그러나 공개 인터페이스 뒤에 구현 세부 사항을 캡슐화 한 후 오브젝트를 사용하는 모든 것이 공용 인터페이스를 통해서만 수행하므로 나중에 구현 세부 사항을 변경하는 것이 더 쉽습니다. 예를 들어, 숫자를 저장하는 데이터 구조가 있고 저장된 값에 2를 곱하는 방법이 필요하다고 가정하십시오.


// Only works for arrays
const doubleAllImperative = data => {
const doubled = [];
for (let i = 0, length = data.length; i < length; i++) {
doubled[i] = data[i] * 2;
}
return doubled;
};
// Same as above, but works for anything with the
// map operation.
const doubleAllInterface = data => data.map(x => x * 2);
const box = value => ({
map: f => box(f(value)),
toString: () => `box(${ value })`
});
console.log(
doubleAllInterface([2,3]), // [4, 6]
doubleAllInterface(box(2)).toString(), // box(4)
);


캡슐화는 공유 가능한 변경 가능 상태로 인한 버그를 방지하고 구성 요소와 그에 의존하는 데이터 구조 간의 밀접한 연결을 줄이는 데 도움이 되는 강력한 도구입니다. 소프트웨어 설계의 세 가지 주요 원칙을 준수하는 데 도움이 됩니다.

  • 공유 가능한 변경 가능 상태를 피하십시오. "비결정론 = 병렬 처리 + 변경 가능 상태"— 스칼라 프로그래밍 언어 디자이너 Martin Odersky
  • “구현이 아닌 인터페이스로의 프로그램”— Gang of Four,“디자인 패턴 : 재사용 가능한 객체 지향 소프트웨어의 요소”
  • 요구 사항을 조금만 변경하면 소프트웨어도 그에 따라 약간만 변경되어야 합니다.”— N. D. Birrell, M. A. Ould,“소프트웨어 개발을 위한 실용적인 핸드북”

JavaScript의 관용적 캡슐화 


Brendan Eich가 1995 년 릴리스에서 절정에 이르는 10 일 동안 JavaScript를 만들었을 때, 그는 두 가지 아이디어를 염두에 두었습니다.

  • 브라우저의 구성표
  • 자바처럼 보인다

Scheme은 함수형 프로그래밍 언어 인 LISP의 방언으로 1958 년에 시작된 우아한 작은 언어입니다. Scheme은 매우 유연한 고차 함수와 클로저를 지원하기 때문에 무게보다 훨씬 뛰어납니다.


Java는 클래스 기반 객체 지향 언어입니다. Java에서 JavaScript는 생성자 함수, (종종) 클래스 및 새로운 키워드 (다른 것들 중에서)라는 개념을 얻었습니다.


Brendan Eich는 자체 프로그래밍 언어인 프로토 타입에서 세 번째로 큰 영감을 얻었습니다.이 프로토 타입은 JavaScript의 상속 개념이 유사하지만 멀리 떨어져 있는 사촌 인 Java보다 훨씬 강력하고 유연합니다. 그러나 이것은 또 다른 이야기입니다.


23 년이 지난 후에도 이 패러다임 용광로는 여전히 약간의 오해입니다. 이러한 일반적인 오해 중 하나는 캡슐화와 관련이 있습니다.


JavaScript에서 객체에 대한 관용적 캡슐화를 시작하기 전에 먼저 강력한 캡슐화 형식이 아닌 일반적인 규칙을 다루고 싶습니다. 오래 전에 많은 JavaScript 개발자가 클로저에 대해 배우기 전에 일부 JavaScript 개발자는 JavaScript 객체 (및 그 이후의 클래스)에 개인 속성에 대한 메커니즘이 포함되어 있지 않음을 알게 되었습니다.


ECMAScript 사양 프로세스의 3 단계에서 개인 필드 제안을 작성하기 전에는 JavaScript에서 객체의 개인 속성을 만들 수 있는 방법이 없었습니다. JavaScript에서 객체에 대한 실제 데이터 프라이버시를 이미 지원하는 클로저로 돌아 가지 않고 일부 개발자는 개인 속성과 메서드를 밑줄로 접두어로 표시하기로 결정했습니다.


여러 가지 이유로 문제가 있습니다.


주요 변경 사항 : 내부 속성 및 메서드는 공용 속성 및 메서드보다 더 자주 변경되는 경향이 있습니다. 밑줄이 있는 많은 메소드 (예 : myComponent._handleClick)의 경우 API 사용자가 직접 호출하도록 의도하지 않은 메소드를 나타냅니다. 대신 내부적으로 만 사용되므로 구현이 변경되거나 완전히 삭제 된 경우 이러한 개발자는 이를 근본적인 변경으로 간주하지 않습니다.


불행히도 사용자에게는 많은 새로운 개발자가 밑줄 접두사 규칙을 알지 못하므로 어쨌든 속성을 사용할 수 있습니다. 숙련 된 개발자는 종종 그 의미가 무엇인지 알고 있지만“내가 하는 일을 알고 있습니다.”라고 생각합니다. 특히 현재 문제에 대한 명확한 해결책을 제시하는 경우에는 사용하십시오. 다시 말해서, 많은 사람들이 이 협약을 무시하고, 그로 인해 필요한 것보다 더 많은 변화가 일어납니다.


누출 된 구현 세부 정보 : 위의 예를 기억하십시오. 여기서 배열 만 지원하기 시작했지만 이제는 지원을 스트림으로 확장하려고 합니까? 사용자가 기본 데이터 구조에 직접 액세스 할 수 있는 경우 해당 데이터 구조에 대한 종속성을 만들 수 있으므로 스트림이 처음 발생할 때 코드가 깨질 때 놀라게 됩니다.


해커를 위한 확장 된 공격 영역 : 특히 퍼블릭 API에서 코드를 사용하는 데 필요한 것보다 더 많은 Surface API를 추가하면 공격자가 악용 할 수 있는 영역이 확장됩니다. 공격 영역을 반드시 필요한 것으로 만 제한하는 소프트웨어 보안의 가장 중요한 원칙 중 하나입니다. 실제로 내부적으로 만 사용하려는 경우 외부에 노출해서는 안됩니다.


자체 문서화 코드 : 공개 API는 가능한 한 자체 문서화 되어야 합니다. 이를 수행하는 한 가지 방법은 사용자가 사용하려는 메소드와 속성 만 노출하는 것입니다. 그렇게 하면 사용자는 지원되지 않고 문서화 되지 않은 방법을 사용하려고 하지 않습니다. 밑줄 표기 규칙을 사용하는 경우 사용자는 그 의미가 무엇인지 알고 자신이 사용하지 않을 것이라는 것을 이해해야 합니다. 캡슐화를 사용하면 걱정할 필요가 없습니다. 액세스 권한이 없는 것을 사용할 수 없습니다.


자바 스크립트에서 실제 캡슐화 


물론, 기능적 군중이 알고 있듯이 JavaScript는 실제 데이터 캡슐화를 모두 지원했습니다. JavaScript로 비공개 데이터를 선언하는 것은 매우 쉽습니다.


클로저 사용 


const createCounter = () => {
// A variable defined in a factory or constructor function scope
// is private to that function.
let count = 0;
return ({
// Any other functions defined in the same scope are privileged:
// These both have access to the private `count` variable
// defined anywhere in their scope chain (containing function
// scopes).
click: () => count += 1,
getCount: () => count.toLocaleString()
});
};
const counter = createCounter();counter.click();
counter.click();
counter.click();
console.log(
counter.getCount()
);


권한 있는 방법은 포함 함수의 범위 (어휘 환경 이라고도 함) 내의 개인 데이터에 액세스 할 수 있는 방법입니다. 특권 함수 및 메소드는 포함 함수가 리턴 된 후에도 포함 함수의 변수에 대한 참조 기반 액세스를 갖습니다. 이러한 참조는 실시간이므로 포함 함수에서 상태가 변경되면 참조에 액세스 할 수 있는 모든 권한 있는 함수에 대한 변경이 변경됩니다. 즉, counter.click()을 호출하면 counter.getCount()가 보는 값이 변경됩니다.


함수형 믹스인을 사용하여 비공개 상태를 상속 할 수도 있습니다.


개인 필드 사용 


이 글을 쓰는 시점에서 3 단계 기능이 활성화 된 개인 필드는 babel로 제공됩니다. Chrome, Opera, Android 브라우저 및 Android 용 Chrome에서 기본적으로 지원되므로 다음과 같은 작업을 수행 할 수 있습니다.


class Counter {
#count = 0

click () {
this.#count += 1;
}
getCount () {
return this.#count.toLocaleString()
}
}
const myCounter = new Counter();myCounter.click();
myCounter.click();
myCounter.click();
console.log(
myCounter.getCount()
);


클래스와 새로운 클래스 필드 사양을 사용하는 것보다 클로저 기반 캡슐화를 사용하여 팩토리 기능과 진정한 개인 정보 보호를 여전히 선호하지만 실제로 캡슐화가 필요한 경우 클로저와 클래스 필드가 밑줄보다 훨씬 낫습니다. 규칙에 의존하는 대신 진정한 캡슐화를 적용합니다.


다음 단계 


EricElliottJS.com 회원은 이 강의의 비디오 버전과 함께 실제 앱을 제작하는 방법, 비디오 빠른 팁 및 웹 캐스트 녹화 및 기타 자료의 아카이브를 볼 수 있습니다.



  • 트위터로 보내기
  • 페이스북으로 보내기
  • 구글플러스로 보내기
  • 카카오톡으로 보내기

페이지 정보

조회 27회 ]  작성일19-11-05 21:20

웹학교