<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>baekschema.log</title>
        <link>https://velog.io/</link>
        <description>기록하고 싶은 내용들을 주로 올리고 있습니다</description>
        <lastBuildDate>Tue, 18 Mar 2025 01:55:38 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. baekschema.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/qiwisil_227" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[SAP Background(Batch Job) 프로그램 실행 결과, 스풀 확인하는 법]]></title>
            <link>https://velog.io/@qiwisil_227/SAP-BackgroundBatch-Job-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EC%8B%A4%ED%96%89-%EA%B2%B0%EA%B3%BC-%EC%8A%A4%ED%92%80-%ED%99%95%EC%9D%B8%ED%95%98%EB%8A%94-%EB%B2%95</link>
            <guid>https://velog.io/@qiwisil_227/SAP-BackgroundBatch-Job-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EC%8B%A4%ED%96%89-%EA%B2%B0%EA%B3%BC-%EC%8A%A4%ED%92%80-%ED%99%95%EC%9D%B8%ED%95%98%EB%8A%94-%EB%B2%95</guid>
            <pubDate>Tue, 18 Mar 2025 01:55:38 GMT</pubDate>
            <description><![CDATA[<h3 id="1-t-code-sm37-입력-후-실행">1. T-Code: SM37 입력 후, 실행</h3>
<hr>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/3b383ebe-1ba4-4b00-9e57-1e179948dbed/image.png" alt=""></p>
<h3 id="2-확인하고자-하는-job의-스풀-아이콘-선택">2. 확인하고자 하는 Job의 스풀 아이콘 선택</h3>
<hr>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/7f7a4dc6-b2b0-400f-ab06-8eb1f8535918/image.png" alt=""></p>
<h3 id="3-유형-아이콘-선택">3. 유형 아이콘 선택</h3>
<hr>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/e23dd8a8-3c1a-4ea1-8dc2-db4555334899/image.png" alt=""></p>
<h3 id="4-결과-확인">4. 결과 확인</h3>
<hr>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/a1bdf2cb-26ad-44f3-8e79-cda59ff6ecfa/image.png" alt=""></p>
<p>끝!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SAP CURSOR: 대량의 데이터를 모두 조회&처리하기]]></title>
            <link>https://velog.io/@qiwisil_227/SAP-CURSOR-%EB%8C%80%EB%9F%89%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EB%AA%A8%EB%91%90-%EC%A1%B0%ED%9A%8C%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@qiwisil_227/SAP-CURSOR-%EB%8C%80%EB%9F%89%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EB%AA%A8%EB%91%90-%EC%A1%B0%ED%9A%8C%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 25 Apr 2024 05:18:46 GMT</pubDate>
            <description><![CDATA[<p>데이터 조회 시, 십만 건 정도까지는 한 번에 조회가 가능하지만</p>
<p>조회해야할 데이터가 천만 건, 1억 건 이상으로 커지면 일반 select문으로 전체 데이터를 조회하는 것은 불가능하다.</p>
<p><strong>이때 DB cursor로 대량의 데이터도 조회하고 처리할 수 있다.</strong></p>
<h3 id="1️⃣-커서에-query-설정하기">1️⃣ 커서에 Query 설정하기</h3>
<hr>
<pre><code class="language-abap">DATA : dbcur TYPE cursor,                  &quot;커서
             lt_tmp TYPE TABLE OF sflight. &quot;타겟 테이블

OPEN CURSOR WITH HOLD dbcur FOR
    SELECT *
    FROM sflight.</code></pre>
<ul>
<li><code>cursor</code>라는 TYPE으로 cursor 변수를 선언한다.</li>
<li><code>OEPN CURSOR</code>이라는 구문과 함께 cursor로 실행할 Query문을 설정해준다.<ul>
<li>이때 바로 Query가 수행되는 것은 아니다.</li>
</ul>
</li>
</ul>
<br>


<h3 id="2️⃣-package-size만큼-반복하며-데이터-select">2️⃣ Package Size만큼 반복하며 데이터 SELECT</h3>
<pre><code class="language-abap">DO.
*- 반복 1회마다 설정한 건수만큼 조회
    FETCH NEXT CURSOR dbcur
    INTO CORRESPONDING FIELDS OF TABLE lt_tmp
    PACKAGE SIZE 50. &quot;몇 건씩 나눠서 조회할 것인지 설정 

*- 더 이상 데이터가 조회되지 않을때 반복문 중단
    IF sy-subrc = 0.
        EXIT.
    ENDIF.

*- 설정한 건수만큼 조회된 lt_tmp 테이블로 다른 작업을 수행
    IF lt_tmp IS NOT INITIAL.
        &quot; 필요한 데이터 처리하거나 다른 변수에 저장하기
    ENDIF.
ENDDO.</code></pre>
<ul>
<li>DO 반복문 속에서 cursor를 통해 조회가 진행된다.</li>
<li>Package Size: 반복문 1회 수행 시 처리할 데이터의 건수</li>
<li>예시<ul>
<li>데이터 15,000건을 Package Size 5,000으로 조회</li>
<li>1회: 5,000건 조회 -&gt; 2회: 5,000건 조회 -&gt; 3회: 5,000건 조회 (누적 15,000건)</li>
<li>총 3번의 반복으로 전체 데이터 조회 가능</li>
</ul>
</li>
</ul>
<br>


<h3 id="3️⃣-커서-닫기">3️⃣ 커서 닫기</h3>
<pre><code class="language-abap">CLOSE CURSOR dbcur.</code></pre>
<p>감사합니다.</p>
<p><a href="https://m.blog.naver.com/zero_it/222741179081">참고한 블로그</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SAP] EXPORT 구문: 문자열 형태로 압축하기]]></title>
            <link>https://velog.io/@qiwisil_227/SAP-EXPORT-%EA%B5%AC%EB%AC%B8-Internal-Table-%EB%AC%B8%EC%9E%90%EC%97%B4-%ED%98%95%ED%83%9C%EB%A1%9C-%EC%95%95%EC%B6%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@qiwisil_227/SAP-EXPORT-%EA%B5%AC%EB%AC%B8-Internal-Table-%EB%AC%B8%EC%9E%90%EC%97%B4-%ED%98%95%ED%83%9C%EB%A1%9C-%EC%95%95%EC%B6%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 25 Apr 2024 04:56:22 GMT</pubDate>
            <description><![CDATA[<p>EXPORT, IMPORT 구문과 buffer 개념을 사용하여 Internal Table을 xstring 타입으로 압축해봅시다.
**
압축: <code>EXPORT 인터널테이블 TO DATA BUFFER 문자열.</code>
원복: <code>IMPORT 인터널테이블 FROM DATA BUFFER 문자열</code>
**</p>
<h2 id="1-데이터-선언">1. 데이터 선언</h2>
<hr>
<pre><code class="language-ABAP">DATA : lt_data TYPE TABLE OF mara. &quot;압축할 데이터
DATA : lv_xstring TYPE xstring.    &quot;Buffer 압축문이 들어갈 변수</code></pre>
<p>압축할 Internal Table과, 압축문을 저장할 변수를 선언해줍니다.</p>
<h2 id="2-데이터-압축">2. 데이터 압축</h2>
<hr>
<pre><code>SELECT * FROM mara INTO TABLE lt_data UP TO 10 ROWS.
EXPORT lt_data TO DATA BUFFER lv_xstring.

* &quot; 압축할 테이블이 2개 이상인 경우
* EXPORT data1 = lt_data
*        data2 = lt_data2 TO DATA BUFFER lv_xstring. </code></pre><p>Internal Table에 데이터를 삽입해주고,
EXPORT 구문으로 데이터를 압축합니다.</p>
<h2 id="3-압축을-풀고-원문-가져오기">3. 압축을 풀고 원문 가져오기</h2>
<hr>
<pre><code>CLEAR lt_data.
IMPORT lt_data FROM DATA BUFFER lv_xstring. &quot;변형된 데이터 다시 테이블 형태로 변경</code></pre><p>IMPORT 구문으로 원본 데이터를 구합니다.</p>
<p><a href="https://blog.naver.com/zero_it/222737790504">참고한 블로그</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ABAP] SELECTION-SCREEN COMMENT: Error when generating selection screen]]></title>
            <link>https://velog.io/@qiwisil_227/ABAP-SELECTION-SCREEN-COMMENT-Error-when-generating-selection-screen</link>
            <guid>https://velog.io/@qiwisil_227/ABAP-SELECTION-SCREEN-COMMENT-Error-when-generating-selection-screen</guid>
            <pubDate>Fri, 19 Apr 2024 07:13:01 GMT</pubDate>
            <description><![CDATA[<h2 id="🐞-에러-발생-상황">🐞 에러 발생 상황</h2>
<p>SELECTION-SCREEN COMMENT를 작성하고 문법 검사(Ctrl+F2)까지 완료했는데, 
아래와 같은 에러 발생</p>
<ul>
<li>사진 자료
<img src="https://velog.velcdn.com/images/qiwisil_227/post/f86e3b6e-5b9e-40fd-9d7f-7fa28f499fec/image.png" alt=""></li>
</ul>
<h2 id="🎉-해결">🎉 해결</h2>
<p>공백(문자 길이)를 뜻하는 숫자 앞에 <code>/</code>를 추가해주면 된다
<a href="https://help.sap.com/doc/abapdocu_750_index_htm/7.50/en-US/abapselection-screen_comment.htm">참고한 Document</a></p>
<ul>
<li>소스코드
<img src="https://velog.velcdn.com/images/qiwisil_227/post/727aea65-052d-4585-9807-a3d12033dfb7/image.png" alt=""></li>
<li>실행 화면
<img src="https://velog.velcdn.com/images/qiwisil_227/post/38f0a50b-9d14-4e4c-99fe-c902833e4a3f/image.png" alt=""></li>
</ul>
<h2 id="🎀-selection-screen-comment-문법">🎀 SELECTION-SCREEN COMMENT 문법</h2>
<p>SELECTION-SCREEN COMMENT <code>/왼쪽공백(문자길이)</code> <code>변수</code>.</p>
<ul>
<li><p>변수</p>
<ul>
<li>텍스트기호 (text-001) :  [이동]-[텍스트기호] F5에서 설정 가능</li>
<li>일반 변수 (gv_text) : 작성 동시에 선언이 되므로 밑에서 값 삽입해주는 로직 추가해야함</li>
</ul>
</li>
<li><p><a href="https://m.blog.naver.com/smj9030/223211386053">SELECTION-SCREEN 정리 잘 되어있는 글</a></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[SAP Background로 즉시 실행하기]]></title>
            <link>https://velog.io/@qiwisil_227/SAP-Background%EB%A1%9C-%EC%A6%89%EC%8B%9C-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@qiwisil_227/SAP-Background%EB%A1%9C-%EC%A6%89%EC%8B%9C-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 25 Mar 2024 01:48:05 GMT</pubDate>
            <description><![CDATA[<ul>
<li>tool bar의 [Program] - [Execute in Background] 선택
<img src="https://velog.velcdn.com/images/qiwisil_227/post/ca262589-b1df-4aa8-b726-e2cc5ca73af8/image.png" alt=""></li>
</ul>
<ul>
<li>체크 아이콘의 [Continue] 선택
<img src="https://velog.velcdn.com/images/qiwisil_227/post/6bb055ac-2a91-4e51-9e8c-314f22341077/image.png" alt=""></li>
</ul>
<ul>
<li>[Immediate] 선택 후 디스크 아이콘의 [Save] 선택
<img src="https://velog.velcdn.com/images/qiwisil_227/post/7cc1552a-869e-4044-bed2-53e6b2179e9c/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[SAP GUI 비밀번호 변경]]></title>
            <link>https://velog.io/@qiwisil_227/SAP-GUI-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EB%B3%80%EA%B2%BD</link>
            <guid>https://velog.io/@qiwisil_227/SAP-GUI-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EB%B3%80%EA%B2%BD</guid>
            <pubDate>Fri, 19 Jan 2024 00:17:26 GMT</pubDate>
            <description><![CDATA[<h1 id="1-시스템---사용자-프로파일---사용자-데이터">1. [시스템] - [사용자 프로파일] - [사용자 데이터]</h1>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/fc135db8-6a8a-4e50-9641-0fc6df47cad5/image.png" alt=""></p>
<h1 id="2-비밀번호">2. [비밀번호]</h1>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/cde1f178-5546-4739-a4c9-496433ba3c9d/image.png" alt=""></p>
<h1 id="3-기존-비밀번호-입력-및-신규-비밀번호-입력">3. 기존 비밀번호 입력 및 신규 비밀번호 입력</h1>
<h1 id="4-비밀번호-변경-완료">4. 비밀번호 변경 완료</h1>
<p><a href="https://bebeya.tistory.com/entry/SAP-SAPGUI-%EC%82%AC%EC%9A%A9%EC%9E%90-%ED%8C%A8%EC%8A%A4%EC%9B%8C%EB%93%9C%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EB%B3%80%EA%B2%BD%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95">참고 블로그</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[암호화 용어 정리]]></title>
            <link>https://velog.io/@qiwisil_227/%EC%95%94%ED%98%B8%ED%99%94-%EC%9A%A9%EC%96%B4-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@qiwisil_227/%EC%95%94%ED%98%B8%ED%99%94-%EC%9A%A9%EC%96%B4-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 26 Oct 2023 07:22:22 GMT</pubDate>
            <description><![CDATA[<h1 id="1-암호화-encryption">1. 암호화 (Encryption)</h1>
<h2 id="11-암호화의-시작">1.1. 암호화의 시작</h2>
<ul>
<li><p>로마시대 전쟁 때, 암호문으로 통신 시작</p>
<ul>
<li>알파벳을 3번 뒤로 미뤄서 작성</li>
<li>ABC -&gt; DEF</li>
</ul>
</li>
<li><p>Key값의 시작</p>
<ul>
<li>ABCKML&lt;평문&gt; </li>
<li>321321 &lt;키값&gt;</li>
<li>&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;</li>
<li>DDDIMKK &lt;암호문&gt;</li>
</ul>
</li>
</ul>
<h2 id="12-key값">1.2. Key값</h2>
<h3 id="소수로-만드는-키값">소수로 만드는 키값</h3>
<p>10자리 이상을 넘어가는 소수(1과 자신만을 약수로 가지는 수)로 키값을 정하면, 키값을 모르는 상태에서 컴퓨터가 복호화를 해내기 어렵다.</p>
<h3 id="key-사이즈">Key 사이즈</h3>
<p>32byte(256bits), 64byte를 주로 사용한다</p>
<p>⭐가장 많이 사용하는 알고리즘: AES256⭐
⭐key 사이즈: 256bit⭐</p>
<h3 id="아스키-코드">아스키 코드</h3>
<p>💡 0에서 127까지의 숫자를 사용해서 문자와 매핑하여, 문자를 숫자로 표현할 수 있게 한다<img src="https://velog.velcdn.com/images/qiwisil_227/post/c9732a9d-eb51-46db-a699-cad4461532b1/image.png" alt="">
<a href="https://stepbystep1.tistory.com/10">사진 출처</a></p>
<ul>
<li>암호화 / 복호화가 진행될 때</li>
<li>컴퓨터에서는 문자를 아스키코드 숫자값으로 인식하기 때문에</li>
<li>평문의 아스키코드 값과 key의 아스키코드 값이 XOR 연산되어진다.</li>
<li>↑ 이 XOR 연산하는 방법이 알고리즘마다 모두 다르다</li>
</ul>
<h3 id="알아두면-유용한-수의-단위">알아두면 유용한 수의 단위</h3>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/ec07fa72-d017-4f26-b4aa-c114d0eea76f/image.png" alt="">
<a href="https://blog.naver.com/PostView.naver?blogId=highkrs&amp;logNo=220194141040">사진 출처</a></p>
<h3 id="padding">Padding</h3>
<ul>
<li>평문의 길이와 키값의 길이는 같아야 한다.</li>
<li>그런데 두 길이가 다를때,</li>
<li>평문에 Padding이라 호칭하는 문자를 덧붙여준다</li>
</ul>
<h1 id="2-복호화-decryption">2. 복호화 (Decryption)</h1>
<p>암호화 과정을 반대로 수행</p>
<h1 id="3-base64">3. Base64</h1>
<h2 id="31-사용-목적">3.1. 사용 목적</h2>
<p>💡 인식하기 어려운 암호문을 인식할 수 있는 문자로 치환해준다</p>
<ul>
<li><p>컴퓨터에서는 총 256개 문자 중에 64개만 눈에 보이는 문자다. (??? 정확치 않은 내용)</p>
</li>
<li><p>암호화를 수행하면 눈에 보이지 않는 문자를 나타내는 비트열이 될 수 있다</p>
</li>
<li><p>암호문을 눈으로 확인해야 하는 경우가 있기 때문에</p>
</li>
<li><p>원래는 문자가 아니라서 아예 알아볼 수 없는 암호문을</p>
</li>
<li><p>Base64를 사용하여 눈에 보이는 문자로 치환해 확인하게 해준다</p>
</li>
</ul>
<h2 id="32-원리">3.2. 원리</h2>
<ul>
<li>암호문 비트열을 6bit 단위로 쪼개고</li>
<li>앞에 &#39;11&#39;을 붙여서 8bit로 만들어준다</li>
<li>=&gt; 눈에 보이는 아스키코드 문자가 된다</li>
</ul>
<p>⭐ 예시
11001011 10110000 &lt;암호문&gt;
110010 111011 0000 &lt;6bit 단위로 쪼개기&gt;
11110010 11111011 110000 &lt;앞에 11 붙여서 8bit로 만들기&gt;</p>
<h1 id="4-hash">4. Hash</h1>
<h2 id="41-사용-목적">4.1. 사용 목적</h2>
<p>💡 무결성을 확인하기 위함</p>
<p>문서 작성 후 해시값을 구하면
그 문서에 대한 고유한 값이 나온다.
. 하나라도 수정이 되면 아예 다른 해시값이 나오게 된다.
=&gt; 무결성, 유효성 확인&amp;찾기</p>
<h2 id="42-hash란">4.2. Hash란?</h2>
<ul>
<li><strong>복호화가 안 된다!!!!</strong></li>
<li>데이터의 길이가 길든 짧든, 나오는 Hash값은 동일하다.</li>
<li>각 원문마다 고유한 값이 나오게 되며, Hash값들이 같을 확률은 수억분의 일이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[ABAP: INTERNAL TABLE 실습]]></title>
            <link>https://velog.io/@qiwisil_227/ABAP-INTERNAL-TABLE-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@qiwisil_227/ABAP-INTERNAL-TABLE-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Thu, 07 Sep 2023 05:00:42 GMT</pubDate>
            <description><![CDATA[<p>❓ 목표: carrid가 &#39;AA&#39;인 CARRID와 CONNID 조회
<img src="https://velog.velcdn.com/images/qiwisil_227/post/a1538255-8311-47f9-ba21-229bfdf409ad/image.png" alt=""></p>
<h1 id="1-table-type을-dictionary에서-가져오기">1. Table Type을 Dictionary에서 가져오기</h1>
<pre><code>DATA: gs_sflight TYPE sflight,
          gt_sflight LIKE TABLE OF gs_sflight.

SELECT carrid connid INTO CORRESPONDING FIELDS OF TABLE  gt_sflight
  FROM sflight
  WHERE carrid = &#39;AA&#39;.

LOOP AT gt_sflight INTO gs_sflight.
  WRITE :/ gs_sflight-carrid, gs_sflight-connid.
ENDLOOP.</code></pre><h2 id="✅-point-1-data-선언">✅ Point 1. DATA 선언</h2>
<ul>
<li><p>sflight는 Abap Dictionary에 있는 테이블이다</p>
</li>
<li><p><code>gs_sflight TYPE sflight</code> 테이블을 바로 타입으로 선언해준다</p>
</li>
<li><p><code>gt_sflight LIKE TABLE OF gs_sflight</code> 이미 선언해둔 gs_sflight의 타입을 선언해준다 </p>
<ul>
<li>TABLE로 타입을 가져옴
(변수를 두개 만드는 이유는 아직 모르겠다. 추후 수정)</li>
</ul>
</li>
</ul>
<h2 id="✅-point-2-open-sql-select문">✅ Point 2. Open SQL Select문</h2>
<ul>
<li><p>open sql: ABAP에서 만든 SQL 문법</p>
<ul>
<li>? Open SQL 사용 안 하면 ?</li>
<li>DB마다 문법이 다르기 때문에 </li>
<li>연동하는 DB가 달라지면 모든 sql문을 수정해줘야 한다</li>
<li>하지만 Open SQL을 사용하면 DB interface만 일해주면 된다<pre><code>SELECT carrid connid INTO CORRESPONDING FIELDS OF TABLE  gt_sflight
FROM sflight
WHERE carrid = &#39;AA&#39;.</code></pre></li>
</ul>
</li>
<li><p>INTO
<code>INTO CORRESPONDING FIELDS OF TABLE 테이블명</code>  </p>
</li>
</ul>
<br>



<h1 id="2-abap-dictionary-table의-필드를-참조해서-itab-만들기">2. Abap Dictionary Table의 필드를 참조해서 ITAB 만들기</h1>
<pre><code>DATA : BEGIN OF gs_sflight,
        carrid TYPE sflight-carrid,
        connid TYPE sflight-connid,
        END OF gs_sflight.

DATA : gt_sflight LIKE TABLE OF gs_sflight.

SELECT carrid connid INTO TABLE gt_sflight
  FROM sflight
  WHERE carrid = &#39;AA&#39;.

LOOP AT gt_sflight INTO gs_sflight.
  WRITE :/ gs_sflight-carrid, gs_sflight-connid.
ENDLOOP.</code></pre><h2 id="✅-point-1-data-선언-1">✅ Point 1. DATA 선언</h2>
<pre><code>DATA : BEGIN OF gs_sflight,
        carrid TYPE sflight-carrid,
        connid TYPE sflight-connid,
        END OF gs_sflight.</code></pre><p>Table 만들때 요소들을 Dictionary Table Field를 참조</p>
<pre><code>DATA : gt_sflight LIKE TABLE OF gs_sflight.</code></pre><p>마찬가지로 <code>LIKE TABLE OF</code></p>
<h2 id="✅-point-2-open-sql">✅ Point 2. Open SQL</h2>
<pre><code>SELECT carrid connid INTO TABLE gt_sflight
  FROM sflight
  WHERE carrid = &#39;AA&#39;.</code></pre><ul>
<li>INTO
<code>INTO TABLE 테이블명</code></li>
<li>컬럼 순서<ul>
<li>Table의 column과 조회하는 column 순서가 같아야 한다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[ABAP: Program 생성]]></title>
            <link>https://velog.io/@qiwisil_227/ABAP-Program-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@qiwisil_227/ABAP-Program-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Thu, 07 Sep 2023 04:16:29 GMT</pubDate>
            <description><![CDATA[<h1 id="1-se38-접속">1. SE38 접속</h1>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/9d25e4fb-d599-4672-bba2-e6a8491c297c/image.png" alt=""></p>
<h1 id="2-이름-명명-유형-선택">2. 이름 명명, 유형 선택</h1>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/d0e492d0-f85e-45ed-8083-81a90ef0f4f3/image.png" alt=""></p>
<h2 id="프로그램-이름">프로그램 이름</h2>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/45959200-05fe-4225-b840-5828642de208/image.png" alt="">
A~X는 SAP에서 사용하고 있기 때문에
Y나 Z로 패키지명, 프로그램명을 시작한다.
단, Y는 잘 사용하지 않기 때문에 자기만 쓰고 싶거나 테스트용인 프로그램에 Y를 사용하는 것이 좋다.</p>
<h2 id="유형-선택">유형 선택</h2>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/889df155-23da-46d4-bee5-92808fae327c/image.png" alt=""></p>
<p>실행가능 프로그램으로 선택</p>
<h1 id="3-패키지-선택">3. 패키지 선택</h1>
<h2 id="패키지-검색창-들어가기">패키지 검색창 들어가기</h2>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/78fc9492-5b35-4ab1-affa-7de941a3d05e/image.png" alt="">
검색 안 하고 바로 타이핑해줘도 된다</p>
<h2 id="검색어-입력">검색어 입력</h2>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/4694d497-aaf5-4624-8de9-4c29ebb93d0c/image.png" alt="">
<code>ZJM*</code> ZJM으로 시작하는 모든 패키지 검색
엔터 누르거나 밑에 체크 아이콘을 눌러주면 된다</p>
<h2 id="검색-결과">검색 결과</h2>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/5caa2f58-86fa-4435-b48b-852088896e46/image.png" alt="">
원하는 패키지 선택해주기</p>
<h2 id="패키지-선택">패키지 선택</h2>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/571b341e-b556-44bd-aa75-c77d2dcf21a3/image.png" alt="">
저장</p>
<pre><code>주의!!!!
로컬오브젝트를 선택하면 안 된다</code></pre><p><img src="https://velog.velcdn.com/images/qiwisil_227/post/6a6fd3a6-cfda-4deb-b332-5c00fa0d4c52/image.png" alt="">
확인</p>
<h2 id="프로그램-확인">프로그램 확인</h2>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/489b8830-494f-433a-84ab-e379d7565c65/image.png" alt="">
SE80에서 패키지 안에 들어간 프로그램 확인</p>
<h1 id="번외-local-파일-package-파일-확인하는-법">번외. Local 파일, Package 파일 확인하는 법</h1>
<h2 id="local-확인">Local 확인</h2>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/61d83413-665a-4e4a-aae3-fe9132aad164/image.png" alt=""></p>
<ul>
<li>로컬오브젝트 선택 후 </li>
<li>입력란에 아이디 작성</li>
<li>안경 아이콘 클릭</li>
</ul>
<h2 id="package-파일-확인">Package 파일 확인</h2>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/90b766e6-aa20-49eb-b659-a24fbbf2d5b0/image.png" alt=""></p>
<ul>
<li>Package 선택 후</li>
<li>입력란에 패키지명 작성</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring boot + React localhost 대신 내 컴퓨터 ip로 실행시키기]]></title>
            <link>https://velog.io/@qiwisil_227/Spring-boot-React-%EB%82%B4-%EC%BB%B4%ED%93%A8%ED%84%B0-ip%EB%A1%9C-%EC%8B%A4%ED%96%89%EC%8B%9C%ED%82%A4%EA%B8%B0</link>
            <guid>https://velog.io/@qiwisil_227/Spring-boot-React-%EB%82%B4-%EC%BB%B4%ED%93%A8%ED%84%B0-ip%EB%A1%9C-%EC%8B%A4%ED%96%89%EC%8B%9C%ED%82%A4%EA%B8%B0</guid>
            <pubDate>Tue, 22 Aug 2023 23:43:53 GMT</pubDate>
            <description><![CDATA[<h1 id="💥-spring-boot">💥 Spring boot</h1>
<h2 id="applicationpropertis-설정">application.propertis 설정</h2>
<pre><code>server.port = 8000 --선택사항
server.address=10.96.124.111 --자기 컴퓨터 ip</code></pre><p>cmd창에 ipconfig 명령어를 통해서 ip 주소를 알 수 있다</p>
<h1 id="💥-react">💥 React</h1>
<h2 id="packagejson-설정">package.json 설정</h2>
<pre><code>
  &quot;scripts&quot;: {...},
  &quot;proxy&quot;: &quot;http://10.96.124.111:8000&quot;,</code></pre><h2 id="proxy-오류가-난다면-터미널에-아래-명령-실행하기">proxy 오류가 난다면 터미널에 아래 명령 실행하기</h2>
<pre><code>npm cache clean --force
npm run build</code></pre><p>그리고 다시 npm start해준다</p>
<h1 id="💥-접속">💥 접속</h1>
<h3 id="http자기-컴퓨터-ip포트번호"><code>http://(자기 컴퓨터 ip):(포트번호)</code></h3>
<p>리액트
<code>http://10.96.124.111:3000/</code></p>
<p>스프링
<code>http://10.96.124.111:8000/</code></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring boot + React 연동하기]]></title>
            <link>https://velog.io/@qiwisil_227/Spring-boot-React-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@qiwisil_227/Spring-boot-React-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 07 Aug 2023 07:56:49 GMT</pubDate>
            <description><![CDATA[<p>처음에 Spring과 React를 같이 쓰게 됐을때 정말정말 막막했다</p>
<p>NGINX를 사용하는게 더 좋다고 하는데, 나는 Spring 프로젝트 안에 React 프로젝트를 넣었다. <del>왜냐면 시간이 촉박했다..ㅎㅎ</del></p>
<p>나는 이 <a href="https://developer.okta.com/blog/2022/06/17/simple-crud-react-and-spring-boot">글</a>한테서 엄청난 도움을 받았는데 솔직히 하면서도 많이 헤맸었다. 그래서 간단하게나마 정리해보려고 한다</p>
<h2 id="1-스프링부트-프로젝트-만들기">1. 스프링부트 프로젝트 만들기</h2>
<p>할 수 있쬬?</p>
<h2 id="2-스프링-프로젝트-안에-리액트-프로젝트-만들기">2. 스프링 프로젝트 안에 리액트 프로젝트 만들기</h2>
<h3 id="💥-터미널-작업">💥 터미널 작업</h3>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/04cefeaf-877e-4ac6-8be7-d9e2d82c7e9d/image.png" alt="">
Main 파일을 가지고 있는 폴더를 터미널에서 열어준다</p>
<br>

<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/f0ba0ba9-2711-46b1-bb76-4fc0dc861ad9/image.png" alt="">
이렇게 뜰텐데 경로를 다시 한 번만!!!! 확인해보자!!!! java 파일 안에 있는지!!! 꼭 확인하자!!! 다른 경로에 잘못 react 프로젝트 설치하면 삭제하는데 애를 먹을 것이다 ...</p>
<p><code>npx create-react-app@5 app</code> 명령어로 리액트를 설치해주자. app이 아닌 다른 이름으로 해도 괜찮다 <del>~</del></p>
<h3 id="💥-리액트-프로젝트-확인하기">💥 리액트 프로젝트 확인하기</h3>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/96cdc522-e37d-4d25-a7b3-e7514b9466d2/image.png" alt="">
짠 리액트 프로젝트가 생긴다
나는 vs code에서 app 폴더만 열어서 프론트엔드 개발을 했다</p>
<h3 id="💥-추가로-부트스트랩을-사용하고-싶다면">💥 추가로 부트스트랩을 사용하고 싶다면</h3>
<p><code>cd app</code>
<code>npm i bootstrap@5 react-cookie@4 react-router-dom@6 reactstrap@9</code>
명령어를 추가로 동작시켜주고 </p>
<p>index.js에 들어가 
<code>import &#39;bootstrap/dist/css/bootstrap.min.css&#39;;</code> 이 한 줄을 추가해준다</p>
<br>

<h2 id="3-port-설정하기">3. port 설정하기</h2>
<h3 id="💥-리액트-packagejson-들어가기">💥 리액트 package.json 들어가기</h3>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/16e7bbce-3e0a-44d7-85a1-9099e6555c49/image.png" alt="">
&quot;scripts&quot;: {...}, 아래에 &quot;proxy&quot;를 추가시켜주면 끝이다
저 포트 번호는 자기가 application.properties나 .yml에 설정한 포트 번호를 써주면 된다! 포트번호를 따로 설정해주지 않았다면 8080을 쓰도록 ~!!</p>
<br>

<h2 id="4-통신은-어떻게-">4. 통신은 어떻게 !</h2>
<h3 id="💥-rest-api로-하면-된다">💥 REST API로 하면 된다</h3>
<p>백엔드, 프론트엔드 나눠서 협업을 하고 있다면 POST MAN 설치는 필수다.. POST MAN에서 REST API가 잘 작동하고 있는지 확인할 수 있다. </p>
<h3 id="💥-spring에서는-controller만-바꿔주면-됨">💥 Spring에서는 Controller만 바꿔주면 됨</h3>
<pre><code>@RequestMapping(&quot;/api/member&quot;)
public class MemberController {

// 맞다는 응답 보내기
@GetMapping(&quot;/me&quot;)
public ResponseEntity&lt;MemberResponseDto&gt; findMemberInfoById() {
    return ResponseEntity.ok(memberService.findMemberInfoById(
            SecurityUtil.getCurrentMemberId()) //내 정보 가져오기
    );
}

// 데이터 보내주기
@GetMapping(&quot;/item_list/{categoryPId}/all&quot;)
public Collection&lt;Item&gt; selectCategoryPIdItem(@PathVariable Long categoryPId){
    System.out.println(&quot;카테고리별 전체 상품목록 get&quot;);
    return itemService.selectItemByCategoryPId(categoryPId); // 카테고리 부모 id 입력 (전통한복, 개량한복, ...)
}

}</code></pre><p>급하게 프로젝트할때 딱 이 두 가지만 알면 다 할 수 있다</p>
<ul>
<li><code>ResponseEntity&lt;&gt;</code> :  로그인, 회원가입 같이 사용자가 데이터를 넘겨줬을때 yes, no로 응답함</li>
<li><code>Collection&lt;&gt;</code> :  상품 목록 등 데이터를 보내줘야할때 Collection&lt;DTO 자료형&gt;으로 반환해주면 된다</li>
</ul>
<h3 id="💥-react에서는-fetch나-axios로-받아오면-됨">💥 React에서는 fetch나 axios로 받아오면 됨</h3>
<pre><code>async function brandDatas() {
  try {
    // api 데이터 받아오기
    const response = await axios.get(`/api/item/item_list/${cid}/brand`, {
      headers: {
        &#39;Authorization&#39;: `Bearer ${getCookie(&quot;accessToken&quot;)}` // header에 토큰 추가
      }
    });

    // 데이터 저장
    setBrands(response.data);
  } catch (error) {
    console.error(&#39;Error fetching data:&#39;, error);
  }
}</code></pre><p>여러 가지 방법들이 있으니 구글링해보면서 자기한테 맞는 방법을 찾으면 좋을 거 같습니당</p>
<br>

<hr>
<p>헤매고 있으셨던 분들에게 도움이 되는 글이 됐으면 좋겠네요 ~ !
<a href="https://github.com/qorwlalsjimin/kiru.git">깃허브 링크</a> 엄청 부끄러운 프로젝트지만;;; REST API만 동작 원리만 참고해보세요 ~ !</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[XAMPP 설치 및 포트 변경]]></title>
            <link>https://velog.io/@qiwisil_227/XAMPP-%EC%84%A4%EC%B9%98-%EB%B0%8F-%ED%8F%AC%ED%8A%B8-%EB%B3%80%EA%B2%BD</link>
            <guid>https://velog.io/@qiwisil_227/XAMPP-%EC%84%A4%EC%B9%98-%EB%B0%8F-%ED%8F%AC%ED%8A%B8-%EB%B3%80%EA%B2%BD</guid>
            <pubDate>Sat, 05 Aug 2023 00:42:01 GMT</pubDate>
            <description><![CDATA[<h1 id="1-xampp-설치">1. XAMPP 설치</h1>
<p><a href="https://www.apachefriends.org/index.html">XAMPP 홈페이지</a>접속 후 Download
설치 과정에서 특별하게 신경써야할 점은 없다</p>
<br>

<h1 id="2-xampp-컨트롤-패널-gui">2. XAMPP 컨트롤 패널 (GUI)</h1>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/14dae1c3-1ac1-49fb-9c80-c81beff64454/image.png" alt="">
컴퓨터에 이미 mysql, oracle, ... 등등 아주 수많은 프로그램들이 포트를 차지하고 있었기 때문에 MySQL과 Tomcata이 포트 충돌이 났다 (캡처 당시에는 다 해결했기에 깔끔함..ㅎㅎ)</p>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/7ab7e44f-cc2f-45fc-8724-a9833eb065d2/image.png" alt="">
포트 바꾸려면 관리자 권한이 있어야하기 때문에 <strong>관리자 권한으로 실행</strong>해야 한다</p>
<br>

<h1 id="3-포트-변경하기">3. 포트 변경하기</h1>
<h2 id="👀행동요령-1">👀행동요령 1</h2>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/49b2c014-b92b-4dac-920a-c1bca1f3e101/image.png" alt="">
패널 전체 Config 접속</p>
<br>

<h2 id="👀행동요령-2">👀행동요령 2</h2>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/138b2c43-4f92-4cd7-95cd-901ef52c1c01/image.png" alt="">
Service and Port Settings 클릭</p>
<br>

<h2 id="👀행동요령-3">👀행동요령 3</h2>
<p>MySQL 3306 → 3300
<img src="https://velog.velcdn.com/images/qiwisil_227/post/608285c3-24da-41c2-933d-0f24670fad80/image.png" alt=""></p>
<p>Tomcat 8080 → 8000
<img src="https://velog.velcdn.com/images/qiwisil_227/post/7efcb119-3ad9-4221-9099-7d34bf147a4b/image.png" alt=""></p>
<p>탭으로 움직이며 바꿔야할 포트 변경
오류 로그보면 어떤 숫자 포트에서 오류가 났는지 알려주니까
오류에 뜬 숫자를 바꾸면 된다</p>
<br>

<h2 id="👀행동요령-4">👀행동요령 4</h2>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/9bb8c516-266c-45f2-8a8a-7ee51b99fddb/image.png" alt="">
MySQL: [Config] - [my.ini]
Tomcat: [Config] - [server.xml]</p>
<p>클릭하면 메모장으로 뜨는데 <code>Ctrl+F</code>로 변경 전 포트를 검색해서 변경한 포트로 싹 다 바꿔주면 된다
나는 3306을 검색해서 3300으로 다 바꿔줬다. 혹시 전체 바꾸기 기능을 쓰면 이상한 곳 건들일까봐 하나하나씩 다 바꿔주기는 했는데, 전체 바꾸기 기능으로 바꿔도 큰 문제는 없어보인다!</p>
<br>


<hr>
<p>누군가에게는 도움이 되는 게시글이었으면 좋겠네요 ~ !</p>
<p><a href="https://teserre.tistory.com/12">참고한 블로그</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[css 위아래 정렬 맞추기]]></title>
            <link>https://velog.io/@qiwisil_227/css-%EC%9C%84%EC%95%84%EB%9E%98-%EC%A0%95%EB%A0%AC-%EB%A7%9E%EC%B6%94%EA%B8%B0</link>
            <guid>https://velog.io/@qiwisil_227/css-%EC%9C%84%EC%95%84%EB%9E%98-%EC%A0%95%EB%A0%AC-%EB%A7%9E%EC%B6%94%EA%B8%B0</guid>
            <pubDate>Sat, 17 Jun 2023 22:23:15 GMT</pubDate>
            <description><![CDATA[<h2 id="부모-div에-아래-css-추가">부모 div에 아래 css 추가</h2>
<pre><code>    display: flex;
    justify-content: center;
    align-items: center;</code></pre><br>


<h2 id="이미지">이미지</h2>
<p>전
<img src="https://velog.velcdn.com/images/qiwisil_227/post/750447e7-cc6b-47b3-9ebb-8b7f0e61bae7/image.png" alt=""></p>
<p>후
<img src="https://velog.velcdn.com/images/qiwisil_227/post/1756ae98-1cb0-45f8-bb73-75a7391fcdac/image.png" alt=""></p>
<br>

<h2 id="전체-코드">전체 코드</h2>
<h3 id="1-html">1. html</h3>
<pre><code>            &lt;label className=&quot;idremem_container&quot;&gt;
              &lt;div className=&quot;idremem&quot;&gt;
                &lt;Unchecked /&gt; &lt;!--svg 파일입니다--&gt;
                &lt;p className=&quot;child&quot;&gt;아이디 기억하기&lt;/p&gt;
              &lt;/div&gt;
            &lt;/label&gt;
            &lt;br /&gt;</code></pre><h3 id="2-css">2. css</h3>
<pre><code>.idremem {

display: flex;
margin-top: 85px;
margin-bottom: 10px;

}</code></pre><p>자식한테는 지정하지 않아도 됨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 React 커스텀 select]]></title>
            <link>https://velog.io/@qiwisil_227/%EB%A6%AC%EC%95%A1%ED%8A%B8-React-%EC%BB%A4%EC%8A%A4%ED%85%80-select</link>
            <guid>https://velog.io/@qiwisil_227/%EB%A6%AC%EC%95%A1%ED%8A%B8-React-%EC%BB%A4%EC%8A%A4%ED%85%80-select</guid>
            <pubDate>Fri, 09 Jun 2023 11:12:00 GMT</pubDate>
            <description><![CDATA[<p>내가 올린 codesandbox 코드!!! 
너무 바빠서 설명은 나중에 추가하도록 하겠습니다..ㅠㅠ 
<a href="https://codesandbox.io/s/custom-radius-select-tag-6jh9qd?file=">https://codesandbox.io/s/custom-radius-select-tag-6jh9qd?file=</a></p>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/4e427e0f-158f-492d-9ae3-99f63e2e75f9/image.png" alt="">
<img src="https://velog.velcdn.com/images/qiwisil_227/post/d4774939-ef35-4fc7-9e7d-efc5092f8f76/image.png" alt="">
<img src="https://velog.velcdn.com/images/qiwisil_227/post/5d850b76-b860-46ba-961a-2ae8db4d32f9/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스크랩] css 각종 정렬]]></title>
            <link>https://velog.io/@qiwisil_227/%EC%8A%A4%ED%81%AC%EB%9E%A9-css-%EA%B0%81%EC%A2%85-%EC%A0%95%EB%A0%AC</link>
            <guid>https://velog.io/@qiwisil_227/%EC%8A%A4%ED%81%AC%EB%9E%A9-css-%EA%B0%81%EC%A2%85-%EC%A0%95%EB%A0%AC</guid>
            <pubDate>Fri, 09 Jun 2023 11:07:18 GMT</pubDate>
            <description><![CDATA[<p><a href="https://inpa.tistory.com/entry/CSS-%F0%9F%95%B9%EF%B8%8F-%EC%88%98%ED%8F%89-%EC%88%98%EC%A7%81-%EA%B0%80%EB%A1%9C-%EC%84%B8%EB%A1%9C-%EC%A0%95%EB%A0%AC-%EA%B8%B0%EB%B2%95-%F0%9F%92%AF-%EB%AA%A8%EC%9D%8C-%EC%A0%95%EB%A6%AC">https://inpa.tistory.com/entry/CSS-%F0%9F%95%B9%EF%B8%8F-%EC%88%98%ED%8F%89-%EC%88%98%EC%A7%81-%EA%B0%80%EB%A1%9C-%EC%84%B8%EB%A1%9C-%EC%A0%95%EB%A0%AC-%EA%B8%B0%EB%B2%95-%F0%9F%92%AF-%EB%AA%A8%EC%9D%8C-%EC%A0%95%EB%A6%AC</a></p>
<p><a href="https://myhappyman.tistory.com/7">https://myhappyman.tistory.com/7</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Security + JWT 회원가입/로그인 로직 이해하기]]></title>
            <link>https://velog.io/@qiwisil_227/%EC%98%88%EC%A0%9C-%EC%BD%94%EB%93%9C-%EB%A1%9C%EC%A7%81-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@qiwisil_227/%EC%98%88%EC%A0%9C-%EC%BD%94%EB%93%9C-%EB%A1%9C%EC%A7%81-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 02 Jun 2023 11:08:51 GMT</pubDate>
            <description><![CDATA[<p>이 글은 &#39;뱀귤&#39;님의 <a href="https://bcp0109.tistory.com/301">Spring Security+JWT 예제 코드 블로그</a>를 보고 개인 공부 목적으로 작성한 글입니다. 이 글에 나오는 모든 코드와 로직은 제것이 아님을 밝힙니다! 전반적인 로직을 확인하려면 계속 보시는 것을 추천드리고 코드가 궁금하시다면 뱀귤님의 블로그를 추천드립니다~!! 저는 <a href="https://github.com/ParkJiwoon/practice-codes/tree/master/spring-security-jwt">깃허브</a>에서 코드를 받아와 디버깅하며 공부했습니다 :)</p>
<p><a href="https://velog.io/@qiwisil_227/%EC%84%B8%EC%85%98-vs-%ED%86%A0%ED%81%B0-JWT%EB%9E%80">JWT 입문 개념 짚고 가기</a></p>
<br>

<h1 id="1-도메인-member">1. 도메인 Member</h1>
<h2 id="🥝-entity-엔티티">🥝 Entity 엔티티</h2>
<ul>
<li>Member, MemberResponseDto 두 가지가 있다<br>

</li>
</ul>
<h3 id="🥥-member">🥥 Member</h3>
<p>회원 정보 멤버변수</p>
<ul>
<li><code>id(GeneratedValue)</code>: 식별 id</li>
<li><code>email</code>: 사용자 이메일 </li>
<li><code>password</code>: 사용자 비밀번호</li>
<li><code>authority(Enum)</code>: 권한<ul>
<li>Authority Enum에는 <code>ROLE_USER</code> <code>ROLE_ADMIN</code> 사용자, 관리자 두 권한이 정의되어 있다</li>
</ul>
</li>
</ul>
<p>메서드</p>
<ul>
<li><code>Builder 생성자</code>: 이메일, 패스워드, 권한<br>

</li>
</ul>
<h3 id="🥥-refreshtoken">🥥 RefreshToken</h3>
<p>DB에 저장할 RefreshToken(↔ AccessToken)</p>
<p>멤버변수</p>
<ul>
<li><code>key</code>: 키</li>
<li><code>value</code>: 값</li>
<li><code>Builder</code> 생성자: 키, 값</li>
</ul>
<p>메서드</p>
<ul>
<li><code>updateValue(토큰)</code>: 매개변수로 전달 받은 token으로 갱신
<br><br></li>
</ul>
<h2 id="🥝-repositorydao-레퍼지토리">🥝 Repository(DAO) 레퍼지토리</h2>
<h3 id="🥥-memberrepository">🥥 MemberRepository</h3>
<p>DB에 있는 회원정보 조회 </p>
<ul>
<li><code>Optional&lt;Member&gt; findByEmail(이메일)</code>: 이메일로 회원 정보 조회</li>
<li><code>boolean existsByEmail(이메일)</code>: 중복 가입 방지를 위해 해당 이메일로 회원 등록 유무 조회
<br><br></li>
</ul>
<h2 id="🥝-service-서비스">🥝 Service 서비스</h2>
<h3 id="🥥-memberservice">🥥 MemberService</h3>
<p>식별 id, 이메일로 회원 정보 조회
반환형: MemberResponseDto</p>
<ul>
<li><code>MemberResponseDto findMemberInfoById(회원 식별 id)</code>: 식별 id로 회원 찾기 (Long 타입)</li>
<li><code>MemberResponseDto findMemberInfoByEmail(이메일)</code>: 이메일로 회원 찾기<br>

</li>
</ul>
<h3 id="🥥-memberresponsedto">🥥 MemberResponseDto</h3>
<p>회원 정보 요청 시 보내줄 응답</p>
<ul>
<li><code>email</code>: 회원 이메일</li>
<li><code>public static MemberResponseDto of(Member 엔티티)</code>: Member을 DTO로 만들어주기
<br><br></li>
</ul>
<h2 id="🥝-restcontroller-컨트롤러">🥝 RestController 컨트롤러</h2>
<h3 id="🥥-membercontroller">🥥 MemberController</h3>
<p>  GET 요청 받았을때 회원 정보 응답 전송
  &quot;api/member&quot;로 RequestMapping</p>
<ul>
<li><code>@GetMapping(&quot;/me&quot;)</code>
<code>public ResponseEntity&lt;MemberResponseDto&gt; findMemberInfoById()</code>: 내 정보 가져옴<br></li>
<li><code>@GetMapping(&quot;/{email}&quot;)</code>
<code>public ResponseEntity&lt;MemberResponseDto&gt; findMemberInfoByEmail(url로 받은 이메일)</code>: Get으로 받은 email로 회원 정보 응답 전송</li>
</ul>
<p>✅ 다른 Controller에서 API 요청이 들어오면 필터에서 <code>Access Token</code>을 복호화 해서 유저 정보를 꺼내 <code>SecurityContext</code>라는 곳에 저장한다 
✅ <code>SecurityContext</code>에 저장된 유저 정보는 전역이라서 어디서든 꺼낼 수 있다
✅ <code>SecurityUtil.getCurrentMemberId()</code>로 꺼낼 수 있다</p>
<h3 id="🥥-securityutil">🥥 SecurityUtil</h3>
<p>회원 정보 조회하는 요청을 했을때 실행됨 &quot;/api/member/me&quot;
/<em>뱀귤님의 설명인데 이해가 잘 되지 않지만 필요한듯 하여ㅜㅜ\</em>/
//SecurityContext에 회원 정보가 저장되는 시점
//요청이 왔을때 JwtFilter의 doFilter에서 저장</p>
<ul>
<li><code>Long getCurrentMemberId()</code>: SpringContext에서 회원 식별 id(이메일 아님)만 반환<ul>
<li>Principal를 상속 받은 Authentication(인증) 클래스 객체에 SecurityContext가 값을 넣어준다</li>
<li>Principal 멤버 변수인 username에 id가 들어있다</li>
<li>만약 인증 정보가 없어서 authentication 객체가 null이라면 에러 반환</li>
<li>인증 정보가 있다면 <code>authentication.getName()</code>으로 id를 반환해준다</li>
</ul>
</li>
</ul>
<h2 id="🥝-applicationyml">🥝 application.yml</h2>
<p>생략</p>
<h1 id="2-jwt와-security">2. JWT와 Security</h1>
<h2 id="🥝-jwt-설정">🥝 JWT 설정</h2>
<h3 id="🥥-tokenprovider">🥥 TokenProvider</h3>
<p>멤버변수</p>
<ul>
<li><code>AUTHORITIES_KEY = &quot;auth&quot;</code>: 인가 키</li>
<li><code>BEARER_TYPE = &quot;Bearer&quot;</code>: 인증 타입으로 JWT 토큰을 사용한다는 뜻</li>
<li><code>ACCESS_TOKEN_EXPIRE_TIME = 30분</code>: Access Token 유효 시간</li>
<li><code>REFRESH_TOKEN_EXPIRE_TIME = 7일</code>: Refresh Token 유효 시간</li>
<li><code>key</code>: 토큰 만드는 과정 중에서 암호화할때 사용될 비밀 키</li>
</ul>
<p>메서드</p>
<ul>
<li><code>TokenProvider(비밀키)</code>: 생성자<ul>
<li><code>application.yml</code>에 정의해놓은 jwt.secret값으로 암호화 키값 생성</li>
<li><code>@Value(&quot;${jwt.secret}&quot;) String secretKey</code>로 가져오면 된다</li>
</ul>
</li>
</ul>
<pre><code>  jwt:
  secret: c3ByaW5nLWJvb3Qtc2VjdXJpdHktand0LXR1dG9yaWFsLWppd29vbi1zcHJpbmctYm9vdC1zZWN1cml0eS1qd3QtdHV0b3JpYWwK</code></pre><ul>
<li><p><code>TokenDto generateTokenDto(Authentication authentication)</code> :  Authentication으로 회원 정보를 받아 Access&amp;Refresh Token 생성</p>
<ul>
<li><p>Access Token</p>
<ul>
<li>현재 시간+토큰 유효 시간 =&gt; 토큰이 만료될 시간 지정</li>
<li><code>Jwts.builder()</code>로 토큰 생성<ul>
<li>setSubject(회원 식별 id)</li>
<li>claim(권한)</li>
<li>setExpiration(토큰이 만료될 시간)</li>
<li>signWith(비밀키, 암호화 알고리즘 이름)</li>
</ul>
</li>
</ul>
</li>
<li><p>Refresh Token</p>
<ul>
<li><code>Jwts.builder()</code>로 토큰 생성<ul>
<li>setExpiration(토큰이 만료될 시간)</li>
<li>signWith(비밀키, 암호화 알고리즘 이름)</li>
</ul>
</li>
</ul>
</li>
<li><p>리턴(TokenDto)</p>
<ul>
<li>grantType(전달자) 승인타입</li>
<li>accessToken(Access 토큰 객체)</li>
<li>accessTokenExpiresIn(Access 토큰 만료될 시간)</li>
<li>refreshToken(Refresh 토큰 객체)</li>
</ul>
<br>
- `Authentication getAuthentication(Access 토큰)` :  토큰을 복호화해서 정보를 꺼낸다</li>
<li><p><code>accessToken</code>에서 클레임을 가져온다. =&gt; 권한이 있는지 체크해준다</p>
</li>
<li><p>클레임의 권한 정보를 List로 저장한다</p>
</li>
<li><p>UserDetails 객체를 만들어서 클레임의 회원 식별 id, 권한 정보를 저장한다</p>
</li>
<li><p>UsernamePasswordAuthenticationTokene 객체로 return하는데 Spring Security를 사용하기 위한 절차로 받아들이면 된다</p>
</li>
</ul>
<br>

<ul>
<li><code>Claims parseClaims(Access 토큰)</code> : 토큰에서 클레임을 가져와 반환<ul>
<li>만료된 토큰이어도 가져올 수 있게 따로 분리했다</li>
<li>Claims로 반환<ul>
<li>setSigningKey(비밀키)</li>
<li>parseClaimsJws(Access 토큰)</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<ul>
<li><code>boolean validateToken(토큰)</code> :  토큰 정보 검증<ul>
<li>Jwts 클래스로 비밀키를 전달하고 토큰으로 클레임을 만들 수 있다면 true 반환</li>
<li>아니라면 상황에 맞춰 Exception을 발생시킨다 (Jwts 모듈이 알아서 해준다)</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><br><br></p>
<h3 id="🥥-jwtfilter">🥥 JwtFilter</h3>
<h3 id="요청-분석할때-사용할-상수들">요청 분석할때 사용할 상수들</h3>
<ul>
<li><code>AUTHORIZATION_HEADER = &quot;Authorization&quot;</code>: 헤더에 Authorization있는지 확인할때</li>
<li><code>BEARER_PREFIX = &quot;Bearer &quot;</code>: 인증 타입이 Bearer Token인지 확인할때<ul>
<li>요청이 들어올때 Authorization, Bearer Token이 들어온다. 이것은 개발자가 지정해주는 것이 아니라 이미 http에 정해져 있는 것이다</li>
</ul>
</li>
</ul>
<h3 id="메서드">메서드</h3>
<ul>
<li><code>String resolveToken(요청)</code>: 요청 헤더에서 토큰 정보를 꺼내온다<ul>
<li><code>getHeader()</code> 메서드로 토큰 정보를 꺼내온다</li>
<li><code>StringUtils.hasText()</code>로 토큰 null 체크를 하고</li>
<li>Bearer Token이면 &quot;Token&quot; 문자열을 반환한다<br></li>
</ul>
</li>
<li><code>void doFilterInternal(요청, 응답, 필터체인)</code> : 토큰 인증 정보를 SecurityContext에 저장한다<ul>
<li>실제 필터링 로직 수행</li>
<li><ol>
<li><code>resolveToken()</code>로 토큰 정보를 꺼내온다</li>
</ol>
</li>
<li><ol start="2">
<li>TokenProvide 클래스의 <code>validateToken()</code> 메서드로 유효성 검사 </li>
</ol>
<ul>
<li>리마인딩: 비밀키와 토큰으로 클레임을 만들 수 있으면 true 반환하는 메서드</li>
</ul>
</li>
<li>2-1. <code>TokenProvider의 getAuthentication()</code>로 Authentication 얻어오고<ul>
<li>리마인딩: 토큰을 복호화하여 토큰에 있는 정보 꺼내는 메서드</li>
</ul>
</li>
<li>2-2. SpringContext에 Authentication 넣기</li>
<li><ol start="3">
<li><code>doFilter()</code>로 필터링</li>
</ol>
<ul>
<li>필터링: 요청, 응답시 데이터를 가로채 작업하는 것 <a href="https://yhmane.tistory.com/144">필터링 공부한 블로그 링크</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="🥝-spring-security-설정">🥝 Spring Security 설정</h2>
<h3 id="🥥-jwtsecurityconfig">🥥 JwtSecurityConfig</h3>
<p>설명</p>
<ul>
<li>TokenProvider, JwtFilter를 SecurityConfig에 적용할때 사용</li>
<li>추상 클래스인 <code>SecurityConfigurerAdapter&lt;DefaultSecurityFilterChain, HttpSecurity&gt;</code>의 구현체</li>
<li><code>http.addFilterBefore()</code>로 직접 만든 JwtFilter를 Spring Security Filter 앞에 추가한다</li>
</ul>
<p>메서드</p>
<ul>
<li><code>void configure(HttpSecurity http)</code>: TokenProvider를 주입 받아서 JwtFilter를 통해 Security 로직에 필터 등록</li>
</ul>
<p><br><br></p>
<h3 id="⚠️-여기서부터-나오는-에러-처리-클래스는-밑에-나올-securityconfig의-filterchain에서-사용될-예정이다-⚠️">⚠️ 여기서부터 나오는 에러 처리 클래스는 밑에 나올 SecurityConfig의 filterChain에서 사용될 예정이다 ⚠️</h3>
<h3 id="🥥-jwtauthenticationentrypoint-인증-정보-없을때-에러">🥥 JwtAuthenticationEntryPoint 인증 정보 없을때 에러</h3>
<p>인증 정보 없을때, 유효한 자격증명을 제공하지 않고 접근하려 할때 =&gt; 401 에러 반환
(Entry Point: 접근, 입구 지점)</p>
<p>메서드</p>
<ul>
<li><code>void commence(요청, 응답, AuthenticationException)</code>: 매개변수로 에러를 받고, 응답에 HttpServletResponse.SC_UNAUTHORIZED 에러를 추가한다</li>
</ul>
<p><br><br></p>
<h3 id="🥥-jwtaccessdeniedhandler-접근-권한-에러">🥥 JwtAccessDeniedHandler 접근 권한 에러</h3>
<p>인증 정보는 있지만 접근 권한은 없을 때 403 에러 반환</p>
<p>메서드</p>
<ul>
<li><code>void handle(요청, 응답, AccessDeniedException)</code></li>
</ul>
<p><br><br></p>
<h3 id="🥥-securityconfig-스프링-시큐리티-설정">🥥 SecurityConfig 스프링 시큐리티 설정</h3>
<p>직접 만들었던 클래스를 사용한다! 리마인딩해보자</p>
<ul>
<li><code>TokenProvider</code>: 토큰 생성 &amp; 토큰에서 정보 꺼내기</li>
<li><code>JwtAuthenticationEntryPoint</code>: 인증 정보 없을때 오류</li>
<li><code>JwtAccessDeniedHandler</code>: 접근 권한 없을때 오류</li>
</ul>
<p>메서드</p>
<ul>
<li><code>PasswordEncoder passwordEncoder()</code>: 비밀번호 암호화</li>
<li><code>WebSecurityCustomizer webSecurityCustomizer()</code>: DB 관련 API 모두 무시(h2 DB라서)</li>
<li><code>SecurityFilterChain filterChain(HttpSecurity http)</code>: 스프링 시큐리티 필터 체인 실행<ul>
<li><a href="https://velog.io/@younghoondoodoom/Spring-Security%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90%EB%8F%99%EC%9E%91%EA%B3%BC%EC%A0%95%ED%8E%B8">Filter 공부 블로그</a></li>
</ul>
</li>
</ul>
<br>

<p>맨마지막 메서드인 필터체인 메서드를 더 자세하게 알아보자!</p>
<ol>
<li><p>CSRF(사이트 간 요청 위조 공격) 설정 Disable</p>
<pre><code>http.csrf().disable()</code></pre></li>
<li><p>직접 만든 클래스로 exception handling</p>
<pre><code>.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint) //클래스 변수로 가져옴
.accessDeniedHandler(jwtAccessDeniedHandler) </code></pre></li>
<li><p>h2-console을 위한 설정 추가 (아직 이해 잘 안 됨)</p>
<pre><code>.and()
.headers() //응답 헤더
.frameOptions() //커스텀 허용
.sameOrigin() //같은 곳에서 오는 요청들을 허락</code></pre></li>
<li><p>세션 stateless로 설정</p>
<pre><code>.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)</code></pre><p>참고: stateless는 값이 변하지 않는 상태, statefull은 값이 변할 수 있는 상태를 말한다. 토큰은 값이 변하지 않지만 세션은 들어있는 값이 변한다는 것을 떠올리면 된다.</p>
</li>
<li><p>로그인, 회원가입 permitAll 설정</p>
<pre><code>.and()
.authorizeRequests() //인가 요청
.antMatchers(&quot;/auth/**&quot;).permitAll() // &quot;/auth/**&quot; api 요청들은 권한 상관없이 모두 허용
.anyRequest().authenticated()   // 나머지 API 는 전부 인증 필요</code></pre></li>
<li><p>JwtSecurityConfig 클래스 적용</p>
<pre><code>.and()
.apply(new JwtSecurityConfig(tokenProvider));</code></pre><p>리마인딩: 직접 만든 JwtFilter를 Security Fillter 앞에 추가하는 클래스이다</p>
</li>
</ol>
<p><br><br></p>
<h3 id="🥥-securityutill-유저-정보-제공">🥥 SecurityUtill 유저 정보 제공</h3>
<p>SecurityContext에서 유저 정보를 꺼낸다</p>
<p>메서드 </p>
<ul>
<li><code>Long getCurrentMemberId()</code>: 현재 context에 들어있는 회원 id 꺼내기</li>
</ul>
<p><br><br></p>
<h2 id="🥝-refresh-토큰-저장소">🥝 Refresh 토큰 저장소</h2>
<h3 id="🥥-refreshtoken-entity-엔티티">🥥 RefreshToken Entity 엔티티</h3>
<p>보통 토큰이 만료될때 Redis로 자동 삭제 처리를 하는데 RDB로 구현할 때에는 배치(일괄) 작업으로 만료된 토큰들을 삭제해주어야 한다.
이때 생성, 수정 시간 컬럼이 필요하다</p>
<ul>
<li><code>key</code>: 회원 고유 id 값</li>
<li><code>value</code>: Refresh Token</li>
<li>근데 왜 생성/수정 시간 컬럼 추가를 안 하셨지?</li>
</ul>
<br>

<h3 id="🥥-refreshtokenrepository-레퍼지토리">🥥 RefreshTokenRepository 레퍼지토리</h3>
<ul>
<li><code>findByKey()</code>: 회원 ID 값으로 토큰을 가져온다</li>
</ul>
<p><br><br></p>
<h1 id="3-사용자-인증-과정">3. 사용자 인증 과정</h1>
<p>드디어!!!!!ㅠㅠ JWT 관련 설정이 끝이 났다..
이제 로그인 요청이 들어왔을 때 인증 처리 후 JWT 토큰을 발급하는 과정을 살펴보자</p>
<ul>
<li>AuthController</li>
<li>AuthService</li>
<li>CustomUserDetailsService</li>
</ul>
<h2 id="🥝-controller-컨트롤러">🥝 Controller 컨트롤러</h2>
<h3 id="🥥-memberrequestdto">🥥 MemberRequestDto</h3>
<ul>
<li>이메일</li>
<li>패스워드</li>
<li><code>toMember(비밀번호 Encoder)</code>: 이 DTO를 Member Entity로 만들어준다</li>
<li><code>toAuthentication()</code>: 이메일과 패스워드로 UsernamePasswordAuthenticationToken을 만들어준다</li>
</ul>
<br>

<h3 id="🥥-tokenrequestdto">🥥 TokenRequestDto</h3>
<ul>
<li>Access Token</li>
<li>Refresh Token</li>
</ul>
<br>

<h3 id="🥥-authcontroller">🥥 AuthController</h3>
<ul>
<li>회원가입, 로그인, 재발급을 처리한다<ul>
<li>참고: 재발급은 아직 회원이 로그아웃하지 않았는데 Access 토큰 유효 기간이 만료되었을때 다시 토큰을 발급해주는 것이다.</li>
</ul>
</li>
<li>/auth/** 요청은 모두 허용했기 때문에 토큰 검증 로직은 타지 않는다</li>
<li>MemberRequestDto: 로그인 form</li>
<li>TokenRequestDto: AccessToken, RefreshToken</li>
</ul>
<h3 id="1-회원가입">1. 회원가입</h3>
<p>회원가입 form을 받아 authService의 <code>signup()</code> 메서드에 보내준다</p>
<h3 id="2-로그인">2. 로그인</h3>
<p>로그인 form을 받아 authService의 <code>login()</code> 메서드에 보내준다</p>
<h3 id="3-재발급">3. 재발급</h3>
<p>Access&amp;Refresh Token이 있는 DTO를 받아 authService의 <code>reissue()</code> 메서드에 보내준다</p>
<p><br><br></p>
<h2 id="🥝-service-서비스-1">🥝 Service 서비스</h2>
<h3 id="🥥-authservice">🥥 AuthService</h3>
<h3 id="1-signup-회원가입">1. signup() 회원가입</h3>
<p>회원가입 form을 받는다</p>
<ul>
<li>이미 있는 회원인지 확인</li>
<li>DTO의 <code>toMember()</code> 메서드로 비밀번호 암호화해서 저장</li>
<li>생성된 Member를 DTO로 변환해서 응답
*참고: <code>@Transactional</code> 어노테이션
모든 작업들이 성공적으로 완료되어야 결과를 적용하고,
오류가 발생했다면 모든 것을 되돌려준다
DB의 Transaction 개념이다~!!</li>
</ul>
<br>

<h3 id="2-login-로그인">2. login() 로그인</h3>
<p>로그인 form을 받는다</p>
<p>로직 순서</p>
<ol>
<li><p>아직 인증 받지 못한 ID와 PW로 UsernamePasswordAuthenticationToken(토큰)을 생성한다</p>
</li>
<li><p>AuthenticationManager의 authenticate() 메서드로 검증을 받는다
2-1. Authentication 객체에 회원 정보를 넣는다
2-2. 회원 식별 id가 이때 이메일, 비밀번호와 함께 들어간다</p>
</li>
<li><p>인증된 정보로 JWT 토큰을 생성한다
3-1. TokenProvider의 <code>generateTokenDto()</code> 메서드
3-2. Access Token, Refresh Token 모두 새로 생성한다</p>
</li>
<li><p>Refresh Token을 DB에 저장한다</p>
</li>
<li><p>권한, Access 토큰, Refresh 토큰, Access 토큰 만료 시간을 반환한다
5-1. TokenDto를 반환한다</p>
</li>
</ol>
<br>

<h3 id="3-reissues-토큰-재발급">3. reissues() 토큰 재발급</h3>
<p>DTO로 Access Token, Refresh Token을 받는다</p>
<p>로직 순서</p>
<ol>
<li><p>Refresh Token이 만료되었는지 검증한다
1-1. 유효하다면 다음 단계!</p>
</li>
<li><p>Access Token에서 회원 식별 id를 가져온다
2-1. TokenProvider의 getAuthentication() 메서드 - 여기서 토큰 복호화해줌</p>
</li>
<li><p>2번에서 가져온 회원 id로 저장소의 Refresh Token 값을 가져온다</p>
</li>
<li><p>DTO로 받은 Refresh Token과 저장소에 있던 Refresh Token이 일치하는지 검사한다
4-1. 일치한다면 다음 단계!</p>
</li>
<li><p>Access 토큰, Refresh 토큰 모두 새로 생성해준다</p>
</li>
<li><p>Refresh 토큰의 재사용을 막기 위해 저장소의 Refresh Token을 업데이트해준다.</p>
</li>
<li><p>권한, Access 토큰, Refresh 토큰, Access 토큰 만료 시간을 반환한다
7-1. TokenDto를 반환한다</p>
</li>
</ol>
<p><br><br></p>
<h3 id="🥥-customuserdetailsservice">🥥 CustomUserDetailsService</h3>
<p>UserDetails와 Authentication의 비밀번호를 비교하고 검증한다</p>
<p>메서드</p>
<ul>
<li><code>UserDetails createUserDetails(Member 엔티티)</code>: Member Entity를 UserDetails로 만든다<ul>
<li>User 객체를 만들어서 반환하면 된다</li>
</ul>
</li>
<li><code>UserDetails loadUserByUsername(이메일)</code>: 이메일 받아서 UserDetails 반환<ul>
<li>email로 회원을 찾고 <code>createUserDetails()</code>로 UserDetails 객체로 만들어준다</li>
</ul>
</li>
</ul>
<p><br><br></p>
<h1 id="회고">회고</h1>
<p>지금 스프링 부트로 쇼핑몰 프로젝트를 구현하고 있는데 Spring Security를 써봐야겠다 싶어서 구글링하게 되었다. 그러던중 괜찮은 예제 코드를 찾게 되었는데 알고보니 Spring Security에서 한 단계 더 나아간.. JWT+Spring Security였다ㅋㅋㅋㅋㅋㅋ (나는 Spring Security와 초면이었는데 말이다!!!!!) 그치만 JWT 공부하는게 너무 재밌었어서 끝까지 재밌게 끝냈다~~ </p>
<p>내가 직접 다 구현했으면 정말 좋았겠지만 프로젝트 마감기한 때문에ㅠㅠㅠ 아쉽게 다른 분의 로직으로 개발을 진행해야겠다.. 그래서 최대한 많이 이해하려고 많이 노력했당......ㅜㅜ 내가 이해하려고 정리한 글이라서 다른 분들께 도움이 될지는 모르겠지만....ㅎㅎㅎㅎㅎㅎㅎㅎ 그래도 짱 수고많았다! 이 글 보고있는 분들도 모두모두 화이팅!!!</p>
<p>피드백이나 틀린 내용 있다면 언제든지 댓글 남겨주세요</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[세션 vs 토큰 JWT란?]]></title>
            <link>https://velog.io/@qiwisil_227/%EC%84%B8%EC%85%98-vs-%ED%86%A0%ED%81%B0-JWT%EB%9E%80</link>
            <guid>https://velog.io/@qiwisil_227/%EC%84%B8%EC%85%98-vs-%ED%86%A0%ED%81%B0-JWT%EB%9E%80</guid>
            <pubDate>Wed, 31 May 2023 03:18:29 GMT</pubDate>
            <description><![CDATA[<p>로그인 기능으로 설명</p>
<h2 id="🥝-그림-요약">🥝 그림 요약</h2>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/0ae13780-153d-463f-a3aa-096daf8756b6/image.png" alt=""></p>
<h2 id="🥝-배경">🥝 배경</h2>
<ul>
<li>그동안 form으로 아이디, 비밀번호를 받자마자 DB에 저장해왔다면, 멈추어야 한다!</li>
<li>DB가 유출되면 모든 회원 정보가 유출되는 것이기 때문이다</li>
</ul>
<h2 id="🥝-기본-지식">🥝 기본 지식</h2>
<h3 id="🥥-인증authentication-로그인">🥥 인증(Authentication): 로그인</h3>
<ul>
<li>특정 권한이 주어진 사용자임을 id, pw로 인증 받는 거다</li>
<li><strong>=&gt; 사용자가 자기 계정을 사용하려고 할때 로그인을 시킴</strong></li>
</ul>
<h3 id="🥥-인가authorization-인증-유지">🥥 인가(Authorization): 인증 유지</h3>
<ul>
<li>내 계정으로&#39;만&#39; 할 수 있는 기능을 이용할때</li>
<li>로그인 되어있음을 알아보고 허가해주는 것이다</li>
<li>JWT는 인가에 더 가까운 기술이다</li>
<li><strong>=&gt; 인증을 받은 사용자가 서비스 안에서 돌아다닐때 서버에서 로그인한 사용자인 것을 알아보고 허가해주는 것</strong></li>
</ul>
<p>stateless: 시간에 따라 바뀌지 않는 상태값(비밀 키)을 갖는 것 =&gt; JWT
stateful: 시간에 따라 바뀌는 상태값(세션 id)을 갖는 것 =&gt; 세션</p>
<h2 id="🥝-서버는-어떻게-요청을-받을때마다-사용자의-로그인-여부를-확인할-수-있을까">🥝 서버는 어떻게 요청을 받을때마다 사용자의 로그인 여부를 확인할 수 있을까?</h2>
<h3 id="🥥-1-세션-아이디에-저장">🥥 1. 세션 아이디에 저장</h3>
<p>(세션 개념은 잡혀있기 때문에 따로 정리하지 않음)</p>
<ul>
<li>문제점<ul>
<li>서버 여러 개를 사용할때</li>
<li>에러 때문에 메모리가 모두 날라가면 세션 정보도 사라짐</li>
</ul>
</li>
</ul>
<h3 id="🥥-2-jwtjson-web-token방식">🥥 2. JWT(Json Web Token)방식</h3>
<ul>
<li><p>토큰 방식이란?</p>
<ul>
<li>사용자가 로그인을 하면 토큰을 생성해서 사용자에게 건네준다</li>
<li>토큰만 주고 서버는 기억하지 않는다</li>
</ul>
</li>
<li><p>토큰이란?</p>
<ul>
<li>알파벳, 숫자가 아무렇게 섞인 백 몇십 글자</li>
</ul>
</li>
</ul>
<h2 id="🥝-jwt">🥝 JWT</h2>
<pre><code>- ex) `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c`
- 인코딩 or 암호화된 3가지 데이터(header, payload, verify signature)를 이어붙인 형태
  - 마침표로 끊어져서 세 부분으로 나뉜다</code></pre><h3 id="🥥-페이로드">🥥. 페이로드</h3>
<p>(header, <strong>payload</strong>, verify signature)</p>
<ul>
<li><p>Base64 디코딩 =&gt; JSON</p>
<ul>
<li>누가 누구에게 발급했는지</li>
<li>토큰이 언제까지 유효한지</li>
<li>서비스가 사용자에게 이 토큰으로 공개하기 원하는 내용</li>
</ul>
</li>
<li><p><strong>Claim</strong>: 토큰에 담긴 사용자 정보 등의 데이터</p>
</li>
<li><p>로그인 이후 요청마다 사용자가 서버한테 보내준다</p>
</li>
<li><p>그런데 Base64로만 디코딩이 되어있으면 사용자가 악용할 소지가 있지 않나요? =&gt; 헤더와 서명에서 암호화함</p>
</li>
</ul>
<h3 id="🥥-헤더">🥥. 헤더</h3>
<p>(<strong>header</strong>, payload, verify signature)
담겨있는 정보</p>
<ul>
<li><strong>type</strong>(토큰의 타입): 언제나 JWT</li>
<li><strong>alg</strong>(알고리즘): 3번 서명값을 만드는데 사용될 암호화 알고리즘</li>
</ul>
<h3 id="🥥-서명">🥥. 서명</h3>
<p>(header, payload, verify <strong>signature</strong>)
페이로드와 헤더, &#39;서버에 감춰놓은 비밀 값&#39;을 암호화하면 <strong>서명값</strong>이 나온다</p>
<h2 id="🥝-프로세스">🥝 프로세스</h2>
<ol>
<li>요청 시 토큰 값이 서버로 오면</li>
<li>헤더와 페이로드를 &#39;서버의 비밀 키&#39;로 암호화한 값이</li>
<li>서명 값과 일치하는지 확인한다
3-1. 만약 서버가 아닌 다른 곳에서 페이로드가 조금이라도 수정된다면 일치하지 않는다 =&gt; 정보 조작한 사용자, 해커로 간주</li>
<li>서명 값과 일치하고 유효기간도 지나지 않았다면 로그인 된 회원으로 인가를 받는다</li>
<li>서버는 사용자의 상태를 저장할 필요 없이 들어오는 토큰만 인식하면 된다</li>
</ol>
<h2 id="🥝-그렇다면-세션보다-jwt가-완전히-우월한가요">🥝 그렇다면 세션보다 JWT가 완전히 우월한가요?</h2>
<h3 id="🥥-정답-x">🥥 정답: X</h3>
<p>세션을 대체하기에는 JWT에 결점이 있다
세션(stateful)처럼 모든 사용자들의 상태를 기억하고 있다는 건 구현하기 부담되고 고려사항도 많지만,
이것이 되기만 하면 기억하는 대상의 상태들을 언제든 제어할 수 있다는 의미이기 때문이다
=&gt; 세션은 서버에서 관리하기 때문에 필요시 삭제시킬 수 있다
=&gt; 토큰은 이미 줘버렸기 때문에 관리할 수 없다</p>
<h3 id="🥥-이러한-jwt의-단점-보완">🥥 이러한 JWT의 단점 보완</h3>
<ol>
<li>토큰의 유효 기간을 짧게 2개를 준다</li>
<li>access토큰, refresh(1~2주) 토큰 발급</li>
<li>refresh 토큰은 상응값을 DB에도 저장한다</li>
<li>클라이언트는 access 토큰 수명이 다 하면 refresh 토큰을 보낸다</li>
<li>서버는 이것을 DB와 비교해서 맞다면 새 access 토큰을 발급해준다<br>

</li>
</ol>
<h2 id="🥝-한줄두줄-총정리">🥝 한줄..두줄... 총정리</h2>
<p>JWT는 <strong>session과 같은 선상에 있는 token의 한 종류</strong>이다
token에는 사용자 정보, 암호화 기법 정보 등이 담겨있고 <strong>서버가 아닌 사용자가 가지고 있다</strong>
사용자: 로그인한 상태에서만 사용할 수 있는 요청을 할때마다 <strong>서버에 token을 전달</strong>
서버: <strong>자신만 아는 비밀키</strong>로 token을 분석하여 인가를 준다</p>
<p><a href="https://www.youtube.com/watch?v=1QiOXWEbqYQ">공부한 유튜브 영상 링크</a>
ㄴ 영상 너무 좋습니다....ㅠㅠ 추천!!!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AI 미션클리어] 6차시 – 영상처리와 인공지능]]></title>
            <link>https://velog.io/@qiwisil_227/AI-%EB%AF%B8%EC%85%98%ED%81%B4%EB%A6%AC%EC%96%B4-6%EC%B0%A8%EC%8B%9C-%EC%98%81%EC%83%81%EC%B2%98%EB%A6%AC%EC%99%80-%EC%9D%B8%EA%B3%B5%EC%A7%80%EB%8A%A5</link>
            <guid>https://velog.io/@qiwisil_227/AI-%EB%AF%B8%EC%85%98%ED%81%B4%EB%A6%AC%EC%96%B4-6%EC%B0%A8%EC%8B%9C-%EC%98%81%EC%83%81%EC%B2%98%EB%A6%AC%EC%99%80-%EC%9D%B8%EA%B3%B5%EC%A7%80%EB%8A%A5</guid>
            <pubDate>Tue, 10 May 2022 00:17:44 GMT</pubDate>
            <description><![CDATA[<h1 id="영상-인식이란">영상 인식이란?</h1>
<p>영상(이미지 데이터)를 가지고 이미지 처리를 하거나 인공지능을 거쳐서 영상에 포함된 의미를 분석, 해석해 내는 과정</p>
<h1 id="영상인식-작업의-종류-5가지">영상인식 작업의 종류 5가지</h1>
<p>컴퓨터를 사용해서 영상을 처리(이미지 처리)를 연구하는 분야인 컴퓨터 비전이라는 큰 범주 안에 속해 있다</p>
<p>1.객체 탐지: 어떠한 객체, 즉 오브젝트가 존재하는지에 대한 유무를 판단
    ㄴ알고리즘: 하고자 하는 객체의 특징을 사전에 추출하고 주어진 영상에서 해당 특징을 검출해내는 접근을 사용
    ex) 빨간색 토마토를 검출해내고 싶을때, 빨갛고 동글다는 특징을 사전에 입력해둔다. 컴퓨터는 자기가 구분해낸 것이 빨간 토마토라는 것을 모른다. 그냥 특징에 맞춰서 객체를 구분해낸 것일뿐이다
    =&gt;정해진 특징에 맞는 &#39;어떤 객체&#39;이다</p>
<p>2.객체 인식: 그 객체의 자체가 무엇인지 인식시키는 개념
    ㄴ객체 탐지가 선행되어야함
    ㄴ라벨링과 비슷한 개념. 탐지된 객체의 라벨링</p>
<p>3.객체 추적: 동영상에서 발생. 탐지된 객체, 인식된 객체를 매 프레임마다 확인하면서 그 객체가 어떻게 움직이는지 그 경로는 어떠한지 등을 파악하며 추적하는 것이 바로 객체 추적이다.
    ㄴ객체 탐지, 객체 인식이 선행되어야함</p>
<p>4.객체 분류: 탐지되고, 인식된 객체들을 정해진 카테고리로 분류하는 작업</p>
<p>5.객체 영역 분할: 단순히 분류에 그치지 않고 인식된 대상을 픽셀단위로 정확히 분할하고 나누어내는 것
    ex) 사람과 자전거 구분</p>
<h1 id="객체의-특징">객체의 특징</h1>
<p>영상에 인식이 되려면 먼저 객체의 특징을 사전에 정의하고 추출해야한다</p>
<p>&lt;특징&gt;
사전적 의미: 다른 것에 비하여 특별히 눈에 띄는 점
수치적 의미: 주변과 다르게 숫자가 변하는, 수치적 느낌이 있는 것</p>
<p>영상도 픽셀에 어떠한 숫자값들로 구성되니 데이터로 이루어져 있다.
이에 대한 특징을 구한다는 것은 숫자값들이 확 변하거나 내가 검출한 점(dot)이나 픽셀들의 간격이 확 벌어진다는 등이다.</p>
<h1 id="4가지-처리방법-알고리즘">4가지 처리방법 알고리즘</h1>
<p>특징을 잡기 쉽게 해주는 전처리 또는 처리 과정으로 네 가지의 처리방법 알고리즘이 존재한다.</p>
<p>1.화소점 처리: 원 화소의 값이나 위치를 바탕으로 단일 화소 값을 변경하는 기술
ㄴ화소 점, 픽셀의 원래 값이나 위치를 기준으로 그 픽셀 안의 값을 변경하면서 이루어지는 처리로 산술 연산과 논리 연산의 방법이 있다
ex)픽셀 값이 127 이상이면 255, 이하면 0으로 처리해버리는 간단한 연산</p>
<p>2.영역 처리: 픽셀의 원래 값과 이웃하는 여러 픽셀이 서로 관계하여 생성된 픽셀 값을 기준으로 값이 변경되는 것
-블러링: 영상의 세세한 부분을 제거해서 흐릿하게 만드는 것
-샤프링: 주변 픽셀에 대비해서 값이 크게 변하는 경계 부분이 돋보이게 하는 기법</p>
<p>3.기하학 처리: 디지털 영상 픽셀의 위치나 픽셀의 모임인 배열을 변화시키는 방법
ㄴ스케일 변화 즉, 영상크기를 비례적으로 줄이거나 확대하여 변화를 주거나, 영상 자체를 회전시키거나 이동시키는 방법을 말한다</p>
<p>4.프레임 처리: 두 가지 이상의 서로 다른 영상으로 각종 연산을 조합하여 새로운 픽셀값을 생성</p>
<h1 id="프레임-처리의-예시">프레임 처리의 예시</h1>
<p>전제: 고정된 카메라로부터 영상 데이터를 얻는다
&lt;&lt;시간이 지나도 바뀌는 것, 바뀌지 않는 것&gt;&gt;</p>
<p>지금 영상 데이터와 1초 전 영상 데이터를 받았다고 가정
(지금) - (1초 전)을 했을때 모두 0이 나오면 둘의 영상이 똑같아 변함이 없다는 뜻이다.
0이 아닌 다른 숫자가 나오면 그 부분에 물체의 어떠한 변화가 있었다는 뜻이다. 그 다른 숫자를 모두 1로 처리하게 되면 흑백 영상이 되는 것이다.</p>
<h1 id="발전된-영상인식-인공지능">발전된 영상인식 인공지능</h1>
<p>최근에 컴퓨터 성능이 많이 발전하고 인공지능기법이 많이 발전을 하면서 영상인식 부분에서 인공지능이 더 우수한 성능을 보이고 있다.</p>
<p>일반적인 물체에 대한 인식률에 대한 대회(ImageNet)에 대한 대회 결과
2010<del>11년 : 75%
2013년</del>     : 85%</p>
<p>딥러닝을 적용하기 시작한 2014년으로 접어들면서 전통적인 컴퓨터 비전 방법보다 딥러닝으로 95%이상의 성능을 보이게 됐다.</p>
<p>최근에는 98%의 성능을 보이게 됐는데 사람의 인식률은 98.5%인 것을 보면 거의 사람 정도나 사람을 뛰어넘을 정도의 인식률이 된 것이다.</p>
<h1 id="인공지능-학습과정-5단계">인공지능 학습과정 5단계</h1>
<p>1.데이터 수집
2.전처리
3.특징추출
--------↑트레이닝 데이터 셋을 모으는 것↑-------
4.학습
5.검증
--------↑학습시키고 추론 결과를 살피는 것↑-------</p>
<p>그냥 날 것의 영상 데이터가 아니라 전처리가 되어 있는 특징이 잘 나타나 있는 사진을 학습시켜 동일한 시간에 성능과 효율을 높여준다</p>
<p>최대한 비슷한 포맷을 만들거나 윤곽을 더 또렷하게 보이게 하거나 특징점을 잡아주는 등 전처리 및 특징 추출을 해줘야한다. 그렇지 않고 그냥 날 것의 데이터를 사용하면 효율이 떨어져 좋은 결과를 얻기 힘들어진다.</p>
<p>사람이 어떻게 학습데이터를 만들어 주는지에 따라 인공지능의 효율은 달라진다. 또 인공지능은 이런 데이터와 학습 없이는 우수해질 수 없다.</p>
<p>경우에 따라 인공지능이 우세한 경우가 있고 전통적인 방식의 영상인식이 우수한 경우도 있다. 이론적으로 무한한 데이터, 무한한 시간이 있는 상황이라면 인공지능이 우수하겠지만 현실에서는 그 돈을 투자할 산업과 소비자가 각기 다르기 때문에 효율에 따라 어떤 방식을 사용할지 선택해야한다.</p>
<p>인공지능을 사용한다는 것이 그냥 컴퓨터한테 막 학습을 시키는 것이 아니라 어느 정도 전통적인 방법을 이해하고 효율적인 부분들만 채택해서 어떻게 전처리 및 특징 추출을 해주느냐에 따라 인공지능의 성능이 결정된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AI 미션클리어] 5차시- 이미지 데이터의 이해 (pixel/frame/resolution)]]></title>
            <link>https://velog.io/@qiwisil_227/AI-%EB%AF%B8%EC%85%98%ED%81%B4%EB%A6%AC%EC%96%B4-5%EC%B0%A8%EC%8B%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%9D%98-%EC%9D%B4%ED%95%B4-pixelframeresolution</link>
            <guid>https://velog.io/@qiwisil_227/AI-%EB%AF%B8%EC%85%98%ED%81%B4%EB%A6%AC%EC%96%B4-5%EC%B0%A8%EC%8B%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%9D%98-%EC%9D%B4%ED%95%B4-pixelframeresolution</guid>
            <pubDate>Tue, 10 May 2022 00:17:18 GMT</pubDate>
            <description><![CDATA[<h1 id="이미지-데이터란">이미지 데이터란?</h1>
<p>오감 중 시각에 대한 의존도가 매우 높다. 정보 80% 이상이 시각을 통해 얻어진다.</p>
<h1 id="영상처리란">영상처리란</h1>
<p>카메라는 시각적 정보를 기록하기 위해 만들어진 것이다. 기록된 시각적 정보를 통해 그 정보를 해석하고 판단하여 정보를 처리하는 것을 바로 영상처리(Image processing)라고 한다.</p>
<p>근데 왜 영상을 이미지라고 하는 것일까? 사전적 의미의 영상 또는 이미지는 2차원 평면 위에 그려진 시각적 표현들이기 때문이다. 원래 영상은 정지된 영상, 움직이는 영상으로 나뉘어지는데 요즘 영상을 그냥 동영상으로만 생각하고 있기 때문이다. 정지된 영상은 사진, 움직이는 영상은 동영상으로 영상은 사진과 동영상을 표현하는 단어이다. </p>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/db1937c9-74f3-42df-b849-3a781e4a2693/image.png" alt="">
실제시간이 연속적으로 흐를 때 일정한 시간 간격을 시간의 변화량, 즉 델타t라는 간격으로 영상이 한 장씩 있는 상황이다. 어떻게 보면 눈을 델타t마다 뜨는 상황이라고 볼 수 있는 것이다.</p>
<h1 id="델타-t-비교하기">델타 t 비교하기</h1>
<p>델타 t가 클 때 : 영상이 띄엄띄엄 발생
ㄴ눈을 감고 있는 시간이 길다. =&gt; 실제 상황에서 놓치는 일이 많아짐</p>
<p>작을 때 : 영상이 촘촘이 발생
ㄴ현실에 가깝게 볼 수 있음</p>
<p>델타 t가 0으로 가까워질수록 실제와 거의 비슷한 동영상이 되는 것이다.</p>
<p>컴퓨터는 델타 t가 무조건 발생한다.</p>
<h1 id="fps의-개념-이해하기">fps의 개념 이해하기</h1>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/70e19497-6afb-4599-a69c-7a4981d8587c/image.png" alt="">
영상으로 만들어지는 동영상에서는 이러한 영상을 frame
1초에 나오는 영상 프레임 수를 f/s, fps라고 한다
ex)10fps라고 하면 1초에 열 개의 프레임이 있는 것. 델타 t가 1초 나누기 10프레임해서 0.1초 즉 100ms가 된다 그 다음 100fps라고 하면 1초에 100개의 프레임, 즉 델타 t가 0.01초 즉, 10ms인 상황이다</p>
<p>30ms인 컴퓨터와 1000ms인 컴퓨터가 있다고 했을때 1초안에서 이미 30배 이상의 정보량의 차이가 있다
<img src="https://velog.velcdn.com/images/qiwisil_227/post/ee348246-ee81-4979-b452-ca9fb33b1960/image.png" alt="">
흔히 컴퓨터상에서 랙이 걸린다라는 표현이 사용되기도 한다.</p>
<h1 id="픽셀의-개념">픽셀의 개념</h1>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/f91764be-a7c9-423c-9673-ef3b917e4d02/image.png" alt="">
우리는 3차원에 살고 있고 영상은 2차원이다. 영상은 수학적으로 2차원 함수는 f(x,y)로 정의될 수 있고 (x,y)는 어떠한 지점 좌표이고 f는 그 지점의 밝기 정보가 되는 것이다. 이런 영상을 디지털 영상이라한다.</p>
<p>디지털 영상은 유한한 개수의 점들로 구성되어 있다.
이러한 점을 화소, 픽셀이라고 부른다.</p>
<p>픽셀을 더 작게 나눌수록 실제의 형상에 가까운 영상이 된다.
<img src="https://velog.velcdn.com/images/qiwisil_227/post/13b8342b-775d-46b4-93f1-46a1683e330f/image.png" alt="">
해상도(resolution)는 픽셀의 수에 따라서 실제에 가까운 정도를 뜻한다.</p>
<h1 id="밝기정보-색의-3원색과-빛의-3원색">밝기정보 (색의 3원색과 빛의 3원색)</h1>
<p><img src="https://velog.velcdn.com/images/qiwisil_227/post/39381feb-f0cc-453c-986e-ee0930f1d0ae/image.png" alt="">
-색의 3원색
시안 마젠타 옐로우
=&gt;인쇄</p>
<p>-빛의 3원색
빨강 초록 파랑 (RGB)
세 가지의 주요한 색을 조합하여 여러 가지 색을 만들어내는 것이다.
=&gt; 화면</p>
<h1 id="색상모델">색상모델</h1>
<p>이런 원리로 픽셀에서 뿜어내는 빛들의 합성을 통해 컬러 모니터나 티비 등의 디스플레이 색상을 보게 된다.
픽셀의 밝기 정보에는 RGB 정보가 담겨있다. 한 개의 픽셀에 세 가지의 정보가 담겨있는 건 세가지가 겹쳐 있다는 것인데 이것은 3차원 개념이다. 하지만 디스플레이 화면에 영상을 구현하는 것은 2차원이라서 동일 위치에 겹칠 수가 없어서 픽셀을 또 3등분한다. RGB LED 소자로 구성하는 것이다.
어떠한 영상은 그 영상의 픽셀 수만큼 RGB 정보가 각각 있다는 것이다.</p>
<p>ex) 64X64, 총 4096개의 픽셀을 가진 영상은 픽세람다 RGB 각각 3가지 정보를 가지고 있기 때문에 총 정보, 즉 데이터의 수는 4096X3=12288(픽셀)개가 되는 것이다. </p>
<p>RGB 각각을 채널이라고 부른다. 그래ㅓㅅ 영상은 R채널 G채널 B채널 총 3개의 채널이 겹쳐져서 만들어진다. RGB는 색이 많으면 255, 없으면 0으로 0부터 255 사이의 숫자들로 그 색이 얼만큼 있는지 나타낸다.</p>
<p>흑백 사진은 RGB가 아니라 흰색이 얼만큼 존재하냐의 문제로 보면 된다.
이를 Gray 채널로 된 영상이라고 하는데 Gray 채널. 한 개의 채널만 존재한다.</p>
<p>흰색이 많으면 255(흰색), 없으면 0(검정)이다.
이러한 영생의 색을 나타내는 옵션을 색상 모델이라고 한다.</p>
<p>RGB모델, Gray 모델 둘 다 아주 기본적인 모델이다.</p>
<h1 id="hsv-모델">HSV 모델</h1>
<p>HSV라는 3채널의 색상 모델도 중요
<img src="https://velog.velcdn.com/images/qiwisil_227/post/0cfe127c-4dee-4c6f-b0d8-19fbc585872c/image.png" alt=""></p>
<p>H 색상
S 채도
V 값/명도
사람이 색상을 인식하는 방식과 비슷한 방식으로 정의된 모델</p>
<p>RGB처럼 색상의 조합이 아니라 어떤 색상을 지정하고 채도 명도의 조합으로 표현되는 것이다. 그러므로 특정 색을 정의 내리기가 훨씬 쉽고 실제에 가깝다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AI 미션클리어] 4차시 – 코딩을 몰라도 개발이 가능하다? 간단한 노코드의 세계]]></title>
            <link>https://velog.io/@qiwisil_227/AI-%EB%AF%B8%EC%85%98%ED%81%B4%EB%A6%AC%EC%96%B4-4%EC%B0%A8%EC%8B%9C-%EC%BD%94%EB%94%A9%EC%9D%84-%EB%AA%B0%EB%9D%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C%EC%9D%B4-%EA%B0%80%EB%8A%A5%ED%95%98%EB%8B%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EB%85%B8%EC%BD%94%EB%93%9C%EC%9D%98-%EC%84%B8%EA%B3%84</link>
            <guid>https://velog.io/@qiwisil_227/AI-%EB%AF%B8%EC%85%98%ED%81%B4%EB%A6%AC%EC%96%B4-4%EC%B0%A8%EC%8B%9C-%EC%BD%94%EB%94%A9%EC%9D%84-%EB%AA%B0%EB%9D%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C%EC%9D%B4-%EA%B0%80%EB%8A%A5%ED%95%98%EB%8B%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EB%85%B8%EC%BD%94%EB%93%9C%EC%9D%98-%EC%84%B8%EA%B3%84</guid>
            <pubDate>Tue, 19 Apr 2022 01:41:45 GMT</pubDate>
            <description><![CDATA[<h1 id="1시민-개발자란">1.시민 개발자란?</h1>
<p>코딩을 깊게 배우고 뛰어난 전문성을 갖춘 전문 개발자는 아니지만 코딩지식이 전무하거나 조금밖에 없는 상태로 웹이나 앱 심지어는 인공지능까지도 개발하는 사람</p>
<p>개발을 전공하지 않았지만 이미 완성되어 있는 개발 플랫폼을 이용해 프로그램 개발을 한다</p>
<h1 id="2네가지-유형의-비즈니스-개발자">2.네가지 유형의 비즈니스 개발자</h1>
<p>가트너의 비즈니스 개발자
1.노 코드 : 완성형 노 코드 툴을 사용하는 시민 개발자
2.로우 코드 : 로우 코드를 사용하는 파워 유저 시민 개발자
3.로우/프로 코드 : 비지니스 주도 전문 개발자
4.프로 코드 : IT기업 전문 개발자</p>
<h1 id="3노코드와-로우코드의-차이점">3.노코드와 로우코드의 차이점</h1>
<p>노 코드 툴 : 사용자가 코딩을 생략하는 틀
ex)블록 코딩</p>
<p>로우 코드 : 코딩이 필요하기는 하나 그 비중이 매우 낮은 틀
ㄴ코딩 지식을 약간이라도 가지고 있어야 함</p>
<p>앱이나 웹개발, 증강현실, 가상현실, 인공지능 콘텐츠도 아주 적은 코딩만으로 구현 가능</p>
<h1 id="4시민개발자의-필요성">4.시민개발자의 필요성</h1>
<p>기술이 나날이 발전하는 만큼 개발업무는 급증하는데 전문 개발 인력은 한정되어 있기 때문</p>
<p>1)전문 개발자가 아닌 사람들이 노코드 툴로 간단한 개발을 할 수 있게 되면 개발에 소모되는 시간과 비용이 절감됨</p>
<p>2)비즈니스 전문가가 직접 개발을 진행하면 전문 개발자보다 기획 의도에 부합하는 결과물을 만들 수 있음</p>
<p>3)개발 직군과 비개발 직군 사이의 부서 간 소통 오류 최소화, 불필요한 커뮤니케이션 단축</p>
<h1 id="5노코드-플랫폼-개발사례">5.노코드 플랫폼, 개발사례</h1>
<p>1.노코드 플랫폼
1)데브온 NCD
-서버 로직, 프레임워크, 화면개발 가능
-Flow chart로 작성</p>
<p>2)칼스 플라츠
-서비스용 소프트웨어 솔루션 제공
-응용 소프트웨어 개발에 필요한 모든 요소를 구현</p>
<p>3)클로바 스튜디오</p>
<p>2.개발사례
1)웹사이트 &#39;블라인드하이어&#39;
2)챗봇 빌더 서비스 &#39;단비챗봇&#39;</p>
]]></description>
        </item>
    </channel>
</rss>