개발자는 사용자에게 안정적인 서비스를 제공하기 위해 여러가지 방법을 사용한다.

정적 타입 시스템도 그 중 하나다.

타입 시스템

타입 시스템은 개발자가 정의한 타입을 기반으로 해당 타입을 언어와 연관시키는 메커니즘이다.

  • 동등: 두 타입이 동일할 때 적용 (number:number)
  • 호환: 두가지 타입이 정확히 일치하지 않더라도 어느정도 호환이 되는지를 나타내는 규칙(any: number)
  • 추론: 타입이 정의되어 있지 않지만 주변 문맥에 따라 타입이 결정되는 규칙(a=3 일 때, a는 number)

모든 언어는 타입시스템이 존재하고 동적 타입과 정적 타입으로 나뉜다.

동적 타입과 정적 타입 차이

동적 타입

런타임에 모든 변수의 유형을 결정한다. 잘못된 경우 예외 발생시킨다.

ex) PHP, JS, Python

정적 타입

컴파일 타임에 모든 변수의 유형을 결정한다. 잘못된 경우 예외 발생시킨다.

ex) C, C++, Java

정적 타입 시스템의 필요성

  1. 타입을 명시하여 나중에 다른 개발자가 보더라도 어떤 매개변수를 넣고 이 함수가 어떤 값을 반환하는지 명시적으로 파악 가능

  2. 어떤 함수 또는 API에 대해 여러가지의 타입으로 이루어진 값이 발생될 수 있는데, 해당 값들에 대한 대처를 “미리” 할 수 있다.

  3. 매개변수 잘못입력하거나 등의 개발자 잔실수 줄여준다.

댓글 공유

[attribute]

특정 attribute를 선택하기 위해 사용하는 선택자이다.

1
2
3
a[target] {
background-color: yellow;
}
  • a 태그의 target attribute가 선언된 요소를 선택한다.
1
2
3
a[target="_blank"] {
background-color: yellow;
}
  • attribute 값에 해당하는 요소를 선택한다.

정확히 특정 단어 포함하는 attribute 선택

1
2
3
[title~="flower"] {
border: 5px solid yellow;
}
1
2
3
4
5
6
7
8
9
<h2>CSS [attribute~="value"] Selector</h2>
<p>
All images with the title attribute containing the word "flower" get a yellow
border.
</p>

<img src="klematis.jpg" title="klematis flower" width="150" height="113" />
<img src="img_flwr.gif" title="flower f" width="224" height="162" />
<img src="img_tree.gif" title="tree" width="200" height="358" />

  • title 속성에 “flower” 단어가 포함된 요소를 선택한다.

띄어쓰기로 구분된 요소는 개별요소로 취급하짐나, 하이푼(-)으로 구분된 요소는 여기서 제외된다.

특정 단어를 포함하는 attribute 선택

1
2
3
[class|="top"] {
background: yellow;
}
1
2
3
4
5
<h2>CSS [attribute|="value"] Selector</h2>

<h1 class="top-header">Welcome</h1>
<p class="top-text">Hello world!</p>
<p class="topcontent">Are you learning CSS?</p>

include value

  • top이라는 단어가 포함된 요소만 선택하는 것을 확인할 수 있다. (-으로 구분할 수 있다.)

특정 단어로 시작하는 attribute 속성 선택

1
2
3
[class^="top"] {
background: yellow;
}
1
2
3
4
5
<h2>CSS [attribute|="value"] Selector</h2>

<h1 class="top-header">Welcome</h1>
<p class="top-text">Hello world!</p>
<p class="topcontent">Are you learning CSS?</p>

startwith

특정 단어로 끝나는 attribute 속성 선택

1
2
3
[class$="test"] {
background: yellow;
}

특정 값을 포함하는 attribute 속성을 선택

1
2
3
[class*="te"] {
background: yellow;
}

댓글 공유

image sprites

이미지 스프라이트는 단일 이미지에 포함된 이미지 모음이다.

이미지가 많은 웹 페이지 로드하는데 많은 시간이 걸릴 수 있고 서버 요청도 여러번 발생할 수 있다.

이미지 스프라이트를 사용하면 서버 요청 수가 줄어들고 대역폭이 줄어든다.

예시

3개의 이미지를 사용하는 대신, 하나의 이미지를 사용한다.

그리고 나서 CSS로 보여주고 싶은 부분만 보여준다.

imageSprites

1
2
<img id="home" src="img_trans.gif" width="1" height="1" />
<img id="next" src="img_trans.gif" width="1" height="1" />
  • src 속성은 비워둘 수 없기 때문에 투명한 이미지를 넣어두었다.
  • 보여주고 싶은 이미지의 부분을 width, height로 정의했다.
  • background 속성으로 배경이미지를 불러오고 left, top 속성으로 배치한다.
1
2
3
4
5
6
7
8
9
10
11
#home {
width: 46px;
height: 44px;
background: url(img_navsprites.gif) 0 0;
}

#next {
width: 43px;
height: 44px;
background: url(img_navsprites.gif) -91px 0;
}

homeNext

댓글 공유

Dropdown

CSS만을 이용하여 dropdown을 구현해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<style>
.dropdown {
position: relative;
display: inline-block;
}

.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
padding: 12px 16px;
z-index: 1;
}

.dropdown:hover .dropdown-content {
display: block;
}
</style>

<div class="dropdown">
<span>Mouse over me</span>
<div class="dropdown-content">
<p>Hello World!</p>
</div>
</div>
  1. 우선 dropdown content를 열기 위한 span, button 요소를 사용한다.

  2. dropdown content는 div같은 컨테이너 요소를 사용한다.

  3. dropdown content를 CSS에 정확하게 배치하기 위해 1,2번을 div로 감싼다.

  4. wrapper div에는 position:relative 속성을 주어 기준이 되도록 하며 dropdown content에는 position:absolute 속성을 주어 dropdown button 바로 아래에 위치하도록 한다.

  5. dropdown content는 hidden 속성이 기본값이다. 그리고 hover시 아래에 보이도록 한다. 그리고 min-width 속성을 줘서 width를 갖게 한다.

이때, dropdown button 만큼 넓게 width를 갖게 하고 싶다면 width:100%로 설정해라. 작은 화면에서 스크롤을 활성화 시키려면 overflow:auto를 설정하라.

  1. border를 설정하는 것 대신 box-shadow 속성으로 카드처럼 보이게 할 수 있다.

  2. hover 속성은 dropdown menu를 보여줄 때 사용된다.

댓글 공유

앞서 IaaS와 PaaS의 장점만 결합시킨 것이 도커이다. IaaS의 이식성과 PaaS의 운영비 장점을 갖춘 도커에 대해 알아보자.

서사를 위해서 전통적인 배포는 어떠했을지 살펴보자.

deploy

전통적 배포

물리적 컴퓨터 한 대에 하나의 OS를 깔고 여러 프로그램을 설치하는 방식이었다. 이 때, 어떤 프로그램 설치하게되면 다른 앱에 영향을 미친다.

예를 들어, 예전에 보안 앱을 깔았더니 인터넷이나 어떤 애플리케이션이 제대로 동작하지 않았던 경험이 있다.

가상화 배포

전통적 배포에서 발전이 되어 가상머신을 기반으로 배포하는 가상화 배포가 등장하였다.

Hypervisor는 하나의 시스템상에서 가상 컴퓨터 여러 개를 구동할 수 있도록 중간계층 역할을 한다.

결과적으로 컴퓨터 한 대로 여러 앱을 독립적으로 실행하여 다른 앱에 영향을 끼치지 않는다.

단, OS도 독립적이기 때문에 비용이 많이든다.

가상머신: 컴퓨터 모든 부품을(HardWare) 소프트웨어적으로 구현한 것

컨테이너 배포

container

컨테이너는 코드와 모든 종속성(Node.js, 라이브러리 등)을 패키징하는 소프트웨어 표준 단위이다.

컨테이너는 VM(Virtual Machine)과 유사하지만, 운영체재(OS)만 공유하고 애플리케이션은 독립적 컴퓨터로 구분되어 있다.

때문에 애플리케이션끼리 영향을 미치지 않고 비용도 가상화 배포보다 저렴하고 빠르다.

다만 OS는 공유하기 때문에 애플리케이션의 문제가 OS에 영향을 미치면 구동 중인 전체 컨테이너에 문제가 될 수 있다.

Docker

그래서 도커는 앞서 설명한 컨테이너라는 단위로 애플리케이션을 실행하는 기능을 제공하는 플랫폼이다.

여러 컴퓨터 간에 공유된 규격으로 리소스를 공유할 수 있어 유지보수성이 좋다.

또한 IaaS의 이식성과 PaaS의 운영비 장점을 갖추었다.

기존의 종속성 라이브러리나 파일 등 기존 시스템에 대한 설치 절차를 Dockerfile 스크립트에 작성하고 배포 관련 사항을 도커스웜이나 쿠버네티스에 맞춰 작성해주기만 하면 된다. 그래서 보통 도커 + 쿠버네티스 또는 도커 + 도커 스웜으로 구축된다.

Docker 컨테이너 빌드 과정

docker

  1. 도커파일이 빌드
  2. 도커 이미지 생성 및 실행
  3. 도커 컨테이너 실행
  • 도커파일(Dockerfile): 컨테이너에 설치해야하는 패키지, 환경 변수설정 등을 기록한 하나의 파일
  • 도커이미지: 도커가 도커파일의 단계에 따라 컴퓨터의 상태를 “스냅샷”으로 저장한 것
  • 컨테이너: 도커가 이미지를 불러와 실행할 때 생성되며, 실행중인 컨테이너는 이미지에 지정된 프로그램과 데이터를 사용해 일반적인 실제 컴퓨터와 연결되어 사용가능

Docker는 IaaS인가? PaaS인가?

엄밀히 말하면 둘 다 아니고 “클라우드 가상화기술”이지만 굳이 따지면 PaaS의 일부라고 보면된다.

컨테이너의 가상화는 가상 머신의 가상화가 일어나는 곳에서 한 추상화 계층 높은 곳에서 일어난다.

댓글 공유

클라우드

클라우드란, 인터넷을 통해 접근할 수 있는 서버와 그 안에서 구동되는 소프트웨어, DB를 의미한다.

직접적으로 컴퓨터 장비를 구매하지 않고 온라인에 분산되어 존재하는 데이터 센터(클라우드)에게 작업을 맡겨 수행하는 것이다.

이를 오프프레미스(off-premise)방식이라고 한다.

반면 기업이나 개인이 자체 시설을 보유하고 직접 유지관리하는 데이터 센터(IDC)fmf 온프레미스(on-premise)방식이라고 한다.

장점

  • 서버컴퓨터, 네트워크, 방화벽, 전력 등에 대한 고려를 하지 않고 서비스 운영에만 집중할 수 있다.

SaaS(Software as a Service)

인터넷을 통해 소프트웨어(완제품)을 제공하는 방법이다. 구글 드라이브, N드라이브, 구글 DOCS 등.

예를 들어, 구글 DOCS처럼 다른 컴퓨터에서도 쉽게 작업을 하며 다른사람과 실시간 공유작업도 가능하다.

IaaS(Infrastructure as a Service)

인터넷을 통해 인프라를 제공(서버와 저장소)한다. 빈 방을 준다고 생각하면 된다.

특정 클라우드에 종속되지 않는 대신 운영비가 상승한다. 또한 이식성이 좋다.

ex) AWS의 EC2, NCP 등이 있다.

PaaS(Platform as a Service)

인터넷을 통해 플랫폼을 제공한다. 빌트인 방을 제공한다. 운영비 절감할 수 있고 모니터링, CI/CD 제공된다.

하지만, 하나의 서버에 여러가지 서비스를 설치할 수 없어서 IaaS보다는 유연하지 않고 플랫폼에 종속된다.

ex) heroku: 자유롭게 클릭 몇번으로 여러가지 서비스 설치 가능

PaaS vs IaaS

PaaS

  • 유연하지 않고 플랫폼에 종속된다.
  • 설치가 쉽다.
  • 이식성이 낮다.
    • 각 서비스가 각자의 서버에서 동작하고 각각의 서버를 따로 연결해줘야하기 때문에…
  • 운영비 효율 좋다.

IaaS

  • 유연하고 플랫폼에 종속되지 않는다.
  • 설치가 어렵다.
  • 이식성이 높다.
    • 반면 IaaS는 빈 방(서버)를 그대로 이식하면 되기에 이식성이 높다.
  • 운영비 효율 낮다.

댓글 공유

Navigation Bar

Navigation Bar는 link의 목록들이다.

1
2
3
4
5
6
<ul>
<li><a href="default.asp">Home</a></li>
<li><a href="news.asp">News</a></li>
<li><a href="contact.asp">Contact</a></li>
<li><a href="about.asp">About</a></li>
</ul>

기본적인 list 스타일을 제거하기 위한 CSS는 다음과 같다.

1
2
3
4
5
ul {
list-style-type: none;
margin: 0;
padding: 0;
}

수직 Navigation Bar

1. li 요소 내부의 a태그에 display:block 선언하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ul {
list-style-type: none;
margin: 0;
padding: 0;
width: 200px;
background-color: #f1f1f1;
}

li a {
display: block;
color: #000;
padding: 8px 16px;
text-decoration: none;
}

/* Change the link color on hover */
li a:hover {
background-color: #555;
color: white;
}

추가로 active 클래스를 추가하여 현재 선택된 link를 표시해줄 수 있다. JavaScript 사용해야한다.

1
2
3
4
5
6
7
8
9
10
li a.active {
background-color: #04aa6d;
color: white;
}

/* .active 클래스는 포함하지 않는 a태그 hover시 효과 */
li a:hover:not(.active) {
background-color: #555;
color: white;
}

2. 고정된 세로 Navigation Bar

sticky side 네비게이션 바를 만드는 방법이다.

1
2
3
4
5
6
7
8
9
10
ul {
list-style-type: none;
margin: 0;
padding: 0;
width: 25%;
background-color: #f1f1f1;
height: 100%; /* Full height */
position: fixed; /* Make it stick, even on scroll */
overflow: auto; /* Enable scrolling if the sidenav has too much content */
}

수평 Navigation Bar

1. li 요소를 display:inline 속성을 준다.

1
2
3
li {
display: inline;
}

2. floating li

float

1
2
3
4
5
6
7
8
9
li {
float: left;
}

a {
display: block;
padding: 8px;
background-color: #dddddd;
}

3. 상단 고정된 Navigation Bar

fixedTop

1
2
3
4
5
ul {
position: fixed;
top: 0;
width: 100%;
}

4. sticky Navigation Bar

sticky

1
2
3
4
5
ul {
position: -webkit-sticky; /* Safari */
position: sticky;
top: 0;
}

IE는 sticky를 지원하지 않는다. Safari는 -webkit- prefix가 필요하다. 또한 top, right, left, bottom 중 적어도 하나는 지정해줘야한다.

CSS만으로 Dropdown Navigation Bar 구현하기

dropdown

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<ul>
<li><a href="#home">Home</a></li>
<li><a href="#news">News</a></li>
<li class="dropdown">
<a href="javascript:void(0)" class="dropbtn">Dropdown</a>
<div class="dropdown-content">
<a href="#">Link 1</a>
<a href="#">Link 2</a>
<a href="#">Link 3</a>
</div>
</li>
</ul>

<h3>Dropdown Menu inside a Navigation Bar</h3>
<p>Hover over the "Dropdown" link to see the dropdown menu.</p>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
ul {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #38444d;
}

li {
float: left;
}

li a,
.dropbtn {
display: inline-block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}

li a:hover,
.dropdown:hover .dropbtn {
background-color: red;
}

li.dropdown {
display: inline-block;
}

.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
}

.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
text-align: left;
}

.dropdown-content a:hover {
background-color: #f1f1f1;
}

.dropdown:hover .dropdown-content {
display: block;
}
  • dropdown-content는 기본 display 속성이 none이다.
  • dropdown 클래스가 있는 li에 hover 했을 때, dropdown-content에 display 속성은 block으로 바꿔주었다.

댓글 공유

tagged templates

카테고리 JavaScript

📌 template literals

템플릿 리터럴은 내장된 표현식을 허용하는 문자열 리터럴이다.

특징

  • ` 백틱이라는 기호를 사용한다.
  • ${}를 사용하여 표현식을 삽입할 수 있다.
  • 개행을 포함한다.

tagged templates

템플릿 리터럴의 발전된 형태로 태그드 템플릿이 있다.

마치 함수처럼 사용할 수 있다.

예제 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const virtualNode = {
name: "figure",
className: "tagged-template-literals",
style: { cssText: "" },
};

const styled = (strings, Node, ...value) => {
const el = strings
.slice(1, 3)
.reduce((acc, cur) => acc + cur.trim(), "")
.split(";");

el.forEach((elem, i) => {
Node.style.cssText += elem + value[i] + ";";
});
return Node;
};

let values = {
margin: "10px",
color: "#d08471",
};

const received = styled`${virtualNode}margin: ${values.margin};color: ${values.color};`;

console.log(received);
  • 마치 함수처럼 인수를 받아서 템플릿 리터럴에 넣어 반환값을 원하는 대로 지정해줄 수 있다.
  • 리액트의 StyledComponent가 태그드 템플릿을 활용하여 탄생하게 되었다.

🏓 소감

문자타입과 다른 타입을 파라미터로 사용하는 함수를 만들 때 원하는 반환값을 직관적으로 사용할 수 있어 용이해보인다.

댓글 공유

this

카테고리 JavaScript

this 키워드

메서드로 프로퍼티를 참조하고 변경하기 위해서는 우선 자신이 속한 객체를 가리키는 식별자를 참조해야만 가능한 일이다.

1
2
3
4
5
6
7
8
9
10
11
12
const circle = {
// 프로퍼티: 객체 고유의 상태 데이터
radius: 5,
// 메서드: 상태 데이터를 참조하고 조작하는 동작
getDiameter() {
// 이 메서드가 자신이 속한 객체의 프로퍼티나 다른 메서드를 참조하려면
// 자신이 속한 객체인 circle을 참조할 수 있어야 한다.
return 2 * circle.radius;
},
};

console.log(circle.getDiameter()); // 10
  • 객체 리터럴은 circle 변수에 할당되기 직전에 평가된다?
    = 할당 연산자에 의해서 피연산자를 할당해주기 위해서는 우측의 객체 리터럴이 평가된 값으로 존재해야 할당을 해줄 수 있기 때문이다.

하지만 위처럼 재귀적으로 자신이 속한 객체를 참조하는 것은 바람직하지 않다.

그 예시를 생성자 함수를 통해 설명해보자

1
2
3
4
5
6
7
8
9
10
11
12
13
// 생성자 함수
function Circle(radius) {
// 이 시점에는 생성자 함수 자신이 생성할 인스턴스를 가리키는 식별자를 알 수 없다.
????.radius = radius;
}

Circle.prototype.getDiameter = function () {
// 이 시점에는 생성자 함수 자신이 생성할 인스턴스를 가리키는 식별자를 알 수 없다.
return 2 * ????.radius;
};

// 생성자 함수로 인스턴스를 생성하려면 먼저 생성자 함수를 정의해야 한다.
const circle = new Circle(5);
  • 생성자 함수 내부에서 프로퍼티나 메서드를 추가하기 위해서는 자신이 생성할 인스턴스를 참조할 수 있어야 하는데, 인스턴스를 생성하려면 생성자 함수가 존재해야한다.

따라서 자신이 속한 객체, 자신이 생성할 인스턴스를 가리킬 특별한 식별자가 필요하다.

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

  • this는 코드 어디서든 참조할 수 있다. (전역에서도 가능)

this는 객체의 프로퍼티나 메소드를 참조하기 위한 자기 참조 변수이므로 객체의 메서드 내부 또는 생성자 함수 내부에서만 의미가 있다. 따라서 strict mode가 선언된 일반 함수 내부의 this는 undefined가 바인딩된다. (일반함수에선 필요 없다)

함수 호출방식과 this 바인딩

this에 바인딩될 값은 함수 호출 방식에 의해 동적으로 결정된다.

1. 일반 함수 호출

전역 객체에 바인딩된다.

중첩 함수 또한 일반 함수로 호출 시 함수 내부의 this는 전역 객체에 바인딩 된다.

매서드 내에서 정의된 중첩함수도 일반 함수로 호출되면 역시 전역 객체에 바인딩 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// var 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티다.
var value = 1;
// const 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티가 아니다.
// const value = 1;

const obj = {
value: 100,
foo() {
console.log("foo's this: ", this); // {value: 100, foo: ƒ}
console.log("foo's this.value: ", this.value); // 100

// 메서드 내에서 정의한 중첩 함수
function bar() {
console.log("bar's this: ", this); // window
console.log("bar's this.value: ", this.value); // 1
}

// 메서드 내에서 정의한 중첩 함수도 일반 함수로 호출되면 중첩 함수 내부의 this에는 전역 객체가 바인딩된다.
bar();
},
};

obj.foo();

콜백함수가 일반함수로 호출된다면 콜백함수 내부의 this에도 전역객체가 바인딩된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var value = 1;

const obj = {
value: 100,
foo() {
console.log("foo's this: ", this); // {value: 100, foo: ƒ}
// 콜백 함수 내부의 this에는 전역 객체가 바인딩된다.
setTimeout(function () {
console.log("callback's this: ", this); // window
console.log("callback's this.value: ", this.value); // 1
}, 100);
},
};

obj.foo();

하지만 메서드 내의 중첩함수와 콜백함수는 외부함수를 돕는 헬퍼 함수의 역할을 하는데 외부함수인 메서드와 중첩함수 또는 콜백함수의 this가 일치하지 않는다는 것은 중첩함수 또는 콜백함수가 헬퍼 함수로 동작하는 것을 어렵게 만든다.

화살표함수 내부에서 this

1
2
3
4
5
6
7
8
9
10
11
var value = 1;

const obj = {
value: 100,
foo() {
// 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다.
setTimeout(() => console.log(this.value), 100); // 100
},
};

obj.foo();

2. 메서드 호출

메서드 내부의 this는 메서드를 소유한 객체가 아닌 메서드를 호출한 객체에 바인딩된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const person = {
name: "Lee",
getName() {
// 메서드 내부의 this는 메서드를 호출한 객체에 바인딩된다.
return this.name;
},
};
const people = {
name: "Kim",
getName() {
return this.name;
},
};

console.log(person.getName()); // Lee
console.log(people.getName()); // Kim
  • person 객체의 getName 프로퍼티가 가리키는 함수 객체는 person 객체에 포함된 것이 아니라 독립적으로 존재하는 별도의 객체이다? 내 생각에는 this가 가리키는 것이 메서드를 소유한 객체라고 생각해도 맞지 않나?
    person 객체에 getName 프로퍼티 키가 가리키는 함수 객체를 소유하고 있는 것이 아니라 참조값을 가지므로 독립적으로 존재하는 객체를 가리키고 있는 것이 맞다.
1
2
3
4
5
6
7
8
const anotherPerson = {
name: "Kim",
};
// getName 메서드를 anotherPerson 객체의 메서드로 할당
anotherPerson.getName = person.getName;

// getName 메서드를 호출한 객체는 anotherPerson이다.
console.log(anotherPerson.getName()); // Kim
  • 새로운 객체의 프로퍼티에 person.getName 프로퍼티를 할당해줄 수 있다.

this는 getName 메서드를 호출한 객체에 바인딩된다.

프로토타입 메서드 내부에서도 마찬가지로 바인딩된다.

3. 생성자 함수 호출

생성자 함수 내부의 this에는 생성자 함수가 (미래에) 생성할 인스턴스가 바인딩 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 생성자 함수
function Circle(radius) {
// 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}

// 반지름이 5인 Circle 객체를 생성
const circle1 = new Circle(5);
// 반지름이 10인 Circle 객체를 생성
const circle2 = new Circle(10);

console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20

4. Function.prototype.apply/call/bind 메서드에 의한 간접 호출

apply, call, bind 메서드는 Function.prototype의 메서드이다. 이들 메서드는 모든 함수가 상속받아 사용 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
function getThisBinding() {
return this;
}

// this로 사용할 객체
const thisArg = { a: 1 };

console.log(getThisBinding()); // window

// getThisBinding 함수를 호출하면서 인수로 전달한 객체를 getThisBinding 함수의 this에 바인딩한다.
console.log(getThisBinding.apply(thisArg)); // {a: 1}
console.log(getThisBinding.call(thisArg)); // {a: 1}

call,apply 메서드는 함수를 호출하면서 첫번째 인수로 전달한 객체를 호출한 함수의 this에 바인딩한다.

  • 위 예제에서는 getThisBinding() 함수에 인수를 전달해주지 않는다.
  • call, apply의 반환값은 호출한 함수의 반환값이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function getThisBinding() {
console.log(arguments);
return this;
}

// this로 사용할 객체
const thisArg = { a: 1 };

// getThisBinding 함수를 호출하면서 인수로 전달한 객체를 getThisBinding 함수의 this에 바인딩한다.
// apply 메서드는 호출할 함수의 인수를 배열로 묶어 전달한다.
console.log(getThisBinding.apply(thisArg, [1, 2, 3]));
// Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// {a: 1}

// call 메서드는 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달한다.
console.log(getThisBinding.call(thisArg, 1, 2, 3));
// Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// {a: 1}

call,apply 메서드로 함수를 호출하면서 호출한 함수에 인수를 전달해줄 수 있다.

유사배열 객체에 배열 메서드 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function convertArgsToArray() {
console.log(arguments);

// arguments 객체를 배열로 변환
// Array.prototype.slice를 인수없이 호출하면 배열의 복사본을 생성한다.
const arr = Array.prototype.slice.call(arguments);
// const arr = Array.prototype.slice.apply(arguments);
// const arr = Array.from(arguments)
console.log(arr);

return arr;
}

convertArgsToArray(1, 2, 3); // [1, 2, 3]
  • arguments 객체는 배열이 아니므로 배열 메서드를 사용할 수 없지만 apply, call 메서드를 사용하면 가능하다.

새로 나온 Array.from() 정적 메서드를 사용할 수 있다. 하지만 arguments 객체를 잘 안쓴다.

bind

1
2
3
4
5
6
7
8
9
10
11
12
function getThisBinding() {
return this;
}

// this로 사용할 객체
const thisArg = { a: 1 };

// bind 메서드는 첫 번째 인수로 전달한 thisArg로 this 바인딩이 교체된
// getThisBinding 함수를 새롭게 생성해 반환한다.
console.log(getThisBinding.bind(thisArg)); // getThisBinding
// bind 메서드는 함수를 호출하지는 않으므로 명시적으로 호출해야 한다.
console.log(getThisBinding.bind(thisArg)()); // {a: 1}
  • bind 메서드는 함수를 호출하지 않고 인수로 전달받은 객체로 this 바인딩이 교체된 함수를 새롭게 생성하여 반환한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const person = {
name: "Lee",
foo(callback) {
// ①
setTimeout(callback, 100);
},
};

person.foo(function () {
console.log(`Hi! my name is ${this.name}.`); // ② Hi! my name is .
// 일반 함수로 호출된 콜백 함수 내부의 this.name은 브라우저 환경에서 window.name과 같다.
// 브라우저 환경에서 window.name은 브라우저 창의 이름을 나타내는 빌트인 프로퍼티이며 기본값은 ''이다.
// Node.js 환경에서 this.name은 undefined다.
});
  • person.foo의 콜백함수가 호출되기 전 1의 시점에서 this는 foo 메서드를 호출한 객체(person)를 가리킨다.
  • 그러나 person.foo의 콜백함수가 일반 함수로서 호출된 2의 시점에서 this는 전역객체 windows를 가리킨다.
  • person.foo의 콜백함수는 헬퍼함수로 person.foo를 돕는 역할을 하기 때문에 서로의 this가 같아야한다.

이 때, bind 메서드를 사용하여 this를 일치시킨다.

1
2
3
4
5
6
7
8
9
10
11
const person = {
name: "Lee",
foo(callback) {
// bind 메서드로 callback 함수 내부의 this 바인딩을 전달
setTimeout(callback.bind(this), 100);
},
};

person.foo(function () {
console.log(`Hi! my name is ${this.name}.`); // Hi! my name is Lee.
});
  • callback 함수에 this가 바인딩된 새로운 함수를 반환

코드해설

즉, foo안의 this는 person 객체를가리키는데, 콜백함수 호출하면 this가 window를 가리킨다. 그러므로 bind함수를 사용하여 foo 메서드가 가리키는 this를 callback 함수에 바인딩해줘서 콜백함수가 가리키는 this와 일치 시켜준다.

bind, call, apply, that으로 this 바인딩을 일치 시켜주는 것 보다 화살표 함수를 사용하는 것이 간편하다. 하지만 여러 가지 방식에 대해서도 알아두자.

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

댓글 공유

📌 window.open()

1
window.open("https://www.w3schools.com");

새 브라우저 탭에서 해당 URL을 연다.

  • 기본값이 target="_blank" 이다.

❗️ 하이퍼 링크를 신뢰할 수 없다면…

target="\_blank" 속성이 적용되어 있다면 성능과 보안 면에서 취약점이 발생한다.

새롭게 열린 페이지가 원본 페이지와 동일한 프로세스에서 실행될 수 있다.

새로 열린 페이지는 보조 브라우징 컨텍스트이다. 새로 열린 페이지는 자기 자신을 생성한 원본 브라우징 컨텍스트를 오프너 브라우징 컨텍스트라는 이름으로 참조하고 있다.

만약 보조 브라우징 컨텍스트가 많은 JavaScript를 실행하는 경우 원본 페이지의 성능이 저하될 수 있다.

  • 단, 위와 같은 문제는 최신 브라우저에서 발생하지 않도록 처리가 되어있다.

👿 Tab nabbing(탭 내빙) - 피싱 공격

가장 큰 문제는 새롭게 열린 페이지에서 JavaScript를 통해 원본 페이지에 직접 접근이 가능해지는 문제이다.

window.opener를 이용해 원본 페이지에 접근 가능한데 이를 **탭 내빙(Tab nabbing)**이라고 부른다.

tab-nabbing

  1. 사용자가 새탭을 열었다.
  2. 해커가 새 탭에 window.opener.location을 사용하여 원본 사이트와 교묘하게 다른 링크로 바꾼다.
  3. 사용자는 본래 탭으로 돌아오니 로그인일 풀렸다고 생각하여 정보를 입력한다.
  4. 피싱 사이트가 사용자의 로그인 정보를 탈취한 후 다시 원래 링크로 되돌려 놓는다.

이러한 탭 내빙 문제는 target="_blank" 속성이 적용되어 있을 때만 발생한다.
window.open() 메서드는 기본속성이다.
그러므로 이러한 피싱 공격을 막기 위해 rel='noopener' 속성을 설정해줘야한다.

noreferer

noreferer는 noopener와 동일한 기능을 하지만 추가로 브라우저가 해당 페이지를 불러오면서 HTTP 요청을 보낼 때 referer Header를 생략하는 기능이 있다.

즉, noreferer 속성이 있으면 링크 클릭 시 해당 유입이 어디에서 발생하였는지에 대한 정보가 새 페이지에 제공되지 않는다.

  • 일반적으로는 noopener noreferer 두 속성을 같이 적어준다.

nofollow

nofollow 속성은 검색 엔진에게 링크된 웹 사이트를 보증하거나 신뢰할 수 없으니 현재 웹 사이트와 연결하지 않기를 바라는 경우에 사용한다.

  • 스팸 댓글이 등장하여 나오게 되었다.

스팸 댓글이 달린 게시물은 사이트 소유와 상관없이 평판이 내려가게 되어서 구글이 이에 대한 해결책으로 nofollow 를 제시하였다.

nofollow 속성이 설정된 링크는 크롤링하지 않고 검색 엔진에도 영향을 미치지 않는다.

그러므로 댓글이나 포럼과 같이 사용자가 참여 콘텐츠의 링크에 적합하다.

🏓 소감

allWAIs 링크 컴포넌트를 만들다가 window.location.href 속성을 사용하다가 새창을 열어주는 메서드인 window.open() 속성을 알게되었다.

그러다가 예전에 HTML 수업 때 배운 noopener, noreferer 속성이 잘 기억이 나지 않아 정리하기 위해 공부하였다.

이번 기회에 제대로 정리할 수 있어서 오래 기억에 남을 것 같다.

참고

요즘 IT - “하이퍼링크를 신뢰할 수 없다면?”

댓글 공유

loco9939

author.bio


author.job