I/O Scheduling 은 운영체제가 여러 프로세스로부터 I/O 요청을 받았을 때 이에 대한 우선순위를 정해서 실행시켜주는 것을 말한다.

 

일반적으로 OS 내에 있는 스케줄러는 throughput 과 latency 를 고려하여 설계되며 하드디스크 검색으로 낭비되는 시간을 최소화하고, 우선적으로 필요한 프로세스들에게 I/O 요청을 할당하는 것이 주 목표이다.

 

I/O Scheduler 의 종류로는 다음과 같은 것들이 있다.

 

- Noop Scheduler : 커널이 스케줄링을 위해 별다른 작업을 하지 않는다. SSD 또는 RAID 디스크를 사용하는 경우 스케줄러의 동작이 오히려 디스크의 성능에 부하를 줄 수 있다는 점에 착안하였다.

 

- CFQ(Completely Fair Queueing) : 기본 스케줄러로, 각 프로세스마다 Round Robin 알고리즘에 의해 스케줄링이 수행된다. 각 작업을 마친 후 일정 time slice 만큼 기다린 후 다른 프로세스로 제어를 넘긴다. Synchronous 작업이 Asynchronous 작업보다 우선순위가 높다.

 

- Deadline : I/O 대기 시간의 한계를 정해놓고 데드라인이 가까운 작업부터 제어를 할당한다. 지연에 최적화되어있으며 I/O 가 몇몇 프로세스에 집중되어 있을 때 유용하기 때문에 Database 시스템에 주로 사용된다.

 

- Anticipatory : 연속된 read 요청의 경우 미리 처리량을 예측해서 요청을 스케줄링하는 방식으로 응답시간이 중요한 경우에 유용하다. 

 


 

 

HTTP/1.0 은 가장 기초적인 형태의 웹 프로토콜을 제시하였고, 이는 일전 포스팅에도 정리되어 있듯이 TCP 프로토콜 위에 HTTP Spec 을 이용한 HTTP 프로토콜을 충실히 따른다.


이 방식은 웹이라는 새로운 환경에서 TCP에 비해 나은 성질의 프로토콜이었으나, 웹의 규모가 거대해지면서 몇가지 이슈들이 발생하였다.

 

특히 Network Latency 이슈가 주된 문제였는데, HTTP/1.1 은 이 문제를 해결하기 위해 특징적인 2가지 개념을 도입한다.

 

(1) HTTP Pipelining (HTTP 파이프라이닝)

 

 

 

HTTP Pipelining 이란 HTTP1.1 로 스펙이 업그레이드 되면서 클라이언트와 서버간 요청과 응답의 효율성을 개선하기 위해 만들어진 개념이다.

 

HTTP Request 들은 연속적으로 발생하며, 순차적으로 동작한다.


HTTP/1.0 에서 HTTP Request 는 소켓에 write 한뒤, 서버의 Response 를 받아 다음 Request 를 보내는 방식으로 웹이 동작한다. 

여러 요청에 대해 여러 응답을 받고, 각 처리가 대기되는 것은 Network Latency 에 있어서 큰 비용을 요구한다.

게다게 HTTP/1.0 에서 HTTP 요청들은 연결의 맺고 끊음을 반복하기 때문에 서버 리소스 적으로도 비용을 요구한다.


HTTP/1.1 에서는 다수의 HTTP Request 들이 각각의 서버 소켓에 Write 된 후, Browser 는 각 Request 들에 대한 Response 들을 순차적으로 기다리는 문제를 해결하기 위해 여러 요청들에 대한 응답 처리를 뒤로 미루는 방법을 사용한다.

 

즉, HTTP/1.1 에서 클라이언트는 각 요청에 대한 응답을 기다리지 않고, 여러개의 HTTP Request 를 하나의 TCP/IP Packet 으로 연속적으로 Packing 해서 요청을 보낸다.


파이프라이닝이 적용되면, 하나의 Connection 으로 다수의 Request 와 Response 를 처리할 수 있게끔 Network Latency 를 줄일 수 있다.


하지만 위의 기법 설명에서 언급하듯이, 결국 완전한 멀티플렉싱이 아닌 응답처리를 미루는 방식이므로 각 응답의 처리는 순차적으로 처리되며, 결국 후순위의 응답은 지연될 수 밖에 없다.


<이미지 출처 : https://www.popit.kr/%EB%82%98%EB%A7%8C-%EB%AA%A8%EB%A5%B4%EA%B3%A0-%EC%9E%88%EB%8D%98-http2/>



이는 HTTP의 Head Of Line Blocking 이라 부르며 Pipelining 의 큰 문제점이다.


따라서 HTTP/1.1 의 웹서버는 서버측에 Multiplexing 을 통한 요청의 처리를 요구하며, 각 Connection 당 요청과 응답의 순서가 보장되는 통신을 한다.

 

HTTP 파이프라이닝은 HTTP/2 가 등장하면서 멀티플렉싱 알고리즘으로 대체되었고, 모던 브라우저들에서도 기본적으로는 활성화하지 않고 있다.

 

 

(2) Persistent Connection

 

 

HTTP 1.0 초기의 HTTP 연결은 요청시 TCP 의 3-way handshake 방식으로 연결이 이루어졌었다.

즉, 웹클라이언트와 서버간의 연결 성립 이후 SYN, SYN-ACK, ACK 핸드셰이킹이 발생하고 이를 바탕으로 통신을 구성한 뒤 연결을 끊는 순서가 필요했다..

웹의 초창기에는 컨텐츠의 수가 많지 않았기 때문에 이런 TCP 연결은 부담되지 않았지만, 웹을 통한 멀티미디어 컨텐츠의 발달로 인해, TCP Connection 의 재사용이 요구되게 되었다.

 

웹에서의 커넥션 재사용을 Keep-alive 또는 Connection reuse 라 하며, HTTP/1.0 에서는 클라이언트가 서버에게 요청하는 Request Header에 다음과 같은 값을 통해 연결을 유지하였다.

 

Connection: keep-alive


HTTP/1.1 에서는 이 헤더를 사용하지 않더라도 모든 요청/응답이 Connection을 재사용하도록 설계되어 있으며, 필요없는 경우에만 TCP 연결을 종료하는 방식으로 변경되었다.

 

Connection: close

 

해제 시에 위와 같은 명시적 Connection 헤더를 이용하며, 그렇지 않은 경우 무조건 연결은 재사용되게 되어 있다.

 

이러한 이슈는 HTTP/2 로 웹이 한단계 더 발전하면서 해결되었다.

자세한 건 다음 링크를 참조

(https://jins-dev.tistory.com/entry/HTTP2-%ED%8A%B9%EC%A7%95%EB%93%A4%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A0%95%EB%A6%AC)

 


현재 웹 표준으로는 HTTP/2 가 개발중이며 이미 공개가 되어있다. HTTP/2 에서는 구글이 프로토타입으로 제시한 SPDY 등의 프로토콜을 기반으로 한 웹 기술의 발전이 예상되는 만큼 위의 기술들 또한 보다 더 나은 방향으로 진화될 것이라 생각한다.


자바의 클래스로더는 자바의  Class  들을 런타임에 JVM으로 로딩하는 역할을 한다.
클래스로더는 JRE(Java Runtime Environment)의 한 부분이다.


JVM은 클래스로더 덕분에 파일의 구조나 파일 시스템을 고려하지 않을 수 있으면서 자바 프로그램을 작동시킬 수 있다.

이는 C/C++언어의 결과처럼 자바 프로그램이 하나의 Single Executable 파일을 생성하는 것이 아니라 여러 개의 자바 클래스들이 JAR의 형태로 묶여서 관리되다가 JVM에 의해 메모리에 올라가서 바이너리 형태로 동작하기 때문에 생기는 차이점이라 할 수 있다.

자바 클래스들은 한번에 모든 클래스가 메모리에 올라가지 않는다. 각 클래스들은 필요할 때 어플리케이션에 올라가게 되며, 이 작업을 클래스로더가 해주게 된다.

 

Class Loader 는 다음과 같은 역할을 해준다. 


 - Loading : 클래스 파일에서 클래스 이름, 상속관계, 클래스의 타입(class, interface, enum) 정보, 메소드 & 생성자 & 멤버변수 정보, 상수 등에 대한 정보를 로딩해서 Binary 데이터로 변경한다.


 - Linking : Verification 과 Preparation, Resolution 단계를 거치면서 바이트코드를 검증하고 필요한 만큼의 메모리를 할당한다. Resolution 과정에서는 Symbolic Reference 를 Direct Reference 등으로 바꿔준다.


 - Initialization : static block 의 초기화 및 static 데이터들을 할당한다. Top->Bottom 방식으로 클래스들을 해석한다.


(자세한 기능 동작은 다음을 참조

https://jins-dev.tistory.com/entry/JVMJava-Virtual-Machine-%EC%9D%B4-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94-%EC%A7%80-How-JVM-Works)


자바의 클래스로더는 3가지의 서로다른 클래스로더로 구성되어 있다.

 

 (1) Bootstrap Class Loader
가령 java.lang 이나 ArrayList 같이 java.lang.ClassLoader 가 로드할 수 없는 bootstrap 또는 primordial 클래스들에 대해서는 JRE의 Bootstrap ClassLoader 가 이 역할을 해준다.
Bootstrap ClassLoader 는 native 언어로 만들어진 JVM 코어의 일부분으로, 일반 클래스 로더가 불러올 수 없는 클래스 들의 로딩을 담당해준다.

 

 (2) Extension Class Loader
Extension Class Loader는 bootstrap Class loader 의 자식클래스로 일반적인 Core Java Class 들의 로딩을 담당한다.

 

 (3) System Class Loader
System Class Loader는 시스템 환경변수 또는 클래스 변수 등과 같은 환경에 종속된 클래스들에 대한 로딩을 담당한다.


클래스로더는 런타임에 모든 클래스들에 대한 정의를 로딩한다. 이때 만약 클래스 매핑에 실패한다면, NoClassDefFoundError 를 만나보게 된다.

 

Java 의 클래스로더는 3가지 원칙을 지키며 동작한다.

 

1. Delegation :  Java의 클래스는 필요할 때 로딩되어야 한다는 원칙이다.

 

2. Visibility : Child 클래스는 Parent 클래스로더에 의해 로딩된 클래스들을 확인할 수 있지만, 그 역은 불가능하다는 원칙이다.

 

3. Uniqueness : 한번 클래스로더에 의해 로딩된 클래스는 재로딩되지 않는다. 가령, Parent 클래스로더에 의해 이미 로딩된 클래스는 Child 클래스로더가 다시 로드하지 못한다.

 

Java의 ClassLoader 클래스를 상속하면, 사용자정의 클래스로더를 구현할 수도 있으며, 언제든 findClass() 와 loadClass() 메서드를 이용해 동적으로 로딩이 가능하다.




컴파일 과정은 사람이 이해할 수 있는 High Level Programming Language 로 구성된 소스코드를 기계가 이해할 수 있는 Lower Level Language 로 바꾸는 과정이다.


컴파일러는 다음과 같은 과정을 통해 컴파일을 수행한다.


[출처 : https://www.programcreek.com/2011/02/how-compiler-works/]


위의 그림은 컴파일의 단계를 간략하게 설명한다. 다음은 그림에 대한 설명이다.


(1) Lexical Analysis 

소스코드를 Token 으로 분할한다. 모든 키워드와 Parenthesis, 변수들 및 괄호들을 분리해낸다.


(2) Syntax Analysis

앞선 단계에서의 스캔으로 만들어진 토큰들(Token Stream)의 문법을 분석하기 위한 자료구조료 변형한다.

이렇게 만들어지는 자료구조를 Parse Tree 라고 한다.

이 단계에서는 Token 이 Valid 한지 검출하지 못하며, Token 이 사용되기 이전에 정의 또는 초기화되어있는 지 등 정적분석은 불가능하다.


또한 이단계에서 파싱이 일어난다. Parsing 작업은 Top-Down, Bottom-Up 두가지 방식으로 나뉜다.

Top-Down Parsing 은 Parse Tree 의 윗쪽부터 파싱을 수행하며, Bottom-Up Parsing 은 트리의 아래쪽부터 파싱이 수행된다.


유명한 파서의 종류로 Top-Down 방식의 Non-Backtracking Predictive Parser 인 LL Parser와 Bottom-Up 방식의 LR Parser 가 존재한다.


(3) Semantic Analysis

각 기호들 및 구문들을 의미있는 값들로 변경한다. 가령 기호 < 는 bool 을 Return 하는 Operand 함수로,

While 과 같은 키워드는 반복 구조를 이루는 void 함수로, 각 변수는 메모리로 치환한다.

이 단계에서 Type 의 Mismatch 나, 변수의 미정의, 파리미터 미정의 등 문법적 요소들이 검증된다.


(4) IR Generation

구문 분석으로 이루어진 자료를 중간 언어로 변경하는 작업을 수행한다.

IR 은 Intermediate Representation 의 약어로 소스코드에 근접한 기계어인 High Level IR, 타겟 머신에 종속적으로 디자인된 Low Level IR 이 존재한다.

컴파일러는 소스코드를 High Level IR -> Low Level IR 로 변경한 뒤 Target Machine Code 로 해석한다.


(5) IR Optimization

중간 언어를 최적화한다. 불필요한 루프를 없애거나, 사용하지 않는 변수의 정리 등이 수행된다.


(6) Code Generation

Syntax Analyzer 및 Semantic Analyzing 된 출력값을 Low level Code 로 해석한다.

이 단계를 거쳐서 Assembly Code 등의 Object Code 로 번역된다.


이 단계에서 수행되는 작업들은 다음과 같다.

 - Instruction Selection : 어떤 명령어를 사용할 것인지

 - Instruction Scheduling : 어떤 명령어를 먼저 실행할지 (최적화)

 - Register Allocation : 변수들을 프로세서 레지스터에 할당

 - Debug data generation : 디버그 모드인 경우 디버그를 위한 코드를 생성


(7) Optimization

위의 과정에서 생성된 코드를 한단계 더 최적화한다.

이 단계의 최적화는 두단계로, Machine 에 종속되지 않은 일반적인 형태의 최적화와 Machine 에 종속된 최적화가 이루어진다.

이 과정을 거치면서 중복 제거, 메모리 확보, 코드 정리, 루프 최적화, Control Flow 개선이 일어난다.


위의 단계들을 거치면 High Level Source Code 는 Machine Code 로 변환된다.

Generating 되는 코드는 대게 머신별로 다르게 되며, 세부로직 및 최적화의 과정 역시 컴파일러에 따라 차이가 존재하게 된다.




본 포스팅은 컴파일러에 대한 간략한 소개를 다루고 있으며 더 자세한 내용은 다음 링크들을 참조한다.

(이해해야할 분량이 많다.)


참조

https://www.tutorialspoint.com/compiler_design/index.htm

https://www.programcreek.com/2011/02/how-compiler-works/



 많은 어플리케이션들에서 사용되는 캐시 로직과 마찬가지로 Web browser 역시 캐시를 사용한다.


웹브라우저에서의 캐시 사용은 단순히 웹페이지의 빠른 로딩을 가능하게 할 뿐 아니라, 이미 저장된 리소스의 경우

서버측에 불필요한 재요청을 하지 않는 방법을 통해 네트워크 비용의 절감도 가져올 수 있다.


이는 아주 중요한 부분으로 리소스를 캐시했다가 재활용함은 트래픽 절감과 클라이언트 측에서의 반응성 향상 및 서버의 부하 감소라는 이점까지도 가져올 수 있다.


그렇다면 웹브라우저는 어떤 항목을 어떻게 캐시하는 것일까?


HTTP Spec 에 의하면, 모든 HTTP Request / Response 는 이러한 캐싱 동작과 관련한 설정을 Header 에 담을 수 있다.

주로 사용되는 HTTP Response Header 로는 Cache-Control 과 ETag 가 있다.


(1) Cache-Control


Cache-Control 헤더는 어떻게, 얼마나 오래 응답을 브라우저가 캐싱하면 좋을지를 브라우저에게 알려준다.


Cache-Control 헤더는 브라우저 또는 다른 캐시가 얼마나 오래 어떤 방식으로 캐싱을 할지를 정의한다. 


Web browser 가 서버에 리소스를 첫 요청할 때, 브라우저는 반환되는 리소스를 캐시에 저장한다. 

Cache-Control 헤더는 몇가지 서로다른 Pair 를 Parameter 를 가질 수 있다.


 - no-cache / no-store : no-cache 파라미터는 브라우저가 캐시를 사용하지 않고 무조건 서버에서 리소스를 받아오게끔 한다.

 하지만 여전히 ETags 헤더를 체크하기 때문에, ETags 헤더의 조건에 맞는다면 서버에 직접 요청하지 않고 캐시에서 리소스를 가져온다.

 no-store 파라미터는 ETags 에 상관없이 Cache 를 사용하지 않고 모든 리소스를 다운받도록 하는 옵션이다.


 - public / private : public 은 리소스가 공개적으로 캐싱될 수 있음을 말하고 private 은 유저마다 리소스에 대한 캐싱을 하도록 한다.

 private 옵션은 특히 캐시에 개인정보가 담길 경우 중요하다.


 - max-age : max-age 는 캐시의 유효시간을 의미한다. 초단위로 입력된다.


(2) ETag


ETag 헤더는 캐시된 리소스가 마지막으로 캐시된 이후에 변했는지를 체크해주는 헤더이다.

전체 리소스를 재다운로드하는 대신, 수정된 부분을 체크하고, Same Resource 는 재다운로드하지 않는다.

ETag 는 서버에서 리소스에 대해 할당하는 Random String 으로 할당이 되며, 이값을 비교함으로써 revision 을 체크한다.

이 유효성 검사 토큰을 사용하면 리소스가 변경되지 않은 경우 이므로 추가 데이터 전송 요청을 전송하지 않는다.


ETag 값은 다시 서버에 전송해야하며, 서버는 리소스의 토큰값과 비교해서 변경되지 않은 경우 304 Not Modified 응답을 반환한다.



이를 바탕으로 브라우저 캐싱 동작을 정리해보자.




웹브라우저는 서버의 응답값을 바탕으로 재사용가능한 Response 인지 확인하고, Validation 과 Cache 의 성질, Expiration Time 에 따라 캐시 정책을 결정한다.

이에 대해 구글은 위와 같은 명확한 형태의 Decision Tree 를 제공한다.


이외에도 웹에서 사용되는 캐시 로직을 좀 더 이해하기 위해선 서버사이드의 Cache 로직 역시 이해할 필요가 있다.


캐시 정책에 있어서 왕도는 없으며, 트래픽 패턴, 데이터 유형 및 서비스 종류에 따라 알맞게 설계하는 것이 중요하다.

그 중에서도 최적의 캐시 Lifetime 의 사용, Resource Validation, 캐시 계층 구조의 결정은 반드시 고려되어야 한다.




좀 더 자세한 자료는 다음을 참고한다.

[https://thesocietea.org/2016/05/how-browser-caching-works/]

[https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=ko]




 Web Storage 는 HTML5 에서 새로 생겨난 신기술로, Cookie 를 대체할 목적으로 만들어진 Browser Storage 이다.


HTML5 이전에 Application data는 쿠키에 저장되었고 모든 Server 에 대한 Request 에 첨부되었다.

이에 반에 Web Storage 는 보다 안전하고 많은 데이터를 저장할 수 있으면서 웹 사이트 퍼포먼스 저하에 영향을 미치지 않는다.


4kb 까지 저장할 수 있는 쿠키와 다르게 5mb 까지 저장할 수 있으며 저장된 정보는 매요청마다 서버로 전달되지 않는다. 


또한 암호화가 가능하며, 쿠키에 비해 접근 및 내용확인이 어렵다.(보안 상 안전하다.)


실제로 오래 유지될 필요가 있을지는 의문이지만... 만료일자가 지정되어있는 쿠키와 다르게 반영구적 보존이 가능하다는 점도 장점이다.



HTML5 Web Storage 는 Local Storage & Session Storage 로 나뉘며 간단한 Key : Value 를 저장할 수 있는 점은 같다.


 - Local Storage : 로컬에 Origin 별로 지속되는 스토리지. Domain 마다 별도로 Local Storage 가 생성된다. Javascript 에서 Window 전역 객체의 localStorage 컬렉션으로 접근이 가능하다.


 - Session Storage : 현재 세션 동안만 유지되는 스토리지. Window 전역 객체의 sessionStorage 컬렉션을 통해 접근이 가능하다. Browser Context 내에서만 데이터가 유지되므로 브라우저 종료 시 데이터도 삭제된다.



HTML5 의 Web Storage 는 기존의 쿠키를 대체할 목적이 아닌 언어 레벨에서 제공해주는 훌륭한 Browser Storage 옵션에 가깝다. 

실제로 쿠키처럼 매 요청시 서버로 전송되지 않기 때문에 필요한 요청에 대해 명시적으로 Request 를 날려야할 필요가 있다.





웹은 HTTP 의 특징 상 TCP와는 다르게 Stateless 프로토콜을 사용하기 때문에 HTTP 를 사용하면서 연결 관리를 위해 여러가지 도구를 사용한다.


그 중 하나가 Cookie 이며, 쿠키는 다양한 방법으로 웹서버의 동작에 도움을 준다.


먼저 쿠키에 대한 흔한 오해로, 웹사이트가 하드디스크에 저장하는 개인에 대한 정보이며 웹사이트에 접근시 사이트는 쿠키의 정보를 이용하며 새로운 정보를 저장할 수 있다는건 잘못된 정보이다.


Cookie 는 프로그램이 아니며, 개인에 대한 정보를 저장하지 못한다. 


Cookie 는 웹서버가 유저의 하드디스크에 저장하는 텍스트 조각이며 Binary 형태의 데이터가 될 수 없다.

일반적으로 쿠키는 Name-Value 쌍으로 이루어진 웹사이트 접근에 대한 정보이다.


웹서버마다 쿠키를 활용하는 방식은 다양하며, 단순히 접속에 대한 정보나 ID 만을 저장하는 데 그치지 않고, 세션 혹은 접속 시간 등 다양한 정보를 기록하기도 한다.


쿠키는 그 자체만으로 어떤 역할도 할 수 없으며, 단지 웹서버로 전송될 수 있는 정보를 가질 뿐이다.


쿠키는 HTTP 프로토콜 상의 제약에 따라 4kb 까지 저장할 수 있으며 하나의 도메인은 브라우저마다 다룰 수 있는 쿠키 갯수의 상한을 갖는다.

즉, 쿠키는 도메인별로 브라우저가 Storage 에 관리하는 형태의 정보로, 브라우저가 쿠키의 보안을 관리한다.


이렇듯 브라우저가 도메인별로 쿠키를 관리하기 때문에 서로 다른 브라우저는 서로 다른 쿠키를 갖는다. 즉, 브라우저별로 쿠키는 공유되지 않는 내부 저장 데이터이다.


쿠키는 서버와 유저의 Local storage 간의 약속이므로, 같은 Name 으로 Value 를 기록(Write) 하고 읽어오는 것(Read)이 중요하다. 

대부분 쿠키는 웹브라우저의 내부 저장소에서 Javascript 를 통해 서버에서 핸들링 할 수 있도록 처리된다.


"여기서 클라이언트가 서버에게 쿠키를 보낸다는 점이 중요한데, 대부분의 경우 특별한 제약 등을 정의하지 않으면 Cookie 는 서버로 향하는 모든 요청과 함께 전송된다."


주로 쿠키를 사용하는 목적은 다음과 같다.


- Authentication : 세션 관리.


- User Tracking : 유저 방문 추적


- Personalization : 테마, 언어 등 커스텀화된 설정에 대한 목적



웹사이트를 통해 개발한다면 브라우저의 개발자도구를 이용해 쿠키에 대한 정보를 언제든 접근 가능하다.




대부분 쿠키나 세션의 경우 브라우저에서는 Cookie API 를, 서버 측에서는 Web framework 들이 처리할 수 있는 로직을 내장하고 있으나, 

관련 작업을 할 경우 위와 같이 체크하면서 작업하면 유용하다.


Mozilla 공식 홈페이지는 쿠키의 생성 및 사용 로직이 단순하기 때문에 정해진 용도 이상의 사용, 특히 개인정보의 저장 등을 위해 사용하는 것이 위험하다고 경고하고 있다.



DOM은 W3C 표준으로 정의되어있는 문서의 항목을 객체화하기 위한 방법으로, 다음과 같은 3가지 파트로 나뉘어 있다.


 - Core DOM : 대부분의 Document Type 에서의 Object 형태를 말한다.

 - XML DOM : XML 문서 타입에서의 객체형태를 말한다.

 - HTML DOM : 포스팅에서 다루고자 하는 내용이며, HTML 문서 내에서 객체들이 해석되기 위한 형태를 말한다.


즉, HTML 에서 DOM이라함은 W3C의 HTML DOM 표준을 말하는 것이다.


DOM 오브젝트란 Document Object Model 의 약어로 웹브라우저가 HTML 을 해석하는 방식이자, Document 및 내부 구성요소를 객체화한 형태의 집합이라 할 수 있다.


HTML 의 각 요소들은 HTML Tag 및 텍스트들로 구성되어 있고, 이 각각의 항목을 노드(Node) 라 하고, 각각의 항목들은 Tree 의 계층형 구조를 이루고 있다.


HTML DOM 은 프로그래밍적 인터페이스를 허용하게 되어있으며 그에 따라 인터프리터가 Object 형태로 관리하게 되어있다.

각 Element 들은 Property 나 methods, events 를 가질 수 있게 설계되어 있다.


HTML DOM 은 HTML 의 Element 들을 나타내는 방식이자 Web 에서 문서가 동작하기 위한 표준으로 이해하면 된다.


<참고 : https://www.w3schools.com/js/js_htmldom.asp>




Python 은 명시적인 Call by Reference 혹은 Call by Value 라는 개념이 존재하지 않는다.


Python의 function에 argument 를 전달할 때, 이는 Manual 에 따르면 call by assignment 라는 방식으로 전달된다. 


이는 Parameter로 전달받는 객체에 따라 객체의 참조방식이 결정된다는 의미로, 공식 문서에 따르면 Mutable Object 는 Call by reference의 형태로 참조형으로 변수를 다루며 Immutable Object는 Call by value 의 형태로 변수를 핸들링한다.


- Mutable Object : list, dict, set 등의 객체


- Immutable Object : str, int, tuple 등의 객체


가령 다음과 같은 코드는 전달되는 객체에 따라 파이썬이 변수를 어떻게 핸들링하는 지를 간단하게 보여준다.




출력 결과는 다음과 같다. 

foo 함수의 경우 int 형을 핸들링하기 때문에 call by value 로 새로 변수를 할당해서 처리하는 모습을 보여주고 list 를 입력받은 foo2 함수의 경우 call by reference 로 전달 받은 객체를 직접 변경하는 모습을 볼 수 있다.




'Programming Language > Python' 카테고리의 다른 글

Python 의 Package 와 __all__ 키워드  (0) 2018.09.25
Python 2 버전과 3 버전의 차이  (0) 2018.08.04



DNS란 웹사이트의 주소 이름을 IP Address 로 매핑시켜 기억할 수 있는 도메인으로 사이트에 접근할 수 있게 해주는 인터넷의 핵심 요소이다. 다음은 verisign 에서 제공하는 How DNS works 의 설명 내용이다.


(1) 쿼리 전송 : 브라우저가 유저로부터 입력받은 도메인 이름을 이용해 쿼리를 날린다. 쿼리는 ISP 나 무선 통신업체가 운영하는 DNS Resolver 서버에 도달하여 도메인네임을 IP로 바꾸기 위해 어떤 DNS 서버에 요청해야 하는지를 질의하게 된다.


(2) 루트 서버 : DNS Resolver 가 상호작용하는 첫번째 DNS 서버를 루트 서버라 하며, 루트 서버는 .com 과 같은 최상위 도메인에 대한 DNS 정보를 알고 있다. 이 루트서버는 쿼리를 요청한 지점에서 멀지 않은 도메인으로 전송시켜 준다.


(3) TLD 서버 : TLD 서버는 최상위 도메인 내에 차상위 도메인에 대한 주소 정보를 저장하는 서버로 쿼리가 TLD 서버에 도착하면 TLD 서버가 도메인 네임에 대한 IP주소를 답변한다.


(4) DNS : 서버를 거친 쿼리문이 도메인 네임에 대한 IP 주소를 DNS Resolver 로 전송한다.


(5) Web site : DNS Resolver 는 문의한 도메인 네임에 해당하는 IP주소를 브라우저에 알려주고 브라우저는 IP 주소로 접근하여 컨텐츠를 가져온다.



DNS 정보를 조회하는 Flow 는 다음과 같다.


1. 클라이언트(웹 브라우저)가 DNS 를 조회하는 쿼리를 발생시킨다.


2. 발생한 요청은 Local DNS 서버로 이동한다. 이 때의 요청은 다음과 같은 DNS Resolver 들을 거치게 된다.

 - 클라이언트에 저장된 로컬 네임 서버 DNS 캐시

 - Internet Service Provider(ISP) 가 제공하는 네임 서버 DNS 캐시

캐시에서 값이 있으면 해당 IP 정보를 응답값으로 전달한다.

여기서 ISP 가 제공하는 네임서버 DNS 캐시가 의무적으로 존재하지는 않는다. ISP 에 따라 존재하는 경우도 있고 없는 경우도 있다. 없다면 요청은 Local PC 의 설정만 조회해보고 바로 3번 이후로 넘어간다.


3. DNS 주소에 대한 쿼리가 Root 서버로 향한다. Root 서버는 .com 과 같은 최상위 도메인에 대한 DNS 정보를 포함한다.

도메인 정보에 따라서 일반 최상위 도메인(.com, .net, .org ...) 이나 국가 최상위 도메인(.kr, .jp ...) 으로 분류되어 각각에 알맞는 TLD 서버의 정보를 전달받는다.


4. TLD 서버 정보를 이용해 TLD 서버에서 해당 도메인 주소(tistory.com)에 대한 관리 주체 서버로 연결해준다. 


5. 도메인 주소를 관리하는 DNS 서버에서 해당 도메인 주소에 대한 IP 를 반환해준다.


6. 반환된 IP 가 응답으로 전달되면서 Local DNS 에 캐싱된다.


7. Local DNS 를 통해 클라이언트(웹 브라우저) 는 IP 정보를 얻게되고 해당 서버로 접근하게 된다.



DNS 를 관리하는 구조는 트리형태로 되어있으며 클라이언트가 트리 형태의 DNS 구조에서 쿼리를 통해 결과값을 받아가는 과정이 재귀적(Recursive)이라 하여, Resolver 를 Resulsive Resolver, Query 를 Recursive Query 등으로 부르기도 한다.



+ Recent posts