<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>..</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 04 May 2023 19:42:50 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. ... All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/boost_dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[MySQL) 기초]]></title>
            <link>https://velog.io/@boost_dev/MySQL-SELECT</link>
            <guid>https://velog.io/@boost_dev/MySQL-SELECT</guid>
            <pubDate>Thu, 04 May 2023 19:42:50 GMT</pubDate>
            <description><![CDATA[<h1 id="1-select">1. SELECT</h1>
<h2 id="11-행열-조회">1.1 행/열 조회</h2>
<p><code>SELECT 컬럼명 FROM 테이블명;</code></p>
<ul>
<li>행 조회는 <code>WHERE 조건;</code><blockquote>
<p><code>*</code>는 해당 테이블의 모든 컬럼명을 나타낸다.</p>
</blockquote>
</li>
</ul>
<h2 id="12-주석">1.2 주석</h2>
<p><code>--</code></p>
<h2 id="13-원하는-순서로-조회">1.3 원하는 순서로 조회</h2>
<p><code>ORDER BY</code></p>
<table>
<thead>
<tr>
<th>오름차순</th>
<th>내림차순</th>
</tr>
</thead>
<tbody><tr>
<td>ASC</td>
<td>DESC</td>
</tr>
</tbody></table>
<h2 id="14-원하는-만큼-데이터-조회">1.4 원하는 만큼 데이터 조회</h2>
<p><code>LIMIT {가져올 갯수}</code>나 <code>LIMIT {건너뛸 갯수}, {가져올 갯수}</code></p>
<h2 id="15-컬럼명-변경">1.5 컬럼명 변경</h2>
<p><code>원래 컬럼명 AS 변경할 컬럼명</code></p>
<hr>
<h1 id="2-연산자">2. 연산자</h1>
<table>
<thead>
<tr>
<th>연산자</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>+, -, *, /</td>
<td>각각 더하기, 빼기, 곱하기, 나누기</td>
</tr>
<tr>
<td>%, MOD</td>
<td>나머지</td>
</tr>
<tr>
<td>IS</td>
<td>양쪽이 모두 TRUE 또는 FALSE</td>
</tr>
<tr>
<td>IS NOT</td>
<td>한쪽은 TRUE, 한쪽은 FALSE</td>
</tr>
<tr>
<td>AND, &amp;&amp;</td>
<td>양쪽이 모두 TRUE일 때만 TRUE</td>
</tr>
<tr>
<td>OR,</td>
<td></td>
</tr>
<tr>
<td>=</td>
<td>양쪽 값이 같음</td>
</tr>
<tr>
<td>!=, &lt;&gt;</td>
<td>양쪽 값이 다름</td>
</tr>
<tr>
<td>&gt;, &lt;</td>
<td>(왼쪽, 오른쪽) 값이 더 큼</td>
</tr>
<tr>
<td>&gt;=, &lt;=</td>
<td>(왼쪽, 오른쪽) 값이 같거나 더 큼</td>
</tr>
<tr>
<td>BETWEEN {MIN} AND {MAX}</td>
<td>두 값 사이에 있음</td>
</tr>
<tr>
<td>NOT BETWEEN {MIN} AND {MAX}</td>
<td>두 값 사이가 아닌 곳에 있음</td>
</tr>
<tr>
<td>IN (...)</td>
<td>괄호 안의 값들 가운데 있음</td>
</tr>
<tr>
<td>NOT IN (...)</td>
<td>괄호 안의 값들 가운데 없음</td>
</tr>
<tr>
<td>LIKE &#39;... % ...&#39;</td>
<td>0~N개 문자를 가진 패턴</td>
</tr>
<tr>
<td>LIKE &#39;... _ ...&#39;</td>
<td>_ 갯수만큼의 문자를 가진 패턴</td>
</tr>
</tbody></table>
<hr>
<p>출처
(얄팍한 코딩사전)[<a href="https://www.youtube.com/watch?v=dgpBXNa9vJc%5D">https://www.youtube.com/watch?v=dgpBXNa9vJc]</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준) 10171: 고양이]]></title>
            <link>https://velog.io/@boost_dev/%EB%B0%B1%EC%A4%80-10171-%EA%B3%A0%EC%96%91%EC%9D%B4</link>
            <guid>https://velog.io/@boost_dev/%EB%B0%B1%EC%A4%80-10171-%EA%B3%A0%EC%96%91%EC%9D%B4</guid>
            <pubDate>Wed, 03 May 2023 19:49:44 GMT</pubDate>
            <description><![CDATA[<p>원래 하던대로 <code>print(r&quot;&quot;)</code>로 입력 후 제출한 결과 <strong>컴파일에러</strong> 발생
파이참에서는 이상 없이 실행돼서 원인을 생각하다가 그냥<code>print(&quot;&quot;)</code>에 백슬래시가 들어갈 때만 한 번 더 입력해주는 방식이 더 빠르겠다고 생각해서 수정</p>
<pre><code>print(&quot;\\    /\\&quot;)
print(&quot; )  ( &#39;)&quot;)
print(&quot;(  /  )&quot;)
print(&quot; \\(__)|&quot;)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[vue) 페이지에 유튜브 영상 띄우기]]></title>
            <link>https://velog.io/@boost_dev/vue%EB%A1%9C-%EC%9C%A0%ED%8A%9C%EB%B8%8C-%EC%98%81%EC%83%81-%EB%9D%84%EC%9A%B0%EA%B8%B0</link>
            <guid>https://velog.io/@boost_dev/vue%EB%A1%9C-%EC%9C%A0%ED%8A%9C%EB%B8%8C-%EC%98%81%EC%83%81-%EB%9D%84%EC%9A%B0%EA%B8%B0</guid>
            <pubDate>Thu, 30 Mar 2023 04:20:34 GMT</pubDate>
            <description><![CDATA[<h2 id="vueyoutube-등을-모르겠으면-아래-방법으로도-올릴-수-있다">VueYoutube 등을 모르겠으면 아래 방법으로도 올릴 수 있다</h2>
<blockquote>
<p>대신 영상 바꿔야할 때마다 하드코딩을 해야된다</p>
</blockquote>
<ul>
<li>원하는 영상을 찾아서 공유 - 퍼가기 순으로 클릭
<img src="https://velog.velcdn.com/images/boost_dev/post/3b2f531f-6ce9-4e64-9961-0460bd900937/image.png" alt=""></li>
<li>퍼가기를 눌렀을 때 나온 코드를 원하는 위치에 붙여넣는다.
<img src="https://velog.velcdn.com/images/boost_dev/post/184cc444-56e2-4d8f-b0dc-711d22d1bfe3/image.png" alt=""></li>
</ul>
<hr>
<h1 id="에러">에러</h1>
<h2 id="vueyoutube-사용시">VueYoutube 사용시</h2>
<h3 id="appjs934-uncaught-typeerror-vue__webpack_imported_module_2__default-is-not-a-constructor">app.js:934 Uncaught TypeError: vue__WEBPACK_IMPORTED_MODULE_2__.default is not a constructor</h3>
<blockquote>
<p>원인
 vue/cli 5.8 버전에서 Vue.js는 2.x 버전대이므로, Vue.js 3.x에서 추가된 createApp 함수를 사용할 수 없다. </p>
</blockquote>
<blockquote>
<p>해결
 <code>new Vue()</code>를 사용</p>
</blockquote>
<hr>
<h1 id="참고">참고</h1>
<ul>
<li>오쌤의 니가스터디, <a href="https://ossam5.tistory.com/73">[HTML기초문법] 8강 iframe태그와 youtube영상 넣기 및 옵션 설정</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[라이브러리 / 프레임워크]]></title>
            <link>https://velog.io/@boost_dev/%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC</link>
            <guid>https://velog.io/@boost_dev/%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC</guid>
            <pubDate>Wed, 29 Mar 2023 12:53:55 GMT</pubDate>
            <description><![CDATA[<p>라이브러리와 프레임워크의 차이는 단어 자체의 뜻인 도서관과 뼈대라는 의미를 생각해보면 이해하기 쉽다</p>
<p>도서관에서 자유롭게 책을 열람하는 일과 인부가 정해진 설계도에 따라 건물을 완성하는 모습을 생각해보자</p>
<p>도서관에 들어가서 책장에서 원하는 책을 집어들고 필요한 내용을 열람하는 과정까지 사용자인 우리가 일의 흐름을 결정한다.</p>
<p>반면, 프레임워크는 미리 철근으로 뼈대를 만들고 건물을 짓는 공사현장이라고 생각해보자.
내가 공사 현장을 겪어보진 못했지만 설계도에 따라 뼈대가 세워지면 인부들은 내키는대로 작업하지 않고 자신에게 정해진 방식으로 건물을 만들어나간다.</p>
<p>떠올린 이미지에서 가장 큰 차이는 <strong>일의 주도권(어플리케이션의 흐름)</strong>이 누구에게 있느냐다.
자유롭게 책을 열람하는 도서관처럼 라이브러리는 어플리케이션의 흐름을 사용자가 가지고 있고, 프레임워크는 건설 중인 인부처럼 미리 정해진 흐름에 따른다는 점이 라이브러리와 프레임워크의 차이다</p>
<hr>
<h3 id="추가">추가</h3>
<h4 id="라이브러리-패키지-모듈-함수-비교">라이브러리, 패키지, 모듈, 함수 비교</h4>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/d02c86b9-70ab-4f04-8193-35b205d8c540/image.png" alt="">
라이브러리 : 패키지의 집합
패키지 : 모듈의 집합
모듈 : 함수와 변수의 집합
으로 외우지 말고 위 그림으로 한번에 이해하고 넘어가자</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git) commit rule]]></title>
            <link>https://velog.io/@boost_dev/Git-commit-rule</link>
            <guid>https://velog.io/@boost_dev/Git-commit-rule</guid>
            <pubDate>Wed, 29 Mar 2023 09:36:12 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>commit을 하면서도 메세지에 작업일자와 내용만 적고 끝내던 방식으로는 Git을 제대로 사용하는게 아니라고 생각해서 커밋 메세지 규칙을 찾아 정리했다.</p>
</blockquote>
<h1 id="commit-message-7가지-규칙">Commit message 7가지 규칙</h1>
<ul>
<li>제목과 본문을 한 줄 띄어 구분</li>
<li>제목은 <strong>50자 이내</strong></li>
<li>제목 <strong>첫 글자는 대문자</strong></li>
<li>제목 끝에 <strong>마침표 X</strong></li>
<li><strong>제목은 명령문</strong>으로, 과거형 X</li>
<li>본문의 <strong>각 행은 72자 이내</strong> (줄바꿈 사용)</li>
<li>본문은 <strong>어떻게 보다 무엇을, 왜에 대하여</strong> 설명</li>
</ul>
<h1 id="commit-message-구조">Commit message 구조</h1>
<p>기본적으로 commit message 는 제목, 본문, 꼬리말로 구성한다.
제목은 <strong>필수사항</strong>이며, 본문과 꼬리말은 선택사항이다.</p>
<pre><code>&lt;type&gt;: &lt;subject&gt;

&lt;body&gt;

&lt;footer&gt;</code></pre><h2 id="type">Type</h2>
<table>
<thead>
<tr>
<th>타입</th>
<th>내용</th>
</tr>
</thead>
<tbody><tr>
<td>feat</td>
<td>새로운 기능 추가, 기존의 기능을 요구 사항에 맞추어 수정</td>
</tr>
<tr>
<td>fix</td>
<td>기능에 대한 버그 수정</td>
</tr>
<tr>
<td>build</td>
<td>빌드 관련 수정</td>
</tr>
<tr>
<td>chore</td>
<td>패키지 매니저 수정, 그 외 기타 수정 ex) .gitignore</td>
</tr>
<tr>
<td>ci</td>
<td>CI 관련 설정 수정</td>
</tr>
<tr>
<td>docs</td>
<td>문서(주석) 수정</td>
</tr>
<tr>
<td>style</td>
<td>코드 스타일, 포맷팅에 대한 수정</td>
</tr>
<tr>
<td>refactor</td>
<td>기능의 변화가 아닌 코드 리팩터링 ex) 변수 이름 변경</td>
</tr>
<tr>
<td>test</td>
<td>테스트 코드 추가/수정</td>
</tr>
<tr>
<td>release</td>
<td>버전 릴리즈</td>
</tr>
</tbody></table>
<h2 id="subject">Subject</h2>
<p>Type 과 함께 헤더를 구성한다. 예를들어, 로그인 API 를 추가했다면 다음과 같이 구성할 수 있다.</p>
<p>ex) feat: Add login api</p>
<h2 id="body">Body</h2>
<p>헤더로 표현이 가능하다면 생략이 가능한다. 아닌 경우에는 자세한 내용을 함께 적어 본문을 구성한다.</p>
<h2 id="footer">Footer</h2>
<p>어떠한 이슈에 대한 commit인지, issue number 를 포함해야 한다. 위의 좋은 예시에서는 (#1) 처럼 포함시켰다. 그리고 close #1 처럼 close 를 통해 해당 이슈를 닫는 방법도 있다.</p>
<hr>
<h2 id="참고">참고</h2>
<p><a href="https://cocoon1787.tistory.com/m/708">코딩 공부 일지, [Git] 좋은 커밋 메세지 작성법</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Docker) 도커 허브에 이미지 올리기]]></title>
            <link>https://velog.io/@boost_dev/Docker-%EB%8F%84%EC%BB%A4-%ED%97%88%EB%B8%8C%EC%97%90-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%98%AC%EB%A6%AC%EA%B8%B0</link>
            <guid>https://velog.io/@boost_dev/Docker-%EB%8F%84%EC%BB%A4-%ED%97%88%EB%B8%8C%EC%97%90-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%98%AC%EB%A6%AC%EA%B8%B0</guid>
            <pubDate>Wed, 29 Mar 2023 02:25:22 GMT</pubDate>
            <description><![CDATA[<h2 id="먼저-도커-허브에-로그인">먼저 도커 허브에 로그인</h2>
<h2 id="레파지토리-생성">레파지토리 생성</h2>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/eebd6efc-c8be-4e79-9cbd-32d035e50c87/image.png" alt=""></p>
<h2 id="터미널에서-작업">터미널에서 작업</h2>
<ul>
<li><p>도커 로그인
<code>docker login</code>
<img src="https://velog.velcdn.com/images/boost_dev/post/307e7009-7b9a-447c-b028-d9933c390a3a/image.png" alt=""></p>
</li>
<li><p>이미지 확인 
<code>docker images</code></p>
</li>
<li><p>도커 허브에 푸시
<code>docker push 이미지명</code></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/002b8ba0-4b4b-429e-90c3-099a56e12d12/image.png" alt=""></p>
<blockquote>
<p>이름을 일치시켜주기 위해 이름 변경이 필요한 경우
<code>docker tag IMAGE_ID 변경할 이름</code></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[포트폴리오) 바로우산]]></title>
            <link>https://velog.io/@boost_dev/%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4-%EB%B0%94%EB%A1%9C%EC%9A%B0%EC%82%B0</link>
            <guid>https://velog.io/@boost_dev/%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4-%EB%B0%94%EB%A1%9C%EC%9A%B0%EC%82%B0</guid>
            <pubDate>Tue, 28 Mar 2023 09:10:50 GMT</pubDate>
            <description><![CDATA[<h1 id="💼개요">💼개요</h1>
<blockquote>
<p>ChatGPT, 객체인식, 수요예측 인공지능을 융합해 보유 자산을 관리할 수 있도록 이식한 공유경제 자동화 서비스 개발과 협업 방식을 익히기 위한 목적으로 팀 프로젝트를 진행했다.</p>
</blockquote>
<hr>
<h1 id="📝-프로젝트-진행-기록">📝 프로젝트 진행 기록</h1>
<ul>
<li><a href="https://github.com/Seongbae103">Github</a></li>
<li><a href="https://velog.io/@boost_dev/%EB%B0%94%EB%A1%9C%EC%9A%B0%EC%82%B0-%EC%A0%84%EC%B2%B4-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%A7%84%ED%96%89">Velog 전체 프로젝트 내용 정리</a></li>
<li><a href="https://borrowsan.shop/">랜딩페이지</a></li>
</ul>
<hr>
<h1 id="💳-주요-기능">💳 주요 기능</h1>
<ul>
<li>YOLOv5를 통한 자산 파손 여부 인식</li>
<li>ChatGPT를 통한 이용자 안내</li>
<li>Keras를 사용한 인공지능으로 지역에 따른 서비스 수요 예측</li>
</ul>
<hr>
<h1 id="👨💻-업무">👨‍💻 업무</h1>
<h2 id="공통-업무">공통 업무</h2>
<ul>
<li>Jira, Confluence를 사용한 프로젝트 진행 내역 정리</li>
<li>FastAPI를 통한 서버 구축</li>
<li>AWS를 통한 배포 </li>
<li>ERD 작성</li>
</ul>
<h2 id="전담-업무">전담 업무</h2>
<ul>
<li>개발 환경 설정</li>
<li>데이터 정형화</li>
<li>크롤링을 통한 데이터 수집</li>
<li>딥러닝 프레임워크를 사용한 주요 기능에서 사용할 모델 구현</li>
<li>웹 목업 작성</li>
<li>랜딩페이지 제작</li>
</ul>
<hr>
<h1 id="📚-기술-스택-및-툴">📚 기술 스택 및 툴</h1>
<ul>
<li>Ptyhon 3.9</li>
<li>FastAPI</li>
<li>AWS</li>
<li>Vue.js</li>
<li>Keras</li>
<li>Pytorch</li>
<li>Flutter</li>
<li>Github</li>
<li>Jira</li>
<li>Confluence</li>
<li>Slack</li>
</ul>
<hr>
<h1 id="개발-환경-설정-✅">개발 환경 설정 ✅</h1>
<h2 id="1-install-django-framework"><strong>1. Install Django Framework</strong></h2>
<h2 id="2-docker-with-mysql">2. <a href="https://www.docker.com/products/docker-desktop/"><strong>Docker with MySQL</strong></a></h2>
<h3 id="21-202212-기준-디폴트로-설정되어-있는-값으로-설치">2.1 2022.12. 기준 디폴트로 설정되어 있는 값으로 설치</h3>
<h3 id="22-재부팅-후-나타나는-링크로-들어가서-업데이트-진행">2.2 재부팅 후 나타나는 링크로 들어가서 업데이트 진행</h3>
<h3 id="23-create-mysql-container">2.3 <strong>create mysql container</strong></h3>
<pre><code>    # powershell
    # 윈도우 터미널을 통해 Docker Container 목록 출력하기
    # (-a 옵션은 실행하지 않은 Container도 출력)
    (base) PS C:\Users\Quiet\DjangoProject&gt; **docker ps -a**

    # 윈도우 터미널을 통해 Docker Container를 생성 및 실행하기
    (base) PS C:\Users\Quiet\DjangoProject&gt; **docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root mysql:5.7**

    # 컨테이너 접속
    # (container ID 값에 해당하는 uuid 값을 컨테이너 name 자리에 넣어주어도 실행할 수 있다)
    (base) PS C:\Users\Quiet\DjangoProject&gt; **docker exec -it mysql bash**

    # yum update
    bash-4.2# **yum update**

    # yum install -y vim
    bash-4.2# **yum install -y vim**

    # my.cnf 설정
    bash-4.2# **cd etc**
    bash-4.2# **vim my.cnf**

    # 키보드 i 누르면 insert 가능
    [client] 
    default-character-set = utf8mb4 

    [mysql] 
    default-character-set = utf8mb4

    [mysqld]  # 추가해주기
    lower_case_table_names=1
    collation-server = utf8_unicode_ci 
    init-connect=&#39;SET NAMES utf8&#39; 
    character-set-server = utf8

    # 키보드 esc 누르고 &quot;:wq!&quot; 입력 후 Enter
    (base) PS C:\Users\Quiet\DjangoProject&gt; exit
    (base) PS C:\Users\Quiet\DjangoProject&gt; docker restart mysql  # container ID
    (base) PS C:\Users\Quiet\DjangoProject&gt; docker exec -it mysql bash

    # DB서버 접속
    bash-4.2# **mysql -u root -p** (Enter 후 password 입력)
    Enter password:

    # DB서버에 접속하면 Databases와 Tables를 통제할 수 있다.(DB서버의 함수 사용 가능)
    mysql&gt; **show databases;**

    # DB를 생성하고 싶다면 create 함수를 이용한다.
    mysql&gt; **create database mydb;**

    # show에서 볼 수 있는 DB서버의 DB를 사용하고, Tables를 볼 수 있다.
    mysql&gt; **use mydb;**
    mydb&gt; **show tables;**
    mydb&gt; **status;**

    # 서버 실행하기
    ****mydb&gt; **exit**
    Bye
    ****bash-4.2# **exit**
    exit
    ****(base) PS C:\Users\AIA\PycharmProjects\djangoProject&gt; **python .\manage.py runserver**</code></pre><h3 id="24-pycharm-database-tabright-side">2.4 PyCharm Database Tab(right side)</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/9184edce-f6b5-4118-b1d0-eaa29a0c1a95/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/c2ef28a9-31ef-4943-8956-096445fe807e/image.png" alt="">
        ⇒ <strong>Test Connection</strong> 반드시 누르기</p>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/3f9da2ea-1687-446b-b204-d06d4d191a79/image.png" alt=""></p>
<hr>
<h2 id="3-python--mysql-네트워크-연결">3. <strong>Python / MySQL 네트워크 연결</strong></h2>
<pre><code># powershell
# Python과 MySQL 네트워크 연결을 위한 라이브러리 설치
(base) PS C:\Users\Quiet&gt; pip install mysqlclient
(base) PS C:\Users\Quiet&gt; pip install mysql-connector-python
(base) PS C:\Users\Quiet&gt; pip install django-cors-headers</code></pre><hr>
<h2 id="4-install-tensorflow--pytorch">4. <strong>Install Tensorflow &amp; Pytorch</strong></h2>
<h3 id="41-version-checktensorflow-python-cudnn-cuda">4.1 <a href="https://www.tensorflow.org/install/source#gpu">Version Check(Tensorflow, Python, cuDNN, CUDA)</a></h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/a72cc286-bef0-480c-bc95-54776fb57f59/image.png" alt=""></p>
<pre><code>⇒ Tensorflow gpu 사용을 위해 2.10.0 버전을 사용한다.</code></pre><p><img src="https://velog.velcdn.com/images/boost_dev/post/f34a8b98-b1f5-46fc-a2b9-06adf1b02e5a/image.png" alt=""></p>
<hr>
<h2 id="5-install-nvidia-drivergeforce-rtx-2080">5. <strong>Install NVIDIA Driver(GeForce RTX 2080)</strong></h2>
<h3 id="51-최신-공식-nvidia-드라이버-다운로드">5.1 <a href="https://www.nvidia.co.kr/Download/index.aspx?lang=kr">최신 공식 NVIDIA 드라이버 다운로드</a></h3>
<p>　　　⇒ 그래픽카드(GeForce RTX 2080) 기준 설치
<img src="https://velog.velcdn.com/images/boost_dev/post/1ca2cdfc-6a05-4ac6-a12c-4e965279046f/image.png" alt=""></p>
<pre><code>    ⇒ 디폴트로 설정된 옵션으로 설치</code></pre><hr>
<h2 id="6-install-cudaver-112">6. <strong>Install CUDA(ver 11.2)</strong></h2>
<blockquote>
<h4 id="uninstall-nvidia-frameview-sdk"><strong>uninstall NVIDIA FrameView SDK</strong></h4>
<pre><code>📖 appwiz.cpl &gt;&gt; NVIDIA FrameView SDK 1.3.8107.31782123</code></pre></blockquote>
<h3 id="61-cuda-toolkit-112-downloads">6.1 <strong><a href="https://developer.nvidia.com/cuda-11.2.0-download-archive?target_os=Windows&amp;target_arch=x86_64&amp;target_version=10&amp;target_type=exelocal">CUDA Toolkit 11.2 Downloads</a></strong></h3>
<pre><code>        ⇒ 디폴트로 설정된 옵션으로 설치</code></pre><hr>
<h2 id="7-install-cudnn810">7. <strong>Install cuDNN(8.1.0)</strong></h2>
<h3 id="71-nvidia-developer-login">7.1 <strong><a href="https://developer.nvidia.com/">NVIDIA Developer</a> login</strong></h3>
<h3 id="72-download-cudnn-v810-january-26th-2021-for-cuda-110111-and-112">7.2 <strong><a href="https://developer.nvidia.com/rdp/cudnn-archive">Download cuDNN v8.1.0 (January 26th, 2021)</a>, for CUDA 11.0,11.1 and 11.2</strong></h3>
<h3 id="73-압축파일-다음-경로에-풀기">7.3 <strong>압축파일 다음 경로에 풀기</strong></h3>
<blockquote>
<p> 📖 <strong>C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.2\include</strong></p>
</blockquote>
<h3 id="74-install-tensorflow2101">7.4 <strong>Install Tensorflow(2.10.1)</strong></h3>
<blockquote>
<p><strong>Update conda and pip</strong></p>
<pre><code>   # powershell
       # 관리자 모드 윈도우 탐색기
       (base) PS C:\&gt; pip install --upgrade pip
       (base) PS C:\&gt; conda update -n base conda
       (base) PS C:\&gt; conda update --all</code></pre><p><strong>Install tensorflow &amp; tensorflow-gpu</strong>
       # powershell
       # 관리자 모드 윈도우 탐색기
         (base) PS C:&gt; pip install tensorflow==2.10.1
         (base) PS C:&gt; pip install tensorflow-gpu==2.10.1</p>
</blockquote>
<pre><code>   # 가상환경에 설치되었는지 확인
     (base) PS C:\&gt; conda list</code></pre><h2 id="8-install-pytorch1131">8. <strong>Install Pytorch(1.13.1)</strong></h2>
<h3 id="-0141-check-install-code"><a href="https://pytorch.org/get-started/locally/">**　0.14.1 Check Install code**</a></h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/60419cf7-87a0-4719-af3e-a41f56d50e31/image.png" alt=""></p>
<pre><code>        ⇒ Conda Package로는 설치가 되지 않았다.(원인미상)</code></pre><blockquote>
<pre><code> # powershell
   (base) PS C:\Users\Quiet&gt; pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu117</code></pre></blockquote>
<blockquote>
<p>※  <strong>참고</strong> CUDA를 분명 11.2를 설치했는데 11.7로 설치하는 이유가 뭐지?<br>📖 <a href="https://kjy042386.tistory.com/324">https://kjy042386.tistory.com/324</a> </p>
</blockquote>
<pre><code>    ⇒ 추정) pytorch 구동환경 내 cuda library 복사본을 설치

         → 설정값은 CUDA 11.2 지만, pytorch 구동환경 내 상태값은 11.7인 것</code></pre><h2 id="9-check-version">9. <strong>Check Version</strong></h2>
<pre><code>    #**Python Code**

    # python
    import os
    import numpy as np
    import tensorflow as tf
    import torch
    import sklearn
    from tensorflow.python.client import device_lib

    # TF_CPP_MIN_LOG_LEVEL Default Setting 관련 경고 임시 조치
    os.environ[&#39;TF_CPP_MIN_LOG_LEVEL&#39;] = &#39;3&#39;

    if __name__ == &#39;__main__&#39;:
        print(f&#39;numpy version : {np.__version__}&#39;)
        print(f&#39;tensorflow version : {tf.__version__}&#39;)
        print(f&#39;torch version : {torch.__version__}&#39;)
        print(f&#39;sklearn version : {sklearn.__version__}&#39;)
        print(f&#39;이 PC에 설치된 디바이스 상세보기 : {device_lib.list_local_devices()}&#39;)
        print(f&#39;CUDA 프로그래밍 가능여부 :  {torch.cuda.is_available()}&#39;)
        print(f&#39;CUDA 프로그래밍 가능여부 : {torch.cuda.get_device_name()}&#39;)
        print(f&#39;사용 가능 GPU 갯수 :  {torch.cuda.device_count()}&#39;)
    ```</code></pre><hr>
<h2 id="10-install-fastapi--uvicorn">10. <strong>Install FastAPI &amp; uvicorn</strong></h2>
<h3 id="101-pip-install">10.1 pip install</h3>
<pre><code>    #powershell
    # Install
    (base) PS C:\fastApiServer&gt; pip install fastapi &#39;uvicorn[standard]&#39;

    # 서버 생성
    (base) PS C:\fastApiServer&gt; uvicorn main:app --reload</code></pre><hr>
<h2 id="11-애자일-방식을-위한-tool-연동">11. 애자일 방식을 위한 Tool 연동</h2>
<h3 id="111-slsck-jira-연동">11.1 slsck-jira 연동</h3>
<ul>
<li>프로젝트 설정
<img src="https://velog.velcdn.com/images/boost_dev/post/208c6014-e84f-4339-877e-3eacb3d9e14d/image.PNG" alt=""></li>
<li>Connect to Slack 클릭
<img src="https://velog.velcdn.com/images/boost_dev/post/bc97d05d-f12e-415f-b046-ae7f54ca9eda/image.png" alt=""></li>
</ul>
<h3 id="112-jira-confluence-연동">11.2 jira-Confluence 연동</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/3f5646e0-1605-47ef-a887-bbba3ced0e98/image.png" alt="">
<img src="https://velog.velcdn.com/images/boost_dev/post/e23b8578-72d5-4313-bc39-9ace0cb4018c/image.png" alt="">
<img src="https://velog.velcdn.com/images/boost_dev/post/0d2b4874-57a4-4bb4-bda6-612bb5df816f/image.png" alt=""></p>
<h3 id="113-jira-github-연동">11.3 jira-GitHub 연동</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/ad5fdcf3-8f6f-4a10-b21b-081a54e861f5/image.png" alt=""></p>
<ul>
<li>GitHub for Jira 검색
<img src="https://velog.velcdn.com/images/boost_dev/post/0230f032-f07a-46b1-8cb4-6433cce7d796/image.png" alt=""></li>
<li>바로우산에서 사용한 앱
<img src="https://velog.velcdn.com/images/boost_dev/post/1659e4b7-2dea-4198-8082-8e55629e69da/image.PNG" alt=""></li>
<li>추가한다
<img src="https://velog.velcdn.com/images/boost_dev/post/677084c7-7f54-4398-a5dd-d37a188532d8/image.png" alt=""></li>
<li>추가한 뒤에는 컨플루언스의 업무에서 브랜치 만들기, 커밋 만들기를 누르면
<img src="https://velog.velcdn.com/images/boost_dev/post/07d45cf3-28df-4e57-b1c3-7378ea943378/image.png" alt=""></li>
<li>아래 이미지가 나오고 Create branch를 누르면 깃허브에 브랜치가 생성된다
<img src="https://velog.velcdn.com/images/boost_dev/post/48a5daa3-030d-4c78-8be4-8b24ef20fa66/image.png" alt=""></li>
</ul>
<hr>
<h1 id="aws☁">AWS☁</h1>
<h2 id="1-설정">1 설정</h2>
<ul>
<li>fastapi requirements.txt에 추가</li>
<li>main.py에 handler = Mangum(app) 추가</li>
</ul>
<h3 id="11-db">1.1 DB</h3>
<ul>
<li>데이터 베이스 수정에서 db 파라미터를 사용중인 db로 변경</li>
<li>db가 제대로 들어갔는지는 <a href="https://github.com/Seongbae103/memo/blob/main/settings/setting(sql).MD">하이디</a>로 확인</li>
<li>하이디의 ip는 db의 연결 보안에 있는 엔드포인트</li>
<li>fastapi의 db도 host는 AWS의 엔드포인트 <blockquote>
<p>이제 docker/mysql 내부의 코드들은 발생x, docker/api만 발생</p>
</blockquote>
</li>
</ul>
<hr>
<h2 id="2-진행-순서">2. <strong>진행 순서</strong></h2>
<blockquote>
<p>RDS -&gt; IAM -&gt; S3</p>
</blockquote>
<h2 id="3-a-href--httpsparksrazortistorycom386rdsa">3 <a href = "https://parksrazor.tistory.com/386">RDS</a></h2>
<h3 id="31-rds-작성">3.1 RDS 작성</h3>
<ul>
<li>도커파일 및 yml파일 변경 후 dockerfile의 경로인 docker/api로 들어가서 &#39;docker build -t 이미지명 .&#39; 입력</li>
<li>원래 경로로 돌아가서 docker compose up</li>
<li>자동으로 테이블이 들어와야 성공
🐋 <a href="https://velog.io/@boost_dev/Docker-Docker-compose">docker compose up 방법이 기억안나면</a></li>
</ul>
<h3 id="32-rds-인스턴스-생성">3.2 <a href="https://velog.io/@boost_dev/AWS-RDS">RDS 인스턴스 생성</a></h3>
<p><strong>생성하면서 확인</strong></p>
<ul>
<li>표준 생성</li>
<li>사용 엔진(이번에는 MySQL)</li>
<li>템플릿 : 프리티어</li>
<li>인스턴스 크기 : t3(t2도 상관없으나 t3도 프리데이터)</li>
<li>스토리지 : 범용, 20, 자동 조정 비활성화
※ 스냅샷 체크를 해제해도 사용으로 된 경우도 있으므로 확인 필요</li>
</ul>
<h3 id="33-보안그룹-생성">3.3 보안그룹 생성</h3>
<blockquote>
<p>주의사항</p>
<blockquote>
<p>잘못하면 DB 해킹의 위험이 있는 부분이므로 주의</p>
</blockquote>
<ul>
<li>SecurityGroup 설정에서 보안 그룹을 생성하고 인바운드 규칙을 추가</li>
<li>외부 접속 방지를 위해 로컬에서 개발하는 동안은 개인 IP 주소를 설정하지만 EC2로 접속하는 과정이 있으면 추가<ul>
<li>위치 무관을 체크해야 ubuntu에서 apt-get-update가 적용된다 </li>
</ul>
</li>
<li>우측 상단 수정 → 생성한 식별자 선택 <ul>
<li>퍼블릭 액세스: 예(마이너 버전으로 변경시 요금 발생)</li>
<li>포트폴리오 사용할 일 없으면 : 아니오</li>
<li>백업 보존기간 : 0일</li>
<li>스냅샷 : 해제</li>
</ul>
</li>
</ul>
</blockquote>
<p>이거 보고도 모르겠으면 <a href="https://parksrazor.tistory.com/386">여기 참고</a></p>
<h3 id="34-파라미터-그룹-생성">3.4 파라미터 그룹 생성</h3>
<blockquote>
<p>좌측사이드바에서 파라미터 그룹(이하 Parameters)에서 파라미터 편집으로 넘어간다. 검색창에 char, coll 을 각각 입력 후 셀렉트박스에서 utf-8과 utf-8_general_ci 를 선택</p>
</blockquote>
<ul>
<li>DB 수정 → DB옵션에서 파라미터 그룹을 방금 설정한 그룹으로 변경</li>
</ul>
<h2 id="4-iam">4. IAM</h2>
<h3 id="41-iam-생성하기">4.1 IAM 생성하기</h3>
<blockquote>
<p>S3 만들기 전에 <strong>반드시</strong> iam 을 생성해야 한다. 이 파트를 생략해도 S3 가 만들어지지만, CORS 에서 많은 오류를 경험하기 싫으면 일단 생성하자</p>
</blockquote>
<p>, s3</p>
<hr>
<h2 id="5-s3">5. S3</h2>
<h3 id="51-envpy-양식">5.1 env.py 양식</h3>
<pre><code>USERNAME = &quot;AWS 유저명&quot;
PASSWORD = &quot;&quot;
HOSTNAME = &quot;aws 사용시의 엔드포인트&quot;
DATABASE = &quot;AWS의 DB명&quot;
PORT = 3306
CHARSET = &quot;utf8&quot;
DB_URL = f&quot;mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}&quot;</code></pre><hr>
<h3 id="52-dockerfile-양식">5.2 dockerfile 양식</h3>
<p>FROM python:3.9</p>
<p>WORKDIR /app
ADD requirements.txt .
RUN pip install --trusted-host pypi.python.org -r requirements.txt
EXPOSE 8000
CMD [&quot;uvicorn&quot;, &quot;app.main:app&quot;, &quot;--reload&quot;, &quot;--host&quot;, &quot;AWS엔드포인트&quot;, &quot;--port&quot;, &quot;8000&quot;]</p>
<hr>
<h2 id="6-한글-깨짐">6. 한글 깨짐</h2>
<ul>
<li>파라미터 그룹 -&gt; 파라미터 편집에서 </li>
<li>char : 전부 utf8mb4로 변경(0,1로 나오는 항목은 제외)</li>
<li>coll : collation_connection, collation_server의 값을 utf8mb4_general_ci로 변경</li>
</ul>
<hr>
<h2 id="7-보안🔒">7. 보안🔒</h2>
<blockquote>
<p>개인적인 피해를 막기 위한 최소한의 보안</p>
</blockquote>
<ul>
<li><p>인바운드는 어렵게 아웃바운드는 쉽게
1) 사용자 그룹 생성</p>
</li>
<li><p>권한 정책 연결 : AmazonS3FullAccess 선택
2) MFA 설정</p>
</li>
<li><p>준비 : Google OTP 어플리케이션</p>
</li>
<li><p>root 계정에서 보안자격 증명 → MFA 활성화 → 가상 MFA 디바이스 체크 후 계속</p>
</li>
<li><p>먼저 본인 핸드폰에 설치한 Google OTP로 해당 QR코드 인식 후 MFA 코드 1 입력</p>
<ul>
<li>MFA 코드 2는 MFA 코드 1 시간 경과 후 나오는 다음 코드</li>
</ul>
</li>
</ul>
<h3 id="71-root가-iam에게-비밀번호-변경-권한-주기">7.1 Root가 IAM에게 비밀번호 변경 권한 주기</h3>
<ul>
<li>IAM에서 &#39;계정 설정&#39; 클릭</li>
<li>&#39;암호 정책-&gt; 편집&#39;에서 체크<ul>
<li>사용자 지정 : 암호감독 체크박스 전체, </li>
<li>기타 요구사항 :사용자 자신의 암호 변경 허용 </li>
</ul>
</li>
</ul>
<h3 id="72-iam-로그인시-비밀번호-지정">7.2 IAM 로그인시 비밀번호 지정</h3>
<p>IAM -&gt; 사용자 -&gt; 사용자명 클릭</p>
<ul>
<li><p>보안 자격 증명 -&gt; 콘솔 액세스 관리 -&gt; 활성화 후 비밀번호 지정</p>
</li>
<li><p>권한을 받은 IAM사용자명, 방금 지정한 비밀번호로 로그인 가능</p>
</li>
</ul>
<h3 id="73-권한-부여">7.3 권한 부여</h3>
<ul>
<li>Root 사용자의 IAM으로 들어가서 권한을 준다</li>
</ul>
<h3 id="74-iam-사용자-결제-대시보드-접근-권한-설정">7.4 IAM 사용자 결제 대시보드 접근 권한 설정</h3>
<ul>
<li>builling은 AdministratorAccess에 포함되어 있다<blockquote>
<p>기본적으로 결제 정보 메뉴에 대한 권한이 없는 IAM 사용자가 해당 권한을 얻기 위한 조건은 </p>
<ul>
<li>(1) 이 계정이 IAM 및 페더레이션 사용자가 결제 정보에 액세스하도록 허용</li>
<li>(2) 필요한 IAM 권한을 보유하도록 함으로써 사용자 또는 그룹에 대해 권한</li>
</ul>
</blockquote>
</li>
</ul>
<ul>
<li>(1)은 Root 계정으로만 설정 가능<ul>
<li>계정 → 결제 정보에 대한 IAM 사용자 및 역할 액세스 :편집 </li>
<li>IMA 액세스 활성화 체크 → 업데이트</li>
</ul>
</li>
</ul>
<h2 id="8-a-href-httpswwwematop3comemaaiblog4-solutions-for-serverless-application-deployment-aws-sam-stackery-serverless-framework-and-pulumiaws에서-lambda-사용a">8. <a href ="https://www.ematop3.com/emaaiblog/4-solutions-for-serverless-application-deployment-aws-sam-stackery-serverless-framework-and-pulumi">AWS에서 lambda 사용</a></h2>
<blockquote>
<p>yaml(context file)을 사용하는 Serverless Framework보다 IaC방식(TypeScript 사용)인 Pulumi를 더 많이 사용</p>
</blockquote>
<ul>
<li>serverless와 pulumi 방식중 선택해서 사용</li>
</ul>
<h2 id="9-cli커맨드-라인-인터페이스">9. CLI(커맨드 라인 인터페이스)</h2>
<blockquote>
<p>리액트 사용시에는 서버리스 프레임워크를 사용하지만 리액트 같은 프론트 요소가 없는 경우는 CLI를 사용한다</p>
</blockquote>
<blockquote>
<p>CLI? 
AWS 클라우드 리소스를 생성, 편집, 검사하는 방식 중 하나</p>
</blockquote>
<p><strong>1) 언제 사용?</strong></p>
<blockquote>
<p>리액트 같은 프론트엔드 부문의 작업을 위한 경우는 서버리스 프레임워크를 사용하지만, 프론트엔드부분을 신경쓰지 않는 작업에서는 CLI를 사용</p>
</blockquote>
<p><strong>2) 장점</strong></p>
<blockquote>
<ul>
<li>코드(CLI 명령 포함)가 사용자의 변경을 기록</li>
<li>버전 제어(깃 등)하에 코드를 배치하고 변경사항을 효과적으로 관리 =&gt; 커밋하면 아마존 내부의 코드도 변경</li>
<li>수동적인 단계의 수행을 줄여서 작업을 빠르게 다시 실행할 수 있다</li>
<li>인적 오류 발생 가능성이 낮다</li>
</ul>
</blockquote>
<h2 id="10-로컬-aws-환경-설정">10. 로컬 AWS 환경 설정</h2>
<pre><code>  # 액세스 키 만들기
  - 경로 : AWS 사용자 설정 - 보안자격 증명 - 액세스 키 만들기 - 액세스 키, 비밀 액세스 키, .csv 파일 다운로드(!!!이 내용은 절대 커밋 금지) 
  - 유닉스계열(맥 등)은 셸 설정에서 환경변수를 추가 
  - KMS는 유료 서비스다

  # 설정 확인(아래 코드 차례대로 실행)
  - aws --version
  - aws s3 li s3://
  - aws configure </code></pre><hr>
<h2 id="11-s3-버킷-생성">11 S3 버킷 생성</h2>
<blockquote>
<p>OAC방식</p>
</blockquote>
<ul>
<li>OAC 설정시 &#39;웹 사이트 엔드포인트 사용&#39;을 누르면 OAC 설정이 사라지니까 먼저 누르지 말것</li>
<li>S3 버킷 액세스</li>
<li>원본 액세스 제어 설정(권장) - 제어설정 생성 : s3로</li>
<li>속성 - 생성한 버킷에서 정적 웹 사이트 호스팅 편집 - 활성화 후 변경사항 저장(CF연결할거니까 테스트목적 아니면 하지 말것)<ul>
<li>OAC 방식이 아니더라도 ACM(인증서)가 아니면 자신이 사용하는 리전으로 등록해야한다</li>
</ul>
</li>
</ul>
<h3 id="111-정책-설정">11.1 정책 설정</h3>
<blockquote>
<ul>
<li>경로 : 정책 - 버킷 정책 - 새 문 추가 - 아래 코드 입력</li>
</ul>
</blockquote>
<ul>
<li>CF 사용시 버킷정책<pre><code>  {
      &quot;Version&quot;: &quot;2012-10-17&quot;,
      &quot;Statement&quot;: [
          {
              &quot;Sid&quot;: &quot;Statement1&quot;,
              &quot;Effect&quot;: &quot;Allow&quot;,
              &quot;Principal&quot;: {&quot;Service&quot;:&quot;cloudfront.amazonaws.com&quot;},  - cloudfront.amazonaws.com를 통해서만 권한 부여
              &quot;Action&quot;: &quot;s3:*&quot;,                                     - 모든 crud를 사용
              &quot;Resource&quot;: &quot;arn:aws:s3:::seongbae.shop/*&quot;,
              &quot;Condition&quot;: {                                  -condition == if
                  &quot;StringEquals&quot; : {                            - &quot;AWS:SoursArn&quot;의 문자열이 &quot;&quot;과 같다면 
                      &quot;AWS:SoursArn&quot;:&quot;클라우드 프론트의 ARN&quot;
                  }
              }
          }
      ]
  }</code></pre></li>
</ul>
<hr>
<h2 id="12-cloudfront">12 CloudFront</h2>
<blockquote>
<p>도메인 | CF | S3 <br/></p>
<ul>
<li><a href="https://github.com/Seongbae103/memo/blob/main/howto/AWS_CF_cost.MD">CF관련 비용 절감 방법</a></li>
</ul>
</blockquote>
<ul>
<li>CF를 억지로 만들 필요 없이 도메인과 S3를 만들면 알아서 생성된다<ul>
<li>그러니까 CF를 먼저 만들지 마라</li>
</ul>
</li>
<li>cf의 오리진 = 버킷</li>
</ul>
<h3 id="120-특징">12.0 특징</h3>
<ul>
<li>고비용에 더 느리지만 보안에 더 유리</li>
<li>S3에서 CF로의 데이터 전송은 요금이 없으나 인터넷으로 전달하는건 비용이 요구된다</li>
<li>같은 리전에서의 전송만 무료니까 설정시 자신이 속한 리전을 설정</li>
</ul>
<h3 id="121-a-hrefhttpsdocsawsamazoncomko_krroute53latestdeveloperguidewelcomehtmldns-연결a아마존은-route53">12.1 <a href="https://docs.aws.amazon.com/ko_kr/Route53/latest/DeveloperGuide/Welcome.html">DNS 연결</a>(아마존은 Route53)</h3>
<ul>
<li>route53 - 시작하기 - 호스팅 영역 생성<ul>
<li>NS(Name Server), SOA가 생겼는지 확인 (NS, SOA에 대한 설명 <a href="https://server-talk.tistory.com/176">참고</a>)</li>
</ul>
</li>
<li>값/트래픽 라우팅 대상에 있는 값을 NDS에 등록 (이번에는 가비아에서 도메인을 받음. 상세설명은 <a href="https://customer.gabia.com/manual/dns/3041/3040">여기</a>)<ul>
<li>등록 장소 : 가비아 - My가비아 - 도메인 - 관리 - 네임서버 설정<ul>
<li>가비아는 NS의 마지막 &#39;.&#39;을 지워야 한다</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="122-cname-등록">12.2 CNAME 등록</h3>
<blockquote>
<p><strong>AWS에서</strong></p>
</blockquote>
<ul>
<li>AWS Certificate Manager에서 인증상태 확인<blockquote>
<p>인증이 안된 상태면 route53에 레코드 자동 생성을 눌렀는지 생각해보자</p>
</blockquote>
</li>
<li>도메인 - CNAME 확인</li>
<li>&#39;Route53에서 레코드 생성&#39; 클릭<ul>
<li>한 번 지운 경우는   </li>
<li>Route53 - 호스팅 영역에서 CNAME이 생기면 된다<ul>
<li>A 레코드를 추가하면서 &#39;ping 주소&#39;나온 IP입력</li>
</ul>
</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>가비아에서</strong></p>
</blockquote>
<ul>
<li>My가비아 - DNS 관리툴 - DNS설정<ul>
<li>호스트 : <del>.com은 @ / www.</del>.com은 www(이때 www를 쓴다면 값도 www가, admin을 쓰는 주소라면 admin.이 포함돼야 한다)</li>
<li>값/위치 : AWS에서 카피한 CNAME 이름 입력</li>
<li>TTL:3600</li>
</ul>
</li>
</ul>
<blockquote>
<p>✅가비아에서 제대로 등록됐는지 확인하는 방법</p>
<ul>
<li>cmd에서 &#39;ping 주소&#39; 입력</li>
<li><strong><a href="https://xn--c79as89aj0e29b77z.xn--3e0b707e/">후이즈 도메인 검색</a></strong></li>
<li>결과가 안나올 경우 ping 네임서버 1차 주소를 넣고 나온 값을 사용해서 A레코드를 만든다</li>
</ul>
</blockquote>
<ul>
<li>후이즈 통과 후 CF로 이동</li>
</ul>
<h3 id="123-cf-배포">12.3 CF 배포</h3>
<ul>
<li>cloundfront 생성에서 원본 도메인 선택하고 <a href="https://sh-t.tistory.com/72">여기를 참고</a><ul>
<li>가격 분류는 본인이 사용할 리전이 속해있는 곳에 해야 과한 비용을 막을 수 있다</li>
<li>CNAME 추가(&#39;www.주소.com&#39;과 &#39;주소.com&#39;을 둘 다 해야한다) -&gt; A 레코드로 연결했으면 CNAME의 값을 도메인 이름으로 해도 괜찮다</li>
<li>ACL를 사용하기 위해서는 먼저 아마존에서 사용하는 DNS인 Route53을 먼저 설정해야 한다(2.2 CNAME 등록 참고)</li>
<li>SSL은 optional로 되어있지만 보안 요소라 사실상 필수</li>
<li>IPv6: off(필수는 아니고 수업에서는 일단 off로 지정함 디폴트는 on)</li>
</ul>
</li>
<li>버킷과 연결<ul>
<li>Amazon S3 - 버킷 - 권한 - 버킷 정책 편집에서 CF의 ARN추가</li>
</ul>
</li>
</ul>
<pre><code>    &quot;Condition&quot;: {
        &quot;StringEquals&quot;: {
            &quot;AWS:SoursArn&quot;: &quot;배포하는 CF의 ARN&quot;
        }
    }</code></pre><h3 id="124-cf-주의사항">12.4 CF 주의사항</h3>
<ul>
<li>정적콘텐츠를 s3에 올리지 마라</li>
<li>OAI는 이제 죽으니까 OAC로 가야된다</li>
<li><a href= "https://jw910911.tistory.com/110">AWS : EC2 / S3 / CloudFront 트래픽 요금 분석</a></li>
<li><a href="https://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html">https://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html</a> </li>
<li><a href="https://www.pabburi.co.kr/content/aws/%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C%ED%94%84%EB%A1%A0%ED%8A%B8-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0/">왜 쓰나?</a><blockquote>
<p>자동확장 : 스케일링</p>
</blockquote>
</li>
</ul>
<hr>
<h2 id="13-ec2">13 EC2</h2>
<h3 id="131-보안그룹-확인">13.1 보안그룹 확인</h3>
<blockquote>
<p>보안그룹 생성은 설정 필기에서 확인</p>
</blockquote>
<h3 id="132-인스턴스-생성">13.2 인스턴스 생성</h3>
<blockquote>
<p>경로 : EC2 - 인스턴스 - 인스턴스 생성</p>
<ul>
<li>우분투 선택</li>
<li>인스턴스 시작 후 PuTTY 사용<ul>
<li>.pem을 .ppk로 하는건 Puttygen사용</li>
</ul>
</li>
<li>퍼블릭 ip를 입력하고 세이브 (먼저 있던 경우는 )</li>
<li>Connection - SSH - Credentioal에서 .ppk 연다 </li>
</ul>
</blockquote>
<blockquote>
<h4 id="☕install-java">☕install java</h4>
<p><code>sudo apt-get update</code>
<code>sudo apt-get install -y --no-install-recommends tzdata g++ git curl</code>
<code>sudo apt-get update</code>
<code>sudo apt-get install openjdk-11-jdk</code>
<code>java -version</code>
<code>javac -version</code>
<code>vim ~/.bashrc</code></p>
</blockquote>
<ul>
<li>i 입력으로 insert로 두고 가장 아랫줄에 <ul>
<li>``export JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))<pre><code>        export PATH=$PATH:$JAVA_HOME/bin``` 추가</code></pre></li>
</ul>
</li>
<li>Esc 누르고 :wq!
<code>source ~/.bashrc</code> 설정 적용
<code>echo $JAVA_HOME</code></li>
</ul>
<blockquote>
<h4 id="🐍intall-python">🐍intall python</h4>
<p><code>sudo apt update</code>
<code>sudo apt-get install -y python3-pip python3-dev</code>
<code>sudo apt install software-properties-common</code>
<code>sudo add-apt-repository ppa:deadsnakes/ppa</code></p>
</blockquote>
<h3 id="133-pip3-pip로-변경">13.3 pip3 pip로 변경</h3>
<pre><code>cd /usr/local/bin
sudo ln -s /usr/bin/pip3 pip
pip3 install --upgrade pip</code></pre><h3 id="134-sudo-apt-get-clean">13.4 sudo apt-get clean</h3>
<h3 id="135-install-nginx">13.5 install nginx</h3>
<pre><code># 설치
sudo apt-get update
sudo apt-get install nginx
sudo systemctl start nginx</code></pre><ul>
<li>/etc/nginx/sites-available 내 file에 쓰기 권한이 없으므로 chmod를 쓰기 권한을 추가해준다.
  sudo chmod 775 /etc/nginx/sites-available</li>
<li>nginx config 추가: cd /etc/nginx/sites-available &amp;&amp; vim &lt;서버이름&gt;</li>
</ul>
<blockquote>
<p>퍼미션 에러 나오면 앞에 sudo를 붙혀준다.</p>
</blockquote>
<p>AWS route53에서 도메인을 추가한다. A record를 생성하기 위해서는 고정된 IP 주소가 필요하다. lightsail 인스턴스에서 고정된 public IP를 생성해 A record에 연결한다. 생성한 도메인은 nginx conf에 추가한다.</p>
<pre><code>server{
       server_name &lt;your-site-domain&gt;;
       location / {
           include proxy_params;
           proxy_pass http://사용할 IP:8000;
       }
}</code></pre><ul>
<li>symlink: sites-available/<server-name> → sites-enabled</li>
</ul>
<pre><code>sudo ln -s /etc/nginx/sites-available/&lt;your-server-name&gt; /etc/nginx/sites-enabled/  </code></pre><h3 id="136-run-app">13.6 run app</h3>
<p><code>git clone &lt;your-server-repo&gt;</code>
<code>cd /var/www</code>
<code>git clone &lt;server-repo&gt;</code>
<code>cd &lt;server-repo&gt;</code></p>
<ul>
<li>nginx 확인<br><code>sudo nginx -t</code></li>
<li>restart nginx 
<code>sudo systemctl restart nginx.service</code><ul>
<li>안되면 <code>sudo systemctl restart nginx</code><blockquote>
<p>failed가 나올 경우 putty에서 /etc/nginx/sites-available과 /etc/nginx/sites-enabled/에 둘 다 같은 vim 파일이 있는지 확인한다.</p>
</blockquote>
</li>
</ul>
</li>
<li>gunicorn으로 ASGI 서버 실행 : 
<code>python3 -m gunicorn -k uvicorn.workers.UvicornWorker main:app</code></li>
</ul>
<blockquote>
<h4 id="🔗연결-확인">🔗연결 확인</h4>
</blockquote>
<ul>
<li>route 53에 레코드를 CNAME으로 들어간 놈을 A로 바꾸고 그 레코드에 고정 IP(탄력적 ip)로 입력했는지 확인<ul>
<li>putty의 vim 파일이 아래 같은 모양인지 확인
```server{
server_name 사용할 도메인 이름;
location /{<pre><code>include proxy_params;
proxy_pass http://사용할 IP:8000;</code></pre>}
}  <blockquote>
<p>! 사용할 IP자리에 사용하기 위해 받은 탄력적 IP를 넣었을 때 실패</p>
</blockquote>
</li>
<li>사용할 IP에 127.0.0.1(로컬호스트)을 주로 사용하는 이유는 EC2 자체가 하나의 PC이기 때문에 로컬로 돌리는 것이기 때문에다</li>
</ul>
</li>
</ul>
<hr>
<h2 id="14-ec2와-서브-도메인-연결">14 EC2와 서브 도메인 연결</h2>
<blockquote>
<p>루트 도메인을 이미 사용했으므로 새 인증서를 요청
<a href="https://blog.nachal.com/1515">참고</a></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/e17cf2dc-fa11-418d-8358-65f3f3aa06cc/image.PNG" alt=""></p>
<p>인증이 완료돼서 CNAME을 얻으면 가비아로 가서 등록
<img src="https://velog.velcdn.com/images/boost_dev/post/69ee5124-d58a-42e1-ba8d-f9576890e69f/image.PNG" alt=""></p>
<h3 id="141-탄력적-ip를-사용">14.1 탄력적 IP를 사용</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/a998a790-b1df-44af-a57b-6c1a286f4d0e/image.PNG" alt=""></p>
<ul>
<li><p>할당
<img src="https://velog.velcdn.com/images/boost_dev/post/64f8bc9f-787b-4375-b97b-49783abdde5f/image.PNG" alt=""></p>
</li>
<li><p>확인
<img src="https://velog.velcdn.com/images/boost_dev/post/9b14c7c9-0f27-41e3-8356-6e411350d6f4/image.PNG" alt=""></p>
</li>
<li><p>EC2 연결
<img src="https://velog.velcdn.com/images/boost_dev/post/4e67da32-447f-4774-b7d5-fc7043dd17b0/image.PNG" alt=""></p>
</li>
</ul>
<blockquote>
<h4 id="💻-putty-사용">💻 putty 사용</h4>
<p>EC2의 인스턴스에서 사용할 인스턴스 재시작
Putty에서 .ppk 실행</p>
</blockquote>
<ul>
<li>푸티의 ip는 &#39;EC2- 인스턴스&#39;에서 우클릭 후 연결을 선택해서 여기에 있는 퍼블릭 IP 입력
<img src="https://velog.velcdn.com/images/boost_dev/post/6080429b-d7bf-4418-9fb8-7aca696435e8/image.PNG" alt=""></li>
</ul>
<hr>
<h2 id="2-error😕">2.? error😕</h2>
<h3 id="도메인-주소로-접속시-주소-앞에-주의-요함이-나오는-경우">도메인 주소로 접속시 주소 앞에 &#39;주의 요함&#39;이 나오는 경우</h3>
<p>  <img src="https://velog.velcdn.com/images/boost_dev/post/e9704fed-4a62-4b18-80e0-89fa5b6b96c4/image.png" alt=""></p>
<blockquote>
<p>원인</p>
<blockquote>
<ol>
<li>배포한 CF에 인증서인 SSL이 없는 경우 </li>
</ol>
</blockquote>
<p>해결</p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/d62d91e2-4ec2-42e4-87f3-b20b860de383/image.png" alt=""></p>
</blockquote>
</blockquote>
<h3 id="s3은-되는데-cf가-안되면">s3은 되는데 CF가 안되면?</h3>
<blockquote>
<p>원인 </p>
<blockquote>
<p>route53 문제 </p>
</blockquote>
<p>해결</p>
<blockquote>
<ul>
<li>호스팅 영역 삭제 후 다시 만들어라 (영역 삭제가 안되면 A, CNAME 레코드를 삭제 후 영역 삭제를 진행하면 된다)</li>
<li>A 레코드의 값을 ip가 아니라 CF의 배포 도메인 이름으로 바꿔준다(별칭 활성 - CF 엔드포인트 선택 배포 도메인 이름 입력)<h4 id="-동적-ip를-사용하는-경우는-퍼블릭-엑세스-차단-활성화--cors-코드-삭제">!!! 동적 ip를 사용하는 경우는 퍼블릭 엑세스 차단 활성화 + CORS 코드 삭제</h4>
</li>
<li>자동으로 안들어오는 경우 대체 CF에서 도메인 이름을 설정하면 해결된다</li>
</ul>
</blockquote>
</blockquote>
<hr>
<h2 id="15-🐋docker-설정">15 🐋Docker 설정</h2>
<blockquote>
<p>안되면 앞에 <code>sudo</code></p>
</blockquote>
<h3 id="1551-docker-설치">155.1 Docker 설치</h3>
<p><code>apt install apt-transport-https ca-certificates curl software-properties-common</code>
<code>curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -</code>
<code>add-apt-repository &quot;deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable&quot;</code>
<code>apt update</code>
<code>apt-cache policy docker-ce</code>
<code>apt install docker-ce</code>    </p>
<h3 id="152-docker에-sudo-권한-부여">15.2 Docker에 sudo 권한 부여</h3>
<p><code>usermod -aG docker $USER</code>
<code>newgrp docker</code></p>
<h3 id="153-설치-확인">15.3 설치 확인</h3>
<p><code>systemctl status docker</code></p>
<h3 id="154-docker-compose">15.4 Docker-compose</h3>
<pre><code>curl -L &quot;https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)&quot; -o /usr/local/bin/docker-compose</code></pre><h3 id="155-docker-compose에-실행권한-부여">15.5 Docker-compose에 실행권한 부여</h3>
<p><code>chmod +x /usr/local/bin/docker-compose</code></p>
<h3 id="156-설치확인">15.6 설치확인</h3>
<p><code>systemctl status docker</code></p>
<pre><code>curl -L &quot;https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)&quot; -o /usr/local/bin/docker-compose</code></pre><p><code>chmod +x /usr/local/bin/docker-compose</code>
<code>docker-compose version</code></p>
<hr>
<h2 id="16-🐍python-설치">16 🐍Python 설치</h2>
<p><code>sudo apt install python3</code>
<code>sudo apt install python3-venv</code>
<code>python3 -m venv 가상 공간 이름</code></p>
<ul>
<li>가상환경 설정여부 확인
<code>cd 가상환경명</code>
<code>ll</code>
아래 사진처럼 나온다면 ok
<img src="https://velog.velcdn.com/images/boost_dev/post/9561629f-e980-403d-b088-88e8ba33a1df/image.png" alt=""></li>
</ul>
<ul>
<li>가상환경 활성화
<code>source 가상 환경 경로/bin/activate</code>
<img src="https://velog.velcdn.com/images/boost_dev/post/1dee6936-d0bb-4a3e-908a-56f58d7ab663/image.png" alt=""></li>
</ul>
<hr>
<h2 id="17-pycharm-연동">17 Pycharm 연동</h2>
<ul>
<li>먼저 .pem 키를 가져오고 
<img src="https://velog.velcdn.com/images/boost_dev/post/10f06698-a1ac-4d7b-a7ee-cb0996eba6ae/image.png" alt="">
<img src="https://velog.velcdn.com/images/boost_dev/post/59f293d4-6bc3-4076-ac03-b8670d072e85/image.png" alt="">
<img src="https://velog.velcdn.com/images/boost_dev/post/d14b911d-e806-43b2-8827-cd7e30bcda6c/image.png" alt="">
<img src="https://velog.velcdn.com/images/boost_dev/post/eb30df08-c8e1-4373-9c73-f0ec2668ef6e/image.png" alt=""><h4 id="해당-경로에-폴더만-생성되고-파일이-안들어오는-경우는">해당 경로에 폴더만 생성되고 파일이 안들어오는 경우는</h4>
remote path 경로를 var/www를 기본으로 사용하고 권한이 없어서 못만드는 경우는 putty에 직접 들어가서 <code>mkdir 폴더명</code> 실행하고 remote path를 지정해준다</li>
<li>*/var/www는 먼저 <a href="https://velog.io/@boost_dev/AWS-Lightsail%EC%97%90-fastapi-%EC%84%A4%EC%B9%98">enginx</a><blockquote>
<p>권한 오류가 나오는 경우는 <a href="https://velog.io/@boost_dev/AWS-Kubernetes-%EC%84%A4%EC%A0%95#4-permission-denied">?.4</a> 참고</p>
</blockquote>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/ea2c7cfc-6068-4c5e-a0c0-eff00b0d700e/image.png" alt=""></p>
<ul>
<li>방금 만든 SSH로 터미널 열고 cd 복사한 경로</li>
</ul>
<h3 id="171-db-연결">17.1 DB 연결</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/2ad16b8c-206a-4f22-bac8-ff07cb54980c/image.png" alt=""></p>
<h2 id="18-프리티어-사용시-주의">18. 프리티어 사용시 주의</h2>
<ul>
<li>인스턴스 구성시 t2로 설정</li>
<li>RDS에서 스토리지의 할당된 스토리지는 20으로(스토리지 자동 조정 활성화는 체크를 해제)</li>
</ul>
<blockquote>
<p>EC2를 사용해서 </p>
</blockquote>
<h3 id="a-href-httpsvelogioyangsijun528aws-ec2ec9980-rds-eca784eca79c-free-tier-ec82acec9aa9ed9598eab8b0주의-a"><a href ="https://velog.io/@yangsijun528/AWS-EC2%EC%99%80-RDS-%EC%A7%84%EC%A7%9C-Free-tier-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0">주의 </a></h3>
<ul>
<li>인공지능 모델은 lambda</li>
<li>상시로 열려있는 페이지는 EC2가 더 유리하다<blockquote>
<p>이제는 매번 도커에 설치할 필요 x</p>
<ul>
<li><a href ="https://parksrazor.tistory.com/386">참고</a></li>
</ul>
</blockquote>
</li>
</ul>
<blockquote>
<p>리전은 ACM에서만 버지니아 북부를 사용</p>
</blockquote>
<hr>
<h1 id="📁데이터-베이스">📁데이터 베이스</h1>
<h2 id="1-erd-구성">1. ERD 구성</h2>
<ul>
<li>운영 시 수집 될 데이터를 보관 할 저장소를 구축한다.<h3 id="11-1차">1.1 1차</h3>
<img src="https://velog.velcdn.com/images/boost_dev/post/af2373da-be94-4878-bc0e-7d0489a03d78/image.PNG" alt=""></li>
</ul>
<h3 id="12-2차">1.2 2차</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/83a6f506-a0be-45ae-b5c9-fa428fa32936/image.PNG" alt=""></p>
<blockquote>
<p><strong>변경사항</strong></p>
</blockquote>
<ul>
<li>사용자엔티티와 우산엔티티는 다대다 관계이므로  크로스엔티티를 추가해 역정규화</li>
<li>우산 파손여부 외에 파손율 변수 추가</li>
<li>대여함 설치위치와 관련된 데이터들과 우산 배치개수와 관련된 데이터로 분리</li>
</ul>
<h3 id="13-3차">1.3 3차</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/7bd2de96-01e4-4e87-a260-5d5055f925b4/image.PNG" alt=""></p>
<blockquote>
<p><strong>변경사항</strong></p>
</blockquote>
<ul>
<li>필요이상으로 많은 테이블이 만들어져서 테이블 종류를 제한</li>
</ul>
<h3 id="14-최종-erd">1.4 최종 ERD</h3>
<blockquote>
<p><strong>변경사항</strong></p>
</blockquote>
<ul>
<li>사용자 엔티티 필드 중 위치를 위도/경도로 구분하고 비밀번호 필드 추가</li>
<li>게시판(boards)엔티티를 추가 </li>
<li>프런트와 필드명 일치 검토 필요하다.</li>
<li>테이블은 관리자, 우산, 대여함, 대여 일지, 사용자, 게시판의 총 6개로 분류한다.</li>
</ul>
<blockquote>
<h3 id="각-테이블의-세부-내용"><strong>각 테이블의 세부 내용</strong></h3>
<p><strong>관리자</strong>
관리자ID, 이름, 비밀번호, 가입 일자, 수정 일자, 토큰
모든 테이블에 접근 권한이 있다.
<strong>우산</strong></p>
</blockquote>
<ul>
<li>우산ID, 파손율, 이미지, 상태</li>
<li>외래 키로 대여함 테이블의 대여함ID를 받는다. </li>
<li><em>대여함*</em></li>
<li>대여함ID, 자치구, 위도, 경도</li>
<li><em>대여 일지*</em></li>
<li>대여ID, 파손 여부, 대여 시각, 반납 시각</li>
<li>외래 키로 우산 테이블의 우산ID와 사용자 테이블의 사용자ID를 받는다.</li>
<li><em>사용자*</em></li>
<li>사용자ID, 연령, 거주 지역, 주 이용 지역, 현 위치 위도, 현 위치 경도, 생년월일, - 등급, 결제정보, 비밀번호, 이메일, 가입 일자, 정보 수정 일자, 성별, 토큰</li>
<li><em>게시판*</em></li>
<li>게시 글ID, 제목, 작성 일자, 수정 일자, 유형, 내용, 원 글ID</li>
<li>외래 키로 사용자 테이블의 사용자ID를 받는다.
<img src="https://velog.velcdn.com/images/boost_dev/post/73f5367e-7308-4b45-8fea-288626911275/image.png" alt=""></li>
</ul>
<h2 id="2-fastapi-데이터-베이스-구축">2. FastAPI 데이터 베이스 구축</h2>
<ul>
<li><p>ERD 구성을 바탕으로 각 테이블과 컬럼을 코드로 생성한다.
<img src="https://velog.velcdn.com/images/boost_dev/post/98df4f2f-fff3-429c-9934-63b564af70db/image.png" alt=""></p>
</li>
<li><p>Docker 를 사용하기 때문에 Docker compose up 명령어 입력 시 테이블이 자동으로 생성된다.
<img src="https://velog.velcdn.com/images/boost_dev/post/43544423-a2f0-41ff-90f9-1369e3db0d07/image.png" alt=""></p>
</li>
</ul>
<hr>
<h1 id="💻웹-제작">💻웹 제작</h1>
<blockquote>
<p>프론트엔드 희망하는 팀원이 따로 없고 앱 위주의 서비스이기 때문에 가벼운 랜딩 페이지로 제작</p>
</blockquote>
<h2 id="1-웹-목업-제작">1. 웹 목업 제작</h2>
<ul>
<li>Balsamiq mockups을 사용해 작업을 진행한다.</li>
<li>웹은 고객이 보는 서비스 소개 페이지와, 관리자가 보는 관리자 페이지 2개로 구분된다.
<img src="https://velog.velcdn.com/images/boost_dev/post/b909be30-beef-42b2-b03b-279451694a9a/image.PNG" alt=""></li>
</ul>
<h3 id="11-figma-제작">1.1 Figma 제작</h3>
<ul>
<li>목업에서 사용 가능한 HTML 코드를 추출하기 위해 Figma를 사용했다.</li>
<li>관리자 페이지는 로그인 창과 관리 창으로 구성되며, 관리창은 네비게이터 버튼만 클릭하면 내부 화면이 바뀌는 형태로 구성한다.
<img src="https://velog.velcdn.com/images/boost_dev/post/fe462691-7871-4259-acf7-77ffffe39ec5/image.png" alt=""></li>
</ul>
<h2 id="2-랜딩-페이지">2. 랜딩 페이지</h2>
<h3 id="21-초안">2.1 초안</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/0395228b-1bff-4614-9f84-b1d047ec16a1/image.png" alt=""><img src="https://velog.velcdn.com/images/boost_dev/post/99cc297f-eb3f-419f-bc71-96702c42208f/image.png" alt=""><img src="https://velog.velcdn.com/images/boost_dev/post/f9db3f7b-4fd5-446c-b17d-efec01b7b490/image.PNG" alt=""><img src="https://velog.velcdn.com/images/boost_dev/post/2121bcb7-327d-4865-b1bc-d732388e9e10/image.png" alt=""></p>
<h4 id="211-제작-중-추가-내용">2.1.1 제작 중 추가 내용</h4>
<ul>
<li><strong>랜딩페이지에서 깃허브 등 상세 링크로 이동할 때 새 화면이 랜딩페이지의 창에서 열려서 열람에 불편을 끼침</strong> <ul>
<li><code>target=&quot;_blank&quot;</code>를 태그에 추가해서 해결</li>
</ul>
</li>
<li></li>
</ul>
<h4 id="212-보완사항">2.1.2 보완사항</h4>
<ul>
<li>서비스 소개 항목 벨로그 게시물로 교체</li>
<li>미구현 항목 제거
<img src="https://velog.velcdn.com/images/boost_dev/post/fe338e87-6c7a-4ec8-9695-cc424c6f3a6f/image.png" alt=""><img src="https://velog.velcdn.com/images/boost_dev/post/5ddd2be8-e25f-441a-a642-bc2fe28dd4ba/image.png" alt=""><img src="https://velog.velcdn.com/images/boost_dev/post/f9db3f7b-4fd5-446c-b17d-efec01b7b490/image.PNG" alt=""><img src="https://velog.velcdn.com/images/boost_dev/post/5c306b82-6be1-43e0-a941-5769d2ef9b3b/image.png" alt=""></li>
</ul>
<hr>
<h1 id="👾인공지능-모델링">👾인공지능 모델링</h1>
<h2 id="1-yolov5를-이용한-파손-여부-확인">1. YOLOv5를 이용한 파손 여부 확인</h2>
<h3 id="11-전처리">1.1 전처리</h3>
<h4 id="111-trainpy에서-실행하기-위해-사용할-데이터를-가져올-yaml-파일을-생성">1.1.1 train.py에서 실행하기 위해 사용할 데이터를 가져올 yaml 파일을 생성</h4>
<h4 id="112-기본-dataset에서-학습-시킬-대상을-가진-데이터만-남기고-나머지-데이터-삭제">1.1.2 기본 Dataset에서 학습 시킬 대상을 가진 데이터만 남기고 나머지 데이터 삭제</h4>
<table>
<thead>
<tr>
<th>실행 전</th>
<th>실행 후</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/8ea24045-4990-4110-965e-89d6fd20fbd3/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/158f17f3-d7bd-4723-87b9-0c4efeb380f1/image.png" alt=""></td>
</tr>
<tr>
<td>images : 116,408 / labels : 116,408</td>
<td>images : 3,904/ labels : 3,904</td>
</tr>
</tbody></table>
<h3 id="12-전체-사물-중-우산만-인식">1.2 전체 사물 중 우산만 인식</h3>
<ul>
<li>학습한 .pt 파일을 실행한 결과</li>
</ul>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/boost_dev/post/9de78ac4-6c23-4628-a111-865379905cff/image.PNG" alt=""></th>
<th><img src="https://velog.velcdn.com/images/boost_dev/post/3e2cecc0-692c-4dab-ac8b-2e5cd2a9dcae/image.PNG" alt=""></th>
</tr>
</thead>
</table>
<blockquote>
<p>목적인 우산 자체는 인식을 했으나 우산이 아닌 것까지 우산으로 인식하는 문제 발생</p>
</blockquote>
<ul>
<li><p>원인?</p>
<ul>
<li>Confidence Threshold가 0.25로 낮아서 발생한 문제</li>
<li>아래 설명에서 알 수 있듯이 이 값이 너무 낮으면 자신감 넘치는 상대의 말은 무조건 믿는 것처럼 정확도가 낮은 결과를 받게된다.<blockquote>
<blockquote>
<h4 id="confidence-threshold">Confidence Threshold?</h4>
<ul>
<li>말 그대로 Confidence에 대한 신뢰도로 Confidence가 일정 수준에 미달이면 버리게 하는 수준이다 .</li>
<li>단, 이 값이 너무 높으면 작은 차이로도 인식에 문제가 생기기 때문에 중간점을 잘 잡아야 한다.</li>
</ul>
</blockquote>
</blockquote>
</li>
</ul>
</li>
<li><p>해결 </p>
<ul>
<li>Confidence Threshold의 값을 0.4정도로 설정해서 정확도가 낮은 박스를 사용하지 않게 했다.</li>
</ul>
</li>
</ul>
<h3 id="13-우산의-파손-여부-인식">1.3 우산의 파손 여부 인식</h3>
<h4 id="131-이미지-크롤링">1.3.1 <a href="https://velog.io/@boost_dev/%ED%81%AC%EB%A1%A4%EB%A7%81">이미지 크롤링</a></h4>
<ul>
<li>우산 파손여부 인식을 위해 기본 데이터셋에 없는 파손된 우산 이미지와 라벨이 필요</li>
<li>크롤링 결과로 나온 이미지들에 <a href="https://velog.io/@boost_dev/%EB%9D%BC%EB%B2%A8%EB%A7%81">라벨링</a> 작업 시행</li>
</ul>
<h4 id="132-1차-시도">1.3.2 1차 시도</h4>
<ul>
<li>파손 데이터의 양이 너무 적고 각 파손된 형태가 제각각이기 때문에 정사 우산과 파손 우산을 구분하지 못함</li>
</ul>
<p>-파손 데이터를 추가하고 epoch 50으로 실행한 결과
<img src="https://velog.velcdn.com/images/boost_dev/post/b3243f42-97c1-4a35-a1a5-1c4e49bea052/image.png" alt=""><img src="https://velog.velcdn.com/images/boost_dev/post/7c21ac18-8507-4801-a381-a8964f4b668c/image.png" alt=""></p>
<blockquote>
<p>이제는 우산과 파손을 잘 잡지만 아직도 이상한 곳에서 우산을 인식한다… 이건 우산관련 데이터를 늘리는 방법이 최선이라고 생각한다.</p>
</blockquote>
<h4 id="133-2차-시도">1.3.3 2차 시도</h4>
<ul>
<li>2차 학습에서 사용한 데이터셋에서 데이터량을 늘려서 학습</li>
<li>구글 이미지 크롤링으로 검색된 파손된 우산 이미지를 모두 모아서 크롤링으로는 우리가 원하는 데이터를 더 구할 수 없으니 다음 단계에서는 직접 데이터셋을 늘리는 방식을 찾아야 한다
<img src="https://velog.velcdn.com/images/boost_dev/post/966d06a5-f921-4bcc-b53c-c362db8e3637/image.png" alt=""><blockquote>
<p>이전 시도보다는 높은 정확도를 가지게 됐다.</p>
</blockquote>
</li>
</ul>
<h2 id="2-kogpt2를-이용한-borrow-san-서비스-챗봇">2. KoGPT2를 이용한 Borrow-San 서비스 챗봇</h2>
<h3 id="21-전처리">2.1 전처리</h3>
<hr>
<h3 id="22-학습">2.2 학습</h3>
<blockquote>
<p>가장 많이 사용되는 송영숙님의 깃허브에 있는 <a href="https://github.com/songys/Chatbot_data">챗봇 데이터셋</a>을 참고해서 프로젝트 시나리오를 추가 
메타데이터는 사용자 입력, 원하는 대답, 내용 분류된다 </p>
</blockquote>
<h4 id="221-1차-시도">2.2.1 1차 시도</h4>
<p>서비스 시나리오를 구상해서 약 100개의 대화 유형을 데이터셋으로 만들어 학습 </p>
<ul>
<li><p>결과
<img src="https://velog.velcdn.com/images/boost_dev/post/861c24fe-42fa-4040-a386-5670644cf316/image.png" alt=""></p>
</li>
<li><p>고정된 답변만 가능하고 그마저도 틀리는 경우가 발생</p>
</li>
<li><p>서비스 이외의 대답은 불가</p>
</li>
</ul>
<blockquote>
<p>대안 :
송영숙님의 데이터셋을 추가해서 대화 가능한 범위 확대</p>
</blockquote>
<h3 id="222-2차-시도">2.2.2 2차 시도</h3>
<ul>
<li>1차 시도의 대안으로 데이터셋을 추가해서 학습</li>
<li>결과
<img src="https://velog.velcdn.com/images/boost_dev/post/2b809b30-f12b-4d3f-adac-ef9ab7dead39/image.png" alt=""></li>
<li>대화 가능범위는 늘었지만 알아듣지 못한다</li>
</ul>
<blockquote>
<p>원인 :
기존 데이터셋에 내용 분류에 해당하는 값들을 넣어주지 않아서  생긴 문제</p>
</blockquote>
<blockquote>
<p>대안 : 
내용 분류에 해당하는 값으로 서비스 관련(3)을 추가했다.</p>
</blockquote>
<h4 id="대안-적용-후"><strong>대안 적용 후</strong></h4>
<p><a href="https://drive.google.com/file/d/15t2kYbFX1jeV41Z8Z6-kAfZseCbqivdJ/view?usp=share_link"><img src="https://velog.velcdn.com/images/boost_dev/post/e00e020c-135f-4637-afa6-38f3aa99c29a/image.png" alt=""></a></p>
<hr>
<h3 id="23-chatgpt3로-변경">2.3 ChatGPT3로 변경</h3>
<blockquote>
<p>ChatGPT3 출시로 기존에 있던 KoGPT2를 파인튜닝하는 방식보다 더 쉽게 좋은 성능을 낼 수 있어서 ChatGPT3를 사용한 서비스용 챗봇으로 변경</p>
</blockquote>
<h4 id="231-준비api키">2.3.1 준비(API키)</h4>
<ul>
<li><strong><a href="https://platform.openai.com/">OpenAI</a></strong>에서 회원가입 후 진행</li>
<li>API키 가져오기<img src="https://velog.velcdn.com/images/boost_dev/post/199ff316-c1c4-43e7-a589-57f582d13045/image.png" alt=""><ul>
<li>API키 생성<br><img src="https://velog.velcdn.com/images/boost_dev/post/527e0a48-8fa7-4145-bfbd-1e574239f256/image.png" alt=""></li>
</ul>
</li>
</ul>
<hr>
<h4 id="232-코드-작성">2.3.2 코드 작성</h4>
<h5 id="openai-설치"><strong>openAI 설치</strong></h5>
<p> 터미널에서 아래 명령어 입력으로 설치</p>
<pre><code>pip install openai</code></pre><h5 id="api-호출"><strong>API 호출</strong></h5>
<pre><code>import pandas as pd
import openai
openai.organization = &quot;Organization 키를 입력&quot;
openai.api_key = &quot;발급받은 API 키를 입력.&quot;</code></pre><ul>
<li>Organization 키 확인<img src="https://velog.velcdn.com/images/boost_dev/post/3e9f92e6-b5c0-46f7-bdd1-39e25f2a5111/image.png" alt=""></li>
</ul>
<hr>
<h2 id="3-수요예측-모델">3. 수요예측 모델</h2>
<h3 id="31-1차">3.1 1차</h3>
<h4 id="311-🧾전처리">3.1.1 🧾전처리</h4>
<p><strong>공공데이터의 범위가 너무 넓어서 서비스 지역을 2호선에 한정하고 그에 따른 전처리 진행</strong></p>
<p><strong>y값 생성</strong></p>
<ul>
<li>원본 데이터에서는 승차총승객수와 하차총승객수만 나와있어서 y값으로 사용할 컬럼이 없는 상태이므로 각 일자별 승하차 변동을 나타내는 컬럼을 추가</li>
</ul>
<p><strong>전처리 과정에 역별로 csv파일을 분리하는 과정 추가</strong></p>
<p><strong>역별 csv로 변경한 이후 아래 코드로 y값 생성</strong></p>
<p><strong>분할 및 y값 생성 결과</strong></p>
<ul>
<li>전처리 이전
<img src="https://velog.velcdn.com/images/boost_dev/post/ecbef0a3-2db1-4061-a272-5d39e894e161/image.png" alt=""></li>
<li>서비스 대상 지역 축소
<img src="https://velog.velcdn.com/images/boost_dev/post/5dfb47da-6025-437c-9940-67bd63b7111f/image.png" alt=""></li>
<li>각 역별 csv 생성
<img src="https://velog.velcdn.com/images/boost_dev/post/3baaad4a-175e-4609-99fb-ae3edbedeace/image.png" alt=""></li>
<li>y값인 승하차 승객수 변동 컬럼 추가
<img src="https://velog.velcdn.com/images/boost_dev/post/1b0dc5e2-615e-48f5-a0d5-67bc6cb4fc2a/image.png" alt=""></li>
</ul>
<p><strong>Min Max Scale</strong>
<img src="https://velog.velcdn.com/images/boost_dev/post/f49c507c-a615-4598-970d-c2c089fab20c/image.png" alt=""></p>
<h3 id="32-모델-생성">3.2 모델 생성</h3>
<ul>
<li>계획 : 각 역별로 일별 승하차총승객수를 바탕으로 하차총승객수를 예측한다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/d07df164-c58b-4597-a9de-4f0968413e13/image.png" alt=""></p>
<blockquote>
<h2 id="보완사항">보완사항</h2>
<p>각 역별로 모델을 만들고 같은 기간동안 지하철 승객 변동이 가장 심한 지역 위주로 지도에 나타내는게 기존의 계획이었으나 모든 역이 요일을 기준으로 거의 일정한 요동인구 변화를 나타내기 때문에 지하철을 이용한 승객 변화를 통해 학습한 모델의 메리트가 떨어진다. </p>
<h2 id="대안">대안</h2>
<p>모든 역에 대한 수요예측 후 비교라는 초반의 방식을 버리고 하나의 역에 대한 작업 우선 진행
강남 하나만 집어서 날씨 등 다른 변수 추가 (2023-02-27)</p>
</blockquote>
<h3 id="32-2차날씨-추가">3.2 2차(날씨 추가)</h3>
<h4 id="321-전처리">3.2.1 전처리</h4>
<p><span style="color:red">이전 단계에서 사용한 데이터셋에는 변수가 너무 적기 때문에 하나의 역을 정해서 <strong>강수 여부</strong>를 추가</span></p>
<h4 id="322-학습">3.2.2 학습</h4>
<h5 id="결과">결과</h5>
<ul>
<li>서비스 항목인 우산과 비슷하게 날씨에 영향을 받는 상품의 수요 변화를 변수로 둘 필요가 있다</li>
</ul>
<blockquote>
<p><strong>날씨 추가 후</strong>
<img src="https://velog.velcdn.com/images/boost_dev/post/8fb60c5a-1f69-41c9-b507-755a7925bb40/image.png" alt="">
😅 다시 생각해보니 날씨가 지하철을 이용량에 영향을 미치지 않고 이걸 뽑아내더라도 우산 대여와 관련이 없다</p>
</blockquote>
<h3 id="33-3차따릉이-대여정보-추가">3.3 3차(따릉이 대여정보 추가)</h3>
<p><span style="color:red">예시 상품인 우산처럼 날씨의 영향을 많이 받으면서 수요의 증감이 우산 사용량과 반비례 관계를 가졌을 것이라고 예상되는 <strong>따릉이 대여기록을 추가</strong> </span>
<img src="https://velog.velcdn.com/images/boost_dev/post/e7a5476c-8ac7-4f3a-963a-d45c302de0a4/image.PNG" alt="">
따릉이 데이터 수 : 53,401,411개</p>
<h4 id="331-전처리">3.3.1 전처리</h4>
<blockquote>
<p>우산과 수요가 반비례하는 따릉이의 자료를 가져오고 y값을 승하차 변동률이 아니라 따릉이 대여량으로 바꾸기 위해 전처리 과정을 거침&#39;</p>
</blockquote>
<h5 id="서울역-승하차정보-추출">서울역 승하차정보 추출</h5>
<ul>
<li>서울역의 역별 승하차 정보와 따릉이 대여소 위치정보 간의 연결이 가장 수월하기 때문에 강남역을 대상으로 한 이전 전처리 결과를 버리고 서울역으로 다시 진행</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/5d2772b1-1a19-434d-b9eb-c8564e49c3f1/image.png" alt=""></p>
<h5 id="따릉이-대여량-추출">따릉이 대여량 추출</h5>
<blockquote>
<p>1년치 따릉이 대여정보가 하루를 기준으로 적게는 5만에서 20만개의 데이터가 모여있어서 전처리가 필수
<img src="https://velog.velcdn.com/images/boost_dev/post/7c7c9a98-a0dd-4c31-9033-f82ede25c224/image.png" alt=""></p>
</blockquote>
<ul>
<li>서울역 승하차 정보와 따릉이 대여량 join</li>
</ul>
<ul>
<li>365개의 csv 파일 중 일부 파일의 메타데이터, 인코딩 방식이 일치하지 않아서 에러 발생
<img src="https://velog.velcdn.com/images/boost_dev/post/876b8d0a-e85f-4252-a11e-606e444c09dd/image.png" alt=""></li>
</ul>
<h4 id="332-학습">3.3.2 학습</h4>
<h5 id="결과-1">결과</h5>
<ul>
<li>epoch : 80
<img src="https://velog.velcdn.com/images/boost_dev/post/567d9f02-6383-4b39-ab7b-6c6e30c71be5/image.png" alt=""></li>
<li>epoch : 800
<img src="https://velog.velcdn.com/images/boost_dev/post/465f6fed-ee14-409a-bf84-08963b0e02f9/image.png" alt=""></li>
</ul>
<blockquote>
<p>날씨 데이터가 빠진 채로 학습해서 그런거라고 생각된다</p>
</blockquote>
<h3 id="34-4차기온-풍속-추가">3.4 4차(기온, 풍속 추가)</h3>
<h4 id="341-전처리">3.4.1 전처리</h4>
<ul>
<li>3차 시기의 데이터에 기온, 풍속 등 변수 추가
<img src="https://velog.velcdn.com/images/boost_dev/post/4faa5329-023b-40d8-a3aa-9e187b9d51e7/image.png" alt=""></li>
</ul>
<h4 id="342-학습">3.4.2 학습</h4>
<h5 id="결과-2">결과</h5>
<ul>
<li>3차 시도 때보다는 loss가 감소했다
<img src="https://velog.velcdn.com/images/boost_dev/post/aa08603e-68bd-4081-8fe0-94cafba9d87a/image.png" alt=""></li>
</ul>
<h5 id="epoch-batch-size에-따른-결과-비교">epoch, batch size에 따른 결과 비교</h5>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td>epoch : 300, batch size : 20</td>
<td>epoch : 400, batch size : 20</td>
</tr>
<tr>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/095d4395-1ebf-4dcd-9001-d47374e3dff5/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/1b227b0a-86c8-45a1-ba30-0100cddd3ddf/image.png" alt=""></td>
</tr>
<tr>
<td>epoch : 400, batch size : 30</td>
<td>epoch : 500, batch size : 20</td>
</tr>
<tr>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/3b52bfce-0787-4997-9c9b-af10a3e0501e/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/e443fdce-9a86-4a33-a56a-0174cc600f3f/image.png" alt=""></td>
</tr>
<tr>
<td>epoch : 600, batch size : 12</td>
<td>epoch : 700, batch size : 30</td>
</tr>
<tr>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/d3d802ef-96e6-4cc7-8985-2ad9045b6d95/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/f24a2a06-d159-47ab-86c5-41c0c6144a56/image.png" alt=""></td>
</tr>
<tr>
<td>epoch : 800, batch size : 15</td>
<td>epoch : 4000, batch size : 64</td>
</tr>
<tr>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/9c3f1390-29fa-46fd-b29d-9d1b4947e6da/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/1bd08963-c708-4140-a60a-5450a3e1645a/image.png" alt=""></td>
</tr>
</tbody></table>
<h5 id="batch-size">batch size</h5>
<blockquote>
<p>각 사이즈 테스트시 에포크는 1000<br>|||
|---|---|
|15|20|
|<img src="https://velog.velcdn.com/images/boost_dev/post/ab06cc2c-1ff5-4657-9ff0-12d69854254d/image.png" alt="">|<img src="https://velog.velcdn.com/images/boost_dev/post/b37ae3aa-972d-4a92-861b-2d5d7bbf8080/image.png" alt="">|
|30|64|
|<img src="https://velog.velcdn.com/images/boost_dev/post/9dbe1f09-b186-4fb1-8508-234356145144/image.png" alt="">|<img src="https://velog.velcdn.com/images/boost_dev/post/71615817-29d3-4820-8d3e-70c168ed92a3/image.png" alt="">|</p>
</blockquote>
<ul>
<li>16 단위로 batch 사이즈 비교
<img src="https://velog.velcdn.com/images/boost_dev/post/4a2197eb-f4a1-4a6c-b1a5-45522c3eac0b/image.png" alt=""></li>
<li>5 단위로 비교
<img src="https://velog.velcdn.com/images/boost_dev/post/977179e2-71a9-4a1c-bfff-b83db24fc8d7/image.png" alt=""></li>
</ul>
<p>--</p>
<h1 id="😨오류">😨오류</h1>
<h2 id="fastapi💨">FastAPI💨</h2>
<h3 id="1-invalid-arguments-encoding-sent-to-create_engine">1. Invalid argument(s) &#39;encoding&#39; sent to create_engine()</h3>
<blockquote>
<p>🤔원인
기존까지 잘 작동되던 코드인데, 포맷 후 버전이 달라지면서 발생한 에러</p>
</blockquote>
<blockquote>
<p>😊해결
<img src="https://velog.velcdn.com/images/boost_dev/post/6ae8fd27-a782-447f-a6b6-82effd12694f/image.png" alt=""></p>
</blockquote>
<ul>
<li>버전이 업그레이드 되면서 encoding을 명시하는 것이 레거시가 되었다.</li>
<li>위 코드에서 encoding=”utf-8” 부분만 지우면 해결 된다.</li>
</ul>
<h3 id="mysql-로그인-시-mycnf-파일-인식-불가">MySQL 로그인 시 my.cnf 파일 인식 불가</h3>
<blockquote>
<p>🤔원인
Dockerfile 에 my.cnf 경로를 지정하지 않아 발생한 오류</p>
</blockquote>
<blockquote>
<p>😊해결
<img src="https://velog.velcdn.com/images/boost_dev/post/4c66982e-0a80-45fb-9c61-dcf73ebbfff1/image.png" alt=""></p>
</blockquote>
<ul>
<li>사진과 같이 경로만 추가하면 해결</li>
</ul>
<h3 id="sqlalchemyexcinvalidrequesterror-one-or-more-mappers-failed-to-initialize">sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize</h3>
<blockquote>
<p>🤔원인</p>
</blockquote>
<ul>
<li>Postman에서 값 전달 시 발생하는 에러</li>
<li>Article 테이블의 back_populates에 적힌 속성 값과 User 테이블의 변수 명이 달라 발생하는 에러</li>
</ul>
<blockquote>
<p>😊해결
<img src="https://velog.velcdn.com/images/boost_dev/post/b0ed58d2-e5cb-4af2-8704-6a1f132f984e/image.png" alt=""><img src="https://velog.velcdn.com/images/boost_dev/post/464f38d8-0dd4-4c08-bcd5-c40dd43444db/image.png" alt=""></p>
</blockquote>
<ul>
<li>변수 명과 속성 값을 동일하게 변경하면 해결
<img src="https://velog.velcdn.com/images/boost_dev/post/3d946a9e-4569-440b-b2f0-3e004ab25918/image.png" alt=""></li>
</ul>
<h2 id="docker🐋">Docker🐋</h2>
<h3 id="다른-컨테이너-실행-오류">다른 컨테이너 실행 오류</h3>
<blockquote>
<p>🤔원인
git 업로드 시 생성되는 .idea 폴더와 충돌해 기존 컨테이너가 실행되어 발생하는 오류</p>
</blockquote>
<blockquote>
<p>😊해결
.idea 폴더 삭제 후 명령어를 재 실행하면 해결
<img src="https://velog.velcdn.com/images/boost_dev/post/9b056f7b-ad0b-4203-9282-8bb498bca23e/image.png" alt=""></p>
</blockquote>
<h3 id="db-테이블-미-생성">DB 테이블 미 생성</h3>
<blockquote>
<p>🤔원인
router 연결 코드가 제대로 입력되지 않은 경우 발생하는 에러</p>
</blockquote>
<blockquote>
<p>😊해결</p>
</blockquote>
<ul>
<li>main.py → router(schema) → repository → model 순으로 router 연결이 되지 않은 부분이 있는지 확인하고, 빠진 부분에 추가로 코드를 입력하면 해결</li>
<li>이번에 발생한 에러는 main.py 에 umbrella, stand의 연결 코드가 없어서 발생
<img src="https://velog.velcdn.com/images/boost_dev/post/6b18c89a-7421-49b5-a725-a0e307d5e7cb/image.png" alt=""></li>
<li>umbrella, stand 연결 코드를 추가한 뒤 docker compose up 명령어를 다시 입력하면 테이블이 생성
<img src="https://velog.velcdn.com/images/boost_dev/post/c440dd72-fc77-4e54-9109-9c1cb5021b60/image.png" alt=""></li>
</ul>
<h3 id="도커-허브-이미지-업로드시-아래-문제-발생">도커 허브 이미지 업로드시 아래 문제 발생</h3>
<p>  <img src="https://velog.velcdn.com/images/boost_dev/post/12c8137a-2e63-4374-9528-5ec10fb39805/image.png" alt=""></p>
<blockquote>
<p>🤔원인 
버전 이슈로 node 16.0 이하의 버전에서는 html 태그를 지원하지 않는다</p>
</blockquote>
<blockquote>
<p>😊해결 
버전을 올려서 사용해준다. 
  물론 이 과정에서 다른 문제가 발생하지 않았는지 확인하는건 당연</p>
</blockquote>
<h2 id="vue">Vue</h2>
<h3 id="unhandled-runtime-error">Unhandled Runtime Error</h3>
<blockquote>
<p>🤔원인
Table 내부 구조를 잘못 짜 발생한 오류</p>
</blockquote>
<blockquote>
<p>😊해결
<code>&lt;td&gt;</code> 태그만 사용해 구성했던 테이블을 <code>&lt;tr&gt;</code> 태그 내부에 <code>&lt;td&gt;</code> 태그를 넣어 구성하자 해결</p>
</blockquote>
<h2 id="yolov5">YOLOv5</h2>
<h3 id="keyerror-path">KeyError: &#39;path&#39;</h3>
<blockquote>
<p>🤔원인
아래 코드를 추가해서 yaml 파일의 내용을 변경하는 과정에서 원래 있던 path:가 사라져서 발생한 문제</p>
</blockquote>
<blockquote>
<p>😊해결
yaml을 수정해주자</p>
</blockquote>
<h3 id="전처리-과정에서-한-개의-파일만-제거하고-다음-파일은-찾지-못하는-문제-발생">전처리 과정에서 한 개의 파일만 제거하고 다음 파일은 찾지 못하는 문제 발생</h3>
<blockquote>
<p>🤔발생 코드</p>
</blockquote>
<pre><code>train_label_list = glob(&#39;C:/Users/AIA/PycharmProjects/datasets/umb/train/labels/*.txt&#39;)
for path in train_label_list:
  print(path)
  f = open(path, &quot;rt&quot;)
  umbrella = []
  for line in f:
    if line.startswith(&quot;76&quot;):
      umbrella.append(line)
  f.close()
  if len(umbrella) == 0 : # person 이 없으면 삭제한다
    os.remove(path)
    os.remove(path.replace(&quot;.txt&quot;, &quot;.jpg&quot;).replace(&quot;/labels/&quot;,&quot;/images/&quot;))
  else :
    f2 = open(path, &#39;wt&#39;)
    for p in umbrella:
      f2.write(p.replace(&quot;76&quot;,&quot;0&quot;,1))
    f2.close()  </code></pre><blockquote>
<p>😊해결
12번 라인의 replace(&quot;/labels/&quot;,&quot;/images/&quot;)를 replace(&quot;labels&quot;,&quot;images&quot;)로 바꿔서 다시 실행</p>
</blockquote>
<h3 id="전처리-후-학습시-nc-이외의-범위에서-키에러-발생">전처리 후 학습시 nc 이외의 범위에서 키에러 발생</h3>
<blockquote>
<p>🤔원인
발생 당시 train과정에서는 전처리가 완료되어 모든 네임클래스가 0으로 정리된 파일을 사용했지만 valid 과정에서는 전처리가 제대로 이뤄지지 않은 파일이 적용돼서 발생한 문제</p>
</blockquote>
<blockquote>
<p>😊해결</p>
</blockquote>
<ul>
<li>에러지점 네임클래스 확인
<img src="https://velog.velcdn.com/images/boost_dev/post/a9398488-2c79-4e29-8b31-797ee9eba80e/image.png" alt=""></li>
<li>0 이외의 다른 nc가 들어온걸 볼 수 있다
<img src="https://velog.velcdn.com/images/boost_dev/post/bfd6925e-8f0f-4c3b-b828-9299bd85c09c/image.png" alt=""></li>
<li>전처리 코드를 valid 폴더 내부에도 실행해준다<pre><code># 에러 발생 
for cls, *xyxy in filtered_labels.tolist():
    metadata.append({
        &#39;label&#39;: f&#39;{self.class_names[int(cls)]}-gt&#39;,
# 변경
for cls, *xyxy in filtered_labels.tolist():
    metadata.append({
        &#39;label&#39;: f&#39;{self.class_names}-gt&#39;,</code></pre></li>
</ul>
<h2 id="kogpt2">KoGPT2</h2>
<h3 id="runtimeerror-expected-all-tensors-to-be-on-the-same-device-but-found-at-least-two-devices">RuntimeError: Expected all tensors to be on the same device, but found at least two devices</h3>
<blockquote>
<p>🤔원인
텐서 별로 사용하는 디바이스가 달라서 발생</p>
</blockquote>
<blockquote>
<p>😊해결</p>
</blockquote>
<ul>
<li><p>사용중인 디바이스 확인
<img src="https://velog.velcdn.com/images/boost_dev/post/5abd67a6-ef65-42dc-bc04-ef2873b96268/image.png" alt=""></p>
<ul>
<li>model과 token_ids의 디바이스가 다른 것을 확인</li>
</ul>
</li>
<li><p>동일한 디바이스를 사용하게 해서 해결
<img src="https://velog.velcdn.com/images/boost_dev/post/8cc2cbe3-69a9-4535-b274-bad7c5bd2d74/image.png" alt=""></p>
</li>
</ul>
<h3 id="typeerror-cant-convert-cuda0-device-type-tensor-to-numpy-use-tensorcpu-to-copy-the-tensor-to-host-memory-first">TypeError: can&#39;t convert cuda:0 device type tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first.</h3>
<blockquote>
<p>🤔원인
GPU에 할당된 텐서를 넘파이 배열로 변환하려 해서 발생</p>
</blockquote>
<blockquote>
<p>😊해결
이전 에러 해결을 위해 CUDA로 사용중이던 텐서의 디바이스를 넘파이 배열 사용 전에 cpu로 바꾼다.</p>
</blockquote>
<h3 id="valueerror-connection-error-and-we-cannot-find-the-requested-files-in-the-cached-path-please-try-again-or-make-sure-your-internet-connection-is-on">ValueError: Connection error, and we cannot find the requested files in the cached path. Please try again or make sure your Internet connection is on.</h3>
<blockquote>
<p>😊해결
재실행하면 된다</p>
</blockquote>
<h2 id="수요예측-모델">수요예측 모델</h2>
<h3 id="typeerror-numpy-boolean-subtract">TypeError: numpy boolean subtract,</h3>
<blockquote>
<p>🤔원인
MinMaxScale 과정에서 String값을 가지는 컬럼까지 포함돼서 발생한 문제</p>
</blockquote>
<blockquote>
<p>😊해결
int값만 사용될 수 있도록 바꿔준다</p>
</blockquote>
<h3 id="전처리-과정중-키에러가-반복적으로-발생">전처리 과정중 키에러가 반복적으로 발생</h3>
<blockquote>
<p>🤔원인
전체 csv 파일 중 일부 파일의 메타데이터, 인코딩 방식이 일치하지 않아서 발생</p>
</blockquote>
<blockquote>
<p>😊해결
따로 정해진 방식은 없고 아래 코드를 통해 해결</p>
</blockquote>
<ul>
<li>이번 에러는 아래 코드로 해결했다<pre><code>dirs = os.listdir(&#39;.\\data\\tpss&#39;)
for i in dirs:  # 월
  dir = os.listdir(f&#39;.\\data\\tpss\\{i}&#39;)
  for j in dir:  # 일
      try:
          df = pd.read_csv(f&#39;.\\data\\tpss\\{i}\\{j}&#39;, encoding=&#39;cp949&#39;)
          if df.columns[2] != &#39;시작_대여소_ID&#39;:
              df.rename(columns={df.columns[2]: &#39;시작_대여소_ID&#39;}, inplace=True)
              df.to_csv(f&#39;.\\data\\tpss\\{i}\\{j}&#39;)
      except UnicodeDecodeError as df:
          df = pd.read_csv(f&#39;.\\data\\tpss\\{i}\\{j}&#39;, encoding=&#39;utf8&#39;)
          if df.columns[2] != &#39;시작_대여소_ID&#39;:
              df.rename(columns={df.columns[2]: &#39;시작_대여소_ID&#39;}, inplace=True)
              df.to_csv(f&#39;.\\data\\tpss\\{i}\\{j}&#39;)
      print(f&#39;{j}일 종료&#39;)  </code></pre></li>
</ul>
<h3 id="valueerror">ValueError:</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/bcdab0fb-6aa0-42d9-b19b-c163cd65f24f/image.png" alt=""></p>
<blockquote>
<p>🤔원인
확인 결과 각 역 별로 csv파일을 나누는 과정에서 딱 역의 종류만큼 인덱스가 다른 것을 알 수 있다
  <img src="https://velog.velcdn.com/images/boost_dev/post/2582b24f-02db-46ff-8ae1-726fe2a8b659/image.png" alt=""></p>
</blockquote>
<blockquote>
<p>😊해결
모든 역을 한 번에 비교하기보다 각 역별 이동인구 변동율을 예측하고 이를 비교하는 방향으로 변경</p>
</blockquote>
<h3 id="npm-run-serve-실행시-noscript만-나오는-경우">npm run serve 실행시 noscript만 나오는 경우</h3>
<p>  <img src="https://velog.velcdn.com/images/boost_dev/post/feb76b39-e1fc-4ec6-870b-b5831cbda0d7/image.png" alt=""></p>
<blockquote>
<p>🤔원인
발생 당시 main.js의 내용은 아래처럼 작성됐는데</p>
</blockquote>
<pre><code>import { createApp } from &#39;vue&#39;
import App from &#39;./App.vue&#39;
import { router } from &#39;@/router/index&#39;

createApp(App).use(router).mount(&#39;#src&#39;)</code></pre><p>여기서 createApp() 메소드는 인자로 전달된 DOM element를 찾아, 해당 DOM element를 Vue 인스턴스에 마운트(Mount)한다. 위에서 DOM element로 전달된 #src가 잘못된 선택자라서 발생한 문제다. </p>
<blockquote>
<p>😊해결</p>
</blockquote>
<ul>
<li>원래는 default인 #src가 맞는 인자지만 이번 프로젝트에서는 App.vue를 통해 전달된 파일들을 보여주는 형태라서 #src를 #app으로 변경해줘야 한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Oracle) 프로젝트 배포]]></title>
            <link>https://velog.io/@boost_dev/Oracle-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@boost_dev/Oracle-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Tue, 28 Mar 2023 05:21:29 GMT</pubDate>
            <description><![CDATA[<h1 id="1-linux-패키지-업데이트">1. Linux 패키지 업데이트</h1>
<p><code>sudo apt update -y &amp;&amp; sudo apt upgrade -y</code></p>
<blockquote>
<ul>
<li>update : 최신 버전 유무를 확인해주는 명령어</li>
</ul>
</blockquote>
<ul>
<li>upgrade : 해당 패키지들을 다운로드 받고 설치</li>
</ul>
<h1 id="2-node-설치">2. node 설치</h1>
<ul>
<li>nvm 설치
<code>curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash</code></li>
<li>위의 명령어로 bash 설정을 바꿨으면 아래 명령어도 실행
<code>source ~/.bashrc</code></li>
<li>npm 설치
<code>npm install</code></li>
<li>설치 확인
<code>node</code>
<code>npm</code></li>
<li>yarn 설치
<code>npm install --global yarn</code></li>
</ul>
<h1 id="3-프론트엔드--백앤드-실행">3. 프론트엔드 / 백앤드 실행</h1>
<ul>
<li>usr 경로에 깃 클론
<code>cd usr</code>
<code>git clone [repo 주소]</code><blockquote>
<p>맨 처음 putty로 접속을 하면 기본적으로 home/ubuntu 경로에 있게되는데, 이 안에 클론을 받으면 나중에 배포할 때 500 에러가 발생할 수 있다. 그러니 꼭 <strong>상위의 usr 폴더에 들어가서</strong> clone</p>
</blockquote>
</li>
<li>front, back 폴더에서 각각 yarn install을 해서 패키지들을 설치
<code>yarn install</code>
<code>yarn start</code></li>
<li>db 정보 등 기존에 env 파일로 관리하던 내용들도 .env 파일에 작성해준 뒤 실행을 확인
<code>cd [.env 파일 만들 경로]</code>
<code>vi .env</code>
<code>파일 내용 작성 후 esc -&gt; :wq</code></li>
</ul>
<h1 id="4-배포">4. 배포</h1>
<p>nginX와 Docker를 사용하니까 설치해준다
<code>npm install --global nginx</code>
<code>npm install --global docker</code></p>
<h2 id="41-서버-배포">4.1 서버 배포</h2>
<ul>
<li>백엔드 폴더에서 서버를 실행
pm2를 사용하는 경우는 <code>pm2 --name back-server start yarn -- start</code>를 사용하지만 
우리는 docker를 사용해서 <code>docker compose up</code>을 하면 된다.</li>
</ul>
<h2 id="42-프론트-배포">4.2 프론트 배포</h2>
<ul>
<li>front 폴더로 이동한 후 build
<code>yarn build</code> 또는 <code>npm run build</code></li>
</ul>
<blockquote>
<p>빌드가 완료되면 nginx 세팅을 할 때 필요하니까 build 폴더로 이동한 뒤 현재 경로를 기록한다. </p>
</blockquote>
<ul>
<li><p>pwd 명령어로 나온 경로를 복사해서 따로 적어놓아야 한다.
<code>cd build</code>
<code>pwd</code></p>
</li>
<li><p>nginx 설정 파일을 수정
<code>sudo vi /etc/nginx/sites-available/default</code></p>
</li>
</ul>
<p>root /var/www/html; 부분을 아까 기록한 폴더 위치로 바꿔주면 된다. 이 때 오타가 있거나 뒤에 세미콜론(;)을 붙이지 않으면 오류가 나니까 주의. (앞의 /도 지우면 안된다.)
예) root /usr/KORrection/front/build;
root 위에서 기록한 build 폴더 위치;</p>
<ul>
<li>nginx 재시작
<code>sudo systemctl reload nginx</code>
<code>sudo service nginx start</code></li>
</ul>
<p><strong>클론 이후 내용은 지금 진행중인 팀 프로젝트 마무리되면 그때 직접 해보면서 다시 정리한다</strong></p>
<hr>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://doooodle932.tistory.com/116">혼자공부끄적끄적</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[FastAPI) 경로 매개변수]]></title>
            <link>https://velog.io/@boost_dev/FastAPI-%EA%B2%BD%EB%A1%9C-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98-mm176goc</link>
            <guid>https://velog.io/@boost_dev/FastAPI-%EA%B2%BD%EB%A1%9C-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98-mm176goc</guid>
            <pubDate>Mon, 27 Mar 2023 13:49:53 GMT</pubDate>
            <description><![CDATA[<h1 id="경로-매개변수">경로 매개변수?</h1>
<blockquote>
<p>파이썬 포맷 문자열이 사용하는 동일한 문법으로 &quot;매개변수&quot; 또는 &quot;변수&quot;를 경로에 선언할 수 있다</p>
</blockquote>
<pre><code>예시)
from fastapi import FastAPI

app = FastAPI()

@app.get(&quot;/items/{item_id}&quot;)
async def read_item(item_id):
    return {&quot;item_id&quot;: item_id}</code></pre><ul>
<li>경로 매개변수 item_id의 값은 함수의 item_id 인자로 전달되서 <code>http://127.0.0.1:8000/items/foo</code>로 이동하면 <code>{&quot;item_id&quot;:&quot;foo&quot;}</code>라는 응답을 볼 수 있다</li>
</ul>
<h2 id="타입이-있는-매개변수">타입이 있는 매개변수</h2>
<p>위의 코드에서 파이썬 표준 타입 어노테이션을 사용하여 함수에 있는 경로 매개변수의 타입을 선언할 수도 있다.</p>
<pre><code>@app.get(&quot;/items/{item_id}&quot;)
async def read_item(item_id:int):</code></pre><h2 id="데이터-변환">데이터 변환</h2>
<p><code>http://127.0.0.1:8000/items/3</code>을 열면 <code>{&quot;item_id&quot;:3}</code>라는 응답을 볼 수 있다</p>
<ul>
<li>이때 함수가 받은(반환도 하는) 값은 문자열 &quot;3&quot;이 아니라 파이썬 int 형인 3으로 타입 선언을 하면 FastAPI는 자동으로 요청을 &quot;파싱&quot;한다.</li>
</ul>
<h2 id="데이터-검증">데이터 검증</h2>
<p>경로의 타입을 int로 설정했으므로 브라우저에서 경로를 str 등으로 주게 되면 아래와 같은 에러가 발생한다 (이는 int와 float 사이에도 동일)</p>
<pre><code>{
    &quot;detail&quot;: [
        {
            &quot;loc&quot;: [
                &quot;path&quot;,
                &quot;item_id&quot;
            ],
            &quot;msg&quot;: &quot;value is not a valid integer&quot;,
            &quot;type&quot;: &quot;type_error.integer&quot;
        }
    ]
}</code></pre><ul>
<li>파이썬 타입 선언을 하면 FastAPI는 데이터 검증하는데 오류는 검증을 통과하지 못한 지점도 정확하게 명시한다.<ul>
<li>이는 API와 상호 작용하는 코드를 개발하고 디버깅하는 데 매우 유용</li>
</ul>
</li>
</ul>
<h2 id="순서-문제">순서 문제</h2>
<p>경로 동작을 만들때 고정 경로를 갖고 있는 상황들을 맞닦뜨릴 수 있다.
/users/me처럼, 현재 사용자의 데이터를 가져오려고 할 때, 사용자 ID를 이용해 특정 사용자의 정보를 가져오는 경로 /users/{user_id}도 있으면
경로 동작은 순차적으로 평가되기 때문에 /users/{user_id} 이전에 /users/me를 먼저 선언해야 한다.</p>
<pre><code>from fastapi import FastAPI

app = FastAPI()


@app.get(&quot;/users/me&quot;)
async def read_user_me():
    return {&quot;user_id&quot;: &quot;the current user&quot;}


@app.get(&quot;/users/{user_id}&quot;)
async def read_user(user_id: str):
    return {&quot;user_id&quot;: user_id}</code></pre><p>그렇지 않으면 /users/{user_id}는 매개변수 user_id의 값을 &quot;me&quot;라고 &quot;생각하여&quot; /users/me도 연결한다.</p>
<p>사전정의 값부터 정리 다시</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[FastAPI) 시작하기]]></title>
            <link>https://velog.io/@boost_dev/FastAPI-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@boost_dev/FastAPI-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 27 Mar 2023 13:41:33 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>main.py의 형식과 실행 방법, 대화형 또는 대안 API 문서를 통한 확인은 이전 게시글 참고</p>
</blockquote>
<h1 id="openapi">OpenAPI</h1>
<blockquote>
<p>FastAPI는 API를 정의하기 위한 OpenAPI 표준을 사용하여 여러분의 모든 API를 이용해 &quot;스키마&quot;를 생성한다</p>
</blockquote>
<h2 id="스키마">스키마</h2>
<h3 id="api-스키마">API &quot;스키마&quot;</h3>
<p>이 경우, OpenAPI는 API의 스키마를 어떻게 정의하는지 지시하는 규격
이 스키마 정의는 API 경로, 가능한 매개변수 등을 포함</p>
<h3 id="데이터-스키마">데이터 &quot;스키마&quot;</h3>
<p>&quot;스키마&quot;라는 용어는 JSON처럼 어떤 데이터의 형태를 나타낼 수도 있는데
이러한 경우 JSON 속성, 가지고 있는 데이터 타입 등을 뜻합니다.</p>
<h3 id="openapi와-json-스키마">OpenAPI와 JSON 스키마</h3>
<p>OpenAPI는 API에 대한 API 스키마를 정의한다. 또한 이 스키마에는 JSON 데이터 스키마의 표준인 JSON 스키마를 사용하여 API에서 보내고 받은 데이터의 정의(또는 &quot;스키마&quot;)를 포함한다.</p>
<h3 id="openapijson-확인">openapi.json 확인</h3>
<p>가공되지 않은 OpenAPI 스키마가 어떻게 생겼는지 궁금하다면, FastAPI는 자동으로 API의 설명과 함께 JSON (스키마)를 생성하니까 로컬에서 앱이 실행되는 url 뒤에 <code>/openapi.json</code>을 추가한 <code>http://127.0.0.1:8000/openapi.json</code>에서 직접 볼 수 있다</p>
<h2 id="openapi의-용도">OpenAPI의 용도</h2>
<p>OpenAPI 스키마는 포함된 두 개의 대화형 문서 시스템을 제공하고 OpenAPI의 모든 것을 기반으로 하는 수십 가지 대안이 있다. </p>
<ul>
<li>FastAPI로 빌드한 애플리케이션에 이러한 대안을 쉽게 추가 할 수 있다.</li>
<li>API와 통신하는 클라이언트를 위해 코드를 자동으로 생성하는 데도 사용할 수 있는데 그 예로 프론트엔드, 모바일, IoT 애플리케이션이 있다.</li>
</ul>
<h2 id="단계별-요약">단계별 요약</h2>
<h3 id="1단계-import-fastapi">1단계: <code>import FastAPI</code></h3>
<p><code>from fastapi import FastAPI</code></p>
<ul>
<li>FastAPI는 API에 대한 모든 기능을 제공하는 파이썬 클래스다<ul>
<li>FastAPI는 Starlette를 직접 상속하는 클래스라서 FastAPI로 Starlette의 모든 기능을 사용할 수 있다.</li>
</ul>
</li>
</ul>
<h3 id="2단계-fastapi-인스턴스-생성">2단계: FastAPI &quot;인스턴스&quot; 생성</h3>
<pre><code>from fastapi import FastAPI
인스턴스명 = FastAPI()</code></pre><ul>
<li>app 변수는 FastAPI 클래스의 &quot;인스턴스&quot;가 돼서 모든 API를 생성하기 위한 상호작용의 주요 지점이 될 것이다.</li>
<li>이 app은 다음 명령에서 uvicorn이 참조하고 것과 동일하다</li>
</ul>
<h3 id="3단계-경로-동작-생성">3단계: 경로 동작 생성</h3>
<pre><code>from fastapi import FastAPI
인스턴스명 = FastAPI()
@인스턴스명.get(&quot;/&quot;)</code></pre><h4 id="31-경로">3.1 &quot;경로&quot;</h4>
<blockquote>
<p>일반적으로 &quot;앤드포인트&quot; 또는 &quot;라우트&quot;라고도 불리며 API를 빌드하는 동안 &quot;경로&quot;는 &quot;관심사&quot;와 &quot;리소스&quot;를 분리하는 주요 방법이다</p>
</blockquote>
<ul>
<li>여기서 &quot;경로&quot;는 첫 번째 /에서 시작하는 URL의 마지막 부분이므로
<code>https://example.com/items/foo</code>라는 URL에서의 경로는 <code>/items/foo</code>다</li>
</ul>
<h4 id="32-동작operation">3.2 &quot;동작(Operation)&quot;</h4>
<blockquote>
<p>HTTP &quot;메소드&quot; 중 하나</p>
</blockquote>
<ul>
<li>POST, GET, PUT, DELETE 외에도 OPTIONS, HEAD, PATCH, TRACE도 있다</li>
<li>HTTP 프로토콜에서는 이러한 &quot;메소드&quot;를 하나(또는 이상) 사용하여 각 경로와 통신할 수 있다</li>
</ul>
<h4 id="something"><code>@something</code></h4>
<blockquote>
<p>데코레이터라고 부르며 아래 있는 함수를 받고 그걸 이용해 무언가를 한다</p>
</blockquote>
<ul>
<li><code>@인스턴스.get(&quot;/&quot;)</code> FastAPI에게 아래 함수가 경로 /에 해당하는 get 동작하라고 알려주는데 이게 <strong>경로 동작 데코레이터</strong>다</li>
</ul>
<h3 id="4단계-경로-동작-함수-정의">4단계: 경로 동작 함수 정의</h3>
<pre><code>from fastapi import FastAPI
인스턴스명 = FastAPI()

@인스턴스명.get(&quot;/&quot;)
async def root():</code></pre><blockquote>
<p>위에서 먼저 정리한대로
경로는 /
동작은 get 등
함수는 &quot;데코레이터&quot; 아래에 있는 함수 (@app.get(&quot;/&quot;) 아래).</p>
</blockquote>
<ul>
<li>위의 경우 async함수를 사용했는데 async def 대신 일반 함수로 정의할 수 있다</li>
</ul>
<h3 id="5단계-콘텐츠-반환">5단계: 콘텐츠 반환</h3>
<pre><code>from fastapi import FastAPI
인스턴스명 = FastAPI()

@인스턴스명.get(&quot;/&quot;)
async def root():
    return {&quot;콘텐츠명&quot;:&quot;콘텐츠&quot;}</code></pre><h2 id="요약">요약</h2>
<ul>
<li>FastAPI 임포트.</li>
<li>인스턴스 생성.</li>
<li>경로 동작 데코레이터 작성.</li>
<li>경로 동작 함수 작성.</li>
<li>개발 서버 <strong>실행</strong>(<code>uvicorn main:app --reload</code>)</li>
</ul>
<hr>
<h1 id="참고">참고</h1>
<p><a href="https://fastapi.tiangolo.com/ko/tutorial/first-steps/">FastAPI, 자습서-사용자 안내서</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드 인터뷰 준비(Learning)]]></title>
            <link>https://velog.io/@boost_dev/Temp-Title</link>
            <guid>https://velog.io/@boost_dev/Temp-Title</guid>
            <pubDate>Mon, 27 Mar 2023 12:27:42 GMT</pubDate>
            <description><![CDATA[<h1 id="인공-지능의-역사">인공 지능의 역사</h1>
<blockquote>
<p>알랜 튜링(1965) - 마민 민스키 (1969, XOR 문제, 멀티 레이어 필요) - 겨울1 (1974 ~ 1980) - 힌튼 (1986, 오류 역전파로 멀티 레이어 문제 해결) -  겨울2 (Vanishing gradient problem, 1987 ~ 1993) - 힌튼 (엑티베이션 함수 relu) - 알파고 (2016)</p>
</blockquote>
<hr>
<h1 id="데이터data">데이터(Data)</h1>
<blockquote>
<p><strong>Data has a categorical, numeric.</strong></p>
</blockquote>
<ul>
<li>카테고리(categorical) = 이산형 = norminal + ordinal = 정수형</li>
<li>숫자형(numeric) = 연속형 = ratio + interval = 실수형<blockquote>
</blockquote>
</li>
<li><em>데이터 분석에는 두 가지의 접근방법이 있다.*</em></li>
<li>확증적 데이터 분석(CDA: Confirmatory Data Analysis) = 추론통계 = 가설 -&gt; .. -&gt; 특정 사례 예측 = 연역 = 가정된 전제</li>
<li>탐색적 데이터 분석(EDA: Exploratory Data Analysis) = 기술통계 = 데이터 -&gt; .. -&gt; 모델 = 귀납 = 개인적 경험</li>
</ul>
<h2 id="데이터셋">데이터셋</h2>
<blockquote>
<p>train, test, validation set 세개로 나눈다.</p>
</blockquote>
<hr>
<h1 id="머신러닝machine-learning">머신러닝(Machine Learning)</h1>
<blockquote>
<p>머신러닝은 통계와 딥러닝의 집합이다.
머신러닝과 딥러닝의 차이점은 신경망의 유무이다.</p>
</blockquote>
<h2 id="통계와-머신러닝의-차이">통계와 머신러닝의 차이</h2>
<blockquote>
<p>기술통계 - 추론통계 = 학습(Learning)</p>
</blockquote>
<hr>
<h1 id="머신러닝-절차">머신러닝 절차</h1>
<h2 id="데이터-로드">데이터 로드</h2>
<h2 id="데이터-분석">데이터 분석</h2>
<blockquote>
<h3 id="데이터-이해를-위한-데이터-분석"><strong>데이터 이해를 위한 데이터 분석</strong></h3>
<p>(= 데이터 전처리를 위한 데이터 분석)</p>
</blockquote>
<ul>
<li>변수</li>
<li>설명</li>
<li>데이터</li>
<li>테이터 타입</li>
<li>종속 변수</li>
<li>특성 생성</li>
<li>특성 삭제</li>
<li>결측값 처리</li>
<li>정규 분포로 변환</li>
<li>이상값 처리</li>
<li>원핫 인코딩</li>
<li>라벨 인코딩</li>
</ul>
<h2 id="데이터-전처리">데이터 전처리</h2>
<blockquote>
<p>데이터 수집 20% + 데이터 전처리 60% 시간 사용 (백조 발 구르기)
특성 생성 (데이터 프레임)
특성 삭제 (데이터 프레임)
변수 나누기 (독립 변수와 종속 변수 나누기)
수(치)가 아닌 데이터를 수(치)로 변환
결측값 처리
정규 분포로 변환
이상값 처리
원핫 인코딩
구간화
라벨 인코딩
특성 스케일링
차원 축소
커스텀 데이터 변환
군집화</p>
</blockquote>
<h2 id="모델-생성">모델 생성</h2>
<h2 id="모델-학습">모델 학습</h2>
<blockquote>
<h3 id="회귀">회귀</h3>
</blockquote>
<h3 id="분류">분류</h3>
<h2 id="모델-검증">모델 검증</h2>
<blockquote>
<p>회귀나 분류냐에 따라 달라진다</p>
</blockquote>
<h2 id="모델-예측">모델 예측</h2>
<hr>
<h1 id="회귀분류군집">회귀/분류/군집</h1>
<ul>
<li>회귀 (지도 학습: 답 있는 데이터로 학습)</li>
<li>분류 (지도 학습)</li>
<li>군집 (비지도 학습: 답 없는 데이터로 학습, 데이터 전처리)</li>
</ul>
<h2 id="회귀-1">회귀</h2>
<blockquote>
<p>특성을 통해 수(치) (실수) (연속형)를 예측한다. 수(치)를 예측하려면 회귀를 사용</p>
</blockquote>
<ul>
<li>회귀는 확률을 예측하는 것이 아니다. 회귀는 출력는 연속성이 있고, 그 연속성 중에 어디에 점을 찍을지 결정하는 문제다</li>
</ul>
<h3 id="타겟-변수-y-가-연속형-변수continuous-variable인-경우">타겟 변수 Y 가 연속형 변수(continuous variable)인 경우</h3>
<p>연속형 변수: 연속 범위내에서 임의의 값을 가질 수 있는 변수 (가질 수 있는 값이 무한정)
설명 변수와 종속 변수의 관계를 선형으로 모델링하냐 비선형으로 모델링하냐에 따라 선형과 비선형으로 나누어진다</p>
<h4 id="1-선형-회귀-모델-선형-수식이-직선">1) 선형 회귀 모델 (선형: 수식이 직선)</h4>
<blockquote>
<h5 id="1-선형-회귀-linearregression-모델">(1) 선형 회귀 (LinearRegression) 모델</h5>
<p>변수들이 서로 선형적으로 연결되어 있는 경우, 오차(실제값 과 예측값의 차이)가 가장 작은 직선인 최저적합선을 찾는다 (= 직선의 수식(모델)에서 w 와 b를 찾는다)</p>
</blockquote>
<table>
<thead>
<tr>
<th>1</th>
<th>2</th>
<th>3</th>
</tr>
</thead>
<tbody><tr>
<td>계수(coefficient)</td>
<td>기울기</td>
<td>가중치(weight)</td>
</tr>
<tr>
<td>상수항(intercept)</td>
<td>y절편</td>
<td>편향(bias)</td>
</tr>
</tbody></table>
<blockquote>
</blockquote>
<ul>
<li>특이치에 영향을 받기 쉬우므로 빅데이터 집합을 분석하는 데 사용해서는 안 된다.</li>
<li>핵심키워드: 선형회귀(Linear Regression) 가설(Hypothesis) 비용함수(Cost function)<h5 id="2-라쏘-lasso-모델">(2) 라쏘 (Lasso) 모델</h5>
<blockquote>
<p>리지 회귀와 같이 모델의 복잡성을 줄여주는 또 다른 정규화 기법이다.</p>
</blockquote>
</li>
<li>회귀 계수의 절대 사이즈를 금지함으로써 복잡성을 줄인다.</li>
<li>리지 회귀와는 다르게 아예 계수 값을 0에 가깝게 만든다.그 장점은 기능 선택을 사용할 수 있다는 것이다. </li>
<li>데이터 집합에서 기능 세트를 선택하여 모델을 구축할 수 있다.</li>
<li>라쏘 회귀는 필요한 요소들만 사용하고 나머지를 0으로 설정함으로써 과대적합을 방지할 수 있다.<h5 id="3-릿지-ridge-모델">(3) 릿지 (Ridge) 모델</h5>
<blockquote>
<p>다중 회귀라고도 불리는 리지 회귀는 정규화 또는 규제화(regularization) 기법으로 알려져 있으며 모델의 복잡성을 줄이는 데 사용된다.</p>
</blockquote>
</li>
<li>또한 ‘리지 회귀 페널티’로 알려진 약간의 편향, 즉 바이어스(bias)를 사용하여 모델이 과대적합(overfitting)에 덜 취약하게 만든다.<blockquote>
<p>Ridge: 능선(稜線)은 산이나 언덕의 꼭대기가 일정 간격을 두고 연결되어 연속적으로 솟아오른 지형을 말한다. 등산 코스로 흔히 사용된다</p>
</blockquote>
</li>
</ul>
<h4 id="2-비선형-회귀-모델-비선형-수식이-곡선">2) 비선형 회귀 모델 (비선형: 수식이 곡선)</h4>
<blockquote>
<h5 id="1-k-최근접-이웃-kneighborsregressor-모델">(1) k-최근접 이웃 (KNeighborsRegressor) 모델</h5>
</blockquote>
<h5 id="2-의사-결정-나무-decisiontreeregressor-모델">(2) 의사 결정 나무 (DecisionTreeRegressor) 모델</h5>
<h5 id="3-서포트-벡터-머신-svr-모델">(3) 서포트 벡터 머신 (SVR) 모델</h5>
<h3 id="타겟변수-y가-이산형-변수-discrete-variable인-경우">타겟변수 Y가 이산형 변수 (discrete variable)인 경우</h3>
<p>이산형 변수: 특정한 값만 가질 수 있는 변수 (가질 수 있는 값이 유한정)</p>
<blockquote>
</blockquote>
<p>Classification<br>Y의 레이블이 디스크리트 일 경우 a b c d e 이렇게 객관식일 경우에 주로 많이 쓰임</p>
<blockquote>
</blockquote>
<p>Classification vs Clustering
분류 문제의 학습: 결정 경계 (decision boundary) 를 경정할 수 있는 함수, 패턴, 모델 찾아내기</p>
<hr>
<h2 id="집계-함수">집계 함수</h2>
<blockquote>
<p>입력이 여러개의 로우이고, 출력이 하나인 결과인 함수</p>
</blockquote>
<ul>
<li>테이블의 전체 로우 수를 구하는 count , 평균( avg ), 총합( sum ), 최대값( max ), 최소값( min ) 등이 집계함수다</li>
</ul>
<hr>
<h2 id="추론과-예측">추론과 예측</h2>
<blockquote>
<p>추론과 예측의 차이는 답이 라벨인가, 그라운드트루스 인가이다.</p>
</blockquote>
<hr>
<h2 id="그라운드-트루스-ground-truth">그라운드 트루스 (Ground-Truth)</h2>
<blockquote>
<p>&quot;label&quot;은 정답지라고도 불리듯이 답이 명확하게 정해져 있는 값
&quot;ground-truth&quot;은 &#39;우리가 정한 정답&#39;, &#39;우리의 모델이 우리가 원하는 답으로 예측해주길 바라는 답&#39;</p>
</blockquote>
<hr>
<h2 id="오차-편차-잔차-편향">오차 편차 잔차 편향</h2>
<ul>
<li>오차(Error) : 실제값(y) - 테스트 단계 예측값(y^)</li>
<li>편차(Deviation) : 예측값평균 - 예측값</li>
<li>잔차(Residual) :  실제값(y) - 트레인 단계 예측값(y^)</li>
<li>편향(bias) : 분산과 반대되는 개념이라고 생각하면 편하다</li>
</ul>
<hr>
<h2 id="우도함수">우도함수</h2>
<blockquote>
<p>실현된 데이터(혹은 관찰된 데이터 observed data)로부터 특정 통계 모델의 적합성을 확인하는데 주로 이용된다.</p>
</blockquote>
<hr>
<h2 id="워드임베딩word-embedding">워드임베딩(Word Embedding)</h2>
<blockquote>
<p>자연어를 머신러닝과 딥러닝에서 사용할 수 있도록 수치화된 데이터로 만드는 과정 </p>
</blockquote>
<hr>
<h2 id="인코딩encoding">인코딩(encoding)</h2>
<blockquote>
<p>문자 -&gt; 숫자</p>
</blockquote>
<hr>
<h2 id="원핫인코딩-one-hot-encoding">원핫인코딩 (One Hot Encoding)</h2>
<blockquote>
<p>가장 기본적인 벡터화의 방법이다</p>
</blockquote>
<ul>
<li>단어 집합의 크기를 벡터의 차원으로 하고, 표현하고 싶은 단어의 인덱스에 1의 값을 부여하고, 다른 인덱스에는 0을 부여하는 단어의 벡터 표현 방식 단어가 뭔지만을 알려줄 뿐 어떤 특징을 표출해주지는 못한다.</li>
</ul>
<hr>
<h2 id="정규화normalization">정규화(Normalization)</h2>
<blockquote>
<p>feature 의 변환은 표준화(Z-score 정규화)와 정규화가 있다.</p>
</blockquote>
<ul>
<li>아웃라이어가 있으면 표준화 나머지는 정규화가 낫다.</li>
</ul>
<hr>
<h2 id="코퍼스corpus">코퍼스(Corpus)</h2>
<blockquote>
<p>말뭉치 또는 코퍼스(Corpus)는 자연언어 연구를 위해 특정한 목적을 가지고 언어의 표본을 추출한 집합</p>
</blockquote>
<ul>
<li>RNN에서 TrainSet을 말한다. 코퍼스에서 소문자(lower_case)전환, 숫자제거, 문장기호를 제거한 상태를 토큰이라고 하고 토큰은 정수화된다. </li>
<li>원핫인코딩으로 정형화된다.</li>
</ul>
<hr>
<h2 id="토큰">토큰</h2>
<blockquote>
<p>코퍼스에서 숫자와 기호가 제거된 사전에 실린 단어 그 자체</p>
</blockquote>
<ul>
<li>토큰(Token)이란 문법적으로 더 이상 나눌 수 없는 언어요소</li>
<li>텍스트 토큰화의 유형은 문장 토큰화와 단어 토큰화로 나눌 수 있습니다.<ul>
<li>문장 토큰화는 텍스트에서 문장을 분리하는 작업을 뜻한다.</li>
<li>단어 토큰화는 문장에서 단어를 토큰으로 분리하는 작업을 뜻합니다.</li>
<li>단어 토큰화 (Word Tokenization)는 기본적으로 띄어쓰기를 기준으로 합니다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="토크나이저">토크나이저</h2>
<blockquote>
<p>data를 문장 혹은 단어 등의 단위로 나누는 것</p>
</blockquote>
<hr>
<h2 id="타깃출력값">타깃(=출력값)</h2>
<ul>
<li>int = 불연속 = 카테고리컬 = 분류 classification</li>
<li>float = 연속 = 시퀀셜 = 회귀 regression</li>
</ul>
<hr>
<h2 id="트랜스포머">트랜스포머</h2>
<blockquote>
<p>트랜스포머는 시퀀스-투-시퀀스 과제를 수행하는 모델</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드 인터뷰 준비 (네트워크)]]></title>
            <link>https://velog.io/@boost_dev/%EB%B0%B1%EC%97%94%EB%93%9C-%EC%9D%B8%ED%84%B0%EB%B7%B0-%EC%A4%80%EB%B9%84-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC</link>
            <guid>https://velog.io/@boost_dev/%EB%B0%B1%EC%97%94%EB%93%9C-%EC%9D%B8%ED%84%B0%EB%B7%B0-%EC%A4%80%EB%B9%84-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC</guid>
            <pubDate>Mon, 27 Mar 2023 08:14:15 GMT</pubDate>
            <description><![CDATA[<h3 id="1-tcp와-udp의-차이">1. TCP와 UDP의 차이</h3>
<table>
<thead>
<tr>
<th>-</th>
<th>TCP</th>
<th>UDP</th>
</tr>
</thead>
<tbody><tr>
<td>연결여부</td>
<td>연결형 서비스</td>
<td>비연결형 서비스</td>
</tr>
<tr>
<td>방식</td>
<td>가상회선 방식</td>
<td>데이터그램 방식</td>
</tr>
<tr>
<td>특징</td>
<td>높은 신뢰성, 제어 기능제공</td>
<td>TCP에 보다 빠르다</td>
</tr>
</tbody></table>
<ul>
<li>TCP는 연결형 서비스로 가상회선 방식을 제공하고 높은 신뢰성을 보장하며 흐름제어 및 혼잡 제어 기능을 제공한다. </li>
<li>UDP는 비연결형 서비스로 데이터그램 방식을 제공하고 속도가 TCP에 빠르다.</li>
</ul>
<h3 id="2-tcp-34-way-handshake">2. TCP 3,4 way handshake</h3>
<ul>
<li>TCP 3 way handshake 는 TCP 통신을 이용하여 데이터를 전송하기 위한 네트워크 연결을 설정하는 과정<ul>
<li>즉, 양쪽 모두 데이터를 전송할 준비가 되었다는 것을 보장하고, 실제로 데이터를 전송하기 전에 한 쪽이 다른 쪽이 준비되었다는 것을 알 수 있도록 한다. </li>
</ul>
</li>
<li>TCP 4 way handshake 는 TCP의 연결을 해제하는 과정</li>
</ul>
<h3 id="3-http와-https의-차이">3. HTTP와 HTTPs의 차이</h3>
<ul>
<li><p>HTTP는 서버/클라이언트 모델을 따라 데이터를 주고받기 위한 프로토콜. 즉, 하이퍼텍스트를 교환하기 위한 통신규약입니다. </p>
</li>
<li><p>HTTPs란 HTTP에 데이터 암호화가 추가된 프로토콜</p>
<h3 id="4-https에-대해서-설명하고-ssl-handshake에-대해서-설명">4. HTTPS에 대해서 설명하고 SSL Handshake에 대해서 설명</h3>
<blockquote>
<p>HTTPS는 HTTP에 보안 계층을 추가한 것으로 제3자 인증, 공개키 암호화, 비밀키 암호화를 사용한다.</p>
</blockquote>
</li>
<li><p>제3자 인증 : 믿을 수 있는 인증기관에 등록된 인증서만 신뢰하는 것</p>
</li>
<li><p>공개키 암호화 : 비밀키를 공유하기 위해 사용 </p>
</li>
<li><p>비밀키 암호화 : 통신하는 데이터를 암호화하는데 사용</p>
</li>
</ul>
<p>클라이언트는 TCP 3way handshake를 수행한 이후 Client Hello를 전송합니다. 서버는 인증서를 보냅니다.(다른 정보들도 전송하나 검색을 통해 알 수 있는 부분입니다. 대개 그 정도까지는 요구하지 않습니다.)</p>
<p>클라이언트는 받은 인증서를 신뢰하기 위해서 등록된 인증기관인지 확인합니다. 이 인증서는 인증기관의 개인키로 암호화되어있고, 공개키로 검증할 수 있습니다.(브라우저에 내장되어있음) 클라이언트는 사이트의 정보와, 서버의 공개키를 얻을 수 있습니다.</p>
<p>서버의 공개키로 통신에 사용할 비밀키를 암호화해서 서버에 보냅니다. 서버는 이를 개인키로 확인하고 이후 통신은 공유된 비밀키로 암호화되어 통신합니다.</p>
<p>제3자 인증: 인증서, 인증기관/공개키 암호화: 인증서, 비밀키 공유/비밀키 암호화: 통신과정</p>
<h4 id="41-왜-공개키-암호화와-비밀키-암호화를-복합적으로-사용하는가">4.1 왜 공개키 암호화와 비밀키 암호화를 복합적으로 사용하는가?</h4>
<blockquote>
<p>공개키 방식은 암/복호화 과정에서 많은 컴퓨터 파워를 사용하기 때문에 성능에 단점이 생기는 문제를 줄이기 위해 복합적으로 사용한다</p>
</blockquote>
<ul>
<li>공개키를 그대로 사용하면 많은 접속이 몰리는 서버는 매우 큰 비용을 지불해야 하고 반대로 대칭키는 암호를 푸는 열쇠를 상대에게 전송해야 하는데 이걸 그냥 전송하면 위험하다 <ul>
<li>그래서 속도는 느리지만 데이터를 안전하게 주고 받을 수 있는 공개키 방식으로 대칭키를 암호화하고 실제 데이터를 주고 받을 때는 대칭키를 이용해서 데이터를 주고 받음</li>
</ul>
</li>
</ul>
<h3 id="5-http-메소드와-역할">5. HTTP 메소드와 역할</h3>
<table>
<thead>
<tr>
<th align="center">메소드</th>
<th align="center">역할</th>
</tr>
</thead>
<tbody><tr>
<td align="center">POST</td>
<td align="center">생성(C)</td>
</tr>
<tr>
<td align="center">GET</td>
<td align="center">조회(R)</td>
</tr>
<tr>
<td align="center">PUT</td>
<td align="center">수정(U)</td>
</tr>
<tr>
<td align="center">DELETE</td>
<td align="center">삭제(D)</td>
</tr>
</tbody></table>
<h4 id="51-get-과-post-방식의-차이">5.1 Get 과 Post 방식의 차이</h4>
<ul>
<li>Get : 클라이언트가 서버로 어떤 데이터를 <strong>조회</strong>하기 위한 요청을 위한 메소드</li>
<li>Post : 서버로 데이터를 <strong>생성</strong>하기 위해 데이터를 보낼때 사용하는 메소드</li>
<li>Get은 URL 파라미터에 데이터를 담아보내므로 body가 없고 Post는 body에 데이터를 실어 보낸다.</li>
</ul>
<table>
<thead>
<tr>
<th align="center">-</th>
<th align="center">Get</th>
<th align="center">Post</th>
</tr>
</thead>
<tbody><tr>
<td align="center">사용목적</td>
<td align="center">데이터 조회</td>
<td align="center">데이터의 생성</td>
</tr>
<tr>
<td align="center">요청에 body</td>
<td align="center">무</td>
<td align="center">유</td>
</tr>
<tr>
<td align="center">멱등성</td>
<td align="center">O</td>
<td align="center">X</td>
</tr>
</tbody></table>
<blockquote>
<p>멱등?
  : 연산을 여러번 적용해도 결과가 달라지지 않는 성질</p>
</blockquote>
<blockquote>
<p>정리🙋‍♂
Get은 데이터의 조회를 위한 요청시 사용하는 메소드이며 멱등하고, URL에 데이터를 실어보내므로 body가 없습니다. Post는 데이터의 생성 위해 사용하는 메서드로 body에 데이터를 실어보내고 멱등하지 않습니다.</p>
</blockquote>
<h3 id="6-restful">6. RESTful?</h3>
<ul>
<li>REST의 원리를 잘 지키는 시스템<blockquote>
<p>REST의 원리?
HTTP URI 를 통해 데이터를 표시하고 HTTP method를 통해 자원에 대한 처리를 하는 것</p>
</blockquote>
</li>
</ul>
<h3 id="7-cors">7. CORS?</h3>
<ul>
<li>서로 다른 도메인간에 자원을 공유하는 것</li>
<li>대부분의 브라우저에서는 이를 차단하며, 서버측에서 헤더를 통해서 사용가능한 자원을 알려준다</li>
</ul>
<h3 id="8-osi-7계층이란-무엇이고-존재이유">8. OSI 7계층이란 무엇이고 존재이유</h3>
<ul>
<li>OSI 7계층은 네트워크에서 통신이 일어나는 과정을 7단계로 나눈것을 말하며 계층을 나눈 이유는 단계별로 통신이 일어나는 과정을 파악할 수 있기 때문이다. </li>
</ul>
<h4 id="81-tcpip-4계층-">8.1 TCP/IP 4계층 ?</h4>
<ul>
<li>실제로 우리가 대부분 사용하는 네트워크는 TCP/IP 4계층으로 통신에 실제로 이용되는 계층이다.</li>
</ul>
<h4 id="82-osi-7계층-중-웹서버-소프트웨어는-어디서-작동">8.2 OSI 7계층 중 웹서버 소프트웨어는 어디서 작동?</h4>
<ul>
<li>웹 서버는 HTTP 프로토콜을 사용하여 html 데이터를 클라이언트에 제공하는 서버입니다. 웹서버 중의 하나로 어플리케이션 계층에서 작동</li>
</ul>
<h3 id="9-csrf">9. CSRF</h3>
<ul>
<li>웹사이트 취약점 공격의 하나로, 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위(수정, 삭제, 등록 등)를 특정 웹사이트에 요청하게 하는 공격</li>
<li>Django에서는 기본적으로 csrf token을 이용하는데 이는 POST 요청에 대해서만 csrf token을 발급하고 체크한다.</li>
</ul>
<hr>
<h2 id="문제-출처">문제 출처</h2>
<p><a href="https://github.com/ksundong/backend-interview-question#%EC%9D%B4-%EC%A0%80%EC%9E%A5%EC%86%8C%EB%A5%BC-%EC%9E%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%ED%8C%81">ksundong/backend-interview-question</a></p>
<h2 id="썸네일-출처">썸네일 출처</h2>
<p><a href="https://velog.io/@heelieben/%EA%B0%9C%EB%B0%9C%EC%A7%84%EC%8A%A4-%EC%A7%A4-%EB%8D%B0%EB%A0%A4%EA%B0%80%EC%84%B8%EC%9A%942">heelieben</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드 인터뷰 준비(Python)]]></title>
            <link>https://velog.io/@boost_dev/%EB%B0%B1%EC%97%94%EB%93%9C-%EC%9D%B8%ED%84%B0%EB%B7%B0-%EC%A4%80%EB%B9%84</link>
            <guid>https://velog.io/@boost_dev/%EB%B0%B1%EC%97%94%EB%93%9C-%EC%9D%B8%ED%84%B0%EB%B7%B0-%EC%A4%80%EB%B9%84</guid>
            <pubDate>Mon, 27 Mar 2023 08:04:48 GMT</pubDate>
            <description><![CDATA[<h3 id="list와-tuple의-차이">list와 tuple의 차이</h3>
<blockquote>
<p>둘 다 인덱스를 가지지만 list는 <a href="%EC%A4%91%EA%B4%84%ED%98%B8"></a>를 사용하고 수정이 가능한 반면, tuple은 ()(소괄호)를 사용하고 수정이 불가능하다</p>
</blockquote>
<h3 id="코루틴">코루틴?</h3>
<blockquote>
<p>서브루틴이 루틴에 종속되는 기존의 함수와 달리, 루틴과 서브루틴이 서로를 호출하는 비동기 방식
따라서 호출하면 코드를 한 번만 실행할 수 있는 일반함수와 달리, 진입점이 여러 개인 코루틴은 코드를 여러 번 실행할 수 있다</p>
</blockquote>
<blockquote>
<p>진입점?(entry point)
함수의 코드를 실행하는 지점</p>
</blockquote>
<h3 id="데코레이터">데코레이터</h3>
<blockquote>
<p>함수를 받아 명령을 추가한 뒤 이를 다시 함수의 형태로 반환하는 함수로 함수의 내부를 수정하지 않고 기능에 변화를 주고 싶을 때 사용한다</p>
</blockquote>
<h3 id="gil">GIL</h3>
<blockquote>
<ul>
<li>Global Interpreter Lock의 약어로 파이썬에서만 존재하는 개념이다</li>
</ul>
</blockquote>
<ul>
<li>여러 개의 스레드가 파이썬 바이트코드를 실행하지 못하게 막아서 한번에 하나만 사용할 수 있게 락을 거는 것으로 하나의 시점에 하나의 스레드만 실행된다<ul>
<li>CPU 연산의 비중이 큰 경우는 멀티 스레딩의 성능이 싱글스레드보다 떨어져서 GIL을 사용하는게 더 좋은 성능을 보인다</li>
</ul>
</li>
</ul>
<h3 id="mro">MRO</h3>
<blockquote>
<p>파이썬의 메소드 결정 순서로 기본적으로 다중 상속을 지원하는 파이썬에서 자식과 부모 클래스를 전부 포함하여 메소드의 실행 순서를 지정하는 것으로 죽음의 다이아몬드를 피할 수 있다.</p>
</blockquote>
<h3 id="magic-method">Magic Method</h3>
<blockquote>
<p>스페셜 메소드(Special method) 또는 던더 메소드(Double UNDERscore method)라고도 불리며 이미 파이썬 내에 정의되어 있고, 클래스 내부에서 매직 메소드들을 오버라이딩 하여 사용할 수 있다. </p>
</blockquote>
<ul>
<li>특징 <ul>
<li>직접 호출해서 사용하지 않고, 정해진 규칙에 따라 알아서 호출된다</li>
<li>앞 뒤로 언더바가 두 개씩 붙는다</li>
</ul>
</li>
</ul>
<h3 id="new와-init의-차이"><strong>new</strong>와 <strong>init</strong>의 차이</h3>
<blockquote>
<ul>
<li><strong>init</strong></li>
</ul>
</blockquote>
<ul>
<li>객체가 생성되어 초기화 되는 즉시 호출하지만 메모리를 할당하지 않는다 -&gt; 클래스 인스턴스를 생성하지 않는다<ul>
<li><strong>new</strong> </li>
</ul>
</li>
<li><strong>init</strong>과 달리 객체에 메모리를 할당하며 init이 실행되기 전에는 항상 new가 실행되고 메모리 할당은 이때 이뤄진다</li>
<li>반드시 오브젝트를 반환한다<ul>
<li>그럼 init이 아니라 new가 생성자인가?</li>
</ul>
</li>
<li>No</li>
<li>일반적으로 우리가 아는 &#39;객체 생성&#39;은 new 메소드로 만든 인스턴스를 사용자가 원하도록 변경하는 것을 말하지만 &#39;생성자&#39;라는 단어에서 나타내는 생성은 객체가 생성되어 초기화 되는 즉시 호출하는 <strong>init</strong>으로 서로 다른 의미를 갖는다<blockquote>
</blockquote>
(참고 : ryu-log, <a href="https://velog.io/@ryu_log/Python-%EC%83%9D%EC%84%B1%EC%9E%90-new-vs-init">[python] <strong>new</strong> vs <strong>init</strong></a>)</li>
</ul>
<h3 id="__repr__와-__str__의-차이"><code>__repr__</code>와 <code>__str__</code>의 차이</h3>
<blockquote>
<p>객체의 타입에 관계없이 데이터를 문자열 표현으로 반환한다는 공통점이 있으나
<code>__repr__</code>은 해당 객체를 인간이 이해할 수 있는 <strong>표현</strong>으로 나타내기 위한 용도로 사용되는 반면, <code>__str__</code>의 본질적인 목적은 서로 다른 자료형 간에 <strong>인터페이스</strong>를 제공하기 위해서 존재한다
(참고:<a href="https://shoark7.github.io/programming/python/difference-between-__repr__-vs-__str__">Parkito&#39;s on the way!</a>)</p>
</blockquote>
<h3 id="r-string과-u-string">r string과 u string</h3>
<blockquote>
<p>문자열 포매팅 방식들로 u와 r 외에 b와 f도 있다.</p>
</blockquote>
<ul>
<li>r string은 r&#39;&#39; 사이의 무자열을 이스케이프 시퀀스(변환) 없이 그대로 저장해서 파일경로나 정규표현식에서 자주 쓰이며</li>
<li>u sting은 unicode로 파이썬3 이후로는 없는 포매팅 방식이다<ul>
<li>아직도 <code>u&#39;&#39;</code>가 존재하는 이유는 레거시 코드들과의 호환성 때문이다<blockquote>
</blockquote>
(참고:<a href="https://www.codeit.kr/community/questions/UXVlc3Rpb246NWZlMzM2YTRkMDZjZjE3NjI5NDJjNzA3">코드잇, 문자열 포매팅의 종류에 관하여</a></li>
</ul>
</li>
</ul>
<h3 id="call-by-assignment">Call by Assignment</h3>
<blockquote>
<p>명시적인 Call by Reference 혹은 Call by Value 라는 개념이 존재하지 않는 파이썬에서 함수에 인수를 전달하는 방식 </p>
</blockquote>
<ul>
<li>파이썬은 모든 것이 객체이기 때문에 변수에 특정 값을 할당할 때, 그 값들이 실제로 변수에 저장되는 것이 아니라 새로운 객체가 생성되고 변수가 그 객체를 가르키는 방식으로 동작한다</li>
</ul>
<h3 id="파이썬에서의-접근제어지시자">파이썬에서의 접근제어지시자</h3>
<blockquote>
<p>클래스의 외부에서 클래스의 멤버 변수, 메서드, 생성자를 사용 가능 여부를 지정하는 키워드로 그 종류로는 어디서나 접근 가능한 public, 같은 패키지나 상속관계의 클레스에서만 접근 가능한 protected, 해당 패키지 내에서만 접근 가능한 default, 같은 클래스 내부에서만 접근 가능한 private가 있다.</p>
</blockquote>
<h3 id="global과-nonlocal-키워드의-차이">global과 nonlocal 키워드의 차이</h3>
<blockquote>
<p>사용하면 변수 선언시 전역변수로 간주되는 global과 달리 nonlocal은 전역변수를 제외하고 본인 스코프 바깥 방향으로 가장 가까운 변수를 찾는다</p>
</blockquote>
<h3 id="classmethod와-staticmethod의-차이">classmethod와 staticmethod의 차이</h3>
<p>staticmethod는 부모 클래스의 클래스 속성값을 가져오지만 classmethod는 cls인자를 활용해서 클래스의 클래스 속성을 가져오기 때문에 상속에서 차이가 있다</p>
<hr>
<h2 id="문제-출처">문제 출처</h2>
<p><a href="https://github.com/ksundong/backend-interview-question#%EC%9D%B4-%EC%A0%80%EC%9E%A5%EC%86%8C%EB%A5%BC-%EC%9E%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%ED%8C%81">ksundong/backend-interview-question</a></p>
<h2 id="썸네일-출처">썸네일 출처</h2>
<p><a href="https://velog.io/@heelieben/%EA%B0%9C%EB%B0%9C%EC%A7%84%EC%8A%A4-%EC%A7%A4-%EB%8D%B0%EB%A0%A4%EA%B0%80%EC%84%B8%EC%9A%942">heelieben</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TensorFlow/Keras/Pytorch]]></title>
            <link>https://velog.io/@boost_dev/TensorFlowKerasPytorch</link>
            <guid>https://velog.io/@boost_dev/TensorFlowKerasPytorch</guid>
            <pubDate>Mon, 27 Mar 2023 01:30:52 GMT</pubDate>
            <description><![CDATA[<h1 id="텐서플로">텐서플로</h1>
<ul>
<li><p>구글이 개발한 오픈소스 소프트웨어 라이브러리이며 머신러닝과 딥러닝을 쉽게 사용할 수 있도록 다양한 기능을 제공</p>
<h3 id="특징">특징</h3>
</li>
<li><p>데이터 플로우 그래프(Data Flow Graph) 구조를 사용</p>
<blockquote>
<p>Data Flow Graph ?
수학 계산식과 데이터의 흐름을 노드(Node)와 엣지(Edge)를 사용한 방향성 그래프로 표현하는 방식</p>
</blockquote>
</li>
<li><p>각 노드 사이의 연결이나 다차원 배열 등을 의미하는 텐서 사이의 연결 관계를 풍부하게 표현할 수 있으며, 텐서보드 시각화 도구 모음을 사용하면 웹 기반의 대화형 대시보드에서 그래프 실행 방법에 대한 검사나 프로파일링도 가능</p>
</li>
</ul>
<h3 id="장점">장점</h3>
<ul>
<li>주로 이미지 인식이나 반복 신경망 구성, 기계 번역, 필기 숫자 판별 등을 위한 각종 신경망 학습에 사용되는데 특히 대규모 예측 모델 구성에 뛰어나 테스트부터 실제 서비스까지 사실상 거의 모든 딥러닝 프로젝트에서 범용적으로 활용할 수 있다.</li>
<li>구글이 지원하기 때문에 지속적인 성능 개선과 지원에서 타 프레임워크보다 빠르고 안정적이란 부분도 강점</li>
<li>추상화 수준이 높아 텐서플로를 활용하면 개발자는 알고리즘의 세세한 구현보다 전체적인 논리 자체에 더 집중할 수 있게 되며 확장성 또한 뛰어나다</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>딥러닝 모델을 만드는 데 기초 레벨부터 직접 작업해야 하기 때문에 초보자가 사용하기 어려울 수 있다.</li>
</ul>
<hr>
<h1 id="케라스">케라스</h1>
<blockquote>
<p>사용이 어려운 텐서플로우의 문제를 해결하기 위해 개발됐으며 텐서플로우보다 단순화된 인터페이스를 제공한다</p>
</blockquote>
<h3 id="특징-1">특징</h3>
<ul>
<li>딥러닝 초급자도 각자 분야에서 손쉽게 딥러닝 모델을 개발하고 활용할 수 있도록 직관적인 API를 제공</li>
<li>케라스에서 제공하는 시퀀스 모델로 원하는 레이어를 쉽게 순차적으로 쌓을 수 있으며, 다중 출력 등 더 복잡한 모델을 구성할 때는 케라스 함수 API를 사용하여 쉽게 구성할 수 있다.</li>
</ul>
<h3 id="장점-1">장점</h3>
<ul>
<li>일반 사용 사례에 최적화된 간단하고 일관된 인터페이스를 제공. </li>
<li>사용자 오류에 대해 명확하고 실용적인 피드백을 제공. </li>
<li>케라스의 구성 요소는 모듈 형태로, 각 모듈이 독립성을 갖기 때문에 새로운 모델을 만들 때 각 모듈을 조합해 쉽게 새로운 모델을 만들 수 있다.</li>
</ul>
<h3 id="단점-1">단점</h3>
<ul>
<li>모듈화의 한계로 복잡한 프로젝트에 구현 범위가 다소 좁은 편이다. 또한 케라스는 다양한 백엔드 위에서 동작하기 때문에 어떤 오류가 발생했을 때 케라스 자체의 문제인지, 백엔드 언어의 문제인지 특정하기 어려운 단점이 있다</li>
</ul>
<hr>
<h1 id="파이토치">파이토치</h1>
<blockquote>
<p>페이스북의 AI 연구 팀이 Torch를 기반으로 개발한 파이썬 기반 오픈소스 머신러닝 라이브러리</p>
</blockquote>
<h3 id="특징-2">특징</h3>
<ul>
<li>절차가 간단하고 그래프가 동적으로 변화할 수 있으며 코드 자체도 파이썬과 유사해 진입 장벽이 낮은 편</li>
</ul>
<h3 id="장점-2">장점</h3>
<ul>
<li>그래프를 만들면서 동시에 값을 할당하는 Define by run 방식으로 코드를 깔끔하고 직관적으로 작성할 수 있다. </li>
<li>학습 속도가 텐서플로보다 빠르다. </li>
<li>텐서플로가 유기적을 신경망을 만들 수 없어 성능의 한계를 가지고 있는 반면, 파이토치는 메모리에서 연산을 하면서도 신경망 사이즈를 최적으로 바꾸면서 동작시킬 수 있다. </li>
<li>파이토치는 Numpy를 대체하면서도 GPU를 이용한 연산이 가능하며 유연하고 빠르기 때문에 최근 딥러닝 관련 논문에서는 텐서플로보다 파이토치를 선호하는 추세입니다.</li>
</ul>
<h3 id="단점-2">단점</h3>
<ul>
<li>텐서플로에 비해 사용자층이 낮고 학습에 필요한 자료와 예제를 구하기 쉽지 않다.</li>
</ul>
<hr>
<p>참고 </p>
<ul>
<li>혼공머신(한빛미디어, 박해선)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter로 프로젝트용 어플 제작]]></title>
            <link>https://velog.io/@boost_dev/Flutter%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%9A%A9-%EC%96%B4%ED%94%8C-%EC%A0%9C%EC%9E%91</link>
            <guid>https://velog.io/@boost_dev/Flutter%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%9A%A9-%EC%96%B4%ED%94%8C-%EC%A0%9C%EC%9E%91</guid>
            <pubDate>Wed, 22 Mar 2023 09:38:46 GMT</pubDate>
            <description><![CDATA[<h1 id="1-flutter-작업-시작">1. Flutter 작업 시작</h1>
<ul>
<li>Figma 디자인에 따른 모바일 어플리케이션 페이지 및 레이아웃 Flutter로 구현</li>
</ul>
<h2 id="11-페이지">1.1 페이지</h2>
<table>
<thead>
<tr>
<th>네비게이션바(임시)</th>
<th>시작화면</th>
<th>로그인</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/53ba7877-554f-468f-89c9-51dd142f1f86/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/5a158318-cbb6-47b5-9045-55929b0c83f4/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/3c82435d-8258-43a2-ada5-257abdbb8efd/image.png" alt=""></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>회원가입</th>
<th>로딩화면</th>
<th>메인 화면 (대여 전)</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/7d7af4d1-8f54-48b6-a906-32fa0bcd33d1/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/84f96616-d8c3-4c43-ab3f-ce78e2a49fbb/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/a7537677-7133-4d39-90f4-7fc55bf320ef/image.png" alt=""></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>QR 스캔</th>
<th>오류보고</th>
<th>메뉴</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/6c381007-d9d8-4e87-ba79-556f466b8185/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/95c1ccef-8b32-4101-8af2-b35aefca3853/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/94ed9a1d-dbb1-4924-8bae-0d172bf7cfe6/image.png" alt=""></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>사용자 프로필</th>
<th>결제 정보</th>
<th>이용 방법</th>
<th>문의 내역</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/af133c59-cbde-4f08-aa75-d455476b399c/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/3f85b04d-f232-4cd9-a4a7-c3f28b535e55/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/24ee4225-577c-4ef9-af3d-69fca0d1cbaf/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/ba41c25b-f5f8-4508-88b2-fbdc039ad80e/image.png" alt=""></td>
</tr>
</tbody></table>
<blockquote>
<h4 id="추후-작업-요망">추후 작업 요망</h4>
</blockquote>
<ul>
<li>챗봇 기능 추가 시 챗봇 위젯 구현 필요</li>
<li>위젯간 데이터 전송 및 상태 관리</li>
<li>API 를 통한 request 및 response 처리</li>
</ul>
<h1 id="2-기능-구현">2. 기능 구현</h1>
<h2 id="21-앱-로그인-및-로그아웃-기능-구현">2.1 앱 로그인 및 로그아웃 기능 구현</h2>
<ul>
<li>REST API를 이용해 fastAPI 서버와 로그인 기능을 연동할 수 있는지 테스트 해보고자 한다.</li>
<li>서버는 POST 방식으로 user_email과 password 키값을 가지는 JSON을 보내면 해당 Token을 똑같은 JSON형태로 돌려준다.</li>
<li><em>소스코드*</em>
<img src="https://velog.velcdn.com/images/boost_dev/post/f3542afe-33bf-44cc-a106-c1122a2cf093/image.png" alt=""><blockquote>
<p>에러 발생
INFO:     172.18.0.1:50394 - &quot;POST /users/login HTTP/1.1&quot; 422 Unprocessable Entity</p>
</blockquote>
</li>
<li>원인 : 데이터를 JSON 형태로 Post 하지 않아서 생긴 문제다. jsonEncode 함수에 Map 형태의 값을 넣어 보내도록 한다.
<img src="https://velog.velcdn.com/images/boost_dev/post/2418e1f3-be5e-4da3-951a-0b5447687c21/image.png" alt=""></li>
<li>화면을 통해 발급받은 토큰을 확인했다.</li>
</ul>
<p><strong>만들어놓은 레이아웃에 적용시킨 소스 코드</strong></p>
<pre><code>import &#39;dart:convert&#39;;import &#39;package:dio/dio.dart&#39;;import &#39;package:flutter/material.dart&#39;;import &#39;package:flutter_project/const/constants.dart&#39;;import &#39;package:flutter_project/screen/1_start.dart&#39;;import &#39;package:flutter_project/screen/3_signup.dart&#39;;import &#39;package:flutter_project/widgets/basic_button.dart&#39;;import &#39;package:flutter_project/widgets/input_box.dart&#39;;import &#39;package:flutter_project/widgets/logo.dart&#39;;class Login extends StatefulWidget {
  Login({super.key});  @override  State&lt;Login&gt; createState() =&gt; _LoginState();}

class _LoginState extends State&lt;Login&gt; {
  String user_email = &#39;&#39;;  String password = &#39;&#39;;  bool logoAppear = true;  @override  Widget build(BuildContext context) {
    final bottomInset = MediaQuery.of(context).viewInsets.bottom;    return Scaffold(
      appBar: AppBar(
        title: Text(&quot;로그인&quot;),      ),      backgroundColor: MAIN_COLOR,      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 10.0),        child: Container(
          width: MediaQuery.of(context).size.width,          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,            crossAxisAlignment: CrossAxisAlignment.stretch,            children: [
              showLogo(),              InputBox(
                hintText: &quot;아이디&quot;,                onTap: () {
                  setState(() {
                    logoAppear = false;                  });                },                onChanged: (String value) {
                  user_email = value;                },              ),              SizedBox(
                height: 10.0,              ),              InputBox(
                hintText: &quot;비밀번호&quot;,                onTap: () {
                  setState(() {
                    logoAppear = false;                  });                },                onChanged: (String value) {
                  password = value;                },              ),              Row(
                children: [
                  Checkbox(value: false, onChanged: (value) {}),                  Text(&#39;자동 로그인&#39;),                ],              ),              SizedBox(
                height: 30.0,              ),              Row(
                mainAxisAlignment: MainAxisAlignment.center,                children: [
                  Expanded(
                    child: BasicButton(
                      buttonTitle: &quot;로그인&quot;,                      onPressed: () {
                        postLoginInfo();                        print(&#39;##### 토큰 정보 : ${postLoginInfo().toString()} #####&#39;);                      },                    ),                  ),                  SizedBox(
                    width: 10.0,                  ),                  Expanded(
                    child: BasicButton(
                      buttonTitle: &quot;계정 찾기&quot;,                      onPressed: () =&gt; Navigator.push(
                        context,                        MaterialPageRoute(
                          builder: (context) =&gt; Start(),                        ),                      ),                    ),                  ),                ],              ),              BasicButton(
                buttonTitle: &quot;회원가입&quot;,                onPressed: () =&gt; Navigator.push(
                  context,                  MaterialPageRoute(
                    builder: (context) =&gt; SignUp(),                  ),                ),              ),            ],          ),        ),      ),    );  }

  Widget showLogo() {
    if (logoAppear) {
      return Column(
        children: [
          Logo(),          SizedBox(
            height: 50.0,          ),        ],      );    } else {
      return SizedBox(
        height: 20.0,      );    }
  }

  Future&lt;Map&gt; postLoginInfo() async {
    Map loginInfo = {
      &#39;user_email&#39;: user_email,      &#39;password&#39;: password,    };    print(&#39;##### loginInfoMap is ${loginInfo} #####&#39;);    final response = await Dio().post(
      &#39;http://10.0.2.2:8000/users/login&#39;,      data: jsonEncode(loginInfo),    );    return response.data;  }
}</code></pre><ul>
<li>이메일과 비밀번호의 초기값이 onChanged 함수를 통해 TextFieldForm의 값으로 변경되어 저장된다.</li>
</ul>
<blockquote>
<h4 id="20230228-로그인-로그아웃-구현-마무리-작업">2023.02.28. 로그인, 로그아웃 구현 마무리 작업</h4>
</blockquote>
<ul>
<li>TextFormField 위젯의 onChanged 함수를 이용하면 이메일과 비밀번호를 작성하는 중에도 상태가 변경되어 상태관리가 어려워진다.</li>
<li>대안으로 onSaved 함수를 이용해 값을 저장하고 TextFormField 위젯을 Form 위젯으로 감싸 GlobalKey() 클래스를 이용해 저장된 값들을 관리한다.</li>
<li>Dio를 이용해 응답받은 토큰은 FlutterSecureStorage 라이브러리를 이용해 값을 저장, 로그아웃에 이용한다.</li>
<li>showDialog함수를 이용해 로그인 성공, 실패시 알림창이 뜨게하고 알림창을 확인했을 때 Navigator를 이용해 알맞은 스크린으로 이동하도록 한다.</li>
<li>obscureText 기능을 통해 비밀번호 입력 시 “*”기호로 보이게 한다.</li>
</ul>
<h3 id="22-앱-로그인-및-로그아웃-기능구현-상세">2.2 앱 로그인 및 로그아웃 기능구현 상세</h3>
<ul>
<li>로그인 성공 혹은 실패 시 API의 응답 데이터가 FutureBuilder의 snapshot에 저장된다. 저장된 데이터의 경우의 수만큼 switch문으로 코드를 작성한다.</li>
<li>비밀번호가 일치하지 않았을 경우</li>
</ul>
<table>
<thead>
<tr>
<th>소스코드</th>
<th>화면</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/ec0ec74b-5dad-4f7b-9452-31c0eb7de7d2/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/411d63f4-df91-4349-845d-ad94a3e8ed03/image.png" alt=""></td>
</tr>
</tbody></table>
<ul>
<li>아이디가 일치하지 않았을 경우</li>
</ul>
<table>
<thead>
<tr>
<th>소스코드</th>
<th>화면</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/44adcbd8-eb0a-4f41-b6ee-a9522e64a341/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/ef3c00d1-85cb-4208-8966-cdd0cb6fdae0/image.png" alt=""></td>
</tr>
</tbody></table>
<ul>
<li>로그인에 성공했을 경우</li>
</ul>
<table>
<thead>
<tr>
<th>소스코드</th>
<th>화면</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/e271ca3d-386b-43e7-9e08-a99d61adfdf1/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/boost_dev/post/63fd2db2-02b7-413e-9648-8658ddca753f/image.png" alt=""></td>
</tr>
</tbody></table>
<h2 id="23-앱-회원가입-기능-구현">2.3 앱 회원가입 기능 구현</h2>
<ul>
<li>우선 회원가입에 필수 정보인 “이메일”과 “비밀번호”, “비밀번호 재입력” TextFieldForm 을 만든다 </li>
<li>“비밀번호 재입력” 칸을 이용해 비밀번호를 정확히 입력했는지 검증한다.</li>
<li>이메일, 비밀번호가 null값이 아니고 비밀번호와 재입력 비밀번호가 일치하면 Dio 라이브러리를 통해 서버에 이메일과 비밀번호 값을 JSON 형태로 보낸다.</li>
<li>서버에서 응답받은 값 (null, 회원가입성공, 회원가입실패, 이미 존재하는 이메일) 에 따라 Dialog를 보여준다.</li>
<li>회원가입이 완료되면 Dialog의 ‘확인’버튼을 누르고 로그인 페이지로 이동한다.</li>
</ul>
<h2 id="24-앱안드로이드-지도-기능-구현">2.4 앱(안드로이드) 지도 기능 구현</h2>
<blockquote>
<p>flutter_naver_map 플러그인을 통해 지도 위에 우산 대여함의 위치를 마킹하고 우산 대여함 아이콘을 클릭하면 우산 대여 화면으로 넘어가도록 한다.</p>
</blockquote>
<blockquote>
<p><strong>네이버 API 사용 방법</strong></p>
</blockquote>
<ul>
<li><p>네이버 클라우드 콘솔에 접속해 지도 API Key를 발급받는다.
<img src="https://velog.velcdn.com/images/boost_dev/post/2c185b87-d886-4e9e-af10-c0cd748781d0/image.png" alt=""></p>
</li>
<li><p>현재 진행 중인 플러터 프로젝트 명을 그대로 입력한다.
<img src="https://velog.velcdn.com/images/boost_dev/post/ba8dfb3f-7946-4be4-b203-b3bd7f3897ad/image.png" alt=""></p>
</li>
<li><p>클라이언트ID를 진행중인 프로젝트의 AndroidManifest.xml 파일에 입력한다.
<img src="https://velog.velcdn.com/images/boost_dev/post/193b89c4-e7b5-4114-a6c6-b1143ac5b536/image.png" alt=""></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/c9693af5-fe4f-439d-bffb-fcf1c7e046da/image.png" alt=""></p>
<ul>
<li>에뮬레이터를 통해 이상없이 나오는지 확인한다.
<img src="https://velog.velcdn.com/images/boost_dev/post/b55754b1-773f-45df-a134-dafa3b9c1adf/image.png" alt=""></li>
</ul>
<h3 id="241-우산대여함-위치에-마킹하기">2.4.1 우산대여함 위치에 마킹하기</h3>
<ul>
<li><code>initialCameraPosition</code> 키를 통해 지도를 켰을 때 처음 위치를 설정한다.</li>
<li>초기 위치는 우선 우산대여함 위치와 동일하게 설정한다.</li>
<li><code>markers</code> 키를 통해 마커를 입력한다.</li>
<li>마커 아이콘은 assets 디렉토리에 있는 우산이미지를 이용하였다.
<img src="https://velog.velcdn.com/images/boost_dev/post/dd197153-c407-4ab1-8cdc-72d38781a5f5/image.png" alt=""></li>
<li>우산 아이콘을 클릭시 우산 대여 혹은 반납 페이지로 이동할 수 있는 네비게이션 창이 실행된다<pre><code>Marker(
                          markerId: &#39;1&#39;,
                          position: LatLng(standLatitude, standLongitude),
                          icon: snapshot.data,
                          width: 32,
                          height: 32,
                          onMarkerTab: (marker, iconSize) {
                            showDialog(
                              context: context,
                              builder: (context) {
                                return AlertDialog(
                                  title: Text(&quot;우산 대여 화면으로\n이동하시겠습니까?&quot;),
                                  content: Row(
                                    mainAxisAlignment:
                                        MainAxisAlignment.spaceEvenly,
                                    children: [
                                      ElevatedButton(
                                        onPressed: () {
                                          Navigator.of(context).push(
                                            MaterialPageRoute(
                                              builder: (context) =&gt;
                                                  RentScreen(),
                                            ),
                                          );
                                        },
                                        child: Text(&quot;예&quot;),
                                      ),
                                      ElevatedButton(
                                        onPressed: () {
                                          Navigator.of(context).pop();
                                        },
                                        child: Text(&quot;아니오&quot;),
                                      ),
                                    ],
                                  ),
                                );
                              },
                            );
                          },
                        ),</code></pre><img src="https://velog.velcdn.com/images/boost_dev/post/172dabf2-e936-47c4-bc99-c83e004f7ece/image.PNG" alt=""></li>
</ul>
<h3 id="242-사용자-현재-위치-표시">2.4.2 사용자 현재 위치 표시</h3>
<ul>
<li>geolocator 패키지를 사용<pre><code>dependencies:
  geolocator: ^9.0.2</code></pre></li>
<li>flutter pub get 명령어로 dependency를 적용<pre><code>import &#39;package:geolocator/geolocator.dart&#39;;
</code></pre></li>
</ul>
<p>Future<String> checkLocationPermission() async {
  final isLocationEnabled = await Geolocator.isLocationServiceEnabled();
  if (!isLocationEnabled) {
    return &quot;위치 서비스를 활성화해주세요.&quot;;
  }
  LocationPermission checkedPermission = await Geolocator.checkPermission();
  if (checkedPermission == LocationPermission.denied) {
    checkedPermission = await Geolocator.requestPermission();
    if (checkedPermission == LocationPermission.denied) {
      return &quot;위치 권한을 허가해주세요.&quot;;
    }
  }
  if (checkedPermission == LocationPermission.deniedForever) {
    return &quot;앱의 위치 권한을 설정에서 허가해주세요.&quot;;
  }
  return &quot;위치 권한이 허가 되었습니다.&quot;;
}</p>
<pre><code></code></pre><p>class BeforeRentScreen extends StatefulWidget {
  const BeforeRentScreen({Key? key}) : super(key: key);</p>
<p>  @override
  State<BeforeRentScreen> createState() =&gt; _BeforeRentScreenState();
}</p>
<p>class _BeforeRentScreenState extends State<BeforeRentScreen> {
  final standLatitude = 37.5706;
  final standLongitude = 126.9853;</p>
<p>  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () {
        return Future(() =&gt; false);
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text(&quot;Before Rent Screen&quot;),
          leading: Container(),
          // 배포 시 홈버튼 삭제
          actions: [homeButton(context)],
        ),
        body: Stack(
          children: [
            FutureBuilder<String>(
              future: checkLocationPermission(),
              builder: (context, snapshot) {
                if (snapshot.hasData &amp;&amp;
                    snapshot.connectionState == ConnectionState.waiting) {
                  return Center(
                    child: CircularProgressIndicator(),
                  );
                }
                if (snapshot.data == &#39;위치 권한이 허가 되었습니다.&#39;) {
                  return FutureBuilder(
                    future: OverlayImage.fromAssetImage(
                      assetName: &#39;assets/images/umb_icon.png&#39;,
                    ),
                    builder: (context, snapshot) {
                      return NaverMap( ...</p>
<pre><code>![](https://velog.velcdn.com/images/boost_dev/post/9e9479b9-bda8-4e0c-871d-3fb2747875d8/image.PNG)

## 2.5 앱 게시판 기능 구현
- 게시글 작성 시 첨부파일 업로드 테스트
- image_picker 패키지를 사용해 갤러리 혹은 카메라로 사진을 불러온다.
소스코드 : </code></pre><p>import &#39;dart:io&#39;;</p>
<p>import &#39;package:dio/dio.dart&#39;;
import &#39;package:flutter/material.dart&#39;;
import &#39;package:image_picker/image_picker.dart&#39;;</p>
<p>void main() {
  runApp(
    MaterialApp(
      home: HomeScreen(),
    ),
  );
}</p>
<p>class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);</p>
<p>  @override
  State<HomeScreen> createState() =&gt; _HomeScreenState();
}</p>
<p>class _HomeScreenState extends State<HomeScreen> {
  String? imagePath;
  XFile? tokenPthoto;</p>
<p>  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          ElevatedButton(
            onPressed: () async {
              final image =
              await ImagePicker().pickImage(source: ImageSource.gallery);
              print(image);
            },
            child: Text(&quot;Load Image&quot;),
          ),
          ElevatedButton(
            onPressed: () async {
              final image = await ImagePicker().pickImage(
                source: ImageSource.camera,
                maxHeight: 100,
                maxWidth: 100,
                imageQuality: 30,
              );
              print(&quot;이미지 경로 : ${image!.path}&quot;);
              print(&quot;이미지 경로 타입 : ${image.path.runtimeType}&quot;);
              setState(() {
                imagePath = image.path;
                tokenPthoto = image;
              });
            },
            child: Text(&quot;Take a Photo&quot;),
          ),
          previewPhoto(),
        ],
      ),
    );
  }</p>
<p>  Widget previewPhoto() {
    if (imagePath == null) {
      return Container(
        child: Text(&quot;이미지 경로 없음&quot;),
      );
    } else {
      return Column(
        children: [
          Text(&quot;이미지 경로 : $imagePath&quot;),
          Image.file(
            File(imagePath!),
            width: 200,
            height: 200,
          ),
        ],
      );
    }
  }
}</p>
<pre><code>
|첫 화면| 이미지 선택 화면|이미지 로드 완료|
|---|---|---|
|![](https://velog.velcdn.com/images/boost_dev/post/d7c69fd7-fbfc-4087-84d3-d7580d9d02ba/image.png)|![](https://velog.velcdn.com/images/boost_dev/post/623f61c0-331d-4368-aa50-2123104777e9/image.png)|![](https://velog.velcdn.com/images/boost_dev/post/8f41dfcc-bc54-4912-9813-33480b6af62c/image.png)|

- 촬영한 데이터 (혹은 불러온 데이터)를 서버에 전송한다.
    - Dio 패키지의 post 함수를 사용한다.

## 2.6 앱 챗봇 기능 구현
**챗봇 응답 서버와 통신 가능 테스트**
- http 패키지의  post 메서드를 이용해 서버의 챗봇 인공지능 모델과 통신한다.
- ListView builder를 이용해 사용자와 챗봇의 메세지를 화면에 나타낸다.
- 소스코드 : </code></pre><p>챗봇 응답 서버와 통신 가능 테스트</p>
<p>http 패키지의  post 메서드를 이용해 서버의 챗봇 인공지능 모델과 통신한다.</p>
<p>ListView builder를 이용해 사용자와 챗봇의 메세지를 화면에 나타낸다.</p>
<p>소스코드 : </p>
<pre><code>- 에뮬레이터 화면
![](https://velog.velcdn.com/images/boost_dev/post/14c89f2c-8a0c-4d15-9718-4918d4c5542b/image.png)

## 2.7 QR코드 인식
- QR code scanner 패키지를 pub.dev 에서 가져온다</code></pre><p>dependencies:
  qr_code_scanner: ^1.0.1</p>
<pre><code>
- QR코드 인식에 성공하는 순간 우산 내부 사진을 캡쳐하기 위해 Screenshot 패키지를 pub.dev 에서 가져온다</code></pre><p>dependencies:
    screenshot: ^1.3.0</p>
<pre><code>- flutter pub dev  명령어로 depndency를 적용
- class의 속성으로 screenshot의 controller, qrview의 controller, qrcode, 캡쳐 시 QR 가이드라인을 제거하기 위해 boolean값을 생성</code></pre><p>class RentScreen extends StatefulWidget {
  const RentScreen({Key? key}) : super(key: key);</p>
<p>  @override
  State<RentScreen> createState() =&gt; _RentScreenState();
}</p>
<p>class _RentScreenState extends State<RentScreen> {
  final screenshotController = ScreenshotController();
  String? qrCode;
  QRViewController? controller;
  final GlobalKey qrKey = GlobalKey(debugLabel: &#39;QR&#39;);
  bool removeQrScannerOverlayShape = false;</p>
<p>  @override
  Widget build(BuildContext context) {
    double deviceWidth = MediaQuery.of(context).size.width;</p>
<pre><code>return Scaffold( ... 생략 ...</code></pre><pre><code>- 캡쳐하는 범위는 QRView 위젯 전체이므로 Screenshot 위젯으로 QRView 위젯을 감싼다. QRcode 스캔에 성공하면 카메라를 멈추고 Screenshot의 캡쳐 기능이 발동하도록 코딩한다. 캡쳐에 성공하면 API서버에 발송할지 여부를 묻는 다이얼로그가 뜨고 승인하면 서버에서 QR코드를 매치하고 커스텀한 YOLOv5를 이용해 우산의 고장 여부를 판별</code></pre><p>... 생략 ...
return Scaffold(
      appBar: AppBar(
        title: Text(&quot;Rent Screen&quot;),
        actions: [
          homeButton(context)
        ],
        leading: IconButton(
          icon: Icon(Icons.arrow_back),
          onPressed: () {
            Navigator.of(context).push(
              MaterialPageRoute(
                builder: (context) =&gt; BeforeRentScreen(),
              ),
            );
          },
        ),
      ),
      body: Screenshot(
        controller: screenshotController,
        child: QRView(
          key: qrKey,
          overlay: renderQrScannerOverlayShape(deviceWidth),
          onQRViewCreated: (controller) {
            controller.scannedDataStream.listen(
              (scanData) {
                if (scanData.code == null) {
                  return;
                } else {
                  qrCode = scanData.code;
                  setState(
                    () {
                      removeQrScannerOverlayShape = true;
                    },
                  );
                  controller.pauseCamera().then(
                    (_) async {
                      final capturedImg = await screenshotController.capture();
                      showDialog(
                        context: context,
                        builder: (context) {
                          return AlertDialog(
                            title: Image.memory(
                              capturedImg!,
                              width: deviceWidth * 0.75,
                              height: deviceWidth * 0.75,
                            ),
                            actions: [
                              ElevatedButton(
                                onPressed: () {
                                  setState(
                                    () {
                                      removeQrScannerOverlayShape = false;
                                    },
                                  );
                                  Navigator.pop(context);
                                  controller.resumeCamera();
                                },
                                child: Text(&quot;다시 스캔&quot;),
                              ),
                              FutureBuilder(
                                future:
                                    FlutterSecureStorage().read(key: &quot;token&quot;),
                                builder: (context, snapshot) {
                                  return ElevatedButton(
                                    child: Text(&quot;대여&quot;),
                                    onPressed: () async {
                                      await Dio()
                                          .post(
                                        &#39;$API_DOMAIN/flutter/rent&#39;,
                                        data: jsonEncode(
                                          {
                                            &quot;token&quot;: snapshot.data,
                                            &quot;qr_code&quot;: qrCode,
                                          },
                                        ),
                                      )
                                          .then(
                                        (value) {
                                          if (value.data[&#39;msg&#39;] == null) {
                                            showDialog(
                                              context: context,
                                              builder: (context) {
                                                return AlertDialog(
                                                  title:
                                                      Text(&quot;서버와 통신에 실패하였습니다&quot;),
                                                  actions: [
                                                    ElevatedButton(
                                                      child: Text(&quot;확인&quot;),
                                                      onPressed: () {
                                                        Navigator.of(context)
                                                            .pop();
                                                      },
                                                    ),
                                                  ],
                                                );
                                              },
                                            );
                                          } else if (value.data[&#39;msg&#39;] ==
                                              &quot;대여 성공&quot;) {
                                            showDialog(
                                              context: context,
                                              builder: (context) {
                                                return AlertDialog(
                                                  title: Text(&quot;대여 성공했습니다&quot;),
                                                  actions: [
                                                    ElevatedButton(
                                                      child: Text(&quot;확인&quot;),
                                                      onPressed: () {
                                                        Navigator.of(context)
                                                            .push(
                                                          MaterialPageRoute(
                                                            builder: (context) =&gt;
                                                                AfterRentScreen(),
                                                          ),
                                                        );
                                                      },
                                                    ),
                                                  ],
                                                );
                                              },
                                            );
                                          } else if (value.data[&#39;msg&#39;] ==
                                              &quot;대여 실패&quot;) {
                                            showDialog(
                                              context: context,
                                              builder: (context) {
                                                return AlertDialog(
                                                  title: Text(&quot;대여 실패했습니다&quot;),
                                                  actions: <Widget>[
                                                    ElevatedButton(
                                                      child: Text(&quot;확인&quot;),
                                                      onPressed: () {
                                                        Navigator.of(context)
                                                            .push(
                                                          MaterialPageRoute(
                                                            builder: (context) =&gt;
                                                                RentScreen(),
                                                          ),
                                                        );
                                                      },
                                                    ),
                                                  ], // add this
                                                );
                                              },
                                            );
                                          }
                                        },
                                      );
                                    },
                                  );
                                },
                              ),
                            ],
                          );
                        },
                      );
                    },
                  );
                }
              },
            );
          },
        ),
      ),
    );</p>
<pre><code>- 캡쳐 시 QR코드 가이드라인이 보이지 않도록 하기 위해 ```renderQrScannerOverlayShape```을 추가</code></pre><p>... 생략 ...
QrScannerOverlayShape renderQrScannerOverlayShape(deviceWidth) {
    if (removeQrScannerOverlayShape == false) {
      return QrScannerOverlayShape(
        borderColor: Colors.red,
        borderRadius: 5,
        borderLength: 15,
        borderWidth: 5,
        cutOutSize: deviceWidth * 0.2,
      );
    } else {
      return QrScannerOverlayShape(
        overlayColor: Colors.black.withOpacity(0.0),
      );
    }
  }</p>
<pre><code>![](https://velog.velcdn.com/images/boost_dev/post/fd0d623c-7159-483b-a7f9-3ed5bef07568/image.jpg)
![](https://velog.velcdn.com/images/boost_dev/post/17b0b646-7507-4d02-a97c-31b932825e33/image.jpg)
![](https://velog.velcdn.com/images/boost_dev/post/306ecaae-b903-4ebd-b0cf-5a12e2cee559/image.jpg)


## 2.8 학습한 모델 실행
&gt;** 소스코드**</code></pre><p>import &#39;dart:convert&#39;;
import &#39;package:dio/dio.dart&#39;;
import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_secure_storage/flutter_secure_storage.dart&#39;;
import &#39;package:qr_code_scanner/qr_code_scanner.dart&#39;;
import &#39;package:screenshot/screenshot.dart&#39;;
import &#39;../const/constant.dart&#39;;</p>
<blockquote>
</blockquote>
<p>class ReturnScreen extends StatefulWidget {
  const ReturnScreen({Key? key}) : super(key: key);</p>
<blockquote>
</blockquote>
<p>  @override
  State<ReturnScreen> createState() =&gt; _ReturnScreenState();
}</p>
<blockquote>
</blockquote>
<p>class _ReturnScreenState extends State<ReturnScreen> {
  final screenshotController = ScreenshotController();
  String? qrCode;
  QRViewController? controller;
  final GlobalKey qrKey = GlobalKey(debugLabel: &#39;QR&#39;);
  bool removeQrScannerOverlayShape = false;</p>
<blockquote>
</blockquote>
<p>  @override
  Widget build(BuildContext context) {
    double deviceWidth = MediaQuery.of(context).size.width;</p>
<blockquote>
</blockquote>
<pre><code>return Scaffold(
  appBar: AppBar(
    title: Text(&quot;Return Screen&quot;),
  ),
  body: Screenshot(
    controller: screenshotController,
    child: QRView(
      key: qrKey,
      overlay: renderQrScannerOverlayShape(deviceWidth),
      onQRViewCreated: (controller) {
        controller.scannedDataStream.listen(
          (scanData) {
            if (scanData.code == null) {
              return;
            } else {
              qrCode = scanData.code;
              setState(() {
                removeQrScannerOverlayShape = true;
              });
              controller.pauseCamera().then(
                (_) async {
                  final capturedImg = await screenshotController.capture(
                    pixelRatio: 0.50,
                  );
                  showDialog(
                    context: context,
                    builder: (context) {
                      return AlertDialog(
                        title: Column(
                          children: [
                            Image.memory(capturedImg!,
                                width: deviceWidth * 0.75,
                                height: deviceWidth * 0.75),
                            Text(qrCode!),
                            Row(
                              children: [
                                ElevatedButton(
                                  onPressed: () {
                                    setState(
                                      () {
                                        removeQrScannerOverlayShape = false;
                                      },
                                    );
                                    Navigator.pop(context);
                                    controller.resumeCamera();
                                  },
                                  child: Text(&quot;다시 스캔&quot;),
                                ),
                                FutureBuilder(
                                  future: FlutterSecureStorage()
                                      .read(key: &quot;token&quot;),
                                  builder: (context, snapshot) {
                                    return ElevatedButton(
                                      onPressed: () async {
                                        var response = await Dio().post(
                                          &#39;$API_DOMAIN/flutter/return&#39;,
                                          data: jsonEncode(
                                            {
                                              &quot;token&quot;: snapshot.data,
                                              &quot;qr_code&quot;: qrCode,
                                              &quot;image&quot;: capturedImg,
                                            },
                                          ),
                                        );
                                        print(
                                            &quot;response : ${response.toString()}&quot;);
                                        Navigator.of(context).pop();
                                      },
                                      child: Text(&quot;반납&quot;),
                                    );
                                  },
                                ),
                              ],
                            ),
                          ],
                        ),
                      );
                    },
                  );
                },
              );
            }
          },
        );
      },
    ),
  ),
);</code></pre><p>  }</p>
<blockquote>
</blockquote>
<p>  QrScannerOverlayShape renderQrScannerOverlayShape(deviceWidth) {
    if (removeQrScannerOverlayShape == false) {
      return QrScannerOverlayShape(
        borderColor: Colors.red,
        borderRadius: 5,
        borderLength: 15,
        borderWidth: 5,
        cutOutSize: deviceWidth * 0.2,
      );
    } else {
      return QrScannerOverlayShape(
        overlayColor: Colors.black.withOpacity(0.0),
      );
    }
  }
}</p>
<p>```</p>
<p><strong>구현된 화면</strong>
<img src="https://velog.velcdn.com/images/boost_dev/post/872ac826-5562-4266-85ed-9b87bb76a636/image.jpg" alt=""></p>
<p><strong>결과</strong>
<img src="https://velog.velcdn.com/images/boost_dev/post/e0c629a1-a4dd-4548-a298-bc0b1454520d/image.png" alt=""></p>
<ul>
<li>로컬에서는 우산을 인식한 모델이 앱에서는 우산인식을 못하는 문제 발생</li>
</ul>
<p><strong>대안</strong></p>
<ul>
<li>우산 내부 인식이 부족하다고 생각해서 파손 여부 판단에 필요한 우산 내부 이미지를 늘려서 재학습</li>
<li>리턴된 값 확인</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[서비스형 AI 구축]]></title>
            <link>https://velog.io/@boost_dev/%EC%84%9C%EB%B9%84%EC%8A%A4%ED%98%95-AI-%EA%B5%AC%EC%B6%95-1ceykp9o</link>
            <guid>https://velog.io/@boost_dev/%EC%84%9C%EB%B9%84%EC%8A%A4%ED%98%95-AI-%EA%B5%AC%EC%B6%95-1ceykp9o</guid>
            <pubDate>Wed, 22 Mar 2023 06:49:23 GMT</pubDate>
            <description><![CDATA[<h1 id="0-기본적인-개발-준비-✅">0. 기본적인 개발 준비 ✅</h1>
<h2 id="01-version-check">0.1 version check</h2>
<ul>
<li><strong>작업 전 프로젝트 파일들이 완전 무결한 상태로 git push 후 작업(권장)</strong></li>
<li><strong>Windows10 환경에서 설치</strong></li>
</ul>
<p><a href="https://www.notion.so/Windows-3dade4f2cfa9454c93dc28fdf4b9b049"><strong>Windows 초기 계정으로 설치하기(권장)</strong></a></p>
<ul>
<li><a href="https://www.google.co.kr/chrome/?brand=YTUH&amp;gclid=CjwKCAiAwc-dBhA7EiwAxPRylNIcNIsuaHN71uiXkyuIH-y2eY-tzw83egikZ3Gvko9D488dBdu_WBoCmE8QAvD_BwE&amp;gclsrc=aw.ds"><strong>chrome</strong></a></li>
<li><a href="https://kr.bandisoft.com/bandizip/"><strong>BandiZip</strong></a></li>
<li><a href="https://slack.com/intl/ko-kr/downloads/windows"><strong>Slack</strong></a>   </li>
<li><a href="https://www.notion.so/ko-kr/desktop"><strong>Notion</strong></a>     </li>
<li><a href="https://git-scm.com/downloads"><strong>Git</strong></a></li>
<li><a href="https://learn.microsoft.com/ko-kr/windows/terminal/install"><strong>Windows Terminal</strong></a></li>
</ul>
<hr>
<h2 id="02-아나콘다-설치">0.2 <a href="https://www.anaconda.com/">아나콘다 설치</a></h2>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/b44c93ca-00dd-4b4e-822c-ebc1c540c6c7/image.png" alt="ANACONDA install"></p>
<pre><code>⇒ 2022. 12. ver 설치 시 위 옵션 외에 디폴트로 설정되어 있는 값을 수정할 필요 없었음

    위 옵션을 선택해야 아나콘다가 전역으로 설치됨</code></pre><h3 id="021-환경변수-설정">0.2.1 <strong>환경변수 설정</strong></h3>
<pre><code>    # powershell
    Windows + R
    sysdm.cpl ,3

    # 시스템 변수 생성
    ANACONDA_HOME1
    C:\ProgramData\Anaconda3
    ANACONDA_HOME2
    C:\ProgramData\Anaconda3\Scripts
    ANACONDA_HOME3
    C:\ProgramData\Anaconda3\Library\bin
    ANACONDA_HOME4
    C:\ProgramData\Anaconda3\Library\mingw-w64\bin

    # path 변수 편집
    %ANACONDA_HOME1%
    %ANACONDA_HOME2%
    %ANACONDA_HOME3%
    %ANACONDA_HOME4%</code></pre><h3 id="022-windows-terminal-관리자-권한--permission-부여--사용-중인-가상환경-시각화">0.2.2 Windows Terminal 관리자 권한 / Permission 부여 / 사용 중인 가상환경 시각화</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/479c670c-8b11-40b2-9017-c16f25d32c9f/image.png" alt=""></p>
<blockquote>
<h4 id="powershell">powershell</h4>
<h5 id="윈도우-터미널-관리자-권한으로-실행"><strong>윈도우 터미널 관리자 권한으로 실행</strong></h5>
<pre><code>PS C:\Users\Quiet&gt; conda init
PS C:\Users\Quiet&gt; Set-ExecutionPolicy RemoteSigned
PS C:\Users\Quiet&gt; Get-ExecutionPolicy  # 권한 확인</code></pre><h5 id="윈도우-터미널-재실행"><strong>윈도우 터미널 재실행</strong></h5>
<pre><code>(base) PS C:\Users\Quiet&gt;</code></pre><h5 id="원하는-가상환경으로-실행"><strong>원하는 가상환경으로 실행</strong></h5>
<pre><code>(base) PS C:\Users\Quiet&gt; conda activate {가상환경 이름}
(가상환경 이름) PS C:\Users\Quiet&gt; 
```</code></pre></blockquote>
<pre><code>⇒ **conda 명령어가 먹히는 것을 확인한 상태에서 PyCharm을 설치하는 것을 권장**</code></pre><hr>
<h2 id="03-pycharm--professional"><strong>0.3 <a href="https://www.jetbrains.com/ko-kr/pycharm/download/#section=windows">Pycharm</a> : Professional</strong></h2>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/540c9835-903c-4d26-a056-a4ba663b4284/image.png" alt=""></p>
<blockquote>
<h4 id="31-pycharm-installation-options">3.1 PyCharm Installation Options</h4>
</blockquote>
<ul>
<li>Create Desktop Shortcut : 바탕화면 Pycharm 바로가기 생성</li>
<li>Update Context Menu : PC 내 임의의 폴더를 마우스 우클릭으로 PyCharm 에서 프로젝트로 선택한 폴더를 열 수 있는 옵션</li>
<li>Create Associations : PyCharm IDE에서 .py 확장자 파일을 열게 해주는 옵션</li>
<li>Update PATH Variable : 명령 프롬프트에서 PyCharm에 직접 접근하게 해주는 옵션</li>
<li>(New Project) Settings &gt;&gt; Python Interpreter &gt;&gt; 아래 경로의 Python.exe 선택  <blockquote>
<blockquote>
<p>💡 C:\ProgramData\Anaconda3  # base interpreter</p>
</blockquote>
</blockquote>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/7fc405d5-21ac-441d-81a3-7b805bc87480/image.png" alt=""></p>
<pre><code>    ⇒ PyCharm 22.2.4 버전에서는 Python Interpreter의 이름이 Python 3.9 로 나타났었으나, 

        PyCharm 22.3.1 버전에서는 위의 이름처럼 경로를 나타내는 식으로 만들어진다.

        show all 해준 후 Python Interpreter rename을 해줄 수는 있으나, 굳이…</code></pre><hr>
<h2 id="04-install-django-framework"><strong>0.4 Install Django Framework</strong></h2>
<h3 id="041-microsoft-c-build-tools-install">　0.4.1 <a href="https://visualstudio.microsoft.com/ko/visual-cpp-build-tools/"><strong>Microsoft C++ Build Tools Install</strong></a></h3>
<h3 id="042-microsoft-c-build-tools---visual-studio">　0.4.2 <a href="https://visualstudio.microsoft.com/ko/visual-cpp-build-tools/">Microsoft C++ Build Tools - Visual Studio</a></h3>
<pre><code>    ![](https://velog.velcdn.com/images/boost_dev/post/6d1a0cbe-5df3-4daf-bbc7-97ef99e3edd3/image.png)

    ⇒ 위의 Tools를 선택하고 우측 하단의 설치 버튼 클릭(약 7GB)</code></pre><h3 id="043-pip-install">　0.4.3 <strong>pip install</strong></h3>
<blockquote>
<h5 id="431-install-django">4.3.1 install django</h5>
<pre><code>    # powershell
    (base) PS C:\Users\Quiet&gt; python -m pip install --upgrade pip
    (base) PS C:\Users\Quiet&gt; pip install django
    (base) PS C:\Users\Quiet&gt; pip install djangorestframework</code></pre><h5 id="432-create-new-django-projectapptable">4.3.2 Create New Django Project/App(table)</h5>
<pre><code>    # powershell
    (base) PS C:\Users\Quiet\DjangoProject&gt; django-admin startproject {project_name}
    (base) PS C:\Users\Quiet\DjangoProject&gt; django-admin startapp {app_name=table_name}
    (base) PS C:\Users\Quiet\DjangoProject&gt; python manage.py makemigrations
    (base) PS C:\Users\Quiet\DjangoProject&gt; python manage.py migrate
    (base) PS C:\Users\Quiet\DjangoProject&gt; python manage.py runserver
    http://127.0.0.1:8000 Ctrl + 클릭</code></pre></blockquote>
<hr>
<h2 id="05-docker-with-mysql">0.5 <a href="https://www.docker.com/products/docker-desktop/"><strong>Docker with MySQL</strong></a></h2>
<h3 id="051-202212-기준-디폴트로-설정되어-있는-값으로-설치">　0.5.1 2022.12. 기준 디폴트로 설정되어 있는 값으로 설치</h3>
<h3 id="052-재부팅-후-나타나는-링크로-들어가서-업데이트-진행">　0.5.2 재부팅 후 나타나는 링크로 들어가서 업데이트 진행</h3>
<h3 id="053-create-mysql-container">　0.5.3 <strong>create mysql container</strong></h3>
<pre><code>    # powershell
    # 윈도우 터미널을 통해 Docker Container 목록 출력하기
    # (-a 옵션은 실행하지 않은 Container도 출력)
    (base) PS C:\Users\Quiet\DjangoProject&gt; **docker ps -a**

    # 윈도우 터미널을 통해 Docker Container를 생성 및 실행하기
    (base) PS C:\Users\Quiet\DjangoProject&gt; **docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root mysql:5.7**

    # 컨테이너 접속
    # (container ID 값에 해당하는 uuid 값을 컨테이너 name 자리에 넣어주어도 실행할 수 있다)
    (base) PS C:\Users\Quiet\DjangoProject&gt; **docker exec -it mysql bash**

    # yum update
    bash-4.2# **yum update**

    # yum install -y vim
    bash-4.2# **yum install -y vim**

    # my.cnf 설정
    bash-4.2# **cd etc**
    bash-4.2# **vim my.cnf**

    # 키보드 i 누르면 insert 가능
    [client] 
    default-character-set = utf8mb4 

    [mysql] 
    default-character-set = utf8mb4

    [mysqld]  # 추가해주기
    lower_case_table_names=1
    collation-server = utf8_unicode_ci 
    init-connect=&#39;SET NAMES utf8&#39; 
    character-set-server = utf8

    # 키보드 esc 누르고 &quot;:wq!&quot; 입력 후 Enter
    (base) PS C:\Users\Quiet\DjangoProject&gt; exit
    (base) PS C:\Users\Quiet\DjangoProject&gt; docker restart mysql  # container ID
    (base) PS C:\Users\Quiet\DjangoProject&gt; docker exec -it mysql bash

    # DB서버 접속
    bash-4.2# **mysql -u root -p** (Enter 후 password 입력)
    Enter password:

    # DB서버에 접속하면 Databases와 Tables를 통제할 수 있다.(DB서버의 함수 사용 가능)
    mysql&gt; **show databases;**

    # DB를 생성하고 싶다면 create 함수를 이용한다.
    mysql&gt; **create database mydb;**

    # show에서 볼 수 있는 DB서버의 DB를 사용하고, Tables를 볼 수 있다.
    mysql&gt; **use mydb;**
    mydb&gt; **show tables;**
    mydb&gt; **status;**

    # 서버 실행하기
    ****mydb&gt; **exit**
    Bye
    ****bash-4.2# **exit**
    exit
    ****(base) PS C:\Users\AIA\PycharmProjects\djangoProject&gt; **python .\manage.py runserver**</code></pre><h3 id="054-pycharm-database-tabright-side">　0.5.4 PyCharm Database Tab(right side)</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/9184edce-f6b5-4118-b1d0-eaa29a0c1a95/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/c2ef28a9-31ef-4943-8956-096445fe807e/image.png" alt="">
        ⇒ <strong>Test Connection</strong> 반드시 누르기</p>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/3f9da2ea-1687-446b-b204-d06d4d191a79/image.png" alt=""></p>
<hr>
<h2 id="06-python--mysql-네트워크-연결">0.6 <strong>Python / MySQL 네트워크 연결</strong></h2>
<pre><code># powershell
# Python과 MySQL 네트워크 연결을 위한 라이브러리 설치
(base) PS C:\Users\Quiet&gt; pip install mysqlclient
(base) PS C:\Users\Quiet&gt; pip install mysql-connector-python
(base) PS C:\Users\Quiet&gt; pip install django-cors-headers</code></pre><hr>
<h2 id="07-install-jdk11">0.7 <strong><a href="https://jdk.java.net/java-se-ri/11">Install jdk11</a></strong></h2>
<blockquote>
<h4 id="71-환경변수-설정">　7.1 <strong>환경변수 설정</strong></h4>
</blockquote>
<pre><code>    # powershell
    # **Windows + R**
    sysdm.cpl ,3

    **# 시스템 변수 생성**
    JAVA_HOME
    C:\Program Files\Java\jdk-11

    # **path 변수 편집**
    %JAVA_HOME%\bin
    ```</code></pre><hr>
<h2 id="08-install-vscode">0.8 <strong><a href="https://code.visualstudio.com/docs/?dv=win">Install VSCode</a></strong></h2>
<h3 id="081-vscode-installation-options">0.8.1 <strong>VSCode Installation Options</strong></h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/bf73850f-0d9f-4fbe-af8a-3494a48abb90/image.png" alt=""></p>
<h3 id="082-install-extensions">0.8.2 <strong>Install Extensions</strong></h3>
<blockquote>
<p><strong>Install docker, ES7, JavaScript(ES6)</strong>
<img src="https://velog.velcdn.com/images/boost_dev/post/02fe73f0-39a0-461d-9a43-5ee135831a37/image.png" alt=""></p>
</blockquote>
<hr>
<h2 id="09install-nodejs">0.9<strong><a href="https://nodejs.org/en/download/">Install node.js</a></strong></h2>
<p>　⇒ <a href="https://nodejs.org/download/release/latest-v16.x/">node.js 최신버전은 문제가 있다고 판단해서 16버전을 설치하려고 한다.</a></p>
<p>　⇒ 추후 설치할 next.js에서 node.js 18버전 사용 시 정상적으로 동작하지 않았다.</p>
<p>  　⇒ NEXT.JS 공식 홈페이지에서 <a href="https://nextjs.org/blog/next-13"><strong>13버전 릴리즈</strong></a>에 대한 설명에도 그에 대한 설명은 없었다.</p>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/d456cab8-b427-46c9-a007-9d6712155344/image.png" alt=""></p>
<pre><code>일단은 굳이 다른 시도를 하지 않고 16버전을 설치하는 것을 추천



아래 설치과정은 동일하다.</code></pre><p><img src="https://velog.velcdn.com/images/boost_dev/post/710383a2-cb5e-43d9-9730-e618665e28e6/image.png" alt=""></p>
<pre><code>⇒ check하면 **python 3.11 등 불필요한 것들이 설치**된다. (**좀 큰일남**)</code></pre><h3 id="091-환경변수-설정">0.9.1 <strong>환경변수 설정</strong></h3>
<pre><code>    # powershell
    # **Windows + R**
    sysdm.cpl ,3

    **# 시스템 변수 생성**
    NODE_HOME
    C:\Program Files\nodejs

    # **path 변수 편집**
    %NODE_HOME%
    ```</code></pre><h3 id="092-install-yarn--redux">0.9.2 <strong>Install yarn &amp; redux</strong></h3>
<pre><code>```powershell
# Install yarn(node.js 설치하며 따라온 npm을 활용하여 설치)
(base) PS C:\Users\Quiet&gt; npm install --location=global
(base) PS C:\Users\Quiet&gt; npm install yarn -g
        =&gt; 에러나면 npm install yarn --force (웬만하면 -g로)
(base) PS C:\Users\Quiet&gt; yarn install
(base) PS C:\Users\Quiet&gt; yarn add react-router-dom
(base) PS C:\Users\Quiet&gt; yarn add redux
(base) PS C:\Users\Quiet&gt; yarn add @reduxjs/toolkit
(base) PS C:\Users\Quiet&gt; yarn add react-redux

# run yarn server
(base) PS C:\Users\Quiet&gt; yarn start
```

- react auto save : react screen 실시간 반영</code></pre><p><img src="https://velog.velcdn.com/images/boost_dev/post/832d6689-1b85-4d93-8661-48b9a1f582ee/image.png" alt=""></p>
<hr>
<h2 id="010-install-tensorflow--pytorch">0.10 <strong>Install Tensorflow &amp; Pytorch</strong></h2>
<h3 id="0101-version-checktensorflow-python-cudnn-cuda">0.10.1 <a href="https://www.tensorflow.org/install/source#gpu">Version Check(Tensorflow, Python, cuDNN, CUDA)</a></h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/a72cc286-bef0-480c-bc95-54776fb57f59/image.png" alt=""></p>
<pre><code>⇒ Tensorflow gpu 사용을 위해 2.10.0 버전을 사용한다.</code></pre><p><img src="https://velog.velcdn.com/images/boost_dev/post/f34a8b98-b1f5-46fc-a2b9-06adf1b02e5a/image.png" alt=""></p>
<hr>
<h2 id="011-install-nvidia-drivergeforce-rtx-2080">0.11 <strong>Install NVIDIA Driver(GeForce RTX 2080)</strong></h2>
<h3 id="0111-최신-공식-nvidia-드라이버-다운로드">0.11.1 <a href="https://www.nvidia.co.kr/Download/index.aspx?lang=kr">최신 공식 NVIDIA 드라이버 다운로드</a></h3>
<p>　　　⇒ 그래픽카드(GeForce RTX 2080) 기준 설치
<img src="https://velog.velcdn.com/images/boost_dev/post/1ca2cdfc-6a05-4ac6-a12c-4e965279046f/image.png" alt=""></p>
<pre><code>    ⇒ 디폴트로 설정된 옵션으로 설치</code></pre><hr>
<h2 id="012-install-cudaver-112">0.12 <strong>Install CUDA(ver 11.2)</strong></h2>
<blockquote>
<h4 id="uninstall-nvidia-frameview-sdk"><strong>uninstall NVIDIA FrameView SDK</strong></h4>
<pre><code>📖 appwiz.cpl &gt;&gt; NVIDIA FrameView SDK 1.3.8107.31782123</code></pre></blockquote>
<h3 id="0121-cuda-toolkit-112-downloads">0.12.1 <strong><a href="https://developer.nvidia.com/cuda-11.2.0-download-archive?target_os=Windows&amp;target_arch=x86_64&amp;target_version=10&amp;target_type=exelocal">CUDA Toolkit 11.2 Downloads</a></strong></h3>
<pre><code>        ⇒ 디폴트로 설정된 옵션으로 설치</code></pre><hr>
<h2 id="013-install-cudnn810">0.13 <strong>Install cuDNN(8.1.0)</strong></h2>
<h3 id="0131-nvidia-developer-login">0.13.1 <strong><a href="https://developer.nvidia.com/">NVIDIA Developer</a> login</strong></h3>
<h3 id="0132-download-cudnn-v810-january-26th-2021-for-cuda-110111-and-112">0.13.2 <strong><a href="https://developer.nvidia.com/rdp/cudnn-archive">Download cuDNN v8.1.0 (January 26th, 2021)</a>, for CUDA 11.0,11.1 and 11.2</strong></h3>
<h3 id="0133-압축파일-다음-경로에-풀기">0.13.3 <strong>압축파일 다음 경로에 풀기</strong></h3>
<blockquote>
<p> 📖 <strong>C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.2\include</strong></p>
</blockquote>
<h3 id="0134-환경변수-설정">0.13.4 <strong>환경변수 설정</strong></h3>
<pre><code>    # powershell
    # **Windows + R**
    sysdm.cpl ,3

    **# 시스템 변수 생성**
    CUDA_HOME1
    C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.2\bin
    CUDA_HOME2
    C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.2\include
    CUDA_HOME3
    C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.2\lib

    # **path 변수 편집**
    %CUDA_HOME1%
    %CUDA_HOME2%
    %CUDA_HOME3%
    ```</code></pre><h3 id="0135-install-tensorflow2101">0.13.5 <strong>Install Tensorflow(2.10.1)</strong></h3>
<blockquote>
<p><strong>0.13.5.1 Update conda and pip</strong></p>
<pre><code>   # powershell
       # 관리자 모드 윈도우 탐색기
       (base) PS C:\&gt; pip install --upgrade pip
       (base) PS C:\&gt; conda update -n base conda
       (base) PS C:\&gt; conda update --all</code></pre><p><strong>0.13.5.2 Install tensorflow &amp; tensorflow-gpu</strong>
       # powershell
       # 관리자 모드 윈도우 탐색기
         (base) PS C:&gt; pip install tensorflow==2.10.1
         (base) PS C:&gt; pip install tensorflow-gpu==2.10.1</p>
</blockquote>
<pre><code>   # 가상환경에 설치되었는지 확인하기
     (base) PS C:\&gt; conda list</code></pre><h2 id="014-install-pytorch1131">0.14 <strong>Install Pytorch(1.13.1)</strong></h2>
<h3 id="-0141-check-install-code"><a href="https://pytorch.org/get-started/locally/">**　0.14.1 Check Install code**</a></h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/60419cf7-87a0-4719-af3e-a41f56d50e31/image.png" alt=""></p>
<pre><code>        ⇒ Conda Package로는 설치가 되지 않았다.(원인미상)</code></pre><blockquote>
<pre><code> # powershell
   (base) PS C:\Users\Quiet&gt; pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu117</code></pre></blockquote>
<blockquote>
<p>※  <strong>참고</strong> CUDA를 분명 11.2를 설치했는데 11.7로 설치하는 이유가 뭐지?<br>📖 <a href="https://kjy042386.tistory.com/324">https://kjy042386.tistory.com/324</a> </p>
</blockquote>
<pre><code>    ⇒ 추정) pytorch 구동환경 내 cuda library 복사본을 설치

         → 설정값은 CUDA 11.2 지만, pytorch 구동환경 내 상태값은 11.7인 것</code></pre><h2 id="015check-version">0.15<strong>Check Version</strong></h2>
<pre><code>    #**Python Code**

    # python
    import os
    import numpy as np
    import tensorflow as tf
    import torch
    import sklearn
    from tensorflow.python.client import device_lib

    # TF_CPP_MIN_LOG_LEVEL Default Setting 관련 경고 임시 조치
    os.environ[&#39;TF_CPP_MIN_LOG_LEVEL&#39;] = &#39;3&#39;

    if __name__ == &#39;__main__&#39;:
        print(f&#39;numpy version : {np.__version__}&#39;)
        print(f&#39;tensorflow version : {tf.__version__}&#39;)
        print(f&#39;torch version : {torch.__version__}&#39;)
        print(f&#39;sklearn version : {sklearn.__version__}&#39;)
        print(f&#39;이 PC에 설치된 디바이스 상세보기 : {device_lib.list_local_devices()}&#39;)
        print(f&#39;CUDA 프로그래밍 가능여부 :  {torch.cuda.is_available()}&#39;)
        print(f&#39;CUDA 프로그래밍 가능여부 : {torch.cuda.get_device_name()}&#39;)
        print(f&#39;사용 가능 GPU 갯수 :  {torch.cuda.device_count()}&#39;)
    ```</code></pre><hr>
<h2 id="016-install-fastapi--uvicorn">0.16 <strong>Install FastAPI &amp; uvicorn</strong></h2>
<h3 id="0161-pip-install">0.16.1 pip install</h3>
<pre><code>    #powershell
    # Install
    (base) PS C:\fastApiServer&gt; pip install fastapi &#39;uvicorn[standard]&#39;

    # 서버 생성
    (base) PS C:\fastApiServer&gt; uvicorn main:app --reload</code></pre><hr>
<h2 id="017-install-android-studio">0.17 <strong><a href="https://developer.android.com/studio?gclid=Cj0KCQiA5NSdBhDfARIsALzs2ECjPu7ggxa8yjtBXMPyBMGSPCJTYmYeqcXkTxga96jC8L9YUIJXRQQaAjF6EALw_wcB&amp;gclsrc=aw.ds">Install Android Studio</a></strong></h2>
<pre><code>⇒ 디폴트 옵션 설치 후 재실행되는 프로그램에서 라이센스 전체 Accept</code></pre><p><img src="https://velog.velcdn.com/images/boost_dev/post/fad69465-80ff-4b39-ba9c-fa4e0693acf0/image.png" alt=""></p>
<hr>
<h2 id="018-install-next">0.18 Install next</h2>
<pre><code># powershell
yarn add next
npm install --location=global
yarn build
yarn add react
yarn add react dom


⇒ git clone 해온 것을 그대로 사용할 때는 다음 코드만 실행해도 됨

# powershell
npm install --location=global
yarn dev  # 두 번 실행하기</code></pre><blockquote>
<h4 id="-pagination-설정">+ <a href="https://dominicarrojado.com/posts/how-to-create-your-own-pagination-in-react-and-typescript-with-tests-part-1/">pagination 설정</a></h4>
<pre><code># powershell
# Windows Terminal에서 설치
(base) PS C:\Users\Quiet&gt; **pip install fastapi-pagination**</code></pre></blockquote>
<hr>
<h2 id="019-install-flutter-ver3">0.19 <strong><a href="https://docs.flutter.dev/get-started/install/windows">Install Flutter ver.3</a></strong></h2>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/28607127-ff85-4638-b8ba-f481d38bbdab/image.png" alt=""></p>
<pre><code>⇒ 경로에 **한글/공백** 있으면 안됨

⇒ 공백없는 영어 사용자 폴더에 설치하고, 환경변수를 등록해주어야 함</code></pre><hr>
<h2 id="020-애자일-방식을-위한-tool-연동">0.20 애자일 방식을 위한 Tool 연동</h2>
<h3 id="0201-slsck-jira-연동">0.20.1 slsck-jira 연동</h3>
<ul>
<li>프로젝트 설정
<img src="https://velog.velcdn.com/images/boost_dev/post/208c6014-e84f-4339-877e-3eacb3d9e14d/image.PNG" alt=""></li>
<li>Connect to Slack 클릭
<img src="https://velog.velcdn.com/images/boost_dev/post/bc97d05d-f12e-415f-b046-ae7f54ca9eda/image.png" alt=""></li>
</ul>
<h3 id="0202-jira-confluence-연동">0.20.2 jira-Confluence 연동</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/3f5646e0-1605-47ef-a887-bbba3ced0e98/image.png" alt="">
<img src="https://velog.velcdn.com/images/boost_dev/post/e23b8578-72d5-4313-bc39-9ace0cb4018c/image.png" alt="">
<img src="https://velog.velcdn.com/images/boost_dev/post/0d2b4874-57a4-4bb4-bda6-612bb5df816f/image.png" alt=""></p>
<h3 id="0203-jira-github-연동">0.20.3 jira-GitHub 연동</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/ad5fdcf3-8f6f-4a10-b21b-081a54e861f5/image.png" alt=""></p>
<ul>
<li>GitHub for Jira 검색
<img src="https://velog.velcdn.com/images/boost_dev/post/0230f032-f07a-46b1-8cb4-6433cce7d796/image.png" alt=""></li>
<li>바로우산에서 사용한 앱
<img src="https://velog.velcdn.com/images/boost_dev/post/1659e4b7-2dea-4198-8082-8e55629e69da/image.PNG" alt=""></li>
<li>추가한다
<img src="https://velog.velcdn.com/images/boost_dev/post/677084c7-7f54-4398-a5dd-d37a188532d8/image.png" alt=""></li>
<li>추가한 뒤에는 컨플루언스의 업무에서 브랜치 만들기, 커밋 만들기를 누르면
<img src="https://velog.velcdn.com/images/boost_dev/post/07d45cf3-28df-4e57-b1c3-7378ea943378/image.png" alt=""></li>
<li>아래 이미지가 나오고 Create branch를 누르면 깃허브에 브랜치가 생성된다
<img src="https://velog.velcdn.com/images/boost_dev/post/48a5daa3-030d-4c78-8be4-8b24ef20fa66/image.png" alt=""></li>
</ul>
<hr>
<h1 id="1-aws☁">1. AWS☁</h1>
<h2 id="11-설정">1.1 설정</h2>
<ul>
<li>fastapi requirements.txt에 추가</li>
<li>main.py에 handler = Mangum(app) 추가</li>
</ul>
<h3 id="111-db">1.1.1 DB</h3>
<ul>
<li>데이터 베이스 수정에서 db 파라미터를 사용중인 db로 변경</li>
<li>db가 제대로 들어갔는지는 <a href="https://github.com/Seongbae103/memo/blob/main/settings/setting(sql).MD">하이디</a>로 확인</li>
<li>하이디의 ip는 db의 연결 보안에 있는 엔드포인트</li>
<li>fastapi의 db도 host는 AWS의 엔드포인트 <blockquote>
<p>이제 docker/mysql 내부의 코드들은 발생x, docker/api만 발생</p>
</blockquote>
</li>
</ul>
<hr>
<h2 id="12-진행-순서">1.2 <strong>진행 순서</strong></h2>
<blockquote>
<p>RDS -&gt; IAM -&gt; S3</p>
</blockquote>
<ul>
<li>RDS는 db에 들어가는 애</li>
<li>s3는 화면을 보여주는 애</li>
</ul>
<h2 id="13-a-href--httpsparksrazortistorycom386rdsa">1.3 <a href = "https://parksrazor.tistory.com/386">RDS</a></h2>
<h3 id="131-rds-작성">1.3.1 RDS 작성</h3>
<ul>
<li>도커파일 및 yml파일 변경 후 dockerfile의 경로인 docker/api로 들어가서 &#39;docker build -t 이미지명 .&#39; 입력</li>
<li>원래 경로로 돌아가서 docker compose up</li>
<li>자동으로 테이블이 들어와야 성공
🐋 <a href="https://velog.io/@boost_dev/Docker-Docker-compose">docker compose up 방법이 기억안나면</a></li>
</ul>
<h3 id="132-rds-인스턴스-생성">1.3.2 <a href="https://velog.io/@boost_dev/AWS-RDS">RDS 인스턴스 생성</a></h3>
<p><strong>생성하면서 확인</strong></p>
<ul>
<li>표준 생성</li>
<li>사용 엔진(이번에는 MySQL)</li>
<li>템플릿 : 프리티어</li>
<li>인스턴스 크기 : t3(t2도 상관없으나 t3도 프리데이터)</li>
<li>스토리지 : 범용, 20, 자동 조정 비활성화
※ 스냅샷 체크를 해제해도 사용으로 된 경우도 있으므로 확인 필요</li>
</ul>
<h3 id="133-보안그룹-생성">1.3.3 보안그룹 생성</h3>
<blockquote>
<p>주의사항</p>
<blockquote>
<p>잘못하면 DB 해킹의 위험이 있는 부분이므로 주의</p>
</blockquote>
<ul>
<li>SecurityGroup 설정에서 보안 그룹을 생성하고 인바운드 규칙을 추가</li>
<li>외부 접속 방지를 위해 로컬에서 개발하는 동안은 개인 IP 주소를 설정하지만 EC2로 접속하는 과정이 있으면 추가<ul>
<li>위치 무관을 체크해야 ubuntu에서 apt-get-update가 적용된다 </li>
</ul>
</li>
<li>우측 상단 수정 → 생성한 식별자 선택 <ul>
<li>퍼블릭 액세스: 예(마이너 버전으로 변경시 요금 발생)</li>
<li>포트폴리오 사용할 일 없으면 : 아니오</li>
<li>백업 보존기간 : 0일</li>
<li>스냅샷 : 해제</li>
</ul>
</li>
</ul>
</blockquote>
<p>이거 보고도 모르겠으면 <a href="https://parksrazor.tistory.com/386">여기 참고</a></p>
<h3 id="134-파라미터-그룹-생성">1.3.4 파라미터 그룹 생성</h3>
<blockquote>
<p>좌측사이드바에서 파라미터 그룹(이하 Parameters)에서 파라미터 편집으로 넘어간다. 검색창에 char, coll 을 각각 입력 후 셀렉트박스에서 utf-8과 utf-8_general_ci 를 선택</p>
</blockquote>
<ul>
<li>DB 수정 → DB옵션에서 파라미터 그룹을 방금 설정한 그룹으로 변경</li>
</ul>
<h2 id="14-iam">1.4 IAM</h2>
<h3 id="141-iam-생성하기">1.4.1 IAM 생성하기</h3>
<blockquote>
<p>S3 만들기 전에 <strong>반드시</strong> iam 을 생성해야 한다. 이 파트를 생략해도 S3 가 만들어지지만, CORS 에서 많은 오류를 경험하기 싫으면 일단 생성하자</p>
</blockquote>
<p>, s3</p>
<hr>
<h2 id="15-s3">1.5 S3</h2>
<h3 id="151-envpy-양식">1.5.1 env.py 양식</h3>
<pre><code>USERNAME = &quot;AWS 유저명&quot;
PASSWORD = &quot;&quot;
HOSTNAME = &quot;aws 사용시의 엔드포인트&quot;
DATABASE = &quot;AWS의 DB명&quot;
PORT = 3306
CHARSET = &quot;utf8&quot;
DB_URL = f&quot;mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}&quot;</code></pre><hr>
<h3 id="152-dockerfile-양식">1.5.2 dockerfile 양식</h3>
<p>FROM python:3.9</p>
<p>WORKDIR /app
ADD requirements.txt .
RUN pip install --trusted-host pypi.python.org -r requirements.txt
EXPOSE 8000
CMD [&quot;uvicorn&quot;, &quot;app.main:app&quot;, &quot;--reload&quot;, &quot;--host&quot;, &quot;AWS엔드포인트&quot;, &quot;--port&quot;, &quot;8000&quot;]</p>
<hr>
<h2 id="16-한글-깨짐">1.6 한글 깨짐</h2>
<ul>
<li>파라미터 그룹 -&gt; 파라미터 편집에서 </li>
<li>char : 전부 utf8mb4로 변경(0,1로 나오는 항목은 제외)</li>
<li>coll : collation_connection, collation_server의 값을 utf8mb4_general_ci로 변경</li>
</ul>
<hr>
<h2 id="17-보안🔒">1.7 보안🔒</h2>
<blockquote>
<p>개인적인 피해를 막기 위한 최소한의 보안</p>
</blockquote>
<ul>
<li><p>인바운드는 어렵게 아웃바운드는 쉽게
1) 사용자 그룹 생성</p>
</li>
<li><p>권한 정책 연결 : AmazonS3FullAccess 선택
2) MFA 설정</p>
</li>
<li><p>준비 : Google OTP 어플리케이션</p>
</li>
<li><p>root 계정에서 보안자격 증명 → MFA 활성화 → 가상 MFA 디바이스 체크 후 계속</p>
</li>
<li><p>먼저 본인 핸드폰에 설치한 Google OTP로 해당 QR코드 인식 후 MFA 코드 1 입력</p>
<ul>
<li>MFA 코드 2는 MFA 코드 1 시간 경과 후 나오는 다음 코드</li>
</ul>
</li>
</ul>
<h3 id="171-root가-iam에게-비밀번호-변경-권한-주기">1.7.1 Root가 IAM에게 비밀번호 변경 권한 주기</h3>
<ul>
<li>IAM에서 &#39;계정 설정&#39; 클릭</li>
<li>&#39;암호 정책-&gt; 편집&#39;에서 체크<ul>
<li>사용자 지정 : 암호감독 체크박스 전체, </li>
<li>기타 요구사항 :사용자 자신의 암호 변경 허용 </li>
</ul>
</li>
</ul>
<h3 id="172-iam-로그인시-비밀번호-지정">1.7.2 IAM 로그인시 비밀번호 지정</h3>
<p>IAM -&gt; 사용자 -&gt; 사용자명 클릭</p>
<ul>
<li><p>보안 자격 증명 -&gt; 콘솔 액세스 관리 -&gt; 활성화 후 비밀번호 지정</p>
</li>
<li><p>권한을 받은 IAM사용자명, 방금 지정한 비밀번호로 로그인 가능</p>
</li>
</ul>
<h3 id="173-권한-부여">1.7.3 권한 부여</h3>
<ul>
<li>Root 사용자의 IAM으로 들어가서 권한을 준다</li>
</ul>
<h3 id="174-iam-사용자-결제-대시보드-접근-권한-설정">1.7.4 IAM 사용자 결제 대시보드 접근 권한 설정</h3>
<ul>
<li>builling은 AdministratorAccess에 포함되어 있다<blockquote>
<p>기본적으로 결제 정보 메뉴에 대한 권한이 없는 IAM 사용자가 해당 권한을 얻기 위한 조건은 </p>
<ul>
<li>(1) 이 계정이 IAM 및 페더레이션 사용자가 결제 정보에 액세스하도록 허용</li>
<li>(2) 필요한 IAM 권한을 보유하도록 함으로써 사용자 또는 그룹에 대해 권한</li>
</ul>
</blockquote>
</li>
</ul>
<ul>
<li>(1)은 Root 계정으로만 설정 가능<ul>
<li>계정 → 결제 정보에 대한 IAM 사용자 및 역할 액세스 :편집 </li>
<li>IMA 액세스 활성화 체크 → 업데이트</li>
</ul>
</li>
</ul>
<h2 id="18-a-href-httpswwwematop3comemaaiblog4-solutions-for-serverless-application-deployment-aws-sam-stackery-serverless-framework-and-pulumiaws에서-lambda-사용a">1.8 <a href ="https://www.ematop3.com/emaaiblog/4-solutions-for-serverless-application-deployment-aws-sam-stackery-serverless-framework-and-pulumi">AWS에서 lambda 사용</a></h2>
<blockquote>
<p>yaml(context file)을 사용하는 Serverless Framework보다 IaC방식(TypeScript 사용)인 Pulumi를 더 많이 사용</p>
</blockquote>
<ul>
<li>serverless와 pulumi 방식중 선택해서 사용</li>
</ul>
<h2 id="19-프리티어-사용시-주의">1.9 프리티어 사용시 주의</h2>
<ul>
<li>인스턴스 구성시 t2로 설정</li>
<li>RDS에서 스토리지의 할당된 스토리지는 20으로(스토리지 자동 조정 활성화는 체크를 해제)</li>
</ul>
<blockquote>
<p>EC2를 사용해서 </p>
</blockquote>
<h3 id="a-href-httpsvelogioyangsijun528aws-ec2ec9980-rds-eca784eca79c-free-tier-ec82acec9aa9ed9598eab8b0주의-a"><a href ="https://velog.io/@yangsijun528/AWS-EC2%EC%99%80-RDS-%EC%A7%84%EC%A7%9C-Free-tier-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0">주의 </a></h3>
<ul>
<li>인공지능 모델은 lambda</li>
<li>상시로 열려있는 페이지는 EC2가 더 유리하다<blockquote>
<p>이제는 매번 도커에 설치할 필요 x</p>
<ul>
<li><a href ="https://parksrazor.tistory.com/386">참고</a></li>
</ul>
</blockquote>
</li>
</ul>
<hr>
<h1 id="2-aws-사용">2. AWS 사용</h1>
<blockquote>
<p>리전은 ACM에서만 버지니아 북부를 사용</p>
</blockquote>
<h2 id="21-aws-계정-생성-및-설정">2.1 AWS 계정 생성 및 설정</h2>
<blockquote>
<p>순서는 cli를 먼저 실행하고 
S3를 실행(버킷없음 오류가 나오면 버킷 생성)
OAC 방식으로 CF 배포 </p>
</blockquote>
<h3 id="211-계정-생성-및-로그인">2.1.1 계정 생성 및 로그인</h3>
<ul>
<li>플랜 선택 시 기본 지원을 선택해야 되는 점만 주의</li>
</ul>
<h3 id="212-cli커맨드-라인-인터페이스">2.1.2 CLI(커맨드 라인 인터페이스)</h3>
<blockquote>
<p>리액트 사용시에는 서버리스 프레임워크를 사용하지만 리액트 같은 프론트 요소가 없는 경우는 CLI를 사용한다</p>
</blockquote>
<blockquote>
<p>CLI? 
AWS 클라우드 리소스를 생성, 편집, 검사하는 방식 중 하나</p>
</blockquote>
<p><strong>1) 언제 사용?</strong></p>
<blockquote>
<p>리액트 같은 프론트엔드 부문의 작업을 위한 경우는 서버리스 프레임워크를 사용하지만, 프론트엔드부분을 신경쓰지 않는 작업에서는 CLI를 사용</p>
</blockquote>
<p><strong>2) 장점</strong></p>
<blockquote>
<ul>
<li>코드(CLI 명령 포함)가 사용자의 변경을 기록</li>
<li>버전 제어(깃 등)하에 코드를 배치하고 변경사항을 효과적으로 관리 =&gt; 커밋하면 아마존 내부의 코드도 변경</li>
<li>수동적인 단계의 수행을 줄여서 작업을 빠르게 다시 실행할 수 있다</li>
<li>인적 오류 발생 가능성이 낮다</li>
</ul>
</blockquote>
<h3 id="213-a-hrefhttpsdocsawsamazoncomko_krclilatestuserguidegetting-started-installhtmlcli-설치a">2.1.3 <a href="https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/getting-started-install.html">CLI 설치</a></h3>
<h3 id="214-로컬-aws-환경-설정">2.1.4 로컬 AWS 환경 설정</h3>
<pre><code>  # 액세스 키 만들기
  - 경로 : AWS 사용자 설정 - 보안자격 증명 - 액세스 키 만들기 - 액세스 키, 비밀 액세스 키, .csv 파일 다운로드(!!!이 내용은 절대 커밋 금지) 
  - 유닉스계열(맥 등)은 셸 설정에서 환경변수를 추가 
  - KMS는 유료 서비스다

  # 설정 확인(아래 코드 차례대로 실행)
  - aws --version
  - aws s3 li s3://
  - aws configure </code></pre><hr>
<h2 id="22-s3-버킷-생성">2.2 S3 버킷 생성</h2>
<blockquote>
<p>OAC방식</p>
</blockquote>
<ul>
<li>OAC 설정시 &#39;웹 사이트 엔드포인트 사용&#39;을 누르면 OAC 설정이 사라지니까 먼저 누르지 말것</li>
<li>S3 버킷 액세스</li>
<li>원본 액세스 제어 설정(권장) - 제어설정 생성 : s3로</li>
<li>속성 - 생성한 버킷에서 정적 웹 사이트 호스팅 편집 - 활성화 후 변경사항 저장(CF연결할거니까 테스트목적 아니면 하지 말것)<ul>
<li>OAC 방식이 아니더라도 ACM(인증서)가 아니면 자신이 사용하는 리전으로 등록해야한다</li>
</ul>
</li>
</ul>
<h3 id="221-정책-설정">2.2.1 정책 설정</h3>
<blockquote>
<ul>
<li>경로 : 정책 - 버킷 정책 - 새 문 추가 - 아래 코드 입력</li>
</ul>
</blockquote>
<pre><code>    {
        &quot;Version&quot;: &quot;2012-10-17&quot;,
        &quot;Statement&quot;: [
            {
                &quot;Sid&quot;: &quot;Statement1&quot;,
                &quot;Effect&quot;: &quot;Allow&quot;,
                &quot;Principal&quot;: &quot;*&quot;,
                &quot;Action&quot;: &quot;s3:GetObject&quot;,    #CRUD기능을 전부 사용할거면 GetObject 대신 * 입력
                &quot;Resource&quot;: &quot;arn:aws:s3:::버킷명/*&quot;
            }
        ]
    }</code></pre><ul>
<li>CORS 코드 추가<pre><code>  [
      {
          &quot;AllowedHeaders&quot;: [
              &quot;*&quot;
          ],
          &quot;AllowedMethods&quot;: [
              &quot;HEAD&quot;,
              &quot;GET&quot;,
              &quot;PUT&quot;,
              &quot;POST&quot;,
              &quot;DELETE&quot;
          ],
          &quot;AllowedOrigins&quot;: [
              &quot;*&quot;
          ],
          &quot;ExposeHeaders&quot;: []
      }
  ]</code></pre></li>
<li>CF 사용시 버킷정책<pre><code>  {
      &quot;Version&quot;: &quot;2012-10-17&quot;,
      &quot;Statement&quot;: [
          {
              &quot;Sid&quot;: &quot;Statement1&quot;,
              &quot;Effect&quot;: &quot;Allow&quot;,
              &quot;Principal&quot;: {&quot;Service&quot;:&quot;cloudfront.amazonaws.com&quot;},  - cloudfront.amazonaws.com를 통해서만 권한 부여
              &quot;Action&quot;: &quot;s3:*&quot;,                                     - 모든 crud를 사용
              &quot;Resource&quot;: &quot;arn:aws:s3:::seongbae.shop/*&quot;,
              &quot;Condition&quot;: {                                  -condition == if
                  &quot;StringEquals&quot; : {                            - &quot;AWS:SoursArn&quot;의 문자열이 &quot;&quot;과 같다면 
                      &quot;AWS:SoursArn&quot;:&quot;클라우드 프론트의 ARN&quot;
                  }
              }
          }
      ]
  }</code></pre></li>
</ul>
<hr>
<h2 id="23-cloudfront">2.3 CloudFront</h2>
<blockquote>
<p>도메인 | CF | S3 <br/></p>
<ul>
<li><a href="https://github.com/Seongbae103/memo/blob/main/howto/AWS_CF_cost.MD">CF관련 비용 절감 방법</a></li>
</ul>
</blockquote>
<ul>
<li>CF를 억지로 만들 필요 없이 도메인과 S3를 만들면 알아서 생성된다<ul>
<li>그러니까 CF를 먼저 만들지 마라</li>
</ul>
</li>
<li>cf의 오리진 = 버킷</li>
</ul>
<h3 id="230-특징">2.3.0 특징</h3>
<ul>
<li>고비용에 더 느리지만 보안에 더 유리</li>
<li>S3에서 CF로의 데이터 전송은 요금이 없으나 인터넷으로 전달하는건 비용이 요구된다</li>
<li>같은 리전에서의 전송만 무료니까 설정시 자신이 속한 리전을 설정</li>
</ul>
<h3 id="231-사용">2.3.1 사용</h3>
<ul>
<li>그냥 버킷 생성한것과 달리 퍼블릭 액세스를 차단한 버킷 사용</li>
</ul>
<h3 id="232-a-hrefhttpsdocsawsamazoncomko_krroute53latestdeveloperguidewelcomehtmldns-연결a아마존은-route53">2.3.2 <a href="https://docs.aws.amazon.com/ko_kr/Route53/latest/DeveloperGuide/Welcome.html">DNS 연결</a>(아마존은 Route53)</h3>
<ul>
<li>route53 - 시작하기 - 호스팅 영역 생성<ul>
<li>NS(Name Server), SOA가 생겼는지 확인 (NS, SOA에 대한 설명 <a href="https://server-talk.tistory.com/176">참고</a>)</li>
</ul>
</li>
<li>값/트래픽 라우팅 대상에 있는 값을 NDS에 등록 (이번에는 가비아에서 도메인을 받음. 상세설명은 <a href="https://customer.gabia.com/manual/dns/3041/3040">여기</a>)<ul>
<li>등록 장소 : 가비아 - My가비아 - 도메인 - 관리 - 네임서버 설정<ul>
<li>가비아는 NS의 마지막 &#39;.&#39;을 지워야 한다</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="233-cname-등록">2.3.3 CNAME 등록</h3>
<blockquote>
<p><strong>AWS에서</strong></p>
</blockquote>
<ul>
<li>AWS Certificate Manager에서 인증상태 확인<blockquote>
<p>인증이 안된 상태면 route53에 레코드 자동 생성을 눌렀는지 생각해보자</p>
</blockquote>
</li>
<li>도메인 - CNAME 확인</li>
<li>&#39;Route53에서 레코드 생성&#39; 클릭<ul>
<li>한 번 지운 경우는   </li>
<li>Route53 - 호스팅 영역에서 CNAME이 생기면 된다<ul>
<li>A 레코드를 추가하면서 &#39;ping 주소&#39;나온 IP입력</li>
</ul>
</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>가비아에서</strong></p>
</blockquote>
<ul>
<li>My가비아 - DNS 관리툴 - DNS설정<ul>
<li>호스트 : <del>.com은 @ / www.</del>.com은 www(이때 www를 쓴다면 값도 www가, admin을 쓰는 주소라면 admin.이 포함돼야 한다)</li>
<li>값/위치 : AWS에서 카피한 CNAME 이름 입력</li>
<li>TTL:3600</li>
</ul>
</li>
</ul>
<blockquote>
<p>✅가비아에서 제대로 등록됐는지 확인하는 방법</p>
<ul>
<li>cmd에서 &#39;ping 주소&#39; 입력</li>
<li><strong><a href="https://xn--c79as89aj0e29b77z.xn--3e0b707e/">후이즈 도메인 검색</a></strong></li>
<li>결과가 안나올 경우 ping 네임서버 1차 주소를 넣고 나온 값을 사용해서 A레코드를 만든다</li>
</ul>
</blockquote>
<ul>
<li>후이즈 통과 후 CF로 이동</li>
</ul>
<h3 id="224-cf-배포">2.2.4 CF 배포</h3>
<ul>
<li>cloundfront 생성에서 원본 도메인 선택하고 <a href="https://sh-t.tistory.com/72">여기를 참고</a><ul>
<li>가격 분류는 본인이 사용할 리전이 속해있는 곳에 해야 과한 비용을 막을 수 있다</li>
<li>CNAME 추가(&#39;www.주소.com&#39;과 &#39;주소.com&#39;을 둘 다 해야한다) -&gt; A 레코드로 연결했으면 CNAME의 값을 도메인 이름으로 해도 괜찮다</li>
<li>ACL를 사용하기 위해서는 먼저 아마존에서 사용하는 DNS인 Route53을 먼저 설정해야 한다(2.2 CNAME 등록 참고)</li>
<li>SSL은 optional로 되어있지만 보안 요소라 사실상 필수</li>
<li>IPv6: off(필수는 아니고 수업에서는 일단 off로 지정함 디폴트는 on)</li>
</ul>
</li>
<li>버킷과 연결<ul>
<li>Amazon S3 - 버킷 - 권한 - 버킷 정책 편집에서 CF의 ARN추가</li>
</ul>
</li>
</ul>
<pre><code>    &quot;Condition&quot;: {
        &quot;StringEquals&quot;: {
            &quot;AWS:SoursArn&quot;: &quot;배포하는 CF의 ARN&quot;
        }
    }</code></pre><h3 id="225-cf-주의사항">2.2.5 CF 주의사항</h3>
<ul>
<li>정적콘텐츠를 s3에 올리지 마라</li>
<li>OAI는 이제 죽으니까 OAC로 가야된다</li>
<li><a href= "https://jw910911.tistory.com/110">AWS : EC2 / S3 / CloudFront 트래픽 요금 분석</a></li>
<li><a href="https://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html">https://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html</a> </li>
<li><a href="https://www.pabburi.co.kr/content/aws/%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C%ED%94%84%EB%A1%A0%ED%8A%B8-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0/">왜 쓰나?</a><blockquote>
<p>자동확장 : 스케일링</p>
</blockquote>
</li>
</ul>
<hr>
<h1 id="23-ec2">2.3 EC2</h1>
<h3 id="231-보안그룹-확인">2.3.1 보안그룹 확인</h3>
<blockquote>
<p>보안그룹 생성은 설정 필기에서 확인</p>
</blockquote>
<h3 id="232-인스턴스-생성">2.3.2 인스턴스 생성</h3>
<blockquote>
<p>경로 : EC2 - 인스턴스 - 인스턴스 생성</p>
<ul>
<li>우분투 선택</li>
<li>인스턴스 시작 후 PuTTY 사용<ul>
<li>.pem을 .ppk로 하는건 Puttygen사용</li>
</ul>
</li>
<li>퍼블릭 ip를 입력하고 세이브 (먼저 있던 경우는 )</li>
<li>Connection - SSH - Credentioal에서 .ppk 연다 </li>
</ul>
</blockquote>
<blockquote>
<h4 id="☕install-java">☕install java</h4>
<p><code>sudo apt-get update</code>
<code>sudo apt-get install -y --no-install-recommends tzdata g++ git curl</code>
<code>sudo apt-get update</code>
<code>sudo apt-get install openjdk-11-jdk</code>
<code>java -version</code>
<code>javac -version</code>
<code>vim ~/.bashrc</code></p>
</blockquote>
<ul>
<li>i 입력으로 insert로 두고 가장 아랫줄에 <ul>
<li>``export JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))<pre><code>        export PATH=$PATH:$JAVA_HOME/bin``` 추가</code></pre></li>
</ul>
</li>
<li>Esc 누르고 :wq!
<code>source ~/.bashrc</code> 설정 적용
<code>echo $JAVA_HOME</code></li>
</ul>
<blockquote>
<h4 id="🐍intall-python">🐍intall python</h4>
<p><code>sudo apt update</code>
<code>sudo apt-get install -y python3-pip python3-dev</code>
<code>sudo apt install software-properties-common</code>
<code>sudo add-apt-repository ppa:deadsnakes/ppa</code></p>
</blockquote>
<h3 id="233-pip3-pip로-변경">2.3.3 pip3 pip로 변경</h3>
<pre><code>cd /usr/local/bin
sudo ln -s /usr/bin/pip3 pip
pip3 install --upgrade pip</code></pre><h3 id="234-sudo-apt-get-clean">2.3.4 sudo apt-get clean</h3>
<h3 id="235-install-nginx">2.3.5 install nginx</h3>
<pre><code># 설치
sudo apt-get update
sudo apt-get install nginx
sudo systemctl start nginx</code></pre><ul>
<li>/etc/nginx/sites-available 내 file에 쓰기 권한이 없으므로 chmod를 쓰기 권한을 추가해준다.
  sudo chmod 775 /etc/nginx/sites-available</li>
<li>nginx config 추가: cd /etc/nginx/sites-available &amp;&amp; vim &lt;서버이름&gt;</li>
</ul>
<blockquote>
<p>퍼미션 에러 나오면 앞에 sudo를 붙혀준다.</p>
</blockquote>
<p>AWS route53에서 도메인을 추가한다. A record를 생성하기 위해서는 고정된 IP 주소가 필요하다. lightsail 인스턴스에서 고정된 public IP를 생성해 A record에 연결한다. 생성한 도메인은 nginx conf에 추가한다.</p>
<pre><code>server{
       server_name &lt;your-site-domain&gt;;
       location / {
           include proxy_params;
           proxy_pass http://사용할 IP:8000;
       }
}</code></pre><ul>
<li>symlink: sites-available/<server-name> → sites-enabled<pre><code>sudo ln -s /etc/nginx/sites-available/&lt;your-server-name&gt; /etc/nginx/sites-enabled/  </code></pre></li>
</ul>
<h3 id="236-run-app">2.3.6 run app</h3>
<p><code>git clone &lt;your-server-repo&gt;</code>
<code>cd /var/www</code>
<code>git clone &lt;server-repo&gt;</code>
<code>cd &lt;server-repo&gt;</code></p>
<ul>
<li>nginx 확인<br><code>sudo nginx -t</code></li>
<li>restart nginx 
<code>sudo systemctl restart nginx.service</code><ul>
<li>안되면 <code>sudo systemctl restart nginx</code><blockquote>
<p>failed가 나올 경우 putty에서 /etc/nginx/sites-available과 /etc/nginx/sites-enabled/에 둘 다 같은 vim 파일이 있는지 확인한다.</p>
</blockquote>
</li>
</ul>
</li>
<li>gunicorn으로 ASGI 서버 실행 : 
<code>python3 -m gunicorn -k uvicorn.workers.UvicornWorker main:app</code></li>
</ul>
<blockquote>
<h4 id="🔗연결-확인">🔗연결 확인</h4>
</blockquote>
<ul>
<li>route 53에 레코드를 CNAME으로 들어간 놈을 A로 바꾸고 그 레코드에 고정 IP(탄력적 ip)로 입력했는지 확인<ul>
<li>putty의 vim 파일이 아래 같은 모양인지 확인
```server{
server_name 사용할 도메인 이름;
location /{<pre><code>include proxy_params;
proxy_pass http://사용할 IP:8000;</code></pre>}
}  <blockquote>
<p>! 사용할 IP자리에 사용하기 위해 받은 탄력적 IP를 넣었을 때 실패</p>
</blockquote>
</li>
<li>사용할 IP에 127.0.0.1(로컬호스트)을 주로 사용하는 이유는 EC2 자체가 하나의 PC이기 때문에 로컬로 돌리는 것이기 때문에다</li>
</ul>
</li>
</ul>
<hr>
<h2 id="24-ec2와-서브-도메인-연결">2.4 EC2와 서브 도메인 연결</h2>
<blockquote>
<p>루트 도메인을 이미 사용했으므로 새 인증서를 요청
<a href="https://blog.nachal.com/1515">참고</a></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/e17cf2dc-fa11-418d-8358-65f3f3aa06cc/image.PNG" alt=""></p>
<p>인증이 완료돼서 CNAME을 얻으면 가비아로 가서 등록
<img src="https://velog.velcdn.com/images/boost_dev/post/69ee5124-d58a-42e1-ba8d-f9576890e69f/image.PNG" alt=""></p>
<h3 id="241-탄력적-ip를-사용">2.4.1 탄력적 IP를 사용</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/a998a790-b1df-44af-a57b-6c1a286f4d0e/image.PNG" alt=""></p>
<ul>
<li><p>할당
<img src="https://velog.velcdn.com/images/boost_dev/post/64f8bc9f-787b-4375-b97b-49783abdde5f/image.PNG" alt=""></p>
</li>
<li><p>확인
<img src="https://velog.velcdn.com/images/boost_dev/post/9b14c7c9-0f27-41e3-8356-6e411350d6f4/image.PNG" alt=""></p>
</li>
<li><p>EC2 연결
<img src="https://velog.velcdn.com/images/boost_dev/post/4e67da32-447f-4774-b7d5-fc7043dd17b0/image.PNG" alt=""></p>
</li>
</ul>
<blockquote>
<h4 id="💻-putty-사용">💻 putty 사용</h4>
<p>EC2의 인스턴스에서 사용할 인스턴스 재시작
Putty에서 .ppk 실행</p>
</blockquote>
<ul>
<li>푸티의 ip는 &#39;EC2- 인스턴스&#39;에서 우클릭 후 연결을 선택해서 여기에 있는 퍼블릭 IP 입력
<img src="https://velog.velcdn.com/images/boost_dev/post/6080429b-d7bf-4418-9fb8-7aca696435e8/image.PNG" alt=""></li>
</ul>
<hr>
<h2 id="2-error😕">2.? error😕</h2>
<h3 id="도메인-주소로-접속시-주소-앞에-주의-요함이-나오는-경우">도메인 주소로 접속시 주소 앞에 &#39;주의 요함&#39;이 나오는 경우</h3>
<p>  <img src="https://velog.velcdn.com/images/boost_dev/post/e9704fed-4a62-4b18-80e0-89fa5b6b96c4/image.png" alt=""></p>
<blockquote>
<p>원인</p>
<blockquote>
<ol>
<li>배포한 CF에 인증서인 SSL이 없는 경우 </li>
</ol>
</blockquote>
<p>해결</p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/d62d91e2-4ec2-42e4-87f3-b20b860de383/image.png" alt=""></p>
</blockquote>
</blockquote>
<h3 id="s3은-되는데-cf가-안되면">s3은 되는데 CF가 안되면?</h3>
<blockquote>
<p>원인 </p>
<blockquote>
<p>route53 문제 </p>
</blockquote>
<p>해결</p>
<blockquote>
<ul>
<li>호스팅 영역 삭제 후 다시 만들어라 (영역 삭제가 안되면 A, CNAME 레코드를 삭제 후 영역 삭제를 진행하면 된다)</li>
<li>A 레코드의 값을 ip가 아니라 CF의 배포 도메인 이름으로 바꿔준다(별칭 활성 - CF 엔드포인트 선택 배포 도메인 이름 입력)<h4 id="-동적-ip를-사용하는-경우는-퍼블릭-엑세스-차단-활성화--cors-코드-삭제">!!! 동적 ip를 사용하는 경우는 퍼블릭 엑세스 차단 활성화 + CORS 코드 삭제</h4>
</li>
<li>자동으로 안들어오는 경우 대체 CF에서 도메인 이름을 설정하면 해결된다</li>
</ul>
</blockquote>
</blockquote>
<hr>
<h1 id="3-aws-lightsail">3. AWS LightSail</h1>
<blockquote>
<p>EC2를 사용하려고 설정했으나 비용 문제가 있어서 성능이 조금 떨어지지만 요금 문제에 비교적 자유로운 LightSail로 변경</p>
</blockquote>
<ul>
<li>앞에서 설정한 route53, cloudFront, 인증서는 삭제 </li>
<li>Lightsail에서는 GPU를 돌리는게 불가능해서 어느정도의 성능 저하는 감안해야한다.</li>
<li>Lightsail 정액제지만 정한 용량을 초과한 순간 과금</li>
</ul>
<h2 id="30-순서">3.0 순서</h2>
<ul>
<li>EC2로 들어갈 파일에서 도커로 먼저 <code>docker compose up</code>실행해서 이미지 생성
1) 인스턴스 생성
2) IP 변경
3) putty</li>
</ul>
<hr>
<h2 id="31-인스턴스-생성lightsail-구매">3.1 인스턴스 생성(Lightsail 구매)</h2>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/917ee010-b465-43e8-9083-29bdbe910667/image.PNG" alt="">
무시하거나 누르거나~
<img src="https://velog.velcdn.com/images/boost_dev/post/18e2ebc2-0f75-474a-92bf-21c23226743f/image.PNG" alt=""></p>
<ul>
<li>자신이 사용할 환경에 맞춰서 구매
<img src="https://velog.velcdn.com/images/boost_dev/post/d60491cb-5ea5-40ff-b2fc-c71ee0938602/image.PNG" alt=""></li>
<li>쿠버네티스를 적용하는 경우 2GB이상의 용량이 필요하므로 4GB 선택
<img src="https://velog.velcdn.com/images/boost_dev/post/dc50ec04-23e0-4997-afb5-4e6b6986f047/image.PNG" alt="">
인스턴스 생성 완료
<img src="https://velog.velcdn.com/images/boost_dev/post/22b487e3-91a5-44b3-90b9-667a3c433777/image.png" alt=""></li>
</ul>
<h2 id="32-고정-ip로-변경">3.2 고정 IP로 변경</h2>
<p>여기 우분투 밑에 있는 아이피는 고정 아이피가 아니므로 고정IP로 바꾸는 설정을 할거다
<img src="https://velog.velcdn.com/images/boost_dev/post/5b0a151d-58d7-407d-9b26-758addf35b6f/image.PNG" alt=""></p>
<p>탄력적 IP 주소에서 연결된 인스턴스가 이미 있는 경우 해당인스턴스의 페이지로 들어간 뒤에 아래 그림처럼 삭제(탄력적 인스턴스도 삭제)
<img src="https://velog.velcdn.com/images/boost_dev/post/e4eb1db2-cf70-4a99-9ac4-0f7d68f9d994/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/b7c462c5-cb25-46e1-bba8-240a996c3d58/image.PNG" alt="">
고정 아이피 생성
<img src="https://velog.velcdn.com/images/boost_dev/post/b1260a56-fc4d-4b69-ad71-4b233727befb/image.PNG" alt=""></p>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/bbd66409-47cf-4cb4-8ef3-34649734655c/image.PNG" alt="">
고정 아이피를 생성하면 아래처럼 나온다</p>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/f7691b41-0cb6-4917-b36b-2018b0dbb806/image.PNG" alt="">
라이트세일 인스턴스에 고정 IP가 제대로 들어왔는지 확인
<img src="https://velog.velcdn.com/images/boost_dev/post/0460d81c-c0f3-4637-bc5a-ceaad644f7a3/image.PNG" alt=""></p>
<p>DNS zone 생성
<img src="https://velog.velcdn.com/images/boost_dev/post/4d13380a-8fa4-46fe-b372-72ae60af0a23/image.PNG" alt=""></p>
<ul>
<li>여기서 Domain name은 사용할 도메인을 입력
<img src="https://velog.velcdn.com/images/boost_dev/post/8538f339-8650-403d-a55c-d811acc3c803/image.PNG" alt="">
만들어진 DNS zone에서 네임서버 확인
<img src="https://velog.velcdn.com/images/boost_dev/post/a0e2b1aa-585c-448e-8312-5fc53bbd399c/image.PNG" alt="">
가비아에서 네임서버 변경(기억이 안나면 이전 내용 참고)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/3511cc79-c821-4a6d-9e4d-c6bd9d48a106/image.PNG" alt="">
레코드를 생성하면 자동으로 aignments가 생성된다(안되면 직접 만들어야 되니까 확인)</p>
<hr>
<h2 id="33-포트-설정">3.3 포트 설정</h2>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/b5e7d156-6e6c-4e69-940e-c36f3c972b8b/image.png" alt="">
<img src="https://velog.velcdn.com/images/boost_dev/post/134f23a8-154f-420c-b429-2dbb2e5bf308/image.PNG" alt=""></p>
<blockquote>
<p>이걸 안해주면 <code>docker compose up</code> 실행시 아래 <a href="https://velog.io/@boost_dev/AWS-Kubernetes-%EC%84%A4%EC%A0%95#2--pymysqlerroperationalerror-2003-cant-connect-to-mysql-server-on-ip%EB%B2%88%ED%98%B8-timed-out">?.2</a>같은 에러가 발생한다</p>
</blockquote>
<hr>
<h2 id="34-서버-설정">3.4 <a href="https://velog.io/@boost_dev/AWS-Lightsail%EC%97%90-fastapi-%EC%84%A4%EC%B9%98">서버 설정</a></h2>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/e31cf2cc-0e8b-4906-8cbf-96c48a4a82c6/image.png" alt=""></p>
<p>😳 사이트를 못찾는다면 http와 https 서로 바꿔보자</p>
<ul>
<li>nohup 사용시 뭔가가 안된다면 라이트세일의 인스턴스로 들어가서 reboot를 한 번 해주자</li>
</ul>
<hr>
<h2 id="35-🐋docker-설정">3.5 🐋Docker 설정</h2>
<blockquote>
<p>안되면 앞에 <code>sudo</code></p>
</blockquote>
<h3 id="351-docker-설치">3.5.1 Docker 설치</h3>
<p><code>apt install apt-transport-https ca-certificates curl software-properties-common</code>
<code>curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -</code>
<code>add-apt-repository &quot;deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable&quot;</code>
<code>apt update</code>
<code>apt-cache policy docker-ce</code>
<code>apt install docker-ce</code>    </p>
<h3 id="352-docker에-sudo-권한-부여">3.5.2 Docker에 sudo 권한 부여</h3>
<p><code>usermod -aG docker $USER</code>
<code>newgrp docker</code></p>
<h3 id="353-설치-확인">3.5.3 설치 확인</h3>
<p><code>systemctl status docker</code></p>
<h3 id="354-docker-compose">3.5.4 Docker-compose</h3>
<pre><code>curl -L &quot;https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)&quot; -o /usr/local/bin/docker-compose</code></pre><h3 id="355-docker-compose에-실행권한-부여">3.5.5 Docker-compose에 실행권한 부여</h3>
<p><code>chmod +x /usr/local/bin/docker-compose</code></p>
<h3 id="356-설치확인">3.5.6 설치확인</h3>
<p><code>systemctl status docker</code></p>
<pre><code>curl -L &quot;https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)&quot; -o /usr/local/bin/docker-compose</code></pre><p><code>chmod +x /usr/local/bin/docker-compose</code>
<code>docker-compose version</code></p>
<hr>
<h2 id="36-🐍python-설치">3.6 🐍Python 설치</h2>
<p><code>sudo apt install python3</code>
<code>sudo apt install python3-venv</code>
<code>python3 -m venv 가상 공간 이름</code></p>
<ul>
<li>가상환경 설정여부 확인
<code>cd 가상환경명</code>
<code>ll</code>
아래 사진처럼 나온다면 ok
<img src="https://velog.velcdn.com/images/boost_dev/post/9561629f-e980-403d-b088-88e8ba33a1df/image.png" alt=""></li>
</ul>
<ul>
<li>가상환경 활성화
<code>source 가상 환경 경로/bin/activate</code>
<img src="https://velog.velcdn.com/images/boost_dev/post/1dee6936-d0bb-4a3e-908a-56f58d7ab663/image.png" alt=""></li>
</ul>
<hr>
<h2 id="37-pycharm-연동">3.7 Pycharm 연동</h2>
<ul>
<li>먼저 .pem 키를 가져오고 
<img src="https://velog.velcdn.com/images/boost_dev/post/10f06698-a1ac-4d7b-a7ee-cb0996eba6ae/image.png" alt="">
<img src="https://velog.velcdn.com/images/boost_dev/post/59f293d4-6bc3-4076-ac03-b8670d072e85/image.png" alt="">
<img src="https://velog.velcdn.com/images/boost_dev/post/d14b911d-e806-43b2-8827-cd7e30bcda6c/image.png" alt="">
<img src="https://velog.velcdn.com/images/boost_dev/post/eb30df08-c8e1-4373-9c73-f0ec2668ef6e/image.png" alt=""><h4 id="해당-경로에-폴더만-생성되고-파일이-안들어오는-경우는">해당 경로에 폴더만 생성되고 파일이 안들어오는 경우는</h4>
remote path 경로를 var/www를 기본으로 사용하고 권한이 없어서 못만드는 경우는 putty에 직접 들어가서 <code>mkdir 폴더명</code> 실행하고 remote path를 지정해준다</li>
<li>*/var/www는 먼저 <a href="https://velog.io/@boost_dev/AWS-Lightsail%EC%97%90-fastapi-%EC%84%A4%EC%B9%98">enginx</a><blockquote>
<p>권한 오류가 나오는 경우는 <a href="https://velog.io/@boost_dev/AWS-Kubernetes-%EC%84%A4%EC%A0%95#4-permission-denied">?.4</a> 참고</p>
</blockquote>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/ea2c7cfc-6068-4c5e-a0c0-eff00b0d700e/image.png" alt=""></p>
<ul>
<li>방금 만든 SSH로 터미널 열고 cd 복사한 경로</li>
</ul>
<h3 id="371-db-연결">3.7.1 DB 연결</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/2ad16b8c-206a-4f22-bac8-ff07cb54980c/image.png" alt=""></p>
<h2 id="38-lightsail-fastapi-연동">3.8 LightSail-FastAPI 연동</h2>
<h3 id="381-pem-파일-받기">3.8.1 pem 파일 받기</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/d7768128-3734-415a-ba62-6845fa6e6846/image.PNG" alt=""></p>
<h3 id="382-ppk로-변경">3.8.2 .ppk로 변경</h3>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/1ae28761-438f-4854-8df2-6d4f9db5cf7e/image.PNG" alt=""></p>
<h3 id="383-putty-열기">3.8.3 putty 열기</h3>
<h3 id="384-명령어-입력">3.8.4 명령어 입력</h3>
<p><code>$ sudo -i</code>
<code>passwd ubuntu</code>
New password: 패스워드 입력 
Retype new password:
passwd: password updated successfully
<code>apt-get update</code>
<code>apt-get install -y --no-install-recommends tzdata g++ git curl</code>
<code>apt-get install openjdk-11-jdk</code></p>
<p><strong>자바버전 확인</strong>
<code>java -version</code>
<code>javac -version</code>
<code>vim ~/.bashrc</code>
i 입력으로 Insert</p>
<pre><code>export JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))
export PATH=$PATH:$JAVA_HOME/bin</code></pre><p>esc누르고
:wq!입력해서 나가기
<code>source ~/.bashrc</code>
<code>echo $JAVA_HOME</code>
<code>apt update</code>
<code>apt install software-properties-common</code> 
<code>add-apt-repository ppa:deadsnakes/ppa</code>
<code>cd /usr/local/bin</code>
<code>apt-get install -y python3-pip</code>
<code>ln -s /usr/bin/pip3 pip</code>
<code>pip3 install --upgrade pip</code>
<code>apt-get clean</code>
<code>apt-get install nginx</code>
<code>systemctl start nginx</code>
<code>chmod 775 /etc/nginx/sites-available</code>
<code>cd /etc/nginx/sites-available</code>
<code>vim api</code></p>
<p>i 입력</p>
<pre><code>server{
     server_name 도메인;
     location / {
          include proxy_params;
          proxy_pass http://127.0.0.1:8000;
     }
}</code></pre><p>esc 
:wq! 
enter</p>
<p><code>ln -s /etc/nginx/sites-available/api /etc/nginx/sites-enabled/</code></p>
<ul>
<li><strong>실행은 여기부터</strong></li>
</ul>
<p><code>nginx -t</code>
<code>systemctl restart nginx</code>
실행할 경로로 이동 
<code>cd /var/www/</code></p>
<blockquote>
<p>경로를 모르겠으면 <code>find / -name 폴더명 -type d</code></p>
</blockquote>
<p><code>git clone https://github.com/gangsanlee2/hello_amazon.git</code>
<code>cd hello_amazon</code>
<code>pip install fastapi uvicorn</code>
<code>nohup python3 main.py &amp; python3 -m uvicorn main:app --reload</code></p>
<p>안되면<code>nohup python3 main.py python3 -m uvicorn main:app --reload</code></p>
<ul>
<li>&amp;은 백그라운드에서 돌리라는 뜻
// type domain.com on browser</li>
</ul>
<hr>
<h1 id="3">3.?</h1>
<h3 id="31-permission-denied-while-trying-to-connect-to-the-docker-daemon-socket-at-unix">3.?.1 permission denied while trying to connect to the Docker daemon socket at unix:</h3>
<blockquote>
<p>원인</p>
</blockquote>
<ul>
<li>도커 권한오류 </li>
</ul>
<blockquote>
<p>해결
<code>sudo chmod 666 /var/run/docker.sock</code>입력</p>
</blockquote>
<h3 id="32--pymysqlerroperationalerror-2003-cant-connect-to-mysql-server-on-ip번호-timed-out">3.?.2  pymysql.err.OperationalError: (2003, &quot;Can&#39;t connect to MySQL server on &#39;IP번호&#39; (timed out)&quot;)</h3>
<blockquote>
<p>원인</p>
</blockquote>
<ul>
<li>주소 및 포트 오류(오타)</li>
<li>방화벽 (서버 , 클라이언트)</li>
<li>공유기 사용중이라면 포트포워딩 유무</li>
<li>sshd 미설치 </li>
</ul>
<blockquote>
<p>해결</p>
</blockquote>
<ul>
<li>lightsail - Networking에서 확인하고 포트번호 등록
<img src="https://velog.velcdn.com/images/boost_dev/post/0a4b5b0e-ee48-4d25-be05-ea9ec78e3dd1/image.PNG" alt=""></li>
<li>아래에 있는 포트들이 다 있는지 확인
<img src="https://velog.velcdn.com/images/boost_dev/post/af158091-9c98-4221-a1ae-7c346c24299d/image.png" alt=""></li>
</ul>
<h3 id="33-socket-time-out">3.?.3 socket time out</h3>
<blockquote>
<p>?.2와 같은 방식으로 해결했다</p>
</blockquote>
<h3 id="34-permission-denied">3.?.4 Permission denied</h3>
<blockquote>
<p>해결
해당 경로로 이동해서 
<code>sudo chmod 777 .</code>
<code>sudo chmod 777 해당 경로</code></p>
</blockquote>
<h3 id="35-putty에서-import관련-에러">3.?.5 putty에서 import관련 에러</h3>
<blockquote>
<p>docker compose up은 했나 생각해보자</p>
</blockquote>
<ul>
<li>requirements.txt가 있는 디렉토리에서 백날 pip 해봐야 aws에는 안올라가지
먼저 docker-compose파일이 있는 경로에서 compose up 실행하고 그 다음에 다시 <code>python3 -m uvicorn main:app --reload</code></li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[git) README.MD로 프로필 꾸미기]]></title>
            <link>https://velog.io/@boost_dev/git-README.MD%EB%A1%9C-%ED%94%84%EB%A1%9C%ED%95%84-%EA%BE%B8%EB%AF%B8%EA%B8%B0</link>
            <guid>https://velog.io/@boost_dev/git-README.MD%EB%A1%9C-%ED%94%84%EB%A1%9C%ED%95%84-%EA%BE%B8%EB%AF%B8%EA%B8%B0</guid>
            <pubDate>Tue, 21 Mar 2023 14:06:27 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/boost_dev/post/1f9db639-e9a6-498b-84da-0eb90ba1de46/image.PNG" alt=""></p>
<hr>
<h1 id="1-프로필용-repositories-생성">1. 프로필용 Repositories 생성</h1>
<ul>
<li>깃허브 홈 화면에서 New 클릭
<img src="https://velog.velcdn.com/images/boost_dev/post/c8007d2d-c372-4667-8624-c6766e794503/image.png" alt=""></li>
<li>Repositories명을 본인 깃허브 ID와 똑같이 만들면 아래처럼 나온다
<img src="https://velog.velcdn.com/images/boost_dev/post/b9a106ff-1858-44d1-913e-c5a87aba4d8e/image.png" alt=""></li>
<li>위의 이미지처럼 나왔는지 확인하고 만들어진 README.MD를 작성하면 프로필로 적용된다.</li>
</ul>
<hr>
<h1 id="2-stack-버튼">2. Stack 버튼</h1>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/8a726ea1-57c0-4659-8ded-7d863677d26e/image.png" alt=""></p>
<pre><code>&lt;a href=&quot;이동할 링크&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/뱃지명-배경색?style=뱃지모양&amp;logo=로고&amp;logoColor=로고색상&quot;/&gt;&lt;/a&gt;</code></pre><h2 id="21-입력-내용">2.1 입력 내용</h2>
<ul>
<li>뱃지명 : 텍스트로 들어갈 자리</li>
<li>배경색 / 로고색상 : Hexcode입력시에는 앞의 #을 제거</li>
<li>로고 : <a href="https://simpleicons.org/">Simple Icons</a>를 참고해서 가져오면 된다</li>
</ul>
<h2 id="22-뱃지-모양">2.2 뱃지 모양</h2>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/7de4f0dc-5904-493a-a79f-dedc899d9b73/image.png" alt=""></p>
<pre><code>&lt;a href=&quot;이동할 링크&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/Sample-EA4335?style=plastic&amp;logo=Git&amp;logoColor=white&quot;/&gt;&lt;/a&gt;
&lt;a href=&quot;이동할 링크&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/Sample-EA4335?style=plat&amp;logo=Git&amp;logoColor=white&quot;/&gt;&lt;/a&gt;
&lt;a href=&quot;이동할 링크&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/Sample-EA4335?style=plat-square&amp;logo=Git&amp;logoColor=white&quot;/&gt;&lt;/a&gt;
&lt;a href=&quot;이동할 링크&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/Sample-EA4335?style=for-the-badge&amp;logo=Git&amp;logoColor=white&quot;/&gt;&lt;/a&gt;
&lt;a href=&quot;이동할 링크&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/Sample-EA4335?style=social&amp;logo=Git&amp;logoColor=black&quot;/&gt;&lt;/a&gt;</code></pre><hr>
<h1 id="3-stat-표시">3. stat 표시</h1>
<p><img src="https://velog.velcdn.com/images/boost_dev/post/9b6b2942-b5f3-415a-a797-e58869ed5420/image.png" alt="">
아래 코드를 추가</p>
<pre><code>![Anurag&#39;s GitHub stats](https://github-readme-stats.vercel.app/api?username=깃허브아이디&amp;show_icons=true&amp;theme=테마)</code></pre><p>아이콘과 테마는 <a href="https://github.com/anuraghazra/github-readme-stats/blob/master/themes/README.md">여기</a>를 참고</p>
<hr>
<h1 id="4-header">4. header</h1>
<blockquote>
<p>kyechan99의 <a href="https://github.com/kyechan99/capsule-render#reversal">capsule-render</a>를 사용했다.</p>
</blockquote>
<p>방법은 간단하게 양식에 따라 아래 코드를 한 줄 추가해주면 된다.</p>
<ul>
<li>MarkDown :
<code>![header](https://capsule-render.vercel.app/api?type=wave&amp;color=auto&amp;height=300&amp;section=header&amp;text=capsule%20render&amp;fontSize=90)</code></li>
<li>HTML :
<code>&lt;img src=&quot;https://capsule-render.vercel.app/api?type=wave&amp;color=auto&amp;height=300&amp;section=header&amp;text=capsule%20render&amp;fontSize=90&quot; /&gt;</code></li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[next.js]]></title>
            <link>https://velog.io/@boost_dev/next.js</link>
            <guid>https://velog.io/@boost_dev/next.js</guid>
            <pubDate>Tue, 21 Mar 2023 11:28:28 GMT</pubDate>
            <description><![CDATA[<h1 id="saga서사구조-화면쪽에서의-인공지능">saga(서사구조, 화면쪽에서의 인공지능)</h1>
<ul>
<li>말로 전한다 → 말하기 패턴  </li>
<li>대기 상태에 있다가 인식해야 될 입력이 발생하면 작동(인식되지 않는건 노이즈)<blockquote>
<p>MSA끼리 이벤트(상태의 변화)를 주고받으며 작업이 이뤄지는 패턴</p>
</blockquote>
</li>
</ul>
<blockquote>
<p>트랜잭션의 관리주체가 DBMS(SQL)이 아닌 Application(fastapi)에 있다</p>
</blockquote>
<ul>
<li>패키징된 입력을 보내서 자료를 찾는 기존의 개념을 지나서 해당 기억을 가지고 입력되는대로 인식한다고 생각하면 좋다</li>
<li>트랜젝션 문제 때문에 어렵다<ul>
<li>트랜젝션 : DB의 상태를 변화시키기 위한 단위</li>
</ul>
</li>
<li>partition / slice<ul>
<li>전체와 부분이 다르지 않으면 슬라이스 </li>
</ul>
</li>
</ul>
<table>
<thead>
<tr>
<th>리듀서</th>
<th>매핑</th>
<th>필터</th>
</tr>
</thead>
<tbody><tr>
<td>축소</td>
<td>전체</td>
<td>하나만</td>
</tr>
</tbody></table>
<pre><code>해당코드는 user에서 reducers에 입력된 상태가 생기면 

const userSlice = createSlice({
    name: &#39;userSlice&#39;,
    initialState,
    reducers: {
        joinRequest(state:IUserState, _payload){    #const joinRequest=()=&gt;{}를 생략한 형태
            state.status = &#39;loading&#39;
        },
        joinSuccess(state:IUserState, {payload}){
            state.status = &#39;idle&#39;
            state.data = [...state.data, payload] # 오버로딩 상태로 &#39;...state.data&#39;가 없으면 이전 데이터를 날리게 된다
        }
    }
})

///Modules/users/userAPI.ts에 Modules/users/index.ts의 인터페이스 타입을 추가</code></pre><ul>
<li>_payload와 payload의 차이는 _가 있으면 보안이 필요하고 이미 아는 정보 _가 없는건 보안이 필요없는 요소에 사용하고 오픈된 상태에서 사용해야 한다 
<a href="https://azderica.github.io/01-architecture-msa/">참고</a></li>
</ul>
<h2 id="db-관련">db 관련</h2>
<p>컴포넌트에서 아래 같은 코드가 없으면 해당 스키마는 db에 적용되지 않는다</p>
<pre><code>{...register(&quot;스키마&quot;, { 
  required: true, 
  maxLength: {
      value: 20,
      message: &quot;20자 이하로 입력해주세요&quot;
  }</code></pre><h2 id="create-next-js-typescript-app">create next js typescript app</h2>
<p><a href="https://nextjs.org/docs/api-reference/create-next-app">https://nextjs.org/docs/api-reference/create-next-app</a></p>
<ul>
<li>터미널에서 npx create-next-app@latest 입력으로 앱 생성</li>
<li>vs코드로 실행 후 터미널에서 아래 명령어 순서대로 입력<ul>
<li>npm install --location=global</li>
<li>npm install yarn</li>
<li>yarn add next</li>
</ul>
</li>
</ul>
<hr>
<h1 id="error">Error</h1>
<h3 id="the-default-export-is-not-a-react-component-in-page">The default export is not a React Component in page:&quot;/&#39;</h3>
<blockquote>
<p>원인</p>
<blockquote>
<p>export default가 없을 때 발생하는 에러</p>
</blockquote>
</blockquote>
<h3 id="4-wrappedapp-created-new-store-with-withreduxmyapp--initialstate-undefined-initialstatefromgsporgssr-undefined-">4. WrappedApp created new store with withRedux(MyApp) { initialState: undefined, initialStateFromGSPorGSSR: undefined }</h3>
<blockquote>
<p>상황</p>
<blockquote>
<p>링크 설정하면 서 경로를 잘못 쓴 경우 발생</p>
</blockquote>
</blockquote>
<h3 id="yarn-build나-yarn-dev-실행시-실행이-안되는-경우">yarn build나 yarn dev 실행시 실행이 안되는 경우</h3>
<blockquote>
<p>원인</p>
<blockquote>
<p>github로 협업시 next가 없을 때 발생하는 문제</p>
</blockquote>
<p>해결</p>
<blockquote>
<p>npm install node나 yarn add node를 실행</p>
</blockquote>
</blockquote>
<h3 id="307">307</h3>
<p>next에서 module의 인터페이스 타입과 fastapi의 schema는 같은 이름으로 통일하는게 편하다</p>
<h3 id="the-above-error-occurred-in-task-watchjoinbrcreated-by-rootsagabrtasks-cancelled-due-to-errorbrrootsaga">The above error occurred in task watchJoin<br>created by rootSaga<br>Tasks cancelled due to error:<br>rootSaga</h3>
<blockquote>
<p>해결</p>
<blockquote>
<p>saga에 있던<br>     alert(&quot; %%% export function* watchJoin()&quot;)<br>를 지웠다</p>
</blockquote>
</blockquote>
<h3 id="cannot-access-before-initialization">Cannot access before initialization</h3>
<blockquote>
<p>원인</p>
<blockquote>
<ol>
<li>export default를 object보다 위에 둔 경우</li>
</ol>
</blockquote>
</blockquote>
<h3 id="sqlalchemyexcintegrityerror-1062">sqlalchemy.exc.IntegrityError: (1062,</h3>
<blockquote>
<p>원인 </p>
<blockquote>
<p>DB에서 primary_key가 중복된 경우 발생하는 에러</p>
</blockquote>
</blockquote>
<h3 id="error-text-content-does-not-match-server-rendered-html">Error: Text content does not match server-rendered HTML</h3>
<blockquote>
<p>원인</p>
<blockquote>
<p>렌더링된 HTML(SSR, ISR 등)이 업데이트되지만 페이지 로드 후 React 코드는 업데이트되지 않아서 발생</p>
</blockquote>
<p>해결</p>
<blockquote>
</blockquote>
</blockquote>
<h3 id="error-hydration-failed-because-the-initial-ui-does-not-match-what-was-rendered-on-the-server">Error: Hydration failed because the initial UI does not match what was rendered on the server.</h3>
<h3 id="error-there-was-an-error-while-hydrating-because-the-error-happened-outside-of-a-suspense-boundary-the-entire-root-will-switch-to-client-rendering">Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.</h3>
<hr>
<h1 id="etc">etc</h1>
<ul>
<li>modules는 테이블명과 일치시켜야 한다</li>
</ul>
<p>###</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[hive]]></title>
            <link>https://velog.io/@boost_dev/hive</link>
            <guid>https://velog.io/@boost_dev/hive</guid>
            <pubDate>Tue, 21 Mar 2023 11:27:04 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>스트림을 사용하니까 기존의 RDBMS방식을 버려야 한다</p>
</blockquote>
<hr>
<h1 id="0-hive">0. hive?</h1>
<blockquote>
<p>No(=Not Only)sql database이면서 In Memory database</p>
<ul>
<li>JSON을 받아와서 시리얼라이즈 후 DB에 저장되는 이전의 과정을 생략하고 바로 JSON으로 저장하는 방식</li>
</ul>
</blockquote>
<ul>
<li>현재 flutter에서의 DB는 hive가 가장 선호된다</li>
<li>장점<ul>
<li>중간 과정이 없어서 빠르다 </li>
<li>Key &amp; Value 구조이기 때문에 인덱스가 없다</li>
<li>Lambda처럼 사용한만큼 사용료를 지불해서 비용절감 가능<ul>
<li>RDS에 저장하지 않아서 좋다</li>
</ul>
</li>
</ul>
</li>
<li>단점<ul>
<li>JOIN 불가<blockquote>
<p><strong>term</strong></p>
<ul>
<li>annotation == Python의 decorator</li>
<li>Adapter == crossEntity</li>
</ul>
</blockquote>
</li>
</ul>
</li>
</ul>
<hr>
<h1 id="1-a-href-httpspubdevpackageshive설치a">1. <a href ="https://pub.dev/packages/hive">설치</a></h1>
<ul>
<li>package → pubspec.yaml 에 추가</li>
</ul>
<pre><code>    dependencies:
      hive: ^[version]
      hive_flutter: ^[version]

    dev_dependencies:
      hive_generator: ^[version]
      build_runner: ^[version]</code></pre><hr>
<h1 id="2-데이터-저장">2. 데이터 저장</h1>
<h2 id="21-initalize">2.1 Initalize</h2>
<blockquote>
<p>hive를 사용하기 위해서는 main.dart에서 초기화가 필수</p>
</blockquote>
<pre><code>    import &#39;package:hive_flutter/hive_flutter.dart&#39;;
&gt;
    await Hive.initFlutter();
    ※ 모바일 앱이 아닌 경우 Hive.initFlutter() 가 아닌 Hive.init()를 사용</code></pre><h2 id="22-openbox">2.2 OpenBox</h2>
<blockquote>
<p>hive에서 박스란 NoSQL의 Collection</p>
</blockquote>
<blockquote>
<p><strong>공식 홈페이지에서의 정의</strong> <br/>
What are boxes? <br/>
All data stored in Hive is organized in boxes. A box can be compared to a table in SQL, but it does not have a structure and can contain anything.</p>
<ul>
<li>box는 table과 비슷하지만 구조화되지 않았다는 차이가 있다 <ul>
<li>결론 : box는 데이터를 저장하는 공간이다</li>
</ul>
</li>
</ul>
</blockquote>
<h3 id="221-box-생성">2.2.1 box 생성</h3>
<pre><code>    await Hive.openBox(&#39;myBox&#39;);
    final box = Hive.box(&#39;myBox&#39;);</code></pre><ul>
<li>OpenBox 이후에는 box 메소드를 통해 해당 박스를 가져올 수 있다</li>
</ul>
<hr>
<h1 id="3-사용법">3. 사용법</h1>
<blockquote>
<p>읽기 : <code>get</code> 
쓰기 : <code>put</code></p>
</blockquote>
<h2 id="31-read">3.1 read</h2>
<pre><code>    final box = Hive.box(&#39;myBox&#39;);

    String name = box.get(&#39;name&#39;);

    DateTime birthday = box.get(&#39;birthday&#39;);</code></pre><ul>
<li>존재하지 않는 데이터를 읽을 때는 null을 리턴하지만 defaultValue를 사용해서 default값을 정할 수 있다.<pre><code>  double height = box.get(&#39;randomKey&#39;, defaultValue: 17.5);</code></pre></li>
</ul>
<h2 id="32-write">3.2 write</h2>
<pre><code>    final box = Hive.box(&#39;myBox&#39;);

    box.put(&#39;name&#39;, &#39;Paul&#39;);

    box.put(&#39;friends&#39;, [&#39;A&#39;, &#39;B&#39;, &#39;C&#39;]);

    box.put(123, &#39;test&#39;);

    box.putAll({&#39;key1&#39;: &#39;value1&#39;, 42: &#39;life&#39;});</code></pre><hr>
<h1 id="4-custom-object-작성">4. Custom Object 작성</h1>
<blockquote>
<p>기본적으로 List, Map, DateTime, Unit8List 등 주요 타입들을 지원하지만 그 외에 새로운 타입을 원하면 TypeAdapter 를 작성하여 Hive에 등록 해주어야 한다.</p>
</blockquote>
<pre><code>    import &#39;package:hive/hive.dart&#39;;

    part &#39;person.g.dart&#39;;

    @HiveType(typeId: 0)
    class Person {
      @HiveField(0)
      String name;

      @HiveField(1)
      int age;

      @HiveField(2)
      List&lt;String&gt; friends;

      Person(this.name, this.age, this.friends);
    }</code></pre><ul>
<li>TypeAdapter 를 만들기 위한 class 를 제작 후, 클래스 위에 @HiveType(typeId:) 를 추가<ul>
<li>TypeField는 0 ~ 233</li>
</ul>
</li>
<li>클래스 안의 항목들은 @HiveField([index], [default value]) 를 추가<blockquote>
<p>Default value 기능은 hive: 2.0.4 , hive_generator: 1.1.0 이후 버전부터 사용 가능</p>
</blockquote>
</li>
<li>part 경로 추가 이후 터미널에서 아래 build_runner 를 실행<pre><code>  flutter packages pub run build_runner build</code></pre></li>
<li>.g.dart 라는 새로운 TypeAdapter 생성을 확인</li>
<li>생성된 TypeAdapter 를 적용하기 위해서는 registerAdapter 를 이용</li>
</ul>
<pre><code>    Hive.registerAdapter(personAdapter());
    final person = await Hive.openBox&lt;Person&gt;(&#39;person&#39;);

    person.put(&#39;david&#39;, Person(&#39;david&#39;, 20, [&#39;Tom&#39;, &#39;Ben&#39;]);
    person.put(&#39;sandy&#39;, Person(&#39;sandy&#39;, 30, [&#39;david&#39;, &#39;suzan&#39;, &#39;eric&#39;]);</code></pre><hr>
<h1 id="5-hive-데이터-타입">5. hive 데이터 타입</h1>
<h2 id="51-primitive-data-types">5.1 Primitive data types</h2>
<h3 id="511-numeric-data-type">5.1.1 Numeric data type</h3>
<ul>
<li>tinyint : 1-byte</li>
<li>smallint : 2-byte</li>
<li>int : 4-byte</li>
<li>bigint : 8-byte</li>
<li>float : 4-byte single-precision</li>
<li>double : 8-byte double-precision</li>
<li>decimal : 17-byte (38 digits)</li>
</ul>
<pre><code>       create table test (id bigint, price decimal(10,2));</code></pre><h3 id="512-string-data-type">5.1.2 String data type</h3>
<ul>
<li>string    :  sequence of characters</li>
<li>varchar  :  variable-length character (1 ~ 65355 )</li>
<li>char      : fixed-length character ( 1 ~ 255 )</li>
</ul>
<h3 id="513-datetime-data-type">5.1.3 Date/Time data type</h3>
<ul>
<li><p>Timestamp : YYYY-MM-DD HH:MM:SS.fffffffff</p>
</li>
<li><p>Date : YYYY-MM-DD(Date 타입은 Date, Timestamp, String으로 변환가능)</p>
<pre><code>     create table test (id int, created_dt date, update_dt timestamp);</code></pre></li>
</ul>
<h3 id="514-etc">5.1.4 etc</h3>
<ul>
<li>Boolean : true or false</li>
<li>Binary : sequence of bytes </li>
</ul>
<h2 id="52-complex-data-tyeps">5.2 Complex data tyeps</h2>
<h3 id="521-struct--similar-c-struct-type">5.2.1 Struct : similar c struct type.</h3>
<pre><code>       struct &lt; column : data_type(primitive type), .........&gt;

       struct_coumn.column로 참조        

       create table test1 (id int, name struct&lt;first:string, last:string&gt;);</code></pre><h3 id="522-map--key-value-pairs">5.2.2 Map : key-value pairs</h3>
<p><code>map_column-&gt;name</code></p>
<h3 id="523-array--ordered-sequece-of-similar-elements-like-string">5.2.3 Array : ordered sequece of similar elements like string..</h3>
<p><code>t[ &#39;a&#39;, &#39;b&#39;,&#39;c&#39; ], t[0], t[1]</code></p>
<h3 id="524-uniontype--store-different-data-types">5.2.4 UNIONTYPE : store different data types.</h3>
<pre><code class="language-uniontype&lt;data">


       create table test2(c1 uniontype&lt;int, double, array&lt;string&gt;, struct&lt;age:int, country:string&gt;&gt;);</code></pre>
<h2 id="53-operators">5.3 operators</h2>
<h3 id="531-비교-연산">5.3.1 비교 연산</h3>
<ul>
<li>A = B</li>
<li>A != B</li>
<li>A &lt;=&gt; B : A and B NUL 이면 true</li>
<li>A &lt;&gt; B</li>
<li>A&lt; B : A or B가 NULL이면 NULL, A &lt; B이면 TRUE</li>
<li>A &lt;= B </li>
<li>A &gt;= B</li>
<li>A between B and C : A or B or C 가 NULL 이면 NULL</li>
<li>A not between B and C</li>
<li>A is null </li>
<li>A is not null</li>
<li>A like B : A or B가 NULL이면 NULL</li>
<li>A not like B </li>
<li>A RLIKE B  : A or B가 NULL이면 NULL, A의 substring이 B에 매칭되면 true</li>
<li>A regexp B </li>
</ul>
<h3 id="532-산술-연산">5.3.2 산술 연산</h3>
<ul>
<li><p>A or B중 NULL이면 결과 NULL이고 모든 연산의 결과는 숫자</p>
</li>
<li><p>A + B , A-B, A*B...</p>
</li>
</ul>
<p><code>hive&gt; select 10+10 from test;</code></p>
<h3 id="533-논리-연산">5.3.3 논리 연산</h3>
<ul>
<li><p>연산자는 AND, OR이며 모든 결과는 TRUE, FALSE이며, operand중 NULL이면 결과도 NULL</p>
<pre><code>    A and B, A &amp;&amp; B, A OR B, ! A , A IN (v1, v2..), A in (subquery)

    A not in (v1, v2..), A not in (subquery), exists (subquery), 

    not exists (subquery)</code></pre></li>
</ul>
<h3 id="534-복잡-연산">5.3.4 복잡 연산</h3>
<pre><code>      A[i], B[key], C.a

      select A[2] from test;

      select C.a from test;</code></pre><hr>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://letyarch.blogspot.com/2021/02/hive.html">hive 시작하기</a></li>
<li><a href="https://velog.io/@udong85/Flutter-Hive-%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%80%EC%9E%A5">[Flutter] Hive 를 이용한 데이터 저장</a></li>
<li><a href="https://semode.tistory.com/27">Hive Data Types</a></li>
<li><a href="https://dalgoodori.tistory.com/m/22">date</a></li>
<li><a href="https://velog.io/@adbr/flutter-TextFormField-onSaved-%ED%98%B8%EC%B6%9C%EC%8B%9C%EC%A0%90">textFormField onSaved 호출</a></li>
<li><a href="https://sgpassion.tistory.com/548">Play store 개발자 계정 만들기</a></li>
<li><a href="https://www.kindacode.com/article/flutter-hive-database/">CRUD</a></li>
<li></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[python) 위치 인수와 리스트 언패킹]]></title>
            <link>https://velog.io/@boost_dev/python-%EC%9C%84%EC%B9%98-%EC%9D%B8%EC%88%98%EC%99%80-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EC%96%B8%ED%8C%A8%ED%82%B9</link>
            <guid>https://velog.io/@boost_dev/python-%EC%9C%84%EC%B9%98-%EC%9D%B8%EC%88%98%EC%99%80-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EC%96%B8%ED%8C%A8%ED%82%B9</guid>
            <pubDate>Tue, 21 Mar 2023 11:02:12 GMT</pubDate>
            <description><![CDATA[<h1 id="1-위치-인수">1. 위치 인수</h1>
<p>함수에 인수(매개변수)를 순서대로 넣는 방식. 즉, 인수의 위치가 정해져 있다</p>
<h2 id="11-위치인수를-사용하는-함수">1.1 위치인수를 사용하는 함수</h2>
<pre><code>def 함수명(인수1, 인수2, 인수3)
    print(인수1)
    print(인수2)
    print(인수3)</code></pre><p>위의 코드가 있을 때 <code>함수명(10, 20, 30)</code>을 실행하면 아래처럼 한 줄씩 출력된다</p>
<pre><code>10
20
30</code></pre><hr>
<h1 id="2-언패킹">2. 언패킹</h1>
<p>인수를 순서대로 넣을 때는 리스트나 튜플을 사용할 수 있다.</p>
<h2 id="21-언패킹-방법">2.1 언패킹 방법</h2>
<ul>
<li><code>함수(*리스트)</code></li>
<li><code>함수(*튜플)</code></li>
</ul>
<pre><code>a = [인수1, 인수2, 인수3]
함수명(*a)

# 출력 결과
인수1
인수2
인수3</code></pre><ul>
<li>고정인수와 가변인수를 함께 쓰고 싶을 때는 
<code>def 함수(고정 인수, *가변 인수):</code></li>
</ul>
<h2 id="22-어디에-쓸-수-있나">2.2 어디에 쓸 수 있나?</h2>
<ul>
<li>매개변수는 보통 arguments의 약자인 args를 사용하며 튜플 구조라 반복이 가능하다</li>
<li>가변인수 활용시에 사용하면 좋다<blockquote>
<p>가변인수?
: 인수의 개수가 정해지지 않은 인수</p>
</blockquote>
</li>
</ul>
<hr>
<p>참고</p>
<ul>
<li><a href="https://dojang.io/course/view.php?id=7">위키독스, 파이썬 코딩도장</a></li>
</ul>
]]></description>
        </item>
    </channel>
</rss>