<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>cjmin-n.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 28 Aug 2025 03:13:47 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>cjmin-n.log</title>
            <url>https://velog.velcdn.com/images/cjmin-n/profile/2e0ca1fe-b4b3-408d-b170-4198fdbf41da/image.jfif</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. cjmin-n.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/cjmin-n" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[RAG | Retriever 정리]]></title>
            <link>https://velog.io/@cjmin-n/RAG-Retriever-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@cjmin-n/RAG-Retriever-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 28 Aug 2025 03:13:47 GMT</pubDate>
            <description><![CDATA[<h2 id="hybrid-retriever">Hybrid Retriever</h2>
<p>밀집 검색(유사도)과 희소 검색(키워드) 두가지 방법을 융합하여 검색 정확도를 향상시키는 대표적인 기법</p>
<ul>
<li>희소 검색: 정확한 검색 용이</li>
<li>밀집 검색: 맥락에 알맞는 검색 용이</li>
</ul>
<p><strong>Dense Vector(조밀 벡터)</strong></p>
<ul>
<li>의미적 유사성에 강함</li>
<li>대부분의 값이 0이 아닌 실수</li>
<li>예: &quot;How to catch fish&quot; -&gt; &quot;fishing techniques&quot;매칭</li>
</ul>
<p><strong>Sparse Vector(희소 벡터)</strong></p>
<ul>
<li>정확한 키워드 매칭에 강함</li>
<li>대부분의 값이 0, 일부만 0이 아님</li>
<li>BM25, TF-IDF 기반</li>
<li>예: &quot;Alaskan Pollock&quot; -&gt; 정확히 &quot;Alaskan Pollock&quot; 매칭</li>
</ul>
<h3 id="어떻게-검색-결과를-합칠까">어떻게 검색 결과를 합칠까?</h3>
<p>두 검색 결과를 합치는 과정에서 다양한 알고리즘이 존재하지만 RRF 방식 가장 선호</p>
<ul>
<li><p><em>직접 구현 X - vector DB나 Langchain에서 제공</em></p>
</li>
<li><p><em>두 리트리버의 비중을 고려해야할 때, Langchain에서 제공하는 앙상블 리트리버 같은 모듈을 사용해볼 수 있다.</em></p>
</li>
</ul>
<hr>
<h2 id="multi-query-retriever">Multi Query Retriever</h2>
<ul>
<li><p>하나의 질문으로 여러 관점의 검색을 동시에 수행하는 방식으로, LLM 기반 성능 향상 기법</p>
</li>
<li><p>LLM을 사용해 원본 쿼리에서 다양한 관점의 여러 쿼리를 자동 생성하는 기법</p>
</li>
<li><p>여러 쿼리를 자동생성하여 검색하면 놓칠 수 있는 관련 문서 추가 발견 가능하여 단일 쿼리 검색의 한계의 문제점 극복</p>
</li>
<li><p><em>랭체인에서 초기부터 사용했음 → 검색 성능을 높이는데 좋기 때문</em></p>
</li>
<li><p><em>멀티쿼리를 만들 때 LLM에 적절한 프롬프트를 주는게 가장 중요하다.</em></p>
</li>
</ul>
<blockquote>
<ol>
<li>주어진 질문이 모호할 때 구체적으로!<ol start="2">
<li>복합한 쿼리를 단순하게</li>
<li>동일한 질문에 대해서 같은 용어들이 다르게 표현되는 것에 초점을 두고 다양한 표현방법으로 표현</li>
</ol>
</li>
</ol>
</blockquote>
<h3 id="왜-multi-query-retriever가-필요한가">왜 Multi-Query Retriever가 필요한가?</h3>
<p><strong>기존 RAG 시스템의 한계점</strong></p>
<ul>
<li><strong>단일 쿼리의 제약</strong>: 사용자의 질문이 모호하거나 불완전할 때 관련 문서를 놓칠 수 있음</li>
<li><strong>의미적 표현의 다양성</strong>: 같은 의마라도 다양한 표현 방식으로 인해 검색 성능이 제한됨</li>
<li><strong>검색 범위의 한계</strong>: 하나의 쿼리로는 복합적인 정보 요구사항을 충족하기 어려움<h3 id="multi-query-retriever-의-장점">Multi-Query Retriever 의 장점</h3>
</li>
<li><strong>검색 범위 확장</strong>: 여러 관점에서 동일한 정보를 검색하여 누락 방지</li>
<li><strong>의미적 다양성</strong>: 다양한 표현으로 쿼리를 생성하여 검색 정확도 향상</li>
<li><strong>컨텍스트 풍부</strong>화: 다각도의 검색 결과를 통합하여 더 완전한 답변 생성</li>
<li><strong>견고성 증대</strong>: 단일 쿼리 실패 시에도 다른 쿼리들이 보완 역할 수행</li>
</ul>
<p><em>- 검색이 실패하는 확률이 줄어든다!</em>
<em>- LLM모델은 가벼운 것을 쓰는게 좋다.</em></p>
<h3 id="rag-fusion">RAG Fusion</h3>
<p>여러 쿼리를 통해 검색된 문서의 중복을 제거하고, 유사도 기준으로 재정렬하는 RAG Fusion 방식도 활용
<em>- Reranker + Multiquery의 조합</em></p>
<hr>
<h2 id="self-query-retriever">Self Query Retriever</h2>
<ul>
<li>주어진 사용자의 질문을 벡터DB를 조회할 필터링 쿼리로 변환하여 검색하는 방식</li>
<li>자연어 쿼리 → 쿼리 구성 LLM이 쿼리 작성 → 벡터 저장소 검색 및 필터링에 활용</li>
<li>사용자의 질문만으로 벡터 DB 검색이 제대로 이뤄지지 않을 수 있는 메타데이터 중심 질문을 처리하기에 적합</li>
<li>핵심은 쿼리 생성기와 쿼리 번역기이다. 벡터 DB에 알맞은 필터링 쿼리 생성을 위해 번역이 필요하다.</li>
</ul>
<ul>
<li><em>Agentic RAG일 경우, 여러 Retriever를 두고 LLM이 선택해서 쓸 수 있게 해줄 수 있다.</em></li>
<li><em>논문에 사용하기 좋을 듯 - &#39;장르&#39; 등 특정 키워드 등 메타데이터 중심의 질문에 사용</em></li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[AI | 기본 환경 세팅]]></title>
            <link>https://velog.io/@cjmin-n/AI-%EA%B8%B0%EB%B3%B8-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85</link>
            <guid>https://velog.io/@cjmin-n/AI-%EA%B8%B0%EB%B3%B8-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85</guid>
            <pubDate>Wed, 12 Mar 2025 07:37:47 GMT</pubDate>
            <description><![CDATA[<ol>
<li>IDE 설치 ( cursor, vscode, pycharm 중 1)</li>
<li>CUDA 설치 <a href="https://developer.nvidia.com/cuda-11-8-0-download-archive?target_os=Windows&amp;target_arch=x86_64&amp;target_version=11&amp;target_type=exe_local">11.8 버전</a><ul>
<li>CUDA는 PyTorch에서 GPU를 활용하기 위한 기술
NVIDIA에서 개발한 GPU 연산 가속 기술로 PyTorch에서는 CUDA를 활용해서 연산을 GPU에서 수행할 수 있도록 도와주는 역할
→ 그럼 해당 경로 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8 에 cuda가 설치된다.<ul>
<li>맥북: cuda 설치할 필요 없음.</li>
<li>NVDIA GPU 없으면 cuda 설치 안해도됨. 속도는 느림</li>
</ul>
</li>
</ul>
</li>
</ol>
<p><strong>가상환경 진입</strong></p>
<pre><code class="language-cmd">conda create -n nlp python=3.10 -y
conda activate nlp # 가상환경 진입
pip3 install torch torchvision torchaudio --index-url # 파이토치 설치</code></pre>
<p><strong>파이토치 설치</strong> </p>
<pre><code class="language-python">import torch</code></pre>
<pre><code class="language-cmd">torch.cuda.is_available() # TRUE 가 나오는지 확인</code></pre>
<p><strong>conda 환경 구축</strong>
conda-forge github에서 window 버전 다운.</p>
<ul>
<li>터미널 환경.
토글에서 powershall이 아닌 cmd 로 변경.
base는 깨끗하게 유지. 보통 c드라이브 혹은 사용자 안 dev &gt; 폴더명 에 위치</li>
</ul>
<pre><code class="language-cmd">conda create -n 폴더명 python=3.12
conda env list # 현재 설치된 파일 확인 가능
conda deactivate → base 로 바꾸기</code></pre>
<p>!주의사항.
⇒ \설치된 pip를 실행시킬때 해당 경로의 인터프리터 변경</p>
<p>** FAST API 를 통한 Serving **</p>
<pre><code class="language-cmd">pip install fastapi uvicorn python-multipart
uvicorn main:app --reload # reload: 수정 시 바로 반영</code></pre>
<p>ex ) <a href="http://127.0.0.1:8000/docs">http://127.0.0.1:8000/docs</a> - API test 가능</p>
<p><strong>FastAPI의 핵심 특징</strong></p>
<ol>
<li>고성능 (Fast!)</li>
</ol>
<ul>
<li>arlette(비동기 웹 서버)와 Pydantic(데이터 검증 라이브러리)을 기반으로 구현
비동기(Async) 기능을 기본적으로 지원해서 동시 요청 처리 성능이 뛰어나다.
성능은 Node.js 및 Go 수준으로 빠름.</li>
</ul>
<ol start="2">
<li>문서화 (Swagger UI, ReDoc)</li>
</ol>
<ul>
<li>API를 만들면 자동으로 문서화(Swagger UI, ReDoc) 제공
/docs (Swagger UI) → API를 직접 테스트 가능
/redoc (ReDoc) → 깔끔한 API 문서 제공</li>
</ul>
<ol start="3">
<li>타입 기반 요청 &amp; 응답 검증</li>
</ol>
<ul>
<li>Pyantic을 활용해서 요청 데이터와 응답 데이터의 타입을 강력하게 검증할 수 있다.
덕분에 버그를 줄이고, 코드 가독성이 높아짐.
비동기(Async) 지원</li>
<li>async/await을 기본적으로 지원해서 IO-bound 작업(데이터베이스, API 호출 등)에서 성능이 뛰어남.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[AI | with Python]]></title>
            <link>https://velog.io/@cjmin-n/AI-with-Python</link>
            <guid>https://velog.io/@cjmin-n/AI-with-Python</guid>
            <pubDate>Sat, 08 Mar 2025 15:24:54 GMT</pubDate>
            <description><![CDATA[<h1 id="ai-모델-활용">AI 모델 활용</h1>
<h2 id="1-python-패키지설치">1. Python 패키지설치</h2>
<h3 id="anaconda">Anaconda</h3>
<p>수학과 과학 분야에서 사용되는 여러 패키지들을 묶어 놓은 파이썬 배포판에 포함되어 있는 패키지 매니저이자 가상환경 관리 프로그램. 아나콘다(Anaconda)는 기존 파이썬에 비해 매우 용량이 크지만 데이터 분석을 위한 다양한 라이브러리를 갖춘 파이썬을 배포한다. 주요 패키지가 설치가 되어있다.</p>
<ul>
<li><p>200명 이상의 직원이 있는 기업 또는 정부 조직이 Anaconda Repository를 사용하는 경우 유료</p>
</li>
<li><p>conda : 패키지 매니저</p>
<h3 id="conda-forge">conda-forge</h3>
<p>conda-forge는 수천명의 기여자로 구성되어 있는 커뮤니티 채널이다. conda-forge는 PyPI와 비슷하지만 통합된 자동화 빌드 인프라와 더 많은 레시피 검토가 이루어진다.</p>
</li>
<li><p>open source</p>
</li>
<li><p><a href="https://github.com/conda-forge/miniforge">mini-forge</a></p>
<ul>
<li>기본(그리고 유일한) 채널로 conda-forge를 사용.</li>
<li>표준 Python 인터프리터(일명 &quot;CPython&quot;) 대신 PyPy에 대한 지원.</li>
<li>콘다보다 더 빠른 맘바(mamba)도 지원.</li>
<li>다양한 CPU 아키텍처(x86_64, ppc64le, Apple M1을 포함한 aarch64)지원.</li>
</ul>
</li>
</ul>
<h2 id="2-mini-forge-설치방법주의">2. mini-forge 설치방법(주의)</h2>
<ol>
<li><strong>기존에 파이썬이 있다면, 설치 시 오류가 날 수 있기 때문에 지워야한다.</strong>
<img src="https://velog.velcdn.com/images/cjmin-n/post/3288f527-8454-4fd4-b088-9b00b41047f1/image.png" alt="2번"></li>
</ol>
<p><strong>2. Advanced Installation Options-&gt; 두번째 Add 추가해줘야함</strong>
-&gt; 시스템환경변수편집&gt;환경변수&gt;...사용자변수&gt;Path 에 ..\miniforge.. 5개정도 추가된지 확인!</p>
<h2 id="3-vscode에서-사용방법주의">3. vscode에서 사용방법(주의)</h2>
<p><strong>1.terminal - cmd 로 실행</strong>
-&gt; powershell은 파이썬과 궁합이 맞지 않음
<strong>2. 에디터, 터미널에서 conda 가상환경 이름 맞춰주기</strong>
-&gt; vscode에서는 PyCharm과 달리 터미널에서 가상환경을 만들어도 에디터에 바로 바뀌지않기때문에 수동으로 바꿔줘야한다.</p>
<pre><code class="language-cmd">python main.py                        # main.py 파일 실행
conda env list                        # base 경로 실행(더럽히면 안됨)
conda create -n proj1 python=3.12    # 가상환경명(proj1:폴더명) / 파이썬 버전설정가능
conda activate proj1                # 설치 후 가상 환경 활성화</code></pre>
<h2 id="4-mediapipe-연습">4. MediaPipe 연습</h2>
<p>MediaPipe 솔루션 가이드 : <a href="https://ai.google.dev/edge/mediapipe/solutions/guide?hl=ko">https://ai.google.dev/edge/mediapipe/solutions/guide?hl=ko</a></p>
<ul>
<li><p>객체감지 : EfficientDet-Lite0, EfficientDet-Lite2 모델 사용</p>
<ul>
<li>EfficientDet-Lite2 사용이유: 하기 사진에서 사람이 몇명인지 구할 때, EfficientDet-Lite0 모델은 2명만 찾았기때문에
<img src="https://velog.velcdn.com/images/cjmin-n/post/0f7d9d61-04d3-4ee6-ab8d-f00805e14da4/image.png" alt=""></li>
</ul>
</li>
<li><p>이미지 분류 : EfficientNet-Lite0 모델 사용</p>
</li>
<li><blockquote>
<p>github <a href="https://github.com/cjmin-n/Python/tree/main/proj1">https://github.com/cjmin-n/Python/tree/main/proj1</a></p>
</blockquote>
</li>
<li><p>손 랜드마크 감지 / 얼굴 랜드마크 감지 / 얼굴 인식 연습</p>
</li>
<li><blockquote>
<p>github <a href="https://github.com/cjmin-n/Python/tree/main/practice">https://github.com/cjmin-n/Python/tree/main/practice</a></p>
</blockquote>
</li>
</ul>
<h2 id="5-insightface-ai">5. InsightFace AI</h2>
<p>github: <a href="https://github.com/deepinsight/insightface">https://github.com/deepinsight/insightface</a>
PyPI: <a href="https://pypi.org/project/insightface/">https://pypi.org/project/insightface/</a></p>
<p><strong>PyPI - windows 설치 에러해결방법</strong></p>
<ul>
<li>23년 이후로 업데이트 되지않았기 때문에</li>
<li>Microsoft C++ Build Tools 설치 
<a href="https://visualstudio.microsoft.com/visual-cpp-build-tools/">https://visualstudio.microsoft.com/visual-cpp-build-tools/</a> ** 
: C++를 사용하여 데스크톱 개발 체크 후 설치**</li>
<li>마지막 에러 시 onnxruntime 설치
: onnxruntime 추론기 기반의 모델이기때문<pre><code class="language-cmd">pip install insightface
...
pip install onnxruntime </code></pre>
</li>
<li>같은 사람 비교 / 다른 사람 비교
  결과가 0.4 이상 시 같은 사람으로 보는데,
  아무리 닮은 사람도 0.4이상이 나오지 않고
  같은 사람의 앞모습, 옆모습을 비교해도 0.4이상이 나왔다.
  <em>이미지사이즈가 작을 경우 오류가 생긴다.</em>
  -&gt; github : <a href="https://github.com/cjmin-n/Python/tree/main/proj2">https://github.com/cjmin-n/Python/tree/main/proj2</a></li>
</ul>
<hr>
<h4 id="ai-참고할-문서">AI 참고할 문서</h4>
<ol>
<li><a href="https://paperswithcode.com/">https://paperswithcode.com/</a> : ai관련 논문(해당 코드)</li>
<li><a href="https://elseif-2022.kakao.com/2022/session/13">https://elseif-2022.kakao.com/2022/session/13</a> : 얼굴인식</li>
<li><a href="https://github.com/xinyu1205/recognize-anything">https://github.com/xinyu1205/recognize-anything</a> : 다양한 물체를 찾아주는 모델 (얼굴인식 검증 시 활용..)</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[2 Flutter | 알림 - Firebase]]></title>
            <link>https://velog.io/@cjmin-n/2-Flutter-%EC%95%8C%EB%A6%BC-Firebase</link>
            <guid>https://velog.io/@cjmin-n/2-Flutter-%EC%95%8C%EB%A6%BC-Firebase</guid>
            <pubDate>Wed, 18 Dec 2024 03:27:45 GMT</pubDate>
            <description><![CDATA[<ol>
<li>pubspec.yaml - dependencies 추가
```yaml</li>
</ol>
<ul>
<li>알림 
flutter_local_notifications: ^18.0.1</li>
<li>권한
permission_handler: ^11.3.1 </li>
<li>알람시간(한국시간-정확한 한국시간)
flutter_timezone: ^4.0.0<pre><code></code></pre></li>
</ul>
<ol start="2">
<li><p>앱아이콘 경로
android/app/src/main/res/drawble/
확장자명 png </p>
</li>
<li><p>AndroidManifest.xml 에 추가</p>
<pre><code class="language-xml"> &lt;uses-permission android:name=&quot;android.permission.VIBRATE&quot; /&gt;
 &lt;!-- 앱이 진동 기능을 사용할 수 있게 허용 --&gt;
 &lt;uses-permission android:name=&quot;android.permission.SYSTEM_ALERT_WINDOW&quot;/&gt;
 &lt;!-- 다른 앱 위에 알림창을 띄울 수 있게 허용 --&gt;
 &lt;uses-permission android:name=&quot;android.permission.SET_EXACT_ALARM&quot; /&gt;
 &lt;!-- 정확한 알람을 설정할 수 있게 허용 (Android 12 이상) --&gt;
 &lt;uses-permission android:name=&quot;android.permission.REQUEST_SCHEDULE_EXACT_ALARM&quot; /&gt;
 &lt;!-- 정확한 알람을 예약할 수 있는 권한을 요청 (Android 12 이상) 사용자에게 정확한 알람 권한을 요청하는 데 필요 --&gt;
 &lt;uses-permission android:name=&quot;com.android.alarm.permission.SET_ALARM&quot; /&gt;
 &lt;!-- 기본 알람 앱을 설정할 수 있게 허용 --&gt;
 &lt;uses-permission android:name=&quot;android.permission.SCHEDULE_EXACT_ALARM&quot; /&gt;
 &lt;!-- 정확한 알람을 예약할 수 있게 허용 (Android 12 이상)  실제 알람 예약 및 실행을 위해 필요--&gt;
 &lt;uses-permission android:name=&quot;android.permission.POST_NOTIFICATIONS&quot;/&gt;
 &lt;uses-permission android:name=&quot;android.permission.ACCESS_NOTIFICATION_POLICY&quot; /&gt;</code></pre>
</li>
</ol>
<ul>
<li>receiver : 알림을 트리거하는 역할 application 내부에 있어야함<pre><code class="language-xml">&lt;application&gt;
  &lt;receiver android:exported=&quot;false&quot; android:name=&quot;com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver&quot; /&gt;
&lt;/application&gt;</code></pre>
</li>
</ul>
<h3 id="firebase-console">Firebase console</h3>
<ol>
<li>앱등록 
패키지 이름만 입력
android/app/src/build.gradle - android/namespace</li>
<li>구성파일 다운로드
google-services.json을
android/app 하위 에넣음</li>
<li>groovy
1)루트 - &gt; settings.gradle /plugins 에 추가
id &#39;com.google.gms.google-services&#39; version &#39;4.4.2&#39; apply false
2)android/app/build.gralde / plugins 에 추가
id &#39;com.google.gms.google-services&#39;</li>
</ol>
<ul>
<li>pubspec.yaml - dependencies 추가<pre><code class="language-yaml">firebase_core: ^3.8.1
firebase_messaging: ^15.1.6
http: ^1.2.2  # http 패키지 추가
flutter_local_notifications: ^18.0.1
permission_handler: ^11.3.1</code></pre>
</li>
</ul>
<h4 id="android프로젝트-설정">android/프로젝트 설정</h4>
<ul>
<li><p>서비스 계정 / java / 새 비공개 키 생성</p>
</li>
<li><p><em>fir-test-app-91e51-firebase-adminsdk-g4aw0-45f9777b36.json*</em></p>
</li>
<li><blockquote>
<p>.gitignore 에 추가하기 !!</p>
</blockquote>
</li>
<li><p>자세히 알아보기
서버에 Firebase Admin SDK 추가 / SDK 추가 / 자바
dependencies 에 추가</p>
<pre><code class="language-gradle">dependencies {
implementation &#39;com.google.firebase:firebase-admin:9.4.1&#39;
}</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[1 Flutter | 연락처, 카메라]]></title>
            <link>https://velog.io/@cjmin-n/1-Flutter-%EC%97%B0%EB%9D%BD%EC%B2%98-%EC%B9%B4%EB%A9%94%EB%9D%BC</link>
            <guid>https://velog.io/@cjmin-n/1-Flutter-%EC%97%B0%EB%9D%BD%EC%B2%98-%EC%B9%B4%EB%A9%94%EB%9D%BC</guid>
            <pubDate>Mon, 16 Dec 2024 05:29:17 GMT</pubDate>
            <description><![CDATA[<p><a href="https://pub.dev/">pub.dev</a></p>
<h3 id="연락처">연락처</h3>
<ol>
<li>권한허용</li>
</ol>
<p><strong>permission_handler</strong>
-installing
dependencies:
  permission_handler: ^11.3.1
2. 연락처
<strong>flutter_contact</strong></p>
<ul>
<li>installing
dependencies:
flutter_contacts: ^1.1.9+2</li>
</ul>
<p>-&gt; yaml / dependencies 에 추가</p>
<pre><code class="language-xml">&lt;uses-permission android:name=&quot;android.permission.READ_CONTACTS&quot;/&gt;
&lt;uses-permission android:name=&quot;android.permission.WRITE_CONTACTS&quot;/&gt;</code></pre>
<p>-&gt; android\app\src\main\AndroidManifest.xml 에 추가</p>
<h3 id="카메라">카메라</h3>
<p><strong>image_picker</strong></p>
<ul>
<li>installing
dependencies:
image_picker: ^1.1.2</li>
</ul>
<p>-&gt; yaml / dependencies 에 추가</p>
<pre><code class="language-xml">&lt;uses-permission android:name=&quot;android.permission.CAMERA&quot; /&gt;
&lt;uses-permission android:name=&quot;android.permission.WRITE_EXTERNAL_STORAGE&quot;/&gt;
&lt;uses-permission android:name=&quot;android.permission.READ_EXTERNAL_STORAGE&quot;/&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[0 Flutter | 설정]]></title>
            <link>https://velog.io/@cjmin-n/0-%EC%84%A4%EC%A0%95-Flutter</link>
            <guid>https://velog.io/@cjmin-n/0-%EC%84%A4%EC%A0%95-Flutter</guid>
            <pubDate>Thu, 12 Dec 2024 06:22:54 GMT</pubDate>
            <description><![CDATA[<p><a href="https://docs.flutter.dev/get-started/install/windows/desktop">Flutter 다운로드</a>
Flutter windows - desktop 설치
압축풀어서 환경변수 path 에 등록</p>
<blockquote>
<p>flutter 버전 확인
C:\flutter_windows_3.24.5-stable\flutter\bin</p>
</blockquote>
<pre><code class="language-cmd">flutter --version</code></pre>
<p><a href="https://developer.android.com/studio?hl=ko&amp;_gl=1*125lyhr*_up*MQ..*_ga*MjA0Mjg0MDcxMi4xNzMzNzA4Njc2*_ga_6HH9YJMN9M*MTczMzcwODY3NS4xLjAuMTczMzcwODY3NS4wLjAuNDQ3MjI1NjAx">안드로이드 스튜디오 다운로드</a></p>
<ol>
<li><p>More Actions -&gt; SDK manager - SDK tools
<img src="https://velog.velcdn.com/images/cjmin-n/post/ad2457e7-a44c-4dac-a3b6-be5c83853fbe/image.png" alt=""></p>
</li>
<li><p>More Actions -&gt; Virtual Device Manager</p>
</li>
</ol>
<p><strong>Medium Phone - VanilaIceCream</strong></p>
<ol start="3">
<li>Plugins -&gt; Flutter / Dart install </li>
</ol>
<pre><code class="language-cmd">flutter doctor # 플러터 설치 시 필요한게 다 깔렸는지 확인
flutter doctor --android-licenses # 이후 전부 y</code></pre>
<ol start="4">
<li>Device Manger</li>
</ol>
<p>-&gt; Edit
-&gt; Show Advanced Settings
-&gt; Emulated Performance - Boot option : Cold boot 더 빨리 됨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Netflix eureka]]></title>
            <link>https://velog.io/@cjmin-n/0-Netflix-eureka</link>
            <guid>https://velog.io/@cjmin-n/0-Netflix-eureka</guid>
            <pubDate>Fri, 06 Dec 2024 02:28:50 GMT</pubDate>
            <description><![CDATA[<ol>
<li>eureka-server
InteliJ project build
: Spring Web / Eureka Server</li>
</ol>
<p>어플리케이션에 @EnableEurekaServer추가</p>
<pre><code class="language-yml">server:
    port: 8761 #Eureka 기본 포트

spring:
    application:
        name: eureka-server # 서비스 간의 식별을 위한 이름

eureka:
    client:
        register-with-eureka: false # 단일 유레카서버 - false / 유레카 서버 증설 시 (트래픽이 늘어나면) true
        fetch-registry: false # 다른 서버와 동기화 하지 않음 (단일 서버) / 유레카서버끼리 통신을 하려면 true
    server:
        enable-self-preservation: false # 자기 보호 기능    
</code></pre>
<p>2.api-gateway</p>
<p>InteliJ project build
: Reactive Gateway / Eureak Discovery Client / Spring Boot Devtools</p>
<pre><code class="language-yml">server:
    port: 8080

spring:
    cloud:
        gateway:
            routes:
                - id: user-service
                  uri: lb://USER-SERVICE
                  predicates:
                    - Path=/user/**
                - id: order-service
                  uri: lb://ORDER-SERVICE
                  predicates:
                    - Path=/order/**</code></pre>
<p>Eureka server에 관한 내용은 bootstrap.yml 에서 설정해야한다.</p>
<pre><code class="language-yml">eureka:
    client:
        service-url:
            defaultZone: http://localhost:8761/eureka/
    instance:
        prefer-ip-address: true # DNS 없이 ip면 true
</code></pre>
<ol start="3">
<li>user-service</li>
</ol>
<p>InteliJ project build
: Eureak Discovery Client / Spring Web / Spring Boot Devtools</p>
<pre><code class="language-yml">server:
    port: 8081

spring:
    application:
        name: user-service # Gateway 에 설정한 이름이랑 같아야함

eureka:
    client:
        service-url:
            defaultZone: http://localhost:8761/eureka/ # 유레카서버 알려줌
</code></pre>
<ol start="4">
<li>order-service</li>
</ol>
<p>InteliJ project build
: Eureak Discovery Client / Spring Web</p>
<pre><code class="language-yml">server:
    port: 8082

spring:
    application:
        name: order-service

eureka:
    client:
        service-url:
            defaultZone: http://localhost:8761/eureka/</code></pre>
<ol start="5">
<li>order-service2
InteliJ project build
: Eureak Discovery Client / Spring Web</li>
</ol>
<pre><code class="language-yml">server:
    port: 8083

spring:
    application:
        name: order-service

eureka:
    client:
        service-url:
            defaultZone: http://localhost:8761/eureka/spring.application.name=order-service2
</code></pre>
<p>-&gt; 똑같은 이름의 서버를 띄우면 서로 번갈아가면서 동작한다.</p>
<p>** 실행순서 - eureka server 가 제일 먼저 실행되야한다.
-&gt; user-service / order-service 프로젝트 내에 컨트롤러 만들어서 gateway 포트로 접속해서 요청 전송되는지 확인</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1 Jenkins | HTTPS 연결]]></title>
            <link>https://velog.io/@cjmin-n/1</link>
            <guid>https://velog.io/@cjmin-n/1</guid>
            <pubDate>Thu, 05 Dec 2024 07:50:02 GMT</pubDate>
            <description><![CDATA[<ol>
<li>젠킨스 를 통해서 백엔드 도커이미지를 만들고 도커컨테이너로 띄움 (배포 자동화를 위해)</li>
<li>만든 도커 컨테이너를 포트 포워딩함 (내부:8082 / 외부:5555)</li>
<li>가비아에서 도메인 구매</li>
<li>가비아에서 DNS 관리</li>
</ol>
<ul>
<li>타입 A에 포트포워딩한 내 외부아이피</li>
<li>ZERO SSL서 SSL 발급받고 CNAME 타입 입력해줌</li>
</ul>
<ol start="5">
<li>ZERO SSL 에서 zip 파일 (crt, key) 받아서 crt2개를 pem 으로 합쳐서 그거를 p12로 변환</li>
</ol>
<ul>
<li>SSL 인증서 Generate jks(Keystore) from .crt and .key ref: 
<a href="https://velog.io/@yebali/Linux-Generate-jksKeystore-from-.crt-and-.key">https://velog.io/@yebali/Linux-Generate-jksKeystore-from-.crt-and-.key</a> (명령어 cat 대신 type 씀)</li>
<li>OpenSSL 설치 ref: <a href="https://bskwak.tistory.com/182">https://bskwak.tistory.com/182</a> </li>
<li>OpenSSL 환경변수 설치 ref: <a href="https://san91.tistory.com/12">https://san91.tistory.com/12</a></li>
</ul>
<ol start="6">
<li><p>인텔리제이 resources/ 에 p12 파일 넣고 yml 작성</p>
<pre><code class="language-yml">server:
 ssl:
     enabled: true
     key-store: classpath:certificate.p12
     key-store-password: 1234
     key-store-type: PKCS12</code></pre>
</li>
<li><p>@Configration 만들어서 http 요청으로 온거를 https 로 보내줌</p>
</li>
<li><p>Corsconfig -&gt; 프론트 서버 연결</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[0 Jenkins | 설정]]></title>
            <link>https://velog.io/@cjmin-n/0-%EC%84%A4%EC%A0%95-%EC%A0%A0%ED%82%A8%EC%8A%A4</link>
            <guid>https://velog.io/@cjmin-n/0-%EC%84%A4%EC%A0%95-%EC%A0%A0%ED%82%A8%EC%8A%A4</guid>
            <pubDate>Wed, 04 Dec 2024 05:40:02 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-cmd">C:\Users\803-01&gt;docker pull jenkins/jenkins:jdk17
#젠킨스가 루트 권한 주고 도커 소켓을 갖게 하겠다. -&gt; 도커와 젠킨스가 서로 모르는 상태
C:\Users\803-01&gt;docker run -d -p 8080:8080 -p 50001:50001 -u root --privileged -v //var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker --name jenkins jenkins/jenkins:jdk17</code></pre>
<p><a href="http://localhost:8080/">localhost</a>
도커컨테이너로 젠킨스 이미지를 띄워서
로컬호스트로 접속해서 젠킨스 컨테이너의 비밀번호 입력 후 왼쪽 기본 설정 설치</p>
<ul>
<li><p>계정만들기
계정명: jenkinstest / 이름 : jenkins / 메일(gmail) / 비번</p>
</li>
<li><p>인텔리제이 프로젝트 의존성 spring web / Springboot devtools / mySql / jpa</p>
</li>
</ul>
<pre><code class="language-docker">FROM eclipse-temurin:17
RUN mkdir /opt/app
COPY build/libs/jenkins_test_app-0.0.1-SNAPSHOT.jar /opt/app/test_app.jar
CMD [&quot;java&quot;, &quot;-jar&quot;, &quot;/opt/app/test_app.jar&quot;]
EXPOSE 8082</code></pre>
<ul>
<li><p>github settings
webhook - Payload URL (젠킨스 주소)</p>
</li>
<li><blockquote>
<p><a href="http://112.221.66.174:50001//github-webhook/">http://112.221.66.174:50001//github-webhook/</a></p>
</blockquote>
</li>
<li><p>github가 젠킨스 서버를 찾을 수 있게 젠킨스서버를 포트포워딩해줌</p>
</li>
</ul>
<h3 id="로컬로-띄운-젠킨스-페이지">로컬로 띄운 젠킨스 페이지</h3>
<ul>
<li><p>DashBoard -&gt; Jenkins 관리-&gt; Plugins -&gt; Avaliable plugins -&gt; docker / docker pipeline</p>
</li>
<li><p>DashBoard -&gt; Tools -&gt; 
  Gradle (Add gradle - name:gradle)
  Docker (Add docker - name:docker / Install automatically/ Add Installer-&gt; download for docker.com)</p>
</li>
<li><p>DashBoard -&gt; Credentials -&gt; global -&gt; add Credentials</p>
</li>
</ul>
<ol>
<li>kind: secret file / application-db.yml 첨부 / id 파일명과 똑같이 ! -&gt; id 기준으로 찾음</li>
<li>id:dockerhub_id / Username/password-&gt; dockerhub 아이디 비번</li>
<li>Username:github 아이디/password:github 토큰 / id:jeongmin_github_token<ul>
<li>Github Settings - Developer Settings - Tokens 발급</li>
</ul>
</li>
</ol>
<ul>
<li>DasheBoard - new Item - pipeline</li>
<li>General</li>
</ul>
<ol>
<li>설명: 해당 파이프라인에 대한 설명</li>
<li>Do not allow concurrent builds : 순차적으로 빌드</li>
<li>Do not allow the pipeline to resume if the controller restarts : 중단된 위치에서 재개하지 않게 -&gt; 처음부터 다시 시작</li>
<li>github project : 만든 깃헙 레파지토리 주소</li>
<li>Pipeline speed/durability override : 속도 최적화 (빠르게하면 문제 시 대처를 못함)</li>
<li>Throttle builds : 빌드의 병렬 실행 (한번에 여러개 실행하지 못하도록 / 1로 해두면 한개만 가능)</li>
<li>오래된 빌드 삭제 : 빌드 이력 유지 기간(일) 15 /보관할 최대갯수 7</li>
<li>Build Trigger : GitHub hook trigger for GITScm polling (깃허브에 등록한 웹훅)</li>
</ol>
<ul>
<li>Pipeline</li>
</ul>
<h3 id="pipeline">Pipeline</h3>
<pre><code class="language-script">

pipeline{
    agent any 

    environment{
        DOCKER_REGISTRY = &#39;docker.io&#39;
        DOCKER_IMAGE_NAME = &#39;jeongmin1213/jenkins-test:latest&#39;
        DOCKER_CONTAINER_NAME = &#39;jenkins-test-container&#39;
        DOCKER_CREDENTIALS_ID = &#39;dockerhub_id&#39;
    }

    tools{
        gradle &#39;gradle&#39;
        dockerTool &#39;docker&#39;
    }

    stages{

        stage(&#39;Github Clone&#39;){
            steps{
                git branch: &#39;main&#39;,
                url: &#39;https://github.com/cjmin-n/jenkins-test-app.git&#39;,
                credentialsId: &#39;jeongmin_github_token&#39;
            }
        }

        stage(&#39;Download secret.yml File&#39;){
            steps{
                withCredentials([file(credentialsId:&#39;application-db.yml&#39;, variable:&#39;dbConfigFile&#39;)]){
                    script{
                        sh &quot;cp $dbConfigFile src/main/resources/application-db.yml&quot;
                    }
                }
            }
        }

        stage(&#39;Build with Gradle&#39;){
            steps{
                sh &#39;gradle build&#39;
            }
        }

        stage(&#39;Docker Build and Push&#39;){
            steps{
                script{
                    withCredentials([usernamePassword(credentialsId:&quot;$DOCKER_CREDENTIALS_ID&quot;, usernameVariable: &#39;DOCKER_USERNAME&#39;, passwordVariable: &#39;DOCKER_PASSWORD&#39;)]){
                        sh &quot;docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD&quot;
                        sh &quot;docker build -t $DOCKER_IMAGE_NAME -f Dockerfile .&quot;
                        sh &quot;docker push $DOCKER_IMAGE_NAME&quot;
                    }
                }
            }
        }

        stage(&#39;Deploy Docker Container&#39;){
            steps{
                script{
                    sh &#39;&#39;&#39;
                    if [ &quot;$(docker ps -q -f name=$DOCKER_CONTAINER_NAME)&quot; ]; then
                        docker stop $DOCKER_CONTAINER_NAME
                        docker rm $DOCKER_CONTAINER_NAME
                    fi
                    &#39;&#39;&#39;

                    sh &quot;docker run -d -p 8082:8082 --name $DOCKER_CONTAINER_NAME -e ENV_VARIABLE=value $DOCKER_IMAGE_NAME&quot;
                }
            }
        }
    }

    post{
        always{
            sh &#39;docker logout&#39;
        }
    }

}</code></pre>
<ul>
<li>변수를 써야하는 경우 &quot;&quot; 사용</li>
<li>if문 쓰려면 sh &#39;&#39;&#39; &#39;&#39;&#39; 안에 사용하고 fi 로 마침</li>
<li>if문 [ ]에 처음,마지막에 띄워쓰기 필수</li>
<li>cp 는 copy</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[0 Docker | 설정]]></title>
            <link>https://velog.io/@cjmin-n/0-%EC%84%A4%EC%A0%95-Docker</link>
            <guid>https://velog.io/@cjmin-n/0-%EC%84%A4%EC%A0%95-Docker</guid>
            <pubDate>Tue, 03 Dec 2024 02:13:18 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.docker.com/products/docker-desktop/">Docker Destop</a>
-&gt; Download for windows - AMD64 설치</p>
<ul>
<li>도커 버전 확인<pre><code class="language-cmd">C:\Users\803-01&gt;docker --version
Docker version 27.3.1, build ce12230</code></pre>
</li>
<li>환경변수 path 에서 docker 추가되었는지 확인</li>
<li>Springboot 프로젝트 생성 후 Dockerfile 만들기</li>
</ul>
<pre><code class="language-dockerfile">FROM eclipse-temurin:17
RUN mkdir /opt/app
COPY build/libs/docker-test-app-0.0.1-SNAPSHOT.jar /opt/app/cjmin_app.jar
CMD [&quot;java&quot;, &quot;-jar&quot;, &quot;/opt/app/cjmin_app.jar&quot;]
EXPOSE 8080
#포트명은 명시적인 것이고 강제성을 가지지않음</code></pre>
<p>docker hub 에서  본인이 정한 jar 파일명으로 create repository</p>
<pre><code class="language-cmd">C:\20240801-gangnam\docker\docker-test-app&gt;gradlew build
C:\20240801-gangnam\docker\docker-test-app&gt;docker build -t cjmin_app:latest .
C:\20240801-gangnam\docker\docker-test-app&gt;docker images
C:\20240801-gangnam\docker\docker-test-app&gt;docker run -d -p 8080:8080 --name cjmin_container cjmin_app:latest
# -d : 백그라운드에서 실행(없으면 cmd에서 바로 실행됨
# -p : 포트 설정

#도커로그확인
C:\20240801-gangnam\docker\docker-test-app&gt;docker logs cjmin_container</code></pre>
<p>docker desktop 에서 Containers
-&gt; Files/opt/app/cjmin_app.jar 확인</p>
<ul>
<li>컨테이너는 가상의 공간에 있어서 외부와 소통불가...</li>
</ul>
<pre><code class="language-cmd">C:\20240801-gangnam\docker\docker-test-app&gt;docker login
#태그이름지어주기
C:\20240801-gangnam\docker\docker-test-app&gt;docker tag cjmin_app:latest jeongmin1213/cjmin_app:latest
C:\20240801-gangnam\docker\docker-test-app&gt;docker push jeongmin1213/cjmin_app:latest

#다른 사람꺼 찾기
C:\20240801-gangnam\docker\docker-test-app&gt;docker search jeongmin1213
#다른사람꺼 풀 받기
C:\20240801-gangnam\docker\docker-test-app&gt;docker pull seoyeonon/seoprog_app:latest
#다른사람꺼 띄우기 (포트번호 알아야함 - 예시)
C:\20240801-gangnam\docker\docker-test-app&gt;docker run -d -p 8081:8081 --name seoyeon_container seoyeonon/seoprog_app:latest</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[0 SpringBoot | 설정]]></title>
            <link>https://velog.io/@cjmin-n/0-SpringBoot-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@cjmin-n/0-SpringBoot-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Wed, 23 Oct 2024 02:05:56 GMT</pubDate>
            <description><![CDATA[<p><strong>프로젝트 생성</strong>
Generator : springBoot
Language: Java
Type : Gradle-Groovy</p>
<p>Dependencies</p>
<ul>
<li>Spring Boot DevTools</li>
<li>Spring Web</li>
<li>Thymeleaf : java 와 html 연결할 때 필요</li>
</ul>
<p><strong>프로젝트 경로</strong></p>
<ul>
<li>java
Application 건드리지 않음</li>
<li>resources
application.properties : 설정 정보 : db 연결 정보들
static : 정적 파일(html,css,img 등) -&gt; index.html 실행 시 첫 페이지
templates: index외 html파일들 - view resolver가 templates부터 찾음</li>
</ul>
<p><strong>메인 메소드 실행</strong>
localhost:8080 : 톰캣 기본 서버</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[6 Servlet | forward/redirect]]></title>
            <link>https://velog.io/@cjmin-n/6-Servlet-forwardredirect</link>
            <guid>https://velog.io/@cjmin-n/6-Servlet-forwardredirect</guid>
            <pubDate>Thu, 17 Oct 2024 08:59:11 GMT</pubDate>
            <description><![CDATA[<h2 id="1-forward">1. forward</h2>
<p>기본적으로 변수의 기본 스코프는 메소드 이기 때문에
다른 페이지(서블릿) 로 데이터를 공유할 수 없다.
하지만 forward 방식은 request 와 response 를 포함하여 위임하므로
request 에 정보를  저장하여 forward 하면 위임받은 서블릿에서도
위임한 서블릿의 정보를 받아올 수 있다.
forward 받은 서블릿의 존재를 클라이언트가 알 필요가 없기 때문에
url 자체는 변경되지 않는다.
forward 방식의 또 다른 특징은 요청 시 서버로 전송한 데이터가
남아있는 상태로 새로고침(재요청)을 하면 동일한 요청을 반복하여
데이터베이스에 insert 하는 등의 행위 등이 중복된 행이 삽입될 가능성이 있다.
따라서 그런 경우는 다른 페이지 전환 방식인 redirect 를 이용한다.</p>
<pre><code class="language-java">package com.ohgiraffers.section01.forward;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet(&quot;/forward&quot;)
public class ReceiveInformationServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        String userId = req.getParameter(&quot;userId&quot;);
        String password = req.getParameter(&quot;password&quot;);

        System.out.println(&quot;userId = &quot; + userId);
        System.out.println(&quot;password = &quot; + password);

        // 다른 서블릿으로 데이터를 request 의 setAttribute 로 전달한다.
        req.setAttribute(&quot;userId&quot;, userId);

        // print 라는 경로로 요청을 넘기기 위한 객체 생성
        // 이 경로는 서블릿이나 JSP 모두 가능
        RequestDispatcher rd = req.getRequestDispatcher(&quot;print&quot;);

        rd.forward(req, resp);


    }
}</code></pre>
<p>위의 /forward 서블릿에서 속성을 포워드해서 /print에서 뿌려준다.</p>
<pre><code class="language-java">package com.ohgiraffers.section01.forward;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(&quot;/print&quot;)
public class PrintLoginSuccessServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        String userId = (String)req.getAttribute(&quot;userId&quot;);
        System.out.println(userId);

        String userId2 = req.getParameter(&quot;userId&quot;);
        System.out.println(userId2);

        // 응답에 필요한 데이터가 준비되면 동적인 웹 페이지를 생성한다.
        StringBuilder responseText = new StringBuilder();
        responseText.append(&quot;&lt;!DOCTYPE html&gt;\n&quot;)
                .append(&quot;&lt;html&gt;\n&quot;)
                .append(&quot;&lt;head&gt;\n&quot;)
                .append(&quot;&lt;/head&gt;\n&quot;)
                .append(&quot;&lt;body&gt;\n&quot;)
                .append(&quot;&lt;h3 align=\&quot;center\&quot;&gt;&quot;)
                .append(userId)
                .append(&quot;님 환영합니다. &lt;/h3&gt;\n&quot;)
                .append(&quot;&lt;/body&gt;\n&quot;)
                .append(&quot;&lt;/html&gt;&quot;);

        resp.setContentType(&quot;text/html; charset=UTF-8&quot;);

        PrintWriter out = resp.getWriter();
        out.print(responseText);
        out.flush();
        out.close();

    }
}</code></pre>
<h2 id="2-redirect">2. redirect</h2>
<p>redirect 하면 url 이 재작성되어 새로고침할 때 redirect 된 페이지에 대한 요청을 반복한다.
즉, 이전 요청에 포함된 정보는 남아있지 않고, url 이 변경되는 것이 redirect 의 특징이다.
첫 요청 시의 request 와 현재 redirect 된 페이지의 request 는 서로 다른 객체이므로
redirect 를 쓰면 이전 서블릿의 값을 공유해서 사용할 수 없다.
http 요청은 요청 시 잠시 connection 을 맺고 응답 시에도 잠시 connection 을 맺으며
요청 단위 당 request 객체는 한 개만 생성된다.</p>
<ul>
<li>다른 사이트로 리다이렉트<pre><code class="language-java">package com.ohgiraffers.section01.othersite;
</code></pre>
</li>
</ul>
<p>import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;</p>
<p>import java.io.IOException;</p>
<p>@WebServlet(&quot;/othersite&quot;)
public class OtherSiteRedirectServlet extends HttpServlet {</p>
<pre><code>@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println(&quot;get 요청 후 naver 로 redirect&quot;);

    resp.sendRedirect(&quot;https://www.naver.com&quot;);
}</code></pre><p>}</p>
<pre><code>
- 다른 서블릿으로 리다이렉트
``` java
package com.ohgiraffers.section02.otherservlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet(&quot;/otherservlet&quot;)
public class OtherServletRedirectTest extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        System.out.println(&quot;get 요청 정상 수락&quot;);
        System.out.println(resp); // org.apache.catalina.connector.ResponseFacade@5c5717be
        resp.sendRedirect(&quot;redirect&quot;);
    }
}</code></pre><ul>
<li>otherservlet 서블릿에서 redirect 서블릿으로 리다이렉트</li>
</ul>
<pre><code class="language-java">package com.ohgiraffers.section02.otherservlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(&quot;/redirect&quot;)
public class RedirectServlet extends HttpServlet {


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        System.out.println(&quot;이 서블릿으로 redirect 성공&quot;);
        System.out.println(resp); // org.apache.catalina.connector.ResponseFacade@70daeadb

        resp.setContentType(&quot;text/html; charset=utf-8&quot;);
        PrintWriter out = resp.getWriter();
        out.print(&quot;&lt;h1&gt;이 서블릿으로 redirect 성공&lt;/h1&gt;&quot;);
        out.flush();
        out.close();


    }

}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[5 Servlet | ExceptionHandler]]></title>
            <link>https://velog.io/@cjmin-n/5-Servlet-ExceptionHandler</link>
            <guid>https://velog.io/@cjmin-n/5-Servlet-ExceptionHandler</guid>
            <pubDate>Thu, 17 Oct 2024 08:51:08 GMT</pubDate>
            <description><![CDATA[<ul>
<li>404 Error<pre><code class="language-java">package com.ohgiraffers.section01.exception;
</code></pre>
</li>
</ul>
<p>import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;</p>
<p>import java.io.IOException;</p>
<p>@WebServlet(&quot;/show404error&quot;)
public class Show404ErrorServlet extends HttpServlet {</p>
<pre><code>@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.sendError(404, &quot;페이지를 찾을 수 없습니당&quot;);
}</code></pre><p>}</p>
<pre><code>- 500 Error
``` java
package com.ohgiraffers.section01.exception;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet(&quot;/show500error&quot;)
public class Show500ErrorServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.sendError(500, &quot;500번 에러는 누구 잘못? 개발자!&quot;);
    }
}</code></pre><ul>
<li>Exception Handler</li>
</ul>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;web-app xmlns=&quot;https://jakarta.ee/xml/ns/jakartaee&quot;
         xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         xsi:schemaLocation=&quot;https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd&quot;
         version=&quot;6.0&quot;&gt;

    &lt;error-page&gt;
        &lt;error-code&gt;404&lt;/error-code&gt;
        &lt;location&gt;/showErrorPage&lt;/location&gt;
    &lt;/error-page&gt;
    &lt;error-page&gt;
        &lt;error-code&gt;500&lt;/error-code&gt;
        &lt;location&gt;/showErrorPage&lt;/location&gt;
    &lt;/error-page&gt;
&lt;/web-app&gt;</code></pre>
<ul>
<li>xml 에 error page code와 location 정의해서 하기 서블릿에서 처리</li>
</ul>
<pre><code class="language-java">package com.ohgiraffers.section01.exception;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletMapping;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

@WebServlet(&quot;/showErrorPage&quot;)
public class ExceptionHandlerServlet extends HttpServlet {


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        Enumeration&lt;String&gt; attrName = req.getAttributeNames();
        while(attrName.hasMoreElements()){
            System.out.println(attrName.nextElement());
        }
        /*
        jakarta.servlet.forward.request_uri
        jakarta.servlet.forward.context_path
        jakarta.servlet.forward.servlet_path
        jakarta.servlet.forward.mapping
        jakarta.servlet.error.message
        jakarta.servlet.error.servlet_name
        jakarta.servlet.error.request_uri
        jakarta.servlet.error.status_code
        * */
        // forward : 요청을 다른 서블릿으로 넘겨서 처리
        // - url 주소창은 바뀌지 않음

        // request_uri : 요청이 포워딩 되었을 때 원래 요청의 uri
        String forwardRequestURI = (String)req.getAttribute(&quot;jakarta.servlet.forward.request_uri&quot;);

        // context_path : 요청이 포워딩 되었을 때 원래 요청의 컨텍스트 경로
        String contextPath = (String)req.getAttribute(&quot;jakarta.servlet.forward.context_path&quot;);

        // 어떤 서블릿에 의해 요청이 처리되었는지
        HttpServletMapping mapping = req.getHttpServletMapping();

        // status_code : 요청 처리 중 발생한 오류의 상태 코드
        Integer statusCode = (Integer)req.getAttribute(&quot;jakarta.servlet.error.status_code&quot;);

        // message : 요청 처리 중 발생한 오류 메시지
        String message = (String)req.getAttribute(&quot;jakarta.servlet.error.message&quot;);

        // servlet_name : 오류를 발생시킨 서블릿의 이름
        String servletName = (String)req.getAttribute(&quot;jakarta.servlet.error.servlet_name&quot;);

        System.out.println(forwardRequestURI); // /show404error
        System.out.println(contextPath); //
        System.out.println(mapping); // org.apache.catalina.core.ApplicationMapping$MappingImpl@6157ccb4
        System.out.println(statusCode); // 404
        System.out.println(message); // 페이지를 찾을 수 없습니당
        System.out.println(servletName); // com.ohgiraffers.section01.exception.Show404ErrorServlet
        System.out.println((String)req.getAttribute(&quot;jakarta.servlet.forward.servlet_path&quot;)); // /show404error
        System.out.println((String)req.getAttribute(&quot;jakarta.servlet.error.request_uri&quot;)); // /show404error

        StringBuilder errorPage = new StringBuilder();
        errorPage.append(&quot;&lt;!doctype html&gt;\n&quot;)
                .append(&quot;&lt;html&gt;\n&quot;)
                .append(&quot;&lt;head&gt;\n&quot;)
                .append(&quot;&lt;/head&gt;\n&quot;)
                .append(&quot;&lt;body&gt;\n&quot;)
                .append(&quot;&lt;h1&gt;&quot;)
                .append(statusCode)
                .append(&quot; - &quot;)
                .append(message)
                .append(&quot;&lt;/h1&gt;\n&quot;)
                .append(&quot;&lt;/body&gt;\n&quot;)
                .append(&quot;&lt;/html&gt;&quot;);
        resp.setContentType(&quot;text/html; charset=UTF-8&quot;);
        PrintWriter out = resp.getWriter();

        out.print(errorPage);
        out.flush();
        out.close();
    }

}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[4 Servlet | request/response]]></title>
            <link>https://velog.io/@cjmin-n/4-Servlet-requestresponse</link>
            <guid>https://velog.io/@cjmin-n/4-Servlet-requestresponse</guid>
            <pubDate>Thu, 17 Oct 2024 08:45:05 GMT</pubDate>
            <description><![CDATA[<h2 id="1-request">1. request</h2>
<p>헤더의 종류는 전통적으로 4가지 카테고리로 구분된다.</p>
<ol>
<li>General header : 요청 및 응답 모두 적용되지만 body 와는 관련 없는 헤더</li>
<li>Request header : fetch 될 리소스나 클라이언트 자체에 대한 상세 정보를 포함하는 헤더</li>
<li>Response header : 위치나 서버 자체와 같은 응답에 대한 부가적인 정보를 갖는 헤더</li>
<li>Entity header : 컨텐츠 길이나 엔티티 body 에 대한 상세 정보를 포함하는 헤더</li>
</ol>
<p>출력 시 나오는 헤더는 요청헤더(Request header) 이다.</p>
<pre><code class="language-java">package com.ohgiraffers.section01.headers;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Enumeration;

@WebServlet(&quot;/headers&quot;)
public class RequestHeaderPrintServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        Enumeration&lt;String&gt; headerNames = req.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            System.out.println(headerNames.nextElement());
        }

        // accept : 요청을 보낼 때 서버에게 요청할 응답 타입 명시
        System.out.println(&quot;accept : &quot; + req.getHeader(&quot;accept&quot;));
        // text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7

        // accept-encoding : 응답 시 원하는 인코딩 방식
        System.out.println(&quot;accpet-encoding : &quot; + req.getHeader(&quot;accpet-encoding&quot;));
        // null

        // accept-language : 응답 시 원하는 언어
        System.out.println(&quot;accept-language : &quot; + req.getHeader(&quot;accept-language&quot;));
        // ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7

        // connection : HTTP 통신이 완료된 후에 네트워크 접속을 유지할지 결정(기본값 - keep-alive = 연결유지)
        System.out.println(&quot;connection : &quot; + req.getHeader(&quot;connection&quot;));
        // keep-alive

        // host : 서버의 도메인 네임과 현재 Listening 중인 TCP 포트 지정
        System.out.println(&quot;host : &quot; + req.getHeader(&quot;host&quot;));
        // localhost:8080

        // referer : 이 페이지 이전에 대한 주소
        System.out.println(&quot;referer : &quot; + req.getHeader(&quot;referer&quot;));
        //  http://localhost:8080/

        // sec-fetch-dest : 요청 대상
        System.out.println(&quot;sec-fetch-dest : &quot; + req.getHeader(&quot;sec-fetch-dest&quot;));
        // document

        // sec-fetch-mode : 요청 모드
        System.out.println(&quot;sec-fetch-mode : &quot; + req.getHeader(&quot;sec-fetch-mode&quot;));
        // navigate

        // sec-fetch-user : 사용자가 시작한 요청일 때만 보내짐
        System.out.println(&quot;sec-fetch-user : &quot; + req.getHeader(&quot;sec-fetch-user&quot;));
        // ?1

        // cache-control : 캐시 설정
        System.out.println(&quot;cache-control : &quot; + req.getHeader(&quot;cache-control&quot;));
        // null

        // user-agent : 현재 사용자가 어떤 클라이언트를 이용해 보낸 요청인지 명시
        System.out.println(&quot;user-agent : &quot; + req.getHeader(&quot;user-agent&quot;));
        // Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36

    }
}</code></pre>
<h2 id="2-response">2. response</h2>
<p>서블릿이 하는 역할은 크게 3가지 이다.</p>
<ol>
<li>요청 받기 - HTTP method get/post 등 요청에 따라
parameter 로 전달받은 데이터를 꺼내올 수 있다.</li>
<li>비즈니스 로직 처리 - DB 접속과 CRUD 에 대한 로직 처리</li>
<li>응답하기 - 문자열로 동적인 웹페이지를 만들어 내보낸다.</li>
</ol>
<pre><code class="language-java">package com.ohgiraffers.section01.response;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(&quot;/response&quot;)
public class ResponseTestServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        // 문자열을 이용해 사용자에게 내보낼 페이지를 작성한다.
        StringBuilder responseBuilder = new StringBuilder();
        responseBuilder.append(&quot;&lt;!DOCTYPE html&gt;\n&quot;)
                .append(&quot;&lt;html&gt;\n&quot;)
                .append(&quot;&lt;head&gt;\n&quot;)
                .append(&quot;&lt;/head&gt;\n&quot;)
                .append(&quot;&lt;body&gt;\n&quot;)
                .append(&quot;&lt;h1&gt;안녕 servlet response&lt;/h1&gt;\n&quot;)
                .append(&quot;&lt;/body&gt;\n&quot;)
                .append(&quot;&lt;/html&gt;&quot;);

        // 브라우저로 내보낼 데이터의 타입을 설정

        // 기본적으로 content-type의 기본값은 null 이다.
        System.out.println(&quot;default response type : &quot; + resp.getContentType());
        // text/plain 으로 설정하면 html 태그를 단순 문자열로 인식한다.
        // resp.setContentType(&quot;text/html&quot;);

        // 브라우저로 내보낼 데이터의 인코딩 방식을 설정
        // 별도로 인코딩을 지정해 주지 않으면 기본 설정된 방식을 따른다.(UTF-8)
        System.out.println(&quot;default response encoding : &quot; + resp.getCharacterEncoding());

        // response.setCharacterEncoding(&quot;UTF-8&quot;);

        // 두 설정을 한 번에 할 수 있다.
        resp.setContentType(&quot;text/html; charset=UTF-8&quot;);

        // 서버가 클라이언트에게 보낼 출력용 클래스
        // 이 객체를 사용해 클라이언트로 데이터를 전송할 수 있다.
        // 사용자 브라우저에 응답하기 위해서는 HttpServletResponse 의
        // getWriter() method 로 PrintWriter 인스턴스를 반환받는다.
        PrintWriter out = resp.getWriter();

        // 데이터를 출력한다.
        out.print(responseBuilder);
        // 버퍼 - 데이터를 임시로 저장하는 공간입니다.

        // 버퍼에 잔류한 데이터를 강제로 밀어넣는다.
        // 버퍼에 남아 있는 데이터를 강제로 클라이언트에게 전송
        out.flush();

        // 닫아준다.
        out.close();
    }
}</code></pre>
<ul>
<li>Response Header<pre><code class="language-java">package com.ohgiraffers.section02.headers;
</code></pre>
</li>
</ul>
<p>import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;</p>
<p>import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Iterator;</p>
<p>@WebServlet(&quot;/headers&quot;)
public class ResponseHeaderPrintServlet extends HttpServlet {</p>
<pre><code>@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.setContentType(&quot;text/html; charset=UTF-8&quot;);

    PrintWriter out = resp.getWriter();

    long currentTime = System.currentTimeMillis();
    // currentTimeMillis() 함수는 1970년 1월 1일 0시를 기준으로 현재까지 경과한 시간을 1000분의 1초(밀리초)로 반환한다.
    out.print(&quot;&lt;h1&gt;&quot; + currentTime + &quot;&lt;/h1&gt;&quot;);
    out.close();

    Collection&lt;String&gt; responseHeaders = resp.getHeaderNames();
    Iterator&lt;String&gt; iter = responseHeaders.iterator();

    while(iter.hasNext()) {
        String headerName = iter.next();
        System.out.println(headerName + &quot; : &quot; + resp.getHeader(headerName));
        /*
        Content-Type : text/html;charset=UTF-8
        Content-Length : 22
        Date : Thu, 17 Oct 2024 08:41:39 GMT
        Keep-Alive : timeout=20
        Connection : keep-alive
        * */
    }
}</code></pre><p>}</p>
<pre><code>
- status
```java
package com.ohgiraffers.section03.status;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet(&quot;/status&quot;)
public class StatusCodeServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 특정 상황에 에러를 만들어 보내준다.
        // resp.sendError(404, &quot;없는 페이지 입니다. 경로를 확인 해주세요.&quot;); // sendError(에러코드, 메시지)

        // 404 : 클라이언트의 잘못된 요청으로 나타난 에러
        resp.sendError(500, &quot;서버 내부 오류입니다. 서버 오류는 개발자의 잘못이고, 개발자는 여러분입니다.&quot;);
        // 500 : 서버 에러
    }
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[3 Servlet | parameter]]></title>
            <link>https://velog.io/@cjmin-n/3-Servlet-parameter</link>
            <guid>https://velog.io/@cjmin-n/3-Servlet-parameter</guid>
            <pubDate>Thu, 17 Oct 2024 08:34:23 GMT</pubDate>
            <description><![CDATA[<h2 id="1-parameter-메소드">1. parameter 메소드</h2>
<ul>
<li>getParameter() : 요청 시 전달한 값을 getParameter() 로 추출 / input의 name</li>
<li>getParameterMap() : 모든 데이터를 key, value 형태로 일괄 반환<pre><code class="language-java">package com.ohgiraffers.section01.querystring;
</code></pre>
</li>
</ul>
<p>import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;</p>
<p>import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;</p>
<p>@WebServlet(&quot;/querystring&quot;)
public class QueryStringTestServlet extends HttpServlet {</p>
<pre><code>@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // service 로 부터 전달받은 HttpServletRequest 는
    // 요청 시 전달한 값을 getParameter() 로 추출할 수 있다..
    // 이 때 인자로 input 태그에 지정한 name 속성의 값을 문자열 형태로 전달하면 된다..

    String name = req.getParameter(&quot;name&quot;);
    System.out.println(&quot;이름 : &quot; + name);

    int age = Integer.parseInt(req.getParameter(&quot;age&quot;));
    System.out.println(&quot;나이 : &quot; + age);

    java.sql.Date birthday = java.sql.Date.valueOf(req.getParameter(&quot;birthday&quot;));
    System.out.println(&quot;생일 : &quot; + birthday); // 2024-10-13

    // radio 태그는 여러 값 중 선택된 하나의 값만 전달된다.
    String gender = req.getParameter(&quot;gender&quot;);
    System.out.println(&quot;성별 : &quot; + gender);

    // selectbox 도 동일하다.
    String national = req.getParameter(&quot;national&quot;);
    System.out.println(&quot;국적 : &quot; + national);

    // checkbox 는 다중으로 입력을 받을 수 있어, 선택된 값이 문자열 배열로 전달된다.
    System.out.print(&quot;취미 : &quot;);
    String[] hobbies = req.getParameterValues(&quot;hobbies&quot;);
    String[] hobbies2 = req.getParameter(&quot;hobbies&quot;).split(&quot;, &quot;);

    for(String hobby : hobbies){
        System.out.print(hobby + &quot; &quot;);
    }
    System.out.println();

    /*
    * getParameterMap() - 모든 데이터를 key, value 형태로 일괄 반환
    * */
    Map&lt;String, String[]&gt; requestMap = req.getParameterMap();

    Iterator&lt;String&gt; iterator = requestMap.keySet().iterator();

    /*while(iterator.hasNext()){
        String key = iterator.next();
        String[] values = requestMap.get(key);
        System.out.print(key + &quot; : &quot;);
        for (int i = 0; i &lt; values.length; i++) {
            System.out.print(values[i] );
        }
        System.out.println();
    }*/

    while(iterator.hasNext()){
        String key = iterator.next();
        String[] values = requestMap.get(key);
        if(key.equals(&quot;name&quot;)){
            System.out.print(&quot;이름 : &quot;);
        }else if(key.equals(&quot;age&quot;)){
            System.out.print(&quot;나이 : &quot;);
        }else if(key.equals(&quot;birthday&quot;)){
            System.out.print(&quot;생일 : &quot;);
        }else if(key.equals(&quot;gender&quot;)){
            System.out.print(&quot;성별 : &quot;);
        }else if(key.equals(&quot;national&quot;)){
            System.out.print(&quot;국적 : &quot;);
        }else if(key.equals(&quot;hobbies&quot;)){
            System.out.print(&quot;취미 : &quot;);
        }

        System.out.println(Arrays.toString(values));


    }

}</code></pre><p>}</p>
<p>````</p>
<h2 id="--요청-하는-jsp">- 요청 하는 JSP</h2>
<pre><code class="language-jsp">&lt;%@ page contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Parameter&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Request Parameter&lt;/h1&gt;
&lt;h3&gt;GET 방식의 요청&lt;/h3&gt;
&lt;h4&gt;form태그를 이용한 get 방식 요청&lt;/h4&gt;
&lt;form action=&quot;querystring&quot; method=&quot;get&quot;&gt;
    &lt;label&gt;이름 : &lt;/label&gt;&lt;input type=&quot;text&quot; name=&quot;name&quot;&gt;
    &lt;br&gt;
    &lt;label&gt;나이 : &lt;/label&gt;&lt;input type=&quot;number&quot; name=&quot;age&quot;&gt;
    &lt;br&gt;
    &lt;label&gt;생일 : &lt;/label&gt;&lt;input type=&quot;date&quot; name=&quot;birthday&quot;&gt;
    &lt;br&gt;
    &lt;label&gt;성별 : &lt;/label&gt;
    &lt;input type=&quot;radio&quot; name=&quot;gender&quot; id=&quot;male&quot; value=&quot;M&quot;&gt;&lt;label for=&quot;male&quot;&gt;남자&lt;/label&gt;
    &lt;input type=&quot;radio&quot; name=&quot;gender&quot; id=&quot;female&quot; value=&quot;F&quot;&gt;&lt;label for=&quot;female&quot;&gt;여자&lt;/label&gt;
    &lt;br&gt;
    &lt;label&gt;국적 : &lt;/label&gt;
    &lt;select name=&quot;national&quot;&gt;
        &lt;option value=&quot;ko&quot;&gt;한국&lt;/option&gt;
        &lt;option value=&quot;ch&quot;&gt;중국&lt;/option&gt;
        &lt;option value=&quot;jp&quot;&gt;일본&lt;/option&gt;
        &lt;option value=&quot;etc&quot;&gt;기타&lt;/option&gt;
    &lt;/select&gt;
    &lt;br&gt;
    &lt;label&gt;취미 : &lt;/label&gt;
    &lt;input type=&quot;checkbox&quot; name=&quot;hobbies&quot; id=&quot;movie&quot; value=&quot;movie&quot;&gt;&lt;label for=&quot;movie&quot;&gt;영화&lt;/label&gt;
    &lt;input type=&quot;checkbox&quot; name=&quot;hobbies&quot; id=&quot;music&quot; value=&quot;music&quot;&gt;&lt;label for=&quot;music&quot;&gt;음악&lt;/label&gt;
    &lt;input type=&quot;checkbox&quot; name=&quot;hobbies&quot; id=&quot;sleep&quot; value=&quot;sleep&quot;&gt;&lt;label for=&quot;sleep&quot;&gt;취침&lt;/label&gt;
    &lt;br&gt;

    &lt;input type=&quot;submit&quot; value=&quot;GET 요청&quot;&gt;
&lt;/form&gt;

&lt;h4&gt;a태그의 href 속성에 직접 파라미터를 쿼리스트링 형태로 작성하여 get 방식 요청&lt;/h4&gt;
&lt;a href=&quot;querystring?name=홍길동&amp;age=20&amp;birthday=2021-01-08&amp;gender=M&amp;national=ko&amp;hobbies=movie&amp;hobbies=music&amp;hobbies=sleep&quot;&gt;쿼리스트링을
    이용한 값 전달&lt;/a&gt;

&lt;h4&gt;form태그를 이용한 post 방식 요청&lt;/h4&gt;
&lt;form action=&quot;formdata&quot; method=&quot;post&quot;&gt;
    &lt;label&gt;이름 : &lt;/label&gt;&lt;input type=&quot;text&quot; name=&quot;name&quot;&gt;
    &lt;br&gt;
    &lt;label&gt;나이 : &lt;/label&gt;&lt;input type=&quot;number&quot; name=&quot;age&quot;&gt;
    &lt;br&gt;
    &lt;label&gt;생일 : &lt;/label&gt;&lt;input type=&quot;date&quot; name=&quot;birthday&quot;&gt;
    &lt;br&gt;
    &lt;label&gt;성별 : &lt;/label&gt;
    &lt;input type=&quot;radio&quot; name=&quot;gender&quot; id=&quot;male2&quot; value=&quot;M&quot;&gt;&lt;label for=&quot;male2&quot;&gt;남자&lt;/label&gt;
    &lt;input type=&quot;radio&quot; name=&quot;gender&quot; id=&quot;female2&quot; value=&quot;F&quot;&gt;&lt;label for=&quot;female2&quot;&gt;여자&lt;/label&gt;
    &lt;br&gt;
    &lt;label&gt;국적 : &lt;/label&gt;
    &lt;select name=&quot;national&quot;&gt;
        &lt;option value=&quot;ko&quot;&gt;한국&lt;/option&gt;
        &lt;option value=&quot;ch&quot;&gt;중국&lt;/option&gt;
        &lt;option value=&quot;jp&quot;&gt;일본&lt;/option&gt;
        &lt;option value=&quot;etc&quot;&gt;기타&lt;/option&gt;
    &lt;/select&gt;
    &lt;br&gt;
    &lt;label&gt;취미 : &lt;/label&gt;
    &lt;input type=&quot;checkbox&quot; name=&quot;hobbies&quot; id=&quot;movie2&quot; value=&quot;movie&quot;&gt;&lt;label for=&quot;movie2&quot;&gt;영화&lt;/label&gt;
    &lt;input type=&quot;checkbox&quot; name=&quot;hobbies&quot; id=&quot;music2&quot; value=&quot;music&quot;&gt;&lt;label for=&quot;music2&quot;&gt;음악&lt;/label&gt;
    &lt;input type=&quot;checkbox&quot; name=&quot;hobbies&quot; id=&quot;sleep2&quot; value=&quot;sleep&quot;&gt;&lt;label for=&quot;sleep2&quot;&gt;취침&lt;/label&gt;
    &lt;br&gt;

    &lt;input type=&quot;submit&quot; value=&quot;POST 요청&quot;&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h2 id="2-formdata---post-방식">2. formdata - post 방식</h2>
<pre><code class="language-java">package com.ohgiraffers.section02.formdata;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

@WebServlet(&quot;/formdata&quot;)
public class FormDataTestServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 톰캣 서버 기본 인코딩 설정 방식
        System.out.println(req.getCharacterEncoding()); // UTF-8

        // 인코딩 방식 지정 가능
        req.setCharacterEncoding(&quot;UTF-8&quot;); 

        String name = req.getParameter(&quot;name&quot;);
        System.out.println(&quot;name = &quot; + name);
        // 인코딩을 제외한 값들은 GET 방식과 동일하게 꺼낼 수 있다.

        Map &lt;String, String[]&gt; requestMap = req.getParameterMap();
        Set&lt;String&gt; keySet = requestMap.keySet();
        Iterator&lt;String&gt; keyIter = keySet.iterator();

        while(keyIter.hasNext()){
            String key = keyIter.next();
            String[] value = requestMap.get(key);

            System.out.println(&quot;key : &quot; + key);
            for (int i = 0; i &lt; value.length; i++) {
                System.out.println(&quot;value[&quot; + i + &quot;] : &quot; + value[i]);
            }
        }
    }

}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[2 Servlet | method]]></title>
            <link>https://velog.io/@cjmin-n/2-Servlet-method</link>
            <guid>https://velog.io/@cjmin-n/2-Servlet-method</guid>
            <pubDate>Thu, 17 Oct 2024 08:17:53 GMT</pubDate>
            <description><![CDATA[<p>요청이 GET/POST 어떤 방식인지에 따라 doGet()/doPost() 메소드를 사용한다.</p>
<ul>
<li>service() 는 확인차 사용</li>
</ul>
<pre><code class="language-java">package com.ohgiraffers.section01.servicemethod;

import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet(&quot;/request-service&quot;)
public class ServiceMethodTestServlet extends HttpServlet {


    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest httpRequest = (HttpServletRequest) req;
        HttpServletResponse httpResponse = (HttpServletResponse) res;
        // HttpServlet 으로 받았기 때문에 사용하려면 ServletRequest req, servletResponse res 를 다운캐스팅해야한다.

        // httpRequest 의 getMethod() 는 어떠한 http method 요청이 들어온건지
        // 문자열로 반환한다.
        String httpMethod = httpRequest.getMethod();
        System.out.println(&quot;http method : &quot; + httpMethod);

        // 구현이 되어있기 때문에 따로 해줄 필요 없음
        if((&quot;GET&quot;).equals(httpMethod)){
            doGet(httpRequest, httpResponse);
        }else if((&quot;POST&quot;).equals(httpMethod)){
            doPost(httpRequest, httpResponse);
        }

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(&quot;GET 요청을 처리할 메소드 호출&quot;);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(&quot;POST 요청을 처리할 메소드 호출&quot;);
    }


}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[1 Servlet | URL 연결방법]]></title>
            <link>https://velog.io/@cjmin-n/1-URL-%EC%97%B0%EA%B2%B0%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@cjmin-n/1-URL-%EC%97%B0%EA%B2%B0%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Thu, 17 Oct 2024 08:13:57 GMT</pubDate>
            <description><![CDATA[<p>서블릿에서 XML 매핑과 어노테이션 매핑은 둘 다 서블릿과
URL을 연결하는 방법이다.
현재는 어노테이션 방식을 훨씬 더 많이 사용한다.
그 이유는 어노테이션이 가지는 여러 가지 장점 때문이다.</p>
<h2 id="1-xml-방식">1. xml 방식</h2>
<ul>
<li>과거 방식으로, web.xml 파일에 서블릿을 등록하고 매핑하는 방식</li>
<li>단점 : 장황함, 유연성 부족, 가독성 저하</li>
</ul>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;web-app xmlns=&quot;https://jakarta.ee/xml/ns/jakartaee&quot;
         xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         xsi:schemaLocation=&quot;https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd&quot;
         version=&quot;6.0&quot;&gt;

    &lt;servlet&gt;
        &lt;servlet-name&gt;xmlmapping&lt;/servlet-name&gt;
        &lt;!-- 이름 지정 --&gt;
        &lt;servlet-class&gt;com.ohgiraffers.section01.xml.LifeCycleTestServlet&lt;/servlet-class&gt;
        &lt;!-- 클래스 경로 지정 --&gt;
        &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
        &lt;!-- 서버가 시작될 때 인스턴스를 생성한다. 태그 안의 숫자가 낮을 수록 서블릿의 우선순위가 높다. --&gt;
    &lt;/servlet&gt;
    &lt;!--  *서블릿 만듦  --&gt;
    &lt;servlet-mapping&gt;
        &lt;servlet-name&gt;xmlmapping&lt;/servlet-name&gt;
        &lt;!-- 어느 서블릿에 연결할지 --&gt;
        &lt;url-pattern&gt;/xml-lifecycle&lt;/url-pattern&gt;
        &lt;!-- 연결할 요청 --&gt;
    &lt;/servlet-mapping&gt;
    &lt;!-- * 요청에 서블릿 매핑 --&gt;

&lt;/web-app&gt;</code></pre>
<pre><code class="language-java">package com.ohgiraffers.section01.xml;

import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServlet;

import java.io.IOException;

public class LifeCycleTestServlet extends HttpServlet {

    private int initCount = 1;
    private int serviceCount = 1;
    private int destroyCount = 1;


    // 서블릿 컨테이너에 의해 최초 요청에만 실행되는 메소드
    //  - 서블릿을 처리하기 전에 준비 (DB 연결 등)
    @Override
    public void init() throws ServletException {
        System.out.println(&quot;xml 매핑 init() 호출 : &quot; + initCount++);
    }


    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println(&quot;xml 매핑 service() 호출 : &quot; + serviceCount++);
        // 최초 요청 시에는 init() 이후에 동작하고,
        // 두 번째 요청 부터는 init() 호출 없이 바로 호출된다.
    }
    // 모든 HTTP 요청이 먼저 전달되는 메소드이다.
    // 요청의 HTTP 메소드에 따라 적절하게 doGet(), doPost() 등을 호출한다. - 따로 오버라이드 안해도 됨


    // 컨테이너가 종료될 때 호출되는 메소드이며, 주로 자원을 반납하는 용도로 사용한다.
    @Override
    public void destroy() {
        System.out.println(&quot;xml 매핑 destroy() 호출 : &quot; + destroyCount++);
    }


}</code></pre>
<h2 id="2-annotation-방식">2. annotation 방식</h2>
<ul>
<li>현대적 방식으로 서블릿 클래스 위에
@WebServlet 어노테이션을 사용하여 직접 매핑하는 방식</li>
<li>장점 - 간결함, 유지보수 용이, 가독성 향상</li>
</ul>
<pre><code class="language-java">package com.ohgiraffers.section02.annotation;

import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;

import java.io.IOException;


@WebServlet(value = &quot;/annotation-lifecycle&quot;, loadOnStartup = 2)
//@WebServlet(value = &quot;/annotation-lifecycle&quot;)
//loadOnStartUp 이 없으면 실행 시 init됨
public class LifeCycleTestServlet extends HttpServlet {

    private int initCount = 1;
    private int serviceCount = 1;
    private int destroyCount = 1;


    // 서블릿 컨테이너에 의해 최초 요청에만 실행되는 메소드
    //  - 서블릿을 처리하기 전에 준비 (DB 연결 등)
    @Override
    public void init() throws ServletException {
        System.out.println(&quot;annotation 매핑 init() 호출 : &quot; + initCount++);
    }


    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println(&quot;annotation 매핑 service() 호출 : &quot; + serviceCount++);
        // 최초 요청 시에는 init() 이후에 동작하고,
        // 두 번째 요청 부터는 init() 호출 없이 바로 호출된다.
    }
    // 모든 HTTP 요청이 먼저 전달되는 메소드이다.
    // 요청의 HTTP 메소드에 따라 적절하게 doGet(), doPost() 등을 호출한다. - 따로 오버라이드 안해도 됨


    // 컨테이너가 종료될 때 호출되는 메소드이며, 주로 자원을 반납하는 용도로 사용한다.
    @Override
    public void destroy() {
        System.out.println(&quot;annotation 매핑 destroy() 호출 : &quot; + destroyCount++);
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[0 Servlet |  세팅]]></title>
            <link>https://velog.io/@cjmin-n/0-Servlet-%EC%84%B8%ED%8C%85</link>
            <guid>https://velog.io/@cjmin-n/0-Servlet-%EC%84%B8%ED%8C%85</guid>
            <pubDate>Mon, 14 Oct 2024 09:02:25 GMT</pubDate>
            <description><![CDATA[<p>인텔리제이 세팅방법</p>
<ul>
<li>new project</li>
<li>Generator : Jakarta EE (대용량 자바 프로젝트)</li>
<li>Template : Web Application</li>
<li>Application server: New-&gt; Tomcat Server-&gt; Home(Tomcat설치한 폴더경로)</li>
<li>Build System: Gradle</li>
<li>Group: com.ohgiraffers (그룹명 지정)</li>
</ul>
<p>edit configurations (한글 깨질 때)</p>
<ul>
<li>Server / vm options : -Dfile.encoding=UTF-8</li>
<li>Deployment / Application context : / (주소값 &#39;/&#39;로 줄임)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[8 React | Api]]></title>
            <link>https://velog.io/@cjmin-n/8-React-Api</link>
            <guid>https://velog.io/@cjmin-n/8-React-Api</guid>
            <pubDate>Tue, 01 Oct 2024 03:31:11 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트를 사용하면 필요할 때
서버에 네트워크 요청을 보내고 새로운 정보를 받아올 수 있다..
과거에는 ajax 를 이용해 페이지 새로고침 없이 (비동기)
서버에 데이터를 가져올 수 있었다..
fetch 는 ajax 의 단점을 개선하고 좀더 현대적인 방식으로
네트워크 요청을 처리할 수 있게 해준다..
따라서 최신 웹 어플리케이션은 fetch 를 사용하는 것을 권장한다.</p>
<p>기본 사용 방법</p>
<p>let promise = fetch(url, [option]); // 반환값은 promise 객체</p>
<p>url : 접근하고자 하는 url
option : http method 나 headers, body 내용을 객체로 지정할 수 있다..
        아무것도 넣지 않으면 기본 get 요청으로 전송한다..</p>
<p>-- 샘플 api
<a href="https://jsonplaceholder.typicode.com">https://jsonplaceholder.typicode.com</a></p>
<pre><code class="language-javascript">async function callApi(){

    const promise = fetch(&quot;https://jsonplaceholder.typicode.com/users&quot;);

    const response = await promise;
    console.log(response);

    console.log(`응답 상태 : ${response.status}`); // 200: 정상, 성공 / 404: 서버 요청한거 찾을수없을때(요청이 잘못됐을때) / 500: 서버 자체 잘못(개발자 잘못)

    console.log(&quot;응답 헤더&quot;); // headers: 사용자,서버가 소통할 때 도움이 될만한 정보를 담고 있다.. ex.어떤 데이터형식/응답날짜시간/응답본문길이 등
    for(let [key, value] of response.headers){
        console.log(`${key} : ${value}`);
    }

    const json = await response.json(); // 응답 본문을 json 형식으로 반환한다.
    console.log(json);

    json.forEach(element =&gt; {
        console.log(element,&quot;\n=======================&quot;);
    })

}

callApi();</code></pre>
<h3 id="json">JSON</h3>
<p>JSON (Javascript Object Notation) 은 데이터를
구조화하여 교환하기 위한 경량 데이터 형식이다..
사람과 기계 모두 읽고 쓰기 쉽게 설계되어 있으며
주로 웹 어플리케이션에서 서버와 클라이언트 사이의 데이터 전송에 사용된다..
특징으로는</p>
<ol>
<li>텍스트 기반의 형식</li>
<li>키 - 값 쌍으로 데이터를 표현</li>
<li>다양한 언어에서 지원</li>
<li>다양한 데이터 타입을 지원</li>
</ol>
<h3 id="axios--fetch-비교">Axios / Fetch 비교</h3>
<p>Axios : 요청 처리에 대한 여러 가지 기능을 제공해준다..
따라서 패키지 크기가 상대적으로 더 크고 무겁다..</p>
<p>Fetch : 기본 브라우저 내장 API 이다..
가볍고 빠르다. 하지만 여러 처리를 수동으로 해주어야 한다.</p>
<pre><code class="language-javascript">import axios from &quot;axios&quot;;

const callApi = () =&gt; {

    fetch(&quot;https://jsonplaceholder.typicode.com/users&quot;)
    .then(r=&gt;r.json()).then(d=&gt;console.log(d));
}

const axiosCall = () =&gt; {

    axios.get(&quot;https://jsonplaceholder.typicode.com/users&quot;) 
    .then(res=&gt;console.log(res.data));
    // axios 요청이 자동으로 제이슨 처리해준다. 
    // axios로 받은값.data-&gt;body의 내용들을 제이슨으로 꺼내준다.
}

const AxiosCallComponent = () =&gt; {
    return (
        &lt;&gt;
            &lt;button onClick={callApi}&gt;fetch 요청&lt;/button&gt;
            &lt;button onClick={axiosCall}&gt;axios 요청&lt;/button&gt;
        &lt;/&gt;
    )
}

export default AxiosCallComponent;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[7 React | async]]></title>
            <link>https://velog.io/@cjmin-n/7-React-async</link>
            <guid>https://velog.io/@cjmin-n/7-React-async</guid>
            <pubDate>Tue, 01 Oct 2024 02:55:16 GMT</pubDate>
            <description><![CDATA[<h3 id="동기-작업과-비동기-작업">동기 작업과 비동기 작업</h3>
<p>동기 작업 : 하나의 작업을 실행하고 마친 후에 작업을 순차적으로 실행</p>
<p>비동기 작업 : 메인 흐름은 멈추지 않은 상태에서 특정 작업들을
백그라운드에서 처리하며 동시에 처리하는 것 처럼 실행</p>
<pre><code class="language-javascript">const sayHello = () =&gt; {
    console.log(&quot;안녕&quot;);
}

setTimeout(sayHello, 3000); // 순서 2
console.log(&quot;end&quot;); // 순서 1</code></pre>
<p>setTimeout은 비동기 함수로
주어진 시간 후에 특정 코드를 실행하도록 예약하지만
기다리는 동안 나머지 코드는 계속 실행된다..
즉 비동기적으로 동작하여 코드의 흐름을 블록하지 않고 다음 작업을 이어간다.</p>
<pre><code class="language-javascript">
function increase(number, callback){
    setTimeout(()=&gt;{
        const result = number + 10;
        if(callback){ // callback = truthy
            callback(result);
        }
    }, 1000);
}

increase(0, result=&gt;{
    console.log(result);
    increase(result, result =&gt; {
        console.log(result);
        increase(result, result=&gt; {
            console.log(result);
        })
    })
})</code></pre>
<p>여러번 순차적으로 결과물을 콜백 함수로 전달하기 위해서는
중첩을 사용해서 호출할 수 있다..
하지만 이러한 형태는 가독성도 좋지 않고, 유지보수도 힘들기 때문에
피해야 하는 패턴 중 하나이다.. 이러한 형태를 콜백 지옥이라고 한다..</p>
<h4 id="그래서-비동기-함수를-왜-쓰나">그래서 비동기 함수를 왜 쓰나?</h4>
<ol>
<li><p>시간이 오래 걸리는 작업을 처리하기 위해서</p>
</li>
<li><p>사용자 인터페이스가 중단되지 않기 위해서..</p>
</li>
<li><p>여러 작업을 동시에 처리하기 위해서
 (실제로는 자바스크립트는 그렇게 동작하지 않지만, 이벤트 루프를 통해 동시에 처리되는 것 처럼 보임)</p>
</li>
<li><p>콜백 지옥을 해결하기 위해서</p>
</li>
</ol>
<p>결국 느린 작업이 전체 프로그램의 흐름을 막지 않도록 하기 위함..
이러면 사용자에게 더 나은 경험을 제공할 수 있고,
특히 네트워크 요청, 파일 입출력, 타이머 같은 시간이 오래 걸리는 작업에서 큰 효과를 발휘한다..</p>
<h2 id="promise-객체">Promise 객체</h2>
<p>promise 는 콜백 지옥과 같은 코드가 형성되지 않게
(비동기 통신 간의 순서를 정하기 쉽게) 하는 방안으로
ES6 에서 도입되었다..</p>
<p>promise 객체 생성 이유?</p>
<ol>
<li>비동기 처리 함수를 실행 시 성공, 실패에 대한 처리가 용이하다..</li>
<li>promise 객체가 제공하는 메소드를 사용하기 위함
(콜백 지옥 상황을 해소해 주기 때문.. 가독성이 좋고, 메소드 체이닝 방식 제공)</li>
</ol>
<pre><code class="language-javascript">const increase = number =&gt; {
    const promise = new Promise((resolve, reject)=&gt;{ 
        setTimeout(()=&gt;{
            const result = number + 10;
            if(result &gt; 50){
                const e = new Error(&quot;numberToBig&quot;);
                return reject(e); // return 생략 가능
            }
            resolve(result);
        }, 1000);
    });
    return promise;
}
// Promise 객체 내부 내용을 동기로 처리한다. (기다려줌) 함수의 인자로 원하는 작업을 하고 값을 return해준다. - 성공 : resolve(값), 실패: reject(값)

console.log(increase(0)); // Promise { &lt;pending&gt; } // 값을 반환하는게 아니라 비동기로 처리한 약속자체를 반환해준다.

// 체이닝
// promise 객체는 then 메소드를 사용하여 비동기 작업의 결과를 처리할 수 있다..

increase(0).then(number =&gt; {
    console.log(number)
    return increase(number);
}).then(number =&gt; {
    console.log(number);
    return increase(number);
}).catch(e=&gt;console.log(e, &quot;가 발생했네&quot;)); // then() 인자로 결과값을 받아옴..</code></pre>
<ol>
<li><p>resolve 값 반환
 -&gt; increase() 의 인자값 40이하 넣었을 때 값 출력</p>
</li>
<li><p>reject 값 반환
 -&gt; increase() 의 인자값 41이상 일때 에러발생 catch에 넣어둔 값 발생
 Error: numberToBig</p>
<pre><code> at Timeout._onTimeout (c:\20240801-gangnam\07_react\04_async\src\01_intro\03_promise.js:17:27)
 at listOnTimeout (node:internal/timers:581:17)
 at process.processTimers (node:internal/timers:519:7) 가 발생했네</code></pre></li>
</ol>
<h2 id="async--await">async / await</h2>
<ul>
<li>Promise : 비동기 작업의 결과를 처리하는 객체</li>
<li>async/await : 비동기 작업을 동기적으로 실행하는 것 처럼 보이게 만드는 구문
then() 과 await : promise 의 결과를 사용할 때
then() 을 사용하거나, async 함수 내에서 await 로 처리할 수 있음..</li>
</ul>
<pre><code class="language-javascript">function increase (number){
    const promise = new Promise((resolve, reject)=&gt;{
        setTimeout(()=&gt;{
            const result = number + 10;
            if(result &gt; 50){
                const e = new Error(&quot;number to big&quot;);
                return reject(e);
            }
            resolve(result);
        }, 1000);
    });
    return promise;
}


// async : 비동기 함수를 정의
async function run(){
    try{
        // resolve 값 받음
        let result = await increase(1); // await : 결과값을 기다렸다가 반환
        console.log(result);

        result = await increase(2);
        console.log(result);

        result = await increase(3);
        console.log(result);

        result = await increase(4);
        console.log(result);

        result = await increase(5);
        console.log(result);

        result = await increase(6);
        console.log(result);

        return result;
    }catch(e){
        // reject 값 받음
        console.log(e, &quot;가 발생함&quot;);
    }
}

const value = async () =&gt; {
    const runValue = await run();
    console.log(&quot;value &quot; + runValue);
}

const result = value();
/* console.log(result); // Promise { &lt;pending&gt; } */</code></pre>
]]></description>
        </item>
    </channel>
</rss>