generated from muhandojeon/study-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
296 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,295 @@ | ||
## 생성자 패턴(Constructor Pattern) | ||
|
||
> 객체가 새로 만들어진 뒤 초기화하는 데에 사용되는 특별한 메서드 | ||
|
||
### 객체 생성 | ||
|
||
자바스크립트에서 새로운 객체를 만들 때는 일반적으로 아래 3가지 방법을 사용한다. | ||
|
||
```js | ||
// 1. 객체 리터럴 | ||
const newObject = {}; | ||
|
||
// 2. Object.create() 메서드 사용 | ||
const newObject = Object.create(Object.prototype); | ||
|
||
// 3. Object 생성자 | ||
const newObject = new Object(); | ||
``` | ||
|
||
### 클래스 | ||
|
||
- ES2015에서 도입 | ||
- 객체 템플릿을 정의하고 캡슐화 및 상속을 구현할 수 있게 해준다. | ||
- 새 객체를 초기화하는 **constructor()** 라는 이름의 메서드를 가지고 있어야 한다. | ||
- **new** 키워드로 생성자를 호출 | ||
- 생성자 내부에서 사용된 **this** 키워드는 새로 생성된 해당 객체(instance) | ||
- 단점 1: 상속이 어렵다. | ||
- 단점 2: 객체 생성시 내부 함수를 매번 새로 정의하는데, 인스턴스들은 모두 동일한 함수를 공유해야 해서 불편이 있다. | ||
|
||
|
||
### 프로토타입 | ||
|
||
- 프로토타입 객체는 함수나 클래스 등 **특정 객체의 모든 인스턴스 내에 공통 메서드를 쉽게 정의**할 수 있게 한다. | ||
- *나의 생각 : prototype 건드는 건 악질 안티패턴이라고 하지 않았나?* | ||
|
||
|
||
## 모듈 패턴(Module Pattern) | ||
|
||
> 프로젝트를 구성하는 코드 단위를 체계적으로 분리 및 관리 | ||
|
||
### 초기 자바스크립트의 모듈 구현 | ||
|
||
- 객체 리터럴 표기법 | ||
- 모듈 패턴 | ||
- AMD 모듈 | ||
- CommonJS 모듈 | ||
|
||
|
||
### 객체 리터럴 | ||
|
||
- 객체는 중괄호(`{}`) 안에서 키와 값을 쉼표(,)로 구분하여 객체를 정의 | ||
- 오류 방지를 위해 마지막 줄 끝에는 쉼표 사용을 권장하지 않는다. | ||
|
||
<br /> | ||
|
||
> 나의 생각 | ||
prettier 같은 곳에서 trailing comma에 대한 여부를 설정할 수 있는데, 오류를 야기할 수 있다면 이런 옵션을 설정하는 기능이 없어야 하는 거 아닌가 하는 생각이 들었어요. | ||
|
||
![trailing commas](/imgs/posts/2024/10/29/1.png) | ||
|
||
그래서 MDN([링크](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Trailing_commas))을 찾아보니 JS 코드에서 새로운 요소를 추가할 때 이전의 마지막 줄을 변경하지 않고 새로운 줄을 바로 추가할 때 유용하게 사용할 수 있다고 나와요. 이렇게 하면 버전 관리가 더 깔끔해지고 코드 편집이 덜 번거로워질 수 있다고 하네요. **JSON을 제외한** 나머지 자바스크립트 코드에서는 허용을 하게 되어있다고 하니, 내용이 조금 충돌하는 면이 있지 않나 싶어요. 오류가 생기는 경우에 대해 알고 계시다면 의견 부탁드릴게요. | ||
|
||
|
||
### 모듈 패턴 | ||
|
||
- 클래스의 캡슐화를 위해 처음 고안 | ||
- 클로저(closure)를 사용하여 '비공개' 상태와 구성을 캡슐화 | ||
- 공개 API만을 노출하고 나머지는 클로저 내부에 비공개로 유지 가능 | ||
- 장점: 다른 app이 사용해야 하는 부분만 노출하고, 핵심 작업은 보호 | ||
- 즉시 실행 함수(IIFE)로 구현 | ||
|
||
|
||
### WeakMap | ||
|
||
- 모듈에서 반환된 객체에 포함된 변수를 비공개할 때 사용 | ||
- 객체만 키로 설정 가능 | ||
- 순회 불가능 | ||
- 모듈 내부의 객체에 접근하는 유일한 방법 : 해당 객체의 참조를 통해서만 가능 | ||
|
||
|
||
### 모듈 패턴의 변형 : 믹스인(mixin) import | ||
|
||
- 유틸 함수나 외부 라이브러리 같은 전역 스코프에 있는 요소를 모듈 내부의 고차 함수에 인자로 전달 | ||
- 전역 스코프 요소를 가져와 마음대로 지정(alias)하여 사용 가능 | ||
|
||
|
||
### 모듈 패턴의 장점 | ||
|
||
- 모듈 패턴은 캡슐화 개념보다 객체 지향 프로그래밍 지식을 가진 초보 개발자가 이해하기 용이 | ||
- 비공개 지원 | ||
- 공개되면 안 되는 코드 캡슐화 가능 | ||
- 여러 의존성을 동시에 사용 가능 | ||
- 이름의 충돌 방지 | ||
|
||
|
||
### 모듈 패턴의 단점 | ||
|
||
- 공개와 비공개 멤버를 서로 다르게 접근해야 한다. | ||
- 공개 여부를 바꾸고 싶다면 값이 위치한 파일로 가서 각각 변경해야 한다. | ||
- 나중에 추가한 메서드에서는 비공개 멤버에 접근할 수 없다. | ||
- 자동화 단위 테스트에서 비공개 멤버는 제외된다. | ||
- 핫픽스가 필요한 오류를 고칠 때 복잡도를 높인다. | ||
- 비공개 멤버는 쉽게 수정하기도 힘들기에 생각만큼 유연하게 사용할 수 없다. | ||
- 모든 JS 런타임에서 ES2015 모듈을 사용하려면 babel 같은 트랜스파일러를 사용해야 한다. | ||
|
||
|
||
### WeakMap을 사용한 모듈 패턴 | ||
|
||
- ES6에서 도입 | ||
- 약한 참조를 가진 키-값의 쌍으로 구성된 집합체 | ||
- 키는 객체, 값은 어떤 것이든 가능 | ||
- 키가 약하게 유지되는 map : 참조되지 않는 키는 가비지 컬렉션의 대상이 된다. | ||
|
||
|
||
## 노출 모듈 패턴 | ||
|
||
- 공개 변수나 메서드에 접근하기 위해 가져온 메인 객체의 이름을 반복 사용해야 하는 불편에서 개선 | ||
- 모든 함수와 변수를 비공개 스코프에 정의 | ||
- 공개하고 싶은 부분만 포인터를 통해 비공개 요소에 접근할 수 있게 하는 익명 객체 반환 | ||
|
||
```js | ||
let privateCounter = 0; | ||
|
||
const privateFunction = () => { | ||
privateCounter++; | ||
}; | ||
|
||
const publicFunction = () => { | ||
publicIncrement(); | ||
}; | ||
|
||
const publicIncrement = () => { | ||
privateFunction(); | ||
}; | ||
|
||
const publicGetCount = () => { | ||
return privateCounter; | ||
}; | ||
|
||
|
||
// 비공개 함수와 속성에 접근하는 공개 포인터 | ||
const myRevealingModule = { | ||
start: publicFunction, | ||
increment: publicIncrement, | ||
count: publicGetCount, | ||
}; | ||
|
||
export default myRevealingModule; | ||
|
||
|
||
// 사용법 | ||
import myRevealingModule from './myRevealingModule.js'; | ||
|
||
myRevealingModule.start(); | ||
``` | ||
|
||
|
||
### 장단점 | ||
|
||
- 장점 | ||
- 코드 일관성 유지 가능 | ||
- 모듈의 가장 아래에 위치한 공개 객체를 더 알아보기 쉽게 바꾸어 가독성 향상 | ||
- 단점 | ||
- 비공개 함수를 참조하는 공개 함수 수정 불가 | ||
- 비공개 변수를 참조하는 공개 객체 멤버 또한 수정 불가 | ||
- 기존 모듈 패턴보다 취약할 수 있다. | ||
|
||
|
||
### 나의 생각 | ||
|
||
메인 객체를 계속 반복해서 사용해야 한다는 점은 같지 않나요...? 기존 모듈 객체 방식이랑 뭐가 크게 차이 나는지는 잘 모르겠어요. | ||
|
||
|
||
## 싱글톤 패턴(Singleton Pattern) | ||
|
||
> 클래스의 인스턴스가 오직 하나만 존재하도록 제한하는 패턴 | ||
- 전역에서 접근 및 공유해야 하는 단 하나의 객체가 필요할 때 유용 | ||
- 이미 존재하는 인스턴스가 없어야 한다. | ||
- 인스턴스가 이미 존재할 경우 해당 인스턴스의 참조를 반환 | ||
- 정적 클래스나 객체와는 다르게 **초기화를 지연시킬 수 있다.** | ||
- 초기화 시점에 필요한 특정 정보가 유효하지 않을 수도 있기 때문 | ||
- 클래스 내에 공개된 get, set 메서드를 통해 인스턴스를 읽기/수정 가능 | ||
|
||
|
||
### 싱글톤의 특징 | ||
|
||
- 인스턴스에 대한 전역 접근을 허용 | ||
- 클래스의 **인스턴스는 정확히 하나만 있어야 한다.** | ||
- 눈에 잘 보이는 곳에 위치시켜 접근을 용이하게 해야 한다. | ||
- 싱글톤의 인스턴스는 **서브클래싱(sub-classing)**을 통해서만 확장할 수 있어야 한다. | ||
- 코드의 수정 없이 확장된 인스턴스를 사용할 수 있어야 한다. | ||
|
||
|
||
### 지연된 실행? | ||
|
||
> 싱글톤에서는 어째서 지연된 실행이 중요한 걸까? | ||
- 개발자들에게 제어권을 주어 동적 초기화 순서의 예측 불가능성을 제거하는 역할 | ||
- 싱글톤과 정적 클래스(또는 객체) 사이의 차이점을 명확히 아는 것이 중요 | ||
- **필요할 때까지는 리소스나 메모리를 소모하지 않도록 지연 생성될 수도 있다.** | ||
|
||
|
||
### 주의할 점 | ||
|
||
> JS에서 싱글톤이 필요하다는 것은 설계를 다시 생각해 봐야 한다는 신호일 수도 있다. | ||
- C++은 객체를 생성하기 위해서 클래스를 정의해야 하지만, JS는 직접 생성할 수 있기 때문 | ||
- 즉, 객체를 하나 생성할 수 있는 상황에서 클래스를 쓰고 있지는 않는지 확인 필요 | ||
- 싱글톤 패턴은 유용하지만 남용되어서는 안 된다.(전역 공간을 차지하기 때문) | ||
|
||
|
||
### 싱글톤의 단점 | ||
|
||
- **싱글톤임을 파악하는 것이 힘들다** : 여러 객체를 인스턴스화 하거나, 부적절하게 수정할 수 있다. | ||
- **테스트하기 힘들다** : 숨겨진 의존성, 의존성 대체의 어려움 등 다양한 문제로 테스트하기 어렵다. | ||
|
||
|
||
### 리액트의 상태관리 | ||
|
||
> 리액트 쓰면 싱글톤 대신 전역 상태 관리 도구를 통해 개발 가능 | ||
- 전역 상태 관리 도구는 변경 불가능한 읽기 전용 상태 제공 | ||
- 단점을 모두 쉽게 처리할 순 없지만, 적어도 컴포넌트가 전역 상태를 직접 변경할 수 없도록 한다. | ||
- **전역 상태가 의도한 대로 변경될 수 있도록 도와준다.** | ||
|
||
|
||
## 프로토타입 패턴(Prototype Pattern) | ||
|
||
> 이미 존재하는 객체를 복제해 만든 템플릿을 기반으로 새 객체를 생성하는 패턴 - GoF | ||
- 프로토타입 상속과 클래스는 별개로 사용 | ||
- 클래스 : 따로 정의 | ||
- 프로토타입 : 존재하는 다른 객체를 복제하여 새 객체를 생성 | ||
|
||
### 장점 | ||
|
||
> 다른 언어의 기능을 따라하지 않고, JS만의 고유한 방식으로 작업할 수 있다. | ||
- *나의 생각 : 사용 언어의 특성을 살리는 것은 좋지만.. JS 구린데 그렇게 해야 하나?* | ||
|
||
<br /> | ||
|
||
> 상속을 구현하는 쉽고 성능 좋은 방법 | ||
- 객체 내에 함수를 정의할 때 복사본이 아닌 참조로 생성 | ||
- 모든 자식 객체가 동일한 함수를 가리키게 할 수 있기 때문 | ||
|
||
|
||
### 클래스와 생성자 | ||
|
||
- 클래스와 생성자도 결국 내부적으로는 함수와 프로토타입으로 컴파일 | ||
- 여전히 프로토타입의 장점과 동시에 성능상 이점을 누리고 있는 것 | ||
- *나의 생각 : 그러면 JS 프로토타입의 성능상 이점을 살리기 위해 굳이 prototype을 쓸 필요는 없는 거 아닌가?* | ||
|
||
|
||
## 팩토리 패턴(Factory Pattern) | ||
|
||
> 객체 생성을 위한 인터페이스를 정의하고, 어떤 클래스의 인스턴스를 만들지 서브클래스에서 결정할 수 있게 하는 패턴 | ||
|
||
### 사용하면 좋은 상황 | ||
|
||
- 객체나 컴포넌트의 생성과정이 복잡할 때 | ||
- 상황에 맞춰 다양한 객체 인스턴스를 편리하게 생성해야 할 때 | ||
- 같은 속성을 공유하는 여러 개의 작은 객체 또는 컴포넌트를 다뤄야 할 때 | ||
- 덕 타이핑처럼 API 규칙만 충족하면 되는 다른 객체의 인스턴스와 함께 객체를 구성할 때 | ||
|
||
|
||
### 사용하면 안 좋은 상황 | ||
|
||
> 잘못된 상황에서 팩토리 패턴을 적용하면 애플리케이션의 복잡도가 크게 증가 | ||
- 객체 생성 인터페이스 제공이 작업중인 라이브러리나 프레임워크의 설계 목표가 아닐 때 | ||
- 이럴 때는 차라리 위험을 피해 생성자를 사용하는 것이 좋다. | ||
- 객체 생성 과정이 복잡할 경우 | ||
- 단위 테스트의 복잡성이 증가할 수 있다. | ||
- 객체 생성 과정을 인터페이스 뒤에 추상화하기 때문 | ||
|
||
|
||
### 추상 팩토리 패턴(Abstract Factory Pattern) | ||
|
||
> 같은 목푤르 가진 각각의 팩토리들을 하나의 그룹으로 캡슐화 | ||
- 객체가 어떻게 생성되는지에 대한 세부사항을 알 필요 없이 객체를 사용할 수 있게 한다. | ||
- **객체의 생성 과정에 영향을 받지 않아야 하거나, 여러 타입의 객체로 작업해야 할 때 유용** | ||
|
||
|
||
## 총평 | ||
|
||
전체적으로 JS를 사용한 디자인패턴을 알아봤는데, 피부로 와닿게 사용에 대한 효용이나 필요성을 느끼지는 못한 것 같아요. 뭔가 실무적인 예시가 더 있었다면 이럴 때 쓰면 편하겠구나 체감이 될 것 같은데 간단한 예시 코드에 그치는 것 같아 아쉬웠어요. 제 깊이가 부족한 탓일까요.. 이후엔 조금 더 앞서 소개한 디자인 패턴을 활용하는 실무적인 과정이나 가이드가 있다면 좋겠다고 생각했어요. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters