<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>SH's_Devlog ☁️</title>
        <link>https://velog.io/</link>
        <description>개발 공부 중 〰️ ٩(๑•̀o•́๑)و ✨</description>
        <lastBuildDate>Mon, 20 Mar 2023 11:39:58 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>SH's_Devlog ☁️</title>
            <url>https://velog.velcdn.com/images/hi_sulhwa/profile/078bce1e-c413-4072-8d6a-9657d13beaee/image.JPG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. SH's_Devlog ☁️. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hi_sulhwa" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[백준]]></title>
            <link>https://velog.io/@hi_sulhwa/%EB%B0%B1%EC%A4%80-d0u5ld33</link>
            <guid>https://velog.io/@hi_sulhwa/%EB%B0%B1%EC%A4%80-d0u5ld33</guid>
            <pubDate>Mon, 20 Mar 2023 11:39:58 GMT</pubDate>
            <description><![CDATA[<p>2738 행렬 덧셈</p>
<pre><code class="language-javascript">let input = require(&#39;fs&#39;).readFileSync(&#39;/dev/stdin&#39;).toString().trim().split(&#39;\n&#39;);

const [row,col]=input[0].split(&#39; &#39;).map(Number);

for (let i=1; i&lt;=row; i++){
    let rows = input[i].split(&#39; &#39;).map(Number);
    let otherRows = input[i+row].split(&#39; &#39;).map(Number);

    let answer= [];
    for (let j=0; j&lt;col; j++){
        answer.push(rows[j]+otherRows[j]);
    }
    console.log(answer.join(&#39; &#39;));
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준]]></title>
            <link>https://velog.io/@hi_sulhwa/%EB%B0%B1%EC%A4%80</link>
            <guid>https://velog.io/@hi_sulhwa/%EB%B0%B1%EC%A4%80</guid>
            <pubDate>Mon, 20 Mar 2023 06:58:23 GMT</pubDate>
            <description><![CDATA[<p>3052</p>
<p>집합은 중복되지 않은 원소 구할때 적합하다</p>
<pre><code class="language-javascript">let input=require(&#39;fs&#39;).readFileSync(&#39;/dev/stdin&#39;).toString().split(&#39;\n&#39;).map(x=&gt;Number(x));

let mySet = new Set(); //집합객체생성

for(let i=0; i&lt;10; i++){
    mySet.add(input[i]%42);
}
console.log(mySet.size);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 이상한 문자만들기]]></title>
            <link>https://velog.io/@hi_sulhwa/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%9D%B4%EC%83%81%ED%95%9C-%EB%AC%B8%EC%9E%90%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@hi_sulhwa/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%9D%B4%EC%83%81%ED%95%9C-%EB%AC%B8%EC%9E%90%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Fri, 10 Feb 2023 10:44:44 GMT</pubDate>
            <description><![CDATA[<p>문제 설명
문자열 s는 한 개 이상의 단어로 구성되어 있습니다. 각 단어는 하나 이상의 공백문자로 구분되어 있습니다. 각 단어의 짝수번째 알파벳은 대문자로, 홀수번째 알파벳은 소문자로 바꾼 문자열을 리턴하는 함수, solution을 완성하세요.</p>
<p>제한 사항
문자열 전체의 짝/홀수 인덱스가 아니라, 단어(공백을 기준)별로 짝/홀수 인덱스를 판단해야합니다.
첫 번째 글자는 0번째 인덱스로 보아 짝수번째 알파벳으로 처리해야 합니다.
입출력 예
s    return
&quot;try hello world&quot;    &quot;TrY HeLlO WoRlD&quot;
입출력 예 설명
&quot;try hello world&quot;는 세 단어 &quot;try&quot;, &quot;hello&quot;, &quot;world&quot;로 구성되어 있습니다. 각 단어의 짝수번째 문자를 대문자로, 홀수번째 문자를 소문자로 바꾸면 &quot;TrY&quot;, &quot;HeLlO&quot;, &quot;WoRlD&quot;입니다. 따라서 &quot;TrY HeLlO WoRlD&quot; 를 리턴합니다.</p>
<hr>
<p>✏️문제풀이</p>
<pre><code class="language-javascript">function solution(s) {
    let word = s.split(&#39; &#39;);

    let answer=word.map(a=&gt;{
        word=a.split(&#39;&#39;);
        return word.map((w,idx)=&gt;{
            if(idx%2){
                return w.toLowerCase();
            }else{
                return w.toUpperCase();
            }
        }).join(&#39;&#39;);
    }).join(&#39; &#39;);

    return answer;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트 회고 - 또로그 ]]></title>
            <link>https://velog.io/@hi_sulhwa/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0-%EB%98%90%EB%A1%9C%EA%B7%B8</link>
            <guid>https://velog.io/@hi_sulhwa/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0-%EB%98%90%EB%A1%9C%EA%B7%B8</guid>
            <pubDate>Mon, 06 Feb 2023 17:26:42 GMT</pubDate>
            <description><![CDATA[<h3 id="🏡-또로그">🏡 또로그</h3>
<ul>
<li>또로그는 소소한 일상들, 기억하고 기록하고 싶은 것들을 공유하고, 이웃들과 소통할 수 있는 블로그입니다.☕︎︎‎𓂃 𓈒𓏸 </li>
</ul>
<h3 id="⏰-개발-기간">⏰ 개발 기간</h3>
<p>﹣ 2023.01.03~2023.02.06 (기획+구현)</p>
<h3 id="🌎-배포">🌎 배포</h3>
<ul>
<li>(프론트) Netlify를 이용하여 배포하였습니다.
또로그 링크입니닷 ጾጽ  ⁀➷♥ <a href="https://ttolog.netlify.app/">https://ttolog.netlify.app/</a></li>
</ul>
<h3 id="⚒️-기술-스택">⚒️ 기술 스택</h3>
<p>[Front-end] - Typescript, React, JavaScript, Router, Sass, Figma(디자인)
[Back-end] - Node.js, Express, Mysql, Typescript
[Communication] - Trello, Discord</p>
<h3 id="🙋🏻♀️-내가-담당한-부분">🙋🏻‍♀️ 내가 담당한 부분</h3>
<h3 id="메인페이지">메인페이지</h3>
<ol>
<li>잔디 기능 </li>
<li>날씨 기능</li>
<li>전체 글 리스트 (페이지네이션)</li>
<li>이웃 새글 리스트</li>
<li>주제 드롭다운 카테고리</li>
</ol>
<h3 id="팔로우">팔로우</h3>
<ol start="6">
<li>팔로우, 팔로잉 기능</li>
</ol>
<br>
<hr>


<h2 id="메인페이지-1">메인페이지</h2>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/38b05772-1e9d-41e2-ad28-bebc4fb1dc1b/image.gif" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/9a79d03a-d6b2-4ca8-aa79-2ba7bf15e3ab/image.png" alt=""></p>
<blockquote>
<p> swiper를 이용하여 잔디와 날씨기능을 캐러셀로 구현하였고, 로그인 하지 않았을 시에는 날씨부분만 보여지도록 구현하였습니다.</p>
</blockquote>
<hr>


<h3 id="1-잔디-기능">(1) 잔디 기능</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/6f436b16-ecc4-4b9f-ba0d-ac616cc5f948/image.png" alt=""></p>
<blockquote>
<p>메인페이지의 잔디는 현재 년도의 1월 1일부터 12월 31일까지를 보여주고, 해당 날짜에 블로그 포스팅을 했을 때 잔디에 색이 들어가도록 하였습니다.</p>
</blockquote>
<pre><code class="language-javascript">  const now = new Date(); //현재 날짜
  const year = now.getFullYear(); //현재 년도
  const start = new Date(year + &#39;-01-01&#39;); //시작일은 현재 년도의 1월 1일
  const timeDiff = now.getTime() - start.getTime(); 
  const day = Math.floor(timeDiff / (1000 * 60 * 60 * 24) + 1); ////1월1일부터 오늘날짜까지의 경과일계산

const [postingData, setPostingData] = useState&lt;postDataInterface[]&gt;([]);
////유저의 데이터에 1월1일과 12월 31일 데이터가 있는지 확인  
const dayCheck1 = postingData.some(
    (data) =&gt; data.date === year + &#39;-01-01&#39; &amp;&amp; year + &#39;-12-31&#39;
  ); 
  const dayCheck2 = postingData.some((data) =&gt; data.date === year + &#39;-01-01&#39;);
  const dayCheck3 = postingData.some((data) =&gt; data.date === year + &#39;-12-31&#39;);

  const firstDate = {
    date: &#39;2023-01-01&#39;,
    count: 0,
    level: 0,
  };

  const lastDate = {
    date: &#39;2023-12-31&#39;,
    count: 0,
    level: 0,
  };

//1년 범위로 보여주기 위한 데이터 추가
  if (dayCheck1 === false) {
    postingData.unshift(firstDate);
    postingData.push(lastDate);
  } else if (dayCheck2 === false) {
    postingData.unshift(firstDate);
  } else if (dayCheck3 === false) {
    postingData.push(lastDate);
  }

</code></pre>
<blockquote>
<p>1년단위의 잔디를 보여주기 위해 1월1일과 12월 31일의 범위를 지정해주었고, 유저가 블로그를 시작한 경과일을 보여주었습니다.  </p>
</blockquote>
<h3 id="2날씨-기능">(2)날씨 기능</h3>
<blockquote>
<p>새벽
<img src="https://velog.velcdn.com/images/hi_sulhwa/post/d5d34c3c-3d7d-4db0-bfe7-7880cb603056/image.png" alt="">
아침
<img src="https://velog.velcdn.com/images/hi_sulhwa/post/891914f1-f2a1-47f6-b050-c2671d84e506/image.png" alt="">
낮
<img src="https://velog.velcdn.com/images/hi_sulhwa/post/53d6ee1c-e39d-4f83-95a5-4d61210f5ebb/image.png" alt="">
노을 지는 오후
<img src="https://velog.velcdn.com/images/hi_sulhwa/post/5b1df6a0-70ab-4bf3-addc-77ec72b21b78/image.png" alt="">
밤
<img src="https://velog.velcdn.com/images/hi_sulhwa/post/e210b1cf-6ac5-4991-80cd-7288dcaf79f9/image.png" alt=""></p>
</blockquote>
<pre><code class="language-javascript">
//현재 시간에 맞추어 (새벽,아침,낮,노을,밤) 으로 저장
useEffect(() =&gt; {
    const date = new Date();
    const hour = date.getHours();
    setCurrentHour(hour);

    if (hour &gt;= 4 &amp;&amp; hour &lt;= 6) {
      setTime(&#39;dawn&#39;);
    } else if (hour &gt;= 7 &amp;&amp; hour &lt;= 12) {
      setTime(&#39;morning&#39;);
    } else if (hour &gt;= 13 &amp;&amp; hour &lt;= 15) {
      setTime(&#39;afternoon&#39;);
    } else if (hour &gt;= 16 &amp;&amp; hour &lt;= 17) {
      setTime(&#39;dusk&#39;);
    } else {
      setTime(&#39;night&#39;);
    }
    getWeather();
  }, [currentHour]);

//현재 위치정보 이용 동의 여부와 , 현재 위치의 현재 날씨 정보를 가져옴
  const getWeather = () =&gt; {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition((position) =&gt; {
        const lat = position.coords.latitude;
        const lon = position.coords.longitude;
        const url = `https://api.openweathermap.org/data/2.5/weather?&amp;lat=${lat}&amp;lon=${lon}&amp;appid=${process.env.REACT_APP_WEATHER_API_KEY}&amp;units=metric`;

        const fetchData = async () =&gt; {
          try {
            const weatherResponse = await fetch(url);
            const weather = await weatherResponse.json();
            setWeatherData(weather.weather[0].main);
            //현재 지역
            setLocation(weather.name);
            //현재 위치 날씨
            setWeatherId(weather.weather[0].id);
            //현재 온도
            const temp = Math.floor(weather.main.temp) + &#39;°C&#39;;
            setTemperature(temp);
            //위치정보 동의
            setAgree(true);
          } catch (error) {
            console.error(&#39;error&#39;);
          }
        };
        fetchData();
      });
    } else {
      setAgree(false);
    }
  };

//현재날씨 한글로 변역
  useEffect(() =&gt; {
    const translateData = async () =&gt; {
      try {
        const weatherTranslateResponse = await fetch(&#39;./data/weather.json&#39;);
        const weatherTranslate = await weatherTranslateResponse.json();
        setTranslateData(weatherTranslate.data);
      } catch (error) {
        console.error(&#39;error&#39;);
      }
    };
    translateData();
  }, []);

{translateData.map((data) =&gt; {
              const { id, content } = data;
              if (id === weatherId) {
                return (
                  &lt;span key={id} className={css.currentWeather}&gt;
                    {content}
                  &lt;/span&gt;
                );
              }
            })}

</code></pre>
<p>☀️ 날씨 api는 openWeatherApi를 사용하였습니다.</p>
<blockquote>
<p>날씨기능은 현재 날씨 api와 3시간 간격의 날씨 api 두가지는 가져와 fetch 해주었습니다.
현재 시간에 맞추어 배경색상을 변경해주었고, 아이콘 또한 낮과 밤 2가지 버전으로 넣어주어 확인하기 편하도록 구현하였습니다. 현재 날씨를 한글로 보여주고 싶어 번역파일을 목데이터로 만들어 번역해주었습니다.</p>
</blockquote>
<h3 id="2-1-날씨-배경-애니메이션">(2)-1 날씨 배경 애니메이션</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/c95a18cb-d541-420b-8325-8e03bc15079c/image.gif" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/2fb4ea05-7af0-49aa-b8ea-00d3687fae3a/image.gif" alt=""></p>
<blockquote>
<p>눈과 비가 내리는 모습을 위해 조건을 true로 하고 영상을 찍어보았습니다. 이 애니메이션들은 현재 날씨에 맞추어 화면에 나타납니다.</p>
</blockquote>
<pre><code class="language-javascript">//비내리는 애니메이션 코드
const Rainy = () =&gt; {
  const RainDrop = ({ style }: any) =&gt; {
    return (
      &lt;p className={css.rainDrop} style={style}&gt;
        💧
      &lt;/p&gt;
    );
  };

  const makeRainDrop = () =&gt; {
    let animationDelay = &#39;0s&#39;;
    let fontSize = &#39;5px&#39;;
    const arr = Array.from({ length: 50 }, (v, i) =&gt; i);

    return arr.map((el, i) =&gt; {
      animationDelay = `${-Math.random() * 10}s`; //랜덤 delay
      fontSize = `${Math.floor(Math.random() * 20)}px`; //랜덤 물방울 크기

      const style = {
        animationDelay,
        fontSize,
      };
      return &lt;RainDrop key={i} style={style} /&gt;;
    });
  };

  return &lt;div className={css.rainContainer}&gt;{makeRainDrop()}&lt;/div&gt;;
};
</code></pre>
<h3 id="3전체-글-리스트-페이지네이션">(3)전체 글 리스트 (페이지네이션)</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/a2df8564-0264-448b-9a0c-dd2b1c27af6c/image.gif" alt=""></p>
<pre><code class="language-javascript">//타입지정
interface AllPostInterface {
  id: string;
  title: string;
  content: string;
  thumbnailImgUrl: string;
  createdAt: string;
  user: {
    id: number;
    nickname: string;
    profileImgUrl: string;
  };
}
//주제 드롭다운 인터페이스
export interface TopicDataInterface {
  setTopicIdData: Dispatch&lt;SetStateAction&lt;number&gt;&gt;;
  setPagination: Dispatch&lt;SetStateAction&lt;number&gt;&gt;;
}

  //전체글 데이터
 //주제 아이디
  const [topicIdData, setTopicIdData] = useState(0);
  //페이지네이션
  const [pagination, setPagination] = useState(1);
  const [allPostData, setAllPostData] = useState&lt;AllPostInterface[]&gt;([]);
  const [maxCountPage, setMaxCountPage] = useState&lt;number&gt;(1);
  useEffect(() =&gt; {
    const fetchData = async () =&gt; {
      try {
        const response = await fetch(
          `${process.env.REACT_APP_API_URL}/posts?${
            topicIdData !== 0 ? `topicId=${topicIdData}&amp;` : &#39;&#39;
          }countPerPage=8&amp;pageNumber=${pagination}`,
          {
            headers: requestHeaders,
          }
        );
        const json = await response.json();
        setMaxCountPage(json.maxPage);
        setAllPostData(json.data);
      } catch (error) {
        console.error(&#39;error&#39;);
      }
    };
    fetchData();
  }, [pagination, topicIdData]);


  const prevPage = () =&gt; {
    setPagination(pagination - 1);
  };

  const nextPage = () =&gt; {
    setPagination(pagination + 1);
  };

//첫 페이지 일 경우 버튼 안보이도록 
  {pagination === 1 ? null : (
      &lt;FaChevronLeft className={css.chevron} onClick={prevPage} /&gt;
  )}

 //최대 페이지 수 일 경우 버튼 안보이도록
  {pagination === maxCountPage ? null : (
       &lt;FaChevronRight className={css.chevron} onClick={nextPage} /&gt;
  )}
</code></pre>
<blockquote>
<p> 주제가 선택되지 않을 시 모든 주제의 전체글을 보여주고, 주제 선택 시 주제에 해당하는 글들만을 보여줍니다. 화살표 버튼 클릭 시 이전과 다음페이지로 이동을 합니다!</p>
</blockquote>
<h3 id="4이웃-새글-리스트">(4)이웃 새글 리스트</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/6fea1c56-abd3-4f4c-88bf-f85b8e311396/image.png" alt=""></p>
<blockquote>
<p>이웃이 없을 시</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/b928db0a-659d-4349-abad-9ef8cff0f7d3/image.gif" alt=""></p>
<blockquote>
<p>이웃이 있으면 이웃의 글을 보여주고, 주제별로 확인이 가능합니다.</p>
</blockquote>
<pre><code class="language-javascript">// 팔로잉 여부 확인
  const [newPostData, setNewPostData] = useState&lt;NewPostInterface[]&gt;([]);

useEffect(() =&gt; {
    const fetchData = async () =&gt; {
      try {
        const response = await fetch(
          `${process.env.REACT_APP_API_URL}/following?myFollowing=true`,
          {
            headers: requestHeaders,
          }
        );
        const json = await response.json();
        if (json.data.length === 0) {
          setBuddyData(false);
        } else {
          setBuddyData(true);
        }
      } catch (error) {
        console.error(&#39;error&#39;);
      }
    };
    fetchData();
  }, []);

  useEffect(() =&gt; {
    const fetchData = async () =&gt; {
      const isSelected = topicIdData !== 0;
      try {
        const response = await fetch(
          `${process.env.REACT_APP_API_URL}/posts?myFollowing=true${
            isSelected ? `&amp;topicId=${topicIdData}` : &#39;&#39;
          }`,
          {
            headers: requestHeaders,
          }
        );
        const json = await response.json();
        if (json.data.length === 0) {
          setBuddyPostData(false);
        } else {
          setNewPostData(json.data);
          setBuddyPostData(true);
        }
      } catch (error) {
        console.error(&#39;error&#39;);
      }
    };
    fetchData();
  }, [topicIdData]);
</code></pre>
<blockquote>
<p>로그인한 유저가 팔로잉한 유저가 있는지 확인 하였고, 주제를 선택했는지 안했는지에 여부에 따라 fetch를 하였습니다.</p>
</blockquote>
<h3 id="5주제-드롭다운-카테고리">(5)주제 드롭다운 카테고리</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/684f34ac-97cd-40a1-961e-0dd1c5b185c6/image.png" alt=""></p>
<pre><code class="language-javascript">const DropDown = ({ setTopicIdData, setPagination }: TopicDataInterface) =&gt; {
  const token = localStorage.getItem(&#39;token&#39;);

  const requestHeaders: HeadersInit = new Headers();
  requestHeaders.set(&#39;Content-Type&#39;, &#39;application/json&#39;);
  if (token) {
    requestHeaders.set(&#39;Authorization&#39;, token);
  }

  //드롭타운 클릭여부
  const [isOpen, setIsOpen] = useState&lt;boolean&gt;(false);
  const [topic, setTopic] = useState&lt;string&gt;(&#39;주제&#39;);
  const onToggle = () =&gt; setIsOpen(!isOpen);
  const onOptionClicked = (value: string, index: number) =&gt; () =&gt; {
    setIsOpen(false);
    setTopic(value);
    setTopicIdData(index);
    setPagination(1);
  };
  const [topicData, setTopicData] = useState&lt;TopicInterface[]&gt;([]);
  useEffect(() =&gt; {
    const fetchData = async () =&gt; {
      try {
        const response = await fetch(`${process.env.REACT_APP_API_URL}/topics`);
        const json = await response.json();
        let topicList = json.data as TopicInterface[];
        topicList.push({ content: &#39;전체&#39;, id: 0 });
        setTopicData(json.data);
      } catch (error) {
        console.error(&#39;error&#39;);
      }
    };
    fetchData();
  }, []);


  {topicData.map((topic) =&gt; {
                const { id, content } = topic;
                return (
                  &lt;li
                    key={id}
                    className={css.topicItem}
                    onClick={onOptionClicked(content, id)}
                  &gt;
                    {content}
                  &lt;/li&gt;
                );
              })}</code></pre>
<blockquote>
<p>주제 데이터를 fetch를 통해 가져오고 해당 주제를 클릭 하였을 때, value값을 저장해주었습니다.</p>
</blockquote>
<hr>

<h2 id="팔로우-1">팔로우</h2>
<h3 id="6-팔로우-팔로잉-기능">(6) 팔로우, 팔로잉 기능</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/ff36f100-84d2-4afd-a27d-ce53ceff8ceb/image.gif" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/921244d5-2701-45e4-8b88-b0a54b1a5130/image.png" alt=""></p>
<blockquote>
<p>팔로워 클릭 시 팔로워 리스트가 보이고, 팔로잉 클릭 시 팔로잉 리스트가 보입니다.
인원이 많을 시 scroll을 이용하였고, 팔로잉에서 언팔로우 클릭 시 리스트에서 사라지며, 팔로워에서 내가 팔로우 했는지 안했는지 여부를 확인할 수 있고 언팔로우 시 팔로워버튼이 뜨도록 하였습니다.
다른사람의 팔로우에서 내 계정은 버튼이 안보이도록 하였습니다.
유저의 정보를 클릭 시 해당 유저의 블로그로 이동할 수 있습니다.</p>
</blockquote>
<pre><code class="language-javascript">//팔로워와 팔로잉 페이지 띄우는 조건
 const onFollowingBtn = () =&gt; {
    if (selectFollowingBtn === false) {
      setSelectFollowingBtn(true);
      setSelectFollowerBtn(false);
    } else if (selectFollowingBtn === true) {
      setSelectFollowingBtn(true);
    }
  };

  const onFollowerBtn = () =&gt; {
    if (selectFollowerBtn === false) {
      setSelectFollowerBtn(true);
      setSelectFollowingBtn(false);
    } else if (selectFollowerBtn === true) {
      setSelectFollowerBtn(true);
    }
  };


  //팔로우, 언팔로우 버튼
  const follow = (value: string, index: number) =&gt; () =&gt; {
    fetch(`${process.env.REACT_APP_API_URL}/follow`, {
      method: &#39;POST&#39;,
      headers: requestHeaders,
      body: JSON.stringify({
        targetUsersId: index,
      }),
    })
      .then((res) =&gt; res.json())
      .then((result) =&gt; {
        if (result.message === &#39;FOLLOW_SUCCESSFULLY&#39;) {
          fetch(`${process.env.REACT_APP_API_URL}/follower/user/${params.id}`, {
            headers: requestHeaders,
          })
            .then((res) =&gt; res.json())
            .then((res) =&gt; setFollowerData(res.data));
        }
      });
  }; // 팔로우 버튼 클릭하고 다시 데이터 fetch하기

  const unFollow = (value: string, index: number) =&gt; () =&gt; {
    fetch(`${process.env.REACT_APP_API_URL}/follow/${index}`, {
      method: &#39;DELETE&#39;,
      headers: requestHeaders,
    })
      .then((res) =&gt; res.json())
      .then((result) =&gt; {
        if (result.message === &#39;UNFOLLOW_SUCCESSFULLY&#39;) {
          fetch(`${process.env.REACT_APP_API_URL}/follower/user/${params.id}`, {
            headers: requestHeaders,
          })
            .then((res) =&gt; res.json())
            .then((res) =&gt; setFollowerData(res.data));
        }
      });
  }; //언팔로우 버튼 클릭 후 다시 데이터 fetch!</code></pre>
<hr>


<h3 id="👏-프로젝트-마치는-소감">👏 프로젝트 마치는 소감</h3>
<p>﹣기획부터 한 프로젝트여서 디자인 부분 또한 많이 신경을 써서 만들었다(날씨 아이콘, 배경, 전체 레이아웃, 팔로우 레이아웃, 로고 등등).그리고 처음으로 타입스크립트를 공부하며 프로젝트를 해보았는데 아직 익숙치 않아 그런지 조금 더 공부를 해야겠다는 생각이 들었다. 부트캠프 수료 후 만든 첫 프로젝트였는데 함께 프로젝트를 해왔던 팀원들과 작업해서 회의뿐만 아닌 소통 모두 수월하고 재미있게 진행했던 것 같다. 중간에 설 연휴로 인해 1주일 조금 안되는 시간 동안은 작업을 할 수 없었어서 기획부터 프로젝트 완성까지 3주정도 소요 된 것 같다. 3주동안 많이 성장하고, 또 많이 즐거웠던 시간들이었다.! 마지막으로 은또 팀원 모두 원하는 곳에 취뽀 성공하길..! ｡ ♡ ˙˚ ʚ ᕱ⑅ᕱ ɞ˚˙ ♡ ｡</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프리온보딩 회고📝 - 패스트파이브 ]]></title>
            <link>https://velog.io/@hi_sulhwa/%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@hi_sulhwa/%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Fri, 30 Dec 2022 07:01:00 GMT</pubDate>
            <description><![CDATA[<p>🥕 프리온보딩 - 패스트파이브</p>
<ul>
<li>패스트파이브의 기획문서를 기반으로 프로젝트를 진행하였습니다.</li>
</ul>
<hr>

<h3 id="⏰-개발-기간">⏰ 개발 기간</h3>
<p>﹣ 2022.12.12~2022.12.22 (11일)</p>
<h3 id="🙌🏻-협업방식---트렐로">🙌🏻 협업방식 - (트렐로)</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/10b40544-e10b-4ece-8160-c3a1236866b9/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/f209ee61-3956-4faf-a2c0-fc87175a8e1f/image.png" alt=""></p>
<blockquote>
<p>트렐로를 이용해서 진행상황을 공유하였고, 디스코드를 이용하여 오전에 회의 하였습니다!</p>
</blockquote>
<h3 id="⚒️-기술-스택">⚒️ 기술 스택</h3>
<p>[프론트] - React, Javascript, Router, Sass
[백엔드] - Javascript, Express(Node.js), Mysql, dbmate(DB scheme 버전관리), jsonwebtoken(토큰 발행), bcryptjs(비밀번호 암호화),Axios</p>
<h3 id="🙋🏻♀️-내가-담당한-부분">🙋🏻‍♀️ 내가 담당한 부분</h3>
<ol>
<li>회원가입</li>
<li>로그인, 로그아웃</li>
<li>대표/멤버 요청</li>
<li>게시글 리스트페이지 (전체보기 페이지, 카테고리페이지)</li>
</ol>
<hr>


<h4 id="먼저-페이지는-모두-반응형으로-구현하였습니다-회원가입과-로그인-대표멤버요청-페이지는-새롭게-모달을-구현할-수-있는-portal을-알게되어-공부하였고-이를-이용해-구현하였습니다">먼저 페이지는 모두 반응형으로 구현하였습니다. 회원가입과 로그인, 대표/멤버요청 페이지는 새롭게 모달을 구현할 수 있는 portal을 알게되어 공부하였고, 이를 이용해 구현하였습니다.</h4>
<h4 id="또한-미디어-쿼리로-넓이가-줄어-들-시-배경-이미지-사라지도록-하였습니다">또한 미디어 쿼리로 넓이가 줄어 들 시 배경 이미지 사라지도록 하였습니다!</h4>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/d5f91b2d-4036-4bbe-92df-2f631de41ad2/image.gif" alt=""></p>
<h4 id="게시글-리스트-페이지는-grid를-minmax를-사용해-반응형으로-만들었습니다">게시글 리스트 페이지는 grid를 minmax를 사용해 반응형으로 만들었습니다.</h4>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/12b59aa4-8b3a-44fc-8b5f-6805ca9fd5a4/image.gif" alt=""></p>
<hr>

<h3 id="1회원가입">(1)회원가입</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/ac562570-aa29-4b98-ac3f-07dc6208a354/image.gif" alt=""></p>
<pre><code class="language-javascript">
  //이메일 중복확인
  const [duplicateEmail, setDuplicateEmail] = useState(true);
  const duplicateCheck = e =&gt; {
    fetch(`${process.env.REACT_APP_API_URI}/email`, {
      method: &#39;POST&#39;,
      headers: {
        &#39;Content-Type&#39;: &#39;application/json&#39;,
      },
      body: JSON.stringify({
        email: email,
      }),
    })
      .then(res =&gt; res.json())
      .then(res =&gt; {
        if (res.duplicateEmail === false) {
          alert(&#39;사용 가능한 이메일 입니다.&#39;);
          setDuplicateEmail(false);
        } else {
          alert(&#39;이미 존재하는 이메일 입니다.&#39;);
        }
      });
  };

  const emailCheck = e =&gt; {
    const emailRegex =
      /^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/i;
    if (emailRegex.test(e.target.value)) {
      setEmailError(false);
      setEmail(e.target.value);
    } else {
      setEmailError(true);
    }
  };

  const passwordCheck = e =&gt; {
    const passwordRegex =
      /^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{8,16}$/;
    if (passwordRegex.test(e.target.value)) {
      setPasswordError(false);
      setPassword(e.target.value);
    } else {
      setPasswordError(true);
    }
  };</code></pre>
<blockquote>
<p>회원가입은 이메일 유효성 검사 및 비밀번호 유효성 검사와 모든 항목을 다 작성해야만 회원가입을 진행할 수 있도록 구현하였다. 이메일과 비밀번호는 실시간으로 인풋창 아래에서 알림이 뜨도록 만들었으며, 모든 조건을 충족했을 시 알림이 사라지도록 구현하였다. 이메일 중복확인은 버튼 클릭 시 fetch를 이용해 백엔드에서 중복되는 이메일 여부를 확인하였다. 중복이면 다시 이메일을 작성해야하고, 사용가능하면 버튼이 비활성화 되도록 구현하였다.</p>
</blockquote>
<h3 id="2로그인-로그아웃">(2)로그인, 로그아웃</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/e27c1d90-62d3-45c0-9696-ba261591f847/image.gif" alt=""></p>
<pre><code class="language-javascript">
 const onLogin = () =&gt; {
    fetch(`${process.env.REACT_APP_API_URI}/login`, {
      method: &#39;POST&#39;,
      headers: {
        &#39;Content-Type&#39;: &#39;application/json&#39;,
      },
      body: JSON.stringify({
        email: email,
        password: password,
      }),
    })
      .then(res =&gt; {
        if (res.status === 200) {
          return res.json();
        } else {
          return null;
        }
      })
      .then(res =&gt; {
        if (res !== null) {
          localStorage.setItem(&#39;token&#39;, res.token);
          alert(&#39;로그인에 성공하였습니다!&#39;);
          window.location.href = &#39;/&#39;;
        } else {
          alert(&#39;로그인에 실패하였습니다.&#39;);
        }
      });
  };

  const onLogout = () =&gt; {
    window.localStorage.setItem(&#39;token&#39;, &#39;&#39;);
    window.location.href = &#39;/&#39;;
    setUserName(&#39;&#39;);
  };</code></pre>
<blockquote>
<p>이메일 양식 검사를 실시간으로 하였고, 비밀번호 입력 후, fetch를 이용해 로그인 성공 시 성공하였다고 알림창이 뜨고, 아닐 시 실패하였다고 알림창이 뜨게 구현하였다. 로그아웃은 토큰 값을 없애주고, 메인화면으로 돌아가도록 하였다.</p>
</blockquote>
<h3 id="3대표멤버-요청">(3)대표/멤버 요청</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/e27c1d90-62d3-45c0-9696-ba261591f847/image.gif" alt=""></p>
<blockquote>
<p>🥕 대표요청</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/7250e52a-28ea-4a98-bd4b-25093630eed0/image.gif" alt=""></p>
<blockquote>
<p>🥕 멤버요청</p>
</blockquote>
<pre><code class="language-javascript">
//대표요청
  useEffect(() =&gt; {
    if (companyCheck) {
      setValidation(true);
    }
  }, [companyCheck]);

  const requestCheck = e =&gt; {
    alert(&#39;회사 이름을 작성해주세요&#39;);
  };

//캘린더
 const [startDate, setStartDate] = useState(new Date());
  const [endDate, setEndDate] = useState(new Date());

  const startDateFormat = moment(startDate, &#39;YYYY.MM.DD&#39;).format(&#39;YYYY-MM-DD&#39;);
  const endDateFormat = moment(endDate, &#39;YYYY.MM.DD&#39;).format(&#39;YYYY-MM-DD&#39;);

  useEffect(() =&gt; {
    setStartDateData(startDateFormat);
    setEndDateData(endDateFormat);
  }, [startDate, endDate]);

//멤버요청
 useEffect(() =&gt; {
    fetch(`${process.env.REACT_APP_API_URI}/company`)
      .then(res =&gt; res.json())
      .then(data =&gt; {
        setCompanyData(data);
      });
  }, []);

  const onCompanyData = e =&gt; {
    setUserCompany(e.target.value);
  };</code></pre>
<blockquote>
<p>대표요청에서는 캘린더(DatePicker)를 이용하여 이용기간을 설정할 수 있도록 하였으며 만료날짜는 시작 기한 이전은 선택할 수 없도록 만들었고, moment를 이용하여 원하는 포맷으로 변환시켜주었다. 대표는 자신의 회사를 요청하면 admin(관리자)가 승인 할 수 있다.</p>
</blockquote>
<blockquote>
<p>멤버요청에서는 등록된 회사들 중 자신의 회사를 선택할 수 있고, 선택 후 요청을 보내면 해당하는 회사의 대표가 승인할 수 있다. </p>
</blockquote>
<h3 id="4게시글-리스트페이지-전체보기-페이지-카테고리페이지">(4)게시글 리스트페이지 (전체보기 페이지, 카테고리페이지)</h3>
<ol>
<li>로그인 여부 확인 후 게시글 페이지 이동가능</li>
</ol>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/f8f4267b-e03b-4606-891c-19e689df4872/image.gif" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/40147962-86ff-40e0-a4c6-4b2a41347265/image.png" alt=""></p>
<blockquote>
<p>로그인을 해야만 게시글 페이지로 이동이 가능하며, 일반 멤버는 우리회사 소개하기 버튼이 보이지 않으며, 대표나 관리자만 우리회사 소개하기 버튼이 보인다.</p>
</blockquote>
<pre><code class="language-javascript">// 로그인 여부 확인
   const toCompanyList = () =&gt; {
    const token = localStorage.getItem(&#39;token&#39;);
    if (token === &#39;&#39; || token === null) {
      alert(`로그인 후 이용해주세요!`);
      navigate(`/`);
    } else {
      navigate(`/companyList`);
    }
  };</code></pre>
<ol start="2">
<li>게시글 전체보기 페이지</li>
</ol>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/25ec772b-8b6f-4898-9765-d8300e24387f/image.gif" alt=""></p>
<blockquote>
<p>게시글 전체보기 페이지에서는 지역필터, 카테고리필터, 상세 카테고리 필터 이렇게 3가지 필터가 들어가야했고, 선택할때마다 쿼리스트링을 사용해 실시간 필터링 되도록 하였다. 그리고 카테고리 필터 변경 시 이전의 상세 카테고리 필터데이터는 리셋 시켜주었다.</p>
</blockquote>
<pre><code class="language-javascript">useEffect(() =&gt; {
    const queryString = `${area ? `locationsId=${area}&amp;` : &#39;&#39;}${
      category ? `categoriesLv1Id=${category}` : &#39;&#39;
    }${subCategory ? `&amp;categoriesLv2Id=${subCategory}` : &#39;&#39;}`;
    setQueryString(queryString);
  }, [area, currentPage, category, subCategory, navigate, setQueryString]);</code></pre>
<ol start="3">
<li>게시글 카테고리 페이지</li>
</ol>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/4c13b955-5b9e-491d-a340-67ce6ac6929b/image.gif" alt=""></p>
<blockquote>
<p>게시글 전체보기 페이지에서는 1차로 해당 카테고리를 클릭해서 들어가기 때문에 지역필터, 상세분야필터 두가지만 뜨며, 해당하는 카테고리 데이터에서 실시간 필터링 됩니다. </p>
</blockquote>
<pre><code class="language-javascript">
//카테고리 해당 데이터 가져오기
  useEffect(() =&gt; {
    const token = localStorage.getItem(&#39;token&#39;);
    if (queryString !== &#39;&#39;) {
      fetch(
        `${process.env.REACT_APP_API_URI}/post?categoriesLv1Id=${params.id}`,
        {
          headers: {
            &#39;Content-Type&#39;: &#39;application/json&#39;,
            authorization: token,
          },
        }
      )
        .then(res =&gt; res.json())
        .then(data =&gt; {
          setAllData(data);
        });
    } else {
      fetch(`${process.env.REACT_APP_API_URI}/post?${queryString}`, {
        headers: {
          &#39;Content-Type&#39;: &#39;application/json&#39;,
          authorization: token,
        },
      })
        .then(res =&gt; res.json())
        .then(data =&gt; {
          setAllData(data);
        });
    }
  }, [params.id, queryString]);</code></pre>
<ol start="4">
<li>페이지네이션</li>
</ol>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/cca39f57-0b32-4916-8ed7-ef8f725e5e26/image.gif" alt=""></p>
<blockquote>
<p>한페이지에 10개의 게시글을 불러오고 페이지네이션 기능으로 페이지를 넘기면 다음 10개의 게시글을 불러오도록 하였다.</p>
</blockquote>
<pre><code class="language-javascript">
//전체 게시글을 10개로 나누어 페이지 버튼 생성
const pagination = [];
  for (let i = 1; i &lt;= Math.ceil(allData.length / 10); i++) {
    pagination.push(i);
  }

{pagination.map(page =&gt; (
                &lt;button
                  className={
                    Number(currentPage) === page ? css.currentPage : css.page
                  }
                  key={page}
                  value={page}
                  onClick={onPages}
                &gt;
                  {page}
                &lt;/button&gt;
              ))}


// fetch로 현재 페이지의 10개의 데이터를 가져왔다.
fetch(  `${process.env.REACT_APP_API_URI}/post?categoriesLv1Id=${params.id}&amp;offset=10&amp;page=${currentPage}`,
        {
          headers: {
            &#39;Content-Type&#39;: &#39;application/json&#39;,
            authorization: token,
          },
        }
      )
        .then(res =&gt; res.json())
        .then(data =&gt; {
          setCompanyListData(data);
        });              
</code></pre>
<hr>


<h3 id="👏-프로젝트-마치는-소감">👏 프로젝트 마치는 소감</h3>
<p>﹣ 기업의 기획문서를 토대로 개발하는것은 처음이라 지금까지 해왔던 프로젝트보다 훨씬 어려웠던 것 같다. 먼저 기획문서에서의 각 페이지마다 주어진 요구사항들을 구현하기 위해 많은 토론을 했으며, 문서 이해를 위해 하루종일 회의했었다. 전 프로젝트에서 구현해 본적이 없는 파트를 담당해보고 싶어 새로운 파트를 담당해보았는데 구현 하니 한층 성장한 느낌이 들어 뿌듯하고 재밌었다. 기한내에 구현하고자 후반부에 가서는 다들 새벽까지 열심히 코딩했는데 같이 한 팀원들 모두 수고하셨고 감사하다고 말하고 싶다!🥺 최고의 팀워크였고, 결과물 또한 너무 마음에 들어 힘들었지만 행복하고 뿌듯했던 2주였다 ❤️</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[두번째 프로젝트 회고 - 밀리는 서재]]></title>
            <link>https://velog.io/@hi_sulhwa/%EB%91%90%EB%B2%88%EC%A7%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0-%EB%B0%80%EB%A6%AC%EB%8A%94-%EC%84%9C%EC%9E%AC</link>
            <guid>https://velog.io/@hi_sulhwa/%EB%91%90%EB%B2%88%EC%A7%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0-%EB%B0%80%EB%A6%AC%EB%8A%94-%EC%84%9C%EC%9E%AC</guid>
            <pubDate>Tue, 29 Nov 2022 08:08:25 GMT</pubDate>
            <description><![CDATA[<p> <img src="https://velog.velcdn.com/images/hi_sulhwa/post/5f3caea3-564c-4562-9bbc-26c68401e2ba/image.png" alt=""></p>
<p>📕 두번째 프로젝트 - [밀리는 서재]</p>
<ul>
<li>두번째 프로젝트 클론코딩 사이트로 &#39;밀리의 서재&#39; 사이트를 선정했다.</li>
</ul>
<p>🎞️ 시연영상
<a href="https://www.youtube.com/watch?v=vOymkA7-0rU">https://www.youtube.com/watch?v=vOymkA7-0rU</a></p>
<hr>

<h3 id="⏰-개발-기간">⏰ 개발 기간</h3>
<p>﹣ 2022.11.14~11.25</p>
<h3 id="⚒️-기술-스택">⚒️ 기술 스택</h3>
<p>[프론트] - React, Javascript, Router, Sass
[백엔드] - Javascript, Express(Node.js), Mysql, dbmate(DB scheme 버전관리), jsonwebtoken(토큰 발행), bcryptjs(비밀번호 암호화),Axios</p>
<h3 id="🙋🏻♀️-내가-담당한-부분">🙋🏻‍♀️ 내가 담당한 부분</h3>
<ol>
<li>도서 상세페이지 구현</li>
<li>서적 찜 기능 구현</li>
<li>서적 서재 담기 기능 구현</li>
<li>댓글 기능 구현</li>
<li>페이지 링크 복사 구현</li>
<li>내 서재 페이지 구현</li>
</ol>
<h3 id="🙌🏻-협업방식---트렐로">🙌🏻 협업방식 - (트렐로)</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/29e7ee43-0cb0-4ca0-9052-5b003a9bdd9c/image.gif" alt=""></p>
<blockquote>
<p>트렐로를 이용해서 진행상황을 공유하고, 디스코드를 이용하여 매일 10시 회의를 하였습니다!</p>
</blockquote>
<h3 id="1-도서-상세페이지-구현">(1) 도서 상세페이지 구현</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/bc97e428-f9d8-4d44-a0b5-3e80199aebc5/image.gif" alt=""></p>
<pre><code class="language-javascript">      {item.map(
        ({
          id,
          title,
          cover_img,
          toc,...
        }) =&gt; (
          &lt;div className=&quot;wrapper&quot; key={id}&gt;
            &lt;div className={css.BookContainer}&gt;
              &lt;img src={cover_img} alt=&quot;책 이미지&quot; /&gt;
              &lt;div className={css.BookInfo}&gt;
                &lt;div className={css.BookInfoContent}&gt;
                    ...
                  {books_authors.map((prop, idx) =&gt; {
                    return &lt;AuthorName key={idx} {...prop} /&gt;;
                  })}
                &lt;/div&gt;
                ...
        &lt;Index key={toc.id} toc={toc} /&gt;
            {books_authors.map(author =&gt; {
              return &lt;Author key={author.author_id} {...author} /&gt;;
            })}
            &lt;Publisher key={publisher.id} publisher={publisher} /&gt;
            &lt;Comments setBookInfoComments={setCommentArray} /&gt;
          &lt;/div&gt;
        )
      )}
</code></pre>
<blockquote>
<p>백엔드에서 해당 책의 정보가 담긴 데이터들을 가져왔다. map 함수를 돌려서 데이터를 넣어주었고, 해당하는 컴포넌트에 props를 이용해 데이터를 넘겼다. 배열데이터는 한번 더 map함수를 돌려 넘겨주었다.</p>
</blockquote>
<br>


<h3 id="2-서적-찜-기능-구현">(2) 서적 찜 기능 구현</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/c1e2a184-2cb5-44aa-b071-ecc3e3d8c105/image.gif" alt=""></p>
<pre><code class="language-javascript">
  const [bookData, setBookData] = useState([]);
  const [likeCheck, setLikeCheck] = useState(false);

//찜 데이터 보내기
  const onFavorite = () =&gt; {
    fetch(`서버주소`, {
      method: &#39;POST&#39;,
      headers: {
        &#39;Content-Type&#39;: &#39;application/json&#39;,
        authorization: localStorage.getItem(&#39;token&#39;),
      },
      body: JSON.stringify({
        books_id: bookData[0].id,
      }),
    })
      .then(res =&gt; res.json())
      .then(res =&gt; {
        fetch(`서버주소`, {
          method: &#39;GET&#39;,
          headers: {
            &#39;Content-Type&#39;: &#39;application/json&#39;,
            authorization: localStorage.getItem(&#39;token&#39;),
          },
        })
          .then(res =&gt; res.json())
          .then(data =&gt; {
            setLikeCheck(data);
          });
      });
  };

  //찜 데이터 삭제
  const delFavorite = () =&gt; {
    fetch(`서버주소`, {
      method: &#39;DELETE&#39;,
      headers: {
        &#39;Content-Type&#39;: &#39;application/json&#39;,
        authorization: localStorage.getItem(&#39;token&#39;),
      },
      body: JSON.stringify({
        books_id: bookData[0].id,
      }),
    })
      .then(res =&gt; res.json())
      .then(res =&gt; {
        fetch(`서버주소`, {
          headers: {
            &#39;Content-Type&#39;: &#39;application/json&#39;,
            authorization: localStorage.getItem(&#39;token&#39;),
          },
        })
          .then(res =&gt; res.json())
          .then(data =&gt; {
            setLikeCheck(data);
          });
      });
  };
</code></pre>
<blockquote>
<p>찜부분은 fetch 를 많이해야만 했다..! 먼저 찜했을때 books_id 데이터를 보내고, 한번 더 클릭 시 삭제할 수 있도록 했다. 그리고 페이지에서 찜한 상품의 별은 노란색이 채워져 있어야해서 찜 값이 true일 시 적용 될 수 있도록 하였다. 물론 이 부분도 체크 데이터를 확인하기 위해 fetch를 사용했다.</p>
</blockquote>
<br>


<h3 id="3-서적-서재-담기-기능-구현">(3) 서적 서재 담기 기능 구현</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/50d09a62-ac32-46e5-b818-01cadbe2e3cd/image.gif" alt=""></p>
<pre><code class="language-javascript">
  //내 서재에 데이터 보내기
  const onMyShelf = () =&gt; {
    fetch(`서버주소`, {
      method: &#39;POST&#39;,
      headers: {
        &#39;Content-Type&#39;: &#39;application/json&#39;,
        authorization: localStorage.getItem(&#39;token&#39;),
      },
      body: JSON.stringify({
        books_id: bookData[0].id,
      }),
    });
  };
</code></pre>
<blockquote>
<p>찜과 같이 서재 담기를 클릭 시 백으로 데이터를 전송하였다! 그 후 내서재 페이지에서 해당 책이 담긴 것을 확인 할 수 있다.</p>
</blockquote>
<br>

<h3 id="4-댓글-기능추가-삭제-구현">(4) 댓글 기능(추가, 삭제) 구현</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/552ddd54-573f-480c-b766-e994bcd652fc/image.gif" alt=""></p>
<pre><code class="language-javascript">
----------댓글 추가

  const addComment = event =&gt; {
    fetch(`서버주소`, {
      method: &#39;POST&#39;,
      headers: {
        &#39;Content-Type&#39;: &#39;application/json&#39;,
        authorization: localStorage.getItem(&#39;token&#39;),
      },
      body: JSON.stringify({
        books_id: params.id,
        content: value.current.value,
      }),
    })
      .then(res =&gt; res.json())
      .then(res =&gt; {
        fetch(`서버주소`)
          .then(res =&gt; res.json())
          .then(data =&gt; {
            setComments(data.reviewInfo.reviewArray);
            setBookInfoComments(data.reviewInfo.reviewArray);
          });
      });
  };

// 댓글 데이터 가져오기
  useEffect(() =&gt; {
    fetch(`서버주소`)
      .then(res =&gt; res.json())
      .then(data =&gt; {
        setComments(data.reviewInfo.reviewArray);
        setBookInfoComments(data.reviewInfo.reviewArray);
      });
  });
</code></pre>
<blockquote>
<p>댓글을 추가하면 백엔드로 데이터를 보내주고, 백에서 리뷰데이터를 가져와 보여주었다.</p>
</blockquote>
<pre><code class="language-javascript">----------댓글 삭제

//comments.js
  const onRemove = review_id =&gt; {
    if (window.confirm(&#39;삭제 하시겠습니까?&#39;)) {
      fetch(`서버주소`, {
        method: &#39;DELETE&#39;,
        headers: {
          &#39;Content-Type&#39;: &#39;application/json&#39;,
          authorization: localStorage.getItem(&#39;token&#39;),
        },
        body: JSON.stringify({
          review_id: review_id,
        }),
      })
        .then(res =&gt; res.json())
        .then(result =&gt; {
          setComments(comments.filter(props =&gt; props.review_id !== review_id));
        });
    }
  };

//comment.js
  {visible &amp;&amp; (
       &lt;button className={css.moreArea}
        onClick={() =&gt; onRemove(review_id)}&gt;
              삭제하기
       &lt;/button&gt;
   )}
</code></pre>
<blockquote>
<p>삭제하기 버튼을 클릭하면 onRemove 함수가 실행 되고, filter메소드를 이용해서 해당 review_id와 다른 review_id들로 새로운 배열을 만들었다.</p>
</blockquote>
<br>

<h3 id="5-페이지-링크-복사-구현">(5) 페이지 링크 복사 구현</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/d884b0f1-22ca-4572-b8c6-27e5d0780806/image.gif" alt=""></p>
<pre><code class="language-javascript">  const copyUrl = () =&gt; {
    let url = &#39;&#39;;
    let textarea = document.createElement(&#39;textarea&#39;);
    document.body.appendChild(textarea);
    url = window.document.location.href;
    textarea.value = url;
    textarea.select();
    document.execCommand(&#39;copy&#39;);
    document.body.removeChild(textarea);
    alert(&#39;복사가 완료되었습니다.&#39;);
  };</code></pre>
<blockquote>
<p>textarea의 value값에 지금 브라우저의 url을 넣어주고, document.execCommand(&#39;copy&#39;);로 클립보드에 복사하고, document.body.removeChild(textarea);로 필요없는 textarea를 제거했다.</p>
</blockquote>
<br>

<h3 id="6-내-서재-페이지-구현">(6) 내 서재 페이지 구현</h3>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/219f946f-6c51-4192-be20-45aeacf897ef/image.png" alt=""></p>
<pre><code class="language-javascript">
useEffect(() =&gt; {
    fetch(`서버주소`, {
      method: &#39;GET&#39;,
      headers: {
        &#39;Content-Type&#39;: &#39;application/json&#39;,
        authorization: localStorage.getItem(&#39;token&#39;),
      },
    })
      .then(res =&gt; res.json())
      .then(data =&gt; {
        setBookDataList(data);
      });
  }, []);

 &lt;div className={css.bookContainer}&gt;
    &lt;div className={css.bookBox}&gt;
       &lt;div className={css.imgBox}&gt;
          {bookDataList.map((bookData, idx) =&gt; (
            &lt;img key={idx} src={bookData.cover_img} alt=&quot;책 이미지&quot; /&gt;
          ))}
        &lt;/div&gt;
       &lt;div className={css.bookSupport}&gt;&lt;/div&gt;
    &lt;/div&gt;
 &lt;/div&gt;
</code></pre>
<blockquote>
<p>백엔드에서 찜 데이터, 서재담기 한 책의 데이터 가져온 후, 찜에 해당하는 책, 내 책장에 해당하는 책들의 이미지를 넣어주었다.</p>
</blockquote>
<br>

<hr>


<h3 id="👏-프로젝트-마치는-소감">👏 프로젝트 마치는 소감</h3>
<p>﹣ 두번째 프로젝트여서 그런지 한결 수월했던 것 같았다. 백엔드와의 연결, 내 파트 코드 작성, 에러 해결 등등 프로젝트 하는 기간 동안 많이 전과 비교해 성장했음을 느낄 수 있었다! 마지막으로 2주동안 함께 열심히 해준 밀리는서재 팀원들 모두 수고하셨고 감사했습니다~!🙇🏻‍♀️</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[첫번째 프로젝트 회고 - Jsop ]]></title>
            <link>https://velog.io/@hi_sulhwa/%EC%B2%AB%EB%B2%88%EC%A7%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0-Jsop</link>
            <guid>https://velog.io/@hi_sulhwa/%EC%B2%AB%EB%B2%88%EC%A7%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0-Jsop</guid>
            <pubDate>Sun, 13 Nov 2022 08:35:28 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/cf8de5eb-4882-4775-b8ce-c77aac764b64/image.png" alt=""></p>
<p>🍊 첫번째 프로젝트 - [제이솝 JSOP]</p>
<ul>
<li>첫번째 프로젝트 클론코딩 사이트로 이솝(Aesop)사이트를 결정했다!</li>
</ul>
<hr>

<h3 id="⏰-개발-기간">⏰ 개발 기간</h3>
<p>﹣ 2022.10.31~11.11</p>
<h3 id="⚒️-기술-스택">⚒️ 기술 스택</h3>
<p>[프론트] - React, Javascript, Router, Sass
[백엔드] - Node.js / Express / Bcrypt /  jsonwebtoken(JWT) / MySQL(+ Morgan / Cors / Typeorm) </p>
<h3 id="🙋🏻♀️-내가-담당한-부분">🙋🏻‍♀️ 내가 담당한 부분</h3>
<ol>
<li>메인화면 UI, 로고, 파비콘, 상품 이미지 제작 🎨</li>
<li>상품 캐러셀 기능 구현</li>
<li>카테고리 필터링 구현</li>
<li>상품 디테일 페이지로 연결</li>
</ol>
<h4 id="1-이미지-제작과정">(1) 이미지 제작과정</h4>
<p>먼저 사이트 디테일과 이미지 저작권(?) 문제로 이미지를 제작했다!
포토샵 이용해서 로고랑 상품이미지를 만들어봤는데 png 저장이 잘 되지 않아서 약간 애를 먹었지만! 다 만들고 나니까 뿌-듯했다! </p>
<h4 id="2-상품-캐러셀-기능">(2) 상품 캐러셀 기능</h4>
<p>처음 파트 분배할때부터 캐러셀 기능을 구현해보고 싶어서 내가 하고싶다고 했다! 
아래 보이는 캐러셀이 내가 구현해낸 캐러셀이다 ! 
두가지 버전이 있는데 첫번째는 상품 한개씩 움직이는 것이고,
두번째는 현 페이지가 모두 넘어가도록 만들었다.</p>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/f93a7238-0c57-4d1a-bd19-ae46dd38cbfb/image.gif" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/58f5a28a-4e4d-4811-9bc9-4957b75e0e0d/image.gif" alt=""></p>
<pre><code class="language-javascript">carousel.js

const prevCarousel = () =&gt; { //이전으로 움직이는 캐러셀
    if (currentCarousel &gt; 0) {
      currentCarousel = currentCarousel - 1;
    }
    let moveToCarousel = currentCarousel * -32; //캐러셀 움직이는 부분
    let moveToPage = currentCarousel * 22; //캐러셀 아래 페이지 이동부분
    carouselRef.current.style.transform = `translate(${moveToCarousel}vw)`; //현재위치에서 지정된 범위만큼 위치 이동
    pageRef.current.style.transform = `translate(${moveToPage}vw)`;
    setCarousel(currentCarousel);
  };

  const nextCarousel = () =&gt; { //다음으로 움직이는 캐러셀
    if (currentCarousel &lt; item.length - 3) {
      currentCarousel = currentCarousel + 1;
    }
    let moveToCarousel = currentCarousel * -32; //캐러셀 움직이는 부분
    let moveToPage = currentCarousel * 22; //캐러셀 아래 페이지 이동부분
    carouselRef.current.style.transform = `translate(${moveToCarousel}vw)`; //현재위치에서 지정된 범위만큼 위치 이동
    pageRef.current.style.transform = `translate(${moveToPage}vw)`;
    setCarousel(currentCarousel);
  };
</code></pre>
<blockquote>
<p>위 코드는 상품 한개 이동 캐러셀 코드로, 전체페이지 이동은 이동범위를 이미지 크기만큼 지정해주었다.</p>
</blockquote>
<h4 id="3-카테고리-필터링-구현">(3) 카테고리 필터링 구현</h4>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/4907c748-e0eb-425e-8247-88290a50d9ec/image.gif" alt=""></p>
<pre><code class="language-javascript">
//카테고리 클릭하는 필터링
 const contentChange = e =&gt; {
    e.preventDefault();
    setContent(e.target.value);
    fetch(
      {서버주소}
    )
      .then(res =&gt; res.json())
      .then(res =&gt; {
        const filterItems = res.filter(detail =&gt; {
          return detail.category.level_2_category === e.target.value; 
        });
        setFilterItem(filterItems);
      });
  };
</code></pre>
<blockquote>
<p>카테고리 버튼에 클릭 이벤트를 주었고, 상단에 클릭한 카테고리를 확인 할 수 있도록 만들었다!
e.target.value에 해당하는 제품들은 filter메소드를 통해 가져와서 저장했고, 밑에서 map을 돌려서 페이지를 만들었다!</p>
</blockquote>
<h4 id="4-상품-디테일-페이지로-연결">(4) 상품 디테일 페이지로 연결</h4>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/795ab330-1aeb-4710-a776-65d5d61450fe/image.gif" alt=""></p>
<pre><code class="language-javascript">
 const [product, setProduct] = useState([]);
 const params = useParams();

  useEffect(() =&gt; {
    window.scrollTo(0, 0);
    fetch(`{서버주소}/${params.id}`)
      .then(res =&gt; res.json())
      .then(res =&gt; setProduct(res));
  }, []);

{product.map(props =&gt; {
        return &lt;CategoryProductDetail key={props.id} {...props} /&gt;;
      })}
</code></pre>
<blockquote>
<p>useParams()를 이용하여 서버에서 해당 상품의 id에 해당하는 정보를 가져왔다! 그리고 props이용해서 디테일 페이지 안에 데이터를 넣어주었다!</p>
</blockquote>
<hr>

<h3 id="🥺-어려웠던-부분">🥺 어려웠던 부분</h3>
<p>− 위에서 카테고리 필터구현을 어떤 식으로 해야할지 감이 안잡혀서 map만 냅다 돌렸는데,, 팀원들이 도와주셔서 filter 메소드 사용해서 해결했다..
그리고 처음 깃에서 merge를 main으로 해버려서 되돌리는데 애를 먹었다..😿 진짜 Git은 끝판왕으로 너무너무 어려웠다... 에러풀면 새로운 에러가 생기구,,눈물의 연속이었다..그리구 gitk ..=&gt;이친구 진짜 눈으로 딱딱보여서 아주 좋았다!!신세계였달까...?</p>
<h3 id="🫶-프로젝트-마치는-소감">🫶 프로젝트 마치는 소감</h3>
<p>﹣ 처음 프로젝트을 진행해봤는데 너무 재밌구, 행복했다🔥 
우리팀<del>~ <strong>(💗지온,기밍,떵아,우령쿤,상유니,기혀니💗)</strong> 모두모두 수고하셨어욤🥺 다들 도움요청도 잘 도와주셔서 너무 감사했고, 회의할때도, 회의안할때도! 분위기 너무너무 좋고 재밌어서 하는 내내 너무너무(x10000)!즐거웠다(제이솝 최고👍)❤️ 다음에는..2인분할수있도록 성장해서..돌아올거니까..! 우리 타입스크립트로 기깔나는거 한번 더 맹드러봐요오</del>🫶</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[새로 추가된 Javascript 기능[es13]]]></title>
            <link>https://velog.io/@hi_sulhwa/%EC%83%88%EB%A1%9C-%EC%B6%94%EA%B0%80%EB%90%9C-Javascript-%EA%B8%B0%EB%8A%A5es13</link>
            <guid>https://velog.io/@hi_sulhwa/%EC%83%88%EB%A1%9C-%EC%B6%94%EA%B0%80%EB%90%9C-Javascript-%EA%B8%B0%EB%8A%A5es13</guid>
            <pubDate>Fri, 28 Oct 2022 13:59:54 GMT</pubDate>
            <description><![CDATA[<h3 id="새롭게-추가된-기능">새롭게 추가된 기능</h3>
<h4 id="1-top-level-await-operator">1. Top-level Await Operator</h4>
<ul>
<li>이전에는 비동기 함수 외부에서 await 키워드를 선언할 수 없어 오류가 발생했는데, 이제는 비동기 함수 및 클래스 외부에서 await 연산자를 선언하여 동기화 문제를 해결할 수 있다.</li>
</ul>
<pre><code class="language-javascript">(async function () {
  await startServer();
})();

-------------------------------
await startServer();</code></pre>
<h4 id="2-error-cause">2. Error Cause</h4>
<ul>
<li>오류의 인과관계를 기록하는데 사용하는 Error 객체 속성을 추가할 수 있게 되었다.</li>
</ul>
<pre><code class="language-javascript">new Error(&#39;New error message&#39;);

-------------------------------
new Error(&#39;New error message&#39;, { cause : &quot;승인되지 않았습니다.&quot; });
new Error(&#39;New error message&#39;, { cause : &quot;데이터가 없습니다.&quot; });
new Error(&#39;New error message&#39;, { cause : &quot;데이터베이스가 가득 찼습니다.&quot; });</code></pre>
<h4 id="3-method-at-function-for-indexing-📎">3. Method .at() function for Indexing <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/at">📎</a></h4>
<ul>
<li><p>.at() 키워드 추가 : at() 함수를 사용하여 양수 및 음수 인덱스를 모두 사용하여 문자열을 인덱싱할 수 있다.</p>
</li>
<li><p>맨 마지막 요소를 가져오고 싶을 때 length 속성을 사용해 array[array.length - 1]을 하는 대신, 짧게 array.at(-1)을 사용할 수 있다.</p>
</li>
</ul>
<pre><code class="language-javascript">const array= [1,2,3,4];
array.at(2); //이전 : array[2];
array.at(0); // 1
array.at(-1); // 4</code></pre>
<h4 id="4-objectprototypehasownproperty-📎">4. Object.prototype.hasOwnProperty() <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty">📎</a></h4>
<ul>
<li><p>Object.prototype.hasOwnProperty의 대안으로 추가, hasOwn() 추가</p>
</li>
<li><p>Object.hasOwnProperty는 Object를 첫 번째 인수로, 확인하려는 속성을 두 번째 인수로 사용합니다.</p>
</li>
<li><p>hasOwnProperty() 메소드는 객체가 특정 프로퍼티를 가지고 있는지를 나타내는 불리언 값을 반환한다.</p>
</li>
</ul>
<pre><code class="language-javascript">
const obj = Object.create(null);
obj.color = &#39;green&#39;;
obj.age = 2;
obj.hasOwnProperty = () =&gt; false;

console.log(Object.hasOwn(obj, &#39;color&#39;)); // true
console.log(Object.hasOwn(obj, &#39;name&#39;)); // false

// 객체와 속성이 인수로, 지정된 속성이 객체의 직접속성이면 true, 아니면 false.</code></pre>
<h4 id="5-class-field-declarations">5. Class field declarations</h4>
<ul>
<li>처음에는 생성자 내에서만 클래스 필드를 선언할 수 있었지만 이제는 4단계의 제안을 사용하여 생성자를 호출할 필요없이 클래스 자체에서 선언할 수 있다.</li>
</ul>
<pre><code class="language-javascript">class Car {
  constructor() {
    this.color = &#39;blue&#39;;
    this.age = 2;
  }
}

const car = new Car();
console.log(car.color); // blue
console.log(car.age); // 2


// ES13 이전에는 클래스 필드를 생성자에서만 선언할 수 있었고, 클래스의 가장 바깥쪽 범위에서 선언하거나 정의할 수 없다.
--------------------------------

class Car {
  color = &#39;blue&#39;;
  age = 2;
}

const car = new Car();
console.log(car.color); // blue
console.log(car.age); // 2

//ES13은 이 제한을 제거한다.</code></pre>
<h4 id="6-ergonomic-brand-checks-for-private-fields-📎">6. Ergonomic Brand Checks for Private Fields <a href="https://codingbeautydev.com/blog/es13-javascript-features/">📎</a></h4>
<ul>
<li>in연산자를 사용하여 개체에 특정 개인 필드가 있는지 확인할 수 있다.</li>
</ul>
<pre><code class="language-javascript">class Car {
  #color;

  hasColor() {
    return #color in this;
  }
}

const car = new Car();
console.log(car.hasColor()); // true;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue 설치 오류 (zsh: command not found: vue)]]></title>
            <link>https://velog.io/@hi_sulhwa/Vue-%EC%84%A4%EC%B9%98-%EC%98%A4%EB%A5%98-zsh-command-not-found-vue</link>
            <guid>https://velog.io/@hi_sulhwa/Vue-%EC%84%A4%EC%B9%98-%EC%98%A4%EB%A5%98-zsh-command-not-found-vue</guid>
            <pubDate>Sat, 22 Oct 2022 14:59:57 GMT</pubDate>
            <description><![CDATA[<h3 id="1-vue-설치-중-오류발생">1. Vue 설치 중 오류발생</h3>
<blockquote>
<p>npm install -g @vue/cli</p>
</blockquote>
<p>여기서 권한 없음 에러가 발생 시! &quot;sudo&quot;를 붙혀서 해결가능합니다!</p>
<blockquote>
<p>sudo npm install -g @vue/cli</p>
</blockquote>
<p>여기서 &quot;<strong>vue --version</strong>&quot;을 실행시켰을때</p>
<ul>
<li><em>&gt;<strong>zsh: command not found: vue</strong> 라는 에러가 발생..</em></li>
</ul>
<h3 id="2-해결방법">2. 해결방법</h3>
<ol>
<li>먼저 모든 vue cli를 제거</li>
</ol>
<blockquote>
<p>sudo npm uninstall --global vue-cli
sudo npm uninstall --global @vue/cli</p>
</blockquote>
<ol>
<li>최신 버전의 vue cli를 설치</li>
</ol>
<blockquote>
<p>sudo npm install --global @vue/cli@latest</p>
</blockquote>
<ol>
<li><strong>&quot;vue --version&quot;</strong> 을 통해 잘 깔린것을 확인할 수 있습니다!</li>
</ol>
<pre><code>~ % vue --version
@vue/cli 5.0.8
</code></pre><p>🐻 참고하였습니다 <a href="https://stackoverflow.com/questions/58590890/how-to-fix-zsh-command-not-found-vue">https://stackoverflow.com/questions/58590890/how-to-fix-zsh-command-not-found-vue</a></p>
<h3 id="🌻-1-vue-파일에서-template-부분에서-문제가-있다고-알려주고-있었다">🌻 1. vue 파일에서 template 부분에서 문제가 있다고 알려주고 있었다</h3>
<p><img src="https://velog.velcdn.com/images/im_sulhwa/post/c77f6084-0eb6-4987-abdd-e17f96e8e32b/image.png" alt="https://velog.velcdn.com/images/im_sulhwa/post/c77f6084-0eb6-4987-abdd-e17f96e8e32b/image.png"></p>
<p>🌻2. 문제 내용!
-&gt; 대충 TypeScript Intellisense를 활성화 시켜야한다는 말..</p>
<pre><code>TypeScript intellisense is disabled on template.
To enable, configure `&quot;jsx&quot;: &quot;preserve&quot;` in the `&quot;compilerOptions&quot;`
property of tsconfig or jsconfig. To disable this prompt instead, configure `&quot;experimentalDisableTemplateSupport&quot;: true` in `&quot;vueCompilerOptions&quot;`
property.volar
</code></pre><p>🌻3. jsconfig.json 에 코드를 추가해주니 문제가 해결되었다!</p>
<blockquote>
<p>&quot;jsx&quot;: &quot;preserve&quot;</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/im_sulhwa/post/a716da4d-5a80-443a-b74c-2f52d32a1ad4/image.png" alt="https://velog.velcdn.com/images/im_sulhwa/post/a716da4d-5a80-443a-b74c-2f52d32a1ad4/image.png"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Javascript this ]]></title>
            <link>https://velog.io/@hi_sulhwa/Javascript-this</link>
            <guid>https://velog.io/@hi_sulhwa/Javascript-this</guid>
            <pubDate>Fri, 21 Oct 2022 16:36:34 GMT</pubDate>
            <description><![CDATA[<h3 id="함수-호출-방식에-따라-결정되는-this-💨">함수 호출 방식에 따라 결정되는 this <a href="https://poiemaweb.com/js-this">💨</a></h3>
<h4 id="✋-this-란">✋ this 란?</h4>
<p>-&gt;  함수를  선언되었을 때가 아닌 <strong>호출</strong>되었을때 결정
-&gt; ES5는 함수를 어떻게 호출했는지 상관하지 않고 this 값을 설정할 수 있는 bind 메서드를 도입
-&gt; ES2015는 스스로의 this 바인딩을 제공하지 않는 화살표 함수를 추가</p>
<hr>

<h3 id="1-함수-호출">1. 함수 호출</h3>
<blockquote>
<p> 전역객체(Global Object)는 모든 객체의 유일한 최상위 객체를 의미한다 </p>
</blockquote>
<ul>
<li>Browser-side에서는 window객체</li>
<li>Server-side(Node.js)에서는 global객체</li>
</ul>
<pre><code class="language-javascript">function foo() {
  console.log(&quot;foo&#39;s this: &quot;,  this);  // window
  function bar() {
    console.log(&quot;bar&#39;s this: &quot;, this); // window
  }
  bar();
}
foo();


👉 기본적으로 this 는 전역객체에 바인딩 되고, 내부함수의 this 도 전역객체에 바인딩 된다.</code></pre>
<pre><code class="language-javascript">
//내부함수
var value = 1;

var obj = {
  value: 100,
  foo: function() {
    console.log(&quot;foo&#39;s this: &quot;,  this);  // obj
    console.log(&quot;foo&#39;s this.value: &quot;,  this.value); // 100
    function bar() {
      console.log(&quot;bar&#39;s this: &quot;,  this); // window
      console.log(&quot;bar&#39;s this.value: &quot;, this.value); // 1
    }
    bar();
  }
};

obj.foo();

----------------------------------------------------

//콜백함수
var value = 1;

var obj = {
  value: 100,
  foo: function() {
    setTimeout(function() {
      console.log(&quot;callback&#39;s this: &quot;,  this);  // window
      console.log(&quot;callback&#39;s this.value: &quot;,  this.value); // 1
    }, 100);
  }
};

obj.foo();
</code></pre>
<p><a href="https://kangdanne.tistory.com/88">🔥</a> this가 전역 객체에 바인딩 되는 경우</p>
<ul>
<li><p>전역 함수에서의 this</p>
</li>
<li><p>내부 함수(중첩 함수)에서의 this</p>
</li>
<li><p>메서드의 내부 함수에서의 this</p>
</li>
<li><p>콜백 함수에서의 this</p>
</li>
<li><p>이벤트에서의 this는 &#39;이벤트 객체&#39;</p>
</li>
<li><p>메서드에서의 this는 &#39;메서드 객체&#39;</p>
</li>
</ul>
<h3 id="2-메소드-호출">2. 메소드 호출</h3>
<p>🫧 메서드 내부에서의 this</p>
<ul>
<li>this에는 호출한 주제에 대한 정보가 담기는데, 어떤 함수를 메서드로서 호출할때의 호출주체는 함수명(프로퍼티명) 앞의 객체이다. </li>
<li>점 표기법의 경우 마지막 점 앞에 명시된 객체가 곧 this가 된다.</li>
</ul>
<pre><code class="language-javascript">
//메소드 내부 this
var obj1 = {
  name: &#39;Lee&#39;,
  sayName: function() {
    console.log(this.name); //Lee
  }
}

var obj2 = {
  name: &#39;Kim&#39;
}

obj2.sayName = obj1.sayName; //Kim

obj1.sayName();
obj2.sayName();

👉 함수가 객체의 프로퍼티 값일 시, 메소드로서 호출되고, this는 메소드를 소유한 객체가 된다.

----------------------------------------------------

//프로토타입 객체
function Person(name) {
  this.name = name;
}

Person.prototype.getName = function() {
  return this.name;
}

var me = new Person(&#39;Lee&#39;);
console.log(me.getName()); //Lee

Person.prototype.name = &#39;Kim&#39;;
console.log(Person.prototype.getName()); //Kim

👉 프로토타입 객체도 메소드를 가질 수 있고, 프로토타입 객체의 this도 해당 메소드를 호출한 객체를 바인딩한다.</code></pre>
<img src="https://poiemaweb.com/img/prototype_metthod_invocation_pattern.png">


<h3 id="3-생성자-함수-호출">3. 생성자 함수 호출</h3>
<ul>
<li>어떤 공통된 성질을 지니는 객체들을 생성하는데 사용하는데 함수이고, 자바스크립트에서는 기존 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다.</li>
<li>객체지향 언어에서는 생성자를 클래스, 클래스를 통해 만든 객체를 인스턴스라고 한다.</li>
</ul>
<blockquote>
<p>어떤 함수가 생성자 함수로서 호출된 경우 내부에서의 this는 곧 새로 만들 구체적인 객체(인스턴스) 자신이다.</p>
</blockquote>
<pre><code class="language-javascript">// 생성자 함수
function Person(name) {
  // 생성자 함수 코드 실행 전 -------- 1
  this.name = name; // --------- 2
  // 생성된 함수 반환 -------------- 3
}

var me = new Person(&#39;Lee&#39;);
console.log(me); // Person {name: &#39;Lee&#39;}

// new 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수로 동작하지 않는다.
var you = Person(&#39;Kim&#39;);
console.log(you); // undefined

👉 new 연산자와 함께 생성자 함수를 호출하면 this 바인딩이 메소드나 함수 호출 때와는 다르게 동작한다.
</code></pre>
<ol>
<li>객체 리터럴 방식과 생성자 함수 방식의 차이</li>
</ol>
<pre><code class="language-javascript">// 객체 리터럴 방식
var foo = {
  name: &#39;foo&#39;,
  gender: &#39;male&#39;
}

console.dir(foo); //Object

// 생성자 함수 방식
function Person(name, gender) {
  this.name = name;
  this.gender = gender;
}

var me  = new Person(&#39;Lee&#39;, &#39;male&#39;);
console.dir(me); //Person

var you = new Person(&#39;Kim&#39;, &#39;female&#39;);
console.dir(you);  //Person


/* 객체 리터럴 방식과 생성자 함수 방식의 차이는 프로토타입 객체([[Prototype]])에 있다.

객체 리터럴 방식의 경우, 생성된 객체의 프로토타입 객체는 Object.prototype이다.

생성자 함수 방식의 경우, 생성된 객체의 프로토타입 객체는 Person.prototype이다.

프로토타입이 다르므로 상위 객체가 다르다
*/
</code></pre>
<blockquote>
<p>가장 큰 차이점은 재사용 가능 여부입니다. 객체 리터럴 방식으로 객체를 생성하는 방법은 재사용할 수 없지만, 생성자 함수는 재사용할 수 있습니다.</p>
</blockquote>
<ol start="2">
<li>생성자 함수에 new 연산자를 붙이지 않고 호출할 경우<blockquote>
<p>생성자 함수는 new 연산자를 붙여서 함수를 호출하면 return문이 없어도 객체가 반환된다는 점 이외에는 일반 함수와 큰 차이점이 없다. (new 연산자를 사용해야 객체가 반환된다)</p>
</blockquote>
</li>
</ol>
<pre><code class="language-javascript">function Person(name, gender) {
  this.name = name;
  this.gender = gender;
}

var me  = Person(&#39;Lee&#39;, &#39;male&#39;);
console.log(me); // undefined

👉 javascript 함수에 return문이 존재하지 않을 시 함수는 undefined를 반환한다.</code></pre>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/38adbae8-d53b-4174-a4f5-3f40894d217e/image.png" alt=""></p>
<h3 id="4-applycallbind-호출-📍">4. apply/call/bind 호출 <a href="https://kosaf04pyh.tistory.com/250">📍</a></h3>
<ul>
<li>this에 바인딩될 객체는 함수 호출 패턴에 의해 결정되는데, 자바스크립트 엔진의 암묵적 this 바인딩 이외에 this를 특정 객체에 명시적으로 바인딩하는 방법도 제공된다. </li>
</ul>
<blockquote>
<p>Function.prototype.apply, Function.prototype.call, Function.prototype.bind 메소드는 모든 함수 객체의 프로토타입 객체인 Function.prototype 객체의 메소드이다.</p>
</blockquote>
<ol>
<li>apply 메서드
call 메서드와 기능적으로 완전히 동일하며, call 메서드는 첫 번째 인자를 제외한 나머지 모든 인자들을 호출할 함수의 매개변수로 지정하는 반면, apply 메서드는 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정한다는 점에서만 차이가 있다.</li>
</ol>
<pre><code class="language-javascript">Person.apply(foo, [1, 2, 3]);

Person.call(foo, 1, 2, 3);```
</code></pre>
<blockquote>
<p>apply() 메소드는 this를 특정 객체에 바인딩할 뿐 본질적인 기능은 함수 호출이다.</p>
</blockquote>
<pre><code class="language-javascript">var Person = function (name) {
  this.name = name;
};

var foo = {};

// apply 메소드는 생성자함수 Person을 호출, 이때 this에 객체 foo를 바인딩한다.
Person.apply(foo, [&#39;name&#39;]);

console.log(foo); // { name: &#39;name&#39; }</code></pre>
<ol start="2">
<li>call 메서드
메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령.
call 메서드의 첫 번째 인자를 this로 바인딩하고, 이후의 인자들을 호출할 함수의 매개변수로 한다. 함수를 그냥 실행하면 this는 전역객체를 참조하지만 call 메서드를 이용하면 임의의 객체를 this로 지정할 수 있다.</li>
</ol>
<pre><code class="language-javascript">function Person(name) {
  this.name = name;
}

Person.prototype.doSomething = function(callback) {
  if(typeof callback == &#39;function&#39;) {
    // --------- 1
    callback();
  }
};

function foo() {
  console.log(this.name); // --------- 2
}

var p = new Person(&#39;Lee&#39;);
p.doSomething(foo);  // undefined

1에서 this는 Person 객체이다.
2에서 this는 전역 객체 window를 가리킨다.
기본적으로 콜백함수 내부의 this는 window를 가리킨다

// --------- 1
callback.call(this);
콜백함수 내부의 this를 콜백함수를 호출하는 함수 내부 this와 일치시킨다.

// --------- 1
callback.bind(this)();
</code></pre>
<ol start="3">
<li>bind 메서드</li>
</ol>
<pre><code class="language-javascript">function Person(name) {
  this.name = name;
}

Person.prototype.doSomething = function (callback) {
  if (typeof callback == &#39;function&#39;) {
    // callback.call(this);
    // this가 바인딩된 새로운 함수를 호출
    callback.bind(this)();
  }
};

function foo() {
  console.log(&#39;#&#39;, this.name);
}

var p = new Person(&#39;Lee&#39;);
p.doSomething(foo);  // &#39;Lee&#39;

👉 함수에 인자로 전달한 this가 바인딩된 새로운 함수를 리턴, 
  Function.prototype.bind는 Function.prototype.apply, Function.prototype.call 메소드와 같이 함수를 실행하지 않기 때문에 명시적으로 함수를 호출할 필요가 있다.</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS-Module]]></title>
            <link>https://velog.io/@hi_sulhwa/CSS-Module</link>
            <guid>https://velog.io/@hi_sulhwa/CSS-Module</guid>
            <pubDate>Tue, 18 Oct 2022 07:44:22 GMT</pubDate>
            <description><![CDATA[<p>🚨 에러발생</p>
<blockquote>
<p>js로 만든 파일을 react로 옮기는데 같은 클래스이름을 가진 css스타일이 같이 적용되는 문제발생..!</p>
</blockquote>
<h3 id="css-module이란">CSS-Module이란?</h3>
<ul>
<li>CSS를 사용할 때 클래스 이름을 고유한 값으로 자동으로 만들어서 컴포넌트 스타일 클래스 이름이 중첩되는 현상을 방지해 주는 기술</li>
</ul>
<p>🫧 사용이유?</p>
<ol>
<li><p>클래스명(className)이 충돌하는 것을 방지해준다.</p>
</li>
<li><p>유지보수하기 편해진다.</p>
</li>
<li><p>간결한 클래스명을 이용해 컴포넌트 단위로 스타일을 적용할 때 유용하다.</p>
</li>
</ol>
<blockquote>
<p>react에서 모듈을 사용하지 않을 시, 부모 컴포넌트의 css가 자녀들에게 모두 적용되어 side effect 발생 확률이 높아진다!</p>
</blockquote>
<h3 id="사용방법">사용방법</h3>
<p>기존 프로젝트의 css 클래스와 이름이 중복되어도 스타일이 꼬이지 않도록 css파일의 확장자를 .module.css로 만든다. 
👉 <strong>다른 파일이 같은 클래스명을 사용해도 중첩되지 않는다.</strong></p>
<pre><code class="language-css">import React from &quot;react&quot;;
import styles from &quot;./Box.module.css&quot;;

const CSSModule = () =&gt; {
  return (
    &lt;div className={styles.Box}&gt;
      Box
    &lt;/div&gt;
  );
};

export default Box;

---------------------------------------------

&lt;button className={`${style.buttonColor} ${style.fontColor}`}&gt;버튼&lt;/button&gt;
//여러개의 클래스 적용 시 백틱 사용
</code></pre>
<p>📍 .module.css 확장자로 파일을 저장
📍 import styles from &quot;CSS module 클래스명&quot; </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Strict mode (엄격 모드)]]></title>
            <link>https://velog.io/@hi_sulhwa/Strict-mode-%EC%97%84%EA%B2%A9-%EB%AA%A8%EB%93%9C</link>
            <guid>https://velog.io/@hi_sulhwa/Strict-mode-%EC%97%84%EA%B2%A9-%EB%AA%A8%EB%93%9C</guid>
            <pubDate>Fri, 14 Oct 2022 13:02:25 GMT</pubDate>
            <description><![CDATA[<h3 id="strict-mode란">Strict mode란?</h3>
<pre><code class="language-javascript">function foo() {
  x = 10;
}

console.log(x);  //10</code></pre>
<ul>
<li><p>자바스크립트 엔진은 암묵적으로 전역객체에 x 프로퍼티를 동적으로 생성, x 프로퍼티를 전역변수처럼 사용 가능한데 이걸 암묵적 전역 변수(implicit global)라 한다.</p>
</li>
<li><p>이 암묵적 전역변수는 오류를 발생시키는 원인이 될 가능성이 높아 꼭 var, let, const 키워드를 사용하여 변수를 선언한 다음에 사용해야한다 !</p>
</li>
<li><p>새롭게 제정된 ES5에서는 새로운 기능이 추가되고 기존 기능 중 일부가 변경되었는데, 기존 기능을 변경하였기에 하위 호환성 문제가 생겨 변경사항 대부분은 ES5의 기본 모드에선 활성화되지 않도록 설계되었는데, <strong>use strict</strong>라는 특별한 지시자를 사용해 엄격 모드(strict mode)를 활성화 했을 때만 이 변경사항이 활성화 되도록 하였다.</p>
</li>
<li><p>잠재적인 오류를 발생시키기 어려운 개발 환경을 만들고 그 환경에서 개발을 하는 것으로 오류를 줄일 수 있다.</p>
</li>
</ul>
<blockquote>
<p><strong>strict mode</strong> 특징</p>
</blockquote>
<ol>
<li>기존에 무시되던 에러들을 Throwing 한다.</li>
<li>JS 엔진의 최적화 작업을 어렵게 만드는 실수들을 바로잡음.</li>
<li>ECMAScript의 차기 버전들에서 정의될 문법을 금지한다.</li>
</ol>
<p><strong>ESLint와 같은 린트 도구</strong>를 사용하여도 strict mode와 유사한 효과를 얻을 수 있다. 
=&gt; 정적 분석(static analysis) 기능을 통해 소스 코드를 실행하기 전에 소스 코드를 스캔하여 문법적 오류만이 아니라 잠재적 오류까지 찾아내고 오류의 이유를 리포팅해주는 유용한 도구</p>
<hr>

<h3 id="strict-mode-적용-방법">Strict mode 적용 방법</h3>
<blockquote>
<p>&#39;use strict&#39;;<br>// 전역의 선두에 추가하면 스크립트 전체에 적용!
// 함수 몸체의 선두에 추가하면 해당 함수 및 중첩된 내부 함수에 적용 !</p>
</blockquote>
<h3 id="strict-mode-발생시키는-에러">Strict mode 발생시키는 에러</h3>
<pre><code class="language-javascript">&#39;use strict&#39;

x = 10;
console.log(x); // Reference Error 
</code></pre>
<blockquote>
<p>선언되지 않은 변수 참조 시 Reference Error 발생</p>
</blockquote>
<pre><code class="language-javascript">&#39;use strict&#39;

let x = 10;
delete x; // Syntax Error 
</code></pre>
<blockquote>
<p>delete연산자로 변수,함수,매개변수 삭제 시도 시 Syntax Error 발생</p>
</blockquote>
<pre><code class="language-javascript">&#39;use strict&#39;

function foo(x, x){
    return x + 10;
} //Syntax Error</code></pre>
<blockquote>
<p>매개변수 이름을 중복으로 사용 시, Syntax Error 발생</p>
</blockquote>
<pre><code class="language-javascript">&#39;use strict&#39;

  function foo() {
    console.log(this); // undefined
  }
  foo();

  function Foo() {
    console.log(this); // Foo
  }
  new Foo();</code></pre>
<blockquote>
<p>strict모드에서 일반함수로 호출 시 this에는 undefined가 반영되고 이때는 오류가 발생하지 않는다. 생성자 함수가 아닌 일반 함수 내부에서는 this를 사용할 필요가 없기 때문이다.</p>
</blockquote>
<pre><code class="language-javascript">&#39;use strict&#39;

var foo = {
    name: &quot;foo&quot;
}
with (foo) {
    console.log(name);
}</code></pre>
<blockquote>
<p>with를 위와 같이 사용할 순 있지만, with 블록 안에 name은 전역 변수의 name인지 foo의 name인지 모호합니다. 그렇기 때문에 strict 모드에서는 with를 사용하는 것이 불가능합니다.</p>
</blockquote>
<hr>

<h3 id="기존-자바스크립트-문법과-다른-strict-mode의-문법">기존 자바스크립트 문법과 다른 Strict Mode의 문법</h3>
<p><a href="http://www.tcpschool.com/javascript/js_exception_strict">이미지출처</a></p>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/c1624945-7e62-4169-8fdc-c1f085c9c31c/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP 메소드(get, post, put, patch, delete) 및 리소스(uri) 이란 ?]]></title>
            <link>https://velog.io/@hi_sulhwa/HTTP-%EB%A9%94%EC%86%8C%EB%93%9Cget-post-put-patch-delete-%EB%B0%8F-%EB%A6%AC%EC%86%8C%EC%8A%A4uri-%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@hi_sulhwa/HTTP-%EB%A9%94%EC%86%8C%EB%93%9Cget-post-put-patch-delete-%EB%B0%8F-%EB%A6%AC%EC%86%8C%EC%8A%A4uri-%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Wed, 12 Oct 2022 08:14:37 GMT</pubDate>
            <description><![CDATA[<h3 id="http란">HTTP란?</h3>
<ul>
<li>Hyper Text Transfer Protocol의 두문자어로, 인터넷에서 데이터를 주고받을 수 있는 프로토콜</li>
</ul>
<h3 id="restrepresentational-state-transfer-🔗">REST(REpresentational State Transfer) <a href="https://usefultoknow.tistory.com/entry/REST%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C">🔗</a></h3>
<p>웹에 존재하는 모든 자원(이미지,동영상,DB 자원)에 고유한 URI를 부여해 활용하는 것으로, 자원을 정의하고 자원에 대한 주소를 지정하는 방법론을 의미.
이런 REST 형식을 따른 시스템을 RESTful 이라 부름.
HTTP URI를 통해 자원을 명시하고 HTTP Method를 통해 해당 자원의 대한 <strong>CRUD Operation</strong>을 적용. </p>
<ul>
<li>CRUD Operation + HTTP Method : Create(POST, 자원 생성) / Read(GET, 자원의 정보 조회) / Update(PUT, 자원의 정보 업데이트) / Delete(DELETE, 자원 삭제)</li>
</ul>
<blockquote>
<p>웹은 클라이언트와 서버로 구분, 클라이언트 (요청 -&gt; 서버) &amp; 서버(응답 -&gt; 클라이언트)
REST API {URI(주소) 통해서 요청--&gt; 서버} &amp; {서버 html,xml,json등으로 --&gt; 응답}</p>
</blockquote>
<blockquote>
<p>주소는 명사로 구성, 명사만 있을 시 무슨 동작을 행하는지 알기 어렵기 때문에 REST에서는 HTTP 요청 메서드를 사용한다</p>
</blockquote>
<h3 id="http-요청-메소드">HTTP 요청 메소드</h3>
<h4 id="1get--crud---read-">1.GET ( CRUD - Read )</h4>
<p>🐯 URI 형식으로 웹 서버측 리소스(데이터)를 요청
서버 자원 가져오고자 할 때 사용.
파라미터를 넘겨서 해당하는 본문형식을 받는다.</p>
<h4 id="2post--crud---create-">2.POST ( CRUD - Create )</h4>
<p>🐻‍❄️ 내용 전송(파일 전송 가능)
클라이언트에서 서버로 전달하고자하는 정보를 서버로 보냄
서버에 자원을 새로 등록하고자 할 때 사용</p>
<h4 id="3put--crud---update-전체-수정-">3.PUT ( CRUD - Update (전체 수정) )</h4>
<p>🐹 내용 갱신 위주(파일 전송 가능)
POST처럼 정보를 서버로 제출하는 것이나 보통 갱신 위주
서버의 자원을 요청에 들어 있는 자원으로 치환할 때 사용</p>
<h4 id="4patch--crud---update-일부-수정-">4.PATCH ( CRUD - Update (일부 수정) )</h4>
<p>🐷 클라이언트가 서버의 리소스를 수정 할 때 사용</p>
<h4 id="5delete--crud---delete--🔗">5.DELETE ( CRUD - Delete ) <a href="https://hjjooace.tistory.com/entry/REST-API%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C">🔗</a></h4>
<p>🦊 웹 리소스를 제거할 때 사용 
하지만, DELETE의 경우 서버에서 클라이언트의 요청을 무시 가능하기 때문에 실제로 삭제되지 않았지만, 클라이언트는 파일이 삭제 되었다고 생각할 수 있다.</p>
<p>-&gt; 원격지 웹 서버에 파일을 삭제하기 위해 사용되며, 멱등성을 가지고 있습니다.
서버에게 요청 URL로 지정한 리소스를 삭제할 것을 요청하더라도 클라이언트는 삭제가 수행되는 것을 보장 못한다.</p>
<blockquote>
<p>이유 : HTTP 명세에는 서버가 클라이언트에게 알리지 않고 요청을 무시하는 것을 허용하지 않기 때문입니다.
 
보통 대부분의 경우 멱등성을 가지고 있다면 Safe한데, DELETE는 멱등하지만 리소스를 변경하므로 Safe 하지 않다.</p>
</blockquote>
<h3 id="uriuniform-resource-identifier-란-🔗">URI(Uniform Resource Identifier) 란? <a href="https://www.charlezz.com/?p=44767">🔗</a></h3>
<ul>
<li>URI는 특정 리소스를 식별하는 통합 자원 식별자를 의미하는데, 웹 기술에서 사용하는 논리적 또는 물리적 리소스를 식별하는 고유한 문자열 시퀀스</li>
</ul>
<blockquote>
<p>URI 는 네트워크 상 자원을 가리키는 일종의 고유 식별자(ID)
URL, URN 은 URI 에 포함되는 개념이며 URL 은 자원의 위치, URN 은 자원의 이름 을 의미 </p>
</blockquote>
<img src="https://blog.kakaocdn.net/dn/AkL2o/btqJptEQJmu/omyDDiWIRr99BFKeVIpTt0/img.png">

<ul>
<li>URL (Uniformed Resource Locator)</li>
</ul>
<p>프로토콜 포함
해당 자원의 위치, Path를 의미
일반적으로 사이트 도메인을 자주 의미함.
웹 상 뿐만 아니라 컴퓨터 네트워크상의 자원은 모두 나타낼 수 있다.</p>
<ul>
<li>URN (Uniformed Resource Name)</li>
</ul>
<p>프로토콜 포함 X
해당 자원의 이름을 의미
독립적인 자원 지시자
Page 이후 부분까지 포함</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이중 느낌표 연산자 (!!)]]></title>
            <link>https://velog.io/@hi_sulhwa/%EC%9D%B4%EC%A4%91-%EB%8A%90%EB%82%8C%ED%91%9C-%EC%97%B0%EC%82%B0%EC%9E%90</link>
            <guid>https://velog.io/@hi_sulhwa/%EC%9D%B4%EC%A4%91-%EB%8A%90%EB%82%8C%ED%91%9C-%EC%97%B0%EC%82%B0%EC%9E%90</guid>
            <pubDate>Fri, 07 Oct 2022 16:21:15 GMT</pubDate>
            <description><![CDATA[<p>JavaScript에서 이중 느낌표 연산자는 Object를 Boolean으로 변환한다. 
&quot;거짓&quot; 개체가 거짓이 되고 &quot;진실&quot; 개체는 참이 되는 방식으로 발생한다!</p>
<p>!!0-&gt; false
!! null -&gt; false
!! undefined -&gt; false</p>
<p>!! 48 -&gt; true
!! &quot;hello&quot; -&gt; true
!![1,2,3] -&gt; true</p>
<blockquote>
<p>실제로 !(not) 을 두번 사용하고, 첫번째 ! 는 객체를 반대 역 값으로, 두번째 ! 는 반전된 값을 반전시킨다.</p>
</blockquote>
<p><a href="https://www.codingem.com/javascript-double-exclamation-operator/">🔗</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Prototype (프로토타입)]]></title>
            <link>https://velog.io/@hi_sulhwa/Prototype-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@hi_sulhwa/Prototype-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85</guid>
            <pubDate>Thu, 06 Oct 2022 15:08:19 GMT</pubDate>
            <description><![CDATA[<h2 id="prototype-객체">Prototype 객체</h2>
<ul>
<li><p>자바스크립트는 프로토타입 기반 객체지향 프로그래밍 언어로 불린다.</p>
</li>
<li><p>자바스크립트의 모든 객체는 자신의 부모 역할을 담당하는 객체와 연결되어 있다. 그리고 이것은 마치 객체 지향의 상속 개념과 같이 부모 객체의 프로퍼티 또는 메소드를 상속받아 사용할 수 있게 한다. 이러한 부모 객체를 Prototype(프로토타입) 객체 또는 줄여서 Prototype(프로토타입)이라 한다.</p>
</li>
<li><p>자바스크립트의 모든 객체는 [[Prototype]]이라는 숨겨진 속성(internal slot)을 갖고 있다. [[Prototype]]은 항상 null 또는, 객체이며 상속을 구현할때 사용된다. </p>
</li>
<li><p>[[Prototype]]의 값은 Prototype(프로토타입) 객체이며 _ <em>proto</em> _ accessor property로 접근할 수 있다. _ <em>proto</em> _ 프로퍼티에 접근하면 내부적으로 Object.getPrototypeOf가 호출되어 프로토타입 객체를 반환한다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/bda96b6b-4047-46d5-8eb1-7954b9eb217b/image.png" alt="">
<a href="https://www.youtube.com/watch?v=wT1Bl5uV27Y">🔗 이미지 출처 : 생활코딩</a></p>
<blockquote>
<p>함수 객체 앞에 new키워드를 사용하면 생성자가 되고, 이걸로 생성한 객체를 인스턴스라고 한다.</p>
</blockquote>
<p>&quot;_ <em>proto</em> _&quot; : 모든 객체가 가지고 있는 속성으로,
객체가 생성될 때 자신의 부모 객체인 프로토타입을 가리킨다!</p>
<p>&quot;prototype&quot; : 함수 객체만이 가지는 프로토타입으로,
함수 객체가 생성자로 사용될 때 인스턴스의 부모 역할을 하는 객체를 가리킨다!</p>
<hr>
<br>


<h3 id="1-prototype-vs-prototype">1. [[Prototype]] vs prototype</h3>
<ul>
<li>모든 객체는 자신의 프로토타입 객체를 가리키는 [[Prototype]] 인터널 슬롯(internal slot) 을 갖으며 상속을 위해 사용되는데, 객체인 함수 또한 [[Prototype]] 슬록을 가지는데 일반 객체와 다르게 prototype 프로퍼티도 소유한다.</li>
</ul>
<pre><code class="language-javascript">
function Person(name) {
  this.name = name;
}

var foo = new Person(&#39;Lee&#39;);

console.dir(Person); // prototype 프로퍼티가 있다.
console.dir(foo);    // prototype 프로퍼티가 없다.


/* dir(object)
매개변수로 전달된 객체의 속성을 출력한다. */
</code></pre>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/6de1fba1-6d29-4839-9a6f-074540e1368c/image.png" alt=""></p>
<p>🍎 [[Prototype]]
함수를 포함한 모든 객체가 가지고 있는 인터널 슬롯이다.
객체의 입장에서 자신의 부모 역할을 하는 프로토타입 객체를 가리키며 함수 객체의 경우 Function.prototype를 가리킨다. 여기서 Function.prototype의 프로토타입 객체는 Object.prototype(프로토타입 체인의 종점)</p>
<blockquote>
<p>console.log(Person._ <em>proto</em> _ === Function.prototype);</p>
</blockquote>
<p>🍏 prototype 프로퍼티
함수 객체만 가지고 있는 프로퍼티이다.
함수 객체가 생성자로 사용될 때 이 함수를 통해 생성될 객체의 부모 역할을 하는 객체(프로토타입 객체)를 가리킨다.</p>
<blockquote>
<p>console.log(Person.prototype === foo._ <em>proto</em> _);</p>
</blockquote>
<hr>

<h3 id="2--constructor-프로퍼티">2.  constructor 프로퍼티</h3>
<ul>
<li>프로토타입 객체는 constructor 프로퍼티를 가지고, constructor 프로퍼티는 객체의 입장에서 자신을 생성한 객체를 가리킨다.</li>
</ul>
<pre><code class="language-javascript">function Person(name) {
  this.name = name;
}

var foo = new Person(&#39;Lee&#39;);

// Person() 생성자 함수에 의해 생성된 객체를 생성한 객체는 Person() 생성자 함수이다.
console.log(Person.prototype.constructor === Person);

// foo 객체를 생성한 객체는 Person() 생성자 함수이다.
console.log(foo.constructor === Person);

// Person() 생성자 함수를 생성한 객체는 Function() 생성자 함수이다.
console.log(Person.constructor === Function);</code></pre>
<blockquote>
<p>Person()생성자에 의해 자손인 foo객체가 생성,
foo의 부모는 Person()생성자, 그래서 foo객체의 프로토 타입 객체는 Person.prototype이며, 이 Person.prototype의 constructor 프로퍼티는 Person() 생성자 함수를 가리킨다 !</p>
</blockquote>
<h3 id="3-prototype-chain-🔗">3. Prototype chain <a href="https://www.youtube.com/watch?v=wUgmzvExL_E">🔗</a></h3>
<p>-자바스크립트는 특정 객체의 프로퍼티나 메소드에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티 또는 메소드가 없다면 [[Prototype]]이 가리키는 링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티나 메소드를 차례대로 검색한다. 이것을 프로토타입 체인이라 한다.</p>
<img src="https://poiemaweb.com/img/constructor_function_prototype_chaining.png">


<pre><code class="language-javascript">function Person(name, gender) {
  this.name = name;
  this.gender = gender;
  this.sayHello = function(){
    console.log(&#39;Hi! my name is &#39; + this.name);
  };
}

var foo = new Person(&#39;Lee&#39;, &#39;male&#39;);

console.dir(Person);
console.dir(foo);

console.log(foo.__proto__ === Person.prototype);                // ① true
console.log(Person.prototype.__proto__ === Object.prototype);   // ② true
console.log(Person.prototype.constructor === Person);           // ③ true
console.log(Person.__proto__ === Function.prototype);           // ④ true
console.log(Function.prototype.__proto__ === Object.prototype); // ⑤ true</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[git status(상태)]]></title>
            <link>https://velog.io/@hi_sulhwa/git-status%EC%83%81%ED%83%9C</link>
            <guid>https://velog.io/@hi_sulhwa/git-status%EC%83%81%ED%83%9C</guid>
            <pubDate>Tue, 04 Oct 2022 14:54:01 GMT</pubDate>
            <description><![CDATA[<h2 id="☀️-git-status">☀️ git status</h2>
<p>▶️ 레포지토리의 상태를 보여주는 명령어이다!</p>
<h4 id="1-git-저장소의-상태-확인-명령어">1. Git 저장소의 상태 확인 명령어</h4>
<blockquote>
<p>$ git status</p>
</blockquote>
<h4 id="2-변경된-파일이-많을-시-유용">2. 변경된 파일이 많을 시 유용</h4>
<blockquote>
<p>$ git status -s
$ git status --short</p>
</blockquote>
<hr>

<h4 id="파일-상태-라이프-사이클-🖇">파일 상태 라이프 사이클 <a href="https://git-scm.com/book/ko/v2/Git%EC%9D%98-%EA%B8%B0%EC%B4%88-%EC%88%98%EC%A0%95%ED%95%98%EA%B3%A0-%EC%A0%80%EC%9E%A5%EC%86%8C%EC%97%90-%EC%A0%80%EC%9E%A5%ED%95%98%EA%B8%B0">🖇</a></h4>
<img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99F0DD505E292B1E2E">

<h4 id="git이-보는-파일의-4가지-상태-🖇출처">Git이 보는 파일의 4가지 상태 <a href="https://velog.io/@belper6/TIL.13-Git-Git%EC%9D%B4-%EB%B3%B4%EB%8A%94-%ED%8C%8C%EC%9D%BC%EC%9D%98-4%EA%B0%80%EC%A7%80-%EC%83%81%ED%83%9C">🖇출처</a></h4>
<p>Untracked 상태 : Git으로 버전 관리를 하지 않는 상태. 파일을 추적 관리하지 않는 상태 (파일을 한 번도 git add 해주지 않았을때의 상태)</p>
<p>Tracked 상태 : 파일이 Git에 의해 변동사항이 추적되고 있는 상태</p>
<ul>
<li><p>unmodified : git add로 파일이 Staging Area에 올라갔지만 파일에 변경은 없는 상태. Git은 이 파일을 추적 관리한다. (커밋을 하고 난 직후에는 working directory 안의 모든 파일들의 상태)</p>
</li>
<li><p>modified : 추적 관리하던 파일이 수정된 상태</p>
<pre><code>Add the file : Untracked 상태의 파일이 처음으로 git add시 Staged 상태가 된다.
Edit the file : 최신 커밋과 비교해 차이가 없는 Unmodified 상태의 파일의 내용을 수정 시 Modified 상태가 된다.
Stage the file : Modified 상태의 파일을 git add시 Staged 상태가 된다.
Remove the file : 파일을 삭제 시 git에서 인식 불가
Commit : 커밋을 하면 staging area에 있던 파일들이 커밋에 반영되고, 모든 파일들은 최신 커밋과 차이가 없어져 Unmodified 상태가 된다.</code></pre></li>
<li><p>staged : Staging Area에서 file이 staged 되어 반영된 상태. (새로 생성한 파일에 내용을 쓰고 git add를 해줄 시, 한 번이라도 커밋에 포함됐었던 파일이라도 내용을 수정하고 git add를 한 경우)</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체와 변경불가성(Immutability)]]></title>
            <link>https://velog.io/@hi_sulhwa/%EA%B0%9D%EC%B2%B4%EC%99%80-%EB%B3%80%EA%B2%BD%EB%B6%88%EA%B0%80%EC%84%B1Immutability</link>
            <guid>https://velog.io/@hi_sulhwa/%EA%B0%9D%EC%B2%B4%EC%99%80-%EB%B3%80%EA%B2%BD%EB%B6%88%EA%B0%80%EC%84%B1Immutability</guid>
            <pubDate>Fri, 30 Sep 2022 14:01:53 GMT</pubDate>
            <description><![CDATA[<h3 id="1-object-immutability-객체의-변경불가성">1. Object immutability (객체의 변경불가성)</h3>
<ul>
<li>객체 생성 이후, 더 이상 변경불가능한 상태로 만드는 디자인 패턴을 의미한다.</li>
<li>객체는 참조(Reference)를 통해 접근이 가능한 특성을 가져서 상태가 변경될 수 있다는 가능성이 있다.</li>
<li>ES5에서는 방어적 복사(defencive copy) 또는 Observer패턴으로 대처</li>
<li>불변 객체로 만들게 되면 복제나 비교를 위한 작업이 단순화 되며, 성능 개선에 도움이 되지만, 객체가 변경이 가능한 데이터가 많을 경우 오히려 좋지 않아 주의해야한다.</li>
<li>ES6에서는 이를 불변 데이터 패턴(immutable data pattern)을 쉽게 구현하는 새로운 기능 추가</li>
<li>자바스크립트에서 변경이 불가능하다는 의미는 메모리에 올라가 있는 데이터는 변경이 불가능하다는 것이고, 재할당은 가능하다.</li>
</ul>
<br>

<h3 id="2-immutable-value변경-불가능한-값-vs-mutable-value-변경-가능한-값">2. immutable value(변경 불가능한 값) vs mutable value (변경 가능한 값)</h3>
<h4 id="①-원시타입-값-값에-의한-전달---immutable-value">① 원시타입 값 (값에 의한 전달) -&gt; immutable value</h4>
<p><span style="color:purple"> 📌 원시타입 (primitive data type)</span></p>
<ul>
<li>Boolean</li>
<li>null</li>
<li>undefined</li>
<li>Number</li>
<li>String</li>
<li>Symbol(New in ECMAScript 6)</li>
</ul>
<blockquote>
<p>재할당 이외의 원시 값인 변수 값을 변경할 수 있다면 예기치않게 변수값이 변경될 수 있다는 것이고, 
이는 값의 변경, 즉 상태 변경을 추적하기 어렵게 만든다. 
따라서예상가능하고 신뢰할 수 있는 코드를 위해 원시 값은 불변</p>
</blockquote>
<br>

<h4 id="②-객체참조타입-값-참조에-의한-전달---mutable-value">② 객체(참조)타입 값 (참조에 의한 전달) -&gt; mutable value</h4>
<p><span style="color:darkgreen"> 📌 객체타입 (primitive data type)</span></p>
<ul>
<li>Object</li>
<li>Array</li>
<li>Function</li>
</ul>
<blockquote>
<p>예기치 못한 변경이 발생하지 않도록 바로 값을 담는것이 아닌, 메모리 공간 주소를 담아 한 단계 더 거쳐 실제 객체에 접근함으로써 명확하고 신뢰성이 확보된다.</p>
</blockquote>
<hr>
변수 : 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해서 붙인 이름
값 : 변수에 저장된 데이터로서 표현식이 평가되어 생성된 결과

<p><em><strong>변경 불가능하다는 것은 &#39;변수&#39;가 아닌 &#39;값&#39;이다.</strong></em></p>
<pre><code class="language-javascript">(String)
var statement = &#39;I am an immutable value&#39;;
var otherStr = statement.slice(8, 17);

console.log(otherStr);   // &#39;immutable&#39;
console.log(statement);  // &#39;I am an immutable value&#39;
---------------------------------------------
(Array)
var arr = [];
console.log(arr.length); // 0

var v2 = arr.push(2);    // arr.push()는 메소드 실행 후 arr의 length를 반환
console.log(arr.length); // 1  
---------------------------------------------
 var user1 = {
  name: &#39;Lee&#39;,
  address: {
    city: &#39;Seoul&#39;
  }
};

var user2 = user1; // 변수 user2는 객체 타입이다.

user2.name = &#39;Kim&#39;;

console.log(user1.name); // Kim
console.log(user2.name); // Kim


✔️ 별도로 새로운 메모리에 할당 되는 것이 아니라 같은 곳을 바라보기 때문에 동시에 변경되고 의도된 코딩이 아니라면 막아야한다.</code></pre>
<h3 id="3-불변-데이터-패턴-immutable-data-pattern">3. 불변 데이터 패턴 (immutable data pattern)</h3>
<h4 id="①-objectassign---객체의-방어적-복사defensive-copy">① Object.assign - 객체의 방어적 복사(defensive copy)</h4>
<ul>
<li>타킷 객체로 소스 객체의 프로퍼티를 복사한다. </li>
<li>소스 객체의 프로퍼티와 동일한 프로퍼티를 가진 타켓 객체의 프로퍼티들은 소스 객체의 프로퍼티로 덮어쓰기된다. </li>
<li>리턴값으로 타킷 객체를 반환한다. ES6에서 추가된 메소드이며 Internet Explorer는 지원하지 않는다.</li>
</ul>
<blockquote>
<p>Object.assign(target, ...sources)</p>
</blockquote>
<pre><code class="language-javascript">const user1 = {
  name: &#39;Lee&#39;,
  address: {
    city: &#39;Seoul&#39;
  }
};

// 새로운 빈 객체에 user1을 copy한다.
const user2 = Object.assign({}, user1);
// user1과 user2는 참조값이 다르다.
console.log(user1 === user2); // false

user2.name = &#39;Kim&#39;;
console.log(user1.name); // Lee
console.log(user2.name); // Kim

// 객체 내부의 객체(Nested Object)는 Shallow copy된다.
console.log(user1.address === user2.address); // true

user1.address.city = &#39;Busan&#39;;
console.log(user1.address.city); // Busan
console.log(user2.address.city); // Busan

/*
user2가 할당한 객체가 수정되어도 user1의 참조 객체는 수정되지 않는다. 
user1이 참조한 객체를 수정하면 user2에 할당된 객체도 수정된다. 
*/
</code></pre>
<hr>

<h4 id="②-objectfreeze---불변객체화를-통한-객체-변경-방지">② Object.freeze - 불변객체화를 통한 객체 변경 방지</h4>
<ul>
<li>타킷 객체로 소스 객체의 프로퍼티를 복사한다. </li>
<li>소스 객체의 프로퍼티와 동일한 프로퍼티를 가진 타켓 객체의 프로퍼티들은 소스 객체의 프로퍼티로 덮어쓰기된다. </li>
<li>리턴값으로 타킷 객체를 반환한다. ES6에서 추가된 메소드이며 Internet Explorer는 지원하지 않는다.</li>
</ul>
<blockquote>
<p>Object.freeze(obj)</p>
</blockquote>
<pre><code class="language-javascript">const user1 = {
  name: &#39;Lee&#39;,
  address: {
    city: &#39;Seoul&#39;
  }
};

// Object.assign은 완전한 deep copy를 지원하지 않는다.
const user2 = Object.assign({}, user1, {name: &#39;Kim&#39;});

console.log(user1.name); // Lee
console.log(user2.name); // Kim

Object.freeze(user1); //프로퍼티 변경 불가능

user1.name = &#39;Kim&#39;; // 무시된다!
user.adress.city = &#39;Busan&#39;; // 변경된다!

console.log(user1); // { name: &#39;Lee&#39;, address: { city: &#39;Seoul&#39; } }

console.log(Object.isFrozen(user1)); // true

--------------------------------------------------------- 

(객체 내부의 객체(Nested Object)는 변경가능)
const user = {
  name: &#39;Lee&#39;,
  address: {
    city: &#39;Seoul&#39;
  }
};

Object.freeze(user);

user.address.city = &#39;Busan&#39;; // 변경된다!
console.log(user); // { name: &#39;Lee&#39;, address: { city: &#39;Busan&#39; } }

---------------------------------------------------------

(Nested Object에 대해서는 하나하나 중첩 안의 객체들에 대해서 freeze로 처리해 주어야 한다.  Deep freeze)  

function deepFreeze(object) {
  //Object.getOwnPropertyNames() 메서드:객체에 정의된 속성명을 추출하여 배열로 반환한다
  let props = Object.getOwnPropertyNames(object); //객체 key를 props에 저장

  // 스스로를 동결하기 전에 속성을 동결
  for(let name of props){ //배열의 length만큼 반복
      let value=object[name];

    object[name]=value&amp;&amp;typeof value ===&quot;object&quot;?
        deppFreeze(value):value;
  }
  return Object.freeze(object);
}

const user = {
  name: &#39;Lee&#39;,
  address: {
    city: &#39;Seoul&#39;
  }
};

deepFreeze(user);

user.name = &#39;Kim&#39;;           // 무시된다
user.address.city = &#39;Busan&#39;; // 무시된다

console.log(user); // { name: &#39;Lee&#39;, address: { city: &#39;Seoul&#39; } }


/*
user2가 할당한 객체가 수정되어도 user1의 참조 객체는 수정되지 않는다. 
user1이 참조한 객체를 수정하면 user2에 할당된 객체도 수정된다. 
*/

</code></pre>
<hr>

<p><span style="color:red"> Object.assign과 Object.freeze을 사용하여 불변 객체를 만드는 방법은 번거러울 뿐더러 성능상 이슈가 있어서 큰 객체에는 사용하지 않는 것이 좋다.</span></p>
<p>또 다른 대안으로 Facebook이 제공하는 Immutable.js를 사용하는 방법이 있다.</p>
<ul>
<li>Immutable.js는 List, Stack, Map, OrderedMap, Set, OrderedSet, Record와 같은 영구 불변 (Permit Immutable) 데이터 구조를 제공한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[js 제어문]]></title>
            <link>https://velog.io/@hi_sulhwa/js-%EC%A0%9C%EC%96%B4%EB%AC%B8</link>
            <guid>https://velog.io/@hi_sulhwa/js-%EC%A0%9C%EC%96%B4%EB%AC%B8</guid>
            <pubDate>Tue, 20 Sep 2022 13:49:39 GMT</pubDate>
            <description><![CDATA[<h2 id="🌱-반복문loop-statement">🌱 반복문(Loop statement)</h2>
<p>💡 반복문이란?</p>
<ul>
<li>주어진 조건식(conditional expression)의 평가 결과가 참인 경우 코드 블럭을 실행한다. 그 후 조건식을 다시 검사하여 여전히 참인 경우 코드 블록을 다시 실행, 조건식이 거짓일 때까지 반복된다.</li>
</ul>
<p>💡 반복문 사용이유</p>
<ul>
<li>여러번 반복되는 작업을 일일이 하나씩 작업을 하게된다면 실수가 일어날 수도 있기 때문에 같은 계산을 여러번 하는 것을 한 번의 코딩으로 해결할 수 있도록 해주는 반복문의 사용으로 실수를 없앨 수 있다.</li>
</ul>
<hr>

<h3 id="1-for-문">1. for 문</h3>
<p>🔗 기본 구조</p>
<pre><code class="language-javascript">for (initialization; condition; afterthought)
  statement
</code></pre>
<ul>
<li>*<em>initialization(초기화식) *</em>
➡️ 루프가 시작되기 전에 한 번 평가 되는 표현식( 할당 표현식 포함 ) 또는 변수 선언으로, 일반적으로 카운터 변수를 초기화하는 데 사용된다. var이나 let키워드를 사용해서 새로운 변수를 선언할 수도 있다.</li>
<li><strong>condition(조건식)</strong> 
➡️ 각 루프 반복 전에 평가하는 표현식으로, 표현식이 true 로 평가되면 statement가 실행되고, false 로 평가 되면 루프를 종료하고 구문 뒤의 첫 번째 명령문으로 이동한다.   </li>
<li><strong>afterthought(증감식)</strong> 
➡️ 반복문을 한 번 실행할 때마다 반복문에 사용되는 변수를 업데이트</li>
<li>*<em>statement(조건식이 참일 시 반복 실행 될 문) *</em>
➡️ 조건이 true로 평가되는 동안 실행되는 명령문이다.</li>
</ul>
<p><span style='background-color: #dcffe4'> for 문의 초기화식, 조건식, 증감식은 모두 옵션으로, 반드시 사용할 필요는 없다. 어떤 식도 선언하지 않으면 무한 루프가 된다. </span>  </p>
<p>1️⃣ for문</p>
<pre><code class="language-javascript">for (let i = 0; i &lt; 3; i++) {
  console.log(i);
}

//i = 0,1,2

for (let i = 2; i &gt;= 0; i--) {
  console.log(i);
}

//i = 2,1,0
</code></pre>
<p>2️⃣ 중첩 for문</p>
<pre><code class="language-javascript">for (let i=2; i&lt;=9; i++)  {
  for(let j=1; j&lt;=9; j++)  {
    console.log(i+&quot;x&quot;+j+&quot;=&quot;+i*j);
    }
}

2x1=2
2x2=4
2x3=6
2x4=8
2x5=10
2x6=12
.
. 
9x9=81</code></pre>
<p>👉 i for문은 다음 반복으로 넘어가기 위해 안쪽에 있는 j for문을 마쳐야한다. j for문을 모두 마치면 i값이 1 증가하고 다음반복으로 넘어간다. console.log는 <strong>(i for문 반복수)*(j for문 반복수)</strong>만큼 출력된다.</p>
<p><span style='background-color: #f0c0c8'>&nbsp;i=2 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;j=123456789&nbsp;</span>
<span style='background-color: #f0c0c8'>&nbsp;i=3 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;j=123456789&nbsp;</span></p>
<h3 id="2-while-문">2. while 문</h3>
<p>for문은 반복 횟수가 명확할 때 주로 사용하고, while문은 반복 횟수가 불명확할 때 주로 사용한다.
while (조건문) 형식으로 이 조건이 참이기만 하면 무조건 반복한다.</p>
<p>🔗while 예제</p>
<pre><code class="language-javascript">let i = 0;
while (i &lt; 3) {
  console.log(i);
  i++;
}

//0,1,2</code></pre>
<p>🔗무한루프</p>
<pre><code class="language-javascript">while (true) {
  console.log(&quot;test&quot;);
}
</code></pre>
<p><img src="https://velog.velcdn.com/images/hi_sulhwa/post/e78f9ba9-38b3-4100-8668-332123b20e14/image.png" alt=""></p>
<blockquote>
<p>무한루프는 피해야한다.</p>
</blockquote>
<pre><code class="language-javascript">let i = 0;
while (true) {
  console.log(i);
  i++;
  if (i === 5 ) {
    console.log(&quot;THE END&quot;);
    break;
  }
}
//0,1,2,3,4,THE END</code></pre>
<blockquote>
<p>코드 블럭 탈출 조건을 if 문에 부여하고 break 문으로 코드 블럭을 탈출한다.</p>
</blockquote>
<h3 id="3-dowhile-문">3. do..while 문</h3>
<p>코드 블록을 먼저 실행하고 조건식을 평가한다. 코드블록은 무조건 한 번 이상 실행된다.</p>
<pre><code class="language-javascript">var count = 0;

// count가 3보다 작을 때까지 코드 블록을 계속 반복 실행한다.
do {
  console.log(count);
  count++;
} while (count &lt; 3); // 0 1 2</code></pre>
<h2 id="☘️-break-문">☘️ break 문</h2>
<p>레이블 문, 반복문, switch 문의 코드 블록을 탈출한다.
레이블 문, 반복문, switch문의 코드 블록 외에 break 문을 사용하면 문법 에러가 발생한다.
break문은 반복문, switch 문에서도 사용할 수 있는데 이 경우 레이블 식별자를 지정하지 않는다.
break문은 반복문을 더 이상 진행하지 않아도 될 때 불필요한 반복을 회피할 수 있어 유용하다.</p>
<img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKAlVf%2Fbtq0zdNgph8%2FgzRvlhklUC20OnQiFK8MC0%2Fimg.png">

<p><a href="https://www.programiz.com/javascript">이미지출처</a></p>
<pre><code class="language-javascript">for(let i = 1; i &lt;= 5; i++){
  if(i == 3){
    break;
  }
  console.log(i);
}

//1,2</code></pre>
<blockquote>
<p>i가 3일때 break를 실행하면 즉시 for문을 빠져나온다.</p>
</blockquote>
<pre><code class="language-javascript">let i = 0;
let j = 0;

while (i &lt; 5) {
  i++;

  if (j == 3) {
    break;
  }

  j += i;
}

console.log(j);

//j = 3 (1+2)</code></pre>
<blockquote>
<p>i가 3일때 break를 실행하게 되면 즉시 while을 빠져나온다.</p>
</blockquote>
<h4 id="➕-레이블-문">➕ 레이블 문</h4>
<p>프로그램에서 다른 곳으로 참조할 수 있도록 식별자로 구문을 제공한다.
중첩된 for 문 외부로 탈출할 때 유용하지만, 그 밖의 경우에는 일반적으로 권장하지 않는다. 
그 이유는 프로그램의 흐름이 복잡해져서 가독성이 나빠지고 오류를 발생시킬 가능성이 높아지기 때문이다.</p>
<p>🔗 구조</p>
<pre><code class="language-css"> label :   
       statement</code></pre>
<blockquote>
<p>label : js에서 사용가능한 식별자면 모두 가능
statement : break는 모든 레이블 구문에서 사용가능, continue는 반복레이블 구문에서만 사용가능</p>
</blockquote>
<p>🔗 레이블 붙인 블록에 break 사용</p>
<pre><code class="language-javascript">foo: {
  console.log(&#39;september&#39;);
  break foo;  //foo 레이블 블록문 탈출
  console.log(&#39;Autumn&#39;);
}
console.log(&#39;october&#39;);

//september
//october</code></pre>
<p>🔗 중첩 for문</p>
<pre><code class="language-javascript">outer: for (let i = 0; i &lt; 3; i++) {
  for (let j = 0; j &lt; 3; j++) {
    // i + j === 3이면 외부 for 문을 탈출한다.
    if (i + j === 3) break outer;
  }
}

console.log(&#39;Done!&#39;);

//Done!</code></pre>
<blockquote>
<p>중첩된 for 문의 내부 for 문에서 break 문을 실행하면 내부 for 문을 탈출하여 외부 for 문으로 진입한다. 이때 내부 for 문이 아닌 외부 for 문을 탈출하려면 레이블 문을 사용한다.</p>
</blockquote>
<br>

<h2 id="🍀-continue-문">🍀 continue 문</h2>
<p>continue 문은 반복을 끝내지 않고 continue문 아래에 있는 실행해야 하는 문장들을 건너 뛰고, 다음 루프를 실행한다. 
반복문을 완전히 종료하는 것이 아니라 for문이라면 업데이트 표현식으로, while문의 경우 다시 조건으로 넘어간다.</p>
<img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6I6Ya%2Fbtq0AqE4Go4%2FnN4t2xJKgubCBx1kSqAuYK%2Fimg.png">

<p><a href="https://www.programiz.com/javascript">이미지출처</a></p>
<pre><code class="language-javascript">for(let i = 1; i &lt;= 5; i++){
  if(i == 3){
    continue;
  }
  console.log(i);
}


//1,2,4,5</code></pre>
<blockquote>
<p>i가 3일때 continue실행하게 되면 console.log를 거치지 않고 넘어가고 i++인 4로 루프를 돈다.</p>
</blockquote>
<pre><code class="language-javascript">let i = 0;
let j = 0;

while (i &lt; 5) {
  i++;

  if (i == 3) {
    continue;
  }

  j += i;
}

console.log(j);

//j = 12 (1+2+4+5)</code></pre>
<blockquote>
<p>i가 3일때 continue실행하게 되면 j에 값을 더하지 않고 while로 돌아간다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[image sprite 이미지스프라이트]]></title>
            <link>https://velog.io/@hi_sulhwa/image-sprite-%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%8A%A4%ED%94%84%EB%9D%BC%EC%9D%B4%ED%8A%B8</link>
            <guid>https://velog.io/@hi_sulhwa/image-sprite-%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%8A%A4%ED%94%84%EB%9D%BC%EC%9D%B4%ED%8A%B8</guid>
            <pubDate>Sun, 18 Sep 2022 11:09:45 GMT</pubDate>
            <description><![CDATA[<h2 id="🌌-image-sprite이미지스프라이트">🌌 image sprite(이미지스프라이트)</h2>
<h3 id="1-image-sprite란">1. image sprite란?</h3>
<ul>
<li>배경이미지를 적용할때 각각의 요소에 들어가는 배경이미지를 따로따로 만들지 않고, 
여러 가지의 이미지를 하나의 이미지 파일 안에 배치하여 이미지 로드 부담을 줄이는 방법이다.</li>
</ul>
<img src="https://s.pstatic.net/static/www/img/uit/sp_main_dba1af.png">

<p><a href="https://s.pstatic.net/static/www/img/uit/sp_main_dba1af.png">이미지 출처</a></p>
<h3 id="2-image-sprite-사용법">2. image sprite 사용법</h3>
<ul>
<li>방법 1: background: url( ) 값 다음에 x, y 2개 값으로 지정</li>
<li>방법 2: background-position 속성으로 지정 </li>
</ul>
<br>

<h4 id="⭐️방법-1">⭐️방법 1</h4>
<pre><code class="language-css">.imgsprite {
  width: 314px; //원하는 이미지의 가로좌표 
  height: 314px; //원하는 이미지의 세로좌표

  background: url(    ) no-repeat;  //이미지 주소
}</code></pre>
<p>결과 ⬇️
<img src="https://velog.velcdn.com/images/hi_sulhwa/post/124999b5-48ae-49a1-b5e7-b0c801e94e06/image.png" alt=""></p>
<h4 id="⭐️방법-2">⭐️방법 2</h4>
<p>background-position을 사용하며, 가로이동(→)과 세로이동(↓)은 -값을 줘야 한다!</p>
<pre><code class="language-css">.tool {
  width: 130px; //원하는 이미지 가로길이
  height: 80px; //원하는 이미지 세로길이

  background: url(  ) no-repeat; //이미지 주소
  background-position: -315px -225px; //원하는 이미지의 위치까지 가로,세로 이동
}</code></pre>
<p>결과 ⬇️
<img src="https://velog.velcdn.com/images/hi_sulhwa/post/e9f677ec-1f16-4c9b-9d27-86a42092cbda/image.png" alt=""></p>
<p>*<em>💡 이미지스프라이트 도움 사이트 💡<br>*</em>
  🔗 <a href="https://www.toptal.com/developers/css/sprite-generator/">이미지 스프라이트 생성사이트</a> 
 🔗 <a href="http://www.spritecow.com/">이미지 좌표확인 사이트</a></p>
<hr>

<h3 id="3-image-sprite-장단점">3. image sprite 장단점</h3>
<p><span style='background-color: #fff5b1'> 장점 </span></p>
<ol>
<li>이미지를 다운받기 위한 서버 요청을 단 몇 번으로 줄일 수 있어 웹페이지 로딩 시간을 줄여준다.</li>
<li>모바일 환경과 같이 한정된 자원을 사용하는 플랫폼(platform)에서는 웹 페이지의 로딩 시간을 단축해주는 효과가 있다.</li>
<li>많은 이미지 파일을 관리하는 대신 몇 개의 스프라이트 이미지(sprite image) 파일만을 관리하면 되므로 매우 간편하다.</li>
<li>hover 시 나타나는 이미지의 경우, 이미 스프라이트 이미지를 로딩했기에 로딩 지연이 없다.</li>
</ol>
<br>

<p><span style='background-color: #dcffe4'> 단점 </span></p>
<ol>
<li>이미지 용량이 클 시 로딩이 매우 오래걸리는 문제점이 있다.</li>
<li>해당 이미지의 정확한 position을 알아야한다.</li>
<li>스프라이트 이미지 내의 이미지를 변경할 시 단일파일보다 번거롭다.</li>
</ol>
<p><a href="http://www.tcpschool.com/css/css_basic_imageSprites">🌠</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript 기본문법]]></title>
            <link>https://velog.io/@hi_sulhwa/JavaScript-%EA%B8%B0%EB%B3%B8%EB%AC%B8%EB%B2%95</link>
            <guid>https://velog.io/@hi_sulhwa/JavaScript-%EA%B8%B0%EB%B3%B8%EB%AC%B8%EB%B2%95</guid>
            <pubDate>Sat, 17 Sep 2022 09:00:41 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트엔 <strong>여덟 가지 *<em>자료형이 있습니다. 
이 중 *</em>일곱 개</strong>는 오직 하나의 데이터(문자열, 숫자 등)만 담을 수 있어** &#39;원시형(primitive type)&#39;**이라 부릅니다.</p>
<h4 id="✔️-원시형primitive-type">✔️ 원시형(primitive type)</h4>
<ul>
<li>Boolean : 논리적인 요소, true와 false값이 있다.</li>
<li>null : 빈 값</li>
<li>undefined : 값을 할당하지 않은 변수가 가지는 값</li>
<li>Number : 숫자형으로 정수와 부동 소수점, 무한대 및 NaN(숫자가 아님)값을 포함한다.</li>
<li>String : 문자열</li>
<li>Symbol</li>
</ul>
<h4 id="✔️-참조형-reference-type">✔️ 참조형 (Reference Type)</h4>
<p>Reference 타입이라고도 하며, Object 클래스 뿐만 아니라, 배열과 함수, 사용자 정의 클래스, ES6에서는 Map, Set, WeakMap, WeakSet 등도 추가</p>
<hr>


<p><strong>- 차이점</strong><br>기본형에는 바로 값을 그대로 할당한다는 것이고
참조형에는 값이 저장된 주소값을 할당(참조)한다는 것</p>
<div align="center">
<img src="https://t1.daumcdn.net/cfile/tistory/991FB43F5D65F9AD21">
</div>
<br>
<hr>


<h2 id="1-변수">1. 변수</h2>
<p>변수란 메모리 주소(Memory address)에 접근하기 위해 사람이 이해할 수 있는 언어로 지정한 식별자(identifier)</p>
<ul>
<li>변수를 선언할 때 var 키워드를 사용한다. 할당 연산자 =는 변수에 값을 할당하기 위해 사용</li>
</ul>
<ul>
<li>기본형식
var 변수명</li>
</ul>
<p>var 변수명 = 기본값</p>
<p>변수명 = 기본값</p>
<pre><code class="language-javascript">var example;  //변수 (example)의 선언
example : 1;  //example에 정수값 할당</code></pre>
<h2 id="2-값">2. 값</h2>
<p>&#39;값&#39; 은 프로그램에 의해 조작될 수 있는 대상</p>
<ul>
<li>변수를 선언할 때 var 키워드를 사용한다. 할당 연산자 =는 변수에 값을 할당하기 위해 사용</li>
</ul>
<pre><code class="language-javascript">var example = &#39;Hello&#39;;  //example변수에 문자열 리터럴 &#39;Hello&#39; 값으로 할당</code></pre>
<ul>
<li>데이터 타입(Data Type) - 프로그래밍 언어에서 사용할 수 있는 값의 종류</li>
<li>변수(Variable) - 값이 저장된 메모리 공간의 주소를 가리키는 식별자(identifier)   </li>
<li>리터럴(literal) - 소스코드 안에서 직접 만들어 낸 상수 값 자체를 말하며 값을 구성하는 최소 단위</li>
</ul>
<pre><code class="language-javascript">// 숫자 리터럴
12.34
12345

// 문자열 리터럴
&#39;Hi&#39;
&quot;Welcome&quot;

// 불리언 리터럴
true
false

// null 리터럴
null

// undefined 리터럴
undefined

// 객체 리터럴
{ name: &#39;Tom&#39;, gender: &#39;male&#39; }
{ name: &#39;Jerry&#39;, gender: &#39;female&#39; }

// 배열 리터럴
[ 1, 2, 3 ]

// 정규표현식 리터럴
/ab+c/

// 함수 리터럴
function() {}</code></pre>
<p>🫥 자바스크립트는 C나 Java외는 다르게 변수를 선언 시 <strong>데이터 타입을 미리 지정</strong>하지 않고, 변수에 <strong>할당된 값의 타입에 의해 동적으로 변수의 타입이 결정</strong>. 이를 동적 타이핑이라 하며 자바스크립트가 다른 프로그래밍 언어와 구별되는 특징 중 하나이다.</p>
<pre><code class="language-c">// 1byte 정수형: -128 ~ 127
char c;

// 4byte 정수형: -2,124,483,648 ~ 2,124,483,647
int num;</code></pre>
<hr>

<h2 id="3-연산자">3. 연산자</h2>
<pre><code class="language-javascript">// 산술 연산자
var area = 3 * 4; // 12

// 문자열 연결 연산자
var str = &#39;Nice to &#39; + &#39;meet you&#39;; // &quot;Nice to meet you&quot;

// 할당 연산자
var animal = &#39;dog&#39;; // &quot;dog&quot;

// 비교 연산자
var foo = 7 &gt; 5; // true

// 논리 연산자
var bar = (5 &gt; 3) &amp;&amp; (2 &lt; 4);  // true (and연산 둘다 충족해야 true)

// 타입 연산자
var type = typeof &#39;study&#39;; // &quot;string&quot; 문자열로 이루어짐

// 인스턴스 생성 연산자
var today = new Date(); //날짜 생성</code></pre>
<blockquote>
<p>피연산자의 타입은 반드시 일치할 필요는 없다. 이때 자바스크립트는 암묵적 타입 강제 변환을 통해 연산을 수행한다.<br>
암묵적 타입 변환이 발생하면 문자열, 숫자, 불리언과 같은 원시 타입 중 하나로 타입을 자동 변환한다.</p>
</blockquote>
<pre><code class="language-javascript">var foo = 1 + &#39;10&#39;; // &#39;110&#39;  
/*예제의 + 연산자는 피연산자 중 하나 이상이 문자열이므로 문자열 연결 연산자로 동작, 문자열 연결 연산자의 역할은 문자열 값을 만든다*/

var bar = 1 * &#39;10&#39;; // 10

var ex = 3 * &#39;hi&#39; //NaN
/*자바스크립트 엔진은 산술 연산자 표현식을 평가하기 위해 산술 연산자의 피연산자 중에서 숫자 타입이 아닌 피연산자를 숫자 타입으로 암묵적 타입 변환한다. 이때 피연산자를 숫자 타입으로 변환할 수 없는 경우는 산술 연산을 수행할 수 없으므로 NaN을 반환한다.*/
</code></pre>
<p>⭐️ 주석
// 는 한줄주석
/* */ 는 여러줄 주석</p>
<h2 id="4-키워드">4. 키워드</h2>
<p>자바스크립트에서 특정한 목적을 위해 사용하는 단어</p>
<ul>
<li>var = 변수를 선언할 때 사용하는 키워드</li>
</ul>
<h2 id="5-예약어">5. 예약어</h2>
<p>프로그램을 작성할 때, 변수명, 함수명 등 이름으로 사용할 수 없는 단어
이미 자바스크립트에서 쓰임이 정해져있는 키워드.</p>
<img src="https://t1.daumcdn.net/cfile/tistory/224D4F415951D8410E">




<h2 id="6-문">6. 문</h2>
<ul>
<li>각각의 명령을 문(statement)이라 하며 문이 실행되면 무슨 일인가가 일어나게 된다.</li>
<li>문은 리터럴, 연산자(Operator), 표현식(Expression), 키워드(Keyword) 등으로 구성되며 세미콜론( ; )으로 끝나야 한다.</li>
</ul>
<pre><code class="language-javascript">// 함수
function example(x, y) {
  return x + y;
}

// if 문
if(x &gt; 0) {
  // do something
}

// for 문
for (var i = 0; i &lt; 10; i++) {
  // do something
}</code></pre>
<h2 id="7-표현식">7. 표현식</h2>
<p>값(리터럴), 변수, 객체의 프로퍼티, 배열의 요소, 함수 호출, 메소드 호출, 피연산자와 연산자의 조합은 모두 표현식이며 하나의 값으로 평가(Evaluation)된다.</p>
<pre><code class="language-javascript">// 표현식
1             // 1
1 * 10        // 10
1 * 10 &gt; 10   // false
(10 * 10 &gt; 10) &amp;&amp; (3 * 10 &lt; 100)  // true</code></pre>
<h2 id="8-함수">8. 함수</h2>
<p>어떤 작업을 수행하기 위해 필요한 문(statement)들의 집합을 정의한 코드 블록이다. 함수는 이름과 매개변수를 갖으며 필요한 때에 호출하여 코드 블록에 담긴 문들을 일괄적으로 실행할 수 있다.</p>
<pre><code class="language-javascript">// 함수의 정의(함수 선언문)
function square(number) {
  return number * number;
}

// 함수의 호출
square(2); // 4
square(3); // 9
square(4); // 16</code></pre>
<blockquote>
<p>함수는 호출에 의해 실행되는데 한번만 호출할 수 있는 것이 아니라 여러번 호출할 수 있다.</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>