var 키워드

  • 함수레벨 스코프
  • 생략 가능
  • 변수 재선언, 재할당 가능
  • 변수 호이스팅: 선언 이전에 참조할 수 있다.
  • 전역 변수로 선언 시 전역 객체의 프로퍼티로 등록

let 키워드

  • 블록레벨 스코프
1
2
3
4
5
6
7
8
9
let foo = 123; // 전역 변수

{
let foo = 456; // 지역 변수
let bar = 456; // 지역 변수
}

console.log(foo); // 123
console.log(bar); // ReferenceError: bar is not defined
  • 재선언 불가
1
2
3
4
5
var foo = 123;
var foo = 456; // 중복 선언 허용

let bar = 123;
let bar = 456; // Uncaught SyntaxError: Identifier 'bar' has already been declared

let 키워드의 호이스팅

var 키워드로 선언된 변수는 선언단계와 초기화 단계가 동시에 일어난다.

하지만 let, const 키워드로 선언된 변수는 선언단계와 초기화 단계가 분리되어 진행된다.

const 키워드

  • 재할당, 재선언 불가
  • 상수를 주로 사용
  • 변수 자체를 재할당할 순 없지만 객체의 프로퍼티는 변경 가능하다.
1
2
3
4
5
6
7
8
9
const user = { name: "Lee" };

// const 변수는 재할당이 금지된다.
// user = {}; // TypeError: Assignment to constant variable.

// 객체의 내용은 변경할 수 있다.
user.name = "Kim";

console.log(user); // { name: 'Kim' }

댓글 공유

JavaScript의 변수란?

카테고리 CS

컴퓨터는 연산을 담당하는 CPU, 저장을 담당하는 메모리로 각각 역할이 나뉘어져있다.

연산결과를 재사용하기 위해서는 메모리에 저장하고 메모리 주소를 통해 연산결과가 저장된 메모리 공간에 접근이 가능하다.

메모리 주소에 직접 접근하는 것은 위험하다. 가령 운영체제가 사용하고 있는 값을 변경하면 시스템을 멈추게 하는 치명적인 오류가 발생할 수 있기 때문에 자바스크립트는 개발자의 직접적인 메모리 제어를 허용하지 않는다.

변수란, 하나의 값을 저장하기 위해 확보한 메모리 공간 또는 메모리 공간을 식별하기 위해 붙힌 이름이다.

  • 변수는 인터프리터나 컴파일러를 통해 값이 저장된 메모리 주소로 치환되어 실행된다.
  • 자바스크립트 엔진은 변수 이름과 매핑된 메모리 주소를 통해 메모리 공간에 접근하여 저장된 값을 반환한다.

변수 선언

변수는 선언단계에서 변수 이름을 등록하여 자바스크립트 엔진에게 변수의 존재를 알린다.

또한 초기화 단계에서 값을 저장하기 위한 메모리 공간을 확보하고 암묵적으로 undefined를 할당한다.

1
2
console.log(score); // undefined
var score;

모든 선언문은 런타임 이전에 먼저 실행된다.

변수 할당

1
score = 100;

변수 할당단계는 변수에 값을 저장하는 것을 말한다.

  • 선언과 할당이 한줄에 있어도 선언과 할당이 실행되는 시점이 다르다.
  • 값의 할당은 소스코드가 순차적으로 실행되는 시점인 런타임때 실행된다.

변수에 값을 할당할 때, undefined가 있던 메모리 공간을 지우고 값을 저장하는 것이 아닌, 새로운 메모리 공간을 확보한 뒤 그곳에 새로운 값을 저장하고 해당 변수의 메모리 주소를 연결한다.

댓글 공유

반복문 시간 복잡도

다음 코드의 시간 복잡도를 구해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <bits/stdc++.h>
using namespace std;
int n, cnt;
int main(){
cin >> n;
int a = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < i; j++){
a += i+j;
cnt++;
}
}
cout << a << '\n';
cout << " cnt : " << cnt << '\n'
return 0
}

처음에는 하나씩 콘솔을 찍어보면서 계산해보자.

  1. i = 0, j는 무시;
  2. i = 1, j = 0
  3. i = 2, j = 0,1
  4. i = 3, j = 0,1,2

정사각형의 한 변의 길이가 n일 때, 정사각형의 넓이는 n^2이다.

이와 마찬가지로 시간복잡도도 해당 도형의 넓이로 구할 수 있다.

위 식을 넓이로 나타내면 j는 i를 포함하지 않고 있기 때문에 n * (n - 1) % 2로 나타낼 수 있다.

재귀함수 시간 복잡도

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<bits/stdc++.h>
using namespace std;
int n, a[1004], cnt;

int go(int l, int r){
cnt++;
if(l == r) return a[l];
int mid = (l + r) / 2;
int sum = go(l, mid) + go(mid + 1, r);
return sum;
}

int main(){
cin >> n;
for(int i = 1; i <= n; i++){
a[i - 1] = i;
}
int sum = go(0, n - 1);
cout << sum << '\n';
cout << 'cnt: ' << cnt << '\n';
}

시간복잡도는 어떤 로직이 몇 번 반복되었는지를 식으로 만들고 빅오표기법으로 표현하는 것이다.

좀 더 자세히 말하면 재귀함수의 메인로직 * 몇번 호출되는지이다.

그래서 해당 go 함수가 몇번 호출되는지 직접 손코딩으로 그려보며 세어보았다.

n = 5일 때, 9가 나오고 n = 10일 때, 19, n = 20일 때, 39가 나오는 것을 보고 아 시간 복잡도가 2n - 1이구나라는 것을 알게되었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<bits/stdc++.h>
using namespace std; int N, cnt;

void solve(int N){
cnt++;
cout << cnt << '\n';
if(N == 0) return;
for(int i = 0; i < 3; i++){
solve(N - 1);
}
return;
}

int main(){
cin >> N;
solve(N);
return 0;
}

예를 들어 n = 3일 때, 처음 1회 실행되고 함수가 3번씩 호출되는데 만약 N이 0이아니면 또 3번씩 호출되므로 공비가 3인 등비수열로 생각할 수 있다.

따라서 등비수열의 합식이 성립한다.

time complexity

📌 Tip

함수 하나가 호출될 때 이 함수가 4번 호출된다면 => 4^n

함수 하나가 호출될 때, 이 함수가 2번 호출된다면 => 2^n

로그 지수 역함수의 시간 복잡도

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<bits/stdc++.h>
using namespace std; int N;

void solve(int N){
int a = 0, i = N;
while (i > 0) {
a += i;
i /= 2;
}
cout << a << '\n';
}

int main(){
cin >> N;
solve(N);
return 0;
}

위 식에서 while문은 log N + 1의 시간복잡도를 가지고 빅오표기법으로 나타내면 log N이다.

log N은 2를 몇번 곱해서 N의 값이 될 수 있는지를 나타낸 수이다.

댓글 공유

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

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

타입 시스템

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

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

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

동적 타입과 정적 타입 차이

동적 타입

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

ex) PHP, JS, Python

정적 타입

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

ex) C, C++, Java

정적 타입 시스템의 필요성

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

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

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

댓글 공유

앞서 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는 빈 방(서버)를 그대로 이식하면 되기에 이식성이 높다.
  • 운영비 효율 낮다.

댓글 공유

자료구조

자료구조는 효율적으로 데이터를 관리하고 수정, 삭제, 탐색, 저장할 수 있는 데이터 집합이다.

어떤 비즈니스 로직을 처리할 때 가장 효과적인 자료구조를 찾아서 쓰는 것이 중요하기 때문에 자료구조를 명확하게 알아야한다.

자료구조를 설명하기 위해 C++은 이해하기 쉬운 언어이기 때문에 자료구조 공부는 C++로 할 것이다.

1
2
3
4
5
6
7
8
#include <bits/stdc++.h> // 1
using namespace std; // 2
string a; // 3
int main(){ // 4
cin >> a; // 5
cout << a << '\n'; // 6
return 0; // 7
}
  1. 헤더 파일. import할 때 사용한다. bits/stdc++.h 는 모든 표준 라이브러리를 의미한다.

  2. std라는 네임스페이스 사용하겠다. cin, cout 사용할 때 원래는 std::cin처럼 네임스페이스 달아서 사용해야하는데 이를 기본으로 설정한다는 뜻이다.

  3. 문자열 선언. <타입> <변수명> 이런식으로 선언한다. 예를들어 string a = 'Wix' 이렇게 선언했다면, a는 lvalue, Wix는 rvalue라고 한다. lvalue는 추후 다시 사용될 수 있는 변수이고 rvalue는 한 번 쓰고 다시 사용되지 않는 변수이다.

  4. 입력. 대표적으로 cin, scanf가 있다.

  5. 출력. 대표적으로 cout, printf가 있다.

  6. return 0은 프로세스가 정상적으로 마무리 됨을 뜻한다.

int는 4바이트 정수를 사용할 때 사용되는 타입이다. 표현범위는 -2,147,483,648 ~ 2,147,483,647 입니다.

시간복잡도

시간복잡도란, 입력 크기에 대해 어떤 알고리즘이 실행되는데 걸리는 시간이다. 직접적인 시간을 측정하는 것이 아닌 주요 로직의 반복횟수를 중점으로 측정된다.

왜냐하면 직접적인 시간은 여러가지 요인에 따라 변경될 수 있기 때문이다.

1
2
3
4
5
6
7
8
9
10
11
for(int i = 0; i < 10; i++){
for(int j =0; j < n; j++){
for(int k = 0; k < n; k++){
if(true) cout << k << '\n';
}
}
}

for(int i = 0; i < n; i++){
if(true) cout << i << '\n';
}

위 코드에서 시간 복잡도는 10n^2+n이다.

빅오표기법(Big - O)

빅오표기법은 복잡도에 영향을 가장 많이 끼치는 항의 상수인자를 제거하고 나머지 항을 없애서 복잡도를 표기하는 표기법이다.

앞선 예시를 빅오표기법으로 나타내면 O(n^2)이다.

여기서 영향을 가장 많이 끼친다는 의미는 입력 크기가 커질수록 증가속도가 가장 큰 것을 의미한다.

예를 들어 n^2은 1,4,9,16,25 이런식으로 증가하는데, n은 1,2,3,4,5 이런식을로 증가하기 때문에 영향이 가장 큰 n^2 항의 상수 인자를 제거하여 빅오표기법으로 나타낸 것이다.

빅오표기법 차트는 외워두자!

n! > 2^n > n^2 > nlogn > n > logn > 1 순입니다.

Big-O chart

상수시간 시간복잡도 O(1)

상수시간 시간 복잡도는 입력 크기와 상관없이 일정한 시간복잡도를 가지는 것을 말한다.

  1. 입력과 출력
1
cin, cout, scanf, printf
  1. 곱하기
1
a[2] *= 2;
  1. 간단한 비교 if 문
1
2
3
if (a[0] == 2) {
// 로직
}
  1. 배열 인덱스 참조
1
2
int a[3] = {1,2,3};
int b = a[2]

댓글 공유

node.js

node.js란 비동기적 이벤트 주도 방식, 논블로킹 I/O 모델을 사용하는 구글의 V8 엔진을 장착한 자바스크립트 런타임이다.

런타임이란, 프로그램이 실행될 때 그 프로그램이 머무는 공간을 의미한다. 때문에 브라우저는 자바스크립트 런타임이라고도 한다.

이와 동일하게 node.js 또한 자바스크립트 런타임이므로 자바스크립트로 만든 프로그램을 실행할 수 있다.

이번에는 node.js로 서버를 만들어서 API를 구현해보는 예시를 해보자.

실습

  1. nodejs를 설치한다.

  2. json 파일과 서버파일을 생성한다.

1
2
3
4
5
// a.json
{
"name": "Wix",
"tall": "178"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// b.js
import express from "express";
import fs from "fs";

const app = express();
const port = 3000;
app.get("/", (req, res) => {
const f = JSON.parse(fs.readFileSync("a.json", { encoding: "utf-8" }));
const data = {
name: f.name,
tall: f.tall,
};
res.send(data);
});
app.listen(port, () => {
console.log(`http://127.0.0.1:${port}`);
});
  1. express 모듈 설치
1
npm install express

express는 node.js에서 동작하는 웹 프레임워크이다. 라우팅, 미들웨어, 정적자원서버 설정이 쉬운 장점이 있다.

  1. 터미널로 node 프로그램을 실행해본다.
1
node b.js

get() 메서드에 설정한 경로 /로 get 요청이 보내졌고 서버에서는 name, tall이 담긴 자바스크립트 객체 데이터가 응답으로 전송되었다.

앞서 API의 특징을 설명할 때, 파일 내부가 변경되더라도 API를 변경되지 않도록 설계할 수 있다고 하였다.

a.json 파일 내용을 바꾸더라도 API를 바꿔줄 필요가 없다.

댓글 공유

API

API란, 둘 이상의 컴퓨터 프로그램이 서로 통신하는 방법이자 컴퓨터 사이에 있는 중계 계층을 의미한다.

API는 어떻게 통신할 것인지, 어떤 데이터를 주고 받을 것인지 등에 대한 방법(HTTP, HTTPS 프로토콜 중 어떤 거 사용할지, GET인지 POST인지 등)이 정의된 중계 계층이다.

API 특징

  • 제공자는 본인이 원하는 부분만 드러낼 수 있다.
  • 사용자는 해당 서비스가 어떻게 구현되는지 알 필요없이 필요한 정보만 받을 수 있다.
  • OPEN API의 경우 애플리케이션 개발 과정을 단순화 시켜주어 개발 시간과 비용을 절약할 수 있다.
  • API를 만들고 나서 내부 프로세스가 수정될 때마다 매번 API를 수정해줄 필요가 없다.
  • 제공자는 데이터를 한곳에 모을 수 있다. 예를 들어 방문자 수, 클릭수 등을 집계하고 싶을 때 해당 API를 만들고 이벤트가 발생할 때 해당 API를 호출하여 데이터를 한 곳에 모을 수 있다.
  • 제공자의 경우 API를 이용해 제 3자가 만든 앱을 통해 데이터를 수집하여 해당 서비스 확장할 수도 있고 홍보에도 도움이 된다.

API 종류

  • private: 내부적으로 사용. 주고 해쉬키를 기반으로 서버와 서버간 통신에 사용된다.
  • public: 모든 사람이 사용. 많은 트래픽 방지하기 위해 하루 요청 수 제한.

댓글 공유

JSON

JavaScript 객체 문법으로 구조화된 데이터를 표현하기 위한 표준 포맷이다.

모든 서비스와 애플리케이션은 데이터를 기반으로 그것을 보여주거나 뒤에서 다루면서 데이터를 많이 사용하게된다.

세상의 모든 정형화된 데이터만 있다면 편리하겠지만, 그렇지 않은 복잡한 데이터도 많다.

예를 들어, 아래와 같은 내용을 데이터로 표현하려면 복잡하다.

Wix

  • 나이: 29
  • 좋아하는 것: 자전거타기(로드), 노래부르기(발라드)

이런 데이터를 컴퓨터도, 사람도 이해하기 쉽게 표현한 것이 바로 JSON이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"Wix": {
"age": 29,
"Likes": [
{
"name": "Riding Bike",
"type": "Road"
},
{
"name": "Sing a song",
"type": "Ballad"
}
]
}
}

특징

  • key:value 형태
  • {} 중괄호로 감싼다.
  • key, value에 “”만 사용가능하다.
  • 자바스크립트와 호환이 좋다.
  • 각 객체가 다른 타입이어도 괜찮다.
  • value에 메서드와 undefined가 올 수 없다. 이외의 수, 문자열, 불리언, 배열, 객체, null 값은 올 수 있다.
  • 프로그래밍 언어나 플랫폼에 독립적이기 때문에 서로 다른 시스템 간의 데이터 교환시 유용하다. ex) API, config 파일

XML

JSON과 비교하면 중괄호 대신 열린 태그와 닫힌 태그 구조로 이루어진 데이터 포맷이 XML이다.

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?> <!-- 프롤로그 -->
<Wix>
<age>29</age>
<Likes>
<Like>
<name>Riding Bike</name> <type>Road</type>
</Like>
<Like>
<name>Sing a song</name> <type>Ballad</type>
</Like>
</Likes>
</Wix>

특징

  • 최상위 태그는 하나만 사용해야한다.
  • JSON에 비해 무겁다. JSON에 비해 닫힌 태그까지 들어가서 2번씩 들어간다.

sitemap.xml

XML의 대표적인 예시로는 SEO에 사용되는 sitemap.xml이 있다.

사이트가 매우 크거나 서로 링크가 종속적으로 연결되어 있지 않다면 크롤러가 일부 페이지를 누락하는 일이 발생하는데 이를 방지하기 위해 sitemap.xml를 사용한다.

댓글 공유

loco9939

author.bio


author.job