props를 유연하게 전달하는 방식이므로 자주 사용됨!!

obj의 key와 컴포넌트의 prop이 일치하면, spread operator를 구현할 수 있다.

function App() {
   const props = { name: '철수', age: 37 }
   return (
      <Student {...props} />
   )
}

function Student({name, age}) {
   return (
      <div>
         <h1>{name}<h1>
         <span>{age}</span>
      </div>
   )
}

 

property로 name, age를 갖는 props 객체를 App 컴포넌트 내에서 Student 컴포넌트에 넘겨주고 있다.

 

아래 Student prop을 보면 name, age가 있다. 즉, ...props은 `name: '철수', age: 37`이자

`<Student name: '철수', age: 37 />`인 것이다.

22. this

1. this 키워드

  • 메서드가 자신이 속한 객체의 프로퍼티를 참조하려면 자신이 속한 객체를 가리키는 식별자를 참조할 수 있어야 한다.
  • 객체 리터럴 방식의 경우 메서드 내부에서 식별자를 재귀적으로 참조할 수 있다.
  • 생성자 함수 방식의 경우 생성자 함수를 정의하는 시점에서는 아직 인스턴스를 생성하기 전이므로 인스턴스를 가리키는 식별자를 알 수 없다.

this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기참조변수다.

⇒ this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다.

  • 함수를 호출하면 arguments 객체와 this가 암묵적으로 함수 내부에 전달된다.
  • 단, this가 가리키는 값, 즉 this 바인딩은 함수 호출 방식에 의해 동적으로 결정된다.

** 바인딩? 식별자와 값을 연결하는 과정

2. 함수 호출방식과 this 바인딩

자바스크립트에서 this 바인딩은 함수의 호출 시점에 따라 동적으로 결정된다

  • 1. 일반함수 호출
    • 일반함수로 호출된 모든 함수(중첩함수, 콜백함수 포함) 내부의 this에는 전역 객체(window)가 바인딩된다.
    • strict 모드가 적용된 일반 함수 내부의 this에는 undefined가 바인딩된다
    • 하지만, 메서드 내의 중첩함수 또는 콜백함수의 this가 외부함수의 this와 일치하지 않는 것은 헬퍼함수로서 동작을 어렵게 한다.
      • Function.prototype.apply Function.prototype.call Function.prototype.bind 로 명시적으로 this를 일치시킬 수 있다
      • 화살표 함수를 사용해서 this를 일치시킬 수 있다
      • ( 화살표 함수는 자신만의 this를 갖지 않기 때문에 lexical scope를 갖게 된다. 그래서, 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다 )
  • 2. 메서드 호출
    • 점 연산자를 통해 객체의 메서드로서 호출되는 함수의 내부에서 this는 점 연산자(.) 앞에 있는 객체에 바인딩된다.
    • 이를 암묵적 바인딩이라고 부른다.
    • const person = { name: 'Lee', getName() { return this.name; } } console.log(person.getName()); //Lee
    • getName 메서드는 person 객체에 포함된 것이 아니라 독립적으로 존재하는 별도의 객체다.
      (새롭게 알게 됨!! 흥미돋!!)
      그저 getName 프로퍼티가 함수 객체를 가리키고 있을 뿐이다. ⇒ 그렇기 때문에 this는 메서드를 소유한 객체가 아닌 메서드를 호출한 객체에 바인딩되는 것인가??
    • 그래서 getName 프로퍼티가 가리키는 함수객체, 즉 getName 메서드는 다른 객체의 메서드도 될 수 있고, 일반 변수에 할당해서 일반함수로 호출될 수 있다.
    • const anoterPerson = { name: "kim" } anotherPerson.getName = person.getName // this는 anotherPerson const getName = person.getName // this는 window
    • 프로토타입 메서드 내부에서 사용되는 this도 일반 메서드와 마찬가지로 호출한 객체에 바인딩된다.
  • 3. 생성자 함수 호출
    • new 연산자와 함께 호출되는 경우 함수는 생성자로써 호출된다
    • 생성자 함수 내부에서 this는 생성할 객체를 의미한다.
    • // 생성자 함수 function Person(name) { this.name = name; } var me = new Person('Lee'); console.log(me); // Person {name: "Lee"} // new 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수로 동작하지 않는다. var you = Person('Kim'); console.log(you); // undefined
  • 4. Function.prototype~ 객체의 메서드에 의한 간접호출
    • Function~~ 객체는 this를 명시적으로 바인딩할 수 있는 메서드를 제공한다.
    • apply, call
      • this로 사용할 객체와 인수 리스트를 인수로 전달받아 함수를 호출한다
      • 본질적인 기능은 함수를 호출하는 것이다. 함수를 호출하면서 첫 번째 인수로 전달한 특정 객체를 호출한 함수의 this에 바인딩한다.
      • 대표적인 용도는 arguments 객체와 같은 유사배열객체에 Array.prototype.slice 같은 배열 메서드를 사용하는 경우다.
      • function convertArgsToArray() { console.log(arguments); // arguments 객체를 배열로 변환 // slice: 배열의 특정 부분에 대한 복사본을 생성한다. var arr = Array.prototype.slice.apply(arguments); // arguments.slice // var arr = [].slice.apply(arguments); console.log(arr); return arr; } convertArgsToArray(1, 2, 3); // [1,2,3]
    • bind
      • this로 사용할 객체로 this 바인딩이 교체된 함수를 새롭게 생성해 반환한다.
      • apply와 call 과 달리 함수를 호출하지 않으므로 명시적으로 호출해야 한다.
      • const person = { name: "Lee", foo(callback) { setTimeout(callback.bind(this), 100); }, }; person.foo(function () { console.log(`Hi! my name is ${this.name}`); }); // Hi! my name is Lee

정리

함수호출방식 this 바인딩
일반함수 호출 전역 객체
메서드 호출 메서드를 호출한 객체
생성자 함수 호출 생성자 함수가 (미래에) 생성할 인스턴스
Function.prototype.apply Function.prototype.call Function.prototype.bind 메서드에 의한 간접 호출 Function.prototype.apply Function.prototype.call Function.prototype.bind 메서드에 첫번째 인수로 전달한 객체

추가1 : this 바인딩 우선순위

new 연산자에 의한 바인딩 > 명시적 바인딩 > 암시적 바인딩 > 일반함수 호출에 의한 바인딩

  • bind 메서드에 의한 바인딩이 apply, call에 의한 바인딩보다 우선순위가 높다
  • apply, call, bind 실행 시 이를 호출한 함수가 화살표 함수면 첫번째 인자인 thisArg가 무시된다. (화살표 함수는 자신만의 this, argument가 없기 때문에 자신의 lexical scope인 window가 this가 된다. )
const person = {
  name: "Lee",
  foo(callback) {
    setTimeout(callback.bind(this), 100);
  },
};

person.foo(() => {
  console.log(`Hi! my name is ${this.name}`);
});

// window.name은 빌트인 프로퍼티로 ""이다.
// Hi! my name is

'엘리스 ai 트랙 > FE' 카테고리의 다른 글

Javascript - ES11 ( ES2020 )의 특징들  (0) 2022.04.04
CI와 CD  (0) 2022.03.31
http 1.1 과 http 2 의 차이점  (0) 2022.03.31
HTTP  (0) 2022.03.13
기본값+rest+spread  (0) 2022.03.07

1. String.prototype.matchall

RegExp.prototype.exec()와 매우 유사하게 동작한다.

  • RegExp.prototype.exec()
    • 정규표현식과 첫번째로 일치하는 하위 문자열의 정보를 반환
    • index 속성은 텍스트와 일치하는 첫번째 문자위치
const regExp = /javascript/g;
const text = 'javascript1 is a very good javascript2';
let matches;

while ((matches = regExp.exec(text)) !== null) {
  console.log(matches);
}

  • String.prototype.matchall()
    • 모든 하위 문자열의 정보를 포함하고 있는 iterator를 반환한다.
const result2 = Array.from(text.matchAll(regExp));

console.log(result2);

 

2. Dynamic import

ES11 이전에 modules를 import하기 위해서는 아래와 같이 사용했다.

import * as TestModule from "./test-module.js";

하지만 위와 같이 사용하면 문제점이 있다.

  • 모든 모듈들이 처음 로드될 때 전부다 import가 된다면 프로그램 상에서 효율을 떨어뜨릴 수 있다.
  • 게다가 모듈 이름을 프로그램이 동작할 동안 동적으로 변경하지 못한다.

위 문제를 해결하기 위해 Dynamic import가 등장

const baseModulePath = "./baseModules";
const btnImportModule = document.getElementById("btnImportModule");
let userList = [];

btnImportModule.addEventListener("click", async e => {
  const userModule = await import(`${baseModulePath}/users.js`);
  
  userList = userModule.getUsers();
});

btnImportModule 버튼을 클릭하면 user.js 모듈이 동적으로 로드되고 getUsers() 메서드가 userList 리스트에 추가될 수 있다. 그리고 모듈명을 변수 형식으로 설정하면 모듈명을 동적으로 변경할 수도 있다.

 

import() 구문은 Promise 객체를 반환하기 때문에 async/awaitthen/catch 를 사용해서 비동기 처리를 해야 한다.

 

 

3. BigInt

Number 원시값이 표현할 수 있는 최대치인 2^53 - 1보다 큰 값을 표현하고 싶을 때 사용한다.

BigInt를 사용하려면 정수 리터럴 뒤에 n을 붙이거나, BigInt() 생성자 함수의 인자로 생성할 숫자를 전달합니다.

주의해야 할 점은 BigInt는 Math객체의 메서드와 함께 사용할 수 없고, 연산에서 Number 타입의 원시값과 같이 연산할 수 없습니다.

// 정수 리터럴 방식
const bigInt1 = 9007199254740999n
// 생성자 함수 방식
const bigInt2 = BigInt(9007199254740998)

console.log(bigInt1 > bigInt2) // true
console.log(bigInt1 > bigInt2 + 2n) // false

 

4. Promise.allSettled

Promise.allSettled 메서드는 인자로 받은 배열이나 iterable 객체를 통해 열거된 Promise 객체들이 모두 실행됐을 때 resolve하는 Promise 객체를 반환한다. 

Promise.allSettled 메서드는 Promise.all 메서드와 유사하지만, Promise.all 메서드는 열거된 Promise 객체들 중 하나라도 reject 되면 자신도 reject하지만 Promise.allSettled 메서드는 이행 여부와 상관없이 전부 다 실행되면 resolve한다는 차이가 있습니다.

Promise.allSettled([
    Promise.resolve(33),
    new Promise(resolve => setTimeout(() => resolve(66), 0)),
    99,
    Promise.reject(new Error('an error'))
])
.then(values => console.log(values));

Promise.all([
    Promise.resolve(33),
    new Promise(resolve => setTimeout(() => resolve(66), 0)),
    99,
    Promise.reject(new Error('an error'))
])
.then(values => console.log(values));

 

5. globalThis

globalThis는 어떠한 javascript 실행환경이라도 동일하게 전역객체를 반환한다.

기존의 javascript 환경에서는 실행환경에 따라서 접근하는 방법이 달랐다.

  • 브라우저 환경 -> window, sef, frames를 사용해서 접근,
  • node.js 환경   -> global 를 사용해서 접근,
  • web worker 환경 -> self를 통해 접근

=> 코드가 다양한 실행환경에서 작동하려면 별도의 예외 처리를 해줘야 했는데, 이제 globalThis를 이용하면 별도의 예외 처리를 하지 않고도 손쉽게 전역객체에 접근할 수 있다.

 

6. Optional Chaining (?.)

왼쪽 피연산자 값이 null이나 undefined일 경우 실행을 멈추고 undefined를 반환하는 연산자.

Optional Chaining 연산자를 사용하면, 존재하지 않을 수도 있는 값에 대해 예외 처리를 해야 할 때 손쉽게 처리할 수 있다.

const person1 = {
    name: 'jin',
    job: {
        title: 'singer',
    }
}
const person2 = {
    name: 'jhope'
}

function printJob(person){
    console.log(person.job.title)
}

printJob(person1) # singer
printJob(person2) # Uncaught TypeError: Cannot read properties of undefined (reading 'title')

Optional chaining 연산자 사용 !

function printJob(person){
    console.log(person?.job?.title) # undefined
}

 

7. Nullish coalescing operator ( ?? )

왼쪽 피연산자 값이 null 이나 undefined인 경우에 오른쪽 피연산자를 반환하고 그렇지 않으면 왼쪽 피연산자를 반환하는 연산자이다.

변수에 기본값을 할당할 때 유용해 보인다.

주로 기본값을 할당할 때 || 연산자( A가 false이면 B가 출력됨 )를 사용하는데 0, '' , NaN 같은 falsy 값들을 null이나 undefined를 할당한 것과 동일하게 처리할 수 있기 때문에 예기치 못한 결과가 발생할 수 있다. 

nullish coalescing 연산자를 사용하면 정확하게 null 이나 undefined인 경우에만 기본값을 할당해주기 때문에 좀 더 안전하게 코드작성가능합니다.


const data = {
  title: 'foo',
  count: 0
}

const description3 = data.count || 100    -> 100
// 0을 할당했지만 0을 falsy한 값으로 취급했기 때문에 null이나 undefined를 할당한 것과 동일하게 처리

const description1 = data.count ?? 100    -> 0
// data.count가 정확하게 null이나 undefined일 경우에만 100이 됩니다. 
// 예시에선 0이라는 값이 할당됐기 때문에 0이 출력된다

const description2 =
  data.count == undefined && data.count == null ? 100 : data.count  -> 0
// 위와 동일한 동작을 함

 

 

참고, 출처:

https://segmentfault.com/a/1190000040438879/en

https://john015.netlify.app/what-is-new-in-es-11

https://zerq.tistory.com/92

https://intrepidgeeks.com/tutorial/regular-expression-correlation-method

'엘리스 ai 트랙 > FE' 카테고리의 다른 글

this [모던 js 딥다이브]  (0) 2022.09.22
CI와 CD  (0) 2022.03.31
http 1.1 과 http 2 의 차이점  (0) 2022.03.31
HTTP  (0) 2022.03.13
기본값+rest+spread  (0) 2022.03.07

일단 요약

CI/CD는 애플리케이션 개발 단계를 자동화하여 애플리케이션을 보다 짧은 주기로 고객에게 제공하는 방법입니다.

CI/CD는 새로운 코드 통합으로 인해 개발 및 운영팀에 발생하는 문제를 해결하기 위한 솔루션입니다.

개발부터 배포까지 모든 단계를 자동화 하는 것

 

CI/CD 파이프라인

애플리케이션의 통합 및 테스트 단계에서부터 제공 및 배포에 이르는 애플리케이션의 라이프 사이클 전체에 걸쳐 지속적인 자동화와 지속적인 모니터링을 제공하는 구축 사례

 

-> 5줄 요약한다면,

 

  1. 개발자가 작은 단위로 코드를 짜고 메인 리포지토리에 merge를 하면,
  2. 자동으로 빌드되고,
  3. 자동으로 테스트되어,
  4. 릴리즈되고(배포준비완료),
  5. 배포된다.

 

CI/CD는 이 파이프라인으로 표현되는 실제 프로세스를 의미하고 지속적인 자동화 및 모니터링을 추가하는 것을 의미합니다.


CI (continuous Integration, 지속적 통합)

애플리케이션의 새로운 코드들이 자동으로 빌드 및 테스트 되어 레포지토리에 통합되는 것을 의미한다.

현상황: 

개발자들은 보통 동일한 애플리케이션의 각기 다른 기능을 동시에 작업합니다.

그러나 각자 작업한 코드를 merge day를 정해 모든 분기 소스 코드를 병합할 경우, 결과적으로 반복적인 수작업 많은 시간을 소모하게 됩니다ㅠ

왜냐면, 각자 작업하는 개발자가 애플리케이션에 변경사항을 적용할 때 다른 개발자가 동시에 적용하는 변경사항과 conflict가 일어날 가능성이 많기 때문입니다.

 

CI를 통해 개발자의 변경사항이 애플리케이션을 손상시키지 않도록 자동으로 구축하고 각기 다른 레벨의 자동화 테스트(단위테스트, 통합테스트) 실행을 통해 변경사항이 잘 적용됐는지를 확인합니다.

다시 말해, 클래스와 기능에서부터 서로 다른 모듈에 이르기까지 모든 것에 대한 테스트를 수행합니다.

이렇게 자동화된 테스트에서 기존 코드와 신규 코드 간의 충돌이 발견되면 CI를 통해 이러한 버그를 더욱 빠르게 자주 수정할 수 있게 됩니다.

 

CI의 예시 ( Jenkins )

1. 개발자는 자신의 로컬에서 코드를 수정하고 Github에 push를 한다.

2. CI 도구에서 변경된 코드에 대한  Build, Test, Merge를 진행하고 결과를 피드백해준다.

빌드 & 테스트에 이상이 있는 경우 -> 실패
빌드 & 테스트가 이상 없이 수행되는 경우 -> 성공!

 

CI 도구

Jenkins , TravisCI, Github Actions


CD (continuous Delivery/Delploy, 지속적 제공/배포)

CI의 빌드 자동화, 유닛 및 통합 테스트 수행 후 이어지는 CD.

효과적인 CD를 실현하기 위해서는 개발 파이프라인에 CI가 먼저 구축되어 있어야 합니다 !

 

개발, 통합, 배포, 릴리즈, 테스트를 자동화하여 지속적으로 배포하는 것을 뜻합니다.

 

continuous Delivery ( 지속적 제공 )

배포 단계에서 release할 준비 단계를 거치고 문제가 없는지 수정할 만한 것들이 없는지 개발자가 검증을 합니다.

그 후에 나온 결론이 "이제 사용자들에게 서비스를 제공해도 되겠다!" 라고 정해져서 배포를 수동적으로 진행하는 것을 말합니다. 수동 검토와 배포시간을 고려해야 합니다.

 

continuous Delploy ( 지속적 배포 )

배포 단계에서 배포할 준비가 되자마자 자동화를 통해 사용자들에게 배포를 진행하는 것을 말합니다.

승인 주기가 필요가 없으므로 개발자는 테스트 사이트의 효과와 안정성을 보장해야 합니다.

최소한의 수동 작업으로 매우 짧은 시간 안에 여러 앱 또는 업데이트를 배포할 수 있다는 것이 장점입니다.

 


마무리.

CI의 장점

하나의 소프트웨어에 대해 수많은 개발자들이 동시에 개발을 진행하고 이 소스코드를 정기적으로 통합하는 회사라면 소스코드의 병합은 상당히 많은 공수를 요구하는 일이 될 것입니다.

CI를 적용한다면 소스코드의 변동이 생길 때마다 지속적으로 개발자들의 코드를 병합하고 테스트를 진행합니다. 변경한 소스코드가 문제가 발생하지 않는지 테스트를 실행하고 일련의 과정 중 코드의 충돌, 테스트의 실패 등이 발생하면 즉각적으로 알림을 전송합니다.

이 과정을 통해 아래 장점을 얻을 수 있습니다.

  • 개발의 편의성이 증가합니다.
  • 변경된 코드에 대한 즉각적 피드백과 검증이 가능합니다.
  • 소스코드의 통합과 검증에 들어가는 시간을 단축합니다.

CD의 장점

  • CD의 장점은 실제 배포할 애플리케이션의 서버가 여러 대일 때, 배포할 작업물이 여러 개일 때 나타납니다. 수작업으로 여러 작업물을 여러 서버에 배포할 때 발생하기 쉬운 실수를 방지할 수 있습니다.
  • '원클릭으로 빌드, 테스트, 배포 자동화'를 진행할 수 있습니다. 즉 소스코드 변경부터 배포까지의 작업을 자동화할 수 있기 때문에 수작업으로 할 때의 불편함을 줄일 수 있습니다.
  • 개발자는 개발에만 집중할 수 있게 됩니다.

 

 

참고, 출처:

https://llshl.tistory.com/50

https://www.redhat.com/ko/topics/devops/what-is-ci-cd

https://deveric.tistory.com/106

https://jhleed.tistory.com/130

https://jud00.tistory.com/entry/CICD%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C

'엘리스 ai 트랙 > FE' 카테고리의 다른 글

this [모던 js 딥다이브]  (0) 2022.09.22
Javascript - ES11 ( ES2020 )의 특징들  (0) 2022.04.04
http 1.1 과 http 2 의 차이점  (0) 2022.03.31
HTTP  (0) 2022.03.13
기본값+rest+spread  (0) 2022.03.07

http 1.1의 문제점

1) HOLB (Head Of Line Blocking) - 특정 응답의 지연

HTTP/1.1의 경우 기본적으로 connection당 하나의 요청을 처리하도록 설계되어 있기 때문에 HOL Blocking이 발생할 수 있습니다. 

HOL (Head-Of-Line) 블로킹이란 네트워크에서 같은 큐에 있는 패킷이 첫 번째 패킷에 의해 지연될 때 발생하는 성능 저하 현상을 말합니다.

웹 환경에서의 HOL 블로킹은 패킷 처리 속도 지연 뿐만 아니라 최악의 경우 패킷 드랍까지 발생할 수 있습니다.

=> HTTP/1.1의 connection당 하나의 요청 처리를 개선할 수 있는 기법 중 pipelining(connection을 통해서 다수개의 파일을 요청/응답받을 수 있는 기법)이 존재

* 하지만, 이 또한 처음의 요청에 대한 응답이 오래 걸리는 경우, 그 다음 응답까지의 시간이 지연되는 현상이 발생합니다.

 

2) 무겁고 중복 많은 헤더 구조

HTTP/1.1의 헤더에는 많은 메타 정보들이 저장되어 있습니다. 클라이언트가 서버로 보내는 HTTP 요청은 매 요청마다 중중복된 헤더 값을 전송하게 되며 서버 도메인에 관련된 쿠키 정보도 헤더에 함께 포함되어 전송된다.

이러한 반복적인 헤더 전송, 쿠키정보로 인한 헤더 크기 증가가 HTTP/1.1의 단점입니다.

 


http 2가 나오기 전의 단점 극복 방법

1) Image spriting

웹 페이지를 구성하는 다양한 아이콘 이미지 파일의 요청 횟수를 줄이기 위해 아이콘을 하나의 큰 이미지로 만든 다음 CSS에서 해당 이미지의 좌표 값을 지정하여 표시

 

2) 도메인 샤딩

HTTP/1.1은 하나의 도메인에 하나의 connection만을 맺을 수 있습니다.

이런 단점을 극복하고자, 서버는 같지만, 도메인명을 여러개 설정해서 이를 리소스 주소로 내려주면, 한 브라우저에서 여러 개의 connection을 맺을 수 있게 됩니다. 이렇게 되면, 리소스를 병렬적으로, 동시다발적으로 받을 수 있게 됩니다.

하지만, 너무 많은 도메인을 연결하는 경우, DNS 검색과 TCP handshake에서 발생하는 시간 때문에 역효과가 날 수도 있다고 합니다.

 

3)css, javascript 이미지 압축

HTTP를 통해 전송되는 데이터의 용량을 줄이기 위해서 CSS, Javascript를 축소해서 적용합니다.

name.min.js , name.min.css 등이 그 예입니다.

 

4) data URI 스키마

// data url
<img src="data:image/png;base64, ....."/>

// image file
<img src="./1.png">

HTML 문서 내에 이미지 리소스를 Base64로 인코딩된 이미지 데이터로 직접 기술하는 방법으로 이를 이용하여 서버로의 요청을 줄이는 방식입니다.

 


 

http 2의 등장!

http 2의 주요 특징들

1) 멀티 플렉싱

HTTP/2는 multiplexed Streams를 이용하여 Connection 한 개로 동시에 여러 개의 메시지를 주고 받을 수 있으며 응답은 요청 순서에 상관없이 stream으로 주고 받을 수 있습니다. HTTP/1.1의 Connection Keep-Alive, Pipelining의 개선 버전으로 보면 됩니다.

 

2) steam priorization

클라이언트가 요청한 HTML문서 안에 CSS파일 1개와 Image파일 2개가 존재하고 이를 클라이언트가 각각 요청하고 난 후 Image파일보다 CSS파일의 수신이 늦어지는 경우 브라우저의 렌더링이 늦어지는 문제가 발생하는데 HTTP/2의 경우 리소스간 의존관계(우선순위)를 설정하여 이런 문제를 해결

* 각 스트림에 가중치를 할당해서 우선순위를 결정합니다.

 

3) 서버 푸시

클라이언트(브라우저)가 HTML문서를 요청하고 해당 HTML에 여러 개의 리소스(CSS, Image...) 가 포함되어 있는 경우 HTTP/1.1에서 클라이언트는 요청한 HTML문서를 수신한 후  HTML문서를 해석하면서 필요한 리소스를 재 요청하는 반면,

HTTP/2에서는 Server Push기법을 통해서 클라이언트가 요청하지 않은 (HTML문서에 포함된) 리소스도 Push 해주는 방법으로 클라이언트의 요청을 최소화 해서 성능 향상을 이끌어 냅니다.

 

4) 헤더 압축

HTTP/2는 헤더 정보를 압축하기 위해서 Header TableHuffman Encoding 기법을 사용해서 처리하는데 이를 HPACK 압축방식이라고 부르며 별도의 명세서(RFC 7531)로 관리하고 있습니다.

위 그림처럼 클라이언트가 두번의 요청을 보낸다고 가정할 때 HTTP/1.x의 경우 헤더 중복이 발생해도 중복 전송합니다. 하지만 HTTP/2에서는 헤더에 중복이 있는 경우 Static/Dynamic Header Table 개념을 이용해서 중복을 검출해내고 해당 테이블에서의 index 값 + 중복되지 않은 Header 정보를 Huffman Encoding 방식으로 인코딩한 데이터를 전송합니다.

 


 

마무리.

HTTP(Hypertext Transfer Protocol)는 월드 와이드 웹 (World Wide Web)의 데이터 통신의 기초가 되는 응용 프로그램 프로토콜입니다.

간단하고 추상적인 예는 레스토랑 손님과 웨이터가 될 것입니다. 게스트(클라이언트)가 웨이터에게(서버)에게 식사를 요청하면 (웨이터 (응용 프로그램 논리)) 식사를 게스트에게 가져옵니다.

HTTP1.x 대신 HTTP2.0만 사용해도 아래 그림과 같이 응답속도(식사를 게스트에게 가져오는 속도)가 15~50%정도 향상됩니다.

 

 

참고, 출처:

https://ijbgo.tistory.com/26

https://seokbeomkim.github.io/posts/http1-http2/

https://jaeho214.tistory.com/32

https://lalwr.blogspot.com/2019/01/http1-vs-http2.html

https://mygumi.tistory.com/282

https://yceffort.kr/2021/05/http1-vs-http2

https://kooku.netlify.app/web/http1.1-vs-http2/

'엘리스 ai 트랙 > FE' 카테고리의 다른 글

Javascript - ES11 ( ES2020 )의 특징들  (0) 2022.04.04
CI와 CD  (0) 2022.03.31
HTTP  (0) 2022.03.13
기본값+rest+spread  (0) 2022.03.07
구조분해할당  (0) 2022.03.07

HTTP (Hypertext Transfer Protocol) ?

  • 인터넷에서 데이터를 주고받을 수 있는 통신규약.
  • 서버-클라이언트 모델을 따르는 프로토콜로 TCP/IP 위에서 동작하며 well-known 포트인 80번 포트를 사용

 

  • 표준 버전은 HTTP/1.1 부터 HTTP/2 HTTP/3이 있다. 여기선 HTTP/1.1의 내용을 정리한다.

 

HTTP Request & Response

HTTP 프로토콜로 데이터를 주고받을 때, 클라이언트와 서버를 이해해야 한다.

클라이언트란 요청을 보내는 쪽, 웹 관점에서는 브라우저

서버란 요청을 받는 쪽, 일반적으로 데이터를 보내주는 원격지의 컴퓨터

 

URL (Uniform Resource Locators)

=> 서버에 자원을 요청하기 위한 영문 주소

 

 

HTTP 프로토콜 특징

1. 비연결성(Connectionless)

클라이언트와 서버가 한 번 연결을 맺은 후, 클라이언트 요청에 대해 서버가 응답을 마치면 맺었던 연결을 끊어버리는 성질을 말한다.

 

1) 비연결성의 장점

HTTP는 인터넷 상에서 불특정 다수의 통신환경을 기반으로 설계되었다.

만약 서버에서 다수의 클라이언트와 연결을 계속 유지한다면, 아주 많은 리소스가 발생하게 된다.

=> 연결을 유지하기 위한 리소스를 줄이면 더 많은 연결을 할 수 있으므로 비연결적인 특징을 갖는다

 

2) 비연결성의 단점

하지만, 서버는 클라이언트를 기억하고 있지 않으므로 동일한 클라이언트의 모든 요청에 대해, 

매번 새로운 연결을 시도/해제의 과정을 거쳐야 하므로 연결/해제에 대한 오버헤드가 발생한다는 단점이 있다.

 

3) KeepAlive

이에 대한 해결책으로 오버헤드를 줄이기 위해 KeepAlive 속성을 사용한다.

KeepAlive는 정된 시간동안 서버와 클라이언트 간 패킷 교환이 없을 경우, 상태방의 안부를 묻기 위해 패킷을 주기적으로 보내는 것.

이 때 패킷에 반응이 없으면 접속을 끊게 된다.

 

주기적으로 클라이언트의 상태를 체크한다는 점에서 KeepAlive도 비연결성의 단점을 완벽히 해결해주진 않는다.

KeepAlive 속성이 On 상태라 해도, 서버가 바쁜 환경에서는 프로세스 수가 기하급수적으로 늘어나기 때문에 Keep Alive로 상태를 유지하기 위한 메모리를 많이 사용하게 되므로 주의해야 한다.

 

 

2. 무상태 (Stateless)

1) 무상태란?

Connectionless로 인해 서버는 클라이언트를 식별할 수가 없는데, 이를 stateless라고 한다.

 

예를 들면,

  1. 쇼핑몰에 접속
  2. 로그인
  3. 상품 클릭 -> 상세화면으로 이동
  4. 로그인
  5. 주문
  6. 로그인
  7. ...

즉, 매번 새로운 인증을 해야하는 번거로움이 발생하게 된다.

 

2) 상태를 기억하는 방법-쿠키

서버가 클라이언트를 기억하는 방법! -> 브라우저 단에서 쿠키를 저장하기

( HTTP 헤더: set-cookie )

 

3) 상태를 기억하는 방법-세션

쿠키는 브라우저에 저장되기 때문에 공격자로부터 위변조의 가능성이 높다.

세션은 브라우저가 아닌 서버단에서 사용자 정보를 저장하는 구조이기 때문에 쿠키보다는 안전하다.

 

하지만 세션도 탈취가 가능하기 때문에 보안에 완벽하다고는 할 수 없다. 또한 서버의 메모리를 많이 차지하게 되고, 만약 동시 접속자가 많은 경우에는 서버 과부화의 원인이 된다.

 

4) 상태를 기억하는 방법- 토큰을 사용하는 Oath, JWT

쿠키와 세션의 문제점을 보완하기 위해 Token 기반의 인증방식이 도입!

핵심은 보호할 데이터를 토큰으로 치환해서 원본 데이터 대신 토큰을 사용하는 기술이다.

그래서 중간에 공격자로부터 토큰이 탈취당하더라도 데이터에 대한 정보를 알 수 없으므로, 보안성이 높은 기술이라고 할 수 있다!

 

대표적으로 Oath, JWT가 있다

 

하지만 꼭 토큰 기반의 인증이 반드시 좋다고는 할 수 없다.

서비스에 따라 기술의 특징을 잘 이해해서 때에 따라 쿠키, 세션, oAuth, JWT 등을 적절히 사용하는 것이 좋다.

 

 

3. HTTP 메서드

클라이언트가 서버로 요청을 할 때 어떠한 목적을 갖는 행위인지 HTTP 메서드에 명시한다.

  • GET: 자료를 요청할 때 사용
  • POST: 자료의 생성을 요청할 때 사용
  • PUT: 자료의 수정을 요청할 때 사용
  • DELETE: 자료의 삭제를 요청할 때 사용
  • HEAD: 정확히 GET과 같지만, 요청에서 헤더만 가져올 때 사용
  • TRACE: 클라이언트와 서버 사이의 모든 HTTP 요청/응답 연쇄를 따라가면서 자신이 보낸 메시지의 이상유무 파악
  • OPTIONS: 서버가 실제 요청을 보내기 전에 서버를 테스트해보는 용도, 서버가 어떤 메소드를 지원하는지 알아본다.

 

4. 응답 상태코드

클라이언트가 서버에 요청하면, 서버는 요청에 대한 처리 상태를 숫자로 변환하는데, 이를 응답 코드라 한다.

HTTP 응답의 헤더에 상태코드를 추가해서 응답한다.

  • 100 - 109
    • 메시지 정보
  • 200 - 206
    • 요청 성공
  • 300 - 305
    • 리다이렉션
  • 400 - 415
    • 클라이언트 에러
  • 500 - 505
    • 서버 에러

상태 코드에 대한 자세한 정보 : https://developer.mozilla.org/ko/docs/Web/HTTP/Status/100

 

API 개발 시 올바른 상태 코드를 응답하는 것은 매우 중요하다.

예를 들어, 사용자가 요청 파라미터를 잘못 입력한 경우 잘못된 파라미터로 인해 비즈니스 로직에서 에러가 발생했다고 하여 500 코드를 반환하면 안된다!

사용자가 잘못 입력한 경우므로 403 코드를 반환해야 한다!

 

5. 헤더

 

1) 요청 헤더

시작줄을 HTTP/1.1 표준 버전

다음 줄부터 요청 헤더 정보

 

크롬 주소창에 https://wjdalwjdal6951.tistory.com/ 이라고 치는 것

=> GET https://wjdalwjdal6951.tistory.com/ HTTP/1.1 요청을 보내는 것과 같다.

 

 

< CORS, Origin, Access-Control-Allow-Origin에 대한 문서>

 

읽을 사이트: https://ko.javascript.info/fetch-crossorigin#ref-2011

만약, https://javascript.info/page에서  https://anywhere.com/request 에 요청을 보낸다고 가정할 때, 헤더는 다음과 같은 형태가 된다.

GET /request
Host: anywhere.com
Origin: https://javascript.info
...

Host

서버의 도메인 네임

Host 헤더는 반드시 하나가 존재해야 한다.

Host: anywhere.com

 

Origin

요청이 어느 도메인에서 왔는지 명시, 출처

스킴 https , 도메인 javascript.info , 포트로 정의된다

응답 헤더의 Access-Control-Allow-Origin와 관련

요청을 보낸 주소와 받는 주소가 다르면 CORS 문제가 발생하기도 한다.

Origin: https://javascript.info

 

 

Content-type

컨텐츠의 타입(MIME 미디어 타입) 및 문자 인코딩 방식(EUC-KR, UTF-8 등)을 지정한다.

Content-Type: text/html; charset=utf-8

=> 현재 메시지 내용이 html 텍스트 문서이고 utf-8 문자 인코딩 방식으로 표현됨.

 

**참고) POST 혹은 PUT 처럼 클라이언트가 서버에게 폼을 통해서 파일을 업로드할 때는

컨텐츠 타입을 application/x-www-form-urlencoded 

Content-Type: application/x-www-form-urlencoded

컨텐츠 타입을 multipart/form-data로 작성 (바이너리 데이터)

-> input이 여러개일 때 하나는 텍스트타입이고, 하나는 이미지타입일 때 2가지 종류의 데이터를 구분해서 넣어주기 위해 multipart 타입이 등장했다.

 

 

If-Modified-Since

페이지가 수정되었으면 최신 버전 페이지 요청을 위한 필드

만약 요청한 파일이 이 필드에 지정된 시간 이후로 변경되지 않았다면,

서버로부터 데이터를 전송받지 않는다. 304 상태코드(리다이렉션)를 전송받게 된다.

If-Modified-Since: Sat, 29 Mar 2022 19:43:31 GMT

 

 

Cookie

웹서버가 클라이언트에 쿠키를 저장해 놨다면 해당 쿠키의 정보를 이름-값 쌍으로 웹서버에 전송함

 

 

2) 응답 헤더

 

Access-Control-*

Access-Control-Allow-Origin: *

이 응답이 주어진 origin으로부터의 요청 코드와 공유될 수 있는지 여부 확인

 

Ex) Access-Control-Allow-Origin: https://javascript.info 

  • 프로토콜, 도메인, 포트 중 하나만 달라도 CORS 에러가 발생

EX) Access-Control-Allow-Origin: *

  • 만약 주소를 일일이 지정하기 싫다면 * 으로 모든 주소에 CORS를 허용한다. 하지만 그만큼 보안에 취약해진다.

 

**관련 헤더

 - Access-Control-Request-Method : 실제로 보내려는 메서드
 - Access-Control-Request-Headers : 실제로 보내려는 헤더
 - Access-Control-Allow-Methods : 서버가 허용하는 메서드
 - Access-Control-Allow-Headers : 서버가 허용하는 헤더

 

**참고: CORS ( Cross Origin Resource Sharing )란? 

서로 다른 도메인 간에 리소스를 활용할 필요가 있을때, 어떤 규칙으로 누구에게 허용할 것인지를 정의하는 HTTP 표준의 일부
https://developer.mozilla.org/ko/docs/Web/HTTP/CORS

 

Set-Cookie

서버에서 클라이언트에게 세션 쿠키 정보를 설정

set-cookie: UserID=JohnDoe; path=/; expires=Mon, 14 Mar 2022 02:05:36 GMT; secure; HttpOnly; SameSite=Lax

자세한 정보: https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Set-Cookie

 

 

Last-Modified 

서버가 알고있는 가장 마지막 수정날짜와 시각. 저장된 리소스가 이전과 같은지 유효성 검사자로 사용됨

Last-Modified: Tue, 15 Mar 2022 12:45:26 GMT

 

 

Location

3xx 상태코드(라다이렉션)일 때 어느 페이지로 이동할지 알려준다

HTTP/1.1 302 Found
Location: /login

=> 위와 같은 응답시 /login 주소로 리다이렉트 한다.

 

 

Allow

해당 엔터티에 대해 서버에서 지원 가능한 HTTP 메소드 리스트.

때론, HTTP 요청 메세지의 HTTP 메소드 OPTIONS에 대한 응답용 항목으로 사용

Allow: GET, HEAD

=> 서버에서 제공가능한 HTTP 메서드는 GET, HEAD 뿐임을 알린다.

 

 

 

 

 

 

참고:출처:

https://developer.mozilla.org/ko/docs/Web/HTTP/CORS

https://ko.javascript.info/fetch-crossorigin#ref-2011

 

https://goddaehee.tistory.com/169

https://www.zerocho.com/category/HTTP/post/5b4c4e3efc5052001b4f519b

https://joshua1988.github.io/web-development/http-part1/#http-%EC%9A%94%EC%B2%AD-%EB%A9%94%EC%84%9C%EB%93%9C

 

https://github.com/baeharam/Must-Know-About-Frontend/blob/main/Notes/network/http.md#%EC%9D%91%EB%8B%B5-%ED%97%A4%EB%8D%94

https://victorydntmd.tistory.com/286

https://gmlwjd9405.github.io/2019/01/28/http-header-types.html

'엘리스 ai 트랙 > FE' 카테고리의 다른 글

CI와 CD  (0) 2022.03.31
http 1.1 과 http 2 의 차이점  (0) 2022.03.31
기본값+rest+spread  (0) 2022.03.07
구조분해할당  (0) 2022.03.07
IIFE (즉시 호출 함수 표현식)  (0) 2022.02.28

1. 매개변수 기본값 (Default Parameter value)

  • 함수를 호출할 때 인수를 전달하지 않은 경우에도 에러가 발생하지는 않는다.
  • 함수는 매개변수와 인수의 개수를 체크하지 않는다. 
  • 인수가 부족한 경우, 매개변수의 값은 undefined이다.
function sum(x, y) {
  // 매개변수의 값이 falsy value인 경우, 기본값을 할당한다.
  x = x || 0;
  y = y || 0;

  return x + y;
}

console.log(sum(1));    // 1
console.log(sum(1, 2)); // 3

ES6에서는 매개변수 기본값을 사용하여 함수 내에서 수행하던 인수 체크 및 초기화를 간소화할 수 있다.

( 매개변수 기본값은 매개변수에 인수를 전달하지 않았을 경우에만 유효하다. )

function sum(x = 0, y = 0) {
  return x + y;
}

console.log(sum(1));    // 1
console.log(sum(1, 2)); // 3

 

 

2. Rest 파라미터

  • Rest 파라미터는 함수에 전달된 인수들의 목록을 배열로 전달받는다.
  • 이름 그대로 먼저 선언된 파라미터에 할당된 인수를 제외한 나머지 인수들이 모두 배열에 담겨 할당된다
function foo(param, ...rest) {
  console.log(param); // 1
  console.log(rest);  // [ 2, 3, 4, 5 ]
}
foo(1, 2, 3, 4, 5);
  • rest 파라미터는 반드시 마지막 파라미터여야 한다.
function foo( ...rest, param1, param2) { }

foo(1, 2, 3, 4, 5);
// SyntaxError: Rest parameter must be last formal parameter
  • 함수 정의시 선언한 매개변수 개수를 나타내는 함수 객체의 length 프로퍼티에 영향을 주지 않는다
function foo(...rest) {}
console.log(foo.length); // 0

function bar(x, ...rest) {}
console.log(bar.length); // 1

function baz(x, y, ...rest) {}
console.log(baz.length); // 2

 

ES5에서는 인자의 개수를 사전에 알 수 없는 경우 arguments 객체를 통해서 인수를 확인한다.

**arguments 객체: 전달된 인수들의 정보를 담고 있는 순회가능한 유사배열 객체

// ES5
var foo = function () {
  console.log(arguments);
};

foo(1, 2); // { '0': 1, '1': 2 }

배열이 아닌 유사배열 객체이므로 배열메소드를 사용하려면 Function.prototype.call을 사용하는 번거로움이 있다

// ES5
function sum() {
  /*
  가변 인자 함수는 arguments 객체를 통해 인수를 전달받는다.
  유사 배열 객체인 arguments 객체를 배열로 변환한다.
  */
  var array = Array.prototype.slice.call(arguments);
  return array.reduce(function (pre, cur) {
    return pre + cur;
  });
}

console.log(sum(1, 2, 3, 4, 5)); // 15

 

ES6에서는 rest 파라미터를 사용해서 가변인자의 목록을 배열로 전달받을 수 있다.

유사배열객체를 배열로 변환하는 번거로움을 피할 수 있다!

/ ES6
function sum(...args) {
  console.log(arguments); // Arguments(5) [1, 2, 3, 4, 5, callee: (...), Symbol(Symbol.iterator): ƒ]
  console.log(Array.isArray(args)); // true
  return args.reduce((pre, cur) => pre + cur);
}
console.log(sum(1, 2, 3, 4, 5)); // 15

 

 

3. spread 문법

  • spread 연산자는 연산자의 대상 배열 또는 이터러블을 개별 요소로 분리한다.

**이터러블: Array, String, Map, Set, DOM구조,     for...of로 순회할 수 있는 자료구조.

// 문자열은 이터러블이다.
console.log(...'Hello');  // H e l l o

// Map과 Set은 이터러블이다.
console.log(...new Map([['a', '1'], ['b', '2']]));  // [ 'a', '1' ] [ 'b', '2' ]
console.log(...new Set([1, 2, 3]));  // 1 2 3

// 이터러블이 아닌 일반 객체는 Spread 문법의 대상이 될 수 없다.
console.log(...{ a: 1, b: 2 });
// TypeError: Found non-callable @@iterator

 

함수의 파라미터로 사용하는 방법

function foo(x, y, z) {
  console.log(x); // 1
  console.log(y); // 2
  console.log(z); // 3
}

const arr = [1,2,3]
/* ...[1, 2, 3]는 [1, 2, 3]을 개별 요소로 분리한다(→ 1, 2, 3)
   spread 문법에 의해 분리된 배열의 요소는 개별적인 인자로서 각각의 매개변수에 전달된다. */
foo(...arr);

 

Rest와 헷갈리지 않기!!
  • Rest 파라미터는 함수 선언문의 파라미터에 spread 연산자(...)를 이용해서 받으면 가변인자를 받아 배열로 만들어서 사용하는 것 즉, spread 문법을 사용해서 파라미터를 정의한 것.
  • 함수 호출문의 파라미터에 spread 연산자(...)를 이용해서 호출하면 배열이 해당 매개변수로 각각 매핑되는 것.      (= spread 문법을 사용한 배열을 인수로 함수에 전달하면 배열의 요소를 순차적으로 파라미터에 할당하는 것.)
  • 또한, Rest 파라미터는 반드시 마지막에 써야 하지만, spread 호출(spread 문법을 사용한 인수)에서는 자유롭게 사용할 수 있다.
//Rest
function foo(param, ...rest) {
  console.log(param); // 1
  console.log(rest);  // [ 2, 3 ]
}
foo(1, 2, 3);
 
//Spread호출
function bar(x, y, z) {
  console.log(x); // 1
  console.log(y); // 2
  console.log(z); // 3
}
bar(...[1, 2, 3]);

 

1. 배열에서 사용하기

//ES5
var arr = [1, 2, 3];
console.log(arr.concat([4, 5, 6])); // [ 1, 2, 3, 4, 5, 6 ]
 
// ES6
const arr = [1, 2, 3];
// ...arr은 [1, 2, 3]을 개별 요소로 분리한다
console.log([...arr, 4, 5, 6]); // [ 1, 2, 3, 4, 5, 6 ]

-> concat() 대신 가독성이 좋아졌다

 

// ES5
var arr1 = [1, 2, 3];
var arr2 = [4, 5, 6];
 
// apply 메소드의 2번째 인자는 배열. 이것은 개별 인자로 push 메소드에 전달된다.
Array.prototype.push.apply(arr1, arr2);
//arr1.push(4, 5, 6); 하는 것과 동일하다
console.log(arr1); // [ 1, 2, 3, 4, 5, 6 ]
 
// ES6
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
 
// ...arr2는 [4, 5, 6]을 개별 요소로 분리한다
arr1.push(...arr2); // == arr1.push(4, 5, 6);
 
console.log(arr1); // [ 1, 2, 3, 4, 5, 6 ]

-> push 를 개별 요소로 전달할 수 있으니 훨씬 간결하고 가독성 또한 좋아졌다.

 

const htmlCollection = document.getElementsByTagName('li');

// 유사 배열인 HTMLCollection을 배열로 변환한다.
const newArray = [...htmlCollection]; // Spread 문법

// ES6의 Array.from 메소드를 사용할 수도 있다.
// const newArray = Array.from(htmlCollection);

-> 유사 배열 객체(Array-like Object)를 배열로 손쉽게 변환할 수 있다.

 

 

2. 객체에서 사용하기

const o1 = { x: 1, y: 2 };
const o2 = { ...o1, z: 3 };
console.log(o2); // { x: 1, y: 2, z: 3 }
 
const target = { x: 1, y: 2 };
const source = { z: 3 };
// Object.assign를 사용하여도 동일한 작업을 할 수 있다.
// Object.assign은 타깃 객체를 반환한다
console.log(Object.assign(target, source)); // { x: 1, y: 2, z: 3 }

-> Object.assign을 대체할 수 있는 간편한 문법이다.

 

 

 

 

참고,출처

https://poiemaweb.com/es6-extended-parameter-handling

https://jeong-pro.tistory.com/117

'엘리스 ai 트랙 > FE' 카테고리의 다른 글

http 1.1 과 http 2 의 차이점  (0) 2022.03.31
HTTP  (0) 2022.03.13
구조분해할당  (0) 2022.03.07
IIFE (즉시 호출 함수 표현식)  (0) 2022.02.28
var vs let vs const  (0) 2022.02.21

Destructuring (비구조화, 파괴)

구조화된 배열 또는 객체를 Desturcturing하여 개별적인 변수에 할당하는 것.
배열 또는 객체 리터럴에서 필요한 값만을 추출하여 변수에 할당하거나 반환할 때 유용

 

1. 배열 Destructuring 

  • 배열의 각 요소를 추출하여 변수 리스트에 할당
  • 추출/할당 기준은 배열의 인덱스
const arr = [1, 2, 3];

// 배열의 인덱스를 기준으로 배열로부터 요소를 추출하여 변수에 할당
// 변수 one, two, three가 선언되고 arr(initializer(초기화자))가 Destructuring(비구조화, 파괴)되어 할당된다.
const [one, two, three] = arr;
// 디스트럭처링을 사용할 때는 반드시 initializer(초기화자)를 할당해야 한다.
// const [one, two, three]; // SyntaxError: Missing initializer in destructuring declaration

console.log(one, two, three); // 1 2 3

 

할당연산자(=) 왼쪽에 배열형태의 변수 리스트가 있고, 오른쪽의 배열은 인덱스를 기준으로 할당된다

let x, y, z;

[x, y] = [1, 2];
console.log(x, y); // 1 2

[x, y] = [1];
console.log(x, y); // 1 undefined

[x, y] = [1, 2, 3];
console.log(x, y); // 1 2

[x, , z] = [1, 2, 3];
console.log(x, z); // 1 3

// 기본값
[x, y, z = 3] = [1, 2];
console.log(x, y, z); // 1 2 3

[x, y = 10, z = 3] = [1, 2];
console.log(x, y, z); // 1 2 3

// spread 문법
[x, ...y] = [1, 2, 3];
console.log(x, y); // 1 [ 2, 3 ]

 

2. 객체 Destructuring 

  • ES6의 객체 destructuring은 각 프로퍼티를 객체로부터 추출하여 변수 리스트에 할당
  • 할당 기준은 프로퍼티 이름(키)
const obj = { firstName: 'Ungmo', lastName: 'Lee' };

// 프로퍼티 키를 기준으로 디스트럭처링 할당이 이루어진다. 순서는 의미가 없다.
// 변수 lastName, firstName가 선언되고 obj(initializer(초기화자))가 Destructuring(비구조화, 파괴)되어 할당된다.
const { lastName, firstName } = obj;

console.log(firstName, lastName); // Ungmo Lee

 

할당연산자(=) 왼쪽에 객체형태의 변수 리스트가 있고, 프로퍼티 이름으로 값을 추출한다

// 프로퍼티 키가 prop1인 프로퍼티의 값을 변수 p1에 할당
// 프로퍼티 키가 prop2인 프로퍼티의 값을 변수 p2에 할당
const { prop1, prop2 } = { prop1: 'a', prop2: 'b' };
console.log({ prop1, prop2 }); // { prop1: 'a', prop2: 'b' }

// default value
const { prop1, prop2, prop3 = 'c' } = { prop1: 'a', prop2: 'b' };
console.log({ prop1, prop2, prop3 }); // { prop1: 'a', prop2: 'b', prop3: 'c' }

 

객체 비구조화는 프로퍼티 이름으로 필요한 프로퍼티 값만을 추출할 수 있다.

const todos = [
  { id: 1, content: 'HTML', completed: true },
  { id: 2, content: 'CSS', completed: false },
  { id: 3, content: 'JS', completed: false }
];

// todos 배열의 요소인 객체로부터 completed 프로퍼티만을 추출한다.
const completedTodos = todos.filter(({ completed }) => completed);
console.log(completedTodos); // [ { id: 1, content: 'HTML', completed: true } ]

filter 메소드의 콜백함수는 todos배열을 순회하며 첫번째 인자로 대상배열의 요소를 받는다.

그래서 completed가 true인 객체만 반환한다

이때 필요한 프로퍼티만을 추출할 수 있다.

 

중첩객체의 경우는 아래와 같이 사용한다

const person = {
  name: 'Lee',
  address: {
    zipCode: '03068',
    city: 'Seoul'
  }
};

const { address: { city } } = person;
console.log(city); // 'Seoul'

 

fail-soft 비구조화는 값이 찾아지지 않더라도 오류가 생기지 않고 undefined를 반환하는 것을 의미한다.

// Fail-soft 비구조화 (Fail-soft는 고장이 나도 작동하도록 짠 프로그램을 말한다)
var [a] = [];
a === undefined;

// 기본 값이 있는 Fail-soft 비구조화
var [a = 1] = [];
a === 1;

하지만, 중첩된 객체에서의 프로퍼티는 fail-soft가 적용되지 않는다는 점에 유의해야 합니다.

let options = {},
  // `delay` would be `undefined`, but trying
  // to assign to `name` is an error
  // since `options.info` is already `undefined`
  {
    delay, 
    info: { name },
  } = options

options.info와 delay는 모두 undefined를 반환한다.

하지만, info안의 name은 error을 발생시킨다. ( name을 감싸고 있는 info가 이미 undefined이기 때문에 ) 

 

 

참고, 출처: 

https://www.benmvp.com/blog/learning-es6-destructuring/

https://poiemaweb.com/es6-destructuring

'엘리스 ai 트랙 > FE' 카테고리의 다른 글

HTTP  (0) 2022.03.13
기본값+rest+spread  (0) 2022.03.07
IIFE (즉시 호출 함수 표현식)  (0) 2022.02.28
var vs let vs const  (0) 2022.02.21
스코프  (0) 2022.02.14

IIFE (Immediately Invoked Funtion Expressions)?

IIFE(즉시 호출 함수 표현식)은 주로 이름 충돌 최소화 및 private 변수 생성에 사용되는 자바스크립트 디자인 패턴이다.
(function(){ 
  console.log('IIFE'); 
})();

함수 표현식으로서 익명 또는 기명 함수를 만들고는, 그 즉시 함수 호출을 함

 

"이 안에 들어있는 코드를 바로 실행하라" 하는 표현으로 이해하자.

 

 

함수 선언문과 함수 표현식?

함수 선언은 함수 이름을 생략할 수 없고, 자바스크립트의 실행 컨텍스트에 로딩되어 있으므로 언제든지 호출할 수 있다.

함수 표현식은 값으로서 평가되며, 익명함수로 나타낼 수 있고, 인터프리터가 해당 라인에 도달하였을 때만 실행됨

 

함수 선언을 괄호 연산자로 그룹핑해주면 함수 표현식으로 나타낼 수 있다.

halo() //호이스팅으로 인해 오류가 나지 않는다, 출력: halo

function halo() {
  console.log('halo')
}


// 익명함수로 사용
(function(){
	console.log("foo")
})() //출력: foo

// 기명함수로 사용
(function foo() {
    console.log('foo')
})() // 함수선언문이 함수표현식으로 바뀌었고 IIFE로 인해서 즉시 호출된다, 출력: foo

foo() //ReferenceError: foo is not defined
//함수 이름인 foo는 함수 몸체에서만 참조할 수 있는 식별자이므로 IIFE를 다시 호출할 수 없다.

 

IIFE의 구조

일반적으로 아래와 같은 두가지 방법으로 사용한다

(function(){ console.log('IIFE'); })();
( function(){ console.log('IIFE'); }() );

ES6에서 도입된 화살표함수도 함께 사용할 수 있다.

(() => console.log('IIFE with arrow'))();

(() => {
    console.log('IIFE with arrow')
})()

 

언제, 왜 사용하나요?

1. 이름 충돌 최소화

=> 불필요한 전역 변수와 함수를 생성하지 않는 것이 좋다

 

프로그램 초기화 시점에 init 함수를 실행한다고 가정하자.

function init() {
  var operate = 'init';
  console.log(operate)
}
init()

이 함수는 전역 scope에 선언되어 이름 충돌이 발생할 확률이 높다. 

개발자 의도와는 달리, 다른 코드에서 init 함수가 재호출될 수도 있다.

(function init() {
  var operate = 'init';
  console.log(operate)
})()

따라서, 외부 코드로부터 사용되지 않는, 단 한 번 호출하는 코드들, 변수와 함수들은 IIFE를 사용하여 전역 scope 오염을 방지하고 다른 개발자가 실수로 호출할 수 없게 해준다.

=> 즉시 실행한 뒤에 전역에서 사라지기 때문에 전역 scope와 충돌하지 않는 것이다.

 

2. clousure와 private 데이터

IFE의 장점은 내부에 있는 것들이 모두 자신만의 스코프를 가지지만, IIFE 자체는 함수이므로 스코프 밖으로 무언가를 내보낼 수 있다는 것이다.

const greet = (function() {
	const secret = "비밀"
return `비밀의 길이는 ${secret.length}이다.`;
})()

console.log(greet) //비밀의 길이는 2 이다

변수 secret은 IIFE 스코프 안에서 안전하게 보호되며 외부에서 접근할 수도 없다.

 

IIFE는 함수이므로 무엇이든 반환할 수 있으며, 배열, 객체, 함수를 반환할 수도 있다

const getCount = (function(){
  let count = 0;
  return function() {
    ++count;
    return count;
  }
})();

console.log(getCount()); // 1
console.log(getCount()); // 2

IIFE 안의 익명함수는 closure가 되며, 변수 count는 private data로, IIFE 안에 안전하게 보관되어 있으므로 전역에서 접근이 안된다.

  • getCount 변수외부함수(IIFE)를 실행한 결과를 저장한다. 처음 한번만 실행되기 때문에 count는 0으로 저장되며 내부함수(익명함수)를 반환한다.
  • getCount()가 호출될 때마다 내부함수가 실행되기 때문에 count변수가 증가하고 반환되기 때문에 콘솔에 나타나는 것은 내부 함수에서 반환한 업데이트된 count값이다.

 

이를 모듈 패턴이라고도 한다. (클로저를 기반으로 동작하면서, 전역변수의 억제는 물론 캡슐화까지 구현)

 

3. 변수의 별칭

만약, jQuery와 또다른 라이브러리를 사용하고 있는데, 둘다 $을 전역 변수로 사용한다면 충돌 문제가 생길 수 있다.

이럴 때, jQuery를 인자로 받는 IIFE 블럭으로 감싸주면 블록 내의 $가 jQuery로 정의되면서 충돌을 방지할 수 있게 된다.

(function($) {
  //예시 코드
   $("p").on("click", function() {
       $("p").css("color", "red");
    });
})(jQuery)

 

4. 자주 사용되는 특정한 키워드를 압축

(function(window, document, undefined) {
   window.alert("안녕")
   document.write("반가워")
})(window, document);

이 코드를 압축해보면,

(function(a, b, c) {
   a.alert("안녕")
   b.write("반가워")
})(window, document);

매개변수로 받은 window, document, undefined를 a,b,c 라는 토큰으로 지정해주기 때문에 

이 IIFE 블록 내에 있는 모든 window 키워드는 a로 대체할 수 있다.

-> 스크립트가 길면 길수록, 이 방식으로 파일의 용량을 크게 줄일 수 있다.

 

 

참고, 출처:

https://velog.io/@bisu8018/JS-IIFEImmediately-Invoked-Function-Expressions-%EB%9E%80

http://chanlee.github.io/2014/01/11/understand-javascript-iife/

https://medium.com/sjk5766/iife-immediately-invoked-function-expression-%EC%A0%95%EB%A6%AC-53ab6543b828

https://findawayer.tistory.com/entry/IIE%EC%9D%98-%EC%9D%98%EB%AF%B8%EB%8A%94

https://code-masterjung.tistory.com/65

'엘리스 ai 트랙 > FE' 카테고리의 다른 글

기본값+rest+spread  (0) 2022.03.07
구조분해할당  (0) 2022.03.07
var vs let vs const  (0) 2022.02.21
스코프  (0) 2022.02.14
BFC (Block Formatting Context)  (0) 2022.02.05

변수(Variable)

고유한 식별자(변수명)에 의해 구별해서 값을 참조할 수 있다.

식별자는 데이터가 저장된 메모리 상의 주소를 기억해서 메모리에 저장된 값을 참조한다.

 

식별자(변수명) 명명 규칙

  • 반드시 영문자(특수문자 제외), underscore ( _ ), 또는 달러 기호($)로 시작하여야 한다. 이어지는 문자에는 숫자(0~9)도 사용할 수 있다.
  • 자바스크립트는 대/소문자를 구별하므로 사용할 수 있는 문자는 “A” ~ “Z” (대문자)와 “a” ~ “z” (소문자)이다.

동적 타입 언어

자바스크립트는 동적 타입 언어.

타입 지정 없이 값이 할당되는 과정에서 값의 타입에 의해서 자동으로 타입이 결정됨. 

같은 변수에 여러 타입의 값을 할당가능. 이를 동적 타이핑

var foo;

console.log(typeof foo);  // undefined

foo = null;
console.log(typeof foo);  // object

foo = {};
console.log(typeof foo);  // object

foo = 3;
console.log(typeof foo);  // number

foo = 3.14;
console.log(typeof foo);  // number

foo = 'Hi';
console.log(typeof foo);  // string

foo = true;
console.log(typeof foo);  // boolean

 

var vs let vs const 

  var let const
global scope o x x
script scope x o o
functional local scope o o o
block scope x o o
재선언 o x x
재할당 o o x

이고잉님 유튜브 참고: https://www.youtube.com/watch?v=61iolhWgQt0&t=543s

 

 

ES5까지는 변수를 참조할 수 있는 방법은 var 키워드밖에 없었음

var은 타 언어와 달리 여러 특징이 있는데 이것은 심각한 문제를 일으킬 수 있다

  1. 함수레벨 스코프
    => for 문의 변수 선언문에서 선언한 변수를 for 문의 코드 블록 외부에서 참조할 수 있다, 전역변수의 남발
    for(var i=0; i<5; i++){
        console.log(i) // 0, 1, 2, 3, 4
    }
    console.log(i) //5​
  2. var 키워드 생략 허용
  3. 변수 중복 선언 허용
  4. 변수 호이스팅
    => 변수를 선언하기 전에 참조 가능

=> 대부분의 문제는 전역변수로 인해 발생, 전역변수는 유효범위가 넓어서 어디서 어떻게 사용할지 파악하기 힘들며 복잡성을 증가시킬 수 있다. 따라서 변수의 스코프는 좁을수록 좋다

 

=> ES6부터 나온 let과 const가 var의 단점을 보완하기 위해 도입

 

let

  1. 블록레벨 스코프
    for(let i=0; i<5; i++){
    	console.log(i) //0, 1, 2, 3, 4
    }
    console.log(i) //Uncaught ReferenceError: i is not defined
  2. 변수 중복 선언 금지
    var foo = 123;
    var foo = 456;  // 중복 선언 허용
    
    let bar = 123;
    let bar = 456;  // Uncaught SyntaxError: Identifier 'bar' has already been declared
  3. 호이스팅 - 일시적 사각지대 (Temporal Dead Zone)
    **호이스팅(Hoisting): var 선언문이나 function 선언문 등을 해당 스코프의 선두로 옮긴 것처럼 동작하는 특성

    변수가 선언문 이전에 참조하면 var는 undefined를 반환하지만 let은 ReferenceError가 발생한다.
    -> let 선언은 var 선언과 달리 TDZ에 의해 제약을 받는다.

    console.log(foo) //undefined
    var foo;​

    var 키워드로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어진다. (호이스팅)
    변수 선언문 이전에 변수에 접근하여도 에러가 발생하지 않는다.  
    console.log(bar); // Error: Uncaught ReferenceError: bar is not defined => TDZ
    let bar; // let bar = undefined; 와 같다, 변수 선언문에서 초기화가 이루어진다.
    
    console.log(bar); // undefined
    
    bar = 1; // 할당문에서 할당 단계가 실행된다.
    console.log(bar); // 1

    let 키워드로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행된다.
    선언단계만 호이스팅되기 때문에 초기화 이전에 변수에 접근하려고 하면 Reference Error가 발생한다
    그래서 초기화 단계가 진행돼야 참조할 수 있게 된다. (let 변수는 변수 선언문에서 초기화가 이루어진다.)
    변수 선언문 이후에 변수에 접근해야 에러가 발생하지 않는다.


    스코프의 선두부터 초기화 지점까지의 구간을 일시적 사각지대 (Temporal Dead Zone)이라고 부른다
    코드 실행이 변수가 실제 있는 위치에 도달할 때까지 액세스할 수 없는 것이다.
    let 변수가 선언된 시점에서 제어 흐름은 TDZ를 떠난 상태이며, 변수를 사용할 수 있게 된다.

 

const

  1. 블록레벨 스코프
  2. let은 재할당이 자유로우나 const는 재할당이 금지
    => const는 반드시 선언과 동시에 할당이 이루어져야 한다
    const FOO = 123;
    FOO = 456; // TypeError: Assignment to constant variable.
    const FOO; // SyntaxError: Missing initializer in const declaration
  3. 상수
  4. const와 객체
    => const는 재할당은 불가능하지만 할당된 객체의 내용(프로퍼티의 추가, 삭제, 프로퍼티 값의 변경)은 변경할 수 있다.
    const user = { name: 'Lee' };
    
    // 객체의 내용은 변경할 수 있다.
    user.name = 'Kim';
    
    console.log(user); // { name: 'Kim' }

 

결론, 정리

  • 재할당이 필요치 않은 원시 값과 객체에는 const 키워드를 사용한다
  • 재할당이 필요한 경우에 한정해 let 키워드를 사용한다. 이때 변수의 스코프는 최대한 좁게 만든다
  • ES6를 사용한다면 var 키워드는 사용하지 않는다.

 

 

참고, 출처:

https://poiemaweb.com/es6-block-scope

https://poiemaweb.com/js-data-type-variable

https://medium.com/korbit-engineering/let%EA%B3%BC-const%EB%8A%94-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85-%EB%90%A0%EA%B9%8C-72fcf2fac365

 

https://www.youtube.com/watch?v=61iolhWgQt0&t=543s 

 

'엘리스 ai 트랙 > FE' 카테고리의 다른 글

구조분해할당  (0) 2022.03.07
IIFE (즉시 호출 함수 표현식)  (0) 2022.02.28
스코프  (0) 2022.02.14
BFC (Block Formatting Context)  (0) 2022.02.05
z-index  (0) 2022.02.02

+ Recent posts