<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>choi-ik.log</title>
        <link>https://velog.io/</link>
        <description>https://choi-ik.tistory.com/ 👈🏻  여기로 블로그 이전했습니다 ㅎ</description>
        <lastBuildDate>Mon, 19 Feb 2024 12:01:34 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>choi-ik.log</title>
            <url>https://velog.velcdn.com/images/choi-ik/profile/d6705541-517c-428c-a622-1e30f54953c5/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. choi-ik.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/choi-ik" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Compound Component Pattern 적용기]]></title>
            <link>https://velog.io/@choi-ik/Compound-Component-Pattern-%EC%A0%81%EC%9A%A9%EA%B8%B0</link>
            <guid>https://velog.io/@choi-ik/Compound-Component-Pattern-%EC%A0%81%EC%9A%A9%EA%B8%B0</guid>
            <pubDate>Mon, 19 Feb 2024 12:01:34 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이전에 구현했던 SNS 플랫폼 기반 익명/기명 롤링페이퍼 형식의 &quot;대박 사건&quot;을 리팩토링 하면서 아키텍처에 대한 아쉬움을 개선했던 경험을 작성한다.</p>
</blockquote>
<p>우선 내가 맡았던 기능중 편지 작성, 이전 편지 수정, 댓글, 등 다양한 부분에서 <code>Textarea</code>가 활용되고 있다. 빠르게 페이지를 구현하느라 다양한 부분에서 동일하게 사용되고 있는 <code>Textarea</code>를 공통 컴포넌트로 분리 할 생각을 하지 못했다. 그러다 보니, 코드의 중복이 잦았고, 불필요한 코드들이 이곳 저곳에서 동일하게 작성되어 있었다. </p>
<p>또한, 다른 분이 구현해주셨던 공통 컴포넌트인 <code>Input</code> 컴포넌트를 이곳 저곳에서 사용하는 것을 보며 나름의 문제점을 찾았는데, 하나의 <code>Input</code>이라는 공통 컴포넌트 안에 자식으로 들어가는 UI를 고정시켜버려 사용하는 입장에서 확장성이 떨어진다고 느꼈다.</p>
<p>위 문제점을 정리해보면</p>
<ol>
<li>중복되는 컴포넌트</li>
<li>고정된 UI로 인한 닫힌 확장성</li>
</ol>
<p>이 부분을 좀 더 사용하는 입장에서 편리하고, 코드를 읽는 사람으로 하여금 좀 더 가독성있게 구현해보자.</p>
<p>우선 내가 수정 할 컴포넌트는 다음 이미지와 같다.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/choi-ik/post/ae2f340d-4c33-426d-925a-83ed29c4bf84/image.png" /></th>
<th><img src="https://velog.velcdn.com/images/choi-ik/post/ec524a0a-40fa-46ab-b7ea-ce1d2e98696f/image.png" /></th>
<th><img src="https://velog.velcdn.com/images/choi-ik/post/deb15059-c9aa-46ed-937d-58e6dda7d6af/image.png" /></th>
</tr>
</thead>
</table>
<p>모두 같은 <code>Textarea</code>를 사용하고 있고 제목 밑의 <code>underLine</code>과 제목, 내용을 가지고 있고 첫번째 이미지만 버튼 두개를 추가적으로 더 가지고있다.</p>
<blockquote>
<p>그렇다면 설계단계에서 무엇을 고민해볼 수 있을까</p>
</blockquote>
<ul>
<li><p>일단 이 여러군데에서 공통적으로 사용되는 <code>Textarea</code>를 <code>common</code> 폴더로 분리하여 공통 컴포넌트로 구현할 수 있다.</p>
</li>
<li><p>어떤 페이지에서는 버튼 아이콘이 추가되어 사용되고, 어떤 페이지에서는 버튼 아이콘이 사용되지 않고 있고, 제목 없이 내용만 적을 수 있는 <code>Textarea</code>가 사용될 수도 있다.</p>
<ul>
<li>위 같은 상황에서는 공통 컴포넌트에서 모든 자식 컴포넌트(UI)를 고정시켜둔채 사용할 수 없게 되는데 어떻게 할 수 있을까?</li>
</ul>
</li>
</ul>
<p>위와 같은 고민을 했다면 <code>Textarea</code> 관련 여러 컴포넌트를 따로 분리해놓고 내가 사용하고 싶을 때 가져다가 붙혀넣는 식으로는 구현해 볼 수 없을까? 라고 생각 할 수 있다.</p>
<p>📌 <strong><em>이때 사용할 수 있는 패턴이 Compound Component Pattern(합성 컴포넌트 패턴)이다.</em></strong></p>
<h3 id="합성-컴포넌트란-무엇일까">합성 컴포넌트란 무엇일까?</h3>
<p><em>&quot;합성 컴포넌트란 소프트웨어 개발에서 재사용이 가능한 구성 요소를 만들기 위해 여러 개의 다른 컴포넌트를 조합하는 디자인 패턴. 복합적인 기능을 가진 큰 컴포넌트를 작은 단위의 컴포넌트들로 구성함으로써 코드의 <strong>유지보수성</strong>과 <strong>확장성</strong>을 높일 수 있다.&quot;</em></p>
<p>쉽게 말하면 자동차 &quot;튜닝과 비슷&quot;한 것 같다. 기본적으로 자동차라는 틀이 있을 때 우리는 미리 만들어 둔 타이어 휠을 좀 더 멋있는 휠로 갈아 끼울 수도 있고, 자동차 로고를 없애버릴 수도 있고, 소리가 크게나는 마후라를 달 수도 있다. 또는 자동차 규격에만 맞는다면 세상에는 없던 튜닝 부품을 만들어서 달 수도 있다.</p>
<p>이처럼 합성 컴포넌트도 하나의 틀을 두고 우리가 튜닝하고 싶은대로 끼워 맞추는 것이라고 보면 좋을 것 같다.</p>
<h3 id="최초-코드">최초 코드</h3>
<p>좀 부끄럽지만... 프로젝트 마감기한이 얼마 남지 않아 빠르게 구현하느라 공통 컴포넌트로 뺄 생각도 못하고 각각의 페이지에서 따로 구현했었다(그럴싸하게 써 놨지만 쉽게 표현하면 그냥 노가다다 ㅋㅋㅋ 머리가 멍청하면 몸이 고생한다는 아주 좋은 예시).</p>
<p>‼️ 아래 코드 자세히 안봐도 됩니다!! 그냥 노가다의 흔적일 뿐 어떻게 구현했나 안보셔도 됩니다 그냥 슥- 지나가세요!!</p>
<pre><code class="language-javascript">/** 편지 작성 페이지의 편지지 컴포넌트 */
function Letter({ darkMode, register, userName }: letterProps) {
  return (
    &lt;Style.LetterContainer darkMode={darkMode}&gt;
      &lt;Style.LetterTitle
        darkMode={darkMode}
        value={
          userName ? (userName === &#39;익명&#39; ? undefined : userName) : undefined
        }
        placeholder={
          userName
            ? userName === &#39;익명&#39;
              ? &#39;작성자명을 입력해주세요(최대 15자)&#39;
              : &#39;&#39;
            : &#39;작성자명을 입력해주세요(최대 15자)&#39;
        }
        maxLength={15}
        {...register(&#39;letterTitle&#39;, {
          required: &#39;작성자명은 반드시 입력해야합니다.&#39;
        })}
      /&gt;
      &lt;Style.TitleUnderLine /&gt;
      &lt;Style.LetterContent
        darkMode={darkMode}
        placeholder=&quot;내용을 입력하세요&quot;
        {...register(&#39;letterComment&#39;, {
          required: &#39;편지 내용은 반드시 입력해야합니다.&#39;
        })}
      /&gt;
    &lt;/Style.LetterContainer&gt;
  );
}

export default Letter;</code></pre>
<pre><code class="language-javascript">/** 받은 편지(작성자라면 수정 또는 삭제 가능) 컴포넌트 */
function PrePost({ userName, darkMode, postId, postDetail }: PrePostProps) {
  /** 이전 코드 많이 생략 */
  return (
    &lt;&gt;
      &lt;Style.PrePostAndCommentContainer&gt;
        &lt;Style.PrePostContainer darkMode={darkMode}&gt;
          &lt;Style.PrePostInnerTitle darkMode={darkMode}&gt;
            {postDetail &amp;&amp; JSON.parse(postDetail.title).title}
          &lt;/Style.PrePostInnerTitle&gt;
          &lt;Style.PrePostUnnerline /&gt;
          {postState ? (
            &lt;Style.PrePostEditContent
              darkMode={darkMode}
              defaultValue={postDetail &amp;&amp; JSON.parse(postDetail.title).content}
              {...register(&#39;prePostContent&#39;, {
                required: &#39;편지 내용은 반드시 입력해야 합니다.&#39;
              })}
            /&gt;
          ) : (
            &lt;Style.PrePostContent darkMode={darkMode}&gt;
              {postDetail &amp;&amp; JSON.parse(postDetail.title).content}
            &lt;/Style.PrePostContent&gt;
          )}
          {postState ? (
            &lt;Style.CompleteImg
              src={completeIcon}
              onClick={handleSubmit(onSubmit)}
            /&gt;
          ) : (
            &lt;Style.EditImg
              src={editIcon}
              onClick={() =&gt; {
                if (userName === &#39;익명&#39;) {
                  toast.error(&#39;익명 회원은 편지를 수정 할 수 없습니다.&#39;);
                  return;
                }
                handlePostToggleClick();
              }}
            /&gt;
          )}
          &lt;Style.DeleteImg
            src={deleteIcon}
            onClick={handleDeletePostClick}
          /&gt;
        &lt;/Style.PrePostContainer&gt;
        &lt;Style.LikeCommentContainer&gt;
          &lt;Style.LikeLogoContainer onClick={handleLikeCreateClick}&gt;
            &lt;Style.LikeLogo src={likeIcon} /&gt;
            &lt;Style.ListCount darkMode={darkMode}&gt;
              {postDetail?.likes.length}
            &lt;/Style.ListCount&gt;
          &lt;/Style.LikeLogoContainer&gt;
          &lt;Style.CommentCountText darkMode={darkMode}&gt;
            총{&#39; &#39;}
            &lt;Style.CommentCount&gt;
              {postDetail?.comments.length}개
            &lt;/Style.CommentCount&gt;
            의 댓글이 있습니다.
          &lt;/Style.CommentCountText&gt;
        &lt;/Style.LikeCommentContainer&gt;
        &lt;Style.PreCommentContainer&gt;
          {postDetail?.comments.map(
            ({ comment, _id, author }, idx) =&gt;
              titleAndCommentParsing(comment) &amp;&amp; (
                &lt;Style.PrePostComment
                  darkMode={darkMode}
                  key={idx}&gt;
                  &lt;Style.PrePostUserName
                    onClick={() =&gt; navigator(`/user/${author._id}`)}&gt;
                    {`💬 ${titleAndCommentParsing(comment).title}: `}
                  &lt;/Style.PrePostUserName&gt;
                  {titleAndCommentParsing(comment).comment}
                  &lt;Style.CommentDeleteImg
                    src={deleteIcon}
                    data-id={_id}
                    onClick={handleDeleteCommentClick}
                  /&gt;
                &lt;/Style.PrePostComment&gt;
              )
          )}
        &lt;/Style.PreCommentContainer&gt;
      &lt;/Style.PrePostAndCommentContainer&gt;
      &lt;Toaster
        toastOptions={{
          style: toastStyle,
          duration: 1000
        }}
      /&gt;
    &lt;/&gt;
  );
}</code></pre>
<pre><code class="language-javascript">/** 받은 편지에 대한 댓글 작성 컴포넌트 */
function Comment({ darkMode, register, userName }: CommentProps) {
  return (
    &lt;&gt;
      &lt;Style.CommentContainer darkMode={darkMode}&gt;
        &lt;Style.CommentTitleInput
          darkMode={darkMode}
          placeholder={userName ? &#39;&#39; : &#39;작성자명을 입력해주세요&#39;}
          value={userName ? userName : undefined}
          {...(userName === &#39;&#39;
            ? {
                ...register(&#39;commentTitle&#39;, {
                  required: &#39;작성자명은 반드시 입력하셔야합니다.&#39;
                })
              }
            : false)}
        /&gt;

        &lt;Style.CommentTitleUnderLine /&gt;
        &lt;Style.CommentContent
          darkMode={darkMode}
          placeholder=&quot;댓글을 입력하세요&quot;
          {...register(&#39;commentContent&#39;, {
            required: &#39;댓글 내용은 반드시 입력하셔야합니다.&#39;
          })}
        /&gt;
      &lt;/Style.CommentContainer&gt;
    &lt;/&gt;
  );
}</code></pre>
<p>이렇게 각 페이지에서 구현되었던 노가다 코드들을 공통 컴포넌트로 합성 컴포넌트 패턴을 적용하여 구현해보겠다!</p>
<h3 id="우선-textarea의-기본-뼈대가-될-코드부터-구현해보자">우선 Textarea의 기본 뼈대가 될 코드부터 구현해보자.</h3>
<p>React-hook-form, jotai 관련 코드는 집중해서 보지 않으셔도 됩니다.</p>
<pre><code class="language-javascript">import { ReactNode, createContext } from &#39;react&#39;;
import { FieldValues, Path, UseFormRegister } from &#39;react-hook-form&#39;;
import { useAtomValue } from &#39;jotai&#39;;
import { darkAtom } from &#39;@/store/theme&#39;;
import TextareaContent from &#39;./TextareaContent&#39;;
import TextareaTitle from &#39;./TextareaTitle&#39;;
import TextareaUnderLine from &#39;./TextareaUnderLine&#39;;
import * as Style from &#39;./index.style&#39;;

export const TextareaContext = createContext({
  darkMode: false
});

interface TextareaContainerProps {
  children: ReactNode;
  width: string;
  height: string;
}

export interface TextareaProps&lt;T extends FieldValues&gt; {
  value?: string;
  register?: UseFormRegister&lt;T&gt;;
  placeholder: string;
  maxLength?: number;
  formKey: Path&lt;T&gt;;
  width: string;
  height: string;
}

function Textarea({ children, width, height }: TextareaContainerProps) {
  const darkMode = useAtomValue(darkAtom);

  return (
    &lt;TextareaContext.Provider value={{ darkMode }}&gt;
      &lt;Style.TextareaContainer
        darkMode={darkMode}
        width={width}
        height={height}&gt;
        {children}
      &lt;/Style.TextareaContainer&gt;
    &lt;/TextareaContext.Provider&gt;
  );
}

Textarea.TextareaTitle = TextareaTitle;
Textarea.TextareaContent = TextareaContent;
Textarea.TextareaUnderLine = TextareaUnderLine;

export default Textarea;</code></pre>
<p>뼈대인 <code>TextareaContainer</code> 컴포넌트를 만들어주고, 제목, 내용, 밑줄 컴포넌트를 임포트해서 Textarea의 속성으로 구현해주었다. JS의 함수는 객체이기 때문에 객체처럼 사용할 수 있다.</p>
<p>제목, 내용, 밑줄 컴포넌트는 children에 들어갈 예정이다.</p>
<p>아래는 <code>Textarea</code>의 제목 부분에 해당하는 컴포넌트이다.</p>
<pre><code class="language-javascript">import { useContext } from &#39;react&#39;;
import { FieldValues } from &#39;react-hook-form&#39;;
import { TextareaContext, TextareaProps } from &#39;./index&#39;;
import * as Style from &#39;./index.style&#39;;

function TextareaTitle&lt;T extends FieldValues&gt;({
  value,
  register,
  placeholder,
  maxLength,
  formKey,
  width,
  height
}: TextareaProps&lt;T&gt;) {
  const { darkMode } = useContext(TextareaContext);
  return (
    &lt;Style.TextareaTitle
      darkMode={darkMode}
      value={value}
      placeholder={placeholder}
      maxLength={maxLength}
      width={width}
      height={height}
      {...(register &amp;&amp; {
        ...register(formKey, {
          required: &#39;작성자명은 반드시 입력해야합니다.&#39;
        })
      })}
    /&gt;
  );
}

export default TextareaTitle;</code></pre>
<p>다음은 <code>Textarea</code>의 내용에 관한 컴포넌트이다.</p>
<pre><code class="language-javascript">import { useContext } from &#39;react&#39;;
import { FieldValues } from &#39;react-hook-form&#39;;
import { TextareaContext, TextareaProps } from &#39;./index&#39;;
import * as Style from &#39;./index.style&#39;;

function TextareaContent&lt;T extends FieldValues&gt;({
  register,
  placeholder,
  formKey,
  width,
  height
}: TextareaProps&lt;T&gt;) {
  const { darkMode } = useContext(TextareaContext);

  return (
    &lt;Style.TextareaContent
      darkMode={darkMode}
      placeholder={placeholder}
      {...(register &amp;&amp; {
        ...register(formKey, {
          required: &#39;작성자명은 반드시 입력해야합니다.&#39;
        })
      })}
      width={width}
      height={height}
    /&gt;
  );
}

export default TextareaContent;</code></pre>
<p>추가로 밑줄 컴포넌트.</p>
<pre><code class="language-javascript">import * as Style from &#39;./index.style&#39;;

function TextareaUnderLine() {
  return &lt;Style.TextareaUnderLine /&gt;;
}

export default TextareaUnderLine;</code></pre>
<p>이제 구현한 TextareaTitle, TextareaContent, TextareaUnderLine을 
Textarea 컴포넌트의 chidren 부분에 잘 합성해주면 끝!</p>
<p>아래는 합성 컴포넌트를 적용한 부분이다.</p>
<pre><code class="language-javascript">/** 편지지 컴포넌트 */
 &lt;Textarea
          width={&#39;100%&#39;}
          height={&#39;20.3125rem&#39;}&gt;
          &lt;Textarea.TextareaTitle
            value={
              userName
                ? userName === &#39;익명&#39;
                  ? undefined
                  : userName
                : undefined
            }
            placeholder={
              userName
                ? userName === &#39;익명&#39;
                  ? &#39;작성자명을 입력해주세요(최대 15자)&#39;
                  : userName
                : &#39;작성자명을 입력해주세요(최대 15자)&#39;
            }
            maxLength={15}
            register={register}
            formKey={LETTER_TITLE}
            width={&#39;95%&#39;}
            height={&#39;40px&#39;}
          /&gt;
          &lt;Textarea.TextareaUnderLine /&gt;
          &lt;Textarea.TextareaContent
            placeholder={&#39;내용을 입력하세요&#39;}
            register={register}
            formKey={LETTER_CONTENT}
            width={&#39;95%&#39;}
            height={&#39;&#39;}
          /&gt;
        &lt;/Textarea&gt;</code></pre>
<p><code>Textarea</code> 의 자식 요소로(children) Textarea.TextareaTitle, Textarea.TextareaContent, Textarea.TextareaUnderLine이 들어가있다. 이 코드를 적용한 UI를 봐보자.</p>
<img src="https://velog.velcdn.com/images/choi-ik/post/7516f57a-a645-467e-8a09-f97a37d670c8/image.png" width="400px" />

<p>굉장히 잘 적용된 것을 볼 수 있다. 그렇다면 디자이너가 UnderLine이 마음에 안든다고 뺐으면 좋겠다고 요구했다고 쳤을때도 아주 쉽게 수정해볼 수 있다.</p>
<pre><code class="language-javascript">/** 편지지 컴포넌트 */
 &lt;Textarea
          width={&#39;100%&#39;}
          height={&#39;20.3125rem&#39;}&gt;
          &lt;Textarea.TextareaTitle
            value={
              userName
                ? userName === &#39;익명&#39;
                  ? undefined
                  : userName
                : undefined
            }
            placeholder={
              userName
                ? userName === &#39;익명&#39;
                  ? &#39;작성자명을 입력해주세요(최대 15자)&#39;
                  : userName
                : &#39;작성자명을 입력해주세요(최대 15자)&#39;
            }
            maxLength={15}
            register={register}
            formKey={LETTER_TITLE}
            width={&#39;95%&#39;}
            height={&#39;40px&#39;}
          /&gt;
          // 이 부분을 제거만 해주면 됨 &lt;Textarea.TextareaUnderLine /&gt;
          &lt;Textarea.TextareaContent
            placeholder={&#39;내용을 입력하세요&#39;}
            register={register}
            formKey={LETTER_CONTENT}
            width={&#39;95%&#39;}
            height={&#39;&#39;}
          /&gt;
        &lt;/Textarea&gt;</code></pre>
<p>밑줄을 삭제한 UI를 보면 아래와 같다.</p>
<img src="https://velog.velcdn.com/images/choi-ik/post/47f1aff4-d653-4faa-ae12-60fec72350f7/image.png" width="400px" />

<p>현재 코드에서는 버튼 컴포넌트를 구현하여 적용하지는 않았지만 지금까지 구현해놓은 흐름과 비슷하게 작성하면 된다. 각각의 기능 또는 UI를 가진 컴포넌트를 구현하고 사용하는 입장에서 자식요소로 넣거나 넣지 않거나 해주면 되는 것이다.</p>
<p>합성 컴포넌트를 사용하면 유연하게 UI를 변경할 수 있으며 Props Drilling을 줄일 수 있다는 장점도 있어 합성 컴포넌트는 여러 측면에서 장점을 가지고 있는 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데브코스 4차 한 달 회고]]></title>
            <link>https://velog.io/@choi-ik/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-4%EC%B0%A8-%ED%95%9C-%EB%8B%AC-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@choi-ik/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-4%EC%B0%A8-%ED%95%9C-%EB%8B%AC-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Tue, 23 Jan 2024 05:51:00 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>벌써 2차 팀원들과 함께 한 프로젝트를 시작하고 1달이 지나서 마무리 되었다. 막상 시작할 때는 한달이라는 기간이 길거라고 생각했는데 막상 시작하니 엄청 짧게 느껴졌다.</p>
<p>주어진 요구사항을 바탕으로 기획, 설계, 디자인, 구현, 배포까지 처음에는 여유있어 보였던 한달이 짧아져 보이기 시작했다. 내가 과연 팀장으로써 1인분을 할 수 있을까? 라는 마음이 앞섰고, 자신감보다는 두려운 마음으로 프로젝트를 시작했던 것 같다. </p>
<p>이젠 두려움으로 시작해 무사히 끝마쳤던 프로젝트를 돌아보자.</p>
</blockquote>
<h1 id="💌-떠나가는-이에게-마음을-전달해요프로젝트명-대박사건">💌 떠나가는 이에게 마음을 전달해요(프로젝트명: 대박사건)</h1>
<p><strong>만남이 있으면 헤어짐도 있는 법!</strong> 부트캠프, 동아리 등등 팀을 이루어 같은 목표를 향해 나아가는 조직이 존재한다. 하지만 조직의 특성상 언젠가 다른 팀으로 교체가 되거나, 수료 등 헤어짐을 마주하게 된다. 이때 떠나가는 이들에게 그동안 고마웠던 마음을 전달할 수 있는 서비스를 구현하고 싶었고, 요구사항인 <strong>&quot;소셜 네트워크 서비스 구현&quot;</strong> 바탕으로 구상하였다. 서비스명은 바로 <strong>&quot;대박사건&quot;</strong>.</p>
<h1 id="의사소통의-힘">의사소통의 힘</h1>
<p>우리 팀의 룰은 단 하나, <strong>&quot;어떠한 결정을 하든 우리끼리만 잘 알아 볼 수 있으면 된다&quot;</strong> 였다. </p>
<p>우리가 만든 프로젝트를 우리 팀이 아닌 다른 이에게 넘겨준다거나, 다른 이가 투입되어 함께 프로젝트를 진행 할 일은 없었기에 그런 미래 지향적인 고민은 접어두고, &quot;우리끼리 잘 알아볼 수 있는 룰을 정하여 프로젝트를 잘 만들어보자&quot;가 우리 팀 문화의 모토였던 것이다. 이를 바탕으로 우리 팀은 판단이 필요할 때 각자 의견을 내고 가장 타당한 의견을 채택하여 본인의 의견이 채택이 되지 않더라도 빠르게 인정하고 결단을 내려 진행을 하였다. 이로써 단 한 번의 트러블도 없었고, 서로를 존중해주는 마음이 너무나 잘 느껴졌다. 또한, 나를 포함한 팀원 모두가 실력이 비슷 하였지만, 각자 잘하는 부분과 잘 못하는 부분이 분명하게 존재 했고, 그 부분을 서로 의사소통을 통해 잘 채워준 느낌이든다.</p>
<p>결국 팀 프로젝트는 나 혼자 하는 것이 아닌, 팀원들과 함께 하는 것이며 함께 할 때 더욱 시너지가 나고, 그 시너지를 내기 위해선 원활한 의사소통이 필요하다는 것을 더욱 느끼게 되었다. 미래에 내가 취직을하면 이렇게 의사소통을 해야 될 때가 올텐데, 그때를 대비할 수 있는 의사소통 능력을 조금 가지게 된 것 같다.</p>
<p>내가 먼저 상대방을 편하게 생각하고 대해야 상대방도 나를 편하게 생각해주는 것 같다. 물론 처음 보는 사람에게 편하게 대하는 것은 쉽지 않다. 그래도 선을 넘지 않는 선에서 편하게 대하는 것이 정말 중요한 것 같다. 늘 열린 모습으로, 열린 마인드로 상대방을 대해야 상대방도 나에게 마음을 여는 것 같다. 물론 우리 팀원 분들의 성격이 너무너무 좋으셔서 가능 했을지도 모르지만 다음에 만날 팀원 분들에게도 시도 해볼거다.</p>
<blockquote>
<p>➕ 팀장이라고 조금 더 존중해주고 잘 따라준 팀원들에게 감사인사를 전합니다 🙇‍♂️</p>
</blockquote>
<h1 id="이번에도-기록-실패">이번에도 기록 실패</h1>
<p>우리는 프로젝트를 하면서 늘 문제를 해결한다. 그런데 정작 무슨 문제를 해결했냐 물으면 답하지 못한다. 왜? 기록하지 않았기 때문. </p>
<p>&quot;이번에는 프로젝트 시작하면 내가 겪은 문제와 이 문제를 해결 한 경험을 꼭 기록 해야지!&quot; 라고 마음을 먹었지만 멍청하게 또 실패. 생각보다 많은 Task를 맡게 되었고, 팀 스크럼 기한과 프로젝트 마감 기한은 빠듯한데 내가 맡은 기능 구현은 다 안됐고, 이로 인해 기록할 시간이 없었다. 가장 큰 이유는 내가 구현력이 저조하기 때문이겠지..ㅠ 내가 맡은 기능을 빠르게 구현했다면 조금이라도 기록할 시간이 생겼을텐데.. 부족함 투성이다. 구현력이 저조하다 치자, 그럼 맞닥뜨린 문제와 해결 경험을 키워드라도 적어놨어야 했는데 이 조차 나중가서 키워드를 본다 한들 뭐가 달라지겠냐 라고 생각하고 적지 않았는데 이 또한 오산인 것 같다. 그 당시 처럼 기억이 생생하지는 못하겠지만 내가 무슨 문제를 겪었고 무엇을 해결했는지 정도는 인지하고 관련되어 학습을 진행할 수 있는 것인데 이 부분은 아쉬운 부분인 것 같다.</p>
<p>일단 기억나는 부분들이랑 리팩토링 진행하면서라도 최대한 기록을 할 예정..!</p>
<h1 id="미흡했던-설계">미흡했던 설계</h1>
<p>기획 및 설계 단계에서 꼼꼼하지 못한 설계가 이후에 협업에 조금 악영향을 미친 것 같았다. 팀원들과 함께 나름대로의 설계는 했지만, 데이터 Flow 라든가 설계 단계에서 누락되었던 것들이 많았어서 그런지 프로젝트를 진행하면서 팀원들 서로 이게 맞는지 저게 맞는지 자꾸만 혼돈이 왔었다. </p>
<p>그래서 이후 진행 할 파이널 프로젝트에서는 기획부터 시작해서 유저시나리오를 그려가며 좀 더 꼼꼼하게 구현할 기능을 정의하고 페이지 또는 기능간 주고 받을 데이터 플로우에 대해 더욱 꼼꼼하게 설계 할 것이다. 초반에 설계를 어떻게 하느냐에 따라 이후 개발 시간이 정해지는 것 같다.</p>
<h1 id="너-제대로-알고-사용했니">너 제대로 알고 사용했니?</h1>
<p>우리 팀의 프로젝트 목표는 <strong>&quot;컴포트존에서 벗어나 현재 새로운 기술 중 많이 쓰이는 기술을 사용해보며 이 기술이 왜 많이 쓰이는지 직접 느껴보자&quot;</strong>이다.</p>
<p>우리 팀은 공통적으로 리액트, 리덕스 같은 주니어 개발자에게 국룰 같은 기술들만 사용했던 경험이 있었다. 그래서 TanStack Query, React Hook Form, Zod, Zotai, 등 한 번도 사용해보지 않았던 다양한 기술들을 도입하여 사용하였고, 각각의 기술 선택에는 이유가 있었다(이 부분은 따로 블로그에 작성 예정).</p>
<p>하지만 위 내용중 하나인 <strong>&quot;이번에도 기록 실패&quot;</strong> 부분에서 언급했듯이, 누군가 내게 너가 사용한 기술을 정확하게 왜 어떻게 쓰이는지 알고 사용한거야? 라고 물으면 아니라고 할 것 같다. 타이트한 스크럼 일정 및 프로젝트 마감 일정으로 인해 오히려 기능 구현에 급급했기 때문.</p>
<p>일단 도입한 이 기술들을 우리가 왜 사용하고 있고, 왜 자주 쓰이고, 각각의 기술들이 어떤 특성을 갖고 있는지는 안다. 하지만 제대로 사용했다고 말하기엔 어렵다. 각각의 기술들은 다양한 기능을 탑재하고 있고 그 다양하게 탑재된 기능들을 효율적으로 사용하지 못했다고 생각한다. 모든 기술들을 공식문서를 보면서 학습하고, 문제에 직면할 때마다 공식문서를 보면서 해결을 했지만, 기능 구현에 급급하다보니 사용했던 기술 내에서도 자주 사용하는 기능만 사용하게 되었던 것 같다. 이러한 부분이 내가 새로 도입한 기술들을 제대로 알고 썼냐고 물었을때 아니라고 대답하는 이유다. </p>
<p>하지만 이번 프로젝트는 거창하게 망하기로 한 프로젝트. 실제로 프로젝트가 망해버릴 정도로 제대로 하지 않겠다가 아니라, 새로운 기술을 많이 사용한 만큼 프로젝트의 완성도가 떨어질 것이기 떄문에 망하기로 한 프로젝트라고 명명한 것이다. 하지만 아직 파이널 프로젝트가 남았고, 프론트엔드 개발자를 하기로 한 이상 앞으로 내 인생에서는 수 없이 많은 프로젝트를 많이 진행할 것이다. 앞으로 남은 프로젝트를 위해 이번 프로젝트를 발판 삼겠다는 의미로 이번의 실패를 본보기로 삼아 다음 프로젝트에서는 더 잘 사용할 수 있을 것이라 예상한다.</p>
<h1 id="❓-그럼-난-무얼-얻었을까">❓ 그럼 난 무얼 얻었을까</h1>
<h2 id="공식문서를-읽는-방법">공식문서를 읽는 방법</h2>
<p>이번 프로젝트를 하면서 처음으로 공식문서를 정말 많이 읽었다. 이전까지는 남들이 사용했던 경험을 적어놓은 블로그를 보면서 내 프로젝트에 적용하고 그 코드를 긁어다가 그대로 사용하는 경우가 정말 많았는데, 이번 프로젝트에서는 그러지 않았다. </p>
<p>나만의 공식 문서를 읽는 방법으로는</p>
<blockquote>
<ol>
<li>이 기술이 왜 이 세상에 나왔고 사용되는지, 이 기술의 원천이 무엇인가</li>
<li>이 기술이 해결하고자 하는 것은 무엇인가?</li>
</ol>
</blockquote>
<p>1, 2를 이해했다면, 이때부터는 주로 가장 많이 사용되는 기능부터 읽어 본 뒤, 특정 상황에 기술의 특정 기능이 필요할 때마다 문서를 읽는 것이다.</p>
<p>처음엔 처음부터 끝까지 그냥 무작정 읽으려했다. 하지만 프로젝트 진행과 병행하기에는 시간적인 비용이 너무 많이 든다. 처음부터 끝까지 읽는 건 프로젝트를 하지 않을 때 해보고, 프로젝트를 진행하는 동안에는 이 기술의 원천, 이 기술이 해결하고자 하는 것은 무엇인지에 대한 이해를 한 후에, 필요할 때마다 공식문서 내의 정의된 기능들을 찾아보며 적용하는 것이 옳다고 생각했다. 나만의 방법이지만 나한텐 잘 맞는 것 같다.</p>
<p>이러한 방법으로 공식문서를 주로 읽고, 거의 블로그 글은 참고만하고 블로그에 내재된 코드는 거의 사용하지 않았던 것 같다. 내 머리속에서 나온 코드를 적어야 남에게 설명 가능하다고 생각했기 때문. 물론 블로그에 써져있는 코드들 복붙한뒤 이해하면 되지만 지금 학습을 하고 있는 단계에서는 그러고 싶지 않았다. 그래서 그런지 최적화를 위한 리팩토링을 진행해야 할 부분이 아주 많을것이라 예상이된다 ㅋㅋ</p>
<h2 id="타입스크립트에-대한-이해-및-적용">타입스크립트에 대한 이해 및 적용</h2>
<p>타이틀만 보면 내가 타입스크립트를 완벽하게 이해한 것 같지만 그건 전혀아니다. 그냥 어느정도 쬐끔 아주 쬐끔 깨달았다 정도..</p>
<p>타입스크립트를 사용해본 경험이 없어 늘 빨간줄 없애기에 급급했는데 이번엔 아니었다. 그렇다고 타입 시스템을 잘 사용했다고 말 할순 없지만 이전 보다는 확연히 나아진 느낌이다.</p>
<p>어떤점이 나아졌다고 느껴졌는지를 예로 들어보자면, 
<img src="https://velog.velcdn.com/images/choi-ik/post/7e7ca508-6118-448d-ab90-5b8c5a614101/image.png" alt="">
아래 코드는 get 메소드에 마우스 호버한 상태이다. 리턴 타입이 <code>Promise&lt;any&gt;</code>타입인 것을 볼 수 있다.
<img src="https://velog.velcdn.com/images/choi-ik/post/468da4ea-a7ee-45a5-a848-6fe36b4212f9/image.png" alt=""></p>
<p>위 코드를 보면 axiosInstance로 아무 제네릭도 꽂아주지 않았고, 이로 인해 axios를 사용하는 곳에서 빈번하게 빨간줄이 생겼었다. </p>
<p>하지만 이는 간단하게 해결 할 수 있는 문제였다. 이 인터페이스가 어떤 타입을 가지고 있는지를 확인하면 됬었는데 타입 스크립트에 대해 무지했던 나는 이 사실을 몰랐다. get 부분에 command+좌클릭으로 타고들어가서 다음 코드를 보자.</p>
<p><img src="https://velog.velcdn.com/images/choi-ik/post/a29522d8-7932-42a3-9164-ea0aeb978c01/image.png" alt=""></p>
<p>위 코드를 보면 get메소드가 어떤 타입을 받고 이쏙 어떤 타입을 리턴하는지 볼 수 있는데 이런식으로 인터페이스의 타입 형식을 쭉 읽어보면 어떤 타입을 꽂아야하는지 금방 알 수 있는 것이었다. </p>
<p><img src="https://velog.velcdn.com/images/choi-ik/post/11a3f204-0a51-4ea2-a61d-39ab69c7c6cf/image.png" alt="">
아래는 get 메소드에 마우스 호버한 상태이다. <code>Promise&lt;Message[]&gt;</code> 타입을 리턴해 주는 것을 알 수 있다.
<img src="https://velog.velcdn.com/images/choi-ik/post/53329425-3ee4-4a92-b7b5-2ead85a5d4ee/image.png" alt=""></p>
<p>아마 모든 분들이 알고 계실테지만 나는 이번에 처음 알았고 이 덕분에 타입 스크립트에 대한 두려움이 많이 사라졌다. 이전까지는 타입 에러를 고치기 바빴는데, 이제는 타입이 코드를 작성하는데 있어 도움을 주고 있는 하나의 도구가 되었다.</p>
<blockquote>
<p>이번 프로젝트를 통해 나를 되돌아보고 무엇이 부족했고, 무엇을 얻었는지 확연하게 꺠닫게 될 수 있는 좋은 경험이 되었다.</p>
<p>의사소통에 적극적으러 임해주고, 서로 부족한 부분을 채워주며 함께 성장할 수 있는 동력을 만들어준 팀원들에게 너무 감사하다.</p>
<p>결국 나 혼자서 하는게 아니라 &quot;함께&quot; 하는 것이기에 내가 팀에게 어떤 부분을 기여할 수 있는지 고민하고 장점은 극대화, 부족한 부분은 채워가는 형식으로 다음 프로젝트에도 좋은 영향을 끼칠 수 있는 그런 사람이 되고싶다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[데브코스 12월 MIL]]></title>
            <link>https://velog.io/@choi-ik/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-12%EC%9B%94-MIL</link>
            <guid>https://velog.io/@choi-ik/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-12%EC%9B%94-MIL</guid>
            <pubDate>Mon, 25 Dec 2023 08:09:33 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-3번째-회고">📌 3번째 회고</h1>
<p>벌써 데브코스를 시작한지 무려 3개월(반절)이 지났다.</p>
<p>특히 이번 한달은 지금까지 3달 중 가장 빨리 지나간 한 달이었다. 팀이 바뀌어 새롭게 만나는 팀원들과 새롭게 시작하는 달이기도 했고, 아주 많은 강의량 + 학습 해야 할 내용들이 많았던 것 같다.</p>
<p>유독 빨리 지나갔던 한 달, 나는 저번 달에 무엇을 했을까 한 번 돌아보자.</p>
<h1 id="📌-vue--react">📌 Vue / React</h1>
<h2 id="vue">Vue</h2>
<p>Vue, React, Angular 3대장 중 하나인 Vue를 배웠었다.</p>
<p>Vue를 이번 기회에 처음 사용해보게 되었고, 공식 문서가 친절하게 잘 나와 있어 강사님이 진행하시는 강의와 함께 보조 자료로 보기 너무나 편했다.</p>
<p>이전에 리액트로 간단한 개인 프로젝트도 했어서 그런지 Vue에서 사용하는 문법만 다르지 하는 역할을 어느정도 비슷하다 느껴졌다.</p>
<p>하지만 Vue를 사용한 영화 검색 페이지가 과제로 나왔고 막상 구현을 하려니 뜻대로 잘 되지가 않았었다.
더구나 처음 사용해보는 TS로 인해 과제가 더욱 어렵게 느껴졌고, 과제를 진행하며 타입 에러를 막기에 급급했다.</p>
<p>그래서 나는 이번 Vue 과제를 리액트와 TS에 더욱 친숙해지기 위한 발판으로 삼기로 생각했고, 기능을 구현하는 과정에서 Vue와 React, JS와 TS가 무엇이 다른지 고민하면서 진행했었다.</p>
<p>한가지 가장 큰 차이점을 느낀 것은 양방향 데이터 바인딩. 리액트 사뭇 다른 처리 방식이었다. 리액트의 단방향 데이터 방식과 달리 양방향 데이터 바인딩 방식을 사용하니 데이터 사용에 있어 조금 편리함을 느꼈다. 하지만 이 부분 외에는 리액트에 조금 더 적응이되어 있어서 그런지 Vue 문법들이 익숙하지가 않았다.</p>
<p>아래는 과제로 구현한 영화 검색 페이지의 메인 화면이다. 스타일링을 정말 못하는데 정말 정말 구리다 ㅋㅋ</p>
<p align='center'><img  src='https://velog.velcdn.com/images/choi-ik/post/c8c63238-9298-448b-b528-3e371dfd031e/image.png' width='600px'/></p>

<p><em>앞으로 다시 사용할 일이 있을까 싶은 Vue, 그래도 한 번도 안해본 것과 해본 것은 아주 많은 차이가 존재할 것이라 생각한다. 덕분에 리액트에 한 걸음 더 가까워질 수 있었던 것 같다.</em></p>
<h2 id="react">React</h2>
<p>대망의 리액트.</p>
<p>이번 강의는 대부분 짧고 굵게 구성이 되어 있었다. 강의가 짧고 굵다보니 강의 외적으로 찾아보게 되는 것이 정말 많았다. 밥상은 차려 주셨으니 떠 먹는 건 내가 직접 해야지.</p>
<p>이번 강의들은 동작 원리에 대해 집중을 많이 했던 것 같다. 이전에도 사용했던 경험이 있었으니 강의에서 나온 컴포넌트와 Hook들이 어떻게 동작을 하는지 많이 찾아봤던 것 같다. 하나하나 다 기억이 나는 것은 아니지만 떠오르지 않을 때마다 다시 또 찾다보면 완전히 내 것이 될 수 있을 것 같다.</p>
<p>그렇게 수 많은 리액트 강의들을 들으며 과제가 시작되었다.</p>
<p>View 네비게이션과 툴팁 둘 중 하나를 2개 이상의 애니메이션과 함께 구현 하는 것이었다.</p>
<p>내심 오 이정도면 쉽겠는데? 라고 생각했지만 막상 또 구현을 들어가니 어려웠다. 머리속에 떠오르는대로 진행이 되지 않았다. 이유는 익숙하지 않은 TS + 애니메이션 구현이 주를 이루었다.</p>
<p>이번 과제에서는 어떻게 하면 추상화를 잘 할 수 있을까를 정말 많이 고민하며 과제를 진행했지만 결과물은 그리 좋지 못했던 것 같다. 타입 조차도 추상화를 잘 하지 못했기 때문.</p>
<p>하지만 항상 컴포트존에만 머무를 순 없기에 익숙하지 않은 것들을 하나 하나 내 것으로 만들어가기 위한 노력을 해야지✌️</p>
<h1 id="📌-멘토님과의-커피챗">📌 멘토님과의 커피챗</h1>
<p>이번 멘토님께서는 우리 팀원들에게 왜? 라는 질문을 되게 많이 하신다. 이로부터 시작하여 커피챗때 팀원들과 토론도 진행 시켜주신다. </p>
<p>모든 것엔 정답이 없고 나보다 더 잘하고 잘 아는 사람이 말하는 것도 항상 의심하라 하셨는데 처음에는 이해가 가지 않았다.</p>
<p>아니 나보다 더 많이 알고 있는 사람의 말을 의심하라는게 말이 되는건가..? 라는 생각이 들었지만 최근 멘토님과의 모각코때 멘토님과의 대화를 통해 이 말의 의미를 알게되었다.</p>
<p><strong><em>&#39;익님 보다 더 잘하고 잘 아는 다른 사람이 하는 말에 무조건 동의하면 익님의 가치관은 그 사람의 가치관으로 덮이게 돼요&#39;</em></strong> 라는 말씀을 해주셨는데 이 말이 너무 확 와 닿았다.</p>
<p>자기 주관이 뚜렷하지 않은 사람은 어떠한 사람으로도 대체가 될 수 있을 거란 생각이 들었다(시키는 대로만 하기 때문). 또한 뚜렷한 자기 주관이 있더라도 남을 설득하지 못한다면 그 주관은 그냥 망상이 되버릴 수 있다.</p>
<p>모든 것엔 정답은 없다. 다만 정답으로 만들려면 남에게 내가 생각한 것이 정답이라는 걸 설득해야 한다는걸 커피챗 때도, 모각코때도 심어주셨다.</p>
<p>남에게 내가 생각한 것이 정답이라는 걸 설득하기 위해서는 내가 생각하는 것에 대해 정확하게 알고 있어야 하며 왜 이게 정답인지 왜?라는 것에 집중해야 한다는 걸 더욱 느꼈다. </p>
<p>앞으로는 무엇을 사용하든 무엇을 공부하든 내가 이걸 왜 공부하며 이걸을 왜 사용하고 이걸 왜 공부하고 사용해야만 하는지 나 스스로가 질문을 던지고 그 질문에 답할 수 있는 사람이 된다면 내 가치는 올라갈거라 생각이 된다. 그 동안 이렇게 살아오지 않았기에 많이 어렵겠지만 지금부터 반복하다보면 충분히 할 수 있을거라 생각한다.</p>
<p>조언을 돌려서 해주시는 것 보다 팩트로 꽂아주시는 것을 좋아하는데, 팩트로 조언해주시는 멘토님 감사합니다 🙏</p>
<h1 id="📌-추상화에-대하여">📌 추상화에 대하여</h1>
<p>이번 한달 중 나는 가장 기억에 남는 것은 멘토님이 설명해주신 추상화다.</p>
<p>추상화란 일반적으로 구체적인 사물간의 공통점을 취하고 차이점을 버리는 것을 의미한다고 한다. 나도 이렇게 생각했다.</p>
<p>하지만 멘토님의 접근 방식은 달랐다.</p>
<p>복잡한 것을 버리고 단순함을 표현하는 것.</p>
<p>이 글에서 내가 보고 듣고 배운걸 설명하기엔 너무 내용이 길어질 것 같아 힘들지만, 내가 느낀 점을 짧고 굵게 말 한다면, 숲을 보는게 아니라 나무만 보는 것이다.</p>
<p>숲을 보고 숲에 존재하는 모든 것들이 어떠한 공통점이 있고 이들을 어떻게 엮을 수 있을 까 고민하는 것 보다, 나무만 보고 이 나무가 하는 아주 아주 단순한 역할에 집중하는 것이다. 그렇게 단순한 역할에 집중하다보면 그 단순한 것들이 모여 복잡한 것이 되고 그 복잡한 것이 모여 하나의 숲이 되는 것이었다.</p>
<p>이번 리액트 과제에서 멘토님이 도움을 주셔서 내가 느낀 것을 코드에 직접 적용해볼 수 있었고 그로인해 더욱 감이 잡힌 것 같다.</p>
<p>추상화는 공식문서도 없고 정답이 없는 문제다. 이는 나 스스로 어떻게 해야 더 좋은 방향으로 설계를 할 수 있을지 고민해야 하는 방법 밖에 없다. 앞으로 있을 프로젝트에서 끊임없이 고민하여 추상화 필요한 부분을 추상화 시키는데 집중해 볼 예정이다.</p>
<h1 id="📌-어색한-팀장">📌 어색한 팀장</h1>
<p>이번 팀에서 팀장을 맡았다. 이번에 만난 팀원들이 모두 편한 분위기 속에서 하고 싶은 말이 있다면 당당하게 하고, 아니라고 생각하는 것에 아니라고 말을하는 그런 편한 분위기를 만들고 싶었다.</p>
<p>이런 팀을 만들기 위해 나름 초반에 빌드업을 열심히 했는데 지금은 내가 원하는 그런 분위기가 된 것 같아서 너무 좋다. </p>
<p>다들 다른 사람의 의견에 묻어가는 것이 없고, 각자 맡은 역할에 최선을 다하고 이제는 내가 주제를 던지지 않아도 다들 먼저 주제를 던져주신다. 또 이제는 내가 주제를 던지면 누군가를 지목하지 않아도 본인들이 먼저 대답 하시느라 오디오가 겹칠 정도다.</p>
<p>물론 내가 잘한 건 아니다. 나는 늘 그냥 신나게 떠든 것 뿐 내가 떠드는 주제를 잘 받아쳐주는 팀원들의 몫이 크다. </p>
<p>팀원들이 내게 호응을 잘 해주지 않거나, 별 의욕이 없거나, 내가 지목해서 물어야만 대답 해주었다면 많이 힘들었을 것 같다.</p>
<p>개발자는 단거리 선수나 개인 종목들(유도, 복싱 등등) 나 하나가 잘한다고 해서 잘 할 수 있는게 아니다. 우리나라 축구에서 손흥민이 축구를 가장 잘하지만, 손흥민이 잘한다고 우리나라가 늘 이기는가? 그렇지 않다.</p>
<p>각자 성장에 대한 노력을 멈추지 않고, 같은 목표를 가지고 있는 집단이라면 하나가 되어 원활한 의사소통이 좋은 결과물을 만드는데 있어 가장 큰 역할을 할 것이라 생각이된다.</p>
<p>늘 호응을 잘 해주시는 우리 팀원분들에게 감사하다.</p>
<p>화이팅합시다 동욱팀 👍🏻</p>
<h1 style='color: blue'>👍🏻 Keep</h1>

<ul>
<li>최근 한달 간 과제를 구현하면서 구현에 급급하지 않고 추상화에 신경쓰며 많은 고민을 하며 코드를 작성하는 습관을 들이고 있는 것.<ul>
<li>아직은 아무리 고민해도 좋은 결과가 나오지는 않지만, 계속해서 실패하고 해결 방안을 제시 받고를 무한 반복하다 보면 어느샌가 나도 추상화를 잘 할 수 있지 않을까란 기대감을 갖고 있다.</li>
</ul>
</li>
</ul>
<ul>
<li>동작원리에 집중하는 것.<ul>
<li>배운 모든 내용의 동작 원리를 이해하고 조사한 것은 아니지만 잘 몰랐던 부분, 문제에 직면한 부분에 대해서는 동작 원리를 이해하려고 노력하였는데 이러한 노력이 쌓이다 보니 해결 방안을 찾아나가는데 조금 시간을 줄일 수 있었다.</li>
</ul>
</li>
</ul>
<h1 style='color: red'>❗️ Problem</h1>

<ul>
<li>조금 안다 싶으면 빠르게 보고 넘어가는 습관</li>
<li>구현력</li>
</ul>
<h1 style='color: green'>🚀 Try</h1>

<ul>
<li><p>조금 안다 싶으면 빠르게 보고 넘어가는 습관이 있는데 내가 남에게 설명할 수 없는 지식은 내 것이 아닌 것이라 생각하며 문제에 직면했을 때 한번 더 살펴보고 깊게 학습하며 해결 방안이 무엇인지 이 해결 방안은 왜 이러한 방법이 사용이 되었으며 무엇을 해결하기 위해 등장하게 되었는지 꼬리에 꼬리를 무는 학습 습관을 다시 가지기.</p>
</li>
<li><p>최근 구현력이 많이 부족하다 느꼈다. 데브코스에 오기 전과 데브코스 시작하고 한달 뒤 부터도 코딩테스트에 조금 집중을 했었는데 지금은 코딩테스트에 집중할 때가 아닌 것 같다. 내가 부족한 기술과 구현 능력을 채우기 위해 많은 기술을 습득하고 여러가지 많은 구현을 위한 시도를 해보며 이유 있는 코드를 짜는 연습을 해야할 것 같다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue의 리스트 렌더링]]></title>
            <link>https://velog.io/@choi-ik/Vue%EC%9D%98-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EB%A0%8C%EB%8D%94%EB%A7%81</link>
            <guid>https://velog.io/@choi-ik/Vue%EC%9D%98-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EB%A0%8C%EB%8D%94%EB%A7%81</guid>
            <pubDate>Wed, 22 Nov 2023 05:37:49 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-vue의-배열-변경-감지">📌 Vue의 배열 변경 감지</h1>
<p>JavaScript에서 우리는 아래의 메소드를 통해 원본 배열을 직접 변경할 수 있다.</p>
<blockquote>
<ul>
<li>push()</li>
</ul>
</blockquote>
<ul>
<li>pop()</li>
<li>shift()</li>
<li>unshift()</li>
<li>splice()</li>
<li>sort()</li>
<li>reverse()</li>
</ul>
<p>위 메소드에 비해 원본 배열은 변경하지 않지만 새로운 배열을 변경후 반환하는 메소드들이 있다.</p>
<blockquote>
<ul>
<li>filter()</li>
</ul>
</blockquote>
<ul>
<li>map()</li>
<li>slice()</li>
<li>concat()</li>
</ul>
<p>새로운 배열을 변경후 반환하는 메소드를 사용하는 예를 보자.</p>
<pre><code class="language-js">el.items = el.items(filter(item =&gt; item.msg.match(/Foo/));</code></pre>
<p>filter 메소드를 사용하여 &#39;Foo&#39;와 매칭되는 문자열만 새로운 배열로 반환하여 el.items에 넣어준다.</p>
<p>그렇다면 기존에 el.items에 있던 배열 데이터를 연결한 DOM을 버리고 새로 반환 받은 배열로 다시 렌더링 할 것이라고 생각할 수 있다.</p>
<p><strong>하지만 Vue는 DOM 요소 재사용을 최적화하기 위해 기존에 사용하던 배열 데이터에서 변경된 배열 데이터의 차이점에 해당하는 내용만 다시 화면에 출력하는 방식을 사용하고 있다(Smart Heuristics).</strong></p>
<p>어떻게 보면 리액트가 가상 돔을 사용해 기존 돔과 비교하여 다른 부분만 렌더링 해주는 것과 비슷한 내용이라고 볼 수 있을까? 라는 의문이 든다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데브코스 2차 MIL]]></title>
            <link>https://velog.io/@choi-ik/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-2%EC%B0%A8-MIL</link>
            <guid>https://velog.io/@choi-ik/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-2%EC%B0%A8-MIL</guid>
            <pubDate>Tue, 21 Nov 2023 10:57:33 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-데브코스-2차-mil">📌 데브코스 2차 MIL</h1>
<h3 id="나는-한-달-동안-무엇을-했을까❓">나는 한 달 동안 무엇을 했을까❓</h3>
<blockquote>
<p><strong>1. 노션 클로닝</strong>
<strong>2. 유튜브 클로닝(HTML+CSS(SCSS))</strong></p>
</blockquote>
<p>큼지막한 주제로 위 두 가지 정도의 학습을 한 것 같다.</p>
<h2 id="🖥️-노션-클로닝">🖥️ 노션 클로닝</h2>
<p>처음 사용해 본 VanillaJS와 이를 이용한 노션 클로닝 프로젝트. 프로젝트 목표는 VanillaJS를 사용한 SPA 이해와 컴포넌트 추상화, 단방향 데이터 흐름 설계였다.</p>
<p>데브코스에 오기 전까지 프론트엔드 기술을 독학하면서 <code>React</code>와 <code>Redux</code>같은 라이브러리를 사용했었는데 이러한 라이브러리 없이 쌩구현 하는것이 굉장히 어색했다.(그렇다고 React와 Redux를 잘 다루는 건 아니다 ㅎㅎ)</p>
<p>그래도 데이터 흐름 설계와 컴포넌트 추상화 같은 설계가 모두 끝난 후에 코드를 작성하기 시작했다. <strong>설계 과정에서 한 가지 아쉬웠던 건 단방향 데이터 흐름을 잘못 설계한 것</strong>. 모든 데이터는 최상단 <code>App.js</code>의 <code>State</code>에서 관리가 되고 있었고 모든 데이터는 최상단의 <code>App.js</code>의 <code>State</code>로 흘러 들어와 최상단에서 다시 데이터를 하위 컴포넌트로 내려주었던 것. 
이런식의 데이터 흐름을 짰더니 컴포넌트 단위로 데이터가 바뀔 때 렌더링이 되는 것이 아니라 어느 한 곳이 바뀌면 전체 모든 컴포넌트가 바뀌게 되었던 것이다. 이로 인해 마크업 기능이 실시간으로 적용되는 것이 아닌 새로고침을 눌러야 적용이 되는 가슴아픈 오류가 있었는데, 리액트 할 때처럼 컴포넌트 단위로 렌더링 시켜줄 것을 생각하지 못하고, 왜 데이터 흐름에만 집중을 한건지 모르겠다 💦. 그래도 덕분에 데이터 관련 디버깅 하는 시간은 전체 코드를 작성하는데 있어서 <code>1~5%</code>정도 밖에 안된 것 같다.</p>
<p>그리고 항상 <code>React</code>를 사용하여 API 통신을 할 일이 있다면 항상 <code>Axios</code> 라이브러리를 사용 했었는데 이번 노션 클로닝을 통해서 <code>Axios</code>와 유사하지만 조금 차이점이 있는 <code>fetch API</code>에 대해 이해하게 되었고, 비동기 로직을 작성하기 위한 <code>Promise, async/await</code>에 대해서도 더욱 이해할 수 있게 되었다. 또한, VanillaJS로 노션 클로닝을 진행하면서 <code>디바운스</code>에 대한 개념과 <code>SPA</code>의 개념이 머리속에 더 확립이 된 것 같다.</p>
<h2 id="🖥️-유튜브-클로닝html--cssscss">🖥️ 유튜브 클로닝(HTML + CSS(SCSS))</h2>
<p>나는 CSS에 가장 자신이 없었다. 다른 것도 다 잘 못하지만 못하는 것 중 CSS를 제일 못한다.. 지금까지 진행한 개인 프로젝트들은 아주 쉬운 레이아웃만 그려서 CSS에 대한 지식이 거의 전무했다. 프론트엔드 개발자를 하겠다면서 CSS를 잘 못하는 아이러니한 상황이다;;</p>
<p>하지만 프론트엔드 개발자와 CSS는 뗄 수 없는 사이. 강사님의 SCSS강의를 듣고 HTML과 SCSS로 유튜브를 클론하기 시작했다. SCSS 강의에서 수많은 SCSS 기능을 배웠지만 나처럼 잘 활용하지 못한 사람은 아마 없을거다. 형식적으로 분리한 <code>@mixin</code>과 <code>@media</code>, 레이아웃을 네 가지 정도의 영역으로 나눠 파일 분리 요정도가 처음 CSS로 각잡고 레이아웃을 그려보는 내가 할 수 있는 최선이었다. </p>
<p>이번 유튜브 클로닝을 진행하면서 모든 레이아웃을 <code>Flex</code>로 그렸는데 <code>Grid</code>를 사용해보지 못한 것이 아쉽고,<code>@media</code> 쿼리를 이용해 반응형을 적용하긴 했지만 주먹구구식으로 적용을 한 것 같다는 느낌이드는데 이 부분도 아쉬운 느낌이 많이 든다. 또한, 구현하기 급급해서 <code>BEM 방법론</code>과 <code>시멘틱 태그</code>를 잘 적용하지 못했는데, 이 부분도 추후 진행할 토이 프로젝트에서 적용해서 아쉽고 부족했던 부분들을 채워나갈 계획이다. </p>
<p>정말이지 CSS 역량이 많이 부족함을 느낀다. 박영웅 강사님의 CSS 강의를 기반으로 다른 강의도 들어보며 혼자서 물 흐르듯 레이아웃을 그리며 반응형을 적용할 수 있게 CSS 역량을 아주 많이 채워야할 것으로 보인다 💦.</p>
<h3 id="💬-멘토님과의-면담">💬 멘토님과의 면담</h3>
<p>1차 팀 기간 마지막 주 멘토님과 팀원들과 낙성대에서 모각코를 진행했다. 이 날 멘토님과 면담을 진행했는데 이력서에 관련한 내용과 블로그에 관련한 내용을 피드백을 해주셨다. </p>
<p>가장 중요한 건 지금 취업을 준비하는 개발자들 누구나 다 열심히 한다. 열심히는 Default로 다른 사람과의 차별점이 필요한데 내가 봐도 나에겐 특별한 차별점이 없다. 또한, 개발 공부 만큼이나 중요한 것이 글로써 나를 표현하고 내가 학습하고 느낀 것을 글로써 잘 표현하는 것이 중요하다고 생각한다. 그래서 나는 차별점을 글로써 나타내 보려 하는데 글을 잘 못 쓰겠다 망할... ㅋㅋㅋ 내 치명적인 단점이 글을 잘 못쓰는 건데.. 어떻게 글로써 나를 표현하지 라는 생각이 지금 글을 현 시점에도 내 뇌를 지배하고있다 💣 </p>
<p>지금 당장 글을 잘 쓰는 것은 불가능하고, 일단 블로그에 글을 쓰기 위해 무언가를 계속 해야겠다. 주로 문제 해결 과정 또는 2뎁스 이상 학습한 내용에 대해 쓰고 싶은데 그럴려면 사이드 프로젝트를 하든 무언가를 학습하든 해야겠지? 그래서 앞으로는 코딩테스트 문제를 푼 과정이나 학습 내용(2뎁스 이상), 또는 사이드 프로젝트를 진행하면서 부딪히는 상황들에 대해 블로그를 작성하려한다.</p>
<h3 id="💻-사이드-프로젝트">💻 사이드 프로젝트</h3>
<p>사이드 프로젝트를 진행하려 하는데 너무너무 고민이다. 프론트엔드 개발자는 현 트렌드에 능동적으로 따라갈 줄 알아야 하는데 그렇다고 Next.js를 사용하자니 한 번도 써 본적이 없다. React를 그나마 가장 잘 쓸 수 있지만 이것 또한 현업 개발자 분들이 보기엔 그냥 애기 수준일 것 같다. 일단 TS만 고정으로 하고 나머지 기술은 어떤 것을 사용할지는 제쳐두고, 프로젝트 주제에 대해서고 고민이 된다. 크롬 확장프로그램에 관련한 프로젝트를 진행할 지, 공공데이터 API를 사용해 간단한 토이 프로젝트를 진행할 지는 고민이 되는데.. 빨리 정해서 여러 기술들을 적용해 보고싶다 ㅎ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트의 this]]></title>
            <link>https://velog.io/@choi-ik/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-this</link>
            <guid>https://velog.io/@choi-ik/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-this</guid>
            <pubDate>Thu, 02 Nov 2023 12:39:42 GMT</pubDate>
            <description><![CDATA[<h1 id="자바스크립트의-this란">자바스크립트의 this란?</h1>
<blockquote>
<p><strong>객체</strong>는 상태를 의미하는 <code>프로퍼티</code>와 동작을 의미하는 <code>메서드</code>로 이루어져 있으며, 메서드는 객체의 상태를 참조 및 변경할 수 있어야합니다.</p>
</blockquote>
<h4 id="그렇다면-객체에서-자주-보이고-쓰이는-this란-무엇일까">그렇다면 객체에서 자주 보이고 쓰이는 this란 무엇일까?</h4>
<blockquote>
<p><strong>this</strong>란 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기참조 변수입니다. 즉, 자신이 속한 객체, 생성할 인스턴스의 프로퍼티 or 메소드를 참조하며 <strong>this는 함수 호출에 의해 동적으로 결정됩니다.</strong></p>
</blockquote>
<h4 id="📢-함수의-호출-방식으로는">📢 함수의 호출 방식으로는</h4>
<ol>
<li><p>일반 함수 호출</p>
</li>
<li><p>메서드 호출</p>
</li>
<li><p>생성자 함수 호출</p>
</li>
<li><p>apply, call, bind 함수에 의한 호출</p>
</li>
</ol>
<h4 id="총-4가지-방법이-있으며">총 4가지 방법이 있으며,</h4>
<p>일반 함수로 호출한 this는 전역 객체를 가리키고, 콜백 함수도 전역 객체를 가리키게되고</p>
<p>메소드 함수의 내부 함수를 호출한다면 this가 전역객체를 가리키며, 메소드 함수를 호출한다면 메소드 함수의 this는 호출한 객체를 가리키게되고,</p>
<p>생성자 함수를 호출하면 this는 해당 함수를 가리키게되며,</p>
<p>aplly, call, bind는 인수로 전달한 객체를 호출당한 함수의 this가 가리키게됩니다.</p>
<p>📌 여기까지는 일단 가볍게 알아보고, 이어서 <strong>bind</strong>에 대해 한번 자세하게 알아보자!</p>
<p>먼자 bind에 대해 알아보기전 밑의 코드를 보자.</p>
<pre><code class="language-js">const obj = {
  someProperty: &quot;Hello, World!&quot;,
  count: function () {
    console.log(this.someProperty);

    const nestedFunction1 = function () {
      console.log(this.someProperty);

      const nestedFunction2 = function () {
        console.log(this.someProperty);

        const nestedFunction3 = function () {
          console.log(this.someProperty);

          const nestedFunction4 = function () {
            console.log(this.someProperty);

            const nestedFunction5 = function () {
              console.log(this.someProperty);
            };
            nestedFunction5();
          };
          nestedFunction4();
        };
        nestedFunction3();
      };
      nestedFunction2();
    };
    nestedFunction1();
  },
};

obj.count();</code></pre>
<p>위 코드를 실행하면 첫번째 출력만 someProperty를 가리켜 &quot;Hello World&quot;를 출력하게 되고 나머지는 undefined를 출력하게 된다.(undefined를 출력하게되는 이유는 메소드내의 함수를 일반 함수로 호출했기 때문)</p>
<p>그렇다면 메소드 내부의 함수들이 obj의 프로퍼티인 someProperty를 가리키게 하려면 어떻게 해야할까?</p>
<p>방법은 다음과 같다.</p>
<pre><code class="language-js">const obj = {
  someProperty: &quot;Hello, World!&quot;,
  count: function () {
    console.log(this.someProperty);

    const nestedFunction1 = function () {
      console.log(this.someProperty);

      const nestedFunction2 = function () {
        console.log(this.someProperty);

        const nestedFunction3 = function () {
          console.log(this.someProperty);

          const nestedFunction4 = function () {
            console.log(this.someProperty);

            const nestedFunction5 = function () {
              console.log(this.someProperty);
            };
            nestedFunction5.bind(this)().;
          };
          nestedFunction4.bind(this)();
        };
        nestedFunction3.bind(this)();
      };
      nestedFunction2.bind(this)();
    };
    nestedFunction1.bind(this)();
  },
};

obj.count();</code></pre>
<p>위 코드를 실행하면 모든 출력이 &quot;Hello World!&quot;가 나온다. bind 함수가 전달해준 this가 대체 뭐길래 모두 같은 obj의 someProperty를 가리키게 되는 걸까?</p>
<p>혹시 bind 함수가 바인딩 시켜준 this가 그냥 당연하게 obj를 가리키게 된다고 생각하는 분들이 계실 수 있다.</p>
<p>물론 결론적으로는 모든 this들이 obj를 가리켜 같은 출력값이 나오기는하지만.. 한번 더 고민해볼 필요가 있다. 이는 어떻게 보면 당연한게 아닐수도.. ㅎ</p>
<blockquote>
<p><strong>우선 bind함수의 this는 바인드가 실행되는 시점을 가리킨다.</strong> </p>
</blockquote>
<p>즉, nestedFunction1에 바인딩된 this는 count 함수 블록에서 생성되었기 때문에 메소드 함수로 생성된 count가 가리키는 this는 obj이기 때문에 바인딩된 this도 obj를 가리키게된다.</p>
<blockquote>
<p><strong>그렇다면 nestedFunction2에 바인딩된 this는..?</strong></p>
</blockquote>
<p>이때 생성된 this는 nestedFunction1 함수 블록에서 생성이 되었고, nestedFunction1 함수의 this가 가리키는 곳은 count를 가리키고, count의 this는 obj를 가리키기 때문에 체이닝이되어 nestedFunction2 함수의 this도 obj를 가리키게된다.</p>
<p>이렇게 나머지 일반함수들에 바인딩된 this도 체이닝이되어 결국 같은 곳인 obj를 바라보게 되는 것!</p>
<p>*<em>당연하게 생각할 수 있는 부분이지만 파보면 결국 원리가 있었다는..! *</em></p>
<p>앞으로도 공부를 하면서 의구심이 드는 부분이 있다면 계속 파고 들어봐야겠다. 그리고 오늘 데브코스 석주님의 발표를 보면서 꾸준하게 학습한 내용을 기록해야겠다는 생각이 들었고, 단순 기록이 아닌 왜?라는 의구심과 2뎁스까지 고민한 흔적, 문제 상황과 해결과정에 대한 기록을 열심히 그리고 &#39;잘&#39; 해야겠다고 느꼈다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[노션 클로닝 기능 회고]]></title>
            <link>https://velog.io/@choi-ik/%EB%85%B8%EC%85%98-%ED%81%B4%EB%A1%9C%EB%8B%9D-%EA%B8%B0%EB%8A%A5-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@choi-ik/%EB%85%B8%EC%85%98-%ED%81%B4%EB%A1%9C%EB%8B%9D-%EA%B8%B0%EB%8A%A5-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Tue, 31 Oct 2023 15:21:06 GMT</pubDate>
            <description><![CDATA[<h1 id="자동-마크업-적용-기능">자동 마크업 적용 기능</h1>
<ol>
<li><p><code>contentEditable=ture</code>인 엘리먼트에서 텍스트를 입력하고 innerText로 모든 Text를 뽑아 util 폴더의 applyMarkup함수(마크업을 적용하는 함수)에 전달하고, 정규표현식과 startsWith 메소드를 이용해 태그를 입혀 리턴하기.</p>
</li>
<li><p>리턴 받은 태그 데이터를 서버에 저장</p>
</li>
<li><p>새로고침 후 마크업 반영</p>
</li>
<li><p>에디터 수정 -&gt; 에디터가 focuson이 되면 각 태그에 맞는 마크업 문법이 텍스트에 나타남
ex) <code>&lt;h1&gt;h1 태그입니다&lt;/h1&gt;</code> -&gt; # h1 태그입니다
위의 예제처럼 각각의 태그가 맞는 마크업 문법을 각각의 태그의 innerText에 적용.</p>
</li>
<li><p>수정 후 2초뒤(디바운스) 서버에 마크업 태그를 다시 입혀 저장</p>
</li>
</ol>
<p>위 순서가 처음 마크업을 자동 적용하기 위한 설계이다.</p>
<h3 id="간단한-구현-내용">간단한 구현 내용</h3>
<p>아래와 같이 별도의 함수를 만들어주고, 매개변수로 에디터의 innerText를 받아 태그를 입혀 리턴해준다.</p>
<pre><code class="language-js">export const applyMarkup = (text) =&gt; {
  const H1 = &quot;# &quot;;
  const H2 = &quot;## &quot;;
  const H3 = &quot;### &quot;;
  const BOLD = /\*\*(.*?)\*\*/g;
  const STRIKETHROUGH = /\~\~(.*?)\~\~/g;
  const UNDERSCORE = /\_\_(.*?)\_\_/g;

  // 개행을 기준으로 h1, h2, h3 태그로 파싱
  const scanEditor = text.split(&quot;\n&quot;).map((tag) =&gt; {
    if (tag.startsWith(H1)) return `&lt;h1&gt;${tag.slice(2)}&lt;/h1&gt;`;
    else if (tag.startsWith(H2)) return `&lt;h2&gt;${tag.slice(3)}&lt;/h2&gt;`;
    else if (tag.startsWith(H3)) return `&lt;h3&gt;${tag.slice(4)}&lt;/h3&gt;`;
    else if (tag.match(BOLD)) return tag.replace(BOLD, &quot;&lt;b&gt;$1&lt;/b&gt;&quot;);
    else if (tag.match(STRIKETHROUGH))
      return tag.replace(STRIKETHROUGH, &quot;&lt;s&gt;$1&lt;/s&gt;&quot;);
    else if (tag.match(UNDERSCORE)) return tag.replace(UNDERSCORE, &quot;&lt;u&gt;$1&lt;/u&gt;&quot;);
    else if (tag.startsWith(&quot;📃&quot;)) return;
    else return `&lt;p&gt;${tag}&lt;/p&gt;`;
  });

  return scanEditor.join(&quot;&quot;);
};</code></pre>
<p>넘겨 받은 태그 데이터는 디바운스로 2초 뒤 서버에 저장!
서버에는 <code>&quot;&lt;h1&gt;h1&lt;/h1&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;h2&lt;/h2&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;h3&lt;/h3&gt;&lt;p&gt;&lt;/p&gt;&lt;u&gt; 밑줄 &lt;/u&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;~~ 취소선 ~&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;b&gt;볼드체&lt;/b&gt;&quot;</code>  이런식으로 데이터가 저장이 된다.</p>
<p>이제 수정을 하기 위해 에디터가 포커싱이 되면 해당 태그의 innerText에 해당 태그의 마크업 문법을 붙혀준다.</p>
<pre><code class="language-js">// 마크업 제거 함수
export const removeMarkup = ($target) =&gt; {
  $target.addEventListener(
    &quot;focusin&quot;,
    (e) =&gt; {
      document.querySelectorAll(&quot;h1&quot;).forEach((e, i) =&gt; {
        if (i !== 0 &amp;&amp; !e.innerText.startsWith(&quot;# &quot;))
          e.innerText = `# ${e.innerText}`;
      });
      document.querySelectorAll(&quot;h2&quot;).forEach((e) =&gt; {
        if (!e.innerText.startsWith(&quot;## &quot;)) e.innerText = `## ${e.innerText}`;
      });
      document.querySelectorAll(&quot;h3&quot;).forEach((e) =&gt; {
        if (!e.innerText.startsWith(&quot;### &quot;)) e.innerText = `### ${e.innerText}`;
      });
      document.querySelectorAll(&quot;b&quot;).forEach((e) =&gt; {
        if (!e.innerText.startsWith(&quot;**&quot;)) e.innerText = `**${e.innerText}**`;
      });
      document.querySelectorAll(&quot;s&quot;).forEach((e) =&gt; {
        if (!e.innerText.startsWith(&quot;~~&quot;)) e.innerText = `~~${e.innerText}~~`;
      });
      document.querySelectorAll(&quot;u&quot;).forEach((e) =&gt; {
        if (!e.innerText.startsWith(&quot;__&quot;)) e.innerText = `__${e.innerText}__`;
      });
    },
    { once: true }
  );
};</code></pre>
<p>addEventListener의 3번째 매개변수에 <code>{once: true}</code>가 있는 걸 볼 수 있다.</p>
<p>세번째 매개변수가 가진 기능은 이벤트 리스너를 <strong>한 번만</strong> 실행하게 해주는거다. 이번에 이벤트 핸들링을 하면서 처음 알게된 기능이다.</p>
<blockquote>
<p>만약 위 focuson 이벤트가 한 번만 실행하지 않으면 어떻게 될까? </p>
</blockquote>
<p>에디터가 포커싱이 될 때마다 h1 태그를 예로 들면 <code># 텍스트 -&gt; (포커싱) # # 텍스트 -&gt; (포커싱) # # # h1 텍스트</code>  이런식으로 innerText에 마크업 문법이 무한으로 늘어나는 버그가 생긴다.</p>
<p>처음 시도해본 것은 flag 변수를 사용해서 한번만 이벤트를 등록하게 해볼까? 라고 생각했지만 이것은 바보 같은 생각.. ㅋㅋㅋ</p>
<p>이미 한번 등록된 이벤트를 flag 변수로 막을 수 있을리가.. ㅋㅋㅋㅋ</p>
<p>flag 변수를 작성하자마자 바로 지워버리고 구글링을 시작했다. &#39;이벤트 한번만 실행하는 법&#39; 이라고 구글링하자마자 바로 {once:true}를 알게되었고, 이 방법을 앞으로도 종종 유용하게 쓸 것 같다.</p>
<h3 id="아쉬운-점">아쉬운 점</h3>
<p>현재 이 기능은 새로고침을 하거나, 다른 문서를 클릭하여 다른 페이지에 갔다오면 적용이된다. API를 뿌려주는 곳이 최상단 App.js라서 한번에 전체가 새로고침되는 구조인데, 이 구조를 좀 수정해서 에디터가 실시간으로 마크업이 반영될 수 있도록 리팩토링 해보야겠다 ㅎㅎ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[노션 클로닝 프로젝트 회고]]></title>
            <link>https://velog.io/@choi-ik/%EB%85%B8%EC%85%98-%ED%81%B4%EB%A1%9C%EB%8B%9D-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@choi-ik/%EB%85%B8%EC%85%98-%ED%81%B4%EB%A1%9C%EB%8B%9D-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 30 Oct 2023 17:26:18 GMT</pubDate>
            <description><![CDATA[<h1 id="프로젝트-목표">프로젝트 목표</h1>
<ul>
<li>컴포넌트간 의존성 최대한 제거</li>
<li>단방향 데이터 흐름</li>
<li>코드의 역할 분리</li>
<li>가독성 높은 코드</li>
<li>모든 설계가 끝날때까지 키보드에 손 대지 않기</li>
</ul>
<p>5가지의 목표를 두고 프로젝트를 시작하였고, 정확히 지켜진건 5번 하나 뿐이다..ㅠㅠ ㅋㅋㅋㅋ</p>
<h3 id="프로젝트-설계-👇">프로젝트 설계 👇</h3>
<p>우선 설계가 끝날때까지 키보드에 손을 아예 대지 않았다. 최대한 꼼꼼하게 설계하여 코드를 작성하다가 막히더라도 데이터의 흐름이 잘못되거나, 순환 참조로 인해 무한 루프에 걸리거나, 데이터를 잘못 받아와지는 실수는 하고 싶지 않았기 때문이다(설계에만 도대체 몇 시간을 썼는지 모르겠다 ㅠ).</p>
<p>최대한 루트에서 시작해서 데이터를 하위 컴포넌트로 뿌려주는 식으로 데이터 흐름도를 그렸고, 콜백에 콜백에 콜백에 콜백에... 콜백에.. 콜백 지옥이 발생하지 않도록 데이터와 코드의 흐름을 잘 파악하기 위해 리액트에서 발생하는 PropsDrilling을 최대한 만들지 않기 위해 많은 고민을 했다.</p>
<p><img src="https://velog.velcdn.com/images/choi-ik/post/2b0414b6-4c6f-4952-8d2f-cc19624befd2/image.jpeg" alt=""></p>
<p>SPA 형태로 프로젝트를 만들기 위해 History API를 사용했으며, 루트 App.js에서 API를 호출하고 하위 App.js에 뿌려주며 각 App.js는 API를 호출하고 뿌려주는 역할을 하고, document를 클릭해 페이지가 변경되면 url이 변경되고 변경된 url의 pathname을 루트의 App.js가 확인하여 다시 데이터를 내려주는 방식이다.</p>
<h3 id="마크업-적용">마크업 적용</h3>
<p>이 부분이 가장 시간이 오래 걸렸던 부분이다. 편집기에 작성한 텍스트에 마크업 문법을 적용하기 위해 <code>contenteditable = true</code>를 설정한 div 엘리먼트의 innerText를 모두 TextScan 파일내 별도의 함수의 매개변수로 넘겨주어 별도의 함수에서 split으로 구분하고 정규식을 통해 리턴해주는 로직을 작성하였다. </p>
<p>여기서 겪은 난항은 마크업 적용이된 텍스트를 다시 수정하여 마크업을 적용할때가 문제였는데, 이 부분을 해결하기 위해 div가 focuson이 되면 벨로그처럼 마크업 문법이 다시 텍스트에 나타나고 그 위에 수정을 하고 TextScan 파일내 별도의 함수로 다시 보내면 마크업이 적용되게끔 작성하였다.</p>
<p>이때 focuson 이벤트가 한번만 실행되게 해주고싶어 아래와 같이 이벤트 핸들러를 작성하였다.</p>
<pre><code class="language-javascript">// 마크업 제거 함수
export const removeMarkup = ($target) =&gt; {
  $target.addEventListener(
    &quot;focusin&quot;,
    (e) =&gt; {
      // 중간 코드 생략
    },
    // 이 부분이 이벤트 핸들러가 한번만 동작하게 해준다.
    { once: true }
  );
};</code></pre>
<p>addEventListener의 세번째 인자로 { once: true } 를 적용해주면 이벤트가 한번만 실행이 되는데 이번 프로젝트에서 유용하게 사용했다 ㅎㅎ</p>
<h3 id="엘리먼트-resizing">엘리먼트 Resizing</h3>
<p>노션의 메뉴바를 클릭하여 늘리거나 줄일 수 있는데, 이번 프로젝트에서 직접 적용을 해봤다. </p>
<p>엘리먼트 Resizing은 처음 시도해보았는데, 대충 이런 구조로 작성했다.</p>
<p>총 3개의 엘리먼트 -&gt; left, center, right를 만들고 center 엘리먼트가 mousedown 될 때, 현재 e.clientX의 좌표를 구하고 mousemove 될 때 초기 clientX 값과 이동중인 clientX의 값을 계속 비교하여 크기를 늘리거나 줄이고 mouseup 될 때 등록된 프로퍼티와 이벤트를 제거해주면 메뉴바가 늘어나거나 줄어드는 형식으로 구현했다.</p>
<h3 id="프로젝트를-진행하며-얻은점">프로젝트를 진행하며 얻은점</h3>
<ul>
<li><strong>API 흐름도 작성 및 설계</strong><ul>
<li>단방향으로 데이터 흐름을 설계</li>
</ul>
</li>
<li><strong>컴포넌트 의존성 제거</strong><ul>
<li>각 컴포넌트의 의존성을 제거하기 위해 하나의 컴포넌트에서 다른 컴포넌트에 접근 하지 않았고, 최대한 컴포넌트의 기능을 분리</li>
</ul>
</li>
<li><strong>디바운스</strong><ul>
<li>최근 팀원들과 모딥다 스터디를 하면서 디바운스와 스로틀에 대해 공부하였었는데 마침 강의에도 디바운스가 나왔었고, 이번 노션 클로닝을 하면서 왜 디바운스를 사용해야하고, 어디에 디바운스를 사용하면 적절한지 알게 되었다.</li>
</ul>
</li>
<li><strong>리액트가 아닌 바닐라JS에서의 SPA 구현</strong><ul>
<li>History API를 사용해 바닐라JS에서 SPA를 구현함으로써 History API와 SPA에 대해 이해</li>
</ul>
</li>
<li><strong>엘리먼트 Resizing</strong><ul>
<li>엘리먼트 Resizing 로직을 직접 작성하며 Resizing 하는 방법 체화</li>
</ul>
</li>
<li><strong>커스텀 이벤트</strong><ul>
<li>커스텀 이벤트를 사용하여 그 안에서 History API를 사용해 페이지를 라우팅 해주며 커스텀 이벤트의 적절한 사용 예시에 대해 이해</li>
</ul>
</li>
<li><strong>async/await, fetch, Promise</strong><ul>
<li>강의와 모딥다에서 공부했던 내용을 직접 프로젝트에 적용해보면서 체화</li>
</ul>
</li>
</ul>
<h3 id="아쉬웠던-점">아쉬웠던 점</h3>
<ul>
<li><strong>편집기 및 메뉴바 수정부분 -&gt; 실시간 렌더링을 적용하지 못한 점</strong><ul>
<li>처음 설계가 루트 App.js에서 하위 컴포넌트로 데이터를 내려주는 방식이었고, 새로고침이나 다른 페이지에 갔다오면 마크업 또는 메뉴바 업데이트가 적용되는 흐름이었어서 먼저 설계 방식대로 구현한 뒤 남은 시간에 실시간을 적용해보려했는데 적용하지 못해 아쉬움이 남아 조만간 기능 추가를 해 볼 예정이다.</li>
</ul>
</li>
<li><strong>깔끔하지 못한 코드</strong><ul>
<li>주어진 기간 내에 모든 기능이 돌아가도록 하다보니 코드를 가독성있게 깔끔하게 짜지 못한 것 같다. 이 부분은 멘토님과 멘토님 동기들과 함께 모각코 했을때 멘토님 동기분이 해주셨던 조언인데 지금은 처음부터 너무 깔끔하게 짜려고 하는 것 보다, 먼저 구현해 놓고 리팩토링 하는것도 좋은 방법이라고 조언해주셨다. 지금 코드가 깔끔하지 못하니 리팩토링 해 볼 예정이다.</li>
</ul>
</li>
<li><strong>렌더링 설계</strong><ul>
<li>루트에서 데이터를 내려주는 게 아닌, 각 컴포넌트별로 따로 렌더링이 될 수 있게 다시 설계하고 코드를 수정하여 Ajax사용하는 것 처럼 렌더링 해줄 수 있도록 리팩토링 해 볼 예정.</li>
</ul>
</li>
</ul>
<blockquote>
<p>프로젝트를 진행하며 겪었던 어려움과 해결 과정은 추후 포스팅 할 예정입니다 😁</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[데브코스 1차 MIL]]></title>
            <link>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-1%EB%8B%AC%EC%B0%A8-MIL</link>
            <guid>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-1%EB%8B%AC%EC%B0%A8-MIL</guid>
            <pubDate>Sun, 22 Oct 2023 10:23:08 GMT</pubDate>
            <description><![CDATA[<p>코딩테스트, 면접을 거치고 최종합격, 9월부터 중순부터 시작된 프론트엔드 데브코스 과정. </p>
<p>그 과정이 시작된 이후로 한달이 지났다.</p>
<blockquote>
<p>이 과정속에서 나는 무엇을 얻었을까? 또, 나는 무엇이 부족함을 인지했을까?
MIL을 통해 지난 한 달을 돌아보자.
(긴 글 주의.... ㅎ)</p>
</blockquote>
<h1 id="한-달-동안-느낀-것-👨💻">한 달 동안 느낀 것 👨‍💻</h1>
<h2 id="psproblem-solving에-대한-메타인지">PS(Problem Solving)에 대한 메타인지</h2>
<p>느낀점부터 설명하자면 알고리즘 역량이 부족하다는 것을 느꼈다. </p>
<p>데브코스를 시작 하기 전 백준 골드3, 프로그래머스 레벨3 정도의 문제를 50분~1시간 이내에 풀었었다(물론 못 푼 문제도 많았다. 이런 경우에는 오래 고민하지 않고 바로 여러 사람의 코드를 보며 접근 방식을 익혔다). </p>
<p>데브코스 면접때부터 PS를 손 놓고 있었는데, 데브코스 과정을 시작하며 다시 풀어보니 머리가 돌아가지 않았다. 어느정도 공부를 하면서 알고리즘 해결 능력이 올라왔다 생각을 했었는데 생각해보니 아닌 것 같다. 그냥 그때 당시 꾸준하게 풀었기에 가능했던 것이었다는 걸 알았고, 꾸준하게 하루에 골드 3이상 문제를 1~2문제 씩 풀어야 겠다고 느꼈다. </p>
<p>PS가 취업? 코딩 실력?에 영향을 미치는가를 고민 해보면 나는 좀 영향이 있다고 생각한다. 문제를 풀면서 어떻게 해결할 지 고민하고 논리적으로 풀어나가는 과정에서 문제 해결력이 상승됨을 조금씩 느낀다. 프로젝트를 하다보면 라이브러리와 구글링이 모든 걸 해결해 줄 수 없는 상황이 생긴다. 오로지 그 상황에선 자신의 논리적인 해결 능력이 필요한데 그 순간에 도움이 되는 것 같다. 필수라고 말 할 순 없지만 열심히 하면 +인 것은 확실한것 같다 ㅎㅎ</p>
<p>PS를 위해서는 꾸준함이 중요하다는 걸 알았고 취업을 하지 못한 지금 상황에서는 꾸준함을 유지해 미래에 있을 코딩테스트에 대비 할 것이다.</p>
<h2 id="js의-기본을-돌아보았다는-것">JS의 기본을 돌아보았다는 것</h2>
<p>데브코스 강의와 &#39;모딥다&#39; 스터디를 통해 수박 겉핥기 식으로 알고 있었던 개념들에 대해 다시 짚어 보았다.
<code>ex) 클로저, 호이스팅, Promise, fetch, async/await, this, 비동기, callback 등등</code></p>
<p>그간 이러한 기본기들을 수박 겉핥기 식으로만 알고 있었고 프로젝트에 적용해 볼 일이 있었다면 구글링하여 어떻게 쓰이는지 빠르게 파악하고 수정하여 덧붙히기만 했었다. 하지만 강의와 모딥다 스터디를 계기로 JS의 원리에 대해 파고들고 다양한 개념들의 기본 원리를 학습하여 <code>왜? 어디에? 어떻게?</code> 쓰이는지에 대해 짚고 넘어갈 수 있게 되었다.</p>
<p>솔직히 짚고 넘어갔다 하지만 학습한 내용을 누군가에게 설명하라면 완벽하게 할 자신은 없다. 내 자신은 한번에 공부한 지식을 머리속에 모두 넣을 수 없다는 걸 너무나도 잘 안다. 그러기에 이번에 JS 기본에 대해 한 번씩 보면서 어느정도 익혔으니 계속 반복적으로 학습한다면 완전히 내 것으로 만들 수 있을 것 같다.</p>
<h2 id="뭐든지-바닥부터">뭐든지 바닥부터</h2>
<p>바닐라 JS로 강의를 듣고 과제의 요구사항을 처음에는 강사님의 코드를 바탕으로 기능을 추가했었다. 하지만 멘토님께서 바닥부터 스스로 구현해 보는 걸 추천하셨고, 이번 노션 클로닝 프로젝트는 완전 바닥부터 스스로 구현해서 진행중이다. </p>
<p>강의를 듣고 진행하는 프로젝트이기에 강사님의 코드흐름과 어느정도 비슷한 부분도 있지만 거의 모든 기능을 스스로 구현하다보니 확실히 코드의 흐름이 잘 보이고 파일 구조부터 데이터 플로우, 코드 흐름까지 모두 스스로 설계하고 진행하다보니 더욱 더 성장하고 있는 느낌이든다.</p>
<p>레퍼런스를 보고 구현할 때 오래 걸리고 돌아가더라도 직접 바닥부터 구현을 해보는 것이 성장에 훨씬 도움이 된다는 걸 느꼈다.</p>
<h2 id="깃-적응">깃 적응</h2>
<p>지금까지 몇 번의 팀프로젝트를 했던 경험이 있지만 깃을 제대로 활용해본 경험은 없었다. 하지만 데브코스에 와서 유림님의 세션을 들으며 깃이 어떤 명령어가 있고, 어떤 흐름으로 사용해야 하는지 깨달았고, 이어지는 과제와 스터디에 깃을 활용함으로써 깃에 조금 적응이 된 것 같다고 느꼈다. 이 부분은 계속 반복적으로 사용하며 숙달하면 머리가 아닌 몸으로 체득이 될 것 같다.</p>
<h2 id="성장을-위한-자세">성장을 위한 자세</h2>
<p>여러 강사님들의 강의 외적으로 특강을 들으면서 어떤 자세로 학습에 임해야 할지 감이 잡혔다. </p>
<p>광휘님께서는 순한맛?으로 말씀을 많이 해주셨는데, 주니어때 생각해보면 좋을 것들, 기록의 중요성(블로그), 왜?라는 의문을 항상 가지는 자세들에 대해 말씀해 주셔서 아직 취직을 하지 못한 예비 주니어 개발자로서 어떠한 자세로 성장하려는 자세에 임해야 할지 스스로 방향성이 잡혔다.</p>
<p>하지만 로토님께서는 꽤나 매운맛?으로 말씀을 많이 해주셨는데, 현재 개발자 시장에 대해 냉정하게 말씀을 많이 해주셨고, 어떤 경험과 실력을 갖추어야 하는지 말씀해주셔서 더욱 현실에 안주하지 않는 마음을 갖게 되었다.</p>
<p>광휘님의 순한맛 특강으로 성장 방향성과 로토님의 매운맛 특강으로 현실을 깨달았으니 방향성을 잘 잡고 꾸준하게 나아가면 될 것 같다.</p>
<h1 id="한달-간-성장한-부분-📈">한달 간 성장한 부분 📈</h1>
<ul>
<li><p><strong>나만의 학습 방법 정립</strong></p>
<ul>
<li>한번에 익히려는 마음보단 직접 부딪히고 반복 학습, 왜?라는 궁금증으로 인한 꼬리물기 학습</li>
</ul>
</li>
<li><p><strong>나에 대한 메타인지</strong></p>
<ul>
<li>코딩테스트 실력, 프로젝트 설계 및 구현 능력, JS 기본기 부족</li>
</ul>
</li>
<li><p><strong>JS 기본기</strong>   </p>
<ul>
<li>클로저</li>
<li>this</li>
<li>호이스팅</li>
<li>즉시실행함수</li>
</ul>
</li>
<li><p><strong>선언형 프로그래밍과 컴포넌트 방식의 구현 집중</strong></p>
<ul>
<li>각 컴포넌트간 의존성 제거 집중 
ex) 콜백 함수 이용 및 컴포넌트 외부에서 핸들링</li>
</ul>
</li>
<li><p><strong>스터디를 통해 익힌 부분</strong></p>
<ul>
<li>배열</li>
<li>DOM</li>
<li>이벤트</li>
<li>타이머, 비동기, Ajax, RestAPI</li>
<li>프로미스</li>
<li>제너레이너, async/await, 에러처리</li>
<li>심볼, 이터러블, 스프레드 문법
위의 JS 기본기와 스터디 내용, 이 모든 내용을 솔직히 다 정확하게 설명할 정도로 기억하진 못한다. 다만 기억이 나지 않을때 다시 찾아보며 반복 학습하면 결국 내 것이 되리라 생각한다.</li>
</ul>
</li>
<li><p>** 구현에 대한 마음가짐**</p>
<ul>
<li>결국 강의를 듣고 내 것으로 만드려면 바닥부터 구현 할 것.</li>
</ul>
</li>
<li><p>** 블로그 기록 + 의도 **</p>
<h1 id="부족한-점">부족한 점?</h1>
<p>한달 동안 느끼고 성장한 부분 모두 부족한 점이다.</p>
</li>
</ul>
<p>그런데 왜 성장한 부분에 써 놓았냐면.. 내가 무엇이 부족한지 알았기에 성장한 셈이다 ㅎㅎ 메타인지 라고 할까나.. 뭐가 부족한지 알았고, 나만의 학습 방법을 알았으니 부족하지만 성장할 수 있는 계기가 되었다고 생각한다 ㅎㅎㅎㅎ </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[fetch에서의 await]]></title>
            <link>https://velog.io/@choi-ik/fetch%EC%97%90%EC%84%9C%EC%9D%98-await</link>
            <guid>https://velog.io/@choi-ik/fetch%EC%97%90%EC%84%9C%EC%9D%98-await</guid>
            <pubDate>Fri, 13 Oct 2023 10:30:02 GMT</pubDate>
            <description><![CDATA[<h1 id="fetch에서의-await">fetch에서의 await</h1>
<h4 id="코드-1">코드 1</h4>
<pre><code class="language-javascript">export const request = async (url, options = {}) =&gt; {
  try {
    const res = await fetch(`${API_END_POINT}${url}`, {
      ...options,
      headers: {
        &quot;Content-Type&quot;: &quot;application/json&quot;,
      },
    });

    if (res.ok) {
      // 여기에서는 왜 await을 한번 더쓰지? res는 이미 await 하고 받아온 값 아닌가..
      // 이 
      const json = await res.json();

      return json;
    }

    throw new Error(&quot;API 호출 오류&quot;);
  } catch (e) {
    alert(e.message);
  }
};</code></pre>
<p>분명 res를 await 키워드를 붙혀 프로미스 객체를 반환 받아 왔는데 왜 await을 한번 더 써서 res.json()으로 파싱해서 보내주는 걸까 라는 고민을 했다.</p>
<p>해당 fetch 데이터를 받아주는 코드는 다음과 같다</p>
<h4 id="코드-2">코드 2</h4>
<pre><code class="language-javascript"> // userList http 요청
  const fetchUserList = async () =&gt; {
    const userList = await request(`/users`);

    this.setState({
      ...this.state,
      userList,
    });
  };</code></pre>
<p>코드 1에서 await을 붙히지 않은 json을 콘솔에 찍어보면 프로미스 객체가 출력되고, await을 붙히면 해당 데이터를 파싱한 데이터가 출력이 된다.</p>
<p>Promise가 await에 넘겨지면, await은 Promise가 fulfill되기를 기다렸다가 해당 값을 리턴하는데, await을 붙히지 않으면 fulfill되기를 기다리지 않아 프로미스 객체가 그대로 콘솔에 출력이 되는 것이고, await을 붙히면 fulfill되기를 기다렸다가 해당 값을 리턴하기에 파싱된 데이터가 출력이 되었었던 것이다.</p>
<p>async/await을 쓰지 않았다면 response.json().then((res) =&gt; return res) 이런식으로 데이터를 리턴해줬어야 했을것이다.</p>
<p>하지만 현재 내 코드에서는 문제는 없다. res를 await으로 감싸서 리턴한다면 받아주는 코드에서 await을 사용하여 넘어온 값을 resolve된 Promise로 변환 하기를 기다리기 때문에 문제가 없고, await으로 감싸서 리턴하지 않아 프로미스 객체가 넘어와도 받아주는 코드의 await이 해당 객체가 fulfill 되기를 기다렸다가 해당 값을 리턴하면 되기 때문이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 데브코스 18일차 TIL]]></title>
            <link>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-18%EC%9D%BC%EC%B0%A8-TIL</link>
            <guid>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-18%EC%9D%BC%EC%B0%A8-TIL</guid>
            <pubDate>Thu, 12 Oct 2023 13:29:14 GMT</pubDate>
            <description><![CDATA[<h1 id="공부한-내용-📘">공부한 내용 📘</h1>
<ul>
<li><strong><code>fetch API</code></strong></li>
<li><strong><code>History API</code></strong></li>
</ul>
<h2 id="fetch-api">fetch API</h2>
<blockquote>
<p>XMLHTTPRequest 객체와 마찬가지로 HTTP 요청 전송 기능을 제공하는 클라이언트 사이드 Web API</p>
</blockquote>
<p>프로미스를 지원하여 비동기 처리를 위한 콜백 패턴의 단점에서 자유로운 fetch.</p>
<p>fetch 함수에는 HTTP 요청을 전송할 URL과 HTTP 요청 메서드, HTTP 요청 헤더, 페이로드 등을 설정한 객체를 전달한다.</p>
<p>이러한 fetch 함수는 HTTP 응답을 나타내는 Response 객체를 래핑한 Promise 객체를 반환한다.</p>
<p>HTTP 응답을 나타내는 Response 객체를 래핑한 프로미스를 반환하므로 후속 처리 메서드(then, catch, finally)를 통해 프로미스가 resolve한 Response 객체를 전달 받을 수 있다. 이 Response 객체를 Response.prototype.json 메서드를 이용하여 역직렬화 하여 사용할 수 있다.</p>
<p>fetch 함수에서 중요한 것은 fetch 함수가 반환하는 프로미스는 기본적으로 HTTP 에러가 발생해도 에러를 reject 하지 않고 불리언 타입의 ok를 false로 설정한 Response 객체를 resolve 한다. 오프라인 등의 네트워크 장애나 CORS 에러에 의해 요청이 완료되지 못한 경우에만 프로미스를 reject 한다.</p>
<p>다음과 같은 상황을 예방하기 위해 아래 코드처럼 ok 상태를 확인해줄 수 있다.</p>
<pre><code class="language-javascript">const URL = &#39;https://~~~~~~~~&#39;

fetch(URL) {
    .then(res =&gt; {
      // res는 HTTP 응답을 나타내는 Response 객체임.
        if (!res.ok) throw new Error(res.statusText);

      // 응답 객체 역직렬화
          return res.json();
    })
}
</code></pre>
<p>다음의 예제 코드로 GET, POST, PATCH, DELETE 등 각각의 상황에 맞게 HTTP 요청을 전송 할 수 있다.</p>
<pre><code class="language-javascript">const request = {
  get(url) {
    return fetch(url);  
  },
  post(url, payload) {
      return fetch(url, {
        method: &#39;POST&#39;,
        headers: {&#39;content-Type&#39;: &#39;application/json&#39;}
        body: JSON.stringify(payload)
    });
  },
  patch(url, payload) {
      return fetch(url, {
        method: &#39;PATCH&#39;,
        headers: {&#39;content-Type&#39;: &#39;application/json&#39;}
        body: JSON.stringify(payload)
    });
  },
  delete(url) {
      return fetch(url, { method: &#39;DELETE&#39;});
  },
}</code></pre>
<p>이후 요청 코드에서 get, delete는 URL만 넣어줘도 실행이 되고, post, patch는 페이로드 부분까지 넣어주면 HTTP 요청이 진행된다.</p>
<h2 id="history-api">History API</h2>
<blockquote>
<p>History API는 브라우저의 세션 기록을 조작할 수 있는 메소드를 담고 있는 객체로 기본적으로 뒤로가기, 앞으로 가기, 페이지 이동 등을 조작할 수 있다.</p>
</blockquote>
<p><code>history.pushState()</code> 를 통해 새로운 주소 목록을 추가하고 이전 url의 주소가 남아 브라우저의 뒤로가기 버튼이 활성화 됨.</p>
<p><code>history.replaceState()</code> 위와 동일한 기능을 하지만 주소 목록에 추가하지 않기 때문에 뒤로가기 버튼이 활성화 되지 않음.</p>
<p>두 함수는 세 가지의 인자를 가지는데 앞의 두 인자는 보통 null을 전달하고 마지막 세 번째 인자에 변경할 url 주소를 넣어주면 된다.</p>
<p>History API에 대해 정확히 알고 넘어가야 할 점은, 화면 이동 없이 브라우저 url만 변경이 가능하고, 그에 맞는 데이터를 새로고침 없이 렌더링 하여 보여줄 수 있다. 중요한 것은 history API로 url 변경 후 새로고침 하면 변경된 url의 실제 파일을 찾으려 하기에 404에러가 발생한다. 이 상황에서 root의 index.html로 요청을 돌려주는 처리가 필요하다(안하면 로컬에서는 잘 작동하지만, 배포 했을 때에는 잘 작동하지 않을 수 있다).</p>
<p>이러한 원리를 잘 적용하기 위해서는 렌더링이 언제 어떻게 되느냐에 대한 완벽한 이해가 필요해 보인다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 데브코스 11일차 TIL]]></title>
            <link>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-11%EC%9D%BC%EC%B0%A8-TIL</link>
            <guid>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-11%EC%9D%BC%EC%B0%A8-TIL</guid>
            <pubDate>Wed, 04 Oct 2023 17:48:06 GMT</pubDate>
            <description><![CDATA[<h1 id="공부한-내용-📘">공부한 내용 📘</h1>
<p>강사님의 코딩테스트 준비 방법에 관한 강의를 보면서 다시 한번 코딩테스트에 대한 준비 자세를 배웠다. 강사님의 조언을 이곳에 써두고 코딩테스트 문제를 풀 때마다 이 블로그를 보며 복기 하면 좋을 것 같다.</p>
<h3 id="문제-풀-때-중요한-것">문제 풀 때 중요한 것</h3>
<ol>
<li>항상 여러가지 풀이 방법이 있을 수 있다.</li>
<li>항상 예외가 있을 수 있다.</li>
<li>내가 풀어낸 답이 베스트인지 의심하자.</li>
<li>문제를 풀었다면 시행착오를 모두 기록.</li>
<li>다른 사람의 코드를 많이 보고 생각의 범위를 넓히자.<h3 id="마음-가짐">마음 가짐</h3>
</li>
<li><strong>알고리즘 마스터가 될 필요는 없다(이게 좀 중요한 것 같다. 제한된 시간 내에 모든 알고리즘을 다 잘 할 순 없다..).</strong></li>
<li>코딩테스트는 대회용 알고리즘을 출제하는 것이 아니기에, 논리적인 사고를 기르자.</li>
<li>제한된 시간내에 어디까지 공부할지 정하는 것이 중요.<h3 id="자신의-성향-파악">자신의 성향 파악</h3>
나는 미리 생각하고, 의사 코드를 작성해야(노트에 순서도도 그리면서) 더 잘 풀리는 사람이다. 키보드에 손을 올리기 전에 가능한 모든 상황을 설계하고 난 뒤 코드를 작성하자.<h3 id="메모하기">메모하기</h3>
코드에 주석을 달면서 문제를 해결해 나가자. 지금까지 코딩테스트 문제를 풀때 항상 주석을 달기는 했는데, 더 자세히 그리고 의미 전달을 쉽게 할 수 있게 달아 보아야겠다.<h3 id="익숙해지기">익숙해지기</h3>
</li>
<li>문제를 제대로 &#39;잘&#39; 읽자...</li>
<li>시간복잡도 계산을 철저하게 하자.</li>
<li><strong>항상 내가 실수하는 엣지 케이스에 대한 설계를 좀 더 치밀하게 하자.</strong><h3 id="간결하고-가독성-좋은-코드">간결하고 가독성 좋은 코드</h3>
</li>
<li>변수, 함수의 이름을 의미전달을 쉽게할 수 있게 정하기.</li>
<li>중복 코드 제거.</li>
<li>함수형 프로그래밍.</li>
<li>가지치기를 하자.<h3 id="자바스크립트를-잘-이용하라">자바스크립트를 잘 이용하라</h3>
지금까지도 그랬고, 앞으로도 자바스크립트로 코딩 테스트를 볼 예정이지 자바스크립트 문법을 잘 활용하자
ex) 구조 분해 할당, ...오퍼레이터 등등</li>
</ol>
<p>이 부분은 코어 자바스크립트와 모딥다를 꾸준하게 읽고, 문제를 많이 풀어보고 다른 사람의 풀이를 많이 봐야 늘 것 같다.</p>
<h3 id="일관성-유지">일관성 유지</h3>
<ol>
<li>var / let 혼용 금지.</li>
<li>snake_case / camelCase 혼용 금지.</li>
<li>변수명 함수명 줄임말 쓰다 어딘가에선 전부 적는 것 금지.<h3 id="입출력-제한">입출력 제한</h3>
<a href="https://school.programmers.co.kr/app/courses/19012/curriculum/lessons/236135">이선협 강사님의 문제의 입출력 제한 팁</a> --&gt; 어느정도는 알고 있던 내용이지만 문제를 풀기전에 습관처럼 보고 익혀야겠다</li>
</ol>
<p>이어서 엣지케이스 찾는 법과 Javascript 9가지 코드 트릭에 대해서도 배워서 앞으로 코딩테스트 문제를 해결함에 있어 큰 도움이 될 것 같다. 이선협 강사님의 조언을 습관처럼 보고 머리속에 새기면 좋을 것 같다.</p>
<h2 id="html-css-dom">HTML, CSS, DOM</h2>
<p>현재 우리 팀의 스터디 모토가 강의 주제에 맞는 모딥다의 챕터를 골라 공부하기 인데, 이 강의를 듣기전 스터디를 위해 모딥다의 &#39;DOM&#39; 부분을 공부했더니 강의의 내용이 머리속에 쏙쏙 잘 들어왔다. 모딥다에 없었던 Virtual DOM 부분은 리액트를 하며 가상돔에 대한 이해가 어느정도 있었기에 이 부분도 이해가 잘 됐었다. </p>
<p>추가로 DOM 조작 실습 강의에서 강사님이 즉시 실행 함수를 사용하셨었다. 나는 강의를 보면서 아 그냥 즉시 실행 함수는 이런거구나~ 이래서 쓴거구나~ 하고 넘겼는데, 다른 팀원분께서 이 부분에 대해 궁금증을 가지시고 그 부분을 블로그에 포스팅 해주셨다. 팀원분의 블로그를 본 덕분에 호이스팅과 즉시 실행 함수에 관한 내용을 다시 한번 익히는데 도움이 많이 되었다.</p>
<h1 id="느낀점👨💻">느낀점👨‍💻</h1>
<p>나는 평상시에 개발, 개발 공부를 할때 항상 큰 궁금증, 의문을 가지지 않았던것 같다. 짧고 빠르게 내게 필요한 것, 이게 왜 쓰는건지(간단하게) 실용적으로 공부를 해왔었다. 지금까지는 이것이 빠르게 개발자가 되는 길 이라고 생각했었는데 데브코스에서 만난 팀원분을 보면서 생각이 바뀌었다. </p>
<p>우리팀 팀원분은 사소한 것에 궁금증을 가지고, 의문을 가지며 그 원리를 바로 찾아보고 차근차근 공부하여 머리속에 넣으신다. 팀원분께서는 이러한 공부 방법이 진도가 느려 별로라고 하셨지만 오히려 나는 팀원분의 공부 방법이 더 좋다고 느꼈다. 나도 앞으로는 사소한 것부터 궁금증을 가지고 고민하는 노력을 기울여 기초를 탄탄하게 쌓아가야겠다. 사소하다고 느끼면 사소할 수 있지만 내겐 큰 배움이었다! 좋은 팀원들을 만나 너무 많은 것을 배워가는데 나는 무슨 도움을 드릴 수 있을까.. 차근차근 공부해서 나도 팀원들에게 큰 도움이 되고 싶다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 데브코스 10일차 TIL]]></title>
            <link>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-10%EC%9D%BC%EC%B0%A8-TIL</link>
            <guid>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-10%EC%9D%BC%EC%B0%A8-TIL</guid>
            <pubDate>Tue, 03 Oct 2023 07:17:14 GMT</pubDate>
            <description><![CDATA[<h1 id="공부한-내용">공부한 내용</h1>
<ul>
<li>백트래킹</li>
<li>DP</li>
<li>트리 구조에서의 전위,중위,후위 순회(과제)</li>
<li>트라이 자료 구조를 이용한 자동 완성 기능 구현(과제)</li>
</ul>
<p>오랜만에 백트래킹 문제를 풀어봤는데 굉장히 애를 먹은 문제(<a href="https://school.programmers.co.kr/learn/courses/30/lessons/12952">N-Queen</a>)였다. 다른 백트래킹 문제들은 조금 풀어 보았는데, 백트래킹의 가장 대표적인 N-Queen 문제는 한번도 풀어본 적이 없었다. </p>
<h3 id="n-queen-접근-방법코드-x">N-Queen 접근 방법(코드 X)</h3>
<p>처음 접근 방법은 2차원 배열을 생성하고 퀸이 존재하는 좌표를 하나의 배열에 저장한 뒤 재귀를 통해 퀸의 좌표를 가지고 있는 배열을 순회하며 현재 퀸을 놓으려는 행에 놓을 수 있는지 없는지를 체크한 후, 어떠한 한 행에라도 퀸을 놓을 수 없으면(배열의 크기가 n*n인데 n개의 체스를 놓아야 하므로) result를 증가시키지 않고 모든 행에 하나씩 퀸을 놓을 수 있으면 result를 증가시키는 방향으로 설계를 했다.</p>
<p>아무리 머리속으로 계산을 하고 순서도(콜 스택) 그려봐도 이 아이디어가 맞다고 생각했는데 자꾸 히든 케이스에서 몇가지가 틀렸다. 끝까지 파고들고 어디 부분이 잘못 됐는지 찾았다면 찾을 수는 있었겠지만 그냥 이번엔 강사님의 코드를 보았다. </p>
<p>역시나 강사님의 코드를 보기를 잘 한 것 같다. 나는 2차원 배열을 사용할 것을 생각했지만, 강사님께서는 1차원 배열을 사용하여 백트래킹을 진행하셨다. </p>
<p>강사님이 1차원 배열을 사용하셨다는 것을 보고 강사님의 코드를 보지 않고 다시 아이디어를 설계했고, 코드를 구현했더니 맞았다. 이후 2차원 배열로도 틀렸던 히든케이스를 찾아 고쳐 실행을 해보았는데 가장 오래걸렸던 히든케이스가 각각 대략 200ms와 900ms가 나왔다. 700ms나 차이가 나는 것을 보았고, 앞으로도 구현하고 난 뒤에 더 나은 방법이 없을지 고민해보고 답이 안나온다면 다른 사람의 코드를 보는 것이 도움이 된다는 것을 다시 한 번 느꼈다.</p>
<h3 id="단어-퍼즐설계도-못함">단어 퍼즐(설계도 못함)</h3>
<p>이어서 DP문제를 풀어보았는데 이건 아예 아이디어조차 떠오르지 않았다. 평소 코딩테스트 준비를 할 때도 DP는 진짜 못하고 어려웠는데 아니나 다를까 이번에도 어려웠다 .. 더군나나 문제는 레벨4..</p>
<p>결국 강사님의 코드를 보았고 한줄 한줄 분석하며 해답을 찾아 이해는 했지만, 다른 어려운 DP 문제들을 보면 또 헤맬 것 같다. DP만큼은 왜 실력이 늘지 않는 것일까..</p>
<h3 id="전중후위-순회">전/중/후위 순회</h3>
<p>강사님께서 스택 or 재귀를 사용해서 구현하라 하셨는데 갑자기 딱 재귀를 이용한 방법이 떠올라서 바로 구현을 하였다. 그런데 다른 분들 과제 제출 PR 날리신 걸 보니 다른 기능들을 추가하신 분들이 많았다.. 멘토님께 리뷰 받고 난 뒤에 나도 기능을 추가 해봐야겠다. </p>
<pre><code class="language-javascript">class Node {
    constructor(value) {
        this.val = value;
        this.left = null;
        this.right = null;
    }
}

// 트리
class Tree {
    constructor(node) {
        this.root = node;
    }

    // 중위순회
    InOrderTree(node, arr) {
        if(!node) {
            return;
        }

        this.InOrderTree(node.left, arr);
        arr.push(node.val);
        this.InOrderTree(node.right, arr);

        return arr;
    }

    // 전위순회
    preOrderTree(node, arr) {
        if(!node) {
            return;
        }

        arr.push(node.val);
        this.preOrderTree(node.left, arr);
        this.preOrderTree(node.right, arr);

        return arr;
    }

    // 후위순회
    postOrderTree(node, arr) {
        if(!node) {
            return;
        }

        this.postOrderTree(node.left, arr);
        this.postOrderTree(node.right, arr);
        arr.push(node.val);

        return arr;
    }
}

const tree = new Tree(new Node(1));

tree.root.left = new Node(2);
tree.root.right = new Node(3);
tree.root.left.left = new Node(4);
tree.root.left.right = new Node(5);
tree.root.right.left = new Node(6);
tree.root.right.right = new Node(7);

console.log(&quot;&gt;&gt;&gt;&gt; 중위 순회&quot;);
console.log(tree.InOrderTree(tree.root, []).join(&quot; - &quot;), &quot;\n&quot;);

console.log(&quot;&gt;&gt;&gt;&gt; 전위 순회&quot;);
console.log(tree.preOrderTree(tree.root, []).join(&quot; - &quot;), &quot;\n&quot;);

console.log(&quot;&gt;&gt;&gt;&gt; 후위 순회&quot;);
console.log(tree.postOrderTree(tree.root, []).join(&quot; - &quot;));</code></pre>
<h3 id="자동-완성-기능트라이">자동 완성 기능(트라이)</h3>
<p>강사님의 강의를 통해 만들어 놓았던 트라이 자료구조에 덧붙혀 자동 완성 기능을 구현하는 과제였다. 처음 생각 했던 건 자동 완성된 단어들을 보여줄 때 길이 순으로 보여주고 싶었고 그러기 위해서는 BFS 알고리즘을 사용한 방법이 맞다고 생각했다(이미 강사님께서 레벨 순회를 이용하라고 써 놓으신걸 못보고 혼자 고민하고 있었다..ㅎ).</p>
<p>레벨 순회를 통해 자동 완성된 단어들이 자연스레 단어의 길이 순서대로 출력이 되었고 찾을 수 없는 단어를 입력했을 때의 예외처리와 내가 찾고자 하는 단어가 자료구조에 포함되어 있는지에 대한 에외처리를 추가로 구현하여 과제를 제출했다.</p>
<p>이 부분도 코드리뷰를 받고 난 뒤에 추가할 기능에 대해 고민해보고 기능을 추가해 보아야겠다.</p>
<pre><code class="language-javascript">// level order traversal을 위한 Queue
class Queue { 
    constructor(){
        this.queue = [];
        this.front = 0;
        this.back = 0;
    }

    enqueue(value){ 
        this.queue[this.back++] = value;
    }

    dequeue(){   
        const value = this.queue[this.front];
        delete this.queue[this.front]; // 
        this.front += 1;
        return value;
    }

    size(){
        return this.back - this.front;
    }
};

class Node {
    constructor(value = &#39;&#39;) {
        this.value = value; // 현재 경로까지의 누적 값
        this.end = false; // 해당 노드에서 끝나는 문자열이 있는지 여부
        this.child = new Map() // 자식
    }
}

// 트라이
class Trie {
    constructor() {
        this.root = new Node();
    }

    // 단어 삽입
    insert(string) {
        let currentNode = this.root; // 루트노드를 시작으로 탐색하면서 삽입

        for (let char of string) {
            // 만일, 해딩 키를 가진 자식이 없다면 새 노드를 만들어준다
            if (!currentNode.child.has(char)) {
                currentNode.child.set(
                    char,
                    new Node(currentNode.value + char)
                );
            }
            currentNode = currentNode.child.get(char); // 자식 노드로 이동한다
        }

        currentNode.end = true; // 해당 노드에서 끝나는 단어가 있음을 알린다
    }

    // 단어 탐색 
    search(string) {
        let currentNode = this.root; // 시작은 루트

        for (let char of string) {
            // 찾고자 하는 문자가 있으면 노드 이동, 없으면 false 리턴
            if (!currentNode.child.has(char)) {
                return false;
            } 

            currentNode = currentNode.child.get(char);
        }

        // 찾는 문자열의 마지막까지 탐색했다는 건, 문자열을 찾았다는 것이므로 마지막 문자의 Node 리턴
        return currentNode;
    }

    // 입력한 단어의 마지막 문자를 기준으로 level order traversal
    levelOrder(node) {
        const queue = new Queue(); 
        queue.enqueue(node);

        while (queue.size()) {
            const currentNode = queue.dequeue();

            // 등록된 단어만 출력
            if (currentNode.end) console.log(currentNode.value);
            // 키에 해당하는 노드(value: 현재까지의 문자를 더한 단어, end: 등록된 단어 유무, child: 다음에 이어지는 문자)를 큐에 입력
            for (let key of currentNode.child.keys()) {
                queue.enqueue(currentNode.child.get(key))
            }
        } 
    }

    autoComplete(string) {
        // string을 가지고 있는 node를 찾아서
        let node = this.search(string);
        console.log(&quot;검색어 :&quot;, string);

        // 예외 처리
        if (!node) return console.log(&quot;❌ 해당하는 문자로 시작되는 단어를 찾을 수 없습니다 ❌&quot;);

        this.levelOrder(node);
    }
};

// 트라이 생성 후, 단어 입력
const trie = new Trie();
trie.insert(&quot;pro&quot;);
trie.insert(&quot;proud&quot;);
trie.insert(&quot;programmers&quot;);
trie.insert(&quot;proto&quot;);
trie.insert(&quot;prou&quot;);
trie.insert(&quot;prototype&quot;);
trie.insert(&quot;primary&quot;);
trie.insert(&quot;picnic&quot;);
trie.insert(&quot;plz dm&quot;);

// 트라이 자료구조 특성상(레벨 순회) 단어의 길이가 짧은 순으로 자동완성이 됨.
trie.autoComplete(&quot;pro&quot;);
console.log(&quot;&quot;);

trie.autoComplete(&quot;br&quot;);
console.log(&quot;&quot;);

// 단어 탐색
let word = trie.search(&quot;proud&quot;);
word ? console.log(word.value) : console.log(&quot;찾는 단어가 없습니다.&quot;);
console.log(&quot;&quot;);

// 단어 탐색
word = trie.search(&quot;proudddd&quot;);
word ? console.log(word.value) : console.log(&quot;찾는 단어가 없습니다.&quot;);</code></pre>
<h1 id="느낀점">느낀점</h1>
<p>DP에 대해서는 완벽하게 부족하다는 걸 느꼈고, 백트래킹은 여러 문제를 더 풀어보면 좋을 것 같았고, 과제를 통해 앞으로 있을 프로젝트에 검색 기능을 넣는다면 직접 자동완성 기능을 구현하여 넣어보고 싶었다.</p>
<p>하나씩 하나씩 결과물이 나오니 점점 재미있어진다. 더욱 열심히 해봐야겠다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 데브코스 1~2주차 WIL]]></title>
            <link>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-1%EC%A3%BC%EC%B0%A8-WIL</link>
            <guid>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-1%EC%A3%BC%EC%B0%A8-WIL</guid>
            <pubDate>Tue, 03 Oct 2023 06:32:21 GMT</pubDate>
            <description><![CDATA[<h1 id="1주차-wil📘">1주차 WIL📘</h1>
<p>지난 6,7 일차 강의에서는 트리, 힙, 트라이, 정렬, 이분 탐색, DFS, BFS, 그리디 등 다양한 자료 구조와 알고리즘에 대해 배웠다. 또한, 강의에서 배운 자료 구조와 알고리즘을 이용해 코딩테스트 문제를 푸는 시간을 가졌었다. 대부분의 문제들을 짧다면 15분 길게는 50분 ~ 1시간안에 풀었지만 여전히 엣지 케이스에 대한 인지가 많이 부족하다는 걸 느꼈다.(6, 7일차 TIL은 건너뜀..!)</p>
<h2 id="1주차-회고">1주차 회고</h2>
<p>스코프와 클로저, 메모리 심화 암호화, 네트워크 기초(URL을 입력하면 발생하는 일), 이벤트 루프 등 아주 얕은 지식만을 가지고 있었는데, 강의를 통해 다시 한번 짚고 넘어가고 모르는 부분에 대해서는 구글링과 코어 자바스크립트 와 모딥다 책을 통해 이해하고 넘어갔다. 하지만 클로저와 프로토타입에 대한 부분은 좀 더 깊은 공부가 필요할 것 같다.</p>
<h2 id="2주차-회고">2주차 회고</h2>
<p>2주차는 대부분의 강의가 자료구조와 알고리즘 이론 및 이론을 바탕으로 코딩테스트 문제를 푸는것이었다. </p>
<p>코딩테스트 준비를 길게 한건 아니었지만 데브코스가 붙고 1~2주 정도 코딩테스트 문제를 풀지 않았는데 잠깐 안풀었다고 그새 머리가 굳어 버린 것 같았다. 이번에 문제를 풀면서 다시 한번 느꼈지만 항상 레벨 별 내가 정해놓은 시간(Lv.2:40분 Lv.3:1시간)내에 아이디어를 짜고 아이디어를 바탕으로 적은 코드는 작동을 잘 하는데 엣지 케이스에 대한 사고가 부족해서 인지 자꾸만 한번에 통과를 하지 못했다. 문제를 보고 아이디어를 잘 떠 올릴 수 있게, 그리고 엣지 케이스에 대한 사고를 기르는 연습이 필요한 것 같다.</p>
<p>그리고 직접 자료구조들을 JS로 구현해보니 다시 한번 자료구조에 대한 복기가 되어 좋았던 시간이었다.</p>
<p>추가로 2주차 부터는 모딥다 스터디를 진행하였는데, 확실히 모딥다 스터디를 진행함으로써 JS에 대한 기본기를 탄탄하게 할 수 있을 것 같아 모딥다 스터디를 하길 잘 한 것 같다.</p>
<h2 id="아쉬웠던-점">아쉬웠던 점</h2>
<p>1~2주차를 진행하면서 아쉬웠던 점은 git을 잘 사용하지 못했던 것이다. </p>
<p>지금까지 git을 제대로 사용해 본 경험이 없어서 간단한 git의 명령어만 알고 있었는데 스터디를 할 때 git을 쓰려고 하는데 계속 막혀서 팀원들이 도움을 주셨다. 유림님의 git강의도 들었고 추가로 git을 공부해 앞으로 있을 스터디와 팀 프로젝트 및 과제에서 막힘 없이 git을 사용하는 것이 목표다.</p>
<h2 id="좋았던-점">좋았던 점</h2>
<p>확실히 혼자 공부를 하는 것 보다 함께 공부하는 팀원들이 있어 나 역시 스스로 더 하게 되는 것 같다. </p>
<p>팀원들에 비해 내가 부족한 부분 또는 서로가 해결한 부분에 대해 이야기를 나누며 부족한 부분은 채우고, 서로가 같은 문제를 해결했다면 서로의 해결안에 대해 이야기하며 좀 더 지식의 폭이 넓어지는 것 같았다. 성격상 내가 알고 있는 것을 남한테 공유하는 것을 좀 좋아하는데(별로 아는건 없지만..😥), 앞으로 많이 학습해서 팀원들에게 내가 아는 지식을 많이 공유하고 싶다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 데브코스 4일차 TIL]]></title>
            <link>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-4%EC%9D%BC%EC%B0%A8-TIL</link>
            <guid>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-4%EC%9D%BC%EC%B0%A8-TIL</guid>
            <pubDate>Tue, 26 Sep 2023 16:27:52 GMT</pubDate>
            <description><![CDATA[<p>데브코스 4일차
JS를 기반으로 큐와 해시 테이블, 그래프 등 전반적인 자료구조와 알고리즘에 대해 배웠다.</p>
<p>대부분 알았던 내용이었으나 큐를 직접 class를 이용해서 구현해 본 것은 처음이었다. 추가로 큐를 연결리스트를 이용하여 구현할 수 있다는 것도 처음 알았다.</p>
<p>오늘은 배운 자료구조와 알고리즘을 토대로 코딩테스트 문제를 풀었으므로 문제에 대한 리뷰를 적어보겠다.</p>
<h3 id="프로그래머스-프린터">프로그래머스 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42587">프린터</a></h3>
<pre><code class="language-javascript">// 큐
class Queue {
    constructor() {
        this.queue = [];
        this.front = 0;
        this.rear = 0;
    };
    // 큐 삽입
    enqueue(value) {
        this.queue[this.rear++] = value; 
    };
    // 큐 제거
    dequeue() {
        let value = this.queue[this.front];
        delete this.queue[this.front];
        this.front++;

        return value;
    };
    // 큐 리턴
    display() {
        return this.queue;
    };
    // 큐 사이즈
    size() {
        return this.rear - this.front;
    };
};

function solution(priorities, location) {
    let answer = [];
    let queue = new Queue();

    // 큐에 넣어주기
    priorities.forEach((e, i) =&gt; queue.enqueue([e, i]));

    // 큰 순서대로 변경
    priorities.sort((a, b) =&gt; b - a);

    let i = 0;
    while (queue.size()) {
        let process = queue.dequeue(); // [프로세스 중요도, 고유 번호]

        if (priorities[i] &gt; process[0]) queue.enqueue(process);
        else {
            answer.push(process)
            i ++;
        }
    };

    // location 실행 순서 확인
    for (let k = 0; k &lt; answer.length; k ++) {
        if (answer[k][1] === location) return k + 1;
    };
};
</code></pre>
<p>처음에는 문제에 접근할 때 먼저 <code>priorities</code>의 길이를 변수에 담아준 뒤, 문제에서 요구하는 조건을 반영하여 반복문의 종료 조건을 큐의 길이가 <code>priorities</code>의 길이와 같아지면 종료 되도록 설정하였다. </p>
<p>하지만 종료 조건을 위처럼 설정해보니 반복문 내의 로직이 너무 복잡해지고, 어디서 데이터가 잘못 들어갔는지 히든 테스트케이스 중 3개 정도가 실패가 떴다.</p>
<p>그래서 다시 코드를 지우고,</p>
<ol>
<li>큐에 <code>priorities</code>의 데이터를 넣어주고 <code>priorities</code>을 내림차순 정렬한 뒤,</li>
<li><code>i</code>라는 변수를 0으로 선언하여 <code>priorities</code>의 맨 첫번째 인덱스를 가리키게 하고,</li>
<li>큐가 빌때까지 반복문을 통해 프로세스의 중요도를 비교해 전역에 선언한 answer 배열에 넣어 location을 확인하여 답을 추출</li>
</ol>
<p>답안을 제출하고 히든 케이스를 모두 성공한 다음 강사님의 강의를 보았는데 다시 접근한 코드 설계가 어느정도 강사님의 코드 설계와 비슷해서 조금 뿌듯했다 ㅎㅎ</p>
<p>다시 한 번 느끼는 거지만 아이패드에 정말 꼼꼼하게 설계한 뒤 코드를 작성해야겠다. 코딩테스트 문제 해결 능력이 좋은건 아니지만, 한 동안 코딩테스트 연습을 안했더니 살짝 대충 설계하고 바로 키보드에 손이 올라간다..</p>
<h3 id="다음-문제-프로그래머스-베스트앨범">다음 문제 프로그래머스 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42579">베스트앨범</a></h3>
<pre><code class="language-javascript">function solution(genres, plays) {
    let answer = [];
    let genre = {}

    // 장르별 고유 번호별 재생횟수와 총 횟수
    genres.forEach((song, i) =&gt; {
        if (!genre[song]) {
            genre[song] = {
                [i]: plays[i],
                score: plays[i],
            }
        } else {
            genre[song][i] = plays[i];
            genre[song].score += plays[i];
        }
    });

    // 총 재생횟수 기준 정렬
    const object = Object.entries(genre);
    object.sort((a, b) =&gt; b[1].score - a[1].score);

    // 정렬된 객체 내의 재생횟수로 또 다시 정렬 후 재생횟수가 가장 많은 2가지 고유번호 answer에 push
    object.forEach(e =&gt; {
        let temp = Object.entries(e[1]);

        // 재생횟수로 정렬 후 재생횟수가 같다면 고유번호로 정렬
        temp.sort((a, b) =&gt; b[1] - a[1]);

        // 장르에 속한 곡이 하나라면 그 곡 선택해서 push
        if (temp.length === 2) answer.push(Number(temp[0][0]));
        else {
            // 정렬 상태가 맨 앞이 총 재생횟수이므로 index 1번부터 2개 탐색
            for (let i = 1; i &lt; 3; i ++) {
                answer.push(Number(temp[i][0]));
            }
        }
    });

    return answer;
}</code></pre>
<p>코드가 생각보다 깔끔하지는 않다..! 우선 문제<code>genres</code>에 들어있는 데이터를 장르를 <code>key</code>로 <code>고유번호</code> : <code>재생횟수</code>와 <code>score(총 재생횟수)</code> : <code>총 재생횟수</code> 로 <code>value</code>를 설정해 주었다. 
ex) { classic: {&#39;0&#39;: 50, &#39;1&#39;: 60, &#39;2&#39;: 100, &#39;3&#39;: 30, score: 240 }</p>
<p>이후, genre 객체를 정렬해 주기 위해 배열 형태로 형변환을 해 주었고, 총 재생 횟수를 기준으로 내림차순 정렬을 해주었다.</p>
<p>이어서 총 재생 횟수를 기준으로 정렬된 object 변수를 순회하여 한 장르 속에서 재생횟수가 가장 많은 2가이 고유번호를 뽑기 위해 temp에 다시 배열로 형변환하여 넣어주고 재생횟수를 기준으로 오름차순 정렬하여 값을 뽑아주었다(곡이 1개일때 예외 처리도 포함).</p>
<h2 id="느낀점">느낀점</h2>
<p>문제를 풀기전 항상 손으로 완벽하게 설계를 한 뒤에 키보드에 손을 대는 습관을 다시 길러야겠다. 그리고 문제를 풀었다면 다른 사람의 코드를 참고하여 다른 사람의 풀이를 보며 다양한 코드를 익히자(강사님의 풀이 과정을 보면서 너무너무 감탄했다...).</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 데브코스 3일차 TIL]]></title>
            <link>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-3%EC%9D%BC%EC%B0%A8-TIL</link>
            <guid>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-3%EC%9D%BC%EC%B0%A8-TIL</guid>
            <pubDate>Fri, 22 Sep 2023 13:07:46 GMT</pubDate>
            <description><![CDATA[<p>오늘 배운 내용</p>
<pre><code>1. 자료구조 &amp; 알고리즘 중요한 이유
2. 자료구조 &amp; 알고리즘의 종류
3. 시간복잡도
4. 배열
5. 연결리스트
6. 스택
7. 올바른 괄호 사용 및 괄호 문제 풀이</code></pre><span style="color: #FFB432">
  <h1>
    자료구조
  </h1>
</span>

<blockquote>
<p>메모리를 효율적으로 사용하며 빠르고 안정적으로 데이터를 처리하는 것이 궁극적인 목표. 상황에 따라 유용하게 사용될 수 있도록 특정 구조를 가지고 있음.</p>
</blockquote>
<p>자료구조는 <code>단순구조 -&gt; 정수, 실수, 문자열, 논리</code> , <code>선형 구조 -&gt; 배열, 연결리스트, 스택, 큐</code>,  <code>비선형 구조 -&gt; 트리, 그래프</code> 로 이루어져있다.</p>
<h2 id="선형구조">선형구조</h2>
<p>한 원소 뒤에 하나의 원소 만이 존재하는 형태. 자료들이 선형으로 나열되어 있는 구조.</p>
<h3 id="배열의-특징">배열의 특징</h3>
<ul>
<li>고정된 크기를 가지고 동적으로 크기를 늘릴 수 없다.<ul>
<li>자바스크립트처럼 대부분의 스크립트 언어는 동적으로 크기가 증감될 수 있다.</li>
</ul>
</li>
<li>원하는 원소의 index를 알면 O(1)로 원소를 찾음.</li>
<li>원소를 삭제하면 해당 index에 빈자리가 생김.</li>
</ul>
<h3 id="배열의-요소-삭제">배열의 요소 삭제</h3>
<p>삭제 후 순서를 맞추려면 최악의 경우 O(n)이 소요.</p>
<h3 id="배열의-요소-추가">배열의 요소 추가</h3>
<p>추가 후 순서를 맞추려면 최악의 경우 O(n)이 소요.</p>
<h3 id="연결리스트-특징">연결리스트 특징</h3>
<p>연결 리스트는 각 요소를 포인터로 연결하여 관리하는 선형 자료구조. 각 요소는 노드라고 부르고 데이터 영역과 포인터 영역으로 구성</p>
<ul>
<li>메모리가 허용하는 한 요소를 제한없이 추가 가능</li>
<li>탐색은 O(n) thdy</li>
<li>요소 추가하거나 제거할 때는 O(1) thdy</li>
<li>Singly Linked List, Doubly Linked List, Circular Linked List가 존재.</li>
</ul>
<h3 id="singly-linked-list">Singly Linked List</h3>
<p>Head에서 Tail까지 단방향으로 이어지는 연결리스트로 가장 단순한 형태의 연결리스트이다.</p>
<h3 id="doubly-linked-list">Doubly Linked List</h3>
<p>양방향으로 이어지는 연결 리스트. Singly Linked Lis보다 자료 구조의 크기가 조금 더 크다.</p>
<h3 id="배열과-연결리스트의-차이점">배열과 연결리스트의 차이점</h3>
<h2 id="비선형-구조">비선형 구조</h2>
<p>원소 간 다대다 관계를 가지는 구조로 계층적 구조나 망형 구조를 표현하기 적절.</p>
<span style="color: #FFB432">
  <h1>
    알고리즘
  </h1>
</span>

<blockquote>
<p>특정 문제를 효율적이고 빠르게 해결하는 것이 궁극적인 목표. 정해진 일련의 절차나 방법을 공식화한 형태로 표현한 것을 말한다.</p>
</blockquote>
<h2 id="그렇다면-자료구조와-알고리즘은-왜-중요할까">그렇다면 자료구조와 알고리즘은 왜 중요할까?</h2>
<p><strong>실무에서 중요하게 생각하는 3가지</strong></p>
<blockquote>
<ol>
<li>기초 코딩 능력 - 논리적 사고 중요</li>
<li>전문 분야 지식</li>
<li>기본 CS 지식</li>
</ol>
</blockquote>
<p><strong>문제 해결 능력에서 중요한 3가지</strong></p>
<blockquote>
<ol>
<li>논리적 사고</li>
<li>전산화 능력</li>
<li>엣지 케이스 탐색</li>
</ol>
</blockquote>
<span style="color: #FFB432">
  <h1>
       시간 복잡도
  </h1>
</span>

<h3 id="빅오-표기법">빅오 표기법</h3>
<blockquote>
<p>O(1) &lt; O(log n) &lt; O(n) &lt; O(n log n) &lt; O(n2) &lt; O(2n) &lt; O(!) 순</p>
</blockquote>
<p>빅오 표기법엔 4가지 법칙이 존재한다</p>
<ol>
<li>계수 법칙</li>
<li>덧셈 법칙</li>
<li>곱셈 법칙</li>
<li>다항 법칙</li>
</ol>
<h3 id="자바스크립트에서-성능을-확인하는-방법">자바스크립트에서 성능을 확인하는 방법</h3>
<p><strong>Date 객체 이용</strong></p>
<pre><code class="language-javascript">const start = new Date().getTime();

~~~~~~

const end = new Date().getTime();
console.log(end - start);</code></pre>
<span style="color: #FFB432">
  <h1>
       연결 리스트 구현
  </h1>
</span>

<p><strong>Doubly Linked List</strong> 기반 <strong>Circular Linked List</strong></p>
<pre><code class="language-javascript">class Node {
    constructor(value) {
        this.value = value;
        this.next = null;
        this.pre = null;
    }
}

class DoublyLinkedList {
    constructor() {
        this.head = null;
        this.tail = null;
        this.length = 0;
    }

    // 탐색하기
    find(data) {
        let curNode = this.tail;

        // 원하는 데이터를 찾을 때까지 무한 반복
        while (curNode.value !== data) {
            // 예외 처리 부분
            if (curNode.pre === this.tail) return false;
            curNode = curNode.pre
        }

        // 찾고자하는 데이터 리턴
        return curNode;
    }

    // 데이터 추가
    append(newData) {
        const newNode = new Node(newData);
        // 연결리스트가 비어있을 경우 새로 들어온 데이터가 head이자 tail
        if (this.head === null) {
            this.head = newNode;
            this.tail = newNode;
        } else { 
          // 리스트를 tail 쪽으로 계속 생성해 나가 tail이 head를 바라보게 하면 무한루프에 빠짐
          // 무한루프에 빠지지 않게 하기 위해 head의 pre가 tail을 바라보게 설정
            this.head.pre = newNode;
            this.tail.next = newNode;
            newNode.pre = this.tail;
            this.tail = newNode;
        }

        // 개수 증가
        this.length ++;
    }

    // 데이터 중간 삽입
    insert(node, newData) {
        // 예외 처리
        if (node === false) return console.log(&quot;찾으려는 데이터를 찾지 못해 데이터를 삽입할 수 없습니다.&quot;);

        const newNode = new Node(newData);
        // 새로 들어온 노드가 이전 노드의 다음 노드를 가리키고
        newNode.next = node.next;
        // 새로 들어온 노드는 이전 노드를 가리키고
        newNode.pre = node;
        // 다음 노드는 새로 들어온 노드를 가리키고
        newNode.next.pre = newNode;
        // 이전 노드는 새로 들어온 노드를 가리킨다
        node.next = newNode;

        // 개수 증가
        this.length ++;
    }

    // 데이터 삭제 (데이터 삭제를 위해 데이터 탐색부터 하는 로직 시간복잡도 O(n))
    remove(data) {
        let preNode = this.tail;
        // 원하는 데이터 찾을때까지 무한 반복
        while (preNode.pre.value !== data) {
            // 예외 처리 부분
            if (preNode.pre.pre === this.tail) return console.log(&quot;삭제하려는 데이터를 찾을 수 없습니다.&quot;);
            preNode = preNode.pre
        }
        // 찾고자하는 데이터를 찾았으면 현재 데이터가 찾은 데이터의 다음 데이터를 가리키게 만듬 -&gt; 추후 JS의 가비지 컬렉터에 의해 사용되지 않는 찾은 데이터는 사라지게 됨.
        if (preNode.pre !== null) {
            preNode.pre = preNode.pre.pre;
            preNode.pre.next = preNode; 
            // 개수 감소
            this.length --;
        }
    }

    dispaly() {
        let curNode = this.head;
        let displayString = &quot;[&quot;;
        while (curNode !== null) {
            displayString += `${curNode.value}, `;
            curNode = curNode.next;
        }

        displayString = displayString.substr(0, displayString.length - 2);
        displayString += &quot;]&quot;;
        console.log(displayString);
    }

    size() {
        console.log(this.length);
    }
}

const linkedList = new DoublyLinkedList();
linkedList.append(1);
linkedList.append(2);
linkedList.append(3);
linkedList.append(4);
linkedList.append(5);
linkedList.dispaly();

linkedList.remove(10);
linkedList.insert(linkedList.find(100), 200)
linkedList.dispaly();
linkedList.remove(200);
linkedList.dispaly();
linkedList.size();</code></pre>
<p>실행 결과</p>
<pre><code>[1, 2, 3, 4, 5]
삭제하려는 데이터를 찾을 수 없습니다.
찾으려는 데이터를 찾지 못해 데이터를 삽입할 수 없습니다.
[1, 2, 3, 4, 5]
삭제하려는 데이터를 찾을 수 없습니다.
[1, 2, 3, 4, 5]
5</code></pre><span style="color: #FFB432">
  <h1>
       올바른 괄호 문제
  </h1>
</span>

<pre><code class="language-javascript">function solution(s){
    let stack = [];

    for (let bracket of s) {
        // 열린 괄호일 때 stack에 삽입
        if (bracket === &quot;(&quot;) stack.push(&quot;(&quot;);
        else {
            // 닫힌 괄호일 때 스택의 길이가 0이면 올바른 괄호가 나올 수 없으므로 false 리턴
            if (stack.length === 0) return false;
            // 스택의 길이가 1 이상이면 pop
            else stack.pop();
        }
    };
    // 열린 괄호가 남아 있으면 false 그렇지 않으면 true
    return stack.length &gt; 0 ? false : true;
};</code></pre>
<h1 id="느낀점">느낀점</h1>
<p>오늘은 대부분 알았던 내용이라 어렵진 않았지만, 연결리스트를 직접 구현해본지 너무 오래돼서 다시 구현하는데 시간이 좀 오래걸렸다. 기본부터 다시 열심히하자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프론트엔드 데브코스 2일차 TIL (추가)]]></title>
            <link>https://velog.io/@choi-ik/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-2%EC%9D%BC%EC%B0%A8-TIL-%EC%B6%94%EA%B0%80</link>
            <guid>https://velog.io/@choi-ik/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-2%EC%9D%BC%EC%B0%A8-TIL-%EC%B6%94%EA%B0%80</guid>
            <pubDate>Fri, 22 Sep 2023 05:58:49 GMT</pubDate>
            <description><![CDATA[<p>이벤트 루프, 쿠키, 세션, 웹 스토리지, 모듈, 정규표현식(이건 외우지 말고 그냥 어떠한 기능이 있는지 이해하고 넘어간 뒤에 필요할 때마다 보고 사용하련다..) 
어제 다 하지 못한 2일차 강의 내용을 추가로 학습한 내용이다. 강사님께서 추가로 찾아보라고 하셨던 내용도 함께 작성했다!</p>
<span style="color: #FFB432">
  <h1>
    HTTPS의 탄생 이유
  </h1>
</span>

<hr>
<h3 id="http-란">HTTP 란</h3>
<blockquote>
<p><code>서버/클라이언트</code> 모델을 따라 데이터를 주고 받기 위한 프로토콜.</p>
</blockquote>
<p>즉 <code>HTTP</code>는 인터넷에서 하이퍼텍스트를 교환하기 위한 통신 규약으로, <code>80</code>번 포트를 사용하고 있으며 HTTP 서버가 80번 포트에서 요청을 기다리고 있으며 클라리언트는 <code>80</code>번 포트로 요청을 보내게 된다.</p>
<h3 id="https-가-나온-이유">HTTPS 가 나온 이유</h3>
<p>HTTP는 암호화가 되지 않은 평문 데이터를 전송하는 프로토콜이기 때문에, HTTP로 비밀번호나 주민등록번호 등을 주고 받으면 제 3자가 정보를 조회할 수 있어 이러한 문제를 해결하기 위해 HTTPS가 등장하게 되었다.</p>
<h3 id="https-란">HTTPS 란?</h3>
<blockquote>
<p><code>HyperText Transfer Protocol over Secure Socket Layer</code>, <code>HTTP over TLS</code>, <code>HTTP over SSL</code>, <code>HTTP Secure</code> 등으로 불리는 HTTPS는 <text style="color: red"> HTTP에 데이터 암호화가 추가된 프로토콜</text>이다. HTTPS는 HTTP와 다르게 <code>443</code>번 포트를 사용하며, 네트워크 상에서 중간에 제3자가 정보를 볼 수 없도록 암호화를 지원하고 있다.</p>
</blockquote>
<p>HTTPS의 보안은 SSL에서 담당하는데 SSL은 클라이언트와 서버가 서로 데이터를 암호화 해 통신할 수 있도록 돕는 보안 계층이다. OSI 계층 구조로 보자면 HTTPS는 아래 그림과 같이 HTTP 계층 아래에 SSL 이라는 보안 계층이 추가된 모습이다.
<img src="https://velog.velcdn.com/images/choi-ik/post/f29b4e12-4adf-4cf1-8d6e-4cbccd2e7b2b/image.png" width= "40%" height="40%"/></p>
<p>그럼 SSL은 어떤 방식으로 있을까? SSL은 <code>대칭키</code> 와 <code>비대칭키</code> 암호화 방식을 모두 사용하고 있다.</p>
<h3 id="대칭-키-암호화">대칭 키 암호화</h3>
<ul>
<li>클라리언트와 서버가 동일한 키를 사용해 암/복호화를 진행</li>
<li>키가 노출되면 매우 위험 / 연산 속도 빠름<h3 id="비대칭-키-암호화">비대칭 키 암호화</h3>
</li>
<li>1개의 쌍으로 구성된 공개키와 개인키를 암/복호화 하는데 사용</li>
<li>키가 노출되어도 비교적 안전 / 연산 속도 느림<ul>
<li>비대칭 키 암호화는 공개/개인 키 암호화 방식을 이용. 공개키와 개인키는 서로를 위한 1쌍의 키.<ul>
<li><strong>공개 키</strong>: 모두에게 공개 가능한 키</li>
<li><strong>개인 키</strong>: 나만 가지고 알고 있어야 하는 키</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="실제-https-연결-과정이-성립되는-흐름">실제 HTTPS 연결 과정이 성립되는 흐름</h3>
<ol>
<li><p>클라이언트가 서버로 최초 연결 시도</p>
</li>
<li><p>서버는 공개 키(인증서)를 브라우저에게 넘김</p>
</li>
<li><p>브라우저는 인증서의 유효성을 검사하고 세션키 발급</p>
</li>
<li><p>브라우저는 세션키를 보관, 추가로 서버의 공개키로 세션키를 암호화하여 서버로 전송</p>
</li>
<li><p>서버는 개인키로 암호화된 세션키를 복호화하여 세션키 얻음</p>
</li>
<li><p>클라이언트와 서버는 동일한 세션키를 공유하므로 데이터를 전달할 때 세션키로 암/복호화 진행</p>
</li>
</ol>
<h3 id="정리">정리</h3>
<blockquote>
<p>HTTP는 암호화가 추가되지 않았기 때문에 보안에 취약한 반면, HTTPS는 안전하게 데이터를 주고받을 수 있다. 하지만 HTTPS를 이용하면 암호화/복호화의 과정이 필요하기 때문에 HTTP보다 속도가 느리다(현재는 별 차이 없다).</p>
</blockquote>
<span style="color: #FFB432">
  <h1>
    이벤트 루프
  </h1>
</span>

<hr>
<p>강의에서 들었던 이벤트 루프의 설명을 토대로 더 나아가 <code>MicroTask Queue</code> 와 <code>Animation Frames</code>에 대해 학습해보았다. </p>
<p><code>Task Queue</code>는 Web API가 수행한 비동기 함수를 넘겨받아 Event Loop가 해당 함수를 Call Stack에 넘겨줄 때까지 비동기 함수들을 쌓아놓는 곳이다.(setTimeout(), setInterval(), setImmediate() 등등)</p>
<p>하지만 <code>Task Queue</code>가 유일한 Queue가 아니며, 이벤트 루프는 브라우저에 존재하는 여러 Queue들에 우선순위를 부여해 어떤 task를 먼저 수행할지 결정한다.</p>
<h3 id="microtask-queue">MicroTask Queue</h3>
<ul>
<li><p>Microtask Queue는 Promise나 async/await, process.nextTick, Object.observe, MutationObserver과 같은 비동기 호출을 넘겨받는다.</p>
</li>
<li><p>그리고 MicroTask의 우선순위는 일반 Task 보다 더 높다.</p>
</li>
<li><p>MicroTask 큐는 일반 Task 큐 보다 높은 우선순위를 가지고 있다.</p>
</li>
</ul>
<p>아래 처럼 Promise로 비동기 호출을 하면 해당 작업은 MicroTask 큐에 쌓이게 되고, setTimeout 보다 먼저 실행되어 먼저 출력이 된다.</p>
<pre><code class="language-javascript">// 1. 실행
console.log(&#39;script start&#39;)

// 2. task queue로 전달
setTimeout(function() {
  // 8. task 실행
  console.log(&#39;setTimeout&#39;)
}, 0)

// 3. microtask queue로 전달
Promise.resolve()
  .then(function() {
    // 5. microtask 실행
    console.log(&#39;promise1&#39;)
    // 6. microtask queue로 전달
  })
  .then(function() {
    // 7. microtask 실행
    console.log(&#39;promise2&#39;)
  })

// 4. 실행
console.log(&#39;script end&#39;)</code></pre>
<p>실행 순서</p>
<pre><code>script start
script end
promise1
promise2
setTimeout</code></pre><h3 id="animation-frames">Animation Frames</h3>
<ul>
<li>Animation Frames는 requestAnimationFrame과 같이 브라우저 렌더링과 관련된 task를 넘겨받는 Queue이다.</li>
<li>우선순위는 MicroTask보다 낮고, 일반 Task 보다는 높다.</li>
</ul>
<p>아래 코드로 확인해 보자</p>
<pre><code class="language-javascript">// 1. 실행
console.log(&quot;script start&quot;);

// 2. task queue로 전달
setTimeout(function () {
  // 10. task 실행
  console.log(&quot;setTimeout&quot;);
}, 0);

//3. microtask queue로 전달
Promise.resolve()
  .then(function () {
    // 6. microtask 실행
    console.log(&quot;promise1&quot;);
  }) // 7. microtask queue로 전달
  .then(function () {
    // 8. microtask 실행
    console.log(&quot;promise2&quot;);
  });

//4. AnimationFrame으로 전달
requestAnimationFrame(function () {
  //9. animation frame 실행
  console.log(&quot;animation&quot;);
});

//5. 실행
console.log(&quot;script end&quot;);</code></pre>
<p>실행 결과</p>
<pre><code>script start
script end
promise1
promise2
animation
setTimeout</code></pre><h3 id="정리-1">정리</h3>
<blockquote>
<p>이벤트 루프가 비동기 작업을 처리하는 우선순위는 <code>MicroTask Queue -&gt; Animation Frames -&gt; Task Queue</code> 순이다.</p>
</blockquote>
<blockquote>
<p><text style="color: red"><strong>또 한가지 중요한 것</strong>,</text> 이벤트 루프는 MicroTask Queue나 Animation Frames를 방문할 때는 큐 안에 있는 모든 작업들을 수행하지만, Task Queue를 방문할 땐 한 번에 하나의 작업만 call Stack으로 전달하고 Queue를 순회한다.</p>
</blockquote>
<span style="color: #FFB432">
  <h1>
    Indexed DB
  </h1>
</span>

<h3 id="indexed-db란">Indexed DB란</h3>
<blockquote>
<p>파일이나 블롭 등 많은 양의 구조화 된 데이터를 클라이언트에 저장하기 위한 로우 레벨 API</p>
</blockquote>
<h3 id="indexed-db-특징">Indexed DB 특징</h3>
<ul>
<li><p>IndexedDB 은 Transaction Database 를 사용하여 Key-Value 로 데이터를 관리하며, B-Tree 데이터 구조를 가진다.</p>
</li>
<li><p>IndexedDB 는 Transaction Model 을 따르며, 모든 변경은 Transaction 안에서 일어난다. 만약 Transaction 내에서 문제가 생긴다면, 모든 변경사항을 폐기되고 이전 상태로 돌아간다.</p>
</li>
<li><p>IndexedDB 은 same-origin policy 을 따른다 때문에, http 도메인에서 만든 IndexedDB 는 다른 https 도메인에서는 접근할 수 없다.</p>
</li>
<li><p>IndexedDB 데이터는 영속적으로 유지되지만, 특정 상황에 따라 삭제될 수 있다.</p>
</li>
</ul>
<h3 id="indexeddb-와-storage-차이">IndexedDB 와 Storage 차이</h3>
<ul>
<li><p>IndexedDB 많은 데이터를 저장하기 하고, 이를 Index 를 이용하여, 빠르게 검색할 수 있게 설계 되었다.</p>
</li>
<li><p>Storage 인 Local Storage 와 Session Storage 는 최대 10MB 만 저장이 가능하며, 오직 String 형태만 저장이 가능하다.</p>
</li>
<li><p>IndexedDB 는 javascript 가 이해하는 어떠한 값이라도 모두 저장할 수 있다.</p>
</li>
<li><p>IndexedDB 는 용량 제한은 특별히 없으나, HDD 저장소 상태 나 브라우저의 상태에 따라서 달라 질 수 있다.</p>
</li>
<li><p>시크릿 모드에서 IndexedDB, Storage 를 사용하면, 값은 저장되지 않고 브라우저 종료시 사라진다.</p>
</li>
<li><p>작은 규모의 데이터는 Storage 를 사용하는것이 좋지만, 큰 데이터는 IndexedDB 를 사용하는 것이 여러모로 유리하다.</p>
</li>
</ul>
<p>IndexedDB 요소와 사용 예제는 아래 사이트에 들어가면 확인이 가능하다.
<a href="https://pks2974.medium.com/indexeddb-%EA%B0%84%EB%8B%A8-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0-ca9be4add614">https://pks2974.medium.com/indexeddb-%EA%B0%84%EB%8B%A8-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0-ca9be4add614</a></p>
<hr>
<p><strong>참조 사이트</strong>
이벤트 루프: <a href="https://velog.io/@titu/JavaScript-Task-Queue%EB%A7%90%EA%B3%A0-%EB%8B%A4%EB%A5%B8-%ED%81%90%EA%B0%80-%EB%8D%94-%EC%9E%88%EB%8B%A4%EA%B3%A0-MicroTask-Queue-Animation-Frames-Render-Queue">https://velog.io/@titu/JavaScript-Task-Queue%EB%A7%90%EA%B3%A0-%EB%8B%A4%EB%A5%B8-%ED%81%90%EA%B0%80-%EB%8D%94-%EC%9E%88%EB%8B%A4%EA%B3%A0-MicroTask-Queue-Animation-Frames-Render-Queue</a>
HTTPS: <a href="https://brunch.co.kr/@swimjiy/47">https://brunch.co.kr/@swimjiy/47</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 데브코스 2일차]]></title>
            <link>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-2%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-2%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 21 Sep 2023 16:53:03 GMT</pubDate>
            <description><![CDATA[<h1 id="2일차">2일차</h1>
<p>오늘은 대부분 CS 지식에 대한 내용이 주를 이루었다. </p>
<p><code>네트워크, 컴퓨터 시간 원리, 암호화, 함수형 프로그래밍, 객체지향/프로토타입, 이벤트 루프, 모듈, 유니코드, 정규표현식, 쿠키와 세션/웹스토리지</code> 등에 대한 내용을 배웠다.</p>
<p>평소 프로젝트를 리액트를 사용하여 했던지라 <code>함수형 프로그래밍</code>은 어느정도 친숙했고, 알고리즘 문제를 풀 때도 큼지막한 기능들은 함수로 구현하려고 노력했었다. 이어 프로젝트에서 사용했던 <code>웹스토리지</code>와 알고리즘 문제를 풀며 사용했던 <code>정규표현식</code>도 조금씩 사용해봤던 경험이 있어 생소하지는 않았다.</p>
<p>오늘도 배웠던 모든 문제에 대해 다루는 것 보다 내가 어려웠던 내용에 대해 다뤄보겠다!</p>
<span style="color: #FFB432">
  <h1>
      브라우저에 URL을 입력하면 발생하는 일
  </h1>
</span>

<p><code>https://channy.creation.net/blog</code>을 예시로 보자.</p>
<h3 id="1-url-해석">1. URL 해석</h3>
<ul>
<li>통신 규약
<code>https://</code>는 통신 프로토콜이다. 이 스키마는 브라우저에 전송 계층 보안(TLS)를 사용하여 서버에 연결하도록 지시하며, TLS는 인터넷을 통한 통신을 보호하는 암호화 프로토콜이다. HTTPS를 사용하면 암호나 신용카드 정보와 같이 브라우저와 서버 간에 교환되는 데이터가 암호화 됨.</li>
<li>도메인
<code>channy.creation.net</code>은 웹 사이트의 도메인 이름이다. 기억하기 쉬운 주소이며 특정 서버의 IP 주소를 가리킨다.</li>
<li>경로
URL에 리소스에 대한 추가 경로가 있는 경우가 있다. ex) <code>https://channy.creation.net/blog/hello-world</code>의 경우 <code>blog</code>는 서버에서 요청된 리소스인    <code>hello-world</code>로 이어지는 경로이다. 이는 컴퓨터에 있는 디렉터리 구조처럼 생각할 수 있다.</li>
<li>리소스
위 주소에서 <code>hello-world</code>는 보려는 우베 사이트의 리소스 이름이다. <code>.html</code>과 같은 파일 확장자로 볼 수 있는데, 이는 HTML 콘텐츠가 있는 서버의 정적 파일임을 나타낸다.</li>
</ul>
<h3 id="2-dns-조회">2. DNS 조회</h3>
<p>브라우저는 인터넷에서 연결할 서버를 파악해야 한다. 입력한 도메인을 사용하여 웹 사이트를 호스팅하는 서버의 IP 주소를 조회해야 한다. DNS 조회를 사용해 이 작업을 수행한다.</p>
<p><U><em>DNS 기록을 찾기 위해 브라우저는 다음과 같은 네 개의 캐시를 확인한다.</em></U></p>
<ul>
<li><p>첫 번째, DNS 쿼리는 우선 브라우저 캐시를 확인한다. 브라우저는 내가 이전에 방문한 웹 사이트의 DNS 기록을 일정 기간 동안 저장하고 있다.</p>
</li>
<li><p>두 번째, 브라우저는 OS 캐시를 확인한다. 브라우저 캐시에 원하는 DNS 레코드가 없다면, 브라우저가 내 컴퓨터 OS에 시스템 호출(ex. 윈도우에서 gethostname 호출)을 통해 DNS 기록을 가져온다. (OS도 DNS 레코드 캐시를 저장하고 있다.)</p>
</li>
<li><p>세 번째, 브라우저는 라우터 캐시를 확인한다. 만약 컴퓨터에도 원하는 DNS 레코드가 없다면, 브라우저는 라우터에서 DNS 기록을 저장한 캐시를 확인한다.</p>
</li>
<li><p>마지막으로, ISP 캐시를 확인한다. 만약 위 모든 단계에서 DNS 기록을 찾지 못한다면, 브라우저는 ISP에서 DNS 기록을 찾는다. ISP(Internet Service Provider)는 DNS 서버를 가지고 있는데, 해당 서버에서 DNS 기록 캐시를 검색할 수 있다.</p>
</li>
</ul>
<h3 id="3-해당-ip가-존재하는-서버로-이동">3. 해당 IP가 존재하는 서버로 이동</h3>
<p>네트워크 장비 라우터를 이용해 이동한다.</p>
<h3 id="4-arp를-이용해-mac-주소-변환">4. ARP를 이용해 MAC 주소 변환</h3>
<p>논리 주소인 IP 주소를 물리 주소인 MAC 주소로 변환하는 프로토콜로 실제 통신을 위해 변하지 않는 고유한 MAC 주소가 필요하다.</p>
<p>네트워크 내에 ARP를 브로드캐스팅하면 해당 IP 주소를 가지고 있는 기기가 MAC 주소를 반환한다.</p>
<h3 id="5-브라우저와-해당-서버의-tcp-연결">5. 브라우저와 해당 서버의 TCP 연결</h3>
<p>브라우저가 올바른 IP 주소를 수신하면 IP 주소와 일치하는 서버와 연결해 정보를 전송한다. 브라우저는 인터넷 프로토콜을 이용하여 이러한 연결을 구축한다. 사용할 수 있는 여러가지 인터넷 프로토콜이 있지만, 일반적으로 HTTP 요청에서는 TCP라는 전송 제어 프로토콜을 사용함.</p>
<p>클라이언트와 서버 간 데이터 패킷을 전송하려면 TCP 연결을 해야하는데 <code>3 way handshake</code>라는 연결 과정을 통해 이뤄진다. 클라이언트와 서버가 SYN(연결 요청) 및 ACK(승인) 메시지를 교환하여 연결을 설정하는 3단계 프로세스를 아래에서 살펴보자.</p>
<ol>
<li><p>클라이언트는 인터넷을 통해 서버에 <code>SYN 패킷</code>을 보내 새 연결이 가능한지 여부를 묻는다.</p>
</li>
<li><p>서버에 새 연결을 수락할 수 있는 열린 포트가 있는 경우, <code>SYN/ACK 패킷</code>을 사용하여 SYN 패킷의 ACK(승인)으로 응답한다.</p>
</li>
<li><p>클라이언트는 서버로부터 <code>SYN/ACK 패킷</code>을 수신하고 <code>ACK 패킷</code>을 전송하여 승인한다.</p>
</li>
</ol>
<h3 id="6-브라우저가-웹-서버에-http-요청을-보낸다">6. 브라우저가 웹 서버에 HTTP 요청을 보낸다</h3>
<p>TCP 연결이 설정되면 데이터 전송이 시작되어 브라우저는 웹 페이지를 요청하는 <code>GET 요청</code>을 보낼 것이다.</p>
<p>만약 <code>자격 증명(credentials)</code>을 입력하거나 <code>form</code>을 제출하는 경우 <code>POST 요청</code>을 사용할 수 있다. 이 요청에는 <code>브라우저 식별(User-Agent 헤더)</code>, <code>수락할 요청 유형(Accept 헤더)</code> 및 <code>추가 요청을 위해 TCP 연결을 유지하라는 연결 헤더와 같은 추가 정보</code>도 포함된다. 또한 브라우저가 이 도메인에 대해 저장한 쿠키에서 가져온 정보도 전달한다.</p>
<h3 id="7-서버가-요청을-처리하고-응답을-보낸다">7. 서버가 요청을 처리하고 응답을 보낸다</h3>
<p>서버에는 웹 서버가 포함되어 있는데, 이는 브라우저로부터 요청을 수신하고, 해당 내용을 <code>requset handler</code>에 전달하여 응답을 읽고 생성하는 역할을 한다. <code>request handler</code>는 요청, 요청의 헤더 및 쿠키를 읽고 필요한 경우 서버의 정보를 업데이트하는 프로그램이다(NET, PHP, Ruby, ASP 등으로 작성됨). 그런 다음 response를 특정 포맷으로(JSON, XML, HTML)으로 작성한다.</p>
<h3 id="8-서버가-http-응답을-보낸다">8. 서버가 HTTP 응답을 보낸다.</h3>
<p>서버 응답에는 요청한 웹 페이지와 함께 상태 코드, 압축 유형, 페이지 캐싱 방법, 설정할 쿠키, 개인 정보 등이 포함된다.</p>
<p>상태 코드는 숫자로 표시가 되는데 숫자 코드를 사용하여 HTTP 응답 결과를 다섯 가지 상태로 나타낸다.</p>
<h3 id="9-브라우저가-html-컨텐츠를-보여준다">9. 브라우저가 HTML 컨텐츠를 보여준다.</h3>
<p>브라우저는 응답받은 HTML을 화면에 단계별로 표시한다. HTML을 읽어 DOM Tree를 구축하고, 만들어진 DOM Tree를 이용하여 화면에 그리고 스크립트를 실행한다.</p>
<span style="color: #FFB432">
  <h1>
     프로토타입
  </h1>
</span>

<h3 id="프로토-타입이란">프로토 타입이란?</h3>
<p>JS에는 기본 데이터 타입을 제외한 모든 것이 객체이다. 객체가 만들어지기 위해서는 자신을 만드는 데 사용된 원형인 프로토타입 객체를 이용하여 객체를 만든다. 이때 만들어진 객체안에 <code>__proto__</code> 속성이 자신을 만들어낸 원형을 의미하는 프로토아입 객체를 참조하는 숨겨진 링크가 있다. 이 숨겨진 링크를 프로토타입이라고 정의함.</p>
<p><img src="https://velog.velcdn.com/images/choi-ik/post/f9e5427e-8cc2-4649-bbaf-76781d0921ca/image.png" alt=""></p>
<pre><code class="language-javascript">function Person() {}

var joon = new Person();</code></pre>
<p>위 그림 joon 객체의 멤버인 <code>__proto__</code> 속성이 프로토타입 객체를 가리키는 숨은 링크가 프로토타입이라고 한다. 프로토타입은 크게 두 가지로 해석이 되는데, 함수의 멤버인 prototype 속성은 프로토타입 객체를 참조하는 속성이다. 그리고 함수와 new 연산자가 만나 생성한 객체의 프로토타입 객체를 지정해주는 역할을 한다. 객체 안의 <code>__proto__</code> 속성은 자신을 만들어낸 원형인 프로토타입 객체를 참조하는 숨겨진 링크로써 프로토타입을 의미한다.</p>
<p>JS에서는 숨겨진 링크가 있어 프로토타입 객체 멤버에 접근할 수 있다. 그래서 이 프로토타입 링크를 사용자가 정의한 객체에 링크가 참조되도록 설정하면 코드의 재사용/객체 지향적 프로그래밍을 할 수 있다.</p>
<h3 id="코드의-재사용">코드의 재사용</h3>
<p>재사용 하면 떠오르는 것은 상속. JS는 프로토타입 기반 언어로 프로토타입을 이용해 코드 재사용을 할 수 있다.</p>
<p>크게 두 가지 방법이 있는데 classical/prototypal 방식이 있다. classical 방식은 new 연산자를 통해 생성한 객체를 이용해 코드를 재사용하는 방법이며, prototypal 방식은 리터럴 또는 Object.create()를 이용하여 객체를 생성하고 확장해 가는 방식이다. classical 보다 간결하게 구현할 수 있어 JS에서 선호하는 prototypal에 대해서만 다루겠다.</p>
<span style="color: green">
  prototypal한 방식의 재사용
</span>

<p>Object.create()를 사용해 객체를 생성과 동시에 프로토타입 객체를 지정한다. 이 함수의 첫 번째 매개변수는 부모객체로 사용할 객체를 넘겨주고, 두 번째 매개변수는 선택적 매개변수로써 반환되는 자식 객체의 속성에 추가되는 부분이다. 이 함수를 사용함으로써 객체 생성과 동시에 부모 객체를 지정하여 코드의 재활용을 간단하게 구현할 수 있다.</p>
<pre><code class="language-javascript">var person = {
  type: &quot;인간&quot;,
  getType: function() {
    return this.type;
  },
  getName: function() {
    return this.name;
  }
};

var joon = Object.create(person);
joon.name = &quot;혁준&quot;;

console.log(joon.getType()); // 인간
console.log(joon.getName()); // 혁준</code></pre>
<p>위 소스에서 부모 객체에 해당하는 person을 객체 리터럴 방식으로 생성했으며 자식 객체 joon은 Object.create() 함수를 시용해 첫 번째 매개변수로 person을 넘겨받아 joon 객체를 생성하였다. 한 줄로 객체를 생성함과 동시에 부모 객체의 속성도 모두 물려받아 classical 방식보다 간단하다. JS에 서는 new 연산자와 함수를 통해 생성한 객체를 사용하는 classical 방식보다 prototypal 방식을 더 선호한다.</p>
<p><em><strong>프로토타입에 관한 그림 및 내용 참조</strong> : <a href="https://www.nextree.co.kr/p7323/">https://www.nextree.co.kr/p7323/</a></em>
위 사이트에 들어가면 classical 한 방식에 대해 알 수 있습니다. 보시면 왜 prototypal 방식을 선호하는지 이해가 잘 됩니다!</p>
<p>3일 차인 내일 강의는 어느정도 숙지가 된 내용이기에 아직 깊게 학습하지 못한 <code>이벤트 루프, 쿠키, 세션, 웹 스토리지, 모듈, 정규표현식(이건 외우지 말고 그냥 어떠한 기능이 있는지 이해하고 넘어간 뒤에 필요할 때마다 보고 사용하련다..)</code> 부분에 대해서는 내일 다시 학습하여 포스팅 할 예정이다 😁</p>
<h1 id="느낀점">느낀점</h1>
<p>CS에 대한 부분이 너무 약하다는 걸 느꼈고,  내가 사용하려는 언어나 기술의 동작원리에 대해 정확히 이해한다면, 알고리즘 문제 또는 프로젝트를 경험하며 실제 맞닥뜨릴 문제에 대한 해결 능력에 있어서 더욱 +가 될 것 같은 느낌이 많이 든다. 너무 이해가 안되는 내용이라면 질문하고, 그게 아니라면 오래 걸리더라도 하나씩 하나씩 이해하고 넘어가자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 데브코스 1일차]]></title>
            <link>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-1%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@choi-ik/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-1%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 21 Sep 2023 13:06:30 GMT</pubDate>
            <description><![CDATA[<h1 id="til-첫-시작">TIL 첫 시작</h1>
<p>어제는 OT 였고, 오늘이 첫 강의 시작이었다.</p>
<p>오늘 배운 내용은 변수, 상수, 자료형, 메모리, 표현식, 연산자, 흐름제어, 배열/객체, 스코프, 클로저 대부분 완벽히는 아니지만 어느정도 아는 내용이었다.</p>
<p>그 중, 잘 몰랐던 클로저에 대해 부족한 내용은 코어 자바스크립트 책과 구글링을 통해 학습하였다.</p>
<span style="color: #FFB432">
  <h1>
    클로저
  </h1>
</span>


<p><strong>클로저</strong>란 함수가 선언된 환경의 스코프를 기억하여 함수가 스코프 밖에서 실행될 때에도 기억한 스코프에 접근할 수 있게 만드는 문법이다.</p>
<pre><code class="language-javascript">function a(name) {
    const greet = &#39;hello, &#39;; // 지역 스코프라 함수가 종료되면 메모리에서 사라짐.

    return function () {
        console.log(greet + name);
    };
};

const world = a(&quot;world&quot;);
const ik = a(&quot;ik&quot;);

world(); // hello, world
ik(); // hello, ik</code></pre>
<p>전역 Lexical 환경에는 현재 각각 <code>a:(function)</code>과 <code>world:(function)</code>, <code>ik(function)</code>이 들어있다.</p>
<p>a의 Lexical 환경에는 <code>greet: ‘hello, ‘</code>가 들어있고,
a의 블록안에 존재하는 익명함수 Lexical 환경에는 선언된 것이 없다.</p>
<p>이제 코드의 결과를 봐보자.</p>
<p>순서대로 hello, world와 hello, ik이 출력될 것이다.</p>
<p>분명 가바지 컬렉터에 의해 greet 변수는 world와 ik에 익명함수를 리턴 함과 동시에 사라져야하는데 말이다.</p>
<p>하지만 이 상황에선 클로저로 인해 함수가 선언된 환경의 스코프를 기억하여 world와 ik이 참조하는 greet는 각각 “world”와 “ik”으로 메모리에서는 사라졌지만 선언될 당시의 스코프를 기억하여 각각 hello, world와 hello, ik을 출력한 것이다.</p>
<p>위 코드의 Lexical 환경이 참조하는 순서는 아래와 같다.</p>
<pre><code>💡 익명함수 Lexical 환경 →(참조) a Lexical 환경 →(참조) 전역 Lexical 환경</code></pre><p>위 순서로 현재 스코프에 존재하지 않는 변수를 상위 스코프(Lexical 환경)에서 찾는다.</p>
<hr>
<p>이어서 다음 코드의 결과에 대해서도 고민해보자.</p>
<pre><code class="language-javascript">function count() {
    let i = 0;
    for (i = 0; i &lt; 5; i ++) {
        setTimeout(function () {
            console.log(i);
        }, i * 100);
    };
};

count();</code></pre>
<p>처음에 setTimeout 안의 익명함수의 존재를 잊어버리고 당연하게 결과를 0, 1, 2, 3, 4가 나올 것이라 예상 하였지만, 애석 하게도 답은 5, 5, 5, 5, 5다.</p>
<p>클로저 중에서도 setTimeout을 사용한 부분이 가장 어려웠는데, 반복문의 상황에 대해 적어보겠다.</p>
<p>첫 번째 반복문 상황 → i의 값은 0이고, timer 함수가 작동하여 100ms 뒤, i의 값을 콘솔에 출력하도록 명령.</p>
<p>두 번째 반복문 상황 → i의 값은 1이고, timer 함수가 작동하여 100ms 뒤, i의 값을 콘솔에 출력하도록 명령.</p>
<p>세 번째 반복문 상황 → i의 값은 2이고, timer 함수가 작동하여 100ms 뒤, i의 값을 콘솔에 출력하도록 명령.</p>
<p>네 번째 반복문 상황 → i의 값은 3이고, timer 함수가 작동하여 100ms 뒤, i의 값을 콘솔에 출력하도록 명령.</p>
<p>마지막 반복문 상황 → i의 값은 4이고, timer 함수가 작동하여 100ms 뒤, i의 값을 콘솔에 출력하도록 명령.</p>
<span style="color: red">
        💡 이 상황에서 
</span>

<ol>
<li>각각의 setTimeout() 함수는 콜스택에 쌓임.</li>
<li>이어서 웹 API중 하나인 타이머가 생성되며, 익명함수는 Web APIs로 이동한다.</li>
<li>setTimeout()이 완료되고 콜스택에서 제거됨.</li>
<li>설정한 타이머가 지난 후 익명함수는 콜백 큐로 이동</li>
<li>이 작업이 5번 반복되고, for 루프는 종료 → 콜백 큐에는 5개의 익명함수가 차례로 쌓인 상태</li>
<li>루프가 모두 종료되어 현재 콜스택이 비어있는 상태이므로, 이벤트 루프는 콜백큐의 익명함수를 하나씩 콜스택으로 올림.</li>
<li>익명함수 실행 → 여기서 익명함수들이 참조해야하는 i는 이미 for 루프가 모두 돌면서 5로 변화된 상태. 따라서 콜백 큐에 있는 익명함수들이 참조하는 i의 값은 5이기 때문에 차례로 콜백큐의 익명함수가 실행 되면서 5가 5번 찍히는 것이다.</li>
</ol>
<span style="color: green">
        💡 이를 해결하기 위해
</span>

<ul>
<li><p>IIFE(즉시 실행 함수)를 사용하여 setTimeout 부분을 괄호로 감싸 즉시 실행 함수 형태로 만들어주면 별도의 스코프가 생성되어 해결 가능하다.</p>
<pre><code class="language-javascript">  let i = 0;
  for (i = 0; i &lt; 5; i++) {
      (function(){
          var innerI = i;
          setTimeout(function timer() {
              console.log(innerI);
          }, i * 100);
      })();
  }
  // 0, 1, 2, 3, 4</code></pre>
</li>
<li><p>또는 let 키워드를 for문 의 () 안에 넣는 것이다. 하나의 블록스코프를 가지고 하나의 반복문 마다 새로운 i를 각각의 익명함수들이 자신들이 선언된 스코프의 i 값을 참조하게 만드는 것이다.</p>
<pre><code class="language-javascript">  for (let i = 0; i &lt; 5; i++) {
      setTimeout(function timer() {
        console.log(i);
    }, i * 100);
  }
  // 0, 1, 2, 3, 4</code></pre>
</li>
</ul>
 <span style="color: #FFB432">
      <h2>
          그렇다면 클로저는 언제 사용할까?
      </h2>
 </span>

<p>상태를 안전하게 변경하고 유지하기 위해 사용한다.</p>
<p>다시 말해, 상태가 의도치 않게 변경되지 않도록 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용한다.</p>
<h1 id="느낀점">느낀점</h1>
<p>클로저에 대해서는 코어 자바스크립트 책을 보며 어느 정도 익혔다고 생각했는데 오산이었다. 개발 공부를 할 때 겉핥기식으로 공부하는 것이 아니라 깊게 파고들어야겠다고 생각했고, 오늘 JS에 대한 기본적인 내용의 강의를 들으며 더욱 기본에 충실해야겠다는 느낌을 받았다.</p>
]]></description>
        </item>
    </channel>
</rss>