ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 브라우저 에서 내 도메인을 검색하면 일어나는일
    ETC 2023. 8. 29. 20:21
    test

     

    언젠가 누군가에게

    "브라우저 URL 입력창에 'www.google.com'을 검색하면 어떤 일이 일어날까요?"

     

    라는 질문이 꽤 유행하는 면접질문이라는 소식을 들었던 것 같다.

     

    처음에는 그냥 별 생각 안 하고 넘어갔었는데

     

    최근에서야 이 질문이 복잡한 웹기술에 대한 이해도를 답변받을 수 있는 좋은 질문이라는 사실을 알게 되었고

    알고 보니 정말 좋은 질문이면서 좋은 주제인 거 같아 블로깅을 한다.

     

    이글에서는 SpringBoot로 만든 서버를 서비스로 운영을 하고 있는 상태에서 브라우저로 접근하는 과정을 나열하려 한다.

     

    서버의 IP를 174.173.172.171,

    도메인을 mail.awesome.com이라고 가정하자.

     

    브라우저는 Domain에 해당하는 IP를 찾는다.

    DNS (domain name service)는 웹사이트의 domain과 특정 IP 주소를 매핑하는 데이터 베이스로

    모든 URL 은 고유한 IP 주소가 할당되어 있다.

     

    IP 주소는 브라우저로 접근하려는 웹사이트 서버를 호스팅 하는 컴퓨터이며

    예로 mail.google.com의 IP 주소는 172.217.31.5이다.

     

     

    따라서 https://174.173.172.171로 웹사이트에 접근할 수 있지만

    사람이 읽기 쉬운 주소를 위해 DNS를 사용하여 기억하기 쉬운 주소인 https://mail.awesome.com로 접근한다.

     

    따라서 브라우저는 mail.awesome.com의 요청이 174.173.172.171이라는 것을 찾아야 한다.

     

    4개의 Cache에서 IP 찾는다

    브라우저는 위에서 mail.awesome.com에 해당하는 DNS 레코드를 찾기 위해 4개의 Cache를 확인한다.

     

    1. Browser Cache를 확인한다. 브라우저는 이전에 방문한 웹사이트의 DNS 레코드를 일정 기간 동안 유지하고 있다.
    2. OS Cache를 확인한다. OS 또한 DNS Cache를 유지하기 때문에 컴퓨터 OS에 시스템 호출을 한다.
    3. Rotuer Cache를 확인한다. 컴퓨터 Cache에 남아있지 않다면 자체 DNS 레코드 Cache를 가지고 있는 Router에서 찾는다
    4. ISP Cache를 확인한다. Browser는 ISP에게 요청하여 ISP의 DNS 서버에서 IP를 찾는다

     

    Cache에서 못 찾으면 DNS 쿼리로 IP 찾는다

    DNS 쿼리는 IP를 얻기 위해 다른 여러 DNS 서버에서 IP를 검색하는 행위이다.

    IP 주소를 찾거나, 찾을 수 없다 는 오류를 받을 때까지 DNS 서버에서 DNS 서버로 반복적으로 검색을 계속하기에

    재귀 검색 이라고도 부른다.

     

     

    이때 ISP의 DNS 서버는 다른 DNS 서버로 부터 IP 주소를 찾는 책임이 있기에

    DNS recursor이라 부르며 그 외의 다른 DNS 서버를 name 서버라 부른다

     

    먼저 DNS recursor는

    미 국방부, NASA, ICANN, WIDE 등 특정 조직에서 운영하는 root name 서버에게 요청한다

     

    root name 서버는

    TLD(top-level domain)이라 부르는 최상위 도메인 인

    . com 서버로 리다이렉션 을 한다

    . com 네임서버는 awesome.com으로 리다이렉션을 한다

    awesome.com 은 자신의 DNS 레코드에서  mail.awesome.com와 일치하는 IP 주소를 찾아서 반환한다.

     

    이렇게 브라우저는 mail.awesome.com의 IP 주소인 174.173.172.171을 얻게 된다.

     

    목적지 IP로 패킷을 전달한다.

    목적지 174.173.172.171로 가기 위해 Router 은 라우팅을 한다.

    Router은 패킷의 전송 경로를 결정하기 위해 3개의 테이블을 사용하게 되는데

    이 테이블들을 활용하여 네트워크에 연결된 모든 장치의 주소를 인식하고 패킷의 전송 경로를 정할 수 있다.

     

    1. 랜테이블을 확인하여 패킷의 목적지가 같은 네트워크인지, 다른 네트워크인지 검사한다.
    2. 네트워크 테이블을 확인하여 패킷을 전달한 네트워크 주소를 찾는다
    3. 라우팅 테이블을 확인하여 적합한 경로를 찾아서 패킷을 보낸다

     

    브라우저와 서버의 TCP 연결 시작한다.

     

     

    서버와 통신을 하기 위한 연결을 시작한다.

    TCP 연결에 사용하는 3 way handshaking을 진행한다.

    1. 클라이언트는 서버에게 연결요청으로 SYN을 전송한다.
    2. 서버는 연결을 수락하고 SYN/ACK를 전송한다.
    3. 클라이언트는 ACK을 통해 응답을 확인하는 것으로 완료가 된다.

    브라우저와 서버는 TLS Hand shaking을시작한다.

     

    1. 클라이언트는 서버에게 "client hello"를 보내는 것으로 hend shaking를 시작한다.
    2. 서버는 클라이언트에게 자신을 검증할 수 있는 정보들을 보낸다
      1. "server hello" 클라이언트에게 보내는 메시지
      2. "certificate"  서버의 인증서와 서버의 공개키
      3. "server key Exchange" 인증서 안에 공개키가 없다면 이 메시지를 통해 공개키를 따로 전송(옵션)
      4. "server done" 전달할 정보 끝
    3. 클라이언트는 서버와 통신할 때 사용할 대칭키를 만들 수 있는 값인 pre-master-secret를  "client key exchange"를 통해 전달한다.
    4. 클라이언트는 이 handshake에 의해 정해진 암호 방식을 적용하겠다고 "change cipher Spec"를 통해 알린다.
    5. finished를 통해 서버에게 handshake 가 종료됨을 알린다.
    6. 서버도 이 handshake에 의해 정해진 암호 방식을 적용하겠다고 "change cipher Spec"를 통해 알린다.
    7. finished를 통해 클라이언트에게 handshake 가 종료됨을 알린다.

     

    hello!

     

    Client Hello, Server Hello라는 이름이 너무 귀엽다.

    패킷에 문자열 그대로 적혀있는 줄 알았는데

     

    client hello는 1, server hello는 2로 보내길래 좀 실망했다. 😥

     

     

    브라우저는 웹 서버에게 HTTP 요청을 보낸다.

    브라우저와 서버의 모든 연결이 끝났기에 데이터 전송을 시작한다.

    브라우저는 mail.awesome.com에 대해 GET 요청을 보내게 된다.

     

    서버는 HTTP 요청을 처리한다.

    요청을 받을 controller에게 위임한다

     

    spring의 경우 dispatcher servlet 가 http 프로토콜로 들어오는 요청을 받아

    요청을 위임할 적절한 controller(handler)를 찾아 요청을 한다.

     

    아래의 코드를 보면

    HTTP Mothod에 따라 do 메서드를 호출하고

    요청에 매핑되는 Handler을 조회한 후에

     Handler를 수행할 수 있는 adapter을 조회해서 실제 controller을 호출하는 과정을 찾을 수 있다.

     

    DispatcherServlet.java

    protected void service(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException
        {
            String method = req.getMethod();
            //생략
            if (method.equals(METHOD_GET)) doGet(req, resp);
            else if (method.equals(METHOD_HEAD)) doHead(req, resp);
            else if (method.equals(METHOD_POST)) doPost(req, resp);
            else if (method.equals(METHOD_PUT)) doPut(req, resp);
            else if (method.equals(METHOD_DELETE)) doDelete(req, resp);
            else if (method.equals(METHOD_OPTIONS)) doOptions(req,resp);
            else if (method.equals(METHOD_TRACE)) doTrace(req,resp);
            //생략
        }

     

    HandlerAdapter.java

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
        HandlerExecutionChain mappedHandler = null;
        //생략
        mappedHandler = getHandler(processedRequest);
        //생략
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        //생략
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
        //생략
    }

     

    서버는 요청에 대한 처리를 수행한다.

    @Controller @Service, @Repository 어노테이션에 의해 스프링 컨테이너에게 관리되어 있는 빈 객체들을 사용하여

    요청에 대한 비즈니스 로직을 수행한다.

     

    Controller 은 @RepouseMapping 어노테이션에 의해 Handle Mapper에게 관리되어 있다가

    http 요청의 진입점 역할을 수행한다.

     

    대표적으로  클라이언트의 요청, 응답을 수행하며 요청에 대한 처리를 비즈니스 계층에 전달한다.

     

    Service는 Controller에게 받거나 혹은 다른 Service에게 받은 비즈니스 처리 메시지를 수행한다.

    대표적으로 @Transactional  어노테이션을 사용하여 트랜잭션 경계를 담당한다.

     

    Repository는 데이터베이스 접근 역할을 한다.

    JPA를 사용하는 경우 EntityManager를 사용하고 JPA의 구현체인 Hibernate 가 JDBC를 사용하여 데이터베이스와 통신을 한다.

    이때 데이터베이스와 통신하기 위해 DB 커넥션 관리를 하게 되는데 DB 커넥션 도구로 HikariCP를 사용한다

     

    이러한 역할 분담을 통해

    클라이언트에게 받은 요청에 대한 비즈니스 처리를 수행한다.

     

     

    서버는 화면을 만들어서 전달한다.

     

    Controller 이 다른 계층에서 얻은 데이터를 가지고 만든 Model을 가지고  View Resolver와 함께

    사용자에게 보여줄 View를 만들어서 반환한다

     

     

     

     

     

     

    브라우저는 HTML을 랜더링 한다

     

    브라우저의 구조는 간단히 보면 위와 같은 구조로 이루어져 있우며

    서버로부터 받은 html, css를 파싱 하여 화면에 그리는 작업을 수행한다.

     

    1. 브라우저는 자신이 받은 HTML을 파싱 하여 DOM(Document Object Model) tree를 생성한다
    2. 브라우저는 HTML에 css 가 포함되어 있으면 CSSOM(CSS Object Model) tree를 생성한다.
    3. DOM Tree와 CSSOM Tree를 매칭시켜서 실제 화면에 그려질 Render Tree를 구성한다.
    4. Render Tree를 화면에 그리기 위한 위치, 크기 계산을 하는 Layer 단계를 수행한다.
    5. 위에서 계산된 값을 이용하여 Render Tree의 노드들을 픽셀로 변환하는 Paint 단계를 수행한다.
    6. 실제 화면에 노출시킨다.

     

     

     

     

     

    참고
    https://tecoble.techcourse.co.kr/post/2021-10-24-browser-rendering/

    https://webhostinggeeks.com/guides/dns/

    https://www.cloudflare.com/ko-kr/learning/ssl/what-happens-in-a-tls-handshake/