node.js가 무엇인지. 그리고 그 쓰임새는 어떠한지. 또한 웹서버란 무엇인지 정확히 알고 인지하기 위해서 해당 글을 작성하였습니다. 

 

 

 

1. node.js


 

Node.js는 Chrome V8 JavaScript 엔진으로 빌드된 JavaScript 런타임입니다.’ 라고 소개되어있다.

 

여기서 종종하는 오해가 Node.js를 웹서버로 생각하는 것인데 Node.js는 '런타임'이고 그 중에서 '웹서버기능'을 가지고 있을 뿐이다.

 

웹애플리케이션을 개발할 때 JavaScript를 사용하게 되는데

모든 브라우저가 JavaScript 코드를 해석하기 위한 JavaScript Engine을 내장하고 있다.

그중에서 대표적으로 Chrome은 V8, Firefox는 SpiderMonkey, Safari는 Webkit 등등이 있다.

 

Node.js는 Chrome의 V8엔진을 이용하여 브라우저에서 JavaScript를 해석하듯이 서버에서 JavaScript를 동작할 수 있도록 하는 환경(플랫폼) 이라고 생각하면 된다.

 

말그대로 환경(플랫폼)이기 때문에 Node.js 자체로는 아무것도 못한다.

그래서 서버도 직접 구현해 주어야 하는데 Node.js 자체 모듈인 http모듈을 import하여 서버를 생성해 주어야 한다.

 

ex) w3schools에 있는 http server를 만드는 예제 코드

ex2) 우리가 React에서 주로 사용하는 npm create-react-app 역시 자동으로 서버를 구현해주는 것이다.

 

 

 

 

2. 웹서버


 

웹서버 - 클라이언트의 요청에 의해 정적 파일을 제공하는 서버. Apache, NginX, IIS등의 종류가 있으며, 정적파일만 제공할 수 있다.

 

WAS(Web Application Server) - 웹 서버와 웹 컨테이너를 포함한 개념. 웹 서버에서는 정적 파일만 제공하므로, 동적으로 DB와 연결하여 여러 데이터를 활용해 Php, Jsp, Asp로 표현된 페이지를 해석할 수 있어야 하는데 이 역할을 웹 컨테이너가 제공한다

 

리버스 프록시 - 내부 어플리케이션과 외부 클라이언트 사이에 자리잡아서 클라이언트의 요청을 적절한 서버로 보내주는 역할을 한다. 많은 어플리케이션들이 자체적으로 가지지 못한 로드 밸런싱, 보안, 가속화 기능 등 기능을 갖고 있다

 

 

 

왜 웹 서버와 WAS를 분리했을까?

 

1.두개를 분리하면 웹 서버를 한번 거치기에 보안상의 이점 존재.

2. 웹 서버에서 고장난 WAS로 보내지 않고, 그 시간동안 이를 복구시킨다면 사용자는 끊김없이 서버에서 서비스를 제공받을 수 있다 (=fail over, fail back).

(톰캣에서는 웹서버와 WAS의 역할을 모두 하기도 한다.)

 

Node.js는 Apache, 톰캣과 같은 개념과 무엇이 다른가?

 

 Node.js 는 정적 파일 제공과 WAS기능(=언어 해석) 모두 담당한다. Express.js를 통해 정적 파일을 제공하고 나머지가 언어해석, DB연결등을 담당하는 큰 그림으로 볼 수 있지만 나누는 것이 크게의미가 있나 싶다. 그렇다고 Node.js를 사용하면 NginX와 같은 웹 서버를 사용하지 않는 것은 아니다. 기업에서는 NginX나 Apache등의 그동안 쌓인 기술들을 활용하여 이를 리버스 프록시 서버로 활용하여 보안상의 이점과 캐싱등을 통해 속도상의 이점을 갖는다.

 

 

# Express.js란?  node.js 프레임워크로 NodeJS를 사용하여 쉽게 서버를 구성할 수 있게 만든 클래스와 라이브러리의 집합체

 

 

#리버스 프록시

 

 

 

 

3.웹서버와 서버측 프로그래밍 언어


 

서버: 아파치, 톰캣, Node.js

 

서버 측 프로그래밍 언어: Python, PHP, JSP, Node.js(JavaScript 기반)

 

아파치나 톰캣은 Python,PHP,JSP 등과 같이 서버와 그 서버를 프로그래밍하기 위한 언어가 따로 나뉘지만 

Node.js는 JavaScript를 기반으로하여 서버와 언어의 역할을 혼자서 수행한다.

 

 

 

 

 

학습자료


정말 잘 정리된 자료. 꼭 읽어보자.

 

웹 서버와 WAS, 컨테이너의 개념 알아보기 - Easy is Perfect

웹서버와 WAS에 대한 개념이 잘 잡혀있지 않아서 구글링을 하면서 정보를 찾아보다가 좋은 포스팅이 있어서 해당 포스팅을 참조하면서 포스팅해보겠습니다. 포스팅 출처는 아래에 남기겠습니다

melonicedlatte.com

 

 

 

[Web] 웹 서버와 WAS의 차이를 쉽게 알아보자

서버 개발에 있어서 가장 기초적인 개념인 '웹 서버'와 'WAS(Web Application Servier)'의 차이점을 다뤄보려고 한다. 💡 웹 서버 사전적 정의 "웹 브라우저 클라이언트로부터 HTTP 요청을 받아들이고 HTML

codechasseur.tistory.com

 

Reference:

 

웹서버 클라이언트(생활코딩)

https://opentutorials.org/course/3084/18890

 

Node.js란?

https://medium.com/@su_bak/node-js-node-js%EB%9E%80-410ae3749c56

https://www.newiki.net/wiki/Node.js

 

웹서버와 Node.js

https://rltmqj.tistory.com/entry/Nodejs%EB%8A%94-Apache-%ED%86%B0%EC%BA%A3%EA%B3%BC-%EA%B0%99%EC%9D%80-%EA%B0%9C%EB%85%90%EA%B3%BC-%EB%AC%B4%EC%97%87%EC%9D%B4-%EB%8B%A4%EB%A5%B8%EA%B0%80

 

Node.js와 Apache,Nginx를 사용한 리버스 프록시

https://uhou.tistory.com/96

 

Express.js란?

https://velog.io/@madpotato1713/JAVASCRIPT-express%EB%9E%80

 

웹서버와 서버 측 프로그래밍 언어

https://lovestudycom.tistory.com/entry/%EC%95%84%ED%8C%8C%EC%B9%98-%ED%86%B0%EC%BA%A3-Nodejs-%EC%B0%A8%EC%9D%B4

 

 

 

 

'CS > 자료' 카테고리의 다른 글

[CS] 쿠키, 세션스토리지, 로컬스토리지  (0) 2021.12.15
정적파일, 동적파일, 미디어파일  (0) 2021.11.11
CSR , SSR에 대해 알아보자  (0) 2021.11.05
인증 & 인가  (0) 2021.06.29
콘솔, 터미널, 쉘 차이점  (0) 2021.06.20

 

 

 

1. Create React App (CRA) ← Only CSR


 

  • "Create React apps with no build configuration."
  •  
  • 아무런 초기 설정 없이도 CRA를 통해 React 기반의 SPA 사이트를 구현할 수 있게 됨.
    • (과장 조금 보태서) 터미널에 npx create-react-app my-app, npm run build 명령어 만으로 사이트 하나를 만드는 것이 가능.
    • webpack, babel 등 복잡한 세팅을 거치지 않아 React 기반 웹 프로젝트 확산에 큰 기여.
  • 그런데 얼마 간 시간이 지나면서 조금씩 이상한 점이 드러나기 시작.
    • "우리 사이트가 검색에 너무 안 걸리는걸?"
    • 원인) CRA로 build한 프로젝트는 Only CSR(Client Side Render)로 실행되기 때문.
  • CSR (Client Side Rendering)
    • 웹 페이지의 렌더링이 클라이언트(브라우저) 측에서 일어나는 것을 의미.
    • 브라우저는 최초 요청에서 html, js, css 확장자의 파일을 차례로 다운로드.
    • 최초로 불러온 html의 내용은 비어있음. (html, body 태그만 존재)
    • js 파일의 다운로드가 완료된 다음, 해당 js 파일이 dom을 빈 html 위에 그리기 시작.
    • 백엔드 호출을 최소화 할 수 있음
      • 최초 호출 때만 html, js, css를 요청
      • 이후에는 화면에서 변화가 일어나야 하는 만큼의 데이터만 요청 (ex. JSON)
    • 라우팅(새로운 페이지로 이동)을 하더라도 html 자체가 바뀌는 것이 아니라 JavaScript 차원에서 새로운 화면을 그려내는 것!

 

 

 

2. SEO (Search Engine Optimization)


 

  • 하지만 웹 크롤러 입장에서 CSR로 구성된 페이지에 접속하는 시나리오를 재구성해보자.
  • 웹 크롤러가 각 사이트를 돌아다니며 조사를 하는 상황이라고 가정.

CSR

  • 똑똑똑~
  • (끼익) ← 문이 열린다 (페이지 요청)
  • (빈 집) ← 첫 페이지
  • (조사 못하고 구글봇 퇴장)
  • (js 파일 다운로드 되며 페이지 로드) ← 뒷북
  • ⇒ 검색 노출 안 됨 

SSR

  • 똑똑똑~
  • (끼익) ← 문이 열린다 (페이지 요청)
  • (주인이 웹 크롤러를 맞이하며) "구글봇님 어서오세요. 저희 사이트는..." ← 첫 페이지
  • (조사를 마친 구글봇 퇴장)
  • ⇒ 검색 노출 됨 
  • CRA로 신나게 만들었던 페이지들이 검색 노출이 잘 안된다는 (충격적인) 사실이 알려지면서, SEO에 민감한 커머스 등의 비즈니스 영역은 대안을 찾아야 하는 상황.
    • "SPA의 장점을 살리면서 SEO도 같이 챙길 수는 없을까?"
    • ⇒ SSR (Server Side Rendering)

 

 

 

3. SSR (Server Side Rendering)


 

  • 위에서 언급했던 CSR과 SSR의 차이에서 알 수 있듯, SSR은 서버에서 첫 페이지의 렌더링을 클라이언트 측이 아닌 서버 측에서 처리해주는 방식.
  • CSR과 비교하면,
    • 1) UX 측면에서 유리
      • CSR에 비해 페이지를 구성하는 속도는 늦어질 수 있지만, 전체적으로 사용자에게 보여주는 콘텐츠 구성이 완료되는 시점은 빨라진다.
      • Code Splitting?
        • 만약 첫 페이지 구성에 불필요한 JS 파일을 받아온다면?
        • 그 번들 파일의 크기가 크다면?
        • 불필요한 내용들은 받아오지 않고 서버 단에서 첫 페이지에 필요한 정적인 부분만 처리한 뒤 JS는 나중에 필요한 부분만 필요할 때 로드할 수 있다면?
    • 2) SEO 측면에서 유리
      • 서버에서 사용자에게 보여줄 페이지를 모두 구성하여 사용자에게 보여주는 방식이기 때문에 CSR의 단점인 "첫 페이지 깡통" 상태를 극복할 수 있음.
    • 주의) 페이지를 잘못 구성할 경우 CSR에 비해 서버 부하가 커지거나 / 첫 로딩이 매우 느려질 수 있음

 

 

 

 

 

4. SPA for SSR


 

SSR for MPA, SSR for SPA...?

  • 이쯤 되면 고개가 또 다시 갸우뚱 해진다. 🤔
  • "2세대 웹의 JSP, PHP, Django Template 같은 것들 역시 SSR이 아니었나요?" ⇒ 맞습니다! ✅
  • "그러면 CSR의 한계를 극복하기 위해 웹이 2세대 기술로 돌아가고 말았나요?" ⇒ 아니에요! 🚫
  • "SPA랑 CSR이랑 같은 의미 아니었나요?" ⇒ 아니에요! 🚫
  • 우리가 지금 얘기해볼 SSR은 SSR for SPA
  • SPA(결과물) / CSR, SSR (렌더링 방법)로 나누어 이해하면 좋음.

 

 

 

5. CSR + SSR?


  • 그렇다면 SSR과 CSR을 섞어 쓸 수는 없나요
  • 사용할 수 있다!
  • 1) 첫 렌더 SSR
  • 2) 그 이후 렌더 부터는 CSR

 

 

:: 원리 & 구조

 

  • SSR은 다음과 같은 요소로 구성된다
    • node.js로 구성된 FE 서버
    • pyhton, django로 되어 있는 BE 서버

 

  • 다음과 같은 과정을 거쳐 SSR이 진행된다 (링크)
    1. 유저가 브라우저에 **/**를 입력.
    2. 미리 실행되고 있는 FE 서버가 요청을 받고 서버사이드 렌더링.
    3. 만들어진 html 을 브라우저에게 보냄.
    4. 브라우저가 응답받은 html 을 그림.
    5. html 에 기능을 부여할 **index.js**파일을 다운로드 받음. (hydration)
    6. 다운로드가 완료된 이후, go to second 링크를 클릭.
    7. **/second**로 라우팅하고 second 페이지 코드를 생성.
    8.  

:: 예제 코드

import React from "react";
import ReactDom from "react-dom";
import App from "./App";

const initialData = window.__INITIAL_DATA__;

ReactDom.hydrate(
  <App page={initialData?.page} />,
  document.getElementById("root")
);
// 출처: <실전 리액트 프로그래밍>, 이재승 저

index.js

 

 

hydration

  • 서버에서 전송한 정적 문서를 데이터 변경에 반응할 수 있는 동적 형태로 변환하는 클라이언트 측 프로세스를 말한다.
  • render와 동일하지만, dom은 이미 그려져 있는 상태이기 때문에 event listener만 부착하는 식으로 작동.

 

<!DOCTYPE html>
<html>
  <head>
    <title>ssr test</title>
    <script type="text/javascript">
      window.__INITIAL_DATA__ = __DATA_FROM_SERVER__;
    </script>
		__STYLE_FROM_SERVER__
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

index.html

 

import express from "express";
import fs from "fs";
import path from "path";
import * as url from "url";
import { renderToString } from "react-dom/server";
import React from "react";
import { ServerStyleSheet } from "styled-components";
import App from "./App";

const app = express();
const html = fs.readFileSync(
  path.resolve(__dirname, "../dist/index.html"),
  "utf8"
);
app.use("/dist", express.static("dist"));

app.get("favicon.ico", (req, res) => res.sendStatus(204));
app.get("*", (req, res) => {
  const parsedUrl = url.parse(req.url, true);
  const page =
    parsedUrl.pathname && parsedUrl.pathname !== "/"
      ? parsedUrl.pathname.substr(1)
      : "home";

  const sheet = new ServerStyleSheet();
  const renderString = renderToString(sheet.collectStyles(<App page={page} />));
  const styles = sheet.getStyleTags();

  const initialData = { page };
  const result = html
    .replace('<div id="root"></div>', `<div id="root">${renderString}</div>`)
    .replace("__DATA_FROM_SERVER__", JSON.stringify(initialData)) // head script에 server 측 주입
    .replace("__STYLE_FROM_SERVER__", styles); // head script에 데이터 주입

  res.send(result);
});

app.listen(3000);

server.js

 

 

 

 

6. Next.js


 

  • 생산성을 위해 Next.js가 주로 채택됨 
 

Next.js by Vercel - The React Framework

Production grade React applications that scale. The world’s leading companies use Next.js by Vercel to build static and dynamic websites and web applications.

nextjs.org

 

  ◦ SSR의 CRA, 간단히 구성 가능

  ◦원티드, 토스, 배민, 카카오커머스 등 사용 중

 

import React from "react";
import Link from "next/link"; // next/link 사용하여 라우팅 -> CSR 동작

export default function About({ data }) {
  return (
    <div>
      <Link href="/">about</Link> {data}
    </div>
  );
}

About.getInitialProps = function () {
  return { data: "data" };
};

 

 

    1. 첫 페이지를 "/"로 호출했을 때 페이지 자체가 SSR 후 HTML 파일로 완성되어 돌아옴
    1. 1번 이후 Link 컴포넌트를 사용하여 라우팅을 하면 CSR이 이뤄지고 서버로부터 about.js만 새로 받아온 뒤 화면을 그리게 됨
    2.  (전부 CSR로 할 땐 js를 한 번에 다운로드 받습니다. 그래서 about.js를 추가적으로 다운받지 않습니다. 서버에서 추가적으로 about.js를 다운받는건 SSR이후 CSR을 하기 위해서입니다.)
    3.  
    4. 2번에서 CSR로 이동했던 "/about" 페이지를 첫 호출로 불러오게 되면 아까는 CSR로 불러와졌던 페이지가 SSR로 호출되었음을 확인할 수 있음 (about.html)

 

 

장점

  • SSR 외에도 React App에 필요한 여러가지 필수 기능들을 제공하는 Framework 역할
    • 페이지 기반 라우팅 시스템 (동적 라우팅 지원)
    • pre-rendering , 페이지별 정적파일 생성과 서버사이드 렌더링 지원
    • 코드 스플리팅
    • CSS, Sass 기본 지원 및 다른 CSS-in-JS 라이브러리 지원 
    • 등등...

 

 

 


추가자료 (SSR CSR 상세동작 순서)

 

[CS / Q] SSR CSR의 상세동작 순서, node.js (window객체)

1. SSR, CSR의 상세동작 순서 더보기 1-1. SSR의 동작 순서 Server Side Rendering의 약자. 서버쪽에서 렌더링 준비를 끝마친 상태로 클라이언트에 전달하는 방식이다. 1. User가 Website 요청을 보냄. 2. Server..

higher77.tistory.com

 

 

Reference

https://workatit.tistory.com/m/74

 

 

 

const RegistrationContextWrapper = () => {
  return (
    <RegistProviderWapper>
      <Registration />
    </RegistProviderWapper>
  );
};

 

import { createContext, useState } from 'react';

export const RegistrationContext = createContext(null);

export const RegistrationUpdateContext = createContext(null);

export const RegistProviderWapper = props => {
  const [activeCategory, setActiveCategory] = useState('Host');
  const [uploadBackImage, setUploadBackImage] = useState('');
  const [uploadProfileImage, setUploadProfileImage] = useState('');
  const [nicknameValue, setNicknameValue] = useState('');
  const [subjectValue, setSubjectValue] = useState('');

  const [activeType, setActiveType] = useState('');
  const [activeSubtype, setActiveSubtype] = useState('');
  const [activePersonnelType, setActivePersonnelType] = useState('');
  const [reviseAddress, setReviseAddress] = useState('');
  const [addressSido, setAddressSido] = useState('');
  const [price, setPrice] = useState(0);

  const contextGetValue = {
    getter: {
      activeCategory,
      uploadBackImage,
      uploadProfileImage,
      nicknameValue,
      subjectValue,

      activeType,
      activeSubtype,
      activePersonnelType,
      reviseAddress,
      addressSido,
      price,
    },
  };

  const contextSetValue = {
    setter: {
      setActiveCategory,
      setUploadBackImage,
      setUploadProfileImage,
      setNicknameValue,
      setSubjectValue,

      setActiveType,
      setActiveSubtype,
      setActivePersonnelType,
      setReviseAddress,
      setAddressSido,
      setPrice,
    },
  };

  return (
    <RegistrationContext.Provider value={contextGetValue}>
      <RegistrationUpdateContext.Provider value={contextSetValue}>
        {props.children}
      </RegistrationUpdateContext.Provider>
    </RegistrationContext.Provider>
  );
};

 

const searchCoordinateToAddress = (lat, lng) => {
    navermaps.Service.reverseGeocode(
      {
        location: new navermaps.LatLng(lat, lng),
      },
      function (status, response) {
        if (status !== navermaps.Service.Status.OK) {
          return alert('Something wrong!');
        }

        setAddress(response.result.items[0].address);
        const sliceSido = response.result.items[0].addrdetail.sido.slice(0, 2);

        setter.setAddressSido(sliceSido);
      },
    );

 

<S.Map>
        <NaverMap
          mapDivId={'maps-getting-started-uncontrolled'}
          style={{
            width: '100%',
            height: '100%',
            outline: 'none',
          }}
          center={{ lat: latitude, lng: longitude }}
          defaultZoom={13}
          onClick={handleMarkerPosition}
        >
          <Marker
            key={1}
            position={{ lat: latitude, lng: longitude }}
            animation={0}
          />
          );
        </NaverMap>

        <MapAddressBox
          address={address}
          setLatitude={setLatitude}
          setLongitude={setLongitude}
        />
</S.Map>

+ Recent posts