<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Dvelopment blog</title>
    <link>https://re-hwi.tistory.com/</link>
    <description>재휘의 개발일기</description>
    <language>ko</language>
    <pubDate>Sat, 30 May 2026 05:29:22 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>re-hwi</managingEditor>
    <image>
      <title>Dvelopment blog</title>
      <url>https://tistory1.daumcdn.net/tistory/5129880/attach/6d52b03856634fb1b385506ed4083e22</url>
      <link>https://re-hwi.tistory.com</link>
    </image>
    <item>
      <title>1월이 가기전에 쓰는 2025 회고</title>
      <link>https://re-hwi.tistory.com/175</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;너무 늦은감이 있지만 그래도 1월이 가기전에 25년 회고를 써보려 한다. 한 해를 돌아보니 정말 많은 일들이 있었다. 부트캠프 졸업부터 대학교 졸업, 취업까지 정신없이 지나간 한 해인것같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발에 있어서도, 그리고 나 자신에게 있어서도 올 한 해는 분명 많은 성장을 이룬 시기였다. 이 글에서는 먼저 개발자로서의 나에 대한 회고부터 시작해보려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프론트엔드 개발을 시작하며&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 회사에 들어가서 정말 좋은 분들을 만나 기술적으로 많이 배웠다. 아키텍처의 설계부터 다양한 디자인 패턴을 사용해보며, 이론적으로만 알고있었던 내용이 어떻게 사용되는지, 왜 쓰여야 하는지에 대한 생각을 많이 해볼 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 &amp;lsquo;작동하는&amp;rsquo; 코드를 작성하는 것이 아닌 전체 구조의 흐름을 고려하며 코드를 설계하는 경험을 하게 되었다. 이전까지는 기능 구현에 집중하며 &amp;ldquo;어떻게 동작하게 만들지&amp;rdquo;에 초점을 맞췄다면, 구조를 먼저 정리하고 나니 기능은 그 흐름 안에서 자연스럽게 따라왔다. 추가로 기능 구현에 급급했을 때보다 버그의 발생도 현저히 줄었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 잘 짜여진 프로젝트안에서는 안정성이 따라왔다. 내가 어떻게 해야할지 모르겠으면, 이전에 작성된 비슷한 코드 스타일을 참고하며 최대한 이해하려고 노력했다. AI가 잘 되어있어 많이 물어보며 공부했다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 모든 프로젝트가 항상 이렇게 잘 정리된 구조를 갖추고 있는 것은 아니었다. 이후에는 혼자서 프론트를 전담해야 하는 프로젝트를 맡게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 프로젝트는 기획의 유동이 많았고, 나는 그걸 다 맞출 능력이 부족했다. 그 때는 앞서 말했던 것과 같이 기능 구현에 급급해 거의 AI가 코드를 짜면 나는 감독만 하는 지경에 이르렀다. 그러다보니 다른 기능이 추가되어야 할 때 나조차 어느 부분을 수정해야 할지 몰랐고, 이대로는 안된다는 생각이 강하게 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 잠깐의 여유가 생겼을 때 코드 전체를 갈아엎기로 했다. 모든 구조는 내가 직접 짜고 흐름을 분석했다. 정말 힘들었지만 그래도 완성되니 전보다는 확실히 내 코드가 된 것 같아 뿌듯했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이후에도 버그는 발생했다. 하지만 이전과 달랐던 점은, 어느 시점에서 어떤 선택 때문에 문제가 발생했는지 스스로 설명할 수 있었다는 것이다. 그 사실만으로도 개발에 대한 부담은 이전보다 한결 가벼워졌다.&lt;/p&gt;
&lt;p data-end=&quot;737&quot; data-start=&quot;654&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;737&quot; data-start=&quot;654&quot; data-ke-size=&quot;size16&quot;&gt;이후 동일한 프로젝트에서 새로운 서버가 추가되며, 프론트엔드 역시 다시 한 번 처음부터 구성해야 하는 상황을 맞이했다. 이미 두 번의 경험이 있었기에, 이번에는 일주일 만에 두 번째 만들었을 때보다도 훨씬 더 자연스럽고 단단한 코드를 만들 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;737&quot; data-start=&quot;654&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;737&quot; data-start=&quot;654&quot; data-ke-size=&quot;size16&quot;&gt;&quot;헤맨만큼 내땅이다.&quot; 라는 책이 최근 인기인것 같다. 이 프로젝트를 진행하며 의미 없는 경험은 없다는 것, 그리고 헤맨만큼 내 땅이라는 것을 다시 한 번 실감했다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-end=&quot;737&quot; data-start=&quot;654&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 진짜 '나의' 회고를 시작해보려 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 올 한해동안 경제와 제테크 공부를 시작했다. 당장 눈앞에 기회가 없더라도, 언젠가 기회가 찾아왔을 때 그게 기회인지도 모르고 그저 흘려보내는 일은 없도록 미리 대비해 놓으려 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통장도 종류별로 나눠놓았고, 공모주도 하고 있는데 이게 나름 재밌다. ㅋㅋ 아무튼 돈을 많이 벌어서 40대부터는 노동 소득 없이 살아갈 수 있도록 열심히 기반을 만들어 놓으려 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 취업하며 한동안 잊고 살았던 운동도 다시 시작했다. 고작 몇개월 안했다고 다시 하려니 너무 힘들지만 그래도 습관이 될 때 까지는 꾸준히 다니려 한다. 요새 운동도 안하고 맨날 컴퓨터 앞에 이러고 앉아있으니 허리도 아프고 그냥 기린이 되어가는 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tys9x/dJMcabwfBUT/KBBMUhsMKSdb7cQYOrurgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tys9x/dJMcabwfBUT/KBBMUhsMKSdb7cQYOrurgK/img.png&quot; data-alt=&quot;회사에서 나&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tys9x/dJMcabwfBUT/KBBMUhsMKSdb7cQYOrurgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftys9x%2FdJMcabwfBUT%2FKBBMUhsMKSdb7cQYOrurgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;302&quot; height=&quot;167&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;167&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;회사에서 나&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 몸이 망가지기 전에 빨리 다시 운동 시작하자는 생각으로 냅다 헬스장 쉬원하게 긁었다. 근데 너무 비싸서 3개월 할부로 긁었다;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 행복은 생각에 달려있다는 말이 있듯이 올해 가장 큰 목표는&lt;b&gt; '긍정적으로 살기'&lt;/b&gt; 이다. 뭐 여러가지가 더 있긴 하지만 이게 가장 큰 목표다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요새 좀 지친건지 사람들 대하는게 쉽지가 않다. 예전에는 나름 친구도 많이 사귀고 그랬던것 같은데 요새는 별로 남일에 관심이 안생기는 것 같다. 나도 여유가 없어서 그런가 아무튼 그렇다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 그런가 사람들이랑 별로 대화도 안하고 그러다보니 점점 히키코모리가 되어가는 것 같다. 이제 사회성을 다시 찾아야지;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 2025년은 나 자신한테는 딱히 좋은 변화가 있었던 해는 아닌것 같다. 좋은 개발자가 되기 위해 에너지를 쏟다보니 정작 나한테 시간을 쏟기는 부족했다. 그래도 그것조차 내 선택이였으니 딱히 후회는 없다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해 목표는 한달에 한 권 책 읽기, 겨울이 끝나기 전에 스키장 가기, 새로운 취미 만들기, 사이드 프로젝트 일단 이렇게 정했다. 사실 사이드 프로젝트로 돈을 버는게 목표다. ㅋㅋ 요새 앱인토스에 관심이 생겨 좀 찾아보려고 한다. 내가 만든것도 대박나서 월 1억 찍었으면 좋겠다 ㅋㅎ&amp;nbsp;&lt;/p&gt;</description>
      <category>etc/이것저것</category>
      <category>2025 회고</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/175</guid>
      <comments>https://re-hwi.tistory.com/175#entry175comment</comments>
      <pubDate>Thu, 29 Jan 2026 01:08:20 +0900</pubDate>
    </item>
    <item>
      <title>FEConf 2025 후기</title>
      <link>https://re-hwi.tistory.com/174</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;서론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음으로 컨퍼런스에 다녀왔다. 프론트엔드 개발 위주의 컨퍼런스라 기대가 컸고, 오랜만에 보는 사람들과 함께 가서 그런지 더 재미있게 즐길 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2025-08-31-23-10-14.jpeg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYwysM/btsQfjIdIC6/u1QriTWRvXpU4kUP8jccxK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYwysM/btsQfjIdIC6/u1QriTWRvXpU4kUP8jccxK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYwysM/btsQfjIdIC6/u1QriTWRvXpU4kUP8jccxK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYwysM%2FbtsQfjIdIC6%2Fu1QriTWRvXpU4kUP8jccxK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;597&quot; height=&quot;448&quot; data-filename=&quot;KakaoTalk_Photo_2025-08-31-23-10-14.jpeg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;331&quot; data-start=&quot;165&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;331&quot; data-start=&quot;165&quot; data-ke-size=&quot;size16&quot;&gt;도착해보니 내가 듣고 싶었던 세션은 거의 끝나가고 있었지만, 다행히 다른 세션들이 아직 많이 남아 있었고 유튜브에 올라오기도 하니 바로 다음에 무엇을 들을지 고민하며 내부를 둘러봤다.&lt;/p&gt;
&lt;p data-end=&quot;331&quot; data-start=&quot;165&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;331&quot; data-start=&quot;165&quot; data-ke-size=&quot;size16&quot;&gt;홀은 A, B, C 세 개로 나뉘어 있었고, C홀은 2시까지 골드 티켓 전용 리더 토크가 진행되고 있었다.&lt;/p&gt;
&lt;p data-end=&quot;515&quot; data-start=&quot;333&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;515&quot; data-start=&quot;333&quot; data-ke-size=&quot;size16&quot;&gt;우선 홀 외부의 네트워킹 공간부터 구경했는데, 여러 사람들이 당근을 이용한 온/오프라인 네트워킹을 하고 있었다. 나는 같이 온 형이랑 친구랑만 돌아다녀서 다른 사람들과의 교류는 없었지만, 재밌는 경험이 될 것 같았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;515&quot; data-start=&quot;333&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;515&quot; data-start=&quot;333&quot; data-ke-size=&quot;size16&quot;&gt;C홀 입구에서는 부스도 있었다. 부스는 오늘의 집, moyo, 강남언니, AWS 이렇게 네 개의 부스가 있었고 나는 경품 받으러 여기 저기 다 돌아다녔다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;세션 정보&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부스를 구경하고 나서 본격적으로 세션을 듣기 시작했다. 먼저 시간대가 맞아 웹 접근성에 대한 이야기를 들었다. 멋사에서 접근성에 대한 이야기를 많이 들어 배웠던 내용이 나오는 것도 반가웠고, 강연을 하는 사람이 대학생이라고 하시던데 어린 나이에 되게 멋있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2025-08-31-22-21-40.jpeg&quot; data-origin-width=&quot;5712&quot; data-origin-height=&quot;4284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d2Af8B/btsQdc4WkGN/bavxiVnnL07k1snKBWMld1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d2Af8B/btsQdc4WkGN/bavxiVnnL07k1snKBWMld1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d2Af8B/btsQdc4WkGN/bavxiVnnL07k1snKBWMld1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd2Af8B%2FbtsQdc4WkGN%2FbavxiVnnL07k1snKBWMld1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;656&quot; height=&quot;492&quot; data-filename=&quot;KakaoTalk_Photo_2025-08-31-22-21-40.jpeg&quot; data-origin-width=&quot;5712&quot; data-origin-height=&quot;4284&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1년에 10억원을 절약한, 강남언니의 SEO 웹 전략 소개&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세션에서는 기술적인 개선이 실질적인 성과로 전환이 되기까지의 과정을 소개했다. 웹사이트에서 전환까지의 전체 여정을 &lt;b&gt;노출 &amp;gt; 유입 &amp;gt; 전환 &lt;/b&gt;으로 설명해주었는데&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;노출량 증가&lt;/b&gt;: LCP 최적화, 크롤링 버짓&lt;/li&gt;
&lt;li&gt;&lt;b&gt;검색페이지에서 전환율 증가&lt;/b&gt;: 메타태그, 시멘틱 마크업&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에 쿠팡에서 온라인 판매를 했던 경험이 떠올랐다. 당시에도 사용자들에게 얼마나 노출되는지, 그 노출이 실제 클릭으로 이어지는지, 클릭 후 얼마나 머무는지, 최종적으로 전환율이 어떻게 되는지를 계속해서 고민해 왔었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이번 세션을 통해 그때의 경험이 웹사이트라는 거대한 생태계 안에서도 동일하게 적용된다는 것을 깨달았다. 결국 사용자 여정의 각 단계를 데이터 기반으로 분석하고 개선하는 것이 핵심이었다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;프론트엔드에서 1000만개 데이터를 실시간으로 처리하라고요?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세션에서는 GPGPU를 활용한 대용량 데이터 처리 방법을 소개했다. 사실 현재 진행 중인 프로젝트에 적용해보고 싶어서 들었던 세션인데, 당장 구현하기에는 시간적, 기술적 리소스가 부족해 아쉽게도 적용해보지는 못할 것 같다. 하지만 이런 접근 방법도 있다는 사실을 알게 된 것만으로도 의미 있는 세션이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 발표를 들었지만 그 중에서 가장 기억에 남는 말이 있었다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;나의 성장은 다른 사람의 조언에서 나오는게 아닌 지금 문제를 풀고 회고하는 과정에서 나온다&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말이 특히 와닿았다. 최근 기술적으로 뛰어난 상사를 만나면서 '이 사람만 따라가면 되겠다'는 생각을 가지고 있었는데, 결국 내 성장의 주체는 나 자신이라는 당연한 사실을 잊고 지냈던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마치며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 들어 너무 나약해진 나에게 다시 열심히 해야겠다는 계기가 되었다. 기대 이상으로 재미있었고 다음에도 또 참여해보고 싶다.&amp;nbsp;&lt;/p&gt;</description>
      <category>etc/이것저것</category>
      <category>2025</category>
      <category>FEConf</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/174</guid>
      <comments>https://re-hwi.tistory.com/174#entry174comment</comments>
      <pubDate>Sun, 31 Aug 2025 23:21:40 +0900</pubDate>
    </item>
    <item>
      <title>[디자인 패턴] 커맨드 패턴 알아보기</title>
      <link>https://re-hwi.tistory.com/173</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 컴포넌트를 리팩토링하는 작업을 맡게 되었다. 이 컴포넌트는 드롭다운을 열거나 닫고, 투파트 버튼을 눌러 특정 동작을 실행하는 등 다양한 UI 액션을 담당하고 있었으며, 모든 동작은 외부에서 주입받은 &quot;커맨드 객체&quot;를 통해 이루어지는 구조였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 코드를 봤을 때는 이해조차 어려웠었다. 라인수도 많기도 했지만 처음 보는 구조였기에 이해하는데 고생을 했던 기억이 있다. 그래도 이번 업무를 진행하며 커맨드 버튼이 어느 시점에서 사용되는지 어떤 방법으로 구현되는지 조금은 알 것 같다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h2 data-end=&quot;113&quot; data-start=&quot;98&quot; data-ke-size=&quot;size26&quot;&gt;커맨드 패턴이란?&lt;/h2&gt;
&lt;p data-end=&quot;238&quot; data-start=&quot;115&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;커맨드 패턴(Command Pattern)&lt;/b&gt; 은 &lt;b&gt;요청(명령)&lt;/b&gt; 을 &lt;b&gt;하나의 객체(Command 객체)&lt;/b&gt; 로 캡슐화해서, 실행자(Invoker)와 수신자(Receiver)를 &lt;b&gt;분리&lt;/b&gt;하는 디자인 패턴이다.&lt;/p&gt;
&lt;blockquote data-end=&quot;280&quot; data-start=&quot;240&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;280&quot; data-start=&quot;242&quot; data-ke-size=&quot;size16&quot;&gt;즉, &amp;ldquo;무엇을 실행할지&amp;rdquo;와 &amp;ldquo;어떻게 실행할지&amp;rdquo;를 분리해주는 구조&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 커맨드 패턴을 왜 써야하는지 부터 생각해보자. 우선 가장 큰 문제점은 요청을 실행하는 &lt;b&gt;실행부와, 처리하는 부분이 강하게 결합&lt;/b&gt;되어있다는 점이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 어떤 버튼 컴포넌트를 만들었다고 가정하자. 그 버튼은 엄청나게 많은 기능을 가질 수 있으며, 상황에 따라 ctrl + z 와 같은 undo 기능이 있어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;왜 커맨드 패턴을 사용해야하는가?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 지금까지 했던 방식은 버튼은 이벤트를 받을 수 있도록 설계하고, 그 이벤트가 발생하는 시점에 함수를 구현했었다. 그렇다면 ToolHolder 라는 컴포넌트에 다음과 같은 양의 이벤트가 필요하다고 가정한다면 이 모든 이벤트를 툴 바에서 구현해야한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2514&quot; data-origin-height=&quot;270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N0wFv/btsQc28EMp2/XcXcvEnZ8rj5iyCS3IcsLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N0wFv/btsQc28EMp2/XcXcvEnZ8rj5iyCS3IcsLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N0wFv/btsQc28EMp2/XcXcvEnZ8rj5iyCS3IcsLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN0wFv%2FbtsQc28EMp2%2FXcXcvEnZ8rj5iyCS3IcsLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2514&quot; height=&quot;270&quot; data-origin-width=&quot;2514&quot; data-origin-height=&quot;270&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적어도 10가지가 넘는 핸들러를 하나의 컴포넌트 내에서 선언해야하고, 이 명령들은 컴포넌트 레이어에 존재한다. 그럼 클라이언트 레이어에서는 해당 기능을&lt;b&gt; &quot;반드시&quot;&lt;/b&gt; 가지고 있어야 한다. 이 부분에서 첫 번째 문제점이 발생한다. 클라이언트에서는 해당 명령들을 가질 필요도 없으며 가져서도 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 어떤 기능이 바뀌어야 한다면? 컴포넌트 단에서 툴바 컴포넌트의 명령을 바꾸면 된다. 그럼 그에 따른 사&lt;b&gt;이드 이펙트는 고스란히 클라이언트 단으로 올라온다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;두 번째는 undo와 redo와 같은 기능을 구현하기 매우 쉽다. &lt;/b&gt;명령을 분리했으니 관리하기도 한결 편해진다. 명령끼리 모아놨으니 여러 명령을 순차적으로 실행하는 로직을 만들 수도 있고, 큐에 담아서 명령을 undo, redo 혹은 사용자 이벤트의 로깅도 남길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 나도 완벽하게 커맨드 패턴을 이해 한 것 같지는 않다. 단순히 &quot;&lt;b&gt;명령을 모아 두고 사용한다&lt;/b&gt;&quot; 정도는 알겠는데 그에 따른 여러 사용성이 어떤 것 들이 있을지는 직접 겪어보기 전까지는 잘 모르겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 부족한 것 같지만 아무튼 뭐 거의 두달만에 블로그 쓰기 완!&lt;/p&gt;</description>
      <category>개발</category>
      <category>Command</category>
      <category>디자인패턴</category>
      <category>커맨드 패턴</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/173</guid>
      <comments>https://re-hwi.tistory.com/173#entry173comment</comments>
      <pubDate>Sun, 31 Aug 2025 21:38:46 +0900</pubDate>
    </item>
    <item>
      <title>[디자인 패턴] 디자인 패턴 시작</title>
      <link>https://re-hwi.tistory.com/172</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;최근 디자인 패턴에 대해 공부를 시작했다. 정처기를 공부하며 디자인 패턴의 종류나, 특징이 되는 키워드는 알고 있었지만 막상 코드에 적용하려니 어떻게 해야할지 감이 잡히지 않기도 하고 잊어버린 부분도 많은 것 같아 다시 짚고 넘어가려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;디자인 패턴이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;소프트웨어 개발에서 자주 발생하는 문제들을 해결하기 위해 검증된 설계 방식이나 구조&lt;/b&gt;를 말한다. 쉽게 보자면 템플릿과 같이 자주 사용되는 기능을 검증된 방식으로 빠르게 해결하기 위해 사용한다고 볼 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그중에서도 가장 널리 알려진 GoF 디자인 패턴이 존재한다. GoF 디자인 패턴은 4명의 저자가 쓴 디자인 패턴 책에 나오는 23가지 패턴들을 말하는데 이 4명을 GoF (Gang of Four) 라고 부른다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GoF 디자인 패턴은 객체지향 프로그램밍의 정석과도 같은 패턴들을 소개하고 있으며 그중에는 다음과 같은 패턴들이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;생성 패턴&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 생성 방식에 대한 패턴&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.9535%;&quot;&gt;싱글턴 패턴&lt;/td&gt;
&lt;td style=&quot;width: 33.4883%;&quot;&gt;오직 하나의 인스턴스만 존재하게 함&lt;/td&gt;
&lt;td style=&quot;width: 33.4883%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.9535%;&quot;&gt;팩토리 메서드 패턴&lt;/td&gt;
&lt;td style=&quot;width: 33.4883%;&quot;&gt;객체 생성 과정을 하위 클래스에 맡김&lt;/td&gt;
&lt;td style=&quot;width: 33.4883%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.9535%;&quot;&gt;추상 팩토리 패턴&lt;/td&gt;
&lt;td style=&quot;width: 33.4883%;&quot;&gt;관련 객체들을 묶어서 생성하는 인터페이스 제공&lt;/td&gt;
&lt;td style=&quot;width: 33.4883%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.9535%;&quot;&gt;빌더 패턴&lt;/td&gt;
&lt;td style=&quot;width: 33.4883%;&quot;&gt;복잡한 객체를 단계적으로 생성&lt;/td&gt;
&lt;td style=&quot;width: 33.4883%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.9535%;&quot;&gt;프로토타입 패턴&lt;/td&gt;
&lt;td style=&quot;width: 33.4883%;&quot;&gt;기존 객체를 복제하여 새로운 객체 생성&lt;/td&gt;
&lt;td style=&quot;width: 33.4883%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;구조 패턴&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스나 객체의 구조를 변경하거나 다른 구조와 조합하는 패턴&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.6744%;&quot;&gt;어댑터 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%;&quot;&gt;서로 다른 인터페이스를 호환시킴&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.6744%;&quot;&gt;브리지 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%;&quot;&gt;구현부와 추상화를 분리하여 독립적으로 확장&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.6744%;&quot;&gt;컴포지트 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%;&quot;&gt;트리 구조로 객체를 구성, 부분-전체를 동일하게 다룸&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.6744%;&quot;&gt;데코레이터 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%;&quot;&gt;객체에 기능을 동적으로 추가&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.6744%;&quot;&gt;퍼사드 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%;&quot;&gt;복잡한 시스템을 단순화된 인터페이스로 감쌈&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.6744%;&quot;&gt;플라이웨이트 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%;&quot;&gt;공유 객체를 사용해 메모리 사용 최소화&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.6744%;&quot;&gt;프록시 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%;&quot;&gt;다른 객체에 대한 접근을 제어하는 대리자 역할&lt;/td&gt;
&lt;td style=&quot;width: 38.6046%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;행위 패턴&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 간의 상호작용 패턴&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 209px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 23.1396%; height: 19px;&quot;&gt;옵서버 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 19px;&quot;&gt;변화가 생기면 관련 객체에 알림을 전달&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 23.1396%; height: 19px;&quot;&gt;전략 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 19px;&quot;&gt;알고리즘을 캡슐화해 교체 가능하게 함&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 23.1396%; height: 19px;&quot;&gt;커맨드 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 19px;&quot;&gt;명령을 객체로 만들어 실행/취소/저장 가능&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 23.1396%; height: 19px;&quot;&gt;이터레이터 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 19px;&quot;&gt;컬렉션 내부 구조를 노출하지 않고 순회&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 23.1396%; height: 19px;&quot;&gt;템플릿 메서드 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 19px;&quot;&gt;알고리즘 구조를 정의하고, 세부는 서브클래스에 위임&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 23.1396%; height: 19px;&quot;&gt;상태 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 19px;&quot;&gt;객체 상태에 따라 행동을 변경&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 23.1396%; height: 19px;&quot;&gt;미디에이터(중재자) 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 19px;&quot;&gt;객체들 간 복잡한 통신을 중재자가 처리&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 23.1396%; height: 19px;&quot;&gt;메멘토(기념물) 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 19px;&quot;&gt;객체 상태를 저장하고 복원&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 23.1396%; height: 19px;&quot;&gt;책임 연쇄 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 19px;&quot;&gt;여러 객체가 순차적으로 요청을 처리&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 23.1396%; height: 19px;&quot;&gt;방문자 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 19px;&quot;&gt;구조는 유지하고 기능(연산)을 외부 객체로 분리&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 23.1396%; height: 19px;&quot;&gt;인터프리터 패턴&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%; height: 19px;&quot;&gt;문법 규칙을 클래스로 표현하고 해석 기능 제공&lt;/td&gt;
&lt;td style=&quot;width: 38.3721%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로는 적어도 이틀에 하나씩, 각 디자인 패턴에 대해 조금 더 깊이 있게 정리해보려 한다. 각 패턴의 개념부터 시작해, 언제 사용하면 좋은지, 장단점은 무엇인지, 그리고 간단한 코드 예제와 함께 설명할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;348&quot; data-start=&quot;250&quot; data-ke-size=&quot;size16&quot;&gt;디자인 패턴은 단순히 암기하는 것이 아니라, 상황에 따라 적절하게 선택하고 응용하는 감각이 중요하다고 생각한다. 그래서 이번 디자인 패턴을 공부하며 각각의 패턴을 이해해 보려 한다.&amp;nbsp;&lt;/p&gt;</description>
      <category>개발</category>
      <category>gof 디자인패턴</category>
      <category>디자인패턴</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/172</guid>
      <comments>https://re-hwi.tistory.com/172#entry172comment</comments>
      <pubDate>Sun, 8 Jun 2025 23:50:24 +0900</pubDate>
    </item>
    <item>
      <title>Next.js에서 useParams 사용법과 클라이언트 컴포넌트</title>
      <link>https://re-hwi.tistory.com/171</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;서론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이드 프로젝트를 진행하며 '동적 매개변수'를 가져와 사용해야 하는 일이 생겼다. 처음에는 useParams를 사용해 값을 가져왔지만 빌드를 해보니 에러가 엄청나게 떠 있었다. 에러의 이유는 useParams를 서버 컴포넌트인 page.tsx에서 사용했던 것이 문제였다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h3 data-end=&quot;457&quot; data-start=&quot;438&quot; data-ke-size=&quot;size23&quot;&gt;useParams란?&lt;/h3&gt;
&lt;p data-end=&quot;539&quot; data-start=&quot;459&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span class=&quot;note&quot;&gt;useParams&lt;/span&gt;는 현재 페이지의 URL 파라미터 값을 가져오는 Next.js의 훅이다. 예를 들어 아래와 같은 URL 경로가 있다면 &lt;span class=&quot;note&quot;&gt;[locale]&lt;/span&gt;는 동적 세그먼트이다. useParams는 이 값을 가져오는 훅이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1746201127571&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/app/[locale]/page.tsx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useParams는 런타임에서 URL 값을 읽기 때문에 서버 컴포넌트에서 사용한다면 브라우저의 현재 URL 정보를 직접 읽을 수 없다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;클라이언트 컴포넌트와 서버 컴포넌트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js App Router 에서는 서버 컴포넌트와 클라이언트 컴포넌트가 존재한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Next.js 는 기본적으로 SSR을 사용한다. SSR은 서버 사이드 렌더링 즉, 서버에서 데이터를 가져오는 방법을 사용한다. 이 방법은 SPA와 반대되는 개념으로 보안성 용이, 높은 SEO, 빠른 초기 렌더링 속도 등 다양한 이점을 가지고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이로 인한 문제점도 발생한다. 서버에서 데이터를 불러오면 유저의 이벤트에 반응 할 수 없다는 점이다. Next.js 에서는 이런 문제점을 클라이언트 컴포넌트를 사용해 해결했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트 컴포넌트는 유저의 이벤트, 브라우저의 훅 모두 사용이 가능하다. 따라서 next.js를 사용할 때에는 컴포넌트 분리 방법을 더욱 신경써서 해야할 듯 하다.&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 102px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;서버 컴포넌트&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;클라이언트 컴포넌트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;실행 위치&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;서버(Node.js)&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;브라우저&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;사용 목적&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;데이터 fetch, SEO 최적화, 초기 렌더링&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;상태 관리, 이벤트 처리, 브라우저 API 사용 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;선언방식&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;별도 선언 없이 기본&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;파일 상단에 'use client' 명시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;예시 기능&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;DB 쿼리, API fetch&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;useState, useEffect, useParams, 클릭 이벤트 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;브라우저 전용 훅 사용&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;x&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;o&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;에러 원인  &lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span class=&quot;note&quot;&gt;useParams&lt;/span&gt;는 브라우저에서 동작하는 훅이다. 따라서 서버에서는 해당 값을 알 수가 없다. 따라서 useParams를 이용해 받은 값은 &lt;span class=&quot;note&quot;&gt;undefined&lt;/span&gt;가 될 것이고 이로 인한 에러가 발생했었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해결 방법 ✅&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useParams를 사용하는 부분만 따로 클라이언트 컴포넌트로 분리하면 된다. 그 후 page.tsx에서는 이 클라이언트 컴포넌트를 &lt;span class=&quot;note&quot;&gt;&amp;lt;Suspense&amp;gt;&lt;/span&gt;로 감싸서 불러오면 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end</category>
      <category>nextJS</category>
      <category>useParams</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/171</guid>
      <comments>https://re-hwi.tistory.com/171#entry171comment</comments>
      <pubDate>Sat, 3 May 2025 01:08:58 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘] 2차원 배열 회전</title>
      <link>https://re-hwi.tistory.com/170</link>
      <description>&lt;h3 data-end=&quot;453&quot; data-start=&quot;429&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Rotate Matrix&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;559&quot; data-start=&quot;455&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Problem:&lt;/b&gt;&lt;br /&gt;Given a 2D array (matrix) of n &amp;times; n elements, rotate the matrix by 90 degrees clockwise.&lt;/p&gt;
&lt;p data-end=&quot;584&quot; data-start=&quot;561&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Example:&lt;/b&gt;&lt;br /&gt;Input:&lt;/p&gt;
&lt;pre id=&quot;code_1745907155215&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Output:&lt;/p&gt;
&lt;pre id=&quot;code_1745907178249&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[
  [7, 4, 1],
  [8, 5, 2],
  [9, 6, 3]
]&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해설&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최상단에 있는 1,2,3 은 배열의 가장 끝번호로 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열로 보면 arr[0][0], arr[0][1], arr[0][2] ➡️ arr[0][끝], arr[1][끝], arr[2][끝] ... 와 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 문장은 for 문을 통해 arr[i][j] ➡️ arr[j][N - 1 - i] 이 될 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;행의 값은 열로 옮겨지고, 열의 인덱스는 회전 방향에 따라 반대로 뒤집히는 구조&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;124&quot; data-start=&quot;105&quot; data-ke-size=&quot;size26&quot;&gt;왜 N - 1 - i일까?&lt;/h2&gt;
&lt;p data-end=&quot;177&quot; data-start=&quot;126&quot; data-ke-size=&quot;size16&quot;&gt;2차원 배열을 시계 방향으로 90도 회전할 때 가장 핵심이 되는 수식은 다음과 같다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1745908479702&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rotateMatrix[j][N - 1 - i] = arr[i][j];&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시계 방향으로 90도 회전한다는 건 다음과 같은 구조 변화를 의미한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;846&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbvNdc/btsNFatcBrQ/RGjUAbaYiMuWrt008uOZvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbvNdc/btsNFatcBrQ/RGjUAbaYiMuWrt008uOZvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbvNdc/btsNFatcBrQ/RGjUAbaYiMuWrt008uOZvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbvNdc%2FbtsNFatcBrQ%2FRGjUAbaYiMuWrt008uOZvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;657&quot; height=&quot;333&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;846&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;530&quot; data-start=&quot;482&quot; data-ke-size=&quot;size16&quot;&gt;행(row)은 열(column)로,&lt;br /&gt;열은 &lt;b&gt;뒤집힌 행 인덱스&lt;/b&gt;로 이동하게 된다. (147 ➡️ 741)&lt;/p&gt;
&lt;p data-end=&quot;530&quot; data-start=&quot;482&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;530&quot; data-start=&quot;482&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 회전을 통해&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;967&quot; data-start=&quot;919&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;941&quot; data-start=&quot;919&quot;&gt;j는 새로운 행 번호가 되고,&lt;/li&gt;
&lt;li data-end=&quot;967&quot; data-start=&quot;942&quot;&gt;i는 뒤집혀서 새로운 열 번호가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1031&quot; data-start=&quot;969&quot; data-ke-size=&quot;size16&quot;&gt;여기서 뒤집힌다는 말은,&lt;br /&gt;0이 2로, 1이 1로, 2가 0으로 바뀌는 것을 말한다.&lt;/p&gt;
&lt;p data-end=&quot;1100&quot; data-start=&quot;1033&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;br /&gt;&lt;b&gt;0 &amp;rarr; 2, 1 &amp;rarr; 1, 2 &amp;rarr; 0&lt;/b&gt;&lt;br /&gt;이걸 일반화한 게 바로 &lt;span class=&quot;note&quot;&gt;N - 1 - i 이&lt;/span&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 코드는 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745907935656&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function RotateMatrix(arr) {
  const ENDPOINT = arr.length;
  let rotateMatrix = Array.from({ length: ENDPOINT }, () =&amp;gt; Array(ENDPOINT).fill(0)); // 2차 배열 선언

  for (let i = 0; i &amp;lt; arr.length; i++) {
    for (let j = 0; j &amp;lt; arr[i].length; j++) {
      rotateMatrix[j][ENDPOINT - 1 - i] = arr[i][j]; // 회전 핵심 로직 (길이 - 1- i)
    }
  }

  return rotateMatrix;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>알고리즘</category>
      <category>코딩테스트</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/170</guid>
      <comments>https://re-hwi.tistory.com/170#entry170comment</comments>
      <pubDate>Tue, 29 Apr 2025 15:40:15 +0900</pubDate>
    </item>
    <item>
      <title>[Project] tAIro 프로젝트 회고</title>
      <link>https://re-hwi.tistory.com/169</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;서론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멋사 프론트엔드 스쿨의 마지막, 파이널 프로젝트를 마쳤다. 기획부터 유지보수까지 모두 진행되는 프로젝트였기 때문에 설렘 반 긴장 반으로 시작했던 것 같다. 프로젝트의 주제는 &quot;타로를 보는 AI&quot; 로 정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로젝트를 진행하며&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기획의 중요성을 깨닫는 순간  &lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자유 주제로 프로젝트를 진행하다보니 어떤 주제로 프로젝트를 진행할지 생각해야했다. 특히 기획 단계가 가장 중요하다는 말을 많이 들어왔기에, 최대한 꼼꼼하게 준비하려고 노력했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;기획과 디자인만 꼬박 일주일 가까이 걸렸고, 개발을 할 시간이 충분할까 하는 걱정도 있었다. 나름 기획을 철저하게 잘 했다고 생각했는데 &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;막상 개발에 들어가고 보니 기획때 예상 못했던 문제들이 하나 둘 씩 나오기 시작했다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&quot;이 버튼 누르면 어떻게 되어야 하나요?&quot; &quot;운세 주제선택 어떻게 하나요?&quot; 와 같은 문제들이 계속해서 생겼고, 결국 개발을 중지하며 다시 팀원들과 이야기를 해야하는 상황이 생겼다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 이 과정을 거치며 기획을 어떻게 해야하는지, 얼마나 더 검수를 해야하는지 체감을 했던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;라이브러리 커스텀 문제  &lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 내가 담당한 부분이 아니지만 팀원들이 정말 많이 고생했던 부분이다. 우리가 직접 기능을 구현하지 않고 라이브러리를 사용한 것들이 꽤 있었다. (스와이퍼, 캘린더...) 그런데 이런 것들을 tailwind 4 를 이용해 커스텀 하려고 하니 정보가 너무 부족했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tailwind 4가 업데이트 얼마 되지 않은 시기이기도 했고, 패키지 공식문서에 tailwind로 커스텀을 어떻게 해야하는지도 나와있지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 이런 저런 방법을 시도해보다가 라이브러리 커스텀은 따로 css 파일을 만들어 디자인을 적용했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 과정들을 통해, 다양한 라이브러리를 사용하는 것이 개발 속도를 높이는 데 큰 도움이 되지만, 완벽하게 원하는 대로 커스텀하기에는 오히려 직접 구현하는 것이 더 나을 수도 있겠다는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;297&quot; data-start=&quot;123&quot; data-ke-size=&quot;size16&quot;&gt;특히, tailwind 4와 같은 최신 기술을 적용하려 할 때는 정보 부족으로 인해 예상보다 많은 시간이 소요될 수 있다는 점도 깨달았다. 라이브러리를 활용하는 것이 무조건 좋은 선택은 아니며, 프로젝트의 요구사항과 커스텀 가능성을 충분히 고려한 후 결정하는 것이 중요하다는 점을 배운 경험이었다.&lt;/p&gt;
&lt;p data-end=&quot;297&quot; data-start=&quot;123&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;297&quot; data-start=&quot;123&quot; data-ke-size=&quot;size16&quot;&gt;+ 막상 완성하고나니 맘에 들었다. 한나님 형주님 고생하셨어요 ㅎㅎ&lt;/p&gt;
&lt;p data-end=&quot;297&quot; data-start=&quot;123&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 568px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style16&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 549px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 549px;&quot; rowspan=&quot;2&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;1608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ci2CmS/btsM2hGxgLc/xY3bZEwHSfjk4q05R1iNPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ci2CmS/btsM2hGxgLc/xY3bZEwHSfjk4q05R1iNPK/img.png&quot; data-alt=&quot;스와이퍼 커스텀&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ci2CmS/btsM2hGxgLc/xY3bZEwHSfjk4q05R1iNPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fci2CmS%2FbtsM2hGxgLc%2FxY3bZEwHSfjk4q05R1iNPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;245&quot; height=&quot;530&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;1608&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스와이퍼 커스텀&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 549px;&quot; rowspan=&quot;2&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;1608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brDW9O/btsM21QK0AW/I4wDmd2noXjLEkljJ1Iadk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brDW9O/btsM21QK0AW/I4wDmd2noXjLEkljJ1Iadk/img.png&quot; data-alt=&quot;캘린더 커스텀&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brDW9O/btsM21QK0AW/I4wDmd2noXjLEkljJ1Iadk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrDW9O%2FbtsM21QK0AW%2FI4wDmd2noXjLEkljJ1Iadk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;243&quot; height=&quot;524&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;1608&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;캘린더 커스텀&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로젝트를 마치며..&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 마친 지금, 돌이켜 생각해보면 재미있게 잘 한것 같다. 팀원분들 모두 좋으신 분들이기도 했고, 사실 프로젝트 난이도가 그렇게 어렵지 않아서 일정도 무난하게 맞출 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;한 가지 아쉬운 점이 있다면, 팀원들 간 코드 리뷰를 하지 않았다는 점이다. 초기에는 바쁘다는 핑계로 PR을 보내도 거의 읽지 않고 승인했다. 이제 와서 생각해보면, 코드 리뷰를 했다면 더 좋은 코드를 작성할 수 있었을 것이고, 팀원들 모두 다른 사람의 코드를 읽어보며 실력을 키울 기회가 되었을 거라 생각한다. &lt;/span&gt;귀찮다고 미뤄둔 게 조장으로서 아쉬움이 많이 남는다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그럼에도 팀원분들이 끝까지 책임감 있게 최선을 다해 주었고, 초기 기획 내용은 모두 개발한 것 같아 만족한다. 팀원분들한테는 내가 장난도 많이 치고 자꾸 불러서 귀찮았을텐데 다 받아주셔서 너무 감사드리며 덕분에 프로젝트를 잘 마무리 할 수 있었다고 전해드리고 싶다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;마지막으로 후회없이 좋은 경험을 할 수 있도록 함께 해 주신 지명님 수진님 형주님 한나님 모두 고생 많으셨고 감사드립니다  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;1800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yI9Tn/btsM13IEW1T/zdkQmz038vtbQk3Kf3Lwek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yI9Tn/btsM13IEW1T/zdkQmz038vtbQk3Kf3Lwek/img.png&quot; data-alt=&quot;첫 스크럼&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yI9Tn/btsM13IEW1T/zdkQmz038vtbQk3Kf3Lwek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyI9Tn%2FbtsM13IEW1T%2FzdkQmz038vtbQk3Kf3Lwek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;775&quot; height=&quot;484&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;1800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;첫 스크럼&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Project</category>
      <category>멋사</category>
      <category>멋쟁이사자처럼</category>
      <category>프론트엔드스쿨</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/169</guid>
      <comments>https://re-hwi.tistory.com/169#entry169comment</comments>
      <pubDate>Sun, 30 Mar 2025 11:28:13 +0900</pubDate>
    </item>
    <item>
      <title>[PWA] React + Vite 프로젝트 PWA 적용</title>
      <link>https://re-hwi.tistory.com/168</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;PWA란 ?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span class=&quot;note&quot;&gt;PWA&lt;/span&gt;는(progressive web app)의 줄임말로서 웹과 네이티브 앱의 기능 모두 사용할 수 있도록 만든 기능이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹사이트의 장점은 빠른 접근성이다. 어플리케이션을 설치하는 것보다 더 빠르게 접근이 가능하고 링크를 통한 공유가 가능하다는 점이 웹 의 특징이다. 반면에 앱은 웹보다 더 부드러운 사용자 경험을 제공한다. 또한, 오프라인에서도 동작이 가능하며 설치가 된 이후에는 웹사이트보다 쉽게 접근이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PWA는 이 두가지 기능을 모두 통합한 웹앱이다. 즉 &lt;b&gt;웹을 앱처럼 사용할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;PWA 시작하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PWA는 &lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;manifest.json 파일과 serviceworker.js 파일만 있으면 만들 수 있다. 그러나 Vite 에서는 PWA를 쉽게 적용할 수 있는 플러그인이 있으므로 해당 플러그인을 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://vite-pwa-org.netlify.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;Vite PWA 바로가기&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 프로젝트를 만들지 않은 상태라면 해당 명령어를 입력하고 프로젝트를 이미 만든 상태에서 PWA를 적용하고 싶다면 아래에 있는 명령어를 입력하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 폴더가 없는 경우&lt;/p&gt;
&lt;pre id=&quot;code_1742048123215&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pnpm create @vite-pwa/pwa&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 프로젝트가 있는 경우&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742048018831&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pnpm add -D vite-plugin-pwa    //npm install -D vite-plugin-pwa&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 완료되었다면 vite.config.ts 에서 플러그인 등록을 해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1742048195779&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { VitePWA } from 'vite-plugin-pwa'

export default defineConfig({
  plugins: [
    VitePWA({ registerType: 'autoUpdate' })
  ]
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 부분으로 앱의 이름, 로고 등 이미지 파일을 넣을 수 있고, 위에서 언급한 매니페스트와 서비스 워커도 함께 생성된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 속성은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;172&quot; data-start=&quot;129&quot;&gt;&lt;b&gt;name / short_name&lt;/b&gt;: 앱 이름 / 짧은 이름&lt;/li&gt;
&lt;li data-end=&quot;231&quot; data-start=&quot;173&quot;&gt;&lt;b&gt;icons&lt;/b&gt;: 앱 아이콘 (여러 크기 지원, 192x192, 512x512 필수)&lt;/li&gt;
&lt;li data-end=&quot;280&quot; data-start=&quot;232&quot;&gt;&lt;b&gt;start_url&lt;/b&gt;: 앱 시작 URL (예: /index.html)&lt;/li&gt;
&lt;li data-end=&quot;361&quot; data-start=&quot;281&quot;&gt;&lt;b&gt;display&lt;/b&gt;: 앱 표시 방식 (standalone, fullscreen, minimal-ui, browser)&lt;/li&gt;
&lt;li data-end=&quot;402&quot; data-start=&quot;362&quot;&gt;&lt;b&gt;background_color&lt;/b&gt;: 초기 로딩 화면 배경색&lt;/li&gt;
&lt;li data-end=&quot;439&quot; data-start=&quot;403&quot;&gt;&lt;b&gt;theme_color&lt;/b&gt;: 상태 표시줄(탭바) 색상&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시&lt;/p&gt;
&lt;pre id=&quot;code_1742048827550&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  plugins: [
    VitePWA({
      registerType: 'autoUpdate',
      manifest: {
        name: 'tAIro - AI가 보는 타로',
        short_name: 'tAIro',
        start_url: '/',
        display: 'standalone',
        background_color: '#ffffff',
        theme_color: '#4F2D86',
        icons: [
          {
            src: 'icon/icon-192x192.png',
            sizes: '192x192',
            type: 'image/png',
          },
          {
            src: 'icon/icon-512x512.png',
            sizes: '512x512',
            type: 'image/png',
          },
        ],
      },
      devOptions: {
        enabled: true,
      },
    }),&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 devOption은 dev로 열었을 때에도 PWA를 확인할 수 있어서 넣는 편이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 프로젝트를 실행하면 URL 창에 다음과 같은 아이콘이 뜬다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpJybr/btsMMn8tGjZ/JydT0Dvt1Waes9sf9rctA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpJybr/btsMMn8tGjZ/JydT0Dvt1Waes9sf9rctA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpJybr/btsMMn8tGjZ/JydT0Dvt1Waes9sf9rctA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpJybr%2FbtsMMn8tGjZ%2FJydT0Dvt1Waes9sf9rctA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;312&quot; height=&quot;110&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;110&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 아이콘을 누르면 어플형태로 해당 사이트를 다운 받을 수 있으며 마찬가지로 휴대폰에서도 다운이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;휴대폰에서 다운 받는 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포된 url 로 이동 뒤, 홈화면에 추가 버튼을 누르면 쉽게 추가할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이폰에서는 &lt;b&gt;사파리&lt;/b&gt;로 들어가서 공유 버튼을 누르면 홈화면에 추가 버튼이 있음 (다른 브라우저에는 없음)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRmWoS/btsMK4I5Sl6/srKl4rTnS6FcLYNFvRrtzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRmWoS/btsMK4I5Sl6/srKl4rTnS6FcLYNFvRrtzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRmWoS/btsMK4I5Sl6/srKl4rTnS6FcLYNFvRrtzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRmWoS%2FbtsMK4I5Sl6%2FsrKl4rTnS6FcLYNFvRrtzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;440&quot; height=&quot;486&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적용 화면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3RGO7/btsMLwFbXVn/TsA9BJWMYQIe5iTo5DsqcK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3RGO7/btsMLwFbXVn/TsA9BJWMYQIe5iTo5DsqcK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3RGO7/btsMLwFbXVn/TsA9BJWMYQIe5iTo5DsqcK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3RGO7%2FbtsMLwFbXVn%2FTsA9BJWMYQIe5iTo5DsqcK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;203&quot; height=&quot;440&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PWA 적용이 끝났다면 FCM을 이용해 알림을 보내는 것도 가능하다. FCM 적용은 &lt;a href=&quot;https://re-hwi.tistory.com/144&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;서 확인할 수 있다.&lt;/p&gt;</description>
      <category>etc/이것저것</category>
      <category>pwa</category>
      <category>REACT</category>
      <category>ViTE</category>
      <category>vite-react</category>
      <category>프로그래시브웹앱</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/168</guid>
      <comments>https://re-hwi.tistory.com/168#entry168comment</comments>
      <pubDate>Sat, 15 Mar 2025 23:38:37 +0900</pubDate>
    </item>
    <item>
      <title>[상태관리] Zustand란?</title>
      <link>https://re-hwi.tistory.com/167</link>
      <description>&lt;p&gt;리액트는 컴포넌트 기반 아키텍처를 따르기 때문에 각각의 컴포넌트는 다른 컴포넌트의 상태에 접근할 수 없다. 하지만 부모 - 자식 관계는 props를 통해 상태를 전달할 수 있다. 리액트는 &lt;code&gt;&amp;quot;단방향 데이터 흐름&amp;quot;&lt;/code&gt; 을 따르기 때문에 부모에서 자식으로 데이터 전달이 가능하지만, 자식에서 부모로 데이터 전달은 불가능하다. &lt;/p&gt;
&lt;p&gt;사실 부모에서 콜백함수를 전달한다던지, 상태를 끌어올리는 방법을 통해 어느 정도 구현이 가능하지만 이 또한 컴포넌트가 많아진다면 &lt;code&gt;드릴링&lt;/code&gt;의 문제가 생길 수 있다. &lt;/p&gt;
&lt;p&gt;그래서 &lt;code&gt;상태관리 라이브러리&lt;/code&gt;가 탄생했다. 상태관리 라이브러리는 전역의 저장소에서 상태를 관리할 수 있도록 돕는다. 이 저장소는 어느 컴포넌트에서도 접근 및 제어가 가능하다.  &lt;/p&gt;
&lt;p&gt;그 중 대표적인 라이브러리가 바로 zustand와 redux이다. 그 중 &lt;code&gt;zustand&lt;/code&gt;는 가볍고 간결한 문법을 가지고 있어 작은 프로젝트에 사용하기 적합하다. &lt;/p&gt;
&lt;h3&gt;Zustand 시작하기&lt;/h3&gt;
&lt;h4&gt;패키지 설치&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;pnpm add zustand    # npm i zustand&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;zustand를 사용하기 위해 프로젝트에 패키지를 설치한다.&lt;/p&gt;
&lt;h4&gt;스토어 생성&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;import { create } from &amp;#39;zustand&amp;#39;;

export const useStore = create()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이렇게 creat라는 함수를 가져와 사용하면 useStore라는 저장소 훅을 만들 수 있다. &lt;/p&gt;
&lt;h4&gt;데이터 및 함수 생성&lt;/h4&gt;
&lt;p&gt;스토어를 만들었으니 전역으로 관리 될 데이터를 정의해야 한다. 그리고 그 데이터를 조작할 함수도 스토에 내부에 정의한다. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const useStore = create((set) =&amp;gt; ({
  count: 0, // count 상태의 초기값
  increment: () =&amp;gt; set((state) =&amp;gt; ({ count: state.count + 1 })), // increment(증가) 함수 정의
  decrement: () =&amp;gt; set((state) =&amp;gt; ({ count: state.count - 1 })), // decrement(감소) 함수 정의
  update: (value) =&amp;gt; set(() =&amp;gt; ({count: value}))
}));&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;필요한 컴포넌트에서 스토어 사용&lt;/h4&gt;
&lt;p&gt;데이터 꺼내오기&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { useStore } from &amp;#39;@/stores/count&amp;#39;;

function CountDisplay() {
  const count = useStore((state) =&amp;gt; state.count);

  return (
    &amp;lt;output&amp;gt;{count}&amp;lt;/output&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;함수 꺼내오기 &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { useStore } from &amp;#39;@/stores/count&amp;#39;;

function CountControls() {
  const [update, increment] = useStore(
      (state) =&amp;gt; [state.update, state.increment]
    );

  return (
    &amp;lt;div role=&amp;quot;group&amp;quot; className=&amp;quot;flex gap-1&amp;quot;&amp;gt;
      &amp;lt;button type=&amp;quot;button&amp;quot; onClick={increment}&amp;gt;+1&amp;lt;/button&amp;gt;
      &amp;lt;button type=&amp;quot;button&amp;quot; onClick={() =&amp;gt; update(99)}&amp;gt;99&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이렇게 함수를 꺼내서 사용하면 불필요한 리렌더링을 막을 수 있다.&lt;/p&gt;</description>
      <category>Front end/React</category>
      <category>Redux</category>
      <category>Zustand</category>
      <category>리액트</category>
      <category>상태</category>
      <category>상태관리</category>
      <category>슈스탄트</category>
      <category>쥬스탠드</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/167</guid>
      <comments>https://re-hwi.tistory.com/167#entry167comment</comments>
      <pubDate>Mon, 24 Feb 2025 00:14:24 +0900</pubDate>
    </item>
    <item>
      <title>React Hook 정리 (useState, useEffect)</title>
      <link>https://re-hwi.tistory.com/166</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;훅(hook) 이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 16.8버전부터 새로 도입된 개념이다. hook은 class 를 사용하지 않고도 리액트의 기능을 사용할 수 있게 도와준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 리액트의 컴포넌트는 클래스를 이용해 상태 관리를 했었지만 훅이 도입된 이후 보다 간편하게 상태 관리를 할 수 있게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;기존 리액트의 상태관리&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class Counter extends Component {
  // 상태 초기화
  state = {
    count: 0,
  };

  // 버튼 클릭 시 호출되는 핸들러 메서드
   handleClick= () =&amp;gt; {
    this.setState({ count: this.state.count + 1 }); // 상태 업데이트
  };

  render() {
    return (
      &amp;lt;div&amp;gt;
        &amp;lt;p&amp;gt;현재 카운트: {this.state.count}&amp;lt;/p&amp;gt;
        &amp;lt;button onClick={this.handleClick}&amp;gt;증가&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }
}

export default Counter;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;훅을 통한 상태관리&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function Counter() {
  const [count, setCount] = useState(0); // 상태 관리

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;현재 카운트: {count}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setCount(count + 1)}&amp;gt;증가&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default Counter;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useState 라는 훅을 이용해 복잡한 코드를 줄였고 상태 로직을 재활용하기 쉽게 만들었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;useState&lt;/h2&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt; const [state, setState] = useState(initialState)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reate 훅의 종류중 하나. 상태를 관리해 주는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용법은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;  const [변수, 변수를 바꿀 수 있는 함수] = useState(초기값);
  
  // 예시
  const [count, setCount] = useState(0);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 자리에는 상태변수가 들어간다. 해당 변수는 다음에 들어갈 함수로 접근이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로 제공된 코드를 읽어보면 count라는 변수를 0으로 초기화 한다. count를 변경하기 위해서는 setCount 라는 함수를 이용해 접근한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 setCount는 setter 역할을 하며 setCount를 사용하지 않고 count를 변경한다면 리렌더링이 되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 리액트의 상태변경은 예측 가능해야한다. 라는 상태 관리 원칙에 따라 함수를 순수하게 유지시키기 위함이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;useEffect&lt;/h2&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;useEffect(setup**,** dependencies?)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useEffect는 외부 시스템과 컴포넌트를 동기화 하는 React Hook이다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;  useEffect(() =&amp;gt; {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();      // 설정함수 (시작하자마자 또는 dependencies가 변경되었을 때 실행)
    return () =&amp;gt; {
      connection.disconnect(); // 정리함수 (사라질 때 또는 dependencies가 변경되기 직전에 실행)
    };
  }, [serverUrl, roomId]);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;매개변수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setup: Effect의 로직이 포함된 함수이다. 의존성 (위 코드에서는 serverUrl, roomId) 이 변화하면 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 함수는 선택적으로 clean up(정리) 함수를 반환할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성의 변화에 따라 컴포넌트가 리렌더링 되었을 때 이전 렌더링에 사용된 값으로 정리 함수를 실행한 후 새로운 값으로 설정 함수를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트가 DOM에서 제거 되었을 때에도 정리함수를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end/React</category>
      <category>Hook</category>
      <category>REACT</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/166</guid>
      <comments>https://re-hwi.tistory.com/166#entry166comment</comments>
      <pubDate>Tue, 21 Jan 2025 16:31:25 +0900</pubDate>
    </item>
    <item>
      <title>명령형 vs 선언형 프로그래밍의 패러다임</title>
      <link>https://re-hwi.tistory.com/165</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;서론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 리액트를 다시 공부하며 명령형과 선언형 프로그래밍에 대해서 공부했다. 이 두 가지 프로그래밍 패러다임은 코드를 작성하는 방식뿐만 아니라 문제를 해결하는 사고방식에서도 큰 차이를 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 리액트와 같은 라이브러리는 선언형 프로그래밍의 장점을 극대화하여 개발자의 생산성을 높이고, 코드의 가독성을 개선하는 데 초점을 맞춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;명령형과 선언형 프로그래밍이 구체적으로 무엇을 의미하는지, 그리고 실제로 코드에 어떻게 적용되는지 궁금해졌다. 이번 글에서는 이 두 가지 프로그래밍 패러다임의 개념과 차이점, 그리고 각각의 장단점에 대해 알아보고자 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;명령형 프로그래밍 vs 선언형 프로그래밍&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;명령형 프로그래밍은 어떤 일을 하는 방법이고 선언형은 무엇을 하는가에 더 가깝다. Tyler McGinnis&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 문장은 명령형과 선언형의 차이에 대해 설명할 때 가장 일반적으로 나오는 문장이다. 하지만 나는 이 문장으로는 이해가 되질 않아 조금 더 찾아봤는데 코드의 차이를 보는게 가장 명확하게 와닿았던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;명령형 프로그래밍&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드의 &lt;span class=&quot;note&quot;&gt;로직&lt;/span&gt;을 개발자가 직접 구현하는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span class=&quot;note&quot;&gt;무엇&lt;/span&gt;을 원하는지 개발자가 직접 로직을 구현하여 프로그래밍 하는 것이다. 예를 들어 택시를 탈 때 목적지를 말하기보단 &lt;span class=&quot;note&quot;&gt;어떻게&lt;/span&gt; 가는지를 설명하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1736915334014&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function double(arr) {
  let results = [];
  for (let i = 0; i &amp;lt; arr.length; i++) {
    results.push(arr[i] * 2);
  }
  return results;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 배열에 있는 요소에 2를 곱한 뒤 리턴하는 함수이다. 이 함수는 내부에 어떤 방법으로 결과를 리턴하는지 작성되어 있다. 이렇게 &lt;span class=&quot;note&quot;&gt;내부 동작&lt;/span&gt;에 초점을 맞추는 방식이 명령형 프로그래밍 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;선언형 프로그래밍&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드의 로직보다는 &lt;span class=&quot;note&quot;&gt;결과&lt;/span&gt;에 초점을 맞추는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 작동하는지는 모르지만 결과를 예측할 수 있도록 코드를 작성하는 방법이다. 코드는 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736916211426&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function double (arr) {
  return arr.map((item) =&amp;gt; item * 2)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 두 가지의 코드는 모두 내부 요소에 2를 곱한 뒤 리턴한다는 같은 기능을 수행한다. 하지만 명령형 프로그래밍으로 작성된 double 함수는 어떻게 2씩 곱하고 배열에 담고 리턴하는 모든 과정을 개발자가 구현해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 선언형으로 작성된 코드는 map을 이용해 간단히 같은 함수를 구현했다. map에 내부 동작이 어떻게 돌아가는지 모르더라도 해당 함수에 익숙하기만 하다면 훨씬 가독성있게 코드를 볼 수 있을 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;마무리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;명령형 프로그래밍은 문제를 해결하는 방법을 컴퓨터에게 직접 설명을 해주는 방법이고 선언형 프로그래밍은 해결방식은 컴퓨터에게 위임하는 방법이다.&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이 문장이 어떤 의미인지 헷갈렸지만 이해하고 나니 명령형과 선언형의 차이를 명확하게 설명하는 문장인 것 같다. 코드를 작성할 때 선언형 프로그래밍을 지향하라는 말이 있듯이 선언형은 명령형이 가진 많은 단점을 해결할 수 있는 방법이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 선언형 프로그래밍을 잘 사용하려면 언어가 가진 내장 함수들에 익숙해져야 장점이 극대화 되는 것 같다. 나도 선언형 프로그래밍에 익숙해지기 위해 JS 코어를 좀 더 공부해야 할 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ui.dev/imperative-vs-declarative-programming&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ui.dev/imperative-vs-declarative-programming&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736917514940&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Imperative vs Declarative Programming&quot; data-og-description=&quot;A guide to understanding the difference between Imperative and Declarative programming.&quot; data-og-host=&quot;ui.dev&quot; data-og-source-url=&quot;https://ui.dev/imperative-vs-declarative-programming&quot; data-og-url=&quot;https://ui.dev/imperative-vs-declarative-programming&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/k6Z1G/hyX4mWSgH2/00wzDOdFXFFewyatEMZnzK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/blKJZw/hyX4p0mQsy/8oyHjVWXMPa7Q48JDqRWPk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/tBECu/hyX0kGpKqu/m10EBfnMmr6eqo7dF8hBYk/img.jpg?width=400&amp;amp;height=400&amp;amp;face=128_99_285_270&quot;&gt;&lt;a href=&quot;https://ui.dev/imperative-vs-declarative-programming&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ui.dev/imperative-vs-declarative-programming&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/k6Z1G/hyX4mWSgH2/00wzDOdFXFFewyatEMZnzK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/blKJZw/hyX4p0mQsy/8oyHjVWXMPa7Q48JDqRWPk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/tBECu/hyX0kGpKqu/m10EBfnMmr6eqo7dF8hBYk/img.jpg?width=400&amp;amp;height=400&amp;amp;face=128_99_285_270');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Imperative vs Declarative Programming&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A guide to understanding the difference between Imperative and Declarative programming.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ui.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://iborymagic.tistory.com/73&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://iborymagic.tistory.com/73&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736917516903&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;명령형 vs 선언형 프로그래밍&quot; data-og-description=&quot;명령형 프로그래밍은 절차적 프로그래밍이라고도 하는데, 최근에 우테코 미션을 하다가 요런 말을 들은 적이 있다. 전반적으로, 너무 절차적 프로그래밍으로 코드를 작성하는 경향이 있습니다.&quot; data-og-host=&quot;iborymagic.tistory.com&quot; data-og-source-url=&quot;https://iborymagic.tistory.com/73&quot; data-og-url=&quot;https://iborymagic.tistory.com/73&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/T7hLB/hyX4vM2015/EGU4SNfbJv3hdDh3MaPWhK/img.jpg?width=338&amp;amp;height=338&amp;amp;face=0_0_338_338,https://scrap.kakaocdn.net/dn/gjGkf/hyX4ugiSFL/XtcBndnkX1bMQdXgi9nxv0/img.jpg?width=338&amp;amp;height=338&amp;amp;face=0_0_338_338&quot;&gt;&lt;a href=&quot;https://iborymagic.tistory.com/73&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://iborymagic.tistory.com/73&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/T7hLB/hyX4vM2015/EGU4SNfbJv3hdDh3MaPWhK/img.jpg?width=338&amp;amp;height=338&amp;amp;face=0_0_338_338,https://scrap.kakaocdn.net/dn/gjGkf/hyX4ugiSFL/XtcBndnkX1bMQdXgi9nxv0/img.jpg?width=338&amp;amp;height=338&amp;amp;face=0_0_338_338');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;명령형 vs 선언형 프로그래밍&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;명령형 프로그래밍은 절차적 프로그래밍이라고도 하는데, 최근에 우테코 미션을 하다가 요런 말을 들은 적이 있다. 전반적으로, 너무 절차적 프로그래밍으로 코드를 작성하는 경향이 있습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;iborymagic.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>etc</category>
      <category>명령형</category>
      <category>선언형</category>
      <category>프로그래밍</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/165</guid>
      <comments>https://re-hwi.tistory.com/165#entry165comment</comments>
      <pubDate>Wed, 15 Jan 2025 14:06:21 +0900</pubDate>
    </item>
    <item>
      <title>[Project] EUID 어플리케이션 프로젝트</title>
      <link>https://re-hwi.tistory.com/164</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;서론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바닐라 JS 를 다시 한 번 공부하며 프로젝트 기간을 가졌다. 처음 프로젝트를 시작하며 충분히 모든 기능을 완성할 수 있을거라 생각했지만 막상 끝내고 보니 아쉬운 점이 너무 많았다. 다음에는 이 경험을 바탕으로 조금 더 진지하게 프로젝트를 진행할 생각이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/FRONTENDBOOTCAMP-12th/Enter-8ollow/wiki&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;깃허브 바로가기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://euid-8ollow.netlify.app&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크 바로가기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그레이 블랙 심플한 마케터 포트폴리오 프레젠테이션.png&quot; data-origin-width=&quot;352&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQatzK/btsLFbAFkyy/qq64w2IIVNArgKIMnIoUB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQatzK/btsLFbAFkyy/qq64w2IIVNArgKIMnIoUB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQatzK/btsLFbAFkyy/qq64w2IIVNArgKIMnIoUB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQatzK%2FbtsLFbAFkyy%2Fqq64w2IIVNArgKIMnIoUB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;215&quot; height=&quot;264&quot; data-filename=&quot;그레이 블랙 심플한 마케터 포트폴리오 프레젠테이션.png&quot; data-origin-width=&quot;352&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;좋았던 점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 좋았던 점도 정말 많았다. 우선 문서화를 하는 방법에 대해 배웠고, 협업에 대한 어느 정도의 틀을 배운 것 같다. 그 동안은 지인들과 프로젝트를 하거나 개인 프로젝트를 하다보니 코딩 컨벤션이라던지, 진행상황을 굳이 알릴 필요 없이 작업을 했었다. 그런데 이번 프젝을 통해 협업에 대한 생각을 다시 한 번 해 볼 수 있는 기회가 되었던 것&amp;nbsp; 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 팀원간의 호흡도 좋았다. 처음 조장이라는 역할을 가지고 프로젝트를 진행 했는데 조금 부담도 있었고 걱정도 많았다. 그런데 팀원분들 모두 열심히 작업을 해 주시고 최선을 다하는게 보여 너무 고마웠다. 그 덕분에 나도 더 자극을 받을 수 있었던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술적인 면에서도 많이 배울 수 있었다. 사실 새로운 걸 배웠다기보다는 무언가를 사용하는데에 익숙해졌다는 느낌을 많이 받는다. Lit이라는 라이브러리도 처음 사용해 봤지만 금방 적응할 수 있었고, 다양한 도구를 다루는것에 대한 경계??가 사라진 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;아쉬웠던 점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉬웠던 점은 먼저 계획한 모든 기능을 만들지 못했다는 점이다. 뭔가 CRUD에 초점을 너무 맞추다보니 다른 기능을 만들진 않고 계속 게시판만 만들었다. 그래도 덕분에 CRUD는 충분히 많이 경험했던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 마이크로 애니메이션과같은 디테일 부분을 만들지 못했다. 이번에 GSAP을 이용해 애니메이션 효과를 많이 써보고 싶었는데 사용해보지 못해 너무 아쉬웠다. GSAP은 &lt;a href=&quot;https://www.inflearn.com/course/%EC%9B%B9-%EC%95%A0%EB%8B%88%EB%A7%A4%EC%9D%B4%EC%85%98-gsap-1/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;범쌤의 인프런 강의&lt;/a&gt;에서 조금 더 공부해보고 써야겠다 ㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;마무리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 느끼는거지만 이론만 배워서는 완전히 내 것으로 만들긴 어려운 것 같다. 직접 사용해봐야 그제서야 사용법을 익히고 끝냈을 때 더 뿌듯함을 많이 받는 것 같다. 아무튼 이번 프로젝트는 정말 재미있었고 나에게 큰 경험이 된 것 같다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Project</category>
      <category>euid</category>
      <category>JS 프로젝트</category>
      <category>바닐라JS</category>
      <category>사이드 프로젝트</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/164</guid>
      <comments>https://re-hwi.tistory.com/164#entry164comment</comments>
      <pubDate>Sun, 5 Jan 2025 17:23:21 +0900</pubDate>
    </item>
    <item>
      <title>2024 3회차 정보처리기사 실기 합격 후기</title>
      <link>https://re-hwi.tistory.com/163</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;정보처리 기사 실기 합격 후기 입니다. 필기는 1회에 봤는데 실기는 미루고 미루다가 3회에 보게 되었네요.   아무튼 제가 했었던 공부법과 합격 후기에 대해 작성하려 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2280&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdEL5X/btsLDWEg7RX/9cCkVF70qTlpVOHs1iTDK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdEL5X/btsLDWEg7RX/9cCkVF70qTlpVOHs1iTDK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdEL5X/btsLDWEg7RX/9cCkVF70qTlpVOHs1iTDK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdEL5X%2FbtsLDWEg7RX%2F9cCkVF70qTlpVOHs1iTDK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2280&quot; height=&quot;406&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2280&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;공부 방법 및 기간&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 필기때 공부한건 다 잊어버렸고 다시 처음부터 공부하자는 생각으로 시작했습니다. 총 공부했던 기간은 약 &lt;span class=&quot;note&quot;&gt;3주&lt;/span&gt;정도 잡았고 평일은 저녁 8시부터 10시까지 &lt;span class=&quot;note&quot;&gt;2시간&lt;/span&gt;씩, 주말에는 아침부터 저녁까지 &lt;span class=&quot;note&quot;&gt;8시간&lt;/span&gt; 정도 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 필기랑 비슷하게 문제랑 답만 외우자는 마음으로 시작했다가 기출을 보고 바로 &lt;span class=&quot;note&quot;&gt;흥달쌤&lt;/span&gt; 인강 결제 했습니다. (흥달쌤 인강 진~~~짜 추천합니다 ㅎㅎ)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 이전 회차에서 나오긴 하지만 생각보다 비율이 크지 않고 애초에 코드 문제 같은 경우는 같은 문제가 나올 수가 없기 때문에 기초를 알고 있어야 맞출 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩 문제 같은 경우에는 &lt;span class=&quot;note&quot;&gt;알고리즘&lt;/span&gt; 유형을 외우고 있는게 도움이 많이 됐습니다. 예를 들어 정렬, 최소공배수 같은 알고리즘은 구조를 보자마자 뭔지 알고 코드를 읽는것과 모르고 코드를 읽는게 차이가 컸던것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;출제 경향&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;span class=&quot;note&quot;&gt;&lt;b&gt;디자인 패턴&lt;/b&gt;&lt;/span&gt;, &lt;span class=&quot;note&quot;&gt;&lt;b&gt;서브넷 계산&lt;/b&gt;&lt;/span&gt;같은 경우는 한 두문제씩 나오는 추세이니 모두 외우고 가는게 좋습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 코드, SQL문제가 절반가까이 나오므로 비전공자분들은 꼭 코딩문제 기초라도 배우고 가야합니다. 저는 전공자긴 하지만 알고리즘 위주로 알고 있던게 도움이 많이 되었습니다. 이 부분도 모두 흥달쌤 인강에서 다루니 꼭 보고 가는거 추천합니다      &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩문제는 계속 해보는게 답인 것 같습니다. 모르는 유형이 나오더라도 코드를 읽을 줄 알면 금방 풀 수 있고, 다른 언어도 대충 비슷비슷 하니 꼭 코드에 익숙해진 뒤에 시험 보는걸 추천드립니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;자료 추천&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 계속 언급했듯 흥달쌤 인강 정말 추천드리고, 네이버에 수제비 카페에 가입해서 매일매일 데일리 문제 풀어보는 것도 추천드립니다. 여기 예상문제도 시험에 나오는 경우가 많기 때문에 조금 어렵더라도 가입해서 풀면 정말 도움 많이 됩니다. 저도 여기서 풀었던 문제 그대로 나와서 맞췄어요 크크&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 전공자 분들에게는 난이도가 그렇게 높지 않으니 기간을 짧게 잡고 하셔도 좋은 방법일 것 같습니다. 다들 합격하시길 바랍니다. 화이팅!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>etc/이것저것</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/163</guid>
      <comments>https://re-hwi.tistory.com/163#entry163comment</comments>
      <pubDate>Sat, 16 Nov 2024 20:29:09 +0900</pubDate>
    </item>
    <item>
      <title>ES6 함수의 추가 기능</title>
      <link>https://re-hwi.tistory.com/162</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 ES6 이전과 이후로 나뉜다. ES6이후 JS의 많은 부분이 바뀌었는데 &lt;span class=&quot;note&quot;&gt;함수&lt;/span&gt; 부분도 마찬가지이다. 먼저 이전의 함수는 생성자함수와 일반 함수의 구분이 없었다. 따라서 모든 함수는 일반함수로서 호출할 수 있으며 생성자 함수로서 new 키워드를 사용해 호출할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 유동성은 성능면에서도 큰 문제를 갖는다. 바로 콜백함수 또는 객체에 바인딩 된 함수 모두 &lt;span class=&quot;note&quot;&gt;cunstructor&lt;/span&gt;를 갖는다는 점이다. 이는 불필요한 프로토타입 객체를 생성하게 되며, 함수의 명확한 구분없이 사용되는 점은 코드의 가독성에도 큰 영향을 미친다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 ES6 이후 함수의 사용 목적에 따라 세가지 종류로 나누었다.&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 72px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;constructor&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;prototype&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;super&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;arguments&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;일반함수&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 17px; text-align: center;&quot;&gt;메서드&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px; text-align: center;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px; text-align: center;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px; text-align: center;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px; text-align: center;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 17px; text-align: center;&quot;&gt;화살표 함수&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px; text-align: center;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px; text-align: center;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px; text-align: center;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px; text-align: center;&quot;&gt;X&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #999999;&quot;&gt; &lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;※ 제너레이트 함수와 async함수는 제외하였습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;메서드&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span class=&quot;note&quot;&gt;메서드&lt;/span&gt;는 함수의 한 종류이다. 이전에는 &quot;메서드는 함수이다.&quot; 라고만 알고있었는데 메서드의 정확한 정의는 &quot;&lt;b&gt;ES6 사양에서 메서드 축약 표현으로 정의된 함수&lt;/b&gt;&quot;이다. 이 때 메서드는 객체에 속한 함수이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드 축약표현이란 function 키워드를 사용하지 않고 함수를 정의하는 것을 말한다. 이렇게 생성된 메서드는 인스턴스를 생성할 수 없으며 프로토타입 또한 존재하지 않는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 자신을 바인딩한 객체를 가리키는 내부슬롯 &lt;span class=&quot;note&quot;&gt;[[HomeObject]]&lt;/span&gt;를 갖는다. 따라서 super 키워드를 사용할 수 있다. 이렇게 객체 내부에서 사용되는 메서드는 본연의 기능 (super)를 추가하고 불필요한 기능 (cunstructor)를 제거했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;화살표 함수&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function 키워드 대신 =&amp;gt;를 이용하여 사용하는 함수이다. 기존에는 없었던 문법이지만 ES6 이후 생겼으며 this 바인딩을 갖지 않는다. 따라서 화살표 함수 내에서 this를 사용하면 상위스코프의 this를 참조한다. 이를 &lt;span class=&quot;note&quot;&gt;lexical this&lt;/span&gt;라고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734527586602&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 화살표 함수 정의
const add = (a,b) =&amp;gt; a + b&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Rest 파라미터&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span class=&quot;note&quot;&gt;rest 파라미터&lt;/span&gt;는 함수에 전달된 인수의 목록을 배열로 전달받는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 일반 매개변수와 함께 사용할 수 있으며 인수들은 매개변수와 rest 파라미터에 순차적으로 할당된다.&lt;/p&gt;
&lt;pre id=&quot;code_1734528361520&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// rest 파라미터

function foo(...rest){
	console.log(rest)	// [1,2,3,4,5]
}

foo(1,2,3,4,5)


// 매개변수와 함께 사용하는 rest 파라미터

function bar(param,...rest){
	console.log(param)	// 1
	console.log(rest)	// [2,3,4,5]
}

bar(1,2,3,4,5)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ES6 이전과 이후의 차이점을 공부하기 위해 작성된 글 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 단원은 함수의 기능에 초점을 맞춘게 아닌 ES6 이후 변경된 점에 초점을 맞추었기 때문에 함수의 기능을 학습하기 위해 보는것은 추천하지 않습니다.&lt;/p&gt;</description>
      <category>Front end/JavaScript</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/162</guid>
      <comments>https://re-hwi.tistory.com/162#entry162comment</comments>
      <pubDate>Fri, 15 Nov 2024 22:21:38 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 음악 플레이리스트 프로그래스바 제작 (3)</title>
      <link>https://re-hwi.tistory.com/161</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;버그를 해결했다. 원인을 찾아보니 크롬을 실행할 때 음원이 나오지 않아도 AudioContext 객체를 생성할 수 없다. 그래서 즉시 실행되던 함수를 묶어 playMusic 에서 실행하도록 바꾸었더니 해결됐다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 프로그래스 바를 만들었다. 내용을 요약하자면 오디오 timeupdate 이벤트 리스너를 이용해 내부 함수가 계속해서 실행될 수 있게 했다. 그 뒤로는 현재길이 / 전체길이 를 백분위로 환산한 뒤 프로그래스 바의 width 길이에 대입했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;재생 시간에 따른 프로그래스 바 크기 조절&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행코드&lt;/p&gt;
&lt;pre id=&quot;code_1731584961025&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  audio.addEventListener(&quot;timeupdate&quot;, (e) =&amp;gt; {
    // console.log(e);

    // 프로그래스 바 진행 리스너
    const currentTime = e.target.currentTime; // 현재 재생되는 시간
    const duration = e.target.duration; // 오디오의  총 길이
    let progressWidth = (currentTime / duration) * 100;
    progressBar.style.width = `${progressWidth}%`;
  });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;클릭 이벤트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 코드&lt;/p&gt;
&lt;pre id=&quot;code_1731585198110&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  progress.addEventListener(&quot;click&quot;, (e) =&amp;gt; {
    console.log(&quot;click&quot;);

    let progressWidth = progress.clientWidth;

    let clickedOffsetX = e.offsetX;
    let songDuration = audio.duration;

    audio.currentTime = (clickedOffsetX / progressWidth) * songDuration;
    console.log(progressWidth);
  });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;offsetX 속성은 기준이 요소이다. 따라서 클릭을 한 위치가 요소의 어느 부분인지를 반환한다. 해당 위치와 전체 위치를 이용해 노래의 어느 부분으로 이동할지 결정한다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end/JavaScript</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/161</guid>
      <comments>https://re-hwi.tistory.com/161#entry161comment</comments>
      <pubDate>Thu, 14 Nov 2024 20:54:37 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 타입별 메서드와 제너레이트</title>
      <link>https://re-hwi.tistory.com/160</link>
      <description>&lt;h1&gt;타입별 메서드&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;숫자형(number)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;e의 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자를 입력할 때 100000000과 같은 큰 수를 입력해야 한다면 0을 치는게 헷갈릴 수 있다. 따라서 e를 쓰고 그 뒤에 0의 개수를 입력하면 된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const billion = 1000000000;
const _billion = 1e9
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Math 함수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삼각함수&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Math.sin(radian) : 라디안의 사인값을 반환하는 함수이다.&lt;/li&gt;
&lt;li&gt;Math.cos(radian) : 라디안의 코사인값을 반환하는 함수이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대/최소&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Math.max( num ) : 인수들 중 가장 큰 값을 반환하는 함수이다.&lt;/li&gt;
&lt;li&gt;Math.min( num ) : 인수들 중 가장 작은값을 반환하는 함수이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문자형 (string)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자는 &lt;b&gt;Immutable (불변)&lt;/b&gt; 이다. 따라서 문자열을 수정하기 위해서는 문자를 슬라이싱해 자른 뒤 원하는 값을 이어 붙히는 형태로 사용해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;슬라이싱&lt;/h3&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;const str = '자바스크립트';

const result1 = str.substr(0, 2);

// 자바
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0번 인덱스부터 2번 인덱스 전까지 자른다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;indexof&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 문자열이 어느 위치에 있는지 확인한다. 응용하면 사용자의 브라우저를 알 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function checkBrowser() {
  const agent = window.navigator.userAgent.toLowerCase();
  let browserName;

  switch (true) {
    case agent.indexOf('edg') &amp;gt; -1:
      browserName = 'MS Edge';
      break;
    case agent.indexOf('chrome') &amp;gt; -1 &amp;amp;&amp;amp; !!window.chrome:
      browserName = 'Chrome';
      break;
    case agent.indexOf('safari') &amp;gt; -1:
      browserName = 'Apple Safari';
      break;
    case agent.indexOf('firefox') &amp;gt; -1:
      browserName = 'FireFox';
      break;
    case agent.indexOf('trident') &amp;gt; -1:
      browserName = 'IE';
      break;
    default:
      browserName = '무슨 브라우저에요..?';
  }
  return browserName;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;배열&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데큐(deque) 연산이 가능한 자료구조. 처음이나 끝에 있는 데이터를 넣거나 뺄 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;큐 (FIFO)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선입선출의 특징을 가진 자료구조. 가장 먼저 들어온 데이터가 가장 먼저 나간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열에서는 다음 메서드를 이용해 사용 가능하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;push : 배열의 끝에 값을 추가한다.&lt;/li&gt;
&lt;li&gt;shift : 배열의 앞에있는 값을 반환하면 배열에서는 그 값이 빠진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스택 (LIFO)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 마지막에 들어온 데이터가 가장 먼저 나가는 자료구조.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;push : 배열의 끝에 값을 추가한다.&lt;/li&gt;
&lt;li&gt;pop : 배열의 마지막에 있는 값을 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;제너레이트&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열과 유사하게 iterable(반복가능)하다. 특징은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지연계산&lt;/li&gt;
&lt;li&gt;무한 시퀀스생성&lt;/li&gt;
&lt;li&gt;반복된 계산 가능&lt;/li&gt;
&lt;li&gt;&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 제너레이터를 사용하는 이유를 몰랐지만 성능 측면에서 제너레이터는 매우 유용하다. 배열에 담긴 데이터는 처음부터 모든 배열의 데이터를 메모리에 올려야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 제너레이트는 하나의 데이터와 다음 데이터가 있는지만 알고있으면 된다. 이런 작동방식은 데이터의 양이 많을수록 성능을 크게 향상시킬 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비동기적 지연계산&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 반복 중 멈출 수 있다는 특징을 가지고 있다. next() 를 호출해 다음으로 이동하지 않으면 함수는 멈춰있게 되고 이로 인한 비동기처리가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 API 요청이 순차적으로 오는 상황에서 요청이 온다면 어떤 기능을 실행한다고 가정하자. 처음 요청을 실행하는 도중에는 그 다음 요청이 어떻게 올지 모른다. 따라서 데이터 처리를 할 수 없지만 제너레이트를 이용한다면 가능하다.&lt;/p&gt;
&lt;h1&gt;try - catch&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;try문에 에러가 있다면 catch가 잡는 구문. 에러처리를 어떻게 할 지 결정한다. 에러의 유무 관계없이 실행되어야 하는 구문이 있다면 finally 를 이용한다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;try {

  // 코드...

} catch (err) {

  // 에러 핸들링

} finally{

	// 항상 실행되는 문장
	
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 자바스크립트는 에러를 잘 내지 않는다. 그 예중에 하나가 객체에 없는 값을 호출한다면 에러가 나야하지만 js는 undefined를 반환한다. 이런 경우를 조심하고 try-catch를 사용해야한다.&lt;/p&gt;</description>
      <category>Front end/JavaScript</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/160</guid>
      <comments>https://re-hwi.tistory.com/160#entry160comment</comments>
      <pubDate>Wed, 13 Nov 2024 20:50:20 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 클로저 (closure)란 무엇일까</title>
      <link>https://re-hwi.tistory.com/159</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;모듈화의 핵심은 &quot;&lt;span class=&quot;note&quot;&gt;응집도&lt;/span&gt;를 높히고 &lt;span class=&quot;note&quot;&gt;결합도&lt;/span&gt;를 낮춘다&quot; 이다. 이 내용을 잘 생각하며 카운트를 증가시키는 함수를 만들어 본다면 어떻게 코드를 작성할 수 있을까&lt;/p&gt;
&lt;pre id=&quot;code_1731401717005&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let cnt = 0;

function count(){
	return cnt ++
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭐 대충 이런식으로 작성할 수 있을 것이다. 이 때 cnt 변수가 함수 외부에 선언되었다. 이렇게되면 어떤 곳에서도 접근이 가능해 위험하고, count 함수를 다른곳으로 옮겨야 할때 항상 저 cnt 변수를 같이 가지고 가야하는 문제가 생긴다. 또 다른 경우에는 코드 200줄 아래에서 누군가가 cnt라는 변수를 사용한다면 에러가 날 수도 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 cnt라는 변수를 count 함수 내부에 선언할 수만 있다면 위 문제 모두 해결된다. 그렇지만 count 함수를 호출할 때마다 cnt가 0으로 초기화되어 정확히 카운트를 할 수 없게된다. 이러한 문제점을 해결하기 위해 &lt;span class=&quot;note&quot;&gt;클로저&lt;/span&gt;가 탄생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 클로저가 이러한 문제를 해결할 수 있을까? 바로 js 에서의 함수는 &lt;span class=&quot;note&quot;&gt;일급객체&lt;/span&gt;이기 때문이다. JavaScript에서 함수는 &lt;span class=&quot;note&quot;&gt;일급 객체&lt;/span&gt;이므로 함수의 반환값으로 사용할 수 있다. 클로저는 이 특성을 이용하여 &lt;b&gt;내부 변수(cnt)를 외부에서 직접 접근하지 못하게 하면서, 함수가 외부 상태를 유지하게 만든다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 앞선 코드를 클로저를 이용해 작성해 보자&lt;/p&gt;
&lt;pre id=&quot;code_1731403633252&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function createCounter() {
    let cnt = 0;

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

const counter = createCounter();
console.log(counter()); // 0
console.log(counter()); // 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하나의 함수 내부에 카운트를 증가하는 function을 선언하여 해당 함수를 내보내 외부에서 사용할 수 있게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 count를 1까지 올린 뒤 또 다른 변수를 createCounter를 사용하여 만들어보자.&lt;/p&gt;
&lt;pre id=&quot;code_1731403814085&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function createCounter() {
    let cnt = 0;

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

const counter = createCounter();
console.log(counter()); // 0
console.log(counter()); // 1

const _counter = createCounter();
console.log(_counter());
console.log(_counter());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 새로 생긴 _counter를 사용한다면 다시 0이 출력된다. 새로운 메모리에 createCounter를 실행하기 때문에 cnt는 새로운 초기값으로할당된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저를 배우며 느낀점은 getter와 setter를 사용해서만 접근이 가능한 데이터들을 보호하는 모습이 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;약간 클래스의 private랑 비슷한 느낌인 것 같았다.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Front end/JavaScript</category>
      <category>Closure</category>
      <category>JavaScript</category>
      <category>js</category>
      <category>오블완</category>
      <category>클로저</category>
      <category>티스토리챌린지</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/159</guid>
      <comments>https://re-hwi.tistory.com/159#entry159comment</comments>
      <pubDate>Tue, 12 Nov 2024 18:42:26 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 음악 플레이리스트 (2) 사운드 스펙트럼, 에러 극복기</title>
      <link>https://re-hwi.tistory.com/158</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;분명히 어제까지 잘 되던 사이트가 오늘 아침부터 소리가 안나기 시작했다. 처음에는 단순 렉이겠거니 싶었는데 핸드폰으로 들어가도 안나오고 시간이 지나도 소리가 나지 않자 뭔가 문제가 생긴 것 같아 한참 문제를 찾았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 코드도 짧아서 에러 나올 곳도 없고 콘솔창도 깨끗하고 심지어는 가끔씩 될 때도 있었다. 그래서 대체 왜 그런가 찾아봤더니 mp3 파일의 용량 문제였다... 파일을 압축해서 다시 호스팅 하니 잘 되는 것을 볼 수 있었다. 그런데 분명히 이 글 시작할 때까지만 해도 잘 되던게 지금 또 안되기 시작했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ 한참 또 찾아보았는데 도저히 왜 안되는지 모르겠다. 이제는 파일 크기도 1.6메가로 작고 콘솔에 내가 출력하는 audio.src를 찍어봤는데 해당 경로로 들어가면 노래가 나온다. 더 짜증나는건 핸드폰으로 들어가면 노래가 나온다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ 진짜 화가나고 왜 안되는지 모르겠는데 지금 또 갑자기 된다. 근데 로컬에서만 된다 ㅋㅋ 아오!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 이 이슈는 모르겠고 오늘은 스펙트럼을 추가했다. 원래는 프로그래스바랑 리스트까지 다 만들어보려고 했는데 저 에러때문에 시간을 다 뺏겼다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;미리보기&lt;/b&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;kakaotv&quot; data-video-url=&quot;https://tv.kakao.com/v/450870246&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/QODnH/hyXwjm9kkZ/KReAziKL2lCpA4NkrqIWKk/img.jpg?width=1728&amp;amp;height=1080&amp;amp;face=0_0_1728_1080,https://scrap.kakaocdn.net/dn/s4nI1/hyXwpt8lBq/Hw1G0yJOEbn4aO1rd6U7FK/img.jpg?width=1728&amp;amp;height=1080&amp;amp;face=0_0_1728_1080&quot; data-video-width=&quot;860&quot; data-video-height=&quot;538&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;538&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;'Dvelopment blog'에서 업로드한 동영상&quot; data-video-play-service=&quot;daum_tistory&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://play-tv.kakao.com/embed/player/cliplink/450870246?service=daum_tistory&quot; width=&quot;860&quot; height=&quot;538&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 스펙트럼은 webAPI AudioContext 를 사용해서 음원의 데이터를 값으로 리턴하면 그 값대로 바의 길이를 조절한다. 값은 최대 255로 나오기 때문에 나는 바의 길이를 줄이기 위해 나누기 5 를 해주었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;requestAnimationFrame&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 애니메이션을 보다 더 부드럽고 효율적으로 실행하도록 도와주는 JavaScript 함수이다. 애니메이션 만드는 코드를 이 함수로 감싸면 자신의 컴퓨터 사양에 맞게 브라우저가 해당 메서드를 호출한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;setInterval과의 차이점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 setInterval도 애니메이션을 렌더링하는 메서드와 자주 쓰인다. 하지만 setInterval은 일정시간마다 특정 작업을 실행하는 함수이다. 즉, 렌더링만을 위한 메서드가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 requestAnimationFrame는 화면 주사율에 맞는 값을 사용할 수 있으며 애니메이션만을 위한 메서드이므로 보다 부드러운 애니메이션 작업이 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ 오류 관련 음원을 바꿀 때마다 계속해서 요청을 보낸다. 그래서 캐싱 문제로 인한 메모리 문제인가 했는데 그것도 아니다. 진짜 마지막으로 오디오 컨텍스트가 현재 즉시실행 함수안에 있어서 크롬에서 막은것 같기도 하다. 이거 안되면 그냥 때려치고 로컬에서나 써야겠다&lt;/p&gt;</description>
      <category>Front end/JavaScript</category>
      <category>js</category>
      <category>WebAPI</category>
      <category>사운드스펙트럼</category>
      <category>스펙트럼</category>
      <category>오블완</category>
      <category>음악</category>
      <category>음악플레이리스트</category>
      <category>티스토리챌린지</category>
      <category>플레이리스트</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/158</guid>
      <comments>https://re-hwi.tistory.com/158#entry158comment</comments>
      <pubDate>Mon, 11 Nov 2024 21:59:25 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 음악 스펙트럼 플레이어 만들기</title>
      <link>https://re-hwi.tistory.com/157</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 도리 노래에 빠졌는데 유튜브 플레이리스트에 도리 신곡이 없어서 내가 플레이리스트를 만들어 보았다. 처음에는 플레이리스트를 유튜브에 올려보려고 영상편집을 배워봤는데 툴을 뭐 쓸지 고민하다 결국 js로 만들게 되었다 ㅋㅋ&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://myeong-jae-hwi.github.io/music-playlist/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;미리보기&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1731244879440&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;플레이리스트&quot; data-og-description=&quot;&quot; data-og-host=&quot;myeong-jae-hwi.github.io&quot; data-og-source-url=&quot;https://myeong-jae-hwi.github.io/music_spectrum/&quot; data-og-url=&quot;https://myeong-jae-hwi.github.io/music_spectrum/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://myeong-jae-hwi.github.io/music-playlist/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://myeong-jae-hwi.github.io/music_spectrum/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;플레이리스트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;myeong-jae-hwi.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 앨범 이미지는 iTunes에서 가져올 수 있다고 해서 이미지를 다운 받았고, mp3파일은 지니에서 구매했다. 지니가 3개월동안 할인을 많이 해줘서 싸게 음악을 다운받을 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;iTunes&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://decoupled.app/itunes-artwork-finder/?term=&amp;amp;country=us&amp;amp;entity=album&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://decoupled.app/itunes-artwork-finder/?term=&amp;amp;country=us&amp;amp;entity=album&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1731245117932&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;iTunes Artwork Finder&quot; data-og-description=&quot;&quot; data-og-host=&quot;decoupled.app&quot; data-og-source-url=&quot;https://decoupled.app/itunes-artwork-finder/?term=&amp;amp;country=us&amp;amp;entity=album&quot; data-og-url=&quot;https://decoupled.app/itunes-artwork-finder/?country=us&amp;amp;entity=album&amp;amp;term=&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://decoupled.app/itunes-artwork-finder/?term=&amp;amp;country=us&amp;amp;entity=album&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://decoupled.app/itunes-artwork-finder/?term=&amp;amp;country=us&amp;amp;entity=album&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;iTunes Artwork Finder&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;decoupled.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;genie&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pay.genie.co.kr/buy/discount&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://pay.genie.co.kr/buy/discount&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1731245158892&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;음악, 그리고 설레임 - 지니&quot; data-og-description=&quot;지니캐시를 사용해보세요! 지니캐시로 다양한 서비스를 이용해보세요! 지니캐시 충전&quot; data-og-host=&quot;pay.genie.co.kr&quot; data-og-source-url=&quot;https://pay.genie.co.kr/buy/discount&quot; data-og-url=&quot;https://pay.genie.co.kr/buy/discount&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bOJ4sm/hyXwuBTuNo/MrODpFs78cgps3ryUH21XK/img.png?width=940&amp;amp;height=220&amp;amp;face=0_0_940_220&quot;&gt;&lt;a href=&quot;https://pay.genie.co.kr/buy/discount&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pay.genie.co.kr/buy/discount&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bOJ4sm/hyXwuBTuNo/MrODpFs78cgps3ryUH21XK/img.png?width=940&amp;amp;height=220&amp;amp;face=0_0_940_220');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;음악, 그리고 설레임 - 지니&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;지니캐시를 사용해보세요! 지니캐시로 다양한 서비스를 이용해보세요! 지니캐시 충전&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;pay.genie.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드는 그냥 인덱스 하나 만들어서 이전 다음 버튼을 누르면 인덱스 값이 변경된다. 인덱스에 따라 앨범 이미지, 노래가 바뀐다. 딱히 어려울게 없어서 스펙트럼을 추가 해 볼까 한다. webAPI 중에 AudioContext 라는 걸 찾아보니 오디오의 파형을 리턴해준다고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이거 이용하면 스펙트럼을 만들 수 있을 것 같아서 준비중이다. 완성되면 추가 할 예정이다. 현재까지의 코드는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1731245584946&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const MUSIC_ARR = [&quot;dori&quot;, &quot;pateco&quot;, &quot;QWER&quot;];
let isPlay = false;

// 오디오 선언
const audio = document.querySelector(&quot;.audio&quot;);
let index = 0;

// 현재 인덱스 실행
function musicStart(idx) {
  audio.src = `./assets/music/${MUSIC_ARR[idx]}.mp3`;
  audio.load();
}

// 플레이 버튼 선언
const mainBtn = document.querySelector(&quot;.mainBtn&quot;);

// 이전, 다음 버튼 선언
const prevBtn = document.querySelector(&quot;.prev&quot;);
const nextBtn = document.querySelector(&quot;.next&quot;);

// 일시정지

function pauseMusic() {
  mainBtn.classList.remove(&quot;play&quot;);
  audio.pause();
  console.log(&quot;pause&quot;);
  isPlay = false;
  mainBtn.style.backgroundImage = &quot;url(./assets/images/svg/play.svg)&quot;;
}

// 노래 시작

function playMusic() {
  mainBtn.classList.add(&quot;play&quot;);
  audio.play();
  mainBtn.style.backgroundImage = &quot;url(./assets/images/svg/pause.svg)&quot;;
  mainBtn.style.backgroundRepeat = &quot;no-repeat&quot;;
  mainBtn.style.backgroundSize = &quot;contain&quot;;
  mainBtn.style.backgroundPosition = &quot;center center&quot;;
  console.log(&quot;play&quot;);
  isPlay = true;
}

// 재생 &amp;amp; 일시정지 이벤트리스너

mainBtn.addEventListener(&quot;click&quot;, () =&amp;gt; {
  console.log(isPlay);

  //pause
  if (isPlay) {
    pauseMusic();
  }

  //play
  else {
    playMusic();
  }
});

// 음악이 끝났다면 자동 다음 곡

audio.addEventListener(&quot;ended&quot;, () =&amp;gt; {
  index += 1;
  if (index &amp;gt;= MUSIC_ARR.length) {
    index = 0;
  }
  musicStart(index);
  albumImg(index);
  playMusic();
});

// 이전 다음 인덱스 설정
nextBtn.addEventListener(&quot;click&quot;, () =&amp;gt; {
  index += 1;
  if (index &amp;gt;= MUSIC_ARR.length) {
    index = 0;
  }
  musicStart(index);
  albumImg(index);
  if (isPlay) {
    playMusic();
  }
});

prevBtn.addEventListener(&quot;click&quot;, () =&amp;gt; {
  index -= 1;
  if (index &amp;lt; 0) {
    index = MUSIC_ARR.length - 1;
  }
  musicStart(index);
  albumImg(index);
  if (isPlay) {
    playMusic();
  }
});

// 앫범 이미지 변경
const album = document.querySelector(&quot;.album&quot;);

function albumImg(idx) {
  console.log(index);

  const style = document.createElement(&quot;style&quot;);
  style.textContent = `
    body::before {
      transition: .3s;
      background-image: url('./assets/images/${MUSIC_ARR[idx]}.jpg');
    }
  `;
  document.head.appendChild(style);
  album.style.transition = &quot;.3s&quot;;
  album.style.backgroundImage = `url('./assets/images/${MUSIC_ARR[idx]}.jpg')`;
}

window.addEventListener(&quot;load&quot;, () =&amp;gt; {
  albumImg(index);

  musicStart(index);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;음악 재생&lt;/h4&gt;
&lt;pre id=&quot;code_1731245721747&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function playMusic() {
  mainBtn.classList.add(&quot;play&quot;);
  audio.play();
  mainBtn.style.backgroundImage = &quot;url(./assets/images/svg/pause.svg)&quot;;
  mainBtn.style.backgroundRepeat = &quot;no-repeat&quot;;
  mainBtn.style.backgroundSize = &quot;contain&quot;;
  mainBtn.style.backgroundPosition = &quot;center center&quot;;
  console.log(&quot;play&quot;);
  isPlay = true;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음악을 재생하는 부분이다. 스타일만 주었고 실제로 음악을 플레이하는 기능은 audio.play() 한 줄이다. isPlay 라는 변수를 두어 일시정지, 플레이를 토글할 수 있도록 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이전 곡 / 다음 곡&lt;/h4&gt;
&lt;pre id=&quot;code_1731245857062&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nextBtn.addEventListener(&quot;click&quot;, () =&amp;gt; {
  index += 1;
  if (index &amp;gt;= MUSIC_ARR.length) {
    index = 0;
  }
  musicStart(index);
  albumImg(index);
  if (isPlay) {
    playMusic();
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스를 1씩 증가 및 감소한다. 인덱스에 따라 앨범 커버와 노래를 변경한다. 만약 플레이 중 다음 버튼을 눌렀다면 다음 노래가 바로 나올 수 있도록 조건문 처리 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래스바랑 음원의 스펙트럼은 아직 못만들어서 만들어지면 포스팅 할 예정이다.&lt;/p&gt;</description>
      <category>Front end/JavaScript</category>
      <category>js</category>
      <category>오블완</category>
      <category>음악</category>
      <category>티스토리챌린지</category>
      <category>플레이리스트</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/157</guid>
      <comments>https://re-hwi.tistory.com/157#entry157comment</comments>
      <pubDate>Sun, 10 Nov 2024 22:40:17 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 가비지 컬렉션</title>
      <link>https://re-hwi.tistory.com/156</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;가비지컬렉션이란 더이상 다른 객체에 참조되지 않는 객체를 삭제하여 메모리 누수를 방지하는 메모리 관리방식이다. GC라고 불리며 메모리 관리를 직접 해야하는 C++과 다르게 가비지 컬렉터가 자동으로 메모리를 관리해준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end/JavaScript</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/156</guid>
      <comments>https://re-hwi.tistory.com/156#entry156comment</comments>
      <pubDate>Sat, 9 Nov 2024 21:28:08 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 얕은복사 vs 깊은복사 feat. 참조복사</title>
      <link>https://re-hwi.tistory.com/155</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;참조복사&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 &lt;span class=&quot;note&quot;&gt;참조 복사&lt;/span&gt;는 같은 메모리 주소를 복사한다. 즉, 원본 데이터를 참조복사한 다른 객체의 값이 바뀌게 된다면 원본에 문제가 생길 가능성이 매우 높다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dhliBP/btsKCv81Tn9/k1RATfi0hVWCbpJHJDkJuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dhliBP/btsKCv81Tn9/k1RATfi0hVWCbpJHJDkJuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dhliBP/btsKCv81Tn9/k1RATfi0hVWCbpJHJDkJuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdhliBP%2FbtsKCv81Tn9%2Fk1RATfi0hVWCbpJHJDkJuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;489&quot; height=&quot;319&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1731056128100&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let message = '문자 값은 프리미티브 데이터 타입으로 값이 복사됩니다.';
let messenger = {
  name: 'kakao talk',
  manufacture: 'kakao',
};

let text = message;
let conversationTool = messenger;

// 비교 (복사 vs. 참조)
console.log(message == text);
console.log(message === text);
console.log(messenger == conversationTool);
console.log(messenger === conversationTool);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 방식으로 복사하게 된다면 데이터의 무결성이 침해된다. 그렇다면 메모리 주소가 다른 변수에 해당 내용을 복사해서 옮기면 이런 문제가 생기지 않을까?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;얕은 복사&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 저장되는 메모리 주소가 다르면 다른 변수에서 값이 변한다고 하더라도 원본은 침해되지 않는다. 얕은 복사를 하는 방법은 다양하다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. for문을 사용한 복사&lt;/h4&gt;
&lt;pre id=&quot;code_1731057088011&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const cloneObject = {};

for (let i in messenger) {
  cloneObject[i] = messenger[i];
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. Object.assign()을 이용한 복사&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1731057159207&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const copyObject = Object.assign({}, messenger);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 전개 연산자 (...)를 사용한 복사&lt;/h4&gt;
&lt;pre id=&quot;code_1731057207407&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const spreadObject = { ...messenger };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 방법으로 얕은 복사를 할 수 있다. 하지만 깊이가 2 이상인 객체의 경우 또 다시 참조되는 문제가 발생한다. 이를 해결하기 위해&amp;nbsp;&lt;span class=&quot;note&quot;&gt;깊은 복사&lt;/span&gt;를 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;깊은복사&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깊은 복사를 구현하기 위해서는 재귀 또는 다양한 라이브러리를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 재귀를 통한 깊은 복사&lt;/h4&gt;
&lt;pre id=&quot;code_1731150440407&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function cloneDeep(object) {
  return Object.fromEntries(
    Object.entries(object).map(([key, value]) =&amp;gt; {
      let type = typeof value;
      if (value &amp;amp;&amp;amp; type === 'object') {
        value = cloneDeep(value);
      }
      return [key, value];
    })
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. Lodash 라이브러리 활용&lt;/h4&gt;
&lt;pre id=&quot;code_1731150494473&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;_.cloneDeep(value)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end/JavaScript</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/155</guid>
      <comments>https://re-hwi.tistory.com/155#entry155comment</comments>
      <pubDate>Fri, 8 Nov 2024 20:04:02 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 구조분해 할당</title>
      <link>https://re-hwi.tistory.com/154</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;구조 분해 할당&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조분해 할당이란 배열 혹은 객체의 속성을 &lt;span class=&quot;note&quot;&gt;개별 변수&lt;/span&gt;에 담을 수 있도록 하는 표현식이다. 가끔 배열의 일부분만 필요한 경우가 있는데 이럴 때 여러 방법이 있지만 &lt;span class=&quot;note&quot;&gt;구조 분해 할당&lt;/span&gt;을 사용하면 매우 쉽게 값을 가져올 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배열 분해&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수를 좌항, 분해하고자 하는 배열을 우항에 놓고 분해한다. 이 때, 배열의 순서를 맞추어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1730974615031&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fruits = ['apple', 'banana', 'cherry'];

// 배열 요소를 변수에 할당
const [first, second, third] = fruits;

console.log(first);  // &quot;apple&quot;
console.log(second); // &quot;banana&quot;
console.log(third);  // &quot;cherry&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본값 지정&lt;/h3&gt;
&lt;pre id=&quot;code_1730974726646&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const colors = ['red'];

// 없는 값에 기본값 설정
const [primary, secondary = 'blue'] = colors;

console.log(primary);   // &quot;red&quot;
console.log(secondary); // &quot;blue&quot; (기본값)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 쉽게 요소에 접근할 수 있다. 또, 만약 배열에 해당 값이 존재하지 않다면 undefined 가 출력되고, 기본값을 지정해 주었다면 해당 값이 나오게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체 분해&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열 분해와 마찬가지로 사용한다. 단 대괄호가 아닌 중괄호를 사용한다. 키 이름으로 변수를 생성할 때에는 아래와 같이 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1730975600310&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const user = {
  name: 'Alice',
  age: 25,
};

// 객체의 키를 변수에 할당
const { name, age } = user;

console.log(name); // &quot;Alice&quot;
console.log(age);  // 25&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다른 변수 이름으로 할당&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키 이름이 아닌 다른 이름의 변수에 속성값을 할당할 때에는 다음과 같이 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1730975851494&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const book = {
  title: 'JavaScript Essentials',
  author: 'John Doe',
};

// 다른 변수 이름으로 할당
const { title: bookTitle, author: bookAuthor } = book;

console.log(bookTitle);  // &quot;JavaScript Essentials&quot;
console.log(bookAuthor); // &quot;John Doe&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end/JavaScript</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/154</guid>
      <comments>https://re-hwi.tistory.com/154#entry154comment</comments>
      <pubDate>Thu, 7 Nov 2024 19:37:58 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] for in과 for of</title>
      <link>https://re-hwi.tistory.com/153</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;for ...in 문&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for ...in 문은 &lt;span class=&quot;note&quot;&gt;열거가능한&lt;/span&gt; 모든 속성을 반복한다. 이 때 &lt;span class=&quot;note&quot;&gt;상속된 속성&lt;/span&gt;도 같이 접근하게 된다. 여기서 두 가지의 특징이 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째로는 상속된 속성에 접근 가능하다는 점과 두 번째로는 열거 가능한 속성을 반복한다는 점이다. 주로 객체의 속성에 접근할 때 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;상속된 속성에 접근 가능&lt;/h3&gt;
&lt;pre id=&quot;code_1730794558160&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const javaScript = {
  creator: 'Brendan Eich',
  createAt: '1995.05',
  standardName: 'ECMAScript',
  currentVersion: 2024,
};

Object.prototype.nickName = 'tiger'; // 최상위 객체인 Object에 nickName이라는 프로퍼티 할당&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 javaScript 객체의 속성은 4개이다. 하지만 for in 문을 이용해 javaScript를 출력한다면 어떻게 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;354&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4JK1f/btsKwuC4zZD/SZieFp30UxZBnhLfpKn09k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4JK1f/btsKwuC4zZD/SZieFp30UxZBnhLfpKn09k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4JK1f/btsKwuC4zZD/SZieFp30UxZBnhLfpKn09k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4JK1f%2FbtsKwuC4zZD%2FSZieFp30UxZBnhLfpKn09k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;354&quot; height=&quot;210&quot; data-origin-width=&quot;354&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 결과가 나오게 된다. 여기서 nickName은 분명히 javaScript의 속성이 아닌데 출력이 되는 상황이 나타난다. 이는 nickName이 javaScript의 조상 즉, Object에 직접 주입했기 때문에 Object의 하위 객체인 javaScript에서도 출력이 되는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 막기 위해서 &lt;b&gt;hasOwn(obj, key)&lt;/b&gt; 메서드를 사용한다. 위 메서드는 인자로 들어간 객체안에 해당 키가 있는지를 확인한다. 따라서 for ...in으로 전체 요소를 출력한 뒤 조건문을 이용해 키가 있다면 출력을 한다거나 이런식으로 사용이 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1730795088810&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (const key in javaScript) {
  if (Object.hasOwn(javaScript, key)) {
    console.log(key);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 모든 속성을 반복함 (이 때 부모요소의 속성이 같이 나올 수 있음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 객체가 가진 속성만을 확인 한 뒤 있다면 출력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;열거 가능한 속성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enumerable : 열거 가능한&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for ...in 문은 열거 가능한 속성에 접근이 가능하다. 모든 속성은 enumerable의 기본값이 true이기 때문에 for in을 사용했을 때 나올 것이다. 하지만 이미지와 같이 어두운 색상의 속성은 모두 열거 불가능한 속성이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;292&quot; data-origin-height=&quot;108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCPxpw/btsKwv2QAWx/uE623g1rrN8TaE5KTaRUiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCPxpw/btsKwv2QAWx/uE623g1rrN8TaE5KTaRUiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCPxpw/btsKwv2QAWx/uE623g1rrN8TaE5KTaRUiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCPxpw%2FbtsKwv2QAWx%2FuE623g1rrN8TaE5KTaRUiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;292&quot; height=&quot;108&quot; data-origin-width=&quot;292&quot; data-origin-height=&quot;108&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 직접 속성을 할당할 때 열거 불가능하도록 만들기 위해서는 아래 코드를 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1730796232943&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Object.defineProperty(객체, 속성, {
	// 열거형이 아닌 속성 
})

// ex
Object.defineProperties(obj, {
  age: {
    value: 30,
    enumerable: true,	// 열거 가능하도록 변경
    writable: false,	// readOnly
    configurable: false,// 삭제 불가
  },
  email: {
    value: 'audwognl@naver.com',
  },
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 지정한 속성은 열거 불가능하게 생성된다. 위에서는 email속성을 위와 같이 설정했기 때문에 열거 불가능한 속성이 되고, for ...in 문에 나오지 않게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;462&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpeubA/btsKxSW0AJC/SUsngyTGMmKfqGldk7yOQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpeubA/btsKxSW0AJC/SUsngyTGMmKfqGldk7yOQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpeubA/btsKxSW0AJC/SUsngyTGMmKfqGldk7yOQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpeubA%2FbtsKxSW0AJC%2FSUsngyTGMmKfqGldk7yOQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;462&quot; height=&quot;76&quot; data-origin-width=&quot;462&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;for ...of 문&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for of문은 반복 가능한 속성에 접근 가능하다. 따라서 배열에 사용하기 유용하다. 이 때 객체에서도 for of 문을 사용하기 위해 객체를 배열로 바꾸는 등 여러 가지 방법으로 for of를 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object.keys =&amp;gt; 객체의 키만 모아 배열로 만들어줌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object.values =&amp;gt; 객체의 값들만 모아 배열로 만들어줌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object.entries =&amp;gt; 객체의 키, 값을 한 쌍의 배열로 총 2차배열을 만들어줌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 메서드를 이용해 &lt;span class=&quot;note&quot;&gt;배열&lt;/span&gt;을 반환 받고 배열을 for of에 넣는 식으로 사용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iterable : 반복 가능한&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end/JavaScript</category>
      <category>for in</category>
      <category>for of</category>
      <category>JavaScript</category>
      <category>js</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/153</guid>
      <comments>https://re-hwi.tistory.com/153#entry153comment</comments>
      <pubDate>Tue, 5 Nov 2024 18:14:53 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] 추상클래스란 무엇인가 (임시)</title>
      <link>https://re-hwi.tistory.com/152</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;추상클래스는 클래스들의 클래스이다. 객체지향의 개념을 알고 있으면 추상화라는 개념을 이해할 수 있지만 그렇지 않다면 너무 어렵게 느껴진다. 이번 단원에서는 추상화의 개념과 추상 클래스에 대해 포스팅 하려한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추상의 사전적 의미는 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;사물이나 표상을 어떤 성질, 공통성, 본질에 착안하여 그것을 추출하여 파악하는 것&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;뽑을 추(抽)&lt;/b&gt;: &quot;뽑다&quot;, &quot;끌어내다&quot;를 의미한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;형상 상(象)&lt;/b&gt;: &quot;형상&quot;이나 &quot;모양&quot;을 뜻한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 어떤 대상의 &lt;span class=&quot;note&quot;&gt;특징&lt;/span&gt;만을 뽑아내어 간추리는 것이다. 그렇다면 특징을 어떻게 뽑아낼 수 있을까?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 자동차, 비행기, 기차가 있다면 이 것들의 공통된 특징은 바로 '이동수단' 이라는 점이다. 이렇게 추상화를 하면 객체 하나하나의 디테일 보다는 공통된 특징만이 남게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 생각해보면 우리가 자동차, 비행기, 기차를 먼저 구현하지 않고 &quot;&lt;b&gt;이동수단&lt;/b&gt;&quot;을 먼저 구현한다면 가지를 뻗어 공통된 성질을 갖는 여러가지 이동수단을 만들 수 있을 것이다. 이것이 바로 추상화의 핵심이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1646&quot; data-origin-height=&quot;924&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWZIIN/btsKpnqs5t9/RE1XPrZxzW3Y8kIOySKA71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWZIIN/btsKpnqs5t9/RE1XPrZxzW3Y8kIOySKA71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWZIIN/btsKpnqs5t9/RE1XPrZxzW3Y8kIOySKA71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWZIIN%2FbtsKpnqs5t9%2FRE1XPrZxzW3Y8kIOySKA71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;591&quot; height=&quot;332&quot; data-origin-width=&quot;1646&quot; data-origin-height=&quot;924&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 처음으로 돌아가 추상 클래스는 클래스들의 클래스라고 했다. 애초에 클래스가 추상화를 한 상태인데 어떻게 또 특징을 뽑아낼 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서 당신이 '메이플스토리' 게임을 하고있다고 가정하자. 당신은 '전사' 직업을 플레이 하고 있다. 이 때의 '전사'는 클래스에 속하고 당신의 캐릭터는 '객체'이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 전사와 마법사 도적 모두 가지고 있는 공통된 특징이 있다. 레벨, 체력바 ... 와 같은 공통된 형상을 뽑아낸 뒤 &quot;&lt;b&gt;캐릭터&lt;/b&gt;&quot; 라는 &quot;추상클래스&quot;에 넣을 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 마찬가지로 추상메서드라는 개념도 존재한다. 모든 클래스는 '&lt;b&gt;공격&lt;/b&gt;'을 할 수 있다. 따라서 '캐릭터' 라는 추상클래스에 공격 메서드를 선언할 수도 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 추상클래스는 직접적인 인스턴스를 뽑아내지 못한다. 반드시 &quot;&lt;b&gt;직업&lt;/b&gt;&quot; 을 가진 구체적인 클래스만 인스턴스를 뽑을 수 있다.&lt;/p&gt;</description>
      <category>Front end/TypeScript</category>
      <category>typeScript</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/152</guid>
      <comments>https://re-hwi.tistory.com/152#entry152comment</comments>
      <pubDate>Wed, 30 Oct 2024 22:45:09 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] 다형성과 제네릭</title>
      <link>https://re-hwi.tistory.com/151</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;서론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭의 개념이 헷갈려서 며칠 공부를 해보고 포스팅을 한다. 제네릭이란 &lt;b&gt;선언부에서 타입을 명시하지 않고 &lt;span class=&quot;note&quot;&gt;호출&lt;/span&gt;이 되었을 때 타입을 명시하는 어떠한 타입도 될 수 있는&lt;/b&gt; 기법이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 제네릭이 어떠한 타입도 될 수 있다면 unknown, any와 뭐가 다를까 하는 생각이 들었는데 오늘 드디어 뭔가 깨달은 것 같다 ㅋㅋ. 앞서 말한것 처럼 제네릭은&lt;b&gt; 사용 시점의 인풋에 의해서 타입이 결정&lt;/b&gt;된다. 따라서 함수 혹은 인터페이스와 같은 곳에서 많이 사용된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 unknown과 any는 사용 용도 자체가 다르다. 애초에 이 두가지를 비교할 수 없을 뿐더러 unknown은 어떠한 타입도 될 수 있지만 사용하기 위해서는 타입을 &lt;span class=&quot;note&quot;&gt;명시&lt;/span&gt;해야 한다는 부분도 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;any도 마찬가지로 비슷한 이유로 다르지만 이걸 쓰면 TS를 쓰는 이유가 없다고 하니 되도록이면 사용하지 않아야겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다형성 (polymorphism)&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span class=&quot;note&quot;&gt;객체지향&lt;/span&gt;의 4가지 특징 중 하나인 &lt;span class=&quot;note&quot;&gt;다형성&lt;/span&gt;은 &lt;b&gt;어떤 객체의 속성이나 기능이 상황에 따라 여러 가지 형태를 가질 수 있는 성질&lt;/b&gt;을 말한다. 예를 들어 '이동수단'은 전진만이 있다고 가정하자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 '이동수단'의 하위인 '비행기'는 '비행' 이 있을 수도 있고 '자동차'는 '후진'을 할 수도 있다. 그렇다면 이 자동차와 비행기는 '이동수단'이 아닌가? 그것도 아니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동차와 비행기는 '이동수단'의 '전진'을 바탕으로 자신의 기능을 추가한 것이다. 이 과정을 &lt;span class=&quot;note&quot;&gt;&lt;b&gt;메서드 오버라이딩&lt;/b&gt;&lt;/span&gt;이라고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 같은 다형성의 개념인 &lt;span class=&quot;note&quot;&gt;&lt;b&gt;메서드 오버로딩&lt;/b&gt;&lt;/span&gt;이다. 오버로딩은 같은 함수명에 인자를 다르게 주어 다른 함수인 것 처럼 사용하는 기법이다. 예를 들어 YYYYMMDD 형식으로 인자를 받는 함수가 있다고 하자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 인자로 들어오는 타입이 string, number 혹은 Date 타입 3개 중 하나가 들어온다고 가정했을 때 메서드를 하나의 타입으로 정의한다면 반드시 오류가 날 것이다. 하지만 세 타입을 메서드 오버라이딩으로 정의하면 각각의 상황에 맞는 메서드가 호출 될 것이다. 이 기법을 메서드 오버로딩이라고 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;제네릭&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span class=&quot;note&quot;&gt;제네릭&lt;/span&gt;은 앞서 설명한 것처럼 호출되는 시점에서 타입이 결정되는 기법을 말한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1730205613226&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type SuperPrint = {
    (arr: number[]) : void
    (arr: boolean[]) : void
}

const superPrint:SuperPrint = (arr) =&amp;gt; {
    arr.forEach(i =&amp;gt; console.log(i))
}

superPrint([1,2,3,4])
superPrint([true, true])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 SuperPrint 타입을 지정하고 해당 타입을 사용하는 superPrint 메서드이다. 이 메서드를 여러 타입에서 사용하기 위해서는 SuperPrint타입에 해당 타입을 모두 명시해 줘야 할까?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제를 해결하기 위해 사용되는 것이 바로 &lt;span class=&quot;note&quot;&gt;제네릭&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭을 이용하면 superPrint()에 어떠한 타입의 배열을 넣었을 때 해당 타입을 지정한다. 예를 들어 [1, 'hi', true] 라는 배열이 superPrint의 인자로 들어간다면 superPrint()의 타입이 그제서야 정의된다는 뜻이다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end/TypeScript</category>
      <category>FE</category>
      <category>TS</category>
      <category>typeScript</category>
      <category>타입스크립트</category>
      <category>프론트엔드</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/151</guid>
      <comments>https://re-hwi.tistory.com/151#entry151comment</comments>
      <pubDate>Sun, 27 Oct 2024 18:19:52 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] unknown, void, never 타입에 대해서 알아보자</title>
      <link>https://re-hwi.tistory.com/150</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;TS에서는 JS에서만 있는 타입 뿐만 아니라 TS만의 타입도 가지고 있다. 이번에 포스팅 하는 3가지 타입이 그 예시이다. 시작하기에 앞서 간단히 이 타입에 대한 설명을 하자면 unknown은 &lt;b&gt;모든 타입이 될 수 있는 타입&lt;/b&gt;이고, void는 &lt;b&gt;아무것도 반환하지 않는 함수&lt;/b&gt;에 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 never는 &lt;b&gt;절대 반환하지 않는 함수&lt;/b&gt;에서 사용된다는데 void랑 뭐가 다른지 잘 모르겠다. 강의의 예시로는 에러를 던지는 함수를 보여줬는데 조금 더 생각을 해봐야겠다..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ 지피티한테 물어봤는데 void는 아무것도 반환하지 않는 메서드 즉, &lt;b&gt;콘솔출력&lt;/b&gt; 또는 &lt;b&gt;이벤트 핸들러&lt;/b&gt;에서 주로 사용된다고 하고 never는 정상적으로 종료되지 않는 함수 즉 &lt;b&gt;무한루프, 에러 던지기&lt;/b&gt;에 주로 사용된다고 한다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;unknown&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떠한 타입도 될 수 있는 타입. 하지만 any와는 다르게 &lt;b&gt;해당 타입을 확인&lt;/b&gt; 해야한다. 에를 들어 API가 어떤 타입을 반환하는지 모를 때 사용되거나 인풋이 여러 타입이 될 수 있을 때 사용된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729927715942&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function check(temp : unknown){
	if(typeof temp === &quot;number&quot;){		// 이러한 타입 확인이 반드시 필요
    	//number 타입일 때 실행되는 코드
    }
    
    if (typeof temp === &quot;string&quot;){
    	//string 타입일 때 실행되는 코드
    }
    
    ...
}

function check(temp : unknown){
	temp + 2 	// 타입 확인이 없기 때문에 error 발생
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;void&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떠한 반환값을 가지지 않는 함수를 선언할 때 사용한다. C에서 사용했던 기억이 있는데 같은 기능을 수행한다고 한다. TS가 아무것도 반환하지 않는 함수를 자동으로 void로 인식하기 때문에 타입을 굳이 지정하지 않아도 된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729927998765&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function hi(){
	console.log(&quot;배고파요&quot;)	//return이 없는 메서드
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;never&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;절대 반환되지 않는 함수를 선언할 때 사용. 해당 메서드가 정상적으로 종료되지 않을 때 사용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1729928596092&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function throwError(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {
    // 무한 루프이므로 종료되지 않음
  }
}

function hello(temp : number | string){
	if(typeof temp === 'number'){
    	// 여기서 temp의 타입은 number
    }
    
    elseif(typeof temp === 'string'){
    	// 여기서 temp의 타입은 string
    }
    
    else{
    	// 여기서 temp의 타입은 never
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 아직 never 타입에 대해서는 정확히 모르겠다. 뭐 더 배우다 보면 알겠지 하는 생각으로 일단은 넘어가려 한다. 나중에 확실히 알게되면 글을 수정할 생각이다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end/TypeScript</category>
      <category>TS</category>
      <category>Type</category>
      <category>typeScript</category>
      <category>Unknown</category>
      <category>타입스크립트</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/150</guid>
      <comments>https://re-hwi.tistory.com/150#entry150comment</comments>
      <pubDate>Sat, 26 Oct 2024 16:47:41 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] Readonly , Tuple에 대해서 알아보자</title>
      <link>https://re-hwi.tistory.com/149</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;ReadOnly&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Readonly 는 말 그대로 &lt;b&gt;읽기 전용&lt;/b&gt; 이라는 키워드 이다. 예를 들어서 어떤 배열을 생성할 때에만 값을 받고 해당 배열을 수정할 수 없게 만들고 싶을 때 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 readonly 키워드는 &lt;b&gt;배열&lt;/b&gt; 및&lt;b&gt; 튜플&lt;/b&gt;에서만 사용 가능하다. 사용법은 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1729773470529&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let arr : readonly number[] = [1, 2, 3]

arr.push(4) //error&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들어진 arr 배열은 추가, 삭제, 업데이트 모두 &lt;b&gt;불가&lt;/b&gt;하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Tuple&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬을 공부하며 리스트와 튜플의 차이는 가변성과 불변성으로 공부했었다. 타입스크립트에서도 비슷하지만 &lt;b&gt;&quot;타입&quot;&lt;/b&gt;을 바꿀 수 없다는 점이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 튜플을 선언할 때에는 타입의 순서를 선언한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729925362256&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a : [number, string, number] = [1,&quot;hi&quot;,3]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생성된 a 튜플은 0번 인덱스에는 number 타입, 1번 인덱스에는 string 타입, 2번 인덱스에도 number 타입만이 고정된다. 하지만 같은 타입이라면 요소를 변경할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1729925489065&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a : [number, string, number] = [1,&quot;hi&quot;,3]

a[0] = 3 // 가능
a[1] = 4 // 불가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end/TypeScript</category>
      <category>FE</category>
      <category>TS</category>
      <category>typeScript</category>
      <category>웹개발</category>
      <category>타스</category>
      <category>타입스크립트</category>
      <category>프론트엔드</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/149</guid>
      <comments>https://re-hwi.tistory.com/149#entry149comment</comments>
      <pubDate>Sat, 26 Oct 2024 15:53:47 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] 변수와 타입, 함수 생성</title>
      <link>https://re-hwi.tistory.com/148</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;서론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript는 JS에서 발생하는 에러를 대비하기 위해 만들어진 언어이다. 자바스크립트에서는 개발자를 위한 에러를 내지 않는다. 계산 식이 잘못 되었더라도 어떻게든 수행한다. 하지만 그로인한 결과값은 원하는 값이 아닌 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 JS에서는 [1, 2,3 ,4] + true 를 실행하면 오류없이 실행이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;output-terminal&quot; class=&quot;typescript&quot; style=&quot;background-color: #f5f5f5; color: #242424; text-align: start;&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;console.log([1,2,3,4] + true)

// result
1,2,3,4true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 위 결과는 원하는 결과 값이 아니다. 위와 같은 수식은 옳은 수식이 아니므로 실행 전 개발자에게 알려주어야 한다. 다음은 또 다른 예시이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729769495242&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function div(val1, val2) {
    return (val1 / val2)
}

console.log(div('안녕?'))

//result
NaN&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 수식에서 나오는 div 메서드는 두 수를 나누는 함수이다. 하지만 &quot;안녕?&quot; 이라는 &lt;b&gt;문자열&lt;/b&gt;이 들어가도 에러가 나지 않는다. 에러가 나지 않으므로 오류메세지도 나오지 않는다. 따라서 디버깅 난이도를 올리는 원인이 될 수도 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 이유로 코드를 실행하기 전 개발자에게 &lt;b&gt;에러&lt;/b&gt;를 알려주는 타입 스크립트가 나오게 되었다고 한다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;변수 선언&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기본 타입&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수를 선언할 때에는 타입을 지정할 수 있다. 여기서 타입을 반드시 지정해야하는 것은 아니다. 타입을 지정하지 않으면 타입스크립트에서 스스로 타입을 결정한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729769968520&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a : number = 1			// number 타입
let b : string = &quot;hello&quot;		// string 타입
let c : boolan = true			// boolan 타입
let d : number[] = [1,2,3]		// number array 타입
let e : string[] = [&quot;hello&quot;, &quot;world&quot;]	// string array 타입&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;옵션 타입&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;object 타입일 때 필수로 들어가는 변수와 필수가 아닌 변수가 있다고 한다면 아래와 같이 정의한다. 변수&amp;nbsp;명&amp;nbsp;뒤에&amp;nbsp;?&amp;nbsp;를&amp;nbsp;붙히면&amp;nbsp;옵션값이&amp;nbsp;된다.&amp;nbsp;따라서&amp;nbsp;age를&amp;nbsp;지정하지&amp;nbsp;않아도&amp;nbsp;에러가&amp;nbsp;발생하지&amp;nbsp;않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1729770703323&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let player : {
	name : string,
    age? : number  // 옵션
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;타입 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 본인이 만든 object 타입을 재사용할 수도 있다. 예를 들어 player 타입을 가진 인원이 많아진다면 위와 같은 내용을 여러번 써야 할 것이다. 따라서 타입을 지정해서 해당 타입을 불러오기만 하면 조금 더 쉽게 코드를 작성할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729771647250&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type player = {
	name: string,
	age?: number
}

const jaehwi : player = {
	name : 'jaehwi',
    	age : 25
}

const nico : player = {
	name : 'nico'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;함수&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트의 함수는 지정한 타입의 리턴만을 반환한다. 함수의 사용법은 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729772098270&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function makePlayer(name : string) : player {
	return {
    	name : name
    }
}

const jaehwi = makePlayer('jaehwi')
jaehwi.age = 25&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Front end/TypeScript</category>
      <category>FE</category>
      <category>frontend</category>
      <category>TS</category>
      <category>typeScript</category>
      <category>타입스크립트</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/148</guid>
      <comments>https://re-hwi.tistory.com/148#entry148comment</comments>
      <pubDate>Thu, 24 Oct 2024 20:07:57 +0900</pubDate>
    </item>
    <item>
      <title>[SCSS] 파일 구조와 모듈화</title>
      <link>https://re-hwi.tistory.com/147</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;서론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SCSS를 사용하는 가장 큰 이유이자 핵심이다. 컴포넌트를 재사용하고 컴포넌트끼리의 결합을 통해 하나의 큰 시스템을 구축한다. html과 css만으로는 그런 방법을 사용할 수 없는줄 알았는데 SCSS를 이용하면 객체지향적으로 코드 작성이 가능하다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;파일 구조&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #444444; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;base/&lt;/li&gt;
&lt;li&gt;components/&lt;/li&gt;
&lt;li&gt;layout/&lt;/li&gt;
&lt;li&gt;pages/&lt;/li&gt;
&lt;li&gt;themes/&lt;/li&gt;
&lt;li&gt;abstracts/&lt;/li&gt;
&lt;li&gt;vendors/&lt;/li&gt;
&lt;li&gt;main.scss&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 형식은 SCSS 뿐만이 아니라 리액트나 뷰와 같은 다른 라이브러리에서도 일반적으로 사용한다. 반드시 이 구조를 사용해야 한다는 것은 아니지만 일반적으로 많이 사용하는 구조이니 SCSS에서도 사용하면 쉽게 적용 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;폴더별 예시 파일&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;1624&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pac3J/btsKhwzu2s5/Dat9lyAsSXyqubNpLxbsBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pac3J/btsKhwzu2s5/Dat9lyAsSXyqubNpLxbsBK/img.png&quot; data-alt=&quot;폴더별 예시 파일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pac3J/btsKhwzu2s5/Dat9lyAsSXyqubNpLxbsBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpac3J%2FbtsKhwzu2s5%2FDat9lyAsSXyqubNpLxbsBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;473&quot; height=&quot;1624&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;1624&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;폴더별 예시 파일&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;@use&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 해당 폴더에서 컴포넌트를 만든 후 다른 컴포넌트에서 사용하기 위한 키워드는 &lt;b&gt;@use&lt;/b&gt; 이다. 모듈을 불러오기 위해서는 아래와 같이 사용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729672552886&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 모듈 선언 
$radius: 3px;

@mixin rounded {
  border-radius: $radius;
}

// 호출부
@use &quot;modules/corners&quot;;

.button {
  @include corners.rounded;
  padding: 5px + corners.$radius;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 네임 스페이스를 사용할 수도 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729672608367&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 모듈 선언 
$radius: 3px;

@mixin rounded {
  border-radius: $radius;
}

// 호출부
@use &quot;modules/corners&quot; as cn;

.button {
  @include cn.rounded;		// cn으로 호출 가능
  padding: 5px + cn.$radius;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;@forward&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@forward 키워드는 각 폴더안에 있는 여러 컴포넌트를 하나로 묶기 위해 사용된다. 각각의 폴더에 있는 _index 파일에서 폴더 내 파일을 모두 묶어서 내보낸다.&lt;/p&gt;
&lt;pre id=&quot;code_1729672810218&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// components 폴더에 있는 _index.scss

@forward 'button';
@forward 'dropdown';&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Front end/Sass</category>
      <category>CSS</category>
      <category>FE</category>
      <category>sass</category>
      <category>scss</category>
      <category>프론트엔드</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/147</guid>
      <comments>https://re-hwi.tistory.com/147#entry147comment</comments>
      <pubDate>Wed, 23 Oct 2024 17:45:34 +0900</pubDate>
    </item>
    <item>
      <title>[SCSS] mixin 사용법</title>
      <link>https://re-hwi.tistory.com/146</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;서론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mixin과 function은 SCSS를 모듈화 하기 위한 가장 큰 요소라고 생각된다. 잘못 사용하면 정말 가독성이 떨어지고 어려울 수 있지만, 잘만 사용한다면 코드를 훨씬 간결하게 유지할 수 있을 것 같다. 유지 보수 면에서도 훨씬 이점이 많을 거라 생각된다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;mixin이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 함수와 유사한 개념 단, 함수와 동일하지 않음 (리턴값이 존재 X)&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mixin 을 선언할 때에는 &lt;span class=&quot;note&quot;&gt;@mixin&lt;/span&gt; 키워드를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 때에는 &lt;span class=&quot;note&quot;&gt;@include&lt;/span&gt; 키워드를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 (매개변수가 없는 믹스인)&lt;/p&gt;
&lt;pre id=&quot;code_1729604910979&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 매개변수가 없는 믹스인

// 선언부
@mixin resetList() {
  margin: 0;
  padding: 0;
  list-style: none;
}

// 사용부
ul {
  background-color: yellow;
  @include resetList();
}

// CSS

/* 결과화면 */

ul {
  background-color: yellow;
  margin: 0;
  padding: 0;
  list-style: none;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 (매개변수가 있는 믹스인)&lt;/p&gt;
&lt;pre id=&quot;code_1729605593331&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 매개변수가 있는 믹스인

// 선언부
@mixin width($width, $height) {
  width: $width;
  inline-size: $width;
  height: $height;
  block-size: $height;
}

// 사용부
ol {
  font-size: 1rem;
  @include resetList();
  @include width(100px, 200px);
}

//CSS

/* 결과 화면 */

ol {
  font-size: 1rem;
  margin: 0;
  padding: 0;
  list-style: none;
  width: 100px;
  inline-size: 100px;
  height: 200px;
  block-size: 200px;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;옵션인자&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수를 옵션으로 넣을 수 있는 믹스인. 넣지 않으면 지정한 기본값을 따른다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729605717883&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//SCSS

@mixin flexBox($direction, $justify: center, $align, $padding: 20px) {
  display: flex;
  justify-content: $justify;
  align-items: $align;
  padding: $padding;
}

.flexContainer {
  @include flexBox(row, center, center);
}

//CSS

/* 결과 화면 */

ol {
  font-size: 1rem;
  margin: 0;
  padding: 0;
  list-style: none;
  width: 100px;
  inline-size: 100px;
  height: 200px;
  block-size: 200px;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;키워드 인자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;믹스인을 호출할 때 매개변수 순서대로 인자를 전달하는 방법 외에도 키워드를 통해 직접 접근이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1729606820224&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//SCSS
.notification {
  @include position(absolute, $top: 20px, $right: 20px, $zIndex: 100);
}

//CSS
.notification {
  position: absolute;
  z-index: 100;
  top: 20px;
  right: 20px;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키워드 인자와 옵션인자를 함께 쓰면 디폴트로 주고 싶은 건 적지 않고 키워드로 직접 접근하여 믹스인을 사용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end/Sass</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/146</guid>
      <comments>https://re-hwi.tistory.com/146#entry146comment</comments>
      <pubDate>Tue, 22 Oct 2024 23:21:52 +0900</pubDate>
    </item>
    <item>
      <title>[SCSS] 주석, 변수, 보간법</title>
      <link>https://re-hwi.tistory.com/145</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;국비 교육을 들으며 Saas를 처음 배워보았다. 처음 SCSS를 사용해보고 나서 든 생각은 정말 프로그래밍 언어와 다를게 없다고 생각했다. 파일 관리를 모듈 단위로 한다는 점에서 리액트와 뷰랑 사용법이 유사하다고 생각이 드는데 이렇게 코드를 작성하면 바닐라 JS에서도 라이브러리를 쓰듯 코드를 작성하는게 가능할 것 같다는 생각이 들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로는 코드의 가독성이 너무 좋다. 또 if 의 사용으로 인해 정말 프로그래밍 언어처럼 코드를 작성할 수도 있다. CSS에서 해당 기능을 사용할 수 있다는게 신기했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 해외 코드를 읽으며 SCSS는 정말 많이 봤지만 국내에서는 인지도가 조금 떨어지는 것 같기도 하다. 리액트나 뷰는 JS 를 쓰는 거의 모든 기업들이 사용을 하고 있지만, SCSS는 지원 자격에서 필수가 아니라던지 인터넷에 떠도는 코드의 양이 많지는 않은 것 같은 느낌이 든다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ 코드 블록중에 SCSS 블록이 없어서 CSS로 작성했더니 코드 컬러링이 조금 이상하게 보입니다 ㅜ 코드는 맞으니 복사하셔서 사용 가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;SCSS 주석 사용법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS에서는 // 주석을 지원하지 않는다. 하지만 SCSS에서는 한 줄 주석을 지원한다. 해당 주석은 CSS로 변환했을 때 사라진다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729603117898&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 주석입니다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 줄 주석은 괄호 내에서도 사용이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1729603169347&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.sans {
  font: SpoqaHanSans, // 한 줄 주석을 이렇게 사용 가능합니다.
				Helvetica, Verdana, sans-serif;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SCSS 변수 사용법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 선언은 &lt;span class=&quot;note&quot;&gt;$&lt;/span&gt;로 선언하고 변수 이름에는 -과 _를 사용할 수 있다. -사용한 변수와 _을 사용한 변수는 같은 변수이다.&lt;/p&gt;
&lt;pre id=&quot;code_1729603015609&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$base-color: #bf2243;
$border-color: rgba($base-color, 0.7);

.alert {
  border: 1px solid $border_color; 
}
// border_color와 border-color는 같은 변수 입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;변수 보간법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수를 만들면 가장 많이 사용하게 되는 &lt;span class=&quot;note&quot;&gt;보간법&lt;/span&gt;이다. &lt;span class=&quot;note&quot;&gt;#{ 변수 }&lt;/span&gt; 형태로 사용 가능하다. 출력문인 @debug와 함께 사용하면 아래와 같은 결과가 나온다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729603538566&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$size: 10px;
@debug &quot;size: &quot; + $size;

// 출력 : Debug: size: 10px&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CSS변수와 SCSS 변수의 차이점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS변수는 &quot;변수&quot; 그 자체로 인식을 하지만 SCSS는 컴파일 과정에서 변수가 아닌 &quot;&lt;span class=&quot;note&quot;&gt;값&lt;/span&gt;&quot; 으로 변환한다. 따라서 우리는 변수처럼 사용하지만 실제 변환된 CSS를 보면 값이 할당 된 것을 볼 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729603762198&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//SCSS 

$size: 10px;

.box {
  width: $size;
  height: $size;
}

// CSS 변환 후

.box {
  width: 10px;
  height: 10px;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 명령형인 Sass 변수는 변수를 사용한 후 그 값을 변경해도 이전에 사용된 값은 그 값을 유지한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729603913864&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// SCSS

$color: #bf2243; //전역 변수 선언
.rule-1 {
  color: $color;
}

$color: #0e0948;  //이름이 같은 전역변수 선언
.rule-2 {
  color: $color;
}


//CSS 변환 후

/* 
rule-1은 이전에 정의된 변수의 색상을 사용하고, rule-2는 이후에 정의된 색상을 사용합니다. 
*/

.rule-1 {
  color: #bf2243;
}

.rule-2 {
  color: #0e0948;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Front end/Sass</category>
      <category>CSS</category>
      <category>FE</category>
      <category>sass</category>
      <category>scss</category>
      <category>웹개발</category>
      <category>프론트엔드</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/145</guid>
      <comments>https://re-hwi.tistory.com/145#entry145comment</comments>
      <pubDate>Tue, 22 Oct 2024 22:32:52 +0900</pubDate>
    </item>
    <item>
      <title>[PWA] FCM 모바일 알림 구현하기 (Vue.js)</title>
      <link>https://re-hwi.tistory.com/144</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;현재 진행중인 프로젝트에서 모바일 알림을 구현해야 했다. 그래서 가장 간편하게 쓸 수 있다는 FCM을 사용해 보았는데 나중에 까먹을 것 같아서 남겨두려고 작성한다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;FCM(Firebase Cloud Messaging) 이란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;firebase에서 제공하는 서비스 중 하나로 여러 플랫폼에 푸시 메세지를 보낼 수 있다. iOS, 안드로이드, 웹 모두 지원하므로 쉽게 사용 가능하다는 장점이 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://firebase.google.com/docs/cloud-messaging?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 문서&lt;/a&gt;가 잘 작성되어 있어 처음 사용해보는 사람들도 쉽게 접근할 수 있어서 좋았던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;개발 환경 ⚙️&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 Vue를 사용하고 있으므로 vue/cli-plugin-pwa&amp;nbsp;플러그인을&amp;nbsp;설치했다.&amp;nbsp;그런데&amp;nbsp;파이어베이스에서&amp;nbsp;서비스&amp;nbsp;워커를&amp;nbsp;줘서&amp;nbsp;제공되는registerServiceWorker.js&amp;nbsp;에서는&amp;nbsp;따로&amp;nbsp;구현하지&amp;nbsp;않았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그리고&amp;nbsp;firebase/config.js&amp;nbsp;파일에&amp;nbsp;파이어베이스&amp;nbsp;초기화&amp;nbsp;부분을&amp;nbsp;넣었고,&amp;nbsp;권한&amp;nbsp;요청&amp;nbsp;부분을&amp;nbsp;service/notificationPermission.js&amp;nbsp;파일에&amp;nbsp;구현했다.&amp;nbsp;마지막으로&amp;nbsp;백그라운드&amp;nbsp;사용을&amp;nbsp;위해서&amp;nbsp;public/firebase-messaging-sw.js&amp;nbsp;파일을&amp;nbsp;넣었다.&amp;nbsp;이&amp;nbsp;파일은&amp;nbsp;공식&amp;nbsp;문서에서도&amp;nbsp;나와있듯이&amp;nbsp;반드시&amp;nbsp;프로젝트&amp;nbsp;루트에&amp;nbsp;넣어야&amp;nbsp;한다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;사용&amp;nbsp;방법&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저&amp;nbsp;파이어베이스&amp;nbsp;프로젝트를&amp;nbsp;만들어야&amp;nbsp;한다.&amp;nbsp;firebase&amp;nbsp;사이트에서&amp;nbsp;프로젝트를&amp;nbsp;생성한다.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그&amp;nbsp;다음&amp;nbsp;프로젝트&amp;nbsp;터미널에서&amp;nbsp;파이어베이스를&amp;nbsp;설치한다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot; style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;code&gt;npm install firebase&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;firebase 초기화&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1726584264265&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//firebase/config.js

import { initializeApp } from 'firebase/app';
import { getMessaging } from &quot;firebase/messaging&quot;;

const firebaseConfig = {
/*
  여기부터는 본인 프로젝트에 맞는 데이터 넣으시면 됩니다. 
  ex) 
  apiKey: process.env.VUE_APP_FIREBASE_API_KEY,
  authDomain: process.env.VUE_APP_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.VUE_APP_FIREBASE_PROJECT_ID,
  storageBucket: process.env.VUE_APP_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.VUE_APP_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.VUE_APP_FIREBASE_APP_ID,
  measurementId: process.env.VUE_APP_FIREBASE_MEASUREMENT_ID,
 */
};

const app = initializeApp(firebaseConfig);

// FCM 메시징 설정
const messaging = getMessaging(app);

export { messaging };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일에서는 파이어베이스를 초기화 하고 메세징 객체를 설정한다. 이 파일에서 내보낸 messaging을 이용하여 푸시 알림을 보낸다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알림을 보내기 위해서는 토큰과 권한이 필요하다. 먼저 권한을 승인하면 해당 기기의 토큰이 발급되고 이 토큰을 이용해 사용자를 알 수 있다. 아래 코드는 권한 요청과 토큰 발급 코드이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1726585412796&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//service/notificationPermission.js

import { getToken } from &quot;firebase/messaging&quot;;
import { messaging } from &quot;../firebase/config&quot;;

export function requestFCMPermission() {
  // Notification 권한 요청
  console.log('권한을 요청하는 중...');
  
  Notification.requestPermission().then((permission) =&amp;gt; {
    if (permission === 'granted') {
      console.log('알림 권한이 부여되었습니다.');
      
      // FCM 토큰 요청
      getToken(messaging, { vapidKey: process.env.VAPID_KEY })
        .then((currentToken) =&amp;gt; {
          if (currentToken) {
            console.log('FCM Token:', currentToken);
          } else {
            console.log('권한을 허용해주세요');
          }
        })
        .catch((err) =&amp;gt; {
          console.log('푸시 토큰 가져오는 중에 에러 발생', err);
        });
      
    } else {
      console.log('권한을 얻을 수 없습니다.');
    }
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 설정이 끝났으면 테스트를 해야한다. 테스트를 하기에 앞서 토큰을 알아야 하는데 방금 작성한 requestFCMPermission 메서드를 main.js에서 불러와서 실행하면 콘솔창에 본인의 토큰이 나올 것 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 토큰을 가지고 다시 파이어 베이스로 간다. 그리고 클라우드 메세징 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2754&quot; data-origin-height=&quot;1348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mMg9p/btsJDhx5h7l/2yJllx7TTcZiq21lfoTAXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mMg9p/btsJDhx5h7l/2yJllx7TTcZiq21lfoTAXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mMg9p/btsJDhx5h7l/2yJllx7TTcZiq21lfoTAXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmMg9p%2FbtsJDhx5h7l%2F2yJllx7TTcZiq21lfoTAXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;367&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2754&quot; data-origin-height=&quot;1348&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 캠페인 만들기 선택 후 알림 제목과 설명을 입력하면 토큰을 추가할 수 있는 버튼이 나온다. 여기에 아까 받은 토큰을 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백그라운드 알림을 보내기 위해서는 루트에 &lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;firebase-messaging-sw.js 파일이 있어야 한다. 나는 public 폴더에 만들었으며 코드는 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1726586055982&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// /public/firebase-messaging-sw.js
importScripts(
    &quot;https://www.gstatic.com/firebasejs/10.8.0/firebase-app-compat.js&quot;
);
importScripts(
    &quot;https://www.gstatic.com/firebasejs/10.8.0/firebase-messaging-compat.js&quot;
);

self.addEventListener(&quot;install&quot;, function (e) {
    console.log(&quot;fcm service worker가 설치되었습니다.&quot;);
    self.skipWaiting();
});

self.addEventListener(&quot;activate&quot;, function (e) {
    console.log(&quot;fcm service worker가 실행되었습니다.&quot;);
});

const firebaseConfig = {
    apiKey: '',
    authDomain: '',
    projectId: '',
    storageBucket: '',
    messagingSenderId: '',
    appId: '',
    measurementId: '',
};

// 파이어베이스 초기화
firebase.initializeApp(firebaseConfig);

const messaging = firebase.messaging();

messaging.onBackgroundMessage((payload) =&amp;gt; {
    const notificationTitle = payload.title;
    const notificationOptions = {
        body: payload.body
    };
    self.registration.showNotification(notificationTitle, notificationOptions);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src에 없으므로 .env 파일이나 inport 를 직접 해주어야 한다. 나는 이 부분에서 왜 파이어베이스를 또 초기화 하는지 궁금했었는데 초기화를 두 번 하는 이유는 애플리케이션 실행 환경과 서비스 워커 실행 환경이 서로 다르기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말해서 앱이 꺼져있을 때 알림이 오면 파이어베이스 객체를 생성해야 하기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 이거 생성하고 테스트를 보내면 끝!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 Mac에서는 설정에서 크롬 알림을 또 켜줘야 한다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;864&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnrLEL/btsJDzdYNY2/1eZllAfiPoBqvupwk7OpRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnrLEL/btsJDzdYNY2/1eZllAfiPoBqvupwk7OpRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnrLEL/btsJDzdYNY2/1eZllAfiPoBqvupwk7OpRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnrLEL%2FbtsJDzdYNY2%2F1eZllAfiPoBqvupwk7OpRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;427&quot; height=&quot;520&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;864&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암튼 이렇게 하면 드디어 끝!!!!!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zSrvA/btsJDg0dk3K/Sa2q2uST9R1c8auFQPh2vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zSrvA/btsJDg0dk3K/Sa2q2uST9R1c8auFQPh2vk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zSrvA/btsJDg0dk3K/Sa2q2uST9R1c8auFQPh2vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzSrvA%2FbtsJDg0dk3K%2FSa2q2uST9R1c8auFQPh2vk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;788&quot; height=&quot;326&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>etc/이것저것</category>
      <category>fcm</category>
      <category>pwa</category>
      <category>모바일알림</category>
      <category>알림</category>
      <category>파이어베이스</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/144</guid>
      <comments>https://re-hwi.tistory.com/144#entry144comment</comments>
      <pubDate>Wed, 18 Sep 2024 00:35:54 +0900</pubDate>
    </item>
    <item>
      <title>npm audit fix --force 후 에러</title>
      <link>https://re-hwi.tistory.com/143</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 리팩토링 하던 중 npm 에서 취약점이 있으니 'npm audit fix --force' 명령어를 입력하라는 경고가 떴다. 처음에는 그냥 넘어갔지만 해당 메세지가 계속해서 뜨다 보니 신경이 쓰여 해당 명령어를 입력하자마자 아래와 같은 에러가 뜨면서 프로젝트가 안열렸다..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CJExX/btsJDrfD0br/61NOEe0WyNRegrjhpQSNLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CJExX/btsJDrfD0br/61NOEe0WyNRegrjhpQSNLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CJExX/btsJDrfD0br/61NOEe0WyNRegrjhpQSNLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCJExX%2FbtsJDrfD0br%2F61NOEe0WyNRegrjhpQSNLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;742&quot; height=&quot;96&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전 문제인것 같아 진짜 별 짓 다해봤는데 에러가 계속해서 생겼으나 아래 방법으로 해결했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;git restore package-lock.json&lt;/li&gt;
&lt;li&gt;git restore package.json&lt;/li&gt;
&lt;li&gt;npm install&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처:&amp;nbsp;&lt;a href=&quot;https://velog.io/@devbin20/React.js-undo-npm-audit-fix-force&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@devbin20/React.js-undo-npm-audit-fix-force&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음부터는 함부로 버전 관련 명령어를 쓰지 말아야겠다고 다짐했다....&lt;/p&gt;</description>
      <category>etc</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/143</guid>
      <comments>https://re-hwi.tistory.com/143#entry143comment</comments>
      <pubDate>Sat, 14 Sep 2024 18:37:01 +0900</pubDate>
    </item>
    <item>
      <title>사이드 프로젝트 후기 - 룸메이트 구하기 어플리케이션 : 그리핀</title>
      <link>https://re-hwi.tistory.com/142</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;학기가 끝나고 자격증 준비에 밀려있던 뷰 공부까지 정신없이 보냈다. 어느정도 이론을 마치고, 현업에서 뷰를 어떻게 사용하는지 느낌을 알기 위해 프로젝트를 진행했다. 프로젝트를 시작하며 정식 어플리케이션을 런칭하기 보다는 어느정도 연습삼아 가벼운 분위기로 진행했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 사이드 프로젝트는 학교 생활에 필요할 것 같은 범위에서 결정했다. 같이 프로젝트를 진행한 형이 기숙사에서 지내며 불편했던 경험(코골이, 흡연 등)을 바탕으로 랜덤으로 룸메이트를 배치하는 것 보다는, 어플을 통해 룸메이트를 구하고 친목을 다질 수 있다면 좋지 않을까? 하는 생각으로 시작하게 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱의 대한 소개는 &lt;a href=&quot;https://github.com/myeong-jae-hwi/Griffindormitory&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;깃허브 링크&lt;/a&gt;에서 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 진행하며 뷰에 대해서 정말 많이 배웠다. 이론을 인강으로 공부했는데 생각보다 크게 어려웠던 부분이 없었다. 섹션마다 과제가 나오는데 과제도 어렵지 않게 풀 수 있었고 다른 방향으로도 생각을 할 수 있게 되니 뭔가 자신감이 엄청 있었다. 그런데 직접 부딪혀보니 이론과 실전은 정말 많이 다르다는 것을 깨달았다 ㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 인강을 들으며 뷰에 어떤 기능이 있는지, 오류가 발생했을 때 왜 이런 오류가 났는지에 대해서는 확실히 배웠다. 프로젝트를 하며 문제가 생겼을 때 아 이거 쓰면 되겠는데? 하는 생각을 했다는 것 만으로도 뿌듯했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;느낀 점&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉬웠던 점은 프로젝트 중 팀원들과 단기 알바를 하며 진행이 멈췄던 적도 있고, 내가 자격증 공부를 하느라 멈춰진 적도 있다보니 다시 시작하기가 너무 힘들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꾸준히 할 때에는 속도도 붙고 의욕이 불탔었는데 잠시 쉬다오니 다시 감을 잡는데에도 시간이 조금 걸린 느낌이 있었다.&amp;nbsp;사이드 프로젝트라지만 데드라인을 가지고 하는게 더 완성도 높은 결과가 나오지 않을까 하는 아쉬움이 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 회원가입 부분에 원래는 대학교 인증을 넣으려고 했었다. 해당 API 까지 알아봤지만 Java와 코틀린만 지원한다는 문제가 있어 넣지 못했다. 그래서 모든 기능을 최대한 프론트에서 구현했고, 그로 인한 보안문제도 있을거라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 팀에 백엔드 개발자가 있었으면 어땠을까 하는 아쉬움도 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋았던 점은 앞에서도 언급한 것처럼 이론에서 배울 수 없었던 문제들을 많이 겪었고, 많이 배웠다. 항상 프로젝트를 하며 내가 한 층 성장했다는 느낌을 받는다. 이 경험으로 다음 프로젝트는 조금 더 편하게 만들 수 있겠다는 생각을 했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 나는 완성 후 전체적인 디자인을 맡았었는데 완성 한 뒤에는 너무 뿌듯했다 ㅋㅋ 전체적인 레이아웃을 바꾸며 불필요한 코드는 제거하고 개선하는 재미도 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 다음에는 우리 학교 과팅 어플을 만들어 볼까 한다. 기간은 1달 정도로 생각하고 있고 백엔드를 다루던 친구와 함께 해보고 싶은 욕심이 생겼다. 열심히 해서 실사용자가 있는 어플을 만들어보고싶다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;</description>
      <category>Project</category>
      <category>fe 사이드 프로젝트</category>
      <category>사이드프로젝트</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/142</guid>
      <comments>https://re-hwi.tistory.com/142#entry142comment</comments>
      <pubDate>Fri, 30 Aug 2024 00:52:19 +0900</pubDate>
    </item>
    <item>
      <title>2024 정보처리기사 실기 (2) 데이터베이스 구축</title>
      <link>https://re-hwi.tistory.com/141</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;데이터 언어&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DDL (데이터 정의어)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CREATE: 테이블 생성&lt;/li&gt;
&lt;li&gt;DROP: 테이블 삭제&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: justify;&quot;&gt;ALTER: 데이터 변경&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;DML (데이터 조작어)&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SELECT: 행 선택&lt;/li&gt;
&lt;li&gt;UPDATE: 행 수정&lt;/li&gt;
&lt;li&gt;DELETE: 행 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DCL (데이터 제어어)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: justify;&quot;&gt;GRANT: 권한 부여&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: justify;&quot;&gt;&lt;span&gt; &lt;span style=&quot;color: #000000; text-align: justify;&quot;&gt;REVOKE: 권한 해제&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: justify;&quot;&gt;&lt;span&gt; &lt;span style=&quot;color: #000000; text-align: justify;&quot;&gt;COMMIT: 작업 저장&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: justify;&quot;&gt;&lt;span&gt; &lt;span style=&quot;color: #000000; text-align: justify;&quot;&gt;ROLLBACK: 원래 상태로 되돌리기&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스키마&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 스키마: 사용자의 관점에서 DB의 논리적 구조를 담당 ex) 웹툰에서 요일별 웹툰&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념 스키마: DB의 전체적인 구조를 담당, 하나의 DB에는 &lt;b&gt;하나의 개념 스키마&lt;/b&gt;가 존재&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 스키마: 물리적인 데이터 구성을 명세하는 스키마&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;데이터 독립성&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;논리적 독립성: 외부스키마와 개념 스키마 사이에서의 독립성&lt;/li&gt;
&lt;li&gt;물리적 독립성: 개념 스키마와 내부 스키마 사이에서의 독립성&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;데이터 모델링&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적 모델&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;E-R다이어그램&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논리적 모델&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관계모델&lt;/li&gt;
&lt;li&gt;계층모델&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물리적 모델&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;레코드 형식, 순서 ...&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 모델 표시해야 할 요소&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구조: 데이터 구조 및 개체 관계&lt;/li&gt;
&lt;li&gt;연산: 데이터 처리 방법&lt;/li&gt;
&lt;li&gt;제약조건: 데이터의 논리적 제약조건&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;개체-관계모델 (E-R Model)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 데이터베이스의 요구사항을 그래픽적으로 표현하는 모델&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;개체&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 현실 세계의 독립적이고 구별 가능한 대상&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;속성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 개체나 관계의 특성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;관계&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 두 개체간의 연결&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;928&quot; data-origin-height=&quot;303&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfUM1k/btsIGr9odQ4/CqN3n2d2WrB5U1yeYz2Jb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfUM1k/btsIGr9odQ4/CqN3n2d2WrB5U1yeYz2Jb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfUM1k/btsIGr9odQ4/CqN3n2d2WrB5U1yeYz2Jb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfUM1k%2FbtsIGr9odQ4%2FCqN3n2d2WrB5U1yeYz2Jb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;523&quot; height=&quot;171&quot; data-origin-width=&quot;928&quot; data-origin-height=&quot;303&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;산출물: E-R다이어그램&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;데이터 베이스 정규화&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 데이터 &lt;b&gt;중복을&lt;/b&gt; &lt;b&gt;최소화&lt;/b&gt; 하기위함&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제 1정규형: 도메인이 원자값&lt;/li&gt;
&lt;li&gt;제 2정규형: 부분적 함수 종속 제거&lt;/li&gt;
&lt;li&gt;제 3정규형: 이행적 함수 종속 제거&lt;/li&gt;
&lt;li&gt;BCNF: 결정자이면서 후보키가 아닌 것 제거&lt;/li&gt;
&lt;li&gt;제 4정규형: 다치종속 제거&lt;/li&gt;
&lt;li&gt;제 5정규형: 조인 종속&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;반 정규화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 성능 향상을 위해 의도적으로 정규화에 반하는 기법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 베이스 백업&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTO: 복구 시간 목표 (~~까지는 반드시 복구를 해야함)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RPO: 복구 시점 목표 (데이터 손실량의 마지노선)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관계 데이터 모델&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;관계대수&lt;/b&gt; &amp;gt; 절차적&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;관계해석&lt;/b&gt; &amp;gt; 비절차적&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;키와 무결성 제약조건&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키의 종류&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;후보키: &lt;b&gt;유일성&lt;/b&gt;과 &lt;b&gt;최소성&lt;/b&gt; 만족&lt;/li&gt;
&lt;li&gt;기본키: 후보키 중 선택한 주 키. &lt;b&gt;NULL을 가질 수 없고 중복된 값을 가질 수 없다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;대체키: 기본키로 선택되지 않은 나머지 후보키&lt;/li&gt;
&lt;li&gt;슈퍼키: &lt;b&gt;유일성&lt;/b&gt;은 만족하지만 최소성은 만족하지 않음&lt;/li&gt;
&lt;li&gt;외래키: 다른 릴레이션의 기본 키를 참조하는 속성&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CRUD&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Create (생성)&lt;/li&gt;
&lt;li&gt;Read (읽기)&lt;/li&gt;
&lt;li&gt;Update (업데이트)&lt;/li&gt;
&lt;li&gt;Delete (삭제)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;트랜잭션&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜잭션의 성질&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원자성: 모든 연산은 &lt;b&gt;모두 반영&lt;/b&gt;되거나 &lt;b&gt;전혀 반영되지 않아야&lt;/b&gt; 한다.&lt;/li&gt;
&lt;li&gt;일관성: 트랜잭션 수행전/후의 데이터베이스 상태는 일관되어야 한다.&lt;/li&gt;
&lt;li&gt;독립성: 동시에 실행되는 트랜잭션은 서로 간섭할 수 없으며 독립적으로 실행되어야 한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;영속성: 트랜잭션이 한 번 커밋되면 고장이 발생해도 영구적으로 반영되어야 한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜잭션의 상태&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;활동: 트랜잭션이 실행중인 상태&lt;/li&gt;
&lt;li&gt;부분완료: 트랜잭션의 마지막 연산까지 실행했지만 커밋하기 전 상태&lt;/li&gt;
&lt;li&gt;실패: 트랜잭션 실행에 오류가 생겨 중단된 상태&lt;/li&gt;
&lt;li&gt;완료: 커밋까지 완료한 상태&lt;/li&gt;
&lt;li&gt;철회: 트랜잭션이 비정상적으로 종료되어 원래 상태로 돌아간 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n4KI6/btsIKBYtzkw/iEAwdSUbZFHPCWksPrgwek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n4KI6/btsIKBYtzkw/iEAwdSUbZFHPCWksPrgwek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n4KI6/btsIKBYtzkw/iEAwdSUbZFHPCWksPrgwek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn4KI6%2FbtsIKBYtzkw%2FiEAwdSUbZFHPCWksPrgwek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;388&quot; height=&quot;279&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>etc/이것저것</category>
      <category>정보처리기사</category>
      <category>정보처리기사 실기</category>
      <category>정처기</category>
      <category>정처기 데이터베이스</category>
      <category>정처기 실기</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/141</guid>
      <comments>https://re-hwi.tistory.com/141#entry141comment</comments>
      <pubDate>Wed, 24 Jul 2024 19:54:17 +0900</pubDate>
    </item>
    <item>
      <title>정보처리기사 실기 (1) 소프트웨어 구조 요약</title>
      <link>https://re-hwi.tistory.com/140</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;제가 중요하다고 생각되는 것만 요약한 글이므로 자세한 내용이 없을 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;소프트 웨어 공학의 3R&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;역공학&lt;br /&gt;: 이미 개발된 시스템을 분석&lt;/li&gt;
&lt;li&gt;재공학&lt;br /&gt;: 시스템의 기능을 개선하거나 재구성&lt;br /&gt;ex) 건물의 문이 고장났을 때 건물 전체를 고치지 않고 문만 고치는 것과 같음&lt;/li&gt;
&lt;li&gt;재사용&lt;br /&gt;: 이미 개발된 소프트웨어의 전체 또는 일부를 다시 사용하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;소프트웨어 개발 단계&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;계획&lt;/li&gt;
&lt;li&gt;요구사항 분석&lt;/li&gt;
&lt;li&gt;설계&lt;/li&gt;
&lt;li&gt;구현&lt;/li&gt;
&lt;li&gt;테스트&lt;/li&gt;
&lt;li&gt;유지보수&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;개발 방법론&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;구조적 방법론&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 절차지향적인 개발 방법론&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;애자일 방법론&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 빠르고 유연하게 대응하는 개발 방식&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애자일 방법론 종류&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; XP: 반복적이고 점진적인 개발&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt;&amp;gt; XP 의 특징: 용기, 존중, 의사소통, 피드백, 단순성 (&lt;b&gt;의사&lt;/b&gt;선생님 약은 &lt;b&gt;피&lt;/b&gt; &lt;b&gt;존 용기&lt;/b&gt;에 &lt;b&gt;담&lt;/b&gt;아주세요)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; SCRUM: 유연하고 생산적인 프로젝트 관리방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;소프트웨어 개발 모델&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;폭포수 모델&lt;br /&gt;분석&amp;gt;설계&amp;gt;개발&amp;gt;테스트&amp;gt;유지보수의 단계로 진행&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;나선형 모델&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;분석&amp;gt;위험분석&amp;gt;개발&amp;gt;평가&lt;/li&gt;
&lt;li&gt;V모형&lt;br /&gt;개발 단계에 따른 테스트를 중점으로 진행&amp;nbsp;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;383&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2QE2h/btsIDujEamb/4XuplAN9ifjXVkT3xVOGDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2QE2h/btsIDujEamb/4XuplAN9ifjXVkT3xVOGDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2QE2h/btsIDujEamb/4XuplAN9ifjXVkT3xVOGDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2QE2h%2FbtsIDujEamb%2F4XuplAN9ifjXVkT3xVOGDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;411&quot; height=&quot;271&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;383&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프로젝트 관리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 핵심 관리대상(3P)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사람&lt;/li&gt;
&lt;li&gt;문제&lt;/li&gt;
&lt;li&gt;프로세스&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PMBOK&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프로젝트 착수&lt;/li&gt;
&lt;li&gt;프로젝트계획&lt;/li&gt;
&lt;li&gt;프로젝트 실행&lt;/li&gt;
&lt;li&gt;프로젝트 통제&lt;/li&gt;
&lt;li&gt;프로젝트 종료&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;개발 비용 산정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기법&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LOC&lt;br /&gt;: 예측치 = 낙관치 + 4(기대치) + 비관치&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;COCOMO&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;- 조직형 (Organic) :&amp;nbsp;&lt;br /&gt;- 반분리형 (Semidetached) :&amp;nbsp;&lt;br /&gt;- 내장형 (Embedded) :&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;요구 공학&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요구사항 분석도구&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HIPO&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 하향식 소프트웨어 개발을 위한 문서화 도구&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;요구사항 분석 모델링&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능적 모델링&lt;br /&gt;&amp;gt; 액티비티 다이어그램&lt;/li&gt;
&lt;li&gt;정적모델링&lt;br /&gt;&amp;gt; 클래스 다이어그램&lt;/li&gt;
&lt;li&gt;동적모델링&lt;br /&gt;&amp;gt; 순서 다이어그램, 상태 다이어그램&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;소프트웨어 설계&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 설계의 원리&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분할 및 정복&lt;/li&gt;
&lt;li&gt;추상화&lt;/li&gt;
&lt;li&gt;분해&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모듈화&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;정보 은닉&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;결합도와 응집도&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;설계 모델링&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구조 모델링&lt;br /&gt;: UML 정적 다이어그램&lt;/li&gt;
&lt;li&gt;행위 모델링&lt;br /&gt;: UML 동적 다이어그램&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;UML&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;구성요소&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사물&lt;/li&gt;
&lt;li&gt;관계&lt;/li&gt;
&lt;li&gt;다이어그램&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;UI 설계&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유형&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CLI : 키보드로 조작&lt;/li&gt;
&lt;li&gt;GUI : 그래픽으로 조작&lt;/li&gt;
&lt;li&gt;NUI : 인간의 움직임으로 조작&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설계 도구&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;와이어프레임 : 기본 구조와 레이아웃을 나타내는 설계도구&lt;/li&gt;
&lt;li&gt;스토리보드 : 시나리오 흐름을 시각적으로 나타내는 도구&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;형상관리 도구&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전관리 도구&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공유폴더 방식 (RCS, SCCS)&amp;nbsp;&lt;/li&gt;
&lt;li&gt;클라이언트/서버 방식 (CVS, SVN)&lt;/li&gt;
&lt;li&gt;분산 저장소 방식 (Git)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;모듈 구현&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;결합도&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자료 결합도&lt;br /&gt;: 값이 전달되는 경우&lt;/li&gt;
&lt;li&gt;스탬프 결합도&lt;br /&gt;: 배열, 오브젝트등이 전달&lt;/li&gt;
&lt;li&gt;제어 결합도&lt;br /&gt;: 제어 요소가 전달되는 경우 (if같은거)&amp;nbsp;&lt;/li&gt;
&lt;li&gt;외부 결합도&lt;br /&gt;: 다른 모듈에서 선언한 변수를 다른 모듈에서 참조하는 경우&lt;/li&gt;
&lt;li&gt;공통 결합도&lt;br /&gt;: 전역변수를 참조하고 갱신하는 경우&lt;/li&gt;
&lt;li&gt;내용 결합도&lt;br /&gt;: 다른 모듈 내부에 있는 변수나 기능을 사용하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; &lt;b&gt;응집도&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능적 응집도&lt;br /&gt;: 모듈 내부 기능이 단일한 목적을 위해 수행되는 경우&lt;/li&gt;
&lt;li&gt;순차적 응집도&lt;br /&gt;: 모듈 내에서 나온 출력값을 다른 활동이 사용할 경우&lt;/li&gt;
&lt;li&gt;통신적 응집도&lt;br /&gt;: 동일한 입출력을 사용하여 다른 기능을 수행하는 활동들이 모여 있을 경우&lt;/li&gt;
&lt;li&gt;절차적 응집도&lt;br /&gt;: 모듈 안에 구성요소들이 순차적으로 수행할 경우 &amp;lt;- 순차라는 말이 들어갔다고 순차적 X&lt;/li&gt;
&lt;li&gt;시간적 응집도&lt;br /&gt;: 특정 시간에 처리되어야 하는 활동&lt;/li&gt;
&lt;li&gt;논리적 응집도&lt;br /&gt;: 유사한 성격을 갖거나 특정 형태로 분률되는 요소들이 한 모듈에서 처리되는 경우&lt;/li&gt;
&lt;li&gt;우연적 응집도&lt;br /&gt;: 모듈 내 구성요소들이 연관 없을 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Pan-in, Pan-out&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B에서 Pan-in은 A 1개, Pan-out은 E,F 2개&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;453&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p7AcL/btsIFbKWCNM/JIf92eqCiK8BZkJOIM4JUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p7AcL/btsIFbKWCNM/JIf92eqCiK8BZkJOIM4JUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p7AcL/btsIFbKWCNM/JIf92eqCiK8BZkJOIM4JUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp7AcL%2FbtsIFbKWCNM%2FJIf92eqCiK8BZkJOIM4JUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;252&quot; data-origin-width=&quot;453&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;인터페이스 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;전송 데이터&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Json : 값과 쌍으로 이루어짐, JS 기반&lt;/li&gt;
&lt;li&gt;XML : 마크업 언어&amp;nbsp;&lt;/li&gt;
&lt;li&gt;YAML : 데이터 직렬화 언어&lt;/li&gt;
&lt;li&gt;CSV : 쉼표로 구분되는 데이터 형식, excel&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인터페이스 구현&lt;/b&gt;&lt;br /&gt;AJAX&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: JS 를 사용해 서버와 브라우저가 비동기 방식으로 데이터를 교환할 수 있는 통신기능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SOAP&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: XML 기반 프로토콜 (XML + 프로토콜 나오면 SOAP)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WSDL&lt;br /&gt;: 웹 서비스 기술언어 (XML + 언어 나오면 WSDL)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 자원을 이름으로 구분하여 해당 자원의 상태를 주고받음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;객체지향 구현&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;객체지향 설계원칙 (SOILD)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단일 책임 원칙&lt;br /&gt;: 한 클래스는 하나의 책임만을 가져야 함&lt;/li&gt;
&lt;li&gt;개방 폐쇄 원칙&lt;br /&gt;: 확장에 대해서는 열려있어야 하고 수정에 대해서는 닫혀있어야 함&lt;/li&gt;
&lt;li&gt;리스코프 치환 원칙&lt;br /&gt;: 자식 클래스는 언제나 부모 클래스를 대체 할 수 있어야 함&lt;/li&gt;
&lt;li&gt;인터페이스 분리 원칙&lt;br /&gt;: 자신이 사용하지 않는 인터페이스는 만들지 않아야 함&lt;/li&gt;
&lt;li&gt;의존성 역전 원칙&lt;br /&gt;: 의존관계를 맺을 때 변화가 없는것에 의존해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;디자인 패턴&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;추상 팩토리 : 서로 연관되거나 의존적인 객체들의 조합을 만드는 인스턴스를 제공&lt;br /&gt;빌더 : 동일한 생성 절차에서도 다른 표현이 가능&lt;br /&gt;팩토리 메서드 : 객체 생성을 서브 클래스로 위임하여 캡슐화&lt;br /&gt;싱글톤 : 클래스의 인스턴스가 하나임을 보장&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;구조
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;어댑터 : 클래스의 인터페이스를 다른 인터페이스로 변환&lt;br /&gt;브리지 : 구현부에서 추상층을 분리하여 독립적인 확장이 가능&lt;br /&gt;프록시 : 객체의 대리를 제공&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;행위
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;옵서버 : 객체의 상태변화를 관찰&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;테스트&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;테스트 오라클&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참 오라클 : 모든 입력값에 정확한 결과를 생성하는 오라클&lt;/li&gt;
&lt;li&gt;샘플링 오라클 : 제한된 입력값 들에 예상되는 결과를 제공하는 오라클&lt;/li&gt;
&lt;li&gt;휴리스틱 오라클 : 특정 입력값에는 정확한 결과값을 제공하나 그 외의 값에는 근사값을 제공&lt;/li&gt;
&lt;li&gt;일관성 검사 오라클 : 소프트웨어의 변경 전/후로 일관성을 검증하는 오라클&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;테스트 레벨&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;383&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2QE2h/btsIDujEamb/4XuplAN9ifjXVkT3xVOGDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2QE2h/btsIDujEamb/4XuplAN9ifjXVkT3xVOGDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2QE2h/btsIDujEamb/4XuplAN9ifjXVkT3xVOGDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2QE2h%2FbtsIDujEamb%2F4XuplAN9ifjXVkT3xVOGDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;411&quot; height=&quot;271&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;383&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;블랙박스 테스트&lt;/b&gt; : 내부 코드를 보지 않고 입출력 만으로 테스트하는 기법&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동등분할기법 : 입력자료에 초점을 맞춰검사&lt;/li&gt;
&lt;li&gt;경계값 분석 : 입력값의 경계값 ex) 100까지 입력 받을 수 있는 코드에 99,100,101&amp;nbsp;&lt;/li&gt;
&lt;li&gt;원인-효과 그래프 : 데이터의 관계와 출력에 영향을 미치는 상황을 분석한 뒤 효용성이 높은 테스트를 선정&lt;/li&gt;
&lt;li&gt;오류예측 : 테스터의 감각으로 테스트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;화이트 박스 테스트&lt;/b&gt; : 내부 코드를 보며 데이터의 흐름 등을 테스트하는 기법&lt;/p&gt;</description>
      <category>etc/이것저것</category>
      <category>정보처리기사</category>
      <category>정보처리기사 기출</category>
      <category>정보처리기사 소프트웨어 공학</category>
      <category>정보처리기사 요약</category>
      <category>정처기</category>
      <category>정처기 기출</category>
      <category>정처기 소프트웨어 공학</category>
      <category>정처기실기</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/140</guid>
      <comments>https://re-hwi.tistory.com/140#entry140comment</comments>
      <pubDate>Thu, 18 Jul 2024 20:39:26 +0900</pubDate>
    </item>
    <item>
      <title>Vue.js를 이용한 '몬스터 슬레이어' 게임 만들기 후기</title>
      <link>https://re-hwi.tistory.com/139</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 단원에서는 그동안 배웠던 주요 기능들을 이용해서 프로젝트를 진행했다. 주요 로직이 예전에 인턴생활을 하며 Java로 만들었던 RPG 게임과 유사해서 쉽게 만들 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임의 내용은 닌텐도 포켓몬스터 게임처럼 턴제 게임이며 공격, 스킬, 힐 등 다양한 기능을 사용할 수 있게 제작되었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 기능은 다음과 같고, 스킬은 3턴에 한 번만 사용 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;kakaotv&quot; data-video-url=&quot;https://tv.kakao.com/v/447553694&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/bcmLR5/hyWoNWL7Ob/hvR776naU2dHEuF71nYPH1/img.jpg?width=1920&amp;amp;height=1020&amp;amp;face=0_0_1920_1020,https://scrap.kakaocdn.net/dn/dBtyaE/hyWlc4YU9m/3qbOzJ3dEQaFkfRUaxWyvk/img.jpg?width=1920&amp;amp;height=1020&amp;amp;face=0_0_1920_1020&quot; data-video-width=&quot;860&quot; data-video-height=&quot;457&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;457&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-play-service=&quot;daum_tistory&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://play-tv.kakao.com/embed/player/cliplink/447553694?service=daum_tistory&quot; width=&quot;860&quot; height=&quot;457&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 속성 부분에는 Data 를 이용해 체력, 턴, 우승자 등을 선언했고, computed에서는 스타일이나 html에 들어가 있을법한 연산을 넣었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나머지 기능은 메서드에서 구현했으며, watch를 이용해 현재 체력을 감시해 0이 되었을 때 승자를 반환하는 코드도 작성했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임의 전체적인 흐름은 몬스터와 플레이어가 한 턴씩 공격하고, 그로 인한 승패를 결정하는 구조이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;느낀 점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 작성하며 크게 어려운 점은 없었고, methods와 computed의 차이점을 이론적으로는 알고 있었지만 실제로 어떨 때 사용하는지는 헷갈렸었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분에 대해서는 다시 한 번 실습을 통해 정확히 짚고 넘어갈 수 있었다. 또한, watch의 사용에 대해서도 이전보다 확실히 이해할 수 있었던 것 같다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end/Vue.js</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/139</guid>
      <comments>https://re-hwi.tistory.com/139#entry139comment</comments>
      <pubDate>Wed, 19 Jun 2024 16:59:24 +0900</pubDate>
    </item>
    <item>
      <title>v-if와 v-for</title>
      <link>https://re-hwi.tistory.com/138</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 단원에서는 Vue에서 if와 for를 사용하는 방법에 대해 배웠다. 일단 문법은 다른 언어들과 같아서 익숙했지만, html 코드 내에 작성하는게 뭔가 맘에 안든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 보통 html에서 태그 속에 뭐 넣는 걸 안좋아하는데 익숙해지면 괜찮아질 것 같기도 하다. 암튼 뭐 되게 간결하고 사용법도 쉽고, Js에서 코드 길게 안짜도 편리하다는 점은 정말 유용하다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;v-if&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뷰에서 사용하는 if 문이다. html 태그 내에서 사용하며, 큰따옴표 안에 Js 코드를 작성한다. 아래는 goals라는 배열이 비어있다면 해당 텍스트를 나타내는 코드이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718437253889&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p v-if=&quot;goals.length === 0&quot;&amp;gt;목표가 설정되지 않았습니다. 목표를 설정해주세요.&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;v-else&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뷰에서 사용하는 else이다. if 바로 아래에서 사용해야 하며, 중간에 다른 태그가 들어가면 실행되지않는다. 아래 코드는 goals 배열이 0이 아니라면 Goal 이라는 텍스트가 리스트 요소로 출력된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718437554064&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p v-if=&quot;goals.length === 0&quot;&amp;gt;목표가 설정되지 않았습니다. 목표를 설정해주세요.&amp;lt;/p&amp;gt;
// 이 사이에 뭐가 들어가면 안된다.
&amp;lt;ul v-else&amp;gt;
  &amp;lt;li&amp;gt; Goal &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;v-for&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뷰에서 사용하는 for문이다. 마찬가지로 기존 for 문과 유사하게 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718437745933&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;li v-for=&quot;(i) in goals&quot;&amp;gt;
  {{ i }} 
&amp;lt;/li&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 사용한다면 goals 배열에 있는 요소(i)를 출력한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 요소의 인덱스 출력하기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1718437960649&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;li v-for=&quot;(i,idx) in goals&quot;&amp;gt;
  {{idx}}. {{ i }} 
&amp;lt;/li&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;괄호안에 다른 변수와 함께 사용한다면 해당 요소의 인덱스를 가져올 수 있다. 이렇게 작성하면 &quot;0. 뷰 공부하기&quot; 이런 결과를 얻을 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4dMZv/btsHZtfRR7z/KscAmDsKc2e3FsKk9vG8XK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4dMZv/btsHZtfRR7z/KscAmDsKc2e3FsKk9vG8XK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4dMZv/btsHZtfRR7z/KscAmDsKc2e3FsKk9vG8XK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4dMZv%2FbtsHZtfRR7z%2FKscAmDsKc2e3FsKk9vG8XK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;559&quot; height=&quot;116&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;169&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 객체에 루프 걸기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1718438517018&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
  &amp;lt;li v-for=&quot;i in {name: 'Jaehwi', age: 25}&quot;&amp;gt;
    {{ i }}
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9gGmj/btsHZ8hRhMr/xvHKU3KkjIWnIDATZcmfJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9gGmj/btsHZ8hRhMr/xvHKU3KkjIWnIDATZcmfJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9gGmj/btsHZ8hRhMr/xvHKU3KkjIWnIDATZcmfJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9gGmj%2FbtsHZ8hRhMr%2FxvHKU3KkjIWnIDATZcmfJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;595&quot; height=&quot;121&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;166&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 사용한다면 '값' 만 출력된다. (Jaehwi, 25) 하지만 이전에 인덱스를 출력했을 때 처럼 변수를 하나 추가해준다면 키를 뽑을 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718438666008&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;li v-for=&quot;(i,key) in {name: 'Jaehwi', age: 25}&quot;&amp;gt;{{key}}: {{ i }}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;183&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxmyBy/btsHZUEcEAw/Gkv1HyszQfPay5FkOvvNWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxmyBy/btsHZUEcEAw/Gkv1HyszQfPay5FkOvvNWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxmyBy/btsHZUEcEAw/Gkv1HyszQfPay5FkOvvNWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxmyBy%2FbtsHZUEcEAw%2FGkv1HyszQfPay5FkOvvNWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;135&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;183&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 숫자에 루프 걸기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1718438997721&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;li v-for=&quot;i in 10&quot;&amp;gt; {{ i }} &amp;lt;/li&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 숫자에 루프를 걸 수도 있다. 그러면 i는 1부터 해당 숫자까지 출력한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;771&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MHFUI/btsH0V21UwY/g0h7lNmFakJwlGwk4xgEWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MHFUI/btsH0V21UwY/g0h7lNmFakJwlGwk4xgEWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MHFUI/btsH0V21UwY/g0h7lNmFakJwlGwk4xgEWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMHFUI%2FbtsH0V21UwY%2Fg0h7lNmFakJwlGwk4xgEWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;447&quot; height=&quot;427&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;771&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end/Vue.js</category>
      <category>JavaScript</category>
      <category>js</category>
      <category>vue</category>
      <category>리액트</category>
      <category>뷰</category>
      <category>뷰 공부</category>
      <category>프론트엔드</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/138</guid>
      <comments>https://re-hwi.tistory.com/138#entry138comment</comments>
      <pubDate>Sat, 15 Jun 2024 17:11:26 +0900</pubDate>
    </item>
    <item>
      <title>computed와 watch</title>
      <link>https://re-hwi.tistory.com/137</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 단원에서는 data와 methods 의 대해 배웠다. 이번 단원에는 Vue의 다른 개념인 computed와 watch에 대해 공부했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 설명하자면 computed는 연산형 데이터? 라고 말 할 수 있을 것 같고 watch는 데이터가 바뀔 때 알 수 있으니 디버깅할 때 좋을 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;computed는 데이터에 직접 연산을 넣으면 가독성이 떨어지고, 효율이 낮아질 수 있으니 따로 분리하는 것이라고 이해했고, watch는 그냥 디버깅 하위호환이라고 이해했다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; computed (연산 프로퍼티)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 연산 작업이 필요한 변수를 데이터처럼 관리하기 위함.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;➡️ 즉, 재사용성의 증가, 코드의 가독성 및 유지보수 작업이 향상된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드와 다른 점: 다른 데이터에 의존하는 데이터에 주로 사용. 해당 속성이 &lt;b&gt;종속된 대상이 변경될 때&lt;/b&gt;만 함수를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;➡️&amp;nbsp;메서드는 렌더링이 되었을 때 실행된다. 따라서 복잡한 계산이 있을 시에 성능이 저하된다. 반면 computed는 해당 종속 데이터가 변경되었을 때만 실행된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718435212766&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;new Vue({
  el: '#app',
  data: {
    firstName: 'John',
    lastName: 'Doe'
  },
  computed: {
    fullName: function() {
      return `${this.firstName} ${this.lastName}`;
    }
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;watch (감시자)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;: 데이터의 변경을 감지하기 위해 사용.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;➡️데이터가 변경되었을 때 &lt;b&gt;비동기&lt;/b&gt; 작업을 수행할 수 있다. ex)&amp;nbsp; API 호출&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 프로퍼티를 업데이트 하지만 항상 업데이트 하면 안되는 로직을 실행하려는 경우 watch 를 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718435190906&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;new Vue({
  el: '#app',
  data: {
    message: '',
    response: ''
  },
  watch: {
    message: function(newVal, oldVal) {
      console.log('message가 변경되었습니다:', newVal);
      // 비동기 작업 예시
      this.fetchResponse(newVal);
    }
  },
  methods: {
    fetchResponse: function(query) {
      // 예시 API 호출 (실제 API로 대체)
      setTimeout(() =&amp;gt; {
        this.response = `Response for query: ${query}`;
      }, 1000);
    }
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end/Vue.js</category>
      <category>FE</category>
      <category>js</category>
      <category>vue</category>
      <category>리액트</category>
      <category>뷰</category>
      <category>프론트엔드</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/137</guid>
      <comments>https://re-hwi.tistory.com/137#entry137comment</comments>
      <pubDate>Sat, 15 Jun 2024 15:45:25 +0900</pubDate>
    </item>
    <item>
      <title>Vue를 이용한 DOM 상호작용</title>
      <link>https://re-hwi.tistory.com/135</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 단원 강의를 들으며 뷰의 기초 문법에 대해 공부했다. 전에 리액트를 한 번 조금 공부해 본 적이 있어서 비슷하게 공부하면 되겠지라는 생각으로 일단 강의를 듣고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딱히 현재까지 어려운 부분은 없었고, vue를 호출하기 전 특정 커맨드를 위주로 문법을 공부했다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;패키지 불러오기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뷰의 커맨드를 불러오기 위해서는 패키지를 먼저 index 파일에 불러왔다. 패키지 경로는 아래와 같다.(24.05.18 기준)&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716023187265&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    &amp;lt;script src=&quot;https://unpkg.com/vue@3.4.9/dist/vue.global.js&quot; defer&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;컴포넌트 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뷰를 사용하기 위해 뷰 컴포넌트를 생성해야 한다. 따라서 createApp() 메소드를 이용해서 컴포넌트를 생성한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716023478472&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const app = Vue.createApp()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;앱 마운트 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성한 코드를 html의 어느 부분에 렌더링 할지 컨테이너를 선택한다. 보통 해당 컨테이너의 id를 주로 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716023677508&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.mount('컨테이너의 ID');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;보간법 및 메소드 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보간법이란 Vue에서 생성한 텍스트, 숫자 등 데이터를 html에 직접 넣는 것을 말한다. 데이터와 메소드는 최상위 컴포넌트에서 생성하며 html 코드에 넣을 때에는 {{}} (중괄호 2개) 를 이용해서 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 data 형식의 리턴은 반드시 object 형식이어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;html&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1716024008605&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p&amp;gt;{{ Goal }}&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Js&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1716024031973&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const app = Vue.createApp({
  data() {
    return {
      Goal: 'Learn Vue!' ,
    };
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서는 Vue 컴포넌트에서 생성한 data를 직접 html 코드에 넣었다. 하지만 태그가 아닌 속성값에 data를 넣고 싶다면 v-bind 를 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;html&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1716024124556&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p&amp;gt;Learn more &amp;lt;a v-bind:href=&quot;vueLink&quot;&amp;gt;about Vue&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Js&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1716024155940&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const app = Vue.createApp({
  data() {
    return {
      vueLink: 'https://vuejs.org/'
    };
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전까지는 object로 되어 있는 데이터를 직접 입력 했지만 메소드를 입력하는 방법도 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;html&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1716024668394&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p&amp;gt;{{ outputGoal }}&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Js&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1716024726081&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const app = Vue.createApp({
  methods: {
    outputGoal() {
      const randomNumber = Math.random();
      if (randomNumber &amp;lt; 0.5) {
        return 'Master Vue!';
      } else {
        return 'Learn Vue!';
      }
    }
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end/Vue.js</category>
      <category>vue</category>
      <category>vue.js</category>
      <category>뷰</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/135</guid>
      <comments>https://re-hwi.tistory.com/135#entry135comment</comments>
      <pubDate>Sat, 18 May 2024 18:32:48 +0900</pubDate>
    </item>
    <item>
      <title>Vue 시작하기</title>
      <link>https://re-hwi.tistory.com/134</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Vue.js 카테고리에 있는 모든 글은 Udemy (Vue 완벽가이드) 강의에서 정리 및 요약 간접 인용한 것임을 밝힙니다.&amp;nbsp;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front end/Vue.js</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/134</guid>
      <comments>https://re-hwi.tistory.com/134#entry134comment</comments>
      <pubDate>Sat, 18 May 2024 17:55:20 +0900</pubDate>
    </item>
    <item>
      <title>캡스톤 디자인 - 분석 및 설계</title>
      <link>https://re-hwi.tistory.com/133</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 GPT AI 비서의 이름은 '미르' 라고 정했다. 올해가 청룡의 해이기도 하고 미르라는 어감도 좋아서 선택했다. 그동안 중간 발표를 위해서 팀원들과 여러번 만나 미르의 기능에 대해 이야기를 많이 했었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용을 요약하자면 음성인식 및 출력 기능, GPT API를 따온 인공지능, 오늘 날짜와 날씨를 알려주는 기능, 음악 재생 기능 이렇게 4가지를 최우선시 하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 모든게 다 API 인것 같아서 이렇게 해도 되나 라는 생각이 들었지만 각각의 기능을 조화롭게 묶는 것 또한 개발자의 역량 이라고 생각해서 한결 편하게 진행할 수 있었던 것 같다. ㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 기능을 어떻게 구현할지에 대한설명은 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;음성인식 및 출력 (STT/TTS)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음성인식은 speech_recognition 라이브러리를 사용 할 예정이다. 처음에는 구글 STT API 를 사용하려 했으나 실시간 음성 인식이 필요하기에 해당 라이브러리를 선택했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 코드를 찾아서 실행해 본 결과 음성 인식률은 굉장히 좋았다. 그런데 음성의 끝을 잘 구분하지 못하는 것 같다. 내가 문단을 끝내도 주변 잡음이 있거나 다른 여러 사람이 말하고 있으면 계속해서 인식한다. 이 문제가 프로젝트의 가장 큰 걸림돌이 될거라고 생각한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; GPT API를 따온 인공지능&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저의 입력을 매개변수로 받고 GPT의 답변을 반환하는 메서드를 만들어 STT 처리가 된 텍스트를 GPT 에 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 오늘 날짜와 날씨를 알려주는 기능&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜와 날씨 API를 받아서 유저가 날짜 혹은 날씨에 대해 물었을 때 답변할 수 있도록 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단은 이렇게 먼저 구현을 하기로 했다 더 필요한 기능이 있다면 그때그때 빠르게 이야기 해보고 추가할 생각이다.&lt;/p&gt;</description>
      <category>Project</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/133</guid>
      <comments>https://re-hwi.tistory.com/133#entry133comment</comments>
      <pubDate>Sun, 21 Apr 2024 12:17:12 +0900</pubDate>
    </item>
    <item>
      <title>캡스톤 디자인 - 주제선정</title>
      <link>https://re-hwi.tistory.com/132</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;4학년에 올라오면서 캡스톤 디자인을 하게 되었다. 다른 친구들은 내가 인턴을 나갔던 이전학기에 조를 나눈 상태여서 나는 모르는 사람 2명과 조를 이루어 하게되었다. 그래도 두분 다 성격도 좋으신것 같고 분업도 잘 된것 같아서 나름 첫 만남은 성공적이었던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 우리가 만들게 될 작품은 'Chat GPT를 이용한 인공지는 비서' 이다. 흔히 알고있는 기가지니에 GPT를 합체한 느낌이라고 생각하면 된다. 조에 3D 모델링을 전문적으로 하는 분이 있어서 외형은 최대한 귀여운 캐릭터로 만들 생각이고 고개를 돌려 이야기하는 사람을 쳐다보는 기능도 추가할 생각이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;필요 부품&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 필요할것이라고 생각되는 것들은 3D 프린터용 레진, 라즈베리 파이, 서보모터, 스피커, 마이크 이렇게 생각했는데 필요한게 나올 때마다 추가할 예정이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예상 핵심 기능&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재까지 생각한 기능으로는 소리를 이용해 소리가 나는 위치를 찾아내는 물체 추적 기술, TTS, GPT (인공지능), 다른 소리와 사람의 소리를 구별하는 음성인식 기술이 중점이 될 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;느낀 점&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지는 프로젝트를 시작하기에 앞서 설계를 하는 단계인 것 같다. 우리는 고객이 없으므로 요구사항 분석 단계를 아이템 선정이라 생각하고 회의를 통해 적당한 아이템을 찾아냈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에 작품이 기술적으로 어떻게 작동할 것인지, 어떤 이점이 있을지에 대한 생각을 해보며 진행되는 대로 블로그에 업로드 할 생각이다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Project</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/132</guid>
      <comments>https://re-hwi.tistory.com/132#entry132comment</comments>
      <pubDate>Sat, 9 Mar 2024 18:42:29 +0900</pubDate>
    </item>
    <item>
      <title>2024 1회 정보처리기사 필기 합격 후기</title>
      <link>https://re-hwi.tistory.com/131</link>
      <description>&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_심플 미니멀리스트 동기부여 여행 메시지 인스타그램 포스트.png&quot; data-origin-width=&quot;1049&quot; data-origin-height=&quot;888&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cr9XlT/btsEXptw7lV/I4vjKvcOnmChwE0AVNLsE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cr9XlT/btsEXptw7lV/I4vjKvcOnmChwE0AVNLsE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cr9XlT/btsEXptw7lV/I4vjKvcOnmChwE0AVNLsE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcr9XlT%2FbtsEXptw7lV%2FI4vjKvcOnmChwE0AVNLsE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;558&quot; height=&quot;558&quot; data-filename=&quot;edited_심플 미니멀리스트 동기부여 여행 메시지 인스타그램 포스트.png&quot; data-origin-width=&quot;1049&quot; data-origin-height=&quot;888&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요 2024 정보처리기사 1회 필기 합격 후기입니다!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정보처리기사 필기시험은 문제은행 형식이라 가능한 cbt에서 &lt;b&gt;문제를 많이 푸는 것&lt;/b&gt;을 추천합니다. 시험때도 cbt에서 풀었던 문제들이 나오므로 회차별로 5회 이상씩 풀고 해설과 함께 문제 자체를 외워버리면 쉽게 합격 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;++ 시험 점수는 시험이 끝난 후 가채점 점수 및 합격여부가 나오게 됩니다. 공식 발표는 3얼 13일에 나오므로 결과 캡쳐본은 3월 13일에 올리도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;시험 정보  &lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 시험은 2020년 개정 이후 총 &lt;b&gt;5단원&lt;/b&gt;으로 이루어져 있습니다. 단원별로 별표친 부분은 CBT 모든 회차에 나왔을 정도로 많이 나오는 유형이니까 많이 풀어주세요&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;소프트웨어 설계 (★☆ ☆ ☆ ☆ ) * 애자일 / 사용자 요구사항&lt;/li&gt;
&lt;li&gt;소프트웨어 개발 (★☆&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;☆&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;☆&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;☆ )&amp;nbsp;&lt;/li&gt;
&lt;li&gt;데이터베이스 구축 (★ ★ &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;☆&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;☆&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;☆ ) * 정규화&lt;/li&gt;
&lt;li&gt;프로그래밍 언어 활용 (★ ★ ★ ★ &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;☆ ) * 코드계산 문제&amp;nbsp;&lt;/li&gt;
&lt;li&gt;정보 시스템 구축관리 (★ ★ ★ ★ ☆ ) * OSI 7계층&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난이도는 제 기준으로 점수를 매겼고 주변 친구들도 비슷하게 4,5 단원이 비교적 어려웠다고 하길래 참고만 해주세요.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 단원은 20문제씩 총 &lt;b&gt;100문제&lt;/b&gt;로 구성되어 있으며 각 단원당 40점 (8문제) 이하는 과락으로 불합격 처리가 됩니다. 합격 기준은 과락 없이 &lt;b&gt;평균 60점 이상&lt;/b&gt;이면 합격입니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 5단원이 제일 어려웠던 것 같았고 4단원도 C언어 포인터 부분은 버린다는 생각으로 공부를 하지 않아서 1,2,3단원에서 점수를 최대한 많이 받고 4,5 단원은 과락만 하지 말자는 생각으로 준비를 했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;공부 방법 ✏️&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;준비 기간은 대충 2주 조금 안되게 했던 것 같고 모르는 부분은 시나공에서 무료로 제공하는 토막 강의를 들으며 공부했습니다. 시나공 강의는 &lt;a href=&quot;https://www.sinagong.co.kr/vod#0302&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;에서 들을 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 개념서를 읽기보다는 무한 문제풀이를 추천합니다. 먼저 &lt;a href=&quot;https://www.comcbt.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CBT&lt;/a&gt;에서 단원별로 문제를 풀어보고 맞은문제 틀린문제 모두 해설을 읽습니다. 해설이 부족한 경우에는 시나공 강의를 들으면서 가볍게 넘어갑니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 풀 때에는 어차피 모르는 문제만 나오기 때문에 한문제씩 풀기를 이용해서 해설을 보며 문제 푸는 것을 추천합니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E2UkM/btsEV8eLpvy/iqFsxOIMkSwKTTXsaPpJUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E2UkM/btsEV8eLpvy/iqFsxOIMkSwKTTXsaPpJUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E2UkM/btsEV8eLpvy/iqFsxOIMkSwKTTXsaPpJUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE2UkM%2FbtsEV8eLpvy%2FiqFsxOIMkSwKTTXsaPpJUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;578&quot; height=&quot;343&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;694&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 한 번 같은 회차를 풀어보고 문제를 눈에 익히는 것이 중요합니다. 어차피 문제은행 식이기 때문에 나왔던 문제가 그대로 나오는 경우도 있으며 비슷한 유형이 나오기 때문에 모르는 문제가 나오더라도 감이 오게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어느정도 시험 점수가 잘 나오게 된다면 저는 하루에 3회차씩은 모의고사를 풀었습니다. CBT 모의고사에서 80점 이상 컷이 나온다면 웬만하면 안전하게 합격할 수 있으니까 너무 긴장하시지 마시고 시험 잘 보고 오시길 바랍니다 !&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두들 합격하길 기원합니다 :)&lt;/p&gt;</description>
      <category>etc/이것저것</category>
      <category>정보처리기사</category>
      <category>정보처리기사 필기</category>
      <category>정보처리기사 후기</category>
      <category>정보처리자격증</category>
      <category>정처기</category>
      <category>정처기 필기</category>
      <category>정처기 후기</category>
      <category>정처기시험</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/131</guid>
      <comments>https://re-hwi.tistory.com/131#entry131comment</comments>
      <pubDate>Sat, 17 Feb 2024 22:21:17 +0900</pubDate>
    </item>
    <item>
      <title>티스토리 블로그 꾸미기 (3)</title>
      <link>https://re-hwi.tistory.com/130</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 이전에 변경한 사이드바 카테고리 및 글을 정렬하는 방식을 변경했다. 먼저 지난 글에서 바뀐점은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;변경된 점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사이드바 카테고리&lt;/li&gt;
&lt;li&gt;깃허브 카드 플립효과 추가&lt;/li&gt;
&lt;li&gt;글 정렬 디자인 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 사이드바의 카테고리 디자인을 현재 UI에 맞게 수정했다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p03lI/btsDHrYIcaU/gNWr84BktVQtjKq5vqsnu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p03lI/btsDHrYIcaU/gNWr84BktVQtjKq5vqsnu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p03lI/btsDHrYIcaU/gNWr84BktVQtjKq5vqsnu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp03lI%2FbtsDHrYIcaU%2FgNWr84BktVQtjKq5vqsnu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;621&quot; height=&quot;423&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 카테고리에 마우스를 가져다 대면 해당 카테고리가 선택되었다는 효과가 나타난다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkI96i/btsDCWe52xQ/2C26Q47Wz59hR6biliDIKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkI96i/btsDCWe52xQ/2C26Q47Wz59hR6biliDIKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkI96i/btsDCWe52xQ/2C26Q47Wz59hR6biliDIKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkI96i%2FbtsDCWe52xQ%2F2C26Q47Wz59hR6biliDIKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;544&quot; height=&quot;407&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 깃허브 카드가 있던 박스에 내 명함을 넣고, 마우스를 가져다 대면 깃허브 카드로 뒤집히게 변경했다. 클릭시 내 깃허브로 이동하게 만들었다.&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;kakaotv&quot; data-video-url=&quot;https://tv.kakao.com/v/444007562&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/0Au2h/hyU8QfD0mv/qrvAOW8E210LakAffjHmt1/img.png?width=273&amp;amp;height=154&amp;amp;face=0_0_273_154&quot; data-video-width=&quot;369&quot; data-video-height=&quot;208&quot; data-video-origin-width=&quot;273&quot; data-video-origin-height=&quot;154&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-play-service=&quot;daum_tistory&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://play-tv.kakao.com/embed/player/cliplink/444007562?service=daum_tistory&quot; width=&quot;369&quot; height=&quot;208&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 글의 정렬 디자인을 변경했다. 이 때 스크롤을 하며 페이드인 되는 효과를 구현했고, 화면에 없을 때에는 사라지도록 만들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;kakaotv&quot; data-video-url=&quot;https://tv.kakao.com/v/444007646&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/hsNvZ/hyU8P160iM/5BfD67dEE85cMfK3N8rtcK/img.png?width=273&amp;amp;height=154&amp;amp;face=0_0_273_154&quot; data-video-width=&quot;273&quot; data-video-height=&quot;154&quot; data-video-origin-width=&quot;273&quot; data-video-origin-height=&quot;154&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-play-service=&quot;daum_tistory&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://play-tv.kakao.com/embed/player/cliplink/444007646?service=daum_tistory&quot; width=&quot;273&quot; height=&quot;154&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 내가 처음 구상했던 디자인은 모두 구현한 것 같다. 앞으로 어떤 부분을 수정해야할 지는 가끔씩 좋은 생각이 날 때 마다 수정하고, 일단은 곧 있을 정보처리 기사 필기 공부를 시작하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대충 한 달 정도 남은 것 같은데 기출만 무한반복할 생각이라 정보처리 기사 공부를 하며 포스팅을 할 지는 잘 모르겠다. 일단 블로그 스킨을 만들며 현업에서 사용하는 코드를 뜯어 봤다는 것 자체에 의의를 두고 블로그 꾸미기를 마친다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ 내가 바꾸고 싶은 부분이 생각날 때마다 수정할 생각이므로 블로그 꾸미기 추가 업로드는 가끔 할 생각이다. 생각해보니 아직 글 내부도 안꾸며서 더 할듯 ㅋ&lt;/p&gt;</description>
      <category>Project</category>
      <category>블로그 꾸미기</category>
      <category>티스토리 블로그</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/130</guid>
      <comments>https://re-hwi.tistory.com/130#entry130comment</comments>
      <pubDate>Thu, 18 Jan 2024 18:47:47 +0900</pubDate>
    </item>
    <item>
      <title>티스토리 블로그 꾸미기 (2)</title>
      <link>https://re-hwi.tistory.com/129</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 어제에 이어서 계속해서 티스토리 스킨을 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들면서 느낀건데 세상에서 디자인이 제일 어려운 것 같다. 디자인만 어떻게 완성되어 있으면 금방 하겠는데 다 만들고 나면 맘에 안들어서 엎고 다시하는 경우가 많았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 나름 지금까지는 맘에 드니 다행이지만 제작 기간이 길어지면 또 바뀔 수 있을 것 같아서 최대한 빨리 끝내보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;미리보기&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;kakaotv&quot; data-video-url=&quot;https://tv.kakao.com/v/443685280&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/qWdmt/hyUXWhoTh7/n6VrVceX4mn7h9OREJqpJk/img.jpg?width=1920&amp;amp;height=1020&amp;amp;face=0_0_1920_1020,https://scrap.kakaocdn.net/dn/qUMGK/hyUXM6VV9g/W63qH8ikqwj3kKKa5ESoxK/img.jpg?width=1920&amp;amp;height=1020&amp;amp;face=0_0_1920_1020&quot; data-video-width=&quot;860&quot; data-video-height=&quot;457&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;457&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-play-service=&quot;daum_tistory&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://play-tv.kakao.com/embed/player/cliplink/443685280?service=daum_tistory&quot; width=&quot;860&quot; height=&quot;457&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 어제 못했던 글 내부에 자리를 차지하고 있던 area-common 을 없앴다. 메인에 집중하다보니 글 내부의 코드가 어떻게 되어 있는지 몰랐는데 글 내부에는 tt-body-page 라는 클래스가 있다는 것을 놓쳤다.&amp;nbsp;그래서 클래스를 추가하며 쉽게 고칠 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 깃허브 바로가기를 만들었다. 먼저 전체 컨테이너를 a 태그로 감싸고 svg를 이용해 다크모드를 했을 때 색상을 반전 시켰다. 그리고 커서를 포인터로 해서 클릭할 수 있도록 유도했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 생각해도 저거 너무 밋밋한것 같아서 마우스를 올리면 카드가 돌아가는 효과를 줄까 생각중이다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;551&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oks0u/btsC6wNeRog/zfQtIfINPmkSVidmyLUrK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oks0u/btsC6wNeRog/zfQtIfINPmkSVidmyLUrK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oks0u/btsC6wNeRog/zfQtIfINPmkSVidmyLUrK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Foks0u%2FbtsC6wNeRog%2FzfQtIfINPmkSVidmyLUrK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;551&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;551&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 글에 들어갔을 때 패딩이 들어가 있으면 정작 글이 들어가야 할 컨테이너가 너무 작아졌다. 그래서 반응형으로 가로 길이가 1061px 이상일 때에는 패딩을 주고, 그렇지 않으면 패딩을 빼서 글을 보기 편하도록 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;936&quot; data-origin-height=&quot;761&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/co1xea/btsC6Sinywy/fzHKYycomNEXjnCG5XgQHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/co1xea/btsC6Sinywy/fzHKYycomNEXjnCG5XgQHK/img.png&quot; data-alt=&quot;창 사이즈가 작을 때&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/co1xea/btsC6Sinywy/fzHKYycomNEXjnCG5XgQHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fco1xea%2FbtsC6Sinywy%2FfzHKYycomNEXjnCG5XgQHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;412&quot; height=&quot;335&quot; data-origin-width=&quot;936&quot; data-origin-height=&quot;761&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;창 사이즈가 작을 때&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;897&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cg35DG/btsC30O4PwI/qWh2ZCyRPCr72kBkTQck4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cg35DG/btsC30O4PwI/qWh2ZCyRPCr72kBkTQck4k/img.png&quot; data-alt=&quot;창 사이즈가 클 때&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cg35DG/btsC30O4PwI/qWh2ZCyRPCr72kBkTQck4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcg35DG%2FbtsC30O4PwI%2FqWh2ZCyRPCr72kBkTQck4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;599&quot; height=&quot;283&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;897&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;창 사이즈가 클 때&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 마지막 컨테이너에 방문자를 넣을 예정인데 디자인을 어떻게 해야할 지 고민이다. 원래 냅다 관리 창에 있는 그래프 가져오려고 했는데 그래도 티스토리에서 제공해 주는 값으로 막대 그래프를 만들까 생각중이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;859&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHPVKt/btsC6TawyPy/k35gnAjSLCECEByB0MZyw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHPVKt/btsC6TawyPy/k35gnAjSLCECEByB0MZyw0/img.png&quot; data-alt=&quot;관리창에 있는거&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHPVKt/btsC6TawyPy/k35gnAjSLCECEByB0MZyw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHPVKt%2FbtsC6TawyPy%2Fk35gnAjSLCECEByB0MZyw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;303&quot; height=&quot;309&quot; data-origin-width=&quot;859&quot; data-origin-height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;관리창에 있는거&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;티스토리에서는 어제 방문자, 오늘 방문자, 총 방문자 이렇게 3가지 값을 주는데 차차 생각해보고 내일 하든가 해야겠다!&amp;nbsp;&lt;/p&gt;</description>
      <category>Project</category>
      <category>블로그 디자인</category>
      <category>스킨</category>
      <category>웹디자인</category>
      <category>티스토리 스킨</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/129</guid>
      <comments>https://re-hwi.tistory.com/129#entry129comment</comments>
      <pubDate>Thu, 4 Jan 2024 17:42:45 +0900</pubDate>
    </item>
    <item>
      <title>티스토리 블로그 꾸미기 (1)</title>
      <link>https://re-hwi.tistory.com/128</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;최근 학교에서 나갔던 인턴 기간이 끝나며 나도 드디어 종강을 하게 되었다. 그래서 이 방학기간동안 티스토리 블로그 스킨을 만들어 보려고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이쁘게 만들면 내 포트폴리오가 되는거고 아니면 그냥 실력 쌓았다고 생각하면서 무지성 시작을 했다. 그런데 시작하자마자 디자인을 어떻게 해야할지 멘붕이 왔었다. 그래서 여러 사람들의 티스토리를 보며 영감을 얻어야겠다고 생각했다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그를 정말 예쁘게 꾸민 분들도 많았고, 다른 사람들이 사용한 방법을 보며 여러 디자인을 생각해 보았다. 나는 내가 사용할 수 있는 기술들을 블로그에 조화롭게 넣는 것을 목표로 프로젝트를 시작했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ 기본 스킨은 Odyssey를 사용했습니다. &amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;354&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ziJJd/btsCX693fSh/jlD1mKKrWswxxW6eaZqNv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ziJJd/btsCX693fSh/jlD1mKKrWswxxW6eaZqNv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ziJJd/btsCX693fSh/jlD1mKKrWswxxW6eaZqNv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FziJJd%2FbtsCX693fSh%2FjlD1mKKrWswxxW6eaZqNv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;217&quot; height=&quot;363&quot; data-origin-width=&quot;354&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;초기 디자인&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 디자인 툴을 하나도 다루지 못해 냅다 ppt로 시작했다. 그 때는 포인트 색을 파란색으로 잡고 뉴모피즘으로 경계를 부드럽게 만들었다.&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;kakaotv&quot; data-video-url=&quot;https://tv.kakao.com/v/443663793&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/ktORQ/hyUXKuduH0/PkC4tMDGNQkbgCfymGnMTK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/MnV6w/hyUXY0heD6/0uLKQjTe45kl0enR1BChpK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-play-service=&quot;daum_tistory&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://play-tv.kakao.com/embed/player/cliplink/443663793?service=daum_tistory&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 초기 디자인을 잡고 코드를 작성해 보았는데 둥근 모서리가 웹브라우저에 맞지 않아 굉장히 킹받는 상황이 발생했다. 그래서 아예 배경을 주고 그 위에 요소를 올려놓는 방식으로 바꾸었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 코드를 짜면서 뭔가 맘에 안드는 부분은 그때그때 바꾸다보니 아래와 같은 디자인이 만들어졌다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1437&quot; data-origin-height=&quot;772&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4kwdc/btsC4IfLfzT/g7mhKaY97Ctf2TlLuwxKPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4kwdc/btsC4IfLfzT/g7mhKaY97Ctf2TlLuwxKPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4kwdc/btsC4IfLfzT/g7mhKaY97Ctf2TlLuwxKPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4kwdc%2FbtsC4IfLfzT%2Fg7mhKaY97Ctf2TlLuwxKPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;864&quot; height=&quot;486&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1437&quot; data-origin-height=&quot;772&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이드바를 왼쪽으로 몰았고, 메인 컨테이너를 글래스 모피즘을 이용해 만들었다. 그 후 그리드 레이아웃을 이용해서 레이아웃을 잡은 후 다크모드도 추가했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에 사이드바에 있는 요소들을 전부 삭제할 예정이다. 일단 혹시 모르니 아직 삭제 안했다 ㅋ&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;908&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/79wPT/btsC1FREcB7/5wIBHgdu8eu1POWgospJv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/79wPT/btsC1FREcB7/5wIBHgdu8eu1POWgospJv0/img.png&quot; data-alt=&quot;레이아웃 잡고 다크모드 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/79wPT/btsC1FREcB7/5wIBHgdu8eu1POWgospJv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F79wPT%2FbtsC1FREcB7%2F5wIBHgdu8eu1POWgospJv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1898&quot; height=&quot;908&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;908&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;레이아웃 잡고 다크모드 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이제 여기서 문제가&lt;span&gt; area-common을 그리드로 잡고 내부에 박스 3개를 만들었는데 글을 들어갔을 때 area-common이 자리를 차지한다는 것이다. 그래서 글이 엄청 아래에서 시작한다. 이거 도대체 어떻게 없애는지 몰라서 일단 오늘은 빡종했다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;779&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UB2yb/btsCUAjyLun/KS8uylEak5bvhdImy4a7V1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UB2yb/btsCUAjyLun/KS8uylEak5bvhdImy4a7V1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UB2yb/btsCUAjyLun/KS8uylEak5bvhdImy4a7V1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUB2yb%2FbtsCUAjyLun%2FKS8uylEak5bvhdImy4a7V1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;786&quot; height=&quot;779&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;779&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;html, css, js 밖에 모르는 상태로 시작했습니다. 부족한 부분이 있다면 댓글로 알려주세요 감사합니다  &lt;/p&gt;</description>
      <category>Project</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/128</guid>
      <comments>https://re-hwi.tistory.com/128#entry128comment</comments>
      <pubDate>Wed, 3 Jan 2024 18:49:04 +0900</pubDate>
    </item>
    <item>
      <title>[ChatGPT] 명언만들기</title>
      <link>https://re-hwi.tistory.com/127</link>
      <description>&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;회사에서 남는 시간을 이용해서 간단한 명언만들기 웹사이트를 만들어보았다. 사실 서버와 클라이언트를 연결하는 방법을 잘 몰라서 일단 하면서 배우자는 생각으로 했던거였는데 생각보다 금방 만들 수 있어서 재미있었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;먼저 간단한 설명으로는 서버에서는 GPT api를 이용해서 검색어와 비슷한 명언을 만들어 달라고 요청한 뒤 프론트에 전송하고 프론트에서는 띄우는 간단한 웹사이트이다.&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;미리보기&lt;/b&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;kakaotv&quot; data-video-url=&quot;https://tv.kakao.com/v/441126197&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/bMscdp/hyTVVRDzKt/gkezdp9zZfw5GJ7beDnhK0/img.jpg?width=1920&amp;amp;height=1020&amp;amp;face=1380_645_1461_735,https://scrap.kakaocdn.net/dn/9warF/hyTV1xz0oX/LBl3bnkLf7vYMrl9wGEGKK/img.jpg?width=1920&amp;amp;height=1020&amp;amp;face=1380_645_1461_735&quot; data-video-width=&quot;860&quot; data-video-height=&quot;457&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;457&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-play-service=&quot;daum_tistory&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://play-tv.kakao.com/embed/player/cliplink/441126197?service=daum_tistory&quot; width=&quot;860&quot; height=&quot;457&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;간단한 원리는 아래 그림과 같다. 클라이언트가 검색어를 입력하면 서버에서 GPT에게 프롬프트를 추가해 GPT로 보낸다. 그 후에는 GPT에서 보낸 대답을 서버를 통해 클라이언트에 나타내게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;673&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmn5fk/btsuGl03lKi/BPokyDOTMN9h7KMDRO5jiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmn5fk/btsuGl03lKi/BPokyDOTMN9h7KMDRO5jiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmn5fk/btsuGl03lKi/BPokyDOTMN9h7KMDRO5jiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbmn5fk%2FbtsuGl03lKi%2FBPokyDOTMN9h7KMDRO5jiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;474&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;673&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;다음은 실행 코드이다.&lt;br /&gt;&lt;b&gt;Front (HTML)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div class = &quot;header&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;a href=&quot;#&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;띵언 만들기
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/a&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;\

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div class=&quot;container&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;ul class=&quot;sidebar&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;li class=&quot;sidebar-brand&quot;&amp;gt;&amp;lt;a href=&quot;&quot;&amp;gt;Sidebar&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Dashboard&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Support&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;About&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Logout&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/ul&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div class=&quot;content&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div class=&quot;wrap&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div class=&quot;search&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;input id = &quot;subject&quot; type=&quot;text&quot; class=&quot;searchTerm&quot; placeholder=&quot;ex) 돈, 시간..&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;button id = &quot;searchBtn&quot;, type=&quot;submit&quot; class=&quot;searchButton&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;img src=&quot;img/search.png&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/button&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/div&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;div id=&quot;dataContainer&quot;&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/div&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;div class=&quot;img&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;img src=&quot;img/human.jpg&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/div&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div class = &quot;footer&quot;&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;script src=&quot;aaaaaa.js&quot;&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Front (JS)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const searchInput = document.getElementById('subject');
const searchButton = document.getElementById('searchBtn');
const dataContainer = document.getElementById('dataContainer');
document.addEventListener(&quot;keydown&quot;, eheckKey, false);

// 버튼클릭
searchButton.addEventListener('click', () =&amp;gt; {
&amp;nbsp;&amp;nbsp;const searchText = searchInput.value;
&amp;nbsp;&amp;nbsp;if (searchText) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fetchData(searchText);
&amp;nbsp;&amp;nbsp;}
});

// 엔터키
function eheckKey(e) {
&amp;nbsp;&amp;nbsp;const searchText = searchInput.value;
	if (e.keyCode === 13) {
		fetchData(searchText)
	}
}

// 서버 통신 (fetch 사용)
async function fetchData(searchText) {
&amp;nbsp;&amp;nbsp;try {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const response = await fetch(`/api/getData?search=${searchText}`);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const data = await response.json();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dataContainer.innerHTML = `&amp;lt;h1&amp;gt; ${data.result}&amp;lt;/h1&amp;gt;`;
&amp;nbsp;&amp;nbsp;} catch (error) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.error('Error:', error);
&amp;nbsp;&amp;nbsp;}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;fetchData 함수를 이용해서 서버에 데이터 (검색어)를 보낸다. 이 떄 엔터키를 누르거나, 돋보기 아이콘을 누르게 되면 이 함수가 실행된다.&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;Back (node.js)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const OpenAI = require('openai');

const express = require(&quot;express&quot;);
const app = express();
const PORT = 8080;

app.use(express.static(&quot;front&quot;));

const openai = new OpenAI({
&amp;nbsp;&amp;nbsp;apiKey: 'API_KEY', 
});


async function main() {

&amp;nbsp;&amp;nbsp;app.get(&quot;/api/getData&quot;, async (req, res) =&amp;gt; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const searchText = req.query.search;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// api/getData?search=[여기있는 변수 추출]&amp;nbsp;&amp;nbsp;(search도 변수명임 보낼때 서치로 보내서 받을때도 같은 텍스트로 받음)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (!searchText) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;res.status(400).json({ error: &quot;검색어를 입력해주세요.&quot; });
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const completion = await openai.chat.completions.create({
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;messages: [
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{ role: 'system', content: '당신은 명언 제조기 입니다. 사 자의 입력에 따라 그에 해당하는 한 줄짜리 명언만을 대답해야합니다. 명언은 최대한 짧게 만들어야 합니다.' },
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{ role: 'user', content: searchText }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;model: 'gpt-3.5-turbo',
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;res.json({ result: completion.choices[0].message.content }); // 클라이언트에 데이터 전송
&amp;nbsp;&amp;nbsp;});

}

app.listen(PORT, () =&amp;gt; {
&amp;nbsp;&amp;nbsp;console.log(`Server is running on port ${PORT}`);
});

main();&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/127</guid>
      <comments>https://re-hwi.tistory.com/127#entry127comment</comments>
      <pubDate>Mon, 18 Sep 2023 17:48:14 +0900</pubDate>
    </item>
    <item>
      <title>파이썬을 이용한 리듬게임 만들기</title>
      <link>https://re-hwi.tistory.com/126</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 종합 아케이드 게임기를 만들며 리듬게임을 만들어 보았다. 사실 그렇게 완성도도 높지 않고 아직 허술하지만 팀원과 함께 만들면서 재미도 있었고, 많은 노력이 들어갔던 만큼 완성했을 때 뿌듯함도 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 노래선택을 할 수 없고, 메인 창이 없어서 되돌아가기도 못하기 때문에 그렇게 완성도 있는 작품이라고 하기엔 조금 허술한 점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 가장 핵심이 되는 노트가 내려오는 타이밍은 파이썬의 라이브러리인 librosa를 사용했다. 이 라이브러리를 사용하면 음악의 특징을 뽑아내서 템포에 맞춰 노트를 생성하는게 가능하다. 여기서 추가로 음의 변화에 따라 노트를 생성하고 싶었는데 라이브러리에 미숙한 탓인지 실패했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1127&quot; data-origin-height=&quot;767&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/leVJY/btskRumifLg/DSdZGF6h6awTZgkxFCZPi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/leVJY/btskRumifLg/DSdZGF6h6awTZgkxFCZPi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/leVJY/btskRumifLg/DSdZGF6h6awTZgkxFCZPi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FleVJY%2FbtskRumifLg%2FDSdZGF6h6awTZgkxFCZPi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;659&quot; height=&quot;448&quot; data-origin-width=&quot;1127&quot; data-origin-height=&quot;767&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 내가 코드를 작성했던 순서이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 노트가 내려오는 트랙 그리기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 노트 클래스 만들기 &amp;gt; 트랙이 아래로 내려 올 수록 넓어지기 때문에 노트의 높이와 넓이 조절&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. librosa 라이브러리를 이용해 음악의 템포 추출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 그 템포에 맞춰 노트 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 노트가 일정 범위 안에 들어왔을 때 버튼을 누르면 이펙트가 생기며 점수 증가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 5가지로 나누었고 다음은 메인 화면이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-21 152011.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzMvCl/btskRP4OfLh/DJLswFqi25lViUn3LtE941/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzMvCl/btskRP4OfLh/DJLswFqi25lViUn3LtE941/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzMvCl/btskRP4OfLh/DJLswFqi25lViUn3LtE941/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzMvCl%2FbtskRP4OfLh%2FDJLswFqi25lViUn3LtE941%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;572&quot; height=&quot;322&quot; data-filename=&quot;스크린샷 2023-06-21 152011.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 코드와 실행 영상이다.&lt;/p&gt;
&lt;pre id=&quot;code_1687328474581&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pygame
import sys
import random
import librosa
import time


def Rithomgame_run():

    pygame.init()

    h, w = 600, 900
    screen = pygame.display.set_mode((w, h))
    font = pygame.font.SysFont(&quot;Courier&quot;, 18, True, True)
    text_color = (255, 255, 255)

    song = &quot;sound/gd.mp3&quot;  # replace with your song file

    pygame.mixer.music.load(song)

    # Load the song using librosa
    y, sr = librosa.load(song, sr=None)
    tempo, beat_frames = librosa.beat.beat_track(y=y, sr=sr)
    beats_timing = librosa.frames_to_time(beat_frames, sr=sr)

    current_beat = 0
    pygame.mixer.music.play()

    class Block:
        # 초기값
        def __init__(self, x, y, dx):
            self.x = x
            self.y = y
            self.dx = dx
            self.width = 50
            self.created_time = time.time()
            self.hit = False

            # 업데이트
        def update(self):
            self.y += 1
            self.x += self.dx
            self.width = int(50 + (150 - 50) * (self.y / h))  # 블록의 크기 업데이트

        def draw(self):
            pygame.draw.rect(screen, (255, 255, 255), (self.x, self.y, self.width, 15))

        def process_note(self, key):
            if not self.hit and 500 &amp;lt; self.y &amp;lt; 650:
                if key == 'left' and self.dx &amp;lt; 0:
                    self.hit = True
                    if 550 &amp;lt; self.y &amp;lt; 650:
                        return 30
                    elif 500 &amp;lt; self.y &amp;lt; 550:
                        return 15
                elif key == 'mid':
                    self.hit = True
                    if 550 &amp;lt; self.y &amp;lt; 650:
                        return 30
                    elif 500 &amp;lt; self.y &amp;lt; 550:
                        return 15
                elif key == 'right' and self.dx &amp;gt; 0:
                    self.hit = True
                    if 550 &amp;lt; self.y &amp;lt; 650:
                        return 30
                    elif 500 &amp;lt; self.y &amp;lt; 550:
                        return 15

            if not self.hit and 450 &amp;lt; self.y &amp;lt; 500:
                if key == 'left' and self.dx &amp;lt; 0:
                    self.hit = True
                    return 10
                elif key == 'mid':
                    self.hit = True
                    return 10
                elif key == 'right' and self.dx &amp;gt; 0:
                    self.hit = True
                    return 10
            return 0

    def lerp_color(color1, color2, factor):
        return (
            int(color1[0] + (color2[0] - color1[0]) * factor),
            int(color1[1] + (color2[1] - color1[1]) * factor),
            int(color1[2] + (color2[2] - color1[2]) * factor)
        )

    blocks = []
    tracks = [(250, 0, -0.24), (300, 0, -0.08), (350, 0, 0.08)]  # 트랙별 시작 위치와 x 이동 속도
    start_time = time.time() - 1  # Add this line to initialize start_time

    # 이펙트 색
    blue_points = [(300, 600), (250, 0), (400, 0), (350, 600)]

    Right_points = [(350, 600), (400, 0), (548, 0), (400, 600)]
    Left_points = [(250, 600), (108, 0), (252, 0), (300, 600)]
    black = (0, 0, 0)
    blue = (0, 0, 255)
    purple = (255, 0, 0)
    rect_width = 2

    Left = False
    Mid = False
    Right = False

    score = 0
    last_block_creation_time = time.time()

    running = True

    # 게임 루프
    while running:
        screen.fill((0, 0, 0))  # 화면을 검은색으로 지우기
        for event in pygame.event.get():
            if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
                running = False

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_DOWN:
                    Mid = True
                    for block in blocks:
                        score += block.process_note('mid')
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_DOWN:
                    Mid = False

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    Left = True
                    for block in blocks:
                        score += block.process_note('left')
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_LEFT:
                    Left = False

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT:
                    Right = True
                    for block in blocks:
                        score += block.process_note('right')
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_RIGHT:
                    Right = False

            print(&quot;점수 : &quot;, score)

        if Mid:
            for i in range(blue_points[1][1], blue_points[0][1], rect_width):
                progress = (i - blue_points[1][1]) / (blue_points[0][1] - blue_points[1][1])
                color = lerp_color(black, blue, progress)
                top_left_x = int(blue_points[0][0] + (blue_points[1][0] - blue_points[0][0]) * progress)
                top_right_x = int(blue_points[3][0] + (blue_points[2][0] - blue_points[3][0]) * progress)

                pygame.draw.rect(screen, color, pygame.Rect(top_left_x, i, top_right_x - top_left_x, rect_width))

        if Right:
            for i in range(Right_points[1][1], Right_points[0][1], rect_width):
                progress = (i - Right_points[1][1]) / (Right_points[0][1] - Right_points[1][1])
                color = lerp_color(black, purple, progress)
                top_left_x = int(Right_points[0][0] + (Right_points[1][0] - Right_points[0][0]) * progress)
                top_right_x = int(Right_points[3][0] + (Right_points[2][0] - Right_points[3][0]) * progress)

                pygame.draw.rect(screen, color, pygame.Rect(top_left_x, i, top_right_x - top_left_x, rect_width))

        if Left:
            for i in range(Left_points[1][1], Left_points[0][1], rect_width):
                progress = (i - Left_points[1][1]) / (Left_points[0][1] - Left_points[1][1])
                color = lerp_color(black, purple, progress)
                top_left_x = int(Left_points[0][0] + (Left_points[1][0] - Left_points[0][0]) * progress)
                top_right_x = int(Left_points[3][0] + (Left_points[2][0] - Left_points[3][0]) * progress)

                pygame.draw.rect(screen, color, pygame.Rect(top_left_x, i, top_right_x - top_left_x, rect_width))

        # 사다리꼴 라인 그리기
        pygame.draw.line(screen, (255, 0, 255), (250, 0), (100, 640), 5)
        pygame.draw.line(screen, (255, 0, 255), (400, 0), (550, 640), 5)
        pygame.draw.line(screen, (1, 85, 255), (300, 0), (250, 640), 3)
        pygame.draw.line(screen, (1, 85, 255), (350, 0), (400, 640), 3)

        for block in blocks:
            block.update()
            block.draw()

        # 스코어 생성
        score_text = font.render(f&quot;Score: {score}&quot;, True, text_color)

        screen.blit(score_text, (600, 50))
        pygame.display.flip()

        elapsed_time = time.time() - start_time
        current_time = time.time() + 0.5
        if current_time - last_block_creation_time &amp;gt;= 0.8:
            block_x, _, block_dx = random.choice(tracks)
            blocks.append(Block(block_x, 0, block_dx))
            last_block_creation_time = current_time

    pygame.quit()
    sys.exit()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;kakaotv&quot; data-video-url=&quot;https://tv.kakao.com/v/438974018&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/kHesl/hyS4n1TbZd/JqH4h0v82OPYkUqZlG5Kf1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/RprwF/hyS4sB95dR/Rgq8qZzVod3LbzmWNdWwgk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-play-service=&quot;daum_tistory&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://play-tv.kakao.com/embed/player/cliplink/438974018?service=daum_tistory&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project</category>
      <category>리듬게임</category>
      <category>파이썬 프로젝트</category>
      <category>파이썬게임</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/126</guid>
      <comments>https://re-hwi.tistory.com/126#entry126comment</comments>
      <pubDate>Wed, 21 Jun 2023 15:22:30 +0900</pubDate>
    </item>
    <item>
      <title>라즈베리 파이를 이용한 게임기 만들기</title>
      <link>https://re-hwi.tistory.com/125</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 어제 종강을 했다. 그동안 기말 프로젝트 때문에 시간도 없고 블로그도 안쓴지 너무 오래되어서 이번 기말 프로젝트로 만들었던 게임기를 포스팅해보려 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하드웨어는&amp;nbsp; 라즈베리파이와 스크린을 이용해 화면을 구성했고 스피커를 달아 사운드를 추가했다.. 또 게임기의 핵심인 컨트롤러는 3D 프린터를 이용해 외관을 구성하고 내부는 버튼, 조이스틱, LED 로 꾸몄다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_KakaoTalk_20230621_142552342.jpg&quot; data-origin-width=&quot;2250&quot; data-origin-height=&quot;2280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dc07ra/btskRprziso/syHX4BdPx5JKAGdMtQQPlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dc07ra/btskRprziso/syHX4BdPx5JKAGdMtQQPlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dc07ra/btskRprziso/syHX4BdPx5JKAGdMtQQPlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdc07ra%2FbtskRprziso%2FsyHX4BdPx5JKAGdMtQQPlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;407&quot; height=&quot;412&quot; data-filename=&quot;edited_KakaoTalk_20230621_142552342.jpg&quot; data-origin-width=&quot;2250&quot; data-origin-height=&quot;2280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 위 사진처럼 빵판을 이용해서 사용하려 했는데 자꾸 선이 빠지기도 하고, 갑자기 발표 전날에 빵판 한 줄이 먹통이 되는 바람에 급하게 납땜을 했다. 또 라즈베리파이 특성상 아날로그 입력이 안되는데, 교수님이 만드신 &lt;a href=&quot;https://xbot.co.kr/edge.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;X-bot &lt;/a&gt;&amp;nbsp;엣지보드를 이용해 조이스틱의 아날로그 입력을 가능하게 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 게임 알고리즘인데, 테트리스와 리듬게임의 이미지는 그림그려주는 AI, DALL-2 가 만들어줬고, 조이스틱을 이용해 원하는 게임을 선택 할 수 있도록 했다. 그리고 각 게임을 모듈화 해서 실행할 때 불러오기만 했다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-21 152011.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daLmBc/btskJWEYsY2/4VTSGJDgUJP9aixPyAUkyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daLmBc/btskJWEYsY2/4VTSGJDgUJP9aixPyAUkyk/img.png&quot; data-alt=&quot;메인화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daLmBc/btskJWEYsY2/4VTSGJDgUJP9aixPyAUkyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaLmBc%2FbtskJWEYsY2%2F4VTSGJDgUJP9aixPyAUkyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;547&quot; height=&quot;308&quot; data-filename=&quot;스크린샷 2023-06-21 152011.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메인화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 제일 중요한건 테트리스와 지렁이게임은 오픈소스여서 쉽게 구할 수 있지만 리듬게임은 오픈되어 있지 않아서 만들기 엄청 힘들었다. 정말 GPT가 없었으면 완성도 못할 뻔했다. 아무튼 리듬게임은 다음 글에서 포스팅 하도록 하고, 아래는 최종 실행영상이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;kakaotv&quot; data-video-url=&quot;https://tv.kakao.com/v/438973229&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/Z1ELG/hyS4AmCFrq/BkZYaCHKPva03L2sWXF3j1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/bFmQAf/hyS4qYABta/6sRwvRHGoDMOttjnWt1Qy0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-play-service=&quot;daum_tistory&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://play-tv.kakao.com/embed/player/cliplink/438973229?service=daum_tistory&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project</category>
      <category>게임기</category>
      <category>라즈베리파이</category>
      <category>아케이드게임</category>
      <category>파이썬</category>
      <author>re-hwi</author>
      <guid isPermaLink="true">https://re-hwi.tistory.com/125</guid>
      <comments>https://re-hwi.tistory.com/125#entry125comment</comments>
      <pubDate>Wed, 21 Jun 2023 14:46:41 +0900</pubDate>
    </item>
  </channel>
</rss>