<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발 정리함</title>
    <link>https://simju9397.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 3 Aug 2026 23:13:21 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>심정욱</managingEditor>
    <item>
      <title>ios mobile safari 에서 SVG 가 보이지 않는 경우</title>
      <link>https://simju9397.tistory.com/47</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개발을 하다 겪은 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;version = 1.1 을 태그에 붙여보라는 말도 있었지만 안 통했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결방법은 SVG 를 div 태그 안에 감싸서 넣었더니 해결했다.&lt;/p&gt;</description>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/47</guid>
      <comments>https://simju9397.tistory.com/47#entry47comment</comments>
      <pubDate>Thu, 21 Jul 2022 22:50:38 +0900</pubDate>
    </item>
    <item>
      <title>2021 Dev-Matching 웹 프론트엔드 개발자(하반기) : 쇼핑몰 SPA 구현 후기 (pushState, popState)</title>
      <link>https://simju9397.tistory.com/46</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/skill_check_assignments/199&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/skill_check_assignments/199&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1646317165103&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/skill_check_assignments/199&quot; data-og-url=&quot;https://programmers.co.kr/users/login&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/IPoiG/hyNAOkd5li/0FyYiUpgsvXjocWjkK1Ds0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bIZ9PS/hyNA2v0H1Q/3Ik8N2mIAm0UQSwQ0QQIok/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/skill_check_assignments/199&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/skill_check_assignments/199&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/IPoiG/hyNAOkd5li/0FyYiUpgsvXjocWjkK1Ds0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bIZ9PS/hyNA2v0H1Q/3Ik8N2mIAm0UQSwQ0QQIok/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 바닐라 JS 개발을 해보고 싶었는데, 마침 SPA 개발 과제가 있길래 해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 과정에서 긴 시간 고전한 주제에 대해 적고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로, 난 완벽한 구현에 성공하지 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째로 SPA 라우팅 처리를 vanilla js 로 해본 게 처음이라 고전했고 (쉬운 건데 삽질했다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째로 js 에서 HTML element 를 만들 때 createElement 를 하나하나 일일히 했다. (미쳤나봄)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해답을 보니 innerHTML = `&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;` 식으로 편하게 했더라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 를 쓰는 실무에서 워낙 할 일 없던 거라 생각을 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SPA 라우팅 처리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SPA는 별도의 서버 사이드 렌더링 없이, 경로 별로 innerHTML 을 변경해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 라우팅 처리가 굉장히 중요한 부분이라고 생각했고, history.pushState 와 popState 가 해결책이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 과정에서 한시간 가량 삽질한 부분에 대해 말해보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index.html&lt;/p&gt;
&lt;pre id=&quot;code_1646317325632&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;커피캣 스토어&amp;lt;/title&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;styles.css&quot;&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;main class=&quot;App&quot;&amp;gt;
    &amp;lt;/main&amp;gt;
    &amp;lt;script src=&quot;src/index.js&quot; type=&quot;module&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src 폴더의 index.js 를 body 태그 밑에 불러온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src/index.js&lt;/p&gt;
&lt;pre id=&quot;code_1646317394541&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import App from &quot;./App.js&quot;

const start = () =&amp;gt; {
    return App();
}

start();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;create-react-app 의 구조가 익숙해서 이런 식으로 했는데, 의미가 없었던 거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src/App.js&lt;/p&gt;
&lt;pre id=&quot;code_1646317575146&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import ProductDetail from &quot;./pages/ProductDetail.js&quot;;
import ProductList from &quot;./pages/ProductList.js&quot;;
import { renderAppContent } from &quot;./utils.js&quot;;

const App = async () =&amp;gt; {
    const app = document.getElementsByClassName(&quot;App&quot;)[0];

	// 맨 첫 페이지는 list 이므로, App 에 ProductList를 렌더한다.
	renderAppContent(await ProductList()); // ProductList 함수를 실행시킨 결과값(HTML element)를 app 에 렌더한다
    history.pushState({page : &quot;list&quot;}, '', `.`);  // 렌더하면서, list 로 갔다는 표시로 pushState 를 해준다.

	
    const onPageChange = async () =&amp;gt; {
        switch(history.state.page) {
            case &quot;list&quot;:
                renderAppContent(await ProductList());
                break;
            case &quot;detail&quot;:
                renderAppContent(await ProductDetail({id : history.state.id}));
                break;
            default:
                return;
        }
    }

    window.addEventListener('popstate', onPageChange)
    return app;
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pushState 는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;history.pushState( data, title, path ) 의 형태로 이루어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 실제로 앱 전체를 다시 불러오는 형태가 아닌, url 의 path 만 변경시키면서 data 를 history 스택에 push 하는 방식으로 진행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pushState 를 하게 되면 뒤로 가기 버튼이 활성화가 된다. (실제로 하나 이동했다는 말. history.go(1) 과 같은 느낌이다 )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 삽질한 부분은, 첫 화면에 들어왔을 때, history.pushState 를 해주지 않고, 리스트의 항목을 클릭했을 때만 pushState 를 해준 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;첫 화면(리스트)이 렌더 됐음에도 pushState 를 해주지 않았기 때문에 null 이 저장됨&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;리스트의 항목 중 하나를 클릭해서, pushState 를 통해 detail 페이지로 이동함 ( 잘 됐음 )&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;뒤로 가기 하면 popstate 이벤트가 발생하는데, 이 때 state 가 null 이었음.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;근데 앞으로 가기를 다시 누르니까 디테일 페이지로 이동하면서, 디테일 페이지에 전해주려 했던 state 들이 잘 찍혔음 (이것 때문에 헷갈렸음)&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 바보같이 pushState 를 처음에 해줘야 하는 지 몰랐다. 그래서 계속 null 이 나타나서 한시간 가량 날렸다. ㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 한줄을 추가하자 거짓말 처럼 잘 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;두 번째 실수 : innerHTML = &amp;lt;div&amp;gt; 를 쓰지 않음&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 ProductDetail 페이지 코드를 보면 ( 다 안 읽는 걸 추천한다 )&lt;/p&gt;
&lt;pre id=&quot;code_1646318154391&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { makeElement } from &quot;../utils.js&quot;

const ProductDetail = async ({id, name, imageUrl, price}) =&amp;gt; {
    const productDetailPage = makeElement(&quot;div&quot;, &quot;ProductDetailPage&quot;);

    const productDetailPageTitle = makeElement(&quot;h1&quot;);
    productDetailPageTitle.innerText = `${name} 상품 정보`;
    productDetailPage.appendChild(productDetailPageTitle);

    const productDetail = makeElement(&quot;div&quot;, &quot;ProductDetail&quot;);

    const produdctImage = makeElement(&quot;img&quot;, &quot;&quot;, {
        src : imageUrl
    })

    productDetail.appendChild(produdctImage);

    const productDetailInfo = makeElement(&quot;div&quot;, &quot;ProductDetail__info&quot;);

    const productDetailInfoTitle = makeElement(&quot;h2&quot;);
    productDetailInfoTitle.innerText = `${name}`;
    productDetailInfo.appendChild(productDetailInfoTitle)

    const productDetailPrice = makeElement(&quot;div&quot;, &quot;ProductDetail__price&quot;);
    productDetailPrice.innerText = `${price}원~`;
    productDetailInfo.appendChild(productDetailPrice)

    const productSelectElement = makeElement(&quot;select&quot;);
    const defaultOption = makeElement(&quot;option&quot;);
    defaultOption.innerText = `선택하세요.`;
    productSelectElement.appendChild(defaultOption);

    productDetailInfo.appendChild(productSelectElement)
    
    const selectedOptions = makeElement(&quot;div&quot;, &quot;ProductDetail__selectedOptions&quot;);
    const selectedOptionsTitle = makeElement(&quot;h3&quot;);
    selectedOptionsTitle.innerText = `선택된 상품`;

    const selectedOptionsUl = makeElement(&quot;ul&quot;);
    selectedOptions.appendChild(selectedOptionsUl);

    productDetailInfo.appendChild(selectedOptions);

    productSelectElement.onchange = () =&amp;gt; {
        const selected = productSelectElement.options[productSelectElement.selectedIndex].value
        console.log(selected)
        productSelectElement.value = productSelectElement.options[0].value;

        const selectedOption = makeElement(&quot;li&quot;);
    }

    try {
        const {productOptions} = await (await fetch(`https://uikt6pohhh.execute-api.ap-northeast-2.amazonaws.com/dev/products/${id}`)).json();
        productOptions.forEach((productOption) =&amp;gt; {
            const option = makeElement(&quot;option&quot;);
            option.innerText = `${name} ${productOption.name}`;
            if(productOption.price &amp;gt;= 0) {
                option.innerText += `(+${productOption.price}원)`;
            }
            if(productOption.stock === 0) {
                option.innerText = `(품절) ${option.innerText}`;
            }
            productSelectElement.appendChild(option);
        })
        
    } catch(e) {

    }
    
    

    productDetail.appendChild(productDetailInfo);
    productDetailPage.appendChild(productDetail)

    return productDetailPage;
}

export default ProductDetail;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 힘들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 productDetailPage.innerHTML = `blahblah` 를 생각하지 못했을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해답을 보고 힘이 다 빠져버렸다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데브 매칭은 바닐라 JS 로 항상 이루어졌고, 앞으로도 그럴 것 같다. (프레임워크 별로 차이가 있는데, 리액트만 받을 순 없자나.. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배울 게 아직도 많다는 걸 여실히 깨달은 하루였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과제에서의 남은 점은, UI 조작의 자잘한 부분 ( 가격에 콤마찍기, 선택 물품 가격 계산하기) 와 주문하기 ( 로컬 스토리지 사용하기 ) 인 것 같은데, 이것들은 쉬우니까 더 안봐도 될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SPA 구현을 해보는 데 굉장히 좋은 기회가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고양이 사진첩 만들기 (상반기 문제) 도 풀어 봐야지.&lt;/p&gt;</description>
      <category>Front-End/HTML,CSS</category>
      <category>dev matching</category>
      <category>데브매칭후기</category>
      <category>쇼핑몰SPA 해답</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/46</guid>
      <comments>https://simju9397.tistory.com/46#entry46comment</comments>
      <pubDate>Thu, 3 Mar 2022 23:39:51 +0900</pubDate>
    </item>
    <item>
      <title>동기, 비동기, 이벤트 루프, 매크로태스크, 마이크로태스크 이해하기 (1) : 동기와 비동기란</title>
      <link>https://simju9397.tistory.com/45</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;오랜만입니다!&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;정~~~말 오랜만에 글을 쓰네요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이사도 하고, 일도 바쁘고 하다 보니 이제야 글을 다시 쓰게 되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Javascript 개발자의 영원한 친구(이자 숙적), 비동기&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dlXk0k/btrnBzAnsdm/n5zU7ZXtG8fimdieR6aYK1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dlXk0k/btrnBzAnsdm/n5zU7ZXtG8fimdieR6aYK1/img.jpg&quot; data-alt=&quot;Javascript 개발을 할 때마다 느끼는 감정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dlXk0k/btrnBzAnsdm/n5zU7ZXtG8fimdieR6aYK1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdlXk0k%2FbtrnBzAnsdm%2Fn5zU7ZXtG8fimdieR6aYK1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;690&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;690&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Javascript 개발을 할 때마다 느끼는 감정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 어느덧 회사에서 react 개발 5개월 차가 되었고, 내가 겪은 대부분의 문제들은 비동기적인 실행으로 인한 이슈들이었다. 그만큼 정말 흔한 이슈이고, 그렇기에 자바스크립트에서 코드가 실행되는 순서를 잘 파악할 수 있는 사람이 실력있는 사람이라고 생각한다. 가보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;동기 vs 비동기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjfpYC/btrnJAK5tfo/Y9mfZdZxWU98BH3R9TKE8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjfpYC/btrnJAK5tfo/Y9mfZdZxWU98BH3R9TKE8K/img.png&quot; data-alt=&quot;출처 :&amp;amp;amp;nbsp;https://velog.io/@daybreak/%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjfpYC/btrnJAK5tfo/Y9mfZdZxWU98BH3R9TKE8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjfpYC%2FbtrnJAK5tfo%2FY9mfZdZxWU98BH3R9TKE8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;810&quot; height=&quot;728&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;728&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;amp;nbsp;https://velog.io/@daybreak/%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기와 비동기는 딱 한 문장으로 정의할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;결과가 나올 때 까지 기다려야 하면 동기, 아니면 비동기&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림을 예로 들면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기다리는 사람들은 앞 사람이 주문 + &lt;b&gt;커피 수령하는 시간&lt;/b&gt;까지 모두 기다려야 한다 =&amp;gt; 동기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기다리는 사람들은 앞 사람이 주문하는 시간만 기다리면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, 자유롭게 유튜브를 보다가, 진동벨이 울리면 커피를 수령하러 가면 된다. =&amp;gt; 비동기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이다. 너무 쉽다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;동기 비동기는 이해했는데요, 자바스크립트는 왜 비동기인가요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카페와 웹사이트의 공통점은,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 사용자가 많다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 그 사용자들을 기다리게 하면 모두 떠나갈 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이다. 웹 사이트는 반드시, 무조건 빨라야한다. 나 같은 90년대 생들은 이해할 것이다. 네이버 블로그에서 동영상 볼 때 버퍼링의 심각함을.. (요새 애들은 버퍼링이 뭔지 알까?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 말하면 &lt;b&gt;비동기가 무조건 좋은 거 아닌가? 동기가 왜 필요해 그럼?&lt;/b&gt; 이라는 의문이 생길 수 있는데, 다시 카페를 예로 들면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5명의 손님이 왔을 때,&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;비록 마지막 손님은 많이 기다려야 하겠지만, 적어도 손님들은 온 순서대로 음료를 받을 것이다. =&amp;gt; 동기&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;합리적인 카페 사장은 3번째 손님이 주문한 레인보우샤베트커피토피넛그린라떼보다, 간단한 아메리카노를 시킨 마지막 손님에게 음료를 줄 수 있다. =&amp;gt; 비동기&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차이를 알겠는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;동기는, 코드가 작성된 순서대로 반드시 실행된다&lt;/b&gt;는 이점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;100ms 를 들여 받아온 결과값 A를 기반으로 B() 함수를 실행할 때, A가 없을 것이라는 걱정은 안해도 된다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;b&gt;비동기는 코드가 작성된 순서대로 실행되지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A를 B() 함수에서 사용하려고 하는데, A가 받아와지기도 전에 B가 실행되면 오류가 생길 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 비동기와 동기의 장단점을 완벽히 알았을 것이라고 믿는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기는, 코드가 작성된 순서대로 실행되지만, 그렇기에 느리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기는 코드가 작성된 순서대로 실행되지 않지만, 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 유튜브 동영상을 로딩하는데 10초가 걸린다고 가정했을 때, 코드가 다음과 같다면,&lt;/p&gt;
&lt;pre id=&quot;code_1639317131410&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;영상받아오기() // 10s
댓글받아오기() // 10ms
관련동영상받아오기() // 100ms&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10초동안, 우리는 댓글도 관련동영상도 받아올 수 없다. 그런 웹사이트는 보지 못했을 것이고, 봤다면 뒤로가기로 뛰쳐 나왔을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이벤트 루프?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 카페 주방으로 가보자. 비동기적으로 주문을 받았고, 알바생은 다음의 음료들을 만들어야 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;1번 손님의 아메리카노 6잔 -&amp;gt; 3m&lt;/li&gt;
&lt;li&gt;2번 손님의 아이스 돌체 라떼 -&amp;gt; 2m&lt;/li&gt;
&lt;li&gt;3번 손님의 알바생고생시키는음료 -&amp;gt; 10m&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 우리는 카페가 비동기적일 경우,&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;2분 후에 2번 손님이 아이스 돌체 라떼를 받고&lt;/li&gt;
&lt;li&gt;1분 후에 ( 총 3분 후 ) 1번 손님이 아메리카노를 받은 뒤&lt;/li&gt;
&lt;li&gt;7분 후( 총 10분 후 ) 3번 손님이 알바생고생시키는음료를 받는다&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 직관적으로 예상할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 Javascript 런타임 엔진이 이런 일을 실제로 한다는 것인데, 그걸 어떻게 하는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그것을 다음 글에서 다룰 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 루프와, 매크로태스크, 마이크로태스크, async/await, Promise 등.. 할 게 너무 많지만, 차근차근 해보자.&lt;/p&gt;</description>
      <category>Programming Language/Javascript</category>
      <category>async/await</category>
      <category>javascript</category>
      <category>javascript 이벤트루프란</category>
      <category>promise</category>
      <category>동기</category>
      <category>마이크로태스크</category>
      <category>매크로태스크</category>
      <category>비동기</category>
      <category>이벤트 루프</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/45</guid>
      <comments>https://simju9397.tistory.com/45#entry45comment</comments>
      <pubDate>Sun, 12 Dec 2021 23:43:36 +0900</pubDate>
    </item>
    <item>
      <title>Javascript 변수 선언법 var 에 대하여 : 호이스팅, IIFE</title>
      <link>https://simju9397.tistory.com/44</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;앞서&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 &lt;a href=&quot;https://ko.javascript.info/var&quot;&gt;https://ko.javascript.info/var&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오래된 'var'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ko.javascript.info&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;](&lt;a href=&quot;https://ko.javascript.info/var&quot;&gt;https://ko.javascript.info/var&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 링크의 글을 기반으로 작성하고 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Javascript 의 변수 선언&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Javascript 를 처음 시작하는 분들이라면, 사실 굳이 몰라도 되는 개념이 바로 var 이다. 하지만 언젠가는 알아야 한다. 다른 사람의 코드를 봐야 할 일이 분명히 생기고, 레거시 코드를 다루면서 let 이나 const 로 고쳤을 때 예기치 못한 에러가 발생할 수도 있다. 그러므로 이 글에서 Javascript 의 변수 선언식 중 가장 오래 되었고, 가장 관대한 변수 선언식인 var 를 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Javascript 에는 세 가지의 변수 선언 방식이 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;let&lt;/li&gt;
&lt;li&gt;const&lt;/li&gt;
&lt;li&gt;var&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최신 Javascript 문법으로 배운 사람들은 let 과 const만 사용하라는 말을 많이 봤을 것이다. let 은 일반 변수를 선언할 때, const 는 변하지 않는 값을 나타낼 때 사용한다고 말이다. 하지만 오래된 코드를 보다보면 var 가 많이 쓰이는 데, 통상적으로 var 는 let 으로 바꿔도 크게 문제가 발생하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 var 는 초기 자바스크립트 구현 방식 때문에 let 과 const 로 선언한 변수와는 다른 방식으로 동작한다. 어떻게 다른지 한번 보자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;var 는 블록 스코프가 없다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var 로 선언한 변수의 스코프는 함수 스코프이거나 전역 스코프이다. 블록 기준으로 스코프가 생기지 않기 때문에, 블록 안에서 선언된 변수를 블록 밖에서 접근 가능하다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;if (true) {
  var test = true; // 'let' 대신 'var'를 사용했습니다.
}

alert(test); // true(if 문이 끝났어도 변수에 여전히 접근할 수 있음)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문에서도 같은 일이 일어난다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;for (var i = 0; i &amp;lt; 10; i++) {
  // ...
}

alert(i); // 10, 반복문이 종료되었지만 'i'는 전역 변수이므로 여전히 접근 가능합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var 가 함수 안에 있다면, 함수 스코프에 있는 변수가 된다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;function sayHi() {
  if (true) {
    var phrase = &quot;Hello&quot;;
  }

  alert(phrase); // 제대로 출력됩니다.
}

sayHi();
alert(phrase); // Error: phrase is not defined&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오래 전의 자바스크립트에서는 블록 수준의 렉시컬 환경이 만들어 지지 않았기 때문이라고 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;var 와 let 의 차이점 : var 는 재선언이 된다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 let 으로 이미 선언된 변수를 다시 선언하면, 에러가 발생한다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;let user;
let user; // SyntaxError: 'user' has already been declared&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 var 로 선언된 변수는 몇 번을 재선언해도 상관없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 var 로 선언된 변수를 다시 var 로 재선언하게 되면, var 라는 키워드는 무시당한다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;var user = &quot;Pete&quot;;

var user = &quot;John&quot;; // this &quot;var&quot; does nothing (already declared)
// ...it doesn't trigger an error

alert(user); // John&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;호이스팅&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var 선언은 함수가 시작될 때 처리된다. 전역에서 선언한 var 변수라면, 스크립트가 시작될 때 처리된다;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 본문 내에서 var 로 선언한 변수는 &lt;b&gt;선언 위치와 상관없이&lt;/b&gt;, 함수 본문이 시작되는 지점에서 정의된다.&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;function sayHi() {
  phrase = &quot;Hello&quot;;

  alert(phrase);

  var phrase;
}
sayHi();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 함수가 있다면, 암묵적으로 var phrase 가 함수가 시작될 때 처리된다.&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;function sayHi() {
  var phrase;

  phrase = &quot;Hello&quot;;

  alert(phrase);
}
sayHi();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록 스코프는 무시되기 때문에, 아래 코드 역시 위처럼 동작한다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;function sayHi() {
  phrase = &quot;Hello&quot;; // (*)

  if (false) {
    var phrase;
  }

  alert(phrase);
}
sayHi();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 변수가 끌어올려 지는 현상을 &lt;b&gt;호이스팅&lt;/b&gt; 이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 함수에서, if (false) 블록 안의 코드는 절대 실행되지 않지만, 호이스팅에는 전혀 영향을 주지 않는다. if 블록 내부의 var 는 sayHi 함수의 시작 부분에서 처리되기 때문에, 이미 (*) 부분에서 phrase 는 선언이 되어 있는 상태이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 주의할 것이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선언은 호이스팅 되지만, 할당은 호이스팅 되지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시를 살펴보자.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;function sayHi() {
  alert(phrase);

  var phrase = &quot;Hello&quot;;
}

sayHi();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var phrase = &quot;Hello&quot; 행에선 두 가지 일(변수의 선언과 할당)이 일어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 선언은 함수 실행이 시작될 때 처리되지만(호이스팅) 할당은 호이스팅되지 않기 때문에, 위 코드는 아래처럼 동작한다.&lt;/p&gt;
&lt;pre id=&quot;code_1631005343908&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function sayHi() {
  var phrase; // 선언은 함수 시작 시 처리됩니다.

  alert(phrase); // undefined

  phrase = &quot;Hello&quot;; // 할당은 실행 흐름이 해당 코드에 도달했을 때 처리됩니다.
}

sayHi();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 모든 var 선언은 함수 시작 시(전역일 경우 스크립트 시작 시 ) 처리되기 때문에, var 로 선언한 변수는 어디서든 참조할 수 있다. 값을 할당하기 전에는 undefined 가 뜨지만 말이다.(할당은 호이스팅 되지 않기 때문이다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;즉시 실행 함수 표현식 ( IIFE )&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금이야 let 과 const 라는 선택지가 있었지만, 옛날에는 정말 선택지가 var 뿐이었다. 그런데 var 는 블록 스코프를 가질 수가 없다. 개발자들은 var 도 블록 스코프를 가질 수 있게 여러가지 방안을 고려하게 되었고, 이 때 만들어진 것이 즉시 실행 함수 표현식(Immediately-invocked function expressions) 이다. 줄여서 IIFE 라고도 한다. (개인적으로 면접 질문으로 받아봤다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘에는 당연히 쓸 일이 없다. let 과 const 가 대놓고 권장되고 있기 때문에. 하지만 오래된 코드를 보다보면 만날 수 있기 때문에 뭔지 알아 둘 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IIFE는 다음과 같이 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1631005614400&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(function() {

  let message = &quot;Hello&quot;;

  alert(message); // Hello

})();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 함수 표현식으로 만든 다음에, 바로 호출해버리는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉시 실행 함수를 만들 때는, 함수 표현식을 괄호로 둘러쌓아 ( function() {...} ) 의 형태로 만들어 줘야 한다. (안하면 에러가 난다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 function 키워드를 만나는 순간, 함수 선언문이 시작될 것이라 예상한다. 근데 함수 선언문으로 함수를 만들 때는 함수의 이름이 반드시 있어야 한다. 그래서 아래처럼 하면 에러가 난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1631005791754&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 함수를 선언과 동시에 실행하려고 함
function() { // &amp;lt;-- Error: Function statements require a function name

  let message = &quot;Hello&quot;;

  alert(message); // Hello

}();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&quot;그럼 이름 넣으면 되는 거 아닌가?&quot; 라고 생각해서 넣어봐도 에러가 발생한다. 자바스크립트는 함수 선언문으로 정의한 함수를 정의와 동시에 바로 호출하는 것을 허용하지 않기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1631005836329&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 맨 아래의 괄호 때문에 문법 에러가 발생합니다.
function go() {

}(); // &amp;lt;-- 함수 선언문은 선언 즉시 호출할 수 없습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 함수를 괄호로 감싸면, 자바스크립트가 함수를 함수 선언문이 아닌 표현식으로 인식하도록 속일 수 있다. 함수 표현식은 이름이 없어도 괜찮고, 즉시 호출도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;괄호를 사용하는 방법 말고도, 자바스크립트가 함수 표현식이라고 인식하게 해주는 다른 방법들이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1631005896009&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// IIFE를 만드는 방법

(function() {
  alert(&quot;함수를 괄호로 둘러싸기&quot;);
})();

(function() {
  alert(&quot;전체를 괄호로 둘러싸기&quot;);
}());

!function() {
  alert(&quot;표현식 앞에 비트 NOT 연산자 붙이기&quot;);
}();

+function() {
  alert(&quot;표현식 앞에 단항 덧셈 연산자 붙이기&quot;);
}();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 모던 자바스크립트에서는 별 필요 없다. 알아만 두자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;요약&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var 로 선언한 변수는 let 과 const 로 선언한 변수와 다른 두 가지 주요한 특성을 보인다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;var 로 선언한 변수는 블록 스코프가 아닌 함수 스코프를 갖는다.&lt;/li&gt;
&lt;li&gt;var 선언은 함수가 시작되는 시점(전역에선 스크립트 시작 시점)에서 처리된다. (호이스팅)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 특성들은 대부분의 상황에서 부작용을 만들어 내고, let 이 표준으로 도입된 이유가 바로 이것이다. 변수는 블록 레벨 스코프를 갖는 게 좋기 때문이다. 이것이 let 과 first 가 대세가 된 이유이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming Language/Javascript</category>
      <category>IIFE란</category>
      <category>javascript IIFE</category>
      <category>javascript var</category>
      <category>javascript 호이스팅</category>
      <category>var let const</category>
      <category>호이스팅</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/44</guid>
      <comments>https://simju9397.tistory.com/44#entry44comment</comments>
      <pubDate>Tue, 7 Sep 2021 18:15:06 +0900</pubDate>
    </item>
    <item>
      <title>Javascript 클로저가 대체 뭘까? - 변수의 유효 범위와 클로저</title>
      <link>https://simju9397.tistory.com/43</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;대체 클로저가 뭐에요&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저는 Javascript 개발자에게 단골 면접 질문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 한마디로 정의하기가 어려웠던 개념이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/closure&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.javascript.info/closure&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1630826856851&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;변수의 유효범위와 클로저&quot; data-og-description=&quot;&quot; data-og-host=&quot;ko.javascript.info&quot; data-og-source-url=&quot;https://ko.javascript.info/closure&quot; data-og-url=&quot;https://ko.javascript.info/closure&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/beTAIx/hyLu5OI1Ek/vF9T0049kikkct7lOsgWr1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/5RxIi/hyLu3ciIOs/oEkkCZES9klkdeu2hfKQNk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/closure&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.javascript.info/closure&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/beTAIx/hyLu5OI1Ek/vF9T0049kikkct7lOsgWr1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/5RxIi/hyLu3ciIOs/oEkkCZES9klkdeu2hfKQNk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;변수의 유효범위와 클로저&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ko.javascript.info&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 이 글을 읽으면서, 내 나름대로 클로저에 대해 정리해보고자 한다. 면접에서 질문을 받았을 때 완벽히 설명할 수 있을만큼 말이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;중첩 함수&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 내부에서 선언한 함수를 중첩 함수( Nested function ) 이라고 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1630827463890&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function greet(firstName, lastName) {
  function getFullName() {
    return `${firstName} ${lastName}`;
  }
  
  alert(&quot;Hello, &quot; + getFullName());
  alert(&quot;Bye, &quot; + getFullName());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복되는 코드를 줄이기에 좋은 방식으로, 자바스크립트에 익숙하다면 자연스럽게 쓰고 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 클로저를 설명할 때, 혹은 질문을 받을 때 가장 단골로 나오는 makeCounter 예제를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1630828013476&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function makeCounter() {
  let count = 0;

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

let counter = makeCounter();

alert( counter() ); // 0
alert( counter() ); // 1
alert( counter() ); // 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의문이 들 것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;대체 왜 0, 1, 2 가 찍히지?&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 0,0,0 이 찍히는 게 맞지 않나? 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 실행될 때 마다 count 가 0이 되고, count++ 이 되지만 리턴 후에 적용되기 때문에 0,0,0 이 찍히는 게 맞지 않나??????&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 의문을 해결할 수 있게 되면 자바스크립트 숙련도가 올라간다고 한다. 가보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;렉시컬 환경&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에선&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 실행 중인 함수,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 코드 블록( {...} ) ,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 스크립트 전체&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;는 Lexical Environment 라 불리는 내부 숨김 연관 객체(Internal hidden associated object) 를 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(일단 넘어가자)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렉시컨 환경 객체는 두 부분으로 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 환경 레코드(Environment Record)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모든 지역 변수&lt;/b&gt;를 프로퍼티로 저장하고 있는 객체. this 값과 같은 정보도 여기에 저장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 외부 렉시컬 환경에 대한 참조 - 외부 코드와 연관됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;변수는 특수 내부 객체인 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;환경 레코드&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의 프로퍼티일 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &lt;span style=&quot;background-color: #009a87;&quot;&gt;&lt;b&gt;변수&lt;/b&gt;&lt;/span&gt;를 가져오거나 변경하는 것은, &lt;span style=&quot;background-color: #009a87;&quot;&gt;&lt;b&gt;환경 레코드의 프로퍼티&lt;/b&gt;&lt;/span&gt;를 가져오거나 변경함을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1630828692105&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let phrase = &quot;Hello&quot;;

alert(phrase);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 두 줄짜리 코드엔 하나의 렉시컬 환경이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식이 될 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;354&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MpFQF/btrd65UQPvl/qkWXSZMbxRU0zkoIKcK3MK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MpFQF/btrd65UQPvl/qkWXSZMbxRU0zkoIKcK3MK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MpFQF/btrd65UQPvl/qkWXSZMbxRU0zkoIKcK3MK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMpFQF%2Fbtrd65UQPvl%2FqkWXSZMbxRU0zkoIKcK3MK%2Fimg.png&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;354&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 스크립트 전체와 관련된 렉시컬 환경을 전역 렉시컬 환경이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 렉시컬 환경은 당연히, 외부 렉시컬이 없으므로 null 을 가리킨다. ( global 보다 더 큰 scope 는 없으니까! )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 긴 걸 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;952&quot; data-origin-height=&quot;272&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GF5fx/btrd7TGAq6x/eyEMthQkdv06s9hUAUsPUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GF5fx/btrd7TGAq6x/eyEMthQkdv06s9hUAUsPUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GF5fx/btrd7TGAq6x/eyEMthQkdv06s9hUAUsPUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGF5fx%2Fbtrd7TGAq6x%2FeyEMthQkdv06s9hUAUsPUk%2Fimg.png&quot; data-origin-width=&quot;952&quot; data-origin-height=&quot;272&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오른쪽의 네모 상자들은 코드가 한 줄 한 줄 실행될 때마다 전역 렉시컬 환경이 어떻게 변화하는지 보여준다,&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;스크립트가 시작되면, 스크립트 내에서 선언한 &lt;b&gt;변수 전체&lt;/b&gt;가 렉시컬 환경의 프로퍼티로 올라간다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 때 변수의 상태는 특수 내부 상태(special internal state) 인 &lt;b&gt;`uninitialized`&lt;/b&gt; 가 된다.&lt;/li&gt;
&lt;li&gt;자바스크립트 엔진은 uninitialized 상태의 변수를 인지하긴 하지만,&lt;b&gt; let 을 만나기 전까진 이 변수를 참조할 수 없다&lt;/b&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;let 이 나타났다! 값을 아직 할당하지 않았기 때문에 phrase 프로퍼티의 값은 undefined 이다. pharse 는 이 시점 이후부터 &lt;b&gt;참조될 수 있다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;phrase 에 &quot;Hello&quot;가 할당되었다.&lt;/li&gt;
&lt;li&gt;phrase 에 &quot;Bye&quot;가 할당되었다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직까지 어려운 건 없다. 지금까지 배운 것을 요약해보면,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;렉시컬 환경에는 환경 레코드와 외부 환경에 대한 참조가 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;변수는 환경 레코드의 프로퍼티&lt;/b&gt;이다.&lt;/li&gt;
&lt;li&gt;환경 레코드는 현재 실행 중인 함수와 코드 블록, 스크립트와 연관되어 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;변수를 변경하면 환경 레코드의 프로퍼티가 변경&lt;/b&gt;된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;함수 선언문&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수도 변수와 마찬가지로, 환경 레코드의 값이다. 하지만 변수와의 큰 차이가 있는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;함수 선언문(function declaration) 으로 선언한 함수는 일반 변수와는 달리 바로 초기화된다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;함수 선언문으로 선언한 함수는 렉시컬 환경이 만들어지는 즉시 사용할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;변수는 let을 만나 선언이 될 때까지 사용할 수 없는데&lt;/b&gt; 말이다. (unintialized 상태로 가는 데 말이다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;250&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5n02K/btrd9maXWGl/gsQRN1vqDX6Rd0rjKsKmf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5n02K/btrd9maXWGl/gsQRN1vqDX6Rd0rjKsKmf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5n02K/btrd9maXWGl/gsQRN1vqDX6Rd0rjKsKmf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5n02K%2Fbtrd9maXWGl%2FgsQRN1vqDX6Rd0rjKsKmf0%2Fimg.png&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;250&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 동작 방식은 함수 선언문으로 정의한 함수에만 적용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;let say = function (name) {...} 같이 함수를 변수에 할당한 함수 표현식은 해당하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;내부와 외부 렉시컬 환경&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 호출해 실행하면 &lt;b&gt;새로운 렉시컬 환경&lt;/b&gt;이 자동으로 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 렉시컬 환경엔 함수 &lt;b&gt;호출 시 넘겨받은 매개변수&lt;/b&gt;와 함수의 &lt;b&gt;지역 변수&lt;/b&gt;가 저장된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;224&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSUUBp/btrd03dbutF/VLCIhCnbPYiqcLGoyK5OV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSUUBp/btrd03dbutF/VLCIhCnbPYiqcLGoyK5OV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSUUBp/btrd03dbutF/VLCIhCnbPYiqcLGoyK5OV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSUUBp%2Fbtrd03dbutF%2FVLCIhCnbPYiqcLGoyK5OV1%2Fimg.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;224&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 호출 중인 동안에,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출 중인 함수를 위한 내부 렉시컬 환경 (name : &quot;John&quot; 이 &lt;b&gt;매개변수로써&lt;/b&gt; 저장된 say 함수 내부 ) 과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 렉시컬 환경이 가리키는 외부 렉시컬 환경을 갖게 된다. ( say : function 과 phrase : &quot;Hello&quot; 가 저장된 전역 렉시컬 환경 )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 내부 렉시컬 환경은, 렉시컬 환경의 두 가지 요소 중 두 번째 (첫 번째는 환경 레코드인거 아시죠 ? ) 인 &quot;외부 렉시컬 환경에 대한 참조&quot;를 갖는다. 즉 전역 렉시컬 환경을 가리키는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;변수의 값을 찾아가는 과정&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;내부 렉시컬 환경을 먼저 찾는다&lt;/li&gt;
&lt;li&gt;내부에 없다면, 내부에서 참조하고 있는 외부 렉시컬 환경에서 찾는다.&lt;/li&gt;
&lt;li&gt;전역 렉시컬 환경에 도달할 때 까지, 2번을 반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 -&amp;gt; 외부 -&amp;gt; 외부의 외부 -&amp;gt; 외부의 외부의 외부 -&amp;gt; 외부의 외부의 .... -&amp;gt; 전역 렉시컬&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;까지 찾는 것이다. 너무 직관적이고 쉽지 않은가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;전역 렉시컬에서도 못 찾으면??&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엄격 모드 : 에러가 발생한다.&lt;/li&gt;
&lt;li&gt;비엄격 모드 : 새로운 전역 변수를 만들어낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;214&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duUvxA/btrd5jMCYOH/pivktKWE86poDPtLKMgBm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duUvxA/btrd5jMCYOH/pivktKWE86poDPtLKMgBm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duUvxA/btrd5jMCYOH/pivktKWE86poDPtLKMgBm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduUvxA%2Fbtrd5jMCYOH%2FpivktKWE86poDPtLKMgBm1%2Fimg.png&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;214&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시를 보면서 변수 검색 과정을 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 렉시컬은 name : &quot;John&quot; 이 들어있는, say 의 렉시컬 환경이고, say 가 참조하는 외부 렉시컬(전역 렉시컬) 에는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;phrase : &quot;Hello&quot; 와 say : function 이 담겨 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;함수 say 내부의 alert 에서, 변수 name 에 접근할 때 먼저 내부 렉시컬 환경을 살펴본다. 내부 렉시컬 환경에서 name 을 찾았다.&lt;/li&gt;
&lt;li&gt;phrase 를 찾아야 하는데, 내부 렉시컬 환경에 없다. 외부 렉시컬 환경으로 확장한다. 찾았다!&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;함수를 반환하는 함수&lt;/h4&gt;
&lt;pre id=&quot;code_1630833045282&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function makeCounter() {
  let count = 0;

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

let counter = makeCounter();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;makeCounter 예시로 돌아오자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;makeCounter() 를 호출할 때마다, 새로운 렉시컬 환경 객체가 만들어지고, 여기에 makeCounter() 를 실행하는데 필요한 변수들이 저장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;256&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bD4MhG/btrd21kQVVS/WujmzklMD2FhxiyhLG5Oz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bD4MhG/btrd21kQVVS/WujmzklMD2FhxiyhLG5Oz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bD4MhG/btrd21kQVVS/WujmzklMD2FhxiyhLG5Oz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbD4MhG%2Fbtrd21kQVVS%2FWujmzklMD2FhxiyhLG5Oz1%2Fimg.png&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;256&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;makeCounter의 내부 렉시컬 환경에는 count : 0이 저장되고, 내부에서 참조하는 외부 렉시컬(전역) 은 makeCounter : function 과 counter : undefined 를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 위의 say(&quot;John&quot;) 예제와의 차이가 있는데, 바로 makeCounter() 는 한줄 짜리인 중첩 함수가 만들어진다는 것이다. (return count++) 현재는 중첩 함수가 생기기만 하고 실행은 되지 않은 상태이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 클로저에 대해 검색할 때 가장 많이 나오는 말이 나온다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;모든 함수는 함수가 생성된 곳의 렉시컬 환경을 기억한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수는 [[Environment]] 라 불리는 숨김 프로퍼티를 갖는데, 여기에 &lt;b&gt;함수가 만들어진 곳의 렉시컬 환경&lt;/b&gt;에 대한 참조가 저장된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;246&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cndkpE/btrea1LcSPm/d0xcfKPNU2h9RqAx47sKCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cndkpE/btrea1LcSPm/d0xcfKPNU2h9RqAx47sKCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cndkpE/btrea1LcSPm/d0xcfKPNU2h9RqAx47sKCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcndkpE%2Fbtrea1LcSPm%2Fd0xcfKPNU2h9RqAx47sKCK%2Fimg.png&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;246&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 counter.[[Environment]] 에는 { count : 0 } 이 있는 렉시컬 환경 ( 즉, makeCounter 함수의 내부 렉시컬) 에 대한 참조가 저장된다. 호출 장소와 상관없이 함수가 자신이 태어난 곳을 기억할 수 있는 건, 이 [[Environment]] 프로퍼티 덕분이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[[ Environment ]] 는 함수가 생성될 때 딱 한 번 값이 세팅되고, 영원히 변하지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;counter() 를 호출하면, 각 호출마다 새로운 렉시컬 환경이 생성된다. 그리고 이 렉시컬 환경은 counter.[[Environment]] 에 저장된 렉시컬 환경을 외부 렉시컬로 참조한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;310&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZRLHC/btrd9k5kphO/MZwX77HPisfJKPPibmO5f1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZRLHC/btrd9k5kphO/MZwX77HPisfJKPPibmO5f1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZRLHC/btrd9k5kphO/MZwX77HPisfJKPPibmO5f1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZRLHC%2Fbtrd9k5kphO%2FMZwX77HPisfJKPPibmO5f1%2Fimg.png&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;310&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function() { return count++; } 의 내부 렉시컬에는 아무 프로퍼티도 없다. ( 당연하다. 매개변수도 지역변수도 없으니 ) 그리고 [[ Environment]] 에는 makeCounter() 의 내부 렉시컬에 대한 참조 값이 저장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨 마지막 줄의 alert() 으로 넘어오면 count 변수가 필요하다. 내부 렉시컬 환경에서 count 를 찾아서 count++ 을 해주려고 하는데, 없다! 그러면 어떻게 하는지 기억날 것이다. 찾을 때 까지 외부로 가는 것이다. 즉 makeCounter() 의 내부 렉시컬에서 count 를 찾아내는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 값이 갱신되어야 한다. 이 때, &lt;b&gt;변수값 갱신은 변수가 저장된 렉시컬 환경&lt;/b&gt;에서 이루어진다. ( 까먹었다면, 맨 위 렉시컬 환경 파트의 네 번째 줄을 보자. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;322&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IHxpF/btrd1yYneMx/KyITQcdByO8sMek7U0vSl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IHxpF/btrd1yYneMx/KyITQcdByO8sMek7U0vSl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IHxpF/btrd1yYneMx/KyITQcdByO8sMek7U0vSl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIHxpF%2Fbtrd1yYneMx%2FKyITQcdByO8sMek7U0vSl1%2Fimg.png&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;322&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;counter() 를 여러 번 호출하면 count 변수가 2,3 으로 증가하는 이유가 바로 여기에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;클로저&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저는 &lt;b&gt;외부 변수를 기억하고, 이 외부 변수에 접근할 수 있는 함수&lt;/b&gt;를 의미한다. 자바스크립트에서는 모든 함수가 자연스럽게 클로저가 된다! 예외가 하나 있긴 하다고 하는데, 자세한 건&lt;a href=&quot;https://ko.javascript.info/new-function&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; new Function 문법&lt;/a&gt; 에서 찾아보자. (나중에 정리하긴 할 것이다!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요점을 정리하면, 함수는 숨김 프로퍼티인 [[ Environment ]] 를 이용해 자신이 어디서 만들어졌는지를 기억한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 본문에서는 [[ Environment ]] 를 사용해 외부 변수에 접근한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;가비지 컬렉션&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 호출이 끝나면 함수에 댁응하는 렉시컬 환경이 메모리에서 제거된다. 함수와 관련된 변수들은 이때 모두 사라져버린다. 함수 호출이 끝나면 관련 변수를 참조할 수 없는 것이 바로 이거 때문이다.모든 객체는 도달 가능한 상태일 때만 메모리에서 유지되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 호출이 끝난 후에도, 여전히 도달 가능한 중첩 함수가 있을 수 있다. 이 때는 이 중첩 함수의 [[ Environment ]] 프로퍼티에 외부 함수 렉시컬 환경에 대한 정보가 저장된다. 도달 가능한 상태가 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1630834430314&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function f() {
  let value = 123;

  return function() {
    alert(value);
  }
}

let g = f(); // g.[[Environment]]에 f() 호출 시 만들어지는
// 렉시컬 환경 정보가 저장됩니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 중첩함수를 사용할 때는 주의점이 있다. f() 를 호출 하고 그 결과를 어딘가에 저장하는 경우, 호출 시 만들어지는 &lt;b&gt;각 렉시컬 환경 모두가 메모리에 유지된다&lt;/b&gt;는 점이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1630834547570&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function f() {
  let value = Math.random();

  return function() { alert(value); };
}

// 배열 안의 세 함수는 각각 f()를 호출할 때 생성된
// 렉시컬 환경과 연관 관계를 맺습니다.
let arr = [f(), f(), f()];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렉시컬 환경 객체는 다른 객체와 마찬가지로 도달할 수 없을 때 메모리에서 삭제된다. 해당 렉시컬 환경 객체를 참조하는 중첩 함수( 여기에서는 alert(value) 가 있는 function 함수 ) 가 하나라도 있다면, 영원히 사라지지 않는다. ( 여기서는 value )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 중첩 함수가 메모리에서 삭제되고 난 후에야 그 중첩 함수를 감싸는 렉시컬 환경 ( value ) 도 메모리에서 제거된다.&lt;/p&gt;
&lt;pre id=&quot;code_1630834632539&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function f() {
  let value = 123;

  return function() {
    alert(value);
  }
}

let g = f(); // g가 살아있는 동안엔 연관 렉시컬 환경도 메모리에 살아있습니다.

g = null; // 도달할 수 없는 상태가 되었으므로 메모리에서 삭제됩니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;최적화&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 본 것 처럼, 이론상으론 함수가 살아있다면 모든 외부 변수가 메모리에 유지된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제로는 자바스크립트 엔진이 이를 지속해서 최적화한다. 변수 사용을 분석하고 외부 변수가 사용되지 않는다고 판단되면 메모리에서 제거한다. 이것이 부작용을 일으킬 수도 있는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1630834747250&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function f() {
  let value = Math.random();

  function g() {
    debugger; // Uncaught ReferenceError: value is not defined가 출력됩니다.
  }

  return g;
}

let g = f();
g();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;value 가 정의되지 않았다고 한다. 이론상으로는 value 에 접근할 수 있어야 하는데, 최적화의 대상이 되어서 이런 에러가 발생한 것이다. 그래서 이런 최적화 때문에 때로는 이상한 문제가 발생하기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1630834912992&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let value = &quot;이름이 같은 다른 변수&quot;;

function f() {
  let value = &quot;가장 가까운 변수&quot;;

  function g() {
    debugger; // 콘솔에 alert(value);를 입력하면 '이름이 같은 다른 변수'가 출력됩니다.
  }

  return g;
}

let g = f();
g();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;g() 가 생성된 환경 ( f() 의 내부 렉시컬 ) 의 value 인 &quot;가장 가까운 변수&quot; 를 출력해야 한다고 생각하지만, 가비지 컬렉션에 의해 &quot;이름이 같은 다른 변수&quot; 를 출력하는 것이다. 알아두면 좋을 것 같다. 알고 이 문제를 직면하는 것과 모르는 것은 천지차이니까 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제를 풀어봅시다!&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 함수는 가장 최근의 변화를 감지할까요?&lt;/p&gt;
&lt;pre id=&quot;code_1630835364592&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let name = &quot;John&quot;;

function sayHi() {
  alert(&quot;Hi, &quot; + name);
}

name = &quot;Pete&quot;;

sayHi(); // what will it show: &quot;John&quot; or &quot;Pete&quot;?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;답은 Pete 입니다. &lt;b&gt;가장 최근의 값&lt;/b&gt;을 사용하기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 어떤 변수가 사용 가능할까요?&lt;/p&gt;
&lt;pre id=&quot;code_1630835488844&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function makeWorker() {
  let name = &quot;Pete&quot;;

  return function() {
    alert(name);
  };
}

let name = &quot;John&quot;;

// create a function
let work = makeWorker();

// call it
work(); // what will it show?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;makeWorker 는 내부 함수를 만들고 리턴하고 있습니다. 내부 함수의 alert에서 사용하는 name 은 어떤 것일까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 &quot;Pete&quot; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 가까운 외부 렉시컬 환경에서의 변수 값을 가져오기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. counter 는 독립적일까요?&lt;/p&gt;
&lt;pre id=&quot;code_1630835653835&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function makeCounter() {
  let count = 0;

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

let counter = makeCounter();
let counter2 = makeCounter();

alert( counter() ); // 0
alert( counter() ); // 1

alert( counter2() ); // ?
alert( counter2() ); // ?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;makeCounter를 사용해 두 개의 카운터를 만들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 값은 0,1,2,3 일까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;답은 0,1,0,1 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 다른 makeCounter 호출에 의해 만들어졌기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;makeCounter one 과 two 가 생성된 환경은 각각 다르니까요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 생성자 함수를 이용해 counter 객체를 만들었다.&lt;/p&gt;
&lt;pre id=&quot;code_1630835747722&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Counter() {
  let count = 0;

  this.up = function() {
    return ++count;
  };
  this.down = function() {
    return --count;
  };
}

let counter = new Counter();

alert( counter.up() ); // ?
alert( counter.up() ); // ?
alert( counter.down() ); // ?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드의 실행 결과는 어떨까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의도한 대로 잘 동작합니다. 1,2,1 이 될 겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this.up 에 저장된 중첩 함수와 this.down 에 저장된 중첩 함수 모두 동일한 외부 렉시컬 환경에서 만들어졌기 때문에, 같은 count 변수를 공유합니다. ( 헷갈리지 마세요. 앞서 언급한 클로저가 되지 않는 함수 선언 방식인 new Function 과는 다른, 생성자 함수입니다!! )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. if 문 안의 함수&lt;/p&gt;
&lt;pre id=&quot;code_1630835976433&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let phrase = &quot;Hello&quot;;

if (true) {
  let user = &quot;John&quot;;

  function sayHi() {
    alert(`${phrase}, ${user}`);
  }
}

sayHi();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if 문 안에서 정의되었기 때문에 sayHi 는 if 블록의 렉시컬 환경에 있습니다. ( 실행 중인 함수, 코드 블럭, 스크립트 전체는 렉시컬 환경을 갖는 거 기억하시죠? )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 sayHi 를 전역 렉시컬 환경에서 호출하면, 내부까지 들어가는 것이 아니므로 에러가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 클로저의 활용&lt;/p&gt;
&lt;pre id=&quot;code_1630836181753&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sum(1)(2) = 3
sum(5)(-1) = 4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 연산을 해주는 함수 sum 을 만들어보세요.&lt;/p&gt;
&lt;pre id=&quot;code_1630837649576&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function sum(a) {
	return function (b) {
      return a + b;
    }
}

alert(sum(1)(2))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난 이렇게 짰다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 함수를 이용해 원하는 값만 걸러내기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열에 사용할 수 있는 내장 메서드 arr.filter(func) 는 함수 func 의 반환 값을 true 로 만드는 모든 요소를 배열로 반환해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;filter 에 넘겨서 사용할 수 있는 함수 두 가지를 만들어봅시다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;inBetween(a, b) - a 이상 b 이하&lt;/li&gt;
&lt;li&gt;inArray([...]) - 배열 안에 있는 값인가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 함수를 활용하면 다음과 같은 결과가 나와야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;arr.filter(inBetween(3,6))&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;ndash; 3과 6 사이에 있는 값만 반환함&lt;/li&gt;
&lt;li&gt;arr.filter(inArray([1,2,3]))&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;ndash;&lt;span&gt;&amp;nbsp;&lt;/span&gt;[1,2,3]&lt;span&gt;&amp;nbsp;&lt;/span&gt;안에 있는 값과 일치하는 값만 반환함
&lt;pre id=&quot;code_1630847824927&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* ... 여기에 두 함수 inBetween과 inArray을 만들어주세요 ...*/
let arr = [1, 2, 3, 4, 5, 6, 7];

alert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6

alert( arr.filter(inArray([1, 2, 10])) ); // 1,2​&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1630848172861&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function inBetween(a, b) {
  return function(x) {
    return x &amp;gt;= a &amp;amp;&amp;amp; x &amp;lt;= b;
  }
}

function inArray(arr) {
  return function(x) {
    return arr.includes(x);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. 필드를 기준으로 정렬하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체가 담긴 배열을 정렬해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1630848242727&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let users = [
  { name: &quot;John&quot;, age: 20, surname: &quot;Johnson&quot; },
  { name: &quot;Pete&quot;, age: 18, surname: &quot;Peterson&quot; },
  { name: &quot;Ann&quot;, age: 19, surname: &quot;Hathaway&quot; }
];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 방법을 사용해 정렬할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1630848261919&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 이름을 기준으로 정렬(Ann, John, Pete)
users.sort((a, b) =&amp;gt; a.name &amp;gt; b.name ? 1 : -1);

// 나이를 기준으로 정렬(Pete, Ann, John)
users.sort((a, b) =&amp;gt; a.age &amp;gt; b.age ? 1 : -1);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데, 아래와 같이 함수를 하나 만들어서 정렬하면 깔끔할 것 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1630848283330&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;users.sort(byField('name'));
users.sort(byField('age'));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;byField 함수를 구현해봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해답&lt;/p&gt;
&lt;pre id=&quot;code_1630848473553&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let users = [
  { name: &quot;John&quot;, age: 20, surname: &quot;Johnson&quot; },
  { name: &quot;Pete&quot;, age: 18, surname: &quot;Peterson&quot; },
  { name: &quot;Ann&quot;, age: 19, surname: &quot;Hathaway&quot; }
];

function byField(key) {
  return (a,b) =&amp;gt; a[key] &amp;gt; b[key] ? 1 : -1
}

console.log(users.sort(byField(&quot;name&quot;)))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9. 함수를 사용해 군대 만들기&lt;/p&gt;
&lt;pre id=&quot;code_1630989954454&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function makeArmy() {
  let shooters = [];

  let i = 0;
  while (i &amp;lt; 10) {
    let shooter = function() { // shooter 함수
      alert( i ); // 몇 번째 shooter인지 출력해줘야 함
    };
    shooters.push(shooter);
    i++;
  }

  return shooters;
}

let army = makeArmy();

army[0](); // 0번째 shooter가 10을 출력함
army[5](); // 5번째 shooter 역시 10을 출력함
// 모든 shooter가 자신의 번호 대신 10을 출력하고 있음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 둘 다 10이 뜰까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 분석해보면, 반복문에서 사용되는 i 가 while 반복문의 스코프의 외부 렉시컬 환경에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, shooters 라는 빈 배열에는 function() {alert(i)} 가 10개 들어가게 될 텐데, 여기서 i 는 외부 렉시컬에서 선언된 i에 의존한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;i 가 0일 때는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;shooters = [ function () { alert(0) } ] 이 될 것이고, i 가 1일 때는 shooters = [ function () { alert(1) }, function () { alert(1) } ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 같은 값을 alert 하는 함수들이 그 개수에 맞춰 증가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 해결 방법은? 당연히 i 의 값을 반복문의 내부 렉시컬에 있는 값으로 대체해주는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for문으로 바꾸거나, let j = i; 같이 선언한 후 i 대신 j 로 바꿔주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1630990559328&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function makeArmy() {

  let shooters = [];

  for(let i = 0; i &amp;lt; 10; i++) {
    let shooter = function() { // shooter function
      alert( i ); // should show its number
    };
    shooters.push(shooter);
  }

  return shooters;
}

let army = makeArmy();

army[0](); // 0
army[5](); // 5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1630990570861&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function makeArmy() {
  let shooters = [];

  let i = 0;
  while (i &amp;lt; 10) {
    let j = i;
    let shooter = function() { // shooter function
      alert( j ); // should show its number
    };
    shooters.push(shooter);
    i++;
  }

  return shooters;
}

let army = makeArmy();

army[0](); // 0
army[5](); // 5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;마치며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 초심자에게는 상당히 어려운 개념일 것 같지만, 다 용어가 어려워서 그런게 아닐까 생각한다. 나도 이해하는데 좀 힘들었다.. 화이팅!&lt;/p&gt;</description>
      <category>Programming Language/Javascript</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/43</guid>
      <comments>https://simju9397.tistory.com/43#entry43comment</comments>
      <pubDate>Tue, 7 Sep 2021 13:56:58 +0900</pubDate>
    </item>
    <item>
      <title>NestJS : @nestjs/mongoose MongooseModule 세팅 시 Authentication failed Error 해결 방법</title>
      <link>https://simju9397.tistory.com/42</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;하루 종일 개삽질하다가 공유합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거두절미하고,&lt;/p&gt;
&lt;pre id=&quot;code_1628961711549&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MongooseModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) =&amp;gt; ({
        uri: configService.get(&quot;DB_URL&quot;),
        auth: {
          user: configService.get(&quot;DB_USERNAME&quot;),
          password: configService.get(&quot;DB_PASSWORD&quot;),
        },
        authSource : &quot;admin&quot;
      }),
      inject: [ConfigService],
    }),&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 이런식으로 많이들 하실텐데요,&lt;/p&gt;
&lt;pre id=&quot;code_1628961728526&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;authSource : &quot;admin&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 집어넣자마자 바로 해결됐습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이거 때문에 하루종일 몽고 재설치하고, 권한부여하고 난리를 쳤는데 허허,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ps) authSource 가 뭐길래?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 몽고DB의 사용자 정보를 담아놓는 데이터베이스라고 합니다. 평소에 mongodb 를 안쓰다가 썼더니 이런 간단한걸 너무 돌아간 거 같네요.&lt;/p&gt;</description>
      <category>Back-End/NestJS</category>
      <category>nestjs mongodb</category>
      <category>nestjs mongodb authentication error</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/42</guid>
      <comments>https://simju9397.tistory.com/42#entry42comment</comments>
      <pubDate>Sun, 15 Aug 2021 02:24:16 +0900</pubDate>
    </item>
    <item>
      <title>[Typescript] typescript 입문자를 위한 지침서 - ( 1 ) 타입스크립트, 왜 필요한데요?</title>
      <link>https://simju9397.tistory.com/41</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;Typescript 왜 인기에요?&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;870&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YWJQV/btrbyIU65Wp/anB3yAR6S5EKNJp1kvXW40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YWJQV/btrbyIU65Wp/anB3yAR6S5EKNJp1kvXW40/img.png&quot; data-alt=&quot;수요가 많은 프로그래밍 언어 순위&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YWJQV/btrbyIU65Wp/anB3yAR6S5EKNJp1kvXW40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYWJQV%2FbtrbyIU65Wp%2FanB3yAR6S5EKNJp1kvXW40%2Fimg.png&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;870&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;수요가 많은 프로그래밍 언어 순위&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한국과는 상황이 많이 다른 것 같지만, 세계적으로 이렇다고 합니다. Python이 1위인 것도 그렇다치고, Javascript 도 2위인 거 알겠는데, typescript 는 뭐가 어떻길래 Java, Kotlin, C++, SQL 마저 이겨버리는 걸까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트의 &lt;a href=&quot;https://www.typescriptlang.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 홈페이지&lt;/a&gt;의 첫 배너를 보면, 한 줄 설명이 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;타입스크립트는 에러를 잡아내주고, 실행 전에 이를 고칠 수 있게 해줍니다!&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 별 거 아닌 것처럼 보일 수 있습니다. 아주아주 잘 짜여진 자바스크립트 코드 베이스에서 일해보신 분이거나, 큰 규모의 자바스크립트 프로젝트를 해보지 않은 분들은 특히 그렇습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;589&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfVpqH/btrbquKOF7R/YQ0v1BBQvPYJjdy4t6scnK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfVpqH/btrbquKOF7R/YQ0v1BBQvPYJjdy4t6scnK/img.jpg&quot; data-alt=&quot;자바스크립트가 싫어질 때&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfVpqH/btrbquKOF7R/YQ0v1BBQvPYJjdy4t6scnK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfVpqH%2FbtrbquKOF7R%2FYQ0v1BBQvPYJjdy4t6scnK%2Fimg.jpg&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;589&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;자바스크립트가 싫어질 때&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트나 파이썬이 특히 초심자에게 인기가 많은 이유가 뭘까요? 당연히&amp;nbsp;&lt;b&gt;배우기 쉬우니까&amp;nbsp;&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 두 언어가 입문하기 가장 좋은 이유가 바로 type 을 지정해줄 필요가 없는 것이라고 생각합니다. 초심자에게는 for 문도 힘든 역경인데, string 과 char 의 차이까지 알고 가는 것은 힘들잖아요! 포인터는 또 어떻구요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;495&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cExs7P/btrbuInKRSc/SSu7Z6PMkENkQ14IF8XuK1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cExs7P/btrbuInKRSc/SSu7Z6PMkENkQ14IF8XuK1/img.jpg&quot; data-alt=&quot;이런 짤도 있잖아요!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cExs7P/btrbuInKRSc/SSu7Z6PMkENkQ14IF8XuK1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcExs7P%2FbtrbuInKRSc%2FSSu7Z6PMkENkQ14IF8XuK1%2Fimg.jpg&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;495&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이런 짤도 있잖아요!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 우리는 자바스크립트를 쭉 써왔습니다. var 로 변수 선언을 하면서, let 과 const 를 배운 뒤 var 을 멀리하기 시작하며 화살표 함수를 멋지게 쓰고 있었어요. 아무런 문제없이 코드는 잘 굴러갔는데, 코드가 커질 수록 몇 가지 문제가 생깁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1628346402650&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function hello(name) {
	console.log(`안녕! ${name}!`);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도 코드는 괜찮죠! 그런데, 페이팔 등을 통해 결제 모듈을 도입해야 하는 일이 생겼다고 해봅시다.&lt;/p&gt;
&lt;pre id=&quot;code_1628346734960&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function newPayment({ username, paymentLength, payAmount, isYearly, whereToPay, paymentType }) {
  paymentLength.toString();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 정도의 인자들이 필요하다고 합니다. 그럼 저 값들은 어디서 올까요? 당연히 유저가 결제를 원하는 페이지에서 설정한 값들로부터 나올 것입니다. 근데 정말 확신할 수 있나요? 유저가 원하는 페이지에서, select option 의 value 를 가져와서, 나름대로의 가공을 거친 뒤 newPayment() 함수를 실행할 때까지, 모든 값들이 원하는 대로 온다는 확신이 있을까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불행하게도 부분은 그렇지 않습니다. 어딘가 하나는 undefined 가 올 수도 있고, 값이 꼬여버려서 isYearly 처럼 boolean 형이 들어가야 하는 자리에 string 이 들어가버릴 수도 있습니다. 이런 문제가 생기는 이유는 자바스크립트가 &lt;b&gt;동적 타입 언어&amp;nbsp;&lt;/b&gt;이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 또 있습니다. paymentLength 가 number 형으로 올 것으로&amp;nbsp;&lt;b&gt;짐작하고&amp;nbsp;&lt;/b&gt;toString() 을 써보려고 합니다.하지만 위에서 말한 것 처럼 동적 타입 언어이기 때문에, 누구도 paymentLength 가 number 형이라고 확신해주지 않습니다. 자동완성도 되지 않죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 뭐, number 형으로 들어오기만 하면 잘 작동할테니 별 문제 없잖아요? 일단 돌려봅니다. 하지만 불행하게도, undefined.toString() 은 불가능하다는 답변이 돌아오네요.&amp;nbsp;&lt;b&gt;작동 중&lt;/b&gt;에 말이에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 불편함들에서 typescript 가 등장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Typescript 의 핵심 가치는&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;정적인 타입 부여&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;(를 통한) 자동 완성 및 컴파일 에러&lt;/li&gt;
&lt;li&gt;결과적으로&amp;nbsp;&lt;b&gt;생산성 향상&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트는 동적 타입 언어에서 정적 타입 언어로 바꿔 줍니다. 그렇기 때문에, 어느 변수에 있는 값에 대한 불확실성을 해소해줄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 타입이 이미 지정되어 있기 때문에 자동 완성 또한 지원 되며, boolean 값이 string 형 변수에 들어올 수 있는 상황이라면 컴파일 에러를 내뱉어 줍니다. 컴파일 에러!! 즉, 런타임 에러 전에 잡아낼 수 있다는 말입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에, 타입스크립트를 도입하게 되면, 실행하기도 전에 어마어마한 에러를 뿜어냅니다. 이것 자체가 굉장히 지칠 수도 있습니다. 하지만 실무 개발을 해보신 분이라면, 새로운 코드를 짜는 것보다 기존의 코드를 보는 게 훨씬 힘들다는 것을 아실 겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트를 적용하면 코드는 길어지지만, 버그를 찾는 데 힘을 덜 들일 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 타입스크립트로 바꿔본다면 어떻게 될까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1628348069261&quot; class=&quot;ts typescript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface PaymentInfo {
  username: string;
  paymentLength: number;
  payAmount: number;
  isYearly: boolean;
  whereToPay: string;
  paymentType: string;
}

function newPayment({ username, paymentLength, payAmount, isYearly, whereToPay, paymentType } : PaymentInfo) {
  paymentLength.toString();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 얻는 이점들이 있죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 이제 우리는 newPayment 의 인자로 들어오는 object 의 타입을, PaymentInfo 로만 받을 것으로 규정했습니다. 따라서 username 부터 paymentType 까지의 6개의 key 값을 제외한 어떤 것도 들어오지 않을 것을 확신합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 또한 우리는 payment 에는 오로지 number 타입만 들어올 것을 확신할 수 있고, number 형에서 쓸 수 있는 메소드들의 자동 완성 기능 또한 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. payment 가 오로지 number 형만 들어온다는 것이 확실하기 때문에, undefined 가 들어올 가능성이 있다면, 컴파일 단계에서 잡아줌으로써 런타임 에러를 방지할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;재밌게 해봅시다!&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 자바스크립트 코드를 타입스크립트로 꼭 바꿀 필요는 없습니다. 하지만 리팩토링의 기회가 생긴다면 하는 것이 좋다고 생각합니다. 레거시 코드의 복잡성은 집값과 같아서, 시간이 지날수록 끝없이 올라가기 때문입니다. 지금이 가장 빠를 때라는 이야기죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 공부할 때 이게 정말 필요한 지를 항상 되뇌어야 한다고 생각합니다. 타입스크립트가 필요한 것이라고 느꼈기 때문에 공부하고, 이 글을 쓰고 있습니다. 여러분도 함께 배워가면 좋겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming Language/TypeScript</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/41</guid>
      <comments>https://simju9397.tistory.com/41#entry41comment</comments>
      <pubDate>Sun, 8 Aug 2021 00:03:39 +0900</pubDate>
    </item>
    <item>
      <title>Mac zsh permission denied : code 해결법</title>
      <link>https://simju9397.tistory.com/40</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;zsh 에서 code . 을 입력하니 저 에러가 떴었다. 거의 6시간 날렸다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sudo code . 을 입력하니 code commant not found 가 떴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vscode 에서 shell command : install code 를 쓰니까 EACCESS 어쩌고가 떴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sudo chown , chmod 등 별 짓거리 다해봤는데, 바로 해결된 것은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sudo rm -rf /usr/local/bin/code 로 vscode 를 삭제? 한뒤&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;applications 폴더에 있는 vsc 를 실행한 뒤,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;shell command install code 를 다시 해보니까 놀랍게도 됐다. 어이가없네 진짜,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--- 2021.12.1 ---&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 vscode 가 applications 폴더에 없다면 넣어주세요.&lt;/p&gt;</description>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/40</guid>
      <comments>https://simju9397.tistory.com/40#entry40comment</comments>
      <pubDate>Fri, 30 Jul 2021 12:28:04 +0900</pubDate>
    </item>
    <item>
      <title>2021년, 주니어 개발자 상반기 회고 : 취직하다</title>
      <link>https://simju9397.tistory.com/39</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;거의 처음으로, 개발에 관한 글이 아닌 회고를 써보는 것 같네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2021년은 제게 어느정도 본격적인 취준의 해였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;SSAFY 5기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1월에는 삼성청년SW아카데미 5기 활동을 시작했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 재미있기도 하고, 힘들기도 하고, 배운 것도 많았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;706&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQCNgM/btq9NSfqFee/h4IVSwH3aWSLRW1bECaCk0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQCNgM/btq9NSfqFee/h4IVSwH3aWSLRW1bECaCk0/img.jpg&quot; data-alt=&quot;사진이 어디갔는지 없어서 https://won-percent.tistory.com/69 에 있는 동기님의 글에서 가져왔습니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQCNgM/btq9NSfqFee/h4IVSwH3aWSLRW1bECaCk0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQCNgM%2Fbtq9NSfqFee%2Fh4IVSwH3aWSLRW1bECaCk0%2Fimg.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;706&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;사진이 어디갔는지 없어서 https://won-percent.tistory.com/69 에 있는 동기님의 글에서 가져왔습니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;저는 저 후드에 김치국물 튈 때까지 열심히 입었습니다. 지금도 좀 꼬질하지만 편의점 갈 때는 좋습니다. ㅋㅋㅋ&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 전까지 Node.js 기반 백엔드 개발만 해왔던 제게, Java Spring , Vuejs 등 풀스택 겉핥기를 배울 수 있었죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이 경험들도 제겐 굉장히 귀중했지만, 가장 도움이 많이 된 것은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;알고리즘 문제 해결 능력&quot; 향상&lt;/b&gt;이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSAFY 에서는 알고리즘을 1학기에 굉장히 많이 합니다. 많이 하고, 또 심층적으로 가르쳐 주십니다. 어디 가서 돈주고 들으려면 최소 몇백만원 정도의 커리큘럼이라고 생각하는데, 한 달에 100만원씩 오히려 받으면서 배웁니다. 최고죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘 문제 해결 능력이 좀 늘어나고, solved.ac 기준 골드 1 까지 찍으니까 코딩테스트에 조금씩 붙기 시작했어요. SSAFY 입과 전에는 과장 좀 보태서 서류 합격률 100%, 코딩테스트 합격률 0% 였습니다. 많이 절망했었어요. ㅠㅠ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSAFY 경쟁률이 점점 늘어나는 것 같더라구요. 제가 합격할 때도 많이 높았던 걸로 기억합니다. (여러분이 생각하는 그 이상) 이제 6기 활동이 시작했습니다. 만약 6기 분들이 이 글을 보신다면, 본인 의지 여부에 따라 저보다 훨씬 더 많이 배워갈 수 있다는 말 드리고 싶습니다. 화이팅입니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6월. SSAFY 취업박람회&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;갑자기 1월부터 6월까지 뛰는 게 말이 되냐고 하시겠지만, 정말 시공간을 넘었다고 할 만큼 시간이 빨리 갔습니다. SSAFY 는 정말 바쁘게 굴러가거든요. 그동안 저는 중점적으로 알고리즘을 풀었던 거 같습니다. 기존에 하던 프로젝트도 조금씩 하면서 취업을 준비하고 있었죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSAFY 1학기가 끝나고, 약 한달 간의 취업박람회가 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 2학기를 하는 데 고민이 많았어요. 2학기는 3가지의 프로젝트를 완성하는 것을 목표로 하는데, 이미 하던 프로젝트의 완성을 위해 달리고 있었고, 짧은 프로젝트를 여러 개 하는 것 보다 하나의 프로젝트를 출시 및 운영까지 해보는 것이 더 좋다고 판단했습니다. 그래서 2학기 전에 취직을 하고 싶었어요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;취업 박람회 때, SSAFY와 연결되어 있는 수많은 기업들 중에서, 여러 곳을 지원했습니다. 저는 스타트업 문화를 좋아하고 친숙한 사람이라, 전부 스타트업만 넣었어요. (사실 대기업보다 스타트업부터 면접 경험 해보는 게 더 낫겠다라는 생각도 없지 않았죠.ㅎㅎ)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 자기소개서를 너무 날려쓴 걸까요? 딱 한 곳만 면접이 잡혔습니다. 심지어, 제가 준비해온 백엔드 개발 직군이 아닌 프론트엔드 개발 직군이었어요. 왜 프론트를 썼냐면, 백엔드 공고에 Python, Django 가 적혀 있었기 때문입니다. 프론트는 제가 가장 잘하는 Javascript + React 였기도 하고, SSAFY 1학기 최종 프로젝트를 VueJS 프론트엔드 개발자로 참여하면서, 프론트에 관심이 조금 생기기도 했었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1차 면접&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 제가 그 당시에 팀 소개 페이지를 제대로 안 봐서, 첫 면접이 컬쳐 핏 면접인 지도 몰라 기술 면접 준비를 해갔었습니다. ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ (미쳤나봄)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 찾아보니 팀 소개 페이지에 채용 절차가 적혀있더라구요..ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 면접 때 긴장하는 스타일은 아니라서, 즐겁게 대화하다가 끝난 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 프론트엔드 개발 경험이 별로 없는데 프론트엔드로 지원하게 된 계기를 물어보셨는데, 위의 이유들을 바탕으로 잘 이야기 했더니 합격했습니다. ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2차 면접&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차 면접은 대면 + 기술 면접이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저희 회사는 외국인 개발자가 있기도 하고, 글로벌 서비스를 준비하는 스타트업이라 사내 소통을 전부 영어로 합니다. 그래서 설마하고 면접을 갔는데 전부 영어로 면접을 봤습니다. ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히 영어를 아주 못하는 편은 아니라서, 그리고 준비해간 면접 질문에서 많이 나와서 다행히 잘 볼 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 인상 깊었던 건, 디버그 문제를 풀면서 제가 헤매니까 3명의 면접관(이젠 팀원이죠) 분들과 함께 토론하며 풀어낼 수 있었던 경험입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 최종 합격을 했고, 출근 D-1이 어느덧 다가왔네요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;아니 왜 스타트업 갔어요? 신입이면 대기업 가는 게 나을 거 같은데??&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 질문이 많을 것 같고, 저도 이 생각 했습니다. ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 직장이 가장 중요하다는 말을 많이 들었어요. 이직할 때 전 직장 연봉 기준으로 한다느니 뭐니 하는 말이 많죠. 대기업의 복지도 그렇고..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 이유는, 한 살이라도 어릴 때 내가 경험해보고 싶었던 스타트업에서 뭔가 해보자라는 의지가 있었습니다. 제가 올해 20대 후반이거나, 30대 초반이었다면 사실 안 갔을 것 같아요. 그 때는 정말 &quot;직장&quot;의 개념으로 첫 직장을 잡아야하는 때일 것 같거든요. ( 사실 아직 그 나이가 아니라서 잘 모르겠습니다. ㅎㅎ )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 팀원입니다. 제가 면접 볼 당시 3분의 면접관이 팀 전체였어요. 팀 구성원이 3명이었다는 소리죠. ㅋㅋㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 3분이 모두 함께하고 싶은 성격이었고, 면접 자체도 재미있게 했었어요. 이 사람들과 함께 뭔가 큰 변화를 일으켜 보고 싶다라는 생각이 들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째는 믿고 배울 수 있는 시니어 였습니다. 저는 이걸 제일 중요하게 봤어요. 스타트업 아무 데나 갔다가 시니어가 없어서 혼자 다 하고 그랬다는 경험을 많이 봤거든요. 주니어 개발자로써, 믿을 수 있는 시니어의 유무는 정말정말 중요하다는 말을 많이 들었어요. 저는 회사의 대표님이자 실리콘밸리 출신 개발자인 분에게 많이 배울 거라고 들었고, 입사를 결정하게 되었네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;한 번 취직했다고 끝나지 않습니다!&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 첫 단추를 꿰었을 뿐이라고 생각해요. 입사 해보니 마음에 안들어서 때려칠 수도 있다고 생각하고, 서비스가 급성장해서 스톡옵션으로 몇 억을 받을 수도 있다고 생각합니다. 결국 다 제가 하기 나름이 아닐까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;취준 단계에는 사실 좀 우울한 적이 많았어요. 대표적인 게 서류 탈락할 때랑 코딩테스트 탈락할 때... 하지만 취업은 결국 n번의 try 가운데 단 한 번만 성공하면 되는 거 아니겠어요.&lt;/p&gt;
&lt;pre id=&quot;code_1628599387563&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try {
	tryToGetJob();
} catch(fail) {
	levelUp();
} finally {
	success();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 슬퍼하기 보다는 다음을 위한 발판으로 생각하시면 좋은 결과 있을 겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코로나 때문에 다들 너무 힘드실텐데, 화이팅 하시길 바라며 포스팅 마칩니다.&lt;/p&gt;</description>
      <category>SSAFY</category>
      <category>SSAFY 5기 후기</category>
      <category>개발자 취업 후기</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/39</guid>
      <comments>https://simju9397.tistory.com/39#entry39comment</comments>
      <pubDate>Sun, 18 Jul 2021 09:23:30 +0900</pubDate>
    </item>
    <item>
      <title>초보를 위한 쿠버네티스 안내서 (2) : 쿠버네티스 아키텍처</title>
      <link>https://simju9397.tistory.com/38</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;앞서&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 게시글은 subicura 님의 인프런 유료 강의, &quot;초보를 위한 쿠버네티스 안내서&quot;를 듣고 정리하기 위해 작성했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%9E%85%EB%AC%B8/dashboard&quot;&gt;https://www.inflearn.com/course/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%9E%85%EB%AC%B8/dashboard&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1624536055589&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;초보를 위한 쿠버네티스 안내서 - 인프런 | 강의&quot; data-og-description=&quot;쿠버네티스를 1도 모르는 입문자, 초보자를 위한 쿠버네티스 안내서 입니다. 초보자도 이해하기 쉬운 내용과 흐름으로 구성했어요., 데브옵스 입문자라면 주목! 지금, 초보자를 위한 쿠버네티스&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%9E%85%EB%AC%B8/dashboard&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%9E%85%EB%AC%B8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bjyQXH/hyKFoBZsc1/tAIBYEOzhyQmN31BaGrSr1/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/CCRQh/hyKFihs4EW/bjrUoaGZsHvaki0WDCYmbk/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/yU4J0/hyKFp1YnDE/Pc7O1rkEa5LiQZEZl5Ybj0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%9E%85%EB%AC%B8/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%9E%85%EB%AC%B8/dashboard&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bjyQXH/hyKFoBZsc1/tAIBYEOzhyQmN31BaGrSr1/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/CCRQh/hyKFihs4EW/bjrUoaGZsHvaki0WDCYmbk/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/yU4J0/hyKFp1YnDE/Pc7O1rkEa5LiQZEZl5Ybj0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;초보를 위한 쿠버네티스 안내서 - 인프런 | 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;쿠버네티스를 1도 모르는 입문자, 초보자를 위한 쿠버네티스 안내서 입니다. 초보자도 이해하기 쉬운 내용과 흐름으로 구성했어요., 데브옵스 입문자라면 주목! 지금, 초보자를 위한 쿠버네티스&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;아키텍처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람을 시켜 컨테이너를 하나를 띄우려면?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 컨테이너 한개 생성 해달라는 요청을 받음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 그 요청을 어딘가에 적어둠&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 서버의 현재 상태가 방금 적어둔 ( 컨테이너 1개 생성 ) 상태와 같은지 체크&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 컨테이너 하나 있어야 하는데 없네? 만들기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 개념이 굉장히 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Desired State&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Observe (상태 체크) : 현재 상태 == 원하는 상태 인지 체크&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Diff ( 차이점 발견 ) : 현재 상태 != 원하는 상태 이므로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Act ( 조치 ) : 현재 상태 -&amp;gt; 원하는 상태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스는 이 3가지를 반복함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 위의 예시로 돌아가서, 이번에는 서버를 2대로 증설하라는 요청을 받았다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 서버를 새로 하나 띄웠다. 이 때 새로운 컨테이너는 어디에 띄워야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 아무 것도 띄워져 있지 않은 2번 서버에 띄워야 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때, 어느 서버에 띄워줄 지 결정하는 사람을 Scheduler 라 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너나 네트워크, 노드 등을 체크해주는 사람을 Controller 라 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 컨트롤러의 갯수에 따라 Desired State 가 굉장히 많아질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 나눠보면, 쿠버네티스의 영역은 두 가지가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 컨테이너들이 띄워져있는 Node 부분과, Node 를 관리하기 위해 움직이는 Master 영역으로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간에서 Scheduler, Controller 들에게 명령을 해줬던 사람은 API Server 라는 용어로 부르고, API Server 가 명령을 기억해해두기 위해 적어두던 데이터베이스를 etcd 라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;etcd&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;모든 상태와 데이터를 저장&lt;/li&gt;
&lt;li&gt;분산 시스템으로 구성하여 안전성을 높임 (고가용성, 보통 3대 정도 띄움 )&lt;/li&gt;
&lt;li&gt;가볍고 빠르면서 정확하게 설계&lt;/li&gt;
&lt;li&gt;Key(directory) - Value 형태로 데이터 저장&lt;/li&gt;
&lt;li&gt;TTL, watch 같은 부가 기능 제공.&lt;/li&gt;
&lt;li&gt;백업은 필수!&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;API Server&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태를 바꾸거나 조회&lt;/li&gt;
&lt;li&gt;etcd 와 유일하게 통신하는 모듈&lt;/li&gt;
&lt;li&gt;REST API 형태로 제공&lt;/li&gt;
&lt;li&gt;권한을 체크하여 적절한 권한이 없을 경우 요청을 차단&lt;/li&gt;
&lt;li&gt;관리자 요청 뿐만 아니라 다양한 내부 모듈과 통신&lt;/li&gt;
&lt;li&gt;요청이 굉장히 많음. 수평으로 확장되도록 디자인&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Scheduler&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로 생성된 Pod ( 지금은 컨테이너라고 생각하세요)&amp;nbsp; 를 감지하고 실행할 노드를 선택 ( 어떤 노드에 어떤 컨테이너를? )&lt;/li&gt;
&lt;li&gt;노드의 현재 상태와 Pod의 요구사항을 체크&lt;/li&gt;
&lt;li&gt;노드에 라벨을 부여 ( 그래픽이 필요한 경우, gpu-enabled 라벨이 되어있는 노드에 배포한다던가..)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Controller&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;논리적으로 다양한 컨트롤러가 존재 (복제 컨트롤러, 노드 컨트롤러, 엔드포인트 컨트롤러...)&lt;/li&gt;
&lt;li&gt;끊임 없이 상태를 체크하고 Desired State를 유지&lt;/li&gt;
&lt;li&gt;복잡성을 낮추기 위해 하나의 프로세스로 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;조회 흐름&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Controller 는 etcd 와 직접 통신하지 않고 API Server 에게 요청한다.&lt;/li&gt;
&lt;li&gt;API Server는 해당 Controller 가 조회 권한이 있는지 체크&lt;/li&gt;
&lt;li&gt;권한이 있다면 etcd 에 정보 조회함.&lt;/li&gt;
&lt;li&gt;etcd 가 Desired State가 변경되었다고 API Server에 알려줌&lt;/li&gt;
&lt;li&gt;API Server는 Controller 에게 변경되었다고 알려줌&lt;/li&gt;
&lt;li&gt;Controller 는 Desired State 로 리소스 변경&lt;/li&gt;
&lt;li&gt;Contoller 는 변경 후에 API Server 에 변경 사항 전달&lt;/li&gt;
&lt;li&gt;API Server 는 얘가 정보를 변경할 권한이 있는지 체크하고,&lt;/li&gt;
&lt;li&gt;권한이 있다면 etcd 에 정보 갱신을 기록함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Scheduler, Controller 들 모두가 API Server 와의 통신을 하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이제 Node 부분을 살펴보자&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크게 Proxy 와 Kubelet 이라는 컴포넌트가 있다. 마찬가지로 API Server 와 통신함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Proxy 는 iptables, ipvs 방식을 사용하고, Kubelet 이 Pod 와 직접 통신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Kubelet&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 노드에서 실행&lt;/li&gt;
&lt;li&gt;Pod을 실행/중지하고 상태를 체크&lt;/li&gt;
&lt;li&gt;CRI (Container Runtime Interface)&lt;/li&gt;
&lt;li&gt;컨테이너를 직접 쓰는게 아니라 Pod 이라는 것으로 감싸서 사용함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Proxy&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크 프록시 같이 부하 분산 역할&lt;/li&gt;
&lt;li&gt;성능상의 이유로 별도의 프록시 프로그램 대신 iptables 또는 IPVS를 사용 ( 설정 파일만 관리 )&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그럼 용어를 토대로 다시 예제로 돌아가볼까요?&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;관리자가 API Server 에게 Pod 를 띄워달라고 요청함&lt;/li&gt;
&lt;li&gt;API Server 는 etcd 에게 &quot;Pod 을 생성하라는 요청이 들어왔다!&quot; 라고 적어둔다.&lt;/li&gt;
&lt;li&gt;Controller 는 &quot;새 Pod 가 확인 되어야 하나??&quot; 를 계속 API Server 에게 체크 중이다.&lt;/li&gt;
&lt;li&gt;헐, 요청이 들어왔네! Pod 할당 요청을 API Server에게 한다.&lt;/li&gt;
&lt;li&gt;API Server 는 다시 받아서 etcd 의 상태를 &quot;Pod 할당 요청&quot; 으로 바꾼다.&lt;/li&gt;
&lt;li&gt;Scheduler 또한 계속 API Server 에게 할당할 Pod 가 있는지 체크 중이다.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;헐 있네! 새 노드에 할당하겠다고 API Server 에 요청함.&lt;/li&gt;
&lt;li&gt;API Server 는 etcd 에 &quot;새로운 Pod 를 특정 노드에 할당하는데, 아직 미실행 상태다!&quot; 를 기록함.&lt;/li&gt;
&lt;li&gt;Node 단의 Kubelet 은 &quot;어, 내 담당 노드에 할당된 Pod 중에서 아직 미실행 된 게 있네?&quot;를 체크함.&lt;/li&gt;
&lt;li&gt;정보를 가져와서 Pod 생성하고, API Server 에 Pod 생성했다고 알려줌&lt;/li&gt;
&lt;li&gt;API Server 는 etcd 에 &quot;새 Pod를 특정 노드에 할당해서 실행중이다&quot; 를 기록함.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Addons&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CNI (네트워크), DNS(도메인, 서비스 디스커버리), 대시보드(시각화) 등의 컴포넌트가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;오브젝트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 쿠버네티스가 어떻게 내부적으로 동작하는 지 알아봤다. 이제 각 오브젝트를 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Pod&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 작은 배포 단위 ( 컨테이너를 감싼다 )&lt;/li&gt;
&lt;li&gt;각 Pod 마다 고유 IP 를 받는다.&lt;/li&gt;
&lt;li&gt;하나의 Pod 에 여러 개의 컨테이너가 들어갈 수 있다. 각 컨테이너가 localhost 의 폴더를 공유할 수도 있고 포트로 네트워크를 공유할 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ReplicaSet&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 개의 Pod 를 관리&lt;/li&gt;
&lt;li&gt;새로운 Pod 는 Pod Template 을 보고 하나 더 띄워줌.&lt;/li&gt;
&lt;li&gt;원하는 Pod의 수(Replicas) 에 맞춰 Pod 를 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Delpoyment&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배포 버전을 관리&lt;/li&gt;
&lt;li&gt;무중단으로 배포하기 위해서, 순간적으로 ReplicaSet 을 2개로 만들고, 각 replicas 를 한 쪽 컨테이너는 줄이고, 한 쪽 컨테이너는 늘이면서 자연스럽게 update 시킴.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이 외에도 다양한 Workload 가 있다.&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Daemon Set : 모든 Node 에 꼭 하나씩만 떠있길 원하는 Pod 을 만들고 싶을 때(모니터링, 로그)&lt;/li&gt;
&lt;li&gt;Staeful Sets : 순서대로 Pod을 실행하고 싶거나, 같은 볼륨을 재활용하고 싶을 때&lt;/li&gt;
&lt;li&gt;Job : 한 번 실행하고 죽는 Pod&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Service - Cluster IP&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;3개의 Pod 이 있을 때, ClusterIP 로 묶여 있으면 ClusterIP 로 요청을 보내면 3개 중에 하나로 보내서 수행함.&lt;/li&gt;
&lt;li&gt;이렇게 하는 이유는, Pod 가 업데이트될 때 기존 Pod 이 유지되지 않고 죽었다가 새로 만들어진다.&lt;/li&gt;
&lt;li&gt;즉 IP 가 유동적이기 때문에, 고유 IP 를 갖는 Service 의 ClusterIP 로 요청을 보내는 것.&lt;/li&gt;
&lt;li&gt;web 에서 redis 가 있는 Pod 로 요청을 보낸다면, redis 가 속해있는 ClusterIP 로 요청을 보냄.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Service - NodePort&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Cluster IP 는 내부 통신만 가능하다. 외부 브라우저에서는 접근이 안 된다.&lt;/li&gt;
&lt;li&gt;외부에서 접근하기 위해서, Cluster IP 를 NodePort 로 감싸준다. (30000-32767 포트)&lt;/li&gt;
&lt;li&gt;노드01 과 노드02 가 있다면, 노드01 로 직접 요청해서 Pod 에 접근할 수도 있고, 노드02에서 노드01 내부의 ClusterIP 에 요청해서 접근할 수도 있다. 즉 둘 중 어느 노드에 접근해도 같은 Pod 에 접근할 수 있게 되는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Service - LoadBalancer&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드01 로 도메인을 연결해놨는데 노드01이 갑자기 죽어버린다면? 노드02로 붙어야 하는데,,&lt;/li&gt;
&lt;li&gt;직접 연결해뒀다면 접속이 안될 것이다.&lt;/li&gt;
&lt;li&gt;이 때 사용자는 LoadBalancer 에게 요청을 한다. 그 요청이 NodePort 로 가고, ClusterIp 로 가고, Pod 로 가는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Ingress&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도메인 또는 경로 별 라우팅&lt;/li&gt;
&lt;li&gt;Nginx, HAProxy, ALB ...&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;일반적인 구성&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pod 를 직접 띄우는 일은 잘 없다. Deployment 를 띄우면 자동으로 ReplicaSet 을 생성하고, 얘는 Pod 를 생성하고..&lt;/li&gt;
&lt;li&gt;외부 노출을 위해 Service (ClusterIp) 를 붙이고, 얘는 NodePort 에 붙이고, LoadBalancer 에 붙이고..&lt;/li&gt;
&lt;li&gt;Ingress 에 연결해 도메인을 연결하면, 사용자가 www.어디어디 로 들어왔을 때 요청이 간다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그 외 기본 오브젝트&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Volume ( Storage )&lt;/li&gt;
&lt;li&gt;Namespace - 논리적인 리소스 구분&lt;/li&gt;
&lt;li&gt;ConfigMap/Secret - 설정&lt;/li&gt;
&lt;li&gt;ServiceAccount - 권한 계정&lt;/li&gt;
&lt;li&gt;Role/ClusterRole - 권한 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;API 호출&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 상태(Desired State) 를 다양한 오브젝트(Object) 로 정의(Spec) 하고 API 서버에 yaml 형식으로 전달&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/Kubernetes</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/38</guid>
      <comments>https://simju9397.tistory.com/38#entry38comment</comments>
      <pubDate>Thu, 24 Jun 2021 21:53:51 +0900</pubDate>
    </item>
    <item>
      <title>초보를 위한 Kubernetes 안내서 (1) : 소개</title>
      <link>https://simju9397.tistory.com/36</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;앞서&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 게시글은 subicura 님의 인프런 유료 강의, &quot;초보를 위한 쿠버네티스 안내서&quot;를 듣고 정리하기 위해 작성했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%9E%85%EB%AC%B8/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.inflearn.com/course/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%9E%85%EB%AC%B8/dashboard&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1624364922709&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;초보를 위한 쿠버네티스 안내서 - 인프런 | 강의&quot; data-og-description=&quot;쿠버네티스를 1도 모르는 입문자, 초보자를 위한 쿠버네티스 안내서 입니다. 초보자도 이해하기 쉬운 내용과 흐름으로 구성했어요., 데브옵스 입문자라면 주목! 지금, 초보자를 위한 쿠버네티스&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%9E%85%EB%AC%B8/dashboard&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%9E%85%EB%AC%B8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/efvVar/hyKD0OAesJ/2ndGj60lsrb1DY0qke51G0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/eynqo/hyKD3kheni/Kmc4ClMlzYkLkdaHneGLdK/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/P1gmB/hyKFfXFSSP/W29XCAHhPTzkMW2YcABfU0/img.png?width=1358&amp;amp;height=1154&amp;amp;face=0_0_1358_1154&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%9E%85%EB%AC%B8/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%9E%85%EB%AC%B8/dashboard&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/efvVar/hyKD0OAesJ/2ndGj60lsrb1DY0qke51G0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/eynqo/hyKD3kheni/Kmc4ClMlzYkLkdaHneGLdK/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/P1gmB/hyKFfXFSSP/W29XCAHhPTzkMW2YcABfU0/img.png?width=1358&amp;amp;height=1154&amp;amp;face=0_0_1358_1154');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;초보를 위한 쿠버네티스 안내서 - 인프런 | 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;쿠버네티스를 1도 모르는 입문자, 초보자를 위한 쿠버네티스 안내서 입니다. 초보자도 이해하기 쉬운 내용과 흐름으로 구성했어요., 데브옵스 입문자라면 주목! 지금, 초보자를 위한 쿠버네티스&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;서버 관리?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;&lt;b&gt;서버의 상태&lt;/b&gt;를 관리하기 위한 노력 ( 밤에 출근하기 싫다. 갑자기 죽는 서버가 밉다... )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 첫 번째 노력 : 문서화&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;환경이 조금 바뀌면 다 바뀌어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 두 번째 노력 : 서버 관리 도구 (CHEF, puppet, ansible ... )&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;좋기는 한데,,, 서버 관리 도구를 또 배워야 하네.&lt;/li&gt;
&lt;li&gt;서버가 복잡해지면 ? 관리도 너무너무너무 복잡해짐.&lt;/li&gt;
&lt;li&gt;Java 도 써야 하고, Node.js 도 써야 하는데.. 한 서버에서 어떻게 하지?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 세 번째 노력 : 가상 머신&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 하나에 가상 머신 여러 개를 띄워서 쓰기 -&amp;gt; 안정적이고 관리도 잘 됨(느리지만..)&lt;/li&gt;
&lt;li&gt;특정 vendor 에 의존하게 됨. 그리고 느림..&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;1208&quot; width=&quot;200&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k94X6/btq7TK4vlXI/BP9SHftJ0jeKwXeCskyaGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k94X6/btq7TK4vlXI/BP9SHftJ0jeKwXeCskyaGK/img.png&quot; data-alt=&quot;그리고 이 때, docker 가 등장하게 되는데..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k94X6/btq7TK4vlXI/BP9SHftJ0jeKwXeCskyaGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk94X6%2Fbtq7TK4vlXI%2FBP9SHftJ0jeKwXeCskyaGK%2Fimg.png&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;1208&quot; width=&quot;200&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그리고 이 때, docker 가 등장하게 되는데..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 실행 환경을 container 로 바꾸게 됨.&lt;/li&gt;
&lt;li&gt;도커만 설치 되어 있으면 어디서든 동작하고, 쉽고, 느리지도 않다. (최고!)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;컨테이너의 특징&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가상머신에 비해 생성이 쉽고 효율적&lt;/li&gt;
&lt;li&gt;컨테이너 이미지를 이용한 배포와 롤백이 간단&lt;/li&gt;
&lt;li&gt;언어나 프레임워크 상관 없이, 동일한 방식으로 애플리케이션 관리&lt;/li&gt;
&lt;li&gt;개발, 테스팅, 운영 환경은 물론 로컬 PC와 클라우드까지 동일한 환경을 구축&lt;/li&gt;
&lt;li&gt;오픈 소스이고, 특정 클라우드 vendor 에 종속적이지 않음.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;MySQL, redis, Jenkins, ... 모든 것을 컨테이너로 관리하고 싶은 욕구가 생긴다. =&amp;gt; Containerization&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;도커의 등장 이후,&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;개발자가 코드 작성&lt;/li&gt;
&lt;li&gt;빌드&lt;/li&gt;
&lt;li&gt;컨테이너화&lt;/li&gt;
&lt;li&gt;실행!&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의 과정으로 개발이 이뤄지게 되었다. 드디어 편해졌나?&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;모든 걸 컨테이너화 하긴 했는데...&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너가 점점 많아지면..?&lt;/li&gt;
&lt;li&gt;4개일 때는 괜찮았는데, 100개, 1000개, Integer.MAX_VALUE 개가 된다면..? 어떻게 관리함???&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;도커 그 이후&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;배포는 어떻게?&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도커 서버가 많아지면 ? 하나하나 실행하기가...&lt;/li&gt;
&lt;li&gt;컨테이너가 실행되지 않는 서버도 생긴다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그럼 당연히 비어있는 서버에 실행하는 게 좋은데,&lt;/li&gt;
&lt;li&gt;어떤 게 비어있지??&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;v1 -&amp;gt; v2 로 업데이트를 힘들게 서버 하나하나 접속해가며 했는데, 오류가 나서 롤백을 해야 한다&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;서비스 검색은 어떻게?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프록시가 있고, 컨테이너를 연결해줄 때, 컨테이너가 늘어나면 ?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생각할 수 있는 것 : 로드밸런서 붙이기&lt;/li&gt;
&lt;li&gt;마이크로서비스가 유행하면서, 점점 컨테이너가 많아지는 데, 이럴 때 마다 도커 서버 주소를 추가해주고, 어떤 건 삭제하고... 일련의 과정이 너무 힘들다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;서비스 노출은?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 방식&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;public 영역에 nginx 같은 프록시 서버를 두고, 들어오는 host 에 따라서 private 영역의 내부 서버에 연결하는 방식&lt;/li&gt;
&lt;li&gt;마찬가지로, 서버 하나 추가되면 nginx conf 파일 들어가서 추가해주고.. 귀찮다&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;모니터링은??&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 서버가 죽어버리거나, 느려져버리면.. 그게 하필 새벽이라면..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;375&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk0Kse/btq7WuMHA4j/fypkNzb9UerGWmGy9FFPZ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk0Kse/btq7WuMHA4j/fypkNzb9UerGWmGy9FFPZ1/img.jpg&quot; data-alt=&quot;명절을 앞둔 서버 개발자의 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk0Kse/btq7WuMHA4j/fypkNzb9UerGWmGy9FFPZ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk0Kse%2Fbtq7WuMHA4j%2FfypkNzb9UerGWmGy9FFPZ1%2Fimg.jpg&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;375&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;명절을 앞둔 서버 개발자의 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;복잡한 컨테이너 환경&lt;/b&gt;을&lt;b&gt; 효과적으로 관리&lt;/b&gt;하기 위한 도구가 필요해!&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Container Orchestration 의 등장&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 관리자의 일을 대신 해주는 도구&lt;/li&gt;
&lt;li&gt;특징&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Cluster
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중앙제어(Cluster)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;node 1번에는 뭐가 있고, 얼마의 리소스가 있고, 2번에는...&lt;/li&gt;
&lt;li&gt;이게 많아지면 ? 사람이 알고 있기에는 한계가 있다.&lt;/li&gt;
&lt;li&gt;이 많은 node 들을 cluster 라는 단위로 묶어서 관리한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;상태관리(State)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;app1 이라는 이미지를 replicas 2로 설정하면 2개 뜨게, 3으로 설정하면 3개 뜨게 함.&lt;/li&gt;
&lt;li&gt;3개인 상황에서 하나가 문제가 생기면, 죽이고 하나를 새로 띄워주는 것&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;배포관리(Scheduling)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어느 서버가 여유있는 지 찾아서, 새로운 앱을 띄울 때 그 서버에 띄워주거나, 혹은 여유 있는 서버가 없으면 서버 하나 새로 띄워주는 것&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;배포 버전관리(Rollout-Rollback)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;{ version : &quot;v2&quot; } 로 하면 v2 로 모두 바뀌고, v1 으로 하면 모두 바뀌게 중앙에서 처리해주는 것&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;서비스 등록 및 조회(Service Discovery)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버가 x.x.x.100 에 뜨면 master node 에 등록되고, 101에 하나 더 뜨면 또 등록됨.&lt;/li&gt;
&lt;li&gt;설정 변경 시 프로세스 재시작을 자동으로 해줌.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;볼륨 스토리지(Volume)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1번 노드는 NFS 에 연결, 2번 노드는 AWS EBS 에 연결, 3번 노드는 ...&lt;/li&gt;
&lt;li&gt;손으로 일일히 할 수 있지만, 설정으로 관리해줄 수 있음.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Container Orchestration 은 하나의 개념이다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 게 생겼고, docker 에서 만든 docker swarm 이란 것도 있다. 하지만 kubernetes 가 암묵적인 표준이 되어버렸다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;왜 kubernetes 가??&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&lt;b&gt; 1주일에 20억개의 컨테이너&lt;/b&gt;를 생성하는 google&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 컨테이너 배포 시스템으로 사용하던 borg 를 기반으로 만듦.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;b&gt;유연&lt;/b&gt;하고, &lt;b&gt;어디서든 동작&lt;/b&gt;하며, &lt;b&gt;오픈소스&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 무한한 확장성 : 머신러닝, CI/CD, 서비스메시, 서버리스 등... k8s 가 하나의 플랫폼이 되어버렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 도커도 쿠버네티스 지원하고, 아마존, azure, google 도 다 지원하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사실상의 표준(de facto)&lt;/b&gt; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;마치며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- subicura 님의 강의력이 너무 좋더라구요. 그래도 제 머리가 미천하여 한 번 듣고는 기억을 못할 것 같아서 작성하게 되었습니다. 혹시 문제가 된다면 삭제하겠습니다.&lt;/p&gt;</description>
      <category>DevOps/Kubernetes</category>
      <category>container</category>
      <category>Container orchestration</category>
      <category>container orchestrations이란</category>
      <category>Kubernetes</category>
      <category>kubernetes 개념</category>
      <category>kubernetes란</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/36</guid>
      <comments>https://simju9397.tistory.com/36#entry36comment</comments>
      <pubDate>Tue, 22 Jun 2021 23:13:33 +0900</pubDate>
    </item>
    <item>
      <title>백준 1504 번 :  특정한 최단 경로 풀이(Java)</title>
      <link>https://simju9397.tistory.com/35</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1504&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/1504&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1623153711514&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;1504번: 특정한 최단 경로&quot; data-og-description=&quot;첫째 줄에 정점의 개수 N과 간선의 개수 E가 주어진다. (2 &amp;le; N &amp;le; 800, 0 &amp;le; E &amp;le; 200,000) 둘째 줄부터 E개의 줄에 걸쳐서 세 개의 정수 a, b, c가 주어지는데, a번 정점에서 b번 정점까지 양방향 길이 존&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1504&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1504&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uLMXo/hyKun4FJEw/Kfkoxew1C1ay2FPfH4vkmK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1504&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1504&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uLMXo/hyKun4FJEw/Kfkoxew1C1ay2FPfH4vkmK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;1504번: 특정한 최단 경로&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 정점의 개수 N과 간선의 개수 E가 주어진다. (2 &amp;le; N &amp;le; 800, 0 &amp;le; E &amp;le; 200,000) 둘째 줄부터 E개의 줄에 걸쳐서 세 개의 정수 a, b, c가 주어지는데, a번 정점에서 b번 정점까지 양방향 길이 존&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;169&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caW9OA/btq6N2ypEsY/KdeIxuV5NrhovLYKg7g6Wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caW9OA/btq6N2ypEsY/KdeIxuV5NrhovLYKg7g6Wk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caW9OA/btq6N2ypEsY/KdeIxuV5NrhovLYKg7g6Wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaW9OA%2Fbtq6N2ypEsY%2FKdeIxuV5NrhovLYKg7g6Wk%2Fimg.png&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;169&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 다익스트라 알고리즘의 응용 문제이다. 이 문제에서 주의할 점은, 다익스트라가 다 그렇겠지만 MAX 값 끼리의 더함, 즉 연결되지 않는 경로의 합 등에 의해 발생하는 오버플로우가 있고,&amp;nbsp;&lt;b&gt;방향성이 없는 그래프&lt;/b&gt; 라는 점 또한 주의해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 오버플로우의 경우, 다음 TC를 시도해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3 0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2 3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간선이 없을 수 있는데, 2번 3번을 지나야 한다고 하면 오버플로우에 의해 음수가 나올 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 방향성이 없는 그래프이다. 즉 양방향 그래프라는 말이고, graph 를 만들 때 from 에도 to 에도 추가를 해줘서 양방향으로 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1623153886224&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    private static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    private static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    private static int N,E;
    private static List&amp;lt;List&amp;lt;Node&amp;gt;&amp;gt; graph;
    private static final int MAX = 987654321;
    private static int[] mustGo;

    private static class Node implements Comparable&amp;lt;Node&amp;gt; {
        int idx;
        int distance;

        public Node(int idx, int distance) {
            this.idx = idx;
            this.distance = distance;
        }

        @Override
        public int compareTo(Node o) {
            return this.distance - o.distance;
        }
    }
    public static void main(String[] args) throws IOException {
        input();
        solve();
        bufferedReader.close();
        bufferedWriter.flush();
        bufferedWriter.close();
    }

    private static void input() throws IOException {
        StringTokenizer ne = new StringTokenizer(bufferedReader.readLine());
        N = Integer.parseInt(ne.nextToken());
        E = Integer.parseInt(ne.nextToken());
        graph = new ArrayList&amp;lt;&amp;gt;();
        for(int i = 0 ; i &amp;lt;= N; i++) {
            graph.add(new ArrayList&amp;lt;&amp;gt;());
        }
        for(int i = 0 ; i &amp;lt; E; i++) {
            StringTokenizer edge = new StringTokenizer(bufferedReader.readLine());
            int from = Integer.parseInt(edge.nextToken());
            int to = Integer.parseInt(edge.nextToken());
            int cost = Integer.parseInt(edge.nextToken());
            graph.get(from).add(new Node(to, cost));
            graph.get(to).add(new Node(from, cost));
        }
        StringTokenizer mg = new StringTokenizer(bufferedReader.readLine());
        mustGo = new int[2];
        mustGo[0] = Integer.parseInt(mg.nextToken());
        mustGo[1] = Integer.parseInt(mg.nextToken());
    }

    private static int dijkstra(int start, int end) {
        int[] distance = new int[N+1];
        Arrays.fill(distance, MAX);
        boolean[] visited = new boolean[N+1];
        PriorityQueue&amp;lt;Node&amp;gt; pq = new PriorityQueue&amp;lt;&amp;gt;();
        pq.offer(new Node(start, 0));
        distance[start] = 0;

        while (!pq.isEmpty()) {
            int idx = pq.poll().idx;
            if(visited[idx]) continue;
            visited[idx] = true;

            for(Node node : graph.get(idx)) {
                if(distance[node.idx] &amp;gt; distance[idx] + node.distance) {
                    distance[node.idx] = distance[idx] + node.distance;
                    pq.add(new Node(node.idx, distance[node.idx]));
                }
            }
        }
        return distance[end];
    }

    private static void solve() throws IOException {
        int d1 = dijkstra(1, mustGo[0]) + dijkstra(mustGo[0], mustGo[1]) + dijkstra(mustGo[1], N);
        int d2 = dijkstra(1, mustGo[1]) + dijkstra(mustGo[1], mustGo[0]) + dijkstra(mustGo[0], N);
        //System.out.println(d1 + &quot; &quot; + d2);
        int result = Math.min(d1, d2);
        result = result &amp;gt;= MAX || result &amp;lt; 0 ? -1 : result;
        bufferedWriter.write(result + &quot;\n&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/BOJ</category>
      <category>1504 java 풀이</category>
      <category>java 1504</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/35</guid>
      <comments>https://simju9397.tistory.com/35#entry35comment</comments>
      <pubDate>Tue, 8 Jun 2021 21:05:03 +0900</pubDate>
    </item>
    <item>
      <title>백준 16234번 : 인구 이동 풀이(Java)</title>
      <link>https://simju9397.tistory.com/34</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/16234&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/16234&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1621262040074&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;16234번: 인구 이동&quot; data-og-description=&quot;N&amp;times;N크기의 땅이 있고, 땅은 1&amp;times;1개의 칸으로 나누어져 있다. 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고 있다. 인접한 나라 사이에는 국경선이 존재한다. 모&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/16234&quot; data-og-url=&quot;https://www.acmicpc.net/problem/16234&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hd20Q/hyKfdfJApZ/HCl74GRZNY0oDIW4vXqE6k/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/FPePe/hyKe2Ff8rU/6vKHj46Ha1fIstQtIJA6fK/img.png?width=895&amp;amp;height=895&amp;amp;face=0_0_895_895,https://scrap.kakaocdn.net/dn/bHyK8o/hyKe0HrUmy/IuOu2lh7FWsdCuDBkN0xw0/img.png?width=895&amp;amp;height=895&amp;amp;face=0_0_895_895&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/16234&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/16234&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hd20Q/hyKfdfJApZ/HCl74GRZNY0oDIW4vXqE6k/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/FPePe/hyKe2Ff8rU/6vKHj46Ha1fIstQtIJA6fK/img.png?width=895&amp;amp;height=895&amp;amp;face=0_0_895_895,https://scrap.kakaocdn.net/dn/bHyK8o/hyKe0HrUmy/IuOu2lh7FWsdCuDBkN0xw0/img.png?width=895&amp;amp;height=895&amp;amp;face=0_0_895_895');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;16234번: 인구 이동&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;N&amp;times;N크기의 땅이 있고, 땅은 1&amp;times;1개의 칸으로 나누어져 있다. 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고 있다. 인접한 나라 사이에는 국경선이 존재한다. 모&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1443&quot; data-origin-height=&quot;141&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ak1kT/btq47wVlv0Y/9iaKV2AK6Jax6Qri59s3sK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ak1kT/btq47wVlv0Y/9iaKV2AK6Jax6Qri59s3sK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ak1kT/btq47wVlv0Y/9iaKV2AK6Jax6Qri59s3sK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAk1kT%2Fbtq47wVlv0Y%2F9iaKV2AK6Jax6Qri59s3sK%2Fimg.png&quot; data-origin-width=&quot;1443&quot; data-origin-height=&quot;141&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 이 문제는 BFS + 시뮬레이션 문제이다. 국경선을 여는 것도, 인구를 이동시키는 것도 모두 BFS로 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 먼저, main 역할을 하는 메서드에서는, 더 이상 열리는 국경선이 없을 때 까지 국경선 열기 -&amp;gt; 인구 이동하기 -&amp;gt; 국경선 열기 -&amp;gt; ... 를 반복해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 그럼 결국 구현해야 할 것은&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;국경선을 열기(즉, 가능한 국가들 끼리 서로 연결하는 것)&lt;/li&gt;
&lt;li&gt;인구 이동하기(연결된 국가들의 인구수를 합치고, 총 인구 수 / 국가 수 로 업데이트 해주기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 국경선을 여는 기능(그룹 기능)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 그룹을 나타내는 int[N][N] 배열을 만들어준다. 이름은 connected 로 했다. 또한 연합이 되는 국가가 하나도 없다면 main 메서드에서 반복문을 끝내기 위해서, atLeastOneOpened 라는 boolean 변수를 만들었다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;N*N 의 map을 돌면서, 아직 그룹에 포함되지 않은 나라라면, 그 나라부터 BFS 를 돌면서 &quot;연합&quot;을 만든다. 1번 연합, 2번 연합... 으로 이어질 것이다.&lt;/li&gt;
&lt;li&gt;그럼 connected[r][c] 의 값이 0인 곳이, 연합에 포함되지 않은 나라이다. 그 곳부터 BFS 를 돌면 된다!&lt;/li&gt;
&lt;li&gt;BFS 를 돌면서 연합에 넣기 전에, 둘의 인구 수 차이가 L이상 R이하라면 연합에 넣어준다. 연합에 넣는다는 의미는, connected[nextR][nextC] 의 값을 connected[r][c] 의 값으로 바꿔주면 된다는 것이다. 이 때, atLeastOneOpened 를 true 로 만들어 줘서 반복문이 종료되지 않고 인구 이동이 일어나게 해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 인구 이동 기능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;connected 배열을 참고해서, 마찬가지로 N*N 배열을 돌며 연합인 국가들의 인구 수를 모두 합해준다. BFS가 끝난다면 해당 그룹의 모든 국가들의 인구수를 다 더해줬다는 의미이므로, 이제 map 의 인구 수를 갱신해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 주의할 점이 있다(시간 초과)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 값이 작길래 별 생각 없이, 각 나라가 해당 연합의 나라인지 나타내는 boolean 2차원 배열을 만들고 N*N 돌면서 연합일 경우 총 인구 수 / 국가 수 로 갱신해줬는데, 이렇게 하면 O(N^4) 의 시간 복잡도가 나오므로 80% 쯤에서 터졌었다. 그래서 생각한 방법은, BFS 를 돌면서 해당 연합인 경우 ArrayList 에 넣어서 기록을 해주고, BFS 가 끝날 때 ArrayList 를 돌면서 갱신해줬고 통과했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Source Code&lt;/h3&gt;
&lt;pre id=&quot;code_1621262946007&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {
    private static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    private static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    private static int N,L,R,RL;
    private static final int UP=0,RIGHT=2,DOWN=1,LEFT=3;
    private static int[][] map;
    private static int[][] connected;
    private static int[] dx = {-1,1,0,0};
    private static int[] dy = {0,0,1,-1};

    private static class Node {
        int r;
        int c;
        int group;

        public Node(int r, int c, int group) {
            this.r = r;
            this.c = c;
            this.group = group;
        }
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
        bufferedReader.close();
        bufferedWriter.close();
    }
    
    private static void input() throws IOException {
        StringTokenizer nlr = new StringTokenizer(bufferedReader.readLine());
        N = Integer.parseInt(nlr.nextToken());
        L = Integer.parseInt(nlr.nextToken());
        R = Integer.parseInt(nlr.nextToken());
        RL = R-L;
        map = new int[N][N];
        for(int i = 0 ; i &amp;lt; N; i++) {
            StringTokenizer line = new StringTokenizer(bufferedReader.readLine());
            for(int j = 0 ; j &amp;lt; N; j++) {
                map[i][j] = Integer.parseInt(line.nextToken());
            }
        }
    }

    private static boolean openLine() {
        connected = new int[N][N];
        int group = 1;
        boolean atLeastOneOpened = false;
        for(int r = 0; r &amp;lt; N; r++) {
            for(int c = 0; c &amp;lt; N; c++) {
                if(connected[r][c] != 0) continue; // 이미 연결된 애들은 넘어간다.
                Queue&amp;lt;Node&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;();
                queue.add(new Node(r, c, group));
                connected[r][c] = group++;
                while (!queue.isEmpty()) {
                    Node before = queue.poll();
                    int sr = before.r;
                    int sc = before.c;
                    int sg = before.group;
                    for(int d = 0; d &amp;lt; 4; d++) {
                        int nr = sr + dx[d];
                        int nc = sc + dy[d];
                        if(nr &amp;lt; 0 || nc &amp;lt; 0 || nr &amp;gt;= N || nc &amp;gt;= N) continue;
                        if(connected[nr][nc] != 0) continue;
                        int next = map[nr][nc];
                        int howManyDiff = Math.abs(map[sr][sc] - next);
                        if(howManyDiff &amp;gt;= L &amp;amp;&amp;amp; howManyDiff &amp;lt;= R) {
                            connected[nr][nc] = connected[sr][sc];
                            atLeastOneOpened = true;
                            queue.add(new Node(nr, nc, sg));
                        }
                    }
                }
            }
        }
        return atLeastOneOpened;
    }
    private static void checkLine() {
        for(int r = 0 ; r &amp;lt; N; r++) {
            for(int c = 0 ; c &amp;lt; N; c++) {
                System.out.print(connected[r][c] + &quot; &quot;);
            }
            System.out.println();
        }
        System.out.println(&quot;===========&quot;);
    }

    private static void printMap() {
        for(int r = 0 ; r &amp;lt; N; r++) {
            for(int c = 0 ; c &amp;lt; N; c++) {
                System.out.print(map[r][c] + &quot; &quot;);
            }
            System.out.println();
        }
        System.out.println(&quot;===========&quot;);
    }

    private static void move() {
        boolean[][] visited = new boolean[N][N];
        for(int r = 0 ; r &amp;lt; N; r++) {
            for(int c = 0 ; c &amp;lt; N; c++) {
                if(visited[r][c]) continue; // 이미 처리한 곳이라면 넘어가기
                ArrayList&amp;lt;Node&amp;gt; inGroup = new ArrayList&amp;lt;&amp;gt;(); // 해당 그룹에 있는 노드들을 담아두기
                int populationSum = map[r][c]; // 해당 그룹의 총 인구 수
                Queue&amp;lt;Node&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;();
                queue.add(new Node(r, c, connected[r][c]));
                inGroup.add(new Node(r, c, connected[r][c]));
                while (!queue.isEmpty()) {
                    Node before = queue.poll();
                    int sr = before.r;
                    int sc = before.c;
                    visited[sr][sc] = true;
                    int sg = before.group;
                    for(int d = 0; d &amp;lt; 4; d++) {
                        int nr = sr + dx[d];
                        int nc = sc + dy[d];
                        if(nr &amp;lt; 0 || nc &amp;lt; 0 || nr &amp;gt;= N || nc &amp;gt;= N) continue;
                        if(visited[nr][nc]) continue;
                        if(connected[nr][nc] == sg) {
                            visited[nr][nc] = true;
                            populationSum += map[nr][nc];
                            queue.add(new Node(nr,nc,sg));
                            inGroup.add(new Node(nr, nc, sg));
                        }
                    }
                }
                for(Node x : inGroup) {
                    map[x.r][x.c] = populationSum / inGroup.size();
                }
            }
        }
    }

    private static void solve() {
        int count = 0;
        while (true) {
            if(!openLine()) {
                break;
            }
            //printMap();
            //checkLine();
            move();
            //printMap();
            count++;
        }
        System.out.println(count);
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>16234 java</category>
      <category>16234 풀이</category>
      <category>백준 16234 java</category>
      <category>인구이동 java</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/34</guid>
      <comments>https://simju9397.tistory.com/34#entry34comment</comments>
      <pubDate>Mon, 17 May 2021 23:49:36 +0900</pubDate>
    </item>
    <item>
      <title>Spring : @Autowired 했는데 null 이 나와서 NullPointerException 이 뜨는 경우</title>
      <link>https://simju9397.tistory.com/33</link>
      <description>&lt;p&gt;@Autowired 는 Spring Container 에서 관리 중인 Bean 을, 자료형이 같을 경우 자동 매핑해주는 어노테이션이다. 예제를 통해 알아보자.&lt;/p&gt;
&lt;p&gt;예제는 ProductController 와 ProductService 로 구성되며,&lt;/p&gt;
&lt;p&gt;&amp;nbsp;ProductService 는 Interface 이고, ProductServiceImpl 내에 구현해놓았다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;ProductServiceImpl.java&lt;/p&gt;
&lt;pre id=&quot;code_1619707756227&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
public class ProductServiceImpl implements ProductService{
	@Autowired
	private SqlSession sqlSession;
    
    ...
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;주의해야 할 점은,&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. 생성자를 사용하면 안 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1619707599993&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller
public class ProductController {
	@Autowired
	private ProductService productService = new ProductServiceImpl();
    
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Controller 에서 생성자로 초기화해주면, 이는 Spring Container 에서 Singleton 으로 생성된 인스턴스를 참조하지 않고, 말 그대로 생성자로 인해 생성된 새로운 ProductServiceImpl 객체의 인스턴스를 참조한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 경우 당연히 Null 은 뜨지 않겠지만, Spring Container 의 Bean 관리를 받는 것이 아니므로 DI 에 실패한 코드이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. (내가 해결한 것) static 키워드를 사용하면 안 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1619707808688&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller
public class ProductController {
	@Autowired
	private static ProductService productService; // 이 경우, 자동으로 생성자가 실행되지 않기 때문에 null 이다.
    
    ...
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;정적 멤버로 선언될 경우, ProductController 가 먼저 로드되고, ProductService 는 그 시점에서 생성되지 않기 때문이다.&lt;/p&gt;
&lt;p&gt;잘 모르겠다면 static autowired 로 검색해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Back-End/Spring</category>
      <category>@Autowired NullPointerException</category>
      <category>spring</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/33</guid>
      <comments>https://simju9397.tistory.com/33#entry33comment</comments>
      <pubDate>Thu, 29 Apr 2021 23:54:05 +0900</pubDate>
    </item>
    <item>
      <title>티스토리 사진이 아예 안뜨거나 관리자 페이지 그래프 안보이거나 하는 경우 해결법</title>
      <link>https://simju9397.tistory.com/32</link>
      <description>&lt;p&gt;&lt;b&gt;길호넷의 시크릿DNS가 깔려있다면 실행을 중지하거나 삭제해보세요. 아예 못들어오는 분 있을까봐 짧게씁니다&lt;/b&gt;&lt;/p&gt;</description>
      <category>티스토리 느림</category>
      <category>티스토리 로딩</category>
      <category>티스토리 사진 로딩</category>
      <category>티스토리 안됨</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/32</guid>
      <comments>https://simju9397.tistory.com/32#entry32comment</comments>
      <pubDate>Mon, 26 Apr 2021 20:12:20 +0900</pubDate>
    </item>
    <item>
      <title>백준 15683번 : 감시 풀이(Java) - DFS</title>
      <link>https://simju9397.tistory.com/31</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15683&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/15683&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1619011482935&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;15683번: 감시&quot; data-og-description=&quot;스타트링크의 사무실은 1&amp;times;1크기의 정사각형으로 나누어져 있는 N&amp;times;M 크기의 직사각형으로 나타낼 수 있다. 사무실에는 총 K개의 CCTV가 설치되어져 있는데, CCTV는&amp;nbsp;5가지 종류가 있다. 각 CCTV가 감&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/15683&quot; data-og-url=&quot;https://www.acmicpc.net/problem/15683&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hlKlJ/hyJXS9Yx9e/dKwVqTZd2l8hp19Kp7aeW0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cT7Dn6/hyJX1eLPPT/wSBrvwrr86ENhNmWNvn2H1/img.png?width=990&amp;amp;height=997&amp;amp;face=0_0_990_997,https://scrap.kakaocdn.net/dn/bmtZ3s/hyJWwgnp1I/NALW7Lz4qUace1kCMNKui1/img.png?width=990&amp;amp;height=718&amp;amp;face=0_0_990_718&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15683&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/15683&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hlKlJ/hyJXS9Yx9e/dKwVqTZd2l8hp19Kp7aeW0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cT7Dn6/hyJX1eLPPT/wSBrvwrr86ENhNmWNvn2H1/img.png?width=990&amp;amp;height=997&amp;amp;face=0_0_990_997,https://scrap.kakaocdn.net/dn/bmtZ3s/hyJWwgnp1I/NALW7Lz4qUace1kCMNKui1/img.png?width=990&amp;amp;height=718&amp;amp;face=0_0_990_718');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;15683번: 감시&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;스타트링크의 사무실은 1&amp;times;1크기의 정사각형으로 나누어져 있는 N&amp;times;M 크기의 직사각형으로 나타낼 수 있다. 사무실에는 총 K개의 CCTV가 설치되어져 있는데, CCTV는&amp;nbsp;5가지 종류가 있다. 각 CCTV가 감&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8Fak8/btq3daetZpL/0YD0vvCImqZ3XtonKolrJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8Fak8/btq3daetZpL/0YD0vvCImqZ3XtonKolrJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8Fak8/btq3daetZpL/0YD0vvCImqZ3XtonKolrJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8Fak8%2Fbtq3daetZpL%2F0YD0vvCImqZ3XtonKolrJ1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;잘 안풀어본 유형이라 어려웠다. 아직 DFS 가 BFS에 비해 익숙하지 않은 것 같다. 교수님의 풀이 코드를 보고 실행 부분을 고쳤더니 해결이 되었다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 문제는 데이터의 범위가 굉장히 작기 때문에, 모든 cctv의 회전을 고려해도 된다. (브루트포스)&lt;/p&gt;
&lt;p&gt;하지만 모든 cctv의 회전을 고려하는 상황을 만들기에 적합한 알고리즘이 DFS 였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사각지대에서 벗어난 빈 지역, 즉 CCTV로 관찰 가능한 0번의 경우를 9번으로 만들어주고, 모든 CCTV 의 경우를 다 보고난 뒤 남은 0의 갯수가 사각지대의 갯수이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;나의 경우, 타입 5번, 상하좌우를 모두 보는 CCTV는 회전을 해봐야 결과가 같으므로, 입력을 받을 때 5번의 상하좌우를 9로 만들고 시작했다. 이제 카메라의 움직임을 주시해야 한다. 나는 switch 문을 쓰기 보다 3차원 배열로 한 방에 했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;각 차원을 자세히 보면&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;타입의 갯수가 4가지( 5번 제외, 1~4 이므로 4 + 1 크기의 1차원)&lt;/li&gt;
&lt;li&gt;각 타입별로, 회전하지 않았을 때, 회전 1번, 2번, 3번 즉 볼 수 있는 방향의 경우의 수가 4가지(4 크기의 2차원)&lt;/li&gt;
&lt;li&gt;1번 타입의 경우는 한 번에 한 방향, 2번 3번의 경우는 두 방향, 4번의 경우는 3방향 (각각 1, 2, 3 크기의 3차원)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이다.&lt;/p&gt;
&lt;p&gt;상하좌우를 0,1,2,3 등으로 표현하면 반드시 헷갈릴 것 같아서 상수로 보기 좋게 표현해줬다. 사실 숫자에 크게 의미는 없다. 비교를 위한 것.&lt;/p&gt;
&lt;pre id=&quot;code_1619012041161&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static final int UP = 0, DOWN = 1, LEFT = 2, RIGHT = 3, WALL = 6, CAN_SEE = 9;
private static int[][][] d = {{}, // 타입 0번은 없다
{{RIGHT},{DOWN},{LEFT},{UP}},  // 타입 1번
{{LEFT, RIGHT}, {UP, DOWN}, {LEFT, RIGHT}, {UP,DOWN}}, //2번
{{UP,RIGHT}, {RIGHT, DOWN}, {DOWN, LEFT}, {LEFT, UP}}, //3번 
{{UP,LEFT,RIGHT},{ UP,RIGHT,DOWN},{RIGHT,DOWN,LEFT},{DOWN,LEFT,UP}}}; // 4번&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음은 이렇게 만든 d를 기반으로, cctv의 위치를 기반으로 UP,DOWN,LEFT, RIGHT 각각에 따라 지역을 감시하는 메서드를 만들어준다.&lt;/p&gt;
&lt;pre id=&quot;code_1619012166629&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static void show(int[][] map, int cameraX, int cameraY, int showDir) {
        if(showDir == UP) {
            for(int x = cameraX; x &amp;gt;= 0; x--) {
                if(map[x][cameraY] == WALL) break; // 벽을 마주치면 더 볼 수 없다.
                if(map[x][cameraY] == 0 || map[x][cameraY] == CAN_SEE) {
                    // 관찰 가능한 0번이거나, 이미 관찰한 9번이라면
                    map[x][cameraY] = CAN_SEE;
                }
            }
        } else if(showDir == DOWN) {
            for(int x = cameraX; x &amp;lt; N; x++) {
                if(map[x][cameraY] == WALL) break;
                if(map[x][cameraY] == 0 || map[x][cameraY] == CAN_SEE) {
                    map[x][cameraY] = CAN_SEE;
                }
            }
        }else if(showDir == LEFT) {
            for(int y = cameraY; y &amp;gt;= 0; y--) {
                if(map[cameraX][y] == WALL) break;
                if(map[cameraX][y] == 0 ||map[cameraX][y] == CAN_SEE) {
                    map[cameraX][y] = CAN_SEE;
                }
            }
        }else if(showDir == RIGHT) {
            for(int y = cameraY; y &amp;lt; M; y++) {
                if(map[cameraX][y] == WALL) break;
                if(map[cameraX][y] == 0 ||map[cameraX][y] == CAN_SEE) {
                    map[cameraX][y] = CAN_SEE;
                }
            }
        }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;마지막으로, 전체 경우의 수를 돌아줄 DFS 메서드를 만들어준다.&lt;/p&gt;
&lt;pre id=&quot;code_1619012508484&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static void dfs(int idx, int[][] map) {
        if(idx == cameras.size()) { // 기저조건 : 모든 카메라를 다 돌았다면 종료
            answer = Math.min(answer, getEdgeCount(map));
            // map 에서 0의 갯수를 센 것과, answer 중 작은 것을 저장한다.
            return;
        }
        Camera camera = cameras.get(idx); // 해당 인덱스의 카메라를 가져오고
        int x = camera.x;
        int y = camera.y;
        int[][] newMap = new int[N][M];
        for(int i = 0 ; i &amp;lt; 4 ; i++) { // 회전횟수 4회를 돌면서
            int[] next = d[camera.type][i]; // 카메라의 타입(1차원) -&amp;gt; 회전 횟수에 따른 움직임들(2차원)
            newMap = copyMap(map); // 이전의 map 을 복사해준다. (서로에게 영향을 끼치지 않기 위해)
            for(int j = 0; j &amp;lt; next.length; j++) {
            	// {LEFT, RIGHT, DOWN}  일 경우, LEFT-&amp;gt;RIGHT-&amp;gt;DOWN 을 감시해준다.
                show(newMap, x, y, next[j]);
            }
            // 이러면, i번 회전한 카메라로 여러 방향을 감시한 결과가 newMap 에 저장되고
            dfs(idx + 1, newMap);
           	// 재귀로 넘겨준다.
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 합치면 끝이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Source Code&lt;/h4&gt;
&lt;pre id=&quot;code_1619012537998&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    private static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    private static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    private static int N,M,answer;
    private static int[][] map;
    private static ArrayList&amp;lt;Camera&amp;gt; cameras;
    // 1번 : RIGHT -&amp;gt; DOWN -&amp;gt; LEFT -&amp;gt; UP
    // 2번 : LEFT, RIGHT -&amp;gt; UP, DOWN -&amp;gt; LEFT, RIGHT -&amp;gt; UP, DOWN
    // 3번 : UP, RIGHT -&amp;gt; RIGHT, DOWN -&amp;gt; DOWN, LEFT -&amp;gt; LEFT, UP
    // 4번 : UP, LEFT, RIGHT -&amp;gt; UP, RIGHT, DOWN -&amp;gt; RIGHT, DOWN, LEFT -&amp;gt; DOWN, LEFT, UP
    private static final int UP = 0, DOWN = 1, LEFT = 2, RIGHT = 3, WALL = 6, CAN_SEE = 9;
    private static int[][][] d = {{}, {{RIGHT},{DOWN},{LEFT},{UP}}, {{LEFT, RIGHT}, {UP, DOWN}, {LEFT, RIGHT}, {UP,DOWN}}, {{UP,RIGHT}, {RIGHT, DOWN}, {DOWN, LEFT}, {LEFT, UP}}, {{UP,LEFT,RIGHT},{ UP,RIGHT,DOWN},{RIGHT,DOWN,LEFT},{DOWN,LEFT,UP}}};

    private static class Camera {
        int x;
        int y;
        int type;

        public Camera(int x, int y, int type) {
            this.x = x;
            this.y = y;
            this.type = type;
        }

        @Override
        public String toString() {
            return &quot;Camera{&quot; +
                    &quot;x=&quot; + x +
                    &quot;, y=&quot; + y +
                    &quot;, type=&quot; + type +
                    '}';
        }
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
    }

    private static void input() throws IOException {
        StringTokenizer nm = new StringTokenizer(bufferedReader.readLine());
        N = Integer.parseInt(nm.nextToken());
        M = Integer.parseInt(nm.nextToken());
        map = new int[N][M];
        answer = Integer.MAX_VALUE;
        cameras = new ArrayList&amp;lt;&amp;gt;();
        for(int i = 0 ; i &amp;lt; N; i++) {
            StringTokenizer line = new StringTokenizer(bufferedReader.readLine());
            for(int j = 0 ; j &amp;lt; M; j++) {
                map[i][j] = Integer.parseInt(line.nextToken());
                if(map[i][j] &amp;gt; 0 &amp;amp;&amp;amp; map[i][j] &amp;lt; 6) {
                    cameras.add(new Camera(i,j, map[i][j]));
                }
            }
        }
        ArrayList&amp;lt;Camera&amp;gt; newCameras = new ArrayList&amp;lt;&amp;gt;();
        for(Camera c : cameras) {
            if(c.type == 5) {
                show(map, c.x, c.y, UP);
                show(map, c.x, c.y, DOWN);
                show(map, c.x, c.y, RIGHT);
                show(map, c.x, c.y, LEFT);
            } else {
                newCameras.add(c);
            }
        }
        cameras = newCameras;
    }

    private static int getEdgeCount(int[][] map) {
        int sum = 0;
        for(int i = 0 ; i &amp;lt; N; i++) {
            for(int j = 0 ; j &amp;lt; M; j++) {
                if(map[i][j] == 0) sum++;
            }
        }
        return sum;
    }

    private static void printMap(int[][] map) {
        for(int i = 0 ; i &amp;lt; N; i++) {
            for(int j = 0 ; j &amp;lt; M; j++) {
                System.out.print(map[i][j] + &quot; &quot;);
            }
            System.out.println();
        }
        System.out.println(&quot;====================&quot;);
    }

    private static int[][] copyMap(int[][] map) {
        int[][] result = new int[N][M];
        for(int i = 0 ; i &amp;lt; N; i++) {
            result[i] = map[i].clone();
        }
        return result;
    }


    private static void show(int[][] map, int cameraX, int cameraY, int showDir) {
        if(showDir == UP) {
            for(int x = cameraX; x &amp;gt;= 0; x--) {
                if(map[x][cameraY] == WALL) break;
                if(map[x][cameraY] == 0 || map[x][cameraY] == CAN_SEE) {
                    map[x][cameraY] = CAN_SEE;
                }
            }
        } else if(showDir == DOWN) {
            for(int x = cameraX; x &amp;lt; N; x++) {
                if(map[x][cameraY] == WALL) break;
                if(map[x][cameraY] == 0 || map[x][cameraY] == CAN_SEE) {
                    map[x][cameraY] = CAN_SEE;
                }
            }
        }else if(showDir == LEFT) {
            for(int y = cameraY; y &amp;gt;= 0; y--) {
                if(map[cameraX][y] == WALL) break;
                if(map[cameraX][y] == 0 ||map[cameraX][y] == CAN_SEE) {
                    map[cameraX][y] = CAN_SEE;
                }
            }
        }else if(showDir == RIGHT) {
            for(int y = cameraY; y &amp;lt; M; y++) {
                if(map[cameraX][y] == WALL) break;
                if(map[cameraX][y] == 0 ||map[cameraX][y] == CAN_SEE) {
                    map[cameraX][y] = CAN_SEE;
                }
            }
        }
    }

    private static void dfs(int idx, int[][] map) {
        if(idx == cameras.size()) {
            answer = Math.min(answer, getEdgeCount(map));
            return;
        }
        Camera camera = cameras.get(idx);
        int x = camera.x;
        int y = camera.y;
        int[][] newMap = new int[N][M];
        for(int i = 0 ; i &amp;lt; 4 ; i++) {
            int[] next = d[camera.type][i];
            newMap = copyMap(map);
            for(int j = 0; j &amp;lt; next.length; j++) {
                show(newMap, x, y, next[j]);
            }
            dfs(idx + 1, newMap);
        }
    }

    private static void solve() throws IOException {
        dfs(0, map);
        System.out.println(answer);
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>15683 java</category>
      <category>java 15683</category>
      <category>java 15683 감시</category>
      <category>java 15683 풀이</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/31</guid>
      <comments>https://simju9397.tistory.com/31#entry31comment</comments>
      <pubDate>Wed, 21 Apr 2021 22:42:49 +0900</pubDate>
    </item>
    <item>
      <title>백준 1062번 : 가르침 풀이(Java) - 브루트포스</title>
      <link>https://simju9397.tistory.com/30</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1062&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/1062&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1618732256542&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;1062번: 가르침&quot; data-og-description=&quot;첫째 줄에 단어의 개수 N과 K가 주어진다. N은 50보다 작거나 같은 자연수이고, K는 26보다 작거나 같은 자연수 또는 0이다. 둘째 줄부터 N개의 줄에 남극 언어의 단어가 주어진다. 단어는 영어 소문&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1062&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1062&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bECkGN/hyJUXEzMID/IrVFWAO2i3WGGS4Ss717f1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1062&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1062&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bECkGN/hyJUXEzMID/IrVFWAO2i3WGGS4Ss717f1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;1062번: 가르침&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 단어의 개수 N과 K가 주어진다. N은 50보다 작거나 같은 자연수이고, K는 26보다 작거나 같은 자연수 또는 0이다. 둘째 줄부터 N개의 줄에 남극 언어의 단어가 주어진다. 단어는 영어 소문&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZCCb6/btq2OOXzzEW/HK36f6GtDvyUq0pcu9wrrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZCCb6/btq2OOXzzEW/HK36f6GtDvyUq0pcu9wrrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZCCb6/btq2OOXzzEW/HK36f6GtDvyUq0pcu9wrrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZCCb6%2Fbtq2OOXzzEW%2FHK36f6GtDvyUq0pcu9wrrk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;이 문제는 조합 + 브루트포스로 풀 수 있다. 해법은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;가르칠 수 있는 문자가 K개로 한정되어 있으므로, a~z 중에 어떤 문자를 가르칠 지 정해야 한다. -&amp;gt; 바로 조합임을 알 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;그럼 주어진 단어들을 읽을 수 있냐는 간단하다. 단어의 0번 알파벳부터 끝 알파벳까지, 가르친 적 없는 문자가 하나라도 있다면 읽을 수 없으므로 갯수에 포함하지 않고, 다 읽을 수 있다면 답을 더해준다.&lt;/li&gt;
&lt;li&gt;모든 조합의 경우의 수에서, 읽을 수 있는 최대값을 구한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이다.&lt;/p&gt;
&lt;p&gt;알파벳은 a부터 z까지 26개 이므로, 26개 중에 K개를 뽑으면 되겠다. 그렇게 푼게 맨 밑의 풀이, 2616ms 가 나왔다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사실 첫 풀이를 제출할 때 시간초과가 날 줄 알았는데, 엄청나게 긴 시간이 나왔으므로 바로 떠오른 시간 줄이기 방법을 적용했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;문제의 조건 중에,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;남극언어의 모든 단어는 &quot;anta&quot;로 시작되고, &quot;tica&quot;로 끝난다. 남극언어에 단어는 N개 밖에 없다고 가정한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;라는 게 있다. 즉,&lt;i&gt;&lt;b&gt; anta 와 tica 를 &quot;반드시&quot; 읽을 수 있어야 단어 전체를 읽어볼 수 있을까 말까한다는 뜻이다. &lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;anta 와 tica 를 읽으려면 기본적으로 a, n, t, i, c 의 알파벳을 알아야 하므로, 이 5개는 기본으로 깔고 가자는 거다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이는 곧, K 가 5 미만이라면(가르칠 수 있는 문자 갯수가 5 이하라면) 어떤 단어가 오든 아예 못 읽는다는 말이므로 조기 종료해주면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;코드를 보면 금방 이해 될 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1618732905185&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.StringTokenizer;

public class Main {
    private static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    private static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    private static int N,K, answer;
    private static String[] words;
    private static boolean[] pickedChars;
    private static char[] defaults = new char[]{'a','c','n','t','i'};
    
    public static void main(String[] args) throws IOException {
        input();
        solve();
        bufferedReader.close();
        bufferedWriter.close();
    }

    private static void input() throws IOException {
        StringTokenizer nk = new StringTokenizer(bufferedReader.readLine());
        N = Integer.parseInt(nk.nextToken());
        K = Integer.parseInt(nk.nextToken());
        if(K &amp;lt; 5) { // 가르칠 수 있는 게 5개 미만이면 어차피 아무것도 못읽는다.
            System.out.println(0);
            System.exit(0);
        }
        K -= 5; // 디폴트로 뽑을 것이므로
        words = new String[N];
        for(int i = 0 ; i &amp;lt; N; i++) {
            String word = bufferedReader.readLine();
            words[i] = word;
        }
    }
    
    private static void solve() {
        makeCombination(0,0,new int[K]);
        System.out.println(answer);
    }
    
    private static void makeCombination(int start, int cnt, int[] picked) {
        if(cnt == K) {
            pickedChars = new boolean[26];
            setDefault();
            for(int i = 0 ; i &amp;lt; K; i++) {
                char c = (char)(picked[i] + 97);
                pickedChars[picked[i]] = true;
            }
            checkReadable();
            return;
        }
        for(int i = start; i &amp;lt; 26; i++) { // 0번부터 25번 까지(a부터 z까지)
            boolean alreadyPicked = false; // 미리 가르친 a,c,n,t,i 중에서 또 가르치면 낭비이므로,
            for(char aDefault : defaults) {
                if(aDefault-'a' == i) { // 만약 i 번이 이미 가르친 a,c,n,t,i 의 code값과 같다면
                    alreadyPicked = true; // 낭비임을 체크해준다.
                    break;
                }
            }
            if(alreadyPicked) { // 만약 낭비라면, 조합을 만들어줄 필요가 없다.
                continue;
            }
            picked[cnt] = i;
            makeCombination(i+1, cnt+1, picked);
        }
    }

    private static void setDefault() {
        for (char aDefault : defaults) {
            pickedChars[aDefault - 'a'] = true;
        }
        // a,c,n,t,i 를 미리 가르친다.
    }

    private static void checkReadable() {
        int sum = 0;
        for(int i = 0 ; i &amp;lt; N; i++) {
            boolean readable = true; // 해당 단어를 읽을 수 있는지
            for(int j = 0 ; j &amp;lt; words[i].length(); j++) {
                char c  = words[i].charAt(j); // 단어의 알파벳 하나하나 중에
                if(!pickedChars[(int)c - 97]) { // 안가르친게 있다면 
                    readable = false; // 읽을 수 없음을 체크해준다.
                    break;
                }
            }
            if(readable) { // 읽을 수 있다면 
                sum++; // 갯수를 더해주고
            }
        }
        answer = Math.max(answer, sum); // 최대값을 저장해준다.
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>1062 java</category>
      <category>백준 1062 java</category>
      <category>백준 1062 풀이</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/30</guid>
      <comments>https://simju9397.tistory.com/30#entry30comment</comments>
      <pubDate>Sun, 18 Apr 2021 17:06:32 +0900</pubDate>
    </item>
    <item>
      <title>백준 7576번 : 토마토 풀이(Java) - BFS</title>
      <link>https://simju9397.tistory.com/29</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/7576&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/7576&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1618363489289&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;7576번: 토마토&quot; data-og-description=&quot;첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 &amp;le; M,N &amp;le; 1,000 이다. 둘째 줄부터는 하나의 상자에 저장된 토마토&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/7576&quot; data-og-url=&quot;https://www.acmicpc.net/problem/7576&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bMZXwA/hyJShiHw0x/4rPgOLIpCLAHtHkblepln0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bpSMOV/hyJR953ZKm/fjkTLqJ7qQSw9TFDYpgYNk/img.png?width=250&amp;amp;height=208&amp;amp;face=0_0_250_208&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/7576&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/7576&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bMZXwA/hyJShiHw0x/4rPgOLIpCLAHtHkblepln0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bpSMOV/hyJR953ZKm/fjkTLqJ7qQSw9TFDYpgYNk/img.png?width=250&amp;amp;height=208&amp;amp;face=0_0_250_208');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;7576번: 토마토&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 &amp;le; M,N &amp;le; 1,000 이다. 둘째 줄부터는 하나의 상자에 저장된 토마토&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LCo7k/btq2BDVz3BU/WD8zJzdG1liYwKcvYz0uh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LCo7k/btq2BDVz3BU/WD8zJzdG1liYwKcvYz0uh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LCo7k/btq2BDVz3BU/WD8zJzdG1liYwKcvYz0uh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLCo7k%2Fbtq2BDVz3BU%2FWD8zJzdG1liYwKcvYz0uh1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;얼마 전에 풀었던 문제인데 다시 풀게 되었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;이 문제는 BFS를 구현할 줄 안다면 크게 어렵지 않은데, 주의할 점은,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미 익어있는 토마토(즉, 시작점) 가 처음에 여러 개 있을 수 있다.&lt;/li&gt;
&lt;li&gt;토마토가 몇 초 대에 익는지 기록한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이다. 즉, 1번의 경우 여러 토마토가 한 번에 큐에 담겨서 같이 순차적으로 진행될 수 있도록 해야한다는 의미이다.&lt;/p&gt;
&lt;p&gt;2번의 경우는, 큐에 담을 때 토마토가 몇 초 대에 익는지 기록하고 나면, 그 토마토에 의해서 익을 다음 토마토들은 이전 토마토가 익은 시간 + 1 이 담기면 된다는 것이다. ( 2초 대에 익은 토마토가 3초 대에 상,하의 토마토를 익힘 )&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;한 번 풀어봤던 문제라 빠르게 풀었다.&lt;/p&gt;
&lt;p&gt;실행 시간을 줄인 것은, 입력 받을 때 익혀야 되는 토마토의 수, 즉 0인 것들의 갯수를 세어서 bfs를 돌고 난 이후에 체크해줘서 2차원 반복을 피할 수 있었다. 입력이 작아서 크게 소용있진 않았지만,,&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Source Code&lt;/h4&gt;
&lt;pre id=&quot;code_1618363816329&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    private static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    private static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    private static int width, height, result, shouldRipeCount;
    private static int[][] map;
    private static ArrayList&amp;lt;Node&amp;gt; tomatoes;
    private static boolean[][] visited;
    private static int[] dx = {-1,1,0,0};
    private static int[] dy = {0,0,-1,1};


    private static class Node {
        int x;
        int y;
        int depth;

        public Node(int x, int y, int depth) {
            this.x = x;
            this.y = y;
            this.depth = depth;
        }
    }

    private static void input() throws IOException {
        StringTokenizer wh = new StringTokenizer(bufferedReader.readLine());
        width = Integer.parseInt(wh.nextToken());
        height = Integer.parseInt(wh.nextToken());
        map = new int[height][width];
        visited = new boolean[height][width];
        tomatoes = new ArrayList&amp;lt;&amp;gt;();
        for(int i = 0 ; i &amp;lt; height; i++) {
            StringTokenizer st = new StringTokenizer(bufferedReader.readLine());
            for(int j = 0 ; j &amp;lt; width; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
                if(map[i][j] == 1) {
                    tomatoes.add(new Node(i,j, 0)); // 이미 익은 토마토들을 담아주고,
                    visited[i][j] = true; // 방문 처리도 해준다.
                } else if(map[i][j] == 0) {
                    shouldRipeCount++; // 익혀야 하는 토마토 갯수
                }
            }
        }
    }

    private static boolean bfs() {
        ArrayDeque&amp;lt;Node&amp;gt; queue = new ArrayDeque&amp;lt;&amp;gt;();
        queue.addAll(tomatoes); // 초기에 익은 토마토들을 먼저 담는다.
        int ripeCount = 0; // BFS로 인해 익을 토마토의 갯수를 담는다.
        while (!queue.isEmpty()) {
            Node s = queue.poll();
            int sx = s.x;
            int sy = s.y;
            int sd = s.depth; // 토마토가 sd 초에 익었다는 것을 나타낸다.
            result = sd; // BFS 가 끝날 때,마지막에 익은 토마토는 sd초에 익을 것이므로 계속 대입해준다.
            for(int i = 0 ; i &amp;lt; 4; i++) {
                int nx = sx + dx[i];
                int ny = sy + dy[i];
                if(nx &amp;lt; 0 || ny &amp;lt; 0 || nx &amp;gt;= height || ny &amp;gt;= width) continue;
                if(visited[nx][ny]) continue;
                if(map[nx][ny] == 0) {
                    map[nx][ny] = 1;
                    ripeCount++;
                    visited[nx][ny] = true;
                    queue.add(new Node(nx, ny, sd + 1));
                }
            }
        }
        if(ripeCount == shouldRipeCount) { // 익힌 갯수 = 익혀야 할 갯수라면 true
            return true;
        } else return false;
    }

    private static void solve() throws IOException {
        if(bfs()) {
            System.out.println(result);
        } else {
            System.out.println(-1);
        }

    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>7576 java</category>
      <category>bfs</category>
      <category>java 7576</category>
      <category>백준 7576</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/29</guid>
      <comments>https://simju9397.tistory.com/29#entry29comment</comments>
      <pubDate>Wed, 14 Apr 2021 10:32:36 +0900</pubDate>
    </item>
    <item>
      <title>백준 12026 번 : BOJ 거리 풀이(Java) - DP</title>
      <link>https://simju9397.tistory.com/28</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/12026&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&amp;nbsp;www.acmicpc.net/problem/12026&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1617195770345&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;12026번: BOJ 거리&quot; data-og-description=&quot;스타트가 링크를 만나는데 필요한 에너지 양의 최솟값을 출력한다. 만약, 스타트가 링크를 만날 수 없는 경우에는 -1을 출력한다.&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/12026&quot; data-og-url=&quot;https://www.acmicpc.net/problem/12026&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jlFOH/hyJJH9byhh/n1kIaUtQaEQhKxdNkdRIvK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/12026&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/12026&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jlFOH/hyJJH9byhh/n1kIaUtQaEQhKxdNkdRIvK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;12026번: BOJ 거리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;스타트가 링크를 만나는데 필요한 에너지 양의 최솟값을 출력한다. 만약, 스타트가 링크를 만날 수 없는 경우에는 -1을 출력한다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EYvWk/btq1z3VGxJj/qanvrSKGrzcCKLPPW2S020/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EYvWk/btq1z3VGxJj/qanvrSKGrzcCKLPPW2S020/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EYvWk/btq1z3VGxJj/qanvrSKGrzcCKLPPW2S020/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEYvWk%2Fbtq1z3VGxJj%2FqanvrSKGrzcCKLPPW2S020%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;1. 이 문제는 N 이 최대 1000 밖에 안된다. 별로 크지 않다는 의미이고, 시간 제한도 2초로 굉장히 널널하다. 그래서 2중 for문을 돌아도 되겠다는 생각이 들었다. 처음에는 DP에 2중 for 문을 쓰면 터지지 않을까.. 걱정했지만 104ms 라는 만족스러운 시간으로 통과했다. 역시 먼저 드는 생각으로 풀어보는 게 최고인 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. 이 문제에는 규칙이 있다.&lt;/p&gt;
&lt;p&gt;B 를 밟았다면, 다음은 무조건 O를, 마찬가지로&lt;/p&gt;
&lt;p&gt;O -&amp;gt; J&lt;/p&gt;
&lt;p&gt;J -&amp;gt; B&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;를 밟아야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;9&lt;/p&gt;
&lt;p&gt;BOJBOJBOJ&lt;/p&gt;
&lt;p&gt;가 입력이라 가정해보자.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;맨 처음(0번)은 무조건 B&lt;/b&gt; 이므로, 다음 순번인 1번부터 끝까지 살피면서 O인 곳을 체크한다. 갈 수 있는 모든 곳을 체크하면서, 인덱스 0번 -&amp;gt; 인덱스 i 번으로 가는 데 드는 비용은 i*i 가 될 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그 다음인 1번은 O 이므로, 다음 순번인 2번부터 끝까지 살피면서 J인 곳을 체크한다. ( B -&amp;gt; O -&amp;gt; J -&amp;gt; B -&amp;gt; ... )&lt;/p&gt;
&lt;p&gt;마찬가지로 (i - 1) * (i - 1) 을 비용으로 정해준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2번인 J 도 똑같은 과정을 했다고 생각하면,&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;0번의 B에 의해서, 1번의 O, 4번의 O, 7번의 O 가 값이 정해졌고,&lt;/p&gt;
&lt;p&gt;1번의 O에 의해서, 2번의 J, 5번의 J, 8번의 J 의 값이 정해졌고,&lt;/p&gt;
&lt;p&gt;2번의 J 에 의해서, 3번의 B, 6번의 B 의 값이 정해졌다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;도착하는 기준은, dp[N-1] 의 값(0 번부터 N번 까지 가는 데 드는&amp;nbsp;&lt;b&gt;최소&lt;/b&gt; 비용) 이 초기화된 값( 최소 값을 구하는 것이므로, 아주 큰 값(Integer.MAX_VALUE 라던가) ) 이 아니라면 N번에 갈 수 있다는 것이 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러므로 출력 형식은, dp[N-1] 이 MAX 라면 -1 을 출력, 아니라면 dp[N-1] 을 출력하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;3. 하지만 2번처럼 그 때마다 값을 그냥 바꿔주면 최소가 보장되지 않는다.&lt;/p&gt;
&lt;p&gt;0번&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; k번&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; i번&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;N-1번&lt;/p&gt;
&lt;p&gt;B&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;O&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; O&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;J&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;라면, k번과 i 번 중 어느 O를 밟고 가야 최선일까?&lt;/p&gt;
&lt;p&gt;0~k-1, k+1 ~ i - 1 번에 있는 것들이 어떻게 될 지 모르니까,&lt;/p&gt;
&lt;p&gt;만약 a -&amp;gt; b 로 간다면,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dp[b] 배열에 담겨있는 값과, ( 0번부터 b 번까지 가는 값들 중 현재까지 발견된 최소 값)&lt;/li&gt;
&lt;li&gt;dp[a] + (b-a)^2 의 값 (0번부터 a 번까지 가는 최소값 + a 번에서 b 번까지 점프하는데 드는 비용 )&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;을 비교해서, 작은 값으로 담아줘야 한다.&lt;/p&gt;
&lt;p&gt;이 점만 알면 구현은 쉽다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Source Code&lt;/h4&gt;
&lt;pre id=&quot;code_1617197023848&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.Arrays;

public class Main {
    private static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    private static final int MAX = 987654321;
    private static int N;
    private static char[] street;
    private static int[] dp;
    private static void input() throws IOException {
        N = Integer.parseInt(bufferedReader.readLine());
        street = new char[N];
        street = bufferedReader.readLine().toCharArray();
        dp = new int[N];
        Arrays.fill(dp, MAX);
    }

    private static void solve() throws IOException {
        dp[0] = 0;
        for(int i = 0; i &amp;lt; N-1; i++) {
            int now = i;
            char nowBlock = street[now];
            if(nowBlock == 'B') {
                for(int j = i +1; j &amp;lt; N; j++) {
                    int next = j;
                    char nextBlock = street[next];
                    if(nextBlock == 'O') {
                        dp[next] = Math.min(dp[next], dp[now] + (next-now) * (next-now));
                    }
                }
            } else if(nowBlock == 'O') {
                for(int j = i +1; j &amp;lt; N; j++) {
                    int next = j;
                    char nextBlock = street[next];
                    if(nextBlock == 'J') {
                        dp[next] = Math.min(dp[next], dp[now] + (next-now) * (next-now));
                    }
                }
            } else {
                for(int j = i +1; j &amp;lt; N; j++) {
                    int next = j;
                    char nextBlock = street[next];
                    if(nextBlock == 'B') {
                        dp[next] = Math.min(dp[next], dp[now] + (next-now) * (next-now));
                    }
                }
            }
        }
        if(dp[N-1] == MAX) {
            System.out.println(&quot;-1\n&quot;);
        } else {
            System.out.println(dp[N-1]);
        }
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>12026 java</category>
      <category>12026 풀이</category>
      <category>java 12026</category>
      <category>백준 12026</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/28</guid>
      <comments>https://simju9397.tistory.com/28#entry28comment</comments>
      <pubDate>Wed, 31 Mar 2021 22:24:25 +0900</pubDate>
    </item>
    <item>
      <title>백준 1915번 : 가장 큰 정사각형 풀이 (Java) - DP</title>
      <link>https://simju9397.tistory.com/27</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1915&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/1915&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1616840131118&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;1915번: 가장 큰 정사각형&quot; data-og-description=&quot;첫째 줄에 n, m(1 &amp;le; n, m &amp;le; 1,000)이 주어진다. 다음 n개의 줄에는 m개의 숫자로 배열이 주어진다.&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1915&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1915&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fmMaj/hyJGH9h2cB/JW4hIppmjssW9q87Dnjsck/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1915&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1915&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fmMaj/hyJGH9h2cB/JW4hIppmjssW9q87Dnjsck/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;1915번: 가장 큰 정사각형&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 n, m(1 &amp;le; n, m &amp;le; 1,000)이 주어진다. 다음 n개의 줄에는 m개의 숫자로 배열이 주어진다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFv2q3/btq1asI5FwV/Tqe9MdEi2gYzQXneZK3EAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFv2q3/btq1asI5FwV/Tqe9MdEi2gYzQXneZK3EAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFv2q3/btq1asI5FwV/Tqe9MdEi2gYzQXneZK3EAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFv2q3%2Fbtq1asI5FwV%2FTqe9MdEi2gYzQXneZK3EAk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;1. N*M 2차원 배열 dp 를 만들고, 안에 들어가는 값은&lt;/p&gt;
&lt;p&gt;dp[a][b] = (a,b) 를 &lt;u&gt;&lt;b&gt;오른쪽 아래로&lt;/b&gt;&lt;/u&gt; 하는 정사각형 중, &lt;u&gt;&lt;b&gt;최대 정사각형의 변의 길이&amp;nbsp;&lt;/b&gt;&lt;/u&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. 오른쪽 아래를 기점으로 삼는다는 말은,&lt;/p&gt;
&lt;p&gt;dp[a-1][b] ( 오른쪽 위 )&lt;/p&gt;
&lt;p&gt;dp[a-1][b-1] ( 왼쪽 위 )&lt;/p&gt;
&lt;p&gt;dp[a][b-1] (왼쪽 아래)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;가 &lt;u&gt;&lt;b&gt;모두 1보다 커야 한다.&amp;nbsp;&lt;/b&gt;&lt;/u&gt; 예제로 한번 보자. 만약에 입력이&lt;/p&gt;
&lt;p&gt;4 4&lt;/p&gt;
&lt;p&gt;0111&lt;/p&gt;
&lt;p&gt;0111&lt;/p&gt;
&lt;p&gt;0111&lt;/p&gt;
&lt;p&gt;0111&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이라면, dp 배열은&lt;/p&gt;
&lt;p&gt;0&amp;nbsp;1&amp;nbsp;1&amp;nbsp;1&amp;nbsp;&lt;br /&gt;0&amp;nbsp;1&amp;nbsp;2&amp;nbsp;2&amp;nbsp;&lt;br /&gt;0&amp;nbsp;1&amp;nbsp;2&amp;nbsp;3&amp;nbsp;&lt;br /&gt;0&amp;nbsp;1&amp;nbsp;2&amp;nbsp;3&amp;nbsp;&lt;/p&gt;
&lt;p&gt;가 된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;천천히 보자. 우선 당연히 (1,1)부터 봐야한다. ( 왼쪽 위까지 봐야 하니까 )&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;(1,1) 에서는 (0,0), (0,1), (1,0) 을 볼텐데, (0,0) 과 (1,0) 이 0, 즉 정사각형의 선분이 될 수 없으므로 그냥 자신의 값인 1만 유지한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;(1,2) 에서라면 ? (0,1), (0,2), (1,1) 을 보는데, 이 3개의 값이 전부 0보다 크다. 즉, 최소 한개의 선분은 가지고 있다는 뜻이 될 것이다. 그럼 일단 직관적으로 봤을 때 선분의 길이가 2인 정사각형을 만들 수 있으므로 넘어가자.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;위 예제에서, 우리는 당연히 길이가 3인 정사각형이 최대인 걸 한 눈에 알 수 있다. 근데 문제는, 오른쪽에 세로 길이가 4인 직사각형 형태가 있다는 것인데, 어떻게 3과 4 중에 올바른 값을 골라낼 수 있을까?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 문제가 생길 수 있는, 맨 오른쪽 아래의 (3,3)을 보자.&lt;/p&gt;
&lt;p&gt;(2,2) 는 2, (2,3) 은 3, (3,2) 는 2 이다. 즉, (2,2) 까지는 길이가 2인 정사각형을 무조건 만들 수 있다는 것이 보장되고, (2,3)까지는 길이가 3인 정사각형을 무조건 만들 수 있다. (3,2)도 마찬가지로 2인 정사각형을 만들 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/00gUA/btq09iNYvxc/bB0d7BBcEA7Fwa1FU3vUT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/00gUA/btq09iNYvxc/bB0d7BBcEA7Fwa1FU3vUT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/00gUA/btq09iNYvxc/bB0d7BBcEA7Fwa1FU3vUT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F00gUA%2Fbtq09iNYvxc%2FbB0d7BBcEA7Fwa1FU3vUT1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서 우리가 알 수 있는 것은,&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;arr[i][j] 가 1 이고, (정사각형의 한 꼭지점이 될 수 있고 )&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;dp[i-1][j-1], dp[i-1][j], dp[i][j-1] 이 세 곳의 값 중에&amp;nbsp;&lt;u&gt;&lt;b&gt;최소값+1 의 선분의 길이가&amp;nbsp;&lt;/b&gt;&lt;/u&gt;최대 정사각형이라는 것이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dLFooZ/btq09kdWgbv/uvIk7jdzQWuSoJjW7jOUIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dLFooZ/btq09kdWgbv/uvIk7jdzQWuSoJjW7jOUIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dLFooZ/btq09kdWgbv/uvIk7jdzQWuSoJjW7jOUIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdLFooZ%2Fbtq09kdWgbv%2FuvIk7jdzQWuSoJjW7jOUIk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이 예시에서, (2,2), (2,3), (3,2) 가 모두 3이면, (3,3) 이 1일 때, 길이가 4인 정사각형이 보장된다. 이것만 이해하면 금방 풀 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Source Code&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1616841669472&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.StringTokenizer;

public class Main {
    private static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    private static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    private static int N,M, max;
    private static int[][] arr, dp;
    private static void input() throws IOException {
        StringTokenizer nm = new StringTokenizer(bufferedReader.readLine());
        N = Integer.parseInt(nm.nextToken());
        M = Integer.parseInt(nm.nextToken());
        arr = new int[N][M];
        dp = new int[N][M];
        for(int i = 0 ; i &amp;lt; N; i++) {
            char[] line = bufferedReader.readLine().toCharArray();
            for(int j = 0 ; j &amp;lt; M; j++) {
                arr[i][j] = Character.getNumericValue(line[j]);
                dp[i][j] = arr[i][j];
                if(i &amp;gt;= 1 &amp;amp;&amp;amp; j &amp;gt;= 1) {
                    if(dp[i-1][j] &amp;gt; 0 &amp;amp;&amp;amp; dp[i-1][j-1] &amp;gt; 0 &amp;amp;&amp;amp; dp[i][j-1] &amp;gt; 0 &amp;amp;&amp;amp; dp[i][j] &amp;gt; 0) {
                        int min = Math.min(dp[i-1][j], Math.min(dp[i-1][j-1], dp[i][j-1]));
                        dp[i][j] = min + 1;
                    }
                }
                max = Math.max(max, dp[i][j]);
            }
        }
        for(int i = 0 ; i &amp;lt; N; i++) {
            for(int j = 0 ; j &amp;lt; M; j++) {
                System.out.print(dp[i][j] + &quot; &quot;);
            }
            System.out.println();
        }
    }

    private static void solve() throws IOException {
        System.out.println(max * max);
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>1915 java</category>
      <category>1915 java 풀이</category>
      <category>가장 큰 정사각형 java</category>
      <category>백준 1915번</category>
      <category>백준 1915번 풀이</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/27</guid>
      <comments>https://simju9397.tistory.com/27#entry27comment</comments>
      <pubDate>Sat, 27 Mar 2021 19:41:32 +0900</pubDate>
    </item>
    <item>
      <title>백준 11659번 : 구간 합 구하기 4 풀이 (Java) - DP</title>
      <link>https://simju9397.tistory.com/26</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11659&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/11659&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1616837941256&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;11659번: 구간 합 구하기 4&quot; data-og-description=&quot;첫째 줄에 수의 개수 N과 합을 구해야 하는 횟수 M이 주어진다. 둘째 줄에는 N개의 수가 주어진다. 수는 1,000보다 작거나 같은 자연수이다.&amp;nbsp;셋째 줄부터 M개의 줄에는 합을 구해야 하는 구간 i와 j&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/11659&quot; data-og-url=&quot;https://www.acmicpc.net/problem/11659&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/byze8l/hyJGESdhBx/Nkm32cJnJkvkKNV53hru51/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11659&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/11659&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/byze8l/hyJGESdhBx/Nkm32cJnJkvkKNV53hru51/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;11659번: 구간 합 구하기 4&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 수의 개수 N과 합을 구해야 하는 횟수 M이 주어진다. 둘째 줄에는 N개의 수가 주어진다. 수는 1,000보다 작거나 같은 자연수이다.&amp;nbsp;셋째 줄부터 M개의 줄에는 합을 구해야 하는 구간 i와 j&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kSSJa/btq1aRhwk2o/3WiTWOyKfqGJyl17XKNHz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kSSJa/btq1aRhwk2o/3WiTWOyKfqGJyl17XKNHz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kSSJa/btq1aRhwk2o/3WiTWOyKfqGJyl17XKNHz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkSSJa%2Fbtq1aRhwk2o%2F3WiTWOyKfqGJyl17XKNHz0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;dp 에 익숙하다면 금방 풀 것이다. 10분도 안 걸린듯하다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;1. N, M 이 모두 최대가 100,000 이다. 만약 합을 구할 구간을 입력받을 때 마다 반복문으로 돌린다고 해도,1~100000 이 100000번 나온다면 100억 회를 해야 한다. 당연히 시간 초과가 뜰 것이다.&lt;/p&gt;
&lt;p&gt;2. 즉 이 문제는, N 만큼의 배열의 숫자들을 입력 받을 때, dp 배열에다가 합을 미리 구해놓는 것이다.&lt;/p&gt;
&lt;p&gt;dp[N] = 1번부터 N번 까지의 구간 합을 나타내는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;3. 그럼 A번 부터 B번 까지 (A &amp;lt;= B) 의 구간 합은,&lt;/p&gt;
&lt;p&gt;dp[A] = 1번부터 A번 까지의 구간 합&lt;/p&gt;
&lt;p&gt;dp[B] = 1번부터 B번 까지의 구간 합 이므로, &lt;b&gt;dp[B] - dp[A]&lt;/b&gt; 이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Source Code&lt;/h4&gt;
&lt;pre id=&quot;code_1616838347561&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {
    private static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    private static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    private static int N,M;
    private static int[] arr, dp;

    private static void input() throws IOException {
        StringTokenizer nm = new StringTokenizer(bufferedReader.readLine());
        N = Integer.parseInt(nm.nextToken());
        M = Integer.parseInt(nm.nextToken());
        arr = new int[N+1];
        dp = new int[N+1];
        StringTokenizer line = new StringTokenizer(bufferedReader.readLine());
        for (int i = 1; i &amp;lt;= N; i++) {
            arr[i] = Integer.parseInt(line.nextToken());
            dp[i] = dp[i-1] + arr[i];
            // 구간합을 구할 때도 매번 i-1 번까지 for문으로 돌릴 필요가 없다.
        }
    }

    private static void solve() throws IOException {
        for(int i = 0; i &amp;lt; M; i++) {
            StringTokenizer section = new StringTokenizer(bufferedReader.readLine());
            int from = Integer.parseInt(section.nextToken()) - 1;
            int to = Integer.parseInt(section.nextToken());
            bufferedWriter.write((dp[to] - dp[from]) + &quot;\n&quot;);
        }
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
        bufferedWriter.close();
        bufferedReader.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>11659 java</category>
      <category>구간합구하기4</category>
      <category>백준 11659</category>
      <category>백준 11659 java</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/26</guid>
      <comments>https://simju9397.tistory.com/26#entry26comment</comments>
      <pubDate>Sat, 27 Mar 2021 18:46:23 +0900</pubDate>
    </item>
    <item>
      <title>백준 1600번 : 말이 되고픈 원숭이 풀이 ( Java ) - BFS</title>
      <link>https://simju9397.tistory.com/25</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1600&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/1600&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1616566778822&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;1600번: 말이 되고픈 원숭이&quot; data-og-description=&quot;첫째 줄에 정수 K가 주어진다. 둘째 줄에 격자판의 가로길이 W, 세로길이 H가 주어진다. 그 다음 H줄에 걸쳐 W개의 숫자가 주어지는데, 0은 아무것도 없는 평지, 1은 장애물을 뜻한다. 장애물이 있&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1600&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1600&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nmC6W/hyJFct2LEW/qtRKizEEUQpSJGotVHi20K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1600&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1600&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nmC6W/hyJFct2LEW/qtRKizEEUQpSJGotVHi20K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;1600번: 말이 되고픈 원숭이&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 정수 K가 주어진다. 둘째 줄에 격자판의 가로길이 W, 세로길이 H가 주어진다. 그 다음 H줄에 걸쳐 W개의 숫자가 주어지는데, 0은 아무것도 없는 평지, 1은 장애물을 뜻한다. 장애물이 있&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zwyL8/btq0Q8LCSHS/duuNQ5rbFR3RL1ea1ZkdK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zwyL8/btq0Q8LCSHS/duuNQ5rbFR3RL1ea1ZkdK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zwyL8/btq0Q8LCSHS/duuNQ5rbFR3RL1ea1ZkdK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzwyL8%2Fbtq0Q8LCSHS%2FduuNQ5rbFR3RL1ea1ZkdK1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;1. 이 문제는 백준 2206 번 : 벽 부수고 이동하기와 굉장히 유사한 문제이다. &lt;a href=&quot;https://simju9397.tistory.com/11&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;simju9397.tistory.com/11&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1616566938369&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;백준 2206번 : 벽 부수고 이동하기 풀이(Java)&quot; data-og-description=&quot;www.acmicpc.net/problem/2206 2206번: 벽 부수고 이동하기 N&amp;times;M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, &quot; data-og-host=&quot;simju9397.tistory.com&quot; data-og-source-url=&quot;https://simju9397.tistory.com/11&quot; data-og-url=&quot;https://simju9397.tistory.com/11&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cmqwgt/hyJFqMGF5t/U2Xzu9dnJuiekspiEjoqG0/img.png?width=800&amp;amp;height=249&amp;amp;face=0_0_800_249,https://scrap.kakaocdn.net/dn/bJYeNP/hyJFbILFmP/xghjk0hd30jHnuxlN1O0qK/img.png?width=800&amp;amp;height=249&amp;amp;face=0_0_800_249,https://scrap.kakaocdn.net/dn/rrteE/hyJDR6erCu/H413LJukf13kis6jXJJxO0/img.png?width=1157&amp;amp;height=361&amp;amp;face=0_0_1157_361&quot;&gt;&lt;a href=&quot;https://simju9397.tistory.com/11&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://simju9397.tistory.com/11&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cmqwgt/hyJFqMGF5t/U2Xzu9dnJuiekspiEjoqG0/img.png?width=800&amp;amp;height=249&amp;amp;face=0_0_800_249,https://scrap.kakaocdn.net/dn/bJYeNP/hyJFbILFmP/xghjk0hd30jHnuxlN1O0qK/img.png?width=800&amp;amp;height=249&amp;amp;face=0_0_800_249,https://scrap.kakaocdn.net/dn/rrteE/hyJDR6erCu/H413LJukf13kis6jXJJxO0/img.png?width=1157&amp;amp;height=361&amp;amp;face=0_0_1157_361');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;백준 2206번 : 벽 부수고 이동하기 풀이(Java)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;www.acmicpc.net/problem/2206 2206번: 벽 부수고 이동하기 N&amp;times;M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N,&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;simju9397.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;2. 아직 저 문제를 풀어보지 않았다면 먼저 풀어보시길 바란다.&lt;/p&gt;
&lt;p&gt;3. 메모리 초과의 이유&lt;/p&gt;
&lt;p&gt;처음에 나는, 200*200*30 정도이므로 괜찮지 않을까? 하는 생각에 이 문제를 3차원 배열로 풀지 않고 2차원 배열을 복사해서 풀어봤다.&lt;/p&gt;
&lt;pre id=&quot;code_1616567049203&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static void bfs(int sx, int sy, int sd, boolean[][] visited, int k) {
        if(sx == W-1 &amp;amp;&amp;amp; sy == H-1) {
            ans = Math.min(ans, sd);
            return;
        }
        visited[sx][sy] = true;
        boolean[][] visited1 = clone(visited);
        boolean[][] visited2 = clone(visited);
        for(int i = 0 ; i &amp;lt; 4; i++) {
            int nx = sx + dx[i];
            int ny = sy + dy[i];
            if(nx &amp;lt; 0 || ny &amp;lt; 0 || nx &amp;gt;= W || ny &amp;gt;= H) continue;
            if(visited1[nx][ny]) continue;
            visited1[nx][ny] = true;
            bfs(nx, ny, sd + 1, visited1, k);
        }
        if(k &amp;gt; 0 ) {
            for(int i = 0 ; i &amp;lt; 8; i++) {
                int mx = sx + kx[i];
                int my = sy + ky[i];
                if(mx &amp;lt; 0 || my &amp;lt; 0 || mx &amp;gt;= W || my &amp;gt;= H) continue;
                if(visited2[mx][my]) continue;
                visited2[mx][my] = true;
                bfs(mx, my, sd + 1, visited2, k-1);
            }
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;일반 상하좌우 이동을 하는 visited1 배열, 말처럼 이동하는 visited2 배열을 각각 visited 에서 복사해주고, 각각을 재귀로 넘겨주는 식으로 했다. 하지만 당연히 터졌다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;4. 이 문제의 핵심은, 방문처리를 하는 visited 배열을&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;H * W 크기의 boolean 배열이&lt;/li&gt;
&lt;li&gt;K+1 개 만큼 있도록&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;3차원으로 만들어주는 것이다. 그럼 visited[x][y][k] 의 의미는, &lt;b&gt;말처럼 이동한 횟수가 k번일 때, (x,y)가 방문한 지점인지&amp;nbsp;&lt;/b&gt;로 생각하는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;평범하게 이동할 때는 visited[nx][ny][현재 k] 를 true 로 해주고,&lt;/p&gt;
&lt;p&gt;&lt;b&gt;말처럼 &lt;/b&gt;이동할 때는 visited[nx][ny][현재 k + 1] 을 true 로 해주면 될 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그럼 왜 K+1 개 만큼 만들어야 할까?&lt;/p&gt;
&lt;p&gt;바로&lt;b&gt; K = 0 인 경우를 포함해야 하기 때문&lt;/b&gt;이다. 원숭이는 &lt;b&gt;K회 만큼&lt;/b&gt; 말처럼 이동 가능하므로, 말처럼 움직이는 행위 없이(K = 0) 도착지에 도착할 수도 있을 것이고, 한 번이든 두 번이든 K회 이하로만 움직이면 된다는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그럼 큐에 저장하기 위한 Node 의 필드 변수는 좌표 x,y 와 더불어, 얼마나 움직였는지를 나타내주는 depth, 그리고 지금까지 몇 번 만큼 말처럼 이동했는지를 나타내주는 k로 지정해주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1616567605425&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static class Node {
        int x;
        int y;
        int depth;
        int k;

        public Node(int x, int y, int depth, int k) {
            this.x = x;
            this.y = y;
            this.depth = depth;
            this.k = k;
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;Source Code&lt;/h4&gt;
&lt;pre id=&quot;code_1616567641404&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    private static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    private static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    private static int K, W, H, ans;
    private static int[][] map;
    private static int[] dx = {-1,1,0,0};
    private static int[] dy = {0,0,-1,1};
    private static int[] kx = {-2,-1,1,2,2,1,-1,-2};
    private static int[] ky = {1,2,2,1,-1,-2,-2,-1};

    private static class Node {
        int x;
        int y;
        int depth;
        int k;

        public Node(int x, int y, int depth, int k) {
            this.x = x;
            this.y = y;
            this.depth = depth;
            this.k = k;
        }
    }

    private static void input() throws IOException {
        K = Integer.parseInt(bufferedReader.readLine());
        StringTokenizer wh = new StringTokenizer(bufferedReader.readLine());
        W = Integer.parseInt(wh.nextToken()); // 가로 길이
        H = Integer.parseInt(wh.nextToken()); // 세로 길이
        map = new int[H][W];
        for(int i = 0; i &amp;lt; H; i++) {
            StringTokenizer st = new StringTokenizer(bufferedReader.readLine());
            for(int j = 0;j &amp;lt; W; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }
        ans = Integer.MAX_VALUE;
    }

    private static void bfs(Node start, boolean[][][] visited) {
        Deque&amp;lt;Node&amp;gt; deque = new ArrayDeque&amp;lt;&amp;gt;();
        deque.offer(start);
        visited[start.x][start.y][0] = true;
        while (!deque.isEmpty()) {
            Node node = deque.poll();
            int sx = node.x;
            int sy = node.y;
            int sd = node.depth;
            int sk = node.k; // 지금까지 말처럼 움직인 횟수
            if(sx == H-1 &amp;amp;&amp;amp; sy == W-1) {
                ans = Math.min(ans, sd);
            }
            for(int i = 0 ; i &amp;lt; 4; i++) { // 평소처럼 이동하는 경우
                int nx = sx + dx[i];
                int ny = sy + dy[i];
                if(nx &amp;lt; 0 || ny &amp;lt; 0 || nx &amp;gt;= H || ny &amp;gt;= W) continue;
                if(sk &amp;gt; K || visited[nx][ny][sk] || map[nx][ny] == 1) continue;
                deque.offer(new Node(nx, ny, sd+1, sk));
                visited[nx][ny][sk] = true; // 평소대로 움직이므로 sk를 그대로
            }
            if(sk &amp;lt;= K-1) { // 말처럼 이동할 수 있는 경우
                for(int i = 0 ; i &amp;lt; 8; i++) { 
                    int nx = sx + kx[i];
                    int ny = sy + ky[i];
                    if(nx &amp;lt; 0 || ny &amp;lt; 0 || nx &amp;gt;= H || ny &amp;gt;= W) continue;
                    if(visited[nx][ny][sk+1] || map[nx][ny] == 1) continue;
                    deque.offer(new Node(nx, ny, sd+1, sk + 1));
                    visited[nx][ny][sk+1] = true; // 한 번 말처럼 이동하므로 sk+1 번째를 바꾼다.
                }
            }

        }
    }

    private static void solve() throws IOException {
        bfs(new Node(0,0,0, 0), new boolean[H][W][K+1]);
        if(ans==Integer.MAX_VALUE) {
            bufferedWriter.write(-1 + &quot;\n&quot;);
        } else {
            bufferedWriter.write(ans + &quot;\n&quot;);
        }
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
        bufferedWriter.close();
        bufferedReader.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>1600 java</category>
      <category>1600 자바 풀이</category>
      <category>백준 1600</category>
      <category>백준 1600 java</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/25</guid>
      <comments>https://simju9397.tistory.com/25#entry25comment</comments>
      <pubDate>Wed, 24 Mar 2021 15:36:11 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 코딩테스트 연습 : N으로 표현 Java 풀이 (DP 방식)</title>
      <link>https://simju9397.tistory.com/24</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42895&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;programmers.co.kr/learn/courses/30/lessons/42895&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1616498920093&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;코딩테스트 연습 - N으로 표현&quot; data-og-description=&quot;&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42895&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42895&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bybOeS/hyJDYX6AKX/PoUqSXDEvS3ZKzypE8IZuk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bgmIqC/hyJDVtwHHW/tQ64kKUQ9D39lQwitccnl1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42895&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42895&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bybOeS/hyJDYX6AKX/PoUqSXDEvS3ZKzypE8IZuk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bgmIqC/hyJDVtwHHW/tQ64kKUQ9D39lQwitccnl1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;코딩테스트 연습 - N으로 표현&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;오랜만에 프로그래머스 문제를 풀었다.&lt;/p&gt;
&lt;p&gt;약 1년 전에 풀다가 못 풀겠어서 포기했던 문제인데, 오늘 드디어 풀었다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;1. N과 number 가 같은 경우, 1을 리턴해야 한다. (&lt;span style=&quot;color: #333333;&quot;&gt;TC 9번)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;2. 최대로 쓸 수 있는 N의 갯수가 8이므로, 1번부터 8번까지 표현할 수 있도록 Integer 를 담는 Set 배열을 9 크기로 만들어준다. 무슨 말이냐면,&lt;/p&gt;
&lt;p&gt;&lt;b&gt;set[1] =&amp;gt; N을 1개 썼을 때 나올 수 있는 숫자들&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;set[2] =&amp;gt; N을 2개 썼을 때 나올 수 있는 숫자들&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이라는 말이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;3. N을 여러개 이어서 붙이는 것, 즉 예시에서는 5를 2개 붙인 숫자인 55 가 있었다. 이미 계산된 결과의 앞, 뒤에 N을 붙이는 경우는 고려하지 않는다.&lt;/p&gt;
&lt;p&gt;예를 들어, set[2] 에 5*5 해서 25 가 있을 텐데, 255 나 525 같은 방식은 고려하지 않는다는 것이다. 그러므로, set[8] 까지 5, 55, 555, 5555.... 만 생각하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;4. 마지막으로, set[i-1] 번째의 숫자들에 N을 사칙연산으로 붙여주면 TC 5번부터 8번까지 해결할 수 없다. 왜냐면, 이는 결국&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;set[i] =&lt;/span&gt; N을 i-1번 써서 만들 수 있는 숫자들 ( 사칙연산자 ) N&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;이라는 것인데, 괄호를 고려해야 한다.&lt;/p&gt;
&lt;p&gt;만약, i가 4이라면 위의 경우에서는&lt;/p&gt;
&lt;p&gt;(5+5+5)+5&lt;/p&gt;
&lt;p&gt;의 경우만 고려한다는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우리가 고려해야 할 경우는&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;(5+5+5)+5&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;5 + (5+5+5)&lt;/p&gt;
&lt;p&gt;(5+5) + 5 + 5&lt;/p&gt;
&lt;p&gt;5 + (5+5) + 5&lt;/p&gt;
&lt;p&gt;5 + 5 + (5 + 5)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉, &lt;b&gt;set[i] = set[i-1] (사칙연산) set[1] + &lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;set[i-2] (사칙연산) set[2] + ... + &lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;set[2] (사칙연산) set[i-2]+ &lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;set[1] (사칙연산) set[i-1]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;5. 주의해야 할점은, 같은 숫자들이 엄청나게 많아질 수 있다는 것이다. 메모리 초과가 날 수도 있다. set[2] 에 11 이 여러 개 있다면, 그 수에 대한 사칙연산 결과는 모두 똑같으므로 비효율적이다. 즉 중복 제거가 필요하므로, set 을 이용하는 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Source Code&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1616499767102&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;

public class Solution {
    public int solution(int N, int number) {
        if(N == number) {
            return 1;
        }
        TreeSet&amp;lt;Integer&amp;gt;[] possibleNumbers = new TreeSet[9];
        for(int i = 1; i &amp;lt;= 8; i++) {
            possibleNumbers[i] = new TreeSet&amp;lt;&amp;gt;();
        }
        possibleNumbers[1].add(N);
        for(int i = 2; i &amp;lt;= 8; i++) {
            String nn = &quot;&quot;;
            for(int o = 0; o &amp;lt; i; o++) {
                nn += N;
            }
            possibleNumbers[i].add(Integer.parseInt(nn));
            if(Integer.parseInt(nn) == number) {
                return i;
            }
            for(int k = 1; k &amp;lt; i; k++) {
                Iterator&amp;lt;Integer&amp;gt; kIter = possibleNumbers[k].iterator();
                Iterator&amp;lt;Integer&amp;gt; pIter = possibleNumbers[i-k].iterator();
                while (kIter.hasNext()) {
                    Integer n1 = kIter.next();
                    HashSet&amp;lt;Integer&amp;gt; possibles = new HashSet&amp;lt;&amp;gt;();
                    while (pIter.hasNext()) {
                        Integer n2 = pIter.next();
                        possibles.add(n1 + n2);
                        possibles.add(n1 - n2);
                        possibles.add(n1 * n2);
                        if(n2 != 0) {
                            possibles.add(n1 / n2);
                        }
                    }
                    if(possibles.contains(number)) {
                        return i;
                    }
                    possibleNumbers[i].addAll(possibles);
                    pIter = possibleNumbers[i-k].iterator();
                    //이거 중요한데,kIter 를 돌면서 pIter를 한번 다 썼으므로 다시 초기화해야한다.
                }
            }
        }
        System.out.println(Arrays.toString(possibleNumbers));
        return -1;
    }

    public static void main(String[] args) {
        System.out.println(new Solution_N으로표현().solution(4, 17));
        //System.out.println(new Solution_N으로표현().solution(2, 11));
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/Programmers</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/24</guid>
      <comments>https://simju9397.tistory.com/24#entry24comment</comments>
      <pubDate>Tue, 23 Mar 2021 20:43:20 +0900</pubDate>
    </item>
    <item>
      <title>백준 1149번 : RGB 거리 풀이 (Java) - DP</title>
      <link>https://simju9397.tistory.com/23</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1149&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/1149&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1616498187300&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;1149번: RGB거리&quot; data-og-description=&quot;첫째 줄에 집의 수 N(2 &amp;le; N &amp;le; 1,000)이 주어진다. 둘째 줄부터 N개의 줄에는 각 집을 빨강, 초록, 파랑으로 칠하는 비용이 1번 집부터 한 줄에 하나씩 주어진다. 집을 칠하는 비용은 1,000보다 작거나&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1149&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1149&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hfOpL/hyJDUOMVfX/jvpVTFC0XaFnMMIE1WfSGk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1149&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1149&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hfOpL/hyJDUOMVfX/jvpVTFC0XaFnMMIE1WfSGk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;1149번: RGB거리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 집의 수 N(2 &amp;le; N &amp;le; 1,000)이 주어진다. 둘째 줄부터 N개의 줄에는 각 집을 빨강, 초록, 파랑으로 칠하는 비용이 1번 집부터 한 줄에 하나씩 주어진다. 집을 칠하는 비용은 1,000보다 작거나&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W2uM3/btq0LO0340x/Pz6lWKwsk6f7OSEqhxb6q1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W2uM3/btq0LO0340x/Pz6lWKwsk6f7OSEqhxb6q1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W2uM3/btq0LO0340x/Pz6lWKwsk6f7OSEqhxb6q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW2uM3%2Fbtq0LO0340x%2FPz6lWKwsk6f7OSEqhxb6q1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;한 번 풀어본 문제라서 쉽게 풀었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;1. 이 문제는 DP 문제이다. N번 집은 N-1 번 집의 색과 같으면 안된다. 세 번째 조건인&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;i(2 &amp;le; i &amp;le; N-1)번 집의 색은 i-1번, i+1번 집의 색과 같지 않아야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;에서 좀 헷갈릴 수 있는데, 어차피 N-1 번의 집과 N번의 집을 다르게 칠한다면, N+1번도 N번과 다르게 칠하므로 상관없다.&lt;/p&gt;
&lt;p&gt;2. 그러므로, 집의 개수 N * 칠할 수 있는 색의 종류 수 3 의 크기의 2차원 배열 dp 를 만든다.&lt;/p&gt;
&lt;p&gt;3. dp[i][0] 번은 i번째의 집에 0번 색을 칠한다는 의미이고, 0번 색을 칠하는 데 드는 비용은&lt;/p&gt;
&lt;p&gt;&amp;nbsp;i 번째 집에 0번을 칠하는 비용 + i-1번째 집에 1번을 칠하는 비용과 2번을 칠하는 비용중에 작은 비용&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;DP에서는 쉬운 문제이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Source Code&lt;/h4&gt;
&lt;pre id=&quot;code_1616498780269&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.StringTokenizer;

public class Main {
    private static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    private static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    private static int N, min;
    private static int[][] costs;
    private static int[][] dp;

    private static void input() throws IOException {
        N = Integer.parseInt(bufferedReader.readLine());
        costs = new int[N][3];
        dp = new int[N][3];
        min = Integer.MAX_VALUE;
        for(int i = 0 ; i &amp;lt; N; i++) {
            StringTokenizer st = new StringTokenizer(bufferedReader.readLine());
            for(int j = 0 ; j &amp;lt; 3; j++) {
                costs[i][j] = Integer.parseInt(st.nextToken());
                if(i == 0) {
                    dp[0][j] = costs[0][j];
                }
            }
        }
    }

    private static void solve() throws IOException {
        for(int i = 1; i &amp;lt; N; i++) {
            dp[i][0] = costs[i][0] + Math.min(dp[i-1][1], dp[i-1][2]);
            dp[i][1] = costs[i][1] + Math.min(dp[i-1][0], dp[i-1][2]);
            dp[i][2] = costs[i][2] + Math.min(dp[i-1][0], dp[i-1][1]);
        }
        min = Math.min(Math.min(dp[N-1][0], dp[N-1][1]), dp[N-1][2]);
        System.out.println(min);
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>java 1149</category>
      <category>java 1149 번</category>
      <category>백준 1149</category>
      <category>백준 1149 풀이</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/23</guid>
      <comments>https://simju9397.tistory.com/23#entry23comment</comments>
      <pubDate>Tue, 23 Mar 2021 20:26:53 +0900</pubDate>
    </item>
    <item>
      <title>백준 1302번 : 베스트셀러 풀이 (Java)</title>
      <link>https://simju9397.tistory.com/22</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1302&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/1302&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1616306856792&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;1302번: 베스트셀러&quot; data-og-description=&quot;첫째 줄에 오늘 하루 동안 팔린 책의 개수 N이 주어진다. 이 값은 1,000보다 작거나 같은 자연수이다. 둘째부터 N개의 줄에 책의 제목이 입력으로 들어온다. 책의 제목의 길이는 50보다 작거나 같고&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1302&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1302&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bkSmtQ/hyJCqUzkXy/9V32svuOCGbhg1ADIexM51/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1302&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1302&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bkSmtQ/hyJCqUzkXy/9V32svuOCGbhg1ADIexM51/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;1302번: 베스트셀러&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 오늘 하루 동안 팔린 책의 개수 N이 주어진다. 이 값은 1,000보다 작거나 같은 자연수이다. 둘째부터 N개의 줄에 책의 제목이 입력으로 들어온다. 책의 제목의 길이는 50보다 작거나 같고&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDgGlG/btq0EDDvXeh/a8Lt5pmRHkjqDKfZhp3uq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDgGlG/btq0EDDvXeh/a8Lt5pmRHkjqDKfZhp3uq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDgGlG/btq0EDDvXeh/a8Lt5pmRHkjqDKfZhp3uq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDgGlG%2Fbtq0EDDvXeh%2Fa8Lt5pmRHkjqDKfZhp3uq1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이 문제를 풀고 나서 깨달았다.&lt;/p&gt;
&lt;p&gt;어제 라인 코딩테스트의 반례가 여기 있었다는 걸 말이다. ㅠㅠ&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 문제는 로직 자체는 쉽지만, &quot;사전순 정렬&quot;을 해본 적이 없다면 주의해야 한다.&lt;/p&gt;
&lt;p&gt;나는 java로 string 을 사전 순으로 정렬하는 것을 처음 해봤는데, 어제 LINE 코딩 테스트에 나왔었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사전 순이면,&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;ab, a 일 경우에&lt;/p&gt;
&lt;p&gt;a 가 먼저 나오고 ab 가 다음에 나온다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;나는 단순하게 0번째 문자로만 비교를 했었다. 그렇기 때문에 위의 경우에서 ab, a 가 되어버렸다.&lt;/p&gt;
&lt;p&gt;하지만 java의 string 에 훌륭한 메소드가 있었는데, 바로 compareTo 메소드이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;string1.compareTo(string2) 의 리턴 형태는 int 이다.&lt;/p&gt;
&lt;p&gt;0일 경우 같은 문자열이고,&lt;/p&gt;
&lt;p&gt;음수일 경우 string1 이 사전 순으로 앞이고, 양수일 경우 string2 가 앞이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이것만 알면 틀릴 리가 없는 문제이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Source Code&lt;/h4&gt;
&lt;pre id=&quot;code_1616307241400&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Main {
    private static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    private static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    private static int N;
    private static HashMap&amp;lt;String, Integer&amp;gt; map;
    private static void input() throws IOException {
        N = Integer.parseInt(bufferedReader.readLine());
        map = new HashMap&amp;lt;&amp;gt;();
        for(int i = 0 ; i &amp;lt; N; i++) {
            String bookName = bufferedReader.readLine();
            Integer bookSoldCount = map.get(bookName);
            if(bookSoldCount == null) {
                map.put(bookName, 1);
            } else {
                map.put(bookName, bookSoldCount + 1);
            }
        }
    }

    private static void solve() throws IOException {
        Iterator&amp;lt;String&amp;gt; bookNames = map.keySet().iterator();
        int count = 0;
        String answer = &quot;&quot;;
        while(bookNames.hasNext()) {
            String bookName = bookNames.next();
            Integer bookSoldCnt = map.get(bookName);
            if(count &amp;lt; bookSoldCnt) {
                answer = bookName;
                count = bookSoldCnt;
            } else if(count == bookSoldCnt) {
                answer = answer.compareTo(bookName) &amp;lt; 0 ? answer : bookName;
            }
        }
        System.out.println(answer);
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>java 1302</category>
      <category>백준 1302 java</category>
      <category>백준 1302 풀이</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/22</guid>
      <comments>https://simju9397.tistory.com/22#entry22comment</comments>
      <pubDate>Sun, 21 Mar 2021 15:14:20 +0900</pubDate>
    </item>
    <item>
      <title>백준 14889번 : 스타트와 링크 풀이 (Java)</title>
      <link>https://simju9397.tistory.com/21</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14889&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/14889&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1615976610700&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;14889번: 스타트와 링크&quot; data-og-description=&quot;예제 2의 경우에 (1, 3, 6), (2, 4, 5)로 팀을 나누면 되고, 예제 3의 경우에는 (1, 2, 4, 5), (3, 6, 7, 8)로 팀을 나누면 된다.&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/14889&quot; data-og-url=&quot;https://www.acmicpc.net/problem/14889&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/JX65J/hyJBjfGHIZ/DWJ824GfoEb8ISv3WXKLxk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14889&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/14889&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/JX65J/hyJBjfGHIZ/DWJ824GfoEb8ISv3WXKLxk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;14889번: 스타트와 링크&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;예제 2의 경우에 (1, 3, 6), (2, 4, 5)로 팀을 나누면 되고, 예제 3의 경우에는 (1, 2, 4, 5), (3, 6, 7, 8)로 팀을 나누면 된다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNJF0R/btq0kEwTF2b/zST0t7x1ibEH4u511bV4Z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNJF0R/btq0kEwTF2b/zST0t7x1ibEH4u511bV4Z1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNJF0R/btq0kEwTF2b/zST0t7x1ibEH4u511bV4Z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNJF0R%2Fbtq0kEwTF2b%2FzST0t7x1ibEH4u511bV4Z1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;&lt;b&gt;조합 + 브루트포스&lt;/b&gt; 문제이다.&lt;/p&gt;
&lt;p&gt;1번부터 N번까지 중에, start 팀에 들어갈 N / 2 명을 뽑으면 나머지는 자연스레 link 팀이 된다.&lt;/p&gt;
&lt;p&gt;나는 isStart 라는 boolean 배열에 뽑은 결과를 기반으로 true 면 start 팀, false 면 link 팀으로 생각하고,&lt;/p&gt;
&lt;p&gt;이중 for문을 돌면서 값을 각각 더해줬다. 별로 어렵지 않으니 코드를 보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Source Code&lt;/h4&gt;
&lt;pre id=&quot;code_1615977022369&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.StringTokenizer;

public class Main {
    private static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    private static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    private static int N, startSum, linkSum, answer;
    private static int[] people;
    private static boolean[] isStart;
    private static int[][] S;
    private static void input() throws IOException {
        N = Integer.parseInt(bufferedReader.readLine());
        people = new int[N];
        S = new int[N][N];
        startSum = 0;
        linkSum = 0;
        answer = Integer.MAX_VALUE;
        isStart = new boolean[N];
        for(int i = 0 ; i &amp;lt; N; i++) {
            StringTokenizer st = new StringTokenizer(bufferedReader.readLine());
            people[i] = i+1;
            for(int j = 0; j &amp;lt; N; j++) {
                S[i][j] = Integer.parseInt(st.nextToken());
            }
        }
    }

    private static void startCombi(int cnt, int start, int[] arr) {
        if(cnt == N / 2) {
            for(int i = 0 ; i &amp;lt; N/2; i++) {
                isStart[arr[i] - 1] = true;
            }
            for(int i = 0; i &amp;lt; N-1; i++) {
                for(int j = i+1; j &amp;lt; N; j++) {
                    if(isStart[i] &amp;amp;&amp;amp; isStart[j]) {
                        startSum += S[i][j] + S[j][i];
                    } else if(!isStart[i] &amp;amp;&amp;amp; !isStart[j]) {
                        linkSum += S[i][j] + S[j][i];
                    }
                }
            }
            answer = Math.min(answer, Math.abs(linkSum - startSum));
            startSum = 0;
            linkSum = 0;
            isStart = new boolean[N];
            // 초기화 해주는 것 잊지말자. 전역변수이기 때문이다.
            return;
        }
        for(int i = start; i &amp;lt; N; i++) {
            arr[cnt] = people[i];
            startCombi(cnt+1, i + 1, arr);
        }
    }

    private static void solve() throws IOException {
        startCombi(0, 0, new int[N/2]);
        System.out.println(answer);
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
        bufferedReader.close();
        bufferedWriter.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>14889 java</category>
      <category>Java</category>
      <category>백준 14889번</category>
      <category>스타트와링크</category>
      <category>스타트와링크 java</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/21</guid>
      <comments>https://simju9397.tistory.com/21#entry21comment</comments>
      <pubDate>Wed, 17 Mar 2021 19:30:52 +0900</pubDate>
    </item>
    <item>
      <title>백준 16236번 : 아기 상어 풀이(Java)</title>
      <link>https://simju9397.tistory.com/20</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/16236&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/16236&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1615943519880&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;16236번: 아기 상어&quot; data-og-description=&quot;N&amp;times;N 크기의 공간에 물고기 M마리와 아기 상어 1마리가 있다. 공간은 1&amp;times;1 크기의 정사각형 칸으로 나누어져 있다. 한 칸에는 물고기가 최대 1마리 존재한다. 아기 상어와 물고기는 모두 크기를 가&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/16236&quot; data-og-url=&quot;https://www.acmicpc.net/problem/16236&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/evqIWT/hyJz7HVgTz/kK59hsNT7i36ZJVGDkQVck/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/16236&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/16236&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/evqIWT/hyJz7HVgTz/kK59hsNT7i36ZJVGDkQVck/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;16236번: 아기 상어&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;N&amp;times;N 크기의 공간에 물고기 M마리와 아기 상어 1마리가 있다. 공간은 1&amp;times;1 크기의 정사각형 칸으로 나누어져 있다. 한 칸에는 물고기가 최대 1마리 존재한다. 아기 상어와 물고기는 모두 크기를 가&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qcuZl/btq0jOr17Wh/3on5IVuiPTJ3Oxshc6PWI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qcuZl/btq0jOr17Wh/3on5IVuiPTJ3Oxshc6PWI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qcuZl/btq0jOr17Wh/3on5IVuiPTJ3Oxshc6PWI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqcuZl%2Fbtq0jOr17Wh%2F3on5IVuiPTJ3Oxshc6PWI0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;최근에 한 번 풀었던 문제를 사정이 있어 다시 풀게 되었는데, 빠르게 풀어서 기분이 좋다. 이런 구현 문제는 정말 조건을 잘 생각하고 그에 맞춰 구현 가능하면 금방 풀리는 데, 아니면 엄청 오래 걸리는 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;1. 먼저, 아기 상어의 이동의 종료 조건은 다음과 같다.&lt;/p&gt;
&lt;p&gt;- 더 이상 먹을 수 있는 물고기가 없을 때(물고기가 아예 없거나, 남은 물고기가 전부 자신보다 크기가 크거나 &lt;b&gt;같을 때&lt;/b&gt;)&lt;/p&gt;
&lt;p&gt;2. 먹을 수 있는 물고기가 한 마리 남았으면 걔를 먹으면 된다.&lt;/p&gt;
&lt;p&gt;3. 한 마리 이상이라면,&lt;/p&gt;
&lt;p&gt;- 1순위 : 가장 가까운 물고기&lt;/p&gt;
&lt;p&gt;- 2순위 : 가장 가까운 물고기가 여러 마리면, 가장 위에 있는 물고기&lt;/p&gt;
&lt;p&gt;- 3순위 : 가장 가깝고 x 축의 값마저 같은 물고기가 여러 마리면, y 값이 가장 작은 물고기&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이를 축약하면 결국,&amp;nbsp;&lt;b&gt;젤 가깝고 젤 위에 있고 젤 왼쪽에 있는 물고기&lt;/b&gt; 를 먹는다고 생각하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;3.&amp;nbsp;&lt;b&gt;가장 가까운&lt;/b&gt; 물고기, 즉 최단거리를 구해야 한다. 최단거리를 구하는 문제는 대부분이 BFS 로 풀 수 있다.&lt;/p&gt;
&lt;p&gt;4. 주의할 점&lt;/p&gt;
&lt;p&gt;- 첫 번째 : 가장 가까운 물고기는&amp;nbsp;&lt;b&gt;여러 마리 일 수 있다.&lt;/b&gt; 위에서도 말한 건데, 즉 처음으로 먹을 수 있는(상어보다 크기가 작은) 물고기를 만났다고 해서, &lt;b&gt;바로 while 문을 끝내버리면 안된다&lt;/b&gt;는 것이다. 물고기를 처음 만났을 때의 거리를 최소 거리로 저장해두고 depth 가 그 최소 거리 이상이 되는 시점에서 끝내버리는 것도 가능하지만, 이 문제의 N은 최대가 20밖에 안되기 때문에 안 그래도 충분히 통과가 된다.&lt;/p&gt;
&lt;p&gt;- 두 번째 : 여러 마리를 담았다면, 가장 가까운 물고기가 2마리 이상이라는 뜻이고, 1순위 x, 2순위 y 에 따라&amp;nbsp;&lt;b&gt;정렬 해야 한다.&lt;/b&gt; Java 의 Comparable 인터페이스를 구현하는 방법과 Collections.sort 를 안다면 이는 어렵지 않다.&lt;/p&gt;
&lt;p&gt;- 세 번째 : 그렇게 정렬까지 마쳤다면, &lt;b&gt;한 턴에 먹을 수 있는 물고기는 &quot;한 마리&quot;&lt;/b&gt; 임을 꼭 생각해야 한다. 정렬을 했으므로, 먹을 수 있는 물고기들 중에 0번째만 먹어야 한다는 것이다.&lt;/p&gt;
&lt;p&gt;- 네 번째 : 물고기를 먹었다면,&amp;nbsp;&lt;b&gt;해당 물고기가 있던 위치는 더 이상 아무것도 없게 되므로 0 으로 만들어 준다.&lt;/b&gt; 또한, 상어가 그 물고기를 먹으러 이동했으므로,&amp;nbsp;&lt;b&gt;상어를 해당 물고기가 있던 위치&lt;/b&gt; 로 이동시켜줘야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이를 모두 숙지했다면 구현이 어렵지 않다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Source Code&lt;/h4&gt;
&lt;pre id=&quot;code_1615944144053&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    static int N, time;
    static int[][] map;
    static Shark shark; // 상어의 정보
    static ArrayList&amp;lt;Fish&amp;gt; fishes; // 현재 남아있는 물고기들
    static int[] dx = {-1,1,0,0};
    static int[] dy = {0,0,-1,1};

    static class Node {
        int x;
        int y;
        int depth; // 상어로부터의 거리

        public Node(int x, int y, int depth) {
            this.x = x;
            this.y = y;
            this.depth = depth;
        }
    }
    static class Fish extends Node implements Comparable&amp;lt;Fish&amp;gt;{
    	// Fish 가 Node 를 상속받게 함으로써, 상어로부터의 거리 또한 가지게 한다.
        int size;

        public Fish(int x, int y, int depth, int size) {
            super(x, y, depth);
            this.size = size;
        }

        @Override
        public int compareTo(Fish o) {
            return this.x==o.x?this.y-o.y : this.x-o.x;
        }
    }

    static class Shark {
        int x;
        int y;
        int size;
        int eatFishCount; // 먹은 물고기 수가 size 가 된 순간, size++ 한 뒤 0으로 만들어줘야 한다.

        public Shark(int x, int y, int size, int eatFishCount) {
            this.x = x;
            this.y = y;
            this.size = size;
            this.eatFishCount = eatFishCount;
        }
    }

    static void input() throws IOException {
        N = Integer.parseInt(bufferedReader.readLine());
        map = new int[N][N];
        fishes = new ArrayList&amp;lt;&amp;gt;();
        for(int i = 0 ; i &amp;lt; N; i++) {
            StringTokenizer st = new StringTokenizer(bufferedReader.readLine());
            for(int j = 0 ; j &amp;lt; N; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
                if(map[i][j] == 9) {
                    shark = new Shark(i,j,2,0); // 초기 사이즈가 2인 아기 상어의 위치를 저장해준다.
                } else if(map[i][j] &amp;gt;= 1 &amp;amp;&amp;amp; map[i][j] &amp;lt;= 6) {
                    fishes.add(new Fish(i,j, map[i][j], 0));
                    // 초기 물고기들의 위치와 크기를 저장해준다.
                }
            }
        }
    }

    static boolean bfs(boolean[][] visited) {
        Queue&amp;lt;Node&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;();
        queue.add(new Node(shark.x, shark.y, 0)); 
        // 상어의 위치부터 시작하고, 상어부터 시작하므로 depth 는 0으로 시작한다.
        visited[shark.x][shark.y] = true;
        ArrayList&amp;lt;Fish&amp;gt; canEatFishes = new ArrayList&amp;lt;&amp;gt;();
        while (!queue.isEmpty()) {
            Node node = queue.poll();
            int sx = node.x;
            int sy = node.y;
            int sDepth = node.depth;
            for(int i = 0 ; i &amp;lt; 4; i++) {
                int nx = sx + dx[i];
                int ny = sy + dy[i];
                if(nx &amp;lt; 0 || ny &amp;lt; 0 || nx &amp;gt;= N || ny &amp;gt;= N) continue;
                if(visited[nx][ny]) continue;
                if(map[nx][ny] &amp;gt; 0 &amp;amp;&amp;amp; map[nx][ny] != 9) {
                //해당 위치에 물고기가 있다면
                    if(shark.size &amp;gt; map[nx][ny]) { // 상어보다 작은 물고기만 먹을 수 있다.
                        if(canEatFishes.size() == 0) { // 아직 먹을 수 있는 물고기가 하나도 없다면 추가
                            canEatFishes.add(new Fish(nx,ny,sDepth+1, map[nx][ny]));
                        } else if(canEatFishes.get(canEatFishes.size() - 1).depth == sDepth + 1) {
                        	//먹을 수 있는 물고기가 하나 이상일 때, 먹을 수 있는 물고기보다
                            //거리가 먼 물고기는 담지 않게 한다.
                            canEatFishes.add(new Fish(nx,ny,sDepth+1, map[nx][ny]));
                        }

                    } else if(shark.size &amp;lt; map[nx][ny]) { // 상어보다 큰 물고기는 지나갈 수도 없다.
                        continue;
                    }
                    // 여기의 else 에는 자연스레, 상어랑 크기가 같은 물고기는 지나갈 수만 있다는 걸 암시한다.
                }
                visited[nx][ny] = true;
                queue.add(new Node(nx, ny, sDepth+1));
            }
        }
        Collections.sort(canEatFishes);
        // compareTo 메서드에 따라 정렬해준다. 같은 물고기가 여럿일 경우
        // x가 작은 물고기, y가 작은 물고기 순서대로 정렬된다.
        if(canEatFishes.size() == 0) {
            return false;
            // 먹을 수 있는 물고기가 아예 없다면, 반복문을 종료하기 위해 false 를 리턴한다.
        }
        Fish eatFish = canEatFishes.get(0);
        // 이미 정렬됐으므로, 먹을 물고기는 0번째에 있다.
        int idx = 0;
        for(int i = 0 ; i &amp;lt; fishes.size(); i++) {
            Fish fish = fishes.get(i);
            if(fish.x == eatFish.x &amp;amp;&amp;amp; fish.y == eatFish.y) {
            	// 물고기들 중에, 먹을 물고기는 제거해줘야 한다.
                idx = i; // 제거될 물고기의 인덱스를 저장해주고
                shark.eatFishCount++; // 상어의 포식 횟수를 늘려준 뒤,
                time += eatFish.depth; // 먹을 물고기까지의 거리만큼의 시간이 흐르고,
                shark.x = eatFish.x;
                shark.y = eatFish.y;
                // 먹을 물고기의 위치로 상어를 이동시킨다
                map[eatFish.x][eatFish.y] = 0;
                // 먹힌 물고기는 사라졌으므로 map 에 0으로 표시해준다.
                break;
            }
        }
        if(shark.eatFishCount == shark.size) {
        	// 포식 횟수가 상어의 크기와 같아진다면
            shark.size++; // 상어의 크기를 늘려주고
            shark.eatFishCount = 0; // 포식 횟수를 초기화 한다.
        }
        fishes.remove(idx);
        // 물고기를 제거한다.
        //System.out.println(eatFish.x + &quot; &quot; + eatFish.y + &quot; 먹음, time : &quot; + time);
        return true;
    }

    static void solve() {
        while (!fishes.isEmpty()) { // 물고기가 아예 없거나
            if(!bfs(new boolean[N][N])) { // 먹을 수 있는 물고기가 없을 때 까지 돌린다.
                break;
            }
        }
        System.out.println(time);
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
        bufferedReader.close();
        bufferedWriter.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>16236번</category>
      <category>bfs</category>
      <category>BOJ</category>
      <category>Java</category>
      <category>ps</category>
      <category>queue</category>
      <category>백준</category>
      <category>아기상어</category>
      <category>풀이</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/20</guid>
      <comments>https://simju9397.tistory.com/20#entry20comment</comments>
      <pubDate>Wed, 17 Mar 2021 10:31:01 +0900</pubDate>
    </item>
    <item>
      <title>백준 1759번 : 암호 만들기 풀이 및 Java 로 순열, 조합, 부분집합 만들기</title>
      <link>https://simju9397.tistory.com/19</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1759&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/1759&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1615895780138&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;1759번: 암호 만들기&quot; data-og-description=&quot;첫째 줄에 두 정수 L, C가 주어진다. (3 &amp;le; L &amp;le; C&amp;nbsp;&amp;le; 15) 다음 줄에는 C개의 문자들이 공백으로 구분되어 주어진다. 주어지는 문자들은 알파벳 소문자이며, 중복되는 것은 없다.&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1759&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1759&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/p8NV8/hyJz8sZqeN/gFB3R5yRwp982sl5ksSNhK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1759&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1759&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/p8NV8/hyJz8sZqeN/gFB3R5yRwp982sl5ksSNhK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;1759번: 암호 만들기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 두 정수 L, C가 주어진다. (3 &amp;le; L &amp;le; C&amp;nbsp;&amp;le; 15) 다음 줄에는 C개의 문자들이 공백으로 구분되어 주어진다. 주어지는 문자들은 알파벳 소문자이며, 중복되는 것은 없다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TYuiT/btq0kDKkhi0/KFp2owckmCJpkRh6Flg0wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TYuiT/btq0kDKkhi0/KFp2owckmCJpkRh6Flg0wK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TYuiT/btq0kDKkhi0/KFp2owckmCJpkRh6Flg0wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTYuiT%2Fbtq0kDKkhi0%2FKFp2owckmCJpkRh6Flg0wK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;골드라고 하기엔 많이 쉬운 문제같다. 평소에 조합 짜는 코드를 잘 알고 있다는 가정 하에..&lt;/p&gt;
&lt;p&gt;해법은 아주 간단하다. 우선&amp;nbsp;&lt;/p&gt;
&lt;p&gt;3 &amp;lt;= L, C &amp;lt;= 15 로 범위가 굉장히 작고, 중복되는 알파벳 소문자도 없으므로 그냥 별 생각없이 조합 + 완전탐색을 생각했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. C개의 chars 중에서 L개의 char 배열인 arr 을 뽑아내야 한다. 조합을 사용하자.&lt;/p&gt;
&lt;p&gt;2. 뽑아낸 L개의 배열 중에서 모음의 개수와 자음의 개수를 세어준다. 모음의 종류들을 미리 List 에 저장해두고, 거기 있으면 모음 아니면 자음으로 세면 된다. 모음 최소 1개, 자음 최소 2개 이상이므로 해당 조건을 만족하지 못하면 결과에 출력하지 않는다.&lt;/p&gt;
&lt;p&gt;3. 해당 조건 말고는 없으므로, 정렬해서 알파벳 순서로 만들어준 뒤 String 으로 합쳐주면 끝이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;워낙 별 게 없지만, 검색이 제한되는 환경이었다면 조금 고생했을 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. 모음 종류를 List 로 만들기&lt;/p&gt;
&lt;pre id=&quot;code_1615896073087&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static List&amp;lt;Character&amp;gt; vowelTypes = Arrays.asList('a','e','i','o','u');&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;List.contains 메서드를 사용하기 위해서 만드려고 했는데, Arrays.asList 는 기억이 났는데 안에 들어가는 매개변수를 { } 로 감싸야하는지, [ ] 로 감싸야하는지 몰라서 둘 다 해봤는데 컴파일 에러가 났었다. 결론은 저렇게 그냥 콤마로 구분해서 넣으면 되더라.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. 조합 코드&lt;/p&gt;
&lt;p&gt;꽤 많이 짜왔다고 생각하는데 아직도 한 번씩 헷갈린다. 순열, 조합, 부분집합 코드는 암기를 해서 사용하면 편하다. 시험 환경에서 snippet 으로 저장해둬도 되지만 IDE 가 허용되지 않는 시험에서는 그럼 어떡할 것인가. 외우는게 낫다. 순열, 조합, 부분집합 코드를 저장해둔 것을 공유한다. 필요없다면 넘기자.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1615896309560&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.Scanner;

public class Main {
    static int N, R = 2;
    static int[] nums;
    public static void main(String[] args) {
        System.out.print(&quot;입력할 갯수 : &quot;);
        Scanner sc = new Scanner(System.in);
        N = sc.nextInt();
        nums = new int[N];
        for(int i = 0 ; i &amp;lt; N; i++) {
            nums[i] = sc.nextInt();
        }
        System.out.println(&quot;1. 조합의 결과&quot;);
        makeCombination(0,0,new int[R]);
        System.out.println(&quot;2. 순열의 결과&quot;);
        makePermutation(0, new int[N], new boolean[N]);
        System.out.println(&quot;3. 부분집합의 결과&quot;);
        makeSubset(0, new boolean[N]);
    }

    private static void makeCombination(int cnt, int start, int[] arr) {
        if(cnt == R) {
            for(int i = 0 ; i &amp;lt; R; i++) {
                System.out.print(arr[i] + &quot; &quot;);
            }
            System.out.println();
            return;
        }
        for(int i = start; i &amp;lt; N; i++) {
            arr[cnt] = nums[i];
            makeCombination(cnt+1, i+1, arr);
        }
    }

    private static void makePermutation(int cnt, int[] arr, boolean[] visited) {
        if(cnt == N) {
            for(int i = 0 ; i &amp;lt; N; i++) {
                if(visited[i]) System.out.print(arr[i] + &quot; &quot;);
            }
            System.out.println();
            return;
        }
        for(int i = 0 ; i &amp;lt; N; i++) {
            if(visited[i]) continue;
            visited[i] = true;
            arr[cnt] = nums[i];
            makePermutation(cnt + 1, arr, visited);
            visited[i] = false;
        }
    }

    private static void makeSubset(int cnt, boolean[] isSelected) {
        if(cnt == N) {
            for(int i = 0 ; i &amp;lt; N; i++) {
                if(isSelected[i]) System.out.print(nums[i] + &quot; &quot;);
            }
            System.out.println();
            return;
        }
        isSelected[cnt] = true;
        makeSubset(cnt+1, isSelected);
        isSelected[cnt] = false;
        makeSubset(cnt + 1, isSelected);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Source Code&lt;/h4&gt;
&lt;pre id=&quot;code_1615896409945&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    static int L,C;
    static char[] chars;
    static List&amp;lt;Character&amp;gt; vowelTypes = Arrays.asList('a','e','i','o','u');

    static void input() throws IOException {
        StringTokenizer lc = new StringTokenizer(bufferedReader.readLine());
        L = Integer.parseInt(lc.nextToken());
        C = Integer.parseInt(lc.nextToken());
        chars = new char[C];
        StringTokenizer line = new StringTokenizer(bufferedReader.readLine());
        for(int i = 0 ; i &amp;lt; C; i++) {
            chars[i] = line.nextToken().charAt(0);
        }
        Arrays.sort(chars);
    }

    static void checkString(char[] arr) throws IOException {
        int vowels = 0;
        int cons = 0;
        for(int i = 0 ; i &amp;lt; L; i++) {
            if(vowelTypes.contains(arr[i])) {
                vowels++; // 모음 갯수
            } else {
                cons++; // 자음 갯수
            }
        }
        if(vowels &amp;lt; 1 || cons &amp;lt; 2) {
            return;
        }
        Arrays.sort(arr);
        bufferedWriter.write(new String(arr) + &quot; \n&quot;);
    }

    static void makeCombination(int cnt, int start, char[] arr) throws IOException {
        if(cnt == L) {
            checkString(arr); // L개의 문자 배열이 완성되면 보낸다.
            return;
        }
        for(int i = start; i &amp;lt; C; i++) {
            arr[cnt] = chars[i];
            makeCombination(cnt+1, i+1, arr);
        }
    }

    static void solve() throws IOException {
        makeCombination(0,0,new char[L]);
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
        bufferedWriter.close();
        bufferedReader.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>1759</category>
      <category>BOJ</category>
      <category>Java</category>
      <category>java 조합</category>
      <category>ps</category>
      <category>백준</category>
      <category>부분집합</category>
      <category>순열</category>
      <category>조합</category>
      <category>풀이</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/19</guid>
      <comments>https://simju9397.tistory.com/19#entry19comment</comments>
      <pubDate>Tue, 16 Mar 2021 21:07:27 +0900</pubDate>
    </item>
    <item>
      <title>Express 로 만든 내 서버, AWS lambda 에다가 올리기  </title>
      <link>https://simju9397.tistory.com/18</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;개요&lt;/h4&gt;
&lt;p&gt;슬슬 공채 시즌 + SSAFY 5기의 삼성 SW역량테스트 A형 이 곧 다가와서 백준만 풀다가.. 정신이 나갈 것 같더라구요. 그런데 포폴을 쓰다가, AWS EC2 의 비용 문제로 스토어에서 내려버렸던( 지금 보니 바보짓이었어요 ) 저의 팀프로젝트 결과물을 다시 살려야 겠다는 생각을 했습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;비용은 한 달에 약 3만원 후반 대 정도였는데, 마케팅을 따로 하지 않았다 보니 사용자의 수에 비해 너무 과한 비용처럼 느껴졌습니다. 프리티어 쓰면 되지 않냐! 하실 수 있는데, 어떤 날은 10명 들어와서 그냥 서버가 터져버렸습니다. 그런 걸 방지하기 위해 ec2 티어를 올렸더니 또 유저가 안들어온 날이면 아.. 1300원 정도가 허공에 날아갔네 싶었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러던 중 Severless 와 AWS Lambda 의 존재를 알게 되었고,&amp;nbsp;&lt;b&gt;쓴 만큼 비용을 낸다!&lt;/b&gt; 는 것을 보고 적용해야겠다는 생각을 했습니다. 서비스는 production 환경은 lambda에서, dev 환경은 ec2 인스턴스에서 굴릴 예정입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;잡설이 길었는데, 얼른 시작해보겠습니다. 이번 글은, 별 설명없이 lambda 를 빠르게 적용해야 하는 분들에게 좋을 것 같습니다. 제 프로젝트의 환경은&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;- nodejs 14.x&lt;/p&gt;
&lt;p&gt;- express&lt;/p&gt;
&lt;p&gt;- http server&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1 단계 : express server를 lambda 에 deploy 하기&lt;/h4&gt;
&lt;p&gt;먼저, serverless 라는 패키지를 global 로 설치해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1615719311318&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install serverless -g&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;설치 되었는지 확인해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1615719366501&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ serverless --version
Framework Core: 2.29.0
Plugin: 4.5.0
SDK: 4.2.0
Components: 3.7.3&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 자신의 AWS 계정 정보를 입력해줘야 합니다. AWS 계정은 당연히 다 있다고 생각하고 진행합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vBY9m/btqZ0twlU1e/Yz6eu1cVUpdm4GzBhqq6q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vBY9m/btqZ0twlU1e/Yz6eu1cVUpdm4GzBhqq6q0/img.png&quot; data-alt=&quot;콘솔의 우상단에 있습니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vBY9m/btqZ0twlU1e/Yz6eu1cVUpdm4GzBhqq6q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvBY9m%2FbtqZ0twlU1e%2FYz6eu1cVUpdm4GzBhqq6q0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;콘솔의 우상단에 있습니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;[내 보안 자격 증명] 을 클릭해서 들어가줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAVG7c/btqZ0s5ggsz/aPi0EkA8IbhoyyMmMh9tI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAVG7c/btqZ0s5ggsz/aPi0EkA8IbhoyyMmMh9tI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAVG7c/btqZ0s5ggsz/aPi0EkA8IbhoyyMmMh9tI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAVG7c%2FbtqZ0s5ggsz%2FaPi0EkA8IbhoyyMmMh9tI0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;저는 이미 있는데, 새 엑세스 키 만들기를 해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VLFuU/btqZ2cHaC5u/urF8C22kn7sw7oB6oFgCgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VLFuU/btqZ2cHaC5u/urF8C22kn7sw7oB6oFgCgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VLFuU/btqZ2cHaC5u/urF8C22kn7sw7oB6oFgCgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVLFuU%2FbtqZ2cHaC5u%2FurF8C22kn7sw7oB6oFgCgk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이렇게 뜹니다.&amp;nbsp;&lt;b&gt;보안 액세스 키는 이 창 이후로 다시 볼 수 없습니다. 다운로드 해두시길 바랍니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 콘솔창으로 돌아와서,&lt;/p&gt;
&lt;pre id=&quot;code_1615719706005&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export AWS_ACCESS_KEY_ID=&amp;lt;your-key-here&amp;gt;
export AWS_SECRET_ACCESS_KEY=&amp;lt;your-secret-key-here&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;[액세스 키 ID] 가 위, [보안 액세스 키] 가 아래입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제, serverless.yml 을 루트 디렉토리에 하나 만들어주고, 다음을 입력합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1615719983669&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;service: {올릴 이름}
provider:
  name: aws
  runtime: nodejs14.x
  stage: production
  region: ap-northeast-2
  memorySize: 128
functions:
  app:
    handler: app/app.handler
    events: 
      - http: 
          path: /
          method: ANY
          cors: true
      - http: 
          path: /{proxy+}
          method: ANY
          cors: true&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 app.js 내에 약간의 수정이 필요합니다. 먼저 serverless-http 패키지를 설치해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1615721006646&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install serverless-http&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이후, app.js 를 다음과 같이 수정합니다.&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615721071324&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var express = require('express');
const serverless = require('serverless-http');
var app = express();

/** 나머지 코드들 **/

module.exports.handler = serverless(app);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;아마 많은 분들이 module.exports = app 을 설정해놨을 텐데, 사용해도 상관없는 것 같습니다.&lt;/p&gt;
&lt;p&gt;이제 다 끝났습니다! 다음 명령어를 입력해줍시다.&lt;/p&gt;
&lt;pre id=&quot;code_1615721152118&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;serverless deploy&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cnohm/btqZZTPyLHA/o0sfQS4qaAk9DlkMFMFTO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cnohm/btqZZTPyLHA/o0sfQS4qaAk9DlkMFMFTO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cnohm/btqZZTPyLHA/o0sfQS4qaAk9DlkMFMFTO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCnohm%2FbtqZZTPyLHA%2Fo0sfQS4qaAk9DlkMFMFTO1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;기다리다 보면 콘솔창에 좌라락 뜨는데, endpoints 부분이 우리가 접근할 수 있는 uri 입니다.&lt;/p&gt;
&lt;p&gt;이제 기존의 localhost:포트번호 대신, https:// 다음 부분부터 끝까지 입력해주시면 됩니다. 기존의 서버가 http 서버였다면 이렇게 해야하고, https 서버면 전체를 입력하면 될 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 aws 콘솔에서 lambda 로 들어가서 보시면 올려놓은 서버가 있을 겁니다. 그럼 끝입니다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;하지만?&lt;/h4&gt;
&lt;p&gt;저는 또 다른 문제점이 있었습니다. 외부 API 와 통신하는 기능이 있었는데, 해당 API 는 고정 IP 를 등록해두고 사용하는 방식이었습니다. 하지만 Lambda 는 고정 ip 가 아니라, 계속해서 ip 가 바뀐다고 하더라구요. 저는 구글링 끝에 이 문제를 어찌저찌 해결했습니다. 다음 포스팅에는 VPC 만들기, 탄력적 ip 할당, Lambda 에 권한 추가 등 제가 했던 일들을 정리해서 올려보겠습니다. 감사합니다!&lt;/p&gt;</description>
      <category>Back-End/Express</category>
      <category>AWS</category>
      <category>Express</category>
      <category>express server lambda</category>
      <category>lambda</category>
      <category>node.js</category>
      <category>Serverless</category>
      <category>서버리스</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/18</guid>
      <comments>https://simju9397.tistory.com/18#entry18comment</comments>
      <pubDate>Sun, 14 Mar 2021 21:40:57 +0900</pubDate>
    </item>
    <item>
      <title>백준 20055 번 : 컨베이어 벨트 위의 로봇 풀이(Java) - 삼성 역량 테스트 A형 기출문제</title>
      <link>https://simju9397.tistory.com/17</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/20055&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/20055&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1615708961179&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;20055번: 컨베이어 벨트 위의 로봇&quot; data-og-description=&quot;길이가 N인 컨베이어 벨트가 있고, 길이가 2N인 벨트가 이 컨베이어 벨트를 위아래로 감싸며 돌고 있다. 벨트는 길이 1 간격으로 2N개의 칸으로 나뉘어져 있으며, 각 칸에는 아래 그림과 같이 1부&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/20055&quot; data-og-url=&quot;https://www.acmicpc.net/problem/20055&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fp98c/hyJyjVCAqa/8jzZxtqOuSW7jCU4soOekK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bkfrU5/hyJynDKuIN/NQRZ2eKvUaCAlA3cdaceGk/img.png?width=952&amp;amp;height=448&amp;amp;face=0_0_952_448,https://scrap.kakaocdn.net/dn/bp6LEh/hyJytjE3LN/DpOE6H6JRdPtmn6LHGu4ZK/img.png?width=512&amp;amp;height=358&amp;amp;face=0_0_512_358&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/20055&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/20055&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fp98c/hyJyjVCAqa/8jzZxtqOuSW7jCU4soOekK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bkfrU5/hyJynDKuIN/NQRZ2eKvUaCAlA3cdaceGk/img.png?width=952&amp;amp;height=448&amp;amp;face=0_0_952_448,https://scrap.kakaocdn.net/dn/bp6LEh/hyJytjE3LN/DpOE6H6JRdPtmn6LHGu4ZK/img.png?width=512&amp;amp;height=358&amp;amp;face=0_0_512_358');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;20055번: 컨베이어 벨트 위의 로봇&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;길이가 N인 컨베이어 벨트가 있고, 길이가 2N인 벨트가 이 컨베이어 벨트를 위아래로 감싸며 돌고 있다. 벨트는 길이 1 간격으로 2N개의 칸으로 나뉘어져 있으며, 각 칸에는 아래 그림과 같이 1부&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCESCh/btqZ6jFE5LD/iO1bOKpBXm7cEHGpAgpy21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCESCh/btqZ6jFE5LD/iO1bOKpBXm7cEHGpAgpy21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCESCh/btqZ6jFE5LD/iO1bOKpBXm7cEHGpAgpy21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCESCh%2FbtqZ6jFE5LD%2FiO1bOKpBXm7cEHGpAgpy21%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;문제 설명이 모호한 점이 많아서 삽질을 좀 했다. 먼저,&lt;/p&gt;
&lt;p&gt;1. 회전할 때는 내구도가 소모되지 않는다. (오로지 이동할 때, 첫 칸에 로봇을 둘 때만 내구도가 감소한다.)&lt;/p&gt;
&lt;p&gt;2. 그림이 2줄의 배열이 돌아가는 형태로 그려져서 오해하기 쉬운데, 로봇은 &quot;내려가는 위치&quot; 즉, [0][N-1] 에&amp;nbsp;&lt;b&gt;도착하자마자&amp;nbsp;&lt;/b&gt;&lt;b&gt;무조건 바로 떨어져서 사라진다.&amp;nbsp;&lt;/b&gt;즉, 이동에 의한 이동이든, 회전에 의한 이동이든&amp;nbsp;&lt;b&gt;무조건&lt;/b&gt; 내려가는 위치에 도착하면 사라져야 한다.&lt;/p&gt;
&lt;p&gt;3. 벨트는 회전한다. 이게 무슨 의미냐면, 벨트 한 칸마다 고유한 내구도가 있는데, 이것들이 회전을 해야한다는 말이다.&lt;/p&gt;
&lt;p&gt;내구도가&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;10 10 10&lt;/p&gt;
&lt;p&gt;9 9 9&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;로 되어있을 경우, 회전하면&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;9 10 10&lt;/p&gt;
&lt;p&gt;9 9 10&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;으로 되어야 한다는 의미이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;로봇의 이동이나 로봇을 새로 놓는 행위에 대해서, 고정적인 위치가 아니라는 의미이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;나는 위의 세 가지를 제대로 파악 못해서 푸는 데 좀 걸렸다. 이런 구현 문제가 정말 어렵게 나오면 어려운 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;0. input 받기&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;- 내구도를 나타내는 A를 윗 줄인 topA와 bottomA 로 나눴고, 이들을 ArrayList&amp;lt;Integer&amp;gt; 로 설정했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;- 로봇은 위의 한 줄에만 있으므로, topRobot 이라는 객체를 ArrayList&amp;lt;boolean&amp;gt; 으로 만들어줬다.&lt;span&gt; &lt;span style=&quot;color: #333333;&quot;&gt;true이면 로봇이 해당 위치에 있는 것, false 이면 없다는 것이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;이 때 bottomA에서 중요한 것이 있는데, input으로 들어오는 순서가 bottomA에서는 역순이 되어야 한다는 것이다. 내구도가 만약&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;1 2 3 4 5 6 7 8&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;로 들어오면, topA와 bottomA로 나눠보면&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1 2 3 4&lt;/p&gt;
&lt;p&gt;8 7 6 5&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;가 되어야 한다.&lt;/p&gt;
&lt;p&gt;만약 topA를 받는 순서가 정순이라고 그대로 받으면,&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1 2 3 4&lt;/p&gt;
&lt;p&gt;5 6 7 8&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 되어버리므로, 주의하자. 나는 받고 난 후에 Collections.reverse(bottomA) 로 뒤집어줬다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. 회전&lt;/p&gt;
&lt;p&gt;회전할 때는 &quot;내려가는 위치&quot;에 있는, 즉 &lt;b&gt;topA의 마지막 요소&lt;/b&gt; 를 &lt;b&gt;bottomA 의 마지막 요소로&lt;/b&gt; 넣어주고, &quot;올라가는 위치&quot; 에 &quot;올라 가야할 요소&quot; 즉, &lt;b&gt; bottomA의 첫 번째 요소&lt;/b&gt; 를&amp;nbsp;&lt;b&gt;topA의 첫 번째 요소로&lt;/b&gt; 넣어준다. 이후, topA의 마지막 요소, bottomA의 첫 번째 요소들은 이미 이동했으므로 삭제해주면, 자연스레 회전이 완성된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. 이동&lt;/p&gt;
&lt;p&gt;로봇은 왼쪽에서 오른쪽으로 이동하므로, (윗 벨트의 이동방향이 왼쪽-&amp;gt;오른쪽이므로) 이동할 수 있는 조건은,&lt;/p&gt;
&lt;p&gt;1. 다음 위치의 내구도가 0보다 큰지(초과)&lt;/p&gt;
&lt;p&gt;2. 다음 위치에 로봇이 이미 있는지&lt;/p&gt;
&lt;p&gt;3. 현재 위치에 로봇이 있어서 이동 가능 한지&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이다. 이 때,&lt;/p&gt;
&lt;p&gt;topRobot을 &lt;b&gt;오른쪽에서 왼쪽으로 순회하면서 확인&lt;/b&gt;한다. 그렇지 않으면,&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;T F F F F&lt;/p&gt;
&lt;p&gt;일 때, 왼쪽에서 오른쪽으로 순회하면&lt;/p&gt;
&lt;p&gt;F F F F T&amp;nbsp;&lt;/p&gt;
&lt;p&gt;가 될 것이다. 로봇은 한 번의 이동에서&amp;nbsp;&lt;b&gt;한 칸&lt;/b&gt; 만 이동하므로 이는 일어나서는 안 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;3. 로봇 올리기&lt;/p&gt;
&lt;p&gt;제일 간단한 메서드이다.&lt;/p&gt;
&lt;p&gt;1. topA의 첫 번째 요소, 즉 &quot;올라가는 위치&quot;의 내구도가 0 보다 크고,&lt;/p&gt;
&lt;p&gt;2. topRobot의 첫 번째 요소, 즉 &quot;올라가는 위치&quot;에 로봇이 없어야 한다.&lt;/p&gt;
&lt;p&gt;그럴 경우, &quot;올라가는 위치&quot;에 topRobot 을 true 로 해서 로봇을 올려주고, topA는 1 감소 시켜주면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;4. 내구도가 0인 칸이 K를 넘어가는지 체크하기&lt;/p&gt;
&lt;p&gt;단순하게 topA와 bottomA를 모두 돌면서 내구도가 0 이하인 칸을 세어주고, K보다 큰지 작은지 boolean 을 리턴해준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 4가지의 메서드를 만들고, main 에서,&lt;/p&gt;
&lt;pre id=&quot;code_1615710006691&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;while(!4()) {
	1();
    2();
    3();
    cnt++;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런 식으로 구현해줬다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Source Code&lt;/h4&gt;
&lt;pre id=&quot;code_1615710049641&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    static int N,K,cnt;
    static ArrayList&amp;lt;Integer&amp;gt; topA;
    static ArrayList&amp;lt;Integer&amp;gt; bottomA;
    static ArrayList&amp;lt;Boolean&amp;gt; topRobot;
    static void input() throws IOException {
        StringTokenizer nk = new StringTokenizer(bufferedReader.readLine());
        N = Integer.parseInt(nk.nextToken());
        K = Integer.parseInt(nk.nextToken());
        topA = new ArrayList&amp;lt;&amp;gt;();
        bottomA = new ArrayList&amp;lt;&amp;gt;();
        topRobot = new ArrayList&amp;lt;&amp;gt;();
        StringTokenizer a = new StringTokenizer(bufferedReader.readLine());
        for(int i = 0; i &amp;lt; 2; i++) {
            for(int j = 0 ; j &amp;lt; N;j++) {
                if(i == 0) {
                    topA.add(Integer.parseInt(a.nextToken()));
                    topRobot.add(false);
                } else {
                    bottomA.add(Integer.parseInt(a.nextToken()));
                }
            }
        }
        Collections.reverse(bottomA);
    }
    
    static void addRobot() {
        //System.out.println(&quot;3. 로봇 놓기 단계&quot;);
        if(topA.get(0) &amp;gt; 0 &amp;amp;&amp;amp; !topRobot.get(0)) {
            topRobot.set(0, true);
            topA.set(0, topA.get(0) - 1);
        }
    }

    static void printRobots() { // 디버그용 메서드.
        System.out.println(&quot;==========로봇 상태==========&quot;);
        for(int j = 0 ; j &amp;lt; N;j++) {
            if(topRobot.get(j)) System.out.print(&quot;R &quot;);
            else System.out.print(&quot;X &quot;);
        }
        System.out.println(&quot;\n=========내구도 상태============&quot;);
        for(int i = 0; i &amp;lt; 2; i++) {
            for(int j = 0 ; j &amp;lt; N;j++) {
                if(i == 0) System.out.print(topA.get(j) + &quot; &quot;);
                else System.out.print(bottomA.get(j) + &quot; &quot;);
            }
            System.out.println();
        }
        System.out.println(&quot;=================&quot;);
    }

    static void rotate() {
        //System.out.println(&quot;1. 회전 단계&quot;);
        topRobot.add(0, false); // 처음에 robot을 없애주고
        topRobot.remove(topRobot.size() - 1); // 마지막에 robot은 땅으로
        topA.add(0, bottomA.get(0));
        bottomA.add(bottomA.size() , topA.get(topA.size() - 1));
        topA.remove(topA.size()-1);
        bottomA.remove(0);
        if(topRobot.get(topRobot.size() - 1)) {
            topRobot.set(topRobot.size() - 1, false);
        }
    }

    static void move() {
        //System.out.println(&quot;2. 움직임 단계&quot;);

        for(int i = N-1; i &amp;gt; 0; i--) {
            int now = topA.get(i);
            boolean isNowRobot = topRobot.get(i);
            boolean isBeforeRobot = topRobot.get(i-1);
            if(now &amp;gt; 0 &amp;amp;&amp;amp; !isNowRobot &amp;amp;&amp;amp; isBeforeRobot) {
                topA.set(i, topA.get(i) - 1);
                topRobot.set(i, true);
                topRobot.set(i-1, false);
            }
            if(topRobot.get(topRobot.size() - 1)) {
                topRobot.set(topRobot.size() - 1, false);
            }
        }
    }

    static boolean isZeroMoreThanK() {
        int zeroCnt = 0;
        for(int j = 0 ; j &amp;lt; N; j++) {
            if(topA.get(j) &amp;lt;= 0) {
                zeroCnt++;
            }
        }
        for(int j = 0 ; j &amp;lt; N; j++) {
            if(bottomA.get(j) &amp;lt;= 0) {
                zeroCnt++;
            }
        }
//        System.out.println(&quot;4. 체크 단계 : 현재 0인 칸의 갯수 : &quot; + zeroCnt);
//        printRobots();
        if(zeroCnt &amp;gt;= K) {
            return true;
        }
        else {
            return false;
        }
    }

    static void solve() {
        while (!isZeroMoreThanK()) {
            rotate();
            move();
            addRobot();
            cnt++;
        }
        System.out.println(cnt);
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>20055번</category>
      <category>BOJ</category>
      <category>Java</category>
      <category>ps</category>
      <category>삼성역량테스트A형</category>
      <category>컨베이어벨트위의로봇</category>
      <category>풀이</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/17</guid>
      <comments>https://simju9397.tistory.com/17#entry17comment</comments>
      <pubDate>Sun, 14 Mar 2021 17:24:07 +0900</pubDate>
    </item>
    <item>
      <title>백준 12100 번 : 2048 (Easy) 풀이 ( 삼성 SW 역량 테스트 기출 문제 )</title>
      <link>https://simju9397.tistory.com/16</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/12100&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/12100&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1615619529399&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;12100번: 2048 (Easy)&quot; data-og-description=&quot;첫째 줄에 보드의 크기 N (1 &amp;le; N &amp;le; 20)이 주어진다. 둘째 줄부터 N개의 줄에는 게임판의 초기 상태가 주어진다. 0은 빈 칸을 나타내며, 이외의 값은 모두 블록을 나타낸다. 블록에 쓰여 있는 수는 2&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/12100&quot; data-og-url=&quot;https://www.acmicpc.net/problem/12100&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uZciL/hyJyrL4g0S/t1SOuFV8AsdqlHHxUl8uZk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/blhPpS/hyJxd20tro/8Z0aPFEnJYxajTsnZpdKdk/img.png?width=1064&amp;amp;height=1066&amp;amp;face=0_0_1064_1066,https://scrap.kakaocdn.net/dn/zPkvD/hyJytXqD5v/5kGL2QZA2pufQGrUjkxJ11/img.png?width=1056&amp;amp;height=1064&amp;amp;face=0_0_1056_1064&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/12100&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/12100&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uZciL/hyJyrL4g0S/t1SOuFV8AsdqlHHxUl8uZk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/blhPpS/hyJxd20tro/8Z0aPFEnJYxajTsnZpdKdk/img.png?width=1064&amp;amp;height=1066&amp;amp;face=0_0_1064_1066,https://scrap.kakaocdn.net/dn/zPkvD/hyJytXqD5v/5kGL2QZA2pufQGrUjkxJ11/img.png?width=1056&amp;amp;height=1064&amp;amp;face=0_0_1056_1064');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;12100번: 2048 (Easy)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 보드의 크기 N (1 &amp;le; N &amp;le; 20)이 주어진다. 둘째 줄부터 N개의 줄에는 게임판의 초기 상태가 주어진다. 0은 빈 칸을 나타내며, 이외의 값은 모두 블록을 나타낸다. 블록에 쓰여 있는 수는 2&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d3MQkA/btqZ0tPZjAY/yXH0c2J4feMwO3cjkCgJY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d3MQkA/btqZ0tPZjAY/yXH0c2J4feMwO3cjkCgJY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d3MQkA/btqZ0tPZjAY/yXH0c2J4feMwO3cjkCgJY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd3MQkA%2FbtqZ0tPZjAY%2FyXH0c2J4feMwO3cjkCgJY1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;곧 SSAFY 에서 있을 삼성 SW 역량 테스트 A형 모의고사를 대비해 기출문제를 풀어보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A형 취득할 수 있을지 걱정된다...&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;1. A형 문제는 대체로 완전탐색을 돌려도 문제가 없다고 하길래, 내 수준을 감안해서 완전탐색 말고는 생각을 하지 않기로 했다.&lt;/p&gt;
&lt;p&gt;2. 먼저 방향에 따라서 배열을 움직이는 shift() 메서드를 만들었다. 방향이 상하좌우로 4가지, 최대 5번을 돌리므로, 4^5 만큼의 shift() 가 수행되어야 한다.&lt;/p&gt;
&lt;p&gt;3. shift 의 파라미터에 depth 를 넣어서, 5번이 될 경우 return 시켜서 끝내버린다. 이후 map 을 이동시키고 나서, 상하좌우 4방향으로 shift 재귀를 돌려준다.&amp;nbsp;&lt;b&gt;이 때, 상하좌우 shift의 파라미터로 들어가는 map의 메모리 주소가 같으면 안되므로 deepcopy 를 수행해야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;4. 이게 무슨 말이냐면, 만약 0번째 수행에서 1번째 수행으로 가기 위해 반복문을 돌리면&lt;/p&gt;
&lt;pre id=&quot;code_1615620066629&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;shift(map, dir, depth+1);
shift(map, dir, depth+1);
shift(map, dir, depth+1);
shift(map, dir, depth+1);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 풀어질텐데, 이는 map이 같은 주소를 가진 채로 재귀가 수행되므로 상-&amp;gt;하-&amp;gt;좌-&amp;gt;우 가 이전의 결과값을 바탕으로 실행이 된다. 그렇기에, 각 함수마다&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615620158582&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;shift(cloneMap(map), dir, depth+1);
shift(cloneMap(map), dir, depth+1);
shift(cloneMap(map), dir, depth+1);
shift(cloneMap(map), dir, depth+1);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런 식으로 deepcopy 를 해주는 cloneMap 함수를 이용해서 각각의 결과가 독립적이도록 했다.&lt;/p&gt;
&lt;p&gt;5. 이제 shift 함수를 구현해보면,&lt;/p&gt;
&lt;p&gt;- 0번 : 위로 이동&lt;/p&gt;
&lt;p&gt;맨 위에서 바로 아래부터(1번 열부터), (map[x][y])&lt;/p&gt;
&lt;p&gt;바로 위의 열을 봤을 때, (map[x-1][y])&lt;/p&gt;
&lt;p&gt;0이라면 -&amp;gt; 0이 아닐 때 까지 swap 을 해주면서 이동한다.&lt;/p&gt;
&lt;p&gt;0이 아니라면?&lt;/p&gt;
&lt;p&gt;-&amp;gt; 만약 같은 값이라면, map[x-1][y] 에 곱하기 2를 해주고, map[x][y] = 0 으로 만들어준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - &lt;b&gt;!&lt;/b&gt;&lt;b&gt;중요!&lt;/b&gt; &lt;b&gt;연쇄 반응이 없다&lt;/b&gt;고 문제에 나와 있으므로, 한 번 이렇게 합쳐진 곳은 다시 합쳐지지 않도록, summed 라는 boolean 2차원 배열에 기록해둔다.&lt;/p&gt;
&lt;p&gt;-&amp;gt; 다른 값이라면 그냥 break&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;6. 한 번 이동해주고, 2차원 배열을 돌면서 max 값을 저장한다.&lt;/p&gt;
&lt;p&gt;7. 이후 위에서 말한 것 처럼 4방향으로 shift 를 돌려주면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Source Code&lt;/h4&gt;
&lt;pre id=&quot;code_1615620763231&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.StringTokenizer;

public class Main {
    static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    static int N, max;
    static int[][] map;

    static void input() throws IOException {
        N = Integer.parseInt(bufferedReader.readLine());
        map = new int[N][N];
        for(int i = 0 ; i &amp;lt; N; i++) {
            StringTokenizer st = new StringTokenizer(bufferedReader.readLine());
            for(int j = 0; j &amp;lt; N; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }
    }

    static boolean isOut(int nx, int ny) {
        return nx &amp;lt; 0 || ny &amp;lt; 0 || nx &amp;gt;= N || ny &amp;gt;= N;
    }

    static int[][] swap(int[][] map, int x, int y, int nx, int ny) {
        int temp = map[x][y];
        map[x][y] = map[nx][ny];
        map[nx][ny] = temp;
        return map;
    }

    static void findMax(int[][] map) {
        for(int i = 0 ; i &amp;lt; N; i++) {
            for(int j = 0; j &amp;lt; N; j++) {
                max = Math.max(max, map[i][j]);
            }
        }
    }

    static int[][] shift(int[][] map, int dir, boolean[][] summed, int depth) {
        if(depth == 5) {
            return null;
        }
        //System.out.println(depth + &quot;회차 : &quot; + &quot;방향 : &quot; + dir);
        switch (dir) {
            case 0:
                for(int x = 1; x &amp;lt; N; x++) {
                    for(int y = 0; y &amp;lt; N; y++) {
                        int nx = x;
                        while (!isOut(nx-1, y) &amp;amp;&amp;amp; map[nx-1][y] == 0) {
                            map = swap(map, nx, y, nx-1, y);
                            nx--;
                        }
                        int nnx = nx;
                        while (!isOut(nnx-1, y)) {
                            if(map[nnx][y] == map[nnx-1][y] &amp;amp;&amp;amp; !summed[nnx-1][y] &amp;amp;&amp;amp; !summed[nnx][y]) {
                                map[nnx-1][y] *= 2;
                                map[nnx][y] = 0;
                                summed[nnx-1][y] = true;
                            } else {
                                break;
                            }
                            nnx--;
                        }
                    }
                }
                break;
            case 1:
                for(int x = 0; x &amp;lt; N; x++) {
                    for(int y = N-2; y &amp;gt;= 0; y--) {
                        int ny = y;
                        while (!isOut(x, ny+1) &amp;amp;&amp;amp; map[x][ny+1] == 0) {
                            map = swap(map, x, ny, x, ny+1);
                            ny++;
                        }
                        int nny = ny;
                        while (!isOut(x, nny+1)) {
                            if(map[x][nny] == map[x][nny+1] &amp;amp;&amp;amp; !summed[x][nny+1] &amp;amp;&amp;amp; !summed[x][nny]) {
                                map[x][nny+1] *= 2;
                                map[x][nny] = 0;
                                summed[x][nny+1] = true;
                            } else {
                                break;
                            }
                            nny++;
                        }
                    }
                }
                break;
            case 2:
                for(int x = N-2; x &amp;gt;= 0; x--) {
                    for(int y = 0; y &amp;lt; N; y++) {
                        int nx = x;
                        while (!isOut(nx+1, y) &amp;amp;&amp;amp; map[nx+1][y] == 0) {
                            map = swap(map, nx, y, nx+1, y);
                            nx++;
                        }
                        int nnx = nx;
                        while (!isOut(nnx+1, y)) {
                            if(map[nnx][y] == map[nnx+1][y] &amp;amp;&amp;amp; !summed[nnx+1][y] &amp;amp;&amp;amp; !summed[nnx][y]) {
                                map[nnx+1][y] *= 2;
                                map[nnx][y] = 0;
                                summed[nnx+1][y] = true;
                            } else {
                                break;
                            }
                            nnx++;
                        }
                    }
                }
                break;
            case 3:
                for(int x = 0; x &amp;lt; N; x++) {
                    for(int y = 1; y &amp;lt; N; y++) {
                        int ny = y;
                        while (!isOut(x, ny-1) &amp;amp;&amp;amp; map[x][ny-1] == 0) {
                            map = swap(map, x, ny, x, ny-1);
                            ny--;
                        }
                        int nny = ny;
                        while (!isOut(x, nny-1)) {
                            if(map[x][nny] == map[x][nny-1] &amp;amp;&amp;amp; !summed[x][nny-1] &amp;amp;&amp;amp; !summed[x][nny]) {
                                map[x][nny-1] *= 2;
                                map[x][nny] = 0;
                                summed[x][nny-1] = true;
                            } else {
                                break;
                            }
                            nny--;
                        }
                    }
                }
                break;
        }
        findMax(map);
        for(int i = 0 ; i &amp;lt; 4; i++) {
            shift(cloneMap(map), i, new boolean[N][N], depth+1);
        }
        return map;
    }

    static int[][] cloneMap(int[][] map) {
        int[][] clone = new int[N][N];
        for(int i = 0; i &amp;lt; N; i++) {
            clone[i] = map[i].clone();
        }
        return clone;
    }

    static void solve() {
        for(int j = 0; j &amp;lt; 4; j++) {
            shift(cloneMap(map), j, new boolean[N][N], 0);
        }
        System.out.println(max);
    }


    public static void main(String[] args) throws IOException {
        input();
        solve();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>12100</category>
      <category>12100번</category>
      <category>2048(easy)</category>
      <category>a형</category>
      <category>BOJ</category>
      <category>Java</category>
      <category>ps</category>
      <category>삼성SW역량테스트</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/16</guid>
      <comments>https://simju9397.tistory.com/16#entry16comment</comments>
      <pubDate>Sat, 13 Mar 2021 16:33:28 +0900</pubDate>
    </item>
    <item>
      <title>백준 2096 번 : 내려가기 풀이 (Java)</title>
      <link>https://simju9397.tistory.com/15</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2096&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/2096&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1615456115778&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;2096번: 내려가기&quot; data-og-description=&quot;첫째 줄에 N(1 &amp;le; N &amp;le; 100,000)이 주어진다. 다음 N개의 줄에는 숫자가 세 개씩 주어진다. 숫자는 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 중의 하나가 된다.&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/2096&quot; data-og-url=&quot;https://www.acmicpc.net/problem/2096&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lHAg5/hyJw7m4DSJ/WqdS4bcCiWglCJnaAtznPk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2096&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/2096&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lHAg5/hyJw7m4DSJ/WqdS4bcCiWglCJnaAtznPk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;2096번: 내려가기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 N(1 &amp;le; N &amp;le; 100,000)이 주어진다. 다음 N개의 줄에는 숫자가 세 개씩 주어진다. 숫자는 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 중의 하나가 된다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTACFB/btqZUW47vHw/NKNX5wR2PqbvVvDRQNu7k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTACFB/btqZUW47vHw/NKNX5wR2PqbvVvDRQNu7k1/img.png&quot; data-alt=&quot;문제를 잘 읽자.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTACFB/btqZUW47vHw/NKNX5wR2PqbvVvDRQNu7k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTACFB%2FbtqZUW47vHw%2FNKNX5wR2PqbvVvDRQNu7k1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;문제를 잘 읽자.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;1. 이 문제의 해법 자체는 dp 문제를 조금이라도 풀어봤다면 어렵지 않을 것이다. dp[i][j] 는, dp[i-1][j-1], dp[i-1][j], dp[i-1][j+1] 중에 최대값, 최소값을 가져와서 입력받았던 input[i][j] 를 더해주면 될 것이다.&lt;/p&gt;
&lt;p&gt;2. 하지만 이 문제는 메모리 제한이 있다. 나는 얼마나 줄여야 메모리 초과가 안날지 궁금해서 가장 처음 떠오르는 방법부터 차근차근 해봤었다. 그리고 나는 이 문제의 행이 3개로 고정 되어있다는 걸 나중에 알았다.(문제 잘읽자 제발!!!) 아래에 적는 것 중에 열 부분의 N을 3이라 생각하고 봐주시길 바란다. (런타임 에러 이유 표시가 안됐다면 지옥이지 않았을까)&lt;/p&gt;
&lt;p&gt;- N*N*2 (2는 최대, 최소를 한 배열에서 해결하기 위해) 의 dp 배열 + &lt;span style=&quot;color: #333333;&quot;&gt;N*N의 map 배열&lt;/span&gt; &amp;nbsp;-&amp;gt; 바로 초과&lt;/p&gt;
&lt;p&gt;- N*N 의 dp배열 2개 + N*N의 map 배열 -&amp;gt; 초과&lt;/p&gt;
&lt;p&gt;- N*N의 dp 배열 2개 -&amp;gt; 초과&lt;/p&gt;
&lt;p&gt;- 2*N의 dp 배열 2개 -&amp;gt; 초과안남&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉, 우리가 선택할 것은 2*3 의 dp 배열 2개이다. 왜 2*3 만으로 되는 걸까? 그 이유는,&lt;/p&gt;
&lt;p&gt;&lt;b&gt;지금 행과 이전 행 만 비교하면 되기 때문이다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;굳이 필요없는 행들 까지 다 저장할 필요가 아예 없다는 것이다.&lt;/p&gt;
&lt;p&gt;그러므로 해법은 이렇게 될 것이다.&lt;/p&gt;
&lt;p&gt;1. &lt;b&gt;입력을 받으면서&amp;nbsp;&lt;/b&gt;dp[0][j-1], dp[0][j], dp[0][j+1] 중에 최대값, 최소값 + 입력값 을 dp[1][j] 에 저장해준다. 입력을 받으면서 해야 N*3의 배열을 쓸 필요가 없어진다.&lt;/p&gt;
&lt;p&gt;2. 모든 열의 값이 계산됐다면, dp[1] 배열을 dp[0] 에 deepcopy 해주고, dp[1] 배열은 0으로 모두 초기화해준다. 행을 올려주는 것이다.&lt;/p&gt;
&lt;p&gt;3. 1, 2 를 반복한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Source Code&lt;/h4&gt;
&lt;pre id=&quot;code_1615457001773&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {
    static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
    static int N, max = Integer.MIN_VALUE, min = Integer.MAX_VALUE;
    static int[][] dp1, dp2;
    static void input() throws IOException {
        N = Integer.parseInt(bufferedReader.readLine());
        dp1 = new int[2][3];
        dp2 = new int[2][3];
        for(int i = 0 ; i &amp;lt; N; i++) {
            StringTokenizer st = new StringTokenizer(bufferedReader.readLine());
            for(int j = 0; j &amp;lt; 3; j++) {
                dp1[1][j] = Integer.parseInt(st.nextToken());
                dp2[1][j] = dp1[1][j];
                if(j == 0) {
                    dp1[1][j] = Math.max(dp1[0][0], dp1[0][1]) + dp1[1][0];
                    dp2[1][j] = Math.min(dp2[0][0], dp2[0][1]) + dp2[1][0];
                } else if(j == 2) {
                    dp1[1][j] = Math.max(dp1[0][2], dp1[0][1]) + dp1[1][2];
                    dp2[1][j] = Math.min(dp2[0][2], dp2[0][1]) + dp2[1][2];
                } else {
                    dp1[1][1] = Math.max(Math.max(dp1[0][0], dp1[0][1]), dp1[0][2]) + dp1[1][1];
                    dp2[1][1] = Math.min(Math.min(dp2[0][0], dp2[0][1]), dp2[0][2]) + dp2[1][1];
                }
            }
            dp1[0] = dp1[1].clone();
            dp2[0] = dp2[1].clone();
            Arrays.fill(dp1[1], 0);
            Arrays.fill(dp2[1], 0);
        }
        for(int j = 0 ; j &amp;lt; 3; j++) {
            max = Math.max(max, dp1[0][j]);
            min = Math.min(min, dp2[0][j]);
        }
        bufferedWriter.write(max + &quot; &quot; + min);
    }


    public static void main(String[] args) throws IOException {
        input();
        bufferedReader.close();
        bufferedWriter.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>2096</category>
      <category>2096 java</category>
      <category>BOJ</category>
      <category>DP</category>
      <category>ps</category>
      <category>내려가기</category>
      <category>내려가기 java</category>
      <category>백준 2096 java</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/15</guid>
      <comments>https://simju9397.tistory.com/15#entry15comment</comments>
      <pubDate>Thu, 11 Mar 2021 19:04:03 +0900</pubDate>
    </item>
    <item>
      <title>백준 12852번 : 1로 만들기 2 풀이 (Java)</title>
      <link>https://simju9397.tistory.com/14</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/12852&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/12852&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1615387457396&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;12852번: 1로 만들기 2&quot; data-og-description=&quot;첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 자연수 N이 주어진다.&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/12852&quot; data-og-url=&quot;https://www.acmicpc.net/problem/12852&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/4AemG/hyJwY30y5I/Cj8y282H4KbINPo1RVnxI1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/12852&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/12852&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/4AemG/hyJwY30y5I/Cj8y282H4KbINPo1RVnxI1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;12852번: 1로 만들기 2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 자연수 N이 주어진다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfW0zk/btqZOsKtL7F/L9indxyBwdK5QkdPkrpYA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfW0zk/btqZOsKtL7F/L9indxyBwdK5QkdPkrpYA1/img.png&quot; data-alt=&quot;6의 배수를 조심하자&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfW0zk/btqZOsKtL7F/L9indxyBwdK5QkdPkrpYA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfW0zk%2FbtqZOsKtL7F%2FL9indxyBwdK5QkdPkrpYA1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;6의 배수를 조심하자&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;1. 이 문제는 다이나믹 프로그래밍, DP 로 풀어야 하는 문제이다. 최대 인풋이 10의 6승이고, 그렇기에 최대 O(n) 으로 풀어야 할 것이다.&lt;/p&gt;
&lt;p&gt;2. N 을 1로 만드는 것이라서 top-down 방식으로 풀까 하다가 웬지 어려울 것 같아서 bottom-up 으로 풀었는데, 출력을 위해서 반복문을 한번 더 돌려야했다. 푼 뒤에 top-down 을 시도해봤는데 이 편이 더 어려운 것 같다..&lt;/p&gt;
&lt;p&gt;3. N+1 크기의 dp 배열을 만들고, dp[i] 에는 i 를 1로 만드는 데 필요한 연산 횟수를 담는다. dp[1] = 0, dp[2] = 1 일 것이다.&lt;/p&gt;
&lt;p&gt;4. 이제, 우리가 할 수 있는 세 가지 연산을 했을 때의 횟수 를 구해줘야 하는데, 이는&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;1을 빼주는 경우 : 1을 빼주는 연산 횟수 ( 1 ) + (i - 1) 을 1로 만드는 데 필요한 연산 횟수( dp[i-1] )&lt;/p&gt;
&lt;p&gt;&amp;nbsp;2로 나눠주는 경우 : 2로 나눠주는 연산 횟수 ( 1 ) + (i / 2) 를 1로 만드는 데 필요한 연산 횟수 (dp[i / 2])&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;3으로 나눠주는 경우 : 3으로 나눠주는 연산 횟수 ( 1 ) + (i / 3) 를 1로 만드는 데 필요한 연산 횟수 (dp[i / 3])&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이다. 이 셋을 비교해주면서 최소인 것들을 담아주면서 dp 배열을 완성시켜준다. 이후, N 이 1이 될 때까지 다시 같은 연산을 해주면서 최소의 경우들을 찾고, 해당하는 수들을 bufferedWriter 에 써주면 그 동안의 기록들이 완성된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Source Code&lt;/h4&gt;
&lt;pre id=&quot;code_1615456082942&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.Arrays;

public class Main {
    static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));

    static void solve() throws IOException{
        int N = Integer.parseInt(bufferedReader.readLine());
        if(N == 1) {
            System.out.println(0);
            System.out.println(1);
            return;
        }
        int[] dp = new int[N+1];
        dp[N] = 0;
        dp[1] = 0;
        dp[2] = 1;
        int i = 3;
        while(i &amp;lt;= N) {
            int minusOneResult = dp[i-1] + 1;
            int divByThreeResult = Math.min(minusOneResult, dp[i/3] + 1);
            int divByTwoResult = Math.min(minusOneResult, dp[i/2] + 1);
            if(i % 3 == 0) {
                if(i % 2 == 0) {
                    dp[i] = Math.min(divByThreeResult,divByTwoResult );
                }
                else {
                    dp[i] = divByThreeResult;
                }
            }
            else if(i % 2 == 0) {
                dp[i] = divByTwoResult;
            } else {
                dp[i] = minusOneResult;
            }
            i++;
        }
        int n = N;
        bufferedWriter.write(dp[N] + &quot;\n&quot;);
        bufferedWriter.write(N + &quot; &quot;);
        while(n &amp;gt; 1) {
            if(n % 3 == 0 &amp;amp;&amp;amp; dp[n / 3] + 1 &amp;lt; dp[n-1] + 1) {
                n /= 3;
                bufferedWriter.write(n + &quot; &quot;);
            } else if(n % 2 == 0 &amp;amp;&amp;amp; dp[n / 2] + 1 &amp;lt; dp[n-1] + 1) {
                n /= 2;
                bufferedWriter.write(n + &quot; &quot;);
            } else {
               n -= 1;
               bufferedWriter.write(n + &quot; &quot;);
            }
        }


    }

    public static void main(String[] args) throws IOException {
        solve();
        bufferedWriter.close();
        bufferedReader.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>12852 java</category>
      <category>12852번</category>
      <category>BOJ</category>
      <category>DP</category>
      <category>Java</category>
      <category>ps</category>
      <category>백준 12852</category>
      <category>백준 12852 java</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/14</guid>
      <comments>https://simju9397.tistory.com/14#entry14comment</comments>
      <pubDate>Thu, 11 Mar 2021 18:48:08 +0900</pubDate>
    </item>
    <item>
      <title>백준 2003 번 : 수들의 합 2 풀이 (Java)</title>
      <link>https://simju9397.tistory.com/13</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2003&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/2003&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1615348437917&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;2003번: 수들의 합 2&quot; data-og-description=&quot;첫째 줄에 N(1 &amp;le; N &amp;le; 10,000), M(1 &amp;le; M &amp;le; 300,000,000)이 주어진다. 다음 줄에는 A[1], A[2], &amp;hellip;, A[N]이 공백으로 분리되어 주어진다. 각각의 A[x]는 30,000을 넘지 않는 자연수이다.&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/2003&quot; data-og-url=&quot;https://www.acmicpc.net/problem/2003&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dtqr2h/hyJvFrjhBK/4t6Ad8ZjIybLxVxc9kkjU0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2003&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/2003&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dtqr2h/hyJvFrjhBK/4t6Ad8ZjIybLxVxc9kkjU0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;2003번: 수들의 합 2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 N(1 &amp;le; N &amp;le; 10,000), M(1 &amp;le; M &amp;le; 300,000,000)이 주어진다. 다음 줄에는 A[1], A[2], &amp;hellip;, A[N]이 공백으로 분리되어 주어진다. 각각의 A[x]는 30,000을 넘지 않는 자연수이다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HmBNV/btqZIzwjFVZ/kfGrmTRu7rulcuQuYHKmzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HmBNV/btqZIzwjFVZ/kfGrmTRu7rulcuQuYHKmzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HmBNV/btqZIzwjFVZ/kfGrmTRu7rulcuQuYHKmzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHmBNV%2FbtqZIzwjFVZ%2FkfGrmTRu7rulcuQuYHKmzK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Key Point&lt;/h4&gt;
&lt;p&gt;이 문제를 2중 for문을 이용해 완전 탐색으로 돌리면 시간 초과가 날 것이라 짐작했다. N의 범위가 1에서 10000까지인데, 10000 * 10000 의 연산을 0.5초의 시간 제한에서 할 수 있을리가.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 문제는 투 포인터 알고리즘 문제이다. 투포인터 알고리즘이란,&lt;/p&gt;
&lt;p&gt;&lt;b&gt;1차원 배열에서 두 개의 포인터를 조작하여 원하는 결과를 얻는 알고리즘&lt;/b&gt; 이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;구간의 합을 구하는 시작점을 start 로, 끝 점을 end 로 둔다. (start &amp;lt;= end)&lt;/p&gt;
&lt;p&gt;그 뒤, 목표하는 값보다 구간합이 작으면 end 위치의 값을 더해주고, end 를 증가시켜주면서 값을 키워간다.&lt;/p&gt;
&lt;p&gt;그러다가 구간합이 목표값보다 커지면, 이번에는 기존 start 위치의 값을 빼주고, start 를 증가시켜주며, 범위와 값을 같이 줄여나간다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그 점만 잘 알고나서 보면 쉬운 문제인데, 구현을 좀 잘못하면 저렇게 틀릴 수 있다. 나의 경우는 인덱스 문제였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615349010776&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.StringTokenizer;

public class Main {
    static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    static int N,M;
    static int[] arr;

    static void input() throws IOException {
        StringTokenizer nm = new StringTokenizer(bufferedReader.readLine());
        N = Integer.parseInt(nm.nextToken());
        M = Integer.parseInt(nm.nextToken());
        arr = new int[N+1];
        StringTokenizer line = new StringTokenizer(bufferedReader.readLine());
        for(int i = 0; i &amp;lt; N; i++) {
            arr[i] = Integer.parseInt(line.nextToken());
        }
    }

    static void solve() {
        if(N == 1 &amp;amp;&amp;amp; arr[0] == M) {
            System.out.println(1);
            return;
        }
        // 1 1
        // 1
        // 의 반례를 잡기 위해서 넣었다.
        int ans = 0, start = 0, end = 0, sum = 0;
        while(end &amp;lt;= N) { // end 가 배열 범위를 초과하면 break해준다.
            if(sum &amp;gt;= M) {
                sum -= arr[start++];
                // 2. 구간합이 목표값 보다 크거나 같으면, sum -= arr[start];
                // start += 1;
            } else if(sum &amp;lt; M) {
                sum += arr[end++]; 
                // 1. 구간합이 목표값 보다 작으면, sum += arr[end];
                // end += 1;
            }
            if(sum == M){
                ans++;
            }
        }
        System.out.println(ans);
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
        bufferedReader.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>2003</category>
      <category>BOJ</category>
      <category>Java</category>
      <category>ps</category>
      <category>수들의합2</category>
      <category>풀이</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/13</guid>
      <comments>https://simju9397.tistory.com/13#entry13comment</comments>
      <pubDate>Wed, 10 Mar 2021 13:03:54 +0900</pubDate>
    </item>
    <item>
      <title>백준 14502번 : 연구소 풀이 (Java)</title>
      <link>https://simju9397.tistory.com/12</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14502&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/14502&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1615298515493&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;14502번: 연구소&quot; data-og-description=&quot;인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다. 연구소는 크&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/14502&quot; data-og-url=&quot;https://www.acmicpc.net/problem/14502&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iiOUW/hyJvNvSFRo/PuwyLKskmtgB2LKVw3TEfK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14502&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/14502&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iiOUW/hyJvNvSFRo/PuwyLKskmtgB2LKVw3TEfK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;14502번: 연구소&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다. 연구소는 크&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sEBL1/btqZJ6UAv4D/tkBCFKjJrkXw6jicoNBEf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sEBL1/btqZJ6UAv4D/tkBCFKjJrkXw6jicoNBEf1/img.png&quot; data-alt=&quot;생각을 너무 오래했다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sEBL1/btqZJ6UAv4D/tkBCFKjJrkXw6jicoNBEf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsEBL1%2FbtqZJ6UAv4D%2FtkBCFKjJrkXw6jicoNBEf1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;생각을 너무 오래했다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Key Point&lt;/h4&gt;
&lt;p&gt;벽 부수고 이동하기(&lt;a href=&quot;https://simju9397.tistory.com/11&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;simju9397.tistory.com/11&lt;/a&gt;) 문제를 풀고 나서 보고 나니 오히려 혼동이 생겨 고민을 굉장히 오래 했다. 이 문제의 핵심은 그냥..&lt;/p&gt;
&lt;p&gt;1. 빈 공간들과 바이러스들을 각각의 ArrayList에 담는다.&lt;/p&gt;
&lt;p&gt;2. 빈 공간들 중에서 벽으로 만들 3개의 공간을 선택한다. 그렇다.&amp;nbsp;&lt;b&gt;조합과 완전탐색 &lt;/b&gt;문제이다.&lt;/p&gt;
&lt;p&gt;3. 조합을 만들었다면, 첫 바이러스들의 위치를 담은 ArrayList를 기반으로 Queue 를 만든 뒤에, BFS를 돌려준다.&lt;/p&gt;
&lt;p&gt;4. 탐색 하면서 감염되는 공간들은 0에서 2로 바꿔준다.&lt;/p&gt;
&lt;p&gt;5. 탐색이 끝난 후, 여전히 0인 '안전 공간'들의 갯수를 세어 정답에 담아 놓은 값과 비교해 큰 값을 정답으로 담는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;3 &amp;lt;= N,M &amp;lt;= 8 이라는, 굉장히 작은 범위이기 때문에 이렇게 풀어도 풀린다. 나는 이전 문제와 비교해 겁을 먹고, 3개니까 3차원 배열을 visited[N][M][4]; 이런 식으로 만들어줘야 하나 싶어서 한시간 넘게 날렸다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;앞으로는 input의 범위를 보고, 작은 것은 무조건 완전 탐색부터 생각하는 습관을 들여야겠다. 완전 탐색은 모든 알고리즘의 기본이고 제일 쉬우니까 말이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Source Code&lt;/h4&gt;
&lt;pre id=&quot;code_1615299181306&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    static int N,M, ans = Integer.MIN_VALUE;
    static int[][] map;
    static ArrayList&amp;lt;Node&amp;gt; viruses; // 초기 바이러스의 위치
    static ArrayList&amp;lt;Node&amp;gt; safes; // 안전 장소들의 위치
    static int[] dx = {-1,1,0,0};
    static int[] dy = {0,0,-1,1};

    static class Node {
        int x;
        int y;

        public Node(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    static void input() throws IOException {
        StringTokenizer nm = new StringTokenizer(bufferedReader.readLine());
        N = Integer.parseInt(nm.nextToken());
        M = Integer.parseInt(nm.nextToken());
        map = new int[N][M];
        viruses = new ArrayList&amp;lt;&amp;gt;();
        safes = new ArrayList&amp;lt;&amp;gt;();
        for(int i = 0 ; i &amp;lt; N; i++) {
            StringTokenizer st = new StringTokenizer(bufferedReader.readLine());
            for(int j = 0;j &amp;lt; M; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
                if(map[i][j] == 2) {
                    viruses.add(new Node(i, j)); // 각각
                } else if(map[i][j] == 0) {
                    safes.add(new Node(i, j)); // 담아준다
                }
            }
        }
    }

    private static void makeCombination(int cnt, int start ,int[] arr) {
        if(cnt == 3) {
            int[][] newMap = new int[N][M];
            for(int i = 0 ; i &amp;lt; N; i++) {
                newMap[i] = map[i].clone(); 
                // 이거 중요한 부분인데, 2차원 배열을 복사하는 것이기 때문에
                // 반복문을 돌면서 한 열씩 복사해줘야 한다!!!
            }
            for(int i = 0; i &amp;lt; 3; i++) {
                Node n = safes.get(arr[i]); // 뽑은 인덱스들을 반복문을 돌면서 Node 를 가져온 뒤
                newMap[n.x][n.y] = 1; // 뽑힌 노드들을 벽으로 만들어서
            }
            bfs(new boolean[N][M], newMap); // bfs 에 던져준다.
            return;
        }
        for(int i = start; i &amp;lt; safes.size() ; i++) {
            arr[cnt] = i; // 0~safes.size()-1 의 범위에서 인덱스 3개를 조합으로 뽑는다.
            makeCombination(cnt+1, i+1, arr);
        }
    }


    static void bfs(boolean[][] visited, int[][] map) {
        Queue&amp;lt;Node&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;(viruses); // 초기 바이러스의 위치들을 담아준다.
        // 한 번에 담아야 여러 바이러스 들이 같이 확산을 시작할 것이다.
        while (!queue.isEmpty()) {
            Node start = queue.poll();
            int sx = start.x;
            int sy = start.y;
            visited[sx][sy] = true;
            for(int i = 0 ; i &amp;lt; 4; i++) {
                int nx = sx + dx[i];
                int ny = sy + dy[i];
                if(nx &amp;lt; 0 || ny &amp;lt; 0 || nx &amp;gt;= N || ny &amp;gt;= M) continue;
                if(visited[nx][ny] || map[nx][ny] != 0) continue;
                // 방문했거나, 0이 아닌 경우는 넘겨준다(이미 확산됐거나(2), 벽이거나(1) )
                visited[nx][ny] =true;
                queue.add(new Node(nx, ny));
                map[nx][ny] = 2; // 방문한 지점은 확산된 것이므로 2로 바꿔준다.
            }
        }
        int count = 0;
        for(int i = 0 ; i &amp;lt; N; i++) {
            for(int j = 0 ; j &amp;lt; M; j++) {
                if(map[i][j] == 0) { // 이후 2차원 배열을 돌면서 안전 지점의 갯수를 세준다.
                    count++;
                }
            }
        }
        ans = Math.max(ans, count);
    }

    static void solve() {
        makeCombination(0,0,new int[3]);
        System.out.println(ans);
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
        bufferedReader.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/12</guid>
      <comments>https://simju9397.tistory.com/12#entry12comment</comments>
      <pubDate>Tue, 9 Mar 2021 23:13:12 +0900</pubDate>
    </item>
    <item>
      <title>백준 2206번 : 벽 부수고 이동하기 풀이(Java)</title>
      <link>https://simju9397.tistory.com/11</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2206&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/2206&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1615246480275&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;2206번: 벽 부수고 이동하기&quot; data-og-description=&quot;N&amp;times;M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/2206&quot; data-og-url=&quot;https://www.acmicpc.net/problem/2206&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bhZsBn/hyJvUA2kd1/yNil0xSKMfT4RWrIzY8LAk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2206&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/2206&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bhZsBn/hyJvUA2kd1/yNil0xSKMfT4RWrIzY8LAk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;2206번: 벽 부수고 이동하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;N&amp;times;M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGUa2L/btqZvM3ZKTZ/CllHKwBNEMVOfkAIliJwf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGUa2L/btqZvM3ZKTZ/CllHKwBNEMVOfkAIliJwf1/img.png&quot; data-alt=&quot;모든 틀림을 다 겪었다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGUa2L/btqZvM3ZKTZ/CllHKwBNEMVOfkAIliJwf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGUa2L%2FbtqZvM3ZKTZ%2FCllHKwBNEMVOfkAIliJwf1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모든 틀림을 다 겪었다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Key Point&lt;/h4&gt;
&lt;p&gt;1. 나는 처음 접근할 때 1000*1000 인줄 모르고 벽을 부술 때 마다 BFS를 다시 돌리는 것으로 생각했다. 당연히 시간초과로 털렸다.&lt;/p&gt;
&lt;p&gt;2. 이 문제에서 가장 중요한 것은 무조건 한번의 BFS 내에서 해결해야 한다는 것이다. 위와 동일한 얘기지만, 이 점이 굉장히 어려웠다.&lt;/p&gt;
&lt;p&gt;3. 질문 검색을 조금 보고나서 기존의 visited 배열을 2차원에서 3차원으로 확장해야 한다는 것을 알았다.&lt;/p&gt;
&lt;p&gt;이 문제는 벽을 단 한번 부술 수 있으므로,&lt;/p&gt;
&lt;p&gt;visited[x][y][0](벽을 아직 안 부순 경우) 과 visited[x][y][1] ( 벽을 부수고 온 경우 ) 로 나뉘어 생각해야 한다.&lt;/p&gt;
&lt;p&gt;벽을 여러 번 부술 수 있다면 3차원 을 그 횟수에 맞춰 확장해야 하지 않을까 싶은데, 나중에 풀어봐야 겠다.&lt;/p&gt;
&lt;p&gt;4. 또한, 큐를 돌면서 도착 지점에 도착했다고 바로 리턴해서 끝내 버리면 안 된다. 반드시 큐가 완전히 빌 때까지 다 돌면서 &quot;최단&quot; 거리를 찾아야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;1. (0,0) 에서 시작하고, (0,0) 은 무조건 벽이 없으므로 visitied[0][0][0] = true; 로 방문 체크를 해준다.&lt;/p&gt;
&lt;p&gt;2. 큐에 (0,0) 노드를 담아준다. 이 때 거리를 나타내는 depth 는 1부터 시작임을 잊지 말자.&lt;/p&gt;
&lt;p&gt;3. 이 후 늘 하던 BFS 처럼 상하좌우를 순회한다.&lt;/p&gt;
&lt;p&gt;4.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615247129241&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if(sx == N-1 &amp;amp;&amp;amp; sy == M-1) {
  ans = Math.min(ans, sd);
  continue;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Key Point 에서 언급한 4번을 담당하는 코드이다. 도착 했다고 바로 끝내면 안 된다!!&lt;/p&gt;
&lt;p&gt;5. 배열의 범위를 벗어나는 다음 지점들은 continue로 넘겨준다.&lt;/p&gt;
&lt;p&gt;6.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615247228799&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if(visited[sx][sy][0]) {
    if(visited[nx][ny][0]) continue;
    if(map[nx][ny] == 1) {
      visited[nx][ny][1] = true;
    } else {
      visited[nx][ny][0] = true;
  }
} else if(visited[sx][sy][1]) {
    if(visited[nx][ny][1]) continue;
    if(map[nx][ny] == 1) {
      continue;
    } else {
      visited[nx][ny][1] = true;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;여기서 분기가 갈린다. 시작 지점인 sx, sy 에서 만약에 0번, 즉 &lt;span style=&quot;color: #f3c000;&quot;&gt;&lt;b&gt;벽을 안 부수고&lt;/b&gt; &lt;/span&gt;지나온 경우가 true 일 경우에,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;벽을 부수지 않은 채로 다음 지점에 가는 경우가 이미 방문한 경우라면 넘겨준다. 안 해주면 당연히 무한 루프가 돌 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;만약 다음 지점에 벽이 있다면,&lt;/p&gt;
&lt;p&gt;다음 지점의 1번을 true로 , 즉 다음 지점 부터는 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;벽을 부순&lt;/b&gt;&lt;/span&gt; 경우로 생각하라고 남겨주는 것이다.&lt;/p&gt;
&lt;p&gt;벽이 없다면, 다음 지점의 0번을 true로 해서 다음 지점에서도 &lt;span style=&quot;color: #f3c000;&quot;&gt;&lt;b&gt;벽을 부수지 않은&lt;/b&gt;&lt;/span&gt; 경우로 생각하라고 남겨준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;반대로, 시작 지점에서 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;벽을 부수고&lt;/span&gt;&amp;nbsp;&lt;/b&gt;지나온 경우가 true일 경우,&lt;/p&gt;
&lt;p&gt;벽을 부순 채로 다음 지점에 가는 경우가 이미 방문한 경우라면 넘겨준다. 마찬가지로 무한 루프 방지.&lt;/p&gt;
&lt;p&gt;만약 다음 지점에 벽이 있다면, 이미 한 번 부쉈으므로 그 지점에 갈 수 없다. 넘겨준다.&lt;/p&gt;
&lt;p&gt;벽이 없다면, 벽을 부쉈다는 것을 기록하기 위해 visited[nx][ny][1] = true 로 기록을 남겨준다.&lt;/p&gt;
&lt;p&gt;이후 하던 것 처럼 기존 거리 + 1 을 큐에 기록해준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;전체 코드&lt;/h4&gt;
&lt;pre id=&quot;code_1615246985714&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package boj.BFS_DFS;
import java.io.*;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main_벽부수고이동하기_2206 {
    static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    static int N,M, ans = Integer.MAX_VALUE;
    static int[][] map;
    static int[] dx = {-1,1,0,0};
    static int[] dy = {0,0,-1,1};

    static class Node {
        int x;
        int y;
        int depth;

        public Node(int x, int y, int depth) {
            this.x = x;
            this.y = y;
            this.depth = depth;
        }
    }

    static void input() throws IOException {
        StringTokenizer nm = new StringTokenizer(bufferedReader.readLine());
        N = Integer.parseInt(nm.nextToken());
        M = Integer.parseInt(nm.nextToken());
        map = new int[N][M];
        for(int i = 0 ; i &amp;lt; N; i++) {
            char[] line = bufferedReader.readLine().toCharArray();
            for(int j = 0; j &amp;lt; M; j++) {
                map[i][j] = Integer.parseInt(Character.toString(line[j]));
            }
        }
    }

    static void bfs(boolean[][][] visited) {
        Queue&amp;lt;Node&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;();
        visited[0][0][0] = true; 
        queue.add(new Node(0,0,1));
        while (!queue.isEmpty()) {
            Node start = queue.poll();
            int sx = start.x;
            int sy = start.y;
            int sd = start.depth;
            if(sx == N-1 &amp;amp;&amp;amp; sy == M-1) {
                ans = Math.min(ans, sd);
                continue;
            }
            for(int i = 0 ; i &amp;lt; 4; i++) {
                int nx = sx + dx[i];
                int ny = sy + dy[i];
                if(nx &amp;lt; 0 || ny &amp;lt; 0 || nx &amp;gt;= N || ny &amp;gt;= M) continue;
                if(visited[sx][sy][0]) {
                    if(visited[nx][ny][0]) continue;
                    if(map[nx][ny] == 1) {
                        visited[nx][ny][1] = true;
                    } else {
                        visited[nx][ny][0] = true;
                    }
                } else if(visited[sx][sy][1]) {
                    if(visited[nx][ny][1]) continue;
                    if(map[nx][ny] == 1) {
                        continue;
                    } else {
                        visited[nx][ny][1] = true;
                    }
                }
                queue.add(new Node(nx, ny, sd+1));
            }
        }
    }

    static void solve() {
        bfs(new boolean[N][M][2]);
        if(ans == Integer.MAX_VALUE) {
            System.out.println(-1);
        } else {
            System.out.println(ans);
        }
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
        bufferedReader.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/BOJ</category>
      <category>2206</category>
      <category>BOJ</category>
      <category>Java</category>
      <category>ps</category>
      <category>벽부수고이동하기</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/11</guid>
      <comments>https://simju9397.tistory.com/11#entry11comment</comments>
      <pubDate>Tue, 9 Mar 2021 08:53:38 +0900</pubDate>
    </item>
    <item>
      <title>백준 6416 : 트리인가 풀이 (Java)</title>
      <link>https://simju9397.tistory.com/10</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/6416&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/6416&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1615215121930&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;6416번: 트리인가?&quot; data-og-description=&quot;트리는 굉장히 잘 알려진 자료 구조이다. 트리를 만족하는 자료 구조는 비어 있거나(노드의 개수가 0개), 노드의 개수가 1개 이상이고 방향 간선이 존재하며 다음과 같은 조건을 만족해야 한다. &quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/6416&quot; data-og-url=&quot;https://www.acmicpc.net/problem/6416&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kNod5/hyJvK54mgD/7sLWvTI3uLfT7PlT0hyeKk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/6416&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/6416&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kNod5/hyJvK54mgD/7sLWvTI3uLfT7PlT0hyeKk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;6416번: 트리인가?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;트리는 굉장히 잘 알려진 자료 구조이다. 트리를 만족하는 자료 구조는 비어 있거나(노드의 개수가 0개), 노드의 개수가 1개 이상이고 방향 간선이 존재하며 다음과 같은 조건을 만족해야 한다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OBdCV/btqZBdUdCet/FVpU0rFQwQV8mBV9F7StZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OBdCV/btqZBdUdCet/FVpU0rFQwQV8mBV9F7StZK/img.png&quot; data-alt=&quot;울화통의 흔적&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OBdCV/btqZBdUdCet/FVpU0rFQwQV8mBV9F7StZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOBdCV%2FbtqZBdUdCet%2FFVpU0rFQwQV8mBV9F7StZK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;울화통의 흔적&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고한 코드&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@pss407/%EB%B0%B1%EC%A4%806416-%ED%8A%B8%EB%A6%AC%EC%9D%B8%EA%B0%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;velog.io/@pss407/%EB%B0%B1%EC%A4%806416-%ED%8A%B8%EB%A6%AC%EC%9D%B8%EA%B0%80&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1615215717769&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[백준]#6416 트리인가?&quot; data-og-description=&quot;문제트리는 굉장히 잘 알려진 자료 구조이다. 트리를 만족하는 자료 구조는 비어 있거나(노드의 개수가 0개), 노드의 개수가 1개 이상이고 방향 간선이 존재하며 다음과 같은 조건을 만족해야 한&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@pss407/%EB%B0%B1%EC%A4%806416-%ED%8A%B8%EB%A6%AC%EC%9D%B8%EA%B0%80&quot; data-og-url=&quot;https://velog.io/@pss407/백준6416-트리인가&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lRXOh/hyJvHVNOE1/Kqk1ENyYz2TovDIfKAsPT1/img.png?width=310&amp;amp;height=163&amp;amp;face=0_0_310_163,https://scrap.kakaocdn.net/dn/iwyTw/hyJvFp9i8U/PRSKv7HxhL6wMjh1oDYncK/img.png?width=310&amp;amp;height=163&amp;amp;face=0_0_310_163&quot;&gt;&lt;a href=&quot;https://velog.io/@pss407/%EB%B0%B1%EC%A4%806416-%ED%8A%B8%EB%A6%AC%EC%9D%B8%EA%B0%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@pss407/%EB%B0%B1%EC%A4%806416-%ED%8A%B8%EB%A6%AC%EC%9D%B8%EA%B0%80&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lRXOh/hyJvHVNOE1/Kqk1ENyYz2TovDIfKAsPT1/img.png?width=310&amp;amp;height=163&amp;amp;face=0_0_310_163,https://scrap.kakaocdn.net/dn/iwyTw/hyJvFp9i8U/PRSKv7HxhL6wMjh1oDYncK/img.png?width=310&amp;amp;height=163&amp;amp;face=0_0_310_163');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;[백준]#6416 트리인가?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;문제트리는 굉장히 잘 알려진 자료 구조이다. 트리를 만족하는 자료 구조는 비어 있거나(노드의 개수가 0개), 노드의 개수가 1개 이상이고 방향 간선이 존재하며 다음과 같은 조건을 만족해야 한&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Key Point&lt;/h4&gt;
&lt;p&gt;0. 먼저, 이 문제는 결국 내 힘으로 풀지 못했다. 풀이법에서는 틀린 게 없는 것 같은데, 아직 Java 가 익숙하지 않아서 코드를 잘못 짠 것인지 모르겠는데 질문 검색 에서의 케이스들도 다 맞아서 반례를 못 찾았다 결국...&lt;/p&gt;
&lt;p&gt;1. 이 문제의 핵심은,&lt;/p&gt;
&lt;p&gt;&quot;루트가 하나인지&quot;, &quot;정점 갯수 - 간선 갯수 == 1인지&quot;, &quot;들어오는 간선의 갯수가 2개 이상인 노드가 있는지&quot;&lt;/p&gt;
&lt;p&gt;이다.&lt;/p&gt;
&lt;p&gt;2. 정점이 순서대로 들어오지 않으므로, HashSet 을 이용해서 중복을 제거하면서 넣으면 그것이 정점이 된다.&lt;/p&gt;
&lt;p&gt;3. 이건 세세한 건데, 문장의 마지막에 점 찍는 거 잊지 말자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;소스코드&lt;/h4&gt;
&lt;pre id=&quot;code_1615215647768&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.HashSet;
import java.util.Iterator;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        HashSet&amp;lt;Integer&amp;gt; set;
        int[] cnt; // idx : 노드 번호 value : 들어오는 간선 의 갯수
        int tc = 0;
        int root;
        boolean hasTwoMoreIncomeVector;
        int v; // 간선의 갯수

        while(true) {
            set = new HashSet&amp;lt;&amp;gt;();
            cnt = new int[1001];
            root = 0;
            tc++;
            hasTwoMoreIncomeVector = false;
            v = 0;

            while(true) {
                int a = sc.nextInt();
                int b = sc.nextInt();

                if (a == 0 &amp;amp;&amp;amp; b == 0) break;
                if (a == -1 &amp;amp;&amp;amp; b == -1) return;

                set.add(a);
                set.add(b);
                cnt[b]++;
                v++;
            }
            if(set.size()==0) {
                System.out.println(&quot;Case &quot;+ tc +&quot; is a tree.&quot;); // 아무 것도 없는 트리도 트리임.
                continue;
            }

            Iterator iter = set.iterator();
            while(iter.hasNext()) {
                int num = (int)iter.next();
                if(cnt[num]==0) root++; // 들어오는 간선의 갯수가 0인 경우 root
                if(cnt[num]&amp;gt;1) {
                    hasTwoMoreIncomeVector = true; // 들어오는 간선의 갯수가 2개 이상인 노드가 있는 경우
                    break;
                }
            }

            if(hasTwoMoreIncomeVector || root!=1 || set.size()-v!=1) // 들어오는 간선의 갯수가 2개 이상 or 루트 노드가 유일하지 않음 or 노드의 개수 - 간선의 개수 가 1이 아님
                System.out.println(&quot;Case &quot;+ tc +&quot; is not a tree.&quot;);

            else
                System.out.println(&quot;Case &quot;+ tc +&quot; is a tree.&quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/BOJ</category>
      <category>6416</category>
      <category>BOJ</category>
      <category>Java</category>
      <category>ps</category>
      <category>트리</category>
      <category>트리인가</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/10</guid>
      <comments>https://simju9397.tistory.com/10#entry10comment</comments>
      <pubDate>Tue, 9 Mar 2021 00:02:38 +0900</pubDate>
    </item>
    <item>
      <title>백준 11660 번 : 구간 합 구하기 5 (Java)</title>
      <link>https://simju9397.tistory.com/9</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11660&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/11660&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1615001930434&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;11660번: 구간 합 구하기 5&quot; data-og-description=&quot;첫째 줄에 표의 크기 N과 합을 구해야 하는 횟수 M이 주어진다. (1 &amp;le; N &amp;le; 1024, 1 &amp;le; M &amp;le; 100,000) 둘째 줄부터 N개의 줄에는 표에 채워져 있는 수가 1행부터 차례대로 주어진다. 다음 M개의 줄에는 네 &quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/11660&quot; data-og-url=&quot;https://www.acmicpc.net/problem/11660&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bkFM8d/hyJsqVCatD/b1TY9GbLNMj27FPfTbfhI1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11660&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/11660&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bkFM8d/hyJsqVCatD/b1TY9GbLNMj27FPfTbfhI1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;11660번: 구간 합 구하기 5&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 표의 크기 N과 합을 구해야 하는 횟수 M이 주어진다. (1 &amp;le; N &amp;le; 1024, 1 &amp;le; M &amp;le; 100,000) 둘째 줄부터 N개의 줄에는 표에 채워져 있는 수가 1행부터 차례대로 주어진다. 다음 M개의 줄에는 네&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzRtT6/btqZlOVcdcC/KIFu29dBxE7FICJF62R5K1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzRtT6/btqZlOVcdcC/KIFu29dBxE7FICJF62R5K1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzRtT6/btqZlOVcdcC/KIFu29dBxE7FICJF62R5K1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzRtT6%2FbtqZlOVcdcC%2FKIFu29dBxE7FICJF62R5K1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Key Point&lt;/h4&gt;
&lt;p&gt;1. (x1,y1) 부터 (x2, y2) 까지의 구역의 합을 구하는 문제이다.&lt;/p&gt;
&lt;p&gt;2. 2차원 배열 dp에 1,1 부터 (x,y) 까지의 합을 기억해놓는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1 2 3 4&lt;/p&gt;
&lt;p&gt;2 3 4 5&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;인 경우, dp 는&lt;/p&gt;
&lt;p&gt;1 3 6 10&lt;/p&gt;
&lt;p&gt;3 7 14 24&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;3. x1 &amp;lt;= x2이고 y1 &amp;lt;= y2 이므로, (x1,y1) 부터 (x2,y2) 까지의 합은,&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1,1에서 x2,y2 까지의 합(큰 사각형) 빼기&lt;/p&gt;
&lt;p&gt;1,1에서 x1,y2 까지의 합 빼기&lt;/p&gt;
&lt;p&gt;1,1에서 x2,y1 까지의 합 더하기&lt;/p&gt;
&lt;p&gt;1,1에서 x1,y1 까지의 합이다. (이 부분은 두번 마이너스 됐기 때문이다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이것만 알면 구현은 쉽다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;소스코드&lt;/h4&gt;
&lt;pre id=&quot;code_1615002732298&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.StringTokenizer;

public class Main {
    static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    static int N, M;
    static int[][] map, dp;
    static Op[] ops;

    static class Op {
        int x1;
        int y1;
        int x2;
        int y2;

        public Op(int x1, int y1, int x2, int y2) {
            this.x1 = x1;
            this.y1 = y1;
            this.x2 = x2;
            this.y2 = y2;
        }
    }

    static void input() throws IOException {
        StringTokenizer nm = new StringTokenizer(bufferedReader.readLine());
        N = Integer.parseInt(nm.nextToken());
        M = Integer.parseInt(nm.nextToken());
        map = new int[N+1][N+1];
        dp = new int[N+1][N+1];
        ops = new Op[M];
        for(int i = 1 ; i &amp;lt;= N; i++) {
            StringTokenizer st = new StringTokenizer(bufferedReader.readLine());
            for(int j = 1 ; j &amp;lt;= N; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }
        for(int i = 1; i &amp;lt;= N; i++) {
            for(int j = 1; j &amp;lt;= N; j++) {
                dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + map[i][j];
                //x1,y1 부터 x2,y2 까지의 합을 구하는 로직과 동일하게 dp를 구한다.
            }
        }
        for (int i = 0; i &amp;lt; M; i++) {
            StringTokenizer st = new StringTokenizer(bufferedReader.readLine());
            ops[i] = new Op(Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken()));
        }
    }

    static void solve() {
        for(int i = 0 ; i &amp;lt; M; i++) {
            Op op = ops[i];
            System.out.println(dp[op.x2][op.y2] - dp[op.x2][op.y1-1] - dp[op.x1-1][op.y2] + dp[op.x1-1][op.y1-1]);
        }
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
        bufferedReader.close();
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>11660번</category>
      <category>Algorithm</category>
      <category>BOJ</category>
      <category>Java</category>
      <category>ps</category>
      <category>구간합구하기5</category>
      <category>백준</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/9</guid>
      <comments>https://simju9397.tistory.com/9#entry9comment</comments>
      <pubDate>Sat, 6 Mar 2021 12:55:08 +0900</pubDate>
    </item>
    <item>
      <title>백준 2636번 : 치즈 풀이(Java)</title>
      <link>https://simju9397.tistory.com/8</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2636&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.acmicpc.net/problem/2636&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1614865259148&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;2636번: 치즈&quot; data-og-description=&quot;아래 &amp;lt;그림 1&amp;gt;과 같이 정사각형 칸들로 이루어진 사각형 모양의 판이 있고, 그 위에 얇은 치즈(회색으로 표시된 부분)가 놓여 있다. 판의 가장자리(&amp;lt;그림 1&amp;gt;에서 네모 칸에 X친 부분)에는 치즈가 놓&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/2636&quot; data-og-url=&quot;https://www.acmicpc.net/problem/2636&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bF3C1q/hyJsa5zyze/Hq7gGmDXClzDTam3IUkv2K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/pIPDW/hyJsdgX7se/XPafFPYm4RhiiE58NSO5b1/img.png?width=545&amp;amp;height=290&amp;amp;face=0_0_545_290,https://scrap.kakaocdn.net/dn/bvHnSF/hyJsflxV6N/b7KeWcrfAYlnLLLZEwQQUK/img.jpg?width=262&amp;amp;height=282&amp;amp;face=0_0_262_282&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2636&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/2636&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bF3C1q/hyJsa5zyze/Hq7gGmDXClzDTam3IUkv2K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/pIPDW/hyJsdgX7se/XPafFPYm4RhiiE58NSO5b1/img.png?width=545&amp;amp;height=290&amp;amp;face=0_0_545_290,https://scrap.kakaocdn.net/dn/bvHnSF/hyJsflxV6N/b7KeWcrfAYlnLLLZEwQQUK/img.jpg?width=262&amp;amp;height=282&amp;amp;face=0_0_262_282');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;2636번: 치즈&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;아래 &amp;lt;그림 1&amp;gt;과 같이 정사각형 칸들로 이루어진 사각형 모양의 판이 있고, 그 위에 얇은 치즈(회색으로 표시된 부분)가 놓여 있다. 판의 가장자리(&amp;lt;그림 1&amp;gt;에서 네모 칸에 X친 부분)에는 치즈가 놓&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FIY35/btqZfiatA90/z63JS93kAkxxNKD1lVMIT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FIY35/btqZfiatA90/z63JS93kAkxxNKD1lVMIT0/img.png&quot; data-alt=&quot;바로 AC가 나와서 기분이 좋았다 ㅎ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FIY35/btqZfiatA90/z63JS93kAkxxNKD1lVMIT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFIY35%2FbtqZfiatA90%2Fz63JS93kAkxxNKD1lVMIT0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;바로 AC가 나와서 기분이 좋았다 ㅎ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;BFS 로 풀 수 있는 문제이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Key Point&lt;/h4&gt;
&lt;p&gt;1. 현재 치즈들의 위치를 arraylist에 담아주고, 모든 치즈들에 대해 영역 바깥으로 갈 수 있는지를 체크해준다. 만약 bfs 로 탐색하면서, 이미 방문한 곳이거나, 그 위치에 치즈가 있다면 그 지점은 바깥에 닿지 않는, 즉 그 시간 대에서는 공기 중으로 사라지지 않는 안전한 치즈이다.&lt;/p&gt;
&lt;p&gt;2.&amp;nbsp;&lt;b&gt;중요 : 치즈들을 돌면서 map의 바깥에 갈 수 있다면, 즉 사라져야 하는 치즈라고 바로 삭제시켜서는 안된다.&amp;nbsp;&lt;/b&gt;바로 삭제시켜버리면 그 치즈에 의해 보호받던 안쪽의 치즈들까지 원치 않게 사라져버릴 것이기 때문이다. 나의 경우는 삭제시켜야 하는 치즈들과 그렇지 않은 치즈들의 ArrayList를 만들어서, 그렇지 않은 치즈들, 즉 생존한 치즈들이 남지 않을 때 까지 while 문으로 돌려주었다.&lt;/p&gt;
&lt;p&gt;3. 내가 실수했던 부분인데,&amp;nbsp;&lt;/p&gt;
&lt;p&gt;11111&lt;/p&gt;
&lt;p&gt;11011&lt;/p&gt;
&lt;p&gt;11111&lt;/p&gt;
&lt;p&gt;이 있다면, 중간의 0과 맞닿는 부분들이 삭제 되어서는 안된다. 1번과 같은 맥락이다. 내부의 공기는 아무 상관 없으므로, 반드시 bfs 를 돌면서 map 의 바깥으로 갈 수 있는 것들이 삭제되어야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위의 두 가지를 감안하면 쉬운 문제이다. 골드 치고 굉장히 빨리 푼 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1614865834943&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {
    static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    static int R,C;
    static int[][] map;
    static int[] dx = {-1,1,0,0};
    static int[] dy = {0,0,-1,1};
    static ArrayList&amp;lt;Node&amp;gt; cheeses;

    static class Node {
        int x;
        int y;

        public Node(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }


    static void input() throws IOException {
        StringTokenizer rc = new StringTokenizer(bufferedReader.readLine());
        R = Integer.parseInt(rc.nextToken());
        C = Integer.parseInt(rc.nextToken());
        map = new int[R][C];
        cheeses = new ArrayList&amp;lt;&amp;gt;();
        for(int i = 0; i &amp;lt; R; i++) {
            StringTokenizer st = new StringTokenizer(bufferedReader.readLine());
            for(int j = 0; j &amp;lt; C; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
                if(map[i][j] == 1) {
                    cheeses.add(new Node(i, j));
                }
            }
        }
    }

    static boolean bfs(Node start, boolean[][] visited) {
        Queue&amp;lt;Node&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;();
        queue.add(start);
        while(!queue.isEmpty()) {
            Node s = queue.poll();
            int x = s.x;
            int y = s.y;
            visited[x][y] = true;
            for(int i = 0 ; i &amp;lt; 4; i++) {
                int nx = x + dx[i];
                int ny = y + dy[i];
                if(nx &amp;lt; 0 || ny &amp;lt; 0 || nx &amp;gt;= R || ny &amp;gt;= C) {
                    return true;
                }
                if(visited[nx][ny] || map[nx][ny] &amp;gt;= 1) continue;
                queue.add(new Node(nx, ny));
                visited[nx][ny] = true;
            }
        }
        return false;
    }

    static void printMap() {
        for(int i = 0; i &amp;lt; R; i++) {
            for(int j = 0; j &amp;lt; C; j++) {
                System.out.print(map[i][j] + &quot; &quot;);
            }
            System.out.println();
        }
    }

    static void solve() {
        int count = 0;
        int time = 0;
        while (!cheeses.isEmpty()) {
            ArrayList&amp;lt;Node&amp;gt; newCheeses = new ArrayList&amp;lt;&amp;gt;();
            ArrayList&amp;lt;Node&amp;gt; deleteCheeses = new ArrayList&amp;lt;&amp;gt;();
            for(Node cheese : cheeses) {
                if(!bfs(cheese, new boolean[R][C])) {
                    newCheeses.add(cheese);
                } else {
                    deleteCheeses.add(cheese);
                }
            }
            for(Node cheese : deleteCheeses) {
                map[cheese.x][cheese.y] = 0;
            }
            count = cheeses.size();
            cheeses = newCheeses;
            time++;
//            printMap();
//            System.out.println(&quot;=================&quot;);
        }
        System.out.println(time);
        System.out.println(count);
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
        bufferedReader.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>2636 java</category>
      <category>백준 2636</category>
      <category>백준 2636 java</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/8</guid>
      <comments>https://simju9397.tistory.com/8#entry8comment</comments>
      <pubDate>Thu, 4 Mar 2021 22:51:25 +0900</pubDate>
    </item>
    <item>
      <title>백준 3190 번 : 뱀 풀이(Java)</title>
      <link>https://simju9397.tistory.com/7</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/3190&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/3190&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1614779755578&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;3190번: 뱀&quot; data-og-description=&quot;&amp;nbsp;'Dummy' 라는 도스게임이 있다. 이 게임에는 뱀이 나와서 기어다니는데, 사과를 먹으면 뱀 길이가 늘어난다. 뱀이 이리저리 기어다니다가 벽 또는 자기자신의 몸과 부딪히면 게임이 끝난다. 게임&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/3190&quot; data-og-url=&quot;https://www.acmicpc.net/problem/3190&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/AR7h7/hyJq4ruSgP/sJXgjWHGmMVkdTKkTFOp20/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/3190&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/3190&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/AR7h7/hyJq4ruSgP/sJXgjWHGmMVkdTKkTFOp20/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;3190번: 뱀&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;&amp;nbsp;'Dummy' 라는 도스게임이 있다. 이 게임에는 뱀이 나와서 기어다니는데, 사과를 먹으면 뱀 길이가 늘어난다. 뱀이 이리저리 기어다니다가 벽 또는 자기자신의 몸과 부딪히면 게임이 끝난다. 게임&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CH95w/btqY5LkgG7y/0K6phbDPZSWCwJRE27PeT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CH95w/btqY5LkgG7y/0K6phbDPZSWCwJRE27PeT1/img.png&quot; data-alt=&quot;시간이 이것보단 길게 나올 줄 알았는데 의외였다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CH95w/btqY5LkgG7y/0K6phbDPZSWCwJRE27PeT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCH95w%2FbtqY5LkgG7y%2F0K6phbDPZSWCwJRE27PeT1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;시간이 이것보단 길게 나올 줄 알았는데 의외였다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Key Point&lt;/h3&gt;
&lt;p&gt;1. 뱀의 머리는 기본적으로 정해진 방향대로 이동한다.&lt;/p&gt;
&lt;p&gt;2. 사과를 먹었을 때는 꼬리를 고정해두고, 먹지 못했다면 꼬리를 머리 쪽으로 당겨야 한다.&lt;/p&gt;
&lt;p&gt;3. 이게 중요한데, &lt;b&gt;사과를 먹었다면 먹은 걸로 처리를 해줘야한다.&amp;nbsp;&lt;/b&gt;(안 해주면 23% 에서 틀리는 것 같다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해법&lt;/h4&gt;
&lt;p&gt;뱀의 상태, 즉 뱀이 차지하고 있는 칸들을 Deque 에 담아 준다. front 와 rear 모두 삽입과 삭제가 가능한 만큼 최적의 자료구조일 것이다. 뱀의 상태를 나타낼 생각을 안하고, 뱀이 방향을 꺾을 때 마다 관절 부분들을 덱에 넣는 방식을 채택했다가 시간을 많이 잡아먹었다. 역시 아직도 많이 부족함을 느낀다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. 입력을 받는다. (받으면서 사과는 map에 다르게 표시를 해준다)&lt;/p&gt;
&lt;p&gt;1-1. 뱀의 상태를 나타내는 snake 덱에 시작 위치인 (0,0) 의 Node 를 담는다.&lt;/p&gt;
&lt;p&gt;2. 무한 루프를 돌면서&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2-1. 뱀의 머리의 다음 위치는 현재 뱀 머리의 위치에, 뱀이 바라보고 있는 방향이다.&lt;/p&gt;
&lt;pre id=&quot;code_1614780308393&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int hx = head.x;
int hy = head.y;
checkChangeDir();
int nx = hx + dx[snakeDir];
int ny = hy + dy[snakeDir];&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;다음 위치를 바라보기 전에, 방향을 바꿔야 하는지 체크하고 필요하다면 바꿔준 뒤에 이동한다.&lt;/p&gt;
&lt;p&gt;2-2. 만약에 다음 위치가 map 을 벗어나거나, 자신의 몸 부분이라면 break 해준다.&lt;/p&gt;
&lt;p&gt;2-2-1. 만약 둘 다 아니라서 이동이 가능한 경우, 머리는 어차피 이동 하므로 다음 위치를 snake 의 rear 에 추가해준다.&lt;/p&gt;
&lt;p&gt;2-2-1-1 . 다음 위치가 사과일 경우, &lt;b&gt;다음 위치는 사과를 먹은 후의 상태로 초기화를 해준다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;2-2-1.2 . 다음 위치가 사과가 아닐 경우, 기존의 꼬리를 떼준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;전체 코드&lt;/h4&gt;
&lt;pre id=&quot;code_1614780542467&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.StringTokenizer;

public class Main {
    static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    static int N,K,L, snakeDir, count;
    static short[][] map;
    static Deque&amp;lt;Node&amp;gt; snake;
    static Deque&amp;lt;Movement&amp;gt; movements;
    static int[] dx = {-1,0,1,0};
    static int[] dy = {0,1,0,-1}; // 상, 우, 하, 좌

    static class Node {
        int x;
        int y;

        public Node(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    static class Movement {
        int time;
        char dir;

        public Movement(int time, char dir) {
            this.time = time;
            this.dir = dir;
        }
    }

    static void input() throws IOException {
        N = Integer.parseInt(bufferedReader.readLine());
        K = Integer.parseInt(bufferedReader.readLine());
        map = new short[N][N];
        movements = new ArrayDeque&amp;lt;&amp;gt;();
        snake = new ArrayDeque&amp;lt;&amp;gt;();
        snake.addLast(new Node(0,0));
        count = 0;
        snakeDir = 1; // 오른쪽 방향을 보고 시작
        for(int i = 0; i &amp;lt; K; i++) {
            StringTokenizer xy = new StringTokenizer(bufferedReader.readLine());
            int x = Integer.parseInt(xy.nextToken()) - 1;
            int y = Integer.parseInt(xy.nextToken()) - 1; // input 이 1,1 부터 시작하는 기준이라서 1씩 빼준다.
            map[x][y] = 1;
        }
        L = Integer.parseInt(bufferedReader.readLine());
        for (int i = 0; i &amp;lt; L; i++) {
            StringTokenizer st = new StringTokenizer(bufferedReader.readLine());
            movements.offer(new Movement(Integer.parseInt(st.nextToken()),  st.nextToken().charAt(0)));
        }
    }

    static boolean isOut(int nx, int ny) {
        if(nx &amp;lt; 0 || ny &amp;lt; 0 || nx &amp;gt;= N || ny &amp;gt;= N) {
            return true;
        }
        return false;
    }

    static boolean isSelfBody(int nx, int ny) {
        for(Node n : snake) {
            if(n.x == nx &amp;amp;&amp;amp; n.y == ny) {
                return true;
            }
        }
        return false;
    }

    static void checkChangeDir() {
        if(!movements.isEmpty() &amp;amp;&amp;amp; movements.peekFirst().time + 1 == count) {
            Movement m = movements.pollFirst();
            if(m.dir == 'D') {
                snakeDir = (snakeDir + 1) % 4;
            } else {
                if(snakeDir == 0) {
                    snakeDir = 3;
                } else {
                    snakeDir -= 1;
                }
            }
        }
    }

    static boolean isApple(int nx, int ny) {
        return map[nx][ny] == 1;
    }

    static void solve() {
        while (true) {
            ++count;
            Node head = snake.peekLast();
            Node tail = snake.peekFirst();
            int hx = head.x;
            int hy = head.y;
            checkChangeDir();
            int nx = hx + dx[snakeDir];
            int ny = hy + dy[snakeDir];
            if(isOut(nx, ny) || isSelfBody(nx, ny)) {
                break;
            }
            snake.addLast(new Node(nx, ny));
            if(!isApple(nx, ny)) {
                snake.pollFirst();
            } else {
                map[nx][ny] = 0;
            }
        }
        System.out.println(count);
    }

    public static void main(String[] args) throws IOException {
        input();
        solve();
        bufferedReader.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/BOJ</category>
      <category>3190</category>
      <category>Deque</category>
      <category>덱</category>
      <category>백준</category>
      <category>뱀</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/7</guid>
      <comments>https://simju9397.tistory.com/7#entry7comment</comments>
      <pubDate>Wed, 3 Mar 2021 23:10:21 +0900</pubDate>
    </item>
    <item>
      <title>이건 보고 HTML 짜자! 깔끔한 HTML을 만들기 위한 HTML 코드 컨벤션</title>
      <link>https://simju9397.tistory.com/6</link>
      <description>&lt;h1&gt;HTML 코드 작성 규칙&lt;/h1&gt;
&lt;h3&gt;대전제&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;DTD를 제외한 모든 요소와 애트리뷰트는 소문자로 작성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;pre&amp;gt;Good!&amp;lt;/pre&amp;gt;
&amp;lt;PRE&amp;gt;Bad..&amp;lt;/PRE&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;애트리뷰트 값은 큰 따옴표로 묶습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div id=&amp;quot;good&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;#39;BAD&amp;#39;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;아이디와 클래스의 속성 값은 숫자, 대문자, 특수문자로 시작할 수 없습니다.&lt;/li&gt;
&lt;li&gt;애트리뷰트의 속성의 값은 숫자, 특수문자로 시작할 수 없으며, 대문자로는 시작할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;아이디, 클래스&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;아이디는 소문자 카멜 표기법 을 사용합니다. 스타일 지정을 위해 사용하지 않으며, DOM 조작을 위해 사용해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;주의 : 숫자, 대문자, 특수문자로 시작할 수 없습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div id=&amp;quot;headerMenu&amp;quot;&amp;gt;
  ...
&amp;lt;/div&amp;gt;

&amp;lt;div id=&amp;quot;layerPop-1&amp;quot;&amp;gt;
 ...
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;클래스는 아이디와 시각적 구분을 위해 &lt;strong&gt;&lt;em&gt;하이픈 표기법&lt;/em&gt;&lt;/strong&gt; 을 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div class=&amp;quot;slide-label n-3 back-swiper&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;swiper-wrapper&amp;quot;&amp;gt;
    ...
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;들여쓰기(indent)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;body 태그 안의 모든 요소는 중첩의 깊이에 따라 1탭 들여 씁니다. 1탭의 크기는 공백 2칸입니다.(vscode 내에서 설정할 수도 있습니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;body class=&amp;quot;&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;ly-wrapper&amp;quot;&amp;gt;
    &amp;lt;div class=&amp;quot;ly-total-menu&amp;quot;&amp;gt;
    ...
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;빈 줄&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;그룹 되어있는 태그들을 구분하기 위하여 코드 간 1 줄의 빈 줄을 사용하는 경우가 있지만, 이 경우 &lt;strong&gt;&lt;em&gt;주석&lt;/em&gt;&lt;/strong&gt; 사용을 권장하며 빈 줄을 사용하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!-- bad --&amp;gt;
&amp;lt;div class=&amp;quot;section-login&amp;quot;&amp;gt;

  &amp;lt;div class=&amp;quot;login-before&amp;quot;&amp;gt;
  ...
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;주석&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;주석 기호와 내용 사이에는 반드시 공백이 한 칸 있어야 합니다. 시작 주석과 끝 주석 모두 작성하는 것을 권장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!-- 2016.07.20 고객권리안내문 추가 --&amp;gt;
&amp;lt;ul class=&amp;quot;policy&amp;quot;&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#none&amp;quot;&amp;gt;이용약관&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#none&amp;quot;&amp;gt;개인정보처리방침&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#none&amp;quot;&amp;gt;고객권리안내문&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#none&amp;quot;&amp;gt;전자금융거래표준약관&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;!--// 2016.07.20 객권리안내문 추가 --&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;공백 및 종료태그&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;태그 명과 애트리뷰트, 애트리뷰트와 애트리뷰트 사이에 한 개의 공백이 필요하며 한 개 이상의 공백은 사용하지 않습니다.&lt;/li&gt;
&lt;li&gt;HTML5를 사용한다면 닫는 기호를 생략해서 문서를 작성하고 XHTML을 사용한 경우는 닫는 기호를 반드시 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;HTML5 가 아닌 경우, 종료 태그가 없는 태그의 닫는 규칙 앞에도 한 개의 공백이 필요합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;label for=&amp;quot;checkboxA-1&amp;quot; class=&amp;quot;label&amp;quot;&amp;gt;체크 레이블&amp;lt;/label&amp;gt;
&amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;CA-1&amp;quot; id=&amp;quot;checkboxA-1&amp;quot; class=&amp;quot;checkbox&amp;quot;&amp;gt;

// Doctypes XHTML일 경우 종료 태그 앞에도 한 개의 공백이 들어갑니다.
&amp;lt;img src=&amp;quot;/img/logo.png&amp;quot; alt=&amp;quot;logo&amp;quot; /&amp;gt;

// HTML
&amp;lt;input type=&amp;quot;text&amp;quot;&amp;gt;

// XHTML
&amp;lt;input type=&amp;quot;text&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;종료 태그가 없는 요소들&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;area,base, &lt;strong&gt;&lt;em&gt;br&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;col&lt;/em&gt;&lt;/strong&gt;, commend, embed, hr, &lt;strong&gt;&lt;em&gt;img&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;input&lt;/em&gt;&lt;/strong&gt;, keygen, &lt;strong&gt;&lt;em&gt;link&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;meta&lt;/em&gt;&lt;/strong&gt;, param, source&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;DOCTYPE&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;보통 DTD라고 표현을 하며, HTML 문서를 작성할 때 가장 먼저 선언하게 됩니다. HTML5를 기본적으로 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;스타일 선언&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;CSS는 외부 파일에 작성해 사용합니다. 이러한 작업 방식을 External Type 방식이라 명칭합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;/*  External Type  */
&amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;css/normalize.css&amp;quot;&amp;gt;
&amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;css/main.css&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;자바스크립트 선언&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;웹 브라우저가 렌더링을 마치기 전에 자바스크립트로 어떠한 동작을 실행해야 한다면 &lt;head&gt; 요소 안에 스크립트를 선언합니다.&lt;/li&gt;
&lt;li&gt;이런 상황이 아니라면 &lt;/body&gt; 태그 앞에 자바스크립트를 작성하는 것이 성능적인 측면에서 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;// 브라우저가 랜더링을 마치기 전에 실행할 파일을 선언한다.
&amp;lt;head&amp;gt;
  ...  
  &amp;lt;script src=&amp;quot;js/vendor/modernizr-2.8.3.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  ..
  // 모든 콘텐츠가 로드되고 실행될 스크립트 파일을 선언한다.
  &amp;lt;script src=&amp;quot;js/main.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Front-End/HTML,CSS</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/6</guid>
      <comments>https://simju9397.tistory.com/6#entry6comment</comments>
      <pubDate>Wed, 3 Mar 2021 09:52:36 +0900</pubDate>
    </item>
    <item>
      <title>Jenkins를 활용한 CI/CD 강의 노트 (2) : 젠킨스 개발환경 및 CI/CD 기본 동작 이해</title>
      <link>https://simju9397.tistory.com/5</link>
      <description>&lt;h3&gt;앞서&lt;/h3&gt;
&lt;p&gt;이전 글(&lt;a href=&quot;https://simju9397.tistory.com/4&quot;&gt;simju9397.tistory.com/4&lt;/a&gt;) 과 이어지는 내용입니다.&lt;/p&gt;
&lt;h3&gt;개발 환경의 종류&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;개발자가 개발을 하는 Local&lt;/li&gt;
&lt;li&gt;개발자들끼리 개발 내용에 대한 통합 테스트를 하는 Development 환경&lt;/li&gt;
&lt;li&gt;개발이 끝나고 QA 엔지니어 및 내부 사용자들이 사용해보기 위한 QA 환경&lt;/li&gt;
&lt;li&gt;실제 유저가 사용하는 Production 환경&lt;/li&gt;
&lt;li&gt;앞으로는 DEV, QA, PROD 환경으로 부른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;개발 프로세스&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;개발자가 Local에서 개발을 진행한다.&lt;/li&gt;
&lt;li&gt;다른 개발자가 작성한 코드와 차이가 발생하지 않는지 내부 테스트를 진행한다.&lt;/li&gt;
&lt;li&gt;진행한 내용을 다른 개발자들과 공유하기 위해 git 에 올린다. -&amp;gt; 흔히 dev 브랜치&lt;/li&gt;
&lt;li&gt;Dev 브랜치의 내용을 개발 환경에 배포하기 전에 테스트와 lint 등 코드 포맷팅을 한다.&lt;/li&gt;
&lt;li&gt;빌드 -&amp;gt; 배포 -&amp;gt; 테스트 -&amp;gt;각각의 환경에 위 과정을 다하고 환경에 배포&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;여러 배포 환경의 관리&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;인프라를 모듈화 하여 어떤 것이 변수인지 잘 설정하고 이를 잘 설계하는 것!!&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;가령 APP_ENV 처럼 현재 배포하고자 하는 것이 무슨 환경인지 설정하고, 앱 내에서 사용하는 다양한 변수들을 APP_ENV 에 맞게 잘 가져다 쓰는 것&lt;/li&gt;
&lt;li&gt;AWS System Manager 의 parameter store 와 같은 키 관리 서비스를 쓰자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;예시 배포 환경&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;웹사이트 코드 작성&lt;/li&gt;
&lt;li&gt;웹사이트 코드를 린트, 웹팩 빌드해서 AWS S3 bucket 에 html 파일 업로드&lt;/li&gt;
&lt;li&gt;Node.js 백엔드 코드를 typescript 로 작성&lt;/li&gt;
&lt;li&gt;위 코드를 javascript compile 하고, 테스트 코드를 돌려서 도커 이미지를 만들어 ECR 에 올린다.&lt;/li&gt;
&lt;li&gt;업로드한 ECR 이미지로 ECS 서비스를 재시작한다.(rolling deploy =&amp;gt; continuous deploy)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;개발에 필요한 리소스 리뷰&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;ECR : 도커 이미지를 저장하는 private repository&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ECS : 도커 컨테이너 기반으로 서비스 운용을 가능하게 해주는 간단한 서비스&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;무중단 배포를 제공. scale up 가능&lt;/li&gt;
&lt;li&gt;수많은 도커 컨테이너 서버를 띄우고 LB가 이들 사이에 밸런싱을 해줌&lt;/li&gt;
&lt;li&gt;fargate(ec2 안띄우고 전적으로 맡겨놓는 것), ec2 모드가 있어서 docker container 리소스만 띄우거나 물리적인 ec2 클러스터로 구성 가능.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;ECS 혹은 k8s 등을 통해 rolling deploy 가 처리 되기 때문에, jenkins 의 역할은 배포 명령만 내려주면 된다! (aws ecs update-service {serviceName} )&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;동작 방식 다이어그램&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b07c9i/btqYN18fCny/l0PtTy0KKjyiaAjdE7WzDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b07c9i/btqYN18fCny/l0PtTy0KKjyiaAjdE7WzDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b07c9i/btqYN18fCny/l0PtTy0KKjyiaAjdE7WzDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb07c9i%2FbtqYN18fCny%2Fl0PtTy0KKjyiaAjdE7WzDk%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;위의 예는 작은 규모의 서비스이며, 현업에서는 dev 와 prod 를 git 부터 aws 계정까지 물리적으로 분리해놓는다고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;마치며&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;다음 수업에서 드디어 시작을 할텐데, 잘 배울 수 있을지 조금 걱정되지만 재밌을 것 같다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DevOps/Jenkins</category>
      <category>cd</category>
      <category>CI</category>
      <category>CICD</category>
      <category>infra</category>
      <category>jenkins</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/5</guid>
      <comments>https://simju9397.tistory.com/5#entry5comment</comments>
      <pubDate>Mon, 1 Mar 2021 23:55:23 +0900</pubDate>
    </item>
    <item>
      <title>Jenkins를 활용한 CI/CD 강의 노트 (1) : 젠킨스(Jenkins) 이해하기</title>
      <link>https://simju9397.tistory.com/4</link>
      <description>&lt;h3&gt;개요&lt;/h3&gt;
&lt;p&gt;이 글은 T아카데미의 &lt;a href=&quot;https://www.youtube.com/watch?v=JPDKLgX5bRg&quot;&gt;세미나 영상&lt;/a&gt; 을 보고 제가 다시 보기 위해 정리하는 글입니다.&lt;/p&gt;
&lt;h3&gt;CI/CD?&lt;/h3&gt;
&lt;h4&gt;CI&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Continuous Integration : 지속적인 통합&lt;/li&gt;
&lt;li&gt;뭘 통합하나 ?&lt;ul&gt;
&lt;li&gt;여러 개발자의 코드베이스를 계속해서 통합&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;없다면?&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;10명의 개발자가 열심히 개발을 한다.&lt;/li&gt;
&lt;li&gt;Merge 어케하지..?&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;있다면?&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;10명의 개발자가 열심히 개발을 한다.&lt;/li&gt;
&lt;li&gt;커밋!&lt;/li&gt;
&lt;li&gt;로컬 테스트 통과 실패 ㅠ&lt;/li&gt;
&lt;li&gt;다시 커밋!&lt;/li&gt;
&lt;li&gt;코드 베이스 Merge&lt;/li&gt;
&lt;li&gt;만족!&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;테스트가 되어 있지 않은 무서운 코드를 애초에 없앤다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;CD&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Continuous Delivery : 지속적인 배달&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;뭘 배달해??&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;사용자에게 제품(서비스)을 지속적으로 배달&lt;/li&gt;
&lt;li&gt;코드베이스가 항상 배포가능한 상태를 유지하는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;즉, CI/CD란, 개발 환경을 사용자가 사용 가능한 서비스로 전달하는 모든 과정을&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;지속 가능한 형태로, 가능하다면 자동으로 해서&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;개발자와 사용자 사이의 격차를 없애는 것!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;코드 작성 -&amp;gt; 빌드 -&amp;gt; 테스트 -&amp;gt; 배포&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;없다면?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;백엔드 코드 개발&lt;/li&gt;
&lt;li&gt;프론트와 통신 해야하니 배포를 해볼까?&lt;/li&gt;
&lt;li&gt;저기 배포좀 해주세요..&lt;/li&gt;
&lt;li&gt;앗 버그났는데 다시 배포좀..&lt;/li&gt;
&lt;li&gt;데브 서버에 누가 배포했나요? 제꺼 안되는데;;&lt;/li&gt;
&lt;li&gt;앗 죄송..&lt;/li&gt;
&lt;li&gt;QA 배포..&lt;/li&gt;
&lt;li&gt;프로덕션 배포시 초긴장 유지..&lt;/li&gt;
&lt;li&gt;개발자가 코드만 짜면 되지 뭐이리 할게 많아 ㅜ&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;있다면?&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;개발&lt;/li&gt;
&lt;li&gt;끝!&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;What is Jenkins?&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;JRE 위에서 동작하는 자동화 서버.&lt;/li&gt;
&lt;li&gt;빌드, 테스트, 배포 등 모든 것을 자동화 해줌.&lt;/li&gt;
&lt;li&gt;난 개발만 할거니 니가 다해라!&lt;/li&gt;
&lt;li&gt;다양한 플러그인들을 활용해서 각종 자동화 작업을 처리할 수 있음.&lt;/li&gt;
&lt;li&gt;플러그인들을 잘 조립해서 , 일련의 자동화 작업의 순서들의 집한인 Pipeline 을 통해 CI/CD 파이프라인 구축.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Plugin&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;대표적인 플러그인들&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;Credential Plugin &lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;배포에 필요한 각종 리소스에 접근하기 위해서는 여러가지 중요 정보들을 저장하고 있어야 함.&lt;/li&gt;
&lt;li&gt;그런 중요한 정보들(AWS token, Git access token, ...)들을 저장해주는 플러그인.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Pipeline&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;플러그인들의 집합이자 구성&lt;/li&gt;
&lt;li&gt;여러 플러그인들을 파이프라인에서 용도에 맞게 사용하고 정의함으로써, 파이프라인을 통해 서비스가 배포됨.&lt;/li&gt;
&lt;li&gt;두 가지 형태의 Syntax 가 존재(Declarative, Scripted Pipeline)&lt;/li&gt;
&lt;li&gt;Declarative Pipeline Syntax 배울 예정.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Pipeline Syntax&lt;/h3&gt;
&lt;h4&gt;Sections&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Agent Section&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;젠킨스는 많은 일들을 해야 하기 때문에 혼자 하기 버겁다.&lt;/li&gt;
&lt;li&gt;여러 slave node 를 두고 일을 시킬 수 있는데, 어떤 젠킨스가 일을 하게 할 것인지 지정함.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Post Section&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;스테이지가 끝난 이후의 결과에 따라서 후속 조치를 취할 수 있다.&lt;/li&gt;
&lt;li&gt;if success -&amp;gt; slack 에 배포됐습니다 띄워준다던지&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Stages Section&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;어떤 일들을 처리할 건지 일련의 stage를 정의함.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Steps Section&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;한 스테이지 안에서의 단계로 일련의 스텝을 보여줌.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Declaratives&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Environment : 어떤 pipeline 이나 stage scope 의 환경 변수 설정&lt;/li&gt;
&lt;li&gt;Parameter : 파이프라인 실행 시 파라미터 받음.&lt;/li&gt;
&lt;li&gt;Trigger : 파이프라인이 무슨 주기로 실행되는지 설정&lt;/li&gt;
&lt;li&gt;When : 언제 실행되는가&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Steps&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Steps 내부는 여러가지 스텝들로 구성&lt;/li&gt;
&lt;li&gt;여러 작업들을 실행 가능&lt;/li&gt;
&lt;li&gt;플러그인을 깔면 사용할 수 있는 스텝들이 생겨남.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;pipeline 예시 코드&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-jenkins&quot;&gt;pipeline {
    agent any

    stages {
        stage(&amp;#39;Prepare&amp;#39;) { 
            steps {git pull}
            post { success { echo &amp;#39;success&amp;#39; }
        }

        stage(&amp;#39;Build&amp;#39;) { steps { echo &amp;#39;building...&amp;#39;}}
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DevOps/Jenkins</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/4</guid>
      <comments>https://simju9397.tistory.com/4#entry4comment</comments>
      <pubDate>Mon, 1 Mar 2021 21:09:59 +0900</pubDate>
    </item>
    <item>
      <title>NestJS(Node.js) 프레임워크로 SMS 문자 인증 서버 구현하기 (Naver Cloud Platform SENS API )</title>
      <link>https://simju9397.tistory.com/3</link>
      <description>&lt;h3&gt;개요&lt;/h3&gt;
&lt;p&gt;현재 진행하는 프로젝트에서 SMS 인증 기능이 추가될 것이라는 말을 듣고, 아직 한 번도 안해본 기능이라 해보고 싶었다. 꽤나 애를 먹었고, 기능 구현에 이 글이 도움이 되기를 바란다. 글의 이해를 위해 다음과 같은 기술들의 개념이 필요하다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;li&gt;NestJS Framework&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;li&gt;async/await&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;API Docs&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://apidocs.ncloud.com/ko/ai-application-service/sens/sms_v2/&quot;&gt;공식 API 문서 바로가기&lt;/a&gt;&lt;br /&gt;API 문서에도 나와있지만, NCP 의 API 인증 방식은 꽤 특이하다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.velog.io/images/junguksim/post/e8a54285-5fec-45e3-ad2a-e33b437cf728/image.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;Content-Type 은 그냥 해도 되는데, timestamp를 찍어줘야 한다.&lt;br /&gt;node.js 기준으로는 &lt;code&gt;Date.now().toString()&lt;/code&gt; 으로 보내주면 된다.&lt;br /&gt;access key 는 계정 관리 - 인증키 관리에서 Access Key ID를 쓰면 된다.&lt;/p&gt;
&lt;p&gt;마지막으로 signature 가 가장 어려웠는데, 생성 가이드대로 했는데도 계속 인증키 오류가 나서 1시간 정도 썼었다. 우리가 해싱해줘야하는 string은 다음 형식이다.&lt;/p&gt;
&lt;pre class=&quot;clojure&quot;&gt;&lt;code&gt;{METHOD} {uri}
{timestamp}
{accesskey}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SMS API를 예제로 하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;POST /sms/v2/services/{serviceId}/messages
Date.now().toString()
{accessKey}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이후, 이렇게 만들어진 string을 &lt;code&gt;crypto.createHmac()&lt;/code&gt; 함수를 통해, 계정 관리-인증키 관리에 있는 secret key로 해싱해주면 된다.&lt;/p&gt;
&lt;p&gt;전체 코드는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import {
  InternalServerErrorException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import axios from 'axios';
import * as crypto from 'crypto';

@Injectable()
export class AppService {
  constructor(
    private readonly config: ConfigService, // .env
  ) {}

  private makeSignature(): string {
    const message = [];
    const hmac = crypto.createHmac('sha256', this.config.get('NCP_secretKey'));
    const space = ' ';
    const newLine = '\n';
    const method = 'POST';
    const timestamp = Date.now().toString();
    message.push(method);
    message.push(space);
    message.push(this.config.get('uri'));
    message.push(newLine);
    message.push(timestamp);
    message.push(newLine);
    message.push(this.config.get('NCP_accessKey'));
    //message 배열에 위의 내용들을 담아준 후에
    const signature = hmac.update(message.join('')).digest('base64');
    //message.join('') 으로 만들어진 string 을 hmac 에 담고, base64로 인코딩한다
    return signature.toString(); // toString()이 없었어서 에러가 자꾸 났었는데, 반드시 고쳐야함.
  } 

  async sendSMS(phoneNumber: string): Promise&amp;lt;string&amp;gt; {
    const body = {
      type: 'SMS',
      contentType: 'COMM',
      countryCode: '82',
      from: this.config.get('hostPhoneNumber'), // 발신자 번호
      content: `문자 내용 부분 입니다.`,
      messages: [
        {
          to: phoneNumber, // 수신자 번호
        },
      ],
    };
    const options = {
      headers: {
        'Content-Type': 'application/json; charset=utf-8',
        'x-ncp-iam-access-key': this.config.get('NCP_accessKey'),
        'x-ncp-apigw-timestamp': Date.now().toString(),
        'x-ncp-apigw-signature-v2': this.makeSignature(),
      },
    };
    axios
      .post(this.config.get('url'), body, options)
      .then(async (res) =&amp;gt; {
        // 성공 이벤트
      })
      .catch((err) =&amp;gt; {
        console.error(err.response.data);
        throw new InternalServerErrorException();
      });

    return authNumber;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;마치며&lt;/h3&gt;
&lt;p&gt;처음 써본 api였는데, 사용법이 꽤 어려웠지만 네이버에서 제공하는 기능이니만큼 안정적일 것이고, 무엇보다 secret key로만 그치지 않고 암호화까지 하는 걸 보니 보안적인 측면에서도 괜찮을 것 같다. 성공적으로 문자 인증 서버를 NestJS를 통해 구축할 수 있었다.&lt;/p&gt;</description>
      <category>Back-End/NestJS</category>
      <category>NaverCloudPlatform</category>
      <category>NestJS</category>
      <category>nestjs config</category>
      <category>node.js</category>
      <category>sms인증</category>
      <category>문자인증</category>
      <category>캐시</category>
      <category>캐싱</category>
      <author>심정욱</author>
      <guid isPermaLink="true">https://simju9397.tistory.com/3</guid>
      <comments>https://simju9397.tistory.com/3#entry3comment</comments>
      <pubDate>Mon, 1 Mar 2021 21:04:11 +0900</pubDate>
    </item>
  </channel>
</rss>