[프로그래머스 데브코스] TIL - Day3

프엔꿈나무·2023년 6월 5일

✏️ 오늘 학습한 내용

1. 네트워크 기초
2. 컴퓨터 시간
3. 암호화
4. 함수형 프로그래밍
5. 객체지향과 프로토타입
6. 이벤트 루프
7. 모듈
8. 유니코드
9. 정규표현식
10. 쿠키와 세션, 웹 스토리지


🔎 네트워크 기초

브라우저에 URL을 입력하게 되면 어떤 일이 발생할까? URL을 입력하게 되면 다음과 같은 과정을 거치게 된다.

1. URL 해석

url은 흔히 다음과 같이 이루어져있다. (scheme: 프로토콜이 들어가는 영역)
scheme://<user>:<password>@<host>:<port>/<url-path>

예시)
1. http:programmers.co.kr
2. ftp://admin:admin123@example.com/image.png

2. DNS(Domain Name System) 조회

DNS는 도메인과 IP 주소를 서로 변환해준다. 보통 통신사(ISP)에서 제공하는 것을 사용하며 DNS를 운영하는 서버를 Name Server라고 부른다.

  1. 브라우저는 DNS로 요청을 보내기전, 먼저 해당 도메인을 알고있는지 찾아보고(캐시 확인) 없다면 host 파일을 참조. (만약 이미 정의되어있다면 IP 반환)
  2. 둘 다 해당 안 된다면, DNS 호출
  3. DNS는 root server, TLD server, Authoritative server 순으로 탐색

3. 해당 IP 존재하는 서버로 이동

해당 IP가 할당된 서버가 존재하는 대역으로 이동. 만약 한국에서 미국에 있는 서버 요청시, 라우터(네트워크 장비)를 통해 여러번의 과정을 거친 뒤 해당 서버가 존재하는 대역으로 접근.

4. ARP(Address Resoulution Protocol) 이용하여 MAC 주소 변환

ARP란 논리 주소인 IP 주소를 물리 주소인 MAC 주소로 변환하는 프로토콜, 네트워크 내에 ARP를 Broadcasting하면 해당 IP 주소를 가지고 있는 기기가 자신의 MAC 주소를 반환함.

그렇다면 MAC 주소가 필요한 이유는 뭘까?

이는 기계의 실제 위치를 알기 위해서인데 IP주소와 MAC주소는 각각 용도가 다르기 때문이다. 예를 들어, 경복궁의 도로명 주소는 '서울 종로구 사직로 161'인데 이 주소체계가 변경된다면 우리는 경복궁을 찾을 수 있을까? 이렇게 될 경우 경복궁의 실제 위치를 찾기 위해서는 GPS상의 좌표가 필요할 것이다. 이때 도로명 주소를 IP주소에 비유할 수 있고, GPS상의 위치를 MAC주소로 비유할 수 있다. 따라서 실제 기계가 있는 위치를 알기 위해서는 MAC주소가 필요한 것이다.

그럼 IP주소는 어떤 용도로 쓰이는 것일까?

경복궁에 택배를 보낸다고 가정해보자. 택배를 보내기 위해서는 위에서 언급한 경복궁의 도로명 주소를 통해 서울 -> 종로구 -> 사직로 순으로 접근해나가며 범위를 좁혀가고 해당 지역에 진짜 위치를 찾으면 될 것이다.
이렇게 도로명 주소를 이용하는 것처럼 IP주소를 통하면 대역을 통해 범위를 좁혀나갈 수 있다.

또한, 택배를 보낼 때 중간 거점을 거쳐가며 택배를 보낼 수 있는데 이 중간 거점들을 라우터에 비유할 수 있을 것이다.

5. TCP 통신 위해 서버 socket 열기

데이터를 전달하기 위해서는 실제 socket을 열어 허락을 받아야한다. 이를 위해서 '3 way handshake'가 실행 -> 요청이 수락되면 데이터를 서버로 전달

6. 서버 응답 반환

데이터를 받은 서버가 데이터를 읽은 후 요청에 따라 처리함

7. 렌더링

브라우저가 받은 Html을 통해 DOM tree 구축 -> 화면에 렌더링 (스크립트 있다면 실행)

host? domain?

  1. present.do
  2. www.present.do
  3. gateway.dev.present.do

모두 도메인은 present.do에 해당, 나머지 서브 도메인이 붙는 경우는 호스트라 부름.

더 알아야 할 것들

  1. OSI 7 계층
  2. Routing Table
  3. Subent mask

등등...

https?

http가 있음에도 https가 왜 탄생했을까? 이유를 찾아보자.


🔎 컴퓨터 시간

시간은 물리량, 위치, 천문 현상 등 물리적인 법칙과 문화, 역사, 사회 등 협의에 의해 결정될 수 있다.
이처럼 시간을 정하는데 있어 고려할 사항이 많기 때문에 대부분의 국가가 '협정 세계시(UTC)'를 통해 시간을 관리한다.

그렇다면 컴퓨터는 시간을 어떻게 표현할까?

컴퓨터는 자체적으로 시스템 클럭 을 가지고 있다. 이를 통해 특정 시각을 기준으로 시스템 클럭의 틱을 세는 것으로 시간을 정의하며 이를 시스템 시간이라 부르고 값으로 표현한 것을 타임스탬프라고 한다. (그 중에서도 표준적으로 많이 사용하는 것을 유닉스 타임(Unix Time)이라고 한다.)

시스템 클럭의 원리

  1. RTC(real time clock)이라는 모듈 사용
  2. RTC는 메인 보드에 붙어있어 전원을 꺼도 계속 작동
  3. RTC는 카운터 회로라는게 존재하는데 결정 진동자라는 부품이 1클럭에 32.768kHz 주파수를 발생(1초를 계산하기 편해서라고 한다)

Unix Time

  1. 1970년 1월 1일 0시 0분 0초가 기준 시각. (큰 이유는 없다고 한다..)
  2. 1970년 이전 시간은 음수로 표현
  3. 초 단위로 시간 증가 (1초 지나면 Unix Timestamp도 1이 더해짐)

그렇다면 현재 시간은 어떻게 알아낼까?

네트워크 타임 프로토콜(NTP)를 통해 서버에서 받아오는 것! (인터넷이 없으면 현재 시각을 알 수 없음)

컴퓨터에서 시간대를 어떻게 고려해야할까?

데이터를 별도 데이터베이스에 저장 -> 만약 일광 시간 절약제가 시행된다면? -> 해당 데이터가 데이터베이스에 기록 -> 데이터베이스의 데이터를 받아 처리하기만 하면 된다. (이때 받아오는 데이터를 'Time Zone'이라 부른다, 표기 법은 대륙/도시, ex) Asia/Seoul 형태)

시간을 어떤 기준으로 사용해야 할까?

글로벌 서비스를 운영할 경우 시간은 매우 중요한 요소이다. 흔히 Time Zone만 잘 적용하면 되지 않을까 생각할 수 있지만 시간을 용도에 맞춰 잘 기록할 필요가 있다.(시간대, 지역, 문화, 사회를 고려하지 않고 순수하게 시간을 기록해야 하는 경우가 있음) ex) 생일, 기업 설립일, 기념일, 국경일

JS에서의 사용법?

  • 간단하게 Date 객체를 사용할 수 있음.
  • 좀 더 복잡하게 사용해야될 경우에는 가장 유명한 'moment.js'를 사용할 수 있다. (지금은 deprecated되어 권장 x)
  • 대체 라이브러리로 'date-fns', 'luxon'이 있음. (기회가 되면 사용해보자!)

🔎 암호화

평문을 해독 불가한 암호문으로 변환하는 것을 말함. 단방향(해싱), 양방향 암호화 2가지 방법이 존재한다.

단방향 암호화

  • 해시 알고리즘을 통해 평문을 복호화 할 수 없는 형태로 암호화함 (저장하는 측에서도 해당 데이터를 알면 안되기 때문에)
  • 대표적으로 'MD5' ,'SHA' 알고리즘이 있다
  • 사용자 비밀번호 등 저장할 때 많이 사용
  • MD5, SHA-0, SHA-1 은 해시 충돌이 발생할 수 있기 때문에 사용 권장 x

고려할 점

  • Rainbow Table에 대한 대비 (Rainbow Table: 평문과 해시 함수로 만든 문자열을 모두 저장시켜 놓은 표)
  • 암호화된 데이터를 탈취당하더라도 원문을 알아낼 수 없도록 조치를 취해야함, ex) Salt, Key stretching

Salt

  • 평문에 임의의 문자열을 추가하여 암호화하는 방법
  • 128bit 이상으로 만들어야 효과적이기 때문에 이렇게 만드는 것을 권장
  • 사용자마다 다르게 작성하는 것이 더 안전한 방법

Key stretching

  • 해시를 여러 번 반복하여 원문을 알기 힘들게 만드는 방법 (1000번 반복할 경우 해커도 원본을 1000번 해시 알고리즘 돌리기전까지 알 수 없다.)
  • 일반적인 시스템에서는 0.2초 이상 반복되면 안전하다고 한다.

'Salt'와 'Key stretching' 모두 직접 구현하는 것보단 이미 검증받은 알고리즘을 사용하는 것이 안전함

  • PBKDF2
  • bcrypt (비밀번호 저장을 사용할 경우 추천)

양방향 암호화

  • 평문을 복호화 할 수 있는 형태로 암호화 하는 방법
  • 대표적으로 대칭키를 이용하는 AES, 비대칭키를 이용하는 RSA로 분류

대칭키 알고리즘

  • 대표적으로 AES를 많이 사용
  • 같은 키를 이용하여 암호화, 복호화 가능

비대칭키 암호 알고리즘

  • 대표적으로 RSA를 많이 사용
  • RSA 공개키와 개인키 2가지 존재
  • 대칭키 암호화만으론 다수를 대상으로 암호문을 받기 힘들기 때문 (나에게 암호문을 보내고 싶은 사람은 공개키를 이용해 암호문을 보낼 수 있고 받은 사람은 개인키로 복호화 가능)

JS에서 암호화를 어떻게 이용할까?

crypto-js 라이브러리를 이용하면 된다! (대부분의 암호화 이용 가능) 단, bcrypt는 구현되어있지 않기 때문에 별도로 알아봐야 함.


🔎 함수형 프로그래밍

객체지향

객체를 통해 데이터와 메소드를 놓고 객체간 통신함으로써 프로그램 작동

함수형

데이터를 함수를 이용해 새로운 데이터를 만들어나가는 데이터 파이프라인의 형태로 프로그램 작동

함수형 패러다임

  • 함수가 최소 단위
  • 재사용성이 높음
  • 불변성 지향함 -> 동작 예측 쉽고, 사이드 이펙트 방지(쓰레드를 통한 동시성 문제도 해결된다는 의미)
  • 객체지향은 '순차 -> 분기 -> 반복 -> 참조' 4가지 제어 흐름의 전환을 객체를 통해 통제하고, 함수는 변수 할당을 통해 4가지 제어 할당을 통제한다고 볼 수 있다.

함수형 프로그래밍의 장점 (이자 단점...)

  1. 상태가 없기 때문에 사이드 이펙트가 없다. (변수 조작이 안됨)
  2. 재사용성이 높다. (함수를 쪼개다가 많아지면 오히려 복잡해짐)
  3. 코드가 짧고 간결하다. (많은 숙련도 요구)

선언형 프로그래밍

객체지향과 함수형처럼 설계에 대한 패러다임이라기보단, 사고에 대한 패러다임으로 생각. 기존 명령형 프로그래밍은 문제를 어떻게 해결할지 컴퓨터에 명령을 내리는 사고방식이라면 선언형 프로그래밍은 무엇을 해결해야할지에 집중 (해결 방법은 컴퓨터에게 위임)

명령형 예시

let a = [1,2,3,4,5];
for (let i =0; i<5; i++){
  if(a[i] % 2 === 0) {
    console.log(a[i]);
  }
}

선언형 예시

[1,2,3,4,5]
	.filter((i) => i % 2 === 0)
	.forEach((i) => console.log(i));

멀티 패러다임

사실 굳이 패러다임을 나눌 필요는 없다! 각각의 장점을 극대화하기 위해서라면 둘 다 사용하는 것이 옳다.


🔎 객체지향과 프로토타입

먼저 객체지향에서 객체란 무엇일까?

현실의 버튼과 웹사이트의 버튼을 떠올려보자. 현실의 버튼은

  1. 누르면 뭔가 발생
  2. 만질 수 있음
  3. 튀어나옴
  4. 고장날 수 있음
  5. 색을 입힐 수 있음

이중에서 웹사이트 버튼에 해당하는 것은 1, 3, 5번일 것이다. 이처럼 객체지향의 객체는 현실에 있는 것을 추상화한 것을 말한다.

그렇다면 여기서 추상이란 무엇일까?

이번엔 실제 지구, 지구본, 지구 지도를 떠올려보자. 우선 지구 지도의 경우에 위도와 경도를 정확하게 표현할 수 있지만 직사각형 모양으로 실제를 왜곡하게 된다. 그리고 지구본의 경우에는 지구 지도처럼 모든 나라를 한눈에 살펴보기는 어렵지만 실제 크기를 그대로 반영한다. 이처럼 추상이란 사물이 지니고 있는 여러 측면 중에서 특정한 부분을 보는 것을 말한다 (그 외의 것들은 모두 버림)

객체지향이란?

  • 객체 위주로 설계하고 프로그래밍하는 패러다임
  • 추상화의 최소 단위는 객체
  • 각각의 객체끼리 메세지를 주고 받으면 통신

객체지향에 대한 오해

  • 객체지향은 패러다임일 뿐 언어와 상관 X (언어는 지향하는 것을 조금 더 편하게 구현할 수 있게 도와줄 뿐)
  • 따라서 클래스가 없는 JS,Go,C언어에서도 객체지향 프로그래밍을 할 수 있음
  • 자바스크립트는 프로토타입을 통해 객체지향 표현
  • 절차지향보다 객체지향이 더 좋은 것은 아님 (만들어야하는 프로그램에 따라 적합한게 존재 -> 비교적 간단한 프로그램이라면 절차지향이 비교적 만들기 쉽고 직관적)

프로토타입

JS의 객체는 class기반 언어와 같이 속성과 행위를 정의할 수 있다.

JS의 객체를 이용하면 객체지향 프로그램이 가능한데 프로토타입이 왜 필요한걸까?? 다음 예시를 보자.

function Person(name, company, move) {
  this.name = name;
  this.company = company;
  
  this.getName = function(){
    return this.name;
  };
  
  this.setName = function(name){
    this.name = name;
  };
}

const kim = new Person('김정호', '백수');
console.log(kim);

const lee = new Person('이정호', '백수');
console.log(lee);

결과

Person {
  name: '김정호',
  company: '백수',
  getName: [Function], // 메서드가 따로 정의
  setname: [Function] 
}
Person {
  name: '이정호',
  company: '백수',
  getName: [Function], // 메서드가 따로 정의
  setname: [Function] 
}

결과를 보면 메서드가 따로 정의되어 있음을 확인할 수 있다. (같은 내용임에도 생성된것이기 때문에 메모리 낭비)

프로토타입을 사용하면 이를 해결할 수 있는데 기존의 객체를 복사하여 새로운 객체를 생성하는 방식이다.

위의 코드를 다음과 같이 수정해보자.

function Person(name, company, move) {
  this.name = name;
  this.company = company;
  
  Person.prototype.getName = function(){
    return this.name;
  };
  
  Person.prototype.setName = function(name){
    this.name = name;
  };
}

const kim = new Person('김정호', '백수');
console.log(kim);

const lee = new Person('이정호', '백수');
console.log(lee);

console.log(Person.constructor);
console.log(Person.__proto__);
console.log(kim.constructor);
console.log(kim.__proto__);

결과

Person {name: '김정호',company: '백수'}
Person {name: '이정호',company: '백수'}
[Function: Function]
[Function]
[Function: Person]
Person {getName: [Function], setName: [Function]}

이렇게 코드를 수정하면 하위 객체들은 상위 객체를 바라보게 됨. 객체들은 각자 프로토라는 객체를 내부적으로 가지게 되는데 이 내부에서 상위의 객체를 링크함. 이렇게 프로토타입을 사용하면 기존 객체를 효율적으로 사용 가능하다.

또한, Object.create를 이용하면 기존 객체를 재활용할 수 있다.

const kim = {
  name: '김정호',
  getName: function () {
    return this.name;
  },
};

const lee = Object.create(kim); // lee는 kim을 복제 (lee의 상위객체는 kim)
lee.name = '이정호';

console.log(lee.getName()); // 이정호
console.log(kim.getName()); // 김정호
console.log(kim.__proto__); // {}
console.log(lee.__proto__); // {name: '이정호', getName: [Function: getName]}

🔎 이벤트 루프

JS는 대표적인 싱글스레드언어라고 한다. (JS의 Call Stack은 하나만 존재)

그렇다면 브라우저에서 실행되는 스크립트는 어떻게 비동기적으로 데이터를 불러오고 애니메이션을 실행시키는걸까?

이는 브라우저에 '이벤트 루프'라는 시스템이 있기 때문이다.

이벤트 루프는 JS의 기능이 아니라 브라우저나 node.js에서 자체적으로 관리하고 있음!

클릭과 같은 dom events나 네트워크 호출, 타이머 등은 Web APIs에서 관리되고 있는데 실행시킬 경우 브라우저에 위임되고 콜백함수를 넘기게 된다. 이 콜백 함수는 비동기 작업이 끝나면 Task Queue에 넣어지고 순차적으로 꺼내 Call Stack에 push된다. 그래서 이러한 과정들이 결국에는 멀티스레드 형식으로 동작하게 된다. (JS 엔진이 싱글스레드일뿐 브라우저는 멀티스레드로 동작하기 때문)


🔎 모듈

일반적으로 웹사이트는 여러개의 JS파일로 이루어져있다. 문제는 웹사이트가 하는일이 많다면 JS의 파일이 그만큼 많이 늘어난다는점인데, 이전에는 스크립트간 통신을 위해 전역 스코프에 존재하는 변수를 사용해야했다. 이 점을 해소하기 위해서 등장한게 모듈인데, 모듈을 사용하면 스크립트간의 의존도를 파악할 수 있고 실행 순서를 쉽게 제어할 수 있다는 장점이 있다.

모듈? 컴포넌트?

흔히 모듈과 컴포넌트를 혼동하는 경우가 많은데 모듈은 설계 시점에 의미있는 요소, 컴포넌트는 런타임 시점에 의미있는 요소라고 생각하자. (모듈은 의식적으로 나눠놓은 요소, 컴포넌트는 나눠놓은 요소에 포함되어 실행되는 요소) 하지만, JS 모듈은 직접적으로 런타임에 실행된다는 것을 기억해두자.

JS가 지원하는 모듈은 로컬 파일에서는 동작하지 않는다. -> Http, Https 프로토콜을 통해서만 동작하므로 서버를 사용해야함 (간단한 웹 서버로 http-server를 사용할 수 있다.)

script 태그의 속성을 type='module'로 지정하면 해당 스크립트는 모듈로 동작하게 된다.

<script type='module'>
  import {hello} from './hello.js';
  
</script>

모듈의 특징

모듈은 일반 스크립트와는 다르게 다음과 같은 특징들이 있다.

  1. 항상 use strict로 실행
  2. 모듈 레벨 스코프가 있다. (최상위에 변수를 선언해도 전역 스코프에 올라가지 않고 자체적인 '모듈 레벨 스코프'에 올라가게 됨 -> 모듈 스크립트에서는 import 하지 않을 경우 다른 스크립트에서 참조가 불가능)
  3. 단 한 번만 평가됨
  4. 지연 실행된다.

3번 예시

<script type='module'>
  import {hello} from './hello.js';
  
</script>

<script type='module'>
  // 두 번 import되었지만 실행은 단 한 번만 됨
  import {hello} from './hello.js';
  
</script>

4번 예시

<script type='module'>
  // DOM이 생성되기 전 실행됨
  console.log(text);  // Error 발생!
</script>

<script type='module'>
   // DOM이 모두 만들어진 후 실행됨
  // defer를 적용한 것과 같다.
  console.log(text);  //  <p id='text'>Hello, World!</p>
</script>

<p id='text'>Hello, World!</p>

사실 요즘은 대부분이 webpack 같은 모듈 번들링을 사용하기 때문에 크게 모듈 스크립트를 사용할 일이 많지 않다. (하지만 모듈의 동작원리는 웹팩에서도 유사하기 때문에 알아두면 좋다!)


🔎 유니코드

브라우저는 한글, 알파벳 등 다양한 문자를 표시하는데 어떻게 가능한 걸까?

먼저 값으로 이루어진 문자와 글꼴이 만나 '렌더링 엔진'을 통해 그려지게 된다.

그렇다면 '문자' 영역에 대해 좀 더 알아보자.

CCS (Coded Character Set)

  • 문자들을 code point에 대응시켜 만든 코드화된 문자들의 집합 (code point는 정의해놓은 정수 값을 의미하며 정수 값들은 각 문자들의 식별자가 된다.) 우리가 알고있는 아스키코드도 CCS에 속한다.

CES (Character Encoding Scheme)

  • CCS를 8bit 집합에 대응시키는 것 (8bit인 이유는 문자를 표현하는 기본단위이기 때문)
  • 최소가 8bit 1개인 집합이 될 수 있고 여러개의 8bit가 집합에 포함될 수 있다. (그러므로 ccs와 ces 1:1 대응)
  • 이 과정을 쉽게 말해 인코딩이라 할 수 있다. (인코딩이란 문자를 시스템이 인식 할 수 있는 값으로 변환하는 것, 디코딩이란 인코딩된 값을 다시 문자로 변환하는 것)
  • ex) UTF-8, UTF-16, euc-kr

TES (Transfer Encoding Syntax)

  • 인코딩한 문자가 특정 프로토콜을 타고 전송이 가능하도록 변환하는 것
  • 이는 통신 프로토콜에서 제약이 있을 수 있기 때문인데 예로 url에서 공백은 사용할 수 없기 때문에 별도로 url을 인코딩하여 전송해야 한다.
  • URL Encoding, BASE64 Encoding

유니코드란 위의 3가지 개념의 끝판왕이라 할 수 있다! -> 전 세계 문자를 컴퓨터에 다룰 수 있도록 만든 표준 시스템 (다양한 나라가 서로 다른 인코딩 방식을 사용해서 호환 및 확장에 문제가 생겨 이를 해결하기 위해 등장했다.)


🔎 정규 표현식

카카오톡이 전화번호, URL을 찾아 링크를 걸어주곤 하는데 이는 정규표현식을 사용하면 가능하다.

정규표현식은 패턴을 이용하여 원하는 문자를 검색하거나 대체 혹은 추출할 수 있게 해준다. 정규 표현식은 성능이 매우 느리지만 매우 편하기 때문에 성능이 크게 이슈가 되지 않는 선에서 많이 사용하곤 한다.

정규표현식은 다음 사이트에서 쉽게 테스트 가능 하다.

정규표현식은 아무리 사용해도 까먹기 때문에 외우려 하지말고 위와 같은 편집기 사이트에서 그때그때 사용해가도록 하자!

표현 방법은 정말 간단하다...

/regexr/i

위와 같이 / 기호로 시작 ~ 종료를 설정하고 내부에 패턴을 입력하면 된다. 그리고 닫는 슬래시 기호 이후에 마지막 한 글자가 플래그가 된다.

정규표현식을 사용하기 위해서는 패턴을 찾는 것이 중요한데 휴대폰 번호로 예를 들어보자.

휴대폰 번호는 010, 018, 011 등 3자리 숫자로 시작하고 뒤에 하이픈 기호를 붙인 후 셋 혹은 네자리 숫자 + 하이픈 + 다시 네 자리 숫자의 패턴으로 표현이 가능하다. (다른 케이스는 일단 무시하고 생각하자)

이런 느낌: 01x-xxx(x)-xxxx

따라서 세 자리 아무 숫자를 매치하는 패턴 (\d{3})과 세, 네 자리 아무 숫자를 매치하는 패턴 (\d{3,4})을 조합해야 한다. 위의 번호 패턴을 정규표현식으로 표현하면 다음과 같다.

\d{3}-\d{3,4}-\d{4}

이밖에도 정규표현식을 사용하면 이메일 주소에서 중간 문자열만 뽑는 등 할 수 있다.

유용현 표현

.+: 한 개 이상의 문자열
(...): 캡처 (소괄호로 해당 표현식을 감싸주면 해당 영역만 뽑을 수 있다.)
\1: 첫 번째 캡처를 이용하겠다는 뜻

정규 표현식을 연습하는데 도움될만한 사이트

JS에서 정규표현식 사용

JS는 RegExp 객체로 정규표현식 기능 제공 (배열, 객체처럼 편하게 사용하도록 literal 제공)

정규표현식의 유용한 기능들

1. test: 입력받은 문자열에 찾는 패턴이 있는지 찾은 후 있다면 true, 없다면 false 반환
2. exec: 패턴 정보를 얻고싶을때 사용
3. match: string 객체의 함수, 정규표현식 객체를 파라미터로 받아 패턴이 있는지 찾은 후 일치한 패턴 정보 반환 (없다면 null 반환)
4. replace: string 객체의 함수, 패턴이 일치한다면 원하는 문자열로 바꿀 수 있는 함수 -> 모두 변경을 원하면 플래그로 g를 붙여서 사용 (자주 사용하므로 알아두자!)
5. search: string 객체의 함수, 매칭된 문자열의 위치 반환한다. 처음 매칭된 곳의 위치만 알려주기 때문에 전부 알고싶다면 search 대신 matchAll 사용
6. capture: 캡처가 적용된 정규표현식 사용할 경우 match 반환값의 1번 인덱스부터 순차적으로 캡처 결과가 들어감


🔎 쿠키와 세션, 웹 스토리지

Http 요청은 기본적으로 상태가 존재하지 않음. (항상 단발성) 이에 따라 서버가 어떤 브라우저에서 요청이 온 것인지 알 수 없지만 헤더에 쿠키를 담게될 경우 서버가 쿠키를 읽어 어디서 온 것인지 알 수 있게된다.

쿠키

  • 쿠키란 프론트엔드에서 관리하고 저장하는 데이터를 말한다. (브라우저를 닫아도 데이터가 유지됨)
  • 서버에서 set-cookie를 응답 헤더로 내려주면 클라이언트가 이를 받고 알아서 저장함. (만약 이게 싫다면 자제척으로 조작 가능)
  • 각 상태에서 수명 정해줄 수 있음

서버에서 내려주는 set-cookie는 키=값 형태로 내려온다. 클라이언트가 이 값을 파싱하여 알아서 쿠키에 데이터를 추가함. 각 데이터에는 다음과 같은 여러 옵션들이 존재할 수 있다.

  • Expires: 쿠키 만료 날짜 지정
  • Secure: HTTPS에서만 쿠키 전송
  • HttpOnly: JS에서 쿠키 접근 못하게 막음 (XSS 예방)
  • Max-Age: 쿠키 수명 정함 (expire와 유사하지만 해당 설정이 존재할 경우 expire를 무시)
  • Domain: 도메인이 일치하는 요청만 쿠키 전송
  • Path: 패스와 일치하는 요청만 쿠키 전송

쿠키의 취약점

XSS (Cross-Site Script) 공격
JS를 통해 해커가 다른 사용자의 쿠키값을 탈취할 수 있다. 위에서 말한 httponly옵션을 사용할 수 있지만 최근에는 SPA 개발이 주를 이루기 때문에 사용하기 힘들다.
또한 쿠키를 암호화하지 않고 보내게될 경우 쿠키값을 중간에 탈취 당할 가능성이 있다. (Https로 해결 가능!)

클라이언트가 쿠키를 아무리 보내도 서버는 이를 알지 못하는데 그렇다면 어떻게 해야할까?

답은 세션을 이용하는 것이다

Session

쿠키에 http session id를 식별자로 담아 클라이언트와 서버간에 통신이 가능하다. -> 서버는 식별자로 사용자를 구분하고 적절한 데이터를 응답해줌

세션의 취약점

  1. 만약 사용자가 많아진다면? -> 저장 공간 부족으로 동작 안함
  2. 서버가 2대라면 세션은 어떻게 관리? -> 인증 정보가 흩어져있기 때문에 제대로 응답 못할 가능성 높아짐

이러한 이유로 최근에는 인증 용도로 세션을 사용하지 않는다. 그래서 이를 해결하기 위해 서버와 클라이언트간의 인증은 JWT같은 별도의 토큰을 이용하고 쿠키는 클라이언트 자체적인 데이터 관리 용도로 많이 사용한다.

웹 스토리지

  • 쿠키에서 하기 힘든 것들을 해결하기 위해 등장
  • HTML5부터 등장
  • 로컬 스토리지, 세션 스토리지가 존재

로컬 스토리지

  • 데이터가 반영구적으로 저장됨 (브라우저 종료해도 남는다.)
  • 쿠키와 마찬가지로 저장했던 도메인과 이용하는 도메인이 다를 경우 접근 불가
  • 쿠키와 마찬가지로 Key-value 형태로 저장

세션 스토리지

  • 새 창 생성시마다 개별적으로 데이터 저장 (브라우저 닫는 순간 사라짐.)
  • 로컬 스토리지와 다르게 같은 도메인이라도 세션이 다르다면 데이터 접근 불가
  • 쿠키와 마찬가지로 Key-value 형태로 저장

쿠키, 로컬 스토리지, 세션 스토리지 사용 예제

// 쿠키: document 객체를 통해 관리 (string으로 관리되어 값 추가 등 매우 불편 -> 라이브러리 사용 권장)
document.cookie = 'key=value; key2=value2';

// 로컬 스토리지, 세션 스토리지는 쿠키에 비해 사용하기 편함. + 같은 인터페이스를 가지기 때문에 외우기도 좋음
localStorage.setItem('name', '김정호');
console.log(localStorage.getItem('name')); // 김정호

localStorage.removeItem('name');
localStorage.clear(); // 데이터 전부 제거


sessionStorage.setItem('name', '김정호');
console.log(sessionStorage.getItem('name')); // 김정호

sessionStorage.removeItem('name');
sessionStorage.clear(); // 데이터 전부 제거

🔥 오늘 회고

생각보다 양이 많아서 제대로 정리하는게 어려웠다. 몰랐던 내용도 많았기 때문에 세세하게 들여다보기가 힘들었다 ㅠㅠ 정리가 미흡한 부분은 주말에 좀 더 보충해서 다시 업로드 하도록 해야겠다.

profile
기록하며 성장하는 프론트엔드 개발자

0개의 댓글