<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>flip's velog</title>
        <link>https://velog.io/</link>
        <description>@flip_404</description>
        <lastBuildDate>Fri, 07 Feb 2025 02:53:49 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>flip's velog</title>
            <url>https://velog.velcdn.com/images/flip_404/profile/2fa6ec0c-5390-4b3e-8484-030678902bc1/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. flip's velog. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/flip_404" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[모노레포 적용하기 with 🚀Turborepo (2/2)]]></title>
            <link>https://velog.io/@flip_404/%EB%AA%A8%EB%85%B8%EB%A0%88%ED%8F%AC-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-with-Turborepo-22</link>
            <guid>https://velog.io/@flip_404/%EB%AA%A8%EB%85%B8%EB%A0%88%ED%8F%AC-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-with-Turborepo-22</guid>
            <pubDate>Fri, 07 Feb 2025 02:53:49 GMT</pubDate>
            <description><![CDATA[<h1 id="turborepo-도입이유">Turborepo 도입이유</h1>
<p>이번에 회사에서 새 프로젝트를 시작하게 되었습니다. 하나의 시스템을 위해 3개의 서비스가 필요했는데, 이는 각각 웹+앱, 앱 전용, 웹 전용으로 구성됩니다. 기술 스택으로는 React와 React Native를 사용하기로 했습니다. 이 3가지 서비스에서 공통적으로 사용하는 모듈과 컴포넌트를 효율적으로 관리하기 위해 모노레포 방식을 도입하고, 이를 지원하는 도구로 Turborepo를 선택했습니다.</p>
<h1 id="turborepo란">Turborepo란?</h1>
<p>Turborepo는 JavaScript와 TypeScript 프로젝트에 최적화된 빌드 시스템입니다. 코드베이스 작업(린트, 빌드, 테스트 등)은 시간이 많이 소요되는데, Turborepo는 캐싱을 통해 이러한 작업을 빠르게 처리하고, CI 속도를 향상시킵니다. 또한 Vercel에서 개발한 도구라는 점에서 많은 장점이 있습니다.</p>
<p><strong>기능</strong>
Turborepo는 고급 빌드 시스템 기술을 활용해 개발 속도를 크게 향상시킵니다. 캐싱을 사용하여 동일한 작업을 반복하지 않으며, 멀티태스킹 능력을 극대화하고, 스케줄링과 CPU 유휴 시간을 최소화합니다.</p>
<h2 id="turborepo-설치">Turborepo 설치</h2>
<p>Turborepo는 여러 방법으로 설치할 수 있습니다. 아래 명령어로 전역 또는 로컬 설치가 가능합니다:</p>
<pre><code class="language-bash"># 전역 설치
npm install turbo --global
yarn global add turbo
pnpm install turbo --global
</code></pre>
<p>로컬 설치는 아래 명령어를 사용합니다:</p>
<pre><code class="language-bash">bash
복사편집
# 로컬 설치
npm install turbo --dev
yarn add turbo --dev --ignore-workspace-root-check
pnpm add turbo --save-dev --ignore-workspace-root-check
</code></pre>
<h2 id="새-모노레포-생성">새 모노레포 생성</h2>
<p>새로운 모노레포를 시작하려면 <strong>create-turbo</strong> 패키지를 사용합니다:</p>
<pre><code class="language-bash">npx create-turbo@latest
yarn dlx create-turbo@latest
pnpm dlx create-turbo@latest
</code></pre>
<h3 id="디렉터리-구조"><strong>디렉터리 구조</strong></h3>
<p>모노레포를 생성하면 다음과 같은 디렉터리 구조가 생성됩니다:</p>
<pre><code class="language-bash">apps
  - apps/docs
  - apps/web
packages
  - packages/eslint-config-custom
  - packages/tsconfig
  - packages/ui
</code></pre>
<p>각각의 폴더는 <strong>package.json</strong>을 포함하며 독립적으로 코드를 작성하고 의존성을 관리할 수 있습니다. 다만, 다른 작업 공간에서 코드를 재사용할 수 있습니다.</p>
<h3 id="패키지-의존성"><strong>패키지 의존성</strong></h3>
<p>예를 들어, <strong>apps/web</strong>은 <strong>packages/ui</strong> 패키지를 의존하고 있습니다. 즉, <strong>web 앱</strong>이 <strong>ui</strong>라는 로컬 패키지를 사용하는 구조입니다. 이는 다른 작업 공간 간 코드 공유를 용이하게 만듭니다.</p>
<pre><code class="language-json">// apps/web/package.json
{
  &quot;name&quot;: &quot;web&quot;,
  &quot;dependencies&quot;: {
    &quot;ui&quot;: &quot;workspace:*&quot;
  }
}
</code></pre>
<p>모노레포 내에서 애플리케이션을 넘어 코드와 설정을 공유하는 패턴은 매우 유용합니다.</p>
<h3 id="imports와-exports"><strong>imports와 exports</strong></h3>
<p>각각의 앱은 공통으로 사용하는 컴포넌트나 모듈을 <code>import</code>하여 사용할 수 있습니다. 예를 들어, <strong>ui</strong> 패키지에서 <code>Button</code> 컴포넌트를 정의한 후, 이를 <strong>web</strong>과 <strong>docs</strong> 애플리케이션에서 불러와 사용합니다.</p>
<pre><code class="language-tsx">// packages/ui/index.tsx
export * from &quot;./Button&quot;;
</code></pre>
<p>이렇게 작성된 코드는 <code>apps/web</code>과 <code>apps/docs</code>에서 모두 사용할 수 있습니다.</p>
<h3 id="tsconfig"><strong>tsconfig</strong></h3>
<p>모노레포의 모든 패키지에서 <strong>tsconfig</strong>를 공유하는 설정이 필요할 때, <code>packages/tsconfig</code> 폴더에 설정을 둡니다. 예를 들어:</p>
<pre><code class="language-json">// packages/tsconfig/package.json
{
  &quot;name&quot;: &quot;tsconfig&quot;,
  &quot;version&quot;: &quot;0.0.0&quot;,
  &quot;private&quot;: true,
  &quot;license&quot;: &quot;MIT&quot;,
  &quot;files&quot;: [&quot;base.json&quot;, &quot;nextjs.json&quot;, &quot;react-library.json&quot;]
}
</code></pre>
<p>그리고 각 애플리케이션은 이 설정을 <code>extends</code>로 참조합니다.</p>
<pre><code class="language-json">// packages/ui/tsconfig.json
{
  &quot;extends&quot;: &quot;tsconfig/react-library.json&quot;
}
</code></pre>
<p>이렇게 공통 설정을 활용하면 중복을 줄이고, 코드 관리가 쉬워집니다.</p>
<h3 id="eslint-config-custom"><strong>eslint-config-custom</strong></h3>
<p><strong>eslint-config-custom</strong>은 <strong>eslint</strong> 설정을 모노레포 내 모든 작업 공간에서 공유할 수 있게 해줍니다. 예를 들어, <strong>packages/eslint-config-custom</strong> 폴더에 ESLint 설정 파일을 두고, 각 작업 공간에서 이를 참조합니다.</p>
<pre><code>// packages/eslint-config-custom/.eslintrc.js
module.exports = {
  extends: [&quot;next&quot;, &quot;turbo&quot;, &quot;prettier&quot;]
};
</code></pre><p>이를 통해, <strong>docs</strong>, <strong>web</strong>, <strong>ui</strong> 등 모든 애플리케이션이 동일한 ESLint 규칙을 따를 수 있습니다.</p>
<h2 id="turborepojson">turborepo.json</h2>
<p>Turborepo의 핵심 설정 파일은 <strong>turbo.json</strong>입니다. 여기서 각 파이프라인의 작업 흐름을 정의할 수 있습니다. 예를 들어, <strong>build</strong> 파이프라인에서는 출력 디렉터리를 지정할 수 있습니다.</p>
<pre><code class="language-json">{
  &quot;$schema&quot;: &quot;https://turbo.build/schema.json&quot;,
  &quot;globalDependencies&quot;: [&quot;**/.env.*local&quot;],
  &quot;pipeline&quot;: {
    &quot;build&quot;: {
      &quot;outputs&quot;: [&quot;.next/**&quot;, &quot;!.next/cache/**&quot;]
    },
    &quot;lint&quot;: {},
    &quot;dev&quot;: {
      &quot;cache&quot;: false,
      &quot;persistent&quot;: true}
  }
}
</code></pre>
<h2 id="turborepo-명령어-실행">Turborepo 명령어 실행</h2>
<h3 id="lint-실행"><strong>lint 실행</strong></h3>
<p>Turborepo는 여러 작업 공간에서 lint를 동시에 실행할 수 있습니다. 예를 들어, <code>turbo lint</code> 명령어를 실행하면 모든 작업 공간에서 정의된 <strong>lint</strong> 스크립트를 동시에 실행합니다.</p>
<pre><code class="language-bash">turbo lint
</code></pre>
<p>이 명령어를 실행하면, <strong>docs:lint</strong>, <strong>web:lint</strong>, <strong>ui:lint</strong> 등의 스크립트가 동시에 실행됩니다.</p>
<h3 id="캐시-활용"><strong>캐시 활용</strong></h3>
<p>Turborepo는 이전 실행의 결과를 캐시하여, 동일한 작업을 반복하지 않습니다. 두 번째로 <code>turbo lint</code>를 실행하면, 캐시된 결과를 사용하여 실행 시간을 단축시킵니다.</p>
<h3 id="build-실행"><strong>build 실행</strong></h3>
<p>빌드 명령어를 실행할 때도, 이전 빌드 결과를 캐시하여 빠르게 빌드를 완료합니다. 예를 들어, <code>turbo build</code>를 실행하면, 캐시된 결과를 복원하고, 필요한 경우에만 다시 빌드를 실행합니다.</p>
<pre><code class="language-bash">turbo build
</code></pre>
<h3 id="dev-실행"><strong>dev 실행</strong></h3>
<p><code>turbo dev</code>는 각 애플리케이션의 <strong>개발 서버</strong>를 실행합니다. 기본적으로 모든 작업 공간에서 <code>dev</code> 스크립트를 실행하지만, 특정 작업 공간만 실행하고 싶다면 <code>--filter</code> 플래그를 사용할 수 있습니다.</p>
<pre><code class="language-bash">turbo dev --filter docs
</code></pre>
<p>이 명령어는 <strong>docs</strong> 작업 공간에서만 <strong>dev</strong> 서버를 실행합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모노레포 적용하기 with 🚀Turborepo (1/2)]]></title>
            <link>https://velog.io/@flip_404/%EB%AA%A8%EB%85%B8%EB%A0%88%ED%8F%AC-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-with-Turborepo-12</link>
            <guid>https://velog.io/@flip_404/%EB%AA%A8%EB%85%B8%EB%A0%88%ED%8F%AC-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-with-Turborepo-12</guid>
            <pubDate>Fri, 07 Feb 2025 02:46:47 GMT</pubDate>
            <description><![CDATA[<h1 id="모노레포의-등장배경">모노레포의 등장배경</h1>
<p>모노레포(Monorepo)는 여러 개의 프로젝트 코드가 동일한 저장소에서 관리되는 소프트웨어 개발 전략입니다. 글로벌 테크 기업들인 Google, Facebook, Microsoft, Uber, Airbnb, 그리고 Twitter 등은 이미 대규모 모노레포 시스템을 운영하며, 각각의 독특한 운영 전략을 가지고 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/a2127b0c-9f29-45eb-8dc1-26f0d8f7a486/image.png" alt=""></p>
<p>모노레포가 등장하기 전에, 대부분의 프로젝트가 선택했던 방식은 <strong>멀티레포(Multirepo)</strong>였죠. 멀티레포는 여러 개의 저장소를 각각 독립적으로 관리하는 방식으로, 각 프로젝트가 개별적인 저장소에 존재합니다. 이는 오늘날 대부분의 애플리케이션 개발에서 사용되는 표준적인 방법이기도 합니다.</p>
<p><strong>멀티레포의 문제점</strong>
멀티레포는 각 프로젝트가 독립적인 저장소를 가진다는 점에서, 여러 가지 문제를 동반할 수 있습니다. 예를 들어, 하나의 서비스가 웹, 앱, 공통 디자인 시스템 등을 갖추고 있다면, 각 부분에 대해 별도의 리포지토리를 관리해야 하므로 다음과 같은 문제가 발생합니다.</p>
<ul>
<li><p>번거로운 프로젝트 생성
새로운 공유 패키지를 만들 때마다, 저장소 생성부터 커밋, 개발 환경 구축, CI/CD 설정, 빌드, 패키지 저장소에 퍼블리싱까지 여러 단계를 거쳐야 합니다. 이는 많은 시간과 노력을 소모합니다.</p>
</li>
<li><p>중복 코드와 관리 부담
각 프로젝트가 공통 구성 요소를 자체적으로 구현하는 경우, 초기 개발 속도는 빠를 수 있지만 시간이 지날수록 보안 및 품질 관리의 부담이 커집니다.</p>
</li>
<li><p>관리 포인트의 증가
프로젝트가 많아지면 그만큼 관리해야 할 포인트가 늘어납니다. 린트, 테스트, 빌드, 배포 등 동일한 작업을 각 저장소에서 반복해야 하므로 관리의 효율성이 떨어집니다.</p>
</li>
<li><p>일관성 없는 개발자 경험(DX)
각 프로젝트가 고유한 빌드, 테스트, 린트 명령어를 사용하면, 여러 프로젝트를 넘나드는 개발자에게 정신적 오버헤드가 발생할 수 있습니다.</p>
</li>
<li><p>변경 사항 추적의 어려움
서로 다른 저장소에서 작업하고 있기 때문에, 관련 패키지 간의 변화를 추적하고 반영하는 데 어려움이 있습니다.</p>
</li>
<li><p>교차 저장소 리팩터링의 비용
여러 저장소에 걸쳐 리팩터링을 해야 할 경우, 이를 관리하는 데 드는 시간과 비용이 커집니다.</p>
</li>
</ul>
<p>서비스가 커질수록 이러한 멀티레포의 문제는 점점 더 복잡해지고, 각 모듈의 의존성에 따라 업데이트 시간이 길어집니다. 그렇다면 어떻게 하면 모듈을 적절히 분리하면서도, 테스트, 빌드, 배포 등을 효율적으로 처리할 수 있을까요? 바로 이때 모노레포가 등장합니다.</p>
<h1 id="모노레포monorepo란">모노레포(Monorepo)란?</h1>
<p>모노레포는 여러 개의 프로젝트가 동일한 저장소에서 관리되는 소프트웨어 개발 전략입니다. 앞서 언급한 멀티레포와 달리, 모노레포는 저장소를 하나로 통합하여 여러 프로젝트를 관리할 수 있게 해줍니다. 이렇게 함으로써, 여러 프로젝트가 공통된 환경에서 동작하게 되고, 그로 인해 여러 가지 이점이 생깁니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/dbdcdc7c-a65a-4a09-9102-ecaf1e6618e0/image.png" alt=""></p>
<h2 id="모노레포가-해결하는-멀티레포의-문제들">모노레포가 해결하는 멀티레포의 문제들</h2>
<p>모노레포는 멀티레포에서 발생하는 문제를 해결하며, 프로젝트 관리와 개발 환경을 한층 개선할 수 있습니다. 주요 해결점을 살펴보겠습니다.</p>
<ol>
<li><p>더 쉬운 프로젝트 생성
멀티레포에서는 새로운 프로젝트를 시작하려면 저장소를 생성하고, 커밋을 추가하며, 개발 환경과 CI/CD, 빌드 등을 다시 설정해야 합니다. 그러나 모노레포에서는 이미 공통된 환경이 준비되어 있기 때문에, 새로운 프로젝트를 시작할 때 추가적인 설정 없이 쉽게 진행할 수 있습니다.</p>
</li>
<li><p>더 쉬운 의존성 관리
여러 프로젝트가 동일한 저장소 내에 있기 때문에, 의존성 관리가 매우 간편합니다. 별도로 패키지를 퍼블리싱하거나 버전을 관리할 필요 없이, 동일한 저장소 내에서 모든 패키지의 의존성을 직접 관리할 수 있습니다.</p>
</li>
<li><p>단일화된 관리 포인트
개발 환경과 DevOps 시스템을 한 곳에서 관리할 수 있기 때문에, 관리해야 할 포인트가 단일화됩니다. 업데이트나 변경 사항을 한 번에 적용할 수 있어 관리가 더 효율적입니다.</p>
</li>
<li><p>일관된 개발자 경험 제공
모든 프로젝트가 동일한 환경에서 동작하므로, 개발자들이 각기 다른 프로젝트에 기여할 때 일관된 개발자 경험(DX)을 제공합니다. 다양한 프로젝트에서 사용되는 명령어 집합이 동일하므로, 새로운 프로젝트를 맡았을 때 정신적 오버헤드가 줄어듭니다.</p>
</li>
<li><p>원자적 커밋
모노레포에서는 하나의 커밋이 모든 프로젝트에 일관되게 적용됩니다. 이로 인해 변경 사항이 영향을 미치는 모든 조직에서 쉽게 변화를 확인할 수 있습니다.</p>
</li>
<li><p>교차 저장소 리팩터링 비용 절감
여러 저장소에 걸친 리팩터링을 쉽게 할 수 있으며, 100개의 라이브러리와 10개의 앱이 있을 때도, 모노레포는 이를 효율적으로 관리할 수 있게 해줍니다.</p>
</li>
</ol>
<p><strong>모노레포에 대한 오해</strong>
모노레포가 항상 멀티레포보다 우수한 방식일까요? 답은 아니오입니다. 모노레포와 멀티레포는 장단점이 뚜렷하게 교차하므로, 각각의 방식이 적합한 상황에서 사용해야 합니다. 모노레포가 적합한 상황은 다음과 같습니다:</p>
<ul>
<li><p>유사한 제품 집합
같은 종류의 프로젝트들이 함께 있는 경우.</p>
</li>
<li><p>여러 프로젝트의 변화를 동시에 관리해야 할 때
다양한 프로젝트에서 일어나는 변경 사항을 한눈에 파악할 때 유리합니다.</p>
</li>
<li><p>플러그인 형태로 애플리케이션을 확장할 때
동일한 기본 애플리케이션을 여러 플러그인으로 확장하는 경우.</p>
</li>
<li><p>공통 기능을 재사용하는 프로젝트 집합
여러 프로젝트가 동일한 기능을 공유하는 경우.</p>
</li>
<li><p>유사한 DevOps 환경을 갖춘 프로젝트들
DevOps 시스템이 비슷한 여러 프로젝트가 있는 경우.</p>
</li>
</ul>
<p><strong>모노레포 도입 전 고려할 점</strong>
모노레포를 도입할 때는 위와 같은 장점 외에도 몇 가지 고려해야 할 점들이 있습니다. 예를 들어, 대규모 프로젝트에서는 저장소 크기가 커질 수 있고, 이를 관리하는 데 필요한 도구들이 복잡해질 수 있습니다. 그렇지만, 모노레포는 프로젝트 간의 의존성을 간소화하고 개발 효율성을 높일 수 있는 유효한 전략입니다.</p>
<p>실제 사례를 통해 배우기
모노레포를 다룬 이론만으로는 이해가 부족할 수 있습니다. 실제로 Turborepo와 같은 도구를 사용해 모노레포 환경을 구축하며, 그 장점을 직접 경험하는 것이 중요합니다. 다음 글에서는 Turborepo를 적용해 보며 모노레포의 실용적인 면을 살펴보겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프론트엔드 에러 모니터링 (with. Sentry)]]></title>
            <link>https://velog.io/@flip_404/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%97%90%EB%9F%AC-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81-with.-Sentry</link>
            <guid>https://velog.io/@flip_404/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%97%90%EB%9F%AC-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81-with.-Sentry</guid>
            <pubDate>Thu, 06 Feb 2025 08:58:04 GMT</pubDate>
            <description><![CDATA[<p>서비스를 배포하고 지속적으로 유지보수하려면 발생하는 에러를 신속하게 분석하고 대응할 수 있어야 합니다.</p>
<p>하지만 개발 환경에서 볼 수 있는 에러는 한정적입니다. 다양한 기기와 버전, 환경을 모두 테스트하는 것은 불가능에 가깝기 때문입니다. 우리는 사용자가 어떤 기기를 사용하고, 어떤 소프트웨어와 브라우저 버전을 사용하고 있는지 예측할 수 없습니다. 게다가 사용자에게 이를 직접 묻는 것도 현실적으로 어려운 일입니다. 이러한 예기치 못한 상황에서 발생하는 에러를 분석하고 대응하려면 <strong>모니터링 툴</strong>이 필요합니다.</p>
<p>프론트엔드에서 오류를 탐지하는 다양한 모니터링 툴이 존재하는데, 그 중에서도 널리 사용되는 툴인 <a href="https://sentry.io/for/frontend/">Sentry</a>에 대해 소개해보겠습니다.</p>
<h1 id="sentry">Sentry</h1>
<p>오류를 탐지하기 위한 프론트엔드 모니터링 툴은 여러 가지가 존재합니다. 그 중 널리 사용되고 있는 <a href="https://sentry.io/for/frontend/">Sentry</a>에 대해서 소개해보고자 합니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/3989090a-e082-4b17-beac-0bf8b3459549/image.png" alt=""></p>
<p>Sentry는 <strong>실시간 로그 수집 및 분석 도구이자 모니터링 플랫폼</strong>으로, 로그를 통해 발생한 이벤트에 대한 다양한 정보를 제공하고 이를 효과적으로 분석할 수 있게 돕습니다. 이벤트가 발생한 시점과 타임라인을 확인할 수 있으며, 필요한 경우 알림을 설정하여 실시간으로 오류를 추적할 수 있습니다. Sentry는 단순히 로그를 수집하는 것에 그치지 않고, 이를 시각적으로 분석할 수 있는 도구도 제공합니다. 또한, 다양한 플랫폼과 통합이 가능합니다.</p>
<p>다음은 Sentry의 주요 특징에 대해 간략하게 설명한 내용입니다.</p>
<ol>
<li><p><strong>이벤트 로그 상세 정보 제공</strong></p>
<p> Sentry는 이벤트가 발생했을 때 관련된 다양한 정보를 제공합니다.</p>
<ul>
<li><p><strong>Exception &amp; Message</strong>: 발생한 이벤트 로그의 메시지 및 코드 라인 정보 (정확한 코드 라인 정보는 source map 설정을 통해 확인할 수 있습니다.)</p>
</li>
<li><p><strong>Device</strong>: 이벤트가 발생한 장치의 정보 (이름, 모델, 메모리 등)</p>
</li>
<li><p><strong>Browser</strong>: 이벤트가 발생한 브라우저의 정보 (브라우저 이름, 버전 등)</p>
</li>
<li><p><strong>OS</strong>: 이벤트가 발생한 운영 체제의 정보 (이름, 버전, 빌드, 커널 버전 등)</p>
</li>
<li><p><strong>Breadcrumbs</strong>: 이벤트 발생 과정에서의 정보</p>
<p>또한, 기본적으로 제공되는 정보 외에도 <strong>Context</strong> 기능을 사용해 특정 이벤트에 대한 추가 정보를 수집할 수 있습니다.</p>
</li>
</ul>
</li>
<li><p><strong>유사 오류 통합</strong></p>
<p> Sentry는 <strong>이슈 그룹화(Issue Grouping)</strong> 기능을 통해 유사한 이벤트 로그를 하나의 이슈로 묶어 관리할 수 있습니다. 이를 통해 비슷한 오류를 빠르게 식별하고 추적할 수 있어 매우 유용합니다.</p>
</li>
<li><p><strong>다양한 알림 채널 지원</strong></p>
<p> 발생한 이슈에 대해 실시간 알림을 받을 수 있는 다양한 채널을 지원합니다. 예를 들어, Slack, Teams, Jira, GitHub 등과 연동하여 알림을 받을 수 있습니다. 현재 제 회사에서는 Slack을 통해 실시간으로 오류를 추적하고 있습니다.</p>
</li>
</ol>
<h1 id="코드보며-이해하기-with-sentryreact">코드보며 이해하기 (with Sentry/react)</h1>
<p>제 회사에서는 프론트엔드 기술 스택으로 주로 <strong>React</strong>를 사용하고 있습니다. 이번에는 React 환경에서 Sentry를 설정하고 데이터를 쌓을 수 있는 기본 기능에 대해 살펴보겠습니다.</p>
<h2 id="설치-및-설정"><strong>설치 및 설정</strong></h2>
<ul>
<li><p><strong>설치</strong></p>
<p>  Sentry를 사용하기 위해 필요한 패키지를 설치합니다. Sentry는 애플리케이션 런타임 내에서 SDK를 활용해 데이터를 캡처하므로 <code>@sentry/react</code>, <code>@sentry/tracing</code> 패키지를 설치해야 합니다.</p>
<pre><code class="language-bash">  # npm 사용
  npm install --save @sentry/react @sentry/tracing

  # yarn 사용
  yarn add @sentry/react @sentry/tracing</code></pre>
<p>  <code>@sentry/browser</code>에서 제공되는 모든 메소드는 <code>@sentry/react</code>에서도 사용할 수 있습니다.</p>
</li>
<li><p><strong>설정</strong></p>
<pre><code class="language-jsx">  import React from &#39;react&#39;;
  import ReactDOM from &#39;react-dom&#39;;
  import * as Sentry from &#39;@sentry/react&#39;;
  import { BrowserTracing } from &#39;@sentry/tracing&#39;;
  import App from &#39;./App&#39;;

  Sentry.init({
    dsn: &#39;dsn key&#39;,
    release: &#39;release version&#39;,
    environment: &#39;production&#39;,
    normalizeDepth: 6,
    integrations: [
      new Sentry.Integrations.Breadcrumbs({ console: true }),
      new BrowserTracing(),
    ],
  });

  ReactDOM.render(&lt;App /&gt;, document.getElementById(&#39;root&#39;));</code></pre>
<p>  Sentry 설정을 위한 주요 항목은 다음과 같습니다:</p>
<ul>
<li><p><strong>dsn</strong>: 이벤트를 전송할 때 사용하는 식별 키</p>
</li>
<li><p><strong>release</strong>: 애플리케이션 버전 (보통 <code>package.json</code>에 명시된 버전 사용, 이는 버전별 오류 추적을 용이하게 합니다)</p>
</li>
<li><p><strong>environment</strong>: 애플리케이션 환경 (예: dev, production 등)</p>
</li>
<li><p><strong>normalizeDepth</strong>: 컨텍스트 데이터를 주어진 깊이까지 정규화 (기본값: 3)</p>
</li>
<li><p><strong>integrations</strong>: 플랫폼 SDK와의 통합 설정 (React에서는 <code>react-router</code> 통합 설정 가능) 이벤트를 전송하기 전 선택적으로 데이터를 수정할 수 있는 <code>beforeSend</code>와 같은 옵션도 제공합니다.</p>
<p>추가적인 설정 옵션은 <a href="https://docs.sentry.io/platforms/javascript/guides/react/configuration/options/">공식 문서</a>에서 확인할 수 있습니다.</p>
</li>
</ul>
</li>
</ul>
<p>추가적으로 React SDK는 자동으로 JavaScript 오류를 탐지하고 Sentry로 전송할 수 있도록 Error Boundary 컴포넌트를 제공하며 다음과 같이 사용할 수 있습니다.</p>
<pre><code class="language-jsx">import React from &#39;react&#39;;
import * as Sentry from &#39;@Sentry/react&#39;;

&lt;Sentry.ErrorBoundary
  fallback={&lt;p&gt;에러가 발생하였습니다. 잠시 후 다시 시도해주세요.&lt;/p&gt;}
&gt;
  &lt;Example /&gt;
&lt;/Sentry.ErrorBoundary&gt;;</code></pre>
<h2 id="sentry로-에러-전송하기">Sentry로 에러 전송하기</h2>
<p>이제 Sentry를 통해 에러를 전송해 봅시다.</p>
<p><strong><code>captureException</code></strong></p>
<ul>
<li><strong>목적</strong>: 예외(오류)가 발생했을 때, 그 오류에 대한 정보를 Sentry로 전송하는 데 사용됩니다.</li>
<li><strong>사용법</strong>: 일반적으로 <code>try-catch</code> 구문과 함께 사용하여 예외를 처리한 뒤, 그 예외를 Sentry로 전송합니다.</li>
</ul>
<pre><code class="language-jsx">try {
  // 오류가 발생할 수 있는 코드
  throw new Error(&quot;Something went wrong!&quot;);
} catch (error) {
  Sentry.captureException(error);
}</code></pre>
<ul>
<li><strong>주요 특징</strong>:<ul>
<li>예외 객체를 그대로 전송하기 때문에 오류의 스택 트레이스와 상세한 정보를 포함합니다.</li>
<li>자동으로 오류 발생 위치와 관련된 정보를 포함해, 오류를 추적하기 쉽게 도와줍니다.</li>
</ul>
</li>
</ul>
<p><strong><code>captureMessage</code></strong></p>
<ul>
<li><strong>목적</strong>: 단순한 메시지나 경고를 전송하고 싶을 때 사용됩니다. 예외가 아니라 특정 상황에 대한 메시지를 로깅하고 싶을 때 유용합니다.</li>
<li><strong>사용법</strong>: 예외 없이 단순히 메시지를 전송할 때 사용됩니다.</li>
</ul>
<pre><code class="language-jsx">Sentry.captureMessage(&quot;This is a custom warning message&quot;, &quot;warning&quot;);</code></pre>
<ul>
<li><strong>주요 특징</strong>:<ul>
<li><code>captureMessage</code>는 예외가 아니므로, 메시지 자체와 그 수준(level)을 지정할 수 있습니다.</li>
<li><code>&quot;info&quot;</code>, <code>&quot;warning&quot;</code>, <code>&quot;error&quot;</code> 등의 메시지 레벨을 설정할 수 있습니다.</li>
</ul>
</li>
</ul>
<p>Sentry의 전송 API는 위의 예시 외에도 다양한 추가 기능을 제공합니다.</p>
<table>
<thead>
<tr>
<th>API</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>captureEvent</code></td>
<td>사용자 정의 이벤트 객체를 직접 전송</td>
</tr>
<tr>
<td><code>captureException</code></td>
<td>예외 객체를 전송 (기존)</td>
</tr>
<tr>
<td><code>captureMessage</code></td>
<td>단순 메시지 전송 (기존)</td>
</tr>
<tr>
<td><code>addBreadcrumb</code></td>
<td>로그나 이벤트에 대한 추가적인 정보를 기록</td>
</tr>
<tr>
<td><code>setUser</code></td>
<td>사용자 정보를 설정하여 해당 사용자에 대한 이벤트 추적</td>
</tr>
<tr>
<td><code>setTag</code></td>
<td>이벤트에 태그를 추가하여 필터링을 용이하게 함</td>
</tr>
<tr>
<td><code>setContext</code></td>
<td>이벤트에 추가적인 컨텍스트(상황 정보)를 설정</td>
</tr>
</tbody></table>
<h2 id="scope의-주요-기능">Scope의 주요 기능</h2>
<p>Sentry는 <code>scope</code> <strong>단위로 이벤트 데이터를 관리</strong>합니다. 이벤트가 전송되면 해당 이벤트의 데이터를 현재 <code>scope</code>의 추가 정보와 병합합니다.</p>
<h3 id="configurescope---스코프-전역-설정">configureScope - 스코프 전역 설정</h3>
<ol>
<li><p><strong>User Information (사용자 정보)</strong></p>
<ul>
<li><p>사용자의 ID, 이름, 이메일 등을 추가하여 발생한 에러가 어느 사용자에게 관련된 것인지 추적할 수 있습니다.</p>
<pre><code class="language-jsx">Sentry.configureScope(scope =&gt; {
scope.setUser({ id: &#39;123&#39;, email: &#39;user@example.com&#39; });
});</code></pre>
</li>
</ul>
</li>
<li><p><strong>Tags (태그)</strong></p>
<ul>
<li><p>이벤트에 대한 추가적인 메타데이터를 태그로 붙여서, 나중에 Sentry 대시보드에서 에러를 필터링하거나 분석하는 데 유용합니다.</p>
<pre><code class="language-jsx">Sentry.configureScope(scope =&gt; {
scope.setTag(&#39;feature&#39;, &#39;checkout&#39;);
});</code></pre>
</li>
</ul>
</li>
<li><p><strong>Extra Data (추가 데이터)</strong></p>
<ul>
<li><p>에러 발생 시 더 많은 정보를 함께 전송할 수 있습니다. 예를 들어, 특정 상태나 환경 변수, 앱의 상태 등을 추가할 수 있습니다.</p>
<pre><code class="language-jsx">Sentry.configureScope(scope =&gt; {
scope.setExtra(&#39;app_version&#39;, &#39;1.0.0&#39;);
});</code></pre>
</li>
</ul>
</li>
<li><p><strong>Breadcrumbs (브레드크럼)</strong></p>
<ul>
<li><p>사용자가 수행한 특정 작업들을 기록하여, 에러가 발생하기 전 어떤 일이 있었는지 추적할 수 있습니다. 이를 통해 에러 발생 경로를 추적할 수 있습니다.</p>
<pre><code class="language-jsx">Sentry.addBreadcrumb({
message: &#39;User clicked checkout button&#39;,
level: &#39;info&#39;,
});</code></pre>
</li>
</ul>
</li>
<li><p><strong>Clearing Scope</strong></p>
<ul>
<li><p>특정 이벤트가 발생한 후, 범위를 초기화하거나 변경할 수 있습니다. 예를 들어, 특정 사용자에 대한 이벤트를 추적한 후, 그 범위를 리셋할 수 있습니다.</p>
<pre><code class="language-jsx">Sentry.configureScope(scope =&gt; {
scope.setUser(null); // 사용자 정보 초기화
});</code></pre>
</li>
</ul>
</li>
</ol>
<h3 id="withscope---스코프-지역-설정">withScope - 스코프 지역 설정</h3>
<p>기본적으로 <code>Sentry.configureScope()</code>는 전역적으로 설정을 변경하지만, <code>withScope</code>는 <strong>특정 블록 내에서만</strong> <code>scope</code> 설정을 적용하도록 제한할 수 있습니다. 이를 통해 에러를 발생시킬 때 해당 블록 내에서만 <code>scope</code> 설정이 영향을 미치게 됩니다.</p>
<h3 id="주요-용도">주요 용도</h3>
<ul>
<li><strong>일시적인 범위 설정</strong>: <code>withScope</code>를 사용하면 한 번의 에러를 기록할 때, 그 에러에 대한 <code>user</code>, <code>tags</code>, <code>extra data</code> 등을 <strong>한정적으로 설정</strong>할 수 있습니다. 이 설정은 블록 내에서만 유효하고, 블록을 벗어나면 자동으로 이전 상태로 돌아갑니다.</li>
</ul>
<h3 id="사용-예시">사용 예시</h3>
<pre><code class="language-jsx">Sentry.withScope(scope =&gt; {
  // 이 블록 내에서만 scope가 설정됩니다.
  scope.setUser({ id: &#39;123&#39;, email: &#39;user@example.com&#39; });
  scope.setTag(&#39;page&#39;, &#39;checkout&#39;);

  // 범위 내에서 발생한 이벤트는 설정된 사용자 정보와 태그를 포함
  Sentry.captureException(new Error(&#39;Something went wrong&#39;));
});</code></pre>
<h3 id="custom-context">Custom Context</h3>
<p>Sentry에서는 기본적인 정보 외에도 <strong>custom context</strong>를 추가하여, 개발자가 정의한 특정 정보도 함께 전달할 수 있습니다. 예를 들어, 특정 API 호출의 응답 데이터나 사용자가 선택한 옵션을 기록하는 등의 작업이 가능합니다.</p>
<pre><code class="language-jsx">Sentry.configureScope(scope =&gt; {
  scope.setContext(&#39;api_call&#39;, {
    endpoint: &#39;/users&#39;,
    method: &#39;POST&#39;,
    status: &#39;failed&#39;,
  });
});</code></pre>
<h3 id="customized-tags-커스텀-태그">Customized Tags (커스텀 태그)</h3>
<p>에러나 이벤트에 <strong>사용자가 정의한 태그</strong>를 추가하여, 나중에 Sentry 대시보드에서 보다 <strong>세부적으로 필터링</strong>하거나 <strong>분석</strong>할 수 있도록 돕는 기능입니다. 태그는 주로 이벤트나 에러의 <strong>특징적인 속성</strong>을 나타내는 데 사용되며, 이를 통해 에러를 그룹화하거나, 특정 조건에 맞는 에러들을 쉽게 검색하고 추적할 수 있습니다.</p>
<h3 id="커스텀-태그의-용도">커스텀 태그의 용도</h3>
<ol>
<li><strong>에러 분류</strong>: 태그를 사용하면 이벤트가 발생한 환경이나 조건에 대한 정보를 제공하여, 다양한 조건으로 에러를 구분할 수 있습니다. 예를 들어, 특정 기능에서 발생한 에러를 필터링하거나, 사용자의 지역, 버전, 요청 타입 등을 기반으로 에러를 그룹화할 수 있습니다.</li>
<li><strong>에러 추적 및 분석</strong>: 에러의 발생 원인을 찾기 위해 다양한 태그를 추가하여, 시간, 사용자 정보, 특정 기능 등을 기준으로 에러를 분석할 수 있습니다.</li>
</ol>
<p><code>scope.setTag()</code>를 사용하여 커스텀 태그를 설정할 수 있습니다. 예를 들어, 특정 페이지나 기능에서 발생한 에러를 태그로 기록할 수 있습니다.</p>
<pre><code class="language-jsx">Sentry.configureScope(scope =&gt; {
  scope.setTag(&#39;feature&#39;, &#39;checkout&#39;);
});</code></pre>
<h3 id="이슈-그룹화">이슈 그룹화</h3>
<p>Sentry에서는 이벤트가 fingerprint를 기반으로 자동 그룹화됩니다. fingerprint는 stacktrace, exception, message 등 정보를 바탕으로 생성되며, 같은 fingerprint를 가진 이벤트는 하나의 이슈로 묶입니다. 그러나 때때로 이슈 그룹화가 예상과 다르게 이루어질 수 있습니다. 예를 들어, 동일한 API에서 발생한 400, 404, 500 오류는 요청 URI가 같으면 하나의 이슈로 묶입니다. 이를 해결하려면 HTTP method, status, url을 fingerprint 조건으로 설정하여 각각의 오류를 독립적인 이슈로 그룹화할 수 있습니다.</p>
<pre><code class="language-jsx">import * as Sentry from &#39;@Sentry/react&#39;;

const { method, url } = error.config; // axios의 error객체
const { status } = error.response; // axios의 error객체

Sentry.setFingerprint([method, status, url]);</code></pre>
<p>Sentry는 이 외에도 Level과 같이 태깅을 하여 에러를 그룹화 하거나, Error 객체를 확장하여 사용자화할 수 있습니다. 이렇게 이슈를 그룹화 하면 동일한 에러 객체로 생성되어 전송된 이벤트들끼리 그룹화 되어 해당 이슈의 빈도나 발생 케이스를 파악할 수 있게 됩니다.</p>
<h1 id="마치며">마치며</h1>
<p>발생하는 모든 이벤트를 모니터링하고 알람을 받는 것은 매우 비생산적인 일입니다. 따라서 서비스의 성격에 따라 알람을 받기 원하는 조건과 임계치(threshold)를 잘 설정해야 합니다. 위에서 소개했던 기능들은 알람에서 알람 조건을 설정할 때 유용하게 사용될 수 있습니다.</p>
<p>API 오류 중 500 에러에 대해서만 알람을 받고 싶다면 <code>tag</code>과 <code>level</code>기능을 이용하여 설정해볼 수 있습니다. 예를 들어 API 500 에러에 대해 <code>level</code>을 <code>Error</code>로, 이벤트의 <code>tag</code>를 <code>api</code>로 설정하면 관련한 이벤트를 추려낼 수가 있게 됩니다. 이 알람 조건은 이벤트나 이슈 검색 조건으로 활용될 수도 있습니다.</p>
<p>또한 API 오류의 경우 사용량이 많은 애플리케이션에서는 HTTP status가 400, 401, 404, 204인 이벤트 수집은 큰 도움이 되지 않을 수 있습니다. 그러나 사용자 경험을 감지하는데 유용하다고 생각된다면 internal server error(500) 이벤트를 수집하고 분석하는 것이 많은 도움이 될 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS와 Reflow의 상관관계]]></title>
            <link>https://velog.io/@flip_404/CSS%EC%99%80-Reflow%EC%9D%98-%EC%83%81%EA%B4%80%EA%B4%80%EA%B3%84</link>
            <guid>https://velog.io/@flip_404/CSS%EC%99%80-Reflow%EC%9D%98-%EC%83%81%EA%B4%80%EA%B4%80%EA%B3%84</guid>
            <pubDate>Wed, 22 Jan 2025 08:50:00 GMT</pubDate>
            <description><![CDATA[<h1 id="1-reflow가-발생하는-이유">1. Reflow가 발생하는 이유</h1>
<p>브라우저의 렌더링 엔진이 DOM 요소들의 <strong>위치와 크기</strong>를 계산하는 과정(레이아웃 단계)에 영향을 미치기 때문입니다. 각 속성은 요소의 <strong>배치, 크기, 관계</strong>에 변화를 일으켜 브라우저가 레이아웃을 다시 계산해야 하는 상황을 만듭니다. 또한, 레이아웃 이후의 렌더링 단계들도 모두 다시 수행되기 때문에 Reflow를 줄이는 것은 성능에 영향을 미칩니다.</p>
<p>레이아웃 재계산은 <strong>메인 스레드에서 수행</strong>되므로, 다른 작업과 동시에 실행되지 못합니다.</p>
<p><strong>Reflow를 발생시키는 경우는 다음과 같습니다.</strong></p>
<p><strong>위치 및 크기 속성 변경</strong></p>
<p>요소의 <strong>크기와 위치</strong>를 변경하므로, 요소 자신뿐만 아니라 주변 요소의 배치도 영향을 받게 됩니다.</p>
<ul>
<li><code>width</code>, <code>height</code>, <code>margin</code>, <code>padding</code> 등은 요소의 크기나 공간을 직접적으로 변경하므로 레이아웃 재계산을 유발합니다.</li>
<li><code>position</code>, <code>top</code>, <code>left</code> 등의 속성은 요소의 <strong>좌표</strong>를 변경하므로 재배치가 필요합니다.</li>
<li><code>display</code>가 <code>none</code>에서 다른 값으로 바뀌면, 요소가 DOM에 다시 참여하게 되며, 해당 요소와 관련된 모든 레이아웃을 다시 계산해야 합니다.</li>
<li><code>margin</code>을 변경하면 해당 요소뿐만 아니라, <strong>인접한 형제 요소의 배치</strong>에도 영향을 미칩니다.</li>
<li><code>overflow</code> 속성은 스크롤 영역을 계산하기 위해 추가적인 레이아웃 계산이 필요합니다.</li>
</ul>
<p><strong>글꼴 및 텍스트 관련 속성 변경</strong></p>
<p>텍스트와 관련된 속성은 글자의 <strong>크기, 간격, 배치</strong>에 영향을 미칩니다. 이는 텍스트가 포함된 요소의 크기와 배치를 다시 계산하게 만듭니다.</p>
<ul>
<li>글꼴 크기(<code>font-size</code>)가 변경되면 텍스트가 차지하는 공간이 달라져 요소의 크기를 변경합니다.</li>
<li><code>line-height</code>는 텍스트 줄 간의 간격을 조정하므로, 요소의 높이가 달라질 수 있습니다.</li>
<li><code>letter-spacing</code>, <code>word-spacing</code>은 텍스트의 너비를 변경해 레이아웃 계산에 영향을 줍니다.</li>
<li><code>white-space</code>는 텍스트가 줄 바꿈 처리되는 방식을 바꿔, 텍스트 배치와 요소의 크기를 재조정하게 만듭니다.</li>
</ul>
<p><strong>DOM 변경</strong></p>
<p>DOM 트리 변경은 요소의 <strong>구조적 변경</strong>을 야기하므로, 브라우저는 레이아웃을 다시 계산해야 합니다.</p>
<ul>
<li>DOM에 새로운 요소가 추가되거나 기존 요소가 삭제되면, 요소들의 배치와 크기를 다시 계산해야 합니다.</li>
<li>부모 요소에 자식이 추가되거나 제거되면 부모 요소의 크기와 레이아웃이 바뀌고, 이는 부모와 관련된 다른 요소들에게도 영향을 미칩니다.</li>
<li>텍스트 콘텐츠 변경(<code>textContent</code>, <code>innerHTML</code>)은 텍스트의 크기나 줄 바꿈 등을 변경시켜 레이아웃을 다시 계산하게 만듭니다.</li>
<li>속성 변경(<code>className</code>, <code>id</code>, <code>style</code>)은 요소의 스타일이나 배치 속성을 변경할 수 있으므로 레이아웃 재계산을 유발합니다.</li>
</ul>
<p><strong>특정 JavaScript 속성 접근</strong></p>
<p>JavaScript에서 일부 속성을 읽으려고 하면 브라우저는 최신 레이아웃 정보를 제공하기 위해 레이아웃을 강제로 계산합니다.</p>
<p>대표적인 속성:</p>
<ul>
<li><code>offsetWidth</code>, <code>offsetHeight</code></li>
<li><code>clientWidth</code>, <code>clientHeight</code></li>
<li><code>getBoundingClientRect()</code></li>
<li><code>scrollWidth</code>, <code>scrollHeight</code></li>
</ul>
<pre><code class="language-jsx">const width = element.offsetWidth; // 레이아웃 강제 재계산</code></pre>
<h1 id="2-graphics-layer-이해하기">2. <strong>Graphics Layer 이해하기</strong></h1>
<p>먼저 다음 그림으로 같이 브라우저 렌더링 과정을 살펴봅시다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/c7aaed42-559a-445b-b0ad-b39ef8da99f9/image.png" alt=""></p>
<p>그림에선 나오지 않지만, Composite 단계 이전에 Layout과 Paint 단계를 거치면 Layer Tree가 형성되고 <strong>Paint Layer</strong>와 <strong>Graphics Layer</strong> 이 두 가지로 나뉘게 됩니다.</p>
<p>Paint Layer는 HTML 요소를 기반으로 생성된 레이어로, 요소들이 화면에 어떻게 보일지에 대한 페인팅 정보를 관리합니다.</p>
<p>Graphics Layer는 Paint Layer를 GPU에서 렌더링하기 위해 <strong>합성(Compositing)</strong> 단계에서 생성된 레이어입니다.</p>
<table>
<thead>
<tr>
<th></th>
<th><strong>Paint Layer</strong></th>
<th><strong>Graphics Layer</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>생성 주체</strong></td>
<td>HTML 요소와 스타일 정보</td>
<td>GPU에서 합성을 위해 Paint Layer 기반으로 생성</td>
</tr>
<tr>
<td><strong>역할</strong></td>
<td>요소를 픽셀로 페인팅</td>
<td>Paint Layer를 GPU에서 합성 및 렌더링 처리</td>
</tr>
<tr>
<td><strong>연관 속성</strong></td>
<td><code>z-index</code>, <code>position</code> 등</td>
<td><code>transform</code>, <code>opacity</code>, <code>will-change</code> 등</td>
</tr>
<tr>
<td><strong>주 사용 영역</strong></td>
<td>CPU에서의 렌더링</td>
<td>GPU 가속을 활용한 합성</td>
</tr>
</tbody></table>
<p><strong>Graphics Layer</strong>는 Paint Layer(Render Layer)에서 한 단계 더 나아가, 화면에 출력될 요소들의 처리를 GPU가 담당하도록 하기 위해 생성되는 레이어입니다. <strong>GPU에게는 하나의 렌더링 단위</strong>로 작용하며, 이를 통해 렌더링 성능을 최적화합니다. </p>
<p><strong>Graphics Layer</strong>는 GPU의 강력한 병렬 처리 능력을 활용하기 위해 생성되며, CPU가 해야 할 일부 작업을 GPU로 위임함으로써 <strong>렌더링 성능을 대폭 향상</strong>시킵니다. GPU가 Graphics Layer를 묶어 처리하는 과정을 <strong>하드웨어 가속</strong> 또는 <strong>GPU 가속</strong>이라고 합니다.</p>
<p><strong>Graphics Layer의 특징</strong></p>
<ul>
<li><strong>GPU가 처리</strong>: Graphics Layer에 위치한 요소는 <strong>CPU가 아닌 GPU</strong>가 처리합니다.</li>
<li><strong>하드웨어 가속(GPU 가속)</strong>: CPU가 처리할 작업을 GPU에 위임하여, Graphics Layer를 묶어 하나의 이미지(텍스처)로 생성합니다.<ul>
<li><strong>텍스처</strong>: GPU에 전달되는 비트맵 이미지 형식.</li>
</ul>
</li>
</ul>
<p><strong>Graphics Layer가 생성되는 경우</strong></p>
<p>Graphics Layer는 다음과 같은 조건에서 생성됩니다.</p>
<p>(아래 외에도 추가적인 조건이 존재할 수 있습니다.)</p>
<ol>
<li><strong><code>&lt;video&gt;</code>나 <code>&lt;canvas&gt;</code> 태그</strong>를 사용하는 경우.</li>
<li><strong>하드웨어 가속 플러그인</strong>을 사용하는 경우.</li>
<li><strong>3D 변환</strong> 속성(<code>transform: perspective</code>, <code>rotate3d</code> 등)이 있는 경우.</li>
<li><strong>하드웨어 가속된 2D canvas</strong> 요소인 경우.</li>
<li><em><code>backface-visibility</code> 속성이 <code>hidden</code></em>인 경우.</li>
<li><strong><code>transition</code>, <code>animation</code></strong> 속성이 있는 경우.</li>
<li><strong><code>will-change</code></strong> 속성이 설정된 경우(<code>opacity</code>, <code>transform</code>, <code>top</code>, <code>left</code> 등).</li>
</ol>
<p><strong>Graphics Layer의 GPU 처리 방식</strong></p>
<ol>
<li>CPU는 Graphics Layer를 <strong>텍스처 형식</strong>으로 변환합니다.<ul>
<li>텍스처는 GPU에 전달될 <strong>비트맵 이미지</strong>를 의미합니다.</li>
</ul>
</li>
<li>GPU는 이 텍스처를 받아 화면에 빠르게 렌더링합니다.</li>
</ol>
<h1 id="3-transform-속성으로-레이아웃--페인팅-단계-피하기">3. transform 속성으로 레이아웃 &amp; 페인팅 단계 피하기</h1>
<p><img src="https://velog.velcdn.com/images/flip_404/post/dca6a256-c6a7-4b75-90a1-f3d5ae47d9d3/image.png" alt=""></p>
<p>브라우저에서 위 그림과 같이 사람 모양의 그림이 이동하는 것을 구현하고 싶을 때 배경까지 프레임 단위로 다시 그리면 굉장히 비효율적일 것 입니다.</p>
<p>Graphics Layer를 활용하면 여기서 사람 모양의 그림만 분리해서 출력하고 <strong>변경된 위치만 계산</strong>하면 됩니다.</p>
<p>이는 GPU에서 작업이 이뤄져 CPU와 그래픽 작업을 분리할 수 있어 CPU의 부담을 덜어줄 수 있습니다.</p>
<p><strong>transform의 동작 방식</strong></p>
<ul>
<li><code>transform</code>은 새로운 픽셀 데이터를 그리지 않습니다. 대신, 기존 레이어를 GPU가 처리하므로 추가적인 페인팅 작업이 필요하지 않습니다.</li>
<li><code>transform</code>은 요소의 렌더링된 결과(합성 단계)를 변경합니다.즉, <strong>레이아웃 단계 없이</strong> 요소의 시각적 표현만 변경되며, 레이아웃 계산에 영향을 미치지 않습니다.</li>
<li>요소가 이동하거나 크기가 변경되어도 다른 요소의 배치에는 영향을 주지 않습니다.</li>
<li>이는 <strong>레벨 3 합성(compositing)</strong> 단계에서 기존 레이아웃 정보를 그대로 사용하면서 GPU 가속을 통해 요소를 이동하거나 회전시킵니다.</li>
</ul>
<h1 id="4-그-외의-reflow를-줄이는-방법">4. 그 외의 Reflow를 줄이는 방법</h1>
<h3 id="1-스타일-변경-최소화">(1) <strong>스타일 변경 최소화</strong></h3>
<ul>
<li>여러 스타일을 한 번에 변경:</li>
</ul>
<pre><code class="language-jsx">
element.style.width = &quot;100px&quot;;
element.style.height = &quot;50px&quot;;

// 효율적
element.style.cssText = &quot;width: 100px; height: 50px;&quot;;</code></pre>
<h3 id="2-클래스-활용">(2) <strong>클래스 활용</strong></h3>
<ul>
<li>개별 스타일 변경 대신 CSS 클래스를 추가/제거:</li>
</ul>
<pre><code class="language-jsx">element.classList.add(&quot;new-class&quot;);</code></pre>
<h3 id="3-dom-변경-작업-병합">(3) <strong>DOM 변경 작업 병합</strong></h3>
<ul>
<li>DOM 업데이트를 한 번에 처리:</li>
</ul>
<pre><code class="language-jsx">// 비효율적
const parent = document.querySelector(&quot;#parent&quot;);
parent.appendChild(child1);
parent.appendChild(child2);

// 효율적
const fragment = document.createDocumentFragment();
fragment.appendChild(child1);
fragment.appendChild(child2);
parent.appendChild(fragment);</code></pre>
<h3 id="4-레이아웃-속성-접근-줄이기">(4) <strong>레이아웃 속성 접근 줄이기</strong></h3>
<ul>
<li>레이아웃 관련 속성을 읽은 뒤 바로 쓰는 작업을 반복하지 않도록 주의:</li>
</ul>
<pre><code class="language-jsx">// 비효율적
element.style.width = element.offsetWidth + &quot;10px&quot;;

****// 효율적
const width = element.offsetWidth;
element.style.width = width + &quot;10px&quot;;</code></pre>
<h3 id="5-애니메이션-최적화">(5) <strong>애니메이션 최적화</strong></h3>
<ul>
<li>레이아웃을 변경하지 않는 속성(<code>transform</code>, <code>opacity</code>)을 사용하여 GPU 가속을 활용:</li>
</ul>
<pre><code class="language-css">.element {
  transform: translateX(100px); /* 레이아웃 변경 없이 애니메이션 가능 */
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[FSD 아키텍쳐 : 프론트엔드 아키텍쳐]]></title>
            <link>https://velog.io/@flip_404/FSD-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90</link>
            <guid>https://velog.io/@flip_404/FSD-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90</guid>
            <pubDate>Fri, 13 Sep 2024 07:12:06 GMT</pubDate>
            <description><![CDATA[<h1 id="fsd-아키텍쳐">FSD 아키텍쳐</h1>
<p>Feature-Sliced Design 으로 프런트엔드 애플리케이션을 스캐폴딩하기 위한 아키텍처 방법론입니다. 간단히 말해, 코드 구성에 관한 규칙의 모음이다. 이 방법론의 주된 목적은 끊임없이 변화하는 비즈니스 요구사항에 맞서 프로젝트를 더 이해하기 쉽고 체계적으로 만드는 것이다.</p>
<h2 id="features">Features</h2>
<ul>
<li>Explicit business logic : 명시적인 비즈니스 로직. 도메인 범위 덕분에 쉽게 검색 가능한 아키텍처이다.</li>
<li>Adaptability : 적응성. 새로운 요구 사항에 따라 아키텍처 구성 요소를 유연하게 교체 및 추가할 수 있다.</li>
<li>Tech debt &amp; Refactoring : 기술 부채 및 리팩토링. 각 모듈은 사이드 이펙트없이 독립적으로 수정/재작성할 수 있다.</li>
<li>Explicit code reuse : 명시적 코드 재사용. DRY와 로컬 사용자화 사이의 균형이 유지된다.</li>
</ul>
<h2 id="scheme">Scheme</h2>
<p>FSD에서 프로젝트는 레이어로 구성되며, 각 레이어는 슬라이스로, 각 슬라이스는 세그먼트로 구성된다.</p>
<h3 id="layers">Layers</h3>
<p>레이어는 디렉토리의 최상위이고, 애플리케이션 분해의 첫번쩨 단계이다. 레이어의 수는 최대 7개로 제한되어 있으며, 일부는 옵셔널하지만 표준화되어 있다. 현재는 아래와 같이 레이어가 구분되어 있다. 각 레이어는 고유의 책임영역이 있고, business-oriented하다.</p>
<ul>
<li><p><strong>App</strong>
애플리케이션 로직이 초기화되는 공간이다. Providers, routers, global styles, global type declarations, 등등 이 여기서 정의된다. 즉 애플리케이션의 진입점 역할을 한다.</p>
</li>
<li><p><strong>Processes(deprecated, optional)</strong>
이 레이어는 여러 페이지에 걸쳐있는 프로세스를 처리한다.(ex, multi-step registration) 이 레이어는 더이상 사용되지 않는다.</p>
</li>
<li><p><strong>Pages</strong>
entities, features, widgets 레이어 로 전체 페이지를 구성하기 위한 구성 레이어이다. 이 레이어는 애플리케이션의 페이지가 포함된다.</p>
</li>
<li><p><strong>Widgets</strong>
entities와 features 레이어를 의미있는 블록으로 결합하는 composition layer이다. 페이지에서 사용되는 독립적인 UI 컴포넌트들이 있다. (ex, IssuesList, UserProfile)</p>
</li>
<li><p><strong>Features(optional)</strong>
이 레이어는 business value를 전달하는 유저 시나리오와 같은 기능을 다룬다. (ex, SendComment, AddToCart, UsersSearch)</p>
</li>
<li><p><strong>Entities(optional)</strong>
이 레이어는 business entities를 나타낸다.(ex, User, Product, Order)</p>
</li>
<li><p><strong>Shared</strong>
이 레이어에는 특정 비즈니스 로직에 종속되지 않은 재사용가능한 컴포넌트와 유틸리티가 포함되어 있다. (ex, UIKit, libs, API)</p>
</li>
</ul>
<p>이러한 레이어들은 코드베이스를 구성하고, 모듈식, 유지보수 가능, 확장 가능한 아키텍쳐를 만드는데 도움이 된다.</p>
<p>FSD의 주요 특징중 하나는 계층적 구조이다. 이 구조에서 entities 레이어는 features 레이어가 더 상위이기 때문에 features 레이어의 기능을 사용할 수 없다. 비슷하게, features 레이어는 widget 이나 processes 레이어의 기능을 사용할 수 없다. 즉, 위 레이어는 아래 레이어만 활용할 수 있는데 이는 한방향으로만 향하는 선형적인 흐름을 유지하기 위한것이다.</p>
<p>따라서 계층구조에서 레이어의 위치가 낮을 수록 코드의 많은 곳에서 사용될 가능성이 높기 때문에 변경하는 것이 위험하다. 예를들어, shared 레이어의 UI-kit는 features, widgets, pages 레이어에서 사용될 수 있다.</p>
<h3 id="slices">Slices</h3>
<p>각 레이어 에는 애플리케이션 분해의 두번째 레벨인 slice라는 하위 디렉토리가 있다. slice에서 연결은 추상적인 것이 아니라, 특정 business entities에 대한 것이다. 즉, 비즈니스 도메인 별로 코드를 분할해서 논리적으로 관련된 모듈을 서로 가깝게 유지하여 코드 베이스를 쉽게 탐색할 수 있다. 슬라이스는 같은 레이어에서 다른 슬라이스를 사용할수 없으므로 응집력이 높고 결합력이 낮다.</p>
<p>slice는 프로젝트의 business 영역에 의해 직접 결정되기때문에 이름이 표준화되어있지 않다. 예를들어, photo gallery를 생각해보면, photo, album, gallery와 같은 섹션이 있을 수 있다. 그리고 SNS는 post, user, newsfeed와 같은 슬라이스가 필요할 것이다.</p>
<p>밀접하게 관련된 부분들은 구조적으로 디렉토리내에서 그룹화되어있지만, 다른 슬라이스와 같은 격리 규칙을 준수해야하며, 이 디렉토리에 있는 코드는 직접적으로 공유되면 안된다.</p>
<h3 id="segments">Segments</h3>
<p>각 슬라이스는 세그먼트로 구성된다. 세그먼트는 기술적 목적에 따라 슬라이스 내의 코드를 나누는데 도움이 되는 작은 모듈이다. 팀의 합의에 따라 세그먼트는 구성과 이름이 결정된다. 가장 일반적인 세그먼트는 ui, model(store, actions), api, lib(utils/hooks) 이다.</p>
<ul>
<li>Api : 서버 요청</li>
<li>UI : 슬라이스의 UI 컴포넌트</li>
<li>Model : 비즈니스 로직, 즉 state와의 상호작용. 예를들어 action, selector가 해당된다.</li>
<li>Lib : 슬라이스 내에서 사용되는 보조 기능</li>
</ul>
<p><strong>FSD 예시(SNS 애플리케이션-글카드)</strong></p>
<ul>
<li>App : routing, store, global styles 설정이 있다.</li>
<li>Pages : 앱의 각 페이지에 대한 라우팅 구성요소가 포함되어 있으며, 대부분 구성이며, 로직은 거의 없다.</li>
<li>Widgets : 백엔드의 관련 호출에 연결되어 있는 content와 interactive 버튼이 조립되어 있는 post card가 있다.</li>
<li>Features : 카드의 상호작용(좋아요 버튼)와 이러한 상호작용을 처리하는 로직을 포함한다.</li>
<li>Entities : 콘텐츠와 인터렉티브 요소를 위한 slot이 있는 card의 shell을 포함한다. 글 작성자를 나타내는 tile도 여기에 있지만 다른 슬라이스의 entities에 있다.</li>
</ul>
<h3 id="public-api">Public API</h3>
<p>각 슬라이스와 세그먼트는 public API를 가진다. public API는 index파일이며, 이 파일을 통해 필요한 기능만 외부로 내부내고 불필요한 기능들은 격리할 수 있다. Index 파일이 진입점 역할을 한다.</p>
<p>Public API는 import와 export로 단순하게 작동하므로 애플리케이션을 변경할때 코드의 모든곳에서 import를 변경할 필요가 없다.</p>
<p><strong>규칙</strong></p>
<ol>
<li><p>Access Control
Public API는 모듈의 콘텐츠에 대한 접근을 제어해야 한다. 애플리케이션의 다른 부분은 public API에 표시된 모듈 엔티티만 접근할 수 있다. 표시되지 않은 모듈 내부부분은 모듈 자체만 접근할 수 있다.</p>
</li>
<li><p>Sustainability for changes
public API는 모듈 내부의 변경사항에 대해 지속가능해야 한다. 즉 내부 구조의 변경이 public API의 변경으로 이어지면 안된다.</p>
</li>
<li><p>Integrability
publicAPI는 쉽고 유연한 통합이 가능해야 한다. 나머지 애플리케이션에서 사용하기 편리해야 한다.</p>
</li>
</ol>
<p>단점</p>
<ul>
<li>대부분의 인기있는 번들러에서, re-exports때문에 code-splitting이 잘 작동하지 못하는데, 이것은 이 접근방식으로 tree-shaking을 하면 모듈의 일부가 아닌 전체 모듈만 버리는 것이 안전하기때문이다. 예를들어, authModel을 page model로 import 하면, AuthForm 컴포넌트 페이지에서 아용되지 않더라도, 페이지 청크안에 들어간다.</li>
<li>결과적으로, 브라우저는 “for the company” 번들에 포함된 모든 모듈을 포함하여 그안에 있는 모든 모듈을 처리해야하므로 청크의 초기화 비용이 많이 든다.</li>
</ul>
<p>해결방법
webpack을 사용하면 re-exports 파일이 부작용이 없는것으로 표시할 수 있으므로 이러한 파일로 작업할때 webpack이 공격적인 최적화를 사용할 수 있다.</p>
<h2 id="deeper-into-architecture">Deeper into architecture</h2>
<p><strong>Abstraction and business logic</strong>
계층이 높은 레이어일수록 특정 business node에 더 종속이 되고, 더많은 business logic이 포함된다. 계층이 낮은 레이어일수록 추상화 수준이 높고, 재사용성이 높고, 레이어 자체의 자율성이 적다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[NextJS] "Error occurred  prerendering page" 해결  (useSearchParams, ReactQuill 등)]]></title>
            <link>https://velog.io/@flip_404/NextJS-Error-occurred-prerendering-page-%ED%95%B4%EA%B2%B0-useSearchParams-ReactQuill-%EB%93%B1</link>
            <guid>https://velog.io/@flip_404/NextJS-Error-occurred-prerendering-page-%ED%95%B4%EA%B2%B0-useSearchParams-ReactQuill-%EB%93%B1</guid>
            <pubDate>Fri, 05 Jul 2024 04:30:43 GMT</pubDate>
            <description><![CDATA[<p>프로젝트 빌드 중에 write 페이지와 post 페이지에서 발생한 prerendering Error를 해결한 과정을 작성합니다.</p>
<p>*<em>Next.js는 서버 사이드와 클라이언트 사이드에서 모두 동작하는 프레임워크입니다. Next.js의 SSR(서버 사이드 렌더링) 구조로 인해, 클라이언트 사이드에서만 사용할 수 있는 기능은 클라이언트에서 렌더링이 완료된 후에 사용해야 합니다.
*</em></p>
<h3 id="post-페이지---usesearchparams-사용-문제"><strong>Post 페이지 - useSearchParams 사용 문제</strong></h3>
<p>post 페이지에서는 useSearchParams 훅을 사용 중에 문제가 발생했습니다. 이 훅은 클라이언트 사이드에서 사용해야 하는데, SSR 과정에서 사용되어 에러가 발생한 것입니다. 이를 해결하기 위해 React의 Suspense를 이용했습니다. Suspense는 비동기 작업이 완료될 때까지 서버가 페이지를 계속 렌더링하도록 하여, 사전 렌더링 과정에서 발생할 수 있는 비동기 작업 관련 문제를 해결합니다. 이를 통해 컴포넌트가 완전히 로드되기 전에 사용자에게 불완전한 페이지가 제공되는 것을 방지할 수 있습니다.</p>
<pre><code class="language-javascript">import { Suspense } from &#39;react&#39;
import PostViewer from &#39;./PostViewer&#39;

function Posts() {
  return (
    &lt;Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
      &lt;PostViewer /&gt;
    &lt;/Suspense&gt;
  )
}
export default Posts</code></pre>
<h3 id="write-페이지---reactquill-사용-문제"><strong>Write 페이지 - ReactQuill 사용 문제</strong></h3>
<p>write 페이지에서는 ReactQuill 라이브러리 사용 중에 문제가 발생했습니다. ReactQuill과 같은 일부 라이브러리는 브라우저 환경에 의존하기 때문에 서버 사이드 렌더링 시 로드하려고 하면 document is not defined와 같은 오류가 발생할 수 있습니다. 이를 해결하기 위해 동적 로딩을 사용하고, ssr: false 옵션을 설정했습니다. 이렇게 하면 해당 컴포넌트는 클라이언트 사이드에서만 로드됩니다.</p>
<pre><code class="language-javascript">const ReactQuill = dynamic(() =&gt; import(&#39;react-quill&#39;), {
  ssr: false,
})</code></pre>
<p>document, window는 클라이언트 측에서만 정의된 전역 변수입니다. document is not defined 및 window is not defined 에러가 발생한다면, 서버에서 해당 객체를 참조하고 있는 것은 아닌지 확인해봐야 합니다. </p>
<h3 id="결론">결론</h3>
<p>Next.js의 서버 사이드 렌더링 환경에서 클라이언트 사이드 전용 기능을 사용할 때는 주의가 필요합니다. Suspense를 사용하여 비동기 작업이 완료될 때까지 렌더링을 지연시키거나, 동적 로딩과 ssr: false 옵션을 사용하여 클라이언트 사이드에서만 컴포넌트를 로드함으로써 이러한 문제를 해결할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[타입스크립트] 브라우저별 동영상 지원을 위한 인터페이스 확장]]></title>
            <link>https://velog.io/@flip_404/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EB%B3%84-%EB%8F%99%EC%98%81%EC%83%81-%EC%A7%80%EC%9B%90%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%ED%99%95%EC%9E%A5</link>
            <guid>https://velog.io/@flip_404/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EB%B3%84-%EB%8F%99%EC%98%81%EC%83%81-%EC%A7%80%EC%9B%90%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%ED%99%95%EC%9E%A5</guid>
            <pubDate>Fri, 04 Aug 2023 07:09:35 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-typescript">    const handleFullscreen = () =&gt; {
        const videoElement = videoRef.current!;

        if (videoElement.requestFullscreen) {
            videoElement.requestFullscreen();
        } else if (videoElement.mozRequestFullScreen) {
            videoElement.mozRequestFullScreen();
        } else if (videoElement.webkitRequestFullscreen) {
            videoElement.webkitRequestFullscreen();
        } else if (videoElement.msRequestFullscreen) {
            videoElement.msRequestFullscreen();
        }
    };</code></pre>
<p>비디오 컴포넌트를 자바스크립트에서 타입스크립트로 전환하던 중, 아래와 같은 에러를 맞이했다.</p>
<pre><code class="language-typescript">Property &#39;msRequestFullscreen&#39; does not exist on type &#39;HTMLVideoElement&#39;.
Property &#39;mozRequestFullScreen&#39; does not exist on type &#39;HTMLVideoElement&#39;.
Property &#39;webkitRequestFullscreen&#39; does not exist on type &#39;HTMLVideoElement&#39;.</code></pre>
<p>이는 HTMLVideoElement에 브라우저별 Property가 정의되어 있지 않기 때문이다.
아래와 같이 HTMLVideoElement 인터페이스를 확장해줌으로써 해결할 수 있다</p>
<pre><code class="language-typescript">declare global {
    interface HTMLElement {
        msRequestFullscreen?: () =&gt; Promise&lt;void&gt;;
        mozRequestFullscreen?: () =&gt; Promise&lt;void&gt;;
        webkitRequestFullscreen?: () =&gt; Promise&lt;void&gt;;
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[API 매개변수를 객체로 묶자]]></title>
            <link>https://velog.io/@flip_404/API-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EB%A5%BC-%EA%B0%9D%EC%B2%B4%EB%A1%9C-%EB%AC%B6%EC%9E%90</link>
            <guid>https://velog.io/@flip_404/API-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EB%A5%BC-%EA%B0%9D%EC%B2%B4%EB%A1%9C-%EB%AC%B6%EC%9E%90</guid>
            <pubDate>Thu, 03 Aug 2023 07:52:11 GMT</pubDate>
            <description><![CDATA[<p>API 매개변수를 객체로 묶었을 때의 장점을 정리해봤다.</p>
<pre><code class="language-javascript">export const getKeywordNews = async (memberKeywordId, pageParam, size ) =&gt; {} (before)
export const getKeywordNews = async ({memberKeywordId, pageParam, size} ) =&gt; {} (after)
</code></pre>
<p>이렇게 작성하면 아래와 같은 장점이 있다.</p>
<p>가독성: 객체 매개변수를 사용하면 함수 호출이 더 가독성 있을 수 있다. 특히, 여러 개의 선택적 매개변수가 있는 경우, 매개변수의 목적을 키 기반으로 쉽게 파악할 수 있다.</p>
<p>(❗️)매개변수 순서 변경에 대한 영향 최소화: 함수의 매개변수 순서가 바뀌더라도, 객체 매개변수를 사용하면 명시적으로 키를 지정해주기 때문에 영향이 최소화된다.</p>
<p>(❗️❗️)선택적 매개변수 지원: 객체 매개변수를 사용하면 매개변수를 선택적으로 만들 수 있다. 각 매개변수에 기본 값이 있거나, 필요한 매개변수만 전달할 수 있으므로 함수를 더 유연하게 사용할 수 있다.</p>
<p>(❗️❗️)코드 자동 완성 및 도움말: 객체 매개변수를 사용하면 IDE에서 해당 매개변수의 필드를 자동 완성하고 도움말을 표시하는 기능을 더 쉽게 사용할 수 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS 파헤치기]]]></title>
            <link>https://velog.io/@flip_404/CS-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0</link>
            <guid>https://velog.io/@flip_404/CS-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0</guid>
            <pubDate>Sat, 05 Nov 2022 18:06:35 GMT</pubDate>
            <description><![CDATA[<h2 id="1-프로그래밍-공통">1. 프로그래밍 공통</h2>
<p>[ OOP란 ]
OOP(Object Oriented Programming)는 현실 세계를 프로그래밍으로 옮겨와 현실 세계의 사물들을 객체로 보고, 그 <strong>객체로부터 개발하고자 하는 특징과 기능을 뽑아와 프로그래밍</strong>하는 기법입니다. OOP로 코드를 작성하면 <strong>재사용성과 변형가능성</strong>을 높일 수 있습니다.</p>
<p>[ OOP의 5가지 설계 원칙 ]
SRP(Single Responsibility Principle, 단일 책임 원칙): 클래스는 단 하나의 목적을 가져야 하며, 클래스를 변경하는 이유는 단 하나의 이유여야 한다.
OCP(Open-Closed Principle, 개방 폐쇠 원칙): 클래스는 확장에는 열려 있고, 변경에는 닫혀 있어야 한다.
LSP(Liskov Substitution Principle, 리스코프 치환 원칙): 상위 타입의 객체를 하위 타입으로 바꾸어도 프로그램은 일관되게 동작해야 한다.
ISP(Interface Segregation Principle, 인터페이스 분리 원칙): 클라이언트는 이용하지 않는 메소드에 의존하지 않도록 인터페이스를 분리해야 한다.
DIP(Dependency Inversion Principle, 의존 역전 법칙): 클라이언트는 추상화(인터페이스)에 의존해야 하며, 구체화(구현된 클래스)에 의존해선 안된다.</p>
<p>[ 절차지향 프로그래밍 VS 객체지향 프로그래밍 ]
<strong>절차지향 프로그래밍</strong>
물이 위에서 아래로 흐르는 것처럼 순차적인 처리를 중요시하는 프로그래밍 기법이다.
가장 대표적인 언어로 C언어가 있다.
컴퓨터의 처리구조와 유사해 실행속도가 빠르다.
코드의 순서가 바뀌면 동일한 결과를 보장하기 어렵다.</p>
<p><strong>객체지향 프로그래밍</strong>
실제 세계의 사물들을 객체로 모델링하여 개발을 진행하는 프로그래밍 기법
가장 대표적인 언어로 Java가 있다.
캡슐화, 상속, 다형성 등과 같은 기법을 이용할 수 있다. 다형성은 동일한 키보드의 키가 다른 역할을 하는 것처럼 하나의 메소드나 클래스가 다양한 방법으로 동작하는 것을 의미한다.
절치지향 언어보다 실행속도가 느리다.</p>
<p>[ RESTful API ]
REST(REpresentational State Transfer)ful API는 HTTP 통신에서 어떤 자원에 대한 CRUD 요청을 Resource와 Method로 표현하여 특정한 형태로 전달하는 방식입니다. RESTful API는 아래와 같은 것들로 구성됩니다.</p>
<p>Resource(자원, URI)
Method(요청 방식, GET or POST 등)
Representation of Resource(자원의 형태, JSON or XML 등)</p>
<p>[ 함수형 프로그래밍 ]
함수평 프로그래밍의 가장 큰 특징은 immutable data와 first class citizen으로서의 함수입니다. 함수형 프로그래밍은 부수효과가 없는 순수 함수를 이용하여 프로그램을 만드는 것이다. 부수 효과가 없는 순수 함수란 데이터의 값을 변경시키지 않으며 객체의 필드를 설정하는 등의 작업을 하지 않는 함수를 의미합니다.</p>
<p>[ 메모리 구조 ]
코드, 데이터, 힙, 스택</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/8c02e001-cc52-487c-b1f2-076b42c19c61/image.png" alt=""></p>
<p>코드 영역: 실행할 프로그램의 코드가 저장되는 영역으로 텍스트 영역이라고도 부릅니다. 사용자가 프로그램 실행 명령을 내리면 OS가 HDD에서 메모리로 실행 코드를 올리게 되고, CPU는 코드 영역에 저장된 명령어를 하나씩 처리하게 됩니다.</p>
<p>데이터 영역: 프로그램의 전역 변수(global)와 정적 변수(static)가 저장되는 영역입니다. 데이터 영역은 프로그램의 시작과 함께 할당되며, 프로그램이 종료되면 소멸합니다.</p>
<p>힙 영역: 프로그래머가 직접 관리할 수 있는 메모리 영역으로 이 공간에 메모리를 할당하는 것을 동적 할당이라고 부릅니다. Java에서는 가비지 컬렉터가 자동으로 해제해줍니다. 힙 영역은 스택 영역과 달리 낮은 주소에서 높은 주소로 메모리가 할당됩니다.</p>
<p>스택 영역: 함수의 호출과 함께 할당되며 지역 변수와 매개 변수가 저장되는 영역입니다. 스택 영역에 저장되는 함수의 호출 정보를 스택프레임이라고 합니다. 스택 영역은 함수의 호출이 완료되면 소멸합니다. 스택 영역은 높은 주소에서 낮은 주소로 메모리가 할당됩니다.</p>
<p>[ Parameter와 Argument의 차이 ]
Parameter: 함수를 선언할 때 사용된 변수
Argument: 함수가 호출되었을 때 함수의 파라미터로 전달된 실제 값</p>
<p>[ Call By Value와 Call By Reference 차이 ]
<strong>Call By Value</strong>
인자로 받은 값을 복사하여 처리하는 방식
Call By Value에 의해 넘어온 값을 증가시켜도 원래의 값이 보존된다.
값을 복사하여 넘기기 때문에 메모리 사용량이 늘어난다.</p>
<p><strong>Call By Reference</strong>
인자로 받은 값의 주소를 참조하여 직접 값에 영향을 주는 방식
<strong>값을 복사하지 않고 직접 참조하기 때문에 속도가 빠르다.</strong>
<strong>원래의 값에 영향을 주는 리스크가 존재한다.</strong></p>
<p>예를 들어 아래와 같은 코드가 있다고 할 때, a라는 새로운 변수가 생성되어 Call By Value로 전달되기 때문에 메모리를 많이 사용하지만 a를 변경하여도 원래 값인 f는 영향을 받지 않습니다
<img src="https://velog.velcdn.com/images/flip_404/post/f1bd1d53-20db-4e88-9999-59baed0366e7/image.png" alt="">
<img src="https://velog.velcdn.com/images/flip_404/post/e504fc98-ecb9-479d-97f7-e4394a900879/image.png" alt=""></p>
<p>[ 프레임워크와 라이브러리 차이 ]
라이브러리: <strong>사용자가 흐름에 대한 제어</strong>를 하며 필요한 상황에 가져다가 쓸 수 있다.
프레임워크: 전체적인 흐름을 <strong>자체적으로 제어</strong>한다.</p>
<p>프레임워크와 라이브러리는 실행 흐름에 대한 제어 권한이 어디 있는지에 따라 달라집니다. <strong>프레임워크</strong>를 사용하면 사용자가 관리해야 하는 부분을 프레임워크에 넘김으로써 신경써야 할 것을 줄이는 <strong>제어의 역전</strong>(IoC, Inversion Of Control)이 적용됩니다.</p>
<p>[ 병렬 처리 프레임워크의 종류와 특징 ]
<strong>Hadoop</strong>
<strong>HDFS(Hadoop Distributed File System)를 활용</strong>해 데이터를 주고 받는다.
<strong>데이터가 여러 노드에 분산되어 저장되기 때문에 손실의 우려가 없다는 장점</strong>이 있다.
하지만 <strong>File I/O를 기반으로 작동하기 때문에 처리 속도가 느리다.</strong></p>
<p><strong>Spark</strong>
<strong>In-Memory 상 에서 데이터를 주고받고 연산을 수행</strong>한다.
메모리를 사용해 데이터를 처리하기 때문에 <strong>Hadoop보다 속도가 약 100배 정도 빠르다.</strong>
하지만 <strong>메모리상에서 처리하기 때문에 장애가 발생한 경우 응용 프로그램을 처음부터 다시 시작해야 한다.</strong></p>
<p>[ 동기와 비동기의 차이 ]
<strong>동기(Synchronous) 방식</strong>
요청을 보내고 실행이 끝나면 다음 동작을 처리하는 방식
순서에 맞추어 진행되기 때문에 제어하기 쉽다.
<strong>여러가지 요청을 동시에 처리할 수 없어 효율이 떨어진다.</strong>
동기 방식의 예시로는 콜센터 종업원이 일을 처리하는 방식이 될 수 있다. 콜센터의 직원은 한 손님의 전화 응대가 끝난 후에 다음 손님의 응대를 진행할 수 있다.</p>
<p><strong>비동기(Asynchronous) 방식</strong>
<strong>요청을 보내고 해당 동작의 처리 여부와 상관없이 다음 요청이 동작</strong>하는 방식
작업이 완료되는 시간을 기다릴 필요가 없기 때문에 자원을 효율적으로 사용할 수 있다.
작업이 완료된 결과를 제어하기 어렵다.
비동기 방식의 예제로는 이메일이 있다. 우리는 한 사람에게 이메일을 보냈을 때 답변을 받지 않고도 이메일을 다시 보낼 수 있다. </p>
<p>[ SQL Injection ]
SQL Injection이란 공격자가 악의적인 의도를 갖는 구문을 삽입하여 공격자가 원하는 SQL을 실행하도록 하는 웹해킹기법입니다. 예를 들어 아래와 같은 간단한 SQL 문이 있을 때 INPUT1에 &#39;OR 1=1--을 넣는 것입니다.</p>
<pre><code class="language-mysql">SELECT * FROM USER WHERE ID = &#39;INPUT1&#39; AND PASSWORD = &#39;INPUT2&#39;

SELECT * FROM USER WHERE ID = &#39;&#39; OR 1=1 --INPUT1&#39; AND PASSWORD = &#39;INPUT2&#39;</code></pre>
<p>INPUT1으로 &#39;OR 1=1--을 넣으면 보이는 것처럼 뒤의 내용은 주석처리가 되고 WHERE 문은 항상 참이 됩니다.</p>
<p>이러한 공격을 방지하기 위해 특수문자 및 SQL 예약어들을 필터링하거나 SQL 오류 메세지를 노출하지 않는 등의 방법을 취해야 합니다.</p>
<ol>
<li>프로그래밍 공통 - 고급
[ 메세지 큐(Message Queue)란? ]</li>
</ol>
<p><strong>메세지 큐(Message Queue)란 Queue 자료구조를 이용하여 데이터(메세지)를 관리하는 시스템</strong>으로, <strong>비동기 통신 프로토콜</strong>을 제공하여 메세지를 빠르게 주고 받을 수 있게 해준다. 메세지 큐에서는 Producer(생산자)가 Message를 Queue에 넣어두면, Consumer가 Message를 가져와 처리하게 된다. 메세지 큐에는 Kafka, Rabbit MQ, AMPQ 등이 있다.</p>
<p>[ Docker(도커)와 Kubernates(쿠버네티스) ]
<strong>Docker는 컨테이너 기반의 가상화 기술</strong>입니다. 기존에는 하드웨어를 가상화하였기 때문에 Host OS 위에 Guest OS를 설치해야 했습니다. 하지만 이러한 방식은 상당히 무겁고 느려 한계가 많이 있었습니다.</p>
<p>그래서 이를 극복하고자 프로세스를 격리시킨 컨테이너를 통해 가상화를 하는 Docker(도커)와 같은 기술들이 등장하게 되었고, 도커를 통해 구동되는 컨테이너를 관리하기 위한 Kubernates(쿠버네티스)가 등장하게 되었습니다.</p>
<p>[ Docker(도커)의 장/단점 ]
<strong>장점</strong>
쉽고 빠른 실행 환경 구축
하드웨어 자원 절감
Docker Hub와 같은 공유 환경 제공</p>
<p><strong>단점</strong>
개발 초기의 오버헤드
Linux 친화적</p>
<p>[ TDD(Test-Driven Development) ]
<strong>TDD(Test-Driven Development)는 매우 짧은 개발 사이클의 반복에 의존하는 개발 프로세스</strong>로, 개발자는 우선 <strong>요구되는 기능에 대한 테스트케이스를 작성</strong>하고, <strong>그에 맞는 코드를 작성하여 테스트를 통과한 후에 상황에 맞게 리팩토링하는 테스트 주도 개발 방식</strong>을 의미합니다.</p>
<p>개발자는 테스트를 작성하기 위해 해당 기능의 요구사항을 확실히 이해해야 하기 때문에 개발 전에 요구사항에 집중할 수 있도록 도와주지만 테스트를 위한 진입 장벽과 작성해야 하는 코드의 증가는 단점으로 뽑힙니다.</p>
<p>[ DDD(Domain-Driven Design) ]
DDD(Domain-Driven Design)는 실세계에서 사건이 발생하는 집합인 Domain(도메인)을 중심으로 설계하는 방법입니다. 옷 쇼핑몰을 예로 들면 손님들이 주문하는 도메인, 점주들이 관리하는 도메인 등이 있을 수 있습니다. 이러한 도메인들이 서로 상호작용하며 설계하는 것이 도메인 주도 설계입니다. 도메인 주도 설계에서 도메인은 각각 분리되어 있는데, 이러한 관점에서 MSA(MicroService Architecture)를 적용하면 용이한 설계를 할 수 있습니다. DDD에서는 같은 객체들이 존재할 수 있는데, 예를 들어 옷 구매자의 입장에서는 (name, price)와 같은 객체 정보를 담지만, 판매자의 입장에서는(madeTie, size, madeCountry) 등이 있을 수 있습니다. 즉, <strong>문맥에 따라 객체의 역할이 바뀔 수 있는 것이 DDD</strong>입니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/44467401-9da5-43cb-a2ff-1970c9b41045/image.png" alt=""></p>
<p>[ MSA란? ]
MSA(Microservice Architecture)는 모든 시스템의 구성요소가 한 프로젝트에 통합되어 있는 Monolithic Architecture(모놀리식 아키텍쳐)의 한계점을 극복하고자 등장하게 되었습니다. MSA는 1개의 시스템을 독립접으로 배포가능한 각각의 서비스로 분할합니다. 각각의 서비스는 API를 통해 데이터를 주고받으며 1개의 큰 서비스를 구성합니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/42e65d4b-3e48-4780-9501-f74fab5c677e/image.png" alt=""></p>
<p><strong>장점</strong>
일부 서비스에 장애가 발생하여도 전체 서비스에 장애가 발생하지 않는다.
각각의 서비스들은 서로 다른 언어와 프레임워크로 구성될 수 있다.
서비스의 확장이 용이하다.</p>
<p><strong>단점</strong>
서비스가 분리되어 있어, 테스팅이나 트랜잭션 처리 등이 어렵다.
서비스 간에 API로 통신하기 때문에 그에 대한 비용이 발생한다.
서비스 간의 호출이 연속적이기 때문에 디버깅 및 에러 트레이싱이 어렵다.</p>
<h2 id="2-자료구조">2. 자료구조</h2>
<p>[ 자료구조와 알고리즘 ]
자료구조는 데이터를 원하는 규칙 또는 목적에 맞게 저장하기 위한 구조이고, 알고리즘이란 자료구조에 쌓인 데이터를 활용해 어떠한 문제를 해결하기 위한 여러 동작들의 모임입니다.</p>
<p>[ 스택, 큐, 트리, 힙 구조 설명 ]
스택: 세로로 된 바구니와 같은 구조로 먼저 넣게 되는 자료가 마지막으로 나오게 되는 First-In Last-Out(FILO) 구조이다.
큐: 가로로 된 통과 같은 구조로 먼저 넣게 되는 자료가 가장 먼저 나오는 First-In First-Out(FIFO) 구조이다.
트리: 정점과 간선을 이용해 사이클을 이루지 않도록 구성한 Graph의 특수한 형태로, 계층이 있는 데이터를 표현하기에 적합하다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/ca0769d2-20d0-4e05-a60b-42bcad47b597/image.png" alt=""></p>
<p> 우선순위 큐와 내부 구조 및 시간복잡도 ]
우선순위큐는 가장 우선순위가 높은 데이터를 먼저 꺼내기 위해 고안된 자료구조입니다. 우선순위 큐를 구현하기 위해서 일반적으로 힙을 사용합니다. 힙은 완전이진트리를 통해서 구현되었기 때문에 우선순위 큐의 시간복잡도는 O(logn)입니다.</p>
<p>[ 해시 테이블와 해시 테이블의 시간 복잡도 ]
해시 테이블은 (Key, Value)로 데이터를 저장하는 자료구조 중 하나로 빠른 데이터 검색이 필요할 때 유용합니다. 해시 테이블은 Key값에 해시함수를 적용해 고유한 index를 생성하여 그 index에 저장된 값을 꺼내오는 구조입니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/51f29bbe-7de4-4d3e-9e39-69facaa1e75b/image.png" alt=""></p>
<p>해시 테이블은 고유한 index로 값을 조회하기 때문에 평균적으로 O(1)의 시간복잡도를 갖습니다. 하지만 해시의 index값이 충돌이 발생한 경우 충돌된 index값에 대해 연결된 데이터들을 조회하여 원하는 값을 조회하기 때문에 O(N)까지 증가할 수 있습니다.</p>
<p>[ LinkedList와 ArrayList 차이 ]
ArrayList는 데이터들이 순서대로 늘어선 배열의 형식을 취하고 있지만, LinkedList는 자료의 주소값으로 서로 연결된 형식을 가지고 있습니다. 이러한 구조에 의해 둘은 각각의 장단점을 가지고 있습니다.</p>
<p><strong>ArrayList</strong>
원하는 데이터에 무작위로 접근할 수 있다.
리스트의 크기가 제한되어 있으며, 리스트의 크기를 재조정하는 것은 많은 연산이 필요하다.
데이터의 추가/삭제를 위해서는 임시 배열을 생성하여 복제하고 있어 시간이 오래 걸린다.</p>
<p><strong>LinkedList</strong>
리스트의 크기에 영향 없이 데이터를 추가할 수 있다.
데이터를 추가하기 위해 새로운 노드를 생성하여 연결하므로 추가/삭제 연산이 빠르다.
무작위 접근이 불가능하며, 순차 접근만이 가능하다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/65041a42-656c-44f6-a06c-303ecd921016/image.png" alt=""></p>
<p>[ 큐와 스택의 구현 ]
큐(Queue): Array로 구현하면 poll 연산 이후 객체를 앞당기는 작업이 필요하다. 하지만 List로 구현하면 객체 1개만 제거하면 되므로 삽입 및 삭제가 용이한 LinkedList로 구현하는 것이 좋다.
스택(Stack): List로 구현하면 객체를 제거하는 작업이 필요하다. 하지만 Array로 구현하면 삭제할 필요 없이 index를 줄이고 초기화만 하면 되므로, Array로 구현하는 것이 좋다.</p>
<ol start="2">
<li>자료구조 - 고급
[ AVL 트리 ]
AVL 트리란 한 쪽으로 값이 치우치는 이진 탐색 트리(Binary Search Tree, BST)의 한계점을 보완하기 위해 만들어진 균형 잡힌 이진 트리입니다. AVL은 항상 좌/우로 데이터를 균형잡힌 상태로 유지하기 위해 추가적인 연산을 진행합니다.</li>
</ol>
<p>[ 레드블랙 트리 ]
레드블랙 트리는 모든 노드를 빨간색 또는 검은색으로 색칠합니다. 그리고 연결된 노드들은 색이 중복되지 않도록 관리됩니다.</p>
<h2 id="3-알고리즘">3. 알고리즘</h2>
<p>[ 버블소트, 힙소트, 머지소트, 퀵소트, 삽입소트 ]</p>
<p><strong>버블소트</strong>
서로 인접한 두 원소를 비교하여 정렬하는 알고리즘입니다. 0번 인덱스부터 n-1번 인덱스까지 n번까지의 모든 인덱스를 비교하며 정렬합니다. 시간복잡도는 𝑂(𝑛2) 입니다.</p>
<p><strong>힙소트</strong>
주어진 데이터를 힙 자료구조로 만들어 최대값 또는 최소값부터 하나씩 꺼내서 정렬하는 알고리즘입니다. 힙소트가 가장 유용한 경우는 전체를 정렬하는 것이 아니라 가장 큰 값 몇개만을 필요로 하는 경우입니다. 시간복잡도는 𝑂(𝑛𝑙𝑜𝑔2𝑛) 입니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/8b1664bf-44e7-4f0e-ab29-6a7813c7f7d6/image.png" alt=""></p>
<p><strong>머지소트</strong>
주어진 배열을 크기가 1인 배열로 분할하고 합병하면서 정렬을 진행하는 분할/정복 알고리즘입니다. 시간복잡도는 𝑂(𝑛𝑙𝑜𝑔2𝑛) 입니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/a23c61d3-5266-48d9-aa21-74776bc73010/image.png" alt=""></p>
<p><strong>퀵소트</strong>
매우 빠른 정렬 속도를 자랑하는 분할 정복 알고리즘 중 하나로 합병정렬과 달리 리스트를 비균등하게 분할합니다. 피봇을 설정하고 피봇보다 큰값과 작은값으로 분할하여 정렬을 합니다. 시간복잡도는 𝑂(𝑛𝑙𝑜𝑔2𝑛) 이며 리스트가 계속해서 불균등하게 나눠지는 경우 시간복잡도가 𝑂(𝑛2) 까지 나빠질 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/d5ceb28f-b9b3-4afa-b858-a0b4f13cef60/image.png" alt=""></p>
<p><strong>삽입정렬</strong>
두 번째 값부터 시작하여 그 앞에 존재하는 원소들과 비교하여 삽입할 위치를 찾아 삽입하는 정렬 알고리즘입니다. 삽입 정렬의 평균 시간복잡도는 𝑂(𝑛2) 이며, 가장 빠른 경우 𝑂(𝑛) 까지 높아질 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/58cc4c71-1bf9-491a-9f8d-959504dff50c/image.png" alt=""></p>
<p>[ 정렬 알고리즘 시간복잡도</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/1f0bb357-117b-4de9-8124-d0a1ab3080e2/image.png" alt=""></p>
<p>[ 동적 프로그래밍(Dynamic Programming)이란? ]
동적 프로그래밍(Dynamic Programming) 이란 주어진 문제를 풀기 위해서, 문제를 여러 개의 하위 문제(subproblem)로 나누어 푼 다음, 그것을 결합하여 해결하는 방식입니다. 동적 프로그래밍에서는 어떤 부분 문제가 다른 문제들을 해결하는데 사용될 수 있어, 답을 여러 번 계산하는 대신 한 번만 계산하고 그 결과를 재활용하는 메모이제이션(Memoization) 기법으로 속도를 향상 시킬 수 있습니다. </p>
<p>[ 동적 프로그래밍(Dynamic Programming)의 두 가지 조건 ]
동적 프로그래밍(Dynamic Programming)으로 문제를 해결하기 위해서는 주어진 문제가 다음의 조건을 만족해야 한다</p>
<p>Overlapping Subproblem(중복되는 부분문제): 주어진 문제는 같은 부분 문제가 여러번 재사용된다.
Optimal Substructure(최적 부분구조): 새로운 부분 문제의 정답을 다른 부분 문제의 정답으로부터 구할 수 있다.</p>
<p>[ 허프만 코딩이란 ]
허프만 코딩은 문자의 빈도를 이용해 압축하는 방법으로 빈도가 높은 문자에 짧은 코드를 부여합니다. 허프만 코드는 접두부 코드와 최적 코드를 사용합니다.</p>
<p>접두부 코드: 문자에 부여된 코드가 다른 이진 코드의 접두부가 되지 않는 코드
최적코드: 인코딩된 메세지의 길이가 가장 짧은 코드</p>
<h2 id="4네트워크">4.네트워크</h2>
<p>[ 웹 동작 방식 ]
<img src="https://velog.velcdn.com/images/flip_404/post/62f67d57-36b4-45f7-b255-096036bafb8e/image.png" alt=""></p>
<ol>
<li>사용자가 브라우저에 URL을 입력</li>
<li>브라우저는 DNS를 통해 서버의 진짜 주소를 찾음</li>
<li>HTTP 프로토콜을 사용하여 HTTP 요청 메세지를 생성함</li>
<li>TCP/IP 연결을 통해 HTTP요청이 서버로 전송됨</li>
<li>서버는 HTTP 프로토콜을 활용해 HTTP 응답 메세지를 생성함</li>
<li>TCP/IP 연결을 통해 요청한 컴퓨터로 전송</li>
<li>도착한 HTTP 응답 메세지는 웹페이지 데이터로 변환되고, 웹 브라우저에 의해 출력되어 사용자가 볼 수 있게 됨</li>
</ol>
<p>[ TCP와 UDP 차이 ]
TCP는 연결형 서비스로 3-way handshaking 과정을 통해 연결을 설정합니다. 그렇기 때문에 높은 신뢰성을 보장하지만 속도가 비교적 느리다는 단점이 있습니다. UDP는 비연결형 서비스로 3-way handshaking을 사용하지 않기 때문에 신뢰성이 떨어지는 단점이 있습니다. 하지만 수신 여부를 확인하지 않기 때문에 속도가 빠릅니다. <strong>TCP는 신뢰성이 중요한 파일 교환과 같은 경우</strong>에 쓰이고 <strong>UDP는 실시간성이 중요한 스트리밍</strong>에 자주 사용됩니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/bdebe1e5-485b-4f56-bf00-4698b83f628b/image.png" alt=""></p>
<p>[ 3-way handshaking vs 4-way handshaking ]
<a href="https://mindnet.tistory.com/entry/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-22%ED%8E%B8-TCP-3-WayHandshake-4-WayHandshake">https://mindnet.tistory.com/entry/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-22%ED%8E%B8-TCP-3-WayHandshake-4-WayHandshake</a></p>
<p><strong>TCP 3-way Handshake</strong>
TCP는 장치들 사이에 논리적인 접속을 성립(establish)하기 위하여 three-way handshake를 사용한다.
TCP 3 Way Handshake는 TCP/IP프로토콜을 이용해서 통신을 하는 응용프로그램이 데이터를 전송하기 전에 </p>
<p>먼저 정확한 전송을 보장하기 위해 상대방 컴퓨터와 사전에 세션을 수립하는 과정을 의미한다..</p>
<p>Client &gt; Server : TCP SYN
Server &gt; Client : TCP SYN ACK
Client &gt; Server : TCP ACK</p>
<p>여기서 SYN은 &#39;synchronize sequence numbers&#39;, 그리고 ACK는&#39;acknowledgment&#39; 의 약자이다.
이러한 절차는 TCP 접속을 성공적으로 성립하기 위하여 반드시 필요하다.</p>
<p><strong>TCP의 3-way Handshaking 역할</strong></p>
<p>양쪽 모두 데이타를 전송할 준비가 되었다는 것을 보장하고, 실제로 데이타 전달이 시작하기전에 한쪽이 다른 쪽이 준비되었다는 것을 알수 있도록 한다.
양쪽 모두 상대편에 대한 초기 순차일련변호를 얻을 수 있도록 한다. </p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/de628527-44ab-4921-bb8e-4b5fea5c688f/image.png" alt=""></p>
<ul>
<li>TCP의 3-way Handshaking 과정</li>
</ul>
<p>[STEP 1]
A클라이언트는 B서버에 접속을 요청하는 SYN 패킷을 보낸다. 이때 A클라이언트는 SYN 을 보내고 SYN/ACK 응답을 기다리는SYN_SENT 상태가 되는 것이다.</p>
<p>[STEP 2] 
B서버는 SYN요청을 받고 A클라이언트에게 요청을 수락한다는 ACK 와 SYN flag 가 설정된 패킷을 발송하고 A가 다시 ACK으로 응답하기를 기다린다. 이때 B서버는 SYN_RECEIVED 상태가 된다.</p>
<p>[STEP 3]
A클라이언트는 B서버에게 ACK을 보내고 이후로부터는 연결이 이루어지고 데이터가 오가게 되는것이다. 이때의 B서버 상태가 ESTABLISHED 이다.
위와 같은 방식으로 통신하는것이 신뢰성 있는 연결을 맺어 준다는 TCP의 3 Way handshake 방식이다.</p>
<p><strong>4-way Handshaking</strong></p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/6cf27acc-4418-4354-8b0d-5c08931de1eb/image.png" alt=""></p>
<p>TCP의 4-way Handshaking 과정</p>
<p><strong>3-Way handshake는 TCP의 연결을 초기화 할 때 사용한다면, 4-Way handshake는 세션을 종료하기 위해 수행되는 절차이다.</strong></p>
<p>[STEP 1]
클라이언트가 연결을 종료하겠다는 FIN플래그를 전송한다.</p>
<p>[STEP 2] 
서버는 일단 확인메시지를 보내고 자신의 통신이 끝날때까지 기다리는데 이 상태가 TIME_WAIT상태다.</p>
<p>[STEP 3]
서버가 통신이 끝났으면 연결이 종료되었다고 클라이언트에게 FIN플래그를 전송한다.</p>
<p>[STEP 4]
클라이언트는 확인했다는 메시지를 보낸다.</p>
<p>그런데 만약 &quot;Server에서 FIN을 전송하기 전에 전송한 패킷이 Routing 지연이나 패킷 유실로 인한 재전송 등으로 인해 FIN패킷보다 늦게 도착하는 상황&quot;이 발생한다면 어떻게 될까요? </p>
<p>Client에서 세션을 종료시킨 후 뒤늦게 도착하는 패킷이 있다면 이 패킷은 Drop되고 데이터는 유실될 것입니다. </p>
<p>이러한 현상에 대비하여 Client는 Server로부터 FIN을 수신하더라도 일정시간(디폴트 240초) 동안 세션을 남겨놓고 잉여 패킷을 기다리는 과정을 거치게 되는데 이 과정을 &quot;TIME_WAIT&quot; 라고 합니다.</p>
<p>[ GET과 POST 차이 ]
<strong>GET은 데이터를 조회하기 위해 사용되는 방식</strong>으로 <strong>데이터를 헤더에 추가하여 전송</strong>하는 방식입니다. URL에 데이터가 노출되기 때문에 보안적으로 중요한 데이터를 포함해서는 안됩니다.</p>
<p><strong>POST는 데이터를 추가 또는 수정하기 위해 사용되는 방식</strong>으로 <strong>데이터를 바디에 추가하여 전송</strong>하는 방식입니다. 완전히 안전하다는 것은 아니지만 URL에 데이터가 노출되지 않아 GET보다는 안전합니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/07222692-8c66-4722-b0a9-3e90ea78dce9/image.png" alt=""></p>
<p>[ 공인 IP와 사설 IP 차이 ]</p>
<p>공인 IP
전세계에서 유일한 IP로 ISP(인터넷 서비스 공급자)가 제공하는 IP주소
외부에 공개되어 있기 때문에 인터넷에 연결된 다른 장비로부터 접근이 가능하다.
그에 따라 방화벽 등과 같은 보안 설정을 해주어야 한다.</p>
<p>사설 IP
어떤 네트워크 안에서 사용되는 IP주소
IPV4의 부족으로 인해 모든 네트워크가 공인 IP를 사용하는 것이 불가능하기 때문에 네트워크 안에서 라우터를 통해 <strong>할당받는 가상의 주소이다.</strong>
<strong>별도의 설정 없이는 외부에서 접근이 불가능</strong>하다.</p>
<p>[ 웹 접근성의 국제표준 ]</p>
<p>웹 접근성을 높이기 위해 고안된 웹 표준은 웹에서 표준적으로 사용되는 기술이나 규칙을 의미합니다. 웹 표준을 정하기 위하 W3C(World Wide Web Consortium)이 설립되었으며 웹 표준으로 구조 언어인 HTML, 표현 언어인 CSS, 동작 언어인 Script를 지정하였습니다.</p>
<p>[ OSI 7계층 ]</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/02010083-f91a-4f17-af2f-eec7e7454190/image.png" alt=""></p>
<p>7 계층(응용 계층): 사용자와 직접 상호작용하는 응용 프로그램들이 포함된 계층
6 계층(표현 계층): 데이터의 형식(Format)을 정의하는 계층
5 계층(세션 계층): 컴퓨터끼리 통신을 하기 위해 세션을 만드는 계층
4 계층(전송 계층): 최종 수신 프로세스로 데이터의 전송을 담당하는 계층
3 계층(네트워크 계층): 패킷을 목적지까지 가장 빠른 길로 전송하기 위한 계층
2 계층(데이터링크 계층): 데이터의 물리적인 전송과 에러 검출, 흐름 제어를 담당하는 계층
1 계층(물리 계층): 데이터를 전기 신호로 바꾸어주는 계층</p>
<p>[ HTTP 프로토콜이란? ]
HTTP(Hyper Text Transfer Protocal)이란 서버/클라이언트 모델을 따라 데이터를 주고 받기 위한 프로토콜입니다. HTTP는 애플리케이션 레벨의 프로토콜로 TCP/IP 위에서 작동합니다. HTTP는 상태를 가지고 있지 않는 Stateless 프로토콜이며 Method, Path, Version, Headers, Body 등으로 구성됩니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/26248928-2f1f-44dd-a7b0-4c5240f33771/image.png" alt=""></p>
<p>[ HTTP vs HTTPS ]
HTTP는 평문 데이터를 전송하는 프로토콜이기 때문에, HTTP로 비밀번호나 주민번호 등을 주고 받으면 제3자에 의해 조회될 수 있습니다. 이러한 문제를 해결하기 위해 HTTP에 암호화가 추가된 프로토콜이 HTTPS입니다. HTTPS에는 대칭키 암호화와 비대칭키 암호화가 모두 사용됩니다. 비대칭키 암/복호화는 비용이 매우 크기 때문에 서버와 클라이언트가 주고받는 모든 메세지를 비대칭키로 암호화하면 오버헤드가 발생할 수 있습니다. 그래서 서버와 클라이언트가 최초 1회로 서로 대칭키를 공유하기 위한 과정에서 비대칭키 암호화를 사용하고, 이후에 메세지를 주고 받을 때에는 대칭키 암호화를 사용합니다. 이러한 과정을 정리하면 다음과 같습니다.</p>
<ol>
<li>클라이언트(브라우저)가 서버로 최초 연결 시도를 함</li>
<li>서버는 공개키(엄밀히는 인증서)를 브라우저에게 넘겨줌</li>
<li>브라우저는 인증서의 유효성을 검사하고 세션키를 발급함</li>
<li>브라우저는 세션키를 보관하며 추가로 서버의 공개키로 세션키를 암호화하여 서버로 전송함</li>
<li>서버는 개인키로 암호화된 세션키를 복호화하여 세션키를 얻음</li>
<li>클라이언트와 서버는 동일한 세션키를 공유하므로 데이터를 전달할 때 세션키로 암호화/복호화를 진행함</li>
</ol>
<p>공개키로 암호화된 메세지는 개인키를 가지고 있어야만 복호화가 가능하기 때문에, 서버(기업)을 제외한 누구도 원본 데이터를 얻을 수 없습니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/2d8af6ea-91c7-4d87-9f76-8bfdd32974e3/image.png" alt=""></p>
<p>[ HTTP 1 vs HTTP 2 ]
HTTP1은 기본적으로 연결당 하나의 요청/응답을 처리하여 다음과 같은 문제를 가지고 있었습니다.</p>
<p>HOL(Head Of Line) Blocking (특정 응답 지연): 클라이언트의 요청과 서버의 응답이 동기화되어 지연 발생
RTT(Round Trip TIme) 증가 (양방향 지연): 패킷 왕복 시간의 지연 발생
헤더 크기의 비대: 쿠키 등과 같은 메타데이터에 의해 헤더가 비대해짐</p>
<p>그리고 HTTP2는 다음과 같은 기술을 사용하여 HTTP1의 성능 문제를 해결하였습니다.</p>
<p>Multiplexed Streams: 하나의 커넥션으로 여러 개의 메세지를 동시에 주고 받을 수 있음
Stream Prioritization: 요청온 리소스간의 의존관계를 설정하여 먼저 응답해야하는 리소스를 우선적으로 반환함
Header Compression: 헤더 정보를 HPACK 압축 방식을 이용하여 압축 전송함
Server Push: HTML문서 상에 필요한 리소스를 클라이언트 요청없이 보내줄 수 있음</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/da4a5dab-90b7-4220-b5dc-2edcdebe3571/image.png" alt=""></p>
<h2 id="5운영체제">5.운영체제</h2>
<p>[ Byte Ordering이란 ]
Byte Ordering이란 데이터가 저장되는 순서를 의미합니다. Byte Ordering의 방식에는 빅엔디안(Big Endian)과 리틀엔디안(Little Endian)이 있습니다.</p>
<p>MSB: Most signnificant byte
LSB: Least signnificant byte</p>
<p><strong>Big Endian</strong>
MSB가 가장 낮은 주소에 위치하는 저장 방식
네트워크에서 데이터를 전송할 때 주로 사용됨
가장 낮은 주소에 MSB가 저장되므로, offset=0인 Byte를 보면 양수/음수를 바로 파악할 수 있다.</p>
<p><strong>Little Endian</strong>
MSB가 가장 높은 주소에 위치하는 방식
마이크로프로세서에서 주로 사용된다.
가장 낮은 주소에 부호값이 아닌 데이터가 먼저 오기 때문에, 바로 연산을 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/972c1864-8dd1-4f64-b45c-012f34d69f05/image.png" alt=""></p>
<p>[ 메모리란 ]
메모리는 컴퓨터에서 작업을 수행하기 위해 처리 대상이나 결과 등을 저장하기 위한 공간입니다. 프로그램을 실행하기 위한 정보들은 메모리에 저장되어 처리됩니다.</p>
<p>[ 프로세스와 쓰레드의 차이 ]
<strong>프로세스</strong>
정의: <strong>메모리에 올라와 실행되고 있는 프로그램의 인스턴스</strong></p>
<ol>
<li>운영체제로부터 독립된 메모리 영역을 할당받는다. (다른 프로세스의 자원에 접근 X)</li>
<li><strong>프로세스들은 독립적</strong>이기 때문에 통신하기 위해 IPC를 사용해야 한다.</li>
<li>프로세스는 최소 1개의 쓰레드(메인 쓰레드)를 가지고 있다.</li>
</ol>
<p><strong>쓰레드</strong>
정의: <strong>프로세스 내에서 할당받은 자원을 이용해 동작하는 실행 단위</strong></p>
<ol>
<li>쓰레드는 프로세스 내에서 Stack만 따로 할당 받고, Code, Data, Heap 영역은 공유한다.
(Stack을 분리한 이유는 Stack에는 함수의 호출 정보가 저장되는데, Stack을 공유하면 LIFO 구조에 의해 실행 순서가 복잡해지기 때문에 실행 흐름을 원활하게 만들기 위함이다.)</li>
<li>쓰레드는 프로세스의 자원을 공유하기 때문에 다른 쓰레드에 의한 결과를 즉시 확인할 수 있다.</li>
<li>프로세스 내에 존재하며 프로세스가 할당받은 자원을 이용하여 실행된다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/flip_404/post/069baebf-39c6-4994-b0e0-26e332a737e6/image.png" alt=""></p>
<p>[ 컨텍스트 스위칭(Context Switching)이란? ]
Context Switching이란 인터럽트를 발생시켜 <strong>CPU에서 실행중인 프로세스를 중단하고, 다른 프로세스를 처리하기 위한 과정</strong>입니다. Context Switching는 <strong>현재 실행중인 프로세스의 상태(Context)를 먼저 저장하고, 다음 프로세스를 동작시켜 작업을 처리한 후에 이전에 저장된 프로세스의 상태를 다시 복구</strong>합니다. 여기서 <strong>인터럽트란 CPU가 프로세스를 실행하고 있을 때, 입출력 하드웨어 등의 장치나 예외상황이 발생하여 처리가 필요함을 CPU에게 알리는 것</strong>을 말합니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/2cd92ad6-8f76-4c44-9971-c5b17514f0d9/image.png" alt=""></p>
<p>[ 멀티 프로세스 VS 멀티 쓰레드 ]
<strong>멀티 프로세스</strong>
하나의 프로그램을 여러 개의 프로세스로 구성하여 각 프로세스가 1개의 작업을 처리하도록 하는 것</p>
<ol>
<li>1개의 프로세스가 죽어도 자식 프로세스 이외의 다른 프로세스들은 계속 실행된다. (자식 프로세스는 실행되지 않는다.)</li>
<li>Context Switching을 위한 오버헤드(캐시 초기화, 인터럽트 등)가 발생한다.</li>
<li><strong>프로세스는 각각 독립적인 메모리를 할당받았기 때문에 통신하는 것이 어렵다.</strong></li>
</ol>
<p>멀티 쓰레드
하나의 프로그램을 여러 개의 쓰레드로 구성하여 각 쓰레드가 1개의 작업을 처리하도록 하는 것</p>
<ol>
<li>프로세스를 위해 자원을 할당하는 시스템콜이나 <strong>Context Switching의 오버헤드를 줄일 수 있다.</strong></li>
<li>쓰레드는 메모리를 공유하기 때문에, 통신이 쉽고 자원을 효율적으로 사용할 수 있다.</li>
<li>하나의 쓰레드에 문제가 생기면 전체 프로세스가 영향을 받는다.</li>
<li>여러 쓰레드가 하나의 자원에 동시에 접근하는 경우 자원 공유(동기화)의 문제가 발생할 수 있다.</li>
</ol>
<p>[ 데드락(DeadLock) 이란? ]
<strong>데드락(DeadLock) 또는 교착상태란 한정된 자원을 여러 프로세스가 사용하고자 할 때 발생하는 상황</strong>으로, 프로레스가 자원을 얻기 위해 영구적으로 기다리는 상태입니다. 예를 들어 다음과 같은 상황에서 데드락이 발생할 수 있습니다.</p>
<p>자원 A를 가진 프로세스 P1과 자원 B를 가진 프로세스 P2가 있을 때, P1은 B를 필요로 하고 P2는 A를 필요로 한다면 두 프로세스 P1, P2는 서로 자원을 얻기위해 무한정 기다리게 됩니다.</p>
<p>[ 멀티 쓰레드 프로그래밍 작성 시 유의점 ]
멀티 쓰레드 프로그램을 개발한다면, 다수의 쓰레드가 공유 데이터에 동시에 접근하는 경우에 상호 배제를 제거해 교착 상태를 예방하고 동기화 기법을 통해 동시성 문제가 발생하지 않도록 발생하지 않도록 주의해야 합니다.</p>
<p>[ 세마포어(Semaphore) vs 뮤텍스(Mutex) 차이 ]
뮤텍스는 Locking 메커니즘으로 락을 걸은 쓰레드만이 임계 영역을 나갈때 락을 해제할 수 있습니다. 하지만 세마포어는 Signaling 메커니즘으로 락을 걸지 않은 쓰레드도 signal을 사용해 락을 해제할 수 있습니다. 세마포어의 카운트를 1로 설정하면 뮤텍스처럼 활용할 수 있습니다.</p>
<p>[ CPU의 메모리 I/O 도중 생기는 병목 현상 해결 방법 ]
이러한 문제를 해결하기 위해 메모리를 계층화하여 병목현상을 해결하고 있습니다. 자주 접근하는 데이터의 경우에는 캐시에 저장하여 접근 속도를 향상 시킴으로써 부하를 줄이고 있습니다.</p>
<ol start="5">
<li>운영체제 - 고급</li>
</ol>
<p>[ 가상메모리와 페이지폴트 ]
<img src="https://velog.velcdn.com/images/flip_404/post/3505e977-469a-43d8-b0f1-ad5d1f5e9832/image.png" alt=""></p>
<p><strong>가상메모리는 RAM의 부족한 용량을 보완하기 위해, 각 프로그램에 실제 메모리 주소가 아닌 가상의 메모리 주소를 할당하는 방식</strong>입니다. OS는 프로세스들의 내용(페이지) 중에서 <strong>덜 중요한 것들을 하드디스크</strong>에 옮겨 놓고, <strong>관련 정보를 페이지 테이블에 기록</strong>합니다. CPU는 프로세스를 실행하면서 페이지 테이블을 통해 페이지를 조회하는데, 실제메모리에 원하는 페이지가 없는 상황이 발생할 수 있습니다(Valid bit를 통해 확인). 이것을 페이지 폴트라고 하는데 프로세스가 동작하면서 실제메모리에 필요한 데이터(페이지)가 없으면 가상메모리를 통해서 해당 데이터를 가져오게 됩니다. 가상메모리는 하드디스크에 저장되어 있기 때문에, 페이지폴트가 발생하면 I/O에 의한 속도의 저하가 발생합니다.</p>
<p>[ 페이지 교체 알고리즘과 LRU(Least Recently Used) ]
LRU(Least Recently Used)는 페이지를 교체하기 위한 알고리즘 중 하나입니다.</p>
<p>페이지를 교체하는 이유는 가상메모리를 통해 조회한 페이지는 다시 사용될 가능성이 높기 때문입니다. 페이지 교체를 위해서는 실제메모리에 존재하는 페이지를 가상메모리로 저장한 후에, 가상메모리에서 조회한 페이지를 실제메모리로 로드해야 됩니다. 그렇다면 어떤 실제메모리의 페이지를 가상메모리로 희생시킬 것이냐에 대한 문제가 발생하는데, 이때 사용하는 알고리즘 중 하나가 LRU(Least Recently Used) 알고리즘 입니다.</p>
<p>LRU 알고리즘은 실제메모리의 페이지들 중에서 가장 오랫동안 사용되지 않은 페이지를 선택하는 방식입니다. 그 외에도 먼저 적재된 페이지를 희생시키는 FIFO(First In First Out) 알고리즘이나 LRU 알고리즘을 응용하여 페이지에 Second-Change를 주는 LRU Approximation 등이 있습니다.</p>
<p>[ 페이지 교체 알고리즘과 LRU(Least Recently Used) ]
LRU(Least Recently Used)는 페이지를 교체하기 위한 알고리즘 중 하나입니다.</p>
<p><strong>페이지를 교체하는 이유는 가상메모리를 통해 조회한 페이지는 다시 사용될 가능성이 높기 때문</strong>입니다. <strong>페이지 교체를 위해서는 실제메모리에 존재하는 페이지를 가상메모리로 저장한 후</strong>에, <strong>가상메모리에서 조회한 페이지를 실제메모리로 로드</strong>해야 됩니다. 그렇다면 <strong>어떤 실제메모리의 페이지를 가상메모리로 희생시킬 것이냐에 대한 문제가 발생하는데, 이때 사용하는 알고리즘 중 하나가 LRU(Least Recently Used) 알고리즘</strong> 입니다.</p>
<p><strong>LRU 알고리즘은 실제메모리의 페이지들 중에서 가장 오랫동안 사용되지 않은 페이지를 선택하는 방식</strong>입니다. 그 외에도 먼저 적재된 페이지를 희생시키는 FIFO(First In First Out) 알고리즘이나 LRU 알고리즘을 응용하여 페이지에 Second-Change를 주는 LRU Approximation 등이 있습니다.</p>
<h2 id="6데이터베이스">6.데이터베이스</h2>
<p>[ 인덱스(index)란? ]
<strong>인덱스란 추가적인 쓰기 작업과 저장 공간을 활용하여 데이터베이스 테이블의 검색 속도를 향상시키기 위한 자료구조</strong>이다. 만약 우리가 책에서 원하는 내용을 찾는다고 하면, 책의 모든 페이지를 찾아 보는것은 오랜 시간이 걸린다. 그렇기 때문에 책의 저자들은 책의 맨 앞 또는 맨 뒤에 색인을 추가하는데, 데이터베이스의 index는 책의 색인과 같다.</p>
<p>데이터베이스에서도 테이블의 모든 데이터를 검색하면 시간이 오래 걸리기 때문에 데이터와 데이터의 위치를 포함한 자료구조를 생성하여 빠르게 조회할 수 있도록 돕고 있다.</p>
<p>만약 Index를 적용하지 않은 컬럼을 조회한다면, 전체를 탐색하는 Full Scan이 수행된다. Full Scan은 전체를 비교하여 탐색하기 때문에 처리 속도가 떨어진다.</p>
<p>[ 인덱스의 자료구조 ]
<strong>해시 테이블</strong></p>
<ol>
<li>컬럼의 값으로 생성된 해시를 기반으로 인덱스를 구현한다.</li>
<li>시간복잡도가 O(1)이라 검색이 매우 빠르다.</li>
<li>부등호(&lt;, &gt;)와 같은 연속적인 데이터를 위한 순차 검색이 불가능하다.</li>
</ol>
<p><strong>B+Tree</strong></p>
<ol>
<li>자식 노드가 2개 이상인 B-Tree를 개선시킨 자료구조이다.</li>
<li>BTree의 리프노드들을 LinkedList로 연결하여 순차 검색을 용이하게 하였다.</li>
<li>해시 테이블보다 나쁜 O(𝑙𝑜𝑔2𝑛) 의 시간복잡도를 갖지만 해시테이블보다 흔하게 사용된다.</li>
</ol>
<p>[ DB 정규화 ]
제1정규형: 모든 속성 값이 원자 값을 갖도록 분해한다.</p>
<p>제2정규형: 제1정규형을 만족하고, 기본키가 아닌 속성이 기본키에 완전 함수 종속이도록 분해한다.
(여기서 완전 함수 종속이란 기본키의 부분집합이 다른 값을 결정하지 않는 것을 의미한다.)</p>
<p>제3정규형: 제2정규형을 만족하고, 기본키가 아닌 속성이 기본키에 직접 종속(비이행적 종속)하도록 분해한다.
(여기서 이행적 종속이란 A-&gt;B-&gt;C가 성립하는 것으로, 이를 A,B와 B,C로 분해하는 것이 제3정규형이다.)</p>
<p>BCNF 정규형: 제3정규형을 만족하고, 함수 종속성 X-&gt;Y가 성립할 때 모든 결정자 X가 후보키가 되도록 분해한다.</p>
<p>[ 트랜잭션(Transaction)이란? ]
트랜잭션이란 데이터베이스 작업의 단위로써 하나 이상의 쿼리를 처리할 때 동일한 Connection 객체를 공유하여 에러가 발생한 경우 모든 과정을 되돌리기 위한 방법입니다.</p>
<p>[ 트랜잭션의 ACID란? ]
원자성(Atomicity): 트랜잭션에 포함된 작업은 전부 수행되거나 전부 수행되지 않아야 한다.
일관성(Consistency): 트랜잭션을 수행하기 전이나 후나 데이터베이스는 항상 일관된 상태를 유지해야 한다.
고립성(Isolation): 수행 중인 트랜잭션에 다른 트랜잭션이 끼어들어 변경중인 데이터 값을 훼손하지 않아야한다.
지속성(Durability): 수행을 성공적으로 완료한 트랜잭션은 변경한 데이터를 영구히 저장해야 한다.</p>
<p>[ 이상 현상의 종류 ]
삭제 이상: 튜플 삭제 시 같이 저장된 다른 정보까지 연쇄적으로 삭제되는 현상
삽입 이상: 튜플 삽입 시 특정 속성에 해당하는 값이 없어 NULL을 입력해야 하는 현상
수정 이상: 튜플 수정 시 중복된 데이터의 일부만 수정되어 일어나는 데이터 불일치 현상</p>
<p>[ DB 락의 종류 ]
DB 락은 여러 개의 트랜잭션들이 하나의 데이터로 동시에 접근하려고 할 때 이를 제어해주는 도구이다.</p>
<p>공유락(LS, Shared Lock): 트랜잭션이 읽기를 할 때 사용하는 락, 데이터를 읽을 수 있지만 쓸 수 없음
베타락(LX, Exclusive Lock): 트랜잭션이 읽고 쓰기를 할 때 사용하는 락, 데이터를 읽고 쓸 수 있음</p>
<p>[ RDBMS와 NoSQL 차이 ]
RDBMS
2차원의 행과 열로 데이터의 관계를 관리하는 데이터베이스 
장점: 스키마에 맞추어 데이터를 관리하기 때문에 데이터의 정합성을 보장할 수 있다.
단점: 시스템이 커질 수록 쿼리가 복잡해지고 성능이 저하되며, 수평적 확장이 어렵다.
NoSQL
RDBMS가 비대해짐에 따라 관계가 복잡해져, 이를 극복하기 위해 등장하게 된 데이터베이스
장점: NOSQL은 스키마 없이 Key-Value 형태로 데이터를 관리하여 좀 더 자유롭게 데이터를 관리할 수 있다.
단점: 중복된 데이터가 추가 가능하여, 이에 대한 관리가 필요하다.</p>
<ol start="6">
<li>데이터베이스 - 고급
[ 힌트(Hint)란? ]
힌트란 SQL을 튜닝하기 위한 지시구문입니다. 옵티마이저가 최적의 계획으로 SQL문을 처리하지 못하는 경우에 개발자가 직접 최적의 실행 계획을 제공하는 것입니다. 힌트는 아래와 같이 SELECT 다음에 작성할 수 있으며, INDEX, PARALLEL 등 다양한 힌트절이 있습니다.</li>
</ol>
<pre><code># 사용가능한 힌트절: PARALLE, INDEX, FULL ...
SELECT /*+ [힌트절] */ </code></pre><p>[ 클러스터링 vs 리플리케이션 ]
리플리케이션
여러 개의 DB를 권한에 따라 수직적인 구조(Master-Slave)로 구축하는 방식이다.
비동기 방식으로 노드들 간의 데이터를 동기화한다.
장점: 비동기 방식으로 데이터가 동기화되어 지연 시간이 거의 없다.
단점: 노드들 간의 데이터가 동기화되지 않아 일관성있는 데이터를 얻지 못할 수 있다.
클러스터링
여러 개의 DB를 수평적인 구조로 구축하여 Fail Over한 시스템을 구축하는 방식이다.
동기 방식으로 노드들 간의 데이터를 동기화한다.
장점: 1개의 노드가 죽어도 다른 노드가 살아 있어 시스템을 장애없이 운영할 수 있다.
단점: 여러 노드들 간의 데이터를 동기화하는 시간이 필요하므로 Replciation에 비해 쓰기 성능이 떨어진다.</p>
<p>[ 데이터베이스 튜닝과 방법 ]
DB 튜닝은 테이터베이스의 구조나 데이터베이스 자체, 운영체제 등을 조정하여 데이터베이스 시스템의 성능을 향상시키는 작업을 의미합니다. 튜닝은 DB 설계 튜닝 -&gt; DBMS 튜닝 &gt; SQL 튜닝의 단계로 진행할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/9349e376-e86e-4ec1-abc3-113e211e878a/image.png" alt="">
<a href="https://makefortune2.tistory.com/84">https://makefortune2.tistory.com/84</a></p>
<p>개발언어: <a href="https://mangkyu.tistory.com/94">https://mangkyu.tistory.com/94</a>
백엔드: <a href="https://mangkyu.tistory.com/94">https://mangkyu.tistory.com/94</a>
기술 외 공통 면접 질문: <a href="https://mangkyu.tistory.com/94">https://mangkyu.tistory.com/94</a></p>
<p>   참고</p>
<ul>
<li><a href="https://mangkyu.tistory.com/category">https://mangkyu.tistory.com/category</a> (망나니 개발자)</li>
<li><a href="https://mindnet.tistory.com/entry/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-22%ED%8E%B8-TCP-3-WayHandshake-4-WayHandshake">https://mindnet.tistory.com/entry/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-22%ED%8E%B8-TCP-3-WayHandshake-4-WayHandshake</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프론트엔드] MPA, SPA, CSR, SSR 개념정리]]></title>
            <link>https://velog.io/@flip_404/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-MPA-SPA-CSR-SSR-%EA%B0%9C%EB%85%90%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@flip_404/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-MPA-SPA-CSR-SSR-%EA%B0%9C%EB%85%90%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 24 Oct 2022 06:39:08 GMT</pubDate>
            <description><![CDATA[<h2 id="서론">서론</h2>
<p><img src="https://velog.velcdn.com/images/flip_404/post/8fa84bd6-e394-4e30-bf1c-51b5ef0d50f0/image.png" alt=""></p>
<p>전통적인 사이트들은 하나의 index.html을 통해 다른 페이지(html)로 이동하는 방식이다. (이러한 방식을 MPA라 한다.)</p>
<p>하지만, 최근의 웹 사이트들은 ReactJS,VueJS,AngularJS와 같은 프레임워크/라이브러리를 사용한 SPA 방식이 주를 이룬다.
위 그림의 SPA 라이프사이클을 보면 하나의 html에서 어떠한 동작을 통해 화면을 전환한다.</p>
<p><strong>MPA에서는 SSR, SPA에서는 CSR 렌더링 방식</strong>을 사용한다. (자세한건 아래에)
이번 글을 통해 SPA와 MPA의 차이를 알아보자.</p>
<h2 id="mpamulti-page-application">MPA(Multi Page Application)</h2>
<p><img src="https://velog.velcdn.com/images/flip_404/post/f749b660-4b29-4e1c-9b04-2b68f0de3cce/image.png" alt=""></p>
<p>a 태그를 통해 페이지를 넘어가는 작업을 해봤을 것이다.
MPA로 웹을 구성할 경우, 위의 사진처럼 각 폴더마다 index.html이 존재한다.</p>
<p>이러한 방식의 경우, 페이지를 이동할 때 마다 프론트 서버에서 request를 보내고 원하는 페이지의 response를 받아 온다. 즉, MPA 방식은 브라우저에서 페이지를 이동할 때 마다 아래의 동작을 계속해서 반복하는 것이다.</p>
<blockquote>
<ol>
<li>서버로부터 HTML, CSS, Javascript, Img files... 등을 응답받는다.</li>
<li>HTML 파서와 CSS 파서를 통해 위 파일들을 DOM Tree, CSSOM 트리로 변환 후, 렌더 트리로 결합한다.</li>
<li>렌더트리를 기반으로 웹페이지를 표시한다.</li>
</ol>
</blockquote>
<p><strong>tip) 2-1. Javascript 파싱</strong></p>
<ul>
<li><p>HTML 파서는 script 태그를 만나면 자바스크립트 코드를 실행하기 위해 DOM 생성 프로세스를 중지하고 자바스크립트 엔진으로 제어 권한을 넘긴다.(자바스크립트는 렌더링 엔진이 아닌 자바스크립트 엔진이 처리한다.) 제어 권한을 넘겨 받은 자바스크립트 엔진은 script 태그 내의 자바스크립트 코드 또는 script 태그의 src 어트리뷰트에 정의된 자바스크립트 파일을 로드하고 파싱하여 실행한다. 자바스크립트의 실행이 완료되면 다시 HTML 파서로 제어 권한을 넘겨서 브라우저가 중지했던 시점부터 DOM 생성을 재개한다.</p>
</li>
<li><p>이처럼 브라우저는 동기(Synchronous)적으로 HTML, CSS, Javascript을 처리한다. 이것은 script 태그의 위치에 따라 블로킹이 발생하여 DOM의 생성이 지연될 수 있다는 것을 의미한다. 따라서 script 태그의 위치는 중요한 의미를 갖는다. </p>
</li>
<li><p>body 요소의 가장 아래에 자바스크립트를 위치시키는 것은 좋은 아이디어이다. 그 이유는 아래와 같다.</p>
</li>
<li><p>HTML 요소들이 스크립트 로딩 지연으로 인해 렌더링에 지장 받는 일이 발생하지 않아 페이지 로딩 시간이 단축된다. DOM이 완성되지 않은 상태에서 자바스크립트가 DOM을 조작한다면 에러가 발생한다.</p>
</li>
</ul>
<p>본론으로 넘어와서, 페이지 이동이 많은 웹 서비스에서 위의 1~3의 과정을 반복하는 것은 굉장히 비효율적일것이다. 이러한 과정을 효율적으로 개선하기 위해 나온 것이 SPA이다.</p>
<h3 id="mpa-장단점">MPA 장단점</h3>
<p>그렇다고 MPA가 이러한 단점만 있는 것이 아니다. 서비스의 이용 목적에 따라 SPA, MPA를 선택하여 개발하는 것이 중요하다.</p>
<p><strong>MPA 장점</strong></p>
<ul>
<li><p><strong>SEO 및 페이지 순위 지정</strong>
검색엔진 (SEO. Search Engine Optimization) 이 페이지를 크롤링 하기에 매우 적합하고 매우 직관적이라는 것이다.
HTML이 서버 측에서 완전히 형성되고 웹 크롤러가 HTML 페이지를 더 쉽게 인덱싱할 수 있기 때문이다.</p>
</li>
<li><p><strong>초기 페이지 로드 시간이 빠르다.</strong>
서버에서 수신한 HTML을 브라우저에서 빠르게 구문 분석하여 즉시 표시할 수 있고 웹 페이지를 표시하기 위해 별도의 JS 번들을 다운로드하여 실행할 필요가 없기 때문에 초기 페이지의 로드 시간이 더 빠르다.</p>
</li>
<li><p><strong>직관적인 작업환경</strong>
처음 웹을 접하는 개발자(또는 퍼블리셔)라면 쉽게 어느 페이지에 뭐가 있는지 파악하기 좋다.</p>
</li>
</ul>
<p><strong>MPA 단점</strong></p>
<ul>
<li><p><strong>페이지 이동 시 발생하는 화면 깜빡임</strong>
로직이나 JS동작이 별로 없으면서 단순한 텍스트만 존재하는 페이지라면 큰 차이를 느끼지 못 할 수도 있다. 하지만 많은 사이트들이 이미지와 방대한 data들을 불러오기 위해 API를 사용하고, 수많은 스타일링이 들어가는 css나 html을 가지고 있기 때문에 이러한 데이터들을 불러오는 과정에서 깜빡임이 발생한다.</p>
</li>
<li><p><strong>나쁜 사용자 경험</strong>
최근이야 기계들의 성능들이 많이 향상되어 조금 차이를 못 느낄 수 있을 수도 있으나, 현대인들의 사용자 경험(학습)도 늘었기 때문에 0.1초라도 늦으면 답답해 하는 나쁜 사용자 경험 을 무시하고 MPA을 고집할 수만은 없을 것이다.</p>
</li>
<li><p><strong>낮은 응답성</strong>
HTML을 수신하기 위해 서버로 계속 왕복하기 때문에 인터넷 연결이 낮거나 연결되지 않은 경우 응답성이 낮다.</p>
</li>
<li><p><strong>웹 트래픽 문제</strong>
Dynamic SSR 의 경우 <strong>서버가 모든 요청에 대해 동적으로 HTML을 생성해야 하므로 웹 트래픽이 증가하면 서버 로드가 증가</strong>한다.</p>
</li>
</ul>
<h2 id="spasingle-page-application">SPA(Single Page Application)</h2>
<p>SPA는 최초 한번 페이지 전체를 로딩한 이후 부터는 <strong>데이터만 변경하여 사용할 수 있는 웹 어플리케이션</strong>이다. 말그대로 Single. 딱 하나의 index.html이 존재한다.
아래의 react 프로젝트(이미지)를 보면 index.html 하나로만 구성되어 있는 것을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/89d070fe-ce22-49f9-bec5-1afb09df192e/image.png" alt=""></p>
<p>하나의 index.html가 열리고 나서 페이지 이동 액션이 실행되면 MPA와 달리 <strong>프론트 서버에서 request를 보내는 것이 아니라 웹 클라이언트 자체적으로 JS에 의해 화면이 전환</strong>된다.</p>
<p>SPA의 경우, 처음에 하나의 빈 페이지만 Server에서 제공받고 클라이언트(Client)에서 JS를 통해 렌더링 하는 방식이다. 필요한 모든 정적 리소스를 한번에 다운로드 받고 특정 부분만 Ajax로 동적 데이터를 요청하여 받은 JSON/XML를 View(화면)에 뿌려주는 형식이다.</p>
<p>위의 설명과 같이, 처음에 이동가능한 모든 페이지에 대한 파일을 클라이언트에서 받아오기 때문에, 초기 페이지 로딩이 오래 걸린다.
하지만, 이후 페이지를 이동하거나 렌더링 할 경우. MPA에 비해서 빨라서 모바일 환경에서 매우 유용하다.</p>
<p>페이지내에서 동적으로 변해야하는 부분이 있는 경우 해당 정보만 백엔드 서버에 요청하여 응답을 받고 웹을 갱신한다.</p>
<p><img src="https://velog.velcdn.com/images/flip_404/post/aa658a5f-7e89-494c-82c0-7863a421b734/image.png" alt=""></p>
<p>위 그림은 왼쪽부터 SPA, MPA를 이미지화 한 것이다.</p>
<h3 id="spa-장단점">SPA 장단점</h3>
<p><strong>SPA 장점</strong></p>
<ul>
<li><p><strong>빠른 로딩 시간(최초만 느리다.)</strong>
페이지가 처음에 로드 되면 서버는 더이상 HTML, CSS를 보내지 않는다. 따라서 빠른 호환성으로 인해 동일한 템플릿을 무수히 많이 사용해야 하는 페이지에 유용하다. Goolere Search 에 의하면 페이지를 로드하는데 200 milliseconds 이상이 되면 비즈니스 및 판매에 큰 영향을 미친다고 하니, 로딩시간은 충분히 큰 이점이 있다.</p>
</li>
<li><p><strong>좋은 캐싱 능력</strong>
SPA는 하나의 요청을 보내고 서버의 모든 데이터를 저장한다. 데이터에 대한 지속적인 액세스가 있으므로 사용자는 오프라인에서도 사용할 수 있을 뿐만 아니라, 로컬데이터는 서버와 동기화가 된다.</p>
</li>
<li><p><strong>향상된 상용자 경험</strong>
콘텐츠의 동적 로딩은 사용자에게 원활한 경험을 제공한다. (쉽게 말하면 로딩이 빨라, 긍정적인 경험을 얻는다)
특히나 모바일 환경에서는 빠른 속도로 랜더링 되기 때문에 쾌적한 환경을 제공 할 수 있다.</p>
</li>
<li><p><strong>신속한 프론트엔드 개발</strong>
분리된 SPA 아키텍쳐이기 때문에, 프론트/백 엔드 개발 서비스를 분리해서 작업 할 수 있다. 그래서 자유로운 빌드, 테스트 및 배포를 할 수 있다.</p>
</li>
</ul>
<p><strong>SPA 단점</strong></p>
<ul>
<li><strong>검색 엔진 최적화(SEO)</strong>
SPA는 자바스크립트에서 실행되기 때문에 사용자가 요청을 보낼 때 데이터가 다운로드되서 페이지의 url이 다른 페이지에 고유한 url이 없는 것과 동일하게 유지되면서 검색 봇이 페이지를 스캔 할 수 없기 때문에 최적화 하기가 어렵다. Google 검색은 개선이 되고 있지만 JS 파일에 Google 자체 크롤러가 있기 때문에 색인을 생성하는지 확인 해야한다. (좀 더 자세히 알아보자)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/flip_404/post/aab3f830-b16b-41e0-9112-a7795592847e/image.png" alt=""></p>
<ul>
<li><p><strong>복잡한 SPA의 문제</strong>
복잡한 SPA를 빌드 할 시 크기 때문에 사용하기가 불편할 수도 있다. 페이지의 로딩시간은 복잡성으로 인해 늘어 날 수있고, 사용자 참여를 감소 시키는 요인된다.</p>
</li>
<li><p><strong>Analytics</strong>
Analytics도구는 페이지에 단일 코드를 추가하는 것만으로 페이지 보기를 추적 할 수 있다. 하지만 SPA는 실제 페이지가 아니기 떄문에 추적을 위한 논리를 추가해야 하며 페이지를 새로 고칠 필요 없이 분석 도구가 새 페이지를 선택하도록 해야한다. 새로운 코드를 추가 하는 것이 다소 번거로울 수 있다.</p>
</li>
</ul>
<blockquote>
<p>tip) <strong>검색 엔진 최적화(영어: search engine optimization, SEO)</strong>
웹 페이지 검색엔진이 자료를 수집하고 순위를 매기는 방식에 맞게 웹 페이지를 구성해서 검색 결과의 상위에 나올 수 있도록 하는 작업을 말한다.</p>
</blockquote>
<h2 id="ssrserver-side-rendering">SSR(Server-Side Rendering)</h2>
<p><img src="https://velog.velcdn.com/images/flip_404/post/dcf2bf00-d3a9-45c3-81b4-98cf82aae56a/image.png" alt=""></p>
<p>동적인 HTML 문서를 만드는 방식으로 PHP, JSP, ASP, Node.js 등 Server-Side Script 언어 기반의 템플릿 엔진을 사용한다.</p>
<p>SSR는 웹 브라우저가 요청하면 브라우저는 구성요소가 이미 생성되어 있는 HTML을 받아온다. (아래의 코드 형태로)</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt; 
   &lt;html&gt; 
     &lt;head&gt; 
        &lt;title&gt; 서버 측에서 렌더링된 웹 페이지 &lt;/title&gt; 
     &lt;/head&gt;
     &lt;body&gt; 
       &lt;h1&gt; 이것은 제목입니다 &lt;/h1&gt; 
       &lt;p&gt; 이것은 단락입니다 &lt;/p&gt; 
       &lt;div&gt; 
         &lt;p&gt; 이것은 양식입니다 &lt;/p&gt; 
           &lt;form&gt; 
             &lt;label for=&quot;fname&quot;&gt; 이름:&lt;/label&gt;&lt;br&gt; 
             &lt;input type=&quot;text&quot; id=&quot;fname&quot; name=&quot;fname&quot;&gt;&lt;br&gt; 
             &lt;label for=&quot;lname&quot;&gt;성:&lt;/label&gt;&lt;br&gt; 
             &lt; 입력 유형=&quot;텍스트&quot; id=&quot;lname&quot; 이름=&quot;lname&quot;&gt; 
           &lt;/form&gt; 
       &lt;div&gt;
      &lt;/body&gt;
   &lt;/html&gt;</code></pre>
<p>브라우저는 HTML을 파싱하여 DOM을 생성하고 웹페이지에 보여주기만 하면 된다.
SSR는 요청 시 서버 측 렌더링은 탐색 요청을 수신하고 외부 데이터(서버 측에서)를 가져오고 HTML을 동적으로 생성하기 위해 실행 중인 웹 서버가 필요하다.</p>
<p>브라우저에 JS가 없어도 HTML, css가 있으면 화면에 노출이 된다.</p>
<p>그래서 MPA는 SSR 방식이라고 설명한다.
(SSR 장점이 MPA의 장점이랑 전체적으로 동일하다.)</p>
<h2 id="csrclient-side-rendering">CSR(Client-Side Rendering)</h2>
<p><img src="https://velog.velcdn.com/images/flip_404/post/08804df1-9cc9-481e-b9fe-85a9dd277ca3/image.png" alt=""></p>
<p>CSR에서의 HTML 구성 요소는 브라우저에서 JS 코드를 실행하여 클라이언트 측에서 생성한다. <strong>브라우저가 웹 페이지를 요청할 때, 초기 서버요청은 최소한의 HTML 파일을 반환한다</strong></p>
<pre><code class="language-html">&lt;html&gt;
    &lt;head&gt; 
       &lt;title&gt; CSR 앱 &lt;/title&gt; 
    &lt;/head&gt; 

      &lt;body&gt; 
       &lt;div id=&quot;app&quot;&gt; &lt;/div&gt; 
       &lt;script src=&quot;../src/index.js&quot;&gt; &lt;/script&gt;
   &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>이 경우 브라우저가 HTML DOM을 생성하기 위해 HTML을 분석할 필요가 없다. 대신 브라우저는 HTML에 링크된 JS을 완전히 다운로드한 다음 브라우저에서 JS를 실행한다. 그러면 <strong>클라이언트 측에서 웹 페이지 HTML DOM이 생성</strong>된다.</p>
<p>또한 앱 내의 링크를 클릭하면 서버에서 HTML을 요청하는 대신 요청된 DOM을 생성하기 위해 HTML DOM 클라이언트을 조작한다.(일반적으로 &quot;라우터&quot; 모듈에 의해 처리된다. React-Router, Vue-Router)</p>
<p>브라우저에 JS가 없을 경우 빈페이지가 표시될 수 있다.(JS가 없으면 랜더링이 되지 않음)</p>
<p>참고)</p>
<ul>
<li><a href="https://poiemaweb.com/js-browser">https://poiemaweb.com/js-browser</a></li>
<li><a href="https://velog.io/@kmlee95/%EC%82%AC%EC%9A%A9%EC%9E%90%EA%B0%80-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EB%A5%BC-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0-%EA%B9%8C%EC%A7%80">https://velog.io/@kmlee95/%EC%82%AC%EC%9A%A9%EC%9E%90%EA%B0%80-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EB%A5%BC-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0-%EA%B9%8C%EC%A7%80</a></li>
<li><a href="https://velog.io/@mari-hidream/SPA-CSR-SSR-%EB%8B%A4-%EB%AC%B4%EC%8A%A8-%EC%86%8C%EB%A6%AC%EC%95%BC">https://velog.io/@mari-hidream/SPA-CSR-SSR-%EB%8B%A4-%EB%AC%B4%EC%8A%A8-%EC%86%8C%EB%A6%AC%EC%95%BC</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[04 - [JSP 프로그래밍] 액션 태그
]]></title>
            <link>https://velog.io/@flip_404/04-JSP-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%95%A1%EC%85%98-%ED%83%9C%EA%B7%B8</link>
            <guid>https://velog.io/@flip_404/04-JSP-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%95%A1%EC%85%98-%ED%83%9C%EA%B7%B8</guid>
            <pubDate>Tue, 18 Oct 2022 13:04:51 GMT</pubDate>
            <description><![CDATA[<p><img src="https://i.imgur.com/AcMiLCW.png" alt=""></p>
<p>본 게시글은 &quot;JSP 웹 프로그래밍&quot;을 학습하며, 내용 요약 또는 몰랐던 부분을 정리하는 글 입니다.</p>
<h2 id="액션-태그">액션 태그</h2>
<h3 id="액션-태그-1">액션 태그</h3>
<ul>
<li>서버나 클라이언트에게 어떤 행동을 하도록 명령하는 태그</li>
<li>JSP 페이지에서 페이지와 페이지 사이 제어</li>
<li>다른 페이지의 실행 결과 내용을 현재 페이지에 포함</li>
<li>자바빈즈(JavaBeans) 등의 다양한 기능 제공<ul>
<li>자바빈즈(JavaBeans): 자바로 작성된 <strong>소프트웨어 컴포넌트</strong></li>
</ul>
</li>
<li>&lt;%...%&gt;와 같은 스크립트 태그의 형식을 따르지 않고, XML 형식 &lt;jsp: .../&gt;을 사용한다.</li>
</ul>
<h4 id="forward-액션태그-jspforward-"><strong>forward 액션태그: &lt;jsp:forward .../&gt;</strong></h4>
<ul>
<li>다른 페이지로의 이동과 같은 페이지 흐름을 제어한다.</li>
<li>JSP 컨테이너는 현재 JSP 페이지에서 forward 액션 태그를 만나면 그 전까지 출력 버퍼에 저장되어 있던 내용을 모두 삭제하고, forward 액션 태그에 설정된 페이지로 프로그램의 제어가 이동</li>
<li>page 속성 값: 이동할 페이지의 외부 파일명(같은 디렉토리면 파일명만, 다르면 전체 URL 또는 상대 경로)
<img src="https://i.imgur.com/Mwwf6Dr.png" alt=""></li>
</ul>
<p>ex) forward 액션 태그 사용 예제
<img src="https://i.imgur.com/g0EGJdz.png" alt="">
&quot;이 파일은 first.jsp입니다&quot;는 출력되지 않은 것을 볼 수 있다. (버퍼를 비우기 때문)
<img src="https://i.imgur.com/B8g9RnK.png" alt=""></p>
<p>tip) forword액션 태그 사용 시 주의점
<img src="https://i.imgur.com/HJvyFYm.png" alt=""></p>
<p>액션 태그를 사용하는 이유: 자바 코드의 삽입을 되도록 최소화하여 유지 보수를 효율적으로 하는 것이 목적이다.</p>
<h4 id="include-액션-태그-jspinclude--외부-페이지의-내용을-포함하거나-페이지를-모듈화한다"><strong>include 액션 태그: &lt;jsp:include .../&gt; 외부 페이지의 내용을 포함하거나 페이지를 모듈화한다.</strong></h4>
<ul>
<li>include 디렉티브 태그처럼 현재 JSP 페이지의 특정 영역에 외부 파일의 내용을 포함하는 태그</li>
<li>현재 JSP 페이지에 포함할 수 있는 외부 파일은 HTML, JSP, 서블릿 페이지 등</li>
<li>page 속성: 현재 JSP 페이지 내에 <strong>포함할 내용</strong>을 가진 외부 파일명</li>
<li>flush 속성(default: false): 설정한 외부 파일로 제어가 이동할 때, 출력 버퍼에 저장한 결과를 처리(true면 제어가 이동할 때 모두 비운다. -&gt; 삭제가 아닌 비우는 것)<ul>
<li>헤더정보도 같이 전송되기 때문에 웹 브라우저에 전송되고 나면 헤더 정보가 반영되지 않아 문제가 될 수 있다.</li>
</ul>
</li>
<li>include 액션태그가 forward 액션태그와 다른 점은 포함된 외부 파일이 실행된 후 현재 JSP 페이지로 제어를 반환한다는 것
<img src="https://i.imgur.com/zaxaUIF.png" alt="">
제어권: first.jsp -&gt; second.jsp -&gt; first.jsp
(&quot;Java Server Page&quot; 출력 위치를 보면 이해가 쉽다.)</li>
</ul>
<p><strong>tip) include 액션 태그 vs include 디렉티브 태그</strong>
<img src="https://i.imgur.com/XJKAfJ2.png" alt=""></p>
<ul>
<li>include 액션 태그는 포함될 페이지의 결과가 원래 페이지의 결과와 <strong>합쳐져서</strong> 보인다.</li>
<li>include 디렉티브 태그는 포함될 파일의 <strong>소스코드를 복사하여 붙여넣기</strong> 함. 조각코드 삽입개념. 단순하게 다른 페이지의 내용이 텍스트로 포함된다.
<img src="https://i.imgur.com/KEq6nHv.png" alt=""></li>
</ul>
<h4 id="param-액션-태그-jspparam--jspforward-jspinclude-jspplugin-태그에-인자를-추가한다">param 액션 태그: &lt;jsp:param .../&gt; <a href="jsp:forward">jsp:forward</a>, <a href="jsp:include">jsp:include</a>, <a href="jsp:plugin">jsp:plugin</a> 태그에 인자를 추가한다.</h4>
<ul>
<li>현재 JSP 페이지에서 다른 페이지에 정보를 전달하는 태그</li>
<li>이 태그는 단독으로 사용되지 못하며 <a href="jsp:forward">jsp:forward</a>나 <a href="jsp:include">jsp:include</a> 태그의 내부에 사용</li>
<li>다른 페이지에 여러 개의 정보를 전송해야 할 때는 다중의 param 액션 태그 사용
<img src="https://i.imgur.com/KTAT9Pm.png" alt=""></li>
</ul>
<p><img src="https://i.imgur.com/Q9rVXVp.png" alt=""></p>
<h4 id="그-외-태그">그 외 태그</h4>
<ul>
<li>useBean: &lt;jsp:useBean .../&gt; JSP 페이지에 자바빈즈를 설정한다.</li>
<li>setProperty: &lt;jsp:setProperty .../&gt; 자바빈즈의 프로퍼티 값을 설정한다.</li>
<li>getProperty: &lt;jsp:getProperty .../&gt; 자바빈즈의 프로퍼티 값을 얻어온다.</li>
<li>plugin: &lt;jsp:plugin .../&gt; 웹 브라우저에 자바 애플릿을 실행한다. 자바 플러그인에 대한 OBJECT 또는 EMBED 태그를 만드는 브라우저별 코드를 생성한다.</li>
<li>element: &lt;jsp:element .../&gt; 동적 XML요소를 설정한다.</li>
<li>attribute: &lt;jsp:attribute .../&gt; 동적으로 정의된 XML 요소의 속성을 설정한다.</li>
<li>body: &lt;jsp:body .../&gt; 동적으로 정의된 XML 요소의 몸체를 설정한다.</li>
<li>text: &lt;jsp:text .../&gt; JSP 페이지 및 문서에서 템플릿 텍스트를 작성한다.</li>
</ul>
<p><img src="https://i.imgur.com/4lZJJGD.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[03 - [JSP 프로그래밍] 디렉티브 태그 ]]></title>
            <link>https://velog.io/@flip_404/03-JSP-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EB%94%94%EB%A0%89%ED%8B%B0%EB%B8%8C-%ED%83%9C%EA%B7%B8</link>
            <guid>https://velog.io/@flip_404/03-JSP-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EB%94%94%EB%A0%89%ED%8B%B0%EB%B8%8C-%ED%83%9C%EA%B7%B8</guid>
            <pubDate>Tue, 18 Oct 2022 13:04:09 GMT</pubDate>
            <description><![CDATA[<p><img src="https://i.imgur.com/AcMiLCW.png" alt=""></p>
<p>본 게시글은 &quot;JSP 웹 프로그래밍&quot;을 학습하며, 내용 요약 또는 몰랐던 부분을 정리하는 글 입니다.</p>
<h2 id="디렉티브-태그">디렉티브 태그</h2>
<h3 id="디렉티브-태그-1">디렉티브 태그</h3>
<ul>
<li>JSP 페이지를 어떻게 처리할 것인지를 설정하는 태그</li>
<li>JSP페이지가 서블릿 프로그램에서 서블릿 클래스로 변환할 때<ul>
<li>JSP 페이지와 관련된 정보를 JSP컨테이너에 지시하는 메시지</li>
</ul>
</li>
</ul>
<p>page: &lt;%@ page... %&gt; JSP 페이지에 대한 정보를 설정합니다.
include: &lt;%@ include... %&gt; JSP 페이지의 특정 영역에 다른 문서를 포함합니다.
taglib: &lt;%@ taglib... %&gt; JSP 페이지에서 사용할 태그 라이브러리를 설정합니다.</p>
<p><img src="https://i.imgur.com/xG1o3nj.png" alt=""></p>
<h3 id="page-디렉티브-태그">page 디렉티브 태그</h3>
<ul>
<li>현재 JSP 페이지에 대한 정보를 설정하는 태그</li>
<li>JSP 페이지의 어디에서든 선언할 수 있지만 일바적으로 JSP 페이지의 최상단에 선언하는 것을 권장
&lt;%@ page 속성1=&quot;값1&quot; [속성2=&quot;값&quot; ... ] %&gt; // &lt;%와 @사이에 공백이 없어야 한다.&gt;</li>
</ul>
<p><strong>page 디렉티브 태그의 속성</strong></p>
<p>isErrorPage: 현재 JSP 페이지가 오류 페이지인지 여부를 설정한다. (defalut: false)
isELignored: 현재 JSP 페이지의 표현 언어(EL) 지원 여부를 설정한다. (defalut: false)
isScriptingEnabled: 현재 JSP 페이지의 스크립트 태그 사용 여부를 설정한다.</p>
<p><img src="https://i.imgur.com/bK0KsnQ.png" alt=""></p>
<p>ex) page 디렉티브 태그 사용법 예제
<img src="https://i.imgur.com/kO7a4s4.png" alt="">
<img src="https://i.imgur.com/Upy9eNZ.png" alt=""></p>
<ul>
<li>contentType=&quot;charset=EUC-KR&quot;: 서버에서는 EUC-KR로 인코딩하여 웹 브라우저로 전송한다.</li>
<li>pageEncoding=&quot;UTF-8&quot;: JSP파일은 UTF-8로 인코딩이 된다.</li>
</ul>
<p>Language 속성: jsp 페이지에서 사용할 프로그래밍 언어를 설정(default: java)
contentType 속성: 현재 JSP 페이지의 콘텐츠 유형(MIME-type)을 설정하는데 사용(default: text/html)
<img src="https://i.imgur.com/LSLXcLB.png" alt="">
<img src="https://i.imgur.com/8e5UQ46.png" alt="">
pageEncoding 속성: 현재 JSP 페이지의 문자 인코딩 유형을 설정하는 데 사용. 문자 인코딩 유형의 기본 값은 ISO-8859-1</p>
<ul>
<li>msword에서 실행하게 된다.
<img src="https://i.imgur.com/NyZSoOn.png" alt="">
위 그림과 같이하면 contentType, pageEncoding 두 속성을 같은 의미로 사용할 수 있다</li>
</ul>
<p>import 속성</p>
<ul>
<li>현재 JSP 페이지에서 사용할 자바 클래스를 설정하는 데 사용</li>
<li>둘 이상의 자바 클래스를 포함하는 경우 쉼표(,)로 구분하여 연속해서 여러 개의 자바 클래스를 설정</li>
<li>또는 여러 개의 자바 클래스를 각각 별도로 설정할 수도 있음</li>
</ul>
<p><img src="https://i.imgur.com/zFTv2VZ.png" alt=""></p>
<p>session 속성</p>
<ul>
<li>현재 JSP 페이지의 <strong>HTTP 세션 사용 여부를 설정</strong>하는 데 사용</li>
<li>세션은 일반적으로 웹 애플리케이션이 실행되는 동안 사용자가 웹 애플리케이션의 데이터를 가져와 확인할 수 있는 권한을 부여받기 위해 사용함<ul>
<li>예를 들어, 사용자가 은행 계좌에 로그인하여 로그아웃(세션 만료)할 때까지 모든 데이터에 접근할 수 있음</li>
</ul>
</li>
<li>JSP 페이지에 대한 세션을 유지하려면 세션 속성을 true로 함</li>
<li>기본 값: 세션을 자동으로 사용하는 true<ul>
<li>만약 session 속성 값을 false로 설정할 경우, 해당 JSP 페이지에서 내장 객체인 session 변수를 사용할 수 없다는 의미이므로 해당 페이지에 대해 세션을 유지 관리할 수 없음</li>
</ul>
</li>
</ul>
<p>buffer 속성</p>
<ul>
<li>현재 JSP 페이지의 출력 버퍼 크기를 설정하는 데 사용</li>
<li>속성 값: none과 &#39;버퍼 크기&#39;로 설정 - 버퍼 크기: 출력 버퍼에 먼저 기록한 후 웹 브라우저로 보냄
<img src="https://i.imgur.com/lXiQ6JY.png" alt=""></li>
</ul>
<p>tip) buffer 사용 이유: 작은 데이터를 여러번 전송하는 것보다, 데이터를 모아서 한번에 보내는 것이 효율적이기 때문</p>
<p>autoFlush 속성</p>
<ul>
<li>출력 버퍼를 자동으로 비울것인지 여부</li>
</ul>
<p>isThreadSafe 속성</p>
<ul>
<li>멀티스레드 처리 여부</li>
</ul>
<p>info 속성</p>
<ul>
<li>JSP 페이지 설명을 위한 설정 (<strong>주석문의 기능과 같음</strong>)</li>
</ul>
<p>errorPage 속성</p>
<ul>
<li>이동할 오류 페이지 ex) MyErrorPage.jsp 설정</li>
<li>웹 서버가 기본 제공하는 오류 페이지를 사용하지 않고 따로 오류페이지를 설정하는 것
<img src="https://i.imgur.com/aIhp2Fq.png" alt=""></li>
</ul>
<p>ex) 오류 발생 예제 (null.toString() 불가능)
<img src="https://i.imgur.com/EKK7BKP.png" alt=""></p>
<p>isErrorPage 속성</p>
<ul>
<li>현재 JSP 페이지가 오류 페이지인지 여부를 설정하는데 사용함</li>
<li>기본값은 false, 예외처리를 위한 내장 객체인 exception 변수를 사용할 수 없음</li>
<li>속성값은 true로 설정하면, 현재 jsp 페이지는 오류페이지가 됨</li>
<li>만약 다른 jsp 페이지에서 오류가 발생하면, 호출되는 오류페이지는 true가 설정된 페이지가 됨</li>
</ul>
<p>tip) MIME(Multipurpose Internet Mail Extensions)</p>
<ul>
<li>전자우편을 위한 인터넷 표준 포맷. 현재는 웹을 통해서 여러 형태의 파일 전달을 위해 사용</li>
</ul>
<p>tip) 표현언어(표현식) 사용법</p>
<ul>
<li>${2+5} // 7 출력</li>
<li>${2+5} // &quot;${2+5}&quot; 출력</li>
</ul>
<p><img src="https://i.imgur.com/kO7a4s4.png" alt=""></p>
<p>###</p>
<p>###</p>
<p>###</p>
<p>###</p>
<p>###</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[02 - [JSP 프로그래밍] 스크립트 태그]]></title>
            <link>https://velog.io/@flip_404/02-JSP-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%83%9C%EA%B7%B8</link>
            <guid>https://velog.io/@flip_404/02-JSP-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%83%9C%EA%B7%B8</guid>
            <pubDate>Tue, 18 Oct 2022 13:03:10 GMT</pubDate>
            <description><![CDATA[<p><img src="https://i.imgur.com/AcMiLCW.png" alt=""></p>
<p>본 게시글은 &quot;JSP 웹 프로그래밍&quot;을 학습하며, 내용 요약 또는 몰랐던 부분을 정리하는 글 입니다.</p>
<h2 id="스크립트-태그">스크립트 태그</h2>
<h3 id="스크립트-태그-1">스크립트 태그</h3>
<ul>
<li>&lt;% ... %&gt; 사용</li>
<li>JSP 페이지가 서블릿 프로그램에서 서블릿 클래스로 변환할 때<ul>
<li>JSP 컨테이너가 자바 코드가 삽입되어 있는 스크립트 태그를 처리하고 나머지는 HTML 코드나 일반 텍스트로 간주</li>
</ul>
</li>
</ul>
<p>자바 서블릿: 자바를 사용해서 웹 페이지를 동적으로 생성하는 서버 프로그램</p>
<p><img src="https://i.imgur.com/wlFf5OQ.png" alt=""></p>
<p><strong>선언문 태그(선언부)</strong>: &lt;%! ... ; %&gt; 자바 변수와 메소드 정의 (jspService() 메소드 외부에 배치가 되어있다.)</p>
<ul>
<li>변수: 전역 변수로 선언한다</li>
<li>메소드: 전역 메소드로 사용한다</li>
<li>변수와 메소드가 전역으로 사용되기 때문에 순서는 상관없다.
<strong>스크립틀릿 태그(처리부)</strong>: &lt;% ... ; %&gt; 자바 로직 코드 작성
<strong>표현문 태그(출력부)</strong>: &lt;%= ... %&gt; 선언문의 메소드를 호출하여 문자열 형태로 출력
<strong>주석</strong>: &lt;%-- ... --%&gt;</li>
</ul>
<p><img src="https://i.imgur.com/tX0QMCn.png" alt=""></p>
<p><img src="https://i.imgur.com/Pg9Fxdc.png" alt="">
JSP 생명주기의 (4)실행 단계에서 jspService()를 호출하여 스크립틀릿 태그(처리부)와 표현문 태그(출력부)를 처리한다.</p>
<p><strong>예시)</strong></p>
<p><img src="https://i.imgur.com/0VRAn3w.png" alt="">
아래와 같이 변환
<img src="https://i.imgur.com/PNh39UE.png" alt=""></p>
<h2 id="tip-한빛-아카데미-예제-소스로-실습-진행하기">tip) 한빛 아카데미 예제 소스로 실습 진행하기</h2>
<h2 id="스크립틀릿-태그의-기능과-사용법">스크립틀릿 태그의 기능과 사용법</h2>
<p><strong>스크립틀릿 태그</strong></p>
<ul>
<li>자바 코드로 이루어진 로직 부분을 표현</li>
<li>out 객체를 사용하지 않고도 쉽게 HTML 응답을 만들어냄</li>
<li><strong>지역 변수를 선언할 수 있다.(메소드는 선언할 수 없다.)</strong></li>
<li>_jspService() 메소드 내부에 배치된다.</li>
</ul>
<p><img src="https://i.imgur.com/t1AaYTO.png" alt="">
<img src="https://i.imgur.com/pwbOG5x.png" alt=""></p>
<h2 id="표현문-태그의-기능과-사용법">표현문 태그의 기능과 사용법</h2>
<p><strong>표현문 태그</strong></p>
<ul>
<li>웹 브라우저에 출력할 부분을 표현</li>
<li>표현문 태그에 숫자, 문자, 불린(Boolean) 등의 기본 데이터 타입과 <strong>자바 객체 타입도 사용 가능</strong></li>
<li><strong>각 행을 세미콜론으로 종료할 수 없음</strong><ul>
<li>출력부의 내용이 _jspService() 메소드의 out.println()의 매개변수로 들어가기 때문이다. (즉, 출력부 자체가 자바코드가 아니다.)
<img src="https://i.imgur.com/dcawj8o.png" alt=""></li>
</ul>
</li>
</ul>
<p>tip) html 주석 vs jsp 주석
html 주석처리는 서블릿으로 변환할 때 주석이 무시되지 않고 모두 컴파일 되지만, 페이지에서 렌더링을 안하는 것(?)
jsp 주석 처리는 서블릿으로 변환할 때 주석처리된 부분은 모두 무시한다.</p>
<h3 id="bootstrap-css-적용하기">bootstrap css 적용하기</h3>
<p><img src="https://i.imgur.com/TbLW0fT.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[01 - [JSP 프로그래밍] 웹과 JSP 프로그래밍 이해하기]]></title>
            <link>https://velog.io/@flip_404/01-JSP-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%9B%B9%EA%B3%BC-JSP-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@flip_404/01-JSP-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%9B%B9%EA%B3%BC-JSP-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 18 Oct 2022 13:02:34 GMT</pubDate>
            <description><![CDATA[<p><img src="https://i.imgur.com/AcMiLCW.png" alt=""></p>
<p>본 게시글은 &quot;JSP 웹 프로그래밍&quot;을 학습하며, 내용 요약 또는 몰랐던 부분을 정리하는 글 입니다.</p>
<h2 id="웹과-jsp-프로그래밍-이해하기">웹과 JSP 프로그래밍 이해하기</h2>
<h3 id="인터넷과-웹의-개요">인터넷과 웹의 개요</h3>
<p>인터넷</p>
<ul>
<li>컴퓨터가 서로 연결되어 TCP/IP라는 통신 프로토콜을 이용하여 정보를 주고 받는 전 세계의 컴퓨터 네트워크</li>
</ul>
<p>웹</p>
<ul>
<li>인터넷에 연결된 컴퓨터들을 통해 사람들이 정보를 공유할 수 있는 정보 공간</li>
<li>월드 와이드 웹(world wide web)의 줄임말</li>
</ul>
<p>웹의 동작 원리</p>
<ul>
<li>웹은 기본적으로 클라이언트/서버 방식으로 동작</li>
<li>기본적으로 http 통신 (요청/응답)</li>
</ul>
<p>가장 널리 쓰이는 웹 서버</p>
<ul>
<li>아파치</li>
<li>톰캣 (아파치 재단에서 배포)<ul>
<li>아파치 소프트웨어재단(Apache Software Foundation)에서 개발한 웹 어플리케이션 서버</li>
<li>자바로 만들어진 웹 페이지를 구동하기 위한 엔진</li>
</ul>
</li>
<li>IIS (윈도우에서만 동작)</li>
</ul>
<h3 id="정적-웹-페이지와-동적-웹-페이지">정적 웹 페이지와 동적 웹 페이지</h3>
<p>정적 웹 페이지</p>
<ul>
<li>컴퓨터에 저장된 텍스트 파일을 그대로 보는 것</li>
<li>서버: 이미 준비된 HTML 문서를 그대로 전달
<img src="https://i.imgur.com/Efryckf.png" alt=""></li>
</ul>
<p>동적 웹 페이지</p>
<ul>
<li>저장된 내용을 다른 변수로 가공 처리하여 보는 것</li>
<li>PHP(Personal Home Page), ASP(Active Server Page), JSP(Java Server Page)</li>
<li>서버: 사용자 요청에 맞게 정제된 HTML 문서를 전달
<img src="https://i.imgur.com/iMmEeYf.png" alt=""></li>
</ul>
<h3 id="웹-프로그래밍과-jspjavaserver-pages">웹 프로그래밍과 JSP(JavaServer Pages)</h3>
<p>웹 프로그래밍 언어</p>
<ul>
<li>클라이언트 측 실행 언어와 서버 측 실행 언어로 구분</li>
<li>자바를 기반으로 하는 <strong>JSP는 서버 측 웹 프로그래밍 언어</strong><ul>
<li>JSP: HTML내에 자바 코드를 삽입하여 웹 서버에서 동적으로 웹 페이지를 생성하여 웹 브라우저에 돌려주는 <strong>서버 사이드 스크립트 언어</strong></li>
</ul>
</li>
</ul>
<p>JSP 특징</p>
<ul>
<li>JSP는 서블릿 기술의 확장(서블릿의 모든 기능을 사용할 수 있다.)</li>
<li>JSP는 유지 관리가 용이</li>
<li>JSP는 빠른 개발이 가능</li>
<li>JSP로 개발하면 코드 길이를 줄일 수 있음</li>
</ul>
<h3 id="jsp-페이지의-처리-과정">JSP 페이지의 처리 과정</h3>
<p><img src="https://i.imgur.com/NhGLduG.png" alt=""></p>
<p>JSP 컨테이너에서 아래와 같은 동작들이 수행된다.</p>
<ol>
<li>JSP 요청 (Hello.jsp)</li>
<li>번역: 서블릿 프로그램 (Hello_jsp.java)</li>
<li>컴파일: 서블릿 클래스 (Hello_jsp.class 바이트 코드)</li>
</ol>
<h3 id="jsp-생명주기">JSP 생명주기</h3>
<p>각 단계별 함수 중요.</p>
<p><img src="https://i.imgur.com/aQgWill.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP 10장 - HTTP/2.0]]></title>
            <link>https://velog.io/@flip_404/HTTP-19%EC%9E%A5-HTTP2.0</link>
            <guid>https://velog.io/@flip_404/HTTP-19%EC%9E%A5-HTTP2.0</guid>
            <pubDate>Mon, 17 Oct 2022 13:48:57 GMT</pubDate>
            <description><![CDATA[<p><img src="https://i.imgur.com/FenftHu.png" alt="">
본 게시글은 &quot;HTTP 완벽 가이드&quot;를 학습하며, 내용 요약 또는 몰랐던 부분을 정리하는 글 입니다.</p>
<h1 id="http20">HTTP/2.0</h1>
<h2 id="1-http20의-등장-배경">1. HTTP/2.0의 등장 배경</h2>
<h3 id="http11의-특징">HTTP/1.1의 특징</h3>
<p>메시지 포맷 단순성과 접근성에 중심을 두고 최적화 되었다.
커넥션 하나를 통해서 요청&amp;응답을 하므로 회전 지연(latency)을 피할수 없다.</p>
<h3 id="http11-vs-http20">HTTP/1.1 vs HTTP/2.0</h3>
<ul>
<li>HTTP1.1은 커넥션 하나에 한 요청과 한 응답밖에 못받았다. 그래서 지연되는 문제때문에 여러 커넥션을 열고 이전에 보낸 요청에대한 응답이 안와도 다음 요청을 했는데, 이것도 열수있는 커넥션 수가 제한 되어 있기때문에 지연성 문제를 완전히 해결하지는 못했다.</li>
<li>2.0에서는 하나의 커넥션에 여러개의 스트림이 동시에 열릴수있다. 즉 하나의 HTTP/2.0 커넥션을 통해 여러개의 요청이 동시에 보내질 수 있어서 HTTP1.1에서 겪던 문제를 쉽게 해결 할 수 있다. </li>
<li>모든 스트림은 31비트의 무부호 정수로 된 고유한 식별자를 가진다</li>
<li>스트림이 클라이언트에 의해 초기화 되었다면 이 식별자는 홀수, 서버라면 짝수이다. 새로 만들어지는 스트림의 식별자는 이전에 만들어졌거나 예약된 식별자보다 반드시 커야한다. (어기면 PROTOCL_ERROR 라는 커넥션 에러가 발생한다.)</li>
<li>한번사용한 스트림 식별자는 다시 사용할 수 없는데 그렇다면 커넥션을 다시 맺으면 된다.</li>
<li>서버와 클라이언트는 커넥션을 맺고나서 스트림을 상대방과 협상 없이 일방적으로 만든다. 즉, TCP 패킷을 주고받는데 시간을 안써도 된다.</li>
<li>헤더 압축<ul>
<li>HTTP1.1 에서는 헤더를 그냥 보냈는데 이제는 페이지 하나볼려고 수백번 요청을 해야할 수도 있기때문에 헤더가 크면 느려진다. -&gt; 그래서 압축한다 </li>
</ul>
</li>
</ul>
<h2 id="2-개요">2. 개요</h2>
<ul>
<li>HTTP/2.0은 서버와 클라이언트 사이의 TCP커넥션 위에서 동작한다.</li>
<li>요청과 응답은 스트림을 통해 보내지며, 하나의 커넥션에 여러개의 스트림이 동시에 만들어질 수 있다.</li>
<li>스트림에 대한 흐름 제어와 우선순위 부여 기능을 제공한다.</li>
<li>서버는 클라이언트에게 필요하다고 생각하는 리소스라면 그에 대한 요청을 명시적으로 받지 않더라도 능동적으로 클라이언트에게 보내줄 수 있다.<h2 id="3-http11과의-차이점">3. HTTP/1.1과의 차이점</h2>
<h3 id="31-프레임">3.1 프레임</h3>
HTTP/2.0에서 모든 메시지는 프레임에 담겨 전송된다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/flip_404/post/8e087266-9ce8-4dc3-bb5d-bbed51b53975/image.png" alt=""></p>
<ul>
<li>R: 예약된 2비트 필드, 값의 의미가 정의되어 있지 않으며, 반드시 0이어 한다.</li>
<li>길이: 페이로드의 길이를 나타내는 14비트 무부호 정수</li>
<li>종류: 프레임의 종류</li>
<li>플래그: 플래그 값의 의미는 프레임의 종류에 따라 다르다.</li>
<li>R: 예약된 1비트 필드, 값의 의미가 정의되어 있지 않으며, 반드시 0이어야 한다.</li>
<li>스트림 식별자: 31비트 스트림 식별자, 커넥션 전체와 연관된 프레임을 의미한다.</li>
</ul>
<h3 id="32-스트림과-멀티플렉싱">3.2 스트림과 멀티플렉싱</h3>
<ul>
<li>HTTP/2.0 커넥션을 통해 클라이언트와 서버 사이에서 교환되는 프레임들의 독립된 양방향 시퀀스</li>
<li>한쌍의 HTTP요청과 응답은 하나의 스트림을 통해 이루어진다.</li>
<li>하나의 커넥션에 여러 개의 스트림이 동시에 열릴 수 있다.</li>
<li>스트림은 우선순위 따라서 요청이 처리 될 수 있다.</li>
</ul>
<h3 id="33-헤더-압축">3.3 헤더 압축</h3>
<ul>
<li>과거에는 웹페이지 방문시에 요청이 많지 않았기 때문에 헤더의 크기가 큰 문제가 되지 않았으나, 최근에는 웹페이지 하나에 수십, 수백개의 요청이 이루어지므로 헤더의 크기가 회전 지연과 대역폭에 영향을 끼친다.</li>
<li>HTTP/2.0에서는 메시지의 헤더를 합축하여 전송한다.</li>
</ul>
<h3 id="34-서버-푸시">3.4 서버 푸시</h3>
<ul>
<li>HTTP/2.0은 서버가 하나의 요청에 대한 응답으로 여러 개의 리소스를 보낼 수 있도록 해준다.</li>
<li>서버가 클라이언트에게 어떤 리소스를 요구할 것인지 미리 알 수 있는 상황에서 유용하다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[01 - [typescript] 타입스크립트 알아보기]]></title>
            <link>https://velog.io/@flip_404/01-typescript-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@flip_404/01-typescript-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Sat, 17 Sep 2022 00:53:28 GMT</pubDate>
            <description><![CDATA[<p><img src="https://i.imgur.com/uI4ROFr.png" alt=""></p>
<p>본 게시글은 &quot;이펙티브 타입스크립트&quot;를 학습하며, 내용 요약 또는 몰랐던 부분을 정리하는 글 입니다.</p>
<h2 id="아이템-1-타입스크립트와-자바스크립트-관계-이해하기">아이템 1. 타입스크립트와 자바스크립트 관계 이해하기</h2>
<p>타입스크립트는 문법적으로 자바스크립트의 상위집합.</p>
<p>→ 자바스크립트 프로그램에 문법 오류가 없다면, 유효한 타입스크립트 프로그램이라고 할 수 있다.</p>
<p>→ 자바스크립트 프로그램에 어떤 이슈가 존재한다면 문법 오류가 아니라도 타입 체커에게 지적당할 가능성이 높다.</p>
<p>→ 문법의 유효성과 동작의 이슈는 독립적인 문제</p>
<p>타입 시스템의 목표 중 하나는 런타임에 오류를 발생시킬 코드를 미리 찾아내는 것이다.</p>
<p>→ 그러나 타입 체커가 모든 오류를 찾아내지는 않음</p>
<p>→ <strong>타입 체커를 통과하면서 런타임 오류를 발생시키는 코드는 충분히 존재.</strong></p>
<p>타입스크립트 타입 시스템은 자바스크립트의 런타임 동작을 &#39;모델링&#39; 한다.</p>
<pre><code class="language-typescript">const x = 2 + &quot;3&quot;; // 정상, string
const y = &quot;2&quot; + 3; // 정상, string</code></pre>
<p>이 예제는 다른 언어였다면 런타임 오류가 될 만한 코드이다. 하지만 타입스크립트의 타입 체커는 정상으로 인식한다.</p>
<h2 id="아이템-2-타입스크립트-설정-이해하기">아이템 2. 타입스크립트 설정 이해하기</h2>
<p>타입스크립트의 noImplicitAny 설정은 변수들이 미리 정의된 타입을 가져야 하는지 여부를 제어한다.</p>
<p>→ 해제하면 다음과 같은 코드도 유효하다.</p>
<pre><code class="language-typescript">function add(a, b) {
  return a + b;
}

// noImplicitAny : false

// function add(a: any, b:any): any {
// ...</code></pre>
<p>any 타입을 매개변수에 사용하면 타입 체커는 속절없이 무력해진다. any는 유용하지만 매우 주의해서 사용해야 한다.</p>
<p>any를 코드에 넣지 않았지만, any 타입으로 간주하는 경우 이를 암시적 any(implicit any)라고 한다.</p>
<p>타입스크립트는 타입 정보를 가질 때 가장 효과적이기 때문에, 되도록 noImplicitAny를 설정해야 한다.</p>
<p>strictNullChecks는 null과 undefined가 모든 타입에서 허용되는지 확인하는 설정이다.</p>
<pre><code class="language-typescript">// strictNullChecks : false
const x: number = null; // 정상

// strictNullChecks : true
const x: number = null; // 에러, &#39;null&#39; 형식은 &#39;number&#39; 형식에 할당할 수 없다.</code></pre>
<p>undefined는 객체가 아닙니다같은 런타임 오류를 방지하기 위해서는 strictNullChecks 를 설정하는 것이 좋다.</p>
<p><strong>컴파일러가 null 또는 undefined 를 제거할 수없는 경우, 타입 선언 연산자를 사용하여 수동으로 제거할 수 있다. 구문은 변수 뒤에 ! 를 붙이는 것입니다. identifier! 는 식별자의 타입에서 null 과 undefined 를 제거한다.</strong></p>
<h2 id="아이템-3-코드-생성과-타입이-관계없음을-이해하기">아이템 3. 코드 생성과 타입이 관계없음을 이해하기</h2>
<p>큰 그림에서 보면, 타입스크립트 컴파일러는 두 가지 역할을 수행한다.</p>
<p>최신 타입스크립트/자바스크립트를 브라우저에서 동작할 수 있도록 구버전의 자바스크립트를 트랜스파일(transpile)한다.
코드의 타입 오류를 체크한다.
이 두 가지는 완전히 독립적이다.</p>
<p>타입 오류가 있는 코드도 컴파일이 가능하다.
타입체크와 컴파일이 동시에 이루어지는 C나 자바 같은 언어를 사용하던 사람이라면 이러한 상황이 황당하다. 타입스크립트 오류는 C나 자바 같은 언어들의 경고(warning)와 비슷하다. 문제가 될 만한 부분을 알려주지만, 그렇다고 빌드를 멈추지는 않는다.</p>
<p>런타임에서는 타입 체크가 불가능하다.</p>
<pre><code class="language-typescript">interface Square {
  width: number;
}

interface Rectangle extends Square {
  height: number;
}

type Shape = Square | Rectangle;

function calculateArea(shape: Shape) {
  if (shape instanceof Rectangle) {
    // ~~ &#39;Rectangle&#39;은 형식만 참조하지만, 여기서는 값으로 사용되고 있습니다.
    return shape.width * shape.height; // &#39;Shape&#39; 형식에 &#39;height&#39; 속성이 없습니다.
  } else {
    return shape.width * shape.width;
  }
}</code></pre>
<p>instanceof 체크는 런타임에 일어나지만, Rectangle은 타입이기 때문에 런타임 시점에 아무런 역할을 할 수 없다. 타입스크립트의 타입은 &#39;제거 가능(eraseable)&#39;하다. 실제로 자바스크립트로 컴파일되는 과정에서 모든 인터페이스, 타입, 타입 구문은 그냥 제거되어 버린다.</p>
<p>위의 코드에서 다루고 있는 shape 타입을 명확하게 하려면, 런타임에 타입 정보를 유지하는 방법이 필요하다. 하나의 방법은 height 속성이 존재하는지 체크해 보는 것이다.</p>
<pre><code class="language-typescript">function calculateArea(shape: Shape) {
  if (&quot;height&quot; in shape) {
    shape; // 타입이 Rectangle
    return shape.width * shape.height;
  } else {
    shape; // 타입이 Square
    return shape.width * shape.width;
  }
}</code></pre>
<p>속성 체크는 런타임에 접근 가능한 값에만 관련되지만, 타입 체커 역시도 shape의 타입을 Rectangle로 보정해 주기 때문에 오류가 사라진다.</p>
<p>타입(런타임 접근 불가)과 값(런타임 접근 가능)을 둘 다 사용하는 기법도 있다. 타입을 클래스로 만들면 된다. Square와 Rectangle을 클래스로 만들면 오류를 해결할 수 있다.</p>
<pre><code class="language-typescript">class Square {
  constructor(public width: number) {}
}

class Rectangle extends Square {
  constructor(public width: number, public height: number) {
    super(width);
  }
}

type Shape = Square | Rectangle;

function calculateArea(shape: Shape) {
  if (shape instanceof Rectangle) {
    shape; // 타입이 Rectangle
    return shape.width * shape.height;
  } else {
    shape; // 타입이 Square
    return shape.width * shape.width;
  }
}</code></pre>
<p>인터페이스는 타입으로만 사용 가능하지만, Rectangle을 클래스로 선언하면 타입과 값으로 모두 사용할 수 있으므로 오류가 없다.</p>
<p>타입스크립트 타입으로는 함수를 오버로드 할 수 없다.
C++ 같은 언어는 동일한 이름에 매개변수만 다른 여러 버전의 함수를 허용한다. 이를 &#39;함수 오버로딩&#39; 이라고 한다. 그러나 타입스크립트에서는 타입과 런타임의 동작이 무관하기 때문에 함수 오버로딩은 불가하다.</p>
<pre><code class="language-typescript">function add(a: number, b: number) {
  return a + b;
} // ~~~ 중복된 함수 구현입니다.
function add(a: string, b: string) {
  return a + b;
} // ~~~ 중복된 함수 구현입니다.</code></pre>
<p>타입스크립트가 함수 오버로딩 기능을 지원하기는 하지만, 온전히 타입 수준에서만 동작한다. 하나의 함수에 대해 여러 개의 선언문을 작성할 수 있지만, 구현체(implementation)는 오직 하나뿐이다.</p>
<pre><code class="language-typescript">function add(a: number, b: number): number;
function add(a: string, b: string): string;

function add(a, b) {
  return a + b;
}

const three = add(1, 2); // 타입이 number;
const twelve = add(&quot;1&quot;, &quot;2&quot;); // 타입이 string;</code></pre>
<p>add에 대한 처음 두 개의 선언문은 타입 정보를 제공할 뿐이다. 이 두 선언문은 타입스크립트가 자바스크립트로 변환되면서 제거되며, 구현체만 남게 된다.</p>
<p>타입스크립트 타입은 런타임 성능에 영향을 주지 않는다.
타입과 타입 연산자는 자바스크립트 변환 시점에 제거되기 때문에, 런타임의 성능에 아무런 영향을 주지 않는다.</p>
<p>&#39;런타임&#39; 오버헤드가 없는 대신, 타입스크립트 컴파일러는 &#39;빌드타임&#39; 오버헤드가 있다.</p>
<h2 id="아이템-4-구조적-타이핑에-익숙해지기">아이템 4. 구조적 타이핑에 익숙해지기</h2>
<p>자바스크립트는 본질적으로 덕 타이핑(duck typing) 기반이다. 만약 어떤 함수의 매개변수 값이 모두 제대로 주어진다면, 그 값이 어떻게 만들어 졌는지 신경쓰지 않고 사용한다.</p>
<p>덕 타이핑이란, 객체가 어떤 타입에 부합하는 변수와 메서드를 가질 경우 객체를 해당 타입에 속하는 것으로 간주하는 방식이다.</p>
<pre><code class="language-typescript">interface Vector2D {
  x: number;
  y: number;
}

// 벡터의 길이를 구하는 함수 (2D)
function calculateLength(v: Vector2D) {
  return Math.sqrt(v.x * v.x + v.y * v.y);
}

interface Vector3D {
  x: number;
  y: number;
  z: number;
}

// 3D 벡터의 길이를 1로 만드는 정규화 함수
function normalize(v: Vector3D) {
  const length = calculateLength(v);
  return {
    x: v.x / length,
    y: v.y / length,
    z: v.z / length,
  };
}

// 함수 normalize는 1보다 조금 더 긴 길이를 가진 결과를 출력
normalize({ x: 3, y: 4, z: 5 });
// {x: 0.6, y: 0.8, z: 1}</code></pre>
<p>여기서 타입스크립트는 오류를 잡지 못했다. 그 이유는 calculateLength는 2D 벡터를 기반으로 연산하는데, 버그로 인해 normalize가 3D 벡터로 연산되었기 때문이다. z가 정규화 과정에서 무시되었다. 타입 체커는 이 문제를 잡아내지 못했다.</p>
<p>이 원인을 조금 더 살펴보면, Vector3D와 호환되는 {x, y, z} 객체로 calculateLength를 호출하면, 구조적 타이핑 관점에서 x, y가 있어서 Vector2D와 호환된다. 따라서 오류가 발생하지 않았고, 타입 체커가 문제로 인식하지 않았다.</p>
<p>구조적 타이핑은 클래스와 관련된 할당문에서도 당황스러운 결과를 보여줄 수 있다.</p>
<pre><code class="language-typescript">class C {
  foo: string;
  constructor(foo: string) {
    this.foo = foo;
  }
}

const c = new C(&quot;instance of C&quot;);
const d: C = { foo: &quot;object literal&quot; }; // 정상</code></pre>
<p>d가 C 타입에 할당되는 이유를 살펴보자. d는 string 타입의 foo 속성을 가진다. 그리고 하나의 매개변수로 호출되는 생성자(Object.prototype으로부터 비롯된)를 가진다. 그래서 구조적으로는 필요한 속성과 생성자가 존재하기 때문에 문제가 없다.</p>
<p>만약 C의 생성자에 단순 할당이 아닌 연산 로직이 존재한다면, d의 경우는 생성자를 실행하지 않으므로 문제가 발생하게 된다. 이러한 부분이 C타입의 매개변수를 선언하여 C 또는 서브클래스임을 보장하는 C++이나 자바 같은 언어와 매우 다른 특징이다.</p>
<h2 id="아이템-5-any-타입-지양하기">아이템 5. any 타입 지양하기</h2>
<p>일부 특별한 경우를 제외하고는 any를 사용하면 타입스크립트의 수많은 장점을 누릴 수 없게 된다. 부득이하게 any를 사용하더라도 그 위험성을 알고 있어야 한다.</p>
<p>any 타입에는 타입 안정성이 없다.
any는 함수 시그니처를 무시한다.
아래의 코드에서 birthDate 매개변수는 string이 아닌 Date 타입이어야 한다. any 타입을 사용하면 calculateAge의 시그니처를 무시하게 된다. 자바스크립트에서는 종종 암시적으로 타입이 변환되기 때문에 이런 경우 특히 문제가 될 수 있다.</p>
<pre><code class="language-typescript">function calculateAge(birthDate: Date): number {
  // ...
}

let birthDate: any = &quot;1900-01-01&quot;;
calculateAge(birthDate); // 정상</code></pre>
<p>any 타입에는 언어 서비스가 적용되지 않는다.
any 타입은 코드 리팩터링 때 버그를 감춘다.
any는 타입 설계를 감춰버린다.
any는 타입시스템의 신뢰도를 떨어뜨린다.
any 타입을 쓰지 않으면 런타임에 발견될 오류를 미리 잡을 수 있고 신뢰도를 높일 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[41 - [JavaScript] 타이머
]]></title>
            <link>https://velog.io/@flip_404/41-JavaScript-%ED%83%80%EC%9D%B4%EB%A8%B8</link>
            <guid>https://velog.io/@flip_404/41-JavaScript-%ED%83%80%EC%9D%B4%EB%A8%B8</guid>
            <pubDate>Sat, 03 Sep 2022 01:02:27 GMT</pubDate>
            <description><![CDATA[<p><img src="https://i.imgur.com/LOqoalW.png" alt=""></p>
<p>본 게시글은 &quot;모던 자바스크립트 Deep Dive&quot;를 학습하며, 내용 요약 또는 몰랐던 부분을 정리하는 글 입니다.</p>
<h1 id="타미어">타미어</h1>
<h2 id="1-호출-스케줄링">1. 호출 스케줄링</h2>
<p>함수를 명시적으로 호출하면 함수가 즉시 실행된다.
만약 함수를 명시적으로 호출하지 않고 일정 시간이 경과된 이후에 호출되도록 함수 호출을 예약하려면 타이머 함수를 사용한다. 이를 호출 스케줄링이라 한다.</p>
<p>자바스크립트는 타이머를 생성할 수 있는 타이머 함수 setTime과 setInterval, 타이머를 제거할 수 있는 타이머 함수 clearTimeout과 clearInterval을 제공한다.
타이머 함수는 ECMAScript 사양에 정의된 빌트인 함수가 아니다. 하지만 브라우저 환경과 Node.js 환경에서 모두 전역 객체의 메서드로서 타이머 함수를 제공한다. 즉, 타이머 함수는 호스트 객체다.</p>
<p>setTimeout 함수가 생성한 타이머는 단 한 번만 동작하고, setInterval 함수가 생성한 타이머는 반복 동작한다.</p>
<p>자바스크립트 엔진은 단 하나의 실행 컨텍스트 스택을 갖기 때문에 두 가지 이상의 태스크를 동시에 실행할 수 없다.
즉, 자바스크립트 엔진은 싱글 스레드로 동작한다. 이런 이유로 타이머 함수 setTimeout과 setInterval은 비동기 처리방식으로 동작한다.</p>
<h2 id="2-타이머-함수">2. 타이머 함수</h2>
<h3 id="1-settimeout-cleartimeoutpermalink">(1) setTimeout/ clearTimeoutPermalink</h3>
<p>setTimeout 함수는 두 번째 인수로 전달받은 시간(ms, 1/1000초)으로 단 한 번 동작하는 타이머를 생성한다.
이후 타이머가 만료되면 첫 번째 인수로 전달받은 콜백 함수가 호출된다.</p>
<pre><code class="language-javascript">const timeoutId = setTimeout(func|code[, delay, param1, param2, ...]);</code></pre>
<p><img src="https://i.imgur.com/bphRoFO.png" alt=""></p>
<pre><code class="language-javascript">//1초(1000ms) 후 타이머가 만료되면 콜백 함수가 호출된다.
setTimeout(() =&gt; console.log(&quot;Hi!&quot;), 1000);

//1초(1000ms) 후 타이머가 만료되면 콜백 함수가 호출된다.
//이때 콜백 함수가 &#39;Lee&#39;가 인수로 전달된다.
setTimeout((name) =&gt; console.log(`Hi! ${name}.`), 1000, &quot;Lee&quot;);

//두 번째 인수(delay)를 생략하면 기본값 0이 지정된다.
setTimeout(() =&gt; console.log(&quot;Hello!&quot;));</code></pre>
<p><strong>setTimeout 함수는 생성된 타이머를 식별할 수 있는 고유한 타이머 id를 반환</strong>한다.
<strong>setTimeout 함수가 반환한 타이머 id는 브라우저 환경인 경우 숫자이며 Node.js 환경인 경우 객체다.</strong></p>
<p>setTimeout 함수가 반환한 타이머 id를 clearTimeout 함수의 인자로 전달하여 타이머를 취소할 수 있다.
즉, clearTimeout 함수는 호출 스케줄링을 취소한다.</p>
<pre><code class="language-javascript">//1초(1000ms) 후 타이머가 만료되면 콜백 함수가 호출된다.
//setTimeout 함수는 생성된 타이머를 식별할 수 있는 고유한 타이머 id를 반환한다.
const timeId = setTimeout(() =&gt; console.log(&quot;Hi!&quot;), 1000);

//setTimeout 함수가 반환한 타이머 id를 clearTimeout 함수의 인수로 전달하여 타이머를 취소한다.
//타이머가 취소되면 setTimeout 함수의 콜백 함수가 실행되지 않는다.
clearTimeout(timerId);</code></pre>
<h3 id="2-setinterval-clearintervalpermalink">(2) setInterval/ clearInterValPermalink</h3>
<p>setInterval 함수는 두 번째 인수로 전달받은 시간(ms, 1/1000초)으로 반복 동작하는 타이머를 생성한다.
이후 타이머가 만료될 때까지 첫 번째 인수로 전달받은 콜백 함수가 반복 호출된다.
이는 타이머가 취소될 때까지 계속된다.</p>
<pre><code class="language-javascript">const timerId = setInterval(func|code[, delay, param1, param2, ...]);
</code></pre>
<p>setInterval 함수는 생성된 타이머를 식별할 수 있는 고유한 타이머 id를 반환한다.</p>
<p>setInterval 함수가 반환한 타이머 id를 clearInterval 함수의 인수로 전달하여 타이머를 취소할 수 있다.
즉, clearInterval 함수는 호출 스케줄링을 취소한다.</p>
<pre><code class="language-javascript">let count = 1;

//1초(1000ms) 후 타이머가 만료될 때마다 콜백 함수가 호출된다.
//setInterval 함수는 생성된 타이머를 식별할 수 있는 고유한 타이머 id를 반환한다.
const timeoutId = setInterval(() =&gt; {
  console.log(count); //
  //count가 5이면 setInterval 함수가 반환한 타이머 id를 clearInterval 함수의 인수로 전달하여
  //타이머를 취소한다. 타이머가 취소되면 setInterval 함수의 콜백 함수가 실행되지 않는다.
  if (count++ === 5) clearInterval(timeout);
}, 1000);</code></pre>
<h2 id="3-디바운스와-스로틀">3. 디바운스와 스로틀</h2>
<p>scroll, resize, input, mousemove 같은 이벤트는 짧은 시간 간격으로 연속해서 발생한다.
이러한 이벤트에 바인딩한 이벤트 핸들러는 과도하게 호출되어 성능에 문제를 일으킬 수 있다.
<strong>디바운스와 스로틀은 짧은 시간 간격으로 연속해서 발생하는 이벤트를 그룹화해서 과도한 이벤트 핸들러의 호출을 방지하는 프로그래밍 기법이다.</strong></p>
<h3 id="1-디바운스">(1) 디바운스</h3>
<p>디바운스는 짧은 시간 간격으로 이벤트가 연속해서 발생하면 이벤트 핸들러를 호출하지 않다가
일정 시간이 경과한 이후에 이벤트 핸들러가 한 번만 호출되도록 한다.
즉, 디바운스는 짧은 시간 간격으로 발생하는 이벤트를 그룹화해서 마지막에 한 번만 이벤트 핸들러가 호출되도록 한</p>
<pre><code class="language-javascript">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;body&gt;
  &lt;input type=&quot;text&quot;&gt;
  &lt;div class=&quot;msg&quot;&gt;&lt;/div&gt;
  &lt;script&gt;
    const $input = document.querySelector(&#39;input&#39;);
    const $msg = document.querySelector(&#39;.msg&#39;);

    const debounce = (callback, delay) =&gt; {
      let timerId;
      //debounce 함수는 timerId를 기억하는 클로저를 반환한다.
      return event =&gt; {
        //delay가 경과하기 이전에 이벤트가 발생하면 이전 타이머를 취소하고 새로운 타이머를 재설정한다.
        //따라서 delay보다 짧은 간격으로 이벤트가 발생하면 callback은 호출되지 않는다.
        if (timerId) clearTimeout(timerId);
        timerId = setTimeout(callback, delay, event);
      };
    };

   //debounce 함수가 반환하는 클로저가 이벤트 핸들러로 등록된다.
   //300ms보다 짧은 간격으로 input 이벤트가 발생하면 debounce 함수의 콜백 함수는
   //호출되지 않다가 300ms 동안 input 이벤트가 더 이상 발생하지 않으면 한 번만 호출된다.
   $input.oninput = debounce(e =&gt; {
    $msg.textContent = e.target.value;
   }, 300);
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="2-스로틀">(2) 스로틀</h3>
<p>스로틀은 짧은 시간 가격으로 이벤트가 연속해서 발생하더라도 일정 시간 간격으로 이벤트 핸들러가 최대 한 번만 호출되도록 한다. 즉, 스로틀은 짧은 시간 간격으로 연속해서 발생하는 이벤트를 그룹화해서 일정 시간 단위로 이벤트 핸들러가 호출되도록 호출 주기를 만든다.</p>
<pre><code class="language-javascript">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;style&gt;
    .container {
      width: 300px;
      height: 300px;
      background-color: rebeccapurple;
      overflow: scroll;
    }

    .content {
      width: 300px;
      height: 1000vh;
    }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;div class=&quot;container&quot;&gt;
    &lt;div class=&quot;content&quot;&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div&gt;
    일반 이벤트 핸들러가 scroll 이벤트를 처리한 횟수:
    &lt;span class=&quot;normal-count&quot;&gt;0&lt;/span&gt;
  &lt;/div&gt;
  &lt;div&gt;
    스로틀 이벤트 핸들러가 scroll 이벤트를 처리한 횟수:
    &lt;span class=&quot;throttle-count&quot;&gt;0&lt;/span&gt;
  &lt;/div&gt;

  &lt;script&gt;
    const $container = document.querySelector(&#39;.container&#39;);
    const $normalCount = document.querySelector(&#39;.normal-count&#39;);
    const $throttleCount = document.querySelector(&#39;.throttle-count&#39;);

    const throttle = (callback, delay) =&gt; {
      let timerId;
      //throttle 함수는 timerId를 기억하는 클로저를 반환한다.
      return event =&gt; {
        //delay가 경과하기 이전에 이벤트가 발생하면 아무것도 하지 않다가
        //delay가 경과했을 때 이벤트가 발생하면 새로운 타이머를 재설정한다.
        //따라서 delay 간격으로 callback이 호출된다.
        if (timerId) return;
        timerId = setTimeout(() =&gt; {
          callback(event);
          timerId = null;
        }, delay, event);
      };
    };

    let normalCount = 0;
    $container.addEventListener(&#39;scroll&#39;, () =&gt; {
      $normalCount.textContent = ++normalCount;
    });

    let throttleCount = 0;
    //throttle 함수가 반환하는 클로저가 이벤트 핸들러로 등록된다.
    $container.addEventListener(&#39;scroll&#39;, throttle(() =&gt; {
      $throttleCount.textContent = ++throttleCount;
    }, 100));
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>![](<a href="https://i.imgur.com/OglXrsH">https://i.imgur.com/OglXrsH</a>.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[36 - [JavaScript] 디스트럭처링 할당
]]></title>
            <link>https://velog.io/@flip_404/36-JavaScript-%EB%94%94%EC%8A%A4%ED%8A%B8%EB%9F%AD%EC%B2%98%EB%A7%81-%ED%95%A0%EB%8B%B9</link>
            <guid>https://velog.io/@flip_404/36-JavaScript-%EB%94%94%EC%8A%A4%ED%8A%B8%EB%9F%AD%EC%B2%98%EB%A7%81-%ED%95%A0%EB%8B%B9</guid>
            <pubDate>Wed, 17 Aug 2022 03:11:43 GMT</pubDate>
            <description><![CDATA[<p><img src="https://i.imgur.com/LOqoalW.png" alt=""></p>
<p>본 게시글은 &quot;모던 자바스크립트 Deep Dive&quot;를 학습하며, 내용 요약 또는 몰랐던 부분을 정리하는 글 입니다.</p>
<h1 id="디스트럭처링-할당">디스트럭처링 할당</h1>
<p>디스트럭처링 할당(구조 분해 할당)은 구조화된 배열과 같은 이터러블 또는 객체를 destructuring(비구조화. 구조 파괴)하여 1개 이상의 변수에 개별적으로 할당하는 것을 말한다. 배열과 같은 이터러블 또는 객체 리터럴에서 필요한 값만 추출하여 변수에 할당할 때 유용하다.</p>
<h2 id="배열-디스트럭처링-할당">배열 디스트럭처링 할당</h2>
<p>ES5에서 구조화된 배열을 디스트럭처링하여 1개 이상의 변수에 할당하는 방법은 다음과 같다.</p>
<pre><code class="language-javascript">// ES5
var arr = [1, 2, 3];

var one = arr[0];
var two = arr[1];
var three = arr[2];

console.log(one, two, three); // 1 2 3
ES6의 배열 디스트럭처링 할당은 배열의 각 요소를 배열로부터 추출하여 1개 이상의 변수에 할당한다. 이때 배열 디스트럭처링 할당의 대상(할당문의 우변)은 이터러블이어야 하며, 할당 기준은 배열의 인덱스다. 즉, 순서대로 할당된다.

const arr = [1, 2, 3];

// ES6 배열 디스트럭처링 할당
// 변수 one, two, three를 선언하고 배열 arr을 디스트럭처링하여 할당한다.
// 이때 할당 기준은 배열의 인덱스다.
const [one, two, three] = arr;

console.log(one, two, three); // 1 2 3</code></pre>
<p>배열 디스트럭처링 할당을 위해서는 할당 연산자 왼쪽에 값을 할당받을 변수를 선언해야 한다. 이때 변수를 배열 리터럴 형태로 선언한다.</p>
<pre><code class="language-javascript">const [x, y] = [1, 2];</code></pre>
<p>배열 디스트럭처링 할당의 변수 선언문은 다음처럼 선언과 할당을 분리할 수도 있다. 단, 이 경우 const 키워드로 변수를 선언할 수 없으므로 권장하지 않는다.</p>
<pre><code class="language-javascript">let x, y;
[x, y] = [1, 2];</code></pre>
<p>배열 디스트럭처링 할당의 기준은 배열의 인덱스다. 즉, 순서대로 할당된다. 이때 변수의 개수와 이터러블의 요소 개수가 반드시 일치할 필요는 없다.</p>
<pre><code class="language-javascript">const [a, b] = [1, 2];
console.log(a, b); // 1 2

const [c, d] = [1];
console.log(c, d); // 1 undefined

const [e, f] = [1, 2, 3];
console.log(e, f); // 1 2

const [g, , h] = [1, 2, 3];
console.log(g, h); // 1 3</code></pre>
<p>배열 디스트럭처링 할당을 위한 변수에 기본값을 설정할 수 있다.</p>
<pre><code>// 기본값
const [a, b, c = 3] = [1, 2];
console.log(a, b, c); // 1 2 3

// 기본값보다 할당된 값이 우선한다.
const [e, f = 10, g = 3] = [1, 2];
console.log(e, f, g); // 1 2 3</code></pre><p>배열 디스트럭처링 할당은 배열과 같은 이터러블에서 필요한 요소만 추출하여 변수에 할당하고 싶을 때 유용하다. 다음 예제는 URL을 파싱하여 protocol, host, path 프로퍼티를 갖는 객체를 생성해 반환한다.</p>
<pre><code class="language-javascript">// url을 파싱하여 protocol, host, path 프로퍼티를 갖는 객체를 생성해 반환한다.
function parseURL(url = &quot;&quot;) {
// &#39;://&#39; 앞의 문자열(protocol)과 &#39;/&#39; 이전의 &#39;/&#39;으로 시작하지 않는 문자열(host)과
// &#39;/&#39; 이후의 문자열(path)을 검색한다.
const parsedURL = url.match(/^(\w+):\/\/([^/]+)\/(._)$/);
console.log(parsedURL);
/_
[
&#39;https://developer.mozilla.org/ko/docs/Web/JavaScript&#39;,
&#39;https&#39;,
&#39;developer.mozilla.org&#39;,
&#39;ko/docs/Web/JavaScript&#39;,
index: 0,
input: &#39;https://developer.mozilla.org/ko/docs/Web/JavaScript&#39;,
groups: undefined
]
\*/

if (!parsedURL) return {};

// 배열 디스트럭처링 할당을 사용하여 이터러블에서 필요한 요소만 추출한다.
const [, protocol, host, path] = parsedURL;
return { protocol, host, path };
}

const parsedURL = parseURL(&#39;https://developer.mozilla.org/ko/docs/Web/JavaScript&#39;);
console.log(parsedURL);
/_
{
protocol: &#39;https&#39;,
host: &#39;developer.mozilla.org&#39;,
path: &#39;ko/docs/Web/JavaScript&#39;
}
_/</code></pre>
<p>배열 디스트럭처링 할당을 위한 변수에 Rest 파라미터와 유사하게 Rest 요소 ...을 사용할 수 있다. Rest 요소는 Rest 파라미터와 마찬가지로 반드시 마지막에 위치해야 한다.</p>
<pre><code class="language-javascript">// Rest 요소
const [x, ...y] = [1, 2, 3];
console.log(x, y); // 1 [ 2, 3 ]</code></pre>
<h2 id="객체-디스트럭처링-할당">객체 디스트럭처링 할당</h2>
<p>ES5에서 객체의 각 프로퍼티를 객체로부터 디스트럭처링하여 변수에 할당하기 위해서는 프로퍼티 키를 사용해야 한다.</p>
<pre><code class="language-javascript">// ES5
var user = { firstName: &quot;Kyeom&quot;, lastName: &quot;Kim&quot; };

var firstName = user.firstName;
var lastName = user.lastName;

console.log(firstName, lastName); // Kyeom Kim</code></pre>
<p>ES6의 객체 디스트럭처링 할당은 객체의 각 프로퍼티를 객체로부터 추출하여 1개 이상의 변수에 할당한다. 이때 객체 디스트럭처링 할당의 대상(할당문의 우변)은 객체이어야 하며, 할당 기준은 프로퍼티 키다. 즉, 순서는 의미가 없으며 선언된 변수 이름과 프로퍼티 키가 일치하면 할당된다.</p>
<pre><code class="language-javascript">const user = { firstName: &quot;Kyeom&quot;, lastName: &quot;Kim&quot; };

// ES6 객체 디스트럭처링 할당
// 변수 lastName, firstName을 선언하고 user 객체를 디스트럭처링하여 할당한다.
// 이때 프로퍼티 키를 기준으로 디스트럭처링 할당이 이루어진다. 순서는 의미가 없다.
const { lastName, firstName } = user;

console.log(firstName, lastName); // Kyeom Kim</code></pre>
<p>배열 디스트럭처링 할당과 마찬가지로 객체 디스트럭처링 할당을 위해서는 할당 연산자 왼쪽에 프로퍼티 값을 할당받을 변수를 선언해야 한다. 이때 변수를 객체 리터럴 형태로 선언한다.</p>
<pre><code class="language-javascript">const { lastName, firstName } = { firstName: &quot;Kyeom&quot;, lastName: &quot;Kim&quot; };</code></pre>
<p>이때 우변에 객체 또는 객체로 평가될 수 있는 표현식(문자열, 숫자, 배열 등)을 할당하지 않으면 에러가 발생한다.</p>
<pre><code class="language-javascript">const { lastName, firstName };
// SyntaxError: Missing initializer in destructuring declaration

const { lastName, firstName } = null;
// TypeError: Cannot destructure property &#39;lastName&#39; of &#39;null&#39; as it is null.</code></pre>
<p>위 예제에서 객체 리터럴 형태로 선언한 변수는 lastName, firstName이다. 이는 프로퍼티 축약 표현을 통해 선언한 것이다.</p>
<pre><code class="language-javascript">const { lastName, firstName } = user;
// 위와 아래는 동치다.
const { lastName: lastName, firstName: firstName } = user;</code></pre>
<p>따라서 객체의 프로퍼티 키와 다른 변수 이름으로 프로퍼티 값을 할당받으려면 다음과 같이 변수를 선언한다.</p>
<pre><code class="language-javascript">const user = { firstName: &quot;Kyeom&quot;, lastName: &quot;Kim&quot; };

// 프로퍼티 키를 기준으로 디스트럭처링 할당이 이루어진다.
// 프로퍼티 키가 lastName인 프로퍼티 값을 ln에 할당하고,
// 프로퍼티 키가 firstName인 프로퍼티 값을 fn에 할당한다.
const { lastName: ln, firstName: fn } = user;

console.log(fn, ln); // Kyeom Kim</code></pre>
<p>객체 디스트럭처링 할당을 위한 변수에 기본값을 설정할 수 있다.</p>
<pre><code class="language-javascript">const { firstName = &quot;Kyeom&quot;, lastName } = { lastName: &quot;Kim&quot; };
console.log(firstName, lastName); // Kyeom Kim

const { firstName: fn = &quot;Kyeom&quot;, lastName: ln } = { lastName: &quot;Kim&quot; };
console.log(fn, ln); // Kyeom Kim</code></pre>
<p>객체 디스트럭처링 할당은 객체에서 프로퍼티 키로 필요한 프로퍼티 값만 추출하여 변수에 할당하고 싶을 때 유용하다.</p>
<pre><code class="language-javascript">const str = &quot;Hello&quot;;
// String 래퍼 객체로부터 length 프로퍼티만 추출한다.
const { length } = str;
console.log(length); // 5

const todo = { id: 1, content: &quot;HTML&quot;, completed: true };
// todo 객체로부터 id 프로퍼티만 추출한다.
const { id } = todod;
console.log(id); // 1</code></pre>
<p>객체 디스트럭처링 할당은 객체를 인수로 전달받는 함수의 매개변수에도 사용할 수 있다.</p>
<pre><code class="language-javascript">function printTodo(todo) {
  console.log(
    `할일 ${todo.content}은 ${todo.completed ? &quot;완료&quot; : &quot;비완료&quot;} 상태입니다.`
  );
}

printTodo({ id: 1, content: &quot;HTML&quot;, completed: true }); // 할일 HTML은 완료 상태입니다.</code></pre>
<p>위 예제에서 객체를 인수로 전달받는 매개변수 todo에 객체 디스트럭처링 할당을 사용하면 좀 더 간단하고 가독성 좋게 표현할 수 있다.</p>
<pre><code class="language-javascript">function printTodo({ content, completed }) {
  console.log(`할일 ${content}은 ${completed ? &quot;완료&quot; : &quot;비완료&quot;} 상태입니다.`);
}

printTodo({ id: 1, content: &quot;HTML&quot;, completed: true }); // 할일 HTML은 완료 상태입니다.</code></pre>
<p>배열의 요소가 객체인 경우 배열 디스트럭처링 할당과 객체 디스트럭처링 할당을 혼용할 수 있다.</p>
<pre><code class="language-javascript">const todos = [
  { id: 1, content: &quot;HTML&quot;, completed: true },
  { id: 2, content: &quot;CSS&quot;, completed: false },
  { id: 3, content: &quot;JS&quot;, completed: false },
];

// todos 배열의 두 번째 요소인 객체로부터 id 프로퍼티만 추출한다.
const [, { id }] = todos;
console.log(id); // 2</code></pre>
<p>중첩 객체의 경우는 다음과 같이 사용한다.</p>
<pre><code class="language-javascript">const user = {
  name: &quot;Kim&quot;,
  address: {
    zipCode: &quot;03068&quot;,
    city: &quot;Seoul&quot;,
  },
};

// address 프로퍼티 키로 객체를 추출하고 이 객체의 city 프로퍼티 키로 값을 추출한다.
const {
  address: { city },
} = user;
console.log(city); // &#39;Seoul&#39;</code></pre>
<p>객체 디스트럭처링 할당을 위한 변수에 Rest 파라미터나 Rest 요소와 유사하게 Rest 프로퍼티 ...을 사용할 수 있다. Rest 프로퍼티는 Rest 파라미터나 Rest 요소와 마찬가지로 반드시 마지막에 위치해야 한다.</p>
<pre><code class="language-javascript">// Rest 프로퍼티
const { x, ...rest } = { x: 1, y: 2, z: 3 };
console.log(x, rest); // 1 { y: 2, z: 3 }</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[37 - [JavaScript] Set And Map
]]></title>
            <link>https://velog.io/@flip_404/37-JavaScript-Set-And-Map</link>
            <guid>https://velog.io/@flip_404/37-JavaScript-Set-And-Map</guid>
            <pubDate>Wed, 17 Aug 2022 02:49:01 GMT</pubDate>
            <description><![CDATA[<p><img src="https://i.imgur.com/LOqoalW.png" alt=""></p>
<p>본 게시글은 &quot;모던 자바스크립트 Deep Dive&quot;를 학습하며, 내용 요약 또는 몰랐던 부분을 정리하는 글 입니다.</p>
<h1 id="set과-map">Set과 Map</h1>
<h2 id="set">Set</h2>
<p>중복되지 않는 유일한 값들의 집합
객체임</p>
<h3 id="set-객체의-생성">Set 객체의 생성</h3>
<p>Set 생성자 함수로 생성
인수로 전달받은 이터러블에서 중복된 값은 제외하고 Set 객체 생성한다</p>
<pre><code class="language-javascript">const set = new Set();
const set1 = new Set([1, 2, 1]); // Set(2) {1,2}
const set2 = new Set(&quot;banana&quot;); // Set(3) {&#39;b&#39;,&#39;a&#39;,&#39;n&#39;}</code></pre>
<p>Set로 배열 중복 요소 제거 가능</p>
<pre><code class="language-javascript">const unique = (arr) =&gt; [...new Set(arr)];
console.log(unique([1, 2, 1])); // [1,2]</code></pre>
<h3 id="요소-개수-확인">요소 개수 확인</h3>
<p>Set.prototype.size로 확인
size프로퍼티(접근자 프로퍼티)는 setter 함수가 없기 때문에 할당해도 변하지 않음</p>
<pre><code class="language-javascript">const set = new Set([1, 2, 1]);
console.log(set.size); // 2
set.size = 100;
console.log(set.size); // 2</code></pre>
<h3 id="요소-추가">요소 추가</h3>
<p>Set.prototype.add 메서드로 추가
add메서드는 Set객체에 요소를 추가한 새로운 객체를 반환 -&gt; add().add() 가능
이미 Set객체에 있는 요소를 추가하면 추가하지 않고 에러는 발생하지 않는다
0과 -0, NaN과 NaN을 같다고 보고 중복 추가하지 않는다
자바스크립트의 모든 값을 Set 객체의 요소로 저장할 수 있다</p>
<pre><code class="language-javascript">const set = new Set();
set.add(1).add(2).add(3);
console.log(set); // Set(3) {1,2,3}
set.add(1).add(1).add(1);
console.log(set); // Set(3) {1,2,3}</code></pre>
<h3 id="요소-존재-여부-확인">요소 존재 여부 확인</h3>
<p>Set.prototype.has 메서드로 확인</p>
<pre><code class="language-javascript">const set = new Set([1, 2]);
set.has(1); // true
set.has(4); // false</code></pre>
<h3 id="요소-삭제">요소 삭제</h3>
<p>Set.prototype.delete 메서드로 삭제
인수로 삭제할 요소를 전달
delete 메서드의 반환값은 삭제 성공여부
삭제시 true, 아닐 시 false
즉 없는 요소를 삭제하면 에러가 아닌 false 반환
Set.prototype.add 의 경우 추가된 객체를 반환하지만 delete는 반환값이 성공여부이기 때문에 .delete().delete() 불가능</p>
<pre><code class="language-javascript">const set = new Set([1, 2, 3]);
set.delete(1); // true
set.delete(5); // false
console.log(set); // Set(2) {2,3}</code></pre>
<h3 id="요소-일괄-삭제">요소 일괄 삭제</h3>
<p>Set.prototype.clear 로 초기화
반환값은 undefined</p>
<pre><code class="language-javascript">const set = new Set([1, 2, 3, 4, 5]);
set.clear(); // undefined
console.log(set); // Set(0) {}</code></pre>
<h3 id="요소-순회">요소 순회</h3>
<p>Set.prototype.forEach 메서드로 요소 순회
Array.prototype.forEach와 비슷하다
Array.prototype.forEach에 인수로 전달하는 콜백함수 안에 세개의 인수는 요소,인덱스,객체자체 인데
Set는 인덱스가 없으므로 요소,요소,객체자체</p>
<pre><code class="language-javascript">const set = new Set([1,2,3]);
set.forEach((item,noIdx,self) =&gt; console.log(item,noIdx,self));
/_
1 1 set(3) {1,2,3}
2 2 set(3) {1,2,3}
3 3 set(3) {1,2,3}
_/</code></pre>
<p>Set 객체는 이터러블임 -&gt; for...of, 스프레드문법, 디스트럭처링 할당 가능
순회 하는 순서는 추가된 순서와 같다
Set객체 내에서 순서가 의미는 없지만 다른 이터러블과 호환성을 유지하기 위해</p>
<h3 id="집합-연산">집합 연산</h3>
<p>집합 연산을 프로토타입 메서드로 구현해보면</p>
<p>교집합</p>
<pre><code class="language-javascript">Set.prototype.intersection = function (otherSet) {
  return new Set([...this].filter((x) =&gt; otherSet.has(x)));
};</code></pre>
<p>합집합</p>
<pre><code class="language-javascript">Set.prototype.union = function (otherSet) {
  return new Set([...this, ...otherSet]);
};</code></pre>
<p>차집합</p>
<pre><code class="language-javascript">Set.prototype.difference = function (otherSet) {
  return new Set([...this].filter((x) =&gt; !otherSet.has(x)));
};</code></pre>
<p>부분집합</p>
<pre><code class="language-javascript">Set.prototype.isSuperset = function (otherSet) {
  for (const item of this) {
    if (!otherSet.has(item)) return false;
  }
  return true;
};</code></pre>
<h2 id="map">Map</h2>
<p>키와 값의 쌍으로 이루어진 컬렉션
객체와 유사하지만 객체는 키로 문자열,심벌 값만 가능한데
Map 객체는 키로 모든 값 가능
Map 객체는 이터러블임</p>
<h3 id="map-객체의-생성">Map 객체의 생성</h3>
<p>Map 생성자 함수로 생성
키와 값의 쌍으로 이루어진 요소로 구성된 이터러블을 인수로 전달받는다
중복된 키를 갖는 요소가 있으면 덮어 쓴다</p>
<pre><code class="language-javascript">const map = new Map();
const map2 = new Map([
  [&quot;key1&quot;, &quot;value1&quot;],
  [&quot;key2&quot;, &quot;value2&quot;],
]);

const map3 = new Map([
  [&quot;key1&quot;, &quot;value1&quot;],
  [&quot;key1&quot;, &quot;value2&quot;],
]);
console.log(map3); // Map(1) {&#39;key1&#39; =&gt; &#39;value2&#39;}</code></pre>
<h3 id="요소-개수-확인-1">요소 개수 확인</h3>
<p>Map.prototype.size로 확인
getter만 있는 접근자 프로퍼티임
값 할당해도 변화하지 않음</p>
<pre><code class="language-javascript">const map = new Map([
  [&quot;key1&quot;, &quot;value1&quot;],
  [&quot;key2&quot;, &quot;value2&quot;],
]);
console.log(map.size); // 2
map.size = 100;
console.log(map.size); // 2</code></pre>
<p>Map.prototype.set 메서드로 추가
set 메서드는 새로운 Map객체를 반환하기 때문에 .set().set() 가능
키값으로 0,-0 NaN,NaN 은 같기 때문에 덮어씌움</p>
<pre><code class="language-javascript">const map = new Map();
map.set(&#39;key1&#39;,&#39;value1&#39;);
키 값으로 모든 값 사용 가능

const map = new Map();
const p1 = {name:&#39;aj&#39;};
const p2 = {name:&#39;mj&#39;};
map.set(p1,&#39;male&#39;);
map.set(p2,&#39;female&#39;);</code></pre>
<h3 id="요소-취득">요소 취득</h3>
<p>Map.prototype.get 메소드로 취득
인수로 키 값을 전달, 해당 키를 갖는 요소가 없을 시 undefined를 반환</p>
<pre><code class="language-javascript">const map = new Map([
  [&quot;key1&quot;, &quot;value1&quot;],
  [&quot;key2&quot;, &quot;value2&quot;],
]);
map.get(&quot;key1&quot;); // &#39;value1&#39;</code></pre>
<h3 id="요소-존재-여부-확인-1">요소 존재 여부 확인</h3>
<p>Map.prototype.has 메서드로 확인
인수로 키 값을 전달
있으면 true, 없으면 false 반환</p>
<h3 id="요소-삭제-1">요소 삭제</h3>
<p>Map.prototype.delete 메서드로 삭제
인수로 전달받은 값을 키로 갖는 요소를 삭제
반환값은 성공시 true, 실패시 false(에러는 안남)</p>
<h3 id="요소-일괄-삭제-1">요소 일괄 삭제</h3>
<p>Map.prototype.clear 로 밀어버린다
반환값은 undefined</p>
<h3 id="요소-순회-1">요소 순회</h3>
<p>Map.prototype.forEach 메서드로 순회
콜백 함수 내부의 3가지 인수는 요소값,요소의 키, Map객체 자체
Map 객체는 이터러블이기 때문에 for...of, 스프레드 문법, 디스트럭처링 할당 가능</p>
<p>이터러블이면서 동시에 이터레이터인 객체를 반환하는 메서드를 제공</p>
<ul>
<li>Map.prototype.keys</li>
<li>Map.prototype.values</li>
<li>Map.prototype.entries</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP 4장 - 커넥션 관리]]></title>
            <link>https://velog.io/@flip_404/HTTP-4%EC%9E%A5-%EC%BB%A4%EB%84%A5%EC%85%98-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@flip_404/HTTP-4%EC%9E%A5-%EC%BB%A4%EB%84%A5%EC%85%98-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Mon, 08 Aug 2022 12:01:40 GMT</pubDate>
            <description><![CDATA[<p><img src="https://i.imgur.com/FenftHu.png" alt="">
본 게시글은 &quot;HTTP 완벽 가이드&quot;를 학습하며, 내용 요약 또는 몰랐던 부분을 정리하는 글 입니다.</p>
<h1 id="커넥션-관리">커넥션 관리</h1>
<h2 id="41-tcp-커넥션">4.1 TCP 커넥션</h2>
<p>전 세계 모든 HTTP 통신은, 지구상의 컴퓨터와 네트워크 장비에서 널리 쓰이고 있는 패킷 교환 네트워크 포로토콜들의 계층화된 집합인 TCP/IP를 통해 이루어진다.</p>
<p><strong>URL을 입력받은 브라우저가 수행하는 7단계 과정</strong>
URL : <a href="http://www.joes-hardware.com:80/power-tools.html">http://www.joes-hardware.com:80/power-tools.html</a></p>
<ol>
<li><a href="http://www.joes-hardware.com%EC%9D%B4%EB%9D%BC%EB%8A%94">www.joes-hardware.com이라는</a> 호스트 명을 추출한다.</li>
<li>이 호스트 명에 대한 IP 주소를 찾는다.</li>
<li><a href="http://www.joes-hardware.com">www.joes-hardware.com</a> -&gt; 202.43.78.3</li>
<li>포트번호(80)를 얻는다.</li>
<li>202.43.78.3의 80포트로 TCP 커넥션을 생성한다.</li>
<li>서버로 HTTP GET 요청 메시지를 보낸다.</li>
<li>서버에서 온 HTTP 응답 메시지를 읽는다.</li>
<li>커넥션을 끊는다.</li>
</ol>
<p>HTTP 커넥션은 몇몇 사용 규칙을 제외하고는 TCP 커넥션에 불과하다. TCP 커넥션은 인터넷을 안정적으로 연결해준다.</p>
<p><strong>HTTP는 프로토콜 스택(레이어)에서 최상위 계층</strong>이다. HTTP에 보안 기능을 더한 <strong>HTTPS는 TLS 혹은 SSL이라 불리기도 하며 HTTP와 TCP 사이에 있는 암호화 계층</strong>이다.</p>
<p><img src="https://i.imgur.com/4OaZyob.png" alt=""></p>
<p>HTTP가 메시지를 전송할 경우, TCP 커넥션을 통해서 메시지 데이터의 내용을 순서대로 보낸다. TCP는 세그먼트라는 단위로 데이터스트림을 잘게 나누고, 세그먼트를 IP 패킷이라 불리는 봉투에 담아서 인터넷을 통해 데이터를 전달한다.</p>
<p>이 모든 것은 TCP/IP 소프트웨어에 의해 처리되며, 그 과정은 HTTP 프로그래머에게 보이지 않는다.
TCP는 IP 패킷(혹은 IP 데이터그램)이라고 불리는 작은 조각을 통해 데이터를 전송한다.</p>
<p>IP 패킷은 다음을 포함한다.</p>
<ul>
<li>IP 패킷 (20byte)</li>
<li>TCP 세그먼트 헤더(20byte)</li>
<li>TCP 데이터 조각(0byte 혹은 그 이상)</li>
</ul>
<p><strong>컴퓨터는 항상 TCP 커넥션을 여러 개 가지고 있다.</strong> TCP는 포트 번호를 통해서 이런 여러 개의 커넥션을 유지한다.</p>
<p>회사의 대표 전화번호는 안내 데스크로 연결되고 내선전화는 해당 직원으로 연결되듯이 IP 주소는 해당 컴퓨터에 연결되고 포트 번호는 해당 애플리케이션으로 연결된다.</p>
<p>TCP 커넥션은 네 가지 값으로 식별한다.
<strong>아래 네 가지 값이 모두 똑같은 중복된 커넥션은 존재할 수 없다.</strong></p>
<ul>
<li>&lt;발신지 IP 주소, 발신지 포트, 수신지 IP 주소, 수신지 포트&gt;</li>
</ul>
<h3 id="tcp-소켓-프로그래밍">TCP 소켓 프로그래밍</h3>
<p>운영체제는 TCP 커넥션의 생성과 관련된 여러 기능(API)을 제공한다. 이 소켓 API는 HTTP 프로그래머에게 TCP와 IP의 세부사항들을 숨긴다. 소켓 TCP는 유닉스 운영체제용으로 먼저 개발되었지만, 지금은 소켓 API의 다양한 구현체들 덕분에 대부분의 운영체제와 프로그램 언어에서 이를 사용할 수 있게 되었다.</p>
<p><img src="https://i.imgur.com/nsNYmXy.png" alt=""></p>
<h2 id="42-tcp-성능에-대한-고려">4.2 TCP 성능에 대한 고려</h2>
<p>HTTP는 TCP 바로 위에 있는 계층이기 때문에 HTTP 트랜잭션의 성능은 TCP 성능에 영향을 받는다.
TCP 성능의 특성을 이해함으로써, HTTP 커넥션 최적화 요소들을 더 잘 알게 되고 더 좋은 성능의 HTTP 애플리케이션을 설계하고 구현할 수 있게 될 것이다.</p>
<h3 id="http-트랜잭션-지연">HTTP 트랜잭션 지연</h3>
<p>트랜잭션을 처리하는 시간은 TCP 커넥션을 설정하고, 요청을 전송하고, 응답 메시지를 보내는 것에 비하면 상당히 짧다.
즉, <strong>HTTP 트랜잭션 지연은 TCP 네트워크 지연 때문에 발생한다.</strong></p>
<ol>
<li>클라이언트가 URI에서 웹서버의 IP/port번호를 알아내야한다.</li>
<li>TCP커넥션 요청을 보내고 서버의 커넥션 허가응답을 기다려아한다.</li>
<li>요청메시지가 인터넷을 통해 전달되는 시간</li>
<li>서버로부터 응답메시지를 전달받는시간</li>
</ol>
<p>이 4가지 원인으로 지연이 발생한다. 하지만 현대는 인프라의 발전으로 크게 발생하지 않는다.</p>
<h3 id="422-성능-관련-중요-요소">4.2.2 성능 관련 중요 요소</h3>
<ul>
<li>tcp 커넥션의 핸드세이크</li>
<li>인터넷 혼잡을 제어하기 위한 slow start</li>
<li>데이터를 한번에 보내는 nagle 알고리즘</li>
<li>piggyback과 확인응답 알고리즘</li>
<li>TIME_WAIT 지연</li>
<li>포트 고갈</li>
</ul>
<h3 id="423-tcp-커넥션-핸드셰이크-지연">4.2.3 TCP 커넥션 핸드셰이크 지연</h3>
<ol>
<li>클라이언트가 tcp 커넥션 생성하려고 SYN flag가 있는 메시지보냄</li>
<li>서버가 SYN+ACK보냄</li>
<li>커넥션이 잘 맺어졌음을 알리기위해서 서버한테 ACK전송</li>
</ol>
<p><strong>크기가 작은 http요청의 경우 50%이상의 시간을 커넥션 연결을 구성하는데 쓰이게되서 지연이라고 볼 수 있다.</strong></p>
<h3 id="424-확인응답-지연">4.2.4 확인응답 지연</h3>
<p>인터넷이 패킷전송을 완벽하게는 보장하지 않기 때문에, 데이터 전송 성공을 위해서 자체적인 확인 체계를 가진다.
TCP 세그먼트들은 순번/데이터무결성체크섬 가지는데, 수신자가 그것을 검증하고 확인응답 패킷을 보내줘야한다.</p>
<p>확인 응답은 그 크기가 작아서, 효율적으로 보내려고 같은 방향으로 나가는 패킷에 편승(piggyback)한다.
하지만 이때 편승할 패킷을 찾지못한다면 그것을 찾을때까지 지연이 발생한다.</p>
<h3 id="425-tcp-느린-시작-slow-start">4.2.5 TCP 느린 시작 (slow start) </h3>
<p>tcp 데이터 전송속도는 tcp 커넥션이 만들어진지 얼마나 지났는지에 따라서 달라질 수 있다.
tcp 커넥션은 시간이 지나면서 자체적으로 튜닝되어가지고, 처음에는 커넥션의 최대 속도를 제한하고 데이터가 성공적으로 전송됨에 따라서 속도제한을 높여간다. (이게 slow start)</p>
<p>이걸로 인터넷의 갑작스러운 부하/혼잡을 막을 수 있다.
http 트랜잭션에서 전송할게 많으면 모든패킷을 한번에 쏠수는 없고 1개의 패킷을 보내고 확인응답을 기다려야한다.
확인응답받으면 이제 2개의 패킷을보내고 확인응답 받는다. 다음에는 4개, 이러한 방식으로 이렇게 보낸다.</p>
<h3 id="426-nagle">4.2.6 nagle</h3>
<p>tcp는 헤더의 크기가 작진않기때문에, 작은 데이터를 여러개의 tcp로 보내면 네트워크의 성능은 떨어질 것이다.
nagle알고리즘은 패킷을 전송하기 전에 많은 양의 tcp데이터를 1개의 덩어리로 합친다.
이때 전송하기 충분할 만큼의 패킷이 쌓였을때 버퍼에 저장되어있던 데이터가 전송되는 로직이 있어서, 충분할 만큼의 패킷이 쌓일때까지 지연이 발생할 수 있다.
TCP_NODELAY 플래그로 nagle 알고리즘을 비활성화할 수도 있다.</p>
<h3 id="427-time_wait-누적-포트고갈">4.2.7 TIME_WAIT 누적, 포트고갈</h3>
<p>tcp 커넥션을 끊으며 엔드포인트에서는 ip주소와 포트번호를 작게 기록해놓는데, 이렇게하면 보통 2분정도 내에 같은 주소와 포트번호를 가지는 커넥션이 또 생성되는것을 막아준다. (값을 다르게 해줌)
그래서 커넥션이 닫힌후에 중복되는 패킷이 생기는 경우는 거의 없어진다.</p>
<p>만약 이전 커넥션의 패킷이 그 커넥션과 같은 연결값으로 생성된 커넥션에 삽입되면 패킷은 중복되고 TCP데이터가 충돌난다.
일반적으로 그럴일은 없는데, 테스트하는 상황에서 이런 문제가 생긴다.</p>
<p>포트개수도 제한이 있기 때문에도 더이상 유일한 커넥션을 생성할 수 없어 지연이 발생하기도 한다.</p>
<h2 id="43-http-커넥션-관리">4.3 HTTP 커넥션 관리</h2>
<p>커넥션을 생성하고 최적화하는 HTTP 기술에 대해서 알아보자</p>
<h3 id="431-흔히-잘못이해하는-connetion-헤더">4.3.1 흔히 잘못이해하는 Connetion 헤더</h3>
<p>Connection 헤더에는 다음 3가지 헤더때문에 헷갈릴 수 있다.</p>
<ul>
<li>HTTP 헤더 필드명은 이 커넥션에만 해당되는 헤더들을 나열한다.</li>
<li>임시적인 토큰값은 커넥션에 대한 비표준 옵션을 의미한다.</li>
<li>close 값은 커넥션 작업이 완료되는 종료되어야한다.</li>
</ul>
<h3 id="432-순차적인-트랜잭션-처리에-의한-지연">4.3.2 순차적인 트랜잭션 처리에 의한 지연</h3>
<p>커넥션 관리가 제대로 안되면 tcp 성능이 매우 안좋아진다.
3개의 이미지가 있는 웹페이지가 있으면 4개의 http 트랜잭션을 만들어야함.
html받기용 1개 + 첨부된 이미지를 받기위한 3개.</p>
<p>이때 순차적으로 트랜잭션을 처리하게 되면,  이미지가 동시에 그려지지않아서 심리적으로 느리게 느껴지고
물론 물리적인 지연도 있고 화면에 배치하려면 크기를 알아야해서 객체의 크기를 파악하는 동안 텅빈화면이 보여진다.</p>
<p>이것을 해결하기 위해서 커넥션은 4가지가 존재한다.</p>
<ol>
<li>병렬 커넥션</li>
<li>지속 커넥션</li>
<li>파이프라인 커넥션</li>
<li>다중 커넥션</li>
</ol>
<h2 id="44-병렬-커넥션">4.4 병렬 커넥션</h2>
<p>브라우저는 순차적으로 객체를 내려받는 식으로 웹페이지를 보여줄 수 있다.
=&gt; 많이 느리다</p>
<p>http는 여러개의 커넥션을 맺음으로써 여러개의 http 트랜잭션을 병렬로 처리할 수 있게한다.</p>
<h3 id="441-병렬-커넥션은-페이지를-더-빠르게-내려받는다">4.4.1 병렬 커넥션은 페이지를 더 빠르게 내려받는다.</h3>
<ul>
<li>단일커넥션의 대역폭 제한과 커넥션이 동작하지 않고 있는 시간을 활용하면 더 빠르게 내려받을 수 있다는 아이디어</li>
<li>하나의 커넥션으로 객체들을 로드할때 대역폭 제한과 대기시간을 줄일 수 있으면 더 빠르게 로드할 수 있다는 아이디어</li>
<li>각 커넥션의 지연 시간을 겹치게하면 총 지연시간을 줄일 수 있다는 아이디어</li>
<li>인터넷 대역폭을 한개의 커넥션이 다 써버리는 것이 아니고 나머지 객체를 내려받는 데에 나머지 대역폭을 사용할 수 있다는 아이디어</li>
</ul>
<h3 id="442-병렬-커넥션이-항상-더-빠르지는-않다">4.4.2 병렬 커넥션이 항상 더 빠르지는 않다.</h3>
<ul>
<li>병렬커넥션은 네트워크 대역폭이 좁을때는 대부분 시간을 데이터 전송에 쓸것이고</li>
<li>여러개의 객체를 병렬로 내려받는 경우 제한된 대역폭이라면 전송받는 것은 느리기 때문에 성능상의 장점이 없어짐</li>
<li>커넥션 생성 자체 부하때문에 객체들을 순차적으로 내려받는 것보다 더 오래걸릴 수도 있음</li>
<li>커넥션자체도 부하임 (100명의 사용자가 100개의 커넥션 맺으면 서버는 10000개 커넥션 감당해야함)</li>
</ul>
<p><strong>=&gt; 그래서 브라우저는 실제로 병렬 커넥션을 사용하긴 하지만 적은수(대부분 4개정도)의 병렬커넥션만을 허용함.</strong></p>
<h3 id="443-병렬-커넥션은-더-빠르게-느껴질-수-있다">4.4.3 병렬 커넥션은 더 빠르게 &quot;느껴질 수&quot; 있다.</h3>
<p>그래서 병렬커넥션은 순차적인 것보다 빠르지 않을 수는 있지만, 여러개의 객체가 거의 동시에 로드되기 때문에
사용자가 빠르게 로드된다고 느낄 수 있다.</p>
<h2 id="45-지속-커넥션">4.5 지속 커넥션</h2>
<ul>
<li>클라이언트는 1개의 웹사이트에 여러개의 커넥션을 맺는데, 계속 똑같은 서버에 요청을 보내는것은 비효율적이다. (site locality)</li>
<li>따라서 http1.1을 지원하는 기기는 처리가 완료된 후에 tcp커넥션을 유지하여 앞으로 있을 http 요청에 재사용할 수 있다.</li>
</ul>
<p>=&gt; 처리가 완료된 후에도 계속 연결된 상태로 있는 tcp 커넥션을 지속커넥션이라고 부른다.
즉, 비지속 커넥션은 각 처리가 끝날때마다 끊지만 지속 커넥션은 처리가 끝나도 커넥션을 유지한다.</p>
<h3 id="451-지속-커넥션-vs-병렬-커넥션">4.5.1 지속 커넥션 vs 병렬 커넥션</h3>
<p><strong>병렬커넥션의 단점</strong></p>
<ol>
<li>각 트랜잭션마다 커넥션을 맺고 끊기 때문에, 시간과 대역폭이 소요된다.</li>
<li>각 커넥션의 생성은 tcp 느린시작 때문에 성능이 떨어진다.</li>
<li>실제로 연결할 수 있는 병렬 커넥션의 수에 제한이 있다.</li>
</ol>
<p><strong>지속커넥션의 장점</strong></p>
<ol>
<li>커넥션을 맺기위한 사전작업과 지연 줄여준다.</li>
<li>튜닝된 커넥션(권한이더 많아진)을 유지한다.</li>
<li>커넥션의 수를 줄여준다.</li>
</ol>
<p><strong>지속커넥션의 단점</strong></p>
<ol>
<li>계속 연결된 상태로 있는 수많은 커넥션이 쌓이게 될 것이다.</li>
</ol>
<p>그래서 병렬커넥션 + 지속커넥션 === Good
오늘날에는 적은 수의 병렬 커넥션만을 맺고 그것을 지속하는 형태가 많이 보이게 되었다.</p>
<p>http 1.0+에는 &#39;keep alive&#39; 커넥션이 있고, <strong>http1.1에는 지속커넥션</strong>이 있다.</p>
<h3 id="452-http10의-keep-alive-커넥션">4.5.2 http1.0+의 keep alive 커넥션</h3>
<ul>
<li>keep alive 커넥션이라는 지속커넥션을 사용하기위해 등장</li>
<li>설계상 문제가 있어서 http1.1에서 빠지게됨 (Q: 어떤문제일까?)</li>
<li>지금 남아있는거는 오늘날에도 keep alive 방법이 사용되는 곳이 있어서 그럼</li>
</ul>
<h3 id="453-keep-alive-동작">4.5.3 keep alive 동작</h3>
<ul>
<li>클라이언트는 커넥션 유지하려고 Connection:Keep-Alive 헤더를 포함시킴</li>
<li>서버도 해당헤더로 응답함.</li>
<li>만약에 서버가 해당헤더로 응답하지 않는다면 keep alive 를 지원하지 않는 것임</li>
</ul>
<h3 id="454-keep-alive-옵션">4.5.4 keep alive 옵션</h3>
<ul>
<li>keep alive는 지속커넥션을 사용하고 싶어요라는 메시지이며 클라이언트/서버 모두 무조건 따를 필요는 없고 언제든지 끊어버릴 수 있다.
timeout: 커넥션이 얼마나 유지될 것인가
max: 커넥션이 몇개의 http 트랜잭션을 처리할 때까지 유지될 것인가</li>
</ul>
<p>Connection: Keep-Alive
Keep-Alive: max=5, timeout=120</p>
<p>위 헤더는 서버가 약 5개의 추가 트랜잭션이 처리될동안 커넥션을 유지하거나
2분동안 커넥션을 유지하라는 내용의 Keep-Alive 응답헤더이다.</p>
<h3 id="455-keep-alive-커넥션-제한과-규칙">4.5.5 keep alive 커넥션 제한과 규칙</h3>
<ul>
<li>keep-alive는 http1.0에서 기본으로 사용되지는 않음</li>
<li>클라이언트가 먼저 Connection Keep-Alive 헤더를 보내야함</li>
<li>클라이언트가 Connection Keep-Alive를 보내지않으면 서버는 요청을 처리하고 끊어버릴 것</li>
<li>서버가 응답헤더에 Connection Keep-Alive를 보내지않으면, 클라이언트는 곧 커넥션이 끊길것임을 알게됨</li>
<li>커넥션이 끊어지기 전에 엔티티 본문의 길이(Content-Length)를 알 수 있어야 기존의 메시지 끝과 새로운 메시지의 시작점을 정확히 알 수 있다.</li>
<li>프록시와 게이트웨이는 Connection 헤더의 규칙을 철저하게 지켜야한다.</li>
<li>프록시와 게이트웨이는 메시지전달/캐시저장시에 Connection 헤더와 관련된 정보를 제거해야한다. (Q: 왜? =&gt; 뒤에서 언급되는 멍청한  프록시문제 때문)</li>
<li>클라이언트는 응답 전체를 모두 받기 전에 커넥션이 끊어졌을 경우, 별다른 문제가 없으면 요청을 다시 보낼 수 있게 준비되어야한다.</li>
</ul>
<h3 id="456-keep-alive와-멍청한-프록시">4.5.6 keep alive와 멍청한 프록시</h3>
<ul>
<li>웹 클라이언트 요청에 Connection Keep-Alive 헤더가 있으면, 클라이언트가 현재 연결하고 있는 tcp 커넥션을 끊지 않고 계속 유지하려는 것이다.</li>
</ul>
<p>문제: Connection 헤더의 무조건 전달
프록시는 Connection 헤더를 이해하지 못해서 해당 헤더들을 삭제하지 않고 요청 그대로를 다음 프록시에 전달</p>
<ol>
<li>클라이언트가 Connection Keep-Alive 헤더 메시지를 보냄</li>
<li>멍청한 프록시가 Connection Keep-Alive 뭔지몰라서 그대로 서버에 전달 (그래서 Connection헤더를 제거하는것)</li>
<li>서버가 Connection Keep-Alive 헤더를 포함한 응답을 보냄</li>
<li>멍청한 프록시가 응답메시지를 그대로 클라이언트에게 전달
(이제 클라이언트와 서버는 지속커넥션으로 비밀친구가되었으나, 프록시는 아무것도 모른다)</li>
<li>프록시는 서버가 커넥션 끊기를 기다림. 하지만 서버는 커넥션을 끊지 않음</li>
<li>클라이언트가 지속커넥션에 메시지를 전송하는데 프록시는 같은 커넥션상에서 다른 요청이 오는경우를 모르게때문에 로드중단</li>
<li>클라이언트는 자신이나 서버가 timeout 날때까지 아무것도 못함</li>
</ol>
<h3 id="457-proxy-connection-살펴보기">4.5.7 Proxy-Connection 살펴보기</h3>
<p>(공부 좀 더 필요)
넷스케이프는 멍청한 프록시문제를 해결하기 위해서 일반적으로 전달하는 Connection 헤더 대신에, 비표준인 Proxy-Connection [확장헤더]를 프록시에게 전달.
이러면 프록시가 Proxy-Connection 전달해도 비표준이기 때문에 서버는 무시함.
이제 프록시는 정말 &quot;전달만&quot; 할 수 있음.</p>
<p>즉, Proxy-Connection는 멍청한 프록시: 단일 무조건 전달 문제를 해결할 수 있음.
=&gt; 하지만 동시에 keep-alive 도 맺어지지 않음 (Connection헤더 제거했으니까)</p>
<p>해결법: 영리한 프록시를 둔다.
영리한 프록시는 Proxy-Connection를 받고 Connection: Keep-Alive 헤더로 바꿔서 서버에 전달한다.
keep alive 지속커넥션이 가능해진다!</p>
<p>근데 이것도, 멍청한 프록시 양옆에 영리한 프록시 있으면 (프록시가 많은 구조) 문제가 생긴다.
변환된 Connection: Keep-Alive가 다시 멍청한 프록시에 갈 수 있기 때문에..
게다가 문제를 발생시키는 프록시들은 네트워크상에서 보이지 않기 때문에 Proxy-Connection 헤더를 보내지 못한다.</p>
<h3 id="458-http11의-지속커넥션">4.5.8 http1.1의 지속커넥션</h3>
<p>http1.1에서는 keep-alive 커넥션은 지원하지 않고, 더 나은 설계의 지속커넥션을 지원한다.
http1.1의 지속커넥션은 기본적으로 활성화되어있다. (별도 설정없으면 모든 커넥션을 지속커넥션으로 취급하는 것이다.)
http1.1은 트랜잭션이 끝난 다음 커넥션을 끊으려면 Connection: close 헤더를 명시해야한다.</p>
<h3 id="459-지속-커넥션-제한과-규칙">4.5.9 지속 커넥션 제한과 규칙</h3>
<ul>
<li>클라이언트가 Connection: close 보내면 클라이언트는 해당 커넥션으로 추가적인 요청 못함</li>
<li>커넥션에 있는 모든 메세지가 자신의 길이 정보를 정확히 가지고 있을 때에만 커넥션 지속 가능</li>
<li>http1.1 프록시는 클라/서버 각각에 대해 별도의 지속커넥션 맺고 관리해야함</li>
<li>http1.1기기는 Connection: close 없어도 언제든지 연결을 자체적으로 끊어버릴 수 있음</li>
<li>http1.1는 중간에 끊어지는 커넥션을 복구할 준비가 되어야있어야함</li>
<li>부하를 대비하기 위해서 N명의 사용자가 서버로 접근하려한다면 프록시는 넉넉잡아 2N개의 커넥션을 유지해야함 (2배)</li>
</ul>
<h2 id="46-파이프라인-커넥션">4.6 파이프라인 커넥션</h2>
<p>http1.1은 지속커넥션을 통해서 요청을 파이프라이닝 가능</p>
<ul>
<li>http클라이언트는 커넥션이 지속 커넥션인지 확인하기 전까지는 파이프라인을 이어서는 안됨.</li>
<li>http 응답은 요청순서와 같게 와야함. (http 메시지에 순번이 매겨지지 않기 때문)</li>
<li>커넥션이 강제로 끊어져도 완료되지 않은 요청이 파이프라인에 있으면 언제든 다시 요청 보낼 수 있어야함</li>
<li>http 클라이언트는 post 요청같이 반복해서 보낼 경우에 문제가 생길 수 있는 요청은 파이프라인을 통해 보내면안됨.(어떤것이 서버에서 처리되었는지 모르니까)</li>
</ul>
<h2 id="47-커넥션-끊기-미스터리">4.7 커넥션 끊기 미스터리</h2>
<p>커넥션 관리에 대해서는 명확한 기준이 없음.</p>
<h3 id="471-마음대로-커넥션-끊기">4.7.1 마음대로 커넥션 끊기</h3>
<ul>
<li>보통 커넥션은 메시지를 다 보낸다음에 끊는데, 에러가 있는 상황에서는 헤더의 중간이나 다른 엉뚱한 곳에서 끊길 수 있음(timeout등등)</li>
<li>당연히 커넥션을 끊는 시점에 클라이언트가 요청을 보낸다면 문제가 생길 수 있다!</li>
</ul>
<h3 id="472-content-length와-truncation">4.7.2 Content-Length와 Truncation</h3>
<ul>
<li>각 http 응답은 엔티티본문의 길이를 가지는 Content-Length 헤더를 가져야한다.</li>
<li>Content-Length 헤더를 일부러 잘못줘서 요청을 끊는 방법을 쓸수도 있어서, http응답받은후에 실제 전달된 엔티티길이와 Content-length값이 일치하지 않거나 없으면 서버한테 물어봐야한다.</li>
</ul>
<h3 id="473-커넥션-끊기의-허용-재시도-멱등성">4.7.3 커넥션 끊기의 허용, 재시도, 멱등성</h3>
<ul>
<li>http 어플리케이션은 예상치 못하게 커넥션이 끊어졌을때에 적절히 대응할 수 있는 준비가 되어야한다.</li>
<li>끊어졌으면 커넥션을 다시맺고 재전송을 하는 과정이 필요하며, 파이프라인의 큐에 요청들이 있는데 끊어버리거나하면 복잡하다.</li>
<li>Post같은 서버의 상태를 바꾸는 작업들은 재시도하기 위험하다. (멱등하지 않음)
(여러번 실행되었는데 한번실행된것과 같은 결과를 반환하면 멱등한것)
GET, HEAD, PUT, DELETE, TRACE, OPTIONS는 멱등하다.</li>
</ul>
<p>즉, 멱등하지 않은 요청은 재요청하면 안된다. 대부분 브라우저는 캐시된 Post 요청 페이지를 다시로드하려할때, 요청을 다시 보내기를 원하는지 묻는 대화상자를 보여준다</p>
<h3 id="474-우아한-커넥션-끊기">4.7.4 우아한 커넥션 끊기 </h3>
<p><strong>전체끊기</strong>
어플리케이션은 tcp입력채널과 출력채널 중 1개만 끊거나 둘다 끊을 수 있다.</p>
<p><strong>절반끊기</strong>
입력채널이나 출력채널 중 하나를 개별적으로 끊을 수 있다.</p>
<p><strong>TCP끊기</strong>
단순한 http는 전체끊기만 상요할 수 있는데 어플리케이션이 각기다른 http클라이언트, 서버, 프락시와 통신할때 지속커넥션을 계속할때, 예상치 못한 쓰기에러를 방지하기 위해 절반끊기가 필요할 수도 있다.</p>
<ul>
<li>보통 출력 채널을 끊는 것이 안전하다.</li>
<li>클라이언트가 데이터를 더이상 보내지않음을 확신하지 않는이상, 입력채널을 끊어버리면 tcp에러가 뜨고 os에서 심각한 에러로 받아들여가지고 버퍼를 다 비워버린다. (문제가 더 커질 수 있음)</li>
</ul>
<p><strong>우아하게 커넥션 끊기</strong>
일반적으로 이상적인 커넥션끊기는 자신의 출력채널을 먼저 끊고 다른 쪽에 있는 기기의 출력 채널이 끊기기를 기다리는 것이다.
양쪽에서 서로 더이상 전송하지 않겠다라고 알려주면 위험없이 종료된다.
안타깝게도 항상 이렇지만은 않기 때문에, 출력채널에 절반끊기를 쓰고 입력채널에 대해서 상태검사를 주기적으로 해야한다.</p>
]]></description>
        </item>
    </channel>
</rss>