<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>im_agination.log</title>
        <link>https://velog.io/</link>
        <description>go-getter</description>
        <lastBuildDate>Thu, 08 May 2025 16:12:45 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>im_agination.log</title>
            <url>https://velog.velcdn.com/images/im_agination/profile/20769e06-c091-450c-b3c8-220bcb668ed5/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. im_agination.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/im_agination" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[JQuery]]></title>
            <link>https://velog.io/@im_agination/JQuery</link>
            <guid>https://velog.io/@im_agination/JQuery</guid>
            <pubDate>Thu, 08 May 2025 16:12:45 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>jQuery는 DOM을 더 쉽게 조작할 수 있도록 도와주는 도구</p>
</blockquote>
<h2 id="dom-이란">DOM 이란</h2>
<p>Document Object Model
HTML 문서를 트리 구조로 표현한 객체 모델
브라우저는 HTML을 파싱해서 태그마다 객체로 만들어서 내부적으로 트리 구조로 관리한다.
만약 아래와 같은 HTML이 있다.</p>
<pre><code class="language-html"> &lt;div id=&quot;box&quot;&gt;Hello&lt;/div&gt;</code></pre>
<p>브라우저는 이것을 아래의 트리구조로 이해한다.</p>
<pre><code>document
└── html
    └── body
        └── div (id=&quot;box&quot;)
            └── &quot;Hello&quot;</code></pre><p>우리가 코드로 이것을 건드릴 수 있게 해주는 것이 DOM이다.</p>
<h4 id="잉-리액트도-dom인가-뭔가-아니냐">잉 리액트도 DOM인가 뭔가 아니냐?</h4>
<p>리액트는 가상 DOM(virtual DOM)을 사용한다.
가상 DOM은 실제 DOM을 복제한 가벼운 자바스크립트 객체 트리이다.
React, Vue같은 친구들은 가상 DOM을 사용해서 최소한의 변경만 실제 DOM에 적용해 빠르게 렌더링한다.
(리액트로 예시를 들자면 JSX로 가상 DOM을 만들고 렌더링을 하면 새로운 가상 DOM 트리가 생성된다 그러고 나서 이전 가상 DOM과 새 가상 DOM을 비교해서 차이점을 찾아 필요한 부분만 실제 DOM에 반영시키는 방식으로 효율적으로 빠르게 업데이트를 한다.)</p>
<p>사실 일반 DOM은 무겁고 느리다.. 변경할 때마다 렌더링이 발생하고 브라우저가 복잡한 작업을 수행하기 때문이다.</p>
<h2 id="그럼에도-jquery-쓰는-이유">그럼에도 JQuery 쓰는 이유</h2>
<ul>
<li>다양한 브라우저에서 동작</li>
<li>쉬운 API를 통해 HTML 문서 탐색과 조작 가능</li>
<li>이벤트 처리, 애니메이션, Ajax 등을 훨씬 더 간단하게 만듦</li>
<li>함수에 별도의 이름을 붙이면 같은 기능이 필요할 때마다 해당 함수를 실행가능 (재사용성)</li>
<li>😀</li>
</ul>
<p>만약 js만 써서 DOM을 건드리려면 이렇게 길게 써야한다.</p>
<pre><code class="language-js">document.getElementById(&quot;myDiv&quot;).style.display = &quot;none&quot;;</code></pre>
<p>하지만 jQuery를 쓰면</p>
<pre><code class="language-js">$(&quot;#myDiv&quot;).hide();</code></pre>
<p>이렇게 더 간단하다.</p>
<h2 id="사용법">사용법</h2>
<p><a href="https://jquery.com/download/">다운로드 링크</a>
<img src="https://velog.velcdn.com/images/im_agination/post/689b0b27-3eea-47f3-b98a-9cf0ffe0c548/image.png" alt=""></p>
<ol>
<li>compressed를 다운받는다. 
</t> compressed는 <code>jquery-1.11.min.js</code> 이런식으로 min이 적혀있다.</li>
<li>프로젝트에 지정한다.
<img src="https://velog.velcdn.com/images/im_agination/post/bf468491-016c-405e-a3b4-b601b01e2d1d/image.png" alt="">
나는 걍 귀찮아서 libs 폴더에 넣었다.</li>
<li>HTML 파일 <head> 또는 <body> 아래 코드를 포함시킨다.<pre><code>&lt;script src = &quot;libs/jquery-3.5.1.min.js&quot;&gt;&lt;/script&gt;</code></pre>사실 인터넷을 사용가능한 환경이면 CDN 쓰면된다.
이 또한 HTML 파일 <head> 또는 <body> 아래에 포함시키면 된다.<pre><code>&lt;script src=&quot;https://code.jquery.com/jquery-3.7.1.min.js&quot;&gt;&lt;/script&gt;
</code></pre></li>
</ol>
<pre><code>
## 기본 문법
``` js
$(선택자).동작();</code></pre><h2 id="예제">예제</h2>
<p>백문이 불여일견 예제를 보고 배워보자</p>
<h4 id="1-문서-준비-완료-시-실행">(1) 문서 준비 완료 시 실행</h4>
<pre><code class="language-js">  $(document).ready(function() {
  // 문서가 준비되면 실행될 코드
});</code></pre>
<p>예... 콜백함수입니다...</p>
<h4 id="2-요소-선택해서-텍스트-바꾸기">(2) 요소 선택해서 텍스트 바꾸기</h4>
<pre><code class="language-js">$(&quot;#myDiv&quot;).text(&quot;새로운 텍스트&quot;);
$(&quot;.myClass&quot;).html(&quot;&lt;b&gt;굵은 글씨&lt;/b&gt;&quot;);</code></pre>
<h4 id="3-클릭-이벤트">(3) 클릭 이벤트</h4>
<pre><code class="language-js">$(&quot;#btn&quot;).click(function() {
  alert(&quot;버튼이 클릭되었습니다!&quot;);
});</code></pre>
<h4 id="4-스타일-변경">(4) 스타일 변경</h4>
<pre><code class="language-js">$(&quot;p&quot;).css(&quot;color&quot;, &quot;red&quot;);</code></pre>
<h4 id="5-숨기기--보이기">(5) 숨기기 / 보이기</h4>
<pre><code class="language-js">$(&quot;#box&quot;).hide();     // 숨김
$(&quot;#box&quot;).show();     // 보임
$(&quot;#box&quot;).toggle();   // 토글  </code></pre>
<h4 id="6-ajax-요청">(6) Ajax 요청</h4>
<pre><code class="language-js">$.get(&quot;data.json&quot;, function(data) {
  console.log(data);
});

$.post(&quot;submit.php&quot;, { name: &quot;홍길동&quot; }, function(response) {
  alert(&quot;서버 응답: &quot; + response);
});</code></pre>
<h4 id="7-전체-코드-예시">(7) 전체 코드 예시</h4>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;나의 첫 웹페이지&lt;/title&gt;
    &lt;script src=&quot;/libs/jquery-3.5.1.min.js&quot;&gt;&lt;/script&gt;
    &lt;script&gt;
      $(document).ready(function () {
        // 모든 .babo 클래스 요소의 텍스트 변경
        $(&quot;.babo&quot;).text(&quot;바보 아니다&quot;);

        // 첫 번째 p 태그만 텍스트 다르게 바꾸기
        $(&quot;.babo&quot;).eq(0).text(&quot;나 바보 아님&quot;);

        // 색상 바꾸기
        $(&quot;.babo&quot;).css(&quot;color&quot;, &quot;blue&quot;);

        // h1 클릭하면 숨기기
        $(&quot;#hello&quot;).click(function () {
          $(this).hide();
        });
      });
    &lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1 id=&quot;hello&quot;&gt;Hello, world!&lt;/h1&gt;
    &lt;p class=&quot;babo&quot;&gt;1&lt;/p&gt;
    &lt;p class=&quot;babo&quot;&gt;2&lt;/p&gt;
    &lt;p class=&quot;babo&quot;&gt;3&lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<h2 id="결론">결론</h2>
<p>jQuery는 프론트 꾸미기를 비교적 쉽게 해주는 자바스크립트 라이브러리랄까..?</p>
<h2 id="다음편">다음편</h2>
  <img src="https://velog.velcdn.com/images/im_agination/post/1ab85886-80e3-4891-9ee4-34e3d87c4bcf/image.png" width="500"/>

]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] 2110 - 공유기 설치(python)]]></title>
            <link>https://velog.io/@im_agination/%EB%B0%B1%EC%A4%80-2110-%EA%B3%B5%EC%9C%A0%EA%B8%B0-%EC%84%A4%EC%B9%98python</link>
            <guid>https://velog.io/@im_agination/%EB%B0%B1%EC%A4%80-2110-%EA%B3%B5%EC%9C%A0%EA%B8%B0-%EC%84%A4%EC%B9%98python</guid>
            <pubDate>Fri, 07 Mar 2025 19:45:55 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/2110">문제 링크</a></p>
<p>C개의 공유기를 N개의 집에 적당히 설치해서, 가장 인접한 두 공유기 사이의 거리를 최대로 해야한다.</p>
<p>예시를 들자면
<img src="https://velog.velcdn.com/images/im_agination/post/4e6f2862-b2d1-497f-a981-55e25d841e74/image.jpg" alt="">
1,2,4,8,9에 3개의 공유기를 설치하려면 1,4,9 혹은 1,4,8에 위치해야 공유기 사이의 거리를 최대로 할 수 있다.
처음에 바보도 아니고 이 말을 이해를 못했는데...</p>
<p>만약 공유기 사이의 최소 거리를 3이라고 지정했다.
두 공유기 사이의 거리를 가장 최대로 하고 싶다면 1부터 시작해야한다. 공유기는 <em>1개</em> 설치됐다.
2에 설치하게 된다면 공유기 사이의 거리가 1이다. 그래서 안된다.
4에 설치하게 된다면 공유기 사이의 거리가 3이다. 가능하다. 공유기는 <em>2개</em> 설치됐다.
이제 4에서 다음 공유기 사이의 거리를 구해야한다.
8에 설치하게 된다면 공유기 사이의 거리가 4가 된다. 공유기는 <em>3개</em> 설치됐다.
혹은, 9에 설치하게 된다면 공유기 사이의 거리가 5가 된다. 그렇기에 9도 가능하다.
그래서 1,4,9 혹은 1,4,8에 위치가 가능한 것이다.</p>
<br>

<p>하지만 공유기 사이의 최소 거리를 5라고 지정했다.
이 또한 가장 최대로 구하고 싶기에 1부터 시작해야한다. 공유기는 <em>1개</em> 설치됐다.
2에 설치하게 된다면 공유기 사이 거리는 1이다. 안된다.
4에 설치하면 거리가 3이다. 안된다.
8에 설치하면 거리가 7이다. 가능하다. 공유기는 <em>2개</em> 설치됐다.
이제 8에서 다음 공유기 사이의 거리를 구해야한다.
9에 설치하게 된다면 8과 9간의 공유기 사이의 거리가 겨우 1이다. 그렇기에 안된다.
공유기가 총 2개가 설치됐기 때문에 최소 거리가 5일 수 없다.</p>
<p><strong>즉, 이 문제는 공유기 간의 최소거리를 최대화 하는 것이 목표다.</strong></p>
<br>


<p>조건에서 집의 좌표를 나타내는 xi (0 ≤ xi ≤ 1,000,000,000) 라고 써져있어서 이분탐색 그 중에서도 파라매트릭 서치를 사용해야 한다는 것을 캐치했다.</p>
<p>start는 두 사이의 거리가 1인 것이 최소 값이니까 1로 지정
end는 정렬한 좌표값의 가장 큰 값과 작은 값을 뺀 것이 최대 거리이기 때문에 그렇게 지정했다.</p>
<p>그 외는 기본 틀에서 문제 해결 완료!</p>
<pre><code class="language-python">import sys

input = sys.stdin.readline

n, c = map(int,input().split())
l = []
for i in range(n):
    l.append(int(input()))

l.sort()

start = 1
end = l[-1] - l[0]
answer = 0
while start &lt;= end:
    mid = (start + end) // 2
    total = 1
    tmp = l[0]
    for i in l[1:]:
        if i - tmp &gt;= mid:
            tmp = i
            total += 1
    if total &lt; c:
        end = mid - 1
    else:
        answer = mid
        start = mid + 1

print(answer)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Jenkins 비밀번호 잊어먹었을 때]]></title>
            <link>https://velog.io/@im_agination/Jenkins-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%9E%83%EC%96%B4%EB%B2%84%EB%A0%B8%EC%9D%84-%EB%95%8C</link>
            <guid>https://velog.io/@im_agination/Jenkins-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%9E%83%EC%96%B4%EB%B2%84%EB%A0%B8%EC%9D%84-%EB%95%8C</guid>
            <pubDate>Thu, 09 Jan 2025 09:29:27 GMT</pubDate>
            <description><![CDATA[<p>세상에 마상에 방금 바꾼 Jenkins 비번 까먹음
<img src="https://velog.velcdn.com/images/im_agination/post/31389d3b-ff48-4f85-a8be-4e6cc1918355/image.gif" alt=""></p>
<p>일단 Jenkins가 올라간 환경에 접속을 하자</p>
<pre><code class="language-bash">sudo vi /var/lib/jenkins/config.xml</code></pre>
<p>useSecurity를 true에서 false로 바꿔주자
못찾겠다면
esc - <code>:/useSecurity</code> 하면 쉽게 찾을 수 있다.
<img src="https://velog.velcdn.com/images/im_agination/post/bcf7ba1e-dad6-4070-b563-a7cd1c19b14d/image.png" alt=""></p>
<pre><code class="language-bash">sudo systemctl restart jenkins</code></pre>
<p>그런 다음에 재시작을 하자.
<img src="https://velog.velcdn.com/images/im_agination/post/a66903a8-316a-4cdb-8156-08c3b6a9b16b/image.png" alt="">
로그인 없이 접속이 가능하다.</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/58bc3fc3-3ba0-4cf7-ab74-c5c014d588ce/image.png" alt="">
Jenkins 관리로 들어간다.<img src="https://velog.velcdn.com/images/im_agination/post/ddc0a84b-52e3-44b8-bf3c-ba7cac6515ed/image.png" alt="">
다양한 옵션들 중에 Security - Security로 들어간다.
<img src="https://velog.velcdn.com/images/im_agination/post/4a993119-a5c7-45ea-abe2-63399a66fb7a/image.png" alt="">
Authentication의 None 상태인 Security Realm을 Jenkins&#39; own user database로 바꾼다.
<img src="https://velog.velcdn.com/images/im_agination/post/6358b2eb-600a-44aa-a585-b30354ff00e0/image.png" alt="">
다시 Jenkins 관리로 가면 Users가 생긴 것을 볼 수 있다.
<img src="https://velog.velcdn.com/images/im_agination/post/73c75fdc-2aae-417e-95d7-68a08d26a0a5/image.png" alt="">
전에 만들어둔 admin 계정이 있는 것을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/im_agination/post/8195a35e-07cd-4241-9374-dcb76c0e230f/image.png" alt="">
admin을 클릭해서 Security로 가면 password를 바꿀 수 있다.
<img src="https://velog.velcdn.com/images/im_agination/post/47a2ad20-4ca2-452c-9273-45eb47b287b2/image.png" alt=""></p>
<pre><code class="language-bash">sudo vi /var/lib/jenkins/config.xml</code></pre>
<p>다시 jenkins config 파일을 true라고 설정하고 재시작하면 끝날 거 같지만...</p>
<p>어째서인지 로그인 없이 접속할 수 있다. 귀찮겠지만 Security에 접속해서 Authentication에 있는 Authentication에서 Logged-in users can do anything으로 바꿔주도록 하자.
<img src="https://velog.velcdn.com/images/im_agination/post/efb76d47-71c6-438c-b03c-851a7402334a/image.png" alt="">
그럼 끝!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ERROR: Ansible could not initialize the preferred locale: unsupported locale setting]]></title>
            <link>https://velog.io/@im_agination/ERROR-Ansible-could-not-initialize-the-preferred-locale-unsupported-locale-setting</link>
            <guid>https://velog.io/@im_agination/ERROR-Ansible-could-not-initialize-the-preferred-locale-unsupported-locale-setting</guid>
            <pubDate>Thu, 02 Jan 2025 08:05:00 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>환경
    rocky-linux-8</p>
</blockquote>
<p>ansible를 설치를 한 뒤 버전을 불러올 수가 없었다.
<img src="https://velog.velcdn.com/images/im_agination/post/18c1acad-8612-4314-a3d6-224a27fa1e59/image.png" alt="에휴">
이것은 Ansible이 “.UTF-8”로 끝나는 locale을 찾을 수 없음을 의미한다.
locale을 확인한 다음에 LC_ALL 변수를 UTF-8로 끝나는 locale 중 하나로 export 하면 된다.</p>
<pre><code class="language-bash">locale -a
export LC_ALL=C.UTF-8</code></pre>
<p><img src="https://velog.velcdn.com/images/im_agination/post/659ead07-637a-4a13-bd1b-f564d3666936/image.png" alt="">
다시 버전 확인하면 잘 되는 것을 볼 수 있다.</p>
<h3 id="출처">출처</h3>
<p><a href="https://fabianlee.org/2023/08/21/ansible-resolving-could-not-initialize-the-preferred-locale-unsupported-locale-setting/">https://fabianlee.org/2023/08/21/ansible-resolving-could-not-initialize-the-preferred-locale-unsupported-locale-setting/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[NCP] 웹서버 구축하기]]></title>
            <link>https://velog.io/@im_agination/NCP-%EC%9B%B9%EC%84%9C%EB%B2%84</link>
            <guid>https://velog.io/@im_agination/NCP-%EC%9B%B9%EC%84%9C%EB%B2%84</guid>
            <pubDate>Tue, 03 Dec 2024 10:35:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>환경
MacOS 15.0.1</p>
</blockquote>
<h2 id="vpc-생성">VPC 생성</h2>
<p><img src="https://velog.velcdn.com/images/im_agination/post/cc877669-772a-41a8-bb03-c6ae3b2b6205/image.png" alt="">
나는 10.0.n.n으로 ip 주소 범위를 구성하고자 한다.</p>
<h2 id="subnet-생성">subnet 생성</h2>
<h3 id="private">private</h3>
<p><img src="https://velog.velcdn.com/images/im_agination/post/0fa98ea7-59a0-478c-8051-7c5310784cb8/image.png" alt="">
<code>Internet Gateway 전용 여부</code>에 <strong>N</strong>이라고 해야한다.</p>
<h3 id="public">public</h3>
<p><img src="https://velog.velcdn.com/images/im_agination/post/77d161fc-7867-4a21-8a71-745d9009c826/image.png" alt="">
<code>Internet Gateway</code> 전용 여부에 <strong>Y</strong>이라고 해야한다.</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/92f732c5-0f68-4d4e-9c21-1f411f88d8bd/image.png" alt=""></p>
<h2 id="server">server</h2>
<h3 id="서버-이미지-선택">서버 이미지 선택</h3>
<p><img src="https://velog.velcdn.com/images/im_agination/post/f6d160d6-8feb-42d3-bb72-1d9ebe70b375/image.png" alt="">
rocky8을 사용하기로 한다.</p>
<h3 id="서버-설정">서버 설정</h3>
<p><img src="https://velog.velcdn.com/images/im_agination/post/5e601650-e1fe-4a44-a2e2-dc6b2ef9b396/image.png" alt=""></p>
<ul>
<li>vpc는 당연히 아까 생성했던 vpc에 넣는다.</li>
<li>subnet의 경우 아까 private랑 public을 생성했는데 이번에는 ssh연결을 위해 public으로 생성하고자 한다.</li>
<li>실습용으로 하고자 하면 시간 요금제 써야한다. 안그럼 요금폭탄...</li>
<li>서버는 당연히 1개</li>
<li>서버 이름은 hostnamectl 없이도 host name을 바꿀 수 있게 해준다.</li>
<li>네트워크 인터페이스도 서브넷에 맞게 설정</li>
<li>공인 ip는 여기서 안해도 vpc/public ip에서 따로 할당 가능하다.</li>
<li>스크립트 설정의 경우 server/init Script에서 생성가능하다. 설명은 따로 밑에서 나는 여기에서 따로 넣어줬다.<h3 id="스토리지-설정">스토리지 설정</h3>
그냥 디폴트인채로 다음<h3 id="인증키-설정">인증키 설정</h3>
<img src="https://velog.velcdn.com/images/im_agination/post/312f8dc8-91a6-470f-a718-08e8dbe2c62d/image.png" alt="">
인증키가 없다면 새로운 인증키 생성하면 된다.
이거 나중에 서버 root 비번 보려면 <code>인증키이름.pem</code>파일이 반드시 필요하다. 그러니 잘 저장해야한다.<h3 id="네트워크-접근-설정">네트워크 접근 설정</h3>
<img src="https://velog.velcdn.com/images/im_agination/post/0f6756fe-5d7c-463a-9781-07bd062ac3cd/image.png" alt=""></li>
</ul>
<p>이거는 AWS로 치면 <strong>보안 그룹</strong>이다.</p>
<h3 id="생성-완료">생성 완료!</h3>
<h2 id="acg">ACG</h2>
<p>서버 그룹으로의 네트워크 접근을 제어 및 관리한다.
서버 그룹을 보안 정책을 일괄 관리할 수 있고 보안 규칙을 재사용성이 높기에 편하다!
나는 디폴트 acg를 사용했는데 이번에 아파치 웹서버를 사용할 계획이라 새로운 규칙을 만들어보고자 한다.</p>
<h3 id="acg-생성">ACG 생성</h3>
<p><img src="https://velog.velcdn.com/images/im_agination/post/ce2c92f8-f6da-4f23-b149-72f6be1f3557/image.png" alt="">
생성한 뒤 수정하고 싶은 acg 수정 체크 후 클릭
ncp는 aws와 다르게 디폴트 규칙이 없다. 일단 outbound부터 수정!</p>
<h3 id="outbound">outbound</h3>
<p><img src="https://velog.velcdn.com/images/im_agination/post/1d149db9-c5f3-4e39-9897-3f0ea58a67ae/image.png" alt="">
1-65535 이렇게 하면 전체로 범위 지정 가능하다.</p>
<h3 id="inbound">inbound</h3>
<p><img src="https://velog.velcdn.com/images/im_agination/post/1aefe7e7-d2ac-4203-aa5d-76e49a29264c/image.png" alt=""></p>
<ul>
<li>80번은 http, 22번은 ssh연결을 위해 icmp는 ping을 보내기 위해 사용했다.<h3 id="server에서-수정하기">server에서 수정하기</h3>
<img src="https://velog.velcdn.com/images/im_agination/post/71ad42bb-ae9b-4878-ae85-58879d81e8ae/image.png" alt=""></li>
</ul>
<p>변경하고 싶은 server 클릭후 <code>ACG 수정</code> 클릭 한 뒤 원하는 ACG로 수정해주면 된다.</p>
<h2 id="init-script">Init Script</h2>
<p>server/Init Script
서버를 처음 생성할 때 자동으로 사용하는 초기화 스크립트를 작성할 수 있다.
<img src="https://velog.velcdn.com/images/im_agination/post/bef0c476-52f1-4fe9-9931-83cf7274b25d/image.png" alt=""></p>
<pre><code class="language-bash">#!/bin/bash
timedatectl set-timezone Asia/Seoul
sudo dnf install -y httpd
systemctl enable --now httpd
echo &quot;&lt;h1&gt;web01&lt;/h1&gt;&quot; &gt; /var/www/html/index.html</code></pre>
<p>간단한 웹서버 관련 쉘스크립트를 넣어줬다.</p>
<h2 id="터미널에서-연결">터미널에서 연결</h2>
<p>그간 윈도우로 주로 사용했고 mobaX를 써왔는데 맥은 터미널에서도 연결가능하다고 한다.
나는 iterm2에서 진행했고 원래있는 터미널에서도 당연히 가능하다.
NCP에서는 root를 디폴트로 사용한다. 마치 aws에서의 ec2-user랄까...</p>
<pre><code class="language-bash">ssh root@publicIp입력
ex) ssh root@0.0.0.0</code></pre>
<p>뭐 연결할거냐 yes or no 물어보는데 당연히 yes!
<img src="https://velog.velcdn.com/images/im_agination/post/826ea811-e134-4d7f-8e48-fe4b10773827/image.png" alt=""></p>
<p>그럼 이제 비밀번호가 뭐지...?</p>
<h3 id="서버-비밀번호-찾기">서버 비밀번호 찾기</h3>
<p><img src="https://velog.velcdn.com/images/im_agination/post/aeb0820e-e8bf-49ff-9512-85bd59ebcb11/image.png" alt="">
관리자 비밀번호 확인에서 아까 위에서 만든 <code>pem키</code>를 넣으면 된다.</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/fd290357-c11d-4ed2-ac06-539bfbc155a8/image.png" alt="">
나와있는 비밀번호 써서 연결하면 끝</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/e245f220-711f-42d4-bd74-6c8256b49e0a/image.png" alt=""></p>
<h2 id="완성">완성</h2>
<p><img src="blob:https://velog.io/c224dd35-198b-46d2-a950-114668cb4c0e" alt="업로드중.."></p>
<p>public ip 주소를 입력하고 나면 아까 쉘스크립트에 넣은대로 잘 나오는 것을 볼 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring Security] 로그인 필터 구현 중 username 제대로 못 받아오는 이슈]]></title>
            <link>https://velog.io/@im_agination/Spring-Security-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%95%84%ED%84%B0-%EA%B5%AC%ED%98%84-%EC%A4%91-username%EC%9D%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%AA%BB-%EB%B0%9B%EC%95%84%EC%98%A4%EB%8A%94-%EC%9D%B4%EC%8A%88</link>
            <guid>https://velog.io/@im_agination/Spring-Security-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%95%84%ED%84%B0-%EA%B5%AC%ED%98%84-%EC%A4%91-username%EC%9D%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%AA%BB-%EB%B0%9B%EC%95%84%EC%98%A4%EB%8A%94-%EC%9D%B4%EC%8A%88</guid>
            <pubDate>Tue, 22 Oct 2024 17:01:45 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.youtube.com/watch?v=3Ff7UHGG3t8">공부중인 영상</a>
요즘? 이 영상 참고해서 JWT로그인을 구현해보고 있다.
코드 상황은 이랬다.</p>
<ol>
<li><p>UserInfo</p>
<pre><code class="language-java">public class UserInfo {
 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 @Column(name=&quot;user_id&quot;)
 private int userId;

 private String password;
 private String nickname;

 private String role;</code></pre>
</li>
<li><p>LoginFilter.class</p>
<pre><code class="language-java"> @Override
 public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException{

     String nickname = obtainUsername(request);
     String password = obtainPassword(request);

     System.out.println(nickname);
     UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(nickname,password,null);

     return authenticationManager.authenticate(authToken);
 }</code></pre>
<p>그런데......일단 잘 받아오나 확인하려는데
<img src="https://velog.velcdn.com/images/im_agination/post/fcda67ca-f28e-4ba5-acdb-a8b3242e5fbb/image.png" alt="">
<img src="https://velog.velcdn.com/images/im_agination/post/8201f254-552d-4ba7-89b8-305a20f8580d/image.png" alt="">
<img src="https://velog.velcdn.com/images/im_agination/post/155fa69c-253f-4d53-8ffe-7a55b891789f/image.png" alt="">
외않되?</p>
</li>
</ol>
<p>스프링 시큐리티는 username, password변수 기반으로 작동해서 나처럼 다른 값을 넣으면 원하는 대로 작동이 안된다는 것이다.</p>
<p>나는 위에 Userinfo에서도 보듯이 nickname을 username이랍시고 사용하고 있었다.</p>
<p>UsernamePasswordAuthenticationFilter 에서 제공해주는 <strong>setUsernameParameter</strong> 함수를 활용하면 된다.</p>
<pre><code class="language-java">    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException{

        setUsernameParameter(&quot;nickname&quot;); // 이거 추가하면 됨!!
        String nickname = obtainUsername(request);
        String password = obtainPassword(request);

        System.out.println(nickname);
        UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(nickname,password,null);

        return authenticationManager.authenticate(authToken);
    }</code></pre>
<p><img src="https://velog.velcdn.com/images/im_agination/post/0dc51297-a5bc-4705-8291-63748637ba10/image.png" alt="">
<img src="https://velog.velcdn.com/images/im_agination/post/beab4baa-9e52-4d5b-ae3c-c407319ee4fc/image.png" alt=""></p>
<p>시큐리티는 참 많은 기능을 제공해주는데... 너무 제공해줘서 더 어렵게 느껴지는 거 같기도.</p>
<p>출처)
<a href="https://www.youtube.com/watch?v=3Ff7UHGG3t8">영상 댓글</a> 및 <a href="https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilter.html#setUsernameParameter(java.lang.String)">spring security docs</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 공원]]></title>
            <link>https://velog.io/@im_agination/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B3%B5%EC%9B%90</link>
            <guid>https://velog.io/@im_agination/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B3%B5%EC%9B%90</guid>
            <pubDate>Wed, 02 Oct 2024 05:31:58 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/340198">[PCCE 기출문제] 10번 / 공원</a></p>
<p><code>구현</code> + <code>완전탐색</code>으로 풀었다.</p>
<pre><code class="language-python">def solution(mats, park):
    answer = -1
    h = len(park)
    w = len(park[0])
    for i in range(h):
        for j in range(w):
            if park[i][j] != &quot;-1&quot;: # 애초에 깔 수 없는 곳에선 시작할 이유가 없다.
                continue
            answer = max(mats_sol(mats,park, i, j),answer)


    return answer

def mats_sol(mats, park, i, j):
    h = len(park)
    w = len(park[0])
    ans = -1
    for s in mats: # 각 매트의 사이즈별 확인
        flag = False # 밑에서 보면 알겠지만 y에서 안된다면 이 돗자리는 그냥 안되는거다.
        for x in range(s):
            for y in range(s):
                ni = x + i
                nj = y + j
                if ni &lt; 0 or ni &gt;= h or nj &lt;0 or nj &gt;= w: # 공원 밖으로 안벗어나게
                    flag = True
                    continue
                if park[ni][nj] != &quot;-1&quot;: # 누가 이미 깔아서 돗자리를 깔 수 없다.
                    flag = True
                    continue
        if flag: # 이 사이즈 돗자리 안된다.
            continue # x축 반복문 벗어나기
        ans = max(ans, s) # 만약에 이 돗자리가 된다면 이것중 가장 큰걸로 지정
    return ans</code></pre>
<p>일단 공원에서 -1이 아닌 곳에서 각 사이즈의 돗자리를 깔 수 있는지 완전탐색으로 확인했다.
그리고 이 좌표에서 각각의 돗자리를 까는 것이 가능한지 함수에 넣어서 확인했다.</p>
<p>이 문제에서 핵심은 각각의 돗자리가 가능한지 완탐할 때 사용하는 flag 사용인 거 같다.
한 좌표라도 불가능해지면 이 돗자리는 그냥 깔 수 없다.
이걸 구현하기 위해 필요한 것은 flag이다.</p>
<p>처음에 맞게 구현한 거 같았는데, 아무리 고쳐도 답이 안나와서 봤더니 scope이슈였다.
flag쓸 때는 항상 scope 생각!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1090 - 체커]]></title>
            <link>https://velog.io/@im_agination/1090-%EC%B2%B4%EC%BB%A4</link>
            <guid>https://velog.io/@im_agination/1090-%EC%B2%B4%EC%BB%A4</guid>
            <pubDate>Sat, 10 Aug 2024 20:47:06 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.inflearn.com/course/2%EC%A3%BC%EB%A7%8C%EC%97%90-%ED%86%B5%EA%B3%BC%ED%95%98%EB%8A%94-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8">풀이 참고 - 2주만에 통과하는 알고리즘 코딩테스트 (2024년)</a></p>
<p>코테에서 다 물먹은 나...
코테에서 다 떨어질 것만 같아서(맞긴하다.) 서류를 쓸 의지도 안생길 지경
심기일전해서 강의를 들을려고 하는데...</p>
<hr>
<p>코테를 보고, 준비하면서 참 완탐과 구현이 중요하다고 생각한다.
왜냐면 내가 최근 연달아 본 3개의 코테에서 모두 다 완탐 + 구현에서 뭔가 한두개씩 삐끗, 혹은 풀이를 너무 오래 붙잡고 있어서 터졌기 때문이다.</p>
<p>여하튼 강의를 들으면서 풀이를 생각하기도 어려웠고, 내가 항상 사고하면서 생각하지 못하는 부분을 후벼파는 문제라 따로 정리하고자 한다.</p>
<h2 id="문제-설명">문제 설명</h2>
<p><a href="https://www.acmicpc.net/problem/1090">https://www.acmicpc.net/problem/1090</a></p>
<p>(x,y)인 n개의 체커가 주어져 있고, 체커를 움직여서 특정 한 칸에 k개의 체커를 모이도록 해야한다. (k는 1 ~ n) 이다.</p>
<h2 id="첫번째-방식틀림">첫번째 방식(틀림)</h2>
<p>내가 처음에 생각한 방법은 x의 값들과 y의 값들의 평균을 구해가지고 각각의 x,y를 이동시킨 값을 구하는 방식이였다.</p>
<pre><code>4
3 2
3 4
2 3
4 3</code></pre><p>만약 위의 예시인 경우에는 x는 3, y도 3이 나온다.
좌표평면을 그려보면 정답이다.
나와있는 예제들도 다 잘된다.
하지만 반례가 있으니...</p>
<pre><code>4
3 2
3 4
2 101
4 101</code></pre><p>이렇게 되면 x는 3, y는 52가 나와버린다.
2개의 체커가 모이도록 하는데 고작 2만([3,2],[3,4]) 이동하면 되는데, 이렇게 되면 y가 50은 넘게 이동해야한다.</p>
<h2 id="두번째-방식틀림">두번째 방식(틀림)</h2>
<p>이렇게 생각하게 된 것은</p>
<ol>
<li>x,y좌표가 각각 1,000,000보다 작거나 같은 수이다.</li>
<li>음... x,y좌표 전부 다 완탐을 돌리진 못하겠담</li>
<li>그러면 x,y 각각의 최대값과 최소값만 돌려볼까?
라는 아이디어에서 시작했다.<pre><code class="language-python"># 메모리 초과나는 풀이
import sys
n = int(input())
xl = []
yl = []
for _ in range(n):
 x,y = map(int,input().split())
 xl.append(x)
 yl.append(y)
dis = [] # 각 좌표에 대한 거리의 차가 들어있는 변수
xn = max(xl) - min(xl) + 1
yn = max(yl) - min(yl) + 1
for i in range(min(xl),max(xl)+1):
 for j in range(min(yl), max(yl) + 1):
     dis.append([0]*n)
a = 0
for i in range(xn):
 for j in range(yn):
     for k in range(n):
         dis[a][k] = abs(xl[k] - (min(xl) + i)) + abs(yl[k] - (min(yl) + j))
     a += 1 # dis에 담기위해...
ans = [999999999] * n
</code></pre>
</li>
</ol>
<p>for k in range(n):
    for j in dis:
        jl = sorted(j)
        if ans[k] &gt; sum(jl[:k+1]):
            ans[k] = sum(jl[:k+1])
print(*ans)</p>
<pre><code>이 코드는 전형적인 나의 고질병인 무식한 완탐 + 엄청 긴 리스트 남발형 풀이인데 메모리 초과가 떴다.

```python
# 시간초과
import math
n = int(input())
xl = []
yl = []
for _ in range(n):
    x, y = map(int, input().split())
    xl.append(x)
    yl.append(y)

ans = [math.inf] * n

for i in range(min(xl), max(xl) + 1):
    for j in range(min(yl), max(yl) + 1):
        current_distances = []
        for k in range(n):
            distance = abs(xl[k] - i) + abs(yl[k] - j)
            current_distances.append(distance)
        current_distances.sort()
        for k in range(n):
            ans[k] = min(ans[k], sum(current_distances[:k + 1]))

print(*ans)</code></pre><p>역시나... 시간초과가 뜬다.
생각해보면 최악의 경우가          x,y    각각 100000개씩 가능하니 당연히 시간초과가...ㅠ</p>
<h2 id="세번째-방식맞음">세번째 방식(맞음)</h2>
<p>강의를 들으며 계속 궁금한 풀이가 있었는데 &#39;우리의 집 중에 한 곳만 구하면 된다&#39; 이거 였다.
아까는 x와 y의 최소, 최대 사이의 값을 다 구해보는 것이였는데, 이제는 아예 x,y의 좌표가 있는 값들에 대한 최소값만 확인하겠다는 거다.
각 좌표에 대한 값들만 확인해도 각 좌표에 대한 거리 비용만큼은 최소값이 나온다.</p>
<p>1차원으로 생각해보면
<img src="https://velog.velcdn.com/images/im_agination/post/dcc8a953-a320-429a-be3b-3414b996ee7a/image.png" alt="">
중간에 9를 넣으나 a에서 시작하나 b에서 시작하나 6인 것은 매한가지이다.
이걸 2차원으로 확장시켜 생각해보면 역시나... 그냥 그 좌표에서 시작해도 된다는 것을 알 수 있다. 이거 뭐 말로 표현을 못하겠다..ㅠ 좌표평면을 그려봐야 그나마 이해가 된다.</p>
<pre><code class="language-python">import math
n = int(input())
xl = []
yl = []
arr = []
ans = [math.inf]*n
for _ in range(n):
    x,y = map(int,input().split())
    arr.append([x,y])
    xl.append(x)
    yl.append(y)
l = []
for x in xl:
    for y in yl:
        dis = []
        for nx, ny in arr:
            dis.append(abs(nx - x) + abs(ny-y))
        dis.sort()

        for i in range(len(dis)):
            d = sum(dis[:i+1])
            if ans[i] &gt; d:
                ans[i] = d
print(*ans)</code></pre>
<p>각각의 거리에 대해 연산을 다 하고 나서 또 다시 최소 값을 구하는 것이 아니라
각각의 거리에 대해 연산을 하면서 최소값을 구할 수 있다.</p>
<p>참고로 문제를 풀다보면 <em>각 좌표에 대한 최소값만</em> 구하면 되는거 아닌가 싶을 수 있는데 바로 밑의 예시를 보면 아니라는 것을 알게된다. 3,3가 적합한데도 좌표끼리만 최소 값을 비교하기 때문이다.
<img src="https://velog.velcdn.com/images/im_agination/post/240e3975-3938-4c99-a4d1-68cff17f623a/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JWT(Json Web Token)]]></title>
            <link>https://velog.io/@im_agination/JWTJson-Web-Token</link>
            <guid>https://velog.io/@im_agination/JWTJson-Web-Token</guid>
            <pubDate>Mon, 17 Jun 2024 13:38:58 GMT</pubDate>
            <description><![CDATA[<h1 id="세션-기반-인증-vs-토큰-기반-인증">세션 기반 인증 vs 토큰 기반 인증</h1>
<h2 id="세션-기반-인증">세션 기반 인증</h2>
<p>서버의 세션 기능과 클라이언트의 쿠키를 활용하여 사용자 인증을 하는 방법이다.
주로 서버 사이드 랜더링 방식으로 처리되는 웹서비스에서 많이 사용된다.</p>
<p>그렇다 보니 클라이언트로부터 요청을 받으면 <strong>클라이언트 상태를 계속 유지해놓고 사용한다. (Stateful)</strong>
이는 사용자가 증가함에 따라 메모리의 성능 문제를 일으킬 수 있고 -&gt; Redis로 메모리 관리 가능
쿠키는 단일 도메인, 서브 도메인에서만 작동하여 여러 도메인에서 사용한다면 따로 관리를 해야한다.</p>
<h2 id="토큰-기반-인증">토큰 기반 인증</h2>
<p>Rest 아키텍처에서 많이 사용된다.
JWT가 있다.
사용자가 로그인을 요청하면 그 사용자가 맞는지 확인하고 토큰을 준다.
서버는 따로 토큰을 가지고 있지 않는다.
<strong>즉, 상태를 유지 하지 않는다.(Stateless)</strong></p>
<h1 id="jwt">JWT</h1>
<p><code>JSON Web Token</code>
JSON 객체를 사용하여 정보를 안전하게 전송하기 위한 개방형 표준이라고 한다.</p>
<h2 id="jwt를-언제-사용할까">JWT를 언제 사용할까?</h2>
<ol>
<li>인증(Authentication)
사용자가 로그인할 때 서버에서 생성하여 클라이언트에 전달
클라이언트는 이후의 요청에 JWT를 포함시켜 서버에 인증한다.
서버는 JWT를 검증하여 사용자를 인증<blockquote>
</blockquote>
</li>
</ol>
<p><strong>인증 (Authentication)</strong>
인증은 사용자가 자신이 누구인지 확인하는 과정입니다. 즉, 사용자가 주장하는 신원이 실제로 그 사람인지 확인하는 절차
<strong>인가 (Authorization)</strong>
인가(권한 부여)는 인증된 사용자가 특정 자원이나 기능에 접근할 수 있는 권한을 가지고 있는지 확인하는 과정. 즉, 사용자가 무엇을 할 수 있는지 결정하는 절차.</p>
<ol start="2">
<li>정보 교환(Information Exchange)
JWT는 서명이 포함되어 있어 정보의 무결성을 보장
그렇기에 양 측으로 정보를 교환하는데 사용할 수 있다.</li>
</ol>
<h2 id="jwt의-구성-요소">JWT의 구성 요소</h2>
<p>JWT는 세 부분으로 구성되어 있다.
각 부분은 점(.)으로 구분된다.</p>
<ol>
<li>헤더(Header): 토큰의 유형과 해싱 알고리즘을 지정한다.</li>
<li>페이로드(Payload): 클레임(claim)을 포함하여 실제 정보가 들어 있는 부분이다.</li>
<li>서명(Signature): 토큰의 무결성을 검증하기 위해 사용한다.</li>
</ol>
<p>예시</p>
<pre><code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJhZG1pbiI6dHJ1ZX0
.
F-cvL2RcfQhUtCavIM7q7zYE8drmj2LJk0JRkrS6He4</code></pre><p><a href="https://jwt.io/#debugger-io">jwt디버거</a> 여기서 위의 jwt를 돌려보면 밑에서 설명하는 값이 나온다.</p>
<h2 id="각-구성-요소의-설명">각 구성 요소의 설명</h2>
<ol>
<li>헤더(Header)<pre><code>{
&quot;alg&quot;: &quot;HS256&quot;,
&quot;typ&quot;: &quot;JWT&quot;
}</code></pre></li>
</ol>
<ul>
<li>alg: 사용할 서명(비밀 키) 알고리즘 (예: SHA256)</li>
<li>typ: 토큰의 타입(JWT)</li>
</ul>
<ol start="2">
<li>페이로드(Payload)<pre><code>{
&quot;sub&quot;: &quot;1234567890&quot;,
&quot;name&quot;: &quot;John Doe&quot;,
&quot;iat&quot;: 1516239022,
&quot;admin&quot;: true
}</code></pre></li>
</ol>
<ul>
<li>sub: 토큰의 주체(Subject)</li>
<li>name: 사용자 이름</li>
<li>iat: 토큰 발행 시간(Issued At)</li>
</ul>
<blockquote>
<p>페이로드는 여러 개의 클레임을 포함할 수 있다.</p>
</blockquote>
<blockquote>
<p>클레임은 <a href="https://datatracker.ietf.org/doc/html/rfc7519#section-4.1">등록된 클레임(Registered Claims)</a>, 공개 클레임(Public Claims), 비공개 클레임(Private Claims)으로 나눌 수 있다.</p>
</blockquote>
<ol start="3">
<li>서명(Signature)<pre><code>HMACSHA256(
base64UrlEncode(header) + &quot;.&quot; + base64UrlEncode(payload),
secret
)</code></pre>서명은 헤더와 페이로드를 인코딩한 후 비밀 키를 사용하여 생성한다.
토큰을 무결성을 검증하는데 사용</li>
</ol>
<h2 id="jwt는-어떻게-작동하는가">JWT는 어떻게 작동하는가</h2>
<p><img src="https://velog.velcdn.com/images/im_agination/post/b8a412e0-c21d-4d5d-86bf-634af4efa078/image.png" alt=""></p>
<ol>
<li><p>사용자 인증
사용자가 로그인할 때, 서버는 사용자의 자격 증명을 확인한다 (예: 아이디과 비번)</p>
</li>
<li><p>JWT생성
서버는 사용자가 인증에서 성공하면 JWT를 생성한다.
이 토큰에는 사용자 정보를 포함한 페이로드와 함께 헤더,서명이 포함된다.</p>
</li>
<li><p>JWT전달
서버는 생성된 JWT를 클라이언트에 전달한다.
클라이언트는 이 JWT를 로컬 스토리지나 쿠키에 저장한다.</p>
</li>
<li><p>요청 시 JWT첨부
클라이언트는 이후의 모든 요청에 JWT를 포함시켜 서버에 보낸다.
보통 이 토큰은 HTTP헤더의 <code>Bearer</code>스키마를 사용한<code>Authorization</code>필드에 포함된다.</p>
<pre><code>Authorization: Bearer &lt;token&gt;</code></pre></li>
<li><p>서버에서 JWT검증
서버는 클라이언트로부터 받은 JWT를 검증한다.
서명 확인, 유효성 검사, 사용자 정보확인과 같은 과정을 거친다.</p>
</li>
<li><p>요청 처리
서버가 JWT가 유효하다고 판단하면 요청한 데이터에 접근할 수 있도록 허용한다.</p>
</li>
</ol>
<h2 id="jwt-단점">JWT 단점</h2>
<ul>
<li>토큰 크기: JWT는 기본적으로 크기가 커질 수 있어, 헤더의 크기가 증가할 수 있다. -&gt; 네트워크 부하 일어날지도</li>
<li>무효화 어려움: Stateless로 서버에서 임의 삭제 불가</li>
<li>토큰 자체에 정보 저장해서 보안 취약</li>
</ul>
<h2 id="출처">출처</h2>
<p><a href="https://jwt.io/introduction">https://jwt.io/introduction</a></p>
<p><a href="https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-JWTjson-web-token-%EB%9E%80-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC">https://inpa.tistory.com/entry/WEB-📚-JWTjson-web-token-란-💯-정리</a></p>
<p><a href="https://velog.io/@jun7867/%EC%84%B8%EC%84%A0-%EA%B8%B0%EB%B0%98-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%ED%86%A0%ED%81%B0-%EA%B8%B0%EB%B0%98-%EC%9D%B8%EC%A6%9DJWT-%EC%B0%A8%EC%9D%B4%EC%A0%90">https://velog.io/@jun7867/세선-기반-인증과-토큰-기반-인증JWT-차이점</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP와 Cookie와 Session]]></title>
            <link>https://velog.io/@im_agination/CS-Cookie%EC%99%80-Session</link>
            <guid>https://velog.io/@im_agination/CS-Cookie%EC%99%80-Session</guid>
            <pubDate>Mon, 17 Jun 2024 12:10:18 GMT</pubDate>
            <description><![CDATA[<hr>
<p>요즈음 JWT로 로그인 기능을 구현하고 있다.
JWT에 대해서 쓰려고 하는데, 먼저 쿠키와 세션에 대해서 설명하고 들어가면 좋을 거 같아서 작성한다.</p>
<hr>
<h2 id="http-프로토콜">Http 프로토콜</h2>
<p><code>HyperText Transfer Protocal</code></p>
<ul>
<li>웹 브라우저와 웹 서버 간에 데이터를 주고받기 위한 프로토콜</li>
<li>클라이언트-서버 모델을 기반</li>
<li>HTML 문서, 이미지, 비디오 등 리소스를 전송하는데 사용</li>
<li>HTTP는 TCP/IP 프로토콜 위에서 작동하고 포트는 보통 80을 사용</li>
</ul>
<h3 id="http의-주요-특징">HTTP의 주요 특징</h3>
<ol>
<li>비연결성 (connectionless)
비연결성은 클라이언트가 서버와 한 번 연결을 맺은 뒤,
클라이언트의 요청에 대해서 서버가 응답하면 맺은 연결을 끊어 버린다.
= 각 요청 간에 상태를 유지하지 않는다.</li>
</ol>
<p><strong>즉, 서버는 각 요청을 독립적으로 처리한다.</strong></p>
<ul>
<li>장점: 연결을 계속 유지하지 않아서 리소스가 줄어든다.</li>
<li>단점: 동일한 클라이언트가 요청해도 매번 새로게 해야해서 오버헤드가 발생한다.</li>
<li><blockquote>
<p>KeepAlive을 사용해서 해소할 수 있지만 메모리 많이 먹는다.</p>
</blockquote>
</li>
</ul>
<ol start="2">
<li>무상태성 (stateless)
비연결성으로 인해 서버는 클라이언트에 대한 상태나 정보가 없고
그렇기 때문에 클라이언트의 상태를 모른다.</li>
</ol>
<p>클라이언트의 상태를 모르기 때문에 로그인하면 그것에 대한 인증을 매번 새로게 받아야한다.</p>
<p>이를 보완하기 위해 상태를 저장할 수 있는 쿠키와 세션을 사용하는 것이다.</p>
<h2 id="쿠키cookie">쿠키(Cookie)</h2>
<p>쿠키는 클라이언트의 브라우저에 저장되는 작은 데이터 조각이다.</p>
<h3 id="쿠키의-주요-특징">쿠키의 주요 특징</h3>
<ol>
<li><strong>클라이언트 사이드 저장</strong>
쿠키는 key-value형식으로 구성된 string으로 클라이언트의 브라우저에 저장된다.</li>
<li>데이터 저장</li>
<li>유효 기간
쿠키는 유효 기간을 설정할 수 있다.
브라우저마다 정책이 다르다. 사파리의 경우 7일(왤케 짧냐?), 크롬의 경우 변경하면 최대 400일 정도</li>
<li>보안
쿠키는 사실 보안성이 세션에 비해 약하다.
그렇기 때문에 로그인 인증과 같은 보안성이 필요한 곳에는 권장되지 않는다.
HttpOnly와 Secure속성을 사용하여 https 연결에만 쿠키가 전송되도록 보안을 강화할 수 있다고는 한다.</li>
</ol>
<h2 id="세션session">세션(Session)</h2>
<p>세션은 서버 측에서 관리되는 상태 정보다.</p>
<h3 id="세션의-주요-특징">세션의 주요 특징</h3>
<ol>
<li><strong>서버 사이드 저장</strong>
세션 정보는 서버에 저장된다.
클라이언트를 식별하기 위해서 세션 ID가 사용된다.</li>
<li>단기 저장
보안을 위해 일반적으로 특정 기간 동안(브라우저 활성) 활성화되며, 사용자가 일정 시간 동안 활동이 없으면(브라우저 종료) 만료됨</li>
<li>데이터 저장</li>
<li>보안
세션은 당연히 서버에 저장되기 때문에 쿠키에 비해 비교적 보안이 높다.
세션 ID가 털리면 그딴거 없다.
그러기 위해서 https를 사용해서 전송될 때 보안을 강화해야 한다.</li>
</ol>
<h2 id="세션과-쿠키의-차이점">세션과 쿠키의 차이점</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>쿠키 (Cookie)</th>
<th>세션 (Session)</th>
</tr>
</thead>
<tbody><tr>
<td>저장 위치</td>
<td>클라이언트 측 브라우저에 저장</td>
<td>서버 측에 저장</td>
</tr>
<tr>
<td>유효 기간</td>
<td>설정된 유효 기간 동안 유지</td>
<td>일반적으로 브라우저 세션 동안 유지, 일정 시간 동안 활동이 없으면 만료</td>
</tr>
<tr>
<td>데이터 저장</td>
<td>작은 데이터 조각, 텍스트 형식(Text)</td>
<td>다양한 형태의 데이터 저장 가능(Object)</td>
</tr>
<tr>
<td>보안</td>
<td>민감한 정보 저장 시 보안 위험 있음</td>
<td>서버 측에 저장되어 상대적으로 보안이 높음</td>
</tr>
<tr>
<td>사용 예</td>
<td>사용자 언어 설정, 자동 로그인 정보, 광고 트래킹 정보</td>
<td>로그인 상태 유지, 장바구니 정보, 사용자 프로필 정보</td>
</tr>
<tr>
<td>전송 방식</td>
<td>HTTP 요청 시 자동으로 서버에 전송</td>
<td>세션 ID를 쿠키나 URL 파라미터를 통해 전달</td>
</tr>
<tr>
<td>용량 제한</td>
<td>일반적으로 4KB 이하</td>
<td>서버 메모리에 저장, 용량 제한 없음</td>
</tr>
</tbody></table>
<h2 id="참고">참고</h2>
<p><a href="https://victorydntmd.tistory.com/286">https://victorydntmd.tistory.com/286</a>
<a href="https://hstory0208.tistory.com/entry/%EC%BF%A0%ED%82%A4-%EC%BA%90%EC%8B%9C-%EC%84%B8%EC%85%98-%EC%9D%B4%EB%9E%80-%EA%B0%81-%EA%B0%9C%EB%85%90%EB%93%A4%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90">https://hstory0208.tistory.com/entry</a>
<a href="https://hstory0208.tistory.com/entry/%EC%BF%A0%ED%82%A4-%EC%BA%90%EC%8B%9C-%EC%84%B8%EC%85%98-%EC%9D%B4%EB%9E%80-%EA%B0%81-%EA%B0%9C%EB%85%90%EB%93%A4%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90">https://hstory0208.tistory.com/entry</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[맥북 m3 MySQL 8.4.0 start가 안된다.]]></title>
            <link>https://velog.io/@im_agination/%EB%A7%A5%EB%B6%81-m3-MySQL-8.4.0%EA%B0%80-%EC%8B%A4%ED%96%89%EC%9D%B4-%EC%95%88%EB%90%9C%EB%8B%A4</link>
            <guid>https://velog.io/@im_agination/%EB%A7%A5%EB%B6%81-m3-MySQL-8.4.0%EA%B0%80-%EC%8B%A4%ED%96%89%EC%9D%B4-%EC%95%88%EB%90%9C%EB%8B%A4</guid>
            <pubDate>Sun, 16 Jun 2024 14:52:14 GMT</pubDate>
            <description><![CDATA[<h2 id="난-그저-latest를-설치하려-한건데">난 그저 latest를 설치하려 한건데...</h2>
<p>맥북 선배 면진이가 mySQL은 직접 다운받아서 설치하라고 했던가..? 그랬었다.
윈도우에서도 mySQl은 설치 한 번 잘못하면 피곤해지기 때문에 그냥 터미널 말고 직접 설치를 하기로 했다.</p>
<p>그냥 comminity server 들어가서 바로 나오는 걸로 설치했다.
<img src="https://velog.velcdn.com/images/im_agination/post/d2a7bec3-e1f4-49ea-9800-b1768c88dad1/image.png" alt=""></p>
<p>제대로 실행하고 잘 되나 했더니
<img src="https://velog.velcdn.com/images/im_agination/post/a5a34391-0178-4285-8f9f-183dad64f442/image.png" alt="">
계속 서버 실행이 안됐다.</p>
<h2 id="버전에-관한-이슈였다">버전에 관한 이슈였다.</h2>
<p><a href="https://discussions.apple.com/thread/254871250?sortBy=best">https://discussions.apple.com/thread/254871250?sortBy=best</a>
여기서 보니까 나만 그런건 아닌듯</p>
<p>8.3을 설치하라는 댓글에 8.3을 설치하니 실행은 잘됐다.</p>
<p>그런데 workbench에서 실행하니 버전이 안맞는다고 한다.</p>
<p>찾아보니까 MySQL Workbench는 주로 MySQL 서버의 특정 버전(5.6, 5.7, 8.0)에 최적화되어 있고, MySQL 8.3.0과 같은 비표준 또는 미지원 버전과의 호환성은 보장되지 않는다고 한다.</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/e3b5c9a1-7a7a-4b1c-9b08-717a3ec1f118/image.png" alt="">
그래서 그냥 8.0.36을 설치했다.
아주 잘 돌아간다.</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/dd78eae7-49eb-478a-a409-c8be62b1c37c/image.png" alt="">
workbench에서 잘 되는 것을 볼 수 있다.</p>
<h2 id="세-줄-요약">세 줄 요약</h2>
<ol>
<li>mySQL 8.4.0은 문제가 있는지 서버 실행이 안된다.</li>
<li>8.x 버전 말고 공식 릴리즈인 <strong>8.0.x 버전</strong>을 설치하도록 하자.</li>
<li>workbench는 공식 릴리즈버전에서 호환이 된다.</li>
</ol>
<h2 id="참고">참고</h2>
<p><a href="https://konkukcodekat.tistory.com/25">https://konkukcodekat.tistory.com/25</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[What did I do after buying a MacBook?]]></title>
            <link>https://velog.io/@im_agination/%EB%A7%A5%EB%B6%81%EC%9D%84-%EC%82%B0-%EB%92%A4-%EB%82%98%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%84-%ED%96%88%EB%8A%94%EA%B0%80</link>
            <guid>https://velog.io/@im_agination/%EB%A7%A5%EB%B6%81%EC%9D%84-%EC%82%B0-%EB%92%A4-%EB%82%98%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%84-%ED%96%88%EB%8A%94%EA%B0%80</guid>
            <pubDate>Wed, 12 Jun 2024 08:01:03 GMT</pubDate>
            <description><![CDATA[<p>학교에서 받은 띵크패드T470을 18년도부터 사용해 왔는데요...
성능 이슈로 인해 면접을 못볼뻔한 사건으로 드디어 새 노트북을 장만했습니다.</p>
<p>님들은 나처럼 이러지 말고 적당한 때 노트북 잘 바꾸십시오.</p>
<p>사건에 대해 말씀드리고 원하는 모델을 찾아본 끝에
맥북 프로를 부모님께서 질러주셨습니다.</p>
<p>당연히 안보시겠지만 블로그에도 감사한 마음 전합니다.</p>
<hr>
<p>사담이 매우 길었는데 나중을 위한 맥북 설정들에 대해 정리해보려고 합니다.</p>
<blockquote>
<p>사양
맥북 프로 M3pro
램 18GB</p>
</blockquote>
<h2 id="homebrew">Homebrew</h2>
<p>macOS용 패키지 관리자
맥의 apt인가보다.
<a href="https://m-ur-phy.tistory.com/entry/%EB%A7%A5%EB%B6%81-Homebrew-%ED%99%88%EB%B8%8C%EB%A5%98-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0">https://m-ur-phy.tistory.com/entry/맥북-Homebrew-홈브류-설치하기</a></p>
<h2 id="iterm2">Iterm2</h2>
<p>면진이가 꼭 설치하라구 했다.
m3프로는 그냥 막하면 또 안된다. 밑의 블로그 참고함 이게 최고임👍
<a href="https://kdohyeon.tistory.com/122">https://kdohyeon.tistory.com/122</a></p>
<h2 id="pycharm">Pycharm</h2>
<p>다들 파이썬은 vscode로 하지만 난 파이참이 좋아...
애플 실리콘 버전으로 다운!
<a href="https://www.jetbrains.com/ko-kr/pycharm/download/?section=mac">https://www.jetbrains.com/ko-kr/pycharm/download/?section=mac</a></p>
<h2 id="vscode">VSCode</h2>
<p>프론트는 이걸로 하는 편
<a href="https://code.visualstudio.com/Download">https://code.visualstudio.com/Download</a></p>
<h2 id="java">Java</h2>
<p>자바 17를 쓰는 중
<a href="https://velog.io/@may_yun/Mac-M1-Java-17-%EC%84%A4%EC%B9%98">https://velog.io/@may_yun/Mac-M1-Java-17-%EC%84%A4%EC%B9%98</a></p>
<h2 id="intellij">IntelliJ</h2>
<p>얘도 애플 실리콘으로 다운
<a href="https://www.jetbrains.com/ko-kr/idea/download/?section=mac">https://www.jetbrains.com/ko-kr/idea/download/?section=mac</a></p>
<h2 id="mysql">MySQL</h2>
<p><a href="https://velog.io/@im_agination/%EB%A7%A5%EB%B6%81-m3-MySQL-8.4.0%EA%B0%80-%EC%8B%A4%ED%96%89%EC%9D%B4-%EC%95%88%EB%90%9C%EB%8B%A4">https://velog.io/@im_agination/%EB%A7%A5%EB%B6%81-m3-MySQL-8.4.0%EA%B0%80-%EC%8B%A4%ED%96%89%EC%9D%B4-%EC%95%88%EB%90%9C%EB%8B%A4</a>
하면서 문제가 생겼어서 따로 정리했다.
+) 이유는 바로 환경변수를 설정안해서였다.
<a href="https://developer-hm.tistory.com/90">https://developer-hm.tistory.com/90</a></p>
<p><del>-&gt; 이것도 안되는 바람에... 그냥 homebrew로 설치함
<a href="https://programmerjoon.tistory.com/23">https://programmerjoon.tistory.com/23</a>
<a href="https://dschloe.github.io/sql/2024/04/mysql_delete_reinstall/">https://dschloe.github.io/sql/2024/04/mysql_delete_reinstall/</a></del>
이건 완전 삭제법
<a href="https://github.com/rangyu/TIL/blob/master/mysql/MySQL-%EC%99%84%EC%A0%84-%EC%82%AD%EC%A0%9C%ED%95%98%EA%B3%A0-%EC%9E%AC%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0-(MacOS).md">https://github.com/rangyu/TIL/blob/master/mysql/MySQL-%EC%99%84%EC%A0%84-%EC%82%AD%EC%A0%9C%ED%95%98%EA%B3%A0-%EC%9E%AC%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0-(MacOS).md</a></p>
<p>저에게 큰 도움 주신 블로거분들께도 감사의 인사를 전하며</p>
<p>여기서 더 뭐 해야지?</p>
<p>C, 도커, SQL은 차후 사용할 때 적도록...!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[FE] HTML input 태그 꾸미기]]></title>
            <link>https://velog.io/@im_agination/FE-HTML-input-%ED%83%9C%EA%B7%B8-%EA%BE%B8%EB%AF%B8%EA%B8%B0</link>
            <guid>https://velog.io/@im_agination/FE-HTML-input-%ED%83%9C%EA%B7%B8-%EA%BE%B8%EB%AF%B8%EA%B8%B0</guid>
            <pubDate>Sat, 18 May 2024 20:57:15 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
<p>환경
React
JavaScript
Shadcn/ui -&gt; 없어도됨
Tailwind -&gt; 이것도 없어도됨</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/im_agination/post/92ccf81b-8d13-40a6-92ab-876a753541a2/image.png" alt=""></p>
<pre><code class="language-html">&lt;input multiple type=&quot;file&quot; accept=&quot;image/png, image/jpeg&quot;/&gt;</code></pre>
<p>보통 html 태그를 활용한 파일 업로드의 경우 이렇게 되어있다.
이건 브라우저마다 다르다는데 파일 업로드 폼에 대해서는 별도로 수정을 할 수 없다고 한다.</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/54b1accc-0d13-4f44-964b-da0e65b41c1a/image.png" alt="">
브라우저별로 각기 다르다.</p>
<p>내가 원한 것은:</p>
<ol>
<li>파일 5개 이하로 업로드 가능할 것</li>
<li>업로드 할 파일이 없는 경우와 있는 경우를 나누어 보여줄 것</li>
<li>있는 경우에 파일의 개수를 보여줄 것</li>
<li>업로드 한 파일명을 볼 수 있고, 그 파일을 개별로 지울 수 있을 것</li>
</ol>
<pre><code class="language-JavaScript">function CreatePostModal() {
  const [files, setFiles] = useState([]);
  const handleFileChange = (e) =&gt; {
    const selectedFiles = Array.from(e.target.files);
    if (selectedFiles.length + files.length &gt; 5) {
      // 기존 파일과 새 파일의 합이 5개를 초과하면 경고
      alert(&quot;최대 5개의 파일만 업로드할 수 있습니다.&quot;);
      return;
    }
    setFiles((prevFiles) =&gt; [...prevFiles, ...selectedFiles]); // 기존 파일과 새 파일을 합칩니다.
  };
  const handleRemoveFile = (index) =&gt; {
    // 특정 인덱스의 파일을 제거합니다.
    setFiles((prevFiles) =&gt; prevFiles.filter((_, i) =&gt; i !== index));
  };
// 생략
return(

              &lt;div&gt;
                &lt;h4 className=&quot;scroll-m-20 text-xl font-semibold tracking-tight&quot;&gt;
                  File
                &lt;/h4&gt;
                &lt;div
                  onClick={handleFileUploadClick}
                  style={{
                    border: &quot;1px solid #e8e8e8&quot;, // 네모칸의 스타일을 지정합니다.
                    padding: &quot;10px&quot;, // 내부 여백을 추가합니다.
                    cursor: &quot;pointer&quot;, // 마우스 커서를 포인터로 변경합니다.
                    borderRadius: &quot;5px&quot;,
                  }}
                &gt;
                  &lt;input
                    type=&quot;file&quot;
                    multiple // 여러 파일 선택 가능
                    onChange={handleFileChange}
                    style={{ display: &quot;none&quot; }}
                    id=&quot;fileInput&quot; // 파일 입력 필드 식별을 위한 ID 추가
                  /&gt;

                  {files.length === 0 ? (
                    &lt;p&gt;파일을 추가하려면 여기를 클릭하세요.&lt;/p&gt;
                  ) : (
                    &lt;p&gt;{files.length}개의 파일이 있습니다.&lt;/p&gt;
                  )}
                &lt;/div&gt;
                &lt;div style={{ paddingBottom: &quot;3px&quot; }} /&gt;
                &lt;ul&gt;
                  {files.map((file, index) =&gt; (
                    &lt;li key={index} style={{ paddingLeft: &quot;5px&quot; }}&gt;
                      {file.name}{&quot; &quot;}
                      &lt;button
                        type=&quot;button&quot;
                        onClick={() =&gt; handleRemoveFile(index)}
                      &gt;
                        삭제
                      &lt;/button&gt;
                    &lt;/li&gt;
                  ))}
                &lt;/ul&gt;
              &lt;/div&gt;
)</code></pre>
<p><img src="https://velog.velcdn.com/images/im_agination/post/ab7f3590-b2cd-4609-965b-d44d39363001/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 페이지네이션의 맛]]></title>
            <link>https://velog.io/@im_agination/React-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98%EC%9D%98-%EB%A7%9B</link>
            <guid>https://velog.io/@im_agination/React-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98%EC%9D%98-%EB%A7%9B</guid>
            <pubDate>Fri, 17 May 2024 00:47:41 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
<p>환경
React
JavaScript
shadcn/ui
tailwind css</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/im_agination/post/b139868a-f1f7-41ac-929d-fe1200a9abd5/image.png" alt="">
Vue가 아닌 React로 돌아왔다.</p>
<p>페이지네이션만 세 번이나 주저리거렸다.</p>
<p>왜일까하니</p>
<p>CRUD수준에서 어려운 부분을 꼽으라면</p>
<ol>
<li>수정 (프론트 구현이 비교적 어려움)</li>
<li>페이지네이션 (백엔드도 직접 구현하면 생각 좀 해야함 + 프론트 또한...)
이기 때문이다.</li>
</ol>
<h2 id="✨필요한-것">✨필요한 것</h2>
<p><a href="https://velog.io/@im_agination/FastAPI-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98-%EC%89%BD%EA%B2%8C-%ED%95%98%EA%B8%B0">나의 멋진 백엔드 코드</a></p>
<h2 id="✨내가-원했던-것">✨내가 원했던 것</h2>
<ol>
<li>5개의 페이지만 띄워줄 것</li>
<li>5페이지 이상이면 6~10페이지 띄워줄 것</li>
<li>prev누르면 -5페이지, next누르면 +5페이지</li>
</ol>
<h2 id="✨참고할-것">✨참고할 것</h2>
<p><a href="https://ui.shadcn.com/docs/components/pagination">https://ui.shadcn.com/docs/components/pagination</a></p>
<h2 id="✨설치">✨설치</h2>
<pre><code class="language-shell">npx shadcn-ui@latest add pagination</code></pre>
<h2 id="✨넘어오는-데이터">✨넘어오는 데이터</h2>
<pre><code>{
    &quot;items&quot;: [
        {
            &quot;id&quot;: 1,
            &quot;title&quot;: &quot;1&quot;,
            &quot;content&quot;: &quot;1&quot;,
            &quot;author&quot;: &quot;1&quot;,
            &quot;password&quot;: &quot;1&quot;,
            &quot;comments&quot;: [],
            &quot;date&quot;: &quot;2024-05-16 22:13:38.212614&quot;
        }
    ],
    ... 생략
    ,
    &quot;total&quot;: 17,
    &quot;page&quot;: 2,
    &quot;size&quot;: 15,
    &quot;pages&quot;: 2
}</code></pre><p>total: 총 게시글의 개수
page: 현재 페이지
pages: 총 페이지
size: 한 번에 보여줄 페이지</p>
<h2 id="✨코드">✨코드</h2>
<pre><code class="language-javascript">import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from &quot;@/components/ui/pagination&quot;;
... 생략

function post(){
    const [posts, setPosts] = useState([]);
  const [currentPage, setCurrentPage] = useState(1); // 현재 페이지 상태 추가
  const [totalPages, setTotalPages] = useState(0);
  const [pageRangeStart, setPageRangeStart] = useState(1);
}
  const updatePageRange = (newCurrentPage) =&gt; {
    const newStartPage = Math.floor((newCurrentPage - 1) / 5) * 5 + 1;
    if (newStartPage !== pageRangeStart) {
      setPageRangeStart(newStartPage);
    }
  };
  useEffect(() =&gt; {
    fetchPosts();
  }, []); // 컴포넌트 마운트 시 첫 페이지의 게시물을 가져옵니다.

  const fetchPosts = async (page = 1) =&gt; {
    const response = await axios.get(
      `http://localhost:8000/post/posts?page=${page}`
    );
    setPosts(response.data.items);
    setCurrentPage(page); // 페이지를 성공적으로 불러온 후 현재 페이지 상태 업데이트
    setTotalPages(response.data.pages);
    updatePageRange(page); // 페이지 업데이트 함수에 현재 페이지 전달
  };

return(
        ... 게시판 목록관련 ...
        &lt;div&gt;
        &lt;Pagination&gt;
          &lt;PaginationContent&gt;
            &lt;PaginationItem&gt;
              &lt;PaginationPrevious
                onClick={() =&gt; fetchPosts(Math.max(currentPage - 5, 1))}
                disabled={currentPage - 5 &lt; 1}
                style={{ cursor: &quot;pointer&quot; }}
              /&gt;
            &lt;/PaginationItem&gt;
            {[...Array(5)].map((_, index) =&gt; {
              const pageNumber = pageRangeStart + index;
              return pageNumber &lt;= totalPages ? (
                &lt;PaginationItem key={pageNumber}&gt;
                  &lt;PaginationLink
                    onClick={() =&gt; fetchPosts(pageNumber)}
                    style={{
                      backgroundColor:
                        pageNumber === currentPage ? &quot;#fff&quot; : &quot;#fff&quot;, // 이 부분 바꾸면 색바뀜
                      color: pageNumber === currentPage ? &quot;#fff&quot; : &quot;#fff&quot;, // 여기도
                      cursor: &quot;pointer&quot;,
                    }}
                  &gt;
                    {pageNumber}
                  &lt;/PaginationLink&gt;
                &lt;/PaginationItem&gt;
              ) : null;
            })}
            &lt;PaginationItem&gt;
              &lt;PaginationNext
                onClick={() =&gt;
                  fetchPosts(Math.min(currentPage + 5, totalPages))
                }
                disabled={currentPage + 5 &gt; totalPages}
                style={{ cursor: &quot;pointer&quot;}}
              /&gt;
            &lt;/PaginationItem&gt;
          &lt;/PaginationContent&gt;
        &lt;/Pagination&gt;
      &lt;/div&gt;
... 생략

);</code></pre>
<h2 id="✨결과">✨결과</h2>
<p><img src="https://velog.velcdn.com/images/im_agination/post/a0ee7868-da42-4912-bf89-3eed20c6b00a/image.gif" alt="">
+) 게시글 잘 넘어감..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[FastAPI] Pagination with fastapi-pagination]]></title>
            <link>https://velog.io/@im_agination/FastAPI-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98-%EC%89%BD%EA%B2%8C-%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@im_agination/FastAPI-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98-%EC%89%BD%EA%B2%8C-%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 14 May 2024 02:03:27 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
<p>환경
Python 3.11
fastapi 0.110.2
fastapi-pagination 0.12.24
MongoDB</p>
</blockquote>
<p>이전에 스프링부트에서 Pageable을 사용하여 myBatis와 함께 간단하게 페이지네이션 하는 법에 대해서 알아보았다.</p>
<p>어쩌다보니 fastAPI를 사용하게 되었는데
Django... 이제 못쓰겠음😒</p>
<p>이번에는 FastAPI에서 간단하게 사용하는 방법에 대해서 정리하려고 한다.</p>
<p>스프링부트에서보다 훨씬 쉽다.</p>
<p>이미 MVC가 구현 다 되었다는 전제하에 시작한다.</p>
<h2 id="✨설치하기">✨설치하기</h2>
<pre><code class="language-shell">pip install fastapi-pagination</code></pre>
<h2 id="✨mainpy">✨main.py</h2>
<pre><code class="language-python">from fastapi import FastAPI
from fastapi_pagination import add_pagination

app = FastAPI()
add_pagination(app)</code></pre>
<h2 id="✨controllerpy">✨controller.py</h2>
<pre><code class="language-python">from fastapi import Depends
from fastapi_pagination import Page, Params, paginate

@post_router.get(&quot;/posts&quot;, response_model=Page[Post])
async def get_posts(params: Params = Depends()):
    params.size = 5
    return paginate(list(get_post_from_db()),params)</code></pre>
<p>보내고 싶은 요소들을 list로 묶어 보내야 하고, 하나당 사이즈를 조절하고 싶으면 Param의 size에 대해서 크기를 지정해 주면 된다. 디폴트는 50이다.</p>
<p>더 알아보고 싶다면 Params 클래스와 Page 클래스에 대해 더 알아보면 될 것이다.</p>
<h2 id="✨servicepy-변경사항-없음">✨service.py (변경사항 없음)</h2>
<pre><code class="language-python">def get_post_from_db():
    posts = db.posts.find()
    return posts</code></pre>
<h2 id="✨modelspy-변경사항-없음">✨models.py (변경사항 없음)</h2>
<p> 그냥 우리가 아는 model이다👀</p>
<h2 id="✨어떻게-뜨는가">✨어떻게 뜨는가</h2>
<pre><code>http://localhost:8000/post/posts?page=2</code></pre><pre><code class="language-JSON">{
    &quot;items&quot;: [
        {
            &quot;id&quot;: 6,
            &quot;title&quot;: &quot;새 포스트 제목22&quot;,
            &quot;content&quot;: &quot;여기에 내용을322 입력하세요.&quot;,
            &quot;nickname&quot;: &quot;작성자 이름&quot;,
            &quot;password&quot;: &quot;1234&quot;,
            &quot;comments&quot;: [],
            &quot;date&quot;: &quot;2024-05-13T22:52:08.062000&quot;
        }
    ],
    &quot;total&quot;: 6,
    &quot;page&quot;: 2,
    &quot;size&quot;: 5,
    &quot;pages&quot;: 2
}
</code></pre>
<p>2 페이지에 대해서 찾아봤고, 잘 찾아진다.</p>
<h2 id="✨참고자료">✨참고자료</h2>
<p><a href="https://uriyyo-fastapi-pagination.netlify.app/">레퍼런스</a>
<a href="https://ctsictai.medium.com/fastapi-pagination-%EC%A0%81%EC%9A%A9%EA%B8%B0-d2471241c1bc">참고자료</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[IntelliJ] 프로젝트 처음 불러올 때 바보 짓 방지]]></title>
            <link>https://velog.io/@im_agination/IntelliJ-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%B2%98%EC%9D%8C-%EB%B6%88%EB%9F%AC%EC%98%AC-%EB%95%8C-%EB%B0%94%EB%B3%B4-%EC%A7%93-%EB%B0%A9%EC%A7%80</link>
            <guid>https://velog.io/@im_agination/IntelliJ-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%B2%98%EC%9D%8C-%EB%B6%88%EB%9F%AC%EC%98%AC-%EB%95%8C-%EB%B0%94%EB%B3%B4-%EC%A7%93-%EB%B0%A9%EC%A7%80</guid>
            <pubDate>Thu, 28 Mar 2024 05:44:20 GMT</pubDate>
            <description><![CDATA[<h3 id="1번-경우">1번 경우</h3>
<p>왜...왜 내 ServerApplication.java는 컵인가.. 그리고 gradle은 왜 안잡히는가..
<img src="https://velog.velcdn.com/images/im_agination/post/7a5e65c7-253d-402b-8f0b-eb93ffc92145/image.png" alt=""></p>
<p>정상상황..
<img src="https://velog.velcdn.com/images/im_agination/post/76f22ded-8b1d-462a-ac5d-e6833fd087ce/image.png" alt=""></p>
<p>1) 님이 프로젝트 여는 폴더를 잘못지정한거 보통 더 상위폴더를 지정해서 생김. 그레이들이 안잡힌다. 위치 잘 잡으면 그레이들도 자동으로 잡힘.</p>
<p>2) 폴더도 제대로 잡았는데 안되는 경우가 왕왕있음. 보통 resources안넣었을 때라던지.. 뭔가 모종의 사유가 있을 때 그러는데 그럴 때는 보통 gradle은 잡혀있다.
<img src="https://velog.velcdn.com/images/im_agination/post/6da2baa9-0e31-46c0-aacc-1986be624ef1/image.png" alt="">
이러고 나면 보통 생김.</p>
<p>그래도 안된다면 물어보던가 다시 받던가 해야하지 않을까</p>
<hr>
<h3 id="2번-경우">2번 경우</h3>
<p>항상 private 수준에서 또 localhost정도로만 작업을 하니 간과하곤 했는데 properties같은 거 public으로 했다가 git에 올라간다면...
거기에 중요한 key가 있었다면...
그게 aws와 관련된 무언가라면...</p>
<p>이번에 협업을 하면서 당연히 gitignore안에 application.properties를 넣었다.</p>
<p>그걸 보고 당황한 나... resources가 어디갔지라고 질문하다...</p>
<p>네 당연히 직접 만들어서 팀원들과 합의한 properties를 넣어야하구요?</p>
<p>밑에처럼 폴더 넣어서 하면 된다.</p>
<p>보통 gradle이 잡혔으면 잘됨.</p>
<ol>
<li>main 폴더 우클릭</li>
</ol>
<p><img src="https://velog.velcdn.com/images/im_agination/post/ff2c9a0a-d488-4965-9fa0-3afd8d1aef05/image.png" alt=""></p>
<ol start="2">
<li>디렉토리 클릭</li>
</ol>
<p><img src="https://velog.velcdn.com/images/im_agination/post/aab00107-362f-4299-9fa9-5c15a8301705/image.png" alt=""></p>
<h1 id="끝">끝</h1>
<p><img src="https://velog.velcdn.com/images/im_agination/post/0606822a-13dc-4385-a7d0-f842f2b3c8d6/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Springboot 3에서 Swagger 생성법]]></title>
            <link>https://velog.io/@im_agination/Springboot-3%EC%97%90%EC%84%9C-Swagger-%EC%83%9D%EC%84%B1%EB%B2%95</link>
            <guid>https://velog.io/@im_agination/Springboot-3%EC%97%90%EC%84%9C-Swagger-%EC%83%9D%EC%84%B1%EB%B2%95</guid>
            <pubDate>Thu, 28 Mar 2024 05:01:04 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
<p>환경
자바 17
스프링부트 3.2.3</p>
</blockquote>
<p>예전에는 spring fox를 사용했던 거 같은데 3버전으로 되면서 spring doc을 쓰는게 맞다고 한다.</p>
<h3 id="buildgradle">build.gradle</h3>
<pre><code>implementation &#39;org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0&#39;</code></pre><h3 id="applicationproperties">application.properties</h3>
<pre><code>springdoc.packages-to-scan= com.demo.project (패키지명)
springdoc.default-consumes-media-type= application/json;charset=UTF-8
springdoc.default-produces-media-type= application/json;charset=UTF-8
springdoc.swagger-ui.path= /swagger-ui.html
springdoc.swagger-ui.disable-swagger-default-url= true
springdoc.swagger-ui.display-request-duration= true
springdoc.swagger-ui.operations-sorter=alpha</code></pre><h3 id="swaggerconfigjava">SwaggerConfig.java</h3>
<pre><code>import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SwaggerConfig {


    @Bean
    public OpenAPI openAPI() {
        Info info = new Info()
                .version(&quot;v1.0.1&quot;)
                .title(&quot;Hello&quot;)
                .description(&quot;안녕하세요?&quot;);

        String jwt = &quot;JWT&quot;;
        SecurityRequirement securityRequirement = new SecurityRequirement().addList(jwt);
        Components components = new Components().addSecuritySchemes(jwt, new SecurityScheme()
                .name(jwt)
                .type(SecurityScheme.Type.HTTP)
                .scheme(&quot;bearer&quot;)
                .bearerFormat(&quot;JWT&quot;)
        );

        return new OpenAPI()
                .info(info)
                .addSecurityItem(securityRequirement)
                .components(components);
    }
}
</code></pre><p>로그인을 넣어서 accessToken을 넣어서 해야하니까 security 부분도 넣게 되었다.</p>
<h3 id="설명-작성하기">설명 작성하기</h3>
<pre><code>@Tag(name =&quot;Itemlist API&quot;, description = &quot;아이템 리스트에 대한 API&quot;)
@RestController
@RequiredArgsConstructor
@RequestMapping(&quot;/api/journeys/{journey_id}/itemlist&quot;)
public class ItemlistController {
    private final ItemService itemService;
    @Operation(summary = &quot;한 여정에 대한 아이템 리스트 다 불러오기&quot;, description = &quot;특정 여정에 대한 모든 아이템을 가져오는 API입니다.&quot;)
    @Parameter(name = &quot;journey_id&quot;, description = &quot;조회할 여정의 ID&quot;)
    @GetMapping
    public ResponseEntity&lt;Object&gt; readItemlist(@PathVariable(&quot;journey_id&quot;) int journeyId) {
        try {
            List&lt;Item&gt; itemList = itemService.readItemlist(journeyId);
            if (itemList.isEmpty()) {
                Message message = new Message(&quot;404&quot;, &quot;아이템 목록이 비어 있습니다.&quot;);
                return new ResponseEntity&lt;&gt;(message, HttpStatus.NOT_FOUND);
            } else {
                Message message = new Message(&quot;200&quot;, &quot;아이템 목록을 가져오기 성공&quot;, itemList);
                return new ResponseEntity&lt;&gt;(message, HttpStatus.OK);
            }
        } catch (Exception e) {
            Message message = new Message(&quot;500&quot;, &quot;아이템 목록 읽기 서버 오류로 인해 아이템 목록을 가져올 수 없습니다.&quot;);
            return new ResponseEntity&lt;&gt;(message, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }</code></pre><p>@ Tag, @Operation과 @Parameter</p>
<h3 id="끝">끝</h3>
<blockquote>
<p><a href="http://localhost:8080/swagger-ui/index.html">http://localhost:8080/swagger-ui/index.html</a>
server.servlet.context-path에 경로 넣었으면 (예: /api)
<a href="http://localhost:8080/api/swagger-ui/index.html">http://localhost:8080/api/swagger-ui/index.html</a>
이런식으로 하면 됨!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cypress]]></title>
            <link>https://velog.io/@im_agination/Cypress</link>
            <guid>https://velog.io/@im_agination/Cypress</guid>
            <pubDate>Fri, 16 Feb 2024 08:20:08 GMT</pubDate>
            <description><![CDATA[<p>아니 버튼이 안보여</p>
<hr>
<blockquote>
<p>npm : 10.2.4
node : 20.11.0</p>
</blockquote>
<p>E2E 테스트 코드와 실행 결과를 가져오라고 해서 검색해보니 Cypress라는게 있다고 한다. Selenium과 고민했는데, Cypress를 써도 지금 프로젝트 수준에선 다 할 수 있을 거 같아서 적용했다.</p>
<p>그런데... 이거 왜이렇게 버튼이 안보일까 처음에 애먹었다.</p>
<p><a href="https://www.cypress.io/">Cypress 다운받기</a></p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/4508af0d-13e6-4c75-9224-5f2048544e55/image.png" alt="">
<img src="https://velog.velcdn.com/images/im_agination/post/d5c362bc-b9a5-4cd6-bd06-68b9816aa334/image.png" alt="">
Cypress는 사용하는 방법이 두가지인데 npm이나 실행파일을 인스톨하는 방법이다.
나는 그냥 인스톨해서 사용하기로 했다.</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/481e3550-4e61-46e4-bc1a-798d1699baa0/image.png" alt=""></p>
<p>cypress 실행 파일을 누르면 지금 나는 몇 개를 해봐서 이렇게 뜨는데 Add project를 하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/2069a475-b39f-4827-a5f9-1c62f2ee3eaf/image.png" alt="">
나는 E2E 테스트를 할 것이라 E2E를 클릭</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/d3af409d-f8ae-46ad-ba4a-0f43b8c660cb/image.png" alt="">
컨티뉴</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/cd6a5137-8932-46bf-a3f9-1f38131d0d2d/image.png" alt="">
나는 크롬으로 해서 E2E 테스팅을 시작했다.</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/63f62419-795a-4372-8ffa-8748f3ee1341/image.png" alt="">
누르면 이렇게 브라우저가 실행되고 두가지 선택지가 생기는데 Scaffold의 경우 예시를 볼 수 있고 Create new spec을 누르면 바로 프로젝트에 반영 가능한 예시 하나만 있다.</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/780138cb-cdb9-4e8b-b0bd-ae1bd1c53725/image.png" alt="">
<img src="https://velog.velcdn.com/images/im_agination/post/9ada5718-2018-4516-9b7d-05c773d9ba86/image.png" alt=""></p>
<p>그저 create</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/2058e058-bced-479a-9305-24dbee4bfc4a/image.png" alt="">
이러면 예시가 하나 나온다.</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/57349ed4-c3e7-4e3a-869e-99f22c7c5dba/image.png" alt=""></p>
<p>설정 해야하는 것이 하나 있는데 setting-project settings으로 들어간다</p>
<pre><code class="language-javascript">const { defineConfig } = require(&quot;cypress&quot;);

module.exports = defineConfig({
  reporter: &quot;cypress-mochawesome-reporter&quot;,
  video: false,
  reporterOptions: {
    charts: true,
    reportPageTitle: &quot;Cypress Inline Reporter&quot;,
    embeddedScreenshots: true,
    inlineAssets: true, //Adds the asserts inline
  },

  e2e: {
    experimentalStudio: true,
    setupNodeEvents(on, config) {
      require(&quot;cypress-mochawesome-reporter/plugin&quot;)(on);
    },
  },
});</code></pre>
<p>나는 나중에 테스트도 할거라서 이렇게 넣었다.</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/6cdf3910-ae34-4fe5-95b7-113e4386872f/image.png" alt="">
다시 spec 있는 곳으로 가면 저렇게 위 사진처럼 커서를 올리면 Add New Test가 있는데 이제 여기서 본격적으로 테스트 코드를 만들면 된다.</p>
<p><img src="https://velog.velcdn.com/images/im_agination/post/346b93fe-dc76-410b-9a05-53c441ce9435/image.gif" alt="">
이렇게 만들면 된다.</p>
<blockquote>
<p>만약 Add New Test가 안된다면 experimentalStudio: true, 이걸 추가했는지 확인 할 것!</p>
</blockquote>
<p>만약 만들어진 테스트 코드가 맘에 안든다면
<img src="https://velog.velcdn.com/images/im_agination/post/cdcc5d94-3ed7-4e29-8174-76f5db1b8d9b/image.gif" alt=""></p>
<p>그냥 나는 Open in IDE에서 그 부분 지운다. 그럼 잘됨.</p>
<p>이렇게 되면 테스트코드는 다 만든거다.</p>
<p>다음 편은 이제 프로젝트에서 직접 테스트하기 편인데... 왜인지 작동을 안한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[도커 설치 맛 좀 볼래?]]></title>
            <link>https://velog.io/@im_agination/%EB%8F%84%EC%BB%A4-%EB%A7%9B-%EC%A2%80-%EB%B3%BC%EB%9E%98</link>
            <guid>https://velog.io/@im_agination/%EB%8F%84%EC%BB%A4-%EB%A7%9B-%EC%A2%80-%EB%B3%BC%EB%9E%98</guid>
            <pubDate>Thu, 01 Feb 2024 05:04:55 GMT</pubDate>
            <description><![CDATA[<p>졸업해도 어려움ㅠ</p>
<hr>
<blockquote>
<p>버전
윈도우 10 </p>
</blockquote>
<p>대충 과정을 말해주면</p>
<ol>
<li>wsl2 설치</li>
<li>도커 설치<h2 id="wsl2-설치">wsl2 설치</h2>
</li>
</ol>
<h3 id="linux용-window-하위-시스템-사용">linux용 window 하위 시스템 사용</h3>
<p>powershell을 관리자 권한으로 열고 밑의 명령어 입력</p>
<pre><code>dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart</code></pre><h3 id="버전-확인">버전 확인</h3>
<p>내가 하려는 수동 설치 기준 밑의 기준은 만족해야한다고 한다.</p>
<pre><code>x64 시스템의 경우: 버전 1903 이상, 빌드 18362.1049 이상.
ARM64 시스템의 경우: 버전 2004 이상, 빌드 19041 이상</code></pre><p>버전 확인하고 싶다면 아래 명령어 입력한다.</p>
<pre><code>winver</code></pre><h3 id="virtual-machine-사용">virtual machine 사용</h3>
<p>powershell 관리자 권한으로 열어서 확인</p>
<pre><code>dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart</code></pre><h3 id="linux-커널-업데이트-패키지-다운">linux 커널 업데이트 패키지 다운</h3>
<p>링크에서 다운
<a href="https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi">x64</a></p>
<p><a href="https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_arm64.msi">ARM64</a></p>
<h3 id="wsl-2를-기본-버전으로-설정">wsl 2를 기본 버전으로 설정</h3>
<p>powershell 관리자 모드로 열고 밑의 명령어 입력</p>
<pre><code>wsl --set-default-version 2</code></pre><h3 id="선택한-linux-배포-설치">선택한 linux 배포 설치</h3>
<p>내가 뭐 이미지 파일 가져와서 해도된다지만... 너무 힘들어
<img src="https://velog.velcdn.com/images/im_agination/post/7141cabd-3a63-4980-9a3f-a05000fc5ada/image.png" alt="">
필요한 버전 넣기
나는 20.04 필요해서 설치</p>
<p>설치가 됐다면 열고나서 실행하면 사용자 설정하고 실행된다.</p>
<p>만약 18.04로 바꾸고 싶다면?</p>
<pre><code>wsl --set-version Ubuntu-18.04</code></pre><p>배포판을 다운로드 했다면 -&gt; <a href="https://learn.microsoft.com/ko-kr/windows/wsl/install-manual#downloading-distributions">배포판 다운로드 한 경우</a></p>
<p>이거 참고하는게 빠를듯 나도 사실 안해봐서 모름..</p>
<h2 id="도커">도커</h2>
<h3 id="도커-설치">도커 설치</h3>
<p><a href="https://www.docker.com/">도커 다운</a>
<img src="https://velog.velcdn.com/images/im_agination/post/de7a9fdd-1d2a-4286-a377-559bcd35c1ba/image.png" alt="">
자 이건 윈도우고~ 저건 맥북 m 시리즈야~</p>
<p>나는 도커 허브도 이용해볼 생각이라 로그인 했다.</p>
<blockquote>
<p>configuration에 있는 Install Required Windows components for WSL2
Add shortcut to desktop은 다 체크✅</p>
</blockquote>
<p>그러면 설치가 되는데 아니 1시간을 해도 안되는거다.
그래서 껐다 켰더니 잘됨ㅋ 우왕<del>굿</del>ㅎㅎ</p>
<h3 id="도커-설정">도커 설정</h3>
<ol>
<li>Setting - General
Use the WSL 2 based engine 체크✅</li>
<li>Setting - Resources - WSL INTEGRATION
사용할 우분투 버전 ON
<img src="https://velog.velcdn.com/images/im_agination/post/f5f9607b-1005-4779-ad5e-49ece1f08457/image.png" alt=""></li>
</ol>
<h2 id="끝">끝</h2>
<p>난 이미지 파일 넣을게 있어서 넣었고
<img src="https://velog.velcdn.com/images/im_agination/post/b1172767-c348-4298-a9a3-423ca968698e/image.png" alt="">
잘된다.</p>
<p>잘되나 보고싶은데? cmd 실행 뒤</p>
<pre><code>wsl -l -v</code></pre><p>보통 설치한 우분투버전이랑 도커랑 도커 데스크탑데이터 이렇게 뜨면 잘 되는 거임</p>
<p>설치는 잘 된거같은데... 왜인지 잘 안된다면 그냥 <strong>재부팅</strong> 하라고 말해줬습니다?</p>
<p><a href="https://learn.microsoft.com/ko-kr/windows/wsl/install-manual">출처</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Pageable, MyBatis를 통한 페이지네이션]]></title>
            <link>https://velog.io/@im_agination/Pageable-myBatis</link>
            <guid>https://velog.io/@im_agination/Pageable-myBatis</guid>
            <pubDate>Mon, 29 Jan 2024 18:53:35 GMT</pubDate>
            <description><![CDATA[<pre><code>from django.core.paginator import Paginator</code></pre><p>한 줄이면 될 걸 온갖 난리를 쳐야하는 Springboot</p>
<hr>
<blockquote>
<p>버전 정보 
Java: 17
spring boot: 3.2.2
mybatis springboot-starter: 3.0.3
mySQL</p>
</blockquote>
<p>JPA의 시대, myBatis 깎는 나는 너무나 괴롭다. 자료가 너무 없다.</p>
<p>눈물겨운 여러가지 시도들</p>
<pre><code>직접 구현하기 (귀찮아) -&gt; pageHelper (뭔가 좀 그래) -&gt; 직접 구현하기 2트 (이게 맞냐?) -&gt; Pageable (성공이요)</code></pre><p>페이지네이션은 자주 쓸 거 같아서 나를 위해 정리해서 저장할거다.</p>
<p>그냥 mapper.xml -&gt; mapper.java -&gt; service -&gt; controller로 간다고 생각하면 된다.</p>
<h2 id="dependency">dependency</h2>
<p> <a href="https://mvnrepository.com/artifact/org.springframework.data/spring-data-commons"><strong>spring-data-commons</strong></a>
여기서 gradle이던 maven이던 찾아서 넣기
나는 스프링도 최근 버전이고 해서 그냥 가장 최근 버전 넣는다.</p>
<h2 id="mapperxml">mapper.xml</h2>
<pre><code class="language-xml">&lt;-- 리스트 나열 --&gt;
    &lt;select id=&quot;list&quot; parameterType=&quot;map&quot; resultType=&quot;com.example.demo.room.dto.RoomDto&quot;&gt;
        SELECT *
        FROM room
        LIMIT ${offset} , ${pageSize}
    &lt;/select&gt;
&lt;-- 개수 --&gt;
    &lt;select id=&quot;countRooms&quot; resultType=&quot;int&quot;&gt;
        SELECT COUNT(*) AS room_count
        FROM room
    &lt;/select&gt;</code></pre>
<h2 id="mapperjava">mapper.java</h2>
<pre><code class="language-java">@Mapper
public interface RoomMapper {
    List&lt;Map&lt;String, Object&gt;&gt; list (Map&lt;String, Object&gt; paramMap);
    int countRooms();
  }</code></pre>
<h2 id="service">service</h2>
<h3 id="roomservice-인터페이스">RoomService (인터페이스)</h3>
<pre><code class="language-java">import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

public interface RoomService {
    List&lt;RoomDto&gt; getRoomList();
    Page&lt;Map&lt;String, Object&gt;&gt; list (Map&lt;String, Object&gt; paramMap, Pageable page);</code></pre>
<h3 id="roomserviceimpljava">RoomServiceImpl.java</h3>
<pre><code class="language-java">...
    @Override
    public Page&lt;Map&lt;String, Object&gt;&gt; list(Map&lt;String, Object&gt; paramMap, Pageable page) {
        paramMap.put(&quot;offset&quot;,page.getOffset());
        paramMap.put(&quot;pageSize&quot;,page.getPageSize());
        List&lt;Map&lt;String, Object&gt;&gt; contents = roomMapper.list(paramMap);
        int count = roomMapper.countRooms();
        return new PageImpl&lt;&gt;(contents,page,count);
    }
   ...</code></pre>
<h2 id="controllerjava">Controller.java</h2>
<pre><code class="language-java">    @GetMapping
    @ResponseBody
    public ResponseEntity&lt;Object&gt; list(@RequestParam Map&lt;String, Object&gt; paramMap, @PageableDefault(value = 10) Pageable page){
        Map&lt;String, Object&gt; resultMap = new HashMap&lt;String, Object&gt;();

        Page&lt;Map&lt;String,Object&gt;&gt; result = roomService.list(paramMap,page);
        resultMap.put(&quot;pages&quot;, result);
        resultMap.put(&quot;size&quot;, page.getPageSize());

            return ResponseEntity.ok()
                    .body(resultMap);
    }</code></pre>
<p><strong>@PageableDefault(value=10)</strong> 이게 딱봐도 한 페이지당 max 개수</p>
<h2 id="postman에서는-잘뜰까">Postman에서는 잘뜰까?</h2>
<pre><code>http://localhost:8080/room?page=1</code></pre><p>딸깍</p>
<pre><code>{
    &quot;pages&quot;: {
        &quot;content&quot;: [
        (생략)
        ],
        &quot;pageable&quot;: {
            &quot;pageNumber&quot;: 1,
            &quot;pageSize&quot;: 2,
            &quot;sort&quot;: {
                &quot;empty&quot;: true,
                &quot;sorted&quot;: false,
                &quot;unsorted&quot;: true
            },
            &quot;offset&quot;: 2,
            &quot;unpaged&quot;: false,
            &quot;paged&quot;: true
        },
        &quot;last&quot;: false,
        &quot;totalPages&quot;: 6,
        &quot;totalElements&quot;: 11,
        &quot;size&quot;: 2,
        &quot;number&quot;: 1,
        &quot;sort&quot;: {
            &quot;empty&quot;: true,
            &quot;sorted&quot;: false,
            &quot;unsorted&quot;: true
        },
        &quot;first&quot;: false,
        &quot;numberOfElements&quot;: 2,
        &quot;empty&quot;: false
    },
    &quot;size&quot;: 2
}</code></pre><p>(대충 잘뜬다는 글)</p>
<p><a href="https://s-yeonjuu.tistory.com/19">나의 은인이자 출처</a></p>
<p><em>[다음편] 지옥의 Vue.js 페이지네이션편</em>
은 없고 어쩌다보니 <a href="https://velog.io/@im_agination/React-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98%EC%9D%98-%EB%A7%9B">React</a>로 하게 되었습니다.</p>
]]></description>
        </item>
    </channel>
</rss>