{"id":18951248,"url":"https://github.com/hmmhmmhm/capsulable","last_synced_at":"2026-05-17T01:41:04.163Z","repository":{"id":98551352,"uuid":"162163120","full_name":"hmmhmmhm/capsulable","owner":"hmmhmmhm","description":"💊 A module that helps developers easily encapsulate classes in nodejs.","archived":false,"fork":false,"pushed_at":"2018-12-21T18:03:24.000Z","size":32,"stargazers_count":4,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-02T00:34:09.307Z","etag":null,"topics":["encapsulation","encapsule","javascript","nodejs","object-oriented-javascript","oop","package","private-variables"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hmmhmmhm.png","metadata":{"files":{"readme":"README-KR.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-17T16:57:43.000Z","updated_at":"2020-02-21T03:35:37.000Z","dependencies_parsed_at":"2023-05-29T14:15:19.180Z","dependency_job_id":null,"html_url":"https://github.com/hmmhmmhm/capsulable","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/hmmhmmhm/capsulable","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hmmhmmhm%2Fcapsulable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hmmhmmhm%2Fcapsulable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hmmhmmhm%2Fcapsulable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hmmhmmhm%2Fcapsulable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hmmhmmhm","download_url":"https://codeload.github.com/hmmhmmhm/capsulable/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hmmhmmhm%2Fcapsulable/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266026353,"owners_count":23866033,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["encapsulation","encapsule","javascript","nodejs","object-oriented-javascript","oop","package","private-variables"],"created_at":"2024-11-08T13:27:10.407Z","updated_at":"2026-05-17T01:40:59.144Z","avatar_url":"https://github.com/hmmhmmhm.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Capsulable\n\n[![Build Status](https://travis-ci.org/hmmhmmhm/capsulable.svg?branch=master)](https://travis-ci.org/hmmhmmhm/capsulable)\n\n```\n순수 자바스크립트 클래스 캡슐화 구현 모듈\n```\n\u003cimg src=\"https://image.flaticon.com/icons/svg/822/822143.svg\" alt=\"icon\" width=\"150\"/\u003e\n\n캡슐러블 모듈은 자바스크립트 클래스에서 쉽게 접근제한자 개념을 사용할 수 있게 돕습니다. 클로저 개념을 통해 캡슐화 및 은닉화 개념을 구현합니다.\n\n## 설치방법\n\n```\nnpm i capsulable --save\n```\n\n--------------------------------------\n\n## 왜 이 모듈이 필요한가요?\n\n자바스크립트의 기본 변수개념에는 _ 와 __으로 표현하는 prvate 및 protected 변수 개념이 존재하지만, 타 클래스에서 해당 변수를 읽는 것을 막지는 못합니다. 또한 protected 개념의 경우 상속 이후 접근제한의 개념이 아직까지 모호합니다. 내부 변수의 은닉성이 필요한 클래스에는 클래스 변수에 보다 명확한 접근제한자 개념이 필요합니다.\n\n## 그냥 Closure 나 Symbol 으로 매번 만들면 되지 않나요?\n\n물론 그렇게 해도 상관은 없습니다. 하지만 클래스 인스턴스 마다 각기 다른 Private 변수 공간을 만들고 싶거나(`외부에서 접근할 수 없는`), 상속 이후에도 각기 인스턴스마다 다른 Protected 변수 공간을 만들고 싶거나, 특정 패키지 사이에서만 접근가능한 Protected Static 변수공간을 매번 직접 만든다면 다소 힘이 빠지는 작업일 수 있습니다. Capsulable 은 안전한 은닉성과 확장성, 그리고 상속 클래스 및 패키지 간의 Protected 및 Protected Static 변수 개념을 제공합니다.\n\n--------------------------------------\n\n## 캡슐화 된 클래스 정의방법\n\nCapsulable 은 `class` 의 `constructor` 함수를 통해서 데이터 필드를 공유합니다. (이는 즉 Capsulable 이 최종적으로 항상 자식 클래스로 구성 됨을 의미합니다.) 캡슐화 하길 원하는 클래스는 사전에 Capsulable 을 통해서 constructor 에서 데이터 필드를 상속 받아야합니다. 예시 상 문법은 ES5로 표기되나, Babel을 통한 ES6 문법으로 구현해도 됩니다.\n\n### 클래스 정의 예제\n\n```js\n// A.js\nconst Capsulable = require('capsulable')\nconst Field = Capsulable()\n\nclass A {\n    constructor(_field){\n        // 데이터 필드를 구성합니다.\n        Field(this, _field)\n\n        // 아래와 같은 코드를 통해서\n        // 클래스 내 함수 작성 시\n        // 데이터 필드에 접근 가능합니다.\n        Field(this).private\n        Field(this).protected\n        Field(this).protectedStatic\n    }\n}\n\nmodule.exports = A\n```\n\n\n### 클래스 캡슐화 예제\n```js\n// index.js\nconst Capsulable = require('./capsulable')\nconst A = require('./A')\n\n// Capsulable에 클래스 원형을 넣으면\n// 캡슐화 된 클래스가 반환됩니다.\nconst SharedA = Capsulable(A)\n\n// 캡슐화 된 클래스를 통해서\n// 인스턴스를 생성할 수 있습니다.\nlet sharedA = new SharedA()\n```\n--------------------------------------\n\n\n## Field 변수 접근법\n```js\nField(this).private\nField(this).protected\nField(this).protectedStatic\n```\nCapsulable 모듈은 각 클래스마다 데이터 필드라는 것을 생성하는데, 이 데이터 필드에는 `private`, `protected`, `protectedStatic` 변수형태(`getter`)로 제공됩니다. 데이터 필드는 클래스의 인스턴스마다 생성되므로 데이터 필드 접근시에는 인스턴스가 필드에 인자로 주어져야 합니다.\n\n\n### Private 필드 사용법\n```js\nField(this).private.set(key, value) // =\u003e Boolean\nField(this).private.exist(key)  // =\u003e Boolean\nField(this).private.get(key) // =\u003e Object\nField(this).private.getAll() // =\u003e Object\nField(this).private.remove(key) // =\u003e Boolean\nField(this).private.removeAll() // =\u003e Boolean\n```\nPrivate 필드는 각 인스턴스마다 격리된 공간을 가지며 타 인스턴스가 접근할 수 없도록 차단됩니다. 개발자가 같은 클래스 내에서 `Field(this)` 를 다른 인스턴스에게 노출시킬 수 있는 코드를 작성하지만 않는다면 변수의 은닉성은 유지될 것입니다.\n\n\n### Protected 필드 사용법\n```js\nField(this).protected.set(className, key, value) // =\u003e Boolean\nField(this).protected.exist(className, key)  // =\u003e Boolean\nField(this).protected.get(className, key) // =\u003e Object\nField(this).protected.getAll(className) // =\u003e Object\nField(this).protected.remove(className, key) // =\u003e Boolean\nField(this).protected.removeAll(className) // =\u003e Boolean\n```\nProtected 필드는 각 인스턴스마다 격리된 공간을 가지며, 접근하고자 하는 className 을 함수 사용시 맨 처음 인자로 제공해야합니다. Protected 필드는 클래스 상속이 발생했을때 부모 클래스와 자식 클래스 사이에만(또한 같은 패키지) 공유되어야 하는 데이터 운용에 적합합니다.\n\n\n### Protected Static 필드 사용법\n```js\nField(this).protectedStatic.set(className, key, value) // =\u003e Boolean\nField(this).protectedStatic.exist(className, key)  // =\u003e Boolean\nField(this).protectedStatic.get(className, key) // =\u003e Object\nField(this).protectedStatic.getAll(className) // =\u003e Object\nField(this).protectedStatic.remove(className, key) // =\u003e Boolean\nField(this).protectedStatic.removeAll(className) // =\u003e Boolean\n```\nProtected Static 필드는 각 인스턴스와 공유된 공간을 가지며, 접근하고자 하는 className을 함수 사용시 맨 처음 인자로 제공해야합니다. Protected Static 필드는 부모클래스와 자식클래스가 같은 데이터를 운용하면서 동시에 모든 클래스 인스턴스들이 같은 데이터를 공유해야 하는 데이터 운용에 적합합니다.\n\n--------------------------------------\n\n## 캡슐화 된 파생 클래스 정의방법 \n\n파생 클래스를 생성했을 때 부모클래스와 자식클래스가 `Protected` 와 `ProtectedStatic` 형태의 데이터 필드를 공유하게 하려면 아래와 같은 방법을 통해 구현할 수 있습니다. (상속을 진행해도 `Private` 데이터 필드는 공유되지 않습니다.)\n\n### 파생클래스 정의\n```js\n// B.js\nconst Capsulable = require('capsulable')\nconst A = require('./A')\nconst Field = Capsulable()\n\nclass B extends A {\n    constructor(_field){\n        // 파생 클래스는 constructor 를 통해서\n        // 받은 필드 객체를 super 키워드를 통해서\n        // 반드시 부모 클래스로 넘겨야합니다.\n        super(_field)\n\n        // 데이터 필드를 구성합니다.\n        Field(this, _field)\n    }\n}\n\nmodule.exports = B\n```\n\n\n### 파생클래스 캡슐화\n```js\n// index.js\nconst Capsulable = require('./capsulable')\nconst B = require('./B')\n\n// Capsulable에 클래스 원형을 넣으면\n// 캡슐화 된 클래스가 반환됩니다.\nconst SharedB = Capsulable(B)\n\n// 캡슐화 된 클래스를 통해서\n// 인스턴스를 생성할 수 있습니다.\nlet sharedB = new SharedB()\n```\n--------------------------------------\n\n\n## 이미 캡슐화 된 클래스의 파생방법 \n\n이미 원 클래스를 Capsulable 로 캡슐화 시킨 이후에 다시한번 다른 클래스에서 해당 클래스를 파생받고 싶은 경우, 아래와 같은 방법을 통해 이를 구현할 수 있습니다.\n\n```js\n// C.js\nconst Capsulable = require('./capsulable')\nconst B = require('./B')\nconst SharedB = Capsulable(B)\nconst Field = Capsulable()\n\nclass C extends SharedB{\n    constructor(inherit){\n        // 부모 클래스인 Capsulable 에서\n        // 데이터 필드를 역으로 받아옵니다.\n        let hook = {}\n        super(hook)\n\n        // 데이터 필드를 구성합니다.\n        Field(this, hook.field)\n\n        // 이후 다시한번 파생이 가능하도록,\n        // 파생됐을 시 객체를 복사해줍니다.\n        if(inherit) Capsulable(hook, inherit)\n    }\n}\n\nmodule.exports = C\n\n// index.js\nconst Capsulable = require('./capsulable')\nconst C = require('./C')\n\n// Capsulable에 클래스 원형을 넣으면\n// 캡슐화 된 클래스가 반환됩니다.\nconst SharedC = Capsulable(C)\n\n// 캡슐화 된 클래스를 통해서\n// 인스턴스를 생성할 수 있습니다.\nlet sharedC = new SharedC()\n```\n--------------------------------------\n\n\n## 패키지 공간 생성방법\n\nCapsulable 에는 Java 의 Package 개념이 미약하게 구현되어 있습니다. 아래처럼 여러 클래스를 한번에 캡슐화 할 경우, 해당 클래스들의 인스턴스들은 `Protected` 데이터 필드와 `Protected Static` 데이터 필드가 자동으로 공유됩니다.\n\n\n#### 주의: 하나의 패키지에 등록되는 여러 클래스 원형들은 서로 이름이 겹칠 수 없습니다.\n```js\n// index.js\nconst A = require('./A')\nconst B = require('./B')\nconst C = require('./C')\n\n// Capsulable 에 인자로\n// 배열에 클래스 원형들을 담아 전달하면\n// 이를 하나의 패키지로 구성합니다.\nlet packages = Capsulable([A, B, C])\n\n// 아래와 같이 packages.* 를 통해\n// 캡슐화 된 클래스 원형에 접근할 수 있습니다.\nlet packA = new packages.A()\nlet packB = new packages.B()\nlet packC = new packages.C()\n```\n\n\n### 패키지 생성 이후 클래스 로드\n```js\n// index.js\nconst A = require('./A')\nconst B = require('./B')\nconst C = require('./C')\nconst D = require('./A')\n\n// Capsulable 에 인자로\n// 배열에 클래스 원형들을 담아 전달하면\n// 이를 하나의 패키지로 구성합니다.\nlet packages = Capsulable([A, B, C])\n\n// packages._load 함수를 통해서\n// 원하는 클래스 원형을 패키지 공간 구성\n// 이후에도 해당 패키지에 추가시킬 수 있습니다.\npackages._load(D)\n\n// packages.* 를 통해서\n// 추가된 클래스 원형에 접근 가능합니다.\nlet packD = new packages.D()\n```\n\n### 패키지 생성 이후 클래스 언로드\n```js\npackages._load(D)\nlet packD = new packages.D()\n\n// packages._unload 함수를 통해서\n// 패키지에 포함되어 있는 클래스 원형을\n// 해당 패키지에서 삭제시킬 수 있습니다.\npackages._unload(D)\n\n// 이미 패키지에 추가된 클래스 원형으로\n// 클래스 인스턴스가 구성된 경우\n// 해당 인스턴스에는 영향이 없습니다.\n```\n--------------------------------------\n\n## Final Class 구현\n\n클래스 원형의 캡슐화 이후 파생 클래스를 더 이상 만들길 원치 않는 경우 Capsulable 함수 사용시 2번째 인자로 final 모드를 적용시킴으로써 파생 클래스와 데이터 필드를 공유하지 않도록 할 수 있습니다. (클래스의 파생을 완전히 막지는 못하나, 데이터 필드가 공유되는 것을 막을 수는 있습니다.)\n\n```js\n// index.js\nconst Capsulable = require('./capsulable')\nconst A = require('./A')\n\n// 2번째 인자로 final을 적으면\n// 파생클래스와 데이터 필드를 공유하지 않습니다.\nconst SharedA = Capsulable(A, 'final')\n\n// 캡슐화 된 클래스를 통해서\n// 인스턴스를 생성할 수 있습니다.\nlet sharedA = new SharedA()\n```\n\n--------------------------------------\n\n## 작업 목록\n\n- [x] Private 변수 구현\n- [x] Protected 변수 구현\n- [x] Protected Static 변수 구현\n- [x] 각 변수의 단계별 초기화 과정 구현\n- [x] 통합 데이터 필드 구현\n- [x] 상속기반의 유출없는 데이터 필드 공유 구현\n- [x] 패키지간 Protected 데이터 공유 구현\n- [x] Helper 를 통한 Private 변수 인자단축 구현\n- [x] 캡슐화 이후 상속 클래스 추가 및 재 캡슐화 구현\n- [x] 테스트 유닛 구현\n- [x] assert 가변모듈화 하여 종속성 완전 배제 구현\n- [x] final 클래스 구현\n- [ ] final 변수 구현\n- [ ] 캡슐화 된 클래스의 constructor 가변변수 인자 구현\n- [ ] 캡슐화 된 클래스의 constructor 가변변수의 파생클래스 공유 구현\n--------------------------------------\n\n## LICENSE\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhmmhmmhm%2Fcapsulable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhmmhmmhm%2Fcapsulable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhmmhmmhm%2Fcapsulable/lists"}