<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jin_jin_dev.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Mon, 15 May 2023 08:18:18 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jin_jin_dev.log</title>
            <url>https://velog.velcdn.com/images/jin_jin_dev/profile/867a6f39-bcba-431c-a7bd-f67ba730ff67/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jin_jin_dev.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jin_jin_dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[TIL] IaaS PaaS SaaS에 대하여]]></title>
            <link>https://velog.io/@jin_jin_dev/TIL-IaaS-PaaS-SaaS%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@jin_jin_dev/TIL-IaaS-PaaS-SaaS%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Mon, 15 May 2023 08:18:18 GMT</pubDate>
            <description><![CDATA[<h2 id="as-a-service">as a service</h2>
<p>IaaS, PaaS, SaaS의 공통인 aaS는 as a service라는 단어의 준말입니다. 직역하면 &quot;서비스처럼&quot; 이라는 뜻이죠.</p>
<p>오늘은 위 세 가지 키워드에 대해 알아보는 시간을 가져보도록 하겠습니다!</p>
<h2 id="iaas">IaaS</h2>
<p>Infrastructure as a Service.
위 문장을 직역하면 &quot;인프라를 서비스처럼 하겠다&quot;입니다.</p>
<p>기업에서 무언가를 서비스하려면 데이터를 저장할 서버가 필요했으며 기존에는 대부분 IDC를 이용해 이를 해결했습니다.</p>
<p>IDC는 Internet Data Center의 준말로 인터넷과 연결된 데이터가 보관된 서버를 모아둔 시설이라고 보면 됩니다.</p>
<p>일반적인 기업에서 서버를 독자적으로 운영하기에는 마련해야 하는 공간과 서버가 정상적인 효율을 낼 수 있도록 온도, 습도 등 유지를 위한 비용이 많이 들어가게 됩니다.</p>
<p>이러한 회사들이 서버가 모여있는 데이터 센터&quot;인 IDC에 비용을 지불하고 물리적인 서버를 할당받아 사용해왔습니다. 
돈을 받고 서버를 사용하게 해준다..? 뭔가 서비스한다기보다는 렌탈 사업의 느낌이 살짝 듭니다.</p>
<p>반면, Cloud 사업 중에는 Instance라고 하는 가상 서버를 제공 받을 수 있습니다.
원하는 성능과 용량의 인스턴스를 설정하면 2~3분 안에 클라우드 서버를 구축할 수 있게되죠. 마치 서비스를 제공 받는 것처럼요!</p>
<p>IaaS에서 제공 받는 Instance는 완전히 비어있는 서버 한 대를 제공 받는다고 생각하시면 되겠습니다.
여기에 Web을 설치하든 DB를 설치하든 그것은 자유입니다.</p>
<p>이처럼 내가 원하는 방식으로 인프라를 구축할 수 있기 때문에 IaaS라는 명칭을 사용합니다.</p>
<h2 id="paas">PaaS</h2>
<p>Platform as a Service.
IaaS와는 다르게 플랫폼이 구성되어있는 Instance입니다.
대표적으로 AWS Lambda, Web, DB 등을 플랫폼이라고 할 수 있겠습니다.</p>
<p>Instance가 실질적인 서버의 기능을 하려면 위에 말씀드린 플랫폼들이 구축이 되어있어야 하는데요, PaaS에서는 이런 플랫폼마저도 제공해줄 수 있는 서비스입니다.</p>
<p>PaaS 사용자는 서비스 외적인 부분에 신경 쓸 필요가 없고, 오로지 애플리케이션 개발과 비즈니스에만 집중할 수 있습니다.
애플리케이션을 개발 및 업데이트해서 배포하면 이미 구축된 플랫폼 서버와 알아서 연동되기 때문에 사용자는 서버가 어디 있는지 알 필요가 없게 됩니다.</p>
<h2 id="saas">SaaS</h2>
<p>Software as a Service.
Software를 서비스처럼 사용합니다. 
대표적으로 google Docs, Microsoft 365 같은 서비스가 있습니다.
클라우드 제공업체가 애플리케이션을 개발 및 유지하고, 사용자는 그냥 갖다 쓰기만 하면 되는 서비스입니다.</p>
<hr>
<h2 id="마치며">마치며..</h2>
<p>평소 헷갈리던 클라우드 서비스에 대해 정리해보았습니다. 어디 기록을 해두지 않으니 머릿속에서 개념들이 정리되지 않아 매번 찾아봤었는데요.
이번 기회에 확실히 개념이 잡힌 것 같습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[wecode] 2차 프로젝트 회고록]]></title>
            <link>https://velog.io/@jin_jin_dev/wecode-2%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@jin_jin_dev/wecode-2%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Sun, 30 Apr 2023 11:58:46 GMT</pubDate>
            <description><![CDATA[<blockquote>
</blockquote>
<p>위코드에서 2차 프로젝트를 진행하였습니다. 1차 때와 마찬가지로 특정 웹사이트를 지정해 클론코딩을 하는 것이었고 금번 프로젝트를 진행하면서 느꼈던 것들에 대해 회고하기 위해 글을 작성하게 되었습니다.</p>
<h2 id="about-our-team">About our Team</h2>
<ul>
<li><p>프로젝트명 : Wevre (위브루)</p>
</li>
<li><p>인원 : 총 4명 (Front : 2, Back : 2)</p>
</li>
<li><p>GitHub Repository
=&gt; Front-end : <a href="https://github.com/wecode-bootcamp-korea/43-2nd-Wevre-frontend">https://github.com/wecode-bootcamp-korea/43-2nd-Wevre-frontend</a>
=&gt; Back-end : <a href="https://github.com/wecode-bootcamp-korea/43-2nd-Wevre-backend">https://github.com/wecode-bootcamp-korea/43-2nd-Wevre-backend</a></p>
</li>
<li><p>프로젝트 내용 : 한정판 경매 사이트인 Kream을 모델링하여 예술품을 경매하는 사이트로 재탄생</p>
</li>
<li><p>Notion : <a href="https://github.com/wecode-bootcamp-korea/43-2nd-Wevre-frontend">https://github.com/wecode-bootcamp-korea/43-2nd-Wevre-frontend</a></p>
</li>
<li><p>Trello : <a href="https://trello.com/b/DnBSCbjV/wecode-2st-project-team-wevre">https://trello.com/b/DnBSCbjV/wecode-2st-project-team-wevre</a></p>
</li>
</ul>
<h2 id="about-our-project">About our Project</h2>
<p>이번 2차 프로젝트에서 우리가 참고할 웹사이트는 다름 아닌 Kream이라는 쇼핑몰이었습니다.
Kream을 분석해보니 의류, 신발, 가방 등 한정판 상품을 거래하는 사이트로써 구매자와 판매자가 대화를 할 필요도, 직접 만나서 거래할 필요도 없이 웹사이트에 판매하고자 하는 물건을 올리면 구매자는 경매를 통해 이 상품을 구매하는 프로세스로 되어있었습니다. 물론 상품이 한정판 정품인지 검증하는 자체 기관이 존재하여 고객에게 신뢰를 주고있었고, 익명으로 간편하게 사고 팔 수 있는 것이 장점인 쇼핑몰이었습니다.</p>
<p>저희는 이런 Kream의 시스템중 &#39;경매&#39;와 &#39;한정판&#39;이라는 두가지 특성을 차용하여 예술품 경매 사이트를 구현하자는 결론이 나왔습니다.</p>
<p>개인적으로 저는 어렸을 때부터 &quot;예술을 하면 굶주린다.&quot; 라는 말을 많이 들었습니다. (예술가의 길을 걷는 분들을 비하하려는 의도는 절대 아닙니다. 오해 없으시길..) 들인 시간과 비용에 비해 아웃풋이 비례하지 않기 때문입니다.
또한 신발, 의류 등은 수많은 온라인 쇼핑몰이 존재하고 또 많이 활성화 되어있지만 예술품의 경우에는 그렇지 않아 우리같은 일반인에게 예술품에 대한 접근성은 그닥 좋은편이 아닙니다.</p>
<p>저희는 이런 생각을 바탕으로 예술품 온라인 경매 사이트를 탄생시키고자 하였습니다.
고가의 유명한 작품이 아니더라도 검증된 예술 작가가 제작한 상품을 판매해 수익성을 높이고,
구매자는 마음에 드는 예술품을 경매를 통해 구매함으로써 예술품 거래에 대한 접근성을 높이는 것이 저희의 주된 목적이라고 할 수 있겠습니다.</p>
<p>이제 저희 사이트에서 예술품을 거래하기 위해 어떠한 플로우를 거쳐야 하는지, 어떠한 정책을 고민했는지에 대해 소개하겠습니다.</p>
<p><strong>입찰 자격과 경매 등록 자격</strong>
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/0ae6c9fc-ab05-4e9c-a6e4-0f8c3797f204/image.png" alt=""></p>
<p>프로젝트를 기획하고 나서 제일 먼저 든 생각이 &quot;구매자가 입찰을 해놓고 지불을 안하면 어떡하지?&quot; 였습니다.
많은 고민을 하다가 경매 입찰 자격을 취득하기 위해 몇 가지 룰을 정하기로 했습니다.</p>
<ol>
<li>경매가 마감되고 최고가를 입찰한 사람에게 결제 권한이 주어지며 일정 시간동안 결제하지 않은 경우에는 구매 자격을 박탈한다.</li>
<li>그리고 두 번째로 높은 가격을 입찰한 사람에게 결제 권한이 넘어간다.</li>
<li>경매 입찰 자격 취득을 하기 위해 위 1, 2 내용에 대해 동의를 해야만 한다.</li>
</ol>
<p>이와 같이 구매를 하기 위해 어느 정도 규칙을 정하고 그 규칙을 강제화합니다.
규칙이 지켜지지 않았을 경우 불가피하게 패널티를 부여합니다.
이것이 올바르고 깔끔한 경매를 위해 저희가 정한 규칙입니다.</p>
<p>또 한가지 고민했던 것은 &quot;너도 나도 아무렇게나 대충 예술품을 만들어 장난식으로 물건을 판매하려고 우리 사이트를 이용하면 어떡하지?&quot; 였습니다.</p>
<p>이 또한 아래와 같은 룰을 적용하였습니다.</p>
<ol>
<li><p>한국 미술협회에 정식으로 등록된 예술가만 추가 인증을 통하여 우리 사이트에 경매를 등록하는 자격이 부여된다. (한국 미술협회측과 협의를 할 수 없어서 실제로 구현은 할 수 없었지만 구두로라도 이런 규칙이 있다는 것을 가정하였습니다.)</p>
</li>
<li><p>다른 작가의 작품을 대신 팔아준다던가, 사칭을 한다던가 불법적인 일을 했다는 사실이 밝혀지면 경매 등록 자격을 박탈하는 패널티를 부여한다.</p>
</li>
</ol>
<p>캡처의 [경매 입찰 자격 취득, 경매 등록 자격 취득] 페이지는 해당 내용이 들어가며, 상황에 따라 규칙도 변경될 수 있습니다.</p>
<hr>
<h2 id="프로젝트에-구현된-기능들">프로젝트에 구현된 기능들</h2>
<p>예술품을 거래하기 위해 저는 아래와 같은 기능들을 구현하였습니다.</p>
<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/jin_jin_dev/post/ef49899b-4cce-4f86-8d83-6f42596473cb/image.gif" alt=""></p>
<p>메인 페이지는 Header, Main, Footer 세 가지 컴포넌트로 구현되어 있습니다.
메인 페이지에서는 스크롤 다운시 요소 렌더링 효과를 부각시키기 위해 AOS 라이브러리를 사용했습니다.</p>
<h3 id="카카오-소셜-로그인">카카오 소셜 로그인</h3>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/b77cf84a-c86a-4cbf-8f82-2f1071921ada/image.gif" alt=""></p>
<p>카카오 소셜 로그인은 KaKao Developer를 참고해서 진행했습니다.
프로젝트에 OAuth를 적용시키는 첫 사례가 된 것 같습니다.
카카오 소셜로그인 플로우는 아래와 같습니다.</p>
<ol>
<li>KaKao Developer에 회원가입 후 API 키를 부여 받아 소셜로그인에 사용한다.</li>
<li>카카오측에 인가 코드 요청 -&gt; 유저 토큰 요청 -&gt; 백엔드에 JWT 토큰을 요청한다.</li>
</ol>
<p>2번을 진행할 때 인가 코드는 프론트가 발급하고 이 코드를 백엔드에 전달한 뒤 
백엔드에서 다시 카카오 API를 호출해 유저 토큰을 요청하고 JWT 토큰으로 암호화 과정을 거쳐 프론트로 전달받는 방식으로 진행했었습니다.</p>
<p>그러나 추후 AWS 배포 과정에서 로컬, EC2 환경에서 각각 소셜 로그인을 진행하려다보니
불필요하게 백엔드쪽에서도 redirect URI를 맞추어 바꿔줘야하는 불편함 때문에
유저 토큰도 프론트에서 발급 받는 것으로 변경하였습니다.</p>
<h3 id="상품-리스트-페이지">상품 리스트 페이지</h3>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/001f88b4-c633-42ad-a584-9d45b0de521f/image.gif" alt=""></p>
<p>저희 Wevre에서는 상품이 리스팅될 때 예술품 경매라는 특성을 고려하여 한 화면에 하나의 상품만 보이게 하고 싶었습니다.
따라서 상품 사진 및 상세설명을 화면에 꽉차게끔 UI를 구성하였고
무한 스크롤을 적용하여 스크롤 다운시 다음 상품이 한 건씩 노출되로록 구현하였습니다.</p>
<h3 id="검색">검색</h3>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/5eff8f84-cdab-40a6-b2f6-a412d548b163/image.gif" alt=""></p>
<p>검색은 작가명, 작품명 등으로 진행할 수 있도록 필터링을 구현하였습니다.</p>
<h3 id="위시리스트">위시리스트</h3>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/16eeddfe-3fba-4aa2-a67d-813b971ca30a/image.gif" alt=""></p>
<p>고객은 상품 찜하기와 비슷하게 위시리스트를 설정할 수 있습니다.
위시리스트에 등록된 상품은 상품 리스팅 페이지 우측 리모컨에 추가한 위시리스트 목록이 보이게 됩니다.</p>
<h3 id="예술품-입찰">예술품 입찰</h3>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/aad42bc3-e338-42f1-8c13-1b89ea1f275c/image.gif" alt=""></p>
<p>저희가 구현한 기능의 핵심이라고 생각합니다.
상품 디테일 페이지에서 입찰을 진행할 수 있으며, 입찰한 횟수에 따라 입찰 상승률(라인 그래프)와 입찰 가격(막대 그래프)가 갱신되도록 구현해보았습니다. (Chart.js를 사용했습니다.)</p>
<p>이런 데이터 시각화의 경험은 언젠가 현업에서도 사용할 수 있기에 좋은 공부가 되었습니다.</p>
<p>또한, 같은 상품에 대해 나 이외의 또 다른 누군가가 입찰을 할 경우 실시간으로 가격이 갱신되도록 구현하였습니다.
실시간 입찰을 반영하기 위해 Socket 통신을 사용하였으며, 기존에 사용하던 REST API와는 작동 방식이 달라 이 또한 많은 공부가 되었습니다.</p>
<hr>
<h2 id="마무리하며">마무리하며</h2>
<p>이번 2차 프로젝트에서는 생각한 것보다 훨씬 다양한 경험과 기능을 구현할 수 있었습니다.
좋은 팀원을 만나 기획 단계에서부터 유저에게 편리한 사용자 경험을 제공하기 위해 세세한 설계를 하는데에 집중했었던 것 같습니다.</p>
<p>2주라는 짧은 시간에 밤낮 구분없이 무리한 일정을 소화하기 위해 정말 최선을 다했으며 각자에게 만족스런 결과물이 되었습니다.</p>
<p>사실 이대로 서비스해도 괜찮은가? 라고 질문을 던져보면 부족한점이 많이 보입니다.
그러나 저희는 Agile Scrum 방식을 채택하였고 이제 한 스프린트가 끝났을 뿐입니다.
또 다른 스프린트가 시작된다면 여기서 더 발전되고 완성도 높은 페이지를 만들 수 있음을 확신합니다.</p>
<p>그럼 이만 포스팅을 마무리하도록 하겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Svelte] Svelte Store에 대해 알아보자]]></title>
            <link>https://velog.io/@jin_jin_dev/Svelte-Svelte-Store%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@jin_jin_dev/Svelte-Svelte-Store%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Thu, 13 Apr 2023 06:11:08 GMT</pubDate>
            <description><![CDATA[<p>Svelte의 store는 마치 react의 redux와 같습니다.</p>
<p>전역으로 상태관리가 필요할 때 사용하는 것으로</p>
<p>부모의 속성을 자식의 자식의 자식의 ... 자식까지 전달하려면</p>
<p>상위 자식에게 props를 주고, 그 자식은 또 하위 자식에게 props를 주고... 이렇게 최하위까지 전달이 되어야 하는 구조입니다.</p>
<p>이런 번거로운 과정을 없애기 위해 부모-자식 트리 외에 store라는 별도의 공간을 두고</p>
<p>이 공간에서 전역으로 관리할 상태를 생성하여 적재적소에 맞게 가져다 사용하면 됩니다.</p>
<p>글로만 이해하면 어려우니 한 번 예시를 통해 알아보겠습니다.</p>
<p>** App.svelte**</p>
<pre><code class="language-svelte">&lt;script&gt;
    import Parent from &#39;./Parent.svelte&#39;
    let temp = &#39;123&#39;;
&lt;/script&gt;

&lt;div&gt;
    App!!
&lt;/div&gt;
&lt;Parent temp={temp}/&gt;</code></pre>
<p><strong>Parent.svelte</strong></p>
<pre><code class="language-svelte">&lt;script&gt;
    import Child1 from &quot;./ChildOne.svelte&quot;
    export let temp;
&lt;/script&gt;

&lt;div&gt;
    Parents!
&lt;/div&gt;
&lt;Child1 temp={temp}/&gt;</code></pre>
<p><strong>ChildOne.svelte</strong></p>
<pre><code>&lt;script&gt;
    import ChildTwo from &#39;./ChildTwo.svelte&#39;
    export let temp;
&lt;/script&gt;

&lt;div&gt;
    child1
&lt;/div&gt;
&lt;ChildTwo temp={temp}/&gt;</code></pre><p><strong>ChildTwo.svelte</strong></p>
<pre><code class="language-svelte">&lt;script&gt;
    export let temp;
&lt;/script&gt;
&lt;div&gt;
    Child2 {temp}
&lt;/div&gt;
</code></pre>
<p><strong>결과</strong>
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/d05d92e3-170d-4d65-8e68-fd96a2424a01/image.png" alt=""></p>
<p>자, 위의 예시는 최상단 App 컴포넌트에서 선언된 temp 변수의 값이
하위의 Parent 하위의 ChildOne 하위의 ChildTwo까지 전달되어 출력하는 로직입니다.</p>
<p>temp의 값은 ChildTwo까지 내려가면서 그 상위 부모 요소를 한 번씩 전부 거쳤습니다.
어떤가요?? 너무 귀찮고 불필요하지 않나요??</p>
<p>이제 Store에 대해서 한 번 알아보겠습니다.</p>
<p>먼저 store.js를 생성합니다.</p>
<p><strong>store.js</strong></p>
<pre><code class="language-js">import {writable} from &#39;svelte/store&#39;

//temp는 writable로 선언된 변수이기 때문에 스토어 객체가 된다.
export let temp = writable(&#39;123&#39;);</code></pre>
<p>store.js에서는 writable이라는 녀석을 통해 temp에 &#39;123&#39;이라는 초기값을 설정해주었습니다.</p>
<p>그리고 App부터 ChildTwo를 다시 작성해줍니다.</p>
<p>** App.svelte**</p>
<pre><code class="language-svelte">&lt;script&gt;
    import Parent from &#39;./Parent.svelte&#39;
&lt;/script&gt;

&lt;div&gt;
    App!!
&lt;/div&gt;
&lt;Parent/&gt;</code></pre>
<p><strong>Parent.svelte</strong></p>
<pre><code class="language-svelte">&lt;script&gt;
    import Child1 from &quot;./ChildOne.svelte&quot;
&lt;/script&gt;

&lt;div&gt;
    Parents!
&lt;/div&gt;
&lt;Child1/&gt;</code></pre>
<p><strong>ChildOne.svelte</strong></p>
<pre><code>&lt;script&gt;
    import ChildTwo from &#39;./ChildTwo.svelte&#39;
&lt;/script&gt;

&lt;div&gt;
    child1
&lt;/div&gt;
&lt;ChildTwo/&gt;</code></pre><p><strong>ChildTwo.svelte</strong></p>
<pre><code class="language-svelte">&lt;script&gt;
    import {temp} from &#39;./store.js&#39;;
    export let temp;
&lt;/script&gt;
&lt;div&gt;
    Child2 {$temp}
&lt;/div&gt;
</code></pre>
<p>먼저 결과를 보고 코드를 다시 분석해봅시다!</p>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/031b1c64-0816-444c-8aa1-d27d0b017cbf/image.png" alt=""></p>
<p>결과는 첫 번째 예시와 동일합니다.
그런데 App 컴포넌트부터 Parent, ChildOne을 거쳐 ChildTwo에 도달하던 temp 속성이 사라졌습니다.
대신 ChildTwo 컴포넌트에서 store에 있는 temp를 가져와 사용하도록 변경되었네요.</p>
<p>그런데 이처럼 컴포넌트에서 store 객체를 import해서 사용할 때 주의할점이 있습니다.
바로 &#39;$&#39; 기호인데요,</p>
<p>store.js에서 writable로 선언된 변수를 그대로 사용하면, 최초 선언한 &#39;123&#39;의 값이 아닌 svelt/store 객체 그대로를 사용하겠다는 의미입니다.</p>
<p>즉, &#39;123&#39;의 값을 가진 temp를 사용하려면 앞에 $ 기호를 붙여줘야 합니다.</p>
<p>추가로 update와 set에 대해 알아봅시다.
먼저 childTwo 컴포넌트를 아래와 같이 변경해줍니다.</p>
<p><strong>childTwo.svelte</strong></p>
<pre><code class="language-svelte">&lt;script&gt;
    import {temp} from &#39;./store.js&#39;;

    const updateTemp = () =&gt; {
        temp.update(n =&gt; n + &#39;1&#39;);
    }

    const setTemp = () =&gt; {
        temp.set(&#39;123&#39;);
    }
&lt;/script&gt;
&lt;div&gt;
    child2 {$temp}
&lt;/div&gt;

&lt;button on:click={updateTemp}&gt;Update!&lt;/button&gt;
&lt;button on:click={setTemp}&gt;set!&lt;/button&gt;
</code></pre>
<p>update와 set을 통해 store에 선언된 temp 변수의 값을 바꿔줄 수 있습니다.
temp는 스토어 객체이기 때문에
temp = &#39;234&#39;; 
이런식으로 변경할 수는 없으며, 따라서 update 혹은 set을 통해 변경해주어야합니다.</p>
<p><strong>결과</strong><img src="https://velog.velcdn.com/images/jin_jin_dev/post/d92a2c40-0f8f-427f-893d-5a609108bdc0/image.gif" alt=""></p>
<p>둘 다 temp의 값을 변경시키는 기능을 담당하는데 </p>
<p>update는 지속적으로 수정사항이 발생하거나, 갱신될 때
set은 초기 값으로 되돌아가야 할 때</p>
<p>등으로 나누어 사용하면 될 것 같습니다.</p>
<p>그럼 이만 포스팅을 마무리하겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Svelte] Svelte 기초 사용법을 알아보자]]></title>
            <link>https://velog.io/@jin_jin_dev/Svelte-Svelte-%EC%82%AC%EC%9A%A9%EB%B2%95%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@jin_jin_dev/Svelte-Svelte-%EC%82%AC%EC%9A%A9%EB%B2%95%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Thu, 13 Apr 2023 04:44:17 GMT</pubDate>
            <description><![CDATA[<h2 id="기초-사용법">기초 사용법</h2>
<p><strong>App.svelte</strong></p>
<pre><code class="language-svelte">&lt;script&gt;
    let name = &#39;world&#39;;
    let age = 31;
&lt;/script&gt;

&lt;h1&gt;
    Hello {name}!
&lt;/h1&gt;

&lt;h2&gt;
    {age}
&lt;/h2&gt;
&lt;style&gt;
    h1 {
        color:red
    }
&lt;/style&gt;</code></pre>
<p>svelte 파일은 .svelte 확장자로 만들어집니다.
.svelte 파일은 세 가지의 영역으로 분리할 수 있는데
먼저 자바스크립트 코드가 들어갈 <strong>&quot;script&quot;</strong> 영역
두 번째로 <strong>&quot;html&quot;</strong> 이 작성될 영역
세 번째로 css를 위한 <strong>&quot;style&quot;</strong> 영역이 존재합니다.</p>
<p>코드는 위 예시와 같이 작성하시면 됩니다.</p>
<p><strong>결과</strong>
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/fa5d64ab-7345-4ffe-b38c-197cd3616650/image.png" alt=""></p>
<h2 id="반응성-binding">반응성 (binding)</h2>
<p>자 이제 스벨트가 시작되었습니다.</p>
<p>스벨트의 중요한 한가지 특징은 바로 &quot;반응성&quot;이 좋다는 것인데요,
반응성이란 무엇일까요??</p>
<p>아래 예시를 통해 알아보겠습니다.</p>
<pre><code class="language-svelte">&lt;script&gt;
    let name = &#39;world&#39;;
    let age = 31;

    const assign = () =&gt; {
        name = &quot;JP&quot;,
        age = 50
    }
&lt;/script&gt;

&lt;h1&gt;
    Hello {name}!
&lt;/h1&gt;

&lt;h2&gt;
    {age}
&lt;/h2&gt;

&lt;button on:click={assign}&gt;Assign!&lt;/button&gt;

&lt;style&gt;
    h1 {
        color:red
    }
&lt;/style&gt;
</code></pre>
<p>script구문 내 assign 함수를 추가하였으며 해당 함수는 name과 age를 변경시켜줍니다.
그리고 이 함수를 실행시켜줄 버튼을 하나 생성했습니다.</p>
<p>그럼 결과를 한 번 볼까요?</p>
<p><strong>결과</strong>
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/d816bc45-5f44-4ddb-87f1-6d8f2c1ccd1f/image.gif" alt=""></p>
<p>버튼을 누르게되면 함수가 실행되면서 name, age를 변경시키고 이 값들이 html에서 바로 적용되었습니다.
즉, 반응성이란 변화에 따른 랜더링이 바로 반영되는가를 뜻하는 것인데 svelte는 변수가 변경됨에 따라 화면에 바로 그려지니 반응성이 좋다고 할 수 있겠습니다.</p>
<p>만약 리액트였다면 useState를 사용해 변수의 상태를 관리하면서 리랜더링을 시켜야 합니다.
svelte는 이런 부분에서 리액트보다 훨씬 간결하고 짧은 코드로 동일한 동작을 구현할 수 있습니다!</p>
<h2 id="바인딩">바인딩</h2>
<p>바인딩이란 &quot;연결해주다&quot;라는 뜻을 가지고 있습니다.
이게 무슨말일까요??
아래 예시를 통해 바로 알아보겠습니다.</p>
<pre><code class="language-svelte">&lt;script&gt;
    let name = &#39;world&#39;;
    let age = 31;

    const assign = () =&gt; {
        name = &quot;JP&quot;,
        age = 50
    }
&lt;/script&gt;

&lt;h1&gt;
    Hello {name}!
&lt;/h1&gt;

&lt;h2&gt;
    {age}
&lt;/h2&gt;

&lt;input type=&quot;text&quot; value={name}/&gt;
&lt;button on:click={assign}&gt;Assign!&lt;/button&gt;

&lt;style&gt;
    h1 {
        color:red
    }
&lt;/style&gt;</code></pre>
<p>버튼 앞에 input을 하나 추가했습니다.
이 input을 잘 보세요!</p>
<p><strong>결과</strong>
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/f70a97a4-31ba-46c6-a9f7-fa2dee47aaac/image.gif" alt=""></p>
<p>input 옆에 있는 Assign 버튼을 누르면 assign 함수로 인해 name, age값이 바뀌면서 
input내 텍스트도 바뀌게 됩니다.</p>
<p>그런데 input에다가 추가로 글자를 적으면 Hello {name} 부분은 동작하지 않습니다.
이는 바인딩이 되지 않아서 그렇습니다.
input에 입력하는 텍스트대로 화면에 노출되는 모든 {name}값을 변경하고 싶다면 바인딩을 시켜주면 됩니다.</p>
<p>그렇다면 바인딩은 어떻게 하는걸까요??
굉장히 간단합니다.</p>
<p>input 태그를 아래와 같이 바꿔보겠습니다.</p>
<pre><code class="language-svelte">*기존
&lt;input type=&quot;text&quot; value={name}/&gt;

*변경
&lt;input type=&quot;text&quot; bind:value={name}/&gt;

//value 앞에 &quot;bind:&quot; 를 붙여준다.</code></pre>
<p>굉장히 간단하죠??
그럼 결과를 한 번 보겠습니다.</p>
<p><strong>결과</strong>
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/9b5ef13c-8593-4a4a-9a8b-c4e69d029a99/image.gif" alt=""></p>
<p>이제 input text의 변화에 따라 모든 {name}의 값이 함께 변하는 것을 확인할 수 있습니다.</p>
<h2 id="조건문">조건문</h2>
<p>svelte에도 조건문이 존재합니다!
어떻게 쓰는지 바로 알아볼까요?</p>
<pre><code class="language-svelte">&lt;script&gt;
    let name=&#39;world&#39;;
    let toggle = false;
    if(toggle) {
        //내용
    }

    const onToggle = () =&gt; {
        toggle = !toggle;
    }
&lt;/script&gt;

&lt;button on:click={onToggle}&gt;toggle!!&lt;/button&gt;

{#if toggle}
    &lt;h1&gt;toggle true!&lt;/h1&gt;
{:else}
    &lt;h1&gt;toggle false!&lt;/h1&gt;
{/if}

</code></pre>
<p>svelte는 특이하게도 중괄호 안에 [&#39;#&#39;, &#39;:&#39;, &#39;/&#39;] 등의 기호로 조건문을 구성합니다.</p>
<p>if문이 시작될때는 {#if}
중간에 else나 else if가 들어갈 떄는 {:else/else if}
if문이 종료될때는 {/if}</p>
<p>위와 같은 형태로 구현합니다.</p>
<p><strong>결과</strong></p>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/2573d566-cc96-4cd0-8002-59c432272164/image.gif" alt=""></p>
<h2 id="반복문">반복문</h2>
<p>반복문도 한 번 알아볼까요??</p>
<pre><code class="language-svelte">&lt;script&gt;
    let name=&#39;Weekend&#39;;
    let weekend = [&#39;Mon&#39;,&#39;Tue&#39;,&#39;Wed&#39;,&#39;Thu&#39;,&#39;Fri&#39;,&#39;Sat&#39;,&#39;Sun&#39;];
&lt;/script&gt;

&lt;h1&gt;
    Hello {name}~
&lt;/h1&gt;

&lt;ul&gt;
    {#each weekend as day (day)}
        &lt;li&gt;{day}&lt;/li&gt;
    {/each}
&lt;/ul&gt;</code></pre>
<p>반복문은 {#each 배열 as 배열 내 아이템 (키 값)}으로 정의됩니다.
배열 내 아이템은 반복문이 돌면서 &#39;Mon&#39; =&gt; &#39;Sun&#39;까지 하나씩 변경되겠죠?
이 아이템들을 li로 구현하여 아래와 같은 결과가 나타납니다.</p>
<p><strong>결과</strong>
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/7d6ef5a0-d450-477c-9aba-b74a3b4e999e/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Svelte] svelte+typescript+tailwind css 설치하기]]></title>
            <link>https://velog.io/@jin_jin_dev/Svelte-sveltetypescripttailwind-css-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jin_jin_dev/Svelte-sveltetypescripttailwind-css-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 11 Apr 2023 02:31:34 GMT</pubDate>
            <description><![CDATA[<p>이번에는 svelte+typescript+tailwind css를 설치해보도록 하겠습니다.</p>
<h2 id="참고-문헌">참고 문헌</h2>
<p><a href="https://dev.to/swimmingkiim/create-svelte-typescript-tailwindcss-projectfeat-error-solved-4g24">https://dev.to/swimmingkiim/create-svelte-typescript-tailwindcss-projectfeat-error-solved-4g24</a></p>
<h2 id="sveltetypescript">svelte+typescript</h2>
<p>먼저 svelte+typescript를 설치해보겠습니다.
vite를 사용해 설치합니다.</p>
<blockquote>
<p>npm create vite@latest
yarn create vite</p>
</blockquote>
<p>콘솔창에 아래와 같은 내용이 뜨는군요</p>
<pre><code>✔ Project name: … svelte-ts
? Select a framework: › - Use arrow-keys. Return to submit.
  vanilla
  vue
  react
  preact
  lit
❯  svelte </code></pre><p>프로젝트 이름을 설정해주면 어떤 프레임워크를 사용할 것인지 묻숩니다.
우리는 svelte를 사용하기 때문에 svelte를 선택해줍니다.</p>
<p>그 다음 타입스크립트를 설치해봅시다.</p>
<pre><code>✔ Project name: … svelte-ts
✔ Select a framework: › svelte
? Select a variant: › - Use arrow-keys. Return to submit.
  svelte
❯  svelte-ts</code></pre><p>이후 설치할 때 생성된 디렉토리로 이동 후 모듈을 설치해줍니다.</p>
<blockquote>
<p>cd svelte-ts
npm install
npm run dev</p>
</blockquote>
<p>npm run dev가 문제 없이 실행된다면 svelte + typescript가 정상적으로 설치된 것입니다.</p>
<h2 id="tailwind-css">tailwind css</h2>
<p>이제 tailwind css를 설치할 예정인데요,
공식문서를 참고해 설치해봅시다.</p>
<p><a href="https://tailwindcss.com/docs/guides/sveltekit">https://tailwindcss.com/docs/guides/sveltekit</a>
공식문서는 sveltekit에 tailwind css를 설치하는 가이드인데
우리는 기본 svelte만 사용할 것이기에 몇 가지 과정은 빼도록 하겠습니다.</p>
<p>먼저 아래 명령어를 통해 tailwindcss를 설치해봅시다.</p>
<blockquote>
<p>npm install -D tailwindcss postcss autoprefixer svelte-preprocess
npx tailwindcss init tailwind.config.cjs -p</p>
</blockquote>
<p><strong>tailwind.config.cjs</strong></p>
<pre><code class="language-js">/** @type {import(&#39;tailwindcss&#39;).Config} */
module.exports = {
  // **here**
  content: [&#39;./src/**/*.{html,js,svelte,ts}&#39;],
  theme: {
    extend: {},
  },
  plugins: [],
}</code></pre>
<p>그리고 tailwind.config.cjs 파일의 content 부분을 위와 같이 수정합니다.</p>
<p><strong>/src/app.css</strong></p>
<pre><code class="language-js">@tailwind base;
@tailwind components;
@tailwind utilities;</code></pre>
<p>app.css 파일 최 상단에 tailwind 관련된 설정을 해줍니다.</p>
<p><strong>/src/layout.svelte</strong></p>
<pre><code class="language-js">&lt;script&gt;
  import &quot;./app.css&quot;;
&lt;/script&gt;

&lt;h1 class=&quot;text-3xl font-bold underline&quot;&gt;Hello world!&lt;/h1&gt;
</code></pre>
<p>마지막으로 컴포넌트에 app.css를 import 하고 npm run dev 하여 결과를 확인합니다.</p>
<h2 id="결과">결과</h2>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/45ca110a-69c8-4357-8cff-5e69e0bfcc85/image.png" alt=""></p>
<p>완성..!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Svelte] svelte 설치방법]]></title>
            <link>https://velog.io/@jin_jin_dev/Svelte-svelte-%EA%B8%B0%EC%B4%88-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@jin_jin_dev/Svelte-svelte-%EA%B8%B0%EC%B4%88-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Mon, 10 Apr 2023 08:19:12 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<p>svelte는 2016년 출시한 오픈 소스 프론트엔드 웹 프레임워크입니다. 기존의 React나 Vue 등 널리 알려진 웹 프레임워크와 달리 가상 DOM을 사용하지 않으며, 빌드 단계에서 구성요소를 컴파일 하여 성능이 향상되었습니다.</p>
<h2 id="특징">특징</h2>
<ol>
<li><p>실제 DOM을 사용하는 컴파일러
svelte는 바로바로 실제 DOM을 반영합니다. 앱을 실행 시점이 아닌 빌드 시점에서 바닐라 자바스크립트 번들로 컴파일하여 속도가 빠르고 따로 라이브러리를 배포할 필요가 없습니다.</p>
</li>
<li><p>document.querySelector 등의 함수로 실제 DOM에 접근 및 조작이 더 용이합니다.</p>
</li>
<li><p>React, Vue 등에 비해 훨씬 적은 양의 코드로 동일한 결과물을 만들 수 있으며, 가독성이 좋습니다.</p>
</li>
</ol>
<h2 id="svelte-설치">Svelte 설치</h2>
<ol>
<li><p>svelte 설치</p>
<blockquote>
<p>npx degit sveltejs/template .
typescript를 사용하려면 아래 명령어를 추가로 수행
node scripts/setupTypeScript.js</p>
</blockquote>
</li>
<li><p>패키지 설치</p>
<blockquote>
<p>npm install</p>
</blockquote>
</li>
<li><p>실행</p>
<blockquote>
<p>npm run dev</p>
</blockquote>
</li>
</ol>
<h3 id="파일-구조">파일 구조</h3>
<pre><code>svelte
│   └── public
│        ├── favicon.png
│        ├── global.css
│        └── index.html
├── scripts
├── src
│   ├── App.svelte
│   └── main.js
├── package.json
└── rollup.config.js</code></pre><p>react와 별반 다를게 없습니다.
src 하위에 코드를 적어내려가야 하는데, App.svelte는 모든 컴포넌트가 모이는 부모컴포넌트입니다.</p>
<p>main.js 에서는 public 하위의 index.html의 body에 App.svelte</p>
<p><strong>main.js</strong></p>
<pre><code class="language-js">import App from &#39;./App.svelte&#39;;

const app = new App({
    target: document.body,
    props: {
        name: &#39;world&#39;
    }
});

export default app;</code></pre>
<h3 id="scss-설치">SCSS 설치</h3>
<blockquote>
<p>npm i --save-dev svelte-preprocess sass</p>
</blockquote>
<h3 id="scss-적용">scss 적용</h3>
<p>rollup.config.js 파일 설정</p>
<ul>
<li>sveltePreprocess는 다음과 같은 전/후처리기들을 지원합니다.</li>
<li>Babel</li>
<li>TypeScript</li>
<li>CoffeeScript</li>
<li>Pug</li>
<li>PostCSS / SugarSS</li>
<li>Sass(SCSS)</li>
<li>Less</li>
<li>Stylus</li>
<li>globalStyle</li>
<li>replace</li>
</ul>
<pre><code class="language-js">import   from &#39;svelte-preprocess&#39;;

export default {
  //...
  plugins: [
    //...
    svelte({
        //...
        preprocess:sveltePreprocess({}),
    }),
  ]
}</code></pre>
<h3 id="scss-적용-1">SCSS 적용</h3>
<p><strong>test.svelte</strong></p>
<pre><code class="language-js">&lt;style lang=&quot;scss&quot;&gt;
    //...
&lt;/style&gt;</code></pre>
<h3 id="prettier-설치">prettier 설치</h3>
<blockquote>
<p>npm i --save-dev prettier-plugin-svelte prettier</p>
</blockquote>
<h3 id="프로젝트-root에-prettierrc-파일-생성">프로젝트 root에 .prettierrc 파일 생성</h3>
<pre><code class="language-js">{
  &quot;svelteSortOrder&quot;: &quot;scripts-styles-markup&quot;,
  &quot;svelteStrictMode&quot;: true,
  &quot;svelteBracketNewLine&quot;: true,
  &quot;svelteAllowShorthand&quot;: false
  //... 기타 옵션
}</code></pre>
<h3 id="babel-설치">Babel 설치</h3>
<p>js 최신 문법을 사용하면서도 빌드된 결과물을 구형 브라우저에서도 호환되게 보여주기 위해서 Babel 설정이 필요합니다.</p>
<blockquote>
<p>npm i --save-dev @babel/core @babel/preset-env @babel/plugin-proposal-optional-chaining</p>
</blockquote>
<p><strong>rollup.config.js</strong></p>
<pre><code class="language-js">plugins: [
        svelte({
            compilerOptions: {
                // enable run-time checks when not in production
                dev: !production
            },
            preprocess:sveltePreprocess({
                babel: {
                    presets: [
                      [&quot;@babel/preset-env&quot;, {
                        loose: true,
                        modules: false,
                        targets: { esmodules: true, },
                      }],
                    ],
                  },
                plugins: [&quot;@babel/plugin-proposal-optional-chaining&quot;],
            }),
        }),
  //...
  ]</code></pre>
<p>//tailwind 오류나서 추후 재진행</p>
<h3 id="tailwindcss-설치">tailwindCSS 설치</h3>
<blockquote>
<p>npm install -D tailwindcss postcss autoprefixer</p>
</blockquote>
<h3 id="tailwindcss-적용">tailwindCSS 적용</h3>
<p>아래 명령어를 수행하면 루트에 tailwind.config.js가 생성된다.</p>
<blockquote>
<p>npx tailwindcss init -p</p>
</blockquote>
<p>** tailwind.config.js**</p>
<pre><code class="language-js">const production = !process.env.ROLLUP_WATCH;
module.exports = {
  purge: {
    content: [&quot;./src/**/*.svelte&quot;],
    enabled: production, // disable purge in dev
  },
  darkMode: false, // or &#39;media&#39; or &#39;class&#39;
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
  future: {
    purgeLayersByDefault: true,
    removeDeprecatedGapUtilities: true,
  },
};</code></pre>
<p><strong>rollup.config.js</strong></p>
<pre><code class="language-js">svelte({
  preprocess: sveltePreprocess({
    sourceMap: !production,
    postcss: {
      plugins: [require(&quot;tailwindcss&quot;), require(&quot;autoprefixer&quot;)],
    },
  }),
  ...
}),</code></pre>
<h3 id="확장앱-설치">확장앱 설치</h3>
<p>Svelte for VS Code 찾아서 설치</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[git] git flow & rebase]]></title>
            <link>https://velog.io/@jin_jin_dev/git-git-flow-rebase</link>
            <guid>https://velog.io/@jin_jin_dev/git-git-flow-rebase</guid>
            <pubDate>Fri, 24 Mar 2023 13:55:36 GMT</pubDate>
            <description><![CDATA[<h2 id="git-flow">git flow</h2>
<p>git flow란 깃에서 제공하는 강력한 브랜칭 기능을 활용한 변경이력 관리 전략입니다.
이 전략은 회사마다, 프로젝트마다 달라질 수 있습니다.</p>
<p>예를 들어 현재 내가 다니고 있는 wecode에서는 프로젝트를 진행할 때 </p>
<p>메인 줄기가 되는 main branch
각 기능을 구현하는 feature/{기능} branch</p>
<p>둘을 가지고 진행하였으며 기능이 완성되면 main에 merge시켜 하나의 줄기로 합치는 작업을 해왔는데요.
이런식으로 브랜칭을 어떻게 할지 전략을 세우고 그에 맞게 작업하는 것을 git flow라고 합니다.</p>
<h2 id="git-rebase">git rebase</h2>
<p>프로젝트를 진행하며 충돌이나 파일이 아예 삭제되버리는 이슈들이 가끔씩 발생되곤 했습니다.
이럴 경우 커밋 기록을 찾아서 revert를 하던지 복붙을 하던지 했어야 했는데 모든 feature 브랜치의 커밋 기록과 merge된 커밋 기록까지 나와버리니 찾기가 여간 힘든게 아니었습니다.</p>
<p>이런 이슈를 해결할 수 있는 것이 rebase입니다.
rebase란 커밋 기록을 작업하고 있는 branch를 우선적으로 나타나게 하거나 수많은 커밋 기록을 합치는 등 기록 관리에 굉장한 효율을 보여주고 있습니다.</p>
<p>그럼 어떻게 사용하는건지 한 번 알아볼까요?</p>
<p>먼저 CRA를 통해 react Project를 생성해주겠습니다.</p>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/554a7deb-4297-40a8-9991-d9e92eb8c063/image.png" alt=""></p>
<p>저는 Main.js와 Login.js를 만들어주었습니다.</p>
<p>여기서 main과 login을 각각 작업하는 개발자가 있으며
서로 feature/main or login 브랜치를 생성해서 작업을 한다고 가정해보겠습니다.</p>
<h4 id="loginjs">Login.js</h4>
<pre><code class="language-js">import React from &quot;react&quot;;

function Login() {
  return &lt;div&gt;Login&lt;/div&gt;;
}

export default Login;</code></pre>
<p>위 코드를 작업했다는 가정하에 아래처럼 바꿔보겠습니다.</p>
<pre><code class="language-js">import React from &quot;react&quot;;

function Login() {
  return &lt;div&gt;Login!!!!!!&lt;/div&gt;;
}

export default Login;</code></pre>
<p>그리고 커밋을 날려줍니다.
(commit message : ADD : first commit)</p>
<p>그리고 어느정도 작업이 진행되었다고 가정하고 두 번째 커밋을 날려줍니다.
변경된 코드는 아래와 같습니다.</p>
<pre><code class="language-js">import React from &quot;react&quot;;

function Login() {
  return (
    &lt;div&gt;
      Login!!!!!!
      &lt;input type=&quot;text&quot; placeholder=&quot;아이디 입력&quot; /&gt;
      &lt;input type=&quot;password&quot; placeholder=&quot;비밀번호 입력&quot; /&gt;
    &lt;/div&gt;
  );
}

export default Login;
</code></pre>
<p>(commit message : ADD : create input)</p>
<p>이제 푸시를 하고 git log로 커밋 기록을 확인해보겠습니다.</p>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/52c20342-8912-4442-bb79-382ddeedd50e/image.png" alt=""></p>
<p>앞서 실행한 두 개의 커밋 기록이 정상적으로 보이네요</p>
<p>이제 main 브랜치에서 작업하는 다른 개발자가 되어봅시다.</p>
<p>main 브랜치에서 feature/main 브랜치를 생성하고 코드를 작성해줍니다.</p>
<h4 id="mainjs">main.js</h4>
<pre><code class="language-js">import React from &#39;react&#39;

function Main() {
  return (
    &lt;div&gt;Main!!&lt;/div&gt;
  )
}

export default Main</code></pre>
<p>그리고 커밋 &amp; 푸시까지 해줍니다.
(commit message : ADD : first commit main)</p>
<p>여기까지 한 후 깃허브 pull request에서 앞서 푸시했던 feature/login 브랜치를 rebase로 merge해줍니다.</p>
<p><strong>- feature/login rebase and merge</strong>
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/1aea2bad-d809-4c87-adf0-ad4afe254649/image.png" alt=""></p>
<p>그리고 feature/main도 rebase merge 해줍니다.</p>
<p><strong>- feature/main rebase and merge</strong>
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/d3b7063c-47e9-4166-a4cf-84fa46917030/image.png" alt=""></p>
<p>자 이제 feature/main에서 머지한 코드를 feature/login에 불러와보도록 하겠습니다.</p>
<p>main branch로 이동 후 &quot;git pull origin main&quot;으로 feature/main의 코드를 적용시킨 후
feature/login으로 이동해 &quot;git merge main&quot;하여 코드를 합쳐줍니다.</p>
<p>이제 git log 명령어로 커밋 기록을 확인해봅시다.</p>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/60016149-314d-4912-b994-9dfb0f122448/image.png" alt=""></p>
<p>로그를 보니 feature/login, feature/main, merge된 커밋기록까지 전부 찍혀있습니다.</p>
<p>지금이야 작업량이 거의 미비하지만 나중에 프로젝트가 커질수록 이런 커밋은 굉장히 많아지고 복잡해질 것입니다.</p>
<p>이제 merge 대신 rebase를 사용해 둘의 커밋 차이를 비교해봅시다.</p>
<hr>
<p>먼저 git reflog 명령어로 merge 이전 기록의 해시 코드를 가져와줍니다.
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/62502ffa-8b1a-47e6-a379-77a0f1e9cf5c/image.png" alt=""></p>
<p>위에서 두 번째인 4f908d9가 되겠네요</p>
<p>이제 저 코드를 git reset --hard 명령어로 merge 전 시점으로 돌아갑니다.</p>
<p>그리고 git rebase main -i main 명령어로 rebase를 진행합니다.</p>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/81084cf8-a993-47d4-9fb9-39abafcbb752/image.png" alt=""></p>
<p>그리하면 이전의 커밋된 내용이 나오네요.
이 내용을 수정하여 저장하면 넘어가지는데 여기서 Pick이라고 된 커밋의 기록이 남게됩니다.
pick을 다른 옵션으로 바꿀 수 있으며 정말 다양한 옵션이 존재하는데 저는 여기서 두 번째 커밋 기록을 squash를 뜻하는 s 옵션을 사용하겠습니다.</p>
<p>정상적으로 진행이 된다면 또 다른 내용이 나타납니다.
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/e3ae9b42-1033-450d-a31b-c0b8bf0aa6c9/image.png" alt=""></p>
<p>이전의 커밋 메세지들인데, 여기서 수정할 수 있습니다.
저는 그냥 넘어가겠습니다.</p>
<p>간혹 설정이 잘못되거나 충돌이 존재하면 rebase -i라는 문구가 표시됩니다. (설정에 따라 안보일수도 있습니다.)
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/2d6ec6ca-fd5b-47b3-8f1d-0ef5b23edfdb/image.png" alt=""></p>
<p>충돌을 해결해주고 git rebase --continue 명령어로 마무리해주어야 합니다.</p>
<h3 id="결과">결과</h3>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/ee9bc180-bedb-4e59-b38b-d787b8d6471e/image.png" alt=""></p>
<p>자 이제 커밋 기록이 정리되었네요. (first commit, create input이 두 번 찍힌 이유는 작성자의 실수입니다 하하.. ^^..)</p>
<p>현재 내가 있는 branch가 제일 상단에 위치하게되며 하나의 커밋 안에 여러개의 커밋 메세지가 묶여있는 형태입니다.</p>
<p>또 한 가지 확인할 수 있는 것은 바로 merge 기록이 사라졌다는 것이죠.
이로써 불필요한 커밋 기록을 줄이고 한 눈에 알아보기 쉬워졌습니다.</p>
<hr>
<p>마무리하며
git rebase는 처음에 접했을 때 개인적으로 어려웠습니다.
그래도 잘 정리된 커밋 기록을 보면 공부하길 잘했다는 생각이 듭니다.</p>
<p>아래 오늘 배운 명령어에 대해 정리하고 마무리하도록 하겠습니다.</p>
<hr>
<p>$ git rebase [-i] =&gt; git rebase를 진행할 때 커밋 메세지를 다시 설정할 수 있다.
$ git rebase --abort =&gt; rebase 도중 처음으로 돌아가고자 할 때 사용한다.
$ git rebase --continue =&gt; rebase 도중 충돌이 발생했을 때 충돌 해결 후 rebase를 다시 진행할 때 사용한다.
$ git reflog =&gt; git 명령 기록을 확인하며 각 시점의 hash 코드를 확인할 수 있다.
$ git reset =&gt; reflog에서 얻어낸 hash 코드를 가지고 특점 시점의 커밋으로 되돌아갈 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Wecode] 1차 프로젝트 회고]]></title>
            <link>https://velog.io/@jin_jin_dev/Wecode-1%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@jin_jin_dev/Wecode-1%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 20 Mar 2023 02:09:26 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>위코드 부트캠프에서 클론코딩 프로젝트를 진행하였습니다.
프로젝트가 마무리 되고 느꼈던 것들에 대해 회고하기 위해 포스팅을 남기게 되었습니다.</p>
</blockquote>
<h2 id="about-our-team">About our team</h2>
<h3 id="span-stylecolor2d3748-backgroundfff5b1github-repositoriesspan"><span style="color:#2D3748; background:#fff5b1">Github Repositories</span></h3>
<ul>
<li>Front-end : <a href="https://github.com/wecode-bootcamp-korea/43-1st-MG-frontend">https://github.com/wecode-bootcamp-korea/43-1st-MG-frontend</a></li>
<li>Back-end : <a href="https://github.com/wecode-bootcamp-korea/43-1st-MG-backend">https://github.com/wecode-bootcamp-korea/43-1st-MG-backend</a></li>
<li>individual : <a href="https://github.com/Dante6327">https://github.com/Dante6327</a></li>
</ul>
<h3 id="span-stylecolor2d3748-backgroundfff5b1front-end-skillsspan"><span style="color:#2D3748; background:#fff5b1">Front-end Skills</span></h3>
<ol>
<li>JavaScript</li>
<li>React</li>
<li>react-router-dom</li>
<li>SASS/SCSS</li>
</ol>
<h3 id="span-stylecolor2d3748-backgroundfff5b1version-managespan"><span style="color:#2D3748; background:#fff5b1">Version Manage</span></h3>
<ol>
<li>git &amp; github</li>
</ol>
<h3 id="span-stylecolor2d3748-backgroundfff5b1project-managespan"><span style="color:#2D3748; background:#fff5b1">Project Manage</span></h3>
<ol>
<li>trello</li>
<li>notion</li>
<li>slack</li>
</ol>
<hr>
<h2 id="about-our-project">About our project</h2>
<p>2023.03.06 ~ 2023.03.17 약 2주간 위코드 1차 프로젝트를 진행하였습니다.
스파오, 닥터마닡, 와이즐리, 이솝 등의 쇼핑몰들 중 하나를 추첨해 클론코딩을 해보는 프로젝트였습니다.
저희가 진행하게된 쇼핑몰은 &#39;와이즐리&#39;였으며 타 쇼핑몰들에 비해 깔끔하고 간결해보였기 때문에 속으로는 나이스를 외치고 있었습니다. (그러나 이것도 쉽지 않음을 느끼고 곧 눈물을 흘림..)</p>
<p>프로젝트는 애자일 방식으로 진행되었습니다.
애자일 방식이란 하나의 완제품을 짧은 기간안에 만들어놓고 점점 살을 붙여가며 제품의 퀄리티를 높여가는 방식입니다.</p>
<p>저희는 이런저런 기능을 전부 제쳐두고 회원가입/로그인부터 결제까지 쇼핑몰에서 꼭 필요한 기능들을 구현하는데에 중점을 두고 프로젝트를 시작했습니다.</p>
<p>팀원은 front 3명, back 2명, 총 5명으로 구성되었으며
그 중 저는 project manager를 겸임하게 되었습니다.</p>
<p>프로젝트 매니저의 역할을 살펴보니 프로젝트의 전반적인 일정 관리 및 진척도에 따른 인원 배분 등 많은 역할을 소화해내야 했습니다.
과감한 결단과 판단이 없으면 팀 전체가 절벽으로 내던져진다는 부담감을 안고 시작하게 되었습니다.</p>
<p>저희가 제일 먼저 시작한 것은 와이즐리 쇼핑몰을 분석한 것이었습니다.
<strong>&quot;와이즐리는 어떤 마인드로 서비스되고 있으며 판매 타깃은 누구이고 타 쇼핑몰에 비해 어떤 장점이 있을까?&quot;</strong></p>
<p>분석한 와이즐리는 포장비, 광고비, 사무실 임대료를 걷어낸 &quot;저렴한 가격&quot;이 가장 경쟁력 있는 장점이었습니다.</p>
<p>저렴한 상품 가격은 남녀노소 누구나 원하는 니즈이지만 사실 저희가 실제로 상품을 판매할 수 있는 상황이 아니라서 다른 장점을 찾기 시작했습니다.</p>
<p>그 중 생각한 것이 &quot;<strong>웹 사이트를 구성함에 있어 보다 편리하게 바꿔보자</strong>&quot; 였습니다.</p>
<p>저희는 쇼핑몰 명칭을 &#39;Weekly&#39;로 정하였으며, 저희 Weekly에서는 다양한 종류의 상품 대신 많은 현대인이 공통적으로 소비하는 &#39;영양제&#39;를 판매하는 사이트로 가정하고 프로젝트를 진행하였습니다.
판매 타깃은 영양제가 필요한 모든 사람. 즉, 신생아부터 남녀노소 전체가 대상이었습니다.</p>
<p>이쯤되니 클론코딩의 목적을 알게 되었습니다.
위코드에서 클론코딩을 시킨 것은 단순히 UI나 기능을 배껴서 구현하라는게 아니라, 
<strong><span style='color:red'>어떤 소비자에게 어떤 서비스를 제공할지를 생각하고 그에 맞게 개발해라!</span></strong> 라는 취지가 숨어있었습니다. </p>
<h3 id="우리가-구현한-기능">우리가 구현한 기능</h3>
<p>저희가 프로젝트동안 구현하게된 기능은 아래와 같습니다.</p>
<ol>
<li>nav/footer</li>
<li>회원가입</li>
<li>로그인</li>
<li>성별/연령 카테고리별 영양제 리스팅</li>
<li>상품 무한스크롤</li>
<li>장바구니 페이지 구현</li>
<li>모달창을 이용한 장바구니 담기</li>
<li>모달창을 이용한 결제</li>
</ol>
<p>그 중 제가 구현한건 1,4,5,7번이며 이를 중점적으로 포스팅하겠습니다!</p>
<h3 id="구현한-내용">구현한 내용</h3>
<p><strong>wisely 홈화면</strong>
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/2e567d1b-df64-4446-8ccc-e05bf1c8b2fb/image.png" alt=""></p>
<p>와이즐리의 홈화면은 네비게이션과 마우스 호버시 내려오는 카테고리 영역, 네비게이션 하단의 슬라이드로 구성된 배너 이미지가 존재합니다.</p>
<p><strong>weekly 홈화면</strong>
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/a9121590-a2c3-414a-9a4d-44f962f3405e/image.png" alt=""></p>
<p>우선 저희 페이지명은 weekly로 정했습니다. (wisely 로고나 이미지 등등은 갖다 쓰면 저작권에 걸릴 것 같아서.. 바꿨습니다.)
이제 어떤 부분을 구현했는지 말씀드리겠습니다!</p>
<hr>
<p><strong>먼저 nav/footer입니다.</strong></p>
<ol>
<li><p>네비게이션은 얼추 비슷한 형태로 만드려고 노력하였습니다.
네비게이션 내에 [회원가입/로그인] 버튼은 로그인시 백엔드에서 토큰을 전달받으며 해당 토큰이 로컬스토리지에 들어있는지의 여부에 따라 [마이페이지/로그아웃]으로 변경되도록 하였습니다.</p>
</li>
<li><p>저희 사이트는 위에서 언급했듯이 영양제를 전문으로 판매하는 쇼핑몰이며 성별/연령대에 따라 상품을 나누는 기준이 되므로 Man, Woman, Kids 세 가지 카테고리로만 구현하였습니다.
각각 버튼 클릭시 카테고리에 맞는 상품만 리스팅되게 하려는 목적으로 이렇게 구현하였습니다.</p>
</li>
<li><p>footer는 그저 UI만 참고해 그렸으며 별다른 기능이 없기 때문에 따로 기재하지는 않겠습니다.</p>
</li>
</ol>
<hr>
<p><strong>두 번째로 성별/연령 카테고리별 영양제 리스팅입니다.</strong></p>
<ol>
<li><p>네비게이션에 설명드렸듯이 ALL, MAN, WOMAN, KIDS 버튼 클릭시 해당 카테고리에 맞는 상품이 리스팅됩니다.</p>
</li>
<li><p>각 상품에는 category_id라는 키 값이 있으며, react-router 쿼리스트링을 통해 백엔드 API 요청에 맞는 데이터를 구해 요청합니다.</p>
</li>
<li><p>쿼리스트링의 형태는 이렇습니다.
데이터베이스에 조회할 카테고리 ID(cateId), 조회할 상품의 순번(offset), 조회할 상품 갯수(limit)입니다.</p>
<blockquote>
<p>/cateCode/{cateId}?offset={offset}&amp;limit={limit}</p>
</blockquote>
</li>
</ol>
<hr>
<p><strong>세 번째로 무한스크롤을 적용하였습니다.</strong></p>
<ol>
<li><p>최초 메인페이지에 보여지는 상품 수는 10개 입니다.
이후 스크롤을 내려 페이지가 특정 위치까지 도달했을 때 상품 조회 API를 호출하여 불러온 상품을 계속해서 보여주는 방식입니다.</p>
</li>
<li><p>무한스크롤에 대한 자세한 방식은 별도로 포스팅하도록 하겠습니다.</p>
</li>
</ol>
<hr>
<p><strong>마지막으로 모달창을 이용한 장바구니 담기입니다.</strong></p>
<ol>
<li><p>메인 페이지에서 카테고리별로 리스팅된 상품에 장바구니 아이콘이 같이 달려있습니다.</p>
</li>
<li><p>해당 아이콘을 클릭할 때 모달창이 노출되며, 장바구니에 담을 수량을 선택할 수 있습니다.</p>
</li>
<li><p>장바구니에 상품을 담을 경우 네비게이션 우측에 있는 장바구니 아이콘에 담겨있는 상품 숫자가 카운팅됩니다.</p>
</li>
</ol>
<h2 id="마무리하며">마무리하며</h2>
<p>*짤막한 결과영상
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/6d470e9f-1811-417a-97b6-bb0ac346eeee/image.gif" alt=""></p>
<hr>
<p><strong>어려웠던 점, 아쉬웠던 점, 잘했던 점</strong></p>
<p>프로젝트를 진행하며 벽을 느꼈던 순간이 종종 있었습니다.</p>
<p><strong>어려웠던 점</strong>
먼저, 무한스크롤을 구현할 때 너무나 힘들었습니다.
저는 Weekly는 페이지 이동 없이도 상품을 지속적으로 리스팅하기 위해 무한스크롤로 상품 리스팅을 구현해봐야겠다! 라고 당차게 결정하였지만 현실은 암담했습니다.
많은 무한스크롤 포스팅을 참고하며 구현하려고 노력했었는데, 
[페이지 스크롤이 얼마나 내려왔다] 에 대한 값을 구하는 것이 정말 어려웠습니다.</p>
<p>엘리먼트의 위치를 파악하기 위한 Element.getBoundingClientRect()
react에서 dom을 조작하기 위한 useRef 등
위 두 가지를 섞어가며 스크롤 계산 값을 구하려했지만 쉽지 않았습니다.</p>
<p>이 때 위코드 멘토님께서 
[document.body.scrollHeight, document.documentElement.scrollTop]
이 두 가지로 스크롤 계산값을 구하는 방법을 알려주셨습니다.
아마 도움을 받지 못했다면 일정상 포기하고 페이지네이션으로 상품 리스팅을 구현했을 것 같습니다.</p>
<p>그래도 getBoundingClientRect()나 useRef 등에 대해 많은 것을 알게되었습니다.
이는 별도로 포스팅하도록 하겠습니다^^</p>
<p><strong>아쉬웠던 점</strong>
프론트 &lt;-&gt; 백 API 통신을 진행하는 동안 몇 번의 고비가 있었습니다.
엔드포인트가 변경되기도 하고 프론트를 구성하는데 필요한 데이터를 깊게 생각하지 않아 추후에 데이터 추가를 요청드린 일도 있었습니다.</p>
<p>이런 과정이 생각보다 시간이나 노력이 많이 들어가는 과정이었는데 
초기에 명확하게 프론트에서 필요한 데이터와, 그에 따른 응답이 어떻게 생겼는지, 엔드포인트는 어떻게 되는지를 확실히 잡고 갔었어야 이런 상황을 피할 수 있을 것 같습니다.
이 부분은 프로젝트를 진행하며 조금 아쉬운 부분이 되었습니다.</p>
<p>또한 마크업 스킬이 약간 아쉬웠습니다.
욕심같아서는 페이지 리사이즈에 따라서 UI가 변경되거나 반응형 디자인이 추가되었으면 했지만, 생각보다 기능구현 하는데에 시간을 많이 쏟게 되어 이런 부분을 놓쳤던 것 같습니다.</p>
<p>마크업에 대한 욕심을 충족시키려면 스스로가 마크업 스킬을 더더더더더욱 갈고 닦아야 할 것 같습니다.</p>
<p><strong>잘했던 점</strong>
위에서 기재했던 위기상황(?)들에 대해서 마크업을 제외하고 빠르게 대처가 된 것 같습니다.
프로젝트 매니저로써 프로젝트가 끝나기 전까지 세부적인 기간이나 플랜을 계속해서 생각해야 했습니다.</p>
<p>그런데 중간중간 커밋한 소스가 다른 영역을 침범해 충돌이 나거나, 아예 파일이 삭제되어버리는 일도 있었습니다.</p>
<p>다행히도 저는 타 영역에서 개발 경험이 있는 3년차 중고이기때문에 다른 팀원들보다 git, github를 다루는데 익숙해져있었고, 이런 경험을 살려 코드 충돌로 일어나는 어지간한 상황은 빠르게 대처했었던 것 같습니다.</p>
<hr>
<p><strong>마무리하며</strong>
이번 프로젝트경험을 기반으로 스스로의 부족함과 기대치를 동시에 느낀 것 같습니다.
부족하고 아쉬웠던점을 통해 &quot;더 좋은 서비스를 구현하려면 어떻게 해야하는가?&quot;에 대해 많은 고민을 하게 되었고 다음 프로젝트 때 이를 녹여 더 높은 품질의 서비스를 내놓을 수 있을 것 같다는 생각이 들었습니다.</p>
<p>긴 글 읽어주셔서 감사합니다.
이만 포스팅을 마치도록 하겠습니다 ^^</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 리액트 라우터 (react-router-dom)]]></title>
            <link>https://velog.io/@jin_jin_dev/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-react-router-dom</link>
            <guid>https://velog.io/@jin_jin_dev/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-react-router-dom</guid>
            <pubDate>Sun, 12 Mar 2023 04:50:20 GMT</pubDate>
            <description><![CDATA[<h2 id="react-router-dom-이란-">react-router-dom 이란 ?</h2>
<p>react는 SPA(Single page application)이며 따라서 하나의 프로젝트에 하나의 html만 존재합니다.
그러나 우리는 브라우저 경로에 따른 다른 페이지를 로딩하는 방식을 오래전부터 사용해 왔습니다.
리액트에서도 이처럼 라우팅을 하는게 가능합니다.
그러나 위에서 말씀드렸듯이 단일 페이지로 구성되어 있기 때문에 경로가 변경된다고 페이지가 갈아끼워지는게 아닌, 컴포넌트가 갈아끼워지게 됩니다.</p>
<p>그러면 지금부터 라우팅에 대해 글을 작성해보겠습니다.</p>
<p>먼저 리액트는 프레임워크가 아닌 라이브러리이므로 vue나 angular처럼 내장 라우팅을 지원하지 않습니다.
따라서 router 라이브러리를 별도로 설치해 사용해야 하죠.</p>
<h3 id="react-router-dom">react-router-dom</h3>
<p>이 라이브러리의 이름은 react-router-dom이라고 합니다.
설치 명령어는 아래와 같습니다.</p>
<blockquote>
<p>$npm i react-router-dom</p>
</blockquote>
<p>라우터의 사용법은 아래와 같습니다.</p>
<pre><code class="language-js">//Router.js
import React from &quot;react&quot;;
import { BrowserRouter, Routes, Route } from &quot;react-router-dom&quot;;
import PageOne from &quot;./components/Page01&quot;;
import PageTwo from &quot;./components/Page02&quot;;
import PageThree from &quot;./components/Page03&quot;;
import App from &quot;./App&quot;;

const Router = () =&gt; {
  return (
    &lt;BrowserRouter&gt;
      &lt;Routes&gt;
        &lt;Route path=&quot;/&quot; element={&lt;App /&gt;} /&gt;
        &lt;Route path=&quot;/pageOne&quot; element={&lt;PageOne /&gt;} /&gt;
        &lt;Route path=&quot;/pageTwo&quot; element={&lt;PageTwo /&gt;} /&gt;
        &lt;Route path=&quot;/pageThree&quot; element={&lt;PageThree /&gt;} /&gt;
      &lt;/Routes&gt;
    &lt;/BrowserRouter&gt;
  );
};

export default Router;


</code></pre>
<p>먼저 컴포넌트 변경을 확인하기 위해 page01,02,03을 생성하였습니다.
그리고 BrowserRouter &gt; Routes &gt; Route 순으로 감싸주며
Route path를 통해 이동한 브라우저 주소와 해당 주소에 어떤 컴포넌트를 불러올지 element 속성으로 설정합니다.</p>
<h3 id="appjs">App.js</h3>
<pre><code class="language-js">//App.js
import React from &quot;react&quot;;
import { Link, useNavigate } from &quot;react-router-dom&quot;;

const App = () =&gt; {
  const navigate = useNavigate();
  const handleNavigate = () =&gt; {
    navigate(&quot;/pageThree&quot;);
  };
  return (
    &lt;div&gt;
      &lt;p&gt;App component입니다.&lt;/p&gt;
      &lt;button&gt;
        &lt;Link to=&quot;/pageOne&quot;&gt;Go to pageOne&lt;/Link&gt;
      &lt;/button&gt;
      &lt;button&gt;
        &lt;Link to=&quot;/pageTwo&quot;&gt;Go to pageTwo&lt;/Link&gt;
      &lt;/button&gt;
      &lt;button onClick={handleNavigate}&gt;Go to pageThree&lt;/button&gt;
    &lt;/div&gt;
  );
};

export default App;</code></pre>
<h3 id="pageonejs">PageOne.js</h3>
<pre><code class="language-js">//PageOne.js
import React from &quot;react&quot;;
import { Link } from &quot;react-router-dom&quot;;

const PageOne = () =&gt; {
  return (
    &lt;div&gt;
      &lt;p&gt;page01입니다. 환영합니다.&lt;/p&gt;
      &lt;button&gt;
        &lt;Link to=&quot;/&quot;&gt;홈으로 이동&lt;/Link&gt;
      &lt;/button&gt;
    &lt;/div&gt;
  );
};

export default PageOne;
</code></pre>
<h3 id="pagetwojs">PageTwo.js</h3>
<pre><code class="language-js">//PageTwo.js
import React from &quot;react&quot;;
import { Link } from &quot;react-router-dom&quot;;

const PageTwo = () =&gt; {
  return (
    &lt;div&gt;
      &lt;p&gt;page02입니다. 환영합니다.&lt;/p&gt;
      &lt;button&gt;
        &lt;Link to=&quot;/&quot;&gt;홈으로 이동&lt;/Link&gt;
      &lt;/button&gt;
    &lt;/div&gt;
  );
};

export default PageTwo;
</code></pre>
<h3 id="pagethreejs">PageThree.js</h3>
<pre><code class="language-js">//PageOne.js
import React from &quot;react&quot;;

import { Link } from &quot;react-router-dom&quot;;

const PageThree = () =&gt; {
  return (
    &lt;div&gt;
      &lt;p&gt;page03입니다. 환영합니다.&lt;/p&gt;
      &lt;button&gt;
        &lt;Link to=&quot;/&quot;&gt;홈으로 이동&lt;/Link&gt;
      &lt;/button&gt;
    &lt;/div&gt;
  );
};

export default PageThree;
</code></pre>
<p>그리고 App ~ Page 1~3을 위와 같이 작성해주도록 합니다.</p>
<h3 id="결과">결과</h3>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/0af5c916-3808-4099-8836-b0abb8d559e8/image.gif" alt=""></p>
<p>결과처럼 페이지를 이동하면 주소창에 각각 pageOne/Two/Three로 변경되며 컴포넌트가 교체됩니다.</p>
<p>여기서 App.js를 자세히 보면 PageOne/Two는 react-router-dom 라이브러리의 내장 컴포넌트인 <strong>Link</strong> 컴포넌트를 사용하였으며
PageThree는 <strong>useNavigate</strong> hook을 사용하였습니다.</p>
<p>이 둘은 설정한 경로로 이동해주는 역할을 한다는 공통점이 있습니다.</p>
<p>그런데 왜 같은 기능을 두 개나 만들어 두었을까요??</p>
<p>먼저 Link 컴포넌트의 경우 묻지도 따지지도 않고 경로를 이동시켜버립니다.
컴포넌트이기 때문에 다른 함수 안에 선언될 수 없고, 함수로 실행시킬수도 없게되는 것이죠.
따라서 로그인이나 회원가입 등 유효성 검증이 필요한 경우에는 곧바로 컴포넌트를 교체하는 Link는 적합하지 않습니다.</p>
<p>반면에 useNavigate의 경우 함수 안에서 사용이 가능합니다.
즉, 로그인/회원가입시 이런저런 유효성 검사를 따진 후에 이상이 없다면 컴포넌트 교체를 해줄 수 있는 것이죠.
이러한 차이 때문에 Link와 useNavigate 훅이 분리되었습니다.</p>
<hr>
<h2 id="동적-라우팅-advanced-routing">동적 라우팅 (Advanced Routing)</h2>
<p>우리는 위에서 라우팅을 통해 브라우저 경로를 이동 및 화면 이동에 대해 알아보았습니다.
그런데 이러한 방식에는 한가지 불편한점이 존재합니다.</p>
<p>예를 들어 온라인 쇼핑몰을 개발하는 중이라고 가정해봅시다.
쇼핑몰 안에는 다양한 상품이 판매되고 있으며 보통 각각의 상품을 구별하기 위해 서로 다른 Id를 부여받습니다.
그리고 이 상품의 상세페이지에 접근할 때 아래와 같은 형식으로 브라우저 주소가 변경된다고 가정해봅시다.</p>
<blockquote>
</blockquote>
<p>localhost:3000/productId/1
localhost:3000/productId/2
localhost:3000/productId/3
localhost:3000/productId/4
...
localhost:3000/productId/100000
localhost:3000/productId/100001
localhost:3000/productId/100002</p>
<p>이렇듯 굉장히 많은 상품이 존재하고 각 상품마다 부여받는 브라우저 주소가 Id값에 따라 변할 때 
위에서 사용했던 방식으로 라우팅을 구성한다면 우리는 10만개가 넘는 라우팅 주소를 일일이 세팅해야 합니다.</p>
<pre><code class="language-js">const Router = () =&gt; {
  return (
    &lt;BrowserRouter&gt;
      &lt;Routes&gt;
        &lt;Route path=&quot;/productId/1&quot; element={&lt;Product1 /&gt;} /&gt;
          &lt;Route path=&quot;/productId/2&quot; element={&lt;Product2 /&gt;} /&gt;
        &lt;Route path=&quot;/productId/3&quot; element={&lt;Product3 /&gt;} /&gt;
        &lt;Route path=&quot;/productId/4&quot; element={&lt;Product4 /&gt;} /&gt;
                            ...
        &lt;Route path=&quot;/productId/1&quot; element={&lt;Product100000 /&gt;} /&gt;
          &lt;Route path=&quot;/productId/1&quot; element={&lt;Product100001 /&gt;} /&gt;
        &lt;Route path=&quot;/productId/1&quot; element={&lt;Product100002 /&gt;} /&gt;
      &lt;/Routes&gt;
    &lt;/BrowserRouter&gt;
  );
};</code></pre>
<p>바로 이렇게 말이죠!
할 수야 있겠지만 팔이 빠져 죽거나, 안압이 올라서 죽거나, 지루해 죽거나 할 것 같습니다.</p>
<p>이러한 약점을 보완하고자 나온 것이 바로 동적 라우팅입니다.</p>
<p>아래 예시를 보면서 차근차근 이해해볼까요??</p>
<h3 id="routerjs">Router.js</h3>
<pre><code class="language-js">//Router.js
import React from &quot;react&quot;;
import { BrowserRouter, Routes, Route } from &quot;react-router-dom&quot;;
import Product from &quot;./components/Product&quot;;
import App from &quot;./App&quot;;

const Router = () =&gt; {
  return (
    &lt;BrowserRouter&gt;
      &lt;Routes&gt;
        &lt;Route path=&quot;/&quot; element={&lt;App /&gt;} /&gt;
        &lt;Route path=&quot;/product/:id&quot; element={&lt;Product /&gt;} /&gt;
      &lt;/Routes&gt;
    &lt;/BrowserRouter&gt;
  );
};

export default Router;</code></pre>
<h3 id="appjs-1">App.js</h3>
<pre><code class="language-js">//App.js
import React from &quot;react&quot;;
import { Link, useNavigate } from &quot;react-router-dom&quot;;

const App = () =&gt; {
  return (
    &lt;div&gt;
      &lt;p&gt;App component입니다.&lt;/p&gt;
      {PRODUCT_LIST.map((prd) =&gt; {
        return (
          &lt;div key={prd.id}&gt;
            &lt;Link to={`/product/${prd.id}`}&gt;{prd.text}&lt;/Link&gt;
          &lt;/div&gt;
        );
      })}
    &lt;/div&gt;
  );
};

export default App;

const PRODUCT_LIST = [
  { id: 1, text: &quot;go to product/1&quot; },
  { id: 2, text: &quot;go to product/2&quot; },
  { id: 3, text: &quot;go to product/3&quot; },
  { id: 4, text: &quot;go to product/4&quot; },
  { id: 5, text: &quot;go to product/5&quot; },
  { id: 6, text: &quot;go to product/6&quot; },
  { id: 7, text: &quot;go to product/7&quot; },
  { id: 8, text: &quot;go to product/8&quot; },
  { id: 9, text: &quot;go to product/9&quot; },
  { id: 10, text: &quot;go to product/10&quot; },
  { id: 11, text: &quot;go to product/11&quot; },
  { id: 12, text: &quot;go to product/12&quot; },
  { id: 13, text: &quot;go to product/13&quot; },
];
</code></pre>
<h3 id="productjs">Product.js</h3>
<pre><code class="language-js">//Product.js
import React from &quot;react&quot;;
import { Link, useParams } from &quot;react-router-dom&quot;;

const Product = () =&gt; {
  const params = useParams();
  const prdId = params.id;
  return (
    &lt;div&gt;
      &lt;p&gt;{`/product/${prdId}`}입니다. 환영합니다.&lt;/p&gt;
      &lt;button&gt;
        &lt;Link to=&quot;/&quot;&gt;홈으로 이동&lt;/Link&gt;
      &lt;/button&gt;
    &lt;/div&gt;
  );
};

export default Product;
</code></pre>
<p>위 로직이 동적 라우팅을 구현한 예시입니다.</p>
<p>&lt;Route path=&quot;/product/:id&quot; element={<Product />} /&gt;
먼저 Router.js에서 해당 로직을 통해 프로덕트의 Id에 따른 페이지 이동을 설정해줍니다.</p>
<p>아이디에 따라 /product/1, /product/2 등으로 브라우저 경로가 설정됩니다.</p>
<p>이 경로는 바로 App.js에서 구현되었습니다.
상품 백엔드 API 대신 가상의 상품 13개를 상수데이터로 선언하고 map 함수를 통해 각 상품으로 이동하는 로직입니다.</p>
<p>각각의 링크를 누르게 되면 상수데이터에 선언된 id값이 브라우저의 엔드포인트로 설정되며
Router.js에서 이를 이용해 브라우저 경로를 사용할 수 있습니다.</p>
<p>마지막으로 Product.js에서 react-router-dom의 내장 hook인 <strong>useParams()</strong>를 사용합니다.
params에는 각 상품마다 {id:${id}} &lt;&lt; 이와 같은 객체 형태의 데이터가 들어있습니다.
prdId로 각 상품의 id값을 추출해 return문 안에서 활용하였습니다.</p>
<h3 id="결과-1">결과</h3>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/74f3565d-67fe-47b5-9e8f-1192d592419d/image.gif" alt=""></p>
<p>이렇게 동적 라우팅까지 구현해보았습니다.
이어서 query String를 포스팅 하겠습니다.</p>
<hr>
<h2 id="query-string">Query String</h2>
<p>쿼리스트링은 브라우저 주소에 key=value 형태의 데이터를 함께 넘겨 보다 디테일한 정보를 구현할 수 있습니다.
형태는 아래와 같습니다.</p>
<blockquote>
<p><a href="https://www.sample.com?color=blue&amp;sort=newest">https://www.sample.com?color=blue&amp;sort=newest</a></p>
</blockquote>
<p>도메인네임 후반 &#39;?&#39;가 쿼리스트링을 시작하는 구분자입니다.
그리고 color=blue, sort=newest와 같이 key=value 형태로 조건을 지정합니다.
이 둘 사이에는 &#39;&amp;&#39;로 서로를 구분해줍니다.</p>
<h3 id="routerjs-1">Router.js</h3>
<pre><code class="language-js">import React from &quot;react&quot;;
import { BrowserRouter, Routes, Route } from &quot;react-router-dom&quot;;
import List from &quot;./components/List&quot;;
import App from &quot;./App&quot;;

const Router = () =&gt; {
  return (
    &lt;BrowserRouter&gt;
      &lt;Routes&gt;
        &lt;Route path=&quot;/&quot; element={&lt;App /&gt;} /&gt;
        &lt;Route path=&quot;/list&quot; element={&lt;List /&gt;} /&gt;
      &lt;/Routes&gt;
    &lt;/BrowserRouter&gt;
  );
};

export default Router;
</code></pre>
<h3 id="appjs-2">App.js</h3>
<pre><code class="language-js">import React from &quot;react&quot;;
import { Link } from &quot;react-router-dom&quot;;

const App = () =&gt; {
  return (
    &lt;div&gt;
      &lt;p&gt;App component입니다.&lt;/p&gt;
      &lt;Link to=&quot;/list?sort=newest&amp;sort=popular&amp;offset=0&amp;limit=20&quot;&gt;
        List페이지로 이동
      &lt;/Link&gt;
    &lt;/div&gt;
  );
};

export default App;
</code></pre>
<h3 id="listjs">List.js</h3>
<pre><code class="language-js">import React from &quot;react&quot;;

const List = () =&gt; {
  return (
    &lt;section&gt;
      &lt;h1&gt;Welcome to list Page 😃&lt;/h1&gt;
    &lt;/section&gt;
  );
};

export default List;
</code></pre>
<h3 id="list-page-이동">List page 이동</h3>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/1e74a983-053d-475b-ad9c-d8c0406860f3/image.png" alt=""></p>
<p>링크를 클릭하면
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/90472b98-a140-49f5-8691-c7c59118ef64/image.png" alt=""></p>
<p>App Component에서 List페이지로 이동하는 링크가 있습니다.
링크에는 sort, offset, limit등의 쿼리 파라미터가 들어있습니다.</p>
<ul>
<li><p><strong>useLocation</strong></p>
<pre><code class="language-js">//List.js
import React from &quot;react&quot;;
import { useLocation } from &quot;react-router-dom&quot;;

const List = () =&gt; {
  const location = useLocation();
  console.log(location);
  return (
    &lt;section&gt;
      &lt;h1&gt;Welcome to list Page 😃&lt;/h1&gt;
    &lt;/section&gt;
  );
};

export default List;
</code></pre>
<p>먼저 useLocation입니다. 
뭐하는 친구인지 콘솔을 찍어봅시다.
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/4fc5099d-ea80-40ed-bc20-12cfb10174b1/image.png" alt=""></p>
<p>여러가지 데이터가 나오네요 ^^
여기서 의미있는 데이터는 search인 것으로 보입니다.</p>
<p>그러나 search를 보면 하나의 스트링으로 모든 조건이 걸려있는 것을 볼 수 있습니다.
각각의 데이터를 사용하려면 &amp;로 split을 하고 key,value를 또 각각 구하는 등의 번거로움이 있네요.</p>
<p>이 때 사용하면 좋은 것인 useSearchParams훅입니다.</p>
</li>
<li><p><strong>useSearchParams</strong>
사용법</p>
<blockquote>
<p>const [searchParams, setSearchParams] = useSearchParams();</p>
</blockquote>
<p>형태가 useState와 유사하죠??
그렇지만 조금 다릅니다.
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/c45572e8-bbb6-450f-8070-78cf0d011938/image.png" alt=""></p>
<p>뭔가 나오기는 했는데 의미있는 데이터는 딱히 보이지 않네요 😅
우리는 이 녀석에게 들어있는 내부 메소드들을 활용해야 합니다.
이 중 유의미한 데이터를 뽑아내는 것들은 무엇이 있는지 살펴봅시다.</p>
<p><strong>searchParams.get(key)</strong>
=&gt; 특정한 key-value를 가져오는 메소드입니다.</p>
<blockquote>
<p>searchParams.get(&#39;offset&#39;) = 20</p>
</blockquote>
<p><strong>searchParams.getAll(key)</strong>
=&gt; 동일한 키 값이 복수개일 때 get 메소드를 활용하면 맨 처음 값만 리턴하지만 getAll은 배열 형태로 전부 리턴합니다.</p>
<blockquote>
<p>searchParams.getAll(&#39;sort&#39;) = [&#39;newest&#39;, &#39;popular&#39;]</p>
</blockquote>
<p><strong>searchParams.toString()</strong>
=&gt; useLocation.search와 같이 쿼리 스트링을 그냥 string 형태로 뽑아냅니다.</p>
<blockquote>
<p>searchParams.toString() = &quot;?sort=newest&amp;sort=popular&amp;offset=0&amp;limit=20&quot;</p>
</blockquote>
<p><strong>searchParams.set(key, value)</strong>
=&gt; 인자로 전달한 key 값에 새로운 value를 설정합니다.
=&gt; sort [&#39;newest&#39;, &#39;popular&#39;]같이 복수개의 값을 가진 key일 경우 새로 설정하는 value만 저장합니다.</p>
<blockquote>
<p>searchParams.set(&#39;sort&#39;, &#39;popular&#39;)
결과 : &quot;?sort=clear&amp;offset=0&amp;limit=20&quot;</p>
</blockquote>
<p><strong>searchParams.append(key, value)</strong>
=&gt; 기존 쿼리 스트링에 새로운 key=value를 추가합니다.</p>
<blockquote>
<p>searchParams.append(&#39;sort&#39;, &#39;clear)
결과 : &quot;?sort=clear&amp;sort=popular&amp;sort=clear&amp;offset=0&amp;limit=20&quot;</p>
</blockquote>
</li>
</ul>
<h3 id="listjs-1">List.js</h3>
<pre><code class="language-js">//List.js
import React from &quot;react&quot;;
import { useLocation, useSearchParams } from &quot;react-router-dom&quot;;

const List = () =&gt; {
  const location = useLocation();
  const [searchParams, setSearchParams] = useSearchParams();
  console.log(&quot;useLocation&quot;, location.search);
  console.log(&quot;searchParams.get&quot;, searchParams.get(&quot;limit&quot;));
  console.log(&quot;searchParams.getAll&quot;, searchParams.getAll(&quot;sort&quot;));
  console.log(&quot;searchParams.toString()&quot;, searchParams.toString());

  searchParams.append(&quot;sort&quot;, &quot;clear&quot;);
  console.log(&quot;searchParams.append&quot;, searchParams.toString());

  searchParams.set(&quot;sort&quot;, &quot;clear&quot;);
  console.log(&quot;searchParams.set&quot;, searchParams.getAll(&quot;sort&quot;));

  return (
    &lt;section&gt;
      &lt;h1&gt;Welcome to list Page 😃&lt;/h1&gt;
    &lt;/section&gt;
  );
};

export default List;

</code></pre>
<h3 id="결과-2">결과</h3>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/47683f9c-4a96-4e8d-94c3-d61f123a2d38/image.png" alt=""></p>
<p>마치며
react-router-dom의 전반적인 내용을 다루어 보았습니다.
정리하며 저도 배운게 참 많았던 것 같습니다.
많이 부족한 포스팅이지만 읽으시는 분들께 조금이나마 도움이 되었으면 합니다.
오류가 있는 부분은 매너있게 지적해주시면 감사하겠습니다.</p>
<p>개발하시는 모든 분들 화이팅!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] RESTful API, Path parameter와 Query parameter]]></title>
            <link>https://velog.io/@jin_jin_dev/RESTful-API-RESTful-API%EB%9E%80</link>
            <guid>https://velog.io/@jin_jin_dev/RESTful-API-RESTful-API%EB%9E%80</guid>
            <pubDate>Thu, 02 Mar 2023 03:09:16 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ol>
<li>RESTful API</li>
<li>Path parameter와 Query parameter</li>
</ol>
</blockquote>
<h2 id="restful-api">RESTful API</h2>
<blockquote>
<p>REST(Representational State Transfer)
REST는 현재 가장 널리 사용되고 있는 API 설계 규칙이다. RESTful  API는 REST 규칙을 지켜 설계된 API를 말한다. self-descriptiveness의 특징을 살려 직관적으로 개발자 사이의 의사소통을 수월하게 구성된 API는 통신 이해도를 높여준다.
즉, REST API란 REST하게 API를 서술하는 방법을 부르는 용어이다.</p>
</blockquote>
<h3 id="그렇다면-rest란-무엇인가">그렇다면 REST란 무엇인가?</h3>
<ul>
<li>자원을 설명으로 구분하여 해당 자원의 상태를 주고 받는 모든 것을 의미</li>
<li>API 시스템을 구현하기 위한 다양한 아키텍처 중에 가장 널리 사용되는 형식 (REST, GraphQL, SOAP, ...)</li>
<li><strong>즉, HTTP URI(Uniform Resource Identifier)를 통해 자원(Resource)을 명시하고, HTTP Method(POST, GET, PUT, DELETE, ...)를 통해 자원에 대한 요청을 처리하도록 설계된 아키텍처를 의미</strong></li>
</ul>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/61bc28f8-a2e1-465f-b9cd-5a75b424f18e/image.png" alt=""></p>
<hr>
<h3 id="rest-api의-장단점">REST API의 장단점</h3>
<h4 id="장점">장점</h4>
<ul>
<li>URI가 무엇을 의미하는지, request가 무엇을 요청하고 resopnse가 무엇을 응답받는지 자명하다. (Self-descriptiveness)</li>
</ul>
<h4 id="단점">단점</h4>
<ul>
<li>표준 규약이 없어 안티 패턴(Ex. /products/all)으로 작성되는 경우가 흔함</li>
</ul>
<hr>
<h3 id="restful-api-설계-원칙">RESTful API 설계 원칙</h3>
<ol>
<li><p>구성요소(클라이언트와 서버 등) 사이의 인터페이스는 일관돼야 한다는 원칙
 =&gt; 플랫폼에 무관하며, 특정 언어나 기술에 종속받지 않는 특징을 의미</p>
</li>
<li><p>URI는 동사를 제외한, 명사로 구성
 =&gt; [GET]/<span style="color:red"><strong>find</strong></span>/users/1 -&gt; [GET]/users/1</p>
</li>
<li><p>Resource에 대한 행위를 HTTP method (GET, POST, PUT, DELETE)만으로 표현
 =&gt; [POST]<span style="color:red"><strong>post</strong></span>/reviews -&gt; [POST]/reviews</p>
</li>
<li><p>Resource 사이에 연관 관계 및 계층 관계가 있는 경우 &#39;/&#39;사용
 =&gt; [GET]/users/{user_id}/posts</p>
</li>
<li><p>URI 마지막 문자로 &#39;/&#39;를 포함하지 않음
 =&gt; [GET]/users/portfolios<span style="color:red"><strong>/</strong></span> -&gt; [GET]/users/portfolios</p>
</li>
<li><p>URI가 길어지는 경우 &#39;-&#39;을 사용하여 가독성을 높임
 =&gt; [GET]/users/1/<span style="color:red"><strong>ordered_items</strong></span> -&gt; [GET]/users/1/ordered-items</p>
</li>
<li><p>파일 확장자는 URI에 포함시키지 않고 파일의 확장자는 headers에 포함
 =&gt; [GET]/users/1/profile-photo.<span style="color:red"><strong>jpg</strong></span> -&gt; [GET]/users/1/profile-photo</p>
</li>
<li><p>응답 Response 의 Status code에 맞는 기본적인 규칙을 따름</p>
</li>
</ol>
<hr>
<h2 id="path-parameter--query-parameter">Path parameter / Query parameter</h2>
<h3 id="path-parameter">Path parameter</h3>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/83ed6924-a2bd-4913-9c3f-d2f8625c5d86/image.png" alt=""></p>
<p>위 그림처럼 GET 요청을 진행할 때 </p>
<p><strong>특정한 하나의 데이터 또는 정제되지 않은 데이터가 필요한 경우에 사용될 수 있다.</strong></p>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/cec00a0e-f136-4e86-96a8-cfbd394a6020/image.png" alt=""></p>
<p>새로운 데이터를 추가하기 위해 POST 요청을 진행해보자</p>
<h5 id="post">POST</h5>
<p>name : &#39;무농약 깐 생강&#39;, price : 3000원인 데이터를 추가해주었다.</p>
<h5 id="patch">PATCH</h5>
<p>id가 1번인 상품에 대해 가격을 수정하기 위해 price : 1100원을 request body에 담아 요청하였다.</p>
<h5 id="put">PUT</h5>
<p>PATCH와 동일하게 수정을 요청하는 Method이다.
그러나 PATCH와 다른 점이 있다.
PUT은 특정 데이터를 저장하기 위해 모든 데이터를 전부 요청해야 한다.</p>
<pre><code>//PUT
{
    id : 1,
    name : &quot;무농약 깐 생강&quot;,
    price : 1100
}</code></pre><p>그러나 PATCH는 수정하고 싶은 데이터만 전송한다.</p>
<pre><code>//PATCH
{
    price : 1100
}</code></pre><h3 id="query-parameter">Query parameter</h3>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/da5c1d09-0337-45b1-8105-f027927970bd/image.png" alt=""></p>
<p>엔드포인트 뒤에 ? {조건}을 통해 조건에 맞는 데이터를 요청한다.</p>
<h4 id="ordering">Ordering</h4>
<p>역순으로 데이터를 ordering하여 보내줄 것을 요청하는 GET메서드이다. 서버는 Query String을 받아 클라이언트가 원하는 방식으로 데이터를 보내주고 있다.</p>
<h4 id="pagenation">Pagenation</h4>
<p>주로 원하는 만큼 데이터를 불러오는 Pagenation이다. offset 0번부터 시작하여 100개의 데이터를 요청하고 있다.</p>
<p>이렇게 Query String은 원하는 조건을 주어 데이터를 정제하여 가져오는 경우에 사용할 수 있다.</p>
<h3 id="path-parameter-vs-query-parameter">Path Parameter vs Query Parameter</h3>
<p>Path Parameter와 Query Parameter는 때에 따라 같은 결과를 가져오기도 한다. 그렇다면 각각의 방식은 어떨 때 가장 적합할까?</p>
<p>Path Parameter
전체 데이터 또는 특정 하나의 데이터를 다룰 때 사용한다.</p>
<p>Query Parameter
Query String은 filtering, ordering, searching에 많이 사용한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 인증(Authentication)/인가(Authorization)]]></title>
            <link>https://velog.io/@jin_jin_dev/%EC%9D%B8%EC%A6%9DAuthentication%EC%9D%B8%EA%B0%80Authorization</link>
            <guid>https://velog.io/@jin_jin_dev/%EC%9D%B8%EC%A6%9DAuthentication%EC%9D%B8%EA%B0%80Authorization</guid>
            <pubDate>Tue, 28 Feb 2023 05:47:13 GMT</pubDate>
            <description><![CDATA[<h2 id="인증-authentication">인증 (Authentication)</h2>
<blockquote>
</blockquote>
<ol>
<li>인증은 누군가 또는 시스템이 실제로 그 누구인지 또는 시스템인지를 결정하는 과정이다.</li>
<li>사용자의 자격 증명 정보와 인증 서버의 자격 증명 정보를 비교하여 시스템에 대한 엑세스 권한을 제공한다.</li>
<li>돈을 인출하기 위해 은행에 간다고 했을 때, 은행원에게 통장과 도장 그리고 신분증을 제시하는 과정과 비슷</li>
</ol>
<h4 id="인증의-유형">인증의 유형</h4>
<ul>
<li>SFA, 2FA, MFA 등등..</li>
<li>SFA &lt; 2FA &lt; MFA 순으로 보안레벨이 올라간다.</li>
</ul>
<h4 id="인증-절차">인증 절차</h4>
<ul>
<li><p>회원가입
=&gt; 서비스 이용을 원하는 사용자는 서비스 가입 절차를 진행
=&gt; 사용자의 아이디와 비밀번호를 데이터 베이스에 저장
=&gt; 단 사용자의 비밀번호를 입력한 문자 그대로가 아닌 암호화를 진행 한 후 저장</p>
</li>
<li><p>로그인
=&gt; 사용자가 회원가입 때 입력한 본인의 아이디와 비밀번호를 입력
=&gt; 사용자가 ㅇ비력한 정보와 DB에 저장된 정보 비교
=&gt; 정보가 일치하면 사용자 인증 정보를 사용자에게 전송</p>
</li>
</ul>
<hr>
<h2 id="인가authorization">인가(Authorization)</h2>
<blockquote>
</blockquote>
<ol>
<li>인가는 사용자에게 특정 리소스 또는 기능에 대한 액세스 권한을 부여하는 프로세스</li>
<li>사용자에게 권한을 부여하기 위해서 항상 인증 절차를 진행</li>
<li>인증 절차 후 조직의 관리자는 요청된 리소스에 대한 액세스 권한을 부여</li>
</ol>
<hr>
<h1 id="cookie와-session">Cookie와 Session</h1>
<h2 id="session">Session</h2>
<blockquote>
<ol>
<li>클라이언트가 브라우저를 통해 웹 서버에 접속한 시점으로부터 브라우저를 종료하여 연결을 끝내는 시점 동안 클라이언트와 웹 서버가 논리적으로 연결된 상태</li>
<li>서버는 Session 정보를 저장하고 클라이언트에게는 Session을 구분할 수 있는 Session ID를 부여</li>
<li>클라이언트 Requset를 보낼 때 해당 Session ID를 함께 보냄으로써, 클라이언트의 상태를 확인</li>
</ol>
</blockquote>
<h2 id="cookie">Cookie</h2>
<blockquote>
<ol>
<li>클라이언트의 컴퓨터에 저장되는 데이터 파일</li>
<li>이름, 값, 만료날짜/시간, 경로 정보 등으로 구성</li>
<li>하나의 도메인 당 20개를 가질 수 있으며, 각 4kbyte를 넘길 수 없음</li>
<li>서버에서는 HTTP Response Header에 Set-Cookie 속성을 이용하여 클라이언트에 Cookie를 제공하여 저장하게 함</li>
<li>클라이언트는 HTTP Request에 저장된 Cookie를 함께 전달하여 이전의 통신에서 사용된 정보들을 파악</li>
<li>Cookie를 이용하여 로그인을 하지 않은 상태로 장바구니에 상품을 담을 수 있게 구현 가능</li>
<li>사용자별로 다른 정보를 표시하는 것이 가능하며, 사용자의 행도오가 패턴을 분석할 수 있음</li>
</ol>
</blockquote>
<h4 id="session-기반-인증의-특징">Session 기반 인증의 특징</h4>
<ul>
<li><p>장점
=&gt; Session ID 자체에는 유의미한 개인정보가 없음
=&gt; 서버에서 정보를 관리하기 때문에 데이터의 손상 우려에 대해 상대적으로 안전
=&gt; 서버에서 상태를 유지하고 있으므로, 사용자의 로그인 여부 확인이 쉬움
=&gt; 경우에 따라서 강제 로그아웃 등의 제재를 가할 수 있음</p>
</li>
<li><p>단점
=&gt; 서버에서 모든 사용자의 상태르르 관리해야 되므로 사용자의 수가 증가 할 수록 서버에 가해지는 부하 증가
=&gt; 사용자가 증가하여 서버의 Scale Out을 해야 할 때 Session 관리가 어려워 짐</p>
</li>
</ul>
<hr>
<h1 id="token">TOKEN</h1>
<blockquote>
<ol>
<li>Token을 가지고 있으면 해당 서비스를 이용할 수 있는 권리가 있다고 간주</li>
<li>제한된 리소스에 대해 일정 기간 동안 접근할 수 있는 권한을 캡슐화</li>
<li>TOKEN은 일반적으로 의미를 알 수 없는 임의의 문자열 형태로 사용자에게 발급</li>
<li>접근 할 수 있는 리소스의 범위와 접근 가능한 기간 또한 통제 가능</li>
</ol>
</blockquote>
<h4 id="token-기반-인증의-특징">TOKEN 기반 인증의 특징</h4>
<ul>
<li><p>장점
=&gt; Token을 사용자 측에서 저장하므로 서버의 메모리나 DB 등의 부담이 없음
=&gt; 사용자의 상태 정보를 서버에서 관리하지 않기 때문에 서버의 Scale Out에 용이
=&gt; Token의 만료 시간을 짧게 설정하여 안정성 증가</p>
</li>
<li><p>단점
=&gt; 사용자의 로그인 여부 확인 및 강제 로그아웃 등의 제재를 가하기 어려움
=&gt; 사용자가 임의로 토큰을 수정하거나 구조가 변경되게 되면 서버에서 확인할 수가 없음
=&gt; Payload 부분에 사용자 식별을 위한 여러 정보들이 포함 되어 있어 Session Id의 길이보다 길어져 HTTP request 전송 데이터의 크기가 증가</p>
</li>
</ul>
<hr>
<h3 id="마무리하며">마무리하며</h3>
<p>인증과 인가는 회원가입, 로그인 등 클라이언트의 정보를 검증하는데에 주로 사용된다.
Session/Cookie와 Token의 장단점을 잘 비교하며 서비스에 따라 인증/인가를 적용하면 될 것 같다.
추후 백엔드를 공부할 때 유용한 자료로 활용되었으면 좋겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[sass/scss] react-scss 사용법]]></title>
            <link>https://velog.io/@jin_jin_dev/sassscss-react-scss-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@jin_jin_dev/sassscss-react-scss-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Mon, 20 Feb 2023 14:00:46 GMT</pubDate>
            <description><![CDATA[<h2 id="sass란">sass란?</h2>
<p>우리는 HTML에 스타일을 주기 위해 css를 사용한다.
그러나 수많은 id, class, 선택자 등을 사용하게 되면 css 파일이 굉장히 보기 어려워진다.
sass는 css 코드를 HTML과 유사한 구조로 작성할 수 있는 전처리기이다.
덕분에 css 코드를 분석하는데 아주 유용하다.</p>
<p>scss는 sass에서 나온 문법이며 코드를 작성하는 규칙이 보다 익숙해 sass보다 scss를 사용하는 것을 권장한다.</p>
<h2 id="sassscss-차이">sass/scss 차이</h2>
<p>=&gt; sass는 중괄호 및 세미콜론이 없지만 scss는 있다.
=&gt; 따라서 scss가 한 눈에 구조를 파악하는데 더욱 익숙하다고 할 수 있다.</p>
<h4 id="sass">sass</h4>
<pre><code class="language-css">.list
  width: 100px
  float: left
  li
    color: red
    background: url(&quot;./image.jpg&quot;)
    &amp;:last-child
      margin-right: -10px</code></pre>
<h4 id="scss">scss</h4>
<pre><code class="language-css">.list {
  width: 100px;
  float: left;
  li {
    color: red;
    background: url(&quot;./image.jpg&quot;);
    &amp;:last-child {
      margin-right: -10px;
    }
  }
}</code></pre>
<h3 id="scss-적용하기">scss 적용하기</h3>
<h4 id="설치-명령어">설치 명령어</h4>
<blockquote>
<p>$npm i sass</p>
</blockquote>
<h4 id="1-nesting">1. Nesting</h4>
<p>앞서 말했듯이 HTML 구조와 유사한 구조로 스타일 시트를 작성하는 것을 말한다.</p>
<pre><code class="language-css">nav {
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  li {
    display: inline-block;
  }

  a {
    display: block;
    padding: 6px 12px;
    text-decoration: none;
  }
}</code></pre>
<h4 id="2-변수">2. 변수</h4>
<p>[$변수명 : 값] 형태로 변수를 지정하여 사용할 수 있다.</p>
<pre><code class="language-css">
$primary-color: #333;

body {
  border: 1px solid black;
  color: $primary-color;
}

.inverse {
  background-color: $primary-color;
  color: white;
}</code></pre>
<h4 id="3--선택자">3. &amp; 선택자</h4>
<p>Nesting 내부에서 &amp; 선택자는 부모 선택자로 치환된다. 
아래 예시처럼 button { &amp;:hover }를 사용하면 button:hover와 같은 의미가 된다.</p>
<pre><code class="language-css">button {
  width: 200px;
  height: 50px;

  &amp;:hover {
    width: 400px;
    height: 100px;
  }
}

/* Compile to CSS */

button {
  width: 200px;
  height: 50px;
}

button:hover {
  width: 400px;
  height: 100px;
}</code></pre>
<h4 id="4-mixin">4. mixin</h4>
<p>mixin은 함수라고 생각하면 편하다. 
mixin 블록 안에 css 코드를 작성하면 해당 mixin을 호출하는 순간 블록 내부 스타일이 적용된다.</p>
<pre><code class="language-css">@mixin flexCenter {
  display: flex;
  justify-content: center;
  align-items: center;
}

.info {
  @include flexCenter;
}</code></pre>
<p>함수와 마찬가지로 매개변수를 넣을 수 있다.
<span style="red">인자로 null을 보내면 값 설정을 하지 않을 수도 있다.</span></p>
<pre><code class="language-css">@mixin flexSort($justify, $alignItems) {
  display: flex;
  justify-content: $justify;
  align-items: $alignItems;
}

.info {
  @include flexSort(&#39;space-between&#39;, &#39;center&#39;);
}

.feed {
  @include flexSort(&#39;space-around&#39;, &#39;center&#39;);
}</code></pre>
<hr>
<h2 id="실습">실습</h2>
<p>이번엔 벨로퍼트님 게시글을 참고하여 버튼 만들기 실습을 해볼 예정이다.
참조 : <a href="https://react.vlpt.us/styling/01-sass.html">https://react.vlpt.us/styling/01-sass.html</a></p>
<p>먼저 trusy 한 값의 이름을 className으로 그대로 들어가게 하고 falsy 값의 이름은 들어가지 않게 하는
classnames &lt;&lt; 라이브러리를 먼저 설치한다.</p>
<blockquote>
<p>npm i classnames</p>
</blockquote>
<p>사용법은 대강 아래와 같다.</p>
<pre><code class="language-javascript">import classNames from &quot;classnames&quot;;
classNames(&quot;one&quot;, { two: true, three: false, four: true }); // one two four</code></pre>
<p>자 다시 본론으로 돌아와서,,
버튼에 이것 저것 props와 해당 props를 이용한 스타일링을 진행할 예정이다.</p>
<h4 id="appjs">App.js</h4>
<pre><code class="language-javascript">import &quot;./App.css&quot;;
import Button from &quot;./components/Button&quot;;

function App() {
  return (
    &lt;&gt;
      &lt;Button size=&quot;small&quot; color=&quot;blue&quot;&gt;
        Button
      &lt;/Button&gt;
      &lt;Button size=&quot;large&quot; color=&quot;red&quot;&gt;
        Button
      &lt;/Button&gt;
      &lt;Button outline&gt;Button&lt;/Button&gt;
    &lt;/&gt;
  );
}

export default App;

</code></pre>
<h4 id="componentsbuttonjs">/components/Button.js</h4>
<pre><code class="language-javascript">import classNames from &quot;classnames&quot;;
import React from &quot;react&quot;;
import &quot;./Button.scss&quot;;

const Button = ({ children, size, color, outline }) =&gt; {
  return (
    &lt;button className={classNames(&quot;Button&quot;, size, color, { outline })}&gt;
      Button
    &lt;/button&gt;
  );
};

Button.defaultProps = {
  size: &quot;medium&quot;,
  color: &quot;black&quot;,
};
export default Button;
</code></pre>
<h4 id="componentsbuttonscss">/components/Button.scss</h4>
<pre><code class="language-css">$blue: rgb(63, 149, 241);
$red: rgb(245, 71, 19);
$black: rgb(46, 44, 44);

@mixin button-color($color) {
  background: $color;
  &amp;:hover {
    background: lighten($color, 10%);
  }
  &amp;:active {
    background: darken($color, 10%);
  }
  &amp;.outline {
    color: $color;
    background: none;
    border: 1px solid $color;
    &amp;:hover {
      background: $color;
      color: white;
    }
  }
}

.Button {
  display: inline-flex;
  color: white;
  font-weight: bold;
  outline: none;
  border-radius: 4px;
  border: none;
  cursor: pointer;

  height: 2.25rem;
  padding-left: 1rem;
  padding-right: 1rem;
  font-size: 1rem;

  &amp;.large {
    height: 3rem;
    font-size: 1.25rem;
  }

  &amp;.medium {
    height: 2.25rem;
    font-size: 1rem;
  }

  &amp;.small {
    height: 1.75rem;
    font-size: 0.875rem;
  }

  &amp;.blue {
    @include button-color($blue);
  }

  &amp;.black {
    @include button-color($black);
  }

  &amp;.red {
    @include button-color($red);
  }
}

</code></pre>
<p>먼저 App.js에서 size=&quot;small&quot;, color=&quot;blue&quot; 속성을 가진 버튼 컴포넌트를 생성했다.</p>
<p>Button.js를 보면 classnames 라이브러리를 사용해 &quot;Button&quot; 클래스명과 인자로 전달 받은 size, color 항목을 가지고 css 스타일링을 진행한다.</p>
<p>마지막으로 Button.scss에서는 [$blue, $red, $black] 각각 color가 지정된 세 변수를 정의하고
@mixin을 통해 전달 받은 색상으로 버튼을 스타일링한다.</p>
<p>color 뿐만 아니라 Button 하위에 있는 [&amp;.large, &amp;.medium, &amp;.small]을 설정하여 각 사이즈에 대응하는 버튼을 스탕일링 하였다.</p>
<p>마지막으로 outline이라는 속성을 통해 테두리만 존재하는 버튼이 생성되었다.
이 때 Button.js에서 인자로 전달하는 {outline}의 값은 trusy, falsy를 판단하여
trusy 값일 경우 &quot;outline&quot; 이라는 텍스트 값을 scss에 전달한다.</p>
<p>(참고 : &amp;.large는 Button.large와 같다.)</p>
<h4 id="결과">결과</h4>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/4af5fb42-b1e2-4684-83ab-8bb1122ed300/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[git/github] git 명령어 및 사용법을 알아보자]]></title>
            <link>https://velog.io/@jin_jin_dev/gitgithub-git-%EB%AA%85%EB%A0%B9%EC%96%B4-%EB%B0%8F-%EC%82%AC%EC%9A%A9%EB%B2%95%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@jin_jin_dev/gitgithub-git-%EB%AA%85%EB%A0%B9%EC%96%B4-%EB%B0%8F-%EC%82%AC%EC%9A%A9%EB%B2%95%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Wed, 15 Feb 2023 09:42:37 GMT</pubDate>
            <description><![CDATA[<h2 id="git-명령어">git 명령어</h2>
<h3 id="1-git-init">1. git init</h3>
<ul>
<li>로컬 저장소에 git을 사용할 준비를 하는 과정이다.</li>
<li>git init을 하게되면 현재 폴더에 .git이라는 폴더가 생성되며 해당 폴더에서 git 명령어를 사용할 수 있게된다.</li>
</ul>
<h3 id="2-git-status">2. git status</h3>
<ul>
<li>파일에 수정 및 변경이 일어나면 해당 파일의 상태를 보여주는 명령어이다.</li>
</ul>
<h3 id="3-git-add">3. git add</h3>
<ul>
<li>커밋하기 전 수정된 파일을 준비시키는 명령어이다.</li>
<li>온라인 쇼핑몰에서 물건을 구매하기 전 장바구니에 넣어두는 것과 유사하다.</li>
<li>git add [파일명] =&gt; 특정 파일을 add한다.</li>
<li>git add . =&gt; 현재 폴더의 모든 파일을 add한다.</li>
</ul>
<h3 id="4-git-commit">4. git commit</h3>
<ul>
<li>로컬 저장소에 변경된 파일을 commit한다.</li>
<li>git commit -m &quot;msg&quot; =&gt; 한 줄 메세지를 포함하여 커밋한다.</li>
<li>git commit =&gt; 여러 줄 커밋 메세지를 남기고 싶을 때 해당 명령어를 사용한다.</li>
</ul>
<h3 id="5-git-log">5. git log</h3>
<ul>
<li>해당 명령어를 통해서 커밋된 파일의 히스토리를 파악할 수 있다.</li>
</ul>
<h3 id="6-git-remote">6. git remote</h3>
<ul>
<li>원격에 있는 레포지토리를 로컬에 연결할 때 사용한다.</li>
<li>git remote add origin [레포 주소]</li>
<li>git remote --v =&gt; 현재 연동된 레포 정보를 확인할 수 있다.</li>
<li>git remote update =&gt; 원격 레포지토리에 등록된 브랜치를 전부 가져온다. (add origin만 할 경우 브랜치는 가져오지 않는다.)</li>
</ul>
<h3 id="6-git-push">6. git push</h3>
<ul>
<li>로컬 저장소에 커밋된 파일을 원격 저장소에 push하는 명령어이다.</li>
<li>이미 원격 저장소가 생성되어 연동까지 되어있어야 한다.</li>
<li>git push [로컬 저장소 별칭] [브랜치명] =&gt; ex) git push origin master</li>
</ul>
<h3 id="7-git-clone">7. git clone</h3>
<ul>
<li>로컬 PC에 원격 저장소를 복사해온다.</li>
<li>git clone [레포지토리 주소]</li>
<li>레포지토리 주소는 아래와 같이 확인할 수 있다.
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/d5476105-8f71-4928-bd85-7fe637d3b747/image.png" alt=""></li>
</ul>
<h3 id="8-git-branch">8. git branch</h3>
<ul>
<li>브랜치 생성 명령어</li>
<li>git branch =&gt; 현재 존재하는 브랜치 전부 조회</li>
<li>git branch [브랜치명] =&gt; 새로운 브랜치 생성</li>
<li>브랜치를 생성해도 현재 브랜치에서 새로 생성한 브랜치로 이동하지는 않는다. (별도로 이동해야함)</li>
</ul>
<h3 id="9-git-checkout">9. git checkout</h3>
<ul>
<li>브랜치 이동 명령어</li>
<li>git branch [브랜치명]</li>
</ul>
<h3 id="10-git-pull">10. git pull</h3>
<ul>
<li>특정 브랜치의 코드를 가져온다.</li>
<li>git clone과 유사하지만 아래와 같은 차이점이 있다.</li>
<li><span style="color:blue">git pull</span> : 특정 브랜치의 코드를 가져옴 </li>
<li><span style="color:red">git clone</span> : 원격 저장소 코드를 통째로 가져옴</li>
<li>git pull [로컬 저장소 별칭] [브랜치명] =&gt; ex) git pull origin master</li>
</ul>
<h3 id="11-git-merge">11. git merge</h3>
<ul>
<li>로컬에서 현재 브랜치와 특정 브랜치를 합칠 때 사용한다.</li>
<li>git merge [브랜치명]</li>
</ul>
<hr>
<h2 id="팀-프로젝트-workflow">팀 프로젝트 workflow</h2>
<blockquote>
<p>위에서 깃 명령어에 대해 알아보았다.
그렇다면 팀 프로젝트는 어떤식으로 진행해야 할까?
정답은 없지만 정리해보고자 한다.</p>
</blockquote>
<ol>
<li><p>팀장의 권한을 가진 사람이 레포지토리를 생성한다.
=&gt; 이 때 브랜치는 master 혹은 main 등으로 생성될 것이다.
=&gt; main으로 생성되었다 치고 이어서 작성하려고 한다.</p>
</li>
<li><p>팀원은 해당 프로젝트에 초대된 후 각각 레포지토리를 clone 한다.
=&gt; git clone을 사용해 레포지토리를 복제한다.</p>
</li>
<li><p>각 팀원은 본인의 개발 목적에 맞는 브랜치를 생성한다.
=&gt; 이 때 브랜치명은 기능 혹은 목적에 맞게 잘 지어준다.</p>
</li>
<li><p>코드를 작성한 후 본인의 브랜치로 push한다.
=&gt; 이 때 결과적으로 원격 main, 로컬 main, 원격 branch, 로컬 branch가 존재하게된다.</p>
</li>
<li><p>Pull Request (PR) 과정을 거쳐 원격 branch에 올라간 파일이 원격 main 브랜치로 merge된다.</p>
</li>
<li><p>다른 팀원은 변경된 사항을 본인의 로컬 및 브랜치에서 사용할 수 있도록, 혹은 코드 최신화를 하기 위해 main branch로 이동 후 git pull을 진행한다.</p>
</li>
<li><p>이제 팀원의 로컬 main branch에 내가 작업한 사항이 추가되었다. 
이를 다시 각자의 branch로 이동 후 git merge main 명령어를 통해 개개인의 브랜치와 main 브랜치를 병합한다.</p>
</li>
</ol>
<p>위 과정을 그림으로 표현하면 아래와 같다.
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/766fc6f6-e89e-4eee-a84c-93b1e6e56285/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React]내가 알아보기 쉽게 정리한 React-hooks]]></title>
            <link>https://velog.io/@jin_jin_dev/React%EB%82%B4%EA%B0%80-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-%EC%89%BD%EA%B2%8C-%EC%A0%95%EB%A6%AC%ED%95%9C-React-hooks</link>
            <guid>https://velog.io/@jin_jin_dev/React%EB%82%B4%EA%B0%80-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-%EC%89%BD%EA%B2%8C-%EC%A0%95%EB%A6%AC%ED%95%9C-React-hooks</guid>
            <pubDate>Thu, 09 Feb 2023 06:58:15 GMT</pubDate>
            <description><![CDATA[<h2 id="usestate">useState</h2>
<h4 id="state란">state란?</h4>
<p>react에서 state는 간단하게 생각하면 그냥 변수이다.
그러나 일반 변수와 차이점이 있는데, 그것은 바로 값이 변하면 브라우저 리랜더링이 일어난다는 것이다.</p>
<blockquote>
<p>사용법</p>
</blockquote>
<pre><code class="language-js">import {useState} from &#39;react&#39;;
&gt; 
&gt; export function App() =&gt; {
    const [변수명, 함수명] = useState(초기 값);
&gt;   
    return ();
}

```js
import &quot;./App.css&quot;;
import { useState } from &quot;react&quot;;

console.log(number);

function App() {
  const [number, setNumber] = useState(0);

  return (
    &lt;div&gt;
      &lt;div className=&quot;div&quot;&gt;
        &lt;input
          type=&quot;number&quot;
          value={number}
          onChange={(e) =&gt; setNumber(e.target.value)}
        /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default App;</code></pre>
<p>예시에서 number라는 변수는 비구조화 할당으로 초기 값 0으로 선언되었으며
input 태그가 변할 때마다 setNumber 함수를 통해 number의 상태를 update 시킨다.</p>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/5c0acec4-a349-4a9a-9a07-c97d53cca58d/image.gif" alt=""></p>
<p>작동 원리에 대해서 깊게 공부가 끝나면 추가로 이어서 작성할 예정이다.
대략적으로 클로저 함수를 통해 useState가 선언될 당시의 초기 값을 setState 함수를 호출할 때 이용한다는 것을 알게 되었다.</p>
<hr>
<h2 id="useeffect">useEffect</h2>
  <h4>useEffect를 알아보기에 앞서 side Effect란?</h4>
  → 내가 의도하지 않은 다른 동작을 한다.<br/>
  → console.log는 대표적인 side effect이다.<br/>
  → 즉, 외부에 있는 어떤 값을 읽어오거나 사용하는 경우를 side effect라고 한다.<br/>
  → Data Fetching, DOM 접근 및 조작, 구독(Subscribe) 등이 side effect이다.<br/>

<pre><code class="language-javascript">  //ex) side effect가 없는 경우
  const sum = (num) =&gt; {
    return num + 1;
  }
  //sum 함수 호출시 함수 외부의 요소에 접근하지 않는다.</code></pre>
<pre><code class="language-javascript">  //ex) side effect가 있는 경우
  const newNum = 1;
  const sum = () =&gt; {
      return nuewNum + 1;
  }
  //sum 함수 호출시 함수 외부의 newNum 값을 변경시켰기 때문</code></pre>
<p>  side Effect를 잘 관리해주지 않으면 렌더링 과정에서 여러가지 문제가 생길 수 있으며, 이 때문에 useEffect를 사용한다.</p>
<blockquote>
<p>사용법</p>
</blockquote>
<pre><code class="language-javascript">  import {useEffect} from &#39;react&#39;
  &gt; 
  &gt; export function App() =&gt; {
    useEffect(() =&gt; {
    &gt; //내용 작성
    }, [])
    return ();
  }
</code></pre>
<p>  useEffect의 첫 번째 인자는 함수, 두 번째 인자는 의존성 배열을 받는다.
  첫 번째로 전달받는 함수 인자는 렌더링 이후 실행된다.
  두 번째로 전달받는 배열 인자는 비어있을 경우 
  렌더링 될 때 한 번만 첫 번째 인자 함수를 수행하며, 
  배열 인자가 존재할 경우 해당 인자가 변경될 때마다 첫 번째 인자인 함수를 수행한다.</p>
<pre><code>#### case1</code></pre><pre><code class="language-javascript">  import { useEffect, useState } from &quot;react&quot;;

  function App() {
    const [cnt, setCnt] = useState(1);

    useEffect(() =&gt; {
      console.log(&quot;enter useEffect!!&quot;);
    }, []);

    return (
      &lt;div&gt;
        {cnt}
        &lt;button onClick={() =&gt; setCnt((prev) =&gt; prev + 1)}&gt;＋&lt;/button&gt;
      &lt;/div&gt;
    );
  }

  export default App;
  //result =&gt; 첫 마운트시에만 &quot;enter useEffect&quot; 출력
</code></pre>
<h4 id="결과">결과</h4>
<p>  <img src="https://velog.velcdn.com/images/jin_jin_dev/post/eb728ee7-8c1f-4741-a097-e573bb06d6f3/image.gif" alt=""></p>
<h4 id="case2">case2</h4>
<pre><code class="language-javascript">  import { useEffect, useState } from &quot;react&quot;;

  function App() {
    const [cnt, setCnt] = useState(1);

    useEffect(() =&gt; {
      console.log(&quot;enter useEffect!!&quot;);
    }, [cnt]);

    return (
      &lt;div&gt;
        {cnt}
        &lt;button onClick={() =&gt; setCnt((prev) =&gt; prev + 1)}&gt;＋&lt;/button&gt;
      &lt;/div&gt;
    );
  }

  export default App;
  //result =&gt; 첫 마운트시 + 버튼 클릭시마다 &quot;enter useEffect&quot; 출력</code></pre>
<h4 id="결과-1">결과</h4>
<p>  <img src="https://velog.velcdn.com/images/jin_jin_dev/post/09f08666-d1c4-4b6e-824a-62bb23a9a62a/image.gif" alt=""></p>
<hr>
<h2 id="usememo">useMemo</h2>
  <p>useMemo란 리렌더링시 동일한 값을 반환하는 함수를 반복적으로 호출하지 않도록
  값을 메모이제이션(메모리에 할당)하여 해당 값이 변경될 때만 함수를 호출하도록 해주는 hook이다.</p>


<blockquote>
<p>사용법</p>
</blockquote>
<pre><code class="language-javascript">  const temp = useMemo(() =&gt; { }, [item]);</code></pre>
<p>  useMemo는 첫 번째 인자로 콜 백 함수, 두 번째 인자로 의존성 배열을 입력받는다.
  의존성 배열의 값이 업데이트 될 때가 아니라면 렌더링되지 않는다.
  의존성 배열을 빈 값으로 입력하면 최초 mount시에만 동작한다.</p>
<pre><code class="language-javascript">  import { useMemo, useState } from &quot;react&quot;;

  function App() {
    const [add, setAdd] = useState(0);
    const [subtract, setSubtract] = useState(0);
    //-----------------------useMemo사용----------------
    const addResult = useMemo(() =&gt; {
      return addFunc(add);
    }, [add]);
    const subtractResult = useMemo(() =&gt; {
      return subtractFunc(subtract);
    }, [subtract]);
    //-------------------------------------------------

    function addFunc(val) {
      console.log(&quot;add&quot;);
      return val + 10;
    }

    function subtractFunc(val) {
      console.log(&quot;subtract&quot;);
      return val - 10;
    }
    return (
      &lt;div&gt;
        &lt;div className=&quot;div&quot;&gt;
          &lt;input
            type=&quot;number&quot;
            value={add}
            onChange={(e) =&gt; setAdd(parseInt(e.target.value))}
          /&gt;
          &amp;nbsp; + 10 = {addResult}
        &lt;/div&gt;
        &lt;div className=&quot;div&quot;&gt;
          &lt;input
            type=&quot;number&quot;
            value={subtract}
            onChange={(e) =&gt; setSubtract(parseInt(e.target.value))}
          /&gt;
          &amp;nbsp; - 10 = {subtractResult}
        &lt;/div&gt;
      &lt;/div&gt;
    );
  }

  export default App;</code></pre>
<p>  위 코드는 input 태그에 입력된 숫자 ± 10을 해주는 코드이다.
  입력된 숫자 ±를 실행하는 addFunc, subtractFunc 함수에 console.log를 통해 렌더링을 확인할 수 있다.
  해당 두 함수는 useMemo를 사용했으므로 useState 변수인 &quot;add&quot;, &quot;substract&quot; 변수가 변경하더라도
  값이 변하지 않는다면 렌더링을 진행하지 않는다.</p>
<h4 id="결과-2">결과</h4>
<p>  <img src="https://velog.velcdn.com/images/jin_jin_dev/post/6cc09f66-8621-4018-ab4d-03b1b57ac0a4/image.gif" alt=""></p>
<hr>
<h2 id="usecallback">useCallback</h2>
<p>  useCallback이란 useMemo와 유사한 기능으로 불필요하게 렌더링 되는 것을 방지하기 위한 hook이다.<br/>
  useMemo는 의존성배열에 입력된 <span style="color:red;">값</span>이 update될 때 렌더링을 진행하도록 하지만<br/>
  useCallback는 의존성배열에 입력된 <span style="color:red;">함수</span>가 update될 때 렌더링을 진행하도록 한다.</p>
<blockquote>
<p>사용법</p>
</blockquote>
<pre><code class="language-javascript">  const anyFunc = () =&gt; {};
  const temp = useCallback(() =&gt; { }, [anyFunc]);</code></pre>
<p>  useCallback은 첫 번째 인자로 함수, 두 번째 인자로 의존성 배열을 입력받는다.
  의존성 배열의 함수가 업데이트 될 때가 아니라면 렌더링되지 않는다.
  의존성 배열을 빈 값으로 입력하면 최초 mount시에만 동작한다.</p>
<pre><code class="language-javascript">  import { useEffect, useMemo, useState } from &quot;react&quot;;

  function App() {
    const [number, setNumber] = useState(0);

    const getNumber = () =&gt; {
      console.log(`입력된 숫자는 ${number} 입니다.`);
      return;
    };

    useEffect(() =&gt; {
      console.log(&quot;getNumber함수가 변경되었습니다.&quot;);
    }, [getNumber]);

    return (
      &lt;div&gt;
        &lt;div className=&quot;div&quot;&gt;
          &lt;input
            type=&quot;number&quot;
            value={number}
            onChange={(e) =&gt; setNumber(e.target.value)}
          /&gt;
          &lt;button onClick={getNumber}&gt;Call function&lt;/button&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    );
  }

  export default App;
    ```

  위 코드를 살펴보자.
  먼저 최초로 &quot;getNumber함수가 변경되었습니다.&quot;가 콘솔로 찍힌다.
  그리고 Call Function 버튼을 누르면 &quot;입력된 숫자는 0입니다.&quot; 가 출력된다.
  숫자를 up / down 시키면 위 문구가 계속해서 찍히는 것을 알 수 있다.
  그리고 다시 Call Function 버튼을 누르면 현재 입력된 숫자가 반영되어
  &quot;입력된 숫자는 ${현재 숫자}입니다.&quot;가 출력된다.

    #### 결과
  ![](https://velog.velcdn.com/images/jin_jin_dev/post/f936da73-d3a1-4c91-ad0d-a4dbd9db11de/image.gif)

  여기서는 사실 비효율적인 로직을 수행하고 있다.

  getNumber는 함수이기 때문에 변수에 저장할 때 별도의 메모리와 주소를 부여받는다.
  즉, 숫자를 up / down 시킬 때마다 각각 메모리 공간을 차지하는 것이다.

  이런 불필요한 메모리 할당을 하지 않기 위해 useCallback을 사용한다.
  이제 getNumber 함수를 아래와 같이 바꿔보자
    ```js
  const getNumber = useCallback(() =&gt; {
    console.log(`입력된 숫자는 ${number} 입니다.`);
    return;
  }, []);</code></pre>
<h5 id="결과-3">결과</h5>
<p>  <img src="https://velog.velcdn.com/images/jin_jin_dev/post/b4e0dcb0-5f0c-441e-8e16-329de6eac662/image.gif" alt=""></p>
<p>  getNumber 함수를 useCallback으로 감싸주었다.
  이후 값을 up / down 시켜도 &quot;getNumber 함수가 변경되었습니다.&quot; 콘솔이 출력되지 않는다.
  이는 useCallback에서 첫 번째로 입력된 함수에 변경사항이 없기 때문이다.</p>
<p>  여기서 한 가지 의문이 든다.
  <strong>number가 계속 변하는데 왜 함수에 변화가 없지?</strong> </p>
<p>  답은 이렇다.</p>
<p>  useCallback을 선언하는 시점에 number는 0이며 해당 함수가 메모이제이션 된다.
  우리가 number를 변경하더라도 메모이제이션 된 getNumber를 (즉, number가 0일 때의) 사용한다.</p>
<p>  자, 다시 아래와 같이 수정해보자.</p>
<pre><code class="language-js">  const getNumber = useCallback(() =&gt; {
    console.log(`입력된 숫자는 ${number} 입니다.`);
    return;
  }, [number]);</code></pre>
<h5 id="결과-4">결과</h5>
<p>  <img src="https://velog.velcdn.com/images/jin_jin_dev/post/6c9fda6c-d98a-4eaa-adb5-cd577d70ae5d/image.gif" alt=""></p>
<p>  의존성 배열에 number를 입력해두면 number가 바뀔 때마다 함수에도 변경이 생기기 때문에 다시 메모이제이션된다.</p>
<p>  <span style="color:red">useCallback을 사용할 때는 이 부분을 조심해서 사용하자!!</span></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] 커스텀 날짜 포맷 함수 사용하기]]></title>
            <link>https://velog.io/@jin_jin_dev/JavaScript-%EC%BB%A4%EC%8A%A4%ED%85%80-%EB%82%A0%EC%A7%9C-%ED%8F%AC%EB%A7%B7-%ED%95%A8%EC%88%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jin_jin_dev/JavaScript-%EC%BB%A4%EC%8A%A4%ED%85%80-%EB%82%A0%EC%A7%9C-%ED%8F%AC%EB%A7%B7-%ED%95%A8%EC%88%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 07 Feb 2023 13:46:14 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트로 날짜를 구할 때 보통 new Date()를 사용한다.</p>
<p>그러나 console로 해당 결과를 출력해보면 아래와 같이 나타난다.</p>
<blockquote>
<p>Tue Feb 07 2023 22:22:03 GMT+0900 (한국 표준시)</p>
</blockquote>
<p>너무나 좋은 기능이지만 우리는 보통 날짜를 웹 상에서 나타내거나</p>
<p>특정 기간을 조회하는 API를 호출할 때 보통 yyyyMMdd, yyyy/MM/dd 등의 형식으로 날짜 데이터를 사용한다.</p>
<p>즉, 위의 날짜 결과를 원하는 형식에 맞게 가공해줘야 할 필요성을 항상 느낀다.</p>
<p>매번 프로젝트를 시작할 때 비슷한 현상을 겪어왔으며</p>
<p>이를 번복하지 않기 위해 함수를 하나 만들었다.</p>
<p>(필요하면 함수를 복붙해서 쓸 예정이다.)</p>
<p>만들어질 함수는 연/월/일별 날짜를 더하거나 뺄 수 있으며</p>
<p>기본적으로 yyyyMMdd 형태로 날짜 데이터를 가공하여 리턴하나</p>
<p>별도의 구분자를 파라미터로 넘기면 yyyy/MM/dd 등의 형식으로 리턴할 수 있도록 작성하였다.</p>
<pre><code class="language-javascript">/**
 * @description
 * - 날짜를 더하거나 빼서 리턴하는 함수입니다.
 * - 현재 날짜를 구하고 싶다면 빈 파라미터로 함수를 호출하면 됩니다.
 * @param {String} type
 * -- &quot;year&quot;, &quot;month&quot;, &quot;date&quot; 중 날짜를 연산할 단위 선택하여 parameter로 사용.
 * - 미입력 혹은 &quot;year&quot;, &quot;month&quot;, &quot;date&quot; 외 다른 문자열 입력시 default : &quot;date&quot;
 * @param {Number} addition
 * -- 더하거나 뺄 날짜 value parameter.
 * - 미입력시 오늘 날짜 리턴
 * @param {String} separator
 * -- &quot;/&quot;, &quot;-&quot;등 입력된 기호로 연,월,일을 구분하는 기호.
 * - 미입력시 default : &quot;yyyyMMdd&quot;
 */
function getFormattedDate(type, addition, separator) {
  //한국 현재 시간 구하기
  const now = new Date();

  const utc = now.getTime() + now.getTimezoneOffset() * 60 * 1000;
  const koreaTimeDiff = 9 * 60 * 60 * 1000;
  let korNow = new Date(utc + koreaTimeDiff);

  let year = korNow.getFullYear();
  let month = korNow.getMonth() + 1; //month는 1~12월이 아닌 0~11월로 계산되므로 + 1을 해준다.
  let date = korNow.getDate();

  //type, addition 연산 처리 시작
  if (addition !== undefined) {
    switch (type) {
      case &quot;year&quot;:
        korNow = new Date(korNow.setFullYear(year + addition));
        break;
      case &quot;month&quot;:
        korNow = new Date(korNow.setMonth(month - 1 + addition)); //원래 getMonth는 month - 1이므로 1을 빼준다.
        break;
      case &quot;date&quot;:
        korNow = new Date(korNow.setDate(date + addition));
        break;
      default:
        korNow = new Date(korNow.setDate(date + addition));
        break;
    }
    year = korNow.getFullYear();
    month = korNow.getMonth() + 1;
    date = korNow.getDate();
  }

  return `${String(year)}${separator || &quot;&quot;}${
    String(month).length === 1 ? &quot;0&quot; + String(month) : String(month)
  }${separator || &quot;&quot;}${
    String(date).length === 1 ? &quot;0&quot; + String(date) : String(date)
  }`;
}
//현재날짜 2023-02-07
getFormattedDate(&quot;year&quot;, 5, &quot;&quot;);
//result : 20280207

getFormattedDate(&quot;month&quot;, 16, &quot;/&quot;);
//result : 2024/07/07

getFormattedDate(&quot;date&quot;, 720, &quot;-&quot;);
//result : 2025-01-27
</code></pre>
<p>마무리하며</p>
<blockquote>
<p>날짜 가공하는 함수는 언젠가 해야겠다고 마음을 먹었지만 잘 되지 않았던 것 같다.
이번에 정리한 함수가 추후 코딩하는 시간을 줄이는데 기여하기를 바란다.
ps. 함수가 괜찮다 싶으면 복붙해서 사용하셔도 좋습니다.
또한 함수에 문제가 있거나 더 좋은 방법이 있다면 소개 부탁드리겠습니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] 자바스크립트에서 this란?]]></title>
            <link>https://velog.io/@jin_jin_dev/JavaScript-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%97%90%EC%84%9C-this%EB%9E%80</link>
            <guid>https://velog.io/@jin_jin_dev/JavaScript-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%97%90%EC%84%9C-this%EB%9E%80</guid>
            <pubDate>Wed, 01 Feb 2023 16:58:49 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>this는 여러 언어에서 자주 볼 수 있는 녀석이다.
타 언어와는 달리 자바스크립트에서는 this가 휙휙 바뀔 수 있는데,
이것에 대해 이해하기 위해 한 번 알아보았다.</p>
</blockquote>
<p>먼저 자바스크립트의 this는 어떻게 동작할까?</p>
<p>MDN에서 인용하기를 자바스크립트의 this는 <span style=color:red>&quot;함수를 호출한 방법에 의해 결정된다&quot;</span> 라고 나와있다.
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/41500039-e8c6-4c18-8d1d-55b0d49971e0/image.png" alt=""></p>
<p>이게 대체 무슨 말인지 글만 보고는 이해하기 어렵다.</p>
<h2 id="ex1">Ex1)</h2>
<pre><code class="language-javascript">const dog = {
  name: &quot;puppy&quot;,
  age: &quot;2&quot;,
  getName: function() {
    console.log(&quot;my name is&quot;, this.name)
  }
}

dog.getName();        //my name is puppy</code></pre>
<p>위 코드를 살펴보자.</p>
<p>dog 객체를 선언하고 그 하위의 getName 메서드를 호출하면 &quot;my name is puppy&quot;가 출력된다.</p>
<p>여기서 this는 호출된 객체 즉, dog 객체 자체를 의미하며 </p>
<p>dog의 name을 호출하였기 때문에 puppy가 출력되었다.</p>
<hr>
<h2 id="ex2">Ex2)</h2>
<p>다음 예시를 보자.</p>
<pre><code class="language-javascript">const dog = {
  name: &quot;puppy&quot;,
  age: &quot;2&quot;,
  getName: function() {
    console.log(&quot;my name is&quot;, this.name)
  }
}

const globalDog = dog.getName;
globalDog();        //window 객체 호출</code></pre>
<p>위 코드는 첫 번째 예시와 유사하지만 전혀 다른 결과가 나온다.</p>
<p>바로 window 객체를 호출하게 되는데, 이는 globalDog()를 호출한 주체가 window 객체이기 때문이다.</p>
<p>좀 쉽게 말하자면 dog.getName()은 dog 객체가 getName 메서드를 호출하는 것이나</p>
<p>globalDog()는 단순히 dog.getName으로 초기화된 변수를 별다른 주체가 지정되지 않은 상태로 호출되는 것이기 때문에</p>
<p>최상위 객체인 window 객체가 이를 호출한 것이다.</p>
<hr>
<h2 id="ex3">Ex3)</h2>
<p>그 다음 예시를 보자</p>
<pre><code class="language-javascript">const dog = {
  name: &quot;puppy&quot;,
  age: &quot;2&quot;,
  getName: function() {
    console.log(&quot;my name is&quot;, this.name)
  }
}

const cat = {
  name: &quot;miyao&quot;,
  age: &quot;1&quot;,
  getName: dog.getName
}

cat.getName();        //my name is miyao</code></pre>
<p>새로 cat 객체를 선언하고 dog.getName의 값을 getName 메서드로 선언하였다.</p>
<p>cat.getName();을 실행하면 결과는 &quot;my name is miyao&quot;가 호출된다.</p>
<p>dog.getName 메서드의 this는 호출한 객체 자신이므로</p>
<p>여기서는 cat 객체의 getName 메서드를 호출하였기 때문에 &quot;miyao&quot;가 출력된 것이다.</p>
<hr>
<h2 id="ex4">Ex4)</h2>
<p>그렇다면 아래 소스에서는 누가 this일까?</p>
<h3 id="indexhtml">index.html</h3>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;Document&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;button id=&quot;btn&quot;&gt;this가 누굴까?&lt;/button&gt;
  &lt;/body&gt;
  &lt;script src=&quot;index.js&quot;&gt;&lt;/script&gt;
&lt;/html&gt;
</code></pre>
<h3 id="indexjs">index.js</h3>
<pre><code class="language-javascript">const dog = {
  name: &quot;puppy&quot;,
  age: &quot;2&quot;,
  getName: function () {
    console.log(this);
  },
};

const cat = {
  name: &quot;miyao&quot;,
  age: &quot;1&quot;,
  getName: dog.getName,
};

const btn = document.getElementById(&quot;btn&quot;);
btn.addEventListener(&quot;click&quot;, dog.getName);        //button Element
</code></pre>
<p>먼저 dog.getName 메서드에서 this를 콘솔로 출력하도록 변경 후 </p>
<p>button Element 클릭 이벤트를 통해 this를 호출해보니 button Element 자체가 호출되었다.
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/536b6711-f9c7-4970-81d5-5f78b8771960/image.png" alt=""></p>
<p>이 또한 cat 객체처럼 this를 호출한 객체가 button Element이기 때문이다.</p>
<p>html Element 또한 예외 없이 호출한 대상이 this가 되는 것을 확인할 수 있다.</p>
<hr>
<h2 id="this를-고정하고-싶을-때">this를 고정하고 싶을 때</h2>
<p>위 예시들처럼 호출하는 객체에 따라 this가 자꾸 변하기 때문에 </p>
<p>this를 고정할 필요가 종종 발생한다.</p>
<p>그런 상황에서는 bind를 이용하면 this를 고정할 수 있다.</p>
<pre><code class="language-javascript">const dog = {
  name: &quot;puppy&quot;,
  age: &quot;2&quot;,
  getName: function() {
    console.log(&quot;my name is&quot;, this.name)
  }
}

const cat = {
  name: &quot;miyao&quot;,
  age: &quot;1&quot;,
  getName: dog.getName
}

const bindGetName = cat.getName.bind(dog);        //my name is puppy</code></pre>
<p>bind를 사용할 경우 해당 객체로 this 를 고정할 수 있다.</p>
<p>즉, bindGetName은 &quot;my name is puppy&quot;가 된다.</p>
<hr>
<h2 id="화살표-함수에서의-this">화살표 함수에서의 this</h2>
<pre><code class="language-javascript">const cow = {
  name: &quot;beaf&quot;,
  age: &quot;5&quot;,
  getName: function() {
    console.log(&quot;my name is&quot;, this.name);
    const innerFunc = function () {
      console.log(&quot;innerFunc&quot;, this);
    }
    innerFunc();
  }
}

cow.getName();
//첫 번째 콘솔 : &quot;my name is beaf&quot; 출력
//두 번째 콘솔 : window객체 출력</code></pre>
<p>이번에는 객체에 선언된 메서드 내 또 다른 함수가 있을 때 this가 어떻게 적용되는지 확인하기 위해 위 코드를 작성했다.</p>
<p>innerFunc()의 경우 함수 호출시 window 객체가 호출된다.</p>
<p>이유인즉 위에 설명했듯이 innerFunc 함수를 호출하는 객체가 마땅히 지정되지 않아서이다.</p>
<p>그렇다면 innerFunc()의 this가 cow 객체가 되게 하려면 어떻게 해야할까?</p>
<p>먼저 위에서처럼 bind 함수를 이용하는 것이다.</p>
<p>그러나 다른 방법도 존재한다.</p>
<p>바로 화살표 함수를 사용하는 것.</p>
<pre><code class="language-javascript">const cow = {
  name: &quot;beaf&quot;,
  age: &quot;5&quot;,
  getName: function() {
    console.log(&quot;my name is&quot;, this.name);
    const innerFunc = () =&gt; {
      console.log(&quot;innerFunc&quot;, this.name);
    }
    innerFunc();
  }
}

cow.getName();
//첫 번째 콘솔 : &quot;my name is beaf&quot; 출력
//두 번째 콘솔 : &quot;innerFunc beaf&quot; 출력</code></pre>
<p>화살표 함수를 사용하면 상위 스코프의 this 객체를 그대로 사용할 수 있다.</p>
<p>그렇다면 왜 화살표 함수와 function은 차이가 나는 것일까?</p>
<p>그 이유는 function에는 this가 존재하지만, 화살표 함수에는 this가 아예 존재하지 않아서 그렇다고 한다.</p>
<p>화살표 함수에 this가 존재하지 않기 때문에 자연스럽게 상위 스코프의 객체가 this가 된다.</p>
<hr>
<h3 id="이럴-때는-화살표-함수를-쓰지-말자">이럴 때는 화살표 함수를 쓰지 말자..!</h3>
<h4 id="1-메소드">1. 메소드</h4>
<pre><code class="language-javascript">const cow = {
  name: &#39;beaf&#39;;
  callName: () =&gt; console.log(this.name);
}

cat.callName();    // undefined</code></pre>
<p>=&gt; 이 경우에는 화살표 함수가 cow 객체보다 상위인 전역 객체를 가리키므로 this.name은 undefined가 된다.</p>
<h4 id="2-생성자-함수">2. 생성자 함수</h4>
<pre><code class="language-javascript">const Foo = () =&gt; {};
const foo = new Foo()    // TypeError: Foo is not a constructor</code></pre>
<p>=&gt; 생성자 함수에서는 사용할 수 없게 만들어졌다.</p>
<h4 id="3-addeventlistener의-콜백함수">3. addEventListener()의 콜백함수</h4>
<pre><code class="language-javascript">const button = document.getElementById(&#39;btn&#39;);

button.addEventListener(&#39;click&#39;, () =&gt; {
  console.log(this);    // Window
  this.innerHTML = &#39;clicked&#39;;
});

button.addEventListener(&#39;click&#39;, function() {
   console.log(this);    // button 엘리먼트
   this.innerHTML = &#39;clicked&#39;;
});</code></pre>
<p>=&gt; <mark>addEventListener</mark>의 콜백함수에서는 this에 해당 이벤트 리스너가 호출된 엘리먼트가 바인딩되도록 정의되어 있다.
처럼 이미 this의 값이 정해져있는 콜백함수의 경우, 화살표 함수를 사용하면 기존 바인딩 값이 사라지고 상위 스코프(이 경우엔 전역 객체)가 바인딩되기 때문에 의도했던대로 동작하지 않을 수 있다.</p>
<h2 id="마무리">마무리</h2>
<p>this에 대해 공부하고 정리하다보니 여러 상황에서 어떻게 사용해야할지 감이 조금 잡힌 것 같다.
프로젝트를 진행하며 분명히 this 관련해 문제를 맞닥뜨릴 것인데, 그 때 좋은 참고자료가 되었으면 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] Closure란?]]></title>
            <link>https://velog.io/@jin_jin_dev/JavaScript-Closure%EB%9E%80</link>
            <guid>https://velog.io/@jin_jin_dev/JavaScript-Closure%EB%9E%80</guid>
            <pubDate>Wed, 01 Feb 2023 14:49:17 GMT</pubDate>
            <description><![CDATA[<h3 id="closure">Closure</h3>
<blockquote>
<p>클로저란 함수와 렉시컬(Lexical) 환경의 조합을 말한다.
(렉시컬 환경이란 내부함수가 선언되었을 때의 스코프를 의미한다.
<span style=color:red>어디에서 호출하였는지가 아닌 어디에서 선언하였는지</span>에 따라 결정된다.)
즉, 함수가 생성될 당시 외부 변수를 기억하며
생성 이후에도 계속 접근이 가능하다.</p>
</blockquote>
<p>간단한 예시를 살펴보자</p>
<pre><code class="language-javascript">const a = 10;

function add(b) {
  return a + b;
}
add(5);        //result : 15</code></pre>
<p>먼저 간단한 예시로 add 함수를 선언해보았다.</p>
<p>add 함수에 5의 값을 인자로 넘기면 15가 리턴된다.</p>
<p>add 함수의 렉시컬 스코프에서는 b에 대한 값만 얻을 수 있는데 어쨰서 15가 리턴되었을까??</p>
<p>이는 상위 스코프에 a가 선언되었기 때문이다.</p>
<p>add 함수 내에서 a 변수를 찾을 수 없자 상위 스코프에서 이를 찾아낸 것이다.</p>
<p>즉, 상위 스코프의 변수를 가져다 사용한 것이다.</p>
<hr>
<p>다음 예시를 살펴보자.</p>
<pre><code class="language-javascript">function add(a) {
  return function(b) {
    return function(c) {
      return a+b+c;
    }
  }
}

const add1 = add(10);
const add2 = add1(20);
const add3 = add2(30);

const add4 = add(10)(20)(30);

console.log(add3);        //60
console.log(add4);        //60</code></pre>
<p>내부함수가 여러개일 경우에도 클로저는 동작한다.</p>
<p>add3,4의 결과가 60이 나오는 것은 상위 렉시컬 스코프의 변수에 접근을 했다는 뜻이다.</p>
<p>add3 함수가 생성된 이후에도 add 함수의 a, b에 접근 가능하다.</p>
<hr>
<pre><code class="language-javascript">function counter() {
  let cnt = 0;    //은닉화

  return function() {
    return cnt++;
  }
}

const plus = counter();

console.log(plus());    //0
console.log(plus());    //1
console.log(plus());    //2</code></pre>
<p>위 코드를 살펴보자.</p>
<p>plus 함수가 선언될 때마다 cnt는 증가되는 counter 함수이다.</p>
<p>함수 내부에 선언된 cnt는 외부에서 조작할 수 없다.</p>
<p>즉, 클로저를 통한 은닉화 또한 가능하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] useEffect로 외부 async await(비동기) 함수 호출하기]]></title>
            <link>https://velog.io/@jin_jin_dev/React-useEffect%EB%A1%9C-%EC%99%B8%EB%B6%80-async-await%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%95%A8%EC%88%98-%ED%98%B8%EC%B6%9C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jin_jin_dev/React-useEffect%EB%A1%9C-%EC%99%B8%EB%B6%80-async-await%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%95%A8%EC%88%98-%ED%98%B8%EC%B6%9C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 20 Jan 2023 23:11:35 GMT</pubDate>
            <description><![CDATA[<p>현재 내 위치 기준으로 날씨를 알 수 있는 사이드 프로젝트를 진행하던 중이었다.</p>
<p>App Component에서 날씨 API를 호출하는게 아닌</p>
<p>별도의 js파일에서 내 위치 구하기 및 해당 위치의 날씨 API를 호출하여</p>
<p>내게 필요한 형태의 데이터로 가공 후 return하도록 비동기 함수를 작성하였다.</p>
<p>해당 함수를 App Component에서 호출하여 사용하도록 할 목적이다. (App.js는 가급적 clean하게 사용하기 위함..!)</p>
<p>이를 수행하기 위해 먼저 아래와 같이 순서를 나누었다.</p>
<ol>
<li>내 위치의 위도 경도 구하기</li>
<li>구한 위도, 경도를 가지고 날씨 API를 호출해 원하는 데이터 가공하기</li>
</ol>
<hr>
<h4 id="1-내-현재-위치-위도-경도-구하기">1. 내 현재 위치 위도 경도 구하기</h4>
<p>먼저 GetCurrentWeather.js에서 위도, 경도를 구하는 처리를 진행했다.</p>
<pre><code class="language-javascript">GetCurrentWeather.js

export const GetCurrentWeather = async () =&gt; {
  const myPos = await getMyPos();
  const latitude = myPos.coords.latitude;
  const longitude = myPos.coords.longitude;
}


const getMyPos = () =&gt; {
  return new Promise((resolve, rejected) =&gt; {
    navigator.geolocation.getCurrentPosition(resolve, rejected);
  });
};</code></pre>
<p>GetMyPos라는 함수는 navigator 객체를 사용하며
navigator.geolocation은 사용자의 현재 위치 정보를 가져오는 자바스크립트 API이다.</p>
<p><span style="color:red">GetCurrentWeather()는 async 함수이기 때문에 정상적인 비동기 함수가 되려면
getMyPos()는 Promise 객체를 리턴해야 한다.<span></p>
<p>(※ 참고 : getCurrentPosition(success, error, option) -&gt; 3개의 매개변수를 받으며 error, option은 생략 가능)
리턴된 myPos.coords를 통해 위도, 경도를 구할 수 있다.</p>
<h4 id="2-위도-경도를-통해-날씨-api-호출하기">2. 위도, 경도를 통해 날씨 API 호출하기</h4>
<p>날씨 API를 사용하기 위해 weatherstack이라는 페이지에 가입해 APIKey를 발급받았다.
보안상 .env파일에 API URL, API Key 등을 환경변수로 설정하고 
실제 통신할 때 process.env.{환경변수}로 접근해 API 호출을 진행했다.</p>
<pre><code class="language-javascript">GetCurrentWeather.js

export const GetCurrentWeather = async () =&gt; {
  const myPos = await getMyPos();
  const latitude = myPos.coords.latitude;
  const longitude = myPos.coords.longitude;

  return await getWeatherByCurrentLocation(latitude, longitude);
};

const getMyPos = () =&gt; {
  return new Promise((resolve, rejected) =&gt; {
    navigator.geolocation.getCurrentPosition(resolve, rejected);
  });
};

const getWeatherByCurrentLocation = async (lat, lon) =&gt; {
  let weather = {};
  const url = `${process.env.REACT_APP_WEATHER_URL}/current`;
  const apiKey = `${process.env.REACT_APP_WEATHER_API_KEY}`;

  try {
    const response = await fetch(
      `${url}?access_key=${apiKey}&amp;query=${lat},${lon}`
    );
    const data = await response.json();
    if (data.success !== undefined &amp;&amp; data.success === false) {
      throw new Error(data.error.info);
    }

    weather.country = data.location.country; //국가
    weather.city = data.location.name; //도시
    weather.temperature = data.current.temperature; //온도
    weather.whatherIcon = data.current.weather_icons; //날씨아이콘
    weather.weatherDescription = data.current.weather_descriptions; //날씨상태
    weather.windSpeed = data.current.wind_speed; //풍속
    weather.windDir = data.current.wind_dir; //풍향
    //return weather;
    return weather;
  } catch (error) {
    return error;
  }
};
</code></pre>
<p>전송 method는 get방식이며 fetch를 통해 통신을 진행한 후 
리턴된 데이터를 가공하여 현재 내가 위치한 국가, 도시, 온도 등등 필요한 데이터를 가공하였다.</p>
<pre><code class="language-javascript">App.js

import { useEffect, useState } from &quot;react&quot;;
import GlobalStyle from &quot;./assets/styles/GlobalStyle&quot;;
import { GetCurrentWeather } from &quot;./components/GetCurrentWeather&quot;;

function App() {
  const [data, setData] = useState();
  useEffect(() =&gt; {
    GetCurrentWeather().then((res) =&gt; setData(res));
  }, []);

  return (
    &lt;&gt;
      &lt;GlobalStyle /&gt;
      {JSON.stringify(data)}
    &lt;/&gt;
  );
}

export default App;</code></pre>
<p>마지막으로 App.js에서 애플리케이션 시작시 
GetCurrentWeather() 함수를 1회 호출해 사용하기 위해 useState, useEffect를 사용하였다.</p>
<p>useEffect 내에서 매개변수로 받는 함수는 async 함수가 아니므로
GetCurrentWeather 함수를 그냥 호출하게 되면 Promise 객체가 리턴되기 때문에
Promise[Pending] 상태가 되어버린다.</p>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/a36dd506-a2ee-41e2-a8d7-5b309c138cc0/image.png" alt=""></p>
<p>이런 경우에 <span style="color:red">.then을 이용해 Promise 객체의 리턴 값을 사용할 수 있다.</span></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[git] 원격 저장소 연결하기]]></title>
            <link>https://velog.io/@jin_jin_dev/git-%EC%9B%90%EA%B2%A9-%EC%A0%80%EC%9E%A5%EC%86%8C-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jin_jin_dev/git-%EC%9B%90%EA%B2%A9-%EC%A0%80%EC%9E%A5%EC%86%8C-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 19 Jan 2023 15:29:48 GMT</pubDate>
            <description><![CDATA[<p>개발을 하다보면 개인이 여러 PC를 오가며 동일한 프로젝트를 작업하거나
팀원들과 하나의 프로젝트를 작업하라면 git과 같은 버전(형상) 관리 도구를 이용해야 한다.</p>
<p>그 중 이미 생성되어 있는 원격 저장소를 
현재 작업하려는 내 PC에 연동하는 방법을 소개해보고자 한다.</p>
<p>참고로 필자는 vscode를 이용해 git을 연동하였다.</p>
<h3 id="1-git-clone">1. git clone</h3>
<p>git clone은 말 그대로 이미 존재하는 원격 저장소를 복사해 사용하겠다는 원리이다.</p>
<p>명령어는 아래와 같다.</p>
<pre><code>$git clone [레포지토리 주소] [디렉토리]</code></pre><p>이미 생성되어 있는 레포지토리를 복사해보도록 하자.
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/2f17cbf1-1e29-444c-850c-52bd2a65f80d/image.png" alt=""></p>
<p>이제 test222라는 테스트 폴더를 생성 후 해당 폴더에 저장소를 연동해보려 한다.
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/9d9894aa-dcbe-480c-890c-536d897f5ed4/image.png" alt=""></p>
<h3 id="결과">결과</h3>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/ce086c60-6d45-4639-86b1-fbf0dd6ba425/image.png" alt="">
정상적으로 폴더가 생성된 것을 확인할 수 있다.</p>
<hr>
<h3 id="2-git-remote">2. git remote</h3>
<p>이 방법은 내 로컬 저장소와 원격 저장소를 연동하기 위한 방법이다.
원격 저장소는 1. git clone과 동일한 레포를 사용하려고 한다.</p>
<p>이번에는 test333 폴더를 테스트로 만들어 진행했다.
순서는 아래와 같다.</p>
<ol>
<li><p>먼저 git을 초기화시켜준다.
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/e20ec983-b673-4aad-89d8-8d79b039b8f8/image.png" alt=""></p>
</li>
<li><p>이후 원격 저장소의 레포를 remote add 한다.
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/3657637b-0c42-4f38-b3a5-70b3d3166f58/image.png" alt=""></p>
</li>
</ol>
<p>그런데 오잉.. ? 폴더에 아무것도 생성이 안된다.
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/976a4bb9-0742-4b7d-b6c4-f74613629500/image.png" alt=""></p>
<p>원인은 로컬 저장소와 원격 저장소를 연결했을 뿐 별도로 pull 작업을 하지 않았기 때문이다.</p>
<ol start="3">
<li>git pull
<img src="https://velog.velcdn.com/images/jin_jin_dev/post/81efa142-aed6-4af7-bb74-848f540a3517/image.png" alt=""></li>
</ol>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/ab309c48-386b-4170-9e86-a845cbf13704/image.png" alt=""></p>
<p>이제야 저장소 연동이 완료되었다.</p>
<hr>
<h3 id="trouble-shooting">trouble shooting</h3>
<p>위 두 방법으로 연동을 진행하다가 몇 가지 에러가 발생하였고, 
추후 동일한 상황이 발생했을 때 참고하기 위해 기록하려고 한다.</p>
<h3 id="1-git-clone---fatal-destination-path-디렉토리-already-exists-and-is-not-an-empty-directory-에러-발생">1. git clone - &quot;fatal: destination path &#39;[디렉토리]&#39; already exists and is not an empty directory.&quot; 에러 발생</h3>
<h4 id="발생-경위">발생 경위</h4>
<p>git clone을 진행하기에 앞서 git init으로 초기화를 진행하였다.
이 경우 폴더에 .git 폴더가 생성되며 동일한 폴더에 clone을 진행하다가 맞닥뜨린 에러이다.</p>
<pre><code>$git clone https://github.com/Dante6327/TodoList.git .

&quot;fatal: destination path &#39;.&#39; already exists and is not an empty directory.&quot; 에러 발생!!</code></pre><p>필자는 현재 폴더에 원격 저장소를 복제하기 위해 마지막 &#39;.&#39;을 붙였다.
즉, 이미 .git 폴더가 생성되어있는 상태로 원격 저장소를 복제했기 때문에 발생한 상황이다.</p>
<h4 id="해결-방법">해결 방법</h4>
<p>현재 디렉토리에 원격 저장소를 복사해 쓰려면 $git init 명령어를 실행하지 않고 $git clone을 진행하면 된다.</p>
<hr>
<h3 id="2-git-remote---fatalrefusingo-merge-unrelated-histories-에러-발생">2. git remote - &quot;fatal:refusingo merge unrelated histories&quot; 에러 발생</h3>
<h4 id="발생-경위-1">발생 경위</h4>
<p>git remote 이후 git pull을 진행해 원격 저장소를 먼저 연동하기에 앞서
로컬 저장소에 변경점을 준 후 git pull을 진행하여 에러가 발생했다.</p>
<pre><code>$git init
$git remote add origin https://github.com/Dante6327/TodoList.git

폴더에서 index.html 파일 생성 및 삭제 (해당과정 때문에 에러 발생!!)

$git pull origin master

&quot;fatal:refusingo merge unrelated histories&quot; 에러 발생!!</code></pre><p>로컬과 원격 저장소를 비교해 반영해야 하는데, 서로 다른 부분이 생기다보니 당연하게도 충돌이 발생된 것이다.</p>
<p>이럴땐 아래 pull 명령어를 수행하면 된다.</p>
<pre><code>$git pull origin master --allow-unrelated-histories</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[CSS] 반응형 분기점 및 media query에 대해서]]></title>
            <link>https://velog.io/@jin_jin_dev/CSS-media-query%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C</link>
            <guid>https://velog.io/@jin_jin_dev/CSS-media-query%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C</guid>
            <pubDate>Wed, 18 Jan 2023 17:23:07 GMT</pubDate>
            <description><![CDATA[<p>우리는 반응형 웹을 제작하고자 할 때 media query를 사용한다.
사용법은 아래와 같다.</p>
<pre><code class="language-css">@media screen and (min-width:375px) and (max-width: 400px) {
    /*코드 작성*/
}</code></pre>
<p>위 코드는 스크린의 가로 너비가 375px 이상 400px 이하일 때 
미디어 쿼리 안에 있는 css를 적용한다는 뜻이다.</p>
<p>여기서 screen은 데스크탑 화면 or 스마트폰 화면을 의미한다.</p>
<p>Frontend Mentor라는 사이트에서 간단한 프로젝트를 진행하며 media query를 사용하게 되었다.</p>
<p>해당 프로젝트는 가로 픽셀이 375px 이하일 경우 모바일 화면을,
1440px 이상일 때는 데스크탑 화면을 기준으로 제작하라고 나와있다.</p>
<p>따라서 미디어 쿼리는 아래와 같이 작성하였다.</p>
<pre><code class="language-css">@media screen and (min-width: 375px) and (max-width: 1439px) {
    /*코드 작성*/
}</code></pre>
<p>width가 1440픽셀이 되는 순간 아래와 같이 변경된다.</p>
<h3 id="375px-">375px ~</h3>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/f5c457ba-bde3-4d97-a4f6-5e6c1abbb2f3/image.png" alt=""></p>
<h3 id="1440px-">1440px ~</h3>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/c6f43890-bfd0-4c35-b5b3-724a2d5818f1/image.png" alt=""></p>
<p>즉 특정 width에서 화면이 전환되게 하려면 </p>
<p>min-width의 경우 해당 픽셀을</p>
<p>max-width의 경우 해당 픽셀 -1을 하면 된다.</p>
<p>(참고)
min-width는 개발 순서가 스마트폰 화면 =&gt; 데스크탑 화면 (즉, 점차 커져가는 화면)일 경우 주로 사용
max-width는 개발 순서가 데스크탑 화면 =&gt; 스마트폰 화면 (즉, 점차 작아지는 화면)일 경우 주로 사용</p>
<hr>
<h1 id="반응형-분기점">반응형 분기점</h1>
<p><img src="https://velog.velcdn.com/images/jin_jin_dev/post/4bf59347-2a88-4d33-a40f-cb7f06589ba5/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>스마트폰 세로: 0px ~ 480px
스마트폰 가로, 태블릿 : 481px ~ 768px
태블릿 가로, 노트북 : 769px ~ 1279px
데스크탑 : 1280px ~</p>
<p>이에 맞는 미디어 쿼리는 아래와 같을 수 있겠다.</p>
<pre><code class="language-css">/* 노트북 &amp; 테블릿 가로 (해상도 1024px ~ 1279px)*/ 
@media all and (min-width:1024px) and (max-width:1279px) { 
  //스타일 입력
} 

/* 테블릿 가로 (해상도 768px ~ 1023px)*/ 
@media all and (min-width:768px) and (max-width:1023px) { 
  //스타일 입력
} 

/* 모바일 가로 &amp; 테블릿 세로 (해상도 480px ~ 767px)*/ 
@media all and (min-width:480px) and (max-width:767px) {
  //스타일 입력
} 

/* 모바일 세로 (해상도 ~ 479px)*/ 
@media all and (max-width:479px) {
  //스타일 입력
}</code></pre>
]]></description>
        </item>
    </channel>
</rss>