<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>devrun_ryan.log</title>
        <link>https://velog.io/</link>
        <description>$ npm run dev:ryan</description>
        <lastBuildDate>Wed, 05 Apr 2023 07:43:46 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>devrun_ryan.log</title>
            <url>https://velog.velcdn.com/images/devrun_ryan/profile/2a9063c9-f819-4cb7-8932-46917a884e71/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. devrun_ryan.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/devrun_ryan" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[rendering_SSR]]></title>
            <link>https://velog.io/@devrun_ryan/renderingSSR</link>
            <guid>https://velog.io/@devrun_ryan/renderingSSR</guid>
            <pubDate>Wed, 05 Apr 2023 07:43:46 GMT</pubDate>
            <description><![CDATA[<p>SSR(Server-Side Rendering)은 클라이언트 측에서 데이터를 받아와 렌더링하는 것이 아닌 클라이언트에서 요청시 서버 측에서 HTML 파일을 렌더링하여 클라이언트에게 보내주는 방식입니다. 이 방식은 SPA(Single-Page Application)에서 발생하는 초기 로딩 속도와 검색 엔진 최적화(SEO) 문제를 해결하기 위해 사용됩니다.</p>
<h2 id="장점">장점</h2>
<ol>
<li><p>초기 로딩 속도 개선 
SSR을 적용하면 서버에서 HTML 파일을 렌더링하므로 초기 로딩 속도가 빨라집니다. SPA에서는 클라이언트에서 JavaScript 파일을 다운로드하고 실행한 후에 데이터를 받아와서 렌더링하기 때문에 초기 로딩 속도가 느립니다. 하지만 SSR을 사용하면 서버에서 HTML 파일을 렌더링하므로 초기 로딩 속도가 빨라집니다.</p>
</li>
<li><p>SEO
SSR을 적용하면 검색 엔진 최적화 문제도 해결할 수 있습니다. 검색 엔진은 페이지의 HTML 코드를 크롤링하여 검색 결과에 반영합니다. SPA에서는 HTML 코드가 클라이언트에서 동적으로 생성되기 때문에 검색 엔진이 크롤링하지 못할 수 있습니다. 하지만 SSR을 사용하면 서버에서 HTML 파일을 렌더링하므로 검색 엔진이 크롤링할 수 있는 HTML 코드를 제공할 수 있습니다.</p>
</li>
<li><p>보안
CSR은 브라우저(클라이언트)에서 js를 통해 렌더링을 하게 되는데, 그 과정에서 서버와 통신이 발생한 경우 클라아언트에서 노출이 될 수 있습니다. 그러나, SSR은 서버에서 렌더링한 HTML을 전달해주기 때문에 브라우저(클라이언트)에 서버와의 통신과 관련된 보안적인 내용에 대해 노출될 가능성이 적다고 볼 수 있습니다. console.log를 할 때 서버에서 실행되는 환경일 경우 브라우저 콘솔이 아닌 terminal에 출력되기 때문에 사용자가 보지 않아도 되는 콘솔내용에 대해 노출되지 않을 수도 있을 것 같습니다.</p>
</li>
<li><p>SSG, ISR과 다르게 요청할 때마다 서버에서 렌더링하기 때문에 실시간 데이터를 사용해서 반영할 수 있고, 사용자별 필요한 데이터를 사용할 수 있다는 것도 장점이 될 수 있다고 생각합니다.</p>
</li>
<li><p>js 없어도 렌더링 됨
서버에서 받은 html로 초기 렌더링 되기 때문에 js를 비활성화 하더라도 화면에 렌더링이 가능합니다.</p>
</li>
</ol>
<h2 id="단점">단점</h2>
<ol>
<li><p>속도
요청할 때마다 서버에서 렌더링해서 전달해주기 때문에, 서버에 과부하가 걸릴 수 있으며 비교적 느릴 수 있다는 단점이 있습니다. (사용자가 많아질수록 사이트에 접속하는 시간이 오래 걸릴수 있습니다.)</p>
</li>
<li><p>캐시
요청할 때마다 렌더링을 하게 되기 때문에, CDN에 캐시가 안된다는 단점이 있습니다. nextjs 프레임워크를 사용할 경우 일정부분은 개선이 될 수도 있다고 생각은 됩니다.</p>
</li>
<li><p>서버환경
js의 경우 서버는 Node.js 환경에서 동작하기 때문에 Node.js와 서버에 대한 이해가 필요할 수 있으며, 브라우저API(window, event(click, change, ...), 기타 등등...)를 사용하지 못한다는 것을 고려해야 될 수 있습니다.</p>
</li>
<li><p>hydrate, css-in-js
서버에서 만든 html로 먼저 화면에 표시되기 때문에 사용자가 초기에 화면을 보는 속도는 빠를수는 있습니다. 그러나, 이후 css js 파일을 다운로드 받아 실행하고, 인터렉션이 가능하게 되는 구조(hydrate)에서 오는 단점도 있다고 생각됩니다.</p>
<ul>
<li>html이 화면에 렌더링은 되지만, js가 실행되지 않을 경우 클릭과 같은 사용자 인터렉션(상호작용)에 반응하지 못합니다. 추가로, 요청할 때 서버에서 html을 만들어서 주기 때문에, 서버에서 html을 받기 이전까지 화면에 표시되지 않아 깜빡이는 현상이 발생할 수도 있습니다.</li>
<li>styled-components와 같은 css-in-js를 통해 스타일을 적용할 경우, js가 비활성화되있을 경우 적용되지 않으며, js가 활성화 되있어도 초기에는 html css로 적용되다가, 이후 js로 styled-components가 실행될 경우 해당 스타일이 적용되기 때문에 시간이 지난뒤 스타일이 적용되면서 발생하는 문제가 있을 수 있습니다(Cumulative Layout Shift 등)</li>
<li>서버에서 렌더링할 때 css-in-js를 적용해서 html을 전달해줄 수 있지만, 결국 서버에서 처리하기 때문에 이로 인한 비용증가 및 관련 문제도 고려하는 것이 필요할 수 있습니다. </li>
</ul>
</li>
</ol>
<p>요약하자면, SSR은 CSR의 초기 로딩 속도와 검색 엔진 최적화 문제를 해결하기 위해 사용되는 방식이라고 생각됩니다. SSR의 장점이 매력적인 것은 맞지만, 단점도 명확하게 존재하기 때문에 사용하는 목적에 따라 어떤 렌더링 방식을 사용할지에 대해 결정하는 것도 중요하다고 생각됩니다. </p>
<p>nextjs 프레임워크를 이용한다면 페이지 단위로(13버전에서는 컴포넌트 단위로) 다른 렌더링 방식을 적용(하이브리드 웹 앱)해서 만들 수 있기 때문에, 이를 이용해서 만들 경우에도 각각의 렌더링 방식에 대해 이해하고 있는 것이 도움이 될 수 있다고 생각됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[rendering_CSR]]></title>
            <link>https://velog.io/@devrun_ryan/renderingCSR</link>
            <guid>https://velog.io/@devrun_ryan/renderingCSR</guid>
            <pubDate>Wed, 22 Mar 2023 07:53:54 GMT</pubDate>
            <description><![CDATA[<h2 id="csr">CSR</h2>
<blockquote>
<p>Client Side Rendering</p>
</blockquote>
<p>브라우저에 표기(렌더링)하기 위한 모든 코드들을 클라이언트측에서 다운로드 받고, 클라이언트 측에서 코드를 실행하고 분석해서 표기하는 것</p>
<p><code>Client Side</code>: 렌더링하는 주체자가 클라이언트이며, 웹 어플리케이션에서는 브라우저를 의미한다.</p>
<ol>
<li>Client -&gt; <code>request</code> -&gt; Server</li>
<li>로딩 화면</li>
<li>Server -&gt; <code>HTML</code> -&gt; Client
body의 내용이 없는 HTML (아무것도 없는 HTML)
<code>vite react app 예시</code><pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
 &lt;meta charset=&quot;UTF-8&quot; /&gt;
 &lt;link rel=&quot;icon&quot; type=&quot;image/svg+xml&quot; href=&quot;/vite.svg&quot; /&gt;
 &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
 &lt;title&gt;Vite + React&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
 &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
 &lt;script type=&quot;module&quot; src=&quot;/src/main.jsx&quot;&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</li>
<li>HTML이 렌더링 되지만 내용이 없기 때문에 빈 화면(UI가 없음)을 보게 됨</li>
<li>React 라이브러리 소스 파일 및 js 소스 파일 로드(컴포넌트 포함)</li>
<li>React 라이브러리 로드 -&gt; root요소에 index(루트) 컴포넌트를 연결해서 렌더링</li>
<li>사용자가 paint된 화면을 보게 됨</li>
</ol>
<h2 id="장점">장점</h2>
<ul>
<li>한번 로딩 되면, 빠른 UX 제공<ul>
<li>1개의 페이지(HTML 파일 1개)</li>
<li>사용자의 요청에 따라 Ajax를 통해 필요한 부분만 변경해서 렌더링</li>
</ul>
</li>
<li>서버의 부하가 작음</li>
</ul>
<h2 id="단점">단점</h2>
<ul>
<li>페이지 로딩 시간(Time To View, First Contentful Paint)이 길다</li>
<li>브라우저 설정에서 js를 비활성화할 경우 웹 어플리케이션이 동작하지 않고, 사용자도 화면을 볼 수 없다 </li>
<li>SEO 최적화가 힘들다</li>
<li>보안에 취약하다(클라이언트에서 모든 코드를 받아서 클라이언트에서 실행 -&gt; 페이지를 표시하기 위한 데이터를 서버에서 받아오기 위해 필요한 키등이 노출되기 때문)</li>
<li>CDN(Content Delivery Network)에 캐시가 안된다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[DesignPattern_Container/Presentational ]]></title>
            <link>https://velog.io/@devrun_ryan/DesignPatternContainerPresentational</link>
            <guid>https://velog.io/@devrun_ryan/DesignPatternContainerPresentational</guid>
            <pubDate>Wed, 15 Mar 2023 03:49:06 GMT</pubDate>
            <description><![CDATA[<p>React에서 관심사의 분리(SoC) 를 강제하는 방법은 Container/Presentational Pattern을 이용하는 방법이 있다. 이 를 통해 비즈니스 로직에서 뷰를 분리해낼 수 있다.</p>
<p>6개의 강아지 사진을 다운로드받아 화면에 렌더하는 앱을 만든다고 가정해 보자.</p>
<p>이상적으로는 이 프로세스를 아래 두가지로 분리하여 관심사의 분리를 강제하고 싶다.</p>
<ul>
<li>Presentational Components: 데이터가 어떻게 사용자에게 보여질 지에 대해서만 다루는 컴포넌트. 예제에서는 강아지 사진의 목록을 렌더링하는 부분이다.</li>
<li>Container Components: 어떤 데이터가 보여질 지에 대해 다루는 컴포넌트. 예제에서는 강아지 사진들을 다운로드한다.</li>
</ul>
<p><video controls width="500" src="https://patterns-dev-kr.github.io/design-container-presentational01.mp4"></video></p>
<p>강아지 사진을 다운로드하는것은 비즈니스 로직의 역할이고. 이미지를 보여주는 것은 뷰의 역할이다.</p>
<h2 id="presentational-component">Presentational Component</h2>
<p><code>Presentational 컴포넌트</code>는 props를 통해 데이터를 받는다. 이 컴포넌트의 주요 기능은 받은 데이터를 화면에 표현하는것이며 그 목적을 위해 스타일시트를 포함한다. 데이터는 건드리지 않는다.</p>
<p>아래 강아지 사진을 출력하는 예제를 확인해 보자. 강아지 사진을 렌더링 할 때. 단순히 각 이미지들을 API로부터 다운로드 받고 화면에 렌더링 하는데. 그렇게 하지 말고. 이미지들을 props를 통해 받아 화면에 그리는 함수형 컴포넌트만 만드는 것이다.</p>
<pre><code class="language-jsx">import React from &quot;react&quot;;

export default function DogImages({ dogs }) {
  return dogs.map((dog, i) =&gt; &lt;img src={dog} key={i} alt=&quot;Dog&quot; /&gt;);
}</code></pre>
<p>여기서 <code>DogImages</code> 컴포넌트는 Presentational 컴포넌트이다. Presentational 컴포넌트는 UI 변경을 위한 상태 외에는 상태를 갖지 않는다. prop을 통해 받은 데이터는 Presentational 컴포넌트에 의해 수정되지 않는다.</p>
<p>Presentational 컴포넌트는 Container 컴포넌트로부터 데이터를 받는다.</p>
<h2 id="container-컴포넌트">Container 컴포넌트</h2>
<p><code>Container 컴포넌트</code>의 주요 기능은 Presentational 컴포넌트에 데이터를 전달하는 것이다. Container 컴포넌트 자체는 화면에 아무것도 렌더링하지 않는다. Container 컴포넌트는 아무것도 화면에 그리지 않으니 스타일시트도 포함하지 않는다.</p>
<p>아래 예제에서 강아지 사진 목록을 <code>DogImages</code> 컴포넌트에 전달하기 위해 Container 컴포넌트를 만들었다. 만들어진 Container 컴포넌트는 외부 API로부터 강아지 이미지를 다운로드하고 Presentational 컴포넌트인 <code>DogImages</code> 컴포넌트에게 전달하고 있다.</p>
<pre><code class="language-jsx">import React from &quot;react&quot;;
import DogImages from &quot;./DogImages&quot;;

export default class DogImagesContainer extends React.Component {
  constructor() {
    super();
    this.state = {
      dogs: []
    };
  }

  componentDidMount() {
    fetch(&quot;https://dog.ceo/api/breed/labrador/images/random/6&quot;)
      .then(res =&gt; res.json())
      .then(({ message }) =&gt; this.setState({ dogs: message }));
  }

  render() {
    return &lt;DogImages dogs={this.state.dogs} /&gt;;
  }
}</code></pre>
<p>위의 두 컴포넌트를 조합하면 비즈니스 로직과 뷰를 분리하고 있다.</p>
<h2 id="hooks">Hooks</h2>
<p>대개 Container/Presentational 패턴은 React Hooks로 대체 가능하다. React 에 Hooks가 추가되면서 Container 컴포넌트 없이도 stateless 컴포넌트를 쉽게 만들 수 있게 되었다.</p>
<p>DogImagesContainer 컴포넌트에 있는 데이터 로드 코드를 아래와 같이 커스텀 훅으로 만들 수 있다.</p>
<pre><code class="language-jsx">export default function useDogImages() {
  const [dogs, setDogs] = useState([])

  useEffect(() =&gt; {
    fetch(&#39;https://dog.ceo/api/breed/labrador/images/random/6&#39;)
      .then(res =&gt; res.json())
      .then(({ message }) =&gt; setDogs(message))
  }, [])

  return dogs
}</code></pre>
<p>위의 훅을 사용하면 데이터를 받아오기 위해 <code>DogImagesContainer</code> 컴포넌트를 사용할 필요가 없다. 대신 Presentational 컴포넌트인 <code>DogImages</code> 에서 훅을 직접 호출해 사용하면 된다.</p>
<pre><code class="language-jsx">import React from &quot;react&quot;;
import useDogImages from &quot;./useDogImages&quot;;

export default function DogImages() {
  const dogs = useDogImages();

  return dogs.map((dog, i) =&gt; &lt;img src={dog} key={i} alt=&quot;Dog&quot; /&gt;);
}</code></pre>
<p><code>useDogImages</code> 훅을 사용하여 Container/Presentational 패턴을 사용한 것과 같이 비즈니스 로직과 뷰를 분리했다. <code>DogImages</code> 는 단순히 훅에서 반환된 값을 수정없이 사용하면 된다.</p>
<p><video width="500" controls src="https://patterns-dev-kr.github.io/design-container-presentational03.mp4"></video></p>
<p>훅은 이 장에서 소개한 패턴처럼 비즈니스 로직과 뷰를 쉽게 분리할 수 있게 해주고. 불필요한 Container 래핑을 줄일 수 있게 해 준다.</p>
<h2 id="장점">장점</h2>
<p>Container/Presentational 패턴은 여러 장점들을 가지고 있다.</p>
<p>해당 패턴을 활용하면 자연스럽게 관심사의 분리를 구현하게 된다. Presentational 컴포넌트는 UI를 담당하는 순수함수로 작성하게 되는 반면 Container 컴포넌트는 상태와 기타 데이터를 책임지게 된다.</p>
<p>Presentational 컴포넌트는 데이터 변경 없이 화면에 출력할 수 있으므로 앱의 여러 곳에서 다양한 목적으로 재사용할 수 있다.</p>
<p>Presentational 컴포넌트는 앱의 비즈니스 로직을 수정하지 않으므로 코드베이스에 대한 이해가 깊지 않은 개발자더라도 쉽게 수정이 가능하다. 공통으로 쓰이는 Presentational 컴포넌트가 디자인의 요구사항에 따라 수정하면 앱 전체에서 반영된다.</p>
<p>Presentational 컴포넌트는 테스트하기도 쉽다. 일반적으로 순수함수로 구현되므로 전체 목 데이터 스토어를 만들 필요 없이 요구하는 데이터만 인자로 넘겨주면 된다.</p>
<h2 id="단점">단점</h2>
<p>Container/Presentational 패턴은 비즈니스 로직과 렌더링 로직을 쉽게 분리할 수 있지만 훅을 활용하면 클래스형 컴포넌트를 사용하지 않고도, 또 이 패턴을 따르지 않아도 같은 효과를 볼 수 있다. 참고로 지금은 상태를 가진 컴포넌트도 함수형으로 만들 수 있다.</p>
<p>훅을 사용하더라도 이 패턴을 사용할 수는 있지만 너무 작은 규모의 앱에서는 오버엔지니어링 일 수 있다.</p>
<ul>
<li>참조
<a href="https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0">Presentational and Container Components - Dan Abramov</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL_Docker(도커)]]></title>
            <link>https://velog.io/@devrun_ryan/TILDocker%EB%8F%84%EC%BB%A4</link>
            <guid>https://velog.io/@devrun_ryan/TILDocker%EB%8F%84%EC%BB%A4</guid>
            <pubDate>Wed, 08 Feb 2023 07:31:30 GMT</pubDate>
            <description><![CDATA[<h2 id="출처">출처</h2>
<p>해당 글의 출처는 fastcampus의 프론트엔드 웹 개발의 모든 것 강의임을 알려드립니다.
<a href="https://fastcampus.co.kr/dev_online_fesignature">https://fastcampus.co.kr/dev_online_fesignature</a></p>
<h2 id="도커를-학습하고자-한-이유">도커를 학습하고자 한 이유</h2>
<p>실제 기업에서 개발을 하거나 부트 캠프 과정중에 협업 프로젝트를 할 경우 어느정도 이해를 하고 사용할 줄 알아야 된다는 필요성을 느껴서 학습하게 되었습니다. (요즘 도커를 사용하지 않는 곳이 거의 없다고 듣기도 해서...)</p>
<h2 id="도커를-사용하는-이유">도커를 사용하는 이유</h2>
<p>어떠한 프로그램을 다운 받는 과정을 굉장히 간단하게 만들기 위해서이다.</p>
<ul>
<li><p>도커 없이 프로그램을 받을 때 원래 프로그램을 다운 받고 실행하는 순서
<code>인스톨러 내려받기</code> =&gt; <code>인스톨러 실행</code> =&gt; <code>프로그램 설치 완료</code></p>
<ul>
<li>실제로 프로그램을 설치하는 과정 중에 갖고 있는 서버, 패키지 버전, 운영체제 등에 따라 프로그램을 설치하는 과정 중에 많은 에러 발생</li>
<li>또한 설치 과정이 다소 복잡함</li>
</ul>
</li>
</ul>
<h3 id="도커-없이-프로그램을-내려받을-때-예시redis">도커 없이 프로그램을 내려받을 때 예시(Redis)</h3>
<p><code>Redis 홈페이지로 이동</code> =&gt; <code>redis.io</code></p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/555001d1-1caf-4dd7-bbc6-5d7c7e468ebe/image.png" alt="redis_install"></p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/ead9b2bb-74c3-4ee9-8b4d-f17a0482502b/image.png" alt="command_notfound"></p>
<p><code>wget</code>이 없어서 에러가 발생하기 때문에, <code>wget</code>을 받은 후 다시 <code>Redis</code>를 받아야 한다.</p>
<p>이와 같이 어떤 특정 프로그램을 받을 때 거기에 맞는 부수적인 것들도 계속 받으면서 설치하는 과정이 있을 수 있기 때문에, 설치하는 과정이 복잡해지고 에러도 많이 생기게 된다.</p>
<h3 id="도커를-사용해서-프로그램을-내려받을-때-예시redis">도커를 사용해서 프로그램을 내려받을 때 예시(Redis)</h3>
<p><code>docker run -it redis</code> =&gt; <code>다운로드 끝</code></p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/950c38f1-7856-46b5-a78e-c86b3181197d/image.png" alt="docker_install_redis"></p>
<p>이와 같이 도커를 이용하여 프로그램을 설치하면 예상치 못한 에러도 덜 발생하며, 설치하는 과정도 훨씬 간단해진다.</p>
<h2 id="도커란-무엇인가">도커란 무엇인가?</h2>
<blockquote>
<p><code>컨테이너</code>를 사용하여 응용프로그램을 더 쉽게 만들고, 배포하고 실행할 수 있도록 설계된 도구이며, 컨테이너 기반의 오픈소스 가상화 플랫폼이며 생태계이다.</p>
</blockquote>
<ul>
<li>일반적인 컨테이너의 개념</li>
</ul>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/cb8b2feb-c73e-43bb-85e5-5286bd757f3e/image.png" alt="container"></p>
<p>위와 같이 컨테이너에 물건을 넣고 다양한 운송수단으로 쉽게 옮길 수 있다.</p>
<ul>
<li>서버에서 컨테이너의 개념</li>
</ul>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/fcd253e3-fa94-4c3b-8aef-96bdfa8a4a76/image.png" alt="container_server"></p>
<p>위와 같이 컨테이너 안에 다양한 프로그램, 실행환경을 컨테이너로 추상화 하고 동일한 인터페이스를 제공하여 프로그램의 배포 및 관리를 단순하게 해준다. (일반 컨테이너의 개념에서 물건을 손쉽게 운송 해주는 것 처럼) 프로그램을 손쉽게 이동,배포,관리를 할 수 있게 해준다. 또한, AWS, Azure, Google cloud 등 어디에서든 실행 가능하게 해준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[API_Mocking_library[Mock Service Worker]]]></title>
            <link>https://velog.io/@devrun_ryan/APIMockinglibraryMock-Service-Worker</link>
            <guid>https://velog.io/@devrun_ryan/APIMockinglibraryMock-Service-Worker</guid>
            <pubDate>Wed, 01 Feb 2023 07:43:48 GMT</pubDate>
            <description><![CDATA[<h2 id="reference">reference</h2>
<p><a href="https://tech.kakao.com/2021/09/29/mocking-fe/">kakao Tech Blog:Mocking으로 생산성까지 챙기는 FE 웹개발</a></p>
<h2 id="프론트엔드-개발-과정의-현실">프론트엔드 개발 과정의 현실</h2>
<p>전체 개발 프로세스 중 요구 사항 분석 및 기획, 백엔드 개발, 프론트엔드 개발에 이르는 각각의 개발 과정은 크게 겹치지 않고 진행되는 것이 가장 이상적일 것 입니다. 왜냐하면, 개발 과정에서 도출되는 요구 사항이나 사용 가능한 인터페이스에 변경이 있으면, 각 단계별 의존성에 따라 다시 작업해야 하는 경우가 발생하기 때문입니다.</p>
<p>따라서, 개발과정이 다음과 같이만 진행된다면 요구 사항과 제공된 스펙에 따라 순조롭게 프론트엔드 개발을 진행할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/7dce84b3-a524-442e-aa6f-c0498e03ba59/image.png" alt="이상적인 개발 과정"></p>
<p>하지만 현실은, 많은 시간을 프론트엔드와 백엔드를 병행하여 개발하게 되는 경우가 많습니다.</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/2a9bcd26-1082-45d7-a5f9-cb85ba77fce4/image.png" alt="현실 개발 과정"></p>
<p>이때 프론트엔드에서 백엔드의 API를 활용해야 하는 것처럼, 백엔드 개발에 종속적인 부분이 있다면, 해당 부분이 완성되기 전까지는 프론트엔드에서 개발을 진행할 수 없고, 그 부분이 진행된 후에나 개발이 가능합니다. 심지어 추가적인 수정사항이 발생했는데 백엔드 개발에 의존성이 있는 부분에 수정이 필요하다면, 이러한 비효율적인 과정을 반복 진행할 수밖에 없습니다.</p>
<p>만약, 이러한 부분을 해결해줄 수 있는 대안이 없다면 심할 경우 아래와 같은 상황이 펼쳐질 수 있다고 개인적으로 생각됩니다.</p>
<blockquote>
<p><code>기획자</code>: ~작업은 어떻게 진행 중이신가요?
<code>프론트엔드 개발자</code>: 그게... 아직 API가 준비되지 않아서 다음주 까지 기달려야 합니다.</p>
</blockquote>
<blockquote>
<p><code>프론트엔드 개발자</code>: 백엔드 때문에 프론트 개발 진행이 안되네요... 🤬 (업무와 밀접한 관련이 있고 일정 압박등으로 인해 스트레스가 심한 경우 욕설이 나올 것 같다고 예상됨)
<code>백엔드 개발자</code>: 그럼 당신이 백엔드 개발을 하시던가요! 🤬 (백엔드 개발자도 개발자만의 사정이 있기 때문에 협업이 어려워 질 것 이라 예상됨)</p>
</blockquote>
<p>제가 실제 협업에서 일해보지는 않았지만, 협업을 할 때 위와 관련된 상황에 대해 생각해보지 않았다면 협업하면서 일하는 것이 어려울 것 같다고 생각됩니다. 또한, 다툼이 있었다면 백엔드 부서와의 관계도 좋지 않게 될 것 같고요. mocking을 활용해서 개발을 진행하지 않으면 시간이 계속 지체될 것 같다고 생각됩니다.</p>
<h2 id="mockmocking">mock(mocking)</h2>
<p>kakao Tech Blog 글에 의하면 기존에도 mocking을 통해 사전 개발을 진행했었고 그 것에 대한 장단점은 아래와 같다고 합니다.</p>
<ol>
<li>화면에 필요한 데이터의 상태를 애플리케이션의 내부 로직에 직접 Mocking 해서 필요한 화면에 붙이는 방식</li>
</ol>
<ul>
<li>장점: 구현이 쉬워 빠르게 적용할 수 있다</li>
<li>단점: 애플리케이션의 서비스 로직에 직접 Mocking을 해야 해 애플리케이션 서비스 로직을 수정해야 하고, HTTP 메소드와 네트워크의 응답 상태에 따라 각각 대응하기가 쉽지 않다.</li>
</ul>
<ol start="2">
<li>Mock 서버를 별도로 만드는 방법 </li>
</ol>
<ul>
<li>장점: 웹 애플리케이션의 서비스 로직을 수정하지 않아도 된다. </li>
<li>단점: 데이터 상태 구현에는 직접 Mocking 대비 비용 및 공수가 상당히 들어가며, 이를 로컬이 아닌 누군가에게 공유해야 한다면, Mock 서버를 원격으로 제공하기 위한 추가 환경 구성 작업 등에도 적지 않은 공수가 들어 효율적이지 않다.</li>
</ul>
<ol start="3">
<li>기존 Mocking의 또 다른 문제점 </li>
</ol>
<ul>
<li>각 화면에 대한 테스팅 및 디버깅 시에 어려움이 발생한다.</li>
<li>결과물을 확인하고자 하는 인원이 담당 프론트엔드 개발자가 아니라면, 예를 들어 에러 화면을 보려고 해도 크롬 개발자 도구가 요청을 블록 처리할 가능성이 높아 특정 상태의 화면을 다른 사람들에게 공유하기가 어렵고, 실제 API 스펙처럼 발생하는 상황을 모의하기가 쉽지 않다.</li>
<li>대기, 로딩, 에러 등 API의 응답 상태에 따른 여러 가지 구현을 가지고 있을 경우, 각 단위 개발은 어떻게 진행한다고 하더라도 실제 화면을 구성하기 위한 구현 단계에서는 각 케이스별로 임의의 상태를 만들어 보면서 개발하거나 그 자체를 디버깅하기에 어려움이 있다.</li>
</ul>
<p>위를 종합하면, 협업하면서 원활히 개발을 진행하기 위해 필요한 Mocking의 수준은 실제 API를 사용하는 것처럼 네트워크 수준에서 Mocking하는 것입니다. 이와 같은 고민을 가지고 찾아보던 중 MSW(Mock Service Worker, 네트워크 요청 과정에서 Request에 대한 Mocking이 가능)를 알게 되었고 추가적으로 카카오 기술 블로그 글도 접하게 되었습니다.</p>
<h2 id="msw">MSW?</h2>
<p><a href="https://mswjs.io">https://mswjs.io</a></p>
<ul>
<li>MSW는 Mock Service Worker의 약자</li>
<li>서버의 네트워크 요청을 가로채서 모의 응답(Mocked Response)을 보내주는 역할을 하는 API Mocking 라이브러리</li>
<li>Mock 서버를 구축하지 않아도 API를 네트워크 수준에서 Mocking 할 수 있음</li>
<li>Service Worker를 통해 HTTP 요청을 가로채기 때문에 API Mocking이 가능함</li>
</ul>
<h3 id="service-worker">service worker</h3>
<p>Service Worker는 웹 애플리케이션의 메인 스레드와 분리된 별도의 백그라운드 스레드에서 실행시킬 수 있는 기술 중 하나이며,  Service Worker 덕분에 애플리케이션의 UI Block 없이 연산을 처리할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/921d7a7f-3ee0-4918-81e6-6efc6e8aeafe/image.png" alt="serviceWorker"></p>
<p>이러한 특징으로 인해, Service Worker는 다음과 같은 기능에 많이 사용되고 있습니다.</p>
<ul>
<li>네트워크가 원활할 때 동기화를 시켜주는 백그라운드 동기화 기능이나, 높은 비용의 계산을 처리할 때 또는 푸시 이벤트를 생성할 때 주로 사용됨 </li>
<li>MSW의 동작 방식과 관계되어 있는, 네트워크 요청을 가로채는 행위도 수행할 수 있음<ul>
<li>Service Worker가 애플리케이션과 서버 사이에서 Request를 가로채서 직접 Fetch에 대한 컨트롤도 할 수 있기 때문에 색다른 작업이 가능해짐</li>
<li>예를 들어, HTTP Request와 Response를 보고 캐싱 처리를 한다든지, 필요하다면 로깅을 한다든지 하는 여러 가지 새로운 동작을 만들어 낼 수 있음 </li>
<li>MSW도 이 과정을 통해서 Request를 가로채서 Response를 Mocking 하는 원리를 사용함</li>
</ul>
</li>
</ul>
<p>Service Worker의 사용이 제한되는 경우도 있습니다.</p>
<ul>
<li>Service Worker는 대부분의 모던 브라우저에서 지원하고 있으나, IE와 같은 일부 브라우저에서는 지원하고 있지 않음</li>
<li>기본적으로 localhost가 아닌 환경이라면 HTTPS 보안 프로토콜 환경이 필요함<ul>
<li>Service Worker는 중간에서 네트워크로 연결을 가로채고 조작할 수 있는 강력한 기능을 갖고 있기 때문에, Service Worker에서는 HTTPS가 기본적으로 제공되는 환경에서만 사용할 수 있음</li>
</ul>
</li>
</ul>
<p>즉, MSW는 Service Worker를 기반으로 모의 API를 만들어내기 때문에 다른 프론트엔드에서 사용하는 수많은 라이브러리나 프레임워크에 종속적이지 않고 호환성에 문제없이 동작합니다.</p>
<h3 id="동작-원리">동작 원리</h3>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/3a52e699-5cf2-4941-ba16-2e70747ddb23/image.png" alt="process_MSW"></p>
<ol>
<li>브라우저에 service worker 설치</li>
<li>설치 이후부터는 브라우저에서 실제 이루어지는 요청을 service worker가 가로챔</li>
<li>service worker에서는 해당하는 실제 요청을 복사해서 MSW에게 해당 요청과 일치하는 모의 응답을 제공받고, 이를 브라우저에 그대로 전달</li>
</ol>
<p>이러한 과정을 통해서, 실제 서버 존재 여부와 상관없이 실제 요청으로 이어지지 않고 예상할 수 있는 요청에 대해 Mocking이 가능해집니다. 이렇게 Mocking이 가능해지면 API가 아직 준비되지 않았어도 다음과 같은 개발 방식을 선택할 수 있습니다.</p>
<h3 id="msw를-활용한-개발-방식">MSW를 활용한 개발 방식</h3>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/5c950aeb-81ab-4ad1-a355-86c37323610e/image.png" alt="process_dev_with_MSW"></p>
<ol>
<li>기획자가 요구 사항을 전달</li>
<li>프론트엔드 개발자와 백엔드 개발자가 API 스펙을 합의하고, 백엔드 개발자는 프론트엔드 개발자에게 API의 스펙을 제공</li>
<li>프론트엔드 개발자는 MSW를 통해 네트워크 레벨에서의 Mocking을 진행한 후, 애플리케이션을 개발</li>
</ol>
<p>API 없이도 프론트엔드 개발자는 높은 완성도를 갖고 있는 수준에서 기획자와 미리 프론트엔드 애플리케이션을 확인하며 피드백을 주고받고, 그 사이 백엔드 개발자는 API 개발을 진행합니다. 이후 백엔드 개발자가 API를 제공하면, 프론트엔드 개발자는 별다른 작업 없이 MSW를 스위치 오프(NODE_ENV 설정)만 하면 Production으로 배포할 수 있는 형태의 개발 과정을 통해 개발을 진행할 수 있습니다.</p>
<p>MSW는 디버깅이 필요한 상황에서도 좋은 시너지를 만들어 낼 수 있습니다.</p>
<p>예를 들어, 특정 API 응답을 기준으로 에러가 발생해 디버깅이 필요한 상황이라면, 기존 서비스 로직을 전혀 건드리지 않고 오로지 MSW에서 Mocking을 만들어 내서 쉽게 디버깅할 수 있습니다.</p>
<p>더 나아가, 이러한 장점은 기획자 등의 다른 누군가에게 각 화면을 공유하고 피드백을 받아야 하는 상황이 발생했을 때에도, 추가적으로 MSW에서 해당 상황을 만들어낼 수 있도록 작업을 해 둔다면 별다른 서비스 로직의 수정 없이도 MSW를 통해 제공이 가능해집니다.</p>
<h2 id="프론트엔드-개발-시-msw-활용을-통해-얻을-수-있는-장점">프론트엔드 개발 시 MSW 활용을 통해 얻을 수 있는 장점</h2>
<ul>
<li>낮은 진입장벽으로 빠른 적용이 가능</li>
<li>별도의 환경 없이 네트워크 요청에 대한 모의 가능</li>
<li>API에 종속성 없이 완성도 높은 수준의 사전 개발 가능</li>
<li>네트워크 상태에 따른 디버깅을 위해 활용 가능</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL_옵저버 패턴]]></title>
            <link>https://velog.io/@devrun_ryan/TIL%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@devrun_ryan/TIL%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Wed, 25 Jan 2023 06:25:55 GMT</pubDate>
            <description><![CDATA[<p><a href="https://patterns-dev-kr.github.io/design-patterns/observer-pattern/">참고 사이트</a></p>
<p>멘토님이 추천해주신 디자인 패턴 사이트 중에서 평소 알고 싶었던 옵저버 패턴에 대해서 학습해보았습니다.</p>
<p><img src="https://mblogthumb-phinf.pstatic.net/MjAyMDExMDNfMjEx/MDAxNjA0MzMyODAxODQ2.91w3CzReSjuUZaQsbN0E5U6K1jCG7RZyWyzQTTBHmHwg.6vu6iimHlJwcxOUx9QnL7fH6rgSQDSxDz4hk4KWtmV4g.JPEG.rara4000/1604332800073.jpg?type=w800" alt="스타크래프트 옵저버"></p>
<p>갑자기 스타크래프트 옵저버가 생각나서... 이 옵저버는 아닙니다 (이 개그에 불쾌감을 느끼신 분들이 있으시다면 사과의 말씀 드립니다...)</p>
<h1 id="observer-패턴">Observer 패턴</h1>
<blockquote>
<p>Observable을 활용해 Subscriber에게 이벤트 발생을 알린다</p>
</blockquote>
<ul>
<li><code>Observer</code> 구독하는 주체</li>
<li><code>Observable</code> 구독 가능한 객체</li>
<li>이벤트가 발생할 때마다 <code>Observable</code>은 모든 <code>Observer</code>에게 이벤트를 전파한다</li>
</ul>
<h2 id="observable-객체">Observable 객체</h2>
<p>Observable 객체는 (보통) 3가지 주요 특징을 포함한다.</p>
<ul>
<li><code>observers</code> 이벤트가 발생할 때마다 전파할 Observer들의 배열</li>
<li><code>subscribe()</code> Observer를 Observer 배열에 추가한다</li>
<li><code>unsubscribe()</code> Observer배열에서 Observer를 제거한다</li>
<li><code>notify()</code> 등록된 모든 Observer들에게 이벤트를 전파한다</li>
</ul>
<pre><code class="language-javascript">class Observable {
  constructor() {
    this.observers = []
  }

  subscribe(func) {
    this.observers.push(func)
  }

  unsubscribe(func) {
    this.observers = this.observers.filter(observer =&gt; observer !== func)
  }

  notify(data) {
    this.observers.forEach(observer =&gt; observer(data))
  }
}</code></pre>
<p>subscribe 메서드를 통해 Observer를 등록하고, unsubscribe를 통해 등록을 해지할 수 있다.
notify 메서드를 통해 모든 Observer에게 이벤트를 전파할 수 있다.</p>
<h2 id="observable-객체를-활용해보기">Observable 객체를 활용해보기</h2>
<ul>
<li>리액트</li>
</ul>
<p>사용자가 <code>Button</code>을 클릭하거나 <code>Switch</code> 를 토글할 때 타임스탬프를 로깅하고자 하며, 이벤트 발생시마다 토스트 알림을 화면에 노출하려고 한다.</p>
<p><video width="500" src=https://patterns-dev-kr.github.io/design-observer01.mp4></video></p>
<p>구현해야 되는 로직은</p>
<ol>
<li>사용자가 <code>handleClick</code> 또는 <code>handleToggle</code> 함수를 호출할 때 마다 핸들러는 Observable의 <code>notify</code>를 호출</li>
<li><code>notify</code> 메서드는 등록된 모든 Observer들에게 <code>handleClick</code>혹은 <code>handleToggle</code>에서 전달된 데이터를 포함한 이벤트를 전파</li>
</ol>
<p>와 같다.</p>
<pre><code class="language-javascript">import { ToastContainer, toast } from &#39;react-toastify&#39;

function logger(data) {
  console.log(`${Date.now()} ${data}`)
}

function toastify(data) {
  toast(data)
}

observable.subscribe(logger)
observable.subscribe(toastify)

export default function App() {
  function handleClick() {
    observable.notify(&#39;User clicked button!&#39;)
  }

  function handleToggle() {
    observable.notify(&#39;User toggled switch!&#39;)
  }

  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;Button&gt;Click me!&lt;/Button&gt;
      &lt;FormControlLabel control={&lt;Switch /&gt;} /&gt;
      &lt;ToastContainer /&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>Observable의 subscribe 메서드를 사용해서 <code>logger</code>, <code>toastify</code> 함수를 연결해서 Observer로서 작동할 수 있도록 해준다. 이벤트가 발생할 때 마다 <code>logger</code>, <code>toastify</code> 함수는 알림을 받게 되며, <code>handleClick</code>과 <code>handleToggle</code> 이벤트 핸들러에서 <code>notify</code>를 호출하는 것을 통해 이벤트를 전파한다. 이 때 Observer에서 필요한 데이터를 인자로 전달한다.</p>
<p>전체 플로우를 살펴보면 아래와 같다.</p>
<p><code>handleClick</code>과 <code>handleToggle</code>이 Observable의 <code>notify</code>를 호출 =&gt; 이를 구독하고 있던 Observer (Observers배열) <code>logger</code>와 <code>toastify</code>함수는 이 이벤트를 받아서 특정 동작을 수행한다. 앱 내에서 인터렉션(상호작용)이 발생하는 동안 <code>logger</code>와 <code>toastify</code>는 <code>notify</code>의 호출로부터 이벤트를 계속 받을 수 있다.</p>
<h2 id="활용할-수-있는-사례">활용할 수 있는 사례</h2>
<p>Observer 패턴은 다양하게 활용할 수 있지만 비동기 호출이나 이벤트 기반 데이터를 처리할 때 매우 유용하다. 만약 어떤 컴포넌트가 특정 데이터의 다운로드 완료 알림을 받기 원하거나, 사용자가 메시지 보드에 새로운 메시지를 게시했을 때 모든 멤버가 알림을 받거나 하는 등의 상황을 말한다.</p>
<h3 id="rxjs">RxJS</h3>
<p>RxJS는 Observer 패턴을 구현한 유명 오픈소스 라이브러리이다</p>
<blockquote>
<p>ReactiveX 는 Observer 패턴, 이터레이터 패턴, 함수형 프로그래밍을 조합하여 이벤트의 순서를 이상적으로 관리할 수 있다.</p>
</blockquote>
<p>RxJS를 사용하면 Observable과 Observer(Subscriber)를 만들어낼 수 있다. 아래 예제는 공식 문서에 소개된 것으로 사용자가 문서를 드래그 중인지 아닌지 콘솔에 출력해준다.</p>
<p>RxJS는 이것 말고도 Observer 패턴에 대한 매우 많은 빌트인 기능들을 제공한다.</p>
<h1 id="observer-패턴의-장점과-단점">Observer 패턴의 장점과 단점</h1>
<h2 id="장점">장점</h2>
<p>Observer 패턴을 사용하는 것도 관심사의 분리와 단일 책임의 원칙을 강제하기 위한 좋은 방법이다. Observer 객체는 Observable 객체와 강결합되어있지 않고 언제든지 분리될 수 있다. Observable 객체는 이벤트 모니터링의 역할을 갖고. Observer는 받은 데이터를 처리하는 역할을 갖게 된다.</p>
<h2 id="단점">단점</h2>
<p>Observer가 복잡해지면 모든 Observer들에 알림을 전파하는 데 성능 이슈가 발생할 수 있다.</p>
<h1 id="참조">참조</h1>
<ul>
<li><a href="https://rxjs-dev.firebaseapp.com/">RxJS</a></li>
<li><a href="https://www.sitepoint.com/javascript-design-patterns-observer-pattern/">JavaScript Design Patterns: The Observer Pattern</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[:empty 가상 선택자 활용해보기]]></title>
            <link>https://velog.io/@devrun_ryan/empty-%EA%B0%80%EC%83%81-%EC%84%A0%ED%83%9D%EC%9E%90-%ED%99%9C%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@devrun_ryan/empty-%EA%B0%80%EC%83%81-%EC%84%A0%ED%83%9D%EC%9E%90-%ED%99%9C%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Wed, 18 Jan 2023 09:03:11 GMT</pubDate>
            <description><![CDATA[<h2 id="empty를-알게된-계기">:empty를 알게된 계기</h2>
<p>개인적으로 과제등을 할 때 로딩처리에 대한 구현을 css로 한 적이 있는데, 처음에는 아래처럼 개별적으로 loading에 대한 클래스를 통해 구현했었습니다.</p>
<pre><code class="language-scss">.movie-title {
  font-size: 3rem;
  &amp;.loading {
    height: 3rem;
    background-color: vars.$background-color--pulse;
    animation: pulse 1s infinite alternate;
  }
}</code></pre>
<p>데이터를 가져오는 동안에는 loading클래스를 삽입하고, 데이터를 가져오는 것이 완료되면 loading클래스를 제거하는 방식으로 구현은 가능했습니다.
좀 더 편하게 적용할 수 있는 방법이 있는지 생각하던 중 저는 아래와 같은 고민을 하게 되었습니다.</p>
<blockquote>
<p>마우스를 올렸을 때 :hover 가상 클래스 선택자를 활용할 수 있는 것처럼, 빈 요소에 대해서 활용할 수 있는 css 선택자가 있을까?</p>
</blockquote>
<p>이후 mdn에서 css 가상 선택자에 대해서 살펴보던 중 <code>:empty</code> 를 접하게 되었고, 실제로 활용할 수 있는지 테스트하게 되었습니다</p>
<h2 id="empty">:empty</h2>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes">https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes</a></p>
<p>mdn 에서는 <code>:empty</code> 를 아래와 같이 설명하고 있습니다.</p>
<blockquote>
<p>The <code>:empty</code> CSS pseudo-class represents any element that has no children. Children can be either element nodes or text (including whitespace). Comments, processing instructions, and CSS content do not affect whether an element is considered empty.</p>
</blockquote>
<p>짧은 영어 실력으로 요약해보면, &#39;자식이 없는 요소(element that has no children)를 의미한다&#39;라고 할 수 있을 것 같습니다.</p>
<pre><code class="language-html">&lt;div id=&quot;app&quot;&gt;&lt;/div&gt;</code></pre>
<p>즉, 위와 같이 요소는 있지만, 하위 자식들이 없어 빈 요소로 표현될 수 있는 경우를 의미한다고 생각합니다.</p>
<h2 id="적용해보기">적용해보기</h2>
<pre><code class="language-scss">.movie-title {
  font-size: 3rem;
  &amp;.loading {
    height: 3rem;
    background-color: vars.$background-color--pulse;
    animation: pulse 1s infinite alternate;
  }
}</code></pre>
<p>위에서 예로 들었던 영화 상세보기(모달 창)창에서 타이틀에 대한 scss를 수정해보면</p>
<pre><code class="language-scss">.movie-title {
  font-size: 3rem;
  &amp;:empty {
    height: 3rem;
    background-color: vars.$background-color--pulse;
    animation: pulse 1s infinite alternate;
  }
}</code></pre>
<p>위와 같이 수정해줄 수 있습니다.</p>
<p><code>:empty</code>를 이용할 때 동작하는 방식을 제가 이해한 방식으로 과정을 요약해보면 아래와 같습니다.</p>
<blockquote>
<p><code>&lt;h3 class=&quot;movie-title&quot;&gt;&lt;/h3&gt;</code> -&gt; 빈 요소 -&gt; <code>.movie-title:empty{...}</code> 스타일 적용 -&gt; 데이터 로드, 렌더링 -&gt; <code>&lt;h3 class=&quot;movie-title&quot;&gt;avengers&lt;/h3&gt;</code> -&gt; 빈 요소가 아님 -&gt; <code>.movie-title:empty{...}</code> 스타일 적용 해제, <code>.movie-title</code> 스타일 적용</p>
</blockquote>
<p>omdbapi를 이용한 영화 검색 사이트(과제)의 배포된 도메인에서 <code>:empty</code>를 활용한 로딩처리 결과 화면은 아래와 같습니다.</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/b2fd1119-f84f-45dc-9b88-208c0873c311/image.gif" alt="modal_loading"></p>
<h2 id="적용해보면서-느낀-장점">적용해보면서 느낀 장점</h2>
<h3 id="자동화">자동화</h3>
<p>제가 적용해보면서 느꼈던 장점은 <code>javascript로 내가 css 클래스를 제어해주지 않아도, css 표준 기술을 활용해서 자동으로 빈요소에 대한 스타일을 제어할 수 있다</code> 입니다. 요소는 있는데 안에 자식이 없어서 비어있는 경우의 스타일링을 <code>:empty</code> 에게 위임할 수 있다는 의미입니다. <code>:empty</code>를 활용하면서 로딩처리 스타일링을 할 때 좀 더 로딩처리 자체 스타일링에만 초점을 맞춰서 진행할 수 있어서 만족하면서 사용을 했습니다. 과제 이후에도 저는 개인적으로 로딩처리에 대한 css를 작성할 때 활용하고 있습니다.</p>
<h3 id="호환성">호환성</h3>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/cb87f570-59ce-4e0c-90ad-f7a1fa7551f1/image.png" alt="mdn호환성"></p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/dfad1b58-96b5-4c2b-9921-dd9bc0e800d3/image.png" alt="caniuse호환성"></p>
<p>기능이 좋아도 호환성이 좋지 않다면, 지금 바로 사용하는 것은 힘들 수 있습니다. 그러나, mdn과 caniuse에서 제공되는 호환성을 보면 거의 대부분의 브라우저에서는 사용이 가능하다고 볼 수 있습니다. 대부분의 사용자 브라우저에서 적용이 될 것이라 예상되기 때문에, 맘 놓고 적용할 수 있을 것 같다는 것이 저의 생각입니다.</p>
<h2 id="끝맺으며">끝맺으며</h2>
<p>오늘은 <code>:empty</code> 를 개인적으로 학습하면서 적용해본 경험을 기반으로 포스트를 작성해보았습니다. <code>더 보기 영역(fetch-more)</code>와 같이 요소 안에 스피너 아이콘에 대한 자식 요소를 보여주고 숨겨줘서 로딩 구현을 할 경우에는 <code>:empty</code> 를 활용해서 로딩 구현을 하기는 어렵다고 생각합니다. 다만, 이 외의 경우에는 좀 더 편하게 로딩에 대한 구현을 할 수 있어서 저는 만족하면서 필요한 경우 계속 활용하고 있습니다.</p>
<p>틀린 내용이 있거나, 이와 관련된 의견이 있으시다면 언제든지 댓글로 알려주시길 바랍니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[env파일이 만능일까? (feat. 프론트엔드)]]></title>
            <link>https://velog.io/@devrun_ryan/about-env</link>
            <guid>https://velog.io/@devrun_ryan/about-env</guid>
            <pubDate>Wed, 11 Jan 2023 08:45:21 GMT</pubDate>
            <description><![CDATA[<h2 id="생각을-해보게-된-이유">생각을 해보게 된 이유</h2>
<p>API를 활용한 과제에서 쿼리스트링 파리미터로 <code>apikey</code>를 전달해줘야 되는데 env파일만 활용하면 중요한 값이 노출되는 상황을 방지할 수 있는지 확인해보고 싶었고, 이와 관련한 실험을 하게 되었습니다.</p>
<h2 id="env-파일을-이용하면-노출이-안되지-않을까">.env 파일을 이용하면 노출이 안되지 않을까?</h2>
<p>실험을 해보기 전까지는 일반적으로 <code>.env</code>파일을 통해 중요 값을 노출시키지 않는다고 이해하고 있었기 때문에 고민을 하지 않고 <code>.env</code>파일만 작성해서 활용했었지만, 실험 후에는 생각이 바뀌게 되었습니다.</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/e2d4c4c3-d268-4f93-8a84-7a326df86692/image.png" alt="env_capture"></p>
<p>.env 파일로 노출되면 안되는 값에 대해 저장하였으며,</p>
<pre><code class="language-javascript">const fetchMovieData = async () =&gt; {
  const moviesResponse = await fetch(
    `https://www.omdbapi.com/?apikey=${process.env.apikey}&amp;s=avengers`
  ).then((res) =&gt; res.json());
  console.log(moviesResponse);
};

fetchMovieData();</code></pre>
<p>js파일에서는 omdbapi를 호출하는 간단한 코드를 작성해보았습니다.</p>
<p>env파일을 통해 중요 값이 노출되지 않는다고 생각한다면 &quot;노출되어도 <code>process.env.apikey</code>로 노출이 되지 않을까?&quot; 라는 예상을 할 수 있을 것 같습니다.</p>
<p>그러나, 실제로 개발서버를 통해 확인해보면...</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/2fce128d-4be1-420e-a497-13e328016a67/image.gif" alt="env_result"></p>
<p>apikey=... 으로 해당 값이 그대로 노출되는 것을 확인할 수 있습니다.</p>
<p>이를 통해서 저는 IDE 상의 코드 작성한 곳에서는 <code>process.env.apikey</code>로 보이기 때문에 노출되는 것을 피할 수 있지만, 실제 필요한 상황에서는   해당 데이터(값)를 이용하기 때문에 발생할 수 있는 현상이라고 생각하게 되었습니다.</p>
<p>물론, 쿼리 파라미터외에도 request header에 어떤 값이 있다면 네트워크 탭을 통해 확인이 가능해서 이와 관련한 고민도 필요하다고 생각합니다.
(그래서 API 플랫폼에서 허용하도록 설정한 주소외에 모든 요청은 응답하지 않는 것과 CORS, JWT과 같은 방법이 등장했다고 생각합니다.)</p>
<h2 id="최대한-중요값을-보호할-수-있는-방법이-있을까">최대한 중요값을 보호할 수 있는 방법이 있을까?</h2>
<p>그럼에도 불구하고 <code>.env</code>은 여전히 중요 값을 보호하기 위해서는 활용해야 되는 파일이라고 생각합니다. 다만, 추가적인 방법을 통해 이를 보호할 수 있는 방법이 무엇이 있을까요?</p>
<p>제 나름대로 생각한 방법은 서버를 이용하는 것입니다.</p>
<ul>
<li>서버를 통해 통신하면 적어도 프론트엔드 측에서 먼저 확인되는 주소는 서버와의 요청을 위해 필요한 주소이다.</li>
<li>서버는 내부 비즈니스 로직에서 중요값을 활용해 외부 API와 통신하거나 다른 방법으로 응답할 데이터를 생성하고 전송해준다.</li>
<li>서버가 노출된다면 중요값을 보호할 수는 없겠으나, 적어도 프론트엔드 측에서 주소로 드러나는 일은 막을 수 있다</li>
</ul>
<p>vercel serverless function을 이용해서 제가 구현한 서버에서 사용하는 movies 데이터 요청 rest api 주소 형식은 <code>/api/v1/movies?searchKeyword=avengers&amp;year=2022&amp;page=1</code> 과 같으며, 서버는 해당 쿼리 파라미터를 실제 사용할 파라미터로 변환해서 omdbAPI서버로 요청, 그 결과를 응답하는 구조입니다.</p>
<p>실제로 작성해서 개발서버로 확인한 결과는 아래와 같습니다.</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/134e1d25-df78-48bd-95ca-9f322369e82f/image.gif" alt="server_response"></p>
<p>과정은 아래와 같습니다.</p>
<ol>
<li>(화면에서는 잘려서 보이지 않지만) avengers 라는 키워드를 입력하고 검색을 진행</li>
<li>server에서는 자체 로직에서 <code>process.env.apikey</code>라는 환경변수 값을 이용해서 omdbAPI와 통신을 하고 <code>{ status, payload, message }</code> 형식으로 응답을 내려줌</li>
</ol>
<p>즉, 서버로 요청하는 restAPI 주소에서 민감한 정보는 가급적 받지 않도록 설계하였고, 이를 통해 얻게된 결과입니다.</p>
<p>이를 통해 저는</p>
<blockquote>
<p>env파일을 통해 IDE상에서의 노출은 피할수 있지만, 실제 값을 이용하는 상황에서 노출될 수 있다. 이를 보완하기 위한 방법은 여러가지가 있지만 서버를 활용하는 방법도 있을 수 있다.</p>
</blockquote>
<p>라는 결론을 내리게 되었습니다.</p>
<h2 id="마무리-지으며">마무리 지으며</h2>
<p><code>.env</code>파일을 보완하기 위한 다양한 방법들이 있는 것 같지만, 그 중에서 필요한 경우 직접 서버를 개발해서 사용해볼 수 있는 방법도 있을 것 같아 시도해보았고 과정을 공유하고 싶은 마음에서 글을 작성하게 되었습니다.</p>
<p>제가 생각한 방법이 틀릴 수도 있기 때문에, 이와 관련한 의견이 있으실경우 의견 남겨주시면 감사할 것 같습니다.</p>
<p>vercel serverless function을 활용해서 개발할 때는 이상이 없었으나, (깃헙 연동등의 방법을 통해) 배포하면 에러가 발생하는 상황을 해결하는 과정도 있었지만 그것은 이후 기회가 된다면 글을 작성해보도록 하겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[레이아웃 클론코딩 도전기_[main]-2]]></title>
            <link>https://velog.io/@devrun_ryan/%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-%EB%8F%84%EC%A0%84%EA%B8%B0main-2</link>
            <guid>https://velog.io/@devrun_ryan/%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-%EB%8F%84%EC%A0%84%EA%B8%B0main-2</guid>
            <pubDate>Mon, 02 Jan 2023 08:40:17 GMT</pubDate>
            <description><![CDATA[<h1 id="시작하기에-앞서">시작하기에 앞서</h1>
<p>원래는 구현하는 것과 동기화해서 글을 작성하는 것을 목표로 하였지만, 시간을 지체할 수 없었습니다. 지금 글을 작성하는 시점에서는 클론코딩 과제를 제출하였고 데모사이트 배포가 된 상황입니다. 실제 개발하면서 경험했던 상황을 재현하기 위해 개발자도구를 통해 제어를 해서 재현을 했는데요... 이 부분에서 양해를 부탁드립니다 ㅜㅜ</p>
<ul>
<li>데모 사이트
<a href="https://zesty-custard-249c6c.netlify.app/">https://zesty-custard-249c6c.netlify.app/</a></li>
<li>검색 버튼 클릭시 검색 컴포넌트(요소) 활성화, 영어와 한글 변환 기능, 모바일 반응형 디자인을 제외한 나머지를 구현했습니다. 공유기능은 실제 API를 사용해야 되서 이번 과제에서는 구현하지 못했습니다.</li>
</ul>
<h1 id="main-title-영역">main title 영역</h1>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/b244a146-6035-4895-b16c-8fc0457d949a/image.gif" alt="main_title"></p>
<p>제가 생각했을 때 main 영역내부에서 실제 카드요소로 이루어진 콘텐츠에 진입하기 직전에 보여지는 영역이기 때문에 <code>main title영역</code>이라고 구분지어봤습니다.
처음에 달력의 애니메이션을 보고 &quot;직접 애니메이션 처리를 해줘야 되나?&quot; 하고 난감했었는데요...</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/c29e5be9-c647-49ea-8a96-1a4095263a49/image.png" alt="day_gif"></p>
<p>다행히도, 해당 달력 요소를 만들기 위해 네트워크 통신으로 gif파일을 받아서 처리하는 것을 확인할 수 있었고, 이를 통해 비교적 손쉽게 구현을 할 수 있었습니다.
주소 양식에서 유추할 수 있듯이 <code>~/ico_date{해당 일자}.gif</code>로 요청하면 해당 일자에 대한 gif파일을 응답으로 가져올 수 있습니다.
(e.g. <code>~/ico_date30.gif</code>로 요청하면 30일에 해당하는 gif 파일을 응답으로 받을 수 있습니다)</p>
<pre><code class="language-html">&lt;img src=&quot;https://www.kakaocorp.com/page/calendar/light/ico_date2.gif&quot; alt=&quot;day&quot; /&gt;</code></pre>
<p>위와 같이 html에서는 img태그를 통해 구현가능 합니다.</p>
<h1 id="card-분석">card 분석</h1>
<h2 id="sticky">sticky</h2>
<p>css의 <code>position:sticky</code> 를 활용하여 좀 더 인터렉티브한 콘텐츠를 만들 수 있습니다. 스크롤 되는 요소가 특정위치에 도달하면 sticky가 적용된 요소는 스크롤이 끝나기 전까지 끈끈하게 달라붙어서 고정되어있습니다.</p>
<p>이게 어떤 차이인지는 실제 눈으로 보면 체감이 되실 것 같기 때문에, 개발자 도구에서 sticky 속성을 제거한 것(미적용)과 제거하지 않은 것(적용)한 것에 대한 차이를 보여드리겠습니다. </p>
<h3 id="sticky가-사용되지-않은-경우">sticky가 사용되지 않은 경우</h3>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/b385fff0-a484-4256-8451-b1a6081d0cd8/image.gif" alt="no_sticky"></p>
<p><code>position:sticky</code>가 적용되지 않은 상태에서 스크롤을 내리면 아무 효과 없이 일반적인 스크롤처럼 동작합니다. 그런데, 무언가 밋밋한 느낌이 들지 않나요? 사용자를 사로잡을 인터렉티브한 요소가 없어 보입니다.</p>
<h3 id="sticky가-사용된-경우">sticky가 사용된 경우</h3>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/14ecd79f-9981-44cf-a338-b7eeef6b65a5/image.gif" alt="sticky"></p>
<p>이번에는 <code>position:sticky</code>를 적용한 화면입니다. sticky가 없던 화면과 다르게 스크롤 될 때 일정위치가 되면 메인뉴스에 해당하는 요소는 그 위치에서 움직이지 않고 고정되어있으며, 메인 카드 서브 요소들의 스크롤이 끝나면 고정되지 않고 정상적인 스크롤 방식으로 동작합니다.</p>
<p>이와 같이 인터렉티브한 요소가 있다면 전과는 다르게 스크롤할 때 콘텐츠에 집중해서 보고 싶은 욕구가 들지 않으시나요? (저만 그럴 수도 있습니다...)</p>
<h3 id="호환성">호환성</h3>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/d54b91a3-0855-4bf1-96b7-122135385a25/image.png" alt="canIUse_sticky"></p>
<p><img src="https://c.ndtvimg.com/2022-06/p3t0a78_internet-explorers-gravestone-240_295x200_18_June_22.jpg" alt="IE_RIP">
역시 <code>IE</code>는 지원하지 않네요 (<code>Internet Explorer</code>를 삭제하여 그동안 수고한 <code>Internet Explorer</code>에게 조의를 표합시다...)</p>
<p>그러나, 대부분의 모던 브라우저에서는 지원해주기 때문에 호환성이 괜찮다고 할 수 있습니다. 예전에 js로 스크롤 위치를 계산해주고 <code>position:fixed</code> 를 활용하여 복잡하게 구현했던 것을 조금 더 간편하게 구현할 수 있도록 도와주는 <code>css 표준 속성</code>이라고 할 수 있습니다. 지원하지 않는 브라우저에도 지원해야 될 경우에는 번거롭지만, 직접 js로 구현이 필요해보이는군요...</p>
<h3 id="sticky를-사용하기-위한-구조">sticky를 사용하기 위한 구조</h3>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/8fe0fb1e-c35b-4f8d-8d34-76fa56c04c8b/image.png" alt="전체 구조"></p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/5e9d7163-a0e9-4138-9310-85925dad35f1/image.png" alt="세부 구조(확대)"></p>
<p>제 생각으로 카카오에서는 자동으로 적용될 수 있도록 아래와 같은 방법을 사용했을 것이라고 추측됩니다.</p>
<ul>
<li>서버사이드 프레임워크인 Nuxt를 사용하고, 요청URL의 뉴스데이터에 카드 타입이 명시되있다.</li>
<li>인라인 스타일로 css가 작성되어 있다.
=&gt; </li>
</ul>
<ol>
<li>타입에 따른 카드요소들의 높이는 정해져 있기 때문에, 통신을 통해 가져온 뉴스 데이터를 활용해서 높이를 계산</li>
<li>js를 이용하여 계산된 수치값을 <code>wrapper요소.style</code>을 통해 인라인 스타일로 작성</li>
</ol>
<p>다만, 저는 레이아웃을 직접 클론코딩해야 되기 때문에 각각 개별적으로 수치를 확인하고 적용했습니다.</p>
<h2 id="height-calc100vh---px">height: calc(100vh - px)</h2>
<p>sticky를 처음에 적용해서 확인했을 때 카카오 사이트와 달라서 어떻게 구현해야되는지 고민을 하게 되었습니다. 이미 사이트에 정답이 있고 그 것을 통해 배울수 있다는 클론코딩의 장점을 활용해서 구현을 하긴 했는데요... 제가 미숙해서 시간이 소요되었습니다. 혹시라도 저처럼 동일한 문제에 처해계신 분들을 위해 공유하고자 합니다. 일단 어떤 차이점이 있는지 아래를 통해 확인해봅시다.</p>
<h3 id="카카오-메인-랜딩페이지-sticky">카카오 메인 랜딩페이지 sticky</h3>
<ul>
<li>일반 </li>
</ul>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/c29db8e2-cb02-46fd-95c0-fe94b249ed7b/image.gif" alt="kakao_landing_sticky"></p>
<ul>
<li>뷰포트 높이가 줄어듬</li>
</ul>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/cff0c341-72f9-4fee-949b-e3744d35cf78/image.gif" alt="kakao_landing_sticky_viewport_height"></p>
<p>네... 역시 카카오답게 매우 자연스러운 sticky 동작을 확인할 수 있습니다. (클론코딩할 때 레이아웃보다 이런 세세한 애니메이션을 구현해야 되는 것에 시간이 더 소요되었다는 사실이...?! ㅠㅠ)</p>
<h3 id="calc100vh---px-미적용">calc(100vh - px) 미적용</h3>
<ul>
<li>일반</li>
</ul>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/dae5d118-0531-4142-bd64-407afde8babc/image.gif" alt="no_calc_height_sticky"></p>
<ul>
<li>뷰포트 높이가 줄어듬</li>
</ul>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/910c5cae-1f21-4adb-9cf6-dde2a6b34fa6/image.gif" alt="no_calc_height_sticky_viewport_height"></p>
<p>이 화면을 처음봤을 때 당황했습니다. sticky 기본적인 내용만 알고 있는 상태이여서 해결방안이 생각이 나지 않아 그랬던 것 같습니다. 처음에는 배우는 입장이고, 시간이 지체될 것 같아서 그냥 넘어갈까도 싶었지만... 꺾이지 않는 마음을 가지고 개발자 도구 형님의 도움을 받아보기로 합니다.</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/a747fbc7-48a9-49de-9a13-0c8fbeaf7de2/image.png" alt="devtool"></p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/dfa19a80-ac52-4e27-820e-05271ce227d8/image.png" alt="vh"></p>
<p><code>vh</code>는 <code>viewport height</code> 뷰포트 높이에 대한 단위이며 100vh의 의미는 실제 보여지는 브라우저 영역 높이의 전체입니다. 이것이 유동적으로 높이가 줄어들어 일정한 비율로 보여지게 되는 원리입니다. 카드 컴포넌트의 높이를 100vh에서 하단 여백으로 설정하고 싶은 높이를 빼주면 됩니다. 여기에서, <code>max-height</code>를 통해 최대 높이를 설정해주지 않을 경우 레이아웃을 해칠수 있기 때문에 700px로 설정해서 하단 콘텐츠들의 레이아웃에 영향이 없도록 한 것을 확인할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/25b1af4c-6f7c-450a-8d26-03b577636e8b/image.png" alt="devtool_img"></p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/15a6a135-ef87-450f-bbaa-50a5cc29a575/image.png" alt="devtool_img"></p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/484ca067-161c-4293-90bc-3855bc5d8542/image.png" alt="img"></p>
<p><code>inline 특성</code>과 <code>vertical-align: baseline</code>으로 인한 <code>img</code> 하단의 공백을 제거하기 위해 <code>display: block</code>을 설정한 것으로 보입니다. (<code>vertical-align: top</code>을 이용해서도 제거할 수 있습니다. 그래서 <code>inline-block</code>으로 설정한 요소의 css에 <code>vertical-align:top</code>속성이 적용되는 것도 같은 이유라고 할 수 있습니다.)</p>
<p>이미지는 넘칠 경우 잘려져서 보이게 되며, 카드 컴포넌트의 높이는 보여지는 뷰포트의 높이에 따라 줄어들고 늘어납니다. 결과적으로 스크롤하였을 때 유동적으로 일정비율을 유지하며 고정되있는 효과와 스크롤 했을 때 이미지가 같이 스크롤 되는 것 같은 효과를 얻을 수 있습니다.</p>
<h2 id="relative-wrapper">relative wrapper</h2>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/5e9d7163-a0e9-4138-9310-85925dad35f1/image.png" alt="세부 구조(확대)"></p>
<p>section_home의 두번째 자식요소의 wrap_item이 메인 카드 옆에 있는 서브카드들의 wrapper입니다. 여기에서 height를 충분히 주는 것을 통해 스크롤이 되서 sticky가 화면상에서 정상적으로 적용되도록 할 수 있습니다. height 값을 늘이고 줄여보면 어떤 의미인지 체감할 수 있습니다.</p>
<h2 id="flex-direction-row-reverse">flex-direction: row-reverse</h2>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/03d3b839-43dc-49a5-9f1a-0120a2d00577/image.png" alt="home2"></p>
<p>처음 카드 섹션이 끝나고 밑으로 더 스크롤이 되면 동일한 구조의 카드 컴포넌트 구조가 확인됩니다. 다만, 여기에서는 서로 방향이 반대로 되어있네요. 반대로 구현하기 위해서 다시 구조를 만들고 스타일을 생성해야 될까요...?</p>
<p>다행히도 <code>card container</code>는 <code>flex container</code>요소이며 현재 flex의 direction은 기본 값인 <code>row</code>입니다. 즉, 가로방향을 따라 정렬되어있습니다. 동일한 html 구조와 class 스타일을 적용한 뒤, <code>flex container</code>인 <code>card container</code>요소에 <code>flex-direction: row-reverse</code> 를 적용하면 순서가 거꾸로 되서 가로로 정렬되기 때문에 비교적 손쉽게 동일한 레이아웃을 만들 수 있습니다.</p>
<h1 id="이미지-스프라이트">이미지 스프라이트</h1>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/f447ac57-4c88-493f-bdcb-d6b3f1ea476a/image.png" alt="culture"></p>
<p>culture 영역의 아이콘에 사용되는 이미지들은 <code>이미지 스프라이트</code>라는 기법을 통해 적용이 됩니다. 그럼 카카오만 이 기술을 적용하고 있을까요? </p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/45e207fc-681f-49ff-a37e-335549de32cd/image.png" alt="naver">
네이버도 동일한 기술을 사용하고 있습니다.</p>
<p>그럼 이 기술을 왜 사용하는 걸까요?
동일한 100MB의 용량이 있고 1MB로 100개를 받는 것과 100MB로 1개를 받는다고 가정해봅시다. 여기에서는 서버에서의 비용이라는 개념을 생각해야 되는데, 서버 입장에서는 여러개로 분할해서 받는 것 보다 최대한 1번에 받는 것이 비용이 저렴합니다. 이런 이유로, 여러 이미지들이 모여져 있는 이미지 1개를 받고, background-position 속성으로 위치를 조정하여 해당하는 아이콘의 이미지를 삽입하는 구조입니다.</p>
<p>이미지 스프라이트에 대해 궁금하신 분들은 아래의 사이트를 참고하셔도 좋을 것 같습니다.</p>
<p><a href="https://velog.io/@untiring_dev/HTMLCSS-Day31.-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%8A%A4%ED%94%84%EB%9D%BC%EC%9D%B4%ED%8A%B8-%EA%B8%B0%EB%B2%95">https://velog.io/@untiring_dev/HTMLCSS-Day31.-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%8A%A4%ED%94%84%EB%9D%BC%EC%9D%B4%ED%8A%B8-%EA%B8%B0%EB%B2%95</a></p>
<p>처음에는 레이아웃 클론코딩으로 시작하였지만 하게 되면서 애니메이션 구현에 대한 배움도 있는 것 같고 다양한 기법들에 대해 학습할 수 있어서 유익한 과정이 되는 것 같습니다. 이외의 내용들은 다음 포스트에서 다룰 수 있도록 하겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[레이아웃 클론코딩 도전기_[main]-1]]></title>
            <link>https://velog.io/@devrun_ryan/%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-%EB%8F%84%EC%A0%84%EA%B8%B0-main-1</link>
            <guid>https://velog.io/@devrun_ryan/%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-%EB%8F%84%EC%A0%84%EA%B8%B0-main-1</guid>
            <pubDate>Tue, 27 Dec 2022 07:03:54 GMT</pubDate>
            <description><![CDATA[<h1 id="시작하기에-앞서">시작하기에 앞서</h1>
<p>클론코딩하면서 관련 내용을 작성하는 것이 의외로 시간을 많이 할애해야 되는 작업이였군요... 조금 힘들지만 미래의 저를 위해 기록으로 열심히 남겨봅니다...</p>
<h2 id="카카오-홈페이지와-동기화를-할-것인가">카카오 홈페이지와 동기화를 할 것인가?</h2>
<p>동기화? 그 의미는 카카오 홈페이지의 최신 내용과 제 클론코딩 사이트에서의 내용을 동일하게 일치시킬지에 대한 문제입니다.</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/9c90ed7d-19c8-4c7a-92dd-9f090bc8a947/image.png" alt="dev_network">
<code>개발자 도구</code> - <code>네트워크</code>탭에서 확인되는 요청URL을 통해 데이터를 가져오고, js에서 <code>dom</code>을 조작하여 데이터에 기반한 내용으로 작성할 수 있습니다. 뉴스와 관련된 데이터도 있지만, 살펴보면 <code>category</code>등 페이지 구성에 필요한 모든 데이터를 네트워크로 요청해서 받아와서 처리하는 것을 볼 수 있습니다(보통 이 작업을 처리하는 방법에 따라 <code>비동기 통신</code> 또는 <code>동기 통신</code> 이라고 부르는 것 같고, 네트워크 통신을 통해 데이터를 관리하는 것도 프론트엔드 분야에서 중요한 역량인 것 같습니다).</p>
<p>비동기 통신, 동기 통신에 대해 궁금하신 분들이 있을 경우 아래 블로그 글을 참고하셔도 좋을 것 같습니다.
<a href="https://inpa.tistory.com/entry/WEB-%F0%9F%8C%90-%EB%B9%84%EB%8F%99%EA%B8%B0Async%ED%86%B5%EC%8B%A0-%EB%8F%99%EA%B8%B0Sync%ED%86%B5%EC%8B%A0">https://inpa.tistory.com/entry/WEB-%F0%9F%8C%90-%EB%B9%84%EB%8F%99%EA%B8%B0Async%ED%86%B5%EC%8B%A0-%EB%8F%99%EA%B8%B0Sync%ED%86%B5%EC%8B%A0</a></p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/715e26af-e906-406e-ab21-32640884827b/image.png" alt="payload">
제가 요청URL인 <a href="https://www.kakaocorp.com/api/v1/content/home?lang=KOR">https://www.kakaocorp.com/api/v1/content/home?lang=KOR</a> 을 통해 <code>get</code>요청을 해도 해당 데이터를 응답받을 수 있기 때문에 클론코딩 사이트에서도 활용해서 구현 가능합니다(일반 사용자, 즉 제가 아니여도 가능합니다). 단, 그렇게 하기 위해서는 <code>fetch API</code>나 <code>axios</code>등을 통해 통신해서 데이터를 가져와야되고 js를 사용해야되는 영역입니다. 이렇게 하나 하나씩 다 하면 실제 사이트의 모든 부분을 클론 코딩하게 될 것 같았고, 이번 과제에서는 특정 날짜에서 카카오 랜딩 페이지에서 보여주는 화면을 기준으로 클론코딩하도록 하겠습니다. 나중에 개선한다면 할 수 있겠지만 js중심의 과제가 아니기 때문에 이 과제에서 필요하다고 생각되는 js 구현(메뉴 클릭시 하위 메뉴 나타남: 레이아웃을 위해 필요하다고 생각하여 구현함)이 아닐 경우 하지 않도록 하겠습니다.</p>
<h2 id="refactoring">refactoring</h2>
<h3 id="scss-css-inner-클래스-사용">(scss, css) inner 클래스 사용</h3>
<p>header가 아닌 main 영역도 가운데로 위치한 형태로 화면에 출력되야 되기 때문에 공통 클래스인 inner를 만들어서 재사용하는 구조로 수정했습니다.</p>
<pre><code class="language-scss">.inner {
  max-width: 1296px;
  margin: 0 auto;
}</code></pre>
<p>또한, mobile을 제외한 breakpoint에서 자연스러운 inner 스타일의 적용을 위해 관련한 media 쿼리도 추가했습니다.</p>
<pre><code class="language-scss">@media screen and (max-width: 1439px) {
  .inner {
    max-width: 952px;
  }
}</code></pre>
<h3 id="header">header</h3>
<p>스크롤을 해도 header 영역이 가려지지 않도록 <code>z-index</code>와 <code>background-color</code>를 설정하였습니다.</p>
<pre><code class="language-scss">.landing_header {
  /* ... */
  z-index: 999;
  background-color: #fff;
  /* ... */
}</code></pre>
<h3 id="js-event-handling">(js) event handling</h3>
<ul>
<li>중복을 줄이고 재사용해서 사용하기 위해 <code>findActiveNavMenu</code> 함수 작성 및 호출</li>
<li>등록하는 이벤트를 줄이고 유지보수를 위해 <code>nav</code> 요소에 이벤트를 위임하는 것으로 변경</li>
</ul>
<pre><code class="language-javascript">const attachNavMenuEvent = () =&gt; {
  /* 
    - 중복성을 줄이고 재사용 하기위해 findActiveNavMenu함수를 만들어서 사용
    - nav element를 parent인자로 주는 것을 전제로 해서 작성함
    - nav element의 클래스 이름(.on)을 활용해서 활성화된 list item을 반환
      없을 경우 undefined 반환
  */
  const findActiveNavMenu = (parent) =&gt; {
    const menu_items = Array.from(parent.children).slice(0, -1);
    const activedMenu = menu_items.find((menu_item) =&gt;
      menu_item.classList.contains(&quot;on&quot;)
    );
    return activedMenu;
  };

  const $nav_menu_list = document.querySelector(&quot;.gnb_list&quot;);

  const notExpandMenuTitles = [&quot;뉴스&quot;];

  /*
    - nav 요소에 Event delegation (이벤트 위임) 적용
    - 처음에는 li 요소에 개별적으로 이벤트를 적용했으나,
      최적화 및 유지보수를 위해 이벤트 위임을 활용하는 방식으로 변경함  
  */
  $nav_menu_list.addEventListener(&quot;click&quot;, (e) =&gt; {
    if (e.target === e.currentTarget) return;
    const title = e.target.textContent;
    if (notExpandMenuTitles.includes(title)) return;

    const activedMenu = findActiveNavMenu(e.currentTarget);
    if (activedMenu &amp;&amp; activedMenu !== e.target.parentElement) {
      activedMenu.classList.remove(&quot;on&quot;);
    }

    /* 
     - a tag영역을 li 요소의 전체영역으로 설정했기 때문에, 
       li를 클릭했을 때 target은 a tag 요소가 됨
     - li &gt; a 인 구조이기 때문에 e.target.parentElement는 li 요소가 됨
     - li 요소에 on클래스를 활용해서 메뉴 펼침을 제어하기 때문에,
       li 요소의 클래스를 toggle하여 제어
    */
    e.target.parentElement.classList.toggle(&quot;on&quot;);
  });

  /*
    - dim 영역(nav외의 영역)을 클릭했을 때 펼쳐진 메뉴가 있을 경우,
      해당 메뉴를 닫기 위해 이벤트 등록
    - 버블링 이벤트시 nav -&gt; window 순서로 이벤트가 전파되기때문에 
      nav menu를 클릭했을 때 영향을 받을 수 있겠다고 생각함 
      -&gt; 캡쳐이벤트 사용
  */
  window.addEventListener(
    &quot;click&quot;,
    (e) =&gt; {
      const activedMenu = findActiveNavMenu($nav_menu_list);
      if (!activedMenu) return;
      if (e.target !== activedMenu.querySelector(&quot;a&quot;)) {
        activedMenu.classList.remove(&quot;on&quot;);
      }
    },
    true
  );
};

attachNavMenuEvent();</code></pre>
<h1 id="main-영역">main 영역</h1>
<p>main이라는 이름에 걸맞게 다른 영역보다 많은 시간을 할애하고 있는 영역입니다.</p>
<h2 id="분석하면서">분석하면서</h2>
<p>부드러운 애니메이션 효과에 신경을 (많이) 썼다는 느낌을 받았습니다. 
예를 들면 아래와 같이</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/deb4e2b8-ef6f-4780-9a42-3ec57f402e23/image.gif" alt="position_transition"></p>
<p>화면이 줄어들어 breakpoint에 도달하면 애니메이션으로 위치가 이동(transition)하고 이를 위해 position으로 각각 위치를 지정한 것을 확인할 수 있었습니다(한땀한땀 정성을 들이는 장인정신...). </p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/772223f0-f077-4181-b2bd-4fc5b45489f7/image.png" alt="initial_state">
이를 고려해서 처음부터 해당 카드 요소의 컨테이너 측에 <code>relative</code>, 카드 요소를 <code>absolute</code>로 배치한 것을 확인할 수 있고</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/465799a4-069f-4eeb-96bc-ce429f21b6df/image.png" alt="after_state">
breakpoint에 도달하면 <code>absolute</code>의 <code>top</code>, <code>left</code> 의 포지션을 수정해서 <code>transition</code>을 통한 애니메이션을 적용합니다.</p>
<p>제가 왜 이렇게 구체적으로 얘기하냐면... 네... 이거 구현하기 위해 생각보다 많은 시간을 할애했기 때문입니다 ㅜㅜ</p>
<p>보통 애플이 이런 측면에서 변태적?인 것 같다는 말을 하는 것을 들을 수 있었는데, 카카오도 ... ? </p>
<h2 id="card">card</h2>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/e851a41c-9dd6-4c16-988e-c6f1b6dca93c/image.png" alt="card_example"></p>
<p>카카오 홈페이지에서 동일한 구조로 보여지는 저 스타일을 일반적으로 card 또는 card 컴포넌트라고 부르는 것 같습니다.
css에서 사용하기 위해서는 card라는 클래스를 만들어서 사용해야 된다고 생각하여 관련 스타일링을 위한 작업을 하게 되었습니다.</p>
<ul>
<li><p>구조
보통은 wrapper요소, 해당 content 요소 2개로 구분해서 작업을 했었는데, 유지보수 및 구현을 위해 container요소, wrapper요소, card요소 3개로 구분하여 구현했습니다.</p>
<ul>
<li>container</li>
</ul>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/7eb587d6-eebc-4137-ae9a-cc779d795269/image.png" alt="card_container"></p>
<p><code>card</code>를 활용한 영역을 구분하기 위해 작성하였고, <code>container</code>에서 외부 여백등에 대한 스타일링을 하여 나중에 수정이 필요할 때 손쉽게 하기 위한 구조라고 볼 수 있습니다. <code>box-sizing: border-box</code> 를 통해 <code>wrapper</code>요소가 <code>container</code>에서 <code>padding</code> 등을 제외한 크기를 가질 수 있도록 합니다. 또한 부모 요소인 <code>aticle</code>은 <code>display: flex</code>, <code>card container</code>는 <code>flex:1</code> 을 적용하여 <code>flex</code> 에 기반한 구조를 가지고 있습니다. <code>card container</code>는 <code>max-width</code>, <code>min-width</code>를 적용하여 카드가 사용되어지는 영역에 대한 크기를 제한합니다.</p>
<ul>
<li>wrapper</li>
</ul>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/8f5c09c9-54f2-4075-b95a-608ab5780ad1/image.png" alt="card_wrapper"></p>
<p><code>card</code> 요소가 차지해야 되는 <code>width</code>를 가지며, <code>card</code>의 외곽요소(border 등)에 대한 적용이 되는 영역입니다.</p>
<ul>
<li>card</li>
</ul>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/3f9542e0-583b-4b26-8c0e-78fe9b886863/image.png" alt="card"></p>
<p>실제 <code>card</code>의 내용이 되는 영역입니다. <code>padding</code>을 통해 여백을 가지며, 저는 여기서 추가적으로 제목 등의 영역을 구분하고자 <code>card header</code> 클래스를 사용하고 <code>html</code> 구조를 작성하였습니다.</p>
</li>
</ul>
<p>이후의 내용은 다음 포스트를 통해 다룰수 있도록 하겠습니다 ㅜㅜ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[레이아웃 클론코딩 도전기_[header]-2]]></title>
            <link>https://velog.io/@devrun_ryan/%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-%EB%8F%84%EC%A0%84%EA%B8%B0header-2</link>
            <guid>https://velog.io/@devrun_ryan/%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-%EB%8F%84%EC%A0%84%EA%B8%B0header-2</guid>
            <pubDate>Sun, 25 Dec 2022 13:52:03 GMT</pubDate>
            <description><![CDATA[<h1 id="시작하기에-앞서">시작하기에 앞서...</h1>
<p>처음에는 반응형으로 만들어서 모든 디바이스에 대응하도록 하고 싶었지만, 시간이 부족할 것 같습니다. 여건이 되는대로 개선하는 것을 목표로 해보겠습니다 ㅠㅠ</p>
<h1 id="header-작업-과정">header 작업 과정</h1>
<h2 id="line-height-">line-height ?</h2>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/50ce8a8a-9450-4a8f-948a-a1e2e309c7eb/image.png" alt="menu_line_height_devtool">
개발자 도구로 네비게이션 메뉴들에 대해 확인하던 중 &quot;여기서 <code>line-height</code>를 적용한다고?&quot;라고 생각되는 부분이 있었습니다. 이와 관련해서 생각해본 결과 <strong>&quot;사용자의 편의성을 위해 li 요소의 글자가 아닌 상하좌우 영역을 클릭하더라도 링크가 동작할 수 있도록 하고자 한다. 그리고, 가운데로 정렬하여 디자인적인 요소를 스타일링한다&quot;</strong> 라는 결론을 내리게 되었습니다. 실제 카카오 사이트의 네비게이션 메뉴글씨 상하단 영역으로 마우스를 올리더라도 클릭시 해당 링크로 이동할 수 있으며 글자는 li의 가운데 정렬 되있습니다.</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/ed83d197-de9a-474b-b71a-1fc89ecd4c51/image.gif" alt="menu_line_height"></p>
<p>일종의 <code>css trick</code>이라고 생각이 되며, 
작업 중간에 <code>flex</code>로 개선할 수도 있지만, <code>line-height</code>를 적용해서 진행해보겠습니다.</p>
<h2 id="hover">:hover</h2>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/a5931c48-0d88-46f4-a3fa-937bfcaa5bb7/image.gif" alt="kakao_nav_hover"></p>
<p>당차게 navigation 영역을 작성하다가 첫번째 난관에 부딪혔습니다.
hover시 어떻게 처리할 것인가에 대한 문제이며 아래와 같은 고민을 하였습니다.</p>
<ol>
<li>(javascript) mouse 이벤트를 통해 클래스를 삽입하고 지워서 스타일을 적용한다.</li>
<li>(css) css변수를 사용하고, javascript로 제어해야되나?</li>
<li>(css) css만 가지고 사용할 수는 없을까?</li>
</ol>
<p>가능한 css만으로 구현하고 싶어서 생각해보았고, 그 결과 구현을 할 수 있었습니다.
제가 구현한 방법은 아래와 같습니다.</p>
<ul>
<li>navigation menu item 중 <code>if(kakao) 2022</code>은 id를 부여해서 우선순위를 더 높게 준 뒤, 해당 item만이 가져야 되는 스타일을 정의해준다.</li>
<li>navigation menu list를 hover할 때 <code>if(kakao) 2022</code>를 제외한 모든 item의 글자색을 원하는 색으로 지정한다(<code>#888</code>)</li>
<li>menu item을 hover할 경우 글자색을 부모의 글자색으로 강제상속하여 원래의 글자색이 되도록 스타일을 덮어쓴다.</li>
</ul>
<pre><code class="language-scss">/* gnb(nav) */
.gnb {
  &amp;_list {
    display: flex;
    align-items: center;
    font-family: sans-serif;
    color: $fontColor;
    margin-left: 74px;
    &amp;:hover &gt; li &gt; a:not(#link_if_kakao) {
      color: #888;
    }
  }
  .list_sub {
    position: absolute;
    top: 60px;
    left: 4px;
  }
  &amp;_item {
    position: relative;
    color: $fontColor;

    .link_menu {
      display: block;
      color: $fontColor;
      flex-shrink: 0;
      font-weight: 700;
      padding: 0 28px;
      line-height: 72px;
      &amp;:hover:not(#link_if_kakao) {
        color: inherit;
      }
    }
    #link_if_kakao {
      height: 42px;
      line-height: 42px;
      padding: 0 20px;
      color: #fff;
      background-color: #000;
      border-radius: 21px;
      position: relative;
      &amp;::after {
        content: &quot;&quot;;
        position: absolute;
        top: 0;
        right: -6px;
        width: 6px;
        height: 6px;
        background-color: #ff4024;
        border-radius: 50%;
      }
    }
  }
}</code></pre>
<blockquote>
<p>결과 </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/7c35b6e3-2fcc-489a-9272-d4d173ef437c/image.gif" alt="hover_result"></p>
<h2 id="click---expand-menu">click -&gt; expand menu</h2>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/80f06258-2679-4f9d-975e-9c5e86cdc4de/image.gif" alt="menu_click"></p>
<p>클릭할 때의 구현을 위해서는 js를 이용해야 될 것 같다는 생각이 들었습니다. css 가상 선택자 중에 클릭했을 때의 요소에 대한 것은 없다고 알고 있기 때문입니다. focus가 대안이 될수는 있겠지만 완벽한 대안이라고 하기에는 말하기 어려울 것 같습니다.</p>
<p>js를 활용해 구현하기 위해 생각한 방법은 아래와 같습니다.</p>
<ul>
<li>클릭시 상위 메뉴의 글자색이 유지될수 있도록 하기 위해 <code>.on</code>클래스를 활용한 스타일을 적용</li>
<li>관련된 sublist를 li의 자식요소로 삽입(<code>position:absolute</code> 로 배치하기 위해서)</li>
<li>상위 메뉴 클릭시 <code>.on</code>클래스를 토글하여 스타일을 적용</li>
<li>메뉴 이외의 영역을 클릭할 경우 active된 메뉴의 <code>.on</code>클래스 제거<ul>
<li>window 클릭 캡쳐 이벤트</li>
<li>버블링 이벤트로 할 경우 메뉴 on -&gt; window 에서 on클래스 제거 -&gt; 메뉴가 보이지 않는다(클릭해도 변화가 없다)</li>
</ul>
</li>
</ul>
<blockquote>
<p>결과</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/ba5d0527-5485-4ced-a13b-cd7d0f596ccd/image.gif" alt="header_result"></p>
<h2 id="button---search-language-darkmode">button - search, language, darkmode</h2>
<ul>
<li>기한 내 제출이 어려울 수 있기 때문에 생략하고 진행</li>
<li>기한 내 제출이 가능할 경우 추가적으로 작업</li>
</ul>
<p>기한 내 제출이 어려울 수도 있기 때문에 먼저는 생략하고 진행하지만, 만약 시간적 여유가 있다면 작업해보는 것으로 하겠습니다 ㅠㅠ
단, button에 대해 focus 가능하도록 하기 위해 요소에 tabindex를 설정해주고, focus시 outline이 생기도록 css 스타일링 하겠습니다.</p>
<h1 id="아쉬웠던-점">아쉬웠던 점</h1>
<ul>
<li>카카오 공식 사이트에서는 kakaobig이라는 자체 글꼴을 사용하고 있지만, 제가 그 글꼴을 사용할 방법은 없기 때문에, 최대한 유사한 <code>NotoSansKR</code> (<code>sans-serif</code>, 고딕체 계열) 구글 웹폰트를 사용했습니다.</li>
<li>글자를 두껍게 적용할 경우 이로 인해 <code>sub_menu_list</code>의 <code>width</code>가 미세하게 증가해서 화면에 출력됨 <ul>
<li><code>max-width:352px</code>로 설정하여 미세하게 width가 증가하지 않도록 함</li>
</ul>
</li>
<li>desktop만 고려하여 작업하고 있음 (시간적 여유가 있을 경우 개선 예정)</li>
</ul>
<h1 id="코드-결과">코드 결과</h1>
<h2 id="html">html</h2>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;meta property=&quot;og:type&quot; content=&quot;website&quot; /&gt;
    &lt;meta property=&quot;og:site_name&quot; content=&quot;kakaocorp.com&quot; /&gt;
    &lt;meta property=&quot;og:url&quot; content=&quot;https://www.kakaocorp.com/page/&quot; /&gt;
    &lt;meta property=&quot;og:title&quot; content=&quot;Kakao&quot; /&gt;
    &lt;meta
      property=&quot;og:image&quot;
      content=&quot;https://t1.kakaocdn.net/kakaocorp/corp_thumbnail/Kakao.png&quot;
    /&gt;
    &lt;meta
      property=&quot;og:description&quot;
      content=&quot;기술과 사람으로 더 나은 세상을 만듭니다&quot;
    /&gt;
    &lt;meta
      name=&quot;description&quot;
      content=&quot;기술과 사람으로 더 나은 세상을 만듭니다&quot;
    /&gt;
    &lt;title&gt;카카오&lt;/title&gt;
    &lt;link rel=&quot;shortcut icon&quot; href=&quot;/favicon.ico&quot; /&gt;
    &lt;link rel=&quot;preconnect&quot; href=&quot;https://fonts.googleapis.com&quot; /&gt;
    &lt;link rel=&quot;preconnect&quot; href=&quot;https://fonts.gstatic.com&quot; crossorigin /&gt;
    &lt;link
      href=&quot;https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700&amp;display=swap&quot;
      rel=&quot;stylesheet&quot;
    /&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;./styles/main.scss&quot; /&gt;
    &lt;script defer src=&quot;./src/index.js&quot;&gt;&lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;header class=&quot;landing_header&quot;&gt;
      &lt;div class=&quot;landing_header__wrapper&quot;&gt;
        &lt;!-- logo --&gt;
        &lt;a class=&quot;logo_link&quot; href=&quot;https://www.kakaocorp.com/page/&quot;&gt;
          &lt;svg
            xmlns=&quot;http://www.w3.org/2000/svg&quot;
            xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot;
            viewBox=&quot;0 0 75 25&quot;
            class=&quot;ico_logo&quot;
          &gt;
            &lt;defs&gt;
              &lt;path
                data-v-37d948ec=&quot;&quot;
                id=&quot;os5cgsl0ta&quot;
                d=&quot;M0.011 0.205L11.948 0.205 11.948 22.203 0.011 22.203z&quot;
              &gt;&lt;/path&gt;
              &lt;path
                data-v-37d948ec=&quot;&quot;
                id=&quot;oanpyfjipc&quot;
                d=&quot;M0.264 0.004L13.566 0.004 13.566 15.487 0.264 15.487z&quot;
              &gt;&lt;/path&gt;
            &lt;/defs&gt;
            &lt;g fill=&quot;#000&quot; fill-rule=&quot;evenodd&quot;&gt;
              &lt;g&gt;
                &lt;path
                  d=&quot;M18.91 20.05c.344 0 .7-.046 1.071-.137.371-.09.742-.209 1.113-.354.371-.146.72-.323 1.045-.532.327-.21.616-.432.87-.668V14.87h-2.607c-1.32 0-2.284.227-2.89.681-.606.455-.91 1.173-.91 2.154 0 1.562.769 2.344 2.308 2.344m-4.706-2.235c0-1.508.503-2.658 1.513-3.448 1.008-.79 2.476-1.186 4.401-1.186h2.89v-.954c0-2.308-1.018-3.461-3.053-3.461-.653 0-1.34.09-2.057.272-.719.182-1.377.409-1.977.681l-.736-1.771c.745-.418 1.55-.74 2.413-.968.862-.227 1.704-.341 2.52-.341 3.526 0 5.288 1.88 5.288 5.642v9.54h-1.852l-.3-1.635c-.745.6-1.54 1.063-2.385 1.39-.845.328-1.649.49-2.414.49-1.325 0-2.365-.376-3.12-1.13-.754-.754-1.131-1.794-1.131-3.12&quot;
                  transform=&quot;translate(-151 -168) translate(79.5 145) translate(72 24) translate(.956 .112)&quot;
                  class=&quot;path&quot;
                &gt;&lt;/path&gt;
                &lt;g
                  transform=&quot;translate(-151 -168) translate(79.5 145) translate(72 24) translate(.956 .112) translate(29.859)&quot;
                &gt;
                  &lt;mask id=&quot;5ym1s98mqb&quot; fill=&quot;#fff&quot;&gt;
                    &lt;use xlink:href=&quot;#os5cgsl0ta&quot;&gt;&lt;/use&gt;
                  &lt;/mask&gt;
                  &lt;path
                    d=&quot;M9.222 6.53l1.963 1.416-4.823 6.052 5.586 6.705-1.934 1.5-6.596-8.07L9.222 6.53zM2.518 21.82H.011V.75L2.518.206V21.82z&quot;
                    mask=&quot;url(#5ym1s98mqb)&quot;
                    class=&quot;path&quot;
                  &gt;&lt;/path&gt;
                &lt;/g&gt;
                &lt;path
                  d=&quot;M48.735 20.05c.343 0 .701-.046 1.072-.137.371-.09.742-.209 1.113-.354.37-.146.718-.323 1.045-.532.324-.21.614-.432.868-.668V14.87h-2.606c-1.322 0-2.285.227-2.89.681-.607.455-.909 1.173-.909 2.154 0 1.562.768 2.344 2.307 2.344m-4.706-2.235c0-1.508.504-2.658 1.512-3.448 1.01-.79 2.475-1.186 4.403-1.186h2.889v-.954c0-2.308-1.017-3.461-3.053-3.461-.655 0-1.34.09-2.058.272-.719.182-1.377.409-1.975.681l-.737-1.771c.746-.418 1.55-.74 2.412-.968.862-.227 1.703-.341 2.522-.341 3.524 0 5.288 1.88 5.288 5.642v9.54h-1.855l-.3-1.635c-.745.6-1.538 1.063-2.385 1.39-.844.328-1.648.49-2.411.49-1.327 0-2.368-.376-3.121-1.13-.755-.754-1.131-1.794-1.131-3.12&quot;
                  transform=&quot;translate(-151 -168) translate(79.5 145) translate(72 24) translate(.956 .112)&quot;
                  class=&quot;path&quot;
                &gt;&lt;/path&gt;
                &lt;g
                  transform=&quot;translate(-151 -168) translate(79.5 145) translate(72 24) translate(.956 .112) translate(58.405 6.66)&quot;
                &gt;
                  &lt;mask id=&quot;e5668wah2d&quot; fill=&quot;#fff&quot;&gt;
                    &lt;use xlink:href=&quot;#oanpyfjipc&quot;&gt;&lt;/use&gt;
                  &lt;/mask&gt;
                  &lt;path
                    d=&quot;M6.915 2.021c-1.308 0-2.312.491-3.011 1.472-.701.982-1.05 2.417-1.05 4.307 0 1.872.349 3.285 1.05 4.239.699.954 1.703 1.431 3.011 1.431 1.326 0 2.34-.477 3.04-1.431.7-.954 1.049-2.367 1.049-4.239 0-1.89-.35-3.325-1.049-4.307-.7-.981-1.714-1.472-3.04-1.472m0-2.017c2.071 0 3.699.673 4.878 2.017 1.182 1.346 1.773 3.272 1.773 5.78 0 2.47-.585 4.37-1.758 5.697-1.172 1.325-2.804 1.989-4.893 1.989-2.07 0-3.698-.664-4.878-1.99C.855 12.172.264 10.272.264 7.8c0-2.507.594-4.433 1.785-5.779C3.24.677 4.862.004 6.915.004&quot;
                    mask=&quot;url(#e5668wah2d)&quot;
                    class=&quot;path&quot;
                  &gt;&lt;/path&gt;
                &lt;/g&gt;
                &lt;path
                  d=&quot;M2.552.205L.044.75v21.07h2.508V.204zm.9 13.929l6.595 8.069 1.937-1.5-5.589-6.705 4.825-6.051-1.962-1.418-5.807 7.605z&quot;
                  transform=&quot;translate(-151 -168) translate(79.5 145) translate(72 24) translate(.956 .112)&quot;
                  class=&quot;path&quot;
                &gt;&lt;/path&gt;
              &lt;/g&gt;
            &lt;/g&gt;
          &lt;/svg&gt;
        &lt;/a&gt;
        &lt;!-- nav --&gt;
        &lt;nav class=&quot;gnb&quot;&gt;
          &lt;ul class=&quot;gnb_list&quot;&gt;
            &lt;li class=&quot;gnb_item flex-container--not-shrink&quot;&gt;
              &lt;a class=&quot;menu_link&quot; href=&quot;javascript:void(0)&quot;&gt;카카오&lt;/a&gt;
              &lt;ul class=&quot;gnb_sub_list&quot;&gt;
                &lt;li class=&quot;gnb_sub_list_item flex-container--not-shrink&quot;&gt;
                  &lt;a
                    class=&quot;sub_menu_link&quot;
                    href=&quot;https://www.kakaocorp.com/page/kakao/kakaoCulture&quot;
                    &gt;카카오문화&lt;/a
                  &gt;
                &lt;/li&gt;
                &lt;li class=&quot;gnb_sub_list_item flex-container--not-shrink&quot;&gt;
                  &lt;a
                    class=&quot;sub_menu_link&quot;
                    href=&quot;https://www.kakaocorp.com/page/kakao/subsidiaryCompany&quot;
                    &gt;공동체&lt;/a
                  &gt;
                &lt;/li&gt;
                &lt;li class=&quot;gnb_sub_list_item flex-container--not-shrink&quot;&gt;
                  &lt;a
                    class=&quot;sub_menu_link&quot;
                    href=&quot;https://www.kakaocorp.com/page/kakao/history&quot;
                    &gt;히스토리&lt;/a
                  &gt;
                &lt;/li&gt;
              &lt;/ul&gt;
            &lt;/li&gt;
            &lt;li class=&quot;gnb_item flex-container--not-shrink&quot;&gt;
              &lt;a class=&quot;menu_link&quot; href=&quot;https://www.kakaocorp.com/page/news&quot;
                &gt;뉴스&lt;/a
              &gt;
            &lt;/li&gt;
            &lt;li class=&quot;gnb_item flex-container--not-shrink&quot;&gt;
              &lt;a class=&quot;menu_link&quot; href=&quot;javascript:void(0)&quot;&gt;기술과 서비스&lt;/a&gt;
              &lt;ul class=&quot;gnb_sub_list&quot;&gt;
                &lt;li class=&quot;gnb_sub_list_item flex-container--not-shrink&quot;&gt;
                  &lt;a
                    class=&quot;sub_menu_link&quot;
                    href=&quot;https://www.kakaocorp.com/page/service/tech&quot;
                    &gt;기술&lt;/a
                  &gt;
                &lt;/li&gt;
                &lt;li class=&quot;gnb_sub_list_item flex-container--not-shrink&quot;&gt;
                  &lt;a
                    class=&quot;sub_menu_link&quot;
                    href=&quot;https://www.kakaocorp.com/page/service/service&quot;
                    &gt;서비스&lt;/a
                  &gt;
                &lt;/li&gt;
              &lt;/ul&gt;
            &lt;/li&gt;
            &lt;li class=&quot;gnb_item flex-container--not-shrink&quot;&gt;
              &lt;a class=&quot;menu_link&quot; href=&quot;javascript:void(0)&quot;&gt;약속과 책임&lt;/a&gt;
              &lt;ul class=&quot;gnb_sub_list&quot;&gt;
                &lt;li class=&quot;gnb_sub_list_item flex-container--not-shrink&quot;&gt;
                  &lt;a
                    class=&quot;sub_menu_link&quot;
                    href=&quot;https://www.kakaocorp.com/page/responsible/esg&quot;
                    &gt;ESG&lt;/a
                  &gt;
                &lt;/li&gt;
                &lt;li class=&quot;gnb_sub_list_item flex-container--not-shrink&quot;&gt;
                  &lt;a
                    class=&quot;sub_menu_link&quot;
                    href=&quot;https://www.kakaocorp.com/page/responsible/social&quot;
                    &gt;소셜임팩트&lt;/a
                  &gt;
                &lt;/li&gt;
                &lt;li class=&quot;gnb_sub_list_item flex-container--not-shrink&quot;&gt;
                  &lt;a
                    class=&quot;sub_menu_link&quot;
                    href=&quot;https://www.kakaocorp.com/page/responsible/digital&quot;
                    &gt;디지털 권리&lt;/a
                  &gt;
                &lt;/li&gt;
                &lt;li class=&quot;gnb_sub_list_item flex-container--not-shrink&quot;&gt;
                  &lt;a
                    class=&quot;sub_menu_link&quot;
                    href=&quot;https://www.kakaocorp.com/page/responsible/aiEthics&quot;
                    &gt;AI 윤리&lt;/a
                  &gt;
                &lt;/li&gt;
              &lt;/ul&gt;
            &lt;/li&gt;
            &lt;li class=&quot;gnb_item flex-container--not-shrink&quot;&gt;
              &lt;a
                class=&quot;menu_link&quot;
                id=&quot;link_if_kakao&quot;
                href=&quot;https://if.kakao.com/&quot;
                target=&quot;_blank&quot;
                &gt;if(Kakao) 2022&lt;/a
              &gt;
            &lt;/li&gt;
          &lt;/ul&gt;
        &lt;/nav&gt;
        &lt;!-- util-area --&gt;
        &lt;div class=&quot;area-util flex-container--not-shrink&quot;&gt;
          &lt;!-- search-button --&gt;
          &lt;button class=&quot;btn btn_search&quot; tabindex=&quot;0&quot;&gt;
            &lt;svg
              xmlns=&quot;http://www.w3.org/2000/svg&quot;
              viewBox=&quot;0 0 28 28&quot;
              class=&quot;ico ico_search&quot;
            &gt;
              &lt;g fill=&quot;none&quot; fill-rule=&quot;evenodd&quot;&gt;
                &lt;g stroke-width=&quot;1.6&quot;&gt;
                  &lt;g transform=&quot;translate(-308 -16) translate(312 20)&quot;&gt;
                    &lt;circle cx=&quot;8.944&quot; cy=&quot;8.944&quot; r=&quot;8.944&quot;&gt;&lt;/circle&gt;
                    &lt;path d=&quot;M14.987 14.987L21.017 21.017&quot;&gt;&lt;/path&gt;
                  &lt;/g&gt;
                &lt;/g&gt;
              &lt;/g&gt;
            &lt;/svg&gt;
          &lt;/button&gt;
          &lt;!-- language-button --&gt;
          &lt;button class=&quot;btn btn_language&quot; tabindex=&quot;0&quot;&gt;
            &lt;svg
              xmlns=&quot;http://www.w3.org/2000/svg&quot;
              viewBox=&quot;0 0 24 24&quot;
              class=&quot;ico ico_language&quot;
            &gt;
              &lt;g fill=&quot;none&quot; fill-rule=&quot;evenodd&quot;&gt;
                &lt;g stroke-width=&quot;1.35&quot;&gt;
                  &lt;path
                    d=&quot;M19.353 9.914c0 5.213-4.226 9.438-9.438 9.438-5.213 0-9.438-4.225-9.438-9.438C.477 4.702 4.702.477 9.915.477c5.212 0 9.438 4.225 9.438 9.437z&quot;
                    transform=&quot;translate(-1402 -23) translate(1358 23) translate(44) translate(2 2)&quot;
                  &gt;&lt;/path&gt;
                  &lt;path
                    stroke-linejoin=&quot;round&quot;
                    d=&quot;M13.662 9.914c0 5.213-3.748 9.438-3.748 9.438s-3.747-4.225-3.747-9.438c0-5.212 3.747-9.437 3.747-9.437s3.748 4.225 3.748 9.437z&quot;
                    transform=&quot;translate(-1402 -23) translate(1358 23) translate(44) translate(2 2)&quot;
                  &gt;&lt;/path&gt;
                  &lt;path
                    d=&quot;M.876 7.018L18.952 7.018M.876 12.811L18.952 12.811&quot;
                    transform=&quot;translate(-1402 -23) translate(1358 23) translate(44) translate(2 2)&quot;
                  &gt;&lt;/path&gt;
                &lt;/g&gt;
              &lt;/g&gt;
            &lt;/svg&gt;
          &lt;/button&gt;
          &lt;!-- theme-button(darkMode) --&gt;
          &lt;button class=&quot;btn btn_theme&quot; tabindex=&quot;0&quot;&gt;
            &lt;svg
              xmlns=&quot;http://www.w3.org/2000/svg&quot;
              viewBox=&quot;0 0 24 24&quot;
              class=&quot;ico ico_theme&quot;
            &gt;
              &lt;g fill=&quot;none&quot; fill-rule=&quot;evenodd&quot; stroke-linejoin=&quot;round&quot;&gt;
                &lt;g stroke-width=&quot;1.5&quot;&gt;
                  &lt;path
                    d=&quot;M16.086 13.417c-5.013 0-9.076-4.04-9.076-9.023 0-1.596.42-3.093 1.152-4.394C3.58.456 0 4.3 0 8.977 0 13.961 4.064 18 9.076 18c3.407 0 6.372-1.868 7.924-4.628-.3.03-.605.045-.914.045z&quot;
                    transform=&quot;translate(-1344 -24) translate(1248 24) translate(96) translate(4 3)&quot;
                  &gt;&lt;/path&gt;
                &lt;/g&gt;
              &lt;/g&gt;
            &lt;/svg&gt;
          &lt;/button&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/header&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<h2 id="scss-css">scss (css)</h2>
<pre><code class="language-scss">/* ./styles/main.scss */

@import url(&quot;./_reset.scss&quot;);
@import url(&quot;https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700&amp;display=swap&quot;);

/* global */
$fontColor: #000;
$baseForeground: #000;
$buttonBaseBackground: transparent;
$buttonBaseHoverColor: #eee;

body {
  font-family: &quot;Noto Sans KR&quot;, sans-serif;
}

a {
  text-decoration: none;
}

.btn {
  appearance: none;
  outline: none;
  border: none;
  margin: 0;
  padding: 0;
  border-radius: 1.125em;
  background-color: $buttonBaseBackground;
  font-weight: 400;
  color: #333;
  cursor: pointer;
  &amp;:hover {
    background-color: $buttonBaseHoverColor;
  }
}

/* icon */
.ico {
  width: 24px;
  height: 24px;
  vertical-align: top;
}

/* custom-common-styles (for deduplication) */
.flex-container--not-shrink {
  display: flex;
  flex-shrink: 0;
}

/* header */
.landing_header {
  position: fixed;
  width: 100%;
  top: 0;
  left: 0;

  &amp;__wrapper {
    display: flex;
    align-items: center;
    min-width: 952px;
    max-width: 1296px;
    margin: 0 auto;
    height: 72px;
    justify-content: space-between;

    a.logo_link {
      display: block;
      text-decoration: none;
      width: 74px;
      height: 24px;
      .ico_logo {
        width: 100%;
        height: 100%;
      }
    }
  }
}

/* gnb(nav) */
.gnb {
  &amp;_list {
    display: flex;
    align-items: center;
    color: $fontColor;
    margin-left: 74px;
    &amp;:hover &gt; li &gt; a:not(#link_if_kakao) {
      color: #888;
    }
    &amp;:hover &gt; li.on &gt; a:not(#link_if_kakao) {
      color: inherit;
    }
  }

  &amp;_item {
    position: relative;
    color: $fontColor;
    .menu_link {
      display: block;
      color: $fontColor;
      flex-shrink: 0;
      font-weight: 700;
      padding: 0 28px;
      line-height: 72px;
      &amp;:hover:not(#link_if_kakao) {
        color: inherit;
      }
    }
    &amp;:not(.on) {
      .gnb_sub_list {
        display: none;
      }
    }
    &amp;.on {
      .gnb_sub_list {
        position: absolute;
        display: flex;
        gap: 24px;
        width: max-content;
        max-width: 352px;
        border-radius: 24px;
        top: 60px;
        left: 4px;
        padding: 0 24.5px;
        background-color: $baseForeground;
        box-sizing: border-box;

        &amp;_item {
          .sub_menu_link {
            display: block;
            color: #eee;
            font-weight: 500;
            flex-shrink: 0;
            padding: 11px 0 13px;
            line-height: 24px;
            z-index: 99;
            &amp;:hover {
              font-weight: 700;
              color: #eee;
            }
          }
        }
      }
    }
    #link_if_kakao {
      height: 42px;
      line-height: 42px;
      padding: 0 20px;
      color: #fff;
      background-color: $baseForeground;
      border-radius: 21px;
      position: relative;
      &amp;::after {
        content: &quot;&quot;;
        position: absolute;
        top: 0;
        right: -6px;
        width: 6px;
        height: 6px;
        background-color: #ff4024;
        border-radius: 50%;
      }
    }
  }
}

/* util-area */
.area-util {
  gap: 12px;

  .btn {
    width: 36px;
    height: 36px;
    flex-shrink: 0;
    &amp;:focus {
      outline: 2px solid;
      border-radius: 50%;
    }

    &amp;_search {
      &amp;:hover {
        border-radius: 50%;
      }
      .ico_search {
        stroke: $baseForeground;
      }
    }

    &amp;_language {
      &amp;:hover {
        border-radius: 50%;
      }
      .ico_language {
        stroke: $baseForeground;
      }
    }

    &amp;_theme {
      &amp;:hover {
        border-radius: 100%;
      }

      .ico_theme {
        stroke: $baseForeground;
      }
    }
  }
}</code></pre>
<h2 id="js-javascript">js (javascript)</h2>
<pre><code class="language-javascript">// ./src/index.js
const attachNavMenuEvent = () =&gt; {
  const $nav_menu_items = document.querySelectorAll(
    &quot;.gnb_list &gt; li:not(:last-child)&quot;
  );

  const notExpandMenuTitles = [&quot;뉴스&quot;];

  $nav_menu_items.forEach((menu_item) =&gt; {
    menu_item.addEventListener(&quot;click&quot;, (e) =&gt; {
      const title = e.currentTarget.querySelector(&quot;a&quot;).textContent;

      if (notExpandMenuTitles.includes(title)) {
        return;
      }

      const [activedMenu] = Array.from($nav_menu_items).filter((menu_item) =&gt;
        menu_item.classList.contains(&quot;on&quot;)
      );
      if (activedMenu) activedMenu.classList.remove(&quot;on&quot;);

      e.currentTarget.classList.toggle(&quot;on&quot;);
    });
  });

  window.addEventListener(
    &quot;click&quot;,
    (e) =&gt; {
      const [activedMenu] = Array.from($nav_menu_items).filter((menu_item) =&gt;
        menu_item.classList.contains(&quot;on&quot;)
      );
      if (!activedMenu) return;
      if (e.target !== activedMenu) activedMenu.classList.remove(&quot;on&quot;);
    },
    true
  );
};

attachNavMenuEvent();</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[레이아웃 클론코딩 도전기_[header]-1]]></title>
            <link>https://velog.io/@devrun_ryan/%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-%EB%8F%84%EC%A0%84%EA%B8%B0header-%EC%98%81%EC%97%AD</link>
            <guid>https://velog.io/@devrun_ryan/%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-%EB%8F%84%EC%A0%84%EA%B8%B0header-%EC%98%81%EC%97%AD</guid>
            <pubDate>Thu, 22 Dec 2022 13:25:25 GMT</pubDate>
            <description><![CDATA[<p>여기에서는 실제 사이트를 확인해보고 header를 구현해보는 과정을 남겨보려고 합니다.</p>
<h1 id="분석">분석</h1>
<h2 id="head">head</h2>
<h3 id="faviconico">favicon.ico</h3>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/a59417ea-eb0f-4f4a-9d61-809ee6bb521a/image.png" alt="kakao_favicon"></p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/954711c4-3123-4ba6-aa33-5b677227c050/image.png" alt="kakao_favicion_href"></p>
<ul>
<li>브라우저 탭에서 확인시 보여지는 저 작은 아이콘을 favicon이라고 부른다고 합니다. </li>
<li>favicon을 직접 제작하는 것도 가능하지만, 공식 사이트에서 제공하는 favicon의 이미지 주소에서 다운로드 받아 사용하도록 하겠습니다.</li>
</ul>
<h3 id="metatag">metaTag</h3>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/391b03d0-bc49-4374-8ec6-0d3e12c95188/image.png" alt="kakao_opengraph"></p>
<p>요즘 웹사이트 주소를 붙여넣으면 사이트에 대한 정보를 표시해주는 데, 페이스북의 opengraph나 트위터의 twitter card를 활용하는 것 같습니다. 카카오도 해당 기술을 사용하는지 확인해보니... 사용하네요 (너무 당연한 얘기인것 같지만...) 위 이미지에서는 검색 봇이 오픈그래프 속성을 확인해서 분석한 내용을 기반으로 표시된 것 같네요</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/d9af1f96-8177-4d96-88ae-2f177b15c746/image.png" alt="meta"></p>
<p><code>property=&quot;og:site_name&quot;</code> 과 같은 메타태그의 속성이 명시되있는 것이 확인됩니다. 헤더 태그를 작성하기 이전에 사이트에 대한 메타데이터를 먼저 작성해주고 시작하는 것이 좋을 것 같습니다.</p>
<p>이런 부분을 신경쓰지 않고 header부터 바로 작성했었는데...
현업에서는 사용자들을 위해 세부적인 것들을 더 작업하는거 같네요 ㅎ
헤더영역을 제작하기 이전에도 head에 메타데이터를 위해 적지 않은 부분을 작성해야 되니... 현업에서 일하시는 개발자분들 존경합니다...</p>
<p>저와 같이 궁금하셨던 분들을 위해 공식 사이트를 링크해두겠습니다</p>
<ul>
<li><p>opengraph
<a href="https://ogp.me/">https://ogp.me/</a></p>
</li>
<li><p>twitter card
<a href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards">https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards</a></p>
</li>
</ul>
<p>이 과제에서는 opengraph만 도입해서 구현해보려고 합니다.</p>
<h3 id="metatag-test">metaTag Test</h3>
<ul>
<li>reference
<a href="https://dev-hyun.tistory.com/186">https://dev-hyun.tistory.com/186</a></li>
<li>배포할 때 까지 기다려서 opengraph를 확인하는 것은 시간이 걸릴거 같고, 현재의 로컬 환경에서 어떻게 오픈그래프가 표시되는지 확인하고 싶어졌습니다. 검색해서 작업해 본 결과, localhost open graph checker 라는 크롬 확장 프로그램을 사용하여 테스트할 수 있었습니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/c172175c-6e3a-444f-807c-4590ab82d753/image.png" alt="test_copy_url"></p>
<p>이 주소를 복사해서 붙여넣기 해보니</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/4a5ee745-cbf7-4ab6-a88d-0f4866100473/image.png" alt="test_opengraph"></p>
<p>wow ! 점점 흥미가 생겨지는 것 같습니다. 사이트 주소는 해당 크롬 확장 프로그램이 firebase에 임시배포를 해서 생기는 것이라고 추측되며, 정확한 테스트는 나중에 배포한 뒤의 주소로 확인이 가능할 것 같습니다.</p>
<p>url은 나중에 배포되면, 배포사이트의 주소로 대체할 예정이지만, (head까지 작성한) 현재 html은 아래와 같습니다.</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;meta property=&quot;og:type&quot; content=&quot;website&quot; /&gt;
    &lt;meta property=&quot;og:site_name&quot; content=&quot;kakaocorp.com&quot; /&gt;
    &lt;meta property=&quot;og:url&quot; content=&quot;https://www.kakaocorp.com/page/&quot; /&gt;
    &lt;meta property=&quot;og:title&quot; content=&quot;Kakao&quot; /&gt;
    &lt;meta
      property=&quot;og:image&quot;
      content=&quot;https://t1.kakaocdn.net/kakaocorp/corp_thumbnail/Kakao.png&quot;
    /&gt;
    &lt;meta
      property=&quot;og:description&quot;
      content=&quot;기술과 사람으로 더 나은 세상을 만듭니다&quot;
    /&gt;
    &lt;meta
      name=&quot;description&quot;
      content=&quot;기술과 사람으로 더 나은 세상을 만듭니다&quot;
    /&gt;
    &lt;title&gt;카카오&lt;/title&gt;
    &lt;link rel=&quot;shortcut icon&quot; href=&quot;/favicon.ico&quot; /&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;./styles/main.scss&quot; /&gt;
  &lt;/head&gt;</code></pre>
<h2 id="header">header</h2>
<h3 id="헤더-영역-계산">헤더 영역 계산</h3>
<p>보통 사이트에서 최상단에 로고와 네비게이션 메뉴들 등이 모여있는 곳이 header라고 생각됩니다. 이런 측면에서 보았을 때 카카오 공식 홈페이지에서의 헤더영역은 아래와 같다고 분석됩니다.</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/a4456bcc-78ff-43db-b09c-e9b8829bd20a/image.png" alt="kakao_header"></p>
<p>header 영역의 가로 너비는 브라우저 창의 전체 너비를 사용할 것으로 예상되는데, 정확한 높이값을 확인해야 될 것 같습니다.</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/254eb7ad-784d-4043-8c96-e614beec917a/image.png" alt="kakao_header_height"></p>
<p>가로너비는 개발자 도구를 열어 놓은 상태여서 영향을 받은 것 같고, 100%가 맞는 것 같습니다. 높이는 72px로 확인됩니다. 이와 같이 계산된 값을 통해서 헤더영역을 제작해보도록 하겠습니다.</p>
<p>내용이 너무 길어지는 것 같아, 이를 기반으로 한 header 영역의 구현에 대한 과정은 다음 포스트에서 남기도록 하겠습니다.</p>
<p>아, commit하는 것을 깜빡할뻔했네요 ㅠㅠ</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/49af3d22-33f7-44de-8633-609d9d4bfc8f/image.png" alt="commit"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[레이아웃 클론코딩 도전기_intro]]></title>
            <link>https://velog.io/@devrun_ryan/%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-%EB%8F%84%EC%A0%84%EA%B8%B0</link>
            <guid>https://velog.io/@devrun_ryan/%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-%EB%8F%84%EC%A0%84%EA%B8%B0</guid>
            <pubDate>Thu, 22 Dec 2022 08:23:37 GMT</pubDate>
            <description><![CDATA[<p>html, css 수업을 열심히 듣고 있던 중에 두려운 단어를 듣게 되었으니, </p>
<p>강사님: &quot;~까지 <strong>과제를 제출</strong>해주세요.&quot;
나: ?!</p>
<p>자유롭게 하는 방식이기는 하지만 과제라는 것은 매번 할 때마다 부담되는 것 같습니다... 첫 과제라서 더 그런것 같기도 하고요...</p>
<p>하지만, 중요한 것은 <strong>꺾이지 않는 마음</strong>이겠죠?
두려움이 있지만 마음을 다시 잡고 과제를 하면서 이 과정을 기록으로 남기면 나중에 저에게도 도움이 될 것 같아 작성해보려고 합니다.</p>
<h2 id="과제">과제</h2>
<ul>
<li>내용<ul>
<li>원하는 사이트(페이지)를 자유롭게 선택하고 해당 페이지의 레이아웃을 클론코딩</li>
<li>평소에 도전해 보고 싶었거나 혹은 자신의 수준에 맞는 사이트(페이지)를 선택</li>
</ul>
</li>
<li>필수 요구사항<ul>
<li>설명이 포함된 README.md 파일 제공</li>
<li>결과와 비교할 수 있는 선택 사이트의 주소를 명시</li>
<li>확인 가능한 HTML, CSS 파일 등이 모두 포함되어야 한다</li>
<li>브라우저에서 정상적으로 출력되어야 한다</li>
</ul>
</li>
<li>선택 요구사항<ul>
<li>시멘틱 태그를 최대한 활용</li>
<li>최신의 CSS Flex 혹은 Grid 등을 활용</li>
<li>BEM 방법론 도입</li>
<li>JS는 자제, JS과제가 아니므로 구현해도 가볍게 구현</li>
<li>SCSS 등의 CSS 전처리 도구 도입</li>
<li>SCSS 컴파일에 Parcel과 같은 번들러를 활용</li>
</ul>
</li>
</ul>
<h2 id="기술셋-선정">기술셋 선정</h2>
<blockquote>
<p>실제로 코드를 작성하기 이전에 설계를 먼저하고 작성하는 습관이 되면 좋을 것 같아서 기술셋을 먼저 선정해보려고 합니다.
시퀀스 다이어그램과 같은 것은 이  과제와는 어울리지 않는다고 생각되기 때문에 이후 기회가 될 경우 학습해보면서 적용해보고자 합니다.</p>
</blockquote>
<p>html, css 중심의 레이아웃 과제이다보니 여러가지 기술들에 대해 비교해보고 선택하는 것은 무의미할 수 있겠지만 제 나름대로 생각해본 결과는 아래와 같습니다.</p>
<blockquote>
<p>css 전처리: SCSS</p>
</blockquote>
<table>
<thead>
<tr>
<th align="center">비교항목</th>
<th align="center">SASS</th>
<th align="center">SCSS</th>
</tr>
</thead>
<tbody><tr>
<td align="center">가독성</td>
<td align="center">△</td>
<td align="center">○</td>
</tr>
<tr>
<td align="center">호환성</td>
<td align="center">X</td>
<td align="center">○</td>
</tr>
</tbody></table>
<ul>
<li><p>가독성</p>
<ul>
<li>SASS
중첩이 들여쓰기로 구분되기 때문에 내용이 길어지지 않을 경우 깔끔하게 보여지기는 하지만, 내용이 길어질 경우에는 구분하기가 어려울 것 같습니다.</li>
<li>SCSS
CSS처럼 { } (브라켓)을 사용하여 중첩이 구분되기 때문에 SASS보다는 내용을 보고 구분하기 쉬울 것 같습니다.</li>
</ul>
</li>
<li><p>호환성
CSS 전처리기이기 때문에, CSS와의 호환성을 기준으로 비교해보겠습니다.</p>
<ul>
<li>SASS
CSS문법으로 작성된 내용을 SASS에 사용할 경우, 들여쓰기로 구분되어지기 때문에 컴파일 되지 않습니다. 이런 이유로 CSS와의 호환성은 좋지 않다고 생각합니다.</li>
<li>SCSS
CSS문법으로 작성된 내용을 SCSS에 사용해도 호환(typescript가 javascript의 superset인 것과 비슷하게 느껴지는 것 같습니다)이 되기 때문에 CSS와의 호환성은 좋다고 생각합니다.</li>
</ul>
</li>
<li><p>결론
CSS와 호환이 되는 SCSS를 사용하는 것이 좋을 것 같습니다.</p>
</li>
</ul>
<blockquote>
<p>번들러: Parcel</p>
</blockquote>
<p>reference
<a href="https://velog.io/@subin1224/Parcel-vs-Rollup-vs-Webpack-%EB%B9%84%EA%B5%90">https://velog.io/@subin1224/Parcel-vs-Rollup-vs-Webpack-%EB%B9%84%EA%B5%90</a></p>
<table>
<thead>
<tr>
<th align="center">비교항목</th>
<th align="center">Parcel</th>
<th align="center">Rollup</th>
<th align="center">Webpack</th>
</tr>
</thead>
<tbody><tr>
<td align="center">환경설정(configuration)</td>
<td align="center">zero-config</td>
<td align="center">entry, output, loaders, plugins 등을 설정하는 구성파일이 필요, 파일변환을 위해서는@svgr/rollup과 같은 라이브러리가 필요함</td>
<td align="center">entry, output, loaders, plugins 등을 설정하는 구성파일이 필요, 파일변환을 위해서는 loader가 필요</td>
</tr>
<tr>
<td align="center">devServer</td>
<td align="center">devServer가 내장되있음</td>
<td align="center"><code>rollup-plugin-serve</code> 제공, live-reload가 필요한 경우 <code>rollup-plugin-livereload</code> 필요함</td>
<td align="center"><code>webpack-dev-server</code> 제공, live-reload 지원</td>
</tr>
</tbody></table>
<ul>
<li><p>환경설정</p>
<ul>
<li>Parcel
zero-configuration으로 별도의 환경설정없이 사용 가능</li>
<li>Rollup
rollup.config.js 와 같이 별도의 환경설정이 필요하며, 경우에 따라 추가적으로 라이브러리를 활용하여 설정해줘야 함</li>
<li>Webpack
rollup과 같이 별도의 환경설정 파일이 필요하며, polyfill을 지원하지 않기 때문에 필요한 경우 관련 설정이 필요합니다. 다양한 변환을 위해서는 <code>css-loader</code>, <code>babel-loader</code>와 같은 <code>loader</code>가 필요하고 관련 설정이 필요합니다.</li>
</ul>
<p>=&gt; Parcel &gt; Rollup &gt; Webpack  </p>
</li>
<li><p>devServer</p>
<ul>
<li>Parcel
devServer가 내장되어있고, 파일이 변경될 경우 다시 빌드합니다.</li>
<li>Rollup
devServer가 필요할 경우 rollup-plugin-serve 라이브러리를 통해 사용가능하며, live-reload가 필요한 경우 rollup-plugin-livereload가 필요합니다.</li>
<li>Webpack
webpack-dev-server를 제공하고, live-reload를 지원합니다.</li>
</ul>
<p>=&gt; Webpack &gt; Parcel &gt; Rollup </p>
</li>
<li><p>결론</p>
<ul>
<li>초기 환경설정과 학습비용(러닝커브)이 있지만 Webpack이 여러 부분에서 우수한 것 같습니다. </li>
<li>안정적인 애플리케이션을 제공해야되는 실제 서비스 사이트 전체를 개발하는 것이라면 Weppack을 사용했을 것 같습니다.</li>
<li>다만, 현재 과제(프로젝트)는 시간이 제한적이고 복잡한 설계가 필요한 것은 아니라고 생각되기 때문에 Parcel을 사용해서 빠르게 개발하는 것이 좋을 것 같습니다. Webpack이나 Rollup을 사용한다면 환경설정을 위해 별도의 시간을 소모해야 되기 때문입니다.</li>
</ul>
</li>
</ul>
<blockquote>
<p>배포: netlify</p>
</blockquote>
<ul>
<li><p>Vercel
Vercel은 주로 Nextjs로 만든 사이트를 배포할 때 여러 이점을 얻을 수 있다고 생각합니다.</p>
</li>
<li><p>Heroku
Heroku의 경우 예전에는 무료여서 간단한 프로젝트에 많이 사용하는 추세인 것 같았지만, 최근에 유료 정책으로 변환되어서 이용하고 싶다는 생각이 들지 않는것 같습니다(AWS기반이고 Heroku자체 이익도 있어야 되기 때문에 AWS를 이용하는 것보다 비용이 더 비싼것 같습니다)</p>
</li>
<li><p>Netlify
비용을 지불하지 않더라도 간편하고 빠르게 사이트를 배포할 수 있는 것 같습니다.</p>
</li>
<li><p>결론
현재 과제에서는 Vercel보다는 Netlify를 이용해 배포하는 것이 좋다고 생각합니다.</p>
</li>
</ul>
<blockquote>
<p>요약</p>
</blockquote>
<p>Parcel을 사용해 SCSS를 컴파일하여 CSS를 생성하고, 다른 사람들이 확인 가능해야 되기 때문에 배포도 해야됩니다. 배포 사이트는 여러 사이트가 있지만, 레이아웃 위주의 클론코딩이기때문에 여러가지를 고려해야 될 필요가 없다고 생각이 됩니다. 그래서 배포는 netlify를 활용하여 진행해보려고 합니다. </p>
<h2 id="클론코딩-사이트-선정">클론코딩 사이트 선정</h2>
<p>생각이 나는 사이트는 네이버, 슬랙, bugs 등의 여러가지가 있었지만 ... 저는 카카오 프렌즈의 캐릭터를 좋아하기 때문에(제 블로그 이름이 devrun_<strong>ryan</strong> 이네요?) 카카오 공식 홈페이지의 랜딩페이지를 클론코딩 해보려고 합니다. 그리고 카카오 공식 홈페이지에서는 <code>position: sticky</code> 를 사용하는 것 같아서 관련한 css도 학습이 될 것 같습니다.</p>
<p>앞으로 진행하면서 내용을 더 남길 수 있도록 노력하겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL_Git_Github_사용해보기]]></title>
            <link>https://velog.io/@devrun_ryan/TILGitGithub%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@devrun_ryan/TILGitGithub%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Tue, 20 Dec 2022 07:13:34 GMT</pubDate>
            <description><![CDATA[<h2 id="git과-함께-사용하는-원격저장소cloud-remote-repository-services">git과 함께 사용하는 원격저장소(Cloud Remote Repository Services)</h2>
<ul>
<li>Github: 비영리였지만, 이후 Microsoft에 인수되었습니다. 현재 가장 유명한 서비스입니다.</li>
<li>Bitbucket<ul>
<li>Atlassian이 서비스</li>
<li>jira, confluence, trello 등의 부가도구와 유기적입니다.</li>
</ul>
</li>
<li>GitLab<ul>
<li>GitLab이 서비스</li>
<li>사설 서버 구성이 가능합니다(보안)</li>
<li>github private repository이더라도 github에 업로드된 소스코드는 github 데이터 센터에 저장되는 것이기 때문에, 보안에 민감할 경우 GitLab을 통해 회사 사설 서버로 구성하여 사용이 가능합니다</li>
</ul>
</li>
</ul>
<h2 id="준비사항">준비사항</h2>
<p>여기에서는 가장 많이 이용하는 github과 같이 연동하여 사용하는 것을 전제로 진행해보겠습니다.</p>
<h3 id="git">git</h3>
<ul>
<li>git 설치<ul>
<li>macOS : git 홈페이지에서 다운로드 <a href="https://git-scm.com/">https://git-scm.com/</a></li>
<li>window<ul>
<li>gitforwindows 홈페이지에서 다운로드 <a href="https://gitforwindows.org/">https://gitforwindows.org/</a></li>
<li>git bash를 통해 unix-like 명령어로 cli이용이 가능(window 명령프롬프트에서 ls와 같은 명령어는 동작하지 않습니다)하며, 이후 브랜치 전략으로 git flow를 적용할 때 작업을 편리하게 해주는 command가 기본 내장되어 있음 =&gt; window에서 편하게 git으로 개발하기 위해 다운로드
<img src="https://velog.velcdn.com/images/devrun_ryan/post/45bb0b8b-76a7-402d-a7e1-72ed3f53f4a9/image.png" alt="window_shell">
<img src="https://velog.velcdn.com/images/devrun_ryan/post/a7850fdc-3487-44a5-b6a1-878bcee50e0b/image.png" alt="window_git_bash"></li>
</ul>
</li>
</ul>
</li>
</ul>
<ul>
<li><p>설치 확인</p>
<pre><code class="language-bash">$ git -v</code></pre>
</li>
<li><p>git 환경설정</p>
<pre><code class="language-bash">$ git config --global user.name &quot;github유저네임(아이디)&quot;
$ git config --global user.email &quot;github메일주소&quot;
$ git config --global core.editor &quot;vim&quot;
$ git config --global core.paper &quot;cat&quot;</code></pre>
<ul>
<li><code>$ git config --global user.name &quot;github유저네임(아이디)&quot;</code><ul>
<li><code>--global</code> 플래그(옵션)이 없으면 새 프로젝트를 할 때마다 계속 설정해야 될 수 있습니다. 번거로운 작업이 될 수 있고 로그인을 여러번 하는 것을 방지하는 등의 이유로, 해당 플래그를 사용해서 설정하는 것을 권장합니다.
(다른 사이트에서 참고할 때 <code>--global</code> 설정하는 내용이 많이 보이는 것도 그런 이유가 아닐까 생각됩니다)</li>
<li>github과 연동할 때 여기서 설정한 유저네임은 매치되지 않기 때문에 일치할 필요성은 없다고 하지만, 특별한 이유가 아니라면 github에서 사용하는 유저네임(아이디)로 설정하는 것을 권장합니다. </li>
</ul>
</li>
<li><code>$ git config --global user.email &quot;github메일주소&quot;</code>
github과 연동할 때 여기서 설정한 이메일을 매치하여 작업하기 때문에, github에서 등록한 email주소와 다르다면 에러가 발생할 수 있습니다. github과 연동하고자 할 때 에러가 발생한다면 <code>git config --global --list</code> 또는 <code>git config --list</code> 명령을 통해 email이 올바르게 작성되었는지 먼저 확인해보시는 것도 괜찮습니다.
<img src="https://velog.velcdn.com/images/devrun_ryan/post/7f1df165-6748-411e-9428-bd7911960572/image.png" alt="git_config_global_list"></li>
<li><code>$ git config --global core.editor &quot;vim&quot;</code>
git에서 vim을 이용하여 텍스트 에디트를 할 경우 설정합니다.</li>
<li><code>$ git config --global core.paper &quot;cat&quot;</code>
cli에서 텍스트 입력이 필요할 때 다른 프로그램(e.g. VSCode)이 열릴 수 있습니다. DX(Developer Experience, 개발자 경험)측면에서 좋지 않기 때문에 shell에서 텍스트를 출력하고 확인할 수 있도록 설정합니다.</li>
</ul>
<blockquote>
<p>잘못 설정한 경우 해결 방법</p>
</blockquote>
<ol>
<li><code>vi ~/.gitconfig</code> 명령으로 텍스트를 편집하여 (직접) git 환경설정 파일을 수정</li>
<li><code>git config --global --unset user.email</code> =&gt; <code>git config --global user.email &quot;수정할 메일 주소&quot;</code>
(위는 예시 입니다. <code>--unset</code>과 수정하고 싶은 환경설정이름을 사용하여 설정을 제거해주고, 이후 다시 설정해주면 됩니다)<blockquote>
<p>window git bash에서 붙여넣기</p>
</blockquote>
</li>
</ol>
<p>마우스 우클릭으로 붙여넣기 하거나, <code>shift</code> + <code>insert</code> 키 입력으로 붙여넣기</p>
</li>
</ul>
<h3 id="github">github</h3>
<ul>
<li><p>github 가입 <a href="https://github.com/">https://github.com/</a></p>
</li>
<li><p>가입할 때 등록한 <code>username</code>, <code>email</code>, <code>password</code> 를 잃어버리지 않고 잘 기억하기</p>
</li>
<li><p>repository(저장소) 생성
<img src="https://velog.velcdn.com/images/devrun_ryan/post/3f4bfa58-9c2e-4e1b-b230-ec68dddc870d/image.png" alt="github_create_path">
Profile페이지 - Repositories탭 에서 <code>New</code> 버튼 클릭
<img src="https://velog.velcdn.com/images/devrun_ryan/post/7d0bfd89-80af-4e66-997b-cf6c1202f259/image.png" alt="github_createRepository_detail"></p>
<ul>
<li><code>*</code> 표시된 항목은 필수 입력 항목입니다.</li>
<li><code>Owner</code> 
repoistory를 생성할 계정을 선택해주시면 됩니다.</li>
<li><code>Repository name</code> 
생성할 repository의 이름입니다.</li>
<li><code>Description</code> 
생성할 repository에 대한 설명입니다. 입력하지 않아도 repository 생성이 가능합니다(Optional)</li>
<li><code>Public</code>, <code>Private</code>
repository를 모든 사용자에게 공개(<code>Public</code>)할지 비공개(<code>Private</code>)로 할 것인지를 설정합니다. Private repository의 경우 추가로 비용을 지불하지 않을 경우 제약사항이 있을 수 있으니 Public으로 사용하는 것을 권장합니다.</li>
<li><code>Add a README file</code><ul>
<li>README.md 파일을 자동으로 생성해줍니다.</li>
<li><code>create react app(CRA)</code>과 같이 자동으로 README.md파일을 생성하는 라이브러리와 같이 사용한다면, 서로 충돌이 발생(github에서 생성한 README.md, CRA에서 생성한 README.md - 둘 다 루트경로에 생성하기 때문에 동일한 위치)할 수 있습니다.
conflict를 해결하면 이용이 가능하지만, 번거롭기 때문에 이런 문제를 방지하고자 하는 경우에는 설정하지 않는 것을 권장합니다.</li>
</ul>
</li>
<li><code>Add .gitignore</code>
gitignore.io 사이트를 통해 .gitignore파일을 커스텀하게 작성할 예정이기 때문에 여기에서는 설정하지 않습니다.  <a href="https://www.toptal.com/developers/gitignore/">https://www.toptal.com/developers/gitignore/</a></li>
<li><code>Choose a license</code><ul>
<li>보통은 <code>MIT License</code>를 많이 사용합니다.</li>
<li>오픈소스 라이센스에 대해 궁금하신 분들은 <a href="https://sktelecom.github.io/guide/use/obligation/">https://sktelecom.github.io/guide/use/obligation/</a> 를 참고해보시는 것도 괜찮습니다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="사용해보기">사용해보기</h2>
<h3 id="git-process-flow">git process flow</h3>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/6b72c863-cced-45c7-b552-75e105b2c6a3/image.png" alt="git_process_flow_and_command"></p>
<h3 id="git-commit-convention">git commit convention</h3>
<ol>
<li>commit의 제목은 commit을 설명하는 하나의 구나 절로 완성</li>
<li><code>Important of Capitalize</code>와 같이 가독성을 위해 대문자를 적절히 사용</li>
<li>prefix 작성</li>
</ol>
<table>
<thead>
<tr>
<th align="center">prefix</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td align="center">feat</td>
<td>기능 개발 관련</td>
</tr>
<tr>
<td align="center">fix</td>
<td>오류 개선 혹은 버그 패치</td>
</tr>
<tr>
<td align="center">docs</td>
<td>문서화 작업</td>
</tr>
<tr>
<td align="center">test</td>
<td>test 관련</td>
</tr>
<tr>
<td align="center">conf</td>
<td>환경설정 관련</td>
</tr>
<tr>
<td align="center">build</td>
<td>빌드 관련</td>
</tr>
<tr>
<td align="center">ci</td>
<td>Continuous Integration</td>
</tr>
<tr>
<td align="center">refactor</td>
<td>코드 리팩토링(코드의 결과물은 동일하고 구조 등이 수정되었을 경우 등)</td>
</tr>
</tbody></table>
<h3 id="commit-할-때-기억해야-할-것">commit 할 때 기억해야 할 것</h3>
<ul>
<li>commit 메시지의 기본 구조<pre><code>prefix: subject
</code></pre></li>
</ul>
<p>body</p>
<p>footer</p>
<pre><code>- 동작 가능한 최소단위로 자주 commit합니다.
- 해당 작업단위에 수행된 모든 파일 변화가 해당 commit에 포함되어야 합니다.
- 모두가 이해할 수 있는 log를 작성해야 합니다.
- Open Source Contribution의 경우 영어가 강제되지만, 그렇지 않을 경우 팀 내 사용언어를 따라 씁니다.
- 제목은 50자이내로 축약해서 작성하며, 내용은 문장형으로 작성하여 추가설명 합니다. 해당 commit의 구성과 의도를 충실히 작성할 수 있도록 합니다.
- 제목과 내용은 구분할 수 있도록 한 줄 띄워 분리합니다.

### git commands
- `status`
```bash
$ git status</code></pre><p>추적상태를 출력해줍니다.</p>
<ul>
<li><p><code>add</code></p>
<pre><code class="language-bash">$ git add 파일경로</code></pre>
<ul>
<li>해당 파일을 스테이지에 올립니다(commit을 작성하기 이전에 관련된 파일들을 설정)</li>
<li><code>git add .</code>을 통해 변경된(추적되지 않은) 모든 파일을 스테이지에 올리고 commit을 작성할 수 있습니다. 그러나, (최소한으로 동작하는) 기능의 단위로 commit을 작성한다는 취지와는 어긋날 수 있습니다. 그래서, 관련된 파일을 직접 지정해서 add하고 commit을 작성하는 것이 올바른 습관이라고 할 수 있습니다.<blockquote>
<p>add 할 때 CRLF 관련 경고(warning) 메시지가 출력될 경우</p>
</blockquote>
</li>
</ul>
<p>운영체제에서 개행문자를 처리하는 방식이 다름에서 오는 경고이며, 그냥 사용하셔도 동작은 되지만, 해결하고 싶으신 경우는 
<a href="https://www.lesstif.com/gitbook/git-crlf-20776404.html">https://www.lesstif.com/gitbook/git-crlf-20776404.html</a> 참고해보시는 것이 좋을 것 같습니다.</p>
</li>
<li><p><code>commit</code></p>
<pre><code class="language-bash">$ git commit</code></pre>
<ul>
<li>commit을 작성합니다.</li>
<li><code>-m</code> 플래그(옵션)은 가급적이면 사용하지 않는 것을 권장합니다.</li>
<li><code>git commit -m &quot;메시지&quot;</code>로 작업할 경우 git commit 명령을 통해 생성되는 기본적인 내용을 무시하고 지정한 메시지가 강제로 적용되어 작성됩니다(기본적인 내용이더라도 유용한 정보들이 요약되어 작성되있는 경우가 있습니다). 그래서, <code>git commit</code> =&gt; <code>제목, 내용 직접 작성</code> 의 흐름으로 git을 사용하는 것이 올바른 습관이라고 할 수 있습니다.</li>
</ul>
</li>
<li><p><code>log</code>
이력을 확인할 수 있습니다.</p>
</li>
<li><p><code>push</code></p>
<pre><code class="language-bash">$ git push {원격저장소 별칭} {브랜치 이름}</code></pre>
<p><code>git push origin main</code>
작성한 commit들을 origin(별명, alias)으로 설정한 원격저장소의 main 브랜치에 보냅니다.</p>
</li>
</ul>
<h3 id="경험할-수-있는-에러">경험할 수 있는 에러</h3>
<blockquote>
<p>remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead.
remote: Please see <a href="https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/">https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/</a> for more information.</p>
</blockquote>
<p>정책이 변경되어서 패스워드 인증대신에 token을 발급하고 사용해야 됩니다.</p>
<ul>
<li>settings - developer settings 클릭
<img src="https://velog.velcdn.com/images/devrun_ryan/post/3be7dcf3-f029-457b-acc5-b5f0791d67e7/image.png" alt="settings"></li>
<li>Personal access tokens - Generate new token - Generate new token(classic) 클릭하여 토큰 생성
<img src="https://velog.velcdn.com/images/devrun_ryan/post/e28f07d8-4475-4ba2-9dce-930603741740/image.png" alt="access_tokens">
토큰을 발급하면 한번만 보여주기 때문에 기억하거나 저장해서 사용해야 됩니다.(잃어버린 경우 다시 발급해야 됩니다)</li>
<li>비밀 번호입력시 해당 토큰 값으로 입력</li>
<li>참고할만한 사이트
<a href="https://yian.tistory.com/38">https://yian.tistory.com/38</a></li>
</ul>
<blockquote>
<p>unable to acess &#39;<a href="https://github.com/%EC%95%84%EC%9D%B4%EB%94%94/%EC%A0%80%EC%9E%A5%EC%86%8C.git&#39;:The">https://github.com/아이디/저장소.git&#39;:The</a> requested URL returned error: 403 (403에러)</p>
</blockquote>
<p>github에 최초로 등록한 계정과 현재 push하는 계정(환경설정에서 설정한 계정)이 달라 해당 저장소에 접근 권한이 없어서 나타나는 에러입니다.</p>
<ol>
<li>윈도우 자격증명 관리자로 해결
<a href="https://itsjh.tistory.com/47">https://itsjh.tistory.com/47</a> 참고하시면 도움이 될 것 같습니다.</li>
<li><code>git remote set-url</code>로 Remote URL 변경하기
<a href="https://velog.io/@arthur/GitHub-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0-The-requested-URL-returned-error-403">https://velog.io/@arthur/GitHub-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0-The-requested-URL-returned-error-403</a> 참고하시면 도움이 될 것 같습니다.</li>
</ol>
<h3 id="gitignoreio-사용법-및-gitignore를-사용하는-이유">gitignore.io 사용법 및 .gitignore를 사용하는 이유</h3>
<ol>
<li>입력 창에 운영체제(window, macOS, ...), 프레임워크(React, Vuejs, ...), 프로그래밍 언어(Node, JAVA, ...)를 입력합니다.</li>
<li>자동완성 목록에서 선택하고, 없을 경우 유사한 것이 있다면 그것을 선택해도 됩니다(e.g. javascript 대신에 Node 선택)</li>
<li>생성 버튼을 누릅니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/886584ce-63cf-48c4-aed0-a56aa297e5a0/image.png" alt="gitignore.io"></p>
<ol start="4">
<li>텍스트 파일을 모두 선택 및 복사하여 사용합니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/ac4f93a0-bf71-49e8-bae5-6d8ef996c6aa/image.png" alt="ignore"></p>
<blockquote>
<p>.gitignore를 사용하는 이유</p>
</blockquote>
<ul>
<li>.gitignore에서 명시한 파일들은 git에서 변경사항이 발생했다고 인식하지 않습니다.</li>
<li>실제 소스코드와 관련된 파일이 아닌 다른 설정파일들도 추적하게 된다면, 의미없는 파일들을 commit하고 push해야 됩니다. window 설정파일 등이 이에 해당한다고 볼 수 있고, 이런 이유로 운영체제 등에 대해 추적할 필요가 없는 파일을 설정하는 것입니다.</li>
<li>npm을 사용할 경우 package.json만 잘 작성되어 있다면 <code>npm i</code>를 통해 의존 라이브러리(사용되는 라이브러리)를 모두 설치할 수 있기때문에 실제 라이브러리 파일들이 저장되는 공간인 node_modules가 github에 올라가지 않아도 됩니다. node_modules는 용량을 많이 차지하는 편이기 때문에 사이즈를 줄이기 위해서라도 git에서 무시하도록 설정하는 것이 좋습니다.</li>
<li><code>.env</code>와 같이 <code>IDE</code> (e.g. VSCode, WebStorm, ...)상에서 노출되어서 않되는 값들을 설정한 경우 git에서 무시하도록 설정해야합니다.( github 등의 원격저장소에 업로드 되고, 일반 사용자들이 해당 값을 본다면 ... ?)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL_Git_개요]]></title>
            <link>https://velog.io/@devrun_ryan/TILGit%EA%B0%9C%EC%9A%94</link>
            <guid>https://velog.io/@devrun_ryan/TILGit%EA%B0%9C%EC%9A%94</guid>
            <pubDate>Sat, 17 Dec 2022 06:54:49 GMT</pubDate>
            <description><![CDATA[<h2 id="git은-무엇인가">git은 무엇인가?</h2>
<blockquote>
<p>소스코드를 관리하거나 버전을 관리하기 위한 시스템, 소프트웨어 공학에서는 형상관리 도구에 포함된다고 할 수 있다.</p>
</blockquote>
<blockquote>
<p>VCS( Version Control System, 버전관리 시스템 )
SCM( Source Code Management, 소스코드 관리 ) &lt; SCM( Software Configuration Management, 형상관리 )</p>
</blockquote>
<ul>
<li>리눅스를 만든 Linus Torvalds가 만들었다. (단 2주만에... 대단하다)</li>
<li>git(버전관리 시스템)과 github(웹 서비스)는 같지 않습니다(같다고 혼용하면 안됩니다)</li>
<li>2022년에 실시한 stackoverflow survey에서 전체 93.87%라는 압도적인 비율을 차지하고 있다(업계 1위로 자리 잡고 있다) 
<a href="https://survey.stackoverflow.co/2022/#section-version-control-version-control-systems">https://survey.stackoverflow.co/2022/#section-version-control-version-control-systems</a></li>
</ul>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/87a5b44f-411b-4677-bb1b-68acce23a167/image.png" alt="stackoverflow_2022_survey_vcs"></p>
<h2 id="git의-필요성">git의 필요성</h2>
<p>당장 현실로 느껴지는 것은 stackoverflow 설문조사에서 볼 수 있듯이 개발을 직업으로 가지면 <strong>git을 사용할 줄 알아야 된다</strong>는 것입니다.
(회사에서는 git을 사용해서 협업하고 있는데 제가 사용할 줄 모르면 원활한 협업이 힘들겠죠? &quot;📨팀장님: 탕비실 ... go?&quot;)
개인이 개발하더라도 git의 특징과 장점이 있기 때문에  사용하는 것이 (정신건강에) 좋다고 생각합니다.</p>
<blockquote>
<p>발표자료_최종 -&gt; 발표자료_최종_수정 -&gt; ... -&gt; 발표자료_진짜_진짜_최종_수정2 -&gt; ... 언제까지?? 이 많은 파일들을 다 가지고 있어야 되나? ㅠㅠ</p>
</blockquote>
<p>git을 사용하면 위와 같은 고민을 하지 않아도 됩니다.
파일 단위가 아니라 라인 단위로 추적하기 때문에 관련된 파일을 다 기록하거나 기억할 필요가 없다는 것입니다.</p>
<p>비유를 들면, 조별 과제를 한다고 하였을 때 </p>
<blockquote>
<p>팀장님이 usb를 팀원들에게 개별적으로 나눠 줌 
=&gt; 팀원들이 작업해서 팀장님에게 usb를 전달
=&gt; 팀장님은 모든 usb를 연결해서 각각 확인
(소스코드 주고 받기)
=&gt; 팀장님은 원하는 내용을 복사 붙여넣기하여 최종 결과물을 파일에 반영
(배포)
=&gt; 반영된 파일을 usb에 저장하고 다시 작업할 때 팀원들에게 다시 usb를 전달
(소스코드 주고 받기, 기능 개발)
=&gt; ... (반복)</p>
</blockquote>
<p>기능 구현하고 하나의 버전으로 배포하는 과정이라고 볼 수 있는데, 이를 위해 번거로운 과정(비효율적)을 거치고 있다는 생각이 들지 않으시나요?</p>
<h2 id="특징">특징</h2>
<ul>
<li>단순한 구조와 빠른 속도</li>
<li>분산형 저장소를 지원한다 (네트워크가 되지 않더라도 독립적으로 로컬에서 개발을 하고, 이후 네트워트가 가능할 때 원격 저장소와 동기화를 할 수 있다)</li>
<li>비선형적 개발(수천개의 브랜치)이 가능하다</li>
</ul>
<h2 id="장점">장점</h2>
<ul>
<li>서로 소스코드를 주고 받지 않아도 동시작업이 가능하기 때문에 생산성이 증가한다</li>
<li>수정내용은 commit 단위로 관리하며, 배포 뿐 아니라 원하는 시점으로 Checkout(Switch) 가능하다</li>
<li>새로운 기능 추가는 Branch로 개발하여 편안하게 실험(개발)이 가능하며, 성공적으로 개발이 완료될 경우 Merge하여 반영한다</li>
<li>인터넷이 연결되지 않아도 개발할 수 있음 (네트워크가 되지 않아 서버에 접속이 되지 않아도, 로컬에서 먼저 개발을 진행할 수 있다)</li>
</ul>
<h2 id="구조">구조</h2>
<p><strong>Git Objects</strong></p>
<ul>
<li>Blob
파일 하나의 내용에 대한 정보</li>
<li>Tree
Blob이나 subtree의 메타데이터(디렉토리 위치, 속성, 이름 등)</li>
<li>Commit
커밋 순간의 스냅샷</li>
</ul>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/5f0dcfb2-aee6-4569-84c5-5e5456e7115a/image.png" alt="Git_Objects"></p>
<h3 id="reference">reference</h3>
<p><a href="https://git-scm.com/book/en/v2/Git-Internals-Git-Objects">https://git-scm.com/book/en/v2/Git-Internals-Git-Objects</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL_CLI_commands]]></title>
            <link>https://velog.io/@devrun_ryan/TILCLIcommands</link>
            <guid>https://velog.io/@devrun_ryan/TILCLIcommands</guid>
            <pubDate>Thu, 15 Dec 2022 06:14:57 GMT</pubDate>
            <description><![CDATA[<h1 id="cli-commands">CLI Commands</h1>
<h2 id="개요">개요</h2>
<p>개인적으로 개발을 진행했을 때 CLI라는 용어를 많이 듣게 되고, 클라우드 플랫폼(e.g. AWS) 공식문서에서 CLI에서 어떤 입력을 통해 어떤 작업을 수행할 수 있다는 것을 상세하게 설명한 것을 보았습니다.</p>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/0da34bf6-20b0-4ed2-a100-7d042569e081/image.png" alt="AWS_Amplify_개발자_문서"></p>
<p>그리고, <code>git push origin main</code> <code>git add .</code> 등을 통해 CLI라는 용어를 몰랐더라도 그동안 자연스럽게 CLI를 사용했던 나를 발견할 수 있습니다.</p>
<p>수학을 공부할 때 본격적으로 수학을 학습하기 이전에 이해하기 위해 필요한 숫자를 학습하는 것처럼, 개발을 공부할 때 shell에 대한 내용에 대해 학습하면 좋을 것 같아 학습한 내용을 정리해보려고 합니다. 개인적으로 학습한 내용을 기반으로 작성된 것이기 때문에 잘못된 내용이 있거나 개선해야 될 점등을 말씀해주시면 참고하고 수정하겠습니다.</p>
<h2 id="용어">용어</h2>
<h3 id="cli">CLI</h3>
<blockquote>
<p>Command Line Interface (명령줄 기반 인터페이스)</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/fa3a41b3-6f80-45bf-a893-97eacbacac87/image.png" alt="CLI_prompt"></p>
<p>CLI란 Command Line(명령줄) 기반으로 명령을 내리고, 응답을 받는다는 의미입니다. 대표적으로 위의 이미지와 같이 윈도우에서 명령프롬프트가 있습니다.</p>
<h3 id="gui">GUI</h3>
<blockquote>
<p>Grapic User Interface</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/940e13cb-a31e-4858-8506-6039f0fbd1d3/image.png" alt="GUI_탐색기"></p>
<p>GUI는 Grapic User Interface의 줄임말이고, 마우스와 같은 그래픽 요소를 이용해서 명령을 내리고 응답을 받는다는 의미입니다. 대표적으로 위의 이미지와 같이 윈도우에서의 탐색기가 있습니다.</p>
<h3 id="알아두면-좋은-용어">알아두면 좋은 용어</h3>
<ul>
<li>delete: 삭제라고 부르며, 물리적인 삭제를 의미합니다.</li>
<li>remove: 제거라고 부르며, 논리적인 삭제를 의미합니다(실제 하드디스크에는 존재하지만, 접근할수 있는 주소를 차단하여 없는 것처럼 동작합니다. 이후 하드디스크에서 큰 용량의 파일을 저장해야 되는 등의 상황이 올 때 해당 공간에 다른 값이 할당될 수 있습니다. 즉 덮여쓰기 될 수 있습니다)</li>
</ul>
<h2 id="commands">Commands</h2>
<h3 id="shell">shell</h3>
<p>개발 공부를 학습할 때 주로 unix-like 명령어를 접하고, 최근에는 윈도우에서도 관련된 명령어를 사용할 수 있도록 지원해주기 때문에 여기에서는 unix-like shell 기반을 기준으로 설명하겠습니다.</p>
<ul>
<li><p><code>~</code></p>
<blockquote>
<p>Tilde</p>
</blockquote>
<p>  틸드(물결표시)라고 부르며, shell에서는 최상위 디렉토리(유저 디렉토리)를 의미합니다.</p>
</li>
<li><p><code>/</code></p>
<blockquote>
<p>Delimiter</p>
</blockquote>
<pre><code> 슬래시라고 부르며, shell에서는 디렉토리(경로)를 구분하기 위해 사용합니다.</code></pre></li>
<li><p><code>$</code> 
현재 shell에서 입력을 받을 준비가 되었다는 의미입니다.</p>
</li>
<li><p><code>*</code>
별표(asterisk, 에스터리스크)라고 부르며, shell에서는 &#39;모든&#39;이라는 의미로 사용됩니다.</p>
</li>
</ul>
<p>위를 종합하면 shell에서 command line의 왼쪽에 보이는 <code>devryan@ubuntu-bionic:~$</code> 은 &quot; 내가 사용하고 있는 <code>서버이름(컴퓨터 이름)</code>은 <strong>ubuntu-bionic</strong>이고 <code>유저이름</code>은 <strong>devryan</strong>이다. 그리고, <code>현재 경로</code>는 <strong>해당 서버(컴퓨터)의 최상단(유저)디렉토리</strong>이며, <strong>입력을 받을 준비가 되어있다</strong>(<code>$</code>) &quot;라는 의미입니다.</p>
<h3 id="shell-commands">Shell Commands</h3>
<ul>
<li><p><code>pwd</code></p>
<blockquote>
<p>Print Working Directory</p>
</blockquote>
<p>  현재 경로를 절대경로로 보여줍니다.</p>
</li>
<li><p><code>ls</code></p>
<blockquote>
<p>List Segment</p>
</blockquote>
<ul>
<li><p>지금 위치(경로)에서 하위로 접근할 수 있는 디렉토리나 파일을 리스트로 출력해 줍니다.</p>
</li>
<li><p>shell에서 디렉토리 단위에서의 <code>.</code> 은 현재 디렉토리, <code>..</code> 은 상위디렉토리를 의미합니다.</p>
</li>
</ul>
</li>
<li><p><code>cd</code></p>
<blockquote>
<p>Change Directory</p>
</blockquote>
<pre><code> directory 단위로 경로를 변경할 수 있습니다. ( `cd ..` 상위 디렉토리로 이동)</code></pre></li>
<li><p><code>mkdir</code></p>
<blockquote>
<p>Make Directory</p>
</blockquote>
<p>  새 폴더를 생성합니다.</p>
</li>
<li><p><code>touch</code> 
파일을 생성합니다.</p>
</li>
<li><p><code>mv</code></p>
<blockquote>
<p>move</p>
</blockquote>
<ul>
<li>command 
  <code>$ mv {이동할 파일경로} {이동시킬 파일경로}</code> 
( <code>$ mv ../style.css .</code> )</li>
<li>기능<ul>
<li>파일을 이동시킵니다.</li>
<li>이름을 변경합니다.
<code>mv app.js main.js</code> mv 내부적인 동작으로 인해 이동 시키는 기능 뿐 아니라 app.js를 main.js라는 이름으로 변경이 가능합니다.</li>
</ul>
</li>
</ul>
</li>
<li><p><code>cp</code></p>
<blockquote>
<p>copy</p>
</blockquote>
<ul>
<li>command
<code>cp {원본 파일경로} {복사할 파일경로}</code>
( <code>cp app.js ../</code> )</li>
<li>기능<ul>
<li>파일을 복사합니다(사본 생성).</li>
<li>이름을 지정하지 않을 경우 원본파일의 이름을 그대로 사용합니다.</li>
</ul>
</li>
</ul>
</li>
<li><p><code>rm</code></p>
<blockquote>
<p>remove</p>
</blockquote>
<ul>
<li><p>command
 <code>rm {파일경로}</code></p>
</li>
<li><p>기능</p>
<ul>
<li>파일을 제거합니다</li>
<li>별도의 옵션(플래그)없이 파일이 아닌 디렉토리(폴더)를 삭제하려고 하면 에러가 발생합니다. (파일은 실제로 존재하지만, 디렉토리는 논리적으로 존재하기 때문입니다)</li>
<li><code>*</code>를 활용하면 조건에 맞는 파일을 매치해서 삭제할 수도 있습니다. 
( <code>rm server.*</code> 현재 경로에서 server라는 이름을 가진 모든 파일을 지움 )</li>
</ul>
</li>
<li><p>옵션</p>
<ul>
<li><p><code>-r</code></p>
<blockquote>
<p>recursive</p>
</blockquote>
<ul>
<li>command
<code>rm -r {디렉토리 이름}</code></li>
<li>디렉토리를 삭제하기 위한 옵션입니다.</li>
<li>재귀적으로 순회하면서 타겟이 되는 디렉토리 하위의 모든 디렉토리, 파일들을 지우고, 이후 타겟이 되는 디렉토리도 지웁니다.</li>
</ul>
</li>
<li><p><code>-rf</code></p>
<ul>
<li>command
<code>rm -rf</code></li>
<li><code>-r</code> 만으로도 충분히 동작하지만, <code>-r</code>에서 지울 때 경우에 따라 지울지 물어보고 진행할수 있으며, 물어보지 않고 강제로 모두 삭제하기를 원할 때 <code>-rf</code> 를 사용합니다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<ul>
<li><code>cat</code><ul>
<li>command
<code>cat {파일 경로}</code></li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/devrun_ryan/post/1329928a-f494-4b1a-b38d-1858bef2b758/image.png" alt="CLI_cat"></p>
<ul>
<li>텍스트 라인을 병합해서 화면에 출력합니다.</li>
<li>shell에서 파일의 텍스트(소스코드)가 올바르게 작성되었는지 확인하고자 할 때 사용할 수도 있습니다.</li>
</ul>
<h3 id="vim-commands">Vim commands</h3>
<ul>
<li><p>vim은 CLI에서 사용할 수 있는 대표적인 텍스트 에디터이며, 소스코드 작성으로도 활용할 수 있습니다. </p>
</li>
<li><p>mode를 기반으로 동작합니다.
그래서, 해당 기능을 사용하기 위해서는 해당하는 mode로 진입 후 사용해야 합니다.</p>
</li>
<li><p>commands</p>
<ul>
<li><p><code>vi {파일 경로}</code> 
해당 파일을 vim으로 open합니다.</p>
</li>
<li><p>vim 에디터</p>
<ul>
<li><p><code>esc</code></p>
<blockquote>
<p>escape</p>
</blockquote>
<p>normal 모드로 진입합니다.</p>
</li>
<li><p>normal 모드</p>
<ul>
<li><p><code>:</code> </p>
<ul>
<li><p>command 모드로 진입합니다.</p>
</li>
<li><p>하단에 표시되는 <code>:</code> 를 통해 command모드에 진입한 것을 확인할 수 있습니다.</p>
</li>
<li><p><code>i</code></p>
<blockquote>
<p>insert</p>
</blockquote>
<ul>
<li>수정 모드로 진입합니다.</li>
<li>하단에 표시되는 <code>-- INSERT --</code> 를 통해 insert 모드에 진입한 것을 확인할 수 있습니다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>command 모드</p>
<ul>
<li><code>w</code> 
작성한 내용을 저장합니다.</li>
<li><code>wq</code> 
작성한 내용을 저장하고 나갑니다(shell로 복귀).</li>
<li><code>q!</code> 
내용을 저장하지 않고 처음 상태를 유지합니다(override).</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>