<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dev_junseok_22.log</title>
        <link>https://velog.io/</link>
        <description>Developer &amp; Publisher 심준석 입니다.</description>
        <lastBuildDate>Mon, 20 Jan 2025 14:41:10 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dev_junseok_22.log</title>
            <url>https://velog.velcdn.com/images/dev_junseok_22/profile/93be7a00-611d-47cb-bc98-d099caba845d/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dev_junseok_22.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dev_junseok_22" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[FreeRTOS] Posix_gcc Demo 구조 분석]]></title>
            <link>https://velog.io/@dev_junseok_22/FreeRTOS-Posixgcc-Demo-%EA%B5%AC%EC%A1%B0-%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@dev_junseok_22/FreeRTOS-Posixgcc-Demo-%EA%B5%AC%EC%A1%B0-%EB%B6%84%EC%84%9D</guid>
            <pubDate>Mon, 20 Jan 2025 14:41:10 GMT</pubDate>
            <description><![CDATA[<h1 id="1-freertos-coding-rule">1. FreeRTOS Coding Rule</h1>
<p>코드 구조 분석을 진행하기 앞서, FreeRTOS에는 중요한 코딩룰을 먼저 알아보자.</p>
<h2 id="1-1-변수명">1-1. 변수명</h2>
<p>FreeRTOS에서는 변수명 결정시, 타입에 따라 아래와 같은 접두어를 사용한다</p>
<ul>
<li><code>c</code> : <code>char</code> Type</li>
<li><code>s</code> : <code>int16_t</code> Type (<code>short</code>)</li>
<li><code>i</code> : <code>int32_t</code> Type (<code>int</code>)</li>
<li><code>x</code> : <code>BaseType_t</code> Type, 주로 구조체나 인스턴스 핸들 등 일반적인 타입을 제외하면 대부분 <code>x</code></li>
<li><code>u</code> : <code>unsigned</code></li>
<li><code>p</code> : <code>pointer</code></li>
</ul>
<h2 id="1-2-함수명">1-2. 함수명</h2>
<ul>
<li><code>v</code> : <code>void</code>, 반환값이 없는 함수를 의미</li>
<li><code>x</code> : 변수명의 접두어 <code>x</code>와 같은 의미, <code>BaseType_t</code>를 반환하는 함수</li>
<li><code>pv</code> : <code>void*</code> 타입을 반환하는 함수</li>
<li><code>prv</code> : <code>private</code> 함수, 대표적으로 아무런 task도 실행되지 않을 때 실행되는 idle task가 호출하는 callback 함수인 hook가 이 접두어를 사용하고 있음</li>
</ul>
<h1 id="2-handle">2. Handle</h1>
<p>내부적으로 생성된 객체를 관리하고 식별하기 위한 포인터 타입, 보통 Task, Queue, Semaphore, Timer 등과 같은 RTOS 객체를 가리키는 <code>참조(포인터)</code> 형태의 자료 구조.</p>
<ul>
<li><code>TaskHandle_t</code> : Task를 식별하는 데 사용되는 핸들</li>
<li><code>QueueHandle_t</code> : Queue를 식별하는 데 사용되는 핸들</li>
<li><code>SemaphoreHandle_t</code> : Semaphore(Mutex 등)를 식별하는 데 사용되는 핸들</li>
<li><code>TimerHandle_t</code> : 소프트웨어 타이머를 식별하는 데 사용되는 핸들</li>
</ul>
<h1 id="3-task-api">3. Task API</h1>
<p>FreeRTOS에서 제공해주는 Task 관련 API가 있다. 대표적으로 몇가지 알아보자면,</p>
<h2 id="3-1-xtaskcreate">3-1. xTaskCreate()</h2>
<pre><code class="language-c">BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
                        const char * const pcName,
                        unsigned short usStackDepth,
                        void *pvParameters,
                        UBaseType_t uxPriority,
                        TaskHandle_t *pxCreatedTask)</code></pre>
<ul>
<li><code>pvTaskCode</code> : task의 기능이 선언된 함수의 함수 포인터</li>
<li><code>pcName</code> : 디버깅 용도로 사용하는 문자열이며, task의 이름을 말함</li>
<li><code>usStackDepth</code> : task마다 할당되는 stack 메모리를 말하며, 단위는 <code>WORD</code>. 일반적으로 사용하는 ARM Cortex-M 보드에서는 <code>1WORD == 4Byte</code></li>
<li><code>pvParameters</code> : task 함수로 전달할 매개변수, 없으면 <code>NULL</code> 사용. 전달할 매개변수를 <code>(void*)</code> 타입으로 캐스팅한 뒤 여기다가 넣어주면 task 함수에서 사용할 수 있음</li>
<li><code>uxPriority</code> : </li>
<li><code>*pxCreatedTask</code> : task를 제어하기 위한 <code>TaskHandle_t</code> 타입 핸들, Task의 우선순위를 바꾸거나, task를 멈추거나 등 task에 대한 설정은 모두 이 핸들을 통해 이루어짐.</li>
</ul>
<p>(예시)</p>
<pre><code class="language-c">xTaskCreate( prvQueueReceiveTask,                // Task 함수, 실제 실행될 로직
             &quot;RX&quot;,                                // 디버깅 시 확인할 수 있는 이름, 커널 내부에서 직접 쓰이지는 않음
             configMINIMAL_STACK_SIZE,            // 스택 크기
             NULL,                                // 입력 파라미터, Task가 필요로 한다면 다른 파라미터를 넣어 사용할 수 있음
             mainQUEUE_RECEIVE_TASK_PROIRITY,    // 우선순위, 어느 Task를 더 자주/빨리 실행할지 결정, 우선순위가 높을수록 CPU 점유 기회가 많아짐
             NULL );                            // Null을 넘기면 Task 핸들을 별도로 받지 않음, Task 핸들이 필요하면 변수로 받아서 vTaskDelete()등에 활용 가능</code></pre>
<h2 id="3-2-vtaskdelay">3-2. vTaskDelay()</h2>
<pre><code class="language-c">void vTaskDelay( TickType_t xTicksToDelay );</code></pre>
<ul>
<li>Task의 상태를 <code>Running</code>에서 <code>Blocked(waiting)</code>으로 변경하는 함수</li>
<li>설정된 시간 <code>xTicksToDelay</code> 동안 Task는 <code>blocked</code> Task가 되며, 다음 우선순위를 가진 task가 실행됨</li>
<li>확정성 및 이식성 좋은 코드를 만들기 위해서 <code>tick</code> 단위 시간을 사용하는 것 보다 <code>pdMS_TO_TICKS()</code> 매크로를 사용해서 ms 단위를 <code>tick</code>으로 변환해서 사용하는 것이 좋음</li>
</ul>
<h2 id="3-3-vtaskdelayuntil">3-3. vTaskDelayUntil()</h2>
<ul>
<li><code>vTaskDelay()</code>와 똑같은 기능을 수행하지만, <code>vTaskDelay()</code>는 호출 시점부터 지정된 시간만큼 blocked 되는 반면,</li>
<li><code>vTaskDelayUntile()</code>은 호출 시점과 관계없이 목표 절대 시간 주기에 맞춰 blocked 됨</li>
</ul>
<h2 id="3-4-vtasksuspend-vtaskresume">3-4. vTaskSuspend(), vTaskResume()</h2>
<ul>
<li>Delay 함수들은 Task를 일정 시간동안 blocked state로 만들지만, suspend 함수는 task를 기약없이 blocked state로 만듬 (resume 함수를 사용할 때 까지)</li>
<li>Task의 우선순위를 바꿀때, Task를 일단 blocked state로 만들기 위해 사용하며, 인자값으로 task의 handle이 들어감</li>
</ul>
<h2 id="3-5-vtaskpriorityset">3-5. vTaskPrioritySet()</h2>
<ul>
<li>Task의 우선순위를 바꿀때 사용하는 API</li>
</ul>
<h1 id="4-posix_gcc-demo--main_blinky">4. Posix_gcc Demo : main_blinky()</h1>
<p>드디어 main_blinky()를 분석해보자.</p>
<p>main_blinky()는 Posix_gcc Demo의 main() 함수에 정의된 <code>mainSELECTTED_APPLICATION = 0</code> 일 때, 호출되는 함수이다.</p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/c3a9a3d7-beff-47a1-bff2-07719e7153e9/image.png" alt=""></p>
<h2 id="4-1-signal-처리-함수-호출">4-1. Signal 처리 함수 호출</h2>
<p>main 함수가 호출됨가 동시에, 먼저 시그널 처리 함수를 등록한다.</p>
<pre><code class="language-c">signal( SIGINT, handle_sight);</code></pre>
<ul>
<li><p>signal 함수는 시그널 처리 함수를 등록하는 함수로, <code>SIGINT</code>는 일반적으로 프로그램 실행 중, 키보드로부터 인터럽트가 들어왔을 때 발생하는 시그널</p>
</li>
<li><p>프로그램 실행 중, 사용자가 Ctrl+C를 눌러 종료 신호를 보냈을 때 바로 종료되는 대신 <code>handlo_sight</code> 함수에서 필요한 동작(자원 정리 및 로그 출력 등)을 수행하도록 설정해주는 코드</p>
</li>
</ul>
<h2 id="4-2-mutex뮤텍스-생성">4-2. Mutex(뮤텍스) 생성</h2>
<p>그 다음으로는 콘솔(또는 표준 입출력) 사용을 위한 뮤텍스(Mutex)를 생성하는 함수 <code>console_init()</code>을 호출한다.</p>
<pre><code class="language-c">console_init();</code></pre>
<p>해당 함수에서는 FreeRTOS에서 제공하는 <code>xSemaphtoreCreateMutex()</code> 또는 <code>xSemaphtoreCreateMutexStatic()</code> 함수를 사용해, 멀티태스킹 환경에서 여러 Task가 동시에 콘솔 입출력을 수행할 때 충돌이 일어나지 않도록 보호한다.</p>
<pre><code class="language-c">void console_init(void)
{
    #if (configSUPPORT_STATIC_ALLOCATION == 1)
    {
        xStdioMutex = xSemaphoreCreateMutexStatic( &amp;xStdioMutexBuffer );
    }
    #else
    {
        xStdioMutex = xSemaphoreCreateMutex();
    }
    #endif
}</code></pre>
<h2 id="4-3-main_blinky">4-3. main_blinky()</h2>
<p><code>mainSELECTED_APPLICATION = 0</code>이라는 가정하에, 이제 <code>main_blinky()</code> 함수로 진입한다. 해당 함수 안에는 Timer 및 Task, Scheduler 등이 선언되어, main 함수에서 실질적인 동작을 수행한다.</p>
<h3 id="4-3-1-타이머-동작-주기-설정">4-3-1. 타이머 동작 주기 설정</h3>
<p>먼저, 변수 타이머 동작 주기 변수 <code>xTimerPeriod</code>는 다음과 같이 선언된다.</p>
<pre><code class="language-c">const TickType_t xTimerPeriod = mainTIMER_SEND_FREQUENCY_MS;</code></pre>
<p>이때, <code>mainTIMER_SEND_FREQUENCY_MS</code>는</p>
<pre><code class="language-c">#define mainTIMER_SEND_FREQUENCY_MS        pdMS_TO_TICKS(2000UL)</code></pre>
<p>로 정의된다.</p>
<p>즉, 매크로 <code>pdMS_TO_TICKS</code>를 통해 1 Tick을 2000ms으로 정의하고 (<code>pdMS_TO_TICKS(2000UL)</code>),</p>
<p>타이머 동작 주기를 1 Tick, 즉 2000ms로 설정해주는 과정이다.</p>
<h3 id="4-3-2-queue-생성">4-3-2. Queue 생성</h3>
<pre><code class="language-c">xQueue = xQueueCreate( mainQUEUE_LENGTH, sizeof( uint32_t ));</code></pre>
<p><code>xQueueCreate</code> 함수로, Queue를 생성해주는 과정이다.
생성된 Queue인 <code>xQueue</code>는 길이가 <code>mainQUEUE_LENGTH</code>이고, 각 element의 크기는 32bit 정수 크기인 형태로 생성된다(<code>sizeof( uint32_t))</code>)</p>
<h3 id="4-3-3-task-생성">4-3-3. Task 생성</h3>
<p>해당 데모에서는 2개의 Task를 생성한다. Task를 생성하기 위해선, <code>xTaskCreate</code> API를 사용한다.</p>
<p>하나는 Queue로부터 데이터를 받아 메시지를 생성해주며,</p>
<pre><code class="language-c">xTaskCreate( prvQueueReceiveTask,                // Task 함수, 실제 실행될 로직
             &quot;RX&quot;,                                // 디버깅 시 확인할 수 있는 이름, 커널 내부에서 직접 쓰이지는 않음
             configMINIMAL_STACK_SIZE,            // 스택 크기
             NULL,                                // 입력 파라미터, Task가 필요로 한다면 다른 파라미터를 넣어 사용할 수 있음
             mainQUEUE_RECEIVE_TASK_PROIRITY,    // 우선순위, 어느 Task를 더 자주/빨리 실행할지 결정, 우선순위가 높을수록 CPU 점유 기회가 많아짐
             NULL );                            // Null을 넘기면 Task 핸들을 별도로 받지 않음, Task 핸들이 필요하면 변수로 받아서 vTaskDelete()등에 활용 가능</code></pre>
<p>그리고, 다른 하나는 Tick을 Count한 값을 Queue에 값을 전송하는 Task이다.</p>
<pre><code class="language-c">xTaskCreate( prvQueueSendTask,
             &quot;TX&quot;,
             configMINIMAL_STACK_SIZE,
             NULL,
             mainQUEUE_SEND_TASK_PRIORITY,
             NULL );</code></pre>
<h3 id="4-3-4-소프트웨어-타이머-생성">4-3-4. 소프트웨어 타이머 생성</h3>
<p>그리고 그 다음으로 소프트웨어 타이머를 생성해준다.</p>
<pre><code class="language-c">xTimer = xTimerCreate( &quot;Timer&quot;,                            //타이머 이름, 디버깅용
                        xTimerPeriod,                    //타이머 주기
                        pdTRUE,                            //pdTRUE면, 타이머가 만료될 떄마다 자동으로 다시 시작, pdFALSE면 한번 만료된 후 자동으로 멈춤
                        NULL,                            //타이머 ID, NULL이므로 별도로 ID를 저장하진 않음. 여러 타이머를 관리할 때 식별이 필요하다면 다른 값을 넣을 수 있음
                        prevQueueSendTimerCallback    );    //콜백 함수 : prvQueueSendTimerCallback이 타이머가 만료될 때마다 실행하는 함수를 가리킴</code></pre>
<h3 id="4-3-5-타이머-시작">4-3-5. 타이머 시작</h3>
<p>타이머가 정상적으로 생성된 경우, <code>xTimerStart()</code> 함수를 통해 타이머 시작</p>
<pre><code class="language-c">if( xTimer!=NULL )
{
    xTimerStart( xTimer, 0 );
}</code></pre>
<h3 id="4-3-6-스케줄러-시작">4-3-6. 스케줄러 시작</h3>
<pre><code class="language-c">vTaskStartScheduler();</code></pre>
<p>모든 Task와 타이머가 생성된 뒤, RTOS 스케줄러를 시작하면, 등록된 Task들이 우선순위와 준비 상태(Ready State)에 따라 CPU를 할당받아 실행된다.</p>
<p>등록된 소프트웨어 타이머도 FreeRTOS 타이머 서비스(Task)에 주기적으로 콜백이 호출되고, 스케줄러가 시작된 후에는 <code>main()</code> 함수로는 다시 돌아오지 않는 것이 일반적이다.</p>
<blockquote>
<p>*참고 : RTOS 스케줄러가 시작되면, 내부적으로 무한 반복 구조로 동작한다. 
따라서, 일반적인 C프로그램처럼 While(1) 루프를 직접쓰지 않아도, RTOS 스케줄러가 각 Task를 무기한으로 스케줄링하고 실행해준다.</p>
</blockquote>
<h1 id="5-마치며">5. 마치며</h1>
<p>가장 기초적인 예제를 통해, FreeRTOS에서 Task를 생성하는 방법, API를 사용하는 방법 등을 알아봤다. 다음 글에는 Posix_gcc에서 두번째 실행 함수 <code>main_full()</code> 함수를 분석하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[FreeRTOS] GCC Based Posix/Linux Simulator Demo Build]]></title>
            <link>https://velog.io/@dev_junseok_22/FreeRTOS-GCC-Based-PosixLinux-Simulator-Demo-Build</link>
            <guid>https://velog.io/@dev_junseok_22/FreeRTOS-GCC-Based-PosixLinux-Simulator-Demo-Build</guid>
            <pubDate>Sun, 19 Jan 2025 14:00:27 GMT</pubDate>
            <description><![CDATA[<h1 id="1-posix-port">1. Posix Port</h1>
<p>FreeRTOS 실험 및 실제 임베디드 하드웨어로 포팅하기 위한 FreeRTOS 애플리케이션을 개발할 수 있는 환경을 제공</p>
<p>하지만, Linux 포트를 사용하는 FreeRTOS 애플리케이션은 실시간 동작을 보이진 않음</p>
<h1 id="2-환경설정">2. 환경설정</h1>
<h2 id="2-1-gcc-arm-툴체인-설치">2-1. GCC ARM 툴체인 설치</h2>
<p>ARM Cortex-M 시리즈 프로세서를 대상으로 하는 바이너리를 빌드할 있는 컴파일러 설치</p>
<pre><code class="language-bash">sudo apt install gcc-arm-none-eabi gdb-multiarch</code></pre>
<h1 id="3-freertos-소스-코드">3. FreeRTOS 소스 코드</h1>
<h2 id="31-freertos-소스-코드-다운로드">3.1 FreeRTOS 소스 코드 다운로드</h2>
<p>FreeRTOS의 최신 소스 코드를 GitHub에서 클론</p>
<pre><code class="language-bash">git clone https://github.com/FreeRTOS/FreeRTOS.git</code></pre>
<p>설치가 완료되면, 해당 디렉토리로 이동</p>
<pre><code class="language-bash">cd FreeRTOS</code></pre>
<p>*그리고 Submodule을 설치해줘야한다.
(내가 못찼은건지 모르겠지만, 공식 문서에서 찾은게 아닌, Stack Overflow 열심히 해매면서 찾아냈다..)</p>
<pre><code class="language-bash">git submodule update --init --recursive</code></pre>
<h2 id="32-소스-코드-구성">3.2 소스 코드 구성</h2>
<ul>
<li><p>Linux용 RTOS Port Layer(POSIX) 위치</p>
<blockquote>
<p><a href="https://github.com/FreeRTOS/FreeRTOS-Kernel/tree/master/portable/ThirdParty/GCC/Posix">FreeRTOS/Source/portable/ThirdParty/GCC/Posix</a></p>
</blockquote>
</li>
<li><p>Demo Project (1) : 커널 전용 데모</p>
<blockquote>
<p><a href="https://github.com/FreeRTOS/FreeRTOS/tree/master/FreeRTOS/Demo/Posix_GCC">FreeRTOS/Demo/POSIX_GCC</a></p>
</blockquote>
</li>
</ul>
<ul>
<li>Demo Project (2) : 네트워킹 데모<blockquote>
<p><a href="https://github.com/FreeRTOS/FreeRTOS/tree/master/FreeRTOS-Plus/Demo/FreeRTOS_Plus_TCP_Echo_Posix">FreeRTOS-Plus/Demo/FreeRTOS_Plus_TCP_Echo_Posix</a></p>
</blockquote>
</li>
</ul>
<h2 id="3-3-posixlinux-시뮬레이터-소개">3-3. Posix/Linux 시뮬레이터 소개</h2>
<ul>
<li>해당 프로젝트는 Linux(POSIX) 포트를 사용하여 FreeRTOS 커널 기능을 보여주는 프로젝트</li>
</ul>
<p>간단한 *Blinky 스타일 데모(<code>BLINKY_DEMO</code>), 또는 보다 *포괄적인 스타일 데모(<code>FULL_DEMO</code>) 상수를 설정하여(<code>mainSELECTED_APPLICATION</code>), <code>main.c</code> 가장 상단에 정의되어 있음</p>
<p>(<code>FULL_DEMO</code>로 설정된 경우)
<img src="https://velog.velcdn.com/images/dev_junseok_22/post/79b1dd46-54fb-4bcf-bdea-e94211b139d0/image.png" alt=""></p>
<h3 id="3-3-1-bliny-demo">3-3-1. Bliny Demo</h3>
<ul>
<li><p><code>mainSELECTED_APPLICATION</code>이 <code>BLINKY_DEMO</code>로 설정되어 있을 경우,</p>
</li>
<li><p><code>main()</code> 함수는 <code>main_blinky()</code> 함수를 호출함 (from <code>main_blinky.c</code>)</p>
</li>
<li><p><code>main_blinky()</code>는 2개의 Task (software timer 및 queue로 구성된)를 포함하고 있으며,</p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/c9180b5c-2f4d-48ee-bfdd-cdbbb50538be/image.png" alt=""></p>
</blockquote>
</li>
</ul>
<ul>
<li><p>하나의 Task(송신 Task)는 200ms마다 queue를 통해 값 100을 다른 Task로 반복적으로 전송하며,
(예시)
<img src="https://velog.velcdn.com/images/dev_junseok_22/post/334f34fe-cb81-4c8f-b7bf-9d3e9d6b162e/image.png" alt=""></p>
</li>
<li><p>timer는 2000ms마다 같은 queue에 값 200을 전송함
(예시)
<img src="https://velog.velcdn.com/images/dev_junseok_22/post/4b144ef9-64c4-4b07-aed1-4f22e84068e4/image.png" alt=""></p>
</li>
<li><p>그리고 수신 Task는 queue에서 두 값 중 하나를 받을때마다 메시지를 출력함</p>
</li>
</ul>
<h3 id="3-3-2-full-demo">3-3-2. Full Demo</h3>
<ul>
<li><p><code>mainSELECTED_APPLICATION</code>이 <code>FULL_DEMO</code>로 설정되어 있을 경우,</p>
</li>
<li><p><code>main()</code> 함수는 <code>main_full()</code> 함수를 호출함 (from <code>main.full.c</code>)</p>
</li>
<li><p><code>main_full()</code>은 하나의 <code>the standard demo tasks</code>들로 구성되어 있으며, 해당 Task는 특정한 기능을 수행하지는 않고, RTOS 포트의 테스트 및 FreeRTOS API 사용 방법을 보여주는 역할을 함</p>
</li>
<li><p>전체 데모에는 check Task가 포함되어 있으며, 이 Task는 10초마다 실행되며, 가장 높은 우선수위를 가져 항상 처리 시간을 확보할 수 있도록 보장됨.(해당 Task의 주요 기능은 모든 표준 데모 태스크가 여전히 정상적으로 동작하고 있는지 확인하는 역할을 함)</p>
</li>
<li><p>Check Task는 실행될 때마다 상태 문자열을 콘솔에 출력하며, 만약 모든 표준 데모 태스크가 오류 없이 실행 중이라면, 문자열에는 &quot;OK&quot;와 현재 틱 카운트가 포함됨
(예시)
<img src="https://velog.velcdn.com/images/dev_junseok_22/post/eef8afd3-39ea-41e2-a6fa-a230beec2859/image.png" alt=""></p>
</li>
</ul>
<ul>
<li>반면, 오류가 감지되면 해당 문자열에는 오류를 보고한 Task를 나타내는 메시지가 포함됨
(예시)
<img src="https://velog.velcdn.com/images/dev_junseok_22/post/2e74b86a-a3df-4e7e-9c5f-00836a59622b/image.png" alt=""></li>
</ul>
<h1 id="4-freertos-데모-프로젝트-빌드">4. FreeRTOS 데모 프로젝트 빌드</h1>
<p>Posix/Linux 시뮬레이터 데모를 위해, 경로 이동
(현 예제에서는 커널 전용 데모를 빌드하고자 하여, 3.2에서 기술한 Demo Project (1) : 커널 전용 데모 의 경로에서 진행)</p>
<pre><code class="language-bash">cd FreeRTOS/Demo/Posix_GCC</code></pre>
<p>make 명령어로 Posix_GCC 데모 프로젝트 빌드</p>
<pre><code>make</code></pre><p>빌드가 성공적으로 완료되면, <code>build</code> 디렉토리가 생성되며, 하위에 <code>posix_demo</code> 파일이 생성됨</p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/476eaef8-08f0-4fb2-993b-faa2510a02cb/image.png" alt=""></p>
<h1 id="5-linux-데모-프로그램-실행">5. (Linux) 데모 프로그램 실행</h1>
<p><code>posix_demo</code> 파일 실행</p>
<pre><code class="language-bash">./posix_demo</code></pre>
<p>*[실행화면]
<img src="https://velog.velcdn.com/images/dev_junseok_22/post/65f7f0eb-998e-4eac-9920-a9bcf3d45e5c/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터 질의어 (DQL, Data Query Language) (1)]]></title>
            <link>https://velog.io/@dev_junseok_22/Temp-Title</link>
            <guid>https://velog.io/@dev_junseok_22/Temp-Title</guid>
            <pubDate>Tue, 04 Jun 2024 13:39:06 GMT</pubDate>
            <description><![CDATA[<h1 id="데이터-질의어-dql-data-query-language">데이터 질의어 (DQL, Data Query Language)</h1>
<p>사용자들은 원하는 데이터를 추출하기 위하여 <code>select</code> 구문을 사용하며, <code>select</code> 구문은 데이터를 질의하는 구문이무로 <code>DQL</code> 문이라고도 함</p>
<blockquote>
<p>SQL 구문 작성 지침</p>
</blockquote>
<ul>
<li>키워드 예약어 등에 대한 대소문자 구분이 없음</li>
<li>여러 라인에 걸쳐서 작성 가능함</li>
<li>키워드는 줄여서 쓰거나 나눠서 쓰면 안됨</li>
<li>절과 절은 나뉘어 사용</li>
<li>적절한 들여 쓰기는 가독성을 향상시킴</li>
<li>세미콜론 (;) 을 사용하여 문장의 끝을 표시하도록 함</li>
</ul>
<table>
<thead>
<tr>
<th>기본 문형</th>
<th>항목 설명</th>
<th>필수 여부</th>
</tr>
</thead>
<tbody><tr>
<td>select *</td>
<td>조회하고자 하는 목록을 열거</td>
<td>O</td>
</tr>
<tr>
<td>from 테이블1[, 테이블 2, …]</td>
<td>사용될 테이블 이름을 명시</td>
<td>O</td>
</tr>
<tr>
<td>where condition</td>
<td>조건절을 명시, 연산자에 대한 이해가 필요</td>
<td>X</td>
</tr>
<tr>
<td>group by column</td>
<td>데이터를 그룹핑하여 좀더 작은 그룹으로 나눌 때 사용</td>
<td>X</td>
</tr>
<tr>
<td>having by column</td>
<td>그룹핑에 대한 조건절을 명시하고자 할 때 사용</td>
<td>X</td>
</tr>
<tr>
<td>order by column</td>
<td>정렬 방식을 지정</td>
<td>X</td>
</tr>
</tbody></table>
<h3 id="기본-데이터-조회-명령어">기본 데이터 조회 명령어</h3>
<pre><code class="language-sql">select * from employees;</code></pre>
<h4 id="비교-연산자">비교 연산자</h4>
<pre><code class="language-sql">select * from employees
where id=&#39;an&#39;;</code></pre>
<h4 id="논리-연산자">논리 연산자</h4>
<pre><code class="language-sql">select * from employees
where salary&gt;=600 and salary&lt;=1000;</code></pre>
<h4 id="널-데이터">널 데이터</h4>
<ul>
<li>비교 판단이 불가능한 정의할 수 없는 어떠한 값</li>
<li>연산 결과 값은 모두 <code>Null</code></li>
<li>비교시, 연산자 대신 <code>is null</code>로 비교 가능</li>
<li>연결 연산자와 합쳐지는 경우에는 흡수<pre><code class="language-sql">select * from employees
where salary is null;</code></pre>
</li>
</ul>
<h4 id="between-연산자">Between 연산자</h4>
<blockquote>
<p>Between 하한값 and 상한값 
이 때, 하한값과 상한값은 모두 구하고자 하는 범위에 포함</p>
</blockquote>
<pre><code class="language-sql">select * from employees
where salary between 120 and 500;</code></pre>
<h4 id="in-연산자">in 연산자</h4>
<pre><code class="language-sql">select * from employees
where manage in (&quot;kim9&quot;, &quot;yusin&quot;);</code></pre>
<h4 id="like-연산자">like 연산자</h4>
<ul>
<li><p>% : 0개 이상의 문자열을 의미</p>
</li>
<li><p>_ : 반드시 1개의 문자를 의미</p>
</li>
<li><p>첫 글자가 [선]인 회원 : <code>select * from employees where name like &quot;선%&quot;;</code></p>
</li>
<li><p>이름 중에 [연]이라는 글자가 있는 회원 : <code>select * from employees where name like &quot;%연%&quot;;</code></p>
</li>
<li><p>이름의 2번째 글자가 [리] 인 회원 : <code>select * from employees where name like &quot;_리&quot;;</code></p>
</li>
<li><p>이름 중에 [성]이 들어 있는 회원 : <code>select * from employees where name like &quot;%선%&quot;;</code></p>
</li>
</ul>
<h4 id="부정어-사용하기">부정어 사용하기</h4>
<blockquote>
</blockquote>
<ul>
<li>where id not in (&#39;kim9&#39;, &#39;yusin&#39;);</li>
<li>where marriage not in(&#39;이혼&#39;);</li>
<li>where salary not between 220 and 230;</li>
<li>where name not like &#39;김%&#39;;</li>
<li>where salary is not null;</li>
<li>where manager is not null;</li>
</ul>
<h4 id="연산자의-복합적-사용">연산자의 복합적 사용</h4>
<p>여러 키워드를 복합적으로 사용 가능</p>
<p>ex) 성씨가 &quot;김&quot;씨 이거나 &quot;여자&quot;인 회원들을 출력하되, 이름에 대하여 내림차순 정렬</p>
<pre><code class="language-sql">select * 
from employees 
where name like &quot;김%&quot; or gender=&quot;여자&quot;
order by name desc;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/dd227a00-0f8c-4306-834e-9e0e9bfb63bb/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[트랜잭션 개요]]></title>
            <link>https://velog.io/@dev_junseok_22/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B0%9C%EC%9A%94</link>
            <guid>https://velog.io/@dev_junseok_22/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B0%9C%EC%9A%94</guid>
            <pubDate>Tue, 04 Jun 2024 11:18:57 GMT</pubDate>
            <description><![CDATA[<h1 id="트랜잭션-개요">트랜잭션 개요</h1>
<ul>
<li>트랜잭션은 데이터 처리에 있어 하나의 논리적 단위.</li>
<li>트랜잭션이 필요한 이유는 데이터의 무결성을 유지하고, 일관성 있게 안정적으로 데이터를 유지하기 위함</li>
</ul>
<h4 id="트랜잭션의-개념">트랜잭션의 개념</h4>
<p><strong>구매</strong> : 강감찬이 사과 10개와 배 20개를 구매
<strong>Update</strong> : 상품의 재고 수량이 감소
<strong>Insert</strong> : 주문 테이블에 주문 내역이 추가
<strong>Update</strong> : 회원의 마일리지 포인트 적립</p>
<p>위 4건의 업무를 논리적으로 보았을 때 하나의 묶음 단위로 처리가 되어야 하며, 이러한 논리적은 묶음을 <strong>트랜잭션</strong>이라고 함</p>
<h2 id="트랜잭션-제어어">트랜잭션 제어어</h2>
<ul>
<li>데이터 조작어(DML)는 해당 작업이 실행 됨과 동시에 트랜잭션이 시작됨</li>
<li>이러한 작업이 영구적으로 저장이 되려면 <code>commit</code> 명령어를</li>
<li>취소하기 위해선 <code>rollback</code> 명령어를 사용</li>
<li><code>commit</code>과 <code>rollback</code> 명령어에 의해 트랜잭션은 종료됨</li>
</ul>
<h3 id="커밋commit">커밋(Commit)</h3>
<p>이전에 수행했던 모든 작업들을 영구적으로 저장하겠다고 데이터베이스에 알려주는 명령어로, commit 명령어를 사용하면 해당 지점까지 하나의 트랜잭션이 종료됨</p>
<p>&lt;----- 첫 번째 트랜잭션 -----&gt; &lt;----- 두 번째 트랜잭션 -----&gt;
  Insert &gt; Update &gt; Delete &gt; Insert &gt; Update &gt; Delete
(출근) --------------- (Commit) --------------- (Commit)</p>
<p>출근과 동시에 첫 번째 트랜잭션이 시작되며, Commit 과 동시에 첫번째 트랜잭션이 종료됨, 이후 두 번째 Insert문이 시작되는 지점에서 두 번째 트랜잭션이 시작됨</p>
<h3 id="롤백rollback">롤백(Rollback)</h3>
<p>작업을 수행하다가 사용자의 실수로 인한 명령어 취소 또는 어떠한 문제가 발생하였을 때 발생했던 모든 변경 사항들을 취소하고자 할 때 사용하는 명령어</p>
<p>&lt;----- 첫 번째 트랜잭션 -----&gt; &lt;----- 두 번째 트랜잭션 -----&gt;
  (1)--------------------(2)--(3)--------------------(4)
  Insert &gt; Update &gt; Delete &gt; Insert &gt; Update &gt; Delete
(출근) --------------- (Commit) --------------- (rollback)</p>
<p>(3) 지점에서 두 번째 트랜잭션이 시작되었을 때, (4) 지점에서 트랜잭션이 완료되었으나 <code>rollback</code> 이 발생하게됨. 이로 인해 두 번째 트랜잭션이 수행되기 전인 (2) 지점으로 원상 복귀함</p>
<h3 id="저장점savepoint">저장점(Savepoint)</h3>
<p>저장점은 큰 트랜잭션을 작게 분할하는 기법으로, 중간 중간에 저장된 저장점까지 rollback을 수행할 수 있음</p>
<p>(명령어)</p>
<pre><code class="language-sql">savepoint 레이블_이름
rollback to 레이블_이름</code></pre>
<p>------(1)------------------(2)-----------------(3)-------(4)</p>
<p>insert &gt;&gt; update &gt;&gt; delete &gt;&gt; insert &gt;&gt; update &gt;&gt; delete</p>
<p>----commit-----------savepoint1---------savepoint2---commit</p>
<p>(4) 지점에서 <code>rollback</code> 할 경우, (1) 지점으로
(4) 지점에서 <code>rollback to savepoint1</code> 할 경우, (2) 지점으로
(4) 지점에서 <code>rollback to savepoint2</code> 할 경우, (3) 지점으로 </p>
<p>원상 복귀 되어짐</p>
<h2 id="실습">실습</h2>
<p><code>CTAS</code> 기법을 통해 집합 연산자 파트에서 좀 더 세부적으로 나눔</p>
<pre><code class="language-sql">create table myemp as
select id, name, salary, marriage, gender from employees;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/04955717-4fd9-4121-9972-972ec21e6153/image.png" alt=""></p>
<p>성별이 여자인 사람의 급여를 1000으로 설정</p>
<pre><code class="language-sql">update myemp1 set salary=1000 where gender=&#39;여자&#39;;
commit;

select * from myemp1;</code></pre>
<p><code>savepoint</code> 실습
이전 테이블을 삭제하고 테이블 다시 생성</p>
<pre><code class="language-sql">create table myemp2 as
select id, name, salary, marriage, gender from employees;

select * from myemp2;</code></pre>
<p>최초의 상태를 <code>savepoint point01</code>로 지정,
그 후 성별이 여자인 회원의 급여를 1000으로 수정</p>
<pre><code class="language-sql">savepoint point01;
update myemp2 set salary=1000 where gender=&#39;여자&#39;;
select * from myemp2;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/e35dc74b-3223-4292-8fc8-5eff5a88882d/image.png" alt=""></p>
<p>현재의 상태를 <code>savepoint point02</code>로 지정,
성별이 남자인 회원을 모두 삭제</p>
<pre><code class="language-sql">savepoint point02;
delete from myemp2 where gender=&#39;남자&#39;;
select * from myemp2;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/d2dff155-6ffa-4bab-9ef0-011cce3f6632/image.png" alt=""></p>
<p><code>rollback to</code> 문구를 사용해 <code>savepoint point02</code> 지점으로 이동</p>
<pre><code class="language-sql">rollback to point02;
select * from myemp2;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/5a3a644e-0911-4450-875a-8431905ca101/image.png" alt=""></p>
<p><code>savepoint point01</code> 지점으로 이동</p>
<pre><code class="language-sql">rollback to point01;
select * from myemp2;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/09d72783-f86d-4913-bb57-caa1a327e8cd/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터 조작어 (DML, Data Manipulation Language)]]></title>
            <link>https://velog.io/@dev_junseok_22/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A1%B0%EC%9E%91%EC%96%B4-DML-Data-Manipulation-Language</link>
            <guid>https://velog.io/@dev_junseok_22/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A1%B0%EC%9E%91%EC%96%B4-DML-Data-Manipulation-Language</guid>
            <pubDate>Tue, 04 Jun 2024 10:42:13 GMT</pubDate>
            <description><![CDATA[<h1 id="데이터-조작어-dml-data-manipulation-language">데이터 조작어 (DML, Data Manipulation Language)</h1>
<p>테이블의 행(row)에 대하여 추가/수정/삭제 등을 수행하기 위한 언어</p>
<ul>
<li>Transaction 및 undo 데이터에 대한 지식이 필요함</li>
</ul>
<h2 id="행row-추가하기">행(row) 추가하기</h2>
<h3 id="명령어">(명령어)</h3>
<pre><code class="language-sql">insert into 테이블이름(컬럼01, 컬럼02, …)
values(값01, 값02, …)</code></pre>
<hr>
<pre><code class="language-sql">Insert into employees(id, name, password, gender, birth, marriage, salary, address, manager)
values(&#39;yusin&#39;, &#39;김유신&#39;, &#39;abc123&#39;, &#39;남자&#39;, &#39;1990/12/25&#39;, &#39;결혼&#39;, 220, &#39;용산&#39;, null);</code></pre>
<hr>
<h2 id="다시-employees-테이블-생성">다시, employees 테이블 생성</h2>
<pre><code class="language-sql">CREATE TABLE employees ( 
    id VARCHAR(20) PRIMARY KEY, 
    name VARCHAR(30) NOT NULL, 
    password VARCHAR(20) NOT NULL, 
    gender VARCHAR(10), 
    birth DATE, 
    marriage VARCHAR(30), 
    salary DECIMAL(10, 2) DEFAULT 100.00, 
    address VARCHAR(50), 
    manager VARCHAR(50) 
);</code></pre>
<h2 id="몇-건의-데이터-추가">몇 건의 데이터 추가</h2>
<hr>
<pre><code class="language-sql">Insert into employees(id, name, password, gender, birth, marriage, salary, address, manager)
values(&#39;yusin&#39;, &#39;김유신&#39;, &#39;abc123&#39;, &#39;남자&#39;, &#39;1990/12/25&#39;, &#39;결혼&#39;, 220, &#39;용산&#39;, null);

Insert into employees(id, name, password, gender, birth, marriage, salary, address, manager)
values(&#39;lee&#39;, &#39;이순신&#39;, &#39;abc123&#39;, &#39;남자&#39;, &#39;1990/12/25&#39;, &#39;이혼&#39;, 220, &#39;용산&#39;, null);

Insert into employees(id, name, password, gender, birth, marriage, salary, address, manager)
values(&#39;choi&#39;, &#39;최영&#39;, &#39;abc123&#39;, &#39;남자&#39;, &#39;1990/12/25&#39;, &#39;결혼&#39;, 155, &#39;용산&#39;, &#39;yusin&#39;);

Insert into employees(id, name, password, gender, birth, marriage, salary, address, manager)
values(&#39;kang&#39;, &#39;강감찬&#39;, &#39;abc123&#39;, &#39;남자&#39;, &#39;1990/12/25&#39;, &#39;결혼&#39;, null, &#39;용산&#39;, &#39;yusin&#39;);</code></pre>
<hr>
<h2 id="결과">(결과)</h2>
<p>컬럼 이름을 명시하는 경우
컬럼의 갯수와 데이터의 타입을 반드시 맞춰줘야함</p>
<pre><code class="language-sql">Insert into employees(id, name, password, gender, birth, marriage, salary, address, manager)
values(&#39;yoon&#39;, &#39;윤봉길&#39;, &#39;abc123&#39;, &#39;남자&#39;, &#39;1990/12/25&#39;, &#39;미혼&#39;, 230, &#39;용산&#39;, &#39;yusin&#39;);

Insert into employees(id, name, password, gender, birth, marriage, salary, address, manager)
values(&#39;kim9&#39;, &#39;김구&#39;, &#39;abc123&#39;, &#39;남자&#39;, &#39;1990/12/25&#39;, &#39;이혼&#39;, 280, &#39;강남&#39;, null);

Insert into employees(id, name, password, gender, birth, marriage, salary, address, manager)
values(&#39;general&#39;, &#39;김좌진&#39;, &#39;abc123&#39;, &#39;남자&#39;, &#39;1990/12/25&#39;, &#39;결혼&#39;, 550, &#39;마포&#39;, &#39;kim9&#39;);

Insert into employees(id, name, password, gender, birth, marriage, salary, address, manager)
values(&#39;an&#39;, &#39;안중근&#39;, &#39;abc123&#39;, &#39;남자&#39;, &#39;1990/12/25&#39;, &#39;결혼&#39;, 155, &#39;용산&#39;, &#39;kim9&#39;);</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/71f81fd3-7035-4158-95bb-5966684316c5/image.png" alt=""></p>
<p>입력이 필요 없는 컬럼을 명시하지 않는 경우
굳이 명시할 필요가 없는 컬럼은 insert 구문에 작성할 필요없음, 급여가 책정되지 않은 직원의 경우 기본값 100으로 입력됨</p>
<pre><code class="language-sql">Insert into employees(id, name, password, gender, birth, marriage, address, manager)
values(&#39;nongae&#39;, &#39;논개&#39;, &#39;abc1234&#39;, &#39;여자&#39;, &#39;1990/12/25&#39;, &#39;미혼&#39;, &#39;강남&#39;, &#39;soon&#39;);</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/1aa5a7e9-d5dd-417e-8816-c9c0f6df0880/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/ee640692-d526-4572-978a-6f65c3d2493e/image.png" alt=""></p>
<h2 id="연산자-살펴-보기">연산자 살펴 보기</h2>
<h3 id="비교-연산자">비교 연산자</h3>
<table>
<thead>
<tr>
<th>연산자</th>
<th>연산자</th>
<th>의미</th>
<th>수학적 기호</th>
<th>오라클 결과</th>
</tr>
</thead>
<tbody><tr>
<td>항등 연산자</td>
<td>=</td>
<td>같다</td>
<td>=</td>
<td>False</td>
</tr>
<tr>
<td>항등 연산자</td>
<td>&lt;&gt;</td>
<td>같지않다</td>
<td>!=</td>
<td>True</td>
</tr>
<tr>
<td>비교 연산자</td>
<td>&gt;</td>
<td>좌측이 크다</td>
<td>&gt;</td>
<td>True</td>
</tr>
<tr>
<td>비교 연산자</td>
<td>&gt;=</td>
<td>좌측이 크거나 같다</td>
<td>&gt;=</td>
<td>False</td>
</tr>
<tr>
<td>비교 연산자</td>
<td>&lt;</td>
<td>좌측이 작다</td>
<td>&lt;</td>
<td>True</td>
</tr>
<tr>
<td>비교 연산자</td>
<td>&lt;=</td>
<td>좌측이 작거나 같다</td>
<td>&lt;=</td>
<td>True</td>
</tr>
</tbody></table>
<h3 id="논리-연산자">논리 연산자</h3>
<table>
<thead>
<tr>
<th>연산자</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>And</td>
<td>논리곱</td>
</tr>
<tr>
<td>Or</td>
<td>논리합</td>
</tr>
<tr>
<td>not</td>
<td>부정</td>
</tr>
</tbody></table>
<h3 id="예시">예시</h3>
<pre><code class="language-sql">select * from employees where name=&#39;강감찬&#39; or name=&#39;논개&#39;;</code></pre>
<h2 id="행-수정하기">행 수정하기</h2>
<p>Update 구문은 이미 저장이 되어 있는 데이터 값을 변경하고자 하는 경우 사용하는 문장</p>
<h3 id="명령어-1">(명령어)</h3>
<pre><code class="language-sql">update 테이블_이름
set 컬럼1=값1, 컬럼2=값2, …, 컬럼n = 값n
Where 조건식;</code></pre>
<h3 id="ex-모든-직원들의-월급-100으로-설정">Ex) 모든 직원들의 월급 100으로 설정</h3>
<pre><code class="language-sql">update employees 
set salary=100;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/c8759ea4-c368-40cb-972d-5a4c47bc8b66/image.png" alt=""></p>
<h3 id="ex-모든-회원들의-생일을-오늘로">Ex) 모든 회원들의 생일을 오늘로</h3>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/40f54183-ae2e-48f2-a029-bc1e1a19cd2c/image.png" alt=""></p>
<h3 id="ex-모든-회원들의-급여를-500으로-비번은-abcd1234로-변경">Ex) 모든 회원들의 급여를 500으로, 비번은 ‘abcd1234’로 변경</h3>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/83e7e665-a83f-46f2-911d-f3a08a40115e/image.png" alt=""></p>
<h3 id="ex-안중근의-급여를-400으로-조정">Ex) 안중근의 급여를 400으로 조정</h3>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/a70bf0d0-5249-47d9-8d2a-08e421d6014d/image.png" alt=""></p>
<h2 id="다시-뒤로-rollback">다시 뒤로 (Rollback)</h2>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/65bfd368-ecd0-4583-828d-3c6dd3d8e28a/image.png" alt=""></p>
<h3 id="명령어-2">(명령어)</h3>
<pre><code class="language-sql">rollback;</code></pre>
<h3 id="ex-모든-직원들의-급여-10-인상">Ex) 모든 직원들의 급여 10% 인상</h3>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/22410d3e-90f4-44a7-8777-9f1769857c5b/image.png" alt=""></p>
<h2 id="in-연산자">in 연산자</h2>
<p>아이디가 kim9 이거나 lee이거나 kang 인 항목을 찾아보는 문구?</p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/b12afc0f-6002-4d1d-a98d-01473e5b9662/image.png" alt=""></p>
<p>그런데 in을 사용한다면?
(명령어) in (set)</p>
<blockquote>
<p>or 연산자 대신에 간략히 표현할 목적으로 만든 키워드
or 연산자의 조합으로 셋트 목록 중의 하나와 일치하는 모두를 의미함</p>
</blockquote>
<pre><code class="language-sql">select * from employees where id in (&#39;kim9&#39;, &#39;lee&#39;, &#39;kang&#39;);</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/be8f47b5-2b11-4247-834f-d3a4100f26a1/image.png" alt=""></p>
<p>Ex) in 키워드를 사용해 관리자가 ‘김유신’이나 ‘김구’인 회원들의 급여를 555로 변경</p>
<pre><code class="language-sql">update employees set salary=555 where manager in (&#39;yusin&#39;,&#39;kim9&#39;);</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/48f3ae7a-c2e3-4200-84f2-70ccbf2b737b/image.png" alt=""></p>
<h2 id="행-삭제하기">행 삭제하기</h2>
<p>delete 문법을 사용함</p>
<pre><code class="language-sql">delete from 테이블_이름 where 조건식;</code></pre>
<blockquote>
<p>주의) column 삭제는 불가능하며, 문자와 날짜는 &#39;&#39;(외따옴표)로 둘러싸야함</p>
</blockquote>
<p>(예시)</p>
<pre><code class="language-sql">delete from employees;
select * from employees;</code></pre>
<p>&quot;안중근&quot; 회원에 대한 정보 삭제</p>
<pre><code class="language-sql">delete from employees where id=&#39;an&#39;;
select * from employees;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/80b018f6-3757-4478-b131-37b3f9e82aa9/image.png" alt=""></p>
<p>관리자가 &quot;김유신&quot;인 회원 이름 삭제</p>
<pre><code class="language-sql">delete from employees where manager=&#39;yusin&#39;;
select * from employees;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/5ccf9810-ed17-4e33-b7f6-5d9bf52ad7cd/image.png" alt=""></p>
<p>관리자가 &quot;soon&quot;이고 급여가 220 이상인 회원 삭제</p>
<pre><code class="language-sql">delete from employees where manager=&#39;soon&#39; and salary&gt;=220;
select * from employees;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/067ba8ae-46fb-409e-b994-634247086db1/image.png" alt=""></p>
<p>주소가 &#39;서대문&#39;과 &#39;강남&#39;을 제외한 다른 지역에 거주하는 회원을 삭제</p>
<pre><code class="language-sql">delete from employees where address not in (&#39;서대문&#39;, &#39;강남&#39;);
select * from employees;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/8952830a-6af7-4598-9a7d-6db8d21f7890/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터 정의어 (DDL, Data Definition Language)]]></title>
            <link>https://velog.io/@dev_junseok_22/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%95%EC%9D%98%EC%96%B4-DDL-Data-Definition-Language</link>
            <guid>https://velog.io/@dev_junseok_22/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%95%EC%9D%98%EC%96%B4-DDL-Data-Definition-Language</guid>
            <pubDate>Tue, 04 Jun 2024 10:13:09 GMT</pubDate>
            <description><![CDATA[<h1 id="데이터-정의어ddl-data-definition-language-란">데이터 정의어(DDL, Data Definition Language) 란?</h1>
<p>행을 row 또는 record라 부르며, 열을 column 또는 field라 부름</p>
<h2 id="테이블-명세">테이블 명세</h2>
<p>테이블의 컬럼 이름이나 개수, 데이터의 타입, 기본값, 특이점 등을 명시 해놓은 정보</p>
<table>
<thead>
<tr>
<th>컬럼</th>
<th>데이터 타입</th>
<th>길이</th>
<th>기본 값</th>
<th>널 허용</th>
<th>코멘트</th>
</tr>
</thead>
<tbody><tr>
<td>id</td>
<td>varchar2</td>
<td>20</td>
<td></td>
<td>no</td>
<td>아이디</td>
</tr>
<tr>
<td>name</td>
<td>varchar2</td>
<td>30</td>
<td></td>
<td>no</td>
<td>이름</td>
</tr>
<tr>
<td>password</td>
<td>varchar2</td>
<td>30</td>
<td></td>
<td>yes</td>
<td>비밀 번호</td>
</tr>
<tr>
<td>gender</td>
<td>varchar2</td>
<td>10</td>
<td></td>
<td>yes</td>
<td>성별</td>
</tr>
<tr>
<td>birth</td>
<td>Date</td>
<td></td>
<td>sysdate</td>
<td>yes</td>
<td>생년월일</td>
</tr>
<tr>
<td>marriage</td>
<td>varchar2</td>
<td>30</td>
<td></td>
<td>yes</td>
<td>결혼 유형</td>
</tr>
<tr>
<td>salary</td>
<td>number</td>
<td></td>
<td>100</td>
<td>yes</td>
<td>급여</td>
</tr>
<tr>
<td>address</td>
<td>varchar2</td>
<td>50</td>
<td></td>
<td>yes</td>
<td>주소</td>
</tr>
<tr>
<td>manager</td>
<td>varchar2</td>
<td>50</td>
<td></td>
<td>yes</td>
<td>관리자의 아이디 정보</td>
</tr>
</tbody></table>
<h2 id="컬럼의-자료-유형">컬럼의 자료 유형</h2>
<ul>
<li><strong>number</strong>: 숫자형 데이터로 정수 및 실수 사용이 가능함</li>
<li><strong>varchar2</strong>: 가변 길이 문자열 타입으로, 최대 4000 바이트까지 가능</li>
<li><strong>date</strong>: 날짜 데이터 타입으로, 시스템 설정에 따라 출력 형태가 다름</li>
</ul>
<h3 id="예시">예시</h3>
<ul>
<li><p>id의 타입을 길이가 20인 문자열을 사용할 경우</p>
<pre><code class="language-sql">id varchar2(20)</code></pre>
</li>
<li><p>salary의 기본값을 100으로 설정할 경우</p>
<pre><code class="language-sql">salary number default 100</code></pre>
</li>
<li><p>필수 사항일 경우</p>
<pre><code class="language-sql">name varchar(30) not null</code></pre>
</li>
</ul>
<h2 id="기본-키primary-key">기본 키(Primary Key)</h2>
<p>기본 키는 각각의 행/레코드를 구별하기 위한 컬럼/필드로, not null이며 다른 행과 중복되어서는 안됨(unique)</p>
<ul>
<li>컬럼 id는 기본키이므로, 다른 컬럼과 중복되어선 안되며 필수적으로 입력되어야함<pre><code class="language-sql">id varchar2(20) primary key</code></pre>
</li>
</ul>
<h2 id="데이터베이스-생성">데이터베이스 생성</h2>
<pre><code class="language-sql">CREATE DATABASE mydatabase;</code></pre>
<h2 id="데이터베이스-실행">데이터베이스 실행</h2>
<pre><code class="language-sql">USE mydatabase;</code></pre>
<h2 id="테이블-생성-구문">테이블 생성 구문</h2>
<pre><code class="language-sql">Create table 테이블_이름(
    컬럼_이름 데이터_타입 [not null] [default 기본값] [primary key],
    컬럼_이름 데이터_타입 [not null] [default 기본값]
    …
);</code></pre>
<h3 id="예시-1">예시</h3>
<pre><code class="language-sql">CREATE TABLE employees ( 
    id VARCHAR(20) PRIMARY KEY, 
    name VARCHAR(30) NOT NULL, 
    password VARCHAR(20) NOT NULL, 
    gender VARCHAR(10), 
    birth DATE, 
    marriage VARCHAR(30), 
    salary DECIMAL(10, 2) DEFAULT 100.00, 
    address VARCHAR(50), 
    manager VARCHAR(50) 
);</code></pre>
<h2 id="데이터-추가하기">데이터 추가하기</h2>
<pre><code class="language-sql">insert into 테이블이름(컬럼01, 컬럼02, …)
values(값01, 값02, …);</code></pre>
<h3 id="예시-2">예시</h3>
<pre><code class="language-sql">Insert into employees(id, name, password, gender, birth, marriage, salary, address, manager)
values(&#39;yusin&#39;, &#39;김유신&#39;, &#39;abc123&#39;, &#39;남자&#39;, &#39;1990/12/25&#39;, &#39;결혼&#39;, 220, &#39;용산&#39;, null);

Insert into employees(id, name, password, gender, birth, marriage, salary, address, manager)
values(&#39;choi&#39;, &#39;최영&#39;, &#39;abc123&#39;, &#39;남자&#39;, &#39;1990/12/25&#39;, &#39;결혼&#39;, 220, &#39;용산&#39;, &#39;yusin&#39;);

Insert into employees(id, name, password, gender, birth, marriage, salary, address, manager)
values(&#39;kang&#39;, &#39;강감찬&#39;, &#39;abc123&#39;, &#39;남자&#39;, &#39;1990/12/25&#39;, &#39;결혼&#39;, 220, &#39;용산&#39;, &#39;yusin&#39;);</code></pre>
<h2 id="테이블의-모든-행-정보-출력하기">테이블의 모든 행 정보 출력하기</h2>
<pre><code class="language-sql">select * from 테이블이름;</code></pre>
<h3 id="예시-3">예시</h3>
<pre><code class="language-sql">select * from employees;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/796a8e4c-9bdf-4ab3-9f83-2ee88a426ae8/image.png" alt=""></p>
<h2 id="테이블-정보-확인">테이블 정보 확인</h2>
<ul>
<li>테이블의 구조 확인<pre><code class="language-sql">desc 테이블명;</code></pre>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/363d8bf9-bdf8-4778-bae6-f3b7d6ec8bc2/image.png" alt=""></p>
<h2 id="테이블-구조-변경">테이블 구조 변경</h2>
<h3 id="컬럼-추가하기">컬럼 추가하기</h3>
<pre><code class="language-sql">alter table 테이블_이름 add (컬럼_이름 데이터_타입);</code></pre>
<h3 id="예시-4">예시</h3>
<pre><code class="language-sql">alter table employees add hphone varchar(15);

![](https://velog.velcdn.com/images/dev_junseok_22/post/90f01681-186e-41b9-8648-7de82859ff3a/image.png)


alter table employees add (age int default 0);

![](https://velog.velcdn.com/images/dev_junseok_22/post/6f83c0e3-8e32-4b03-9196-04f5ba87ff96/image.png)


alter table employees add (nickname varchar(30) default &#39;철수&#39;);</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/5e09f337-5627-4b6e-bfae-bc016a398d25/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/26cd94af-ec27-4f49-a117-ca144e7447a0/image.png" alt=""></p>
<h3 id="컬럼-구조-변경하기">컬럼 구조 변경하기</h3>
<p>데이터의 Type, 길이, Default 값 등을 변경</p>
<pre><code class="language-sql">alter table 테이블_이름 modify 컬럼_이름 데이터_타입;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/3d213303-eb91-471e-b9f3-d7c77cbcf53f/image.png" alt=""></p>
<h3 id="예시-5">예시</h3>
<pre><code class="language-sql">alter table employees modify id varchar(30);</code></pre>
<h3 id="컬럼-이름-변경">컬럼 이름 변경</h3>
<pre><code class="language-sql">alter table 테이블_이름 rename column 이전_컬럼_이름 to 새로운_컬럼_이름;</code></pre>
<h3 id="예시-6">예시</h3>
<pre><code class="language-sql">alter table employees rename column hphone to handphone;</code></pre>
<h2 id="테이블-이름-변경하기">테이블 이름 변경하기</h2>
<pre><code class="language-sql">rename 이전_테이블_이름 to 신규_테이블_이름;</code></pre>
<h3 id="예시-7">예시</h3>
<pre><code class="language-sql">rename table employees to emp99;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/4ec66d9c-af40-470c-8c90-1fc2fdd47176/image.png" alt=""><img src="https://velog.velcdn.com/images/dev_junseok_22/post/f53db087-d6de-4b89-b26c-d38e4ce8c05a/image.png" alt=""></p>
<h2 id="테이블-삭제">테이블 삭제</h2>
<p>Purge 구문 없이 사용하면 테이블이 휴지통으로 이동하고, Purge 구문을 사용하면 영구 삭제됨</p>
<pre><code class="language-sql">drop table 테이블_이름 purge;</code></pre>
<h3 id="예시-8">예시</h3>
<pre><code class="language-sql">drop table emp99;</code></pre>
<p><img src="blob:https://velog.io/b4c48b97-2abd-4640-9dbe-443c0cd3e839" alt="업로드중.."></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스란?]]></title>
            <link>https://velog.io/@dev_junseok_22/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%9E%80</link>
            <guid>https://velog.io/@dev_junseok_22/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%9E%80</guid>
            <pubDate>Tue, 04 Jun 2024 10:07:53 GMT</pubDate>
            <description><![CDATA[<p>markdown</p>
<h1 id="데이터베이스란">데이터베이스란?</h1>
<p>데이터를 저장해두고, 필요시 이것을 가져다가 사용하거나 수정/조회/삭제 하기 위한 자료 저장소 </p>
<h2 id="데이터베이스의-목표">데이터베이스의 목표</h2>
<p>지속적 데이터 관리 및 보호와, 안정성 및 무결성의 보장</p>
<h2 id="데이터베이스의-특징">데이터베이스의 특징</h2>
<ul>
<li>실시간 접근</li>
<li>동적인 변화에 대한 유연한 대처</li>
<li>동시 공용성 (특정인의 전유물이 되어선 안됨)</li>
<li>실제 데이터에 의한 참조</li>
</ul>
<h1 id="sql">SQL?</h1>
<p>Structured Query Language의 준말로, DataBase로부터 데이터를 조회/삭제/수정 등의 작업을 수행할 때 사용하는 질의 언어 </p>
<blockquote>
<p>질의란? 데이터베이스에게 어떠한 질문을 하여 결과를 도출하기 </p>
</blockquote>
<h2 id="주요-sql-구문">주요 SQL 구문</h2>
<h3 id="ddl-data-definition-language">DDL (Data Definition Language)</h3>
<ul>
<li>데이터 정의어 (CREATE, ALTER, DROP)</li>
<li>데이터 구조를 정의(CREATE)하고, 구조 변경(ALTER) 및 테이블 삭제(DROP) 등을 수행하기 위한 문법</li>
<li>실행 주체: 객체 단위</li>
</ul>
<h3 id="dml-data-manipulation-language">DML (Data Manipulation Language)</h3>
<ul>
<li>데이터 조작어 (INSERT, UPDATE, DELETE)</li>
<li>행 단위로 데이터를 조작하기 위한 언어로 행을 추가(INSERT)하거나 특정 컬럼에 대한 데이터 수정(UPDATE), 삭제(DELETE) 등을 의미</li>
<li>실행 주체: 행 단위</li>
</ul>
<h3 id="dcl-data-control-language">DCL (Data Control Language)</h3>
<ul>
<li>데이터 제어문 (GRANT, REVOKE)</li>
<li>데이터 접근 권한 등에 대한 부여(GRANT), 접근 권한 삭제(REVOKE) 등의 목적으로 사용되는 언어</li>
<li>실행 주체: 사용자 단위</li>
</ul>
<h3 id="dql-data-query-language">DQL (Data Query Language)</h3>
<ul>
<li>데이터 질의어 (SELECT)</li>
<li>질의(Query)를 통한 데이터 내용을 조회하기 위한 언어</li>
</ul>
<h3 id="tcl-transaction-control-language">TCL (Transaction Control Language)</h3>
<ul>
<li>트랜잭션 (COMMIT, ROLLBACK)</li>
<li>데이터를 영구적으로 저장하는 동작(Commit), DML 구문에 대하여 작업 취소하는 동작(RollBack) 등을 수행</li>
</ul>
<h1 id="dbms란">DBMS란?</h1>
<p>데이터베이스를 관리하기 위하여 필요한 SW 패키지의 집합체 </p>
<h2 id="dbms-주요-기능">DBMS 주요 기능</h2>
<ul>
<li>데이터의 추가/조회/변경/삭제가 가능해야함</li>
<li>데이터의 무결성이 유지되어야함</li>
<li>트랜잭션 관리가 되어야함</li>
<li>데이터 백업 및 복원이 가능해야함</li>
<li>데이터 보안을 중요시 생각함</li>
</ul>
<h2 id="데이터베이스-객체">데이터베이스 객체</h2>
<p>데이터베이스를 구성하고 있는 개별적인 단위 요소</p>
<ul>
<li><strong>테이블</strong>: 행과 열로 구성된 2차원적인 표</li>
<li><strong>시퀀스</strong>: 정수형 번호표 생성기</li>
<li><strong>인덱스</strong>: 데이터 검색을 빨리 하기 위하여 만들어 둔 개념</li>
<li><strong>프로시저</strong>: 반환 타입이 없는 객체</li>
<li><strong>함수</strong>: 반환 타입이 있는 객체</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[구조화(2)]]></title>
            <link>https://velog.io/@dev_junseok_22/%EA%B5%AC%EC%A1%B0%ED%99%942</link>
            <guid>https://velog.io/@dev_junseok_22/%EA%B5%AC%EC%A1%B0%ED%99%942</guid>
            <pubDate>Sat, 11 May 2024 22:48:03 GMT</pubDate>
            <description><![CDATA[<h2 id="라우트-구현">라우트 구현</h2>
<p>사용자 라우트는 로그인, 로그아웃, 등록으로 구성된다. 인증을 완료한 사용자는 이벤트를 생성, 변경, 삭제할 수 있으며, 인증을 거치지 않은 사용자는 생성된 이벤트를 확인하는 것만 가능하다.</p>
<h3 id="사용자-라우트">사용자 라우트</h3>
<p><code>routes</code> 폴더의 <code>users.py</code>에 사용자 라우트를 정의해보자</p>
<p>먼저 <strong>등록 라우트</strong>이다. 등록 라우트는 애플리케이션에 내장된 데이터베이스를 사용하며, 사용자를 등록하기 전 데이터베이스에 비슷한 이메일이 존재하는지 확인한다.</p>
<pre><code class="language-python">from fastapi import APIRouter, HTTPException, status
from models.users import User, UserSignIn

user_router = APIRouter(
    tags = [&quot;User&quot;]
)

users = {}

@user_router.post(&quot;/signup&quot;)
async def sign_new_user(data: User) -&gt; dict:
    if data.email in users:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=&quot;User wuth supplied username exists&quot;
        )
    users[data.email] = data
    return {
        &quot;message&quot;:&quot;User successfully registered&quot;
    }</code></pre>
<p>다음은 <strong>로그인(<code>/signin</code>) 라우트</strong>이다.</p>
<p>이 라우트는 로그인하려는 사용자가 데이터베이스에 존재하는지를 먼저 확인하고, 없으면 예외를 발생시킨다. </p>
<p>사용자가 존재하면 패스워드가 일치하는지 확인해서 성공 또는 실패 메시지를 반환한다.</p>
<pre><code class="language-python">@user_router.post(&quot;/signin&quot;)
async def sign_user_in(user: UserSignIn) -&gt; dict:
    if user.email not in users:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=&quot;User does not exist&quot;
        )
    if users[user.email].password != user.passward:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=&quot;Wrong credentials passed&quot;
        )

    return {
        &quot;message&quot;:&quot;User signed in successfully&quot;
    }
</code></pre>
<p>이제 사용자 처리용 라우트를 정의했으니, <code>main.py</code>에 라우트를 등록하고 애플리케이션 실행을 해보자.</p>
<pre><code class="language-python">from fastapi import FastAPI
from routes.users import user_router

import uvicorn

# FastAPI() 인스턴스를 만들고 앞서 정의한 라우트 등록
app = FastAPI()

# 라우트 등록
app.include_router(user_router, prefix=&quot;/user&quot;)

if __name__==&quot;__main__&quot;:
    # uvicorn.run() 메서드를 사용해 8000번 포트에서 애플리케이션을 실행하도록 설정
    uvicorn.run(&quot;main:app&quot;, host=&quot;127.0.0.1&quot;, port=8000, reload=True)</code></pre>
<p>이제 실행해보자.</p>
<pre><code>python3 main.py</code></pre><h2 id="테스트">테스트</h2>
<p>애플리케이션이 실행됐으니 사용자 라우트를 테스트해보자</p>
<h3 id="사용자-등록-테스트">사용자 등록 테스트</h3>
<pre><code>curl -X &#39;POST&#39; \
&#39;http://127.0.0.1:8000/user/signup&#39; \
-H &#39;accept: application/json&#39; \
-H &#39;Content-Type: application/json&#39; \
-d &#39;{
&quot;email&quot;:&quot;fastapi@packt.com&quot;,
&quot;password&quot;:&quot;Stro0ng!&quot;,
&quot;username&quot;:&quot;FastPackt&quot;
}&#39;</code></pre><h4 id="결과">(결과)</h4>
<pre><code>{&quot;message&quot;:&quot;User successfully registered&quot;}%                           </code></pre><h3 id="로그인-테스트">로그인 테스트</h3>
<h4 id="정상-로그인-케이스">정상 로그인 케이스</h4>
<pre><code>curl -X &#39;POST&#39; \
&#39;http://127.0.0.1:8000/user/signin&#39; \
-H &#39;accept: application/json&#39; \
-H &#39;Content-Type: application/json&#39; \
-d &#39;{
&quot;email&quot;:&quot;fastapi@packt.com&quot;,
&quot;password&quot;:&quot;Stro0ng!&quot;
}&#39;</code></pre><h4 id="결과-1">(결과)</h4>
<pre><code>{&quot;message&quot;:&quot;User signed in successfully&quot;}%               </code></pre><h4 id="비정상-로그인-케이스패스워드-틀림">비정상 로그인 케이스(패스워드 틀림)</h4>
<pre><code>curl -X &#39;POST&#39; \
&#39;http://127.0.0.1:8000/user/signin&#39; \
-H &#39;accept: application/json&#39; \
-H &#39;Content-Type: application/json&#39; \
-d &#39;{
&quot;email&quot;:&quot;fastapi@packt.com&quot;,
&quot;password&quot;:&quot;password!&quot;
}&#39;</code></pre><h4 id="결과-2">(결과)</h4>
<pre><code>{&quot;detail&quot;:&quot;Wrong credentials passed&quot;}%              
</code></pre><h3 id="이벤트-라우트">이벤트 라우트</h3>
<p>사용자 라우트가 준비됐으니 이제 이벤트 라우트를 구현해야한다.</p>
<p>routes 폴더의 events.py를 열어서 다음과 같이 의존 라이브러리를 임포트하고 이벤트 라우트를 정의한다.</p>
<pre><code class="language-python">from fastapi import APIRouter, Body, HTTPException, status
from models.events import Event
from typing import List

event_router = APIRouter(
    tags = [&quot;Events&quot;]
)

events = []</code></pre>
<p><strong>모든 이벤트를 추출</strong>하거나 <strong>특정 ID의 이벤트만 추출</strong>하는 라우트를 정의한다.</p>
<pre><code class="language-python">@event_router.get(&quot;/&quot;, response_model=Event)
async def retrieve_all_events() -&gt; List[Event]:
    if len(events)==0: 
        raise HTTPException(
        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
        detail=&quot;No events found&quot;
    )
    return events

@event_router.get(&quot;/{id}&quot;, response_model=Event)
async def retrieve_event(id: int) -&gt; Event:
    for event in events:
        if event.id == id:
            return event

    raise HTTPException(
        status_code=status.HTTP_404_NOT_FOUND,
        detail=&quot;Event with supplied ID does not exist&quot;
    )</code></pre>
<p>이제 <strong>이벤트 생성 및 삭제 라우트</strong>를 정의하자.</p>
<p><strong>첫 번째 라우트</strong>는 <strong>이벤트 생성</strong>, 
<strong>두 번째</strong>는 <strong>데이터베이스에 있는 단일 이벤트 삭제</strong>, 
<strong>세 번째</strong>는 <strong>전체 이벤트 삭제</strong>이다.</p>
<pre><code class="language-python">@event_router.post(&quot;/new&quot;)
async def create_event(body: Event = Body(...)) -&gt; dict:
    events.append(body)
    return {
        &quot;message&quot;:&quot;Event created successfully&quot;
    }

@event_router.delete(&quot;/{id}&quot;)
async def delete_event(id: int) -&gt; dict:
    for event in events:
        if event.id == id:
            events.remove(event)
            return {
                &quot;message&quot; : &quot;Event deleted successfully&quot;
            }

    raise HTTPException(
        status_code=status.HTTP_404_NOT_FOUND,
        detail=&quot;Event with supplied ID does not exist&quot;
    )

@event_router.delete(&quot;/&quot;)
async def delete_all_events() -&gt; dict:
    events.clear()
    return {
        &quot;message&quot;:&quot;Events deleted successfully.&quot;
    }</code></pre>
<p>이제 <code>main.py</code>의 라우트 설정을 변경해서 이벤트 라우트를 추가하자.</p>
<pre><code class="language-python">from fastapi import FastAPI
from routes.users import user_router
from routes.events import event_router # event 라우트

import uvicorn

# FastAPI() 인스턴스를 만들고 앞서 정의한 라우트 등록
app = FastAPI()

# 라우트 등록
app.include_router(user_router, prefix=&quot;/user&quot;)
app.include_router(event_router, prefix=&quot;/event&quot;) # event 라우트 등록

if __name__==&quot;__main__&quot;:
    # uvicorn.run() 메서드를 사용해 8000번 포트에서 애플리케이션을 실행하도록 설정
    uvicorn.run(&quot;main:app&quot;, host=&quot;127.0.0.1&quot;, port=8000, reload=True)</code></pre>
<h3 id="테스트-1">테스트</h3>
<p>이제 이벤트 라우트도 테스트해보자.</p>
<ul>
<li>GET 라우트 : 다음 명령을 실행하여 이벤트를 조회해보자.
```
curl -X &#39;GET&#39; <br>&#39;<a href="http://127.0.0.1:8000/event/&#39;">http://127.0.0.1:8000/event/&#39;</a> \</li>
<li>H &#39;accept: application/json&#39;<pre><code></code></pre></li>
</ul>
<p>이벤트가 없기에, 500 에러가 발생하고 아래와 같은 출력을 확인할 수 있다.</p>
<pre><code>{&quot;detail&quot;:&quot;No events found&quot;}%                                     </code></pre><ul>
<li>POST 라우트 : 다음 명령을 실행하여 _<strong>이벤트를 추가</strong>_해보자.
```
curl -X &#39;POST&#39; <br>&#39;<a href="http://127.0.0.1:8000/event/new&#39;">http://127.0.0.1:8000/event/new&#39;</a> \</li>
<li>H &#39;accept: application/json&#39; \</li>
<li>H &#39;Content-Type: application/json&#39; \</li>
<li>d &#39;{
&quot;id&quot;:1,
&quot;title&quot;:&quot;FastAPI Book Launch&quot;,
&quot;image&quot;:&quot;<a href="https://linktomyimage.com/image.png&quot;">https://linktomyimage.com/image.png&quot;</a>,
&quot;description&quot;: &quot;We will be discussing the contents of the FastAPI book in this event.Ensure to come with your own copy to win gifts!&quot;,
&quot;tags&quot;:[&quot;python&quot;, &quot;fastapi&quot;, &quot;book&quot;, &quot;launch&quot;],
&quot;location&quot;: &quot;Google Meet&quot;
}&#39;<pre><code></code></pre></li>
</ul>
<h4 id="결과-3">(결과)</h4>
<pre><code>{&quot;message&quot;:&quot;Event created successfully&quot;}%   </code></pre><ul>
<li>GET 라우트 : 다음 명령을 실행하여 생성한 이벤트를 추출해보자.
```
curl -X &#39;GET&#39; <br>&#39;<a href="http://127.0.0.1:8000/event/1&#39;">http://127.0.0.1:8000/event/1&#39;</a> \</li>
<li>H &#39;accept: application/json&#39;<pre><code></code></pre></li>
</ul>
<h4 id="결과-4">(결과)</h4>
<p>앞서 추가한 이벤트가 그대로 표시된다.</p>
<pre><code>{
  &quot;id&quot;:1,
  &quot;title&quot;:&quot;FastAPI Book Launch&quot;,
  &quot;image&quot;:&quot;https://linktomyimage.com/image.png&quot;,
  &quot;description&quot;:&quot;We will be discussing the contents of the FastAPI book in this event.Ensure to come with your own copy to win gifts!&quot;,
  &quot;tags&quot;:[&quot;python&quot;,&quot;fastapi&quot;,&quot;book&quot;,&quot;launch&quot;],
  &quot;location&quot;:&quot;Google Meet&quot;
}%  
</code></pre><ul>
<li>DELETE 라우트 : 다음 명령어를 실행하여 이벤트를 삭제해보자.
```
curl -X &#39;DELETE&#39; <br>&#39;<a href="http://127.0.0.1:8000/event/1&#39;">http://127.0.0.1:8000/event/1&#39;</a> \</li>
<li>H &#39;accept: application/json&#39;<pre><code></code></pre></li>
</ul>
<h4 id="결과-5">(결과)</h4>
<pre><code>{&quot;message&quot;:&quot;Event deleted successfully&quot;}%                             </code></pre><p>동일한 삭제 명령을 다시 실행해보자.</p>
<pre><code>{&quot;detail&quot;:&quot;Event with supplied ID does not exist&quot;}%   </code></pre><p>해당 ID의 이벤트가 존재하지 않기 때문문에 <code>detail</code>이 출력된다.</p>
<p>여기까지 이벤트 플래너 애플리케이션의 라우트와 모델을 성공적으로 구현했으며, 테스트를 통해 제대로 실행되는지도 확인했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[구조화(1)]]></title>
            <link>https://velog.io/@dev_junseok_22/%EA%B5%AC%EC%A1%B0%ED%99%941</link>
            <guid>https://velog.io/@dev_junseok_22/%EA%B5%AC%EC%A1%B0%ED%99%941</guid>
            <pubDate>Thu, 09 May 2024 22:02:56 GMT</pubDate>
            <description><![CDATA[<h1 id="fastapi-애플리케이션-구조화">FastAPI 애플리케이션 구조화</h1>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/6d35c311-3d15-4833-add0-1884668fc461/image.png" alt=""></p>
<ul>
<li><p>database 폴더</p>
<ul>
<li>connection.py : 데이터베이스 추상화와 설정에 사용하는 파일</li>
</ul>
</li>
<li><p>routes 폴더</p>
<ul>
<li>events.py : 이벤트 생성, 변경, 삭제 등의 처리를 위한 라우팅</li>
<li>users.py : 사용자 등록 및 로그인 처리를 위한 라우팅</li>
</ul>
</li>
<li><p>models 폴더</p>
<ul>
<li>events.py : 이벤트 처리용 모델을 정의</li>
<li>users.py : 사용자 처리용 모델을 정의</li>
</ul>
</li>
</ul>
<h2 id="이벤트-플래너-애플리케이션-개발">이벤트 플래너 애플리케이션 개발</h2>
<p>등록된 사용자는 이벤트를 추가, 변경, 삭제할 수 있으며 애플리케이션이 자동으로 만든 이벤트 페이지에서 생성된 이벤트를 확인할 수 있다.</p>
<p>등록된 사용자와 이벤트는 모두 고유한 ID를 가지며, 이를 통해 사용자와 이벤트가 중복되는 것을 방지할 수 있다.</p>
<p>먼저 가상환경을 활성화 하자</p>
<pre><code>python3 -m venv venv
source venv/bin/activate</code></pre><p>필요한 라이브러리도 설치하고</p>
<pre><code>pip install fastapi uvicorn &quot;pydantic[email]&quot;</code></pre><p>필요한 라이브러리를 requirements.txt에 저장하자</p>
<pre><code>pip freeze &gt; requirements.txt</code></pre><p>이제 필요한 라이브러리가 포함된 개발 환경이 모두 준비됐다. 이제 애플리케이션 모델을 구현해보자</p>
<h3 id="모델-구현">모델 구현</h3>
<p>모델 구현은 이벤트 모델과 사용자 모델의 정의부터 시작한다. 이 모델들은 데이터가 어떤 방식으로 입력 및 저장되고 애플리케이션에 표현되는지를 정의한다.</p>
<h4 id="이벤트-모델">이벤트 모델</h4>
<p>이벤트 모델은 <code>models</code> 폴더의 <code>events.py</code>에 정의한다.</p>
<pre><code class="language-python">from pydantic import BaseModel
from typing import List

class Event(BaseModel):
    id: int
    title: str
    image: str
    description: str
    tags: List[str]
    location: str</code></pre>
<ul>
<li>id : 자동 생성되는 고유 식별자</li>
<li>title : 이벤트 타이틀</li>
<li>image : 이벤트 이미지 배너의 링크</li>
<li>description : 이벤트 설명</li>
<li>tags : 그룹화를 위한 이벤트 태그</li>
<li>location : 이벤트 위치</li>
</ul>
<p>Event 클래스 안에 Config 서브 클래스를 추가한다. 이 클래서는 문서화할 때 샘플 데이터를 보여주기 위한 용도이다.</p>
<pre><code class="language-python">    class Config:
        schema_extra = {
            &quot;example&quot; : {
                &quot;title&quot; : &quot;FastAPI Book Launch&quot;,
                &quot;image&quot; : &quot;https://linktomyimage.com/image.png&quot;,
                &quot;description&quot; : &quot;We will be discussing the contents of the FastAPI book in this event. Ensure to come with your own copy to win gifts!&quot;,
                &quot;tags&quot; : [&quot;python&quot;, &quot;fastapi&quot;, &quot;book&quot;, &quot;launch&quot;],
                &quot;location&quot; : &quot;Google Meet&quot;
            }
        }
</code></pre>
<p>이 코드는 샘플 데이터를 정의하며, API를 통해 신규 이벤트를 생성할 때 참고할 수 있다.</p>
<h4 id="사용자-모델">사용자 모델</h4>
<p>사용자 모델(User)을 <code>models</code> 폴더의 <code>users.py</code> 파일에 정의한다.</p>
<pre><code class="language-python">from pydantic import BaseModel, EmailStr
from typing import Optional, List
from models.events import Event

class User(BaseModel):
    email: EmailStr
    password: str
    events: Optional[List[Event]]
</code></pre>
<ul>
<li>email : 사용자 이메일</li>
<li>password : 사용자 패스워드</li>
<li>events : 해당 사용자가 생성한 이벤트, 처음에는 비어있다.</li>
</ul>
<p>데이터를 어떻게 저장하고 설정하는지 보여주는 샘플 데이터를 만든다.</p>
<pre><code class="language-python">    class Config:
        schema_extra = {
            &quot;example&quot; : {
                &quot;email&quot; : &quot;fastapi@packt.com&quot;,
                &quot;passward&quot; : &quot;strong!!!&quot;,
                &quot;events&quot; : [],
            }
        }</code></pre>
<p>마지막으로 사용자 로그인 모델(UserSignIn)을 만든다.</p>
<pre><code class="language-python">class UserSignIn(BaseModel):
    email: EmailStr
    passward: str

    class Config:
        schema_extra = {
            &quot;example&quot; : {
                &quot;email&quot; : &quot;fastapi@packt.com&quot;,
                &quot;passward&quot; : &quot;strong!!!&quot;,
                &quot;events&quot; : [],
            }
        }</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[FastAPI에서 Jinja를 사용하는 방법]]></title>
            <link>https://velog.io/@dev_junseok_22/FastAPI%EC%97%90%EC%84%9C-Jinja%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@dev_junseok_22/FastAPI%EC%97%90%EC%84%9C-Jinja%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 07 May 2024 13:32:09 GMT</pubDate>
            <description><![CDATA[<p>Jinja를 사용하려면 Jinja2 패키지를 설치하고 기존 작업 디렉터리에 templates이라는 신규 폴더를 만들어야 한다.</p>
<p>이 폴더에 모든 Jinja 관련 파일이 저장된다.</p>
<p>Jinja2 관련 패키지들을 먼저 설치하자</p>
<pre><code>pip3 install jinja2 python-multipart</code></pre><p><code>templates</code> 폴더를 생성하자</p>
<pre><code>mkdir templates</code></pre><p>이제 templates 폴더 안에 <code>home.html</code>,<code>todo.html</code> 이라는 두 개의 파일을 만든다.</p>
<pre><code>cd templates
touch {home,todo}.html</code></pre><ul>
<li><code>home.html</code> : 애플리케이션 홈 페이지용</li>
<li><code>todo.html</code> : todo 페이지용</li>
</ul>
<p>이제 FastAPI 애플리케이션이 Jinja를 사용하도록 설정해보자</p>
<h4 id="1-todo-api-컴포넌트todopy의-post-라우트-수정">1. todo API 컴포넌트(todo.py)의 POST 라우트 수정</h4>
<pre><code class="language-python">from fastapi import APIRouter, Path, HTTPException, status, Request, Depends
from fastapi.templating import Jinja2Templates
from model import Todo, TodoItem, TodoItems

todo_router = APIRouter()

todo_list = []

templates = Jinja2Templates(directory=&quot;templates/&quot;)

@todo_router.post(&quot;/todo&quot;)
async def add_todo(request: Request, todo: Todo = Depends(Todo.as_form)):
    todo.id = len(todo_list) + 1
    todo_list.append(todo)
    return templates.TemplateResponse(&quot;todo.html&quot;,
                                      {
                                          &quot;request&quot;:request,
                                          &quot;todos&quot;:todo_list
                                      })
</code></pre>
<h4 id="2-get-라우트-수정">2. GET 라우트 수정</h4>
<pre><code class="language-python">@todo_router.get(&quot;/todo&quot;, response_model = TodoItems)
async def retreive_todos(request:Request):
    return templates.TemplateResponse(&quot;todo.html&quot;,
                                    {
                                        &quot;request&quot;:request,
                                        &quot;todos&quot;:todo_list
                                    })

@todo_router.get(&quot;/todo/{todo_id}&quot;)
async def get_single_todo(request:Request, todo_id: int =Path(..., title=&quot;The ID of the todo to retrieve&quot;)) -&gt; dict:
    for todo in todo_list:
        if todo.id == todo_id:
            return templates.TemplateResponse(&quot;todo.html&quot;,
                                {
                                    &quot;request&quot;:request,
                                    &quot;todos&quot;:todo_list
                                })
    raise HTTPException(
        status_code=status.HTTP_404_NOT_FOUND,
        detail=&quot;Todo with supplied ID doesn&#39;t exist&quot;
    )</code></pre>
<p>이 코드는 Jinja가 template 폴더를 참조해서 그 안에 있는 특정 템플릿을 사용하도록 지정한다. 템플릿은 <code>templates.TemplateResponse()</code> 메서드를 통해 전달된다.
(todo를 추가하는 POST 메서드는 의존성을 사용해서 입력값을 전달한다.)</p>
<h4 id="3-modelpy의-config-서브-클래스-추가">3. model.py의 Config 서브 클래스 추가</h4>
<pre><code class="language-python">from pydantic import BaseModel
from typing import List, Optional
from fastapi import Form

class Item(BaseModel):
    item: str
    status: str

class Todo(BaseModel):
    id: Optional[int] = None
    item: str

    @classmethod
    def as_form(
        cls,
        item: str = Form(...)
    ):
        return cls(item=item)
</code></pre>
<h4 id="4-homehtml-파일의-문서-유형-선언">4. home.html 파일의 문서 유형 선언</h4>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta content=&quot;IE=edge&quot; http-equiv=&quot;X-UA-Compatible&quot;&gt;
    &lt;meta content=&quot;width=device-width, initial-scale=1.0&quot; name=&quot;viewport&quot;&gt;
    &lt;title&gt;Packt Todo Application&lt;/title&gt;
    &lt;link crossorigin=&quot;anonymous&quot; href=&quot;https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css&quot;
          integrity=&quot;sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4&quot; rel=&quot;stylesheet&quot;&gt;
    &lt;link crossorigin=&quot;anonymous&quot; href=&quot;https://use.fontawesome.com/releases/v5.0.10/css/all.css&quot;
          integrity=&quot;sha384-+d0P83n9kaQMCwj8F4RJB66tzIwOKmrdb46+porD/OvrJ+37WqIM7UoBtwHO6Nlg&quot; rel=&quot;stylesheet&quot;&gt;
&lt;/head&gt;</code></pre>
<h4 id="5-템플릿-바디를-작성">5. 템플릿 바디를 작성</h4>
<p><code>container-fluid</code> Class에 <code>block</code> 태그를 사용해 todo_container를 추가한다.</p>
<pre><code>&lt;body&gt;
&lt;header&gt;
    &lt;nav class=&quot;navar&quot;&gt;
        &lt;div class=&quot;container-fluid&quot;&gt;
            &lt;center&gt;
                &lt;h1&gt;Packt Todo Application&lt;/h1&gt;
            &lt;/center&gt;
        &lt;/div&gt;
    &lt;/nav&gt;
&lt;/header&gt;
&lt;div class=&quot;container-fluid&quot;&gt;
    {% block todo_container %}{% endblock %}
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="6-todohtml을-열어서-다음과-같은-템플릿을-작성">6. todo.html을 열어서 다음과 같은 템플릿을 작성</h4>
<pre><code>{% extends &quot;home.html&quot; %}

{% block todo_container %}
&lt;main class=&quot;container&quot;&gt;
    &lt;hr&gt;
    &lt;section class=&quot;container-fluid&quot;&gt;
        &lt;form method=&quot;post&quot;&gt;
            &lt;div class=&quot;col-auto&quot;&gt;
                &lt;div class=&quot;input-group mb-3&quot;&gt;
                    &lt;input aria-describedby=&quot;button-addon2&quot; aria-label=&quot;Add a todo&quot; class=&quot;form-control&quot; name=&quot;item&quot;
                           placeholder=&quot;Purchase Packt&#39;s Python workshop course&quot; type=&quot;text&quot;
                           value=&quot;{{ item }}&quot;/&gt;
                    &lt;button class=&quot;btn btn-outline-primary&quot; data-mdb-ripple-color=&quot;dark&quot; id=&quot;button-addon2&quot;
                            type=&quot;submit&quot;&gt;
                        Add Todo
                    &lt;/button&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/form&gt;
    &lt;/section&gt;
    {% if todo %}
    &lt;article class=&quot;card container-fluid&quot;&gt;
        &lt;br/&gt;
        &lt;h4&gt;Todo ID: {{ todo.id }} &lt;/h4&gt;
        &lt;p&gt;
            &lt;strong&gt;
                Item: {{ todo.item }}
            &lt;/strong&gt;
        &lt;/p&gt;
    &lt;/article&gt;
    {% else %}
    &lt;section class=&quot;container-fluid&quot;&gt;
        &lt;h2 align=&quot;center&quot;&gt;Todos&lt;/h2&gt;
        &lt;br&gt;
        &lt;div class=&quot;card&quot;&gt;
            &lt;ul class=&quot;list-group list-group-flush&quot;&gt;
                {% for todo in todos %}
                &lt;li class=&quot;list-group-item&quot;&gt;
                    {{ loop.index }}. &lt;a href=&quot;/todo/{{ loop.index }}&quot;&gt; {{ todo.item }} &lt;/a&gt;
                &lt;/li&gt;
                {% endfor %}
            &lt;/ul&gt;
        &lt;/div&gt;
        {% endif %}
    &lt;/section&gt;
&lt;/main&gt;
{% endblock %}</code></pre><p>todo 템플릿이 home 템플릿을 상속한다. 또한 todo_container 블록을 정의해서 부모 템플릿이 이 템플릿의 콘텐츠를 표시할 수 있게 한다.</p>
<p>todo 템플릿은 모든 todo를 추출하는 라우트와 단일 todo 추출하는 라우트 모두에서 사용된다. 결과적으로 라우트에 따라 다른 콘텐츠를 렌더링하게 되는 것이다.</p>
<p>이제 웹 브라우저에 들어가 변경 내용이 반영되었는지 확인해보자.</p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/e66a62e3-a344-4247-8bd6-91671b5eb627/image.png" alt=""></p>
<p>홈 페이지가 제대로 작동하는지 확인하기 위해 todo 아이템을 하나 추가해보자
<img src="https://velog.velcdn.com/images/dev_junseok_22/post/e156d1bb-a190-47a2-a2c5-d99992711e56/image.png" alt=""></p>
<p>잘 작동된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[오류 처리]]></title>
            <link>https://velog.io/@dev_junseok_22/%EC%98%A4%EB%A5%98-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@dev_junseok_22/%EC%98%A4%EB%A5%98-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Mon, 06 May 2024 11:46:30 GMT</pubDate>
            <description><![CDATA[<p>요청이 알 수 없는 오류 메시지를 그대로 노출하면 클라이언트 입장에서는 오류의 원인을 파악하기 어려울 수 있다. </p>
<p>존재하지 않는 리소스나 권한이 없는 페이지에 접근하는 경우 요청 시 오류가 발생하며 서버 자체에서 오류가 발생하기도 한다. </p>
<p>FastAPI에서 오류는 FastAPI의 <strong>HTTPException</strong> 클래스를 사용해 예외를 발생시켜 처리한다.</p>
<blockquote>
<p>HTTP 예외
HTTP 예외는 요청 흐름상에 발생하는 오류나 문제를 가리키는 이벤트</p>
</blockquote>
<p>HTTPException 클래스는 다음 세 개의 인수를 받는다</p>
<ul>
<li>status_code : 예외 처리 시 반환할 상태 코드</li>
<li>detail : 클라이언트에게 전달할 메시지</li>
<li>headers : 헤더를 요구하는 응답을 위한 선택적 인수</li>
</ul>
<p>존재하지 않는 todo를 추출하면 404가 아닌 200 응답이 반환된다.</p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/a7ecb8f8-b621-4bd8-9cdb-ec1a6c2ee223/image.png" alt=""></p>
<p>HTTPException 클래스를 사용해 라우트를 변경하면 적절한 응답 코드와 함께 상태 메시지도 반환할 수 있다. todo.py에서 추출, 변경, 삭제 라우트를 다음과 같이 변경하자.</p>
<pre><code class="language-python">from fastapi import APIRouter, Path, HTTPException, status

...

@todo_router.get(&quot;/todo/{todo_id}&quot;)
async def get_single_todo(todo_id: int =Path(..., title=&quot;The ID of the todo to retrieve&quot;)) -&gt; dict:
    for todo in todo_list:
        if todo.id == todo_id:
            return {
                &quot;todo&quot;:todo
            }
    raise HTTPException(
        status_code=status.HTTP_404_NOT_FOUND,
        detail=&quot;Todo with supplied ID doesn&#39;t exist&quot;
    )


@todo_router.put(&quot;/todo/{todo_id}&quot;)
async def update_todo(todo_data: TodoItem, todo_id: int = Path(..., title=&quot;The ID of the todo to be updated&quot;)) -&gt; dict:
    for todo in todo_list:
        if todo.id == todo_id:
            todo.item = todo_data.item
            return {
                &quot;message&quot; : &quot;Todo updated successfully&quot;
            }
    raise HTTPException(
        status_code=status.HTTP_404_NOT_FOUND,
        detail=&quot;Todo with supplied ID doesn&#39;t exist&quot;
    )

@todo_router.delete(&quot;/todo/{todo_id}&quot;)
async def delete_single_todo(todo_id: int) -&gt; dict:
    for index in range(len(todo_list)):
        todo = todo_list[index]
        if todo_id == todo_id:
            todo_list.pop(index)
            return {
                &quot;message&quot; : &quot;Todo deleted successfully&quot;
            }
    raise HTTPException(
        status_code=status.HTTP_404_NOT_FOUND,
        detail=&quot;Todo with supplied ID doesn&#39;t exist&quot;
    )

</code></pre>
<p>이제 다시 한번 존재하지않는 todo를 추출해주자.
<img src="https://velog.velcdn.com/images/dev_junseok_22/post/0b18a3ab-63d3-49cb-9d33-3fdf0a9380de/image.png" alt=""></p>
<p>마지막으로 데코레이터 함수에 status_code 인수를 추가해 기본 응답 코드(200)를 다른 코드로 변경해보자.</p>
<pre><code class="language-python">@todo_router.post(&quot;/todo&quot;, status_code=201)
async def add_todo(todo: Todo) -&gt; dict:
    todo_list.append(todo)
    return {
        &quot;message&quot;:&quot;Todo added successfully!&quot;
    }</code></pre>
<p>참고로 요청 성공을 의미하는 기본 상태 코드는 <strong>200</strong>이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[응답 모델 작성]]></title>
            <link>https://velog.io/@dev_junseok_22/%EC%9D%91%EB%8B%B5-%EB%AA%A8%EB%8D%B8-%EC%9E%91%EC%84%B1</link>
            <guid>https://velog.io/@dev_junseok_22/%EC%9D%91%EB%8B%B5-%EB%AA%A8%EB%8D%B8-%EC%9E%91%EC%84%B1</guid>
            <pubDate>Mon, 06 May 2024 11:28:13 GMT</pubDate>
            <description><![CDATA[<h1 id="응답-모델-작성">응답 모델 작성</h1>
<h4 id="라우트-경로-정의">라우트 경로 정의</h4>
<pre><code class="language-python">@app.get(&quot;/todo&quot;)
async def retreive_todo() -&gt; dict:
    &#39;&#39;&#39;

    현재 DB에 있는 모든 todo 아이템의 목록을 반환
    &#39;&#39;&#39;
    return {
        &quot;todos&quot; : todo_list
    }</code></pre>
<p>모든 todo를 추출해서 배열로 반환하는 라우트를 ID없이 todo 아이템만 반환하도록 변경하기 위해서 새로운 모델을 추가한다.</p>
<pre><code class="language-python">from typing import List

...

class TodoItems(BaseModel):
    todo: List[TodoItem]

    class Config:
        schema_extra = {
            &quot;example&quot;: {
                &quot;todos&quot; : [
                    {
                        &quot;item&quot; : &quot;Example schema 1&quot;
                    },
                    {
                        &quot;item&quot; : &quot;Example schema 2&quot;
                    },
                ]
            }
        }
</code></pre>
<p>이제 todo.py에 있는 라우트에 다음과 같은 응답 모델을 추가해보자.</p>
<pre><code class="language-python">from model import Todo, TodoItem, TodoItems

@todo_router.get(&quot;/todo&quot;, resposn_model = TodoItems)
async def retreive_todos() -&gt; dict:
    return {
        &quot;todos&quot;:todo_list
    }</code></pre>
<p>애플리케이션 실행</p>
<pre><code>uvicorn main:app --host=127.0.0.1 --port 8000 --reload</code></pre><p>이제 새로운 todo를 추가해주자.</p>
<pre><code>curl -X &#39;POST&#39; \
&#39;http://127.0.0.1:8000/todo&#39; \
-H &#39;accept: application/json&#39; \
-H &#39;Content-Type: application/json&#39; \
-d &#39;{
&quot;id&quot;: 1,
&quot;item&quot;: &quot;This todo will be retrieved without exposing my ID!&quot;
}&#39;</code></pre><p>todo를 추출해보자</p>
<pre><code>curl -X &#39;GET&#39; \
&#39;http://127.0.0.1:8000/todo&#39; \
-H &#39;accept: application/json&#39;</code></pre><h4 id="결과">결과</h4>
<hr>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/91bb594c-bcaf-43b9-b1e7-6ecb4a2cb178/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[응답 모델과 오류 처리]]></title>
            <link>https://velog.io/@dev_junseok_22/%EC%9D%91%EB%8B%B5-%EB%AA%A8%EB%8D%B8%EA%B3%BC-%EC%98%A4%EB%A5%98-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@dev_junseok_22/%EC%9D%91%EB%8B%B5-%EB%AA%A8%EB%8D%B8%EA%B3%BC-%EC%98%A4%EB%A5%98-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Mon, 06 May 2024 11:08:26 GMT</pubDate>
            <description><![CDATA[<h4 id="응답-모델">응답 모델</h4>
<p>API 라우트 경로가 반환하는 데이터의 템플릿 역할을 하며, 서버에 전달된 요청을 기준으로 적절한 응답을 렌더링 하기 위해 pydantic을 사용한다.</p>
<h4 id="오류-처리">오류 처리</h4>
<p>애플리케이션에서 발생하는 오류를 처리하는 로직과 방법으로, 오류 처리에는 적절한 오류 상태 코드와 오류 메시지가 포함된다.</p>
<h1 id="fastapi의-응답">FastAPI의 응답</h1>
<p>응답은 API 처리 과정의 한 부분으로, HTTP 메서드를 통해 API와 상호 작용하며 API로부터 받은 결과를 가리킨다. API 응답은 JSON, XML, 문서 형식이며 <strong>헤더</strong>와 <strong>바디</strong>로 구성된다.</p>
<h4 id="응답-헤더">응답 헤더</h4>
<p>요청 상태 및 응답 바디 전달을 안내하는 정보로 구성되며, 대표적으로는 반환하는 콘텐츠 유형이 무엇인지 클리아언트에게 알려주는 역할을 함</p>
<h4 id="응답-바디">응답 바디</h4>
<p>응답 바디는 서버가 클라이언트에게 반환하는 데이터로, 대표적인 예로 application/json이 있다. (todo_list가 응답바디에 해당한다.)</p>
<h4 id="상태-코드">상태 코드</h4>
<p>서버가 반환한 응답에 포함되는 짧은 고유 코드로, 클라이언트가 보낸 요청의 상태를 나타낸다.</p>
<ul>
<li>1XX : 요청을 받았다.</li>
<li>2XX : 요청을 성공적으로 처리했다. (ex. 200)</li>
<li>3XX : 요청을 리다이렉트했다. </li>
<li>4XX : 클라이언트 측에 오류가 있다. (ex. 404)</li>
<li>5XX : 서버 측에 오류가 있다. (ex. 500)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[간단한 CRUD 애플리케이션 개발]]></title>
            <link>https://velog.io/@dev_junseok_22/%EA%B0%84%EB%8B%A8%ED%95%9C-CRUD-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EA%B0%9C%EB%B0%9C</link>
            <guid>https://velog.io/@dev_junseok_22/%EA%B0%84%EB%8B%A8%ED%95%9C-CRUD-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EA%B0%9C%EB%B0%9C</guid>
            <pubDate>Thu, 02 May 2024 13:12:54 GMT</pubDate>
            <description><![CDATA[<p>기존 아이템을 변경 혹은 삭제하는 라우트</p>
<h4 id="update-라우트의-요청-바디용-모델">UPDATE 라우트의 요청 바디용 모델</h4>
<pre><code class="language-python">class TodoItem(BaseModel):
    item: str

    class Config:
        schema_extra = {
            &quot;example&quot;: {
            &quot;item&quot;: &quot;Read the next chapter of the book.&quot;
            }
        }</code></pre>
<p>todo 변경을 위해 todo.py에 추가</p>
<pre><code class="language-python">from model import Todo, TodoItem

...

@todo_router.put(&quot;/todo/{todo_id}&quot;)
async def update_todo(todo_data: TodoItem, todo_id: int = Path(..., title=&quot;The ID of the todo to be updated&quot;)) -&gt; dict:
    for todo in todo_list:
        if todo.id == todo_id:
            todo.item = todo_data.item
            return {
                &quot;message&quot; : &quot;Todo updated successfully&quot;
            }

    return {
        &quot;message&quot; : &quot;Todo with supplied ID doesn&#39;t exist&quot;
    }
</code></pre>
<p>새로 추가한 라우트를 테스트하자.</p>
<pre><code>curl -X &#39;POST&#39; \
&#39;http://127.0.0.1:8000/todo&#39; \
-H &#39;accept: application/json&#39; \
-H &#39;Content-Type: application/json&#39; \
-d &#39;{&quot;id&quot;:1, &quot;item&quot;:&quot;Example Schema!&quot;}&#39;</code></pre><p>잘 적용된거같다.</p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/0c8256bd-6112-4012-a2f9-0f01da704737/image.png" alt=""></p>
<p>이제 <strong>PUT</strong> 요청을 보대서 추가한 아이템을 수정해보자</p>
<pre><code>curl -X &#39;PUT&#39; \
&#39;http://127.0.0.1:8000/todo/1&#39; \
-H &#39;accept: application/json&#39; \
-H &#39;Content-Type: application/json&#39; \
-d &#39;{&quot;item&quot;:&quot;Read the next chapter of the book&quot;}&#39;</code></pre><p>PUT 요청에 의해 UPDATE 라우트가 정상 작동했다.</p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/63b414f6-fd78-4808-9b3c-d3e25667c518/image.png" alt=""></p>
<h4 id="delete-라우트의-요청-바디용-모델">DELETE 라우트의 요청 바디용 모델</h4>
<pre><code class="language-python">@todo_router.delete(&quot;/todo/{todo_id}&quot;)
async def delete_single_todo(todo_id: int) -&gt; dict:
    for index in range(len(todo_list)):
        todo = todo_list[index]
        if todo_id == todo_id:
            todo_list.pop(index)
            return {
                &quot;message&quot; : &quot;Todo deleted successfully&quot;
            }
    return {
        &quot;message&quot; : &quot;Todo with supplied ID doesn&#39;t exist&quot;
    }

@todo_router.delete(&quot;/todo&quot;)
async def delete_all_todo() -&gt; dict:
    todo_list.clear()
    return {
        &quot;message&quot; : &quot;Todo deleted successfully&quot;
    }</code></pre>
<p>이제 테스트 해보자.</p>
<p>먼저 신규 todo 아이템을 넣어주자</p>
<pre><code>curl -X &#39;POST&#39; \
&#39;http://127.0.0.1:8000/todo&#39; \
-H &#39;accept: application/json&#39; \
-H &#39;Content-Type: application/json&#39; \
-d &#39;{&quot;id&quot;:1, &quot;item&quot;:&quot;Example Schema!&quot;}&#39;</code></pre><p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/1600edf1-f9fc-4f27-bb81-99c95814906f/image.png" alt=""></p>
<p>이제 DELETE 시켜보자.</p>
<pre><code>curl -X &#39;DELETE&#39; \
&#39;http://127.0.0.1:8000/todo/1&#39; \
-H &#39;accept: application/json&#39;</code></pre><p>삭제됐다!</p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/c21e009b-78db-4323-88f8-d3133ab898b1/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[요청 바디]]></title>
            <link>https://velog.io/@dev_junseok_22/%EC%9A%94%EC%B2%AD-%EB%B0%94%EB%94%94</link>
            <guid>https://velog.io/@dev_junseok_22/%EC%9A%94%EC%B2%AD-%EB%B0%94%EB%94%94</guid>
            <pubDate>Thu, 02 May 2024 12:58:00 GMT</pubDate>
            <description><![CDATA[<h1 id="요청-바디">요청 바디</h1>
<p>요청 바디란 POST와 UPDATE 등 라우팅 메서드를 사용해 <strong>API로 전달되는 데이터</strong>다.</p>
<pre><code>curl -X &#39;POST&#39; &#39;http://127.0.0.1:8000/todo&#39; \
-H &#39;accept: application/json&#39; \
-H &#39;Content-Type: application/json&#39; \
-d &#39;{&quot;id&quot;:2, &quot;item&quot;:&quot;Validation&quot;}&#39;</code></pre><p>에서 요청 바디는</p>
<pre><code>{&quot;id&quot;:2, &quot;item&quot;:&quot;Validation&quot;}</code></pre><p>이다.</p>
<p>FastAPI는 추가 검증할 수 있는 Body 클래스를 제공한다.</p>
<p>모델은 API 라우트와 요청 바디의 유형을 자동으로 문서화할 때도 사용된다. </p>
<h1 id="fastapi-자동-문서화">FastAPI 자동 문서화</h1>
<ul>
<li>스웨거</li>
<li>ReDoc</li>
</ul>
<p>FastAPI는 다음 총 2가지의 문서 유형을 제공하는데,
스웨거는 앞서 설명했던, <code>http://127.0.0.1:8000/docs</code>이다.</p>
<p>ReDoc은 모델, 라우트, API에 관한 정보를 더 직관적이로 상세하게 전달한다.
<code>http://127.0.0.1:8000/redoc</code></p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/bc931247-d789-4b32-b454-f3ea1fb013ad/image.png" alt=""></p>
<p>샘플 데이터를 추가해보자.</p>
<pre><code class="language-python">class Todo(BaseModel):
    id: int
    item: str

    class Config:
        schema_extra = {
            &quot;example&quot;: {
                &quot;id&quot;:1,
                &quot;item&quot;: &quot;Exmple Schema!&quot;
            }
        }</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[경로 매개변수와 쿼리 매개변수]]></title>
            <link>https://velog.io/@dev_junseok_22/%EA%B2%BD%EB%A1%9C-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EC%99%80-%EC%BF%BC%EB%A6%AC-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98</link>
            <guid>https://velog.io/@dev_junseok_22/%EA%B2%BD%EB%A1%9C-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EC%99%80-%EC%BF%BC%EB%A6%AC-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98</guid>
            <pubDate>Thu, 02 May 2024 12:38:42 GMT</pubDate>
            <description><![CDATA[<h1 id="경로-매개변수">경로 매개변수</h1>
<p>경로 매개변수는 리소스를 식별하기 위해 API 라우팅에 사용된다. 이 매개변수는 식벽자 역할을 하며 웹 애플리케이션이 추가 처리를 할 수 있도록 연결 고리가 되도록 한다.</p>
<p>앞서 만든 todo_list에서 하나의 todo 작업만 할 수 있는 라우트를 만들자
예제에서는 경로 매개변수가 <code>todo_id</code>가 된다.</p>
<pre><code class="language-python">@todo_router.get(&quot;/todo/{todo_id}&quot;)
async def get_single_todo(todo_id: int) -&gt; dict:
    for todo in todo_list:
        if todo.id == todo_id:
            return {
                &quot;todo&quot;:todo
            }
    return {
        &quot;message&quot;:&quot;Todo with supplied ID doesn&#39;t exist&quot;
    }</code></pre>
<p>추가한 라우트를 테스트 해보자. (리액트에서 id 3을 출력하도록 코딩했다)</p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/aa05e902-fd62-4967-820c-ad6abd742f99/image.png" alt=""></p>
<p>잘된다.</p>
<p>이제 <strong>Path</strong> 클래스를 추가해보자. Path는 FastAPI가 제공하는 클래스로, 라우트 함수에 있는 다른 인수와 경로 매개변수를 구분하는 역할을 한다. Path 클래스는 스웨거와 ReDoc 등으로 OpenAPI 기반 문서를 자동 생성할 때 라우트 관련 정보를 함께 문서화하도록 돕는다.</p>
<pre><code class="language-python">from fastapi import APIRouter, Path

...


async def get_single_todo(todo_id: int =Path(..., title=&quot;The ID of the todo to retrieve&quot;)) -&gt; dict:</code></pre>
<blockquote>
<p>Path(..., kargs)
Path 클래스는 첫 인수로 None 또는 ... 을 받을 수 있는데, 첫 인수가 ...이면 경로 매개변수를 반드시 지정해야한다. gt, le와 같은 검증 기호를 사용해, 경로 매개변수에 사용된 값이 특정 범위에 있는 숫자인지 검증도 가능하다.</p>
</blockquote>
<h1 id="쿼리-매개변수">쿼리 매개변수</h1>
<p>쿼리 매개변수는 선택 사항이며 보통 URL에서 ? 뒤에 온다. 제공된 쿼리를 기반으로 특정한 값을 반환하거나 요청을 필터링할 때 사용된다.</p>
<p>예시를 보자</p>
<pre><code class="language-python">async query_route(query: str = Query(None)):
    return query</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[요청 바디 검증하기]]></title>
            <link>https://velog.io/@dev_junseok_22/%EC%9A%94%EC%B2%AD-%EB%B0%94%EB%94%94-%EA%B2%80%EC%A6%9D%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dev_junseok_22/%EC%9A%94%EC%B2%AD-%EB%B0%94%EB%94%94-%EA%B2%80%EC%A6%9D%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 02 May 2024 12:22:26 GMT</pubDate>
            <description><![CDATA[<h1 id="pydantic-모델을-사용한-요청-바디-검증하기">pydantic 모델을 사용한 요청 바디 검증하기</h1>
<p>FastAPI에서는 정의된 데이터만 전송되도록 요청 바디를 검증할 수 있다. 이는 요총 데이터가 적절한지 확인하고 악의적인 공격의 위험을 줄여줄 수 있기에 매우매우 중요하다.</p>
<blockquote>
<p>pydantic?
파이썬의 타입 어노테이션을 사용해 데이터를 검증하는 파이썬 라이브러리</p>
</blockquote>
<p>FastAPI에서 모델은 데이터가 어떻게 전달되고 처리돼야 하는지를 정의하는 구조화된 클래스로, 모델은 pydantic의 BaseModel 클래스의 하위 클래스로 생성된다.</p>
<p>모델은 요청 바디 객체와 요청 응답 객체의 유형에 관한 힌트를 제공한다.</p>
<h4 id="pydantic을-사용해-요청-바디를-검증하는-부분">pydantic을 사용해 요청 바디를 검증하는 부분</h4>
<pre><code class="language-python">from pydantic import BaseModel

class Todo(BaseModel):
    id: int
    item: str</code></pre>
<p>이 모델을 POST 라우트에 사용해보자</p>
<pre><code class="language-python">from fastapi import APIRouter
from model import Todo # todo.py 파일에 모델 임포트

todo_router = APIRouter()

todo_list = []

@todo_router.post(&quot;/todo&quot;)
async def add_todo(todo: Todo) -&gt; dict: #todo에 Todo 사용
    todo_list.append(todo)
    return {
        &quot;message&quot;:&quot;Todo added successfully!&quot;
    }

@todo_router.get(&quot;/todo&quot;)
async def retreive_todos() -&gt; dict:
    return {
        &quot;todos&quot;:todo_list
    }</code></pre>
<p>빈 딕셔너리를 요청 바디로 보내서 모델이 제대로 검증되는지 확인하자</p>
<pre><code>curl -X &#39;POST&#39; &#39;http://127.0.0.1:8000/todo&#39; \
-H &#39;accept: application/json&#39; \
-H &#39;Content-Type: application/json&#39; \
-d &#39;{}&#39;</code></pre><p>성공했다. </p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/45591279-318e-4352-9c79-832b91388105/image.png" alt=""></p>
<p>성공하면...안되는데..?</p>
<p>모델과 일치하는 데이터를 보내보자</p>
<pre><code>curl -X &#39;POST&#39; &#39;http://127.0.0.1:8000/todo&#39; \
-H &#39;accept: application/json&#39; \
-H &#39;Content-Type: application/json&#39; \
-d &#39;{&quot;id&quot;:2, &quot;item&quot;:&quot;Validation&quot;}&#39;</code></pre><p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/6c83c4a0-7640-46a2-a059-820aca6e5e68/image.png" alt=""></p>
<h4 id="중첩-모델">중첩 모델</h4>
<p>pydantic 모델은 다음과 같이 중첩해서 정의할 수 있다.</p>
<pre><code class="language-python">from pydantic import BaseModel

class Item(BaseModel):
    item: str
    status: str

class Todo(BaseModel):
    id: int
    item: Item</code></pre>
<p>결과적으로 Todo형의 데이터는</p>
<pre><code class="language-json">{
  &quot;id&quot;: 1,
  &quot;item&quot;: {
    &quot;item&quot;: &quot;Nested models&quot;,
    &quot;status&quot;: &quot;completes&quot;
  }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[FastAPI 라우팅]]></title>
            <link>https://velog.io/@dev_junseok_22/FastAPI-%EB%9D%BC%EC%9A%B0%ED%8C%85</link>
            <guid>https://velog.io/@dev_junseok_22/FastAPI-%EB%9D%BC%EC%9A%B0%ED%8C%85</guid>
            <pubDate>Thu, 02 May 2024 12:00:53 GMT</pubDate>
            <description><![CDATA[<h1 id="fastapi-라우팅이란">FastAPI 라우팅이란?</h1>
<p>라우트는 HTTP 요청 메서드의 요청을 수락하고 선택적으로 인수를 받을 수 있도록 정의됨
요청이 특정 라우트로 전달되면 애플리케이션은 <strong>라우트 처리기(Route handler)</strong>가 요청을 처리하기 전에 해당 라우트가 정의되어 있는지 확인함
<strong>Route handler</strong>는 서버로 전송된 요청을 처리하는 함수로, Route handler의 예로는 요청을 받아 DB에서 특정 데이터를 추출하는 함수가 있음</p>
<blockquote>
<p>HTTP 요청 메서드
HTTP 메서드 처리 유형을 정의하는 식별자로, 표준 메서드에는 <strong>GET, POST, PUT, PATCH, DELETE</strong> 등이 있음</p>
</blockquote>
<h4 id="라우팅-예">라우팅 예</h4>
<ul>
<li>단일 라우트 애플리케이션 예시<pre><code class="language-python">from fastapi import FastAPI
</code></pre>
</li>
</ul>
<p>app = FastAPI()</p>
<p>@app.get(&quot;/&quot;)</p>
<p>async def welcom() -&gt; dict:
    return {
        &quot;message&quot;:&quot;Hello World&quot;
    }</p>
<pre><code>
# APIRouter 클래스를 사용한 라우팅
APIRouter 클래스는 다중 라우팅을 위한 경로 처리 클래스로 애플리케이션 라우팅과 로직을 독립적으로 구성하고 모듈화 할 수 있음

#### 예시
```python
from fastapi import APIRouter

router = APIRouter()

@router.get(&quot;/hello&quot;)
async def say_hello() -&gt; dict:
    return {
        &quot;message&quot;:&quot;Hello!&quot;
    }</code></pre><h4 id="참고-클라이언트와-다른-포트를-사용할-경우">(참고) 클라이언트와 다른 포트를 사용할 경우,</h4>
<p><strong>CORS 이슈</strong>: 클라이언트(리액트 앱)와 서버(FastAPI)가 다른 포트에서 실행되고 있을 경우, CORS(Cross-Origin Resource Sharing) 정책 때문에 문제가 발생할 수 있습니다. 서버에서 CORS를 허용하도록 설정해야 합니다. FastAPI에서는 fastapi.middleware.cors.CORSMiddleware를 사용하여 CORS를 설정할 수 있습니다. 다음은 CORS 설정 예시입니다:</p>
<pre><code class="language-python">from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=[&quot;http://localhost:3000&quot;],  # 리액트 앱의 URL
    allow_credentials=True,
    allow_methods=[&quot;*&quot;],
    allow_headers=[&quot;*&quot;],
)

@app.get(&quot;/&quot;)
async def read_root():
    return {&quot;Hello&quot;: &quot;World&quot;}
</code></pre>
<h2 id="새로운-라우트와-라우트-처리기-만들기">새로운 라우트와 라우트 처리기 만들기</h2>
<p>새로운 파일 생성</p>
<pre><code>touch todo.py</code></pre><p>다음은 APIRouter를 활용한 예시 코드이다.</p>
<pre><code class="language-python">from fastapi import APIRouter

todo_router = APIRouter()

todo_list = []

@todo_router.post(&quot;/todo&quot;)
async def add_todo(todo: dict) -&gt; dict:
    todo_list.append(todo)
    return {
        &quot;message&quot;:&quot;Todo added successfully!&quot;
    }

@todo_router.get(&quot;/todo&quot;)
async def retreive_todos() -&gt; dict:
    return {
        &quot;todos&quot;:todo_list
    } </code></pre>
<p>APIRouter는 FastAPI 클래스와 동일한 방식으로 작동하지만, 정의한 라우트를 FastAPI() 인스턴스에 추가해야 외부에서 접근 가능하다.</p>
<p>main.py 파일에 다음과 같이 todo.py 파일에서 생성한 todo_router를 import하고
FastAPI() 인스턴스의 include_router() 메서드를 사용해야한다.</p>
<pre><code class="language-python">from fastapi import FastAPI
from todo import todo_router

from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=[&quot;http://localhost:3000&quot;],  # 리액트 앱의 URL
    allow_credentials=True,
    allow_methods=[&quot;*&quot;],
    allow_headers=[&quot;*&quot;],
)

@app.get(&quot;/&quot;)
async def read_root():
    return {&quot;Hello&quot;: &quot;World&quot;}

app.include_router(todo_router)</code></pre>
<p>결과를 한번 볼까? (난 React로 심플하게 확인할 수 있는 코드를 짰다)
<img src="https://velog.velcdn.com/images/dev_junseok_22/post/934149a8-f187-4d20-9164-3387389356ba/image.png" alt=""></p>
<p>이제 POST 요청을 전달해서 todo_list에 추가해보자</p>
<pre><code>curl -X &#39;POST&#39; \
&#39;http://127.0.0.1:8000/todo&#39; \
-H &#39;accept: application/json&#39; \
-H &#39;Content-Type: application/json&#39; \
-d &#39;{
&quot;id&quot;:1,
&quot;item&quot;:&quot;First Todo is to finish this book!&quot;
}&#39;</code></pre><p>그럼 </p>
<pre><code>{&quot;message&quot;:&quot;Todo added successfully!&quot;}%                  </code></pre><p>라는 메시지가 출력될꺼다.</p>
<p>프론트엔드에서 확인해보니,
<img src="https://velog.velcdn.com/images/dev_junseok_22/post/91ccf19b-7983-428d-b2eb-8e4478c4a701/image.png" alt=""></p>
<p>잘 적용된거 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[FastAPI 프로젝트]]></title>
            <link>https://velog.io/@dev_junseok_22/FastAPI-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</link>
            <guid>https://velog.io/@dev_junseok_22/FastAPI-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</guid>
            <pubDate>Tue, 30 Apr 2024 08:38:15 GMT</pubDate>
            <description><![CDATA[<h1 id="fastapi-설치">FastAPI 설치</h1>
<h1 id="python-가상-환경-생성">Python 가상 환경 생성</h1>
<p>파이썬은 가상 환경을 통해 프로젝트마다의 독립된 환경을 구축할 수 있다. 하나의 PC안에서 독립된 가상 환경을 여러 개 만들수 있어, 하나의 PC안에 서로 다른 버전의 파이썬과 라이브러리를 쉽게 설치해 사용할 수 있다.</p>
<h4 id="가상-환경-생성">가상 환경 생성</h4>
<pre><code>python3 venv -m myapi</code></pre><p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/ca96af3f-7180-4515-bd1b-de132de99098/image.png" alt=""></p>
<h4 id="가상-환경-활성화">가상 환경 활성화</h4>
<pre><code>source myapi/bin/activate</code></pre><p>가상 환경에 정상적으로 진입했다면, 명령 프롬프트 왼쪽에 <strong>(myapi)</strong> 프롬프트가 보이는지 확인하자.</p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/73e2b523-d69e-4c43-a975-9b8c52ecc689/image.png" alt=""></p>
<h1 id="가상-환경에-fastapi-설치하기">가상 환경에 FastAPI 설치하기</h1>
<p>myapi 가상 환경에 진입한 상태에서 <code>pip3 install fastapi</code> 명령을 입력하면된다</p>
<pre><code>pip3 install fastapi</code></pre><h1 id="파이보-프로젝트">파이보 프로젝트</h1>
<p>웹 브라우저에서 &quot;안녕하세요 파이보&quot;를 출력해주는 첫 번째 프로그램을 만들어보겠다.</p>
<h2 id="hello-api-만들기">Hello API 만들기</h2>
<hr>
<p>다음과 같이 main.py 파일을 작성하자.</p>
<pre><code class="language-python">from fastapi import FastAPI

app = FastAPI()

@app.get(&quot;/hello&quot;)
def hello():
    return {&quot;message&quot;:&quot;안녕하세요 파이보&quot;}</code></pre>
<p>FastAPI 클래스로 생성한 app 객체가 바로 FastAPI의 핵심 객체이다. 모든 동작은 이 객체로부터 비롯된다. 함수명 위에 <code>@app.get(&quot;/hello&quot;)</code> 어노테이션은 <code>/hello</code>라는 URL요청이 발생하면 해당 함수를 실행하여 결과를 리턴하라는 의미이다. 따라서 <code>/hello</code>라는 URL이 요청되면 FastAPI는 <code>{&quot;message&quot;:&quot;안녕하세요 파이보&quot;}</code>라는 딕셔너리를 리턴할 것이다.</p>
<p>이제 작성한 프로그램을 실행해야한다. FastAPI로 작성한 프로그램을 실행하기 위해서는 FastAPI 프로그램을 구동할 서버가 필요하다. 먼저 <code>uvicorn</code>을 설치하자.</p>
<blockquote>
<p>유비콘(Uvicorn)은 비동기 호출을 지원하는 파이썬용 웹 서버이다.</p>
</blockquote>
<pre><code>pip3 install uvicorn[standard]</code></pre><p>설치가 완료되었다면, 이제 생성한 Hello API를 실행해보자.</p>
<pre><code>uvicorn main:app --reload</code></pre><p>그러면 현재 PC에 8000번 포트로 FastAPI서버가 구동된다.</p>
<blockquote>
<p><code>main:app</code>에서 main은 main.py 파일을 의미하고 app는 main.py의 app객체를 의미한다. <code>--reload</code> 옵션은 프로그램이 변경되면 서버 재시작 없이 그 내용을 반영하라는 의미히다.</p>
</blockquote>
<h2 id="hello-api-테스트하기">Hello API 테스트하기</h2>
<hr>
<p>FastAPI가 실행되면 FastAPI로 작성한 API는 <code>/docs</code> URL을 호출하여 테스트해볼 수 있다.</p>
<pre><code>http://127.0.0.1:8000/docs</code></pre><p>다음 화면이 나타난다. 이 화면이 바로 FastAPI의 테스트가 가능한 API 문서라고 한다.</p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/5d3e60b3-c794-4e63-a0bc-1d634f1c591c/image.png" alt=""></p>
<p>main.py에서 작성한 <code>/hello</code> URL에 대한 API가 등록된 것을 볼 수 있다. 해당 API를 누르고 &quot;Try it out&quot; 버튼을 누른 후 &quot;Execute&quot; 버튼을 누르면 Hello API의 동작을 테스트할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/481243cf-a598-426a-a4cd-c2a286fc330a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/95433591-673e-43cb-ba2a-15da90ceef7f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dev_junseok_22/post/086ebce1-abf4-4310-aa80-7afb0b3b1004/image.png" alt=""></p>
<p>hello 함수에서는 딕셔너리를 리턴했지만 FastAPI는 이를 자동으로 json 형태의 응답으로 리턴한다. </p>
<h2 id="한줄평">한줄평</h2>
<p>개좋네.. 예전에 끄적였던 스마트팜 모니터링용으로 구현한 React 서버랑 연동해서 한번 써봐야겠다..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[FastAPI 란?]]></title>
            <link>https://velog.io/@dev_junseok_22/FastAPI-%EB%9E%80</link>
            <guid>https://velog.io/@dev_junseok_22/FastAPI-%EB%9E%80</guid>
            <pubDate>Tue, 30 Apr 2024 08:03:03 GMT</pubDate>
            <description><![CDATA[<h1 id="fastapi란">FastAPI란?</h1>
<ul>
<li><p>API를 만들기 위한 파이썬 웹 프레임워크로, API를 만드는데 보다 집중한 프레임워크</p>
<blockquote>
<p>API(Application Programing Interface)
요청과 응답을 처리하는 서비스(기능)</p>
</blockquote>
</li>
<li><p>FastAPI로 작성한 API는 React와 같은 Frontend 웹 프레임워크나 iOS, Android에서도 사용할 수 있음</p>
</li>
<li><p><code>Starlette</code>라는 비동기 프레임워크를 통해 파이썬 웹 프레임워크 중 가장 빠른 속도를 자랑함</p>
</li>
<li><p><code>Pydantic</code>을 통해, 입출력을 정의하고 입출력 값의 검증을 빠르고 안전하게 할 수 있음, 그리고 작성한 API는 자동으로 생성되는 API 문서를 통해 손쉽게 테스트할 수 있음</p>
</li>
<li><p><code>SQLAlchemy</code>를 사용해 ORM(Object Relational Mapping)을 사용할 수 있음</p>
<blockquote>
<p><strong>SQLAlchemy?</strong>
파이썬 오픈 소스 SQL 툴킷 및 객체 관계 매핑(ORM) 라이브러리로, ORM은 데이터베이스의 테이블을 파이썬 객체로 표현하여, 파이썬 코드 내에사 데이터베이스를 좀 더 직관적이고 객체 지향적인 방식으로 다룰 수 있게 해줌</p>
</blockquote>
</li>
<li><p><em>Core*</em> : SQL 표현 언어를 사용하여 명시적인 SQL 쿼리를 작성할 수 있음</p>
</li>
<li><p><em>ORM*</em> : 더 높은 수준의 추상화를 제공하여, 클래스와 객체를 사용하여 데이터베이스 테이블을 매핑하고 조작할 수 있음</p>
</li>
</ul>
<p>*참고 : FastAPI는 파이썬 3.6.X 이상 버전에서 지원함</p>
]]></description>
        </item>
    </channel>
</rss>