<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>joey_log</title>
        <link>https://velog.io/</link>
        <description>취준생</description>
        <lastBuildDate>Thu, 29 Feb 2024 08:29:23 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>joey_log</title>
            <url>https://velog.velcdn.com/images/klee_623/profile/3d921067-0e71-4382-a93d-29d97bfc8719/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. joey_log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/klee_623" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Web Crawling - 테이블 데이터 취득하기(C#, Selenium)]]></title>
            <link>https://velog.io/@klee_623/Web-Crawling-%ED%85%8C%EC%9D%B4%EB%B8%94-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B7%A8%EB%93%9D%ED%95%98%EA%B8%B0C-Selenium</link>
            <guid>https://velog.io/@klee_623/Web-Crawling-%ED%85%8C%EC%9D%B4%EB%B8%94-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B7%A8%EB%93%9D%ED%95%98%EA%B8%B0C-Selenium</guid>
            <pubDate>Thu, 29 Feb 2024 08:29:23 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/klee_623/post/3d714b4a-9720-4dfd-92a5-ae4e92faaee6/image.png" alt=""> </p>
<blockquote>
</blockquote>
<p><strong>[과제]</strong>
위 페이지에서 테이블의 데이터를 취득해서 바탕화면의 엑셀 파일에 저장한다.
테이블의 세 번째 행인 &#39;공고명&#39;부터 &#39;입력일시&#39;까지 취득하며
마지막 행에는 데이터를 취득한 시간을 추가한다.</p>
<h4 id="1-nosuchelementexception">1. NoSuchElementException</h4>
<p>테이블을 개발자 도구로 검사하고 Xpath를 복사하고 검색하면 &#39;<strong>NoSuchElementException</strong>&#39; 에러가 발생한다.
이 문제는 HTML 문서가 여러 개의 frame으로 구성되어 있고 frame 마다 head와 body가 존재하기 때문에 발생한다.
내가 취득하려는 데이터가 있는 frame으로 이동하려면 다음과 같이 입력해야 한다.</p>
<pre><code>// Switch to main frame
driver.SwitchTo().Frame(&quot;sub&quot;);
driver.SwitchTo().Frame(&quot;main&quot;);</code></pre><h4 id="2-table-xpath---tr">2. table Xpath -&gt; tr</h4>
<p>그 다음 table의 Xpath를 복사하고, table의 element 중 tr 태그를 찾아 리스트에 저장한다.</p>
<pre><code>// Find table element
var table = driver.FindElement(By.XPath(&quot;//*[@id=\&quot;resultForm\&quot;]/div[2]/table&quot;));

// Find every table row
var rows = table.FindElements(By.TagName(&quot;tr&quot;));

// Save table data in &#39;tableData&#39;
List&lt;List&lt;string&gt;&gt; tableData = new List&lt;List&lt;string&gt;&gt;();

 // flag to check first row
bool isFirstRow = true; 
</code></pre><h4 id="3-데이터-취득특정-인덱스부터-내용-추가">3. 데이터 취득(특정 인덱스부터, 내용 추가)</h4>
<p>과제의 경우 공고명에서부터 입력일시까지의 데이터를 취득해야 했다. 
이를 구현하기 위해 td 태그의 3번째 인덱스부터 cells.Count-2까지 리스트에 저장하도록 반복을 수행했다. 
셀에 검색한 오늘의 날짜도 입력해야 해서 데이터를 취득한 현재 시간을 &#39;yyyy.MM.dd HH:mm&#39;의 골로 변환해서 행에 추가했다.</p>
<pre><code>foreach (var row in rows)
{
    // Not to add current date and time in first row
    if (isFirstRow)
    {
        isFirstRow = false;
        continue; 
    }

    // Find every &#39;td&#39; in every row
    var cells = row.FindElements(By.TagName(&quot;td&quot;));
    List&lt;string&gt; rowData = new List&lt;string&gt;();

    // Use for loop start with 3rd index
    for (int i = 3; i &lt; cells.Count-2; i++)
    {
        // Extract cell data
        rowData.Add(cells[i].Text);
    }

    // After extracting, add current date and time
    string currentDateTime = DateTime.Now.ToString(&quot;yyyy.MM.dd HH:mm&quot;);
    rowData.Add(currentDateTime);

    // If row data is not empty, then add to list &#39;tableData&#39;
    if (rowData.Count &gt; 0)
    {
        tableData.Add(rowData);
    }
}</code></pre><h4 id="4-바탕화면의-엑셀-파일-경로-가져오기">4. 바탕화면의 엑셀 파일 경로 가져오기</h4>
<p>바탕화면에 &#39;장표.xlsx&#39; 파일이 있다고 가정한다. 해당 파일의 경로를 가져와서 작업하기 위해 다음과 같이 작성한다. ExcelPackage를 사용할 때는 EPPlus 패키지가 필요하고 상업적, 비상업적으로 사용하겠다는 라이센스 설정이 필요하다. 필자는 비상업적으로 이용하기 때문에 이를 NonCommercial로 설정했다.</p>
<pre><code>// License setting as NonCommercial
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;

// Get folder path of desktop
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

// Set file path
string filePath = Path.Combine(desktopPath, &quot;장표.xlsx&quot;);

// Create ExcelPackage object
FileInfo fileInfo = new FileInfo(filePath);</code></pre><h4 id="5-취득한-데이터를-엑셀-워크시트에-추가하기">5. 취득한 데이터를 엑셀 워크시트에 추가하기</h4>
<p>가장 첫 번째 워크시트를 사용하고, 마지막 행을 찾아 한 줄을 더한 행에 데이터를 추가한다. 
table의 데이터를 저장한 &#39;tableData&#39; 리스트의 row를 반복문으로 추가한다.
전부 추가하고 나면 변동사항을 저장하고, 출력이 성공적으로 완수되었다는 콘솔 메세지를 출력한다.</p>
<pre><code> using (ExcelPackage package = new ExcelPackage(fileInfo))
{   
    // Use first worksheet
    ExcelWorksheet worksheet = package.Workbook.Worksheets[0]; 

    // Find last row to add extracted data
    int lastRow = worksheet.Dimension.End.Row + 1; 

    // Add data
    foreach (var row in tableData)
    {
        for (int i = 0; i &lt; row.Count; i++)
        {
            worksheet.Cells[lastRow, i + 1].Value = row[i];
        }
    lastRow++;
    }
    // Save changes
    package.Save();
}
Console.WriteLine(&quot;Extracting is sucessfully worked!&quot;);</code></pre><h4 id="6-실행결과">6. 실행결과</h4>
<p><img src="https://velog.velcdn.com/images/klee_623/post/e61563fe-d568-463b-9e4d-f5617a7fca34/image.png" alt="">전
<img src="https://velog.velcdn.com/images/klee_623/post/1b3bdfe2-06a9-44ed-a7c0-5ef990c3c65d/image.png" alt="">후</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Web Crawling - 검색 자동화(C#, Selenium)]]></title>
            <link>https://velog.io/@klee_623/Web-Crawling-%EA%B2%80%EC%83%89-%EC%9E%90%EB%8F%99%ED%99%94C-Selenium</link>
            <guid>https://velog.io/@klee_623/Web-Crawling-%EA%B2%80%EC%83%89-%EC%9E%90%EB%8F%99%ED%99%94C-Selenium</guid>
            <pubDate>Wed, 28 Feb 2024 15:33:43 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/klee_623/post/00f6324f-5e59-4ae3-8514-a966f27d708f/image.png" alt="">Visual Studio Code에서 C#을 이용해 크롤링하는 과제를 받았다.
크롤링 대상 사이트는 &#39;나라장터&#39;이다.
키워드와 기간을 검색한 후 테이블의 데이터를 엑셀에 저장하는 과제였다.</p>
<h3 id="1-vs-code-에서-c-프로젝트-만들기">1. VS code 에서 C# 프로젝트 만들기</h3>
<p><a href="https://www.youtube.com/watch?v=1PJ5lT4nCbM">How to Program C# in Visual Studio Code [2023]</a>
이 영상을 참고해서 C# 프로젝트를 생성했다.
방법은 다음과 같다.</p>
<blockquote>
<ol>
<li>Extension에서 C# Dev Kit 설치</li>
<li>VS code를 다시 시작하고 &#39;C# 개발 키트 시작&#39; 을 따라한다.</li>
<li>.NET SDK가 설치되었는지 확인한다. dotnet --version 커맨드로 설치 여부를 검사할 수 있다.</li>
<li>작업할 폴더를 연다. &#39;.csproj&#39; 파일이 위치한 폴더이다. 없으면 검색창에서 새로 생성한다.</li>
<li>검색창에 &gt;.NET: New Project 로 새 프로젝트를 생성할 수 있다.</li>
<li>새 프로젝트의 이름을 생성했다면 하위 파일 중 &#39;.cs&#39;가 포함된 파일에서 작업하면 된다.</li>
<li>실행과 디버그 창에서 &#39;모든 자동 디버그 구성 표시&#39; -&gt; &#39;인터페이스 변경&#39; 을 누르면 왼쪽 상단에 녹색 실행버튼이 생성되는데 이걸로 디버깅하면 된다. </li>
<li>추가로 GitHub Copilot을 설치하면 코딩할 때 도움이 된다고 한다.</li>
</ol>
</blockquote>
<h3 id="2-selenium">2. Selenium</h3>
<blockquote>
<p><a href="https://luckygg.tistory.com/246">[셀레니움/Selenium. C#] 웹 페이지의 텍스트 박스에 문자열 입력하기 (예제 포함)</a>
<a href="https://luckygg.tistory.com/245">[셀레니움/Selenium, C#] 웹 페이지의 버튼 클릭하기 (예제 포함)</a>
<a href="https://luckygg.tistory.com/251">[셀레니움/Selenium, C#] 테이블의 셀 데이터 취득하기 (예제 포함)</a></p>
</blockquote>
<p>상단의 블로그 게시물을 참고해서 전체 프로세스를 이해했다.
코드 작성은 GPT4를 사용했다. </p>
<h3 id="3-검색-자동화">3. 검색 자동화</h3>
<p><img src="https://velog.velcdn.com/images/klee_623/post/e9ec6726-23c1-4e46-b41e-a7acda42ba9a/image.png" alt="">위 캡쳐본은 나라장터의 검색창의 일부를 캡쳐한 것이다.</p>
<p>과제는 다음과 같다.</p>
<blockquote>
<ol>
<li>공고명에 &#39;RPA&#39;를 입력한다.</li>
<li>공고/개찰일에 각각 오늘날짜로부터 5일전 날짜/오늘날짜를 입력한다.</li>
<li>검색버튼을 클릭한다.</li>
</ol>
</blockquote>
<p>이 세 가지 단계를 자동화해야 했다.
**</p>
<ol>
<li>공고명에 &#39;RPA&#39;를 입력한다.<em>*
텍스트 박스를 개발자 도구로 검사한다. 공고명 검색창의 Xpath는 //</em>[@id=&quot;bidNm&quot;] 이다. 이제 이 태그를 찾아 검색어를 key로 보내주면 된다.</li>
</ol>
<p>아래 코드는 다음과 같이 동작한다.</p>
<blockquote>
<p>1-1. 원하는 URL로 이동한다.
1-2. 페이지가 정상적으로 로딩될 때까지 기다린다.
1-3. id 태그가 bidNm인 element를 찾아서 &quot;RPA&quot;를 key로 전송한다.</p>
</blockquote>
<pre><code>// Navigate to the website
driver.Navigate().GoToUrl(&quot;https://www.g2b.go.kr/index.jsp&quot;);

// Wait for the page to load completely
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));

// Find the search input by its ID and enter a search term
var searchInput = driver.FindElement(By.Id(&quot;bidNm&quot;));
searchInput.SendKeys(&quot;RPA&quot;);</code></pre><p><strong>2. 공고/개찰일에 각각 오늘날짜로부터 5일전 날짜/오늘날짜를 입력한다.</strong>
다음으로 조회하려는 기간을 &#39;yyyy/MM/dd&#39;의 형태로 입력해야 했다. 이를 위해 오늘 날짜를 계산하고 4를 빼서 조회기간을 설정했다. 그 후 날짜 입력 창의 id에 조회기간을 key로 전송한다.</p>
<p>아래 코드는 다음과 같이 동작한다.</p>
<blockquote>
<p>2-1. 오늘 날짜와 5일 전 날짜를 계산한다.
2-2. 데이터를 yyyy/MM/dd 형태로 format한다.
2-3. id 태그가 fromBidDt와 toBidDt인 element를 찾아 format 한 데이터를 입력한다. 검색창이 채워져 있을 수도 있으니 Clear()로 지워준다.</p>
</blockquote>
<pre><code>// Calculate the today dates
DateTime today = DateTime.Today;
DateTime fiveDaysAgo = today.AddDays(-4);

// Format the dates as YYYY/MM/DD
string todayFormatted = today.ToString(&quot;yyyy/MM/dd&quot;, CultureInfo.InvariantCulture);
string fiveDaysAgoFormatted = fiveDaysAgo.ToString(&quot;yyyy/MM/dd&quot;, CultureInfo.InvariantCulture);

// Find the start date input by its ID and enter the calculated date
var fromDateInput = driver.FindElement(By.Id(&quot;fromBidDt&quot;));
fromDateInput.Clear(); // Clears any pre-filled values in the input field
fromDateInput.SendKeys(fiveDaysAgoFormatted);

// Find the end date input by its ID and enter today&#39;s date
var toDateInput = driver.FindElement(By.Id(&quot;toBidDt&quot;));
toDateInput.Clear(); // Clears any pre-filled values in the input field
toDateInput.SendKeys(todayFormatted);</code></pre><p><strong>3. 검색버튼을 클릭한다.</strong>
검색 버튼의 Xpath를 복사해서 Click()한다.</p>
<pre><code>// After entering the dates, click the search button 
var searchButton = driver.FindElement(By.XPath(&quot;//*[@id=\&quot;searchForm\&quot;]/div/fieldset[1]/ul/li[4]/dl/dd[3]/a/strong&quot;));
searchButton.Click();</code></pre><p>위 단계를 거치면 검색 자동화 완성이다.
코드를 실행하면 자동으로 테이블이 위치한 페이지로 이동한다.
다음 게시글에서 테이블의 셀 데이터를 취득하는 방법을 알아보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] 게으른 백곰]]></title>
            <link>https://velog.io/@klee_623/%EB%B0%B1%EC%A4%80-%EA%B2%8C%EC%9C%BC%EB%A5%B8-%EB%B0%B1%EA%B3%B0</link>
            <guid>https://velog.io/@klee_623/%EB%B0%B1%EC%A4%80-%EA%B2%8C%EC%9C%BC%EB%A5%B8-%EB%B0%B1%EA%B3%B0</guid>
            <pubDate>Fri, 09 Feb 2024 14:59:21 GMT</pubDate>
            <description><![CDATA[<p>첫 접근</p>
<ul>
<li>양동이가 놓일 수 있는 위치를 미리 0 으로 만든다.</li>
<li>양동이 위치를 하나씩 방문해, 해당 양동이부터 -K, +K 까지 범위에 얼음이 있는 양동이가 존재한다면, 양동이에 얼음을 합산한다.</li>
<li>최대 얼음이 존재하는 양동이의 인덱스 위치를 반환한다.</li>
</ul>
<pre><code>N, K = map(int, input().split())

xg = {}
for _ in range(N):
    g, x = map(int, input().split())
    xg[x] = g
xg1 = dict(sorted(xg.items()))

mx = max(xg1)
mn = min(xg1)

length = mx - mn + 1
bag = [0 for _ in range(length)]

for i in range(mn, mx + 1):
    for j in range(i - K, i + K + 1):
        if j in xg1:
            bag[i - mn] += xg1[j]

print(bag.index(max(bag)) + 1)</code></pre><p>결과</p>
<ul>
<li><strong>시간초과</strong></li>
<li>비효율적인 탐색, 불필요한 반복</li>
</ul>
<hr>
<p><strong>투포인터 알고리즘</strong> or <strong>슬라이딩 윈도우</strong> 기법</p>
<ul>
<li>각 구간의 합을 한 번만 계산한다. 구간이 이동할 때마다 더하거나 빼는 방식으로 빠르게 구간 합을 업데이트할 수 있다.</li>
</ul>
<p><a href="https://wooono.tistory.com/648">참고한 솔루션</a></p>
<p>구간합 = 슬라이딩 윈도우</p>
<ul>
<li>윈도우의 사이즈를 2*K + 1로 설정한다.</li>
<li>윈도우를 한 칸씩 옮겨가면서 마지막 값은 추가하고, 첫 번째 값은 빼주는 식으로 윈도우 값을 업데이트한다.<pre><code># 슬라이딩 윈도우
N, K = map(int, input().split())
ice = [0] * 10000001
</code></pre></li>
</ul>
<p>for _ in range(N):
    g, x = map(int, input().split())
    ice[x] = g</p>
<h1 id="초기-윈도우">초기 윈도우</h1>
<p>window = sum(ice[:2*K+1])
max_sum = window</p>
<p>for i in range(1, 10000001 - 2<em>K):
    window = window - ice[i - 1] + ice[i + 2</em>K]
    max_sum = max(window, max_sum)</p>
<p>print(max_sum)</p>
<p>```</p>
<p>투포인터 익숙해지기.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 겹치는 선분의 길이]]></title>
            <link>https://velog.io/@klee_623/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B2%B9%EC%B9%98%EB%8A%94-%EC%84%A0%EB%B6%84%EC%9D%98-%EA%B8%B8%EC%9D%B4</link>
            <guid>https://velog.io/@klee_623/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B2%B9%EC%B9%98%EB%8A%94-%EC%84%A0%EB%B6%84%EC%9D%98-%EA%B8%B8%EC%9D%B4</guid>
            <pubDate>Fri, 09 Feb 2024 14:55:35 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/klee_623/post/ae981527-134c-4efd-8fb9-2c48e246108a/image.png" alt=""></p>
<h3 id="겹치는-선분의-길이">겹치는 선분의 길이</h3>
<pre><code>def solution(line):
    answer = 0
    count = [0 for _ in range(200)]
    for l in line:
        for i in range(l[0], l[1]):
            count[i + 100] += 1

    answer += count.count(2)
    answer += count.count(3)
    return answer</code></pre><p>첫 번째 솔루션</p>
<ul>
<li>선분의 길이 제한이 -100 이상, 100 이하인 정보를 사용한다.</li>
<li>각 점의 위치마다 선분이 지나가면 점에 +1 을 추가한다. 모든 선분을 지난 후 해당 점이 포함된 선분의 개수가 2개 혹은 3개인 경우에만 정답으로 세서 겹치는 선분의 총 길이를 계산한다.</li>
</ul>
<hr>
<pre><code>def solution(line):
    sets = [set(range(min(l), max(l))) for l in line]
        return len(sets[0] &amp; sets[1] | sets[0] &amp; sets[2] | sets[1] &amp; sets[2])</code></pre><p>두 번째 솔루션</p>
<ul>
<li>set()으로 최소, 최소 길이를 받아주고 두 번 겹치는 선분은 and 연산으로 결합한다. 이러면 겹치는 선분의 범위만 구할 수 있다.</li>
<li>겹치는 선분의 최종 합을 구하려면 겹치는 선분끼리는 or 연산으로 결합해야 한다. 이때 set를 사용하기 때문에 범위가 중복되지 않는다.</li>
</ul>
<p>후기</p>
<ul>
<li>level 0 치고는 아이디어 떠올리기가 어려웠던 문제라고 생각한다. 겹치는 선분의 길이의 합은 겹치는 점의 총 갯수를 세서 구할 수 있다. set을 사용하는 방법은 떠올리기 쉽지 않을 것 같다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[연속된 수의 합, 1]]></title>
            <link>https://velog.io/@klee_623/%EC%97%B0%EC%86%8D%EB%90%9C-%EC%88%98%EC%9D%98-%ED%95%A9-1</link>
            <guid>https://velog.io/@klee_623/%EC%97%B0%EC%86%8D%EB%90%9C-%EC%88%98%EC%9D%98-%ED%95%A9-1</guid>
            <pubDate>Wed, 07 Feb 2024 15:26:47 GMT</pubDate>
            <description><![CDATA[<h4 id="프로그래머스-연속된-수의-합">프로그래머스: 연속된 수의 합</h4>
<p>난이도: Level 0</p>
<pre><code>def solution(num, total):
    &#39;&#39;&#39;
    num개의 수의 합이 total과 같은지를 확인하는 문제
    start, start+1, start+2, ... start+num-1
    = num*start + num*(num-1)//2

    start를 어떻게 결정할 것인가?
    num이 홀수인 경우
        total // num = 가운데 수
        start = total // num - num // 2
    num이 짝수인 경우
        total // num = 가운데 바로 옆수
        start = total // num - num // 2 + 1
    &#39;&#39;&#39;

    answer = []
    start = 0

    # num이 홀수
    if num%2:
        start = total // num - num // 2
    # num이 짝수
    else:
        start = total // num - num // 2 + 1

    for i in range(start, start + num):
        answer.append(i)
    return answer</code></pre><h4 id="백준-1-4375번">백준: 1 (4375번)</h4>
<p>난이도: 실버3</p>
<pre><code>&#39;&#39;&#39;
각 자리가 모두 1로 구성된 n의 배수의 자리수를 출력하는 문제

n의 배수를 구해서 1로 구성됐는지 확인한다 X
1의 배수를 만들어서 n의 배수인지 확인한다 o
&#39;&#39;&#39;
import sys

while 1:
    try:
        n = int(sys.stdin.readline())
    except:
        break

    # i는 자리수
    i = 1
    check_num = 1

    while 1:
        if check_num % n == 0:
            print(i)
            break
        check_num = 10*check_num + 1
        i += 1</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[저주의 숫자 3, 쉬운 계단 수]]></title>
            <link>https://velog.io/@klee_623/%EC%A0%80%EC%A3%BC%EC%9D%98-%EC%88%AB%EC%9E%90-3-%EC%89%AC%EC%9A%B4-%EA%B3%84%EB%8B%A8-%EC%88%98</link>
            <guid>https://velog.io/@klee_623/%EC%A0%80%EC%A3%BC%EC%9D%98-%EC%88%AB%EC%9E%90-3-%EC%89%AC%EC%9A%B4-%EA%B3%84%EB%8B%A8-%EC%88%98</guid>
            <pubDate>Tue, 06 Feb 2024 15:01:34 GMT</pubDate>
            <description><![CDATA[<h3 id="저주의-숫자-3">저주의 숫자 3</h3>
<p>저주의 숫자 3
프로그래머스 LEVEL 0
조건:
3의 배수와 숫자 3을 사용하지 않는다.</p>
<p>풀이:
n만큼 for문을 돌면서
3을 포함하거나 3의 배수인 경우에는 + 1 한다.
증가한 숫자가 저주의 숫자인 경우도 검사하기 위해
while 문을 사용</p>
<pre><code>def solution(n):
    i = 0
    for _ in range(n):
        i += 1
        while i % 3 == 0 or &#39;3&#39; in str(i):
            i += 1
    return i</code></pre><hr>
<h3 id="10844-쉬운-계단-수">10844: 쉬운 계단 수</h3>
<p>아이디어</p>
<ul>
<li><p><strong>점화식, 2차원 DP 테이블</strong></p>
</li>
<li><p>DP 테이블: dp[자리수][해당 자리에서 가장 뒤에 오는 숫자] = 경우의 수</p>
</li>
<li><p>점화식의 세분화</p>
<ul>
<li><p>가장 뒤에 오는 숫자 = 0)</p>
<ul>
<li><p>0 앞에 올 수 있는 건 1 하나 뿐이다.</p>
<p>  dp[자릿수][0] = 1</p>
</li>
</ul>
</li>
<li><p>가장 뒤에 오는 숫자 = 1~8)</p>
<ul>
<li>dp[자리수][가장 뒤에 오는 숫자] = dp[자리수-1][가장 뒤에 오는 숫자-1] + dp[자리수-1][가장 뒤에 오는 숫자+1]</li>
</ul>
</li>
<li><p>가장 뒤에 오는 숫자 = 9)</p>
<ul>
<li>dp[자리수][9] = 1</li>
<li>9 앞에 올 수 있는 건 8 하나 뿐이다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code># 1일 1백준
import sys
n = int(sys.stdin.readline())

# dp 테이블 초기화
dp = [[0]*10 for _ in range(n+1)]

# 1의 자릿수의 경우 초기화
for i in range(1, 10):
    dp[1][i] = 1

for i in range(2, n+1):
    for j in range(10):
        if j == 0:
            dp[i][j] = dp[i-1][1]
        elif 1 &lt;= j &lt;= 8:
            dp[i][j] = dp[i-1][j-1] + dp[i-1][j+1]
        else:
            dp[i][j] = dp[i-1][8]
print(sum(dp[n]) % 10**9)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 기초 강좌 : 100분 완성]]></title>
            <link>https://velog.io/@klee_623/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EC%B4%88-%EA%B0%95%EC%A2%8C-100%EB%B6%84-%EC%99%84%EC%84%B1</link>
            <guid>https://velog.io/@klee_623/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EC%B4%88-%EA%B0%95%EC%A2%8C-100%EB%B6%84-%EC%99%84%EC%84%B1</guid>
            <pubDate>Mon, 05 Feb 2024 16:42:39 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>240204 - 240205
<a href="https://youtu.be/KF6t61yuPCY?si=AvD9q8BhhX7_TF0M">자바스크립트 기초 강좌 : 100분 완성</a> 강의를 수강하고 공부한 내용을 정리한 것입니다. </p>
</blockquote>
<h2 id="240204">240204</h2>
<h3 id="출력-함수">출력 함수</h3>
<ul>
<li>alert(): 브라우저 경고창 출력하기</li>
<li>console.log(): 콘솔창에 출력하기</li>
</ul>
<h3 id="변수의-자료형">변수의 자료형</h3>
<ul>
<li>let, const</li>
<li>차이점: let은 변수에 다른 값을 재할당할 수 있지만, const는 상수로 한 번만 선언이 가능하며 값을 바꿀 수도 없다.</li>
</ul>
<h3 id="변수의-약속">변수의 약속</h3>
<ul>
<li><ol>
<li>변수는 문자와 숫자, $만 사용한다.</li>
</ol>
</li>
<li><ol start="2">
<li>첫 글자는 숫자가 될 수 없다.</li>
</ol>
</li>
<li><ol start="3">
<li>예약어는 사용할 수 없다.</li>
</ol>
</li>
<li><ol start="4">
<li>가급적 상수는 대문자로 작성한다.</li>
</ol>
</li>
<li><ol start="5">
<li>변수명은 읽기 쉽고 이해하기 쉽게 선언한다.</li>
</ol>
</li>
</ul>
<h3 id="자료형">자료형</h3>
<ul>
<li>\뒤에 선언하면 특수문자로 인식한다.</li>
<li>₩벡틱은 문자를 변수로 표현해줄 때 효과적이다.<ul>
<li>${변수명}</li>
</ul>
</li>
<li>숫자형은 사칙연산이 가능하다.</li>
<li>NaN = Not A Number</li>
<li>boolean = true, false</li>
<li>null = 값이 존재하지 않는다 / undefined = 값이 정의되지 않았다.</li>
<li>typeof 연산자는 변수의 자료형을 출력한다.</li>
<li>숫자형을 문자형과 더할 수 있다. 다만 이 경우, 숫자형이 문자형으로 변환됨에 주의하자.</li>
</ul>
<h3 id="객체형">객체형</h3>
<ul>
<li>null ≠ 객체</li>
</ul>
<h3 id="주석">주석</h3>
<ul>
<li>//</li>
</ul>
<h3 id="대화상자">대화상자</h3>
<ul>
<li>alert, prompt, confirm</li>
<li>alert() = 일방적인 알림 용도</li>
<li>prompt() = 사용자에게 메세지를 보여주고, 어떤 값을 입력받을 때 사용한다.    <ul>
<li>취소를 입력하면 null이 입력된다.</li>
<li>인수 = 함수의 입력에 들어가는 값</li>
<li>두 개 이상의 인수가 들어가면 마지막 인수가 디폴트 인수가 된다. 안내 사항을 전달할 때 좋다.</li>
<li>prompt()로 입력받은 값은 문자형으로 입력된다.</li>
</ul>
</li>
<li>confirm() = 사용자로부터 확인 받을 때 사용한다.<ul>
<li>alert()와 다른 점은 취소버튼이 추가되어 있음.</li>
</ul>
</li>
</ul>
<p>대화상자 예시코드</p>
<pre><code class="language-jsx">const name = prompt(&quot;메세지&quot;);
alert(&quot;안녕하세요&quot; + name + &quot;님.&quot;);
alert(`안녕하세요 ${name}님.`);</code></pre>
<p>기본 알림창의 단점</p>
<ul>
<li>스크립트가 일시정지된다.</li>
<li>스타일링이 불가능하다. 디자이너가 싫어한다.</li>
</ul>
<h3 id="자료형-변환">자료형 변환</h3>
<ul>
<li>String() = 문자형으로 변환</li>
<li>Nubmer() = 숫자형으로 변환<ul>
<li>true 와 false는 Number()로 형변환하면 1과 0이 나온다.</li>
</ul>
</li>
<li>사칙연산에는 자동형변환이 적용된다. 원인을 찾기 힘드니까 의도를 가지고 명시적 형변환을 해주는 것이 좋다.</li>
</ul>
<p><strong>암기: 자료형 주의사항</strong></p>
<pre><code class="language-jsx">Number(null) // 0
Number(undefined) // NaN

Number(0) // false
Number(&#39;0&#39;) // true
Number(&#39;&#39;) // false
Number(&#39; &#39;) // true</code></pre>
<h3 id="연산자">연산자</h3>
<ul>
<li>증감연산자<ul>
<li>앞에 붙이면 증가한 값을 반영, 뒤에 붙이면 값을 반영한 후 증가. C랑 똑같다.</li>
</ul>
</li>
<li>=== : 일치연산자, 자료형까지 검사한다. 동등연산자보다 일치연산자로 검사하는게 좋다.</li>
</ul>
<h3 id="조건문">조건문</h3>
<pre><code class="language-jsx">if(조건문의 내용){
    조건을 만족하면 수행하는 부분
}</code></pre>
<h3 id="논리연산자">논리연산자</h3>
<ul>
<li>|| : OR, 첫 번째 true를 발견하는 즉시, 평가를 멈춘다.</li>
<li>&amp;&amp; : AND, 첫 번째 false를 발견하는 즉시, 평가를 멈춘다.</li>
<li>! : NOT</li>
<li><strong>어떤 평가로 순서를 배치하는지도 중요하다. 성능 최적화를 위함.</strong></li>
</ul>
<h3 id="반복문">반복문</h3>
<pre><code class="language-jsx">// for
for(let i;i &lt; 10;i++){
    // 반복할 코드
}

// while
let i = 0;
while(i&lt;10){
    // code
    i++;
}

// do while
let i = 0;
do{
// code
    i++;
} while(i&lt;10)
</code></pre>
<p>반복문 탈출 - break, continue</p>
<ul>
<li>break = 멈추고 반복문 탈출</li>
<li>continue = 멈추고 다음 반복문</li>
</ul>
<p><strong>개발자는 항상 좀 더 쉽게 일하고, 코드를 줄이기 위해 노력해야 한다. 그래야 성능도 좋아진다.</strong></p>
<h3 id="switch-문">Switch 문</h3>
<ul>
<li>if else 를 알고 있으면 몰라도 된다.</li>
<li>case가 많을 때 사용하기 유용하다는 것만 알면된다.</li>
</ul>
<pre><code class="language-jsx">switch(평가){
    case A:
    // A일 때 코드
    case B:
    // B일 때 코드
    default:
    // code
}</code></pre>
<ul>
<li>switch는 case 이후 모든 문장을 출력한다. 각 case마다 break를 걸어주자.</li>
<li>case에 없는 입력값이 주어지면 제대로 동작한 건지, 오류가 난건지 사용자 입장에서 모르기 때문에 default를 사용한다.</li>
</ul>
<h3 id="함수function">함수(function)</h3>
<ul>
<li>자주 사용하거나 여러 곳에서 사용하는 코드라면 하나로 만든 다음 재사용한다. 중복되는 코드도 줄어들고 재사용성도 좋아진다.</li>
<li>브라우저 내장함수: console, alert, confirm</li>
</ul>
<pre><code class="language-jsx">// 함수 선언 예시
function sayHello(name){
    console.log(`Hello, ${name}`);
}</code></pre>
<ul>
<li>매개변수가 여러개라면 쉼표로 구분할 수 있다.</li>
<li>let으로 선언한 변수는 다시 let으로 선언할 수 없다.</li>
</ul>
<hr>
<h2 id="240205">240205</h2>
<ul>
<li>전역변수와 지역변수는 서로 간섭을 받지 않는다.</li>
<li>함수 선언시 매개변수의 디폴트 값을 선언해줄 수 있다.<pre><code class="language-jsx">// default value
</code></pre>
</li>
</ul>
<p>function sayHello(name = &#39;friend&#39;){
    let msg = <code>Hello, ${name}</code>
    console.log(msg)
}</p>
<p>sayHello() 
// result = friend
sayHello(&#39;Jane&#39;)
// result = Jane</p>
<pre><code>
```jsx
// return으로 값 반환
function add(num1, num2){
    return num1 + num2;
}

const result = add(2,3);
console.log(result);
// 5</code></pre><ul>
<li>return 문이 쓰이거나 return만 단독으로 쓰이면, undefined를 반환한다.</li>
<li>return 만 선언해서 함수를 종료하는 목적으로도 사용한다.</li>
<li>읽기 쉽고 어떤 동작인지 알 수 있게 네이밍한다.</li>
</ul>
<h3 id="함수-선언문-vs-함수-표현식">함수 선언문 vs 함수 표현식</h3>
<ul>
<li>순차적으로 코드를 읽으면서 즉시 결과를 반환하는 언어를 인터프리터 언어라고 한다.</li>
<li>함수를 함수 선언문으로 작성하면 어느 위치에서든 함수를 선언하고 사용할 수 있다.</li>
<li>자바스크립트 내부 알고리즘은 실행 전 초기화 단계에서 코드의 모든 함수 선언문을 찾아서 생성해둔다. 함수의 사용 가능 범위는 코드보다 높아진다. 이를 Hoisting이라고 한다.</li>
<li>함수 표현식은 코드에 도달했을 때 함수가 생성된다. 함수 표현식으로 함수를 작성할 경우, 함수 표현식 이후에만 해당 함수를 사용할 수 있다.</li>
<li>함수 선언문이 함수 표현식보다 더 자유롭다.</li>
</ul>
<h3 id="화살표-함수arrow-function">화살표 함수(arrow function)</h3>
<ul>
<li>한 줄씩 존재하는 건 대부분 생략 가능하다.</li>
<li>return 문 하나만 작성되는 경우, 소괄호로 나타낼 수 있다.</li>
</ul>
<pre><code class="language-jsx">// 화살표 함수 변환 전
const sayHello = function(name) {
}
// 화살표 함수 변환 후
const sayHello = (name) =&gt; {
}

// 전
const add = function(num1, num2) {
    const result = num1 + num2;
    return result;
}

// 후
const add = (num1, num2) =&gt; num1 + num2; </code></pre>
<ul>
<li>화살표 함수는 ES6 이후 활발하게 사용되고 있기 때문에 필수로 알고 있어야 한다.</li>
</ul>
<h3 id="객체object">객체(object)</h3>
<ul>
<li>객체는 key와 value인 property로 구분된다. 파이썬의 dictionary와 비슷하다.</li>
<li>각 property는 쉼표로 구분한다. 마지막 쉼표는 없어도 무방하지만 있는게 수정이 용이하다.</li>
<li>접근, 추가, 삭제    <pre><code class="language-jsx">// Object
const superman = {
  name:&#39;clark&#39;;
  age;33,
}
</code></pre>
</li>
</ul>
<p>// 접근
superman.name // &#39;clark&#39;
superman[&#39;age&#39;] // 33</p>
<p>// 추가
superman.gender = &#39;male&#39;;
superman[&#39;haircolor&#39;] = &#39;black&#39;;</p>
<p>// 삭제
delete superman.haircolor; </p>
<pre><code>
- in 연산자를 사용하면 객체에 특정 property가 존재하는지 검사할 수 있다.
- for(let key in object) {} 로 선언한다.

```jsx
// for in 문 예제
const Mike = {
    name: &#39;Mike&#39;,
    age: 33,
}

for(x in Mike){
    console.log(x); // &#39;name&#39;, &#39;age&#39;
    console.log(Mike[x]); // &#39;Mike&#39;, 33
}</code></pre><h3 id="객체-method--this">객체 method / this</h3>
<ul>
<li><p>객체 property로 할당된 함수를 method라고 부른다. (C)</p>
</li>
<li><p>fly: function() {} 를 fly() {} 처럼 단축 구문으로도 작성할 수 있다.</p>
</li>
<li><p>객체와 method의 관계 (this)</p>
</li>
<li><p>내 자신의 property를 method에 넣고 싶다면 this를 사용한다.</p>
<pre><code class="language-jsx">  // 객체 지향 프로그래밍
  sayHello: function() {
      console.log(&#39;Hello, I&#39;m ${this.name}&#39;);
  }

  let boy = {
      name: &#39;Mike&#39;,
      sayHello(),
  }

  let girl = {
      name: &#39;Jane&#39;,
      sayHello(),
  }</code></pre>
<ul>
<li>this는 런타임에 결정된다.</li>
<li>주의: 화살표 함수는 this를 가지지 않는다.</li>
<li>화살표 함수 내부에서 this를 사용하면, 그 this는 외부에서 값을 가져온다. (전역객체를 가리킨다.)</li>
<li>this가 가장 어렵다.</li>
<li>메소드에서는 this를 활용하는데 가장 좋다.</li>
<li>메소드는 화살표 함수로 작성하는 것을 지양한다.</li>
</ul>
</li>
</ul>
<h3 id="array">Array</h3>
<p>선언</p>
<ul>
<li>let 배열명 = [요소1, 요소2, … 요소n개];</li>
</ul>
<p>접근</p>
<ul>
<li>배열명.[index값]</li>
</ul>
<p>수정 </p>
<ul>
<li>배열명.[index값] = ‘수정값’;</li>
</ul>
<p>특징</p>
<ul>
<li>숫자, 문자, 함수, 객체 포함 가능</li>
</ul>
<p>길이</p>
<ul>
<li>배열명.length;</li>
</ul>
<p>뒤에 추가</p>
<ul>
<li>배열명.push(추가요소);</li>
</ul>
<p>뒤에 제거</p>
<ul>
<li>배열명.pop();</li>
</ul>
<p>앞에 추가</p>
<ul>
<li>배열명.unshift(추가요소);</li>
</ul>
<p>앞에 제거 </p>
<ul>
<li>배열명.shift();</li>
</ul>
<p>추가(push, unshift)의 경우 여러 개를 추가할 수도 있다.</p>
<p>반복문 for</p>
<ul>
<li>for(let index=0;index&lt;days.length;index++){<pre><code>  console.log(days[index])</code></pre>  }</li>
</ul>
<p>for of: for문 보다 간단하지만 인덱스를 얻지 못한다는 단점이 있다.</p>
<h2 id="정리">정리</h2>
<p>강의로 학습한 내용은 다음과 같다.</p>
<ul>
<li>변수, 자료형 이해</li>
<li>console로 출력 확인</li>
<li>alert, prompt, confirm</li>
<li>연산자, 조건문, 반복문, 함수와 객체, 배열에 대해 간단하게 학습함.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[다항식 더하기, 수리공 항승]]></title>
            <link>https://velog.io/@klee_623/%EB%8B%A4%ED%95%AD%EC%8B%9D-%EB%8D%94%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@klee_623/%EB%8B%A4%ED%95%AD%EC%8B%9D-%EB%8D%94%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 30 Jan 2024 15:57:28 GMT</pubDate>
            <description><![CDATA[<h4 id="프로그래머스">프로그래머스</h4>
<p>[다항식 더하기]</p>
<ul>
<li>‘+’로 구분하여 리스트에 담기 = .split(’+’)</li>
<li>가장 마지막 원소가 ‘x’인 경우 x의 계수값에, 아닌 경우 상수값에 더해준다.</li>
<li>계수의 합이 0인 경우, 상수만을 출력한다.</li>
<li>계수의 합이 1인 경우, 1x 대신 x를 출력한다.</li>
<li>원소가 숫자인지 검사하고 싶다 = .isdigit()</li>
<li>원소가 문자인지 검사하고 싶다 = .isalpha()</li>
<li>level 0 치고는 까다로웠던 문제</li>
</ul>
<h4 id="백준">백준</h4>
<p>[수리공 항승]</p>
<p>아이디어는 테이프를 이어붙인 범위 내에 구멍이 존재하는지 여부를 검사하는 것.</p>
<p>필요한 테이프의 길이의 총합을 구해서 필요한 테이프의 개수를 구한다 X</p>
<p>테이프가 필요할 때마다 개수를 한 개씩 추가한다.</p>
<p>필요할 때를 어떻게 아느냐? 시작지점부터 테이프 길이를 붙인다. 테이프 길이 안에 포함되면 기존의 테이프로 막을 수 있다.구멍이 1,2 테이프 길이가 2라면range(1, 3)까지는 기존 테이프로 막을 수 있음.하지만 3부터는 불가능하기 때문에 다음 구멍이 3을 넘어가면 그 값부터 다시 출발하도록 한다.</p>
<pre><code class="language-jsx"># 1일 1백준
N, L = map(int, input().split())
hole = sorted(list(map(int, input().split())))

start = hole[0]
count = 1

for h in hole[1:]:
    if h in range(start, start + L):
        continue
    else:
        start = h
        count += 1
print(count)</code></pre>
<ul>
<li>의외로 간단했던 문제, 아이디어 접근을 잘못했다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[특이한 정렬, 세 개의 소수 문제]]></title>
            <link>https://velog.io/@klee_623/%EC%98%A4%EB%8A%98%EC%9D%98-%EB%B2%A8%EB%A1%9C%EA%B7%B8</link>
            <guid>https://velog.io/@klee_623/%EC%98%A4%EB%8A%98%EC%9D%98-%EB%B2%A8%EB%A1%9C%EA%B7%B8</guid>
            <pubDate>Mon, 29 Jan 2024 15:48:34 GMT</pubDate>
            <description><![CDATA[<h4 id="프로그래머스">프로그래머스</h4>
<p>문제 - 특이한 정렬</p>
<ul>
<li>정렬에 우선순위 부여하기</li>
<li>sorted(array, key=lambda x:[조건1, 조건2 …])</li>
<li>sorted는 기존 배열에 영향을 주지 않는다.</li>
</ul>
<hr>
<h4 id="백준">백준</h4>
<p>문제 - 세 개의 소수 문제</p>
<p>만약 어떤 숫자가 소수인지 알고 싶다면 제곱수를 사용한다. </p>
<ul>
<li>2부터 루트 n까지의 수 중에서 n이 나누어 떨어진다면, n은 소수가 아니다.</li>
<li>O(루트 n)</li>
</ul>
<p>만약 어떤 숫자 이하의 모든 소수를 알고싶다면 에라토스테네스의 체를 사용한다.</p>
<ul>
<li>2부터 루트 n + 1 까지 탐색하며, 자기 자신을 제외한 배수를 모두 제거한다. </li>
<li>이후 지워지지 않은 수들이 n 이하의 소수이다.</li>
<li>O(NlogN)</li>
</ul>
<ul>
<li>‘5보다 큰 홀수’ 로 풀이 시도했으나 아이디어가 틀림.</li>
<li>‘3개 소수의 합’ 으로 나타내야 하며 이는 삼중 반복문으로 구현한다.</li>
<li>세 가지 원소를 오름차순으로 정렬하려면 key = lambda x: (x[0], x[1], x[2]) 를 사용한다.</li>
<li>리스트에 *를 사용하면 원소들을 unpacking 할 수 있다.</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS 계정 생성하기 (root, IAM)]]></title>
            <link>https://velog.io/@klee_623/AWS-%EA%B3%84%EC%A0%95-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@klee_623/AWS-%EA%B3%84%EC%A0%95-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 27 Nov 2023 19:56:59 GMT</pubDate>
            <description><![CDATA[<p>[참고자료]</p>
<p><a href="https://y-joo.tistory.com/8">(배포) Django + React + Gunicorn + Nginx + EC2 배포하기</a>
<a href="https://velog.io/@misun9283/AWS-EC2-%EB%B0%B0%ED%8F%AC-%EA%B3%BC%EC%A0%95">AWS EC2 인스턴스 생성</a>
<a href="https://inpa.tistory.com/entry/AWS-%F0%9F%93%9A-%EC%95%84%EB%A7%88%EC%A1%B4-%EA%B0%80%EC%9E%85-%EC%A0%88%EC%B0%A8-%C2%A7-%EB%B3%B4%EC%95%88-%EC%84%A4%EC%A0%95-MFA-IAM-%EC%A0%95%EB%A6%AC">[AWS] 📚 아마존 가입 절차 / 보안 설정 (MFA &amp; IAM) 정리</a>
<a href="https://growth-coder.tistory.com/115">[AWS] Identity Center를 활용한 관리자 IAM 사용자 생성 (관리자 IAM 사용자를 사용하는 이유)</a></p>
<p>[+공부 필요]
AWS 간단히 이해하기 - S3와 EC2
인프라 기술, 기계학 개념 이해하기</p>
<hr>
<h2 id="배포-django--react-배포하기">배포) Django + React 배포하기</h2>
<p>캡스톤 디자인의 Django로 백엔드를, React로 프론트를 구성하여 웹을 만든 후, 배포를 진행한다.</p>
<h3 id="1-리액트-앱-빌드---django에-넣기">1. 리액트 앱 빌드 -&gt; Django에 넣기</h3>
<p>리액트 프로젝트 npm run build 로 빌드 폴더를 생성한다.
그 후 장고 프로젝트 디렉토리 안에 client 폴더를 만들어 build 폴더 안에 있는 파일들을 모두 넣어준다.</p>
<h3 id="2-build로-만들어진-indexhtml-파일을-들어가면-파일-경로를-지정해놓은-부분이-있을텐데-모두-앞에-을-붙여주도록-한다-이걸-안하면-파일-경로를-참조하지-못한다">2. build로 만들어진 index.html 파일을 들어가면 파일 경로를 지정해놓은 부분이 있을텐데 모두 앞에 .을 붙여주도록 한다. 이걸 안하면 파일 경로를 참조하지 못한다.</h3>
<p>ex) /static/... -&gt; ./static/...</p>
<h3 id="3-template-static-경로-설정-debug--false">3. template, static 경로 설정, debug = False</h3>
<p>setting.py 에 들어가 template, static 경로를 설정해준다.</p>
<pre><code>// template 경로 설정
TEMPLATES = [
    {
        &#39;BACKEND&#39;: &#39;django.template.backends.django.DjangoTemplates&#39;,
        &#39;DIRS&#39;: [&#39;client&#39;],
        &#39;APP_DIRS&#39;: True,
        &#39;OPTIONS&#39;: {
            &#39;context_processors&#39;: [
                &#39;django.template.context_processors.debug&#39;,
                &#39;django.template.context_processors.request&#39;,
                &#39;django.contrib.auth.context_processors.auth&#39;,
                &#39;django.contrib.messages.context_processors.messages&#39;,
            ],
        },
    },
]</code></pre><pre><code>static 경로 설정
STATIC_URL = &#39;/static/&#39;
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ROOT_DIR = os.path.dirname(BASE_DIR)

STATICFILES_DIRS = [
        # 실제 static 파일은 모두 client 측에서 소유
        os.path.join(ROOT_DIR, &#39;client/static&#39;)
    ]</code></pre><h3 id="4-aws-ec2-인스턴스-생성">4. AWS EC2 인스턴스 생성</h3>
<p>AWS EC2는 아마존 웹 서비스에서 제공하는 서비스로 크기 조정이 가능한 컴퓨팅 파워를 제공하는 웹 서비스이며, 몇 가지 주요 특징은 다음과 같다.</p>
<ul>
<li>원하는 만큼의 가상 서버를 구축하고 보안 및 네트워크 구성과 스토리지 관리가 가능</li>
<li>클라우드 컴퓨팅 : 사용한 만큼만 돈을 지불한다.</li>
</ul>
<h4 id="클라우드-컴퓨팅이란">클라우드 컴퓨팅이란?</h4>
<p>데이터 저장과 컴퓨팅 파워를 개인의 컴퓨터에 의존하는 대신, 원격 서버와 데이터 센터의 자원을 사용하여 데이터를 처리하고 저장하는 방식을 말한다. 
사용자가 필요에 따라 자원을 증가시키거나 감소시킬 수 있어 변동하는 작업에 유연한 대처가 가능하다.
또한 사용하는 만큼 비용을 지불하기 때문에 기업이나 개인이 비싼 하드웨어를 구매하고 유지할 필요가 없다.
인터넷이 연결 가능한 곳이라면 어디에서든 연결할 수 있다는 장점이 있고, 서비스 제공업체에서 서버를 유지 관리하므로, 소프트웨어와 보안 업데이트가 자동으로 이루어진다.</p>
<ul>
<li>AMI, EBS, EIP의 특징이 있다. (생략)</li>
</ul>
<h4 id="aws---amazon-web-services">AWS - Amazon Web Services</h4>
<ul>
<li>&quot;Amazon Web Services(AWS)는 전 세계적으로 분포한 데이터 센터에서 200개가 넘는 완벽한 기능의 서비스를 제공하는, 세계적으로 가장 포괄적이며, 널리 채택되고 있는 클라우드입니다.&quot;</li>
<li>AWS란 아마존에서 제공하는 클라우드 서비스이다.</li>
<li>아마존의 모든 서비스는 API 중심으로 설계되어 있어 모든 기능이 API로 제어가 가능하다. </li>
<li>AWS는 10년 연속 매직 쿼드런트 클라우드 인프라 및 서비스 부문의 리더로 선정되었고, 전세계적으로 가장 큰 오픈소스 생태계를 가지고 있어 많은 오픈소스 프로젝트를 참고하거나 사용할 수 있다는 장점이 있다.</li>
<li>다양한 기능, 비용 절감, 빠른 일 처리, 보안까지 확실한 장점을 가진다.</li>
</ul>
<h4 id="api---application-programming-interface">API - Application Programming Interface</h4>
<ul>
<li>응용 프로그램에서 사용할 수 있도록, 운영체제나 프로그래밍 언어가 제공하는 기능ㅇ르 제어할 수 있게 만든 인터페이스이다. </li>
</ul>
<h4 id="aws-ec2---amazon-web-services-elastic-compute-cloud">AWS EC2 - Amazon Web Services Elastic Compute Cloud</h4>
<ul>
<li>AWS 에서 제공하는 서비스 중 하나로, 사용자가 자신의 컴퓨터 앱을 실행할 가상 컴퓨터를 대여할 수 있도록 해준다. 쉽게 말해 &quot;아마존에서 클라우스를 통해 빌려주는 가상 컴퓨터 서비스&quot;이다.</li>
</ul>
<h3 id="ec2-인스턴스-생성하기">EC2 인스턴스 생성하기</h3>
<p>EC2 서비스에서 생성한 가상 서버를 인스턴스라고 한다.</p>
<hr>
<h3 id="aws-회원가입">AWS 회원가입</h3>
<h4 id="루트-사용자">루트 사용자</h4>
<ul>
<li>개인 서버 오픈 -&gt; AWS 회원가입이나 로그인 수행</li>
<li>루트 사용자 계정은 모든 권한을 갖는 SSO(Single Sign In) ID로 시작하게 된다. 이 말은 우리가 AWS의 모든 자원과 리소스를 이용할 수 있다는 이야기가 되며, 그 말은 모든 자원을 구매할 수 있다는 소리이다.</li>
<li>만약 악의적인 사용자의 공격으로 루트 사용자 계정이 탈취된다면, 악의적인 공격자는 돈을 벌기 위해 AWS의 아주 비싼 컴퓨터를 구매해서 엄청난 비트코인 채굴기를 설치해 채굴을 돌릴 것이다. 그럼 우리의 계정이 곧 비트코인 채굴용 컴퓨팅 서비스 용도로 사용되고, 천문학적인 금액의 피해가 발생할 수 있다. AWS는 공식적으로 루트 사용자 계정을 사용하지 않는 것을 추천한다.</li>
</ul>
<h4 id="iam-사용자">IAM 사용자</h4>
<p>AWS IAM - Amazon Web Service Identity and Access Management</p>
<ul>
<li>AWS 리소스에 대한 엑세스를 안전하게 제어할 수 있는 웹 서비스이다.</li>
<li>IAM을 사용하면 각각의 행동에 따른 사용자들을 나눠 하나의 인스턴스를 이용할 수 있게 된다.</li>
</ul>
<h4 id="aws-계정의-공유-엑세스">AWS 계정의 공유 엑세스</h4>
<p><img src="https://velog.velcdn.com/images/klee_623/post/969d5a14-e8fa-404f-9d56-939387b04f27/image.png" alt=""></p>
<ul>
<li>IAM 증명서를 사용하면, 루트 계정을 몰라도 서로 공유하는 인스턴스에 접근할 수 있다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/klee_623/post/a2e828ed-2d65-499b-8924-d15caefd2636/image.png" alt=""></p>
<ul>
<li>다음과 같이 접근 권한을 다르게 나누어 줄 수도 있다.</li>
</ul>
<h3 id="root-계정-보안-강화하기">Root 계정 보안 강화하기</h3>
<p>처음 aws 계정을 생성하면 루트 계정으로 생성된다. 루트 계정은 모든 서비스와 빌링 관련 업무가 가능한 계정이기 때문에 가장 중요하게 관리해야 한다.
내 aws 계정이 비트코인 채굴 계정이 되어 3억원을 태우는 경험은 하고 싶지 않다.</p>
<h4 id="보안-챌린지-설정">보안 챌린지 설정</h4>
<p><img src="https://velog.velcdn.com/images/klee_623/post/86a6d883-e169-412d-b8da-672d732be294/image.png" alt="">
내 계정 - 보안 챌린지 질문
흔한 2단계 보안설정과 같다.</p>
<h4 id="mfa-설정하기">MFA 설정하기</h4>
<p>Multi-Factor Authentication, 초단위 또는 분단위로 바뀌는 숫자</p>
<ol>
<li>검색창에 &quot;IAM&quot; 검색하기</li>
<li>&quot;MFA 추가&quot; 버튼 클릭</li>
<li>MFA 디바이스 선택 -&gt; 모바일, 컴퓨터 선택</li>
<li>구글 OTP 설치 -&gt; QR 코드 띄워서 인식</li>
<li>이제 로그인할 때 기본 ID, PW    와 OTP를 통해 로그인한다.</li>
</ol>
<h3 id="iam-서브계정-사용자-추가하기">IAM 서브계정 사용자 추가하기</h3>
<ol>
<li>루트계정 - 사용자 클릭</li>
<li>사용자 - 사용자 추가 클릭</li>
<li>그외 나머지 방법은 해당 링크 참고
<a href="https://growth-coder.tistory.com/115">https://growth-coder.tistory.com/115</a></li>
</ol>
<p><img src="https://velog.velcdn.com/images/klee_623/post/0516bd39-1d4c-4b18-8862-9d2f3f5196fd/image.png" alt=""></p>
<p>계정생성 끝. 추가 연동은 다음 글에 작성.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[M1 Mac에서 OpenGL 작동하기]]></title>
            <link>https://velog.io/@klee_623/M1-Mac%EC%97%90%EC%84%9C-OpenGl-%EC%9E%91%EB%8F%99%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@klee_623/M1-Mac%EC%97%90%EC%84%9C-OpenGl-%EC%9E%91%EB%8F%99%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 06 Oct 2023 13:00:32 GMT</pubDate>
            <description><![CDATA[<p>Xcode로 해보려다가 실패.
vscode lets try this</p>
<h2 id="opengl-setupglfw-and-glad-in-visual-studio-code-on-macos">OpenGL setup:GLFW and GLAD in Visual Studio Code on macOS</h2>
<p><a href="https://youtu.be/7-dL6a5_B3I">Video Link</a></p>
<h3 id="1-glfw-install-and-folder-setting">1. glfw install and folder setting</h3>
<ol>
<li><p>homebrew 설치</p>
<pre><code>brew install glfw</code></pre></li>
<li><p>모든 파일과 라이브러리를 관리할 폴더 생성</p>
</li>
</ol>
<ul>
<li>youtube 폴더 생성 (이름 달라도 됨)</li>
<li>하위 폴더 dependencies 생성</li>
<li>하위 폴더 include, library 생성</li>
</ul>
<ol start="3">
<li>터미널로 돌아와서 glfw 경로 복사</li>
</ol>
<ul>
<li>Finder 열고 cmd+t로 새 파인더 탭 열기</li>
<li>&quot;폴더로 이동&quot; 의 폴더 경로에 glfw 설치 경로 넣기</li>
<li>glfw 폴더만 복사해서 include 폴더 안에 붙혀넣기</li>
</ul>
<ol start="4">
<li>lib 폴더 돌아가서 libglfw.3.3.dylib 만 복사</li>
</ol>
<ul>
<li>library 폴더에 붙혀넣기</li>
</ul>
<h3 id="2-vscode-기본-세팅">2. vscode 기본 세팅</h3>
<ol>
<li>vscode에서 youtube 폴더 열기</li>
<li>main.cpp 파일 만들기</li>
<li><img src="https://velog.velcdn.com/images/klee_623/post/c12a2f85-fada-4978-93c8-d30ffb92d3ca/image.png" alt=""></li>
<li>컴파일러 선택
<img src="https://velog.velcdn.com/images/klee_623/post/bbe19995-0c5a-4a92-8efb-07433140b4d1/image.png" alt=""></li>
</ol>
<p>영상따라 선택함</p>
<ol start="5">
<li>tasks.json 코드에 &quot;args&quot;에 코드 추가  <pre><code>&quot;-std=c++17&quot; # c++ version
&quot;-Wall&quot; # ignore warnings
&quot;-I${workspaceFolder}/dependencies/include&quot; # specify path to our include files with -I
&quot;-L${workspaceFolder}/dependencies/library&quot; # specify the path to our library so with -L flag add path to our library
&quot;${workspaceFolder}/*.cpp&quot; # execute all the cpp files so write *.cpp
&quot;${workspaceFolder}/dependencies/library/libglfw.3.3.dylib&quot;
&quot;${workspaceFolder}/app&quot;
# famework 추가
&quot;-framework&quot;
&quot;OpenGL&quot;
&quot;-framework&quot;
&quot;CoCoa&quot;
&quot;-framework&quot;
&quot;IOKit&quot;
&quot;-framework&quot;
&quot;CoreVideo&quot;
&quot;-framework&quot;
&quot;CoreFoundation&quot;
# disable the warning about warning no the deprecated 
&quot;-Wno-deprecated&quot;
</code></pre></li>
</ol>
<p>```
이제 {workspaceFolder}가 우리의 프로젝트 폴더를 의미한다.
이거 다하고 main.cpp에서 command + shift + B로 빌드
./app 누르면 실행 가능하다.</p>
<h3 id="3-glad-설치">3. GLAD 설치</h3>
<p>GLAD는 필요한 함수의 위치를 검색하고 이를 함수 포인터에 저장하는 데 도움이 됩니다.</p>
<p>OpenGL은 단순히 명세서일 뿐이고, 실제 함수의 구현은 driver manufacturer에 의해 제공된다.</p>
<ol>
<li>구글 GLAD 입력</li>
<li>gl 3.3 버전 - profile은 core로 선택</li>
<li>generate GLAD and download zip files </li>
</ol>
<p>다운로드가 완료되면 
glad - include 폴더 안의 두 파일(glad, KHR)을 복사한다.
<img src="https://velog.velcdn.com/images/klee_623/post/56e4d221-9d9e-4cb7-b948-37110d05f07a/image.png" alt=""></p>
<p>복사 완료</p>
<ol start="4">
<li>glad 폴더 - src - glad.c 복사 - youtube 폴더(main directory)에 붙여넣기</li>
<li>vscode 설정 변경
&quot;${workspaceFolder}/glad.c&quot; 추가</li>
</ol>
<p>다시 cmd+shift+b로 빌드
./app으로 실행
<img src="https://velog.velcdn.com/images/klee_623/post/1c034d38-c43d-4573-9a5a-f592ea95e247/image.png" alt=""></p>
<p>예제코드가 잘 수행된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React #2]]></title>
            <link>https://velog.io/@klee_623/React-2</link>
            <guid>https://velog.io/@klee_623/React-2</guid>
            <pubDate>Wed, 04 Oct 2023 13:48:01 GMT</pubDate>
            <description><![CDATA[<p>MUI 어떻게 쓰는지 대강 알았는데
그래서 템플릿으로 웹페이지 딸깍하려면 어떻게 해야되는지 궁금해졌다.</p>
<p>그러던 중 <strong>리액트 + AI</strong>로 웹사이트 딸깍하는 방법 있지 않을까를 고민하다가 재밌어보이는 걸 발견했다.</p>
<p><a href="https://www.builder.io/blog/ai-components">builder.io</a>
<img src="https://velog.velcdn.com/images/klee_623/post/d7938cae-2d4b-42c3-8149-eae8cdfdd22b/image.png" alt="">
오호라.. 리액트와 챗지피티를 결합해
프롬프트만 입력하면 웹사이트를 만들어주는 사이트가 있는 것 같아서 
한 번 이용해보기로 했다.</p>
<p><img src="https://velog.velcdn.com/images/klee_623/post/c9df0d04-ca27-4f58-94d8-baf2fb9d95b9/image.png" alt=""></p>
<p>회원가입을 하고 나서 보인 페이지
피그마를 code로 바꿀 수도 있고
적은 코드로도 랜딩 페이지를 만들 수 있다고 한다.
오호라.. 날먹 가능한 것인가?</p>
<p><img src="https://velog.velcdn.com/images/klee_623/post/0a03b055-f4d4-4ea7-9c40-f700ed4d4568/image.png" alt="">
프롬프트로 입력해서 완성한 코드는 리액트에서도 잘 돌아간다고 적혀있다.</p>
<p><img src="https://velog.velcdn.com/images/klee_623/post/d4a2eb58-d404-4c25-89db-87d05ae0c258/image.png" alt="">
우선 하라는대로 따라가보았다.
사용하는 프레임워크 (NextJS +@)의 터미널에 다음 명령어를 입력한다.</p>
<pre><code>npm install @builder.io/react</code></pre><hr>
<p>하다가 든 생각은
builder.io는 피그마의 플러그인이라는 점.
그렇다면 피그마에서 Ai로 만들기도 충분히 가능하지 않을까 하는 생각이 들었다.</p>
<p>헷갈렸던 점
builder.io를 이용한다 vs figma에서 수행한다.
피그마에서 UI를 작업해도 리액트 문법으로 바꿔줄 수 있다.
아 그니까 builder.io도 어쨌든 피그마를 쓰는건데
AI 플러그인은 여러가지가 있는거고
그중에서 웹 사이트 프롬프트로 만들어주는게
builder.io라는 소리구나</p>
<p>찾아보니 피그마를 리액트로 만들어주는 사이트도 있었다.
그러면 AI로 피그마 스타일의 웹사이트만 만들면
리액트로도 얼마든지 바꿔줄 수 있다는 소리.</p>
<p><a href="https://www.youtube.com/watch?v=EYfm1sU53cQ">AI가 만들어주는 웹사이트</a>
위 영상이 꽤나 흥미로웠다.
웹사이트의 와이어 프레임을 뚝딱 만들어주는데
신기한 건 바로 피그마로 내보낼 수 있다는 점.</p>
<p><img src="https://velog.velcdn.com/images/klee_623/post/eaf9ae10-085d-4de4-886a-4225f6940731/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/klee_623/post/e617f8bb-9b56-464a-b126-29bf72533763/image.png" alt=""></p>
<p>처음 회원가입을 하고 build website로 넘어오면 보이는 페이지.
<a href="https://www.youtube.com/watch?v=VrBFf71ySGw&amp;t=2s">Introduction guide 영상</a></p>
<p>개요</p>
<ol>
<li><p>Sitemap을 먼저 만들어준다.
<img src="https://velog.velcdn.com/images/klee_623/post/be6ea087-eb66-4549-8bcd-cd6786ab7fbe/image.png" alt=""></p>
</li>
<li><p>최대한 많고 구체적인 정보를 Sitemap에 입력한다. 정보가 많을수록 더욱 정교한 Sitemap을 뽑아낸다.</p>
</li>
<li><p>Sitemap은 AI로 복제도 가능하고 직접 텍스트를 추가하거나 페이지를 추가하고 생성할 수도 있다. 얼마든지 수정할 수 있다.</p>
</li>
<li><p>Sitemap to WireFrame
<img src="https://velog.velcdn.com/images/klee_623/post/746a4359-5a4a-4ac2-a182-55df7836df53/image.png" alt=""></p>
</li>
</ol>
<p>사이트맵을 만들고 그 옆에 와이어 프레임 버튼을 누르면 
사이트맵을 기준으로 여러가지 와이어 프레임을 추천해준다.</p>
<p>와이어 프레임을 만들었지만 
무료계정으로는 피그마로 추출하는게 불가능하다.</p>
<p>아 좀 어지러워서 정리 다시하고 또 써야겠다.</p>
<hr>
<p>기타 서치글
<a href="https://maily.so/dailyprompt/posts/d83b725d">https://maily.so/dailyprompt/posts/d83b725d</a>
<a href="https://www.builder.io/blog/ai-figma">https://www.builder.io/blog/ai-figma</a>
Figma to React
<a href="https://producthunt.com/">https://producthunt.com/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React with MUI component #1]]></title>
            <link>https://velog.io/@klee_623/React</link>
            <guid>https://velog.io/@klee_623/React</guid>
            <pubDate>Wed, 04 Oct 2023 10:01:24 GMT</pubDate>
            <description><![CDATA[<p>급하게 리액트로 프론트 UI 만들기</p>
<p>하나부터 열까지 다 내 스타일대로 작업하면 좋겠지만
프론트라고는 HTML 태그, CSS 깔짝인 거 밖에 없는 내가
혼자 힘으로 웹앱 페이지를 전부 디자인하기에는
시간이 부족하다고 판단했다. </p>
<p>찾아보니 리액트도 이미 많은 사람들이 
템플릿을 만들어 놓은 것이 있어서
마음에 드는 템플릿을 활용해 
단시간에 있어보이는 웹페이지를 만들어보기로 했다.</p>
<p>지금이야 급해서 기본기를 안 다지고 바로 넘어가지만
시간 날 때마다 기본기 공사를 틈틈이 해둬야 한다.</p>
<hr>
<h3 id="템플릿과-라이브러리-차이">템플릿과 라이브러리 차이</h3>
<p>템플릿은 시작점이나 기본 구조를 제공하는 코드 묶음
라이브러리는 리액트와 함께 사용할 수 있는 도구나 기능 집합을 제공하는 코드 묶음</p>
<p>템플릿이고 나발이고 아예 사용법을 모르니 유튜브에서 사용방법을 찾아봤다. 우선 MUI 강좌로 컴포넌트 사용법부터 맛보기로 했다.
<a href="https://youtu.be/or3np70c7zU">생활코딩 MUI 강좌</a></p>
<p>개요</p>
<ol>
<li>Component를 여러가지 만든다.</li>
<li>Layout으로 Component를 정리한다.</li>
<li>Component간의 Interaction을 정리한다.</li>
</ol>
<h3 id="js-part">JS part</h3>
<p>모르는 JS 문법 정리</p>
<p>header
nav - 다른 부분과 이어지는 링크를 게시할 때 사용하는 태그, 일반적으로 메뉴, 목차, 인덱스 등이 있다.
ol - 순서가 있는 목록을 나열할 때 사용하는 태그
article - 문서, 사이트와 완전히 독립적으로 구성할 수 있는 요소를 정의할 때 사용한다.
br - 줄 바꿈 태그</p>
<hr>
<h3 id="mui-설치">MUI 설치</h3>
<p>리액트 샘플 코드를 만들고
MUI에서 컴포넌트를 가져와 보자.</p>
<pre><code>npm install @mui/material @emotion/react @emotion/styled</code></pre><p>경고 문구가 이것저것 나오길래 뭘 더 설치해야 하나 싶었지만
  버전확인 코드로 정상적으로 설치된 것을 확인했다.</p>
<pre><code>버전 확인 코드
npm list @mui/material</code></pre><p><img src="https://velog.velcdn.com/images/klee_623/post/041d663b-a701-4f8b-b4b1-e6e368a57937/image.png" alt=""></p>
<h3 id="컴포넌트-가져오기">컴포넌트 가져오기</h3>
<p>설치가 끝났다면 MUI 공식 사이트로 가서 Component 탭을 확인해보자.
사용할 수 있는 컴포넌트가 잔뜩 있다.
우선 버튼 컴포넌트를 사용해본다.</p>
<p><img src="https://velog.velcdn.com/images/klee_623/post/50e55eed-af19-43b3-822f-17e1ab6d5406/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/klee_623/post/e45ae8a8-61e2-41a4-bc43-7c3d0f4813ec/image.png" alt=""></p>
<p>버튼 항목에 들어가보면 버튼 이미지의 샘플 이미지와 간략한 코드를 제공한다.
풀 코드로 오픈해서 확인할 수도 있다.
따로 개발환경 없이도 stackblitz에서 어떻게 실행되는지도 확인해볼 수 있다.</p>
<p>사용하려는 컴포넌트를 import하면 자동으로 버튼에 적용되는 걸 확인할 수 있다.</p>
<hr>
<h3 id="fix">fix</h3>
<p>갑자기 localhost와 연결이 끊어져서 웹 로딩이 안된다.</p>
<p>react script 버전이 낮아서 같은 에러 발생했다는 블로그 참고해서 fix해봤다.
[[<a href="https://naraewool.tistory.com/350">https://naraewool.tistory.com/350</a></p>
<p><img src="https://velog.velcdn.com/images/klee_623/post/8680411e-2941-46a4-aa44-fed176ec7498/image.png" alt=""></p>
<pre><code>재설치 코드
npm uninstall react-scripts

npm install react-scripts</code></pre><pre><code>npm install --save-dev @jridgewell/sourcemap-codec</code></pre><p>이거 하니까 다시 로딩 잘된다!</p>
<hr>
<p>Button 페이지를 제일 마지막까지 내려보면
<img src="https://velog.velcdn.com/images/klee_623/post/d5ba4df8-6671-4482-aeba-2af93988ac05/image.png" alt="">
API 링크가 나온다
여기 링크를 타고 들어가면
사용할 수 있는 props로 어떤 종류가 있는지 확인할 수 있다.</p>
<p>여러가지 버튼을 사용하고 싶을 땐 어떻게 하냐
ButtonGroup을 사용한다</p>
<p><img src="https://velog.velcdn.com/images/klee_623/post/785801b7-e982-42fe-8584-1911053ea933/image.png" alt=""></p>
<p>버튼이 잘 묶였다.</p>
<p>이제 더미 텍스트를 가져와서 사용해보자.</p>
<p>더미텍스트를 그냥 입력하면 글이 화면을 가득 채워 읽기 불편하다.
이렇게 화면이 클 때 가운데로 고정되게 하는 UI로는 대표적으로 네이버가 있다.
이렇게 정리하는 기능들을 모아 Layout이라고 부른다.</p>
<h2 id="layout">Layout</h2>
<p><img src="https://velog.velcdn.com/images/klee_623/post/8dab1611-1548-4823-8147-97b7adf8a107/image.png" alt="">
작게 써있는게 카테고리
Container를 써보자</p>
<h3 id="container">Container</h3>
<p>여기서
Fluid는 화면 전체를
Fixed는 화면 일부만을 컨테이너로 가둘 수 있다.</p>
<pre><code>import Container from &#39;@mui/material/Container&#39;;

&lt;Container fixed&gt;
    // 가두고 싶은 내용
&lt;/Container&gt;</code></pre><p>Container로 가두면 반응형 웹페이지를 처음 만들어낸 것이다.</p>
<h3 id="grid">Grid</h3>
<p>그리드로 감싸면
컴포넌트 비율을 수평으로 나눌 수 있다.
기본적으로 화면을 12등분하고
초과하면 자동으로 다음 행으로 넘어간다.
<img src="https://velog.velcdn.com/images/klee_623/post/f8fc150e-bcb9-4dc6-8453-4bae1c582fbc/image.png" alt="">
이런식으로 Grid container로 감싸고
Grid item으로 한 번 더 감싸서 
컴포넌트 구성 값을 12로 맞추어 사용한다.</p>
<h3 id="dialog">Dialog</h3>
<p>컴포넌트를 통해 다른 컴포넌트로 넘어가기</p>
<hr>
<p>24분짜리 강의를 직접 실습하고 정리까지 하면서 하려니까 정신 사납고 진행이 더딘 것 같다.</p>
<p>모든 MUI 컴포넌트를 자유자재로 사용하는 것도 꽤나 시간이 걸릴 것 같다.</p>
<p><a href="https://seomal.com/map/1/178">서말링크</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[코부캠 연습]]></title>
            <link>https://velog.io/@klee_623/%EC%BD%94%EB%B6%80%EC%BA%A0-%EC%97%B0%EC%8A%B5</link>
            <guid>https://velog.io/@klee_623/%EC%BD%94%EB%B6%80%EC%BA%A0-%EC%97%B0%EC%8A%B5</guid>
            <pubDate>Sat, 23 Sep 2023 08:20:20 GMT</pubDate>
            <description><![CDATA[<h3 id="24480---dfs">24480 - dfs</h3>
<p>인접리스트 꼴로 사용하는 거 까먹음
그래프 n by n 비워놓고 삽입
미방문 노드의 처리 고민</p>
<p>1트) 런타임에러
재귀함수는 런타임에러가 자주 나므로 sys.setrecursionlimit(10**8) 써줘야 함</p>
<p>dfs는 몇 번을 해도 익숙해지지가 않는구먼</p>
<hr>
<h3 id="24444---bfs">24444 - bfs</h3>
<p>bfs가 최고야</p>
<hr>
<h3 id="7562---나이트-이동">7562 - 나이트 이동</h3>
<p>현재 위치 - 다음 위치
최소 몇 번의 이동이 필요한가?
나이트 가능한 이동
dx = [1,1, 2,2, -1,-1, -2, -2]
dy = [2,-2, 1, -1, 2, -2, 1,-1]
총 8방향
q.pop 방향으로 간다. 
아이디어) 도착한 다음 좌표가 이전좌표 거리보다 작아지는 조건만 탐색
초기 00 -&gt; 70 거리제곱 49
다음 12 -&gt; 70 40
다음 -2-1 -&gt; 82 안감
거기에 방문한 체스 보드에 깊이정보까지 한 번에 queue에 저장해주기</p>
<p>풀어보고 나니 짧은 거리만 갈 필요가 없었다. 이미 이동한 다음 좌표가 같으면 깊이를 출력하고 종료하는 조건문에서 검사되기 때문에..
나이트 방향에 맞춰 이동 + 체스보드 범위 안 + not visited 로 충분했던 것이다.
또 뻘짓했다.</p>
<pre><code>from collections import deque
import math
import sys
input = sys.stdin.readline

dx = [1, 1, 2, 2, -1, -1, -2, -2]
dy = [2, -2, 1, -1, 2, -2, 1, -1]


def bfs(x, y, fx, fy):
    cnt = 0
    q = deque([])
    q.append([x, y, 0])
    v[x][y] = True
    while q:
        cx, cy, steps = q.popleft()
        if cx == fx and cy == fy:
            print(steps)
            return
        for dir in range(8):
            nx, ny = cx+dx[dir], cy+dy[dir]
            if 0 &lt;= nx &lt; l and 0 &lt;= ny &lt; l and not v[nx][ny]:
                v[nx][ny] = True
                q.append([nx, ny, steps + 1])


n = int(input())
for _ in range(n):
    l = int(input())
    ix, iy = map(int, input().split())
    fx, fy = map(int, input().split())

    v = [[False] * (l) for _ in range(l)]

    if ix == fx and iy == fy:
        print(0)
    else:
        bfs(ix, iy, fx, fy)
</code></pre><h3 id="6137---문자열-생성">6137 - 문자열 생성</h3>
<p>투포인터 쓰는 문제
투포인터는 또 뭔데</p>
<p>투포인터 연습 문제도 몇 개 풀어보자.</p>
<p>ACDBCB
아이디어 - 펠린드롬</p>
<p>1) 양 끝에서 부터 l, r 다가옴
l과 r이 다르다 = 아스코드 값 중 작은 값을 T에 삽입, 삽입한 부분을 업데이트 (l이면 +1 r이면 -1)
2) 같다 = 두 값이 달라지는 부분까지 탐색 = 달라진다 = 아스키코드 값 중 작은 값을 T에 삽입, 삽잆한 값 업데이트</p>
<p>투 포인터 구현 능력 부족 다른 문제 풀어보기</p>
<pre><code>import sys
input = sys.stdin.readline


def build_string(s):
    l, r = 0, len(s) - 1  # 출발값
    T = []

    while l &lt;= r:  # 좌우 값이 다를 때

        if s[l] != s[r]:
            if s[l] &lt; s[r]:  # 파이썬은 문자열 간의 비교도 가능하다
                T.append(s[l])
                l += 1
            else:
                T.append(s[r])
                r -= 1

        else:
            temp_l, temp_r = l, r
            while temp_l &lt; temp_r and s[temp_l] == s[temp_r]:  # 다를 때까지 찾기
                temp_l += 1
                temp_r -= 1

            if temp_l &gt;= temp_r or s[temp_l] &lt; s[temp_r]:
                T.append(s[l])
                l += 1
            else:
                T.append(s[r])
                r -= 1
    return &#39;&#39;.join(T)


n = int(input())
s = [input().strip() for _ in range(n)]
s = &#39;&#39;.join(s)
result = build_string(s)
# 출력하기 (80글자마다 줄 바꿈)
for i in range(0, len(result), 80):
    print(&#39;&#39;.join(result[i:i+80]))
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[코테 연습 - 그리디 기초]]></title>
            <link>https://velog.io/@klee_623/%EC%BD%94%ED%85%8C-%EC%97%B0%EC%8A%B5</link>
            <guid>https://velog.io/@klee_623/%EC%BD%94%ED%85%8C-%EC%97%B0%EC%8A%B5</guid>
            <pubDate>Fri, 22 Sep 2023 17:48:22 GMT</pubDate>
            <description><![CDATA[<h3 id="11047---동전-0">11047 - 동전 0</h3>
<p>그리디 기초, 매우 쉬운 문제
동전의 합으로 금액 맞추기 
가장 큰 돈의 단위부터 나눠나간다 - local optimal</p>
<pre><code>11047 동전 0 
import sys
input = sys.stdin.readline

n, k = map(int, input().split())
lst = []
for _ in range(n):
    x = int(input())
    lst.append(x)

lst.reverse()
ans = 0
for i in range(n):
    if k == 0:
        break
    if k &gt;= lst[i]:
        a = k // lst[i]  # 몫
        k = k % lst[i]  # 나머지로 갱신
        ans += a
print(ans)
</code></pre><hr>
<h3 id="1931번---회의실-배정">1931번 - 회의실 배정</h3>
<p>풀이를 봤나요? Y
아이디어 - 당장 해를 생각, 그 최적해로부터 발전
끝나는 시간으로 정렬
-&gt; 시작지점을 기준으로 정렬, 종료 시간을 기준으로 정렬.</p>
<pre><code>import sys
input = sys.stdin.readline

n = int(input())
lst = []
for _ in range(n):
    x, y = map(int, input().split())
    lst.append([x, y])

lst.sort(key=lambda x: x[0])
lst.sort(key=lambda x: x[1])

cnt = 1
end = lst[0][1]
for i in range(1, n):
    if lst[i][0] &gt;= end:
        cnt += 1
        end = lst[i][1]
print(cnt)</code></pre><hr>
<h3 id="11399---atm">11399 - ATM</h3>
<p>덧셈의 합
3 1 4 3 2
3+
3+1+
3+1+4+
3+1+4+3+
3+1+4+3+2
= 결과합이 누적합, 순서에 따라 달라진다.
그리디
인출시간 순으로 정렬하면 
가장 적은 값을 가장 많이 누적함
그리디 기초 - 쉬운문제</p>
<p>sum(arr[:i]) 생각했는데 안되서 이중 for문으로 품</p>
<pre><code>import sys
input = sys.stdin.readline

n = int(input())
arr = list(map(int, input().split()))
arr.sort()
rst = 0
# 누적합 구하기
for i in range(len(arr)):  # 5
    for j in range(0, i+1):
        rst += arr[j]
print(rst)</code></pre><hr>
<h3 id="13305---주유소">13305 - 주유소</h3>
<p>주유비용 계산</p>
<p>예제
필요한 기름양 = sum(도로길이)
처음 도시에서 모두 주유 = 5<em>6 = 30원
분할 주유 = 5</em>2L + 2<em>3L + 4</em>1L = 20원
분할 주유2 = 5<em>2L + 2</em>4L = 18원</p>
<p>입력) 각 주유소 기름값, 도로 길이
출력) 최소 비용
도로 길이가 있어서 6L를 마음대로 분배 못함.
[2, 3, 1]</p>
<p>아이디어 - 
그리디가...<strong>주유소에 도착한 순간에 가장 싼 주유소에서 가장 많이 주유하고 간다.</strong>
젤나가 맙소사 전혀 그리디하게 접근하지 못했다..</p>
<p>주유소와 조건에 집하다가 코딩 스타일에서 많이 벗어나버렸다.
뻘 풀이하는데 시간을 꽤 오래썼고
그리디한 방식으로 접근하지도 못했다.
발상을 전혀 하지 못했고 풀이는 잘 봤다고 생각한다.</p>
<pre><code>import sys
input = sys.stdin.readline

n = int(input())
dist = list(map(int, input().split()))  # [2 3 1] N개
cost = list(map(int, input().split()))  # [5 2 4 1] N-1개

c = cost[0]
rst = 0
for i in range(n-1):
    if c &gt; cost[i]:
        c = cost[i]
    rst += c*dist[i]
print(rst)
</code></pre><hr>
<h3 id="1541---잃어버린-괄호">1541 - 잃어버린 괄호</h3>
<p>55-50+40
괄호치기
&#39;-&#39; 나옴 : 다음 &#39;-&#39; 나올 때까지 파싱해서 더함
그래야 빼는 값이 최대가 됨</p>
<p>모르는 부분
5 5 - 5 0 + 4 0 - 2 0 + 3 0 - 10
입력 int형으로 분리</p>
<p>풀이봤냐? Y
&#39;-&#39;와 &#39;-&#39;사이를 다 더해서 빼준다
는 건 알겠는데 그 사이를 더하는 방법을 모르겠음</p>
<p><strong>split(&#39;-&#39;) 사용하면 입력에서 - 단위로 끊어준다.</strong>
<strong>int 정수형을 사용하면 앞자리 불필요한 0이 삭제된다.</strong>
&#39;-&#39; 기준으로 자르면 처음 숫자 이후로 다 더해서 빼면 됨.</p>
<pre><code>import sys
input = sys.stdin.readline

n = str(input())
m = n.split(&#39;-&#39;)

ans = 0

x = sum(map(int, m[0].split(&#39;+&#39;)))
if n[0] == &#39;-&#39;:
    ans -= x
else:
    ans += x

for item in m[1:]:
    x = sum(map(int, item.split(&#39;+&#39;)))
    ans -= x

print(ans)</code></pre><p>이해는되는데 왜 그리디인지는 모르겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[코부캠 연습문제]]></title>
            <link>https://velog.io/@klee_623/%EC%BD%94%EB%B6%80%EC%BA%A0-%EC%97%B0%EC%8A%B5%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@klee_623/%EC%BD%94%EB%B6%80%EC%BA%A0-%EC%97%B0%EC%8A%B5%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Tue, 19 Sep 2023 15:25:18 GMT</pubDate>
            <description><![CDATA[<h3 id="4963---섬의-개수">4963 - 섬의 개수</h3>
<p>BFS
8방향</p>
<p>visited 사용 안하는 방법 = BFS 탐색으로 들어간 섬을 0으로 바꿔준다.
BFS call 할 때마다 섬의 개수를 1개씩 늘린다 (count += 1)</p>
<pre><code>from collections import deque
import sys
input = sys.stdin.readline

dx = [0, 0, -1, 1, 1, -1, 1, -1]
dy = [1, -1, 0, 0, -1, -1, 1, 1]

ans = []

def bfs(v):
    q = deque()
    q.append(v)

    while q:
        v = q.popleft()
        for dir in range(8):
            ni, nj = v[0]+dx[dir], v[1]+dy[dir]
            if 0 &lt;= ni &lt; h and 0 &lt;= nj &lt; w and graph[ni][nj] == 1:
                graph[ni][nj] = 0
                q.append([ni, nj])

while True:
    w, h = map(int, input().split())
    count = 0
    if w == h == 0:
        break
    graph = []
    for _ in range(h):
        graph.append(list(map(int, input().split())))

    for i in range(h):
        for j in range(w):
            if graph[i][j] == 1:
                bfs([i, j])
                count += 1
    ans.append(count)

print(*ans, sep=&#39;\n&#39;)
</code></pre><hr>
<h3 id="3518---공백왕-빈칸">3518 - 공백왕 빈칸</h3>
<p><a href="https://www.acmicpc.net/problem/3518">https://www.acmicpc.net/problem/3518</a>
포기</p>
<hr>
<h3 id="17609---회문">17609 - 회문</h3>
<p>회문 0
유사회문 1 
일반문 2</p>
<p>문자열 입력 
a b b a
0 1 2 3 
짝수면 (0+3) / 2 =&gt; 1
홀수면 (0+2) / 2 =&gt; 1
탐색 기본 rst = 0
** 조건 추가 **
s u m m u u s
&quot;유사 펠린드롬&quot; =&gt; 어떤 문자를 제거해야 되는지 알아야 함.
x a b b a
a b b a x
대칭...양쪽 끝에서 하나씩 보는 거 말고
대칭을 확인하는 다른 방법
..
[펠린드롬 함수]
양쪽에서 확인하면서 온다
만약에 다르다 ex. x, a = str[0] str[4]
그러면 이거 임시로 저장하고 다음거 본다.
그 다음 두 개 중에 x나 a 중 대칭이 되는 것이 있을 것
대칭이 안되는 걸 뺀다 ex. 다음이 a, b = str[1] str[3]</p>
<p>+1 (유사 후보)
왜냐, 유사 펠린드롬은 한 문자 삭제하면 대칭이 됨
i와 -(i+1)가 달라
둘 다 자기 자신을 뺀 나머지 문자가 펠린드롬인지 검사
(자기자신을 제외하고 부분문자열을 합친다.)
ex. str[i]빼기 = str[:i] + str[i+1:]
나머지 문자가 펠린드롬이면 유사펠린드롬이고
아니면 일반문이다.</p>
<pre><code>import sys
input = sys.stdin.readline

def is_pd(arr):
    for i in range(len(arr)):
        if arr[i] != arr[-i-1]:
            return False
    return True

t = int(input())
for _ in range(t):
    rst = 0
    str = list(input().rstrip())
    check = len(str) // 2
    if is_pd(str):  # 바로 펠린드롬이면 0
        rst = 0
    else:  # 아니면 유사 혹은 일반문
        for i in range(check):
            if str[i] != str[-(i+1)]:
                outI1 = str[:i]+str[i+1:]
                outI2 = str[:-(i+1)]+str[-i:]

                if is_pd(outI1) or is_pd(outI2):  # 유사펠린드롬이야
                    rst = 1

                else:  # 안되네? = 일반문
                    rst = 2
    print(rst)
</code></pre><p>1차코드, 시간초과 문제</p>
<p>투포인터를 사용하지 않아서 틀렸다.</p>
<p>투포인터의 개념을 모른다.
공부시작. 
파이썬에서는 left, right로 리스트를 옮겨다니면 되는 것 같다.
모든 경우를 테스트하면 O(N^2)이지만
투 포인터를 사용하면 O(N)이 된다.</p>
<p>투 포인터 적용하면
left, right에서 하나씩 줄면서 탐색.
다만, 두 문자가 다를 때 로직이 다름.
이미 지나온 문자는 &quot;다르다&quot;에서 검사 안됨 = 이미 펠린드롬 대칭이다.
그러면 left빼면 left + 1: right,
right 빼면 left:right+1
까지만 검사하면 된다.</p>
<p>두 포인터가 최대로 움직여도 2N이므로 
시간복잡도는 O(N)</p>
<pre><code>import sys
input = sys.stdin.readline


def is_pd(arr, left, right):
    while left &lt; right:
        if arr[left] != arr[right]:
            return False
        left += 1
        right -= 1
    return True


t = int(input())
for _ in range(t):
    rst = 0
    str = list(input().rstrip())
    left, right = 0, len(str)-1
    while left &lt; right:
        if str[left] != str[right]:
            if is_pd(str, left+1, right) or is_pd(str, left, right-1):
                print(1)
            else:
                print(2)
            break
        left += 1
        right -= 1
    else:
        print(0)
</code></pre><hr>
<h3 id="1259---팰린드롬수">1259 - 팰린드롬수</h3>
<p>날먹하기</p>
<pre><code>import sys
input = sys.stdin.readline

def is_pd(arr, left, right):
    while left &lt; right:
        if arr[left] != arr[right]:
            return False
        left += 1
        right -= 1
    return True

while True:
    A = list(map(int, input().rstrip()))
    if A[0] == 0 and len(A) == 1:
        break
    left, right = 0, len(A)-1
    if is_pd(A, left, right):
        print(&#39;yes&#39;)
    else:
        print(&#39;no&#39;)
</code></pre><hr>
<h3 id="2609---최대공약수와-최소공배수">2609 - 최대공약수와 최소공배수</h3>
<p>10000이하의 자연수
맞긴한데 지저분함</p>
<p>내코드)</p>
<pre><code>import sys
input = sys.stdin.readline

n, m = map(int, input().split())

# 약수 찾기
s = min(n, m)
a1 = []
a2 = []
for i in range(1, s+1):
    tmp1 = n / i
    tmp2 = m / i

    if int(tmp1) == tmp1:
        a1.append(i)
        a1.append(int(tmp1))
    if int(tmp2) == tmp2:
        a2.append(i)
        a2.append(int(tmp2))

ans = []
for item in a1:
    if item in a2:
        ans.append(item)

S = max(ans)
print(S)

rst = (n*m)//S
print(rst)
</code></pre><p>다른풀이) 예전에 GCD 많이 풀었는데 다 까먹음.
최대공약수 - i 탐색을 range(min(a,b), 0, -1)로 줄여나감. 둘 다 동시에 나눠질 때 (나머지 연산이 0) 출력하고 종료
최소공배수 - for문을 최소로 돌리는 범위 : range(max(a,b), (a*b)+1), i가 a와 b로 동시에 나눠지면 출력하고 종료.</p>
<pre><code>import sys
input = sys.stdin.readline

n, m = map(int, input().split())

# 최대공약수
for i in range(min(n, m), 0, -1):
    if n % i == 0 and m % i == 0:
        print(i)
        break

# 최소공배수
for i in range(max(n, m), n*m + 1):
    if i % n == 0 and i % m == 0:
        print(i)
        break
</code></pre><p>모르는대로 마구잡이로 푼 내 코드는 정말로 지저분 그 자체다. ㅠ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백트래킹 연습 (순열, N-Queen)]]></title>
            <link>https://velog.io/@klee_623/%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9-%EC%97%B0%EC%8A%B5</link>
            <guid>https://velog.io/@klee_623/%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9-%EC%97%B0%EC%8A%B5</guid>
            <pubDate>Tue, 19 Sep 2023 09:12:32 GMT</pubDate>
            <description><![CDATA[<p><strong>DFS + 조건</strong></p>
<p>풀이봤냐: O</p>
<p>DFS:
    if (답 리스트 길이) == m: # 조건
        print(&#39; &#39;.join(map(str,s)))
           return
       for i in range(1,n+1):
        s.append(i)
        DFS()
        s.pop()
DFS()</p>
<p>해결 방향:
    재귀 DFS call -&gt; 1 - 1234 - (1234) 234 ...
    for i in range(1,n+1):
        i가 append되고
        다음 바로 dfs call
        다음 탐색을 위햇 pop 필요.
        s.pop() = 마지막 원소 제거 = 재귀 호출 이전 상태로 되돌림</p>
<hr>
<p>1 -&gt; DFS call -&gt; len(s) == m, print &amp; 종료 -&gt; pop() 하고 다시 돌아야 중복순열 선택..</p>
<hr>
<p>N과 M (4)</p>
<p>이전 방문 노드보다 작은값은 추가하지 않는다.
이 조건을 어디에 추가해야 될까.</p>
<p>내 풀이) -&gt; 출력은 됨. 비효율적 = 역시나 시간초과
길이가 다 나왔을 때 (DFS 경로탐색 후)
정렬되었는지 여부를 검사한다.
정렬된 경우만 출력한다.</p>
<p>원하는 풀이)
탐색 노드보다 탐색 직전 노드가 크면 s에 넣지 않는다.
아. dfs 탐색에서 for i in range(i, n+1)로 넣어주던걸 start부터 넣어주면 된다. start는 dfs의 인자로 제공한다.</p>
<pre><code>import sys
input = sys.stdin.readline

n, m = map(int, input().split())
s = []


def dfs(start):
    if len(s) == m:
        print(&#39; &#39;.join(map(str, s)))
        return
    for i in range(start, n+1):
        s.append(i)
        dfs(i)
        s.pop()


dfs(1)
</code></pre><hr>
<p>N-Queen</p>
<p>내 풀이)
마킹)
이차원 리스트에서 퀸을 놓는다.
퀸의 영향을 받는 모든 리스트 부분을 1로 마킹한다.
놓을 수 있는 리스트 위치에 다시 퀸을 놓는다. (재귀)
조건) 퀸 갯수가 N개를 만족하면 정답으로 1개 카운트한다.
=&gt; 이 풀이에서는 Backtracking으로 그래프를 못 되돌림.
시간초과</p>
<p>해설)
v1,v2,v3로 visited가 3방향으로 Queen 존재여부를 검사한다.
row를 한 줄씩 탐색하며 row가 N-1까지 도달하면 ans+=1, 도달하지 못하고 중간에 백트래킹하는 경우, v1,v2,v3를 다시 0으로 돌려놓는다.
dfs(row) -&gt; dfs(row+1) 순으로 탐색 진행</p>
<p>추가로 백트래킹의 조건을 달아주는 것이 아니라, dfs탐색 후 if 조건문에 도달하지 못하면 v1,v2,v3를 다시 0으로 되돌리는 것 자체가 백트래킹임.</p>
<p>3방향을 위에만 보면서 한 줄씩 내려간다는 개념을 떠올리지도 못했다.
나는 십자방향 BFS 문제만 풀어서 퀸도 검사 조건에 8방향을 모두 검사해야 한다고 생각햇다. 하지만 이렇게 가면 비효율적으로 모든 노드를 검사하게 된다.</p>
<p>한 줄에 퀸 하나를 놓고 바로 다음 줄로 넘어가버리면, 가로 줄의 불필요한 검사를 스킵할 수 있다.
퀸의 특성과 visited를 여러개 사용할 수 있다는 점. 백트래킹과 DFS를 이해하기 좋은 기본 문제라고 생각한다.</p>
<p>python 시간초과 이슈
<img src="https://velog.velcdn.com/images/klee_623/post/416dfc9d-aac5-49a1-b21b-b2206c9c6212/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 2468 - 안전영역]]></title>
            <link>https://velog.io/@klee_623/%EB%B0%B1%EC%A4%80-2468-%EC%95%88%EC%A0%84%EC%98%81%EC%97%AD</link>
            <guid>https://velog.io/@klee_623/%EB%B0%B1%EC%A4%80-2468-%EC%95%88%EC%A0%84%EC%98%81%EC%97%AD</guid>
            <pubDate>Mon, 18 Sep 2023 05:57:15 GMT</pubDate>
            <description><![CDATA[<p>장마철에 비 얼만큼 오는데?
비 양 따라서 안전영역이 달라진다.
비는 최소 높이부터 최대 높이까지로 설정
할 때마다 모든 값을 ans = []에 저장
max(ans) 
비오게 해서 잠긴 부분은 날리고
살아있는 섬만 bfs 탐색.
상하좌우 이동 될 때만 count += 1</p>
<p>ㄷㄱㅈ~
틀린 이유
visited 선언이슈</p>
<pre><code>from collections import deque
import sys
input = sys.stdin.readline

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]


def bfs(graph, i, j, visited, rain):
    q = deque([])
    q.append([i, j])
    visited[i][j] = True
    while q:
        ci, cj = q.popleft()
        for dir in range(4):
            ni, nj = ci+dx[dir], cj+dy[dir]
            if 0 &lt;= ni &lt; n and 0 &lt;= nj &lt; n and not visited[ni][nj] and graph[ni][nj] &gt; rain:
                q.append([ni, nj])
                visited[ni][nj] = True


n = int(input())
graph = [list(map(int, input().split())) for _ in range(n)]

max_rain = 0
min_rain = 101
for item in graph:
    for i in item:
        max_rain = max(max_rain, i)
        min_rain = min(min_rain, i)

ans = []
for rain in range(min_rain-1, max_rain+1):  # 0~9, min_rain - 1 : 아무 지역도 안 잠김
    count = 0
    visited = [[False]*(n) for _ in range(n)]
    for i in range(n):
        for j in range(n):
            if not visited[i][j] and graph[i][j] &gt; rain:
                bfs(graph, i, j, visited, rain)
                count += 1
    ans.append(count)
print(max(ans))
# rain이 내리면
# graph 원소를 지워야 함.
# bfs 탐색에서 rain보다 큰 값만 탐색하면 되지 않나?
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[백준 11724 - 연결 요소]]></title>
            <link>https://velog.io/@klee_623/%EB%B0%B1%EC%A4%80-11724-</link>
            <guid>https://velog.io/@klee_623/%EB%B0%B1%EC%A4%80-11724-</guid>
            <pubDate>Mon, 18 Sep 2023 03:11:21 GMT</pubDate>
            <description><![CDATA[<p>미방향 그래프 연결 요소 개수 구하기
N M 이 주어진다. 사이즈 작음</p>
<p>입력 예제로 따라가기</p>
<h3 id="연결요소">연결요소</h3>
<p>connected component</p>
<p>한 노드에 대해서 BFS 돌 때마다 1번씩 카운트</p>
<p>graph[a].append(b) 꼴로 넣는 이유는 연결리스트로 나타내기 위함.
1-2,5
2-5
3-4
4-3,6
5-1
6-4</p>
<p>풀이) 1,N+1까지 탐색한다.
1번 노드를 시작 노드로 넣고 BFS 돌리면 visited로 2,5처리.
2, 5는 이미 방문으로 탐색 ㄴㄴ
그럼 3가서 4도 처리.
그럼 6가도 4는 이미 방문 안셈 (already visited)</p>
<pre><code>from collections import deque
import sys
input = sys.stdin.readline

def bfs(i, graph, visited):
    q = deque()
    q.append(i)
    visited[i] = True
    while q:
        v = q.popleft()
        for node in graph[v]:
            # 검사조건 추가
            if not visited[node]:
                q.append(node)
                visited[node] = True

N, M = map(int, input().split())
graph = [[]*(N+1) for _ in range(N+1)]
visited = [False]*(N+1)
count = 0
for _ in range(M):
    a, b = map(int, input().split())
    graph[a].append(b)
    graph[b].append(a)

for i in range(1, N+1):
    if not visited[i]:
        bfs(i, graph, visited)
        count += 1
print(count)
</code></pre><p>bfs에 검사 조건을 어디 넣어줄 지 잘 생각해보자.
bfs로 날먹하지 말고 dfs도 연습 같이 해라.
그치만 재귀는 너무 싫은 걸.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 1679 - 숨바꼭질]]></title>
            <link>https://velog.io/@klee_623/%EC%88%A8%EB%B0%94%EA%BC%AD%EC%A7%88</link>
            <guid>https://velog.io/@klee_623/%EC%88%A8%EB%B0%94%EA%BC%AD%EC%A7%88</guid>
            <pubDate>Mon, 18 Sep 2023 00:07:40 GMT</pubDate>
            <description><![CDATA[<p>5 17</p>
<p>걷기 +- 1
순간이동 2*X
가장 빠른 시간?</p>
<p>5-10-9-18-17 5회</p>
<p>접근 방법이 안 떠오름
<strong>일단 따라가봐라</strong></p>
<p>5 
-&gt; 4 6 10
-&gt; 3 8 / 7 12 / 9 11 20 (이미 방문한 번호는 제외됨 (visited))
-&gt; 2 / 16 / 14 / 13 24 / 18 22 / 19 21 40
-&gt; 1 3 / 15 17 (탐색 종료)</p>
<p>BFS
<em>visited = [] 문제 조건 따라 20만개로 설정</em></p>
<h3 id="bfs-공략">BFS 공략</h3>
<ul>
<li><strong>Queue, Visited</strong></li>
<li><strong>초기 노드 넣기</strong></li>
<li><strong>current node = q.pop()</strong></li>
<li><strong>방향 넣기</strong></li>
<li><strong>범위조절</strong></li>
<li><strong>next node = q.append(nn) (미방문시)</strong></li>
</ul>
<pre><code>수도코드
bfs(s,e):
    [1] q생성, v생성
    [2] q.append(s) v[s] = 1
    [3] while q:
             c= q.pop(0)
            # pop했는데 답이야 출력하고 종료
            # 3방향 =&gt; [c-1, c+1, c*2]
            # 범위 내 0 &lt;= n &lt;= 200000
                q.append(next)
                visited[n] = visited[c] + 1 # 깊이 더하기</code></pre><pre><code>전체코드
import sys
from collections import deque
input = sys.stdin.readline


def bfs(s, e):
    global visited
    q = deque()
    q.append(s)
    visited[s] = 1
    while q:
        cn = q.popleft()
        if cn == e:
            return visited[cn]-1
        for n in (cn-1, cn+1, cn*2):
            if 0 &lt;= n &lt;= 200000 and visited[n] == 0:
                q.append(n)
                visited[n] = visited[cn] + 1


N, K = map(int, input().split())
visited = [0]*200001
print(bfs(N, K))
</code></pre>]]></description>
        </item>
    </channel>
</rss>