<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>매일 1% 이상 씩 성장하기</title>
        <link>https://velog.io/</link>
        <description> 매일 1% 이상 씩 성장하기</description>
        <lastBuildDate>Tue, 15 Nov 2022 08:10:19 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. 매일 1% 이상 씩 성장하기. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dae_eun2" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Yarn berry cannot find module Error]]></title>
            <link>https://velog.io/@dae_eun2/Yarn-berry-cannot-find-module-Error</link>
            <guid>https://velog.io/@dae_eun2/Yarn-berry-cannot-find-module-Error</guid>
            <pubDate>Tue, 15 Nov 2022 08:10:19 GMT</pubDate>
            <description><![CDATA[<h2 id="typescript">Typescript</h2>
<p>웹팩을 설정하는데 npm 으로 하면 에러가 없는데 이상하게 yarn 으로 하니 </p>
<blockquote>
<p>모듈 또는 해당 형식 선언을 찾을 수 없습니다.ts(2307)</p>
</blockquote>
<p>위의 에러가 뜨더라...</p>
<p>yarn berry 공식문서를 살펴보니</p>
<blockquote>
<p>yarn add @yarnpkg/sdks -D
yarn dlx @yarnpkg/sdks vscode</p>
</blockquote>
<p>Smart IDE(예: VSCode 또는 IntelliJ)는 Plug&#39;n&#39;Play 설치 를 사용할 때 TypeScript가 작동하도록 특별한 구성이 필요합니다 .
yarn sdks vscode vim지원되는 지정된 편집기에 대한 기본 SDK와 설정을 모두 생성하는 데 사용 합니다.</p>
<p>위를 실행했더니 에러가 사라졌습니다.</p>
<h2 id="eslint--prettier">Eslint + Prettier</h2>
<blockquote>
<p>yarn dlx eslint --init</p>
</blockquote>
<p><code>yarn dlx</code>는 yarn berry에서 쓰이는 <code>npx</code>와 같은 명령어 입니다.</p>
<p>개발을 할 때 eslint 와 prettier를 같이 사용하게 되는데,
prettier에서 관리하는 스타일을 eslint에서도 확인하여서 충돌이 생기는 경우가 있을 수 있습니다.
이럴 때에는 <code>eslint-config-prettier</code>를 사용하여 중복 관리되는 스타일을 Eslint에서 비활성화 할 수 있습니다.</p>
<blockquote>
<p>yarn add eslint-config-prettier -D</p>
</blockquote>
<p>그리고 <code>.eslintrc.json</code>에  설정해주면 됩니다.</p>
<pre><code>&quot;extends&quot;: [
      &quot;react-app&quot;,
      &quot;airbnb&quot;,
      &quot;prettier&quot; //여기서도 꼭 마지막에 추가
],</code></pre><p>그리고 prettier의 바이너리 파일을 찾지 못하여 올바르게 작동하지 않을때가 종종있습니다.
이럴때는</p>
<pre><code>yarn dlx @yarnpkg/sdks vscode</code></pre><p>를 해주시면 됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Module : CommonJs 와 Es6 차이]]></title>
            <link>https://velog.io/@dae_eun2/Module-CommonJs-%EC%99%80-Es6-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@dae_eun2/Module-CommonJs-%EC%99%80-Es6-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Fri, 11 Nov 2022 06:02:08 GMT</pubDate>
            <description><![CDATA[<h1 id="moduel이란">Moduel이란?</h1>
<p>개발하는 애플리케이션의 규모가 커지면 파일을 여러 개로 분리해야 하는 시점이 옵니다.
이때 분리된 하나의 파일을 모듈 이라고 부르는데, 모듈은 대게 클래스 한 혹은 목적을 가진 여러개의 함수를 포함하는 라이브러리로 구성되어 있습니다.</p>
<p>모듈은 단지 파일 하나에 불과합니다.
스크립트 하나는 모듈 하나입니다.</p>
<h2 id="모듈-장점">모듈 장점</h2>
<ul>
<li>유지보수 용이 - 기능들이 모듈화가 잘 되어 있는 경우, 의존성을 줄일 수 있기 때문에 기능을 개선하거나 수정이 용이합니다.</li>
<li>네임스페이스화 - 코드의 양이 많아질수록 전역스코프에 존재하는 변수명이 곂치는 경우가 존재합니다. 이때 모듈로 분리하면 모듈만의 네임스페이스를 갖기 때문에 그 문제를 해결할 수 있습니다.</li>
<li>재사용성 - 같은 코드를 반복하지 않고 모듈로 분리시켜서 필요할 때마다 재활용할 수 있습니다.</li>
</ul>
<h2 id="모듈-시스템의-종류">모듈 시스템의 종류</h2>
<ul>
<li>AMD - 가장 오래된 시스템 중 하나로 require.js라는 라이브러리를 통해 처음 개발되었습니다.</li>
<li>CommonJS - NodeJs 환경을 위해 만들어진 모듈 시스템 입니다.</li>
<li>UMD - AMD와 NodeJs와 같은 다양한 모듈 시스템을 함께 사용하기 위해 만들어졌습니다.<ul>
<li>ES Module - ES6(Es2015)에 도입된 자바스크립트 모듈 시스템입니다.</li>
</ul>
</li>
</ul>
<h2 id="es-module-방식">ES Module 방식</h2>
<p> ES6에 도입된 자바스크립트 모듈 시스템입니다.
 모듈을 외부에서 사용할 수 있도록 내보낼 때는 <code>named export</code>,<code>default export</code>와 같은 키워드를 사용하며,
 외부에서 모듈을 불러올때는 import를 사용하여 모듈을 불러올 수 있습니다.</p>
<p> 개발자가 지정한 파일은 entry point로 하여 나머지 파일들은 import문을 따라가며 찾습니다.</p>
<h3 id="내보내기">내보내기</h3>
<ul>
<li>namde export를 사용하여 함수 또는 변수를 내보낼 수 있습니다.</li>
<li>default export 를 사용하여 하나의 기본 함수를 내보낼 수 있습니다. 단, 모듈 당 하나만 가능합니다.</li>
</ul>
<h3 id="불러오기">불러오기</h3>
<p>import 를 사용하여 모듈을 불러올 수 있습니다.</p>
<ul>
<li><p>export 를 import 하는 경우 각각의 변수나 함수를 가져올 수 있습니다. 
또한 <code>* as 별칭</code> 을 통하여 가져올 수 있습니다.</p>
<h3 id="특징">특징</h3>
<ul>
<li>모듈을 비동기 환경에서 다운로드하며, import export 구문을 찾아 파싱한다.</li>
<li>EsModules는 실행해보지 않아도 import,export 에러를 감지할 수 있다.</li>
<li>config를 type=&#39;module&#39;로 세팅 해주어야 사용할 수 있다.</li>
<li>top-level await가 가능하다.</li>
</ul>
</li>
</ul>
<h2 id="commonjs">CommonJs</h2>
<p>NodeJs 환경에서 자바스크립트 모듈을 사용하기 위해 만들어진 모듈 시스템 입니다.
모듈을 외부에서 사용할 수 있도록 내보낼 때는 <code>exports</code>,<code>module.exports</code>와 같은 키워드를 사용하며, 외부에서 모듈을 불러올 때는 <code>required</code>를 사용합니다.</p>
<h3 id="내보내기-1">내보내기</h3>
<ul>
<li>exports 변수의 속성으로 내보낼 함수를 설정할 수 있습니다.</li>
<li>module.exports를 사용하여 하나의 객체로 묶어서 내보낼 수 있습니다.<h3 id="불러오기-1">불러오기</h3>
</li>
<li>require 키워드를 통해 변수에 할당할 수 있습니다.</li>
<li>전개구문을 통하여 불러올 수 있습니다.</li>
</ul>
<h3 id="특징-1">특징</h3>
<ul>
<li>require가 동기로 이루어지므로 promise를 return 하지 않는다.</li>
<li>CommonJs는 실행을 해보아야 import,export에러를 감지할 수 있다.</li>
<li>디폴트 값으로 적용된다.</li>
<li>top-level await가 불가능하다.</li>
</ul>
<h2 id="순환-의존성">순환 의존성</h2>
<pre><code>// main.js
let count = require(&#39;./counter.js&#39;).count;
export message = &#39;hi&#39;;

// counter.js
let message = require(&#39;./main.js&#39;).message; // message 정의되어 있지 않음
setTimeout(() =&gt; console.log(message), 0); // CommonJS : undefined, ES Modules : &#39;hi&#39;</code></pre><p>순환 관계가 있을 때 CommonJs는 변수 값을 복사해서 사용하므로 main.js에서의 message가 할당 되어도 counter.js에서의 변수는 변하지 않는다.
하지만 ES Module에서는 같은 변수를 참조하고 있으므로 setTimout에서 제대로 할당된 변수가 출력된다.
ES Module은 위와 같이 순환 의존성을 지원한다.</p>
<h2 id="실제사용">실제사용</h2>
<p>리엑트에 서는 ESModules를 주로 사용합니다.
하지만 패키지 모듈들은 결국 CommonJs를 기본 모듈 시스템으로 선택하기 때문에, 코드 작성시 CommonJs로 변환해 주어야 하는데  해당 작업은 Babel이 수행합니다.</p>
<h2 id="정리">정리</h2>
<p>CommonJS와 비교하여 ESModules는 비동기로 동작하여 속도가 빠르고
실제 사용되는 부분만을 import(tree shaking)하여 메모리를 적게 차지하며, 가독성이 좋고 순환 의존성을 지원한다는 이점이 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript Compiler Options]]></title>
            <link>https://velog.io/@dae_eun2/TypeScript-Compiler-Options</link>
            <guid>https://velog.io/@dae_eun2/TypeScript-Compiler-Options</guid>
            <pubDate>Fri, 11 Nov 2022 03:48:09 GMT</pubDate>
            <description><![CDATA[<p>TypesScript로 짜여진 코드를 실행하기 위해서는 먼저 JavaScript로 변환하는 트랜스파일링 과정을 거쳐야 한다.</p>
<p>이 과정에 적용될 설정을 정리해두는 파일이 tsconfig.json 이다.
tsconfig.json은 직접 생성하거나, 혹은 tsc 명령어로 생성 가능하다.</p>
<pre><code>npm i -g typescript
tsc --init</code></pre><p>위 명령어를 실행하면 아래와 같은 파일이 생긴다.</p>
<pre><code>// tsconfig.json
{
  &quot;compilerOptions&quot;: {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Projects */
    // &quot;incremental&quot;: true,                              /* Enable incremental compilation */
    // &quot;composite&quot;: true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
    // &quot;tsBuildInfoFile&quot;: &quot;./&quot;,                          /* Specify the folder for .tsbuildinfo incremental compilation files. */
    // &quot;disableSourceOfProjectReferenceRedirect&quot;: true,  /* Disable preferring source files instead of declaration files when referencing composite projects */
    // &quot;disableSolutionSearching&quot;: true,                 /* Opt a project out of multi-project reference checking when editing. */
    // &quot;disableReferencedProjectLoad&quot;: true,             /* Reduce the number of projects loaded automatically by TypeScript. */

    /* Language and Environment */
    &quot;target&quot;: &quot;es2016&quot;,                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    // &quot;lib&quot;: [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
    // &quot;jsx&quot;: &quot;preserve&quot;,                                /* Specify what JSX code is generated. */
    // &quot;experimentalDecorators&quot;: true,                   /* Enable experimental support for TC39 stage 2 draft decorators. */
    // &quot;emitDecoratorMetadata&quot;: true,                    /* Emit design-type metadata for decorated declarations in source files. */
    // &quot;jsxFactory&quot;: &quot;&quot;,                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. &#39;React.createElement&#39; or &#39;h&#39; */
    // &quot;jsxFragmentFactory&quot;: &quot;&quot;,                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. &#39;React.Fragment&#39; or &#39;Fragment&#39;. */
    // &quot;jsxImportSource&quot;: &quot;&quot;,                            /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
    // &quot;reactNamespace&quot;: &quot;&quot;,                             /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
    // &quot;noLib&quot;: true,                                    /* Disable including any library files, including the default lib.d.ts. */
    // &quot;useDefineForClassFields&quot;: true,                  /* Emit ECMAScript-standard-compliant class fields. */

    /* Modules */
    &quot;module&quot;: &quot;commonjs&quot;,                                /* Specify what module code is generated. */
    // &quot;rootDir&quot;: &quot;./&quot;,                                  /* Specify the root folder within your source files. */
    // &quot;moduleResolution&quot;: &quot;node&quot;,                       /* Specify how TypeScript looks up a file from a given module specifier. */
    // &quot;baseUrl&quot;: &quot;./&quot;,                                  /* Specify the base directory to resolve non-relative module names. */
    // &quot;paths&quot;: {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
    // &quot;rootDirs&quot;: [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
    // &quot;typeRoots&quot;: [],                                  /* Specify multiple folders that act like `./node_modules/@types`. */
    // &quot;types&quot;: [],                                      /* Specify type package names to be included without being referenced in a source file. */
    // &quot;allowUmdGlobalAccess&quot;: true,                     /* Allow accessing UMD globals from modules. */
    // &quot;resolveJsonModule&quot;: true,                        /* Enable importing .json files */
    // &quot;noResolve&quot;: true,                                /* Disallow `import`s, `require`s or `&lt;reference&gt;`s from expanding the number of files TypeScript should add to a project. */

    /* JavaScript Support */
    // &quot;allowJs&quot;: true,                                  /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
    // &quot;checkJs&quot;: true,                                  /* Enable error reporting in type-checked JavaScript files. */
    // &quot;maxNodeModuleJsDepth&quot;: 1,                        /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */

    /* Emit */
    // &quot;declaration&quot;: true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
    // &quot;declarationMap&quot;: true,                           /* Create sourcemaps for d.ts files. */
    // &quot;emitDeclarationOnly&quot;: true,                      /* Only output d.ts files and not JavaScript files. */
    // &quot;sourceMap&quot;: true,                                /* Create source map files for emitted JavaScript files. */
    // &quot;outFile&quot;: &quot;./&quot;,                                  /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
    // &quot;outDir&quot;: &quot;./&quot;,                                   /* Specify an output folder for all emitted files. */
    // &quot;removeComments&quot;: true,                           /* Disable emitting comments. */
    // &quot;noEmit&quot;: true,                                   /* Disable emitting files from a compilation. */
    // &quot;importHelpers&quot;: true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
    // &quot;importsNotUsedAsValues&quot;: &quot;remove&quot;,               /* Specify emit/checking behavior for imports that are only used for types */
    // &quot;downlevelIteration&quot;: true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
    // &quot;sourceRoot&quot;: &quot;&quot;,                                 /* Specify the root path for debuggers to find the reference source code. */
    // &quot;mapRoot&quot;: &quot;&quot;,                                    /* Specify the location where debugger should locate map files instead of generated locations. */
    // &quot;inlineSourceMap&quot;: true,                          /* Include sourcemap files inside the emitted JavaScript. */
    // &quot;inlineSources&quot;: true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
    // &quot;emitBOM&quot;: true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
    // &quot;newLine&quot;: &quot;crlf&quot;,                                /* Set the newline character for emitting files. */
    // &quot;stripInternal&quot;: true,                            /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
    // &quot;noEmitHelpers&quot;: true,                            /* Disable generating custom helper functions like `__extends` in compiled output. */
    // &quot;noEmitOnError&quot;: true,                            /* Disable emitting files if any type checking errors are reported. */
    // &quot;preserveConstEnums&quot;: true,                       /* Disable erasing `const enum` declarations in generated code. */
    // &quot;declarationDir&quot;: &quot;./&quot;,                           /* Specify the output directory for generated declaration files. */
    // &quot;preserveValueImports&quot;: true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */

    /* Interop Constraints */
    // &quot;isolatedModules&quot;: true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
    // &quot;allowSyntheticDefaultImports&quot;: true,             /* Allow &#39;import x from y&#39; when a module doesn&#39;t have a default export. */
    &quot;esModuleInterop&quot;: true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
    // &quot;preserveSymlinks&quot;: true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
    &quot;forceConsistentCasingInFileNames&quot;: true,            /* Ensure that casing is correct in imports. */

    /* Type Checking */
    &quot;strict&quot;: true,                                      /* Enable all strict type-checking options. */
    // &quot;noImplicitAny&quot;: true,                            /* Enable error reporting for expressions and declarations with an implied `any` type.. */
    // &quot;strictNullChecks&quot;: true,                         /* When type checking, take into account `null` and `undefined`. */
    // &quot;strictFunctionTypes&quot;: true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
    // &quot;strictBindCallApply&quot;: true,                      /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
    // &quot;strictPropertyInitialization&quot;: true,             /* Check for class properties that are declared but not set in the constructor. */
    // &quot;noImplicitThis&quot;: true,                           /* Enable error reporting when `this` is given the type `any`. */
    // &quot;useUnknownInCatchVariables&quot;: true,               /* Type catch clause variables as &#39;unknown&#39; instead of &#39;any&#39;. */
    // &quot;alwaysStrict&quot;: true,                             /* Ensure &#39;use strict&#39; is always emitted. */
    // &quot;noUnusedLocals&quot;: true,                           /* Enable error reporting when a local variables aren&#39;t read. */
    // &quot;noUnusedParameters&quot;: true,                       /* Raise an error when a function parameter isn&#39;t read */
    // &quot;exactOptionalPropertyTypes&quot;: true,               /* Interpret optional property types as written, rather than adding &#39;undefined&#39;. */
    // &quot;noImplicitReturns&quot;: true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
    // &quot;noFallthroughCasesInSwitch&quot;: true,               /* Enable error reporting for fallthrough cases in switch statements. */
    // &quot;noUncheckedIndexedAccess&quot;: true,                 /* Include &#39;undefined&#39; in index signature results */
    // &quot;noImplicitOverride&quot;: true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
    // &quot;noPropertyAccessFromIndexSignature&quot;: true,       /* Enforces using indexed accessors for keys declared using an indexed type */
    // &quot;allowUnusedLabels&quot;: true,                        /* Disable error reporting for unused labels. */
    // &quot;allowUnreachableCode&quot;: true,                     /* Disable error reporting for unreachable code. */

    /* Completeness */
    // &quot;skipDefaultLibCheck&quot;: true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
    &quot;skipLibCheck&quot;: true                                 /* Skip type checking all .d.ts files. */
  }
}</code></pre><h3 id="options">Options</h3>
<pre><code>{
  &quot;compilerOptions&quot;: {
    /* https://aka.ms/tsconfig.json 를 방문하면 해당 파일에 대한 더 많은 정보를 얻을 수 있습니다. */

    // 옵션은 아래와 같은 형식으로 구성되어 있습니다.
    // &quot;모듈 키&quot;: 모듈 값                        /* 설명: 사용가능 옵션 (설명이 &quot;~ 여부&quot;인 경우 &#39;true&#39;, &#39;false&#39;) */

    /* 기본 옵션 */
    // &quot;incremental&quot;: true,                   /* 증분 컴파일 설정 여부 */
    &quot;target&quot;: &quot;es5&quot;,                          /* 사용할 특정 ECMAScript 버전 설정: &#39;ES3&#39; (기본), &#39;ES5&#39;, &#39;ES2015&#39;, &#39;ES2016&#39;, &#39;ES2017&#39;, &#39;ES2018&#39;, &#39;ES2019&#39;, &#39;ES2020&#39;, 혹은 &#39;ESNEXT&#39;. */
    &quot;module&quot;: &quot;commonjs&quot;,                     /* 모듈을 위한 코드 생성 설정: &#39;none&#39;, &#39;commonjs&#39;, &#39;amd&#39;, &#39;system&#39;, &#39;umd&#39;, &#39;es2015&#39;, &#39;es2020&#39;, or &#39;ESNext&#39;. */
    // &quot;lib&quot;: [],                             /* 컴파일에 포함될 라이브러리 파일 목록 */
    // &quot;allowJs&quot;: true,                       /* 자바스크립트 파일 컴파일 허용 여부 */
    // &quot;checkJs&quot;: true,                       /* .js 파일의 오류 검사 여부 */
    // &quot;jsx&quot;: &quot;preserve&quot;,                     /* JSX 코드 생성 설정: &#39;preserve&#39;, &#39;react-native&#39;, 혹은 &#39;react&#39;. */
    // &quot;declaration&quot;: true,                   /* &#39;.d.ts&#39; 파일 생성 여부. */
    // &quot;declarationMap&quot;: true,                /* 각 &#39;.d.ts&#39; 파일의 소스맵 생성 여부. */
    // &quot;sourceMap&quot;: true,                     /* &#39;.map&#39; 파일 생성 여부. */
    // &quot;outFile&quot;: &quot;./&quot;,                       /* 단일 파일로 합쳐서 출력합니다. */
    // &quot;outDir&quot;: &quot;./&quot;,                        /* 해당 디렉토리로 결과 구조를 보냅니다. */
    // &quot;rootDir&quot;: &quot;./&quot;,                       /* 입력 파일의 루트 디렉토리(rootDir) 설정으로 --outDir로 결과 디렉토리 구조를 조작할 때 사용됩니다. */
    // &quot;composite&quot;: true,                     /* 프로젝트 컴파일 여부 */
    // &quot;tsBuildInfoFile&quot;: &quot;./&quot;,               /* 증분 컴파일 정보를 저장할 파일 */
    // &quot;removeComments&quot;: true,                /* 주석 삭제 여부 */
    // &quot;noEmit&quot;: true,                        /* 결과 파일 내보낼지 여부 */
    // &quot;importHelpers&quot;: true,                 /* &#39;tslib&#39;에서 헬퍼를 가져올 지 여부 */
    // &quot;downlevelIteration&quot;: true,            /* 타겟이 &#39;ES5&#39;, &#39;ES3&#39;일 때에도 &#39;for-of&#39;, spread 그리고 destructuring 문법 모두 지원 */
    // &quot;isolatedModules&quot;: true,               /* 각 파일을 분리된 모듈로 트랜스파일 (&#39;ts.transpileModule&#39;과 비슷합니다). */

    /* 엄격한 타입-확인 옵션 */
    &quot;strict&quot;: true,                           /* 모든 엄격한 타입-체킹 옵션 활성화 여부 */
    // &quot;noImplicitAny&quot;: true,                 /* &#39;any&#39; 타입으로 구현된 표현식 혹은 정의 에러처리 여부 */
    // &quot;strictNullChecks&quot;: true,              /* 엄격한 null 확인 여부 */
    // &quot;strictFunctionTypes&quot;: true,           /* 함수 타입에 대한 엄격한 확인 여부 */
    // &quot;strictBindCallApply&quot;: true,           /* 함수에 엄격한 &#39;bind&#39;, &#39;call&#39; 그리고 &#39;apply&#39; 메소드 사용 여부 */
    // &quot;strictPropertyInitialization&quot;: true,  /* 클래스의 값 초기화에 엄격한 확인 여부 */
    // &quot;noImplicitThis&quot;: true,                /* &#39;any&#39; 타입으로 구현된 &#39;this&#39; 표현식 에러처리 여부 */
    // &quot;alwaysStrict&quot;: true,                  /* strict mode로 분석하고 모든 소스 파일에 &quot;use strict&quot;를 추가할 지 여부 */

    /* 추가적인 확인 */
    // &quot;noUnusedLocals&quot;: true,                /* 사용되지 않은 지역 변수에 대한 에러보고 여부 */
    // &quot;noUnusedParameters&quot;: true,            /* 사용되지 않은 파라미터에 대한 에러보고 여부 */
    // &quot;noImplicitReturns&quot;: true,             /* 함수에서 코드의 모든 경로가 값을 반환하지 않을 시 에러보고 여부 */
    // &quot;noFallthroughCasesInSwitch&quot;: true,    /* switch문에서 fallthrough 케이스에 대한 에러보고 여부 */

    /* 모듈 해석 옵션 */
    // &quot;moduleResolution&quot;: &quot;node&quot;,            /* 모듈 해석 방법 설정: &#39;node&#39; (Node.js) 혹은 &#39;classic&#39; (TypeScript pre-1.6). */
    // &quot;baseUrl&quot;: &quot;./&quot;,                       /* non-absolute한 모듈 이름을 처리할 기준 디렉토리 */
    // &quot;paths&quot;: {},                           /* &#39;baseUrl&#39;를 기준으로 불러올 모듈의 위치를 재지정하는 엔트리 시리즈 */
    // &quot;rootDirs&quot;: [],                        /* 결합된 컨텐츠가 런타임에서의 프로젝트 구조를 나타내는 루트 폴더들의 목록 */
    // &quot;typeRoots&quot;: [],                       /* 타입 정의를 포함할 폴더 목록, 설정 안 할 시 기본적으로 ./node_modules/@types로 설정 */
    // &quot;types&quot;: [],                           /* 컴파일중 포함될 타입 정의 파일 목록 */
    // &quot;allowSyntheticDefaultImports&quot;: true,  /* default export이 아닌 모듈에서도 default import가 가능하게 할 지 여부, 해당 설정은 코드 추출에 영향은 주지 않고, 타입확인에만 영향을 줍니다. */
    &quot;esModuleInterop&quot;: true,                  /* 모든 imports에 대한 namespace 생성을 통해 CommonJS와 ES Modules 간의 상호 운용성이 생기게할 지 여부,  &#39;allowSyntheticDefaultImports&#39;를 암시적으로 승인합니다. */
    // &quot;preserveSymlinks&quot;: true,              /* symlik의 실제 경로를 처리하지 않을 지 여부 */
    // &quot;allowUmdGlobalAccess&quot;: true,          /* UMD 전역을 모듈에서 접근할 수 있는 지 여부 */

    /* 소스 맵 옵션 */
    // &quot;sourceRoot&quot;: &quot;&quot;,                      /* 소스 위치 대신 디버거가 알아야 할 TypeScript 파일이 위치할 곳 */
    // &quot;mapRoot&quot;: &quot;&quot;,                         /* 생성된 위치 대신 디버거가 알아야 할 맵 파일이 위치할 곳 */
    // &quot;inlineSourceMap&quot;: true,               /* 분리된 파일을 가지고 있는 대신, 단일 파일을 소스 맵과 가지고 있을 지 여부 */
    // &quot;inlineSources&quot;: true,                 /* 소스맵과 나란히 소스를 단일 파일로 내보낼 지 여부, &#39;--inlineSourceMap&#39; 혹은 &#39;--sourceMap&#39;가 설정되어 있어야 한다. */

    /* 실험적 옵션 */
    // &quot;experimentalDecorators&quot;: true,        /* ES7의 decorators에 대한 실험적 지원 여부 */
    // &quot;emitDecoratorMetadata&quot;: true,         /* decorator를 위한 타입 메타데이터를 내보내는 것에 대한 실험적 지원 여부 */

    /* 추가적 옵션 */
    &quot;skipLibCheck&quot;: true,                     /* 정의 파일의 타입 확인을 건너 뛸 지 여부 */
    &quot;forceConsistentCasingInFileNames&quot;: true  /* 같은 파일에 대한 일관되지 않은 참조를 허용하지 않을 지 여부 */
  }
}</code></pre><h3 id="참고-이미지">참고 이미지</h3>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/8e4b9d38-8901-4fe2-951a-b0bcc5977179/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Framer-motion]]></title>
            <link>https://velog.io/@dae_eun2/Framer-motion</link>
            <guid>https://velog.io/@dae_eun2/Framer-motion</guid>
            <pubDate>Mon, 07 Nov 2022 03:06:46 GMT</pubDate>
            <description><![CDATA[<p>당프소 클론 코딩을 하다가 발견하게된
새로운 라이브러리인 Framer-motion
<a href="https://www.framer.com/docs/">공식 문서</a></p>
<p>Framer-motion은 리액트에서 다양한 애니메이션을 만들 때 쓰면 정말정말 유용해 보인다.</p>
<p>오늘은 위 라이브러리의 사용법을 정리해보려 한다.</p>
<h2 id="설치">설치</h2>
<blockquote>
<p>npm i Framer-motion
yarn add Framer-motion</p>
</blockquote>
<blockquote>
<p>import {motion} from &#39;framer-motion&#39;</p>
</blockquote>
<p>motion.div와 같이 HTML태그 앞에 motion 키워드를 붙여줍니다.
이렇게 motion 키워드가 붙은 요소를 <code>motion component</code>라 합니다.
초기 상태를 initial 속성에 객체 형태로 넣고,
애니메이션 할 상태를 animate 속성에 객체로 넣습니다.</p>
<pre><code>&lt;motion.div
    initial={{ scale:0}}
    animate={{scale:1,rotateZ:360}}</code></pre><h2 id="animation">Animation</h2>
<h3 id="transition">transition</h3>
<p>기본적으로 Motion은 애니메이션 되는 값의 유형에 따라 적절한 애니메이션을 만들어 준다.
예를 들어 x/y 변경, scale 변경 등 물리적인 변경은 스프링 시뮬레이션을 통해 애니매이션되고, opacity 나 color가 바뀔 때는 자연스러운 Tween 효과를 적용한다.</p>
<p>사용자는 transition 속성으로 원하는 유형의 애니메이션을 정의할 수 있다.</p>
<pre><code>&lt;motion.div
  initial={{ opacity: 0, scale: 0.5 }}
  animate={{ opacity: 1, scale: 1 }}
  transition={{
    duration: 0.8,
    delay: 0.5,
    ease: [0, 0.71, 0.2, 1.01]
  }}
/&gt;</code></pre><h3 id="enter-animation">Enter Animation</h3>
<p>motion 컴포넌트가 처음 생성될 때, animate 속성에 적용된 값이 style 또는 inital에 정의된 값과 다르다면 animate속성에 적용된 값으로 자동으로 애니메이션을 적용해 줍니다.
자동으로 적용하길 원치 않는다면 inital 값을 <code>false</code>로 설정해주세요.</p>
<pre><code>&lt;motion.div animate={{ x: 100 }} initial={false} /&gt;</code></pre><h3 id="exit-animations">Exit Animations</h3>
<p>리엑트에서는 컴포넌트가 트리에서 삭제될 경우 &#39;즉시&#39; 사라져버리기 때문에 사라지는 애니메이션을 적용하기 어렵다는 문제가 있습니다.
하지만 <code>AnimatePresence</code>컴포넌트를 사용하면 사라지는 애니메이션이 보여지는 동안 <br>DOM에 유지 되도록 할 수 있습니다.
<a href="https://jaeseokim.dev/React/Framer-Motion%EC%9D%98-AnimatePresence-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EB%B6%84%EC%84%9D%ED%95%98%EA%B8%B0/">AnimatePresence 동작원리 파악하기</a></p>
<pre><code>import { motion, AnimatePresence } from &quot;framer-motion&quot;;

&lt;AnimatePresence&gt;
  {isVisible &amp;&amp; (
    &lt;motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    /&gt;
  )}
&lt;/AnimatePresence&gt;</code></pre><h3 id="keyframes">Keyframes</h3>
<p>Animate의 값을 배열로 설정하면 motion이 각 값을 차례로 처리합니다.
현재 값을 초기 키프레임으로 사용하고 싶다면 null 값을 주면 됩니다.</p>
<p>이렇게 하면 애니메이션 되는 도중에 애니메이션이 시작되더라도 전환이 자연스러워집니다.
또한 코드의 중복을 줄입니다.</p>
<p>각 키프레임은 애니메이션 전체에 걸쳐 균등하게 배치됩니다.
하지만 times를 통해 원하는 타이밍을 지정할 수 있습니다.
times 는 0과 1사이에 숫자로 정의되어있습니다.</p>
<pre><code>&lt;motion.circle
  cx={500}
  animate={{ cx: [null, 100, 200] }}
  transition={{ duration: 3, times: [0, 0.2, 1] }}
/&gt;</code></pre><h3 id="gesture-aniamtions">Gesture Aniamtions</h3>
<p>Framer motion 에는 hover,tap,drag,focus,inView 와 같이 제스처가 시작될 때 값 집합에 애니메이션을 적용할 수 있습니다.</p>
<pre><code>&lt;motion.button
  initial={{ opacity: 0.6 }}
  whileHover={{
    scale: 1.2,
    transition: { duration: 1 },
  }}
  whileTap={{ scale: 0.9 }}
  whileInView={{ opacity: 1 }}
/&gt;</code></pre><p>이러한 제스처가 끝나면 다시 애니메이션할 값을 자동으로 파악합니다.</p>
<h3 id="variants">Variants</h3>
<p>단일 개체에 애니메이션을 설정하는 것은 쉽습니다.
그러나 때로는 DOM 전체에 파생되는 애니메이션이나, 차례로 이뤄지는 애니메이션을 설정하고 싶을 때는 어떻게 할까요??</p>
<p>그럴때는 variants를 써봅시다.</p>
<pre><code>const variants = {
  hidden: { opacity: 0 },
  visible: { opacity: 1 }
}</code></pre><p>motion 컴포넌트에 variants 속성으로 사전에 정의한 내용을 넘겨줍니다.</p>
<pre><code>&lt;motion.div variants={variants} /&gt;</code></pre><p>초기 상태 initial, 적용할 애니메이션 animate 속성을 variants 객체에 있는 속성 이름으로 지정하면 끝!</p>
<pre><code>&lt;motion.div
  initial=&quot;hidden&quot;
  animate=&quot;visible&quot;
  variants={variants}
/&gt;
</code></pre><h3 id="propagation">Propagation</h3>
<p>만약 motion 컴포넌트에 자식 요소가 있다면, 자식 요소가 자체 animate속성을 정의하기 전까지 variants의 변화를 상속 받도록 할 수 있습니다.
쉽게 말해, variants에 정의하 속성명을 자식에게 그대로 물려줄 수 있습니다.</p>
<pre><code>const list = {
  hidden: { opacity: 0 },
  visible: { opacity: 1 },
}

const item = {
  hidden: { opacity: 0, y: -100 },
  visible: { opacity: 1, y: 0 },
}

return (
  &lt;motion.ul
    initial=&quot;hidden&quot;
    animate=&quot;visible&quot;
    variants={list}
  &gt;
    &lt;motion.li variants={item} /&gt;
    &lt;motion.li variants={item} /&gt;
    &lt;motion.li variants={item} /&gt;
  &lt;/motion.ul&gt;
)</code></pre><p>이 경우 li에 달린 variants={item}은 initial=&quot;hidden&quot;과 animate=&quot;visible&quot;가 자동으로 적용됩니다.</p>
<h3 id="orchestration">Orchestration</h3>
<p>위의 예제에서 볼 수 있는 것 처럼, item에 달린 애니메이션은 모두 동시에 시작됩니다.
하지만 transition에 추가적인 속성을 더해 자식 애니메이션의 실행을 조정할 수 있습니다.</p>
<pre><code>
const list = {
  visible: {
    opacity: 1,
    transition: {
      when: &quot;beforeChildren&quot;,
      staggerChildren: 0.3,
    },
  },
  hidden: {
    opacity: 0,
    transition: {
      when: &quot;afterChildren&quot;,
    },
  },
}</code></pre><h3 id="dynamic-variants">Dynamic variants</h3>
<p>함수를 정의해서 각 variant에 동적으로 애니메이션을 설정할 수도 있습니다.
이러한 variant함수들은 컴포넌트의 custom 속성으로 넘어오는 값을 인자로 받습니다.</p>
<pre><code>const variants = {
  visible: i =&gt; ({
    opacity: 1,
    transition: {
      delay: i * 0.3,
    },
  }),
  hidden: { opacity: 0 },
}

return items.map((item, i) =&gt; (
  &lt;motion.li
    custom={i}
    animate=&quot;visible&quot;
    variants={variants}
  /&gt;
))</code></pre><h3 id="manual-controls">Manual Controls</h3>
<p>대부분의 UI 인터렉션에 맞춰 애니메이션이 실행되지만,
좀더 복잡한 시퀀스를 구현하고 싶다면 <code>useAnimationControls</code>훅으로 애니메이션을 수동 시작/중지할 수 있습니다.</p>
<pre><code>import { useEffect, useState } from &quot;react&quot;;
import { motion, useAnimationControls } from &quot;framer-motion&quot;;

export default function App() {
  const [show, setShow] = useState(false);
  const controls = useAnimationControls();

  useEffect(() =&gt; {
    if (show) {
      controls.start({ scale: 6 });
    }
  }, [controls, show]);

  return (
    &lt;div className=&quot;wrap&quot;&gt;
      &lt;motion.h1 animate={controls}&gt;{show ? &quot;Wow!&quot; : &quot;...&quot;}&lt;/motion.h1&gt;
      &lt;button onClick={() =&gt; setShow(true)}&gt;setShow(true)&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre><h2 id="motion-components">Motion Components</h2>
<p>motion 컴포넌트는 60fps 애니메이션에 최적하된 DOM 요소 입니다.
숫자, 숫자를 포함한 문자열, 16진수 또는 RGB 등 원하는 값만 넣으면 motion이 알아서 다 해줍니다.</p>
<h3 id="supported-values">Supported values</h3>
<p>일반적으로 같은 유형끼리 애니메이션이 가능하지만,
HTML의 x-y,width-hieght,top,left,right,bottom 은 다른 값 유형이더라도 자유롭게 애니메이션될 수 있습니다.</p>
<pre><code>
&lt;motion.div
  initial={{ x: &quot;100%&quot; }}
  animate={{ x: &quot;calc(100vw - 50%)&quot; }}
/&gt;</code></pre><h3 id="transform">transform</h3>
<p>css transform 속성은 GPU에 의해 가속화 되므로 애니메이션 하기 좋은 속성이죠.
여러 값이 있을 때 translate =&gt; scale =&gt; rotate =&gt; skew 순대로 적용되지만,
원한다면 transform Tmeplate 속성으로 사용자 정의도 가능합니다.</p>
<pre><code>const transform = ({ rotate, x }) =&gt; `rotate(${rotate}) translateX(${x})`

&lt;motion.div transformTemplate={transform} /&gt;</code></pre><h3 id="css-variables">CSS variables</h3>
<p>motion은 css 변수값도 애니메이션 처리할 수 있습니다.
(타입스크립트를 쓰고 있다면 as any로 타입 처리를 해주세요)</p>
<pre><code>&lt;motion.ul
  initial={{ &#39;--rotate&#39;: &#39;0deg&#39; } as any}
  animate={{ &#39;--rotate&#39;: &#39;360deg&#39; } as any}
&gt;
  &lt;li style={{ transform: &#39;rotate(var(--rotate))&#39; }} /&gt;
&lt;/motion.ul&gt;</code></pre><h3 id="svg-line-drawing">SVG line drawing</h3>
<p>pathLength,pathSpacing,pathOffset 속성을 사용하여
SVG 애니메이션을 만들 수 있습니다.
0~1사이의 값으로 설정되며, 여기서 1은 path의 측정된 길이입니다.
패스 에니메이션은 circle,ellipse,line,path,polygon,polyline,rect 와 호환됩니다.</p>
<pre><code>const variants = {
  start: { pathLength: 0, fill: &quot;rgba(255, 255, 255,0)&quot; },
  end: { pathLength: 1, fill: &quot;rgba(255, 255, 255, 1)&quot; }
};

&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 400 500&quot;&gt;
  &lt;motion.path
    variants={variants}
    initial=&quot;start&quot;
    animate=&quot;end&quot;
    transition={{
      default: { duration: 1.8 },
      fill: { duration: 1, delay: 1.1 }
    }}
    d=&quot;M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z&quot;
  /&gt;
&lt;/svg&gt;</code></pre><h2 id="layout-animations">Layout Animations</h2>
<p>Framer motion의 강력한 강점 중 하나는 손쉽게 레이아웃 애니메이션을 설정할 수 있다는 점입니다.</p>
<p>css 레이아웃은 사실 힘들고 어려운 일입니다.
예를 들어, height를 100px에서 500px로 만들고 트랜지션을 적용하면 리플로우(Reflow)가 발생하면서 성능이 저하됩니다. 
게다가 트랜지션으로는 한계가 있습니다.
justify-content 상태일때 flex-start와 flex-end를 트랜지션할 수 있을까요??</p>
<p>motion을 사용한다면 가능합니다.</p>
<pre><code>&lt;motion.div layout /&gt;</code></pre><p>재렌더링의 결과로 발생하느 모든 레이아웃 변경사항을 감지해 애니메이션을 실행합니다.
다음과 같은 조합이 될 수 있습니다.</p>
<ul>
<li>목록 재정렬</li>
<li>컴포넌트 자체에 설정된 스타일 변경(width,position등)</li>
<li>부모 레이아웃의 변경(felxbox,grid등)</li>
<li>그밖에 다른 모든 레이아웃 변경사항</li>
</ul>
<h3 id="scale-correction">scale correction</h3>
<p>모든 레이아웃 애니메이션은 transform 속성을 사용하므로 부드럽게 구현됩니다.
transform을 사용하는 레이아웃 애니메이션은 때때로 자식 요소가 시각적으로 왜곡되곤 하는데요, 이를 수정하기 위해 요소의 첫 번째 자식에도 레이아웃 속성을 지정할 수 있습니다.</p>
<p>box-shadow나 border-raius 속성이 왜곡될 수도 있습니다만,
값이 설정되어있는 motion컴포넌트는 이를 자동으로 수정합니다.
만약 이런 값들을 애니메이션화 하지 않을거라면 그냥 style로 지정하는것이 좋습니다.</p>
<pre><code>&lt;motion.div layout style={{ borderRadius: 20 }} /&gt;</code></pre><h3 id="customisiong-layout-animations">customisiong layout animations</h3>
<p>레이아웃 애니메이션도 transition 속성으로 커스터마이징 가능합니다.</p>
<pre><code>&lt;motion.div layout transition={{ duration: 0.3 }} /&gt;</code></pre><p>특별히 레이아웃 애니메이션에만 트랜지션을 지정하고 싶다면 layout속성을 설정해주세요.</p>
<pre><code>
&lt;motion.div
  layout
  animate={{ opacity: 0.5 }}
  transition={{
    opacity: { ease: &quot;linear&quot; },
    layout: { duration: 0.3 }
  }}
/&gt;</code></pre><h3 id="animating-within-scroll-containers">Animating within scroll containers</h3>
<p>스크롤 가능한 요소 내에서 레이아웃 애니메이션을 실행하고 싶다면
layoutScroll 속성을 꼭 넣어주세요
그럼 라이브러리가 자식을 측정할 때 요소의 스크롤 오프셋을 고려할 수 있습니다.</p>
<pre><code>&lt;motion.div layoutScroll style={{ overflow: &quot;scroll&quot; }}/&gt;</code></pre><h3 id="coordination-layout-animations">coordination layout animations</h3>
<p>컴포넌트가 리렌더링 되면서 레이아웃이 변경되면 애니메이션이 자동으로 트리거 됩니다.
그런데 만약 동시에 리렌더링 되는 일은 없지만 서로의 레이아웃에 영향을 주는 컴포넌트가 함께 있다면 어떨까요?
예를 들어 FAQ등에서 자주 쓰는 아코디언 메뉴가 있겟죠</p>
<p>메뉴 하나를 클릭해 펼치는 경우를 생각해봅시다.
클릭된 메뉴는 리렌더링 되지만 그 옆에 있는 다른 메뉴는 레이아웃 변경을 감지하지 못합니다.
이럴때는 <code>LayoutGroup</code>컴포넌트를 사용해 함께 묶어주세요</p>
<pre><code>import { LayoutGroup } from &quot;framer-motion&quot;

function List() {
  return (
    &lt;LayoutGroup&gt;
      &lt;Accordion /&gt;
      &lt;Accordion /&gt;
    &lt;/LayoutGroup&gt;  
  )
}</code></pre><p>이렇게 그룹화된 컴포넌트 중 하나에서 레이아웃 변경이 감지되면 부가적인 리렌더링 없이 다른 모든 컴포넌트들에게도 레이아웃 애니메이션이 발생합니다.</p>
<h3 id="shared-layout-animations">Shared layout animations</h3>
<p>layoudId 속성을 가진 기존 컴포넌트와 일치하는 새로운 컴포넌트가 추가될 경우, 이전 컴포넌트에서 자동으로 애니메이션이 적용됩니다.
이를 통해 이전 컴포넌트에서 새 컴포넌트를 보여줄 때 자연스러운 처리가 가능합니다.</p>
<pre><code>&lt;ul&gt;
    {tabs.map((item) =&gt; (
      &lt;li
        key={item.label}
        className={item === selectedTab ? &quot;selected&quot; : &quot;&quot;}
        onClick={() =&gt; setSelectedTab(item)}
      &gt;
        {`${item.icon} ${item.label}`}
        {item === selectedTab ? (
          &lt;motion.div className=&quot;underline&quot; layoutId=&quot;underline&quot; /&gt;
        ) : null}
      &lt;/li&gt;
    ))}
&lt;/ul&gt;</code></pre><h2 id="gestures">gestures</h2>
<p>Motion은 리엑트에서 제공하는 기본 이벤트 리스너를 확장해 제스처 애니메이셔늘 제공합니다.
motion 컴포넌트에 hover,tap,pan,viewport,drag등의 제스처 이벤트를 붙일 수 있습니다.</p>
<h3 id="animation-helpers">Animation helpers</h3>
<p>motion 컴포넌트는 다양한 애니메이션 제스쳐 속성을 제공합니다.
whileHover,whileTap,whileFocus,whileDrag,whileInview</p>
<pre><code>&lt;motion.button
  whileHover={{
    scale: 1.2,
    transition: { duration: 1 },
  }}
  whileTap={{ scale: 0.9 }}
/&gt;</code></pre><p>애니메이션할 값을 직접 입력해도 되고, variant속성을 통해 정의된 이름으로 설정할 수도 있습니다.
이 경우, variants에 정의된 속성명을 자식 요소에서 그대로 사용할 수 있습니다.</p>
<pre><code>
const buttonVariants = {
  hover: { scale: 2, rotzteZ: 90 }
};

&lt;motion.button
  whileHover=&quot;hover&quot;
  variants={buttonVariants}
/&gt;
</code></pre><h3 id="hover">Hover</h3>
<p>호버 제스처는 포인터가 컴포넌트 위로 이동하거나 컴포넌트를 떠날 때 감지합니다.
onMouseEnter, onMouseLeave와 다르게 실제 마우스 이벤트 결과가 있을때만 실행됩니다.</p>
<pre><code>&lt;motion.a
    whileHover={{ scale: 1.1 }}
  onHoverStart={e =&gt; {}} // 컴포넌트 위로 포인터를 가져갈 때 실행되는 콜백 함수
  onHoverEnd={e =&gt; {}}   // 컴포넌트에서 포인터가 떠날 때 실행되는 콜백 함수
/&gt;</code></pre><h3 id="tap">Tap</h3>
<p>tap 제스쳐는 포인터가 동일한 컴포넌트를 눌렀다 뗄 때 감지됩니다.
어떤 구성요소에 탭핑이 성공적으로 완료되면 tap이벤트를 실행하고, 컴포넌트 외부에서 탭핑이 종료되면 tapCancel이벤트를 실행합니다.
만약 컴포넌트가 드래그 가능한 컴포넌트의 자식인 경우 제스처 중 포인터가 3픽셀 이상 이동하면 탭 제스처가 자동으로 취소됩니다.</p>
<pre><code>&lt;motion.div
  whileTap={{ scale: 0.9 }}
    onTap={e =&gt; {}}       // 탭 제스처가 성공적으로 종료될 때 실행되는 콜백 함수
    onTapStart={e =&gt; {}}  // 탭 제스처가 시작될 때 실행되는 콜백 함수
    onTapCancel={e =&gt; {}} // 탭 제스처가 컴포넌트 외부에서 끝날 때 실행되는 콜백 함수
/&gt;</code></pre><h3 id="drag">Drag</h3>
<p>포인터가 구성요소를 누르고 3픽셀 이상 이동할 때를 인식합니디ㅏ.
포인터를 놓으면 제스처가 종료됩니다.
drag=&#39;x&#39;를 통해 x방향 또는 y방향으로만 드래그 되도록 설정할 수 있습니다.</p>
<pre><code>&lt;motion.div 
    drag
    dragConstraints={{top:100}} // 드래그 허용 영역 (useRef hook으로 생성된 컴포넌트도 지정 가능)
    dragSnapToOrigin            // 드래그한 요소를 놓으면 원점으로 되돌아감
    dragElastic                 // constraints 밖으로 허용되는 움직임의 정도 (0=없음, 1=전체)
    dragTransition={}           // 드래그 요소의 관성(inertia) 애니메이션을 지정
 /&gt;</code></pre><h2 id="scroll-animations">Scroll Animations</h2>
<p>motion에는 2가지의 스크롤 애니메이션 유형이 있습니다.</p>
<ul>
<li>스크롤 링크(Scroll-linked) : 스크롤 진행 상황이 애니메이션 진행 상황과 연결됨</li>
<li>스크롤 트리커(Scroll-triggered) : 요소가 뷰포트에 들어오거나 나갈 때 애니메이션이 트리거됨</li>
</ul>
<h3 id="scroll-linked-animations">Scroll-linked animations</h3>
<p>모션 값 (Motion Vlaues)와 <a href="https://www.framer.com/docs/use-scroll/">useScroll</a> 훅을 사용해 구현 할 수 있습니다.</p>
<blockquote>
<p>useScroll
기본적으로 페이지 스크롤을 추적하며, 4개의 모션 값을 반화합니다.</p>
</blockquote>
<ul>
<li>scrollX,scrollY, :  절대 위치 값</li>
<li>scrollXProgress,scrollYProgress : 정의된 오프셋 사이의 스크롤 값 (0 -1)</li>
</ul>
<pre><code>import { motion, useScroll } from &quot;framer-motion&quot;

function Component() {
  const { scrollYProgress } = useScroll();

  return (
    &lt;motion.div style={{ scaleX: scrollYProgress }} /&gt;  
  )
}</code></pre><h3 id="scroll-triggerd-animations">scroll-triggerd animations</h3>
<p>whileInview 속성 세트(&amp;트랜지션) 를 정의하여 요소가 뷰에 있을 때 애니메이션을 적용할 수 있습니다.</p>
<pre><code>
&lt;motion.div
  initial={{ opacity: 0 }}
  whileInView={{ opacity: 1 }}
  viewport={} // 뷰포트가 감지되는 방식을 정의하는 뷰포트 옵션 개체
  onViewportEnter={()=&gt;{}}  // 뷰포트에 진입할 때 호출 (단, 브라우저가 IntersectionObserver를 지원해야 함)
  onViewportLeave={()=&gt;{}}  // 뷰포트에서 떠날 때 호출 (단, 브라우저가 IntersectionObserver를 지원해야 함)
/&gt;</code></pre><p>Viewport Options</p>
<ul>
<li><p>once (boolean)
true인 경우, 뷰포트에 들어가면 whileInView 상태가 유지됩니다. 즉, 뷰포트 콜백이 호출되지 않습니다.</p>
</li>
<li><p>root (RefObject<Element>)
viewport.root와 조상 요소에 ref 객체를 넘기면, 해당 요소를 뷰포트 측정에 사용합니다.</p>
</li>
<li><p>margin (string)
뷰포트 진입을 판단할 때 마진을 추가할 수 있습니다.</p>
</li>
<li><p>amount (&quot;some&quot; | &quot;all&quot; | number)
요소가 쬐끔(some) 보일 때 뷰포트와 교차한다고 판단할지, 전부(all) 또는 일정한 만큼 보일 때 판단할지 정의합니다.</p>
<pre><code>function Component() {
const scrollRef = useRef(null)

return (
  &lt;div ref={scrollRef}&gt;
    &lt;motion.div
      variants={emojiVariants}
      initial=&quot;hidden&quot;
      whileInView=&quot;visible&quot;
      viewport={{ root: scrollRef, once: true, amount: 0.3 }}
    /&gt;
  &lt;/div&gt;
)
}
</code></pre></li>
</ul>
<pre><code>
## Transition
두 값 사이에 애니메이션을 적용할 때 사용합니다.
</code></pre><p>  &lt;motion.div
  animate={}
  transition={{ duration: 1 }}
/&gt;</p>
<pre><code>
이렇게 정의한 트랜지션은 모든 속성에 적용되지만, 값에 각각 다른 트랜지션을 적용하는 것도 가능합니다.</code></pre><p>  &lt;motion.div
  transition={{
        default: { duration: 1 },
    opacity: {
      delay: 0.3
    },
    y: {
      type: &quot;spring&quot;,
      damping: 3,
      stiffness: 50,
      restDelta: 0.01,
      duration: 0.3
    }
  }}
/&gt;</p>
<pre><code>
### Orchestration
  다음과 같은 속성을 통해 트랜지션을 보다 심미적으로 만들 수 있습니다.
- delay (number)
말그대로 n초 간의 딜레이를 적용합니다.
- delayChildren (number)
variants를 사용하는 경우, n초 후 자식 컴포넌트의 애니메이션이 시작됩니다.
- staggerChildren (number)
variants를 사용할 때, 자식 컴포넌트 간 애니메이션 간격을 설정합니다.
- staggerDirection (number)
스태거를 적용할 방향입니다. -1을 주면 역순이 됩니다.
- when (false | &quot;beforeChildren&quot; | &quot;afterChildren&quot; | string)
varaints를 사용할 때, 하위 트랜지션이 시작하기 전 또는 완료한 후에 이 트랜지션을 완료할지 정합니다.
- repeat (number)
트랜지션을 반복할 횟수입니다. Infinity로 설정하면 영원히 계속됩니다
 값을 설정하지 않으면 repeatType애니메이션이 반복됩니다.
- repeatType (&quot;loop&quot; | &quot;reverse&quot; | &quot;mirror&quot;)
트랜지션이 반복되는 경우 어떻게 처리할지 정의합니다.

### Spring
motion의 좋은 점은 보다 사실적인 애니메이션을 표현해준다는 것입니다.
- bounce (number)
말그대로 얼마나 바운스바운스할지 정합니다
 0이라면 탄력이 없고, 1은 매우 탄력적입니다. duration 기본값은 0.25로 설정됩니다.
- damping (number)
저항하는 반대 힘. 0으로 설정하면 스프링이 무한으로 바운스합니다. (기본값: 10)
- mass (number)
질량. 값이 높을수록 움직임이 둔해집니다. (기본값: 1)
- stiffness (number)
값이 높을수록 더 갑작스럽게 움직입니다. (기본값: 100)
- velocity (number)
스프링의 초기 속도.
- restSpeed (number)
절대 속도가 이 값 아래로 떨어지고 델타가 restDelta보다 작으면 애니메이션을 종료합니다. (기본값: 0.01)
- restDelta (number)
거리(distance)가 이 값보다 작고 속도가 restSpeed 보다 작으면 애니메이션을 종료합니다. (기본값: 0.01)


## Motion Values
모든 모션 컴포넌트는 내부적으로 MotionValue를 사용해 애니메이션 값의 상태와 속도를 추적합니다.
이는 자동으로 생성되지만 사용자가 원한다면 수동으로 생성해 넣을 수 있습니다.
수동으로 MotionValue를 설정하는 경우,
- 상태를 Set/Get 할 수 있습니다.
- 여러 컴포넌트 간 모션을 동기화할 수 있습니다.
- useTransform 훅으 통해 motionValue를 연결할 수 있습니다.
- 리엑트의 렌더링 주기를 트리거하지 않고도 시각적 속성을 업데이트할 수 있습니다.
- 업데이트를 구독할 수 있습니다.


## OverView
MotionValue는 useMotionValue 훅으로 생성할 수 있습니다.
문자열 또는 숫자로된 초기값을 지정할 수 있습니다.
사용자는 set()메서드로 업데이트 할 수 있고, get()으로 값을 읽어올 수 있습니다.
  getVelocity()로 초당 계산된 속도를 받아 올 수도 있습니다.
  세부 메서느는 [여기](https://www.framer.com/docs/motionvalue/#motionvalue)를 참고하세요.

재밌는 점은 MotionValue가 바뀌더라도 컴포넌트는 리렌더링되지 않는다는 것입니다.
`&lt;motion.div/&gt;`가 움직인다고 한들 이 컴포넌트는 다시 랜더링되지 않기 때문에 여기 붙어있는 api가 계속해서 호출된다거나 하는 불상사는 없겠죠
</code></pre><p>  import { motion, useMotionValue } from &quot;framer-motion&quot;</p>
<p>function App() {
  const x = useMotionValue(0)</p>
<pre><code>x.set(100);
x.get();
x.getVelocity();</code></pre><p>}</p>
<pre><code>
### Injecting MotionValues
일단 motionValue가 생성되면, 시각적 속성을 줬던 것 처럼 모션 컴포넌트에 연결할 수 있습니다.
HTML이라면 style 속성을 통해, SVG라면 SVG 속성을 통해 넣어주면 됩니다.
</code></pre><p>const x = useMotionValue(0);
const cx = useMotionValue(0);</p>
<p>&lt;motion.div style={{ x }} /&gt; // HTML
&lt;motion.circle cx={cx} /&gt;    // SVG</p>
<pre><code>

하나의 MotionValue를 여러 개의 컴포넌트에 넣는 것도 가능합니다. 그럼 그 MotionValue를 바라보는 모든 컴포넌트에 영향이 가겠죠


### Responding to changes
  onChange 메서드로 MotionValue에 리스너를 추가할 수도 있습니다.
  리엑트 컴포넌트 안에서 onChange를 호출할 때는 useEffect 훅으로 꼭 감싸주세요.
  onChange는 unsubscribe 함수를 반환하므로 subscriber 중복을 막기 위해 useEffect() 안에서 쓰여야 합니다.</code></pre><p>  useEffect(() =&gt; x.onChange(latest =&gt; {}), [])</p>
<pre><code>

### Composing MotionValues
  useTransform 등의 훅을 써서 MotionValue값을 구성할 수 있습니다.

useTransform : 값 범위를 다른 값 범위로 매핑할 수 있습니다.


박스를 왼쪽으로 드래그하면 scale(3)로, 오른쪽으로 드래그하면 scale(0.1)로 바뀌도록 하는 예제입니다.
</code></pre><p>import { useEffect } from &quot;react&quot;;
import { motion, useMotionValue, useTransform } from &quot;framer-motion&quot;;
import &quot;./styles.css&quot;;</p>
<p>export default function App() {
  const x = useMotionValue(0);
  const scale = useTransform(
    x,
    // Map x from these values:
    [-400, 400],
    // Into these values:
    [3, 0.1]
  );</p>
<p>  useEffect(() =&gt; {
    x.onChange(() =&gt; {
      console.log(x.get());
    });
  }, [x]);</p>
<p>  return (
    <div className="app">
      &lt;motion.div
        className=&quot;box&quot;
        drag=&quot;x&quot;
        dragSnapToOrigin
        style={{ x, scale }}
      /&gt;
    </div>
  );
}</p>
<p>  ```</p>
<h3 id="출처">출처</h3>
<p> <a href="https://nykim.work/114">https://nykim.work/114</a>
<a href="https://www.framer.com/developers/">https://www.framer.com/developers/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 18 변경점]]></title>
            <link>https://velog.io/@dae_eun2/React-18-%EB%B3%80%EA%B2%BD%EC%A0%90</link>
            <guid>https://velog.io/@dae_eun2/React-18-%EB%B3%80%EA%B2%BD%EC%A0%90</guid>
            <pubDate>Fri, 04 Nov 2022 09:51:14 GMT</pubDate>
            <description><![CDATA[<p>21년 6월 8일, 리엑트 코어 개발자들이 리엑트 공식 홈페이지에 
<a href="https://reactjs.org/blog/2021/06/08/the-plan-for-react-18.html#whats-coming-in-react-18">리엑트 18에 새롭게 추가되는 기능들</a>을 소개했습니다.</p>
<h1 id="new-suspense-ssr-architecture">New Suspense SSR Architecture</h1>
<p>리엑트의 서버사이드렌더링은 다음의 스텝으로 이루어집니다.</p>
<ul>
<li>서버사이드에서 페이지를 그리는데 필요한 데이터들을 fetch합니다.</li>
<li>렌더링 서버(서버사이드)에서 데이터를 처리한 후 html을 response에 내려보냅니다.</li>
<li>클라이언트(브라우저)에서는 필요한 자바스크립트 코드를 다운받습니다.</li>
<li>클라이언트(브라우저)에서는 서버사이드에서 렌더링된 html파일에 자바스크립트 파일은 연결시킵니다 (hydration)</li>
</ul>
<p>각 단계들은 synchronous하게 진행되며 전체 과정은 Top-Bottom / WaterFall모델의 형태를 띄고 있습니다.
전체 컴포넌트 중 특정 부분만 느리게 처리가 되어도 앱 전체가 제일 느린 부분이 완전히 준비될 때까지 기다려야 하므로 비효율적인 시간소모가 발생합니다.</p>
<p>서버사이드에서 html을 클라이언트로 보내주기 전에 먼저 렌더링에 필요한 데이터들을 api서버에 호출해야 하는 상황이 있습니다.
api응답이 느리다면 해당 api를 사용하는 컴포넌트로 인해 사용자는 첫 페이지가 뜨기 전까지 오랜 시간을 기다려야 합니다.
data fetching은 클라이언트단의 코드를 개선한다고 완전히 해결할 수 없는 상황이기에 초기 페이지를 빠르게 보내기 위해서 아래의 선택지에서 취사선택을 해야합니다.</p>
<p><strong>1. SSR에서 해당 API fetch를 제외시키고 CSR단계에서 useEffect에서 호출하는것으로 대신합니다.</strong></p>
<p><strong>2. 페이지를 구성하는 10개의 컴포넌트 중 9개의 컴포넌트가 해당 api응답과 무관하더라도 딜레이를 감수합니다.</strong></p>
<p>초기페이지가 브라우저에서 그려지기 시작하더라도 아직 딜레마가 하나 더 남아있습니다.
브라우저는 리엑트와 같은 싱글페이지 웹 어플리케이션을 실행할 때 자바스크립트를 다음과 같은 과정으로 처리하는데요,</p>
<ol>
<li>Fetch JS</li>
<li>Load JS</li>
<li>Hydrate
페이지가 버튼클릭과 같은 유저 인터렉션에 반응하기 위해서는 이벤트핸들러와 같은 JS 코드들이 html에 동화되어야 합니다.
이것을 수화(hydration)과정이라고 하는데 자바스크립트 파일 번들용량이 크거나 복잡하다면 위의 과정이 오래걸릴 수밖에 없습니다.
또한 모든 컴포넌트가 완전히 수화과정을 완료하기 전까지는 페이지 기능을 정상적으로 사용할 수 없습니다.</li>
</ol>
<p>고사양의 컴퓨터에서 이런 딜레이는 큰 문제가 되지 않을 수 있습니다.
하지만 사양이 낮은 디바이스 환경에서 hydration딜레이는 충분히 발생할 수 있으며 나쁜UX를 젝오하는 원인이 될 수 있기 때문에 개발자가 고려해야할 사항입니다.</p>
<p><strong>1. 용량이 크거나 복잡한 로직이 담긴 컴포넌트를 제외시킨다.</strong>
<strong>2. JS 코드들이 완전히 수화될때까지 유저를 기다리게 한다.</strong></p>
<p>결국 리엑트를 사용하는 모던 웹에서 interaction blocking과 slow FTP(Frist time to Paint)두가지의 UX문제는 서로 트레이드 오프적인 관계를 지닐 수 밖에 없습니다.</p>
<h3 id="리엑트-17버전에서의-기존-폭포수-렌더링-방식">리엑트 17버전에서의 기존 폭포수 렌더링 방식</h3>
<p>서버에서 각 컴포넌트 렌더링에 필요한 데이터를 먼저 처리하고 전체 페이지를 렌더링해 클라이언트에 내려줍니다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/c0a1932b-dc39-4861-bed8-d5198ed381c4/image.png" alt="">
자바스크립트 번들을 다운받고 브라우저가 불러올때까지 기다립니다.
그 이후 hydration을 진행합니다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/77f17afd-b25c-4171-83c7-729881c7c7a3/image.png" alt=""></p>
<p>18버전 부터는 유저에게 처음 보여지는 페이지 전체를 그려 내려주는것이 아니라 빠르게 준비되는 부분부터 렌더링/수화 시켜주며 이 기능을 <code>HTML Streaming</code>과 <code>Selective Hydration</code>으로 명명하고 있습니다.</p>
<p>위의 두가지 기능이 어떻게 사용되어지는지 react18저장소 에서 친절하게 설명해주고 있는데,
아래에서 하나씩 살펴보겠습니다.</p>
<h3 id="html-streaming">HTML Streaming</h3>
<p>서버에서 html을 보내주는것을 html streaming이라고 하는데, renderToString을 사용한 전통적인 방식으로 SSR을 구현하면 브라우저에서는 서버에서 보내주는 html페이지를 하나의 파일로 통째로 받았었습니다.
새로운 버전에서 서버는 pipeToNodeWritable을 사용해 html코드를 작은 청크로 나누어 보내줄 수 있습니다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/80fb07ea-b7ab-4804-8093-f2db0d65eb85/image.png" alt=""></p>
<pre><code>&lt;Layout&gt;
  &lt;NavBar /&gt;
  &lt;Sidebar /&gt;
  &lt;RightPane&gt;
    &lt;Post /&gt;
    &lt;Suspense fallback={&lt;Spinner /&gt;}&gt;
      &lt;Comments /&gt;
    &lt;/Suspense&gt;
  &lt;/RightPane&gt;
&lt;/Layout&gt;</code></pre><p>전체 앱 중 첫 페이지를 렌더링하는데 시간이 오래걸리는 <code>&lt;Commnets /&gt;</code>컴포넌트를 <code>&lt;Suspnese &gt;</code>로 감쌋습니다.
리엑트에게 해당 컴포넌트를 렌더링할 준비가 되기 전까지는 fallback props로 넘긴 <code>&lt;Spinner /&gt;</code>를 대신 보여달라고 말한것인데요,
실제로 사용자는 아래와 같은 페이지를 보게됩니다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/bc1f8e75-138f-4edb-8dda-f4a19753abc6/image.png" alt=""></p>
<p>화면 아래에서는 이런 일들이 일어나고 있습니다.
<code>&lt;Comments /&gt;</code>와 관련된 태그가 전ㄹ혀 보이지를 않습니다.</p>
<pre><code>&lt;main&gt;
  &lt;nav&gt;
    &lt;!--NavBar --&gt;
    &lt;a href=&quot;/&quot;&gt;Home&lt;/a&gt;
   &lt;/nav&gt;
  &lt;aside&gt;
    &lt;!-- Sidebar --&gt;
    &lt;a href=&quot;/profile&quot;&gt;Profile&lt;/a&gt;
  &lt;/aside&gt;
  &lt;article&gt;
    &lt;!-- Post --&gt;
    &lt;p&gt;Hello world&lt;/p&gt;
  &lt;/article&gt;
  &lt;section id=&quot;comments-spinner&quot;&gt;
    &lt;!-- Spinner --&gt;
    &lt;img width=400 src=&quot;spinner.gif&quot; alt=&quot;Loading...&quot; /&gt;
  &lt;/section&gt;
&lt;/main&gt;</code></pre><p>서버에서 <code>&lt;Comments/&gt;</code>컴포넌트를 렌더링할 준비가 모두 끝나면 리엑트는 추가적인 html코드를 스트리밍하는데요, 앞서 대신 보여줬던 fallback엘리먼트와 대체해줍니다.</p>
<pre><code>&lt;div hidden id=&quot;comments&quot;&gt;
  &lt;!-- Comments --&gt;
  &lt;p&gt;First comment&lt;/p&gt;
  &lt;p&gt;Second comment&lt;/p&gt;
&lt;/div&gt;
&lt;script&gt;
  // This implementation is slightly simplified
  document.getElementById(&#39;sections-spinner&#39;).replaceChildren(
    document.getElementById(&#39;comments&#39;)
  );
&lt;/script&gt;</code></pre><p><img src="https://velog.velcdn.com/images/dae_eun2/post/daebdcbc-fd2f-4c8f-aee2-6ffc0388716a/image.png" alt=""></p>
<p>이전 버전까지는 전체 페이지가 준비되기까지 사용자가 페이지의 다른부분을 전혀 볼 수 없었습니다.
준비된 부분부터 보는것이 가능하다는 것은 렌더링 전체 과정에 있어서
FTTB(First Time To Byte)시간이 줄어든다는 뜻이며, 
이것은 단순한 사용자 경험을 떠나 정량적인 수치로도 렌더링 퍼포먼스의 향상이 있다는 것을 의미합니다.</p>
<p>위의 예제코드에서 등장한 <code>&lt;Suspense&gt;</code>는 전체 페이지를 각각의 작은 청크로 나누어 렌더링 할 수 있게 도와줍니다.
응답을 받는데 오래 걸리는 컴포넌트에는 <code>&lt;Suspence&gt;</code>를 사용해 나머지 영역의 초기 렌더링 속도에 영향을 미치지 않게 할 수 있습니다.</p>
<p><code>&lt;Suspence&gt;</code>태그는 리엑트18 에서 처음 소개된 것이 아닙니다.
이 태그는 2018년도 처음 소개되어 클라이언트 사이드 렌더링 단계에서 큰 번들의 자바스크립트 코드들을 청크들로 나누어 로드될 수 있게 해주는 역할을 React.Lazy와 함께 수행했습니다. 이 기능을 코드 스플리팅 이라 합니다.</p>
<p>React18이전에도 코드 스플리팅을 구현해 큰 번들의 자바스크립트 코드를 청크로 잘게 나누어 로드되는 시간들을 분산시킬 수 있었습니다.
하지만 서버사이드 렌더링을 구현할 때 사용되는 <code>renderToString</code>과 함께 사용할 수 없었고, 정상적인 SSR환경을 구축하기 위해서는 loadable-component와 같은 서드파티 라이브러리를 함께 사용해야 했습니다.
React18부터는 서드파티 라이브러리를 활용하지 않고도 <code>&lt;Suspense&gt;</code>를 SSR환경에서도 정상적으로 이용할 수 있게 되었습니다.</p>
<h2 id="selective-hydrating선택적-수화">selective hydrating(선택적 수화)</h2>
<p>이제 최소한 복잡하고 용량이 큰 <code>&lt;Comments/&gt;</code>컴포넌트 때문에 페이지 전체가
FCP(First Content Paint)에서 손해를 보는 상황에서는 벗어날 수 있게 되었습니다.
하지만 극단적인 예시를 들어 일부 컴포넌트의 용량이 너무나 크고 복잡하다고 가정해볼까요?</p>
<p>다른 엘리먼트들이 자바스크립트를 다운받고 hydration할 준비를 다 마쳤더라도 언제 풀릴지 모르는, <code>&lt;Spinner /&gt;</code>만 계속 바라보고 있어야 할지도 모릅니다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/e739824c-e481-48bc-8d64-f56507125cea/image.png" alt="">
이렇게 렌더링하는데 비용이 큰 컴포넌트들을 <code>&lt;Suspence&gt;</code>로 감쌈으로 인해 해당 부분이 여전히 fallback엘리먼트는 내보내고 있어도 그와 상관없이 페이지의 다른 부분은 hydrating을 시작할 수 있게 되었습니다.</p>
<p>이전 버전의 Top-Down방식과 다르게 JS번들이 로드된 컴포넌트들은 먼저 hydration을 시작할 수 있습니다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/5269b435-a0c4-46cf-b788-35b419fabeb2/image.png" alt=""></p>
<p>기존에는 번들 로드가 느린 <code>&lt;Comments/&gt;</code>때문에 모든 페이지가 완전하게 인터렉션하게 될때까지 기다려야 했지만 이제는 사이드바나 네비게이션바가 본인들의 역할을 보다 빨리 할 수 있게 되었습니다.
유저가 해당 페이지가 완전히 동작하기 이전에 다른곳으로 이동하고 싶다면 이미 hydration이 완료된 네비게이션 바를 이용할 수 있습니다.
빠른 인터렉션을 제공해 더 나은 UX를 제공할 수 있게 되는 것입니다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/1bf89ce6-5ccb-4482-a814-4f39c40da6c7/image.png" alt="">
또한 사용자의 인터렉션에 따라 어떤 것을 먼저 hydration시킬지에 대한 우선순위를 정할 수 있게 되었습니다.
아래와 같이 <code>&lt;SideBar/&gt;</code>와 <code>&lt;Comments/&gt;</code>두 태그가 <code>&lt;Suspense&gt;</code>로 둘러싸여 있다고 할 때 일반적으로 hydration은 돔트리에 배치된 순서에 따라 순차적으로 진행이 됩니다.
다음과 같은 상황에서는<code>&lt;SideBar/&gt;</code> 가 <code>&lt;Comments/&gt;</code>보다 먼저 hydration이 진행됩니다.</p>
<pre><code>&lt;Layout&gt;
  &lt;NavBar /&gt;
  &lt;Suspense fallback={&lt;Spinner /&gt;}&gt;
    &lt;Sidebar /&gt;
  &lt;/Suspense&gt;
  &lt;RightPane&gt;
    &lt;Post /&gt;
    &lt;Suspense fallback={&lt;Spinner /&gt;}&gt;
      &lt;Comments /&gt;
    &lt;/Suspense&gt;
  &lt;/RightPane&gt;
&lt;/Layout&gt;</code></pre><p><img src="https://velog.velcdn.com/images/dae_eun2/post/345b819d-40cc-45c0-a4e2-b32217e9575c/image.png" alt=""></p>
<p>사용자가 <code>&lt;SideBar/&gt;</code>가 hydration되기 이전에 <code>&lt;Commnets/&gt;</code>에 대해 관심을 가지고 버튼 클릭을 했다면
<img src="https://velog.velcdn.com/images/dae_eun2/post/d193a542-e0f4-4439-bca0-19888115423c/image.png" alt="">
리엑트는 해당 클릭 이벤트를 기록하고 <code>&lt;Comments/&gt;</code>부분에 대한 hydration의 우선순위를 높여서 진행합니다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/5f40dc24-2c7b-4940-93ea-8c5b2b6bbca0/image.png" alt="">
<code>&lt;Comments/&gt;</code> 에 대한 hydration이 완료되면 앞서서 기록 놓았던 클릭 이벤트를 실행하고 남은<code>&lt;Sidebar/&gt;</code> 의 처리도 마저 진행합니다.<img src="https://velog.velcdn.com/images/dae_eun2/post/0feccc7d-2194-4086-9fb1-1c1e164ba904/image.png" alt=""></p>
<p><strong>selected hydrating</strong> 로 인해 항상 정해진 순서를 따르지 않고 사용자가 관심 있는 부분부터 인터렉션 가능한 컨텐츠를 제공할 수 있게 되었습니다.</p>
<p><code>&lt;Suspense/&gt;</code>를 조금 더 세분화 해서 여러 부모 자식 관계를 가진 컴포넌트를 대상으로 적용시킨다면 해당 기능의 장점이 좀 더 극명하게 나타납니다.</p>
<pre><code>&lt;Layout&gt;
  &lt;NavBar /&gt;
  &lt;Suspense fallback={&lt;BigSpinner /&gt;}&gt;
    &lt;Suspense fallback={&lt;SidebarGlimmer /&gt;}&gt;
      &lt;Sidebar /&gt;
    &lt;/Suspense&gt;
    &lt;RightPane&gt;
      &lt;Post /&gt;
      &lt;Suspense fallback={&lt;CommentsGlimmer /&gt;}&gt;
        &lt;Comments /&gt;
      &lt;/Suspense&gt;
    &lt;/RightPane&gt;
  &lt;/Suspense&gt;
&lt;/Layout&gt;</code></pre><p>아래의 그림에서 사용자가 <code>&lt;Comments/&gt;</code>컴포넌트 안에 있는 <code>&lt;Comment/&gt;</code>들 중 첫번째 엘리먼트를 먼저 클릭했다고 가정하겠습니다.
클릭한 요소를 둘러싸고 있는<code>&lt;Suspense/&gt;</code>중 최상위 부모 엘리먼트 부터 hydration을 시작하게 됩니다.
인터렉션과 관계없는 <code>&lt;Suspense/&gt;</code>로 둘러싸인 형제 엘리먼트는 일단 hydration을 스킵하고 인터렉션이 발생한 요소부터 실행하기 때문에
hydration이 즉시 일어나는 것 같은 느낌을 줄 수 있습니다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/8511a0bc-d044-4278-8539-56012d8375a1/image.png" alt=""></p>
<h1 id="state-batch-update">State batch update</h1>
<p>배칭(batching)은 업데이트 대상이 되는 상태값들을 하나의 그룹으로 묶어서 한번의 리렌더링에 업데이트가 모두 진행될 수 있게 해주는 것을 의미합니다.</p>
<p>한 함수 안에서 setState를 아무리 많이 호출시키더라도 리렌더링은 단 한번만 발생합니다.</p>
<pre><code>function handleClick() {
    setCount(c =&gt; c + 1); // Does not re-render yet
    setFlag(f =&gt; !f); // Does not re-render yet
    // React will only re-render once at the end (that&#39;s batching!)
  }</code></pre><p>batch update를 사용함으로 불필요한 리렌더링을 줄일 수 있어서 퍼포먼스 적으로 큰 이점을 얻을 수 있는데요,
이전 버전에서도 이런 batch update가 지원되었지만 클릭과 같은 브라우저 이벤트에서만 적용이 가능하고 api호출에 콜백으로 넣은 함수나 timeouts함수에서는 작동하지 않았습니다.</p>
<pre><code>function handleClick() {
    fetchSomething().then(() =&gt; {
      // React 17 and earlier does NOT batch these because
      // they run *after* the event in a callback, not *during* it
      setCount(c =&gt; c + 1); // Causes a re-render
      setFlag(f =&gt; !f); // Causes a re-render
    });
  }

setTimeout(() =&gt; {
  setCount(c =&gt; c + 1); // re-render occurs
  setFlag(f =&gt; !f); // re-render occurs again!
}, 1000);</code></pre><h2 id="automatic-batching">automatic batching</h2>
<p>18버전 부터는 <code>React.createRoot</code>를 이용해 브라우저 이벤트 뿐만 아니라
timeouts,promises를 비롯한 모든 이벤트에서 batching이 자동으로 적용되게 할 수 있습니다.
리엑트 팀은 여러 상황에서 발생할 수 있는 렌더링 횟수를 줄임으로 퍼포먼스 개선을 기대할 수 있다고 하는데요 , 이 기능을<strong>automatic batching</strong>이라 합니다.</p>
<p>앞선 예제코드에서는 작동하지 않았던 배칭 업데이트가 리엑트 18버전의 <code>createRoot</code>아래에서는 자동으로 적용되는 것을 확인할 수 있습니다.</p>
<pre><code>// onClick 핸들러
function handleClick() {
  setCount(c =&gt; c + 1);
  setFlag(f =&gt; !f);
  // React will only re-render once at the end (that&#39;s batching!)
}

// setTimeout
setTimeout(() =&gt; {
  setCount(c =&gt; c + 1);
  setFlag(f =&gt; !f);
  // React will only re-render once at the end (that&#39;s batching!)
}, 1000);

// fetch API
fetch(/*...*/).then(() =&gt; {
  setCount(c =&gt; c + 1);
  setFlag(f =&gt; !f);
  // React will only re-render once at the end (that&#39;s batching!)
})

// addEventListener callback
elm.addEventListener(&#39;click&#39;, () =&gt; {
  setCount(c =&gt; c + 1);
  setFlag(f =&gt; !f);
  // React will only re-render once at the end (that&#39;s batching!)
});</code></pre><p>만약 automatic batching을 사용하고 싶지 않다면, ReactDom.flushSync()를 이용해 해당 상태 업데이트 호출을 대상에서 제외시킬 수 있습니다.</p>
<pre><code>
import { flushSync } from &#39;react-dom&#39;; // Note: react-dom, not react

function handleClick() {
  flushSync(() =&gt; {
    setCounter(c =&gt; c + 1);
  });
  // React has updated the DOM by now
  flushSync(() =&gt; {
    setFlag(f =&gt; !f);
  });
  // React has updated the DOM by now
 }</code></pre><h1 id="transition">Transition</h1>
<p>해당 기능은 상태 업데이트를 함에 있어서 우선순위를 정하는데 도움을 줍니다.
리엑트 팀은 상태 업데이트 대상을 두가지로 나우었으며 이를 통해 transition이 의미하는 바가 무엇인지를 파악할 수 있습니다.</p>
<ul>
<li>Urgent updates : 버튼 클릭, 키보드 입력과 같이 직관적으로 보았을 때 업데이트가 즉각적으로 일어나는 것을 기대하는 상태 값들을 대상으로 합니다.</li>
<li>Tranistion updates : 사용자가 상태 값의 변화에 따른 모든 업데이트가 뷰에 즉각적으로 일어나는 것을 기대하지 않습니다.</li>
</ul>
<h2 id="어떤-문제를-해결하나요">어떤 문제를 해결하나요?</h2>
<p>타이핑과 같이 빈번하게 일어나는 이벤트에 따라 큰 화면이 업데이트 되어야 한다면 각 이벤트마다 일어나느 리렌더링이 해당 화면에 렉을 일으키거나 스무스한 UI를 제공하지 못하는 요인으로 작용할 수 있습니다.</p>
<p>검색 사이트에서 auto complete 기능이나 검색 필터링 기능을 사용한다고 했을때 결과값을 이용해 상태를 업데이트 하기 위해 보통 이러한 코드를 작성하게 됩니다.</p>
<pre><code>// show what was typed
setInputValue(input);
 // show results
setSearchQuery(input);</code></pre><p>검색 결과 리스트가 매우 길거나 많지 않더라도 사이트에서 검색 결과 값을 가지고 내부적으로 복잡한 작업을 진행할 수 있으며 유저의 이벤트 값이 약간이라도 달라지더라도 페이지 UI에 큰 변화를 불러 일으키기에 이 때 발생하는 렉을 최적화할 수 있는 명확한 방법을 제시하기가 매우 어렵습니다.</p>
<p>페이지에서 사용자의 타이핑에 따라 화면이 달라지는 부분은 크게 입력 폼 과 결과 창 두가지 입니다.
입력 창은 네이티브 이벤트를 발생하는 UI이므로 유저는 타이핑이 입력창에 즉각적으로 반영되기를 기대할 것입니다.
결과 창은 직관적으로 어디에서 검색결과를 가져오는 작업을 하는 공간으로 느껴지기에 입력창 보다 UI업데이트가 느린것에 대해 자연스럽게 받아 들여집니다.</p>
<p>입력 창과 결과 창에 사용하는 상태 값은 항상 동일한 시간에 업데이트되기 때문에 이로인해 결과 값에 따라 입력 창의 업데이트가 지연될 가능성이 발생합니다.
지금까지는 리엑트가 모든 상태 업데이트의 우선순위를 동일하게 처리하였기 때문에
사람이 기대하는 것에 맞춰서 뷰의 각 부분에 세밀하게 우선순위를 주어 렌더링하는것이 매우 어려웠습니다.</p>
<p>리엑트 18에서 소개된 <code>startTransition</code>을 이용해 각 상태 업데이트에 대한 우선순위를 정해줄 수 있게 되었습니다.</p>
<pre><code>import { startTransition } from &#39;react&#39;;


// Urgent: Show what was typed
setInputValue(input);

// Mark any state updates inside as transitions
startTransition(() =&gt; {
  // Transition: Show the results
  setSearchQuery(input);
});
</code></pre><p>startTransition으로 둘러싸인 부분은 클릭이나 키 입력에 의해 우선순위 높은 상태 업데이트가 발생하게 되면 렌더링 업데이트가 중단되고 키 입력이 다 끝난 이후의 업데이트만 발생하게 됩니다.</p>
<p>transition을 이용해 UI가 크게 달라지는 부분이 빈번하게 발생하더라도 사용자와 페이지간의 상호작용을 신속하고 원활하게 유지할 수 있습니다.
또한 더이상 사용자에게 보여지는 부분과 관련이 없는 컨텐츠를 렌더링하는데 있어서 시간을 낭비하지 않아도 됩니다.</p>
<p>유저에게 transition업데이트가 백그라운드에서 진행됨을 알려주고 싶을 수 있습니다.
이 때는 <code>useTransition</code>훅을 이용해 <code>&lt;Spinner/&gt;</code>와 같은 UI를 표시해줄 수 있습니다.</p>
<pre><code>import { useTransition } from &#39;react&#39;;

const [isPending, startTransition] = useTransition();
...
{isPending &amp;&amp; &lt;Spinner /&gt;}
...</code></pre><p>보통 auto complete 구현을 위해 debounce기능을 사용합니다.
debounce에는 setTimeout을 사용하는데, startTransition은 setTimeout과는 다르게 함수 호출 스케쥴을 뒤로 미루는 것이 아닙니다.
startTransition에 넘겨지는 콜백함수는 동기적으로 호출되며 콜백함수 안에서 일어나는 상태 업데이트는 <code>transition</code>으로 마킹되어 리엑트가 업데이트를 처리할 때 어떤 우선순위로 처리해야 할지를 알려줍니다.</p>
<p>이 말은 즉, timeout 함수와 같이 macroTask에 의해 둘러싸인 상태 업데이트보다 먼저 처리됨을 의미합니다.
고사양의 디바이스에서는 이러한 차이가 미세할 수 있지만, 최적화가 필수적인 디바이스 환경에서 이러한 차이는 큰 영향을 미칠 수 있습니다.</p>
<p>startTransition은 크게 리엑트가 UI업데이트를 위해 크고 복잡한 일을 함으로 써 대기 시간이 발생하거나 느린 네트워크 환경에서 데이터를 받아오기 위해 기다리는 상황에서 사용한다고 합니다.</p>
<h3 id="출처">출처</h3>
<p><a href="https://velog.io/@jay/React-18-%EB%B3%80%EA%B2%BD%EC%A0%90">https://velog.io/@jay/React-18-%EB%B3%80%EA%B2%BD%EC%A0%90</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[useSyncExternalStore]]></title>
            <link>https://velog.io/@dae_eun2/useSyncExternalStore</link>
            <guid>https://velog.io/@dae_eun2/useSyncExternalStore</guid>
            <pubDate>Fri, 04 Nov 2022 08:45:46 GMT</pubDate>
            <description><![CDATA[<p>리엑트 18의 새로운 훅인 <code>useSyncExternalStore</code>에 대해 알아보려한다.
이 훅은 사용해본적이 없으며, 찾아볼 일도 많이 없었다.
그 이유는 리엑트 18에서 제공하는 concurrent feature기능을 기존 앱에 많이 사용하지 않기때문이다.
또 다른 이유는 현재 각 프로젝트에서 사용하고 있는 mobx,redux,recoil과 같은 exteranal store에서 useSyncExternalStore사용이 필요없게 패치를 해놓았기 때문이다.</p>
<p>이 훅에 대해 본격적으로 알아보기에 앞서 먼저 선행해야 할 지식들이 있다.</p>
<h2 id="concurrent-feature">concurrent feature</h2>
<blockquote>
<p>자세한 내용이 아래 링크에서 확인 가능합니다.
<a href="https://velog.io/@jay/Concurrent-React">https://velog.io/@jay/Concurrent-React</a></p>
</blockquote>
<p>간단하게 말해서 렌더링 타이밍 도중 사용자의 입력과 같은 즉각적으로 UI에 적용되어야 하는 부분에 대해 우선순위를 정해 렌더링 할 수 있는 기능을 의미합니다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/c75ea998-bf68-4eb2-a1e9-f4af6e245a04/image.png" alt=""></p>
<h2 id="external-store">external store</h2>
<p>한글로 external store를 따로 번역해서 사용하는 것은 아직 보지 못했다.
리엑트에서 내부적으로 제공하는 useState,useReducer와 같은 상태관리 api가 아니라
자체적으로 상태관리 툴을 만들어 리엑트 훅과 연동시킨 상태관리 라이브러리들을 external state라고 합니다.</p>
<p>이들의 상태 관리 흐름은 리엑트에서 관찰하지 않습니다.</p>
<h2 id="internal-store">internal store</h2>
<p>앞서 나열했던 라이브러리들과 다르게 리엑트에서 제공하는 상태관리 도구입니다.
useState,useReducer,context,props가 이에 해당합니다.</p>
<h2 id="왜-생겼는가">왜 생겼는가?</h2>
<p>useSyncExternalStore이 해결이하는 문제는 concurrent feature에서 발생하는 Tearing이라는 이슈입니다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/1349ffbd-d302-4f1a-9c42-4eaeab6da2f7/image.png" alt=""></p>
<p>리엑트에서 말하는 tearing은 의도치 않게 상태 불일치로 서로 일치하지 않는 시점의 UI가 렌더링되는 것을 의미합니다.</p>
<p>concurrent feature는 렌더링 도중 들어오는 유저 인터렉션에 대해 기존 렌더링을 중지하고 인터렉션에 대한 UI를 먼저 렌더링하기 때문에 아래처럼 빨간색으로
렌더링 트리를 업데이트하는 도중 파란색의 인터렉션으로 인해 트리의 일부분이 파란색으로 렌더링될 수 있습니다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/3db18162-d360-4afb-a850-e4a0e75e39d3/image.png" alt="">
리액트의 internal store는 이러한 concurrent feature에 대비하여 내부 깊숙한 곳에 상태처리를 할 수 있는 알고리즘을 구현해놓았지만, 외부 렌더링을 사용할 경우 이러한 리엑트팀의 노력을 사용하지 못하게 되는 문제가 발생한다는 것이 이슈이다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/25dc139a-5c52-4d29-9f5a-cc16c2391d06/image.png" alt=""></p>
<p>어떤 이슈가 있었는지 보면</p>
<h3 id="redux">redux</h3>
<p>redux의 메인테이너는 tearing을 막기 위해 리엑트팀이 만든 useMutableSource를 사용 시 selctor 함수를 useCallback으로 감싸줘야하는 필요성에 대해 <a href="https://github.com/reactwg/react-18/discussions/84#discussion-3531681">이야기</a>를 했습니다.
이는 기존에 selector 함수를 사용하던 모든 리덕스 사용자들의 불편함을 야기하죠.</p>
<p>여기서 selector 함수란 스토어에 있는 여러 값들 중 필요한 값만 불러오거나 원본 상태 값은 그대로 두고 상태의 특정 값만 계산해서 사용하게 도와주는 함수를 의미합니다.</p>
<pre><code>// Arrow function, direct lookup
const selectEntities = state =&gt; state.entities

// Function declaration, mapping over an array to derive values
function selectItemIds(state) {
  return state.items.map(item =&gt; item.id)
}

// Function declaration, encapsulating a deep lookup
function selectSomeSpecificField(state) {
  return state.some.deeply.nested.field
}

// Arrow function, deriving values from an array
const selectItemsWhoseNamesStartWith = (items, namePrefix) =&gt;
  items.filter(item =&gt; item.name.startsWith(namePrefix))</code></pre><h2 id="usesyncexternalstore"><a href="https://reactjs.org/docs/hooks-reference.html#usesyncexternalstore">useSyncExternalStore</a></h2>
<p>이름부터 external store와 synchronize하겠다는 의지가 보이는 이 훅은 아래와 같이 사용합니다.</p>
<p>이 훅은 external state의 변경사항을 관찰하고 있다가 tearing이 발생하지 않도록 상태 변경이 관찰되면 다시 렌더링을 시작합니다.</p>
<pre><code>const state = useSyncExternalStore(
 subscribe,
 getSnapshot[,
 getServerSnapshot]
);</code></pre><ul>
<li>subscribe : store가 변경되었을때 호출할 callback함수 입니다.</li>
<li>getSnapshot : store의 현재 값을 리턴하는 함수 입니다.</li>
<li>getServerSnapshot : 서버사이드 렌더링 시 가지고 있던 snapshot을 리턴하는 함수입니다.</li>
</ul>
<h3 id="실제-호출-코드">실제 호출 코드</h3>
<pre><code>const state = useSyncExternalStore(store.subsribe, store.getSnapshot)</code></pre><p><code>store.getSnapshot</code>은 직전 렌더링 시점과 비교해 스토어의 상태 값이 변경되었는지를 확인하기 위해 넣는 값입니다.
getSnapshot()은 primitive한 number,string값일 수도 있고,메모이제이션 된 object일 수도 있습니다.</p>
<h3 id="특정-필드만-subscribe하는-코드">특정 필드만 subscribe하는 코드</h3>
<pre><code>const selectedField = useSyncExternalStore(
 store.subscribe,
 ()=&gt; store.getSnapshot().selectedField,
);</code></pre><p>두번째 <code>()=&gt;store.getSnapshot().selectedField</code>가 <code>selector</code>입니다. 
이 useSyncExternalStore에 들어가는 selector는 메모이제이션 되지 않아도 됩니다.</p>
<p>세번째 인자인 getServerSnapshot은 hydration시 일어나는 server,client 상태 값의 mismatch를 방지하기 위해 사용합니다.</p>
<pre><code>const selectedField = useSyncExternalStore(
 store.subscribe,
 ()=&gt;store.getSnapshot().selecetedField,
 ()=&gt;INITIAL_SEVER_SNPASHOT.selectedFiled,
);</code></pre><h2 id="starttransition">startTransition</h2>
<p>useMutableSource의 대체제인 useSyncExternalStore는 <code>startTransition</code>을 사용하는 리엑트 18에서 useMutableSource가 해결하기 못한 문제점을 해결해 줍니다.</p>
<p>startTransition는 웹 사용자의 심리를 사용해 렌더링 우선순위를 정해주는 것입니다.
startTransition으로 둘러쌓인 렌더링 업데이트는 낮은 우선순위를 가집니다.</p>
<pre><code>import { startTransition } from &#39;react&#39;;

// Urgent: Show what was typed
setInputValue(input);

// Mark any state updates inside as transitions
startTransition(() =&gt; {
  // Transition: Show the results
  setSearchQuery(input);
});</code></pre><p>startTransition이 유용하게 쓰일 수 있는 부분은 Suspence 입니다.</p>
<p>아래 코드에서 handleClick 시 comments에 대한 응답을 받아오는 중이라면 fallback UI인 Spinner를 그려줄 것입니다.
하지만 commnets를 불러오기 전에도 <code>&lt;SearchList/&gt;</code>를 보여주고 싶다면
setCommentQuery를 startTransition으로 감싸주면 됩니다.</p>
<pre><code>function handleClick(){
 setCommentQuery();
}
&lt;Suspense fallback={&lt;Spinner /&gt;}&gt;
     {queries === &#39;searches&#39; ? &lt;SearchList /&gt;: &lt;Comments/&gt;}
&lt;/Suspense&gt;</code></pre><pre><code>function handleClick() {
  startTransition(() =&gt; {
    setCommentQuery();
  })
}</code></pre><p>하지만 setCommentQuery가 exteranal store에서 파생된 함수라면 예상처럼
<code>&lt;SearchList /&gt;</code>를 보여주지 않고 <code>&lt;Spinner /&gt;</code>를 보여주는 문제점이 발생합니다.</p>
<p><a href="https://www.youtube.com/watch?v=oPfSC5bQPR8&amp;t=694s">ReactConf21</a>에서 이와 같은 startTransition 문제를 useSyncExternalStore를 사용해서 해결하는 예제를 보여줍니다.</p>
<p>어떻게 해결하는지 한번 확인해 보겠습니다.</p>
<h2 id="create-simple-external-store">create simple external store</h2>
<pre><code class="language-import">
const createStore = (initialState) =&gt; {
  let state = initialState
  const getState = () =&gt; state
  const listeners = new Set()
  const setState = (fn) =&gt; {
    state = fn(state)
    listeners.forEach((l) =&gt; l())
  }
  const subscribe = (listener) =&gt; {
    listeners.add(listener)
    return () =&gt; listeners.delete(listener)
  }
  return { getState, setState, subscribe }
}

const useStore = (store, selector) =&gt; {
  const [state, setState] = useState(() =&gt; selector(store.getState()))
  useEffect(() =&gt; {
    const callback = () =&gt; setState(selector(store.getState()))
    const unsubscribe = store.subscribe(callback)
    callback()
    return unsubscribe
  }, [store, selector])
  return state
}</code></pre>
<h3 id="createstore">createStore</h3>
<p>먼저 <code>createStore</code>를 살펴보면 초기 상태 값인 initialState를 인자로 받아 내부에서 state 변수를 초기화 하고</p>
<ul>
<li>getState함수 에서 이 변수를 클로저로 사용하고 있음을 알 수 있습니다.</li>
<li>setState함수는 함수 fn을 ㅇ니자로 받아 fn(state)를 실생시켜 기존 state를 업데이트해주고 Set()안에있는 listener들을 순회하며 실행해줍니다.</li>
<li>subscribe함수는 listener함수를 인자로 받아 Set에 넣어주고 unsubscribe해줄 수 있는 함수를 리턴해주고 있음을 알 수 있습니다.</li>
</ul>
<h3 id="usestore">useStore</h3>
<p>createStore를 사용하는 커스텀 훅인 useStore를 살펴보면 인자로 external store와 selector를 받고</p>
<ul>
<li>selector(store.getState())값으로 state를 초기화 시켜줍니다.</li>
<li>useEffect 내부는<ul>
<li>callback함수는 selector로 store.getState()의 일부를 가져다가 setState로 state를 업데이트 해줍니다.</li>
<li>그리고 store,selector가 변경될 때마다 앞서 선언 해두었던 callback함수를 실행해 state값을 최신 값으로 업데이트 시켜 줍니다.</li>
<li>useEffect의 clean up함수에서는 external store를unsubscribe 해주어 Garbage Collector가 사용안하는 메모리 영역을 정리하게 해줍ㅂ니다.</li>
</ul>
</li>
</ul>
<p>이제 이 훅을 실제로 사용해보겠습니다.</p>
<h2 id="countertextbox">Counter,TextBox</h2>
<pre><code>const store = createStore({ count: 0, text: &#39;hello&#39; })

const Counter = () =&gt; {
  const count = useStore(
    store,
    useCallback((state) =&gt; state.count, []),
  )
  const inc = () =&gt; {
    store.setState((prev) =&gt; ({ ...prev, count: prev.count + 1 }))
  }
  return (
    &lt;div&gt;
      {count} &lt;button onClick={inc}&gt;+1&lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre><p>Counter컴포넌트 내부에서 count변수는 useStore에서 리턴되는 값을 참조하고 있습니다.
inc함수는 external store의 현재 상태 값을 참조해서 카운터를 하나 올리고 있습니다.</p>
<ul>
<li>Inc를 호출할 경우 external store의 setState를 호출합니다.</li>
<li>external store의 setState는 useStore내부에서 subscribe했던 콜백함수를 호출합니다. 이 경우 콜백 함수는 useStore에서 선언했던 useState의 반환값 중 setState함수 입ㅂ니다.</li>
<li>업데이트가 된 count가 Counter컴포넌트 내부에서 표시됩니다.</li>
</ul>
<pre><code>const TextBox = () =&gt; {
  const text = useStore(
    store,
    useCallback((state) =&gt; state.text, []),
  )
  const setText = (event) =&gt; {
    store.setState((prev) =&gt; ({ ...prev, text: event.target.value }))
  }
  return (
    &lt;div&gt;
      &lt;input value={text} onChange={setText} className=&quot;full-width&quot; /&gt;
    &lt;/div&gt;
  )
}

const App = () =&gt; {
  return (
    &lt;div className=&quot;container&quot;&gt;
      &lt;Counter /&gt;
      &lt;Counter /&gt;
      &lt;TextBox /&gt;
      &lt;TextBox /&gt;
    &lt;/div&gt;
  )
}</code></pre><p>위 코드에서 startTransition을 사용한다면 store가 external store이기 때문에 tearing 현상이 일어날 수 있습니다.</p>
<h2 id="usesyncexternalstore-사용하기">useSyncExternalStore 사용하기</h2>
<h3 id="after">After</h3>
<pre><code>import { useSyncExternalStore } from &#39;react&#39;

const useStore = (store, selector) =&gt; {
  return useSyncExternalStore(
    store.subscribe,
    useCallback(() =&gt; selector(store.getState(), [store, selector])),
  )
}</code></pre><p>useExternalStore를 사용해서 useStore내부를 변경했습니다.
아주 간단하게 업데이트된 상태값을 조회할 수 있게 되었습니다.</p>
<p>아래는 사용하기 전의 코드입니다.</p>
<pre><code>const useStore = (store, selector) =&gt; {
  const [state, setState] = useState(() =&gt; selector(store.getState()))
  useEffect(() =&gt; {
    const callback = () =&gt; setState(selector(store.getState()))
    const unsubscribe = store.subscribe(callback)
    callback()
    return unsubscribe
  }, [store, selector])
  return state
}</code></pre><p>subscribe api를 제공하는 어떤 store든지 상관 없이 useSyncExternalStore를 활용하면 concurrent feature에서 발생하는 tearing을 예방할 수 있습니다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/a2a48bcd-0b51-430b-a091-99d5ec06b2d4/image.png" alt="">
기존에 external store와 함께 사용하돈 useState, useEffect, useRef 로직이 있었다면 useSyncExternalStore를 사용해 migration 하는 것이 더 좋아 보입니다.</p>
<h3 id="출처">출처</h3>
<p><a href="https://velog.io/@jay/useSyncExternalStore#starttransition">dante.log</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[useDeferredValue,useTransition]]></title>
            <link>https://velog.io/@dae_eun2/useDeferredValueuseTransition</link>
            <guid>https://velog.io/@dae_eun2/useDeferredValueuseTransition</guid>
            <pubDate>Thu, 03 Nov 2022 16:25:14 GMT</pubDate>
            <description><![CDATA[<p>리액트가 18버전이 되면서, 많은 기능이 추가되었다.
그중 가장 돋보이는 변화는 &#39;동시성&#39; 개념이다.
<a href="https://reactjs.org/blog/2022/03/29/react-v18.html">React v18.0 Blog</a>
기존에 디바운싱,스로트링과 같이 미묘한 처리로 우회하던 무제를
react hooks로 직접 해결할 수 있게 되었다.</p>
<h2 id="디바운싱스로틀링">디바운싱,스로틀링</h2>
<p>자바스크립트에서는 짧은 시간에 유저 입력 등의 이벤트가 많이 일어나는 경우 화면이 끊길 수 있다. 그래서 이런 경우 특별한 최적화가 필요하다.</p>
<p>대표적인 예가 바로 자동완성 기능이다.
만약 유저 입력 한번에 추천검색어 이벤트 한 번이 일어난다면, 불필요한 리랜더링이 여러번 발생할 것이다.
그래서 정작 필요한 &#39;진짜 검색어&#39; 에 대한 자동완성이 느려질 수 있다.
이러한 상황을 방지하기 위해
디바운싱 이라는 처리를 한다.</p>
<p>디바운싱은 특정 함수의 호출이 너무 자주 일어나지 않도록 일정 시간 동안 함수 실행을 지연시키는 것을 의미한다.</p>
<pre><code>import React, { useState, useEffect } from &quot;react&quot;;

const useQueryDebounce = (value, delay) =&gt; {
  const [debounceValue, setDevounceValue] = useState(&quot;&quot;);

  useEffect(() =&gt; {
    const handler = setTimeout(() =&gt; {
      setDevounceValue(value);
    }, delay);

    return () =&gt; {
      clearTimeout(handler);
    };
  }, [value, delay]);
  return debounceValue;
};

export default useQueryDebounce;</code></pre><h2 id="모든-문제가-해결되었을까">모든 문제가 해결되었을까?</h2>
<p>디바운싱과 스로틀링에서 적절한 딜레이를 선택하는 것은 꽤 어렵다.
딜레이가 너무 길면 유저 입력에 대한 반응도 그만큼 느려지지만,
너무 짧으면 적용하는 의미가 없어진다.
유저가 빠르게 입력할 경우, 분명히 그걸 전부 처리하는것은 비효율적이다.
그러나 디바운싱을 적용한다면 delay를 얼마로 주어야 할까?
그건 컴퓨터 사양에 따라 다를것이다.</p>
<p>게다가 디바운싱을 적용한다고 해도, 일단 계산을 시작하면 메인 스레드가 거기서 블록되기 때문에 다음 입력을 처리하지 못한다.
한 순간 입력을 멈추면 다음 입력을 받지 못할 정도로 프레임이 저하된다.</p>
<p>사실, 디바운싱과 스로틀링은 문제를 근본적으로 해결하는 방법은 아니다.
이 문제를 근본적으로 해결하려면 반응속도는 최대한 빠르게 하되,
사용자의 상호작용이 있으면 그걸 우선적으로 처리하여 화면이 멈춘것처럼 보이지 않게 해야한다.
무거운 연산은 메인 스레드가 놀고 있을때만 처리하고, 유저 입력이 들어오면 다시 거기에 집중하는 것이다.</p>
<p>즉, 이벤트의 우선순위를 나누고, 우선순위가 높은 이벤트가 발생하면
context switch하여 그 작업을 먼저 핸들링하면 된다.
그러나 자바스크립트에는 병령 스레드나, 스케쥴러가 없다.</p>
<h2 id="usetransition">useTransition</h2>
<p>상태 변화의 우선순위를 지정하기 위해  <code>useTranstion</code>훅이 새로 도입되었다.
<code>[inpending,startTransition]</code>을 반환하는데,
<code>ispending</code>은 작업이 지연되고 있음을 알리는 <code>boolean</code>이며,
<code>startTransition</code>은 낮은 우선순위로 실행할 함수를 인자로 받는다.</p>
<pre><code>function App() {
  const [isPending, startTransition] = useTransition();
  const [count, setCount] = useState(0);

  function handleClick() {
    startTransition(() =&gt; {
      setCount(c =&gt; c + 1);
    })
  }


  return (
    &lt;div&gt;
      {isPending &amp;&amp; &lt;Spinner /&gt;}
      &lt;button onClick={handleClick}&gt;{count}&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre><p>클릭할 때마다 일어나는 count에 대한 상태 업데이트를 낮은 우선순위로 실행한다.
그래서 더 중요한 이벤트가 있는 경우, count의 업데이트를 지연시키고 대신 이전의 값을 보여준다.
<code>inpending</code>을 이용하여 업데이터가 지연되는동안 스피너를 보여줄 수도 있다.</p>
<h2 id="usedeferredvalue">useDeferredValue</h2>
<p>이 훅 또한 <code>useTransition</code>과 유사하게 낮은 우선순위를 지정한다.
다만, <code>useTransition</code>은 함수 실행의 우선순위를 지정하는 반면,
<code>useDefferdValue</code>는 값의 업데이트 우선순위를 지정한다.
우선순위가 높은 작업을 실행하는 동안 <code>useMemo</code>와 유사하게 이전의 값을 가지고 있으면서 업데이트를 지연시킨다.</p>
<p>이 훅은 <code>useMemo</code>와 함께 사용하면 효과가 더 좋다.
종속된 값들을 memoize 시키면 불필요한 재렌더링을 막으면서 하위 컴포넌트나 상태의 업데이트를 지연시킬 수 있다.</p>
<p>아래는 검색 자동완성을 처리하는 코드이다.
사용자 입력 query의 업데이트에 대해 추천 검색어인 suggetions을 띄워준다.
추천 검색어의 업데이트는 우선 순위 높은 작업이 없을때만 실행되기 때문에
디바운싱을 적용하지 않아도 좋은 유저경험을 줄 수 있다.</p>
<pre><code>function App() {
  const [isPending, startTransition] = useTransition();
  const [count, setCount] = useState(0);

  function handleClick() {
    startTransition(() =&gt; {
      setCount(c =&gt; c + 1);
    })
  }


  return (
    &lt;div&gt;
      {isPending &amp;&amp; &lt;Spinner /&gt;}
      &lt;button onClick={handleClick}&gt;{count}&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre><p>참고</p>
<ul>
<li><a href="https://velog.io/@ktthee/React-18-%EC%97%90-%EC%B6%94%EA%B0%80%EB%90%9C-useDeferredValue-%EB%A5%BC-%EC%8D%A8-%EB%B3%B4%EC%9E%90">ktthe.log velog</a></li>
<li><a href="https://reactjs.org/docs/hooks-reference.html#usedeferredvalue">React.js</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Interactive Web - Transition,Transform,Animation]]></title>
            <link>https://velog.io/@dae_eun2/Interactive-Web-TransitionAnimation</link>
            <guid>https://velog.io/@dae_eun2/Interactive-Web-TransitionAnimation</guid>
            <pubDate>Fri, 28 Oct 2022 05:42:56 GMT</pubDate>
            <description><![CDATA[<h1 id="transition">Transition</h1>
<p>트랜지션은 css의 프로퍼티의 값이 변화할 때, 프로퍼티값의 변화가 일정시간(duration)에 걸쳐 일어나도록 하는 것.
<img src="https://user-images.githubusercontent.com/104764474/198507372-324ecfe7-44f6-4651-832c-009258a6b91e.png" alt="스크린샷 2022-10-28 오후 2 08 47"></p>
<blockquote>
<p>transition: property duration function delay</p>
</blockquote>
<h2 id="transition-property">Transition-property</h2>
<p>Transition-propery 는 트랜지션의 대상이 되는 CSS프로퍼티명을 지정한다.
지정하지 않는 경우, 모든 프로퍼티가 트랜지션의 대상이 된다.
복수의 경우 쉼표(,)로 구분한다.</p>
<p>대상이 되는 프로퍼티는 아래와 같다.</p>
<ul>
<li>width , height , max-width, max-height, mix-width,min-height</li>
<li>padding,margin</li>
<li>boder-color,border-width,border-spacing</li>
<li>background-color,background-position</li>
<li>top,left,right,bottom</li>
<li>color,font-size,font-weight,letter-spacing,line-height
text-indent,text-shadow, vertical-alingn , word-spacing</li>
<li>opacity, outline-color, outline-offset, outline-width
visibility, z-index</li>
</ul>
<h2 id="transition-duration">Transition-duration</h2>
<p>트랜지션이 일어나는 지속시간을 지정한다.
지정하지않을 경우 0s으로 설정되어 트랜지션이 일어나지 않는다.</p>
<p>duration은 proprerty와 1:1대응한다</p>
<pre><code>transition-property:width;
transition-duration:2s</code></pre><pre><code>transition-property:width,opacity;
transition-duration:2s,4s;
</code></pre><pre><code>transition: width 2s, opacity 4s;</code></pre><pre><code>transition-property: width, opacity, left, top;
transition-duration: 2s, 1s;</code></pre><h2 id="transition-timing-function">Transition-timing-function</h2>
<p>트래지션 효과의 변화 흐름, 시간에 따른 변화 속도와 같은 일종의 변화 리듬을 지정한다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/5a7af8c0-3e3e-4890-b750-43f6146e3d52/image.png" alt=""></p>
<h3 id="cubic-bezier">cubic-bezier</h3>
<p><a href="https://basemenks.tistory.com/281">cubic-bezier란?</a>
<a href="https://matthewlein.com/tools/ceaser">cubic-bezier test</a> 또는 개발자도구에서 직접 설정하여 원하는 timing-function을 만들어 사용할 수 있다.</p>
<h2 id="transition-delay">transition-delay</h2>
<p>말 그대로 트랜지션이 실제로 시작하는 사이에 대기하는 시간을 설정한다.</p>
<h1 id="transform">Transform</h1>
<p>트랜스폼은 요소에 이동,회전,확대,축소, 비틀기 효과를 부여하기 위한 함수를 제공한다.
단, 애니메이션 효과를 제공하지는 않기 때문에, 정의된 프로퍼티가 바로 적용되어 화면에 표시된다.
트랜스폼은 애니메이션효과를 위해 사용하여야 하는것은 아니지만,
애니메이션 효과를 부여할 필요가 있다면 트랜지션이나 애니메이션과 함께 사용한다.</p>
<h3 id="2d-transform">2d transform</h3>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/7e85c148-4b09-4d2a-b2be-762f4a6aa0fa/image.png" alt=""></p>
<h3 id="3d-transform">3d transform</h3>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/9c3f64a5-7237-4bb2-b483-21a934632087/image.png" alt=""></p>
<h2 id="perspective">perspective</h2>
<p>3D공간에서 요소와 관측 점과의 거리, 즉 <strong>원근감을 지정하는 속성이다</strong>
값이 작을수록 가깝게 보이고, 멀수록 멀게 보인다.</p>
<ul>
<li>perspective 는 뷰어와 요소와의 거리를 나타낸다.</li>
<li>transform-style 은 요소의 표현을 나타낸다.</li>
<li>perspective-origin은 뷰어가 보는 위치를 설정한다.</li>
</ul>
<h3 id="perspective-와-transformperspective의-차이">perspective 와 transform:perspective()의 차이</h3>
<ul>
<li>perspecive의 경우
<img src="https://velog.velcdn.com/images/dae_eun2/post/3a50668c-71eb-4b02-8e84-530892be5ff8/image.png" alt=""></li>
<li>transfomr:perspective() 의 경우
<img src="https://velog.velcdn.com/images/dae_eun2/post/1a080785-17cc-4a8e-a69f-c74a1f2668b0/image.png" alt=""></li>
</ul>
<h2 id="transform-origin">transform-origin</h2>
<p>요소의 기본기준점을 설정할 때 사용된다.
즉, 요소가 움직이는 기준 지점을 특정할 수 있다.
기본 기준점은 정중앙이다.</p>
<pre><code>transform-origin:center;
transform-origin:top left;
transform-origin:50px 50px;
transform-origin:bottom right 60px;</code></pre><h1 id="animation">Animation</h1>
<p>애니메이션은 애니메이션을 나타내는 CSS스타일과 애니메이션의 sequence를 나타내는 <code>@keyframes</code>로 이루어진다.</p>
<p>트랜지션으로도 어느정도의 애니메이션 효과를 표현할 수 있으나, 제한적이다.</p>
<blockquote>
<p>animation: name duration timing-function delay iteration-count direction fill-mode play-state
<img src="https://velog.velcdn.com/images/dae_eun2/post/7922f2c1-03d4-4a49-b994-a7eedd93631c/image.png" alt=""></p>
</blockquote>
<h2 id="keyframes">@keyframes</h2>
<p>애니메이션의 흐름(sequence)중의 여러 시점(breakpoint)에서 css프로퍼티값을 설정할 수 있다.</p>
<pre><code>@keyframes {keyframe-name} {
//애니메이션 시작지점
 0%{ left: 0 }
// breakpoint
30% { left:10px }
70% { left:20px }
//애니메이션 종료지점
100% { left:30px } 
}</code></pre><p>0%,100%는 각각 from,to로 표현할 수 있다.</p>
<h2 id="animation-duration">animation-duration</h2>
<p>애니메이션에 소요되는 시간을 지정한다.
지정하지 않을경우 0s로 설정되어 어떠한 애니메이션도 실행되지 않는다.</p>
<h2 id="animaion-timing-function">animaion-timing-function</h2>
<p>transition-timing-function 과 동일하게 작동한다.</p>
<h2 id="animation-delay">animation-delay</h2>
<p>애니메이션이 실제로 시작하는 사이에 대기시간을 설정한다.</p>
<h2 id="animation-iteration-count">animation-iteration-count</h2>
<p>애니메이션 주기의 재생 횟수를 지정한다.
기본은 1이며, infinite로 무한반복이 가능하다.</p>
<h2 id="animation-direction">animation-direction</h2>
<p><strong>애니메이션이 종료된 이후</strong> 반복될 때 진행하는 방향을 결정한다.</p>
<ul>
<li>normal : 기본값으로, from에서 to로 진행</li>
<li>reverse: to 에서 from으로 진행</li>
<li>alternate: 홀수는 normal, 짝수는 reverse로 진행, 왕복 진행이 가능</li>
<li>alternate-reverse : 홀수는 reverse,짝수는 normal</li>
</ul>
<h2 id="animation-fill-mode">animation-fill-mode</h2>
<p>애니메이션 미실행시(대기 또는 종료) 요소의 스타일을 지정한다.</p>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/0ca3e7bb-12d7-4d5c-9111-8bd1975a2e7b/image.png" alt=""></p>
<p><a href="https://poiemaweb.com/css3-transform">참조</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Typescript 기초]]></title>
            <link>https://velog.io/@dae_eun2/Typescript-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@dae_eun2/Typescript-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Thu, 20 Oct 2022 15:25:18 GMT</pubDate>
            <description><![CDATA[<h1 id="typescript">Typescript</h1>
<ul>
<li>메인 룰: typescript는 최종적으로 javascript로 변환된다.</li>
<li>typescript는 언어이자 컴파일러(tsc)이다. 컴파일러는 ts 코드를 js로 바꿔준다.</li>
<li>tsc는 tsconfig.json(tsc --init 시 생성)에 따라 ts 코드를 js(tsc 시 생성)로 바꿔준다. 인풋인 ts와 아웃풋인 js 모두에 영향을 끼치므로 tsconfig.json 설정을 반드시 봐야한다.</li>
<li>단순히 타입 검사만 하고싶다면 tsc --noEmit 하면 된다.</li>
<li>개인 의견: tsconfig.json에서 그냥 esModuleInterop: true, strict: true 두 개만 주로 켜놓는 편. strict: true가 핵심임.<ul>
<li>allowJs :true  ( ts와 js동시에 사용가능)</li>
<li>target: &quot;es2016&quot; (tsc가 코드를 변환 할 대상)</li>
<li>forceConsistentCasingInFileNames: true (import 시 대,소문자 구분해서 적용됨)</li>
<li>skipLibCheck (안쓰는 라이브러리 타입체크 패스하기)</li>
</ul>
</li>
<li>ts 파일을 실행하는 게 아니라 결과물인 js를 실행해야 한다.</li>
</ul>
<h2 id="ts-문법">TS 문법</h2>
<h3 id="js-to-ts">Js to Ts</h3>
<ul>
<li>기본적으로 JS에 변수,속성,매개변수, 리턴값에 타입을 붙이면 TS</li>
<li>소문자를 사용하여 타입기입<pre><code>const a: number = 5;
function add(x: number, y: number): number { return x + y }
const add: (x: number, y: number) =&gt; number = (x, y) =&gt; x + y;
const obj: { lat: number, lon: number } = { lat: 37.5, lon: 127.5 };</code></pre></li>
</ul>
<h3 id="타입-별칭">타입 별칭</h3>
<pre><code>type Add =(x:number,y:number) =&gt; number
const add:Add=(x,y) =&gt; x+y
</code></pre><h3 id="interface">Interface</h3>
<pre><code>interface Add{
   (x:number,y:number):number
}
const add: Add = (x,y) =&gt; x+y</code></pre><h3 id="타입-별칭과-interface의-차이">타입 별칭과 interface의 차이</h3>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/931f7f31-675d-48fb-8b46-bcb872232cba/image.png" alt=""></p>
<h3 id="배열튜플-문법">배열,튜플 문법</h3>
<p>튜플이란 ?  <strong>길이가 고정된 배열</strong></p>
<pre><code>let arr: string[] = [];
let arr2: Array&lt;string&gt; = [];
function rest(...args: string[]) {}

const tuple: [string, number] = [&#39;1&#39;, 1];
tuple[2] = &#39;hello&#39;;
tuple.push(&#39;hello&#39;);</code></pre><h3 id="고정된-원시값-타입--리터럴-타입">고정된 원시값 타입 ( 리터럴 타입)</h3>
<p>const의 경우 바뀔 일이 없어 고정된 원시값으로 타이핑을 하여 
정확한 타입을 기입 가능</p>
<pre><code>const a: true =true;
const b: 5 = 5;
const b = 6 //error</code></pre><pre><code>const req = { url: &quot;https://example.com&quot;, method: &quot;GET&quot; };
handleRequest(req.url, req.method);
//Argument of type &#39;string&#39; is not assignable to parameter of type &#39;&quot;GET&quot; | &quot;POST&quot;&#39;.

// 수정 
const req = { url: &quot;https://example.com&quot;, method: &quot;GET&quot; } as const;
handleRequest(req.url, req.method);</code></pre><h3 id="as">as</h3>
<p>앞의 타입을 강제로 다른 타입으로 바꿈
가급적 비사용을 추천</p>
<pre><code>const aa =123
aa= &#39;hello&#39; as unknown as number</code></pre><p><img src="https://velog.velcdn.com/images/dae_eun2/post/8cef245e-8415-4072-ba0c-06a601801f59/image.png" alt="">
<img src="https://velog.velcdn.com/images/dae_eun2/post/0ab73e45-9f75-4cd3-b041-5d8e5cc2fe83/image.png" alt=""></p>
<h3 id="never">never</h3>
<p><a href="https://ui.toast.com/weekly-pick/ko_20220323">never관련 좋은 글</a>
빈 배열은 never[]로 나오는데 이 경우 아무것도 넣을 수 없다.</p>
<h3 id="-non-null-assertion">! (non-null assertion)</h3>
<p>null 또는 undefined가 아님을 개발자가 보증한다. 라는 의미로 보면된다</p>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/aa32217d-bcef-4e7b-bba0-4140f7518b45/image.png" alt="">
<img src="https://velog.velcdn.com/images/dae_eun2/post/cca9f0dd-1d15-40ef-bf4c-355593106a34/image.png" alt=""></p>
<ul>
<li>최대한 ! 대신 if를 사용할 것<pre><code>const head = document.querySelector(&#39;#head&#39;)!;
console.log(head);
</code></pre></li>
</ul>
<p>const head = document.querySelector(&#39;#head&#39;);
if (head) {
  console.log(head);
}</p>
<pre><code>
### 템플릿 리터럴 타입
</code></pre><p>type World = &#39;world&#39;;</p>
<p>type Greeting = <code>hello ${World}</code>;</p>
<pre><code>### keyof , typeof
Key만 뽑아내고 싶다 ? keyof
자바스크립트 원형을 타입으로 쓰고 싶다? typeof
</code></pre><p>const obj = {a:&#39;123&#39;,b:&#39;hello&#39;,c:&#39;world&#39;};
type Key = keyof typeof obj // &#39;a&#39;|&#39;b&#39;|&#39;c&#39;
type Value = typeof obj[keyof typeof obj] // &#39;123&#39;|&#39;hello&#39;|&#39;world&#39;</p>
<pre><code>
### union(|), intersection(&amp;)
union`|`은 &#39;or&#39; , `||`의 의미와 같고 (하나라도 있으면 된다)
intersection`&amp;`은 &#39;and&#39; `&amp;&amp;`의 의미와 같다. (모든 속성이 있어야한다)</code></pre><p>function add(x:string | number, y:string| number):string | number {return x+ y}</p>
<p>add(1,2)
add(&#39;1&#39;,&#39;2&#39;)
add(1,&#39;2&#39;)</p>
<pre><code>단, 타입은 모든 경우의 수를 다 고려하기 때문에
위의 경우 x:string y:number와 같은 경우 까지 고려한다.
![](https://velog.velcdn.com/images/dae_eun2/post/f7b79c17-9884-4f4e-9b83-41ac8d4d0eb7/image.png)
![](https://velog.velcdn.com/images/dae_eun2/post/d63332a4-f9a8-496e-a59c-4b48d99c81e2/image.png)

### type Alias , interface extends</code></pre><p>type Animal = {breath :true};
type Mammal = Animal &amp; {breed:true};
type Human = Mammal &amp; {think :true};</p>
<p>const a: Human ={breath:true, breed:true,think:true}</p>
<pre><code></code></pre><p>interface A {
 breath:true
}
interface B extends A {
 breed:true
}</p>
<p>const b: B ={breat:true, breed:true}</p>
<pre><code>interface는 재선언이 가능하며,
재 선언시 기존 타입과 합쳐진다.
이 특징을 기반으로 다른 사람의 라이브러리 코드를 수정하여 사용할 수 도 있다.

### 좁은 타입과 넓은 타입</code></pre><p>type A = string | number
type B = string</p>
<pre><code>위 코드 중 type A가 type B에 비하여 더 넓은 타입을 가진다.

좁은타입에서 넓은 타입으로는 대입이 가능하지만,
넓은타입에서 좁은 타입으로는 대입이 불가능하다.</code></pre><p>type A = {name:string};
type B = {age:number};</p>
<p>type C = {name:string, age:number}; // A &amp; B</p>
<pre><code>위의 경우는 type C가 가장 좁은 타입이다.
쉽게 말해 더 구체적일 수록 좁은 타입이다.
### 잉여 속성 체크,구조적 서브타이핑
구조적 서브타이핑은 상위와 하위의 개념으로, 상위의 개념이 하위를 포함하고 있는 것이다.
구조적 서브타이핑에서는 하위가 다른 어떤 프로퍼티를 가지고 있는지 보단,
그저 상위의 프로터미만 가지고 있으면 된다.

하지만 잉여 속성 체크는 그에 반해 상위에 없는 프로퍼티를 가지면 에러를 발생시킨다.

이 둘의 발동 조건은 바로 객체 리터럴을 사용하냐, 안하냐의 차이다.</code></pre><p>//잉여 속성 체크
interface A { a:string }
const obj1: A ={a: &#39;hello&#39; b:&#39;world&#39;}. // error
// 구조적 서브 타이핑
const obj ={a: &#39;hello&#39; b:&#39;world&#39;}
const obj1: A = obj //에러안남</p>
<pre><code>### void
void 타입은 return 값을 사용하지 않겠다 라는 뜻이다.
하지만 메서드나 매개변수에서는 리턴값이 사용이 가능하다. 
</code></pre><p>function a(callback:()=&gt;void): void {
  return &#39;3&#39; //error
  return undefined //pass
  return null /error
}
a(() =&gt; {
    return &#39;3&#39;
})
interface Human {
    talk:()=&gt;void;
}
const human:Human = {
     talk() {retrun &#39;abc&#39;}
}</p>
<pre><code>### unknown
any는 모든 타입이 다 가능하며, 타입검사를 포기한다고 보면 될듯하고
unknown 은 지금은 타입을 잘 모르겠지만, 추후에 지정을 하겠다 라고 생각하시면 편합니다.

### 타입간 대입 가능 표
![](https://velog.velcdn.com/images/dae_eun2/post/02eed9bb-0b02-4fc6-96ed-0deb0b9d3f47/image.png)
초록색 체크도 x 로 보시면 됩니다.

### 타입가드</code></pre><p>function numOrStr(a: number | string) {
  if (typeof a === &#39;string&#39;) {
    a.split(&#39;,&#39;);<br>  } else {
    a.toFixed(1);
  }
}</p>
<p>function numOrNumArr(a: number | number[]) {
  if (Array.isArray(a)) {
    a.slice(1);<br>  } else {
    a.toFixed(1);
  }
}</p>
<p>type B = { type: &#39;b&#39;, bbb: string };
type C = { type: &#39;c&#39;, ccc: string };
type D = { type: &#39;d&#39;, ddd: string };
type A = B | C | D;
function typeCheck(a: A) {
  if (a.type === &#39;b&#39;) {
    a.bbb;
  } else if (a.type === &#39;c&#39;) {
    a.ccc;
  } else {
    a.ddd;
  }</p>
<pre><code>### is 
is를 활용하면 타입을 구분해주는 커스텀 함수를 직접 만들 수 있습니다.</code></pre><p>interface Cat { meow:number }
interface Dog { bow: number }
function catOrDog (a: Cat | Dog) : a is Dog{
// 타입 판별
    if((a as Cat).meow) { return false }
    return true;
}</p>
<p>function pet(a: Cat | Dog){
    if(catOrDog(a)){
        console.log(a.bow)
    }
    if(&#39;meow&#39; in a){
        console.log(a.meow)
    }
}</p>
<pre><code></code></pre><p>const isRejected = (input: PromiseSettledResult<unknown>): input is PromiseRejectedResult =&gt; { return input.status === &#39;rejected&#39;};
const isFulfilled = <T>(input: PromiseSettledResult<T>): input is PromiseFulfilledResult<T> =&gt;{ return input.status === &#39;fulfilled&#39; } ;</p>
<p>const promises = await Promise.allSettled([Promise.resolve(&#39;a&#39;), Promise.resolve(&#39;b&#39;)]);
const errors = promises.filter(isRejected);</p>
<pre><code>
### class
class 또한 타입으로 사용할 수 있다
단, 주의사항은 이 경우 클래스 자체가 아니라 인스턴스를 의미한다
클래스 자체의 타입은 type of 클래스 이다.</code></pre><p>class A {
 aa() { }
}
class B {
 bb() { }
}</p>
<p>function aOrB(para: A|B){
if(param instanceof A){
  param.aa();
  }
}</p>
<p>aOrB(A) //error
aOr(new B()) </p>
<pre><code></code></pre><p>interface A {
    readonly a: string;
   b: string;
}</p>
<p>class B implements A {
    private a: string = &#39;123&#39;;
    protectd b: string = &#39;456&#39;;
    c: string=&#39;wow&#39;;</p>
<pre><code>method(){ // 모두 가능
  console.log(this.a);
  console.log(this.b);
  console.log(this.c);
}</code></pre><p>}
class C extneds B { 
  method(){ 
      console.log(this.a); // 불가능
      console.log(this.b);
      console.log(this.c);
    }
} </p>
<p>new C().a; // 접근 불가
new C().b; // 접근 불가
new C().c; //  접근 가능</p>
<pre><code>- implements : 구현하다 는 의미로 interface를 따른다 
- private : 안에서는 사용 가능하지만 , 밖에서는 불가능.
- protected : private과 비슷하지만, 상속의 경우 사용 가능

||public|protected|private
|----|---|----|---|
|클래스 내부|가능|가능|가능|
|인스턴스 |가능|불가능|불가능|
|상속 클래스|가능|가능|불가능|

단, 자바스크립트로 변환시 모두 사라지게된다.

### readonly
readonly는 말 그대로 읽기 전용 속성이라 속성이 바뀌는것을 막을 수 있다.</code></pre><p>inteface A {
 readonly a: string;
 b: string;
}
const aa :A {a:&#39;hello&#39; ,b: &#39;world&#39;};
aa.a = &#39;123&#39; //error</p>
<pre><code>### 인덱스드 시그니처
어떤 값 , 수 이든 상관없이 전부 특정 타입이길 원할 때
</code></pre><p>type A = { [key: string] : number }</p>
<pre><code>
### 맵드 타입스
</code></pre><p>type B =&#39;Human&#39;|&#39;Mammal&#39;|&#39;Animal&#39;
type A = { [key in B] : number }</p>
<pre><code>
### 옵셔널</code></pre><p>function abc (a:number, b?:number, c?:number){ }
abc(1)
abc(1,2)
abc(1,2,3)
abc(1,2,3,4)//error</p>
<pre><code>
### 제네릭
제네릭은 타입에 대한 함수라고 생각하면 된다.
</code></pre><p>function add<T>(x: T, y: T): T { return x + y }
add<number>(1, 2);
add(1, 2);
add<string>(&#39;1&#39;, &#39;2&#39;);
add(&#39;1&#39;, &#39;2&#39;);
add(1, &#39;2&#39;); // error</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[JWT vs Cookie & Session]]></title>
            <link>https://velog.io/@dae_eun2/JWT-vs-Cookie-Session</link>
            <guid>https://velog.io/@dae_eun2/JWT-vs-Cookie-Session</guid>
            <pubDate>Fri, 14 Oct 2022 10:11:53 GMT</pubDate>
            <description><![CDATA[<h1 id="http-특성">HTTP 특성</h1>
<p>HTTP는 인터넷 상에서 데이터를 주고 받기 위한 서버/클라이언트 모델을 따르는 프로토콜 입니다.
클라이언트가 서버에게 요청을 보내면 서버는 응답을 보냄으로써, 데이터를 교환합니다.</p>
<p>HTTP는 <strong>비연결성</strong> 및 <strong>무상태성</strong> 이라는 특징을 가지고 있습니다.
따라서, 클라이언트에 대한 이전의 상태 정보 및 현재 통신의 상태가 남아있지 않습니다.</p>
<p>서버가 다수의 클라이언트와 연결을 계속 유지한다면, 이에 따른 자원낭비가 심해집니다.
비 연결성및 무상태성의 특징을 가진다면 불필요한 리소스 낭비를 줄일 수 있다는 장점이 있습니다.</p>
<p>그러나 서버는 클라이언트를 인식할 수 없다는 단점 또한 존재합니다.
로그인을 하더라도 다음 요청에서는 해당 클라이언트를 기억하지 못해, 또 로그인을 해야 하는 문제가 발생합니다.</p>
<p>하지만 우리가 사용하고 있는 웹 사이트들의 경우, 한번 로그인 하면 다시 로그인 할 필요 없이 여러 페이지를 보고, 다양한 기능을 이용할 수 있습니다.
이는 HTTP의 비연결성 및 무결성의 특징을 보완한 기술인
Cookie와 Session 덕분입니다.</p>
<h1 id="cookie">Cookie</h1>
<p>쿠키란 클라이언트가 어떠한 웹사이트를 방문할 경우, 그 사이트가 사용하고 있는 서버를 통해 클라이언트의 브라우저에 설치되는 작은 기록 정보 파일을 의미합니다.</p>
<ul>
<li>서버는 클라이언트의 로그인 요청에 대한 응답을 작성할 때, 클라이언트 측에 저장하고 싶은 정보를 응답 헤더의 <code>Set-Cookie</code>에 담습니다.</li>
<li>쿠키는 Key-Value 형식의 문자열 입니다.</li>
<li>이후 해당 클라이언트는 요청을 보낼 때 마다, 매번 저장된 쿠키를 요청 헤더의 <code>Cookie</code>에 담아 보냅니다.</li>
<li>서버는 쿠키에 담긴 정보를 바탕으로 해당 요청의 클라이언트가 누군지 식별할 수 있습니다.</li>
</ul>
<h2 id="단점">단점</h2>
<ul>
<li>보안에 취약합니다.<ul>
<li>요청 시 쿠키의 값을 그대로 보냅니다.</li>
<li>유출 및 조작 당할 위험이 존재합니다.</li>
</ul>
</li>
<li>쿠키에는 용량 제한이 있어 많은 정보를 담지 못합니다.</li>
<li>웹 브라우저마다 쿠키에 대한 지원 형태가 다르기 때문에 브라우저간 공유가 불가능 합니다.<ul>
<li>쿠키의 사이즈가 커질수록 네트워크에 부하가 심해집니다.</li>
</ul>
</li>
</ul>
<h1 id="cookie--session-기반-인증">Cookie &amp; Session 기반 인증</h1>
<p>쿠키를 통해 클라이언트 로그인 상태를 유지시킬 수 있지만, 가장 큰 단점은
쿠키가 유출 및 조작 당할 위험이 존재한다는 점 입니다.
개인정보를 HTTP로 주고 받는 것은 위험합니다.</p>
<p>세션은 비밀번호 등 클라이언트의 인증 정보를 쿠키가 아닌 서버측에 저장하고 관리합니다.</p>
<pre><code>HTTP/1.1 200
Set-Cookie: JSESSIONID=FDB5E30BF20045E8A9AAFC788383680C;</code></pre><ul>
<li>서버는 클라이언트의 로그인 요청에 대한 응답을 작성할 때, 인증 정보는 서버에 저장하고 클라이언트 식별자인 JSESSIONID를 쿠키에 담습니다.</li>
<li>이후 클라이언트는 요청을 보낼 때 마다, JSESSIONID 쿠키를 함께 보냅니다.</li>
<li>서버는 JSESSIONID 유효성을 판별해 클라이언트를 식별합니다.</li>
</ul>
<h2 id="장단점">장단점</h2>
<ul>
<li>쿠키를 포함한 요청이 외부에 노출되더라도 세션ID자체는 유의미한 개인 정보를 담고 있지 않습니다.<ul>
<li>그러나 해커가 이를 중간에 탈취하여 클라이언트인 척 위장할 수 있다는 한계가 있습니다.</li>
</ul>
</li>
<li>각 사용자마다 고유한 세션 ID가 발급되기 때문에, 요청이 들어올 때마다 회원정보를 확인할 필요가 없습니다.</li>
<li>서버에서 세션 저장소를 사용하므로 요청이 많아지면 서버에 부하가 심해집니다.</li>
</ul>
<h1 id="jwt기반-인증">JWT기반 인증</h1>
<p>JWT(JSON Web Token)란 인증에 필요한 정보들을 암호화시킨 토큰을 의미합니다.</p>
<p>JWT기반 인증은 쿠키/세션 방식과 유사하게 JWT토큰을 HTTP헤더에 실어 서버가 클라이언트를 식별합니다.</p>
<h2 id="jwt-구조">JWT 구조</h2>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/834b9fe8-952f-4f18-a7cd-58acefe68bc2/image.png" alt=""></p>
<p>JWT 는<code>.</code>를 구분자로 나누어지는 3가지 문자열의 조합입니다.
실제 코딩된 JWT는 다음과 같은 구조를 가집니다.</p>
<h3 id="header">Header</h3>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/0dc47d38-00b5-4403-907f-81158b7a0610/image.png" alt="">
Header 의 alg와 typ은 각각 정보를 암호화할 해싱 알고리즘 및 토큰의 타입을 지칭합니다.</p>
<h3 id="payload">Payload</h3>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/5538725b-0bd9-4825-be89-7c9950402b44/image.png" alt="">
Payload는 토큰에 담을 정보를 가지고 있습니다.
주로 클라이언트의 고유 ID값 및 유효기간 등이 포함되는 영역입니다.
Key-Value형식으로 이루어진 한 쌍의 정보를 Claim 이라 지칭합니다.</p>
<h3 id="signature">Signature</h3>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/35e69735-109f-49fa-a99f-58694d6007f1/image.png" alt="">
Signature는 인코딩된 Header와 Payload를 더한 뒤 비밀키로 해싱하여 생성합니다.
Header 와 Payload는 단순히 인코딩 된 값이기 때문에 제 3자가 복호화 및 조작할 수 있지만, Signature는 서버측에서 관리하는 비밀키가 유출되지 않는 이상 복호화할 수 없습니다.
따라서 Signature 는 토큰의 위변조 여부를 확인하는데 사용됩니다.</p>
<h2 id="인증과정">인증과정</h2>
<ol>
<li>클라이언트 로그인 요청이 들어오면, 서버는 검증 후 클라이언트 고유ID등의 정보를 Payload에 담습니다.</li>
<li>암호화할 비밀키를 사용해 Access Token(JWT)을 발급합니다.</li>
<li>클라이언트는 전달받은 토큰을 저장해두고, 서버에 요청할 때 마다 토큰을 요청 헤더 <code>Authorization</code>에 포함시켜 함께 전달합니다.</li>
<li>서버는 토큰의 Signature를 비밀키로 복호화한 뒤, 위변조 여부 및 유효기간 등을 확인합니다.</li>
<li>유효한 토큰이라면 요청에 응답합니다.</li>
</ol>
<h2 id="장점">장점</h2>
<ul>
<li>Header 와 Payload를 가지고 Signature를 생성하므로 데이터 위변조를 막을 수 있습니다.</li>
<li>인증 정보에 대한 별도의 저장소가 필요 없습니다.</li>
<li>JWT는 토큰에 대한 기본 정보와 전달할 정보 및 토큰이 검증됬음을 증명하는 서명 등 필요한 모든 정보를 자체적으로 지니고 있습니다.</li>
<li>클라이언트 인증 정보를 저장하는 세션과 다르게, 서버는 무상태가 됩니다.</li>
<li>확장성이 우수합니다.</li>
<li>토큰 기반으로 다른 로그인 시스템에 접근 및 권한 공유가 가능합니다.</li>
<li>OAuth의 경우 Facebook,Google등 소셜 계정을 이용하여 다른 웹서비스에서도 로그인을 할 수 있습니다.</li>
<li>모바일 어플리케이션 환경에서도 잘 동작합니다.</li>
</ul>
<h2 id="단점-1">단점</h2>
<ul>
<li>쿠키/세션과 다르게 JWT는 토큰의 길이가 길어, 인증 요청이 많아질수록
네트워크 부하가 심해집니다.</li>
<li>Payload 자체는 암호화되지 않기 때문에 유저의 중요한 정보는 담을 수 없습니다.</li>
<li>토큰을 탈취당하면 대처하기 어렵습니다.</li>
<li>토큰은 한 번 발급되면 유효기간이 만료될 때 까지 계속 사용이 가능하기 때문입니다.</li>
<li>특정 사용자의 접속을 강제로 만료하기 어렵지만, 쿠키/세션 기반 인증은 서버쪽에서 쉽게 세션을 삭제할 수 있습니다.</li>
</ul>
<h1 id="보안전략">보안전략</h1>
<p>JWT 사용시 상기한 단점들을 극복하기 위해 다양한 전략을 채택할 수 있습니다.
각각의 전략은 장단점이 상이하기 때문에, 서비스의 특성을 고려하여 보안 수준을 높일지 사용자의 편의성을 높일지 결정해야 합니다.</p>
<h2 id="짧은-만료기한-설정">짧은 만료기한 설정</h2>
<p>토큰의 만료 시간을 짧게 설정하는 방법을 고려할 수 있습니다.
토큰이 탈취되더라도 빠르게 만료되기 때문에 피해를 최소화할 수 있습니다.
그러나 사용자가 자주 로그인해야하는 불편함이 수반됩니다.</p>
<h2 id="sliding-session">Sliding Session</h2>
<p>글을 작성하는 도중 토큰이 만료가 된다면 Form Submit 요청을 보낼 때 작업이 정상적으로 처리되지 않고, 이전에 작성한 글이 날아가는 등의 불편함이 존재합니다.
Sliding Session은 서비스를 지속적으로 이용하는 클라이언트에게 자동으로 토큰 만료 기한을 늘려주는 방법입니다.
글 작성 혹은 결제 등을 시작할때 새로운 토큰을 발급해줄 수 있습니다.
이를 통해 사용자는 로그인을 자주 할 필요가 없어집니다.</p>
<h2 id="refresh-token">Refresh Token</h2>
<p>클라이언트가 로그인 요청을 보내면 서버는 Access Token및 그보다 긴 만료 기간을 가진 Refresh Token을 발급하는 전략입니다.
클라이언트는 Access Token이 만료되었을 때 Refresh Token을 사용하여
Access Token의 재발급을 요청합니다.
서버는 DB에 저장된 Refresh Token과 비교하여 유효한 경우 새로운 Access Token을 발급하고, 만료된 경우 사용자에게 로그인을 요구합니다.</p>
<p>해당 전략을 사용하면 Access Token의 만료 기한을 짧게 설정할 수 있으며, 사용자가 자주 로그인할 필요가 없습니다.
또한 서버가 강제로 Refresh Token을 만료시킬 수 있습니다.</p>
<p>그러나 검증을 위해 서버는 Refresh Token을 별도의 storage에 저장해야합니다.
이는 추가적인 I/O작업이 발생함을 의미하기 때문에 JWT 의 장점(I/O작업이 필요 없는 빠른 인증처리)을 완벽하게 누릴 수 없습니다.
클라이언트도 탈취 방지를 위해 Refresh Token을 보안이 유지되는 공간에 저장해야 합니다.</p>
<h1 id="정리">정리</h1>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/f9354f77-47e0-400c-ad32-99cd7cd34619/image.png" alt=""></p>
<p>출처 : <a href="https://tecoble.techcourse.co.kr/post/2021-05-22-cookie-session-jwt/">https://tecoble.techcourse.co.kr/post/2021-05-22-cookie-session-jwt/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CORS]]></title>
            <link>https://velog.io/@dae_eun2/CORS</link>
            <guid>https://velog.io/@dae_eun2/CORS</guid>
            <pubDate>Fri, 14 Oct 2022 06:38:46 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dae_eun2/post/46958f9f-f0b0-4df1-8302-8086ba1d5207/image.png" alt="">
개발 하다보면 누구나 한번쯤은 봤을법한 메세지입니다</p>
<h1 id="출처origin-이란">출처(Origin) 이란?</h1>
<p>먼저 Origin에 대해 먼저 이해할 필요가 있다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/2b595560-51d9-42d4-9989-cd44a3ffd982/image.png" alt=""></p>
<p>서버의 위치를 의미하는 URL은 하나의 문자열과 같지만 위와 같이 구성되어 있다.</p>
<p>이때 출처(Origin)은 Protocol,Host,Port를 모두 합친 것을 의미한다.</p>
<h1 id="sop-same-origin-policy">SOP (Same Origin Policy)</h1>
<p>먼저 CORS를 보기전에, SOP에 대하여 알아보자
SOP는 <strong><em>같은 출처에서만 리소스를 공유할 수 있다</em></strong> 라는 규칙을 가진 정책이다.</p>
<p>즉 같은 프로토콜,호스트,포트를 사용한다면 다른 요소는 다르더라도 같은 출처로 인정된다.
반대로 리소스가 자신의 출처와 다를경우 브라우저는 교차출처를 요청한다.</p>
<p>출처를 비교하는 로직은 서버에서 구현된 스펙이 아닌 <strong>브라우저에 구현된 스펙이다.</strong>
만약 CORS정책을 위반하는 요청에 서버가 정상적으로 응답을 하더라도
브라우저가 이 응답을 분석해서 CORS정책에 위반되면 그 응답은 처리하지 않게 된다.</p>
<p>정리하자면 프로토콜,호스트,포트 모두 일치해야 Same Origin이며,
이들 중 하나라도 일치하지 않는다면 Cross Origin이 된다.</p>
<h1 id="cors-cross-origin-resource-sharing">CORS (Cross Origin Resource Sharing)</h1>
<blockquote>
<p>교차 출처 리소스 공유 (Cross-Origin Resource Sharing,CORS)는,
추가 HTTP 헤더를 사용하여 한 출처에서 실행중인 웹 애플리케이션이
<strong><em>다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여</em></strong> 하도록 브라우저에서 알려주는 체제다.</p>
</blockquote>
<p>CORS 에러는 CORS를 허용해서 아무런 문제 없이 다른 출처 리소스 공유를 해달라는 권고사항 같은 것이다.</p>
<h2 id="cors-기본-동작과정">CORS 기본 동작과정</h2>
<h3 id="1-클라이언트에서-http요청의-헤더에-origin을-담아-전달한다">1. 클라이언트에서 HTTP요청의 헤더에 Origin을 담아 전달한다</h3>
<p>다른 Origin의 리소스 요청시 클라이언트는 HTTP요청을 보낸다.
이 때 요청헤더의 Origin필드에는 요청을 보내는 Origin을 담아 보낸다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/17d9738c-6b9a-41f2-a3af-059fbc736417/image.png" alt=""></p>
<h3 id="2-서버는-응답헤더에-access-control-allow-origin을-담아-클라이언트로-전달한다">2. 서버는 응답헤더에 Access-Control-Allow-Origin을 담아 클라이언트로 전달한다.</h3>
<p>서버가 응답을 보낼때, 허락하는 Origin을 클라이언트에게 전달한다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/059b901d-5885-4917-b9df-3335249b8dc0/image.png" alt=""></p>
<h3 id="3-클라이언트에서-자신이-보냈던-요청의-origin과-서버가-보내준-access-control-allow-origin을-비교한다">3. 클라이언트에서, 자신이 보냈던 요청의 Origin과 서버가 보내준 Access-Control-Allow-Origin을 비교한다.</h3>
<p>자신이 보낸 Origin과 서버가 보내준 Access-Control-Allow-Origin을 비교하여 차단할지 말지를 결정한다.
만약 유효하지 않다면, 그 응답을 사용하지 않고 버린다.</p>
<blockquote>
<p><strong>서버 응답은 CORS정책 위반 여부에 관여하지 않는다.</strong>
CORS정책에 의해 Origin을 비교하는 로직은 브라우저에 구현되어 있다.
그래서 서버에서 정상적인 응답을 하여 상태코드가 200이 나오더라도,
브라우저가 응답을 CORS정책 위반이라고 분삭하면 그 응답은 사용하지 않는다.
브라우저가 CORS정책 위반을 분석하는 시간은 서버의 응답이 도착한 이후이다.
즉, CORS정책을 위반하는 리소스 요청때문에 에러가 발생하더라도 서버 쪽 로그에서는 정상응답을 했다는 로그만 남기 때문에,
CORS를 정확히 이해야하만 CORS에러를 해결할 수 있는것이다.</p>
</blockquote>
<h2 id="cors-작동-방식---3가지-시나리오">CORS 작동 방식 - 3가지 시나리오</h2>
<p>위에서 소개한 방식은 CORS의 가장 기본적인 작동 방식을 나타낸 것이고,
실제 CORS가 작동하는 방식은 3가지 시나리오에 따라 변경된다.</p>
<h3 id="예비요청-preflight-request">예비요청 (Preflight Request)</h3>
<p>브라우저는 요청을 한번에 보내지 않고, 예비요청과 본 요청을 나누어 서버에 전달한다.
이때 브라우저가 예비요청을 보내는 것을 preflight라고 부르며,
이 예비청의 메소드는 GET이나POST가 아닌 OPTIONS라는 요청이 사용된다.</p>
<p>예비요청의 역할은 본 요청을 보내기 전에 브라우저 스스로 안전한 요청인지 확인하는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/8b8f9660-4d0c-4179-af97-a70701015663/image.png" alt=""></p>
<ul>
<li>자바스크립트의 fetch API를 통해 브라우저에게 리소스를 받아 오려고 한다.</li>
<li>브라우저는 서버로 예비요청을 먼저 보낸다.</li>
<li>서버는 이 예비요청에 대한 응답으로 어떤 것을 허용하고 어떤 것을 금지하고 있는지에 대한 정보를 담아서 브라우저에게 다시 보내준다.</li>
<li>이후 브라우저는 보낸 요청과 서버가 응답해준 정책을 비교하여 해당 요청이 안전한지 확인하고 본 요청을 보내게 된다.</li>
<li>이후 서버가 본 요청에 대한 응답을 하면 최종적으로 이 응답 데이터를 자바스크립트로 넘겨준다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/a7d53d8d-512c-4b71-b290-6ec0dffa2ac7/image.png" alt=""></li>
</ul>
<p>이 다음에 배울 단순 요청은 GET, POST같은 서버 자체 데이터가 변경될 일이 적은 요청에서 작동된다고 보면 된다.</p>
<p>하지만 POST 같은 경우 서버에 데이터를 변경할수 있는 것이기 때문에, 따라서 개발자는 SOP만 믿을 것이 아니라 따로 서버 프로그래밍을 해야한다.</p>
<h3 id="단순요청-simple-request">단순요청 (Simple Request)</h3>
<p>단순요청은 예비요청을 보내지않고 바로 서버에 직행으로 본 요청을 보낸 후, 서버가 이에 대한 응답의 헤더에 Access-Control-Allow-Origin과 같은 값을 보내주면 브라우저가 CORS정책 위반여부를 검사하는 방식이다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/36a11f7b-535c-4d7b-b21c-540d75e3ac60/image.png" alt=""></p>
<p>즉, 단순요청(Simple Request)를 사용한다는 것은 예비요청(preflight request)를 생략한다는 뜻인데, 이러한 예비요청 생략은 아래의 3가지 경우를 만족할 때만 가능하다.</p>
<ul>
<li><p>요청의 메서드는 GET,HEAD,POST중 하나여야 한다.(PUT,DELETE는 반드시 preflight)</p>
</li>
<li><p>유저 에이전트가 자동으로 설정한 헤더외에, 수동으로 설정할 수 있는 헤더는
Fetch 명세에서 <code>CORS-safelisted request-header</code>로 정의한 헤더만 사용할 수 있다.</p>
<ul>
<li>Accept</li>
<li>Accept-Language</li>
<li>Content-Language</li>
<li>Content-Type</li>
<li>DPR</li>
<li>Downlink</li>
<li>Save-Data</li>
<li>Viewport-Width</li>
<li>Width</li>
</ul>
</li>
<li><p>Content-Type을 사용하는 경우에는 다음의 값들만 허용된다.</p>
<ul>
<li>application/x-www-form-urlencoded</li>
<li>multipart/form-data</li>
<li>text/plain</li>
</ul>
</li>
</ul>
<p>위 조건과 같이 다소 까다로운 조건들이 많기 때문에 위 조건을 모두 만족되어 단순요청이 일어나는 상황을 만드는것은 쉽지 않다.
왜냐하면 POST요청이라 해도 대부분 application/json 으로 통신하기 때문에 3번째 Content-Type이 위반되기 때문이다.</p>
<p>따라서 대부분의 요청은 그냥 예비 요청으로 이루어진다 라고 이해하면 된다.</p>
<h3 id="인증된-요청credentialed-request">인증된 요청(Credentialed Request)</h3>
<p><strong><em>기존 예비 요청에서 보안을 더 강화하고 싶을 때 사용한다</em></strong>
기본적으로 브라우저가 제공하는 비동기 리소스 요청 API인 XMLHttpRequest객체나 fetch API는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 헤더를 함부로 요청에 담지 않는다.
따라서, 요청에 인증과 관련된 정보(쿠키)를 담을 수 있게 해주는 옵션이 있는데 바로 <code>credentials</code>옵션이다.
이 옵션은 총 3가지 값을 사용할 수 있다.</p>
<table>
<thead>
<tr>
<th>옵션 값</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>same-origin(기본값)</td>
<td>같은 출처 간 요청에만 인증 정보를 담을 수 있다.</td>
</tr>
<tr>
<td>include</td>
<td>모든 요청에 인증 정보를 담을 수 있다</td>
</tr>
<tr>
<td>omit</td>
<td>모든 요청에 인증 정보를 담지 않는다</td>
</tr>
</tbody></table>
<p>만약 Credentials 옵션값인 same-origin 이나 include와 같은 옵션을 사용하여 리소스 요청에 인증 정보가 포함된다면, 브라우저는 다른 출처의 리소스를 요청할 때 Access-Control-Allow-Origin만 확인 하는것이 아니라 다른 조건을 추가로 검사하게 된다.</p>
<p>예를들어, credentials 옵션을 사용하여 요청에 인증정보가 담겨있는 상태에서 다른 출처의 리소스를 요청헤가 되면, 브라우저는 CORS정책 위반 여부를 검사하는 룰에 다음 두가지를 추가하게 된다.</p>
<ul>
<li>Access-Control-Allow-Origin 에는 모든 요청을 허용하는 <code>*</code>을 사용할 수 없으며, 명시적인 URL이여야 한다.</li>
<li>응답 헤더에는 반드시 Access-Control-Allow-Credentails :true 가 존재해야 한다.<blockquote>
<p>인증된 요청 역시 Preflight가 먼저 일어난다.</p>
</blockquote>
</li>
</ul>
<p>출처 : <a href="https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F">https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[그런 REST API로 괜찮은가 정리]]></title>
            <link>https://velog.io/@dae_eun2/%EA%B7%B8%EB%9F%B0-REST-API%EB%A1%9C-%EA%B4%9C%EC%B0%AE%EC%9D%80%EA%B0%80-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dae_eun2/%EA%B7%B8%EB%9F%B0-REST-API%EB%A1%9C-%EA%B4%9C%EC%B0%AE%EC%9D%80%EA%B0%80-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 13 Oct 2022 16:57:58 GMT</pubDate>
            <description><![CDATA[<h1 id="rest-api-란">REST API 란?</h1>
<p>REST 는 REpresentational State Transfer의 약자이다.</p>
<blockquote>
<p>a way of providing interoperability between computer systems on the Internet.</p>
</blockquote>
<ul>
<li>REST API 란 ? REST 아키텍처를 따르는 API 이다.</li>
<li>REST 란 ? 분산 하이퍼 미디어 시스템 (ex.웹) 을 위한 아키텍처 스타일</li>
<li>아키텍처 스타일 ? 제약 조건의 집합</li>
</ul>
<p>즉,REST에서 정의한 제약 조건을 모두 지켜야 REST를 따른다고 말할 수 있다는 것이다.</p>
<p>사실 REST는 아키텍처 스타일이면서 하이브리드 아키텍처 스타일 이라고 말한다.
왜냐하면 아키텍처 스타일이면서, 동시에 아키텍처 스타일의 집합이기 때문이다.</p>
<h2 id="rest-아키텍처-스타일">REST 아키텍처 스타일</h2>
<p><a href="https://velog.io/@dae_eun2/REST-REST-API-%EB%9E%80#rest%EC%9D%98-%ED%8A%B9%EC%A7%95">전글</a>을 참조하면 좋다.</p>
<ul>
<li>Client-Server</li>
<li>Stateless</li>
<li>Cache</li>
<li>Uniform Interface</li>
<li>Layered System</li>
<li>code - on - demand(optioanl)</li>
</ul>
<p>Code-on-Demand는 서버에서 코드를 클라이언트로 보내서 실행할 수 있어야 한다는 것을 의미하며, 즉 자바스크립ㅂ트를 의미한다.</p>
<p>대체로 REST라고 부르는 것들은 위의 조건을 대부분 지키고 있다.
HTTP만 잘 따라도 Uniform Interface를 제외하곤 다 지킬 수 있기 때문이다.
하지만 Uniform Interface는 잘 지켜지지 않는다고 한다.</p>
<h2 id="uniform-interface-제약조건">Uniform Interface 제약조건</h2>
<ul>
<li><p>Identification or resource : 리소스가URL로 식별된다.</p>
</li>
<li><p>Manipulation or resources through representations : represenstation 전송을 통해 resources를 조작해야한다.</p>
<ul>
<li>representation은 HTTP 메소드(PUT,DELETE,GET,POST등)을 의미한다.</li>
</ul>
</li>
<li><p>self-descriptive messages : 메세지는 스스로를 설명할 수 있어야 한다.</p>
</li>
<li><p>hypermedia as the enginer of application state (HATEOAS) : 애플리케이션의 상태는 HyperLink를 이용해서 전이가 되어야한다.</p>
</li>
</ul>
<p>위 2가지 조건은 대부분 잘 지켜지는 반면, 아래 2가지 조건이 잘 지켜지지 않는다고 한다.</p>
<h2 id="self-descriptive-message">Self-descriptive message</h2>
<h3 id="메세지는-스스로를-설명해야-한다">메세지는 스스로를 설명해야 한다.</h3>
<p>예를들어 아래와 같은 메세지가 있다고 해보자</p>
<pre><code>GET / HTTP/1.1</code></pre><p>단순히 루트를 얻어오는 GET 요청이다.
이 HTTP 요청 메세지는 뭔가 빠져 있어 self-descriptive하지 못하다.</p>
<p>우선 목적지가 빠져있다.</p>
<pre><code>GET /HTTP/1.1
Host: www.example.org</code></pre><p>이 요청이 <a href="http://www.example.org">www.example.org</a> 라는 도메인으로 간다 라는 목적지가 빠져 있기 때문에 self-descriptive 하지 않다고 한다.</p>
<p>또 이런 예시도 있다</p>
<pre><code>HTTP/1.1 200 OK
[ { &quot;op&quot;: &quot;remove&quot;, &quot;path&quot;: &quot;/a/b/c&quot; } ]</code></pre><p>위 코드는 이걸 클라이언트가 해석하려면, 어떤 문법으로 작성된 것인지 모르기 때문에 해석에 실패하여 self-descriptive하지 않다고 한다.
그렇기 때문에 이게 어떤 타입인지를 명시해줘야한다.</p>
<pre><code>HTTP/1.1 200 OK
Content-Type: application/json

[ { &quot;op&quot;: &quot;remove&quot;, &quot;path&quot;: &quot;/a/b/c&quot; } ]</code></pre><p>그렇다면 이제 Self-descriptive하다고 볼 수 있는가는 아니다.
해석했다고 하더라도, op 값은 무슨 뜻이고, path가 무엇을 의미하는지는 알 수 없다.</p>
<pre><code>HTTP/1.1 200 OK
Content-Type: application/json-patch+json

[ { &quot;op&quot;: &quot;remove&quot;, &quot;path&quot;: &quot;/a/b/c&quot; } ]</code></pre><p>이렇게 작성을 하면 완전해진다.
이 응답은 json-patch+json이라는 미디어 타입으로 정의된 메시지 이기 때문에 json-patch라는 명세를 찾아가 이 메세지를 해석하면 올바르게 메세지의 의미를 이해할 수 있게 된다.</p>
<p>이처럼 Self-descriptive message라는 것은 메시지를 봤을 때 메시지의 내용으로 온전히 해석이 다 가능해야된다는 것이다.</p>
<h2 id="hateoas">HATEOAS</h2>
<p>애플리케션의 상태는 HyperLink를 이용해 전이되어야 한다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/719e3bf0-1715-4eb5-8245-5a1c090d544d/image.png" alt="">
이렇게 상태를 전이하는 것을 애플리케이션 상태 전이라 하고, 이 상태 전이마다
항상 해당 페이지에 있던 링크를 따라가면서 전이 했기 때문에 HATEOAS라 할 수 있다.</p>
<p>그래서 html 같은 경우는 HATEOAS를 만족하게 되는데,</p>
<pre><code>HTTP/1.1 200 OK
Content-Type: text/html

&lt;html&gt;
&lt;head&gt; &lt;/head&gt;
&lt;body&gt; &lt;a href=&quot;/test&quot;&gt; test &lt;/a&gt; &lt;/body&gt;
&lt;/html&gt;
</code></pre><p>a 태그를 통해 하이퍼 링크를 통해 다음 상태로 전이가 가능하기 때문에 만족한다고 볼 수 있다.</p>
<p>JSON으로 표현하면</p>
<pre><code>HTTP/1.1 200 OK
Content-Type: application/json
Link: &lt;/articles/1&gt;; rel=&quot;previous&quot;,
      &lt;/articles/3&gt;; rel=&quot;next&quot;;
{
    &quot;title&quot;: &quot;The second article&quot;,
    &quot;contents&quot;: &quot;blah blah...&quot;
}</code></pre><p>Link라는 헤더가 있는데, 이것이 바로 이 리소스와 하이퍼링크로 연결되어 있는 다른 리소스를 가르킬 수 있는 기능을 제공해준다.</p>
<p>여기서 어떤 1개의 게시물을 표현 했는데, 이전의 게시물 URI가 /articles/1, 다음 게시물은 /articles/3에 있다는 정보를 표현해준 것이다.</p>
<p>또한, 이 정보는 Link 헤더가 이미 표준으로 문서가 나와 있기 때문에 이 메시지를 보낸 사람이 온전히 해석해서 어떻게 링크가 되어 있는가를 이해하고 하이퍼링크를 타고 다른 상태로 전이가 가능하다.</p>
<h2 id="왜-uniform-interface가-필요한가-">왜 Uniform Interface가 필요한가 ?</h2>
<h3 id="독립적-진화">독립적 진화</h3>
<ul>
<li>서버와 클라이언트가 각각 독립적으로 진화한다.</li>
<li>서버의 기능이 변경되어도 클라이언트를 업데이트할 필요가 없다.</li>
<li>REST를 만든 계기가 <code>How do i improve HTTP without breaking the Web</code> 어떻게 웹이 깨지지 않고 HTTP를 개선할 수 있는가 이다.</li>
</ul>
<p>독립적으로 진화를 한다면 ??
서버의 기능이 바뀌고, API가 추가되고 변동이 생겨도, 클라이언트는 바뀌지 않아도 된다.</p>
<p>이를 위해서는 Unifrom Interface가 필수적이기기에, 이를 만족하지 못하면 REST라고 부를 수 없는 것이다.</p>
<h3 id="실제로-잘-지켜지는-곳은-어디인가-">실제로 잘 지켜지는 곳은 어디인가 ?</h3>
<p>바로 웹이다.</p>
<ul>
<li>웹 페이지를 변경했다고 웹 브라우저를 업데이트 할 필요는 없다.</li>
<li>웹 브라우저를 업데이트했다고 웹 페이지를 변경할 필요도 없다.</li>
<li>HTTP명세서가 변경되어도 웹은 잘 작동한다.</li>
<li>HTML명세서가 변경되어도 웹은 잘 작동한다.</li>
</ul>
<p>하지만 모바일 앱은 많은 문제가 있다고 한다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/f329d77c-b1db-4f17-a7ac-8df660b54099/image.png" alt="">
서버의 기능이 변경되었는데, 클라이언트가 그거을 지원해주는데 한계가 있을 때
업데이트를 진행하는 것이다.
이는 모바일 앱 클라이언트 와 서버가 REST아키텍처 스타일을 따르지 않고있다 라고 말할 수 있다.</p>
<p>웹은 어떻게 가능한 것인가?</p>
<ul>
<li>마법은 없다. 엄청난 노력의 결실이다.</li>
<li>W3C Working groupsHTML5 첫 초안에서 권고안 나오는데까지 6년</li>
<li>IETF Working groups HTTP/1.1 명세 개정판 작업하는데 7년
기능의 추가 없이, 하위 호환성 문제를 위해 문서를 가다듬기만 했다고 한다.</li>
<li>웹 브라우저 개발자들 의 노력</li>
<li>웹 서버 개발자들 의 노력</li>
</ul>
<h2 id="상호운용성interoperability에-대한-집착">상호운용성(Interoperability)에 대한 집착</h2>
<ul>
<li>Referer -&gt; 오타가 났지만 고치지 않음</li>
<li>charset -&gt; 잘못 지은 이름이지만 고치지 않음</li>
<li>HTTP 상태코드 416 포기</li>
<li>HTTP/0.9를 여전히 지원(크롬,파이어폭스)</li>
</ul>
<p>25년전 Referer 오타를 냈다.
charset이라는 이름을 잘 못 지었다.
하지만 고치지 않는다 왜?
이름을 고치게 되면 상호운용성이 깨지기 때문이다.
HTTP 상태코드 416도 만우절때 만들었던 코드인데 http가 아니라 상관이 없었지만, 이미 몇몇 서버들이 이를 HTTP상태 코드로 구현해 버렸다.
그래서 416코드를 아예 영구 결번으로 만들어 버렸다.</p>
<p>이렇게 상호 운용성에 대한 많은 노력과 시행착오가 모여 덕분에 독립적인 진화를 할 수 있다.</p>
<h2 id="원격-api가-꼭-rest-api-여야-하는가">원격 API가 꼭 REST API 여야 하는가?</h2>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/d1706db7-9531-4769-872d-2430811d0351/image.png" alt="">
시스템 전체를 통제할 수 있다 ? </p>
<ul>
<li>클라이언트 개발자를 통제 가능할때 또는, 클라이언트, 서버를다 만들 때</li>
</ul>
<p>진화에 관심이 없다 ? </p>
<ul>
<li>매일 업데이트해서 유저의 불만이 나와도 상관이 없다.</li>
</ul>
<p>하지만 우리는 둘다 어려우니... REST를 따라야 한다.</p>
<h2 id="어떻게-해야-하는가">어떻게 해야 하는가?</h2>
<ol>
<li>REST API를 구현하고 REST API라 부른다.</li>
<li>REST API를 포기하고 HTTP API라 부른다.</li>
<li>REST API가 아니지만, REST API라 부른다. (현재 대부분의 상태)</li>
</ol>
<h2 id="왜-api는-rest가-어려울까">왜 API는 REST가 어려울까?</h2>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/e04acb7c-78bf-408e-8fbf-6632542aa076/image.png" alt="">
웹과 API를 비교해보면,
커뮤니케이션이 다르다.
HTTP API는 사람이 아닌 기계가 해석한다.
또한 미디어 타입이 다르다.
<img src="https://velog.velcdn.com/images/dae_eun2/post/38d0d60e-f30e-4744-94bd-8e40ff8a543f/image.png" alt="">
self-descriptive 측면에서 보면 JSON은 불완전하다.
완전 하다는 것은 적어도 문법은 정의되어 있다. 
어떻게 파싱하고 array를 어떻게 해석해라 까지는 되어있다.
하지만 안에 들어갈 수 있는 key-value에 대한 의미는 아무도 정의되지 않는다.</p>
<p>즉, 문법은 해석 가능하지만 의미를 해석하려면 별도로 문서(API 문서 등)가 필요하다.</p>
<h3 id="해결-방법">해결 방법</h3>
<ol>
<li><p>self-descriptive message
<img src="https://velog.velcdn.com/images/dae_eun2/post/b514b282-f7ab-4ba0-b8db-68f79e469c23/image.png" alt="">
<img src="https://velog.velcdn.com/images/dae_eun2/post/c0f78a4b-e0ba-4b4f-89dc-7b0b0e6e3c9e/image.png" alt=""></p>
</li>
<li><p>HATEOAS
<img src="https://velog.velcdn.com/images/dae_eun2/post/8e5571f0-863f-46e3-bea9-091feee8c76e/image.png" alt="">
<img src="https://velog.velcdn.com/images/dae_eun2/post/e27f751b-a666-422f-a544-b9f7b92f968d/image.png" alt=""></p>
</li>
</ol>
<h2 id="정리">정리</h2>
<ul>
<li>오늘날 대부분의 REST API는 사실 REST를 따르고 있지 않다.</li>
<li>REST의 제약 조건 중 특히 self-descriptive와 HATEOAS를 잘 만족하지 못한다.</li>
<li>REST 긴 시간에 걸쳐(수십년) 진화하는 웹 애플리케이션을 위한 것이다.</li>
<li>REST를 따를 것인지는 API를 설계하는 이들이 스스로 판단하여 결정해야 한다.</li>
<li>REST를 따르겠다면, self-descriptive와 HATEOAS를 만족시켜야 한다.<ul>
<li>self-descriptive는 custom media type 이나 profile link relation 등으로 만족시킬 수 있다.<ul>
<li>HATEOAS는 HTTP헤더나 본문에 링크를 담아 만족시킬 수 있다.</li>
</ul>
</li>
<li>REST를 따르지 않겠다면, &quot;REST를 만족하지 않는 REST API&quot;를 뭐라고 부를지 결정해야할 것이다.<ul>
<li>HTTP API라고 부를 수도 있고</li>
<li>그냥 이대로 REST API라고 부를 수도 있다 (roy가 싫어합니다)</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[REST , REST API 란? ]]></title>
            <link>https://velog.io/@dae_eun2/REST-REST-API-%EB%9E%80</link>
            <guid>https://velog.io/@dae_eun2/REST-REST-API-%EB%9E%80</guid>
            <pubDate>Thu, 13 Oct 2022 15:08:33 GMT</pubDate>
            <description><![CDATA[<h1 id="rest-란">REST 란?</h1>
<h2 id="rest의-정의">REST의 정의</h2>
<p>&quot;REpresentational State Transfer&quot;의 약자로,
자원을 이름(자원의 표현)으로 구분해 해당 자원의 상태(정보)를 주고 받는 모든 것을 의미합니다.
즉, 자원(resource)의 표현(representation)에 의한 상태 전달을 뜻합니다.</p>
<ul>
<li>자원 : 해당 소프트웨어가 관리하는 모든 것(문서, 그림,데이터,해당 소프으웨어 등)</li>
<li>표현 : 그 자원을 표현하기 위한 이름 (DB의 학생 정보가 자원이면,&#39;students&#39;를 자원의 표현으로 정함)</li>
<li>상태 전달 : 데이터가 요청되는 시점에 자원의 상태를 전달한다(JSON 혹은  XML을 통해 데이터를 주고 받는 것이 일반적)</li>
</ul>
<p>REST는 기본적으로 웹의 기존 기술과 HTTP프로토콜을 그대로 활용하기 때문에,
<strong>웹의 장점을 최대한 활용할 수 있는 아키텍처 스타일</strong> 입니다.
REST는 네트워크 상에서 Client 와 Server 사이의 통신 방식 중 하나입니다.</p>
<h2 id="rest의-개념">REST의 개념</h2>
<p>어떤 자원에 대해 CRUD연산을 수행하기 위해 URI(Resource)로 GET,POST등의 방식(Method)을 사용하여 요청을 보내며, 요청을 위한 자원은 특정한 형태(Representation of Resoucre)로 표현됩니다.</p>
<blockquote>
<p>URL 과 URI의 차이점
URL은 Uniform Resource Locator로 인터넷 상 자원의 위치를 의미합니다.
URI는 Uniform Resource Identifier로 인터넷 상의 자원을 식별하기 위한 문자열의 구성으로, URI는 URL을 포함하게 됩니다.
URI가 URL보다 포괄적인 범위라 할 수 있습니다.</p>
</blockquote>
<h2 id="rest의-구성-요소">REST의 구성 요소</h2>
<h3 id="1-자원resource---uri">1. 자원(Resource) - URI</h3>
<ul>
<li>모든 자원에는 고유한 ID가 존재하고, 이 자원은 Server에 존재합니다.</li>
<li>자원을 구별하는 ID는 <code>/exgorups/:exgroup_id</code>와 같은 HTTP URI입니다.</li>
<li>Client는 URI를 이용해 자원을 지정하고 해당 자원의 상태(정보)에 대한 조작을 Server에 요청합니다.</li>
</ul>
<h3 id="2-행위verb---method">2. 행위(Verb) - Method</h3>
<ul>
<li>HTTP 의 Method를 사용합니다.</li>
<li>HTTP는 GET,POST,PUT,PATCH,DELETE의 Method를 제공합니다.</li>
</ul>
<table>
<thead>
<tr>
<th>Method</th>
<th>내용</th>
</tr>
</thead>
<tbody><tr>
<td>GET</td>
<td>Read: 정보 요청, URI가 가진 정보를 검색하기 위해 서버에 요청한다.</td>
</tr>
<tr>
<td>POST</td>
<td>Create : 정보 입력, 클라이언트에서 서버로 전달하려는 정보를 보낸다.</td>
</tr>
<tr>
<td>PUT</td>
<td>Update : 정보 업데이트, 주로 내용을 갱신하기 위해 사용한다.(데이터 전체를 바꿀때)</td>
</tr>
<tr>
<td>PATCH</td>
<td>Update : 정보 업데이트, 주로 내용을 갱신하기 위해 사용한다. (데이터 일부만 바꿀때)</td>
</tr>
<tr>
<td>DELETE</td>
<td>Delete : 정보삭제, 안전성 문제로 대부분 서버에서 비활성화한다.</td>
</tr>
</tbody></table>
<h3 id="3-표현-representation-of-resource">3. 표현 (Representation of Resource)</h3>
<ul>
<li>Client와 Server가 데이터를 주고 받는 형태로 JSON,XML,TEXT,RSS등이 있습니다.</li>
<li>JSON,XML을 통해 데이터를 주고 받는 것이 일반적 입니다.</li>
</ul>
<h2 id="rest의-특징">REST의 특징</h2>
<h3 id="1-server-client서버---클라이언트-구조">1. Server-Client(서버 - 클라이언트 구조)</h3>
<ul>
<li>자원이 있는 쪽이 Server,요청하는 쪽이 Client가 됩니다.</li>
<li>REST server는 API를 제공하고 비지니스 로직 처리 및 저장을 책임지며,</li>
<li>Client는 사용자 인증이나 context(세션, 로그인 정보 등)등을 직접 관리하고 책임집니다.</li>
<li>역할을 확실히 구분시키믕로써 서로간의 의존성을 줄입니다.</li>
</ul>
<h3 id="2-stateless">2. Stateless</h3>
<ul>
<li>HTTP 는 Stateless Protocol이므로 REST 또한 Stateless입니다.</li>
<li>Client의 context를 Server에 저장하지 않습니다.</li>
<li>Server는 각각의 요청을 완전히 별개의 것으로 인식하고 처리합니다.</li>
<li>각 API서버는 Client의 요청만을 단순 처리합니다.</li>
<li>즉, 이전 요청이 다음 요청의 처리에 연관되어서는 안됩니다.(DB에 의해 바뀌는 것은 허용)</li>
<li>Server의 처리 방식에 일관성을 부여하기 때문에 서비스의 자유도가 높아집니다.</li>
</ul>
<h3 id="3-cacheable">3. Cacheable</h3>
<ul>
<li>웹 표준 HTTP를 그대로 사용하므로 웹에서 사용하는 기존의 인프라를 그대로 사용할 수 있습니다.<ul>
<li>즉, HTTP가 가진 가장 강력한 특징 중 하나인 캐싱 기능을 적용할 수 있습니다.</li>
<li>HTTP에서 사용하는 Last-Modifed Tag 또는 E-Tag를 이용해 캐싱을 구현합니다.</li>
<li>대량의 요청을 효율적으로 처리할 수 있습니다.</li>
</ul>
</li>
</ul>
<h3 id="4-layered-system-계층-구조">4. Layered System (계층 구조)</h3>
<ul>
<li>Client는 REST API server만 호출합니다.</li>
<li>REST Server는 다중 계층으로 구성될 수 있습니다.<ul>
<li>보안, 로드 밸런싱, 암호화 등을 위한 계층을 추가하여 구조를 변경 할 수 있습니다.</li>
<li>Proxy,Gateway와 같은 네트워크 기반의 중간매체를 이용할 수 있습니다.</li>
<li>하지만 Client는 Server와 직접 통신하는지, 중간 서버와 통신하는지 알 수 없습니다.</li>
</ul>
</li>
</ul>
<h3 id="5-uniform-interface인터페이스-일관성">5. Uniform Interface(인터페이스 일관성)</h3>
<ul>
<li><p>URI로 지정한 Resource에 대한 요청을 통일되고, 한정적으로 수행하는 아키텍처 스타일을 의미합니다.</p>
<ul>
<li>HTTP를 따르는 모든 플랫폼에서 사용이 가능하며, Loosely Coupling(느슨한 결함)형태를 갖습니다.</li>
</ul>
<h3 id="6-self-descriptiveness">6. Self-Descriptiveness</h3>
<ul>
<li>요청 메세지만 보고도 쉽게 이해할 수 있는 자체 표현 구조로 되어있습니다.</li>
</ul>
<hr>
<h1 id="rest-api-란">REST API 란?</h1>
<h2 id="rest-api의-정의">REST API의 정의</h2>
<p>REST의 특징을 기반으로 서비스 API를 구현한 것</p>
<h2 id="rest-api의-특징">REST API의 특징</h2>
<p>REST API의 가장 큰 특징은 각 요청이 어떤 동작이나 정보를 위한 것인지를 그 요청의 모습 자체로 추론이 가능한 것 입니다.</p>
<h2 id="rest-api-디자인-가이드">REST API 디자인 가이드</h2>
<p>REST API 설계시 가장 중요한 항목은 다음의 2가지로 요약합니다.</p>
<ul>
<li>첫 번째,URI는 정보의 자원을 표현해야 한다.</li>
<li>두 번째,자원에 대한 행위는 HTTP Method로 표현한다.</li>
</ul>
<h2 id="rest-api의-설계-규칙">REST API의 설계 규칙</h2>
</li>
</ul>
<ol>
<li><p>URI는 명사를 사용한다.(리소스 명은 동사가 아닌 명사를 사용해야 한다)</p>
</li>
<li><p>슬래시(/)로 계층관계를 표현한다.</p>
</li>
<li><p>URI마지막 문자로 슬래시를 포함하지 않는다.</p>
</li>
<li><p>언더바를 사용하지 않고, 하이픈을 사용한다.</p>
</li>
<li><p>URI는 소문자로만 구성한다.</p>
</li>
<li><p>HTTP응답 상태 코드 사용</p>
<ul>
<li>클라이언트는 해당 요청에 대한 실패, 처리완료 또는 잘못된 요청등에 대한 피드백을 받아야한다.</li>
</ul>
</li>
<li><p>파일확장자는 URI에 포함하지 않는다.
Ex) <a href="http://dev-coco.tistory.com/restapi/220/photo.jpg">http://dev-coco.tistory.com/restapi/220/photo.jpg</a> (X)</p>
</li>
</ol>
<h2 id="rest-api와-restful-api차이는-뭘까">REST API와 RESTful API차이는 뭘까?</h2>
<p>REST의 설계 규칙을 잘 지켜서 설계된 API를 RESTful한 API라 합니다.
즉, REST의 원리를 잘 따르는 시스템을 RESTful이란 용어로 지칭됩니다.</p>
<p>RESTful하게 만든 API는 요청을 보내는 주소만으로도 어떤 것을 요청 하는지 파악이 가능합ㅂ니다.</p>
<h2 id="rest-api를-간단하게-요약하자면">REST API를 간단하게 요약하자면</h2>
<p>URI는 정보의 자원만 표현해야 하며, 자원의 행위는 HTTP Method에 명시한다는 것 입니다.</p>
<p>출처 : <a href="https://dev-coco.tistory.com/97">https://dev-coco.tistory.com/97</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTPS 동작 과정]]></title>
            <link>https://velog.io/@dae_eun2/HTTPS-%EB%8F%99%EC%9E%91-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@dae_eun2/HTTPS-%EB%8F%99%EC%9E%91-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Thu, 13 Oct 2022 08:18:47 GMT</pubDate>
            <description><![CDATA[<h2 id="https의-등장-배경">HTTPS의 등장 배경</h2>
<p>인터넷에서 내 정보를 어디론가 전달하거나, 열람 하는 경우가 많다.
예를 들면, 포털 사이트에 내 아이디와 비밀번호를 입력하여 로그인을 하거나,
은행 사이트에 들어가서 내 계좌정보를 조회하는 경우 등이 있다.
만약 인터넷에서 누군가가 귀중한 정보를 송수신하는 동안에 정보를 몰래 감시해서,나의 정보를 보거나, 악용한다면 더이상 안전한 거래를 할 수 없을것이다.</p>
<p>HTTP는 암호화 되지 않는 방법으로 데이터를 전송하기 때문에
서버와 클라이언트가 주고 받는 데이터를 스니핑 하는것이 매우 쉽다.
이를 방지하기 위해 만든 것이 HTTPS이다.</p>
<h2 id="https란">HTTPS란?</h2>
<blockquote>
<p>HTTPS는 HTTP에 Secure라는 말이 추가 된 것이다. 
즉, HTTPS는 보완이 강호된 HTTP라는 것을 짐작할 수 있다.</p>
</blockquote>
<p>인터넷은 안전한 통신을 위하여 암호화를 한다.
암호화란 일반적인 평문을 알아볼 수 없도록 암호문으로 만드는 과정이다.
암호문을 상대방에게 전달하고, 상대방은 이를 다시 복호화 하여 평문으로 볼 수 있다.
이와 같은 과정을 웹 브라우저와 웹 서버에서 사용하는 대표적인 기술이 바로
HTTPS(Hyper-Text-Transfer-Secure)이다.
인터넷 콘텐츠를 전달하는 TCP프로토콜의 일종인 HTTP에 Secure 기능을 더한 것 이다.</p>
<h2 id="ssltls란">SSL(TLS)란?</h2>
<p>HTTPS는 SSL(Secure Socket Layer)/TLS(Transport Layer Security)전송 기술을 사용한다.
TCP,UDP와 같은 일반적인 인터넷 통신에 보안계층을 추가 하는 방식이다.
그리고 이 기술을 구현하기 위해 웹 서버에 설치하는것이 SSL/TLS 인증서 이다.
TLS는SSL의 개선 버전으로, 최신 인증서는 TLS를 사용하지만 편의상 SSL인증서라고 부르고 있다.</p>
<h2 id="대칭키-공개키비대칭키">대칭키, 공개키(비대칭키)</h2>
<h3 id="대칭키">대칭키</h3>
<p>대칭키 암호화 박식이란, 하나의 키로 평문을 암호화 하고 , 다시 암호문을 복호화 할때 같은 키를 사용하는 방식이다.
대칭키 암호화 방식은 키를 단 하나만 사용한다는 간편함이 있지만,
같은 키를 사용하기 위해선 클라이언트에서 서버로 대칭키를 보내야하고,
이 키가 중간에 가로채져 악용 될 수 있다는 단점이 있다.</p>
<h3 id="공개키비대칭키">공개키(비대칭키)</h3>
<p>공개키 암호화 방식은 공개키,개인키 이렇게 두개의 키를 한 쌍으로 각각 암호화/복호화에 사용한다.
일반적으로 공개키로 암호화를 하고 개인키로 복호화를 진행한다.
개인키를 먼저 만들고, 여기서 공개키를 파생하여 한 쌍의 키를 만들기 때문에
key pair라 부르기도 한다.
만약 같은 쌍이 아닌 키를 사용하려면 암호화/복호화가 불가능하다.</p>
<p>공개키는 말 그대로 누구에게나 공개할 수 있는 키이다.
엇필 들으면 보안에 사용하는 키를 누구에게나 공개 할 수 있다는 점이 불안해 보일 수 있지만,
개인키 없이는 복호화가 불가능 하기 때문에 안전하다.</p>
<p>참고) 개인 키와 공개 키는 쌍으로 동시에 생성이 된다.</p>
<ul>
<li>개인 키로 매번 공개키를 만들어 낼 수 있는것은 아니다.</li>
<li>개인키를 만들때, 공개키도 같이 만들어진다.</li>
</ul>
<h3 id="전자서명">전자서명</h3>
<p>비공개키의 소유자는 비공개 키를 이용해서 정보를 암호화 한 후에 공개키와 함께 암호화된 정보를 전송한다.
정보와 공개키를 획든한 사람은, 공개키를 이용해 암호화된 정보를 복호화한다.
이 과정에서 공개키가 유출 된다면, 의도하지 않은 공격자에 의해 데이터가 복호화될 위험이 있다.</p>
<p>이런 위험에도 불구하고 비공개키를 이용해 암호화 하는 이유는 뭘까?
비공개키는 데이터를 보호하는것이 목적이 아니다.</p>
<p>암호화된 데이터를 공개키를 가지고 복호화할 수 있다는 것은 그 데이터가 공개키와 쌍을 이루는 비공개키에 의해서 암호화되었다는 것을 의미힌다.</p>
<p>즉, 공개키가 데이터를 제공한 사람의 신원을 보장해주는 것이다. 
이러한 것을 전자서명 이라 한다.</p>
<h3 id="ssl-인증서와-ca">SSL 인증서와 CA</h3>
<p>SSL을 적용하기 위해서는 인증서가 필요하다.
SSL인증서는 아래의 2가지를 제공한다.</p>
<ul>
<li>클라이언트가 접속한 서버가 신뢰있는 서버임을 증명한다.
즉, 클라이언트가 접속한 서버가 클라이언트가 목적한 서버가 맞는지 확인한다.</li>
<li>SSL통신에 사용될 공개키를 클라이언트에게 제공한다.
서버와 클라이언트가 통신할 때 사용될 공개키와 공개키 암호화 방법들의 정보가 있다.</li>
</ul>
<p>CA는 이 SSL인증서를 발급해 주는 기업이다.
인증서가 보안에 관련된 것인만큼 CA는 신뢰할 수 있는 기업에서만 가능하다.</p>
<p>SSL 인증서 및 내용은 CA에 의해서 암호화 된다.
이때 CA는 자신의 비공개키로 서버가 제출한 인증서를 암호화한다.</p>
<p>CA의 비공개키는 절대로 유출되어서는 안된다.</p>
<h3 id="브라우저와-ca">브라우저와 CA</h3>
<p>브라우저는 내부적으로 CA의 리스트를 미리 알고 있다.
즉, 브라우저 소스안에 CA리스트가 존재한다.
CA리스트 뿐만 아니라,각 CA의 공개키도 이미 브라우저 안에 내장되어 있다.</p>
<p>브라우저가 서버에 접속할 때, 서버는 클라이언트에게 인증서를 제공한다.
브라우저는 서버에서 받은 인증서가 자신이 가지고 있는 CA리스트에 있는가를 확인한다.</p>
<p>비교 후 정상적인 인증서면,해당 CA의 공개키를 이용하여, 인증서를 복호화한다.
복호화가 되었다면, 그 인증서는 CA의 비공개키로 암호화된것을 의미한다.</p>
<p>즉, 해당 CA에 의해 정상적으로 발급된 인증서라는 의미가 되며, 그것은 클라이언트가 접속한 서버에서 제공한 인증서가 해당 CA에 의해 정상적으로 발급됐다는 의미이다. </p>
<h2 id="ssl의-동작">SSL의 동작</h2>
<h3 id="악수handshake">악수(handshake)</h3>
<p>클라이언트, 서버 간의 통신을 하기 전 실제 통신을 할 수 있는지, 서로 검토하는 단계 이다.</p>
<p>클라이언트가 서버에 처음 접속하게 되는 시점, 단계를 client hello라 하고,
또한, 서버는 Client Hello에 대한 응답으로 Server Hello를 하게 된다. </p>
<p>Client Hello 단계</p>
<ul>
<li>클라이언트에서 랜덤한 데이터를 생성한다.</li>
<li>클라이언트에서 사용할 수 있는 암호화 방식들을 서버에 전달한다.</li>
<li>클라이언트에서 사용되는 암호화 방식과 서버에서 가능한 암호화 방식이 다를 수 있기 때문에, 두군데 모두 사용할 수 있는 암호화 방식에 대해 협상이 필요하다.</li>
</ul>
<p>Sever Hello 단계</p>
<ul>
<li>클라이언트와 동일하게 랜덤 데이터를 생상하며, 클라이언트에게 전달한다.</li>
<li>클라이언트가 전달한 암호화 방식 중에서 서버 쪽에서도 사용할 수 있는 암호화 방식을 선택해서 클라이언트로 전달한다.
채택된 암호화 방식으로 클라이언트 - 서버간 통신이 진행된다.</li>
<li>서버가 발급 받은 인증서를 클라이언트로 전달한다.</li>
</ul>
<p>Client 인증 단계</p>
<ul>
<li><p>클라이언트는 서버의 인증서가 CA에 의해서 발급된것인지를 확인하기 위해
클라이언트에 내장된 CA리스트를 확인힌다.</p>
</li>
<li><p>CA리스트에 인증서가 없다면 사용자에게 경고 메세지를 출력한다.</p>
</li>
<li><p>인증서가 CA에의해서 발급된 것인지를 확인하기 위해 클라이언트에 내장된 CA의 공개키를 이용해서 인증서를 복호화한다.</p>
</li>
<li><p>복호화에 성공했다면, 인증서는 CA의개인키로 암호화된 문서임이 암시적으로 보증된 것이다.</p>
</li>
<li><p>클라이언트는 server hello단계에서 받은 랜덤한 데이터와 client hello단계에서 생성한 랜덤한 데이터를 조합하여 pre master secret키를 생성한다.</p>
</li>
<li><p>이 키는 데이터를 주고 받을때 사용하기 위한 암호화 키이며, 대칭키 방식으로 사용되어질 키 값이다.이 키는 절대 제 3자에게 노출되어서는 안된다.</p>
</li>
<li><p>pre master secret키 값을 서버에게 전달할 때 서버가 제공한 공개키로 암호화하여 전송하며, 서버는 자신의 비공개키로 복호화하여 값을 취득한다.
클라이언트가 사용한 공개키는 Server Hello 단계에서 전달받은 인증서 안에 들어있다. </p>
</li>
</ul>
<p>Sever 인증 단계</p>
<ul>
<li>서버는 클라이언트가 전송한 pre master secret값을 자신의 비공개키로 복호화 한다. 이제 서버와 클라이언트 모두 pre master secret값을 공유한다.</li>
<li>서버와 클라이언트는 모두 일련의 과정을 거쳐서 pre master secret값을 master secret값으로 만든다.</li>
<li>master secret은 session key를 생성하는데, 이 session key값을 이용해서 서버와 클라이언트는 데이터를 대칭키 방식으로 암호화 한 후에 주고 받는다.</li>
<li>이렇게 하여 세션키를 클라이언트와 서버가 공유하게 된다.</li>
</ul>
<h3 id="데이터-전송">데이터 전송</h3>
<p>실제로 서버와 클라이언트가 데이터를 주고 받는 과정이다.
상대방에게 데이터를 송수신하기 전,악수 단계에서 발생한 새션키 값으로 데이터를 대칭키방식으로 암호화 한다.</p>
<p>암호화된 데이터는 상대방으로 송신되며, 상대방도 세션키를 알고 있기 때문에 데이터를 복호화할 수 있다.</p>
<p>SSL통신은 공개키 방식, 대칭키 방식의 하이브리드 조합으로 통신한다.
공개키 방식은 컴퓨터 자원을 많이 사용함으로 비용이 많이 들며, 또한 대칭키 방식만 사용하게 되면 인터넷 상 대칭키를 서로 주고 받아야 하는 상황에서 보안생 매우 위험하다.</p>
<p>그래서 속도는 느리지만, 데이터를 안전하게 주고 받을 수 있는 공개키 방식으로 대칭키를 암호화 하고, 실제 데이터를 주고 받을때는 대칭키를 이용해서 데이터를 주고 받는 것이다.</p>
<p>참조: <a href="https://dolphinsarah.tistory.com/53">https://dolphinsarah.tistory.com/53</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TCP 와 UDP의 특징과 차이]]></title>
            <link>https://velog.io/@dae_eun2/TCP-%EC%99%80-UDP%EC%9D%98-%ED%8A%B9%EC%A7%95%EA%B3%BC-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@dae_eun2/TCP-%EC%99%80-UDP%EC%9D%98-%ED%8A%B9%EC%A7%95%EA%B3%BC-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Wed, 12 Oct 2022 09:52:11 GMT</pubDate>
            <description><![CDATA[<h2 id="전송계층">전송계층</h2>
<p>TCP와 UDP는 TCP/IP의 전송계층에서 사용되는 프로토콜이다. 
전송계층은 IP에 의해 전달되는 패킷의 오류를 검사하고 재전송 요구 등의 제어를 담당하는 계층이다.</p>
<h2 id="tcp-vs-udp">TCP vs UDP</h2>
<p>TCP는 Transmission Control Protocol의 약자이고, UDP는 User Datagram Protocol의 약자이다.
두 프로토콜은 모두 패킷을 한 컴퓨터에서 다른 컴퓨터로 전달해주는 <code>IP프로토콜</code>을 기반으로 구현되어 있지만, 서로 다른 특징을 가지고 있다.</p>
<h3 id="그림으로-비교하는-tcp-vs-udp">그림으로 비교하는 TCP vs UDP</h3>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/3e5de00f-3ba3-4c66-9780-9df502a7b798/image.png" alt=""></p>
<p>TCP는 클라이언트와 서버가 서로 연결되어 송수신 하는 모습을 볼 수 있다.
반면 UDP의 경우는 일방적인 모습을 볼 수 있다.</p>
<p>즉, 신뢰성이 요구되는 애플리케이션에서는 TCP를 사용하고 , 간단한 데이터를 빠른 속도로 전송 하고자 하는
애플리케이션에는 UDP를 사용한다.</p>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/48be8c86-0cb0-420f-ad42-2d87e2685616/image.png" alt=""></p>
<h3 id="표로-비교하는-tcp-vs-udp">표로 비교하는 TCP vs UDP</h3>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/46621d95-c58e-4165-a756-637a02f7eb67/image.png" alt=""></p>
<h1 id="tcp-transmission-control-protocol">TCP (Transmission Control Protocol)</h1>
<p>TCP는 네트워크 계층 충 전송계층에서 사용하는 프로토콜로서, 장치들 사이에 논리적인 접속을 성립하기 위하여
연결을 설정하여 <strong>신뢰성을 보장하는 연결형 서비스</strong>이다.
TCP는 네트워크에 연결된 컴퓨터에서 실행되는 프로그램 간에 일련의 옥텟(데이터, 메세지, 세그먼트 라는 블록단위)를 <strong>안정적으로, 순서대로, 에러없이  교환</strong> 할 수 있게한다.</p>
<h2 id="tcp의-특징">TCP의 특징</h2>
<h3 id="연결형-서비스">연결형 서비스</h3>
<p>연결형 서비스로 가상 회선 방식을 제공한다.</p>
<ul>
<li>3-way handshaking과정을 통해 연결을 설정</li>
<li>4-way handshaking 을 통해 연결을 해제.</li>
</ul>
<h3 id="흐름제어-flow-control">흐름제어 (Flow control)</h3>
<p>데이터 처리 속도를 조절하여 수신자의 버퍼 오버 플로우를 방지한다.</p>
<ul>
<li>송신하는 곳에서 감당이 안되게 많은 데이터를 빠르게 보내 수신하는 곳에서 문제가 일어나는 것을 방지한다.</li>
<li>수신자가 window size값을 통해 수신량을 정할 수 있다.</li>
</ul>
<h3 id="혼합제어-congestion-control">혼합제어 (Congestion control)</h3>
<p>네트워크 내의 패킷 수가 넘치게 증가하지 않도록 방지한다.</p>
<ul>
<li>정보의 소통량이 과다하면 패킷을 조금만 전송하여 혼잡 붕괴 현상이 일어나는 것을 방지한다.<h3 id="신뢰성이-높은-전송reliable-transmission">신뢰성이 높은 전송(Reliable Transmission)</h3>
</li>
<li>Dupack-based retransmission<ul>
<li>정상적인 상황에서는 ACK값이 연속적으로 전송되어야 한다.</li>
<li>그러나 ACK값이 중복으로 올 경우 패킷 이상을 감지하고 재전송을 요청한다.</li>
</ul>
</li>
<li>Timeout-based retransmission<ul>
<li>일정시간 동안 ACK값이 수신을 못할 경우 재전송을 요청한다.</li>
</ul>
</li>
</ul>
<h3 id="전이중-점대점-방식">전이중, 점대점 방식</h3>
<ul>
<li>전이중 (Full-Duplex)
전송이 양방향으로 동시에 일어날 수 있다.<ul>
<li>점대점 (Point to Point)
각 연결이 정확히 2개의 종단점을 가지고 있다.</li>
</ul>
</li>
</ul>
<p>=&gt; 멀티캐스팅이나 브로드캐스팅을 지원하지 않는다.</p>
<h2 id="tcp-header-정보">TCP Header 정보</h2>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/b89ff15c-79c6-405c-be01-f772b646fccc/image.png" alt=""></p>
<h2 id="tcp의-연결-및-해제-과정">TCP의 연결 및 해제 과정</h2>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/b2abeb61-3c19-483a-bb2e-c7df1bfe66cf/image.png" alt=""></p>
<h3 id="tcp-connection--3-way-handshake">TCP Connection ( 3-way handshake)</h3>
<ol>
<li>먼저 open()을 실행한 클라이언트가 <code>syn</code>을 보내고 <code>syn-sent</code>상태로 대기한다.</li>
<li>서버는 <code>syn-RCVD</code>상태로 바꾸고 <code>syn</code>과<code>ack</code>를 보낸다.</li>
<li><code>syn</code>과 응답<code>ack</code>를 받은 클라이언트는 <code>established</code>상태로 변경하고 서버에게 응답 <code>ack</code>를 보낸다.</li>
<li>응답 <code>ack</code>를 받은 서버는 <code>Established</code>상태로 변경한다.</li>
</ol>
<h3 id="tcp-disconnection-4-way-hanshake">TCP Disconnection (4-way hanshake)</h3>
<ol>
<li>먼저 close()를 실행한 클라이언트가 <code>FIN</code>을 보내고 <code>FIN_WAIT1</code> 상태로 대기한다.</li>
<li>서버는 <code>CLOSE_WAIT</code>으로 바꾸고 응답 <code>ACK</code>를 전달한다. 동시에 해당 포트에 연결되어 있는 어플리케이션에게 close()를 요청한다.</li>
<li>ACK를 받은 클라이언트는 상태를 <code>FIN_WAIT2</code>로 변경한다.</li>
<li>close() 요청을 받은 서버 어플리케이션은 종료 프로세스를 진행하고 <code>FIN</code>을 클라이언트에 보내 <code>LAST_ACK</code> 상태로 바꾼다.</li>
<li><code>FIN</code>을 받은 클라이언트는 <code>ACK</code>를 서버에 다시 전송하고 <code>TIME_WAIT</code>으로 상태를 바꾼다. TIME_WAIT에서 일정 시간이 지나면 CLOSED된다. ACK를 받은 서버도 포트를 CLOSED로 닫는다.</li>
</ol>
<blockquote>
<p>주의 </p>
</blockquote>
<ul>
<li>반드시 서버만 CLOSE_WAIT 상태를 갖는 것은 아니다.</li>
<li>서버가 먼저 종료하겠다고 FIN을 보낼 수 있고, 이런 경우 서버가 FIN_WAIT1 상태가 됩니다.</li>
<li>누가 먼저 close를 요청하느냐에 따라 상태가 달라질 수 있다.</li>
</ul>
<h2 id="정리">정리</h2>
<h3 id="공통점">공통점</h3>
<ul>
<li>포트 번호를 이용하여 주소를 지정한다.</li>
<li>데이터 오류 검사를 위한 checksum이 존재한다.</li>
</ul>
<h3 id="차이점">차이점</h3>
<table>
<thead>
<tr>
<th>TCP</th>
<th>UDP</th>
</tr>
</thead>
<tbody><tr>
<td>연결이 성공해야 통신가능(연결형 프로토콜)</td>
<td>연결 없이 통신이가능(비연결형 프로토콜)</td>
</tr>
<tr>
<td>데이터의 경계를 구분하지않음(Byte-Stream Service)</td>
<td>데이터의 경계를 구분함(Datagram Service)</td>
</tr>
<tr>
<td>신뢰성 있는 데이터 전송(데이터의 재전송 존재)</td>
<td>비신뢰성있는 데이터 전송(데이터의 재전송없음)</td>
</tr>
<tr>
<td>1 : 1(Unicast)통신</td>
<td>1:1, 1:N,N:N (Multicast)통신</td>
</tr>
</tbody></table>
<p>참조 :
<a href="https://velog.io/@hidaehyunlee/TCP-%EC%99%80-UDP-%EC%9D%98-%EC%B0%A8%EC%9D%B4">https://velog.io/@hidaehyunlee/TCP-%EC%99%80-UDP-%EC%9D%98-%EC%B0%A8%EC%9D%B4</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[OSI 7계층 모델과 TCP/IP 모델]]></title>
            <link>https://velog.io/@dae_eun2/OSI-7%EA%B3%84%EC%B8%B5-%EB%AA%A8%EB%8D%B8%EA%B3%BC-TCPIP-%EB%AA%A8%EB%8D%B8</link>
            <guid>https://velog.io/@dae_eun2/OSI-7%EA%B3%84%EC%B8%B5-%EB%AA%A8%EB%8D%B8%EA%B3%BC-TCPIP-%EB%AA%A8%EB%8D%B8</guid>
            <pubDate>Wed, 12 Oct 2022 09:07:18 GMT</pubDate>
            <description><![CDATA[<h2 id="계층구조">계층구조</h2>
<p>네트워크 상에서 여러 대의 컴퓨터가 데이터를 주고 받으려면, 이들을 서로 연동할 수 있도록
표준화 된 인터페이스를 지원해야한다.
OSI7 모델과 TCP/IP 모델 모두 <code>계층 구조</code>를 갖고 있기 때문에, 자세히 알아 보기 전에
먼저 계층 구조가 어떤 것인지, 적용하면 어던 점이 좋은지를 알 필요가 있다.
계층 구조(Layered)는 네트워크 뿐만 아니라 운영체제 등 다양한 분야에서 적용되는데, 계층 구조를 사용하는 목적은 <code>분할 정복(Divide and Conquer)</code> 때문이다. 어떠한 복잡한 문제를 해결하고자 할 때, 나누어 생각하면 쉽게 해결 할 수 있다는 취지인 것이다.</p>
<p>계층 구조의 또다른 특징은 위, 아래 층으로만 이동할 수 있다는 점이다.건너뛰어 한번에 맨위 또는 아래로 갈 수 없다. 즉, 다음 단계로 넘어가려면 이전 계층이 <code>전제조건</code>이 되어야 한다.</p>
<h2 id="osi-7계층-모델">OSI 7계층 모델</h2>
<p>OSI 7 모델은 네트워크 통신 과정을 7개의 계층으로 구분한 산업 표준 참조 모델이다.
초창기의 네트워크는 각 컴퓨터 마다 시스템이 달랐기 때문에 하드웨어와 소프트웨어의 논리적인 변경없이 통신할 수 있는 표준 모델이 나타나게 되었다.
OSI참 조모델은  아래의 그림과 같이 7개의 층으로 이루어져 있다.</p>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/7740505f-9987-4d11-9668-fd6f14ef6bc5/image.png" alt=""></p>
<h3 id="pdu-란">PDU 란?</h3>
<p>OSI 7계층에서는 PDU개념을 중요시 하는데, PDU(Process Data Unit)란 각 계층에서 전송되는 단위를 말한다.
1계층에서 PDU가 비트(Bit)라고 생각하기 쉽지만 PDU라고 하지 않고 여기서는 비트는 단위라기 보다는 단지 전기 신호의 흐름일 뿐이다.</p>
<p>PDU는 2계층-프레임(Frame), 3계층-패킷(Packet), 4계층-세그먼트(Segment) 만 생각하면 된다. 네트워크 통신과정을 깊게 이해하기 위해서는 왜 각각의 계층의 PDU가 다른지 알아야 하고, 역할에 대해 알고 있어야 한다.</p>
<h3 id="1계층--물리계층-physical-layer">1계층 : 물리계층 (Physical Layer)</h3>
<p>물리계층은 OSI 모델의 최하위 계층에 속하며, 상위 계층에서 전송된 데이터를 물리매체 (허브,라우터,케이블 등)를 통해 다른 시스템에 전기적 신호를 전송하는 역할을 한다.</p>
<p>즉, 기계어를 전기적 신호로 바꿔서 와이어에 실어주는 것이다.</p>
<ul>
<li>PDU : 비트(Bit)</li>
<li>프로토콜 : Modem,Cable,Fiber,RS-232C</li>
<li>장비 :  허브, 리피터</li>
</ul>
<h3 id="2계층--링크계층link-layer">2계층 : 링크계층(Link Layer)</h3>
<p>링크계층은 네트워크 기기들 사이의 데이터 전송을 하는 역할을 한다. 시스템 간의 오류 없는 데이터 전송을 위해 패킷을 <code>프레임</code>으로 구성하여 물리계층으로 전송한다. 3계층에서 정보를 받아 주소와 제어정보를 헤더와 테일에 추가한다.</p>
<ul>
<li>PDU : 프레임(Frame)</li>
<li>프로토콜 : 이더넷,MAC,PPP,ATM,LAN,Wifi</li>
<li>장비 : 브릿지, 스위치</li>
</ul>
<h3 id="3계층--네트워크계층network-layer">3계층 : 네트워크계층(Network Layer)</h3>
<p>네트워크계층은 기기에서 데이터그램(Datagram)이 가는 경로를 설정해주는 역할을 한다.
라우팅 알고리즘을 사용하여 최적의 경로를 선택하고 송신측으로 부터 수신측으로 전송한다.
이때, 전송되는 데이터는 <code>패킷</code>단위로 분할하여 전송한 후 다시 합쳐진다. 
2계층이 노드 대 노드 전달을 감독한다면, 3계층은 각 패킷이 목적지 까지 성공적이고 효과적으로 전달되도록 한다.</p>
<ul>
<li>PDU : 패킷(Packet)</li>
<li>프로토콜 : IP, ICMP 등</li>
<li>장비 : 라우터, L3 스위치</li>
</ul>
<h3 id="4계층--전송계층transport-layer">4계층 : 전송계층(Transport Layer)</h3>
<p>발신지에서 목적지(End-to-End)간 제어와 에러를 관리한다.
패킷의 전송이 유효한지 확인하고 전송에 실패된 패킷을 다시 보내는것과 같은 신뢰성 있는 통신을 보장하며, 헤드에는<code>세그먼트</code>가 포함된다.
주소 설정, 오류 및 흐름제어, 다중화를 수행한다.</p>
<ul>
<li>PDU : 세그먼트(Segment)</li>
<li>프로토콜 : TCP, UDP , ARP, RTP</li>
<li>장비 : 게이트웨이, L4 스위치</li>
</ul>
<h3 id="5계층--세션계층session-layer">5계층 : 세션계층(Session Layer)</h3>
<p>통신 세션을 구성하는 계층으로,<code>포트(Port)</code>번호를 기반으로 연결한다.
통신 장치 간의 상호작용을 설정하고, 유지하며 동기화 한다.
동시송수신(Duplex), 반이중(Half-Duplex), 전이중(Full-Duplex) 방식의 통신과 함께 체크 포인팅과 유후, 종료, 다시 시작 과정 등을 수행한다.</p>
<ul>
<li>프로토콜 : NetBIOS, SSH, TLS</li>
</ul>
<h3 id="6계층--표현계층presentation-layer">6계층 : 표현계층(Presentation Layer)</h3>
<p>표현계층은 송신측과 수신측 사이에서 데이터의 형식을 정해준다. 
받은 데이터를 코드 변환, 구문 검색, 암호화, 압축 의 과정을 통해 올바른 표준방식으로 변환해준다.</p>
<ul>
<li>프로토콜 : JPG, MPEG, SMB, AFP</li>
</ul>
<h3 id="7계층--응용계층application-layer">7계층 : 응용계층(Application Layer)</h3>
<p>응용계층은 사용자와 바로 연결되어 있으며 응용SW를 도와주는 계층이다.
사용자로부터 정보를 입력받아 하위 계층으로 전달하고 하위 계층에서 전송한 데이터를 사용자에게 전달한다.</p>
<p>파일 전송, DB, 메일 전송 등 여러가지 응용 서비스를 네트워크에 연결해 주는 역할을 한다.</p>
<ul>
<li>프로토콜 : DHCP, DNS, FTP, HTTP</li>
</ul>
<h2 id="tcpip모델">TCP/IP모델</h2>
<p>그렇지만 OSI참조 모델은 말 그대로 참조 모델일 뿐 실제 사용 되는 인터넷 프로토콜은 7계층을 완전히 따르지는 않는다.
인터넷 프로토콜 스택(Internet Protocol Stack)은 대부분 TCP/IP를 따른다.</p>
<p>TCP/IP 는 인터넷 프로토콜 중 가장 중요한 역할을 하는 <code>TCP</code>와<code>IP</code>의 합성어로 데이터의 흐름관리, 정확성 확인, 패킷의 목적지 보장을 담당한다.
데이터의 정확성 확인은 TCP가, 패킷을 목적지까지 전송하는 일은 IP가 담당한다.</p>
<h3 id="tcpip의-4계층">TCP/IP의 4계층</h3>
<p>TCP/IP는 OSI 참조 모델과 달리 표현계층, 세션계층을 응용계층에 다 포함시키고 있지만, 사실상 TCP/IP Model의 Application 계층 하나에서 Application, Presentatiom, Session 계층의 구현을 다 하고 있다고 이해하는 게 올바르다.</p>
<p><img src="https://velog.velcdn.com/images/dae_eun2/post/f4e19378-8ddc-4d03-9476-060c6b2ba55e/image.png" alt=""></p>
<p>데이터는 아래 그림과 같이 단계별로 헤더(Data -&gt; Segment -&gt; Datagram -&gt; Frame)을 붙여 전송하며
이를 &#39;데이터 캡슐화&#39; 라고 한다
<img src="https://velog.velcdn.com/images/dae_eun2/post/8521db37-fe9d-4498-9456-3b998b46824d/image.png" alt=""></p>
<p>참고 : 
<a href="https://velog.io/@hidaehyunlee/%EB%8D%B0%EC%9D%B4%ED%84%B0%EA%B0%80-%EC%A0%84%EB%8B%AC%EB%90%98%EB%8A%94-%EC%9B%90%EB%A6%AC-OSI-7%EA%B3%84%EC%B8%B5-%EB%AA%A8%EB%8D%B8%EA%B3%BC-TCPIP-%EB%AA%A8%EB%8D%B8">https://velog.io/@hidaehyunlee/%EB%8D%B0%EC%9D%B4%ED%84%B0%EA%B0%80-%EC%A0%84%EB%8B%AC%EB%90%98%EB%8A%94-%EC%9B%90%EB%A6%AC-OSI-7%EA%B3%84%EC%B8%B5-%EB%AA%A8%EB%8D%B8%EA%B3%BC-TCPIP-%EB%AA%A8%EB%8D%B8</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[쿠키 와 세션의 차이  + 캐시]]></title>
            <link>https://velog.io/@dae_eun2/%EC%BF%A0%ED%82%A4-%EC%99%80-%EC%84%B8%EC%85%98%EC%9D%98-%EC%B0%A8%EC%9D%B4-%EC%BA%90%EC%8B%9C</link>
            <guid>https://velog.io/@dae_eun2/%EC%BF%A0%ED%82%A4-%EC%99%80-%EC%84%B8%EC%85%98%EC%9D%98-%EC%B0%A8%EC%9D%B4-%EC%BA%90%EC%8B%9C</guid>
            <pubDate>Wed, 12 Oct 2022 08:26:54 GMT</pubDate>
            <description><![CDATA[<h2 id="쿠키와-세션을-사용하는-이유-">쿠키와 세션을 사용하는 이유 ?</h2>
<p>HTTP 프로토콜의 특징이자 약점을 보완하기 위해서 사용한다.</p>
<h3 id="http-프로토콜의-특징">HTTP 프로토콜의 특징</h3>
<ol>
<li>Connectionless Protocol (비연결 지향)
클라이언트가 서버에 요청(Request)했을 때, 그 요청에 맞는 응답(Response)를 보낸 후 연결을 끊는 처리방식이다.</li>
</ol>
<ul>
<li>HTTP 1.1버전에서 커넥션을 계속 유지하고, 요청(Request)에 재활용하는 기능이 추가되었다.
HTTP Header에 Keep-alive옵션을 주어 커넥션을 재활용하게 한다. , 1.1버전에서는 디폴트 옵션이다.</li>
<li>HTTP 가 TCP(연결지향)위에서 구현되었기 때문에 연결 지향적이라고 할 수 있다는 이야기가 있어
논란이 있지만, 아직까진 네트워크 관점에서 keep-alive는 옵션으로 두고, 서버측에서 비연결 지향적인 특성으로 커넥션 관리에 대한 비용을 줄이는것이 명확한 장점으로 보이기 때문에 비연결지향으로 알아 두었다.</li>
<li>HTTP는 불특정 다수와 통신하기 위하여 사용되기 때문에, 계속 연결을 유지한다며 많은 리소스가 낭비되기 때문이다.</li>
</ul>
<ol start="2">
<li>Stateless Protocol
커넥션을 끊는 순간 클리언트와 서버의 통신이 끝나며 상태 정보는 유지하지 않는 특성이 있다.<ul>
<li>클라이언트와 첫 번째 통신에서 데이터를 주고 받았다 하여도,두번째 통신에서 이전 데이터를 유지하지 않는다.</li>
</ul>
</li>
</ol>
<p>정보가 유지 되지 않는다면, 매번 페이지를 이동할 때마다 로그인을 다시하거나, 상품을 선택했을때,
구매 페이지에서 선택한 상품의 정보가 없거나 하는등의 문제가 발생할 수 있다.
따라서, Stateless의 단점을 보완하기 위해 쿠키와 세션을 사용한다.</p>
<h2 id="쿠키-란">쿠키 란?</h2>
<p>HTTP의 일종으로 사용자가 어떠한 웹 사이트를 방문할 경우,
그 사이트가 사용하고 있는 서버에서 사용자의 컴퓨터에 저장하는 작은 기록 정보 파일이다.</p>
<h3 id="쿠키의-특징">쿠키의 특징</h3>
<ul>
<li>쿠키는 클라이언트(브라우저) 로컬에 저장되는 키와 밸류가 들어있는 작은 데이터 파일 이다.</li>
<li>사용자 인증이 유효한 시간을 명시할 수 있으며, 유효 시간이 정해지면 브라우저가 종료되어도 인증이 유지된다는 특징이 있습니다.</li>
<li>쿠키는 클라이언트의 상태 정보를 로컬에 저장했다가 참조합니다.</li>
<li>클라이언트에 300개까지 저장이 가능하며, 각 도메인당 20개의 값만 가질수 있습니다.</li>
<li>하나의 쿠키는 최대 4KB까지 저장이 가능합니다.</li>
<li>Response Header에 Set-Cookie 속성을 사용하면 클라이언트에 쿠키를 만들 수 있습니다.</li>
<li>쿠키는 사용자가 따로 요청하지 않아도 브라우저가 Request시 헤더에 자동으로 넣어 서버로 전송합니다.</li>
</ul>
<h3 id="쿠키의-구성요소">쿠키의 구성요소</h3>
<ul>
<li>이름: 각 쿠키를 구별하는데 사용되는 이름</li>
<li>값 : 쿠키의 이름과 관련된 값</li>
<li>유효시간 : 쿠키의 유지시간</li>
<li>도메인 : 쿠키를 전송할 도메인</li>
<li>경로 :  쿠키를 전송할 요청 경로</li>
</ul>
<h3 id="쿠키의-동작-방식">쿠키의 동작 방식</h3>
<ol>
<li>클라이언트가 페이지를 요청합니다.</li>
<li>서버에서 쿠키를 생성하여 HTTP헤더에 쿠키를 포함시켜 응답합니다.</li>
<li>브라우저가 종료되어도 쿠키 만료 기간이 있다면 클라이언트에서 보관하고 있습니다.</li>
<li>같은 요청을 할 경우, HTTP 헤더에 쿠키를 함께 보냅니다.</li>
<li>서버에서 쿠키를 읽어 이전 상태 정보를 변경 할 필요가 있을 때, 쿠키를 업데이트 하여 변경된 쿠키를
HTTP 헤더에 포함시켜 응답합니다.</li>
</ol>
<h3 id="쿠키의-사용-예">쿠키의 사용 예</h3>
<ul>
<li>사이트에 로그인 시, &#39;아이디와 비밀번호를 저장하시겠습니까?&#39;</li>
<li>쇼핑몰의 장바구니 기능</li>
<li>자동 로그인, 팝업 창 더보지 않기 버튼</li>
</ul>
<h2 id="세션이란-">세션이란 ?</h2>
<p>일정 시간 동안 같은 사용자(브라우저)로 부터 들어오는 일련의 요구를 하나의 상태로 보고,
그 상태를 유지시키는 기술이다.
여기서 일정 시간은 방문자가 웹 브라우저를 통해 웹 서버에 접속한 시점부터 웹 브라우저를 종료하여 연결을 끝내는 시점을 말한다.</p>
<h3 id="세션의-특징">세션의 특징</h3>
<ul>
<li>세션은 쿠키를 기반으로 하고 있지만, 사용자 정보 파일을 브라우저에 저장하는 쿠키와 다르게 세션은 서버측에서 관리를 한다.</li>
<li>서버에서는 클라이언트를 구분하기 위해 세션ID를 부여하며, 웹 브라우저가 서버에 접속해서 브라우저를 종료할 때 까지 인증상태를 유지한다.</li>
<li>접속 시간에 제한을 두어 일정 시간 응답이 없다면 정보가 유지되지 않게 설정도 가능하다.</li>
<li>사용자에 대한 정보를 서버에 두기 때문에 쿠키보다 보안이 좋지만, 사용자가 많아질 경우 서버 메모리를 많이 소모하게 된다.</li>
<li>클라이언트가 Request를 보내면, 해당 서버의 엔진이 클라이언트에게 유일한 ID를 부여하는데 이것이 세션ID이다.</li>
</ul>
<h3 id="세션의-동작-방식">세션의 동작 방식</h3>
<ol>
<li>클라이언트가 서버에 접속 시 세션ID를 발급 받음</li>
<li>클라이언트는 세션 ID에 대해 쿠키를 사용해서 저장하고 가지고 있음</li>
<li>클라이언트는 서버에 요청할 때, 이 쿠키의 세션 ID를 같이 서버에 전달해서 요청</li>
<li>서버는 세션ID를 전달 받아서 별 다른 작업 없이 세션ID로 세션에 있는 클라이언트이 정보를 가져와 사용</li>
<li>클라이언트 정보를 가지고 서버 요청을 처리하여 클라이언트에게 응답</li>
</ol>
<h3 id="세션의-사용-예">세션의 사용 예</h3>
<ul>
<li>화면을 이동해도 로그인이 풀리지 않고 로그아웃 하기 전까지 유지됨</li>
</ul>
<h2 id="쿠키와-세션의-차이">쿠키와 세션의 차이</h2>
<ul>
<li>쿠키와 세션은 비슷한 역할을 하며, 동작 원리 또한 비슷하다. 그 이유는 세션도 결국 쿠키를 사용하기 때문이다.</li>
<li>큰 차이점은 사용자의 정보가 저장되는 위치이다. 
쿠키는 서버의 자원을 전혀 사용하지 않으며, 세션은 서버의 자원을 사용한다.</li>
<li>보안 면에서 세션이 더 우수하며,</li>
<li>쿠키는 클라이언트 로컬에 저장되기 때문에 변질 되거나 request에서 스니핑 당할 우려가 있어 보안에 취약하다.</li>
<li>세션은 쿠키를 이용하여 세션ID만 저장하고 그것으로 구분하여 서버에서 처리하기 때문에 비교적 보안성이 높다.</li>
<li>라이프 사이클은 쿠키도 만료기간이 있지만, 파일로 저장되기 때문에 브라우저를 종료해도 정보가 유지 될 수 있다. 또한 만료기간을 따로 지정해 쿠키를 삭제할 때까지 유지할 수도 있다.</li>
<li>쿠키는 쿠키에 정보가 있기 때문에 서버에 요청시 속도가 빠르고</li>
<li>세션은 정보가 서버에 있기 때문에 비교적 느린 속도를 낸다.</li>
</ul>
<h2 id="세션을-사용하면-좋은데-왜-쿠키를-사용할까">세션을 사용하면 좋은데 왜 쿠키를 사용할까?</h2>
<p>세션이 쿠키에 비해 보안이 높은 편이나, 쿠키를 사용하는 이유는 세션은 서버에 저장되고, 서버의 자원을
사용하기에 서버자원에 한계가 있고, 속도가 느려질 수 있기 때문에 자원관리 차원에서 쿠키와 세션을 적절한 요소 및 기능에 병행 사용하여 서버 자원의 낭비를 방지하는것이 좋다.</p>
<h2 id="쿠키와-세션-그리고-캐시">쿠키와 세션 그리고 캐시?</h2>
<p>캐시는 웹 페이지 요소를 저장하기 위한 임시 저장소 이며, 쿠키/세션은 정보를 저장하기 위해 사용된다.
캐시는 웹 페이지를 빠르게 렌더링 할 수 있도록 도와주고, 쿠키/세션은 자용자의 인증을 도와준다.</p>
<ul>
<li>캐시는 이미지,비디오,css,js파일 등 데이터나 값을 미리 복사해 놓는 리로스 파일들의 임시 저장소 이다.</li>
<li>저장 공간이 작고 비용이 비싼 대신 빠른 성능을 제공한다.</li>
<li>같은 웹 페이지에 접속할 때 사용자의 PC에서 로드하므로 서버를 거치지 않아도 된다.</li>
<li>이전에 사용된 데이터가 다시 사용될 가느엇ㅇ이 많으면 캐시 서버에 있는 데이터를 사용한다.</li>
<li>그래서 다시 사용될 확률이 있는 데이터들이 빠르게 접근할 수 있어진다. (페이지의 로딩속도 증가)</li>
<li>캐시 히트(hit): 캐시를 사용할수 있는 경우 (ex. 이전에 왔던 요청이랑 같은 요청이 왔을 경우)</li>
<li>캐시 미스(miss) : 캐시를 사용할 수 없는 경우 (ex. 웹 서버로 처음 요청 했을 경우)</li>
</ul>
<p>참고 :
<a href="https://dev-coco.tistory.com/61">https://dev-coco.tistory.com/61</a>
<a href="https://interconnection.tistory.com/74">https://interconnection.tistory.com/74</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React useDebounce]]></title>
            <link>https://velog.io/@dae_eun2/React-useDebounce</link>
            <guid>https://velog.io/@dae_eun2/React-useDebounce</guid>
            <pubDate>Mon, 03 Oct 2022 04:53:22 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>디바운싱 훅의 사용법을 기억하기 위하여 기록합니다.</p>
</blockquote>
<pre><code>import React, { useState, useEffect } from &quot;react&quot;;

const useQueryDebounce = (value, delay) =&gt; {
  const [debounceValue, setDevounceValue] = useState(&quot;&quot;);

  useEffect(() =&gt; {
    const handler = setTimeout(() =&gt; {
      setDevounceValue(value);
    }, delay);

    return () =&gt; {
      clearTimeout(handler);
    };
  }, [value, delay]);
  return debounceValue;
};

export default useQueryDebounce;
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[React useInterval]]></title>
            <link>https://velog.io/@dae_eun2/React-useInterval</link>
            <guid>https://velog.io/@dae_eun2/React-useInterval</guid>
            <pubDate>Sun, 21 Aug 2022 15:03:48 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://velog.io/@jakeseo_me/%EB%B2%88%EC%97%AD-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85%EC%8A%A4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%97%90%EC%84%9C-setInterval-%EC%82%AC%EC%9A%A9-%EC%8B%9C%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90">리액트 훅스 컴포넌트에서 setInterval 사용 시의 문제점</a>
<a href="https://velog.io/@yeyo0x0/React-React-Hooks%EC%97%90%EC%84%9C-setInterval-%EC%82%AC%EC%9A%A9-%EB%AC%B8%EC%A0%9C"> React Hooks에서 setInterval() 사용 문제</a></p>
</blockquote>
<p>본 게시글은 위 링크를 바탕으로 작성되었습니다.
useInterval의 사용법을 익히기 위하여 기록합니다.</p>
<h1 id="useinterval-hooks">useInterval hooks</h1>
<pre><code>function useInterval(callback, delay) {
    const savedCallback = useRef();
    useEffect(() =&gt; {
        savedCallback.current = callback;
    }, [callback]);

    useEffect(() =&gt; {
        function tick() {
            savedCallback.current();
        }
        if (delay !== null) {
            let id = setInterval(tick, delay);
            return () =&gt; clearInterval(id);
        }
    }, [delay]);
}</code></pre><h2 id="useref의-활용">useRef의 활용</h2>
<pre><code>const savedCallback = useRef();</code></pre><p><code>useInterval</code>훅을 보면 callback 함수와 delay를 전달받아서
useEffect 내부에서 callback 함수를 savedCallback.current에 저장한다.</p>
<p>이때, <code>useRef</code>훅을 사용한 이유는 리랜더링을 방지하기 위함이다.
<code>useRef</code> 는 함수형 프로그래밍에서 사용하는 ref로 초기화된 ref 객체인 <code>{current:null}</code>을 반환하며 , 반환된 객체는 컴포넌트의 전 생애주기 동안 유지되어 <code>useRef</code>로 관리되는 값은 변경되더라도 컴포터는가 리 랜더링 되지 않는다.</p>
<h3 id="만약-usestate를-사용했더라면">만약 useState를 사용했더라면?</h3>
<pre><code>const [count, setCount] = useState(0);

useInterval(()=&gt;{
  setCount(count =&gt; count+1);
}, 1000);</code></pre><p><code>useState</code>를 사용하여 데이터를 관리할 경우 값이 변경될 때 리렌더링이 일어나기 때문에 <code>useEffect()</code> 내부에서 <code>savedCallback</code> 값이 변경될 때마다 리렌더링이 일어나게 되고,
이 때문에 두 번째 <code>useEffect()</code> 내부에서 <code>savedCallback</code> 값을 확인하면 계속해서 초기값만을 가져오게 될 것이다.</p>
<h2 id="callback-함수를-저장하는-useeffect-hooks">callback 함수를 저장하는 useEffect hooks</h2>
<pre><code>useEffect(() =&gt; {
  savedCallback.current = callback;
}, [callback]);</code></pre><p>위 코드를 보면 <code>callback</code>데이터가 변경될 때마다 <code>useEffect</code>가 실행되어 <code>savedCallback</code>의 current 값이 새로운 callback 데이터로 업데이트된다.</p>
<p>🤔 왜 useInterval()에 함수를 전달해서 내부에서 값을 업데이트 해주는 것일까?</p>
<ul>
<li><code>useEffect</code>는 의존성 배열을 전달하지 않으면 리렌더링 될때마다 실행된다.</li>
<li><code>useEffect</code>의 두번째 인자에 빈 배열을 전달할 경우 첫 렌더링 시에만 실행되어 훅 내부에서는 변화된 데이터 값을 얻을 수 없다.</li>
<li><code>useEffect</code>는 특정 데이터가 변경될때 마다 실행되도록 할 수 있으나,
<code>useEffect</code>내부에서 값이 업데이트 되는 데이터에 의존할 경우 무한 루프에 빠지게 된다.</li>
</ul>
<p>이를 해결하고자 <code>useInterval</code>훅은 리렌더링이 될 때마다 실행되어 업데이트 된 데이터를 가질수 있게 만들고, 데이터를 업데이트 하는 함수를 <code>useInterval</code>훅에 전달하여 내부에서 <code>savedCallback.current</code>에 저장하여 새로 업데이트 된 데이터 값을 <code>useEffect</code>훅 내부에서도 얻을 수 있도록 한것이다.</p>
<h2 id="setinterval을-호출하는-useeffect-hooks">setInterval을 호출하는 useEffect hooks</h2>
<p>첫 번째 <code>useEffect</code>훅을 통해 <code>callback</code> 함수를 저장했으면, 두 번째 <code>useEffect</code>에서는 <code>setInterval</code>함수에 해당 <code>callback</code> 함수를 전달해 실행되도록 만들어야한다.</p>
<pre><code>useEffect(() =&gt; {
        function tick() {
            savedCallback.current();
        }
        if (delay !== null) {
            let id = setInterval(tick, delay);
            return () =&gt; clearInterval(id);
        }
    }, [delay]);</code></pre><p>위의  <code>useEffect</code>는 delay가 변경될 때마다 실행되며 delay가 null값이 아닐 경우에 <code>setInterval</code>함수를 호출하여 callback 함수를 실행한다.
이렇게 작성하면</p>
<ul>
<li><code>useEffect</code>가 무한히 실행되는것을 방지하며 delay가 변경될 때에만 타이머를 재실행하게 된다.</li>
<li>첫 번째 <code>useEffect</code>는 콜백함수가 변경될때마다 업데이트하기 때문에 결국 두번째 <code>useEffect</code> 내의 <code>setInterval()</code>함수는 재 실행되지 않고도 새로 업데이트 된 콜백함수를 실행할 수 있다.</li>
</ul>
<p>이 때 delay를 null check 하는 이유는 Interval을 일시중지 할 수 있게 하기 위해서이며 <code>useInterval</code>에 null인 delay를 전달할 경우 더 이상 <code>setInterval</code> 함수가 실행되지 않게 된다.</p>
<p><code>return () =&gt; clearInterval(id);</code>부분은 <code>clean-up</code> 함수로 class component의 경우는 componentWillUnmount라는 라이프사이클 메서드를 이용해 구현하며 funtion component의 경우는 <code>useEffect()</code>에 전달한 함수의 return 함수로 구현한다.
<code>useEffect()</code> 내에서 함수를 반환하면 컴포넌트가 unmount 될 때 해당 함수가 실행되어 불필요한 동작을 제거하거나 메모리 누수 문제를 방지할 수 있어 사용한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[react S3 Image Upload]]></title>
            <link>https://velog.io/@dae_eun2/react-S3-Image-Upload</link>
            <guid>https://velog.io/@dae_eun2/react-S3-Image-Upload</guid>
            <pubDate>Sun, 21 Aug 2022 14:04:24 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://aldrn29.tistory.com/16?category=969086">S3 버킷 생성 참조</a></p>
</blockquote>
<h1 id="s3-란">S3 란?</h1>
<p>Simple Storage Service의 약자로 내구성과 확장성이 뛰어난 스토리지 서비스이다.</p>
<h2 id="upload-하는법">upload 하는법</h2>
<blockquote>
<p>npm i reacet-asw-s3
 import S3upload from &#39;react-aws-s3&#39;</p>
</blockquote>
<ul>
<li>upload<pre><code>const submitBtn = (e) =&gt; {
 e.preventDefault();
 let file = fileUpload.current.files[0];
 let newFileName = fileUpload.current.files[0].name;
 const config = {
   bucketName: process.env.REACT_APP_BUCKET_NAME,
   region: process.env.REACT_APP_REGION,
   accessKeyId: process.env.REACT_APP_ACCESS_ID,
   secretAccessKey: process.env.REACT_APP_ACCESS_KEY,
 };
 const ReactS3Client = new S3upload(config);
 ReactS3Client.uploadFile(file, newFileName).then(data =&gt; {
   if(data.status === 204) {
     let imgUrl = data.location
     dispatch(addRecordDB(recordDate, imgUrl))
   } else {
     window.alert(&#39;사진 업로드에 오류가 있어요! 관리자에게 문의해주세요.&#39;)
   }
 });
}

</code></pre></li>
</ul>
<p>위 함수 config에 보면 aws의 accesskey와 보안키가 들어간다.
이 부분은 aws에서 생성해야한다.
여기서 주의가 필요한 부분은 이 보안키는 절대 외부로 노출되어서는 안되는데 처음 .env파일을 만들어서 그 안에 저장하고
절대 git에는 올리지 않도록 gitignore 에 반드시 추가하자</p>
<h2 id="image-preview">Image Preview</h2>
<pre><code>const [fileUrl, setFileUrl] = useState(null);
    //인풋이 onChange 될 때
const chgPreview = (e) =&gt; {
  //현재 이미지 파일
   const imageFile = e.target.files[0];
  //선택한 이미지 파일의 url
   const imageUrl = URL.createObjectURL(imageFile);
  //Image ele의 src를 해당 이미지 url로 변경시켜준다.
   setFileUrl(imageUrl)
 }</code></pre><h2 id="image-resizing">Image Resizing</h2>
<p>이미지를 리사이징 하는 이유는 뷰에 그려주는 문제가 아니라
해당 이미지가 로드될 때의 성능을 위해서 이다.
용량이 클수록 클라이언트에 보여주기 위한 로딩 시간이 길어질테니
이를 줄여주기 위해 리사이징을 한다.</p>
<blockquote>
<p>npm i browser-image-compression
    import imageCompression from &quot;browser-image-compression&quot;; </p>
</blockquote>
<p>아래 코드는 Image Preview 와 Image Resizing 이 합쳐진 코드이다.</p>
<pre><code>//const options = { 
  //maxSizeMB: number,          // (default: Number.POSITIVE_INFINITY)
  //maxWidthOrHeight: number,   // compressedFile will scale down by ratio to a point that width or height is smaller than maxWidthOrHeight (default: undefined)
  //onProgress: Function,       // optional, a function takes one progress argument (percentage from 0 to 100) 
  //useWebWorker: boolean,      // optional, use multi-thread web worker, fallback to run in main-thread (default: true)

  // following options are for advanced users
  //maxIteration: number,       // optional, max number of iteration to compress the image (default: 10)
  //exifOrientation: number,    // optional, see https://stackoverflow.com/a/32490603/10395024
  //fileType: string,           // optional, fileType override
  //initialQuality: number      // optional, initial quality value between 0 and 1 (default: 1)
//}

const options = {
    maxSizeMB: 1,
    maxWidthOrHeight: 1920,
    useWebWorker: true
  }

  const imgUpload = useRef();
//+ input(type=file)의 onChange 함수
  const onChangeImage = async (e) =&gt; {
    const imageFile = e.target.files[0];
    const options = {
      maxSizeMB: 1,
      maxWidthOrHeight: 1920,
      useWebWorker: true,
    };
    try {
      const compressedFile = await imageCompression(imageFile, options);
      const imageUrl = URL.createObjectURL(compressedFile);
      setFileUrl(imageUrl);
    } catch (error) {
      console.error(error);
    }
  };</code></pre>]]></description>
        </item>
    </channel>
</rss>