HTTP 통신 동작 원리

1. 클라이언트가 주소창에 원하는 URL을 입력하면 URL을 해석한다.

2. DNS를 조회한다.

DNS란?

도메인 이름 시스템(DNS)은 사람이 읽을 수 있는 도메인 이름(예: www.amazon.com)을 머신이 읽을 수 있는 IP 주소(예: 192.0.2.44)로 변환합니다.

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

네트워크 장비인 라우터를 통해 이동한다.

4. ARP를 이용하여 MAC 주소를 변환한다.

  • ARP(Address Resolution Protocol): 논리 주소인 IP 주소를 물리 주소인 MAC 주소로 변환하는 프로토콜이다.
  • 실제 통신은 변하지 않는 MAC 주소가 필요하다.

왜 논리적 주소로는 통신을 할 수 없는가??
그것은 논리적 주소로는 정확한 위치를 알 수 없기 때문이다.
예를 들자면 도로명 주소가 논리적 주소이고, 위도 경도가 물리적 주소라고 생각하면 된다.
논리적 주소는 대역을 좁혀나가는 용도로 사용된다!

5. TCP 통신을 통해 Socket을 열어야 한다.

  • 네트워크를 통해 해당 기기로 패킷을 전달한다.
  • 3 way handshaker로 연결을 요청한다. (TCP 연결을 허락받아야 함)
  • 요청이 수락되면 패킷을 서버로 전달하고 처리한다.

6. 서버는 응답을 반환한다.

  • HTTP 프로토콜로 들어온 패킷을 읽고 처리한다.
  • 요청에 따른 적절한 응답 값을 반환한다. Ex) HTML을 전달

7. 브라우저는 받은 파일을 바탕으로 렌더링 한다.

  • HTML 파일을 읽어 DOM Tree를 구축한다.
  • 만들어진 DOM Tree를 이용하여 화면에 그린다.
  • 만약 스크립트 파일이 있으면 스크립트를 실행한다.

HTTP란?

HTTP(HyperText Transfer Protocol)는 하이퍼 텍스트 전송 프로토콜로 간단히 말해서 인터넷을 작동시키는 역할을 하며, 웹 서버 및 웹 브라우저 상호 간의 데이터 전송을 위한 응용 계층 프로토콜입니다.

웹 사이트에 액세스하기 위해서는 프로토콜 변형이 필요한데, 이때 웹 사이트 URL이 일반적으로 “http://www..”로 시작하며 URL에 해당하는 웹 페이지를 가져오기 위해 웹 사이트 서버에 명령을 보내 작동하게 됩니다.

HTTPS란?

HTTPS(Hypertext Transfer Protocol Secure)는 하이퍼 텍스트 전송 프로토콜 보안으로 표준 HTTP와 동일한 방식으로 작동합니다. 서버와 주고받는 데이터가 암호화되기 때문에 웹사이트에 추가적인 보호를 제공합니다. 즉, 개인 데이터를 훔치거나, 해킹하거나 볼 수 없도록 작동합니다.

HTTP와 HTTPS의 차이점

  • HTTP 메시지는 일반 텍스트이므로, 권한이 없는 당사자가 인터넷을 통해 쉽게 액세스하고 읽을 수 있습니다.

  • 반면, HTTPS는 모든 데이터를 암호화된 형태로 전송합니다. 따라서 HTTP보다 더 안전한 보안용 프로토콜입니다.

컴퓨터 시간 원리

컴퓨터 시간에 대해서는 깊게 생각해 보지 않았는데 이번 기회에 원리를 알아보고 정리하려고 한다!
시간은 다양한 측면에서 영향을 받는다. (물리량, 위치, 천문 현상, 문화, 역사, 사회)
따라서 시간을 처리하는 것에 어려움을 느꼈고, 세계는 표준화를 통해 이를 통일시켰다.

협정 세계시 (UTC)

  • 모든 시간대를 UTC+0을 기준으로 환산한다.
  • 대한민국의 시간은 UTC+9 = 영국(UTC+0)이 오전 1시면 대한민국은 오전 10시
  • 표기법은 ISO 8601을 따른다.
    2023-05-19T09:00:00.000Z // UTC+0 기준 2023년 5월 19일 9시
    2023-05-19T09:00:00.000+09:00 // UTC+9(한국 시간) 기준 2023년 5월 19일 9시
    여기서 T는 Time을 의미하고 Z는 Zulu Time(UTC+0)을 뜻한다.

컴퓨터는 어떻게 시간을 표현하는가?

  • 하드웨어의 시스템 클럭을 이용한다.
  • 특정 시각을 기준으로 시스템 클럭의 틱을 세는 것으로 구현된다. = 시스템 시간이라고 한다.
  • 시스템 시간을 값으로 표현한 것이 타임스탬프(Timestamp)라고 한다.
  • 타임스탬프는 운영체제마다 기준 시간과 단위가 다를 수 있다.

시스템 클럭의 원리

  • RTC(Real Time Clock)라는 모듈을 사용한다. 이는 메인보드에 붙어있고 전원을 끄더라도 계속 작동한다.
  • RTC는 카운터 회로를 통해 클럭을 발생시킨다. 이는 카운터 회로의 부품인 결정 진동자가 만드는 주파수를 이용하는 것이다.

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

NTP 서버에서 네트워크 요청을 통해 현재 시간을 받아온다.
네트워크가 없으면 받아올 수 없다!

NTP

  • 트리 구조의 계층으로 이루어져 있다. 이 계층을 Stratum이라고 부른다.
  • 최상위 계층을 PRC(Primary Reference Clock)이라 부른다.

컴퓨터는 시간대를 어떻게 고려하는가?

국가, 지역별로 시간이 다르고 시간대가 여러 개거나 시간대가 바뀐다면 어떡하지..?
바로 Time Zone 데이터를 이용할 수 있다.
Time Zone은 별도의 데이터 베이스에 저장해 놓은 것이다.
시간이 바뀌면 DB의 값도 업데이트한다.
표기법은 대륙/도시 형태를 따른다. = ZoneId라고 부른다.
ex) Asia/Seoul, America/New_york

서비스를 운영할 때 시간을 어떤 기준으로 사용해야 할까

서비스에 사용되는 시간을 용도에 맞춰서 기록할 필요가 있다!

  • 순수한 시간: 생일, 기업 설립일, 기념일, 국경일 (시간대, 지역, 문화, 사회를 고려하지 않음)

  • UTC: 로깅, 감사, 시계열 데이터 (사건이 발생한 시각만을 고려할 때)

  • Time Zone: 결제 시각, 푸시 알림 시간, UI 시각 표시, 캘린더 (역사, 사회, 문화를 고려하여 사용자가 정확한 시간을 알아야 할 때, UI에 표시되는 시간을 사용자 기준으로 보여줄 때)

JavaScript에서 시간 표현

JavaScript에서는 간단하게 표현할 때는 Date 객체를 통해 시간을 표현할 수 있다.
또는 복잡하게 표현하거나 Time Zone을 적용해야 할 때는 moment.js를 사용할 수 있다. (Deprecated 됨)
대체 라이브러리: date-fns, luxon

암호화

평문을 해독할 수 없는 암호문으로 변환하는 것을 의미한다.
단방향과 양방향 암호화가 존재한다.

단방향 암호화

  • 해시 알고리즘을 이용하여 평문을 복호화 할 수 없는 형태로 암호화한다.
  • 대표적으로 MD5와 SHA 알고리즘이 있다.
  • 사용자 비밀번호 등을 저장할 때 자주 사용된다.
  • MD5와 SHA-0, SHA-1은 해시 충돌이 발생할 수 있는 취약점이 있다고 해서 사용을 권장하지 않는다.

단방향 암호화에서 고려할 점

복호화가 불가능할 거라고 생각하지만 Rainbow Table을 통해 원문을 알아낼 수 있다.

Rainbow Table 이란?

평문과 해시 함수로 만든 문자열을 모두 저장시켜 놓은 표를 말한다.

따라서 불의의 사고로 암호화된 데이터를 탈취당하더라도 원문을 알아낼 수 없도록 조치를 해야 한다.
Salt, Key stretching을 이용하여 해결할 수 있다.

Salt

  • 평문에 임의의 문자열을 추가하여 암호화하는 방법을 말한다.
  • Salt는 128bit 이상으로 만들 것을 권장한다.
  • 사용자마다 다른 Salt를 사용하게 하면 더 안전하다.

Key stretching

  • 해시를 여러 번 반복하여 원문을 알기 힘들게 만드는 방법
  • 일반적인 시스템에서 0.2초 이상 반복되면 안전하다고 한다.

이러한 Salt나 Key stretching은 직접 구현하는 것보다 이미 검증받은 알고리즘을 사용하는 것이 안전하다. (PBKDF2 & bcrypt)

만약 비밀번호 저장을 사용한다면 dcrypt를 사용하는 것이 더 간단하고 안전하다.

양방향 암호화

  • 평문을 복호화 할 수 있는 형태로 암호화하는 방법
  • 대칭키와 비대칭키 알고리즘으로 나뉜다.

1. 대칭키 암호 알고리즘

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

2. 비대칭키 암호 알고리즘

  • 대표적으로 RSA가 있다.
  • 공개키, 개인키가 따로 존재한다.

JavaScript에서 암호화를 사용하려면 라이브러리인 crypto-js를 사용할 수 있다.
bcrypt는 구현되어 있지 않아서 만약 사용하려면 crtyto-js 외 다른 라이브러리를 사용해야 한다.

crypto-js 바로가기

설계에 대한 패러다임

1. 객체지향 프로그래밍

객체지향 프로그래밍(OOP)이란 프로그래밍하려는 대상을 하나의 객체(사물)로 정의하는 설계 방법으로 객체의 관점에서 구조를 만들고 사용하는 방법입니다. 조금 더 구체적인 설명을 덧붙이자면 단순한 자료 구조(변수)를 넘어서 기능(메서드)을 포함한 형태로 객체를 사용하는 프로그래밍입니다.


2. 함수형 프로그래밍

함수형 프로그래밍(FP)은 프로그래밍하려는 문제를 함수들의 정의와 조합을 통해서 해결하는 프로그래밍 방법입니다. 조금 바꿔 말하면 함수의 개념을 최우선적으로 사용해서 모든 문제를 해결하는 프로그래밍 기법입니다.


함수형 프로그래밍의 장단점

장점단점
상태가 없기 때문에 사이드 이펙트가 없다.쓸데없이 메모리를 사용하는 단점이 있음
재사용성이 높다.함수를 잘게 쪼개어야 한다. (복잡해질 수 있음)
코드가 짧고 간결하다.많은 숙련도가 요구된다.

사고에 대한 패러다임

1. 명령형 프로그래밍

문제를 어떻게 해결해야 하는지 컴퓨터에게 명령을 내리는 방법이다.

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

2. 선언형(함수형) 프로그래밍

무엇을 해결해야 할지에 집중하고 해결 방법은 컴퓨터에게 위임하는 방법이다.

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

예시
"oo마트에서 10000원으로 5000원짜리 우산 구매하고 5000원 거슬러서 집으로 와." - 명령형
"우산 사 와." - 선언형


객체지향이란?

객체는 현실에 있는 것을 추상화한 것이다.
추상이란 사물이 지니고 있는 여러 측면 중 특정한 부분만 보는 것이지
현실에 있는 것을 그대로 코드에 옮기는 작업이 아니다!

  • 객체 위주로 설계하고 프로그래밍 하는 패러다임
  • 객체지향 언어에선 추상화의 최소 단위가 객체다.
  • 각각의 객체는 메시지를 주고받을 수 있다.

절차지향이랑 뭐가 다른가요??

절차지향은 공유 데이터를 각 함수가 절차적으로 직접 통제했다면 객체지향은 객체가 데이터를 관리하고 각 메시지를 통해 절차를 간접적으로 통제한다.

객체지향에 대한 오해

  • 객체지향은 패러다임일 뿐 언어와는 관계가 없다.
  • 언어는 지향하는 것을 조금 더 편하게 구현할 수 있게 도와줄 뿐이다.
  • 클래스가 없는 JavaScript, Go, C언어로도 객체지향 프로그래밍을 할 수 있다.
  • 자바스크립트는 프로토타입을 통해 객체지향을 표현한다.
  • 절차지향보다 객체지향이 무조건 더 좋은 것은 아니다.
  • 간단한 프로그램일수록 절차지향이 더 만들기 쉽고 직관적이다.
  • 객체지향은 객체 간 통신하기 때문에 흐름이 더 직관적이어서 복잡한 프로그램에 적합하다.

프로토타입

객체를 생성할 때 프로토타입을 이용하면 상위 객체를 참조할 수 있고, 기존의 객체를 복사하여 새로운 객체를 생성할 수 있다. 따라서 기존 객체를 효율적으로 사용할 수 있는 방법이다.

프로토타입 사용 방법은?

Object.create()를 이용하면 된다.

const user1 = {
  name: "이영준",
  getName: function () {
    return this.name;
  },
};

const user2 = Object.create(user1); // user1의 객체를 재활용
user2.name = "김영준";

console.log(user1.getName()); // 이영준
console.log(user2.getName()); // 김영준
console.log(user1.__proto__); // 프로토타입 확인 {}
console.log(user2.__proto__); // 프로토타입 확인{name: '이영준', getName: [Function: getName]}


이벤트 루프

JavaScript는 Single Thread로 동작한다.
실제로 JavaScript 엔진의 Call Stack은 하나만 존재한다. -> Single Thread로 동작
그럼 브라우저에서 실행되는 스크립트는 어떻게 비동기적으로 데이터를 불러오고 애니메이션을 실행시키는가?
그 이유는 이벤트 루프라는 시스템이 있기 때문이다.

이벤트 루프란?

컴퓨터 프로그래밍에서 비동기 이벤트 처리를 담당하는 메커니즘입니다. 이벤트 루프는 주로 이벤트 기반 프로그래밍 모델에서 사용되며, 이벤트를 받아들이고 처리하는 루프를 말합니다.

동작 원리

Web APIs(DOM Events, AJAX, Timer 등...) 는 브라우저에서 제공하는 API다.
이러한 동작은 실행시킬 경우에 브라우저에 위임된다. 보통 이런 web API는 콜백 함수를 넘기는데, 콜백 함수는 비동기 작업이 끝나면 Task que에 넣어져 순차적으로 꺼내 Call Stack에 Push가 된다.
이런 과정들은 결국 멀티 스레드로 동작한다.
자바스크립트 엔진이 싱글 스레드이지만 브라우저가 멀티 스레드로 동작하기에 이러한 과정들이 가능하다.

추가로 이벤트 루프는 자바스크립트 엔진에 포함되어 있지 않고, 브라우저나 NodeJs에서 자체적으로 관리하고 있다.

모듈

모듈을 사용하면 스크립트 간 의존도를 파악할 수 있고, 실행 순서를 쉽게 제어할 수 있다.

모듈: 설계 시점에 의미 있는 요소 (우리가 의식적으로 나눠놓은 요소)
컴포넌트: 런타임 시점에 의미 있는 요소 (나눠놓은 요소에 포함되어 실행되는 요소)

근데 모듈은 런타임 시점에 실행이 된다. 그럼 왜 이름이 모듈이야??!!

JavaScript는 파일 하나가 프로그램이기 때문에 모듈이라 지었을 것이라고 추측된다.
설계 시 용어가 혼동되는 경우가 많기 때문에 제대로 된 모듈 역할을 하기 위해서 디렉터리 단위를 모듈 개념에 가깝게 사용하는 경우가 많다.
import와 export를 통해 모듈 불러오기와 내보내기를 수행할 수 있다.

모듈을 로컬 파일에서는 동작하지 않고 HTTP 또는 HTTPS 프로토콜을 통해서만 동작한다.
따라서 서버를 실행시켜야 한다.

모듈의 특징

  1. 항상 use strict(엄격 모드)로 실행된다.

  2. 모듈 레벨 스코프가 있다. (최상위에 변수를 선언하더라도 전역 스코프가 아닌 모듈 레벨 스코프에 올라가기 때문에 다른 스크립트에서 참조가 불가능)

  3. 단 한 번만 평가된다. (import문을 두 번 사용하더라도 한 번만 실행됨)

  4. 지연 실행된다. (defer 옵션이 없어도 모든 DOM이 만들어진 후에 실행이 된다.)

요즘은 Webpace 등을 이용하여 번들링 한 스크립트를 불러오면 되기 때문에 모듈 스크립트를 사용할 일이 많지 않다.

유니코드

  • 유니코드가 없을 때도 각 나라들은 알아서 자국 문자를 잘 표현했다.

  • 하지만 다양한 나라가 서로 다른 인코딩 방식을 사용함으로써 호환성 및 확장성에 문제를 일으켰다. (심지어 같은 문자여도 깨지는 경우가 발생했다.)

  • 결국 전 세계 문자를 컴퓨터에서 다룰 수 있도록 만든 표준 시스템을 만들었다.

  • 대부분의 문자를 포함하여 이모티콘도 포함되어 있다.

유니코드 더 알아보기

// 다음 이모지는 4바이트로 구성되어 있다.
// Surrogate Pari를 통해 2바이트보다 큰 문자를 표현할 수 있다.
const smile = "😀";
console.log(smile.charCodeAt(0).toString(16)); // d83d
console.log(smile.charCodeAt(1).toString(16)); // de00

const unicodePoo = "\uD83D\uDE00"; // \u를 통해 유니코드 문자를 표현할 수 있다.
console.log(unicodePoo);

console.log(smile === unicodePoo); // 둘은 같다. true

console.log(smile.length); // 이모지는 문자 길이를 2 차지한다.

console.log("가나다".length); // 유니코드에서 영어든 한글이든 2바이트로 읽힌다. 따라서 한글은 한 글자당 길이를 1 차지한다.


정규 표현식

정규 표현식의 목적

패턴을 이용하여 문자 검색, 대체, 추출할 수 있다.
성능은 매우 느리지만 매우 편하다.

정규 표현식 테스트기 바로 가기

표현 방법
/regexr/i

/: 시작, 종료 기호
regexr: 패턴
i: 플래그

정규 표현식을 사용하기 위해서는 먼저 패턴을 알아야 한다.

예제를 통해 살펴보자

010-1234-5678
018-123-4567

위 패턴을 보면 3자리 숫자, - , 3 or 4자리 숫자, - 4자리 숫자라는 패턴으로 이루어져 있다.
\d{3}: 3자리 아무 숫자를 의미한다.
\d{4}: 4자리 아무 숫자를 의미한다.

이번에는 이메일에서 중간 문자열만 뽑아보자
이번 패턴은 문자열, @, 문자열, ., 문자열 패턴으로 이루어져 있다.
.+: 한 개 이상의 문자열
(...): 캡처 (소괄호로 묶어주면 해당 영역만 뽑을 수 있다.)

yeongjun@naver.com
jun@abcd.com


자바스크립트에서 정규 표현식 사용법

JavaScript는 RegExp 객체로 정규 표현식 기능을 제공한다.
Array, Object처럼 Literal로 생성 가능하다.

// 생성자 함수 방식
const regExp1 = new RegExp("^d+");
const regExp2 = new RegExp("^d+", "gi");

// 리터럴 방식
const regexp1 = /^\d+/;
const regexp2 = /^\d+/gi;

정규 표현식 함수들

test 함수
정규 표현식 객체의 test 함수는 입력받은 문자열에 찾는 패턴이 있는지 찾은 후 있다면 true를 반환하고 없으면 false를 반환한다.

const message = "내 번호는 010-1234-5678이야.";
const message2 = "너에게 줄 번호는 없어!";

const regExp = /\d{3}-\d{3,4}-\d{4}/;
console.log(regExp.test(message)); // true
console.log(regExp.test(message2)); // false

exec 함수
정규 표현식 객체의 exec 함수는 입력받은 문자열에 찾는 패턴이 있는지 찾은 후 일치한 패턴 정보를 반환하고 없으면 null을 반환한다. 문자 추출에 해당한다.

const message = "내 번호는 010-1234-5678이야.";
const message2 = "너에게 줄 번호는 없어!";

const regExp = /\d{3}-\d{3,4}-\d{4}/;
console.log(regExp.exec(message)); 
// [ '010-1234-5678', index: 6, input: '내 번호는 010-1234-5678이야.', groups: undefined]

console.log(regExp.exec(message2)); // null

match 함수
String 객체의 match 함수는 정규 표현식 객체를 파라미터로 받아 패턴이 있는지 찾은 후 일치한 패턴 정보를 반환하고 없으면 null을 반환한다. 정규 표현식 객체의 exec 함수와 같다. 문자 추출에 해당한다. matchAll 함수는 매칭된 모든 케이스를 반환한다.

const message = "내 번호는 010-1234-5678이야.";
const message2 = "사실 내 번호는 010-1234-5678이 아니라 010-123-5678이야.";

const regExp = /\d{3}-\d{3,4}-\d{4}/;
console.log(message.match(regExp));
console.log(message2.match(regExp));
console.log([...message2.matchAll(/\d{3}-\d{3,4}-\d{4}/g)]); // g는 전역 탐색 플래그

출력 결과

[
  '010-1234-5678',
  index: 6,
  input: '내 번호는 010-1234-5678이야.',
  groups: undefined
]
[
  '010-1234-5678',
  index: 9,
  input: '사실 내 번호는 010-1234-5678이 아니라 010-123-5678이야.',
  groups: undefined
]
[
  [
    '010-1234-5678',
    index: 9,
    input: '사실 내 번호는 010-1234-5678이 아니라 010-123-5678이야.',
    groups: undefined
  ],
  [
    '010-123-5678',
    index: 28,
    input: '사실 내 번호는 010-1234-5678이 아니라 010-123-5678이야.',
    groups: undefined
  ]
]

replace 함수 ( 상당히 자주 사용된다!! )
String 객체의 replace 함수는 정규 표현식 객체를 파라미터로 받아 패턴이 있는지 찾은 후 일치한 패턴 정보를 원하는 문자열로 바꿀 수 있다. 문자 대체에 해당한다.

const message = "내 번호는 010-1234-5678이야.";
const message2 = "사실 내 번호는 010-1234-5678이 아니라 010-123-5678이야.";

const regExp = /\d{3}-\d{3,4}-\d{4}/;
console.log(message.replace(regExp, "전화번호"));
console.log(message2.replace(regExp, "전화번호")); 
console.log(message2.replace(/\d{3}-\d{3,4}-\d{4}/g, "전화번호")); // g는 전역 탐색 플래그

출력 결과

내 번호는 전화번호이야.
사실 내 번호는 전화번호이 아니라 010-123-5678이야.
사실 내 번호는 전화번호이 아니라 전화번호이야.   

search 함수
String 객체의 search 함수는 정규 표현식 객체를 파라미터로 받아 패턴이 있는지 찾은 후 일치한 패턴 정보의 위치를 반환한다. 문자 검색에 해당한다. 처음 매칭된 것만 반환하기 때문에 전부 알고 싶으면 matchAll 함수를 사용해야 한다.

const message = "내 번호는 010-1234-5678이야.";
const message2 = "사실 내 번호는 010-1234-5678이 아니라 010-123-5678이야.";

const regExp = /\d{3}-\d{3,4}-\d{4}/;
console.log(message.search(regExp));
console.log(message2.search(regExp));
console.log(message2.search(/\d{3}-\d{3,4}-\d{4}/g)); // 이렇게 해도 처음 매칭된 것만 반환한다. matchAll을 사용해야 함
console.log([...message2.matchAll(/\d{3}-\d{3,4}-\d{4}/g)]);

출력 결과

6
9
9
[
  [
    '010-1234-5678',
    index: 9,
    input: '사실 내 번호는 010-1234-5678이 아니라 010-123-5678이야.',
    groups: undefined
  ],
  [
    '010-123-5678',
    index: 28,
    input: '사실 내 번호는 010-1234-5678이 아니라 010-123-5678이야.',
    groups: undefined
  ]
]

capture
캡처가 적용된 정규 표현식을 이용하면 match 반환값의 1번 인덱스부터 순차적으로 캡처 결과가 들어간다.

const message = "내 번호는 010-1234-5678이야.";

const regExp = /(\d{3})-(\d{3,4})-(\d{4})/;
console.log(message.match(regExp));

출력 결과

[
  '010-1234-5678',
  '010',
  '1234',
  '5678',
  index: 6,
  input: '내 번호는 010-1234-5678이야.',
  groups: undefined
]

Run-length encoding 알고리즘

매우 간단한 비손실 압축 방법
"AAAAAABBBDFFFFFFFKK" 문자열을 어떻게 압축할 것인가? -> "6A3B1D7F2K"로 압축할 수 있다.

여기서 알아야 할 정규 표현식 문법!

  • (.): 소괄호는 그룹을 나타내며, '.'은 임의의 한 문자를 의미한다. 따라서 '(.)'은 한 개의 임의의 문자를 그룹화한다.
  • '\1': 역슬래시와 숫자 1은 첫 번째 그룹을 참조하는 역참조이다. 즉 첫 번째 그룹과 동일한 문자열을 찾는다.
  • '*': 앞에 있는 패턴이 0회 이상 반복됨을 나타낸다.
const raw = "AAAAAABBBDFFFFFFFKK";
const compressed = "";

const regExp = /(.)\1*/g; // 첫번째 문자를 찾고, 그 뒤에 해당 문자와 동일한 문자가 0회 이상 반복되는 패턴을 찾음.
const result = raw.match(regExp).reduce((a, b) => a + `${b.length}${b.slice(0, 1)}`, "");
console.log(result); // 6A3B1D7F2K

쿠키와 세션, 웹 스토리지

쿠키를 알기 전에 먼저 HTTP에 대해 알아보자.

HTTP 통신의 특징

HTTP Request는 기본적으로 상태가 존재하지 않는다. (단발성)
따라서 서버는 어떤 브라우저에서 요청이 온 것인지 알 수 없다.
이때 헤더에 쿠키를 담으면 서버가 쿠키를 읽어 어디서 온 것인지 알 수 있다.

쿠키의 특징

  • 클라이언트에서 저장, 관리하는 데이터들. 브라우저를 닫아도 데이터를 유지할 수 있다.
  • 서버에서 Set-Cookie를 응답 헤더로 내려주면 클라이언트는 받아서 저장한다.
  • 클라이언트에서 자체적으로 조작할 수 있다.
  • 각 상태에 수명을 정할 수 있다.

Set-Cookie: 키=값; 옵션

  • 응답 헤더에 담으면 브라우저가 알아서 저장한다.
  • 각 데이터엔 여러 옵션이 존재한다.
  1. Expires: 쿠키 만료 날짜를 지정한다.
  2. Secure: HTTPS에서만 쿠키를 전송한다.
  3. HttpOnly: JavaScript에서 쿠키에 접근하지 못하도록 막는다. (XSS을 막을 수 있음)
  4. Max-Age: 쿠키의 수명을 정한다. Expires는 무시된다.
  5. Domain: 도메인이 일치하는 요청만 쿠키가 전송된다.
  6. Path: 패스와 일치하는 요청만 쿠키가 전송된다.

쿠키의 취약점

  • XSS(Cross-Site Script) 공격을 당할 수 있다. (최근에는 SPA로 웹사이트를 만들기 때문에 HttpOnly를 사용하기 어려움)

XSS 이란?

웹 애플리케이션에서 일어나는 취약점으로 관리자가 아닌 권한이 없는 사용자가 웹 사이트에 스크립트를 삽입하는 공격 기법입니다. 악의적인 사용자가 리다이렉션 스크립트를 주입하기도 하고, 사용자의 쿠키를 탈취하여 세션 하이재킹(Session Hijacking) 공격을 수행하기도 합니다.

JavaScript를 이용해 악의적인 사용자가 다른 사용자의 쿠키 값을 탈취 할 수 있다.
또한 쿠키를 암호화하지 않고 보내면 쿠키 값을 중간에 탈취 당할 가능성이 있다. -> HTTPS를 이용하면 해결 가능

쿠키를 보냈을 때 서버가 사용자를 구분하려면?

정답은 Session을 사용하는 것이다.

  • HTTP Session id를 식별자로 사용자를 구분한다.
  • 클라이언트는 HTTP Session Id를 쿠키 형태로 저장한다.
  • 서버 자체적으로 기록하고 관리한다.

세션의 문제점

  • 세션은 서버에 파일로 저장된다. 만약 사용자가 엄청 많아진다면 저장 공간이 부족해 서버가 정상적으로 동작하지 않을 것이다.
  • 서버가 만약 2대라면 세션은 인증 정보가 퍼져 있어서 제대로 된 인증이 어려울 수 있다.

결론

따라서 서버와 클라이언트 간 인증은 JWT 같은 별도 토큰을 사용하고
쿠키는 자체적이고 데이터 관리 용도로 많이 사용된다.

웹 스토리지

  • 클라이언트에 데이터를 저장하기 위한 새로운 방법
  • HTML5부터 등장했다.
  • 쿠키에서 하기 힘든 것들을 지원하기 위해 등장했다.
  • 로컬 스토리지와 세션 스토리지가 있다.

1. 로컬 스토리지

  • 로컬 스토리지에 데이터를 저장하면 반영구적으로 데이터가 저장된다.
  • 브라우저를 종료해도 계속해서 데이터가 남아있다.
  • 저장했던 도메인과 이용하는 도메인이 다를 경우엔 접근할 수 없다.
  • 쿠키와 마찬가지로 Key-Value 형태로 저장한다.

2. 세션 스토리지

  • 새 창을 생성할 때마다 개별적으로 저장되는 데이터를 관리한다.
  • 브라우저를 닫는 순간 사라진다.
  • 같은 도메인이어도 세션이 다르면 데이터에 접근할 수 없다.
  • 쿠키와 마찬가지로 Key-Value 형태로 저장한다.

아래는 쿠키, 로컬 스토리지, 세션 스토리지 사용법이다.

document.cookie = "key=value; key2=value2"; // 쿠키 관리는 String으로 한다. 불편하기 때문에 별도의 라이브러리를 사용하는 것이 좋다.

localStorage.setItem('name', '김영준'); // 로컬 스토리지에 key:"name" value:"김영준" 형태로 저장
console.log(localStorage.getItem('name'));

localStorage.removeItem('name'); // 로컬 스토리지에 name이라는 키를 가진 데이터를 지움

localStorage.clear(); // 로컬 스토리지에 있는 데이터를 전부 지운다.


localStorage.setItem('name', '김영준'); // 세션 스토리지에 key:"name" value:"김영준" 형태로 저장
console.log(localStorage.getItem('name'));

localStorage.removeItem('name'); // 세션 스토리지에 name이라는 키를 가진 데이터를 지움

localStorage.clear(); // 세션 스토리지에 있는 데이터를 전부 지운다.

참고
https://www.ascentkorea.com/difference-between-http-and-https/
https://overcome-the-limits.tistory.com/510
https://aws.amazon.com/ko/route53/what-is-dns/

profile
꾸준히 성장하는 개발자 블로그

0개의 댓글