|
| 1 | +# CHAPTER 11 네임스페이스 패턴 |
| 2 | + |
| 3 | +namespace : 코드 단위를 고유한 식별자로 그룹화한 것 |
| 4 | + |
| 5 | +- 전역 네임스페이스 내에 존재하는 다른 객체나 변수와의 충돌을 방지 |
| 6 | +- 코드 재사용성, 관리 편의성 증가 |
| 7 | +- 객체와 클로저를 활용해 네임스페이스와 비슷한 효과를 얻을 수 있음 (JS 언어 자체에서 기본 지원 X) |
| 8 | + |
| 9 | +> 구분이 가능하도록 정해놓은 범위가 네임스페이스 |
| 10 | +> 대한민국에서 홍길동이라는 사람을 찾는다면, xx시 xx구 xx로에 사는 홍길동이라는 정보가 필요함 |
| 11 | +> 자바스크립트 언어 관점에서 보면 'xx시 xx구 xx로'에 해당하는 자세한 정보가 바로 네임스페이스 |
| 12 | +> 객체나 변수가 겹치치 않는 안전한 소스코드를 만드는 개념 |
| 13 | +
|
| 14 | +## 네임스페이스의 기초 |
| 15 | + |
| 16 | +외부 코드가 내 코드를 방해하는 것을 막기 위해 네임스페이스를 올바르게 구현하는 것이 중요 |
| 17 | + |
| 18 | +## 단일 전역 변수 패턴 |
| 19 | + |
| 20 | +- 하나의 전역 변수를 주요 참조 객체로 사용하는 방식 |
| 21 | +- 문제점 : 다른 개발자가 같은 이름의 전역 변수를 이미 사용하고 있다면 충돌 발생 |
| 22 | + |
| 23 | +## 접두사 네임스페이스 패턴 |
| 24 | + |
| 25 | +- 단일 전역 변수 문제에 대한 해결책 중 하나 |
| 26 | +- 고유한 접두사를 선정한 다음에 메서드, 변수, 객체를 접두사 뒤에 붙여서 정의 |
| 27 | + ```javascript |
| 28 | + // 접두사를 myApplication으로 정한 경우 |
| 29 | + const myApplication_propertyA = {}; |
| 30 | + const myApplication_propertyB = {}; |
| 31 | + function myApplication_myMethod() { |
| 32 | + //... |
| 33 | + } |
| 34 | + ``` |
| 35 | +- 문제점 : 애플리케이션이 커짐에 따라 많은 전역 객체 생성 |
| 36 | + |
| 37 | +## 객체 리터럴 표기법 패턴 |
| 38 | + |
| 39 | +- 객체 리터럴 표기법 : 일종의 객체, 키-값 구조, 키 자체가 새로운 네임스페이스 |
| 40 | +- 전역 네임스페이스를 오염시키지 않으면서 코드와 매개변수를 논리적으로 구성 |
| 41 | +- 쉽게 읽을 수 있고, 깊은 중첩까지 지원하는 구조를 구현할 때 유용 |
| 42 | +- 동일한 이름의 변수가 있는지 검사하도록 설계되는 경우가 많아 충돌 가능성 감소 |
| 43 | + |
| 44 | +## 중첩 네임스페이스 패턴 |
| 45 | + |
| 46 | +- 객체 리터럴 패턴을 발전시킨 형태 |
| 47 | +- 다른 패턴에 비해 충돌 위험이 낮은 편 |
| 48 | + |
| 49 | +## 즉시 실행 함수 표현식 패턴 |
| 50 | + |
| 51 | +- 즉시 실행 함수 : 정의 직후 바로 실행되는 이름이 없는 함수 |
| 52 | +- 즉시 실행 함수 내부의 변수와 함수 모두 외부에서 접근할 수 없어 쉽게 은닉성 구현 가능 |
| 53 | +- 애플리케이션 로직을 캡슐화하여 전역 네임스페이스로부터 보호 |
| 54 | + |
| 55 | +## 네임 스페이스 주입 패턴 |
| 56 | + |
| 57 | +- 즉시 실행 함수 패턴의 변형 |
| 58 | +- this를 네임스페이스의 proxy로 활용해 특정 네임스페이스에 메서드와 속성을 주입 |
| 59 | +- 장점 : 여러 객체나 네임스페이스에 기능적인 동작 쉽게 적용 가능, 확장될 기본 메서드(ex. 게터, 세터)에 적용할 때 유용 |
| 60 | +- 단점 : 같은 목적을 달성하는 더 쉽고 효율적인 방법이 존재할 수도 있음(ex. 심층 개체 확장 또는 병합) |
| 61 | +- 유용한 상황 |
| 62 | + - 여러 모듈이나 네임스페이스에 비슷한 기본 기능들을 할당할 때 |
| 63 | + - 객체/클로저 내에서 명시적으로 기능을 선언할 때 직접 접근하는 것이 불가능한 상황 |
| 64 | + |
| 65 | +## 고급 네임스페이스 패턴 |
| 66 | + |
| 67 | +### 중첩 네임스페이스 자동화 패턴 |
| 68 | + |
| 69 | +```javascript |
| 70 | +const application = { |
| 71 | + utilities: { |
| 72 | + drawing: { |
| 73 | + canvas: { |
| 74 | + paint: { |
| 75 | + // ... |
| 76 | + }, |
| 77 | + }, |
| 78 | + }, |
| 79 | + }, |
| 80 | +}; |
| 81 | +``` |
| 82 | + |
| 83 | +중첩 네임스페이스는 추가하고자 하는 계층이 늘어날수록 최상위 네임스페이스에 더 많은 하위 객체들이 정의되어야 한다는 단점 존재 |
| 84 | +하나의 문자열 인자를 받아서 파싱한 뒤, 필요한 객체를 기반 네임스페이스에 자동으로 추가해 해결할 수 있음 |
| 85 | + |
| 86 | +### 의존성 선언 패턴 |
| 87 | + |
| 88 | +- 중첩 네임스페이스 패턴을 변형한 형태 |
| 89 | +- 객체에 대한 로컬 참조가 전체적인 조회 시간을 단축한다는 원칙을 네임스페이스에 적용 |
| 90 | +- 함수나 모듈에서 사용할 로컬 네임스페이스를 함수 영역의 상단에 선언할 것을 권장 |
| 91 | +- 모듈 단위로 작업할 때 가장 효과적 |
| 92 | +- 네임스페이스 간의 의존성이 많이 중복된다면 함수 단위로 네임스페이스를 지역화하지 않는 게 좋음 |
| 93 | + (차라리 상위 레벨에서 정의하고 모든 함수가 접근할 수 있도록 하기) |
| 94 | + |
| 95 | +### 심층 객체 확장 패턴 |
| 96 | + |
| 97 | +- 자동 네임스페이스 생성에 대한 해결책 |
| 98 | +- 객체 리터럴 표기법으로 선언된 네임스페이스는 다른 객체(또는 네임스페이스)와 쉽게 확장/병합 가능 |
| 99 | + 병합 이후에는 두 네임스페이스의 속성과 함수를 동일한 네임스페이스에서 접근 가능 |
| 100 | + |
| 101 | +> 응집도를 높이고 결합도를 낮추기 위해서 컴포넌트의 하위 속성으로 컴포넌트를 전달하는 React Namespace Pattern이 있음 |
| 102 | +> |
| 103 | +> ```typescript |
| 104 | +> const Dropdown = ({ children }: { children: React.ReactNode }) => { |
| 105 | +> return <div className="dropdown">{children}</div>; |
| 106 | +> }; |
| 107 | +> |
| 108 | +> const Item = ({ label }: { label: string }) => { |
| 109 | +> return <div className="dropdown-item">{label}</div>; |
| 110 | +> }; |
| 111 | +> |
| 112 | +> // 하위 속성으로 추가 |
| 113 | +> Dropdown.Item = Item; |
| 114 | +> |
| 115 | +> export default Dropdown; |
| 116 | +> ``` |
| 117 | +> |
| 118 | +> ```typescript |
| 119 | +> const App = () => { |
| 120 | +> return ( |
| 121 | +> <Dropdown> |
| 122 | +> <Dropdown.Item label="Option 1" /> |
| 123 | +> <Dropdown.Item label="Option 2" /> |
| 124 | +> </Dropdown> |
| 125 | +> ); |
| 126 | +> }; |
| 127 | +> ``` |
| 128 | +> |
| 129 | +> `forwardRef`와 함께 사용하면 타입 오류 발생 |
| 130 | +> |
| 131 | +> ```typescript |
| 132 | +> const Dropdown = forwardRef<HTMLDivElement, { children: React.ReactNode }>(({ children }, ref) => { |
| 133 | +> return <div ref={ref}>{children}</div>; |
| 134 | +> }); |
| 135 | +> |
| 136 | +> const Item = ({ label }: { label: string }) => { |
| 137 | +> return <div className="dropdown-item">{label}</div>; |
| 138 | +> }; |
| 139 | +> |
| 140 | +> // ❌ 타입 오류 발생 |
| 141 | +> Dropdown.Item = Item; |
| 142 | +> ``` |
| 143 | +> |
| 144 | +> `forwardRef`로 반환된 컴포넌트는 `ForwardRefExoticComponent` 타입 |
| 145 | +> `ForwardRefExoticComponent`는 속성을 동적으로 확장할 수 있는 구조가 아님 |
| 146 | +> `Object.assign`으로 하위 속성을 병합해 해결 가능 |
| 147 | +> |
| 148 | +> ```typescript |
| 149 | +> const Dropdown = forwardRef<HTMLDivElement, { children: React.ReactNode }>(({ children }, ref) => { |
| 150 | +> return <div ref={ref}>{children}</div>; |
| 151 | +> }); |
| 152 | +> |
| 153 | +> const Item = ({ label }: { label: string }) => { |
| 154 | +> return <div className="dropdown-item">{label}</div>; |
| 155 | +> }; |
| 156 | +> |
| 157 | +> // Object.assign으로 병합해서 해결 |
| 158 | +> Object.assign(Dropdown, { Item }); |
| 159 | +> ``` |
| 160 | +> |
| 161 | +> 출처 : https://careerly.co.kr/comments/95411 |
0 commit comments