앞의 글에서 자바스크립트에는 타입이 다음과 같은 것들이 존재한다고 설명했다.

undefined, null, boolean, number, string, object, function, array, Date, RegExp

 

  • 자바스크립트에서 타입은 선언할 때가 아니고, 실행타임에 결정된다. (실제 그 타입의 변수가 실행되는 경우)
  • 함수 안에서의 파라미터나 변수는 실행될 때 그 타입이 결정된다

위 성질은 자바스크립트가 컴파일을 총 2번 하기 때문에 일어난다.

자세한 컴파일 과정은 다음과 같다.

  1. 함수, 변수들 호이스팅하며 선언한다.
  2. 선언된 객체들에게 값을 할당한다.

몇몇 타입들은 typeof 를 이용해 타입을 검사할 수 있다.

  • undefined : 변수가 정의되지 않거나 값이 없을 때
  • number : 데이터 타입이 수일 때
  • string : 데이터 타입이 문자열일 때
  • boolean : 데이터 타입이 불리언일 때
  • object : 데이터 타입이 함수, 배열 등 객체일 때
  • function : 변수의 값이 함수일 때
  • symbol : 데이터 타입이 심볼일 때

심볼 타입

위 타입들 중 대부분은 이해가 가능하지만, 심볼 타입은 도데체 무엇일까?

 

심볼은 es6에서 추가된 새로운 타입이다.

유일한 객체의 프로퍼티 키(property key)를 만들기 위해 사용한다.

 

key로 사용했을 때, 다른것들과 절대 충돌이 나지 않는 점을 이용한다.

 

ES6의 class에서 private한 객체를 만들 때 사용할 수 있다.

const Count = (() => {
  const count = Symbol('COUNT');
  class Count {
    constructor() {
      this[count] = 0;
    }
    getScore() { return this[count]; }
    setScore(score) { this[count] = score; }
  }
  return Count;
})();

const test = new Count();

위와 같이 선언했을 때, test.count에 접근할 수 없다.

 

하지만 이 방법으로 완전히 private한 요소를 만들 수 있는 것은 아니다.

다음과 같은 방법으로 test.count에 접근할 수 있다.

const testSymbol = Object.getOwnPropertySymbols(test)[0];
test[testSymbol] = 20 // this is count

원시 타입 vs 참조 타입

위 타입을 기본형(원시 타입)참조형(참조 타입)으로 나눌 수 있는데 이는 C언어에서 call by value, call by reference의 차이로 이해하면 쉽다.

 

원시 타입 (Primitive) : call by value

  • Number
  • String
  • Boolean
  • null
  • undefined

참조 타입 (Reference) : call by reference

  • Object
  • Array
  • Function
  • RegExp

두 타입의 차이를 알아보기 위해 다음의 예제를 실행해보자

// 원시 타입의 경우
let a = 3;
let b = a;

b = 2;	// 2
a	// 3

// 참조 타입의 경우
let objA = { value : 3 }
let objB = objA

objB.value = 2;
objA	// {value: 2}

objA는 객체 (Object)타입 이므로 objB = objA 를 수행했을 때, objB는 objA의 주소를 가진다.

 

그렇다면 참조 타입에서 '값 복사'를 하려면 어떻게 해야 할까?? (주소를 복사하지 않음)

 

Object.assign()을 이용한다. (객체의 depth가 1단계만일때)

function cloneObject(obj) { 
  return Object.assign({}, obj); 
}

위 함수의 경우 obj의 depth가 1단계 만이라면, (최대 depth가 1단계) 객체를 값 복사 할 수 있다!

 

다음과 같은 객체의 경우 유효하다.

 

// 자식들이 원시 타입이다.
let obj = {
  child1 : 10,
  child2 : "hello",
  child3 : undefined,
  child4 : null
}

허나 Object.assign의 경우 자식이 참조 타입인 경우 자식에 대한 복사는 다시 참조 복사를 해버리는 문제가 발생한다.

이러한 복사를 얕은 복사 라고 한다.

 

이 경우 다음 방법을 이용한다.

 

JSON.parse() & JSON.stringify()을 이용한다.

JSON.stringify(obj)는 obj의 형태를 문자열로 치환해준다.

let obj = {
  child1 : 10,
  child2 : "hello",
  child3 : undefined,
  child4 : null
}

JSON.stringify(obj) // "{"child1":10,"child2":"hello","child4":null}"

JSON.parse("string")의 경우 "string"을 객체로 복구한다.

let str = JSON.stringify(obj)
JSON.parse(str) // {child1: 10, child2: "hello", child4: null}

두 메소드를 합치면 객체를 깊은 복사 할 수 있다.

허나 이 방법은 매우 느리므로 정말 깊은 복사를 해야 할 때만 이용하도록 하자.

(문자열 치환후 다시 객체로 복구하므로...)

 

함수의 반환 타입은 무엇인가?

function printName(firstname) {
    var myname = "jisu";
    var result = myname + " " +  firstname;
}

위 코드를 실행했을 때, printName의 반환값은 무엇일까?

답은 undefined이다.

 

자바스크립트 함수는 반드시 return값이 존재하며, 없을 때는 기본 반환값인 'undefined'를 반환한다.

 


실제 개발을 해보다 보면, 자바스크립트의 타입 때문에 생기는 에러가 자주 발생한다.

다음 객체를 비동기로 받아 사용한다고 생각해보자.

let res = {
  hello : "world"
}

만약 API에서 res를 제대로 받아온다면 res.hello를 실행했을 때 문제가 없을 것이다.

그러나 res를 제대로 받아오지 못한다면 (res가 undefined)라면 res.hello는 undefined일 것이다.

 

이 경우 res.hello는 원시타입이므로 큰 문제가 발생하지 않을 수 있지만 함수등의 경우는 어떨까?

 

let res = {
  func : () => { // do something }
}

위 경우 res가 undefined인 상태에서 res.func()를 실행하려고 한다면 에러가 발생한다.

 

이런 타입과 관련한 에러를 직접, 실행하던 도중에 발견한다면 찾는데 시간이 오래 걸린다.

 

자바스크립트 타입에 관련한 에러를 컴파일시 발견할 수 있게 만든것이 타입스크립트다.

 

타입스크립트

 

https://www.typescriptlang.org/

 

TypeScript - JavaScript that scales.

State of the art JavaScript TypeScript offers support for the latest and evolving JavaScript features, including those from ECMAScript 2015 and future proposals, like async functions and decorators, to help build robust components. These features are avail

www.typescriptlang.org

 

node를 사용하고 있다면 다음 명령을 통해 설치할 수 있다.

 

npm install -g typescript

 

타입스크립트와 자바스크립트는 다음과 같은 차이가 존재한다.

 

var a = 1; // number
var b = '2'; // string
console.log(a+b) // "12"
var a:number = 1; 
var b:string = '2'; 
console.log(a+b)

첫 번째 코드는 javascript 코드이다. a와 b가 다른타입인데 + 연산을 수행하고, 이 연산이 문자열의 합을 출력한다.

이는 생각보다 자주 발생할 수 있는 문제인데, 특히 숫자의 경우 문자열로 처리한 뒤 저장할 수 있기 때문이다.

 

타입스크립트에서는 a와 b의 타입을 지정해 줄 수 있으므로 연산 전에 에러를 발생시킨다.

 


 

대부분의 초보자가 자바스크립트를 접할 때 가장 난해하게 여기는 것이 바로 타입이라고 생각한다.

특히 처음 배울 때 변수에 어떤 값이던 할당할 수 있다는 점은 복잡한 프로그램을 개발하기 어렵게 한다.

 

실제 로직을 구현했다 하더라고 타입때문에 에러가 발생하는 경우 이를 파악하기 힘들 수도 있다.

 

만약 자바스크립트에 한계를 느끼기 시작했다면, 타입스크립트로 옮겨갈 기회이다!

자바스크립트 언어는 현재 매우 많은곳에 쓰이고 있다.

 

웹 개발자로서 백엔드, 프론트엔트를 가리지 않고 자바스크립트 만으로 개발을 할 수 있을 정도다.

(예를 들면 node express server + react)

 

특히 브라우저에서 자체적으로 자바스크립트를 지원해주기도 하고,

react나 vuejs등을 사용하는 경우는 전부 자바스크립트이다.

 

그리고 자바스크립트에서 '타입'을 지정해주는 타입스크립트 언어까지 나온 상태이니

자바스크립트만 제대로 할 줄 알아도 큰 이점이 된다.

 

글을 작성하는 시점에서 (2020.02.17) 티오베(TIOBE)에서 자바스크립트 언어의 순위를 확인해보니 7위를 기록하고 있었다.

 

그러면 다른언어와 구별되는 자바스크립트의 특징과, 사용법을 알아보자.

 

강의 링크

 

[LECTURE] 1) 자바스크립트 변수-연산자-타입 : edwith

들어가기 전에 컴파일 단계가 없는 자바스크립트의 type(형)은 실행단계에서 타입이 결정됩니다. 변수선언은 어떻게 정의하고, 자바스크립트의 타입은 어떤 것들이 있는지 확인해봅니다.... - 부스트코스

www.edwith.org


hello world

console.log("hello world");	// 이것도 되고
console.log('hello world')	// 이것도 된다

자바스크립트를 처음 배울 때 특이했던 점 중 하나는 바로 ; (세미콜론) 이 필요가 없다는 것이다.

 

그리고 C언어처럼 변수의 형태를 지정해 줄 필요가 없다. (var, let, const 는 scope를 설정)

 

자바스크립트 언어를 직접 확인해보고 싶은가??

 

크롬을 사용한다면 F12 (개발자 도구)를 열고 console 탭으로 진입하자!

 

개발자 도구에서 자바스크립트를 사용해 볼 수 있다.

크롬이 아니더라도 파이어폭스, 인터넷 익스플로러, 엣지 브라우저 등등 대부분의 브라우저에서 자바스크립트를 이용해 볼 수 있다.

 

만약 브라우저가 아닌 터미널에서 코드를 실행시키고 싶다면??

 

Node.js를 설치하는 것을 추천한다!

 

https://nodejs.org/ko/

 

Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

Node.js가 설치되었는지 확인하고 싶다면 터미널에서 다음을 실행해보자

node -v

제대로 설치되었다면 버전정보가 나타난다.

설치가 완료되었다면 실행하고 싶은 자바스크립트 코드를 작성한다.

파일명을 a.js라고 했을 때 다음과 같이 실행하자

 

// a.js
console.log("hello world");

a.js 파일이 존재하는 위치에서 node a.js를 실행하면 a.js가 실행횐다.

자바스크립트 문법

다음 코드를 살펴보자

var a = 2;
var a = "aaa";
var a = 'aaa';
var a = true;
var a = [];
var a = {};
var a = undefined;

a는 여러번 선언되었고, 각 경우마다 데이터 타입이 다르다.

그러나 이는 전혀 문제를 일으키지 않는다.

 

사실 자바스크립트를 이루고 있는 모든것이 객체이다.

 

일단 모든 것이 객체이기 때문에 형태가 달라질 수 있다고 생각하자.

 

다음 코드를 실행해보자

 

var a;
console.log(a);	// undefined

도데체 이 undefined는 무엇인가??

 

undefined는 값이 할당되지 않은 상태이다. 이는 null과 다르다

 

undefined === null	// false
undefined == null	// true

직접 테스트해보자

위 예제는 자바스크립트에서 == 연산대신 === 연산을 사용해야 하는 중요한 이유 중 하나이다.

 

== 는 두 객체의 '값'이 같은지 확인하는 연산이다.

=== 는 두 객체의 '값'과 '타입'이 같은지 확인하는 연산이다.

 

typeof null		// "object"
typeof undefined	//"undefined"

그러면 자바스크립트에는 어떤 타입들이 존재할까?

 

  • undefined
  • null
  • boolean
  • number
  • string
  • object
  • function
  • array
  • Date
  • RegExp

여기까지 '대략 이것이 자바스크립트' 이다라는 내용의 글이였다.

다음에는 자바스크립트 type들의 성질에 대하여 알아보자.

 

이 포스트는 다음 강의의 내용을 포함하고 있습니다 링크

목표

  • Request 객체 & Response 객체에 대해 이해한다.

 

 


 

API 서버를 구성하다보면 reqest와 response 객체를 자주 접하게 된다.

 

주로 Node.js의 express.js로 서버를 구축하면, API 요청을 응답할 때 req, res로 줄여서 사용하곤 한다.

/* GET home page. */
router.get("/", function(req, res, next) {
  console.log(res);    // 객체 정보가 정말 많으므로 콘솔로 바로 확인하는것은 추천하지 않는다...
  console.log(req);
  res.render("index", { title: "Express" });
});

다음 코드는 express에서 get요청을 받았을 때 다음을 수행한다.

  • console.log로 각 객체를 출력해준다.
  • index페이지를 렌더링 해준다. (단순히 라우팅의 역할을 수행한다)

response 객체를 출력해보면... 다음과 같다

ServerResponse {
  _events:
   [Object: null prototype] { finish: [Array], end: [Function: onevent] },
  _eventsCount: 2,
  _maxListeners: undefined,
  output: [],
  outputEncodings: [],
  outputCallbacks: [],
  outputSize: 0,
  writable: true,
  _last: false,
  chunkedEncoding: false,
  shouldKeepAlive: true,
  useChunkedEncodingByDefault: true,
  sendDate: true,
  _removedConnection: false,
  _removedContLen: false,
  _removedTE: false,
  _contentLength: null,
  _hasBody: true,
  _trailer: '',
  finished: false,
  _headerSent: false,
  socket:
   Socket {
     connecting: false,
     _hadError: false,
     _handle: [TCP],
     _parent: null,
     _host: null,
     _readableState: [ReadableState],
     readable: true,
     _events: [Object],
     _eventsCount: 8,
     _maxListeners: undefined,
     _writableState: [WritableState],
     writable: true,
     allowHalfOpen: true,
     _sockname: null,
     _pendingData: null,
     _pendingEncoding: '',
     server: [Server],
     _server: [Server],
     timeout: 120000,
     parser: [HTTPParser],
     on: [Function: socketOnWrap],
     _paused: false,
     _httpMessage: [Circular],
     _peername: [Object],
     [Symbol(asyncId)]: 7,
     [Symbol(lastWriteQueueSize)]: 0,
     [Symbol(timeout)]: [Timeout],
     [Symbol(kBytesRead)]: 0,
     [Symbol(kBytesWritten)]: 0 },
  connection:
   Socket {
     connecting: false,
     _hadError: false,
     _handle: [TCP],
     _parent: null,
     _host: null,
     _readableState: [ReadableState],
     readable: true,
     _events: [Object],
     _eventsCount: 8,
     _maxListeners: undefined,
     _writableState: [WritableState],
     writable: true,
     allowHalfOpen: true,
     _sockname: null,
     _pendingData: null,
     _pendingEncoding: '',
     server: [Server],
     _server: [Server],
     timeout: 120000,
     parser: [HTTPParser],
     on: [Function: socketOnWrap],
     _paused: false,
     _httpMessage: [Circular],
     _peername: [Object],
     [Symbol(asyncId)]: 7,
     [Symbol(lastWriteQueueSize)]: 0,
     [Symbol(timeout)]: [Timeout],
     [Symbol(kBytesRead)]: 0,
     [Symbol(kBytesWritten)]: 0 },
  _header: null,
  _onPendingData: [Function: bound updateOutgoingData],
  _sent100: false,
  _expect_continue: false,
  req:
   IncomingMessage {
     _readableState: [ReadableState],
     readable: true,
     _events: [Object],
     _eventsCount: 1,
     _maxListeners: undefined,
     socket: [Socket],
     connection: [Socket],
     httpVersionMajor: 1,
     httpVersionMinor: 1,
     httpVersion: '1.1',
     complete: true,
     headers: [Object],
     rawHeaders: [Array],
     trailers: {},
     rawTrailers: [],
     aborted: false,
     upgrade: false,
     url: '/',
     method: 'GET',
     statusCode: null,
     statusMessage: null,
     client: [Socket],
     _consuming: false,
     _dumped: false,
     next: [Function: next],
     baseUrl: '',
     originalUrl: '/',
     _parsedUrl: [Url],
     params: {},
     query: {},
     res: [Circular],
     _startAt: [Array],
     _startTime: 2020-02-09T07:52:51.231Z,
     _remoteAddress: '::1',
     body: {},
     secret: undefined,
     cookies: [Object: null prototype] {},
     signedCookies: [Object: null prototype] {},
     _parsedOriginalUrl: [Url],
     route: [Route] },
  locals: [Object: null prototype] {},
  _startAt: undefined,
  _startTime: undefined,
  writeHead: [Function: writeHead],
  __onFinished: { [Function: listener] queue: [Array] },
  [Symbol(isCorked)]: false,
  [Symbol(outHeadersKey)]: [Object: null prototype] { 'x-powered-by': [Array] } }

엄청 많은 정보들이 포함되어 있는 것을 확인할 수 있다!

이 객체에 대해 전부 설명하기 보다는 필요한 부분만 설명하고자 한다.

Request, Response 객체?

request객체는 클라이언트(브라우저)를 통해 서버에 어떤 정보를 요청하는 정보를 담고있는 객체이다.

response객체는 서버가 클라이언트의 요청에 응답하는 정보를 담고 있는 객체이다.

 

일반적으로 요청은 다음과 같은 순서로 이루어진다.

  1. 클라이언트에서 서버로 요청
  2. 서버는 클라이언트에 응답

requset > response 로 간략화 할 수도 있을것이다.

클라이언트와 서버의 응답

여기서 WAS는 Web Application Server를 의미하므로 편의상 서버라고 하자.

 

이 그림에선 요청와 응답 객체에 어떤 정보를 포함하고 있는지 나와있지 않다.

과연 어떤 정보를 포함하고 있을까?

 

캡틴판교님의 글에 나와있는 그림을 확인해보자

 

출처 https://joshua1988.github.io/web-development/http-part1/

 

각 객체가 가지고 있는 정보

더 자세한 정보는 MDN 문서에서 확인할 수 있다. 링크

 

HTTP 메시지

HTTP 메시지는 서버와 클라이언트 간에 데이터가 교환되는 방식입니다. 메시지 타입은 두 가지가 있습니다. 요청(request)은 클라이언트가 서버로 전달해서 서버의 액션이 일어나게끔 하는 메시지고, 응답(response)은 요청에 대한 서버의 답변입니다.

developer.mozilla.org

Request

시작 줄

  • 요청 메소드 타입 (GET, POST, PUT, DELETE... 그리고 잘 사용하지 않는 HEAD, OPTION)
  • URL 정보

헤더

  • HTTP 헤더

본문 (옵션)

  • 패러미터 (전달받은 데이터)

Response

상태 줄

  • 프로토콜 버전
  • 상태 코드
  • 상태 텍스트

헤더

  • HTTP 헤더

본문 (옵션)

response객체의 헤더 예시

Java Servlet으로 실습

다음 링크의 코드를 이용해 실습을 해보자 링크

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <h1>Request API</h1>
        <ul>
            <li>접속자 IP주소: <%= request.getRemoteAddr() %></li>
            <li>서버 이름: <%= request.getServerName() %></li>
            <li>요청 방식: <%= request.getMethod() %></li>
            <li>프로토콜: <%= request.getProtocol() %></li>
            <li>요청 URL: <%= request.getRequestURL() %></li>
        </ul>
    </body>
</html>

다음 jsp 파일을 보면 request 객체의 값들을 다음 메소드들을 통해서 가져옴을 알 수 있다.

 

다음 링크에서 Servlet에서 제공하는 Requset, Response의 API들을 확인할 수 있다.

HttpServletRequest

 

HttpServletRequest (Servlet 3.0 API Documentation - Apache Tomcat 7.0.99)

Reconstructs the URL the client used to make the request. The returned URL contains a protocol, server name, port number, and server path, but it does not include query string parameters. Because this method returns a StringBuffer, not a string, you can mo

tomcat.apache.org

HttpServletResponse

 

HttpServletResponse (Servlet 3.0 API Documentation - Apache Tomcat 7.0.99)

Deprecated.  As of version 2.1, due to ambiguous meaning of the message parameter. To set a status code use setStatus(int), to send an error with a description use sendError(int, String).

tomcat.apache.org

 

request객체에 parameter가 같이 오는 경우 어떻게 이용할 수 있을까??

 

다음와 코드를 살펴보자

package examples;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class ParameterServlet
 */
@WebServlet("/param")
public class ParameterServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public ParameterServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head><title>form</title></head>");
        out.println("<body>");

        String name = request.getParameter("name");        // "name"이란 key를 가진 parameter의 값을 가져오는 부분
        String age = request.getParameter("age");

        out.println("name : " + name + "<br>");
        out.println("age : " +age + "<br>");

        out.println("</body>");
        out.println("</html>");
    }

}

다음 코드에서 이 부분을 유심히 살펴보자

String name = request.getParameter("name");        // "name"이란 key를 가진 parameter의 값을 가져오는 부분

이 방법으로 request 객체의 패러미터를 이용할 수 있다.

 


 

강의의 댓글 중 다음과 같은 질문이 눈에 띄었다.

 

 

궁금한게 있습니다.

ParameterServlet.java 파일에

request의 모든 헤더를 확인하는 코드도 추가해봤습니다(이전 동영상 실습)

request에 getParameter() 함수도 있으니까 파라미터 정보도 담고있는지 확인하고 싶어서요

그런데 모든 헤더 정보를 출력하는 부분에 파라미터 정보는 왜 출력되지 않는걸까요?

아시는분 답글 부탁드릴게요~! 감사합니다

 

질문자가 첨부한 이미지 파일

왜 헤더 정보를 출력할 때 파라미터를 확인할 수 없을까?

 

답은 본문의 request 객체가 포함하고 있는 정보를 설명한 부분에 있다.

 

파라미터 정보는 애초에 헤더에 들어가지 않는다!

 

크롬 네트워크 탭으로 확인해보자

 

크롬 개발자도구의 Network 탭에서 다양한 정보를 확인할 수 있다.

 

 

웹 서비스를 배포 했을 때, 일반적으로 사용자는 브라우저를 통해서 배포한 웹 페이지에 접근한다.

 

이 과정을 간략히 요약해보자.

 

브라우저가 80번 포트로 웹 서버 (WAS)에 접근해 html, css, javascript 파일을 읽어들인다.

(읽어 들일 때 한번에 다 받는것이 아닌 여러번에 걸쳐 나누어 받는다)

html 에서는 DOM 구조를 설정하고 (뼈대를 구성한다)
css 에서 스타일 속성을 부여하며
javascript 에서 동작을 부여한다.

 

우리가 매일 사용하고 있는 브라우저는 사실 많은 동작을 수행하고 있다.

 

웹 페이지를 보여주기 위해 브라우저는 노력하고있다.

웹 브라우저의 구성 요소

이러한 브라우저의 구성 요소에는 아래와 같은 것들이 존재한다.

  1. 사용자 인터페이스
  2. 브라우저 엔진
  3. 렌더링 엔진
  4. 통신
  5. UI 백엔드
  6. 자바스크립트 해석기
  7. 자료 저장소

[출처](https://d2.naver.com/helloworld/59361)

여기서 앞에서 설명한 방식대로 (html, css, javascript) view를 렌더링하는 '렌더링 엔진'에 대해서 알아보자.

렌더링 엔진의 동작 과정

렌더링 엔진의 동작 순서는 다음과 같다.

  1. DOM 트리 구축을 위한 HTML 파싱

  2. 렌더 트리 구축

  3. 렌더 트리 배치

  4. 렌더 트리 그리기

여기서 1번 DOM 트리 구축을 위한 HTML 파싱 부분을 자세히 살펴보자.

[출처](https://ko.wikipedia.org/wiki/%EB%AC%B8%EC%84%9C_%EA%B0%9D%EC%B2%B4_%EB%AA%A8%EB%8D%B8)

DOM 은 Document Object Model의 약자이다.

 

위키피디아에서는 이를 문서 객체 모델이라고 번역했다.

 

위 그림을 보면 html 문서와 많이 비슷해 보이지 않는가??

바로 들여쓰기로 정렬된 html 문서의 구조와 트리구조가 비슷하다는 것을 알 수 있다.

 

렌더링 트리의 주요 동작 중 하나는, html 문서를 파싱해 트리구로조 변환한다는 것이다!

 

물론 html 만으로 구성된 트리는 뼈대만 있는 구조이다.

스타일을 적용하기 위해 css또한 css 파서를 이용해 파싱 후 CSS 트리를 생성한다.

렌더링 엔진은 최종적으로 DOM 트리 CSS 트리를 합쳐 렌더링 트리를 만든다.

 

여기서 재미있는 점은, html 문서에서 태그가 제대로 닫히지 않는 문제가 발생하더라도,

오류가 발생하지 않는다는 것이다!

 

body 태그가 닫히지 않았지만, hello world는 정상적으로 출력된다.

 

브라우저 동작을 더 빠르게!

작성한 코드 (html css js 등을 편의상 코드라 부르겠다)를 브라우저에 더 빨리 렌더링 할 수 있는 방법은 무엇일까??

 

이를 웹 사이트 최적화 라고도 하며, 자잘한 팁들이 있어 소개하고자 한다.

css와 javascript의 위치

html 파일에서 css파일과 javascript 파일을 읽는 코드의 위치를 다음과 같이 해보자.

css : 상단

javascript: 하단

 

css파일의 경우 렌더링 트리를 구성하는 데 중요한 역할을 하기 때문에, DOM 트리가 만들어질 때 이미 생성되어 있다면 바로 렌더링 할 수 있을것이다.

 

브라우저는 파일을 다운로드 할 때까지 DOM 트리 구성을 멈추므로, html 문서 중간에 javascript 파일이 존재한다면 DOM 트리를 생성하는 것을 멈출 것이다.

 

이미지 잘라서 사용하기

브라우저는 한번에 읽어들일 수 있는 이미지의 수가 제한적이다 (렌더링 시 다운로드 하는 이미지)

 

따라서 페이지 UI에 필요한 이미지의 경우, 파일 하나로 다운을 받고 필요한 부분별로 쪼개 쓰는 방식을 사용한다면 초기 로딩시 읽어들여야 하는 이미지의 수를 줄일 수 있다.

네이버에서 사용하는 UI에 대한 이미지

 

 


브라우저는 다양한 기능을 수행한다. 

그리고 브라우저 렌더링을 최적화 하기 위해 다양한 기술들이 존재하고 있다.

 

리액트의 경우 하위 컴포넌트가 변경되었을 때 전체를 다시 render하지 않도록 하기 위해 Virtual DOM (가상 DOM) 기술을 이용하고 있다.

 

그만큼 브라우저의 렌더링이 시스템 자원을 많이 잡아 먹는다는것에 대한 반증일 수 도 있다.

 

 

다음 질문에 답해보자

 

Q : 우리가 흔히 브라우저 탐색을 할 때 스크롤을 하거나, 어떤 것을 클릭하면서 화면의 위치를 바꿀 때, 브라우저는 어떻게 다시 화면을 그릴까요?

A : 처음 브라우저를 로딩할 때 생성된 렌더링 트리를 메모리에 캐싱한다. 스크롤 등의 작업을 통해 화면의 위치가 바뀌작은변화가 일어난다면 repainting의 과정만 거쳐 시스템 자원을 아낄 수 있다.

 

 

2018년 즈음에 일본에서 유학하면서 들었던 web 과정 (backend가 java spring) 을 복습하려고 한다.

그 때 수강하면서 제대로 이해하지 못한 내용이 많기 (거의 전부) 때문이다.

부스트 캠프 2019 과정을 참가하면서 node.js를 공부하며 기본을 잡았으니 이번에는 이해하기 편할것이라고 생각한다...

 

아마도?

 


포트

이름 프로토콜 포트 기능
WWW HTTP 80 웹 서비스
Email SMTP/POP3/IMAP 25/110/114 이메일 서비스
FTP FTP 21 파일 전송 서비스
DNS TCP/UDP 53 네임 서비스
NEWS NNTP 119 인터넷 뉴스 서비스

위의 표는 알아두는 편이 좋다. 적어도 HTTP 프로토콜이 80번 포트를 사용하는건 중요하다!

 

AWS 서버에 배포하면서 80번 포트를 뚫어주면 URL (도메인 호스팅을 사용하지 않는다면 IP주소) 로 접근했을 때 배포한 페이지가 바로 나타난다.

 

포트 관련해서 할 이야기는 많이 있다.

 

주요 데이터베이스 포트

  • MySQL (MariaDB) : 3306
  • MongoDB : 27017

이 외에도 VM (가상머신)으로 다른 운영체제를 가상화 한 경우에 포트포워딩 할 때 포트를 지정하기 등등...

 

이 부분은 기회가 되면 이야기 하고 싶다.

 

HTTP

하이퍼 테스트 트랜스퍼 프로토콜 (Hypertext Transfer Protocol)

최신 버전은 HTTP/2 (ver 2)이다. 주로 많이 사용하는 버전은 ver 1.1

 

'Client' 가 'Server'에 요청을 보내면, 'Server'가 'Client'에게 응답을 보내는 방식이다.

 

이 부분이 가장 중요한데, 'Server'가 'Client'에게 먼저 요청을 보낼 수가 없기 때문이다.

이런 이유 중 한 가지는, 요청>응답 이후 연결을 끊기 때문이다.

 

연결을 끊는 것의 장점은, 연결을 계속 유지할 필요가 없기 때문에 더 많은 사용자들의 요청을 해결할 수 있다.

 

그렇다면 'Server'가 'Client'에게 먼저 요청을 보내려면 어떻게 해야 할까?

 

'Socket'이 답이 될 수 있다. 'Socket'은 연결을 계속 유지하기 때문이다.

 

그리고 'Socket'의 단점은, HTTP의 장점과 정확히 상반된다. (연결을 끊는지, 유지하는지에 대한 차이)

 

HTTP의 sequence diagram

HTTP를 수행할 때, request 객체와 response 객체를 자세히 보면 위 정보들이 들어있다.

 

요청 메시지의 요청 메소드에 주의하자. 특히 Get 과 Post를 구분하는것은 정말 중요하다!

차후 Restful API 내용을 포스팅 할 때 자세히 설명하려고 한다. (저도 Restful을 거의 지키지 않습니다...)

 

그리고 응답에 숫자로 된 번호가 있는것에 유의하자. 각 숫자들을 응답의 상태를 의미하는데,

대표적으로 404 (not found) 등이 존재한다.

 

그리고 개발자들의 숙명인 500번 에러...

Cookie

HTTP 연결은 응답이 완료되면 연결이 끊는다. 이 과정에서 클라이언트의 이전 상황을 알 수 가 없다.

이를 무상태 (Stateless)라고 한다.

 

그렇다면... 요청 이후 변경되는 정보를 저장하고 싶으면 어떻게 할까?

 

  • 서버에 정보를 저장한다 (데이터베이스, 메모리, 캐싱)
  • 클라이언트에 정보를 저장한다 (쿠키)

크롬 사용자의 경우 사용자 정보 제거 (Ctrl + Shift + Delete) 를 누르면 쿠키를 제거하는 체크박스가 존재한다.

보통 쿠키에 로그인 여부 등등을 저장하곤 하는데 (나는 주로 JWT를 저장했었다...)

 

이것과 관련해서 처리하기 애매한 보안 이슈들이 발생한다!

 

ex) 로그인 정보를 쿠키에 저장할지??

 

왜나하면 쿠키는 브라우저에서 까볼수 있기 때문이다! (확인 가능하다는 의미)

브라우저 콘솔 창을 열고 document.cookie를 확인해보자...

document.cookie

 

document 객체에 대해서 나중에 기회가 되면 설명하고 싶다.

 

URL

공부를 하다보면 URI 와 URL이란 단어가 존재하는 것을 확인 할 수 있다.

정말 대충 설명하면 URI = URL + URN 이다.

 

일반적으로 URL만 알아두어도...

 

https://developer.mozilla.org/ko/docs/Learn/Common_questions/What_is_a_URL

 

What is a URL?

Hypertext 와 HTTP 함께, URL 은 웹에서 중요한 개념 중 하나이다.  URL은 웹에 게시된 어떤 자원을 찾기 위해서 browsers에 의해 사용되는 메카니즘이다 .

developer.mozilla.org

MDN 문서를 확인하면 문법에 대해 더 자세히 알 수 있다.

 

바쁜 사람들을 위해 대충 설명하면

 

접근 프로토콜 :// IP또는 도메인 : 포트번호 / 문서경로 / 문서이름

 

으로 나타낼 수 있다. (포트번호는 80번을 사용하는 경우 생략가능하다)

 

부스트코스 강의에서는 포트번호 관련 내용이 빠져있어서 추가했다.

 

 


오랫만에 한 복습이였는데, 예전엔 그냥 수강했던 강의의 내용이 지금에서 보니 엄청 중요했구나 라는 것을 느꼇다...

특히 개론에 대한 설명인데도, 본질적인 부분을 꿰뚫고 있는 부분이 있었다. (HTTP의 장 단점)

 

공부는 끝이 없다...

+ Recent posts