<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>te-me-city12.log</title>
        <link>https://velog.io/</link>
        <description>개발하는 디자이너입니다.</description>
        <lastBuildDate>Fri, 17 Dec 2021 10:24:51 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>te-me-city12.log</title>
            <url>https://images.velog.io/images/te-me-city12/profile/02711eb8-97aa-4417-b219-8ee4a7dba705/썸넬.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. te-me-city12.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/te-me-city12" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[항해99 TIL [12/17]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1217</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1217</guid>
            <pubDate>Fri, 17 Dec 2021 10:24:51 GMT</pubDate>
            <description><![CDATA[<p><strong>주특기 주차를 맞이하고 어느덧 금요일을 맞이했다. 오늘은 이전에 공부했던 모바일 우선 반응형 웹의 또다른 부분에 대해 조사해보기로 했다.</strong></p>
<h3 id="▶-모바일-우선-반응형-웹-디자인-2">▶ <strong>모바일 우선 반응형 웹 디자인 (2)</strong></h3>
<blockquote>
<p><strong>css 단위</strong></p>
</blockquote>
<h3 id="rem-em"><strong>rem, em</strong></h3>
<p><strong>◻ 소개</strong></p>
<ul>
<li>rem: root element에 지정된 font 크기.</li>
<li>em: 현재 element의 font 크기.</li>
</ul>
<p><strong>◻ 사용법</strong></p>
<ul>
<li>&#39;rem&#39; for global sizing, &#39;em&#39; for local sizing
<a href="https://css-tricks.com/rem-global-em-local/">참고: Use &#39;rem&#39; for Global Sizing; Use &#39;em&#39; for Local Sizing-css tricks</a></li>
</ul>
<pre><code>.wrapper1 {
    font-size: 2rem;
}
.wrapper1 .heading {
    font-size: 3em;
}
.wrapper1 .content {
       font-size: 1em;
}

.wrapper2 {
    font-size: 3rem;
}
.wrapper2 .heading {
       font-size: 2em;
}
.wrapper2 .content {
       font-size: 0.5em;
}</code></pre><p><strong>◻ 주의사항</strong></p>
<ul>
<li>em의 남용은 레이아웃이 깨졌을 때 어디서부터 잘못됐는지 모르는 사태를 초래할 수 있음.</li>
</ul>
<pre><code>// 혼돈의 카오스
html {
    font-size: 100%
}
body {
    font-size: 1em;
}
.article {
    font-size: 1.5em;
}
.article .wrapper {
    font-size: 2em;
}
.article .wrapper .heading {
    font-size: 3em;
}
.article .wrapper .content {
       font-size: 1em;
}</code></pre><p><strong>✔ 해결 방법</strong></p>
<ul>
<li>가능한 low-level에서 em을 선언함.</li>
<li>rem을 우선적으로 사용하고, em은 local에서 부모와 자식 요소의 상대적 크기를 지정할 때 사용함.</li>
</ul>
<h3 id="vw-vh-vmin-vmax"><strong>vw, vh, vmin, vmax</strong></h3>
<p><strong>◻ 소개</strong></p>
<ul>
<li>vw: 1vw는 뷰포트 너비의 1%</li>
<li>vh: 1vh는 뷰포트 높이의 1%</li>
<li>vmin: 1vmin은 뷰포트의 작은 방향의 1%</li>
<li>vmax: 1vmax는 뷰포트의 큰 방향의 1%
ex) width: 1000px, heigth: 2000px인 뷰포트의 경우
1vmin = 10px
1vmax = 20px</li>
</ul>
<p><strong>◻ 사용법</strong></p>
<ul>
<li>responsive typhography 구현에 사용할 수 있음.</li>
<li>full-height layout, hero image, sticky footer 구현에 사용할 수 있음.</li>
<li>fluid aspect ratio 구현에 사용할 수 있음.
<a href="https://css-tricks.com/fun-viewport-units/#full-height-layouts-hero-images-and-sticky-footers">출처: Fun with viewport units-css tricks</a></li>
</ul>
<p><strong>◻ 주의사항</strong></p>
<ul>
<li>블록 요소의 width 단위로 vw를 사용할 시, 스크롤바를 고려하지 못할 수 있음.</li>
<li>브라우저 호환성이 %, rem, em 등 여타 단위들보다 좋지 않음.</li>
</ul>
<blockquote>
<p><strong>반응형 콘텐츠</strong></p>
</blockquote>
<h3 id="이미지"><strong>이미지</strong></h3>
<p><strong>◻ img vs background-image</strong></p>
<p>이미지가 사용자의 페이지에 대한 이해를 돕는다면 img를 사용하고, 오로지 디자인이나 배경 이미지로 사용할 목적이라면 background-image를 사용함.</p>
<p><a href="https://stackoverflow.com/questions/492809/when-to-use-img-vs-css-background-image">img vs background-image__stack overflow</a>
<a href="https://velog.io/@chris/the-css-background-image-property-as-an-anti-pattern">안티 패턴으로서의 CSS background-image 속성__velog</a></p>
<p>html의 img와 css의 background-image 각각 ppi와 사이즈를 고려한 반응형 이미지를 만들 수 있는 방법 ▶ <a href="https://developers.google.com/web/fundamentals/design-and-ux/responsive/images?hl=ko">developers.google.com: Responsive images</a></p>
<p><strong>◻ width, height</strong>
🙌 상대 크기를 사용할 것.</p>
<p>ex) 예시로서의 html 구조</p>
<pre><code>&lt;div class=&quot;container&quot;&gt;
    &lt;img src=&quot;lion image from unsplash&quot; alt=&quot;lion-father and baby&quot;&gt;
&lt;/div&gt;</code></pre><ul>
<li>기본적으로 이미지에 하나의 치수만 선언하면 늘 종횡비가 유지됨.</li>
</ul>
<p><img src="https://images.velog.io/images/te-me-city12/post/fbef3ddb-0c4a-4df0-b42c-70267aa9aa91/%EC%9D%B4%EB%AF%B8%EC%A7%801.png" alt=""></p>
<pre><code>.containing-block {
  width: 50%;
  height: 100vh;
  background-color: red;
}

.containing-block img {
  width: 100%;
}</code></pre><ul>
<li>기존 이미지의 크기가 줄어들거나 늘어나는 것에 상관없이 컨테이닝 블록의 width에 이미지 크기를 딱 맞추고 종횡비를 유지하길 원한다면 이미지 태그의 width에 100%, height에 auto를 주면 됨.
<img src="https://images.velog.io/images/te-me-city12/post/0f341869-5401-471b-b63d-8ac3c80c3f96/%EC%9D%B4%EB%AF%B8%EC%A7%802.png" alt=""></li>
</ul>
<pre><code>.containing-block {
  width: 100%;
  height: 100vh;
  background-color: red;
}

.containing-block img {
  width: 100%;
  height: auto;
}</code></pre><p>아래 사진처럼 기존 이미지의 크기가 컨테이닝 블록을 벗어날 때가 있음.
<img src="https://images.velog.io/images/te-me-city12/post/bda929b4-66ff-4216-8389-116f12ff766e/%EC%9D%B4%EB%AF%B8%EC%A7%803.png" alt=""></p>
<pre><code>.containing-block {
  width: 50%;
  height: 100vh;
  background-color: red;
}</code></pre><p>이럴 땐 max-width: 100%, height: auto를 주면 컨테이닝 블록의 width에 맞게 줄어들면서 종횡비를 유지함.
<img src="https://images.velog.io/images/te-me-city12/post/629fb4d8-5667-4e1e-a187-830360431db3/%EC%9D%B4%EB%AF%B8%EC%A7%804.png" alt=""></p>
<pre><code>.containing-block {
  width: 50%;
  height: 100vh;
  background-color: red;
}

.containing-block img {
  max-width: 100%;
  height: auto;
}</code></pre><p>벗어난 부분만 자르고 싶다면 부모 요소에 overflow:hidden을 줄 수도 있음.
<img src="https://images.velog.io/images/te-me-city12/post/9bf0bd43-174c-4a69-9862-a1446e88dc12/%EC%9D%B4%EB%AF%B8%EC%A7%806.png" alt=""></p>
<pre><code>.containing-block {
  width: 50%;
  height: 100vh;
  background-color: red;
  overflow: hidden;
}</code></pre><ul>
<li>기존 종횡비를 유지하되 가로 세로 크기를 임의로 제한하고자 한다면 max-width와 max-height가 매우 유용함.</li>
</ul>
<p><img src="https://images.velog.io/images/te-me-city12/post/8ec0a6ab-6a20-41dd-9fa7-6324750c853e/%EC%9D%B4%EB%AF%B8%EC%A7%805.png" alt=""></p>
<pre><code>.containing-block {
  width: 50%;
  height: 100vh;
  background-color: red;
}

.containing-block img {
  max-width: 300px;
  max-height: 300px;
}</code></pre><p>위에 더해 이미지가 반응형이길 원한다면 상대적 단위값(%, vw 등)을 가지는 width를 img에 선언하여 max-width와 함께 활용할 수 있음.</p>
<pre><code>.containing-block {
  width: 50%;
  height: 100vh;
  background-color: red;
}

.containing-block img {
  max-width: 300px;
  max-height: 300px;
  width: 40%;
}</code></pre><p><a href="https://stackoverflow.com/questions/3751565/css-100-width-or-height-while-keeping-aspect-ratio">참고: 100% width or height while keeping aspect ratio?__stack overflow</a></p>
<p><strong>◻ 해상도 전환</strong></p>
<ul>
<li>기기의 특성(화면 크기, 픽셀 밀도)에 따라 적합한 사이즈의 이미지를 제공하는 문제.</li>
<li>같은 이미지 다른 크기.</li>
</ul>
<p><img src="https://images.velog.io/images/te-me-city12/post/3a0b65a7-64f6-4bf4-8ed7-2fddadcd96a6/%ED%95%B4%EC%83%81%EB%8F%84%20%EC%A0%84%ED%99%98.png" alt=""><a href="https://www.howtogeek.com/402335/how-to-automatically-size-pictures-in-powerpoint/">출처: howtogeek-how to automatically size pictures in powerpoint</a></p>
<ul>
<li>img 요소의 srcset을 활용하며 sizes 프로퍼티는 w(width descriptor)를 사용한다면 필수이고, x(display density descriptor)를 사용한다면 필수가 아님.</li>
<li>x는 화면 픽셀 밀도를 계산해 적합한 이미지를 다운로드 하지만, w는 화면 픽셀 밀도와 함께 이미지의 최종 사이즈를 고려해 최적의 이미지를 다운로드 하기 때문임.</li>
<li>w 사용 시 sizes가 없으면 이미지의 최종 사이즈가 어떻게 될 지 판단할 수 없는데 그 이유는 이미지 다운로드가 css와 javascript를 로딩하고 해석하는 과정이 끝나지 않은 상태에서 이루어지기 때문임.</li>
<li>사용자 화면의 레이아웃과 이미지의 사이즈가 아직 결정되지 않은 상태인데 최적의 이미지를 골라 다운로드 하라고 하는 것은 아직 침대 사이즈를 어떤 걸로 살 지 결정되지도 않았는데 침대 커버를 사이즈에 맞게 사오라는 것과 같은 뜻이라 할 수 있음.</li>
</ul>
<p>▶참고
<a href="https://cloudfour.com/thinks/responsive-images-101-part-4-srcset-width-descriptors/">cloudfour: Responsive Images 101, Part 4: Srcset Width Descriptors</a>
<a href="https://developers.google.com/web/fundamentals/design-and-ux/responsive/images">developers.google.com: Responsive images</a></p>
<pre><code>// w

&lt;img src=&quot;400.png&quot; 
     sizes=&quot;(min-width: 600px) 25vw, (min-width: 500px) 50vw, 100vw&quot;
     srcset=&quot;100.png 100w, 200.png 200w, 400.png 400w,
             800.png 800w, 1600.png 1600w&quot; alt=&quot;an example image&quot;&gt;</code></pre><pre><code>// x
&lt;img src=&quot;photo.png&quot; srcset=&quot;photo@2x.png 2x, photo@3x.png 3x, ...&quot;&gt;</code></pre><ul>
<li><p>따라서 크기가 정해진 이미지에 한해 x descriptor를 사용하는 것이 권장되고, 나머지 반응형에서는 w descriptor 사용이 권장됨.</p>
</li>
<li><p>srcset과 sizes가 지원되지 않는 브라우저에서는 src 내부의 이미지가 사용되기에 src에 1x의 이미지를 잘 적용하는 것은 중요함.</p>
</li>
</ul>
<p><strong>◻ 아트 디렉션</strong></p>
<ul>
<li><p>기기의 특성(화면 크기, 픽셀 밀도)에 따라 특정 부분을 자르거나 강조해서 보여주고 싶은 문제.</p>
</li>
<li><p>같은 이미지 다른 부분.
<img src="https://images.velog.io/images/te-me-city12/post/4fdb6b05-b4e9-4801-a8f1-471424a3f01b/art-direction-2x.png" alt=""></p>
</li>
</ul>
<p><a href="https://developers.google.com/web/fundamentals/design-and-ux/responsive/images?hl=ko">출처: developers.google.com</a></p>
<ul>
<li>picture 요소를 활용하며 기본적으로 활용될 이미지를 img 태그에 선언하고, 브레이크 포인트별로 사용될 이미지들은 source에 선언함.</li>
<li>source도 img와 동일하게 srcset, sizes, w, x를 상황에 맞게 사용할 수 있음.</li>
</ul>
<p>▶참고
<a href="https://cloudfour.com/thinks/responsive-images-101-part-6-picture-element/">cloudfour: Responsive Images 101, Part 6: Picture Element</a>
<a href="https://developer.mozilla.org/ko/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images">MDN: 반응형 이미지</a></p>
<pre><code>&lt;picture&gt;
  &lt;source media=&quot;(min-width: 800px)&quot; srcset=&quot;head.jpg, head-2x.jpg 2x&quot;&gt;
  &lt;source media=&quot;(min-width: 450px)&quot; srcset=&quot;head-small.jpg, head-small-2x.jpg 2x&quot;&gt;
  &lt;img src=&quot;head-fb.jpg&quot; srcset=&quot;head-fb-2x.jpg 2x&quot; alt=&quot;a head carved out of wood&quot;&gt;
&lt;/picture&gt;</code></pre><h3 id="동영상"><strong>동영상</strong></h3>
<p><strong>◻ video</strong></p>
<ul>
<li>이미지와 동일하게 width, max-width, height를 적절히 섞어 쓰면 됨.</li>
</ul>
<pre><code>video {
  width: 100%;
  max-height: 100%;
  height: 100%;
}</code></pre><p><strong>◻ iframe</strong></p>
<ul>
<li>컨테이닝 블록의 특성을 이용함.</li>
<li>요소의 position이 absolute인 경우, position이 static이 아닌 부모의 패딩 영역(내부 여백 영역)이 컨테이닝 블록이 됨.</li>
</ul>
<pre><code>// html
&lt;div class=&quot;videoWrapper&quot;&gt;
    &lt;iframe width=&quot;560&quot; height=&quot;349&quot; src=&quot;https://www.youtube.com/embed/tgbNymZ7vqY&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;

// css
.videoWrapper {
  position: relative;
  padding-bottom: 56.25%; /* 16:9 Aspect Ratio (divide 9 by 16 = 0.5625) */
  height: 0;
}
.videoWrapper iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}</code></pre><p>혹은 이렇게도 사용가능함.</p>
<pre><code>&lt;div class=&quot;videoWrapper&quot; style=&quot;--aspect-ratio: 3 / 4;&quot;&gt;
  &lt;iframe ...&gt;
&lt;/div&gt;

.videoWrapper {
  ...
  /* falls back to 16/9, but otherwise uses ratio from HTML */
  padding-bottom: calc(var(--aspect-ratio, .5625) * 100%); 
}</code></pre><p>▶참고
<a href="https://css-tricks.com/fluid-width-video/">css tricks: fluid width video</a>
<a href="https://developer.mozilla.org/ko/docs/Web/CSS/var()">MDN: var()</a>
<a href="https://developer.mozilla.org/ko/docs/Web/CSS/Using_CSS_custom_properties">MDN: css custom properties</a></p>
<blockquote>
<p><strong>유동적 레이아웃</strong></p>
</blockquote>
<h3 id="flexbox"><strong>flexbox</strong></h3>
<p><strong>◻ 개념</strong></p>
<p>1차원에서 강력한 공간 배분 및 정렬 기능을 보유한 css3의 새로운 레이아웃 기능.</p>
<p><strong>◻ 특징</strong></p>
<ul>
<li>수직, 혹은 수평 중 한 방향으로 정렬하거나 공간 배분을 하는 내비게이션과 같은 레이아웃을 만드는데 탁월함.</li>
<li>container와 item으로 구성됨.</li>
<li>container에 display: flex를 선언함으로써 적용됨.</li>
<li>주축(Main Axis)과 교차축(Cross Axis)으로 구성됨.</li>
</ul>
<p><strong>◻ 참조</strong>
<a href="https://developer.mozilla.org/ko/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox">MDN: flexbox의 기본 개념</a>
<a href="https://heropy.blog/2018/11/24/css-flexible-box/">heropy님 블로그: css flex 완벽 가이드</a>
<a href="https://d2.naver.com/helloworld/8540176">네이버 D2: flexbox로 만들 수 있는 10가지 레이아웃</a></p>
<h3 id="grid"><strong>grid</strong></h3>
<p><strong>◻ 개념</strong>
2차원의 레이아웃 시스템으로 수평선과 수직선이 교차해서 이루어지며, 각각 가로행과 세로열을 정의함.</p>
<p><strong>◻ 특징</strong></p>
<ul>
<li>바둑판이라고 생각하면 이해하기 쉬우며 flexbox와 마찬가지로 container와 item으로 구성되고 flexbox와 함께 사용하면 매우 강력함.</li>
<li>container에 display: grid를 선언함으로써 적용됨.</li>
<li>column 축과 row 축으로 구성됨.
<img src="https://images.velog.io/images/te-me-city12/post/717200b2-f7ad-4035-a537-a714f96e9262/01-1.jpg" alt="">
<a href="https://studiomeal.com/archives/533">스튜디오밀 1분코딩: 이번에야말로 CSS Grid를 익혀보자</a></li>
</ul>
<p><strong>◻ 참조</strong>
<a href="https://developer.mozilla.org/ko/docs/Web/CSS/CSS_Grid_Layout">MDN: CSS 그리드 레이아웃</a>
<a href="https://developer.mozilla.org/ko/docs/Web/CSS/CSS_Grid_Layout/Basic_concepts_of_grid_layout">MDN: Basic concepts of grid layout</a>
<a href="https://heropy.blog/2019/08/17/css-grid/">heropy님 블로그: css grid 완벽 가이드</a>
<a href="https://uid.gitbook.io/css-grid/box-alignment#block-inline-axis">Box 정렬 가이드 by 야무</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [12/13]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1213</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1213</guid>
            <pubDate>Mon, 13 Dec 2021 09:50:26 GMT</pubDate>
            <description><![CDATA[<p><strong>마지막 세 번째 알고리즘 시험을 보는 날이 되었다. 이제는 내가 선택한 주특기를위해 따로 공부하는 1주일을 보내게 될 것이다. 비록 1주일이라는 짧은 시간이었지만 그래도 꾸준히 알고리즘을 공부하는 것은 그래도 의미있는 시간이었다고 생각한다. 물론 앞으로도 알고리즘은 계속 공부할 것이긴 하지만 온전히 알고리즘을 공부하는 데에만 시간을 보낸 것은 처음이기에 더 기억에 남을 듯 하다.</strong></p>
<blockquote>
<p><strong><a href="https://programmers.co.kr/learn/courses/30/lessons/67257">[카카오 인턴] 수식 최대화</a></strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/fec6fe44-833c-4814-8ed3-00d6fb41dea3/Cap%202021-12-13%2015-45-03-867.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/c639d473-8946-4638-b177-044dbcd461bc/Cap%202021-12-13%2015-45-31-835.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/3946c54d-abf1-450f-ad72-dd4ee2f01a9d/Cap%202021-12-13%2015-45-38-640.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/3c58bbdf-a37f-44a7-ac6d-5aa0ab81a89d/Cap%202021-12-13%2015-45-43-582.png" alt=""></p>
<blockquote>
<p><strong>문제에서 구하는 것</strong></p>
</blockquote>
<p>연산자의 우선순위를 새로 정의해서 만들 수 있는 가장 큰 상금 금액의 절댓값. * 를 해야 가장 크지 않을까 생각했으나, 입출력 예시를 보면 아닌 경우도 있기에 일반적인 규칙은 없는 듯함.</p>
<p>▶ 따라서, 이런 경우에는 <strong>우선 순위의 모든 경우를</strong> 다 해봐야함.</p>
<p>생각해보면 <em>, + , - 이 3가지를 순서를 바꿔봤자 어차피 6가지 밖에 없기 때문에, 이 경우들을 *</em>모두 시도해서** expression 을 계산한 뒤에 가장 절댓값이 큰 값을 반환해주면 됨.</p>
<p><code>* &gt; + &gt; -</code>  , <code>* &gt; - &gt; +</code> , <code>+ &gt; * &gt; -</code>  , <code>+ &gt; - &gt; *</code>, <code>- &gt; * &gt; +</code>, <code>- &gt; + &gt; *</code>
<br>
☝ 구현하기 전에, 알고 있으면 조금 더 좋은 코드를 만들기 위한 선행 개념</p>
<p><strong>1. 순열</strong> 
<strong>2. 정규표현식</strong></p>
<blockquote>
<p><strong>1. 순열</strong> </p>
</blockquote>
<p><code>* &gt; + &gt; -</code>  , <code>* &gt; - &gt; +</code> , <code>+ &gt; * &gt; -</code>  , <code>+ &gt; - &gt; *</code>, <code>- &gt; * &gt; +</code>, <code>- &gt; + &gt; *</code> </p>
<p>위처럼 순서가 부여된 임의의 집합. 즉, 순열이 있음. 파이썬에서는 순열을 쉽게 만들 수 있는 방법이 있는데 바로 itertools 라는 라이브러리를 이용하는 것. </p>
<p>다음과 같이 모든 경우의 수를 쉽게 구할 수 있음.</p>
<pre><code> from itertools import permutations

operation_list = [&quot;*&quot;, &quot;+&quot;, &quot;-&quot;]
operation_permutations = list(permutations(operation_list))
print(operation_permutations) # [(&#39;*&#39;, &#39;+&#39;, &#39;-&#39;), (&#39;*&#39;, &#39;-&#39;, &#39;+&#39;), (&#39;+&#39;, &#39;*&#39;, &#39;-&#39;), (&#39;+&#39;, &#39;-&#39;, &#39;*&#39;), (&#39;-&#39;, &#39;*&#39;, &#39;+&#39;), (&#39;-&#39;, &#39;+&#39;, &#39;*&#39;)]</code></pre><blockquote>
<p><strong>2. 정규표현식</strong></p>
</blockquote>
<p>expression 을 보면 다음과 같이<code>&quot;100-200*300-500+20&quot;</code>로 되어 있는데 이 문자열에서 문자와 연산자를 구분해야 함.</p>
<p>문자열을 돌면서 - * + 중 하나를 뽑아서 나눌수도 있는데 이런 작업을 하려면 여러줄의 반복문과 분기문이 필요할 것. </p>
<p><code>문자열을 보면서 숫자가 나오지 않을 때까지 탐색 길이를 늘리고, 연산자가 나올 때까지 탐색한다.</code> 라는 코드를 작성해야 하기 때문임. </p>
<p>이 긴 요구사항을 단 한 줄로 표시하는 방법이 정규표현식. 정규 표현식은 문자열에 나타나는 특정 문자 조합과 대응시키기 위해 사용되는 패턴으로 주로 문자열의 검색과 치환을 위한 용도로 쓰이고 있음.</p>
<p>▶ 우선 반환해 줘야 하는 것 : 연산자의 우선순위를 새로 정의해서 만들 수 있는 가장 큰 상금 금액의 절댓값이므로 answer = 0 을 만들어서 반환해 줄 것.</p>
<pre><code>def solution(expression):
    answer = 0

    return answer</code></pre><p>그리고 itertools 를 이용해서 순열을 만들어 줌.</p>
<pre><code>from itertools import permutations


def solution(expression):
    answer = 0

    operation_list = [&quot;*&quot;, &quot;+&quot;, &quot;-&quot;]
    operation_permutations = list(permutations(operation_list))

    return answer</code></pre><p>순열이 준비가 되면, 정규표현식을 이용해서 expression 을 숫자와 연산자로 분리해 주는데 <strong>re</strong> 라는 모듈을 가져와서, 다음과 같이 코드를 작성.</p>
<p><strong>참고</strong> : [^0-9] 는 0~9를 제외한 문자 하나를 찾는 다는 의미로 * 이나 + 이나 - 를 찾을 수 있으며 그 기준으로 입력된 문자열을 잘라 배열에 저장하게 됨.</p>
<pre><code>import re
from itertools import permutations


def solution(expression):
    answer = 0

    operation_list = list()
    if &#39;*&#39; in expression:
        operation_list.append(&#39;*&#39;)
    if &#39;+&#39; in expression:
        operation_list.append(&#39;+&#39;)
    if &#39;-&#39; in expression:
        operation_list.append(&#39;-&#39;)

    operation_permutations = list(permutations(operation_list))
    expression = re.split(&#39;([^0-9])&#39;, expression)

    return answer</code></pre><p>👉 이제 각각의 연산자 우선순위에 따라 연산값이 얼마가 나오는지 파악하고, 그 연산값 중 절댓값이 가장 높은 경우의 값을 반환하면 됨. 이를 위해서 연산자의 우선순위 순열에서 하나의 값을 뽑고, 그 순열에서 정해진 순서대로 계산을 진행하면 됨.</p>
<pre><code>import re
from itertools import permutations


def solution(expression):
    answer = 0

    operation_list = list()
    if &#39;*&#39; in expression:
        operation_list.append(&#39;*&#39;)
    if &#39;+&#39; in expression:
        operation_list.append(&#39;+&#39;)
    if &#39;-&#39; in expression:
        operation_list.append(&#39;-&#39;)

    operation_permutations = list(permutations(operation_list))
    expression = re.split(&#39;([^0-9])&#39;, expression)

    for operation_permutation in operation_permutations:
                for operator in operation_permutation:
                # ????
    return answer</code></pre><p>👉 계산을 하는 방법은 다양한 방법들이 있을 수 있지만, 여기서는 계산할 때마다 기존 계산식에 있는 연산자와 숫자를 제거하는 방식으로 진행할 것.</p>
<p>예를 들어 <code>&quot;100-200*300-500+20&quot;</code> 라는 입력값이 들어왔을 때, 연산자 우선순위가 <code>* &gt; + &gt; -</code> 라고 한다면 다음과 같이 연산할 것임.</p>
<p>1) 우선 [<em>]를 찾음.
2) [</em>] 기준으로 앞 뒤의 숫자들을 연산한 뒤 연산 결과를 제외한 연산자와 숫자들은 제거함.
3) 그리고 다시 [*]를 찾고, 없다면 다음 연산자를 찾음.</p>
<p>1) 런타임의 값들로 보면, 현재 <strong>expression</strong> 변수가 다음과 같이 되어 있음.
[&quot;100&quot;, &quot;-&quot;, &quot;200&quot;, &quot;*&quot;, &quot;300&quot;, &quot;-&quot;, &quot;500&quot;, &quot;+&quot;, &quot;20&quot;]</p>
<p>2) 여기서 [*]  를 찾고 앞 뒤에 있는 문자열인 &quot;200&quot;과 &quot;300&quot;을 연산하면 숫자 60000이 됨.</p>
<p>3) 그리고 &quot;200&quot;, &quot;*&quot;, &quot;300&quot;을 없애고 60000을 넣으면 <strong>expression</strong> 변수가 다음과 같이 되어 있음. [&quot;100&quot;, &quot;-&quot;, &quot;60000&quot;, &quot;-&quot;, &quot;500&quot;, &quot;+&quot;, &quot;20&quot;]</p>
<p>3) 이제 다음 연산자인 +로 다시 반복.</p>
<p>위의 작업을 진행하려면, 우선 기존 expression 배열을 복사해야 하며 왜냐면 우리는 연산한 작업을 하면서 배열을 잘라서 붙이는 작업을 해야 하기 때문에 원본 배열이 아닌 복사된 배열로 연산 작업을 한다는 것.</p>
<p>따라서 copied_expression 을 만든 후, 현재 가장 높은 우선순위의 연산자가 해당 표현식에 있을 때까지 반복함. 해당 연산자와 앞 뒤의 숫자들을 기준으로 연산을 할 예정이고 어떤 연산을 하는지는 연산자의 내용에 따라서 달라잠. 여기서는 이렇게 if 문을 사용하도록 구현했으나, 다른 방식으로 구현이 가능함.</p>
<pre><code>import re
from itertools import permutations


def solution(expression):
    answer = 0

    operation_list = list()
    if &#39;*&#39; in expression:
        operation_list.append(&#39;*&#39;)
    if &#39;+&#39; in expression:
        operation_list.append(&#39;+&#39;)
    if &#39;-&#39; in expression:
        operation_list.append(&#39;-&#39;)

    operation_permutations = list(permutations(operation_list))
    expression = re.split(&#39;([^0-9])&#39;, expression)

    for operation_permutation in operation_permutations:
        copied_expression = expression[:]
        for operator in operation_permutation:
            while operator in copied_expression:
                op_idx = copied_expression.index(operator)

                if copied_expression[op_idx] == &quot;*&quot;:
                    cal = int(copied_expression[op_idx - 1]) * int(copied_expression[op_idx + 1])
                elif copied_expression[op_idx] == &quot;+&quot;:
                    cal = int(copied_expression[op_idx - 1]) + int(copied_expression[op_idx + 1])
                elif copied_expression[op_idx] == &quot;-&quot;:
                    cal = int(copied_expression[op_idx - 1]) - int(copied_expression[op_idx + 1])
    return answer</code></pre><p>👉 자 이제 3번째 단계인 연산자와 앞 뒤의 숫자를 없애고 결괏값을 배열에 넣어야 함.</p>
<p>즉, [&quot;100&quot;, &quot;-&quot;, &quot;200&quot;, &quot;*&quot;, &quot;300&quot;, &quot;-&quot;, &quot;500&quot;, &quot;+&quot;, &quot;20&quot;]
                               <strong>op_idx</strong> 라고 한다면,</p>
<p>[&quot;100&quot;, &quot;-&quot;, &quot;200&quot;, &quot;*&quot;, &quot;300&quot;, &quot;-&quot;, &quot;500&quot;, &quot;+&quot;, &quot;20&quot;]
                    <strong>op_idx - 1 에 60000</strong>을 넣고</p>
<p>[&quot;100&quot;, &quot;-&quot;, &quot;60000&quot;<strong>, &quot;*&quot;, &quot;300&quot;, **&quot;-&quot;, &quot;500&quot;, &quot;+&quot;, &quot;20&quot;]</strong>
       여기까지의 배열과                      여기까지의 배열을 합치는 것인데 그렇게 되면 연산에 사용된 값들과 연산자는 제거되기 때문임.</p>
<pre><code>import re
from itertools import permutations

def solution(expression):
    answer = 0

    operation_list = list()
    if &#39;*&#39; in expression:
        operation_list.append(&#39;*&#39;)
    if &#39;+&#39; in expression:
        operation_list.append(&#39;+&#39;)
    if &#39;-&#39; in expression:
        operation_list.append(&#39;-&#39;)

    operation_permutations = list(permutations(operation_list))
    expression = re.split(&#39;([^0-9])&#39;, expression)

    for operation_permutation in operation_permutations:
        copied_expression = expression[:]
        for operator in operation_permutation:
            while operator in copied_expression:
                op_idx = copied_expression.index(operator)
                cal = str(
                    eval(copied_expression[op_idx - 1] + copied_expression[op_idx] + copied_expression[op_idx + 1])
                )

                **copied_expression[op_idx - 1] = cal
                copied_expression = copied_expression[:op_idx] + copied_expression[op_idx + 2:]**

    return answer</code></pre><p>그리고 나서 copied_expression 의 첫번째 원소를 뽑는데 이는 모든 연산이 끝났다면 전부 계산이 완료되어서 하나의 원소값만 가지고 있을 것이기 때문임. 따라서 이 값과 answer 를 비교해서 가장 큰 값만 남도록 하면, 연산자의 우선순위를 새로 정의해서 만들 수 있는 가장 큰 상금 금액의 절댓값을 구할 수 있게 된 것임. </p>
<pre><code>import re
from itertools import permutations


def solution(expression):
    answer = 0

    operation_list = list()
    if &#39;*&#39; in expression:
        operation_list.append(&#39;*&#39;)
    if &#39;+&#39; in expression:
        operation_list.append(&#39;+&#39;)
    if &#39;-&#39; in expression:
        operation_list.append(&#39;-&#39;)

    operation_permutations = list(permutations(operation_list))
    expression = re.split(&#39;([^0-9])&#39;, expression)

    for operation_permutation in operation_permutations:
        copied_expression = expression[:]
        for operator in operation_permutation:
            while operator in copied_expression:
                op_idx = copied_expression.index(operator)
                cal = str(
                    eval(copied_expression[op_idx - 1] + copied_expression[op_idx] + copied_expression[op_idx + 1])
                )

                copied_expression[op_idx - 1] = cal
                copied_expression = copied_expression[:op_idx] + copied_expression[op_idx + 2:]

        answer = max(answer, abs((int(copied_expression[0]))))

    return answer</code></pre><p>이 문제에서 사용되는 개념인 순열과 정규표현식을 알면 문제를 더욱 수월하게 풀 수 있었음. 그러나 이런 개념들이 중요한 것은 아니고, 문제의 요구사항을 분석해서 구현해나가는 과정이 더욱 중요함.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [12/1]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-121</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-121</guid>
            <pubDate>Sat, 11 Dec 2021 00:05:16 GMT</pubDate>
            <description><![CDATA[<p><strong>그 동안 준비해온 것들을 보여주게 될 실전 프로젝트 발표일이 내일모레로 다가왔다. 이제는 팀이 해결할 수 있는 문제에 더 집중하면서 팀을 잘 보여줄 수 있는 프레젠테이션을 만드는 작업에 돌입하게 되었고, 그 과정에 대해 이 곳에 어느 정도 남겨두고자 한다.</strong></p>
<blockquote>
<p><strong>프레젠테이션 설명</strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/5b534c5b-22c0-4760-b3b2-232daf6afb80/Cap%202021-12-10%2017-39-07-893.jpg" alt="">20년대 이후 캠핑과 차박이라는 트래픽 키워드가 꾸준히 급상승해 왔고, 이는 상단 슬라이드의 검색율 기사와 캠핑산업의 규모 및 도표가 증명하고 있음. 또한 사용자들의 별 보기 좋은 장소를 알고 싶어하는 니즈를 파악한 후, 우리 팀은 사람들이 알고 있는 캠핑과 차박 혹은 별 보기 좋은 장소를 추천하는 웹사이트를 만들고자 하였음.
<br>
<img src="https://images.velog.io/images/te-me-city12/post/d6f50755-64c1-40cd-a16c-6c95a80c9872/Cap%202021-12-10%2017-40-56-929.png" alt="">프론트엔드 아키텍쳐의 경우, 깃액션을 사용하여 S3와 CloudFront의 자동배포를 구축하였으며Route53을 통해서 https를 구축하였음. 
<br>
<img src="https://images.velog.io/images/te-me-city12/post/5975a65f-72ed-4177-ae7c-7330f2ae704b/Cap%202021-12-10%2017-43-51-408.jpg" alt="">프론트에서는 사용자의 편리성을 중심으로 한 웹사이트의 제작을 목표로 하였으며 이에 대한 챌린지로서 사용자 위치에 기반한 콘텐츠를 보여주는 것, 그리고 클라이언트의 부담을 최소화 하는 것에 중점을 두었음. 
<br>
<img src="https://images.velog.io/images/te-me-city12/post/0da96a60-58bc-49f1-a181-0276087f8f16/Cap%202021-12-10%2017-44-13-598.jpg" alt="">사용자 위치기반 콘텐츠들은 geolocation api를 통해서 사용자 위치정보를 가져오고 이로 인해서 사용자 위치 기반 컨텐츠를 제공할 수 있게되었음. 
<br>
<img src="https://images.velog.io/images/te-me-city12/post/94a23e89-bf4a-4577-83d0-5b24ab78466d/Cap%202021-12-10%2017-44-26-562.jpg" alt="">클라이언트 부담을 줄이기위해 지도페이지, 커뮤니티페이지에 무한스크롤을 적용하고 적절한 리덕스 분배를 하였으며 페이지 렌더링 속도까지 개선되었음.적절한 리덕스 분배의 예시로, 커뮤니티 페이지 작업 초반에는 모든 게시글 리스트를 리덕스에 저장하고 북마크, 좋아요 등 관련기능까지 리덕스 모듈로 관리되고 있었으며 이후 성능 개선을 위해 코드를 개선시켰음. 변경된 코드는 변화가 적은 “추천순&quot; 탭의 게시글만 저장하고 “인기순&quot;과 “최신순&quot;은 useState를 통해 관리함.
<br>
<img src="https://images.velog.io/images/te-me-city12/post/c45090ab-cdfb-45fb-8aa0-74343c69b1c2/Cap%202021-12-10%2017-44-31-634.jpg" alt="">실제로 적절한 리덕스 분배를 통해 커뮤니티페이지에서 탭과 탭 사이의 이동에 걸리는 시간이 약 25%~50% 감소하였음.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [12/3]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-123</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-123</guid>
            <pubDate>Fri, 10 Dec 2021 23:23:12 GMT</pubDate>
            <description><![CDATA[<p><strong>6주간 길게 이어 온 실전 프로젝트 발표가 오늘로서 드디어 끝났다. 사실 무척이나 중요한 날임에도 불구하고 아침부터 두통이 계속 이어지는 탓에 여러가지로 힘든 시간이기도 했지만 그래도 마무리가 잘 끝나야 모든 게 잘 끝나는 것이라는 말이 있기에 약을 먹으며 버텼고, 그래서 그런지 끝난 이후엔 뭔가 더 시원섭섭한 기분도 들었다. 오늘은 그간 준비해 온 프레젠테이션에 대해 기록하고 되돌아보는 시간을 가져보고자 한다.</strong></p>
<blockquote>
<p><strong>발표 프레젠테이션</strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/24401179-9662-476a-bf26-08540daa283e/Cap%202021-12-10%2017-37-46-887.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/384e45b3-0cf6-4230-a9dd-7999f5d36f15/Cap%202021-12-10%2017-37-57-341.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/a3d82aa7-6030-48db-b875-6eb6af1c83f6/Cap%202021-12-10%2017-39-07-893.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/3bcb6d43-fdaf-4ebd-81b8-2ef80721ec15/Cap%202021-12-10%2017-40-40-241.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/49de1ae8-2bc5-4fe3-8bd9-bc4e6804805b/Cap%202021-12-10%2017-40-56-929.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/f0e8e494-6481-4bff-979c-415f06d3c0ec/Cap%202021-12-10%2017-42-39-432.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/f725ecb6-4a14-47a1-844b-8fe0b0a64cae/Cap%202021-12-10%2017-43-19-196.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/76e21bff-ca89-461e-b3eb-29a767142f34/Cap%202021-12-10%2017-43-51-408.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/597a331c-45f1-46ce-bf47-cb410a9def86/Cap%202021-12-10%2017-44-13-598.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/528560dc-f26e-4966-b6b9-9a39a7a3a90c/Cap%202021-12-10%2017-44-26-562.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/e3ff1bf1-c8b9-4d52-a9a8-dd75d30a45b3/Cap%202021-12-10%2017-44-31-634.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/9e3df252-1a6b-4c84-a9ba-2a286b2356e9/Cap%202021-12-10%2017-54-45-448.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/d7731d85-f901-4058-ba03-4fe9a3b2a1a5/Cap%202021-12-10%2017-55-06-468.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/2b1f35b3-08b6-4270-a227-c4657a58ec88/Cap%202021-12-10%2017-55-26-198.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/d210b6fd-c598-4fec-8ad8-91bd82e25d33/Cap%202021-12-10%2017-55-33-089.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/f500952d-1260-46aa-93c3-a5d4b063b82d/Cap%202021-12-10%2017-55-38-797.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/faf4aabb-6239-48df-b0ac-bc4942314944/Cap%202021-12-10%2017-56-22-582.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/ebfc1ce9-8faf-4532-8fe9-cca3b815d49c/Cap%202021-12-10%2017-56-45-608.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/fb66e6b6-8c63-4bf2-a620-9edb9a82a90a/Cap%202021-12-10%2017-56-54-876.jpg" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/23443539-b9e8-4474-8bd4-608629398925/Cap%202021-12-10%2017-58-26-914.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [12/10]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1210</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1210</guid>
            <pubDate>Fri, 10 Dec 2021 07:42:06 GMT</pubDate>
            <description><![CDATA[<p><strong>두 번째 알고리즘 시험을 보는 날이 되었다. 아무리 생각해도 알고리즘을 공부하는 것은 쉬운 일이 아닌 것 같다. 그래도 포기하지 않고 꾸준히 공부하다보면 이전보다는 훨씬 발전된 모습을 발견할 수 있을 것이라고 믿고 할 수 있는 만큼은 해보려고 한다.</strong></p>
<blockquote>
<p><strong><a href="https://programmers.co.kr/learn/courses/30/lessons/64061">크레인 인형뽑기 게임</a></strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/048104d9-99eb-4aa2-9b8a-7e766ce0e76c/Cap%202021-12-10%2011-42-19-610.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/acd2d563-4174-474c-86d1-1e2f0eb47ea2/Cap%202021-12-10%2011-42-26-073.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/4fd55a04-9824-469e-a049-a1d034f8dc5e/Cap%202021-12-10%2011-42-30-066.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/354f3cfa-89f4-4142-9cfe-7c2cb55f9371/Cap%202021-12-10%2011-42-42-095.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/2c1a2ae8-20a2-4111-a377-7acad44b8892/Cap%202021-12-10%2011-42-50-630.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/272ae2a2-55b5-4f79-8712-be4f650f4263/Cap%202021-12-10%2011-42-55-688.jpg" alt=""></p>
<blockquote>
<p><strong>문제에서 구하는 것</strong></p>
</blockquote>
<p>이 문제에서는 &quot;바구니에 쌓으면 같은 모양 인형 두 개가 없어집니다.&quot; 라고 함.
즉, 인형을 뽑아 바구니에 저장해두는데, 이 때 같은 게 2개 쌓은 인형 두 개가 없어지게 됨. 따라서, 뽑은 순서대로 저장해 놓는 자료구조가 필요하고 순서대로 데이터를 저장하는 자료구조는 &quot;스택&quot; 또는 &quot;큐&quot;임. 현재 바구니는 아래서부터 쌓이고 위에서 빠지는 형태이므로 스택을 써야 한다는 것을 유추할 수 있음.</p>
<p>▶ <strong>우선 반환해 줘야 하는 것: &quot;크레인을 모두 작동시킨 후 터트려져 사라진 인형의 개수&quot;</strong></p>
<p>따라서 answer = 0 을 지정해서 반환해 줌. 그리고 스택을 이용해서 바구니를 구현할 것이라고 했기 때문에 그 값을 저장해두기 위한 bucket = [] 라는 변수를 만들어 둘 것.</p>
<pre><code>def solution(board, moves):
    bucket = []
    answer = 0

    return answer</code></pre><p>그러면 이제 moves 에 따라서 board 의 특정 칼럼의 가장 윗값을 조회해서 bucket 안에 넣어줘야 하는데 이는 moves 가 1일 때 board 의 1열을 (실제로는 인덱스 0) 위에서부터 조회해서 빈 값이 아닐 때 bucket 에다 쌓고, 또 moves 가 5라고 한다면 board 의 5열을 (실제로는 인덱스 4) 위에서부터 조회해서 빈 값이 아닐 때 bucket 에다 쌓는다는 것.</p>
<pre><code>python

# 아래와 같은 board 에 [1,5,3,5,1,2,1,4] 라는 moves 가 입력된다고 해볼 것.
# 0. moves 에서 원소를 하나씩 꺼내면서 board 의 열에 해당하는 가장 위의 원소를 bucket 에 넣으면 됨.
[
    [0, 0, 0, 0, 0],
    [0, 0, **1**, 0, **3**],
    [0, **2**, 5, 0, 1],
    [**4**, 2, 4, **4**, 2],
    [3, 5, 1, 3, 1]
] # board

# 1. 첫 번째 원소인 1, 즉 0번째 인덱스의 칼럼인 [0,0,0,4,3] 에서 가장 위의 원소인 4를 꺼내 bucket 에 넣음.
[
    [0, 0, 0, 0, 0],
    [0, 0, 1, 0, 3],
    [0, 2, 5, 0, 1],
    [**4**, 2, 4, 4, 2],
    [3, 5, 1, 3, 1]
] # board

-&gt; 

[
    [0, 0, 0, 0, 0],
    [0, 0, 1, 0, 3],
    [0, 2, 5, 0, 1],
    [**0**, 2, 4, 4, 2],
    [3, 5, 1, 3, 1]
] # board

[4] # bucket

# 2. 다음 원소인 5, 즉 4번째 인덱스의 칼럼인 [0,3,1,2,1] 에서 가장 위의 원소인 3을 꺼내 bucket 에 넣음.
[
    [0, 0, 0, 0, 0],
    [0, 0, 1, 0, **3**],
    [0, 2, 5, 0, 1],
    [0, 2, 4, 4, 2],
    [3, 5, 1, 3, 1]
] # board

-&gt;

[
    [0, 0, 0, 0, 0],
    [0, 0, 1, 0, **0**],
    [0, 2, 5, 0, 1],
    [0, 2, 4, 4, 2],
    [3, 5, 1, 3, 1]
]# board

[3, 4] # bucket

# 3. 다음 원소인 3, 즉 2번째 인덱스의 칼럼인 [0,1,5,4,1] 에서 가장 위의 원소인 1을 꺼내 bucket 에 넣음.
[
    [0, 0, 0, 0, 0],
    [0, 0, **1**, 0, 0],
    [0, 2, 5, 0, 1],
    [0, 2, 4, 4, 2],
    [3, 5, 1, 3, 1]
]# board

-&gt;

[
    [0, 0, 0, 0, 0],
    [0, 0, **0**, 0, 0],
    [0, 2, 5, 0, 1],
    [0, 2, 4, 4, 2],
    [3, 5, 1, 3, 1]
]# board

[1, 3, 4] # bucket

# 4. 다음 원소인 5, 즉 4번째 인덱스의 칼럼인 [0,0,1,2,1] 에서 가장 위의 원소인 1을 꺼내 bucket 에 넣음.
# 이 때, bucket의 맨 위에 1이 있습니다. 그러면 동일한 1, 1은 제거되고 result 에 2가 추가됨.
[
    [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0],
    [0, 2, 5, 0, **1**],
    [0, 2, 4, 4, 2],
    [3, 5, 1, 3, 1]
]# board

-&gt;

[
    [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0],
    [0, 2, 5, 0, **0**],
    [0, 2, 4, 4, 2],
    [3, 5, 1, 3, 1]
]# board

[3, 4] # bucket</code></pre><p>위의 내용을 코드로 구현하려고 할 때, 먼저 moves 를 반복문을 통해서 조회함. 여기서 move 를 인덱스로 변환해주기 위해서 index 라는 변수에 move - 1 값을 저장함. 이후 어떤 인덱스를 구해야 하는지는 알게될 때, 이 정보를 바탕으로 board 를 조회해야 함. 따라서 board 의 row 값들을 row_info 에 담은 뒤, 각 행에서 해당 index 에 있는 열의 원소 값이 0인지 아닌지 확인함. 만약 0이 아니라면 bucket 에 append 해서 넣어주고 중단시키면 됨.</p>
<pre><code>def solution(board, moves):
    bucket = []
    answer = 0

    for move in moves:
        index = move - 1
        for row_info in board:
            if row_info[index] != 0:
                bucket.append(row_info[index])
                break

    return answer</code></pre><p>하지만 이렇게 계속 bucket 에 넣으면 안 됨. 해당 row_info[index] 값을 bucket 에 담았다는 것은, 해당 원소의 값을 뽑았다는 것을 의미하므로 0으로 초기화 시켜주는 작업을 해야 함. 그래야 맨 위의 인형이 뽑혀져 나갔다는 것을 드러내줄 수 있음.</p>
<pre><code>def solution(board, moves):
    bucket = []
    answer = 0

    for move in moves:
        index = move - 1
        for row_info in board:
            if row_info[index] != 0:
                bucket.append(row_info[index])
                row_info[index] = 0
                break

    return answer</code></pre><p>또한, 추가적인 로직이 하나 더 필요함. 맨 위의 인형과 방금 넣은 인형이 같다면, 그 두 인형을 제거하고 answer 에 2를 추가하는 로직이 필요함.</p>
<p>따라서 bucket 의 맨 뒤에 원소가 맨 뒤에서 두번째 원소와 같다면, bucket 의 두 원소를 제거하며 answer 를 2를 더해주는 로직을 추가하면 됨.</p>
<pre><code>def solution(board, moves):
    bucket = []
    answer = 0

    for move in moves:
        index = move - 1
        for row_info in board:
            if row_info[index] != 0:
                bucket.append(row_info[index])
                row_info[index] = 0
                if len(bucket) &gt;= 2 and bucket[-1] == bucket[-2]:
                    answer += 2
                    bucket = bucket[0:-2]
                break
    return answer</code></pre><p>실제 코드를 작성하기 전에, 요구사항에 대해서 먼저 분석하고 어떻게 해결할 것인지에 대해서 입력 예시를 통해 생각해보시는 것이 좋음. 실제 런타임 시에 변수들이 어떻게 변화되는지에 대해 생각하다보면 &quot;아 이렇게 하면 더 효율적이겠구나&quot; 라는 직관이 오게 되고 이런 단계가 올 때까지 연습해보는 게 좋음.</p>
<pre><code># 결과 코드

def solution(board, moves):
    bucket = []
    answer = 0

    for move in moves:
        index = move - 1
        for row_info in board:
            if row_info[index] != 0:
                bucket.append(row_info[index])
                row_info[index] = 0
                if len(bucket) &gt;= 2 and bucket[-1] == bucket[-2]:
                    answer += 2
                    bucket = bucket[0:-2]
                break
    return answer</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [12/9]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-129</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-129</guid>
            <pubDate>Fri, 10 Dec 2021 02:31:12 GMT</pubDate>
            <description><![CDATA[<p><strong>어느덧 날짜는 목요일이 되었고 알고리즘 주차도 거의 끝을 다해가고 있다. 나는 오늘도 계속해서 알고리즘에 대해 좀 더 공부하고 벨로그에 내 나름대로 정리해보고자 한다.</strong></p>
<blockquote>
<p><strong><a href="https://www.acmicpc.net/problem/9012">괄호</a></strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/3cb489e7-4a0e-45c1-9b5e-a795605e0864/FireShot%20Capture%20005%20-%209012%EB%B2%88_%20%EA%B4%84%ED%98%B8%20-%20www.acmicpc.net.png" alt=""></p>
<blockquote>
<p><strong>해결 포인트</strong></p>
</blockquote>
<ul>
<li><p>&quot;(&quot;가 나올 때는 1을 더해주고, &quot;)&quot;가 나올 때는 1을 빼줘서 최종 sum이 0이 되면 해결된 것. 처음부터 문제 풀이를 해보면, t 변수로 테스트 케이스의 수를 입력받고 for문을 통해 테스트 케이스 수만큼 반복해주면서 그 안에서 check할 문자열을 입력받아 줌.</p>
</li>
<li><p>다음으로 check 변수로 받은 문자열은 ls 변수로 list로 입력받은 후 1을 더하고 빼주는 것을 기록할 sum 변수를 만들어서 0으로 초기화 시켜둠.</p>
</li>
<li><p>다음으로는 key point에 써둔 것처럼 for문으로 반복해주는데 여기서 for문 안에 sum이 0보다 작아질 때, 즉 &quot;)&quot;가 나와서 sum에 -1이 축적되어 조건을 만족하지 못할 때 &quot;NO&quot;를 출력하고 break 해줌.</p>
</li>
<li><p>나머지는 sum이 0보다 크다면 문자열에 &quot;(&quot;가 하나 더 있는거니 조건에 만족하지 못하여 &quot;NO&quot;가 출력됨.</p>
</li>
</ul>
<p>▶ 마지막으로 else가 아니라 elif를 써서 또 하나의 조건문으로 sum이 0일 때 즉, &quot;(&quot;와 &quot;)&quot;가 만나서 0이 될 때만 &quot;YES&quot;를 출력하면 최종 해결.
<br></p>
<pre><code># 9012번 : 괄호
t = int(input())

for _ in range(t):
    check = input()
    ls = list(check)
    sum = 0

    for i in ls:
        if i == &quot;(&quot;:
            sum += 1
        elif i == &quot;)&quot;:
            sum -= 1
        if sum &lt; 0:
            print(&quot;NO&quot;)
            break

    if sum &gt; 0:
        print(&quot;NO&quot;)
    elif sum == 0:
        print(&quot;YES&quot;)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [12/2]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-122</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-122</guid>
            <pubDate>Thu, 09 Dec 2021 03:48:16 GMT</pubDate>
            <description><![CDATA[<p><strong>오랜 시간 동안 준비해온 실전 프로젝트 발표일이 드디어 내일로 다가왔다. 하지만 내가 속한 프론트엔드의 경우는 이미 만들어진 반응형 웹을 기반으로 한 모바일로의 추가 작업(뷰)이라는 또 다른 숙제가 남았고 따라서 이를 위해 모바일 우선 반응형 웹 디자인에 대해 좀 더 공부해 보는 시간을 가져보기로 했다.</strong></p>
<h3 id="▶-모바일-우선-반응형-웹-디자인-1">▶ <strong>모바일 우선 반응형 웹 디자인 (1)</strong></h3>
<blockquote>
<p><strong>설계</strong></p>
</blockquote>
<p><a href="https://www.youtube.com/watch?v=8-uJ_4136uI&amp;ab_channel=%EB%93%9C%EB%A6%BC%EC%BD%94%EB%94%A9by%EC%97%98%EB%A6%AC">출처: 유튜브-드림코딩 by 엘리</a></p>
<p><strong>화면 구성</strong>
header, nav, main, footer 등 모바일 화면을 구성하는 각 요소들의 배치를 고려함.</p>
<p><img src="https://images.velog.io/images/te-me-city12/post/d2325cb7-fbf7-4b35-9645-5756aa97358e/%EB%B0%98%EC%9D%91%ED%98%95%20%EC%9B%B9%20%ED%99%94%EB%A9%B4%20%EA%B5%AC%EC%A1%B0.png" alt=""></p>
<p><strong>브레이크 포인트</strong>를 정함.</p>
<p><img src="https://images.velog.io/images/te-me-city12/post/4a4de53b-3027-4ad7-8eb2-c0abb377fdeb/%EB%B8%8C%EB%A0%88%EC%9D%B4%ED%81%AC%ED%8F%AC%EC%9D%B8%ED%8A%B8.png" alt=""></p>
<p>위는 범용 예시들이며, 절대적인 기준이 아니므로 지원하고자 하는 기기와 실제 동작을 참고하여 조정해야 함.</p>
<blockquote>
<p><strong>미디어 쿼리</strong></p>
</blockquote>
<p><strong>정의</strong>
사용자 미디어의 종류와 상태에 대해 질의를 던져 기기의 종류나 화면 크기에 맞춰 개별적인 css 코드가 적용될 수 있도록 하는 CSS3의 기능.</p>
<p><strong>문법</strong>
@media only|not 매체유형 and (표현식) { CSS스타일코드; }</p>
<p><strong>예시</strong>
<img src="https://images.velog.io/images/te-me-city12/post/987c3612-04f9-4b8c-a6a2-6fe8e0c40343/%EB%AF%B8%EB%94%94%EC%96%B4%20%EC%BF%BC%EB%A6%AC.png" alt=""></p>
<p><a href="https://www.nextree.co.kr/p8622/">출처: Nextree</a></p>
<p><strong>브라우저 호환성</strong></p>
<p>IE9부터 지원. 브라우저 호환성 좋음.</p>
<p><img src="https://images.velog.io/images/te-me-city12/post/c77ac0cb-08c2-4f8b-97d9-6850b289555d/media%20query.png" alt=""></p>
<p><a href="https://caniuse.com/#home">출처: Can I use</a></p>
<blockquote>
<p><strong>뷰포트</strong></p>
</blockquote>
<p><strong>정의</strong>
화면 위 가상의 표시 영역</p>
<p><img src="https://images.velog.io/images/te-me-city12/post/35a49e81-c6af-4105-a23d-9d36d079c171/%EB%B7%B0%ED%8F%AC%ED%8A%B8.png" alt=""></p>
<p><a href="https://jw910911.tistory.com/24">출처: 번데기 개발자님 티스토리 블로그</a></p>
<ul>
<li><p>뷰포트 시초는 애플인데, 아이폰 3GS 시절 320 x 480개의 픽셀로 데스크톱 기준으로 개발된 웹 사이트를 렌더링 할 때 한 화면에 담기지 못하는 문제점이 있었고 이에 애플은 가상의 뷰포트 개념을 도입해 320px인 디스플레이를 980px의 뷰포트에 렌더링을 함. ☞ 이후 별다른 뷰포트 설정이 없으면 대부분의 모바일 브라우저의 뷰포트는 980px이 기본값이 됨.</p>
</li>
<li><p>뷰포트를 제대로 알려면 픽셀을 알아야 하는데 픽셀은 기기의 물리적 크기와 다르며 개수로 세는 것.</p>
<ul>
<li><p>ex) 해상도 확인</p>
<ol>
<li>LG그램 15인치: 1920x1080 (가로 1920개, 세로 1080개 픽셀)</li>
<li>갤럭시 S10: 1440x3040 (가로 1440개, 세로 3040개 픽셀)</li>
</ol>
</li>
<li><p>갤럭시는 그램에 비해 물리적인 크기가 작을 뿐, 픽셀 개수는 적지 않으며
물리적인 화면 크기는 훨씬 작은데 데스크톱과 비슷한 개수의 픽셀로 렌더링 시,    작아보일 수 밖에 없음. 실제로는 뷰포트 설정이 없으면 980px로 렌더링 되겠지만, 이 또한 스마트폰의 화면에서는 큰 편임.</p>
<p>☞ 작은 스마트폰 화면에 웹 페이지를 모두 표시하려고 하니 전체 페이지의 배율이 작게 축소되어 보이는 것.</p>
</li>
</ul>
</li>
</ul>
<p>▶ <strong>해결 방법</strong></p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;
    &lt;/head&gt;
    &lt;body&gt;
        // mobile first markup
    &lt;/body&gt;
&lt;/html&gt;</code></pre><p><strong>width</strong>: 뷰포트의 가로 크기를 결정.
device-width로 설정하면 최적화된 UI를 적용하기 위해 기기마다 독립적으로 설정된 픽셀값을 뷰포트의 width로 설정하고, 그것에 웹 페이지를 맞춤.</p>
<p><strong>initial-scale</strong>: 페이지가 처음 로드될 때 줌 레벨을 조정.</p>
<p>그 외</p>
<ul>
<li><strong>minimum-scale</strong> : viewport의 최소 배율값, 기본값은 0.25.</li>
<li><strong>maximum-scale</strong> : viewport의 최대 배율값, 기본값은 1.6.</li>
<li><strong>user-scalable</strong> : 사용자의 확대/축소 기능을 설정, 기본값은 yes.</li>
</ul>
<blockquote>
<p><strong>모바일 우선</strong></p>
</blockquote>
<ul>
<li>우선 모바일을 기준으로 마크업을 함.</li>
<li>이후 작성된 코드 아래에 미디어 쿼리를 사용해 각 브레이크 포인트별로 코드를 더 함.</li>
</ul>
<pre><code>.block__element--modifier {
    // Mobile
    @media screen and (min-width: 768px) and (max-width: 1023px) {
        // Tablet
    }
    @media screen and (min-width: 1024px) {
        // PC
    }
}</code></pre><blockquote>
<p><strong>css 단위</strong></p>
</blockquote>
<h3 id="px"><strong>px</strong></h3>
<p>◻ <strong>소개</strong>
css의 가장 기본 단위로, 1px는 화소 1개의 크기를 의미하며 절대값임.</p>
<p>◻ <strong>사용법</strong>
사용자의 브라우저 설정에도 불구하고 늘 절대적인 값을 유지했으면 하는 경우
ex) border</p>
<p>◻ <strong>주의사항</strong>
웹 접근성(Accessibility)을 위해 위와 같이 꼭 필요할 때만 사용하는 것이 좋음.</p>
<h4 id="">%</h4>
<p>◻ <strong>소개</strong>
언제나 다른 수치를 참조하며 참조값은 같은 요소에 적용된 다른 속성이거나 혹은 조상 요소의 같은 속성이거나, 컨테이닝 블록의 오프셋 속성(left, right, top, bottom)이나 박스모델 속성(width, height, margin, padding)과 같은 서식 상황(formatting context)에서의 측정값이거나, 혹은 다른 어떤 것임.</p>
<p><a href="https://www.w3.org/TR/css-values-4/#percentages">참조: w3.org</a></p>
<p>◻ <strong>사용법</strong>
레이아웃 지정에 유용함.</p>
<p><img src="https://images.velog.io/images/te-me-city12/post/8481ba4f-96f9-49b4-b45f-89bb0a6a4667/%EB%B8%94%EB%A1%9C%EA%B7%B8.png" alt=""></p>
<pre><code>// html
&lt;article class=&quot;blog__post&quot;&gt;
    &lt;div class=&quot;blog__post--image&quot;&gt;
    &lt;/div&gt;
    &lt;div class=&quot;blog__post--content&quot;&gt;
    &lt;/div&gt;
&lt;/article&gt;

// css
.blog__post {
    width: 100%;
}

.blog__post .blog__post--image {
    width: 50%;
}

.blog__post .blog__post--content {
    width: 50%;
}</code></pre><p>flexbox와 함께 사용하면 유용함.</p>
<pre><code>// html
&lt;div class=&quot;container&quot;&gt;
    &lt;div class=&quot;item&quot;&gt;1&lt;/div&gt;
    &lt;div class=&quot;item&quot;&gt;22&lt;/div&gt;
    &lt;div class=&quot;item&quot;&gt;three&lt;/div&gt;
&lt;/div&gt;

//css
.container {
    display: flex;
    justify-content: center;
    background-color: red;
}

.container .item {
    width: 20%;
    border: 1px solid black;
    background-color: skyblue;
}</code></pre><p>sandbox에서 위 코드를 실험한 결과 container에 display: flex와 justify-content: center를 선언하면 디폴트로 item들의 width는 내부 요소의 width에 맞춰져 주축 row를 기준으로 중앙 정렬 되고, 창의 폭을 줄이기 시작하면 여백이 먼저 감소하다가 여백이 모두 줄어들었을 때에도 item들의 width는 내부 요소의 width 아래로 떨어지지 않음.</p>
<p>이 때, item에 width: 20%를 선언하면 item들이 container의 width를 20%씩 총 60%를 갖게 되고 나머지 40%는 양 쪽에 20%씩 나눠져서 그 비율이 내부 요소의 크기와 관계없이 유지됨. 즉, 위와 같이 창의 폭을 줄일 때, item의 width가 내부 요소의 width보다 줄어들더라도 해당 비율을 고수하며 이는 height도 마찬가지임.</p>
<ul>
<li>마진, 패딩에 사용하면 유용함.<br>

</li>
</ul>
<p>◻ <strong>주의사항</strong>
% 단위가 무조건 부모 요소를 참조한다고 생각하기 쉽지만, 이는 사실이 아니며 특히 박스 모델 속성(width, height, padding, margin)과 오프셋 속성(left, right, top, bottom)에 %를 적용할 때 -&gt; % 수치는 단순히 &#39;부모 요소&#39;가 아닌 &#39;컨테이닝 블록&#39;을 기준으로 계산됨. 대부분의 경우 어떤 요소의 컨테이닝 블록은 가장 가까운 블록 레벨 조상의 콘텐츠 영역이나, 항상 그렇지는 않음.</p>
<ul>
<li><p>height, top, bottom은 컨테이닝 블록의 height를, width, padding, margin, left, right은 width를 사용해 백분율을 계산함.</p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/CSS/Containing_block">참고: 컨테이닝 블록의 모든 것-MDN</a></p>
</li>
<li><p>특정 요소의 height에 100%를 선언할 때 margin을 고려하지 못하면 컨테이닝 블록 밖으로 탈출하는 현상이 생김.</p>
</li>
</ul>
<p><img src="https://images.velog.io/images/te-me-city12/post/656cae7a-3411-4f69-bdd3-d0914cbd6c3e/%ED%8A%B8%EB%9E%99%20%EC%9D%B4%ED%83%88%20%EC%82%AC%EC%A7%84.png" alt=""></p>
<pre><code>// html
&lt;div class=&quot;slider__track&quot;&gt;
    &lt;div class=&quot;slider__track--slide&quot;&gt;
    &lt;/div&gt;
&lt;/div&gt;

// css
.slider__track {
    display: flex;
    justify-content: center;
    height: 150px;
    background-color: red;
}

.slider__track--slide {
    width: 20%;
    height: 100%;
    margin: 20px 0;
    background-color: skyblue;
}</code></pre><p>✔ <strong>해결 방법</strong></p>
<ol>
<li>slide 요소의 마진을 제거함.</li>
<li>slide 요소의 height에서 마진값을 뺌.</li>
</ol>
<pre><code>.slider__track--slide {
    height: calc(100% - (20px + 20px));
}</code></pre><ul>
<li>컨테이닝 블록의 중앙에 내부 요소를 배치하기 위해 position과 top, left를 %와 함께 사용할 때, 아래와 같이 내부 요소의 중심이 아닌 모서리가 중앙에 맞춰짐.</li>
</ul>
<p><img src="https://images.velog.io/images/te-me-city12/post/9329402d-8ccf-423e-9d2f-98d856d14f8a/%EC%A4%91%EC%95%99%20%EC%A0%95%EB%A0%AC%20%EC%82%AC%EC%A7%84%201.png" alt=""></p>
<pre><code>// html
&lt;header class=&quot;header&quot;&gt;
    &lt;div class=&quot;header__logo&quot;&gt;
    &lt;/div&gt;
&lt;/header&gt;

// css
.header {
    position: relative;
}

.header .header__logo {
    position: absolute;
    top: 50%;
    left: 50%;
}</code></pre><p><strong>✔ 해결 방법</strong></p>
<pre><code>.header .header__logo {
    position: absolute;
    top: 50%;
    left: 50%;
    // 아래 코드를 추가함
    transform: translate(-50%, -50%);
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [12/8]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-128</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-128</guid>
            <pubDate>Wed, 08 Dec 2021 08:11:02 GMT</pubDate>
            <description><![CDATA[<p><strong>오늘은 알고리즘 시험을 보았다. 예상은 했지만 나에게는 쉽지 않은 문제였기에 제한 시간 동안 나름 씨름을 해 보았지만, 결국 완전하게 해결을 하지는 못했다. 하지만 이 알고리즘 주차 기간 동안 조금이라도 더 알고리즘을 이해할 수 있다면 그것만으로 큰 수확이라고 생각하고 여기에 시험 문제를 정리해보고자 한다.</strong></p>
<blockquote>
<p><strong><a href="https://programmers.co.kr/learn/courses/30/lessons/17683?language=python3">[3차] 방금그곡</a></strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/2fc6c4b5-860b-4daa-ae0d-727b3d187960/1.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/80acfcd2-74b9-4136-b5a6-7865a8c4db7a/2.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/05d432cb-6d2a-416f-a8cb-f74ef7e0d654/3.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/ec98ec89-dc09-4b33-ae20-65ce5a662f0f/4.png" alt=""></p>
<blockquote>
<p><strong>문제에서 구하는 것</strong></p>
</blockquote>
<p>▶ <strong>자신이 들은 멜로디가 포함되어 있는 음악 중 재생된 시간이 제일 긴 음악 제목</strong></p>
<p>따라서, 그러면 자신이 들은 멜로디가 각 음악에 포함되어 있는지 아닌지를 먼저 파악함. 그 뒤 포함되어 있는 음악들 중에서 재생된 시간이 가장 긴 음악의 제목을 반환하면 됨.</p>
<blockquote>
<p><strong>주의할 점</strong></p>
</blockquote>
<p>▶ 입출력 예시에서 HELLO는 C#DEFGABC#DEFGAB로, WORLD는 ABCDE로 재생됨. HELLO 안에 있는 ABC#은 기억한 멜로디인 ABC와 일치하지 않고, WORLD 안에 있는 ABC가 기억한 멜로디와 일치함. → ABC# 은 ABC 를 포함하지 않음. 즉, C# 과 C는 아예 다른 음이라는 것! 따라서 C# 과 C를 구분하기 위한 방법을 구해야 함.</p>
<blockquote>
<p>✅ 첫 번째 방법: 문자열을 기준으로 묶어버리는 것.</p>
</blockquote>
<p><code>C#DEFGABC#DEFGAB</code> 라는 문자열을 [&quot;C#&quot;, &quot;D&quot;, E&quot;, &quot;F&quot;, &quot;G&quot;, &quot;A&quot;, &quot;B&quot;, &quot;C#&quot;, &quot;D&quot;, E&quot;, &quot;F&quot;, &quot;G&quot;, &quot;A&quot;, &quot;B&quot;] 라고 하는 것. 그러면 이 안에 ABC 가 있는지 확인하기 위해서 배열을 순회하면서 &quot;A&quot;, &quot;B&quot;, &quot;C&quot; 원소가 순서대로 있는지 파악하면 됨.</p>
<blockquote>
<p>✅ 두 번째 방법: #이 붙은 문자들을 치환해버리는 것.</p>
</blockquote>
<p>C# → c, D# → d 처럼 둘 때,<code>C#DEFGABC#DEFGAB</code> 라는 문자열이 <code>cDEFGABcDEFGAB</code> 라고 표시가 됨. 그러면 이 안에 ABC 가 있는지 확인하기 위해 문자열을 돌면서 &quot;ABC&quot; 라는 부분 문자열이 있는지 파악하면 됨.
<br>
▶두 가지 경우 다 비슷한 시간 복잡도와 구현 난이도가 있지만, 두 번째 방법이 보다 편한 방법이라고 할 수 있기에 두 번째 방법 채택. python 에서는 문자열 찾기 관련된 함수가 많기 때문에 쉬울 수 있음.</p>
<p>우선 #이 붙은 문자들을 치환해버리기로 했으니, replace_step 이라는 함수를 만들어서 m의 #붙은 문자열들을 치환해주도록 함. </p>
<pre><code>def replace_step(m):
    return m.replace(&#39;C#&#39;, &#39;c&#39;).replace(&#39;D#&#39;, &#39;d&#39;).replace(&#39;F#&#39;, &#39;f&#39;).replace(&#39;G#&#39;, &#39;g&#39;).replace(&#39;A#&#39;, &#39;a&#39;)


def solution(m, musicinfos):
    answer = &#39;&#39;
    m = replace_step(m)

    return answer</code></pre><br>
이제 musicinfos 를 순회하면서, music info 에서 원하는 정보를 뽑아보면 시작 시간, 끝나는 시간, 이름, 멜로디가 "," 라는 문자를 기준으로 이루어져있는데 이를 분리해서 변수에 담아봄. 음악의 시작 시각과 끝난 시각을 이용하는 용도는 단순히 이 음악이 몇초나 재생되었는지 밖에 없음. 따라서 end_time 에서 start_time 을 빼서 얼마나 재생되었는지, play_time 을 계산해봄.
<br>
<br>

<pre><code>def replace_step(m):
    return m.replace(&#39;C#&#39;, &#39;c&#39;).replace(&#39;D#&#39;, &#39;d&#39;).replace(&#39;F#&#39;, &#39;f&#39;).replace(&#39;G#&#39;, &#39;g&#39;).replace(&#39;A#&#39;, &#39;a&#39;)


def solution(m, musicinfos):
    answer = &#39;&#39;
    m = replace_step(m)

    for musicinfo in musicinfos:
        start_time, end_time, name, melody = musicinfo.split(&quot;,&quot;)
        play_time = (int(end_time[:2]) * 60 + int(end_time[3:])) - (int(start_time[:2]) * 60 + int(start_time[3:]))

    return answer</code></pre><p>여기서, melody 에는 아직 C# 등의 문자가 남아있을테니 마찬가지로 치환을 해 주면, melody 및 그 멜로디가 몇초나 진행되었는지도 알 수 있음. 예를 들어, ABC라는 멜로디가 15초 진행되었다고 하면 ABCABCABCABCABC는 이렇게 계속 반복이 되었을 것이며 이는 3이라는 멜로디의 길이를 15초 동안 실행해주기 위해서 5번이 반복된 것. 만약 실행기간이 16초 였다면 ABCABCABCABCABCA 까지 실행되었을 것임.</p>
<p>즉, 멜로디의 모습과 실행시간을 이용해서 실제 실행된 멜로디를 알아와야 하는데 이를 구하기 위해 올림(실행시간 / 멜로디의 길이)을 통해서 멜로디를 반복할 숫자를 구하면 ABCABCABCABCABCABC 라는 문자열이 됨. 그리고 멜로디의 길이 까지 잘라주는데, 이 때 16까지 길이를 잘라주면 ABCABCABCABCABCA 가 되기 때문에, 실제 실행된 멜로디를 구할 수 있음.</p>
<pre><code>import math


def replace_step(m):
    return m.replace(&#39;C#&#39;, &#39;c&#39;).replace(&#39;D#&#39;, &#39;d&#39;).replace(&#39;F#&#39;, &#39;f&#39;).replace(&#39;G#&#39;, &#39;g&#39;).replace(&#39;A#&#39;, &#39;a&#39;)


def solution(m, musicinfos):
    answer = &#39;&#39;
    m = replace_step(m)

    for musicinfo in musicinfos:
        start_time, end_time, name, melody = musicinfo.split(&quot;,&quot;)
        play_time = (int(end_time[:2]) * 60 + int(end_time[3:])) - (int(start_time[:2]) * 60 + int(start_time[3:]))

        melody = replace_step(melody)
        melody_repeated_count = math.ceil(play_time / len(melody))
        melody_played = (melody * melody_repeated_count)[:play_time]
    return answer</code></pre><p>따라서 melody_played 는 ABCABCABCABCABCABC 가 만들어짐. 이 안에 m 이라는 문자열이 존재하는지 확인하는데 이 때, in 이라는 연산자를 쓰면 간단하게 구현가능.</p>
<p>최종적으로 구하려는 것은 자신이 들은 멜로디가 포함되어 있는 음악 중 재생된 시간이 제일 긴 음악 제목. 즉, 가장 재생된 시간이 긴 제목을 저장하고 있어야 함. 따라서 이를 구분하기 위해 max_play_time 이라는 변수를 만들고, answer 의 기본값을 None 으로 만듬. 만약 play_time 이 max_play_time 보다 크다면 answer 와 max_play_time 을 갱신하도록 함.</p>
<pre><code>import math


def replace_step(m):
    return m.replace(&#39;C#&#39;, &#39;c&#39;).replace(&#39;D#&#39;, &#39;d&#39;).replace(&#39;F#&#39;, &#39;f&#39;).replace(&#39;G#&#39;, &#39;g&#39;).replace(&#39;A#&#39;, &#39;a&#39;)


def solution(m, musicinfos):
    answer = None
    max_play_time = 0
    m = replace_step(m)

    for musicinfo in musicinfos:
        start_time, end_time, name, melody = musicinfo.split(&quot;,&quot;)
        play_time = (int(end_time[:2]) * 60 + int(end_time[3:])) - (int(start_time[:2]) * 60 + int(start_time[3:]))

        melody = replace_step(melody)
        melody_repeated_count = math.ceil(play_time / len(melody))
        melody_played = (melody * melody_repeated_count)[:play_time]
        if m in melody_played and play_time &gt; max_play_time:
            answer = name
            max_play_time = play_time

    return answer</code></pre><p>그리고 &#39;조건이 일치하는 음악이 없을 때에는 “(None)”을 반환한다.&#39; 라는 요구사항에 맞춰서 간단한 if 문도 추가하면 완료.</p>
<pre><code>import math


def replace_step(m):
    return m.replace(&#39;C#&#39;, &#39;c&#39;).replace(&#39;D#&#39;, &#39;d&#39;).replace(&#39;F#&#39;, &#39;f&#39;).replace(&#39;G#&#39;, &#39;g&#39;).replace(&#39;A#&#39;, &#39;a&#39;)


def solution(m, musicinfos):
    answer = None
    max_play_time = 0
    m = replace_step(m)

    for musicinfo in musicinfos:
        start_time, end_time, name, melody = musicinfo.split(&quot;,&quot;)
        play_time = (int(end_time[:2]) * 60 + int(end_time[3:])) - (int(start_time[:2]) * 60 + int(start_time[3:]))

        melody = replace_step(melody)
        melody_repeated_count = math.ceil(play_time / len(melody))
        melody_played = (melody * melody_repeated_count)[:play_time]
        if m in melody_played and play_time &gt; max_play_time:
            answer = name
            max_play_time = play_time
    if not answer:
        return &quot;(None)&quot;
    return answer</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [12/7]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-127</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-127</guid>
            <pubDate>Wed, 08 Dec 2021 03:36:13 GMT</pubDate>
            <description><![CDATA[<p><strong>알고리즘 주간 2일차, 오늘도 어제와 크게 다른 것 없이 주어진 알고리즘 인강으로 학습을 하며 백준 사이트에서 또다른 알고리즘 문제를 학습해보는 시간을 가져보았다. 사실 알고리즘 문제를 푸려고 할 때마다 수학문제의 연장선 같기도 하고 감이 오지 않는 경우가 아직까지는 대부분이다. 하지만 첫 술에 배부를 수는 없는 법이니, 일종의 문제해결력을 기른다는 생각을 하며 참을성을 가지고 이어나가 보려고 한다.</strong></p>
<blockquote>
<p><strong><a href="https://www.acmicpc.net/problem/4948">베르트랑 공준</a></strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/f00acd11-d61c-4ed8-b614-c82544989ab4/%EB%B2%A0%EB%A5%B4%ED%8A%B8%EB%9E%91%20%EA%B3%B5%EC%A4%80.png" alt=""></p>
<blockquote>
<p><strong>단계별 해결법</strong></p>
</blockquote>
<p>해답을 도출하기 위해서는 먼저 소수를 구하는 방법을 알아햐 함.</p>
<p>링크 : <a href="https://ko.wikipedia.org/wiki/%EC%86%8C%EC%88%98_(%EC%88%98%EB%A1%A0)">https://ko.wikipedia.org/wiki/%EC%86%8C%EC%88%98_(%EC%88%98%EB%A1%A0)</a>
<img src="https://images.velog.io/images/te-me-city12/post/d9524c10-2604-4feb-a4f0-5cef91bb5896/%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C.png" alt=""></p>
<p>이번 문제에서 까다로웠던 부분은 &quot;시간 초과&quot;. 결과를 도출하는 것까지 수행을 완료했지만 cpu 부담을 최대한 낮추어 경량화할 수 있는 코드가 필요했음. 처음에 진행한 코드는 배열에 소수들을 미리 저장시키지 않고서 즉각적인 계산을 수행하도록 했지만,정답이 된 코드는 소수들을 미리 구하여 리스트나 튜플에 저장시킨 후 값들을 찾아내야 했음.</p>
<p>처음에는 소수들을 저장하는 리스트 만드는 시간이 더 오래 걸릴 것 같다는 생각을 했지만, 좀 더 생각해봤을 때 주어진 값만 입력하는 것이 아니라 더욱 다양한 값들을 입력했을 경우에는 즉각적인 연산 방식이 cpu를 기하급수적으로 많이 사용하는 결과를 가져온다는 것.</p>
<blockquote>
<p><strong>정답 코드</strong></p>
</blockquote>
<pre><code>import math

# 소수인지를 판별하는 함수
def checkValue(numValue):
    if numValue == 1:
        return True
    else:
        for comp in range(2, int(math.sqrt(number))+1):
            if numValue % comp == 0:
                return False
    return True


# 소수를 리스트에 저장
value = list(range(123456*2))
prime_number = list()

for number in value:
    if checkValue(number):
        prime_number.append(number)


# 사용자 입력 및 결과 출력
while(1):
    insertN = int(input())
    cnt = 0

    if insertN == 0:
        break

    for valueN in prime_number:
        if insertN &lt; valueN &lt;= insertN*2:
            cnt += 1
    print(cnt)
</code></pre><blockquote>
<p><strong>시간 초과 코드</strong></p>
</blockquote>
<pre><code>import math

# 소수를 판별하는 함수
def checkValue(numValue):
    if numValue == 1:
        return True
    else:
        for comp in range(2, int(math.sqrt(number))+1):
            if numValue % comp == 0:
                return False
    return True


# 사용자 입력 및 출력 부분
while(1):
    insertN = int(input())
    prime_number = 0
    if insertN == 0:
        break
    else:
        for number in range(insertN+1, insertN*2+1):
            if checkValue(number):
                prime_number += 1
    print(prime_number)
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [12/4]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-124</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-124</guid>
            <pubDate>Tue, 07 Dec 2021 06:36:44 GMT</pubDate>
            <description><![CDATA[<p><strong>6주간 어떻게 달려왔는지도 모를 실전 프로젝트와 발표까지 끝나고 맞은 토요일은 너무나 달콤했다. 오늘은 별다른 일을 하지 않고, 그저 다면평가 및 다음 주부터 시작될 스케쥴을 생각해보는 시간과 이전에 공부하던 것들을 정리하는 시간을 가져보기로 했다.</strong></p>
<blockquote>
<p><strong>여러개의 input 상태 관리하기 (React)</strong></p>
</blockquote>
<h4 id="▶-inputsamplejs-1"><strong>▶ InputSample.js 1</strong></h4>
<pre><code>import React, { useState } from &#39;react&#39;;

function InputSample() {
  const onChange = (e) =&gt; {
  };

  const onReset = () =&gt; {
  };


  return (
    &lt;div&gt;
      &lt;input placeholder=&quot;이름&quot; /&gt;
      &lt;input placeholder=&quot;닉네임&quot; /&gt;
      &lt;button onClick={onReset}&gt;초기화&lt;/button&gt;
      &lt;div&gt;
        &lt;b&gt;값: &lt;/b&gt;
        이름 (닉네임)
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default InputSample;</code></pre><p><img src="https://images.velog.io/images/te-me-city12/post/7570d6de-c00b-4a95-b383-69d73ab1cb9c/%EA%B2%B0%EA%B3%BC.png" alt=""></p>
<p>input 의 개수가 여러개가 됐을 때는 단순히 useState 를 여러번 사용하고 onChange 도 여러개 만들어서 구현 가능하지만 그것보다는, input 에 name 을 설정하고 이벤트가 발생했을 때 이 값을 참조하는 것이 더 좋음. 또, useState 에서는 문자열이 아니라 객체 형태의 상태를 관리해주어야 함.</p>
<h4 id="▶-inputsamplejs-2"><strong>▶ InputSample.js 2</strong></h4>
<pre><code>import React, { useState } from &#39;react&#39;;

function InputSample() {
  const [inputs, setInputs] = useState({
    name: &#39;&#39;,
    nickname: &#39;&#39;
  });

  const { name, nickname } = inputs; // 비구조화 할당을 통해 값 추출

  const onChange = (e) =&gt; {
    const { value, name } = e.target; // 우선 e.target 에서 name 과 value 를 추출
    setInputs({
      ...inputs, // 기존의 input 객체를 복사한 뒤
      [name]: value // name 키를 가진 값을 value 로 설정
    });
  };

  const onReset = () =&gt; {
    setInputs({
      name: &#39;&#39;,
      nickname: &#39;&#39;,
    })
  };


  return (
    &lt;div&gt;
      &lt;input name=&quot;name&quot; placeholder=&quot;이름&quot; onChange={onChange} value={name} /&gt;
      &lt;input name=&quot;nickname&quot; placeholder=&quot;닉네임&quot; onChange={onChange} value={nickname}/&gt;
      &lt;button onClick={onReset}&gt;초기화&lt;/button&gt;
      &lt;div&gt;
        &lt;b&gt;값: &lt;/b&gt;
        {name} ({nickname})
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default InputSample;</code></pre><br>

<p><strong>리액트 상태에서 객체를 수정해야 할 때는</strong></p>
<pre><code>inputs[name] = value;</code></pre><p><strong>이런식으로 직접 수정하면 안되며 대신 새로운 객체를 만들어서 새로운 객체에 변화를 주고, 이를 상태로 사용해주어야 함.</strong></p>
<pre><code>setInputs({
  ...inputs,
  [name]: value
});</code></pre><p>여기서 사용한 ... 문법은 spread 문법으로, 객체의 내용을 모두 &quot;펼쳐서&quot; 기존 객체를 복사해줌. <a href="https://learnjs.vlpt.us/useful/07-spread-and-rest.html">(참고링크)</a></p>
<p>이러한 작업을, &quot;불변성을 지킨다&quot; 라고 부름. 불변성을 지켜주어야만 리액트 컴포넌트에서 상태가 업데이트가 됐음을 감지 할 수 있고 이에 따라 필요한 리렌더링이 진행됨. 만약 inputs[name] = value 이런식으로 기존 상태를 직접 수정하게 되면, 값을 바꿔도 리렌더링이 되지 않음. 또한 리액트에서는 불변성을 지켜주어야 컴포넌트 업데이트 성능 최적화도 가능함.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [12/6]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-126</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-126</guid>
            <pubDate>Mon, 06 Dec 2021 11:11:06 GMT</pubDate>
            <description><![CDATA[<p><strong>실전 프로젝트가 끝나고 새로운 한 주가 또 시작되었다. 나는 이번 주와 다음 주 동안 백준 사이트에서 알고리즘 학습 및 추후엔 개인공부를 하기로 했고, 오늘 TIL에는 백준 사이트에서 풀었던 알고리즘 문제에 대해 정리해보고자 한다.</strong></p>
<blockquote>
<p><strong><a href="https://www.acmicpc.net/problem/2839">설탕배달</a></strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/02aeefd5-4ae2-49e3-8c13-08fd870aa888/%EC%84%A4%ED%83%95%EB%B0%B0%EB%8B%AC.png" alt=""></p>
<p>사용자로부터 설탕 무게를 입력 받고 3kg과 5kg 봉지를 최소로 사용해 담으면 몇봉지가 나오는지를 계산하는 문제로, 만약 정확히 떨어지지 않는다면 -1을 반환하는 문제.</p>
<blockquote>
<p><strong>단계별 알고리즘</strong></p>
</blockquote>
<p>1) 5kg 에 설탕을 빠짐없이 담을 수 있다면 (입력 값이 5로 나눠떨어진다면) 입력값을 5로 나눈 몫을 출력하고 프로그램 종료</p>
<p>2) 5kg 에 설탕을 빠짐없이 담을 수 없다면 3kg 봉지에 한 번 담기 (봉지 카운트는 1 증가, 입력값은 3 감소)</p>
<p>3) 만약 입력값이 0보다 작아지면 -1을 출력하고 프로그램 종료</p>
<blockquote>
<p><strong>단계별 해결법</strong></p>
</blockquote>
<p>1) Box 변수와 inp 변수를 두고, inp 변수에는 int(input()) 을 이용해 사용자의 입력을 받고, Box 변수를 통해 발생하는 봉지수를 카운트 함.</p>
<p>2) while True 를 이용해 무한 루프를 만들어 줌.</p>
<p>3) while 문 내에 if 문을 넣어 inp 값이 5로 나눠지면 Box내에 inp 값을 5로 나눈 몫을 넣고 그 값을 프린트 한 후, while 문을 종료. 만약 그렇지 않다면 inp 값을 3 감소시키고 Box 값을 하나 증가시켜줌.</p>
<p><strong>▶ 여기서 파이썬 코드는 C나 다른 언어들처럼 ++ 연산자를 사용할 수 없으며, 따라서 꼭 += 1 을 사용하여 1씩 증가시켜야 함.</strong></p>
<p>4) 마지막으로 또다른 if를 사용해 inp 값이 0보다 작아지면 -1을 출력하고 while 문을 종료시켜 줌.</p>
<p><img src="https://images.velog.io/images/te-me-city12/post/8c658139-5163-4753-be0c-5e68459464ed/%ED%95%B4%EA%B2%B0.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 n주차 WIL [12/5]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-n%EC%A3%BC%EC%B0%A8-WIL-125</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-n%EC%A3%BC%EC%B0%A8-WIL-125</guid>
            <pubDate>Mon, 06 Dec 2021 08:27:31 GMT</pubDate>
            <description><![CDATA[<p><strong>6주간의 길고도 짧았던 실전 프로젝트가 끝나고 첫 휴일을 맞았다. 많은 경험을 할 수 있었고 팀원들의 다양한 인간군상을 볼 수 있기도 했던 시간이었으며 프로젝트 발표일엔 뼈아픈 평가의 시간도 있었다. 그러나 어쨌든 끝은 왔고 소회도 풀만큼 풀었으니 우리 팀이 그 동안 해온 작업들을 정리해보고자 한다.</strong> </p>
<blockquote>
<p><strong>별 보러 가지 않을래?</strong></p>
</blockquote>
<p>▶ <a href="https://stellakorea.co.kr/">사이트 보러가기</a>
▶ <a href="https://www.youtube.com/watch?v=mB3gAzsY8s4&amp;ab_channel=%EB%B0%95%EA%B8%B0%EB%82%A8">발표영상</a></p>
<blockquote>
<p><strong>개발기간</strong></p>
</blockquote>
<ul>
<li>2021/10/23 ~ 2021/12/02</li>
</ul>
<blockquote>
<p><strong>View</strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/1abbb7f3-33c2-42f6-952e-0e0ac604d954/a.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/abbca7fb-7b6e-42ad-a5a7-87cf11ca16f0/b.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/1d439087-97b0-4e5a-a60a-35a59f75f59b/c.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/50aa8763-0187-4f9a-9eb3-ed279ca77d51/d.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/6fde07e1-842e-4b5a-b23f-02e2859eeee1/e.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/14503725-7315-4234-8587-2cf6584cee7e/f.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/9c9f56a2-f457-408f-899f-2ac7961a15de/g.png" alt=""><img src="https://images.velog.io/images/te-me-city12/post/e20fc70c-18a5-462b-bb2c-1379e46d9981/h.png" alt=""><img src="https://images.velog.io/images/te-me-city12/post/3723631b-9f8e-49f3-bb0b-8f1af907198d/i.png" alt=""></p>
<blockquote>
<p><strong>주요기능</strong></p>
</blockquote>
<ul>
<li>메인<ul>
<li>별자리 보기 좋은곳 추천</li>
</ul>
</li>
<li>별자리 페이지<ul>
<li>유저의 위치에 기반한 날씨정보 제공</li>
<li>매일 변하는 실시간 별보기 좋은 지역 정보 제공</li>
</ul>
</li>
<li>지도 페이지<ul>
<li>지도 및 캠핑장 위치 마커 표시</li>
<li>유저의 위치 주변 캠핑장 정보 제공</li>
<li>무한 스크롤을 통한 랜더링 시간 단축</li>
<li>지역별 검색기능</li>
</ul>
</li>
<li>커뮤니티 페이지<ul>
<li>추천순, 인기순, 최신순 게시글 리스트 출력</li>
<li>게시글 작성</li>
<li>게시글 제목 혹은 지역명을 이용한 검색 기능</li>
<li>좋아요, 북마크 기능 (북마크는 마이페이지에서 확인 가능)</li>
</ul>
</li>
<li>로그인/회원가입<ul>
<li>아이디 저장 기능</li>
<li>회원가입 시 닉네임, 아이디 중복확인</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>기술스택</strong></p>
</blockquote>
<ul>
<li>✏️ javascript, react</li>
<li>📝 axios, redux, immer</li>
<li>📒 kakao map API, geolocation API</li>
<li>📤 S3, Route53, CloudFront =&gt; HTTPS</li>
</ul>
<blockquote>
<p><strong>주요 라이브러리</strong></p>
</blockquote>
<ul>
<li>axios : 서버 연결</li>
<li>react-cookie : 사용자 로그인 인증용도</li>
<li>react-redux : 클라이언트 데이터 저장</li>
<li>immer : redux state 불변성 유지</li>
<li>react-helmet : 메타태그 변경</li>
<li>react-slick : 많은 데이터를 슬라이더 형식으로 보여줌.</li>
<li>react-kakao-maps-sdk : 카카오맵API, 지도 및 마커 제공</li>
<li>lodash : input 이벤트 콜백 감소</li>
<li>react-quill : 텍스트 편집기 기능 제공</li>
</ul>
<blockquote>
<p><strong>사용자 피드백 및 개선된 점</strong></p>
</blockquote>
<ul>
<li>추천 장소에 대한 정보가 부족 =&gt; 내용 추가</li>
<li>별자리 설명 클릭시 컨텐츠 고정 토글</li>
<li>관측지수 0일때 데이터가 들어오는게 맞는지 헷갈림 : 별 관측지수에 따라 문구 변경 &quot;ex) 별 보기 어려운 날 :( &quot;</li>
<li>사용자 위치를 페이지 이동할때마다 가져와서 너무 느리게 느껴짐 : 사용자 위치 정보를 리덕스에 저장하여 재활용</li>
</ul>
<blockquote>
<p><strong>개선 예정</strong></p>
</blockquote>
<ul>
<li>모바일 반응형 스타일 작업</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [11/13]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1113</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1113</guid>
            <pubDate>Sat, 13 Nov 2021 11:24:53 GMT</pubDate>
            <description><![CDATA[<p><strong>드디어 며칠 동안 마음을 졸이게 하던 중간평가를 오늘 진행하게 되었다. 팀 프로젝트 뿐만 아니라 발표까지 준비해야 하는 상황들이 꽤 스트레스를 주는 환경이긴 했지만 결국 자신이 3주간 해온 것을 보여주는 자리라는 것을 상기하는 순간 부담감이 조금은 덜어지는 것 같았다. 오늘은 나의 팀 14조가 발표했던 부분에 대해 소개하면서 리뷰하는 시간을 가져보고자 한다.</strong></p>
<blockquote>
<p><strong>MVP 소개</strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/c2eeba4c-b079-445a-9451-af01e4937f92/MVP%EC%86%8C%EA%B0%9C1.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/9afcd3b1-2b8f-4314-9373-97ea713f7dbf/MVP%EC%86%8C%EA%B0%9C2.png" alt=""></p>
<blockquote>
<p><strong>Architecture</strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/1ea5da4b-2f44-4ffe-990d-41de2e8d2b2d/Architecture%201.jpg" alt=""><img src="https://images.velog.io/images/te-me-city12/post/d1383ff2-35af-4cdb-a8ee-8f6d973c9cd4/Architecture%202.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/857de6b2-ea74-41d9-bd56-1d7d629a0fe8/Architecture%203.png" alt=""></p>
<blockquote>
<p><strong>프로젝트 구조</strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/0768d1df-269d-46f1-81b4-88339ad71a52/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%20%EA%B5%AC%EC%A1%B0.png" alt=""></p>
<blockquote>
<p><strong>어려웠던 점 or 신경쓴 점</strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/c778f0c1-c219-44bd-a834-1ad68b6e1dcb/%EC%96%B4%EB%A0%A4%EC%9B%A0%EB%8D%98%20%EC%A0%90%20or%20%EC%8B%A0%EA%B2%BD%EC%93%B4%20%EC%A0%90.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/ef2ea175-f38a-4910-ade8-4b7165802c20/2.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [10/27]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1027</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1027</guid>
            <pubDate>Mon, 08 Nov 2021 00:11:51 GMT</pubDate>
            <description><![CDATA[<p><strong>본격적으로 실전 프로젝트를 시작한 후 팀원 전체와 기획한 와이어프레임을 기반으로 어떤 뷰를 만들지 계속 생각하는 나날이 이어지고 있다. 그래도 기본적인 와이어프레임이 있어서 크게 어렵지는 않을 거라고 생각했는데 막상 그것을 구체적인 뷰로 구현하는 것은 또 다른 문제였던 것 같다. 그래서 오늘은 그런 과정을 좀 더 쉽게 하기 위해 일단 CSS를 살펴보는 시간을 가져보기로 했다.</strong></p>
<blockquote>
<p><strong>CSS 정리</strong></p>
</blockquote>
<ul>
<li><p>css standard syntax
selector { property: value; }
prpoerty    : 속성 지정
value       : 속성 값</p>
</li>
<li><p>css는 가급적 따로 style.css 와 같이 파일을 만들어 link 태그로 html 문서에 적용할 것.</p>
</li>
</ul>
<p>▶ Box Model
What is box?
☞ html 문서가 rendering될 때 box 모양으로 처리됨 =&gt; html이 css로 표현될 때 box로 표현함.
ex) .box {
    width: 500px;
    height: 300px;
    padding: 20px;
}</p>
<p>▶ Content : content가 포함되어 있는 box
            가로 길이 : width
            세로 길이 : height</p>
<p>▶ Padding : 안쪽 여백, content와 border 사이 공간
        : 방향별 여백도 지정 가능 ( ex padding-left:30px)</p>
<p>▶ Border  : 테두리 - 테두리의 굵기, 스타일, 색상 지정 필요, 필요 없을 경우 none으로 설정</p>
<p>▶ border-radius: 주어진 px 또는 %만큼 둥글어짐, 50%가 주어졌을 때 원으로 표현
                            : Padding과 같이 방향 별로 지정 가능</p>
<p>▶ Margin  : 요소와 요소 사이의 간격
        : 0 auto;   =&gt; margin을 자동 분배 =&gt; 결과적으로 가운데 위치</p>
<p>▶ Shorthand   : padding: 10px, 20px, 30px, 40px   =&gt;   순서대로 top, right, bottom, left 의미
            : margin: 20px, 40px    =&gt; top&amp;bottom : 20px, right&amp;left : 40px =&gt; left에 대한 명시가 없으면 right의 값 대입</p>
<p>▶box-sizing  : content-box;
            : default value is content-box
            : border-box -&gt; width, height 값을 border까지 포함한 값으로 계산</p>
<p>▶Display     : block - 다른 element가 침범하지 않도록 막음
            : width를 선언하지 않은 경우, width= 부모의 content-box의 100%
            : width를 선언한 경우, 남은 공간을 margin으로 자동 채움
            : height를 선언하지 않은 경우, 자식 element의 height의 합
: inline - 옆으로 나열 (vs block)
            : 공간이 모자랄 경우 줄바꿈
            : width, height, top, bottom 값 사용 불가 -&gt; 균일한 흐름을 방해하기 때문
            : img 태그의 경우 width, height를 가질 수 있음
: inline-block : inline + block</p>
<p>▶ Float       : 가로 배치
            : float가 되었을 때, 부모 요소, 형제 요소와 떨어지게 됨 -&gt; 본래 있던 곳을 빈 공간으로 인식함
            : float된 요소는, block이 됨 but, 실제 conetnts의 길이만큼 width를 가지게 되며 margin도 자동으로 생기지 않음
            : 모든 요소가 float될 경우 부모의 height값이 0이 됨
            : inline요소들은 주위에 위치한 float 요소에 영향을 받음 -&gt; float요소를 인식하고 inline 구성
            : 부모에게 overflow: hidden; =&gt; float된 요소를 인식하게 됨
            : clear : clear: left =&gt; float: left인 요소를 인식하게 됨 =&gt; clear된 요소를 이용하여 부모의 height값 자동 조정
                    : left, right, both
                    : block인 요소에게만 사용 가능
            : Pseudo Element    -&gt; css를 이용하여 virtual element 생성 (::before, ::after 요소의 가장 앞, 마지막에 가상 요소를 생성)
                                ex) .pseudo::before{ content: value }    -&gt; value : 가상 요소가 가질 내용</p>
<p>▶ Opacity     : 투명도 ( 0 ~ 1 ) 숫자가 클 수록 불투명</p>
<p>▶ Position    : 요소를 원하는 위치로 이동시키기 위해 사용
            : static    - default
            : relative  - float와 유사하나, 원래 위치를 기억, 원래 위치를 기준으로 이동
                        - ex) top : 20px -&gt; top에서부터 20px 이동
            : absolute  - display 값이 block, margin이 없어져 길막은 타요소 차단 불가
                        - 부모 요소가 인식 x
                        - inline요소 또한 인식 x ( float와 차이점 )
                        - 가장 가까운 static이 아닌 조상 요소를 기준으로 위치 이동 가능
            : fixed     - viewport 사이즈를 기준으로 이동 -&gt; 스크롤과 상관 없이 위치 고정
                : z-index: x;   -&gt; float된 정도, 값이 클수록 값이 낮은 요소를 덮음
text-align  : center; : inline 성질을 갖고 있는 객체를 가운데 정렬
Flexbox     : display: flex(or inline-flex); -&gt; flex 사용 - 정렬하고자 하는 요소를 감싸는 부모에서 선언
            : flex-direction: row; -&gt; 가로 방향 정렬(main axis: row, cross axis: column) if 세로? column(main axis: column, cross axis: row),<br>                                    row-revese, column-reverse의 경우 방향 반대(오른쪽에서 왼쪽, 밑에서 위쪽)
            : flex-wrap:    nowrap - 한줄로 정렬(감싸지 않음) -&gt; 자식의 size를 강제로 줄일 수도 있음
                            wrap   - 자식의 size를 줄이지 않고 정렬(강제 한줄 정렬 x)
            : justify-content   : flex-start;   - flex line의 시작점부터 정렬
                                : flex-endl     - flex line의 끝점부터 정렬
                                : center        - 가운데 정렬
                                : space-between - 요소들 사이 간격을 같게 정렬
                                : space-around  - 요소 좌우 별로 동일한 너비만큼 margin 제공하고 정렬
            : align-items   : (justify-conetnt attr) - cross axis 기준으로 정렬
            : align-content : 전체 요소를 기준으로 정렬
            : order         : n - n번째 순서로 정렬 (자식 요소에 포함)
            : flex-grow     : n - 0 -&gt; 키우지 않음, n &gt; 0 -&gt; 남는 영역을 채움</p>
<ul>
<li>media screen and (min-width: 768px){}  =&gt; width 768px 이상인 device에서 적용</li>
<li>vh = viewport height : viewport의 높이 기준 비율 -&gt; 1vh = 1%(viewport 기준)   &lt;-&gt; vw(viewport width)</li>
</ul>
<p>▶ Typography  - font-size      : npx   - 절대 단위
                             : nem   - 상대 단위(비례값) =&gt; 실제로 적용된 폰트 사이즈를 기준으로 1em
                             : nrem  - 상대 단위(비례값) =&gt; root(html) em - html에 적용된 font-size = 1rem
            - line-height    : 줄간격
                             : font-size와 같은 attr 적용
                             : em을 적용할 때는 생략 가능 ( EX. 1.5em = 1.5 )
                             : line-height의 중간에 글자가 위치
            - letter-spacing : font-size와 같은 attr 적용
            - font-family    : &quot;적용할 font&quot;
                             : &quot;적용할 font&quot;, sans-serif;    -&gt; font가 존재하지 않을 경우 임의의 sans-serif 적용
                             : &quot;적용할 font1&quot;, &quot;적용할 font2&quot;, sans-serif    -&gt; font1이 존재하지 않을 경우 font2, font2도 존재하지 않을 경우 임의의 sans-serif
            - text-align     : left      - 왼쪽 정렬
                             : right     - 오른쪽 정렬
                             : center    - 가운데 정렬
            - color          : hex       - 16진수 값
                             : rgb       - (r, g, b)
                             : rgba      - (r, g, b, a) =&gt; t는 투명도
            - text-indent    : npx   - npx만큼 들여쓰기
            - text-transform : alphbet based에만 영향
                             : none
                             : capitalize    - 음절의 앞자리를 대문자로
                             : uppercase     - 모든 문자를 대문자
                             : lowercase     - 모든 문자를 소문자
            - text-decoration: underline     - 밑줄 생성
                             : line-through  - 줄 가림(취소선)
                             : none
            - font-style     : normal
                             : italic        - 글자 기울게
                             : oblique</p>
<p>▶ Background  - background-color      : hex, rgb, rgba =&gt; color와 같은 attr
            - background-image      : url()     -&gt; img 경로( 로컬 상대 경로 or 네트워크 이미지 주소 )
            - background-repeat     : repeat    -&gt; 반복(default)
                                    : no-repeat -&gt; 반족 x
            - background-size       : contain   - background를 넘어가지 않도록
                                    : cover     - background를 가득 채우도록 ( 비율 유지 -&gt; 이미지가 잘릴 수 있음 )
                                    : custom    - width, height를 조정 가능 ( 100% auto ...)
            - background-position   : x y 값 조정 -&gt; center center, center bottom, center 30px...</p>
<ul>
<li>cusor       : pointer   -&gt; 커서를 해당 요소 위에 올리면 선택 가능 커서로 변경</li>
</ul>
<p>▶ Transition  : property          - 지정된 요소를 자연스럽게 변화 (ex. font-size) -&gt; all =&gt; 모든 요소에 영향
            : duration          = ms
                                = s     - 변화가 얼마동안 일어날지
            : timing-function   = ease-in           - 천천히 바뀌다 빠르게
                                = ease-out          - 빠르게 바뀌다 천천히
                                = ease-in-out       - 천천히 -&gt; 빠르게 -&gt; 천천히
                                = cubic-bezier()    - 속도 변화폭을 customizing (cubie-bezier 웹 테스트 가능)
            : delay             = 값으로 들어간 ms 이후 변화 시작
            =&gt; property와 duration은 필수 parameter
            =&gt; 각각의 property 마다 따로 transition을 적용도 가능</p>
<p>▶ Animation   : 애니메이션 부여
            : @keyframes name{ from{ Rules } to{ Rules } }  -&gt; 시작할 때는 from의 Rules, 끝날 때는 to의 Rules           =&gt; name 애니메이션 정의
                                                            -&gt; 0 ~ 100%로 범주로 Rules를 지정해도 됨( like Swtich )
            : animation-name            : name 으로 name 애니메이션 적용
            : animation-duration        = Transition의 duration과 동일
            : animation-timing-function = Transition의 animation-timing-function과 동일
            : animation-delay           = Transition의 delay와 동일
            : animation-iteration-count : 값만큼 반복, infinite - 무한 반복
            : animation-direction       : reverse - to-&gt;from    - 애니메이션의 진행 방향 설정
                                        : alternate             - 방향을 번갈아가면서</p>
<p>▶ box-shadow  : h-offset v-offset - 그림자의 방향 ( +이면 오른쪽 또는 아래쪽, -이면 왼쪽 도는 위쪽)
              blur              - 흐린 정도 (커질 수록 흐려짐)
              spread            - 그림자 크기
              color             - 그림자 색상</p>
<p>▶ opacity     : 0(투명) ~ 1(불투명) 사이 값</p>
<p>▶ overflow    : width, height값을 자식 요소 또는 컨텐츠가 넘어섰을 때 사용
            : visible       - default값 -&gt; 넘치더라도 보여줌
            : auto,scroll   - 스크롤 표시
            : hidden        - 숨기기
            - overflow-x, overflow-y로 가로 세로 각 방향에 대한 옵션도 설정 가능</p>
<p>▶ transform   : 기존 요소의 크기와 위치를 기억해 다른 요소에게 영향 x
            : translate(x, y)   - x, y 방향으로 위치 - translateX, translateY로 x 또는 y 방향으로만 이동 가능
                                - % 단위를 사용할 경우 transform이 사용된 해당 요소의 width, height를 기준으로 움직임
            : scale(N)          - N배만큼 크기 조절
                                - scale(x, y)를 이용하여 가로는 x배, 세로는 y배 조절 가능
            : rotate(Ndeg)      - N도만큼 회전  (deg는 각도를 의미하는 단위)</p>
<p>▶ visiblity   : visible   - 보이기
            : hidden    - 숨기기    -&gt; 요소는 존재 (opacity:0과 흡사)   vs display: none    -&gt; 요소가 존재하지 않는 것으로 취급</p>
<p>▶ Selector    - ID        - #id로 선택 (EX_ in html, id = name in CSS, #name{})
            - &#39;&gt;&#39;을 이용하여 child 선택         EX_ section &gt; h1    -&gt; section의 child 중 h1 
            - &#39; &#39;을 이용하여 descendants 선택   EX_ section h1      -&gt; section의 자손 중 h1
            - &#39;+&#39; 해당 선택자 바로 이후에 오는 형제 요소 하나 선택  EX_ .active + li    -&gt; active 이후에 위치한 li 하나
            - &#39;~&#39; 해당 선택자 이후에 오는 형제 요소들 선택          EX_ .active ~ li    -&gt; active 이후에 위치한 li
            - :first-child  - 첫번째 요소에만 적용      EX_ li:first-child  - 리스트의 첫번째 요소에 적용
            - :last-child   - 마지막 요소에만 적용      EX_ li:last-child   - 리스트의 마지막 요소에 적용
            - :nth-child(N) - N번째 요소에만 적용       EX_ li:nth-child(3) - 리스트의 3번째 요소에 적용    -&gt; 짝수일 경우 N=2n, 홀수일 경우 N=2n-1도 적용 가능
            - :hover        - 마우스 커서가 해당 요소 위에 위치할 때 적용
            - :active       - 해당 요소를 클릭하였을 때 적용
            - :focus        - 요소가 선택(focus)되었을 때 (EX_ 입력창 등) 적용
            - 나중에 선언된 스타일이 덮어씌움
            - 선택자 우선 순위{
                type 선택자가 우선 순위 마지막(EX_ span, div, a 등) 
                class, 가상 class 선택자가 2순위
                ID 선택자가 1순위
            }
            - 선택자 우선 순위 무시{
                inline style    : html 내부에 style을 작성
                !important      : 최고 우선 순위
                    EX_   p{
                            color: red !important;
                          }
            } =&gt; 가급적 지양</p>
<p>▶ Grid System : container     - grid system이 적용되는 전체 범위
            : column        - container를 분할하는 칸 (일반적으로 12칸을 주로 이용)
            : gutter        - column간 여백 -&gt; 간격을 위한 여백</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [10/28]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1028</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1028</guid>
            <pubDate>Sun, 07 Nov 2021 17:56:48 GMT</pubDate>
            <description><![CDATA[<p><strong>뷰를 만드는 데에 생각보다 시간이 걸리고 있다. 분명 지금까지 공부해왔던 부분인데 실전에서 보다 나은 완성도를 내려고 하니 쉽지만은 않다. 하지만 실전프로젝트를 시작하고 계획했던 뷰(지도 상세 페이지)는 어느 정도 그 모습을 드러내고 있기에 1차 뷰로서 작업을 마무리했다. 지도 상세는 캠핑장 리스트를 클릭했을 때 보이는 페이지이기에 가장 기본적인 캠핑장을 나타내는 지도 및 그 캠핑장을 소개하는 레이아웃으로 구성했다.</strong></p>
<blockquote>
<p><strong>지도 상세 페이지 1차 뷰</strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/a0472b3e-6780-46ae-91cf-c9c7b93ebc62/main-%EC%A7%80%EB%8F%84-%EC%83%81%EC%84%B8%ED%8E%98%EC%9D%B4%EC%A7%80.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [10/29]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1029</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1029</guid>
            <pubDate>Sun, 07 Nov 2021 17:19:32 GMT</pubDate>
            <description><![CDATA[<p><strong>프로젝트를 시작한 뒤 첫 주말을 향해 시간이 흐르고 있다. 항상 드는 생각이지만 금요일 오후로 시간이 흐를수록 왜 마음은 이다지도 안정감이 드는 것일까. 어쨌거나 오늘 나는 예정대로 커뮤니티 상세 페이지의 1차 뷰 작업을 완료할 수 있었다. 일단은 가장 기본적인 지도 및 커뮤니티 페이지가 주가 된 간단한 형태로 제작해 보았다.</strong></p>
<blockquote>
<p><strong>커뮤니티 상세 페이지 1차 뷰</strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/7eb4454c-7bad-42ac-a4a0-edbed39c4aba/main-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0-%EA%B8%80%20%EC%83%81%EC%84%B8%ED%8E%98%EC%9D%B4%EC%A7%80.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [10/30]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1030</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-1030</guid>
            <pubDate>Sun, 07 Nov 2021 16:54:25 GMT</pubDate>
            <description><![CDATA[<p><strong>실전 프로젝트가 시작되고 첫 토요일을 맞았다. 한 주를 무사히 보냈다는 안도감과 더불어 다음 주를 잘 보낼 수 있을지에 대한 불안감이 동시에 찾아오지만 그래도 내가 할 수 있는 하는 것이 지금은 최선이라고 생각한다. 오늘은 일주일 동안 부족하다고 느꼈던 HTML에 대해 좀 더 공부해 보는 시간을 가져보기로 했다.</strong></p>
<blockquote>
<p><strong>HTML 태그정리</strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/bb67b215-38e7-4327-8e05-8039be7c52b3/1.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/6d047d65-1534-40f3-abfb-9530f787cd3e/2.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/235c3cc8-ef3a-4557-9a36-afbc1da2dbf8/3.png" alt=""></p>
<p><img src="https://images.velog.io/images/te-me-city12/post/d246ace8-6ebe-4c8f-84ed-254ad4c8facf/4.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [11/1]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-111</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-111</guid>
            <pubDate>Sun, 07 Nov 2021 05:55:11 GMT</pubDate>
            <description><![CDATA[<p><strong>11월이 시작되었다. 그리고 내가 참가하고 있는 실전 프로젝트의 2주차도 시작되었다. 6주라는 시간이 처음엔 다소 아득하게 느껴지기도 했지만 모든 것은 과거가 되어가는 중이기에, 이번 주도 익숙한 듯 새롭게 시작하기로 했다. 오늘 나는 프로젝트용 사이트에 사용될 텍스트 에디터 라이브러리를 검색하고 일부 라이브러리를 테스트해보는 시간을 가졌다.</strong></p>
<blockquote>
<p><strong>Top 5 Rich Text Editors for React in 2021</strong></p>
</blockquote>
<p>▶ <a href="https://blog.bitsrc.io/top-5-rich-text-editors-for-react-in-2021-628fecf0f7e0">https://blog.bitsrc.io/top-5-rich-text-editors-for-react-in-2021-628fecf0f7e0</a></p>
<blockquote>
<p><strong>Draft.js 텍스트 에디터 라이브러리 테스트</strong></p>
</blockquote>
<p>▶ Draft.js는 페이스북에서 공개한 것으로 react에서 텍스트에디터를 만들 수 있는 툴이다. “WYSIWYG(what you see is what you get)” 이라고 작성할때 보이는게, 결과물이랑 같은 유저 인터페이스를 말함.</p>
<p>▶ import</p>
<ul>
<li>draft-js 를 추가로 설치하는 이유는 DOM 에 해당 텍스트를 넣어줘야 하기 때문.<pre><code>import { Editor } from &quot;react-draft-wysiwyg&quot;;
import { EditorState, convertToRaw } from &quot;draft-js&quot;;</code></pre></li>
</ul>
<p>▶ state 선언</p>
<ul>
<li>text를 담을 state의 초기값은 EditorState.createEmpty() 로 해줘야 Immutable 객체로 나옴.</li>
</ul>
<p>→ RichTextEditor 코드(JS)</p>
<pre><code>import React from &quot;react&quot;;
import { Editor, EditorState, getDefaultKeyBinding, RichUtils} from &quot;draft-js&quot;;
import &#39;./RichText.css&#39;;
import &#39;../../node_modules/draft-js/dist/Draft.css&#39;;
​
​
class RichTextEditor extends React.Component {
    constructor(props) {
      super(props);
      this.state = {editorState: EditorState.createEmpty()};
​
      this.focus = () =&gt; this.refs.editor.focus();
      this.onChange = (editorState) =&gt; this.setState({editorState});
​
      this.handleKeyCommand = this._handleKeyCommand.bind(this);
      this.mapKeyToEditorCommand = this._mapKeyToEditorCommand.bind(this);
      this.toggleBlockType = this._toggleBlockType.bind(this);
      this.toggleInlineStyle = this._toggleInlineStyle.bind(this);
    }
​
    _handleKeyCommand(command, editorState) {
      const newState = RichUtils.handleKeyCommand(editorState, command);
      if (newState) {
        this.onChange(newState);
        return true;
      }
      return false;
    }
​
    _mapKeyToEditorCommand(e) {
      if (e.keyCode === 9 /* TAB */) {
        const newEditorState = RichUtils.onTab(
          e,
          this.state.editorState,
          4, /* maxDepth */
        );
        if (newEditorState !== this.state.editorState) {
          this.onChange(newEditorState);
        }
        return;
      }
      return getDefaultKeyBinding(e);
    }
​
    _toggleBlockType(blockType) {
      this.onChange(
        RichUtils.toggleBlockType(
          this.state.editorState,
          blockType
        )
      );
    }
​
    _toggleInlineStyle(inlineStyle) {
      this.onChange(
        RichUtils.toggleInlineStyle(
          this.state.editorState,
          inlineStyle
        )
      );
    }
​
    render() {
      const {editorState} = this.state;
​
      // If the user changes block type before entering any text, we can
      // either style the placeholder or hide it. Let&#39;s just hide it now.
      let className = &#39;RichEditor-editor&#39;;
      var contentState = editorState.getCurrentContent();
      if (!contentState.hasText()) {
        if (contentState.getBlockMap().first().getType() !== &#39;unstyled&#39;) {
          className += &#39; RichEditor-hidePlaceholder&#39;;
        }
      }
​
      return (
        &lt;div className=&quot;RichEditor-root&quot;&gt;
          &lt;BlockStyleControls
            editorState={editorState}
            onToggle={this.toggleBlockType}
          /&gt;
          &lt;InlineStyleControls
            editorState={editorState}
            onToggle={this.toggleInlineStyle}
          /&gt;
          &lt;div className={className} onClick={this.focus}&gt;
            &lt;Editor
              blockStyleFn={getBlockStyle}
              customStyleMap={styleMap}
              editorState={editorState}
              handleKeyCommand={this.handleKeyCommand}
              keyBindingFn={this.mapKeyToEditorCommand}
              onChange={this.onChange}
              placeholder=&quot;Tell a story...&quot;
              ref=&quot;editor&quot;
              spellCheck={true}
            /&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      );
    }
  }
​
  // Custom overrides for &quot;code&quot; style.
  const styleMap = {
    CODE: {
      backgroundColor: &#39;rgba(0, 0, 0, 0.05)&#39;,
      fontFamily: &#39;&quot;Inconsolata&quot;, &quot;Menlo&quot;, &quot;Consolas&quot;, monospace&#39;,
      fontSize: 16,
      padding: 2,
    },
  };
​
  function getBlockStyle(block) {
    switch (block.getType()) {
      case &#39;blockquote&#39;: return &#39;RichEditor-blockquote&#39;;
      default: return null;
    }
  }
​
  class StyleButton extends React.Component {
    constructor() {
      super();
      this.onToggle = (e) =&gt; {
        e.preventDefault();
        this.props.onToggle(this.props.style);
      };
    }
​
    render() {
      let className = &#39;RichEditor-styleButton&#39;;
      if (this.props.active) {
        className += &#39; RichEditor-activeButton&#39;;
      }
​
      return (
        &lt;span className={className} onMouseDown={this.onToggle}&gt;
          {this.props.label}
        &lt;/span&gt;
      );
    }
  }
​
  const BLOCK_TYPES = [
    {label: &#39;H1&#39;, style: &#39;header-one&#39;},
    {label: &#39;H2&#39;, style: &#39;header-two&#39;},
    {label: &#39;H3&#39;, style: &#39;header-three&#39;},
    {label: &#39;H4&#39;, style: &#39;header-four&#39;},
    {label: &#39;H5&#39;, style: &#39;header-five&#39;},
    {label: &#39;H6&#39;, style: &#39;header-six&#39;},
    {label: &#39;Blockquote&#39;, style: &#39;blockquote&#39;},
    {label: &#39;UL&#39;, style: &#39;unordered-list-item&#39;},
    {label: &#39;OL&#39;, style: &#39;ordered-list-item&#39;},
    {label: &#39;Code Block&#39;, style: &#39;code-block&#39;},
  ];
​
  const BlockStyleControls = (props) =&gt; {
    const {editorState} = props;
    const selection = editorState.getSelection();
    const blockType = editorState
      .getCurrentContent()
      .getBlockForKey(selection.getStartKey())
      .getType();
​
    return (
      &lt;div className=&quot;RichEditor-controls&quot;&gt;
        {BLOCK_TYPES.map((type) =&gt;
          &lt;StyleButton
            key={type.label}
            active={type.style === blockType}
            label={type.label}
            onToggle={props.onToggle}
            style={type.style}
          /&gt;
        )}
      &lt;/div&gt;
    );
  };
​
  var INLINE_STYLES = [
    {label: &#39;Bold&#39;, style: &#39;BOLD&#39;},
    {label: &#39;Italic&#39;, style: &#39;ITALIC&#39;},
    {label: &#39;Underline&#39;, style: &#39;UNDERLINE&#39;},
    {label: &#39;Monospace&#39;, style: &#39;CODE&#39;},
  ];
​
  const InlineStyleControls = (props) =&gt; {
    const currentStyle = props.editorState.getCurrentInlineStyle();

    return (
      &lt;div className=&quot;RichEditor-controls&quot;&gt;
        {INLINE_STYLES.map((type) =&gt;
          &lt;StyleButton
            key={type.label}
            active={currentStyle.has(type.style)}
            label={type.label}
            onToggle={props.onToggle}
            style={type.style}
          /&gt;
        )}
      &lt;/div&gt;
    );
  };
​
  export default RichTextEditor;</code></pre><p>→ RichTextEditor 코드(CSS)</p>
<pre><code>/**
 * Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
 *
 * This file provided by Facebook is for non-commercial testing and evaluation
 * purposes only. Facebook reserves all rights not expressly granted.
 *
 * THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 .RichEditor-root {
    background: #fff;
    border: 1px solid #ddd;
    font-family: &#39;Georgia&#39;, serif;
    font-size: 14px;
    padding: 15px;
  }

  .RichEditor-editor {
    border-top: 1px solid #ddd;
    cursor: text;
    font-size: 16px;
    margin-top: 10px;
  }

  .RichEditor-editor .public-DraftEditorPlaceholder-root,
  .RichEditor-editor .public-DraftEditor-content {
    margin: 0 -15px -15px;
    padding: 15px;
  }

  .RichEditor-editor .public-DraftEditor-content {
    min-height: 100px;
  }


  .RichEditor-hidePlaceholder .public-DraftEditorPlaceholder-root {
    display: none;
  }

  .RichEditor-editor .RichEditor-blockquote {
    border-left: 5px solid #eee;
    color: #666;
    font-family: &#39;Hoefler Text&#39;, &#39;Georgia&#39;, serif;
    font-style: italic;
    margin: 16px 0;
    padding: 10px 20px;
  }

  .RichEditor-editor .public-DraftStyleDefault-pre {
    background-color: rgba(0, 0, 0, 0.05);
    font-family: &#39;Inconsolata&#39;, &#39;Menlo&#39;, &#39;Consolas&#39;, monospace;
    font-size: 16px;
    padding: 20px;
  }

  .RichEditor-controls {
    font-family: &#39;Helvetica&#39;, sans-serif;
    font-size: 14px;
    margin-bottom: 5px;
    user-select: none;
  }

  .RichEditor-styleButton {
    color: #999;
    cursor: pointer;
    margin-right: 16px;
    padding: 2px 0;
    display: inline-block;
  }

  .RichEditor-activeButton {
    color: #5890ff;
  }</code></pre><p>▶ 테스트 화면 스크린샷
<img src="https://images.velog.io/images/te-me-city12/post/e2fd3431-2137-4b7c-bd3f-093cdc677df9/draft.js%20%ED%85%8C%EC%8A%A4%ED%8A%B8%20%ED%99%94%EB%A9%B4%20%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [11/2]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-112</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-112</guid>
            <pubDate>Sun, 07 Nov 2021 05:14:44 GMT</pubDate>
            <description><![CDATA[<p><strong>폭풍같던 월요일이 지나고 화요일이 되었다. 급할수록 돌아가라는 말이 있듯이 조금은 숨을 돌리고 앞으로는 무엇을 할지 생각해보았다. 그리고 나서, 앞으로 구현할 뷰를 생각하며 어떤 식으로 만들지 계획하고 관련된 기능을 생각하는 시간을 가져봄과 동시에 필요한 작업들을 위해 공부를 하는 시간을 가져보기로 했다.</strong></p>
<blockquote>
<p><strong>따라하며 배우는 노드, 리액트 시리즈 - 기본강의 (John Ahn)
 ▶ React 부분만 정리</strong></p>
</blockquote>
<ul>
<li><p>React는 프레임워크가 아니라 라이브러리이며, 2013년도 페이스북에서 만들어짐.</p>
</li>
<li><p>컴포넌트로 구성되며 컴포넌트는 모듈과 비슷하게 재사용성이 뛰어남.</p>
</li>
</ul>
<p>▶ VirtualDOM</p>
<ul>
<li>RealDom
리스트들이 10개 정도가 있고 어떤 1개만 변화가 일어나면 RealDom에서는 다시 10개의 리스트를 Reload를 해야함.</li>
</ul>
<ul>
<li>VirtualDom
하나만 변경이 일어나면 하나만 Reload 하면 되서 더욱 부하가 적음.
왜 하나만 가능한가 ▶ VirtualDom은 RealDom의 카피본으로 VirtualDom에서 스냅샷을 찍은 후, 이후 변경이 있으면 스냅샷 찍어둔거랑 변경 후 차이를 분석하여 바뀐 부분만 realDom에서 바꿔줌.</li>
</ul>
<p>▶ Babel
자바스크립트를 이용 할 때, es6, es7등 더 많은 메소드들을 추가함. 이런 새로운 메소드들은 오래된 브라우저에서 작동이 안될때가 있어, 최신 자바스크립트 문법을 사용해도 구형 브라우저에서 작동할 수 있도록 es6 자바스크립트 문법으로 변환시켜줌.</p>
<p>▶ Webpack
예전에 웹페이지를 만들때는 Javascript, css, html등 간단하게 만들면 됨. 하지만 점점 웹사이트들이 커지면서 라이브러리, 프레임워크 등을 많이 쓰다 보니 복잡해졌고 복잡하게 된 것을 웹펙을 이용하여 번들을 해줌.</p>
<p><img src="https://images.velog.io/images/te-me-city12/post/1f1e34f1-ef05-42b4-8cbe-e032fd99b1af/%EC%9B%B9%ED%8C%A9.png" alt=""></p>
<p>▶ NPM vs NPX</p>
<ul>
<li>이제는 npx를 이용하여 다운함.</li>
</ul>
<p>▶ NPM</p>
<ul>
<li>Node Package Manager</li>
<li>registry(저장소) 역할을 하고 라이브러리들을 저장함.</li>
<li>g 옵션을 써서 라이브러리를 설치하면 /usr/local/bin(linux), /AppData/npm(window)에 저장됨.</li>
<li>어플리케이션을 킬 때 npm run start, npm run build 등 을 함.</li>
<li>npm에 관한 정보는 package.json에 저장됨.</li>
</ul>
<p>▶ NPX</p>
<ul>
<li>npx가 npm registry에서 최신 버전에 해당하는 패키지를 설치하여 실행하고, 실행된 이후 해당 패키지를 제거함.</li>
<li>disk space를 낭비 하지 않고, 항상 최신 버전을 사용할 수 있음.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[항해99 TIL [11/3]]]></title>
            <link>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-113</link>
            <guid>https://velog.io/@te-me-city12/%ED%95%AD%ED%95%B499-TIL-113</guid>
            <pubDate>Sun, 07 Nov 2021 04:06:28 GMT</pubDate>
            <description><![CDATA[<p><strong>실전 프로젝트 2주차의 수요일. 이제 슬슬 적응이 되어가는 듯 싶다가도 갑자기 고개를 드는 향후에 대한 불안과 내가 지금 잘하고 있는 것인지에 대한 두려움이다.다. 용기를 낸다는 것은 자신과의 끊임없는 싸움에 대한 증거인 것 같다. 오늘의 나는 디자이너분들이 확정된 뷰를 만들어주시기 전까지 임의로 사용할 수 있는 지도 상세페이지 및 커뮤니티 상세페이지에 관련된 뷰를 HTML 및 CSS를 통해 구체적으로 만들어보았다.</strong></p>
<blockquote>
<p><strong>지도 상세페이지</strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/81af96cc-f1dc-4521-97ec-723423fc157f/%EC%A7%80%EB%8F%84%EC%83%81%EC%84%B8.jpg" alt=""></p>
<blockquote>
<p><strong>커뮤니티 상세페이지</strong></p>
</blockquote>
<p><img src="https://images.velog.io/images/te-me-city12/post/81765cbf-8659-4682-8193-3d5881540609/%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0%EC%83%81%EC%84%B8.jpg" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>