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

 

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

 

브라우저가 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의 장 단점)

 

공부는 끝이 없다...

7699. 수지의 수지 맞는 여행

링크

난이도 : d4
정답률 : _51%

설계

자료구조

이미 방문한 알파벳은 방분하지 않으므로 visited['Z' - 'A'] 를 만들어 사용한다.

backtracking

이미 정답의 max를 구한 경우, 더이상 탐색하지 않아도 된다.

가능한 max의 경우는 입력된 지도의 알파벳 종류 + 1이다.

따라서 dfs 함수에 다음 코드를 추가해준다

if (answer > MAX) {
  return;
}

정리

내 코드 빠른 코드
18 ms 18 ms

내 코드가 제일 빠른 코드이다.

고생한 점

처음에 BFS로 풀려고 한 경우 계속 시간초과가 발생했다.

DFS로 바꾼 뒤 완전탐색을 수행했을 때 약 3초정도의 시간이 걸렸다.

코드

#include <algorithm>
#include <iostream>

using namespace std;

struct axis {
  int y, x;
};

axis moves[4] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
char map[20][20];
int answer, R, C, MAX;
bool visited['Z' - 'A'];

void clear() {
  for (int i = 0; i < 'Z' - 'A'; i++) {
    visited[i] = 0;
  }
  answer = 1, MAX = 0;
}

void dfs(axis current, int count) {
  answer = max(answer, count);

  if (answer > MAX) {
    return;
  }

  visited[map[current.y][current.x] - 'A'] = 1;

  for (int dir = 0; dir < 4; dir++) {
    axis next = current;
    next.x += moves[dir].x;
    next.y += moves[dir].y;

    if (next.y < 0 || next.y >= R || next.x < 0 || next.x >= C ||
        visited[map[next.y][next.x] - 'A']) {
      continue;
    }

    dfs(next, count + 1);
  }

  visited[map[current.y][current.x] - 'A'] = 0;
}

void solution(int test_case) {
  clear();
  cin >> R >> C;

  bool alphabets['Z' - 'A'] = {
      0,
  };

  for (int y = 0; y < R; y++) {
    for (int x = 0; x < C; x++) {
      cin >> map[y][x];
      alphabets[map[y][x] - 'A'] = 1;
    }
  }

  for (bool b : alphabets) {
    b ? MAX += 1 : MAX;
  }

  dfs(axis{0, 0}, 1);

  cout << "#" << test_case << " " << answer << "\n";
}

int main() {
  ios_base ::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);

  int T;
  cin >> T;

  for (int test_case = 1; test_case <= T; test_case++) {
    solution(test_case);
  }

  return 0;
}

'공부 > 알고리즘 문제풀이' 카테고리의 다른 글

백준.1197.최소 스패닝 트리  (0) 2021.01.24
큰 수 연산 (Big Integer)  (0) 2021.01.04
삼성 9088. 다이아몬드  (0) 2019.12.27
삼성 8458. 원점으로 집합  (0) 2019.12.03
삼성 8934. 팰린드롬 공포증  (0) 2019.12.02

9088. 다이아몬드

링크

난이도 : d4
정답률 : _41%

설계

자료구조

다이아몬드 무게의 범위가 1 ~ 10,000 이므로 10001 크기의 배열을 선언한다.

알고리즘

다이아몬드 무게 입력시 무게를 index로 하는 배열의 count를 증가시킨다.

배열을 순회하며 현재 inex ~ 현재 inex + K 까지의 수를 count하며 max를 갱신한다.

정리

내 코드 빠른 코드
29 ms 6 ms

고생한 점

문제의 이해가 어려웠다.

처음엔 다이아몬드 쌍을 구하는 줄 알고, 정답이 모두 짝수로 나오는 경우로 생각함.

그것이 아닌 묶음이므로 한정된 범위 내의 다이아몬드의 최대 갯수를 구하는 문제였다.

코드

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

using namespace std;

void solution(int test_case) {
  int answer = 0, N, K, temp, MAX = 0, MIN = 10000;
  int dias[10001] = {
      0,
  };

  cin >> N >> K;

  for (int i = 0; i < N; i++) {
    cin >> temp;
    dias[temp] += 1;
    MAX = max(MAX, temp);
    MIN = min(MIN, temp);
  }

  for (int i = MIN; i <= MAX; i++) {
    if (dias[i] == 0) {
      continue;
    }
    temp = 0;
    for (int j = i; j <= min(MAX, i + K); j++) {
      temp += dias[j];
    }
    answer = max(answer, temp);
  }

  cout << "#" << test_case << " " << answer << "\n";
}

int main() {
  ios_base ::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);

  int T;
  cin >> T;

  for (int test_case = 1; test_case <= T; test_case++) {
    solution(test_case);
  }

  return 0;
}

2019년 10월 즈음에 핵토버페스트에 참가했었다.

 

오픈소스에 기여를 하고싶은 초심자들을 위한 이벤트인데, 핵토버 페스트가 시작할 즈음 해서 여러 repository가 생성된다.

 

기존에 풀던 알고리즘 문제를 올려도 되고, 실제 존재하는 프로젝트에 기여를 한 부분이 있으면 PR을 날리면 되고, 이를 행사 주최측에서 검사해 (아마 bot이 검사하겠지?) 유효한 PR로 인정할 경우에만 count가 증가한다.

 

progress bar에 PR의 갯수가 표시된다

 

아마 4개정도 였던것 같은데 혹시 몰라서 하나 더 날렸었다.

 

아무래도 boostcamp 4기 과정에 참여하면서 진행하다 보니 의미있는 PR을 만들지는 못했었다.

 

그나마 영어로 된 README.md를 한국어로 번역해 올린 정도??

 

행사가 끝나고 PR을 채운 사람들에게 이메일로 T셔츠를 받을 주소를 적는 링크를 보내준다.

 

배송 온 티셔츠

배송 주소를 적고.... 하염없이 기다리면 (12월 초 즈음에 배송이 시작? 되는듯 하다)

거의 1달이 걸려서 T셔츠가 도착한다!

 

티셔츠는 이런 모양이다

이 외에도 자잘한 스티커들도 같이 오는데, 노트북에 붙여 놓았다.

 

노트북에 덕지덕지 붙은 스티커들...

'인생' 카테고리의 다른 글

나는 비로소 성장했습니다. (우아한테크캠프 3기)  (1) 2020.08.22
좋은 동료는 무엇인가?  (0) 2020.07.27

+ Recent posts