CDN
CDN
분산된 서버 플랫폼인데, 수많은 물리적 위치와 네트워크 위치에 분산되어 있어 웹 콘텐츠에 대한 엔드유저의 요청에 직접 응답한다. CDN은 오리진이라고 불리는 컨텐츠 서버와 엔드유저 사이에서 중재자 역할을 한다.
CDN을 사용하지 않으면, 오리진 서버들은 모든 엔드유저의 요청에 일일히 응답해야 한다. 이는 오리진에 막대한 트래픽을 유발하고 이후에도 엄청난 부하를 유발하여 트래픽이 과도하게 증가하는 등 장애 발생 확률을 높인다.
CDN은 오리진을 대신하여 엔드유저와 가까운 물리적 위치 및 네트워크에서, 응답하여 컨텐츠 서버의 트래픽 부하를 줄인다.
대부분의 CDN의 경우 컨텐츠에 대한 각 요청이 발생시 최적으로 배치된 CDN 서버에 엔드유저가 매핑된다. 해당 서버는 요청된 파일의 캐싱된 버전으로 응답한다. 서버가 파일 찾는데 실패할 경우 CDN 플랫폼의 다른 서버에서 컨텐츠를 찾은 다음 엔드유저에게 응답한다. 컨텐츠를 사용할 수 없거나 오래된 경우, CDN은 오리진 서버에 대한 요청 프록시로 작동하여 향후 요청에 대해 응답할 수 있도록 페칭된 컨텐츠를 저장한다.
index란
사전에 비유할 수 있다. 사전의 내용들이 데이터가 되고, 인덱스 목록에 있는 페이지 번호는 저장된 레코드의 주소다. DBMS도 데이터베이스 테이블의 모든 데이터 검색해서 원하는 결과 가져오려면 시간이 오래 걸린다. 그래서 칼럼의 값과 해당 레코드가 저장된 주소를 key,value 쌍으로 인덱스를 만든다.
DBMS의 인덱스는 항상 정렬된 상태를 유지하기 때문에 원하는 값을 탐색하는데는 빠르다. 하지만 새로운 값을 추가, 삭제, 수정하는 경우에는 쿼리문 실행 속도가 느려진다.
결론적으로 DBMS에서 인덱스는 데이터의 저장 성능을 희생하고 데이터의 READ 속도를 높이는 기능이다. SELECT 쿼리 문장의 WHERE 조건절에 사용되는 칼럼이라고 전부 인덱스로 생성하면 저장 성능이 떨어지고 인덱스의 크기가 비대해져서 오히려 역효과만 불러온다.
index 자료구조
B+-트리
일반적으로 사용되는 인덱스 알고리즘은 B+-Tree 알고리즘이다. 칼럼의 값을 변형하지 않고 원래의 값을 이용해 인덱싱하는 알고리즘이다.
Hash index
칼럼의 값으로 해시 값을 계산해서 인덱싱하는 알고리즘으로 매우 빠른 검색 지원. 왜냐하면 1대1매칭 되니까. 하지만 값을 변형(해싱)해서 인덱싱하므로, 특정 문자로 시작하는 값으로 검색을 하는 등 전방 일치와 같이 값의 일부만으로 검색하고자 할 때 해시 인덱스 사용이 불가능하다.
왜 index 생성시 b-tree 인가?
데이터에 접근시 시간복잡도가 O(1)인 hash table이 더 효율적인데?? SELECT 쿼리에는 부등호 연산도 포함된다. hash table은 등호 연산이 아닌 부등호 연산의 경우 문제가 발생한다. 동등 연산에 특화된 hashtable은 디비의 자료구조로 적합하지 않다.
인덱스는 항상 좋을까? 모든 컬럼에 인덱스 생성하면 빨라지지 않을까?
index 생성시 insert, delete, update 쿼리문 실행시 별도의 과정이 추가적으로 발생한다. insert는 index에 대한 데이터도 추가해야 하므로 그만큼 성능에 손실이 따른다. delete의 경우 index에 존재하는 값은 삭제하지 않고 사용 안한다는 표시로 남는다. 즉 row의 수는 그대로인데, 이 작업이 반복되면?
실제 데이터는 10만 건인데 데이터가 100만건 있는 결과를 낳을 수 있다.
이렇게 되면 인덱스는 더 이상 제 역할을 못하게 되는 것이다. UPDATE 의 경우는 INSERT 의 경우, DELETE 의 경우의 문제점을 동시에 수반한다. 이전 데이터가 삭제되고 그 자리에 새 데이터가 들어오는 개념이기 때문이다. 즉 변경 전 데이터는 삭제되지 않고 insert 로 인한 split 도 발생하게 된다.
하지만 더 중요한 것은 컬럼을 이루고 있는 데이터의 형식에 따라서 인덱스의 성능이 악영향을 미칠 수 있다는 것이다. 즉, 데이터의 형식에 따라 인덱스를 만들면 효율적이고 만들면 비효율적은 데이터의 형식이 존재한다는 것이다. 어떤 경우에 그럴까?
이름, 나이, 성별 세 가지의 필드를 갖고 있는 테이블을 생각해보자. 이름은 온갖 경우의 수가 존재할 것이며 나이는 INT 타입을 갖을 것이고, 성별은 남, 녀 두 가지 경우에 대해서만 데이터가 존재할 것임을 쉽게 예측할 수 있다. 이 경우 어떤 컬럼에 대해서 인덱스를 생성하는 것이 효율적일까? 결론부터 말하자면 이름에 대해서만 인덱스를 생성하면 효율적이다.
왜 성별이나 나이는 인덱스를 생성하면 비효율적일까? 10000 레코드에 해당하는 테이블에 대해서 2000 단위로 성별에 인덱스를 생성했다고 가정하자. 값의 range 가 적은 성별은 인덱스를 읽고 다시 한 번 디스크 I/O 가 발생하기 때문에 그 만큼 비효율적인 것이다.
정규화
한 릴레이션에 여러 엔티티의 애트리뷰트들을 혼합하게 되면 정보가 중복 저장되며, 저장 공간을 낭비하게 된다. 또 중복된 정보로 인해 갱신 이상이 발생하게 된다. 동일한 정보를 한 릴레이션에는 변경하고, 나머지 릴레이션에서는 변경하지 않은 경우 어느 것이 정확한지 알 수 없게 되는 것이다. 이러한 문제를 해결하기 위해 정규화 과정을 거치는 것이다.
그래서 정규화란?
관계형 데이터베이스에서 중복을 최소화하기 위해서 데이터를 구조화하는 작업이다.
1정규화
도메인이 오직 원자값만을 포함하고, 튜플의 모든 애트리뷰트가 도메인에 속하는 하나의 값을 가져야 한다. 배열 ㄴㄴ
2정규화
후보키 K와 K에 속하지 않은 속성 A가 있을 때, A를 결정하기 위해 K의 일부가 아닌 K 전체를 참조해야만 하는 경우 2NF다.
{종업원} 이나 {기술}은 둘다 이 테이블의 후보키는 아니다. {종업원}은 다수의 기술을 가지고 있으면 테이블에 한 차례 이상 나타나기 때문이고, {기술} 또한 다수의 종업원이 같은 기술을 보유하고 있을때 테이블에 한 차례 이상 나타나기 때문이다. 오직 복합 키 {종업원, 기술} 이 이 테이블의 후보 키이다.
그런데 남은 속성인 {근무지}는 후보 키의 일부분인 {종업원}에만 영향을 받는다. 그래서 이 테이블은 2NF가 아니다
그니까 테이블 분리해야함. 종업원-근무지 테이블 따로 만들기
3정규화
"기본키(Primary Key)에 의존하지 않고 일반 컬럼에 의존하는 칼럼들을 제거한다."
React 이점
redux 이점
로컬 스토리지에 상태를 영속적으로 저장하고 시작할 때 다시 불러오는 데 특히 뛰어납니다. 상태를 서버에서 미리 채워서 HTML에 담아 클라이언트로 보내고 앱을 시작할 때 다시 불러오는데 특히 뛰어납니다. 사용자의 액션을 직렬화해서 상태와 함께 자동으로 버그 리포트에 첨부할 수 있습니다. 개발자들은 이를 통해 에러를 재현할 수 있습니다. 액션 객체를 네트워크를 통해 보내면 코드를 크게 바꾸지 않고도 협업 환경을 구현할 수 있습니다. 실행취소 내역의 관리나 낙관적인 변경(optimistic mutations)을 코드를 크게 바꾸지 않고도 구현할 수 있습니다. 개발할 때 상태 내역 사이를 오가고 액션 내역에서 현재 상태를 다시 계산하는 일을 TDD 스타일로 할 수 있습니다. 개발자 도구에게 완전한 조사와 제어를 가능하게 해서 개발자들이 자신의 앱을 위한 도구를 직접 만들 수 있게 해 줍니다. 비즈니스 로직 대부분을 재사용하면서 UI를 변경할 수 있게 해 줍니다.
어려운점
Virtual DOM만 믿고 최적화 신경 안썼는데 소켓통신할 때마다 동일 데이터에 대해 리렌더링 되는 이슈 발견. SCU로 불필요한 render 호출 막아서 성능 이슈 해결.
하면서 어려운점
redux를 사용하지 않은 이유?
redux는 무엇이 일어나는가
와 어떻게 바꾸는가
를 분리하기 위해 빙 돌아가는 방식을 추가하는 것을 제안하는 등가교환이다.
이런식으로 하는게 항상 좋을까? 놉 등가교환이다.
Redux 라이브러리 자체는 리듀서를 하나의 전역 스토어 객체에 장착하는 도우미들 모음이다.
싱글톤
싱글턴은 필요에 의해 단 하나의 객체만을 만들 때 사용한다. 객체 리터럴
이 대표적인 예다. 하지만 아래 속성이 다 공개되므로 비공개로 만드는게 제대로 된 싱글턴이다.
자바스크립트 메모리 관리
-
퀵정렬
최악의 경우 피벗의 왼쪽요 소가 매번 1개인 경우다. 이렇게 되면 높이가 n, 각 층에서 n개의 요소에 대해 정렬을 수행해야 한다.
정렬 알고리즘 비교
스레드 프로세스
프로세스
프로세스는 실행 중인 프로그램이다. 디스크로부터 메모리에 적재되어 CPU의 할당을 받을 수 있는 것을 말한다.
OS로부터 주소 공간, 파일, 메모리 등을 할당 받는다.
구체적으로 살펴보면 프로세스는
함수의 매개변수, 복귀 주소, 로컬 변수와 같은 임시 자료를 갖는 스택과 전역 변수들을 수록하는 데이터 섹션이다.
실행 중인 프로그램, 디스크로부터 메모리에 적재되어 CPU를 할당 받는다. 주소 공간이나 메모리, 파일 등을 할당 받는다.
스레드
스레드는 프로세스의 실행 단위다.
한 프로세스 내에서 동작되는 여러 실행 흐름으로 프로세스 내의 주소 공간 및 자원을 공유할 수 있다. 같은 프로세스에 속한 다른 스레드와 코드, 데이터 섹션, 열린 파일이나 신호와 같은 OS 자원을 공유한다. 하나의 프로세스를 다수의 실행 단위로 구분하여 자원을 공유하고 자원의 생성과 관리의 중복성을 최소화하여 수행 능력을 향상시키는 것을 멀티스레딩이라고 한다. 이 경우 각 스레드는 독립적인 작업을 수행해야 하므로 각자의 스택을 가진다.
PC Register 를 스레드마다 독립적으로 할당하는 이유 PC 값은 스레드가 명령어의 어디까지 수행하였는지를 나타나게 된다. 스레드는 CPU 를 할당받았다가 스케줄러에 의해 다시 선점당한다. 그렇기 때문에 명령어가 연속적으로 수행되지 못하고 어느 부분까지 수행했는지 기억할 필요가 있다. 따라서 PC 레지스터를 독립적으로 할당한다.
스레드는 프로세스의 실행 단위다. 프로세스 내에서 동작되는 여러 실행 컨텍스트로 주소 공간이나 자원을 공유할 수 있다. 같은 프로세스에 속한 스레드 간에 코드, 데이터 섹션, 열린파일과 같은 OS 자원을 공유할 수 있다.
하나의 프로세스를 다수의 실행 단위로 구분하여 자원을 공유하고 자원의 생성과 관리의 중복성을 최소화하여 수행능력을 향상 시키는 것이 멀티스레딩. 문제점은 프로세스 간 공유 자원이 없기 때문에 동일 자원에 동시에 접근하는 일이 없었지만 멀티스레딩 기반 프밍은 이부분을 신경써야한다. 서로 다른 스레드가 데이터와 힙 영역을 공유하기 때문에 어떤 스레드가 다른 스레드에서 사용중인 변수에 접근하여 엉뚱한 값을 read 할 수 있다. 그래서 동기화 작업이 필요하다. 데이터에 접근하는 스레드의 개수를 한개 이하로 유지하는 세마포어와 같은 방법이 있다.
4 hand shaking
3 hand shaking
http1과 http2 차이
http1은 커넥션당 1개의 요청을 처리하도록 설계. 그래서 동시 전송 불가. 요청과 응답이 순차적으로 이뤄진다. 그래서 다수의 리소스(image, css, script)를 처리하려면 리소스 개수에 비례해서 레이턴시가 길어진다.
HOL
하나의 요청처리를 개선하는 기법중 파이프라이닝이 있다. 하나의 커넥션으로 다수개의 파일을 요청/응답받을 수 있는 기법이다. 구체적으로, connection: keep-alive속성과 함께 영속적인 커넥션을 통해 응답을 기다리지 않고 요청을 연속으로 보내는 것이다. 이것으로 커뮤니케이션 레이턴시를 낮추며 성능향상이 되지만 문제가 있다. 즉, 매우 큰 문제가 발생될 수 있는 구조인데, 여러개의 request를 날리면 서버는 이에대해서 request의 차례에 따라 순서대로 response를 보내야한다
첫번째 이미지 요청하고 응답이 지연되면 2,3번째 이미지는 1번째 응답처리가 완료되기 전까지 대기하며 HOL이라는 문제가 발생한다.
RTT 증가 1개의 커넥션에 1개의 요ㅕ청을 처리한다. 그래서 3-way handshake가 반복적으로 일어나고 불필요한 RTT가 증가한다.
무거운 header(특히 쿠키 다수의 http 요청이 발생하게 되는데 이 경우 매 요청시 마다 중복된 헤더값을 전송하게된다. 또한 도메인에 설정된 cookie 정보도 매 요청시마다 헤더에 포함되어 배보다 배꼽이 더 큰 경우가 많다.
눈물 겨운 노력
image spriting: 요청 횟수 줄임
도메인 샤딩: 다수의 커넥션을 생성하여 병렬로 요청.(서브도메인 이용), 하지만 브라우저 별로 domain당 커넥션 개수 제한이 존재하고 근본적 해결책 아님.
minify css, js
data uri scheme은 이미지 리소스를 base64로 인코딩된 이미지 데이터로 직접 기술하는 방식인데 요청 수 줄임.근데 이 방법도 ㄷ문제가 있음
load faster
스타일 싵느 html 문서 상단 위치
스타일시트 html 문서 하단에 배치
html 파싱 도중 js 만나면 DOM 생성을 멈춘다. 그리고 js 먼저 실행한다. JS에서 DOM 구조를 변경할 수 있기 때문이다. 따라서, js는 문서의 끝에서 하는 것이 좋다. 왜냐면 렌더링 엔진이 더 나은 상요자 경험을 위해 점진적 진행하기 때문이다. js가 실행되는 동안 DOM 생성은 멈추지만, DOM이 변경되어 렌더트리를 다시 만드는 일은 없기 떄문이다.
http2
connection: keep-alive와 pipelining의 개선안이다. 한 커넥션으로 동시에 여러개의 메시지를 주고 받으며(응답 다중화), 응답은 순서에 상관없이 stream으로 주고 받는다.
frame단위로 보내게 되는데 그러면 한번에 클라는 서로 다른 request를 나타내는 stream의 frame이 섞여서 보낼 것이고, 받는 서버는 이를 stream별로 정리하여 해석하고 그에 대한 응답은 먼저 준비되는 순서대로 보낸다. 이로 인해 하나의 request가 오래 걸려도 다른 요청에는 문제 없이 처리 되어 response를 받을 수 있다.
서버 푸시
어떤 html 파일은 css, js를 필요로 한다. 보통 html 요청 후 파싱할 때까지 어떤 css, js파일이 필요한지 모른다. 그래서 문서 파싱하면서 필요한 리소스 재요청하는 반면, 2에서는 클라가 요청하지 않은 리소스를 푸시해주는 방법으로 클라 요청을 최소하하여 성능 향상을 이끌어 낸다.
또 이 frame에는 push할 데이터를 어떤 stream을 통하여 보낼껀지 적어둘 수있는데 이때 stream은 당연히 사용중이면 안되고, 해당 스트림은 reserved된다.
기존에 처리된 스트림과 별개로 리저브 되어있던 스트림으로 서버는 보내고자 하는 push data의 리스폰스 헤더를 날리고 end_ehader flag 설정해서 다 보냈음을 알린다.
Stream 1번으로 /index.html 요청이 들어왔다고 가정하자, 그러면 server는 index.html은 style.css가 필요로한다는 사실을 알고 있다(알고 있을것이다!!) 그러면 이때 client에게 PUSH_PROMISE를 1번 stream을 통해서 보낸다.
PUSH_PROMISE의 내용에는 server가 2번 스트림을 열것이고, :path는 /style.css이고 :method는 GET이고 scheme은 https다, authority등 헤더들을 포함한 내용이 들어갈것이다. 그러면 서버는 stream 1번으로 index.html의 요청에 해당하는 응답 헤더와 html내용에 더불어서 stream 2번으로는 /style.css의 응답 header와 내용을 보낼 수 있게된다.
실행 컨텍스트
실행컨텍스트는 실행 가능한 javascript 코드 블록이 실행되는 환경이다. 실행 가능한 javascript 코드 블록이란 대부분 함수인데, 이게 실행되면 실행 컨텍스트가 생성된다.
실행 컨텍스트의 생성 과정에서 중요한 것은 AO, VO, Scope Chain이다. AO 생성 후,
arguments 객체 생성 Scope Chain([[scope]]) 생성 변수 생성(메모리에 추가, but not 초기화) this 바인딩 코드 실행(변수 초기화, 연산 및 함수 실행)
scope chain
C언어의 {}와 달리 오직 함수
만 유효 범위의 한 단위다. 유효범위를 나타내는 스코프가 [[scope]]
프로퍼티다.
각각의 함수는 [[scope]] 프로퍼티로 자신이 생성된 실행 컨텍스트의 스코프 체인을 참조한다. 함수가 실행되는 순간 실행 컨텍스트가 만들어지고, 이 실행 컨텍스트는 실행된 함수의 [[scope]] 프로퍼티를 기반으로 새로운 스코프 체인을 만든다.
함수 객체 생성시 그 함수 객체의 [[scope]]는 현재 실행되는 컨텍스트의 VO에 있는 [[scope]]를 그대로 가진다. 따라서, func 함수 객체의 [[scope]]는 전역 변수 객체가 된다.
function, prototype chaining
OOP 상속의 근간이 되는 프로토타입과 프로토타입 체이닝에 대해 알아보자. JS가 객체 리터럴 생성자 함수로 객체 생성하면, 객체의 부모는 생성자 함수의 prototype 객체다. 즉, 상속 개념과 동일하게 자식객체가 부모 객체가 가진 프로퍼티 접근 및 메서드를 상속받아 호출 가능하다.
virtual DOM
브라우저 렌더링 과정
dom tree 생성, cssom 생성 -> 렌더 트리 생성 -> 리플로우(크기, 사이즈 배치 관련) -> 페인팅(색 입히기)
DOM 변화 생기면, 렌더트리 재생성(모든 요소 스타일 재계산) -> 리플로우 -> 페인팅.
VIRTUAL DOM
위 과정에서 virtual DOM은 뷰에 변화가 있을 경우, 실제 DOM에 적용되기 전에 가상의 DOM에 먼저 적용시키고 최종 결과를 실제 DOM 에 전달. 브라우저 내에서 연산의 양을 줄여준다.
DOM 조작의 실제 문제는 레이아웃 변화, 트리 변화와 렌더링을 일으킨다는 것이다. 그래서 30개의 노드를 하나하나 수정하면 30번의 레이아웃 재계산과 리렌더링을 초래한다.(물론 virtualDOM이 아니더라도 최적화 할 수 있다. 근데 이걸 virtual DOM이자동으로 해준다)
virtual DOM은 변화가 일어나면 일단 offline DOM트리에 적용시킨다. 이 DOM tree는 렌더링이 안되서 연산 비용이 적다. 연산 끝나고 나면 최종 변화를 실제 DOM에 던져준다. 딱 한번만 한다. 모든 변화릃 하나로 묶어서!. 그러면 레이아웃 계산과 리렌더링 규모는 커지겠지만 딱 한 번만 한다. (이 과정은 변화가 있을 떄 우리가 묶어서 처리해주는 코드를 작성하면 된다.) virtual DOM은 DOM관리를 수동으로 하나하나 하는게 아닌 자동화하고 추상화하는거다. 만약 이 작업을 수작업으로 하려면 기존 값중 어떤게 바뀌었고 어떤게 바뀌지 않았는지 알아야한다(그래야 붤필요한 DOM 트리를 피한다) 이것도 virtual DOM이 자동으로 해준다.
리액트는 빠른게 아닌, 유지보수가능한 어플리케이셔ㅑㄴ을 만들도록 도와준다.
Last updated
Was this helpful?