<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>bamsol</title>
        <link>https://velog.io/</link>
        <description>프론트 엔드 개발자 지향</description>
        <lastBuildDate>Thu, 09 Dec 2021 17:15:07 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>bamsol</title>
            <url>https://images.velog.io/images/bambi-bam/profile/85fde46b-d9f0-4f5c-ac2b-32ecee7be173/—Pngtree—hand drawn cute shy deer_4248856.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. bamsol. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/bambi-bam" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[React 되짚어보기] React란?]]></title>
            <link>https://velog.io/@bambi-bam/React-%EB%90%98%EC%A7%9A%EC%96%B4%EB%B3%B4%EA%B8%B0-React%EB%9E%80</link>
            <guid>https://velog.io/@bambi-bam/React-%EB%90%98%EC%A7%9A%EC%96%B4%EB%B3%B4%EA%B8%B0-React%EB%9E%80</guid>
            <pubDate>Thu, 09 Dec 2021 17:15:07 GMT</pubDate>
            <description><![CDATA[<h2 id="react란">React란?</h2>
<p>React 공식문서를 보면 아래와 같이 설명하고 있다.</p>
<blockquote>
<p><strong>사용자 인터페이스(UI)를 만들기 위한 JavaScript 라이브러리</strong>
출처 - <a href="https://ko.reactjs.org/">React 공식 문서</a></p>
</blockquote>
<p><a href="https://v8.dev/">V8 자바스크립트 엔진</a>의 등장으로 JavaScript로 desktop APP이나 mobile APP과 유사한 UX를 제공할 수 있는 Web APP을 만들 수 있게 되었다. 
또한 과거 <code>웹 서버</code>에서 수행되던 로직들이 <code>클라이언트(브라우저)</code>로 이동하면서 새로운 HTML 페이지를 server에 요청할 필요없이 사용자가 보는 HTML 구조(DOM)을 조작할 수 있게 되었다.</p>
<p>React.js는 이런 JavaScript의 <code>클라이언트 사이드 라이브러리</code>로 클라이언트 사이드 JavaScript 코드를 작성하는데 도움이 된다.</p>
<blockquote>
<p>즉, <strong>React는 모던하고 리액티브한 사용자 인터페이스(UI)를 구축</strong>하게 해주는데, 그 과정에서 <code>더 높은 레벨의 문법(syntax)</code>를 제공한다. 이 문법을 통해 코드를 <code>선언적(Declative)</code>이고 <code>컴포넌트 중심적인 방법(component-focused approach)</code>으로 작성할 수 있게 해준다.</p>
</blockquote>
<h2 id="react-특징-3가지">React 특징 3가지</h2>
<ol>
<li>선언형 프로그래밍</li>
<li>컴퍼넌트 시스템</li>
<li>멀티 플랫폼 지원</li>
</ol>
<h3 id="선언형-프로그래밍">선언형 프로그래밍</h3>
<p><strong>JavaScript만 사용한다면,</strong> createElement()로 요소를 생성하고 textContent로 안에 내용을 넣고, className으로 클래스를 추가하고, addEventListener()로 이벤트를 연결하고 등등... <strong>모든 단계들이 서술되어야 한다. 이건 명령적 프로그래밍(Imperative Programming) 방식으로 단순히 이어지는 행동들을 단계별로 설명하는 방식</strong>이다. 이런 접근은 모든 핵심적인 세부사항을 처리해줘야 하고 높은 확률로 반복적인 코드들을 작성하게 된다.
하지만 React.js를 사용하면 아래와 같이 구조적으로 한 눈에 알아볼 수 있는 코드를 작성할 수 있다. &lt; Post &gt;라는 컴포넌트 안에 필요한  HTML 구성 요소들이 들어있고 React는 이런 컴포넌트들의 조합을 이용하는 것이다.</p>
<pre><code class="language-javascript">imoprt Post from &#39;./components/Post&#39;;

function APP(){
  return (
    &lt;div&gt;
        &lt;h1&gt;Post Lists&lt;/h1&gt;
        &lt;Post title=&#39;post title&#39; content=&#39;post content&#39; /&gt;
    &lt;/div&gt;
  )
}

export default App;</code></pre>
<p>React는 이처럼 작은 컴포넌트로 나누어 작업하는 방식으로 보면 된다. 각각의 컴포넌트(js, ts 등)들은 명확한 작업을 가진다.  </p>
<p>그리고 그 코드들을 React 라이브러리가 훅 등을 이용한 선언적 코드를 해석해 화면에 랜더링한다.</p>
<p>즉, JavaScript에서 작성했어야했던 createElement()로 요소를 생성하는 등의 <strong>낮은 레벨의 지시사항(low level instriction)은 React에서는 직접 작성하지 않고, React 라이브러리에 의해 작성된다. 이로 인해, React를 사용하면 복잡한 사용자 인터페이스를 훨씬 더 쉽게 구축할 수 있는 것이다.</strong></p>
<h3 id="컴퍼넌트-시스템">컴퍼넌트 시스템</h3>
<h3 id="멀티-플랫폼-지원">멀티 플랫폼 지원</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[참고할 만한 JS 코딩 컨벤션]]></title>
            <link>https://velog.io/@bambi-bam/%EC%B0%B8%EA%B3%A0%ED%95%A0-%EB%A7%8C%ED%95%9C-JS-%EC%BD%94%EB%94%A9-%EC%BB%A8%EB%B2%A4%EC%85%98</link>
            <guid>https://velog.io/@bambi-bam/%EC%B0%B8%EA%B3%A0%ED%95%A0-%EB%A7%8C%ED%95%9C-JS-%EC%BD%94%EB%94%A9-%EC%BB%A8%EB%B2%A4%EC%85%98</guid>
            <pubDate>Sun, 05 Dec 2021 12:33:20 GMT</pubDate>
            <description><![CDATA[<h3 id="1-javascript-standard-style">1. <a href="https://standardjs.com/">JavaScript Standard Style</a></h3>
<h3 id="2-airbnb-javascript-style-guide">2. <a href="https://github.com/airbnb/javascript">Airbnb JavaScript Style Guide</a></h3>
<h3 id="3-google-javascript-style-guide">3. <a href="https://google.github.io/styleguide/jsguide.html">Google JavaScript Style Guide</a></h3>
<h3 id="4-nhn-fe개발랩">4. <a href="https://ui.toast.com/fe-guide/ko_CODING-CONVENTION">NHN FE개발랩</a></h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[[webpack] basic react webpack babel 설정]]></title>
            <link>https://velog.io/@bambi-bam/webpack-basic-react-webpack-babel-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@bambi-bam/webpack-basic-react-webpack-babel-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Fri, 03 Dec 2021 16:37:04 GMT</pubDate>
            <description><![CDATA[<h1 id="basic-react-webpack-babel-설정">basic react webpack babel 설정</h1>
<p>기본적인 React Webpack Babel 설정을 연습, 기록한다.</p>
<h2 id="프로젝트-구성">프로젝트 구성</h2>
<ul>
<li>프로젝트의 메타 정보를 관리할 pakage.json 파일을 프로젝트 루트에 생성한다.</li>
</ul>
<pre><code class="language-bash">$ npm init --yes</code></pre>
<h2 id="react-기본-프로젝트-코드-작성">React 기본 프로젝트 코드 작성</h2>
<h3 id="1-react와-react-dom-package를-설치한다">1. react와 react-dom package를 설치한다.</h3>
<pre><code class="language-bash">$ npm i react react-dom</code></pre>
<h3 id="2-프로젝트-폴더-구성">2. 프로젝트 폴더 구성</h3>
<h2 id="webpack-설정">Webpack 설정</h2>
<h3 id="1-webpack-package-설치">1. webpack package 설치</h3>
<ul>
<li>Webpack 개발 서버 환경을 구성하기 위한 의존 패키지를 설치한다.</li>
<li>개발 서버에서만 사용할 것이므로 -D 속성을 붙여준다.</li>
</ul>
<pre><code class="language-bash">$ npm i -D webpack webpack-cli webpack-dev-server dotenv rimraf</code></pre>
<h3 id="2-환경-변수-추가">2. 환경 변수 추가</h3>
<ul>
<li>프로젝트 루트 위치에 <code>.env</code>파일을 생성하고 전역 환경 변수를 설정한다.</li>
<li>이 .env는 git repository에 올라가지 않도록 .gitignore에 추가한다.</li>
<li>환경변수는 </li>
</ul>
<pre><code class="language-python">PORT=3500</code></pre>
<h3 id="3-webpack-폴더-구성">3. Webpack 폴더 구성</h3>
<pre><code class="language-powershell">.
├── config/
│   ├── webpack.config.dev.js
│   └── webpack.config.server.js
├── dist/
├── src/
├── package-lock.json
└── package.json</code></pre>
<h3 id="4-개발용-webpack-구성-파일-작성">4. 개발용 webpack 구성 파일 작성</h3>
<ul>
<li>webpack.config.dev.js</li>
<li>webpack.config.server.js</li>
</ul>
<h4 id="webpackconfigdevjs">webpack.config.dev.js</h4>
<pre><code class="language-js">require(&#39;dotenv&#39;).config();

const path = require(&#39;path&#39;);
const __ROOT = process.cwd();
const webpack = require(&#39;webpack&#39;);
const serverConfig = require(&#39;./webpack.config.server&#39;);

module.exports = {
  target: &#39;web&#39;,
  context: __ROOT,
  mode: &#39;development&#39;,
  devtool: &#39;eval&#39;,
  devServer: serverConfig,
  entry: {
    main: path.join(__ROOT, &#39;./src/index.js&#39;),
  },
  output: {
    path: path.join(__ROOT, &#39;./public&#39;),
    filename: &#39;js/[name].bundle.js&#39;,
    chunkFilename: &#39;[id].chunk.js&#39;,
    publicPath: &#39;/&#39;,
  },
    resolve: { extensions: [&#39;.js&#39;, &#39;.jsx&#39;, &#39;.json&#39;] },
    plugins: [
    new webpack.DefinePlugin({
      process: {
        env: {
          NODE_ENV: JSON.stringify(process.env.NODE_ENV),
        },
      },
    }),
  ],
};</code></pre>
<h4 id="webpackconfigserverjs">webpack.config.server.js</h4>
<pre><code class="language-js">const path = require(&#39;path&#39;);

module.exports = {
  // 정적 콘텐츠를 제공 할 위치를 서버에 알립니다.
  contentBase: path.join(process.cwd(), &#39;./public&#39;),

  // 요청을 수신할 포트 번호를 설정합니다.
  port: process.env.PORT,

  // 인덱스로 설정할 파일 이름을 설정합니다.
  index: &#39;index.html&#39;,

  // 컴파일러 오류 또는 경고가있을 때 브라우저 화면에 오류 내용을 덮어씁니다.
  overlay: true,

  // 생성된 파일의 gzip 압축을 사용합니다.
  compress: true,

  // Hot Module Replacement 기능을 활성화 합니다.
  hot: true,

  // contentBase 옵션에서 제공하는 파일을 감시하도록 서버에 설정합니다.
  // 활성화 되면 파일 변경으로 전체 페이지 다시 로드 합니다.
  watchContentBase: true,

  // 알려진 바에 의하면 node_modules를 watchOptions 항목에서 제외하지 않을 경우
  // CPU 과부하 문제가 발생할 수 있습니다.
  // https://github.com/facebookincubator/create-react-app/issues/293
  watchOptions: {
    ignored: /node_modules/,
  },

  // Webpack Dev Server의 자체 로그는 일반적으로 유용하지 않습니다.
  // 로그를 보지 않도록 설정하거나, 너무 장황하지 않도록 &#39;silent&#39; 값을 설정합니다.
  clientLogLevel: &#39;none&#39;,

  // 터미널에 오류 정보만 간략히 표시하도록 설정합니다.
  stats: &#39;errors-only&#39;,

  // HTML5 History API를 사용하는 경우, index.html 페이지가 404 응답 대신 제공 되어야 합니다.
  historyApiFallback: true,

  // 실제 디스크에 파일을 쓰기합니다. (파일 생성)
  writeToDisk: true,
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[폴더별 github 계정 분리하기]]></title>
            <link>https://velog.io/@bambi-bam/%ED%8F%B4%EB%8D%94%EB%B3%84-github-%EA%B3%84%EC%A0%95-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@bambi-bam/%ED%8F%B4%EB%8D%94%EB%B3%84-github-%EA%B3%84%EC%A0%95-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 01 Dec 2021 16:45:55 GMT</pubDate>
            <description><![CDATA[<h2 id="계정별-ssh-key-생성">계정별 ssh key 생성</h2>
<ol>
<li><p>홈 디렉토리(~) 폴더 안 .ssh 폴더(숨긴 풀더)로 이동</p>
<ul>
<li>홈 디렉토리(~) 폴더는 window의 경우, <code>c: / 사용자 / 사용자 아이디</code> 폴더에 해당한다.</li>
<li>~폴더 안에 <strong>.ssh 폴더가</strong> <strong>없다면 생성</strong>한다.</li>
</ul>
</li>
</ol>
<pre><code class="language-bash"># .ssh 폴더 생성 명령어
$ mkdir ~/.ssh</code></pre>
<pre><code class="language-bash"># .ssh 폴더 안으로 이동 명령어
$ cd ~/.ssh</code></pre>
<ol start="2">
<li>계정별 ssh key 생성</li>
</ol>
<ul>
<li>userA 대신 해당 계정을 구분할 이름을 넣어주면 된다.</li>
<li>userA가 사용할 github 아이디@email.com는 userA가 github에서 로그인할 때 사용하는 계정을 넣어준다.</li>
</ul>
<pre><code class="language-bash"># userA 계정에 대한 ssh key 생성
$ ssh-keygen -t rsa -C &quot;userA가 사용할 github 아이디@email.com&quot; -f &quot;id_rsa_userA&quot;</code></pre>
<ul>
<li>userB 대신 해당 계정을 구분할 이름을 넣어주면 된다.</li>
<li>userB가 사용할 github 아이디@email.com는 userB가 github에서 로그인할 때 사용하는 계정을 넣어준다.</li>
</ul>
<pre><code class="language-bash"># userB 계정에 대한 ssh key 생성
$ ssh-keygen -t rsa -C &quot;userB가 사용할 github 아이디@email.com&quot; -f &quot;id_rsa_userB&quot;</code></pre>
<ul>
<li>이 때, 아래와 같이 비밀번호를 설정하라고 뜨는데 굳이 설정하지 않을 거라면 그냥 엔터를 눌러 넘어가면 된다.<pre><code class="language-bash">Enter passphrase (empty for no passphrase):</code></pre>
</li>
</ul>
<ol start="3">
<li>생성된 ssh key 확인</li>
</ol>
<p>위와 같이 ssh key를 생성하고 나면 .ssh 폴더 안에 </p>
<ul>
<li>id_rsa_userA</li>
<li>id_rsa_userA.pub</li>
<li>id_rsa_userB</li>
<li>id_rsa_userB.pub</li>
</ul>
<p>이 4개의 파일이 생성되었을 것이다.</p>
<blockquote>
<p>.pub이 붙은 파일은 공개키(public key)이다.
붙지 않은 파일은 비밀키(private key)이다.</p>
</blockquote>
<h2 id="ssh-agent에-ssh-key의-비밀키를-등록">ssh-agent에 ssh key의 비밀키를 등록</h2>
<ul>
<li>현재 폴더가 ~/.ssh 여야 한다.</li>
</ul>
<pre><code class="language-bash">$ ssh-add id_rsa_userA
$ ssh-add id_rsa_userB</code></pre>
<ul>
<li>만약 이때 오류가 발생한다면<pre><code class="language-bash">$ eval &quot;$(ssh-agent -s)&quot;</code></pre>
로 초기화를 해준다.</li>
</ul>
<h2 id="github에-ssh-key의-공개키를-등록">Github에 ssh key의 공개키를 등록</h2>
<h3 id="usera">userA</h3>
<ol>
<li><p>userA github 계정에 로그인한다.
여기에 userA 계정의 ssh key 공개키를 등록할 것이다.</p>
</li>
<li><p><code>우측 상단의 프로필 이미지를 클릭</code> &gt; <code>Settings 클릭</code></p>
</li>
<li><p><code>좌측 메뉴 중 SSH and GPG keys 클릭</code> &gt; <code>New SSH key 클릭</code></p>
</li>
<li><p>Title엔 해당 ssh keys를 구분할 이름을 정해준다.
ex) userA - desktop MAC</p>
</li>
<li><p>Key엔 ssh key의 공개키를 복사해 붙여 넣어준다.</p>
</li>
</ol>
<pre><code class="language-bash">// ssh key의 공개키 확인
$ cat id_rsa_userA.pub</code></pre>
<ul>
<li>ssh-rsa로 시작하는 공개키가 나오는데 ssh-rsa를 포함해 복사해 붙여 넣는다.</li>
</ul>
<ol start="6">
<li><code>Add SSH Key 클릭</code> </li>
</ol>
<ol start="7">
<li>userB github 계정에 로그인한다.</li>
</ol>
<h3 id="userb">userB</h3>
<ol>
<li>userA 와 동일하게 진행해준다.</li>
</ol>
<h2 id="config-파일-설정">config 파일 설정</h2>
<ol>
<li>.ssh 폴더 안에 config 파일을 생성한다.</li>
</ol>
<ul>
<li>안에 내용을 적을 것이므로 vi로 생성하면 바로 내용을 적을 수 있다.</li>
</ul>
<pre><code>$ vi ~/.ssh/config</code></pre><ol start="2">
<li>config 안에 아래와 같이 작성한다.<pre><code># 여기는 주석이다. - userA 깃험 계정 - for work
Host github.com-userA
 HostName github.com
 IdentityFile ~/.ssh/id_rsa_userA
 User userA
</code></pre></li>
</ol>
<h1 id="여기는-주석이다---userb-깃헙-계정---for-study">여기는 주석이다. - userB 깃헙 계정 - for study</h1>
<p>Host github.com-userB
    HostName github.com
    IdentityFile ~/.ssh/id_rsa_userB
    User userB</p>
<pre><code>
## 폴더 별 path 설정
1. ~/.gitconfig 파일을 열어 아래의 정보를 삭제한다.

+ 이는 Global로 설정해둔 user의 정보이다.
</code></pre><p>[user]
    email = <a href="mailto:userG@email.com">userG@email.com</a>
    name = userG</p>
<pre><code>
2. 그리고 ~/.gitconfig 파일을 맨 아래에 아래의 정보를 추가한다.
</code></pre><p>[includeIf &quot;gitdir:~/userA계정을 쓸 폴더의 경로&quot;]
      path = .gitconfig-userA</p>
<p>[includeIf &quot;gitdir:~/userB계정을 쓸 폴더의 경로&quot;]
      path = .gitconfig-userB</p>
<pre><code>3. path에 정의한 각각의 .gitconfig-계정명 파일을 만들고 내용을 입력한다.

### userA path
+ path가 .gitconfig-userA이므로 ~/.gitconfig-userA에 생성해야 한다.
+ 생성한 파일엔 아래와 같은 내용을 입력한다.
</code></pre><p>[user]
    email = <a href="mailto:userA@email.com">userA@email.com</a> (여기는 해당 폴더에서 사용하고자하는 github 계정의 이메일을 입력한다.)
    name = userA</p>
<p>[github]
    user = 여기는 반드시 github 프로필의 이름을 넣어줘야 한다.</p>
<pre><code>
### userB path
+ userA와 동일하다.

## 폴더별로 다른 github 계정이 적용되는 지 확인
### userA를 사용하는 폴더에서 아래 명령어 입력
+ userA 계정의 이름과 이메일이 나온다면 적용이 된 것이다.
```bash
$ git config user.name
$ git config user.email</code></pre><h3 id="userb를-사용하는-폴더에서-같은-명령어-입력">userB를 사용하는 폴더에서 같은 명령어 입력</h3>
<ul>
<li>userB 계정의 이름과 이메일이 나온다면 적용이 된 것이다.</li>
</ul>
<blockquote>
<p>만약 위 명령어 입력 시 아무 것도 나오지 않는다면 위에 설정에 잘못된 곳은 없는 지 다시 확인한다. 
특히 path 경로나 파일 이름, 계정 이름 등 대소문자까지 모두 일치해야함을 주의하자.</p>
</blockquote>
<h2 id="ssh를-이용한-git-clone">SSH를 이용한 git clone</h2>
<p>git repository clone 옵션 중 SSH의 주소는 아래와 같은 형식이다.</p>
<ul>
<li><a href="mailto:git@github.com">git@github.com</a>:userA/repositoryName.git</li>
</ul>
<blockquote>
<p>이때, 이걸 그대로 git clone 하면 안 된다.
<a href="mailto:git@github.com">git@github.com</a> 뒤에 -userA 와 같이 사용자 이름을 붙여줘야 한다.
이건 위에서 <code>$ git config user.name</code>해서 나온 사용자 이름을 붙여주면 된다.</p>
</blockquote>
<h3 id="처음-repository-clone-시">처음 repository clone 시,</h3>
<pre><code class="language-bash"># 처음 repository clone 시 설정
$ git clone git@github.com-userA:userA/repositoryName.git</code></pre>
<h3 id="이미-repository가-clone-되었을-시">이미 repository가 clone 되었을 시,</h3>
<p>이미 clone된 repository에 설정을 변경해주고 싶다면 아래와 같이 set-url을 이용하면 된다.</p>
<pre><code class="language-bash"># 이미 clone된 repository의 설정 변경
$ git remote set-url origin git@github.com-userA:userA/repositoryName.git</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HTML/CSS] 이미지 크기와 위치 조절 방법 3가지]]></title>
            <link>https://velog.io/@bambi-bam/HTMLCSS-%EC%9D%B4%EB%AF%B8%EC%A7%80-%ED%81%AC%EA%B8%B0%EC%99%80-%EC%9C%84%EC%B9%98-%EC%A1%B0%EC%A0%88-%EB%B0%A9%EB%B2%95-3%EA%B0%80%EC%A7%80</link>
            <guid>https://velog.io/@bambi-bam/HTMLCSS-%EC%9D%B4%EB%AF%B8%EC%A7%80-%ED%81%AC%EA%B8%B0%EC%99%80-%EC%9C%84%EC%B9%98-%EC%A1%B0%EC%A0%88-%EB%B0%A9%EB%B2%95-3%EA%B0%80%EC%A7%80</guid>
            <pubDate>Fri, 26 Nov 2021 12:58:38 GMT</pubDate>
            <description><![CDATA[<h1 id="이미지-크기-조절-방법-3가지">이미지 크기 조절 방법 3가지</h1>
<ol>
<li>&lt; img &gt; 태그 + position: absolute</li>
<li>&lt; img &gt; 태그 + object-fit</li>
<li>&lt; div &gt; 태그 + background-image: url();</li>
</ol>
<h2 id="1--img--태그--position-absolute">1. &lt; img &gt; 태그 + position: absolute</h2>
<blockquote>
<p>이미지를 컨테이너로 한 번 감싼 후, 가운데 정렬한 뒤 컨테이너 크기를 벗어나는 이미지 부분을 자르는 방법.
&lt; img &gt; 요소에 <code>top</code>과 <code>left</code>, <code>translate</code> 등의 속성으로 이미지의 위치를 조절할 수 있다.</p>
</blockquote>
<ol>
<li>&lt; img &gt; 태그를 &lt; div class=&#39;container&#39; &gt; 태그로 감싼다.</li>
<li>&lt; div class=&#39;container&#39; &gt; 태그 와 &lt; img &gt; 태그에 CSS 속성을 추가한다.</li>
</ol>
<h3 id="div-classcontainer--css-속성">&lt; div class=&#39;container&#39; &gt; CSS 속성</h3>
<ul>
<li><code>width</code>와 <code>height</code>는 렌더링하고 싶은 이미지의 크기를 설정한다.</li>
<li><code>overflow: hidden;</code>는 &lt; div class=&#39;container&#39; &gt; 태그를 넘어가는 부분의 이미지는 잘라준다.</li>
<li><code>position: relative;</code>는 &lt; img &gt; 태그에 position: absoulte를 해줄텐데 절대적인 위치의 기준점이 &lt; div class=&#39;container&#39; &gt;이 되도록 설정한다.</li>
</ul>
<h3 id="img--css-속성">&lt; img &gt; CSS 속성</h3>
<ul>
<li><code>positon: absoulte;</code>는 position: relative 속성을 가지는 가장 가까운 요소를 기준으로 절대적인 위치를 가지도록 한다.</li>
<li><code>width: 100%;</code>는 삽입된 이미지의 가로세로비를 유지한 채, 가로폭을 기준으로 세로 길이가 잘리도록 한다. 이때, 가로 길이는 &lt; div class=&#39;container&#39; &gt;에 설정한 width이다.</li>
<li><code>height: 100%;</code>는 삽입된 이미지의 가로세로비를 유지한 채, 세로폭을 기준으로 가로 길이가 잘리도록 한다. 이때, 세로 길이는 &lt; div class=&#39;container&#39; &gt;에 설정한 height이다.</li>
<li><code>width: 100%;</code>와 <code>height: 100%;</code> 를 모두 입력하면 삽입된 이미지의 가로세로비가 망가지더라도 &lt; div class=&#39;container&#39; &gt;에 설정한 width와 height 길이만큼 늘어난다.</li>
<li><code>top: 50%;</code>, <code>left: 50%;</code>, <code>transform: translate(-50%, -50%);</code>는 &lt; img &gt;태그를 &lt; div class=&#39;container&#39; &gt; 안에서 가운데 정렬시킨다.</li>
</ul>
<pre><code class="language-css">.container {
  width: 원하는 px 혹은 rem 혹은 em;
  height: 원하는 px 혹은 rem 혹은 em;
  overflow: hidden;
  position: relative;
}

.container &gt; img {
  position: absolute;
  width: 100%;
  /* height: 100%; */
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}</code></pre>
<h3 id="장점">장점</h3>
<ul>
<li>IE(Internet Explorer)에서도 호환된다. (<a href="https://caniuse.com/?search=position">Can I use?</a>)</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>&lt; img &gt; 태그를 불필요하게 &lt; div class=&#39;container&#39; &gt;와 같은 태그로 한 번 감싸줘야 한다.</li>
<li>설정해줘야하는 CSS 속성이 많다.</li>
</ul>
<h2 id="2--img--태그--object-fit">2. &lt; img &gt; 태그 + object-fit</h2>
<blockquote>
<p><code>object-fit</code> 속성을 이용해 이미지의 크기를 조절하는 방법
<code>object-position</code> 속성을 사용해 이미지의 위치를 조절할 수 있다.</p>
</blockquote>
<blockquote>
<p><code>object-fit</code> 속성은 &lt; img &gt; 요소나 &lt; video &gt; 요소와 같은 <a href="https://developer.mozilla.org/ko/docs/Web/CSS/Replaced_element">대체 요소</a>의 콘텐츠 크기를 조절하는 속성이다.
<code>object-position</code> 속성을 사용해 대체 요소 콘텐츠가 콘텐츠 박스 내에 위치할 지점을 바꿀 수 있다.</p>
</blockquote>
<ul>
<li><a href="https://developer.mozilla.org/ko/docs/Web/CSS/object-fit">출처 - MDN</a></li>
</ul>
<h3 id="장점-1">장점</h3>
<ul>
<li><code>object-fit</code> 속성 하나면 간편하게 이미지 크기를 설정할 수 있다.</li>
</ul>
<h3 id="단점-1">단점</h3>
<ul>
<li>IE(Internet Explorer)에서는 지원하지 않는다. (<a href="https://caniuse.com/?search=object-fit">Can I use?</a>) </li>
</ul>
<h2 id="3--div--태그--background-image-url">3. &lt; div &gt; 태그 + background-image: url();</h2>
<blockquote>
<p>이미지를 &lt; div &gt; 태그에 <code>background-image</code> 속성을 이용해 배경 이미지로 삽입하는 방법이다.
<code>background-size</code> 속성으로 크기를 조절한다.
<code>background-position</code> 속성을 이용해 이미지의 위치 조정이 가능하다.</p>
</blockquote>
<h3 id="장점-2">장점</h3>
<ul>
<li>IE(Internet Explorer)에서도 호환된다. (<a href="https://caniuse.com/?search=background-image">Can I use?</a>)</li>
</ul>
<h3 id="단점-2">단점</h3>
<p><strong>아래의 단점은 이미지가 콘텐츠가 아닌 꾸미는 용도인 배경 이미지로 삽입하려는 의도였다면 무관하다.</strong></p>
<ul>
<li>이미지를 삽입한 것인데 &lt; img &gt; 태그를 사용할 수 없어 UX나 SEO, 웹 접근성 측면에서 좋지 않다.<ul>
<li>UX의 경우, &lt; img &gt; 태그에서 지원되는 이미지 복사 및 저장 기능을 사용할 없고 drag and drop 기능도 적용되지 않는다.</li>
<li>SEO나 웹 접근성 측면에선, 이미지가 &lt; div &gt; 태그로 인식되기 때문에 시맨틱한 마크업이라고 볼 수 없다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Instagram 사용자입장에서 웹 접근성 - 대체 텍스트]]></title>
            <link>https://velog.io/@bambi-bam/%EC%9D%B8%EC%8A%A4%ED%83%80-%EA%B7%B8%EB%9E%A8%EC%97%90%EC%84%9C-alt%EB%8C%80%EC%B2%B4-%ED%85%8D%EC%8A%A4%ED%8A%B8-%EC%B6%94%EA%B0%80%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@bambi-bam/%EC%9D%B8%EC%8A%A4%ED%83%80-%EA%B7%B8%EB%9E%A8%EC%97%90%EC%84%9C-alt%EB%8C%80%EC%B2%B4-%ED%85%8D%EC%8A%A4%ED%8A%B8-%EC%B6%94%EA%B0%80%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Wed, 24 Nov 2021 17:19:21 GMT</pubDate>
            <description><![CDATA[<h1 id="작성-이유">작성 이유</h1>
<p>웹 접근성을 처음 접한 것은 프론트 엔드 개발을 시작하면서였다. 그러다보니 항상 개발자의 입장에서 웹 접근성을 고려해왔다. 하지만 <strong>사용자의 입장에서도 웹 접근성에 대해 고려해야할 점이 있다고는 생각하지 못했었다.</strong>
Instagram 게시 시, 대체 텍스트가 그 예이다. 지금까지 Instagram을 자주 사용했지만 사진에 대체 텍스트를 넣은 적은 없었다. 
<strong><code>Imgar 프로젝트</code>를 하면서 대체 텍스트에 대해 여러 가지 고민을 할 기회가 있었고, 그 과정에서 Instagram의 사진에 대체 텍스트를 넣을 수 있다는 사실을 알게 되어 정리</strong>해둔다.</p>
<h2 id="instagram-대체-텍스트">Instagram 대체 텍스트</h2>
<p>인스타그램에서 대체 텍스트는 아래와 같은 상황에서 필요하다.</p>
<ol>
<li>네트워크 오류로 사진을 불러올 수 없을 때 동일한 정보를 주기 위해</li>
<li>스크린 리더 사용자에게 동일한 정보를 주기 위해</li>
</ol>
<h2 id="instagram-자동-대체-텍스트">Instagram 자동 대체 텍스트</h2>
<p>Instagram에서는 게시글 등록 시 자동으로 대체 텍스트를 생성해 제공한다.
하지만 이는 정확하지 않을 수 있다. 
가장 좋은 방법은 사진을 게시하는 사용자가 사진에 대한 자세한 설명을 제공하는 것이다.</p>
<blockquote>
<p>이미지를 업로드 또는 게시한 사람이 대체 텍스트를 포함하지 않을 경우 Facebook의 자동 대체 텍스트(AAT) 기술을 통해 컴퓨터 비전과 인공 지능이 사용되어 이미지에 대한 설명이 자동으로 생성됩니다. 자동 대체 텍스트는 완전하지 않을 수도 있습니다. 하지만 직접 업로드 또는 게시하는 이미지에 대한 대체 텍스트를 수정할 수 있습니다. 
<a href="www.facebook.com/help/216219865403298/?helpref=uf_share">출처 - Facebook에서 자동 대체 텍스트는 어떻게 작동하나요?</a></p>
</blockquote>
<h2 id="instagram에-적용된-대체-텍스트-확인">Instagram에 적용된 대체 텍스트 확인</h2>
<p>Instagram 웹 버전에서 확인해보면, <strong>img 요소의 alt속성에 대체 텍스트가 들어가 있는 것을 확인</strong>할 수 있다.
<img src='https://images.velog.io/images/bambi-bam/post/0254ba70-7742-4431-b909-22886648a091/%EC%BA%A1%EC%B2%98.PNG'/></p>
<h2 id="게시글-등록-시-대체-텍스트-추가">게시글 등록 시 대체 텍스트 추가</h2>
<p>게시글 등록 시, <code>고급 설정</code> &gt; <code>대체 텍스트 입력</code> &gt; <code>사진에 대한 자세한 설명 추가</code></p>
<img src='https://images.velog.io/images/bambi-bam/post/f363283f-ceb8-44af-be09-0ad5e9c15fb8/%EA%B2%8C%EC%8B%9C%EA%B8%80%20%EB%93%B1%EB%A1%9D%20%EC%8B%9C%20%EB%8C%80%EC%B2%B4%20%ED%85%8D%EC%8A%A4%ED%8A%B8%20%EC%84%A4%EC%A0%95.gif' width=300>

<h2 id="이미-등록된-게시글에-대체-텍스트-추가">이미 등록된 게시글에 대체 텍스트 추가</h2>
<p>게시글 수정 시, <code>수정</code> &gt; 사진 우측 아래 <code>대체 텍스트 수정</code> &gt; <code>사진에 대한 자세한 설명 추가</code></p>
<img src='https://images.velog.io/images/bambi-bam/post/a71e93b3-5e6b-42f2-a98c-6c06869d56ab/%EA%B2%8C%EC%8B%9C%EA%B8%80%20%EC%88%98%EC%A0%95%20%EC%8B%9C%20%EB%8C%80%EC%B2%B4%20%ED%85%8D%EC%8A%A4%ED%8A%B8%20%EC%84%A4%EC%A0%95.gif' width=300>]]></description>
        </item>
        <item>
            <title><![CDATA[[Issue] SVG를 React 컴포넌트로 사용 시 오류]]></title>
            <link>https://velog.io/@bambi-bam/Issue-SVG%EB%A5%BC-React-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%A1%9C-%EC%82%AC%EC%9A%A9-%EC%8B%9C-%EC%98%A4%EB%A5%98</link>
            <guid>https://velog.io/@bambi-bam/Issue-SVG%EB%A5%BC-React-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%A1%9C-%EC%82%AC%EC%9A%A9-%EC%8B%9C-%EC%98%A4%EB%A5%98</guid>
            <pubDate>Wed, 06 Oct 2021 15:51:56 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-상황">문제 상황</h2>
<p><img src='https://images.velog.io/images/bambi-bam/post/4a5f1fb2-4b65-4973-b53b-2a40799488a1/image.png' width=700></img></p>
<pre><code class="language-typescript">import { ReactComponent as upIconSVG } from &#39;../../assets/Icon/UpIcon.svg&#39;;</code></pre>
<p>위와 같이 UPIcon.svg 파일을 upIconSVG라는 이름의 React 컴포넌트로 불러와 JSX로 렌더링하려고 했더니 아래와 같이 JSX.IntrinsicElements 형식에 upIconSVG 속성이 없다는 오류가 발생했다.</p>
<img src='https://images.velog.io/images/bambi-bam/post/8f582486-0ef7-4eda-a062-435fa45a0c3d/image.png' width='500'/>

<h2 id="문제-이유">문제 이유</h2>
<p>처음엔 webpack 설정에 문제가 있나 고민했는데 찾아낸 이유는 아주 간단했다. Element가 &lt; upIcon /&gt;과 같이 소문자로 시작하여 문제가 되었던 것이다. 소문자로 시작하는 경우, React에서 내장 컴포넌트로 해석되는데 upIcon은 import한 컴포넌트이기 때문이다.</p>
<h3 id="element가-소문자로-시작하는-경우">Element가 소문자로 시작하는 경우</h3>
<p>&lt; div &gt;나 &lt; span &gt; 같은 내장 컴포넌트라는 것을 뜻하며 &#39;div&#39;나 &#39;span&#39; 같은 문자열 형태로 React.createElement에 전달된다.</p>
<h3 id="element가-대문자로-시작하는-경우">Element가 대문자로 시작하는 경우</h3>
<p>&lt; Foo /&gt; 와 같이 대문자로 시작하는 타입들은 React.createElement(Foo)의 형태로 컴파일 되며  JavaScript 파일 내에 사용자가 정의했거나 import한 컴포넌트를 가리킨다.</p>
<h2 id="문제-해결">문제 해결</h2>
<pre><code class="language-typescript">import { ReactComponent as UpIconSVG } from &#39;../../assets/Icon/UpIcon.svg&#39;;</code></pre>
<p>위와 같이 대문자로 시작하도록 바꿔주니 정상작동했다.</p>
<h2 id="참고-자료">참고 자료</h2>
<p><a href="https://ko.reactjs.org/docs/jsx-in-depth.html#user-defined-components-must-be-capitalized">React 공식문서 &gt; JSX 이해하기 &gt; React Element의 타입 지정하기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Issue] 문자열 리터럴 타입(String Literal Types) 사용 시 에러]]></title>
            <link>https://velog.io/@bambi-bam/Issue-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%A6%AC%ED%84%B0%EB%9F%B4-%ED%83%80%EC%9E%85String-Literal-Types-%EC%82%AC%EC%9A%A9-%EC%8B%9C-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@bambi-bam/Issue-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%A6%AC%ED%84%B0%EB%9F%B4-%ED%83%80%EC%9E%85String-Literal-Types-%EC%82%AC%EC%9A%A9-%EC%8B%9C-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Wed, 06 Oct 2021 14:11:00 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-상황">문제 상황</h2>
<img src='https://images.velog.io/images/bambi-bam/post/baa86633-9a96-4e4d-afef-5eed303565cb/image.png' width = '700' />

<p>오른쪽에서 타입스크립트의 <a href="https://joshua1988.github.io/ts/guide/interfaces.html">인터페이스(interface)</a>로 객체의 속성과 속성의 타입을 정의했다.
그리고 ImageCard 컴포넌트 테스트를 위해 더미데이터인 imageInfo 객체를 만들어 props로 넘겨줬더니 아래와 같이 타입이 일치하지 않다는 오류가 발생했다.</p>
<img src='https://images.velog.io/images/bambi-bam/post/40e335bd-da09-4ca9-ab7a-1f09c149c547/image.png' width='400'/>

<p>ImageInfo의 type 속성의 경우 문자열 리터럴 타입(string literal types)을 이용해 &#39;image/jpeg&#39; 혹은 &#39;video/mp4&#39;만을 허용한다.
그래서 더미데이터인 imageInfo의 type 속성에 &#39;image/jpeg&#39;로 넣어줬는데 오류가 발생한 것이다.</p>
<h2 id="문제-이유">문제 이유</h2>
<p>자세히 읽어보니, 더미데이터인 imageInfo의 type 속성에 넣어준 &#39;image/jpeg&#39;는 <strong>string 타입</strong>이고, 인터페이스인 ImageInfo의 type 속성에 정의된 타입은 <strong>문자열 리터럴 타입(string literal types)</strong>이기 때문이었다.</p>
<h2 id="해결-방법">해결 방법</h2>
<h3 id="처음-접근좋은-방법x">처음 접근(좋은 방법X)</h3>
<p>처음엔 더미데이터인 imageInfo의 type 속성에 &#39;image/jpeg&#39;를 그냥 넣어줬더니 타입스크립트가 string으로 타입추론을 해서 문제가 되었다고 판단했다.</p>
<pre><code class="language-typescript">const imageInfo = {
  id: &#39;id1234&#39;;
  description: &#39;null&#39;;
  type: &#39;image/jpeg&#39; as const;
  hasSound: false;
  imageWidth: 100;
  imageHeight: 300;
  bandWidth: 400;
} </code></pre>
<p>그래서 타입 추론을 &#39;image/jpeg&#39;라는 리터럴 타입을 가지도록 <code>as const</code>로 <a href="https://joshua1988.github.io/ts/guide/type-assertion.html#%ED%83%80%EC%9E%85-%EB%8B%A8%EC%96%B8-type-assertion">타입 단언(type assertion)</a>을 해주었다.
타입스트립트에서 const로 선언한 경우, 리터럴 자체로 타입을 추론하기 때문에 &#39;image/jpeg&#39;는 string 타입이 아닌 &#39;image/jpeg&#39;라는 리터럴 타입으로 추론이 되게 된다.</p>
<h3 id="적절한-방법">적절한 방법</h3>
<p>위의 방법으로 오류는 해결할 수 있지만, 임시방편일 뿐이다.
데미데이터인 imageInfo의 type 속성만이 아니라 다른 속성들도 ImageInfo 인터페이스 타입을 만족해야하는 상황이므로, 데미데이터인 <strong>imageInfo의 타입 자체를 imageInfo 인터페이스 타입임을 명시하는 것이 적절</strong>하다.</p>
<pre><code class="language-typescript">const imageInfo: ImageInfo = {
  id: &#39;id1234&#39;,
  description: &#39;null&#39;,
  type: &#39;image/jpeg&#39;,
  hasSound: false,
  imageWidth: 100,
  imageHeight: 300,
  bandWidth: 400,
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] SPA 방법 및 문제점]]></title>
            <link>https://velog.io/@bambi-bam/React-SPA-%EB%B0%A9%EB%B2%95-%EB%B0%8F-%EB%AC%B8%EC%A0%9C%EC%A0%90</link>
            <guid>https://velog.io/@bambi-bam/React-SPA-%EB%B0%A9%EB%B2%95-%EB%B0%8F-%EB%AC%B8%EC%A0%9C%EC%A0%90</guid>
            <pubDate>Sun, 12 Sep 2021 16:16:23 GMT</pubDate>
            <description><![CDATA[<h2 id="spa-방법">SPA 방법</h2>
<p>react-router-dom을 이용한다.</p>
<pre><code class="language-jsx">import { Route, Switch, Redirect, NavLink } from &#39;react-router-dom&#39;;

&lt;Router&gt;
  &lt;Switch&gt;
    &lt;Route path={[&#39;/&#39;, &#39;/tab1&#39;]} exact&gt;
      / 혹은 /tab1에서 보여줄 돔 요소
    &lt;/Route&gt;
    &lt;Route path=&quot;/tab2&quot;&gt;
      /tab2에서 보여줄 돔 요소
    &lt;/Route&gt;
    &lt;Route path=&quot;/tab3&quot;&gt;
      /tab3에서 보여줄 돔 요소
    &lt;/Route&gt;
  &lt;/Switch&gt;
&lt;/Router&gt;</code></pre>
<h2 id="문제점">문제점</h2>
<p>탭을 누르는 것 뿐인데 path가 바뀌어서 마케팅 팀 쪽에서는 클릭 수에 대한 정보를 정확하게 파악할 수 없게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] CORS 에러 해결]]></title>
            <link>https://velog.io/@bambi-bam/React-CORS-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@bambi-bam/React-CORS-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Sun, 12 Sep 2021 15:49:38 GMT</pubDate>
            <description><![CDATA[<h2 id="cors-에러-원인">CORS 에러 원인</h2>
<p>api를 가져다 쓰는 데 api가 있는 서버의 주소와 api를 가져오는 클라이언트의 주소가 달랐기 때문</p>
<h2 id="해결-방법">해결 방법</h2>
<p>서버와 클라이언트 사이에 proxy를 둬서 해결 할 수 있다.</p>
<h2 id="webpack의-proxy-기능">Webpack의 proxy 기능</h2>
<p>Webpack에서는 Proxy 기능을 지원해 준다.
package.json 파일에 서버의 루트를 추가해주면 된다.</p>
<pre><code class="language-json">{
  &quot;proxy&quot;: &quot;api를 가져오고자 하는 서버의 루트 URL(http://xxx)&quot;
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] 프로그래머스 위클리 챌린지 3주차 퍼즐 조각 채우기]]></title>
            <link>https://velog.io/@bambi-bam/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%9C%84%ED%81%B4%EB%A6%AC-%EC%B1%8C%EB%A6%B0%EC%A7%80-3%EC%A3%BC%EC%B0%A8-%ED%8D%BC%EC%A6%90-%EC%A1%B0%EA%B0%81-%EC%B1%84%EC%9A%B0%EA%B8%B0</link>
            <guid>https://velog.io/@bambi-bam/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%9C%84%ED%81%B4%EB%A6%AC-%EC%B1%8C%EB%A6%B0%EC%A7%80-3%EC%A3%BC%EC%B0%A8-%ED%8D%BC%EC%A6%90-%EC%A1%B0%EA%B0%81-%EC%B1%84%EC%9A%B0%EA%B8%B0</guid>
            <pubDate>Mon, 06 Sep 2021 08:33:52 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-링크">문제 링크</h2>
<ul>
<li><a href="https://images.velog.io/images/bambi-bam/post/0254ba70-7742-4431-b909-22886648a091/%EC%BA%A1%EC%B2%98.PNG">코딩테스트 연습 &gt; 위클리 챌린지 &gt; 퍼즐 조각 채우기</a></li>
</ul>
<h2 id="문제-접근">문제 접근</h2>
<ol>
<li><p>빈 공간의 블록 모양을 파악한다. <strong>(회전 고려X)</strong></p>
</li>
<li><p>퍼즐 조각의 모양을 파악한다. <strong>(4방향 회전을 모두 고려한다.)</strong></p>
</li>
<li><p>퍼즐 조각과 일치하는 빈 블록 모양이 있는 지 확인한다.    </p>
<ul>
<li><p>일치한다면, 이후의 퍼즐 조각은 해당 빈 블록은 비교하지 않는다.
(해당 빈 블록은 이제 채워진 상태이다.)</p>
</li>
<li><p>일치하지 않는다면, 다음 빈 블록 모양과 비교하거나 다음 빈 블록 모양이 없다면 다음 퍼즐 조각으로 넘어간다.</p>
</li>
</ul>
</li>
</ol>
<ul>
<li><strong>빈 공간의 블록 모양은 회전을 고려하지 않는 이유 :</strong> 퍼즐 조각을 4방향으로 돌려 빈 공간과 일치하는 지 확인할 것이기 때문</li>
</ul>
<h2 id="문제-풀이">문제 풀이</h2>
<h3 id="getshapearr-basex-basey">getShape(arr, baseX, baseY)</h3>
<p>arr[baseX][baseY]를 기준으로  arr[baseX][baseY]와 같은 value를 가지는 인접한 칸의 행과 열의 좌표를 구한다.</p>
<pre><code class="language-javascript">// 하나의 모양의 좌표들 구하기
const getShape = (arr, baseX, baseY) =&gt; {
  const shape = [];
  const value = arr[baseX][baseY];

  const dx = [1, 0, -1, 0];
  const dy = [0, 1, 0, -1];

  // value값과 같은 인접한 좌표들을 탐색
  const bfs = (x, y, value) =&gt; {
    if (arr[x][y] !== value) return;

    shape.push([x - baseX, y - baseY]);
    arr[x][y] = value ? 0 : 1;

    for (let i = 0; i &lt; 4; i++) {
      const nx = x + dx[i];
      const ny = y + dy[i];
      if (
        nx &gt;= 0 &amp;&amp;
        nx &lt; arr.length &amp;&amp;
        ny &gt;= 0 &amp;&amp;
        ny &lt; arr.length &amp;&amp;
        arr[nx][ny] === value
      ) {
        bfs(nx, ny, value);
      }
    }
  };

  bfs(baseX, baseY, value);

  return shape;
};
</code></pre>
<h3 id="findshapesarr-value">findShapes(arr, value)</h3>
<p>arr 에서 value 값을 가지는 모양들을 찾는다.
arr를 돌면서 모양의 시작 지점을 찾아 getShape()함수를 호출해 찾은 shape들의 배열을 리턴한다.</p>
<pre><code class="language-javascript">const findShapes = (arr, value) =&gt; {
  const shapes = [];

  for (let i = 0; i &lt; arr.length; i++) {
    for (let j = 0; j &lt; arr.length; j++) {
      if (arr[i][j] === value) {
        shapes.push(getShape(arr, i, j));
      }
    }
  }

  return shapes;
};</code></pre>
<h3 id="getrotatedshapesshape">getRotatedShapes(shape)</h3>
<p>모양의 좌표값들을 오른쪽으로 90도 180도 270도로 회전했을 때 좌표값을 구한다.</p>
<pre><code class="language-javascript">const getRotatedShapes = shape =&gt; {
  const rotatedShapes = [shape];

  for (let i = 0; i &lt; 3; i++) {
    const lastShapeIdx = rotatedShapes.length - 1;

    rotatedShapes.push(
      rotatedShapes[lastShapeIdx].map(([x, y]) =&gt; [-y || 0, x])
    );
  }

  return rotatedShapes;
};</code></pre>
<h3 id="normalizeshapeshape">normalizeShape(shape)</h3>
<p>회전시킨 모양의 좌표들을 모두 (0,0)을 기준으로 좌표값을 가지도록 변환한다.
(음의 좌표값이 없도록 정규화한다.)</p>
<pre><code class="language-javascript">const normalizeShape = shape =&gt; {
  const [row, col] = shape.reduce(
    (acc, cur) =&gt; [Math.min(acc[0], cur[0]), Math.min(acc[1], cur[1])],
    [Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]
  );

  return shape
    .map(([r, c]) =&gt; [row &lt; 0 ? r - row : r, col &lt; 0 ? c - col : c])
    .sort()
    .flat()
    .join(&#39;&#39;);
};</code></pre>
<h3 id="solution">solution()</h3>
<h4 id="puzzleshapes">puzzleShapes</h4>
<p>table에서 퍼즐들의 모양을 회전가능한 경우의 수를 모두 고려하여 구한다.</p>
<h4 id="blankshapes">blankShapes</h4>
<p>game_board에서 빈 공간의 모양을 구한다.
(이 때는 회전가능한 경우의 수까지 고려하지 않아도 된다.)</p>
<h4 id="blankshapesforeach">blankShapes.forEach()</h4>
<p>game_board의 빈 공간들이 table에서 가져온 블록들의 경우의 수 중에 존재하는 지 확인 후, 존재한다면 그 블록은 다음부턴 확인하지 않고, 그 블록의 칸수만큼 answer에 더해준다.</p>
<pre><code class="language-javascript">function solution(game_board, table) {
  const PUZZLE_VALUE = 1;
  const BLANK_VALUE = 0;

  const puzzleShapes = findShapes(table, PUZZLE_VALUE)
    .map(shape =&gt; getRotatedShapes(shape))
    .map(rotatedShape =&gt; rotatedShape.map(shape =&gt; normalizeShape(shape)));

  const blankShapes = findShapes(game_board, BLANK_VALUE).map(shape =&gt;
    normalizeShape(shape)
  );

  let answer = 0;
  blankShapes.forEach(shape =&gt; {
    for (let i = 0; i &lt; puzzleShapes.length; i++) {
      if (puzzleShapes[i].includes(shape)) {
        puzzleShapes.splice(i, 1);
        answer += Math.floor(shape.length / 2);
        break;
      }
    }
  });

  return answer;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[카카오 이모티콘 샵 분석 및 개선 프로젝트]]></title>
            <link>https://velog.io/@bambi-bam/%EC%B9%B4%EC%B9%B4%EC%98%A4-%EC%9D%B4%EB%AA%A8%ED%8B%B0%EC%BD%98-%EC%83%B5-%EB%B6%84%EC%84%9D-%EB%B0%8F-%EA%B0%9C%EC%84%A0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</link>
            <guid>https://velog.io/@bambi-bam/%EC%B9%B4%EC%B9%B4%EC%98%A4-%EC%9D%B4%EB%AA%A8%ED%8B%B0%EC%BD%98-%EC%83%B5-%EB%B6%84%EC%84%9D-%EB%B0%8F-%EA%B0%9C%EC%84%A0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</guid>
            <pubDate>Mon, 06 Sep 2021 08:00:53 GMT</pubDate>
            <description><![CDATA[<h1 id="프로젝트-설명">프로젝트 설명</h1>
<p>카카오 이모티콘 샵을 마크업과 라이트 하우스를 기반으로 분석한 뒤, 개선점이 있다면 개선 방법까지 반영하여 <strong>html</strong>, <strong>css</strong>로 구현해보았다.</p>
<h2 id="프로젝트-진행-이유">프로젝트 진행 이유</h2>
<p>현재 이미 서비스 중인 웹 사이트를 분석하여 왜 지금과 같이 마크업을 했는지, 같은 기능을 다르게 구현할 수는 없었는지를 고민할 수 있었다. </p>
<h2 id="카카오-이모티콘-샵을-선택한-이유">카카오 이모티콘 샵을 선택한 이유</h2>
<p>✔ 직접 사용해 보았을 때, 이미지가 주 컨텐츠인만큼 이미지가 늦게 로딩되는 경우 그 공백이 눈에 들어왔다. </p>
<p>✔  Light House 분석 결과 Performance도 다른 웹 사이트에 비해 점수가 낮아서 이를 개선해보고 싶었다. </p>
<ul>
<li><p>카카오 이모티콘 샵 Light House 결과⬇️</p>
<img src='https://images.velog.io/images/bambi-bam/post/1d3b87c6-483e-430b-a3ef-dc908c28c980/image.png' alt='카카오 이모티콘 샵 Light House 분석 결과' title='카카오 이모티콘 샵 Light House 분석 결과' width='600'/> 
</li>
<li><p>개발자 도구 &gt; 네트워크 패널로 확인한 로딩 속도⬇️</p>
<img src='https://images.velog.io/images/bambi-bam/post/fd2b28dc-f157-4e3b-9032-f301bceed2b9/%EC%B9%B4%EC%B9%B4%EC%98%A4%EC%9D%B4%EB%AA%A8%ED%8B%B0%EC%BD%98%EC%83%B5%20%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%8F%84%EA%B5%AC%20Network%20%ED%8C%A8%EB%84%90%20img%20%ED%95%84%ED%84%B0.gif' alt='개발자 도구 > 네트워크 패널로 확인한 로딩 속도' title='개발자 도구 > 네트워크 패널로 확인한 로딩 속도' width='600'/>

</li>
</ul>
<h2 id="목표">목표</h2>
<p>✔ 카카오 이모티콘 샵의 메인 페이지를 <code>마크업</code>, <code>접근성</code>, <code>성능</code> 관점에서 분석하고 개선하기</p>
<p>✔ 개선 후 Light House 분석 결과를 비교해 유의미한 변화 확인</p>
<h2 id="역할-분배">역할 분배</h2>
<p>✅ <strong>HTML 마크업</strong>
✅ 헤더 및 네비게이션 구현
✅ <strong>인기 이모티콘 콘텐츠, 스타일 콘텐츠 구현</strong>
✅ 신규 이모티콘 콘텐츠, 푸터 영역 구현</p>
<p>HTML 마크업은 시맨틱한 마크업을 중점으로 다같이 의논하며 진행했고
내가 온전히 맡은 부분은 인기 이모티콘 콘텐츠와 스타일 콘텐츠 구현이었다.</p>
<h2 id="나의-주안점">나의 주안점</h2>
<ul>
<li>시맨틱 마크업</li>
<li>이미지 성능 최적화</li>
<li>WCAG 기반 접근성 개선</li>
<li>반응형 구현</li>
</ul>
<h2 id="이미지-성능-최적화">이미지 성능 최적화</h2>
<h3 id="기존-사이트의-문제점">기존 사이트의 문제점</h3>
<p>✔  현재 카카오 이모티콘 샵에서 사용하는 이미지 포맷은 모두 png 혹은 gif이다. </p>
<p>이들은 무손실 포맷으로 다른 포맷에 비해 파일 크기가 크다.
<a href="#%EC%B9%B4%EC%B9%B4%EC%98%A4-%EC%9D%B4%EB%AA%A8%ED%8B%B0%EC%BD%98-%EC%83%B5%EC%9D%84-%EC%84%A0%ED%83%9D%ED%95%9C-%EC%9D%B4%EC%9C%A0">위의 카카오 이모티콘 샵 Light House 결과</a>에서 이미 확인할 수 있듯이 크기가 큰 이미지 파일을 다운로드 시 전체 페이지 로딩 시간을 지연시킨다. </p>
<h3 id="이미지-파일-포맷-변경">이미지 파일 포맷 변경</h3>
<p>✔ 대부분의 웹 사용자들이 크게 구분하지 못할 정도의 화질 저하로 이미지 로딩 속도를 올릴 수 있다면, 그 편이 사용자 경험에 더 유리하다고 판단했다.</p>
<h3 id="1-webp로-변환">1. Webp로 변환</h3>
<p>png 대신 WebP로 변환하여 파일 크기를 25~35%로 줄였다.</p>
<ul>
<li>png와 webP 파일 크기 비교⬇️<img src = 'https://images.velog.io/images/bambi-bam/post/c6f1cd85-301c-42e0-9800-c1c5045125d3/image.png' alt='png를 webP로 변환하자 줄어든 파일 크기' title='png를 webP로 변환하자 줄어든 파일 크기' width='600'>
웹 사이트에서 보다 빠르게 이미지를 읽을 수 있도록 개발된 압축포맷이다.
손실 압축(JPEG)와 비손실 압축(PNG, GIF)를 모두 지원하며, 모두 약 30% 정도 용량을 줄여 빠른 웹 사이트 로딩이 가능하다.
하지만 WebP를 지원하지 않는 브라우저들도 있어 그런 경우엔 <source> 태그를 이용해  png 파일로 보이도록 설정해주었다.

</li>
</ul>
<pre><code class="language-html">  &lt;picture class=&quot;popularEmoticon__img&quot;&gt;             
      &lt;source
        type=&quot;image/webp&quot;
        srcset=&quot;./images/popularEmoticon/optimization/tiny-big-bean_thumbnail.webp&quot;
      /&gt;
      &lt;source
        type=&quot;image/png&quot;
        srcset=&quot;./images/popularEmoticon/tiny-big-bean_thumbnail.png&quot;
      /&gt;
      &lt;img
        class=&quot;popularEmoticon__img&quot;
        alt=&quot;뽀시래기 짱큰콩 썸네일&quot;
        src=&quot;./images/popularEmoticon/tiny-big-bean_thumbnail.png&quot;
      /&gt;
    &lt;/picture&gt;</code></pre>
<h3 id="mp4로-변환">Mp4로 변환</h3>
<p>gif의 경우 Mp4로 바꿔주어 용량을 줄였다.
WebP로도 변환하여 Webp가 지원되지 않으면 Mp4로 나오도록 했으면 더 좋았을 것 같다.</p>
<pre><code class="language-html">&lt;video 
       autoplay loop muted playsinline
       class=&quot;popularEmoticon__img popularEmoticon__img__hover&quot;&gt;
    &lt;source
            type=&quot;video/mp4&quot;
            src=&quot;./images/popularEmoticon/optimization/tiny-big-bean_hover.mp4&quot;/&gt;
  뽀시래기 짱큰콩 썸네일
&lt;/video&gt;</code></pre>
]]></description>
        </item>
    </channel>
</rss>