<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>pearlkinn.log</title>
        <link>https://velog.io/</link>
        <description>진주링딩동🎵</description>
        <lastBuildDate>Sat, 24 Feb 2024 04:26:46 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>pearlkinn.log</title>
            <url>https://velog.velcdn.com/images/pearlx_x/profile/bc770eb6-a2e9-438e-b565-f766f38b2456/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. pearlkinn.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/pearlx_x" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[TMI - 사진 용량 줄이기 트러블 슈팅]]></title>
            <link>https://velog.io/@pearlx_x/TMI-%EC%82%AC%EC%A7%84-%EC%9A%A9%EB%9F%89-%EC%A4%84%EC%9D%B4%EA%B8%B0-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85</link>
            <guid>https://velog.io/@pearlx_x/TMI-%EC%82%AC%EC%A7%84-%EC%9A%A9%EB%9F%89-%EC%A4%84%EC%9D%B4%EA%B8%B0-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85</guid>
            <pubDate>Sat, 24 Feb 2024 04:26:46 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/pearlx_x/post/f922334b-de64-4a56-9cd9-dc75f65ce3ab/image.gif" alt="">
최적화 전
<img src="https://velog.velcdn.com/images/pearlx_x/post/5f64a7c4-8b4f-476c-8efc-757d861a7d5a/image.png" alt="">
<img src="https://velog.velcdn.com/images/pearlx_x/post/f04a6aec-62e8-409e-8dae-0cca027b5221/image.gif" alt="">
최적화 후 / 마지막 사진은 최적화 전이다.</p>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/8cc36116-7f47-4c03-bfcd-ad9d5ddecd62/image.gif" alt="">
같은 사진 최적화 전후  / 왼쪽이 최적호 된 이미지 / 오른쪽이 최적화 전 이미지</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TMI - SSOT 리팩토링]]></title>
            <link>https://velog.io/@pearlx_x/TMI-SSOT-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81</link>
            <guid>https://velog.io/@pearlx_x/TMI-SSOT-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81</guid>
            <pubDate>Sat, 24 Feb 2024 04:22:21 GMT</pubDate>
            <description><![CDATA[<p>Login =&gt; pocketbase 업데이트 =&gt; zustand authUserData </p>
<p>컴포넌트 =&gt; zustand</p>
<p>일반 컴포넌트기준</p>
<ol>
<li>Zustand authUserData를 체크</li>
<li>데이터가 있다면 =&gt; 로그인</li>
<li>데이터가 없다면 =&gt; 로그인 안되어 있음.</li>
</ol>
<p>Zustand AuthUserData</p>
<ol>
<li>초기에 localStorage를 확인</li>
<li>데이터가 존재하면 상태 업데이트</li>
<li>데이터가 없으면 놔두면됨.</li>
</ol>
<ul>
<li>SSOT를 준수하도록 리팩토링</li>
</ul>
<p>As-is : 유저 정보를 관리하는 과정에서 SSOT가 지켜지지 않아서,  로그인, 유저 정보 수정 등을 통해 유저 정보가 바뀔 경우, 
To-be : SSOT를 준수하여 로컬 스토리지에서 관리되던 유저정보 데이터를 zustand state 
 checkLogIn: () =&gt; {
    // 컴포넌트가 렌더링 된 후, authUserData가 비어있는지 체크
    // localStorage 확인해서 업데이트
    try {
      const { model, token } = getData(&#39;pocketbase_auth&#39;);</p>
<pre><code>  if (model) {
    set({ isAuth: true, user: model, token: token });
  }
} catch (error) {
  console.log(error);
}</code></pre><p>  },</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TMI 유저 버그 수정]]></title>
            <link>https://velog.io/@pearlx_x/TMI-%EC%9C%A0%EC%A0%80-%EB%B2%84%EA%B7%B8-%EC%88%98%EC%A0%95</link>
            <guid>https://velog.io/@pearlx_x/TMI-%EC%9C%A0%EC%A0%80-%EB%B2%84%EA%B7%B8-%EC%88%98%EC%A0%95</guid>
            <pubDate>Fri, 16 Feb 2024 07:04:59 GMT</pubDate>
            <description><![CDATA[<h1 id="bug-fix-로그인-후-메인으로-넘어올-떄-유저-정보를-받아오지-못하는-버그-수정">:bug: [Fix] 로그인 후 메인으로 넘어올 떄 유저 정보를 받아오지 못하는 버그 수정</h1>
<p>로그인하고 메인페이지로 넘어올 때 로그인한 유저 정보가 바로 넘어오지 않는 버그를 발견하였다.
그래서 코드를 뜯어보다 기존 코드의 storageData가 변하지 않아서 리렌더링이 발생하지 않아 생기는 문제라고 판단하여 버그를 해결하기 위해 생각해보았다.
그래서 생각해낸 결과는 다음과 같다.
로그인을 할 때 상태관리를 zustand를 사용하여 하고 있었는데 </p>
<pre><code class="language-js">
// zustand를 사용하여 로그인 상태관리
  signIn: async (userNameOrEmail, password) =&gt; {
    try {
      const user = await pb
        .collection(&#39;users&#39;)
        .authWithPassword(userNameOrEmail, password);
      set({ isAuth: true, user, token: user.token }); //
    } catch (error) {
      console.error(error);
      throw new Error(&#39;아이디나 비밀번호를 확인해주세요.&#39;);
    }
  },</code></pre>
<p>이곳에 유저의 정보를 담기 때문에 그걸 사용하여 적용해주면 되겠다고 생각했다.</p>
<pre><code class="language-jsx">  const signInAuthData = useAuthStore((store) =&gt; store.user);

  const { storageData } = useStorage(&#39;pocketbase_auth&#39;);
  const [authModelData, setAuthModelData] = useState(storageData?.model);

  useEffect(() =&gt; {
    const pocketBaseAuthData = getData(&#39;pocketbase_auth&#39;);
    if (pocketBaseAuthData) {
      setAuthModelData(pocketBaseAuthData.model);
      return;
    }
    if (signInAuthData) {
      setAuthModelData(signInAuthData.record);
      return;
    }
  }, [signInAuthData, signInAuthData?.record]);
</code></pre>
<p>그래소 위와 같이 코드를 변경했다.
로컬스토리지에 <code>pocketbase_auth</code>정보가 있으면 그걸 가져와서 사용하고 없다면 zustand에 저장되어 있는 user 정보를 받아와서nav 바에 마이페이지 부분에 적용해주었다.</p>
<h2 id="수정전">수정전</h2>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/3e30acb6-645f-4f1d-8483-07a5888b5cbb/image.gif" alt=""></p>
<h2 id="수정후">수정후</h2>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/6c835026-6927-499f-897c-adac4039942e/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TMI - refactoring (userEdit)]]></title>
            <link>https://velog.io/@pearlx_x/TMI-refactoring-userEdit</link>
            <guid>https://velog.io/@pearlx_x/TMI-refactoring-userEdit</guid>
            <pubDate>Wed, 31 Jan 2024 11:15:55 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-jsx">// 기존 코드
import pb from &#39;@/api/pocketbase&#39;;
import ProfileUpload from &#39;@/components/ProfileUpload/ProfileUpload&#39;;
import UserProfilePicture from &#39;@/components/UserProfilePicture/UserProfilePicture&#39;;
import useFetchData from &#39;@/hooks/useFetchData&#39;;
import useStorage from &#39;@/hooks/useStorage&#39;;
import { useEffect, useState } from &#39;react&#39;;
import S from &#39;./UserProfileEdit.module.css&#39;;

const PB = import.meta.env.VITE_PB_URL;
const PB_USER_ENDPOINT = `${PB}/api/collections/users/records`;

function UserProfileEdit() {
  const { data: userData, isLoading } = useFetchData(PB_USER_ENDPOINT);
  const [nickname, setNickname] = useState(&#39;&#39;);
  const [validationResult, setValidationResult] = useState(&#39;&#39;);
  const [usernames, setUsername] = useState([]);
  const { storageData: pocketbaseAuthData } = useStorage(&#39;pocketbase_auth&#39;);

  useEffect(() =&gt; {
    if (userData.items) {
      const usernames = userData.items.map((user) =&gt;
        user.username.toLowerCase()
      );
      setUsername(usernames);
    }
  }, [userData.items]);

  const handleCheckDuplicate = () =&gt; {
    if (isLoading) {
      return;
    }

    if (
      nickname.length &gt;= 2 &amp;&amp;
      nickname.length &lt;= 10 &amp;&amp;
      /^[a-zA-Z0-9]+$/.test(nickname)
    ) {
      const isDuplicate = usernames.includes(nickname.toLowerCase());

      if (isDuplicate) {
        setValidationResult(&#39;중복된 닉네임입니다.&#39;);
      } else {
        setValidationResult(&#39;사용가능한 닉네임입니다&#39;);
      }
    } else {
      setValidationResult(&#39;2 ~ 10글자의 영문 대소문자와 숫자만 입력하세요.&#39;);
    }
  };

  const handleNicknameChange = (value) =&gt; {
    if (
      value.length &gt;= 2 &amp;&amp;
      value.length &lt;= 10 &amp;&amp;
      /^[a-zA-Z0-9]+$/.test(value)
    ) {
      setNickname(value);
      setValidationResult(&#39;&#39;);
    } else {
      setNickname(value);
      setValidationResult(&#39;2 ~ 10글자의 영문 대소문자와 숫자만 입력하세요.&#39;);
    }
  };

  const handleButtonClick = () =&gt; {
    if (!isLoading) {
      handleCheckDuplicate();
    }
  };

  const updateUsernameOnServer = async () =&gt; {
    try {
      const response = await pb
        .collection(&#39;users&#39;)
        .update(pocketbaseAuthData.id, {
          username: nickname,
        });

      if (response.status === 200) {
        return true;
      } else {
        return false;
      }
    } catch (error) {
      console.error(&#39;서버 요청 오류:&#39;, error);
      return false;
    }
  };

  const username =
    pocketbaseAuthData?.model?.username || &#39;사용자 이름이 없습니다&#39;;

  if (pocketbaseAuthData) {
    return (
      &lt;div className={S.profile}&gt;
        &lt;UserProfilePicture avatar={pocketbaseAuthData?.model} /&gt;
        &lt;div className=&quot;h-[90px] flex&quot;&gt;
          &lt;span className=&quot;mt-5 text-xl font-semibold&quot;&gt;{username}&lt;/span&gt;
        &lt;/div&gt;

        &lt;div className={S.editwrapper}&gt;&lt;/div&gt;
        &lt;div className=&quot;flex flex-col gap-6 mt-10&quot;&gt;
          &lt;div className=&quot;flex gap-4 items-end&quot;&gt;
            &lt;div className=&#39;flex flex-col&#39;&gt;
              &lt;label htmlFor=&quot;nickname&quot; className=&quot;text-sm&quot;&gt;
                닉네임
              &lt;/label&gt;
              &lt;div className=&quot;flex gap-4&quot;&gt;
                &lt;input
                  type=&quot;text&quot;
                  id=&quot;nickname&quot;
                  name=&quot;nickname&quot;
                  placeholder=&quot;2 ~ 10문자(특수문자 사용불가)&quot;
                  className={S.nick}
                  value={nickname}
                  onChange={(e) =&gt; handleNicknameChange(e.target.value)}
                /&gt;
                &lt;button
                  onClick={() =&gt; {
                    handleButtonClick();
                  }}
                  className={`h-10 text-gray-900 border rounded px-2 ${
                    nickname.length &gt;= 2 &amp;&amp;
                    nickname.length &lt;= 10 &amp;&amp;
                    /^[a-zA-Z0-9]+$/.test(nickname)
                      ? &#39;bg-primary&#39;
                      : &#39;bg-gray-300 cursor-not-allowed&#39;
                  }`}
                  disabled={
                    isLoading ||
                    !(
                      nickname.length &gt;= 2 &amp;&amp;
                      nickname.length &lt;= 10 &amp;&amp;
                      /^[a-zA-Z0-9]+$/.test(nickname)
                    )
                  }
                &gt;
                  중복확인
                &lt;/button&gt;
            &lt;/div&gt;
              {validationResult &amp;&amp; (
                &lt;div
                  className={
                    validationResult === &#39;중복된 닉네임입니다.&#39;
                      ? &#39;text-red-500 text-xs&#39;
                      : &#39;text-green-500 text-xs&#39;
                  }
                &gt;
                  {validationResult}
                &lt;/div&gt;
              )}
            &lt;/div&gt;
          &lt;/div&gt;
          &lt;ProfileUpload /&gt;
        &lt;/div&gt;
        &lt;button
          onClick={async () =&gt; {
            if (!isLoading &amp;&amp; validationResult === &#39;사용가능한 닉네임&#39;) {
              const isUsernameUpdated = await updateUsernameOnServer();
              if (isUsernameUpdated) {
                pocketbaseAuthData.username = nickname;
              } else {
                console.error(&#39;서버에서 username 업데이트 실패&#39;);
              }
            }
          }}
          className={S.save}
          disabled={isLoading || validationResult !== &#39;사용가능한 닉네임&#39;}
        &gt;
          저장하기
        &lt;/button&gt;
      &lt;/div&gt;
    );
  }
}

export default UserProfileEdit;</code></pre>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/8ae42851-3a73-4d2e-9ee6-adb52906cc48/image.gif" alt="">
변ㄴ경후</p>
<pre><code class="language-jsx">import pb from &#39;@/api/pocketbase&#39;;
import ProfileUpload from &#39;@/components/ProfileUpload/ProfileUpload&#39;;
import UserProfilePicture from &#39;@/components/UserProfilePicture/UserProfilePicture&#39;;
import useFetchData from &#39;@/hooks/useFetchData&#39;;
import PropTypes from &#39;prop-types&#39;;
import { useEffect, useState } from &#39;react&#39;;
import toast from &#39;react-hot-toast&#39;;
import { useNavigate, useParams } from &#39;react-router-dom&#39;;
import style from &#39;./UserProfileEdit.module.css&#39;;
import useAuthStore from &#39;@/store/auth&#39;;

const PB = import.meta.env.VITE_PB_URL;
const PB_USER_ENDPOINT = `${PB}/api/collections/users/records`;

function UserProfileEdit() {
  const navigate = useNavigate();
  const { userId } = useParams();
  const { data: userData, isLoading } = useFetchData(PB_USER_ENDPOINT);
  const [nickname, setNickname] = useState(&#39;&#39;);
  const [validationResult, setValidationResult] = useState(&#39;&#39;);
  const [usernameList, setUserNameList] = useState([]);
  const user = useAuthStore((store) =&gt; store.user);
  const checkLogIn = useAuthStore((store) =&gt; store.checkLogIn);

  const handleCheckDuplicate = () =&gt; {
    if (isLoading) {
      return;
    }

    if (
      nickname.length &gt;= 5 &amp;&amp;
      nickname.length &lt;= 10 &amp;&amp;
      /^[a-zA-Z0-9]+$/.test(nickname)
    ) {
      const isDuplicate = usernameList.includes(nickname.toLowerCase());

      if (isDuplicate) {
        setValidationResult(&#39;중복된 닉네임입니다.&#39;);
      } else {
        setValidationResult(&#39;사용가능한 닉네임입니다&#39;);
      }
    } else {
      setValidationResult(&#39;5~ 10글자의 영문 대소문자와 숫자만 입력하세요.&#39;);
    }
  };

  const handleNicknameChange = (value) =&gt; {
    if (
      value.length &gt;= 5 &amp;&amp;
      value.length &lt;= 10 &amp;&amp;
      /^[a-zA-Z0-9]+$/.test(value)
    ) {
      setNickname(value);
      setValidationResult(&#39;&#39;);
    } else {
      setNickname(value);
      setValidationResult(&#39;5 ~ 10글자의 영문 대소문자와 숫자만 입력하세요.&#39;);
    }
  };

  const handleButtonClick = () =&gt; {
    if (!isLoading) {
      handleCheckDuplicate();
    }
  };

  const updateUsernameOnServer = async () =&gt; {
    try {
      return await pb.collection(&#39;users&#39;).update(userId, {
        username: nickname,
      });
    } catch (error) {
      console.error(&#39;서버 요청 오류:&#39;, error);
      return false;
    }
  };

  const isNicknameValid =
    nickname.length &gt;= 5 &amp;&amp;
    nickname.length &lt;= 10 &amp;&amp;
    /^[a-zA-Z0-9]+$/.test(nickname);
  const isNicknameAvailable = validationResult === &#39;사용가능한 닉네임입니다&#39;;
  const isSaveButtonDisabled = isLoading || !isNicknameAvailable;

  async function handleSaveButtonClick() {
    try {
      if (!isLoading &amp;&amp; isNicknameAvailable) {
        const result = await updateUsernameOnServer();
        if (result) {
          toast.success(&#39;성공적으로 변경되었습니다&#39;);
          navigate(`/mypage/${userId}`);
        }
      }
    } catch (error) {
      toast.error(error.message);
    }
  }

  const username = user?.username || &#39;사용자 이름이 없습니다&#39;;

  useEffect(() =&gt; checkLogIn(), [checkLogIn]);

  useEffect(() =&gt; {
    if (userData.items) {
      const usernameList = userData.items.map((user) =&gt;
        user.username.toLowerCase()
      );
      setUserNameList(usernameList);
    }
  }, [userData.items]);

  if (!user) {
    return null;
  }

  return (
    &lt;div className={style.profile}&gt;
      &lt;UserProfilePicture avatar={user} /&gt;
      &lt;div className=&quot;h-[90px] flex&quot;&gt;
        &lt;span className=&quot;mt-5 text-xl font-semibold&quot;&gt;{username}&lt;/span&gt;
      &lt;/div&gt;
      &lt;div className={style.editwrapper} /&gt;
      &lt;div className=&quot;flex flex-col gap-6 mt-10&quot;&gt;
        &lt;NicknameInputSection
          nickname={nickname}
          handleNicknameChange={handleNicknameChange}
          handleButtonClick={handleButtonClick}
          validationResult={validationResult}
          isNicknameValid={isNicknameValid}
        /&gt;
        &lt;ProfileUpload /&gt;
      &lt;/div&gt;
      &lt;button
        onClick={handleSaveButtonClick}
        className={`${style.save} ${
          isSaveButtonDisabled ? &#39;bg-gray-300&#39; : &#39;bg-primary&#39;
        }`}
        disabled={isSaveButtonDisabled}
      &gt;
        저장하기
      &lt;/button&gt;
    &lt;/div&gt;
  );
}

function NicknameInputSection({
  nickname,
  handleNicknameChange,
  handleButtonClick,
  validationResult,
  isNicknameValid,
}) {
  return (
    &lt;div className=&quot;flex gap-4 items-end&quot;&gt;
      &lt;div className=&quot;flex flex-col&quot;&gt;
        &lt;label htmlFor=&quot;nickname&quot; className=&quot;text-sm&quot;&gt;
          닉네임
        &lt;/label&gt;
        &lt;div className=&quot;flex gap-4&quot;&gt;
          &lt;input
            type=&quot;text&quot;
            id=&quot;nickname&quot;
            name=&quot;nickname&quot;
            placeholder=&quot;5 ~ 10문자(특수문자 사용불가)&quot;
            className={style.nick}
            value={nickname}
            onChange={(e) =&gt; handleNicknameChange(e.target.value)}
          /&gt;
          &lt;button
            onClick={handleButtonClick}
            className={`h-10 text-gray-900 border rounded px-2 ${
              isNicknameValid ? &#39;bg-primary&#39; : &#39;bg-gray-300 cursor-not-allowed&#39;
            }`}
            disabled={!isNicknameValid}
          &gt;
            중복확인
          &lt;/button&gt;
        &lt;/div&gt;
        {validationResult &amp;&amp; (
          &lt;div
            className={
              validationResult === &#39;중복된 닉네임입니다.&#39;
                ? &#39;text-red-500 text-xs&#39;
                : &#39;text-green-500 text-xs&#39;
            }
          &gt;
            {validationResult}
          &lt;/div&gt;
        )}
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

NicknameInputSection.propTypes = {
  nickname: PropTypes.string.isRequired,
  handleNicknameChange: PropTypes.func.isRequired,
  handleButtonClick: PropTypes.func.isRequired,
  validationResult: PropTypes.string,
  isNicknameValid: PropTypes.bool.isRequired,
};

export default UserProfileEdit;
</code></pre>
<p>중복되는 코드를 정리하고 가독성을 높이기 위해 코드를 정리했다.
prop-types을 정의해서 팀원들이 컴포넌트를 사용할 떄 어떤 타입을 사용하면 되는지 명시해놧다.
닉네임이 수정되지 않는 버그를 수정하였다
입력양식이 충족되지 않을 때에 저장하기 버튼을 disable로 설정하고 사용자가 알아보기 쉽도록 색을 변경하였다.
변경 후 마이페이지로 이동하게 만들었다.</p>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/3d150fa0-374f-43b7-940e-048741ea1991/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[23.11.08 TIL]]></title>
            <link>https://velog.io/@pearlx_x/23.11.8-TIL</link>
            <guid>https://velog.io/@pearlx_x/23.11.8-TIL</guid>
            <pubDate>Wed, 08 Nov 2023 08:53:17 GMT</pubDate>
            <description><![CDATA[<h1 id="118">11/8</h1>
<h1 id="1교시">1교시</h1>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/6ed178af-9e1d-4f08-90de-02de4c9d5bde/3073cc13-76c0-4883-a960-e6aee3c5a407/Untitled.png" alt="Untitled"></p>
<p>훅은 최상위 레벨에서만 훅을 호출해야 한다</p>
<p>컴포넌트 내에서만 사용가능</p>
<p>훅은 컴포넌트가 렌더링될 때마다 매번 같은 순서로 렌더링되어야 한다</p>
<h2 id="usestate">useState</h2>
<p>초기값 설정시 무거운 계산이 동반되는 경우 콜백함수로 작성!</p>
<h2 id="useeffect">useEffect</h2>
<ol>
<li>렌더링될 때마다 실행</li>
<li>첫번째 렌더링 시에만 실행</li>
<li>첫 렌더링 + 특정값이 변할 때</li>
<li>클린업</li>
</ol>
<h1 id="2교시">2교시</h1>
<h2 id="usestate-콜백--usememo-용도의-차이점">useState 콜백 &amp; useMemo 용도의 차이점</h2>
<ul>
<li><p>설명</p>
<p>  <a href="https://www.zigae.com/useState-one-initialization/">https://www.zigae.com/useState-one-initialization/</a>
  해당 포스트가 잘 설명해주고있는 것 같습니다.
  제가 이해한 바로는</p>
<ol>
<li>state는 참조 동일성을 보장(setter에 의해서만 변경)</li>
<li>useMemo는 불필요한 재연산을 방지
에 초점을 맞춰 사용하는 게 바람직하다&#39; 인 것 같습니다.
<a href="https://careerly.co.kr/comments/87690">https://careerly.co.kr/comments/87690</a>
Hooks의 실행순서는 렌더링 과정에서 동일하지만 useMemo에서 &#39;not as a semantic guarantee&#39; 라고 적혀있는 반면
<a href="https://legacy.reactjs.org/docs/hooks-reference.html#lazy-initial-state">https://legacy.reactjs.org/docs/hooks-reference.html#lazy-initial-state</a>
State의 초기값에서는 초기 렌더링시점에 대한 언급이 존재합니다.
The initialState argument is the state used during the initial render
강사님이 보여주셨던 예제에서는(무거운 연산에 한해서) 동일하게 동작하는 것 같은데 리액트에서는 순서에 대해 언급한 의도가 책임 못지니 용도에 맞게 (참조 동일성/재연산 방지) 사용해라가 핵심인 것 같습니다!</li>
</ol>
</li>
</ul>
<p>🧞‍♂️ 순서가 상관 없으면 useMemo 순서가 중요하면 useState callback</p>
<h1 id="3교시">3교시</h1>
<h3 id="제네릭-타입을-사용하는-이유">제네릭 타입을 사용하는 이유</h3>
<p>사용자한테 권한을 주는 것</p>
<h2 id="useref">useRef</h2>
<p>ref는 컴포넌트 전 생애주기를 통해 유지가 된다.</p>
<p>= 리렌더링되어도 값을 유지</p>
<ol>
<li>특정 상태값이 변할 때, 리렌더링 시키면 안되는 상태값인 경우</li>
<li>컴포넌트 내 특정 돔 요소에 접근해야 하는 경우</li>
<li>부모 컴포넌트가 자식 컴포넌트의 돔에 접근해야 하는 경우 (forwardRef)</li>
</ol>
<ul>
<li><p>forwardRef</p>
<p>  <a href="https://www.daleseo.com/react-forward-ref/">https://www.daleseo.com/react-forward-ref/</a></p>
<p>  자식컴포넌트의 돔이 외부로 노출되는 것이기 때문에 가독성에 부정적인 영향을 줍니다. </p>
<p>  이상으로 React의 forwardRef() 함수를 사용하여 어떻게 React 컴포넌트에 ref prop을 넘길 수 있는지에 대해서 알아보았습니다.</p>
<p>  일반적으로 forwardRef() 함수는 HTML 엘리먼트 대신에 사용되는 최말단 컴포넌트(ex. <Input/>, <Button/>)를 대상으로 주로 사용되며, 그 보다 상위 컴포넌트에서는 forwardRef() 함수를 사용하는 것이 권장되지 않습니다. 왜냐하면 어떤 컴포넌트의 내부에 있는 HTML 엘리먼트의 레퍼런스를 외부에 있는 다른 컴포넌트에서 접근하도록 하는 것은 컴포넌트 간의 결합도(coupling)을 증가시켜 애플리케이션의 유지보수를 어렵게 만들기 때문입니다.</p>
</li>
</ul>
<h3 id="만보기-앱을-만들-때-useref를-사용해야-하는-곳과-그-이유">만보기 앱을 만들 때 useRef를 사용해야 하는 곳과 그 이유</h3>
<p>시간과 걸음수가 있을 때 걸음 수에 useRef를 사용하여 값을 유지해야 한다고 생각한다.</p>
<p>걸음수는 하루의 전체 걸음수를 표시해주어야 하기 때문에 리렌더링이 발생한다고 값이 사라지면 안 되고 계속 누적되어 나중에 결과를 표시해주어야 하기 때문이다 </p>
<h1 id="4교시">4교시</h1>
<h2 id="usecontext">useContext</h2>
<p>context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있다</p>
<h2 id="redux와-context-api의-차이"><strong>Redux와 Context API의 차이</strong></h2>
<ul>
<li><p>Redux가 Context API에 비해 가지는 강점</p>
<ul>
<li>리덕스는 로컬 스토리지에 상태를 영속적으로 저장하고 시작할 때 다시 불러오는데 뛰어남</li>
<li>상태를 서버에서 미리 채워서 HTML에 담아 클라이언트로 보내고 앱을 시작할 떄 다시 불러오는데 뛰어남</li>
<li>사용자의 액셕을 직렬화해서 상태와 함꼐 자동으로 버그리포트에 첨부할 수 있고 개발자들은 이를 통해 에러를 재현할 수 있음</li>
<li>액션 객체를 네트워크를 통해 보내면 코드를 크게 바꾸지 않고도 협업 환경을 구현할 수 있음</li>
<li>실행취소 내역의 관리나 낙관적인 변경 (optimistic mutations)을 코드를 크게 바꾸지 않고도 구현 가능</li>
<li>개발할 때 상태 내역 사이를 오가고 액션 내역에서 현재 상태를 다시 계산하는 일을  TDD스타일로 할 수 있음</li>
<li>개발자 도구에게 완전한 조사와 제어를 가능하게 해서 개발자들이 자신의 앱을 위한 도구를 직접 만들 수 있게 해줌</li>
<li>비즈니스 로직 대부분을 재사용하면서 UI를 변경할 수 있게 해줌</li>
</ul>
</li>
<li><p>차이점</p>
<h3 id="redux"><strong>Redux:</strong></h3>
<ol>
<li><p><strong>예측 가능한 상태 관리</strong>: Redux는 애플리케이션 전체의 전역 상태를 중앙 집중식으로 저장하고 관리합니다.</p>
</li>
<li><p><strong>단일 스토어</strong>: Redux는 단일 스토어를 사용하며, 앱의 상태를 여러 개의 리듀서로 분할하여 관리합니다.</p>
</li>
<li><p><strong>불변성 유지</strong>: Redux는 불변성을 유지하며, 상태를 변경할 때 항상 새로운 상태 객체를 반환합니다.</p>
</li>
<li><p><strong>Middleware</strong>: 미들웨어를 사용하여 액션과 리듀서 사이에 추가 기능을 삽입할 수 있습니다. 예를 들어, 비동기 작업을 처리할 수 있습니다.</p>
</li>
<li><p><strong>Redux DevTools</strong>: Redux는 강력한 개발 도구를 제공하여 애플리케이션 상태의 시간 여행 및 디버깅을 용이하게 합니다.</p>
<h3 id="context-api"><strong>Context API:</strong></h3>
</li>
<li><p><strong>로컬 상태 관리</strong>: React의 Context API는 주로 특정 컴포넌트 트리 내에서 데이터를 공유하기 위한 용도로 사용됩니다.</p>
</li>
<li><p><strong>하위 컴포넌트에 전달</strong>: Context를 사용하여 값을 전달하면 해당 값에 접근할 수 있는 컴포넌트는 해당 Context를 구독하고 있는 컴포넌트 뿐입니다.</p>
</li>
<li><p><strong>간편한 사용</strong>: 상대적으로 Redux보다 더 간단하게 사용됩니다. 주로 특정 컴포넌트 간의 데이터 전달이 목적입니다.</p>
</li>
<li><p><strong>적은 양의 데이터 전달</strong>: 일반적으로 Redux보다는 소규모 애플리케이션이나 단순한 데이터 전달에 사용됩니다.</p>
</li>
</ol>
</li>
<li><p>오직 전역 상태 관리를 위한다면 Context API를 사용하라.</p>
</li>
<li><p>상태 관리 외에 여러 기능이 필요하다면 Redux 를 사용하라.</p>
</li>
<li><p>high-frequency한 어플리케이션의 경우 Context API를 사용하면 성능상 이슈가 있을 수 있다.</p>
</li>
<li><p>Redux는 크고 복잡한 애플리케이션에서 상태를 효과적으로 관리하는 데 유용,</p>
</li>
<li><p>반면에 Context API는 상대적으로 작은 규모의 애플리케이션에서 로컬 상태 관리나 특정 컴포넌트 간의 데이터 전달에 활용</p>
</li>
</ul>
<h1 id="5교시">5교시</h1>
<h2 id="usememo--usecallback을-사용하는-것이-항상-성능을-향상시키는-것이-아니다-그-이유는힌트-참조-동일성">useMemo / useCallback을 사용하는 것이 항상 성능을 향상시키는 것이 아니다 그 이유는?(힌트: 참조 동일성)</h2>
<ul>
<li><p>설명</p>
<pre><code class="language-jsx">  const Foo = () =&gt; {
    const [count, setCount] = useState(0);

    // case 1
    const handleIncrement = useCallback(() =&gt; {
      setCount(count + 1);
    }, []);

    // case 2
    const handleIncrement = () =&gt; {
      setCount(count + 1);
    };

    return &lt;Box *onClick*={handleIncrement} /&gt;;
  };</code></pre>
<p>  case1과 case2 중에 비용이 적게 드는 코드는 어느쪽일까? 많은 아티클에서 <code>inline</code> 함수는 <code>useCallback</code> 으로 안에 넣는 것이 성능에 더 좋다 말하고 있기 때문에 case1을 선택할 수 있겠지만 실상은 그렇지 않다.</p>
<p>  모든 추상화 및 최적화 코드에는 비용이 들기 마련이다. 이때 발생하는 비용을 상쇄 시킬만한 비용절약이 있지 않으면 오히려 비용 증가가 일어난다. 최적화 관점에서 <code>useCallback</code>을 사용하기 위해선 전후 성능을 비교 후에 사용하는 것이 옳다.</p>
<h3 id="참조-동일성">참조 동일성</h3>
<p>  객체, 배열 같은 참조타입일 경우 렌더링될 때마다 객체가 새로 만들어지고 렌더링될 때마다 계속 호출되게 된다. </p>
<p>  이 때 useCallback이나 useMemo를 사용하여 캐싱하고 싶어도 아무의미없이 컴포넌트는 리렌더링될 때마다 실행되게 된다. 따라서 무조건 useCallback을 사용하거나 useMemo를 사용한다고 해서 무조건 비용이 절감되는 것이 아니고 코드도 더 복잡해질 수 있다</p>
<ul>
<li><code>useCallback</code>과 <code>useMemo</code>를 성능을 향상시킨다는 초점에 맞춰서 무분별하게 사용한다면??<ul>
<li>코드가 더 복잡하게 보일수도 있으며 팀원들이 이해하기에 복잡성 또한 증가할 수 있습니다.</li>
<li><code>dependencies</code> 배열에 들어가는 참조 <code>value</code>들이 누락되거나 잘못 넣게되면서 휴먼 에러가 자주 발생합니다.</li>
<li><code>dependencies</code> 배열 내부의 값들이 메모제이션되면서 가비지 컬랙터가 안되게 만들 수 있습니다.</li>
<li>성능 개선을 위해 추가된 비용때문에 오히려 효율성이 떨어져 성능이 더 하락할 수 있습니다.</li>
</ul>
</li>
<li>즉 적절한 상황에서 <code>useCallback</code>과 <code>useMemo</code>를 사용하여 성능 개선을 통해 얻어지는 이점이 추가된 비용을 상쇄할 수 있다면 의미있게 사용할 수 있지 않을까? 생각합니다!</li>
</ul>
</li>
</ul>
<h1 id="6교시">6교시</h1>
<h2 id="usereducer">useReducer</h2>
<p>useState와 비슷하지만 복잡한 상태(중첩된 객체 nested object)를 관리하는데 사용</p>
<p>또는 이전상태에 의존하는 상태를 관리하는데 사용</p>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/6ed178af-9e1d-4f08-90de-02de4c9d5bde/e70c2bc8-a3dd-4e5b-9b7d-877cbc00abc8/Untitled.png" alt="Untitled"></p>
<h2 id="usereducer를-사용할-때-action-dispatch-reducer에-대해-설명해주세요">useReducer를 사용할 때, action, dispatch, reducer에 대해 설명해주세요.</h2>
<ul>
<li><p>설명</p>
<p>  <strong><code>useReducer</code></strong>는 React에서 제공하는 훅 중 하나로, 상태 관리를 위해 사용됩니다. 기존의 <strong><code>useState</code></strong>와 달리, 복잡한 상태 논리를 처리하고 상태를 업데이트하기 위한 더 구조화된 방법을 제공</p>
<ul>
<li><strong>Reducer</strong>: Reducer는 현재 상태와 업데이트하고자 하는 액션을 받아 새로운 상태를 반환하는 함수입니다. Reducer는 <strong><code>(이전 상태, 액션) =&gt; 새로운 상태</code></strong>의 형태를 갖습니다. 여기서 &quot;액션&quot;은 상태를 어떻게 변경할지에 대한 정보를 가지고 있습니다.</li>
<li><strong>Dispatch</strong>: <strong><code>dispatch</code></strong> 함수는 Reducer에게 상태를 변경하라는 명령을 내리는 함수입니다. 액션 객체를 인자로 받아 Reducer에게 전달하고, Reducer가 이에 따라 상태를 업데이트합니다.</li>
<li><strong>Action</strong>: 액션은 상태를 변경하는 유형을 나타내는 객체입니다. 일반적으로 &quot;type&quot;이라는 필드를 포함하고 있으며, Reducer에서 어떤 종류의 업데이트를 수행할지 식별하는 데 사용됩니다. 추가적인 데이터를 전달해야 할 경우, &quot;payload&quot; 또는 기타 필드를 포함할 수 있습니다.</li>
</ul>
</li>
</ul>
<h2 id="usereducer에서-액션을-처리할-때-주의해야-할-점은-무엇인가요">useReducer에서 액션을 처리할 때 주의해야 할 점은 무엇인가요?</h2>
<p>(Hint 순수함수)</p>
<ul>
<li><p>이유</p>
<p>  <strong><code>useReducer</code></strong>에서 액션을 처리할 때 주의해야 할 점 중 하나는 <strong>순수 함수</strong>를 사용해야 한다는 것입니다.</p>
<p>  순수 함수란 다음과 같은 특징을 갖는 함수를 의미합니다:</p>
<ol>
<li><p><strong>부수 효과(side effects)가 없어야 합니다</strong>: 순수 함수는 함수 내부에서 외부의 상태를 변경하거나 외부에 영향을 미치는 작업을 수행해서는 안 됩니다. 외부 상태에 변경을 가하지 않아야 합니다.</p>
</li>
<li><p><strong>동일한 입력에 대해서는 항상 동일한 출력을 반환해야 합니다</strong>: 함수는 동일한 인자(입력)에 대해 항상 같은 결과(출력)를 반환해야 합니다. 외부 상태의 변화나 무작위 요소 등이 없어야 합니다.</p>
<p>액션을 처리하는 함수인 리듀서는 순수 함수여야 합니다. 외부의 상태를 변경하면 안 되며, 같은 액션에 대해 항상 같은 결과를 반환해야 합니다. 왜냐하면 <strong><code>useReducer</code></strong>는 순수 함수인 리듀서를 통해 이전 상태와 액션을 바탕으로 새로운 상태를 반환하는데, 이 규칙을 어겨버리면 예상치 못한 문제가 발생할 수 있습니다.</p>
<p>순수 함수를 유지하여 예측 가능하고 안정적인 상태 업데이트를 보장하면서, 코드의 테스트 용이성도 높아집니다. 따라서 리듀서에서는 순수 함수의 원칙을 준수하여 액션을 처리해야 합니다.</p>
</li>
</ol>
</li>
</ul>
<h2 id="초기-상태가-복잡한-계산이-필요할-때-usereducer를-사용할-때-초기-상태를-지연-초기화하는-방법은-무엇인가요">초기 상태가 복잡한 계산이 필요할 때, useReducer를 사용할 때 초기 상태를 지연 초기화하는 방법은 무엇인가요?</h2>
<ul>
<li><p>방법</p>
<p>  <strong><code>useReducer</code></strong>를 사용할 때 초기 상태가 복잡한 계산이 필요하거나 지연 초기화해야 할 때, 보통 초기 상태를 계산하는 함수를 사용합니다.</p>
<p>  여기서 <strong><code>useReducer</code></strong>의 초기값으로 함수를 전달할 수 있습니다. 이 함수는 실제 초기 상태를 계산하고 반환하는 역할을 합니다. 이를 통해 초기 상태가 복잡한 계산이 필요한 경우 해당 함수를 호출하여 초기 상태를 반환하도록 할 수 있습니다</p>
</li>
</ul>
<h1 id="7교시">7교시</h1>
<h2 id="customhook">customHook</h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[23.11.07 TIL]]></title>
            <link>https://velog.io/@pearlx_x/23.11.07-TIL</link>
            <guid>https://velog.io/@pearlx_x/23.11.07-TIL</guid>
            <pubDate>Wed, 08 Nov 2023 00:46:33 GMT</pubDate>
            <description><![CDATA[<p>엔트리레벨 - 특정 기능의 코드를 작성하는 법
주니어레벨 - 코드 작성 , 테스트, 개발 지원</p>
<hr>
<p>2008 구글 크롬 브라우저  - v8 자바스크립트 엔진 등장</p>
<p>2009 node.js 등장</p>
<p>2012 제이쿼리가 유행한 배경 </p>
<p>SPA 등장 배경 - 좋아요를 구현하고 싶어서 / SPA가 가능하게  된 필요 조건</p>
<h2 id="리액트의-대표적인-특징">리액트의 대표적인 특징</h2>
<h3 id="가상-돔">가상 돔</h3>
<p>리액트는 브라우저가 가진 돔을 직접 조작하지 않음</p>
<p>복사본 업데이트 전후의 가상돔 2개를 비교해서 차이를 알아내서</p>
<p>한번에 업데이트</p>
<p>CSR / SSR </p>
<p>SSR 사이트를 빠르게 표시, SEO </p>
<p>SSR를 보완하는 SSG 사전에정적 파일로 생성/ 배포하는 구조</p>
<h3 id="선언적-ui">선언적 UI</h3>
<h3 id="단방향-데이터-전달props">단방향 데이터 전달(props)</h3>
<p>컴포넌트 지향 / 함수 컴포넌트</p>
<p>플럭스 아키텍처와의 연관성</p>
<hr>
<p>리액트는 무엇인가요?</p>
<p>리엑트의 컴포넌트와 그 구성요소는 무엇인가요?</p>
<p>props, state, 렌더링, JSX를 설명해주세요.</p>
<p>가상돔은 무엇이고 리액트에서 어떻게 작용하나요?</p>
<h3 id="리액트-컴포넌트의-생애주기를-설명해주세요-그리고-개발자가-왜-알아야하나요-알면-무엇을-알-수-있나요">리액트 컴포넌트의 생애주기를 설명해주세요. 그리고 개발자가 왜 알아야하나요? 알면 무엇을 알 수 있나요?</h3>
<p>리액트 컴포넌트의 생명 주기는 컴포넌트가 생성되고 업데이트를 거쳐 소멸되는 과정을 설명한다. </p>
<p>생성 단계에선 마운팅된다고 하는데 construtor가 컴포넌트의 생성자로 초기화 작업을 수행한다. 그 다음 render가 UI를 렌더링하고 componentDidMount는 컴포넌트가 마운트된 직후에 호출되며, 초기 데이터를 가져오거나 외부 리소스 요청등의 작업을 수행한다.</p>
<p>업데이트 단계에선 성능 최적화를 위해 사용되는 shouldComponentUpdate가 컴포넌트를 업데이트 할지 결정을 한다</p>
<p>업데이트된 컴포넌트가 있으면 render가 실행되어 UI를 업데이트하고 componentUpdate에선 컴포넌트의 업데이트가 완료된 직후에 호출되며 업데이트 이후의 작업을 수행한다.</p>
<p>마지막으로 소멸단계에선 언마운팅된다고 하는데 componentWillUnmount에서 컴포넌트가 제거되기 전에 호출되며, 정리 작업을 수행한다.</p>
<p>리소스 해제나 이벤트 핸들러 제거를 수행한다.</p>
<p>개발자는 컴포넌트 동작에 대해 이해하고 성능 최적화와 자원관리를 필요로 하기 때문에 필요로 하는 지식들이다.</p>
<p>리액트 훅은 무엇이고 왜 쓰나요?</p>
<p>☆ </p>
<p>함수형 컴포넌트에서 클래스형 컴포넌트의 라이프 사이클 메소드를 비슷하게 사용하는 방법에 대해 설명해주세요.</p>
<hr>
<p>문서 탐색 단계</p>
<ol>
<li>배경파악 문제인식</li>
<li>존재이유</li>
<li>해결방법→이렇게 해결한다고? 이 해결방식에 내가 동의하나?</li>
<li>튜토리얼  / useCase</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[23.10.25 TIL]]></title>
            <link>https://velog.io/@pearlx_x/23.10.25-TIL</link>
            <guid>https://velog.io/@pearlx_x/23.10.25-TIL</guid>
            <pubDate>Wed, 08 Nov 2023 00:45:22 GMT</pubDate>
            <description><![CDATA[<h1 id="일급객체">일급객체</h1>
<p>일급 객체(First-class object)
• 변수, 배열 엘리먼트, 다른 객체의 프로퍼티에 할당될 수 있다.
• 함수의 인자로 전달될 수 있다.
• 함수의 결과 값으로 반환될 수 있다.
• 리터럴로 생성될 수 있다.
• 동적으로 생성된 프로퍼티를 가질 수 있다.</p>
<p>자바스크립트의 함수(Function)는 일급 객체이다.
• 함수= (호출+ 객체)</p>
<p> 함수가 일급 객체라서 가능한 일
• 콜백 함수(Callback function)
• 고차 함수(Higher order function)
• 클로저(Closure)</p>
<hr>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/21411e25-56b8-488a-9a83-abcb1a9a56e2/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/030aa9a7-899a-4f96-8a58-079007a96bea/image.png" alt=""></p>
<p>TS
<img src="https://velog.velcdn.com/images/pearlx_x/post/3892c802-306a-4530-93a2-ce5034e98872/image.png" alt=""></p>
<p>interface -  중복 선언 가능
중복된 속성 값에 두 타입이 다르면 상속 불가!     ex 01-16</p>
<p>type - 중복선언 불가능 error</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[23.08.26 TIL]]></title>
            <link>https://velog.io/@pearlx_x/23.08.26-TIL</link>
            <guid>https://velog.io/@pearlx_x/23.08.26-TIL</guid>
            <pubDate>Wed, 08 Nov 2023 00:45:13 GMT</pubDate>
            <description><![CDATA[<h1 id="깃허브-페이지스레-프로젝트-배포하기">깃허브 페이지스레 프로젝트 배포하기!</h1>
<p><a href="https://www.npmjs.com/package/gh-pages">https://www.npmjs.com/package/gh-pages</a></p>
<pre><code class="language-bash">npm install gh-pages --save-dev</code></pre>
<pre><code>npm run build</code></pre><pre><code class="language-bash">npx gh-pages -d dist</code></pre>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/4b9ca934-da3c-48d6-876e-88dc9259b700/image.png" alt="">여기 주소를
<img src="https://velog.velcdn.com/images/pearlx_x/post/b034d1b4-3560-4ad2-9125-e5408ba0b74a/image.png" alt="">비트에 알려준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[23.08.19 - 23.08.24 TIL]]></title>
            <link>https://velog.io/@pearlx_x/23.08.19-23.08.24-TIL</link>
            <guid>https://velog.io/@pearlx_x/23.08.19-23.08.24-TIL</guid>
            <pubDate>Wed, 08 Nov 2023 00:45:04 GMT</pubDate>
            <description><![CDATA[<h1 id="230819">23.08.19</h1>
<h3 id="useeffect를-사용하는-이유">useEffect를 사용하는 이유</h3>
<p>useEffect를 사용하는 이유는 데이터를 가져오기 위해서가 아니라 리액트는 프라미스와 에러에 대응할 수 없기 때문이다.</p>
<hr>
<h3 id="useeffect-안에-직접-async-함수를-사용하는-것은-권장되지-않는-이유">useEffect 안에 직접 async 함수를 사용하는 것은 권장되지 않는 이유</h3>
<p>비동기 함수와의 일관성: <code>useEffect</code> 는 일반적으로 부수 효과를 처리하기 위해 사용됩니다. 하지만 <code>useEffect</code>가 반환하는 값은 주로 정리(clean-up) 작업을 위한 함수이어야 합니다. <code>async</code> 함수는 항상 <code>Promise</code> 를 반환하기 때문에, 반환 값이 기대하는 일반적인 정리 함수 형태와는 다릅니다.</p>
<p>오류 처리의 어려움: <code>useEffect</code> 안에서 직접 <code>async</code> 함수를 사용하면 내부에서 발생한 오류를 캐치하거나 처리하기가 어려울 수 있습니다. <code>try-catch</code> 블록 안에서 await를 사용하여 오류를 처리할 수 없습니다. 이로 인해 예기치 않은 동작이 발생할 수 있습니다.</p>
<p>올바른 패턴은 <code>useEffect</code> 안에서 비동기 코드를 호출하는데 사용되는 별도의 함수를 선언하고, 그 함수 내에서 <code>async/await</code> 패턴을 사용하는 것입니다. 예를 들면 다음과 같습니다</p>
<pre><code class="language-jsx">useEffect(() =&gt; {
  const fetchData = async () =&gt; {
    try {
      // 비동기 작업 수행
    } catch (error) {
      // 오류 처리
    }
  };

  fetchData();
}, []);
</code></pre>
<pre><code class="language-jsx">import { useEffect, useState } from &quot;react&quot;;
// 리액트는 자식 컴포넌트가 프로미스를 반환하거나
// 에러를 던질 때는 전혀 대응하지 못한다.
function App() {
  const [값, set값] = useState(0);
  const 비동기데이터가져오기 = async () =&gt; {
    const res = await fetch(
      &quot;https://pokeapi.co/api/v2/pokemon?limit=5&amp;offset=0&quot;
    );
    const data = await res.json();
    set값(data.results.length);
  };

  // fetch를 react 컴포넌트와 격리하기 위해
  // useEffect의 부수효과를 이용한다.
  useEffect(() =&gt; {
    // useEffect 안에서 async 함수를 선언하면
    // 프로미스를 반환하므로 에러가 발생한다
    비동기데이터가져오기();
  }, []);

  return &lt;&gt;{값}&lt;/&gt;;
}

export default App;</code></pre>
<hr>
<pre><code class="language-jsx"> function 메모이즈(함수) {
        // 0. 캐시라는 변수는 클로저가 된다
        const 캐시 = {};

        return function (키) {
          console.log(&quot;캐시 내부&quot;, 캐시);
          // 1.캐시 안에서 이전에 계산한 값을 꺼내 온다
          const 값 = 캐시[키];

          // 2. 이전에 계산한 값이 있으면 그 값을 반환한다
          if (값) {
            console.log(&quot;히트다 히트!&quot;);
            return 값;
          }

          // 3. 이전에 계산한 값이 없으면 새로 계산해서 캐시에 넣자
          캐시[키] = 함수(키);

          // 4. 그리고 계산한 값을 반환!
          return 캐시[키];
        };
      }</code></pre>
<hr>
<h1 id="230822">23.08.22</h1>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/078641ce-f1a7-48b5-95bd-134e38b11061/image.png" alt=""></p>
<h2 id="환경-변수-설정--동적-라우팅">환경 변수 설정 &amp; 동적 라우팅</h2>
<h4 id="envlocal">.env.local</h4>
<pre><code>VITE_PB_URL = http://127.0.0.1:8090 # 환경변수 설정
VITE_PB_API = $VITE_PB_URL/api #API 엔드포인트를 저장
DB_PASSWORD = qweiuickx2@zkjw # 비트와의 약속 : 변수명에 &#39;VITE&#39;를 명시하지 않으면 값을 불러 올 수 없음!</code></pre><p><img src="https://velog.velcdn.com/images/pearlx_x/post/07783c0f-de36-430d-9234-aca3a8b30719/image.png" alt=""> <code>.env.local</code>에 있는 변수 불러오는 방법</p>
<h4 id="srcroutes">src/routes</h4>
<pre><code class="language-jsx">&lt;Route path=&quot;product/edit/:productId&quot; element={&lt;ProductEdit /&gt;}/&gt;
{/* :productId - url 파라미터에 따라 동적으로 라우팅 해주겠다는 뜻  */}
// productId는 동적 경로 매개변수                                 
// ProductEdit 컴포넌트에서 이 매개변수를 추출하려면 useParams 훅을 사용 </code></pre>
<pre><code class="language-jsx">${import.meta.env.VITE_PB_API}/collections/products/records/${productId}
// http://127.0.0.1:8090/api/collections/products/records/상품ID</code></pre>
<h2 id="value-vs-defaultvalue">value vs defaultValue</h2>
<p>defaultvalue - 폼 요소의 초기값을 설정하는 데 사용 / 리액트가 관리하지 않고 사용자에 의해서 값을 바꾸고 싶을 때 사용</p>
<pre><code> &lt;div className=&quot;flex gap-3&quot;&gt;
      &lt;label htmlFor={titleId}&gt;타이틀&lt;/label&gt;
      &lt;input
        type=&quot;text&quot;
        name=&quot;title&quot;
        id={titleId}
        // 리액트가 제공하지 않는 함수를 사용할 때는  defaultValue 사용해야 한다.
        defaultValue={formState.title} 
        onChange={handleDebounceChangeInput} // debounce 함수
      /&gt;
&lt;/div&gt;</code></pre><p>value - 폼요소의 현재 값을 나타냄 / 리액트가 관리할 때 사용</p>
<hr>
<h2 id="pocketbase-sdk">pocketbase SDK</h2>
<p><a href="https://www.notion.so/euid/SDK-b544aadfd64144f996c30ec78826df20">PocketBase SDK</a>
NPM을 사용해 PocketBase SDK 패키지를 설치합니다.</p>
<pre><code class="language-bash">npm i pocketbase</code></pre>
<h4 id="srcapipocketbasejs">src/api/pocketbase.js</h4>
<pre><code class="language-js">import PocketBase from &#39;pocketbase&#39;;

const pb = new PocketBase(import.meta.env.VITE_PB_URL);

// PocketBase SDK {}
export default pb;</code></pre>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/0ba61b94-5a7b-4ec5-bb63-7ce409b69d93/image.png" alt=""> <a href="https://pocketbase.io/docs/api-records/#view-record">api 문서</a></p>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/47bc6aa1-4d14-4265-9036-a38c83c97ab8/image.png" alt=""></p>
<h2 id="useparams">useParams</h2>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/a523e565-7ff1-4ebb-a64f-893d20e3edd3/image.png" alt="">
<img src="https://velog.velcdn.com/images/pearlx_x/post/2215fc16-bc2b-49ca-9935-c6fb900f8ad0/image.png" alt=""></p>
<pre><code>&lt;Route&gt; 설정:
이 부분은 처음에 설정되며, 브라우저의 주소창에 특정 URL 경로가 입력되었을 때 
일치하는 라우트가 있으면 해당 라우트의 컴포넌트를 렌더링합니다. 
path 속성에 있는 경로 패턴을 가지고 매칭 여부를 결정합니다.

컴포넌트 내에서 const { productId } = useParams(); 실행:
이 부분은 ProductEdit 컴포넌트 내부에서 실행됩니다. 
해당 컴포넌트가 라우트에 의해 렌더링될 때, 컴포넌트 내부의 코드가 실행됩니다. 
따라서 useParams()를 호출하여 동적 매개변수 값을 추출하는 것은 ProductEdit 컴포넌트가 렌더링될 때 일어납니다.

따라서 순서는 &lt;Route&gt; 설정이 먼저 이루어진 다음, ProductEdit 컴포넌트가 렌더링되면서 내부에서 useParams()를 호출하여 동적 매개변수 값을 가져오게 됩니다.</code></pre><ol>
<li>사용자가 링크를 클릭하여 /product/edit/123과 같은 URL로 이동합니다.</li>
<li>Route 컴포넌트가 /product/edit/:productId 경로와 일치하는지 확인합니다.</li>
<li>경로와 일치하므로 ProductEdit 컴포넌트가 렌더링됩니다.</li>
<li>ProductEdit 컴포넌트 내에서 const { productId } = useParams(); 코드가 실행됩니다. 이때 productId는 123과 같은 값이 됩니다.</li>
<li>만약 item.id가 456인 링크를 클릭한 경우, to 속성이 /product/edit/456가 되며, ProductEdit 컴포넌트에서 productId는 456과 같은 값이 됩니다.</li>
</ol>
<p>이렇게 함으로써 동적으로 변화하는 경로와 해당 경로의 매개변수 값을 활용하여 원하는 정보를 렌더링하거나 네비게이션을 처리할 수 있습니다.</p>
<h1 id="230823">23.08.23</h1>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/76a1cc5a-6bd0-4d8c-9ecd-c32a1053684c/image.png" alt="">
<img src="https://velog.velcdn.com/images/pearlx_x/post/367f557f-3d01-44c4-a720-45568190b74c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/6355ee39-c2df-48b7-a8b9-853f935036c1/image.png" alt="">
lifting
<img src="https://velog.velcdn.com/images/pearlx_x/post/f6ab5212-6f33-4081-9bfa-0d5279399396/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/5837ca35-4789-4707-98d0-4fd9356d89ee/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/2a7c4ff5-6df9-4997-898d-8f1a9ecfbf58/image.png" alt="">
상태와 달리, 참조 객체의 현재 값은 변경되어도 리액트가 다시 렌더링을 하지 않는다.
상태는 시간의 흐름에 따라 변하고, 상태가 변경되면 리액트는 필연적으로 반응(리액션: 렌더링)한다.
함수 내부의 변수나 포함된 함수는 다음 번 실행(리-렌더링) 시, 초기화된다. (가비지 컬렉터에 의해서)
<img src="https://velog.velcdn.com/images/pearlx_x/post/de850b36-258b-4bb9-9dfa-f296f2c2d171/image.png" alt="">
<img src="https://velog.velcdn.com/images/pearlx_x/post/01969851-cb68-44ec-9756-45956bb4d168/image.png" alt="">
컨텍ㄷ스트 == 영역
<img src="https://velog.velcdn.com/images/pearlx_x/post/7a1bd9c3-ce6f-4a6f-a799-676dde24caaf/image.png" alt=""></p>
<h2 id="usecontext">useContext</h2>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/f9998fdb-09dc-4034-bb4e-f3a839ccb348/image.png" alt="">
전
<img src="https://velog.velcdn.com/images/pearlx_x/post/84116d32-073a-4925-97fb-e61bcb4cba69/image.png" alt="">
후
<img src="https://velog.velcdn.com/images/pearlx_x/post/16375040-353c-496f-a704-1a6070d9a23d/image.png" alt=""></p>
<h2 id="usereducer">useReducer</h2>
<h1 id="230824">23.08.24</h1>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/6002d4ed-e0b3-40b8-b47f-c656839d91d4/image.png" alt=""></p>
<h1 id="230824-1">23.08.24</h1>
<p>Serialize(직렬화): 객체를 데이터스트림(연속적인 바이너리 형태나 텍스트 형태(예: JSON) )으로 만드는 것
즉, 객체에 저장된 데이터를 스트림에 쓰기위해 연속적인 데이터를 변환하는것. (JSON.stringify())
Deserialization(역직렬화): 반대로 스트림으로부터 데이터를 읽어 객체를 만드는 것 (JSON.parse())</p>
<h2 id="usememo--usecallback">useMemo / useCallback</h2>
<p>useCallback 훅은 리액트 컴포넌트 내에서 함수를 최적화하여 생성하거나 업데이트할 때 사용됩니다. 함수 컴포넌트가 렌더링될 때마다 새로운 함수가 생성되는 경우, 불필요한 리렌더링이 발생할 수 있고, 성능 저하의 원인이 될 수 있습니다. 이러한 문제를 해결하고자 useCallback 훅을 사용하여 함수를 메모이제이션하고 최적화할 수 있습니다.</p>
<pre><code class="language-jsx">  // useCallback : 함수 값만 기억 vs. useMemo : JS 모든 값(함수 포함)을 기억
  // useMemo 훅: 모든 JS 값 유형 기억
  // const cachedUpdate = useMemo(() =&gt; 함수 값, [])

  // useCallback 훅: JS 함수 값만 기억

  const update = useCallback(
    (nextData) =&gt; {
      setData(key, nextData);
      setStorageData(nextData);
    },
    [key]
  );

  // const updateMemo = useMemo(
  //   () =&gt; (nextData) =&gt; {
  //     setData(key, nextData);
  //     setStorageData(nextData);
  //   },
  //   [key]
  // );

  const remove = useCallback(() =&gt; {
    deleteData(key);
    setStorageData();
  }, [key]);

  // const removeMemo = useMemo(
  //   () =&gt; () =&gt; { deleteData(key); },
  //   [key]
  // );
</code></pre>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/76841fa4-c1dc-425b-bd08-c7b39cb0c7b3/image.png" alt=""></p>
<h3 id="route가-보호되는-상항에서-발생되는-에러-처리">Route가 보호되는 상항에서 발생되는 에러 처리</h3>
<p>토스트에서 문제 상황 발생
순수한 영역에서 상태를 바꾸려고 시도했기 때문에 에러가 발생한다
useEffect를 사용</p>
<pre><code>const { isAuth } = useAuth();

  useEffect(() =&gt; {
    // if (!isAuth) {
      toast(&quot;로그인 된 사용자만 이용 가능한 페이지입니다.&quot;, {
        icon: &quot;⚠️&quot;,
        ariaProps: {
          role: &quot;alert&quot;,
          &quot;aria-live&quot;: &quot;polite&quot;,
        },
      });
    // }

    return () =&gt; {};
  }, [isAuth]);

  if (!isAuth) {
    return &lt;Navigate to=&quot;/signin&quot; /&gt;;
  }

  return children;
}</code></pre><h3 id="spa에서-검색-엔진-최적화를-도와주는-react-helmet-async">spa에서 검색 엔진 최적화를 도와주는 react-helmet-async</h3>
<p><a href="https://www.npmjs.com/package/react-helmet-async">https://www.npmjs.com/package/react-helmet-async</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체지향프로그래밍 / 선언형 프로그래밍]]></title>
            <link>https://velog.io/@pearlx_x/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%84%A0%EC%96%B8%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@pearlx_x/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%84%A0%EC%96%B8%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Mon, 07 Aug 2023 12:39:57 GMT</pubDate>
            <description><![CDATA[<h1 id="의문점">의문점</h1>
<p>명령형 프로그래밍에서 객체지향 프로그래밍 방법론은 유지보수와 코드 재사용성이 용이하다고 배운적이 있습니다! 그래서 수업 중 명령형 프로그래밍의 단점에서 코드의 재사용성과 유지보수성이 떨어진다는 부분이 잘 이해가 되지 않습니다.</p>
<p>선언형 프로그래밍이 객체지향 프로그래밍보다 유지보수성과 재사용성이 더 좋다고 보면 될까요?</p>
<hr>
<h1 id="답변">답변</h1>
<p>오늘 수업에서 다룬 것은 명령형 프로그래밍 코드 구문과 선언형 프로그래밍 코드 구문 비교입니다. 질문에서 처럼 객체 지향 프로그래밍 코드와 비교하지는 않았습니다. 객체 지향 프로그래밍 코드는 함수를 사용해 작성하는 프로그래밍과 비교하는 중이었죠. 내일 수업에서 이어집니다.</p>
<p>그럼 아래 작성된 명령형 vs. 선언형 코드 구문을 먼저 살펴본 후, 아래 작성된 글을 참고하세요. 😃</p>
<ul>
<li><p><strong>명령형 프로그래밍 코드</strong></p>
<p>  함수 정의 및 호출 과정 없이 작성된 명령형 코드 (원본 변형함)</p>
<pre><code class="language-jsx">  const courses = [
    { 
      id: 1,
          name: &#39; imperative programming&#39;
      },
    { 
      id: 2,
          name: &#39;declarative programming &#39;
      },
  ];

  // 과정 배열을 순환하여 각 과정 이름의 좌우 공백 제거
  for (let i=0, l=courses.length; i &lt; l; ++i) {
    courses[i].name = courses[i].name.trim();
  }

  // 과정 배열을 순환하여 각 과정 이름 대문자화
  for (let i=0, l=courses.length; i &lt; l; ++i) {
    courses[i].name = courses[i].name.toUpperCase();
  }

  // 과정 배열을 순환하여 각 과정 이름의 공백 밑줄 변경
  for (let i=0, l=courses.length; i &lt; l; ++i) {
    courses[i].name = courses[i].name.replace(/\s+/g, &#39;_&#39;);
  }</code></pre>
</li>
<li><p><strong>선언형 프로그래밍 코드</strong></p>
<p>  각 기능 별 함수 선언 후, 배열의 map 메서드를 사용해 함수 조립(구성)한 선언형 코드 (원본 변형 안함)</p>
<pre><code class="language-jsx">  const subjects = [
    { 
      id: 1,
          name: &#39; imperative programming&#39;
      },
    { 
      id: 2,
          name: &#39;declarative programming &#39;
      },
  ];

  // 객체 이름(name) 속성 좌우 공백 제거 기능
  function toTrim(object) {
    const o = { ...object };
    o.name = o.name.trim();
    return o;
  }

  // 객체 이름(name) 속성 대문자화 기능
  function toUpperCase(object) {
    const o = { ...object };
    o.name = o.name.toUpperCase();
    return o;
  }

  // 객체 이름(name) 속성 밑줄 변경 기능
  function toUnderscore(object) {
    const o = { ...object };
    o.name = o.name.replace(/\s+/g, &#39;_&#39;);
    return o;
  }

  // 함수 조합
  **subjects
      .map(toTrim)
      .map(toUpperCase)
      .map(toUnderscore);**</code></pre>
</li>
</ul>
<p>자! 이제 유지보수 관점에서 생각해봅시다. 다음의 수정사항이 주어졌습니다.</p>
<ol>
<li>먼저 이름을 대문자로 변경합니다. </li>
<li>그리고 이름에 포함된 공백을 모두 밑줄로 변경합니다.</li>
<li>좌우 공백이 제거되도록 코드를 수정합니다.</li>
</ol>
<ul>
<li><p><strong>명령형 프로그래밍 코드</strong></p>
<p>  명령형 구문을 통째로 잘라내서 붙여넣든 하는 방식으로 코드를 옮겨야 유지보수가 되요.</p>
<pre><code class="language-jsx">  // 원래 순서: 2
  for (let i=0, l=courses.length; i &lt; l; ++i) {
    courses[i].name = courses[i].name.toUpperCase();
  }

  // 원래 순서: 3
  for (let i=0, l=courses.length; i &lt; l; ++i) {
    courses[i].name = courses[i].name.replace(/\s+/g, &#39;_&#39;);
  }

  // 원래 순서: 1
  for (let i=0, l=courses.length; i &lt; l; ++i) {
    courses[i].name = courses[i].name.trim();
  }</code></pre>
</li>
</ul>
<p>같은 내용의 유지보수를 선언형 프로그래밍에서 진행해봅니다.</p>
<ol>
<li>먼저 이름을 대문자로 변경합니다. </li>
<li>그리고 이름에 포함된 공백을 모두 밑줄로 변경합니다.</li>
<li>좌우 공백이 제거되도록 코드를 수정합니다.</li>
</ol>
<ul>
<li><p><strong>선언형 프로그래밍 코드</strong></p>
<p>  선언된 함수 조립 방식으로 설계된 코드이므로 수정 사항을 코드 라인의 순서만 바꾸면 됩니다.</p>
<pre><code class="language-jsx">  // 함수 조립(구성) 순서만 변경하면 됨
  subjects
      .map(toUpperCase) // 원래 순서: 2
      .map(toUnderscore) // 원래 순서: 3
      .map(toTrim); // 원래 순서: 1</code></pre>
<p>  위 코드는 추후에 함수형 프로그래밍 방식에 따라 아래처럼 사용됩니다.</p>
<pre><code class="language-jsx">  // 함수 조립(구성)
  pipe(
      toUpperCase,
      toUnderscore,
      toTrim
  )(subjects);</code></pre>
<p>  <code>subjects</code> 말고도 다양한 객체에 코드를 재사용하기 용이합니다.</p>
<pre><code class="language-jsx">  pipe(
      toUpperCase,
      toUnderscore,
      toTrim
  )(**courses**);</code></pre>
<p>  선언된 데이터에 기능을 추가하거나, 삭제하는 것도 쉽습니다.</p>
<pre><code class="language-jsx">  pipe(
      **filterStocked(10), // 재고가 10개 이상인 제품만 걸러냄
      filterMoreExpensive(50_000), // 5만원 이상의 제품만 걸러냄
      filterMadeInKorea() // 국산 제품만 걸러냄**
  )(**products**);</code></pre>
</li>
</ul>
<p>명령형도 함수로 분리할 수 있지 않나 의문이 들 수도 있어요. 그럼 아래와 같이 작성되겠죠. 역시 문으로 구성되어 있다 보니 유지보수가 용이하지는 않아 보여요.</p>
<pre><code class="language-jsx">for (let i=0, l=courses.length; i &lt; l; ++i) {
  **toUpperCase(courses[i]);**
}

for (let i=0, l=courses.length; i &lt; l; ++i) {
  **toUnderscore(courses[i]);**
}

for (let i=0, l=courses.length; i &lt; l; ++i) {
  **toTrim(courses[i]);**
}</code></pre>
<p>그럼? 반복문을 3번 돌리지 않고 하나로 묶는 생각도 할 수 있을 거에요. 아래 처럼 말이죠. 뭐 이러면 유지 보수가 나아보일 수 있지만, 재사용은 어렵죠.</p>
<pre><code class="language-jsx">**for (let i=0, l=courses.length; i &lt; l; ++i) {**
  const course = courses[i];
  toUpperCase(course);
    toUnderscore(course);
    toTrim(course);
**}**</code></pre>
<p>그럼 더 나아가 재사용을 목적으로 함수로 만들 생각을 할 수도 있을 거에요.</p>
<pre><code class="language-jsx">**function toUpperCaseAndUpserscoreAndTrim(courses) {**
  for (let i=0, l=courses.length; i &lt; l; ++i) {
      ****toUpperCase(courses[i]);
        toUnderscore(courses[i]);
        toTrim(courses[i]);
    }
**}**</code></pre>
<p>그런데 함수로 선언해서 재사용은 가능하지만, 순서가 항상 정해져 있잖아요. 수정이 또 발생하면 함수는 재사용이 어려워져요. 1회성으로 끝나고 버려지게 되죠. 결국은 다시 아래 코드로 돌아가야 합니다.</p>
<pre><code class="language-jsx">for (let i=0, l=courses.length; i &lt; l; ++i) {
  const course = courses[i];
  toUpperCase(courses[i]);
    toUnderscore(courses[i]);
    toTrim(courses[i]);
}</code></pre>
<h3 id="정리">정리</h3>
<p>어쩌면 작성된 글만으로는 여전히 의문이 남을지 몰라요. 😅</p>
<p>수업에서도 말했던 바를 다시 한 번 말씀드리지만, <strong>절대 “함수형이 명령형보다 낫다” 이런 말이 아니에요. 프로그래밍 하는 방법이 다를 뿐. 패러다임은 다양합니다.</strong> </p>
<p>지하철을 타거나, 버스를 타거나, 자전거를 타는 것 중 “어떤게 낫다 아니다” 그런 문제가 아니라 상황에 따라 최선의 방법은 달라질 수 있어요. 어떤 경우는 지하철이 다른 경우는 버스가 나을 수 있는 거죠.</p>
<p>다만, 우리 수업에서 다루는 주제인 <strong>React가 채택한 프로그래밍 방식이 “함수형”이라는 겁니다.</strong></p>
<p>그럼 함수형 프로그래밍에서 코드를 어떻게 관리하는가? 그 부분을 중점적으로 봐주시고, 절대적으로 함수형이 명령형 또는 객체 지향 프로그래밍 보다 낫다고 말한 것이 아니므로 오해가 없었으면 합니다. 😄</p>
<hr>
<h2 id="결론">결론</h2>
<p>객체지향 프로그래밍과 선언형 프로그래밍 중에서 유지보수성과 코드 재사용성에서 뭐가 더 나은지 비교할 순 없다. =&gt; 설계에 따라서 달라짐</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS 과제 리뷰]]></title>
            <link>https://velog.io/@pearlx_x/JS-%EA%B3%BC%EC%A0%9C-%EB%A6%AC%EB%B7%B0</link>
            <guid>https://velog.io/@pearlx_x/JS-%EA%B3%BC%EC%A0%9C-%EB%A6%AC%EB%B7%B0</guid>
            <pubDate>Thu, 20 Jul 2023 08:08:02 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/pearlx_x/post/8e737609-1fbe-4b9c-ae40-b6b7f8fa719e/image.gif" alt="">
영화 포스터 갤러리 리드미</p>
<h1 id="🥰엘리멘탈-포스터-갤러리">🥰엘리멘탈 포스터 갤러리</h1>
<p>엘리멘탈 포스터 갤러리는 사용자가 클릭한 영화에 해당하는 정보를 화면에 출력하는 간단한 웹 페이지입니다. 사용자가 각 캐릭터 포스터를 클릭하면 해당 캐릭터의 이름이 페이지 상단에 표시되며, 배경색도 해당 캐릭터의 대표 색상으로 변경됩니다.</p>
<h2 id="🏷️주요-기능">🏷️주요 기능</h2>
<ol>
<li>사용자가 각 캐릭터 포스터를 클릭하면 해당 캐릭터의 이름을 화면 상단에 표시합니다.</li>
<li>클릭한 캐릭터 포스터의 배경색이 해당 영화의 색상으로 변경됩니다.</li>
<li>메인 사진에서 캐릭터 포스터의 이미지와 대체 텍스트(alt)가 올바르게 설정됩니다.<h2 id="🏷️프로젝트-실행-방법">🏷️프로젝트 실행 방법</h2>
</li>
<li>엘리멘탈 포스터 갤러리 레포지토리를 클론 또는 다운로드합니다.</li>
<li>index.html 파일을 웹 브라우저로 엽니다.</li>
<li>캐릭터 포스터를 클릭하여 기능을 확인합니다.</li>
</ol>
<p>이 프로젝트는 영화 포스터 갤러리를 보여주는 간단한 예시로, 추가적인 기능과 디자인을 적용하여 더욱 풍부한 웹 페이지로 발전시킬 수 있습니다. 코드에 대한 주석과 각 함수들의 역할을 잘 이해하시고, 필요한 기능을 추가하여 웹 페이지를 더욱 완성도 있게 만들어보세요.</p>
<h2 id="🏷️함수">🏷️함수</h2>
<h3 id="setimage">setImage</h3>
<pre><code class="language-js">function setImage(node, imageName, altPath) {
  if (typeof node === &quot;string&quot;) node = getNode(node);

  if (!node) return;

  const imagePath = `./assets/${imageName.toLowerCase()}.jpeg`;

  attr(node, &quot;src&quot;, imagePath);
  attr(node, &quot;alt&quot;, altPath);
}</code></pre>
<p><code>setImage</code> 함수를 사용하면 특정 이미지 엘리먼트의 이미지 파일과 대체 텍스트를 손쉽게 설정할 수 있습니다.</p>
<h3 id="setnametext">setNameText</h3>
<pre><code class="language-js">function setNameText(node, text) {
  if (!text || typeof text !== &quot;string&quot;) {
    throw new TypeError(&quot;setNameText 함수의 인수는 문자열이어야 합니다.&quot;);
  }

  node.textContent = text;
}</code></pre>
<p><code>setNameText</code> 함수를 사용하면 특정 노드에 간편하게 텍스트를 설정할 수 있습니다. 이 함수를 활용하여 HTML 문서의 특정 부분에 동적인 텍스트를 적용하거나, 사용자 입력에 따라 텍스트를 변경하는 등 다양한 상황에서 유용하게 활용할 수 있습니다. </p>
<h3 id="setbgcolor">setBgColor</h3>
<pre><code class="language-js">function setBgColor(node, value) {
  if (typeof node === &quot;string&quot;) node = getNode(node);

  if (!value || typeof value !== &quot;string&quot;) {
    throw new TypeError(&quot;setBgColor 함수의 인수는 문자열이어야 합니다.&quot;);
  }

  node.style.background = value;
}</code></pre>
<p><code>setBgColor</code> 함수를 사용하면 특정 노드의 배경색을 간편하게 변경할 수 있습니다. 이를 활용하여 JavaScript를 통해 동적으로 웹 페이지의 스타일을 조작할 수 있으며, 사용자 인터랙션에 따라 동적으로 스타일을 변경하는데 유용하게 활용할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[23.7.17 - 23.7.18 TIL]]></title>
            <link>https://velog.io/@pearlx_x/23.7.17-TIL</link>
            <guid>https://velog.io/@pearlx_x/23.7.17-TIL</guid>
            <pubDate>Tue, 18 Jul 2023 07:59:35 GMT</pubDate>
            <description><![CDATA[<h1 id="📌23717">📌23.7.17</h1>
<h2 id="🏷️arraymethod">🏷️arrayMethod</h2>
<h3 id="arrayisarray">Array.isArray</h3>
<p>기존에 사용하던 <code>typeof</code>는 언어가 가지는 오류 때문에 Null이나 배열의 타입을 알 수 없었다.</p>
<p>그래서 <code>Array.isArray</code>이 나왔는데 다음과 같이 사용하면 배열의 타입을 알아낼 수 있었다.<img src="https://velog.velcdn.com/images/pearlx_x/post/b77a12da-50ce-4144-aa13-b65db9ebc316/image.png" alt=""> 하지만 Null은 타입을 알기 어려운데 이것은 <code>toString</code>을 사용하면 알아낼 수 있다.</p>
<blockquote>
<p><code>toString</code>은 모든 타입이 가지고 있지만 찐객체가 가지고 있는 <code>toString</code>을 사용하면 
우리가 무슨 타입을 사용하고 있는지 정확하게 알려준다. <img src="https://velog.velcdn.com/images/pearlx_x/post/4043e54b-3ef1-4b2b-9037-d5d636980bf0/image.png" alt=""> <img src="https://velog.velcdn.com/images/pearlx_x/post/37de68bd-f212-4a31-955a-d2719a108e07/image.png" alt="">
<code>toString</code>, <code>toLowerCase</code> 와 <code>slice</code>를 사용하여 원하는 값을 얻어낼 수 있다. 그걸 이용해 다음과 같은 유틸 함수를 만들었다.</p>
</blockquote>
<pre><code class="language-js">// isArray 화살표함수.ver
const isArray = (data) =&gt;
  Object.prototype.toString.call(data).slice(8, -1).toLowerCase() === &#39;array&#39;;
//isArray 일반함수.ver
function nomalIsArray(data) {
  return (
    Object.prototype.toString.call(data).slice(8, -1).toLowerCase() === &#39;array&#39;
  );
}
//isNull 화살표 함수.ver
const isNull = (data) =&gt;
  Object.prototype.toString.call(data).slice(8, -1).toLowerCase() === &#39;null&#39;;
//isNull 일반함수.ver
function normalIsNull(data) {
  return (
    Object.prototype.toString.call(data).slice(8, -1).toLowerCase() === &#39;null&#39;
  );
}</code></pre>
<h3 id="tosorted-toreversed-tospliced"><code>toSorted</code>, <code>toReversed</code>, <code>toSpliced</code></h3>
<blockquote>
<p>올해 2023년에 새롭게 나온 메소드인 <code>toSorted</code>, <code>toReversed</code>, <code>toSpliced</code>를 사용해 봤다. 이름만 봐도 기존의 메소드와 비슷해서 무슨 일을 하는지 알 수 있었다.</p>
</blockquote>
<p>그럼 나온 이유가 무엇이나 하면
기존의 <code>sort</code>, <code>reverse</code>, <code>splice</code>는 원형을 파괴하는 메소드였다. 
하지만 이번에 새로 나온 메소드들은 기존과 같은 일을 하되, 원형을 파괴시키지 않는다.</p>
<pre><code class="language-js">const arr = [10, 100, 1000, 10000];

// toSorted =&gt; 원형을 파괴하지 않음 
const toSorted = arr.toSorted((a, b) =&gt; b - a);
 console.log(toSorted);

// toReversed =&gt; 원형을 파괴하지 않음 
const toReversed = arr.toReversed();
 console.log(toReversed);

// toSpliced =&gt; 원형을 파괴하지 않음 
 console.log(arr);
const toSpliced = arr.toSpliced(2, 0, &#39;js&#39;, &#39;css&#39;, &#39;react&#39;)
 console.log(toSpliced);
</code></pre>
<h3 id="⭐⭐map⭐⭐">⭐⭐map⭐⭐</h3>
<blockquote>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/081cf1b1-33c3-4663-865e-c2d76a893896/image.png" alt=""></p>
</blockquote>
<pre><code class="language-js">const job = people.map((item) =&gt; `&lt;div&gt;${item.profession}&lt;/div&gt;`); 
document.body.insertAdjacentHTML(&#39;beforeend&#39;, job);</code></pre>
<p>리스트가 잘 나왔지만 화면에 <code>,</code> 까지 출력이 되는 걸 볼 수 있다.
<span style="font-size:25px">🤔왜 <code>,</code> 까지 출력이 되는 것일까?</span></p>
<details>
  <summary>✅ 정답</summary>
map() 메서드는 새로운 배열을 반환하기 때문에 job 변수에는 문자열 배열이 들어가게 된다. <br>그리고 insertAdjacentHTML() 메서드는 문자열을 HTML로 파싱하여 해당 위치에 삽입한다. <br> 따라서 job 배열이 전달되면 각 문자열 요소가 콤마로 구분되어 삽입되게 된다 
</details>

<p><span style="font-size:30px">해결 방법 </span>
<img src="https://velog.velcdn.com/images/pearlx_x/post/7e954f43-59cb-412e-8577-49aafa6b5d9d/image.png" alt=""></p>
<pre><code class="language-js">const job = people.map((item) =&gt; `&lt;div&gt;${item.profession}&lt;/div&gt;`); 

// forEach문을 사용하면 원하는 결과값이 나오게 됨 
job.forEach((item) =&gt; document.body.insertAdjacentHTML(&#39;beforeend&#39;, item))</code></pre>
<h3 id="reduce">reduce</h3>
<p>reduce를 배울 때 acc의 초기값을 설정해주지 않으면 0이 선언되는 것으로 알고 있었다.</p>
<pre><code class="language-js">const totalAge = people.reduce((acc, cur) =&gt; acc + cur.age);</code></pre>
<p>이렇게 코드를 작성해주었는데 원하지 않는 값이 나왔다.
<img src="https://velog.velcdn.com/images/pearlx_x/post/3e9b4187-bda5-40f4-adb9-6888568a5d91/image.png" alt=""></p>
<blockquote>
<p>사실 <code>reduce</code>의 초기값은 배열에서 아이템으로 넘어온 대상을 초기값으로 설정한다.
숫자를 넣을 경우 숫자를 초기값으로 지정하므로 간단한 숫자를 더하거나 뺄 경우는 문제가 되지 않으나
배열안에 다른 자료구조가 있는 경우에는 이를 정확하게 인식할 수 있도록  initialValue를 설정해주는게 좋다.
<small><span style="color:gray">&amp;ltreduce안에서 acc자체만 출력했을때는 객체 자체가 나오기 때문에  숫자만 다루는 배열이 아닌 다른 자료구조가 들어오는 경우는 초깃값을 숫자로 지정해주어야 합니다 🙂&amp;gt</span></small></p>
</blockquote>
<p>따라서 위에 코드는 다음과 같이 수정할 수 있다.</p>
<pre><code class="language-js">const totalAge = people.reduce((acc, cur) =&gt; acc + cur.age, 0);

console.log(totalAge);</code></pre>
<h2 id="🏷️try-catch문">🏷️try catch문</h2>
<p>JS에서는 에러가 발생하면 페이지가 동작하지 않는다. 이런 문제를 해결하기 위해 try..catch문을 사용하면 된다.</p>
<pre><code class="language-js">try {
  let a = 5;
  a + b // try catch문을 쓰지 않으면 b가 정의되어 있지 않다는 에러메시지와 함께 에러가 발생한다.
} catch(e) {
  console.log(e.name);
  console.log(e.message);
  console.log(e.stack);
}</code></pre>
<p>error 메시지는 name, message, stack 3가지로 구성되어 있다.
<img src="https://velog.velcdn.com/images/pearlx_x/post/0db97276-3c46-4f3f-ab82-9ce159faa31d/image.png" alt=""></p>
<h2 id="🏷️bom">🏷️bom</h2>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/a35239b1-341d-4072-b633-fd2dd8cb7486/image.png" alt=""></p>
<h1 id="📌23718">📌23.7.18</h1>
<h2 id="🏷️nodeclass">🏷️nodeClass</h2>
<pre><code class="language-js">/* 노드 콘텐츠 읽기/쓰기 ---------------------------------------------------- */

// - innerHTML
// * 기존 내용 삭제
// * 기존 내용과 새로운 내용을 합친 새로운 내용을 씀
first.innerHTML = &#39;&lt;div&gt;helloooooooo&lt;/div&gt;&#39;; //html을 추가하는 것이기 때문에 위험하다

// - textContent
// * 요소 내의 텍스트에 접근
// * 태그는 제외하고 오로지 텍스트만 추출
console.log((first.textContent = &#39;hh&#39;));</code></pre>
<h2 id="🏷️attribute">🏷️attribute</h2>
<h3 id="표준속성">표준속성</h3>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/7e885845-a81c-4cbf-8348-b8465aecb040/image.png" alt=""><img src="https://velog.velcdn.com/images/pearlx_x/post/8c36703d-577e-4e46-921f-ecb788b15ccf/image.png" alt=""> <code>attributes</code>는 iterable 하기 때문에 for...of문을 사용할 수 있다.
<img src="https://velog.velcdn.com/images/pearlx_x/post/ec3f0ba0-0167-4d4f-aecc-8218bed84f4d/image.png" alt=""></p>
<h3 id="비표준-속성">비표준 속성</h3>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/07318d2e-fdc3-4b90-9e36-f2c2cc1b8d32/image.png" alt=""></p>
<h2 id="🏷️style">🏷️style</h2>
<p>elementNode.style.porp은 getter로는 사용 불가능한 이유?</p>
<blockquote>
<p>해당 요소에 직접적으로 backgroundColor 속성이 지정하면 사용할 수 있지만 그렇지 않은 경우에는 계산되기 이전 값을 가져오기 때문에 사용할 수 없다. </p>
</blockquote>
<p>그럼 스타일을 가져올 수 없느냐
그건 아니다 </p>
<pre><code class="language-js">// - getComputedStyle(element, [pseudoElement]) `읽기 전용`
console.log(getComputedStyle(first).fontSize); // 32px / 아래와 같음
console.log(getComputedStyle(first).getPropertyValue(&#39;font-size&#39;)); // 32px</code></pre>
<p><code>getComputedStyle</code>를 사용하면 값을 찾아올 수 있다.</p>
<p>수업에서 <code>getCss()</code>, <code>setCss()</code> 유틸 함수를 만들었는데 
수업시간에 만든 것은 케밥 케이스로만 값을 입력해야 함수가 제대로 작동하는 것만 만들었다. 
하지만 둘 중 아무거나 사용해도 제대로 작동할 수 있도록 만들고 싶어 수정해 보았다.</p>
<pre><code class="language-js">function getCss(node, prop) {//&#39;font-size&#39;.indexOf(&#39;-&#39;)
  if(typeof node === &#39;string&#39;) node = getNode(node)

  if(!(prop in document.body.style)) {
    throw new SyntaxError(&#39;getCss 함수의 두 번째 인자인 prop은 유효한 css 속성이 아닙니다&#39;)
  }

  return getComputedStyle(node).getPropertyValue(prop)
}</code></pre>
<p>수업시간에 내가 작성한 코드다 여기서 무엇을 추가하면 카멜 케이스로 사용할 수 있을 까 생각해보았는데 indexOf()를 사용하면 될 것 같아 다음과 같이 수정해 보았다.</p>
<pre><code class="language-js">function getCss(node, prop) {//&#39;font-size&#39;.indexOf(&#39;-&#39;)
  if(typeof node === &#39;string&#39;) node = getNode(node)

  if(!(prop in document.body.style)) {
    throw new SyntaxError(&#39;getCss 함수의 두 번째 인자인 prop은 유효한 css 속성이 아닙니다&#39;)
  }

  if(prop.indexOf(&#39;-&#39;) == -1) return getComputedStyle(node)[prop] 
  return getComputedStyle(node).getPropertyValue(prop)
}</code></pre>
<p><code>prop.indexOf(&#39;-&#39;) == -1</code> prop이라는 매개변수에 <code>-</code>이 포함되어 있지 않다면 <code>getComputedStyle(node)[prop]</code> 코드를 사용하여 리턴해주는 코드를 작성했다.</p>
<p>결과는 아주 잘 작동했다.😊</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[23.7.13 TIL]]></title>
            <link>https://velog.io/@pearlx_x/23.7.13-TIL</link>
            <guid>https://velog.io/@pearlx_x/23.7.13-TIL</guid>
            <pubDate>Thu, 13 Jul 2023 09:50:22 GMT</pubDate>
            <description><![CDATA[<h1 id="📌class">📌class</h1>
<p><span style="font-size:30px">클래스 vs 객체 코드 비교</span><img src="https://velog.velcdn.com/images/pearlx_x/post/41823a4c-ce22-4aaf-a92a-96ee9295a39d/image.png" alt=""> </p>
<hr>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/dc4a0ce8-83b9-4c86-b259-3d3d5df59a33/image.png" alt="">외부에서 beom.prey로 접근 불 가능하지만, 내부에서는 prey로 접근이 가능하다 </p>
<hr>
<pre><code class="language-js"> static sleep(name) {
    console.log(name + &#39;이 잠을 잔다.&#39;);
  }</code></pre>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/e6c6902c-5922-4291-823b-6a050add01bd/image.png" alt="">static 키워드가 붙은 메서드는 클래스 자체에 속하는 정적 메서드입니다. =&gt; 모두가 사용할 수 있게 바뀜
정적 메서드는 인스턴스 객체에서 직접 호출할 수 없으며, 클래스명을 통해 호출해야 한다.</p>
<blockquote>
<p>따라서, beom.sleep()과 같이 인스턴스 객체인 beom에서 static 메서드인 sleep()을 호출하는 것은 잘못된 사용입니다. 정적 메서드는 <code>클래스 수준에서 공유되는 기능을 제공</code>하기 때문에, Tiger.sleep(&#39;범&#39;)과 같이 클래스명을 통해 호출해야 한다.</p>
</blockquote>
<p>올바른 사용 예시는 다음과 같습니다:</p>
<h1 id="📌closure">📌closure</h1>
<p>클로저란? 
JavaScript의 매우 강력한 특성으로 독립적인 변수를 참조하는 함수를 말합니다.
★ 즉, 클로저에 정의된 함수는 그것이 작성된 환경을 &#39;기억&#39;합니다.
클로저를 사용하는 이유</p>
<blockquote>
<ol>
<li>전역의 오염을 막기 위해 </li>
<li>값을 기억하기 위해(가비지 컬렉터 수집 x)</li>
<li>함부로 내가 설정한 값을 수정할 수 없게 하고 특정 함수를 통해서만 접근할 수 있게 하기 위해</li>
</ol>
</blockquote>
<pre><code class="language-js">function first() {
  let x = 10;

  function second() {
    let y = 5;
    return x + y;
  }
  // return x;
  return second // 함수를 실행하는게 아닌 본문을 내보냄
}

first() //  function second() {
        //     let y = 5;
        //    return x + y;
        //   }

//currying function
const value = first() //first()() // 15</code></pre>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/254b83b2-15d7-4e49-a0a3-5dabb3e50e66/image.png" alt=""></p>
<p>first 내부에 있는 second를 실행시킨 값을 내보내는 게 아닌 함수 본문을 first의 return 값으로 내보냈다.</p>
<pre><code class="language-js">function counter(){
  let count = 0;

  count++;

  return count;
}


let result = counter();</code></pre>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/a95ed952-6803-4cf7-90a4-0ed5aeddf83a/image.png" alt="">클로저를 이용한 counter 함수</p>
<hr>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/d7ae7db0-ccad-4009-a403-91906b5fccfd/image.png" alt=""></p>
<blockquote>
<p>🤔 만약 호랑이가 지구에서 쫓겨난 후에 지구에 새로운 키, 밸류가 추가되면 그것도 호랑이가 수집할 수 있나요? ✅ yes
🤔 그럼 클로저를 통해 earth() 안의 정보를 가져오고 싶을 때는 내부 function에 적어줘야만 가져올 수 있는 건가요? 
✅ yes
🤔그럼 water나 apple은 접근 못하는건가요??
 ,그럼 보통 외부 function에는 변수를 지정하고 내부 function에는 실행 시킬내용을 지정하면되는거죠?
 ✅ yes</p>
</blockquote>
<p>value에는 second가 담겨 있는 것 이지만 second 함수는 자신이 작성된 실행환경을 기억하고 있기 때문에 
earth() 전체가 가비지 컬렉터의 수집 대상이 되지 않는 것이다. <img src="https://velog.velcdn.com/images/pearlx_x/post/b7f01a01-590c-4d5c-987b-3925847be9bc/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[<form> - 405 에러 해결]]></title>
            <link>https://velog.io/@pearlx_x/405-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@pearlx_x/405-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Thu, 13 Jul 2023 07:50:20 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트 첫 과제를 진행하는데 
버튼을 눌렀을 때 입력받은 input 값과 user의 값이 일치하면 다음 페이지로 넘어가게 하는 기능을 구현하는 과정에서
<code>웹 서버에서 요청된 URL에 대해 HTTP 메서드를 허용하지 않을 때 발생</code> 하는 405 에러가 발생했다.
<img src="https://velog.velcdn.com/images/pearlx_x/post/3a23ff85-179f-4d3a-933c-3010856020fe/image.png" alt=""></p>
<h1 id="🤔문제발생">🤔문제발생</h1>
<blockquote>
<p>처음엔 로직에 문제가 있는 줄 알고 콘솔창에 직접 함수를 입력하여 구동시켰더니 정상 작동을 했다.
원인을 찾기 위해 구글링을 했지만 나는 서버와 협업해서 하는 것이 아니기 때문에 검색된 결과들은 나에게 별 도움이 되지 않았다.</p>
</blockquote>
<h1 id="🧐원인발견">🧐원인발견</h1>
<blockquote>
<p>계속 문제가 무엇인지 찾다보니
<code>&lt;form action=&quot;URL&quot; class=&quot;login-form&quot; method=&quot;POST&quot;&gt;</code>에서
<code>Http request</code>를 시도했는데 정확한 서버도 아닌 있지도 않은 서버에게 요청을 해서 발생한 문제라는 걸 찾아냈다!!</p>
</blockquote>
<h1 id="😊문제해결">😊문제해결</h1>
<p>이제 문제를 찾았으니 js 함수를 수정해봤다.</p>
<pre><code class="language-js">//수정 전
function checkUser(checkId, checkPw) {
  if ((checkId.value === user.id) &amp;&amp; (checkPw.value === user.pw)) {
    window.location.href = &#39;welcome.html&#39;;
  }else if(checkId.value.length === 0 || checkPw.value.length === 0) {
    throw new Error(&#39;아이디와 비밀번호는 필수 입력값입니다.. &#39;)
  }
  else {
    throw new Error(&#39;찾을 수 없는 회원입니다&#39;)
  }
}
loginButton.addEventListener(&#39;click&#39;, ()=&gt; {
  checkUser(emailInput, passwordInput);
})</code></pre>
<p>로그인 버튼을 찾아와서 클릭 이벤트가 발생했을때 checkUser 함수를 실행하는 알고리즘을 작성했다.</p>
<hr>
<pre><code class="language-js">// 수정 후
form.addEventListener(&#39;submit&#39;, (event) =&gt; {
  event.preventDefault();
  checkUser(emailInput, passwordInput);
});</code></pre>
<blockquote>
<p>기존 코드는 지운 후</p>
</blockquote>
<ol>
<li><code>loginButton</code>이 아닌 form태그에 이벤트 함수를 작성해주었다.<br></li>
<li>기존의 <code>click</code>이 아닌 폼의 제출을 감지하고 처리하기 위해서는 <code>submit</code> 이벤트를 사용해야 하기 때문에 <code>submit</code>으로 수정해주었다.<br></li>
<li><code>event.preventDefault();</code>
<small><span style="color:gray">어떤 이벤트를 명시적으로 처리하지 않은 경우, 해당 이벤트에 대한 사용자 에이전트의 기본 동작을 실행하지 않도록 지정 </span></small></li>
</ol>
<ul>
<li><code>form</code>의 경우, <code>제출(submit)</code> 버튼을 클릭하면 기본적으로 폼 데이터를 서버로 전송하고 페이지를 다시 로드하는 동작이 수행되는데,
지금 프로젝트에서는 서버가 없기 때문에 이 동작을 막아주기 위해 추가해줬다.<br></li>
</ul>
<blockquote>
<p>이렇게 변경한 후 다시 로그인 버튼을 눌러보면
<img src="https://velog.velcdn.com/images/pearlx_x/post/2818d27a-8963-45d1-b1e5-a3cb61b103d1/image.gif" alt="">다음 페이지로 잘 넘어가는 것을 확인할 수 있다!! </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[23.7.12 TIL]]></title>
            <link>https://velog.io/@pearlx_x/23.7.12-TIL</link>
            <guid>https://velog.io/@pearlx_x/23.7.12-TIL</guid>
            <pubDate>Wed, 12 Jul 2023 11:03:16 GMT</pubDate>
            <description><![CDATA[<h2 id="🏷️일반함수-단축구문">🏷️일반함수 단축구문</h2>
<pre><code class="language-js">// 아래 두 객체는 동일하게 동작합니다.

user = {
  sayHi: function() {
    alert(&quot;Hello&quot;);
  }
};

// 단축 구문을 사용하니 더 깔끔해 보이네요.
user = {
  sayHi() { // &quot;sayHi: function()&quot;과 동일합니다.
    alert(&quot;Hello&quot;);
  }
};</code></pre>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/53b78510-7f13-4820-88c2-6003952b5c7b/image.png" alt=""></p>
<blockquote>
<p>SayBye는 단축구문으로 정의한 함수
SayHi는 단축구문이 아닌 평범하게 정의한 함수</p>
</blockquote>
<p>⭐ 단축구문은 일반함수지만 prototype: {constructor}를 내장하지 않는다. 그러나 일반함수이기에 this도 단축구문을 사용하지 않은 일반함수처럼 잘 찾아줌!!
⭐ 객체의 메소드를 정의할 때 consise method만 사용하자!</p>
<h2 id="🏷️객체의-메소드를-정의할-때-화살표-함수는-안되는-이유">🏷️객체의 메소드를 정의할 때 화살표 함수는 안되는 이유</h2>
<pre><code class="language-js">const navigationMenu = {// 수정 전
  name: &#39;글로벌 내비게이션&#39;,
  items: [
    { id: &#39;link-g&#39;, text: &#39;Google&#39;, link: &#39;https://google.com&#39; },
    { id: &#39;link-n&#39;, text: &#39;Naver&#39;, link: &#39;https://naver.com&#39; },
  ],
  getItem(index) {
    return this.items[index]; // this는 navigationMenu
  },
     addItem: (newItem) =&gt; {
     this.items.push(newItem); //this는 navi가 아닌 window이기 때문에 에러 발생 (widow.items.push())
   },

};

navigationMenu.getItem()
navigationMenu.addItem({
  id: &#39;link-l&#39;,
  text: &#39;Lycos&#39;,
  link: &#39;https://lycos.co.kr&#39;
})</code></pre>
<p>다음과 같이 작성하면 콘솔창에 에러가 뜨는데</p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/6a579603-3ba9-4b5c-a237-c7d263b24b47/image.png" alt="">
push를 할 수 없다는 에러가 발생한다.
그 이유는 화살표함수를 사용하게 되면 this는 navi~가 아닌 window를 가리키기 때문에 에러 발생한다. 따라서 정확한 this를 찾기 위해서는 일반함수를 사용해야 한다.</p>
</blockquote>
<pre><code class="language-js">const navigationMenu = { //수정 후
  name: &#39;글로벌 내비게이션&#39;,
  items: [
    { id: &#39;link-g&#39;, text: &#39;Google&#39;, link: &#39;https://google.com&#39; },
    { id: &#39;link-n&#39;, text: &#39;Naver&#39;, link: &#39;https://naver.com&#39; },
  ],
  getItem(index) {
    return this.items[index]; // this는 navigationMenu
  },
  addItem(newItem) {
    this.items.push(newItem); //this는 navigationMenu
  },
};

navigationMenu.getItem()
navigationMenu.addItem({
  id: &#39;link-l&#39;,
  text: &#39;Lycos&#39;,
  link: &#39;https://lycos.co.kr&#39;
})</code></pre>
<h1 id="📌프로토타입객체지향">📌프로토타입(객체지향)</h1>
<h2 id="🏷️프로토타입-상속">🏷️프로토타입 상속</h2>
<blockquote>
<p>설명!<img src="https://velog.velcdn.com/images/pearlx_x/post/13eeb0e9-0097-4ce1-aeef-1e42b203ec3a/image.png" alt="">object에서 프로퍼티를 읽으려고 하는데 해당 프로퍼티가 없으면 자바스크립트는 자동으로 프로토타입에서 프로퍼티를 찾기 때문이죠. 프로그래밍에선 이런 동작 방식을 &#39;프로토타입 상속’이라 부릅니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/4fab83be-8787-44c2-b2b6-765350f08c5e/image.png" alt="">
찐객체가 만들어낸 자식 객체 animal은 <code>__proto__</code>를 사용해서 찐객체에 접근할 수 있다
<code>Object.prototype === animal.__proto__</code>는 같다.
Object.prototype - 찐객체에 바로 접근할 수 있고 객체를 생성하지 않고 사용 가능
<code>객체.__proto__</code> - 양산품에서 찐객체에 접근할 일이 생길 때 사용</p>
<pre><code class="language-js">let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};
//rabbit의 부모를 찐객체에서 animal로 바꿀 수 있음 (rabbit -&gt; animal -&gt; realObject)
rabbit.__proto__ = animal;</code></pre>
<p>장점: rabbit.eats를 사용할 수 있음!! (상속 받았기 때문에)
<img src="https://velog.velcdn.com/images/pearlx_x/post/fbfb741c-7e38-4a5f-ae8d-07cb826bef5a/image.png" alt=""></p>
<blockquote>
<p>this로 찾아지는 윈도우의 위치
<img src="https://velog.velcdn.com/images/pearlx_x/post/02595569-02a1-49d9-aef0-a83cc3c958f2/image.png" alt=""></p>
</blockquote>
<blockquote>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/e10ce13c-3b9c-4edf-bbec-5921bda60f05/image.png" alt="">찐객체 위의 null 찾기</p>
</blockquote>
<blockquote>
<p>🤔 프로토타입은 다른 자료형을 상속하면  그 자료형의 프로토타입 메서드도 사용할 수 있는건가요? ✅ yes
<img src="https://velog.velcdn.com/images/pearlx_x/post/01397655-61bc-4c5f-9e31-d55eb572bdbc/image.png" alt="">
<code>__proto__</code>를 사용하지 않는 이유 -&gt; 비표준이라</p>
</blockquote>
<h3 id="🏷️프로토타입-체이닝">🏷️프로토타입 체이닝</h3>
<pre><code class="language-js">let animal = {
  eats: true,
  walk() {
    alert(&quot;동물이 걷습니다.&quot;);
  }
};

let rabbit = {
  jumps: true,
  __proto__: animal
};

let longEar = {
  earLength: 10,
  __proto__: rabbit
};

// 메서드 walk는 프로토타입 체인을 통해 상속받았습니다.
longEar.walk(); // 동물이 걷습니다.
alert(longEar.jumps); // true (rabbit에서 상속받음)</code></pre>
<p>⚠️ 프로토타입 체이닝의 제약사항</p>
<blockquote>
<ol>
<li>순환 참조(circular reference)는 허용되지 않습니다. <code>__proto__</code>를 이용해 닫힌 형태로 다른 객체를 참조하면 에러가 발생한다.</li>
<li><code>__proto__</code>의 값은 객체나 null만 가능합니다. 다른 자료형은 무시된다.
➕ 객체엔 오직 하나의 <code>[[Prototype]]</code>만 있을 수 있다.
객체는 두 개의 객체를 상속받지 못한다.</li>
</ol>
</blockquote>
<h3 id="🏷️enumerable열거-가능한-프로퍼티">🏷️enumerable(열거 가능한) 프로퍼티</h3>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/4f2737c7-e125-4e01-a872-caea5a5b3738/image.png" alt="">
<code>enumerable</code> 하지 못한 애들은 어두운 색으로 표시
for..in문에서 표시가 되지 않는 이유는 for..in문은 <code>enumerable</code>한 프로퍼티만 순회대상에 포함되기 때문이다.</p>
<blockquote>
<p>🤔 우린 enumerable한 요소들만 만들 수 있는건가?
✅ Object.defineProperty()를 사용하면 enumerable 하지 않은 요소들도 만들 수 있다
<img src="https://velog.velcdn.com/images/pearlx_x/post/c42280b0-0752-4eb5-bb86-143806598b11/image.png" alt=""><img src="https://velog.velcdn.com/images/pearlx_x/post/a583da57-ddb2-4448-9442-134c92cef1fa/image.png" alt=""></p>
</blockquote>
<h2 id="🏷️함수의-프로토타입-프로퍼티">🏷️함수의 프로토타입 프로퍼티</h2>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/cfd216ba-60f8-4a1e-a5cb-27bd57ff9177/image.png" alt=""></p>
<pre><code class="language-js">cat.hunt = function (target) {
  this.prey = target, 
  console.log(`${target}에게 슬금슬금 접근한다.`);
}</code></pre>
<p>🤔 여기서의 this는 무엇일까?
✅ 정답 cat <img src="https://velog.velcdn.com/images/pearlx_x/post/190601ee-96f4-47ae-bd0c-175371822ce2/image.png" alt=""></p>
<hr>
<blockquote>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/22c3bc6d-7d2b-4817-bbca-2d6b5908f3b9/image.png" alt="">return 값이 없기 때문에 undefined!!
<img src="https://velog.velcdn.com/images/pearlx_x/post/32cf7a75-d5d5-41a5-a2ce-0917eb9ab762/image.png" alt="">화살표 함수는 함수로서의 기능만 하니까 constructor를 내장하고 있지 않기 때문에 new 키워드를 사용하여 객체를 만들 수 없다. 
<code>error 발생!!!</code></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[23.7.11 TIL]]></title>
            <link>https://velog.io/@pearlx_x/23.7.11-TIL</link>
            <guid>https://velog.io/@pearlx_x/23.7.11-TIL</guid>
            <pubDate>Tue, 11 Jul 2023 14:12:04 GMT</pubDate>
            <description><![CDATA[<h1 id="📌this">📌this</h1>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/1af617e0-48c3-4571-8366-36892008c5c2/image.png" alt=""></p>
<h1 id="함수의-실행-컨텍스트">함수의 실행 컨텍스트</h1>
<p>함수는 전역으로 선언되고 윈도우랑 바인딩된다. 
그후 함수를 실행 할 때 함수 내부에서는 또 다른 함수 실행 컨텍스트(환경)가 만들어진다.
<img src="https://velog.velcdn.com/images/pearlx_x/post/a307fbfd-690c-417a-ad7a-15ed13b00457/image.png" alt="">
<img src="https://velog.velcdn.com/images/pearlx_x/post/54d14fd9-2ba9-4b2c-b791-59ed3fda0b5c/image.png" alt=""></p>
<h1 id="📌object">📌Object</h1>
<h2 id="🏷️표기법">🏷️표기법</h2>
<pre><code class="language-js">// 점(.) 표기법
// authUser 객체의 프로퍼티에 접근해 Console에 출력해봅니다.
 console.log( authUser.uid );
 console.log( authUser.permission );
 console.log( authUser.email );

// 대괄호([]) 표기법
// 대괄호 표기법을 사용해 접근 Console에 출력해봅니다.
console.log( authUser[&#39;uid&#39;]);
console.log( authUser[&#39;email&#39;]);
console.log( authUser[&#39;name&#39;]);
</code></pre>
<p>⚠️ 변하는 키를 받아야 할 땐 점 표기법말고 대괄호를 사용해야 한다.</p>
<h3 id="⚒️클래스-객체-생성하기">⚒️클래스 객체 생성하기</h3>
<pre><code class="language-js">class User{
  constructor(name,email){
    this.name = name;
    this.email = email
  }
}

const user3 = new User(&#39;동혁&#39;)</code></pre>
<h3 id="⚒️함수로-객체-생성하기">⚒️함수로 객체 생성하기</h3>
<pre><code class="language-js">/* shorthand property (단축 프로퍼티)  */
function createUser(
  name,
  email,
  computedProp = &#39;phone&#39;,
  number = &#39;010-0000-0000&#39;   
) {
  return {
    name: name,
    email: email,
    [computedProp]: number
  }

}
const user1 = createUser(
  &#39;진승&#39;,
  &#39;victory&#39;,
  &#39;tel&#39;,
  &#39;010-1234-5678&#39;
)</code></pre>
<h2 id="🏷️프로퍼티-나열">🏷️프로퍼티 나열</h2>
<pre><code class="language-js">// 프로퍼티 나열

// key만 모아놓은 배열
let keyArray = Object.keys(authUser);
let valueArray = Object.values(authUser);

function getProp(object) {
  if(typeof object !== &#39;object&#39;) {
    throw new Error(&#39;getProp 함 수의 매개변수는 객체 타입이어야 합니다&#39;)
  }
  return Object.keys(object);
}
/* --------- Object.keys를 사용하지 않고 키만 배열로 담아주는 함수 만들기 -------------------- */
function getP(object) {
  let result = [];

  for(let key in object) {
    if(({}).hasOwnProperty.call(object, key)) {
      result.push(key)
    }
  }

  return result;
}</code></pre>
<h2 id="🏷️remove와-delete">🏷️remove와 delete</h2>
<table>
<thead>
<tr>
<th>remove</th>
<th>delete</th>
</tr>
</thead>
<tbody><tr>
<td>null(비우기)</td>
<td>없앰</td>
</tr>
<tr>
<td>authUser.name = null</td>
<td>delete authUser.uid</td>
</tr>
</tbody></table>
<pre><code class="language-js">function removeProperty(object, key) { 
  if(typeof object !== &#39;object&#39;) {
    throw new Error (&#39;....&#39;)
  }

  if(key === &#39;all&#39;) {
    for(let key of Object.keys(object)) {
      object[key] = null;
    }
      return object
    }

    object[key] = null;

    return object;
  }

function deleteProperty(object, key) {

  if(isEmptyObject(object)) {
    return;
  }
  delete object[key];
  return  object;
}</code></pre>
<pre><code class="language-js">// 객체가 프로퍼티를 포함하는 지 유무를 반환하는 유틸리티 함수 isEmptyObject 작성
function isEmptyObject(object) { // length를 사용하여 비었는지 확인
// 실행 시, object가 비어있다면 true를 반환하고, 그렇지 않으면 false를 반환.
  return !(Object.keys(object).length); // 1
/*--------------------------------------*/
 if(Object.keys(object).length===0) { // 2
  return true;
 }
   return false;
/*----------------------------------------------------*/
  return Object.keys(object).length === 0 ? true: false // 3
}</code></pre>
<h2 id="🏷️구조분해할당">🏷️구조분해할당</h2>
<p><code>배열의 구조분해할당</code> : 순서가 정해져 있음, 변수의 이름을 바꿀 수 있다.
<code>객체의 구조분해할당</code> : 순서가 정해져 있지 않음, 선언은 같은 변수로 지정해야 하고 <code>:</code>을 사용하여 변수를 바꿀 수 있다. 기본값도 할당가능.</p>
<h3 id="배열-구조분해할당">배열 구조분해할당</h3>
<pre><code class="language-js">let color = [&#39;#ff0000&#39;, &#39;#2b00ff&#39;, &#39;#00ff02&#39;];

let [red, blue, green] = color // 순서 중요! let [,,g] = color =&gt; green이 담김
                               // let red = color[0];
                               // let blue = color[1];
                               // let green = color[2];

  for(let [key, value] of Object.entries(authUser)) {
     let key = keyValue[0]
     let value = keyValue[1]
     console.log(key)
  }
/*-------------------------------------------------------------------*/
 const [a,b,c,d] = document.querySelectorAll(&#39;nav li button&#39;)
 a.addEventListener(&#39;click&#39;, ()=&gt; {})
 b.addEventListener(&#39;click&#39;, ()=&gt; {})
 c.addEventListener(&#39;click&#39;, ()=&gt; {})
 d.addEventListener(&#39;click&#39;, ()=&gt; {})</code></pre>
<h3 id="객체-구조분해할당">객체 구조분해할당</h3>
<pre><code class="language-js">const salaries = {
  권헤미: 50,
  이수연: 3000,
  강예나: 500,
  김태일: 700
}
const { 권혜미, 이수연, 강예나, 김태일} = salaries // const 권혜미 = salaries.권헤미
console.log(권혜미)

function setElementCss(options) {
  const {width:w, height:h = 10, overflow, color} = options;
  console.log(w, color);
}
/*--------------------------------------------------------------*/
  function setElementCss(options) { //객체를 던지면
    console.log(options.width, options.color) // 순서에 상관없이 사용가능
  }

setElementCss({
  width: 100,
  height: 200,
  overflow: false,
  color: &#39;orange&#39;
})</code></pre>
<h2 id="🏷️복사copy-vs-참조reference">🏷️복사(copy) vs. 참조(reference)</h2>
<p><strong>원시값은 복사 객체는 참조</strong></p>
<h3 id="객체-복사">객체 복사</h3>
<pre><code class="language-js">// 객체 복사
// 1. for ~ in 문을 사용한 복사

// 얕은 복사
const cloneObject = {};

for (const key in messenger) {
  cloneObject[key] = messenger[key];
}

// 2. Object.assign()을 사용한 복사
const copyObject = Object.assign({}, messenger);

// 3. 전개 연산자(...)를 사용한 복사
const spreadObject = { ...messenger };

// 4. 객체를 복사해주는 유틸 함수
 function copyedObject(object) {
   return {...object} // or Object.assign({}, object)
 } //아래는 화살표 함수로 바꾼 것 

// copyedObject라는 함수는 object를 매개변수로 받고 Object.assign()을 통해서 전달받은 object를 복사해서 값을 반환하는 함수다.
const copyedObject = object =&gt; Object.assign({}, object); 

const newObject = copyedObject(messenger);
console.log(newObject)

// 객체 병합(합성)
const cssMapA = {
  color: &#39;#4b004b&#39;,
  margin: &#39;0 auto&#39;,
};

const cssMapB = {
  display: &#39;flex&#39;,
  flexFlow: &#39;column&#39;,
  justifyContent: &#39;center&#39;,
  padding: &#39;0.4em 0.62em&#39;,
  color: &#39;#3f9e97&#39;,
};

let combinedCssMap = {...cssMapA, ...cssMapB}; // 중복된 값이 있으면 뒤에 값으로 덮여 쓰여진다.
// ==  let combinedCssMap = Object.assign({}, cssMapA, cssMapB)</code></pre>
<h3 id="중첩-객체-복사">중첩 객체 복사</h3>
<pre><code class="language-js">// 중첩된 프로퍼티에 객체를 포함하는 객체 복사
// 얕은 복사 vs. 깊은 복사
const containerStyles = {
  &#39;min-height&#39;: &#39;100vh&#39;,
  &#39;max-width&#39;: {
    sm: &#39;90%&#39;,
    md: 640,
    lg: 960,
    xl: 1120,
    xxl: 1140,
  },
};

// let copyedContainerStyles = {...containerStyles};  얕은 복사
// let copyedContainerStyles = cloneDeep(containerStyles);  깊은 복사

// 1. 깊은 복사 유틸리티 함수 (재귀함수)
function cloneDeep(object) {
  return Object.fromEntries(
    Object.entries(object).map(([key, value]) =&gt; {
      let type = typeof value;
      if (value &amp;&amp; type === &#39;object&#39;) {
        value = cloneDeep(value);
      }
      return [key, value];
    })
  );
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[23.7.10 TIL]]></title>
            <link>https://velog.io/@pearlx_x/23.7.10-TIL</link>
            <guid>https://velog.io/@pearlx_x/23.7.10-TIL</guid>
            <pubDate>Mon, 10 Jul 2023 10:57:30 GMT</pubDate>
            <description><![CDATA[<h1 id="📌함수-표현식">📌함수 표현식</h1>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/05213d92-2aa5-41a6-8ac1-ddad99dbd38e/image.png" alt=""></p>
<h2 id="📌arguments">📌arguments</h2>
<p><span style="font-size:30px">arguments는 </span>JS 함수 내부에서 사용되는 특별한 <code>객체</code>이다. 
이 객체에는 함수에 전달된 <code>인수(argument)</code>를 포함하고 있다.</p>
<blockquote>
<p>arguments 객체는 배열 유사 객체(Array-like object)로서, 배열의 일부 메소드(예: length, forEach)를 사용할 수는 있지만, 진짜 배열은 아니기 때문에 배열의 다른 메소드(예: push, pop)는 사용할 수 없다. 하지만 다음과 같이 arguments 객체를 진짜 배열로 변환할 수 있다.</p>
</blockquote>
<pre><code class="language-js">// slice를 빌려써서 배열로 만드는 방법 
// Array의 슬라이스라는 기능을 빌려써서 
// arguments 진짜 배열로 바꿔 realArray에 담았다. (instance method) 

   let realArray = Array.prototype.slice.call(arguments);
   realArray.forEach(function(item) {
     total += item;
   })
   Array.from() (Static method)
/* ------------------------------------------------ */

 //% Array.from() (Static method)
   let realArray = Array.from(arguments);
   realArray.forEach(function (item) {
     total += item;
   });

/* ------------------------------------------------ */

  // spread syntax    69~85 
  // 스프레드 문법을 사용하여 배열로 바꿈 
  let arr = [12, 40, 100];
   let realArray = [...arguments];

   realArray.forEach(function (item) {
     total += item;
   });
 /* ------------------------------------------------ */

  //% spread syntax + Array.reduce 
  let realArray = [...arguments];
   return realArray.reduce((acc,item) =&gt; { //acc - 누적값
    return acc + item
   }, 0) // initValue 최초값</code></pre>
<h1 id="📌즉시-실행-함수">📌즉시 실행 함수</h1>
<pre><code class="language-js">// 즉시 실행 함수 (표현)식
// Immediately Invoked Function Expression - 변수의 보호를 위해 탄생
let IIFE;


// 변수의 보호
// 은닉화 캡슐화 

const MASTER = (function (){//선언부
  var x =10; 
   let uid = &#39;ajttk753!@&#39;

   return {
    getKey() {
      return uid;
    }
   };
})()//실행부

// 장점: 선언부는 매개변수를 실행부는 인수를 가지는데 즉시실행함수에서는 뭔가를 받았을 때 인자의 이름을 변경하여 사용할 수 있다.

console.log(MASTER.getKey());// MATSTER 변수를 통해서만 접근할 수 있음
</code></pre>
<blockquote>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/45f7de66-3ea9-43cb-87d7-57bf7a34c4da/image.png" alt=""><span style="font-size:30px">🤔함수인데 왜 객체처럼 쓰는 것이 가능할까?</span></p>
</blockquote>
<details>
  <summary>✅ JavaScript에서 함수도 결국 객체로부터 만들어져 객체의 일종이기 때문이다..!</summary><hr>
 <span style="color:#FF3939">JavaScript</span>는 "함수형 프로그래밍"이라는 개념을 지원하며, 함수를 일급 객체로 취급한다. <br>
  이는 함수를 변수에 할당하거나, 다른 함수의 인자로 전달하거나, 함수에서 반환할 수 있는 것을 의미
 함수는 <span style="color:yellow"></span>
"Function"이라는 내장된 <strong style="color:#FF3939">객체 타입의 인스턴스</strong>입니다. <br>∴ 함수는 객체처럼 속성과 메서드를 가질 수 있습니다.<br> <small>ex) <br> 함수의 속성 - name, length... <br>함수의 메서드 - call(), apply(), bind()...</small>
</details>

<h1 id="🤔함수-선언문과-함수-표현식-중-어떤-것이-더-좋을-까">🤔함수 선언문과 함수 표현식 중 어떤 것이 더 좋을 까?</h1>
<blockquote>
<p>✅ 어떤게 더 좋다고 말할 수는 없지만 보통 함수 선언문을 사용하고 콜백함수를 만들 때는 함수 표현식 중 화살표 함수를 많이 사용한다.</p>
</blockquote>
<h1 id="📌콜백callback-함수">📌콜백(callback) 함수</h1>
<pre><code class="language-js">// 콜백 함수 (표현)식 //@ 나중에 실행 시켜줄게
let callbackFunctionExpression = function(callback) {
// const callback = function() { console.log(&#39;콜백 함수 실행!)} 내부적으로 일어나는 일

  callback();
};

callbackFunctionExpression(
  function() {
    // console.log(&#39;콜백 함수 실행&#39;);
  }
)
&lt;*--------------------------------------------------*&gt;
const movePage = function(url,success,fail){

  if(url.match(/http.+www/) &amp;&amp; typeof url === &#39;string&#39;){ // url의 문자열이고 http.+www 값이 들어가면 실행
     success(url)
  }else{
     fail()
  }
}

movePage(
  &#39;www.naver.com&#39;, // ==&gt; fail
  function(url){
    console.log(&#39;성공 몇초 뒤 해당 페이지로 이동합니다.&#39;);

    setTimeout(() =&gt; {
      window.location.href = url
    }, 3000);

  },
  function(){
    console.log(&#39;올바르지 않은 주소입니다.&#39;);
  }
)</code></pre>
<h1 id="📌화살표-함수">📌화살표 함수</h1>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/24f846d5-c20f-494b-9117-2d574d3af007/image.png" alt=""><code>arrow function</code> 은 <code>arguments</code>를 내장하고 있지 않다!!!
<code>생성자(constructor)</code>를 내장하고 있지 않다. </p>
<p>장점: 다른 함수들 보다 속도가 빠르다. 
단점: constructor를 내장하고 있지 않기 때문에 생성자 함수를 사용할 수 없다 ➡️ 함수로서의 일만 한다.
<img src="https://velog.velcdn.com/images/pearlx_x/post/a50cd03d-e365-4fc2-a3c0-38d51a2053be/image.png" alt=""></p>
<hr>
<h1 id="⭐this⭐">⭐this⭐</h1>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/ca93b9dc-5c8e-467c-bc96-897404e333bf/image.png" alt=""></p>
<p>++++ 함수 표현식은 변수 선언만 호이스팅되고, 실제 함수 할당은 런타임에 이루어지므로, 할당되기 전에 호출할 수 없다. (호이스팅되지 않는게 아님!!!!)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[23.7.7 TIL]]></title>
            <link>https://velog.io/@pearlx_x/23.7.7-TIL</link>
            <guid>https://velog.io/@pearlx_x/23.7.7-TIL</guid>
            <pubDate>Sat, 08 Jul 2023 01:45:32 GMT</pubDate>
            <description><![CDATA[<h1 id="📌nodetype">📌nodeType</h1>
<p><code>text, comment, span</code> 같은 것들을 노드라고 하는데 <code>노드</code>는 가장 작은 단위이다.
노드는 타입이 존재 하는데 그 타입은 다음과 같다.<br><small><span style="color:gray">* html태그를 엘리먼트라고 한다 / 코드의 길이를 줄이기 위해 보통 이름을 사용하기 보단 숫자를 많이 사용한다!</span></small></p>
<blockquote>
<ul>
<li>⭐ <code>ELEMENT_NODE (1)</code>: 
<span style="color:orange">HTML 요소(태그)</span>를 나타내는 노드입니다. 
예를 들어 <code>&lt;div&gt;</code>, <code>&lt;p&gt;</code>, <code>&lt;a&gt;</code> 등의 HTML 요소는 <span style="color:#CCCCFF">ELEMENT_NODE</span>입니다.</li>
</ul>
</blockquote>
<ul>
<li>⭐ <code>ATTRIBUTE_NODE (2)</code>: 
HTML 요소의<span style="color:orange"> 속성</span>을 나타내는 노드입니다. 
예를 들어 class, id, src 등의 속성은 <span style="color:#CCCCFF">ATTRIBUTE_NODE</span>입니다.</li>
<li>⭐ <code>TEXT_NODE (3)</code>: 
HTML 요소의 <span style="color:orange">텍스트 내용</span>을 나타내는 노드입니다. 
예를 들어 <code>&lt;p&gt;Hello, World!&lt;/p&gt;</code>에서 <code>&quot;Hello, World!&quot;</code> 부분은 <span style="color:#CCCCFF">TEXT_NODE</span>입니다.</li>
<li>⭐ <code>COMMENT_NODE (8)</code>: 
HTML <span style="color:orange">주석을</span> 나타내는 노드입니다. 예를 들어 <code>&lt;!-- 주석입니다 --&gt;</code>는 <span style="color:#CCCCFF">COMMENT_NODE</span>입니다.</li>
</ul>
<hr>
<ul>
<li><code>DOCUMENT_NODE (9)</code>: 전체 문서(document)를 나타내는 노드입니다. 즉, 문서 전체를 포함하는 최상위 노드입니다.</li>
<li><code>DOCUMENT_TYPE_NODE (10)</code>: 문서의 타입(DOCTYPE)을 나타내는 노드입니다.</li>
<li><code>DOCUMENT_FRAGMENT_NODE (11)</code>: 문서의 일부분을 나타내는 노드입니다. 예를 들어 <code>&lt;template&gt;</code> 요소의 내용은 DOCUMENT_FRAGMENT_NODE입니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/bf5f3b35-7f0f-4de4-b1ef-ca29962f0f1d/image.png" alt=""></p>
<blockquote>
<p>for문 </p>
</blockquote>
<pre><code class="language-js"> for (let i = l - 1; i &gt;= 0; i--) {
  let value = frontEndDev[i];
  console.log(value);
} 
//^ 6~0 까지 나오길 원함 
for (let i = l; i &gt; 0; ) {
  let value = frontEndDev[--i]; //frontEndDev에서 바로 선감소를 시키면 조건을 간단하게 할 수 있다. 
  console.log(value);
}</code></pre>
<hr>
<h1 id="📌for-in문">📌for in문</h1>
<blockquote>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/6861fa77-2199-4370-a6cf-939b1b450150/image.png" alt=""> for...in 문을 사용하여 javaScript 객체가 가진 값을 조회 하려고 했지만 for in문에는 프로토타입 체이닝에 의해 조상 객체까지 찾아지는 치명적인 단점이 있다.
그래서 자신이 가진 값만 나올 수 있는 <span style="color:orange">hasOwnProperty</span>를 사용하면 된다.<br>
⚠️ for...in은 방문 순서가 중요한 배열보다는 객체에 사용하는 것이 좋다! 
<small><code>for...in</code>은 순서를 보장하지 않습니다. 배열의 인덱스는 순서에 의미를 갖기 때문에, 순서를 보장하지 않는 <code>for...in</code>을 사용하여 배열을 순회하면 예기치 않은 결과를 얻을 수 있습니다. 대신에 <code>for...of</code> 루프나 배열의 <code>forEach</code> 메소드를 사용하여 배열을 순회하는 것이 좋습니다. </small></p>
</blockquote>
<h1 id="📌hasownproperty">📌hasOwnProperty</h1>
<blockquote>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/78ff4258-6b09-4e65-8e04-21264f941167/image.png" alt=""> 하지만 <code>eslint</code>가 경고를 해준다. 그 이유는 <img src="https://velog.velcdn.com/images/pearlx_x/post/b2a8d862-be4b-47ee-b821-200cc3370993/image.png" alt=""> 위와 같이 기존의 기능을 덮어 쓰여질 가능성이있기 때문에 경고 해주는 것이다<br>
✅ 해결방법
<code>call</code>을 사용하여 진짜 객체에게 능력을 빌려야 한다. <code>call</code>이라는 기능은 다른 타입에 있는 기능을 빌리고 싶을 때도 <code>call</code>을 사용하면 능력을 빌릴 수 있다.
<code>Object.prototype.hasOwnProperty.call(javaScript, key);</code>을 사용해야 온전히 자신의 객체만 조회할 수 있다..</p>
</blockquote>
<blockquote>
<p>위의 예시와 같이 기존 기능이 오염될 일이 없는대도 사용해야 하는 이유<img src="https://velog.velcdn.com/images/pearlx_x/post/fc35747a-54a9-4759-b2ba-917a4137795c/image.png" alt="">개발자가 임의로 조상 객체에 새로운 프로퍼티를 추가하여 <span style="color:#FF3939">프로토타입을 오염</span> 시켰을 때에 자신이 만든 객체 javaScript의 <span style="color:yellow">key</span> 값을 조회하면 조상객체에 만든 프로퍼티의 값까지 모두 조회가 되기 때문에 반드시 <code>Object.prototype.hasOwnProperty.call(javaScript, key);</code> 사용해주는 게 좋다.</p>
</blockquote>
<blockquote>
<p>Object.prototype ➡️ {} 나 ({}) 로 대체 가능하다</p>
</blockquote>
<h1 id="📌forof">📌for...of</h1>
<pre><code class="language-js">// iterable(반복이 가능한) = String, Array, Array-like
//! for...of는 이터러블한 아이들만 사용가능
const arryLike = {
  0: &#39;body&#39;,
  1: &#39;head&#39;,
  2: &#39;div&#39;,
  length: 3,
}; //% 배열처럼 인덱스와 길이가 존재하지만 배열은 아님 (완전한 유사배열은 아님)/ 객체는 이터러블하지 않음
</code></pre>
<pre><code class="language-js">for (let value of languages) {
  const name = value.name;

  // if (name === &#39;Java&#39;) break;
  if (name.includes(&#39;Java&#39;) &amp;&amp; name.length &lt; 5) break; //! JavaScript도 Java를 포함하고 있기 때문에 출력되지 않는다.

  console.log(name);
}</code></pre>
<h1 id="📌함수">📌함수</h1>
<pre><code class="language-js">function plus(a, b){ // a, b 는 매개변수(parameter), 인자
    return a + b;
}
let result = plus(1, 2); // 1, 2 는 인수(argument)</code></pre>
<h2 id="함수-이름짓기">함수 이름짓기</h2>
<p><code>함수</code>는 어떤 동작을 수행하기 위한 코드를 모아놓은 것이기 때문에 <code>함수</code>의 이름은 대개 <span style="color:skyblue">동사</span>이다.</p>
<blockquote>
<ul>
<li>간결하고 명확해야 한다.</li>
</ul>
</blockquote>
<ul>
<li>함수가 어떤 동작을 하는지 설명할 수 있어야 한다.</li>
<li>코드를 읽는 사람이 함수 이름만 보고도 함수가 어떤 기능을 하는지 힌트를 얻을 수 있어야 한다.</li>
</ul>
<blockquote>
<p><span style="color:#CCCCFF"><code>get…</code></span> :  값을 반환함
<span style="color:#CCCCFF"><code>calc…</code></span> : 무언가를 계산함
<span style="color:#CCCCFF"><code>create…</code></span> :   무언가를 생성함
<span style="color:#CCCCFF"><code>check…</code></span> :무언가를 확인하고 불린값을 반환함</p>
</blockquote>
<hr>
<h2 id="⭐❌💯-함수는-동작-하나만-담당해야-한다">⭐❌💯 함수는 동작 하나만 담당해야 한다.</h2>
<p>독립적인 두 개의 동작은 독립된 함수 두 개에서 나눠서 수행할 수 있게 해야 합니다. 한 장소에서 두 동작을 동시에 필요로 하는 경우라도 말이죠(이 경우는 제3의 함수를 만들어 그곳에서 두 함수를 호출합니다).</p>
<blockquote>
<p><span style="font-size: 40px">😥개발자들이 빈번히 하는 실수</span> 
getAge 함수는 나이를 얻어오는 동작만 수행 해야한 다.
<small><span style="color:gray">alert 창에 나이를 출력해 주는 동작은 이 함수에 들어가지 않는 것이 좋다. </span></small>
createForm 함수는 form을 만들고 이를 반환하는 동작만 해야 한다.
<small><span style="color:gray">form을 문서에 추가하는 동작이 해당 함수에 들어가 있으면 좋지 않습니다. </span></small></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[23.7.6 TIL]]></title>
            <link>https://velog.io/@pearlx_x/23.7.6-TIL</link>
            <guid>https://velog.io/@pearlx_x/23.7.6-TIL</guid>
            <pubDate>Thu, 06 Jul 2023 07:41:36 GMT</pubDate>
            <description><![CDATA[<h1 id="📌조건문">📌조건문</h1>
<table>
<thead>
<tr>
<th>문(Statement)</th>
<th>식(Expression)</th>
</tr>
</thead>
<tbody><tr>
<td>실행 가능한 동작을 수행하고 값을 반환하지 않는다.</td>
<td>값을 평가하고 반환한다.</td>
</tr>
<tr>
<td><small><span style="color:gray">문은 값을 뱉지 않지만 식은값을 내뱉는다🤤 </span></small></td>
<td></td>
</tr>
<tr>
<td># 📌논리연산자</td>
<td></td>
</tr>
</tbody></table>
<blockquote>
<p><code>단락 평가</code>
OR||은 왼쪽부터 시작해서 오른쪽으로 평가를 진행하는데, <span style="color:skyblue">truthy</span>를 만나면 나머지 값들은 건드리지 않은 채 평가를 멈춘다. 이런 프로세스를 <code>단락 평가</code>라고 한다.</p>
</blockquote>
<p> <code>단락 평가</code>의 동작 방식은 두 번째 피연산자가 변수 할당과 같은 부수적인 효과(side effect)를 가지는 표현식 일 때 명확히 볼 수 있다.</p>
<p><code>단락 평가</code>는 연산자 왼쪽 조건이 <span style="color:pink">falsy</span>일 때만 명령어를 실행하고자 할 때 자주 사용한다.</p>
<hr>
<p><code>||</code> - 첫 번째 true를 찾는다.
<code>&amp;&amp;</code> - 첫 번째 false를  찾는다.</p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/dae66293-925c-495e-ad47-79c3726317c5/image.png" alt="">1. 식을 평가하기 위해 <code>alert(1)</code>이 먼저 실행이 된다.
2.  alert 메서드는 값을 반환하지 않는다. ➡️ <code>undefined</code>가 반환된다.
3. 첫 번째 식이 false이기 때문에 두 번째 식으로 넘어간다.
4. <code>2</code>는 truthy한 값이기 때문에 값이 반환된다.
5. alert 창에 2가 출력된다.
<small><span style="color:gray"> 결과: <code>1,2</code>가 차례대로 출력된다.</span></small>
∴ alert 메서드는 값을 반환하지 않는다. <small>즉, <code>undefined</code>를 반환</small></p>
</blockquote>
<blockquote>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/9a0dc26e-7356-4b8f-8747-912dbf0528ca/image.png" alt=""></p>
</blockquote>
<ol>
<li>식을 평가하기 위해<code>alert(1)</code>이 먼저 실행이 된다.</li>
<li><code>undefined</code>가 반환된다.</li>
<li>alert 창에  <code>undefined</code>가 출력된다.</li>
</ol>
<blockquote>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/d7cdbb43-ef06-4bdf-b63c-a0ba5422dc00/image.png" alt="">함수를 값으로 인식하기 때문에 <span style="color:#FF3939">true</span>
<img src="https://velog.velcdn.com/images/pearlx_x/post/add616bd-dd23-4711-9e85-ee1e0db9bc40/image.png" alt="">하지만 함수의 리턴 값이 0이면 <span style="color:#FF3939">false</span></p>
</blockquote>
<h1 id="📌switch문">📌switch문</h1>
<p>자료형이 중요함!!</p>
<pre><code class="language-js">let arg = prompt(&quot;값을 입력해주세요.&quot;);
switch (arg) {
  case &#39;0&#39;:
  case &#39;1&#39;:
    alert( &#39;0이나 1을 입력하셨습니다.&#39; );
    break;

  case &#39;2&#39;:
    alert( &#39;2를 입력하셨습니다.&#39; );
    break;

  case 3:
    alert( &#39;이 코드는 절대 실행되지 않습니다!&#39; );
    break;
  default:
    alert( &#39;알 수 없는 값을 입력하셨습니다.&#39; );
}</code></pre>
<p>3을 입력하더라도 입력 받은 값은 문자열이기 때문에
디폴트 값이 출력된다.</p>
<h1 id="📌nullish-병합-연산자-">📌nullish 병합 연산자 &#39;??&#39;</h1>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/30f7d3b3-75af-4bf8-bfec-283a3bcdfe17/image.png" alt="">
위에서 배운 <code>||</code> 와 결과가 동일하다</p>
<h2 id="와-의-차이">&#39;??&#39;와 &#39;||&#39;의 차이</h2>
<p><code>||</code>는 첫 번째 <span style="color:pink">truthy </span>값을 반환한다
<code>??</code>는 첫 번째 <span style="color:yellow">정의된(defined)</span> 값을 반환한다.</p>
<pre><code class="language-js">let height = 0;

alert(height || 100); // 100
alert(height ?? 100); // 0</code></pre>
<p>⚠️ 0이 할당될 수 있는 변수를 사용해 기능을 개발할 땐 <code>||</code>보다 ??가 적합하다</p>
<blockquote>
<p>⭐ 정리
<code>T ||</code>: 먼저 나오는 truthy를 찾는다, 아니면 끝을 반환한다
<code>F &amp;&amp;</code> : 먼저 나오는 falsy를 찾는다, 아니면 끝을 반환한다
<code>정 ??</code>: 먼저 나오는 정의한 값을 찾는다, 아니면 끝을 반환한다</p>
</blockquote>
<h1 id="📌loop">📌loop</h1>
<h2 id="while">while</h2>
<p> 성능 진단 : 순환 vs. 역순환
⭐역방향이 성능상에 더 좋다!!⭐</p>
<hr>
<h1 id="📌야무쌤-강의---dom-api--문서-객체들에-접근하는-방법">📌야무쌤 강의 - DOM API  문서 객체(들)에 접근하는 방법</h1>
<pre><code class="language-js">// -------------------------------------------------------------
// DOM API
// - 문서 객체(Document Objects)를 선택하는 방법
// - 요소노드(ELEMENT_NODE) vs 노드리스트(NodeList) | HTMLCollection(유물)
// -------------------------------------------------------------

// tagName 으로 선택하는 방법
// 문서에서 tagName 값이 [    ]인 요소들을 찾아라.
// 태그이름이 피규어인 요소들을 가려와라
var figures = document.getElementsByTagName(&quot;figure&quot;);
console.log(figures); // HTMLCollection 집합객체

// id 속성 값으로 선택하는 방법
var boy = document.getElementById(&quot;boy&quot;);
boy.style.transform = &#39;perspective(800px) rotateY(56deg)&#39;;

var boy = &#39;야망 ~&#39;;
// console.log(boy);

/* 
// class 속성 값으로 선택하는 방법
var paragraphs = document.getElementsByTagName(&#39;p&#39;);
var kbds = paragraphs.getElementsByTagName(&#39;kbd&#39;);
=
=
==&gt; paragraphs는 복수형이기 때문에 getElementsByTagName 형태로 사용할 수 없다.
    paragraphs.item(0).getElementsByTagName(&#39;kbd&#39;)하면 접근 가능

console.log(kbds);
 */

var paragraphs = document.getElementsByTagName(&#39;p&#39;);
var kbds = paragraphs.item(0).getElementsByTagName(&#39;kbd&#39;);
kbds.item(0).style.color = &#39;blueviolet&#39;; // or kbds[0]
// class 속성 값으로 선택하는 방법
var clouds = document.getElementsByClassName(&#39;cloud&#39;);
var kbd = document.getElementsByClassName(&#39;info&#39;)[0].getElementsByTagName(&#39;kbd&#39;)[0];

// CSS 선택자(selector)로 선택하는 방법 (중요)
var boy = document.querySelector(&#39;boy&#39;);// 단수 ELEMENT_NODE
var clouds = document.querySelectorAll(&#39;.cloud&#39;);// 복수 NodeList [] 인덱스 값을 사용해서 하나의 값을 찾을 수 있음
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[23.7.4 - 23.7.5 TIL]]></title>
            <link>https://velog.io/@pearlx_x/23.7.4-TIL</link>
            <guid>https://velog.io/@pearlx_x/23.7.4-TIL</guid>
            <pubDate>Wed, 05 Jul 2023 07:00:59 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-2374">📌 23.7.4</h1>
<pre><code class="language-js">function solution(k, score) {
  let answer = [];
  let rank = [];

  for (let i = 0; i &lt;score.length; i++) {
    if (rank.length &lt; k) {
      rank.push(score[i]);
    } else if (rank[rank.length - 1] &lt; score[i]) {
      rank.pop();
      rank.push(score[i]);
    } else {
      answer.push(rank[rank.length - 1]);
      continue;
    }
    rank.sort((a, b) =&gt; b - a);
    answer.push(rank[rank.length - 1]);
  }
  return answer;
}</code></pre>
<p>어제 푼 코드를 나름 최적화..? 해봤다. 더 줄일 수 있을거 같은데 생각이 잘 안난다...</p>
<hr>
<h2 id="⭐-변수명-짓기">⭐ 변수명 짓기</h2>
<pre><code class="language-js">// - 갯수 별 상품 가격 계산하기
let calcProductPriceQuantity;

// - 구매 제품 가격의 총 합
let totalProductPrice;

// - 1년 기준 일(day)자 수
// 윤년까지 생각했을 땐 let
const DAYS_PER_YEAR = 365;

// - 구매 결제 여부 (했어 안했어 / 있어 없어) 
// 여부를 물어볼 때 사용하는 네이밍 is/has
let isPayment;
let hasClassName;

// - 구매 결제 내역
let paymentHistory;

// - 브랜드 접두사
const BRAND_PREFIX = &#39;NIKE&#39;;

// - 오늘의 운세
let todayFortune;

// - 상품 정보
const productInfomation = &#39;product&#39;;</code></pre>
<p>변수명은 누구라도 알아볼 수 있도록 정의한다.
줄임말은 되도록 사용하지 않는다.</p>
<hr>
<h2 id="⭐-execution-context">⭐ Execution Context</h2>
<p>자바스크립트를 실행하면 자동으로 글로벌 실행환경 생성된다.
렉시컬 Environment(어휘적 환경) - 말로 설명할 수 있는 실행환경
environment Record - 렉시컬 Environment에 들어온 모든 정보를 저장한다.</p>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/1086164e-015d-432c-b9aa-a3494fba05fa/image.png" alt=""></p>
<hr>
<h2 id="⭐-오래된-var">⭐ 오래된 var</h2>
<p>scope 
블록 스코프 - var는 블록 스코프가 없지만 let과 const는 있다.
함수 스코프 - var도 함수 스코프에서는 영향을 받는다.</p>
<p>let은 함수 실행이 시작될 때 처리되지만(호이스팅) 할당은 호이스팅 되지 않기 때문에 에러가 발생한다.
var는 호이스팅 되자마자 undefined를 할당 받기때문에 선언하기 전에 호출되더라도 에러는 발생하지 않는다.</p>
<hr>
<h2 id="⭐-데이터-타입">⭐ 데이터 타입</h2>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/79e285f6-f66a-43f0-a801-36318f55ac81/image.png" alt=""></p>
<p>🤔 생성자로 생성하면 타입이 object로 나오는데 리터럴로 만드는 경우와 쓰임에 차이점?
✅아무런 차이가 없지만 함수로 가면 엄청난 차이가 있다.</p>
<hr>
<h1 id="2375">23.7.5</h1>
<h2 id="⭐-형변환">⭐ 형변환</h2>
<p><code>null</code>은 값이 비어있는 상태 
<code>undefined</code>는 값이 없는 상태
숫자 이외의 글자가 들어가 있는 문자열을 숫자형으로 변환하려고 하면 <code>NaN</code>이 나온다.</p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/dbcbae04-9537-444f-b666-85fd60f93a0f/image.png" alt=""> 숫자형의 암시적형 변환</p>
</blockquote>
<hr>
<blockquote>
<p><img src="https://velog.velcdn.com/images/pearlx_x/post/514f147e-fbe9-49c4-aa8e-ee12ac7aa10b/image.png" alt=""> Boolean의 암식적 형변환</p>
</blockquote>
<h2 id="⭐-연산">⭐ 연산</h2>
<p>🌟 자바스크립트에서 대부분의 연산자들은 값을 반환한다.
<code>spread syntax</code> =&gt; ...배열이름
JavaScript에서 배열이나 객체를 확장하거나 병합하는 데 사용되는 문법이다. 
주로 배열이나 객체의 요소를 분해하여 다른 배열이나 객체에 펼치는 역할을 한다.</p>
<hr>
<h2 id="야무쌤-강의">야무쌤 강의</h2>
]]></description>
        </item>
    </channel>
</rss>