2023 12 15
2023-12-15¶
JS 모듈 시스템 - CJS, AMD, UMD, ESM¶
참고: https://beomy.github.io/tech/javascript/cjs-amd-umd-esm/ 참고: https://betterprogramming.pub/what-are-cjs-amd-umd-esm-system-and-iife-3633a112db62
- 개요
- 현대 JS 프로젝트는 번들러를 통해서 작은 조각의 코드를 하나의 뭉텅이로 뭉쳐서 내보내야 함 (라이브러리/어플리케이션 구축)
- webpack, Rollup, Parcel, RequireJS, Browserify
- JS 모듈을 가져오거나 내보내는 방법이 없었음 => 하나의 파일에 모든 기능들이 들어가야 함
- CJS,AMD,UMD,ESM이 등장 후에 모듈로 개발/배포할 수 있게 됨
- 현대 JS 프로젝트는 번들러를 통해서 작은 조각의 코드를 하나의 뭉텅이로 뭉쳐서 내보내야 함 (라이브러리/어플리케이션 구축)
- 번들러
- 하나의 파일로 뭉텅이 지어서 사용하는것은 간단한 경우에는 괜찮은데...
- 프로젝트가 확장될 수록, 우리는 모듈화의 니즈가 커짐 (별도의 namespace, 캡슐화, 의존성관리, 재사용 등)
- 이때 번들러가 필요해짐
- css, 이미지 등등
- 번들러가 코드를 어떤 형식으로 내보낼지 정의하게 됨
cjs: node 혹은 타 번들러에 적합amd: RequireJS와 같은 모듈 로더와 함께 쓰임umd:amd,cjs,iife올인원으로 쓰임es/esm: ES 모듈 파일<script type=module>태그임system: SystemJS에서 Native Formatiife: Self Executing Function
- 하나의 파일로 뭉텅이 지어서 사용하는것은 간단한 경우에는 괜찮은데...
-
CJS (CommonJS)
- CommonJS: JS를 브라우저뿐만이 아니라 범용언어로 사용하고자 만들어진 그룹
- [코드 예제]
- [특징]
- 동기적으로 동작
- 서버사이드에서 사용하기 용이함
- Node.js가 CommonJS를 채택하여 사용 중
-
AMD (Asynchronous Module Definition)
- CommonJS는 모든 파일이 로컬디스크에 있어서 바로 불러올 수 있어야 해
- 동기적으로 동작이 가능한 서버사이드 JS 환경을 전제로 함
- 브라우저에서는 이게 다운 못 받으면 암것도 못하는 상황으로 치닫음
- AMD는 브라우저 안에서의 동작을 중점을 둔 그룹임
- [코드 예제]
- [특징]
- 비동기
- 네트워크를 통해 모듈을 내려받는 브라우저이기에, 비동기로 동작해야 함
- 비동기적인 특징으로 클라이언트 사이드 개발에 용이
-
UMD (Universal Module Definition)
- AMD, CommonJS 호환을 위해 해결
- AMD, CommonJS, window에 추가하는 방식까지 모두 가능한 모듈 작성 방식
- 두 부분으로 구성됨
- 모듈 로더를 확인하는 즉시 실행 함수(IIFE): 이 함수는
root(전역 범위)와factory(모듈을 선언하는 함수) 2개의 파라미터 가짐 - 모듈을 생성하는 익명 함수: 이 함수가 즉시 실행 함수의 2번째 파라미터로 넘어감
- 모듈 로더를 확인하는 즉시 실행 함수(IIFE): 이 함수는
- [코드 예제]
(function(root, factory) { if (typeof define === 'function' && define.amd) { // AMD define(['exports', 'b'], factory); } else if (typeof exports == 'object' && typeof exports.nodeName !== 'string') { // CommonJS factory(exports, require('b')); } else { // Browser globals factory((root.commonJsStrict = {}), root.b); } }(this, function (exports, b) { exports.action = function() {}; }));
- [특징]
- 여러 모듈 로더에서 사용 가능
- AMD/CommonJS 모두 사용가능
- AMD, CommonJS 호환을 위해 해결
-
ESM (ECMAScript Module)
- ES6에 JS 모듈 기능이 추가됨
- [코드 예제]
- [특징]
- ECMAScript에서 지원하는 JS 자체 모듈 시스템
- 브라우저에서는
import,export를 지원하지 않아 번들러를 함께 사용해야 함
- [부록]
<script type="module" src="index.mjs">type="module"선언하면 자바스크립트 파일은 모듈로 동작- 모듈이라는 것을 명확히 알기 위해
mjs확장자를 사용하도록 권장 - 해당 파일에
import,export사용이 가능 foo.mjs,bar.mjs파일의 window는 서로 공유되지 않음
IIFE (Immediately Invoked Function Expression)¶
참고: https://developer.mozilla.org/ko/docs/Glossary/IIFE 참고: https://mine-it-record.tistory.com/339
- 개요
- 정의되자마자 즉시 실행되는 JS Function
- 형식
- 크게 두 부분으로 나뉨
- Grouping Operator
- () 안에 어휘 범위로 둘러싸인 익명함수.
- 전역 스코프에 불필요한 변수 추가해서 오염을 방지함.
- 스코프를 딱 요만큼안으로 제한
- IIFE 안으로 다른 변수 접근 못하게 함.
- 즉시 실행함수를 생성하는 괄호
- JS 엔진은 함수를 즉시 해석해서 실행
- Grouping Operator
- 크게 두 부분으로 나뉨
- 언제 사용하지?
- 코드 사이의 충돌을 예방 (스코프 제한)
- 전역 변수/전역 함수가 되지 않도록 방지
- 변수의 값을 즉시 할당
JS 코드 축소¶
참고: https://www.cloudflare.com/ko-kr/learning/performance/why-minify-javascript-code
- 최소화?
- JS에서 기능을 변경하지 않고 불필요한 문자를 모두 제거하는 프로세스
- 공백/주석/세미콜론 제거/더 짧은 함수-변수 이름 사용
- 코드 재사용, 캐시 적용, CDN 사용
- 코드의 압축
- 압축(minify)
- 전체 소스코드 중 아래의 경우를 제거하는 작업
- 불필요한 줄바꿈, 공백 및 들여쓰기
- 짧게 쓸 수 있는 긴 구문 축소
- 주석
- 스코프 내 미사용 변수
- 전체 소스코드 중 아래의 경우를 제거하는 작업
- 난독화(uglification)
- 본질적으로 최소화와 동일
- JS 파일을 축소하기 위한 JS 라이브러리
- 분석을 어렵게 하기 위해 난독화 뚝딱
- 압축 도구
- 주요 JS 라이브러리에서 가장 많이 쓰이는 소스 압축 도구
- Node.js 기반이라 다른 Node 모듈과의 연계 좋음
npm install -g uglify-jsuglifyjs hello.js -o hello.min.js
IOT (Index Organized Table) - 인덱스 일체형 테이블¶
참고: https://blog.naver.com/geartec82/220954857049
참고: http://gurubee.net/lecture/2601

- 일반 테이블
- 키값 -> 인덱스 -> ROW_ID -> ROW_ID로 테이블 데이터 읽기
- 키 칼럼이 인덱스와 테이블 양쪽에 중복해서 저장되기에, 키 값이 큰 경우에는 중복된 인덱스 크기가 할당됨.
- 테이블 구조 기본하에 인덱스가 참조하는 방식
- IOT
- 인덱스 구조 하에 테이블 데이터 저장 구조를 가진 것
- 키값에 해당되는 레코드를 테이블에서 읽을 필요도, 데이터의 중복 문제도 동시 해결 가능
- 인덱스를 읽는 것만으로도 데이터 참조가 가능하다네?!
- Primary Key를 근간으로 한 인덱스 이기에, 전제조건으로 Primary를 반드시 필요로 함
- IOT 특징
- IOT는 테이블 데이터를 PK에 대한 B-Tree Index에 적재
- IOT의 index row들은 인덱스 Key 값과 Non-Key 값을 포함
- IOT의 index에는 ROW_ID 정보가 없음
- IOT 장점
- RANGE SEARCH, EXACT MATCH 수행 시 일반적인 TABLE보다 빠른 KEY-BASED ACCESS 가능
- FULL TABLE SCAN시 PK에 대한 FULL INDEX SCAN이 이루어짐 (자동적인 ORDERING)
- INDEX KEY COLUMN과 ROW_ID에 대한 스토리지 중복을 피할 수 있음 (스토리지 절약)
- IOT 제약사항
- 추가적인 index 생성 X
- 오로지 PK에 대해서만 indexing
- cluster table로 이용되지 못함
- 병렬 작업 불가능
- 분산/복제/분할 불가능
- long/long raw/lob 지원 안됨
- 추가적인 index 생성 X
- 일반 테이블과 IOT 테이블의 차이점
- 일반 테이블은 ROW_ID로 행 구별 | IOT는 PK로 행 구별
- 일반 테이블의 FULL SCAN 행이 리턴되는 순서 예측 가능 | IOT는 PK값의 순서에 따라 출력
- IOT는 Unique 못 걸음
- IOT는 일반 테이블보다 저장 공간 감소
- IOT는 PK 필수로 생성
- IOT의 Secondary Index는 PK값과 그것을 기반으로 하는 Universal Rowid (인덱스 구성 테이블의 논리적인 위치)
- IOT는 일반 인덱스와 달리 UROWID를 통해 빠른 검색 가능
Promise¶
- 아 이제 보이네
- iife 방식으로 코드를 작성해 즉시 실행함수를 호출해서 뚝딱 스코프 벗어나지 않으면서 실행시킬 수 있도록!
-
헤맷던 promise ```typescript export abstract class ServiceTypeClass { abstract isUsingNotice(): Promise
; } class NoticeApi { private static instance: NoticeApi; private noticeUsingServiceTypes: string[] | null = null;
public static getInstance() { if (!NoticeApi.instance) { NoticeApi.instance = new NoticeApi(); } return NoticeApi.instance; }
getNoticeServiceTypes = async () => { const response = await NoticeApiService.getServiceTypes(); this.noticeUsingServiceTypes = response.list; }
isUsingNotice = async (serviceType: string): Promise
=> { if (this.noticeUsingServiceTypes == null) { await this.getNoticeServiceTypes(); } return this.noticeUsingServiceTypes?.some((st) => st === serviceType) || false; } } export class Test extends ServiceTypeClass { isUsingNotice = (): Promise
=> NoticeApi.getInstance().isUsingNotice('test') } ```
Promise resolve/reject & await/async¶
참고: https://springfall.cc/article/2022-11/easy-promise-async-await
- 개요
- JS에서는 함수를 전달할 수 있음
- function a에 인자로 function b를 전달하더라도, b는 바로 실행되지 않음. b 함수는 a에서 언젠간~ 실행이 될것이다 하고 넘김
- 이걸
콜백 함수라고 지칭
- 이걸
- 비동기
- 흐름을 예측하기 어려움
- 콜백 지옥
- 비동기 작업이 끝났을 뒤에 이어질 작업을 미리 부여하는 식으로 흐름을 제어할 수 밖에 없음
-
Promise
-
Promise: 비동기 작업의 단위
- Promise 생성자 안에 화살표 함수를 넘겨줘
(resolve, reject) => {}(executor로 지칭)js const promise1 = new Promise((resolve, reject) => { // 비동기 작업 });
resolve: 비동기 작업이 성공했어요!reject: 비동기 작업이 실패했어요...- Promise 선언된 순간 비동기 작업 시작됨
- new Promise()를 통해 기다리지 않고 바로 호출해버림!
- Promise 생성자 안에 화살표 함수를 넘겨줘
-
Promise가 끝난 다음: then, catch
then: 해당 promise가 성공했을때의 동작 지정catch: 해당 promise가 실패했을때의 동작 지정-
체인 형태로 활용 가능 ```js const promise1 = new Promise((resolve, reject) => { resolve(); });
promise1 .then(() => {console.log("then!")}) .catch(() => {console.log("catch!")});
// then!
js const promise1 = new Promise((resolve, reject) => { reject(); });promise1 .then(() => {console.log("then!")}) .catch(() => {console.log("catch!")});
// catch! ```
-
executortipsexecutor내부에서 에러가 throw 된다면 해당 에러로reject가 수행됨executor의 리턴값은 무시됨- 첫번째
reject,resolve값만 유효
- 정리
Promise: 비동기 작업을 생성/시작하는 부분(new Promise(...))과 작업 이후의 동작 지정 부분(then,catch)을 분리함으로써 유연한 설계 제공Promise의 상태: pending, fulfilled, rejected
-
-
async/await
async: 함수를 선언할 때 붙여줄 수 있음executor에서 몇가지 규칙 적용하면new Promise(...)를 리턴하는 함수를async함수로 손쉽게 변환 가능- 함수에
async키워드 붙임 new Promise...없애고executor본문만resolve(value)를return value로 변경reject(new Error(...))를throw new Error(...)로 변경
- 함수에
async함수의 리턴값은 무조건Promise!
await: 프로미스 끝날때 까지 기다려주세요async내부에서만 사용 가능- resolve된 값을 사용하거나, reject되어 던저진 에러를 핸들링하거나
-
둘 중 하나의 패턴을 고르세요~ ```js function setTimeoutPromise(delay) { return new Promise((resolve) => setTimeout(resolve, delay)); }
async function startAsync() { await setTimeoutPromise(1000); await setTimeoutPromise(1000); console.log("A"); console.log("B"); }
startAsync();
js function setTimeoutPromise(delay) { return new Promise((resolve) => setTimeout(resolve, delay)); }function startAsync() { setTimeoutPromise(1000) .then(() => setTimeoutPromise(1000)) .then(() => console.log("A")) .then(() => console.log("B")); }
startAsync(); ```
-
Promise.all
-
다음과 같은 끔찍한 코드 => 10초가 걸린다!! ```js function setTimeoutPromise(ms) { return new Promise((resolve, reject) => { setTimeout(() => resolve(), ms); }); }
async function fetchAge(id) { await setTimeoutPromise(1000); console.log(
${id} 사원 데이터 받아오기 완료!); return parseInt(Math.random() * 20, 10) + 25; }async function startAsyncJobs() { let ages = []; for (let i = 0; i < 10; i++) { let age = await fetchAge(i); ages.push(age); }
console.log(
평균 나이는? ==> ${ ages.reduce((prev, current) => prev + current, 0) / ages.length }, ); }startAsyncJobs();
/* 0 사원 데이터 받아오기 완료! 1 사원 데이터 받아오기 완료! 2 사원 데이터 받아오기 완료! 3 사원 데이터 받아오기 완료! 4 사원 데이터 받아오기 완료! 5 사원 데이터 받아오기 완료! 6 사원 데이터 받아오기 완료! 7 사원 데이터 받아오기 완료! 8 사원 데이터 받아오기 완료! 9 사원 데이터 받아오기 완료! 평균 나이는? ==> 33
- */ ```
-
정상적인 비동기로... ```js function setTimeoutPromise(delay) { return new Promise((resolve) => setTimeout(resolve, delay)); }
async function fetchAge(id) { await setTimeoutPromise(1000); console.log(
${id} 사원 데이터 받아오기 완료!); return Math.round(Math.random() * 20) + 25; }async function startAsyncJobs() { const ids = Array.from({ length: 10 }).map((_, index) => index);
const promises = ids.map(fetchAge);
const ages = await Promise.all(promises);
console.log(
평균 나이는? ==> ${ ages.reduce((prev, current) => prev + current, 0) / ages.length }, ); }startAsyncJobs(); ```
- 이를 통해 map 함수에다가 async 함수 넣으면 Promise 배열 얻는데 -> 이걸
Promise.all()로 뚝!딱! 한큐에 처리가능
-
JS가 싱글쓰레드임에도 비동기로 처리할 수 있는 이유¶
참고: https://chanyeong.com/blog/post/44
참고: https://stitchcoding.tistory.com/44
