let str = "abcde"

console.log(str.length)  //5

 

 

 

str.length라는 표현식을 뜯어보자.

 

  .  프로퍼티 참조 연산자라고 한다.

 

  .   앞에는 객체를 가리키는 식별자가 와야하고, 뒤에는 프로퍼티 이름이 와야한다.

 

그런데 str은 문자열 타입으로 원시 타입이다. 그렇다면 어떻게 이와같은 것이 가능한 것일까?

 

이에 대해 알기 위해서 래퍼 객체에 대해서 알아보자.

 

 

 

 

래퍼 객체


래퍼 객체란 이름처럼 원시 타입의 값을 감싸는 형태의 객체이다. 

number, string, boolean, symbol 데이터 타입에 각각 대응하는 Number, String, Boolean, Symbol이 제공된다.

 

자바스크립트의 문자열은 원시 타입으로 존재한다.

 

우리가 문자열의 프로퍼티에 접근하려고 할 때(위의 str.length 예제처럼) 자바스크립트는 new String을 호출한 것처럼 문자열 값을 객체로 변환한다.


이 객체를 래퍼 객체라고 한다. 래퍼 객체는 프로퍼티를 참조할 때 생성되며 프로퍼티 참조가 끝나면 사라진다.

 

 

let str = 'abcde';
str.len = 5; // new String(str).len = 5

console.log(str.len); // undefined

console.log(new String(str) === new String(str)) // false

두번째 줄에서 str.len에 5의 값을 할당했다. 하지만 console.log를 찍으면 undefined라고 출력된다. 

그 이유는 console.log안에 있는 str.len역시 new String(str).len이기 때문에 즉, 서로 다른 객체를 참조하고 있기 때문이다. 

 

숫자와 불리언 타입도 문자열과 같은 방식으로 동작한다.

 

 

변수의 프로퍼티에 접근할 때 래퍼 객체가 임시로 생성된다. 프로퍼티의 값을 할당하는 것은 임시로 생성된 래퍼 객체에서 수행되며 지속되지 않는다. 이 때문에 원시 타입의 프로퍼티(실제로는 래퍼 객체의 프로퍼티)가 마치 읽기 전용 값처럼 존재하는 것이다.

 

 

 

 

 

 

 

또한 String()생성자 함수로서 사용할 수도 그냥 함수로도 사용 할 수도 있다.(Number(), Boolean()도 사용가능)

 

let num = 1557

console.log(String(num)) // 함수로 사용시 =>  리턴값으로 문자열 '1557'이 출력

console.log(String(num) === new String(num)) //String === Object이기 때문에 false

 

 

 

 

 

 

reference:

https://velog.io/@kim-jaemin420/Wrapper-Object%EB%9E%98%ED%8D%BC-%EA%B0%9D%EC%B2%B4-jyt19oms

 

 

 

 

 

 

'JScript > Today I learned' 카테고리의 다른 글

[JS] While문과 for문  (0) 2021.12.10
함수형 프로그래밍이란?  (0) 2021.12.05
formData의 값을 console.log로 볼 수 없는 원인  (0) 2021.07.25
TIL#11 Array.reduce()함수  (0) 2021.07.11
TIL#10 Scope !중요  (0) 2021.06.14

 

1. SSR, CSR의 상세동작 순서

더보기

 

1-1.  SSR의 동작 순서


 

Server Side Rendering의 약자.
서버쪽에서 렌더링 준비를 끝마친 상태로 클라이언트에 전달하는 방식이다.

 

 

1. User가 Website 요청을 보냄.

2. Server는 'Ready to Render'. 즉, 즉시 렌더링 가능한 html파일을 만든다.(리소스 체크, 컴파일 후 완성된 HTML 컨텐츠로 만든다.)

! 이 즉시 렌더링 가능한 html파일에는(html,css,js)가 들어간다. 클라이언트가 읽는 JS파일과 html에서 렌더링 하는 JS코드의 정확한 기준점은 모르겠다. 다만, html은 <script>태그 안에 들어가는 js만 해당하지 않을까 생각중이다. 보통 document.createElement와 같은 돔을 직접 조종하는 코드도  클라이언트가 JS파일을 읽을 때 적용된다. 이것도 <sciprt>태그 안에 있으면 html에 랜더가 되지 않을까?라고 생각중이다.


3. 클라이언트에 전달되는 순간, 이미 렌더링 준비가 되어있기 때문에 HTML은 즉시 렌더링 된다. 그러나 사이트 자체는 조작 불가능하다. (Javascript가 읽히기 전이다.)

4. 클라이언트가 자바스크립트를 다운받는다.

5. 다운 받아지고 있는 사이에 유저는 컨텐츠는 볼 수 있지만 사이트를 조작 할 수는 없다. 이때의 사용자 조작을 기억하고 있는다.

6. 브라우저가 Javascript 프레임워크를 실행한다.

7. JS까지 성공적으로 컴파일 되었기 때문에 기억하고 있던 사용자 조작이 실행되고 이제 웹 페이지는 상호작용 가능해진다.

 

 

서버에서 이미 '렌더 가능한' 상태로 클라이언트에 전달되기 때문에, JS가 다운로드 되는 동안 사용자는 
무언가를 보고 있을 수 있다.

 

 

 

 

1-2.  CSR의 동작 순서


 

Client Side Rendering의 약자.
SSR과 달리 렌더링이 클라이언트 쪽에서 일어난다.
즉, 서버는 요청을 받으면 클라이언트에 HTML과 JS를 보내준다. 클라이언트는 그것을 받아 렌더링을 시작한다.

 

1. User가 Website 요청을 보냄.

2. CDN이 HTML 파일JS로 접근할 수 있는 링크를 클라이언트로 보낸다.
    CDN(Content Delivery Network): 유저의 요청에 '물리적'으로 가까운 서버에서 요청에 응답하는 방식 
    ex) aws의 cloudflare

3. 클라이언트는 HTML과 JS를 다운로드 받는다.(이때 SSR과 달리 유저는 아무것도 볼 수 없다.)

4. 생략

5. 다운이 완료된 JS가 실행된다. 데이터를 위한 API가 호출된다.
    (이때 유저들은 placeholder를 보게된다.)


6. 서버가 API로부터의 요청에 응답한다.

7. API로부터 받아온 data를 placeholder 자리에 넣어준다. 이제 페이지는 상호작용이 가능해진다.

 

서버에서 처리 없이 클라이언트로 보내주기 때문에 자바스립트가 모두 다운로드 되고 실행이 끝나기 전까지 사용자는 볼
수 있는게 없다.

 

 

 

 

 

 

2. WAS의문점 (Node.js)

더보기

 

 

의문점


SSR에서

 

Node.js는(웹서버 + WAS[웹서버+웹컨테이너]로 이루어져있다.)

 

Node.js는 클라이언트가 요청한 정적인 콘텐츠(html,css,javaScript)를 HTTP 프로토콜을 통하여 제공해주는 것 말고도 동적으로 DB와 연결하여 여러 데이터를 활용해 표현된 페이지를 해석한다고 한다.

 

브라우저에서는 나머지 JS파일을 읽어 상호작용을 활성해 주는걸로 보인다.

 

그런데 내가 window.localStorage객체를 사용해서 토큰을 저장하고 그 토큰을 유저의 데이터를 가져올 때 헤더에 담아서 보낸다면 node.js의 경우 window전역 객체가 없기 때문에

 

토큰에 대한 정보가 없으니 데이터를 가져오지 못할텐데.

 

이런 경우에는 WAS에서 데이터를 활용해 페이지를 해석하지않고 브라우저로 넘겨서 브라우저가 처리하는 것인가?

 

 

 

해답


첫번째,  웹스토리지는(로컬스토리지, 세션스토리지)는

클라이언트에 데이터를 저장할 수 있도록 HTML5부터 새롭게 지원하는 저장소이다.

 

즉, SSR이 아닌 CSR에서 사용하는 저장소이다.  

If you mean html 5 localStorage, there's no such a thing since node.js is a server-side technology. Html 5 localStorage is a client side feature supported

 

SSR인 경우에도 node.js에서 npm을 통해 모듈을 설치해서(node-localstroge) 사용할 수 있지만 이 경우에는 Client의 localStorage에 접근하는 것이 아니라 Server-side의 localStorage를 사용하는 것이다.

 

 

 

 

 

 

두번째, Node.js는 window객체가 없는데 settimeout같은 window 전역객체의 메소드들은 어떻게 사용하는 걸까?

 

fetch(메서드), localStorage(프로퍼티)같은 window객체에 속해있는 것은 node.js에 존재하지 않아서 npm을 통해 모듈을 다운로드 받아야 비슷한 기능을 하는 것을 사용할 수 있다.

 

 

 

settimeout과 같은 window객체에 속한 메서드는 환경에서 내부적인 스케쥴러를 통해 제공하고 있다.

https://m.blog.naver.com/dktmrorl/221936872055

위의 함수들은 자바스크립트 스팩의 일부가 아닙니다. 하지만 대부분의 환경은 내부적인 스케줄러를 지니고 있습니다. 그리고 이러한 함수들을 제공합니다(node.js 및 모든 브라우저 제공).

 

간단하게 말하자면,

 

window객체가 없기에 제공하지 못하는 메서드와 프로퍼티가 있지만 Node.js 내부적으로 제공하는 메서드와 프로퍼티도 있다고 생각하면 된다.

 

 

 

 

 

reference:

https://oneshottenkill.tistory.com/316

 

https://stackoverflow.com/questions/10358100/how-to-access-localstorage-in-node-js/10358172

 

https://stackoverflow.com/questions/38495168/how-to-use-window-sessionstorage-in-node-js

 

https://m.blog.naver.com/dktmrorl/221936872055

 

1.  쿠키


  • 쿠키를 사용하는 이유는 HTTP요청은 상태를 가지고 있지 않기 때문이다(Stateless) 즉, 브라우저에서는 서버에 요청을 보낼 때 그 요청 자체로는 그 요청이 누구에게서 오는지 알 수 없고, 쿠키에 정보를 담아서 보내야 서버는 쿠키를 통해 파악할 수 있게 된다.

 

  • 클라이언트가 서버에 방문한 정보를 클라이언트 단에 저장하는 작은 파일이다. 
  • 클라이언트의 브라우저 메모리 혹은 하드디스크에 저장이 된다.(<-> 세션)
  • 매번 서버에 전송되므로 크기가 클 경우 서버에 부담이 갈 수 있다.
  • SameSite 옵션이 Strict가 아닌 경우, 다른 도메인에서 요청할 때도 자동 전송되는 위험성이 있다. (CSRF 취약)
  • 대략 4KB까지의 데이터를 저장할 수 있으며 유효 기간이 존재한다.
  • 사용자 인증이 유효한 시간을 명시할 수 있으며, 유효 시간이 정해지면 브라우저가 종료되어도 인증이 유지됩니다.
  • 대부분의 브라우저가 지원합니다.

 

 

 

 

2. 웹스토리지


  • 클라이언트에 데이터를 저장할 수 있도록 HTML5부터 새롭게 지원하는 저장소이다. 
  • 키(Key)와 값(Value)의 쌍 형태로 데이터를 저장한다.
  • 쿠키와 달리, 서버에 전송되지 않으므로 서버에 부담이 가지 않는다. (명시적으로만 전송 가능)
  • 쿠키와 달리, 필요한 경우에만 꺼내 쓰는 것이므로 자동 전송의 위험성이 없다. 다른 도메인에서 요청하는 경우에는, 꺼내 쓰고 싶어도 도메인 단위로 접근이 제한되는 특성(CORS) 덕분에 값을 꺼내 쓸 수 없다. (CSRF 안전)
  • 쿠키와 달리, 대략 5MB까지의 데이터를 저장할 수 있으며 유효 기간이 존재하지 않는다.
  • HTML5를 지원하지 않는 브라우저에서는 사용할 수 없다.
  • 종류로는 로컬 스토리지(Local Storage)와 세션 스토리지(Session Storage)가 있다. 이들은 window 객체의 프로퍼티로서 존재하며, 같은 Storage 객체를 상속하기 때문에 동일한 메소드들을 가진다. 이 둘의 가장 큰 차이점은 데이터의 영구성이다.

 

 

2-1.  로컬 스토리지

  • window.localStorage 객체
  • 브라우저를 종료해도 유지되는 데이터로, 명시적으로 지우지 않는 한 영구적으로 저장된다.
  • 도메인별로 생성되며, 다른 도메인의 로컬 스토리지에는 접근이 불가능하다.
  • 서로 다른 브라우저 탭이라도 동일한 도메인이라면 동일한 로컬 스토리지를 사용한다.
  • 지속적으로 필요한 정보를 저장하기에 좋다. (ex. 자동 로그인 등)

 

 

2-2. 세션스토리지

  • window.sessionStorage 객체
  • 세션하고 쿠키와 달리, 탭/윈도우 단위로 세션 스토리지가 생성된다.
  • 즉 window 객체와 동일한 유효 범위 및 생존 기간을 가지며, 탭/윈도우를 닫을 시 데이터가 삭제된다.
  • 또한 동일한 탭/윈도우라도 다른 도메인이라면 또 다른 세션 스토리지가 생성된다.
  • 서로 다른 세션 스토리지는 서로 영향을 주지 않으며 독립적으로 동작한다.
  • 윈도우 복제로 생성된 경우, 혹은 스크립트로 새 창을 연 경우 세션 스토리지가 복제되어 생성된다.
  • 잠시 동안 필요한 정보를 저장하기에 좋다. (ex. 입력 폼 저장, 일회성 로그인 등)

 

 

 

 

 


 

reference:

<기본 정보>

https://it-eldorado.tistory.com/90

 

 

<추가 심화 정보> <- 꼭 읽어볼 것! (쿠키,세션비교 / 웹스토리지 상세설명)

https://ykss.netlify.app/web/storage_session_cookie/

 

<웹스토리지 쓰는 방법>

https://jess2.github.io/2018/06/06/JavaScript/JS-%EB%A1%9C%EC%BB%AC-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80-Local-Storage/

 

 

[React][자료] useEffect Hook :: Higher의 창작소

 

higher77.tistory.com

해당 글은 위의 자료를 보충 설명하기 위한 자료입니다. 

 

 

 

 


 

1. Side Effect

더보기
더보기
더보기

1. Side Effect Example


예제1

function printNum(number){
  console.log(number); //side Effect
  return number;
}

printNum(2)

함수는 인풋을 받아서 아웃풋을 내면 끝인데 그런데 그거말고 다른 행위(console.log)를 하고 있다.

그러므로 이것도 엄밀히 말하면 Side Effect다.

 

 

예제2

const num = 3;

function printNum(number){
  const result = number + num; //Side Effect
  return result;
}

printNum(2);

Output을 내는 과정에서 필요한게 아닌가 생각할 수 있지만 함수가 Input으로 받은 것들 외에 외부에 있는 나머지 값들(num)을 읽어오는 행위를 했다.

이러한 행위도 Side Effect다.

 

 

예제3

function printNum(number){
  const result = number + 2 // NOT Side Effect!!
  return result;
}

printNum(2);

이건 외부의 값을 받아오지 않았기에  Side Effect가 아니다. 

외부의 값을 가져와서 읽거나 변화시키거나 어떤 행위를 하면 SideEffect.

 

내가 인자로 받은 것들을 함수 안에서 필요한 것들로만 조합해서 Output되면 그냥 함수의 계산 과정인 것이다.

 

2. useEffect

더보기
더보기
더보기

2. Effect Hook


<첫번째 Rendering Block 예시>

const num = 3;

function printNum(number){
  const result = number + num; // sideEffect
  //
  1분걸림
  //
  
  
  return result; // UI rendering
}

printNum(2);

 

2-1  Side Effect를 그냥 함수에서 쓰던 것처럼 일으키면 안된다.

 

첫번째.  UI 랜더링 Block 문제 (Rendering Block)

 

UI를 렌더링하려면 함수형 컴포넌트에서 리턴(JSX)을 해줘야 한다.

그런데 Side Effect는 리턴을 하기 전에 발생하는 행위들이다.

만약,  return까지 도달하는데 1분이 걸린다면 그동안 UI 랜더링이 안일어난다. 그러면 유저들은 그 시간동안 기다리지 않고 사용하지 않을 것이다.

 

 

두번째.  트리거(Trigger)

 

Rerender(리랜더)는 화면을 다시 그리는 것이다. 화면을 다시 그릴 때는 함수형 컴포넌트를 다시 호출한다.

 

함수 본문에서 Data fetching을 하면 state가 바뀌면 다시 데이터를 fetching한다. 

Data fetching은 시작할 때 한 번만 필요하다. 나머지 때는 계속 기억만 하고 있으면 된다. 

계속 요청하면 서버에 불필요한 부하가 일어나고 클라이언트도 필요없는 연산을 계속 하게 된다.

즉, 특정 시점에만 실행을 하고 싶은데 함수 본문에 넣으면 매번 실행되는 트리거 문제가 있다.

 

 

이런 문제를 하는게 useEffect!

 

 

2-2 useEffect

import React, { useState, useEffect } from "react";

export function Main(){
  const [count, setCount] = useState(0);
  const [toggle, setToggle] = useState(false);
  
  useEffect(()=>{
   console.log("effect trigger");
  }, []);
  
  console.log("function exe");
  
  return(
    <>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>up</button>
      <div>
        <h1>{toggle ? "on" : "off"}</h1>
        <button onClick={() => setToggle(!toggle)}>toggle button</button>
      </div>
    </>
  );
}

export function SecondPage(){
  return <h1>Hello, JaseSang</h1>
}

// console.log 실행순서 
// function exe => effect trigger

useEffect(effect, dependency array)

  • 첫번째 인자는 우리가 발생시키기 원하는 effect를 넣는 것으로 함수가 들어간다. 이 함수는 렌더링이 끝나고나면 실행된다.  => 첫번째 UI 랜더링 Block 문제 (Rendering Block) 해결

 

  • 두번째 인자는 의존성 배열로 얘네가 바뀔 때만 실행하라는 의미이다. => 두번째 Trigger 해결 
    • 두번째 인자를 넣지 않으면 매번 렌더링 할때마다 실행한다.
    • [count]만 넣으면 카운트가 바뀔 때마다, [toggle]만 넣으면 토글이 바뀔 때마다 실행된다.
    • []빈 배열을 넣으면 처음에만 렌더된다. (data fetching을 여기서 하면 된다)

 

  • 이러한 useEffect를 이용하면 stater값이 잘 변하는지 디버깅 할 때도 쓰일 수 있다.

 

 //모든 랜더링마다
 
 useEffect(()=>{
   console.log("effect trigger");
  });
  
 //count마다
 
 useEffect(()=>{
   console.log("count changed", count);
  },[count]);
  
 //toggle마다
  
 useEffect(()=>{
   console.log("toggle changed", toggle);
  }, [toggle]);
  
  //첫번째 랜더링만
  
 useEffect(()=>{
   console.log("empty dependency", count);
  }, []);

 

 

Mount

첫번째 랜더링 때는 모든 effect가 다 실행된다.
'첫번째 랜더링이 됐다는 것은 이 컴포넌트가 비로소 화면에 들어왔다.'라는 의미다. 
그래서 첫번째 랜더링을 보통 Mount(마운트)라고 부른다. 
화면에 마운트가 됐다(화면에 처음 들어왔다.)

 

 

관심사의 분리

effect는 내가 하고자하는 동작별로 분리되어 있는게 가장 좋다.
하나의 useEffect에 다 쓰면 유지보수 측면에서 불리하다. 여러개가 들어가 있으면 나중에 뭐가 뭔지 헷갈리게 된다.

ex) Data fetching의 경우
한 번에 useEffect(() => {} , [])안에 다 넣을 수 있지만 만약 프로필, 이미지, 댓글 등등 여러개의 fetch를 사용하면 나중에 어떤 것이 프로필이고 댓글인지 헷갈리게 된다. 그래서 여러개의 useEffect로 분리해야 하는 것이다.

 

 

 

 

3. Clean up Effect

더보기
더보기
더보기

 

3. Clean up Effect


이전에 내가 발생시킨 effect가 있다. 그 effect가 단발성(ex> console.log)이면 괜찮다.

그런데 어떤 이벤트를 구독을 붙이면 그 구독을 해제하지 않는다면 그 이벤트는 끝날 때까지 붙어있다. (사용자가 탭을 닫을 때까지)

 

 

import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";

export function Main(){
  const [count, setCount] = useState(0);
  
  useEffect(()=>{
   setInterval(()=>{
     console.log("timer");
   },1000)
  }, []);
  
  console.log("function exe");
  
  return(
    <>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>up</button>
      <div>
        <Link to="/second">go to second</Link>
      </div>
    </>
  );
}

export function SecondPage(){
  return <h1>Hello, JaseSang</h1>
}
첫번째 페이지                                                                                                   두번째 페이지

 

첫 페이지에서 타이머를 effect로 부르고 두번째 페이지로 가면 구독을 하고 지우지 않았기 때문에 페이지가 바뀌었음에도 타이머는 계속 올라가고 있다.

 

SPA이기 때문에 하나의 html에서 계속 돈다. (페이지를 싹다 치우고 다시 받아오는게 아니다.)

그러면 이렇게 불필요한게 계속 돌고 있는 것이다.

 

만약 여기다가 백엔드로부터 api를 호출했다면 1초마다 api를 호출하는 꼴이 된다. 사용자가 많아지면 서버는 터질 가능성이 높아진다. 그래서 이걸 지워야 하는 것이다. 

 

이걸 클린업(Clean up)이라고 한다.

 

 

<첫번째 예시>

  useEffect(()=>{
   setInterval(()=>{
     console.log("timer");
   },1000)
   
   return () => {
    console.log("cleanup effect");
    clearInterval(id);
   }
  }, []);

 

  • effect 함수 안에 return으로 어떤 함수를 리턴해주면 된다.
  • 언제 발생되냐면
    1.  마운트된 시점에 useEffect가 발생한다.
    2.  의존성 배열이 []이므로 다시 effect가 발생하지 않는다.
    3.  언마운트(화면에서 사라졌다) 컴포넌트가 화면에서 사라지면 실행되는게 return 부분이다.

 

 

 

사실 클린업 effect는 언마운트의 경우에만 발생하는게 아니다.

첫번째 페이지에서 up버튼을 누르면 사실상 클린업 이펙트가 발생하는 정확한 시점은 엄밀히 따지자면 다음 effect가 발생하기 전에 실행하는 것이다.

 

<두번째 예시>

useEffect(() => {
  console.log("count efft")
  console.log("count", count);
  
  return () => {
    console.log("count clean up");
  };
}, [count]);

//console.log 순서

//count efft
//count 0
//count clean up   <= up버튼 클릭
//count efft
//count 1

 

세탁기에 빨래를 넣었다. 다음 빨래를 돌리기 위해서는 세탁기에 이미 들어가 있는 완료된 세탁물을 빼낸 다음에 넣는다. 

우리가 일상생활에서 하는 클린업처럼 그것과 똑같이 새로운 effect가 발생하기 전에는 항상 클린업을 먼저 돌려준다.

 

  • 첫 랜더링 때는 클린업 할게 없으니 클린없이 없다. 그 다음 이펙트가 발생할 때는 계속해서 클린업이 트리거 된다.
  • 우리가 뭔가 기존의 것을 취소하거나 무언가를 할 때 이럴 때 쓰면된다. 만약에 컴포넌트가 언마운트 되면 모든 클린업이 한 번 쭉 실행된다.
  • 첫번째 예시는 다음 effect가 없기 때문에 언마운트 될 때 그 시점에서 한 번만 클린업이 실행되었다.
  • 두번째 예시는 다음 effect가 실행되기 전에 무조건 클린업이 돌아가는 것이다.

 

 

 

 

 

'React > Today I learned' 카테고리의 다른 글

[React] hook, props, state, event + batch  (0) 2021.12.10
리액트 매우 쉬운 별점기능 구현  (0) 2021.07.11
fetch함수를 이용한 로그인&회원가입  (0) 2021.07.02
state,props,event  (0) 2021.07.01
Sass  (0) 2021.06.30

+ Recent posts