<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>seul-dev.log</title>
        <link>https://velog.io/</link>
        <description>Connecting dots </description>
        <lastBuildDate>Mon, 17 Oct 2022 09:12:28 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>seul-dev.log</title>
            <url>https://velog.velcdn.com/images/seul_/profile/71ab0989-faa8-4629-affe-ca2930027761/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. seul-dev.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/seul_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[메인 프로젝트(나만의 작은 설렘) 회고 ]]></title>
            <link>https://velog.io/@seul_/%EB%A9%94%EC%9D%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%82%98%EB%A7%8C%EC%9D%98-%EC%9E%91%EC%9D%80-%EC%84%A4%EB%A0%98-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@seul_/%EB%A9%94%EC%9D%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%82%98%EB%A7%8C%EC%9D%98-%EC%9E%91%EC%9D%80-%EC%84%A4%EB%A0%98-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 17 Oct 2022 09:12:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>코드스테이츠 프론트엔드 부트캠프 4개월 간의 교육과정 이후 2주간의 프리 프로젝트를 함께 진행한 프론트엔드 2명, 백엔드 2명 총 4명의 팀원들과 협업하여 파이널(메인) 프로젝트를 진행했고 프론트엔드로 참여했다.  </p>
</blockquote>
<h2 id="1-intro">1. Intro</h2>
<p>프로젝트 명: 나만의 작은 설렘 
프로젝트 형태: 수강생 프로젝트
팀원: 김형섭(팀장), 김주현, 한성욱, 이슬
프로젝트 기간: 2022.09.08 - 2022.10.12. (5주)  </p>
<p>배포 링크 : <a href="https://www.seollem.link/">https://www.seollem.link/</a>
깃헙 링크: <a href="https://github.com/codestates-seb/seb39_main_035">https://github.com/codestates-seb/seb39_main_035</a>
프로젝트 일지: <a href="https://flannel-handbell-dd4.notion.site/32642672dda94e0597f50519d06589b2?v=34e2a0d29c554291bee8fdca0505a521">링크</a></p>
<p><img src="https://velog.velcdn.com/images/seul_/post/7555a5db-05c6-43a6-b9bd-098912911899/image.gif" alt=""></p>
<h2 id="2-프로젝트-기획목적">2. 프로젝트 기획(목적)</h2>
<p>이번 프로젝트는 기획 단계부터 경험해 본 첫 프로젝트였다. 초반 회의에서 나온 여러 아이디어 중 유저가 남긴 독서 기록을 책 형태로 다시 볼 수 있는 기능이 가장 많은 표를 얻었고, 등록한 책들의 진행 상황을 입력하면 이를 시각화해서 독서에 대한 동기부여를 제공할 수 있는 서비스를 기획하게 됐다. 로그인한 유저의 개인적인 독서 기록이라는 점과 우리 팀이 구입한 도메인 seollem(설렘!)을 결합해서 프로젝트 명을 ‘나만의 작은 설렘’으로 결정했다.</p>
<h2 id="3-프로젝트에-사용한-기술-스택프론트엔드">3. 프로젝트에 사용한 기술 스택(프론트엔드)</h2>
<p><code>TypeScript</code>, <code>React</code>, <code>Redux-toolkit</code>, <code>redux-persist</code>,<code>styled components</code>, <code>axios</code>, <code>react-router</code></p>
<h3 id="typescript">TypeScript</h3>
<ul>
<li>멘토님이 적극 추천하셨고, 프론트 팀원이신 성욱님도 도입 해보자고 하셔서 만나게 된 타입스크립트! 프로젝트 기획 후 추석 명절에 코딩앙마 강의와 공식사이트의 핸드북으로 기본만 후다닥 익히고 프로젝트를 시작했다. 프로젝트 초반에는 수많은 에러를 만났고… 오히려 개발 속도가 느려지는 느낌이었다. 하지만 프로젝트 중반 부터는, 팀원이 작성한 컴포넌트를 사용하는 경우에 타입스크립트에서 제공하는 코드 자동완성이나 에러 피드백이 굉장이 유용했다. 코드를 이해하는데 수월해지기때문에 협업하는 상황에서는 장점이 많다는 거구나!를 몸으로 느낄 수 있었다. 이번 프로젝트에서는 아주 기초적인 수준에서만 적용했지만 더 공부해서 제대로 활용해보고 싶다.</li>
</ul>
<h3 id="redux-toolkit">Redux-toolkit</h3>
<ul>
<li>프리 프로젝트에서도 사용한 리덕스 툴킷을 사용했다. redux를 사용할때 작성해야하는 보일러플레이트 코드를 줄일 수 있었고, redux-devtool등 라이브러리들이 내장되어 있어서 개발 환경에서 편리하게 사용할 수 있었다. redux-persist도 함께 사용해서 store를 세션 스토리지에 저장해놓고 새로고침 시에도 상태가 유지되도록 했다. 어려웠던 점은 typescript와 함께 사용하면서 type 정의하는 부분이었는데, 공식문서를 통해서 해결했다. 프리와 메인 두번의 프로젝트에서 사용하면서 프리 프로젝트 때 사용했던 방식이 왜 잘못되었는지(잘못된 사용보다는 왜 내 의도대로 동작하게 사용하지 못했는지?)를 알게돼서 많이 배울 수 있었던 스택이었다.</li>
</ul>
<h2 id="4-kptkeep-problem-try-회고">4. KPT(keep problem Try) 회고</h2>
<h3 id="지난번-프리프로젝트-회고-try">지난번 프리프로젝트 회고 try</h3>
<pre><code>- 문서화(깃헙 협업기능 활용하기)를 더 신경써서 해보자(이슈 세분화해서 작성, 이슈 번호로 커밋 등등) ✅
- 설계 부분에 더 많은 시간을 써서 효율적으로 개발에 돌입하기 ✅ ✨
- 상태관리 더 공부하기 (redux-toolkit 활용도 높이기) ✅  ✨
- 프로젝트 구조 관리 프로젝트 후반부까지 포기하지 말기
- 재사용되는 부분, ui 통일되어야 하는 부분은 컴포넌트로 더 쪼개서 만들기✨
- 개발 일정을 계획할때 후반부 수정하는 부분을 넉넉히 잡아두기. 클라이언트배포도 개발단계에서 지속적으로 해서 막판에 몰아서 배포 오류를 파악해야하는 상황을 피하자! ✅ ✨</code></pre><h3 id="👍-잘한-점">👍 잘한 점</h3>
<blockquote>
<p>프리프로젝트 팀 회고 및 설계 과정의 오버 커뮤니케이션 </p>
</blockquote>
<p>프리프로젝트의 배포 실패(정확히는 통신 실패)를 경험하고, 메인 프로젝트만큼은 꼭 기능을 완성해서 배포하자고 다짐했던 팀 회고부터가 우리 메인 프로젝트의 시작점었던 것 같다. 회고 과정에서 이야기 했던 <em>1. 설계 부분에 더 많은 시간을 써서 최대한 구체적으로 결정하고 효율적으로 개발에 돌입하기, 2. 클라이언트 배포도 최대한 빨리 진행하기</em>는 프로젝트 초반부터 진행과정 내내 팀원들이 염두해두고 있는 부분이었고, 그 덕분에 프로젝트를 완성시킬 수 있었다고 생각한다. 기획 단계에서 나왔던 아이디어들 가운데 현재 팀의 역량으로 구현 가능한가를 고려해서 기획을 결정한 것도 좋은 선택이었다. 화면정의서를 작성하는 부분에서는 해당 화면에서 필요한 데이터를 정의하면서 api도 함께 설계했는데, open api 선택부터 프로젝트에서 핵심적인 부분들을 대부분 결정할 수 있었다. 오버 커뮤니케이션으로 단단한 설계를 한 덕분에 기능 개발 기간에 큰 이슈가 없었다고 생각한다. </p>
<blockquote>
<p>프로젝트 초반 1차 배포를 진행하면서 CORS 해결 </p>
</blockquote>
<p>프로젝트 기능 구현 2주차, 클라이언트에서 알라딘 open api를 직접 호출해서 페이지를 구현했다. (요청횟수 등의 문제로 서버에서 알라딘 api를 호출하는 걸로 설계했지만 mock 데이터 느낌으로 이용했다. 개발 환경에서는 <a href="https://www.npmjs.com/package/http-proxy-middleware">http-proxy-middleware</a> 라이브러리를 이용해서 프록시 설정으로 cors 문제를 피할 수 있었다.) 그러던 중 계획한 aws s3 클라이언트 1차 배포를 진행하면서 cors 에러를 만나게 됐다.  멘토님께 에러 상황을 공유하며 질문드리니까 서버 response로  access-control-allow-header 값이 들어오지 않는다고 키워드를 던져주셨고, 프리 프로젝트에서도 알라딘 api와 마찬가지로 서버에서 엔드포인트가 다른 요청을 허용해주는 처리를 하지 않았다는 걸 알게됐다.(한 줄 요약하면 1차 배포를 하면서 프리 프로젝트 때의 통신 실패 이유도 알게된 것!) 백엔드 쪽에서 요청을 허용해주는 cors filter 적용해서 배포하면서 서버 통신 문제를 해결하게 됐다. (이것도 한번에 후루룩 해결되진 않았지만… 구체적인 내용은 <a href="https://flannel-handbell-dd4.notion.site/11-5c0c2801e0a543ce9ba611ffd338c488">프로젝트 일지</a>에 정리되어 있다.)</p>
<blockquote>
<p>프로젝트 전반에 사용되는 레이아웃, ui 컴포넌트 재사용</p>
</blockquote>
<p>프리 프로젝트 이후 가장 반성했던 부분이 컴포넌트로 묶어서 재사용성을 높일 수 있는 부분도 그냥 코드를 복붙해서 페이지에서 활용되는 부분이었다. 당연히 개발할때 효율도 낮았고 코드도 지저분했다. 이번에는 꼭 컴포넌트 쪼개기에 신경쓰자고 다짐했고 이 부분은 어느정도 지켜졌다. 서버 api가 배포되기 전에 화면설계를 보면서 반복해서 사용될 수 있는 부분을 파악했고 pageLayout, Title, Button, Modal, Rating 컴포넌트를 만들어서 프로젝트 곳곳에서 요긴하게 사용했다. </p>
<blockquote>
<p>문서화</p>
</blockquote>
<p>프로젝트 기간 내내 일지를 작성했다. 그냥 그날 할일 정리하고 어려움 부딪혔던 부분 캡쳐, 시도해본 해결 방법, 참고한 블로그 링크 등을 정리했다. 다시 읽어보니 문장으로 정리하지 못한 날도 많고 내용이랄게 없는 날도 많다. 그래도 덕분에 에러가 나면 일단 캡쳐를 하고 보는 습관이 생겨서 팀원들에게 에러 상황을 공유하는데도 큰 도움이 됐다.</p>
<h3 id="🥲-아쉬운-점">🥲 아쉬운 점</h3>
<blockquote>
<p>깃헙으로 프로젝트 관리 </p>
</blockquote>
<p>프로젝트 일지 작성이나, 프론트 아침 조회 등등 노션 워크스페이스에 프로젝트 전반에 대한 문서화는 꼼꼼히 했지만, 깃헙에 이슈를 세분화해서 작성하고 관련된 이슈 커밋을 날리고 이런 부분은 많이 소홀했다. 깃헙에 프로젝트 관리하는 부분은 제대로 지켜지지 않았다. 은연 중에 우리 팀은 프로젝트 진행에 대한 커뮤니케이션이 잘 되고 있다고 핑계를 대면서 이슈 작성에 소홀했다.. 다른 팀 깃헙을 구경해보니 이슈나 마일스톤으로 깔끔하게 프로젝트 관리를 한 과정이 보여서 좋아보였다. 이번 프로젝트에 적극적으로 사용하면서 깃헙 프로젝트 관리 툴에 익숙해질 수 있었는데 아쉽다. 다음 번에는 꼭 잘 작성하는 걸로! </p>
<blockquote>
<p>프로젝트 구조 관리 </p>
</blockquote>
<p>지난번에도 반성했던 부분이었지만 엄청 개선되지는 못했다. 컴포넌트 폴더에 페이지 전반에서 사용되는 컴포넌트와 특정 페이지에서 사용되는 컴포넌트들이 혼재되어 있는 상태고 역시나 프로젝트 후반부에 가서는 혼란을 야기했다. 프로젝트 기간이 끝나고 가장 신경쓰였던 부분이어서 여러 레퍼런스를 찾아보았고, 리팩토링 과정의 1순위 과제가 됐다..! </p>
<blockquote>
<p>서버 API 나오기 전에 효율적으로 개발하기</p>
</blockquote>
<p>서버 api가 나오기 전에 개발한 부분에서 수정해야할 부분이 꽤나 있었다는 점이 아쉽다. 물론 이유는 더미데이터나  mock service worker 등등을 빠르게 적용하지 않고 알라딘 api로 직접 호출해서 페이지를 구현했기 때문이다. 이번에는 백엔드 기능 구현이 엄청 빠르게 돼서 한 이틀 정도의 삽질이기는 하지만, 서버 api랑 딱맞는 데이터로 구현하고 있었으면 시간 낭비가 적었을텐데 하는 아쉬움이 남는다. </p>
<blockquote>
<p>나의 부족한 실력🥲</p>
</blockquote>
<p>내가 구현을 맡았던 부분에서는 이미지 슬라이더, 캘린더, 에디터 등등 라이브러리를 많이 사용했다. 우리 프로젝트에 딱맞게 커스텀하는게 어려웠고 일정이 많이 소요되었다. 처음부터 개발하지 않아도 되는 편리함 때문에 라이브러리를 사용하는 것이지만, 기본적인 구현 원리에 대한 이해가 부족했기 때문에 라이브러리를 가져와서 적용시키는데도 어려움이 많았다고 생각한다. 라이브러리로 구현했던 부분을 처음부터 만들어보는 시간을 가져볼 계획이다. 프로젝트 기간이 막판에  백엔드 주현님이 리프레시 토큰 발행 부분을 구현하셨는데 다른 에러 처리나 발표 준비에 밀려서 이 부분을 프론트에서 구현해내지 못했다. 이 부분까지 마무리해서 아름답게 버젼1을 마무리하고 싶다.</p>
<h3 id="💪-try">💪 Try</h3>
<p>프로젝트 이후, 개인적으로 아쉬웠던 부분을 리팩토링하려고 한다. 일단, 프로젝트 폴더 구조 정리, css 단위 통일, 리프레시 토큰 기능 추가를 해보려고 한다. 팀 전체적으로 서비스를 유지하면서 기능을 더 추가해보자는 이야기가 나와서 그것도 진행하게 될 것 같다. 아직 본격적으로 회의를 진행하지는 않았지만 기획 때 나왔던 아이디어들을(다 읽은 책에 대해서 축하팝업 띄우는 기능과 작성한 메모를 공유하는 기능) 추가해보고 싶다.</p>
<h2 id="5-outro">5. Outro</h2>
<p><del>잡세션을 진행하면서 밀려오는 현타로 계획했던 것보다 늦게 메인프로젝트 회고를 작성하게됐다. 프로젝트 기간동안 체력 관리에 소홀한 결과로 프로젝트가 끝나고 몸살이 세게 나버렸다. 더 늦으면 메인 프로젝트 경험이 다 잊혀질 것 같아서 만사 제쳐두고 회고를 적는다.</del></p>
<p>팀 프로젝트를 진행하면서 얻은 가장 큰 수확은 내가 뭘 잘 모르는지(css, react hooks, 네트워크에 대한 이해)에 대해서 좀 더 명확히 알게됐다는 점이다. 부트캠프 4개월 차에 과제도 힘들고 내 이해 수준을 수업진도에 맞추는게 정말 힘들었다. 지금 생각해보면 내가 할 수 있을까라는 의심에 잡아먹힌 시점이었고 공부도 제일 재미없었던 것 같다. 그 당시 나의 고민이나 힘듦이 너무 막연하고 총체적이었는데 이제는 ‘이부분을 리팩토링 해봐야지, 이걸 더 공부해봐야지 하는식으로’ 좀 더 구체적인 형태로 바뀐 것 같다.</p>
<p>최악의 멘탈 상태에서 그래도 끝까지는 해보자라고 마음을 다잡고 시작한 프로젝트였는데 생각보다 과정이 즐거웠다. 설계한대로 기능을 구현하는 것도, 오류가 나서 안되던걸 되게 만드는 것도 너무너무 재미있었다. 무엇보다도 프리 프로젝트에서 해결하지 못해서 찝찝했던 통신 문제를 해결하고 프로젝트를 완성할 수 있어서 좋았다. (집착적인? 내 성격이 프로젝트 진행하면서는 장점처럼 느껴졌다ㅎㅎ) </p>
<p>마지막으로 이 모든걸 함께한 분들께 감사를 전하고 싶다. 서로서로 배려하면서 적극적으로 소통하고 각자 자기 역할을 책임감있게 구현한 덕분에 프로젝트를 마무리할 수 있었다고 생각한다. 친절한 팀원들과 sos 칠때마다 항상 적극적으로 답변해주셨던 멋진 멘토님까지🫶 너무너무 수고하셨고 정말 감사했다! 모두모두 항상 건강하시고 하시는일 다 잘되시길!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[pre-project 회고(kpt) ]]></title>
            <link>https://velog.io/@seul_/pre-project-%ED%9A%8C%EA%B3%A0kpt</link>
            <guid>https://velog.io/@seul_/pre-project-%ED%9A%8C%EA%B3%A0kpt</guid>
            <pubDate>Wed, 07 Sep 2022 06:01:08 GMT</pubDate>
            <description><![CDATA[<h2 id="🥰-keep">🥰 KEEP</h2>
<blockquote>
<p>프로젝트 기간 중 잘한 점, 앞으로 계속 유지할 부분</p>
</blockquote>
<ul>
<li>잘 모르고 막연해서 어려웠던 깃헙에 익숙해질 수 있었다. pr을 올릴때 머지 충돌이 생겨도 여러번 해결해보니 두려움이 좀 사라졌다.<ul>
<li><code>resolve conflicts</code> 버튼이 활성화되지 않으면 pr할 dev브랜치를 pull 받아서 로컬에서 내 작업 브랜치와 dev브랜치를 머지하면서 충돌난 부분 해결해서 다시 올리면 된다!!</li>
<li>풀리퀘를 하루 단위로 해서 해결이 더 쉬웠던 부분도 있던 것 같아서 이 부분은 계속 지켜갔으면 좋겠다.</li>
</ul>
</li>
<li>서버 데이터를 불러왔을때 감동적이었다.</li>
<li>팀원분들 모두 피드백도 빨리 주시고 책임감있게 열심히 해주셔서 많이 배울 수 있었다.</li>
<li>프로젝트하면서 redux-toolkit이라는 상태관리 라이브러리를 처음 사용해봤는데, 깨지고 구르면서 그래도 많이 배울 수 있었다!!</li>
<li>새로운 라이브러리를 빠르게 익히는 방법은 실전에서 사용해보는 거라는 걸 다시금 깨달았다.</li>
</ul>
<h2 id="🥲-problem">🥲 PROBLEM</h2>
<blockquote>
<p>프로젝트 진행하면서 느꼈던 문제점, 아쉬웠던 점</p>
</blockquote>
<ul>
<li>전체적인 개발 일정 관리나, 작업에 소요되는 시간을 가늠하기 어려웠다.</li>
<li>redux toolkit에 대한 이해도가 떨어져서 제대로 활용을 하지 못한 것 같다. (redux-persist가 로컬 스토리지에 저장되는 것도 너무 늦게 알았다... 새로고침해도 데이터 유지되는 거구나 하고 그냥 단편적으로 알고 사용했음. 유저 정보-비밀번호는 여기에 넣어서 관리하면 안되겠구나도 너무 늦게 알아버렸다..)</li>
<li>index.js 데이터를 한번에 불러오고 이걸 각 페이지단에서 활용하는 식으로 코드를 짰는데 관리하고 활용하기 편한 부분만 생각했지, 불러오는데 걸리는 시간을 고려하지 못했던 것 같다.(위에 persist로 이전에 프로젝트를 띄웠을때의 데이터를 가지고 있어서 빨리 로드 됐던 부분이 있어서 이부분을 인지하지 못했다. 시크릿창에서도 테스트를 여러번 해봤어야했는데.._)</li>
<li>후반부로 갈수록 프로젝트 구조 관리에 신경을 쓰지 못해서 코드를 수정할때 해당부분을 찾는데만도 많은 시간이 걸린 것 같다.</li>
<li>배포문제를 해결하지 못한 점이 너무 아쉽다…</li>
<li>로그인, 회원가입 부분도 결과를 내지 못했더라도 구현을 시도해봤으면 실패에서 얻어가는 점이 있었을텐데, 우리 팀의 전체적인 개발 일정에서는 구현해보지 못한 부분이라서 아쉽다.</li>
<li>안풀리는 부분을 혼자서 어떻게든 해결해보려고하는 버릇이 있는데, 적당한 시점에 주위에 물어보고 해결되지 않는 부분을 나누는 것도 중요하다!</li>
</ul>
<h2 id="🤗-try">🤗 TRY</h2>
<blockquote>
<p>개선을 위해서 시도해볼 점 (우선순위도 생각해보자!! )</p>
</blockquote>
<ul>
<li>문서화(깃헙 협업기능 활용하기)를 더 신경써서 해보자(이슈 세분화해서 작성, 이슈 번호로 커밋 등등) ✅</li>
<li>설계 부분에 더 많은 시간을 써서 효율적으로 개발에 돌입하기 ✅</li>
<li>상태관리 더 공부하기 (redux-toolkit 활용도 높이기) ✅</li>
<li>프로젝트 구조 관리 프로젝트 후반부까지 포기하지 말기</li>
<li>재사용되는 부분, ui 통일되어야 하는 부분은 컴포넌트로 더 쪼개서 만들기</li>
<li>개발 일정을 계획할때 후반부 수정하는 부분을 넉넉히 잡아두기. 클라이언트배포도 개발단계에서 지속적으로 해서 막판에 몰아서 배포 오류를 파악해야하는 상황을 피하자! ✅</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git 개념 정리 ]]></title>
            <link>https://velog.io/@seul_/Git-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@seul_/Git-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 08 Aug 2022 15:51:10 GMT</pubDate>
            <description><![CDATA[<h2 id="git이란">Git이란?</h2>
<p>Git이란 소스 코드를 관리하기 위해 개발된 분산형 버전 관리 시스템이다. Linus Benedict Torvalds(linux 개발)가 linux 소스코드를 관리할 목적으로 개발했다. </p>
<p>Git은 branch 기능을 제공하여 같은 파일을 여러 명이 동시에 작업하고 합치는 식(merge)의 병렬 개발이 가능하다. 또한, Git은 로컬에 원격 저장소의 모든 데이터를 복제하여 인터넷이 연결되지 않은 환경에서도 개발을 진행할 수 있고, 중앙 저장소의 데이터가 사라져도 원상 복구할 수 있다. </p>
<p>Git으로 소스 코드를 관리하면 소스 코드 변경 이력을 쉽게 확인할 수 있고, 특정 시점에 저장된 버전과 비교하거나 특정 시점으로 되돌릴 수 있어서 버전 관리를 효율적으로 할 수 있고, 협업 시에도 유용하다. </p>
<h2 id="git의-3가지-작업-영역">Git의 3가지 작업 영역</h2>
<p>Git은 <strong>Working Directory, Staging Area, Git Directory(Repository)</strong> 3가지 작업영역으로 파일들을 관리한다.
<img src="https://velog.velcdn.com/images/seul_/post/1a297be9-bb2f-4f40-abe4-d88110c6954c/image.png" alt=""></p>
<ul>
<li>Working Directory : 소스 코드를 작업하는 프로젝트 디렉토리, Git이 추적 중인 파일들이 위치 </li>
<li>Staging Area : commit 할 준비가 된 파일들이 위치하는 영역. working directory에서 <code>git add</code> 명령어로 추가한 파일들이 모여있는 영역 </li>
<li>Git Directory(Repository) : 버전을 관리하는 파일들이 위치하는 영역. staging area에 있는 소스 코드에 <code>git commit</code> 명령을 실행하면 최종적으로 Git 저장소에 저장됨.</li>
</ul>
<h2 id="git의-3가지-상태">Git의 3가지 상태</h2>
<ul>
<li><code>Committed</code>란 데이터가 로컬 <strong>데이터베이스에 안전하게 저장됐다는 것</strong>을 의미한다.</li>
<li><code>Modified</code>는 수정한 파일을 아직 <strong>로컬 데이터베이스에 커밋하지 않은 것</strong>을 말한다.</li>
<li><code>Staged</code>란 현재 <strong>수정한 파일을 곧 커밋할 것</strong>이라고 표시한 상태를 의미한다.</li>
</ul>
<h2 id="파일-관점에서-git의-4가지-상태">파일 관점에서 Git의 4가지 상태</h2>
<p><img src="https://velog.velcdn.com/images/seul_/post/97ed9fb6-4b84-43bd-a0a1-978841c39cfe/image.png" alt=""></p>
<ul>
<li>Untracked : Working Directory에 있는 파일이지만 Git으로 버전관리를 하지 않는 상태</li>
<li>Unmodified : 신규로 파일이 추가되었을 때, new file 상태와 같음</li>
<li>Modified : 파일이 추가된 이후 해당 파일이 수정되었을 때의 상태</li>
<li>Staged : Staging Area에 반영된 상태</li>
</ul>
<h2 id="git-기본-용어-정리">Git 기본 용어 정리</h2>
<ul>
<li>repository: 저장소. Git으로 버전 관리하는 디렉토리를 의미 </li>
<li>local repository: 로컬 저장소. 작업자의 개발환경(pc)에 설정된 Git 저장소</li>
<li>remote repository: 원격 저장소. GitHub 등 외부 서버에 설정된 Git 저장소</li>
<li>commit: 특정 상태를 기록한 버전.</li>
<li>branch: 또 다른 작업 공간. 브랜치를 이용하면 코드를 여러개로 복사한 후, 이전의 코드와는 독립적으로 개발할 수 있다. </li>
<li>merge: 특정 브랜치에서 작업한 내용을 또 다른 브랜치에 적용하는 것을 의미</li>
<li>push: 로컬 저장소의 수정사항을 원격 저장소에 업로는 하는 것 </li>
<li>pull: 원격 저장소에서 로컬 저장소로 다운로드 하는 것 </li>
</ul>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://git-scm.com/book/ko/v2/%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-Git-%EA%B8%B0%EC%B4%88">https://git-scm.com/book/ko/v2/%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-Git-%EA%B8%B0%EC%B4%88</a></li>
<li><a href="https://subicura.com/git/guide/#git%E1%84%8B%E1%85%B4-%E1%84%90%E1%85%B3%E1%86%A8%E1%84%8C%E1%85%B5%E1%86%BC">https://subicura.com/git/guide/#git%E1%84%8B%E1%85%B4-%E1%84%90%E1%85%B3%E1%86%A8%E1%84%8C%E1%85%B5%E1%86%BC</a></li>
<li><a href="https://cornswrold.tistory.com/71#google_vignette">https://cornswrold.tistory.com/71#google_vignette</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[환경변수 설정하기(create-react-app)]]></title>
            <link>https://velog.io/@seul_/%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0create-react-app</link>
            <guid>https://velog.io/@seul_/%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0create-react-app</guid>
            <pubDate>Tue, 02 Aug 2022 13:32:50 GMT</pubDate>
            <description><![CDATA[<h2 id="node_env">NODE_ENV</h2>
<p>create-react-app은 실행 명령어에 따라 NODE_ENV라는 환경변수를 기본으로 제공한다.
개발, 테스트, 배포 환경별로 다른 값을 적용할때 유용하다. </p>
<ul>
<li>npm start =&gt; development</li>
<li>npm test =&gt; test</li>
<li>npm run build =&gt; production</li>
</ul>
<pre><code class="language-js">console.log(`NODE_ENV = ${process.env.NODE_ENV}`); </code></pre>
<p>개발환경이라면 (npm start) 위 코드의 출력 결과는 <code>NODE_ENV = development</code></p>
<h2 id="환경변수-설정-방법">환경변수 설정 방법</h2>
<ul>
<li>프로젝트 최상위 디렉토리에 <code>.env</code> 파일을 생성한다. </li>
<li>변수명은 <code>REACT_APP_</code> 키워드로 시작해야 한다.</li>
<li>쌍따옴표, 작은따옴표 등으로 감싸면 안된다. 띄어쓰기도 ❌</li>
<li><code>proce.env.[변수명]</code> 으로 접근해서 환경변수를 사용할 수 있다.</li>
<li>.gitignore 파일에서 .env를 추가한다.</li>
</ul>
<h2 id="env-파일-종류-및-우선순위">.env 파일 종류 및 우선순위</h2>
<p><code>.env</code>: 기본
<code>.env.local</code>: test 환경을 제외한 모든 환경에서 로드됨 
<code>.env.development</code>: 개발 환경 (npm start)
<code>.env.test</code>: 테스트 환경 (npm test)
<code>.env.production</code>: 배포 환경 (npm run build)</p>
<p>우선순위 (오른쪽으로 갈수록 우선순위가 낮은 것)
<code>npm start</code>: .env.development.local, .env.local, .env.development, .env
<code>npm run build</code>: .env.production.local, .env.local, .env.production, .env
<code>npm test</code>: .env.test.local, .env.test, .env (note .env.local is missing)</p>
<h2 id="🔗-참고">🔗 참고</h2>
<ul>
<li><a href="https://create-react-app.dev/docs/adding-custom-environment-variables/">https://create-react-app.dev/docs/adding-custom-environment-variables/</a></li>
<li><a href="https://gaemi606.tistory.com/entry/React-%ED%99%98%EA%B2%BD-%EB%B3%80%EC%88%98-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0-create-react-app">https://gaemi606.tistory.com/entry/React-%ED%99%98%EA%B2%BD-%EB%B3%80%EC%88%98-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0-create-react-app</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[REST API란? ]]></title>
            <link>https://velog.io/@seul_/REST-API%EB%9E%80</link>
            <guid>https://velog.io/@seul_/REST-API%EB%9E%80</guid>
            <pubDate>Thu, 28 Jul 2022 07:30:59 GMT</pubDate>
            <description><![CDATA[<h1 id="rest-api"><strong>REST API</strong></h1>
<p>REST는 Representational State Transfer라는 용어의 약자로, 2000년도에 로이 필딩 (Roy Fielding)의 박사학위 논문에서 <strong>웹의 장점을 최대한 활용할 수 있는 아키텍처</strong>로써 처음 소개되었다. 이러한 REST는 다음과 같은 구성으로 이루어져있다.  </p>
<ul>
<li><strong>자원(RESOURCE)</strong> - URI</li>
<li><strong>행위(Verb)</strong> - HTTP METHOD<strong>(GET, POST, PUT, DELETE 등)</strong></li>
<li><strong>표현(Representations)</strong></li>
</ul>
<p>REST는 웹에서 사용되는 자원들에 대한 정의, 자원에 접근 방식에 대한 제약 조건들의 집합이며, 이 제약 조건들을 지키며 설계한 API를 <strong>REST API</strong>라고 할 수 있다. </p>
<blockquote>
<p>💡 REST API란 <strong>웹에서 사용되는 데이터나 리소스를 HTTP URI로 표현하고, HTTP 프로토콜을 통해 요청과 응답을 정의하는 방식</strong>을 말한다.</p>
</blockquote>
<h1 id="rest-성숙도-모델">REST 성숙도 모델</h1>
<p>레너드 리처드슨(Leonard Richardson)은 REST방법론을 보다 실용적으로 적용하기 위해서 REST API를 잘 적용하기 위한 4단계 모델을 만들었다. </p>
<p>로이 필딩은 이 모델의 모든 단계를 충족해야 REST API라고 부를 수 있다고 했지만, 2단계까지만 적용하더라도 좋은 API 디자인이라고 볼 수 있고, 이런 경우를 HTTP API라고 부른다. </p>
<h3 id="rest-성숙도-모델---0단계">REST 성숙도 모델 - 0단계</h3>
<ul>
<li><strong>HTTP 프로토콜</strong>을 사용</li>
<li>이것만으로 REST API라고 하지 않는다, REST API를 작성하기 위한 기본 단계이다.</li>
</ul>
<h3 id="rest-성숙도-모델---1단계">REST 성숙도 모델 - 1단계</h3>
<ul>
<li>개별 리소스(Resource)와의 통신을 준수해야 한다.</li>
<li><strong>모든 자원은 개별 리소스에 맞는 엔드포인트(Endpoint)를 사용해야한다</strong>. 요청하고 받는 자원에 대한 정보를 응답으로 전달해야 한다.</li>
<li>엔드포인트 작성 시에는 동사, HTTP 메서드, 혹은 어떤 행위에 대한 단어 사용은 지양하고, <strong>리소스</strong>
에 집중해 <strong>명사 형태의 단어로 작성</strong>하는 것이 바람직한 방법</li>
</ul>
<h3 id="rest-성숙도-모델---2단계"><strong>REST 성숙도 모델 - 2단계</strong></h3>
<ul>
<li>CRUD에 맞게 적절한 HTTP 메서드를 사용한다.</li>
</ul>
<p><strong>HTTP Method</strong> </p>
<ul>
<li>GET                        조회(Read)</li>
<li>POST                     추가(Create)</li>
<li>PUT(또는 PATCH) 갱신 (Update)</li>
<li>DELETE                 삭제(Delete)</li>
</ul>
<blockquote>
</blockquote>
<p>💡 <strong>HTTP Method 사용 규칙</strong></p>
<ul>
<li>GET: 서버의 데이터를 변화시키지 않는 요청에 사용</li>
<li>POST: 요청마다 새로운 리소스를 생성</li>
<li>PUT: 요청마다 같은 리소스를 반환, 교체의 역할</li>
<li>PATCH: 수정의 용도로 사용</li>
</ul>
<h3 id="rest-성숙도-모델---3단계">REST 성숙도 모델 - 3단계</h3>
<ul>
<li>HATEOAS(Hypertext As The Engine Of Application State)라는 약어로 표현되는 하이퍼미디어 컨트롤을 적용한다.</li>
<li>2단계와 동일하지만, 응답에는 리소소의 URI를 포함한 링크요소를 삽입하여 작성해야 한다.</li>
</ul>
<hr>
<h1 id="open-api">OPEN API</h1>
<h3 id="api란">API란?</h3>
<p>Application Programming Interface의 약자로, 응용 프로그램을 만드는데 필요한 연결장치, 매개체라고 이해할 수 있다. 실제 개발할때, 혼자 모든 것을 개발할 수 없기 때문에 이러한 매개체가 필요하다.  접근 권한을 부여받아서 사용할 수 있으며 프로그램 내부가 어떻게 동작하고 있는지는 알 수 없고, API 활용 규약에 따라서 정보를 요청하고 받을 수 있다.  </p>
<h3 id="open-api-1">OPEN API</h3>
<blockquote>
<p>💡 오픈 <strong>API</strong>(Open Application Programming Interface, Open API, 공개 API)는 누구나 사용할 수 있도록 공개된 API를 말한다</p>
</blockquote>
<ul>
<li>지도, SNS, 음악, 날씨, 공공데이터 등 다양한 분야에서 사용 가능하며 구글, 다음 카카오, 네이버뿐만 아니라 정부에서도 그동안 수집한 공공데이터들을 OPEN API 형태로 제공하고 있다.</li>
<li>사용하는 입장에서는 개발할 때 들어가닌 시간과 비용을 줄일 수 있으며, 더욱 양질의 개발이 가능하다.</li>
<li>정보를 제공하는 입장에서는 API를 통해 정보 및 기능을 제공하면서 홍보효과, 회원 이탈율을 줄이는 등 더 많은 이익을 얻을 수 있다.</li>
<li>API마다 정해진 이용 수칙이 있고, 그 이용 수칙에 따라 제한사항(가격, 정보의 제한 등)이 있을 수 있다.</li>
</ul>
<h3 id="api-key">API Key</h3>
<ul>
<li>API를 이용하기 위해서 API Key가 필요하다.</li>
<li>정보를 제공하는 입장에서 누가 얼마만큼 사용하고 있는지 추적할 수 있다.</li>
<li>API Key가 필요한 경우에는 로그인한 이용자에게 자원에 접근할 수 있는 권한을 API Key의 형태로 제공하고, 데이터를 요청할 때 API key를 같이 전달해야 원하는 응답을 받을 수 있다.</li>
</ul>
<hr>
<h2 id="🔗-참고">🔗 참고</h2>
<p>코드스테이츠 학습 자료 </p>
<p>REST - <a href="https://meetup.toast.com/posts/92">https://meetup.toast.com/posts/92</a></p>
<p>API</p>
<ul>
<li><a href="https://rlakuku-program.tistory.com/19">https://rlakuku-program.tistory.com/19</a></li>
<li><a href="https://whwl.tistory.com/87">https://whwl.tistory.com/87</a></li>
<li><a href="https://helloworld-88.tistory.com/21">https://helloworld-88.tistory.com/21</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 웹앱 번들링 후 배포(웹팩으로 CRA없이 리액트 개발환경 구축?!) ]]></title>
            <link>https://velog.io/@seul_/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%9B%B9%EC%95%B1-%EB%B2%88%EB%93%A4%EB%A7%81-%ED%9B%84-%EB%B0%B0%ED%8F%AC%EC%9B%B9%ED%8C%A9%EC%9C%BC%EB%A1%9C-CRA%EC%97%86%EC%9D%B4-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95</link>
            <guid>https://velog.io/@seul_/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%9B%B9%EC%95%B1-%EB%B2%88%EB%93%A4%EB%A7%81-%ED%9B%84-%EB%B0%B0%ED%8F%AC%EC%9B%B9%ED%8C%A9%EC%9C%BC%EB%A1%9C-CRA%EC%97%86%EC%9D%B4-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95</guid>
            <pubDate>Tue, 26 Jul 2022 16:07:07 GMT</pubDate>
            <description><![CDATA[<h2 id="과제-요약">과제 요약</h2>
<p>리액트 웹 애플리케이션을 웹팩으로 번들링하는 과제를 진행하는 과정을 기록으로 남겨보려고 한다. 저번 세션에서 진행한 레퍼런스 코드를 복사해와서 진행했다. (처음에는 clone해와서 시작했는데, 페이지를 배포를 진행하기 위해서 fork해온 리포지토리에서 빌드를 완성하고 push하다가 오류가 생겨서 처음부터 다시 시작했다는 슬픈 사연.. 🥲)</p>
<p><img src="https://velog.velcdn.com/images/seul_/post/9a409ef8-1611-4fd5-8fe9-a2e22b304a5d/image.png" alt=""></p>
<h3 id="1개발환경-초기화">1.개발환경 초기화</h3>
<blockquote>
<p>npm i -y</p>
</blockquote>
<h3 id="2-리액트-라이브러리-설치">2. 리액트 라이브러리 설치</h3>
<blockquote>
<p>npm i react react-dom</p>
</blockquote>
<ul>
<li>react : 리액트 컴포넌트와 Hooks, 라이프 사이클에 대한 정보가 들어있는 코어 라이브러리</li>
<li>react-dom : 리액트와 DOM 연결</li>
</ul>
<h3 id="3-웹팩-설치">3. 웹팩 설치</h3>
<blockquote>
<p>npm i -D webpack webpack-cli</p>
</blockquote>
<ul>
<li>webpack: 웹팩 코어</li>
<li>webpack-cli: 터미널에서 웹팩 사용</li>
</ul>
<h3 id="4-바벨-설치">4. 바벨 설치</h3>
<p>바닐라 js로 작성된 프로젝트는 번들링할때 이 단계에서 웹팩 config파일에서 entry와 output만 설정하고 번들링할 수 있었지만, JSX문법으로 작성된 리액트 코드는 바벨을 통해서 브라우저가 이해할 수 있는 JavaScript로 변환해서 번들링해줘야 한다. 이걸 처리해주는 것이 바벨 로더! </p>
<blockquote>
<p>npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react</p>
</blockquote>
<ul>
<li>babel/core : 바벨 코어 </li>
<li>babel/preset-env : ES6+ 코드를 ES5 코드로 변환시켜줌 </li>
<li>babel/preset-react : jsx지원</li>
<li>babel-loader : 바벨과 웹팩을 연결</li>
</ul>
<pre><code class="language-js">//webpack.config.js
const path = require(&#39;path&#39;);

module.exports = {
  entry: &#39;./src/index.js&#39;, // 빌드 시작 위치
  output: {
    // 빌드결과 저장 위치
    filename: &#39;app.bundle.js&#39;,
    path: path.resolve(__dirname, &#39;docs&#39;),
    clean: true,
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: &#39;babel-loader&#39;,
          options: {
            presets: [
              [&#39;@babel/preset-env&#39;],
              [&#39;@babel/preset-react&#39;, { runtime: &#39;automatic&#39; }],
            ],
          },
        },
      },
    ],
  },
};</code></pre>
<ul>
<li>loader이기 때문에 module.rules 하단에 작성한다. </li>
</ul>
<h3 id="css-번들링">CSS 번들링</h3>
<p><code>styl-loader</code>와 <code>css-loader</code>로 번들링하면 header태그 안에서 internal 방식으로 번들링 되는 것과 달리 external 방식으로 CSS 번들링하는 플러그인인 <code>css-minimizer-webpack-plugin</code> 를 설치했다. </p>
<blockquote>
<p>npm i -D css-minimizer-webpack-plugin</p>
</blockquote>
<blockquote>
<p>npm i -D mini-css-extract-plugin</p>
</blockquote>
<ul>
<li><code>mini-css-extract-plugin</code>은 css 파일을 한줄로 압축한다.</li>
</ul>
<pre><code class="language-js">//webpack.config.js
const path = require(&#39;path&#39;);

const MiniCssExtractPlugin = require(&#39;mini-css-extract-plugin&#39;);
const CssMinimizerPlugin = require(&#39;css-minimizer-webpack-plugin&#39;);

module.exports = {
  entry: &#39;./src/index.js&#39;, // 빌드 시작 위치
  output: {
    // 빌드결과 저장 위치
    filename: &#39;app.bundle.js&#39;,
    path: path.resolve(__dirname, &#39;docs&#39;),
    clean: true,
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: &#39;babel-loader&#39;,
          options: {
            presets: [
              [&#39;@babel/preset-env&#39;],
              [&#39;@babel/preset-react&#39;, { runtime: &#39;automatic&#39; }],
            ],
          },
        },
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, &#39;css-loader&#39;],
        exclude: /node_modules/,
      },
    ],
  },
  optimization: {
    minimizer: [
      new CssMinimizerPlugin(), // css를 줄여주는 플러그인
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: &#39;style.css&#39;,
    }),
  ],
};</code></pre>
<pre><code class="language-js">// index.js 

import style from &#39;./index.css&#39;</code></pre>
<p>index.js 파일에 css파일이 import 된 것을 확인하고 빌드한다. </p>
<h3 id="html-번들링">HTML 번들링</h3>
<blockquote>
<p>npm install -D html-webpack-plugin</p>
</blockquote>
<pre><code class="language-js">const path = require(&#39;path&#39;);
const HtmlWebpackPlugin = require(&#39;html-webpack-plugin&#39;);
const MiniCssExtractPlugin = require(&#39;mini-css-extract-plugin&#39;);
const CssMinimizerPlugin = require(&#39;css-minimizer-webpack-plugin&#39;);

module.exports = {
  entry: &#39;./src/index.js&#39;, // 빌드 시작 위치
  output: {
    // 빌드결과 저장 위치
    filename: &#39;app.bundle.js&#39;,
    path: path.resolve(__dirname, &#39;docs&#39;),
    clean: true,
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: &#39;babel-loader&#39;,
          options: {
            presets: [
              [&#39;@babel/preset-env&#39;],
              [&#39;@babel/preset-react&#39;, { runtime: &#39;automatic&#39; }],
            ],
          },
        },
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, &#39;css-loader&#39;],
        exclude: /node_modules/,
      },
    ],
  },
  optimization: {
    minimizer: [
      new CssMinimizerPlugin(), // css를 줄여주는 플러그인
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: &#39;./public/index.html&#39;,
    }),
    new MiniCssExtractPlugin({
      filename: &#39;style.css&#39;,
    }),
  ],
};</code></pre>
<p>여기까지하면 docs 폴더에 번들 결과 파일이 잘 생성된다. (최소한의 웹팩 설정!) </p>
<h2 id="advanced-challenge">Advanced Challenge</h2>
<p>레퍼런스 코드를 보면서 추가적으로 유용한 설정을 추가했다. </p>
<h3 id="개발환경과-배포환경-나누기">개발환경과 배포환경 나누기</h3>
<p><a href="https://yamoo9.gitbook.io/webpack/webpack/config-webpack-dev-environment/webpack-mode">webpack 러닝 가이드 - Mode</a></p>
<p>웹팩 설정 파일에 mode 속성을 추가해서 사용할 수 있다. 기본값은 production(배포 모드)이고, 웹팩 모듈 번들링 과정에서 코드를 최적화해서 용량을 줄인다. 반면에 development(개발 모드)는 안전한 로컬 환경에서 개발한다고 가정하고, 편리한 개발을 위해서 어느 파일에서 에러가 발생했는지도 알려주는 최적화되지 않은 번들 파일을 제공한다. </p>
<p>레퍼런스 코드에서는 웹팩 설정파일을 두가지 모드로 설정하기 위해서 webpack-merge를 설치한다. </p>
<blockquote>
<p> npm install -D webpack-merge</p>
</blockquote>
<p>기본 설정 파일을 머지한 각각의 개발/배포 환경에 필요한 설정파일을 따로 만들어준다. 
<code>webpack-dev-server</code>도 같이 설치해서 변경된 코드를 개발 서버에 반영해서 보여줄 수 있도록 설정을 추가했다. </p>
<blockquote>
<p>npm i -D webpack-dev-server</p>
</blockquote>
<pre><code class="language-js">// webpack.config.dev.js
const { merge } = require(&#39;webpack-merge&#39;);
const baseConfig = require(&#39;./webpack.config.base&#39;);
const path = require(&#39;path&#39;);

module.exports = merge(baseConfig, {
  mode: &#39;development&#39;,
  devServer: {
    static: {
      directory: path.resolve(__dirname, &#39;docs&#39;),
    },
    port: 3001,
    hot: true,
  },
});</code></pre>
<pre><code class="language-js">// webpack.config.prod.js
const { merge } = require(&#39;webpack-merge&#39;);
const baseConfig = require(&#39;./webpack.config.base&#39;);

module.exports = merge(baseConfig, {
  mode: &#39;production&#39;,
});</code></pre>
<p>package.json scripts에 추가</p>
<pre><code class="language-js"> &quot;scripts&quot;: {
    &quot;build&quot;: &quot;webpack --config webpack.config.prod.js&quot;,
    &quot;dev&quot;: &quot;webpack server --open --config webpack.config.dev.js&quot;,
  },</code></pre>
<h3 id="react-refresh-webpack-plugin-설치">react-refresh-webpack-plugin 설치</h3>
<p>webpack-dev-server처럼 저장할때마다 변경사항을 적용시며주고, 리액트의 상태를 유지해주는 플러그인 설치한다. 
<a href="https://github.com/pmmmwh/react-refresh-webpack-plugin">react-refresh-webpack-plugin</a></p>
<blockquote>
<p>npm i -D @pmmmwh/react-refresh-webpack-plugin react-refresh</p>
</blockquote>
<pre><code class="language-js">const { merge } = require(&#39;webpack-merge&#39;);
const baseConfig = require(&#39;./webpack.config.base&#39;);
const path = require(&#39;path&#39;);
const ReactRefreshPlugin = require(&#39;@pmmmwh/react-refresh-webpack-plugin&#39;);

module.exports = merge(baseConfig, {
  mode: &#39;development&#39;,
  devServer: {
    static: {
      directory: path.resolve(__dirname, &#39;docs&#39;),
    },
    port: 3001,
    hot: true,
  },
  plugins: [new ReactRefreshPlugin()],
});</code></pre>
<h3 id="eslint-설치">eslint 설치</h3>
<p>코드에 문제가 없는지 검사하기 위해서 설치 </p>
<blockquote>
<p>npm install -D eslint eslint-plugin-react @babel/eslint-parser</p>
</blockquote>
<pre><code class="language-js">//pacakge.json scripts에 추가 

 &quot;lint&quot;: &quot;eslink ./src&quot;,</code></pre>
<p>웹 접근성에 대해서 지켜야하는 부분을 알려주는 eslint rule 추가 설치 </p>
<blockquote>
<p>npm install -D eslint-plugin-jsx-a11y</p>
</blockquote>
<p>따로 설정파일을 작성해야 한다. (레퍼런스 코드라서 옵션에 대해서는 더 공부해봐야 함.. ) </p>
<pre><code class="language-js">// .eslintrc.js
module.exports = {
  parser: &quot;@babel/eslint-parser&quot;,
  env: {
    browser: true,
    commonjs: true,
    es6: true,
    node: true,
  },
  extends: [
    &quot;eslint:recommended&quot;,
    &quot;plugin:react/recommended&quot;,
    &quot;plugin:jsx-a11y/recommended&quot;,
  ],
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 2018,
    sourceType: &quot;module&quot;,
  },
  settings: {
    react: {
      version: &quot;18.2.0&quot;,
    },
  },
  plugins: [&quot;react&quot;],
  rules: {
    &quot;react/react-in-jsx-scope&quot;: 0,
    &quot;react/jsx-uses-react&quot;: 0,
    &quot;react/prop-types&quot;: 0,
  },
};</code></pre>
<h3 id="prettier-설치">prettier 설치</h3>
<p>코드 형식 통일(vs extension으로만 알았는데 이렇게 개발환경에 추가할 수 있다!!) </p>
<blockquote>
<p>npm install -D prettier</p>
</blockquote>
<pre><code class="language-js">//pacakge.json scripts에 추가 

&quot;pretty&quot;: &quot;prettier --write ./&quot;,</code></pre>
<p>따로 설정파일을 만들어줘야 한다. </p>
<pre><code class="language-js">// .prettierrc.js
module.exports = {
  singleQuote: true,
  jsxSingleQuote: true,
};</code></pre>
<h2 id="배포-결과와-코드">배포 결과와 코드</h2>
<p><a href="https://seul-dev.github.io/react-webpack-build-prac/">https://seul-dev.github.io/react-webpack-build-prac/</a></p>
<h4 id="packagejson">package.json</h4>
<pre><code class="language-js">{
  &quot;dependencies&quot;: {
    &quot;react&quot;: &quot;^18.2.0&quot;,
    &quot;react-dom&quot;: &quot;^18.2.0&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@babel/core&quot;: &quot;^7.18.9&quot;,
    &quot;@babel/eslint-parser&quot;: &quot;^7.18.9&quot;,
    &quot;@babel/preset-env&quot;: &quot;^7.18.9&quot;,
    &quot;@babel/preset-react&quot;: &quot;^7.18.6&quot;,
    &quot;@pmmmwh/react-refresh-webpack-plugin&quot;: &quot;^0.5.7&quot;,
    &quot;babel-loader&quot;: &quot;^8.2.5&quot;,
    &quot;css-loader&quot;: &quot;^6.7.1&quot;,
    &quot;css-minimizer-webpack-plugin&quot;: &quot;^4.0.0&quot;,
    &quot;eslint&quot;: &quot;^8.20.0&quot;,
    &quot;eslint-plugin-jsx-a11y&quot;: &quot;^6.6.1&quot;,
    &quot;eslint-plugin-react&quot;: &quot;^7.30.1&quot;,
    &quot;html-webpack-plugin&quot;: &quot;^5.5.0&quot;,
    &quot;mini-css-extract-plugin&quot;: &quot;^2.6.1&quot;,
    &quot;prettier&quot;: &quot;^2.7.1&quot;,
    &quot;react-refresh&quot;: &quot;^0.14.0&quot;,
    &quot;webpack&quot;: &quot;^5.74.0&quot;,
    &quot;webpack-cli&quot;: &quot;^4.10.0&quot;,
    &quot;webpack-dev-server&quot;: &quot;^4.9.3&quot;,
    &quot;webpack-merge&quot;: &quot;^5.8.0&quot;
  },
  &quot;name&quot;: &quot;react-webpack-build-prac&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;main&quot;: &quot;webpack.config.js&quot;,
  &quot;scripts&quot;: {
    &quot;build&quot;: &quot;webpack --config webpack.config.prod.js&quot;,
    &quot;dev&quot;: &quot;webpack server --open --config webpack.config.dev.js&quot;,
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;,
    &quot;lint&quot;: &quot;eslink ./src&quot;,
    &quot;pretty&quot;: &quot;prettier --write ./&quot;
  },
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;,
  &quot;description&quot;: &quot;&quot;
}</code></pre>
<h4 id="webpackconfigbasejs">webpack.config.base.js</h4>
<pre><code class="language-js">const path = require(&#39;path&#39;);
const HtmlWebpackPlugin = require(&#39;html-webpack-plugin&#39;);
const MiniCssExtractPlugin = require(&#39;mini-css-extract-plugin&#39;);
const CssMinimizerPlugin = require(&#39;css-minimizer-webpack-plugin&#39;);

module.exports = {
  entry: &#39;./src/index.js&#39;, // 빌드 시작 위치
  output: {
    // 빌드결과 저장 위치
    filename: &#39;app.bundle.js&#39;,
    path: path.resolve(__dirname, &#39;docs&#39;),
    clean: true,
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: &#39;babel-loader&#39;,
          options: {
            presets: [
              [&#39;@babel/preset-env&#39;],
              [&#39;@babel/preset-react&#39;, { runtime: &#39;automatic&#39; }],
            ],
          },
        },
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, &#39;css-loader&#39;],
        exclude: /node_modules/,
      },
    ],
  },
  optimization: {
    minimizer: [
      new CssMinimizerPlugin(), // css를 줄여주는 플러그인
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: &#39;./public/index.html&#39;,
    }),
    new MiniCssExtractPlugin({
      filename: &#39;style.css&#39;,
    }),
  ],
};</code></pre>
<h4 id="webpackconfigdevjs">webpack.config.dev.js</h4>
<pre><code class="language-js">const { merge } = require(&#39;webpack-merge&#39;);
const baseConfig = require(&#39;./webpack.config.base&#39;);
const path = require(&#39;path&#39;);
const ReactRefreshPlugin = require(&#39;@pmmmwh/react-refresh-webpack-plugin&#39;);

module.exports = merge(baseConfig, {
  mode: &#39;development&#39;,
  devServer: {
    static: {
      directory: path.resolve(__dirname, &#39;docs&#39;),
    },
    port: 3001,
    hot: true,
  },
  plugins: [new ReactRefreshPlugin()],
});</code></pre>
<h4 id="webpackconfigprodjs">webpack.config.prod.js</h4>
<pre><code class="language-js">const { merge } = require(&#39;webpack-merge&#39;);
const baseConfig = require(&#39;./webpack.config.base&#39;);

module.exports = merge(baseConfig, {
  mode: &#39;production&#39;,
});</code></pre>
<h3 id="느낀점과-아쉬운-점">느낀점과 아쉬운 점</h3>
<p>CRA가 얼마나 편리한 것인지 느꼈다.🥲 손쉽게 사용가능하던 리액트 프로젝트 개발환경을 처음부터 하나하나 설정해나가보니 create-react-app 한 줄의 강력함을 느꼈다. 그래도 내가 필요한 설정들을 계속 추가하는 느낌으로 설치+ 설정 저장을 반복하다보니 웹팩의 큰 개념에는 제법 익숙해질 수 있었던 시간이었다. 커스텀 개발환경을 구축해볼 수 있었다.</p>
<p>이번 과제에서 favicon, manifest가 번들링되지 않았는데 이 부분은 아래 블로그를 참고해서 다음에 다시 해결해보는걸로!!  
<a href="https://yujo11.github.io/webpack/webpack%20favicon,%20manifest.json%20%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0/">블로그)webpack plugin을 이용한 favicon, manifest 추가 방법</a></p>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://juni-official.tistory.com/158">https://juni-official.tistory.com/158</a></li>
<li><a href="https://365kim.tistory.com/147">https://365kim.tistory.com/147</a></li>
<li><a href="https://velog.io/@hih0327/Webpack-%EA%B8%B0%EC%B4%88#webpack%EC%9D%B4%EB%9E%80">https://velog.io/@hih0327/Webpack-기초#webpack이란</a></li>
<li><a href="https://phrygia.github.io/react/2022-02-18-react/">https://phrygia.github.io/react/2022-02-18-react/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[간단한 웹앱 번들링 후 배포(웹팩 기초 튜토리얼)]]></title>
            <link>https://velog.io/@seul_/%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%9B%B9%EC%95%B1-%EB%B2%88%EB%93%A4%EB%A7%81-%ED%9B%84-%EB%B0%B0%ED%8F%AC%EC%9B%B9%ED%8C%A9-%EA%B8%B0%EC%B4%88-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC</link>
            <guid>https://velog.io/@seul_/%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%9B%B9%EC%95%B1-%EB%B2%88%EB%93%A4%EB%A7%81-%ED%9B%84-%EB%B0%B0%ED%8F%AC%EC%9B%B9%ED%8C%A9-%EA%B8%B0%EC%B4%88-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC</guid>
            <pubDate>Tue, 26 Jul 2022 14:04:36 GMT</pubDate>
            <description><![CDATA[<h2 id="진행할-과제-간단-소개">진행할 과제 간단 소개</h2>
<p>이번 유닛에서 웹팩을 학습하면서 진행한 웹팩을 이용한 번들링 과제를 기록해보려고 한다. </p>
<p>바닐라 js로 작성한 프로젝트 하나를 src 디렉토리에 복사해와서 js → html → css 순서로 번들링 진행했다. (이렇게 작은 프로젝트에 번들링이 필요한 과정일지 모르겠으나, 웹팩에 친숙해지는데는 큰 도움이 되었다.) 
<img src="https://velog.velcdn.com/images/seul_/post/4652c77a-6b86-442b-94ed-b6d8a24cfc07/image.png" alt=""></p>
<h3 id="1-현재-디렉토리에-npm-설치">1. 현재 디렉토리에 npm 설치</h3>
<blockquote>
<p>npm init -y</p>
</blockquote>
<ul>
<li><p>package.json 파일 생성됨 </p>
<h3 id="2-webpack-설치">2. webpack 설치</h3>
<blockquote>
<p>npm install -D webpack webpack-cli</p>
</blockquote>
</li>
<li><p><code>-D</code> : <code>--save-dev</code> package.json 파일의 devDependencies 항목에 저장(개발 단계에서만 이용)</p>
</li>
</ul>
<h3 id="3-webpack-설정-파일-작성">3. Webpack 설정 파일 작성</h3>
<ul>
<li>웹팩은 우선 번들링하는 원하는 파일을 확인하고(entry), 여기에 import한 라이브러리나 코드가 있으면 해당 코드를 모드 인식해서 하나의 번들(output) 안으로 압축한다.</li>
<li><code>webpack.config.js</code> 파일에 entry와 output 정보를 작성한다. </li>
</ul>
<pre><code class="language-js">const path = require(&#39;path&#39;);

module.exports = {
  entry: &#39;./src/index.js&#39;,
  output: {
    filename: &#39;app.bundle.js&#39;,
    path: path.resolve(__dirname, &#39;dist&#39;),
  },
};</code></pre>
<h3 id="4--js-파일-번들링-하기">4.  js 파일 번들링 하기</h3>
<blockquote>
<p>npx webpack</p>
</blockquote>
<ul>
<li>src 폴더에 있는 js 파일을 번들링한다.</li>
<li>package.json 파일에 스크립트를 추가해서 번들링을 명령어를 사용할 수 있다.  이제 <code>npm run build</code> 로 번들링을 실행할 수 있다.</li>
</ul>
<pre><code class="language-json">&quot;scripts&quot;: {
    &quot;build&quot;: &quot;webpack&quot;,
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;
  },</code></pre>
<ul>
<li>번들링 결과 output으로 지정해준 dist 폴더가 생겼고, <code>dist/app.bundle.js</code> 파일에 웹팩이 uglify, minify를 통해 읽기 어려운 한 줄의 코드로 바꿔놓은 것을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/seul_/post/6630bd2e-b3b4-43aa-96cc-395d9e0d8c2b/image.png" alt="">
<img src="https://velog.velcdn.com/images/seul_/post/ce4791f8-53a6-40b5-aa81-842c95d7e06a/image.png" alt=""></li>
</ul>
<h3 id="5-html파일-번들링하기-plugin">5. HTML파일 번들링하기 (plugin)</h3>
<p>5-1. <code>html-webpack-plugin</code> 설치 </p>
<blockquote>
<p>npm i -D html-webpack-plugin</p>
</blockquote>
<p>5-2. <code>webpack.config.js</code> 파일에 해당 플러그인을 등록</p>
<pre><code class="language-js">const path = require(&#39;path&#39;);
const HtmlWebpackPlugin = require(&#39;html-webpack-plugin&#39;);

module.exports = {
  entry: &#39;./src/index.js&#39;,
  output: {
    filename: &#39;app.bundle.js&#39;,
    path: path.resolve(__dirname, &#39;dist&#39;),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, &#39;src&#39;, &#39;index.html&#39;),
    }),
  ],
};</code></pre>
<ul>
<li><code>template</code> 옵션을 추가해서 기존에 만들어둔 파일을 이용해서 html 번들링 파일을 생성한다.</li>
</ul>
<p>5-3. <code>npm run build</code> 로 번들링 한다. </p>
<p>5-4. dist 폴더에 <code>index.html</code> 파일이 생성된다. </p>
<pre><code class="language-html">&lt;script defer=&quot;defer&quot; src=&quot;app.bundle.js&quot;&gt;&lt;/script&gt;</code></pre>
<ul>
<li>그리고 파일을 열어보면 위 코드가 추가 되어 있는 것을 볼 수 있다.</li>
<li><code>index.html</code> 브라우저에서 실행시켜보면 스타일은 적용되지 않았지만 잘 동작하는 것을 확인할 수 있다. 
<img src="https://velog.velcdn.com/images/seul_/post/3a2b6374-72f2-47b1-9573-c78f0db4a900/image.png" alt="">
<img src="https://velog.velcdn.com/images/seul_/post/a43cc77e-3fe5-4a1f-8148-fd3fdb23029e/image.png" alt=""></li>
</ul>
<h3 id="6-css-번들링-loader">6. CSS 번들링 (loader)</h3>
<p>6-1. <code>style-loader</code> 와 <code>css-loader</code>  설치 </p>
<blockquote>
<p>npm i -D style-loader css-loader</p>
</blockquote>
<p>6-2. <code>index.js</code> 파일에서 CSS 파일 임포트 </p>
<pre><code class="language-js">import &#39;./style.css&quot;;</code></pre>
<ul>
<li>index.js파일을 기준으로 css파일을 함께 묶어주는 개념이기때문에 css파일과의 연결 관계도 js파일에 명시되어야 한다(는 것으로 이해했다..) </li>
</ul>
<p>6-3. <code>webpack.config.js</code> 웹팩 설정 로더 등록</p>
<pre><code class="language-js">const path = require(&#39;path&#39;);
const HtmlWebpackPlugin = require(&#39;html-webpack-plugin&#39;);

module.exports = {
  entry: &#39;./src/index.js&#39;,
  output: {
    filename: &#39;app.bundle.js&#39;,
    path: path.resolve(__dirname, &#39;dist&#39;),
  },
  module : {
    rules: {
        test: /\.css$/,
        use: [&quot;style-loader&quot;, &quot;css-loader&quot;],
        exclude: /node_modules/,
    }
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, &#39;src&#39;, &#39;index.html&#39;),
    }),
  ],
};</code></pre>
<ul>
<li>module.rules 속성에 등록한다.</li>
<li><code>test</code> 항목에 정의된 정규식에 매칭되는 파일(확장자가 .css인 파일)은 <code>use</code> 항목에 등록된 로더를 통해서 처리된다. </li>
<li>로더는 <code>css-loader</code>가 먼저 적용되고, <code>styled-loader</code>가 적용된다.(순서에 주의)</li>
<li>exclude 규칙은 필수는 아니다.</li>
</ul>
<p>6-4. <code>npm run build</code> 로 번들링 한다. 
6-5. <code>dist/index.html</code> 파일을 브라우저에서 열어보면 스타일이 적용된 것을 확인 할 수 있다. 
<img src="https://velog.velcdn.com/images/seul_/post/b3fadab2-8528-4b59-9b5e-d567432f4568/image.png" alt=""></p>
<ul>
<li><code>&lt;header&gt;</code>태그 내부에 있는 <code>style</code> 태그에 해당 css 스타일이 적용됨 (internal 방식)</li>
</ul>
<p>여기까지 진행하면 내 프로젝트 파일이 모두 dist 폴더 아래에 번들링된다. 아래에는 advanced 과제를 진행하면서 알게된 내용을 정리해보려고 한다. </p>
<h2 id="advanced-challenge">Advanced Challenge</h2>
<h3 id="github-page-배포">github page 배포</h3>
<ul>
<li><code>webpack.config.js</code> 에서 output을 <code>dist</code>에서 <code>docs</code>로 바꿔준다. </li>
<li>이미 번들링된 dist파일이름을 docs로 바꿨다.(깃헙 페이지 배포가 목표라면 애초에 output을 docs로 설정하고 저장할 것! ) </li>
<li>빌드를 다 마치고 <code>git push</code></li>
<li>현재 작업 중인 깃헙 리포지토리의 setting- pages에서 source 옵션을 현재 브랜치, root를 /docs 폴더로 설정하고 저장한다. </li>
</ul>
<h3 id="webpack-dev-server">webpack-dev-server</h3>
<p>코드 변경시마다 빌드를 다시 하고 이 결과물을 브라우저에 띄워서 확인하는 과정을 편리하게 진행할 수 있다. 
실제 번들링된 결과물을 파일로 생성하는 것이 아니라, 메모리에 올려놓은채로 보여주기 때문에 빠른 속도로 변경된 코드를 개발 서버에 반영해서 보여줄 수 있다. 
(create-react-app으로 개발할때 <code>npm start</code>로 실행해놓으면 변경 내용을 저장하면 라이브 서버로 바로바로 결과를 확인해볼 수 있는 것처럼!) </p>
<blockquote>
<p>npm i -D webpack-dev-server </p>
</blockquote>
<p><code>webpack.config.js</code> 웹팩 설정</p>
<pre><code class="language-js">devServer: {
  static: {
    directory: path.resolve(__dirname, &#39;dist&#39;),
  },
    port: 3001, //포트 번호는 임의로 지정한다.
},</code></pre>
<p><code>package.json</code> scripts에 <code>webpack serve</code>를 추가</p>
<pre><code class="language-js">&quot;scripts&quot;: {
    &quot;build&quot;: &quot;webpack --mode=production&quot;,
    &quot;start&quot;: &quot;webpack serve --open --mode=development&quot;,
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;
},</code></pre>
<ul>
<li>개발 모드에서 사용하기 때문에 mode옵션을 위와 같이 작성</li>
</ul>
<p><code>npm start</code>로 실행해놓으면, 변경되는 코드를 바로바로 반영해서(빌드된 것처럼) 보여준다. </p>
<h3 id="clean-webpack-plugin">clean-webpack-plugin</h3>
<p>기존 빌드를 통해 생성되었지만 사용하지 않는 번들 파일을 지우고싶은 경우 사용하는 플러그인 </p>
<blockquote>
<p>npm i -D clean-webpack-plugin</p>
</blockquote>
<p><code>webpack.config.js</code> 웹팩 설정</p>
<pre><code class="language-js">const {CleanWebpackPlugin} = require(&quot;clean-webpack-plugin&quot;);

module.exports = {
  // 생략
  plugins: [new CleanWebpackPlugin()],
};</code></pre>
<h2 id="배포-페이지-및-전체-설정-코드">배포 페이지 및 전체 설정 코드</h2>
<blockquote>
<p><a href="https://seul-dev.github.io/bundling-webpack-prac/">https://seul-dev.github.io/bundling-webpack-prac/</a></p>
</blockquote>
<h4 id="packagejson">package.json</h4>
<pre><code class="language-js">{
  &quot;name&quot;: &quot;bundling-webpack-prac&quot;,
  &quot;homepage&quot;: &quot;https://seul-dev.github.io/bundling-webpack-prac&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;scripts&quot;: {
    &quot;build&quot;: &quot;webpack --mode=production&quot;,
    &quot;predeploy&quot;: &quot;npm run build&quot;,
    &quot;deploy&quot;: &quot;gh-pages -d dist&quot;,
    &quot;start&quot;: &quot;webpack serve --open --mode=development&quot;,
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;
  },
  &quot;keywords&quot;: [],
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;,
  &quot;devDependencies&quot;: {
    &quot;clean-webpack-plugin&quot;: &quot;^4.0.0&quot;,
    &quot;css-loader&quot;: &quot;^6.7.1&quot;,
    &quot;gh-pages&quot;: &quot;^4.0.0&quot;,
    &quot;html-webpack-plugin&quot;: &quot;^5.5.0&quot;,
    &quot;style-loader&quot;: &quot;^3.3.1&quot;,
    &quot;webpack&quot;: &quot;^5.74.0&quot;,
    &quot;webpack-cli&quot;: &quot;^4.10.0&quot;,
    &quot;webpack-dev-server&quot;: &quot;^4.9.3&quot;
  }
}</code></pre>
<h4 id="webpackconfigjs">webpack.config.js</h4>
<pre><code class="language-js">onst path = require(&#39;path&#39;);
const HtmlWebpackPlugin = require(&#39;html-webpack-plugin&#39;);
const { CleanWebpackPlugin } = require(&#39;clean-webpack-plugin&#39;);

module.exports = {
  entry: &#39;./src/index.js&#39;,
  output: {
    filename: &#39;app.bundle.js&#39;,
    path: path.resolve(__dirname, &#39;docs&#39;),
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [&#39;style-loader&#39;, &#39;css-loader&#39;],
        exclude: /node_modules/,
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, &#39;src&#39;, &#39;index.html&#39;),
    }),
    new CleanWebpackPlugin(),
  ],
  devServer: {
    static: {
      directory: path.resolve(__dirname, &#39;docs&#39;),
    },
    port: 3001,
  },
};</code></pre>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://www.daleseo.com/webpack-config/">https://www.daleseo.com/webpack-config/</a></li>
<li><a href="https://youtu.be/zal9HVgrMaQ">https://youtu.be/zal9HVgrMaQ</a></li>
<li><a href="https://blog.naver.com/PostView.naver?blogId=thdwlsgus0&amp;logNo=222395735170&amp;parentCategoryNo=&amp;categoryNo=53&amp;viewDate=&amp;isShowPopularPosts=false&amp;from=postView">https://blog.naver.com/PostView.naver?blogId=thdwlsgus0&amp;logNo=222395735170&amp;parentCategoryNo=&amp;categoryNo=53&amp;viewDate=&amp;isShowPopularPosts=false&amp;from=postView</a></li>
<li><a href="https://webpack.kr/configuration/mode/">https://webpack.kr/configuration/mode/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹팩 기초  ]]></title>
            <link>https://velog.io/@seul_/%EB%B2%88%EB%93%A4%EB%A7%81%EA%B3%BC-%EC%9B%B9%ED%8C%A9</link>
            <guid>https://velog.io/@seul_/%EB%B2%88%EB%93%A4%EB%A7%81%EA%B3%BC-%EC%9B%B9%ED%8C%A9</guid>
            <pubDate>Mon, 25 Jul 2022 14:45:53 GMT</pubDate>
            <description><![CDATA[<h2 id="웹팩이란">웹팩이란?</h2>
<ul>
<li>현재 프론트엔드 애플리케이션 배포를 위해 가장 많이 사용하는 모듈 번들러(Module Bundler)이다. </li>
<li>모듈 번들러란 웹 애플리케이션을 구성하는 HTML, CSS, JavaScript, image 등의 자원을 전부 각각의 모듈로 보고 이를 조합해 하나의 묶음으로 번들링(빌드)하는 도구를 의미한다. </li>
</ul>
<h3 id="모듈이란">모듈이란?</h3>
<ul>
<li>모듈이란 프로그래밍 관점에서 특정 기능을 갖는 작은 코드 단위를 의미한다. </li>
<li>웹팩에서 지칭하는 모듈은 자바스크립트 모듈에만 국한된 것이 아닐, 웹 애플리케이션을 구성하는 모든 자원을 의미.</li>
</ul>
<h3 id="모듈-번들링이란">모듈 번들링이란?</h3>
<p><img src="https://velog.velcdn.com/images/seul_/post/a30b0c2e-4f76-4cd5-aa4a-aefef6354a78/image.png" alt="">
그림과 같이 웹 애플리케이션을 구성하는 몇십, 몇백개의 자원들을 하나의 파일로 병합 및 압축해주는 동작을 모듈 번들링이라고 한다. </p>
<h2 id="webpack의-필요성">Webpack의 필요성</h2>
<ul>
<li>웹 애플리케이션의 빠른 로딩 속도와 높은 성능을 위해 필요하다. <ul>
<li>웹 페이지를 구성하는 코드의 양이 많으면(무거우면) 웹 페이지의 로딩 속도와 성능이 저하된다.</li>
<li>일반적으로 하나의 웹사이트에 접근하는 순간부터 3초 이내에 페이지가 뜨지 않으면 이탈을 택하는 유저가 많다. </li>
<li>로딩 속도를 개선하기 위한 노력으로 브라우저에서 서버로 요청하는 파일의 숫자를 줄이는 것이 있다.</li>
<li>각 자원들을 일일이 서버에 요청할 필요없이 webpack을 통해서 같은 타입의 파일들은 묶어서 요청 및 응답을 받을 수 있어서 네트워크 코스트가 줄어든다.</li>
</ul>
</li>
<li>일부 브라우저에서 지원하지 않는 JavaScript ES6 문법 → ES5로 변환하는 babel-loader 사용할 수 있다.</li>
<li>개발자가 선택하는 최선의 개발 방식으로 개발할 수 있게 지원- 웹 개발 작업 자동화 도구</li>
<li>Webpack4 버전 이상부터는 Develoment, Production 두 가지의 모드를 지원<ul>
<li>Production 모드로 번들링을 진행할 경우, 코드 난독화, 압축, 최적화(Tree Shaking) 작업을 지원하기도 한다. 상용화 된 프로그램을 사용자가 느끼기에 더욱 쾌적한 환경 및 보안까지 신경쓰면서 노출시킬 수 있다.</li>
</ul>
</li>
</ul>
<h2 id="webpack의-핵심-컨셉-4가지">Webpack의 핵심 컨셉 4가지</h2>
<h3 id="1-entry">1. Entry</h3>
<ul>
<li>번들링을 원하는 파일 위치(다른 모듈을 사용하고 있는 최상위 자바스크립트 파일)</li>
<li>웹팩은 엔트리를 통해서 필요한 모듈을 로딩하고 하나의 파일을 묶는다. <h3 id="2--output">2.  Output</h3>
</li>
<li>번들되 결과물을 내보낼 위치</li>
</ul>
<h3 id="3-loader">3. Loader</h3>
<ul>
<li>JavaScript, JSON 파일이 아닌 다른 유형의 파일 처리</li>
<li>image, font, stylesheet 등의 파일을 웹팩이 이해할 수 있는 모듈로 변환시킨다.<ul>
<li><strong><code>test</code></strong>: 변환이 필요한 파일들을 식별하기 위한 속성(필수)</li>
<li><strong><code>use</code></strong>: 변환을 수행하는데 사용되는 로더를 가리키는 속성(필수)</li>
<li><strong><code>exclude</code></strong>: 바벨로 컴파일하지 않을 파일이나 폴더를 지정. (반대로 <code>include</code> 속성을 이용해 반드시 컴파일해야 할 파일이나 폴더 지정 가능)<h3 id="4-plugin">4. Plugin</h3>
</li>
</ul>
</li>
<li>로더로 설정하기 애매한 부분 등 광범위한 작업</li>
<li>번들된 결과물을 처리 </li>
<li>bundle optimization, asset management, injection of environment </li>
</ul>
<h3 id="참고">참고</h3>
<p><a href="https://joshua1988.github.io/webpack-guide/getting-started.html#%EC%9B%B9%ED%8C%A9-%EB%A7%9B%EB%B3%B4%EA%B8%B0-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC">https://joshua1988.github.io/webpack-guide/getting-started.html#%EC%9B%B9%ED%8C%A9-%EB%A7%9B%EB%B3%B4%EA%B8%B0-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 반응형 헤더 만들기(useState, React Router, styled components)]]></title>
            <link>https://velog.io/@seul_/React-%EB%B0%98%EC%9D%91%ED%98%95-%ED%97%A4%EB%8D%94-useState-router-styled-components</link>
            <guid>https://velog.io/@seul_/React-%EB%B0%98%EC%9D%91%ED%98%95-%ED%97%A4%EB%8D%94-useState-router-styled-components</guid>
            <pubDate>Fri, 22 Jul 2022 16:12:48 GMT</pubDate>
            <description><![CDATA[<h2 id="🎨-구현-목표">🎨 구현 목표</h2>
<ul>
<li>미디어쿼리<code>@media</code> 공부한 후 복습 </li>
<li>styled components에 익숙해지기 </li>
<li>React Router 라이브러리 사용해서 페이지 라우팅까지 구현해보기 </li>
</ul>
<h3 id="미디어-쿼리---화면이-줄어들면-토글-버튼-나타나게-하기">미디어 쿼리 - 화면이 줄어들면 토글 버튼 나타나게 하기</h3>
<p>styled components로 선언한 컴포넌트 안에 태블릿 가로 사이즈(768x)이하의 화면에서는 nav-menu들이 column방향으로 정렬되도록 하고, 숨겨놨던 토글 버튼이 나타나도록 했다.  </p>
<pre><code class="language-js">  const StyledHeader = styled.header`
    /* 생략 */
  .menuToggleBtn {
    display: none;
    /* 생략 */
  }

  @media screen and (max-width: 768px) {
    flex-direction: column;
    align-items: flex-start;
    .menuToggleBtn {
      display: block;
    }
  }
`;</code></pre>
<h3 id="토글-버튼에-이벤트-달기">토글 버튼에 이벤트 달기</h3>
<p>토글 버튼을 열고 닫고 하기 위해서 이 상태를 관리할 수 있는 <code>isToggleOpen</code> state를 만들어주고, false로 초기화한다. 이 state를 NavMenu 컴포넌트(ul 태그)에 props로 내려준다. 그 다음 토글 버튼에 click이벤트가 발생하면 호출할 함수를 전달하고, 여기서 <code>isToggleOpen</code> 상태를 변화시키는 함수를 호출한다. </p>
<pre><code class="language-js">const Header = () =&gt; {
  const [isToggleOpen, setIsToggleOpen] = useState(false);

  const handleToggleOpen = () =&gt; {
    setIsToggleOpen(!isToggleOpen);
  };
  return (
    &lt;&gt;
      &lt;StyledHeader&gt;
        &lt;div className=&quot;nav_logo&quot;&gt;
          &lt;Link to={&quot;/&quot;} className=&quot;nav-logo-link&quot;&gt;
            Logo
          &lt;/Link&gt;
        &lt;/div&gt;
        &lt;NavManu isToggleOpen={isToggleOpen}&gt;
           {/* 생략 */}
        &lt;/NavManu&gt;
        &lt;FaBars className=&quot;menuToggleBtn&quot; onClick={handleToggleOpen} /&gt;
      &lt;/StyledHeader&gt;
    &lt;/&gt;
  );
};

export default Header;</code></pre>
<h3 id="istoggleopen-state에-따라서-메뉴를-화면에-보여주기">isToggleOpen state에 따라서 메뉴를 화면에 보여주기</h3>
<p>NavMenu 컴포넌트가 props로 내려받은 isToggleOpen이 true일때는 메뉴를 display:block 으로, false일때는 display: none으로 적용한다. </p>
<pre><code class="language-js">const NavManu = styled.ul`
 /*생략 */
  @media screen and (max-width: 768px) {
    display: ${(props) =&gt; (props.isToggleOpen ? &quot;block&quot; : &quot;none&quot;)};
    flex-direction: column;
    align-items: center;
    width: 100%;
  }
`;</code></pre>
<h3 id="페이지-라우팅">페이지 라우팅</h3>
<p>헤더(+네비게이션바)만 간단하게 만들어보려고 했는데 Router로 페이지 이동까지 넣으면 더 좋을 것 같아서 페이지를 여러개 만들었다. 
<img src="https://velog.velcdn.com/images/seul_/post/7f76df89-ea40-4029-ac70-78a4589633f5/image.png" alt="">
라우터 컴포넌트들을 import 해주고, App.js에 page들도 모두 import 해준다. 라우터 역할을 하는 <code>BrowserRouter</code>로 전체 div를 감싸주고 <code>Route</code> 컴포넌트로 경로와 element를 연결해준다. </p>
<blockquote>
<ol>
<li><code>BrowserRouter</code> router(라우터 역할)</li>
<li><code>Routes</code> <code>Route</code> route mathers(경로 매칭 역할)  </li>
<li><code>Link</code> route changers(경로 변경 역할)</li>
</ol>
</blockquote>
<pre><code class="language-js">import { BrowserRouter, Routes, Route } from &quot;react-router-dom&quot;;
import &quot;./styles.css&quot;;
import Header from &quot;./components/Header&quot;;
import Home from &quot;./pages/Home&quot;;
import About from &quot;./pages/About&quot;;
import Projects from &quot;./pages/Projects&quot;;
import Til from &quot;./pages/Til&quot;;
import Diary from &quot;./pages/Diary&quot;;

export default function App() {
  return (
    &lt;BrowserRouter&gt;
      &lt;div className=&quot;App&quot;&gt;
        &lt;Header /&gt;
        &lt;Routes&gt;
          &lt;Route path=&quot;/&quot; element={&lt;Home /&gt;} /&gt;
          &lt;Route path=&quot;/about&quot; element={&lt;About /&gt;} /&gt;
          &lt;Route path=&quot;/projects&quot; element={&lt;Projects /&gt;} /&gt;
          &lt;Route path=&quot;/Til&quot; element={&lt;Til /&gt;} /&gt;
          &lt;Route path=&quot;/diary&quot; element={&lt;Diary /&gt;} /&gt;
        &lt;/Routes&gt;
      &lt;/div&gt;
    &lt;/BrowserRouter&gt;
  );
}</code></pre>
<p>Header.js에서 Link 컴포넌트를 임포트 하고<code>import { Link } from &quot;react-router-dom&quot;;</code>
NavMenu들이 각각 해당 경로로 이동할 수 있도록 연결해준다. </p>
<pre><code class="language-js"> {/* 생략 */}
        &lt;NavManu isToggleOpen={isToggleOpen}&gt;
          &lt;li&gt;
            &lt;Link to={&quot;/about&quot;} className=&quot;nav-menu-list&quot;&gt;
              About
            &lt;/Link&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;Link to={&quot;/projects&quot;} className=&quot;nav-menu-list&quot;&gt;
              Projects
            &lt;/Link&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;Link to={&quot;/til&quot;} className=&quot;nav-menu-list&quot;&gt;
              TIL
            &lt;/Link&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;Link to={&quot;/diary&quot;} className=&quot;nav-menu-list&quot;&gt;
              Diary
            &lt;/Link&gt;
          &lt;/li&gt;
        &lt;/NavManu&gt;
 {/* 생략 */}</code></pre>
<h2 id="전체-코드와-실행결과">전체 코드와 실행결과</h2>
<p>!codesandbox[responsive-header-component-22epbs?fontsize=14&amp;hidenavigation=1&amp;theme=dark]</p>
<h3 id="🥲-아쉬움">🥲 아쉬움</h3>
<ul>
<li>이번 유닛에서 반응형 웹, CSS 애니메이션, HTML5를 공부하면서 레퍼런스로 기깔나는 작품들을 많이 봐서 눈은 잔뜩 높아졌는데 내가 익숙하게 구현할 수 있는 스타일링은 별게 없어서 아쉬웠다. 그치만 많이 만들어보는 수밖에 없겠지! </li>
<li>일단 되게 만드는 것에만 집중하느라 styled components를 잘 활용하지 못한 것 같다. 다음에 더 잘 활용해보기!</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[코드스테이츠 섹션3 회고]]></title>
            <link>https://velog.io/@seul_/%EC%BD%94%EB%93%9C%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%B8%A0-%EC%84%B9%EC%85%983-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@seul_/%EC%BD%94%EB%93%9C%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%B8%A0-%EC%84%B9%EC%85%983-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Wed, 20 Jul 2022 06:55:38 GMT</pubDate>
            <description><![CDATA[<p>너무 상투적인 표현이지만, 진짜 시간이 너무 빨리 지나갔다.. 그냥 하루하루 살았는데 어김없이 한달차 회고타임이 돌아왔다!! </p>
<p>섹션 3를 요약한다면 ‘기가막히게 어려운 데일리 코딩’이 아닐까싶다. 재귀, redux, 네트워크 심화, 인증,보안..  학습한 내용 모두 어려웠지만 페어 활동과 세션이 있어서 내가 이해가 안되는 부분을 파악하고라도 넘어갈 수 있는 반면, 데일리코딩은 🥲 갈피를 못잡는 삽질과 생각지도 못한 방식의 레퍼런스 코드에 압도당하기의 반복이었다. 중반부터는 데일리 코딩 풀기가 아니라, 레퍼런스 코드 이해하기가 되버린 것 같았다.. 혼자서 공부했다면, 내가 쉽게 해결할 수 있는 만만한 난이도만 깔짝거리고 있었을지도 모르지만,, 어려운건 어려운거다. </p>
<p>매번 하는 생각이지만, 내가 제 몫을 하는 개발자가 될 수 있을까 하는 의심과 불안을 떨쳐내는 것이 제일 어려운 것 같다. 이런 생각은 하자면 끝이 없으니까 구체적인 목표들에 집중해야겠다.</p>
<h3 id="keep">Keep</h3>
<ul>
<li>배운 내용 노션에 정리하는 습관을 유지했다.</li>
<li>저번에 선배 세션을 듣고부터, 코드 샌드박스를 애용하기 시작했다. 배웠던 걸 써먹으면서 처음부터 만들어보는 식으로 복습하는게 중요한 것 같다.</li>
</ul>
<h3 id="problem">Problem</h3>
<ul>
<li>블로그에 데일리 코딩 문제 해결 과정을 기록하던 것을 꾸준히 하지 못했다.(변명을 덧붙이자면 문제 파악과 레퍼런스 코드 이해에 드는 시간이 너무 길어졌다는 것)</li>
<li>잠을 잘 못잤다</li>
<li>수영장 휴장으로 운동을 쉬었더니 체력이 떨어진게 체감된다. 운동은 피곤하지 않으려고 한다는 걸 다시금 깨달았다.</li>
</ul>
<h3 id="try">Try</h3>
<ul>
<li>홈트 짧게라도 꾸준히 하기 + 수영장 개장하면 수영</li>
<li>데일리코딩 문제 포기하지 않고 해결과정 기록하기</li>
<li>개인 프로젝트 배포까지 완성하기<ul>
<li>진행과정 블로그에 남기기</li>
</ul>
</li>
<li>자바스트크립트 핵심 개념 복습하면서 블로깅하기(일단은 일주일에 2개)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS]_daily coding #33]]></title>
            <link>https://velog.io/@seul_/JSdaily-coding-33-py9z64ij</link>
            <guid>https://velog.io/@seul_/JSdaily-coding-33-py9z64ij</guid>
            <pubDate>Wed, 13 Jul 2022 13:14:17 GMT</pubDate>
            <description><![CDATA[<h2 id="seb-fe-section3-daily-coding-15_powerset">[SEB FE] Section3 Daily Coding 15_powerSet</h2>
<p>문자열을 입력받아 각 문자를 가지고 만들 수 있는 모든 부분집합을 리턴해야 한다. </p>
<p><strong>주의사항</strong></p>
<ul>
<li>arr[i]는 각 부분집합을 구성하는 원소를 연결한 문자열입니다.</li>
<li>arr[i]는 알파벳 순서로 정렬되어야 합니다.</li>
<li>집합은 중복된 원소를 허용하지 않습니다.</li>
<li>부분집합은 빈 문자열을 포함합니다.</li>
<li>arr은 사전식 순서(lexical order)로 정렬되어야 합니다</li>
</ul>
<p><strong>입출력 예시</strong></p>
<pre><code class="language-js">let output1 = powerSet(&#39;abc&#39;);
console.log(output1); // [&#39;&#39;, &#39;a&#39;, &#39;ab&#39;, &#39;abc&#39;, &#39;ac&#39;, &#39;b&#39;, &#39;bc&#39;, &#39;c&#39;]

let output2 = powerSet(&#39;jjump&#39;);
console.log(output2); // [&#39;&#39;, &#39;j&#39;, &#39;jm&#39;, &#39;jmp&#39;, &#39;jmpu&#39;, &#39;jmu&#39;, &#39;jp&#39;, &#39;jpu&#39;, &#39;ju&#39;, &#39;m&#39;, &#39;mp&#39;, &#39;mpu&#39;, &#39;mu&#39;, &#39;p&#39;, &#39;pu&#39;, &#39;u&#39;]</code></pre>
<h3 id="수도-코드">수도 코드</h3>
<ol>
<li>입력받은 문자열의 중복을 제거하고, 정렬한다. <code>arr</code></li>
<li>부분집합을 구해서 저장할 배열을 선언하고, 초기값으로 빈문자열을 넣는다.</li>
<li><code>arr</code> 배열의 요소를 하나씩 순회하면서 </li>
<li>부분집합 배열의 원소와 조합한 문자열을 부분집합 배열에 추가한다. </li>
<li>부분집합 배열을 반환한다.</li>
</ol>
<ul>
<li>위의 &#39;abc&#39;의 부분 집합을 구한다면 아래와 같다. 
초기 배열 -[&#39;&#39;]
i=0 - [&#39;&#39;, &#39;a&#39;] 
i=1 - [&#39;&#39;,&#39;a&#39;, &#39;ab&#39;, &#39;b&#39;] 
i=2 - [&#39;&#39;, &#39;a&#39;, &#39;ab&#39;, &#39;b&#39;, &#39;ac&#39; &#39;bc&#39; &#39;abc&#39;]</li>
</ul>
<p>문제의 조건에서 사전식 순서로 정렬되어야한다고 했기 때문에 마지막에 부분집합 배열을 정렬해서 반환한다.  </p>
<h3 id="코드">코드</h3>
<pre><code class="language-js">const powerSet = function (str) {
  const arr = Array.from(new Set(str)).sort()

  let set = [&quot;&quot;]; 
  for (let i = 0; i &lt; arr.length; i++) { 
    let len = set.length;
    for (let j = 0; j &lt; len; j++) {
      set.push(set[j] + arr[i]);
    }
  }
  return set.sort();
};</code></pre>
<h3 id="레퍼런스-코드">레퍼런스 코드</h3>
<p>레퍼런스 코드에서는 재귀를 사용해서 풀었다. </p>
<pre><code class="language-js">const powerSet = function (str) {
  // 정렬
  const sorted = str.split(&#39;&#39;).sort();

  // 중복 제거
  const deduplicated = sorted.reduce((acc, item) =&gt; {
    if (acc[acc.length - 1] === item) {
      return acc;
    } else {
      return acc.concat(item);
    }
  });

  let subSets = [];
  const pickOrNot = (idx, subset) =&gt; {
    // base case
    if (idx === deduplicated.length) {
      // 마지막 문자까지 검토한 경우
      subSets.push(subset);
      return;
    }

    // recursive case
    // idx번째 문자가 포함되지 않는 경우
    pickOrNot(idx + 1, subset);

    // idx번째 문자가 포함되는 경우
    pickOrNot(idx + 1, subset + deduplicated[idx]);
  };

  pickOrNot(0, &#39;&#39;);

  return subSets.sort();
};

</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS]_daily coding #32]]></title>
            <link>https://velog.io/@seul_/JSdaily-coding-32</link>
            <guid>https://velog.io/@seul_/JSdaily-coding-32</guid>
            <pubDate>Thu, 07 Jul 2022 01:21:54 GMT</pubDate>
            <description><![CDATA[<h2 id="seb-fe-section3-daily-coding-11_getitemfromtwosortedarrays">[SEB FE] Section3 Daily Coding 11_getItemFromTwoSortedArrays</h2>
<p><strong>문제</strong>
길이가 m, n이고 오름차순으로 정렬되어 있는 자연수 배열들을 입력받아 전체 요소 중 k번째 요소를 리턴해야 합니다.</p>
<p><strong>입력</strong>
인자 1 : arr1 - 자연수를 요소로 갖는 배열
인자 2 : arr2 - 자연수를 요소로 갖는 배열
인자 3 : k - number 타입의 0 이상의 정수</p>
<p><strong>출력</strong>
number 타입을 리턴해야 합니다.</p>
<p><strong>주의사항</strong>
두 배열의 길이의 합은 1,000,000 이하입니다.
어떤 배열 arr의 k번째 요소는 arr[k-1]을 의미합니다.</p>
<p><strong>입출력 예시</strong></p>
<pre><code class="language-js">let arr1 = [1, 4, 8, 10];
let arr2 = [2, 3, 5, 9];
let result = getItemFromTwoSortedArrays(arr1, arr2, 6);
console.log(result); // --&gt; 8

arr1 = [1, 1, 2, 10];
arr2 = [3, 3];
result = getItemFromTwoSortedArrays(arr1, arr2, 4);
console.log(result); // --&gt; 3</code></pre>
<p><strong>Advanced</strong>
단순히 처음부터 끝까지 찾아보는 방법(O(K)) 대신 다른 방법(O(logK))을 탐구해 보세요.</p>
<p><strong>힌트</strong>
이진 탐색(binary search)을 응용하여 해결합니다.</p>
<hr>
<h3 id="첫번째-접근">첫번째 접근</h3>
<pre><code class="language-js">const getItemFromTwoSortedArrays = function (arr1, arr2, k) {
  const total = [...arr1, ...arr2];
  total.sort((a,b) =&gt; a-b);
  return total[k-1]
};</code></pre>
<p>처음 생각한 것은 배열을 합치고 정렬해준 다음에 k-1 인덱스 값을 뽑아내는 방식이다. 문제의 조건과는 맞지 않아서 테스트를 통과하지 못한다. </p>
<h3 id="두번째-접근">두번째 접근</h3>
<pre><code class="language-js">const getItemFromTwoSortedArrays = function (arr1, arr2, k) {
  const m = arr1.length;
  const n = arr2.length; 

  const sorted = Array(m+n); // undefined로 이루어진 길이 m+n 배열

  let i= 0, j=0, idx= 0;

  while(i &lt; m &amp;&amp; j &lt; n ) {
    if(arr1[i] &lt; arr2[j]){
      sorted[idx++] = arr1[i++];
    } else {
      sorted[idx++] = arr2[j++];
    }
    console.log(sorted)
  }
  // sorted 배열의 끝까지 담기지 않으니까 추가로 while문을 돌려준다.
    while ( i &lt; m) {
        sorted[idx++] = arr1[i++]
        console.log(sorted)
    }
    while(j&lt;n) {
        sorted[idx++] = arr2[j++]
        console.log(sorted)
    }

  return sorted[k-1]

};</code></pre>
<p><img src="https://velog.velcdn.com/images/seul_/post/508c02e2-a802-4e11-9020-bbcdbf0602c4/image.png" alt="">
정렬하면서 배열에 담는 방식으로 풀어봤는데 이 방법도 역시 조건은 만족하지 못하고 입력받는 배열의 크기가 늘어날 수록 처리시간이 길어진다.. </p>
<h3 id="레퍼런스-코드">레퍼런스 코드</h3>
<p>일단 시간복잡도를 해결하지 못한 naive 코드인데 count변수가 k-1번째 target 값을 찾을때까지 반복한다. arr1 인덱스를 접근하는 변수 left, arr2 인덱스를 접근하는 변수 right를 선언해서 값을 비교하면서 left, right 인덱스를 각각 증가시키면서 순회한다. </p>
<pre><code class="language-js">const getItemFromTwoSortedArrays = function (arr1, arr2, k) {
  let count = 0,
    left = 0,
    right = 0;
  let target;
  while (count &lt; k) {
    if (arr1[left] &lt; arr2[right]) {
      target = arr1[left];
      left++;
    } else {
      target = arr2[right];
      right++;
    }
    count++;
  }
  return target;
};</code></pre>
<p>아래는 시간복잡도를 해결한 레퍼런스 코드인데, 이해가 잘 되지 않아서 다시 보는걸로!! </p>
<pre><code class="language-js">const getItemFromTwoSortedArrays = function (arr1, arr2, k) {
  let leftIdx = 0,
    rightIdx = 0;

  while (k &gt; 0) {
    // 이진 탐색을 위해 각 배열에서 k를 절반으로 쪼개서 카운트 한다.
    let cnt = Math.ceil(k / 2);
    let leftStep = cnt,
      rightStep = cnt;

    // 엣지 케이스
    // 카운트가 남았음에도 배열의 끝에 도달하면 k를 나머지 배열쪽으로 넘긴다.
    if (leftIdx === arr1.length) {
      rightIdx = rightIdx + k;
      break;
    }

    if (rightIdx === arr2.length) {
      leftIdx = leftIdx + k;
      break;
    }

    // 엣지 케이스
    // 현재 카운트가 남아있는 후보 요소들보다 많을 경우, leftStep(현재 할당량)을 남아있는 요소들의 개수로 바꾼다.
    if (cnt &gt; arr1.length - leftIdx) leftStep = arr1.length - leftIdx;
    if (cnt &gt; arr2.length - rightIdx) rightStep = arr2.length - rightIdx;

    // 두 배열의 현재 검사 요소 위치를 비교해서, 그 값이 작은 배열은 비교한 위치 앞에 있는 요소들을 모두 후보군에서 제외시킨다.
    if (arr1[leftIdx + leftStep - 1] &lt; arr2[rightIdx + rightStep - 1]) {
      leftIdx = leftIdx + leftStep;
      // 처리가 끝나면 k값을 절반으로 떨어뜨린다.
      k = k - leftStep;
    } else {
      rightIdx = rightIdx + rightStep;
      k = k - rightStep;
    }
  }

  leftMax = arr1[leftIdx - 1] || -1;
  rightMax = arr2[rightIdx - 1] || -1;

  return Math.max(leftMax, rightMax);
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 상태관리와 Redux]]></title>
            <link>https://velog.io/@seul_/React-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC%EC%99%80-Redux</link>
            <guid>https://velog.io/@seul_/React-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC%EC%99%80-Redux</guid>
            <pubDate>Wed, 06 Jul 2022 04:15:18 GMT</pubDate>
            <description><![CDATA[<h1 id="상태관리-도구의-필요성">상태관리 도구의 필요성</h1>
<ul>
<li><p>리액트에서 state는 컴포넌트 안에서 관리된다.</p>
</li>
<li><p>자식 컴포넌트들 간의 state 직접 전달이 불가능하고, 최상위 부모 컴포넌트에 state를 배치하고 부모 컴포넌트로부터 state를 전달받아서 데이터를 전달해야 한다.</p>
<ul>
<li><p>문제점 </p>
</li>
<li><p>애플리케이션이 커서 관리하는 state가 많아질 경우 이 데이터 흐름이 복잡해진다.</p>
</li>
<li><p>컴포넌트 구조가 바뀐다면 데이터 흐름을 완전히 바꿔야할 수도 있다.</p>
</li>
<li><p>또한, 해당 state를 사용하지 않는데도 하위 컴포넌트에 전달하기 위해서 props를 내려받아야 한다. (props drilling이슈)</p>
</li>
</ul>
</li>
<li><p>이런 문제는 컴포넌트에서 상태변화 로직을 분리해서 전역상태를 관리할 수 있는 저장소를 따로 두고 관리하는 방식으로 해결할 수 있다.</p>
</li>
</ul>
<h1 id="redux">Redux</h1>
<ul>
<li>Redux JavaScript의 라이브러리 중 하나(Node.js 모듈)이며, React와는 독립적인 존재</li>
<li>Redux는 MVC패턴을 대체하기 위한 아키텍처에 Reducer를 결합한 것</li>
</ul>
<h2 id="redux의-세-가지-원칙"><strong>Redux의 세 가지 원칙</strong></h2>
<h3 id="1-single-source-of-truth"><strong>1. Single source of truth</strong></h3>
<ul>
<li>동일한 데이터는 항상 같은 곳에서 가지고 와야 한다</li>
<li>데이터를 저장하는 <strong>Store</strong>라는 단 하나뿐인 공간이 있다.</li>
</ul>
<h3 id="2-state-is-read-only"><strong>2. State is read-only</strong></h3>
<ul>
<li>Reducer 이외의 공간에서는 state는 읽기 전용</li>
<li>state를 변경시키는 유일한 방법을 <strong>Action</strong> 객체를 Reducer에 dispatch하는 것</li>
</ul>
<h3 id="3-changes-are-made-with-pure-functions"><strong>3. Changes are made with pure functions</strong></h3>
<ul>
<li><strong>Reducer는</strong> 순수함수로 작성해야 한다.</li>
<li>parameter로 state, action을 전달받아서 기존의 state를 직접변경하는 것이 아니라, 새로운 state객체를 리턴해야 함</li>
</ul>
<h2 id="redux의-구조">Redux의 구조</h2>
<blockquote>
<p>Redux에서는 <strong>Action → Dispatch → Reducer → Store</strong> 순서로 데이터가 단방향으로 흐른다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/seul_/post/1c7ddcf7-9a77-4380-bef3-832da0e6ab71/image.png" alt=""></p>
<ol>
<li>상태가 변경되어야 하는 이벤트가 발생하면, 변경될 상태에 대한 정보가 담긴 <strong>Action 객체</strong> 생성</li>
<li><strong>Dispatch 함수</strong>의 인자로 Action객체를 전달</li>
<li>Dispatch 함수는 Action 객체를 <strong>Reducer 함수</strong>로 전달</li>
<li>Reducer 함수는 Action 객체의 값을 확인하고, 그 값에 따라 <strong>전역 상태 저장소 Store</strong>의 상태를 변경</li>
<li>상태가 변경되면, React는 화면을 다시 렌더링</li>
</ol>
<h3 id="action"><strong>Action</strong></h3>
<ul>
<li>어떤 액션을 취할 것인지 정의해 놓은 객체, <code>type</code> 필수, <code>payload</code> 옵션</li>
</ul>
<pre><code class="language-jsx">{ type: ‘ADD_TO_CART’, payload: request }</code></pre>
<h3 id="dispatch"><strong>Dispatch</strong></h3>
<ul>
<li>Action을 전달하는 메서드</li>
<li>Action 객체를 전달받은 Dispatch 함수는 Reducer를 호출</li>
</ul>
<pre><code class="language-jsx">// Action 객체를 직접 작성하는 경우
dispatch( { type: &#39;INCREASE&#39; } );
dispatch( { type: &#39;SET_NUMBER&#39;, payload: 5 } );

// 액션 생성자(Action Creator)를 사용하는 경우
dispatch( increase() );
dispatch( setNumber(5) );</code></pre>
<h3 id="store"><strong>Store</strong></h3>
<ul>
<li>state가 관리되는 오직 하나뿐인 저장소의 역할</li>
</ul>
<pre><code class="language-jsx">import { createStore } from &#39;redux&#39;;

const store = createStore(rootReducer);</code></pre>
<h3 id="reducer"><strong>Reducer</strong></h3>
<ul>
<li>Reducer는 현재의 state와 Action을 이용해서 새로운 state를 만들어 내는 순수함수</li>
<li><strong><strong>dispatch에게 전달받은 action 객체의 type값에 따라서 상태를 변경시킴</strong></strong></li>
</ul>
<pre><code class="language-jsx">const count = 1

// Reducer를 생성할 때에는 초기 상태를 인자로 요구합니다.
const counterReducer = (state = count, action) {

  // Action 객체의 type 값에 따라 분기하는 switch 조건문입니다.
  switch (action.type)

    //action === &#39;INCREASE&#39;일 경우
    case &#39;INCREASE&#39;:
            return state + 1

    // action === &#39;DECREASE&#39;일 경우
    case &#39;DECREASE&#39;:
            return state - 1

    // action === &#39;SET_NUMBER&#39;일 경우
    case &#39;SET_NUMBER&#39;:
            return action.payload

    // 해당 되는 경우가 없을 땐 기존 상태를 그대로 리턴
    default:
      return state;
}
// Reducer가 리턴하는 값이 새로운 상태가 됩니다.</code></pre>
<hr>
<h2 id="redux-hooks">Redux Hooks</h2>
<h3 id="useselector"><strong>useSelector()</strong></h3>
<ul>
<li>컴포넌트에서 <code>useSelector</code> 메서드를 통해 Store의 state에 접근할 수 있다. (컴포넌트와 state연결)</li>
<li>전달인자로 콜백함수를 받고, 콜백함수의 전달인자로는 state 값이 들어감</li>
</ul>
<pre><code class="language-jsx">// Redux Hooks 메서드는 &#39;redux&#39;가 아니라 &#39;react-redux&#39;에서 불러옵니다.
import { useSelector } from &#39;react-redux&#39;
const counter = useSelector(state =&gt; state.counterReducer)
console.log(counter) // 1</code></pre>
<h3 id="usedispatch"><strong>useDispatch()</strong></h3>
<ul>
<li>Action 객체를 Reducer로 전달해 주는 메서드</li>
</ul>
<pre><code class="language-jsx">import { useDispatch } from &#39;react-redux&#39;

const dispatch = useDispatch()
dispatch( increase() )
console.log(counter) // 2

dispatch( setNumber(5) )
console.log(counter) // 5</code></pre>
<h2 id="참고">참고</h2>
<ul>
<li>코드스테이츠 학습 자료 </li>
<li><a href="https://haruair.github.io/flux/docs/overview.html">https://haruair.github.io/flux/docs/overview.html</a></li>
<li><a href="https://velog.io/@huurray/React%EC%9D%98-%ED%83%84%EC%83%9D%EA%B3%BC-Flux-%ED%8C%A8%ED%84%B4%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC">https://velog.io/@huurray/React%EC%9D%98-%ED%83%84%EC%83%9D%EA%B3%BC-Flux-%ED%8C%A8%ED%84%B4%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</a></li>
<li><a href="https://bestalign.github.io/translation/cartoon-guide-to-flux/">https://bestalign.github.io/translation/cartoon-guide-to-flux/</a></li>
<li><a href="https://basemenks.tistory.com/284#6">https://basemenks.tistory.com/284#6</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS]_daily coding #31]]></title>
            <link>https://velog.io/@seul_/JSdaily-coding-31</link>
            <guid>https://velog.io/@seul_/JSdaily-coding-31</guid>
            <pubDate>Wed, 06 Jul 2022 01:29:25 GMT</pubDate>
            <description><![CDATA[<h2 id="seb-fe-section3-daily-coding-10_balancedbrackets">[SEB FE] Section3 Daily Coding 10_balancedBrackets</h2>
<p>문자열을 입력받아 문자열 내의 모든 괄호의 짝이 맞는지 여부를 리턴해야 합니다.</p>
<ul>
<li>다음 단계에 맞춰 함수를 작성해 보세요<ol>
<li>괄호의 종류를 단 한가지로 한정합니다.</li>
<li>괄호의 종류를 늘려 모든 종류의 괄호에도 작동하도록 합니다.<ol start="3">
<li>괄호를 제외한 문자열이 포함된 경우에도 작동하도록 합니다.</li>
</ol>
</li>
</ol>
</li>
</ul>
<p><strong>입력</strong>
인자 1 : str
string 타입의 괄호가 포함된 문자열</p>
<p><strong>출력</strong>
boolean 타입을 리턴해야 합니다.</p>
<p><strong>주의사항</strong>
괄호의 종류는 (, )만 고려합니다.
괄호는 먼저 열리고((), 열린만큼만 닫혀야()) 합니다.
빈 문자열을 입력받은 경우, true를 리턴해야 합니다.</p>
<p><strong>Advanced</strong>
모든 종류의 괄호((, ), {, }, [, ])가 포함된 문자열을 입력빋아 모든 괄호의 짝이 맞는지 여부를 리턴해 보세요.</p>
<pre><code class="language-js">let output = balancedBrackets(&#39;[](){}&#39;);
console.log(output); // --&gt; true

output = balancedBrackets(&#39;[({})]&#39;);
console.log(output); // --&gt; true

let output3 = balancedBrackets(&#39;[(]{)}&#39;);
console.log(output); // --&gt; false</code></pre>
<h3 id="첫번째-코드">첫번째 코드</h3>
<pre><code class="language-js">const balancedBrackets = function (str) {
  let lookUp = {};
  for(let i = 0; i &lt; str.length; i++) {
    let ele = str[i];
    lookUp[ele] ? lookUp[ele] += 1: lookUp[ele] = 1;
  }
  return lookUp[&#39;(&#39;] === lookUp[&#39;)&#39;] ? true: false;
}</code></pre>
<p>처음에는 괄호의 짝을 맞추는 경우만 생각해서, 주어진 입력에 <code>(</code>과 <code>)</code>의 개수를 객체에 따로 저장해서 출현 빈도수만으로 파악했다.문제의 조건인 열린 괄호가 먼저 나오고 그다음 열린만큼 닫혀야한다는 걸 고려하지 못했다. </p>
<h3 id="두번째-코드">두번째 코드</h3>
<p>나중에 들어온 것이 먼저 빠지는(Last In First Out) 스택 구조를 이용해서 풀었다. </p>
<ol>
<li>스택을 선언</li>
<li>먼저 입력된 문자열을 순회하는 반복문에서 str의 요소가 열린 괄호인 경우에는 스택에 담는다.</li>
<li>닫힌 괄호가 들어온 경우에는 스택에서 하나를 빼서 비교한다.
3-1. 스택에서 뺀 요소가 열린 괄호가 아닌 경우 - (열림-닫힘)짝을 맞출 수 없는 경우- 에는 false를 리턴한다.</li>
<li>반복문을 빠져나와서 스택에 남아있는 요소가 없다면 모든 괄호의 짝을 찾은 경우이므로 true를 리턴한다. <pre><code class="language-js">const balancedBrackets = function (str) {
const stack = []; 
for(let char of str) {
 if( char === &#39;(&#39;) {
   stack.push(char);
 } else {
     if(stack.pop() !== &#39;(&#39;) {
       return false;
     }
 }
}
return stack.length === 0? true: false;
}
</code></pre>
</li>
</ol>
<pre><code>이렇게 하면, 열린 괄호가 먼저 들어오고 그 다음에 닫힌 괄호가 들어와야한다는 조건을 만족시킬 수 있다. 
이제 다음 단계는 `{}`, `[]`까지 포함한 문자열을 입력받는 경우에 짝이 맞는지 판별할 수 있도록 수정해야 한다. 

### 세번째 코드 
열린 괄호일 경우에는 스택에 담아주는 것은 동일하지만, 짝에 맞는 열린 괄호인 경우를 체크해주는 조건이 더 필요하다. 
```js
const balancedBrackets = function (str) {
  const stack = []; 
  for(let char of str) {
    //열린 괄호가 들어오는 경우- stack에 담아줌 
    if( char === &#39;(&#39; || char ===&#39;{&#39; || char === &#39;[&#39;) {
      stack.push(char);
      continue;
    } 

    if( stack.length === 0) return false;

    //닫힌 괄호가 들어오는 경우 체크 
    let check = stack.pop(); 
    switch (char) {
      case &#39;)&#39;:
        if (check == &#39;{&#39; || check == &#39;[&#39;) return false;
        break;
      case &#39;}&#39;:
        if (check == &#39;(&#39; || check == &#39;[&#39;) return false;
        break;
      case &#39;]&#39;:
        if (check == &#39;(&#39; || check == &#39;{&#39;) return false;
        break;

    }
  }
  return stack.length === 0? true : false;
};</code></pre><p>닫힌 괄호인 경우 종류에 따라 케이스를 나누고, 각각 스택에서 빼낸 값이 괄호 짝이 맞는지를 체크해줬다. </p>
<h3 id="레퍼런스-코드">레퍼런스 코드</h3>
<pre><code class="language-js">const balancedBrackets = function (str) {
  const stack = [];
  const opener = {
    &#39;{&#39;: &#39;}&#39;,
    &#39;[&#39;: &#39;]&#39;,
    &#39;(&#39;: &#39;)&#39;,
  };
  const closer = &#39;}])&#39;;

  for (let i = 0; i &lt; str.length; i++) {
    if (str[i] in opener) {
      stack.push(str[i]);
    } else if (closer.includes(str[i])) {
      const top = stack.pop();
      const pair = opener[top];
      if (pair !== str[i]) {
        return false;
      }
    }
  }

  return stack.length === 0;
};</code></pre>
<p>레퍼런스 코드는 괄호 짝을 객체로 만들어서 비교를 좀 더 깔끔하게 했다!bb </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS]_daily coding #30]]></title>
            <link>https://velog.io/@seul_/JSdaily-coding-30</link>
            <guid>https://velog.io/@seul_/JSdaily-coding-30</guid>
            <pubDate>Tue, 05 Jul 2022 14:19:11 GMT</pubDate>
            <description><![CDATA[<h2 id="seb-fe-section3-daily-coding-09_rotatedarraysearch">[SEB FE] Section3 Daily Coding 09_rotatedArraySearch</h2>
<h3 id="문제">문제</h3>
<p>부분적으로 오름차순 정렬된 정수의 배열(rotated)과 정수(target)를 입력받아 target의 인덱스를 리턴해야 합니다.
부분적으로 정렬된 배열: 배열을 왼쪽** 혹은 오른쪽으로 0칸 이상 순환 이동할 경우 완전히 정렬되는 배열
예시: [4, 5, 6, 0, 1, 2, 3]은 왼쪽으로 3칸 또는 오른쪽으로 4칸 순환 이동할 경우 완전히 정렬됩니다.
**
<strong>입력</strong>
인자 1 : rotated
number 타입을 요소로 갖는 배열
rotated[i]는 정수
인자 2 : target
number 타입의 정수</p>
<p><strong>출력</strong>
number 타입을 리턴해야 합니다.</p>
<p><strong>주의사항</strong>
rotated에 중복된 요소는 없습니다.
target이 없는 경우, -1을 리턴해야 합니다.</p>
<p><strong>입출력 예시</strong></p>
<pre><code class="language-js">let output = rotatedArraySearch([4, 5, 6, 0, 1, 2, 3], 2);
console.log(output); // --&gt; 5

output = rotatedArraySearch([4, 5, 6, 0, 1, 2, 3], 100);
console.log(output); // --&gt; -1</code></pre>
<p><strong>Advanced</strong>
단순히 처음부터 끝까지 찾아보는 방법(O(N)) 대신 다른 방법(O(logN))을 탐구해 보세요.</p>
<p><strong>힌트</strong>
이진 탐색(binary search)을 약간 변형하여 해결합니다.</p>
<hr>
<h3 id="문제-접근">문제 접근</h3>
<p>우선, 이진 탐색은 <strong>정렬된 리스트</strong>에서 검색 범위를 줄여 나가면서 검색 값을 찾는 알고리즘이다. 이진 탐색 알고리즘을 수도코드로 표현하면 다음과 같다. </p>
<ol>
<li>배열의 중간 값을 가져와서</li>
<li>검색 값과 비교한다
2-1. 중간값 === 검색값 - 탐색 종료
2-2. 중간 값 &lt; 검색 값 - 중간값의 오른쪽 구간을 대상으로 탐색
2-3. 중간 값 &gt; 검색 값 - 중간값의 왼쪽 구간을 대상으로 탐색</li>
<li>검색값을 찾거나, 검색구간이 비어있을 경우 탐색 종료</li>
</ol>
<p>코드로 구현할 경우에는 인덱스를 이용해서 최소, 최대값을 따로 저장해서 반복문을 통해서 이 값을 갱신해서 탐색하는 배열의 범위를 줄여나간다. </p>
<pre><code class="language-js">function binary(arr, target) {
  let left = 0;
  let right = arr.length-1;

  while(left&lt;=right){
    let mid = parseInt((left+right)/2)
    if(arr[mid]===target) {
        return mid 
    } else if(target &lt; arr[mid]) {
        right = mid -1;
    } else {
        left = mid +1;
    }
  }
    return -1
}</code></pre>
<h3 id="solution">solution</h3>
<p>그런데 이 문제의 경우에는 입력되는 배열이 두 부분으로 나뉘어서 각각 정렬되어 있기 때문에 정렬된 두 부분의 경계를 고려해서 left와 right를 설정하는데 추가적인 로직이 필요하다. </p>
<pre><code class="language-js">const rotatedArraySearch = function (rotated, target) {
  // O(n) solution:
  //return rotated.findIndex((el)=&gt; el === target)

  //O(log n) solution:
  let left = 0;
  let right = rotated.length-1;

  while(left&lt;=right){
    let mid = parseInt((left+right)/2)
    if(rotated[mid]===target)return mid

    // 왼쪽 절반이 정렬되어 있는 상태
    if(rotated[mid]&lt;rotated[left]){
      if(target&lt;=rotated[right] &amp;&amp; target&gt;rotated[mid]){
        left = mid+1;
      }else { 
        right = mid-1;
      }
    }

       // 오른쪽 절반이 정렬되어 있는 상태
    else{
      if(target&gt;=rotated[left] &amp;&amp; target&lt;rotated[mid]){
        right = mid-1;
      }else{
        left = mid+1;
      }
    }

  }return -1
};</code></pre>
<h3 id="참고">참고</h3>
<ul>
<li><a href="https://yoongrammer.tistory.com/75">https://yoongrammer.tistory.com/75</a></li>
<li><a href="https://cjh5414.github.io/binary-search/">https://cjh5414.github.io/binary-search/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS]_daily coding #29]]></title>
            <link>https://velog.io/@seul_/JSdaily-coding-29</link>
            <guid>https://velog.io/@seul_/JSdaily-coding-29</guid>
            <pubDate>Mon, 04 Jul 2022 01:45:48 GMT</pubDate>
            <description><![CDATA[<h2 id="seb-fe-section3-daily-coding-08_treebfs">[SEB FE] Section3 Daily Coding 08_treeBFS</h2>
<p>임의의 tree를 구성하는 노드 중 하나의 Node 객체를 입력받아, 해당 노드를 시작으로 너비 우선 탐색(BFS, Breadth First Search)을 합니다. 이 때, 탐색되는 순서대로 노드의 값이 저장된 배열을 리턴해야 합니다.</p>
<hr>
<h3 id="입출력-예시">입출력 예시</h3>
<pre><code class="language-js">let root = new Node(1);
let rootChild1 = root.addChild(new Node(2));
let rootChild2 = root.addChild(new Node(3));
let leaf1 = rootChild1.addChild(new Node(4));
let leaf2 = rootChild1.addChild(new Node(5));
let output = bfs(root);
console.log(output); // --&gt; [1, 2, 3, 4, 5]

leaf1.addChild(new Node(6));
rootChild2.addChild(new Node(7));
output = bfs(root);
console.log(output); // --&gt; [1, 2, 3, 4, 5, 7, 6]</code></pre>
<h3 id="문제접근">문제접근</h3>
<p>예시에서 생성자 함수(Node)와 메소드(addChild)를 통해서 만든 root 객체를 출력한다면,아래와 같다.  </p>
<pre><code class="language-bash">{
  value: 1,
  children: [
    {
      value: 2,
      children: [
        { value: 4, children: [] },
        { value: 5, children: [] },
      ],
    },
    { value: 3, children: [] },
  ],
}</code></pre>
<p>이 root객체를 시작으로 너비 우선 탐색을 하면 1, 2, 3, 4, 5 의 순서로 탐색을 해야한다.  </p>
<p>여기에 Node(6), Node(7)을 추가하면, 1, 2, 3, 4, 5,  7, 6 순서로 탐색해야 한다. </p>
<pre><code class="language-bash">{
  value: 1,
  children: [
    {
      value: 2,
      children: [
        { value: 4, children: [{ value: 6, children: [] }] },
        { value: 5, children: [] },
      ],
    },
    { value: 3, children: [{ value: 7, children: [] }] },
  ],
}</code></pre>
<p>깊이 우선 탐색과 달리 너비 우선 탐색은 시작 노드에서 인접한 노드를 먼저 탐색하는 방법이라고 한다. dfs와는 달리 자식노드로 파고드는 재귀적인 방법으로 풀 수 없다. 일반적으로 큐를 이용해서 반복적 형태로 구현한다. </p>
<blockquote>
<p>큐(queue)는 컴퓨터 과학 분야에서 쓰이는 컴퓨터의 기본적인 자료 구조의 한가지로, 먼저 집어 넣은 데이터가 먼저 나오는 FIFO(First In First Out)구조로 저장하는 형식</p>
</blockquote>
<p>예시의 루트노드를 너비 우선 탐색하는 과정을 생각해보면, </p>
<ol>
<li>(value:1)을 먼저 큐에 담고 <ul>
<li>현재 큐의 상태: [(value:1)]</li>
</ul>
</li>
<li>(value:1)을 큐에서 꺼낸 뒤에 (value:1)의 인접한 자식노드인 (value:2), (value:3)를 큐에 담는다. <ul>
<li>현재 큐의 상태: [(value:2), (value:3)]</li>
</ul>
</li>
<li>다시 큐의 맨 처음 요소인 (value:2)를 꺼내고 인접한 자식노드인 (value:4) (value:5)를 큐에 담는다. <ul>
<li>현재 큐의 상태: [(value:3), (value:4), (value:5)]    </li>
</ul>
</li>
<li>다시 큐의 맨 처음 요소인 (value:3)를 꺼내고 인접한 자식노드인 (value:7)을 큐에 담는다. <ul>
<li>현재 큐의 상태: [(value:4), (value:5), (value:7)]    </li>
</ul>
</li>
<li>다시 큐의 맨 처음 요소인 (value:4)를 꺼내고 인접한 자식노드 (value:6)을 큐에 담는다.<ul>
<li>현재 큐의 상태: [(value:5), (value:7), (value:6)]    </li>
</ul>
</li>
<li>다시 큐의 맨 처음 요소 (value:5)를 꺼내고, 인접한 자식 노드가 없으니까 큐에 새로운 노드를 넣는 과정은 생략한다. <ul>
<li>현재 큐의 상태: [(value:7), (value:6)]</li>
</ul>
</li>
<li>큐의 맨 처음 요소 (value:7)을 꺼낸다. <ul>
<li>현재 큐의 상태: [(value:6)]</li>
</ul>
</li>
<li>큐의 맨 처음 요소 (value:8)을 꺼낸다. <ul>
<li>현재 큐의 상태: []</li>
</ul>
</li>
<li>큐에 저장된 노드가 없으니까 탐색을 종료한다. </li>
</ol>
<p>요약하면, 
큐에 첫번째 요소를 꺼내고, 해당 요소와 인접한 자식 노드를 큐에 담는 과정을 반복한다. 이 과정을 큐에 저장된 노드가 없을때까지 계속 반복한다. </p>
<h3 id="수도코드">수도코드</h3>
<ol>
<li>큐로 기능할 배열은 선언하고, 탐색할 노드를 배열에 담아준다. </li>
<li>탐색 순서를 저장할 빈 배열 order를 선언한다. </li>
<li>큐의 0번 인덱스를 변수 head로 저장하고 (큐 배열에서는 삭제)</li>
<li>head의 value를 order배열에 저장한다. </li>
<li>head의 children 배열의 요소를 큐에 저장한다. </li>
<li>위의 3-4-5 과정을 큐 배열의 길이가 0이 될때까지 반복한다.</li>
<li>order 배열을 리턴한다. </li>
</ol>
<h3 id="작성한-코드">작성한 코드</h3>
<pre><code class="language-js">let bfs = function (node) {
  // TODO: 여기에 코드를 작성합니다.
  let queue = [node];
  let order = [];
  while(queue.length &gt; 0) {
    //큐에서 첫번째 요소를 제거하고, 이를 head에 저장한다.  
    const head = queue.shift()
    // head value값을 order 배열에 저장한다. 
    order.push(head.value);
    // head의 자식 노드를 하나씩 큐에 담아준다. 
    for(let child of head.children) {
      queue.push(child)
    }
  }
  return order;
};

// 이 아래 코드는 변경하지 않아도 됩니다. 자유롭게 참고하세요.
let Node = function (value) {
  this.value = value;
  this.children = [];
};

// 위 Node 객체로 구성되는 트리는 매우 단순한 형태의 트리입니다.
// membership check(중복 확인)를 따로 하지 않습니다.
Node.prototype.addChild = function (child) {
  this.children.push(child);
  return child;
};</code></pre>
<h3 id="레퍼런스-코드">레퍼런스 코드</h3>
<pre><code class="language-js">let bfs = function (node) {
  // TODO: 여기에 코드를 작성합니다.
  let queue = [node];
  const values = [];
  while (queue.length &gt; 0) {
    const head = queue[0];
    queue = queue.slice(1);

    values.push(head.value);

    head.children.forEach((child) =&gt; queue.push(child));
  }
  return values;
};</code></pre>
<p>shift()로 큐에서 삭제하는 것이 아니라 slice()로 head를 뺀 나머지 요소를 복사해서 큐에 재할당 해주었다. </p>
<h3 id="참고">참고</h3>
<ul>
<li><a href="https://youtu.be/_hxFgg7TLZQ">https://youtu.be/_hxFgg7TLZQ</a></li>
<li><a href="https://gmlwjd9405.github.io/2018/08/15/algorithm-bfs.html">https://gmlwjd9405.github.io/2018/08/15/algorithm-bfs.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS]_daily coding #28]]></title>
            <link>https://velog.io/@seul_/JSdaily-coding-28</link>
            <guid>https://velog.io/@seul_/JSdaily-coding-28</guid>
            <pubDate>Fri, 01 Jul 2022 09:55:07 GMT</pubDate>
            <description><![CDATA[<h2 id="seb-fe-section3-daily-coding-07_power">[SEB FE] Section3 Daily Coding 07_power</h2>
<p><strong>문제</strong>
두 수를 입력받아 거듭제곱을 리턴해야 합니다.</p>
<p><strong>입력</strong>
인자 1: base
number 타입의 자연수 (base &gt;= 2)
인자 2: exponent
number 타입의 정수 (exponent &gt;= 0)</p>
<p><strong>출력</strong>
number 타입을 리턴해야 합니다.
실제 계산 결과를 94,906,249로 나눈 나머지를 리턴해야 합니다.</p>
<p>*<em>주의사항 *</em>
Math.pow, 거듭제곱 연산자 사용은 금지됩니다.
시간복잡도 O(logN)을 만족해야 합니다.
나머지를 구하는 이유는 계산 결과가 컴퓨터로 나타낼 수 있는 수의 범위를 넘을 수 있기 때문입니다. 하지만 모든 연산이 끝난 뒤에 그 결과를 94,906,249로 나누려고 해서는 안 됩니다. 연산 중간에도 이 범위를 넘을 수 있기 때문에, 연산을 할 때마다 나머지를 구하고 그 결과에 연산을 이어가시기 바랍니다.</p>
<hr>
<h3 id="문제-접근">문제 접근</h3>
<p>문제의 주의사항에 나온 것처럼 Math.pow(), <code>**</code>, 반복문을 사용하면 쉽게 거듭제곱을 구할 수 있다. 그러나 주어지는 exponent로 엄청 큰 값이 들어올 경우를 생각해보면 거듭제곱하는 연산 횟수도 그만큼 늘어나야 하기 때문에 시간복잡도가 O(N)이다.
일단, 거듭제곱의 성질을 살펴 보면,
<img src="https://velog.velcdn.com/images/seul_/post/b0d38f1c-06ef-4de4-bc2b-d1864ae8c25a/image.png" alt=""></p>
<p>지수를 2로 나눠서 결과를 반으로 쪼개면서 답을 구해나갈 수 있다. 지수가 홀수인 경우와 짝수인 경우를 나눠서 처리해주면 된다. </p>
<h3 id="코드">코드</h3>
<pre><code class="language-js">function power(base, exponent) {
  if(exponent === 0) return 1

  let tmp = power(base, Math.floor(exponent/2))

  let result = tmp * tmp % 94906249 //94,906,249보다 작으면 tmp * tmp가 출력

  if(exponent % 2 === 0) {
    return result  //짝수
  } else {
    return base * result % 94906249 // 홀수
  } 
}</code></pre>
<h3 id="주말에-더-공부할-것">주말에 더 공부할 것</h3>
<ul>
<li>분할정복</li>
<li>재귀 + 이번주 문제들 다시 풀어보기 </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS]_daily coding #27]]></title>
            <link>https://velog.io/@seul_/JSdaily-coding-27</link>
            <guid>https://velog.io/@seul_/JSdaily-coding-27</guid>
            <pubDate>Thu, 30 Jun 2022 14:11:30 GMT</pubDate>
            <description><![CDATA[<h2 id="코플릿-데일리-코딩-06_treedfs">코플릿 데일리 코딩 06_treeDFS</h2>
<h3 id="문제">문제</h3>
<p>임의의 tree를 구성하는 노드 중 하나의 Node 객체를 입력받아, 해당 노드를 시작으로 깊이 우선 탐색(DFS, Depth First Search)을 합니다. 이 때, 탐색되는 순서대로 노드의 값이 저장된 배열을 리턴해야 합니다.</p>
<h3 id="입력">입력</h3>
<p>인자 1 : node
&#39;value&#39;, &#39;children&#39; 속성을 갖는 객체 (Node)
&#39;node.value&#39;는 number 타입
&#39;node.children&#39;은 Node를 요소로 갖는 배열</p>
<h3 id="출력">출력</h3>
<p>배열을 리턴해야 합니다.</p>
<h3 id="입출력-예시">입출력 예시</h3>
<pre><code class="language-js">let root = new Node(1);
let rootChild1 = root.addChild(new Node(2));
let rootChild2 = root.addChild(new Node(3));
let leaf1 = rootChild1.addChild(new Node(4));
let leaf2 = rootChild1.addChild(new Node(5));
let output = dfs(root);
console.log(output); // --&gt; [1, 2, 4, 5, 3]

leaf1.addChild(new Node(6));
rootChild2.addChild(new Node(7));
output = dfs(root);
console.log(output); // --&gt; [1, 2, 4, 6, 5, 3, 7]</code></pre>
<h3 id="문제-접근">문제 접근</h3>
<p>예시에서 생성자 함수(Node)와 메소드(addChild)를 통해서 만든 root 객체를 출력한다면,아래와 같다.  </p>
<pre><code class="language-bash">{
  value: 1,
  children: [
    {
      value: 2,
      children: [
        { value: 4, children: [] },
        { value: 5, children: [] },
      ],
    },
    { value: 3, children: [] },
  ],
}</code></pre>
<p>이 root객체를 시작으로 깊이 우선 탐색을 하면 1, 2, 4, 5, 3의 순서로 탐색을 해야한다.  </p>
<p>여기에 Node(6), Node(7)을 추가하면, 1, 2, 4, 6, 5, 3, 7 순서로 탐색해야 한다. </p>
<pre><code class="language-bash">{
  value: 1,
  children: [
    {
      value: 2,
      children: [
        { value: 4, children: [{ value: 6, children: [] }] },
        { value: 5, children: [] },
      ],
    },
    { value: 3, children: [{ value: 7, children: [] }] },
  ],
}</code></pre>
<h3 id="코드">코드</h3>
<p>재귀함수를 만들어서 풀었다. node의 value를 결과배열에 담고 children 배열에 각각 재귀 함수를 적용한다. for문을 통해서 children 배열에 요소가 한 개가 아닐 경우에는 0번 인덱스 객체에 먼저 접근하게 된다. (계속 0번 객체 만 깊이로 방문하다가) 말단 노드의 children이 빈배열일 경우에는 value만 result에 담고 for문은 실행하지 않는다. 그 다음에는 상위 노드의 1번 인덱스 객체(형제 노드)가 있으면 접근하는 식으로 깊이 우선 탐색이 이루어진다.   </p>
<pre><code class="language-js">let dfs = function (node) {
  let result = [];
  const recursion = function (node) {
    //console.log(node.value);
    result.push(node.value);
    for (let i = 0; i &lt; node.children.length; i++) {
      recursion(node.children[i]);
    }
  };
  recursion(node);
  return result;
};

// 이 아래 코드는 변경하지 않아도 됩니다. 자유롭게 참고하세요.
let Node = function (value) {
  this.value = value;
  this.children = [];
};

// 위 Node 객체로 구성되는 트리는 매우 단순한 형태의 트리입니다.
// membership check(중복 확인)를 따로 하지 않습니다.
Node.prototype.addChild = function (child) {
  this.children.push(child);
  return child;
};
</code></pre>
<h3 id="레퍼런스-코드">레퍼런스 코드</h3>
<pre><code class="language-js">let dfs = function (node) {
  let values = [node.value];

  node.children.forEach((n) =&gt; {
    values = values.concat(dfs(n));
  });

  return values;
};

// 이 아래 코드는 변경하지 않아도 됩니다. 자유롭게 참고하세요.
let Node = function (value) {
  this.value = value;
  this.children = [];
};

// 위 Node 객체로 구성되는 트리는 매우 단순한 형태의 트리입니다.
// membership check(중복 확인)를 따로 하지 않습니다.
Node.prototype.addChild = function (child) {
  this.children.push(child);
  return child;
};</code></pre>
<h3 id="더-공부할-것">더 공부할 것</h3>
<ul>
<li>재귀, dfs</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS]_daily coding #26]]></title>
            <link>https://velog.io/@seul_/JSdaily-coding-26-8c95fq70</link>
            <guid>https://velog.io/@seul_/JSdaily-coding-26-8c95fq70</guid>
            <pubDate>Wed, 29 Jun 2022 13:26:48 GMT</pubDate>
            <description><![CDATA[<h2 id="코플릿-데일리-코딩-05_tiling">코플릿 데일리 코딩 05_tiling</h2>
<p>세로 길이 2, 가로 길이 n인 2 x n 보드가 있습니다. 2 x 1 크기의 타일을 가지고 이 보드를 채우는 모든 경우의 수를 리턴해야 합니다.</p>
<p>입력: number 타입의 1 이상의 자연수
출력: number 타입을 리턴해야 합니다.</p>
<hr>
<h3 id="문제-파악하기">문제 파악하기</h3>
<p>일단 문제를 이해하는데도 한참 걸렸다. 문제에 있는 입출력 예시는 입력으로 4가 주어지면, 
2 X 4 보드에 타일을 놓는 방법이 아래의 5가지이므로 5가 출력된다. 
<img src = 'https://velog.velcdn.com/images/seul_/post/319a64e7-75d4-4259-820e-de97eb109e90/image.png' height = 60%></p>
<p>패턴을 살펴보면 1,2,3의 경우는 맨 앞에 세로로 놓인 타일 1개를 제외하면 2 X 3보드에 타일을 놓는 패턴과 일치하고, 
<img src="https://velog.velcdn.com/images/seul_/post/34234879-d3d2-416a-9c67-004e9189e047/image.png" alt=""></p>
<p>4,5의 경우는 맨 앞에 가로로 놓인 타일 2개를 제외하면 2 X 2보드에 타일을 놓는 패턴과 같다. 
<img src="https://velog.velcdn.com/images/seul_/post/de4fb401-c355-4b3d-b9b8-bd8d7edf82f2/image.png" alt=""></p>
<p>즉, 2 x 4 보드에 타일을 놓는 방법은 2 x 3 보드에 타일을 놓는 방법과 2 x 2 보드에 타일을 놓는 방법을 더한 결과와 같다는 것을 알 수 있다. </p>
<p>마찬가지로, 2 x 5 보드에 타일을 놓는 패턴도 살펴보면 2 x 4 보드에 타일을 놓는 방법과 2 x 3 보드에 타일을 놓는 방법을 더한 결과와 같다.
<img src = 'https://velog.velcdn.com/images/seul_/post/6bd259d2-59f0-4be2-b5be-3e19e5b79afa/image.png
'></p>
<h3 id="첫번째-접근">첫번째 접근</h3>
<p>따라서, 피보나치 수열 문제와 같아서 재귀함수로 구할 수 있다. 이렇게 하면 예시처럼 작은 수의 입력은 정상적으로 출력할 수 있다. </p>
<pre><code class="language-js">let tiling = function (n) {
    if (n &lt;= 2) return n;
    return tiling(n - 1) + tiling(n - 2);
};</code></pre>
<p>다만 피보나치 수열 문제를 풀 때처럼 동일한 연산이 중복해서 실행되는 경우를 고려해서 풀어야지 크기가 큰 입력이 주어졌을때 효율적으로 해결할 수 있다. </p>
<h3 id="두번째-접근">두번째 접근</h3>
<p>연산 결과를 배열에 기록하고 이미 해결한 문제는 풀지 않는 보조 함수를 만들어서 해결하는 방법을 사용해서 해결할 수 있다. (재귀 + 메모지에이션)</p>
<pre><code class="language-js">let tiling = function (n) {

  const memo = [0,1,2]; 
  const aux = (size) =&gt;{
    // 이미 해결한 적이 있으면, 메모해둔 정답을 리턴한다.
    if (memo[size] !== undefined) return memo[size];
    // 새롭게 풀어야하는 경우, 문제를 풀고 메모해둔다.
    memo[size] = aux(size -1) + aux(size -2);
    return memo[size];
  }
  return aux(n)
};</code></pre>
<h3 id="세번째-접근">세번째 접근</h3>
<p>재귀 함수를 따로 만들어주는 것이 아니라 결과를 저장해두고 결과를 리턴하는 방식으로 해결할 수도 있다. </p>
<pre><code class="language-js">let tiling = function (n) {
  if (n &lt;= 1) return n
  return tiling(n-1) + tiling(n-2)
  const memo = [0, 1, 2];
  if (n &lt;= 2) return memo[n];
  for (let size = 3; size &lt;= n; size++) {
    memo[size] = memo[size - 1] + memo[size - 2];
  }
  return memo[n];
};
</code></pre>
<h3 id="더-공부할-것">더 공부할 것</h3>
<ul>
<li>동적계획법 </li>
<li>레퍼런스 코드에 dynamic with slicing window 방식도 나와있었는데 이해하지 못했다. 주말에 더 공부해보기! </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS]_daily coding #25]]></title>
            <link>https://velog.io/@seul_/JSdaily-coding-25</link>
            <guid>https://velog.io/@seul_/JSdaily-coding-25</guid>
            <pubDate>Wed, 29 Jun 2022 00:33:28 GMT</pubDate>
            <description><![CDATA[<h2 id="코플릿-04_issubsetof">코플릿 04_isSubsetOf</h2>
<p>두 개의 배열(base, sample)을 입력받아 sample이 base의 부분집합인지 여부를 리턴해야 합니다.</p>
<ul>
<li>number 타입을 요소로 갖는 임의의 배열 2개를 입력 받아서 boolean 타입을 리턴해야 한다. </li>
<li>base, sample 내에 중복되는 요소는 없다고 가정한다. </li>
</ul>
<hr>
<h3 id="수도코드">수도코드</h3>
<ol>
<li><p>sample요소가 base 배열에 포함되어있는지를 기록하는 count 변수를 선언하고 0으로 초기화</p>
</li>
<li><p>sample 배열의 요소를 순회하는 반복문 선언 </p>
</li>
<li><p>base배열에 sample 배열의 요소들이 포함되어 있는 경우에는 count 값에 1을 더해준다. </p>
</li>
<li><p>반복문을 빠져나와서 count가 sample 길이와 같은 경우 true를 리턴한다.(sample 배열의 모든 요소가 base배열에 포함된 경우이므로)</p>
</li>
<li><p>그렇지 않은 모든 경우에는 false 리턴 </p>
<h3 id="코드">코드</h3>
<p>advanced 케이스를 생각하지 않고 일단 풀었다. 문제의 예시처럼 입력되는 배열의 길이가 짧은 경우에는 정상적으로 값을 반환하지만 실행시간이 초과되어서 테스트를 통과하지 못했다. 내가 작성한 코드는 sample 배열을 순회할때마다 includes()메서드를 통해서 base 배열을 순회하고 있기 때문에 이중 for문처럼 작동한다. </p>
<pre><code class="language-js">const isSubsetOf = function (base, sample) {
let count = 0;
for(let item of sample) {
 if(base.includes(item)) {
   count++;
 }
}
if(count === sample.length) return true;
return false;

// every() 메서드를 이용한 풀이_배열 안의 모든 요소가 주어진 판별 함수를 통과하는지 여부를 boolean으로 리턴
// return sample.every((item) =&gt; base.includes(item));
};</code></pre>
<h3 id="다시-풀어보기">다시 풀어보기!</h3>
<p>레퍼런스 코드에는 입력된 배열을 각각 정렬한 다음 두 배열을 비교해주는 것 같은데, 잘 이해가 되지 않아서 다른 방법을 찾아봤다. 지금 듣고 있는 유데미 자바스크립트 알고리즘 강의 목차를 뒤지고, 구글을 뒤진 결과, 이 문제를 해시 유형으로 볼 수 있을 것 같았다. </p>
<blockquote>
<p>해시 테이블은 (Key, Value)로 데이터를 저장하는 자료구조 중 하나로 빠르게 데이터를 검색할 수 있는 자료구조. 해시 테이블은 각각의 key값에 해시함수를 적용해 배열의 고유한 인덱스를 생성하고 이 인덱스를 활용해서 값을 저장하거나 검색할 수 있다. </p>
</blockquote>
</li>
</ol>
<p>아직 해시 자료구조 자체에 대해서는 잘 이해가 되지 않지만, 문제 풀이에는 적용시켜 볼 수 있을 것 같았다. 비교대상인 길이가 긴 배열의 요소를 키로 하여 객체에 담아주고, 비교 주체인 배열의 요소를 키로 갖는 값이 있는지 확인하는 식으로 접근할 수 있다. </p>
<ol>
<li>base 배열을 순회하면서 요소를 하나씩 새로운 객체에 담아준다. (key는 base 배열의 요소이고,value는 임의의 값)</li>
<li>sample 배열을 순회하면서 해당 객체에 sample 배열의 요소를 key로 갖는 value가 있는지 확인한다. 해당 요소를 가지고 있지 않다면 (undefined) sample배열은 base배열의 부분집합이 아니기 때문에 false를 리턴한다. </li>
<li>위 조건에 해당되지 않아서 for문을 통과하면 true를 리턴한다. <h3 id="코드-1">코드</h3>
<pre><code class="language-js">const isSubsetOf = function (base, sample) {
let obj = {}
for(let item of base) {
 obj[item] = 1; // 임의의 값 
}
for(let item of sample){
 if(obj[item]=== undefined) {
   return false;
 }
}
return true
};</code></pre>
Map을 사용해서도 풀 수도 있다. <pre><code class="language-js">const isSubsetOf = function (base, sample) {
let map = new Map()
for(let item of base) {
 map.set(item, true);
}
for(let item of sample) {
 if(map.get(item)===undefined) {
   return false;
 }
}
return true
};</code></pre>
프로그래머스 알고리즘 문제 중에도 해시를 사용해서 풀 수 있는 문제가 있어서 풀어봤다. </li>
</ol>
<h2 id="프로그래머스-해시-완주하지-못한-선수">프로그래머스 :<a href="https://programmers.co.kr/learn/courses/30/lessons/42576">(해시) 완주하지 못한 선수 </a></h2>
<p>문제 설명
수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.</p>
<p>마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.</p>
<p>제한사항
마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.
completion의 길이는 participant의 길이보다 1 작습니다.
참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.
참가자 중에는 동명이인이 있을 수 있습니다.</p>
<hr>
<h3 id="수도코드-1">수도코드</h3>
<ol>
<li>participant를 매핑할 빈 객체를 선언한다. </li>
<li>participant 배열을 순회하면서 선수 이름을 key로 1을 value로 담아준다. 
2-1. 동명이인이 있는 경우가 있으므로 해당 key가 존재할 경우에 기존 value에 1을 더해서 재할당한다. </li>
<li>completion 배열을 돌면서 매핑한 객체에 선수이름을 key가 존재하는지를 검사하고, 존재하면 -1을 해준다. </li>
<li>완주 선수는 0을 value로 가지고, 그렇지 않은 선수는 1이상을 가지고 있다. </li>
<li>value가 1이상인 선수를 리턴한다. <h3 id="코드-2">코드</h3>
<pre><code class="language-js">function solution(participant, completion) {
let hashTable = {};
for(let p of participant) {
 //이미 해당 키가 존재하는 경우에는 value에 +1 
 !hashTable[p] ? hashTable[p] = 1: hashTable[p] = hashTable[p] + 1;
}
for(let c of completion) {
 //매핑한 객체에 완주선수 이름이 존재하면 value에 -1 
 hashTable[c] ? hashTable[c] -= 1 : hashTable[c]
}
// 완주한 선수는 0을 value로 가짐, 그렇지 않은 경우는 1이상
for(let p of participant) {
 if(hashTable[p]&gt;=1) {
     return p
 }
}
}</code></pre>
<h3 id="더-공부할-것">더 공부할 것</h3>
</li>
</ol>
<ul>
<li>자료구조, 해시 </li>
<li>set, map
푸는 것도 힘들었지만 이렇게 다시 이해한 내용을 설명하려니까 더 힘들었다. 쉽게 설명할 수 있도록 더 공부해봐야겠다.<h3 id="참고">참고</h3>
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map</a>
<a href="https://velog.io/@garudanish/8%EC%9E%A5-%ED%95%B4%EC%8B%9C-%ED%85%8C%EC%9D%B4%EB%B8%94%EB%A1%9C-%EB%A7%A4%EC%9A%B0-%EB%B9%A0%EB%A5%B8-%EB%A3%A9%EC%97%85">https://velog.io/@garudanish/8%EC%9E%A5-%ED%95%B4%EC%8B%9C-%ED%85%8C%EC%9D%B4%EB%B8%94%EB%A1%9C-%EB%A7%A4%EC%9A%B0-%EB%B9%A0%EB%A5%B8-%EB%A3%A9%EC%97%85</a></li>
</ul>
]]></description>
        </item>
    </channel>
</rss>