<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>lheesung.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Wed, 12 Jun 2024 02:09:09 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>lheesung.log</title>
            <url>https://velog.velcdn.com/images/dev_lheesung/profile/0f63dc07-9eff-47d1-8932-a8e3fadecc04/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. lheesung.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dev_lheesung" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[니네가 이정도 받아도 된다고 생각해?]]></title>
            <link>https://velog.io/@dev_lheesung/%EB%8B%88%EB%84%A4%EA%B0%80-%EC%9D%B4%EC%A0%95%EB%8F%84-%EB%B0%9B%EC%95%84%EB%8F%84-%EB%90%9C%EB%8B%A4%EA%B3%A0-%EC%83%9D%EA%B0%81%ED%95%B4</link>
            <guid>https://velog.io/@dev_lheesung/%EB%8B%88%EB%84%A4%EA%B0%80-%EC%9D%B4%EC%A0%95%EB%8F%84-%EB%B0%9B%EC%95%84%EB%8F%84-%EB%90%9C%EB%8B%A4%EA%B3%A0-%EC%83%9D%EA%B0%81%ED%95%B4</guid>
            <pubDate>Wed, 12 Jun 2024 02:09:09 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/c6a62ee7-95cc-4932-8620-f2da728538f5/image.png" alt=""></p>
<p>어제 경기도 어김없이 졌다. 
<img src="https://velog.velcdn.com/images/dev_lheesung/post/1dd734b1-1c6c-428a-ae91-6d184150b116/image.png" alt=""></p>
<p>KBO 테이블 바닥 청소는 항상 롯데 담당이다.
<img src="https://velog.velcdn.com/images/dev_lheesung/post/acbebb1c-c30f-4fab-acad-062efe5f14e6/image.png" alt=""></p>
<p>이게맞냐?
<img src="https://velog.velcdn.com/images/dev_lheesung/post/87e2ca0e-c93d-429d-8cbf-d4efc64b8889/image.png" alt=""></p>
<p>작년에 롯데 직관을 갔을 때 상상초월 볼넷이 14개였나 그정도가 나왔다.
롯데 투수야 이거 맞냐?</p>
<p>오늘은 KBO 투수 작자들이 얼마나 받아갈지 예측해보도록하자.</p>
<hr>

<h1 id="1-kbo-연봉-데이터-둘러보기">1. KBO 연봉 데이터 둘러보기</h1>
<p>먼저 대지피티님을 이용해 <a href="https://statiz.sporki.com/">스탯티즈</a>에서 데이터를 들고왔다.</p>
<blockquote>
<p><em>지피티야~ <a href="https://statiz.sporki.com/">https://statiz.sporki.com/</a> 여기서 데이터 들고와주려무나~</em></p>
</blockquote>
<p>불러온 데이터들은 <code>picher</code> 라는 변수에 담았다. 그럼 어떤 컬럼들이 있는지 확인해보자.</p>
<pre><code class="language-py">picher.columns</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/5b857e8f-fee9-4bf3-8cf2-0cb4d59018ee/image.png" alt="">
쓸만한 컬럼들이 정말 많다!
한번 데이터들도 확인해보자.</p>
<pre><code class="language-py">picher.head()</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/90f98e1d-e931-4591-be67-c55bd7ba36b3/image.png" alt=""></p>
<p>오늘은 연봉에 대해 예측을 해볼 것이기에 이제 연봉들을 다뤄보자.</p>
<p>우선 <code>describe</code>로 2018년 선수들의 연봉 데이터를 한번 알아보자.</p>
<pre><code class="language-py">picher[&#39;연봉(2018)&#39;].describe()</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/0d652617-94f7-44d9-8f8e-0ffc2b663636/image.png" alt="">
와흥. 시각화를 해서 연봉 분포를 확인해보자.</p>
<pre><code class="language-py">picher.boxplot(column=[&#39;연봉(2018)&#39;])</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/91ecc148-4cec-47e7-907d-c6ce6eb14936/image.png" alt=""></p>
<p>50000에서 150000사이에 많이 분포해있는 것을 확인할 수 있었다.</p>
<h1 id="2-투수-연봉-예측">2. 투수 연봉 예측</h1>
<p>이제 본격적으로 투수의 연봉을 예측해보자.
먼저 회귀분석에 사용할 피쳐들을 변수에 넣어보자. 피쳐들은 아까 <code>columns</code> 로 확인했던 컬럼들을 사용하자. 피쳐들은 <code>pf_df</code>라는 변수에 저장했다.</p>
<h3 id="피처스케일링">피처스케일링</h3>
<p>먼저 <strong>피처 스케일링</strong>이라는 작업을 해줘야한다. 피처 스케일링은 <strong>서로 다른 변수의 값 범위를 일정한 수준으로 맞추는 작업인데</strong> 왜 굳이 해야하는걸까?</p>
<ul>
<li>변수 값의 범위 or 단위가 달라서 발생하는 문제를 예방할 수 있다.</li>
<li>머신러닝 모델이 특정 데이터에 편향성을 가지는 것을 예방할 수 있다.</li>
<li>데이터 범위 크기에 따라 모델이 학습하는 데 있어서 바이어스가 달라질 수 있으므로 하나의 범위 크기로 통일해주는 작업이 필요하다.</li>
</ul>
<p>그럼 각각의 피처에 대해 스케일링을 하는 함수를 작성해보자.</p>
<pre><code class="language-py">def features_scaling(df, scale_columns):
    for col in scale_columns:
        series_mean = df[col].mean()
        series_std = df[col].std()
        df[col] = df[col].apply(lambda x: (x-series_mean)/series_std)
    return df</code></pre>
<p><code>features_scaling</code> 를 이용하여 picher의 모든 컬럼을 스케일링 해줬다.</p>
<pre><code class="language-py">picher_df = standard_scaling(picher, scale_columns)</code></pre>
<p><code>picher_df</code> 를 한번 확인해보자.
<img src="https://velog.velcdn.com/images/dev_lheesung/post/dc84d636-a9cf-4d16-9a8a-237fad1742dc/image.png" alt="">
잘된 것 같죠잉?
이어서 <a href="https://wikidocs.net/22647">one-hot-encoding</a>을 통해 피처들의 단위까지 맞춰보자.</p>
<pre><code class="language-py">team_encoding = pd.get_dummies(picher_df[&#39;팀명&#39;])
picher_df = picher_df.drop(&#39;팀명&#39;, axis=1)
picher_df = picher_df.join(team_encoding)

team_encoding.head(5)</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/351abb66-539e-4686-b39f-f47d094eccfa/image.png" alt="">
단위까지 맞춰줬다. 이제 본격적으로 회귀분석을 해보자.</p>
<p>먼저 학습 데이터와 테스트 데이터를 분리시켰다.</p>
<pre><code class="language-py">X = picher_df[picher_df.columns.difference([&#39;선수명&#39;, &#39;y&#39;])]
y = picher_df[&#39;y&#39;]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=19)</code></pre>
<p>이제 회귀분석 계수를 학습시키고 코이피션트를 확인해보자.</p>
<pre><code class="language-py">lr = linear_model.LinearRegression()
model = lr.fit(X_train, y_train)
print(lr.coef)</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/f219756e-ebf1-48c8-bb8e-086279f2447b/image.png" alt=""></p>
<h1 id="3-시각화">3. 시각화</h1>
<p>먼저 어떤 피쳐들이 영향이 높은지 확인을 해보자.</p>
<pre><code class="language-py">X_train = sm.add_constant(X_train)
model = sm.OLS(y_train, X_train).fit()
model.summary()</code></pre>
<p>실행 결과 FIP, WAR, 볼넷, 삼진 정도가 영향이 높다고 볼 수 있을 것 같다.</p>
<p>2018년 연봉을 예측하여 데이터프레임의 column으로 생성하자.</p>
<pre><code class="language-py">X = picher_df[[&#39;FIP&#39;, &#39;WAR&#39;, &#39;볼넷/9&#39;, &#39;삼진/9&#39;, &#39;연봉(2017)&#39;]]
predict_2018_salary = lr.predict(X)
picher_df[&#39;예측연봉(2018)&#39;] = pd.Series(predict_2018_salary)</code></pre>
<p>이제 예측한 연봉이 어느정도 맞는지 실제 데이터와 비교해보자.</p>
<p>2017년도의 데이터를 먼저 가져와서 <code>picher</code>에 넣어주고</p>
<pre><code class="language-py">picher = pd.read_csv(picher_file_path)
picher = picher[[&#39;선수명&#39;, &#39;연봉(2017)&#39;]]</code></pre>
<p>원래의 데이터 프레임에 2018년 연봉 정보를 합치자.</p>
<pre><code class="language-py">result_df = picher_df.sort_values(by=[&#39;y&#39;], ascending=False)
result_df.drop([&#39;연봉(2017)&#39;], axis=1, inplace=True, errors=&#39;ignore&#39;)
result_df = result_df.merge(picher, on=[&#39;선수명&#39;], how=&#39;left&#39;)
result_df = result_df[[&#39;선수명&#39;, &#39;y&#39;, &#39;예측연봉(2018)&#39;, &#39;연봉(2017)&#39;]]
result_df.columns = [&#39;선수명&#39;, &#39;실제연봉(2018)&#39;, &#39;예측연봉(2018)&#39;, &#39;작년연봉(2017)&#39;]</code></pre>
<p>재계약하여 연봉이 변화한 선수만을 대상으로만 보자.</p>
<pre><code class="language-py">
result_df = result_df[result_df[&#39;작년연봉(2017)&#39;] != result_df[&#39;실제연봉(2018)&#39;]]
result_df = result_df.reset_index()
result_df = result_df.iloc[:10, :]
result_df.head(10)</code></pre>
<p>확인해보면?
<img src="https://velog.velcdn.com/images/dev_lheesung/post/93b12a3b-337d-4319-8338-5b13a1261a7e/image.png" alt="">
보기 더 쉽도록 그래프로 시각화를 하자.
<img src="https://velog.velcdn.com/images/dev_lheesung/post/597179d1-fb80-47a5-8c83-d63d4cdd194a/image.png" alt=""></p>
<p>대 현 종. 데이터로는 예측할 수 없는 퍼포먼스를 보여주셨는지 더 많이 가져가 버리셨다. 다른 선수들도 예측보다는 조금 더 많은 연봉을 가져갔다. 아무래도 데이터에 없었던 또다른 어떤 피쳐가 영향을 줘서 조금의 오차가 생겼던 것 같다. 대충 예측값의 + 20000 ~ 30000 정도가 실제 연봉이면 나쁘지 않다고 할 수 있다.<em><del>?</del></em></p>
<p>롯데 투수인 레일리도 예상보다 많은 연봉을 받아갔다. 더 많은 연봉을 받았으면 더 잘해야 했었는데 놀랍게도 2018년 KBO 롯데의 순위는...
<img src="https://velog.velcdn.com/images/dev_lheesung/post/961224f0-2b3d-4447-a4f0-e112da51a0d3/image.png" alt="">
엄.</p>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/9ad4c46d-7703-45e9-a447-22c7135ff761/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[최첨단 슈퍼 초고지능 울트라 GPT]]></title>
            <link>https://velog.io/@dev_lheesung/%EC%B5%9C%EC%B2%A8%EB%8B%A8-%EC%8A%88%ED%8D%BC-%EC%B4%88%EA%B3%A0%EC%A7%80%EB%8A%A5-%EC%9A%B8%ED%8A%B8%EB%9D%BC-GPT</link>
            <guid>https://velog.io/@dev_lheesung/%EC%B5%9C%EC%B2%A8%EB%8B%A8-%EC%8A%88%ED%8D%BC-%EC%B4%88%EA%B3%A0%EC%A7%80%EB%8A%A5-%EC%9A%B8%ED%8A%B8%EB%9D%BC-GPT</guid>
            <pubDate>Thu, 18 Apr 2024 01:17:17 GMT</pubDate>
            <description><![CDATA[<p>오늘은 AI를 이용해서 가상의 프로젝트를 설계해보자!</p>
<p>먼저 SW Architect GPT(GPT-4)에게 내가 어떤 서비스를 만들고 싶은지 설명을 하자. 내가 생각한 서비스는 크록스에 달 수 있는 지비츠를 미리 웹에서 코디를 하는 서비스였다. 이렇게 GPT한테 보내면 아래와같은 질문을 한다. 조금 더 자세히 답변을 주면...
<img src="https://velog.velcdn.com/images/dev_lheesung/post/bbdaeb86-aeff-4bc3-abf5-03fb9f02915c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/9fcc4b60-8670-4ba6-8aeb-cb6333214dee/image.png" alt=""></p>
<p>위와 같이 더 구체화를 해준다. 이제 다이어그램들을 그려보자.</p>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/7ea711d6-61bb-4dd8-8fdf-04b4e5876880/image.png" alt=""></p>
<p>클래스다이어그램을 구현해달라고 해보자.</p>
<blockquote>
<p>GPT야~ 그럼 이것들을 클래스 다이어그램으로 구현해주렴</p>
</blockquote>
<p>라고 하니깐 아래와 같은 <code>plaintext</code>를 제공해주었다.</p>
<pre><code class="language-plaintext">@startuml

class UserInterface {
    +uploadCrocsPhoto()
    +selectJibbitz()
    +coordinateJibbitz()
    +purchaseJibbitz()
}

class JibbitzCoordinator {
    +addJibbitzToPhoto()
    +addJibbitzToModel()
}

class PaymentSystem {
    +processPayment()
}

class AccountManagement {
    +login()
    +logout()
    +register()
    +viewPurchaseHistory()
}

UserInterface --&gt; JibbitzCoordinator : uses
UserInterface --&gt; PaymentSystem : uses
UserInterface --&gt; AccountManagement : uses

@enduml</code></pre>
<p>이 코드를 <a href="https://www.plantuml.com/plantuml/uml/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80">PlatnUML</a>에서 삽입하면 아래와 같은 클래스 다이어그램을 얻을 수 있다!</p>
<p>이제 요구사항 명세를 해보자.</p>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/1a24064f-35ca-46db-9b3c-e57206f2d23f/image.png" alt="">
이제 액티브 다이어그램을 만들어보자.</p>
<blockquote>
<p>GPT야~ 그럼 이것들을 액티브 다이어그램도~</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/9b3edfb4-e88c-4cd1-9954-0a058320c4f0/image.png" alt="">
이 코드 또한 PlantUML에서 변환시키면
<img src="https://velog.velcdn.com/images/dev_lheesung/post/8c065c52-25f8-4048-88e6-00e3c832e6c3/image.png" alt="">
이렇게 acitivity diagram을 얻을 수 있다.</p>
<pre><code class="language-asciidoc">= SPEC-001: Crocs Jibbitz Coordination and Commerce Platform
:sectnums:
:toc:

== Background

이 플랫폼은 크록스 착용자들이 자신의 신발에 맞는 지비츠를 가상으로 코디해보고, 마음에 드는 구성을 실제로 구매할 수 있게 하는 서비스입니다. 사용자는 자신의 크록스 사진을 업로드하거나 제공된 크록스 모델을 선택하여 지비츠를 가상으로 장착해볼 수 있습니다.

== Requirements

.사용자 인터페이스
* 사용자는 자신의 크록스 사진을 업로드할 수 있어야 합니다. (MUST)
* 사용자는 제공된 크록스 모델에 지비츠를 코디해볼 수 있어야 합니다. (MUST)
* 사용자는 코디된 지비츠 구성을 저장하고 나중에 볼 수 있어야 합니다. (SHOULD)
* 사용자는 코디된 지비츠 구성을 SNS 등에 공유할 수 있어야 합니다. (COULD)

.지비츠 코디네이션
* 시스템은 사용자가 업로드한 크록스 사진에 지비츠를 가상으로 추가할 수 있어야 합니다. (MUST)
* 시스템은 제공된 크록스 모델에 지비츠를 가상으로 추가할 수 있어야 합니다. (MUST)
* 시스템은 지비츠를 가상으로 추가할 때, 위치 조정과 크기 조정 기능을 제공해야 합니다. (MUST)

.결제 시스템
* 사용자는 코디한 지비츠를 실제로 구매할 수 있어야 합니다. (MUST)
* 시스템은 다양한 결제 방식(신용카드, 페이팔, 애플 페이 등)을 지원해야 합니다. (SHOULD)

.계정 관리
* 사용자는 계정을 생성하고 로그인할 수 있어야 합니다. (MUST)
* 사용자는 자신의 구매 이력을 조회할 수 있어야 합니다. (MUST)

== Method

이 섹션에서는 요구사항을 충족하기 위한 기술적 방법론, 데이터베이스 스키마, 아키텍처 디자인, 주요 알고리즘 등을 상세히 다룰 예정입니다.

== Implementation

개발 과정, 팀 구성, 사용할 기술 스택, 구현 단계별 주요 작업 항목 등을 포함합니다.

== Milestones

프로젝트 진행 상황을 모니터링하고 관리하기 위한 주요 이정표와 해당 날짜를 기재합니다.

== Gathering Results

이 섹션에서는 개발된 시스템의 성능 평가 및 요구사항 충족 여부에 대한 검토를 다룹니다.</code></pre>
<p>그럼 위와 같이 asciidoc으로 짜여진 요구사항 명세를 얻을 수 있다!</p>
<p>이제 어느정도 기능과 요구사항 명세를 했으니 디자인의 전체적인 큰 틀을 <a href="https://www.midjourney.com/home">midjourney</a>를 이용해서 잡아보자.</p>
<p>midjourney에게 GPT와 비슷하게 요청을 했다.
&quot;크록스에 달 수 있는 지비츠를 코디하고 구매할 수 있는 커머스 웹 서비스를 만들건데 디자인 예시를 줘. 구현된 웹 서비스의 레이아웃 화면을 원해&quot;
위와 같은 식으로 질문을 하니 아래와 같이 예쁜 레이아웃을 얻을 수 있었다.
<img src="https://velog.velcdn.com/images/dev_lheesung/post/1f800d00-ff0b-4ae2-a8ed-80e2984da2b8/image.png" alt="">
<img src="https://velog.velcdn.com/images/dev_lheesung/post/c1ca4591-96da-4310-9b75-ebf2106b7c26/image.png" alt=""></p>
<p>이렇게 AI 딸-깍으로 서비스 하나를 쉽게 기획할 수 있었다. 서비스를 시작할 때 무작정 생각만 하면서 긴 시간을 소비하지 말고 이렇게 생성형 AI의 도움을 빌려 참고를 하면서 하면 더 빠르고 효율적인 기획이 가능할 것 같다~<img src="https://velog.velcdn.com/images/dev_lheesung/post/779f56b7-88b2-4ac8-989a-80c6e32dfbbf/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[openCV] 선형회귀와 CNN, 그리고 미디어파이프 써보기]]></title>
            <link>https://velog.io/@dev_lheesung/openCV-%EC%84%A0%ED%98%95%ED%9A%8C%EA%B7%80%EC%99%80-CNN-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EB%AF%B8%EB%94%94%EC%96%B4%ED%8C%8C%EC%9D%B4%ED%94%84-%EC%8D%A8%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@dev_lheesung/openCV-%EC%84%A0%ED%98%95%ED%9A%8C%EA%B7%80%EC%99%80-CNN-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EB%AF%B8%EB%94%94%EC%96%B4%ED%8C%8C%EC%9D%B4%ED%94%84-%EC%8D%A8%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Wed, 03 Apr 2024 01:31:10 GMT</pubDate>
            <description><![CDATA[<h1 id="linear-regression">Linear Regression</h1>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/390c7e04-660f-4f0f-b7f2-2f0ea28529c6/image.png" alt=""></p>
<p>선형 회귀는 데이터 포인트들 사이의 관계를 모델링하는 통계적 방법 중 하나다. 주로 입력 변수와 출력 변수 사이의 관계를 이해하고 예측하기 위해 사용된다.</p>
<p>여기서 <strong>선형</strong>이란 말은 모델이 직선 형태의 관계를 나타낸다는 것을 의미합니다. 즉, 하나 이상의 입력 변수들과 하나의 출력 변수 간의 선형 관계를 나타내는 모델이다.</p>
<p>선형 회귀 모델은 입력 변수의 선형 조합을 사용하여 출력 변수를 예측한다. 이 선형 조합은 각 입력 변수와 해당 변수의 가중치(coefficients)를 곱한 후, 상수항(intercept)을 더하여 구성됩니다. 모델은 이러한 가중치와 상수항을 학습하여 데이터에 가장 잘 맞는 예측을 할 수 있도록 한다.</p>
<p>선형 회귀는 주어진 데이터에 대해 가장 적합한 직선을 찾아내는 방법 중 하나로, 데이터를 통해 이러한 직선의 방정식을 찾아내고, 새로운 입력 값이 주어졌을 때 출력 값을 예측할 수 있다.</p>
<h1 id="선형회귀의-기본-원리">선형회귀의 기본 원리</h1>
<p>선형 회귀의 기본 원리는 데이터를 가장 잘 설명하는 선형 관계를 찾는 것입니다. 이것은 주어진 입력 변수와 출력 변수 간의 관계를 모델링하는 것을 의미합니다.</p>
<p>기본적으로 선형 회귀는 다음과 같은 단계로 이루어진다: </p>
<h3 id="1-데이터-수집">1. 데이터 수집</h3>
<p>우선, 연구나 문제 해결을 위해 관련 데이터를 수집한다. 이 데이터에는 입력 변수와 출력 변수이 포함된다. </p>
<h3 id="2-모델-정의">2. 모델 정의</h3>
<p>선형 회귀 모델을 선택한다. 이 모델은 입력 변수들의 선형 조합으로 출력 변수를 예측하는 방법을 나타낸다. 보통은 다음과 같은 형태로 표현된다.</p>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/d3ae5ee0-89c2-4c7e-95d2-e2215590f746/image.png" alt="">
여기서 각각의 변수들은 아래와 같다.
<img src="https://velog.velcdn.com/images/dev_lheesung/post/a4438c10-9981-4caf-a13e-300a886ed433/image.png" alt=""></p>
<h3 id="3-손실-함수-정의">3. 손실 함수 정의</h3>
<p>선형 회귀에서는 일반적으로 최소 제곱 오차를 사용하여 모델의 예측값과 실제값 사이의 차이를 측정한다. 이 오차를 최소화하기 위해 손실 함수를 정의한다.</p>
<h3 id="4-모델-학습">4. 모델 학습</h3>
<p>주어진 데이터를 사용하여 모델의 가중치와 상수항을 조정하여 손실 함수를 최소화하는 방향으로 모델을 학습시킨다. 이는 일반적으로 경사 하강법(Gradient Descent) 또는 최적화 알고리즘을 사용하여 수행된다.</p>
<h3 id="5-모델-평가">5. 모델 평가</h3>
<p>학습된 모델을 평가하여 모델이 새로운 데이터에 대해 얼마나 잘 일반화되는지 확인한다. 이것은 예측 성능 지표를 사용하여 수행된다.</p>
<h3 id="6-예측">6. 예측</h3>
<p>마지막으로, 학습된 모델을 사용하여 새로운 입력 변수에 대한 출력 변수 값을 예측한다.</p>
<h1 id="cnnconvolutional-neural-networks">CNN(Convolutional Neural Networks)</h1>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/756a5531-7fed-45ed-901c-bbffff2fd846/image.png" alt=""></p>
<p>CNN은 인간의 시신경을 모방하여 만든 딥러닝 구조 중 하나다. 특히 convolution 연산을 이용하여 이미지의 공간적인 정보를 유지하고, Fully connected Neural Network 대비 연산량을 획기적으로 줄였으며, 이미지 분류에서 좋은 성능을 보이는 것으로 알려져있다.</p>
<h2 id="cnn의-원리">CNN의 원리</h2>
<p>CNN은 이미지 처리에 특화된 딥러닝 모델의 한 종류로, 이미지의 특징을 자동으로 추출하고 이를 기반으로 이미지를 분류한다. 기존의 신경망과는 다르게, CNN은 이미지의 공간 정보를 유지하면서 학습이 가능하도록 설계되었다. 이는 CNN이 이미지 내의 작은 부분에 집중하여 패턴을 인식할 수 있게 해준다.</p>
<h2 id="cnn의-주요-구성-요소">CNN의 주요 구성 요소</h2>
<h3 id="합성곱-계층convolutional-layer">합성곱 계층(Convolutional Layer)</h3>
<p>이 계층은 이미지에서 특징을 추출하는 역할을 합니다. 여러 개의 필터(커널)를 사용하여 이미지를 스캔하고, 각 필터가 이미지의 특정 특징을 활성화시키는 방식으로 작동합니다.</p>
<h3 id="활성화-함수activation-function">활성화 함수(Activation Function)</h3>
<p>대부분의 CNN에서는 ReLU(Rectified Linear Unit) 활성화 함수가 사용된다. 이 함수는 비선형성을 도입하여 네트워크가 복잡한 패턴을 학습할 수 있게 도와준다.</p>
<h3 id="풀링-계층pooling-layer">풀링 계층(Pooling Layer)</h3>
<p>풀링 계층은 이미지의 차원을 축소하여 계산량을 줄이는 동시에 중요한 정보를 유지한다. 가장 흔히 사용되는 방법은 맥스 풀링(Max Pooling)으로, 지정된 영역 내에서 가장 큰 값을 선택하는 방식이다.</p>
<h3 id="완전-연결-계층fully-connected-layer">완전 연결 계층(Fully Connected Layer)</h3>
<p>이 계층은 합성곱 계층과 풀링 계층을 거친 후의 정보를 바탕으로 최종 분류를 수행한다. 이미지가 속할 클래스에 대한 확률을 출력하기 위해 사용된다.</p>
<h1 id="전의학습">전의학습</h1>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/a3518a5a-6d55-4ad7-8950-96ca8ef4afc1/image.png" alt=""></p>
<p>전이 학습(Transfer Learning)은 딥러닝에서 기존에 학습된 모델의 지식을 새로운 관련 작업에 활용하는 기술이다.</p>
<p>기존의 대규모 데이터셋에서 사전에 학습된 모델을 가져와서, 이 모델의 가중치를 새로운 작업에 적용하여 성능을 개선하거나 학습 시간을 단축하는 것이 전이 학습의 핵심 아이디어다.</p>
<h2 id="전의학습의-주요-단계">전의학습의 주요 단계</h2>
<h3 id="사전-학습된-모델-선택">사전 학습된 모델 선택</h3>
<p>일반적으로 대규모 이미지 데이터셋(예: ImageNet)에서 사전에 학습된 모델을 선택한다. 이 모델은 보통 많은 계층(layer)으로 구성된 딥러닝 아키텍쳐다.</p>
<h3 id="모델-파인-튜닝fine-tuning">모델 파인 튜닝(Fine-tuning)</h3>
<p>선택한 사전 학습된 모델을 가져와서, 새로운 작업에 맞게 일부 계층을 고정하고 나머지 계층의 가중치를 조정한다. 새로운 작업에 더 적합하도록 네트워크를 조정하는 과정이다.</p>
<h3 id="새로운-데이터셋에-대한-학습">새로운 데이터셋에 대한 학습</h3>
<p>파인 튜닝된 모델을 새로운 작업에 맞는 데이터셋으로 학습시킨다. 이 과정에서 적은 양의 데이터로도 높은 성능을 달성할 수 있다.</p>
<h1 id="해보자">해보자</h1>
<p>먼저 openCV를 활용해 동물을 구분해보자.</p>
<pre><code class="language-py">import sys
import numpy as np
import cv2

# 이미지 파일 경로 지정
filename = &#39;data/리트리버.webp&#39;

# 이미지 파일 읽어오기
img = cv2.imread(filename)

# 이미지 파일이 제대로 읽혔는지 확인
if img is None:
    print(&#39;Image load failed!&#39;)
    exit()

# 신경망 모델 불러오기
net = cv2.dnn.readNet(&#39;data/bvlc_googlenet.caffemodel&#39;, &#39;data/deploy.prototxt&#39;)

# 신경망 모델이 제대로 불러와졌는지 확인
if net.empty():
    print(&#39;Network load failed!&#39;)
    exit()

# 클래스 이름 불러오기
classNames = None
with open(&#39;data/classification_classes_ILSVRC2012.txt&#39;, &#39;rt&#39;) as f:
    classNames = f.read().rstrip(&#39;\n&#39;).split(&#39;\n&#39;)

# 이미지 전처리 (크기 조정 및 평균값 제거)
inputBlob = cv2.dnn.blobFromImage(img, 1, (224, 224), (104, 117, 123))
net.setInput(inputBlob)

# 이미지를 신경망에 전달하고 예측 수행
prob = net.forward()

# 예측 결과 확인 및 출력
out = prob.flatten()
classId = np.argmax(out)
confidence = out[classId]

# 클래스 이름과 해당 클래스의 신뢰도를 텍스트로 생성
text = &#39;%s (%4.2f%%)&#39; % (classNames[classId], confidence * 100)

# 이미지에 텍스트 출력
# 수정된 부분: 폰트 스케일을 2로 크게 조정하여 텍스트 크기를 키움
cv2.putText(img, text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 2, cv2.LINE_AA)

# 이미지 창에 이미지 출력
cv2.imshow(&#39;img&#39;, img)

# 키 입력 대기
cv2.waitKey()

# 모든 창 닫기
cv2.destroyAllWindows()
</code></pre>
<p>이렇게 귀여운 리트리버인 것을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/dev_lheesung/post/11511bb2-b8ec-48fd-b897-a227f31a6bef/image.png" alt=""></p>
<h1 id="욕-모자이크하기">욕 모자이크하기</h1>
<p>mediapipe를 활용해서 미들핑거를 모자이크해서 어린이들을 지켜보자.</p>
<pre><code class="language-py">import cv2
import mediapipe as mp

mp_hands = mp.solutions.hands
# 최대 손 갯수를 1개로 설정하여 손을 감지.
hands = mp_hands.Hands(static_image_mode=False, max_num_hands=1)

# 모자이크 함수를 정의합니다.
def mosaic(img, x, y, w, h, size=30):
    if x &lt; 0 or y &lt; 0 or x + w &gt;= img.shape[1] or y + h &gt;= img.shape[0]:
        return

    for i in range(int(w / size)):
        for j in range(int(h / size)):
            xi = x + i * size
            yi = y + j * size
            if xi &lt; 0 or yi &lt; 0 or xi + size &gt;= img.shape[1] or yi + size &gt;= img.shape[0]:
                continue
            # 해당 영역을 블러 처리하여 모자이크 효과 생성
            img[yi:yi + size, xi:xi + size] = cv2.blur(img[yi:yi + size, xi:xi + size], (23, 23))

# 웹캠
cap = cv2.VideoCapture(0)

while cap.isOpened():
    # 프레임을 읽어옵니다.
    ret, frame = cap.read()
    if not ret:
        continue


    frame = cv2.flip(frame, 1)

    # 프레임을 RGB 형식으로 변환한다.
    rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    # 손 감지 결과를 얻습니다.
    results = hands.process(rgb_frame)

    # 손 감지 결과가 있을 경우
    if results.multi_hand_landmarks:
        for hand_landmarks in results.multi_hand_landmarks:
            # 검지 손가락 끝과 중지 손가락 끝의 y 좌표를 얻는다.
            index_finger_tip_y = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP].y * frame.shape[0]
            middle_finger_tip_y = hand_landmarks.landmark[mp_hands.HandLandmark.MIDDLE_FINGER_TIP].y * frame.shape[0]

            # 검지 손가락 끝이 중지 손가락 끝보다 아래에 있을 경우
            if index_finger_tip_y &gt; middle_finger_tip_y:
                # 손가락들의 최소 x, y 좌표와 최대 x, y 좌표를 구함
                x_min, y_min = int(min(l.x * frame.shape[1] for l in hand_landmarks.landmark)), int(min(l.y * frame.shape[0] for l in hand_landmarks.landmark))
                x_max, y_max = int(max(l.x * frame.shape[1] for l in hand_landmarks.landmark)), int(max(l.y * frame.shape[0] for l in hand_landmarks.landmark))

                mosaic(frame, x_min, y_min, x_max - x_min, y_max - y_min)

    # 모자이크가 적용된 프레임 보여줌
    cv2.imshow(&#39;Mosaic hand&#39;, frame)
    if cv2.waitKey(1) &amp; 0xFF == ord(&#39;q&#39;):
        break

cap.release()
cv2.destroyAllWindows()
</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/9080275c-b859-43af-a576-d9dfb11b14cb/image.png" alt=""></p>
<p>도중에 오류가 발생했었는데 mediapipe 버전문제였다. 원래 <code>0.10.11</code>이었던 버전을 <code>0.10.9</code> 버전으로 다운그레이드하니 오류가 해결됬다.</p>
<pre><code class="language-bash">pip uninstall mediapipe</code></pre>
<p>미디어파이프를 삭제했다가 낮은 버전으로 다시 설치하자.</p>
<pre><code class="language-bash">pip install mediapipe == 0.10.9</code></pre>
<p>오늘은 선형회귀와 CNN을 알아보았다. ai를 공부할 수록 흥미로워 져서 더 파볼려고 생각중이다.
mediapipe도 가지고 놀아봤는데 다음 글엔 더 재밌는 걸로 돌아오겠다.
<img src="https://velog.velcdn.com/images/dev_lheesung/post/e92f2565-1204-45d6-88d5-efa1627fa471/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[소프트웨어 공학에서의 방법론들.]]></title>
            <link>https://velog.io/@dev_lheesung/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EA%B3%B5%ED%95%99</link>
            <guid>https://velog.io/@dev_lheesung/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EA%B3%B5%ED%95%99</guid>
            <pubDate>Thu, 21 Mar 2024 02:26:08 GMT</pubDate>
            <description><![CDATA[<h1 id="소프트웨어-공학에서의-프로세스">소프트웨어 공학에서의 프로세스</h1>
<p>소프트웨어 공학에서의 프로세스는 소프트웨어 제품을 개발하기 위한 계획, 실행, 평가 및 관리의 일련의 단계나 절차를 나타낸다. 이러한 프로세스들은 소프트웨어를 효과적으로 개발하고 유지보수하기 위한 체계적이고 구조화된 방법을 제공한다. 소프트웨어에서 프로세스는 방법은 왜 필요한걸까?</p>
<h3 id="구조화된-절차">구조화된 절차</h3>
<p>프로세스는 개발 과정을 체계적으로 관리하여 프로젝트의 효율성을 향상시키고 개발자 간의 협업을 촉진시킨다.</p>
<h3 id="위험-관리">위험 관리</h3>
<p>프로세스는 프로젝트 위험을 식별하고 관리하여 예상치 못한 문제를 최소화하고 프로젝트의 성공 가능성을 높인다.</p>
<h3 id="품질-향상">품질 향상</h3>
<p>각 단계에서의 검증과 테스트를 통해 제품의 품질을 유지하고 개선하여 최종 제품의 품    질을 보장한다.</p>
<h1 id="cmmicapability-maturity-model-integration">CMMI(Capability Maturity Model Integration)</h1>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/f0c2a668-3e15-475b-93f2-1cfb79a2e5bc/image.png" alt=""></p>
<p>CMMI(Capability Maturity Model Integration), 통합성숙모델은 조직의 프로세스 개선 활동을 효율적으로 지원하기 위한 모델이다.조직의 개발능력이 얼마나 성숙되었는지를  평가할 수 있도록 만든 모델이라 할 수 있다. 전 세계의 여러 국가에서 도입하고 있는 산업 표준이다.</p>
<h1 id="cmmi-v20">CMMI V2.0</h1>
<p>CMMI V2.0 모델은 뷰(View), 프랙티스 영역(Model), 프랙티스 그룹(Practice Group), 프랙티스(Practice) 및 정보자료(Informative Material) 이렇게 5개의 컴포넌트로 구성되어있고 사전에 정의된 모델 뷰를 사용할 수도 있고 사용자에 의도에 따라 자체적으로 뷰를 구성하여 프로세스 개선을 진행할 수도 있다.
<img src="https://velog.velcdn.com/images/dev_lheesung/post/61bbdac6-9b0e-405c-919d-7e8d08d89a19/image.png" alt=""></p>
<h1 id="프로세스의-종류">프로세스의 종류</h1>
<p>이제 어떤 프로세스 방식들이 있는지 알아보자.</p>
<h2 id="워터풀-모델">워터풀 모델</h2>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/f1df4ca3-cc88-4ffa-bcbe-a30b0e95cf5c/image.png" alt="">
폭포수 모델은 전통적인 소프트웨어 개발 방법 중 하나로, 일련의 선형하고 순차적인 단계로 구성됩니다. 주요 단계는 요구 사항 분석, 설계, 구현, 테스트 및 유지보수다. 각 단계는 이전 단계의 결과물을 바탕으로 진행되며, 한 번 시작된 후에는 거의 변경되지 않습니다. 이러한 선형적인 접근은 초기에 요구 사항을 완전히 이해하고 정의하는 것을 가정하며, 변경이 발생할 경우에는 비용이나 시간이 많이 소요될 수 있다. 폭포수 모델은 간단하고 이해하기 쉽지만, 요구 사항의 변화에 적응하기 어렵고 유연성이 부족한 단점을 가지고 있다.</p>
<h2 id="rad-모델">RAD 모델</h2>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/c50e8e37-f17c-412e-92a9-85ec62b4553b/image.png" alt="">
RAD(Rapid Application Development) 모델은 빠른 프로토타입을 사용하여 소프트웨어를 빠르게 개발하는 방법론이다. 이 모델은 초기에 작은 규모의 프로토타입을 빠르게 개발하고 이를 고객에게 제공하여 피드백을 받는다. 그 후 반복적인 개발 단계를 통해 프로토타입을 확장하고 완성도를 높여 최종 제품을 만들어간다. RAD 모델은 빠른 시간 내에 시스템을 개발하고 변경에 유연하게 대처할 수 있으며, 고객의 요구사항을 적극적으로 수용할 수 있는 장점을 가지고 있다. 그러나 프로토타입에 대한 충분한 검증이 이루어지지 않을 경우 품질 문제가 발생할 수 있다.</p>
<h2 id="프로토타이핑-모델">프로토타이핑 모델</h2>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/2f80fa51-0e3a-4bd1-bec0-07db6d98ef29/image.png" alt="">
프로토타이핑 모델은 사용자의 요구사항을 수집하고 이를 기반으로 초기 버전의 소프트웨어 프로토타입을 빠르게 개발하는 방법론이다. 이 모델은 사용자와 개발자 간의 의사소통을 촉진하고 개발 초기부터 피드백을 받아 제품의 요구사항을 명확히 이해하는 데 도움이 된다. 프로토타입은 일반적으로 기능이 제한적이지만 사용자가 원하는 기능을 시연하고 테스트할 수 있는 정도의 기능을 포장한다. 이후에는 사용자 피드백을 바탕으로 프로토타입을 수정하고 개선하여 최종 제품을 개발한다. 
그럼 이 방법론의 장단점은 무엇이 있을까?</p>
<h3 id="장점">장점</h3>
<ol>
<li>빠른 피드백: 초기에 프로토타입을 제공하여 사용자 피드백을 빠르게 수집할 수 있다.</li>
<li>요구사항 명확화: 프로토타입을 통해 요구사항을 명확히 이해하고 사용자의 요구를 충족시킬 수 있다</li>
<li>변경 용이성: 프로토타입은 유연하게 수정하고 변경할 수 있어 사용자의 요구사항 변화에 대응할 수 있다.</li>
</ol>
<h3 id="단점">단점</h3>
<ol>
<li>품질 문제: 프로토타입은 보통 테스트되지 않은 기능이 포함되어 있을 수 있으며, 이로 인해 품질 문제가 발생할 수 있다.</li>
<li>범위 제한: 프로토타입은 일반적으로 제한된 기능을 포함하고 있어 실제 제품의 범위를 정확하게 파악하기 어려울 수 있다.</li>
<li>비용과 일정: 반복적인 프로토타입 개발 및 수정으로 인해 비용과 일정이 증가할 수 있다.</li>
</ol>
<h2 id="incremental-model증분적-모델">Incremental Model(증분적 모델)</h2>
<p>증분적 모델은 전체 시스템을 여러 개의 작은 부분으로 나누어 개발하는 방법론으로, 각각의 부분은 독립적으로 개발되어 완성된 후 전체 시스템으로 통합된다. 이 모델은 전통적인 폭포수 모델의 단점을 보완하고, 초기에 기능이 제한된 버전을 빠르게 제공하여 고객의 요구사항을 더 잘 반영할 수 있다.</p>
<h3 id="장점-1">장점</h3>
<ol>
<li>초기 가시성: 각각의 증분은 완전히 독립적으로 개발되므로 초기에도 기능이 제한된 시스템을 빠르게 제공할 수 있다.</li>
<li>고객 피드백: 완성된 증분을 제공함으로써 고객이 제품의 진행 상황을 확인하고 피드백을 제공할 수 있다.</li>
<li>변경 용이성: 각각의 증분은 독립적으로 개발되기 때문에 변경이 필요한 경우 해당 부분만 수정하면 되므로 전체적인 변경이 쉽다.</li>
</ol>
<h3 id="단점-1">단점</h3>
<ol>
<li>통합 문제: 각각의 증분을 통합하는 과정에서 일부 시스템 간의 호환성 문제가 발생할 수 있다.</li>
<li>초기 계획의 복잡성: 전체 시스템을 여러 증분으로 나누는 초기 계획이 복잡할 수 있다.</li>
<li>테스트 문제: 각각의 증분은 독립적으로 테스트되어야 하지만, 전체 시스템의 통합 테스트는 복잡성을 증가시킬 수 있다.</li>
</ol>
<h2 id="워터풀-모델-맛보기">워터풀 모델 맛보기</h2>
<p>랜덤채팅 서비스를 구현한다고 생각하고 워터풀 모델의 1단계인 요구사항 명세를 해봤다.
<img src="https://velog.velcdn.com/images/dev_lheesung/post/f514a0b1-afa9-4766-b045-31bfa21d5015/image.png" alt=""></p>
<p>GG</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[openCV] openCV 영상인식 발 담궈보기]]></title>
            <link>https://velog.io/@dev_lheesung/openCV%EB%82%B4-%EC%9B%B9%EC%BA%A0-%EC%BB%A4%EC%8A%A4%ED%84%B0%EB%A7%88%EC%9D%B4%EC%A7%95-%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dev_lheesung/openCV%EB%82%B4-%EC%9B%B9%EC%BA%A0-%EC%BB%A4%EC%8A%A4%ED%84%B0%EB%A7%88%EC%9D%B4%EC%A7%95-%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 20 Mar 2024 01:29:28 GMT</pubDate>
            <description><![CDATA[<h1 id="먼저-영상인식이-뭐냐">먼저! 영상인식이 뭐냐</h1>
<p>영상인식은 컴퓨터 비전 분야에서 중요한 기술 중 하나다. 이 기술은 디지털 이미지나 비디오에서 정보를 추출하고 해석하여 컴퓨터가 이해할 수 있는 형태로 변환하는 것을 목표로 한다.</p>
<p>영상인식은 일반적으로 다음과 같다.</p>
<ol>
<li><p><strong>전처리(Preprocessing)</strong>
입력된 이미지나 비디오를 컴퓨터가 처리하기 쉬운 형태로 변환한다. 이 단계에서는 이미지 크기 조정, 색상 변환, 잡음 제거 등의 작업이 수행될 수 있다. </p>
</li>
<li><p><strong>특징 추출(Feature Extraction)</strong>
이미지나 비디오에서 중요한 정보를 추출하는 과정이다. 주요한 특징을 식별하고, 이를 사용하여 이미지를 설명하는 특징 벡터를 생성한다. 예를 들어, 에지, 코너, 텍스처 등의 특징이 추출될 수 있다. </p>
</li>
<li><p><strong>학습 및 분류(Learning and Classification)</strong>
미리 정의된 학습 데이터셋을 사용하여 기계학습 알고리즘을 훈련시키고, 추출된 특징 벡터를 사용하여 이미지를 분류하거나 객체를 인식한다. 주로 사용되는 기계학습 기술로는 지도학습(Supervised Learning), 비지도학습(Unsupervised Learning), 강화학습(Reinforcement Learning) 등이 있다. </p>
</li>
<li><p><strong>평가 및 응용(Evaluation and Applications)</strong>
마지막으로, 인식된 결과를 평가하고 필요에 따라 추가적인 후처리를 수행한다. 이러한 결과는 다양한 응용 분야에 활용될 수 있다. 예를 들어, 자율주행 자동차, 보안 시스템, 의료 진단, 로봇 공학, 환경 모니터링 등 다양한 분야에서 활용될 수 있다.</p>
</li>
</ol>
<h1 id="opencv">openCV</h1>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/fb987b44-2d15-4170-b771-03353034052e/image.png" alt=""></p>
<p>OpenCV(Open Source Computer Vision Library)는 실시간 컴퓨터 비전을 위한 오픈소스 라이브러리로, 이미지 및 비디오 처리, 객체 검출, 얼굴 인식, 동영상 분석 등 다양한 컴퓨터 비전 작업을 수행할 수 있도록 설계되었다. OpenCV는 C++, Python, Java와 같은 다양한 프로그래밍 언어에서 사용할 수 있다.</p>
<h1 id="python환경에서-opencv써보기">python환경에서 openCV써보기</h1>
<p>먼저 아나콘다를 설치하자</p>
<pre><code class="language-bash">brew install --cask anaconda</code></pre>
<p>환경변수를 설정하고 가상환경을 만들자</p>
<pre><code class="language-bash">conda create -n &quot;환경명&quot; python=&lt;버전(ex:3.5이나 3.7 등)&gt;</code></pre>
<p>만든 가상환경으로 가자</p>
<pre><code class="language-bash">conda activate &quot;환경명&quot;
jupyter notebook</code></pre>
<p>주피터 노트북을 켜고 본격적으로 해보자</p>
<h1 id="opencv-맛보기">openCV 맛보기</h1>
<p>먼저 간단하게 이미지와 영상을 출력하는 법을 알아도록 하도록 하자.</p>
<pre><code class="language-python">import cv2

img = cv2.imread(&#39;./cookie.png&#39;)

cv2.imshow(&#39;lovely cookie&#39;, img)
cv2.waitKey()
cv2.destroyAllWindows()</code></pre>
<p>이렇게 하면 간단하게 맛있는 쿠키를 볼 수 있다.
<img src="https://velog.velcdn.com/images/dev_lheesung/post/7c1b623b-15b7-460c-8afe-fa6cd6c3d235/image.png" alt=""></p>
<pre><code class="language-python">import cv2

cap = cv2.VideoCapture(0)
print(cap.isOpened())
while(cap.isOpened()):
    ret, frame = cap.read()
    if ret :
        cv2.imshow(&#39;frame&#39;, frame)

        if cv2.waitKey(1) &amp; 0xFF == ord(&#39;q&#39;):
            break
    else:
        break

cap.release()
cv2.destroyAllWindows()</code></pre>
<p>동영상은 위와 같이 띄울 수 있다. 직접 해보도록.</p>
<pre><code class="language-python">import cv2

cap = cv2.VideoCapture(0)
print(cap.isOpened())
while(cap.isOpened()):
    ret, frame = cap.read()
    if ret :
        cv2.imshow(&#39;frame&#39;, frame)

        if cv2.waitKey(1) &amp; 0xFF == ord(&#39;q&#39;):
            break
    else:
        break

cap.release()
cv2.destroyAllWindows()</code></pre>
<p>위와 같이 코딩하면 웹캠을 띄울 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/9aa0c30f-3afd-46f5-ab12-ea2cf3e0827a/image.png" alt=""></p>
<p>이제 이미지와 웹캠에 글자를 써보자!!?!!</p>
<pre><code class="language-python">import cv2

img = cv2.imread(&#39;ㅇ.png&#39;, cv2.IMREAD_UNCHANGED)
blue = (255, 0, 0)
font =  cv2.FONT_HERSHEY_PLAIN

fontScale = 5

img = cv2.putText(img, &quot;happy&quot;, (0, 100), font, fontScale, blue, 2, cv2.LINE_AA) 
cv2.imshow(&#39;lovely cookie&#39; , img)
cv2.waitKey(0) 
cv2.destroyAllWindows()
</code></pre>
<p>이렇게 하면 왼쪽 위 글자가 생긴걸 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/0249b888-a2e8-4aa2-8026-9f83e405d35e/image.png" alt=""></p>
<pre><code class="language-python">import cv2


cap = cv2.VideoCapture(0)

frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
out = cv2.VideoWriter(&#39;output.avi&#39;, cv2.VideoWriter_fourcc(&#39;M&#39;,&#39;J&#39;,&#39;P&#39;,&#39;G&#39;), 10, (frame_width,frame_height), False)

font = cv2.FONT_HERSHEY_SIMPLEX
text_position_left = (10, 50)
text_position_right = (frame_width - 200, 50)
font_scale = 2
font_thickness = 2
font_color = (0, 0, 150)

while(cap.isOpened()):
    ret, frame = cap.read()
    if not ret:
        break

    cv2.putText(frame, &quot;HADURI&quot;, text_position_left, font, font_scale, font_color, font_thickness, cv2.LINE_AA)
    cv2.putText(frame, &quot;HeeSung&quot;, text_position_right, font, font_scale, font_color, font_thickness, cv2.LINE_AA)

    cv2.imshow(&#39;Webcam&#39;, frame)
    gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    out.write(gray_frame)

    if cv2.waitKey(1) &amp; 0xFF == ord(&#39;q&#39;):
        break

cap.release()
out.release()
cv2.destroyAllWindows()</code></pre>
<p>이렇게 웹 캠에도 커스텀을 할 수 있다!
<img src="https://velog.velcdn.com/images/dev_lheesung/post/0aa0e778-d32b-443a-8509-b33d7a3f6a7b/image.png" alt=""></p>
<p><code>gray_frame</code>을 활용하면 회백색 필터를 껴 웹캠을 띄울 수 도 있다.</p>
<p>다음엔 더 신기한걸 공부해보자꾸나.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[테스팅 라이브러리 알아보기]]></title>
            <link>https://velog.io/@dev_lheesung/%ED%85%8C%EC%8A%A4%ED%8C%85-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@dev_lheesung/%ED%85%8C%EC%8A%A4%ED%8C%85-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Tue, 06 Jun 2023 12:28:29 GMT</pubDate>
            <description><![CDATA[<h1 id="jest">Jest?</h1>
<p><strong>Jest</strong>는 우리가 짠 코드가 제대로 정상적으로 동작하는지 테스트 할 수 있게 테스트 케이스를 만드는 <strong>테스팅 프레임워크</strong>다.Jest는 zero-config 철학을 가지고 있기에 별도의 설정없이 테스트가 가능하다. Jest는 우리가 만든 비둘기가 머리가 돌아서 날아가는지 날개짓으로 날아가는지 체크할 수 있게 해준다.
<img src="https://velog.velcdn.com/images/dev_lheesung/post/46c0a483-0761-49e1-b788-af12f1b1a73d/image.png" alt=""></p>
<h1 id="jest-사용하기">Jest 사용하기</h1>
<p>이제 Jest를 사용해보자! 일단 먼저 설치를 해야 사용을 하겠죠잉?</p>
<pre><code class="language-bash">yarn add -D jest</code></pre>
<p>설치를 한 뒤 package.json의 scripts 안에 test의 값을 jest로 바꿔주면 준비가 완료된다.</p>
<pre><code class="language-bash">&quot;scripts&quot;: {
    &quot;test&quot;: &quot;jest&quot;
}</code></pre>
<p>간단하게 Jest를 사용해보자. 먼저 func.js 라는 파일을 만들고 다음과 같이 두 수를 더하는 함수를 만들자.</p>
<pre><code class="language-js">const func ={
    add: (num1, num2) =&gt; num1 + num2,
};

module.exports = func;</code></pre>
<p>이제 func.js 와 똑같은 이름으로 func.test.js를 만들어 test를 작성해보자!</p>
<pre><code class="language-js">const func = require(&#39;./func&#39;);

test(&#39;1은 1이다!&#39;, ()=&gt;{
    expect(1).toBe(1);
});

test(&#39;2 더하기 3은 4다!&#39;, ()=&gt;{
    expect(func.add(2, 3)).not.toBe(5);
});

test(&#39;1 더하기 1은 1이다!&#39;, ()=&gt;{
    expect(func.add(1, 1)).toBe(1);
});</code></pre>
<p>위 코드에서는 <code>expect()</code>에 실제 값을 넣어주고 <code>toBe()</code>에 기대하는 값을 넣어주면 된다. 이제 func.test.js를 실행해보자.</p>
<pre><code class="language-bash">yarn test</code></pre>
<p>위와 같이 적으면 <code>__test__</code>라는 파일에 있는 파일 혹은 test 파일을 모두 실행시켜 준다. 그러면 아래 사진과 같이 테스팅이 결과를 확인할 수 있다.
<img src="https://velog.velcdn.com/images/dev_lheesung/post/0f0d48df-5816-47e7-9f56-bcda8492b8ff/image.png" alt=""></p>
<h2 id="유용한-matcher-알아보기">유용한 Matcher 알아보기</h2>
<p>Matcher 는 앞에서 썼던 <code>toBe()</code> 와 같이 사용되는 함수를 말한다. 이름과 나이를 받아서 객체로 반환시켜주는 간단한 함수를 만들어보자!</p>
<pre><code class="language-js">const func ={
    add: (num1, num2) =&gt; num1 + num2,
    makeUser: (name, age) =&gt; ({name, age}),
};

module.exports = func;</code></pre>
<p>이제 테스트를 해보자!</p>
<pre><code class="language-js">test(&#39;이름과 나이를 전달받아 객체를 반환!!&#39;, ()=&gt;{
    expect(func.makeUser(&quot;Lee&quot;, 18)).toBe({
        name: &quot;Lee&quot;,
        age: 18
    })
})</code></pre>
<p>위에서 썼던 것과 같이 <code>toBe()</code>를 사용하니깐 테스팅에 실패했다..! 분명 기대값과 기존값이 같은데 왜 그럴까? 객체나 배열은 재귀적으로 돌면서 값을 확인해야하기 때문에 <code>toEqual()</code>를 써줘야 한다. 그럼 코드를 고쳐보자.</p>
<pre><code class="language-js">test(&#39;이름과 나이를 전달받아 객체를 반환!!&#39;, ()=&gt;{
    expect(func.makeUser(&quot;Lee&quot;, 18)).toEqual({
        name: &quot;Lee&quot;,
        age: 18
    })
})</code></pre>
<p>코드를 위와 같이 <code>toEqual</code>로 바꿔주니 성공적으로 테스팅이 되는것을 확인할 수 있을 것이다. 여기서 더 깊이 비교를 하고 싶다면 <code>toStrictEqual</code>을 사용하면 된다. 이 외에도 <code>toBeNull</code>, <code>toBeUndefined</code>, <code>toBeDifiend</code>, <code>toBeTruthy</code>, <code>toBeFalsy</code> 와 같이 많은 매쳐들이 있다. (이름이 엄청 직관적!)</p>
<h1 id="playwright">Playwright?</h1>
<p><strong>Playwright</strong>는 하나의 API로 모든 브라우저에서 안정적으로 자동화를 할 수 있도록 MS가 만든 자동화 도구다. <strong>Playwright</strong>는 여러개의 페이지, 도메인, iframe에 걸쳐 테스트 시나리오를 작성할 수 있다. 그럼 이제 <strong>Playwright</strong>를 사용해보자!</p>
<pre><code class="language-bash">yarn init playwright@latest</code></pre>
<p>위와 같이 초기화가 끝나면 준비가 끝났다. 이제 테스트를 해보자!</p>
<h2 id="browser">Browser</h2>
<p>브라우저의 값을 나타낸다. Playwright는 브라우저 인스턴스를 시작하고 닫는 것으로 끝낸다. </p>
<pre><code class="language-js">const { chrominum } = require(&#39;playwright&#39;);

const browser = await chromium.launch();
// 테스팅 코드..
await browser.close();</code></pre>
<h2 id="context">context</h2>
<p>Playwright에서는 컨텍스트를 나누어 각 테스트를 새로운 브라우저 컨텍스트에서 실행하여 테스트 간의 상태를 분리하기 용이하다.</p>
<pre><code class="language-js">const browser = await chromium.launch();
const context = await browser.newContext();</code></pre>
<h2 id="pages--frames">Pages &amp; Frames</h2>
<p>컨텍스트는 단일 탭이나 팝업 창을 URL을 통해 이동하여 상요작용 할 수 있다. 페이지를 이용하여 각각 다른 탭에서 작업하는 것 처럼 할 수 있다.</p>
<pre><code class="language-js">const page1 = await context.newPage();
await page1.goto(&#39;happyhappyhappy.com&#39;);
await page1.fill(&#39;#search&#39;, &#39;query&#39;);

const page2 = await context.newPage();
await page2.goto(&#39;lheesung.com&#39;);
await page2.click(&#39;.lheesung_locale&#39;);

console.log(page.url()); // 새로운 url 출력</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next]렌더링 전략 알아보기]]></title>
            <link>https://velog.io/@dev_lheesung/Next%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%A0%84%EB%9E%B5-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@dev_lheesung/Next%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%A0%84%EB%9E%B5-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Fri, 02 Jun 2023 12:46:05 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가며">들어가며..</h1>
<p>렌더링 전략이란 웹 페이지 or 웹 애플리케이션을 웹 브라우저에서 사용자에게 제공하는 방식을 말한다. 오늘 알아볼 Next.js의 렌더링 전략은 기존의 프레임워크와는 완전히 새로운 수준을 제공한다. 이번에는 Next.js의 렌더링 전략에 대해 알아보자.</p>
<h1 id="ssrserver-side-rendering">SSR(Server Side Rendering)</h1>
<p>서버 사이드 렌더링은 웹 페이지를 제공하는 가장 흔한 방법이다. Next.js에서는 서버에 HTML 페이지를 동적으로 렌더링하고 웹 브라우저로 전송할 수 있다. 또 _*하이드레이션_을 이용하여 처리한다.</p>
<blockquote>
<p>💡 하이드레이션(hydration)
하이드레이션은 서버 측에서 생성한 HTML 페이지를 구성하는 각각의 DOM 객체에 필요한 JS코드를 추가하여 클라이언트가 동적으로 렌더링 할 수 있는 것을 말한다.</p>
</blockquote>
<p>SSR을 적용하면 좋을 때를 알아보자. 예를 들어 블로그를 만든다고 해보자. 사람들이 작성한 블로그를 한 페이지에 렌더링 한다고 할 때 SSR을 적용하기 적합하다. 사용자가 페이지에 접근하면 서버는 페이지를 렌더링 해서 HTML 페이지를 만들 클라이언트로 보낸다. 그리고 브라우저는 받은 페이지에 필요한 JS코드를 추가한 뒤 각 JS 코드를 하이드레이션한다. 그렇기에 페이지를 새로 고치지 않아도 사용자와 웹 페이지가 원활하게 상호작용할 수 있다.
이제 SSR의 장단점을 알아보자!</p>
<h2 id="👍-이-놈은-더-안전한-웹-애플리케이션-관리가-가능하다">👍 이 놈은 더 안전한 웹 애플리케이션 관리가 가능하다</h2>
<p>SSR을 사용하면 페이지를 서버에서 렌더링한다는 것은 쿠키 관리, 주요 API 과 같은 작업들을 서버에서 처리하기에 중요한 데이터를 클라이언트에게 노출할 필요가 없다.</p>
<h2 id="👍-이-놈은-웹사이트-호환성이-뛰어나다">👍 이 놈은 웹사이트 호환성이 뛰어나다</h2>
<p>클라리언트 환경이 JS를 사용하지 못하거나 오래된 브라우저라도 SSR을 사용하면 웹 페이지를 제공할 수 있다.</p>
<h2 id="👍-이-놈은-seo-가-뛰어나다">👍 이 놈은 SEO 가 뛰어나다</h2>
<p>서버가 렌더링한 HTML 콘텐츠를 클라이언트가 받기만 하기 때문에 웹 크롤러와 같은 검색 엔진 웹 문서 수집기가 페이지를 렌더링할 필요가 없기에 SEO 점수가 높아진다.</p>
<hr>

<h2 id="👎-이-놈은-ttfb가-늦어질-수-있다">👎 이 놈은 TTFB가 늦어질 수 있다.</h2>
<p>SSR은 새로운 페이지로 넘어갈 때 마다 페이지를 렌더링 하기 때문에 서버에서 페이지를 생성하는 데에 시간이 걸려 TTFB(Time to First Byte)가 느려질 수 있다.</p>
<h2 id="👎-서버에-부하가-갈-수-있다">👎 서버에 부하가 갈 수 있다.</h2>
<p>SSR은 페이지를 로딩할 때마다 서버에 요청을 보내기 때문에 서버에 부하가 갈 수 있다.</p>
<h1 id="ssgstatic-side-generation">SSG(Static side Generation)</h1>
<p>SSG는 페이지의 일부 혹은 전체 페이지를 빌드 시점에서 미리 랜더링 하는 방법이다. 웹 애플리케이션을 빌드할 때 내용이 거의 변하지 않는 페이지는 정적으로 만드는것이 아무래도 좋을 것이다. Next.js는 이런 페이지를 빌드 과정에서 정적인 페이지 형태로 미리 렌더링 하여 HTML 마크업 형태로 제공해준다. <em>(SSR 처럼)</em> 이제 SSR의 장단점을 알아보자.</p>
<h2 id="👍-이-놈은-확장이-쉽다">👍 이 놈은 확장이 쉽다</h2>
<p>정적인 페이지는 단순 HTML 파일이기에 CDN을 통해서 파일을 제공하거나 캐시에 저장하기 쉽다. SSG는 별도의 연산 없이 정적 자원 형태로 제공되기에 서버에 부하를 거의 주지 않는다</p>
<h2 id="👍-이-놈은-성능이-좋다">👍 이 놈은 성능이 좋다</h2>
<p>SSG는 웹 서버에서 정적 파일을 보내기만 하고 클라이언트 부라우저는 파일을 받아서 표시만 하면 되기에 정적 HTML 마크업 내에 미리 렌더링한 내용만 있으면 된다. 즉, 각 요청별로 발생할 수 있는 지연 시간을 최소화 할 수 있기에 좋은 성능을 보인다.</p>
<h2 id="👍-이-놈은-api-요청을-더-안전하게-할-수-있다">👍 이 놈은 API 요청을 더 안전하게 할 수 있다</h2>
<p>SSG는 필요한 정보가 이미 미리 렌더링 되어있기에 서버에서 클라이언트에게 민간한 데이터를 보낼 필요가 없어지기에 사용자가 악의적인 행동을 할 여지를 주지않는 무시무시한 녀석이다.</p>
<hr>

<h2 id="👎-이-놈은-데이터의-변화를-바로-적용시킬-수-없다">👎 이 놈은 데이터의 변화를 바로 적용시킬 수 없다</h2>
<p>만약 우리가 페이지에 어떠한 데이터를 바꾸고 싶어 바꿨지만 SSG 를 사용하면 빌드 시 이미 페이지가 캐싱되어있기에 데이터의 변화가 바로 적용되지 않는다.
이러한 SSG의 문제를 해결하기 위해서 ISR 이라는 녀석이 태어났다.</p>
<h2 id="isrincremental-static-regeneration">ISR(Incremental Static Regeneration)</h2>
<p>ISR은 앞서 말했듯이 정적 사이트 생성을 사용하면서도 동적 데이터를 자주 업데이트해야 하는 경우에 유용하게 사용된다. 간단한 코드를 통해 ISR을 사용하는 법을 알아보자!</p>
<pre><code class="language-js">export default function Example({ data }) {
  return (
    &lt;div&gt;
      &lt;h1&gt;Example Page&lt;/h1&gt;
      &lt;p&gt;{data}&lt;/p&gt;
    &lt;/div&gt;
  );
}

export async function getStaticProps() {
  const res = await fetch(&#39;zoooso&#39;);
  const data = await res.json();

  return {
    props: {
      data,
    },
    revalidate: 600,
  };
}</code></pre>
<p>위 코드에서 <code>getStaticProps</code> 함수는 페이지를 사전 렌더링하기 위해 필요한 데이터를 가져오는 역할을 한다. <code>revalidate</code> 옵션은 페이지의 재생성 주기를 설정한다. 위 코드에서는 10분마다 한 번씩 페이지를 재생성 하도록 설정되어있다.</p>
<h1 id="마치며">마치며</h1>
<p>사실 이번 기회를 통해 렌더링 전략에 대해 처음으로 자세히 공부해 보았다. 이런 전략들을 더 공부해서 효율적인 코드를 짜는 개발자가 될 수 있도록 노력해보도록 하겠다! 그럼 20000<img src="https://velog.velcdn.com/images/dev_lheesung/post/2839489e-70e1-45df-b7b6-9a56c622ebe0/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Redux를 알아보자]]></title>
            <link>https://velog.io/@dev_lheesung/Redux%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@dev_lheesung/Redux%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Wed, 31 May 2023 13:41:09 GMT</pubDate>
            <description><![CDATA[<h1 id="redux">Redux?</h1>
<p>시작하기 전 Redux가 뭔지 알아보자. Redux는 <strong>상태를 관리하는 라이브러리</strong>다. Redux를 사용하면 컴포넌트의 상태 업데이트에 관한 로직을 각각의 파일로 나누어 더 효율적으로 관리할 수 있게 해주는 녀석이다.</p>
<p>더 알아보기 전 Redux에서 사용되는 키워드들을 먼저 정리하고가보자.</p>
<h2 id="action">Action</h2>
<p>상태에 어떠한 변화가 필요할 때 Action이라는 것이 발생한다. Action은 하나의 객체로 표현되며 이는 다음과 같은 형식을 가지고 있다.</p>
<pre><code class="language-jsx">const Action = {
    type: &quot;ACTION_TYPE&quot;,
    data: {
        id: 1,
        content: &#39;Redux를 알아보자&#39;
    }
}</code></pre>
<p>Action 객체는 반드시 type 필드를 반드시 있어야 하며 그 type 의 이름이 Action의 이름이라고 생각하면 된다. Action 의 이름은 우리가 마음대로 정할 수있다.</p>
<h3 id="actoin-생성-함수">Actoin 생성 함수</h3>
<p>Action 생성 함수는 말 그대로 Action을 만드는 함수다. 상태에 어떠한 변화를 일으켜야 할 때마다 액션 객체를 만들어야하는 번거로운 작업을 방지하기위해 Action 생성함수를 사용한다. Action 생성 함수의 형태는 다음과 같다. (Action 생성 함수 작성은 필수적이지 않음.)</p>
<pre><code class="language-jsx">export const changeState = text =&gt; ({type: &quot;CHANGE_STATE&quot;});</code></pre>
<h2 id="reducer">Reducer</h2>
<p>reducer 는 변화를 일으키는 함수라고 생각하면 된다. Action을 생성하여 발생시키면 Reducer가 현재 상태와 전달받은 Action 객체를 매개변수로 받아온다. 받아온 두 값을 참고하여 새로운 상태를 만들어 주는게 Reducer다. Reducer의 형태는 다음과 같다.</p>
<pre><code class="language-jsx">// 초기값 세팅
const initialState = {
    counter: 0
}

// reducer 함수
const reducer(state = initialState, action) {
    switch(action.type) {
        case INCREMENT:
            return { counter: state.counter + 1 };
        default:
            return state;
    }
}</code></pre>
<h2 id="store">Store</h2>
<p>Store는 현재의 앱 상태와, 리듀서, 내장함수 포함하는 것이다. Store는 한 어플리케이션에 하나씩 존재한다. Store를 정의하는 방법은 다음과 같다.</p>
<pre><code class="language-jsx">const { createStore } = Redux;
const store = createStore(counterReducer);</code></pre>
<h2 id="dispatch">dispatch</h2>
<p>dispatch 는 Store의 내장함수 중 하나다. dispatch는 Action은 발생시키는 것이라고 생각하면 된다. dispatch 는 Action을 매개변수로 전달한다. dispatch의 형태는 다음과 같다.</p>
<pre><code class="language-jsx">// Action 생성자
const increment = () =&gt; ({ type: INCREMENT });
const decrement = () =&gt; ({ type: DECREMENT });

// Action 디스패치
store.dispatch(increment());
console.log(store.getState()); // 출력: 1</code></pre>
<h2 id="subscribe">subscribe</h2>
<p>subscribe는 Store의 내장함수 중 하나다. subscribe 함수 안에는 리스너함수가 매개변수로 넣어 호출해 주면, 이 리스너 함수가 Action이 dispatch되어 상태가 업데이트될 때마다 호출된다. subscribe 의 형태는 다음과 같다.</p>
<pre><code class="language-jsx">const listener =()=&gt; {
    console.log(&quot;state is updated&quot;);
}

const unsubscribe = store.subscribe(listener);

unsubscribe(); // 구독 비활성화 시 호출하여 사용.</code></pre>
<p>react에서는 상태를 조회하는 과정에서 react-router라는 라이브러리가 대신 해주기 때문에 할 필요가 없다. </p>
<h1 id="redux의-3가지-규칙">Redux의 3가지 규칙</h1>
<h2 id="1-하나의-애플리케이션-안에는-하나에-스토어">1. 하나의 애플리케이션 안에는 하나에 스토어</h2>
<p>하나의 애플리케이션에는 하나에 스토어만 만들어서 사용한다. 여러개가 가능하긴 하지만 권장하지 않음.</p>
<h2 id="2-읽기-전용-상태">2. 읽기 전용 상태</h2>
<p>Redux는 기존의 상태를 건들이지 않고 새로운 객체를 생성해 불변성을 유지해야한다.</p>
<h2 id="3-리듀서는-순수한-함수">3. 리듀서는 순수한 함수</h2>
<p>리듀서는 이전 상태와 액션 객체를 매개변수로 받는다. 리듀서는 받은 매개변수 이외에는 의존해선 안된다. 리듀서는 불변성과 멱등성을 항상 만족해주어야한다.</p>
<h1 id="redux의-장점">Redux의 장점</h1>
<h2 id="1-단방향-모델링">1. 단방향 모델링</h2>
<p>actoin 을 dispatch 할 때마다 기록이 남아서 에러를 찾기 용이하다. 시간 여행 디버깅이 가능하다.</p>
<h2 id="2-상태의-중앙화">2. 상태의 중앙화</h2>
<p>Store를 통해 상태를 한 곳에서 관리하기에 전역상태를 관리할 때 좋다.</p>
<h2 id="3-읽기-전용">3. 읽기 전용</h2>
<p>앞에서 말했듯이 Redux 는 읽기 전용이다. 즉 이전 상태로 돌아가려면 이전의 상태를 현재의 상태에 덮어쓰기만 하면 된다.</p>
<h1 id="redux의-단점">Redux의 단점</h1>
<h2 id="1-코드량이-늘어난다">1. 코드량이 늘어난다</h2>
<p>사용할 때 마다 Action 과 같은 것들을 만들어줘야 하기 때문에 파일이나 코드량이 늘어난다.</p>
<h2 id="2-매번-객체를-생성해줘야한다">2. 매번 객체를 생성해줘야한다</h2>
<p>시간 여행 디버깅을 위해 불변성을 지켜줘야해서 매번 state(복사한) 객체를 만들어줘야한다.</p>
<h2 id="3-진짜로-읽기-전용이-아니다">3. 진짜로 읽기 전용이 아니다.</h2>
<p>Redux는 상태를 읽기 전용으로 취급하긴 하지만 실제 읽기 전용으로 만들어주지 않아 상태를 변경하지 않도록 주의해야한다.</p>
<h1 id="todo-list-만들기-redux--react">Todo List 만들기 (Redux + React)</h1>
<p>Redux 와 React 를 함께 사용하여 앞에서 말했던 순서대로 Todo List 를 구현해보자.</p>
<h2 id="action-type-정의">Action type 정의</h2>
<p>Action type 의 정의는 프로젝트가 커젔을 때 이름이 곂치는걸 방지하기 위해 ‘모듈명/액션명’ 형식으로 작성한다. </p>
<pre><code class="language-jsx">const CHANGE_INPUT = &#39;todos/CHANGE_INPUT&#39;;
const INSERT = &#39;todos/INSERT&#39;;
const TOGGLE = &#39;todos/TOGGLE&#39;;
const REMOVE = &#39;todos/REMOVE&#39;;</code></pre>
<h2 id="action-생성함수-생성">Action 생성함수 생성</h2>
<pre><code class="language-jsx">let id = 1;

export const changeInput = input =&gt; ({
    type: CHANGE_INPUT,
    input
});

export const onInsertTodo = (contents) =&gt; ({
    type: INSERT,
      todo: {
          id: id++,
          contents,
          isCompleted: false
    }
});

export const onToggleTodo = (id) =&gt; ({
      type: TOGGLE,
      id
});

export const onRemoveTodo = (id) =&gt; ({
      type: REMOVE,
      id
});</code></pre>
<h2 id="초기값-세팅">초기값 세팅</h2>
<pre><code class="language-jsx">const initTodo = {
     input: &#39;&#39;,
      todos: [
          { id: 1, contents: &#39;리액트를 다루는 기술 공부하기&#39;, isCompleted: false },  
          { id: 2, contents: &#39;운동하기&#39;, isCompleted: true }
    ]
}</code></pre>
<h2 id="reducer-함수-정의">Reducer 함수 정의</h2>
<pre><code class="language-jsx">const todoReducer = (state = initTodo, action) =&gt; {
    const { type, input, id } = action
    const { todos } = state

    if (type === CHANGE_INPUT) { return { ...state, input } }

    if (type === INSERT) { return { ...state, todos: [...todos, action.todo] } }

    if (type === TOGGLE) {
        return {
            ...state,
            todos: state.todos.map(todo =&gt;
                todo.id === action.id ? { ...todo, done: !todo.done } : todo
            )
        }
    }

    if (type === CHANGE_INPUT) { return { ...state, input } }

    if (type === REMOVE) {
        return { ...state, todos: todos.filter((todo) =&gt; todo.id !== id) }
    }
    return state;
}

export default todos;</code></pre>
<h2 id="루트-리듀서-만들기">루트 리듀서 만들기</h2>
<p>createStore 라는 함수를 사용해 스토어를 만들 때는 리듀서를 하나만 써야하기 때문에 리덕스에 있는 combineReducers 라는 함수를 사용하여 함수명처럼 함수를 묶어줄 수 있다.</p>
<pre><code class="language-jsx">import {...}

const reducer = combineReducers({todos})</code></pre>
<h2 id="스토어-만들기--리덕스-적용하기">스토어 만들기 &amp; 리덕스 적용하기</h2>
<p>Redux 에서 제공하는 Provider 컴포넌트로 App 을 감싸주면 리액트 컴포넌트에서 store 를 사용할 수 있다.</p>
<pre><code class="language-jsx">import {...}

const store = createStore(rootReducer);

const root = ReactDom.createRoot(document.getElementById(&#39;root&#39;));
root.render(
    &lt;Provider store={store}&gt;
        &lt;App /&gt;
    &lt;/Provider&gt;
);</code></pre>
<h2 id="connect-시켜주기">Connect 시켜주기</h2>
<p>컴포넌트와 리덕스를 연동하려면 connect 라는 함수를 사용해야한다. connect 의 형태는 다음과 같다.</p>
<pre><code class="language-jsx">const makeContainer = connect(mapStateToProps, mapDispatchToProps);
makeContainer(&lt;Component /&gt;);</code></pre>
<p>mapStateToProps는 스토어 안에 상태를 props로 넘겨주기 위해 설정하는 함수다.</p>
<p>mapDispatchToProps는 액션 생성 함수를 컴포넌트에 props로 넘겨주기 위해 사용하는 함수다. </p>
<h2 id="todoscontainer-만들기">TodosContainer 만들기</h2>
<p>이제 Todos 컴포넌트를 위한 TodosContainer 를 만들어보자. 앞에서 말한듯이 connect 함수를 사용하여 만들어보자.</p>
<pre><code class="language-jsx">import {...}

const TodoContainer = ({
    input,
    todos,
    chageInput,
    insert,
    toggle,
    remove,
}) =&gt; {
    return (
        &lt;Todos 
            input={input}
            todos={todos}
            onChangeInput={changeInput}
            onInsert={insert}
            onToggle={toggle}
            onRemvoe={remove}
        /&gt;
    )
}

export default conncet(
    ({ todos }) =&gt; ({
        input: todos.input,
        todos: todos.todos
    }),
    {
        changeInput,
        insert,
        toggle,
        remove
    },
)(TodosContainer);</code></pre>
<h2 id="todos-component-만들기">Todos Component 만들기</h2>
<pre><code class="language-jsx">const TodoItem = ({ todo, onToggle, onRemove }) =&gt; {
    const Todos = ({
        input,
        todos,
        onChangeInput,
        onInsert,
        onToggle,
        onRemove
    }) =&gt; {
        const onSubmitTodo = (e) =&gt; {
            e.preventDefault();
            onInsert(input);
            onChangeInput(&#39;&#39;);
        }

        return (
            &lt;div&gt;
                &lt;form onSubmit={onSubmitTodo} &gt;
                    &lt;input value={input} onChange={onChange} /&gt;
                    &lt;button type=&quot;submut&quot;&gt;등록&lt;/button&gt;
                &lt;/form&gt;
                &lt;div&gt;
                    {todos.map((todo) =&gt; (
                        &lt;TodoItem {...todo} /&gt;
                    ))}
                &lt;/div&gt;
            &lt;/div&gt;
        )
    }
}</code></pre>
<h1 id="redux-를-더-편하게">Redux 를 더 편하게</h1>
<h2 id="redux-actions">redux-actions</h2>
<p>redux-actions 는 액션 생성 함수를 더 짧게 쓸 수 있게 해준다. 또 리듀서를 작성할 때도 조건문이 아닌 handleActions 라는 함수를 사용하여 작성 할 수 있게 해준다. 간단한 counter 를 만들면서 알아보자</p>
<pre><code class="language-jsx">import { createAction } from &#39;redux-actions&#39;

export const increase = createAction(INCREASE)
export const decrease = createAction(DECREASE)

const initialState = {
  number: 0,
}

const counterReducer = handleActions(
  {
    [INCREASE]: (state, action) =&gt; ({ number: state.number + 1 }),
    [DECREASE}: (state, action) =&gt; ({ number: state.number - 1 }),
  },
  initialState,
) </code></pre>
<p>위에서 사용했던 액션 생성 함수와는 확연히 더 간결해 진 걸 볼 수 있다. </p>
<h2 id="redux-actions-액션-생성-함수에-파라미타-주기">redux-actions 액션 생성 함수에 파라미타 주기</h2>
<pre><code class="language-jsx">const MY_ACTION = &#39;todos/MY_ACTION&#39;;
console.log(myAction(&#39;반갑다 리덕스야~~&#39;))
// console: { type: MY_ACTION, payload: &#39;반갑다 리덕스야~~&#39; }</code></pre>
<p>액션 생성 함수에서 받아온 그대로 payload에 넣는게 아니라 변형을 시켜서 넣고싶으면 아래 코드처럼 createAction의 두번째 함수에 payload를 정의하는 함수를 따로 만들어 넣어주면 된다. </p>
<pre><code class="language-jsx">const myActioN = createAction(MY_ACTION, (contents) =&gt; `${contents}`)

const insert = createAction(INSERT, (payload) =&gt; ({
  id: id++,
  contents,
  isCompleted: false
})</code></pre>
<h1 id="hooks-를-사용하여-컨테이너-컴포넌트-만들기">Hooks 를 사용하여 컨테이너 컴포넌트 만들기</h1>
<p>react-redux에서 제공하는 훅들을 사용하면 컨테이너를 꼭 만들지 않아도 상태를 불러올 수 있다.</p>
<h2 id="useselector-로-상태-조회하기">useSelector 로 상태 조회하기</h2>
<p>useSelector 라는 hook 을 사용하면 connect 함수를 사용하지 않고도 리덕스의 상태 조회가 가능하다. useSelecor 의 문법은 counter 를 통해 알아보자.</p>
<pre><code class="language-jsx">
const result = useSelector(state =&gt; state.counter.number);</code></pre>
<p>위 코드에서 상태 선택 함수는 앞에서 나왔던 mapStateToProps와 같다. </p>
<h2 id="usedispatch-로-액션-디스패치하기">useDispatch 로 액션 디스패치하기</h2>
<pre><code class="language-jsx">import { useSelector, useDispatch } from &#39;react-redux&#39;

const TodoList =()=&gt; {
    const todos = useSelector(state =&gt; state);
    const dispatch = useDispatch();

    return &lt;TodoListitem 
        onIncrease={()=&gt;dispatch(increase())}
        onDecrease={()=&gt;dispatch(decrease())}
    /&gt;
}</code></pre>
<p>useDispatch 를 사용할 때는 위와 같이 useCallback과 함께 쓰는 습관을 들이는 것이 좋다고 한다.</p>
<h2 id="usestore-로-스토어-쓰기">useStore 로 스토어 쓰기</h2>
<p>useStore 는 컴포넌트 내부에서 스토어 객체를 직접 사용할 수 있게 해준다. 문법은 다음과 같다.</p>
<pre><code class="language-jsx">const store = useStore();
store.dispatch({type:&#39;EXMPLE_ACTION&#39;});
console.log(store.getState());
// {type:&#39;EXMPLE_ACTION&#39;}</code></pre>
<h2 id="useactions-로-유틸-훅-만들어서-쓰기">useActions 로 유틸 훅 만들어서 쓰기</h2>
<p>useActions 는 리덕스 개발팀이 꼭 필요하지 않다고 판단하여 실제로 출시한 훅은 아니지만 공식문서에서 복사하여 사용할 수 있다.</p>
<pre><code class="language-jsx">import { bindActionCreators } from &#39;redux&#39;;
import { useDispatch } from &#39;react-redux&#39;;
import { useMemo } from &#39;react&#39;;

const useActions = (actions, deps) =&gt; {
    const dispatch = useDispatch();
      return useMemo(
        () =&gt; {
         if(Arrays.isArray(actions)){
              return actions.map(a =&gt; bindActionCreators(a, dispatch))
         }
         return bindActionCreators(actions, dispatch);
        },
      deps ? [dispatch, ...deps] : deps
    );
}

export default useActions</code></pre>
<p>useActions를 사용하면 액션함수가 여러개 있을 때 코드의 유지보수를 용이하게 해주고 가독성을 높혀준다. Todo 를 예시로 이녀석의 장점을 알아보자</p>
<pre><code class="language-jsx">const onChangeInput = useCallback((input) =&gt; dispatch(changeInput(input)), [dispatch]);
const onInsert = useCallback((text)=&gt;dispatch(insert(text)), [dispatch])
const onToggle = useCallback((id)=&gt;dispatch(toggle(id)), [dispatch])
const onRemove = useCallback((id)=&gt;dispatch(remove(id)), [dispatch])</code></pre>
<pre><code class="language-jsx">import {...}
const [onChangeInput, onInsert, onToggle, onRemove] = useActions([changeInput, insert, toggle, remove], []);</code></pre>
<p>원래 였으면 4줄이나 나왔을 코드를 한줄만에 줄일 수 있는 개꿀 훅이다.</p>
<h1 id="리덕스-미들웨어">리덕스 미들웨어</h1>
<p>리액트에서 리덕스를 사용할 때 비동기 작업을 관리해야 할 때 미들웨어를 사용하면 매우 효율적이고 편하게 상태를 관리 할 수있다. 미들웨어는 액션을 디스패치할 때 처리하기 전 저장된 작업들을 실행시킨다.</p>
<pre><code class="language-jsx">const loggerMiddleware = store =&gt; next =&gt; action =&gt; {
        console.log(`prev : ${store.getState()}`)
      console.log(`action : ${action}`)
      next(action);
      console.log(`next : ${store.getState()}`)
}</code></pre>
<p>다음 코드를 보면 함수가 함수를 반환하고 또 그 함수가 다음 함수를 반환하는 형식이다.</p>
<p>미들웨어를 추가하는 방법은 다음과 같다.</p>
<pre><code class="language-jsx">import { createStore, applyMiddleware } from &#39;redux&#39;
import loggerMiddleWare from &#39;./lib/loggerMiddleWare&#39;

const store = createStore(reducer, applyMiddleware(loggerMiddleWare))</code></pre>
<h2 id="redux-logger-사용하기">redux-logger 사용하기</h2>
<p>redux-logger 은 앞서 사용했던 loggerMiddleWare 보다 더 유용한 라이브러리다. 알아보자~</p>
<pre><code class="language-jsx">const logger = createLogger();
const store = createStore(rootReducer, applyMiddleware(logger));</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React]React Query 를 알아보자~!]]></title>
            <link>https://velog.io/@dev_lheesung/ReactReact-Query-%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@dev_lheesung/ReactReact-Query-%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Tue, 30 May 2023 00:57:49 GMT</pubDate>
            <description><![CDATA[<h1 id="react-query">React Query?</h1>
<p>React Query 는 어플리케이션의 <strong>비동기 데이터를 쉽게 관리</strong>하고 동기화 할 수 있도록 도와주는 React 전용 라이브러리다. React Query 는 데이터의 fetching, caching, updating 과 컴포넌트 트리의 변경사항에 대한 과정을 쉽게 만들어 준다. 또한 실시간 업데이트, 페이지네이션, 오류 처리 등 다양한 기능을 제공한다. </p>
<h1 id="react-query-장점">React Query 장점</h1>
<ul>
<li>캐싱을 쉽게 해준다</li>
<li>get을 한 데이터에 대하여 update를 하면 자동으로 get을 다시 수행한다.<ul>
<li>가져온 데이터로 컴포넌트를 생성하면 데이터를 get 하는 api 가 자동으로 실행됨.</li>
</ul>
</li>
<li>데이터가 오래되었다 판단되면 다시 get 해준다.</li>
<li>페이지네이션 지원.</li>
<li>react hook 과 비슷함.</li>
</ul>
<hr>
오늘은 React Query에 useQuery와 useMutation에 대해 알아보는 절차를 가져보도록 하는 절차를 가져보자!
<hr>

<h1 id="usequery">useQuery</h1>
<p><code>useQuery</code>는 서버에서 데이터를 조회해올 때 사용하는 녀석이다. 데이터베이스에 비유하면 <code>select</code> 키워드 정도라고 생각하면 된다. <code>useQuery</code>를 사용하려면 먼저 <code>queryKey</code>와 <code>queryFn</code>에 대해 알아보고 가야한다. 가보자잇~</p>
<h2 id="querykey">queryKey</h2>
<p><code>queryKey</code>란 useQuery마다 부여되는 고유 Key 값이다. <code>queryKey</code>는 문자열 또는 배열 형태로 사용된다. <code>queryKey</code>는 React Query가 query 캐싱을 관리할 수 있도록 도와준다.<code>queryKey</code>의 문법은 다음과 같다.</p>
<pre><code class="language-js">const res0 = useQuery(&#39;users&#39;,queryFn);
const res1 = useQuery([&#39;users&#39;, &#39;add Id&#39;]);
const res2 = useQuery([&#39;add Id&#39;,&#39;users&#39;]);
const res3 = useQuery([&#39;users&#39;,{type: &#39;add&#39;, name: &#39;ID&#39;}], queryFn); </code></pre>
<p><code>res0</code>은 <code>&#39;users&#39;</code>라는 문자열이 <code>queryKey</code>로 사용되고 있다.
<code>res1</code>과 <code>res2</code>는 언뜻보기에 같아 보이지만 React Query에서는 <code>queryKey</code>할당 시 입력되는 순서도 보장해주고 있기 때문에 다른 <code>queryKey</code>로 인식한다.
이제 예시를 통해 이놈이 무슨 역할을 하는지 알아보자.</p>
<pre><code class="language-jsx">import {...}

const Query = () =&gt; {
 const getUsers1 = () =&gt; {
     const res1 = useQuery([&#39;users&#39;],queryFn1);
 }
 const getUsers2 = () =&gt; {
     const res2 = useQuery([&#39;users&#39;],queryFn2); // queryFn2는 생략해도 좋다~!
 }
 return(
     &lt;&gt;
     {getUsers1()}
     {getUsers2()}
    &lt;/&gt;
 )
}</code></pre>
<p>위 코드에서는 <code>res1</code>과 <code>res2</code>가 동일한 queryKey를 사용하여 서버에 있는 데이터를 조회하려고 하고있다. <code>res1</code>과 <code>res2</code>에 대한 요청이 있었으므로 서버에 2개의 request가 전달될 것이라 예상했지만 위 코드에서는 <strong>1개의 request만 전달된다</strong>. 왜 why? <code>res1</code>에서 request를 서버에 전달하게 되면 <code>res2</code>에서는 이미 조회한 queryKey에 대한 결과값이 존재하기에 서버에 다시 요청하지 않고 <code>res1</code>의 결과를 그대로 가져와서 쓴다.<span style="color: #d2d2d2">(굳이 비유하자면 메모이제이션 느낌쓰)</span>
또 queryFn이 다르게 정의되어 있어도 <code>res2</code>는 <code>res1</code>의 결과를 그대로 받기 때문에 <code>queryFn1</code>이 처리된 결과가 확인이 가능하다.</p>
<h2 id="queryfn">queryFn</h2>
<p><code>queryFn</code>은 query Function을 통하여 <strong>promise 처리가 이루어지는 함수</strong>라고 이해하면 된다. 다시 말해 aixos를 이용하여 서버에 API 요청을 하는 코드인 것이다.
<code>queryFn</code>의 문법은 다음과 같다.</p>
<pre><code class="language-jsx">const res = useQuery({
  queryKey: [&#39;users&#39;],
  queryFn: () =&gt; axios.get(&#39;http://localhost:8080/users&#39;)
});</code></pre>
<p>이제 간단한 예제를 통해 useQuery를 써보도록 하자!
useQuery는 앞서 말했듯이 서버에서 데이터를 get 할때 사용한다. useQuery의 unique Key는 고유한 값이 들어가야 하고, 호출 시 해당 키로 호출할 수 있다. Query Function은 api 사용 시 필요한 fetcher 함수가 들아가는 부분이고, Query option 은 onSuccess,onError 와 같은 옵션들이 들아가는 부분이다.</p>
<pre><code class="language-jsx">const Func = () =&gt; {
    const { isLoading, isError, data, error } = useQuery(&#39;hello&#39;, () =&gt; axios.get(&#39;&#39;), {
        refetchOnWindowFocus: false, // 사용자가 해당 창을 재방문 했을 때 재실행 여부
        retry: 0, // 실패시 재호출 횟수
        onSuccess: (data) =&gt; {
            console.log(data)
        },
        onError: (e) =&gt; {
            console.log(e.message)
        },
    })
}</code></pre>
<p><code>useQuery</code>에 첫 번째 파라미터로 고유 키가 들어가고 두번째 파리미터로는 api 호출 함수가 들어간다. <code>useQuery</code>는 비동기적으로 작동한다. 동기적으로 작동하게 하고싶으면 다음과 같이 해보자.</p>
<pre><code class="language-jsx">const { status, data, error } = useQuery(&#39;안녕!!!&#39;, () =&gt; axios.get(&#39;zuso&#39;), { enabled: !!check, })</code></pre>
<h1 id="usemutation">useMutation</h1>
<p><code>useMutation</code>은 React Query를 이용해 서버에 <strong>데이터 변경 작업을 요청할 때</strong> 사용한다. 데이터베이스와 비교하면 <code>insert</code>, <code>update</code>와 비슷한 친구다. <code>useMutation</code>을 알기 전 먼저 mutate와mutationFn를 알아봐야한다. 알아보자~</p>
<h2 id="mutationfn">mutationFn</h2>
<p>mutationFn은 <strong>promise 처리가 이루어지는 함수다.</strong> axios를 서서 서버에 API를 요청하는 부분이라고 알면 된다. mutationFunction의 문법은 다음과 같다.</p>
<pre><code class="language-jsx">const saveUser = useMutation({mutationFn: (user: Iuser) =&gt; axios.post(&#39;이거슨 주소다~&#39;,user)})</code></pre>
<h2 id="mutate">mutate</h2>
<p>mutate 는 useMutation을 이용해 작성한 내용들이 실행될 수 있는 트리거 역할을 한다. useMutation을 정의한 다음 이벤트가 발생하면 mutate를 사용하면된다. 간단한 예시로 알아보자.</p>
<pre><code class="language-jsx">const [person, setPerson] = React.useState&lt;Iperson&gt;({
  id: 0,
  name: &#39;&#39;,
  phone: &#39;&#39;,
  age: 0,
})

const saveuser = useMutation((user: Iuser) =&gt; axios.post(&#39;주주줏소&#39;, user){
    onSuccess: (res) =&gt; { console.log(res) }, // try
    onError: (error) =&gt; { console.log(res) }, // catch
    onSettled: () =&gt; { console.log(&#39;끝났소&#39;) } // finally
});

const onSaveUser = () =&gt; {
  savePerson.mutate(user);
}</code></pre>
<p>위 코드에서 <code>saveuser</code> 에서 useMutate를 정의하고 <code>onSaveUser()</code>에서 데이터를 저장한다.</p>
<h2 id="invalidatequeries">invalidateQueries</h2>
<p>invalidateQueriessms useQuery에서 사용되는 고유키의 유효성을 없애기 위한 목적으로 사용된다. 고유키의 유효성을 제거하는 이유는 서버로부터 다시 데이터를 조회하기 위해서다.</p>
<pre><code class="language-jsx">const queryClient = useQueryClient()
const users = useMutation((user) =&gt; axios.post(&#39;&#39;, user), {
  onSuccess: (res) =&gt; {
    console.log(res)
    queryClient.invalidateQueries(&#39;user&#39;)
},
  onError: (err) =&gt; { console.log(err) },
  onSettled: () =&gt; { console.log(&#39;ended&#39;) },
})</code></pre>
<p>위 코드에서 <code>queryClinet</code>에서 queiryClient 값을 가져온다. 다음으로 <code>users</code>에서 고유키의 유효성을 없앤다.</p>
<h2 id="setquerydata">setQueryData</h2>
<p>setQueryData는 기존에 queryKey에 매핑되어 있는 데이터를 새롭게 정의하여 데이터를 업데이트하는 방법이다. setQuery는 유효성을 제거하지 않고도 값을 업데이트 할 수 있다. 쓰는법 알아보자~</p>
<pre><code class="language-jsx">const users = useMutation((user) =&gt; axios.post(&#39;&#39;, user), {
  onSuccess: (res) =&gt; {
    console.log(res)
    queryClient.setQueryData(&#39;user&#39;, (data) =&gt; {
          const res = data
             res.data.push(user)
            return res
        })
    },
    onError: (err) =&gt; { console.log(err) },
    onSettled: () =&gt; { console.log(&#39;ended&#39;) },
})</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] useEffect,fetch,async & await]]></title>
            <link>https://velog.io/@dev_lheesung/React-useEffectfetchasync-await</link>
            <guid>https://velog.io/@dev_lheesung/React-useEffectfetchasync-await</guid>
            <pubDate>Thu, 11 May 2023 00:18:50 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/7475e796-12d4-4b90-9b5d-42d5f9792df8/image.png" alt=""></p>
<h1 id="🧪-useeffect">🧪 useEffect()</h1>
<p><code>useEffect()</code> 란 컴포넌트가 랜더링 되었을 때 특정 작업을 실행해 주는 hook 이다. 이는 컴포넌트가 <code>Mount</code>, <code>Unmount</code>, <code>Update</code> 시에 특정 작업을 처리한다.</p>
<h2 id="💡-useeffect-사용하기">💡 useEffect() 사용하기</h2>
<p><code>useEffect()</code> 의 기본형태는 <code>useEffect(함수, 검사하고자 하는 배열)</code> 식으로 사용된다. 거두절미하고 코드를 통해 알아보자.</p>
<pre><code class="language-jsx">useEffect(() =&gt; {
  console.log(&#39;컴포넌트가 랜더링 될 때마다 실행&#39;)
})</code></pre>
<p><code>useEffect()</code> 를 다음과 같이 사용하면 컴포넌트가 <strong>랜더링</strong> 될 때마다 <code>useEffect()</code> 안에 있는 함수가 실행된다.</p>
<pre><code class="language-jsx">useEffect(() =&gt; {
    console.log(&#39;컴포넌트가 마운트 될 때마다 실행&#39;)
}, [])</code></pre>
<p><code>useEffect()</code> 를 다음과 같이 사용하면 컴포넌트가 <strong>마운트</strong> 될 때마다 <code>useEffect()</code> 안에 있는 함수가 실행된다.</p>
<pre><code class="language-jsx">const [state,setState] = useState(0)

useEffect(() =&gt; {
    console.log(&#39;[] 안에 있는 state가 바뀔 때 마다 실행&#39;)
}, [state])</code></pre>
<p><code>useEffect()</code> 를 이와 같이 사용하면 <code>[]</code> 안에 <code>state</code> 의 값이 변할 때마다 <code>useEffect()</code> 안에 있는 함수가 실행된다.</p>
<h1 id="🧃-fetch">🧃 fetch</h1>
<p><code>fetch</code> 는 데이터를 받아올 때 API 를 받아 올 때 사용하는 JavaScript 내장함수다.</p>
<pre><code class="language-jsx">fetch(&#39;api 주소&#39;)
  .then(res =&gt; res.json())
  .then(res =&gt; {
    // data를 응답 받은 후의 실행할 로직
  });</code></pre>
<h1 id="🧑🏭-활용---api-호출하기">🧑‍🏭 활용 - API 호출하기</h1>
<pre><code class="language-jsx">import { useEffect, useState } from &quot;react&quot;;
export default function App() {
  const [data, setData] = useState({});
  const [count, setCount] = useState(0);
  console.log(&quot;component rendered&quot;);
  useEffect(() =&gt; {
    // 최초 랜더링 시 한 번 실행
    console.log(&quot;Effect ran&quot;);
    fetch(`https://swapi.dev/api/people/${count}`)
      .then((res) =&gt; res.json())
      .then((data) =&gt; setData(data));
  }, []);
  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;pre&gt;{JSON.stringify(data, null, 2)}&lt;/pre&gt;
      &lt;h2&gt;the count is {count}&lt;/h2&gt;
      &lt;button onClick={() =&gt; setCount((prev) =&gt; prev + 1)}&gt;plus&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>다음 코드에서는 위 <code>App()</code> 컴포넌트가 마운트 될 때마다 <code>fetch</code> 를 사용하여 스타워즈 캐릭터에 대한 정보를 받아오는 API 를 사용한 것 이다.</p>
<h1 id="🕸️-async-와-await">🕸️ Async 와 Await</h1>
<p>React에서는 비동기 처리를 할때 <code>Promise</code>를 사용했었지만 ES7으로 올라가면서 <code>async</code> 와 <code>await</code> 가 추가되고 비동기 코드를 동기 코드처럼 작성할 수 있게 되었다.</p>
<blockquote>
</blockquote>
<p>💡 동기와 비동기
동기 방식은 서버에서 요청을 보냈을 때 응답이 돌아와야 다음 동작을 수행할 수 있는 방식을 말한다.
비동기 방식은 동기 방식과 반대로 응답 상태와 상관없이 다음 동작을 수행할 수 있는 방식을 말한다.</p>
<h2 id="🫵-async-function">🫵 Async function</h2>
<pre><code class="language-jsx">async function() {
  return &quot;name&quot;
}
const users = async() =&gt; { return &quot;name&quot; }</code></pre>
<p>비동기 함수를 만드려면 그냥 함수 앞에 <code>async</code> 키워드를 붙혀주기만 하면 된다.</p>
<h2 id="🫵-await">🫵 Await</h2>
<p><code>await</code> 은 async/await 문법에서 사용되며, 함수 내부에서 비동기 처리를 하는 Promise 객체를 동기적으로 처리할 수 있게 해주는 키워드다.</p>
<pre><code class="language-jsx">async function fetchData() {
  const response = await fetch(&#39;api 주소&#39;);
  const data = await response.json();
  return data;
}</code></pre>
<p>위 코드에서 <code>fetch()</code> 함수는 Promise 객체를 반환합니다. 이 함수를 await 키워드와 함께 사용하면, <code>fetch()</code> 함수가 데이터를 반환할 때까지 코드가 블록되어 다음 코드가 실행되지 않습니다. 그리고, <code>response.json()</code> 함수도 Promise 객체를 반환하므로 <code>await</code> 키워드를 사용하여 데이터를 가져올 때까지 기다린다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] State로 input 상태 관리하기]]></title>
            <link>https://velog.io/@dev_lheesung/React-State%EB%A1%9C-input-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dev_lheesung/React-State%EB%A1%9C-input-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 19 Apr 2023 23:15:03 GMT</pubDate>
            <description><![CDATA[<hr>

<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/a130e514-68e5-41c6-b994-306ab3fc21d8/image.png" alt=""></p>
<hr>

<h1 id="👨🔧-usestate-로-input-상태-관리하기">👨‍🔧 useState 로 input 상태 관리하기</h1>
<p>React 에서 사용자의 입력을 받아들이는 input 요소를 다룰 때, input의 상태를 관리해주어야 한다. 이때 useState를 사용하여 input 상태를 관리하기 편하다. 코드로 알아보자.</p>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;;
function InputComponent() {
  const [inputValue, setInputValue] = useState(&#39;&#39;);
  const handleInputChange = (event) =&gt; {
    setInputValue(event.target.value);
  };
  return (
    &lt;div&gt;
      &lt;input type=&quot;text&quot; value={inputValue} onChange={handleInputChange} /&gt;
      &lt;p&gt;입력한 값: {inputValue}&lt;/p&gt;
    &lt;/div&gt;
  );
}
export default InputComponent;</code></pre>
<p>위 코드에서input 요소의 값이 변경될 때마다 <code>handleInputChange</code> 함수가 호출되며, <code>setInputValue</code> 함수를 사용하여 inputValue 상태 값을 변경한다. 이렇게 하면 <code>input</code> 요소의 값을 바꿀 때마다 <code>inputValue</code> 상태 값이 업데이트되며, 입력한 값을 출력할 수 있다.</p>
<h2 id="⚒️-활용">⚒️ 활용</h2>
<p>이번에는 위에서 정리한 내용을 활용하여 <code>Object</code> 형 <code>useState()</code> 를 선언하여 하나의 변수에 여러 값을 담아보자.</p>
<pre><code class="language-jsx">import { useState } from &quot;react&quot;;
export default function Form() {
  const [form, setFormData] = useState({
    firstName: &quot;&quot;,
    lastName: &quot;&quot;,
    email: &quot;&quot;
  });
  const handleInput = (e) =&gt; {
    setFormData((prev) =&gt; {
      return {
        ...prev, [e.target.name]: e.target.value
      };
    });
    console.log(form);
  };
  return (
    &lt;form&gt;
      &lt;input
        name=&quot;firstName&quot;
        type=&quot;text&quot;
        placeholder=&quot;first name&quot;
        onChange={handleInput}
      /&gt;
      &lt;input
        name=&quot;lastName&quot;
        type=&quot;text&quot;
        placeholder=&quot;last name&quot;
        onChange={handleInput}
      /&gt;
      &lt;input
        name=&quot;email&quot;
        type=&quot;email&quot;![](https://velog.velcdn.com/images/dev_lheesung/post/175d7006-c330-45e7-87b8-ef8ec67472d4/image.png)

        placeholder=&quot;email&quot;
        onChange={handleInput}
      /&gt;
    &lt;/form&gt;
  );
}</code></pre>
<p>위 코드는 첫번째 이름, 두번째 이름, 이메일을 입력받는 컴포넌트다. 각각의 input 에 식별할 수 있는 이름을 부여했다. <code>handleInput</code> 함수는 input의 변경 이벤트(onChange)를 처리하며, 이 함수는 <code>setFormData</code> 함수를 호출하여 form 상태를 업데이트한다. <code>setFormData</code> 함수는 이전 상태(prev)를 전달받아 새로운 상태를 반환한다. 이때, 전달받은 input의 name 속성을 사용하여 해당 input의 값을 새로운 상태에 반영한다. 이를 위해 전개 연산자(...)를 사용하여 기존 상태의 속성을 유지하면서 새로운 값을 덮어쓴다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OS]프로세스와 스레드]]></title>
            <link>https://velog.io/@dev_lheesung/OS%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C</link>
            <guid>https://velog.io/@dev_lheesung/OS%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C</guid>
            <pubDate>Tue, 11 Apr 2023 00:28:09 GMT</pubDate>
            <description><![CDATA[<h1 id="프로세스란">프로세스란?</h1>
<p>프로그램이 저장장치에 저장되어 있는 정적인 상태라면, 프로세스는 프로그램 실행을 위해 메모리에 올라온 동적인 상태라고 할 수 있다. 프로세스는 또한 가상메모리 공간, 코드, 데이터, 시스템 자원의 집합이라고도 할 수 있다. OS는 프로세스를 하나의 작업의 단위로 보고 자원들을 프로세스에 적적히 분배한다.</p>
<p>New</p>
<ul>
<li>프로세스가 생성되는 도중의 상태이다.</li>
</ul>
<p>Ready</p>
<ul>
<li>프로세스가 생성된 후, 프로세서에 할당되기를 기다리는 상태이다.</li>
</ul>
<p>Runing</p>
<ul>
<li>instruction이 실행되는 상태이다.
프로세서에 할당되어 있다.</li>
</ul>
<p>Wait</p>
<ul>
<li>프로세스가 특정 이벤트를 기다리는 상태이다.
프로세서에 할당되어 있지만 프로세서의 클락을 낮추거나 일시적으로 멈춤으로써 에너지 소비를 줄일 수 있는 상태이다.</li>
</ul>
<p>Terminated</p>
<ul>
<li>process 종료된 상태</li>
</ul>
<h1 id="프로세스의-상태">프로세스의 상태</h1>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/245123a8-3adf-4760-ab48-1c5309e627ee/image.png" alt=""></p>
<h2 id="생성상태">생성상태</h2>
<pre><code>- 프로그램이 메모리에 올라오고 운영체제로부터 PCB를 할당받은 상태
- 생성된 프로세스는 바로 실행되는 것이 아니라 준비 상태에서 자기 순서를 기다리며, PCB 도 같이 준비 상태로 옮겨짐</code></pre><h2 id="준비상태">준비상태</h2>
<pre><code>- 실행 대기 중인 모든 프로세스가 자기 순서를 기다리는 상태
- PCB는 준비 큐에서 기다리며 CPU 스케줄러에 의해 관리
- CPU 스케줄러는 준비 상태에서 큐를 몇 개 운영할지, 큐에 있는 어떤 프로세스의 프로세스 제어 블록을 실행 상태로 보낼지 결정
- CPU 스케줄러가 어떤 프로세스 제어 블록을 선택하는 작업은 dispatch(PID) 명령으로 처리
- CPU 스케줄러가 dispatch(PID)를 실행하면 해당 프로세스가 준비 상태에서 실행 상태로 바뀌어 작업이 이루어짐</code></pre><h2 id="실행상태">실행상태</h2>
<pre><code>- 프로세스가 CPU를 할당 받아 실행되는 상태
- 실행 상태에 있는 프로세스는 자신에게 주어진 시간, 즉 타임 슬라이스 동안만 작업할 수 있음
- 그 시간을 다 사용하면 timeout(PID)가 실행되어 실행 상태에서 준비 상태로 옮김
- 실행 상태 동안 작업이 완료되면 exit(PID)가 실행되어 프로세스가 정상 종료
- 실행 상태에 있는 프로세스가 입출력을 요청하면 CPU는 입출력 관리자에게 입출력을 요청하고 block(PID)를 실행
- block(PID)는 입출력이 완료될 때까지 작업을 진행할 수 없기 때문에 해당 프로세스를 대기 상태로 옮기고 CPU 스케줄러는 새로운 프로세스를 실행 상태로 가져옴</code></pre><h2 id="대기상태">대기상태</h2>
<pre><code>- 실행 상태에 있는 프로세스가 입출력을 요청하면 입출력이 완료될 때까지 기다리는 상태
- 대기 상태의 프로세스는 입출력장치별로 마련된 큐에서 기다리다가 완료되면 인터럽트가 발생하고, 대기 상태에 있는 여러 프로세스 중 해당 인터럽트로 깨어날 프로세스를 찾는데 이것이 wakeup(PID)
- wakeup(PID)로 해당 프로세스의 프로세스 제어 블록이 준비 상태로 이동</code></pre><h2 id="완료상태">완료상태</h2>
<pre><code>- 프로세스가 종료되는 상태
- 코드와 사용했던 데이터를 메모리에서 삭제하고 프로세스 제어 블록을 폐기
- 정상적인 종료는 간단히 exit( )로 처리
- 오류나 다른 프로세스에 의해 비정상적으로 종료되는 강제 종료를 만나면 디버깅하기 위해 종료 직전의 메모리 상태를 저장장치로 옮기는데 이를 코어 덤프(core dump)라고 함</code></pre><h2 id="휴식상태">휴식상태</h2>
<p>프로세스가 작업을 일시적으로 쉬고 있는 상태
유닉스에서 프로그램을 실행하는 동중에 <code>Ctrl + Z</code> 키를 누르면 볼 수 있음
종료 상태가 아니기 때문에 원할 때 다시 시작할 수 있는 상태</p>
<h2 id="보류상태">보류상태</h2>
<pre><code>- 프로세스가 메모리에서 잠시 쫓겨난 상태
- 프로세스는 다음과 같은 경우에 보류 상태가 됨
    + 메모리가 꽉 차서 일부 프로세스를 메모리 밖으로 내보낼 때
    + 프로그램에 오류가 있어서 실행을 미루어야 할 때
    + 바이러스와 같이 악의적인 공격을 하는 프로세스라고 판단될 때
    + 매우 긴 주기로 반복되는 프로세스라 메모리 밖으로 쫓아내도 큰 문제가 없을 때
    + 입출력을 기다리는 프로세스의 입출력이 계속 지연될 때</code></pre><h1 id="프로세스의-구성">프로세스의 구성</h1>
<p>프로세스는 프로그램 코드, 데이터, 스택, 힙 등의 구성 요소로 구성된다. 프로그램 코드는 실행할 명령어가 포함된 바이너리 파일이며, 데이터는 프로그램에서 사용하는 전역 변수 및 정적 변수와 같은 정보를 포함한다. 스택은 함수 호출에 필요한 지역 변수 및 매개 변수와 같은 정보를 저장하는 데 사용되며, 힙은 프로그램에서 동적으로 할당되는 메모리를 관리하는 데 사용된다.</p>
<h1 id="스레드">스레드</h1>
<p>스레드는 프로세스 내에서 실행되는 작은 실행 단위입니다. 즉, 프로세스는 스레드의 집합체로 이루어져 있다. 각 스레드는 별도의 스택을 가지며, 공통된 코드와 데이터를 사용한다.</p>
<h1 id="스레드와-프로세스의-차이점">스레드와 프로세스의 차이점</h1>
<p>스레드는 프로세스 내에서 실행되는 작은 실행 단위이고, 프로세스는 실행 중인 프로그램의 인스턴스다. 스레드는 프로세스 내에서 공유되는 메모리 영역을 사용하여 데이터를 주고 받으며, 각각의 스레드는 독립적인 실행 흐름을 가진다. 프로세스는 각각의 인스턴스가 독립적인 메모리 영역을 가지고 실행되며, 프로세스 간의 통신을 위해서는 IPC(Inter-Process Communication) 방법을 사용해야 한다.</p>
<h1 id="스레드의-장단점">스레드의 장단점</h1>
<h2 id="장점">장점</h2>
<ol>
<li>빠른 처리: 스레드는 프로세스 내에서 각각 병렬적으로 실행되므로, 멀티스레드 프로그램은 단일 스레드 프로그램보다 빠른 처리 속도를 보일 수 있다.</li>
<li>자원공유: 스레드는 프로세스 내에서 공유된 메모리 영역을 사용하므로, 데이터를 주고 받는 데 있어서 별도의 IPC 메커니즘이 필요하지 않는다.<h2 id="단점">단점</h2>
</li>
<li>동기화: 멀티스레드 프로그램에서는 스레드 간에 공유된 자원에 대한 동기화 문제를 해결해야 한다. 이를 제대로 처리하지 않으면 데이터 불일치 문제가 발생할 수 있다.</li>
<li>디버깅: 멀티스레드 프로그램에서는 스레드 간의 상호작용을 디버깅하기가 어렵다.</li>
</ol>
<h1 id="스레드의-종류">스레드의 종류</h1>
<h2 id="1-사용자-수준-스레드">1. 사용자 수준 스레드</h2>
<pre><code>- 사용자 수준에서 관리되며, 운영 체제에서는 스레드가 존재하지 않는 것으로 처리한다.
- 유연성이 높아서 스레드를 생성하고 스케줄링하는 데 소요되는 오버헤드가 적다.
- 그러나, 사용자 수준에서 관리되기 때문에, 스레드의 입출력 작업 등이 블로킹되면 전체 프로세스가 멈출 수 있다.
그러나, 사용자 수준에서 관리되기 때문에, 스레드의 입출력 작업 등이 블로킹되면 전체 프로세스가 멈출 수 있다.</code></pre><h2 id="2-커널-수준-스레드">2. 커널 수준 스레드</h2>
<pre><code>- 운영 체제에서 관리되며, 각 스레드는 개별적인 PCB(Process Control Block)를 가지고 있다.
- 입출력 작업 등의 시스템 호출 시, 블로킹되더라도 다른 스레드가 계속 실행될 수 있다.
- 그러나, 스레드 생성 및 스케줄링에 대한 오버헤드가 커서, 스레드 생성 수의 제한이 있을 수 있다.</code></pre><h2 id="3-하이브리드-스레드">3. 하이브리드 스레드</h2>
<pre><code>- 사용자 수준 스레드와 커널 수준 스레드의 장점을 모두 결합한 것이다.
- 스레드 생성 시, 사용자 수준 스레드와 커널 수준 스레드 중 선택하여 생성할 수 있다.
- 사용자 수준 스레드처럼 유연하게 생성 및 스케줄링할 수 있으면서도, 입출력 작업 등의 블로킹 시 다른 스레드가 실행될 수 있어서, 높은 처리 성능을 보인다.</code></pre><h1 id="멀티스레드">멀티스레드</h1>
<p>멀티스레드란 운영체제가 소프트웨어적으로 프로세스를 작은 단위의 스레드로 분할하여 운영하는 기법
<img src="https://velog.velcdn.com/images/dev_lheesung/post/de30cb5b-e06d-48ad-9b4c-43bf9660d5db/image.png" alt=""></p>
<h2 id="멀티태스킹과-멀티스레드의-차이">멀티태스킹과 멀티스레드의 차이</h2>
<pre><code>- fork( ) 시스템 호출로 여러 개의 프로세스를 만들면 필요 없는 정적 영역이 여러 개가 됨
- 멀티스레드는 코드, 파일 등의 자원을 공유함으로써 자원의 낭비를 막고 효율성 향상</code></pre><h2 id="멀티스레드의-장점">멀티스레드의 장점</h2>
<pre><code>- 응답성 향상
- 자원 공유
- 효율성 향상
- 다중 CPU 지원</code></pre><h2 id="멀티스레드의-단점">멀티스레드의 단점</h2>
<pre><code>- 모든 스레드가 자원을 함께 공휴하기에 한 스레드의 영향이 생기면 전체 스레드에 문제가 발생.
    + ex) 인터넷 익스플로러</code></pre><h1 id="커널스레드와-사용자스레드">커널스레드와 사용자스레드</h1>
<h2 id="커널스레드">커널스레드</h2>
<p>커널스레드란 커널이 직접 생성하고 관리하는 스레드다.</p>
<h2 id="사용자-스레드">사용자 스레드</h2>
<p>사용자스레드란 라이브러리에 의해 구현된 일반적인 스레드다.</p>
<h1 id="프로세스의-구조">프로세스의 구조</h1>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/8c80efb2-554e-40b4-93da-ce214ab08888/image.png" alt="">
코드영역: 프로그램의 본체가 존재하는 곳
데이터 영역: 프로그램이 사용하려고 정의한 변수와 데이터가 있는 곳
스택 영역, 힙 영역: 프로세스가 실행되는 동안 만들어지는 영역으로, 그 크기가 늘어났다 줄어들기도 하는 동적 할당 영역</p>
<h2 id="스택-영역">스택 영역</h2>
<pre><code>- 스레드가 작동하는 동안 추가되거나 삭제되는 동적 할당 영역
- 스레드가 진행됨에 따라 그의 따라 크기가 변하는 영역</code></pre><h3 id="예시">예시</h3>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/daef4efd-44c6-4c07-99fd-57841e2d1d7c/image.png" alt=""></p>
<h2 id="힙-영역">힙 영역</h2>
<pre><code>- 프로그램이 실행되는 동안 할당되는 변수 영역
- 포인터, malloc( ) 함수, calloc( ) 함수 등은 메모리를 효율적으로 사용하기 위해 만들어진 것으로 어쩌다 한 번 쓰는 큰 배열을 처음부터 선언하고 끝까지 놔두는 일이 없어야 함 
- 새로운 프로세스가 생성되면 malloc( ) 함수를 만들어 해당 큐에 사이 가능</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[React] Map 과 전개연산자(...)]]></title>
            <link>https://velog.io/@dev_lheesung/React-Map-%EA%B3%BC-%EC%A0%84%EA%B0%9C%EC%97%B0%EC%82%B0%EC%9E%90</link>
            <guid>https://velog.io/@dev_lheesung/React-Map-%EA%B3%BC-%EC%A0%84%EA%B0%9C%EC%97%B0%EC%82%B0%EC%9E%90</guid>
            <pubDate>Wed, 05 Apr 2023 15:09:18 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/060dbe9a-c302-445d-8990-91fe25033832/image.png" alt=""></p>
<h1 id="🔄-map-이란">🔄 map() 이란?</h1>
<p><code>map()</code> 은 반복되는 컴포넌트를 랜더링 하기 위해 Js 배열의 내장 함수인 map() 을 사용한다. 또 <code>map()</code> 은 매개변수로 전달된 함수를 사용하여 배열 내 각 요소를 원하는 규칙에 다라 변환한 후 새로운 배여을 생성한다. 그럼 간단한 예시코드를 보며 알아보자.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsx">let list = [1,2,3]
list.map(()=&gt;{
  console.log(&quot;Mikel Arteta&quot;);
});</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/11231fa2-16d0-4242-9a64-07d662f479cb/image.png" alt="">
이와 같이 아르테타가 3번 나온 걸 확인 할 수 있다. 이제 map 에 매개변수로 들어가는 함수의 매개변수를 이용해보자.</p>
<pre><code class="language-jsx">let list = [&#39;Saka&#39;, &#39;Ødegaard&#39;, &#39;Xhaka&#39;]
list.map((e)=&gt;{
  console.log(e);
});</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/a22dc4c0-fd96-40fb-9a35-109ebe63c7cd/image.png" alt="">
다음 코드에서 map 의 파라미타 함수에 매개변수 e 를 주고, 그 e를 이용하여 list 에 있는 요소들을 순서대로 쓸 수 있는 것을 알 수 있다.</p>
<h1 id="👷-map-활용하기">👷‍ map() 활용하기</h1>
<p>앞에서 정리한 <code>map()</code> 을 컴포넌트를 이용해 활용하면 여러개의 데이터를 하드코딩 하지 않고 반복문을 사용해 간결히 끝낼 수 있다.
Data.js</p>
<blockquote>
</blockquote>
<pre><code class="language-js">export default [
    {
        setup: &quot;I got my daughter a fridge for her birthday.&quot;,
        punchline: &quot;I can&#39;t wait to see her face light up when she opens it.&quot;
    },
    {
        setup: &quot;How did the hacker escape the police?&quot;,
        punchline: &quot;He just ransomware!&quot;
    },
    {
        setup: &quot;Why don&#39;t pirates travel on mountain roads?&quot;,
        punchline: &quot;Scurvy.&quot;
    },
    {
        setup: &quot;Why do bees stay in the hive in the winter?&quot;,
        punchline: &quot;Swarm.&quot;
    },
    {
        setup: &quot;What&#39;s the best thing about Switzerland?&quot;,
        punchline: &quot;I don&#39;t know, but the flag is a big plus!&quot;
    }
]</code></pre>
<p>다음 데이터를 이용하여<code>&lt;Joke /&gt;</code> 라는 컴포넌트를 만들어보자.
App.jsx</p>
<pre><code class="language-jsx">import Joke from &quot;./components/Joke&quot;;
import JokesData from &quot;./components/JokesData&quot;;
ㅤ
export default function App() {
  const JokeElements = JokesData.map((joke) =&gt; {
    return &lt;Joke setup={joke.setup} punchline={joke.punchline} /&gt;;
  });
  ㅤ
  return &lt;div className=&quot;App&quot;&gt;{JokeElements}&lt;/div&gt;;
}</code></pre>
<p>Joke.jsx</p>
<pre><code class="language-jsx">export default function Joke(props) {
  return (
    &lt;div&gt;
      {props.setup &amp;&amp; &lt;h3&gt;{props.setup}&lt;/h3&gt;}
      {props.punchline &amp;&amp; &lt;p&gt;{props.punchline}&lt;/p&gt;}
      &lt;hr&gt;&lt;/hr&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>다음 코드에서는 <code>.map()</code> 을 사용하여 <code>JokesData</code> 에 있는 <code>setup</code> 과 <code>punchline</code> 을 순서대로 넘겨주며 Joke 컴포넌트를 랜더링 하고 있는 것을 알 수 있다. Joke.js 에서 4, 5번 째 줄에 있는 코드의 의미는 받아온 <code>setup</code>과 <code>punchline</code> 의 값이 있을 때만 뒤에 있는 태그를 랜더링 하겠다는 뜻을 의미한다.</p>
<h1 id="🥅-전개연산자">🥅 전개연산자(...)</h1>
<p>전개연산자는 코드를 축약하기 위한 기법 중 하나이다. 주로 배열을 이용할 때 사용되며, 그 기능은 객체 또는 배열을 펼친다는 것이다. 배열을 펼친다는 것이 이해가 잘 되지 않을 수 있다. 코드를 통해 더 자세히 알아보자.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsx">let list = [];
let list1 = [1,2,3];
let list2 = [4,5,6];
ㅤ
list = [list1,list2];
console.log(list) // [[1,2,3], [4,5,6]]
ㅤ
list = [...list1,...list2];
console.log(list) // [1,2,3,4,5,6]</code></pre>
<p>다음 코드에서 알 수 보았듯이 전개연산자를 사용하지 않으면 배열 그 자체가 <code>list</code> 에 들어가지만 전개연산자를 사용하면 배열 내의 있는 값들이 펼쳐져 <code>list</code> 에 들어가는 것을 알 수 있다.</p>
<h1 id="👨🔧-전개연산자-활용하기">👨‍🔧 전개연산자 활용하기</h1>
<p>이번에는 앞서 이야기 했던 전개연산자를 <code>props</code> 와 함께 활용해 보자.</p>
<blockquote>
</blockquote>
<p>다음은 가상의 사이트를 구축한 것이다.
Data.js</p>
<pre><code class="language-js">export default [
  {
    id: 1,
    title: &quot;Life Lessons with Katie Zaferes&quot;,
    description:
      &#39;I will share with you&#39;,
    price: 136,
    stats: {
      rating: 5.0,
      reviewCount: 6
    },
    location: &quot;Online&quot;,
    openSpots: 0
  },
  {
    id: 2,
    title: &quot;Learn Wedding Photography&quot;,
    description:
      &quot;Interested in becoming a wedding photographer?&quot;,
    price: 125,
    stats: {
      rating: 5.0,
      reviewCount: 30
    },
    location: &quot;Online&quot;,
    openSpots: 27
  },
  {
    id: 3,
    title: &quot;Group Mountain Biking&quot;,
    description:
      &quot;Experience the beautiful&quot;,
    price: 50,
    stats: {
      rating: 4.8,
      reviewCount: 2
    },
    location: &quot;Norway&quot;,
    openSpots: 3
  }
];</code></pre>
<p>App.js</p>
<pre><code class="language-jsx">import Card from &quot;./components/Card&quot;;
import data from &quot;./components/Data&quot;;
ㅤ
const CardData = data.map((item) =&gt; {
  return &lt;Card {...item} /&gt;;
  // return &lt;Card item = {item} /&gt;;
  // return &lt;Card openslote = {item.openslote} ...&gt;
});
ㅤ
export default function App() {
  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;section className=&quot;cardsList&quot;&gt; {CardData} &lt;/section&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>App.js 에서는 <code>map()</code> 내의 <code>item</code> 이라는 매개변수를 이용하여 데이터를 Card 컴포넌트에 넘겨주는 코드다. 이 때 넘겨주는 방식이 총 3가지가 있다.
먼저 첫 번째 <code>return</code> 문은 전개연산자를 이용해 item 객체를 분해하여 Card 에 전달하기 때문에  Card에서 <code>props.rating</code> 과 같이 접근이 가능하다.
두 번째 <code>return</code> 문은 <code>item</code> 의 값으로 가지는 객체를 Card 에 전달하기 때문에 <code>props.item.rating</code> 과 같이 접근해야한다는 이전 방식 보다 비교적 까다로운 방법으로 값에 접근을 할 수 있다.
마지막 <code>return</code> 문의 의미는 그 매개변수를 직접 하나 씩 정해줘야 하고, 그렇지 못하면 접근을 할 수 없는 어려움이 있다.</p>
<p>오늘은 <strong><code>map()</code></strong> 과 <strong>전개연산자</strong> 에 대하여 알아보았다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Java Collection Framework]]></title>
            <link>https://velog.io/@dev_lheesung/Java-Java-Collection-Framework</link>
            <guid>https://velog.io/@dev_lheesung/Java-Java-Collection-Framework</guid>
            <pubDate>Mon, 27 Mar 2023 03:03:12 GMT</pubDate>
            <description><![CDATA[<h1 id="☕️-java-collection-framework-란">☕️ Java Collection Framework 란?</h1>
<p><code>Collection Framework</code> 는 자바에서 빠질 수 없는 필수적인 요소다. <code>Collection</code> 은 다수의 요소를 하나의 그룹으로 묶어서 효율적으로 저장 및 관리할 수 있는 기능을 제공하고 있는 일종의 Container 라고 생각하면 된다. <strong>즉, 데이터를 저장하는 자료구조와 데이터를 처리하는 알고리즘을 클래스로 구현해 놓은 것이다.</strong>(이러한 컬렉션 프레임워크는 <code>interface</code> 를 사용하여 구현됨.)</p>
<h1 id="👍-collection-framework-의-이점">👍 Collection Framework 의 이점</h1>
<p><code>Collection Framework</code> 의 장점은 크게 4가지라고 할 수 있다.</p>
<ul>
<li>List, Queue 등의 자료구조(인터페이스)를 제공하고, 이를 구현하는 클래스를 제공하여 <strong>일괄된 API 를 사용</strong></li>
<li><strong>가변적인 저장공간</strong></li>
<li>자료구조, 알고리즘 코드를 직접 작성할 필요 없이, <strong>이미 구현되있는 것을 가져다 쓰면 됌</strong>.</li>
<li>제공된 API 의 코드는 <strong>검증</strong>되었고, <strong>고도로 최적화</strong> 되어있음.</li>
</ul>
<h1 id="🧱-collection-framework-의-주요-인터페이스">🧱 Collection Framework 의 주요 인터페이스</h1>
<p>컬렉션 프레임워크에서는 데이터를 저장하는 자료구조에 따라 크게 3가지 인터페이스를 정의하고 있다.</p>
<h3 id="1-list">1. List</h3>
<p>순서가 있는 데이터의 집합으로, 데이터의 중복을 허용하는 자료구조.
    - Vector
    - ArrayList
    - LinkedList
    - Stack
    - Queue</p>
<h3 id="2-set">2. Set</h3>
<p>순서가 없는 데이터의 집합으로, 데이터의 중복을 허용하지 않는 자료구조.
    - HashSet
    - TreeSet</p>
<h3 id="3-map">3. Map</h3>
<p>키와 값의 한 쌍으로 이루어지는 데이터의 집합으로, 순서가 없음. 중복되는 키는 허용하지 않지만 중복되는 값은 허용됨.
    - HashMap
    - TreeMap
    - Hashtable
    - Properties</p>
<hr>
ArrayList 선언 시 메모리 구조는 다음과 같다.

<pre><code class="language-java">List&lt;int&gt; arr = new ArrayList&lt;int&gt;();
arr.add(0);
arr.add(1);
arr.add(2);
arr.add(3);</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/8cfc9d7b-d583-499e-8940-af4c6e4080eb/image.png" alt=""></p>
<h1 id="🖼️-collection-framework-계층">🖼️ Collection Framework 계층</h1>
<p>Collection Framework 의 계층은 다음과 같다.
<img src="https://velog.velcdn.com/images/dev_lheesung/post/26543004-4320-4dd0-a608-0e0dbd76f08b/image.png" alt=""></p>
<h1 id="📝-collection-framework-예시">📝 Collection Framework 예시</h1>
<h2 id="arraylist">ArrayList</h2>
<p><code>ArrayList</code> 를 사용하여 Collection Framework 사용의 예시를 들어보자.</p>
<pre><code class="language-java">public class Ex01 {

    public static void main(String[] args) {
        ArrayList&lt;String&gt; list = new ArrayList&lt;String&gt;();

        list.add(&quot;Eddie&quot;);
        list.add(&quot;Nketiah&quot;);
        list.add(&quot;Arteta&quot;);
        System.out.println(&quot;list의 크기 : &quot; + list.size());
        System.out.println(&quot;세번째 값 : &quot; + list.get(3));

        for(int i=0; i&lt;list.size(); i++) {
            System.out.println(&quot;list[&quot;+i+&quot;]번째 : &quot;+list.get(i));
        }

        list.clear(); // 모든 요소 제거
        System.out.println(&quot;list의 크기 &gt;&gt;&gt; &quot; + list.size()); // 0

        list.add(&quot;Eddie&quot;);
        list.add(&quot;Nketiah&quot;);
        list.add(&quot;Arteta&quot;);

        list.remove(2);

        for(int i=0; i&lt;list.size(); i++) {
            System.out.println(&quot;list[&quot;+i+&quot;]번째 : &quot;+list.get(i));
        }
        list.add(2, &quot;사카&quot;);

        for(int i=0; i&lt;list.size(); i++) {
            System.out.println(&quot;list[&quot;+i+&quot;]번째 : &quot;+list.get(i));
        }
    }
}</code></pre>
<h2 id="hashmap">HashMap</h2>
<p>이제 <code>HashMap</code> 에 대해 알아보자
<code>HashMap</code> 의 특징은 다음과 같다.
    - 같은 키의 값을 삽입하려고하면 해당 키의 값이 변경이 됨.
    - 키는 고유한 속성이지만 값은 고유한 속성이 아님
    - 키는 중복이 되지 않지만 값은 중복이 될 수 있음.
    - 다른 특징으로는 HashTable과 유사하지만 동기화가 되지 않고 Null값도 저장이 가능함.
그렇다면 <code>HashMap</code> 을 사용하는 예시를 알아보자.</p>
<pre><code class="language-java">// 선언
HashMap&lt;Integer,String&gt; map = new HashMap&lt;Integer,String&gt;(){{//초기값 지정
    put(1,&quot;Eddie&quot;);
    put(2,&quot;Arteta&quot;);
    put(3,&quot;Saka&quot;);
}};
System.out.println(map); // 전체 출력 : {1=Eddie, 2=Arteta, 3=Saka}
System.out.println(map.get(1)); // key값 1의 value얻기 : Eddie

//entrySet() 활용
for (Entry&lt;Integer, String&gt; entry : map.entrySet()) {
    System.out.println(&quot;[Key]:&quot; + entry.getKey() + &quot; [Value]:&quot; + entry.getValue());
}
//[Key]:1 [Value]:Eddie
//[Key]:2 [Value]:Arteta
//[Key]:3 [Value]:Saka

//저장된 key값 확인
for(Integer i : map.keySet()){
    System.out.println(&quot;[Key]:&quot; + i + &quot; [Value]:&quot; + map.get(i));
}
//[Key]:1 [Value]:사과
//[Key]:2 [Value]:바나나
//[Key]:3 [Value]:포도</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] React 기본]]></title>
            <link>https://velog.io/@dev_lheesung/React-React-%EA%B8%B0%EB%B3%B8</link>
            <guid>https://velog.io/@dev_lheesung/React-React-%EA%B8%B0%EB%B3%B8</guid>
            <pubDate>Wed, 22 Mar 2023 15:41:03 GMT</pubDate>
            <description><![CDATA[<h1 id="🌐-jsx란">🌐 JSX란?</h1>
<p><strong>JSX</strong>(JavaScript XML)는 JavaSript 코드에 XML을 추가한 확장한 문법이다. JSX는 React 프로젝트를 개발할 때 사용되므로 우리가 사용했던 공식적인 JavaScipt 문법은 아니다. JSX를 쉽게 설명하자면 <strong>HTML 태그를 변수로 <em>할당, 호출, 반환</em> 할 수 있는 확장문법이라고 보면 된다.</strong></p>
<h1 id="🏋️babel이란">🏋️Babel이란?</h1>
<p><strong>Babel</strong> 의 원래이름 <code>6to5</code> 를 들으면 이 녀석이 뭐하는 놈인지 짐작이 갈 것이다. &#39;6에서5&#39; 라는 이름을 가지고 있던 <code>Bable</code>은 <strong>ES6 를 ES5로 바꿔준다는 것을 뜻한다</strong>. 2015년에 ES6가 출시되고 Chrome, Firefox 등의 브라우저는 2017년이 돼서야 ES6 지원을 시작했다. 그러다 보니 ES6를 ES5로 바꿔주는 도구가 있어야만 아직 최신 방식을 채택하지 않은 브라우저에서도 사용이 가능했다.</p>
<blockquote>
</blockquote>
<h4 id="👨👨👦👦react와-jsx와-babel의-관계">👨‍👨‍👦‍👦React와 JSX와 Babel의 관계</h4>
<p>React는 JSX와 Babel이 없었다면 출시조차 못했을 수 있다. JSX와 Babel이 없었다면 React 를 쓰는것은 매우 어렵거나 아니면 세상에 못나왔을 수 도 있다.</p>
<h1 id="🚩-react-란">🚩 React 란?</h1>
<p>React는 사용자 인터페이스를 구축하기 위한 선언적이고 효율적이며 유연한 JavaScript 라이브러리다. <code>컴포넌트</code> 라고 불리는 작고 고립된 코드의 파편을 이용하여 복잡한 UI를 구성하도록 돕는다. 그럼 컴포넌트의 예시를 코드를 통해 알아보자.</p>
<iframe src="https://codesandbox.io/s/competent-curie-iqmvdn?file=/src/App.js"></iframe>

<p>다음 코드에서 보았듯이 컴포넌트의 선언은 함수형식으로 하고, 첫 글자는 꼭 대문자로 해야한다. 왜냐하면 react 에서 코드들이 랜더링이 될 때 첫 글자가 소문자로 시작하는 태그는 react 가 html 코드로 인식을 해버리기 때문이다.</p>
<h1 id="🖥️-indexjs">🖥️ index.js</h1>
<p>index.js 는 react 에서 최초의 <code>react</code>, <code>react-dom</code> 이라는 모듈을 로드하고 있고, 이게 우리가 브라우저의 우리의 코드를 보여주게 하는 본체라고 할 수 있다. 그럼 index.js 를 코드를 통해 알아보자</p>
<pre><code class="language-js">import { StrictMode } from &quot;react&quot;;
import { createRoot } from &quot;react-dom/client&quot;;
import App from &quot;./App&quot;;

const rootElement = document.getElementById(&quot;root&quot;);
const root = createRoot(rootElement);

root.render(
  &lt;StrictMode&gt;
    &lt;App /&gt;
  &lt;/StrictMode&gt;
);</code></pre>
<p>다음 코드에서 <code>root.render()</code> 에 인수에는 <code>App.js</code> 로 정의하고 있는 <code>App</code> 컴포넌트를 지정하고 있다. <code>root.render()</code>를 통해 <code>root</code> 안에 있는 컴포넌트가 랜더링 되어 출력되는 것을 볼 수 있을 것이다.</p>
<h1 id="📮-props">📮 Props</h1>
<p>이제 부모 컴포넌트에서 자식 컴포넌트에게 값을 전달하는 방법을 알아보자. 우리가 React 에서 자식 컴포넌트에게 값을 넘겨줄 때는 <code>Properties</code>, 줄여서 <span style="color: #5D59EA"><code>Props</code></span> 라는 문법을 사용한다. <code>Props</code> 가 다른 컴포넌트에 값을 넘겨줄 때, 부모 컴포넌트에서 자식 컴포넌트로 값을 주는 것은 가능하지만, 자식 컴포넌트에서 부모 컴포넌트에는 값을 넘겨줄 수 없는 <strong>단방향 데이터의 흐름</strong>을 가지고 있다. 또한 자식 컴포넌트는 넘겨받은 Props 값을 수정할 수 없다. 이제 코드를 통해 <code>Props</code> 를 사용하는 방법을 알아보자.</p>
<blockquote>
</blockquote>
<p>App.js</p>
<pre><code class="language-jsx">import Test from &#39;./Test&#39;;
function App() {
  const date = new Date;
  let pandan;
  if (date.getTime() &lt;= 12) pandan = &quot;Good Morning&quot;
  else if (date.getTime() &lt;= 17) pandan = &quot;Good Evening&quot;
  else pandan = &quot;Good Afternoon&quot;
  return (
    &lt;&gt;
      &lt;Test 
        name={&quot;Eddie Nketiah&quot;}
        team={&quot;Arsenal&quot;}
        position = {&quot;FW&quot;}
      /&gt;
      &lt;Test 
        name={&quot;Mikel Arteta&quot;}
        team={&quot;Arsenal&quot;}
        position = {&quot;Coach&quot;}
      /&gt;
      &lt;Test 
        name={&quot;Arsène Wenger&quot;}
        team={&quot;FIFA&quot;}
        position = {&quot;Director&quot;}
      /&gt;
    &lt;/&gt;
  );
}</code></pre>
<p><code>props</code>    를 사용하는 방법은 부모 컴포넌트에서 자식컴포넌트가 사용되는 곳에 자신이 넘길 <code>props</code> 의 이름을 작명해주고 그 안에 넘겨주고 싶은 값을 넣으면 된다. 그럼 자식 컴포넌트에서 부모에게 넘겨받은 <code>props</code> 를 사용하는 방법을 알아보자.
test.js</p>
<pre><code class="language-jsx">export default function Test(props){
    return(
        &lt;div&gt;
            &lt;h1&gt;Hello, I&#39;m {props.name}&lt;/h1&gt;
            &lt;p&gt;I belong to {props.team}&lt;/p&gt;
            &lt;p&gt;My position is {props.position}&lt;/p&gt;
        &lt;/div&gt;
    )
}</code></pre>
<p>자식 컴포넌트에서 물려받은 <code>props</code> 를 사용하는 방법은 자식 컴포넌트 함수의 매개변수로 props를 적어주고 <span style="color: grey"><del>(이름은 마음대로 적어도 가능하지만 대게 props 로 사용)</del></span> 내가 받아온 값을 사용할 자리에 <code>props.{넘겨준 props 이름}</code> 을 사용하면 된다. 따라서 다음코드의 실행결과는 다음과 같다. 
<img src="https://velog.velcdn.com/images/dev_lheesung/post/dfeb0b48-0eae-4388-b5d7-cbff77119d5b/image.png" alt=""></p>
<h1 id="🔎-조건부-랜더링">🔎 조건부 랜더링</h1>
<p>조건부 랜더링은 말 그대로 어떠한 조건에 따라 선택적으로 요소를 랜더링 한다는 뜻이다. 좀 전에 알아보았던 <code>props</code>문법을 사용한 코드를 통해 바로 알아보자.</p>
<blockquote>
</blockquote>
<p>App.js</p>
<pre><code class="language-jsx">import Test from &#39;./Test&#39;;
function App() {
  return (
    &lt;&gt;
      &lt;Test 
        name={&quot;Eddie Nketiah&quot;}
        team={&quot;Arsenal&quot;}
        position = {&quot;FW&quot;}
        champion = {true}
      /&gt;
      &lt;Test 
        name={&quot;Mikel Arteta&quot;}
        team={&quot;Arsenal&quot;}
        position = {&quot;Coach&quot;}
      /&gt;
      &lt;Test 
        name={&quot;Arsène Wenger&quot;}
        team={&quot;FIFA&quot;}
        position = {&quot;Director&quot;}
      /&gt;
    &lt;/&gt;
  );</code></pre>
<p>Test.js</p>
<pre><code class="language-jsx">export default function Test(props){
    return(
        &lt;div&gt;
            {props.champion &amp;&amp; &lt;h1&gt;It&#39;s True&lt;/h1&gt;}
        &lt;/div&gt;
    )
}</code></pre>
<p><code>Test.js</code>에서 <code>{props.champion &amp;&amp; &lt;h1&gt;It&#39;s True&lt;/h1&gt;}</code> 문을 해석하자면 <code>props.champion</code> 이 <code>True</code> 라면 뒤에 JSX 코드를 랜더링 시키고 <code>False</code>라면 랜더링을 하지 않겠다는 간단한 조건문이다.</p>
<h1 id="🥄-react-hook-맛보기">🥄 react hook 맛보기</h1>
<h2 id="🗃️-usestate">🗃️ useState</h2>
<p><code>useState</code> 를 배우기 전 먼저 state 라는 개념에 대해 알아보자. state는 간단히 말하면 변수와 비슷하다. state는 일반 변수와 다르게 값이 변하게 되면 렌더링이 발생한다. 그럼 코드를 통해 useState()를 사용하는 법을 알아보자.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsx">import { useState } from &#39;react&#39;;
function Example() {
  const [count, setCount] = useState(0);
  return (
    &lt;div&gt;
      &lt;p&gt;니가 {count} 번 눌렀음.&lt;/p&gt;
      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;클릭하셈&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>다음 코드에서 먼저 <code>useState</code> 를 임포트했다. 그리고 <code>const [count, setCount] = useState(0);</code> 줄이 useState 를 선언한 곳이다. <code>useState()</code> 의 <code>()</code>에 쓰고싶은 자료형의 형태를 적으면 된다.(object, string, bool 등...)</p>
<h2 id="🤷🏼♂️usestate-왜씀">🤷🏼‍♂️useState 왜씀?</h2>
<p><code>useState</code> 는 유동적이지 않은 데이터를 저장할 때 주로 사용된다. <code>useState</code> 를 사용하는 이유를 몇가지로 정리해보자.</p>
<blockquote>
</blockquote>
<ol>
<li>간단하고 직관적인 사용법을 제공해 상태 관리를 쉽게 할 수 있다.</li>
<li>객체나 배열과 같은 복잡한 상태를 관리할 수 있다.</li>
<li>자동 재렌더링이 되어, state 데이터들이 변경되면 HTML에도 자동적으로 변경사항이 반영된다.</li>
<li>새로고침 없이도 DOM의 요소를 변경시켜 보여 주는 웹/앱 사이트를 만들 수 있다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] SQL 알아보기]]></title>
            <link>https://velog.io/@dev_lheesung/DB-SQL-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@dev_lheesung/DB-SQL-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Sat, 18 Mar 2023 12:05:29 GMT</pubDate>
            <description><![CDATA[<h1 id="🗄️-sqlstructured-query-language">🗄️ SQL(Structured Query Language)</h1>
<p>SQL은 관계형 데이터베이스에서 데이터 정의, 데이터 조작, 데이터 제어 등을 위하여 사용하는 언어다. SQL의 종류로는 <strong>DDL, DML, DCL, TCL</strong> 이 있다.</p>
<h1 id="1-ddldata-definition-language">1. DDL(Data Definition Language)</h1>
<p>DDL 은 테이블이나 인데스 같은 데이터의 구조를 정의하거나 수정, 삭제하는데 사용되는 명령어다. 이제 DDL 에 대하여 더 알아보자.</p>
<blockquote>
</blockquote>
<h3 id="ddl-의-대상">DDL 의 대상</h3>
<blockquote>
</blockquote>
<table>
<thead>
<tr>
<th align="center">DDL 대상</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">Table</td>
<td align="center">데이터를 저장하는 기본 저장단위, 행과 열로 구성</td>
</tr>
<tr>
<td align="center">View</td>
<td align="center">하나 이상의 물리 테이블에서 유도되는 가상의 테이블</td>
</tr>
<tr>
<td align="center">Index</td>
<td align="center">검색 속도를 향상시키기 위한 데이터베이스 오브젝트</td>
</tr>
<tr>
<td align="center">Schema</td>
<td align="center">데이터베잉스의 구조와 제약조건에 관한 전반적인 명세를 기술한 메타데이터의 집합</td>
</tr>
<tr>
<td align="center">### 데이터 유형</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">컬럼을 정의할 때 선언한 데이터 유형은 해당 컴럼이 받아드리는 자료형을 규정해야한다. 또 선언한 데이터의 자료형이 아닌 데이터가 입력되거나 지정한 데이터 크기를 초과하면 에러가 발생한다.</td>
<td align="center"></td>
</tr>
</tbody></table>
<blockquote>
</blockquote>
<table>
<thead>
<tr>
<th align="center">자료형</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">CHARACTER(size)</td>
<td align="center">길이가 size인 고정 길이 문자열 데이터</td>
</tr>
<tr>
<td align="center">VARCHAR(size)</td>
<td align="center">가변 길이 문자 데이터(size 는 최대 크기, 가변 길이로 조정되므로 할당된 변수값 만큼의 byte 만 적용됨)</td>
</tr>
<tr>
<td align="center">NUMERIC</td>
<td align="center">정수 데이터</td>
</tr>
<tr>
<td align="center">DATETIME</td>
<td align="center">날짜와 시각 데이터</td>
</tr>
<tr>
<td align="center">### 명령어</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">DDL 명령어는 생성, 수정, 삭제로 나뉜다.</td>
<td align="center"></td>
</tr>
</tbody></table>
<blockquote>
</blockquote>
<table>
<thead>
<tr>
<th>유형</th>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>생성</td>
<td>CREATE</td>
<td>새로운 데이터베이스 개체 생성</td>
</tr>
<tr>
<td>수정</td>
<td>ALTER</td>
<td>이미 존재하는 데이터베이스 개체에 대한 변경</td>
</tr>
<tr>
<td>삭제</td>
<td>DROP<br>TRUNCATE</td>
<td>이미 존재하는 데이터베이스 개체 삭제<br>데이터베이스 개체에서 내용 삭제</td>
</tr>
</tbody></table>
<h2 id="1-create-사용하기">1. CREATE 사용하기</h2>
<p>CREATE TABLE 을 사용하여 테이블을 생성할 때는 다음을 주의해야한다. 테이블 이름에는 단수형 객체를 의미하는 적절한 이름을 사용하고, 다른 테이블과 중복된 이름을 가져서는 안된다. 또한 한 테이블안에 같은 이름의 컬럼이 존재해서도 안되며 컬럼 뒤에는 반드시 자료형을 지정해주어야한다. 또 모든 컬럼은 반드시 문자로 시작해야하며 예약어는 사용이 불가능하다. 이제 예제를 통해 CREATE TABLE 을 사용하는 법을 알아보자.</p>
<blockquote>
</blockquote>
<h3 id="create-table-문법">CREATE TABLE 문법</h3>
<table>
<thead>
<tr>
<th align="center">명령어</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">NOT NULL</td>
<td align="center">NULL 값 불허</td>
</tr>
<tr>
<td align="center">PRIMARY KEY</td>
<td align="center">기본키 선언</td>
</tr>
<tr>
<td align="center">UNIQUE</td>
<td align="center">고유키 선언</td>
</tr>
<tr>
<td align="center">FOREIGN KEY</td>
<td align="center">외래키 선언</td>
</tr>
<tr>
<td align="center">CONSTRAINT</td>
<td align="center">제약조건 설명</td>
</tr>
<tr>
<td align="center">### 테이블 생성 조건</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">대상</td>
<td align="center">조건</td>
</tr>
<tr>
<td align="center">:--:</td>
<td align="center">:--</td>
</tr>
<tr>
<td align="center">테이블 명</td>
<td align="center">STUDNET</td>
</tr>
<tr>
<td align="center">컬럼</td>
<td align="center">ST_ID 고정문자 8자리<br>ST_NAME 가변문자 20자리<br>CLASS_ID 고정문자 4자리<br>BIRTH 날짜</td>
</tr>
<tr>
<td align="center">제약 조건</td>
<td align="center">Primary Key: ST_ID(제약조건 명은 ST_PK)<br>NOT NULL: ST_NAME, ST_ID</td>
</tr>
<tr>
<td align="center">```sql</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">CREATE TABEL STUDENT(</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">ST_ID CHAR(8) NOT NULL,</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">ST_NAME VARCHAR2(20) NOT NULL,</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">CLASS_ID CHAR(4),</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">BIRTH DATE,</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">CONSTRAINT ST_PK PRIMARY KEY(ST ID)</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">);</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">```</td>
<td align="center"></td>
</tr>
</tbody></table>
<h2 id="2-alter-사용하기">2. ALTER 사용하기</h2>
<p>ALTER 명령어는 컬럼이나 제약조건을 추가/삭제 하기 위하여 사용한다.</p>
<blockquote>
</blockquote>
<h3 id="alter-table-문법">ALTER TABLE 문법</h3>
<table>
<thead>
<tr>
<th align="center">구분</th>
<th align="center">문법</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">ADD</td>
<td align="center">ALETER TABLE 테이블명 ADD 컬럼명 데이터유형 NOT NULL</td>
<td align="center">테이블에 컬럼 추가</td>
</tr>
<tr>
<td align="center">MODIFY</td>
<td align="center">ALTER TABLE 테이블명 MODIFY 컬럼명 데이터 유형 NOT NULL</td>
<td align="center">컬럼의 제약조건 변경</td>
</tr>
<tr>
<td align="center">DROP COLUMN</td>
<td align="center">ALTER TABLE 테이블명 DROP COLUNM 컬럼명</td>
<td align="center">컬럼을 삭제</td>
</tr>
<tr>
<td align="center">RENAME COLUNM</td>
<td align="center">ALTER TABLE 테이블명 RENAME COLUNM 컬럼명 TO 바꿀 컬럼명</td>
<td align="center">컬럼의 이름 변경</td>
</tr>
<tr>
<td align="center">DROP CONSTRAINT</td>
<td align="center">ALTER TABLE 테이블명 DROP CONSTRAINT 제약조건</td>
<td align="center">기존 제약조건 삭제</td>
</tr>
<tr>
<td align="center">ADD CONSTRAINT</td>
<td align="center">ALTER TABLE 테이블명 ADD CONSTRAINT 제약조건명 제약조건</td>
<td align="center">테이블에 제약조건 추가</td>
</tr>
</tbody></table>
<h2 id="3-drop-table-사용하기">3. DROP TABLE 사용하기</h2>
<p>DROP 은 불필요한 테이블을 삭제하기 위한 명령어다.</p>
<blockquote>
</blockquote>
<h3 id="drop-table-문법">DROP TABLE 문법</h3>
<table>
<thead>
<tr>
<th align="center">문법</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">DROT TABLE 테이블 명 [CASCADE CONSTRAINT]</td>
<td align="center">테이블의 모든 데이터와 구조까지 완전히 삭제<br><em>CASECADE CONSTRAINT 는 PRIMARY KEY, UNIQUE KEY 를 참조하는 다른 테이블의 참조 무결성 제약도 함께 삭제시키는 명령이다.</em></td>
</tr>
</tbody></table>
<h2 id="4-truncate-table-사용하기">4. TRUNCATE TABLE 사용하기</h2>
<p>TRUNCATE TABLE 은 테이블에 저장된 모든 행을 제거하고 저장공간을 재사용 할 수 있도록 해제하는 명령어다.</p>
<blockquote>
</blockquote>
<h3 id="truncate-문법">TRUNCATE 문법</h3>
<table>
<thead>
<tr>
<th align="center">문법</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">TURNCATE TABLE 테이블명</td>
<td align="center">테이블의 모든 행을 삭제<br>최초로 생성된 초기의 상태로 만듦</td>
</tr>
</tbody></table>
<h2 id="5-drop-truncate-delete-비교해보기">5. DROP, TRUNCATE, DELETE 비교해보기</h2>
<blockquote>
</blockquote>
<table>
<thead>
<tr>
<th>구분</th>
<th>DROP</th>
<th>TRUNCATE</th>
<th>DELETE</th>
</tr>
</thead>
<tbody><tr>
<td>명령어 종류</td>
<td>DDL</td>
<td>DDL</td>
<td>DML</td>
</tr>
<tr>
<td>Rollback</td>
<td>불가능</td>
<td>불가능</td>
<td>COMMIT 안했으면 가능</td>
</tr>
<tr>
<td>Commit</td>
<td>Auto</td>
<td>Auto</td>
<td>사용자가 직접 COMMIT</td>
</tr>
<tr>
<td>Release</td>
<td>테이블이 사용한 모든 저장소 모두</td>
<td>테이블이 사용했던 저장소 중 최초 테이블 생성 시 할당된 저장소만 남기고 Realese</td>
<td>사용했던 저장소는 Reelase 되지 않음</td>
</tr>
<tr>
<td>삭제 상태</td>
<td>테이블 정의까지 완전삭제</td>
<td>최초 생성된 초기상태로 만듦</td>
<td>테이터만 삭제</td>
</tr>
<tr>
<td>삭제 로그</td>
<td>없음</td>
<td>없음</td>
<td>있음</td>
</tr>
</tbody></table>
<h1 id="2-dml-data-mainpulation-language">2. DML (DATA Mainpulation Language)</h1>
<p>DML 은 데이터베이스에 저장된 데이터를 조회, 입력, 수정, 삭제하는 데 사용하는 질의어다. DML은 비절차적 데이터 조작어이고, 사용자가 무슨 데이터를 원하는지 명세한다.</p>
<blockquote>
</blockquote>
<h3 id="dml-의-유형">DML 의 유형</h3>
<table>
<thead>
<tr>
<th align="center">유형</th>
<th align="center">동작</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">SELECT</td>
<td align="center">데이터 조회</td>
<td align="center">테이블을 구성하는 레코드 중 전체 또는 조건을 만족하는 레코드를 조회</td>
</tr>
<tr>
<td align="center">INSERT</td>
<td align="center">데이터 입력</td>
<td align="center">테이블에 새로운 *레코드를 입력</td>
</tr>
<tr>
<td align="center">UPDATE</td>
<td align="center">데이터 수정</td>
<td align="center">테이블에 특정 레코드를 변경</td>
</tr>
<tr>
<td align="center">DELETE</td>
<td align="center">데이터 삭제</td>
<td align="center">테이블에 특정 레코드를 삭제</td>
</tr>
<tr>
<td align="center"><p style="color: grey">레코드: 관계형 DB에서는 행을 레코드 또는 튜플로 정의함</p></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<h2 id="1-select-사용하기">1. SELECT 사용하기</h2>
<p>SELECT 명령어는 데이터의 내용을 조회할 때 사용하는 명령어다.</p>
<blockquote>
</blockquote>
<h3 id="select-문법">SELECT 문법</h3>
<table>
<thead>
<tr>
<th align="center">문법</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">SELECT [ALL | DISTINCT]<br>컬럼명 AS 별명<br>FROM 테이블명</td>
<td align="center">SELECT절에서 명시한 컬럼을 FROM 절의 테이블에서 조회<br>ALL(중복된 데이터 O), DISTINCT(중복된 데이터 X) 는 생략 가능<br>AS 를 사용하면 별명 지을수 있음<br>AS도 생략 가능</td>
</tr>
</tbody></table>
<h2 id="2-insert-사용하기">2. INSERT 사용하기</h2>
<p>INSERT 명령어는 데이터의 내용을 입력할 떄 사용하는 명령어다.</p>
<blockquote>
</blockquote>
<h3 id="insert-문법">INSERT 문법</h3>
<table>
<thead>
<tr>
<th>문법</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>INSERT INTO 테이블명(컬럼명) VALUES(입력값)</td>
<td>데이터를 입력하고자 하는 테이블의 컬럼을 정의하여 데이터를 입력<br>컬럼은 생략 가능</td>
</tr>
</tbody></table>
<h2 id="3-update-사용하기">3. UPDATE 사용하기</h2>
<p>UPDATE 명령어는 데이터의 내용을 수정할 떄 사용하는 명령어다.</p>
<blockquote>
</blockquote>
<h3 id="update-문법">UPDATE 문법</h3>
<table>
<thead>
<tr>
<th>문법</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>UPDATE 테이블명<br>SET 컬럼명=입력값<br>WHERE 조건</td>
<td>UPDATE 문의 테이블에서 SET 절의 컬럼을 입력값으로 수정<br>WHERE 절에서 데이터 수정 조건 정의</td>
</tr>
</tbody></table>
<h2 id="4-delete-사용하기">4. DELETE 사용하기</h2>
<p>DELETE 명령어는 데이터의 내용을 삭제할 떄 사용하는 명령어다.</p>
<blockquote>
</blockquote>
<h3 id="delete-문법">DELETE 문법</h3>
<table>
<thead>
<tr>
<th>문법</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>DELETE[FROM] 테이블명<br>WHERE 조건</td>
<td>DELETE 문의 테이블에서 WHERE 절의 조건에 맞는 데이터를 제거<br>FROM 은 생략 가능</td>
</tr>
</tbody></table>
<h1 id="3-tcltransaction-control-language">3. TCL(Transaction Control Language)</h1>
<h2 id="1-트랜지션">1. 트랜지션</h2>
<p>TCL은 트랜재견을 제어하는 명령어다. 트랜잭션은 업무 처리를 위한 데이터베이스의 논리적인 작업 단위를 의미한다. 하나의 트랜잭션은 한 개 이상의 연산으로 이루어질 수 있고, 이 연산들은 완전히 처리되거나 아예 한 개도 처리가 되지 않아야 한다. 트랜잭션의 특징은 다음과 같다.</p>
<ul>
<li>Atomicity(원자성)</li>
<li>Consistency(일관성)</li>
<li>Isolation(고립성)</li>
<li>Durability(영속성)</li>
</ul>
<h2 id="2-commit">2. COMMIT</h2>
<p>COMMIT 은 하나의 트랜잭션을 정상적으로 완료하고 그 결과를 데이터베이스에 반영하는 명령어다. 쉽게 GitHub 커밋이라 생각하면 된다.</p>
<blockquote>
</blockquote>
<h3 id="commit-예제">COMMIT 예제</h3>
<pre><code class="language-sql">INSERT INTO ARSENAL
VALUES(100,&#39;은케티아&#39;,14,101,&#39;아스널 공격수&#39;,&#39;축신&#39;,1400000);
COMMIT;</code></pre>
<p>위 코드에서 INSERT 키워드를 사용하여 데이터를 입력하고 COMMIT 을 통하여 트랜잭션을 완료하고 데이터베이스에 추가한 정보가 반영된 것을 알 수 있다.</p>
<h2 id="3-rollback">3. ROLLBACK</h2>
<p>ROLLBACK 은 문제가 발생한 트랜잭션을 취소하기 위한 명령어다. 데이터베이스가 비정상적으로 종료된다면 자동으로 ROLLBACK 된다.</p>
<blockquote>
</blockquote>
<h3 id="rollback-예제">ROLLBACK 예제</h3>
<pre><code class="language-sql">UPDATE COACH
    SET COACH_NAME = &quot;아르테타&quot;
WHERE COACH_NAME = &quot;뱅거&quot;;
ROLLBACK;</code></pre>
<p>위 코드에서는 UPDATE 문장으로 COACH 테이블에서 COACH_NAME이 &#39;뱅거&#39; 라면 &#39;아르테타&#39;로 수정한다. 그리고 ROLLBACK 명령어를 통해 UPDATE 문장이 데이터베이스에 반영되지 않게 함.</p>
<h2 id="4-savepoint">4. SAVEPOINT</h2>
<p>SAVEPOINT 는 하나의 트랜잭션을 작게 분할하여 저장하는 명령어다. 하나의 트랜잭션에는 여러개의 SQVEPOINT 지정이 가능하다. 다음 예제를 통해 더 알아보자.</p>
<blockquote>
</blockquote>
<h3 id="savepoint-예제">SAVEPOINT 예제</h3>
<pre><code class="language-sql">SAVEPOINT SP1;
INSERT INTO MEMBERS VALUES(
    102, &#39;트로사르&#39;, 19, 101,
    &#39;최소 아자르&#39;, &#39;FW&#39;, 999999
);
SAVEPOINT SP2;
UPDATE MEMBERS
    SET MEMBER_NAME = &#39;외데고르&#39;
WHERE MEMBER_NAME = &#39;외질&#39;;
ROLLBACK TO SP2;</code></pre>
<p>다음 코드에서 알 수 있는 동작은 먼저 저장점 SP1을 지정 후 INSERT 문으로 데이터를 입력했다. 그 다음 저장점 SP2 를 지정한 뒤 UPDATE 문으로 MEMBER_NAME을 &#39;외질&#39; 에서 &#39;외데고르&#39;로 수정했다. 그 다음 ROLLBACK 명령어로 SP2 지점까지 롤백했다.</p>
<h1 id="4-sql-구문과-그-순서">4. SQL 구문과 그 순서</h1>
<p>SQL 구문의 종류는 다음과 같다.</p>
<table>
<thead>
<tr>
<th align="center">구문</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">FROM</td>
<td align="left">테이블 고르기</td>
</tr>
<tr>
<td align="center">WHERE</td>
<td align="left">테이블에 주어진 조건</td>
</tr>
<tr>
<td align="center">GROUP BY</td>
<td align="left">공통적인 데이터들끼리 그룹 묶기</td>
</tr>
<tr>
<td align="center">HAVING</td>
<td align="left">주어진 조건에 맞는 그룹들을 추출</td>
</tr>
<tr>
<td align="center">SELECT</td>
<td align="left">최종적으로 추출된 데이터 조회</td>
</tr>
<tr>
<td align="center">ORDER BY</td>
<td align="left">추출된 데이터 정렬</td>
</tr>
</tbody></table>
<p>SQL 구문의 작성/실행 순서는 다음 코드에서 확인할 수 있다.</p>
<pre><code class="language-sql">SELECT 컬럼명        //5
FROM 테이블명        //1
WHERE 테이블 조건    //2
GROUP BY 컬럼명    //3
HAVING 그룹조건    //4
ORDER BY 컬럼명    //6</code></pre>
<hr>

<p>오늘은 SQL의 3가지 종류 DML, DCL, TCL 에 대하여 알아보았다.
<span style="color: #d0d0d0"><del>(사실 내일 SQLD 시험이라 마지막 정리 느낌으로 했는데 붙었으면...)</del></span></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] ES6 문법 알아보기]]></title>
            <link>https://velog.io/@dev_lheesung/JavaScript-ES6-%EB%AC%B8%EB%B2%95-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@dev_lheesung/JavaScript-ES6-%EB%AC%B8%EB%B2%95-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Thu, 09 Mar 2023 11:18:05 GMT</pubDate>
            <description><![CDATA[<h1 id="📝-template-literal">📝 Template literal</h1>
<p><code>Template literal</code> 은 내장된 표현식을 허용하는 문자열 리터럴이다. <code>Template literal</code> 표현식/문자열 삽입, 여러ß 줄 문자열, 문자열 형식화, 문자열 태깅 등 다양한 기능을 제공합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-js">var name = &#39;Eddie Nketiah&#39;
var club = &#39;Arsenal&#39;
alert(`${name} is belong ${Arsenal}.`)</code></pre>
<p>사용 방식은 <code>backtick</code>으로 감싸고 <span style="color: orange"><code>${변수 명}</code></span> 을 넣고 싶은곳에 작성해주면 된다.</p>
<h1 id="🧬-destructuring">🧬 Destructuring</h1>
<p><strong>Destructuring</strong>(구조분해할당)은 구조화된 배열 또는 객체를 비구조화 하여 개별적인 변수에 할당하는 것이다. 배열 또는 객체 리터럴에서 필요한 값만 추출하여 변수에 할당하거나 반환할 때 유용하게 쓰인다. 그럼 ES6 에서 Destructuring 하는 법을 알아보자.</p>
<blockquote>
</blockquote>
<pre><code class="language-js">function address(address){
  const newAddress = {
    city: address.city,
    state: address.state,
    coutry: &#39;London&#39;
  }
  console.log(newAddress.city, newAddress.state, newAddress.coutry)
}
address({city: &#39;Eddie&#39;, state: &#39;Xhaka&#39;})</code></pre>
<p>다음 코드에서는 <code>{city: &#39;Eddie&#39;, state: &#39;Xhaka&#39;}</code> 객체가 먼저 <code>address</code> 함수에 전달되고, <code>address</code>객체의 <code>city</code>와 <code>state</code> 속성을 복사하여 <code>newAddress</code> 객체를 생성한다.</p>
<h1 id="🔄-반복문">🔄 반복문</h1>
<p>ES6에서 가장 많이 쓰는 반복문 중 하나인 <code>forEach</code> 이 있다. 빠르게 예제를 통해서 알아보자.</p>
<h3 id="foreach">forEach</h3>
<p><code>forEach()</code> 메서드는 배열에 활용이 가능한 메서드로, 파라미터로 주어진 함수를 배열 요소 각각에 대해 실행하는 메서드이다.
<code>map()</code> 메서드와 거의 비슷하지만 차이점은 따로 return 하는 값이 없다는 점이다. </p>
<blockquote>
</blockquote>
<pre><code class="language-js">var list = [1, 2, 3, 4, 5];
var loop = list.forEach(function(e, i) {
  return e;
});</code></pre>
<h1 id="🗂️-spread-operator">🗂️ Spread Operator</h1>
<p><strong>Spread Operator</strong>를 사용하면 배열, 문자열, 객체 등 반복 가능한 객체를 개별 요소로 분리시킬 수 있다. 예제를 보며 알아보자.</p>
<blockquote>
</blockquote>
<pre><code class="language-js">let GroupA = [&quot;Mary&quot;, &quot;Joel&quot;, &quot;Danny&quot;];
let GroupB = [&quot;David&quot;, ...GroupA, &quot;Lily&quot;];
console.log(GroupB); // [&quot;David&quot;, &quot;Mary&quot;, &quot;Joel&quot;, &quot;Danny&quot;, &quot;John&quot;, &quot;Lily&quot;]</code></pre>
<p><code>GroupB</code> 에서 <span style="color: orange"><code>...</code></span>을 사용하여 <code>[&quot;Mary&quot;, &quot;Joel&quot;, &quot;Danny&quot;]</code> 가 전재되어 <code>GroupB</code>에 추가된 것을 확인할 수 있다.</p>
<h1 id="🤖-function">🤖 Function</h1>
<h3 id="1-🦦-parameter매개변수">1. 🦦 Parameter(매개변수)</h3>
<p>ES6 부터 함수 <code>Parameter</code>에 기본값을 줄 수 있게 되었다. 또 <code>Parameter</code>에 함수 호출을 넣어줄 수도 있다. 또 여러개의 매개변수를 받을 때에는 <code>...Parameter</code> 를 Parameter로 써주게 되면 매개변수를 여러개 받을 수 있다.</p>
<blockquote>
</blockquote>
<pre><code class="language-js">function example0(a = 1){
  console.log(a);
  return 10;
}
function example1(b = example0){
  console.log(b);
}
function example2(...c){
  return c;
}
example0();                // { a: 1 }
example1();             // { b: 10 }
example2(1,2,3,4);         // { c: [1, 2, 3, 4] }</code></pre>
<h3 id="2-🏹-arrow-function">2. 🏹 Arrow function</h3>
<p>ES6 에서는 화살표를 이용하여 함수를 정의하는 새로운 방법이 추가되었다.</p>
<blockquote>
</blockquote>
<h4 id="한-줄에-쓰기">한 줄에 쓰기</h4>
<p>화살표 함수는 한줄로 간단히 정의할 수 있다. 중괄호를 사용하지 않고 바로 오른쪽에 정의하고, <code>return</code> 키워드를 명시적으로 정의하지 않아도 오른쪽에 있는 값이 알아서 <code>return</code> 된다.(써도 문제 X)</p>
<pre><code class="language-js">const example3 = (/*Parameter 는 여기에*/) =&gt; { return 10; }
const example4 = (a, b) =&gt; a+b;
console.log(example3);            // 10
console.log(example4(10, 20);    // 20</code></pre>
<h4 id="여러줄에-쓰기">여러줄에 쓰기</h4>
<p>화살표 함수에 여러줄의 코드가 포함되어야 한다면 전체를 중괄호로 묶고 <code>return</code> 키워드를 사용해야 한다.</p>
<pre><code class="language-js">const example5 = (a, b) =&gt; {
  var result;
  if(a &gt; b) result = a-b;
  else result = b-a;
  return result;
}
console.log(example5(14, 7))    // 7</code></pre>
<h3 id="3-🥷-anonymous-function">3. 🥷 Anonymous function</h3>
<p>Anonoymous 함수는 변수에 함수를 대입하는 느낌의 방식으로 함수를 정의한다.</p>
<blockquote>
</blockquote>
<pre><code class="language-js">const Func = function () {
  return &quot;I love Arseanl F.C&quot;;
};
console.log(Func); // I love Arsenal F.C</code></pre>
<h1 id="🏟️-includes">🏟️ includes</h1>
<p><code>includes()</code> 는 Array 에 <code>()</code> 안에 있는 값이 있는지 검사하여 <code>Boolean</code> 으로 반환해준다.</p>
<blockquote>
</blockquote>
<pre><code class="language-js">let numArray = [1, 2, 3, 4, 5];
console.log(numArray.includes(7)); // false</code></pre>
<h1 id="🆚-var-let-const">🆚 var, let, const</h1>
<p><code>var</code> 타입은 스코프가 함수 단위다.Js에서는 값의 유효 범위를 스코프라고 표현한다. 쉽게 말하자면 값을 사용할 수 있는 코드 영역이라고 말할 수 있다.</p>
<blockquote>
</blockquote>
<pre><code class="language-js">var a = &#39;hi&#39;;
var a = &#39;hello&#39;;
let b = &#39;good&#39;;
// let b = &#39;job&#39; // Uncaught SyntaxError: Identifier &#39;message&#39; has already been declared
const c = &#39;London&#39;;
// const c = &#39;Paris&#39;; //Uncaught SyntaxError: Identifier &#39;message&#39; has already been declared
if(true){
  var a = &#39;world!&#39;;
  let b = &#39;great&#39;;
  const c = &#39;Tokyo&#39;;
  console.log(a);    // world!
  console.log(b);    // great
  console.log(c);    // Tokyo
}
console.log(a); // world!
console.log(b); // good
console.log(c); // London</code></pre>
<p>위 코드에서 나온 것 처럼 var 는 중복으로 변수를 설정할 수 있다. 위 코드에서 나온 실행결과처럼 <code>if</code> 문 밖에 <code>a</code> 에 &#39;hello&#39;로 설정하고 <code>if</code> 문 안에서 새로운 값을 설정했는데 <code>if</code> 문 바깥의 값도 수정됬다. 이것은 <code>var</code> 가 스코프 관련 문제를 야기시킨다는 것을 알 수 있다. 이러한 단점을 보안한 것이 바로 let 과 const 다.</p>
<h1 id="🧳class">🧳Class</h1>
<p>ES6에서는 기존의 <code>prototype</code> 기반으로 클래스를 만드는 것보다 명료하게 클래스를 만들 수 있게 바뀌었다.</p>
<blockquote>
<h3 id="✏️-class-선언하기">✏️ Class 선언하기</h3>
</blockquote>
<pre><code class="language-js">// animal.js
export class Animal {
  constructor(type, legs) {
    this.type = type;
    this.legs = legs;
  }
  makeNoise(sound = &quot;Loud Noise&quot;) {
    console.log(sound);
  }
  get metaData() {
    return `Type: ${this.type}, Legs: ${this.legs}`;
  }
  static return10() {
    return 10;
  }
}
export class Cat extends Animal {
  makeNoise(sound = &quot;meow&quot;) {
    console.log(sound);
  }
}
// Class 선언은 Function 선언과 달리 호이스팅 되지 않음.</code></pre>
<h3 id="🖇️-class-사용하기">🖇️ Class 사용하기</h3>
<pre><code class="language-js">// app.js
import { Animal, Cat } from &quot;./aniaml&quot;;
console.log(Animal.return10()); // 10
const cat = new Cat(&quot;feline&quot;, 4);
cat.makeNoise(); // meow</code></pre>
<h1 id="⛓️-비동기-처리">⛓️ 비동기 처리</h1>
<p>자바스크립트의 비동기 처리란 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 자바스크립트의 특성을 말한다. 동시에 여러가지 작업을 처리할 수 있고 기다리는 과정에서 다른 함수를 호출할수도 있다.
<img src="https://images.velog.io/images/yangddu/post/f6a3a5be-59e9-4fb7-9074-966bc8b61574/%E1%84%83%E1%85%A9%E1%86%BC%E1%84%80%E1%85%B5%E1%84%87%E1%85%B5%E1%84%83%E1%85%A9%E1%86%BC%E1%84%80%E1%85%B52.jpeg">
다음 그림에서 동기 처리를 할 때는 하나의 작업씩 처리하기 때문에 45초가 걸린다. 하지만 비동기 처리를 하였을 때는 1번 작업이 시작하면 동시에 2번 작업이 실행된다. 1번 작업은 결과값이 나오는대로 출력된다.</p>
<h1 id="📟-es6-modules">📟 ES6 Modules</h1>
<p>이전 버전의 js 에서는 외부 라이브러리를 가져오는 방법으로만 <code>import</code> 가 가능했지만, ES6 부터는 정식으로 module 을 제공하고 있다.
특히 같은 파일 내에서 여러 개의 모듈을 <code>export</code> 하는 것도 가능하며, object destructuring 을 통해 간편하게 <code>import</code> 할 수 있으며, 특정 모듈을 원하는 이름 으로 <code>import</code> 하는 것도 가능하다.</p>
<blockquote>
</blockquote>
<pre><code class="language-js">// module.js
export const func = (a,b) =&gt; a + b;</code></pre>
<pre><code class="language-js">import { func } from &quot;./module.js&quot;
console.log(func(1,2)); // 3</code></pre>
<h1 id="🤙🏻-promise">🤙🏻 promise</h1>
<p>ES6 <code>Promise</code> 는 비동기 처리를 위한 표준 API이다. 기존에는 콜백 함수를 이용하여 비동기 처리를 했지만, 콜백 함수를 이용한 비동기 처리는 코드의 가독성과 유지보수의 어려움이 존재한다. ES6 <code>Promise</code>는 이러한 문제점을 보완하여 비동기 처리를 보다 쉽고 효율적으로 할 수 있도록 도와준다. 그럼 <code>Promise</code> 를 사용하는 법을 알아보자.</p>
<h3 id="1-promise-생성">1. promise 생성</h3>
<p>ES6 <code>Promise</code> 는 new Promise()를 통해 생성할 수 있다. <code>new Promise()</code>는 하나의 콜백 함수를 인자로 받으며, 이 콜백 함수는 <code>resolve</code>와 <code>reject</code>라는 두 개의 인자를 받는다. <code>resolve</code>는 성공적으로 실행됐을 때, <code>reject</code>는 실패했을 때 호출된다.</p>
<blockquote>
</blockquote>
<pre><code class="language-js">new Promise((resolve, reject) =&gt; {
  // 비동기 처리 코드
  if (/* 성공적으로 처리됐을 때 */) {
    resolve(/* 결과 값 */);
  } else {
    reject(/* 에러 메시지 */);
  }
});</code></pre>
<h3 id="2-promise-사용">2. promise 사용</h3>
<p><code>Promise</code> 객체를 생성한 후, <code>.then()</code>과 <code>.catch()</code>를 이용하여 비동기 처리 결과를 다루어 줄 수 있다. <code>.then()</code>은 성공적으로 처리됐을 때 실행되며, <code>.catch()</code>는 실패했을 때 실행된다.</p>
<blockquote>
</blockquote>
<pre><code class="language-js">promiseObject.then(result =&gt; {
  console.log(&quot;Succeed&quot;); // 성공적으로 처리됐을 때 실행될 코드
}).catch(error =&gt; {
  console.log(&quot;Failed&quot;) ;// 실패했을 때 실행될 코드
});</code></pre>
<h3 id="3-promise-chaining">3. promise chaining</h3>
<p><code>Promise</code> 객체는 <strong>체이닝(Chaining)</strong> 을 지원한다. 즉, 여러 개의 비동기 처리를 연결해서 사용할 수 있다. 이를 통해 가독성이 높아지고, 중첩된 콜백 함수를 사용하지 않아도 된다.</p>
<blockquote>
</blockquote>
<pre><code class="language-js">promiseObject.then(result =&gt; {
  console.log(1);// 첫 번째 비동기 처리
  return result;
}).then(result =&gt; {
  console.log(2);  // 두 번째 비동기 처리
  return result;
}).then(result =&gt; {
  console.log(3);  // 세 번째 비동기 처리
}).catch(error =&gt; {
    console.log(&quot;Error&quot;);// 실패했을 때 실행될 코드
});</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] 02.Hello,JavaScript!]]></title>
            <link>https://velog.io/@dev_lheesung/JavaScript-02.Hello-JavaScript</link>
            <guid>https://velog.io/@dev_lheesung/JavaScript-02.Hello-JavaScript</guid>
            <pubDate>Tue, 07 Mar 2023 11:09:50 GMT</pubDate>
            <description><![CDATA[<h1 id="👋-hello-javascript">👋 Hello, JavaScript!</h1>
<p><strong>HTML</strong> 내에서 JavaScript 를 사용하고 싶으면 <code>&lt;script&gt;</code> 태그를 이용하면 된다. 다음 코드를 통해 간단한 메세지를 뛰워보자.</p>
<pre><code class="language-html">&lt;!DOCTYPE HTML&gt;
&lt;html&gt;
&lt;body&gt;
  &lt;h1&gt;JavaScript&lt;/h1&gt;
  &lt;script&gt;
    alert( &#39;Hello, JavaScript!&#39; );
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/54847d0f-02a2-43d6-b5d0-943b5705ad1f/image.png" alt=""></p>
<p>이 처럼 <code>&lt;script&gt;</code> 태그에는 js 코드가 들어가고, 브라우저가 이 태그와 만나면 <code>&lt;script&gt;</code> 태그 내의 코드를 자동으로 처리한다.</p>
<blockquote>
<p><strong>cf) 외부 스크립트 참조하여 쓰기</strong>
코드를 짜다 보면 코드가 길어져서 가독성이 떨어질 때가 있을 것이다. 그럴때는</p>
</blockquote>
<pre><code class="language-html">&lt;script src=&quot;/*js 파일 경로*/&quot;&gt;&lt;/script&gt;</code></pre>
<p>이렇게 쓰면 코드를 분할하고, 그걸 참조하여 쓸 수 있게 된다.</p>
<h1 id="✍️-세미콜론">✍️ 세미콜론</h1>
<p>JavaScript 는 C, C++ 처럼 세미콜론을 꼭 붙이지 않아도 된다.JavaScript 에서는 줄바꿈이 있다면 세미콜론을 생략해도 된다. 물론 적어줘도 상관은 없다. 하지만 같은 줄의 2개의 명령을 실행하는 코드가 함께 있다면 세미콜론으로 문장의 끝을 나타내야 한다.</p>
<pre><code class="language-js">console.log(1); console.log(2);
console.log(1)
console.log(2)</code></pre>
<h1 id="🧐-use-strict">🧐 use strict</h1>
<p><code>&quot;use strict&quot;</code> 는 단순한 문자열 처럼 보인다. 하지만 <code>&quot;use strict&quot;</code> 가 스크립트 최상단에 위치한다면 스크립트 전체가 <strong>모던</strong>한 방식으로 바뀐다.이 특별한 지시자를 사용하여 엄격모드(strict mode)를 활성화 했을 때만 이 변경사항이 활성화되도록 하였다.</p>
<h1 id="📦변수">📦변수</h1>
<p><strong>변수(variable)</strong> 는 데이터를 저장할 때 쓰이는 이름이 붙은 저장소라고 할 수 있다. JavaScript 에서는 대개 <code>let</code> 키워들를 사용하여 변수를 생성한다. 아래 코드를 통해 더 알아보자.</p>
<pre><code class="language-js">let a;
let b = 10;
let c, d;
a = b;
alert(a + &quot; &quot; + b);</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_lheesung/post/16a9af46-00a1-4783-9e81-6ab78dd612ef/image.png" alt="">
위 코드를 실행하면 이와 같이 <code>a</code> 와 <code>b</code> 에 10이 저장되어 나오는 모습을 볼 수 있다. 또 <code>c</code> 와 <code>d</code> 같이 쉼표로 구분하여 한줄에 변수를 선언할 수도 있다.</p>
<blockquote>
<p><strong>cf) <code>let</code> 대신 <code>var</code> ?</strong>
사실 변수를 선언하는 방법은 하나 더 존재한다. 바로 <code>var</code> 키워드를 사용하는 것이다. <code>var</code> 를 사용하는 방법은 <code>let</code> 과 똑같다.하지만 <code>var</code> 와 <code>let</code> 의 차이는 존재한다.</p>
</blockquote>
<pre><code class="language-js">var a = &#39;중복선언&#39;
console.log(a); // 중복선언
var a = &#39;가능&#39;
console.log(a); // 가능</code></pre>
<pre><code class="language-js">let a = &#39;중복선언&#39;
console.log(a); // 중복선언
let a = &#39;불가능&#39;
console.log(a); // Uncaught SyntaxError: Identifier &#39;name&#39; has already been declared
a = &#39;이렇게 하는건 가능&#39;
console.log(a); // 이렇게 하는건 가능</code></pre>
<p>위처럼 <code>var</code> 는 중복선언이 가능하지만 <code>let</code> 은 중복 선언이 불가능 하다.</p>
<h1 id="🏷️-변수-명명-규칙">🏷️ 변수 명명 규칙</h1>
<p>JavaScript 에서의 변수 명명 시 두가지의 제약사항이 있다.</p>
<ol>
<li>변수명에는 오직 문자와 숫자, 그리고 <code>$</code>와 <code>_</code>만 들어갈 수 있다.</li>
<li>첫 글자는 숫자가 될 수 없다.</li>
<li>여러 단어를 조합할때는 <strong>카멜 표기법(camelCase)</strong> 를 사용한다.<pre><code class="language-js"> var thisIsLongName_1234$;</code></pre>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] 01.자바스크립트?]]></title>
            <link>https://velog.io/@dev_lheesung/JavaScript-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</link>
            <guid>https://velog.io/@dev_lheesung/JavaScript-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</guid>
            <pubDate>Tue, 07 Mar 2023 06:00:19 GMT</pubDate>
            <description><![CDATA[<h1 id="🍋자바스크립트란">🍋자바스크립트란?</h1>
<p>자바스크립트는 딱딱한 웹 페이제에 생동감을 불어넣기 위하여 만들어진 프로그래밍 언어다. 자바스크립트로 작성한 문법은 <strong>script</strong> 라고 한다. 이 스크립트는 웹페이지의 HTML 안에 작성할 수 있고, 이 DOM을 불러올 때 자바스크립트가 자동으로 실행된다.</p>
<blockquote>
<p>cf) 🤷🏼‍♂️ 자바스크립트? 자바랑 비슷한건가?
결론부터 말하자면 자바스크립트는 자바와는 전혀 다른 독자적인 언어다. 자바스크립트가 처음 세상에 나왔을 때는 &#39;LiveScript&#39; 라 불렸다. 이 시기에 자바의 인기는 아주 높은 상황이었고 사람들은 &#39;LiveScript&#39;를 자바의 동생 격 언어로 홍보하면 널리 알려질 것 같아 이름이 &#39;JavaScript&#39; 로 바뀌었다는 어이없는 사실이 있다.</p>
</blockquote>
<h1 id="🛠️js가-브라우저에서-할-수-있는-일">🛠️Js가 브라우저에서 할 수 있는 일</h1>
<p>자바스크립트는 <strong>안전한</strong> 프로그래밍 언어라고 할 수 있다. 메모리나 CPU 같은 저수준 영역의 조작을 허용하지 않기 때문이다. 애초에 이러한 접근이 필요하지 않은 브라우저를 대상으로 만들어진 언어이기 때문이다.
자바스크립트는 실행 환경에 따라서 그 능력이 달라진다. <strong>Node.js</strong> 환경에선 임의의 파일을 읽거가 쓰고, 네트워크 요청을 수행하는 다양한 함수를 지원한다.</p>
<blockquote>
<ul>
<li>페이지에 새로운 DOM을 추가하거나, 기존 HTML, 혹은 스타일을 수정할 수 있다.</li>
<li>마우스 클릭, 포인터의 움직임 등 사용자의 의한 이벤트에 반응할 수 있다.</li>
<li>네트워크를 통하여 원격 서버에 요청을 보내거나, 파일 업로드, 혹은 다운로드를 할 수 있다.</li>
<li>쿠키를 가져오거나 설정하고, 사용자에게 메세지를 보여줄 수 있다.</li>
<li>로컬 스토리지에 데이터를 저장할 수 있다.</li>
</ul>
</blockquote>
<h1 id="🤦js가-브라우저에서-할-수-없는-일">🤦‍Js가 브라우저에서 할 수 없는 일</h1>
<p>자바스크립트는 브라우저의 보안을 위해 기능에 제약을 걸어 두었다.</p>
<blockquote>
<ul>
<li>웹페이지 내 스크립트 티스크에 저장되어있는 파일을 보고, 쓰고, 실행할 때 제약을 받을 수 있다.</li>
<li>브라우저 내 탭과 창은 대개 서로의 정보를 알 수 없다. 하지만 자바스크립트를 사용하면 한 창에서 다른 창을 열 때는 예외가 적용이 된다. 하지만 이 겨우에서도 도메인이나 프로토콜, 포트가 다르면 그 페이지에 접근할 수 없다.</li>
<li>타 사이트나 도메인에서 데이터를 받아올 수 없다. 만일 가능하더라도 원격 서버에서 명확한 승인이 필요하다.</li>
</ul>
</blockquote>
<h1 id="💪js만의-강점">💪Js만의 강점</h1>
<blockquote>
<ul>
<li>HTML/CSS 와 완전히 통합할 수 있음</li>
<li>간단한 일은 간단히 처리 가능</li>
<li>주요 브라우저는 모두 지원하고, 기본 언어로 사용됨</li>
</ul>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>