<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>villain_uni.log</title>
        <link>https://velog.io/</link>
        <description>이제 그만 노력이란걸 해보자</description>
        <lastBuildDate>Tue, 01 Jul 2025 07:46:16 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>villain_uni.log</title>
            <url>https://velog.velcdn.com/images/villain_uni/profile/7f7b08b5-52f8-4c2a-a0ad-2b69dbed6659/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. villain_uni.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/villain_uni" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[퇴사하기전에 내가 짠 코드 리팩토링으로 개선하기]]></title>
            <link>https://velog.io/@villain_uni/%ED%87%B4%EC%82%AC%ED%95%98%EA%B8%B0%EC%A0%84%EC%97%90-%EB%82%B4%EA%B0%80-%EC%A7%A0-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81%EC%9C%BC%EB%A1%9C-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@villain_uni/%ED%87%B4%EC%82%AC%ED%95%98%EA%B8%B0%EC%A0%84%EC%97%90-%EB%82%B4%EA%B0%80-%EC%A7%A0-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81%EC%9C%BC%EB%A1%9C-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 01 Jul 2025 07:46:16 GMT</pubDate>
            <description><![CDATA[<h2 id="서론">서론</h2>
<p>퇴사 전 내 코드들을 지켜보는데, 기존 코드들은 UI 컴포넌트와 Hooks가 분리되지 않고, 컴포넌트 내부에 얽혀 있는 상태로 작성되어 있었다. 이는 가독성을 떨어뜨릴 뿐 아니라, 유지보수 과정에서 잦은 수정과 오류를 유발했다. 또한 테스트코드를 작성하려해도 로직이 분리되어있지 않아 해당 Hooks만 따로 분리하기 난감한 상황이었다.</p>
<p>그래서 회사 코드를 리팩토링 하는 작업을 진행하려한다.</p>
<blockquote>
<p>💡 코드 예시들은 실제 코드가 아닌 약간의 모방을 한 예시코드임을 알려드립니다.</p>
</blockquote>
<h2 id="그래서-어떻게-개선할껀데">그래서 어떻게 개선할껀데?</h2>
<p>리팩토링 하기에 앞서 일단 나 자신이 지켜야할 리팩토링 규칙을 세웠다.</p>
<ol>
<li><strong>역할과 책임을 명확히 분리</strong><ol>
<li>UI Components는 View 역할만, Hooks는 상태 로직을 관리한다.</li>
</ol>
</li>
<li><strong>의존성 역전 주의</strong><ol>
<li>하위 모듈에 상위 Components가 직접 의존하지 않도록 한다.</li>
<li>API와 해당 API를 이용한 ReactQuery Hooks를 직접 사용하지않고 분리하여 사용한다.</li>
</ol>
</li>
<li><strong>상수 분리</strong><ol>
<li>상수는 상수 폴더에 따로 관리한다.</li>
</ol>
</li>
<li><strong>Hooks의 관심사 분리 및 공통화</strong><ol>
<li>Hooks폴더에 Hooks를 저장할때 서로 관련된 관심사 끼리 묶어준다.</li>
<li>공통 Hooks는 Common과 같은 공통 폴더에 구분시켜 공통화를 진행한다.</li>
</ol>
</li>
<li><strong>UI Components는 최대한 Props만 받아서 렌더링하는 구조로 유지</strong></li>
<li><strong>테스트 할 수 있는 코드 작성</strong><ol>
<li>위 내용이 지켜진다면 테스트할 수 있는 코드가 되지않을까?</li>
<li>테스트 가능한 코드가 되었다면 테스트 코드도 작성한다.</li>
</ol>
</li>
</ol>
<p>위 규칙은 ChatGPT와 함께 정리한 것이며, 이를 최대한 지키며 가독성과 유지보수성을 높이는 방향으로 리팩토링을 진행하고자 한다.</p>
<h3 id="폴더-구조-변경">폴더 구조 변경</h3>
<p>일단 리팩토링에 앞서 가장 먼저 진행한 작업은 <code>Private folders</code> 구조를 분해하는 것 이였다.</p>
<p><a href="https://nextjs.org/docs/app/getting-started/project-structure#private-folders">Nextjs - Project-structure#private-folders</a></p>
<p>처음 도입 당시에는 nextjs에서 UI 로직과 라우팅 로직은 분리할때나 editor에서 파일 정렬 및 그룹화 하고싶을때 도입해보라는 내용을 참고하여 작성하였는데, 막상 사용하고보니 작성자 외에는 <code>_components</code> 와 <code>@/components</code> 의 구분이 잘 가지 않는다는 문제점이 있었다.</p>
<pre><code class="language-null">
src/
├── app/
│   ├── device/
│   │   ├── _components/
│   │   └── [deviceSn]/
│   ├── .../
│   │   ├── _components/
│   │   └── [...]/
│   └── .../
│       ├── _components/
│       └── [...]/
└── components/
    ├── device/
    │   ├── components.tsx
    │   ├── components.tsx
    │   └── *.tsx
    └── domain
        ├── components.tsx
        ├── components.tsx
        └── *.tsx</code></pre>
<p>또한 import 환경에서도 다소 복잡함을 유발하는 경우도 생겨났다.</p>
<pre><code class="language-javascript">import DeviceContainer from &#39;./_components/deviceContainer&#39;
import DeviceSearchForm from &#39;@/components/device/deviceSearchForm&#39;</code></pre>
<p>작성자는 이해할 수 있다 하더라도, 처음 이 코드를 접한사람은 다음과 같은 의문을 가지게 될 가능성이 있다.</p>
<blockquote>
<p>&quot;이게 무엇을 기준으로 나눈거지?&quot;
&quot;이렇게 나눔으로써 생기는 이점이 뭐지?&quot;
&quot;이 코드는 왜 여기에 작성되었지?&quot;</p>
</blockquote>
<p>그리고 나 조차도 장시간 해당 코드를 보지 않게 된다면 나중에 이러한 생각을 할 가능성이 높았고, 실제로 몇몇 코드는 위와 같은 생각이 들었다. 이는 유지보수하기 좋은 코드라고 생각이 들지 않았고, 이에 대해선 분명히 개선해야한다고 느꼈다.</p>
<h4 id="개선">개선</h4>
<p>폴더 구조 개선은 우리에게 익숙한 파일 중심의 구조로 변경하였다.</p>
<pre><code class="language-null">
src/
├── app/
│   ├── device/
│   │   └── [deviceSn]/
│   ├── .../
│   │   └── [...]/
│   └── .../
│       └── [...]/
├── components/
    ├── device/
    │   ├── components.tsx
    │   ├── components.tsx
    │   ├── _components의 컴포넌트.tsx
    │   ├── _components의 컴포넌트.tsx
    │   └── *.tsx
    └── ...domain
        ├── components.tsx
        ├── components.tsx
        ├── _components의 컴포넌트.tsx
        ├── _components의 컴포넌트.tsx
        └── *.tsx</code></pre>
<p>누구나 가장 자주 접하는 폴더 구조인 만큼 명확하게 구분짓기 좋다고 판단하여 위와 같이 개선하였다.</p>
<p>처음에는 항해 플러스 프론트엔드 스터디에서 알게 된 <code>FSD(Feature-Sliced Design)</code> 를 도입해볼까 고민했다. 확실히 관리자 사이트는 사이트 내에서 여러 도메인으로 갈라지기 때문에 FSD 아키텍쳐로 명확히 구분지어 폴더구조를 나눌수 있다고 생각이 들어서다.</p>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/d6bcaa50-f94c-4847-9d4e-3d91e4c3c1bb/image.jpeg" alt="중성마녀 짤"></p>
<p>막상 도입하려고 <code>features, entities, widgets, shared</code> 등등... 코드를 <code>Layer</code> 단위로 쪼개는 작업에서부터 막혔다.</p>
<ul>
<li>device 페이지를 예로 들면, device_list는 entity인가? 아니면 사용자의 액션(검색과 같은)에 의해 변하는 데이터이기때문에 features인가?</li>
<li>device_list를 보여주는 컴포넌트는 features, entity에서 데이터를 받아와 읽기만하는 widgets으로 구분해야하나? 그러면 features에는 device_list components가 존재하면 안되나?</li>
<li>device_detail 은 주 관리자 외에는 정보의 수정이 불가능하다. 그러면 이는 주관리자를 제외하면 손댈수 없으니 순수한건가? 아니면 어찌됫든 주 관리자가 손댈 수 있으니 features가 맞는건가?</li>
</ul>
<p>위와 같은 이슈들은 결국 도입하려는 나 조차도 FSD에 아직 완벽하게 이해못함을 의미했고, 또 단순히 도메인으로 나누기 쉽다고 무작정 도입할 수 있는 아키텍쳐가 아니라는 생각이 들었다.</p>
<blockquote>
<p>이런 이슈도 있었다.
nextjs는 v13.4 이후부터 정식 도입된 app router를 사용중인데 이는 FSD아키텍쳐의 app layer와도 충돌이 일어나는 부분이 있어 구조 변경을 해야한다는 문제점도 있었다.
<a href="https://feature-sliced.design/docs/guides/tech/with-nextjs">FSD 공식문서 - with nextjs</a></p>
</blockquote>
<p>FSD는 결국 더 나은 개발 환경을 위한 아키텍처다. 하지만 내가 제대로 이해하지 못한 상태에서 도입하고, 함께 협업하는 사람이 이를 불편하게 느낀다면 굳이 적용할 필요가 있을까 하는 생각이 들었다.</p>
<h3 id="역할과-책임-분리">역할과 책임 분리</h3>
<p>기존 코드들은 UI Components내에 상태관리, ReactQuery 호출 등 비즈니스 로직이 섞인 코드였다.</p>
<pre><code class="language-typescript">const DeviceSearchForm = () =&gt; {
  const [deviceInfo, setDeviceInfo] = useState&lt;IDeviceSearch | null&gt;();
  const getDeviceInfo = (data: IDeviceSearch | null) =&gt; {
    setDeviceInfo(data);
  };

  const formSchema = z.object({
    search: string()
  });
  const {
    register,
    handleSubmit,
    setError,
    formState: { errors },
  } = useForm({
    resolver: zodResolver(formSchema),
  });
  const mutateDeviceSearch = useGetDeviceSearch(setError, getDeviceInfo);
  const handleSubmitDeviceAdd = handleSubmit(async (data) =&gt; {
    mutateDeviceSearch.mutate({
      ...
    });
  });

  return (
    &lt;div className=&quot;w-full&quot;&gt;
      &lt;form className=&quot;my-5&quot; onSubmit={handleSubmitDeviceAdd}&gt;
        &lt;div className=&quot;flex items-start justify-center gap-4&quot;&gt;
          &lt;Label
            className=&quot;text-sm font-medium sr-only&quot;
          &gt;
            기기 검색 폼
          &lt;/Label&gt;
          &lt;div className=&quot;w-full&quot;&gt;
            &lt;Input
              {...register(&quot;search&quot;)}
            /&gt;
            {errors &amp;&amp; (
              &lt;p className=&quot;text-sm text-red-500 mt-1&quot;&gt;
                {typeof errors.message === &quot;string&quot; &amp;&amp;
                  errors.message}
              &lt;/p&gt;
            )}
          &lt;/div&gt;
          &lt;Button type=&quot;submit&quot;&gt;기기 검색하기&lt;/Button&gt;
        &lt;/div&gt;
      &lt;/form&gt;
      &lt;div className=&quot;mt-4&quot;&gt;
        &lt;h2 className=&quot;text-lg&quot;&gt;기기 정보&lt;/h2&gt;

        {deviceInfo &amp;&amp; (
          &lt;&gt;
            &lt;Separator className=&quot;my-4&quot; /&gt;
            &lt;DeviceAddContainer deviceInfo={deviceInfo} /&gt;
          &lt;/&gt;
        )}
      &lt;/div&gt;
    &lt;/div&gt;
  );
};</code></pre>
<p><code>deviceInfo의 상태</code> 와 <code>form과 관련된 로직들</code> 그리고 <code>form UI</code> 등.. 여러 코드가 섞여있어 로직에 대한 테스트도 힘들고, 한눈에 코드가 들어오지않아 다소 복잡하다. 또한 특정 로직을 수정하려하면 컴포넌트 전체의 수정이 이루어져야 하는 경우도 있어 개선이 필요하였다.</p>
<h4 id="개선-1">개선</h4>
<p>먼저 UI와 상태 관리 로직을 분리하기위해 <code>useDeviceSearchForm</code> 이라는 <code>Custom Hooks</code> 를 생성했다. 이후 1차적으로 상태값과 API로직 등을 옮겨주어 form에 필요한 상태나 액션들을 리턴해주었다.</p>
<pre><code class="language-typescript">export const useDeviceSearchForm = () =&gt; {
    const [deviceInfo, setDeviceInfo] = useState&lt;IDeviceSearch | null&gt;();
      const getDeviceInfo = (data: IDeviceSearch | null) =&gt; {
        setDeviceInfo(data);
      };

      const formSchema = z.object({
        search: string()
      });
      const {
        register,
        handleSubmit,
        setError,
        formState: { errors },
      } = useForm({
        resolver: zodResolver(formSchema),
      });
      const mutateDeviceSearch = useGetDeviceSearch(setError, getDeviceInfo);
      const handleSubmitDeviceAdd = handleSubmit(async (data) =&gt; {
        mutateDeviceSearch.mutate({
          ...
        });
      });
      return {
          deviceInfo,
          register,
          handleSubmit,
          setError,
          errors,
          handleSubmitDeviceAdd
      }
}</code></pre>
<p>이렇게 구조를 정리하고 보니, <code>zod</code> 와 <code>react-hook-form</code> 도 <code>methods</code> 로 묶어서 리턴하는 방식이 더 간결하겠다고 판단했다.</p>
<pre><code class="language-typescript">// hooks/device/useDeviceSearchForm.ts

export const useDeviceSearchForm = () =&gt; {
    const [deviceInfo, setDeviceInfo] = useState&lt;IDeviceSearch | null&gt;();
      const getDeviceInfo = (data: IDeviceSearch | null) =&gt; {
        setDeviceInfo(data);
      };

      const formSchema = z.object({
        search: string()
      });
      const methods = useForm({
        resolver: zodResolver(formSchema),
      });
      const mutateDeviceSearch = useGetDeviceSearch(methods.setError, getDeviceInfo);
      const handleSubmitDeviceAdd = methods.handleSubmit(async (data) =&gt; {
        mutateDeviceSearch.mutate({
          ...
        });
      });
      return {
          deviceInfo,
          methods,
          handleSubmitDeviceAdd
    }
}</code></pre>
<p>이후 <code>DeviceSearchForm</code> 도 <code>Container</code> 와 <code>Form</code> 으로 분리해주었다.</p>
<pre><code class="language-typescript">
// components/device/DeviceSearchContainer.tsx

const DeviceSearchContainer = () =&gt; {
  const {deviceInfo, methods, handleSubmitDeviceAdd} = useDeviceSearchForm();
  return (
    &lt;div className=&quot;w-full&quot;&gt;
      &lt;DeviceSearchForm
        register={methods.register}
        handleSubmitDeviceAdd={handleSubmitDeviceAdd}
        errors={methods.formState.errors}
      /&gt;
      &lt;div className=&quot;mt-4&quot;&gt;
        &lt;h2 className=&quot;text-lg&quot;&gt;기기 정보&lt;/h2&gt;

        {deviceInfo &amp;&amp; (
          &lt;&gt;
            &lt;Separator className=&quot;my-4&quot; /&gt;
            &lt;DeviceAddContainer deviceInfo={deviceInfo} /&gt;
          &lt;/&gt;
        )}
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

// components/device/DeviceSearchForm.tsx

export const DeviceSearchForm = ({
  register,
  handleSubmitDeviceAdd,
  errors,
}: {
  register: UseFormReturn&lt;IDeviceSearchForm&gt;[&quot;register&quot;];
  handleSubmitDeviceAdd: () =&gt; void;
  errors: UseFormReturn&lt;IDeviceSearchForm&gt;[&quot;formState&quot;][&quot;errors&quot;];
}) =&gt; {
  return (
    &lt;form className=&quot;my-5&quot; onSubmit={handleSubmitDeviceAdd}&gt;
        &lt;div className=&quot;flex items-start justify-center gap-4&quot;&gt;
          &lt;Label
            className=&quot;text-sm font-medium sr-only&quot;
          &gt;
            기기 검색 폼
          &lt;/Label&gt;
          &lt;div className=&quot;w-full&quot;&gt;
            &lt;Input
              {...register(&quot;search&quot;)}
            /&gt;
            {errors &amp;&amp; (
              &lt;p className=&quot;text-sm text-red-500 mt-1&quot;&gt;
                {typeof errors.message === &quot;string&quot; &amp;&amp;
                  errors.message}
              &lt;/p&gt;
            )}
          &lt;/div&gt;
          &lt;Button type=&quot;submit&quot;&gt;기기 검색하기&lt;/Button&gt;
        &lt;/div&gt;
      &lt;/form&gt;
  );
};</code></pre>
<p>이와 같이 개선함으로써 기존의 복잡하고 얽힌 코드들을 보다 간결하고 한눈에 들어오게 개선하였으며, <code>비즈니스 로직</code> 과 <code>UI</code> 가 분리됨으로써 비즈니스 로직에 대한 수정이 이루어져도 컴포넌트에서의 로직에 대한 의존성을 다소 덜어낼 수 있었다.</p>
<h3 id="의존성-역전-원칙-반영">의존성 역전 원칙 반영</h3>
<p>기존 코드들중 몇몇 코드는 <code>fetch를 이용해 직접 api를 호출</code> 하는식으로 api hooks가 작성된 코드들이 있었다.</p>
<pre><code class="language-typescript">export const useGetData = () =&gt; {
    return useQuery({
        queryKey: [&quot;datakey&quot;],
        queryFn: async () =&gt; await fetch(&quot;...api&quot;)
    })
}</code></pre>
<p>위와 같은 코드가 존재할 때 fetch에서 axios로 변경시 useGetData 까지 수정해야하는 번거로움이 생기게 되고, 코드 자체가 API에 의존적이게 되어버린다.</p>
<h4 id="개선-2">개선</h4>
<p>이를 개선하고자 <code>API를 service로 분리</code> 하고, 해당 service코드를 import함으로써 의존성을 제거하고, 테스트시에도 해당부분만 mock으로 바꿔주면 됨으로써 테스트에도 용이해졌다.</p>
<pre><code class="language-typescript">// hooks/useGetData.ts
export const useGetData = () =&gt; {
    return useQuery({
        queryKey: [&quot;datakey&quot;],
        queryFn: async () =&gt; await getData()
    })
}

// service/getData.ts
export const getData = async () =&gt; {
    const response = await axios.get(&quot;...api&quot;);
    return response.data;
}</code></pre>
<h3 id="hooks-관심사-분리">Hooks 관심사 분리</h3>
<p>기존에는 <code>hooks/*.ts</code> 형태로 훅들을 관리했는데, API 훅과 커스텀 훅이 섞여 있어 구조가 다소 지저분했다. 이를 개선하고자 <code>hooks의 관심사</code> 를 분리하였다.</p>
<pre><code class="language-null">hooks/
├── usePostUserApi.ts
├── useGetUserApi.ts
├── useDeleteUserApi.ts
├── usePatchUserApi.ts
├── useSearchDeviceForm.ts
├── useGetDeviceListApi.ts
├── usePostDeviceApi.ts
├── useGetDeviceDetail.ts
├── useSearchUserForm.ts 
└── ...*.ts</code></pre>
<h4 id="개선-3">개선</h4>
<p>우선 <code>API를 위한 Hooks</code> 인지 <code>state를 관리하기위한 custom Hooks</code> 인지 구분해주었다. 이후 해당 API Hooks를 도메인별로 한번 더 구분하여 Hooks의 관심사를 한눈에 볼 수 있도록 수정하였다.</p>
<pre><code class="language-null">hooks/
│   └── api/
│       ├── device/
│       │   ├── useGetDeviceListApi.ts
│       │   ├── usePostDeviceApi.ts
│       │   └── useGetDeviceDetail.ts
│       └── user/
│           ├── usePostUserApi.ts
│           ├── useGetUserApi.ts
│           ├── usePatchUserApi.ts
│           └── useDeleteUserApi.ts
├── useSearchDeviceForm.ts
├── useSearchUserForm.ts 
└── ...*.ts</code></pre>
<h2 id="결론">결론</h2>
<p>이번 리팩토링은 코드의 구조를 재정비하고 역할과 책임을 명확히 나눔으로써 <code>가독성과 유지보수성</code> 을 크게 향상시킬 수 있었던 작업이었다.
특히 UI와 로직, <code>API 계층을 분리하고 의존성</code> 을 낮추는 것만으로도 테스트가 쉬워지고, 추후 확장 시에도 부담이 줄어들었다.</p>
<p>비록 완전한 FSD 구조를 도입하진 않았지만, 현재 팀 상황과 프로젝트의 규모를 고려해 현실적인 선에서 개선 가능한 부분들을 명확히 분리하고 정리한 것이 더 큰 의미가 있었다고 생각한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[공간복잡도]]></title>
            <link>https://velog.io/@villain_uni/%EA%B3%B5%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84</link>
            <guid>https://velog.io/@villain_uni/%EA%B3%B5%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84</guid>
            <pubDate>Wed, 25 Jun 2025 07:48:48 GMT</pubDate>
            <description><![CDATA[<h1 id="공간복잡도">공간복잡도</h1>
<p>공간복잡도는 알고리즘이 실행되는 동안 사용되는 메모리의 양을 입력 크기(n)에 따라 Big-O 표기법으로 표현한 것 입니다.</p>
<blockquote>
<p>공간복잡도 = 입력 공간 + 추가 공간</p>
</blockquote>
<p>입력 공간: 함수 인자로 받은 배열, 입력데이터 등을 의미합니다.
추가 공간: 알고리즘 내부에서 새로 선언하는 변수나 ,배열, 재귀 호출, 스택 등을 의미합니다.</p>
<p>공간복잡도의 복잡성은 <code>추가 공간</code> 을 얼마나 사용하냐를 중심으로 분석합니다.</p>
<h2 id="공간복잡도-표">공간복잡도 표</h2>
<p>chatGPT를 통해 정리한 공간복잡도 표는 다음과 같습니다.</p>
<table>
<thead>
<tr>
<th>공간복잡도</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td>O(1)</td>
<td>단일 변수, 포인터만 사용</td>
</tr>
<tr>
<td>O(n)</td>
<td>배열, 해시맵, Set, 단일 For문을 이용한 저장 등 n개의 저장에 사용</td>
</tr>
<tr>
<td>O(log n)</td>
<td>이진 탐색의 재귀 호출 깊이</td>
</tr>
<tr>
<td>O(n²)</td>
<td>2차원 배열, 그래프 인접 행렬</td>
</tr>
<tr>
<td>O(n!)</td>
<td>순열 저장 시 사용, 최악의 케이스</td>
</tr>
</tbody></table>
<h2 id="공간복잡도-알아보기">공간복잡도 알아보기</h2>
<h3 id="o1">O(1)</h3>
<p>O(1)은 시간복잡도에서 처럼 단순히 1개의 변수 혹은 상수로만 처리 될 경우 입니다.</p>
<h4 id="예시">예시</h4>
<pre><code class="language-typescript">function sum(arr: number[]): number {
  let total = 0;
  for (let i = 0; i &lt; arr.length; i++) {
    total += arr[i]
  }
  return total;
}</code></pre>
<p>total 변수 1개만 사용하였기 때문에 입력 크기와 무관하게 고정된 공간을 사용합니다.</p>
<h3 id="on">O(n)</h3>
<p>입력 크기만큼 새 배열을 생성할 경우 입니다. 입력이 n이면 공간도도 O(n)이게 됩니다.</p>
<h4 id="예시-1">예시</h4>
<pre><code class="language-typescript">function copyArray(arr: number[]): number[] {
  return [...arr];
}</code></pre>
<p>새로운 배열을 생성하여 입력하고 리턴하기에 입력크기 n만큼 공간을 차지하는 모습을 볼 수 있습니다.</p>
<h3 id="olog-n">O(log n)</h3>
<p>입력이 줄어드는 과정(이진 탐색 등)에서 로그 깊이만큼 공간을 사용하기에 O(log n)이라는 복잡도를 가지게됩니다.</p>
<h4 id="예시-2">예시</h4>
<pre><code class="language-typescript">function binarySearch(arr: number[], k: number): boolean {
  const sortArr = arr.sort((a, b) =&gt; a - b);

  let low = 0;
  let high = sortArr.length - 1;

  while (low &lt;= high) {
    const mid = Math.floor((low + high) / 2);
    if (arr[mid] === k) return true;
    if (arr[mid] &lt; k) low = mid + 1;
    else high = mid - 1;
  }

  return false;
}</code></pre>
<p>시간복잡도에서 사용한 이진 탐색 예시입니다. low, high라는 O(1)의 값 외에 mid라는 값을 저장합니다. 그리고 해당 mid값은 탐색이 진행됨에 따라 log n 번 만큼 생성되게 됩니다. 이는 곧 O(log n)의 복잡도를 가진다고 볼 수 있습니다.</p>
<h3 id="on²">O(n²)</h3>
<p>2차원 배열이나 그래프 인접 행렬 등 2차원 구조를 저장할 경우 O(n²)라고 볼 수 있습니다.</p>
<h4 id="예시-3">예시</h4>
<pre><code class="language-typescript">function newArray(num: number): number[][] {
  const newNums: number[][] = [];
  for (let i = 0; i &lt; num; i++) {
    newNums[i] = []; // ← 초기화 먼저
    for (let j = 0; j &lt; num; j++) {
      newNums[i].push(j);
    }
  }
  return newNums;
}</code></pre>
<p>위와 같이 n번 순회하는 for문 속 새로운 n번만큼 순회하여 2차원 배열을 생성하는 코드는 n * n 만큼 공간을 차지하기에 O(n²) 라고 볼 수 있습니다.</p>
<h3 id="on-1">O(n!)</h3>
<p>팩토리얼을 의미하기도 하는 n!은 입력 n개로 만들 수 있는 모든 순열/배열 순서 조합을 생성하거나 저장할 때 사용하는 공간입니다.
가령 5라는 입력값이 있다면 5 x 4 x 3 x 2 x 1이 되어 120번의 생성 혹은 저장이라는 어마어마한 수치가 나오게됩니다.</p>
<h4 id="예시-4">예시</h4>
<pre><code class="language-typescript">// chatGPT 참고... 추후에 직접 구현해볼 예정.
function permute(arr: number[]): number[][] {
  const result: number[][] = [];

  const getPermutation = (path: number[], rest: number[]) =&gt; {
    if(rest.length === 0) {
      result.push([...path]);
      return;
    }
    for(let i = 0; i &lt; rest.length; i++) {
      const next = rest[i];
      const nextRest = [...rest.slice(0, i), ...rest.slice(i + 1)];
      getPermutation([...path, next], nextRest);
    }
  }

  return result;
}</code></pre>
<p>앞서 시간복잡도에서 gpt와 함께 구현한 순열 구하기 문제입니다.
함수를 재귀적으로 호출함으로써 n! 만큼 반복하게됩니다.</p>
<h2 id="여담">여담</h2>
<p>공간복잡도는 앞서 시간복잡도를 한번 익히고 나서 봐서 그런지 생각보다 이해하기 쉬웠습니다. 다만 팩토리얼의 경우 여전히 직접 구현하면 난해함을 겪을꺼 같아 더욱 깊이있는 공부와 수학적?지식도 필요할 것 같습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[시간복잡도]]></title>
            <link>https://velog.io/@villain_uni/%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84</link>
            <guid>https://velog.io/@villain_uni/%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84</guid>
            <pubDate>Fri, 20 Jun 2025 08:31:39 GMT</pubDate>
            <description><![CDATA[<h1 id="시간복잡도">시간복잡도</h1>
<p>시간복잡도는 입력 크기 n이 커질수록 알고리즘이 수행해야 하는 연산 횟수의 증가 정도를 표현한 것 입니다.
주로 Big-O 표기법을 이용해서 O(n), O(log n), O(n²) 등으로 표현합니다.</p>
<h2 id="시간복잡도-표">시간복잡도 표</h2>
<p>다음은 chatGPT를 이용해 시간복잡로를 표로 정리한 모습입니다.</p>
<table>
<thead>
<tr>
<th>표기</th>
<th align="left">이름</th>
<th align="center">예시</th>
</tr>
</thead>
<tbody><tr>
<td>O(1)</td>
<td align="left">상수 시간</td>
<td align="center">배열 인덱스 접근 - arr[3]</td>
</tr>
<tr>
<td>O(n)</td>
<td align="left">선형 시간</td>
<td align="center">단순 for문</td>
</tr>
<tr>
<td>O(n²)</td>
<td align="left">2차 시간</td>
<td align="center">이중 for문</td>
</tr>
<tr>
<td>O(n!)</td>
<td align="left">팩토리얼 시간</td>
<td align="center">순열/조합 완전탐색</td>
</tr>
<tr>
<td>O(2ⁿ)</td>
<td align="left">지수 시간</td>
<td align="center">피보나치 재귀</td>
</tr>
<tr>
<td>O(log n)</td>
<td align="left">로그 시간</td>
<td align="center">이진 탐색</td>
</tr>
<tr>
<td>O(n log n)</td>
<td align="left">로그 선형</td>
<td align="center">병합 정렬, 퀵 정렬</td>
</tr>
</tbody></table>
<hr>
<table>
<thead>
<tr>
<th>입력 크기(n)</th>
<th>1</th>
<th>10</th>
<th>100</th>
<th>1,000</th>
<th>10,000</th>
</tr>
</thead>
<tbody><tr>
<td>O(1)</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td>O(n)</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td>O(n²)</td>
<td>✅</td>
<td>✅</td>
<td>⚠️</td>
<td>❌</td>
<td>❌</td>
</tr>
<tr>
<td>O(n!)</td>
<td>✅</td>
<td>❌</td>
<td>❌</td>
<td>❌</td>
<td>❌</td>
</tr>
<tr>
<td>O(2ⁿ)</td>
<td>✅</td>
<td>⚠️</td>
<td>❌</td>
<td>❌</td>
<td>❌</td>
</tr>
<tr>
<td>O(log n)</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td>O(n log n)</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
<td>⚠️</td>
</tr>
</tbody></table>
<p>✅ : 통과
⚠️ : 위험
❌ : 시간초과</p>
<p>위 표가 정확히 무슨 의미인지 하나하나 접근해 보겠습니다.</p>
<h2 id="시간복잡도-알아보기">시간복잡도 알아보기</h2>
<h3 id="o1-상수-시간">O(1) 상수 시간</h3>
<h4 id="핵심키워드--단순연산-조건문-해시접근mapkey">핵심키워드 : 단순연산, 조건문, 해시접근(map[key])</h4>
<p>상수 시간은 특정 인덱스를 콕 찝어내기 때문에 입력크기 n과는 상관없이 일정한 시간이 걸립니다.</p>
<h4 id="특징">특징</h4>
<ul>
<li>가장 빠른 복잡도</li>
<li>테이블,배열에 직접 접근, 변수 연산 등</li>
</ul>
<h4 id="예시-문제">예시 문제</h4>
<blockquote>
<p>Q. 배열의 첫번째 요소를 반환하시오.</p>
</blockquote>
<pre><code class="language-typescript">function getFirstElement(arr: number[]): number {
  return arr[0]
}
getFirstElement([3,2,6,3,4,2,3]); // 입력크기와 상관없이 0번째 반환.</code></pre>
<hr>
<h3 id="on-선형-시간">O(n) 선형 시간</h3>
<h4 id="핵심키워드--단일-for문-filter-map-reduce-배열문자열-탐색-최대값-최소값">핵심키워드 : 단일 for문, filter, map, reduce, 배열/문자열 탐색, 최대값, 최소값</h4>
<p>선형시간은 입력 크기만큼 시간이 걸리는 알고리즘입니다. 주로 단일 for문에 사용됩니다.</p>
<h4 id="특징-1">특징</h4>
<ul>
<li>가장 많이 나오는 복잡도</li>
<li>단일 반복문의 형태</li>
</ul>
<h4 id="예시-문제-1">예시 문제</h4>
<blockquote>
<p>Q. 배열에서 가장 큰 값을 구하시오.</p>
</blockquote>
<pre><code class="language-typescript">function getBiggestNumber(arr: number[]): number {
  let result = 0;

  for(let i = 0; i &lt; arr.length; i++) {
    if(arr[i] &gt; result) {
      result = arr[i]
    }
  }

  return result;
}
getFirstElement([3,2,6,3,4,2,3]); // 입력크기 만큼 순회하여 가장 큰값을 반환.</code></pre>
<hr>
<h3 id="on²-2차-시간">O(n²) 2차 시간</h3>
<h4 id="핵심키워드--이중-for문-버블정렬">핵심키워드 : 이중 for문, 버블정렬</h4>
<p>2차 시간은 입력 요소마다 또다른 모든 요소를 순회하는 n개의 값을 가진 배열을 O(n)으로 순회할때 그안에서 새로 또 n개의 값을 가진 배열을 순회한다면 O(n * n) = O(n²) 이 되는 알고리즘입니다.</p>
<h4 id="특징-2">특징</h4>
<ul>
<li>이중 반복문에서 자주 발생</li>
<li>데이터가 조금만 커져도 제곱으로 시간이 늘어나기에 느려짐</li>
<li>완전탐색류, 브루트포스 등에서 사용</li>
</ul>
<h4 id="예시-문제-2">예시 문제</h4>
<blockquote>
<p>Q. 빨강, 흰색, 파랑 색의 객체가 n개인 배열이 주어졌을 때, 같은 색의 객체가 빨강, 흰색, 파랑 순서로 인접하도록 제자리에서 정렬하시오.
정수 0, 1, 2를 사용하여 각각 빨간색, 흰색, 파란색을 냄.</p>
</blockquote>
<pre><code class="language-typescript">function sortColors(nums: number[]): void {
  for(let i = 0; i &lt; nums.length; i++) {
    for(let j = 0; j &lt; nums.length - i - 1; j++) {
      if(nums[j] &gt; nums[j+1]) {
        let temp = nums[j];
        nums[j] = nums[j+1];
        nums[j+1] = temp;
      }
    }
  }
  console.log(nums);
};

sortColors([2,0,2,1,1,0]);</code></pre>
<p>nums의 length인 6번의 순회 속에서 또다시 nums의 length를 기반으로 i만큼 차감하여 반복하게됩니다.
이는 결국 n² 에 근접하게 됩니다.</p>
<hr>
<h3 id="on-팩토리얼-시간">O(n!) 팩토리얼 시간</h3>
<h4 id="핵심키워드--팩토리얼-조합-백트래킹모든-경우-탐색-외판원-문제">핵심키워드 : 팩토리얼, 조합 백트래킹(모든 경우 탐색), 외판원 문제</h4>
<p>n! 에서 알수 있다시피 입력한 n개를 모든 순열/조합으로 나열할 때 사용하는 알고리즘입니다.</p>
<h4 id="특징-3">특징</h4>
<ul>
<li>팩토리얼 특성상 느림(시간복잡도 중 가장느림)</li>
<li>10 이상만 넘어가도 시간초과가 생기기 때문에 10이하에서 사용 권장합니다.</li>
</ul>
<h4 id="예시-문제-3">예시 문제</h4>
<blockquote>
<p>Q. 숫자 배열의 모든 순열을 구하시오.</p>
</blockquote>
<pre><code class="language-typescript">// chatGPT 참고... 추후에 직접 구현해볼 예정.
function permute(arr: number[]): number[][] {
  const result: number[][] = [];

  const getPermutation = (path: number[], rest: number[]) =&gt; {
    if(rest.length === 0) {
      result.push([...path]);
      return;
    }
    for(let i = 0; i &lt; rest.length; i++) {
      const next = rest[i];
      const nextRest = [...rest.slice(0, i), ...rest.slice(i + 1)];
      getPermutation([...path, next], nextRest);
    }
  }

  return result;
}

permute([1,2,3])</code></pre>
<hr>
<h3 id="o2ⁿ-지수-시간">O(2ⁿ) 지수 시간</h3>
<h4 id="핵심키워드--백트래킹-조합부분집합-완전탐색-dfs-피보나치수열">핵심키워드 : 백트래킹, 조합/부분집합 완전탐색, DFS, 피보나치수열</h4>
<p>입력 요소마다 또 다른 모든 요소를 순회하는 알고리즘입니다.</p>
<h4 id="특징-4">특징</h4>
<ul>
<li>매우 느린편. 10개의 입력 요소만 존재해도 2의 10승의 시간이 걸립니다..</li>
<li>재귀호출 방식 + 중복계산이 존재합니다.</li>
<li>반드시 메모이제이션이 필요합니다.</li>
</ul>
<h4 id="예시-문제-4">예시 문제</h4>
<blockquote>
<p>Q. 정수 배열 nums와 정수 maxSum이 주어집니다.
nums의 모든 부분집합 중에서, 원소의 합이 maxSum 이하인 부분집합들만 구하세요.</p>
</blockquote>
<pre><code class="language-typescript">function subsets(nums: number[], maxNum: number): number[][] {
  const result: number[][] = [];

  const dfs = (path: number[], index: number) =&gt; {
    const sum = path.reduce((acc, curr) =&gt; acc + curr, 0);
    if (sum &lt;= maxNum) {
      result.push([...path]);
    }
    for (let i = index; i &lt; nums.length; i++) {
      path.push(nums[i]);
      dfs(path, i + 1);
      path.pop();
    }
  };
  dfs([], 0);
  return result;
}

sunsets([1,2,3], 4)</code></pre>
<p>2ⁿ이라는 시간복잡도에 의해 해당 배열은 2^3 = 8번 순회하게 될껍니다.
dfs를 통한 배열의 순회는 다음과 같습니다.
[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]
그리고 해당 순회를 통해 합이 4보다 큰 값들을 출력하게됩니다.</p>
<hr>
<h3 id="olog-n-로그-시간">O(log n) 로그 시간</h3>
<h4 id="핵심키워드--이진-탐색-트리-탐색-mapset-내부-탐색">핵심키워드 : 이진 탐색, 트리 탐색, Map/Set 내부 탐색</h4>
<p>데이터를 매번 절반씩 줄여가며 탐색하는 방식</p>
<h4 id="특징-5">특징</h4>
<ul>
<li>데이터가 클수록 효율</li>
<li>정렬된 데이터일때 사용 가능</li>
<li>log₂n 기준 (이진트리 깊이와 동일)</li>
</ul>
<h4 id="예시-문제-5">예시 문제</h4>
<blockquote>
<p>Q. 배열 arr에서 특정 값 k를 이진 탐색으로 찾기.</p>
</blockquote>
<pre><code class="language-typescript">function binarySearch(arr: number[], k: number): boolean {
  const sortArr = arr.sort((a, b) =&gt; a - b);

  let low = 0;
  let high = sortArr.length - 1;

  while (low &lt;= high) {
    const mid = Math.floor((low + high) / 2);
    if (arr[mid] === k) return true;
    if (arr[mid] &lt; k) low = mid + 1;
    else high = mid - 1;
  }

  return false;
}

console.log(binarySearch([2, 8, 7, 5, 3, 4, 2, 10, 38], 7));</code></pre>
<p>log n 에 의해 log 9 = 3회의 순회를 하게됩니다. 해당 예시는 특정값 k를 이진탐색으로 찾기위해 최초에 배열을 정렬 해주는 과정을 거쳤습니다.
이후 0 부터 sortArr.length - 1을 각각 low, high로 적용하여 배열의 첫번째 인덱스와 마지막 인덱스를 기반으로 중심값을 k값을 찾기 위해 비교분석에 들어갑니다.
정렬된 배열: [2,2,3,4,5,7,8,10,38]
1번째 탐색 : (0+8)/2 = 배열의 4번째 = 5는 k인 7보다 작기때문에 low = mid+1 = 5가 됩니다.
2번째 탐색 : (5+8)/2 = 배열의 6번째인 8은 k보다 크기때문에 high = mid-1 = 5가 됩니다.
3번재 탐색 : hight가 low보다 크거나 같을경우 반복하기에 사실상 마지막 탐색입니다. arr[5]의 값인 7은 k와 같기때문에 true가 반환되고 해당 탐색은 종료됩니다.</p>
<p>log n = log 9 = 3 에 맞게 3번의 탐색을 통해 찾은모습을 볼 수 있습니다.</p>
<hr>
<h3 id="on-log-n-로그-선형">O(n log n) 로그 선형</h3>
<h4 id="핵심키워드--arraysort---timsort기반-quick-merge-균형트리">핵심키워드 : Array.sort() -&gt; timsort기반, Quick, Merge, 균형트리</h4>
<p>입력 크기만큼 반복하면서 각 단계마다 log n만큼의 연산이 필요한 경우</p>
<h4 id="특징-6">특징</h4>
<ul>
<li>효율적 정렬 알고리즘의 핵심</li>
<li>정렬 알고리즘에서 등장: MergeSort, QuickSort </li>
<li>이진 트리 기반 작업을 n번 반복하는 경우</li>
</ul>
<h4 id="예시-문제-6">예시 문제</h4>
<blockquote>
<p>Q. 숫자 배열을 오름차순으로 정렬하라</p>
</blockquote>
<pre><code class="language-typescript">function sortArray(arr: number[]) : number[] {
  return arr.sort((a,b) =&gt; a - b);
}

sortArray([2,6,30,4,11,0,1,8,8,2,13,17]);</code></pre>
<p>12 log 12 =  12 * (log 12) -&gt; log 12 = 약 3.58 -&gt; 12 * 3.58 = 42.96.
위 정렬은 대략 42.96번 정렬한다고 생각할 수 있습니다.
<img src="https://velog.velcdn.com/images/villain_uni/post/cb641975-8347-466d-9fce-2f6a757b840c/image.png" alt="">
실제로 지피티를 통해 해당 횟수와 근사하게 반복한다는 결과를 도출할 수 있었습니다.</p>
<h2 id="여담">여담</h2>
<p>시간 복잡도를 이해하고 관련 문제들을 풀어보면서도 <code>아직 완벽하게 이해하진 못한거 같다</code> 라는 생각이 들었습니다.
비교적 쉬운 시간 복잡도도 있는 반면, 지수나 팩토리얼 같은 복잡하고 이해하기 쉽지않은 복잡도도 많았습니다.
향후에 공간 복잡도와 알고리즘에 대한 글을 작성하면서 더 자세히 알아보고, 이해하는 시간을 가져야 할 꺼 같습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[디스코드와 깃허브를 연동해보자]]></title>
            <link>https://velog.io/@villain_uni/%EB%94%94%EC%8A%A4%EC%BD%94%EB%93%9C%EC%99%80-%EA%B9%83%ED%97%88%EB%B8%8C%EB%A5%BC-%EC%97%B0%EB%8F%99%ED%95%B4%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@villain_uni/%EB%94%94%EC%8A%A4%EC%BD%94%EB%93%9C%EC%99%80-%EA%B9%83%ED%97%88%EB%B8%8C%EB%A5%BC-%EC%97%B0%EB%8F%99%ED%95%B4%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Fri, 07 Mar 2025 01:39:33 GMT</pubDate>
            <description><![CDATA[<h1 id="discord---github-연동">DISCORD - GITHUB 연동</h1>
<p>디스코드의 <code>Webhook</code> 을 깃허브에 등록하고, 깃허브 액션을 통하여 <code>Issue</code> , <code>Pull Request</code> 와 관련된 이벤트 발생 시 자동으로 디스코드 <code>Webhook</code> 을 통해 메시지를 전송하도록 셋팅해보자.</p>
<h2 id="discord-webhook">Discord webhook</h2>
<p>들어가기에 앞서 <code>Webhook</code> 에 대해 알아보자. <code>Webhook</code> 은 웹 혹은 앱에서 특정한 이벤트에 대해 커스텀 콜백함수로 리턴해주는 기술이다. 디스코드의 경우 유니크한 URL(Endpoint)를 주어 해당 URL로 요청이 들어오면 그에 대한 콜백함수를 실행시켜준다.</p>
<img src="https://velog.velcdn.com/images/villain_uni/post/3bf767d2-f790-4b08-b124-ec27d454d78f/image.png" />

<p><a style="display: flex; justify-content: center; width: 100%;" href="https://support.discord.com/hc/ko/articles/228383668-%EC%9B%B9%ED%9B%85%EC%9D%84-%EC%86%8C%EA%B0%9C%ED%95%A9%EB%8B%88%EB%8B%A4">디스코드 웹훅 소개사이트. 후루룩 진공 튜브라는 표현이 귀엽다.</a></p>
<p>그럼 이제 후루룩 진공 튜브(웹훅)에 대해 알아보았으니 내 디스코드 서버에 직접 적용시켜보자.</p>
<h3 id="discord-webhook-생성">Discord webhook 생성</h3>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/59b02a59-c492-4af1-b47a-6e4bd9ef583e/image.png" alt=""></p>
<p>먼저 위 사진과 같이 서버설정 - 연동 탭을 클릭한다.</p>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/3a01914d-0ea4-46ac-8e34-aecbe0ff51da/image.png" alt=""></p>
<p>그러면 위 사진과 같이 해당서버의 연동메뉴가 나타나는데 여기서 웹후크 만들기를 클릭한다.</p>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/abcf404e-8548-40d6-be15-ce1b60e6ac85/image.png" alt=""></p>
<p>이후 새 웹후크를 클릭하여 나만의 웹훅을 만든다. </p>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/eecbeb0f-672c-498e-a49f-b087095d29f3/image.png" alt=""></p>
<p>위 사진과 같이 웹 훅이 생성되는것을 볼 수 있는데, 어떤 웹훅인지 간단하게 이름을 설정해주고, 어떤채널에서 웹훅이 메시지를 전송하게할지 채널을 선택해준 후, 웹후크 URL을 복사하면된다.
<img src="https://velog.velcdn.com/images/villain_uni/post/2d850a7c-a384-4ba2-97c3-93a399989912/image.png" alt=""></p>
<p>위 사진과 같이 이름에 간단한 역활을 적어주고, 채널을 조정해준 모습을 볼 수 있다.
잊지말고 웹후크 URL을 복사해준 후 저장해주면된다. 디스코드에서의 셋팅은 이게 끝이다.</p>
<h2 id="github-actions">Github-Actions</h2>
<p>깃허브는 깃허브액션스 라는 사용자 지정 자동화 기능을 제공해준다.</p>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/ffaa9d59-a690-4e91-b0fd-6ad42aa8c0d3/image.png" alt="">
<a style="display: flex; justify-content: center; width: 100%;" href="https://docs.github.com/ko/actions">Github Actions 설명서 페이지</a></p>
<p>해당 기능을 사용하기 위해서 Github에 명령을 하기 위한 <code>workflow</code> 를 작성해 줘야하는데, 직접 작성해도되고, 누군가 만들어놓은 워크플로우를 이용해도 된다.</p>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/e4ab0a50-a292-4e61-858f-67d41e8c534d/image.png" alt=""></p>
<p>우리는 그중 <a href="https://github.com/Ilshidur/action-discord">Ilshidur/action-discord</a> 를 사용할 예정이다.</p>
<h3 id="github-secret-셋팅">github secret 셋팅</h3>
<p>그 전에 이전에 디스코드에서 복사해온 웹후크 URL을 <code>깃허브의 secrets</code> 에 저장해줘야한다.</p>
<blockquote>
<p>[?] 그냥 저장해도 되는데 왜 secrets에 저장해야할까?
디스코드 웹후크URL은 따로 인증과정을 걸치지 않기 때문에 해당 URL을 가지고 누군가 악의적으로 사용할 가능성이 있다.
내 디스코드와 웹후크를 위해서라도 URL은 secrets에 저장해주는게 좋다.
그 외에도 github-actions에서 workflow를 작성할 때 민감한 내용이 있다면 secrets에 저장해주는게 좋다.</p>
</blockquote>
<p>먼저 <code>github-actions</code> 를 사용할 <code>repository</code> 의 <code>Settings</code> 탭을 클릭한다.
<img src="https://velog.velcdn.com/images/villain_uni/post/ce6e01ef-f4c1-4550-9307-0aa93290ca9b/image.png" alt=""></p>
<p>그리고 좌측 메뉴에서 <code>Security</code> 의 <code>Secrets and variables</code> 탭의 <code>Actions</code> 를 클릭해준다.</p>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/5bf98d98-9536-4508-b067-42c0d53d8fe5/image.png" alt=""></p>
<p>그러면 아래와 같은 화면이 나올탠데 <code>Repository secrets</code> 의 <code>New repository secret</code> 을 통하여 새로운 <code>secret</code> 을 생성하고 저장해준다.</p>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/9980a308-ff99-48d6-a789-95b4ae58039f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/1cb4ba17-fffd-4f07-b7ab-84b1a842b8ca/image.png" alt=""></p>
<p>이제 해당 repository에서 사용할수있는 secrets 값이 생겼으니 해당값을 이용해 디스코드 웹후크 URL을 불러와 워크플로우를 작성해주면된다.</p>
<h3 id="workflow-작성">workflow 작성</h3>
<p><code>workflow</code> 작성은 해당 <code>repository</code> 의 <code>.github/workflows</code> 폴더 안에 작성하면된다. <code>workflow</code> 는 <code>YML</code> 파일로 작성된다.</p>
<blockquote>
<p>YML은 XML이나 JSON같은 데이터를 포맷팅한 방식 중 하나이다.</p>
</blockquote>
<p>앞서 태그한 github-actions에 접속해보면 워크플로우 작성 박식에 대해 가르켜준다.</p>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/8518dfbd-8b38-4741-9b86-446bbec320bf/image.png" alt=""></p>
<p>간단하게 워크플로우 문법에 대해 알아보자.</p>
<ul>
<li><p>name: 워크플로우의 이름</p>
</li>
<li><p>run-name: 워크플로우가 실행될때 표현 될 이름</p>
</li>
<li><p>on: 워크플로우의 트리거. 워크플로우가 깃허브 repository의 어떤 이벤트가 발생 했을 때 실행되게할지 정할수 있다. 아래 간단한 예시 이미지를 준비했다.
<img src="https://velog.velcdn.com/images/villain_uni/post/a4352ae8-ed62-4a0c-a303-751701940393/image.png" alt="간단한 예시이미지">
위 이미지는 on을 통해 repository의 issue가 발생했을때 실행시키는데, issue에도 여러 종류가 있어 어떤 타입의 issue일때 발생시킬지 상세히 적은 이미지이다. 자세한 내용은 <a href="https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows">github-actions trigger</a> 를 통하여 알아볼 수 있다.</p>
</li>
<li><p>jobs: 워크플로우가 어떤 업무를 수행할지 명시해준다.</p>
</li>
<li><p>jobs.job-id: job의 고유한 식별자 아이디값이다.jobid는 알파벳과 하이픈(-), 언더바(_) 를 이용하여 구성할 수 있다.</p>
</li>
<li><p>jobs.job-id.name: job의 이름</p>
</li>
<li><p>jobs.job-id.runs-on: job이 실행될 환경 (ex. linux, x64, ubuntu, etc...)</p>
</li>
<li><p>jobs.job-id.steps: job이 순차적으로 할 일에 대한 명시</p>
</li>
<li><p>jobs.job-id.steps.uses: 다른 repository에서 생성한 actions을 가져다 쓸 때 사용</p>
</li>
</ul>
<p>이후에도 steps에서 사용할수있는 구문 등이 있지만 너무 많아질 수 있으므로 여기까지만 간단하게 서술하고 링크를 남긴다. <a href="https://docs.github.com/ko/actions/writing-workflows/workflow-syntax-for-github-actions">[github workflow syntax]</a></p>
<p>그럼 해당 내용을 바탕으로 간단하게 작성해보자.</p>
<pre><code class="language-yml">name: Opened Issue Notification # 액션의 이름

on: # 액션의 trigger
  issues:
    types:
      - opened  # 이슈가 열릴 때 실행

jobs: # 액션이 할 일
  create-issue:
    name: Discord Notification - Create Issue # 액션이 할일의 이름
    runs-on: ubuntu-latest # 액션이 실행될 환경
    steps: # 액션의 일들
      - name: Send Issue
        uses: Ilshidur/action-discord@master # Ilshidur/action-discord 를 사용할것
        env: # 액션이 실행 될 때 사용할 값들. 아래 값들은 Ilshidur/action-discord 를 참고함
          DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
          DISCORD_USERNAME: TIME_LETTER_BOT
          DISCORD_AVATAR: https://github.com/pknu-wap/WAPP/blob/main/image/icon.png?raw=true
          DISCORD_EMBEDS: &#39;[{&quot;title&quot;:&quot;신규 이슈 사항:${{ github.event.issue.title }}&quot;,&quot;color&quot;:10478271,&quot;description&quot;:&quot;${{ github.event.issue.html_url }}&quot;,&quot;fields&quot;:[{&quot;name&quot;:&quot;ISSUE NUMBER&quot;,&quot;value&quot;:&quot;#${{ github.event.issue.number }}&quot;,&quot;inline&quot;:true},{&quot;name&quot;:&quot;AUTHOR&quot;,&quot;value&quot;:&quot;${{ github.event.issue.user.login }}&quot;,&quot;inline&quot;:true}]}]&#39;</code></pre>
<p>위 yml에 대한 설명은 주석을 통해 적어놓았다. Ilshidur/action-discord를 사용할 때 color가 특이한 숫자로 구성된 걸 확인할 수 있는데, 이는 Ilshidur/action-discord에서 색상을 10진수로 읽기때문에 Hexcode(16진수)를 10진수로 변환하여 적용시켜주어야 한다.</p>
<p>또 yml은 주의할점이 매우많은데 이는 chatGPT를 통해 간략하게 보여주겠다.
<img src="https://velog.velcdn.com/images/villain_uni/post/992d234e-edf5-4613-9252-91e7d966afb5/image.png" alt=""></p>
<p>해당 문법을 지키지 않으면 아래와같은 SyntaxError를 매우매우매우매우매우매우매우매우매우매우 많이 볼 수 있을것이다.
<img src="https://velog.velcdn.com/images/villain_uni/post/351d38ef-833d-4430-a1b6-077a371642a3/image.png" alt=""></p>
<p>문법과 워크플로우가 알맞게 작성되었다면 이제 직접 실행해보자.</p>
<h2 id="연동-실행">연동 실행</h2>
<p>먼저 나는 pr실행시와 종료시 github actions가 이를 감지하고 discord를 전송하는 워크플로우를 작성하였다.
<img src="https://velog.velcdn.com/images/villain_uni/post/ffb2feff-5b4a-4fca-9df4-1dc7557412f9/image.png" alt="">
위 이미지를 보면 PR하는데 Opened PR Notification이라는 워크플로우가 실행되는것을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/villain_uni/post/bb77fe22-d1c9-4df0-8364-56da88e4c491/image.png" alt="">
성공적으로 워크플로우가 실행되었다고 체크표시가 나타났다. 그럼 이제 디스코드를 봐보자.</p>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/0e400819-7924-4b43-9c53-faaba99ff2f4/image.png" alt=""></p>
<p>성공적으로 디스코드 봇이 메시지를 전송한 모습을 볼 수 있다!</p>
<h2 id="후기">후기</h2>
<p>discord webhook과 github actions를 공부하고 직접 사용해보면서 자동화의 장점에 대해 뼈저리게 느끼게 되었다.
깃허브를 직접 접속하지않아도 누군가의 PR이나 Issue를 파악할 수 있다는게 너무 크게 와닿았고, 이를 이용해 나와 관련된 많은 이슈들 혹은 이메일까지도 webhook을 지원한다면 연동시켜서 내 디스코드에서 나와 관련된 모든정보를 파악할 수 있지않을까 라는 생각을 하게되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Modern Javscript Study - 문서]]></title>
            <link>https://velog.io/@villain_uni/Modern-Javscript-Study-%EB%AC%B8%EC%84%9C</link>
            <guid>https://velog.io/@villain_uni/Modern-Javscript-Study-%EB%AC%B8%EC%84%9C</guid>
            <pubDate>Wed, 26 Feb 2025 06:30:58 GMT</pubDate>
            <description><![CDATA[<h1 id="문서">문서</h1>
<h2 id="브라우저-환경과-다양한-명세서">브라우저 환경과 다양한 명세서</h2>
<p>자바스크립트(Javascript)는 본래 웹 브라우저에서 사용하려고 만든 언어이다. 넷스케이프에서 브랜든 아이크가 처음 만든 이후 진화를 거쳐 다양한 사용처와 플랫폼을 지원하는 언어로 변모하였다.</p>
<p>자바스크립트가 돌아가는 플랫폼들은 <code>호스트(Host)</code>라고 불리는데 이는 브라우저 혹은 웹 서버, 또는 커피머신에서 자바스크립트가 돌아가기만 한다면 호스트라고 불릴 수 있다. 각 플랫폼들은 해당 플랫폼에 특정되는 기능을 제공하는데, 이를 자바스크립트 명세서에선 <code>호스트 환경(Host Environment)</code>라고 부른다.</p>
<p>자바스크립트의 호스트 환경에서는 랭귀지 코어(ECMAScript)에 더하여 플랫폼에 특정되는 객체와 함수를 제공한다. 웹 브라우저일 경우 웹페이지를 제어하기 위한 수단을 제공하고, Node.js는 서버 사이드 기능을 제공해준다.
<img src="https://velog.velcdn.com/images/villain_uni/post/692d0b7e-3eca-46dc-9148-f545a040262c/image.png" width="400px"></p>
<p style="text-align:center; font-size:14px">웹 브라우저일 때의 호스트 환경의 기능들을 보여주는 이미지</p>

<p>웹 브라우저의 호스트환경에서 <code>window</code>라는 루트 객체가 존재한다. window는 자바스크립트 전역객체로써 browser window를 대변하고 이를 제어할 수 있는 메서드(method)를 제공한다.
<img src="https://velog.velcdn.com/images/villain_uni/post/fff5c58c-046d-4cd7-8d86-1058ef6d4814/image.png" alt=""></p>
<p style="text-align:center; font-size:14px">브라우저의 개발자 도구에서 window를 입력한 모습</p>
위와 같이 window는 전역객체이자 루트객체로써 alert ,blur, devicePixelRatio, event, 등등 수많은 내장객체와 함수들을 제공함으로써 브라우저를 컨트롤 할 수 있게 도와준다.

<p>저 수많은 객체, 함수들은 크게 3가지 역활로 나눌 수 있다.</p>
<h2 id="dom">DOM</h2>
<p><code>DOM(Document Object Model)</code>은 문서 객체 모델이란 뜻으로 웹 페이지 내의 모든 콘텐츠를 객체로 나타내준다. 이를 통해 객체에 접근하여 수정을 가능케 해준다. DOM의 가장 대표적이자 페이지의 가장 기본 진입점 역활을 하는 객체는 <code>document</code>이다. </p>
<div style="display: flex; align-items: start; justify-content: center; gap: 20px;">
  <img src="https://velog.velcdn.com/images/villain_uni/post/293dda07-d05e-4048-a7c0-027808e41ef2/image.png" />
<img src="https://velog.velcdn.com/images/villain_uni/post/8bc21e90-35f4-4aa5-9bb6-525f2e205e1f/image.png" />
</div>
<p style="text-align:center; font-size:14px">개발자도구에 document를 입력하지 페이지 전체를 가리키는 모습</p>

<p><code>document</code>를 통하여 직접적으로 <code>HTML Element</code>의 값을 추가, 변경 할 수 있다
<img src="https://velog.velcdn.com/images/villain_uni/post/20ac80da-73fb-4354-9436-88ffb76d3394/image.png" alt=""></p>
<p style="text-align:center; font-size:14px">document의 내부객체들을 통하여 새로운 div element를 생성해 추가한 모습</p>
이 외에도 다양한 기능들을 제공한다. 관련 프로퍼티와 메서드에 대한 정보는 <a href="https://dom.spec.whatwg.org/">WHATWG의 DOM Living Standard</a> 에서 확인할 수 있다.

<blockquote>
<p>DOM은 브라우저만을 위한 모델이 아니다. DOM은 문서의 구조와 이를 조작할 수 있는 객체에 대한 설명이 담겨져 있기 대문에 브라우저가 아닌곳, 서버와같은곳에서 페이지를 그려 브라우저로 보내는 방식(서버사이드렌더링)등으로 사용하기도 한다.</p>
</blockquote>
<h2 id="bom">BOM</h2>
<p><code>BOM(Browser Object Model)</code>은 브라우저 객체 모델로 문서 이외의 모든것을 제어하기 위해 호스트환경(브라우저)에서 제공하는 객체들을 나타낸다.</p>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/bff2f13b-36b4-4200-96b8-40c3ecff8cbd/image.png" alt=""></p>
<p style="text-align:center; font-size:14px">앱의 이름이나 앱의 코드네임, 언어 등 브라우저의 정보들과 운영체제의 정보들이 나타나는 모습</p>

<h2 id="dom-tree">DOM tree</h2>
<p>HTML을 지탱하는것은 <code>태그(tag)</code> 이다. DOM에 따르면, 모든 HTML <code>태그는 객체</code>이다. 태그 하나가 감싸고 있는 자식(child)태그는 중첩 태그(nested tag)라고 부른다. 위 DOM 에서 사용한 객체 역시 body의 자식 객체를 추가하기에 appendChild라는 메서드로 newDiv를 추가한 모습을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/f0756915-df37-43f9-a4cf-ca4fcf811fcd/image.png" alt=""></p>
<p style="text-align:center; font-size:14px">html의 노드와 자식노드를 확인하는 모습</p>


<p><code>HTML</code>은 HTML이라는 가장 큰 tag 안에 자식태그 Head tag가 있고 Head tag는 또 Title tag라는 자식 태그가 존재하게되고 이는 거대한 태그 트리 구조를 이루게 된다. 또한 title태그 안에 적게 될 &quot;타이틀&quot; 이라는 텍스트 역시 하나의 <code>텍스트 노드</code>로써 title tag의 자식 노드가 된다.</p>
<p>위에 글을 확인해보면 텍스트는 텍스트 노드 라고 부르고 태그라 불렀는데 텍스트 노드는 DOM의 Node Type들 중 하나로써 Node Type은 크게 12가지로 나뉜다. 그중 가장 자주 쓰이는 타입과 비교적 덜 쓰이는 타입을 구분해보았다.</p>
<h3 id="자주-쓰는-노드타입타입넘버">자주 쓰는 노드타입(타입넘버)</h3>
<ol>
<li>document Node(9) - DOM에서 확인하였던 document 객체가 해당하는 노드타입이다.</li>
<li>element Node(1) - HTML 태그에서 만들어지며, DOM 트리를 구성하는 블록인 요소 노드타입이다.</li>
<li>text Node(3) - 텍스트를 포함하는 노드타입이다.</li>
<li>comment Node(8) - 실제 화면에 보이지는 않지만, 정보를 기록하고 주석을 담당하는 주석 노드타입이다.</li>
</ol>
<h3 id="그-외의-노드-타입타입넘버">그 외의 노드 타입(타입넘버)</h3>
<ol>
<li>attribute Node(2)</li>
<li>CDATA Section Node(4)</li>
<li>Entity Reference Node(5) - Legacy</li>
<li>Entity Node(6) - Legacy</li>
<li>Processing Insturction Node(7)</li>
<li>Document Type Node(10)</li>
<li>Document Fragment Node(11)</li>
<li>Notation Node(12) - Legacy</li>
</ol>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/26e79f9c-edcb-463f-b087-c2f531cd18c2/image.png" alt=""></p>
<p style="text-align:center; font-size:14px">개발자 도구에서 nodeType이라는 메서드로 다양한 노드타입들을 확인할 수 있다.</p>

<h2 id="dom-탐색하기">DOM 탐색하기</h2>
<p>앞서 DOM은 객체에 접근하여 값을 수정할 수 있다고 하였다. 그럼 DOM은 객체에 어떻게 접근하는것일까? </p>
<p>앞의 예시를 보면 <code>document</code> 를 통해 객체에 접근하거나 객체의 속성, 노드타입 등을 확인하는 모습을 볼 수 있다. <code>document</code>가 DOM에 접근하기 위한 가장 기본적인 진입점이기 때문에 가능했던 일이다.
<img src="https://velog.velcdn.com/images/villain_uni/post/6bb60217-7010-41bf-97a5-35279c54a712/image.png" alt=""></p>
<p style="text-align:center; font-size:14px">Document 관계 이미지 참조(출처 - <a href="https://ko.javascript.info/dom-navigation">모던자바스크립트 - 문서</a>)</p>

<p>하나씩 글로써 풀어보자면 <code>document</code>는 DOM에 접근하기위한 가장 기본적인 진입점으로써 최상위에 위치해있다. <code>document</code>안에서 <code>documentElement</code>는 페이지 내 html의 루트객체인 <code>html</code>태그를 가리킨다. 그리고 html 노드의 자식노드인 <code>body</code>가 다음에 위치하게된다. 또 <code>body</code>의 자식노드 에 div, main, section등의 자식노드가 오게 되고, 해당 노드의 <code>형제노드(sibling)</code> 혹은 또 다른 <code>자식노드(childNodes)</code> 가 오게된다. 이런식으로 document의 DOM tree 형식을 이용하여 DOM을 탐색할 수 있다.</p>
<p>위와 같은 방식으로 하나하나 자식,형제 노드를 탐색하지 않고 특정한 값을 찾고 싶을때는 <code>getElement*, querySelector*</code> 메서드로 탐색할 수 있다.</p>
<h3 id="getelement">getElement*</h3>
<p><code>getElement*</code>에는 다음과 같은 메서드 들이 있다.</p>
<ol>
<li><code>getElementById()</code> - 태그의 속성 중 id의 값을 이용해 DOM을 탐색한다. id는 중복될 수 없는 값이기에 1개의 노드만을 리턴하게된다.</li>
<li><code>getElementsByTagName()</code> - 태그의 이름을 통해 DOM을 탐색한다. 태그의 이름은 여러개가 존재할 수 있기에 노드의 배열을 반환한다.</li>
<li><code>getElementsByClassName()</code> - 태그의 속성 중 className을 이용하여 DOM을 탐색한다. className는 중복될 수 있기에 노드의 배열을 반환한다.</li>
<li><code>getElementsByName()</code> - 태그의 속성 중 name속성을 이용하여 DOM을 탐색한다. name은 중복될 수 있기에 노드의 배열을 반환한다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/c3251281-8f98-42ea-b252-fa727982498d/image.png" alt=""></p>
<p style="text-align:center; font-size:14px">getElement*를 이용하여 DOM을 탐색하는 모습</p>

<h3 id="queryselector">querySelector*</h3>
<p><code>querySelector*</code>에는 다음과 같은 메서드 들이 있다.</p>
<ol>
<li><code>querySelector()</code> - CSS선택자(ex. #id, .class, a[href$=&#39;zip&#39;])을 토대로 탐색하여 가장 첫번째 노드를 리턴한다. 아래 <code>querySelectorAll()</code>의 0번째 인덱스값과 같다.</li>
<li><code>querySelectorAll()</code> - CSS선택자(ex. #id, .class, a[href$=&#39;zip&#39;])을 토대로 탐색하여 연관된 노드의 배열을 반환한다.</li>
</ol>
<h2 id="주요-노드-프로퍼티">주요 노드 프로퍼티</h2>
<h3 id="dom의-노드-클래스">DOM의 노드 클래스</h3>
<p>DOM의 노드들은 종류에 따라 각기 다른 프로퍼티들을 지원한다. a tag를 대응하는 element node에는 링크와 관련된 프로퍼티를, input tag를 대응하는 element node에는 입력과 관련된 프로퍼티를 제공한다.
<img src="https://velog.velcdn.com/images/villain_uni/post/0792e6be-2ce9-458d-a17b-65b566015086/image.png" alt=""></p>
<p style="text-align:center; font-size:14px">대응하는 element node에 관련된 프로퍼티를 제공하는 모습</p>

<p>그런데 DOM 노드는 공통 조상에게서 만들어지기 때문에 노드타입은 다를 수 있지만 결국 모든 DOM 노드는 공통된 프로퍼티와 메서드를 지원한다.
<code>EventTarget</code> 로부터 <code>node</code>는 <code>EventTarget</code>을, 자식노드들은 부모 노드의 <code>node class</code>를 상속받게된다.
<img src="https://velog.velcdn.com/images/villain_uni/post/946b334c-d685-44dc-8d5e-a859d0d2e41d/image.png" alt=""></p>
<p style="text-align:center; font-size:14px">상속관계를 그림으로 나타낸 이미지[<a href="https://ko.javascript.info/basic-dom-node-properties">출처 - modern javascript DOM노드 클래스</a>]</p>

<blockquote>
<p>새로 등장한 <code>EventTarget</code>이란 클래스는 지금까지 언급된적 없는 새로운 친구인데, 이 부분은 브라우저의 이벤트를 알아보면서 더 자세히 알아볼 예정이다.</p>
</blockquote>
<p><code>EventTarget</code>으로부터 상속받은 <code>Node</code>라는 클래스가 DOM 노드의 주요 베이스역활을 하게되는 클래스이다. <code>EventTarget</code>으로부터 상속 받았기에 <code>addEventListener</code>와 같은 이벤트 메서드를 사용할 수 있게된다. 또한 Node로부터 상속받는 <code>Text</code>, <code>Element</code>, <code>Comment</code> 역시 <code>Node</code>로부터 할당받기 때문에 노드가 가진 메서드들을 사용할 수 있다. 또 이중 <code>Element</code>로부터 상속받은 <code>HTMLElement</code>가 있게되고 위와같은 과정을 반복하게된다.</p>
<p>이렇게 꼬리에 꼬리를 물듯이 상속이 되어가는데 <code>instanceof</code> 라는 연산자를 통해 상속 여부를 확인할 수 있다.
<img src="https://velog.velcdn.com/images/villain_uni/post/df9c614e-88ec-4c1d-b76e-0e870fddc79e/image.png" alt=""></p>
<p style="text-align:center; font-size:14px">instanceof 를 이용해 document와 document.body가 어디서 상속받았는지 추적하는모습</p>



]]></description>
        </item>
        <item>
            <title><![CDATA[Use NodeMailer]]></title>
            <link>https://velog.io/@villain_uni/2023.11.14-NodeMailer</link>
            <guid>https://velog.io/@villain_uni/2023.11.14-NodeMailer</guid>
            <pubDate>Tue, 14 Nov 2023 08:21:04 GMT</pubDate>
            <description><![CDATA[<h1 id="nodemailer">NodeMailer</h1>
<p><a href="https://nodemailer.com/usage/using-gmail/">NodeMailer</a>
NodeMailer는 Node.js내에서 Mail을 손쉽게 보내주는 모듈이다.</p>
<p>블로그 기능 중 CONTACT를 작성 시 내 메일로 보내기 위해 방법을 모색하던 도중 NodeMailer에 대해 알게되었다. NodeMailer는 많은 프로토콜 방식중 SMTP(Simple Mail Transfer Protocol)방식을 지향한다.</p>
<h2 id="how">How?</h2>
<p>먼저 NodeMailer의 Transforter(발송자)를 구현해준다.
<img src="https://velog.velcdn.com/images/villain_uni/post/16e9a52f-9dcb-46d5-919d-dc174d7e6fad/image.png" alt="">
auth의 경우 계정정보이기때문에 env파일로 관리해주는것을 권장한다.</p>
<p>이후 해당 발송자에 메일형식을 작성해주면 된다.
<img src="https://velog.velcdn.com/images/villain_uni/post/3bbde3c0-dc9c-4e7e-a36a-77f9ba6396c8/image.png" alt=""></p>
<h2 id="error">Error</h2>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/1c63309a-b82c-4488-9c76-683837024d3b/image.png" alt=""></p>
<p>위 에러가 떠 확인해보니 
<img src="https://velog.velcdn.com/images/villain_uni/post/80c10458-c0be-4e20-ad5a-f51d46a16735/image.png" alt=""></p>
<p>nodemailer는 465를 제외한 모든 포트는 secure을 false처리하는걸 기본으로 하고있었다. 그래서 해당 이슈를 고치니 정상적으로 처리되었다.
<img src="https://velog.velcdn.com/images/villain_uni/post/dae5f895-97b3-4878-a7ab-84403b3c594c/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Nuxt Hydration Trouble shooting]]></title>
            <link>https://velog.io/@villain_uni/2023.11.19-Hydration</link>
            <guid>https://velog.io/@villain_uni/2023.11.19-Hydration</guid>
            <pubDate>Thu, 09 Nov 2023 15:53:31 GMT</pubDate>
            <description><![CDATA[<h1 id="hydration-mismatch">Hydration Mismatch</h1>
<p>nuxt를 이용해 토이프로젝트를 작업하던 중 hydration mismatch라는 에러가 쌓이는 모습을 발견했다.
<img src="https://velog.velcdn.com/images/villain_uni/post/5bbacfd9-f4f3-4f98-a5b2-71528521d205/image.png" alt=""></p>
<p>구글링을 통해 조사해보니 Hydration mismatch는 SSR에서 주로 발생하는 이슈였다.</p>
<h2 id="원인">원인</h2>
<p>SSR은 pre-render에서 만들어진 DOM tree와 HTML에서 렌더링된 tree를 일치할것이라고 판단하고 이벤트핸들러를 바인딩하는데(Hydration) 그게 일치하지않아서(Mismatch) 생긴 이슈이다.</p>
<h2 id="수정과정">수정과정</h2>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/148096dd-7248-4598-aeba-379e281148be/image.png" alt="">
에러가 난 부분은 이부분이였다. 서버로부터 API로 data라는 값을 받아오는부분인데, pre-render과정에서는 v-if에 의해 값이 만들어지지않아 생긴 이슈같았다. Nuxt에서는 이를 위해 컴포넌트를 의도적으로 클라이언트측에서 렌더링하게 해주는 ClinetOnly라는 컴포넌트를 제공한다.</p>
<p>이를 이용해 해당 주석처리한 부분을 ClinetOnly컴포넌트로 감싸주어 해결하였다.</p>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/1604a085-236b-4c54-ad51-ea83197aa0be/image.png" alt="">
<img src="https://velog.velcdn.com/images/villain_uni/post/307ca3cc-034b-4edb-b124-dfd0f3e44b84/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Async Await에 대해 알아보자]]></title>
            <link>https://velog.io/@villain_uni/2023.10.31-TIL</link>
            <guid>https://velog.io/@villain_uni/2023.10.31-TIL</guid>
            <pubDate>Tue, 31 Oct 2023 13:08:56 GMT</pubDate>
            <description><![CDATA[<h1 id="async--await">Async &amp; Await</h1>
<p>Async Await는 Promise객체 보다 쉽게 사용할 수 있게 해주는 문법이다. Promise를 기반으로 하지만 Async함수 내에서 await를 이용해 비동기를 동기처럼 작동시킬 수 있다.</p>
<pre><code class="language-javascript">function delays(s) {
    return new Promise((resolve)=&gt;{
       setTimeout(()=&gt;{
           resolve(s+&quot;ms 경과&quot;);
       }, s); 
    });
}

async function asyncHandle() {
    console.log(&quot;Async &amp; Await&quot;);
    const times = await delays(5000);
    console.log(times);
}

asyncHandle();
// Async &amp; Await
// ...5초후(실제로 이런 콘솔이 찍히진 않습니다. 이해하기 쉽게 글로 적은것)
// 5000ms 경과</code></pre>
<p>위 코드를 보면 asyncHandle함수를 실행시키게되면 먼저 콘솔로그가 찍히고, times의 값이 할당되는데 5초의 시간이 경과 한 것을 확인할 수 있다.</p>
<p>Async Await 없이 비동기를 실행하게되면 아래와 같은 결과가 나오게된다.</p>
<pre><code class="language-javascript">const delay = new Promise((resolve, reject) =&gt; {
    setTimeout(() =&gt; {
        resolve(&quot;5000&quot;);
    },5000);
});
const delay2 = new Promise((resolve, reject) =&gt; {
    setTimeout(() =&gt; {
        resolve(&quot;3000&quot;);
    },3000);
});

function promiseHandle() {
    delay.then((response)=&gt;{
        console.log(response);
    });
    delay2.then((response)=&gt;{
        console.log(response);
    });
}
promiseHandle();
// 3000
// 5000</code></pre>
<p>delay와 delay2를 바꿔도 결과는 똑같이 나오게된다. 비동기 이기 때문에 내가 delay를 먼저 출력하고싶어도 delay2가 먼저 출력되게된다.
이때 사용하게되는게 async await 이다.</p>
<pre><code class="language-javascript">const delay = new Promise((resolve, reject) =&gt; {
    setTimeout(() =&gt; {
        resolve(&quot;5000&quot;);
    },5000);
});
const delay2 = new Promise((resolve, reject) =&gt; {
    setTimeout(() =&gt; {
        resolve(&quot;3000&quot;);
    },3000);
});

async function asyncHandle() {
    await delay.then((response)=&gt;{
        console.log(response);
    });
    await delay2.then((response)=&gt;{
        console.log(response);
    });
}
asyncHandle();
// 5000
// 3000</code></pre>
<p>async를 이용해 async내부의 비동기 객체들을 동기화시켜 delay의 결과값을 기다리다 5000을 먼저 출력하고 그 이후 3000이 출력되는 모습일 볼 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Promise에 대해 알아보자]]></title>
            <link>https://velog.io/@villain_uni/2023.10.30-TIL</link>
            <guid>https://velog.io/@villain_uni/2023.10.30-TIL</guid>
            <pubDate>Mon, 30 Oct 2023 07:44:29 GMT</pubDate>
            <description><![CDATA[<h1 id="promise">Promise</h1>
<p>Promise는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타내는 객체이다.</p>
<blockquote>
<p>비동기란 
동기는 일이 순차적으로 진행되는 반면 비동기는 일이 순차적이지 않은, 동시에 진행될 수 있음을 말한다.</p>
</blockquote>
<p>예를 들어 브라우저와 웹서버 사이에서의 통신은 언제 끝날지 모를때는 동기 처리가 아닌 비동기 처리를 해줌으로써 더 효율적인 결과를 받을 수 있다.</p>
<h2 id="상태">상태</h2>
<p>Promise에는 3가지 상태가 있는데 다음과 같다</p>
<ol>
<li>대기상태(pending) : 이행하지도, 거부하지도않은 초기 상태.</li>
<li>이행상태(fulfilled) : 연산이 성공적으로 완료된 상태.</li>
<li>거부상태(rejected) : 연산이 실패한 상태.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/villain_uni/post/145006f4-7389-48f9-b44c-0ccf33078d75/image.png" alt="">
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise">출처 - MDN 공식 문서</a></p>
<h3 id="pending">Pending</h3>
<p>Pending은 Promise가 실행 되기 전 대기중인 상태이다.</p>
<pre><code class="language-javascript">const getItems = new Promise((resolve, reject) =&gt; {
    let a = 1;
    if(a === 1) {
        resolve(&quot;A는 1이다!!!&quot;);
    } else {
        reject(&quot;A는 1이 아니였다?!&quot;);
    }
});</code></pre>
<p>위와 같은 코드가 있을 때 new Promise()메서드는 호출 시 대기(pending)상태가 된다. 또한 호출할 때 콜백함수를 선언 할 수 있는데 위 코드를 보면 인자로 resolve와 reject를 받은걸 확인할 수 있다. 
if의 조건이 만족했을대 resolve, 만족하지 못했을 때 reject를 실행한 모습을 볼 수 있는데, resolve를 실행하면 위의 promise는 이행상태가 된다.
그리고 reject를 실행하면 거부상태가 된다.</p>
<h3 id="fulfilled">Fulfilled</h3>
<p>Fulfilled는 연산이 성공적으로 완료한 상태이다.</p>
<pre><code class="language-javascript">const getItems = new Promise((resolve, reject) =&gt; {
    let a = 1;
    if(a === 1) {
        resolve(&quot;A는 1이다!!!&quot;);
    } else {
        reject(&quot;A는 1이 아니였다?!&quot;);
    }
});

getItems.then((response)=&gt;{
  console.log(response); // A는 1이다!!!
});</code></pre>
<p>Promise는 이행상태가 되면 then 메서드를 통해 결과값을 받을 수 있다.</p>
<h3 id="rejected">Rejected</h3>
<p>Rejected는 연산이 실패 했을 경우의 상태이다.</p>
<pre><code class="language-javascript">const getItems = new Promise((resolve, reject) =&gt; {
    let a = 1;
    if(a === 2) {
        resolve(&quot;A는 1이다!!!&quot;);
    } else {
        reject(new Error(&quot;A는 1이 아니였다?!&quot;));
    }
});

getItems.then((response)=&gt;{
  console.log(response); 
}).catch((error) =&gt; {
  console.log(error); // Error: A는 1이 아니였다?!
});</code></pre>
<p>Promise두번째 인자 reject를 실행하게되면 promise는 rejected상태가 되어 catch로 리턴값을 받을 수 있다.</p>
<ul>
<li>finally라는 메서드도 존재하는데 이는 결과값과는 상관없이 항상 값을 반환한다.<pre><code class="language-javascript">const getItems = new Promise((resolve, reject) =&gt; {
  let a = 1;
  if(a === 2) {
      resolve(&quot;A는 1이다!!!&quot;);
  } else {
      reject(new Error(&quot;A는 1이 아니였다?!&quot;));
  }
});
</code></pre>
</li>
</ul>
<p>getItems.then((response)=&gt;{
  console.log(response); 
}).catch((error) =&gt; {
  console.log(error); // Error: A는 1이 아니였다?!
}).finally(() =&gt; {
  console.log(&quot;FINALLY&quot;); // FINALLY - 성공 실패 여부와 상관없이 finally가 실행된 모습
});</p>
<pre><code>&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
추가로 then, catch, finally는 연속해서 연결하여 사용할 수 있는데 이를 Promise Chaining이라고 한다. 이것이 가능한 이유는 then, catch, finally는 메서드를 호출하고나면 새로운 프로미스 객체가 반환되기 때문이다.

```javascript
const getItems = new Promise((resolve, reject) =&gt; {
    let a = 1;
    if(a === 2) {
        resolve(&quot;A는 1이다!!!&quot;);
    } else {
        reject(new Error(&quot;A는 1이 아니였다?!&quot;));
    }
});

getItems.then((response)=&gt;{
  console.log(response); 
}).catch((error) =&gt; {
    return error;
}).then((response) =&gt; {
    console.log(&quot;new Then&quot;); // new Then
    console.log(response); // Error: A는 1이 아니였다?!
});</code></pre><p>위와 같이 catch로부터 에러값을 리턴받아 then에서 새로운 작업이 진행된걸 확인할 수 있다.</p>
<h2 id="promiseall-promiserace">Promise.all, Promise.race</h2>
<p>Promise에는 all이라는 메서드가 존재하는데 이는 여러개의 Promise객체를 실행하고, 결과값을 배열로 반환해주는 메서드이다.</p>
<pre><code class="language-javascript">Promise.all([getItems,getTitle]).then((response) =&gt; {
    console.log(response); //[1, &#39;title&#39;]
});</code></pre>
<p>race라는 메서드 역시 여러 Promise객체를 실행하는데 all과는 다르게 가장 먼저 이행된 Promise객체의 결과값을 반환한다.</p>
<pre><code class="language-javascript">Promise.race([getItems,getTitle]).then((response) =&gt; {
    console.log(response); // 1
});</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Execution Context에 대해 알아보자]]></title>
            <link>https://velog.io/@villain_uni/2023.10.28-TIL</link>
            <guid>https://velog.io/@villain_uni/2023.10.28-TIL</guid>
            <pubDate>Fri, 27 Oct 2023 18:05:45 GMT</pubDate>
            <description><![CDATA[<h1 id="execution-context">Execution Context</h1>
<p><strong>J</strong>avascript에서 가장 중요한 개념중 하나인 ExecutionContext(실행컨텍스트)는 자바스크립트엔진이 자바스크립트를 실행할 때 <strong>실행할 코드에 제공할 환경 정보들을 모아놓은 객체</strong> 이다.</p>
<p>여기서 말하는 정보는 변수, 함수 선언, 변수의 유효범위(Scope), this와 같은 것들이 있다.</p>
<p>EC의 종류 크게 GlobalContext(전역)과 FunctionalContext(함수) 그리고 EvalContext로 나뉜다.</p>
<p>전역 컨텍스트는 자바스크립트 코드를 실행할 때 단하개만 정의되는 컨텍스트이다. Global Object(전역 객체)를 생성하며 this 값에 전역객체를 참조한다. 전역 컨텍스트는 App 실행 시 가장먼저 추가되며 후술할 CallStack에 의해 가장 나중에 제거된다.</p>
<p>함수 컨텍스트는 함수가 실행될 때 매번 정의되는 컨텍스트이다. 함수 컨텍스트는 전역 컨텍스트와는 다르게 함수가 실행될 때 마다 정의되고, 또 함수의 실행이 종료될 시(return) CallStack에서 제거된다.</p>
<p>EvalContext는 eval함수로 실행한 코드의 context이다. 그러나 보안상의 문제도 있고, MDN에서도 절대 사용하지 말것을 권장하고 있기에 있다는 것만 알아둘 예정이다.
<img src="https://velog.velcdn.com/images/villain_uni/post/5f8cb606-082f-41b7-9706-df14fba8e382/image.png" alt="">
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/eval#eval%EC%9D%84_%EC%A0%88%EB%8C%80_%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80_%EB%A7%90_%EA%B2%83!">출처 - MDN eval()</a></p>
<h2 id="callstack">CallStack</h2>
<p>Stack이란 데이터를 차곡차곡 쌓아올리고, 마지막으로 쌓인 데이터부터 꺼낼 수 있는 LIFO(Last In First Out, 후입선출) 형식의 자료구조이다. (데이터를 쌓는것을 push, 꺼내는것을 pop 이라한다.)</p>
<p>실행 컨텍스트는 자바스크립트 엔진으로부터 CallStack으로 관리되게 되는데 다음 예제를 보며 이해 해보자.</p>
<pre><code class="language-javascript">var name = &quot;Cain&quot;;
function nameCall(values) {
  console.log(&quot;values = &quot;,values);
  nameReCall(values);
}
function nameReCall(reValues) {
  console.log(&quot;reValues = &quot;,reValues);
}
nameCall(name);</code></pre>
<p>위와 같은 코드가 있다고 가정할 때, 먼저 전역컨텍스트가 CallStack에 할당(push)되게된다. 이후의 순서를 살펴 보자.</p>
<ol>
<li>CallStack - [전역컨텍스트] - 전역컨텍스트 Push.</li>
<li>CallStack - [전역컨텍스트, nameCall함수 컨텍스트] - nameCall함수 컨텍스트 Push.</li>
<li>CallStack - [전역컨텍스트, nameCall함수 컨텍스트, console.log(&quot;values = &quot;,values)] - console.log(&quot;values = &quot;,values) 실행</li>
<li>CallStack - [전역컨텍스트, nameCall함수 컨텍스트, nameReCall함수 컨텍스트] - console.log(values)는 실행되고 Pop되었고, 이후 nameReCall 함수 컨텍스트가 Push.</li>
<li>CallStack - [전역컨텍스트, nameCall함수 컨텍스트 nameReCall함수 컨텍스트, console.log(&quot;reValues = &quot;,reValues)] - console.log(reValues) 실행</li>
<li>CallStack - [전역컨텍스트, nameCall함수 컨텍스트 nameReCall함수 컨텍스트] - console.log(&quot;reValues = &quot;,reValues)는 실행되고 Pop.</li>
<li>CallStack - [전역컨텍스트, nameCall함수 컨텍스트] - nameReCall 함수 컨텍스트 Pop</li>
<li>CallStack - [전역컨텍스트] - nameCall 함수 컨텍스트 Pop</li>
<li>CallStack - [] - 전역컨텍스트 Pop.</li>
</ol>
<p>이러한 순서를 통하여 Stack형식으로 엔진이 자바스크립트를 실행하게 된다. 그리고 그 결과 값은 아래와 같다.
<img src="https://velog.velcdn.com/images/villain_uni/post/4847f1c3-4e28-4b12-8f4b-4c366c18ff0e/image.png" alt=""></p>
<p>먼저 values = Cain이라는 순서 4번에 해당하는 값이 찍히고, 그 이후 6번 reValues = Cain 이 찍히는 모습을 볼 수 있다.</p>
<h2 id="execution-context의-구성">Execution Context의 구성</h2>
<p>실행 컨텍스트는 물리적으로 객체의 형태를 가지며, 3가지 프로퍼티를 갖고 있다.</p>
<h3 id="1-변수-객체">1. 변수 객체</h3>
<p>변수 객체(Variable Object)는 실행에 필요한 정보를 담은 객체를 생성하는데 이 객체를 변수 객체 라고한다.
변수 객체는 변수, 함수선언(표현식X) 매개변수와 인수정보 와 같은 정보들을 담고 있다.
변수 객체는 크게 전역 컨텍스트와 함수 컨텍스트에서 각각 가르키는바가 틀린 부분이 있는데, 먼저 전역 컨텍스트에서는 전역변수와 전역함수를 프로퍼티로 소유한 전역 객체(Global Object)를 가르킨다.
함수 컨텍스트에서는 지역변수, 내부함수, 매개변수, 인수정보를 배열의 형태로 담고있는 활성 객체(Activation Object)를 가르킨다.
<br></p>
<h3 id="2-스코프-체인">2. 스코프 체인</h3>
<p>스코프 체인(Scope Chain)은 일종의 리스트로써 전역 객체와 중첩된 함수의 스코프의 레퍼런스를 차례로 저장하고, 각각의 스코프가 어떻게 연결 되고 있는지를 보여주는것이다.</p>
<pre><code class="language-javascript">// Code 1

let a = &quot;This is A&quot;;
function whatA() {
    let a = &quot;It&#39;s A?&quot;; 
    function whatIts() {
        console.log(a);
    }
    whatIts();
}

whatA(); // It&#39;s A?</code></pre>
<pre><code class="language-javascript">// Code 2

let a = &quot;This is A&quot;;
function whatA() {
//    let a = &quot;It&#39;s A?&quot;; 
    function whatIts() {
        console.log(a);
    }
    whatIts();
}

whatA(); // This is A</code></pre>
<p>위와 같은 두 코드가 있다고 가정 해 보자. 우리는 앞서 실행컨텍스트가 어떤식으로 관리되는지 보았을것이다. </p>
<p>Code 1은 CallStack에 의해 a값을 찾을때 자신이 소속된 함수의 스코프내에서 a값을 찾게된다. 
그리고 let a = &quot;It&#39;s A?&quot;; 라는 지역변수 a를 찾게되어 해당 값을 출력하는 모습을 볼 수 있다. 
그러나 Code 2에서는 지역변수 a를 찾을 수 없음으로 상위스택인 전역스코프를 참조하여 let a = &quot;This is A&quot;;라는 전역변수 a를 찾게되어 해당 값을 출력하는 모습을 볼 수 있다.</p>
<p>이런식으로 엔진이 해당 컨텍스트의 스코프가 어떻게 연결되어있는지를 탐색하는 과정들을 스코프 체인이라고 볼 수 있다.</p>
<h3 id="3-this-value">3. this value</h3>
<p>this란 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는(값을 참조하는) 식별자이다. 실행컨텍스트가 생성되는 과정에 있어 아래 후술할 생성단계 의 마지막에 this의 값이 할당되는데, 이때 참조하는 값이 없거나 this의 값이 결정되기 이전에는 this는 전역객체를 가리키게된다.</p>
<p>상황에 따른 this를 알아보자.</p>
<ol>
<li><p>객체의 메서드 호출시 this
메서드란 객체의 프로퍼티가 함수일 경우 이 함수를 메서드 라고 한다.</p>
<pre><code class="language-javascript">let propertys = {
 name: &quot;test&quot;,
 thisis: function() {
     console.log(this);
     console.log(this.name);
 }
};
propertys.thisis(); // {name: &#39;test&#39;, thisis: function}
// test</code></pre>
<p>propertys의 메서드thisis의 this는 자신이 속한 객체 propertys를 가리키는 걸 볼 수 있다.</p>
</li>
<li><p>함수 호출시 this
함수 호출시 this는 전역객체에 바인딩된다. 브라우저에서 전역 객체는 window 객체가 되는데 이때 전역변수는 전역객체의 프로퍼티로도 접근할수 있어 아래와 같은 결과가 나온다.</p>
<pre><code class="language-javascript">var test = &quot;this is test&quot;;
console.log(window.test);
</code></pre>
</li>
</ol>
<p>var testCall = function(){
    console.log(this.test);
}
testCall();
// this is test
// this is test</p>
<pre><code>
3. 생성자 함수 호출 시 this
```javascript
function test(a,b) {
    this.a = a;
    this.b = b;
}

var createTest = new test(&quot;test&quot;, 0);
createTest.a; // test
createTest.b; // 0</code></pre><h2 id="execution-context의-생성실행-과정">Execution Context의 생성/실행 과정</h2>
<h3 id="1-creation-phase">1. Creation Phase</h3>
<p>실행 컨텍스트의 생성단계에서는 변수객체 혹은 활성객체가 생성되고 호이스팅이 일어나며, 스코프체인이 생생되고, this의 값이 결정된다.</p>
<h3 id="2-execution-phase">2. Execution Phase</h3>
<p>실행 컨텍스트의 실행단계에서는 생성된 변수의 값이 할당되고, 코드가 실행된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Hoisting에 대해 알아보자]]></title>
            <link>https://velog.io/@villain_uni/2023.10.26-TIL</link>
            <guid>https://velog.io/@villain_uni/2023.10.26-TIL</guid>
            <pubDate>Thu, 26 Oct 2023 14:22:12 GMT</pubDate>
            <description><![CDATA[<h1 id="hoisting">Hoisting</h1>
<p><strong>H</strong>oisting은 자바스크립트에서 발생하는 현상 중 하나로 변수와 함수를 선언하는 위치와 상관없이 해당스코프의 최상위로 끌어올려지는것을 말한다.</p>
<pre><code class="language-javascript">a();

function a(){
    console.log(&quot;a&quot;);
}
// 에러가 아닌 함수 a가 실행되는 모습을 볼 수 있다.</code></pre>
<p>단, 값이 할당되는 과정이 아닌 선언되는 과정에서 호출되기때문에 값이 비어있는 모습을 볼 수 있다.</p>
<pre><code class="language-javascript">console.log(a); // undefined
var a = 1;
console.log(a); // 1</code></pre>
<p>당연하게도 지역변수로 선언된 변수는 해당 지역안에서 최상위로 올라와지는것이지 전역에서 올라와지는것이 아니기때문에 에러가 발생한다.</p>
<pre><code class="language-javascript">a(); // a
console.log(b); // b is not defined;
function a(){
      const b= 0;
    console.log(&quot;a&quot;);
}</code></pre>
<p>ECMAScript 6 에서 추가된 let과 const는 변수 선언시 블록 스코프 변수이기때문에 var와는 다르게 undefined가 아닌 is not defined 에러가 발생한다.</p>
<pre><code class="language-javascript">console.log(b); // b is not defined
let b = 1;
console.log(b); // 1</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Closure에 대해 알아보자]]></title>
            <link>https://velog.io/@villain_uni/2023.10.25-TIL</link>
            <guid>https://velog.io/@villain_uni/2023.10.25-TIL</guid>
            <pubDate>Wed, 25 Oct 2023 12:07:14 GMT</pubDate>
            <description><![CDATA[<h1 id="closure">Closure</h1>
<p>우리가 특정 함수에서만 컨트롤 되어야 하는 변수 special이 있다고 가정해보자.</p>
<pre><code class="language-javascript">let special = 0;
function specialHandle() {
  special += 1;
}
console.log(special); // 0
specialHandle(); // special 컨트롤
console.log(special); // 1</code></pre>
<p>위와 같이 컨트롤 할 수 있는데 만약 누군가가 실수로 인해 special값을 임의로 손대버리면 값이 변하게 되어버린다.</p>
<pre><code class="language-javascript">let special = 0;
function specialHandle() {
  special += 1;
}
console.log(special); // 0
specialHandle(); // special 컨트롤
console.log(special); // 1
special = 100;
specialHandle();
console.log(special); // 101</code></pre>
<p>물론 한두줄만 있는 코드라면 금방 찾을 수 있는데 여러명과 협업하게되고 코드의 줄 수 가 길어질수록 이러한 실수는 찾기 힘들어진다.</p>
<p>specialHandle로 수정할때를 제외하고 special변수를 접근 못하게하는 방법이 무엇이 있을까?</p>
<p>이때 사용하는게 closure이다.</p>
<p><strong>Closure</strong>는 MDN문서를 보면 &quot;주변 상태(어휘적 환경)에 대한 참조와 함께 묶인(포함된) 함수의 조합&quot; 이라고 나와있다.</p>
<p>이렇게 보면 어려운 말이고 무슨 뜻인지 이해하기 힘든데 밑의 예시를 봐보자.</p>
<pre><code class="language-javascript">function closure() {
  let special = 0;
  function specialHandle() {
    special += 1;
  }
  return {
    specialHandle,
  }
}
console.log(special); // special is not defined
const closureSpecial = closure();
console.log(closureSpecial); // {specialHandle : Function}
</code></pre>
<p>기존과 달리 special을 closure라는 함수안에서 선언함으로써 전역변수에서 지역변수가 된 걸 확인할 수 있다. 또한 closure함수에서 specialHandle을 리턴해줌으로써 closureSpecial에 specialHandle이라는 내부함수가 담김으로써 special을 컨트롤 할 수 있게되었다.</p>
<p>여기서 만약 내가 special을 출력하고싶으면 어떤방식을 사용해야할까? special은 지역변수기때문에 그냥 접근은 할 수 없고 특정한 방법을 사용해야한다.</p>
<pre><code class="language-javascript">function closure() {
  let special = 0;
  function specialHandle() {
    special += 1;
  }
  function consoleSpecial() {

    console.log(special);
  }
  return {
    specialHandle,
    consoleSpecial,
  }
}
const closureSpecial = closure();
closureSpecial.consoleSpecial(); // 0
closureSpecial.specialHandle();
closureSpecial.consoleSpecial(); // 1</code></pre>
<p>return을 똑바로 안해줄경우 아래와 같은 에러가 발생하게된다. 주의하자.</p>
<pre><code class="language-javascript">function closure() {
  let special = 0;
  function specialHandle() {
    special += 1;
  }
  function consoleSpecial() {
    console.log(special);
  }
  return {
    specialHandle,
  }
}
const closureSpecial = closure();
closureSpecial.consoleSpecial(); // closureSpecial.consoleSpecial() is not define</code></pre>
<p>이런식으로 클로저 개념을 이용하여 함수를 이용해 지역변수로 선언해주면 특정 함수에서만 컨트롤 할 수 있는 나만의 변수가 만들어지게된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Scope에 대해 알아보자]]></title>
            <link>https://velog.io/@villain_uni/2023.10.24-TIL</link>
            <guid>https://velog.io/@villain_uni/2023.10.24-TIL</guid>
            <pubDate>Tue, 24 Oct 2023 08:39:45 GMT</pubDate>
            <description><![CDATA[<h1 id="javascript---scope">Javascript - Scope</h1>
<p><strong>J</strong>avascript에서 Scope는 어떠한 <strong>참조 대상의 식별자</strong>를 찾아내기 위한 규칙이다.</p>
<blockquote>
<p>여기서 말하는 <strong>식별자</strong>란 무엇일까?
식별자(identifier)는 변수, 함수의 이름과 같이 어떤 대상을 다른 대상과 구분하여 식별할 수 있는 이름이다.</p>
</blockquote>
<pre><code class="language-javascript">const a = &quot;identifier&quot;
// String&quot;identifier&quot;라는 값을 가진 상수 a. 이때 a는 식별자라고 할 수 있다.</code></pre>
<br>
<br>

<h3 id="스코프">스코프</h3>
<p>스코프는 크게 전역(Global) 스코프와 지역(Local / Function-Level) 스코프로 나눌 수 있다.
<br>
전역스코프는 코드 어디에서든 참조할 수 있다는걸 의미하고, 지역 스코프는 지역(함수), 블록이 만든 스코프로 해당 함수와 그 하위 함수에서만 참조할 수 있다.
<br></p>
<p>ECMAScript6 이전에는 변수를 선언시 var라는 키워드만으로 변수를 선언하여 Javascript에서는 블록({}) 레벨로 스코프를 사용할 수 없었는데, let과 const라는 키워드가 도입된 후로 블록 레벨 스코프를 사용할 수 있게 되었다.</p>
<pre><code class="language-javascript">// var
var a = 1;
{
  var a = 2;
  console.log(a); // 2
}
console.log(a); // 2

// let
let b = 1;
{
  let b = 3;
  console.log(b); // 3
}
console.log(b); // 1</code></pre>
<br>
<br>

<h3 id="전역스코프와-지역스코프">전역스코프와 지역스코프</h3>
<p>전역스코프와 지역스코프는 다음의 예시로 볼 수 있다.</p>
<pre><code class="language-javascript">let globalScope = &quot;global&quot;; // 전역스코프
function test() {
  let localScope = &quot;local&quot;; // 지역스코프
  console.log(globalScope);
  console.log(localScope);
}
test();
// global
// local
console.log(globalScope);
// global
console.log(localScope);
// localScope is not defined</code></pre>
<p>위 예시와 같이 전역에서 선언된 globalScope에는 함수내,외에서 호출이 가능한데, test라는 함수 안에서 선언된 localScope는 test함수 내에서만 호출이 가능하고 test함수 외에서는 호출이 불가하다.
<br>
<br>
그렇다면 만약 함수안에서 새로운 함수와 변수를 생성하여 값을 변형시킨다면 어떤 결과가 나올까?</p>
<pre><code class="language-javascript">let global = 10;
function test() {
    let global = 100;
    console.log(global); //100

    function test2() {
        global = 2000;
        console.log(global); //2000 test()함수 내에서 선언된 global을 참조하여 값을 재할당
    }
    test2();
}
test();
console.log(global); //10 전역에서 선언된 global을 참조하여</code></pre>
<p>위 예시와 같이 해당 스코프에 의해 global값을 참조하여 출력하는 결과를 볼 수 있다.
<br>
<br>
<br></p>
<h3 id="렉시컬-스코프">렉시컬 스코프</h3>
<p>스코프는 함수를 어디서 호출하였는지에 따라 상위스코프를 결정하는 동적(dynamic)스코프 와 함수를 어디서 선언하였는지에 따라 스코프를 결정하는 렉시컬(Lexical or 정적 static)스코프로 나뉜다.</p>
<pre><code class="language-javascript">let value = 1;
function test() {
  let value = 10;
  function test2(){
    console.log(value); //10 dynamic scope
  }
  test2();
  test3();
}
function test3() {
  console.log(value); //lexical scope
}
test(); // 1
test3(); // 1</code></pre>
<p>위 예시와 같이 test함수내의 새로운 함수 test2에서는 value가 10으로 결정되었지만, test함수내에서 호출한 test3는 test함수 밖에있기때문에 함수를 어디서 선언하였는지를 따라가 value의 값이 1이 나온걸 볼 수 있다.</p>
<br>
<br>
<br>
<br>
<br>

<p>오늘 간단하게 스코프에 대해 알아보았는데, 스코프와 밀접한 관계가 있는 <strong>클로저, 호이스팅</strong>에 대해서도 이후 다뤄 볼 예정이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[static, method, override]]></title>
            <link>https://velog.io/@villain_uni/%ED%98%BC%EA%B3%B5%EC%8A%A4-Study-static-method-override</link>
            <guid>https://velog.io/@villain_uni/%ED%98%BC%EA%B3%B5%EC%8A%A4-Study-static-method-override</guid>
            <pubDate>Tue, 15 Feb 2022 11:01:53 GMT</pubDate>
            <description><![CDATA[<h1 id="static-method">static, method</h1>
<p>프레임워크를 더 효율적으로 개발할 수 있게 다양한 패턴을 고안하는데, 이를 <strong>디자인 패턴</strong> 이라고 한다.
이러한 디자인 패턴을 사용하기위해 추가된 문법이있는데 <strong>static속성</strong>과 <strong>static메소드</strong>이다. </p>
<pre><code class="language-javascript">class ClassName {
  static attr = value
  static method () {
  }
}

ClassName.attr;
ClassName.method();</code></pre>
<p>이를 이용해 간단하게 사용문법을 만들어보면 아래와 같다.</p>
<pre><code class="language-javascript">class className {
  static attr = 30;
  static stMethod () {
    console.log(&quot;This is static method&quot;);
  }
}

className.attr // 30
className.stMethod() // This is static method</code></pre>
<p>이를 이용해 제곱을 계산해주는 class의 static도 만들 수 있다.</p>
<pre><code class="language-javascript">class calculator {
  static square(value) {
    value *= value;
    console.log(value);
  }
}

calculator.square(30); // 900</code></pre>
<h1 id="override">override</h1>
<p>부모가 갖고 있는 함수를 자식에서 다시 선언해서 덮어쓰는 것을 <strong>override</strong>라고 한다.</p>
<pre><code class="language-javascript">class className extends 상속하는ClassName {
  상속하는 class의 함수
}</code></pre>
<p>위와 같은 방식으로 사용할 수 있다.</p>
<p>다음은 예시로 abc라는 class를 만들었다.</p>
<pre><code class="language-javascript">class abc {
    call () {
        this.a();
        this.b();
        this.c();
    }

    a () {
        console.log(&quot;A Method&quot;);
    }

    b () {
        console.log(&quot;B Method&quot;);
    }

    c () {
        console.log(&quot;C Method&quot;);
    }
}

new abc().a() // A Method</code></pre>
<p>그리고 abc class를 상속받아 a method를 override 해보겠다.</p>
<pre><code class="language-javascript">class overrideABC extends abc {
  a () {
    console.log(&quot;A&#39;s child Method&quot;);
  }
}

new overrideABC().a() // A&#39;s child Method</code></pre>
<p>위와 같이 a method 혹은 함수들을 상속받아 사용하는것을 override했다 라고 한다.</p>
<p>만약 위와같이 재구성하지않고 부모의 a method를 사용하고 싶다면 super를 사용하면된다.</p>
<pre><code class="language-javascript">class overrideABC extends abc {
  a() {
    super.a();
    console.log(&quot;A&#39;s child Method&quot;);
  }
}

new overrideABC().a() // A Method A&#39;s child Method</code></pre>
<p>이런식으로 사용하면 된다.</p>
<h3 id="tostring">toString()</h3>
<p>어떤 객체를 문자열로 만들때 toString()이라는 메소드를 사용하였다. 자바 스크립트 모든 객체는 toString()을 갖는데, 이는 Object가 최상위 클래스를 가지며 어떤클래스를 만들어도 Object클래스로부터 상속을 받게되기 때문에 발생하는 현상이다. 이를 이용해 클래스를 만들때 toString()이라는 메소드를 만들면 Obeject로부터 상속받은 toString()을 override하게된다.</p>
<pre><code class="language-javascript">class Pet {
    constructor (name, age) {
        this.name = name;
        this.age = age;
    }

    toString() {
        return `이름: ${this.name}\n나이: ${this.age}`
    }
}

const pet = new Pet(&#39;나비&#39;, 6);
console.log(pet); // Pet {name: &#39;나비&#39;, age: 6}
pet.toString(); // &#39;이름: 나비\n나이: 6&#39;</code></pre>
<p>위와같이 그냥 객체를 문자열로 바꾸는게 아닌 이름: ${this.name}\n나이: ${this.age} 에 맞춰 바뀐 모습을 볼 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[고급예외처리 ~ 예외객체]]></title>
            <link>https://velog.io/@villain_uni/%ED%98%BC%EA%B3%B5%EC%8A%A4-Study-%EA%B3%A0%EA%B8%89%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC-%EC%98%88%EC%99%B8%EA%B0%9D%EC%B2%B4</link>
            <guid>https://velog.io/@villain_uni/%ED%98%BC%EA%B3%B5%EC%8A%A4-Study-%EA%B3%A0%EA%B8%89%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC-%EC%98%88%EC%99%B8%EA%B0%9D%EC%B2%B4</guid>
            <pubDate>Wed, 09 Feb 2022 09:25:42 GMT</pubDate>
            <description><![CDATA[<h1 id="고급-예외-처리">고급 예외 처리</h1>
<blockquote>
<p>고급 예외 처리에는 try catch finally 구문이 있다. 기본적인 구조는 아래와 같다.</p>
</blockquote>
<pre><code class="language-javascript">try {
  // 예외가 발생할 가능성이 있는 코드
} catch(exception) {
  // 예외가 발생할 경우 실행되는 코드
} finally {
  // 예외와 상관없이 무조건 실행할 코드. 필요한 경우에만 사용
}</code></pre>
<p>예시로 살펴보자</p>
<pre><code class="language-javascript">try {
    // console.log(&quot;Hi&quot;)
    errorStart.asdf();

} catch(exception) {
    alert(&quot;에러발생&quot;)
}
// finally {
//     console.log(&quot;무조건 무조건이야~&quot;)
// }</code></pre>
<p>위의 js코드에서 try문에서 errorStart.asdf()를 실행시킬경우 그런 것은 존재하지않기에 catch문의 코드가 실행된다. 반대로 errorStart~를 주석처리하고 console.log(&quot;Hi&quot;)를 실행시키면 catch문이 실행되지않는것을 볼 수 있다. catch문은 에러가 발생할때만 실행되기 대문에 에러가 발생하지않으면 실행되지않는다. 이후 finally문을 실행시키면 console.log를 실행시키든 errorStart를 실행시키든 무조건 콘솔창에 해당 문구가 적히는 모습을 볼 수 있다.</p>
<p>errorStart 밑에 console.log 를 찍게되면 errorStart에서 이미 에러가 발생하였기에 더이상 try문을 진행하지않고 바로 catch문으로 빠져나가는 모습을 볼 수 있다.</p>
<pre><code class="language-javascript">try {
    errorStart.asdf();
    console.log(&quot;야 찍혔냐?&quot;);

} catch(exception) {
    alert(&quot;에러발생&quot;);
}</code></pre>
<p>그러면 여기서 한가지 의문이 생길 수 있다. catch문을 놔두고 왜 finally를 써야할까??</p>
<p>이는 return 키워드를 통해 예시로써 알아 볼 수 있다. </p>
<pre><code class="language-javascript">function test () {
    try {
        console.log(&quot;try 시작&quot;);
        throw &quot;예외 발생&quot;
    } catch(exception) {
        console.log(&quot;catch 시작&quot;);
        return
    }

    console.log(&quot;try catch 끝&quot;);
}
test();

function test2 () {
    try {
        console.log(&quot;try 시작&quot;);
        throw &quot;예외 발생&quot;
    } catch(exception) {
        console.log(&quot;catch 시작&quot;);
        return
    }
    finally {
        console.log(&quot;try catch 끝&quot;);
    }
}
test2();</code></pre>
<p>위의 코드를 실행시켜보면 test의 경우 catch에서 return 값을 주었기때문에 그 이후 console.log가 실행되지않는 모습을 볼 수 있다.</p>
<p>그러나 finally문으로 감싸줄 경우 return 하였음에도 finally문의 강제성 덕분에 try catch 끝 이라는 콘솔이 찍히는 모습을 볼 수 있다.</p>
<h1 id="예외-객체">예외 객체</h1>
<blockquote>
<p>프로그래밍을 하다보면 예외가 생길 수 있고 그러한 예외와 발생된 정보를 확인할 수 있는데, 이러한 정보를 확인하게 해주는것이 <strong>예외 객체</strong>이다.</p>
</blockquote>
<p>위의 고급 예외 처리에서 catch문 안에 (exception)을 적은 모습을 볼수있는데 (exception)이 예외 객체이다.</p>
<pre><code class="language-javascript">function test3 () {
    try {
        throw &quot; 강제 예외 발생 &quot;
    } catch (exception) {
        console.log(&quot;예외 발생&quot;);
        console.log(exception);
    }
}
test3();</code></pre>
<p>throw문법을 이용해 강제로 예외를 발생시기고 catch문에서 console.log로 exception을 출력시키면 throw의 내용이 출력된다.</p>
<pre><code class="language-javascript">function test4 () {
    try {
        const a = new Array(9999999999999999999999999999999999);
    } catch (exception) {
        console.log(exception);
    }
}
test4();</code></pre>
<p>위와 같이 자바스크립트가 처리할수있는 배열의 크기를 넘게 입력하여 강제로 오류를 발생시키고 console.log로 예외객체를 출력시키면 RangeError가 출력되고 어디에서 에러가 떳는지 파일이름과 에러가 발생한 라인을 보여주는 모습을 볼 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[글자 입력 양식 이벤트]]></title>
            <link>https://velog.io/@villain_uni/%ED%98%BC%EA%B3%B5%EC%8A%A4-Study-%EA%B8%80%EC%9E%90-%EC%9E%85%EB%A0%A5-%EC%96%91%EC%8B%9D-%EC%9D%B4%EB%B2%A4%ED%8A%B8</link>
            <guid>https://velog.io/@villain_uni/%ED%98%BC%EA%B3%B5%EC%8A%A4-Study-%EA%B8%80%EC%9E%90-%EC%9E%85%EB%A0%A5-%EC%96%91%EC%8B%9D-%EC%9D%B4%EB%B2%A4%ED%8A%B8</guid>
            <pubDate>Mon, 07 Feb 2022 10:32:08 GMT</pubDate>
            <description><![CDATA[<h1 id="글자-입력-양식-이벤트">글자 입력 양식 이벤트</h1>
<blockquote>
<p>입력양식이란? 사용자로부터 어떠한 입력을 받을 때 사용하는 요소.</p>
</blockquote>
<p>HTML에서는 input, textarea, Button, select 등이 입력양식이다.</p>
<h2 id="input">input</h2>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;HTML_STUDY&lt;/title&gt;
    &lt;style&gt;
        input {
            width: 300px;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Welcome to Study!&lt;/h1&gt;
    &lt;p&gt;cm 를 m 로 변환하기&lt;/p&gt;
    &lt;input class=&quot;changeCm&quot; type=&quot;number&quot; placeholder=&quot;변환하고자 하는 cm를 적어주세요.&quot;&gt;
    &lt;button class=&quot;cmButton&quot;&gt;변환&lt;/button&gt;
    &lt;p class=&quot;valueCm&quot;&gt;&lt;/p&gt;

    &lt;script&gt;
        const changeCm = document.querySelector(&quot;.changeCm&quot;);
        const cmButton = document.querySelector(&quot;.cmButton&quot;);
        const valueCm = document.querySelector(&quot;.valueCm&quot;);

        function changeEvent() {
            valueCm.innerText = `${changeCm.value}cm의 변환값은 ${changeCm.value/100}m입니다.`
            changeCm.value = &quot;&quot;;
        }
        cmButton.addEventListener(&#39;click&#39;, changeEvent)
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>위와 같은 방식으로 input요소로부터 입력을 받아 숫자를 변환시키는 작업을 할 수 있다.</p>
<h2 id="select">select</h2>
<p>이를 활용해 여러 숫자변환을 드롭다운 형식으로도 구현할 수 있다.</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;HTML_STUDY&lt;/title&gt;
    &lt;style&gt;
        input {
            width: 300px;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Welcome to Study!&lt;/h1&gt;
    &lt;p&gt;cm를 변환시켜보자 (소수점 둘째 자리까지만 출력)&lt;/p&gt;
    &lt;input class=&quot;changeCm&quot; type=&quot;number&quot; placeholder=&quot;변환하고자 하는 cm를 적어주세요.&quot;&gt;
    &lt;br /&gt;
    &lt;select id=&quot;selectChange&quot;&gt;
        &lt;option&gt;---선택---&lt;/option&gt;
        &lt;option&gt;미터&lt;/option&gt;
        &lt;option&gt;킬로미터&lt;/option&gt;
        &lt;option&gt;인치&lt;/option&gt;
    &lt;/select&gt;
    &lt;p class=&quot;valueCm&quot;&gt;&lt;/p&gt;

    &lt;script&gt;
        const changeCm = document.querySelector(&quot;.changeCm&quot;);
        const valueCm = document.querySelector(&quot;.valueCm&quot;);
        const selectChange = document.querySelector(&quot;#selectChange&quot;);

        selectChange.addEventListener(&#39;change&#39;, (event)=&gt;{
            const options = event.target.options;
            const index = event.target.options.selectedIndex;
            if (index == 0) {
                valueCm.innerText = &quot;&quot;;

            }
            else if (index == 1) {
                valueCm.innerText = `${changeCm.value}cm의 변환값은 ${(changeCm.value/100).toFixed(2)}m 입니다.`
            }
            else if (index == 2) {
                valueCm.innerText = `${changeCm.value}cm의 변환값음 ${(changeCm.value/100000).toFixed(2)}km 입니다.`
            }
            else if (index == 3) {
                valueCm.innerText = `${changeCm.value}cm의 변환값음 ${(changeCm.value/2.54).toFixed(2)}inch 입니다.`
            }
        })

    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>위와 같이 select박스를 이용하여 변환값을 출력 할 수 있다. 또한 toFixed메소드를 통해 소수점을 둘째자리까지 출력할 수 있도록 적용하였다.</p>
<h2 id="checkbox">checkbox</h2>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;HTML_STUDY&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Welcome to Study!&lt;/h1&gt;
    &lt;p&gt;checkBox 사용하기&lt;/p&gt;
    &lt;input class=&quot;inputCheck&quot; type=&quot;checkbox&quot;&gt;&lt;span&gt;타이머 활성화&lt;/span&gt;
    &lt;p class=&quot;timerOutput&quot;&gt;&lt;/p&gt;

    &lt;script&gt;
        const inputCheck = document.querySelector(&quot;.inputCheck&quot;);
        const timerOutput = document.querySelector(&quot;.timerOutput&quot;);
        let [timer, timerId] = [0, 0]

        inputCheck.addEventListener(&#39;change&#39;, (event) =&gt; {
            if(event.target.checked){
                timerId = setInterval(()=&gt;{
                    timer += 1;
                    timerOutput.textContent = `${timer}초`
                }, 1000)
            }
            else{
                clearInterval(timerId)
            }
        })

    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>위 코드를 이용하여 체크박스가 checked되었을경우 setInterval에 의해 시간이 지나게 하고, checked가 풀릴경우 clearInterval로 시간을 멈추게 한다.</p>
<h2 id="radiobutton">radiobutton</h2>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;HTML_STUDY&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Welcome to Study!&lt;/h1&gt;
    &lt;p&gt;나는 어떤 인간일까? with &lt;strong&gt;radioBox&lt;/strong&gt;&lt;/p&gt;
    &lt;input type=&quot;radio&quot; name=&quot;human&quot; value=&quot;아침형인간&quot;&gt;&lt;span&gt;아침형인간&lt;/span&gt;
    &lt;input type=&quot;radio&quot; name=&quot;human&quot; value=&quot;점심형인간&quot;&gt;&lt;span&gt;점심형인간&lt;/span&gt;
    &lt;input type=&quot;radio&quot; name=&quot;human&quot; value=&quot;저녁형인간&quot;&gt;&lt;span&gt;저녁형인간&lt;/span&gt;
    &lt;input type=&quot;radio&quot; name=&quot;human&quot; value=&quot;심야형인간&quot;&gt;&lt;span&gt;심야형인간&lt;/span&gt;

    &lt;h3 class=&quot;myType&quot;&gt;&lt;/h3&gt;

    &lt;script&gt;

        const myType = document.querySelector(&quot;.myType&quot;);
        const humans = document.querySelectorAll(&quot;[name=human]&quot;)

        humans.forEach((radio) =&gt; {
            radio.addEventListener(&#39;change&#39;, (event)=&gt;{
                const radios = event.target;
                if(radios.checked){
                    myType.textContent = `당신은 ${radios.value}입니다.`;
                }
            })
        })

    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>위와 같이 radio라는 input type을 이용하여 내가 해당하는 인간에 체크를 할 경우 해당 부류의 인간이라고 출력되는 모습을 볼 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spread Operator]]></title>
            <link>https://velog.io/@villain_uni/JS200-Study-%EC%A0%84%EA%B0%9C-%EC%97%B0%EC%82%B0%EC%9E%90</link>
            <guid>https://velog.io/@villain_uni/JS200-Study-%EC%A0%84%EA%B0%9C-%EC%97%B0%EC%82%B0%EC%9E%90</guid>
            <pubDate>Wed, 26 Jan 2022 09:55:44 GMT</pubDate>
            <description><![CDATA[<h1 id="spread-operator">Spread Operator</h1>
<blockquote>
<p>전개연산자 : [...배열] , {...객체}</p>
</blockquote>
<p>전개연산자는 얕은 복사가 아닌 깊은 복사를 하기위해 사용하는 연산자이다.</p>
<h1 id="깊은복사-얕은복사">깊은복사? 얕은복사?</h1>
<p>자바스크립트에서 배열과 객체는 할당할때 얕은 복사라는것을 한다. </p>
<pre><code class="language-javascript">const food = [&#39;apple&#39;, &#39;peach&#39;, &#39;berry&#39;];
const food2 = food;
food2.push(&#39;rice&#39;);
console.log(food);
console.log(food2);</code></pre>
<p>위의 결과물을 보면 아래와 같은 사진이 나온다.
<img src="https://images.velog.io/images/villain_uni/post/583890ef-165f-4291-8927-41483f1b99a3/%EC%96%95%EC%9D%80%EB%B3%B5%EC%82%AC1.PNG" alt="">
이런식으로 새로운 변수에 배열을 할당해주었는데도 불구하고 같은 값을 갖는 것을 <strong>얕은 복사(참조복사)</strong> 라고 한다.</p>
<p>깊은 복사는 이와 반대로 배열이 새로운 변수에 할당될때 독릭접으로 할당되는 경우를 깊은 복사라 한다. 그리고 이때 사용하는것이 위의 전개 연산자 이다.
<img src="https://images.velog.io/images/villain_uni/post/65f1a141-81a4-49f0-9abd-9577d328beec/%EA%B9%8A%EC%9D%80%EB%B3%B5%EC%82%AC.PNG" alt="">
위와같이 food3에 전개연산자를 이용해 새로운 배열을 할당하고 각각의 배열에 melon과 watermelon을 push해주었는데 얕은복사의 food2와는 다르게 서로 다른 배열값을 갖는모습을 볼 수 있다.</p>
<p>자료와 자료 사이에 전개연산자를 기입할 수 있다.
<img src="https://images.velog.io/images/villain_uni/post/bb0716c7-9134-44b3-9398-e2e467155d8c/%EA%B9%8A%EC%9D%80%EB%B3%B5%EC%82%AC2.PNG" alt=""></p>
<p>객체 또한 배열과 마찬가지로 깊은복사 할 때 전개연산자를 사용할 수 있다.
<img src="https://images.velog.io/images/villain_uni/post/2f1835f8-f794-46e1-96ad-fa42c548a906/%EA%B0%9D%EA%B9%8A.PNG" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Callback function]]></title>
            <link>https://velog.io/@villain_uni/JS200-Study-Callback-function</link>
            <guid>https://velog.io/@villain_uni/JS200-Study-Callback-function</guid>
            <pubDate>Wed, 19 Jan 2022 07:30:31 GMT</pubDate>
            <description><![CDATA[<h1 id="콜백-함수">콜백 함수</h1>
<p>자바스크립트의 함수는 사용할때 값을 받아와서 사용할 수 있는데, 함수내에서 이를 매개변수라고 한다. 그리고 매개변수값으로 함수를 전달할때 이를 콜백 함수라고한다.
<img src="https://images.velog.io/images/villain_uni/post/6f4c295b-0fd9-4819-bf4f-c1056a4f9b84/callback1.PNG" alt=""></p>
<p>위 사진을 보면 value과 add 함수를 선언한 후 value의 매개변수로 add함수가 들어간것을 볼 수 있다. 그리고 add함수의 매개변수 a는 value함수의 for문안에서 index값이 들어가 1부터 10까지의 값을 console.log로 찍어낸것을 볼 수 있다. 또한 아래와같은 방식으로도 사용할 수 있다.</p>
<p><img src="https://images.velog.io/images/villain_uni/post/06ca48b3-de99-4ef6-b014-61fd88890d57/callback2.PNG" alt="">
또한 화살표 함수로도 사용할 수 있다.
<img src="https://images.velog.io/images/villain_uni/post/20fdf6f4-ea5c-401e-b12d-f3c80e4abcc9/callback3.PNG" alt=""></p>
<h1 id="foreach">forEach()</h1>
<p>forEach()메소드는 콜백함수를 활용하는 가장 기본적인 함수이다. forEach()메소드는 배열이 갖고 있는 함수로써 단순하게 배열 내부의 요소를 사용해서 콜백함수를 호출해준다.</p>
<blockquote>
<p>forEach()메소드 : forEach(function (value, index, array) {})</p>
</blockquote>
<p><img src="https://images.velog.io/images/villain_uni/post/73f51348-3254-4896-9935-6ec19c1b93eb/foreach1.PNG" alt="">
<img src="https://images.velog.io/images/villain_uni/post/65601af6-4d2a-4f03-a360-1c785ce23743/foreach2.PNG" alt=""></p>
<h1 id="map">map()</h1>
<p>map() 메소드도 forEach와 같은 콜백을 활용한 함수이다. 
<img src="https://images.velog.io/images/villain_uni/post/0e5933a5-dc33-4e73-b8e3-6d7057ad71a0/map1.PNG" alt=""></p>
<p>새로운 배열을 만들기에 console.log로 찍을 경우 리턴받을 값이없어 undefined로 나온다.
<img src="https://images.velog.io/images/villain_uni/post/2b5921e6-ac31-427c-a49b-9d8baf0892ad/map2.PNG" alt=""></p>
<h1 id="filter">filter()</h1>
<p>filter() 메소드는 콜백함수에서 return하는값이 true인것만 모아 새로운 배열을 만들어준다.</p>
<p><img src="https://images.velog.io/images/villain_uni/post/ef639f8e-670b-4352-a3a6-e33a7aa54fcc/filter.PNG" alt=""></p>
<h1 id="화살표-함수">화살표 함수</h1>
<p>위의 함수들을 좀 더 간편히 사용하고자 할 때 화살표 함수를 사용한다.</p>
<blockquote>
<p>(매개변수) =&gt; 리턴값</p>
</blockquote>
<p><img src="https://images.velog.io/images/villain_uni/post/84e15bd4-c221-4cad-bc2a-9e25ba4263e7/%ED%99%94%EC%82%B4%ED%91%9C.PNG" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[json.Stringify]]></title>
            <link>https://velog.io/@villain_uni/JS200-Study-stringify</link>
            <guid>https://velog.io/@villain_uni/JS200-Study-stringify</guid>
            <pubDate>Sat, 15 Jan 2022 13:37:15 GMT</pubDate>
            <description><![CDATA[<h1 id="json을-문자열로-변환하기">JSON을 문자열로 변환하기</h1>
<p>stringify 메소드는 대입한 값을 JSON 문자열로 변환한다.</p>
<blockquote>
<p>JSON.stringify(값, 리플레이서, 공백 개수)</p>
</blockquote>
<p>stringify의 첫 번째 인자는 JSON문자열로 변환할 대상의 값이다.</p>
<p>두 번째 인자는 리플레이서 라고 하는데 JSON문자열로 변환하기 전에 값을 변경하는 인자이다. 
콜백함수를 넣거나, 특정 key 정보를 담은 배열을 넣어 값을 변경한다.</p>
<p>세번째 인자는 공백 개수를 나타낸다. JSON문자열의 들여쓰기 시 공백 개수를 의미한다. 1~10까지 지정할 수 있으며, null이나 0 또는 음수인 경우 공백을 사용하지 않는 것으로 간주한다.</p>
<h2 id="값">값</h2>
<p>stringify를 이용해 JSON 문자열로 변환시켰기 때문에 typeof로 type을 확인할 경우 string으로 나옵니다.
<img src="https://images.velog.io/images/villain_uni/post/18b982b0-3ab9-4738-a874-147f962173b4/jsonstringify.PNG" alt=""></p>
<p>어떤 값을 넣어도 type이 string으로 변환되나 undefined나 함수를 넣을경우 undefined값을 가지게됩니다. NaN을 넣을경우 null을 string값으로 가지게됩니다.
함수의 경우 return값을 가지고 있어도 undefined로 출력됩니다.</p>
<pre><code class="language-javascript">JSON.stringify(&#39;a&#39;); // &#39;&quot;a&quot;&#39;
typeof(JSON.stringify(&#39;a&#39;)); // string
JSON.stringify(1); // &#39;1&#39;
typeof(JSON.stringify(1)); // string
JSON.stringify(&#39;true&#39;); // &#39;&quot;true&quot;&#39;
typeof(JSON.stringify(&#39;true&#39;)); // string
JSON.stringify([1,2,3,4]); // &#39;[1,2,3,4]&#39;
typeof(JSON.stringify([1,2,3,4])); // string
JSON.stringify({x: 10, y : {z : 10}}); //&#39;{&quot;x&quot;: 10, &quot;y&quot;: {&quot;z&quot; : 10}}&#39;
typeof(JSON.stringify({x: 10, y : {z : 10}})); // string
JSON.stringify(null); // &#39;null&#39;
typeof(JSON.stringify(null)); // string
JSON.stringify(undefined); // undefined
typeof(JSON.stringify(undefined)); // undefined
JSON.stringify(NaN); // &#39;null&#39;
typeof(JSON.stringify(NaN)); // string
JSON.stringify(()=&gt;{}); // undefined
JSON.stringify(function() {}); // undefined
typeof(JSON.stringify(function() {})); // &#39;undefined&#39;</code></pre>
<p><img src="https://images.velog.io/images/villain_uni/post/085b4cac-d9e0-4961-b39d-9c04a7c5fd3e/js200_2.PNG" alt="">
문자열로 변환된 만큼 문자열에 관련된 메소드체이닝도 가능합니다.
<img src="https://images.velog.io/images/villain_uni/post/1400d14c-d989-41c1-a7f3-7022757df8ff/js200.PNG" alt="">
그리고 undefined값을 갖는 값들은 당연히 위와같은 메소드체이닝이 불가능합니다.
<img src="https://images.velog.io/images/villain_uni/post/63ef7dc9-c967-4813-b530-96e4e4c40341/js200_1.PNG" alt="">
<del>undefined, function, NaN은 왜 다른지 저도 모르겠습니다.</del></p>
<h2 id="리플레이서">리플레이서</h2>
<p>리플레이서는 JSON문자열로 변환하기전의 값을 변경한다 했는데 아래의 예시를 보면 좀 더 쉽게 이해할 수 있다.</p>
<pre><code class="language-javascript">function stringCheck(key, value) {
    if (typeof value === &quot;string&quot;) {
        return undefined;
    }
    return value;
}

let obj = { name : &quot;Seongyoon&quot;, age : 28, gender : &quot;man&quot;, birth : 1026 }

JSON.stringify(obj, stringCheck)
// &#39;{&quot;age&quot;: 28, &quot;birth&quot;: 1026}&#39;</code></pre>
<p>obj객체를 JSON문자열로 변환하였는데 stringCheck함수를 이용해 변환전의 값을 수정하였다. 그 결과 string값을 가지고있던 name, gender은 undefined가 되었고, number값을 가지고 있던 age와 birth는 JSON문자열로써 출력된것을 확인할 수 있다.</p>
<p>리플레이서는 undefined, 함수, 심볼의 경우 JSON문자열로 변환될때 객체안에있을경우 생략이되고, 배열안에있을경우 null값으로 변환된다.
<img src="https://images.velog.io/images/villain_uni/post/8314eb8d-2830-4c9f-b358-d4fc208b179c/replacer.PNG" alt=""></p>
<h2 id="공백개수">공백개수</h2>
<p>공백 개수는 가독성을 위해 JSON문자열에 공백을 추가해준다.
<img src="https://images.velog.io/images/villain_uni/post/4edb177f-8508-4eaa-a241-593cdb82dcc8/space.PNG" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[중첩반복문]]></title>
            <link>https://velog.io/@villain_uni/%ED%98%BC%EA%B3%B5%EC%8A%A4-Study-%EC%A4%91%EC%B2%A9%EB%B0%98%EB%B3%B5%EB%AC%B8</link>
            <guid>https://velog.io/@villain_uni/%ED%98%BC%EA%B3%B5%EC%8A%A4-Study-%EC%A4%91%EC%B2%A9%EB%B0%98%EB%B3%B5%EB%AC%B8</guid>
            <pubDate>Sat, 15 Jan 2022 11:49:23 GMT</pubDate>
            <description><![CDATA[<h1 id="중첩반복문">중첩반복문</h1>
<blockquote>
<p>중첩반복문이란? 반복문을 여러겹으로 중첩해 사용하는 반복문을 말한다.</p>
</blockquote>
<p>여러겹으로 감싸진 배열을 <strong>n차원 배열</strong>이라 하는데 n차원 배열의 요소를 모두 확인하려면 반복문 또한 n번 중첩해야한다.</p>
<h1 id="사용하기">사용하기</h1>
<h3 id="예시-1-피라미드-만들기">예시 1 피라미드 만들기</h3>
<p><img src="https://images.velog.io/images/villain_uni/post/6e026eff-b1ef-4bed-a8d9-23a0981a9e78/%ED%94%BC%EB%9D%BC%EB%AF%B8%EB%93%9C1.PNG" alt=""></p>
<pre><code class="language-javascript">let value = &#39;&#39;;
for( let index = 0; index &lt; 7; index++){
    value += &#39;*&#39;
    console.log(value)
};</code></pre>
<p>for문을 사용해 이런식으로도 구성할 수 있지만 문제는 for문을 작동했을때 콘솔로그로 한줄 한줄 찍는식으로 위 사진과 같은 결과물을 갖게 된다는점이다.
value라는 변수 자체가 피라미드모양의 값을 가지고 싶으면 중첩 반복문을 사용하면된다.</p>
<pre><code class="language-javascript">let value = &#39;&#39;;

for(let index = 0; index &lt; 7; index++) {
    for(let j = 0; j &lt; index; j++) {
        value += &#39;*&#39;;
    }
    value += &#39;\n&#39;;
}

console.log(value);</code></pre>
<p>이런식으로 구성 할 수 있다.</p>
<h3 id="예시-2-구구단">예시 2 구구단</h3>
<p>중첩 반복문을 이용해 구구단 역시 만들수 있다.</p>
<pre><code class="language-javascript">for(let i = 1; i &lt; 10; i++){
    for (let j = 1; j &lt; 10; j++){
        let x = i*j;
        console.log(`${i} x ${j} = ${x}`);
    }
}</code></pre>
<p><img src="https://images.velog.io/images/villain_uni/post/a7609fed-7ef8-4c2b-8057-c9551bb6be2a/99%EB%8B%A8.PNG" alt=""></p>
<h3 id="문제---피라미드">문제 - 피라미드</h3>
<p>다음 사진과 같은 피라미드를 만들기위해 빈칸을 채우시오.
<img src="https://images.velog.io/images/villain_uni/post/59a058cd-c2d8-4cac-86fa-ab9b949d0c48/%ED%94%BC%EB%9D%BC%EB%AF%B8%EB%93%9C2.PNG" alt=""></p>
<pre><code class="language-javascript">let value = &#39;&#39;;

for (let index = 1; index &lt; 10; index++){
    for (let x = (①) ; x &gt; (②); x--){
        value += &#39; &#39;;
    }
    for (let y = 0; y &lt; ③; y++){
        value += &#39;*&#39;;
    }
    value +=&#39;\n&#39;;
}</code></pre>
]]></description>
        </item>
    </channel>
</rss>