<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>작심삼일</title>
    <link>https://shoney.tistory.com/</link>
    <description>개발하면서 느끼고 배운 것들을 정리한 블로그</description>
    <language>ko</language>
    <pubDate>Wed, 8 Jul 2026 17:17:35 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>shoney9254</managingEditor>
    <image>
      <title>작심삼일</title>
      <url>https://tistory1.daumcdn.net/tistory/4705173/attach/c5784757a8334e0baf14c089656d05f9</url>
      <link>https://shoney.tistory.com</link>
    </image>
    <item>
      <title>RAG, 에이전트, 프롬프트: AI 프로젝트 시작을 위한 기술 스택</title>
      <link>https://shoney.tistory.com/entry/RAG-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%ED%94%84%EB%A1%AC%ED%94%84%ED%8A%B8-AI-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%9C%EC%9E%91%EC%9D%84-%EC%9C%84%ED%95%9C-%EA%B8%B0%EC%88%A0-%EC%8A%A4%ED%83%9D</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AI 프로젝트, 특히 LLM(거대 언어 모델)을 활용한 서비스를 만들 때 고려해야 할 기술들을 정리한 가이드입니다. 각 기술의 개념을 이해하고, 자신의 프로젝트 규모와 목적에 맞는 최적의 조합을 찾아보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. RAG (Retrieval-Augmented Generation)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG는 LLM이 알지 못하는 최신 정보나 특정 분야의 전문 지식(예: 우리 학교 학사 규정, 특정 게임 공략집)을 &lt;b&gt;외부 문서에서 찾아와(Retrieval)&lt;/b&gt;, 그 내용을 바탕으로 답변을 생성(Generation)하는 기술입니다. LLM의 환각(Hallucination)을 줄이고 답변의 신뢰도를 높이는 가장 핵심적인 기술이죠.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1) 플랫폼&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG 파이프라인(데이터 준비 &amp;rarr; 검색 &amp;rarr; 생성)을 쉽게 만들도록 도와주는 도구들입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Makebot LLM Builder&lt;/b&gt;: 기업 내부 문서와 LLM을 연결해 빠르게 엔터프라이즈 챗봇을 구축 가능. &amp;rarr; &lt;b&gt;실무 활용도 높음&lt;/b&gt; (예: 고객센터 자동화).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Botgrade&lt;/b&gt;: 모델 답변을 자동 평가해 품질 관리. &amp;rarr; &lt;b&gt;사람이 매번 수작업 검증할 필요를 줄여줌&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;InstructRAG&lt;/b&gt;: 명령 기반 검색&amp;middot;응답. &amp;rarr; &lt;b&gt;FAQ, 지식베이스 질의응답에 적합&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ReaRAG&lt;/b&gt;: 실시간 검색 + 순환 추론 결합. &amp;rarr; &lt;b&gt;최신 뉴스 요약 같은 실시간성 요구 서비스에 유리&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Haystack&lt;/b&gt;: 오픈소스&amp;middot;대규모 문서 처리에 최적화. &amp;rarr; &lt;b&gt;연구&amp;middot;프로토타입 환경에 적합&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RAGFlow&lt;/b&gt;: 시각적 파이프라인 UI 제공. &amp;rarr; &lt;b&gt;비개발자도 손쉽게 조합 가능&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;txtai&lt;/b&gt;: 검색&amp;middot;분류&amp;middot;추천 통합 플랫폼. &amp;rarr; &lt;b&gt;텍스트와 비정형 데이터 동시 활용&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LlamaIndex&lt;/b&gt;: 다양한 데이터 타입 지원, 맞춤 검색에 강점.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Verba&lt;/b&gt;: 캐싱&amp;middot;하이브리드 검색 강점. &amp;rarr; &lt;b&gt;빠른 응답 필요한 챗봇에 적합&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2) 임베딩 모델&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'임베딩'은 텍스트(단어, 문장)를 컴퓨터가 이해할 수 있는 숫자들의 배열, 즉 **벡터(Vector)**로 바꾸는 과정입니다. 좋은 임베딩 모델은 '강아지'와 '멍멍이'처럼 의미가 비슷한 단어들을 벡터 공간에서 가까운 위치에 둡니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OpenAI (text-embedding-3): 영어 중심, 저비용&amp;middot;고차원&amp;middot;빠른 추론.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;large(1536차원): 긴 문서&amp;middot;복잡한 맥락 반영 유리.&lt;/li&gt;
&lt;li&gt;small(512차원): 속도&amp;middot;비용 효율적, FAQ 검색 적합.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cohere (v3.0): &lt;b&gt;다국어 강점, 의미 밀집 표현 최적화&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Hugging Face (BGE/E5/Instructor/MiniLM/MPNet) : 오픈소스 모델의 성지입니다. 특히 한국어 프로젝트를 할 때는 ko-sroberta-multitask 같은 한국어 특화 모델을 무료로 다운받아 사용할 수 있어 매우 유용합니다.&lt;/li&gt;
&lt;li&gt;Mistral (mistral-embed/Codestral): 1024차원&amp;middot;다국어&amp;middot;코드 임베딩에 특화&amp;middot;빠른 추론.&lt;/li&gt;
&lt;li&gt;VoyageAI (voyage-3/large/lite): 32K 컨텍스트&amp;middot;초저가&amp;middot;한국어 포함 다국어&amp;middot;작은 벡터로 효율적 검색.&lt;/li&gt;
&lt;li&gt;Google Gemini-Embedding: SOTA급&amp;middot;68점대 MTEB 다국어 성능&amp;middot;8192 토큰&amp;middot;범용성.&lt;/li&gt;
&lt;li&gt;Jina Embeddings v3: 570M 파라미터&amp;middot;8192 토큰&amp;middot;최첨단 다국어 임베딩&amp;middot;오픈소스.&lt;/li&gt;
&lt;li&gt;Amazon Titan: 멀티모달(텍스트+이미지)&amp;middot;클라우드 특화.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3) 벡터 차원&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임베딩 모델이 문장을 몇 개의 숫자로 표현하는지를 의미합니다. (예: 1536차원 = 1536개의 실수로 이루어진 배열)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;차원이 높을수록&lt;/b&gt;: 데이터의 세부적인 정보와 특징을 더 잘 반영할 수 있습니다. 예를 들어, 긴 문장이나 복잡한 이미지를 잘 표현하려면 더 높은 차원의 임베딩이 필요합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;차원이 낮을수록&lt;/b&gt;: 연산량이 줄어들고, 메모리 사용량이 적어집니다. 하지만 차원을 너무 낮추면 중요한 정보를 놓칠 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;결론&lt;/b&gt;: 일반적인 문서 검색은 768 ~ 1024차원으로도 충분하며, 사용하는 임베딩 모델이 정해주는 차원을 따르는 것이 일반적입니다.&lt;/li&gt;
&lt;li&gt;예: 짧은 FAQ 검색 &amp;rarr; 512차원, 논문 검색 &amp;rarr; 1536차원.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4) 검색 방식&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 질문과 가장 관련 있는 문서를 어떻게 찾을지에 대한 방법론입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;BM25 (Sparse Retrieval)&lt;/b&gt;: 단어 빈도 기반 전통 검색, 빠르고 정확한 키워드 매칭에 강점. &amp;rarr; &lt;b&gt;법률&amp;middot;규정 검색&lt;/b&gt; 적합.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DPR (Dense Retrieval) :&lt;/b&gt; 임베딩된 벡터 간의 &lt;b&gt;의미적 유사도&lt;/b&gt;(코사인 유사도 등)를 계산합니다. 고전적인 dense 모델, 아직도 baseline으로 쓰임.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Contriever (Dense Embedding)&lt;/b&gt;: 최신 self-supervised dense 모델, 의미 기반 검색에 강점.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Hybrid (Dense + Sparse)&lt;/b&gt;: BM25와 Dense Embedding 결합, 키워드 정확성과 의미 유사도를 동시에 확보. &amp;rarr; &lt;b&gt;FAQ 챗봇, 문서 검색 등에서 가장 널리 사용&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;aside&amp;gt;  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[어떤 방식을 선택할까?]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;법률 조문, 사내 규정 검색&lt;/b&gt;: 키워드가 정확히 일치해야 하므로 &lt;b&gt;BM25&lt;/b&gt;가 유리합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&quot;요즘 유행하는 패션 스타일 알려줘&quot;&lt;/b&gt;: 사용자의 의도를 파악해야 하므로 &lt;b&gt;Dense&lt;/b&gt; 검색이 적합합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대부분의 Q&amp;amp;A 챗봇&lt;/b&gt;: 두 가지 장점을 모두 취하는 &lt;b&gt;Hybrid Search&lt;/b&gt;를 사용하는 것이 가장 안정적입니다. &amp;lt;/aside&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5) 백터 인덱스 / 근사검색&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수백만 개의 문서 벡터 중에서 내 질문 벡터와 가장 가까운 벡터를 어떻게 '효율적으로' 찾을지에 대한 기술입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Flat (Brute Force)&lt;/b&gt;: 모든 벡터와 일일이 거리를 계산하는 가장 무식하고 정확한 방법. 데이터가 수천 개 이하라면 쓸 수 있지만, 그 이상이면 속도가 너무 느려져 현실적으로 사용하기 어렵습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;IVF (Inverted File Index)&lt;/b&gt;: 벡터들을 여러 개의 '방(클러스터)'으로 미리 나눠놓고, 질문 벡터가 들어오면 가장 가까운 방 몇 개만 뒤지는 방식입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HNSW (Hierarchical Navigable Small World Graph)&lt;/b&gt;: 벡터들을 '친구의 친구는 친구' 같은 소셜 네트워크 형태의 그래프로 연결해, 가장 빠른 길을 따라가며 근접 이웃을 찾는 방식입니다. 검색 속도가 매우 빨라 현재 가장 널리 쓰이는 인덱스 방식 중 하나입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PQ (Product Quantization)&lt;/b&gt;: 벡터를 여러 조각으로 나눠 압축하여 메모리 사용량을 획기적으로 줄이는 기술입니다. 약간의 정확도 손실을 감수하고 메모리 효율을 극대화할 때 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;6) 인덱스/벡터 DB&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Chroma, Faiss&lt;/b&gt;: &lt;b&gt;파일 기반&lt;/b&gt;으로 동작하여 별도의 서버 설치 없이 가볍게 시작할 수 있습니다. 개인 프로젝트나小규모 테스트에 매우 적합합니다. Faiss는 Facebook(Meta)에서 개발했으며, 성능이 매우 뛰어납니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Chroma&lt;/b&gt;: Python 친화적, 소규모/중규모 RAG 환경에 최적화.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Faiss&lt;/b&gt;: Facebook 오픈소스, 다양한 인덱스 옵션 지원, 대규모 벡터 검색에 특화.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Milvus, Qdrant, Weaviate, Pinecone&lt;/b&gt;: 대규모 서비스를 위한 &lt;b&gt;서버-클라이언트 구조&lt;/b&gt;의 벡터 DB입니다. 실시간 데이터 추가/삭제, 필터링, 확장성 등 운영에 필요한 다양한 기능을 제공합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Pinecone&lt;/b&gt;: 클라우드 기반 벡터 DB, 자동 스케일링과 관리형 인프라 제공.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Milvus&lt;/b&gt;: 오픈소스 고성능 벡터 DB, GPU 지원으로 대규모 검색 최적화.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Qdrant&lt;/b&gt;: 실시간 벡터 검색에 강점, ML 모델과 쉽게 통합 가능.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Weaviate&lt;/b&gt;: GraphQL 지원, 메타데이터 기반 필터링과 시맨틱 검색 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;7) Chunking 전략&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;긴 문서를 LLM에 한 번에 넣을 수 없기 때문에, 의미 있는 단위로 잘게 쪼개는 과정입니다. RAG 성능에 가장 큰 영향을 미치는 요소 중 하나입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Fixed-length&lt;/b&gt;: 512 토큰(대략 250~300자) 같은 고정된 길이로 무조건 자르는 가장 간단한 방식.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Sliding window (Overlap)&lt;/b&gt;: 문장의 중간이 잘리는 것을 방지하기 위해 Chunk를 나눌 때 이전 Chunk의 끝부분 일부(e.g., 50 토큰)를 다음 Chunk의 시작 부분에 포함시키는 방식입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Semantic chunking&lt;/b&gt;: 문단, 문장 등 의미가 완결되는 단위로 자르거나, 임베딩 벡터의 유사도를 기반으로 의미적 경계를 찾아 자르는 고급 방식입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Hierarchical chunking&lt;/b&gt;: 큰 단위 &amp;rarr; 작은 단위 순차 검색 (multi-stage retrieval)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;8) Re-ranking&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 검색(Retrieval) 단계에서 찾아온 여러 개의 문서(e.g., 상위 20개)를 LLM에게 넘기기 전에, 질문과 정말로 관련이 높은 문서만 추려내 순서를 다시 매기는 과정입니다. LLM에 들어가는 정보의 질을 높여 최종 답변의 품질을 향상시킵니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Cross-encoder (e.g. msmarco-distilbert, bge-reranker) : 검색된 각 (질문, 문서) 쌍을 모델에 함께 입력하여 관련도를 훨씬 정밀하게 계산하는 방식입니다. 속도는 느리지만 정확도가 높습니다.&lt;/li&gt;
&lt;li&gt;LLM re-ranker (GPT-4, Claude로 후보 문서 다시 평가) : GPT-4 같은 고성능 LLM에게 직접 후보 문서들을 보여주고, &quot;이 질문에 답하는 데 가장 중요한 순서대로 3개만 골라줘&quot;라고 요청하는 방식입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Statistical Ranking&lt;/b&gt;: 단순 스코어 기반&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;9) 기타 고려사항&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;성능/비용 trade-off&lt;/b&gt;: 고성능 임베딩 모델, 큰 벡터 차원, Re-ranking 등은 성능을 높이지만 API 비용과 응답 시간을 증가시킵니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스케일링&lt;/b&gt;: 문서가 1만 개일 때와 100만 개일 때의 검색 속도와 시스템 구조는 완전히 달라야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;검색 파라미터&lt;/b&gt;: Top-k(몇 개를 검색할지), 유사도 점수 임계값 등을 조절하며 최적의 값을 찾아야 합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;k (Top-k 검색 개수)&lt;/b&gt;: 3, 5, 10, 20&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Max marginal relevance (MMR)&lt;/b&gt;: 중복 줄이기&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Score threshold&lt;/b&gt;: 특정 유사도 이상만 채택&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Hybrid Weighting&lt;/b&gt;: Sparse vs Dense 비율 조정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. Agent&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트는 단순히 질문에 답하는 것을 넘어, 스스로 &lt;b&gt;사고(Thinking)하고, 계획(Planning)을 세우고, 도구(Tools)를 사용하여&lt;/b&gt; 주어진 목표를 달성하는 자율적인 프로그램입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 아키텍처 / 프레임워크&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;프레임워크&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;LangChain&lt;/b&gt;, &lt;b&gt;LlamaIndex&lt;/b&gt;가 에이전트 개발에도 널리 쓰이며, 최근에는 상태(State)와 흐름 제어에 강점이 있는 &lt;b&gt;LangGraph&lt;/b&gt;, 여러 에이전트의 협업에 특화된 &lt;b&gt;AutoGen&lt;/b&gt;, &lt;b&gt;CrewAI&lt;/b&gt; 등이 주목받고 있습니다.&lt;/li&gt;
&lt;li&gt;LangChain, LangGraph, AutoGen, CrewAI, Semantic Kernel, LlamaIndex&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실행 모델&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단일 에이전트 vs 멀티에이전트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 상호작용 (Agent Collaboration)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;멀티에이전트 상호작용 패턴&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;협력(Cooperative): 각자 맡은 역할을 수행하고 결과를 합치는 방식.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에이전트 3명이 문서 요약 &amp;rarr; 최종 합산 요약 결과를 평가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;경쟁(Competitive): 결과 품질을 겨루고 우승 답변 채택
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에이전트 2명이 질문에 대해 독립적으로 답변 &amp;rarr; 우승 답변 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;혼합(Hybrid): 경쟁 후 협력적 합의
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;경쟁 후 합의(Consensus) 단계 추가 &amp;rarr; 합의 결과 평가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;자기 검증형 (Reflective) : 에이전트가 자신의 결과물을 스스로 평가하고 부족한 점을 수정, 보완하는 방식. (마치 코드 리뷰를 스스로 하는 개발자처럼)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;의사결정 방식&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;투표(Voting), 합의(Consensus), 평판(Reputation-based) 알고리즘&lt;/li&gt;
&lt;li&gt;deadlock / 무한루프 방지 메커니즘&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대규모 시뮬레이션&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수십 명 에이전트가 가상 사회에서 협업&amp;middot;토론하는 시나리오 (e.g. Social Simulation)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) 도구 / API 연동 (Tool Use)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트의 능력을 확장하는 핵심 기능입니다. LLM이 할 수 없는 일(e.g., 실시간 정보 검색, 계산, 외부 서비스 연동)을 함수(API)로 구현하여 에이전트에게 제공합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;툴 선택 로직&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단일 툴 선택&lt;/li&gt;
&lt;li&gt;복수 툴 체이닝 (A &amp;rarr; B &amp;rarr; C 호출)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예시: '환율 계산 &amp;rarr; 해외 결제 금액 추정 &amp;rarr; 이메일로 결과 전송' 같이 여러 API를 연결할 필요가 있을 때 체이닝을 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실시간 데이터 연동&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순히 내장된 지식이 아닌, 실시간 검색(날씨&amp;middot;금융&amp;middot;주식&amp;middot;뉴스 등)이나 최신 데이터를 API로 가져오는 기능이 요구됩니다.&lt;/li&gt;
&lt;li&gt;API latency가 높거나 불안정할 때 fallback 전략
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예시: &quot;날씨 정보 API가 3초 내 응답하지 않으면 다른 날씨 API 호출&quot;, &quot;검색 실패 시 가장 최근 데이터 사용&quot;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Streaming 응답 처리 가능 여부&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;잘못된 툴 선택(오작동) 검출률 (Tool reliability metrics)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에이전트가 잘못된 도구를 선택하거나 API 호출 결과가 실패할 수 있으므로, 오작동(실패) 발생률 모니터링 및 평가 지표가 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) 상태 관리 / 메모리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;단기 메모리 (Session memory)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대화 중 맥락 유지, 이전 입력 반영 여부&lt;/li&gt;
&lt;li&gt;랭그래프에서 상태 관리 전략&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장기 메모리 (Long-term memory)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이전 대화의 중요한 내용을 Vector DB 등에 요약, 저장해두고 필요할 때 다시 꺼내 쓰는 방식&lt;/li&gt;
&lt;li&gt;RDB 사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5) 결과 품질 및 제어&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;추론 Traceability&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;ldquo;왜 이 결정을 내렸는가?&amp;rdquo; 설명 가능성 (나중에 물어 봤을 때라도 추론 추적)&lt;/li&gt;
&lt;li&gt;호출 경로(Which tool, Which agent)를 기록/리플레이 가능 여부&lt;/li&gt;
&lt;li&gt;또는 결정 내린 히스토리를 어디에다가 데이터화 해놓는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;오류 유형&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;할루시네이션 / 무한 루프 / 툴 실패 / Deadlock 발생 빈도&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Guardrails&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정책 위반(PII 노출, 금칙어 등) 차단&lt;/li&gt;
&lt;li&gt;응답 길이&amp;middot;포맷 강제 여부 (예: JSON schema validation)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6) 멀티모달 에이전트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;입력/출력 모달리티&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;텍스트, 음성, 이미지, 비디오, 센서 데이터&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Cross-modal reasoning&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지 기반 질문 &amp;rarr; 텍스트 답변, 오디오 지시 &amp;rarr; 코드 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;멀티모달&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7) 기타 고려사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;오케스트레이션&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Workflow 관리: Prefect, Airflow, Temporal 같은 외부 오케스트레이터 연동&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스케일링&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에이전트 수 증가 시 비용, Latency, Throughput 변화 측정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장애 대응&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에이전트 일부 실패 시 점진적 기능 축소(graceful degradation) 가능 여부&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Observability&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로그, 메트릭, 이벤트 트레이싱 (Prometheus, OpenTelemetry 연동)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 프롬프트&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM에게 원하는 결과물을 얻어내기 위해 내리는 '지시문'입니다. 좋은 프롬프트는 좋은 결과물의 시작이며, '프롬프트 엔지니어링'은 그 자체로 중요한 기술 분야입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1) 프롬프트 엔지니어링 기법&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Prompt chaining&lt;/b&gt;: 여러 개의 작은 프롬프트를 연결해 단계별로 처리&lt;/li&gt;
&lt;li&gt;예: 질문 요약 &amp;rarr; 관련 문서 검색 &amp;rarr; 최종 답변 생성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Self-reflection&lt;/b&gt;: 모델이 스스로 답변을 검토하고 수정&lt;/li&gt;
&lt;li&gt;예: &quot;너의 답변을 다시 읽고, 논리적 오류가 있는지 점검하라&quot;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Few-shot / Zero-shot / Hybrid&lt;/b&gt;: 예시 제공 여부에 따른 차이 검증&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/PromptEngineering/comments/1k7jrt7/advanced_prompt_engineering_techniques_for_2025/&quot;&gt;Reddit 최신 기법&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;1. Recursive Self-Improvement Prompting (RSIP) - 반복적 자기 개선 프롬프트&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM이 자신의 출력물을 반복적으로 평가&amp;middot;비판&amp;middot;수정하며 스스로 품질을 높여가는 방식.&lt;/li&gt;
&lt;li&gt;예시: 출력 후 최소 3개 결점 스스로 진단 &amp;rarr; 해당 사항 개선하여 재출력 &amp;rarr; 선정된 평가 기준별로 단계별 개선 반복 후 최종본 제출.&lt;/li&gt;
&lt;li&gt;적용 분야: 창작, 기술 문서, 논증 설계 등(평가 기준별로 단계 개선).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;2. Context-Aware Decomposition (CAD) - 맥락 기반 복합 문제 분해&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복잡한 과제의 핵심 컴포넌트를 분리&amp;middot;분석 후, 각 요소를 맥락과 연결해서 각각 해결하고, 종합적으로 전체 답안을 제시하는 방식.&lt;/li&gt;
&lt;li&gt;'Thinking Journal(사고 일지)'로 각 단계별 사고 과정을 기록하여 중간 결과와 통합적 해법 연결.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;3. Controlled Hallucination for Ideation (CHI) - &amp;lsquo;통제된 환각&amp;rsquo; 활용 창의 이데이션&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM의 hallucination을 완전히 억제하지 않고, 창의적 아이디어 도출에 적극 활용.&lt;/li&gt;
&lt;li&gt;'Speculative' 결과물 명확 표시 &amp;rarr; 아이디어별 이론적 근거, 실제 구현 필요사항, 최종적으로 실현 가능성까지 비평.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;4. Multi-Perspective Simulation (MPS) - 다관점 시뮬레이션&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순 찬반 구도가 아닌 다섯 개 이상의 복잡한 시각/가정/논리 구조를 생성.&lt;/li&gt;
&lt;li&gt;각 관점별로 핵심 가치, 강점/약점, 논리 근거 분석 &amp;rarr; 서로 간 대화/합의/비판 &amp;rarr; 최종 통합적 시각으로 결론 도출.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;5. Calibrated Confidence Prompting (CCP) - 신뢰도 등급화 프롬프트&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM이 각 주장이나 정보에 대해 명시적으로 Confidence(신뢰도) 등급을 부여하도록 프롬프트 설계(예: Virtually Certain, Highly Confident, Moderately Confident, Speculative 등).&lt;/li&gt;
&lt;li&gt;각 등급별로 증거/불확실성/추가 정보 필요성 명시.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mirascope.com/blog/advanced-prompt-engineering&quot;&gt;Mirascope Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Tree-of-Thought (ToT)&lt;/b&gt;: 의사결정을 트리 형태로 분기해 탐색&lt;/li&gt;
&lt;li&gt;예: 수학 문제 &amp;rarr; 여러 풀이 경로 생성 &amp;rarr; 최적 해법 선택&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Graph-of-Thought (GoT)&lt;/b&gt;: 노드-엣지 형태로 사고 과정을 네트워크화&lt;/li&gt;
&lt;li&gt;예: 복잡한 프로젝트 계획을 여러 경로/연관성으로 연결해 정리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Recall-Augmented Generation&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 지식 DB를 참조하여 기억 기반 답변을 강화함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Tool-Calling Prompt / Agentic Loop&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프롬프트 안에 API 호출&amp;middot;서드파티 도구 사용 지시를 포함하여, LLM이 실질적 행동(예: 검색, 연산)을 수행하도록 유도.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2) 프롬프트 표준화&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동일한 prompt, 동일한 seed/temperature, 동일한 토큰 한도&lt;/li&gt;
&lt;li&gt;재현성 확보: 모델 버전, 토크나이저 버전, DB 버전 기록&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3) 프롬프트 옵저버빌리티&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Langfuse, PromptLayer 등은 프롬프트 입력, 결과, 비용, 지연 시간 등을 추적/시각화합니다.&lt;/li&gt;
&lt;li&gt;비효율적인 프롬프트를 빠르게 탐지&amp;middot;개선할 수 있습니다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) 기타 고려 사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정책 준수 검증&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;레드팀 프롬프트(민감정보, jailbreak, 혐오발화) 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fusion 전략&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Late Fusion&lt;/b&gt;: 검색(검색된 여러 문서)에 대해 각각 LLM이 답변을 생성한 뒤,&amp;nbsp;각 문서별로 생성된 여러 답변을 다시 합치는 방식입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Early Fusion&lt;/b&gt;: 검색된 여러 문서의&amp;nbsp;retrieval 결과를 미리 합친 뒤, 한 번에 LLM에 입력하여 답변을 생성하는 방식입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Retrieval 결과와 LLM 입력을 어떻게 조합해서 최종 답변을 생성할지 결정&lt;/li&gt;
&lt;li&gt;평가 자동화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;HELM, EvalScope&lt;/b&gt;: 학계나 기업에서 대규모로 LLM의 종합 성능을 벤치마킹하는 프레임워크입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테스트케이스 자동화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 &lt;b&gt;도메인/업무 상황에 맞춘 테스트 세트&lt;/b&gt;를 자동 생성해서 모델에 넣고 결과 검증&lt;/li&gt;
&lt;li&gt;예: 고객 문의 100개 &amp;rarr; 모델 응답 &amp;rarr; 정답셋과 비교하여 pass/fail 체크&lt;/li&gt;
&lt;li&gt;소프트웨어 테스트에서의 단위테스트 자동화와 비슷한 개념을 LLM 성능 평가에 적용한 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;</description>
      <category>AI</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/237</guid>
      <comments>https://shoney.tistory.com/entry/RAG-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%ED%94%84%EB%A1%AC%ED%94%84%ED%8A%B8-AI-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%9C%EC%9E%91%EC%9D%84-%EC%9C%84%ED%95%9C-%EA%B8%B0%EC%88%A0-%EC%8A%A4%ED%83%9D#entry237comment</comments>
      <pubDate>Mon, 20 Oct 2025 13:22:34 +0900</pubDate>
    </item>
    <item>
      <title>통합 지식 관리 포털</title>
      <link>https://shoney.tistory.com/entry/%ED%86%B5%ED%95%A9-%EC%A7%80%EC%8B%9D-%EA%B4%80%EB%A6%AC-%ED%8F%AC%ED%84%B8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 상황 (Scenario):&lt;/b&gt; 현재 CoreTech의 기술 자료, 프로젝트 산출물, 개발 노하우 등은 각 팀의 Confluence, Google Drive, 개인 Notion, 심지어는 특정 팀원의 로컬 PC에 흩어져 있습니다. 이로 인해 다음과 같은 심각한 문제들이 발생하고 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;정보 검색의 어려움:&lt;/b&gt; 신규 입사자가 특정 기술 정보를 찾기 위해 여러 채널을 헤매야 하며, 사내 전문가에게 반복적으로 질문하는 상황이 빈번합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지식의 사일로(Silo)화:&lt;/b&gt; A팀에서 해결한 장애 경험이나 유용한 개발 팁이 B팀에 공유되지 않아 비슷한 문제를 다시 해결하느라 리소스가 낭비됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보안 및 접근 제어의 부재:&lt;/b&gt; 민감한 프로젝트 정보나 고객 데이터가 포함된 문서가 적절한 통제 없이 공유될 위험이 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지식의 비활용:&lt;/b&gt; 축적된 데이터가 단순한 '창고'에 머물러, 이를 활용한 기술 동향 분석이나 사내 전문가(SME, Subject-Matter Expert) 발굴 등이 전혀 이루어지지 않고 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;과제:&lt;/b&gt; 당신은 이 문제를 해결하기 위한 &lt;b&gt;'통합 지식 관리 포털(Unified Knowledge Portal)'&lt;/b&gt; 구축 프로젝트의 리더로 임명되었습니다. 아래 질문에 대해 당신의 해결 방안을 구체적인 기술 스택과 함께 논리적으로 서술하십시오.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 1: 데이터 모델링 및 저장소 설계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;산재한 다양한 형태(정형/비정형)의 지식(API 명세, 장애 해결 문서, 회의록, 소스코드 스니펫, 동영상 가이드 등)을 효과적으로 저장하고 관리하기 위한 &lt;b&gt;데이터베이스 스키마와 저장소 아키텍처&lt;/b&gt;를 설계하십시오.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[평가 포인트]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문서의 메타데이터(작성자, 태그, 소속 팀, 관련 기술 등)를 어떻게 구조화할 것인가?&lt;/li&gt;
&lt;li&gt;본문 검색(Full-Text Search)을 효율적으로 지원하기 위해 어떤 기술(e.g., Elasticsearch, OpenSearch)을 도입하고, RDBMS와 어떻게 연동할 것인가?&lt;/li&gt;
&lt;li&gt;대용량 파일(동영상, 이미지) 첨부를 고려한 저장소(e.g., S3, NAS) 설계 방안은 무엇인가?&lt;/li&gt;
&lt;li&gt;데이터베이스 모델링 시 확장성(새로운 유형의 지식 추가)을 어떻게 고려했는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 2: 똑똑한 검색(Smart Search) 기능 구현 방안&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 최소한의 노력으로 원하는 정보를 정확하게 찾을 수 있도록 &lt;b&gt;'똑똑한 검색' 기능&lt;/b&gt;을 구현하려고 합니다. 단순히 키워드 매칭을 넘어선 검색 품질 향상 방안을 구체적인 기술과 함께 제시하십시오.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[평가 포인트]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자연어 검색(Semantic Search)을 도입한다면 어떤 임베딩 모델(e.g., SBERT, OpenAI-Ada)을 사용할 것이며, 검색 파이프라인은 어떻게 구성할 것인가?&lt;/li&gt;
&lt;li&gt;검색 결과의 랭킹(Ranking)을 어떻게 개선할 것인가? (e.g., 조회 수, '좋아요' 수, 최신성, 작성자 신뢰도 등)&lt;/li&gt;
&lt;li&gt;사용자의 검색 의도를 파악하고 연관 지식을 추천해주는 로직을 어떻게 구현할 것인가? (e.g., '이 문서를 본 다른 사람들은 이런 문서도 봤어요')&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 3: 권한 관리 및 보안 아키텍처 설계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서별, 팀별로 유연하면서도 강력한 &lt;b&gt;접근 제어(Access Control) 시스템&lt;/b&gt;을 구축해야 합니다. 사내 HR 시스템의 조직도/직책 정보와 연동하는 것을 전제로, 권한 관리 모델을 설계하고 발생할 수 있는 보안 위협과 그 대응책을 서술하십시오.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[평가 포인트]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 권한 모델(e.g., RBAC, ABAC)을 선택할 것이며, 그 이유는 무엇인가?&lt;/li&gt;
&lt;li&gt;민감 정보(개인정보, API Key 등)가 포함된 문서가 업로드될 경우, 이를 자동으로 탐지하고 마스킹하거나 경고하는 방안은 무엇인가?&lt;/li&gt;
&lt;li&gt;API 엔드포인트 설계 시, 발생할 수 있는 보안 취약점(e.g., Insecure Direct Object References)과 방어 전략을 설명하시오.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 4: 지식 활용 및 동기 부여 방안&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지식 포털이 '죽은 문서'의 무덤이 되지 않고, 살아있는 지식 생태계로 기능하기 위한 &lt;b&gt;기술적/정책적 방안&lt;/b&gt;을 제시하십시오.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[평가 포인트]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우수 콘텐츠 작성자나 활발한 답변자에게 동기를 부여할 수 있는 시스템(e.g., 지식 포인트, 뱃지, 랭킹 보드)을 어떻게 구현할 것인가?&lt;/li&gt;
&lt;li&gt;정기적으로 특정 기술 분야의 전문가(SME)를 시스템이 자동으로 식별하고 추천해주는 로직을 어떻게 설계할 것인가?&lt;/li&gt;
&lt;li&gt;Slack, MS Teams 등 기존 협업 도구와의 연동을 통해 지식 포털의 활용성을 높일 수 있는 구체적인 아이디어(e.g., 특정 채널에 질문 시 관련 지식 링크 자동 추천)를 제안하시오.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;문제 1: 데이터 모델링 및 저장소 설계&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;핵심 해결안 (키워드)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;메타데이터 관리:&lt;/b&gt;&amp;nbsp;&lt;b&gt;PostgreSQL&lt;/b&gt;을 주 데이터베이스로 사용.&amp;nbsp;문서의 구조화된 정보(ID,&amp;nbsp;작성자,&amp;nbsp;생성일,&amp;nbsp;팀,&amp;nbsp;태그)는 정규화된 테이블로 관리.&amp;nbsp;핵심 스키마 외의 유연한 속성은&amp;nbsp;JSONB&amp;nbsp;타입을 활용하여 확장성 확보.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;본문 검색:&lt;/b&gt;&amp;nbsp;&lt;b&gt;Elasticsearch&lt;/b&gt;를 검색 엔진으로 도입.&amp;nbsp;문서 내용은 색인하여 전문 검색(Full-Text Search)을 지원.&amp;nbsp;PostgreSQL의 데이터가 변경될 때마다 CDC(Change Data Capture) 패턴 또는 메시지 큐를 통해 Elasticsearch와 데이터를 동기화.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;대용량 파일:&lt;/b&gt;&amp;nbsp;&lt;b&gt;AWS S3&lt;/b&gt;와 같은 Object Storage 활용.&amp;nbsp;DB에는 파일의 메타데이터와 S3 Object Key만 저장.&amp;nbsp;애플리케이션은 S3 Pre-signed URL을 생성하여 클라이언트가 직접 파일을 업로드/다운로드하게 하여 서버 부하 최소화.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;기술 선택 및 장점 (비교)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;PostgreSQL vs. MySQL/NoSQL:&lt;/b&gt;&amp;nbsp;JSONB&amp;nbsp;타입 지원이 강력하여,&amp;nbsp;정형 데이터의 무결성을 지키면서도 비정형 메타데이터를 유연하게 처리할 수 있습니다.&amp;nbsp;스키마 변경 없이 새로운 속성을 추가할 수 있어 NoSQL의 장점을 일부 흡수하면서 RDBMS의 안정성을 가져갑니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;Elasticsearch vs. RDBMS 내장 검색:&lt;/b&gt;&amp;nbsp;단순&amp;nbsp;LIKE&amp;nbsp;검색이나 기본적인 Full-Text Search 기능과 비교할 수 없는 수준의&amp;nbsp;&lt;b&gt;Relevance Scoring(연관도 점수화)&lt;/b&gt;,&amp;nbsp;형태소 분석,&amp;nbsp;동의어/오타 처리 등 고품질의 검색 기능을 제공합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;S3 vs. 서버 로컬 저장(NAS/NFS):&lt;/b&gt;&amp;nbsp;&lt;b&gt;무한한 확장성&lt;/b&gt;과&amp;nbsp;&lt;b&gt;비용 효율성&lt;/b&gt;,&amp;nbsp;강력한 내구성(99.999999999%)이 장점입니다.&amp;nbsp;애플리케이션 서버와 파일 스토너리지를 분리하여 서버를 Stateless하게 유지할 수 있어,&amp;nbsp;수평 확장에 매우 유리합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;문제 2: 똑똑한 검색(Smart Search) 기능 구현&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;핵심 해결안 (키워드)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;검색 방식:&lt;/b&gt;&amp;nbsp;&lt;b&gt;하이브리드 검색(Hybrid Search)&lt;/b&gt;&amp;nbsp;아키텍처 채택.&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;키워드 검색 (Lexical Search):&lt;/b&gt;&amp;nbsp;Elasticsearch의 BM25 알고리즘 기반.&amp;nbsp;정확한 키워드가 일치할 때 높은 점수를 부여.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;벡터 검색 (Vector/Semantic Search):&lt;/b&gt;&amp;nbsp;문장 임베딩 모델(&lt;b&gt;SBERT&lt;/b&gt;)을 사용하여 문서 본문과 사용자 질의를 벡터로 변환.&amp;nbsp;의미적 유사도를 기반으로 검색.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;검색 파이프라인:&lt;/b&gt;&amp;nbsp;사용자 질의가 들어오면,&amp;nbsp;키워드 검색과 벡터 검색을&amp;nbsp;&lt;b&gt;동시에 실행&lt;/b&gt;하고 각 결과의 점수를 조합(e.g.,&amp;nbsp;Reciprocal Rank Fusion)하여 최종 랭킹을 결정.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;랭킹 부스팅:&lt;/b&gt;&amp;nbsp;최종 랭킹에&amp;nbsp;&lt;b&gt;문서의 최신성, 조회 수, '좋아요' 수, 작성자의 SME(사내 전문가) 레벨&lt;/b&gt;&amp;nbsp;등의 비즈니스 로직을 가중치로 적용하여 신뢰도를 높임.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;기술 선택 및 장점 (비교)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;하이브리드 검색 vs. 벡터 검색 Only:&lt;/b&gt;&amp;nbsp;벡터 검색은 문맥적 의미를 잘 파악하지만,&amp;nbsp;NullPointerException,&amp;nbsp;PROD-API-KEY와 같은&amp;nbsp;&lt;b&gt;고유명사나 기술 용어 검색에 취약&lt;/b&gt;할 수 있습니다.&amp;nbsp;키워드 검색을 결합하면,&amp;nbsp;의미적 검색의 장점과 키워드 검색의 정확성을 모두 잡아 검색 품질을 극대화할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;SBERT vs. OpenAI API (Ada-002):&lt;/b&gt;&amp;nbsp;&lt;b&gt;데이터 프라이버시&lt;/b&gt;와&amp;nbsp;&lt;b&gt;비용 통제&lt;/b&gt;에 유리합니다.&amp;nbsp;SBERT와 같은 오픈소스 모델은 사내 GPU 서버에 직접 배포하여 민감한 내부 데이터의 외부 유출 없이 사용할 수 있습니다.&amp;nbsp;초기 구축 비용은 들지만,&amp;nbsp;장기적으로 API 호출 비용보다 저렴하고,&amp;nbsp;사내 데이터로&amp;nbsp;&lt;b&gt;Fine-tuning하여 도메인 특화 성능을 높일 수 있습니다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;문제 3: 권한 관리 및 보안 아키텍처&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;핵심 해결안 (키워드)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;권한 모델:&lt;/b&gt;&amp;nbsp;&lt;b&gt;RBAC(역할 기반 접근 제어)를 기본&lt;/b&gt;으로,&amp;nbsp;복잡한 정책을 위해&amp;nbsp;&lt;b&gt;ABAC(속성 기반 접근 제어)를 보조&lt;/b&gt;로 사용.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;RBAC:&lt;/b&gt;&amp;nbsp;Admin,&amp;nbsp;TeamLead,&amp;nbsp;Member&amp;nbsp;등 직관적인 역할을 정의하고,&amp;nbsp;역할에 권한(읽기/쓰기/삭제)을 부여.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;ABAC:&lt;/b&gt;&amp;nbsp;'A팀 소속의 TeamLead만 &quot;대외비&quot; 태그가 붙은 문서에 접근 가능'과 같이 사용자,&amp;nbsp;리소스,&amp;nbsp;환경의 속성을 조합한 동적인 제어 규칙을 적용.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;민감정보 탐지:&lt;/b&gt;&amp;nbsp;문서 저장 시 비동기 파이프라인(Message Queue + Worker)을 통해&amp;nbsp;&lt;b&gt;정규표현식(RegEx)&lt;/b&gt;&amp;nbsp;기반의 패턴 매칭으로 API 키,&amp;nbsp;개인정보 등을 탐지.&amp;nbsp;탐지 시 해당 문서에 '민감정보 포함' 플래그를 추가하고 관리자에게 알림.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;API 보안:&lt;/b&gt;&amp;nbsp;API Gateway를 도입하여 인증/인가를 중앙에서 처리.&amp;nbsp;&lt;b&gt;IDOR(Insecure Direct Object References) 취약점&lt;/b&gt;&amp;nbsp;방지를 위해,&amp;nbsp;GET /documents/123과 같은 직접적인 ID 접근 시 반드시 현재 사용자가 해당 문서(123)에 대한 접근 권한이 있는지 로직을 추가.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;기술 선택 및 장점 (비교)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;RBAC+ABAC vs. RBAC Only:&lt;/b&gt;&amp;nbsp;순수 RBAC는 'A팀장',&amp;nbsp;'B팀장' 등 비슷한 권한의 역할이 계속 늘어나는&amp;nbsp;&lt;b&gt;'역할 폭증(Role Explosion)'&lt;/b&gt;&amp;nbsp;문제를 야기할 수 있습니다.&amp;nbsp;ABAC를 결합하면 역할 수를 최소한으로 유지하면서 속성 기반의 유연하고 세밀한 정책을 적용할 수 있어 관리 효율성이 높습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;비동기 처리 vs. 동기 처리:&lt;/b&gt;&amp;nbsp;민감정보 스캔은 시간이 걸릴 수 있는 작업입니다.&amp;nbsp;이를 동기적으로 처리하면&amp;nbsp;&lt;b&gt;사용자의 파일 업로드 응답 시간이 길어집니다.&lt;/b&gt;&amp;nbsp;비동기로 처리하면 일단 업로드를 빠르게 완료시킨 후 백그라운드에서 스캔을 수행하므로 사용자 경험(UX)을 해치지 않습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;문제 4: 지식 활용 및 동기 부여&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;핵심 해결안 (키워드)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;활성화 방안:&lt;/b&gt;&amp;nbsp;&lt;b&gt;게이미피케이션(Gamification)&lt;/b&gt;&amp;nbsp;도입.&amp;nbsp;문서 작성,&amp;nbsp;답변 채택,&amp;nbsp;'좋아요' 획득 시&amp;nbsp;&lt;b&gt;포인트&lt;/b&gt;를 부여하고,&amp;nbsp;누적 포인트에 따라&amp;nbsp;&lt;b&gt;뱃지&lt;/b&gt;(Best Answer,&amp;nbsp;Top Contributor)와&amp;nbsp;&lt;b&gt;레벨&lt;/b&gt;을 부여하는 시스템 구현.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;전문가 발굴:&lt;/b&gt;&amp;nbsp;특정 기술 태그(#Java,&amp;nbsp;#Kubernetes)별로&amp;nbsp;&lt;b&gt;사용자의 기여도 점수를 주기적으로 계산&lt;/b&gt;하는 스케줄링 잡(Job)을 실행.&amp;nbsp;점수가 가장 높은 사용자를 해당 분야의&amp;nbsp;&lt;b&gt;SME(사내 전문가)로 자동 추천&lt;/b&gt;하고 프로필에 표시.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;외부 연동:&lt;/b&gt;&amp;nbsp;&lt;b&gt;Slack/Teams 봇(Bot)&lt;/b&gt;&amp;nbsp;개발.&amp;nbsp;사용자가 채널에서&amp;nbsp;/knowledge [검색어]&amp;nbsp;명령어를 입력하면,&amp;nbsp;지식 포털 검색 API를 호출하여 상위 검색 결과를&amp;nbsp;&lt;b&gt;채널에 직접 메시지로 요약&lt;/b&gt;해서 보여줌.&amp;nbsp;사용자가 있는 곳으로 지식을 직접 배달하는 컨셉.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;기술 선택 및 장점 (비교)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;게이미피케이션 vs. 수동 보상:&lt;/b&gt;&amp;nbsp;관리자의 주관적인 판단에 의한 연말 보상보다,&amp;nbsp;활동에 대한&amp;nbsp;&lt;b&gt;즉각적이고 투명한 피드백&lt;/b&gt;을 제공하여 지속적인 참여 동기를 유발하는 데 훨씬 효과적입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;데이터 기반 SME 추천 vs. 직책 기반:&lt;/b&gt;&amp;nbsp;직책이 높다고 항상 특정 기술 전문가인 것은 아닙니다.&amp;nbsp;실제&amp;nbsp;&lt;b&gt;지식 기여도를 기반으로 전문가를 식별&lt;/b&gt;하는 것이 더 객관적이며,&amp;nbsp;숨겨진 고수를 발굴할 기회를 제공합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;협업 도구 연동 vs. 포털 내 기능만 제공:&lt;/b&gt;&amp;nbsp;개발자는 대부분의 시간을 Slack/Teams와 같은 메신저에서 보냅니다.&amp;nbsp;별도의 웹사이트를 방문하는 '허들'을 낮추고,&amp;nbsp;&lt;b&gt;기존 업무 흐름(Workflow)에 자연스럽게 지식 검색을 통합&lt;/b&gt;시켜 활용도를 극대화할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;문제 1: 데이터 모델링 및 저장소 설계 (초안)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;핵심 해결안&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;하이브리드 데이터 아키텍처:&lt;/b&gt;&amp;nbsp;RDBMS + Document Search Engine + Object Storage의 3가지 저장소를 목적에 맞게 조합.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;키워드 및 기술 선택 이유&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;메타데이터 및 관계 관리:&amp;nbsp;PostgreSQL&amp;nbsp;(RDBMS)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;선택 이유:&lt;/b&gt;&amp;nbsp;문서의 소유권,&amp;nbsp;버전,&amp;nbsp;권한 등&amp;nbsp;&lt;b&gt;데이터 무결성이 중요한 정보&lt;/b&gt;를 안정적으로 관리하기 위함.&amp;nbsp;ACID&amp;nbsp;트랜잭션을 보장하여 정합성이 깨질 위험이 적음.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;타 기술 대비 장점:&lt;/b&gt;&amp;nbsp;MongoDB&amp;nbsp;같은 NoSQL 대비,&amp;nbsp;복잡한 사용자-팀-문서 간의&amp;nbsp;&lt;b&gt;관계(Join)를 명확하고 효율적으로 처리&lt;/b&gt;할 수 있음.&amp;nbsp;또한&amp;nbsp;JSONB&amp;nbsp;타입을 지원하여 태그(tags)와 같은 유연한 비정형 메타데이터도 효과적으로 저장 가능.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;본문 검색(Full-Text Search):&amp;nbsp;Elasticsearch&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;선택 이유:&lt;/b&gt;&amp;nbsp;&lt;b&gt;빠르고 정확한 텍스트 검색&lt;/b&gt;이라는 특정 목적에 고도로 특화된 검색 엔진.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;타 기술 대비 장점:&lt;/b&gt;&amp;nbsp;PostgreSQL의 내장 검색 기능보다 훨씬 뛰어난 성능과 검색 품질(Ranking)을 제공.&amp;nbsp;특히,&amp;nbsp;nori와 같은 한글 형태소 분석기 플러그인을 통해&amp;nbsp;&lt;b&gt;한국어 검색의 정확도를 극대화&lt;/b&gt;할 수 있음.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;대용량 파일 저장:&amp;nbsp;AWS S3&amp;nbsp;(Object Storage)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;선택 이유:&lt;/b&gt;&amp;nbsp;동영상,&amp;nbsp;고화질 이미지 등 대용량 바이너리 파일을&amp;nbsp;&lt;b&gt;비용 효율적이고 확장성 있게 저장&lt;/b&gt;하기 위함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;타 기술 대비 장점:&lt;/b&gt;&amp;nbsp;파일을 DB에&amp;nbsp;BLOB&amp;nbsp;타입으로 저장하거나 서버의 파일 시스템에 직접 저장하는 방식은&amp;nbsp;&lt;b&gt;DB 성능을 저하시키고, 서버 확장(Scale-out) 시 파일 동기화 문제를 야기&lt;/b&gt;함.&amp;nbsp;S3는 이러한 문제를 원천적으로 해결하며,&amp;nbsp;CDN 연동도 용이함.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;문제 2: 똑똑한 검색(Smart Search) 기능 (초안)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;핵심 해결안&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;하이브리드 검색:&lt;/b&gt;&amp;nbsp;키워드 검색의 정확성(Precision)과 시맨틱 검색의 재현율(Recall)을 결합하여 검색 품질을 극대화.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;키워드 및 기술 선택 이유&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;시맨틱 검색(Semantic Search):&amp;nbsp;SBERT&amp;nbsp;+&amp;nbsp;FAISS&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;선택 이유:&lt;/b&gt;&amp;nbsp;&quot;서버 접속이 안 돼요&quot;라고 검색해도 &quot;Connection Refused 해결 방법&quot; 문서를 찾아주도록,&amp;nbsp;&lt;b&gt;단순 키워드 매칭이 아닌 문장의 '의미'를 기반으로 검색&lt;/b&gt;하기 위함.&amp;nbsp;SBERT는 문장 임베딩에 특화되어 효율적.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;타 기술 대비 장점:&lt;/b&gt;&amp;nbsp;전통적인&amp;nbsp;TF-IDF&amp;nbsp;방식은 동의어나 유의어를 처리하지 못하는 한계가 명확함.&amp;nbsp;SBERT를 통해 생성된 벡터를&amp;nbsp;FAISS&amp;nbsp;같은 벡터 DB/라이브러리로 검색하면,&amp;nbsp;대규모 문서에서도&amp;nbsp;&lt;b&gt;빠른 속도로 유사도 높은 문서를 탐색&lt;/b&gt;할 수 있음.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;검색 결과 랭킹 개선:&amp;nbsp;Elasticsearch Function Score&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;선택 이유:&lt;/b&gt;&amp;nbsp;검색 결과의 순위를&amp;nbsp;&lt;b&gt;비즈니스 로직에 맞게 동적으로 조정&lt;/b&gt;하기 위함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;타 기술 대비 장점:&lt;/b&gt;&amp;nbsp;단순히 검색어와의 관련도(_score)만으로 정렬하는 것이 아니라,&amp;nbsp;&lt;b&gt;문서의 최신성, 조회 수, '좋아요' 수, 작성자의 신뢰도&lt;/b&gt;&amp;nbsp;등을 가중치로 반영하여 사용자에게 더 유용하고 신뢰도 높은 결과를 상단에 노출시킬 수 있음.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;문제 3: 권한 관리 및 보안 (초안)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;핵심 해결안&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;유연하고 확장 가능한 정책 기반 접근 제어&lt;/b&gt;를 구현하고,&amp;nbsp;민감 정보 유출을 자동화된 방식으로 방지.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;키워드 및 기술 선택 이유&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;권한 모델:&amp;nbsp;ABAC&amp;nbsp;(Attribute-Based Access Control)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;선택 이유:&lt;/b&gt;&amp;nbsp;&quot;A팀의 시니어 개발자만 '인프라' 태그가 붙은 문서를 수정할 수 있다&quot;와 같이&amp;nbsp;&lt;b&gt;복잡하고 동적인 권한 정책&lt;/b&gt;을 유연하게 설정하기 위함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;타 기술 대비 장점:&lt;/b&gt;&amp;nbsp;RBAC(Role-Based) 모델은 역할(Role)이 증가할수록 관리 복잡도가 기하급수적으로 늘어나는 'Role Explosion' 문제가 발생하기 쉬움.&amp;nbsp;ABAC는&amp;nbsp;&lt;b&gt;사용자, 리소스, 환경의 '속성(Attribute)'을 기반&lt;/b&gt;으로 정책을 만들기 때문에 확장성이 훨씬 뛰어남.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;정책 관리:&amp;nbsp;OPA&amp;nbsp;(Open Policy Agent)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;선택 이유:&lt;/b&gt;&amp;nbsp;권한 정책 로직을&amp;nbsp;&lt;b&gt;애플리케이션 코드로부터 분리&lt;/b&gt;하여,&amp;nbsp;정책 변경 시 서비스 재배포가 필요 없게 만들기 위함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;타 기술 대비 장점:&lt;/b&gt;&amp;nbsp;권한 로직을 코드에 하드코딩하는 방식은 유지보수가 어렵고 변경에 취약함.&amp;nbsp;OPA를 사용하면 정책을&amp;nbsp;Rego라는 선언형 언어로 관리하여&amp;nbsp;&lt;b&gt;중앙에서 일관성 있게 정책을 감사하고 업데이트&lt;/b&gt;할 수 있음.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;민감 정보 탐지:&amp;nbsp;AWS Macie&amp;nbsp;또는&amp;nbsp;Google Cloud DLP&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;선택 이유:&lt;/b&gt;&amp;nbsp;사용자가 실수로 문서에&amp;nbsp;&lt;b&gt;API 키, 주민등록번호, 계좌번호 등 민감 정보&lt;/b&gt;를 포함했을 때,&amp;nbsp;이를 자동으로 탐지하고 마스킹/알림 처리하기 위함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;타 기술 대비 장점:&lt;/b&gt;&amp;nbsp;정규표현식(Regex) 기반으로 직접 구현하는 방식은 패턴을 일일이 관리해야 하고 탐지 정확도에 한계가 있음.&amp;nbsp;Macie와 같은&amp;nbsp;&lt;b&gt;머신러닝 기반의 관리형 서비스&lt;/b&gt;를 이용하는 것이 훨씬 정확하고 효율적.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;문제 4: 지식 활용 및 동기 부여 (초안)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;핵심 해결안&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;게임화(Gamification)&lt;/b&gt;&amp;nbsp;요소를 도입하고,&amp;nbsp;기존 업무&amp;nbsp;&lt;b&gt;워크플로우에 자연스럽게 통합&lt;/b&gt;하여 참여를 유도.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;키워드 및 기술 선택 이유&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #1b1c1d; color: #ffffff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;동기 부여:&amp;nbsp;게임화(Gamification)&amp;nbsp;- 포인트, 뱃지, 리더보드&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;선택 이유:&lt;/b&gt;&amp;nbsp;지식 공유 활동에 대한&amp;nbsp;&lt;b&gt;즉각적이고 가시적인 보상&lt;/b&gt;을 제공하여 구성원의 참여 동기를 내재적으로 자극.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;접근 방식:&lt;/b&gt;&amp;nbsp;문서 작성,&amp;nbsp;질문에 대한 답변 채택,&amp;nbsp;'좋아요' 획득 등의 활동에 포인트를 부여.&amp;nbsp;누적 포인트에 따라 뱃지를 수여하고,&amp;nbsp;주간/월간 리더보드를 통해 선의의 경쟁을 유도.&amp;nbsp;이는 단순한 금전적 보상보다&amp;nbsp;&lt;b&gt;성취감과 인정의 욕구&lt;/b&gt;를 채워주어 지속적인 참여를 이끌어냄.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;전문가 식별:&amp;nbsp;SME(Subject-Matter Expert) Score&amp;nbsp;알고리즘&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;선택 이유:&lt;/b&gt;&amp;nbsp;특정 기술 분야에 대해&amp;nbsp;&lt;b&gt;누가 전문가인지 데이터를 기반으로 식별&lt;/b&gt;하고,&amp;nbsp;도움이 필요한 사람과 연결해주기 위함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;접근 방식:&lt;/b&gt;&amp;nbsp;특정 태그(#Java,&amp;nbsp;#Kubernetes)가 달린 문서 작성 수,&amp;nbsp;해당 분야 질문에 대한 채택 답변 수 등을 가중치로 계산하여 점수화.&amp;nbsp;이를 통해 시스템이 &quot;이 질문은 OOO님에게 물어보세요&quot;라고 추천해줄 수 있음.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;워크플로우 통합:&amp;nbsp;Slack / MS Teams Bot&amp;nbsp;연동&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;선택 이유:&lt;/b&gt;&amp;nbsp;지식 포털에 접속하는&amp;nbsp;&lt;b&gt;별도의 행동 없이, 매일 사용하는 협업 툴 안에서&lt;/b&gt;&amp;nbsp;지식을 검색하고 공유하게 만들기 위함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;타 기술 대비 장점:&lt;/b&gt;&amp;nbsp;이메일 알림은 스팸으로 인식되기 쉽지만,&amp;nbsp;챗봇은&amp;nbsp;/knowledge [검색어]와 같은 명령어로&amp;nbsp;&lt;b&gt;필요한 순간에 즉시 정보를 가져오는(On-demand) 상호작용&lt;/b&gt;이 가능.&amp;nbsp;채널에서 오가는 대화를 분석해 관련 지식 문서를 자동으로 추천하는 등 프로액티브한 기능 구현도 가능함.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>etc</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/236</guid>
      <comments>https://shoney.tistory.com/entry/%ED%86%B5%ED%95%A9-%EC%A7%80%EC%8B%9D-%EA%B4%80%EB%A6%AC-%ED%8F%AC%ED%84%B8#entry236comment</comments>
      <pubDate>Mon, 20 Oct 2025 13:15:57 +0900</pubDate>
    </item>
    <item>
      <title>3. 검색 및 활용 기능 설계</title>
      <link>https://shoney.tistory.com/entry/3-%EA%B2%80%EC%83%89-%EB%B0%8F-%ED%99%9C%EC%9A%A9-%EA%B8%B0%EB%8A%A5-%EC%84%A4%EA%B3%84</link>
      <description>&lt;p style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;효과적인 지식 검색은 **정보 검색(IR)**과 **AI 기반 의미 추론(NLP)**의 결합으로 이루어진다.&lt;/p&gt;
&lt;p style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 기술 스택 제안&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 스토리지: PostgreSQL (문서 메타데이터 및 권한 관리)&lt;/li&gt;
&lt;li&gt;콘텐츠 저장: Object Storage (예: AWS S3, 내부 NAS)&lt;/li&gt;
&lt;li&gt;벡터 검색: Elasticsearch + OpenSearch Vector Store 또는 ChromaDB&lt;/li&gt;
&lt;li&gt;임베딩 모델: OpenAI text-embedding-3-small 또는 KoSimCSE (한국어 중심 서비스 시)&lt;/li&gt;
&lt;li&gt;애플리케이션 계층: FastAPI + React 기반 Web UI&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 검색 알고리즘 구조&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자가 검색어 입력&lt;/li&gt;
&lt;li&gt;키워드 기반 1차 필터링 (Elasticsearch Full Text Search)&lt;/li&gt;
&lt;li&gt;입력 쿼리를 임베딩 벡터로 변환 &amp;rarr; Knowledge 콘텐츠의 임베딩과 유사도 계산&lt;/li&gt;
&lt;li&gt;relevance_score = &amp;alpha; * semantic_score + &amp;beta; * keyword_score + &amp;gamma; * popularity_score&lt;br /&gt;(&amp;alpha;, &amp;beta;, &amp;gamma;는 가중치 조정 파라미터)&lt;/li&gt;
&lt;li&gt;결과 랭킹 후 상위 N개 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 검색 품질 향상 방안&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 Context 기반 검색 (부서/직무/최근 조회 문서 가중 처리)&lt;/li&gt;
&lt;li&gt;문서의 최신성 점수(time decay factor) 적용&lt;/li&gt;
&lt;li&gt;결과 설명(Explainability)을 위한 스니펫 생성 오버뷰 제공&lt;/li&gt;
&lt;li&gt;&amp;ldquo;검색 &amp;rarr; 열람 &amp;rarr; 피드백&amp;rdquo; 로그를 학습 데이터로 삼아 지속적 재랭킹&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이와 같은 설계를 통해 시스템은 단순 검색 툴을 넘어,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;지식 발견과 의사결정 지원이 가능한 지능형 포탈&lt;/b&gt;로 발전할 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;RDB, Elasticsearch, 그리고 벡터 시멘틱 검색을 조합해 &amp;ldquo;하이브리드 검색&amp;rdquo; 시스템을 구축하면, 사용자는 키워드 기반뿐 아니라 의미(컨텍스트) 기반으로도 원하는 정보를 매우 효과적으로 찾을 수 있다.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;하이브리드 검색 시스템 설계 원칙&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;검색 엔진별 역할 분담&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RDB: 메타데이터 관리 및 권한 제어, 관계성 질의(SQL), 정형 필터링 수행에 특화.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Elasticsearch: 대량의 텍스트 필드에 대한 빠른 full-text 검색 및 BM25 같은 스코어링 기반 랭킹에 강점.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;벡터 DB (혹은 Elasticsearch의 벡터 필드): 문서 임베딩 기반 의미 유사성 판단, 연관 정보 추천 등 AI 기반 검색 적용.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;통합 검색 프로세스 예시&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1차 필터:&lt;br /&gt;RDB에서 사용자 권한&amp;middot;문서 타입&amp;middot;날짜 등 기본 조건으로 후보 문서 집합을 빠르게 추림.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;2차 키워드 검색:&lt;br /&gt;Elasticsearch에서 BM25 점수 기반으로 키워드 검색 수행, 후보군을 랭킹.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;3차 벡터 임베딩 검색:&lt;br /&gt;쿼리를 임베딩벡터로 변환하여, 문서의 임베딩 벡터와 코사인 유사도/ANN(Approximate Nearest Neighbor) 유사도 기반으로 의미적 스코어 산출.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;결과 통합:&lt;br /&gt;두 결과를&lt;span&gt;&amp;nbsp;&lt;/span&gt;normalized score = &amp;alpha; * 벡터 유사도 + &amp;beta; * BM25 점수&lt;span&gt;&amp;nbsp;&lt;/span&gt;식으로 조합하거나 Reciprocal Rank Fusion(RRF) 알고리즘 적용.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;사용 시 유의사항 및 Best Practice&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;벡터 임베딩 데이터 관리&lt;/b&gt;:&lt;br /&gt;RDB에&lt;span&gt;&amp;nbsp;&lt;/span&gt;embedding&lt;span&gt;&amp;nbsp;&lt;/span&gt;컬럼 추가(예: PostgreSQL pgvector) 후, 텍스트 데이터를 임베딩하여 동기화, 혹은 별도 벡터 DB(Chroma, Pinecone)에 키 기반 연동.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대량 데이터 처리&lt;/b&gt;:&lt;br /&gt;임베딩 사전 계산 및 배치 처리로 검색 지연 최소화.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;&lt;b&gt;검색 결과 집계/재랭킹&lt;/b&gt;:&lt;br /&gt;키워드 일치와 의미 유사도를 함께 고려한 re-ranking 적용, 스코어 normalize 후 가중치 설정.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용자 Context 활용&lt;/b&gt;:&lt;br /&gt;최근 검색, 부서, 업무 이력 등을 바탕으로 랭킹 알고리즘에 가중치 부여.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;실제 활용 예시&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 특정 키워드와 자연어 질문(&amp;ldquo;Python 데이터 분석 실무 문서&amp;rdquo;)을 입력하면,
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;RDB에서 접근 권한/부서&amp;middot;날짜 필터로 1차 후보 문서 집합 검색&lt;/li&gt;
&lt;li&gt;Elasticsearch에서 키워드 일치도 기반 랭킹 적용&lt;/li&gt;
&lt;li&gt;벡터 DB/Elasticsearch 벡터 기능으로 의미 유사 문서 발견 및 랭킹&lt;/li&gt;
&lt;li&gt;둘을 최종 랭킹 알고리즘으로 합쳐 결과 제공&lt;br /&gt;&amp;rarr; 키워드&amp;middot;의미&amp;middot;접근권한&amp;middot;문서 최신성 등 종합적으로 우수한 결과 도출.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;지식관리 포탈&amp;middot;엔터프라이즈 검색에선 위와 같은 &amp;ldquo;하이브리드 검색&amp;rdquo; 구조가 가장 널리 쓰이며, 실제 대한민국 대기업/글로벌 IT 기업에서 필수적으로 채택하는 모델이다.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;​&lt;/p&gt;</description>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/235</guid>
      <comments>https://shoney.tistory.com/entry/3-%EA%B2%80%EC%83%89-%EB%B0%8F-%ED%99%9C%EC%9A%A9-%EA%B8%B0%EB%8A%A5-%EC%84%A4%EA%B3%84#entry235comment</comments>
      <pubDate>Mon, 20 Oct 2025 13:11:52 +0900</pubDate>
    </item>
    <item>
      <title>2. 정보 구조 설계 (데이터 모델링)</title>
      <link>https://shoney.tistory.com/entry/2-%EC%A0%95%EB%B3%B4-%EA%B5%AC%EC%A1%B0-%EC%84%A4%EA%B3%84-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AA%A8%EB%8D%B8%EB%A7%81</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;지식 관리 포탈의 핵심은 콘텐츠를 표준화하고 관계를 정의하는 것이다. 다음은 대표적인 **엔터티 관계 설계(ERD 개요)**이다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Entity: Knowledge&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주요 속성:&lt;span&gt;&amp;nbsp;&lt;/span&gt;id,&lt;span&gt;&amp;nbsp;&lt;/span&gt;title,&lt;span&gt;&amp;nbsp;&lt;/span&gt;content,&lt;span&gt;&amp;nbsp;&lt;/span&gt;summary,&lt;span&gt;&amp;nbsp;&lt;/span&gt;author_id,&lt;span&gt;&amp;nbsp;&lt;/span&gt;created_at,&lt;span&gt;&amp;nbsp;&lt;/span&gt;updated_at,&lt;span&gt;&amp;nbsp;&lt;/span&gt;category_id,&lt;span&gt;&amp;nbsp;&lt;/span&gt;version&lt;/li&gt;
&lt;li&gt;설명: 지식의 기본 단위. 텍스트, 파일, 코드 등 형태를 불문하고 동일 구조로 관리.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Entity: User&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주요 속성:&lt;span&gt;&amp;nbsp;&lt;/span&gt;user_id,&lt;span&gt;&amp;nbsp;&lt;/span&gt;name,&lt;span&gt;&amp;nbsp;&lt;/span&gt;department,&lt;span&gt;&amp;nbsp;&lt;/span&gt;role,&lt;span&gt;&amp;nbsp;&lt;/span&gt;email&lt;/li&gt;
&lt;li&gt;설명: 작성자&amp;middot;검토자&amp;middot;조회자 구분. RBAC(Role Based Access Control)을 지원.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Entity: Tag&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주요 속성:&lt;span&gt;&amp;nbsp;&lt;/span&gt;tag_id,&lt;span&gt;&amp;nbsp;&lt;/span&gt;name,&lt;span&gt;&amp;nbsp;&lt;/span&gt;description&lt;/li&gt;
&lt;li&gt;설명: 지식 분류를 위한 보조 체계. 자동 태깅 기능과 수동 태깅 병행.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Entity: Knowledge_Tag (연결 테이블)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;knowledge_id,&lt;span&gt;&amp;nbsp;&lt;/span&gt;tag_id&lt;/li&gt;
&lt;li&gt;설명: N:M 관계를 구현하여 지식과 태그의 다대다 연결 관리.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Entity: Feedback&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주요 속성:&lt;span&gt;&amp;nbsp;&lt;/span&gt;feedback_id,&lt;span&gt;&amp;nbsp;&lt;/span&gt;knowledge_id,&lt;span&gt;&amp;nbsp;&lt;/span&gt;user_id,&lt;span&gt;&amp;nbsp;&lt;/span&gt;rating,&lt;span&gt;&amp;nbsp;&lt;/span&gt;comment,&lt;span&gt;&amp;nbsp;&lt;/span&gt;created_at&lt;/li&gt;
&lt;li&gt;설명: 문서 품질 개선을 위한 피드백 및 평판 데이터.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Entity: SearchLog&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주요 속성:&lt;span&gt;&amp;nbsp;&lt;/span&gt;search_id,&lt;span&gt;&amp;nbsp;&lt;/span&gt;user_id,&lt;span&gt;&amp;nbsp;&lt;/span&gt;keyword,&lt;span&gt;&amp;nbsp;&lt;/span&gt;timestamp&lt;/li&gt;
&lt;li&gt;설명: 검색 사용자 행동 기반 추천이나 인기 문서 분석에 활용.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 구조의 핵심은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;지식(콘텐츠)을 중심으로 관계형 메타데이터를 구성하여 확장성&lt;/b&gt;을 유지하는 것이다.&lt;br /&gt;추후 LLM 기반 이벤트 로그나 임베딩 데이터를 별도 테이블(embedding_vector)로 분리하면, 의미 기반 검색에도 대응이 가능하다.&lt;/p&gt;</description>
      <category>etc</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/234</guid>
      <comments>https://shoney.tistory.com/entry/2-%EC%A0%95%EB%B3%B4-%EA%B5%AC%EC%A1%B0-%EC%84%A4%EA%B3%84-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AA%A8%EB%8D%B8%EB%A7%81#entry234comment</comments>
      <pubDate>Mon, 20 Oct 2025 13:10:28 +0900</pubDate>
    </item>
    <item>
      <title>1. 시스템 분석 및 요구사항 정의</title>
      <link>https://shoney.tistory.com/entry/1-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%B6%84%EC%84%9D-%EB%B0%8F-%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD-%EC%A0%95%EC%9D%98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;사내에는 다양한 형태의 지식이 부서별로 흩어져 있고, 중복 작성이나 검색의 어려움으로 인해 생산성이 저하되고 있다. 지식 관리 포탈의 목표는 **&amp;lsquo;지식을 중앙집중적으로 관리하고, 필요할 때 누구나 쉽게 찾아 활용할 수 있는 환경&amp;rsquo;**을 구축하는 것이다.&lt;/p&gt;
&lt;p style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결해야 할 주요 문제점 및 기능적 요구사항&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;지식 분산 및 중복 관리 문제&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제: 부서별 문서 서버, 메신저 공유 파일, 개인 노트 등 다양한 경로에 지식이 분산되어 있음.&lt;/li&gt;
&lt;li&gt;요구사항:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문서, 회의록, 코드, 링크 등 다양한 지식 자산을 통합 저장할 수 있는 리포지토리 구축&lt;/li&gt;
&lt;li&gt;중복 지식 감지 기능 (문서 유사도 계산 기반)&lt;/li&gt;
&lt;li&gt;버전 관리 및 개정 이력 추적&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;검색 효율성 부족&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제: 제목 기반 검색만으로는 관련 문서를 찾기 어려움. 동일 주제도 표현 방식이 다름.&lt;/li&gt;
&lt;li&gt;요구사항:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;키워드 기반 + 의미 기반 통합 검색 기능&lt;/li&gt;
&lt;li&gt;태그 자동 부여, 추천 검색어 제공&lt;/li&gt;
&lt;li&gt;사용자별 검색 히스토리 및 연관 문서 추천&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지식 활용도 저하&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제: 작성된 지식이 재활용되지 않고, 문서만 쌓이는 형태로 정체됨.&lt;/li&gt;
&lt;li&gt;요구사항:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;업무 시스템(예: Jira, Slack, Teams)과 연동하여 자동 참조 기능 지원&lt;/li&gt;
&lt;li&gt;LLM 기반 Q&amp;amp;A 챗봇 메뉴로 검색 보완&lt;/li&gt;
&lt;li&gt;지식 평판(추천, 조회 수, 최신성 등) 기반 품질 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: oklch(0.9902 0.004 106.47); color: oklch(0.3039 0.04 213.68); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이를 통해 단순한 문서 관리 수준을 넘어,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;지식의 생성 &amp;rarr; 저장 &amp;rarr; 탐색 &amp;rarr; 재활용 &amp;rarr; 개선&lt;/b&gt;으로 이어지는 피드백 구조를 내재화할 수 있다.&lt;/p&gt;</description>
      <category>etc</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/233</guid>
      <comments>https://shoney.tistory.com/entry/1-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%B6%84%EC%84%9D-%EB%B0%8F-%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD-%EC%A0%95%EC%9D%98#entry233comment</comments>
      <pubDate>Mon, 20 Oct 2025 13:09:41 +0900</pubDate>
    </item>
    <item>
      <title>LangGraph란?</title>
      <link>https://shoney.tistory.com/entry/LangGraph%EB%9E%80</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;LangGraph &amp;ndash; LangChain에서 더 똑똑하게 멀티스텝 처리하는 법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain을 사용하면서 &lt;b&gt;&quot;에이전트가 생각보다 복잡한 작업을 잘 못하네?&quot;&lt;/b&gt; 라는 느낌을 받은 적이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 검색이나 API 호출 정도는 잘하지만, 여러 단계에 걸친 작업을 하면 이상한 답을 내놓거나, 비효율적으로 동작하는 경우가 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 프로젝트에서 &lt;b&gt;LangGraph&lt;/b&gt;를 살짝 찍먹(?) 해봤는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 생각보다 &lt;b&gt;LangChain의 부족한 부분을 꽤 잘 보완해줄 수 있는 기능&lt;/b&gt; 같아서 정리해본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;LangGraph란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangGraph는 &lt;b&gt;LangChain의 멀티스텝 처리 기능을 강화하기 위한 라이브러리&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 LangChain은 &lt;b&gt;한 번의 프롬프트 &amp;rarr; 답변 생성 &amp;rarr; 끝&lt;/b&gt; 이라는 단순한 흐름으로 동작하는 경우가 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 현실에서는 AI가 &lt;b&gt;여러 단계를 거쳐야 하는 작업&lt;/b&gt;이 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;❓ &lt;b&gt;&quot;서울에서 뉴욕까지 가는 최적의 비행편 찾아줘&quot;&lt;/b&gt; 라고 하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ 먼저 서울에서 뉴욕으로 가는 항공편 검색&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ 가격 비교&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3️⃣ 경유 여부 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4️⃣ 가장 좋은 옵션 추천&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 여러 단계의 작업이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangGraph는 &lt;b&gt;이런 멀티스텝 프로세스를 쉽게 구성할 수 있도록 돕는 프레임워크&lt;/b&gt;다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;LangGraph의 주요 특징&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1. &lt;b&gt;그래프 기반 처리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangGraph는 이름 그대로 &lt;b&gt;&quot;Graph(그래프)&quot; 구조를 기반으로 동작&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 LangChain은 &lt;b&gt;단순히 프롬프트를 주고받는 선형 구조&lt;/b&gt;였지만, LangGraph는 &lt;b&gt;노드(Node)와 엣지(Edge)를 활용해서 유연한 흐름을 만들 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;노드(Node)&lt;/b&gt;: AI가 수행해야 하는 개별 작업 (예: 검색, 데이터 분석, 요약)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;엣지(Edge)&lt;/b&gt;: 작업 간의 연결 관계 (예: 검색 결과를 분석 후, 분석된 데이터를 요약)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;즉, AI가 마치 플로우차트처럼 단계별로 작업을 수행할 수 있도록 만든다!&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;⚡ 2. &lt;b&gt;병렬 처리 가능&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 LangChain에서는 하나의 작업이 끝나야 다음 작업이 가능했지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangGraph에서는 여러 작업을 동시에 수행할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;❓ &lt;b&gt;&quot;오늘 서울과 뉴욕의 날씨를 각각 찾아줘&quot;&lt;/b&gt; 라고 하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 LangChain 방식:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  서울 날씨 검색 &amp;rarr; 뉴욕 날씨 검색 &amp;rarr; 답변 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangGraph 방식:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚡ 서울 &amp;amp; 뉴욕 날씨 &lt;b&gt;동시에&lt;/b&gt; 검색 &amp;rarr; 결과 합쳐서 답변&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;독립적인 작업을 병렬로 실행할 수 있어 속도가 훨씬 빨라진다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3. &lt;b&gt;더 똑똑한 의사결정 가능&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangGraph에서는 AI가 상황에 따라 &lt;b&gt;어떤 노드를 선택할지 동적으로 결정할 수도 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 모든 작업을 일괄적으로 처리하는 게 아니라, &lt;b&gt;필요한 작업만 선택적으로 실행할 수도 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;❓ &lt;b&gt;&quot;서울의 날씨 알려줘&quot;&lt;/b&gt; 라고 했을 때,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색이 필요하면 &lt;b&gt;날씨 API 호출&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;데이터가 이미 캐시에 있으면 &lt;b&gt;바로 응답&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;검색 결과가 이상하면 &lt;b&gt;추가적인 확인 과정 수행&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  즉, AI가 &lt;b&gt;유연하게 상황을 판단하고, 필요한 작업만 실행&lt;/b&gt;할 수 있다!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  4. &lt;b&gt;다양한 AI 시스템과 결합 가능&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangGraph는 &lt;b&gt;LangChain뿐만 아니라, OpenAI, Hugging Face, 기타 AI 프레임워크와도 쉽게 연동 가능&lt;/b&gt;하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 기존 AI 시스템을 더 똑똑하게 만들기 위한 &lt;b&gt;&quot;업그레이드 키트&quot;&lt;/b&gt; 같은 느낌이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;LangGraph 코드 예제 (간단한 AI 워크플로우 만들기)&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END

# 간단한 State 정의
class WorkflowState:
    user_input: str
    search_result: str = None
    summary: str = None

# LLM 설정
llm = ChatOpenAI(model=&quot;gpt-4-turbo&quot;)

# 노드 1: 검색 수행
def search(state):
    query = state.user_input
    print(f&quot;  검색 실행: {query}&quot;)
    return {&quot;search_result&quot;: f&quot;{query}에 대한 가짜 검색 결과&quot;}

# 노드 2: 검색 결과 요약
def summarize(state):
    text = state.search_result
    print(f&quot;  요약 실행: {text}&quot;)
    return {&quot;summary&quot;: f&quot;{text} 요약된 내용&quot;}

# 그래프 설정
workflow = StateGraph(WorkflowState)
workflow.add_node(&quot;search&quot;, search)
workflow.add_node(&quot;summarize&quot;, summarize)

# 검색 후 요약하는 흐름 설정
workflow.set_entry_point(&quot;search&quot;)
workflow.add_edge(&quot;search&quot;, &quot;summarize&quot;)
workflow.add_edge(&quot;summarize&quot;, END)

# 실행
app = workflow.compile()
result = app.invoke({&quot;user_input&quot;: &quot;LangGraph란 무엇인가?&quot;})
print(&quot;최종 결과:&quot;, result[&quot;summary&quot;])

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ &lt;b&gt;코드 설명&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;WorkflowState&lt;/b&gt;: LangGraph에서 사용할 상태를 정의 (user_input &amp;rarr; search_result &amp;rarr; summary)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;search() 노드&lt;/b&gt;: 사용자의 입력을 기반으로 가짜 검색 결과를 반환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;summarize() 노드&lt;/b&gt;: 검색된 결과를 요약&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LangGraph의 그래프 생성&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색 수행 &amp;rarr; 요약 실행 &amp;rarr; 최종 결과 반환하는 흐름을 만든다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;*app.invoke()**를 호출하면 전체 워크플로우가 실행됨&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;이제 AI가 검색 후 요약까지 자동으로 수행할 수 있다!&lt;/b&gt;  &lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;LangGraph를 활용하면 뭐가 좋아?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;복잡한 AI 플로우를 쉽게 설계&lt;/b&gt; &amp;ndash; 멀티스텝 프로세스를 효율적으로 구현 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;작업 속도 향상&lt;/b&gt; &amp;ndash; 병렬 실행을 통해 처리 속도 개선&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;더 스마트한 AI 만들기&lt;/b&gt; &amp;ndash; 상황에 맞는 유연한 의사결정 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;기존 LangChain 시스템과 연동 가능&lt;/b&gt; &amp;ndash; 기존 프로젝트에 바로 적용 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;LangGraph를 어떻게 활용하면 좋을까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;사용 사례 예제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;1. 복잡한 데이터 분석 자동화&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 수집 &amp;rarr; 정리 &amp;rarr; 분석 &amp;rarr; 요약하는 전체 과정을 자동화&lt;/li&gt;
&lt;li&gt;예: 뉴스 기사 크롤링 후, 주요 내용을 추출해서 요약&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;2. AI 비서 시스템&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LangGraph를 활용하면 AI 비서가 &lt;b&gt;여러 작업을 동시에 처리하면서 최적의 답을 줄 수 있도록 설계 가능&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;예: 일정 확인 &amp;rarr; 이메일 요약 &amp;rarr; 미팅 준비 자동화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;3. AI 기반 검색 및 추천 시스템&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자의 질문을 분석 &amp;rarr; 필요한 정보 검색 &amp;rarr; 검색 결과 최적화 &amp;rarr; 최종 추천&lt;/li&gt;
&lt;li&gt;예: 여행 추천 시스템 (날씨, 가격, 비행기 일정 분석 후 최적의 일정 추천)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;한줄 요약 ✨&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;LangGraph는 LangChain의 한계를 극복하고, AI가 더 복잡한 멀티스텝 작업을 할 수 있도록 돕는 강력한 도구다!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 살짝 찍먹한 수준이지만, 앞으로 &lt;b&gt;더 복잡한 AI 프로세스를 설계할 때 LangGraph를 적극 활용해보면 좋을 것 같다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 팀에서도 AI 워크플로우를 만들 때, LangChain만 쓸 게 아니라 LangGraph까지 고려해보면 어떨까?  &lt;/p&gt;</description>
      <category>AI</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/231</guid>
      <comments>https://shoney.tistory.com/entry/LangGraph%EB%9E%80#entry231comment</comments>
      <pubDate>Fri, 21 Mar 2025 15:45:54 +0900</pubDate>
    </item>
    <item>
      <title>AI 에이전트 vs LangChain 에이전트</title>
      <link>https://shoney.tistory.com/entry/AI-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-vs-LangChain-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;최근 프로젝트에서 LangChain을 활용해 RAG 기반 검색 시스템을 테스트할 일이 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain을 다루다 보니 **&quot;LangChain의 에이전트&quot;**라는 개념이 나왔는데, 처음엔 그냥 &lt;b&gt;&quot;AI 에이전트랑 같은 거 아니야?&quot;&lt;/b&gt; 라고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 막상 써보니 &lt;b&gt;LangChain 에이전트는 우리가 흔히 말하는 AI 에이전트랑은 조금 다른 개념&lt;/b&gt;이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 팀원들 중에서도 나처럼 헷갈리는 분이 있을까 봐, 이번 기회에 한 번 정리해본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;AI 에이전트란?  &lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 간단히 말하면 **&quot;스스로 판단하고 행동하는 AI 시스템&quot;**이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경에서 정보를 받아들이고, 목표를 달성하기 위해 스스로 판단하여 동작하는 것이 핵심이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;AI 에이전트의 특징&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;자율성&lt;/b&gt; &amp;ndash; 사람이 직접 명령하지 않아도 스스로 판단하여 동작&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;목표 중심&lt;/b&gt; &amp;ndash; 주어진 목표를 달성하기 위해 움직임&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;환경과 상호작용&lt;/b&gt; &amp;ndash; 입력을 받아서 적절한 반응을 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;의사결정&lt;/b&gt; &amp;ndash; 단순 규칙이 아니라, 상황에 맞게 최적의 행동을 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;예제&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;챗봇&lt;/b&gt; &amp;ndash; 사용자의 질문을 이해하고 답변을 제공&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AI 비서&lt;/b&gt; &amp;ndash; 일정 관리, 이메일 정리, 정보 검색 등&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자율주행 AI&lt;/b&gt; &amp;ndash; 도로 상황을 분석하고 최적의 경로 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, AI 에이전트는 &lt;b&gt;자율적으로 동작하는 AI 시스템 전반을 의미하는 넓은 개념&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;LangChain 에이전트란?  ️&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 LangChain의 에이전트도 AI 에이전트랑 비슷한 개념이라고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 LangChain을 써보면서 느낀 점은, &lt;b&gt;LangChain 에이전트는 단순한 AI가 아니라, 외부 도구를 활용할 수 있는 강력한 시스템&lt;/b&gt;이라는 거였다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;LangChain에서 '에이전트'가 필요한 이유&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언어 모델(LLM)은 기본적으로 **&quot;똑똑한 텍스트 생성기&quot;**다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 질문을 하면 가장 적절한 텍스트를 출력할 수 있지만, &lt;b&gt;실제로 행동을 수행할 수는 없다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;❓ &lt;b&gt;&quot;서울 날씨 어때?&quot;&lt;/b&gt; 라고 물어보면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;&quot;오늘 서울의 날씨는 맑습니다.&quot;&lt;/b&gt; 라고 대답할 수 있지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실제로 날씨를 검색하는 기능은 없다!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain의 에이전트는 바로 이런 **&quot;행동 수행&quot;**을 가능하게 해준다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;LangChain 에이전트의 동작 방식&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain 에이전트는 언어 모델을 &lt;b&gt;추론 엔진&lt;/b&gt;으로 사용해서 다음과 같은 과정으로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;어떤 행동을 해야 하는지 결정&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;서울의 날씨 알려줘&quot; &amp;rarr; 단순 답변이 아니라 &lt;b&gt;&quot;날씨를 검색해야겠군!&quot;&lt;/b&gt; 이라고 판단&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ &lt;b&gt;필요한 입력을 정리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;날씨를 검색하려면, 검색 엔진에 '서울 날씨'를 입력해야겠어!&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3️⃣ &lt;b&gt;도구를 사용해서 행동 수행&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;언어 모델 자체는 검색을 못 하지만, LangChain을 통해 &lt;b&gt;API 호출, 검색 엔진 사용 등 외부 도구를 실행&lt;/b&gt;할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4️⃣ &lt;b&gt;결과를 확인하고 추가 행동 결정&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;이 정보로 충분한가? 추가 검색이 필요할까?&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5️⃣ &lt;b&gt;최종적으로 사용자에게 답변 제공&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 과정이 끝나면 사용자에게 답변 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;LangChain 에이전트의 강점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain의 에이전트는 단순한 답변 생성기가 아니라, &lt;b&gt;외부 도구와 결합하여 더 강력한 AI 시스템을 구축&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;LangChain 에이전트가 할 수 있는 일&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;웹 검색&lt;/b&gt; &amp;ndash; 구글 검색을 실행하고 최신 정보를 가져오기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;DB 조회&lt;/b&gt; &amp;ndash; 내부 데이터베이스에서 필요한 정보 찾기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;API 호출&lt;/b&gt; &amp;ndash; 특정 API를 호출하여 필요한 데이터 가져오기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;계산 수행&lt;/b&gt; &amp;ndash; 단순한 산수부터 복잡한 연산까지 수행 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;AI 에이전트 vs LangChain 에이전트 차이점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분 AI 에이전트 LangChain 에이전트&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;정의&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;자율적으로 동작하는 AI 시스템&lt;/td&gt;
&lt;td&gt;LangChain을 활용해 도구를 사용하는 AI 시스템&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;기반 기술&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;머신러닝, 딥러닝, LLM 등 다양함&lt;/td&gt;
&lt;td&gt;LLM(GPT 등) + LangChain 프레임워크&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;자율성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;자체적인 학습과 규칙 기반 동작&lt;/td&gt;
&lt;td&gt;외부 도구를 사용하여 더 똑똑하게 동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;도구 사용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;특정 도구를 직접 사용하지 않음&lt;/td&gt;
&lt;td&gt;API, 검색 엔진, 데이터베이스, 계산기 등을 활용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;챗봇, AI 비서, 자율주행&lt;/td&gt;
&lt;td&gt;검색 엔진 연동, 데이터 조회, 문서 요약&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;이 글을 쓰게 된 이유...&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트에서 LangChain을 활용하면서 처음엔 **&quot;LangChain 에이전트 = AI 에이전트&quot;**라고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제로 LangChain을 사용해보니, &lt;b&gt;단순한 답변 생성기가 아니라 외부 도구까지 활용할 수 있는 강력한 시스템&lt;/b&gt;이라는 걸 깨달았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain을 도입하면 AI가 단순히 &lt;b&gt;질문에 답하는 것이 아니라, 직접 정보를 검색하고 데이터를 조회하며 더 똑똑한 응답을 제공&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 팀에서도 AI 시스템을 설계할 때, 단순 답변형 AI를 만들지 않을 거라면 &lt;b&gt;LangChain의 에이전트 개념을 적극적으로 활용&lt;/b&gt;해보는 게 좋을 것 같다!  &lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;한 줄 요약&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;AI 에이전트&quot;는 넓은 개념이고, &quot;LangChain 에이전트&quot;는 AI가 외부 도구를 활용할 수 있도록 만든 시스템이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 나처럼 헷갈렸던 분이 있었다면, 이 글이 이해하는 데 도움이 되었길!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 더 깊이 테스트해보고, 팀원들과도 LangChain 활용 방안을 논의해보면 좋을 것 같다.  &lt;/p&gt;</description>
      <category>AI</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/230</guid>
      <comments>https://shoney.tistory.com/entry/AI-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-vs-LangChain-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8#entry230comment</comments>
      <pubDate>Fri, 21 Mar 2025 15:10:13 +0900</pubDate>
    </item>
    <item>
      <title>LangChain Agent vs LangGraph: Super-Sub 에이전트 구조 구현 비교</title>
      <link>https://shoney.tistory.com/entry/LangChain-Agent-vs-LangGraph-Super-Sub-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EA%B5%AC%EC%A1%B0-%EA%B5%AC%ED%98%84-%EB%B9%84%EA%B5%90</link>
      <description>&lt;h1&gt;LangChain Agent vs LangGraph: Super-Sub 에이전트 구조 구현 비교&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요, 기술 블로그 독자 여러분! 오늘은 AI 에이전트 시스템을 구축할 때 많이 사용되는 두 가지 접근 방식에 대해 알아보려고 합니다. 바로 LangChain Agent만을 사용한 Super-Sub 구조와 LangGraph를 활용한 Super-Sub 구조의 차이점입니다. 두 방식 모두 복잡한 작업을 여러 전문 에이전트에게 분배하는 구조를 가지고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;있지만, 구현 방식과 특징에는 큰 차이가 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LangChain Agent로 구현하는 Super-Sub 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain은 대규모 언어 모델(LLM)을 활용한 애플리케이션 개발을 위한 프레임워크로, 다양한 도구와 에이전트를 쉽게 구현할 수 있게 해줍니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구현 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain만으로 Super-Sub 구조를 구현할 때는 주로 다음과 같은 방식을 사용합니다:&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;class SuperAgent:
    def __init__(self, model_name: str = DEFAULT_MODEL):
        self.llm = ChatGoogleGenerativeAI(model=model_name)

        # 서브 에이전트들 초기화
        self.db_agent = DBAgent(self.llm)
        self.search_agent = SearchAgent(self.llm)
        self.rag_agent = RAGAgent()

        # 슈퍼 에이전트용 도구 설정
        self.tools = [
            Tool(
                name=&quot;DB_작업&quot;,
                func=self.db_agent.run,
                description=&quot;데이터베이스 관련 작업이 필요할 때 사용&quot;
            ),
            Tool(
                name=&quot;정보_검색&quot;,
                func=self.search_agent.run,
                description=&quot;웹에서 정보를 검색해야 할 때 사용&quot;
            ),
            Tool(
                name=&quot;문서_검색(RAG)&quot;,
                func=self.rag_agent.answer_question,
                description=&quot;저장된 문서에서 관련 정보를 검색할 때 사용&quot;
            )
        ]

        # 슈퍼 에이전트 생성
        prompt = hub.pull(&quot;hwchase17/react&quot;)
        self.agent = create_react_agent(self.llm, self.tools, prompt)
        self.agent_executor = AgentExecutor(agent=self.agent, tools=self.tools, verbose=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식에서는 슈퍼 에이전트가 Tool 객체 형태로 서브 에이전트들을 관리하며, 사용자 질문에 따라 적절한 도구(서브 에이전트)를 선택하여 작업을 수행합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;구현이 간단합니다&lt;/b&gt;: LangChain의 Tool과 AgentExecutor를 사용하면 비교적 적은 코드로 구현할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;직관적인 구조&lt;/b&gt;: 슈퍼 에이전트가 도구를 선택하는 방식이 직관적이고 이해하기 쉽습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빠른 프로토타이핑&lt;/b&gt;: 새로운 기능이나 에이전트를 추가하기 쉬워 빠르게 프로토타입을 만들 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;복잡한 워크플로우 관리의 어려움&lt;/b&gt;: 여러 에이전트 간의 복잡한 상호작용이나 조건부 실행 흐름을 관리하기 어렵습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;상태 관리의 한계&lt;/b&gt;: 에이전트 간 상태 공유나 복잡한 상태 전이를 구현하기 어렵습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;디버깅의 어려움&lt;/b&gt;: 복잡한 에이전트 시스템에서 문제가 발생했을 때 디버깅이 어려울 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LangGraph로 구현하는 Super-Sub 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangGraph는 LangChain 위에 구축된 라이브러리로, 복잡한 에이전트 시스템을 그래프 기반으로 구현할 수 있게 해줍니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구현 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangGraph를 사용한 Super-Sub 구조는 다음과 같이 구현할 수 있습니다:&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;# 상태 정의
class AgentState(TypedDict):
    question: str
    messages: Annotated[Sequence[BaseMessage], operator.add]
    current_agent: str
    agent_results: Dict[str, Any]
    final_answer: Optional[str]
    error: Optional[str]
    iteration_count: int

# 그래프 생성 함수
def create_agent_graph(llm: Optional[BaseLanguageModel] = None) -&amp;gt; StateGraph:
    # 기본 LLM 설정
    if llm is None:
        llm = ChatGoogleGenerativeAI(model=DEFAULT_MODEL)

    # 노드 생성
    super_agent_node = SuperAgentNode(llm)
    db_agent_node = DBAgentNode(llm)
    rag_agent_node = RAGAgentNode()
    error_handler_node = ErrorHandlerNode(llm)

    # 그래프 생성
    workflow = StateGraph(AgentState)

    # 노드 추가
    workflow.add_node(AgentType.SUPER, super_agent_node)
    workflow.add_node(AgentType.DB, db_agent_node)
    workflow.add_node(AgentType.RAG, rag_agent_node)
    workflow.add_node(&quot;error_handler&quot;, error_handler_node)

    # 조건부 엣지 추가
    workflow.add_conditional_edges(
        AgentType.SUPER,
        lambda state: &quot;error_handler&quot; if state.get(&quot;error&quot;) else state[&quot;current_agent&quot;],
        {
            &quot;error_handler&quot;: &quot;error_handler&quot;,
            AgentType.DB: AgentType.DB,
            AgentType.RAG: AgentType.RAG,
            END: END
        }
    )

    # 각 에이전트에서 슈퍼 에이전트로 돌아가는 엣지
    workflow.add_edge(AgentType.DB, AgentType.SUPER)
    workflow.add_edge(AgentType.RAG, AgentType.SUPER)

    # 시작 노드 설정
    workflow.set_entry_point(AgentType.SUPER)

    return workflow

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식에서는 에이전트 시스템을 그래프로 모델링하며, 각 에이전트는 노드로, 에이전트 간 전환은 엣지로 표현됩니다. 상태(State)를 통해 에이전트 간 정보를 공유하고, 조건부 엣지를 통해 복잡한 워크플로우를 구현할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;복잡한 워크플로우 관리&lt;/b&gt;: 그래프 기반 접근 방식으로 복잡한 에이전트 간 상호작용과 조건부 실행 흐름을 명확하게 모델링할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;상태 관리의 용이성&lt;/b&gt;: TypedDict를 사용한 상태 정의로 에이전트 간 상태 공유와 전이를 체계적으로 관리할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시각화 가능&lt;/b&gt;: 그래프 구조를 시각화하여 복잡한 에이전트 시스템의 흐름을 쉽게 이해하고 디버깅할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;에러 처리의 유연성&lt;/b&gt;: 에러 처리 노드를 별도로 구현하여 다양한 예외 상황에 대응할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;반복 실행 제어&lt;/b&gt;: 반복 횟수를 추적하고 제한하여 무한 루프를 방지할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;확장성&lt;/b&gt;: 새로운 에이전트나 기능을 그래프에 쉽게 추가할 수 있어 시스템 확장이 용이합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;구현 복잡성&lt;/b&gt;: LangChain만 사용하는 것보다 초기 구현이 더 복잡하고 코드량이 많습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;학습 곡선&lt;/b&gt;: 그래프 기반 접근 방식과 상태 관리에 대한 이해가 필요하여 학습 곡선이 높을 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;오버헤드&lt;/b&gt;: 간단한 작업에는 불필요한 복잡성과 오버헤드가 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;디버깅 복잡성&lt;/b&gt;: 그래프 구조가 복잡해질수록 디버깅이 어려워질 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실제 사용 사례 비교&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LangChain Agent만 사용한 경우&lt;/h3&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;async def process_message(self, message: str) -&amp;gt; str:
    &quot;&quot;&quot;메시지 처리 및 응답 생성&quot;&quot;&quot;
    # RAG 관련 키워드 확인
    rag_keywords = [&quot;찾아줘&quot;, &quot;검색해줘&quot;, &quot;관련 정보&quot;, &quot;문서에서&quot;, &quot;문서 검색&quot;]

    try:
        if any(keyword in message for keyword in rag_keywords):
            # RAG 도구 사용
            context = self.rag_agent.answer_question(message)
            prompt = f&quot;&quot;&quot;다음 컨텍스트를 기반으로 질문에 답변해주세요:

            컨텍스트:
            {context}

            질문: {message}
            &quot;&quot;&quot;
            return await self._generate_response(prompt)

        # 일반적인 에이전트 실행
        result = await self.agent_executor.ainvoke({&quot;input&quot;: message})
        return result[&quot;output&quot;]

    except Exception as e:
        return f&quot;죄송합니다. 오류가 발생했습니다: {str(e)}&quot;

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식에서는 키워드 기반으로 간단하게 에이전트를 선택하거나, AgentExecutor에게 도구 선택을 위임합니다. 구현은 간단하지만, 복잡한 의사결정이나 에이전트 간 상호작용을 구현하기 어렵습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LangGraph를 사용한 경우&lt;/h3&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;def decide_agent(self, state: AgentState) -&amp;gt; Dict[str, Any]:
    &quot;&quot;&quot;어떤 에이전트를 사용할지 결정하는 함수&quot;&quot;&quot;
    # 반복 횟수 확인 - 최대 반복 횟수에 도달하면 강제 종료
    iteration_count = state.get(&quot;iteration_count&quot;, 0)
    if iteration_count &amp;gt;= 10:
        print(f&quot;[슈퍼 에이전트] 최대 반복 횟수({iteration_count})에 도달하여 현재까지의 결과로 답변합니다.&quot;)
        return {
            &quot;reasoning&quot;: f&quot;최대 반복 횟수({iteration_count})에 도달하여 현재까지의 결과로 답변합니다.&quot;,
            &quot;selected_agent&quot;: &quot;super_agent&quot;,
            &quot;is_complete&quot;: True
        }

    # 현재까지의 에이전트 결과를 문자열로 변환
    agent_results_str = json.dumps(state[&quot;agent_results&quot;], ensure_ascii=False, indent=2)

    # 이미 시도한 에이전트 확인
    tried_agents = set(state[&quot;agent_results&quot;].keys())

    # DB 에이전트 결과 확인
    if &quot;db_agent&quot; in state[&quot;agent_results&quot;]:
        db_result = state[&quot;agent_results&quot;][&quot;db_agent&quot;]
        # 결과 분석 로직...

    # RAG 에이전트 결과 확인
    if &quot;rag_agent&quot; in state[&quot;agent_results&quot;]:
        rag_result = state[&quot;agent_results&quot;][&quot;rag_agent&quot;]
        # 결과 분석 로직...

    # 아직 어떤 에이전트도 시도하지 않았다면, 질문 유형에 따라 에이전트 선택
    if not tried_agents:
        # 프롬프트에 질문과 에이전트 결과 전달
        chain = self.agent_selection_prompt | self.llm | StrOutputParser()
        result = chain.invoke({
            &quot;question&quot;: state[&quot;question&quot;],
            &quot;agent_results&quot;: agent_results_str
        })
        # 결과 파싱 및 반환...

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangGraph를 사용하면 상태 기반으로 복잡한 의사결정 로직을 구현할 수 있습니다. 이전 에이전트의 결과를 분석하고, 반복 횟수를 추적하며, 다양한 조건에 따라 다음 에이전트를 선택할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;어떤 방식을 선택해야 할까?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LangChain Agent만 사용하는 것이 좋은 경우&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;간단한 에이전트 시스템&lt;/b&gt;: 복잡한 워크플로우가 필요 없는 간단한 에이전트 시스템&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빠른 프로토타이핑&lt;/b&gt;: 빠르게 개념 증명(PoC)을 구현해야 하는 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리소스 제약&lt;/b&gt;: 개발 리소스나 시간이 제한적인 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단일 에이전트 중심&lt;/b&gt;: 여러 에이전트 간 복잡한 상호작용이 적은 경우&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LangGraph를 사용하는 것이 좋은 경우&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;복잡한 워크플로우&lt;/b&gt;: 여러 에이전트 간 복잡한 상호작용과 조건부 실행이 필요한 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;상태 기반 의사결정&lt;/b&gt;: 이전 결과와 상태에 기반한 복잡한 의사결정이 필요한 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;확장성 중시&lt;/b&gt;: 시스템이 점진적으로 확장될 것으로 예상되는 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;엄격한 에러 처리&lt;/b&gt;: 다양한 예외 상황에 대한 체계적인 처리가 필요한 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;반복 실행 제어&lt;/b&gt;: 에이전트 간 반복적인 상호작용을 제어해야 하는 경우&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain Agent만 사용하는 방식은 구현이 간단하고 직관적이지만, 복잡한 워크플로우와 상태 관리에 한계가 있습니다. 반면 LangGraph를 사용하는 방식은 초기 구현이 복잡하지만, 복잡한 에이전트 시스템을 체계적으로 모델링하고 관리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 복잡성, 확장성 요구사항, 개발 리소스 등을 고려하여 적절한 방식을 선택하는 것이 중요합니다. 간단한 프로젝트는 LangChain Agent만으로 충분할 수 있지만, 복잡한 에이전트 시스템을 구축하려면 LangGraph의 그래프 기반 접근 방식이 더 적합할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 회사의 경우, 초기에는 LangChain Agent만으로 프로토타입을 빠르게 개발했지만, 시스템이 복잡해지면서 LangGraph로 마이그레이션하여 더 체계적인 에이전트 시스템을 구축할 수 있었습니다. 두 접근 방식의 장단점을 이해하고 프로젝트 요구사항에 맞게 선택하는 것이 성공적인 AI 에이전트 시스템 구축의 핵심입니다.&lt;/p&gt;</description>
      <category>AI</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/229</guid>
      <comments>https://shoney.tistory.com/entry/LangChain-Agent-vs-LangGraph-Super-Sub-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EA%B5%AC%EC%A1%B0-%EA%B5%AC%ED%98%84-%EB%B9%84%EA%B5%90#entry229comment</comments>
      <pubDate>Wed, 19 Mar 2025 16:17:11 +0900</pubDate>
    </item>
    <item>
      <title>[AI] LangChain 에이전트 구현하기: ReAct 프레임워크 기반 자동화 시스템</title>
      <link>https://shoney.tistory.com/entry/AI-LangChain-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-ReAct-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EA%B8%B0%EB%B0%98-%EC%9E%90%EB%8F%99%ED%99%94-%EC%8B%9C%EC%8A%A4%ED%85%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain을 활용하면 단순한 언어 모델(LLM)을 넘어, &lt;b&gt;자율적으로 행동하는 AI 에이전트&lt;/b&gt;를 만들 수 있습니다.&lt;br /&gt;이번 글에서는 &lt;b&gt;Google Gemini Pro&lt;/b&gt; 모델과 &lt;b&gt;ReAct 에이전트 프레임워크&lt;/b&gt;를 사용해,&lt;br /&gt;웹 검색과 파이썬 코드 실행이 가능한 &lt;b&gt;스마트 에이전트&lt;/b&gt;를 구축하는 방법을 설명하겠습니다.  &lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1. 환경 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 필요한 라이브러리를 임포트하고 환경 변수를 설정합니다.&lt;br /&gt;특히, &lt;b&gt;SSL 인증을 비활성화&lt;/b&gt;하여 네트워크 요청에서 발생할 수 있는 보안 경고를 방지합니다.&lt;/p&gt;
&lt;pre class=&quot;moonscript&quot;&gt;&lt;code&gt;import os
import ssl
from langchain import hub
from langchain_community.utilities.serpapi import SerpAPIWrapper
from langchain_experimental.tools import PythonREPLTool
from langchain_core.tools import Tool
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain.agents import create_react_agent, AgentExecutor

# SSL 검증 비활성화
ssl._create_default_https_context = ssl._create_unverified_context

# 환경 변수 설정
os.environ[&quot;GOOGLE_APPLICATION_CREDENTIALS&quot;] = &quot;agent/api-key.json&quot;
os.environ[&quot;SERPAPI_API_KEY&quot;] = &quot;YOUR_SERPAPI_KEY&quot;  # SerpAPI에서 무료 API 키 발급
os.environ['PYTHONHTTPSVERIFY'] = '0'
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;SerpAPI는 검색 엔진을 활용하기 위해 필요한 API&lt;/b&gt;입니다.&lt;br /&gt;&lt;a href=&quot;https://serpapi.com/&quot;&gt;SerpAPI 공식 사이트&lt;/a&gt;에서 무료 키를 발급받아 YOUR_SERPAPI_KEY를 대체하세요.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  2. 언어 모델(LLM) 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 &lt;b&gt;Gemini Pro 모델&lt;/b&gt;을 사용해 에이전트의 &quot;두뇌&quot; 역할을 담당하도록 설정합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;# Gemini Pro 모델을 활용한 LLM 초기화
llm = ChatGoogleGenerativeAI(model=&quot;models/gemini-1.5-pro&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;LangChain은 다양한 LLM을 지원&lt;/b&gt;하며, OpenAI의 GPT, Anthropic의 Claude, Google의 Gemini 등 다양한 모델을 연결할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  3. 에이전트 도구(Tools) 정의&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트가 단순한 텍스트 생성기에서 &lt;b&gt;더 똑똑한 AI&lt;/b&gt;로 동작하려면, 외부 도구를 사용할 수 있어야 합니다.&lt;br /&gt;이번 프로젝트에서는 &lt;b&gt;웹 검색&lt;/b&gt;과 &lt;b&gt;파이썬 코드 실행기&lt;/b&gt;를 도구로 추가하겠습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 에이전트가 사용할 도구 정의
tools = [
    Tool(
        name=&quot;웹검색&quot;,
        func=SerpAPIWrapper().run,
        description=&quot;최신 정보를 검색해야 할 때 사용하는 도구&quot;
    ),
    PythonREPLTool(
        name=&quot;파이썬_실행기&quot;,
        description=&quot;파이썬 코드를 실행해야 할 때 사용하는 도구&quot;
    )
]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  도구의 역할&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;웹 검색&lt;/b&gt;: 검색 엔진을 사용해 최신 정보를 가져옵니다.&lt;br /&gt;✅ &lt;b&gt;파이썬 실행기&lt;/b&gt;: 수학 계산, 데이터 분석, 문자열 처리 등을 수행할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  4. ReAct 기반 에이전트 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 LLM과 도구들을 조합하여 &lt;b&gt;ReAct(Reasoning + Acting) 프레임워크&lt;/b&gt; 기반의 에이전트를 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;# 프롬프트 템플릿 가져오기
prompt = hub.pull(&quot;hwchase17/react&quot;)

# ReAct 에이전트 생성
agent = create_react_agent(llm, tools, prompt)

# 에이전트 실행기(Agent Executor) 생성
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;ReAct 프레임워크란?&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 &lt;b&gt;&quot;생각(Reasoning) &amp;rarr; 행동(Acting) &amp;rarr; 피드백&quot;&lt;/b&gt; 을 반복하는 방식으로 동작하는 프레임워크입니다.&lt;br /&gt;예를 들어, AI가 &quot;GDP 검색 후 원화 환산&quot;을 요청받으면,&lt;br /&gt;&lt;b&gt;① 검색 &amp;rarr; ② 환산 계산 &amp;rarr; ③ 최종 결과 도출&lt;/b&gt; 의 과정을 스스로 수행합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  5. 에이전트 테스트  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 우리가 만든 에이전트가 실제로 어떻게 동작하는지 테스트해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# 에이전트 테스트 함수
def test_agent():
    # 1️⃣ 파이썬 코드 실행 테스트 - 짝수 출력
    result1 = agent_executor.invoke(
        {&quot;input&quot;: &quot;파이썬을 사용해서 1부터 10까지의 숫자 중 짝수만 출력해주세요&quot;}
    )
    print(&quot;\n기본 계산 결과:&quot;, result1[&quot;output&quot;])
    
    # 2️⃣ 파이썬 코드 실행 테스트 - 통계 분석
    result2 = agent_executor.invoke(
        {&quot;input&quot;: &quot;&quot;&quot;다음 데이터의 평균과 표준편차를 계산해주세요: 
        [23, 45, 67, 89, 12, 34, 56, 78, 90, 11]&quot;&quot;&quot;}
    )
    print(&quot;\n통계 분석 결과:&quot;, result2[&quot;output&quot;])
    
    # 3️⃣ 문자열 처리 테스트 - 모음 개수 세기
    result3 = agent_executor.invoke(
        {&quot;input&quot;: &quot;문자열 'Hello, Python World!'에서 모든 모음(a,e,i,o,u)의 개수를 세어주세요&quot;}
    )
    print(&quot;\n문자열 분석 결과:&quot;, result3[&quot;output&quot;])
    
    # 4️⃣ 복합 작업 테스트 - GDP 검색 후 환산
    result4 = agent_executor.invoke(
        {&quot;input&quot;: &quot;대한민국의 2023년 GDP가 얼마인지 검색하고, 이를 달러에서 원화로 환산해주세요 (1달러=1300원 기준)&quot;}
    )
    print(&quot;\n복합 작업 결과:&quot;, result4[&quot;output&quot;])

if __name__ == &quot;__main__&quot;:
    test_agent()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  실행 결과 예시&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;기본 계산 결과: [2, 4, 6, 8, 10]

통계 분석 결과: 평균=50.5, 표준편차=28.4

문자열 분석 결과: 모음 개수 = 6

복합 작업 결과: 대한민국 2023년 GDP는 약 1.7조 달러이며, 원화로 환산하면 약 2210조 원입니다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;정리하자면...&lt;/b&gt;&lt;br /&gt;✅ &lt;b&gt;단순한 LLM이 아니라, 직접 행동할 수 있는 AI&lt;/b&gt;를 만들었다!&lt;br /&gt;✅ &lt;b&gt;웹 검색을 통해 실시간 정보를 얻고, 파이썬 실행기로 데이터를 처리할 수 있다!&lt;/b&gt;&lt;br /&gt;✅ &lt;b&gt;ReAct 프레임워크를 활용해 AI가 스스로 생각하고, 판단하고, 실행한다!&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 LangChain을 활용해 &lt;b&gt;스스로 검색하고 계산하는 AI 에이전트&lt;/b&gt;를 만들었습니다.&lt;br /&gt;이를 확장하면 &lt;b&gt;&quot;AI 자동 보고 시스템&quot;&lt;/b&gt;, &lt;b&gt;&quot;데이터 분석 도우미&quot;&lt;/b&gt;, &lt;b&gt;&quot;스마트 챗봇&quot;&lt;/b&gt; 등 다양한 응용이 가능합니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✨ 앞으로의 확장 아이디어&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;✅ &lt;b&gt;추가적인 도구(Tool) 연결&lt;/b&gt; &amp;rarr; 데이터베이스 조회, API 호출 등&lt;/li&gt;
&lt;li&gt;✅ &lt;b&gt;더 강력한 모델 적용&lt;/b&gt; &amp;rarr; GPT-4, Claude-3, Mixtral 등&lt;/li&gt;
&lt;li&gt;✅ &lt;b&gt;자연어 처리 강화&lt;/b&gt; &amp;rarr; 복잡한 요청을 더 정확하게 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에도 더 흥미로운 LangChain 활용법을 소개하겠습니다!  &lt;br /&gt;궁금한 점은 댓글로 남겨주세요!  &lt;/p&gt;</description>
      <category>AI</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/228</guid>
      <comments>https://shoney.tistory.com/entry/AI-LangChain-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-ReAct-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EA%B8%B0%EB%B0%98-%EC%9E%90%EB%8F%99%ED%99%94-%EC%8B%9C%EC%8A%A4%ED%85%9C#entry228comment</comments>
      <pubDate>Thu, 6 Mar 2025 13:06:23 +0900</pubDate>
    </item>
    <item>
      <title>[AI] Google Generative AI 모델 사용하기: 모델 리스트 조회 및 프롬프트 테스트</title>
      <link>https://shoney.tistory.com/entry/AI-Google-Generative-AI-%EB%AA%A8%EB%8D%B8-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EB%AA%A8%EB%8D%B8-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EC%A1%B0%ED%9A%8C-%EB%B0%8F-%ED%94%84%EB%A1%AC%ED%94%84%ED%8A%B8-%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google에서 제공하는 &lt;b&gt;Generative AI API&lt;/b&gt;를 사용하면 강력한 AI 모델을 활용할 수 있습니다. 이 글에서는 &lt;b&gt;Python 코드&lt;/b&gt;를 활용하여 다음과 같은 작업을 수행하는 방법을 설명합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Google AI의 모델 리스트 조회&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프롬프트를 사용하여 AI 모델과 상호작용&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 사전 준비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google Generative AI API를 사용하기 위해서는 &lt;b&gt;서비스 계정 키 파일&lt;/b&gt;이 필요합니다. 이를 환경 변수로 설정해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;moonscript&quot;&gt;&lt;code&gt;import os

os.environ[&quot;GOOGLE_APPLICATION_CREDENTIALS&quot;] = &quot;agent/api-key.json&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 &quot;agent/api-key.json&quot; 파일은 &lt;b&gt;Google Cloud 콘솔&lt;/b&gt;에서 생성한 서비스 계정 키 파일입니다. 자신의 키 파일 경로로 수정해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 사용 가능한 모델 리스트 조회&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, &lt;b&gt;Google AI에서 지원하는 모델 목록&lt;/b&gt;을 조회해 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from google.ai import generativelanguage_v1beta

# 모델 리스트를 가져오기 위한 클라이언트 생성
client = generativelanguage_v1beta.ModelServiceClient()

# 사용 가능한 모델 리스트 출력
try:
    models = client.list_models()
    print(&quot;사용 가능한 모델 목록:&quot;)
    for model in models:
        print(f&quot;- {model.name}: {model.description}&quot;)
except Exception as e:
    print(&quot;모델 목록 조회 중 오류 발생:&quot;)
    print(str(e))
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  코드 설명&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ModelServiceClient()를 사용하여 &lt;b&gt;모델 리스트를 가져오는 클라이언트&lt;/b&gt;를 생성합니다.&lt;/li&gt;
&lt;li&gt;list_models() 메서드를 호출하여 사용 가능한 모델 목록을 가져옵니다.&lt;/li&gt;
&lt;li&gt;모델의 이름(model.name)과 설명(model.description)을 출력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  실행 결과 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;사용 가능한 모델 목록:
- models/gemini-1.5-pro: 고급 AI 모델, 자연어 처리에 강함
- models/gemini-1.0-pro: 이전 버전의 고급 AI 모델
...
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 프롬프트를 활용한 AI 응답 테스트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제, AI 모델을 사용하여 프롬프트를 입력하고 응답을 받아보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from google.api_core import client_options

# 생성 모델 클라이언트 생성
client = generativelanguage_v1beta.GenerativeServiceClient()

# 테스트할 프롬프트 준비
content = generativelanguage_v1beta.Content()
content.parts = [{&quot;text&quot;: &quot;안녕하세요! 당신은 누구인가요?&quot;}]

# 요청 생성
request = generativelanguage_v1beta.GenerateContentRequest(
    model=&quot;models/gemini-1.5-pro&quot;,  # 사용할 AI 모델 선택
    contents=[content]
)

try:
    response = client.generate_content(request)
    print(&quot;\n응답:&quot;)
    print(response.candidates[0].content.parts[0].text)
except Exception as e:
    print(&quot;오류 발생:&quot;)
    print(str(e))
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  코드 설명&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;**GenerativeServiceClient()**를 사용하여 AI 생성 모델 클라이언트를 생성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프롬프트&lt;/b&gt;(content.parts)를 설정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GenerateContentRequest&lt;/b&gt;를 생성하여 요청을 보냅니다.&lt;/li&gt;
&lt;li&gt;AI의 응답을 출력합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  실행 결과 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;erlang-repl&quot;&gt;&lt;code&gt;응답:
안녕하세요! 저는 Google AI의 Gemini 모델입니다. 무엇을 도와드릴까요?
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Google AI의 &lt;b&gt;모델을 조회하고, 프롬프트를 테스트하는 방법&lt;/b&gt;을 알게 되었습니다. 이 API를 활용하면 다양한 AI 기반 응용 프로그램을 개발할 수 있습니다.  &lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 정리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Google Cloud 서비스 계정 키를 설정&lt;/b&gt;해야 합니다.&lt;/li&gt;
&lt;li&gt;ModelServiceClient()를 사용하여 &lt;b&gt;지원 모델 리스트를 조회&lt;/b&gt;할 수 있습니다.&lt;/li&gt;
&lt;li&gt;GenerativeServiceClient()를 사용하여 &lt;b&gt;AI 모델과 상호작용&lt;/b&gt;할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 AI 모델을 활용하여 다양한 &lt;b&gt;자연어 처리(NLP) 프로젝트&lt;/b&gt;를 진행해 보세요!  &lt;/p&gt;</description>
      <category>AI</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/227</guid>
      <comments>https://shoney.tistory.com/entry/AI-Google-Generative-AI-%EB%AA%A8%EB%8D%B8-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EB%AA%A8%EB%8D%B8-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EC%A1%B0%ED%9A%8C-%EB%B0%8F-%ED%94%84%EB%A1%AC%ED%94%84%ED%8A%B8-%ED%85%8C%EC%8A%A4%ED%8A%B8#entry227comment</comments>
      <pubDate>Thu, 6 Mar 2025 10:38:04 +0900</pubDate>
    </item>
    <item>
      <title>[AI] 에이전트란 무엇인가? -  (초보자를 위한 쉬운 설명)</title>
      <link>https://shoney.tistory.com/entry/AI-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-%EC%B4%88%EB%B3%B4%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%89%AC%EC%9A%B4-%EC%84%A4%EB%AA%85</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;언어 모델(LLM)은 기본적으로 &quot;똑똑한 텍스트 생성기&quot;입니다. 주어진 질문이나 문장을 기반으로 가장 적절한 텍스트를 출력할 뿐, 실제 행동을 취하는 것은 아닙니다. 예를 들어, &quot;날씨가 어때?&quot;라고 물어보면, &quot;오늘 서울의 날씨는 맑습니다.&quot;라고 답할 수 있지만, 실제로 날씨를 검색하는 기능은 없습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;LangChain에서 '에이전트'가 필요한 이유&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain에서는 이런 언어 모델이 단순한 답변 생성기가 아니라, &lt;b&gt;스스로 행동할 수 있도록&lt;/b&gt; 도와주는 기능을 추가할 수 있습니다. 이 역할을 하는 것이 바로 에이전트(Agent)입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트는 언어 모델을 추론 엔진으로 사용하여 다음과 같은 과정을 수행합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;어떤 행동을 해야 하는지 결정&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어, &quot;서울의 날씨 알려줘&quot;라고 하면, 단순히 문장을 생성하는 게 아니라, &quot;날씨 정보를 검색하는 행동을 해야겠어!&quot;라고 판단합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;필요한 입력을 정리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;날씨를 찾으려면 검색 엔진에 '서울 날씨'라고 입력해야겠군!&quot;&lt;/li&gt;
&lt;li&gt;즉, 어떤 정보를 얻기 위해 무엇을 입력해야 하는지를 결정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;도구를 사용해서 행동 수행&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LangChain에서는 언어 모델이 단독으로 행동하지 못하므로, &lt;b&gt;외부 도구(검색 엔진, 데이터베이스 등)를 사용해서 정보를 가져오도록&lt;/b&gt; 도와줍니다.&lt;/li&gt;
&lt;li&gt;예를 들어, 검색 엔진을 사용해 '서울 날씨'를 실제로 검색합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;결과를 다시 확인하고 다음 행동 결정&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색 결과를 받아서 &lt;b&gt;&quot;이 정보로 충분한가? 아니면 추가로 다른 행동이 필요한가?&quot;&lt;/b&gt; 판단합니다.&lt;/li&gt;
&lt;li&gt;만약 정보가 부족하면 다시 검색하거나 다른 방법을 시도할 수도 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;최종적으로 사용자에게 답변 제공&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 과정이 끝나면 사용자에게 최종적인 답을 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제로 쉽게 이해하기 ✨&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain의 에이전트는 여러 도구를 사용할 수 있도록 설정할 수 있습니다. 예를 들어,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;도구 1: 로컬 데이터베이스&lt;/b&gt; &amp;rarr; 내 컴퓨터에 저장된 정보를 찾아볼 수 있음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;도구 2: 검색 엔진&lt;/b&gt; &amp;rarr; 구글에서 최신 정보를 검색할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 에이전트는 &lt;b&gt;언어 모델이 직접 할 수 없는 일(검색, 데이터 조회 등)을 도와주는 역할&lt;/b&gt;을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;실제 동작 예시:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;❓ &lt;b&gt;질문:&lt;/b&gt; &quot;일론 머스크의 최신 트윗이 뭐야?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;에이전트의 판단:&lt;/b&gt; &quot;이건 검색이 필요하군! 트위터 검색 도구를 사용해야겠어.&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;행동:&lt;/b&gt; 트위터 검색 엔진을 실행하고 최신 트윗을 가져옴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;결과 확인:&lt;/b&gt; 가져온 트윗을 사용자에게 전달&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 &lt;b&gt;LangChain의 에이전트는 언어 모델이 단순히 텍스트만 출력하는 것이 아니라, 실제로 행동을 수행할 수 있도록 만드는 역할&lt;/b&gt;을 합니다!  &lt;/p&gt;</description>
      <category>AI</category>
      <category>에이전트</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/226</guid>
      <comments>https://shoney.tistory.com/entry/AI-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-%EC%B4%88%EB%B3%B4%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%89%AC%EC%9A%B4-%EC%84%A4%EB%AA%85#entry226comment</comments>
      <pubDate>Wed, 5 Mar 2025 17:40:32 +0900</pubDate>
    </item>
    <item>
      <title>[토큰 관리 방식] 쿠기 vs 로컬/세션 스토리지</title>
      <link>https://shoney.tistory.com/entry/%ED%86%A0%ED%81%B0-%EA%B4%80%EB%A6%AC-%EB%B0%A9%EC%8B%9D-%EC%BF%A0%EA%B8%B0-vs-%EB%A1%9C%EC%BB%AC%EC%84%B8%EC%85%98-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;쿠키(Cookie)와 로컬/세션 스토리지(localStorage / sessionStorage)를 사용하여 토큰을 관리하는 방식에는 각각의 &lt;b&gt;장점과 단점&lt;/b&gt;이 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;✅ &lt;b&gt;쿠키 vs 로컬/세션 스토리지의 차이점&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분 쿠키 (Cookie) 로컬스토리지 (localStorage) / 세션스토리지 (sessionStorage)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;저장 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;HTTP 요청 시 자동으로 전송&lt;/td&gt;
&lt;td&gt;JS를 통해 읽고 쓰기 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;보안성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;HttpOnly 설정 가능 &amp;rarr; &lt;b&gt;XSS 방어 가능&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;XSS 공격에 취약&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CSRF 공격&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;기본적으로 취약 &amp;rarr; CSRF 토큰 필요&lt;/td&gt;
&lt;td&gt;CSRF 공격에 안전&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;만료 시간&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;서버가 설정 가능 (Max-Age, Expires)&lt;/td&gt;
&lt;td&gt;개발자가 직접 관리해야 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;도메인 제한&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;특정 도메인에서만 접근 가능&lt;/td&gt;
&lt;td&gt;모든 JS에서 접근 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;전송 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Cookie 헤더를 통해 자동 전송&lt;/td&gt;
&lt;td&gt;API 요청 시 Authorization 헤더에 수동으로 추가해야 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;용량 제한&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;약 &lt;b&gt;4KB&lt;/b&gt; 제한&lt;/td&gt;
&lt;td&gt;약 &lt;b&gt;5MB&lt;/b&gt; 제한&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;✅ &lt;b&gt;1. 쿠키로 토큰 관리 (HTTP-Only 쿠키)&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ &lt;b&gt;쿠키 관리 방법&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;서버에서 Set-Cookie를 통해 쿠키에 토큰 저장&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클라이언트는 withCredentials: true 옵션을 사용하여 요청&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;쿠키는 HTTP 요청 시 자동으로 포함됨&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;예제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서버에서 쿠키 설정&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Set-Cookie: access_token=abc123; Path=/; HttpOnly; Secure; SameSite=Strict;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클라이언트 요청 (Axios 예시)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;axios.get(&quot;https://api.example.com/user&quot;, {
  withCredentials: true
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ &lt;b&gt;장점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ &lt;b&gt;보안성이 높음&lt;/b&gt; (HttpOnly, Secure 옵션 사용 가능)&lt;br /&gt;✔ &lt;b&gt;XSS 공격 방어 가능&lt;/b&gt; (HttpOnly 설정 시 JS에서 접근 불가)&lt;br /&gt;✔ &lt;b&gt;자동으로 포함되므로 클라이언트에서 토큰을 수동으로 추가할 필요 없음&lt;/b&gt;&lt;br /&gt;✔ &lt;b&gt;서버가 만료 기간을 설정하고 관리 가능&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;❌ &lt;b&gt;단점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;❌ &lt;b&gt;CORS 설정이 복잡함&lt;/b&gt; (Access-Control-Allow-Credentials: true 필요)&lt;br /&gt;❌ &lt;b&gt;CSRF 공격에 취약할 수 있음 &amp;rarr; CSRF 방어를 위한 추가적인 조치 필요&lt;/b&gt;&lt;br /&gt;❌ &lt;b&gt;도메인 간 공유가 어려움&lt;/b&gt; (다른 도메인에서는 접근 불가)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;✅ &lt;b&gt;2. 로컬스토리지 또는 세션스토리지에서 토큰 관리&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ &lt;b&gt;로컬스토리지(LocalStorage)와 세션스토리지(SessionStorage)의 차이&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장 방식 설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;localStorage&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;브라우저가 종료되어도 토큰이 유지됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;sessionStorage&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;브라우저 또는 탭이 종료되면 토큰이 삭제됨&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;예제&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;// 로그인 시 토큰 저장
localStorage.setItem(&quot;access_token&quot;, &quot;abc123&quot;);

// API 요청 시 헤더에 포함
axios.get(&quot;https://api.example.com/user&quot;, {
  headers: {
    Authorization: `Bearer ${localStorage.getItem(&quot;access_token&quot;)}`
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ &lt;b&gt;장점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ &lt;b&gt;CSRF 공격에 안전함&lt;/b&gt; (쿠키처럼 자동 전송되지 않음)&lt;br /&gt;✔ &lt;b&gt;CORS 설정이 비교적 간단함&lt;/b&gt;&lt;br /&gt;✔ &lt;b&gt;도메인 간 공유가 가능함&lt;/b&gt; (단, postMessage API를 사용해야 함)&lt;br /&gt;✔ &lt;b&gt;응답 속도가 빠름 (쿠키보다 I/O 비용이 적음)&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;❌ &lt;b&gt;단점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;❌ &lt;b&gt;XSS 공격에 취약함&lt;/b&gt; (악성 스크립트가 토큰을 훔칠 수 있음)&lt;br /&gt;❌ &lt;b&gt;토큰이 자동으로 포함되지 않으므로 매 요청마다 Authorization 헤더에 추가해야 함&lt;/b&gt;&lt;br /&gt;❌ &lt;b&gt;수동으로 만료 시간을 관리해야 함&lt;/b&gt; (쿠키는 서버에서 Max-Age 설정 가능)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;✅ &lt;b&gt;그럼 언제 쿠키를 쓰고, 언제 로컬스토리지를 써야 할까?&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;쿠키를 써야 하는 경우&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;보안이 중요한 경우 (예: 로그인, 금융 서비스 등)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;XSS 공격 방어가 필요할 때 (HttpOnly 쿠키 사용)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;토큰을 자동으로 요청과 함께 보내야 할 때&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서버가 인증을 철저하게 관리해야 할 때&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;로컬스토리지/세션스토리지를 써야 하는 경우&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CSRF 공격을 막고 싶을 때&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CORS 설정이 복잡한 API를 사용할 때&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;OAuth 2.0의 Authorization: Bearer 토큰을 활용할 때&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;토큰을 수동으로 관리할 필요가 있을 때 (리프레시 토큰 전략 등)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;✅ &lt;b&gt;결론&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택 기준 쿠키 (HttpOnly) 로컬스토리지 / 세션스토리지&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;보안&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;XSS 공격 방어 가능&lt;/td&gt;
&lt;td&gt;XSS 공격에 취약&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CSRF 공격 방어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;별도 방어 필요 (CSRF 토큰)&lt;/td&gt;
&lt;td&gt;기본적으로 안전&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;자동 전송&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;요청 시 자동 전송 (Cookie 헤더)&lt;/td&gt;
&lt;td&gt;Authorization 헤더에 수동 추가 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;설정 난이도&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;CORS, 쿠키 설정 필요&lt;/td&gt;
&lt;td&gt;비교적 설정이 쉬움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;도메인 공유&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;제한적 (도메인 간 공유 불가)&lt;/td&gt;
&lt;td&gt;비교적 자유로움&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;보안이 중요한 경우&lt;/b&gt; &amp;rarr; &lt;b&gt;HttpOnly 쿠키&lt;/b&gt; 추천&lt;br /&gt;  &lt;b&gt;토큰을 수동으로 관리하고 싶다면&lt;/b&gt; &amp;rarr; &lt;b&gt;로컬/세션스토리지&lt;/b&gt; 추천&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front-End</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/225</guid>
      <comments>https://shoney.tistory.com/entry/%ED%86%A0%ED%81%B0-%EA%B4%80%EB%A6%AC-%EB%B0%A9%EC%8B%9D-%EC%BF%A0%EA%B8%B0-vs-%EB%A1%9C%EC%BB%AC%EC%84%B8%EC%85%98-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80#entry225comment</comments>
      <pubDate>Tue, 4 Mar 2025 14:53:46 +0900</pubDate>
    </item>
    <item>
      <title>[java8] 인터페이스의 디폴트 메서드, 스태틱 메서드</title>
      <link>https://shoney.tistory.com/entry/java8-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EC%9D%98-%EB%94%94%ED%8F%B4%ED%8A%B8-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%8A%A4%ED%83%9C%ED%8B%B1-%EB%A9%94%EC%84%9C%EB%93%9C</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Java 8 인터페이스의 기본 메소드와 스태틱 메소드 가이드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java 8에서는 인터페이스에 기본 메소드(Default Methods)와 스태틱 메소드(Static Methods)가 추가되었습니다. 이를 통해 인터페이스를 더욱 유연하게 사용하고, 기존 코드를 변경하지 않고도 새로운 기능을 추가할 수 있게 되었습니다. 다음은 이 기능들을 효과적으로 사용하는 방법에 대한 가이드입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 기본 메소드 (Default Methods)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본 메소드란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 메소드는 인터페이스 내에서 메소드 선언뿐만 아니라 구현까지 제공하는 방법입니다. 이를 통해 인터페이스를 구현한 클래스들을 변경하지 않고도 새로운 메소드를 인터페이스에 추가할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 인터페이스를 구현한 클래스를 깨트리지 않고 새 기능을 추가할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주의사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 메소드는 구현체가 모르게 추가된 기능으로 인해 런타임 에러가 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;반드시 문서화하여 해당 메소드의 구현 명세를 명확히 해야 합니다. (@implSpec 자바독 태그 사용)&lt;/li&gt;
&lt;li&gt;Object 클래스가 제공하는 기능(equals, hashCode)은 기본 메소드로 제공할 수 없습니다. 이 경우 구현체가 해당 메소드를 재정의해야 합니다.&lt;/li&gt;
&lt;li&gt;자신이 수정할 수 있는 인터페이스에만 기본 메소드를 제공해야 합니다.&lt;/li&gt;
&lt;li&gt;인터페이스를 상속받는 인터페이스에서 기본 메소드를 추상 메소드로 변경할 수 있습니다.&lt;/li&gt;
&lt;li&gt;인터페이스 구현체가 기본 메소드를 재정의할 수도 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본 메소드 정의&lt;/h3&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public interface MyInterface {

    /**
     * Perform an action.
     *
     * @implSpec
     * This implementation prints &quot;Default action&quot;.
     */
    default void performAction() {
        System.out.println(&quot;Default action&quot;);
    }

    // Abstract method
    void abstractMethod();
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인터페이스 구현 클래스&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class MyClass implements MyInterface {

    @Override
    public void abstractMethod() {
        System.out.println(&quot;Abstract method implementation&quot;);
    }

    // 기본 메소드를 재정의할 수도 있습니다.
    @Override
    public void performAction() {
        System.out.println(&quot;Custom action&quot;);
    }

    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        myClass.performAction(); // Output: Custom action
        myClass.abstractMethod(); // Output: Abstract method implementation
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 스태틱 메소드 (Static Methods)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스태틱 메소드란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스에 타입 관련 헬퍼 또는 유틸리티 메소드를 제공하기 위해 스태틱 메소드를 사용할 수 있습니다. 이를 통해 인터페이스와 관련된 공통 기능을 인터페이스 자체에 정의할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스태틱 메소드 정의&lt;/h3&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public interface UtilityInterface {

    // 스태틱 메소드 정의
    static void utilityMethod() {
        System.out.println(&quot;Utility method&quot;);
    }

    // Abstract method
    void anotherMethod();
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스태틱 메소드 사용&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class UtilityClass implements UtilityInterface {

    @Override
    public void anotherMethod() {
        System.out.println(&quot;Another method implementation&quot;);
    }

    public static void main(String[] args) {
        UtilityClass utilityClass = new UtilityClass();
        utilityClass.anotherMethod(); // Output: Another method implementation

        // 스태틱 메소드는 인터페이스명으로 직접 호출
        UtilityInterface.utilityMethod(); // Output: Utility method
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 가이드는 Java 8의 기본 메소드와 스태틱 메소드를 사용하는 방법에 대해 설명합니다. 기본 메소드는 인터페이스에 새로운 기능을 추가할 때 유용하지만, 문서화와 런타임 에러에 주의해야 합니다. 스태틱 메소드는 유틸리티 메소드를 인터페이스에 추가할 때 유용합니다. 이를 통해 인터페이스를 더욱 유연하고 기능적으로 사용할 수 있습니다.&lt;/p&gt;</description>
      <category>프로그래밍언어/JAVA</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/224</guid>
      <comments>https://shoney.tistory.com/entry/java8-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EC%9D%98-%EB%94%94%ED%8F%B4%ED%8A%B8-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%8A%A4%ED%83%9C%ED%8B%B1-%EB%A9%94%EC%84%9C%EB%93%9C#entry224comment</comments>
      <pubDate>Fri, 14 Jun 2024 14:31:41 +0900</pubDate>
    </item>
    <item>
      <title>[Java8] Optional 사용 방법과 예시</title>
      <link>https://shoney.tistory.com/entry/Java-Optional-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95%EA%B3%BC-%EC%98%88%EC%8B%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Java 8의 Optional 클래스와 관련된 다양한 메서드들을 정리하고, 각 메서드에 대한 예제를 작성해 보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Optional 만들기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Optional.of()&lt;/b&gt;: null이 아닌 값을 가지는 Optional을 생성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Optional.ofNullable()&lt;/b&gt;: 값이 null일 수 있는 Optional을 생성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Optional.empty()&lt;/b&gt;: 비어있는 Optional을 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Optional&amp;lt;String&amp;gt; optional1 = Optional.of(&quot;Hello&quot;); // &quot;Hello&quot;를 포함하는 Optional 생성
Optional&amp;lt;String&amp;gt; optional2 = Optional.ofNullable(null); // null을 허용하는 Optional 생성
Optional&amp;lt;String&amp;gt; optional3 = Optional.empty(); // 비어있는 Optional 생성

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Optional에 값이 있는지 없는지 확인하기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;isPresent()&lt;/b&gt;: 값이 존재하는지 확인합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;isEmpty()&lt;/b&gt;: 값이 존재하지 않는지 확인합니다. (Java 11부터 제공)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Optional&amp;lt;String&amp;gt; optional = Optional.of(&quot;Hello&quot;);

if (optional.isPresent()) {
    System.out.println(&quot;Value is present&quot;);
}

if (optional.isEmpty()) { // Java 11부터 사용 가능
    System.out.println(&quot;Value is empty&quot;);
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Optional에 있는 값 가져오기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;get()&lt;/b&gt;: 값을 가져옵니다. 값이 없으면 NoSuchElementException을 던집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Optional&amp;lt;String&amp;gt; optional = Optional.of(&quot;Hello&quot;);
String value = optional.get(); // &quot;Hello&quot; 반환

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Optional에 값이 있는 경우에 그 값을 가지고 ~~를 하라&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ifPresent(Consumer)&lt;/b&gt;: 값이 존재하면 Consumer를 실행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;Optional&amp;lt;String&amp;gt; optional = Optional.of(&quot;Spring Course&quot;);
optional.ifPresent(value -&amp;gt; {
    if (value.startsWith(&quot;Spring&quot;)) {
        System.out.println(&quot;Course ID: 123&quot;);
    }
});

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Optional에 값이 있으면 가져오고 없는 경우에 ~~를 리턴하라&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;orElse(T)&lt;/b&gt;: 값이 없으면 기본값을 리턴합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Optional&amp;lt;String&amp;gt; optional = Optional.ofNullable(null);
String value = optional.orElse(&quot;Default Course&quot;); // &quot;Default Course&quot; 반환

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. Optional에 값이 있으면 가져오고 없는 경우에 ~~를 하라&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;orElseGet(Supplier)&lt;/b&gt;: 값이 없으면 Supplier를 실행하여 값을 리턴합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Optional&amp;lt;String&amp;gt; optional = Optional.ofNullable(null);
String value = optional.orElseGet(() -&amp;gt; &quot;Created New Course&quot;); // &quot;Created New Course&quot; 반환

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. Optional에 값이 있으면 가져오고 없는 경우 에러를 던져라&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;orElseThrow()&lt;/b&gt;: 값이 없으면 예외를 던집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Optional&amp;lt;String&amp;gt; optional = Optional.ofNullable(null);
String value = optional.orElseThrow(() -&amp;gt; new IllegalArgumentException(&quot;No value present&quot;));

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. Optional에 들어있는 값 걸러내기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;filter(Predicate)&lt;/b&gt;: 값이 Predicate를 만족하면 값을 포함하는 Optional을 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Optional&amp;lt;String&amp;gt; optional = Optional.of(&quot;Spring Course&quot;);
optional.filter(value -&amp;gt; value.startsWith(&quot;Spring&quot;))
        .ifPresent(System.out::println); // &quot;Spring Course&quot; 출력

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9. Optional에 들어있는 값 변환하기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;map(Function)&lt;/b&gt;: 값을 변환하여 새로운 Optional을 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;flatMap(Function)&lt;/b&gt;: Optional 안에 들어있는 인스턴스가 Optional인 경우 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Optional&amp;lt;String&amp;gt; optional = Optional.of(&quot;Spring Course&quot;);

// map 사용 예
Optional&amp;lt;Integer&amp;gt; lengthOptional = optional.map(String::length);
lengthOptional.ifPresent(System.out::println); // 13 출력

// flatMap 사용 예
Optional&amp;lt;Optional&amp;lt;String&amp;gt;&amp;gt; nestedOptional = Optional.of(Optional.of(&quot;Nested Value&quot;));
nestedOptional.flatMap(Function.identity())
              .ifPresent(System.out::println); // &quot;Nested Value&quot; 출력

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;종합 예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 Optional 메서드를 사용하는 예제 코드를 보여드리겠습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import java.util.Optional;

public class OptionalExample {

    public static void main(String[] args) {
        // 1. Optional 만들기
        Optional&amp;lt;String&amp;gt; optional1 = Optional.of(&quot;Hello&quot;);
        Optional&amp;lt;String&amp;gt; optional2 = Optional.ofNullable(null);
        Optional&amp;lt;String&amp;gt; optional3 = Optional.empty();

        // 2. Optional에 값이 있는지 없는지 확인하기
        if (optional1.isPresent()) {
            System.out.println(&quot;Value is present: &quot; + optional1.get());
        }
        if (optional3.isEmpty()) {
            System.out.println(&quot;Value is empty&quot;);
        }

        // 3. Optional에 있는 값 가져오기
        try {
            String value = optional2.get(); // NoSuchElementException 발생
        } catch (Exception e) {
            System.out.println(&quot;Caught exception: &quot; + e);
        }

        // 4. Optional에 값이 있는 경우에 그 값을 가지고 ~~를 하라
        optional1.ifPresent(value -&amp;gt; System.out.println(&quot;Value is: &quot; + value));

        // 5. Optional에 값이 있으면 가져오고 없는 경우에 ~~를 리턴하라
        String defaultValue = optional2.orElse(&quot;Default Value&quot;);
        System.out.println(&quot;Default value: &quot; + defaultValue);

        // 6. Optional에 값이 있으면 가져오고 없는 경우에 ~~를 하라
        String orElseGetValue = optional2.orElseGet(() -&amp;gt; &quot;Created New Value&quot;);
        System.out.println(&quot;OrElseGet value: &quot; + orElseGetValue);

        // 7. Optional에 값이 있으면 가져오고 없는 경우 에러를 던져라
        try {
            String orElseThrowValue = optional2.orElseThrow(() -&amp;gt; new IllegalArgumentException(&quot;No value present&quot;));
        } catch (Exception e) {
            System.out.println(&quot;Caught exception: &quot; + e);
        }

        // 8. Optional에 들어있는 값 걸러내기
        Optional&amp;lt;String&amp;gt; filteredOptional = optional1.filter(value -&amp;gt; value.startsWith(&quot;H&quot;));
        filteredOptional.ifPresent(value -&amp;gt; System.out.println(&quot;Filtered value: &quot; + value));

        // 9. Optional에 들어있는 값 변환하기
        Optional&amp;lt;Integer&amp;gt; lengthOptional = optional1.map(String::length);
        lengthOptional.ifPresent(length -&amp;gt; System.out.println(&quot;Length: &quot; + length));

        Optional&amp;lt;Optional&amp;lt;String&amp;gt;&amp;gt; nestedOptional = Optional.of(Optional.of(&quot;Nested Value&quot;));
        nestedOptional.flatMap(Function.identity())
                      .ifPresent(nestedValue -&amp;gt; System.out.println(&quot;Nested value: &quot; + nestedValue));
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제는 다양한 Optional 메서드를 사용하는 방법을 보여줍니다. 각 메서드가 어떻게 동작하는지, 어떤 상황에서 사용해야 하는지 이해하는 데 도움이 될 것입니다.&lt;/p&gt;</description>
      <category>프로그래밍언어/JAVA</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/223</guid>
      <comments>https://shoney.tistory.com/entry/Java-Optional-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95%EA%B3%BC-%EC%98%88%EC%8B%9C#entry223comment</comments>
      <pubDate>Wed, 12 Jun 2024 22:41:30 +0900</pubDate>
    </item>
    <item>
      <title>[Java8] Optional을 사용할 때 주의해야 할 사항과 관련된 예제 소스</title>
      <link>https://shoney.tistory.com/entry/Java-Optional%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%A0-%EB%95%8C-%EC%A3%BC%EC%9D%98%ED%95%B4%EC%95%BC-%ED%95%A0-%EC%82%AC%ED%95%AD%EA%B3%BC-%EA%B4%80%EB%A0%A8%EB%90%9C-%EC%98%88%EC%A0%9C-%EC%86%8C%EC%8A%A4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Java 8에서 Optional을 사용할 때 주의해야 할 사항과 관련된 예제 소스&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 리턴값으로만 쓰기를 권장한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메소드의 매개변수 타입, 맵의 키 타입, 인스턴스 필드 타입으로 사용하지 않는 것이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;// 권장되지 않는 사용 예
public void process(Optional&amp;lt;String&amp;gt; input) {
    // ...
}

// 권장되는 사용 예
public Optional&amp;lt;String&amp;gt; process() {
    // ...
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Optional을 리턴하는 메소드에서 null을 리턴하지 말자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Optional을 반환하는 메소드에서는 절대로 null을 반환하지 않도록 합니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;// 잘못된 사용 예
public Optional&amp;lt;String&amp;gt; getValue() {
    return null; // 절대 이렇게 하지 말 것
}

// 올바른 사용 예
public Optional&amp;lt;String&amp;gt; getValue() {
    return Optional.ofNullable(null); // null일 경우 빈 Optional을 반환
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 프리미티브 타입용 Optional이 따로 있다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프리미티브 타입을 위한 OptionalInt, OptionalLong 등을 사용합니다. (성능, 명확한 의도 표현)&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;// Optional 사용 예
public OptionalInt getOptionalInt() {
    int value = ...;
    return (value != -1) ? OptionalInt.of(value) : OptionalInt.empty();
}

&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OptionalInt, OptionalLong, ...&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Collection, Map, Stream, Array, Optional을 Optional로 감싸지 말 것&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collection, Map, Stream, Array, Optional 타입을 다시 Optional로 감싸지 않도록 합니다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;// 잘못된 사용 예
public Optional&amp;lt;Optional&amp;lt;String&amp;gt;&amp;gt; getOptionalValue() {
    Optional&amp;lt;String&amp;gt; value = Optional.of(&quot;test&quot;);
    return Optional.of(value); // 이런 중첩은 피할 것
}

// 올바른 사용 예
public Optional&amp;lt;String&amp;gt; getOptionalValue() {
    return Optional.of(&quot;test&quot;);
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;종합 예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 주의사항을 종합하여 하나의 클래스 예제로 보여드리겠습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import java.util.Optional;
import java.util.OptionalInt;

public class OptionalExample {

    // 리턴값으로만 Optional 사용
    public Optional&amp;lt;String&amp;gt; findValue() {
        String value = null; // 비어있을 수 있는 값
        return Optional.ofNullable(value);
    }

    // null을 리턴하지 말고 Optional.empty() 사용
    public Optional&amp;lt;String&amp;gt; getValue() {
        String value = null;
        return (value != null) ? Optional.of(value) : Optional.empty();
    }

    // 프리미티브 타입용 Optional 사용
    public OptionalInt findOptionalInt() {
        int value = -1; // 기본값
        return (value != -1) ? OptionalInt.of(value) : OptionalInt.empty();
    }

    // Collection, Map, Stream, Array, Optional 타입을 Optional로 감싸지 말 것
    public Optional&amp;lt;String&amp;gt; getNestedOptional() {
        return Optional.of(&quot;test&quot;);
    }

    public static void main(String[] args) {
        OptionalExample example = new OptionalExample();

        // findValue 메서드 사용
        example.findValue().ifPresent(System.out::println);

        // getValue 메서드 사용
        example.getValue().ifPresent(System.out::println);

        // findOptionalInt 메서드 사용
        example.findOptionalInt().ifPresent(System.out::println);

        // getNestedOptional 메서드 사용
        example.getNestedOptional().ifPresent(System.out::println);
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제 코드는 Optional을 올바르게 사용하는 방법을 보여줍니다. 메서드는 Optional을 리턴하지만, 메서드 매개변수나 클래스 필드로는 사용하지 않습니다. 또한 Optional을 리턴할 때 null을 리턴하지 않고, 프리미티브 타입용 Optional을 사용하며, 중첩 Optional을 사용하지 않습니다.&lt;/p&gt;</description>
      <category>프로그래밍언어/JAVA</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/222</guid>
      <comments>https://shoney.tistory.com/entry/Java-Optional%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%A0-%EB%95%8C-%EC%A3%BC%EC%9D%98%ED%95%B4%EC%95%BC-%ED%95%A0-%EC%82%AC%ED%95%AD%EA%B3%BC-%EA%B4%80%EB%A0%A8%EB%90%9C-%EC%98%88%EC%A0%9C-%EC%86%8C%EC%8A%A4#entry222comment</comments>
      <pubDate>Wed, 12 Jun 2024 22:35:42 +0900</pubDate>
    </item>
    <item>
      <title>[React] JWT Util만들기, Axios 인터셉터 설정하기</title>
      <link>https://shoney.tistory.com/entry/React-JWT-Util%EB%A7%8C%EB%93%A4%EA%B8%B0-Axios-%EC%9D%B8%ED%84%B0%EC%85%89%ED%84%B0-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 애플리케이션에서 JWT(JSON Web Token)을 활용하여 Axios를 호출하기 전에 액세스 토큰을 요청하여 사용할 수 있는 유틸리티를 만들어 보았습니다. 이를 통해 애플리케이션에서 보안적인 통신을 구현할 수 있게 되었습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JWT Util로 Axios 인터셉터 설정하기&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import axios from &quot;axios&quot;;
import { getCookie, setCookie } from &quot;./cookieUtil&quot;;
import { API_SERVER_HOST } from &quot;../api/todoApi&quot;;

// Axios 인스턴스 생성
const jwtAxios = axios.create();

// JWT 갱신 함수
const refreshJWT = async (accessToken, refreshToken) =&amp;gt; {
  const host = API_SERVER_HOST;
  const header = { headers: { Authorization: `Bearer ${accessToken}` } };
  const res = await axios.get(
    `${host}/api/member/refresh?refreshToken=${refreshToken}`,
    header
  );
  console.log(res.data);
  return res.data;
};

// 요청 전 처리
const beforeReq = (config) =&amp;gt; {
  console.log(&quot;before request.............&quot;);

  const memberInfo = getCookie(&quot;member&quot;);
  if (!memberInfo) {
    console.log(&quot;Member NOT FOUND&quot;);
    return Promise.reject({
      response: {
        data: { error: &quot;REQUIRE_LOGIN&quot; },
      },
    });
  }
  const { accessToken } = memberInfo;
  config.headers.Authorization = `Bearer ${accessToken}`;
  return config;
};

// 실패한 요청 처리
const requestFail = (err) =&amp;gt; {
  console.log(&quot;request error............&quot;);
  return Promise.reject(err);
};

// 응답 전 처리
const beforeRes = async (res) =&amp;gt; {
  console.log(&quot;before return response...........&quot;);

  const data = res.data;

  if (data &amp;amp;&amp;amp; data.error === &quot;ERROR_ACCESS_TOKEN&quot;) {
    const memberCookieValue = getCookie(&quot;member&quot;);

    const result = await refreshJWT(
      memberCookieValue.accessToken,
      memberCookieValue.refreshToken
    );

    // 새로운 토큰으로 쿠키 갱신 필요
    memberCookieValue.accessToken = result.accessToken;
    memberCookieValue.refreshToken = result.refreshToken;
    setCookie(&quot;member&quot;, JSON.stringify(memberCookieValue), 1);

    // 새로운 액세스 토큰으로 원래 하려고 했던 작업을 다시 작업
    const originalRequest = res.config;
    originalRequest.headers.Authorization = `Bearer ${result.accessToken}`;

    return await axios(originalRequest);
  }
  return res;
};

// 실패한 응답 처리
const responseFail = (err) =&amp;gt; {
  console.log(&quot;response fail error.............&quot;);
  return Promise.reject(err);
};

// Axios 인터셉터 설정
jwtAxios.interceptors.request.use(beforeReq, requestFail);
jwtAxios.interceptors.response.use(beforeRes, responseFail);

export default jwtAxios;

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 포인트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Axios 인스턴스를 생성하여 JWT 관련 설정을 추가합니다.&lt;/li&gt;
&lt;li&gt;요청 전, 응답 전, 실패한 요청, 실패한 응답에 대한 처리 함수를 정의하여 인터셉터에 추가합니다.&lt;/li&gt;
&lt;li&gt;액세스 토큰이 만료된 경우, 리프레시 토큰을 사용하여 새로운 액세스 토큰을 요청하고 쿠키를 갱신합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 위의 JWT Util을 사용하여 Axios를 호출하면, 보안적인 통신을 구현할 수 있게 됩니다. 사용자의 인증 및 권한을 효과적으로 관리하여 안전한 애플리케이션을 개발할 수 있습니다.&lt;/p&gt;</description>
      <category>Front-End/React</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/221</guid>
      <comments>https://shoney.tistory.com/entry/React-JWT-Util%EB%A7%8C%EB%93%A4%EA%B8%B0-Axios-%EC%9D%B8%ED%84%B0%EC%85%89%ED%84%B0-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0#entry221comment</comments>
      <pubDate>Mon, 8 Apr 2024 22:10:32 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] JPA Query 메서드 DTO 바로 생성하기</title>
      <link>https://shoney.tistory.com/entry/Spring-JPA-Query-%EB%A9%94%EC%84%9C%EB%93%9C-DTO-%EB%B0%94%EB%A1%9C-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Data JPA를 사용하여 데이터베이스에서 정보를 가져올 때 DTO(DTO: Data Transfer Object)를 사용하는 경우가 많습니다. DTO는 보통 데이터베이스의 엔티티(Entity)와 클라이언트 간의 데이터 전송을 위해 사용됩니다. 이 글에서는 Spring Data JPA에서 Query 메서드를 사용하여 DTO를 바로 생성하는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. DTO 클래스 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO 클래스는 데이터 전송을 위한 객체입니다. 먼저 DTO 클래스를 생성해야 합니다. 예를 들어, 장바구니에 담긴 상품 정보를 전달하기 위한 CartItemListDto 클래스를 만들어봅시다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;package com.example.demo_mall.mallapi.dto;

public class CartItemListDto {
    private Long cino;
    private int qty;
    private String pname;
    private double price;
    private String fileName;

    // 생성자, 게터 및 세터 생략
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Repository 인터페이스 작성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 DTO를 사용하여 데이터를 가져올 Repository 인터페이스를 작성합니다. 여기서는 CartItemRepository를 예로 들어 설명하겠습니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface CartItemRepository extends JpaRepository&amp;lt;CartItem, Long&amp;gt; {

    @Query(&quot;select &quot; +
            &quot;new com.example.demo_mall.mallapi.dto.CartItemListDto(ci.cino, ci.qty, p.pname, p.price, pi.fileName) &quot; +
            &quot;from CartItem ci &quot; +
            &quot;inner join Cart mc on ci.cart = mc  &quot; +
            &quot;left join Product p on ci.product = p &quot; +
            &quot;left join p.imageList pi &quot; +
            &quot;where mc.owner.id = :id &quot; +
            &quot;and pi.ord = 0 &quot; +
            &quot;order by ci.cino desc&quot;)
    List&amp;lt;CartItemListDto&amp;gt; getItemsOfCartDtoById(@Param(&quot;memberId&quot;) Long id);
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서 @Query 어노테이션을 사용하여 직접 SQL 쿼리를 정의하고 있습니다. 그러나 여기서 특이한 점은 SQL 쿼리의 결과를 바로 CartItemListDto 객체로 매핑하고 있다는 것입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Query 메서드 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Data JPA에서는 Query 메서드를 사용하여 간단한 쿼리를 작성할 수 있습니다. 그러나 Query 메서드를 사용하여 DTO를 바로 생성하는 것은 조금 더 복잡합니다. 따라서 위와 같이 @Query 어노테이션을 사용하여 직접 SQL 쿼리를 작성하는 것이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제에서는 getItemsOfCartDtoById 메서드를 통해 CartItemListDto 객체를 바로 생성하고 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Spring Data JPA를 사용하여 Query 메서드를 통해 DTO를 바로 생성하는 방법을 알아보았습니다. DTO를 사용하여 데이터베이스에서 필요한 정보를 효율적으로 전달할 수 있습니다. 이를 통해 코드의 가독성과 유지 보수성을 향상시킬 수 있습니다.&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/220</guid>
      <comments>https://shoney.tistory.com/entry/Spring-JPA-Query-%EB%A9%94%EC%84%9C%EB%93%9C-DTO-%EB%B0%94%EB%A1%9C-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0#entry220comment</comments>
      <pubDate>Sun, 7 Apr 2024 15:49:26 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Security CSRF 비활성화 (6.1 버전 이상)</title>
      <link>https://shoney.tistory.com/entry/Spring-Security-CSRF-%EB%B9%84%ED%99%9C%EC%84%B1%ED%99%94-61-%EB%B2%84%EC%A0%84-%EC%9D%B4%EC%83%81</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;0. 먼저 CSRF란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSRF(Cross-Site Request Forgery, 사이트 간 요청 위조)는 웹 보안 취약점 중 하나로, 사용자가 자신의 의지와는 무관하게 공격자가 조작한 요청을, 신뢰할 수 있는 사이트에 대해 보내도록 만드는 공격입니다. 이 공격은 사용자가 해당 사이트에 로그인한 상태에서 이루어지면, 사용자의 권한을 이용하여 악의적인 변경사항(예: 비밀번호 변경, 무단 게시물 작성 등)을 적용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. CSRF 비활성화 방법 (시큐리티 6.1버전 이상)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 시큐리티 6버전부터는 보안 구성 방식에 몇 가지 변화가 있습니다. CSRF 보호를 비활성화하는 방법에도 변화가 있을 수 있으며, 새로운 방식을 적용해야 할 필요가 있습니다. 스프링 시큐리티 6버전에서 CSRF 보호를 비활성화하는 방식은 다음과 같습니다:&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.Customizer;

@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -&amp;gt; csrf.disable()) // CSRF 비활성화
            .authorizeHttpRequests(auth -&amp;gt; auth
                .anyRequest().permitAll()
            );

        return http.build();
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제에서는 .csrf(csrf -&amp;gt; csrf.disable()) 구문을 사용하여 CSRF 보호를 비활성화하고 있습니다. 이는 스프링 시큐리티 6버전에서 도입된 새로운 구성 방식을 반영한 것으로, 보다 선언적이고 람다 기반의 구성 방식을 사용하여 보안 구성을 더욱 간결하고 읽기 쉽게 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 시큐리티 6버전에서의 변경사항을 적용할 때는 공식 문서를 참조하는 것이 좋습니다. 스프링 프로젝트와 스프링 시큐리티 프로젝트의 문서는 항상 최신 기능과 변화된 사항을 상세히 설명하고 있으며, 보안 구성의 최선의 방법을 제시합니다.&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/219</guid>
      <comments>https://shoney.tistory.com/entry/Spring-Security-CSRF-%EB%B9%84%ED%99%9C%EC%84%B1%ED%99%94-61-%EB%B2%84%EC%A0%84-%EC%9D%B4%EC%83%81#entry219comment</comments>
      <pubDate>Fri, 9 Feb 2024 22:43:19 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] @RestControllerAdvice 사용해서 Controller 예외처리</title>
      <link>https://shoney.tistory.com/entry/Spring-RestControllerAdvice-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-Controller-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그림요청.webp&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baXHRf/btsEHplxCPc/eWloXgjbjm7h4bmbuXkix0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baXHRf/btsEHplxCPc/eWloXgjbjm7h4bmbuXkix0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baXHRf/btsEHplxCPc/eWloXgjbjm7h4bmbuXkix0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaXHRf%2FbtsEHplxCPc%2FeWloXgjbjm7h4bmbuXkix0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;370&quot; height=&quot;370&quot; data-filename=&quot;그림요청.webp&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;1. @RestControllerAdvice 사용해서 Controller 예외처리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@RestControllerAdvice 어노테이션은 Spring Framework에서 예외 처리를 위해 사용되는 특별한 클래스에 붙이는 어노테이션입니다. 이 어노테이션을 사용하면 전역에서 발생할 수 있는 예외를 캡처하고, 공통된 예외 처리 로직을 적용하여 API 응답을 일관성 있게 관리할 수 있습니다. @RestControllerAdvice는 @ControllerAdvice에 @ResponseBody가 추가된 형태로, REST API 개발 시 JSON 등의 응답 바디를 직접 제어할 수 있게 해줍니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-1. 동작 방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring의 AOP(Aspect-Oriented Programming)를 이용하여, 애플리케이션의 컨트롤러 계층에서 발생하는 예외를 잡아낸 후, 지정된 예외 처리 로직에 따라 처리합니다.&lt;/li&gt;
&lt;li&gt;@ExceptionHandler 어노테이션을 사용하여 특정 예외를 처리하는 메서드를 정의할 수 있습니다.&lt;/li&gt;
&lt;li&gt;@RestControllerAdvice 어노테이션을 사용한 클래스는 애플리케이션 전역에서 발생하는 예외를 처리할 수 있는 공통의 예외 처리기 역할을 합니다.&lt;/li&gt;
&lt;li&gt;예외 처리 메서드는 예외 유형별로 다르게 정의할 수 있으며, 예외 발생 시 적절한 HTTP 상태 코드와 메시지를 응답으로 반환할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-2. 전역 예외 처리기 구현&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.http.ResponseEntity;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity&amp;lt;String&amp;gt; handleGenericException(Exception e) {
        // 모든 예외를 처리하고 500 Internal Server Error 반환
        return ResponseEntity
                .status(500)
                .body(&quot;Internal Server Error: &quot; + e.getMessage());
    }

    @ExceptionHandler(value = NullPointerException.class)
    public ResponseEntity&amp;lt;String&amp;gt; handleNullPointerException(NullPointerException e) {
        // NullPointerException을 처리하고 400 Bad Request 반환
        return ResponseEntity
                .status(400)
                .body(&quot;Null Pointer Exception: &quot; + e.getMessage());
    }

    @ExceptionHandler(value = CustomException.class)
    public ResponseEntity&amp;lt;String&amp;gt; handleCustomException(CustomException e) {
        // 사용자 정의 예외 처리
        return ResponseEntity
                .status(e.getStatusCode())
                .body(&quot;Custom Error: &quot; + e.getMessage());
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시에서는 세 가지 예외 처리 메서드를 정의하고 있습니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Exception.class에 대한 처리: 모든 종류의 예외를 처리하며, HTTP 상태 코드 500을 반환합니다.&lt;/li&gt;
&lt;li&gt;NullPointerException.class에 대한 처리: NullPointerException을 처리하며, HTTP 상태 코드 400을 반환합니다.&lt;/li&gt;
&lt;li&gt;CustomException.class에 대한 처리: 사용자 정의 예외를 처리하며, 예외에 설정된 상태 코드와 메시지를 반환합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@RestControllerAdvice를 사용함으로써 애플리케이션 전역에서 발생하는 예외를 효과적으로 관리할 수 있으며, API 응답의 일관성을 유지할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h1&gt;2. 상속 관계에서의 예외처리는 어떻게 되는 것일까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서는 예외 처리 시 클래스 간의 상속 관계를 고려하여 더 구체적인 예외, 즉 자식 예외 클래스에 대한 처리기(**@ExceptionHandler**가 붙은 메서드)가 있을 경우, 그 처리기를 우선적으로 사용합니다. 이는 예외가 발생했을 때 더 세밀한 예외 처리를 가능하게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, **NullPointerException**이 **RuntimeException**의 자식 클래스인 경우, 두 예외 모두를 처리할 수 있는 **@ExceptionHandler**가 있다면 **NullPointerException**에 대한 처리기가 우선적으로 호출됩니다. 만약 해당하는 구체적인 예외를 처리하는 **@ExceptionHandler**가 없다면, 상위 클래스를 처리하는 **@ExceptionHandler**가 대신 호출됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식은 예외를 더 정확하게 처리하고, 다양한 예외 상황에 대해 더 상세한 응답을 제공하는 데 유용합니다. 예외 클래스의 상속 구조를 이해하고 올바르게 활용하면, 예외 처리 로직을 효과적으로 구성할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2-1. 상속 관계에서의 예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 상속 관계에 있는 예외를 처리하는 간단한 예시입니다. 이 예시에서는 RuntimeException과 그 자식 예외인 ArithmeticException을 처리하는 방법을 보여줍니다. ArithmeticException이 발생하면, 더 구체적인 예외 처리기인 handleArithmeticException 메서드가 호출되며, RuntimeException이나 그 외의 자식 예외들이 발생할 경우 handleRuntimeException 메서드가 호출됩니다.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.http.ResponseEntity;

@RestControllerAdvice
public class ExceptionHandlingController {

    // ArithmeticException 처리
    @ExceptionHandler(ArithmeticException.class)
    public ResponseEntity&amp;lt;String&amp;gt; handleArithmeticException(ArithmeticException e) {
        return ResponseEntity
                .badRequest()
                .body(&quot;Arithmetic Exception Occurred: &quot; + e.getMessage());
    }

    // RuntimeException 및 그 자식들 처리 (ArithmeticException 제외)
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity&amp;lt;String&amp;gt; handleRuntimeException(RuntimeException e) {
        return ResponseEntity
                .status(500)
                .body(&quot;Runtime Exception Occurred: &quot; + e.getMessage());
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시에서는 다음과 같은 작동 방식을 확인할 수 있습니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;ArithmeticException 발생 시&lt;/b&gt;: handleArithmeticException 메서드가 호출되어 HTTP 상태 코드 400(Bad Request)과 함께 &quot;Arithmetic Exception Occurred: [에러 메시지]&quot;라는 응답 바디를 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ArithmeticException 이외의 RuntimeException 발생 시&lt;/b&gt;: handleRuntimeException 메서드가 호출되어 HTTP 상태 코드 500(Internal Server Error)과 함께 &quot;Runtime Exception Occurred: [에러 메시지]&quot;라는 응답 바디를 반환합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식을 통해 애플리케이션에서 발생할 수 있는 다양한 예외 상황에 대해 더 세분화된 예외 처리를 구현할 수 있으며, 예외의 유형에 따라 적절한 응답을 제공할 수 있습니다.&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/218</guid>
      <comments>https://shoney.tistory.com/entry/Spring-RestControllerAdvice-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-Controller-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC#entry218comment</comments>
      <pubDate>Fri, 9 Feb 2024 21:15:06 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] @SuperBuilder 의 특징과 간단한 예제</title>
      <link>https://shoney.tistory.com/entry/Spring-SuperBuilder-%EC%9D%98-%ED%8A%B9%EC%A7%95%EA%B3%BC-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%98%88%EC%A0%9C</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;240&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FDIl9/btsEGR3ykbr/0E35sKVuB0x9uDB0lhdTU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FDIl9/btsEGR3ykbr/0E35sKVuB0x9uDB0lhdTU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FDIl9/btsEGR3ykbr/0E35sKVuB0x9uDB0lhdTU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFDIl9%2FbtsEGR3ykbr%2F0E35sKVuB0x9uDB0lhdTU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;240&quot; height=&quot;150&quot; data-origin-width=&quot;240&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;@SuperBuilder 의 특징과 간단한 예제&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@SuperBuilder는 Project Lombok 라이브러리의 어노테이션 중 하나로, 상속 구조를 가진 클래스에서 빌더 패턴을 쉽게 사용할 수 있게 해줍니다. 일반적인 @Builder 어노테이션과 달리 @SuperBuilder는 부모 클래스와 자식 클래스 간의 상속 관계를 고려하여 빌더를 생성합니다. 이를 통해 각 클래스의 필드를 초기화하는 데 필요한 빌더 메소드를 자동으로 생성해줍니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. @SuperBuilder의 장점&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;상속 구조 지원&lt;/b&gt;: 상속 구조에서도 빌더 패턴을 쉽게 구현할 수 있게 해줍니다. 부모 클래스의 필드와 자식 클래스의 필드를 모두 한 번에 설정할 수 있는 빌더 클래스를 생성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;타입 안전성&lt;/b&gt;: 빌더 패턴을 사용함으로써 객체를 생성할 때 타입 안전성을 보장받을 수 있습니다. 필요한 필드에 대한 값을 메소드 체이닝 방식으로 설정하기 때문에, 컴파일 타임에 타입 체크가 가능합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가독성 향상&lt;/b&gt;: 객체 생성 시 생성자에 여러 매개변수를 전달하는 대신, 메소드 체이닝을 통해 필요한 필드만을 명시적으로 설정할 수 있습니다. 이는 코드의 가독성을 크게 향상시킵니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;불변 객체 생성 용이&lt;/b&gt;: 빌더 패턴은 불변 객체(Immutable Objects)를 생성하기에 적합한 패턴입니다. 객체 생성 후 상태를 변경할 필요가 없는 경우, 빌더를 통해 불변 객체를 손쉽게 생성할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. @SuperBuilder 예제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모 클래스 Vehicle과 이를 상속받는 자식 클래스 Car가 있다고 가정해보겠습니다. @SuperBuilder를 사용하여 빌더 패턴을 적용하는 예제 코드는 다음과 같습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-1. 부모 클래스 (Vehicle.java)&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import lombok.experimental.SuperBuilder;

@SuperBuilder
public class Vehicle {
    private String brand;
    private String model;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-2. 자식 클래스 (Car.java)&lt;/h3&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import lombok.experimental.SuperBuilder;

@SuperBuilder
public class Car extends Vehicle {
    private int seats;
    private boolean sportsCar;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-3. 객체 생성 예제&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Main {
    public static void main(String[] args) {
        Car car = Car.builder()
                      .brand(&quot;Toyota&quot;)
                      .model(&quot;Supra&quot;)
                      .seats(2)
                      .sportsCar(true)
                      .build();

        System.out.println(car);
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제에서는 @SuperBuilder를 사용하여 Vehicle 클래스와 Car 클래스에 대한 빌더를 생성했습니다. Car 객체를 생성할 때 Car.builder() 메소드를 사용하여 부모 클래스인 Vehicle의 필드(brand, model)와 Car의 필드(seats, sportsCar)를 모두 설정할 수 있습니다. 이렇게 @SuperBuilder를 사용하면, 상속 구조에서도 각 클래스의 필드를 쉽게 설정하며 객체를 생성할 수 있습니다.&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/217</guid>
      <comments>https://shoney.tistory.com/entry/Spring-SuperBuilder-%EC%9D%98-%ED%8A%B9%EC%A7%95%EA%B3%BC-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%98%88%EC%A0%9C#entry217comment</comments>
      <pubDate>Fri, 9 Feb 2024 16:53:22 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] DTO 간 복사(BeanUtils, ModelMapper커스텀 하는방법 포함)</title>
      <link>https://shoney.tistory.com/entry/Spring-DTO-%EA%B0%84-%EB%B3%B5%EC%82%ACBeanUtils-ModelMapper%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%95%98%EB%8A%94%EB%B0%A9%EB%B2%95-%ED%8F%AC%ED%95%A8</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;376&quot; data-origin-height=&quot;124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vUxM8/btsEFPd5f0k/0676prvKnR50weNkstMbs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vUxM8/btsEFPd5f0k/0676prvKnR50weNkstMbs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vUxM8/btsEFPd5f0k/0676prvKnR50weNkstMbs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvUxM8%2FbtsEFPd5f0k%2F0676prvKnR50weNkstMbs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;376&quot; height=&quot;124&quot; data-origin-width=&quot;376&quot; data-origin-height=&quot;124&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;DTO 간 복사&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 DTO 간 변수를 복사하는 경우, 주로 BeanUtils.copyProperties 메소드를 사용하거나 ModelMapper 라이브러리를 활용할 수 있습니다. 아래에 두 방법을 사용하는 예시를 제시하겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. BeanUtils 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BeanUtils.copyProperties 메소드는 스프링 프레임워크에 포함된 유틸리티 메소드로서, 한 객체의 프로퍼티 값을 다른 객체로 복사하는 데 사용됩니다. 이 방법은 추가적인 라이브러리를 필요로 하지 않으며, Spring Framework에 기본적으로 포함되어 있습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import org.springframework.beans.BeanUtils;

// Source DTO
public class SourceDto {
    private String name;
    private int age;
    // getters and setters
}

// Target DTO
public class TargetDto {
    private String name;
    private int age;
    // getters and setters
}

SourceDto source = new SourceDto();
// source 객체 초기화

TargetDto target = new TargetDto();
BeanUtils.copyProperties(source, target);

&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. ModelMapper 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ModelMapper는 더 복잡한 매핑이 필요할 때 유용하며, 객체 간의 데이터 매핑을 보다 세밀하게 제어할 수 있습니다. 이 라이브러리는 별도로 추가해야 합니다. ModelMapper를 사용하면 복잡한 매핑 규칙도 쉽게 정의할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- pom.xml --&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.modelmapper&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;modelmapper&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.3.8&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import org.modelmapper.ModelMapper;

// Source DTO
public class SourceDto {
    private String name;
    private int age;
    // getters and setters
}

// Target DTO
public class TargetDto {
    private String name;
    private int age;
    // getters and setters
}

ModelMapper modelMapper = new ModelMapper();
SourceDto source = new SourceDto();
// source 객체 초기화

TargetDto target = modelMapper.map(source, TargetDto.class);

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ModelMapper는 좀 더 복잡한 변환 작업에 유리하며, 자동 매핑 외에도 맞춤 매핑 전략을 정의할 수 있습니다. BeanUtils에 비해 세밀한 제어가 가능하지만, 성능면에서는 BeanUtils가 더 빠를 수 있습니다. 따라서 상황에 맞게 선택하여 사용하시면 됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. ModelMapper 커스텀 하는 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ModelMapper는 복잡한 변환 작업에 유리한 여러 가지 이유가 있는데, 그 중 하나는 객체 간의 필드 이름이 다르거나, 변환 로직이 필요할 때 보다 유연하게 대응할 수 있다는 점입니다. 예를 들어, 한 객체의 필드를 다른 객체의 여러 필드로 나누거나, 반대로 여러 필드를 하나로 합치는 등의 작업을 ModelMapper를 사용하여 쉽게 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시에서는 ModelMapper를 사용하여 서로 다른 필드 이름을 매핑하고, 특정 필드에 대해 커스텀 변환 로직을 적용하는 방법을 보여줍니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-1. Source DTO&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class SourceDto {
    private String fullName; // TargetDto에서는 firstName과 lastName으로 나누어져 있음
    private String email;
    private String joinDate; // String 형식, TargetDto에서는 LocalDate 형식으로 변환 필요
    // getters and setters
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-2. Target DTO&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class TargetDto {
    private String firstName;
    private String lastName;
    private String email;
    private LocalDate joinDate; // SourceDto에서는 String 형식, 여기서는 LocalDate로 변환
    // getters and setters
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-3. ModelMapper 설정 및 사용&lt;/h3&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;import org.modelmapper.ModelMapper;
import org.modelmapper.PropertyMap;
import org.modelmapper.Converter;
import org.modelmapper.spi.MappingContext;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

ModelMapper modelMapper = new ModelMapper();

// fullName을 firstName과 lastName으로 나누는 커스텀 매핑
modelMapper.addMappings(new PropertyMap&amp;lt;SourceDto, TargetDto&amp;gt;() {
    @Override
    protected void configure() {
        map().setEmail(source.getEmail());
        using((Converter&amp;lt;String, String&amp;gt;) context -&amp;gt; context.getSource().split(&quot; &quot;)[0]).map(source.getFullName(), destination.getFirstName());
        using((Converter&amp;lt;String, String&amp;gt;) context -&amp;gt; context.getSource().split(&quot; &quot;).length &amp;gt; 1 ? context.getSource().split(&quot; &quot;)[1] : null).map(source.getFullName(), destination.getLastName());
        // joinDate 변환
        using(new Converter&amp;lt;String, LocalDate&amp;gt;() {
            @Override
            public LocalDate convert(MappingContext&amp;lt;String, LocalDate&amp;gt; context) {
                return LocalDate.parse(context.getSource(), DateTimeFormatter.ofPattern(&quot;yyyy-MM-dd&quot;));
            }
        }).map(source.getJoinDate(), destination.getJoinDate());
    }
});

SourceDto source = new SourceDto();
// source 객체 초기화

TargetDto target = modelMapper.map(source, TargetDto.class);

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시에서는 ModelMapper의 addMappings 메소드를 사용하여 커스텀 매핑을 정의했습니다. fullName을 firstName과 lastName으로 나누는 로직과 joinDate를 String에서 LocalDate로 변환하는 로직을 구현했습니다. 이처럼 ModelMapper는 필드 이름이 다르거나, 특정 필드에 대한 변환 로직이 필요할 때 유연하게 대응할 수 있는 강력한 도구입니다.&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/216</guid>
      <comments>https://shoney.tistory.com/entry/Spring-DTO-%EA%B0%84-%EB%B3%B5%EC%82%ACBeanUtils-ModelMapper%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%95%98%EB%8A%94%EB%B0%A9%EB%B2%95-%ED%8F%AC%ED%95%A8#entry216comment</comments>
      <pubDate>Fri, 9 Feb 2024 13:18:24 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 시큐리티 예외 처리와 사용자 인증 (시큐리티6 이상)</title>
      <link>https://shoney.tistory.com/entry/Spring-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC%EC%99%80-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%B8%EC%A6%9D</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;130&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dwy4g1/btsEbD6vLGo/7YT98P48Mrur40uZJbOfck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dwy4g1/btsEbD6vLGo/7YT98P48Mrur40uZJbOfck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dwy4g1/btsEbD6vLGo/7YT98P48Mrur40uZJbOfck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdwy4g1%2FbtsEbD6vLGo%2F7YT98P48Mrur40uZJbOfck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;130&quot; height=&quot;127&quot; data-origin-width=&quot;130&quot; data-origin-height=&quot;127&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 시큐리티는 애플리케이션의 보안을 강화하고, 다양한 상황에서의 인증 및 권한 부여를 관리합니다. 오늘은 스프링 시큐리티의 exceptionHandling() 메서드를 활용한 예외 처리 방법과 사용자 인증 과정에 대해 알아보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예외 처리 설정 (exceptionHandling())&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 시큐리티에서 예외 처리는 보안 관련 예외가 발생했을 때 적절한 대응을 할 수 있도록 돕습니다. 대표적으로 두 가지 핸들러를 설정할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. &lt;b&gt;AuthenticationEntryPoint (인증 진입점)&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;.exceptionHandling(xh -&amp;gt; xh
    .authenticationEntryPoint(new AuthenticationEntryPoint() {
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
            response.sendRedirect(&quot;/login&quot;);
        }
    })
)

&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증되지 않은 사용자가 보호된 리소스에 접근하려 할 때 실행됩니다.&lt;/li&gt;
&lt;li&gt;예를 들어, 로그인하지 않은 사용자가 로그인이 필요한 페이지에 접근하려고 시도할 때, 로그인 페이지로 리다이렉트합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. &lt;b&gt;AccessDeniedHandler (접근 거부 핸들러)&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;.exceptionHandling(xh -&amp;gt; xh
    .accessDeniedHandler(new AccessDeniedHandler() {
        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
            response.sendRedirect(&quot;/denied&quot;);
        }
    })
)

&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 필요한 권한 없이 보호된 리소스에 접근하려 할 때 실행됩니다.&lt;/li&gt;
&lt;li&gt;예를 들어, 일반 사용자가 관리자 페이지에 접근하려고 시도할 때, 접근 거부 페이지로 리다이렉트합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 예외 처리 설정을 통해, 애플리케이션의 보안을 강화하고 사용자에게 적절한 피드백을 제공할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 로그인 성공 핸들러 (AuthenticationSuccessHandler)&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;.formLogin(f -&amp;gt; f
    .successHandler(new AuthenticationSuccessHandler() {
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            RequestCache requestCache = new HttpSessionRequestCache();
            SavedRequest savedRequest = requestCache.getRequest(request, response);
            String redirectUrl = savedRequest.getRedirectUrl();
            response.sendRedirect(redirectUrl);
        }
    })
)

&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 성공적으로 로그인한 후 실행되는 로직을 정의합니다.&lt;/li&gt;
&lt;li&gt;사용자가 원래 요청했던 URL로 리다이렉트하는 기능을 구현할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 설정을 통해 사용자 인증 과정을 보다 유연하게 관리하고, 사용자 경험을 개선할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 시큐리티의 이러한 기능들은 애플리케이션의 보안을 강화하는 데 핵심적인 역할을 합니다. 코드의 각 부분이 어떻게 작동하는지 이해하고 적절히 활용한다면, 보다 안전하고 사용자 친화적인 웹 애플리&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/215</guid>
      <comments>https://shoney.tistory.com/entry/Spring-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC%EC%99%80-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%B8%EC%A6%9D#entry215comment</comments>
      <pubDate>Tue, 30 Jan 2024 17:56:44 +0900</pubDate>
    </item>
    <item>
      <title>[Feasibility Study] 타당성 조사란</title>
      <link>https://shoney.tistory.com/entry/Feasibility-Study-%ED%83%80%EB%8B%B9%EC%84%B1-%EC%A1%B0%EC%82%AC%EB%9E%80</link>
      <description>&lt;div style=&quot;color: #0f0f0f; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1792&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1sMKJ/btsD5x0bvjp/0FFcSmBDLNIV2yYdwkO3L0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1sMKJ/btsD5x0bvjp/0FFcSmBDLNIV2yYdwkO3L0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1sMKJ/btsD5x0bvjp/0FFcSmBDLNIV2yYdwkO3L0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1sMKJ%2FbtsD5x0bvjp%2F0FFcSmBDLNIV2yYdwkO3L0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1792&quot; height=&quot;1024&quot; data-origin-width=&quot;1792&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;color: #0f0f0f; text-align: start;&quot; data-message-id=&quot;7c54e9d0-9d39-4985-a79f-0b4ce8420b3e&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;div style=&quot;color: #000000;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타당성 조사(Feasibility Study)에 대한 블로그 글&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;타당성 조사란 무엇인가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타당성 조사는 새로운 프로젝트, 제품, 서비스 등을 시작하기 전에 그 실현 가능성과 경제적 타당성을 평가하는 중요한 과정입니다. 이 과정을 통해 프로젝트의 성공 가능성을 높이고, 실패 위험을 최소화할 수 있습니다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;타당성 조사의 주요 요소&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;시장 타당성&lt;/b&gt;: 시장의 수요, 경쟁 상황 분석을 통해 제품이나 서비스의 시장 내 성공 가능성을 평가합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기술적 타당성&lt;/b&gt;: 프로젝트나 제품이 기술적으로 실행 가능한지를 평가합니다. 여기에는 필요한 기술, 자원, 전문 지식 등이 포함됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;경제적 타당성&lt;/b&gt;: 비용과 예상 수익을 비교하여 프로젝트나 제품의 경제적 수익성을 분석합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;법적 및 규제적 타당성&lt;/b&gt;: 프로젝트나 제품이 관련 법률 및 규정을 준수하는지 평가합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;조직적 타당성&lt;/b&gt;: 프로젝트가 조직의 목표와 전략에 부합하며 조직 내에서 실행 가능한지를 평가합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;타당성 조사의 중요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타당성 조사는 프로젝트나 사업의 성공 가능성을 높이고, 실패 위험을 최소화하기 위해 필수적인 과정입니다. 충분한 조사와 분석을 통해 투자의 위험을 줄이고, 보다 효과적인 의사결정을 할 수 있게 됩니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>비즈니스</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/213</guid>
      <comments>https://shoney.tistory.com/entry/Feasibility-Study-%ED%83%80%EB%8B%B9%EC%84%B1-%EC%A1%B0%EC%82%AC%EB%9E%80#entry213comment</comments>
      <pubDate>Tue, 30 Jan 2024 13:00:45 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Security 6 버전 인메모리 사용자 설정 간단한 예제</title>
      <link>https://shoney.tistory.com/entry/Spring-Security-6-%EB%B2%84%EC%A0%84-%EC%9D%B8%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%84%A4%EC%A0%95-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%98%88%EC%A0%9C</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cu2fu0/btsEbFo2unz/KkAWY7SzLSQdg7azeERNZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cu2fu0/btsEbFo2unz/KkAWY7SzLSQdg7azeERNZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cu2fu0/btsEbFo2unz/KkAWY7SzLSQdg7azeERNZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcu2fu0%2FbtsEbFo2unz%2FKkAWY7SzLSQdg7azeERNZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;307&quot; height=&quot;137&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;권한 설정과 표현식&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Security 5에서는 **AuthenticationManagerBuilder**를 사용하여 메모리 인증을 설정할 수 있었지만, Spring Security 6에서는 몇 가지 변경이 있을 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. PassworEncoder 사용 필수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, Spring Security 6에서는 비밀번호 암호화에 대한 설정이 강화되었으므로 **{noop}**을 사용하는 것이 더 이상 권장되지 않습니다. 대신에 비밀번호 인코더를 명시적으로 지정해야 합니다. 그래서 아래와 같이 PasswordEncoder를 빈으로 등록하는 소스코드를 먼저 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. inMemoryAuthentication 사용해서 메모리 인증 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 Spring Security 6에 대한 예제입니다. 여기서는 **PasswordEncoder**를 사용하여 비밀번호를 인코딩하고 **inMemoryAuthentication**을 통해 사용자를 설정합니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Primary
@Bean
public AuthenticationManagerBuilder configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().withUser(&quot;user1&quot;).password(passwordEncoder().encode(&quot;1111&quot;)).roles(&quot;USER&quot;)
            .and().withUser(&quot;sys&quot;).password(passwordEncoder().encode(&quot;1111&quot;)).roles(&quot;SYS&quot;, &quot;USER&quot;)
            .and().withUser(&quot;admin&quot;).password(passwordEncoder().encode(&quot;1111&quot;)).roles(&quot;ADMIN&quot;, &quot;SYS&quot;, &quot;USER&quot;);
    return auth;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;AuthenticationManagerBuilder&lt;b&gt;를 사용하여 세 가지 유형의 사용자 - 일반 사용자(&lt;/b&gt;USER&lt;/b&gt;), 시스템 관리자(&lt;b&gt;SYS&lt;/b&gt;), 그리고 최고 관리자(&lt;b&gt;ADMIN&lt;/b&gt;) - 를 설정합니다.&lt;/li&gt;
&lt;li&gt;패스워드는 인코드 사용해야합니다. (시큐리티6 이상부터 필수)&lt;/li&gt;
&lt;li&gt;user에 roles를 통해 역할을 부여합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. url과 권한 Matcher&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로, HTTP 요청에 대한 보안을 설정합니다. &lt;b&gt;HttpSecurity&lt;/b&gt; 객체를 사용하여 다양한 HTTP 경로에 대한 접근 제어를 설정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 시큐리티 5에서는 antMatchers를 사용해서 권한설정을 했다면, 6버전 이상부터는 requestMatchers 를 사용해서 url의 권한을 매칭할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;x86asm&quot;&gt;&lt;code&gt;@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
            .authorizeHttpRequests(ar -&amp;gt; ar
                    .requestMatchers(&quot;/user&quot;).hasRole(&quot;USER&quot;)
                    .requestMatchers(&quot;/admin/pay&quot;).hasRole(&quot;ADMIN&quot;)  // 더 구체적인 범위가 위에 나와야합니다.
                    .requestMatchers(&quot;/admin/**&quot;).hasAnyRole(&quot;ADMIN&quot;, &quot;SYS&quot;) // 6.0 이하 버전에서는 antMatchers(&quot;/admin/**&quot;).access(&quot;hasRole('ADMIN') or hasRole('SYS')&quot;)
                    .anyRequest()
                    .authenticated()
            )
            .formLogin(withDefaults())
            .build();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;더 구체적인 범위가 위에 나와야지 세부 권한을 적용 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;이 설정에서는 &lt;b&gt;/user&lt;/b&gt; 경로는 &lt;b&gt;USER&lt;/b&gt; 역할을 가진 사용자만, **/admin/pay**는 &lt;b&gt;ADMIN&lt;/b&gt; 역할을 가진 사용자만 접근할 수 있습니다. &lt;b&gt;/admin/**&lt;/b&gt; 경로는 &lt;b&gt;ADMIN&lt;/b&gt; 또는 &lt;b&gt;SYS&lt;/b&gt; 역할을 가진 사용자가 접근할 수 있습니다. 그리고 모든 요청은 인증을 필요로 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Security를 이용하면 몇 가지 간단한 설정만으로도 강력한 인증 및 권한 관리 시스템을 구현할 수 있습니다. 이 글이 Spring Security의 기본적인 사용 방법을 이해하는 데 도움이 되길 바랍니다.&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/212</guid>
      <comments>https://shoney.tistory.com/entry/Spring-Security-6-%EB%B2%84%EC%A0%84-%EC%9D%B8%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%84%A4%EC%A0%95-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%98%88%EC%A0%9C#entry212comment</comments>
      <pubDate>Tue, 30 Jan 2024 12:41:15 +0900</pubDate>
    </item>
    <item>
      <title>[Swagger] Spring 3.0 이상 버전에서 스웨거 적용하는 방법</title>
      <link>https://shoney.tistory.com/entry/Swagger-Spring-30-%EC%9D%B4%EC%83%81-%EB%B2%84%EC%A0%84%EC%97%90%EC%84%9C-%EC%8A%A4%EC%9B%A8%EA%B1%B0-%EC%A0%81%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;0. 기존 SpringFox로 설치할 경우 발생하는 문제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 방법대로 springfox 스웨거를 implementaion하면 에러가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기존 방식&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;implementation 'io.springfox:springfox-swagger2:2.9.2'
implementation 'io.springfox:springfox-swagger-ui:2.9.2'&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 방식대로 했을 경우 아래와 같은 에러가 발생하는 것을 확인 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #80cbc4; border: 1px solid #0f111a; padding: 2px 3px 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt;print 결과&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; border: #0F111A 1px solid; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;Action:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;Correct&amp;nbsp;the&amp;nbsp;classpath&amp;nbsp;of&amp;nbsp;your&amp;nbsp;application&amp;nbsp;so&amp;nbsp;that&amp;nbsp;it&amp;nbsp;contains&amp;nbsp;compatible&amp;nbsp;versions&amp;nbsp;of&amp;nbsp;the&amp;nbsp;classes&amp;nbsp;org.springframework.plugin.core.support.AbstractTypeAwareSupport$BeansOfTypeTargetSource&amp;nbsp;and&amp;nbsp;org.springframework.util.Assert&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 이상 3.0 버전 이상에서는 Springfox로 설치가 어려워졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 해결방법&lt;/b&gt;&lt;/h3&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 그래들 빌드만 해주면, 알아서 스웨거 UI에서 볼수 있게 변경됐습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존하고 그래들 빌드 한번만 해주면 설정 끝&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 결과&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;http://localhost:8080/swagger-ui/index.html#/&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;1167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Dda72/btsEbCFFv3e/uP806j2qGrCe3vYCfc5gDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Dda72/btsEbCFFv3e/uP806j2qGrCe3vYCfc5gDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Dda72/btsEbCFFv3e/uP806j2qGrCe3vYCfc5gDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDda72%2FbtsEbCFFv3e%2FuP806j2qGrCe3vYCfc5gDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1392&quot; height=&quot;1167&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;1167&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/211</guid>
      <comments>https://shoney.tistory.com/entry/Swagger-Spring-30-%EC%9D%B4%EC%83%81-%EB%B2%84%EC%A0%84%EC%97%90%EC%84%9C-%EC%8A%A4%EC%9B%A8%EA%B1%B0-%EC%A0%81%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95#entry211comment</comments>
      <pubDate>Mon, 29 Jan 2024 20:24:17 +0900</pubDate>
    </item>
    <item>
      <title>[EC2] mysql 설치시 conflicting requests 발생 해결방법</title>
      <link>https://shoney.tistory.com/entry/EC2-mysql-%EC%84%A4%EC%B9%98%EC%8B%9C-conflicting-requests-%EB%B0%9C%EC%83%9D-%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. 문제 상황&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #14171a; text-align: left;&quot;&gt;EC2: Amazon Linux 2023 환경에서 mysql을 설치하려고 하니&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #80cbc4; border: 1px solid #0f111a; padding: 2px 3px 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt;에러문구&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; border: #0F111A 1px solid; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;Error:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&amp;nbsp;Problem: conflicting requests&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;이라는 문구와 함께 아래처럼 에러가 발생했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctkWFb/btsD7m4Lb29/Q7X58FkcWPkQH7O2RGUkKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctkWFb/btsD7m4Lb29/Q7X58FkcWPkQH7O2RGUkKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctkWFb/btsD7m4Lb29/Q7X58FkcWPkQH7O2RGUkKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctkWFb%2FbtsD7m4Lb29%2FQ7X58FkcWPkQH7O2RGUkKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;827&quot; height=&quot;392&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 해결방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지원하지 않는 버전의 설치를 진행했기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 명령어로 다시 설치 진행해보시면 정상 진행됩니다.&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #80cbc4; border: 1px solid #0f111a; padding: 2px 3px 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt; 해결방법&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; border: #0F111A 1px solid; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;pre class=&quot;sql&quot; style=&quot;background-color: #222222; color: #ffffff; text-align: left;&quot;&gt;&lt;code&gt;sudo dnf install https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm
sudo dnf install mysql-community-server&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Back-end</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/210</guid>
      <comments>https://shoney.tistory.com/entry/EC2-mysql-%EC%84%A4%EC%B9%98%EC%8B%9C-conflicting-requests-%EB%B0%9C%EC%83%9D-%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95#entry210comment</comments>
      <pubDate>Mon, 29 Jan 2024 17:29:09 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Access-Control-Allow-Origin  header is present 해결방법</title>
      <link>https://shoney.tistory.com/entry/Spring-Access-Control-Allow-Origin-header-is-present-%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FtWLN/btsD3qe6KSB/gmLwgyxtCH9YFWkWoCAmqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FtWLN/btsD3qe6KSB/gmLwgyxtCH9YFWkWoCAmqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FtWLN/btsD3qe6KSB/gmLwgyxtCH9YFWkWoCAmqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFtWLN%2FbtsD3qe6KSB%2FgmLwgyxtCH9YFWkWoCAmqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;607&quot; height=&quot;184&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;Access-Control-Allow-Origin&quot; 헤더가 브라우저에서 발생하는 보안 정책 중 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 헤더는 웹 페이지가 다른 도메인에서 리소스를 요청할 때 CORS(Cross-Origin Resource Sharing) 정책을 적용하도록 브라우저에 지시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 테스트 서버에서 이 에러가 발생한다면, 보통은 서버 측에서 CORS를 허용하지 않아서 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결하기 위해서는 서버에서 CORS 정책을 설정해야 합니다. Spring Boot에서는 아래와 같은 방법들로 해결할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot에서 CORS를 허용하려면 다음과 같이 설정할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #edf7f6; padding: 10px; margin: 10px 12px 10px 12px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;application.yml 설정&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;@CrossOrigin 애노테이션 사용&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. application 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 모든 요청에 대해 CORS를 허용하려면 Spring Boot의 설정 파일인 application.properties 또는 application.yml에 다음과 같이 추가합니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.properties:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;spring.mvc.cors.allowed-origins=*

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.yml:&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;spring:
  mvc:
    cors:
      allowed-origins: &quot;*&quot;

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 설정은 모든 요청에 대해 모든 Origin을 허용합니다. 실제 환경에서는 보안 상의 이유로 '*'를 사용하는 것보다는 특정 Origin을 명시하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. @CrossOrigin 애노테이션을 사용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 간단한 방법은 @CrossOrigin 애노테이션을 사용하는 것입니다. 이를 이용하면 특정 컨트롤러나 메소드에 대해 CORS 정책을 설정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 특정 컨트롤러나 메소드에 대해 CORS를 허용하려면 다음과 같이 @CrossOrigin 애노테이션을 사용합니다:&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PostController {

    @CrossOrigin(origins = &quot;&amp;lt;http://localhost:3000&amp;gt;&quot;) // 허용할 Origin을 명시
    @GetMapping(&quot;/api/posts&quot;)
    public List&amp;lt;Post&amp;gt; getPosts() {
        // 게시물을 반환하는 로직
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제에서는 /api/posts 엔드포인트에 대해 localhost:3000 Origin에서의 요청을 허용합니다. 이렇게 하면 해당 Origin에서의 요청은 CORS 에러가 발생하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 프로덕션 환경에서는 보안을 고려하여 필요한 Origin만 허용하도록 설정하는 것이 좋습니다.&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/209</guid>
      <comments>https://shoney.tistory.com/entry/Spring-Access-Control-Allow-Origin-header-is-present-%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95#entry209comment</comments>
      <pubDate>Sat, 27 Jan 2024 22:46:41 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 시큐리티 Security Form Login 인증 기본 설정 (스프링 3.0 버전 이상, 시큐리티 6.0 버전 이상, WebSecurityConfigurerAdapter 대체)</title>
      <link>https://shoney.tistory.com/entry/Spring-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-Security-Form-Login-%EC%9D%B8%EC%A6%9D-%EA%B8%B0%EB%B3%B8-%EC%84%A4%EC%A0%95-%EC%8A%A4%ED%94%84%EB%A7%81-3-%EB%B2%84%EC%A0%84-%EC%9D%B4%EC%83%81%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9-WebSecurityConfigurerAdapter-%EC%97%86%EC%9D%8C</link>
      <description>&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;websecurityconfigureradapter 대체되는 방식으로 form login을 사용하는 기본 예제를 알려드립니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스프링 3.0 이상 버전은 스프링 시큐리티 6.0 버전 이상만 사용가능하게 되면서, 기존의 방식으로는 사용불가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스프링 3.0 버전 이상부터는 websecurityconfigureradapter deprecated 됨에 따라서,&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;@Bean을 사용하고 SecurityFilterChain를 리턴하는 함수로 사용해야합니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해당 함수는 아래 예제들에서 자세히 설명드리겠습니다.&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Form Login 인증&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zHxLt/btsC8XTzsof/z9DHZGiIpoZ7CdPO2Av5tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zHxLt/btsC8XTzsof/z9DHZGiIpoZ7CdPO2Av5tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zHxLt/btsC8XTzsof/z9DHZGiIpoZ7CdPO2Av5tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzHxLt%2FbtsC8XTzsof%2Fz9DHZGiIpoZ7CdPO2Av5tk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;685&quot; height=&quot;802&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;802&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래 예제를 통해서 어떻게 동작하는지 살펴보도록 하겠습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-1. formLogin 기본적인 내용 입력하기&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; style=&quot;color: #000000; text-align: start;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;@Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(ar -&amp;gt; ar
                        .anyRequest()
                        .authenticated()
                )
                .formLogin(form -&amp;gt; form
                        .loginPage(&quot;/loginPage&quot;)
                        .defaultSuccessUrl(&quot;/members&quot;)
                        .failureUrl(&quot;/login.html?error=true&quot;)
                        .usernameParameter(&quot;userid&quot;)
                        .passwordParameter(&quot;passwd&quot;)
                        .loginProcessingUrl(&quot;/login_page&quot;)
                        .permitAll()
                )
                .build();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- loginPage : 로그인 화면의 url을 설정합니다. (시작 화면으로 지정됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;defaultSuccessUrl : 로그인이 성공할 경우, 이동하게 되는 디폴트 url을 지정할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;failureUrl : 실패시 이동하는 url 설정&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;usernameParameter : 웹의 username의 파라이름을 설정 (아래 캡처 참조)&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;passwordParameter : 웹의 password의 파라이름을 설정 (아래 캡처 참조)&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;loginProcessingUrl : 웹 로그인창의 action 설정 (아래 캡처 참조)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dha9Kn/btsDawnCJAP/fq8biJXkIeq9hfKruzWwm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dha9Kn/btsDawnCJAP/fq8biJXkIeq9hfKruzWwm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dha9Kn/btsDawnCJAP/fq8biJXkIeq9hfKruzWwm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdha9Kn%2FbtsDawnCJAP%2Ffq8biJXkIeq9hfKruzWwm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;229&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-2. 핸들러 사용하는 방법&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;성공 또는 실패할 경우 핸들러를 사용해서 원하는 것들을 실행할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래 예제를 통해서 확인해봅시다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; style=&quot;color: #000000; text-align: start;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
            .authorizeHttpRequests(ar -&amp;gt; ar
                    .anyRequest()
                    .authenticated()
            )
            .formLogin(form -&amp;gt; form
                    .loginPage(&quot;/loginPage&quot;)
                    .defaultSuccessUrl(&quot;/members&quot;)
                    .failureUrl(&quot;/login.html?error=true&quot;)
                    .usernameParameter(&quot;userid&quot;)
                    .passwordParameter(&quot;passwd&quot;)
                    .loginProcessingUrl(&quot;/login_page&quot;)
                    .successHandler(new AuthenticationSuccessHandler() {
                        @Override
                        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                            System.out.println(&quot;authentication :&quot; + authentication.getName());
                            response.sendRedirect(&quot;/&quot;);
                        }
                    })
                    .failureHandler(new AuthenticationFailureHandler() {
                        @Override
                        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                            System.out.println(&quot;exception :&quot; + exception.getMessage());
                            response.sendRedirect(&quot;/login&quot;);
                        }
                    })
                    .permitAll()
            )
            .build();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1-1 에서 사용했던 내용과 핸들러가 같이 사용되는 경우는 핸들러의 소스코드를 우선순위가 높게 실행됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;successHandler : AuthenticationSuccessHandler로 익명 메서드를 만들어서 사용했습니다. response.sendRedirect를 통해서 성공 시 이동하는 url를 입력할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;failureHandler : AuthenticationFailureHandler로 익명 메서드를 만들어서 사용했습니다.response.sendRedirect를 통해서 성공 시 이동하는 url를 입력할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핸들러 실행결과 로그&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;successHandler&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uVFEV/btsC8VOXRwi/NWqugzVvVvjkIvfowh5gNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uVFEV/btsC8VOXRwi/NWqugzVvVvjkIvfowh5gNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uVFEV/btsC8VOXRwi/NWqugzVvVvjkIvfowh5gNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuVFEV%2FbtsC8VOXRwi%2FNWqugzVvVvjkIvfowh5gNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;488&quot; height=&quot;77&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;failureHandler&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 5.png&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v4qv7/btsDiu2RMej/ALfwKlZK8DKQkuOZbQwVVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v4qv7/btsDiu2RMej/ALfwKlZK8DKQkuOZbQwVVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v4qv7/btsDiu2RMej/ALfwKlZK8DKQkuOZbQwVVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv4qv7%2FbtsDiu2RMej%2FALfwKlZK8DKQkuOZbQwVVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;488&quot; height=&quot;77&quot; data-filename=&quot;Untitled 5.png&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/208</guid>
      <comments>https://shoney.tistory.com/entry/Spring-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-Security-Form-Login-%EC%9D%B8%EC%A6%9D-%EA%B8%B0%EB%B3%B8-%EC%84%A4%EC%A0%95-%EC%8A%A4%ED%94%84%EB%A7%81-3-%EB%B2%84%EC%A0%84-%EC%9D%B4%EC%83%81%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9-WebSecurityConfigurerAdapter-%EC%97%86%EC%9D%8C#entry208comment</comments>
      <pubDate>Wed, 10 Jan 2024 13:56:17 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 시큐리티 Security 의존성 추가 방법</title>
      <link>https://shoney.tistory.com/entry/Spring-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-Security-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%B6%94%EA%B0%80-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 의존성 추가&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-1. build.gradle에 의존성 추가&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;clean&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;dependencies{
implementation 'org.springframework.boot:spring-boot-starter-security'
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;가장 먼저 해야할 것은 의존성을 추가하는 것입니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;의존성을 추가하고, 스프링을 실행하게 되면 아래와 같이 패스워드를 제공해주는 로그를 확인 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmk3V9/btsC8VnWvLb/4JBQrn8QoNcQzeYPONr9Q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmk3V9/btsC8VnWvLb/4JBQrn8QoNcQzeYPONr9Q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmk3V9/btsC8VnWvLb/4JBQrn8QoNcQzeYPONr9Q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbmk3V9%2FbtsC8VnWvLb%2F4JBQrn8QoNcQzeYPONr9Q0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;546&quot; height=&quot;111&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;124&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-2. 정상적으로 의존 됐는지 확인하는 방법&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 크롬으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;http://localhost:8080/&quot;&gt;localhost:8080/&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;을 접속해보면 아래와 같이 로그인 창으로 바로 접속되는 것을 확인 할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;id는 user, password는 위에서 공유된 내용을 입력하면 본인이 만든 api를 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;987&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cs3m7j/btsDhTogIKH/yMbvGLs6lKERYtD4KpSDK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cs3m7j/btsDhTogIKH/yMbvGLs6lKERYtD4KpSDK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cs3m7j/btsDhTogIKH/yMbvGLs6lKERYtD4KpSDK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcs3m7j%2FbtsDhTogIKH%2FyMbvGLs6lKERYtD4KpSDK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;541&quot; height=&quot;987&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;987&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 사용자 정의 보안 기능 구현&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2-1. appilcation.yml 에서 name, password 지정하는 방법&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;spring:
  security:
    user:
      name: user
      password: 1111
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 설정하면 Id, password를 지정한 아이디와 패스워드로 사용할 수 있게 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2-2. 기본 Config 작성&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SecurityConfig.java를 아래와 같이 작성해서 기본적인 Config 설정을 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .anyRequest().authenticated();
        http
                .formLogin();
        return http.build();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/207</guid>
      <comments>https://shoney.tistory.com/entry/Spring-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-Security-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%B6%94%EA%B0%80-%EB%B0%A9%EB%B2%95#entry207comment</comments>
      <pubDate>Wed, 10 Jan 2024 11:51:57 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] mybatis에서 카멜케이스로 자동 변환 옵션(application.yml)</title>
      <link>https://shoney.tistory.com/entry/Spring-mybatis%EC%97%90%EC%84%9C-%EC%B9%B4%EB%A9%9C%EC%BC%80%EC%9D%B4%EC%8A%A4%EB%A1%9C-%EC%9E%90%EB%8F%99-%EB%B3%80%ED%99%98-%EC%98%B5%EC%85%98applicationyml</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1 사용해야하는 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 디비에서는 스네이크 표기법 credit_limit&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바에서는 카멜케이스 표기법 creditLimit&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 자주 사용하게 되는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mybatis에서 아무런 설정을 하지 않으면, 아래 처럼 select 문에 alias를 사용해서 직접 매핑해줘야한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1704690386492&quot; class=&quot;cs&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;select
credit_limit as 'creaditLimit'
from customer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 몇개 변수라면 상관없지만 무수히 많은 변수가 존재한다면 정말 귀찮은 일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2 . 설정 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.yml에서 옵션으로 쉽게 변경가능하다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;&lt;code&gt;mybatis:
  configuration:
    map-underscore-to-camel-case: true #db는 스네이트 표기법, dto는 카멜케이스 사용 시&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 블로그에서는 뭐 이런저런 설정을 하던데, 사실 이렇게 쉬운 방법이 존재한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 이렇게만 사용해도 dto의 creditLimit 변수에 값이 들어오는 것을 확인 할 수있다.&lt;/p&gt;
&lt;pre id=&quot;code_1704690562892&quot; class=&quot;sql&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select
credit_limit
from customer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 결과&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 dto는 카멜케이스이고, 쿼리는 *로 전체 변수를 가저온다고 할 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Customer DTO&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;419&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kVfoO/btsC9lyHu9q/QwJ6qj7kBv64KyKOwTgf8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kVfoO/btsC9lyHu9q/QwJ6qj7kBv64KyKOwTgf8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kVfoO/btsC9lyHu9q/QwJ6qj7kBv64KyKOwTgf8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkVfoO%2FbtsC9lyHu9q%2FQwJ6qj7kBv64KyKOwTgf8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;459&quot; height=&quot;419&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;419&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마이바티스 쿼리문&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;87&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfzZW1/btsDdCUfkB7/czhceLzd6Didxv6gXqZJf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfzZW1/btsDdCUfkB7/czhceLzd6Didxv6gXqZJf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfzZW1/btsDdCUfkB7/czhceLzd6Didxv6gXqZJf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfzZW1%2FbtsDdCUfkB7%2FczhceLzd6Didxv6gXqZJf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;674&quot; height=&quot;87&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;87&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 출력됨을 확인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;987&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYe6WN/btsDdEdqDtd/I01vtU03Jup4KgBocUJXh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYe6WN/btsDdEdqDtd/I01vtU03Jup4KgBocUJXh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYe6WN/btsDdEdqDtd/I01vtU03Jup4KgBocUJXh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYe6WN%2FbtsDdEdqDtd%2FI01vtU03Jup4KgBocUJXh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;832&quot; height=&quot;987&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;987&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/206</guid>
      <comments>https://shoney.tistory.com/entry/Spring-mybatis%EC%97%90%EC%84%9C-%EC%B9%B4%EB%A9%9C%EC%BC%80%EC%9D%B4%EC%8A%A4%EB%A1%9C-%EC%9E%90%EB%8F%99-%EB%B3%80%ED%99%98-%EC%98%B5%EC%85%98applicationyml#entry206comment</comments>
      <pubDate>Mon, 8 Jan 2024 14:13:39 +0900</pubDate>
    </item>
    <item>
      <title>[mySql] 공식 샘플 데이터 (Example Database)</title>
      <link>https://shoney.tistory.com/entry/mySql-%EA%B3%B5%EC%8B%9D-%EC%83%98%ED%94%8C-%EB%8D%B0%EC%9D%B4%ED%84%B0-Example-Database</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. 필요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 사이드 프로젝트를 진행하다가 보면, 데이터들이 많이 필요합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 주로 mySql로 사이드 프로젝트를 진행하는데요.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드 사이드 프로젝트에서 데이터를 만드는 일이 쉬운일이 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔터티간의 관계도 생각해야하고, 테이블을 생성하게 되면 데이터도 다 넣어야하고...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럴 때는 mySql 공식 홈페이지에서 제공하는 Sample 데이터를 사용하면 매우 용이합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.&amp;nbsp;mySql 샘플 데이터 링크&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dev.mysql.com/doc/index-other.html&quot;&gt;https://dev.mysql.com/doc/index-other.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1704685638291&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;MySQL :: Other MySQL Documentation&quot; data-og-description=&quot;Other MySQL Documentation This page provides additional documentation. There's even more available on these extra pages: MySQL Server Doxygen Documentation Title HTML Online MySQL Server (latest version) View Expert Guides Language Title Version HTML Onlin&quot; data-og-host=&quot;dev.mysql.com&quot; data-og-source-url=&quot;https://dev.mysql.com/doc/index-other.html&quot; data-og-url=&quot;https://dev.mysql.com/doc/index-other.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://dev.mysql.com/doc/index-other.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dev.mysql.com/doc/index-other.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;MySQL :: Other MySQL Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Other MySQL Documentation This page provides additional documentation. There's even more available on these extra pages: MySQL Server Doxygen Documentation Title HTML Online MySQL Server (latest version) View Expert Guides Language Title Version HTML Onlin&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dev.mysql.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 링크로 들어가시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 사용 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래에 보면 데이터 베이스를 선택할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FcoF7/btsC6yMdh2P/krBe9UkvS1XoAL0mFEjLRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FcoF7/btsC6yMdh2P/krBe9UkvS1XoAL0mFEjLRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FcoF7/btsC6yMdh2P/krBe9UkvS1XoAL0mFEjLRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFcoF7%2FbtsC6yMdh2P%2FkrBe9UkvS1XoAL0mFEjLRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;822&quot; height=&quot;229&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본인이 구현할 프로젝트와 가장 유사한 데이터베이스를 zip으로 다운 받으시면, sql문이 들어가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>SQL</category>
      <category>example data</category>
      <category>MySQL</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/205</guid>
      <comments>https://shoney.tistory.com/entry/mySql-%EA%B3%B5%EC%8B%9D-%EC%83%98%ED%94%8C-%EB%8D%B0%EC%9D%B4%ED%84%B0-Example-Database#entry205comment</comments>
      <pubDate>Mon, 8 Jan 2024 13:58:13 +0900</pubDate>
    </item>
    <item>
      <title>[Error] No serializer found for class 해결방법</title>
      <link>https://shoney.tistory.com/entry/error-No-serializer-found-for-class-%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;0. 현상&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;co&lt;a href=&quot;http://m.fasterxml.jackson.databind.exc.InvalidDefinitionException:&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;http://m.fasterxml.jackson.databind.exc.InvalidDefinitionException:&lt;/a&gt;&amp;nbsp;No&amp;nbsp;serializer&amp;nbsp;found&amp;nbsp;for&amp;nbsp;class&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 캡쳐로 내가 responsebody로 리턴할 클래스가 없다는 표현이 나오는 에러가 발생했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2640&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRxg1C/btsC50ay0Hg/cz8kvTVKSBEb4CK1cXrep0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRxg1C/btsC50ay0Hg/cz8kvTVKSBEb4CK1cXrep0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRxg1C/btsC50ay0Hg/cz8kvTVKSBEb4CK1cXrep0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRxg1C%2FbtsC50ay0Hg%2Fcz8kvTVKSBEb4CK1cXrep0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2640&quot; height=&quot;768&quot; data-origin-width=&quot;2640&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 원인&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Controller에서 @ResponseBody를 사용해서 json으로 리턴하는 방식으로 구현했는데, @ResponseBody에서 객체를 json으로 변환하려면 객체의 클래스에 Getter가 존재해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Setter만 필요해 보였던 클래스라서 Getter를 따로 입력하지 않았는데, 그게 문제가 된 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 해결방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-1. @Getter 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 일반적인 방법은 Getter를 추가하면 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- @ResponsBody에서 객체를 Json으로 변환 할 때, Getter를 사용해서 가져오는게 일반적입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1264&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rYkhL/btsC8VTPLy9/VZaDIYjf7hHIyPTtvnW1ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rYkhL/btsC8VTPLy9/VZaDIYjf7hHIyPTtvnW1ok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rYkhL/btsC8VTPLy9/VZaDIYjf7hHIyPTtvnW1ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrYkhL%2FbtsC8VTPLy9%2FVZaDIYjf7hHIyPTtvnW1ok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;541&quot; height=&quot;313&quot; data-origin-width=&quot;1264&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-2. @JsonAutoDetect 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Getter를 사용하기 싫을 때는 JsonAutoDetect를 사용해서 사용하면됩니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;&lt;code&gt;@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LkECp/btsC4duCzvT/nqeJVMsZFUMgjzaZjyJ231/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LkECp/btsC4duCzvT/nqeJVMsZFUMgjzaZjyJ231/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LkECp/btsC4duCzvT/nqeJVMsZFUMgjzaZjyJ231/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLkECp%2FbtsC4duCzvT%2FnqeJVMsZFUMgjzaZjyJ231%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;526&quot; height=&quot;329&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본인 프로젝트 상황에 맞게 @Getter 또는 @JsonAutoDetect를 사용해서 해결하시면 될것 같습니다.&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <category>스프링 #자바 #에러해결방법</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/204</guid>
      <comments>https://shoney.tistory.com/entry/error-No-serializer-found-for-class-%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95#entry204comment</comments>
      <pubDate>Fri, 5 Jan 2024 16:23:36 +0900</pubDate>
    </item>
    <item>
      <title>[IntelliJ] Database에서 DTO/VO 자동 생성 방법</title>
      <link>https://shoney.tistory.com/entry/IntelliJ-Database%EC%97%90%EC%84%9C-DTOVO-%EC%9E%90%EB%8F%99-%EC%83%9D%EC%84%B1-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;여러가지 플러그인이 있겠지만, 알고보니 인테리제이에서 기본으로 제공해주는 generater가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;0. 나의 피시 환경 상태&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Mysql 설치완료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 인텔리제이에서 db 연결 완료된 상태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 인텔리제이에서 Database가 연결이 되어있는 상태여야한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 DB를 연결하지 않았더라면, 인텔리제이 화면 우측 상단에서 데이터 베이스를 연결하면된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 캡쳐 사진에서 + 버튼을 눌러서 연결하도록 하자 (기본적으로 본인이 사용하는 sql은 설치가 되어있어야 한다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;445&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHUg0U/btsCVGxFYZ5/2n4Kf9Gk9mwhsWZPBEkw0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHUg0U/btsCVGxFYZ5/2n4Kf9Gk9mwhsWZPBEkw0K/img.png&quot; data-alt=&quot;저는 이미 데이터 베이스가 연결되어있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHUg0U/btsCVGxFYZ5/2n4Kf9Gk9mwhsWZPBEkw0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHUg0U%2FbtsCVGxFYZ5%2F2n4Kf9Gk9mwhsWZPBEkw0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;392&quot; height=&quot;445&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;445&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;저는 이미 데이터 베이스가 연결되어있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. DB -&amp;gt; DTO 파일 뽑아내기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-1. 원하는 테이블의 우측을 클릭해서, Tools -&amp;gt; Scripted Extesions -&amp;gt; Generate POJOs.groovy 클릭&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;552&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tKICx/btsC3Z3N4TX/oq0Nl44j99KHlOHVqcmSgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tKICx/btsC3Z3N4TX/oq0Nl44j99KHlOHVqcmSgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tKICx/btsC3Z3N4TX/oq0Nl44j99KHlOHVqcmSgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtKICx%2FbtsC3Z3N4TX%2Foq0Nl44j99KHlOHVqcmSgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;552&quot; height=&quot;572&quot; data-origin-width=&quot;552&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 저장할 위치를 지정하라는 창이 뜨면, 자신이 원하는 위치를 선택하면 됩니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-2. 위에서 지정한 위치에 Table 명으로 DTO 클래스가 생성 된 것을 확인 할 수 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- dto내의 변수만 존재하는 것이 아니라, 아래에 getter/setter까지 존재하는 것을 확인 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;848&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpeNxq/btsCVG5slFq/zfs3EYVPgck7FNdR1s21P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpeNxq/btsCVG5slFq/zfs3EYVPgck7FNdR1s21P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpeNxq/btsCVG5slFq/zfs3EYVPgck7FNdR1s21P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpeNxq%2FbtsCVG5slFq%2Fzfs3EYVPgck7FNdR1s21P1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;717&quot; height=&quot;848&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;848&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-3. 위에서 생성 java파일을 원하는 패키지 위치에 옮김과 동시에, Getter/Setter를 지우고 @Data로 변경하면 DTO로 변경 완료&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqR5Ra/btsCZgMkmQw/iY7GvUHEgpLNrKtTdeFaO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqR5Ra/btsCZgMkmQw/iY7GvUHEgpLNrKtTdeFaO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqR5Ra/btsCZgMkmQw/iY7GvUHEgpLNrKtTdeFaO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqR5Ra%2FbtsCZgMkmQw%2FiY7GvUHEgpLNrKtTdeFaO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;630&quot; height=&quot;472&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 지금까지, 외부 플러그인들을 설치해서 사용했었는데... 인텔리제이에서 기본적으로 제공하는 제너레이터가 있었군요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 사용하면 많은 시간을 단축할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <category>인텔리제이 #DTO자동생성</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/203</guid>
      <comments>https://shoney.tistory.com/entry/IntelliJ-Database%EC%97%90%EC%84%9C-DTOVO-%EC%9E%90%EB%8F%99-%EC%83%9D%EC%84%B1-%EB%B0%A9%EB%B2%95#entry203comment</comments>
      <pubDate>Thu, 4 Jan 2024 23:32:25 +0900</pubDate>
    </item>
    <item>
      <title>[Error] Could not find mysql:mysql-connector-java:.</title>
      <link>https://shoney.tistory.com/entry/Error-Could-not-find-mysqlmysql-connector-java</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Could&amp;nbsp;not&amp;nbsp;find&amp;nbsp;mysql:mysql-connector-java:.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 발생 시, 해결방법을 공유드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;0. 현상&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 에러가 발생합니다. gradle에서 dependencies를 잘못 입력했다는 것을 직감할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2038&quot; data-origin-height=&quot;618&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dr9RIl/btsC0aSmlrB/YrQM974pKGTJgOVb0ZgXBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dr9RIl/btsC0aSmlrB/YrQM974pKGTJgOVb0ZgXBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dr9RIl/btsC0aSmlrB/YrQM974pKGTJgOVb0ZgXBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdr9RIl%2FbtsC0aSmlrB%2FYrQM974pKGTJgOVb0ZgXBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2038&quot; height=&quot;618&quot; data-origin-width=&quot;2038&quot; data-origin-height=&quot;618&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 해결방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mysql 버전에 따라서 implementation을 다르게 입력해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MySQL 8.0.31 이전 버전&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;clean&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;&lt;code&gt;implementation 'mysql:mysql-connector-java'&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MySQL 8.0.31 이후 버전&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;clean&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;&lt;code&gt;implementation 'com.mysql:mysql-connector-j'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신의 버전에 맞게 mysql connector를 입력하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/202</guid>
      <comments>https://shoney.tistory.com/entry/Error-Could-not-find-mysqlmysql-connector-java#entry202comment</comments>
      <pubDate>Thu, 4 Jan 2024 19:23:08 +0900</pubDate>
    </item>
    <item>
      <title>[Error] Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.</title>
      <link>https://shoney.tistory.com/entry/Error-Failed-to-configure-a-DataSource-url-attribute-is-not-specified-and-no-embedded-datasource-could-be-configured</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. 문제 현상&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2348&quot; data-origin-height=&quot;868&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w9MQd/btsC1FxSgXr/Ph6AE1eVEZUkQsFLn1uLw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w9MQd/btsC1FxSgXr/Ph6AE1eVEZUkQsFLn1uLw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w9MQd/btsC1FxSgXr/Ph6AE1eVEZUkQsFLn1uLw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw9MQd%2FbtsC1FxSgXr%2FPh6AE1eVEZUkQsFLn1uLw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2348&quot; height=&quot;868&quot; data-origin-width=&quot;2348&quot; data-origin-height=&quot;868&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 에러가 발생한 경우, 해결하는 과정을 작성해봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 이 문제에 직면한 분들은 모두 새롭게 스프링을 설정하는 상황이라서 발생할 겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.yml 에 datasource 정보를 입력하지 않아서 발생하는 문제입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 해결 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.yml 파일로 들어갑니다. (위치는 resource에 존재합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 datasource를 입력합니다. (본인의 db정보에 맡게 입력하세요)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.yml&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;less&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;&lt;code&gt;spring:
  datasource:
    url: jdbc:mysql://localhost:3306
    username: root
    password:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어라? 그렇게 해도 에러가 발생하네요? (아래 에러가 발생하지 않으면, 다음 단계를 진행하실 필요없습니다.)&lt;/p&gt;
&lt;p&gt;&lt;img style=&quot;text-align: center; caret-color: transparent; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot; src=&quot;https://blog.kakaocdn.net/dn/bso1Re/btsC6U8jnAc/wQvn0aiPLKP6HE9WAIjyt1/img.png&quot; data-origin-width=&quot;2650&quot; data-origin-height=&quot;618&quot; data-is-animation=&quot;false&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 부터는 추가 에러 발생한 분들만 보시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.&amp;nbsp; Error creating bean with name 'dataSourceScriptDatabaseInitializer'&amp;nbsp; 에러 해결방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;스프링 버전 2.5부터 SQL Scrpit DataSource Initialization 기능 변경으로,&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;아래와 같이 defer-datasoure-initialization: true 로 변경하면 해당 에러도 발생하지 않게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;&lt;code&gt;spring:
  datasource:
    url: jdbc:mysql://localhost:3306
    username: root
    password:
  jpa:
    defer-datasource-initialization: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스트레스 받지 않는 행복한 코딩하시길 바랍니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/201</guid>
      <comments>https://shoney.tistory.com/entry/Error-Failed-to-configure-a-DataSource-url-attribute-is-not-specified-and-no-embedded-datasource-could-be-configured#entry201comment</comments>
      <pubDate>Thu, 4 Jan 2024 17:17:09 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 멀티 MQ 리스너 개발 내용</title>
      <link>https://shoney.tistory.com/entry/Spring-%EB%A9%80%ED%8B%B0-MQ-%EB%A6%AC%EC%8A%A4%EB%84%88-%EA%B0%9C%EB%B0%9C-%EB%82%B4%EC%9A%A9</link>
      <description>&lt;h1&gt;멀티 MQ 리스너 개발 내용&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MQ 서버 추가 및 큐 이름 추가 되는 경우 아래 개발 내용 참조해서 추가하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 MQ 서버 연결에서 다중 MQ 서버로 변경하는 내용을 정리했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. MQ application.yml&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;profile에 맞는 설정을 해야 합니다. 설명을 위해 default profile로 설명 드립니다. (dev, prd는 서버 주소에 맞게 모두 설정해야합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-1. MQ 서버 주소를 yml 설정&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;ibm:
  mq:
    serverA:
      hostName: mqa.server.com
      port: 1961
      queueManager: ABT.PSS
      channel: CLIENT.TO.QNAME
      userName: mpns
      password:
      appName:  QNAME
      retryMaxInterval: 300000
      sleepTime: 2000
    serverB:
      hostName: mqb.server.com
      port: 1962
      queueManager: A1T.PSS
      channel: CLIENT.TO.QNAME
      userName: mpns
      password:
      appName:  QNAME
      retryMaxInterval: 300000
      sleepTime: 2000&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;serverA와 serverB의 구분이 필요하기 때문에 serverA, serverB 레이어 추가&lt;/li&gt;
&lt;li&gt;MQ 서버안에 여러 큐가 존재합니다. 큐 이름 설정은 Listener에서 설정하면 됩니다. (&lt;a href=&quot;http://3.MQ&quot;&gt;3.MQ&lt;/a&gt; JmsListener 참조)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-2. Props&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;props는 yml에서 설정한 내용들을 객체로 사용하기 위해서 만들어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불필요하면 사용하지 않아도 되지만, Map과 객체를 사용해서 휴먼 에러를 줄일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(props를 사용하지 않을 경우에는 @Value로 yml 설정 내용 사용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-2-1. MqPropsMap&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@Component
@Data
@ConfigurationProperties(prefix = &quot;ibm&quot;)
public class MqPropsMap {
    private Map&amp;lt;String, MqProps&amp;gt; mq;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@ConfigurationProperties 를 사용해서 MqProps를 Map으로 관리합니다.&lt;/li&gt;
&lt;li&gt;단일 서버일 경우에는 MqProps를 바로 만들어도 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-2-2. MqProps&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;@Data
public class MqProps {
   private String hostName;
    private int port;
    private String queueManager;
    private String channel;
    private String userName;
    private String password;
    private String appName;
    private long retryMaxInterval;
    private long sleepTime;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MqProps는 MQQueueConnectionFactory 에서 설정으로 사용할 대상들을 변수 관리합니다.&lt;/li&gt;
&lt;li&gt;단일 서버일 경우에는 MqProps클래스에 @ConfigurationProperties 사용해야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. MQ Config&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 두가지 팩토리 클래스로 MQ 서버 연결을 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MQQueueConnectionFactory : MQ 서버 주소 및 옵션 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JmsListenerContainerFactory : ConnectionFactory 연결&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2-1.&lt;/b&gt; MQQueueConnectionFactory&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Bean
public MQQueueConnectionFactory serverAMqQueueConnectionFactory() {
    MQQueueConnectionFactory connectionFactory = createConnectFactory(MqKeys.SERVER_A);
    return connectionFactory;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MQ Config 설정을 위해 위 내용을 Bean에 등록합니다.&lt;/li&gt;
&lt;li&gt;createConnectFactory은 private 메서드로 아래 내용에서 추가 설명하도록 하겠습니다.&lt;/li&gt;
&lt;li&gt;휴먼 에러 및 관리를 위해 MqKeys는 Enum으로 관리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2-2.&lt;/b&gt; MQQueueConnectionFactory &lt;b&gt;내부의&lt;/b&gt; createConnectFactory &lt;b&gt;private 메서드&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;private MQQueueConnectionFactory createConnectFactory(MqKeys keys) {
    MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
    if (mqPropsMap.getMq().containsKey(keys.getKeyName())) {
        MqProps mqProps = mqPropsMap.getMq().get(keys.getKeyName());
        mqQueueConnectionFactory.setHostName(mqProps.getHostName());
        try {
            mqQueueConnectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
            mqQueueConnectionFactory.setChannel(mqProps.getChannel());
            mqQueueConnectionFactory.setPort(mqProps.getPort());
            mqQueueConnectionFactory.setQueueManager(mqProps.getQueueManager());
            mqQueueConnectionFactory.setAppName(mqProps.getAppName());
            mqQueueConnectionFactory.setClientReconnectOptions(WMQConstants.WMQ_CLIENT_RECONNECT_Q_MGR);
            mqQueueConnectionFactory.setClientReconnectTimeout(43200);        // 12 hours
        } catch (JMSException e) {
log.error(&quot;[MQQueueConnectionFactory JMSException - {}] : {}&quot;, keys.getKeyName(), e.getMessage());
        }
    }
    return mqQueueConnectionFactory;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1-2에서 만든 MqPropsMap를 사용합니다. Map에서 Props를 꺼내고, Props 내의 설정 정보를 꺼내와서 MQQueueConnectionFactory에 설정을 해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2-3.&lt;/b&gt; JmsListenerContainerFactory&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;@Bean
public JmsListenerContainerFactory&amp;lt;?&amp;gt; jmsServerAListenerContainerFactory(@Qualifier(&quot;serverAMqQueueConnectionFactory&quot;) MQConnectionFactory connectionFactory) {
    DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
    factory.setConnectionFactory(connectionFactory);
    return factory;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@Qualifier로 빈 객체명을 정확하게 작성해야지 원하는 서버와 연결이 가능합니다. (MQ 서버가 단일 서버일 경우에는 사용하지 않아도 됩니다.)&lt;/li&gt;
&lt;li&gt;setConnectionFactory를 사용해서 JmsListenerContainerFactory 와 MQQueueConnectionFactory간의 연결을 해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. MQ JmsListener&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다대일로 구현하기 위해서는 @JmsListener 어노테이션을 사용해서 어떤 서버의 어떤 큐로 부터 메세지를 받을 건지 지정할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@JmsListener(destination = &quot;SERVER_A_MQNAME_1&quot;, containerFactory = &quot;jmsServerAListenerContainerFactory&quot;)
public void onMessageA1(Message message) {
	// 로직 구현 
}

@JmsListener(destination = &quot;SERVER_A_MQNAME_2&quot;, containerFactory = &quot;jmsServerAListenerContainerFactory&quot;)
public void onMessageA2(Message message) {
	// 로직 구현 
}

@JmsListener(destination = &quot;SERVER_B_MQNAME_1&quot;, containerFactory = &quot;jmsServerBListenerContainerFactory&quot;)
public void onMessageB1(Message message) {
	// 로직 구현 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 처럼 사용하게 되면, Controller의 메서드와 비슷한 포맷으로 사용할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;destination : 큐 이름을 작성하면, 해당 큐에서 메세지를 가져옵니다. (큐 이름 기준)&lt;/li&gt;
&lt;li&gt;containerFactory : ContainerFactory 빈 객체 명을 작성 (MQ 서버 ContainerFactory 기준)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 하나의 MQ 서버에 여러 이름의 큐가 들어가 있을 수 있다.&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/200</guid>
      <comments>https://shoney.tistory.com/entry/Spring-%EB%A9%80%ED%8B%B0-MQ-%EB%A6%AC%EC%8A%A4%EB%84%88-%EA%B0%9C%EB%B0%9C-%EB%82%B4%EC%9A%A9#entry200comment</comments>
      <pubDate>Wed, 20 Dec 2023 09:30:49 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 페이지 처리 Request, Response Dto 변수 항목</title>
      <link>https://shoney.tistory.com/entry/Spring-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%B2%98%EB%A6%AC-Request-Response-Dto-%EB%B3%80%EC%88%98-%ED%95%AD%EB%AA%A9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Controller 소스코드로 설명을 진행하려고 합니다. 아래 소스코드를 참조해주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #80cbc4; border: 1px solid #0f111a; padding: 2px 3px 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt;소스 코드&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; border: #0F111A 1px solid; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;GetMapping&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;/announcement/page&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Map&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Object&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getAnnouncePage&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequestParam&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;reqPageNo&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequestParam&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;reqPageRowCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;List&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;AnnouncementDto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;announcementDtos&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;announcementMapper&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getAnnouncementListWithSkill&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Map&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Object&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;responseMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;HashMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;pageNo&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Integer&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;parseInt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;reqPageNo&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;pageRowCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Integer&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;parseInt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;reqPageRowCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;totalCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;announcementDtos&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;size&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;totalPageCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = (&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Math&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;ceil&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;double&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;totalCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;pageRowCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = (&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;pageNo&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; - &lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;) * &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;pageRowCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;end&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Math&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;min&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; + &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;pageRowCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;totalCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;responseMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;list&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;announcementDtos&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;subList&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;end&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)); &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 보여질 항목&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;responseMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;totalCnt&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;totalCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;); &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 전체 item 개수&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;responseMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;totalPageCnt&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;totalPageCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;); &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 전체 페이지 개수&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;responseMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;pageNo&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;pageNo&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;); &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 현재 페이지&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;responseMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;pageRowCnt&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;pageRowCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;); &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 페이지에 보여질 item row 개수&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;responseMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Request 항목&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 항목에서는 현재 페이지 번호(=pageNo), 페이지에 보여질 항목의 갯수(=pageRowCnt)를 요청하면된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 소스코드에서는 쿼리파라로 요청하는 방식으로 구현하기 위해, RequestParam으로 사용했다.&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #ffffff; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px; border: 1px solid #0f111a;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #80cbc4; border: 1px solid #0f111a; padding: 2px 3px 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt;요청 url&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #ffffff; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;/announcement/page?reqPageNo=2&amp;amp;reqPageRowCnt=3&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Response 항목&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ORM에서 해당 항목만 가져오도록 start, end 인덱스를 활용해 가져오는 것이 메모리 및 성능 측면에서 더 좋겠지만, 지금 게시글은 페이지 처리 하는 방법에 대해서만 설명하기 위해서 이렇게 Controller에서 모든 것을 표현했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #80cbc4; border: 1px solid #0f111a; padding: 2px 3px 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt;소스 코드&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; border: #0F111A 1px solid; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;totalCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;=&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;announcementDtos&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;size&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;();&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;font-family: 'Nanum Gothic'; background-color: #0f111a; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;totalPageCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;= (&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Math&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;ceil&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;double&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;totalCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;pageRowCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: 'Nanum Gothic'; background-color: #0f111a; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;= (&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;pageNo&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;) *&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;pageRowCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: 'Nanum Gothic'; background-color: #0f111a; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;end&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;=&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Math&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;min&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;+&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;pageRowCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;totalCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;font-family: 'Nanum Gothic'; background-color: #0f111a; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;responseMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;list&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;announcementDtos&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;subList&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;end&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)); &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 보여질 항목&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: 'Nanum Gothic'; background-color: #0f111a; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;responseMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;totalCnt&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;totalCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;); &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 전체 item 개수&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: 'Nanum Gothic'; background-color: #0f111a; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;responseMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;totalPageCnt&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;totalPageCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;); &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 전체 페이지 개수&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: 'Nanum Gothic'; background-color: #0f111a; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;responseMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;pageNo&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;pageNo&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;); &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 현재 페이지&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-family: 'Nanum Gothic'; background-color: #0f111a; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;responseMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;pageRowCnt&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;pageRowCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;); &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 페이지에 보여질 item row 개수&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) &lt;b&gt;보여질 Content&lt;/b&gt; : 위 자바버전이 11이전이라서 subList를 통해서 List 항목을 가져왔다. 11이후 버전부터는 List.copyOfRange() 도 사용가능하다. start, end 인덱스를 계산해서 사용하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) &lt;b&gt;전체 item 개수&lt;/b&gt; : 프론트에서 전체 카운트 개수를 보여주기 위해 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) &lt;b&gt;현재 페이지&lt;/b&gt; : 요청에서 보내준 값을 그대로 보여주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) &lt;b&gt;페이지에 보여질 item row 개수&lt;/b&gt; : 요청에서 보내준 값을 그대로 보여주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프론트도 리액트로 간단하게 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 리액트로 간단하게 페이지 처리 구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스코드는 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #80cbc4; border: 1px solid #0f111a; padding: 2px 3px 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt;소스 코드&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; border: #0F111A 1px solid; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;axios&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;axios&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;useState&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;react&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;App&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;() {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;totalPageCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;setTotalPageCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;] = &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;useState&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;reqPageNo&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;setReqPageNo&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;] = &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;useState&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;list&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;setList&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;] = &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;useState&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;([]);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getData&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = () &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;axios&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;`http://localhost:8080/api/v1/announcement/page?reqPageNo=&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;reqPageNo&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&amp;amp;reqPageRowCnt=4`&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;setTotalPageCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;totalPageCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;setList&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;list&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; });&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; };&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;totalPageCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;clickPage&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = (&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;page&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;setReqPageNo&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;page&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; };&lt;/span&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getData&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; }, [&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;reqPageNo&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;]);&lt;/span&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; (&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;hello&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;list&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;item&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; (&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;item&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; : &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;item&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ))&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Array&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;totalPageCnt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;fill&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;_&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;index&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; (&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;onClick&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;clickPage&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;index&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; + &lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;index&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;index&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; + &lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ))&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; );&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;App&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. 프론트 결과 확인&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 10개의 데이터를 DB구성했고, 4개씩 보이는 것으로 현재페이지 1페이지를 요청하니 아래와 같이 보이게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L3umM/btsnLucFOCk/ZhDWKPokuCMyFMMXWYaBa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L3umM/btsnLucFOCk/ZhDWKPokuCMyFMMXWYaBa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L3umM/btsnLucFOCk/ZhDWKPokuCMyFMMXWYaBa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL3umM%2FbtsnLucFOCk%2FZhDWKPokuCMyFMMXWYaBa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;464&quot; height=&quot;282&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;282&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/199</guid>
      <comments>https://shoney.tistory.com/entry/Spring-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%B2%98%EB%A6%AC-Request-Response-Dto-%EB%B3%80%EC%88%98-%ED%95%AD%EB%AA%A9#entry199comment</comments>
      <pubDate>Sun, 16 Jul 2023 00:11:19 +0900</pubDate>
    </item>
    <item>
      <title>[Mybatis] resultMap을 이용해서 쿼리 서브 정보 받아오기 (left join 정보 받아오기)</title>
      <link>https://shoney.tistory.com/entry/Mybatis-resultMap%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%B4%EC%84%9C-%EC%BF%BC%EB%A6%AC-%EC%84%9C%EB%B8%8C-%EC%A0%95%EB%B3%B4-%EB%B0%9B%EC%95%84%EC%98%A4%EA%B8%B0-left-join-%EC%A0%95%EB%B3%B4-%EB%B0%9B%EC%95%84%EC%98%A4%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;jpa를 사용하는 환경이면 @OneToMany를 사용해서 Dto안의 List&amp;lt;Dto&amp;gt;를 반환하는 것을 쉽게 구현할 수 있다. 하지만, mybatis에서는 resultMap을 이용해서 서브정보를 쿼리로 반환해야 한다. (xml에 resultMap을 사용하는 예시를 바로 보려면 &lt;b&gt;2-2. 쿼리작성&lt;/b&gt; 부터 보시면 됩니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 요구사항&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트에서 아래와 같은 json 포맷으로 요구했다. 메인 정보 안에 서브정보를 리스트로 반환해야 한다.&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #ffffff; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px; border: 1px solid #0f111a;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #80cbc4; border: 1px solid #0f111a; padding: 2px 3px 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt;요구사항 JSON&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #ffffff; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;[&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;id&quot;:&amp;nbsp;&quot;an001&quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;title&quot;:&amp;nbsp;&quot;데브옵스개발팀에&amp;nbsp;초대합니다.&quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;content&quot;:&amp;nbsp;&quot;오세요&quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;skills&quot;:&amp;nbsp;[&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;skillId&quot;:&amp;nbsp;&quot;sk001&quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;skNm&quot;:&amp;nbsp;&quot;java&quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;skillId&quot;:&amp;nbsp;&quot;sk002&quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;skNm&quot;:&amp;nbsp;&quot;python&quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;skillId&quot;:&amp;nbsp;&quot;sk003&quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;skNm&quot;:&amp;nbsp;&quot;react&quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;]&lt;br /&gt;&amp;nbsp;&amp;nbsp;},&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;id&quot;:&amp;nbsp;&quot;an002&quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;title&quot;:&amp;nbsp;&quot;춘개발자를&amp;nbsp;모집합니다.&quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;content&quot;:&amp;nbsp;&quot;오십쇼&quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;skills&quot;:&amp;nbsp;[&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;skillId&quot;:&amp;nbsp;&quot;sk002&quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;skNm&quot;:&amp;nbsp;&quot;python&quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;skillId&quot;:&amp;nbsp;&quot;sk004&quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;skNm&quot;:&amp;nbsp;&quot;mySql&quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;]&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;]&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 반환하기 위해서는 여러 가지 방법이 있겠지만, resultMap을 사용해서 한 번에 반환해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 해결방법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-1. dto를 2개 만든다.&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 json 내용을 보면 공고에 대한 정보(=dto) 하나 안에, 스킬에 관한 서브정보(=dto)로 2개의 dto가 필요할 것을 예상할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2-1-1. 공고 dto&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #80cbc4; border: 1px solid #0f111a; padding: 2px 3px 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt;AnnouncementDto&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; border: #0F111A 1px solid; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Data&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;AnnouncementDto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;List&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;SkillDto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;skills&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2-1-2. 스킬 dto&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #80cbc4; border: 1px solid #0f111a; padding: 2px 3px 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt;SkillDto&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; border: #0F111A 1px solid; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Data&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;SkillDto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;skillId&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;skNm&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-2. 쿼리 작성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;xml에 쿼리를 작성할 때, resultMap을 사용하면 된다.&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #80cbc4; border: 1px solid #0f111a; padding: 2px 3px 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt;쿼리&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; border: #0F111A 1px solid; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;resultMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;resultMap&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;com.lgcns.tct.mcu.dto.AnnouncementDto&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;property&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;column&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;property&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;column&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;property&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;column&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;collection&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;property&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;skills&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;ofType&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;com.lgcns.tct.mcu.dto.SkillDto&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;property&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;skillId&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;column&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;skill_id&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;property&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;skNm&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;column&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;sk_nm&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;collection&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;resultMap&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;getAnnouncementListWithSkill&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;resultMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;resultMap&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SELECT ta.Id, ta.title, ta.content, tas.skill_id, ts.sk_nm&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; FROM kickboard_svc.t_announcement ta&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; LEFT OUTER JOIN kickboard_svc.t_announcement_skill tas&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ON ta.Id = tas.an_id&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; INNER JOIN kickboard_svc.t_skill ts&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ON tas.skill_id = ts.sk_id&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼, resultMap을 사용해서 메인 dto, 서브 dto 관계를 정의를 해서 쿼리문에서 사용하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 결과 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vscode에서는 Thunder Client로 api를 테스트할 수 있다. 아래 캡처 화면을 보면 정상적으로 반환되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1025&quot; data-origin-height=&quot;777&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cO2aCU/btsnE9UXAuk/iNjcJ5mEB1gXI0H5i4mT00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cO2aCU/btsnE9UXAuk/iNjcJ5mEB1gXI0H5i4mT00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cO2aCU/btsnE9UXAuk/iNjcJ5mEB1gXI0H5i4mT00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcO2aCU%2FbtsnE9UXAuk%2FiNjcJ5mEB1gXI0H5i4mT00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1025&quot; height=&quot;777&quot; data-origin-width=&quot;1025&quot; data-origin-height=&quot;777&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/198</guid>
      <comments>https://shoney.tistory.com/entry/Mybatis-resultMap%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%B4%EC%84%9C-%EC%BF%BC%EB%A6%AC-%EC%84%9C%EB%B8%8C-%EC%A0%95%EB%B3%B4-%EB%B0%9B%EC%95%84%EC%98%A4%EA%B8%B0-left-join-%EC%A0%95%EB%B3%B4-%EB%B0%9B%EC%95%84%EC%98%A4%EA%B8%B0#entry198comment</comments>
      <pubDate>Sat, 15 Jul 2023 17:54:10 +0900</pubDate>
    </item>
    <item>
      <title>스프링</title>
      <link>https://shoney.tistory.com/entry/%EC%8A%A4%ED%94%84%EB%A7%81</link>
      <description>&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/3moFU/btsm8geGamq/2zsrotrvMcOKLOR0XuVnx0/6.%20%EC%8A%A4%ED%94%84%EB%A7%81%20MVC%20-%20%EA%B8%B0%EB%B3%B8%20%EA%B8%B0%EB%8A%A5.pdf?attach=1&amp;amp;knm=tfile.pdf&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;6. 스프링 MVC - 기본 기능.pdf&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.86MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/OX2mJ/btsmUIcClqB/4iPhbHvtncmqGjGqLc7BWk/4.%20%EB%8D%B0%EC%9D%B4%ED%84%B0%20%EC%A0%91%EA%B7%BC%20%EA%B8%B0%EC%88%A0%20-%20MyBatis.pdf?attach=1&amp;amp;knm=tfile.pdf&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;4. 데이터 접근 기술 - MyBatis.pdf&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.30MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/197</guid>
      <comments>https://shoney.tistory.com/entry/%EC%8A%A4%ED%94%84%EB%A7%81#entry197comment</comments>
      <pubDate>Mon, 10 Jul 2023 12:27:17 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Mybatis Sample</title>
      <link>https://shoney.tistory.com/entry/Spring-Mybatis-Sample</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤러&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1e1e; color: #d4d4d4;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RestController&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;CrossOrigin&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(origins = &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, allowedHeaders = &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequestMapping&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(value = &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;/api/v1&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequiredArgsConstructor&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UserdataController&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;final&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UsedataService&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;usedataService&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;final&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UserService&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;userService&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//제공 api : 수정 불가&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequestMapping&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(value = &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;/user/{user_no}&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, method = &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;RequestMethod&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;GET&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UserInfoDto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getUseInfo&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;PathVariable&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;user_no&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;user_no&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;) {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UserInfoDto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;userService&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getUserInfo&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;user_no&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//제공 api : 해당 api를 바탕으로 프론트엔드를 구현하시오&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequestMapping&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(value = &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;/user/{user_no}/usage/summary&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, method = &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;RequestMethod&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;GET&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UsedataDto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getUseData&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;PathVariable&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;user_no&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;user_no&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; , @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequestParam&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;ptype&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;ptype&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; ) {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start_dt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getStartDate&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;ptype&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UsedataDto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;usedataService&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getUseData&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;user_no&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start_dt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 작성해야할 api&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequestMapping&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(value = &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;/user/{user_no}/usage&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, method = &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;RequestMethod&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;GET&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Map&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Object&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getUsedList&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;PathVariable&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;user_no&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;user_no&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; , @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequestParam&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;ptype&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;ptype&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;) {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start_dt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getStartDate&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;ptype&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Map&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Object&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;usedataService&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getUsedList&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;user_no&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start_dt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 날짜 계산을 위한 제공 method&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getStartDate&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Calendar&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;currentCalendar&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Calendar&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getInstance&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;nType&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;switch&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;nType&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;case&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 1주일 전&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;currentCalendar&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Calendar&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;DATE&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, -&lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;7&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;); &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;break&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;case&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 1달 전&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;currentCalendar&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Calendar&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;MONTH&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, -&lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;break&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;case&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 3달 전&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;currentCalendar&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Calendar&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;MONTH&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, -&lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;break&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;default:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;currentCalendar&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Calendar&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;DATE&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, -&lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;7&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;); &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;break&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;returnDate&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;java&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;SimpleDateFormat&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;yyyy-MM-dd&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;format&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;currentCalendar&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getTime&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;());&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;returnDate&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매퍼&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1e1e; color: #d4d4d4;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Mapper&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UsedataMapper&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; { &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UsedataDto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getUseData&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Param&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;user_no&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;user_no&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Param&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;start_dt&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start_dt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;List&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UsedListDto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getUsedList&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Param&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;user_no&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;user_no&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Param&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;start_dt&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start_dt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 i/f&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1e1e; color: #d4d4d4;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;lgcns&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;tct&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;service&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;org&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;apache&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;ibatis&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;annotations&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Param&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;lgcns&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;tct&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;dto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UsedataDto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;java&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;util&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.*;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UsedataService&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UsedataDto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getUseData&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Param&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;user_no&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;user_no&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Param&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;start_dt&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start_dt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Map&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Object&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getUsedList&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Param&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;user_no&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;user_no&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Param&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;start_dt&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start_dt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 impl&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1e1e; color: #d4d4d4;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Service&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequiredArgsConstructor&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UseddataServiceimpl&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;implements&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UsedataService&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Autowired&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;final&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UsedataMapper&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;usedataMapper&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Override&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UsedataDto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getUseData&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;user_no&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start_dt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;){&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UsedataDto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;usedataMapper&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getUseData&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;user_no&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start_dt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Override&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Map&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Object&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getUsedList&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;user_no&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start_dt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;List&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UsedListDto&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;list&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;usedataMapper&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getUsedList&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;user_no&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;start_dt&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Map&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Object&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;HashMap&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;list&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;list&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매퍼xml&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1e1e; color: #d4d4d4;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;getUseData&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;parameterType&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;String&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;resultType&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;com.lgcns.tct.dto.UsedataDto&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SELECT COUNT(*) AS usage_count&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; , SUM(use_time) AS usage_minute&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; , SUM(use_distance) AS usage_meter&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; , round(SUM(use_distance)/1000*0.232, 1) AS carbon_reduction&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; FROM kickboard_svc.t_svc_use&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; WHERE 1=1&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; AND user_no=#{user_no} &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; AND DATE(use_start_dt)&amp;gt;=#{start_dt}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;getUsedList&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;parameterType&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;String&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;resultType&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;com.lgcns.tct.dto.UsedListDto&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; SELECT svc.USE_NO AS use_no&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; , svc.USE_DISTANCE AS use_distance&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; , svc.USE_TIME AS use_time&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; , date_format(svc.USE_START_DT, '%Y-%m-%d %h:%i') AS use_start_dt&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; , date_format(svc.USE_END_DT, '%Y-%m-%d %h:%i') AS use_end_dt&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; , date_format(pay.PAY_DATETIME, '%Y-%m-%d %h:%i') AS pay_datetime&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; , max(case when &amp;nbsp;pay.PAYMETHOD_CODE = 'C' then pay.PAY_COST END) AS card_pay&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; , max(case when &amp;nbsp;pay.PAYMETHOD_CODE = 'P' then pay.PAY_COST END) AS point_pay&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; FROM kickboard_svc.t_svc_use_pay as pay&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; , kickboard_svc.t_svc_use AS svc&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; WHERE 1=1&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; AND svc.USE_NO = &amp;nbsp;pay.USE_NO&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; AND svc.USER_NO =#{user_no}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; AND DATE(svc.use_start_dt)&amp;gt;=#{start_dt}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; GROUP BY svc.USE_NO&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ORDER BY use_start_dt DESC&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/196</guid>
      <comments>https://shoney.tistory.com/entry/Spring-Mybatis-Sample#entry196comment</comments>
      <pubDate>Sun, 9 Jul 2023 22:55:46 +0900</pubDate>
    </item>
    <item>
      <title>[SourceTree] window에서 소스트리 실행 안될 때</title>
      <link>https://shoney.tistory.com/entry/SourceTree-window%EC%97%90%EC%84%9C-%EC%86%8C%EC%8A%A4%ED%8A%B8%EB%A6%AC-%EC%8B%A4%ED%96%89-%EC%95%88%EB%90%A0-%EB%95%8C</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 문제 상황&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;갑자기 소스트리가 실행이 안된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 화면에서 더 진행이 안되고 바로 꺼진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Sz6Cx/btsl1AmOiKd/7CmVvaxOPXs5GMMwWWXeKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Sz6Cx/btsl1AmOiKd/7CmVvaxOPXs5GMMwWWXeKk/img.png&quot; data-alt=&quot;이 화면에서 더이상 실행 없이 꺼짐&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Sz6Cx/btsl1AmOiKd/7CmVvaxOPXs5GMMwWWXeKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSz6Cx%2Fbtsl1AmOiKd%2F7CmVvaxOPXs5GMMwWWXeKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;437&quot; height=&quot;261&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이 화면에서 더이상 실행 없이 꺼짐&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 해결방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때, 아래 경로에 있는 SourceTree.exe._Url_로 시작하는 폴더를 삭제하면 실행된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;경로 : C:\Users\User\AppData\Local\Atlassian&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpnukB/btsl9MTyb3X/dI23VzqiGXe2f8UGzZgrc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpnukB/btsl9MTyb3X/dI23VzqiGXe2f8UGzZgrc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpnukB/btsl9MTyb3X/dI23VzqiGXe2f8UGzZgrc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpnukB%2Fbtsl9MTyb3X%2FdI23VzqiGXe2f8UGzZgrc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;801&quot; height=&quot;333&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Git</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/195</guid>
      <comments>https://shoney.tistory.com/entry/SourceTree-window%EC%97%90%EC%84%9C-%EC%86%8C%EC%8A%A4%ED%8A%B8%EB%A6%AC-%EC%8B%A4%ED%96%89-%EC%95%88%EB%90%A0-%EB%95%8C#entry195comment</comments>
      <pubDate>Sun, 2 Jul 2023 22:13:20 +0900</pubDate>
    </item>
    <item>
      <title>[MySql] COALESCE 함수 사용 예제</title>
      <link>https://shoney.tistory.com/entry/MySql-COALESCE-%ED%95%A8%EC%88%98-%EC%82%AC%EC%9A%A9-%EC%98%88%EC%A0%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL의 COALESCE 함수는 인자로 전달된 여러 개의 값 중에서 첫 번째로 NULL이 아닌 값을 반환하는 함수입니다. 즉, NULL이 아닌 첫 번째 값을 찾아 반환하거나, 모든 값이 NULL인 경우에는 NULL을 반환합니다.&lt;br /&gt;COALESCE 함수는 다음과 같은 형태로 사용됩니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #edf7f6; padding: 10px; margin: 10px 12px 10px 12px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #e9950c;&quot;&gt;COALESCE&lt;/span&gt;(value1, value2, ...)&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 개의 값(value1, value2, ...)이 주어지고, COALESCE 함수는 이 값들을 왼쪽에서 오른쪽으로 순서대로 확인하여 첫 번째로 NULL이 아닌 값을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;간단한 예제로 설명해보면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;customers 테이블의 테이터이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 44.8837%; height: 101px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 24.2597%; height: 17px;&quot;&gt;id&lt;/td&gt;
&lt;td style=&quot;width: 32.5529%; height: 17px;&quot;&gt;name&lt;/td&gt;
&lt;td style=&quot;width: 36.253%; height: 17px;&quot;&gt;city&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 24.2597%; height: 17px;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 32.5529%; height: 17px;&quot;&gt;John&lt;/td&gt;
&lt;td style=&quot;width: 36.253%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #2e95d3;&quot;&gt;NULL&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 24.2597%; height: 17px;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 32.5529%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;Alice&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.253%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;London&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 24.2597%; height: 17px;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 32.5529%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;Michael&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.253%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #2e95d3;&quot;&gt;NULL&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 아래와 같이 없는 city가 null인 경우에는 Unknown으로 표현하는 예제처럼 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #ffffff; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px; border: 1px solid #0f111a;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;border: 1px solid #0f111a; padding: 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt;예제&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #ffffff; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #a2aaa8;&quot;&gt;&lt;span style=&quot;color: #2e95d3;&quot;&gt;SELECT&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;id, name,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #e9950c;&quot;&gt;COALESCE&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(city,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #00a67d;&quot;&gt;'Unknown'&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;)&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #2e95d3;&quot;&gt;AS&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;city&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #2e95d3;&quot;&gt;FROM&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;customers;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 쿼리를 실행하면 다음과 같은 결과는 아래와 같다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 46.0466%; height: 121px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #6ed3d8; color: #ffffff; width: 16.9192%;&quot;&gt;id&lt;/td&gt;
&lt;td style=&quot;background-color: #6ed3d8; color: #ffffff; width: 38.1313%;&quot;&gt;name&lt;/td&gt;
&lt;td style=&quot;background-color: #6ed3d8; color: #ffffff; width: 44.697%;&quot;&gt;city&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef; width: 16.9192%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 38.1313%;&quot;&gt;John&lt;/td&gt;
&lt;td style=&quot;width: 44.697%;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #00a67d; text-align: justify;&quot;&gt;Unknown&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef; width: 16.9192%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9; width: 38.1313%;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;Alice&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9; width: 44.697%;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;London&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef; width: 16.9192%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 38.1313%;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;Michael&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.697%;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #00a67d; text-align: justify;&quot;&gt;Unknown&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;COALESCE 함수는 NULL 처리나 기본값을 설정할 때 유용하게 사용될 수 있습니다.&lt;/p&gt;</description>
      <category>SQL</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/194</guid>
      <comments>https://shoney.tistory.com/entry/MySql-COALESCE-%ED%95%A8%EC%88%98-%EC%82%AC%EC%9A%A9-%EC%98%88%EC%A0%9C#entry194comment</comments>
      <pubDate>Tue, 27 Jun 2023 17:51:31 +0900</pubDate>
    </item>
    <item>
      <title>[React] axios 사용한 GET, POST, PUT, DELETE 메서드 구현 간단한 예제</title>
      <link>https://shoney.tistory.com/entry/React-axios-%EC%82%AC%EC%9A%A9%ED%95%9C-GET-POST-PUT-DELETE-%EB%A9%94%EC%84%9C%EB%93%9C-%EA%B5%AC%ED%98%84-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%98%88%EC%A0%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서 axios로 get, post, put, delete 간단한 예제를 알아보도록 하자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px; border: 1px solid #0f111a;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #80cbc4; border: 1px solid #0f111a; padding: 2px 3px 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt;소스 코드&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #0f111a; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;useState&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #c3e88d;&quot;&gt;react&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; axios &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #c3e88d;&quot;&gt;axios&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;;&lt;/span&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;App&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;users&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;setUsers&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;useState&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;([])&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;setName&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;useState&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;setEmail&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;useState&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;getUsers&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;},&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; [])&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;getUsers&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;axios&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #c3e88d;&quot;&gt;&lt;a href=&quot;https://api.example.com/users&quot;&gt;https://api.example.com/users&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;setUsers&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;};&lt;/span&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;addUser&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;axios&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;post&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #c3e88d;&quot;&gt;&lt;a href=&quot;https://api.example.com/users&quot;&gt;https://api.example.com/users&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;getUsers&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;setName&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;setEmail&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;};&lt;/span&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;updateUser&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;userId&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;axios&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #c3e88d;&quot;&gt;&lt;a href=&quot;https://api.example.com/users/&quot;&gt;https://api.example.com/users/&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;userId&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;getUsers&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;setName&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;setEmail&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;};&lt;/span&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;deleteUser&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;userId&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;axios&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;delete&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #c3e88d;&quot;&gt;&lt;a href=&quot;https://api.example.com/users/&quot;&gt;https://api.example.com/users/&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;userId&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;getUsers&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;};&lt;/span&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt; (&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;Users&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;users&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; (&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; - &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;onClick&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;={()&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;deleteUser&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;id)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;Delete&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ))&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;Add User&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;input&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #c3e88d;&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;onChange&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;={(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;setName&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;target&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;placeholder&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #c3e88d;&quot;&gt;Name&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;input&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #c3e88d;&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;onChange&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;={(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;setEmail&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;target&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;placeholder&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #c3e88d;&quot;&gt;Email&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c792ea;&quot;&gt;onClick&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color: #82aaff;&quot;&gt;addUser&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;Add&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f07178;&quot;&gt;&amp;nbsp; )&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;color: #a6accd;&quot;&gt; Test&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #ffffff; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px; border: 1px solid #0f111a;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #80cbc4; border: 1px solid #0f111a; padding: 2px 3px 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt;결과&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #ffffff; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;239&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baMJpd/btslaI6QDkM/pjWYn6jCxMKccSgKRG3ykk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baMJpd/btslaI6QDkM/pjWYn6jCxMKccSgKRG3ykk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baMJpd/btslaI6QDkM/pjWYn6jCxMKccSgKRG3ykk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaMJpd%2FbtslaI6QDkM%2FpjWYn6jCxMKccSgKRG3ykk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;486&quot; height=&quot;239&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;239&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Front-End/React</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/193</guid>
      <comments>https://shoney.tistory.com/entry/React-axios-%EC%82%AC%EC%9A%A9%ED%95%9C-GET-POST-PUT-DELETE-%EB%A9%94%EC%84%9C%EB%93%9C-%EA%B5%AC%ED%98%84-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%98%88%EC%A0%9C#entry193comment</comments>
      <pubDate>Sun, 25 Jun 2023 15:55:10 +0900</pubDate>
    </item>
    <item>
      <title>[SQL] CASE 문으로 유용한 쿼리문 예시</title>
      <link>https://shoney.tistory.com/entry/SQL-CASE-%EB%AC%B8%EC%9C%BC%EB%A1%9C-%EC%9C%A0%EC%9A%A9%ED%95%9C-%EC%BF%BC%EB%A6%AC%EB%AC%B8-%EC%98%88%EC%8B%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://overit.tistory.com/entry/SQL-CASE-%EC%8B%9D-Feat-%EC%A1%B0%EA%B1%B4%EB%B6%84%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://overit.tistory.com/entry/SQL-CASE-%EC%8B%9D-Feat-%EC%A1%B0%EA%B1%B4%EB%B6%84%EA%B8%B0&lt;/a&gt;&lt;/p&gt;</description>
      <category>SQL</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/192</guid>
      <comments>https://shoney.tistory.com/entry/SQL-CASE-%EB%AC%B8%EC%9C%BC%EB%A1%9C-%EC%9C%A0%EC%9A%A9%ED%95%9C-%EC%BF%BC%EB%A6%AC%EB%AC%B8-%EC%98%88%EC%8B%9C#entry192comment</comments>
      <pubDate>Sun, 21 May 2023 17:30:17 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 컨트롤러 Request, Response 간단한 예시</title>
      <link>https://shoney.tistory.com/entry/Spring-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC-%EC%BF%BC%EB%A6%AC%ED%8C%8C%EB%9D%BC-%EB%B3%80%EC%88%98%ED%8C%A8%EC%8A%A4-JSON%EB%B0%94%EB%94%94-%EB%B0%9B%EB%8A%94-%EB%B0%A9%EB%B2%95-%EA%B0%84%EB%8B%A8%ED%95%9C%EC%98%88%EC%8B%9C</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Request 받는 예시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 사용하는 1. 쿼리파라, 2.변수패스, 3. json 바디 예시를 작성했습니다.&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 쿼리 파라미터&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;java&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(&quot;/api/books&quot;)
public class BookController {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Autowired
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private BookService bookService;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@GetMapping(&quot;/search&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public List&amp;lt;Book&amp;gt; searchBooks(@RequestParam(&quot;keyword&quot;) String keyword) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return bookService.searchBooks(keyword);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;위의 예제에서는 `@RequestParam` 어노테이션을 사용하여 `keyword`라는 이름의 쿼리 파라미터를 전달받습니다. `/api/books/search?keyword=java`와 같은 요청을 처리합니다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;b&gt;2. URL 경로 변수(Path variable)&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;java&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(&quot;/api/books&quot;)
public class BookController {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Autowired
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private BookService bookService;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@GetMapping(&quot;/{id}&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public Book getBookById(@PathVariable(&quot;id&quot;) Long id) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return bookService.getBookById(id);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;위의 예제에서는 `@PathVariable` 어노테이션을 사용하여 `id`라는 이름의 경로 변수를 전달받습니다. `/api/books/1`와 같은 요청을 처리합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;b&gt;3. JSON&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;java&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(&quot;/api/books&quot;)
public class BookController {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Autowired
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private BookService bookService;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@PostMapping
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public Book addBook(@RequestBody Book book) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return bookService.addBook(book);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;위의 예제에서는 `@RequestBody` 어노테이션을 사용하여 JSON 형식의 데이터를 전달받습니다. 클라이언트에서는 HTTP POST 요청을 보내며, 요청 본문에 JSON 형식의 Book 객체를 담아 전송합니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Response&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;다음은 Spring MVC에서 자주 사용되는 response 유형 예시입니다.&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;b&gt;1. Map을 사용하여 JSON 응답 생성하기&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1684227539723&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(&quot;/api/books&quot;)
public class BookController {
    
    @Autowired
    private BookService bookService;

    @GetMapping(&quot;/{id}&quot;)
    public Map&amp;lt;String, Object&amp;gt; getBookById(@PathVariable(&quot;id&quot;) Long id) {
        Book book = bookService.getBookById(id);
        Map&amp;lt;String, Object&amp;gt; response = new HashMap&amp;lt;&amp;gt;();
        response.put(&quot;title&quot;, book.getTitle());
        response.put(&quot;author&quot;, book.getAuthor());
        response.put(&quot;publishedYear&quot;, book.getPublishedYear());
        return response;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;위의 예제에서는 Map 객체를 사용하여 JSON 응답을 생성합니다. 클라이언트에서 `/api/books/1`와 같은 GET 요청을 보내면, 해당 Book 객체를 Map으로 변환하여 JSON 형식으로 응답합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. List를 사용하여 JSON 응답 생성하기&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1684227563522&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(&quot;/api/books&quot;)
public class BookController {
    
    @Autowired
    private BookService bookService;

    @GetMapping(&quot;/all&quot;)
    public List&amp;lt;Book&amp;gt; getAllBooks() {
        return bookService.getAllBooks();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;위의 예제에서는 List 객체를 사용하여 JSON 응답을 생성합니다. 클라이언트에서 `/api/books/all`와 같은 GET 요청을 보내면, 모든 Book 객체를 List에 담아 JSON 형식으로 응답합니다.&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;b&gt;3. DTO(Data Transfer Object)를 사용하여 JSON 응답 생성하기&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1684227587411&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(&quot;/api/books&quot;)
public class BookController {
    
    @Autowired
    private BookService bookService;

    @GetMapping(&quot;/{id}&quot;)
    public BookDto getBookById(@PathVariable(&quot;id&quot;) Long id) {
        Book book = bookService.getBookById(id);
        BookDto bookDto = new BookDto();
        bookDto.setTitle(book.getTitle());
        bookDto.setAuthor(book.getAuthor());
        bookDto.setPublishedYear(book.getPublishedYear());
        return bookDto;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;위의 예제에서는 DTO 객체를 사용하여 JSON 응답을 생성합니다. BookDto는 Book 엔티티 클래스와 유사하지만, 클라이언트에 필요한 필드만을 가지고 있습니다. 클라이언트에서 `/api/books/1`와 같은 GET 요청을 보내면, 해당 Book 객체를 BookDto로 변환하여 JSON 형식으로 응답합니다.&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/191</guid>
      <comments>https://shoney.tistory.com/entry/Spring-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC-%EC%BF%BC%EB%A6%AC%ED%8C%8C%EB%9D%BC-%EB%B3%80%EC%88%98%ED%8C%A8%EC%8A%A4-JSON%EB%B0%94%EB%94%94-%EB%B0%9B%EB%8A%94-%EB%B0%A9%EB%B2%95-%EA%B0%84%EB%8B%A8%ED%95%9C%EC%98%88%EC%8B%9C#entry191comment</comments>
      <pubDate>Tue, 16 May 2023 17:27:37 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 가변객체, 불변객체</title>
      <link>https://shoney.tistory.com/entry/Java-%EA%B0%80%EB%B3%80%EA%B0%9D%EC%B2%B4-%EB%B6%88%EB%B3%80%EA%B0%9D%EC%B2%B4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서 가변(mutable) 객체와 불변(immutable) 객체의 차이점은 객체의 내용을 변경할 수 있는지 여부!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 자바를 사용했다면, 해당 어휘에 대해서만 모를 뿐이지 모두가 사용해본 내용일 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 내용들을 보면서 어떤 의미인지 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 가변 객체 (Mutable Object)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체의 내용을 변경할 수 있는 객체입니다.&lt;/li&gt;
&lt;li&gt;객체가 생성된 후, 내부의 상태를 변경할 수 있습니다.&lt;/li&gt;
&lt;li&gt;가변 객체를 사용하면 메모리 사용량이 크게 증가할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; mutableList = new ArrayList&amp;lt;&amp;gt;();
mutableList.add(&quot;A&quot;);
mutableList.add(&quot;B&quot;);
mutableList.add(&quot;C&quot;);
mutableList.remove(1); // &quot;B&quot; 제거&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;2. 불변 객체 (Immutable Object)&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체가 생성된 후, 내부의 상태를 변경할 수 없는 객체입니다.&lt;/li&gt;
&lt;li&gt;불변 객체를 사용하면 안정성과 보안성이 향상됩니다.&lt;/li&gt;
&lt;li&gt;불변 객체는 스레드 안전성을 보장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;String immutableString = &quot;Hello, world!&quot;;
immutableString.toUpperCase(); // 새로운 문자열을 반환하지만 immutableString 객체의 내용은 변하지 않음&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불변 객체의 경우, 객체가 생성된 후에는 내부 상태를 변경할 수 없으므로, 새로운 객체를 생성하거나 복사해야 하는 경우가 많습니다. 이러한 특성으로 인해, 불변 객체는 보안성이나 안정성이 중요한 경우, 혹은 함수형 프로그래밍에서 많이 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면, 가변 객체는 내부 상태를 변경할 수 있으므로, 유연한 코드 작성이 가능하며, 일부 자료 구조나 알고리즘에서는 가변 객체를 사용해야만 효율적인 작업이 가능한 경우가 있습니다.&lt;/p&gt;</description>
      <category>프로그래밍언어/JAVA</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/190</guid>
      <comments>https://shoney.tistory.com/entry/Java-%EA%B0%80%EB%B3%80%EA%B0%9D%EC%B2%B4-%EB%B6%88%EB%B3%80%EA%B0%9D%EC%B2%B4#entry190comment</comments>
      <pubDate>Thu, 30 Mar 2023 14:10:46 +0900</pubDate>
    </item>
    <item>
      <title>[Java] String, StringBuilder, StringBuffer 차이점</title>
      <link>https://shoney.tistory.com/entry/Java-String-StringBuilder-StringBuffer-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서 문자열을 사용할 때, 자주 보이는 클래스 String, StringBuffer, StringBuilder 차이점을 알아봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. String&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;String은 불변(immutable)한 문자열을 나타내는 클래스입니다.&lt;/li&gt;
&lt;li&gt;문자열을 변경할 경우, 기존 문자열 객체를 변경하는 것이 아니라 새로운 문자열 객체를 생성합니다.&lt;/li&gt;
&lt;li&gt;멀티 스레드 환경에서 안전합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;String str = &quot;hello&quot;;
str = str + &quot; world&quot;; // &quot;hello world&quot; 문자열 생성&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;2. StringBuilder&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;StringBuilder는 가변(mutable)한 문자열을 나타내는 클래스입니다.&lt;/li&gt;
&lt;li&gt;문자열을 변경할 때, 기존 객체를 변경하며, 새로운 객체를 생성하지 않습니다.&lt;/li&gt;
&lt;li&gt;멀티 스레드 환경에서 안전하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;StringBuilder sb = new StringBuilder(&quot;hello&quot;);
sb.append(&quot; world&quot;); // &quot;hello world&quot; 문자열 생성&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;3. StringBuffer&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;StringBuffer는 StringBuilder와 기능이 거의 동일하지만, 멀티 스레드 환경에서 안전합니다.&lt;/li&gt;
&lt;li&gt;StringBuilder와 달리 동기화를 지원하므로, 스레드 안전성을 보장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;StringBuffer sb = new StringBuffer(&quot;hello&quot;);
sb.append(&quot; world&quot;); // &quot;hello world&quot; 문자열 생성&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍언어/JAVA</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/189</guid>
      <comments>https://shoney.tistory.com/entry/Java-String-StringBuilder-StringBuffer-%EC%B0%A8%EC%9D%B4%EC%A0%90#entry189comment</comments>
      <pubDate>Thu, 30 Mar 2023 13:50:48 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] @ResponseBody에서 객체를 JSON 반환(HttpMessageConverter)</title>
      <link>https://shoney.tistory.com/entry/Spring-ResponseBody%EC%97%90%EC%84%9C-%EA%B0%9D%EC%B2%B4%EB%A5%BC-JSON-%EB%B0%98%ED%99%98HttpMessageConverter</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;결론은 @ResponseBody어노테이션을 사용해도, Getter 사용하지 않는 필드는 자동으로 JSON 변환이 안됩니다. (Getter를 사용해!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;롬복 @Data 어노테이션을 남발하다 보면, 기본적인 @ResponseBody가(이것도 물론 @RestController로 사용하긴 합니다.) 작동하는 최소의 조건을 잊고 살 수 있다. 토이 프로젝트를 진행하면서 최소한의 어노테이션으로 진행해보려고 하니 예전에 배웠던 것들도 다시 한번 정리하는 시간을 가지게 되었다. 하나둘 씩 정리해서 블로그에 올리려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. @ResponseBody&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- @ResponseBody 어노테이션은 컨트롤러 메서드에서 반환되는 객체를 HTTP 응답 본문에 직접 쓰기 위해 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 반환 타입에 따라 적절한 &lt;b&gt;HttpMessageConverter&lt;/b&gt;가 선택되어 해당 객체를 변환 후 응답 바디에 쓰여진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. HttpMessageConverter&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 스프링에서 요청과 응답할 때&amp;nbsp;HttpMessageConverter를 사용해서 변환한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 예를 들어,&amp;nbsp; &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;String은 StringHttpMessageConverter가 선택되어 반환.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;- Json은&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px; color: #374151;&quot;&gt;MappingJackson2HttpMessageConverter 가 선택되어서 반환.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px; background-color: #f3c000; color: #374151;&quot;&gt;- &lt;span style=&quot;color: #374151;&quot;&gt;MappingJackson2HttpMessageConverter가 반환할 때, 필드의 Getter를 사용해서 JSON객체로 변환한다.&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Back-end/Spring</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/188</guid>
      <comments>https://shoney.tistory.com/entry/Spring-ResponseBody%EC%97%90%EC%84%9C-%EA%B0%9D%EC%B2%B4%EB%A5%BC-JSON-%EB%B0%98%ED%99%98HttpMessageConverter#entry188comment</comments>
      <pubDate>Thu, 30 Mar 2023 11:20:31 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] StringBuilder 메서드 별 간단한 사용 예제</title>
      <link>https://shoney.tistory.com/entry/JAVA-StringBuilder-%EB%A9%94%EC%84%9C%EB%93%9C-%EB%B3%84-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%82%AC%EC%9A%A9-%EC%98%88%EC%A0%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;StringBuilder 클래스는 Java에서 문자열을 효율적으로 처리하는 데 사용되는 클래스이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실, + 를 통해서 String을 append 하는 것 보다 객체 생성될 여지가 적기 때문에, 약간의 속도 개선이 있다. (가독성이 떨어진다는 점은 단점이긴 하다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 메서드와 사용 방법을 간단한 예제를 통해서 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. append(String str) : 문자열을 추가합니다.&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;StringBuilder sb = new StringBuilder();
sb.append(&quot;Hello&quot;);
sb.append(&quot; World&quot;);
String result = sb.toString(); // &quot;Hello World&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. insert(int offset, String str) : 문자열을 특정 위치에 삽입합니다.&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;StringBuilder sb = new StringBuilder();
sb.append(&quot;Hello&quot;);
sb.insert(2, &quot;Java &quot;);
String result = sb.toString(); // &quot;HeJava llo&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;3. delete(int start, int end) : 문자열의 일부를 삭제합니다.&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;StringBuilder sb = new StringBuilder();
sb.append(&quot;Hello World&quot;);
sb.delete(5, 11);
String result = sb.toString(); // &quot;Hello&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;4. replace(int start, int end, String str) : 문자열의 일부를 다른 문자열로 교체합니다.&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;StringBuilder sb = new StringBuilder();
sb.append(&quot;Hello World&quot;);
sb.replace(6, 11, &quot;Java&quot;);
String result = sb.toString(); // &quot;Hello Java&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;5. reverse() : 문자열을 뒤집습니다.&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;StringBuilder sb = new StringBuilder();
sb.append(&quot;Hello&quot;);
sb.reverse();
String result = sb.toString(); // &quot;olleH&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍언어/JAVA</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/187</guid>
      <comments>https://shoney.tistory.com/entry/JAVA-StringBuilder-%EB%A9%94%EC%84%9C%EB%93%9C-%EB%B3%84-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%82%AC%EC%9A%A9-%EC%98%88%EC%A0%9C#entry187comment</comments>
      <pubDate>Wed, 29 Mar 2023 22:43:20 +0900</pubDate>
    </item>
    <item>
      <title>[macOS] 실행중인 포트 종료하기 (lsof, kill 명령어 옵션 설명)</title>
      <link>https://shoney.tistory.com/entry/macOS-%EC%8B%A4%ED%96%89%EC%A4%91%EC%9D%B8-%ED%8F%AC%ED%8A%B8-%EC%A2%85%EB%A3%8C%ED%95%98%EA%B8%B0-lsof-kill-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%98%B5%EC%85%98-%EC%84%A4%EB%AA%85</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;급하신 분들을 위해 해결 방법부터 설명하고, 옵션에 대한 추가적인 설명을 하도록 하겠습니다.&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 해결 방법 (포트 번호 8080 기준)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 터미널에서 &lt;span&gt;lsof -i :8080 입력합니다.&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #2b2b2b; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #3e86a1; border: 1px solid #0f111a; padding: 2px 3px 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt;터미널&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #2b2b2b; border: #2B2B2B 1px solid; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;$ lsof -i :8080&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에 lsof -i 8080 명령어를 입력하게 되면, 아래와 같이 해당 포트의 PID를 알수있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;34&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1VJKB/btr4hy6255d/1ByDKjmk18vck2JTsK0xN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1VJKB/btr4hy6255d/1ByDKjmk18vck2JTsK0xN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1VJKB/btr4hy6255d/1ByDKjmk18vck2JTsK0xN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1VJKB%2Fbtr4hy6255d%2F1ByDKjmk18vck2JTsK0xN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;34&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;34&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8080포트에서는 java가 실행중이고 PID는 1621인것을 알수있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 터미널에서 해당 PID를 종료시키기(PID 1621기준)&lt;/b&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #2b2b2b; padding: 0px; margin: 0px 0px 2px 0px; font-family: 'Nanum Gothic'; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p style=&quot;text-align: right; margin: 0px;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #3e86a1; border: 1px solid #0f111a; padding: 2px 3px 2px 3px; margin: 1px; border-radius: 5px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #3e86a1; color: #333333; text-align: right;&quot;&gt;터미널&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #2b2b2b; border: #2B2B2B 1px solid; padding: 0px 12px 15px 12px; margin: 0px; font-size: 16px; border-radius: 5px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;$ kill -9 1621&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-9옵션은 강제 종료 옵션입니다. 뒤에 PID를 붙인 명령어를 입력해서 강제 종료합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어를 입력하고 다시 lsof -i :8080으로 실행하고 있는지 조회해보면 제대로 종료 됐는지 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 추가적인 옵션 설명&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) lsof (&lt;b&gt;l&lt;/b&gt;&lt;span style=&quot;background-color: #fefefe; color: #000c34; text-align: start;&quot;&gt;i&lt;/span&gt;&lt;b&gt;s&lt;/b&gt;&lt;span style=&quot;background-color: #fefefe; color: #000c34; text-align: start;&quot;&gt;t&amp;nbsp;&lt;/span&gt;&lt;b&gt;o&lt;/b&gt;&lt;span style=&quot;background-color: #fefefe; color: #000c34; text-align: start;&quot;&gt;pen&amp;nbsp;&lt;/span&gt;&lt;b&gt;f&lt;/b&gt;&lt;span style=&quot;background-color: #fefefe; color: #000c34; text-align: start;&quot;&gt;iles 약자)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'lsof'는 열린 파일 목록(l&lt;span style=&quot;background-color: #fefefe; color: #000c34; text-align: start;&quot;&gt;i&lt;/span&gt;s&lt;span style=&quot;background-color: #fefefe; color: #000c34; text-align: start;&quot;&gt;t&amp;nbsp;&lt;/span&gt;o&lt;span style=&quot;background-color: #fefefe; color: #000c34; text-align: start;&quot;&gt;pen&amp;nbsp;&lt;/span&gt;f&lt;span style=&quot;background-color: #fefefe; color: #000c34; text-align: start;&quot;&gt;iles)&lt;/span&gt;을 나타내는 명령어입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 macOS에서 'lsof' 명령어에 사용 가능한 옵션들입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f7f7f8; color: #374151; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'-c': 프로세스 이름으로 파일을 찾습니다.&lt;/li&gt;
&lt;li&gt;'-d': 파일 기술자 (file descriptor)를 찾습니다.&lt;/li&gt;
&lt;li&gt;'-g': 그룹 ID로 파일을 찾습니다.&lt;/li&gt;
&lt;li&gt;'-i': 네트워크 소켓을 찾습니다.&lt;/li&gt;
&lt;li&gt;'-n': 호스트 이름을 IP 주소로 해석하지 않습니다.&lt;/li&gt;
&lt;li&gt;'-P': 포트 번호를 숫자로 보여줍니다.&lt;/li&gt;
&lt;li&gt;'-p': 지정한 프로세스 ID의 파일을 찾습니다.&lt;/li&gt;
&lt;li&gt;'-u': 사용자 ID로 파일을 찾습니다.&lt;/li&gt;
&lt;li&gt;'-t': 지정한 파일 타입의 파일을 찾습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) kill&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'kill' 명령어는 다른 프로세스를 중지시키는 명령어입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'kill' 명령어에는 여러 옵션이 있습니다. 일반적으로 많이 사용되는 몇 가지 옵션을 살펴보겠습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f7f7f8; color: #374151; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'-9' : SIGKILL 시그널을 보내며, 프로세스를 즉시 강제 종료합니다.&lt;/li&gt;
&lt;li&gt;'-15' : SIGTERM 시그널을 보내며, 프로세스에게 정상적인 종료를 요청합니다.&lt;/li&gt;
&lt;li&gt;'-s' : 보낼 시그널을 지정합니다. 기본적으로 SIGTERM 시그널이 사용됩니다.&lt;/li&gt;
&lt;li&gt;'-l' : 사용 가능한 시그널 목록을 보여줍니다.&lt;/li&gt;
&lt;li&gt;'-u' : 프로세스의 유저 ID를 지정합니다.&lt;/li&gt;
&lt;li&gt;'-c' : 프로세스의 이름을 지정합니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>etc</category>
      <author>shoney9254</author>
      <guid isPermaLink="true">https://shoney.tistory.com/186</guid>
      <comments>https://shoney.tistory.com/entry/macOS-%EC%8B%A4%ED%96%89%EC%A4%91%EC%9D%B8-%ED%8F%AC%ED%8A%B8-%EC%A2%85%EB%A3%8C%ED%95%98%EA%B8%B0-lsof-kill-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%98%B5%EC%85%98-%EC%84%A4%EB%AA%85#entry186comment</comments>
      <pubDate>Fri, 17 Mar 2023 10:47:18 +0900</pubDate>
    </item>
  </channel>
</rss>