<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>neymar_10.log</title>
        <link>https://velog.io/</link>
        <description>범수의 개발 놀이터😋</description>
        <lastBuildDate>Mon, 19 Aug 2024 15:17:30 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. neymar_10.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/neymar_10" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[리눅스 간단한 서버 구축하기 - 재고관리(2)]]></title>
            <link>https://velog.io/@neymar_10/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-%EC%9E%AC%EA%B3%A0%EA%B4%80%EB%A6%AC2</link>
            <guid>https://velog.io/@neymar_10/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-%EC%9E%AC%EA%B3%A0%EA%B4%80%EB%A6%AC2</guid>
            <pubDate>Mon, 19 Aug 2024 15:17:30 GMT</pubDate>
            <description><![CDATA[<h2 id="준비하기">&gt;&gt; 준비하기 &lt;&lt;</h2>
<blockquote>
</blockquote>
<ul>
<li>공용 IP 주소 확인하기</li>
<li>공유기 설정에서 포드포워딩 설정하기</li>
<li>방화벽 설정하기</li>
<li>/etc/nginx/sites-available/default 설정</li>
</ul>
<h3 id="포드포워딩">포드포워딩</h3>
<blockquote>
</blockquote>
<ul>
<li>포드포워딩이란?<ul>
<li>외부 IP에서 내부의 특정 IP에 연결해주는 역할</li>
</ul>
</li>
</ul>
<ol>
<li>공용IP를 이용해 공유기 설정 접속</li>
<li>포트포워드 설정(외부:8000포트, 내부:80포트)
<img src="https://velog.velcdn.com/images/neymar_10/post/dd2ef356-ab72-48e6-936e-7ebc71e1a840/image.png" alt=""></li>
</ol>
<h3 id="방화벽-설정">방화벽 설정</h3>
<blockquote>
</blockquote>
<pre><code>sudo ufw allow 8000/tcp
// sudo ufw allow 80/tcp
// sudo ufw allow 433/tcp</code></pre><p>8000포트로 들어오는 트레픽 허용</p>
<ul>
<li>안될 시 밑에 주석도 추가해보기</li>
</ul>
<h3 id="etcnginxsites-availabledefault">/etc/nginx/sites-available/default</h3>
<blockquote>
</blockquote>
<pre><code>server {
    listen 80;
    server_name public_ip;  # 공용 IP 주소
&gt;
    location ~ \.html {
        root /var/www/html;  # stock.html 파일이 있는 디렉토리
        index stock.html
        try_files $uri $uri/ =404;
    }
&gt;
    location /stock {
        proxy_pass http://localhost:8000;  # FastAPI 서버의 주소와 포트
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
* stock.html fetch IP주소 공용 IP로 변경</code></pre><h3 id="다양한-환경에서-접속해보기">다양한 환경에서 접속해보기</h3>
<blockquote>
</blockquote>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/38d794ae-dc4d-4fb1-9a41-0a84f3c31e5f/image.png" alt=""> 같은 IP의 다른 기기에서 접속</th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/970382b7-b2a1-4ffb-9c11-e7c1d06ef11f/image.png" alt=""> 외부 IP에서 기기에서 접속</th>
</tr>
</thead>
<tbody><tr>
<td>* 같은 IP에서 다른 기기로 연결할 시 서버 로컬 기기의 IP주소로 연결</td>
<td></td>
</tr>
<tr>
<td>* 외부 IP의 기기에서 연결할 시 공용 IP:8000포트로 연결</td>
<td></td>
</tr>
</tbody></table>
<h3 id="주의사항">주의사항</h3>
<blockquote>
</blockquote>
<ul>
<li>외부 IP에서 접속할 시 포드포워딩 확인하기</li>
<li>stock.html의 fetch주소 공용 IP로 설정하기</li>
<li>/etc/nginx/sites-available/default의 location 설정하기</li>
<li>307에러 발생 시 CORS 설정 확인하기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스 간단한 서버 구축하기 - 재고관리(1)]]></title>
            <link>https://velog.io/@neymar_10/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-%EC%9E%AC%EA%B3%A0%EA%B4%80%EB%A6%AC1</link>
            <guid>https://velog.io/@neymar_10/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-%EC%9E%AC%EA%B3%A0%EA%B4%80%EB%A6%AC1</guid>
            <pubDate>Mon, 19 Aug 2024 14:34:54 GMT</pubDate>
            <description><![CDATA[<h2 id="준비하기">&gt;&gt; 준비하기 &lt;&lt;</h2>
<h3 id="기능">기능</h3>
<blockquote>
</blockquote>
<ul>
<li>테이블<ul>
<li>Id(int): 고유 번호 사용</li>
<li>name(varchar): 상품명</li>
<li>quantity(int): 수량</li>
<li>description(varchar): 아이템 설명  </li>
</ul>
</li>
<li>기능<ul>
<li>전체 아이템 출력</li>
<li>아이템 검색(상품명에 따른 검색)</li>
<li>이아템 추가(상품명, 수량 필수 기입)</li>
<li>아이템 수정(수량, 설명만 수정 가능)</li>
<li>아이템 삭제</li>
</ul>
</li>
</ul>
<h3 id="stockapppy">stockapp.py</h3>
<blockquote>
<p>stockapp.py 중</p>
</blockquote>
<pre><code>&gt;
class Stock(BaseModel):   // Stock 모델 정의
    id: int
    name: str
    quantity: int
    description: str
&gt;
class UpdateModel(BaseModel):   // Update 시 사용할 모델 정의
    quantity: int
    description: str
&gt;
        ... stockapp.py 중 ...
 @app.put(&quot;/stock/{stock_id}&quot;)   
async def put_stock(stock_id: int, update: UpdateModel):     // 데이터 업데이트
    with app.db_connection.cursor() as cursor:
        sql = &quot;update stock set quantity = %s, description = %s where id = %s&quot;
        cursor.execute(sql, (update.quantity, update.description, stock_id))
        app.db_connection.commit()
    return {&quot;message&quot;: &quot;stock updated successfully&quot;}</code></pre><p>함수 제작 시 다양한 변수 사용 시 pydantic 모델로 정의 필요
- 변수로 대입 시 오류 발생(때문에 Stock, Update 모델 각각 정의 필요)</p>
<h3 id="stockhtml">stock.html</h3>
<blockquote>
<p>stock.html 중</p>
</blockquote>
<pre><code>const response = await fetch(&#39;http://공용 IP:8000/stock&#39;);  // 네이버에서 공용 IP 검색 
const items = await response.json();</code></pre><p>fetch 시 공용 IP 주소 사용(외부 IP에서 접속 시 필요)
- 내부 IP에서만 사용 시에는 localhost 사용 가능</p>
<blockquote>
</blockquote>
<pre><code> const response = await fetch(`http://공용 IP:8000/stock/${itemId}`, {
        method: &#39;PUT&#39;,
        headers: {
          &#39;Content-Type&#39;: &#39;application/json&#39;
        },
        body: JSON.stringify({id: id, quantity: quantity, description: description})
  });</code></pre><p>fetch 시 stringify로 stockapp.py의 함수에 필요한 변수 값 전송
- put_stock(id, quantity, description) 사용</p>
<h2 id="실행하기">실행하기</h2>
<blockquote>
<p>uvicorn stockapp:app --host 0.0.0.0 --port 8000 // 서버 가동</p>
</blockquote>
<ul>
<li>--host 0.0.0.0: 0.0.0.0은 모든 네트워크 인터페이스에서의 요청을 수신하겠다는 의미</li>
<li>--post 8000: 서버가 수신할 포트를 지정 즉 8000포트로 수신</li>
</ul>
<blockquote>
</blockquote>
<p><a href="http://localhost/stock.html">http://localhost/stock.html</a> 접속
stock테이블과 메인 페이지 정상 연결 확인</p>
<blockquote>
</blockquote>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/50e7ea0f-6426-4553-814a-d753f9abf2b6/image.png" alt=""> 메인 페이지</th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/1c951e20-0bf7-4465-9626-f7690af22803/image.png" alt=""> stock 테이블</th>
</tr>
</thead>
<tbody><tr>
<td>1. GET, localhost:8000/stock을 통해 전체 아이템 값 item에 저장</td>
<td></td>
</tr>
<tr>
<td>2. for문을 통해 각각의 아이템 출력</td>
<td></td>
</tr>
</tbody></table>
<blockquote>
<p>상품명으로 검색하기
<img src="https://velog.velcdn.com/images/neymar_10/post/6e7187c4-41a3-45c5-a5d8-6da9d8c4162e/image.png" alt=""></p>
</blockquote>
<ol>
<li>GET, localhost:8000/stock을 통해 전체 아이템 값 item에 저장</li>
<li>for문을 통해 item 값을 돌며, 검색 값과 일치할 시 출력 </li>
</ol>
<blockquote>
<p>상품 등록</p>
</blockquote>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/4fa5e772-3cf0-4aef-9815-94277a2f6f55/image.png" alt=""> 상품명 &quot;나사&quot; 추가</th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/4053ae2e-0e28-4ecc-84ee-355c39b34e86/image.png" alt=""> 추가 후 메인 페이지</th>
</tr>
</thead>
<tbody><tr>
<td>1. POST, localhost:8000/stock로 stock테이블에 값 저장</td>
<td></td>
</tr>
<tr>
<td>- Id(고유번호) 값은 가장 테이블의 가장 큰 Id의 다음 수로 설정</td>
<td></td>
</tr>
<tr>
<td>- 상품명, 수량은 필수</td>
<td></td>
</tr>
</tbody></table>
<blockquote>
<p>상품 수정</p>
</blockquote>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/3381ca48-9197-4eef-9508-b90a73246a6d/image.png" alt=""> 수량 150, 11일로 수정</th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/74ebecb3-8894-42ba-82f4-fd3789f3e246/image.png" alt=""> stock 테이블</th>
</tr>
</thead>
<tbody><tr>
<td>수량을 150, 일정 11로 수정</td>
<td></td>
</tr>
<tr>
<td>1. PUT, localhost:8000/stock/{itemId}로 업데이트(id, 상품명, 수량)</td>
<td></td>
</tr>
</tbody></table>
<blockquote>
<p>상품 삭제</p>
</blockquote>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/6350c933-3894-4588-82d3-653473a1649d/image.png" alt=""> &quot;케이블&quot; 삭제</th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/7b2a7390-4dfb-4ddb-b32d-be75477b53c8/image.png" alt=""> 삭제 후 메인 페이지</th>
</tr>
</thead>
<tbody><tr>
<td>1. &quot;케이블&quot; 삭제 시 DELETE, localhost:8000/stock/{itemId} 전송</td>
<td></td>
</tr>
<tr>
<td>2. 고유 번호에 맞는 아이템 삭제</td>
<td></td>
</tr>
</tbody></table>
<h3 id="확인하기">확인하기</h3>
<blockquote>
</blockquote>
<ul>
<li>출력 에러 시  fastAPI에서 CORS 설정 확인하기<img src="https://velog.velcdn.com/images/neymar_10/post/6af24797-09f5-496c-947f-705f5db2e1bc/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스 간단한 서버 구축하기 - RestAPI(2)]]></title>
            <link>https://velog.io/@neymar_10/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-RestAPI2</link>
            <guid>https://velog.io/@neymar_10/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-RestAPI2</guid>
            <pubDate>Tue, 09 Jul 2024 09:05:34 GMT</pubDate>
            <description><![CDATA[<h2 id="준비하기">&gt;&gt; 준비하기 &lt;&lt;</h2>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>sudo apt-get update
sudo apt-get install libmysqlclient-dev    //  MySQL 클라이언트 라이브러리의 개발 파일을 제공
&gt;
sudo install pymysql    </code></pre><p>&gt; pymysql 추가 설치하기</p>
<h3 id="dbapppy">dbapp.py</h3>
<blockquote>
<p>dbapp.py 생성</p>
</blockquote>
<pre><code>from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import pymysql
&gt;
# FastAPI 애플리케이션 초기화
app = FastAPI()
&gt;
# 데이터베이스 연결 설정
db_config = {
    &#39;host&#39;: &#39;localhost&#39;,
    &#39;user&#39;: &#39;사용자_이름&#39;,
    &#39;password&#39;: &#39;비밀번호&#39;,
    &#39;database&#39;: &#39;데이터베이스_이름&#39;,
    &#39;charset&#39;: &#39;utf8mb4&#39;,
    &#39;cursorclass&#39;: pymysql.cursors.DictCursor
}
&gt;
# FastAPI 시작 시 데이터베이스 연결 풀 생성
@app.on_event(&quot;startup&quot;)
async def startup():
    app.db_connection = pymysql.connect(**db_config)
&gt;
# FastAPI 종료 시 데이터베이스 연결 풀 닫기
@app.on_event(&quot;shutdown&quot;)
async def shutdown():
    app.db_connection.close()
&gt;
# Pydantic 모델 정의
class User(BaseModel):
    id: int
    name: str
    phone: str
    birth: str
&gt;
# MySQL에서 데이터를 가져오는 route 정의
@app.get(&quot;/&quot;)
async def read_root():
    with app.db_connection.cursor() as cursor:
        cursor.execute(&quot;SELECT * FROM your_table&quot;)
        result = cursor.fetchall()
    return result
&gt;
# MySQL에서 특정ID 데이터 가져오는 route 정의
@app.get(&quot;/users/{user_id}&quot;)
async def get_user(user_id: int):
    with app.db_connection.cursor() as cursor:
            sql = &quot;select * from your_table where id = %s&quot;
            cursor.execute(sql,(user_id,))
            result = cursor.fetchall()
    return result
&gt;
# MySQL에 데이터 삽입하는 route 정의
@app.post(&quot;/users&quot;)
async def post_user(user: User):
    with app.db_connection.cursor() as cursor:
        sql = &quot;INSERT INTO your_table (id, name, phone, birth) VALUES (%s, %s, %s, %s)&quot;
        cursor.execute(sql, (user.id, user.name, user.phone, user.birth))
        app.db_connection.commit()
    return {&quot;message&quot;: &quot;User created successfully&quot;}
&gt;
# MySQL에서 데이터 수정하는 route 정의
@app.put(&quot;/users/{user_id}&quot;)
async def put_user(user_id: int, user: User):
    with app.db_connection.cursor() as cursor:
        sql = &quot;UPDATE your_table SET name = %s, phone = %s, birth = %s WHERE id = %s&quot;
        cursor.execute(sql, (user.name, user.phone, user.birth, user_id))
        app.db_connection.commit()
    return {&quot;message&quot;: &quot;User updated successfully&quot;}
&gt;
# MySQL에서 데이터 삭제하는 route 정의
@app.delete(&quot;/users/{user_id}&quot;)
async def delete_user(user_id: int):
    with app.db_connection.cursor() as cursor:
        sql = &quot;DELETE FROM your_table WHERE id = %s&quot;
        cursor.execute(sql, (user_id,))
        app.db_connection.commit()
    return {&quot;message&quot;: &quot;User deleted successfully&quot;}
&gt;</code></pre><h3 id="db연결-및-crud">DB연결 및 CRUD</h3>
<h4 id="get">GET</h4>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>uvicorn dbapp:app --host 0.0.0.0 --port 8000 --reload</code></pre><p>&gt; <a href="http://your_server_ip:8000">http://your_server_ip:8000</a> 접속
- PHP 연결 시 사용했던 user DB연결(<a href="https://velog.velcdn.com/images/neymar_10/post/c0a08b47-e7a7-4074-923b-1e6fa48ea1a3/image.png">https://velog.velcdn.com/images/neymar_10/post/c0a08b47-e7a7-4074-923b-1e6fa48ea1a3/image.png</a>)</p>
<blockquote>
</blockquote>
<p align="center">
<img src="https://velog.velcdn.com/images/neymar_10/post/b790d7b8-9713-4c1a-859c-af81bdd3c635/image.png">
</p>
>
#### 특정 ID 조회
\> http://your_server_ip:8000/users/id 접속
<p>
<img src="https://velog.velcdn.com/images/neymar_10/post/f1fe72a1-fcb0-462a-b9ee-d9442580839e/image.png">
</p>

<h4 id="post">POST</h4>
<blockquote>
<p>postman -&gt; body탭 입력</p>
</blockquote>
<pre><code>{
    &quot;id&quot;: 4,
    &quot;name&quot;: &quot;Min Jae&quot;,
    &quot;phone&quot;: &quot;010-4444-4444&quot;,
    &quot;birth&quot;: &quot;961115&quot;
{</code></pre><ul>
<li>메소드 POST로 설정<blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/neymar_10/post/0d2737a4-7afc-4b6f-ba92-fe8a1e3f642a/image.png" alt=""> | <img src="https://velog.velcdn.com/images/neymar_10/post/6c068cba-7bf7-41db-8895-cab873b75f68/image.png" alt=""> 전체 조회</li>
<li>--| ---|</li>
</ul>
<h4 id="put">PUT</h4>
<blockquote>
<p>postman -&gt; body탭 입력</p>
</blockquote>
<pre><code>{
    &quot;id&quot;: 1,
    &quot;name&quot;: &quot;Beom Su&quot;,
    &quot;phone&quot;: &quot;010-1111-1111&quot;,
    &quot;birth&quot;: &quot;111111&quot;
{</code></pre><ul>
<li>메소드 PUT으로 설정<blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/neymar_10/post/e4803931-f240-47c3-910e-d08550e0ddb1/image.png" alt=""> birth를 &quot;111111&quot;로 수정| <img src="https://velog.velcdn.com/images/neymar_10/post/327e429a-e72c-4b5c-90ce-b262562fc2ce/image.png" alt=""></li>
<li>--| ---|</li>
</ul>
<h4 id="delete">DELETE</h4>
<blockquote>
</blockquote>
<ul>
<li>메소드 DELETE로 설정</li>
<li><a href="http://your_server_ip:8000/users/id">http://your_server_ip:8000/users/id</a> (DELETE를 원하는 Id 지정)</li>
<li>Send<blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/neymar_10/post/a4a2bac0-bf5c-44e7-8789-33cbd6566144/image.png" alt=""> id가 3인 user 삭제| <img src="https://velog.velcdn.com/images/neymar_10/post/a83a03ca-9583-421b-b5d5-4254ac1daf14/image.png" alt=""></li>
<li>--| ---|</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스 간단한 서버 구축하기 - RestAPI(1)]]></title>
            <link>https://velog.io/@neymar_10/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-RestAPI1</link>
            <guid>https://velog.io/@neymar_10/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-RestAPI1</guid>
            <pubDate>Mon, 08 Jul 2024 15:28:36 GMT</pubDate>
            <description><![CDATA[<h2 id="알아보기">&gt;&gt; 알아보기 &lt;&lt;</h2>
<h3 id="restapi란">RestAPI란?</h3>
<ul>
<li>RestAPI는 클라이언트와 서버 간의 상호작용을 간단하고 유연하게 만들어주는 웹 서비스 아키텍처 스타일 중 하나</li>
</ul>
<h4 id="주요-개념">주요 개념</h4>
<blockquote>
<ol>
<li>자원 <ul>
<li>REST API에서 모든 것은 자원으로 간주</li>
<li>각 자원은 고유한 URI를 통해 식별된다. 즉, 특정 사용자의 자원 URI는 <a href="http://api.example.com/users/123%EC%99%80">http://api.example.com/users/123와</a> 같을 수 있음</li>
</ul>
</li>
<li>HTTP 메서드<ul>
<li>REST API는 HTTP 메서드를 사용하여 자원에 대한 CRUD(생성, 읽기, 갱신, 삭제) 작업을 수행</li>
</ul>
</li>
</ol>
</blockquote>
<pre><code>    GET: 자원 조회
    POST: 새로운 자원 생성
    PUT: 기존 자원 갱신
    DELETE: 자원 삭제
    PATCH: 자원의 일부를 수정 </code></pre><blockquote>
<ol start="3">
<li>표현<ul>
<li>클라이언트와 서버 간의 데이터 교환은 자원의 표현을 통해 발생</li>
<li>표현은 일반적으로 JSON 또는 XML 형식으로 전송</li>
</ul>
</li>
<li>상태 없는 통신(Stateless Communication)<ul>
<li>각 요청은 클라이언트의 상태나 이전 요청의 문맥을 서버가 기억하지 않고 독립적으로 처리
  - 모든 필요한 정보는 요청 내에 포함되어야 함</li>
</ul>
</li>
<li>계층화된 시스템<ul>
<li>클라이언트는 중간 서버를 통하거나 직접 서버와 통신할 수 있음
  - 중간 서버는 로드 밸런싱, 캐싱, 보안 정책 등 다양한 기능을 제공가능</li>
</ul>
</li>
<li>캐싱<ul>
<li>서버는 응답에 캐시 가능한 정보를 포함시킬 수 있으며, 클라이언트는 이를 이용해 불필요한 서버 요청을 줄일 수 있음</li>
</ul>
</li>
</ol>
</blockquote>
<h4 id="rest-api-설계-방법">REST API 설계 방법</h4>
<blockquote>
<ol>
<li>명확하고 일관된 자원 URI 설계<ul>
<li>자원의 URI는 직관적이고 일관성 있게 설계되어야 함</li>
</ul>
</li>
<li>적절한 HTTP 상태 코드 사용<ul>
<li>각 요청에 대해 적절한 HTTP 상태 코드를 반환하여 클라이언트가 요청의 성공 여부를 쉽게 이해할 수 있어야 함</li>
</ul>
</li>
</ol>
</blockquote>
<pre><code>    200 OK: 요청 성공
    201 Created: 새로운 자원 생성
    400 Bad Request: 잘못된 요청
    404 Not Found: 자원 찾을 수 없음
    500 Internal Server Error: 서버 오류</code></pre><blockquote>
<ol start="3">
<li>HATEOAS(Hypermedia As The Engine Of Application State)<ul>
<li>클라이언트가 서버의 응답을 통해 다음 가능한 행동에 대한 정보를 얻을 수 있어야 함</li>
<li>응답에 관련 링크를 포함하여 클라이언트가 쉽게 탐색할 수 있어야 함</li>
</ul>
</li>
<li>보안<ul>
<li>인증(Authentication)과 권한 부여(Authorization)를 통해 자원에 대한 접근을 제어</li>
<li>HTTPS를 사용하여 데이터 전송 중 보안을 강화</li>
</ul>
</li>
</ol>
</blockquote>
<h2 id="준비하기">&gt;&gt; 준비하기 &lt;&lt;</h2>
<ul>
<li>Python 중 Flask, Django, FastAPI 등 다양한 프레임워크 중에서 FastAPI를 사용해 제작보았습니다.</li>
</ul>
<h4 id="패키지-설치">패키지 설치</h4>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>----------------FastAPI, Uvicorn 설치[1]------------------
ssh your_username@your_server_ip    // Ubuntu 서버에 SSH로 접속
&gt;
sudo apt update
sudo apt install python3 python3-pip    // 파이썬 설치
&gt;
pip3 install fastapi uvicorn    // FastAPI, Uvicorn 설치
&gt;
----------------FastAPI, Uvicorn 등 설치 실패 시[2](가상 환경)-----------------
sudo apt install python3-venv    // 가상 환경 설치
&gt;
mkdir my_fastapi_app    // 디렉터리 생성 및 이동
cd my_fastapi_app        
&gt;
python3 -m venv venv        // 프로젝트 디렉토리에서 가상 환경을 생성
source venv/bin/activate    // 가상 환경 활성화
&gt;
pip3 install fastapi uvicorn    // FastAPI, Uvicorn 설치</code></pre><p>&gt; 1, 2중에서 상황에 맞는 패키지 설치하기</p>
<h4 id="fastapi">FastAPI</h4>
<blockquote>
<p>my_fastapi_app/app.py </p>
</blockquote>
<pre><code>from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
&gt;
app = FastAPI()
&gt;
# 데이터 모델
class Note(BaseModel):
    id: int
    title: str
    content: str
&gt;
# 임시 데이터베이스
notes = []
&gt;
# notes값 전체 조회
@app.get(&quot;/notes&quot;, response_model=list[Note])
def get_notes():
    return notes
&gt;
# id별 notes 조회
@app.get(&quot;/notes/{note_id}&quot;, response_model=Note)
def get_note(note_id: int):
    note = next((note for note in notes if note.id == note_id), None)
    if note is None:
        raise HTTPException(status_code=404, detail=&quot;Note not found&quot;)
    return note
&gt;
# notes 생성(id 자동으로 생성)
@app.post(&quot;/notes&quot;, response_model=Note, status_code=201)
def create_note(note: Note):
    notes.append(note)
    return note
&gt;
# id별 notes 수정
@app.put(&quot;/notes/{note_id}&quot;, response_model=Note)
def update_note(note_id: int, updated_note: Note):
    note = next((note for note in notes if note.id == note_id), None)
    if note is None:
        raise HTTPException(status_code=404, detail=&quot;Note not found&quot;)
    note.title = updated_note.title
    note.content = updated_note.content
    return note
&gt;
#id별 notes 삭제
@app.delete(&quot;/notes/{note_id}&quot;, status_code=204)
def delete_note(note_id: int):
    global notes
    notes = [note for note in notes if note.id != note_id]
    return None
&gt;
# 현재 스크립트가 직접 실행될 때만 코드 블록 안의 내용 실행
if __name__ == &quot;__main__&quot;:
    import uvicorn
    uvicorn.run(app, host=&quot;0.0.0.0&quot;, port=8000)</code></pre><p>&gt; 메모리에 저장되는 CRUD 기능이 포함된 app.py 제작
- 임시로 데이터베이스 역할을 표현하기 위해 배열로 저장
- host=&quot;0.0.0.0&quot;: 서버가 모든 네트워크 인터페이스에서 접속을 허용</p>
<h3 id="apppy-실행하기">app.py 실행하기</h3>
<ul>
<li>Postman 설치 후 실행(API를 디자인, 빌드, 테스트하기 위한 API 플랫폼) </li>
</ul>
<h4 id="get">GET</h4>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>uvicorn app:app --host 0.0.0.0 --port 8000</code></pre><p>&gt;<a href="http://your_server_ip:8000">http://your_server_ip:8000</a> 접속</p>
<p align="center">
<img src="https://velog.velcdn.com/images/neymar_10/post/c9118d89-faad-4333-be90-72a9970f23b1/image.png">전체 notes값 조회</p>

<h4 id="post">POST</h4>
<blockquote>
</blockquote>
<p>Postman -&gt; body탭 입력</p>
<pre><code>{
  &quot;id&quot;: 1,
  &quot;title&quot;: &quot;Sample Note&quot;,
  &quot;content&quot;: &quot;This is a sample note.&quot;
}</code></pre><ul>
<li>메서드를 POST로 설정</li>
<li>URL을 <a href="http://your_server_ip:8000/notes">http://your_server_ip:8000/notes</a> 로 설정</li>
<li>Body 탭에서 raw와 JSON을 선택하고, 위의 JSON 데이터를 입력</li>
<li>Send 버튼을 클릭<blockquote>
</blockquote>
<p align="center">
<img src="https://velog.velcdn.com/images/neymar_10/post/9484630b-3821-4861-a152-04e55f05d09a/image.png" width="80%">POST
</p>
<p align="center">
<img src="https://velog.velcdn.com/images/neymar_10/post/7728f8b9-b1b0-4438-9203-44efbcecc2ab/image.png">전체 notes값 조회
</p>

</li>
</ul>
<h4 id="put">PUT</h4>
<blockquote>
<p>Postman -&gt; body 탭 입력</p>
</blockquote>
<pre><code>{
  &quot;id&quot;: 1,
  &quot;title&quot;: &quot;Updated Sample Note&quot;,
  &quot;content&quot;: &quot;This is a Updated sample&quot;
}</code></pre><ul>
<li>메서드를 PUT으로 설정</li>
<li>URL을 <a href="http://your_server_ip:8000/notes/1">http://your_server_ip:8000/notes/1</a> 로 설정</li>
<li>Body 탭에서 raw와 JSON을 선택하고, 위의 JSON 데이터를 입력</li>
<li>Send 버튼을 클릭<blockquote>
</blockquote>
<p align="center">
<img src="https://velog.velcdn.com/images/neymar_10/post/d153ce9f-d809-403c-ac6a-e8b2f459dd91/image.png" width="60%">PUT
</p>
<p align="center">
<img src="https://velog.velcdn.com/images/neymar_10/post/0c70f872-efa3-42e9-b9d5-08af6b102a06/image.png" width="50%">전체 notes값 조회
</p>

</li>
</ul>
<h4 id="delete">DELETE</h4>
<blockquote>
</blockquote>
<ul>
<li>메서드를 DELETE으로 설정</li>
<li>URL을 <a href="http://your_server_ip:8000/notes/1">http://your_server_ip:8000/notes/1</a> 로 설정</li>
<li>Send 버튼을 클릭<blockquote>
</blockquote>
<p align="center">
<img src="https://velog.velcdn.com/images/neymar_10/post/c00948dd-7d0f-4a8e-b07e-285cca40ca4a/image.png" width="60%">DELETE
</p>
<p align="center">
<img src="https://velog.velcdn.com/images/neymar_10/post/8840d268-4445-4e40-823c-2fce98dc1c6e/image.png">전체 notes값 조회
</p></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스 간단한 서버 구축하기 - MySQL]]></title>
            <link>https://velog.io/@neymar_10/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-MySQL</link>
            <guid>https://velog.io/@neymar_10/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-MySQL</guid>
            <pubDate>Thu, 04 Jul 2024 14:37:15 GMT</pubDate>
            <description><![CDATA[<h2 id="mysql-설치하기">&gt;&gt; MySQl 설치하기 &lt;&lt;</h2>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>sudo apt update
sudo apt install mysql-server    // Ubuntu MySQL 설치
&gt;
sudo mysql_secure_installation    // MySQL 설치 후 보안 설정</code></pre><p>&gt; 우분투에서 MySQL 설치 후 보안 설정하기</p>
<h3 id="mysql-접속하기">MySQL 접속하기</h3>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>sudo mysql -u root -p    // MySQl 접속
CREATE DATABASE user_db; // DB생성
&gt;
USE user_db;    // DB접속</code></pre><p>&gt; MySQL 접속 후 user_db에 접속하기</p>
<h3 id="테이블-생성-및-데이터-삽입">테이블 생성 및 데이터 삽입</h3>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(20) NOT NULL,
    phone VARCHAR(15),
    birth VARCHAR(6)
);    // users 테이블 생성
&gt;
INSERT INTO users (name, phone, birth) VALUES (&#39;Beom Su&#39;, &#39;010-1111-1111&#39;, &#39;000000&#39;);
INSERT INTO users (name, phone, birth) VALUES (&#39;Heung Min&#39;, &#39;010-2222-2222&#39;, &#39;920708&#39;);
INSERT INTO users (name, phone, birth) VALUES (&#39;Kang In&#39;, &#39;010-3333-3333&#39;, &#39;010219&#39;);
// 테이블 데이터 삽입</code></pre><h3 id="php와-mysql-연결">PHP와 MySQL 연결</h3>
<blockquote>
<p>콘솔 창 입력    // conn.php파일 생성 후 아래 코드 삽입</p>
</blockquote>
<pre><code>&lt;?php
$servername = &quot;localhost&quot;;
$username = &quot;root&quot;;
$password = &quot;your_password&quot;;
$dbname = &quot;your_db&quot;;
&gt;
// MySQL 연결 생성    mysqli 중요!(철자 틀리면 안됨*함수임*)
$conn = new mysqli($servername, $username, $password, $dbname);
&gt;
// 연결 확인
if ($conn-&gt;connect_error) {
    die(&quot;연결 실패: &quot; . $conn-&gt;connect_error);
}
echo &quot;연결 성공!&quot;;
?&gt;</code></pre><p><span style="color:red"> ** 주의! **<br>연결 안될 시 페스워드 재설정 필요!</span>
&gt;MySQL8.0 이상에서는 비밀번호 정책이 설정되어 충족하는 번호로 변경해야 함</p>
<ol>
<li>비밀번호 길이 최소 몇 글자 이상이여야 함</li>
<li>대문자, 소문자, 숫자, 특수문자 등을 포함해야 함<pre><code>MySQL에서 변경 시
ALTER USER &#39;root&#39;@&#39;localhost&#39; IDENTIFIED WITH mysql_native_password BY &#39;Strong_Password123!&#39;;</code></pre><blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/neymar_10/post/6906d566-482e-489d-a7a3-add4b7d9ede2/image.png" alt=""> conn.php 파일 | <img src="https://velog.velcdn.com/images/neymar_10/post/6959fd5d-2859-479b-b47d-cc41a2c1a6ce/image.png" alt=""> 정상 연결화면</li>
</ol>
<p>---|---|</p>
<h3 id="mysql-비밀번호-정책-설정-변경">MySQL 비밀번호 정책 설정 변경</h3>
<blockquote>
<p>MySQL 설정 파일 (my.cnf 또는 my.ini)에서 비밀번호 정책을 변경</p>
</blockquote>
<pre><code>-----------------my.ini---------------------
[mysqld]
validate_password.policy=LOW
// MySQL 설정 파일을 열어서 [mysqld] 섹션에 다음과 같이 비밀번호 정책 관련 옵션을 추가</code></pre><ul>
<li>&#39;LOW&#39;: 비밀번호 길이만 체크</li>
<li>&#39;MEDIUM&#39;: 길이와 숫자/특수 문자 포함 여부 체크</li>
<li>&#39;STRONG&#39;: 길이, 숫자, 특수 문자, 대문자/소문자 포함 여부 체크</li>
</ul>
<h3 id="mysql-테이블과-php-연결">MySQL 테이블과 PHP 연결</h3>
<blockquote>
<p>MySQL user 테이블 확인</p>
</blockquote>
<p align="center">
<img src="https://velog.velcdn.com/images/neymar_10/post/c0a08b47-e7a7-4074-923b-1e6fa48ea1a3/image.png">users 테이블</p>
user.php 파일 생성
>
```
<!DOCTYPE html>
<html>
<head>
    <title>User Table</title>
</head>
<body>
    <?php    
    $conn = new mysqli('localhost','root','your_password','user_db');
    if($conn->connect_error){
        die("연결 실패: " . $mysqli->connect_errno  . $conn->connect_error);
    }else{
        echo "연결 성공!";
    }
    $sql = "select * from users";
    $result = $conn->query($sql);
    $conn->close();
    ?>
    <h2>User Info</h2>
    <table>
    <tr>
        <th>Name</th>
        <th>Phone</th>
        <th>Birth</th>
    </tr>
    <?php while($row = $result->fetch_assoc()){ ?>
    <tr>
        <td><?php echo $row["name"]; ?></td>
        <td><?php echo $row["phone"]; ?></td>
        <td><?php echo $row["birth"]; ?></td>
    </tr>
    <?php }; ?>
    </table>
</body>
</html>
```
\> MySQL과 PHP 연결 꼭 확인!
<p align="center">
<img src="https://velog.velcdn.com/images/neymar_10/post/79a07dd1-ccce-48cb-8630-cbd0da8c1d18/image.png">user.php user테이블 연결확인</p>

<h2 id="마무리">마무리</h2>
<blockquote>
</blockquote>
<ul>
<li>MySQL와 PHP 연결이 안될 경우<ul>
<li>MySQL 버전이 8.0이상일 경우 정책에 맞게 비밀번호 변경해보기</li>
</ul>
</li>
<li>WIFI연결 시 다른 로컬 기기에서도 user.php 접속해보기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스 간단한 서버 구축하기 - PHP]]></title>
            <link>https://velog.io/@neymar_10/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-PHP-c3p7vdtv</link>
            <guid>https://velog.io/@neymar_10/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-PHP-c3p7vdtv</guid>
            <pubDate>Thu, 04 Jul 2024 13:41:01 GMT</pubDate>
            <description><![CDATA[<h2 id="apache2와-php-설치">&gt;&gt; Apache2와 PHP 설치 &lt;&lt;</h2>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>php -v    //버전 확인 후 7.4이상 버전일 경우
---------------------7.4버전 이상일 경우 실행---------------------- 
sudo apt remove php* --purge    
sudo apt autoremove    // 기존 php 제거
&gt;
sudo apt update    
sudo apt install software-properties-common
sudo add-apt-repository ppa:ondrej/php
sudo apt update    // PPA 저장소 추가
&gt;
sudo apt install php7.4-cli php7.4-fpm php7.4-mysql php7.4-xml php7.4-mbstring php7.4-curl
// php7.4 버전으로 설치
-----------------------이상 없다면 생략 가능------------------------
&gt;
sudo apt install libapache2-mod-php7.4    // Apache2 모듈 설치
&gt;
sudo a2dismod php8.x  # 기존 PHP 8.x 모듈 비활성화 (설치된 경우)
sudo a2enmod php7.4
sudo systemctl restart apache2</code></pre><p>&gt; Apache2와 PHP 연결에 이상이 있다면 버전 문제일 수 있으므로 7.4로 재설치(권장)
&gt; php설치 후 libapache2-mod-php* 자신 PHP 버전에 맞는 Apache2설치 요망</p>
<h3 id="php와-nginx-연동">PHP와 Nginx 연동</h3>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>sudo nano /etc/nginx/sites-available/default</code></pre><p>&gt; Nginx 설정 파일 열고 PHP-FPM 소켓을 사용하도록 설정</p>
<pre><code>    **    /etc/nginx/sites-available/default 파일 내용    **
    &gt;
location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;  # PHP 7.4 FPM 소켓 경로
}</code></pre><p>&gt; /etc/nginx/sites-available/default에 위 코드 추가(PHP, Nginx연결)
&nbsp;&nbsp;&nbsp;&nbsp; -&gt; 자신의 PHP 버전에 맞는 소켓으로 추가!</p>
<pre><code>sudo systemctl restart php7.4-fpm
sudo systemctl restart nginx</code></pre><p>&gt; 설정 후 Nginx와 PHP-FPM 재시작</p>
<h3 id="연결-확인">연결 확인</h3>
<blockquote>
</blockquote>
<p>sudo nano /var/www/html/info.php 파일 생성</p>
<pre><code>&lt;?php phpinfo(); ?&gt;</code></pre><p>&gt; /var/www/html/info.php 파일에 <?php phpinfo(); ?>입력 후 접속</p>
<p align="center">
<img src="https://velog.velcdn.com/images/neymar_10/post/d4385e26-5c49-48e9-a484-1fefcc676744/image.png"> 연결 확인</p>

<h2 id="php-파일-생성">&gt;&gt; PHP 파일 생성 &lt;&lt;</h2>
<blockquote>
<p>index.php 파일 생성</p>
</blockquote>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;PHP Example&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;&lt;?php echo &quot;Hello, this is a PHP file.&quot;; ?&gt;&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>&gt; /var/www/html/index.php파일 생성 후 코드 입력</p>
<blockquote>
</blockquote>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/4f1eee6d-b3ae-4d14-aa86-8de7ab871da2/image.png" alt="">index.php 파일</th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/819d36d3-29ee-483a-a78d-effcd05d94cf/image.png" alt=""> index.php 동작화면</th>
</tr>
</thead>
<tbody><tr>
<td>&gt; 만약 정상작동이 하지 않는다면 꼭 버전을 확인해보기</td>
<td></td>
</tr>
</tbody></table>
<h3 id="더-해보기">더 해보기</h3>
<blockquote>
<p>list.php 파일 제작해보기</p>
</blockquote>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;User Information Table&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h2&gt;User Information&lt;/h2&gt;
&lt;?php
// 사용자 정보를 담은 배열
$users = [
    [&quot;name&quot; =&gt; &quot;Beom Su&quot;, &quot;phone&quot; =&gt; &quot;010-1111-1111&quot;, &quot;address&quot; =&gt; &quot;Seoul&quot;],
    [&quot;name&quot; =&gt; &quot;Gil Dong&quot;, &quot;phone&quot; =&gt; &quot;010-2222-2222&quot;, &quot;address&quot; =&gt; &quot;Busan&quot;],
    [&quot;name&quot; =&gt; &quot;Su Jin&quot;, &quot;phone&quot; =&gt; &quot;010-3333-3333&quot;, &quot;address&quot; =&gt; &quot;Gangwon&quot;]
];
?&gt;
&lt;table&gt;
    &lt;tr&gt;
        &lt;th&gt;Name&lt;/th&gt;
        &lt;th&gt;Phone&lt;/th&gt;
        &lt;th&gt;Address&lt;/th&gt;
    &lt;/tr&gt;
    &lt;?php foreach ($users as $user) { ?&gt;
    &lt;tr&gt;
        &lt;td&gt;&lt;?php echo htmlspecialchars($user[&quot;name&quot;]); ?&gt;&lt;/td&gt;
        &lt;td&gt;&lt;?php echo htmlspecialchars($user[&quot;phone&quot;]); ?&gt;&lt;/td&gt;
        &lt;td&gt;&lt;?php echo htmlspecialchars($user[&quot;address&quot;]); ?&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;?php }; ?&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p align="center">
<img src="https://velog.velcdn.com/images/neymar_10/post/0cda4d27-c466-49cf-a2ea-b8f92eaa8a8b/image.png">list.php 화면</p>
>
\> list.php의 테이블이 정상적으로 출력되는 것을 확인할 수 있다.


<h2 id="마무리">마무리</h2>
<blockquote>
</blockquote>
<ul>
<li>정상적으로 작동을 안한다면 PHP, Apache2버전, /etc/nginx/sites-available/default파일 안에 PHP-FPM버전이 자기 PHP버전과 일치한 지 확인해보기</li>
<li>Apache2, Nginx의 포트가 중첩된 오류가 있을 수 있으니 포트도 확인해보기</li>
<li>다음엔 MySQL을 이용해 테이블 제작해보기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스 간단한 서버 구축하기 - 설치]]></title>
            <link>https://velog.io/@neymar_10/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@neymar_10/%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Thu, 04 Jul 2024 12:20:18 GMT</pubDate>
            <description><![CDATA[<h2 id="준비하기">&gt;&gt;준비하기&lt;&lt;</h2>
<h3 id="1-기본-패키지-업데이트">1. 기본 패키지 업데이트</h3>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>sudo apt update</code></pre><p>&gt; 새로운 버전이 생겼을 수도 있으므로 업데이트 필요</p>
<h3 id="2-기본-설정-및-보안-설정">2. 기본 설정 및 보안 설정</h3>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>sudo apt install ufw
sudo ufw allow OpenSSH
sudo ufw enable</code></pre><p>&gt; SSH 및 방화벽 설정: 기본적으로 SSH를 통해 서버에 접근할 수 있도록 설정
&gt; UFW(방화벽)를 설치하고 실행 시 항상 실행되도록 설정
&gt; sudo ufw status로 상태확인 가능</p>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>sudo nano /etc/ssh/sshd_config
sudo systemctl restart ssh</code></pre><p>&gt; &#39;PermitRootLogin no&#39;: 루트 사용자로 SSH 접속을 차단
&gt; &#39;PasswordAuthentication yes&#39;: 비밀번호 인증 허용(키 인증으로 변경 권장)</p>
<h3 id="3-웹-서버-서비스-설치-및-구성">3. 웹 서버 서비스 설치 및 구성</h3>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>sudo apt install nginx -y
sudo systemctl start nginx
sudo systemctl enable nginx</code></pre><p>&gt; nginx(웹 서버 서비스)시작 및 자동 실행 설정</p>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>sudo nano /etc/nginx/sites-available/default</code></pre><p>&gt; nginx 기본 설정 파일 편집</p>
<blockquote>
<p>sudo nano /etc/nginx/sites-available/default 파일 내용</p>
</blockquote>
<pre><code>server {
    listen 81 default_server; // nginx와 apache2의 포트가 80번 충돌이 있을 수 있으므로 포트 조정
    listen [::]:81 default_server;
&gt;
    root /var/www/html;
    index index.html index.htm index.nginx-debian.html index.php;
&gt;
    server_name _;
&gt;
    location / {
        try_files $uri $uri/ =404;
    }
&gt;
    location ~ \.php$ { // 추후 php 연결을 위해 설정
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.4-fpm.sock;
    }
}</code></pre><p><span style="color:red">&gt;&gt; php7.4이상은 연결이 다르므로 php7.4버전으로 설치 &lt;&lt; </span>
&nbsp;&nbsp;&nbsp;&nbsp; <font size=2>-&gt; 혹시 다른 7.4이상 버전으로 방법이 있다면 알려주세요ㅠㅠ</font>
&#39;server&#39; 블록 안 기본 루트 디렉토리 설정
&#39;sudo systemctl reload nginx&#39;
 &nbsp;&nbsp; <span style="color:red"> -&gt; &#39;<a href="http://your_server_ip&#39;">http://your_server_ip&#39;</a> 주소로 접속을 통해 welcome 페이지 확인</span></p>
<h2 id="웹-페이지-설정">&gt;&gt;웹 페이지 설정&lt;&lt;</h2>
<h3 id="1-기본-웹-페이지-디렉터리">1. 기본 웹 페이지 디렉터리</h3>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>cd /var/www/html</code></pre><p>&gt; nginx의 기본 웹 페이지 파일은 &#39;/var/www/html&#39; 디렉터리에 존재<br></p>
<h3 id="2-기본-인덱스-파일-수정">2. 기본 인덱스 파일 수정</h3>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>sudo nano index.html</code></pre><p>&gt; 접속 후 편집</p>
<blockquote>
<p>/var/www/html/index.html파일</p>
</blockquote>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Welcome to Nginx!&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Success! Your Nginx server is working!&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>&gt; &#39;uccess! Your Nginx server is working!&#39;문구가 뜨면 정상 작동!<br></p>
<blockquote>
</blockquote>
<p align="center" >
<img src="https://velog.velcdn.com/images/neymar_10/post/a45768a7-ff6e-4464-a90b-62b45ce00714/image.png" style="margin:30px 0 0 0">
< 정상 작동 화면(index.html) >
</p><br>

<h3 id="3-로컬에서-도메인명-사용">3. 로컬에서 도메인명 사용</h3>
<blockquote>
<p>콘솔 창 입력</p>
</blockquote>
<pre><code>sudo nano /etc/hosts</code></pre><p>&gt; 접속 후 도메인과 IP주소 매핑 추가(your_server_ip <a href="http://www.beomsu.com">www.beomsu.com</a>)</p>
<blockquote>
</blockquote>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/15dc4e37-51c9-43b5-8645-9da344d28c6d/image.png" alt=""> ip주소 도메인으로 변경</th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/199752a7-da22-4dec-8d52-b3b7cc95e25f/image.png" alt=""> <a href="http://www.beomsu.com%EC%9C%BC%EB%A1%9C">www.beomsu.com으로</a> 접속 확인</th>
</tr>
</thead>
<tbody><tr>
<td><br></td>
<td></td>
</tr>
</tbody></table>
<h2 id="마무리">마무리</h2>
<blockquote>
</blockquote>
<ul>
<li>nginx와 apache2의 포트 충돌 주의<br></li>
<li>php7.4이상은 연결 방법이 다르므로 php7.4로 설치하는 것 추천<br></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring boot - AOP]]></title>
            <link>https://velog.io/@neymar_10/Spring-boot-AOP</link>
            <guid>https://velog.io/@neymar_10/Spring-boot-AOP</guid>
            <pubDate>Mon, 04 Mar 2024 10:35:03 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="목차">목차</h3>
</blockquote>
<ol>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0">프로젝트 생성하기</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-View-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95">View 환경설정</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EC%B4%88">스프링 웹 개발 기초</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%98%88%EC%A0%9C">회원 관리 예제</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88%EA%B3%BC-%EC%9D%98%EC%A1%B4-%EA%B4%80%EA%B3%84">스프링 빈과 의존 관계</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%9B%B9-MVC-%EC%A0%9C%EC%9E%91">웹 MVC 제작</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB-%EC%A0%9C%EC%9E%911">DB 제작(1)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB%EC%A0%9C%EC%9E%912">DB 제작(2)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-AOP">AOP</a></li>
</ol>
<h3 id="1-aop">1. AOP</h3>
<blockquote>
<p>회원 조회 시간 측정 추가</p>
</blockquote>
<pre><code> public Long join(Member member) {
     long start = System.currentTimeMillis();    //시작 시간 측정
     try {
         validateDuplicateMember(member); //중복 회원 검증
        memberRepository.save(member);
         return member.getId();
    }finally {
         long finish = System.currentTimeMillis();    // 종료 시간 측정
         long timeMs = finish - start;
         System.out.println(&quot;join &quot; + timeMs + &quot;ms&quot;); // 총 시간 = 종료 시간 - 시작 시간
    }
}
// 회원가입 함수</code></pre><p>&lt;&lt;문제점&gt;&gt;</p>
<ol>
<li>시간 측정은 핵심 로직이 아님(But 시간 측정 로직은 공통 사항)</li>
<li>시간을 측정하는 로직과 핵심 비즈니스의 로직이 섞여서 유지보수 어려움(별도의 공통 로직으로 만드는 것도 힘듬)</li>
<li>시간 측정 로직을 변경할 때 모든 로직을 찾아서 변경해야 함</li>
</ol>
<blockquote>
<p>AOP 적용</p>
</blockquote>
<pre><code>@Component    // @Bean으로도 등록 가능(선호)
@Aspect
public class TimeTraceAop {
   @Around(&quot;execution(* hello.hellospring..*(..))&quot;) // hello.hellospring 하위 모두 실행
    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
             long start = System.currentTimeMillis();
             System.out.println(&quot;START: &quot; + joinPoint.toString());
         try {
             return joinPoint.proceed();
        }finally {
             long finish = System.currentTimeMillis();
             long timeMs = finish - start;
             System.out.println(&quot;END: &quot; + joinPoint.toString()+ &quot; &quot; + timeMs + &quot;ms&quot;);
        }
     }
}
//hello.hellospring.aop.TimeTraceAop.java</code></pre><p>&lt;&lt;해결&gt;&gt;</p>
<ul>
<li>시간을 측정하는 로직을 별도의 공통 로직으로 제작</li>
<li>변경 시에도 공통 로직만 변경하면 됨(타겟팅도 가능)</li>
</ul>
<blockquote>
<p>AOP 동작 방식</p>
</blockquote>
<p>&lt;&lt;기존 스프링 컨테이너&gt;&gt;
[컨트롤러 -&gt; 서비스]</p>
<p>&lt;&lt;AOP 적용 후 의존 관계&gt;&gt;
[컨트롤러 -&gt; 서비스(프록시) -&gt;joinPoint.proceed()-&gt; 서비스(실제)]</p>
<ul>
<li>AOP의 Around범위를 지정하면 서비스가 지정되면서 프록시 생성</li>
<li>컨트롤러가 프록시를 호출한 후 실제 서비스와 proceed가 함께 호출</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring boot - DB제작(2)]]></title>
            <link>https://velog.io/@neymar_10/Spring-boot-DB%EC%A0%9C%EC%9E%912</link>
            <guid>https://velog.io/@neymar_10/Spring-boot-DB%EC%A0%9C%EC%9E%912</guid>
            <pubDate>Wed, 21 Feb 2024 21:27:35 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="목차">목차</h3>
</blockquote>
<ol>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0">프로젝트 생성하기</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-View-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95">View 환경설정</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EC%B4%88">스프링 웹 개발 기초</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%98%88%EC%A0%9C">회원 관리 예제</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88%EA%B3%BC-%EC%9D%98%EC%A1%B4-%EA%B4%80%EA%B3%84">스프링 빈과 의존 관계</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%9B%B9-MVC-%EC%A0%9C%EC%9E%91">웹 MVC 제작</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB-%EC%A0%9C%EC%9E%911">DB 제작(1)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB%EC%A0%9C%EC%9E%912">DB 제작(2)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-AOP">AOP</a></li>
</ol>
<h3 id="4-더-간단한-db">4. 더 간단한 DB</h3>
<blockquote>
<p>JdbcTemplate</p>
</blockquote>
<pre><code> public class JdbcTemplateMemberRepository implements MemberRepository {

     private final JdbcTemplate jdbcTemplate;

     public JdbcTemplateMemberRepository(DataSource dataSource) {
        jdbcTemplate = new JdbcTemplate(dataSource);
    }
    ...
 }

 //JdbcTemplateMemberRepository.java</code></pre><ul>
<li>JdbcTemplate 사용을 위해 DataSource 주입</li>
</ul>
<pre><code> @Override
 public Member save(Member member) {
     SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate); 
    jdbcInsert.withTableName(&quot;member&quot;).usingGeneratedKeyColumns(&quot;id&quot;);

     Map&lt;String, Object&gt; parameters = new HashMap&lt;&gt;();
    parameters.put(&quot;name&quot;, member.getName()); //(필드이름, 값)

     Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters));
    member.setId(key.longValue());
     return member;
}

 // JdbcTemplateMemberRepository.java &gt; 저장 부분</code></pre><ul>
<li>jdbcInsert.withTableName(&quot;member&quot;).usingGeneratedKeyColumns(&quot;id&quot;);<ul>
<li>member테이블의 id를 자동 증가 값 추가(앞서 사용했던 id Generated Key와 같음)</li>
</ul>
</li>
<li>jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters));<ul>
<li>executeAndReturnKey 메서드는 데이터베이스에 쿼리를 실행</li>
<li>MapSqlParameterSource는 이름-값 쌍으로 매핑된 파라미터를 제공<ul>
<li>parameters를 Map형태로 설정한 이유!</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>@Override
public Optional&lt;Member&gt; findByName(String name) {
    List&lt;Member&gt; result = jdbcTemplate.query(&quot;select * from member where name = ?&quot;, memberRowMapper(), name);
     return result.stream().findAny();
} //이름으로 조회 기능

private RowMapper&lt;Member&gt; memberRowMapper() {
    return (rs, rowNum) -&gt; {
         Member member = new Member();
        member.setId(rs.getLong(&quot;id&quot;));
        member.setName(rs.getString(&quot;name&quot;));
         return member;
     };
} // memberRowMapper구현 코드</code></pre><ul>
<li>RowMapper는 데이터베이스의 반환 결과인 ResultSet을 객체로 변환해 주는 클래스<ul>
<li>기존 Jdbc에서는 각 값을 일일이 get해주어야하는 수고로움을 자동화 해줌</li>
</ul>
</li>
<li>jdbcTemplate.query(&quot;select * from member where name = ?&quot;, memberRowMapper(), name);<ul>
<li>sql에 맞는 name이 매칭되면 RowMapper후 매핑된 값을 반환</li>
</ul>
</li>
</ul>
<pre><code> @Bean
 public MemberRepository memberRepository() {
     //return new MemoryMemberRepository();
     //return new JdbcMemberRepository(dataSource);
     return new JdbcTemplateMemberRepository(dataSource);
}
// SpringConfig.java</code></pre><ul>
<li>스프링 컨테이너의 빈 모듈을 교체해줘야 함으로 JdbcTemplateMemberRepository(dataSource);로 교체<p align="center"><img src="https://velog.velcdn.com/images/neymar_10/post/8a3fae13-21ed-4ab3-8d7a-17eb8e93fd46/image.png">테스트 정상 작동</p>

</li>
</ul>
<blockquote>
<p>JPA</p>
</blockquote>
<p>&amp; 반복 코드는 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행</p>
<pre><code> implementation &#39;org.springframework.boot:spring-boot-starter-jdbc&#39;
 implementation &#39;org.springframework.boot:spring-boot-starter-data-jpa

 // build.gradle &gt; dependencies</code></pre><ul>
<li>build.Gradle의 Dependencies jdbc에서 jpa로 수정</li>
</ul>
<pre><code> spring.jpa.show-sql=true
 spring.jpa.hibernate.ddl-auto=none

 // application.properties</code></pre><ul>
<li>application.properties에 show-sql, hibernate.ddl 추가<ul>
<li>show-sql=true는 JPA가 생성하는 SQL을 출력</li>
<li>hibernate.ddl-auto=none는 자동으로 테이블 생성x<ul>
<li>auto=create는 엔티티 정보를 바탕으로 테이블 자동 생성</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code> @Entity
 public class Member {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
     private String name;
    ...
}

// Member.java</code></pre><ul>
<li>@Entity는 DB 테이블과 Jpa를 매핑<ul>
<li>DB에 생성했던 Member 테이블의 이름과 class의 이름으로 매핑</li>
</ul>
</li>
<li>@ID는 엔티티의 기본 키 필드를 나타냄<ul>
<li>DB 테이블의 기본 키 열과 매핑</li>
</ul>
</li>
<li>@GeneratedValue(strategy = GenerationType.IDENTITY)는 기본 키 값을 자동으로 생성하는 옵션</li>
<li>나머지 칼럼은 자동으로 매핑</li>
</ul>
<pre><code> public class JpaMemberRepository implements MemberRepository{

     private final EntityManager em; // ENtityManager 생성

     public JpaMemberRepository(EntityManager em) {
         this.em = em;
    }

     public Member save(Member member) {    // 저장
        em.persist(member);    // DB 저장
         return member;
    }

     public Optional&lt;Member&gt; findById(Long id) {    // id로 찾기
         Member member = em.find(Member.class, id); //@id 기반으로 검색
         return Optional.ofNullable(member);
    }

     public List&lt;Member&gt; findAll() {    // 모두 찾기
         return em.createQuery(&quot;select m from Member m&quot;, Member.class)
                  .getResultList(); // alias로 전체 조회
    }

    public Optional&lt;Member&gt; findByName(String name) {  // 이름으로 찾기
         List&lt;Member&gt; result = em.createQuery(&quot;select m from Member m where m.name = :name&quot;, Member.class)
                .setParameter(&quot;name&quot;, name)
                .getResultList();
         return result.stream().findAny();
    }
 }

 // JpaMemberRepository.java</code></pre><ul>
<li>EntityManager: 엔티티의 영구 저장과 관리를 담당</li>
<li>persist(): 메서드는 전달된 엔티티 객체를 영구 저장소에 저장하고, 관리 상태로 전환</li>
<li>find(table.class, id): 주어진 엔티티 클래스와 @id id를 기반으로 데이터베이스에서 엔티티를 찾음<ul>
<li>회원ID 기반이 아닌 @ID로 설정한 ID로 조회</li>
</ul>
</li>
<li>createQuery(sql, T.class): 테이블 클래스에 Sql 쿼리 전송<ul>
<li>*대신 alias를 사용해 전체 조회</li>
</ul>
</li>
</ul>
<p align="center"><img src="https://velog.velcdn.com/images/neymar_10/post/aec415a5-4bb3-4b64-8c12-94295a928eb6/image.png">테스트 및 Hibernate=true 확인</p>

<blockquote>
<p>스프링 데이터 JPA</p>
</blockquote>
<ul>
<li>리포지토리에 구현 클래스 없이 인터페이스 만으로 개발 가능</li>
<li>CRUD 기능 제공</li>
<li>스프링 데이터 JPA는 JPA를 편리하게 사용하도록 도와주는 기술<ul>
<li>JPA 완전 이해 필요!</li>
</ul>
</li>
</ul>
<pre><code> public interface SpringDataJpaMemberRepository extends JpaRepository&lt;Member, Long&gt;, MemberRepository {
     @Override
     Optional&lt;Member&gt; findByName(String name);
 }

 // SpringDataJpaMemberRepository.java(인터페이스)</code></pre><ul>
<li>스프링 데이터 JPA가 SpringDataJpaMemberRepository를 스프링 빈으로 자동 등록<ul>
<li>인터페이스를 보고 자동으로 등록해줌</li>
</ul>
</li>
<li>JpaRepository는 기본적으로 사용되는 조회, 저장, 삭제 등을 포함하고 있음<ul>
<li>findBy(컬럼명) 등으로 간단하게 기본적인 조회 가능</li>
</ul>
</li>
</ul>
<pre><code>    ...
 private final MemberRepository memberRepository;

 public SpringConfig(MemberRepository memberRepository) {
     this.memberRepository = memberRepository;
 }

    @Bean
 public MemberService memberService() {
     return new MemberService(memberRepository);
 }
     ...

 // SpringConfig.java</code></pre><ul>
<li>memberRepository만 등록해주면 자동으로 JPA로 연결가능</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring boot - DB 제작(1)]]></title>
            <link>https://velog.io/@neymar_10/Spring-boot-DB-%EC%A0%9C%EC%9E%911</link>
            <guid>https://velog.io/@neymar_10/Spring-boot-DB-%EC%A0%9C%EC%9E%911</guid>
            <pubDate>Tue, 20 Feb 2024 20:38:10 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="목차">목차</h3>
</blockquote>
<ol>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0">프로젝트 생성하기</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-View-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95">View 환경설정</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EC%B4%88">스프링 웹 개발 기초</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%98%88%EC%A0%9C">회원 관리 예제</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88%EA%B3%BC-%EC%9D%98%EC%A1%B4-%EA%B4%80%EA%B3%84">스프링 빈과 의존 관계</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%9B%B9-MVC-%EC%A0%9C%EC%9E%91">웹 MVC 제작</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB-%EC%A0%9C%EC%9E%911">DB 제작(1)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB%EC%A0%9C%EC%9E%912">DB 제작(2)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-AOP">AOP</a></li>
</ol>
<h3 id="1-h2-데이터베이스">1. H2 데이터베이스</h3>
<blockquote>
<p>H2 데이터베이스 설치</p>
</blockquote>
<p><a align="center"><img src="https://velog.velcdn.com/images/neymar_10/post/f57886dc-d5cc-49c0-960f-cfb64b8bfc13/image.png" width=400></a></p>
<ul>
<li><a href="https://www.h2database.com">https://www.h2database.com</a> 방문 후 다운로드!</li>
</ul>
<blockquote>
<p>H2 데이터베이스 초기 설정</p>
</blockquote>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/2bc9f9a2-6598-4f3e-9d4f-449bb5119d53/image.png">h2.jar File로 접속</th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/7d96e4cf-6e00-4f0a-a7c0-e29129dff353/image.png"> h2접속(localhost:8082)</th>
</tr>
</thead>
</table>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/f8141e79-5569-4f95-b671-c54570a685d6/image.png">test.mv.DB 생성 확인</th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/8277c2ba-fc0d-4437-b365-80cc9669b050/image.png">test.db 화면</th>
</tr>
</thead>
</table>
<ol>
<li>Install 후 prgramFile &gt; H2 &gt; bin &gt; h2.Jar File로 접속</li>
<li>페이지 로드 후 연결(img2)<ul>
<li>페이지 주소명 앞 부분 localhost:8082로 변경</li>
</ul>
</li>
<li>Users에 test.mv.DB파일이 생성된 것을 확인할 수 있다.<ul>
<li>test파일 생성한 후에는 img2의 URL을 jdbc:h2:tcp://localhost/~/test 변경 후 접속</li>
</ul>
</li>
<li>정상적으로 testDB를 생성한 것을 확인 가능</li>
</ol>
<blockquote>
<p>H2 데이터베이스 사용해보기</p>
</blockquote>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/105fd28a-89f7-42c7-9591-d75554a1b993/image.png"></th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/af44b0d0-877b-4580-924e-b1d4f054257f/image.png"></th>
</tr>
</thead>
<tbody><tr>
<td>* Member 테이블을 생성된 것을 확인할 수 있다.</td>
<td></td>
</tr>
<tr>
<td>* bigint는 Java에서 Long 자료형을 의미</td>
<td></td>
</tr>
<tr>
<td>* 칼럼 값을 자동으로 증가를 위한 generated by default as identify 설정</td>
<td></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/729d2840-cea2-4bc7-bba8-0cb399c9d495/image.png"></th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/3e383900-34dc-4425-9a84-dd10ead57371/image.png"></th>
</tr>
</thead>
<tbody><tr>
<td>* Member 테이블에 홍길동 추가</td>
<td></td>
</tr>
<tr>
<td>* generated by default as identify를 설정했기 때문에 pk없이 추가 가능</td>
<td></td>
</tr>
<tr>
<td>* (1, 홍길동)이라는 값이 제대로 들어있는 것을 조회해볼 수 있다.</td>
<td></td>
</tr>
</tbody></table>
<h3 id="2-순수jdbc">2. 순수JDBC</h3>
<blockquote>
<p>IntelliJ 환경 설정</p>
</blockquote>
<pre><code>implementation &#39;org.springframework.boot:spring-boot-starter-jdbc&#39;
runtimeOnly &#39;com.h2database:h2&#39;

//build.gradle에 라이브러리 추가</code></pre><ul>
<li>build.gradle에 spring-boot-starter-jdbc 추가<ul>
<li>Spring에 Jdbc를 연결하기 위해 추가</li>
</ul>
</li>
<li>com.h2database:h2 라이브러리 추가</li>
</ul>
<pre><code>spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa

//resources &gt; application.properties에 추가</code></pre><ul>
<li>스프링에 H2를 연결했기 때문에 접속을 위한 설정 추가<ul>
<li>H2 DB생성 및 연결 시 사용하는 값과 동일</li>
<li>스프링부트 2.4부터는 spring.datasource.username=sa를 꼭 추가</li>
</ul>
</li>
</ul>
<blockquote>
<p>JDBC 리포지토리 구현</p>
</blockquote>
<pre><code>public class JdbcMemberRepository implements MemberRepository{

    //javax.sql.DataSource 사용
    private final DataSource dataSource;

    public JdbcMemberRepository(DataSource dataSource) {
        this.dataSource = dataSource;
    }</code></pre><ul>
<li>JdbcMemberRepository 생성</li>
<li>spring.datasource는 DB 연결에 관한 예약된 key<ul>
<li>driver-class-name, url, username, password 속성 필요</li>
</ul>
</li>
<li>this.dataSource = dataSource를 통해 스프링 부트의 DBSource 주입<ul>
<li>application.properties에서 세팅한 값을 통해 스프링 부트 DBSource 제작</li>
</ul>
</li>
</ul>
<pre><code>@Override
    public Member save(Member member) {
        String sql = &quot;insert into member(name) values(?)&quot;;
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            conn = getConnection(); // DB 연결
            // sql 전송
            pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            pstmt.setString(1, member.getName()); // sql의 첫번째 &#39;?&#39;에 대입
            pstmt.executeUpdate(); // DB에 데이터 전송
            rs = pstmt.getGeneratedKeys();
            if (rs.next()) {
                member.setId(rs.getLong(1));
            } else {
                throw new SQLException(&quot;id 조회 실패&quot;);
            }
            return member;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }
 // JdbcMemberRepositiory &gt; Save 기능</code></pre><ul>
<li>pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);의 Statement.RETURN_GENERATED_KEYS는 기존 DB내에서 ID값을 자동으로 생성되도록 설정했으므로 Spring에서 값을 받기 위해 사용한 옵션<ul>
<li>즉 rs = pstmt.getGeneratedKeys();의 값을 받기 위해 사용</li>
<li>pstmt.executeUpdate();는 값을 받아올 수 없음</li>
</ul>
</li>
<li>연결한 기능을 모두 사용한 후 항상 close로 마무리<ul>
<li>계속 연결되어 있으면 오류 및 장애 발생</li>
</ul>
</li>
</ul>
<pre><code>@Override
    public List&lt;Member&gt; findAll() {
        String sql = &quot;select * from member&quot;;

        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            conn = getConnection(); // DB 연결
            pstmt = conn.prepareStatement(sql); // sql 전송

            rs = pstmt.executeQuery(); // 데이터 받음

            List&lt;Member&gt; members = new ArrayList&lt;&gt;();
            while (rs.next()) { // 값이 있을 때까지 반복
                Member member = new Member();
                member.setId(rs.getLong(&quot;id&quot;));
                member.setName(rs.getString(&quot;name&quot;));
                members.add(member);
            }

            return members;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs); // 연결 종료
        }
    }
 // JdbcMemberRepository &gt; 조회(findAll) 기능</code></pre><ul>
<li>executeQuery();는 excuteUpdate()와 달리 쿼리 값을 받음</li>
</ul>
<blockquote>
<p>SpringConfig 설정</p>
</blockquote>
<pre><code>@Bean
public MemberRepository memberRepository(){
    //return new MemoryMemberRepository();
    return new JdbcMemberRepository(dataSource);
}
//SpringConfig.java</code></pre><ul>
<li>컨테이너 안의 MemoryMemberRepository를 JdbcMemberRepository로 변경해 쉽게 전환 가능<ul>
<li>서비스-메모리(리)의 연결을 서비스-Jdbc(리)로 변경</li>
</ul>
</li>
</ul>
<blockquote>
<p>돌려보기</p>
</blockquote>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/ffc8cb58-b5f0-4dd2-b93c-3c5c6fe28ddb/image.png"></th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/a2758899-7b0a-4887-a08f-fd6e138bcc47/image.png"></th>
</tr>
</thead>
<tbody><tr>
<td>* H2 DB에서 넣어놨던 Member 테이블 값이 조회되는 것을 확인 가능</td>
<td></td>
</tr>
<tr>
<td>* Spring과 DB가 잘 연결되어 있음을 확인 가능</td>
<td></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/283fcc5c-c598-47c4-aef3-1fe673d3d627/image.png"></th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/29ba797d-23ea-40d9-af4a-ddfc97a10335/image.png"></th>
</tr>
</thead>
<tbody><tr>
<td>* 데이터 삽입 또한 잘되는 것을 확인할 수 있다.</td>
<td></td>
</tr>
</tbody></table>
<h3 id="3-스프링-통합-테스트">3. 스프링 통합 테스트</h3>
<blockquote>
<p>코드 작성</p>
</blockquote>
<pre><code> @SpringBootTest
 @Transactional
 class MemberServiceIntegrationTest {
    @Autowired MemberService memberService;
    @Autowired MemberRepository memberRepository;
    @Test
     public void 회원가입() throws Exception {
         //Given
         Member member = new Member();
        member.setName(&quot;hello&quot;);
         //When
         Long saveId = memberService.join(member);
        //Then
         Member findMember = memberRepository.findById(saveId).get();
         assertEquals(member.getName(), findMember.getName());
    }
    @Test
     public void 중복_회원_예외() throws Exception {
         //Given
         Member member1 = new Member();
        member1.setName(&quot;spring&quot;);
         Member member2 = new Member();
        member2.setName(&quot;spring&quot;);
         //When
        memberService.join(member1);
         IllegalStateException e = assertThrows(IllegalStateException.class,
                () -&gt; memberService.join(member2));//예외가 발생해야 한다.
         assertThat(e.getMessage()).isEqualTo(&quot;이미 존재하는 회원입니다.&quot;);
    }
 }

 // MemberServiceIntegrationTest.java</code></pre><table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/53251555-2ab3-472b-a103-1a3f3c41c378/image.png" width=400>회원가입</th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/38a88225-3610-4efe-88d4-e8b2a2b93d98/image.png">테스트 확인</th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/bccd9687-a62d-46b3-82a3-619dae81cc63/image.png">DB에 커밋 X</th>
</tr>
</thead>
</table>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/7822e84c-2445-47a6-9d52-035b7acba891/image.png" width=400>@Commit 후 실행</th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/a3e38fc9-8723-4ae8-a0a5-20ebe6f63a29/image.png">DB에 커밋 O</th>
</tr>
</thead>
</table>
<ul>
<li>@SpringBootTest는 스프링 컨테이너와 테스트 실행<ul>
<li>JDBC를 이용하기 위해선 스프링 컨테이너 필요!</li>
</ul>
</li>
<li>@Transactional는 독립적인 DB테스트 실행 가능<ul>
<li>테스트 시작 전에 트렌젝션을 시작하고 테스트 완료 후 항상 롤백</li>
<li>@Commit을 통해 커밋도 가능</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring boot - 웹 MVC 제작]]></title>
            <link>https://velog.io/@neymar_10/Spring-boot-%EC%9B%B9-MVC-%EC%A0%9C%EC%9E%91</link>
            <guid>https://velog.io/@neymar_10/Spring-boot-%EC%9B%B9-MVC-%EC%A0%9C%EC%9E%91</guid>
            <pubDate>Mon, 19 Feb 2024 14:36:16 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="목차">목차</h3>
</blockquote>
<ol>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0">프로젝트 생성하기</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-View-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95">View 환경설정</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EC%B4%88">스프링 웹 개발 기초</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%98%88%EC%A0%9C">회원 관리 예제</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88%EA%B3%BC-%EC%9D%98%EC%A1%B4-%EA%B4%80%EA%B3%84">스프링 빈과 의존 관계</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%9B%B9-MVC-%EC%A0%9C%EC%9E%91">웹 MVC 제작</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB-%EC%A0%9C%EC%9E%911">DB 제작(1)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB%EC%A0%9C%EC%9E%912">DB 제작(2)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-AOP">AOP</a></li>
</ol>
<h3 id="1-회원-웹-기능">1. 회원 웹 기능</h3>
<blockquote>
<p>홈 화면 추가</p>
</blockquote>
<pre><code>@Controller
public class HomeController {
    @GetMapping(&quot;/&quot;)    // localhost:8080
    public String home(){
        return &quot;home&quot;;
    }
}
// HomeController.java</code></pre><ul>
<li>(&quot;/&quot;)는 index.html대신 localhost:8080에서 기본 동작<ul>
<li>spring 순서: 스프링 컨테이너(컨트롤러) -&gt; static 순 
즉 index.html보다 &quot;/&quot;이 먼저 동작하게 됨</li>
</ul>
</li>
</ul>
<blockquote>
<p>등록 폼 추가</p>
</blockquote>
<pre><code>    ...
@GetMapping(&quot;/members/new&quot;)    // /members/new를 받음
public String createForm() {
    return &quot;members/createForm&quot;;    //members/createFrom.html 이동
}

@PostMapping(value = &quot;/members/new&quot;)    // members/new를 받음
public String create(MemberForm form) {
    Member member = new Member();
    member.setName(form.getName());
    memberService.join(member);    //회원 가입
    return &quot;redirect:/&quot;;    // 홈화면으로 이동
}
    ...

//MemberController.java 추가</code></pre><ul>
<li>이름을 summit하게 되면 리포지토리로 값 전달 후 홈 화면 이동</li>
<li>memberService와 연결된 컨트롤러인 memberController안에서 동작할 수 밖에 없음</li>
</ul>
<pre><code>&lt;div class=&quot;container&quot;&gt;
    &lt;form action=&quot;/members/new&quot; method=&quot;post&quot;&gt;
        &lt;div
                class=&quot;form-group&quot;&gt;
            &lt;label
                    for=&quot;name&quot;&gt;이름&lt;/label&gt;
            &lt;input
                    type=&quot;text&quot; id=&quot;name&quot; name=&quot;name&quot; placeholder=&quot;이름을 입력하세요&quot;&gt;
        &lt;/div&gt;
        &lt;button type=&quot;submit&quot;&gt;등록&lt;/button&gt;
    &lt;/form&gt;
&lt;/div&gt;
//createForm.html</code></pre><ul>
<li>summit하게되면 리포지토리에 저장(save()함수 실행)<ul>
<li>join() 제작 시 추가한 중복 값 예외 코드때문에 동일한 값 저장X</li>
</ul>
</li>
<li>action=&quot;/members/new&quot;로 전송하기 때문에 @postMapping을 &quot;/members/new&quot;로 받게 됨<ul>
<li>action=&quot;hi&quot;이면 @postMapping(&quot;/members/hi&quot;)로 전달 가능</li>
<li>createForm.html이 members안의 파일임으로 action=&quot;hi&quot;는 /members/hi로 전송하게 됨</li>
</ul>
</li>
</ul>
<blockquote>
<p>조회 기능 추가</p>
</blockquote>
<pre><code>@GetMapping(&quot;/members&quot;)    //     /members시 동작
public String list(Model model){
    List&lt;Member&gt; members = memberService.findMembers();
    model.addAttribute(&quot;members&quot;,members);
    return &quot;members/memberList&quot;;
}</code></pre><ul>
<li>member의 전체 리스트를 조회 후 List에 저장</li>
<li>&quot;members&quot;이름으로 model에 members 리스트를 담은 후 memberList.html로 전송</li>
</ul>
<pre><code>&lt;div class=&quot;container&quot;&gt;
    &lt;div&gt;
        &lt;table&gt;
            &lt;thead&gt;
            &lt;tr&gt;
                &lt;th&gt;#&lt;/th&gt;
                &lt;th&gt;이름&lt;/th&gt;
            &lt;/tr&gt;
            &lt;/thead&gt;
            &lt;tbody&gt;
            &lt;tr th:each=&quot;member : ${members}&quot;&gt;
                &lt;td th:text=&quot;${member.id}&quot;&gt;&lt;/td&gt;
                &lt;td th:text=&quot;${member.name}&quot;&gt;&lt;/td&gt;
            &lt;/tr&gt;
            &lt;/tbody&gt;
        &lt;/table&gt;
    &lt;/div&gt;
&lt;/div&gt;</code></pre><ul>
<li>each를 통해 ${members}로 받은 members리스트를 반복<ul>
<li>반복되는 각 member의 id와 name을 출력</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring boot - 스프링 빈과 의존 관계]]></title>
            <link>https://velog.io/@neymar_10/Spring-boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88%EA%B3%BC-%EC%9D%98%EC%A1%B4-%EA%B4%80%EA%B3%84</link>
            <guid>https://velog.io/@neymar_10/Spring-boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88%EA%B3%BC-%EC%9D%98%EC%A1%B4-%EA%B4%80%EA%B3%84</guid>
            <pubDate>Mon, 19 Feb 2024 12:42:49 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="목차">목차</h3>
</blockquote>
<ol>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0">프로젝트 생성하기</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-View-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95">View 환경설정</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EC%B4%88">스프링 웹 개발 기초</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%98%88%EC%A0%9C">회원 관리 예제</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88%EA%B3%BC-%EC%9D%98%EC%A1%B4-%EA%B4%80%EA%B3%84">스프링 빈과 의존 관계</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%9B%B9-MVC-%EC%A0%9C%EC%9E%91">웹 MVC 제작</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB-%EC%A0%9C%EC%9E%911">DB 제작(1)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB%EC%A0%9C%EC%9E%912">DB 제작(2)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-AOP">AOP</a></li>
</ol>
<h3 id="1-컴포넌트-스캔과-자동-의존-관계">1. 컴포넌트 스캔과 자동 의존 관계</h3>
<blockquote>
<p>member 컨트롤러 생성</p>
</blockquote>
<pre><code>@Controller    //@Component 대체 가능
public class MemberController {
    private final MemberService memberService;

    @Autowired //컨트롤러 서비스 연결     //에러 발생 - 서비스, 리포지토리 등록x
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }
}</code></pre><ul>
<li>생성자에 @Autowired가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다.<ul>
<li>객체 의존관계를 외부에서 넣어주는 것을 DI (Dependency Injection), 의존성 주입이라 한다.</li>
</ul>
</li>
<li>실행 시 에러 발생<ul>
<li>서비스, 리포지토리 등록X</li>
</ul>
</li>
<li>helloController는 스프링이 제공하는 컨트롤러여서 스프링 빈으로 자동 등록</li>
<li>@Component 스프링에서 사용되는 가장 기본적인 annotation<ul>
<li>scan-auto-detection과 DI을 사용하기 위해서 스프링 컨테이너에 등록하는 기본 annotation</li>
<li>Spring에서는 컨트롤러, 서비스, 리포지토리 annotation 추천</li>
</ul>
</li>
</ul>
<blockquote>
<p>서비스, 리포지토리 객체 생성</p>
</blockquote>
<pre><code>@Service    //서비스 객체 등록
public class MemberService {
    ...
} //MemberService.java

@Repository    //리포지토리 객체 등록
public class MemoryMemberRepository implements MemberRepository{
    ...
}//MemoryMemberRepository.java</code></pre><ul>
<li>서비스, 리포지토리 객체 등록<ul>
<li>객체들 간의 연결을 안해주어 여전히 에러 발생</li>
</ul>
</li>
<li>스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본으로 싱글톤으로 등록</li>
</ul>
<blockquote>
<p>객체 간 연결(@Autowired)</p>
</blockquote>
<pre><code>    ...
@Autowired //서비스 객체 연결
public MemberController(MemberService memberService) {
   this.memberService = memberService;
}    
    ...    // MemberConcroller.java

    ...
@Autowired // 리포지토리 객체 연결
public MemberService(MemberRepository memberRepository) {
    this.memberRepository = memberRepository;
}    
    ...    //MemberService.java</code></pre><ul>
<li>동작 시 스프링 애플리케이션이 정상 작동하게 된다.<ul>
<li>스프링 컨테이너에 등록 되있을 시 정상 작동(주의)</li>
</ul>
</li>
<li>필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방법이 있다. 의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장<ul>
<li>보안, 편의성 좋음</li>
</ul>
</li>
</ul>
<h3 id="2-자바-코드로-직접-스프링-빈-등록">2. 자바 코드로 직접 스프링 빈 등록</h3>
<blockquote>
<p>SpringConfig 생성</p>
</blockquote>
<pre><code>@Configuration
public class SpringConfig {

    @Bean    // 컨테이너에 등록
    public MemberService memberService(){
        return new MemberService(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository(){
        return new MemoryMemberRepository();
    }
}    //SpringConfig.java</code></pre><ul>
<li>@Bean 서비스, 리포지토리를 통해 빈에 등록<ul>
<li>MemberService(<span style="color:red">memberRepository()</span>);을 통해 서비스와 리포지토리 연결</li>
</ul>
</li>
</ul>
<blockquote>
<p>장단점</p>
</blockquote>
<ul>
<li>실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용</li>
<li>정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록<ul>
<li>코드를 수정할 필요 없이 쉽게 변경 가능</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring boot - 회원 관리 예제 ]]></title>
            <link>https://velog.io/@neymar_10/Spring-boot-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%98%88%EC%A0%9C</link>
            <guid>https://velog.io/@neymar_10/Spring-boot-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%98%88%EC%A0%9C</guid>
            <pubDate>Mon, 19 Feb 2024 09:33:47 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="목차">목차</h3>
</blockquote>
<ol>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0">프로젝트 생성하기</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-View-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95">View 환경설정</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EC%B4%88">스프링 웹 개발 기초</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%98%88%EC%A0%9C">회원 관리 예제</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88%EA%B3%BC-%EC%9D%98%EC%A1%B4-%EA%B4%80%EA%B3%84">스프링 빈과 의존 관계</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%9B%B9-MVC-%EC%A0%9C%EC%9E%91">웹 MVC 제작</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB-%EC%A0%9C%EC%9E%911">DB 제작(1)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB%EC%A0%9C%EC%9E%912">DB 제작(2)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-AOP">AOP</a></li>
</ol>
<h3 id="1-비즈니스-요구사항-정리">1. 비즈니스 요구사항 정리</h3>
<blockquote>
<p>요구사항</p>
</blockquote>
<ul>
<li>데이터: 회원ID, 이름</li>
<li>기능: 회원 등록, 조회</li>
<li>아직 데이터 저장소가 선정되지 않음(가상의 시나리오)</li>
</ul>
<h3 id="2-회원-도메인과-리포지토리-만들기">2. 회원 도메인과 리포지토리 만들기</h3>
<blockquote>
<p>회원 객체</p>
</blockquote>
<pre><code>public class Member {
     private Long id;
     private String name;
     public Long getId() {
         return id;
     }
     public void setId(Long id) {
         this.id = id;
    }
    public String getName() {
         return name;
    }
     public void setName(String name) {
         this.name = name
    }
 }</code></pre><ul>
<li>getter and setter를 이용해 id, name에 관한 Member클래스 생성<ul>
<li>단축키 Alt + Insert 사용</li>
</ul>
</li>
</ul>
<blockquote>
<p>회원 리포지토리</p>
</blockquote>
<pre><code> public interface MemberRepository {
    Member save(Member member);
    //null값을 optional로 가공 후 반환
    Optional&lt;Member&gt; findById(Long id); 
    Optional&lt;Member&gt; findByName(String name);
    List&lt;Member&gt; findAll();
}
// 회원 리포지토리 인터페이스</code></pre><ul>
<li>리포지토리에 가공하기 위한 기본적인 인터페이스들을 생성</li>
</ul>
<pre><code>public class MemoryMemberRepository implements MemberRepository{

    private  static Map&lt;Long, Member&gt; store = new HashMap&lt;&gt;();
    private static long sequence = 0L;

    @Override
    public Member save(Member member) {
        member.setId(++sequence);
        store.put(member.getId(),member);
        return member;
    }

    @Override
    public Optional&lt;Member&gt; findById(Long id) {
        return Optional.ofNullable(store.get(id));  //NULL값 반환 시 Optional로 감싸서 NULL값을 반환
    }

    @Override
    public Optional&lt;Member&gt; findByName(String name) {
        return store.values().stream()     // 루프
                .filter(member -&gt; member.getName().equals(name))  // name과 값이 같을 시 필터링
                .findAny();     // 찾으면 반환
    }

    @Override
    public List&lt;Member&gt; findAll() {
        return new ArrayList&lt;&gt;(store.values());
    }

    public void clearStore(){
        store.clear();
    }
}
//회원 리포지토리 메모리 구현체</code></pre><ul>
<li>생성한 인터페이스의 기능을 구현<ul>
<li>Map&lt;&gt;을 통해 회원 정보 저장</li>
<li>편리성을 위해 List<Member> findAll()에서 List 사용</li>
<li>store.clear(); 함수는 리포지토리 초기화해주는 함수</li>
</ul>
</li>
</ul>
<blockquote>
<p>테스트 케이스 작성</p>
</blockquote>
<ul>
<li>기존에는 main메소드, 컨트롤러에서 테스트를 진행<ul>
<li>준비과정, 반복진행 등 어려움 발생</li>
</ul>
</li>
<li>JUnit이라는 프레임워크로 테스트를 실행해서 이러한 문제를 해결</li>
</ul>
<pre><code>class MemoryMemberRepositoryTest {
    MemoryMemberRepository repository = new MemoryMemberRepository();


      @AfterEach    // 각 테스트 진행 후 동작
    public void afterEach(){
        repository.clearStore();
      }    

    @Test
    public void save(){
          //given
        Member member = new Member();
        member.setName(&quot;spring&quot;);
        //when
        repository.save(member);
        //then
        Member result = repository.findById(member.getId()).get();
        Assertions.assertEquals(result,member);   // (값, 비교값) 동일 시 무응답, 다를 시 값 반환
        assertThat(member).isEqualTo(result);
    }

    @Test
    public void findByName(){
        Member member1 = new Member();
        member1.setName(&quot;spring1&quot;);
        repository.save(member1);

        Member member2 = new Member();
        member2.setName(&quot;spring2&quot;);
        repository.save(member2);

        Member result = repository.findByName(&quot;spring1&quot;).get();

        assertThat(result).isEqualTo(member1);
    }

    @Test
    public void findAll(){
        Member member1 = new Member();
        member1.setName(&quot;spring1&quot;);
        repository.save(member1);

        Member member2 = new Member();
        member2.setName(&quot;spring1&quot;);
        repository.save(member2);

        List&lt;Member&gt; result = repository.findAll();
        assertThat(result.size()).isEqualTo(2);
    }
}
//회원 리포지토리 테스트 케이스</code></pre><ul>
<li>단축키 Ctrl + Shift + T 사용</li>
<li>각 테스트 케이스를 작성하여 main, 컨트롤러에서 하는 것 보다 안전하고 쉽게 테스트 가능</li>
<li>전체 테스트 케이스 동작 시 findByName과 findAll의 member값이 중복되어 에러 발생<ul>
<li>@AfterEach를 통해 각 테스트마다 <span style="color: red">store.clear();</span> 진행(스토리지 초기화)</li>
</ul>
</li>
<li>assertThat(비교값).isEqualTo(비교 대상); 값이 같을 시 true, false를 반환(비교 함수)</li>
</ul>
<h3 id="3-회원-서비스-개발">3. 회원 서비스 개발</h3>
<blockquote>
<p>서비스 구현</p>
</blockquote>
<pre><code>public class MemberService {
    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {  
        this.memberRepository = memberRepository;
    }

    public Long join(Member member){    // 회원 가입
        validateDuplicateMember(member);    // 중복 회원 검증
        memberRepository.save(member);
        return member.getId();
    }

    private void validateDuplicateMember(Member member) {   // 중복회원검증
        memberRepository.findByName(member.getName())
                .ifPresent(m -&gt; {                       // findByName값이 도출될 시 동작
                    throw new IllegalStateException(&quot;이미 존재하는 회원입니다.&quot;);
                });
    }

    public List&lt;Member&gt; findMembers(){
        return memberRepository.findAll();
    }   // 전체 맴버 출력
    //Java에서는 대부분 List 사용(편의성)
    public Optional&lt;Member&gt; findOne(Long memberId){
        return memberRepository.findById(memberId);
    }   // 회원 검색(Id)
}
// 회원 서비스 </code></pre><ul>
<li>서비스는 비즈니스적인 요소(회원가입, 조회 등)를 구현<pre><code>before&gt; private final MemberRepository memberRepository = 
new MemoryMemberRepository();
</code></pre></li>
</ul>
<p>after&gt; public MemberService(MemberRepository memberRepository) {
     this.memberRepository = memberRepository;
  }</p>
<pre><code>* 회원 서비스가 메모리 회원 리포지토리를 직접 생성(사실 상 기존 리포지토리와 다른 메모리 리포지토리 생성)
  * 회원 리포지토리의 코드가 회원 서비스 코드를 DI 가능하게 변경

&gt; 서비스 테스트 케이스
</code></pre><p>class MemberServiceTest {</p>
<pre><code>MemberService memberService;
MemoryMemberRepository memberRepository;

@BeforeEach    // 항상 새로운 객체 생성
public void beforeEach(){
    memberRepository = new MemoryMemberRepository();
    memberService = new MemberService(memberRepository);
}

@AfterEach    // 리포지토리 초기화
public void afterEach(){
    memberRepository.clearStore();
}

@Test
void 회원가입() {
    //given
    Member member = new Member();
    member.setName(&quot;park&quot;);
    //when
    Long saveId = memberService.join(member);
    //then
    Member findMember = memberService.findOne(saveId).get();
    assertThat(member.getName()).isEqualTo(findMember.getName());

}

@Test
void 중복회원() {
    //given
    Member member1 = new Member();
    member1.setName(&quot;park&quot;);

    Member member2 = new Member();
    member2.setName(&quot;park&quot;);

    //when
    memberService.join(member1);
    IllegalStateException e = assertThrows(IllegalStateException.class,
            () -&gt; memberService.join(member2));//예외가 발생해야 한다.
    assertThat(e.getMessage()).isEqualTo(&quot;이미 존재하는 회원입니다.&quot;);

    //then
}</code></pre><p>}</p>
<pre><code>*  @BeforeEach 각 테스트 실행 전에 호출되어 테스트가 서로 영향이 없도록 항상 새로운 객체를 생성하고, 의존관계도 새로 맺어준다
* 테스트 케이스는 main 코드가 아니므로 가독성을 위한 클래스 명으로 생성 가능(회원가입, 중복회원 등)
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot - 스프링 웹 개발 기초]]></title>
            <link>https://velog.io/@neymar_10/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@neymar_10/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Sun, 11 Feb 2024 07:59:24 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="목차">목차</h3>
</blockquote>
<ol>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0">프로젝트 생성하기</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-View-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95">View 환경설정</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EC%B4%88">스프링 웹 개발 기초</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%98%88%EC%A0%9C">회원 관리 예제</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88%EA%B3%BC-%EC%9D%98%EC%A1%B4-%EA%B4%80%EA%B3%84">스프링 빈과 의존 관계</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%9B%B9-MVC-%EC%A0%9C%EC%9E%91">웹 MVC 제작</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB-%EC%A0%9C%EC%9E%911">DB 제작(1)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB%EC%A0%9C%EC%9E%912">DB 제작(2)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-AOP">AOP</a></li>
</ol>
<h3 id="1-정적-컨텐츠">1. 정적 컨텐츠</h3>
<blockquote>
<p>정적 페이지 제작</p>
</blockquote>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/5bf6469e-5fb8-43f4-9418-2f17d77fa267/image.png" width=900>static &gt; static.html 제작</th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/9d7b3e9f-96aa-4529-9083-a450141b45ae/image.png" alt="">8080:static.html</th>
</tr>
</thead>
</table>
<blockquote>
<p>정적 페이지의 구동 과정</p>
</blockquote>
<ul>
<li>localhost:8080/static.html -&gt; 톰캣 서버 -&gt; 스프링 컨테이너<ul>
<li>static 관련 컨트롤러 확인</li>
</ul>
</li>
<li>없을 시 resources: static/static.html 확인<ul>
<li>확인 시 출력</li>
</ul>
</li>
</ul>
<h3 id="2-mvc와-템플릿-엔진">2. MVC와 템플릿 엔진</h3>
<ul>
<li>MVC: Model, View, Controller<blockquote>
<p>Controller</p>
</blockquote>
</li>
</ul>
<pre><code>@GetMapping(&quot;hello-mvc&quot;)
public String helloMvc(@RequestParam(&quot;name&quot;) String name, Model model){
    model.addAttribute(&quot;name&quot;, name);
    return &quot;hello-template&quot;;
}
//controller

&lt;!DOCTYPE HTML&gt;
&lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;
&lt;head&gt;
    &lt;title&gt;hello&lt;/title&gt;
    &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p th:text=&quot;&#39;hello &#39; + ${name}&quot;&gt; hello empty!&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
//resources/templates/hello-template.html</code></pre><table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/7477737b-535c-4314-85c3-2f6b50c06965/image.png" width=700>hello-mvc</th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/d6264bbd-0980-470b-8006-58c3fc959d05/image.png" alt="">hello-mvc?name=spring!!</th>
</tr>
</thead>
</table>
<p>hello-mvc가 호출될 시 name 파라미터를 받아 hello-template에 model(&quot;name&quot;, name)로 전송하게 된다.</p>
<ul>
<li>Required request parameter &#39;name&#39; for method parameter type String is not present 즉 name 파라미터의 값이 없다는 애러가 발생하게 된다.</li>
<li>hello-mvc?name=spring!!을 통해 파라미터 값을 지정해 hello spring!! 출력<ul>
<li>public String helloMvc(<span style="color:red">@RequestParam(&quot;name&quot;)</span> String name, Model model) 생성 시 파라미터&quot;name&quot;의 값을 받기로 한 것을 확인할 수 있다.</li>
</ul>
</li>
</ul>
<blockquote>
<p>MVC, 템플릿 엔진 구동 과정</p>
</blockquote>
<ul>
<li>localhost:8080/hello-mvc -&gt; 톰캣 서버 -&gt; 스프링 컨테이너<ul>
<li>hello-mvc 관련 컨트롤러 확인</li>
<li>확인 후 return hello-template, model(&quot;name&quot;, name) 전송</li>
</ul>
</li>
<li>컨트롤러 -&gt; viewResolver -&gt; hello-template.html<ul>
<li>model 값을 통해 html 변환 후 처리</li>
</ul>
</li>
</ul>
<h3 id="3-api">3. API</h3>
<blockquote>
<p>String 전송</p>
</blockquote>
<pre><code>@GetMapping(&quot;hello-string&quot;)
@ResponseBody // http body에 직접 삽입
public String helloString(@RequestParam(&quot;name&quot;) String name,Model model){
        return &quot;hello&quot; + name;
    }
 //string 컨트롤러</code></pre><p align="center"><img src="https://velog.velcdn.com/images/neymar_10/post/22f96e46-99d2-4866-a862-40b7a4290939/image.png" width=400d></p>

<ul>
<li>return 값인 hello + name 문자 그대로 출력되는 것을 확인할 수 있다.</li>
</ul>
<blockquote>
<p>JSON 전송</p>
</blockquote>
<pre><code>@GetMapping(&quot;hello-api&quot;)
@ResponseBody
public Hello helloApi(@RequestParam(&quot;name&quot;) String name){
    Hello hello = new Hello();
    hello.setName(name);
    return hello;
}
//api 컨트롤러

static class Hello{
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
//자바 빈 표준 방식(프로퍼티 접근 방식)</code></pre><p align="center"><img src="https://velog.velcdn.com/images/neymar_10/post/8947ae04-7a55-4963-bd69-bc65a28e9585/image.png" width=400></p>

<ul>
<li>JSON 형식(키-값 쌍)으로 출력되는 것을 확인할 수 있다.</li>
</ul>
<blockquote>
<p>@ResponseBody 구동 과정</p>
</blockquote>
<ul>
<li>HTTP의 BODY에 문자 내용을 직접 반환<ul>
<li>&#39;viewResolver 대신에 &#39;HttpMessageConverter&#39;가 동작</li>
<li>기본 문자처리: &#39;StringHttpMessageConverter&#39;</li>
<li>기본 객체처리: &#39;MappingJackson2HttpMessageConveter&#39;</li>
<li>byte 처리 등등 기타 여러 HttpMessageConveter가 기본으로 등록</li>
<li>hello-api -&gt; 톰캣 서버 -&gt; 스프링 컨테이너</li>
<li>@ResponseBody(return:hello(name:spring)) -&gt; HttpMessageConveter</li>
<li>JsonConveter, StringConverter 중 키-값 쌍인 Json형식으로 출력</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot - View 환경설정]]></title>
            <link>https://velog.io/@neymar_10/Spring-Boot-View-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@neymar_10/Spring-Boot-View-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95</guid>
            <pubDate>Sun, 11 Feb 2024 03:36:12 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="목차">목차</h3>
</blockquote>
<ol>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0">프로젝트 생성하기</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-View-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95">View 환경설정</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EC%B4%88">스프링 웹 개발 기초</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%98%88%EC%A0%9C">회원 관리 예제</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88%EA%B3%BC-%EC%9D%98%EC%A1%B4-%EA%B4%80%EA%B3%84">스프링 빈과 의존 관계</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%9B%B9-MVC-%EC%A0%9C%EC%9E%91">웹 MVC 제작</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB-%EC%A0%9C%EC%9E%911">DB 제작(1)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB%EC%A0%9C%EC%9E%912">DB 제작(2)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-AOP">AOP</a></li>
</ol>
<h3 id="1-welcome-page-만들기">1. Welcome Page 만들기</h3>
<blockquote>
<p>인덱스 페이지 만들기</p>
</blockquote>
<pre><code>&lt;!DOCTYPE HTML&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;static content&lt;/title&gt;
    &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;a&gt;hello&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>Resources &gt; static &gt; index.html 추가</p>
<ul>
<li>8080포트에 접속 시 &quot;hello&quot;라는 문구 확인 가능</li>
<li>static 파일에 정적 페이지(index.html) 제작 가능</li>
</ul>
<blockquote>
<p>thymeleaf 템플릿 엔진 동작</p>
</blockquote>
<pre><code> @Controller
 public class HelloController {
    @GetMapping(&quot;hello&quot;)
 public String hello(Model model) {
        model.addAttribute(&quot;data&quot;, &quot;hello!!&quot;);
 return &quot;hello&quot;;
    }
 }
 // Java &gt; controller &gt; HelloController.java</code></pre><pre><code> &lt;!DOCTYPE HTML&gt;
 &lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;
 &lt;head&gt;
    &lt;title&gt;Hello&lt;/title&gt;
    &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
 &lt;/head&gt;
 &lt;body&gt;
 &lt;p th:text=&quot;&#39;안녕하세요. &#39; + ${data}&quot; &gt;안녕하세요. 손님&lt;/p&gt;
 &lt;/body&gt;
 &lt;/html&gt;
 // resources &gt; templates &gt; hello.html</code></pre><p align="center"><img src="https://velog.velcdn.com/images/neymar_10/post/7ee16ad4-b20b-40f0-a498-68fd01d79e48/image.png" width="50%"></p>

<p>model의 &lt;data, hello!!&gt;가 hello.html에서 반환되는 것을 확인할 수 있다.</p>
<ul>
<li>컨트롤러의 리턴 값에 대응하는 html에 동작하는 것을 알 수 있다.</li>
<li>컨트롤러 &gt;&gt; return <span style="color: red">hello</span>, model(data: hello!) &gt;&gt; viewResolver &gt;&gt; <span style="color: red">hello</span>.html</li>
</ul>
<blockquote>
<p>CMD창으로 실행해보기</p>
</blockquote>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/c41a8b67-58fe-40e1-b94f-fbe171a2252c/image.png" width=1200>gradlew.bat 실행</th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/32a6b050-a7bb-4a0a-85e6-1ea6506d4604/image.png" alt="">build&gt;libs 이동</th>
</tr>
</thead>
</table>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/8c31bba2-c1b9-475e-bb40-83cb020fbac0/image.png" width=700> java -jar 실행</p></th>
<th><img src="https://velog.velcdn.com/images/neymar_10/post/d353adaa-9dc0-4d60-8438-1b44bd4e2d2c/image.png" alt="">8080/hello 화면</th>
</tr>
</thead>
</table>
<ul>
<li>cmd창에서 프로젝트 파일로 이동한 뒤 gradlew.bat(window시) -&gt; build -&gt; libs 순으로 이동하다 보면 hellow-spring-0.0.1-SNAPSHOT.jar를 찾을 수 있다.</li>
<li>java -jar hellow-spring-0.0.1-SNAPSHOT.jar를 실행하게 되면 IntelliJ에서 보았던 화면이 보이게 된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring boot - 프로젝트 생성하기]]></title>
            <link>https://velog.io/@neymar_10/Spring-boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@neymar_10/Spring-boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 10 Feb 2024 18:02:40 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="목차">목차</h3>
</blockquote>
<ol>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0">프로젝트 생성하기</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-View-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95">View 환경설정</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EC%B4%88">스프링 웹 개발 기초</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%98%88%EC%A0%9C">회원 관리 예제</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88%EA%B3%BC-%EC%9D%98%EC%A1%B4-%EA%B4%80%EA%B3%84">스프링 빈과 의존 관계</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-%EC%9B%B9-MVC-%EC%A0%9C%EC%9E%91">웹 MVC 제작</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB-%EC%A0%9C%EC%9E%911">DB 제작(1)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-DB%EC%A0%9C%EC%9E%912">DB 제작(2)</a></li>
<li><a href="https://velog.io/@neymar_10/Spring-boot-AOP">AOP</a></li>
</ol>
<h3 id="1프로젝트-환경-설정">1.프로젝트 환경 설정</h3>
<blockquote>
<p>사전 준비물</p>
</blockquote>
<ul>
<li>Java 17이상 설치</li>
<li>IDE:IntelliJ 설치</li>
</ul>
<blockquote>
<p>스프링 부트 스타터 사이트 이용
 <a href="https://start.spring.io">https://start.spring.io</a>
 <img src="https://velog.velcdn.com/images/neymar_10/post/e25168b7-6281-407d-8c79-38aa2287f1f6/image.png" alt=""></p>
</blockquote>
<ul>
<li>프로젝트 선택<ul>
<li>Project: Gradle-Groovy Project</li>
<li>Language: Java</li>
<li>Spring Boot: 3.2.2</li>
</ul>
</li>
<li>Dependencies<ul>
<li>Spring Web</li>
<li>Thymeleaf</li>
</ul>
</li>
</ul>
<h3 id="2-프로젝트-생성">2. 프로젝트 생성</h3>
<blockquote>
<p>프로젝트 IntelliJ에 연동하기</p>
</blockquote>
<ul>
<li>생성한 파일의 경로로 Intellij 연동
(프로젝트 생성 시 지정한 Project MataData: Name으로 프로젝트 생성)
<img src="https://velog.velcdn.com/images/neymar_10/post/422059c8-3399-47d4-a66a-22fdab19981e/image.jpg" alt=""></li>
</ul>
<blockquote>
<p>Gradle 둘러보기</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/neymar_10/post/950daa08-28d1-458c-aaf5-c31c382848e7/image.png" alt=""></p>
<ul>
<li>플러그<ul>
<li>Java, version 3.2.2</li>
</ul>
</li>
<li>JAVA<ul>
<li>Java 17버전</li>
</ul>
</li>
<li>Dependencies<pre><code>- thymeleaf
- web</code></pre></li>
</ul>
<p><em>프로젝트 생성 시 체크했던 속성들과 동일하게 생성된 것을 확인할 수 있다.</em></p>
<p align="center">
<img src="https://velog.velcdn.com/images/neymar_10/post/8b78cee4-9032-4c76-b440-bdd93a022543/image.png" width="40%"></p>

<p>조금 더 자세히 살펴보면 api, log, json, tomcat 등 웹 제작 필요한 다양한 라이브러리들을 자동을 추가해주는 고마운 일을 확인할 수 있다.</p>
<p><strong>스프링 부트 라이브러리</strong></p>
<ul>
<li>spring-boot-starter-web<ul>
<li>spring-boot-starter-tocat: 톰캣(웹서버)</li>
<li>spring-boot-webmvc: 스프링 웹 MVC</li>
</ul>
</li>
<li>spring-boot-starter-thymeleaf: 타임리프 템플릿 엔진(view)</li>
<li>spring-boot-starter(공통): 스프링 부트 + 스프링 코어 + 로깅<ul>
<li>spring-boot<ul>
<li>spring-core</li>
</ul>
</li>
<li>spring-boot-starter-logging<ul>
<li>logback, slf4j</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>테스트 라이브러리</strong></p>
<ul>
<li>spring-boot-starter-test<ul>
<li>junit: 테스트 프레임워크</li>
<li>mockito: 목 라이브러리</li>
<li>assertj: 테스트 코드를 좀 더 편하게 작성하게 도와주는 라이브러리</li>
<li>spring-test: 스프링 통합 테스트 지원</li>
</ul>
</li>
</ul>
<blockquote>
<p>프로젝트 실행해보기</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/neymar_10/post/01ec61e5-9787-4e55-a588-ca224cf85b4a/image.png" alt="">
<em>기본 코드를 실행해보면 Spring이라는 문구와 함께 구동되는 것을 볼 수 있다.</em></p>
<ul>
<li>왜 Main, Test 파일로 나뉘어 있을까<ul>
<li>관리, 유지보수, 테스트를 용이하게 만들고 개발 프로세스를 자동화하는데 필요<p align="center"><img src="https://velog.velcdn.com/images/neymar_10/post/ea758af8-3fb9-4cab-97c9-9d782d35e86b/image.png" width="400px"></p>
Tomcat의 기본 포트인 8080포트에 접속해보면 애러 페이지가 연결된 것을 확인할 수 있다.


</li>
</ul>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>