<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발 블로그</title>
    <link>https://daily1313.tistory.com/</link>
    <description>[IT(PS, CS, SW, etc.)  지식  기록]



Github : https://github.com/daily1313/</description>
    <language>ko</language>
    <pubDate>Wed, 10 Jun 2026 01:16:10 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>SeungbeomKim</managingEditor>
    <image>
      <title>개발 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/5421750/attach/d27a8cc76cea4e2e9dad7aa1f86daa6f</url>
      <link>https://daily1313.tistory.com</link>
    </image>
    <item>
      <title>[WebSquare] 웹스퀘어에 대해 알아보자</title>
      <link>https://daily1313.tistory.com/entry/WebSquare-%EC%9B%B9%EC%8A%A4%ED%80%98%EC%96%B4%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 이직한 회사에서는 화면단 기술로 WebSquare를 사용하고 있습니다. 이전 프로젝트에서 Nexacro를 잠시 사용해 본 경험이 있었는데, 두 기술은 차이점도 있지만 개인적으로는 개발 방식이나 사용 흐름에서 비슷한 느낌을 많이 받았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;317&quot; data-start=&quot;165&quot; data-ke-size=&quot;size16&quot;&gt;실제로 전 직장에서 WebSquare를 사용해보지 않고 입사한 분들이 많아 회사에서도 &lt;a href=&quot;https://wtech.inswave.kr/websquare/websquare.html?w2xPath=/cm/xml/index.xml&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;온라인 교육(Inswave 교육)&lt;/a&gt;을 지원해주었고, 팀원분들도 모두 해당 교육을 수강했다고 들었습니다. 저 역시 입사 직후 해당 교육을 수강했으며, 이를 바탕으로 WebSquare에 대해 전체적으로 정리해보려고 합니다.&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;&lt;span data-token-index=&quot;0&quot;&gt;WebSquare&lt;/span&gt;&lt;span data-token-index=&quot;0&quot;&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인스웨이브 시스템즈의 UI 개발 프레임워크로서 xml을 서버의 웹스퀘어 엔진(서블릿)이 해석해 HTML/Javascript로 렌더링하는 방식으로 동작합니다.&lt;/li&gt;
&lt;li&gt;UI 컴포넌트와 Util API, 통합 개발 도구를 지원합니다.&lt;/li&gt;
&lt;li&gt;주요 화면은 xml로 구성하고 w-Pack 기술을 통해 컴파일 시 xml을 js로 변환합니다.&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;Web 루트 (WebContent) 이하에서 websquare 파일을 생성합니다.&lt;/li&gt;
&lt;li&gt;파일 생성 시 WebSquare Page Component를 클릭합니다.&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;WebSquare xml 파일 구성요소&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Design, Info, Script, DataCollection, Submission, Source&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;1. Design&lt;/b&gt;&lt;/u&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;u&gt;&lt;b&gt;2. Info&lt;/b&gt;&lt;/u&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;화면 id, 화면명 등을 입력하는 영역 (개발 영역과는 별개, 주로 설계에서 사용합니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;3. Script&lt;/b&gt;&lt;/u&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;모든 변수나 함수를 scwin 객체 기반으로 다룹니다. (효율적인 메모리 및 자원관리)
&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;초기화: scwin.onpageload = function() = {} (init함수 역할)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;xml 파일 생성 시 자동으로 생성해줍니다.&lt;/li&gt;
&lt;li&gt;onpageload: 화면이 로딩될 때 발생하는 이벤트&lt;/li&gt;
&lt;li&gt;onbeforepageunload: 페이지가 닫히기 전 발생하는 이벤트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;4. DataCollection&lt;/b&gt;&lt;/u&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저의 메모리에 할당되는 JavaScript 데이터 객체 (DataSet과 비슷하며 더욱 세분화된 영역)&lt;/li&gt;
&lt;li&gt;1. DataMap (단건 데이터 관리(key, value))&lt;/li&gt;
&lt;li&gt;2. DataList (다건 데이터 관리(col1, col2)) ~= Grid&lt;/li&gt;
&lt;li&gt;3. LinkedDataList (Java의 LinkedList와 유사합니다. Filter처리 등..)&lt;/li&gt;
&lt;li&gt;4. AliasDataList (메인 페이지와 서브 페이지로 구성된 경우에 사용하며, 활용도가 낮습니다.)&lt;/li&gt;
&lt;li&gt;5. AliasDataMap&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;5. Submission&lt;/b&gt;&lt;/u&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;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;6. Source&lt;/b&gt;&lt;/u&gt;&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;websquare 최종본 (직접 수정 X)&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;개인적으로 소스를 분석하면서 scwin이라는 키워드가 자주 사용되는 것을 확인했고, 자연스럽게 이에 대한 궁금증이 생겼습니다. 그래서 &lt;a href=&quot;https://earscoming.tistory.com/entry/WebSquare-%EC%9B%B9%EC%8A%A4%ED%80%98%EC%96%B4-scwin-%EC%9D%B4%EB%9E%80-%EB%9C%BB-%EC%97%AD%ED%95%A0-%EC%9E%A5%EB%8B%A8%EC%A0%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;관련 자료&lt;/a&gt;를 찾아보며 scwin에 대해 조금 더 자세히 살펴보게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;205&quot; data-start=&quot;125&quot; data-ke-size=&quot;size16&quot;&gt;scwin이 무엇인지, 어떤 역할을 하는 객체인지, 그리고 이를 사용하는 이유와 장점에는 어떤 것들이 있는지 정리해 보겠습니다.&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;scwin&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹스퀘어 5(WebSquare5) 프레임워크에서 화면(XML) 단위로 스크립트 함수와 변수를 관리(메모리 관리) 하기 위해 사용하는 기본 네임스페이스(Namespace) 객체입니다.&lt;/li&gt;
&lt;li&gt;scwin을 사용하지 않으면 모든 변수와 함수가 전역 스코프에 저장되는데, 이는 메모리 효율성이 떨어지며 성능 저하의 원인이 될 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, scwin을 사용하면 namespace 내에서 변수와 함수를 선언하기에 메모리 효율성이 뛰어납니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&amp;nbsp;역할 및 장점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1. 독립적인 네임스페이스 생성을 통해 자원 관리의 효율성을 높여줍니다.&lt;/li&gt;
&lt;li&gt;2. 페이지에서 닫힐 때 자동으로 변수와 함수를 정리하여 memory leak를 방지해 줍니다.&lt;/li&gt;
&lt;li&gt;3. 스코프의 명확화를 통한 가독성 향상과 디버깅이 용이해집니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;View 설정파일&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;server.config.xml
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다국어 처리 및 excel 파일 처리, 파일 upload 기능과 관련한 server 설정을 제어하는 파일&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;client.config.xml
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Websquare component들을 비롯한 구성 요소들에 대한 전반적인 제어를 하는 파일&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;WebSqaure Component&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;$p&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WebSquare에서 제공하는 Util을 활용하기 위한 객체입니다.&lt;/li&gt;
&lt;li&gt;$p.reinitialize(true) 자신이 실행된 화면을 다시 로딩하거나 브라우저를 Refresh 합니다.&lt;/li&gt;
&lt;li&gt;$p.getEventTarget() 이벤트가 발생한 컴포넌트의 정보를 확인할 수 있습니다.&lt;/li&gt;
&lt;li&gt;$p.dynamicCreate()&amp;nbsp; 컴포넌트를 동적으로 생성할 수 있습니다.&lt;/li&gt;
&lt;li&gt;$p.local.setItem(): 브라우저의 LocalStorage에 값을 저장하는 메서드입니다.&lt;/li&gt;
&lt;li&gt;$p.local.getItem(): 브라우저의 LocalStorage에 값을 반환하는 메서드입니다.&lt;/li&gt;
&lt;li&gt;$p.local.removeItem(): 브라우저의 LocalStorage에 값을 삭제하는 메서드입니다.&lt;/li&gt;
&lt;li&gt;$p.getFormattedDate(_date, _format): format에 맞춘 날짜를 반환하는 메서드입니다.&lt;/li&gt;
&lt;li&gt;$p.executeSubmission(&amp;rdquo;submission1&amp;rdquo;): submission 실행합니다.&lt;/li&gt;
&lt;li&gt;JSON.parse: String to JSON (Json 문자열을 Json 객체로 반환합니다.)&lt;/li&gt;
&lt;li&gt;JSON.stringify: Json to String (Json 객체를 Json 문자열로 변환합니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;WebSquare.date&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;toLunar(): 양력 &amp;rarr; 음력으로 반환합니다.&lt;/li&gt;
&lt;li&gt;toSolar(): 음력 &amp;rarr; 양력으로 반환합니다.&lt;/li&gt;
&lt;li&gt;isValidate(): 유효하지 않은 날짜에 대해 얼럿을 표시하고 유효성 확인 결과를 반환할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1779553029764&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 음력 2022년 7월 15일을 양력으로 변환해서 20220812를 반환. 

WebSquare.date.toSolar(20220715);&lt;/code&gt;&lt;/pre&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;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;숨기기: hide()&lt;/li&gt;
&lt;li&gt;보이기: show()&lt;/li&gt;
&lt;li&gt;비활성화: setDisabled()&lt;/li&gt;
&lt;li&gt;이름 반환: getPluginName()&lt;/li&gt;
&lt;li&gt;자식 컴포넌트 반환: getChildRen()&lt;/li&gt;
&lt;li&gt;chooseOption, chooseOptionLabel
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;chooseOptionLabel 미지정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;true: selectbox에서 -선택-으로 표시합니다.&lt;/li&gt;
&lt;li&gt;false: selectbox의 첫 번째 항목을 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;chooseOptionLabel 지정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;true: selectbox에서 지정한 label로 표시합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;component.trigger(&quot;event&quot;): 컴포넌트에 등록된 이벤트를 발생합니다.&lt;/li&gt;
&lt;li&gt;component.bind(&quot;event&quot;): 컴포넌트에 발생된 이벤트를 연결합니다.&lt;/li&gt;
&lt;li&gt;component.unbind(&quot;event&quot;): 컴포넌트에 발생된 이벤트 연결을 해제합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 컴포넌트에 대해서도 알아보았으니, client-server 간에 전달되는 Data를 정의하는 DataCollection과 실제 통신을 담당하는 Submission에 대해서도 설명드리겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DataCollection&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정의
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저에서 메모리에 할당되는 Javascript Data 객체입니다.&lt;/li&gt;
&lt;/ul&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;서버와의 통신을 위한 request, response Data를 정의합니다.&lt;/li&gt;
&lt;li&gt;화면에서 사용하고자 하는 임시 Data를 정의합니다.&lt;/li&gt;
&lt;li&gt;UI 컴포넌트와 연동하여 데이터를 효율적으로 표시하고 조작합니다.&lt;/li&gt;
&lt;li&gt;서버로부터 수신한 단일형(DataMap) 또는 목록형(DataList) 데이터를 웹 브라우저에 출력하는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&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;DataMap : Key와 Value로 이루어진 단건 Data 객체 (단일 데이터 객체)&lt;/li&gt;
&lt;li&gt;DataList : Column과 Value로 이루어진 다건 Data 객체 (다중 데이터 객체)&lt;/li&gt;
&lt;li&gt;LinkedDataList : 정의된 DataList 객체를 참조하여 Filter, Sort를 적용한 다건 Data 객체 (필터링/정렬된 결과 객체)&lt;/li&gt;
&lt;li&gt;AliasDataMap : pageFrame의 Scope 사용한 경우, 자식 페이지에서 부모 페이지의 DataMap을 참조하는 Data 객체&lt;/li&gt;
&lt;li&gt;AliasDataList : pageFrame의 Scope 사용한 경우, 자식 페이지에서 부모 페이지의 DataList을 참조하는 Data 객체&lt;/li&gt;
&lt;/ul&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;각 데이터 객체의 id는 필수값입니다.&lt;/li&gt;
&lt;li&gt;JSON, XML, JSON Array 포맷으로 설정(set)하거나 또는 반환(get) 받을 수 있습니다. (LinkedDataList는 반환만 가능합니다. )&lt;/li&gt;
&lt;li&gt;초기값을 정의할 수 있습니다. (LinkedDataList 제외 )&lt;/li&gt;
&lt;li&gt;동적(Script)으로 생성할 수 있습니다.&lt;/li&gt;
&lt;li&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;h3 data-ke-size=&quot;size23&quot;&gt;DataCollection 제공 메서드&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;setJson() : DataList의 데이터를 Json 형식으로 설정합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;setJson(data, true): append (기존에 있는 것에 새롭게 데이터를 추가합니다.)&lt;/li&gt;
&lt;li&gt;setJson(data, false): newly set (새롭게 데이터를 설정합니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;setArray(): 전체 데이터를 '1차원 Array 형식 + 컬럼 정보'으로 설정합니다.&lt;/li&gt;
&lt;li&gt;setData(): 전체 데이터를 '1차원 Array 형식'으로 설정합니다.&lt;/li&gt;
&lt;li&gt;setXML(): 전체 데이터를 XML 형식으로 설정합니다.&lt;/li&gt;
&lt;li&gt;insertRow(), removeRows(): 행 추가 및 삭제합니다.&lt;/li&gt;
&lt;li&gt;dataList.insertJSON(0, json1, {&quot;fixScroll&quot;: true}): 스크롤을 고정한 채로 데이터를 추가합니다.&lt;/li&gt;
&lt;li&gt;getAll(): 전체 데이터를 반환합니다.&lt;/li&gt;
&lt;li&gt;getRowCount(), getTotalRow(): 행 수를 반환합니다.&lt;/li&gt;
&lt;li&gt;getAllJson(): DataList 데이터를 JSON 형식으로 반환합니다. (행 상태(rowStatus) 제외)&lt;/li&gt;
&lt;li&gt;getTotalCol(): 컬럼 수를 반환합니다.&lt;/li&gt;
&lt;li&gt;setColumnFilter(): 필터링을 적용합니다.&lt;/li&gt;
&lt;li&gt;clearFilter(): 모든 필터를 해제합니다.&lt;/li&gt;
&lt;li&gt;removeColumnFilter(&amp;rdquo;column&quot;): 특정 컬럼에 적용된 필터를 해제합니다.&lt;/li&gt;
&lt;li&gt;keepDataType=&quot;true&quot; (DataList), dataType=&quot;number&quot; (dataList.column - col1 &amp;amp; col2) + typeof 연산자: DataList에서 string 타입 데이터를 number 타입으로 설정합니다.&lt;/li&gt;
&lt;li&gt;getModifiedKey(): 변경된 key 목록을 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Submission&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-05-24 오전 1.23.37.png&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yQ2MB/dJMcadhQnQR/xScKMkHCm7zbdRZbkh0MiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yQ2MB/dJMcadhQnQR/xScKMkHCm7zbdRZbkh0MiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yQ2MB/dJMcadhQnQR/xScKMkHCm7zbdRZbkh0MiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyQ2MB%2FdJMcadhQnQR%2FxScKMkHCm7zbdRZbkh0MiK%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;534&quot; height=&quot;329&quot; data-filename=&quot;스크린샷 2026-05-24 오전 1.23.37.png&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;522&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;Submission은 서버와의 데이터 통신을 위한 모듈로서, AJAX로 구현되어 있습니다.&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;특징&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;서버와의 통신용 전송/수신 데이터를 DataCollection 형식으로 정의해야 합니다.&lt;/li&gt;
&lt;li&gt;화면의 전환 없이 데이터만 전송합니다.&lt;/li&gt;
&lt;li&gt;동기/비동기 통신 선택이 가능합니다.&lt;/li&gt;
&lt;li&gt;통신의 전/후 처리를 위해 각 이벤트에 호출할 Function을 정의할 수 있습니다.&lt;/li&gt;
&lt;li&gt;Script를 통해 동적 Submission 생성 가능합니다.&lt;/li&gt;
&lt;li&gt;ID를 제외한 속성은 동적 변경이 가능합니다.&lt;/li&gt;
&lt;li&gt;Request, Response데이터는 DataCollection에 정의한 데이터 객체와 연동합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Request 데이터는 reference에 정의합니다. (소스에서는 ref 속성으로 표기됩니다.)&lt;/li&gt;
&lt;li&gt;Request Response는 target에 정의합니다.&lt;/li&gt;
&lt;/ul&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;용도별로 Submission을 등록하는 것을 권장합니다. (유지보수 시 파악 용이)&lt;/li&gt;
&lt;li&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;(1) 화면 UI 작성&lt;/li&gt;
&lt;li&gt;(2) 서버와 주고받는 데이터 형식 정의 (DataCollection 정의)&lt;/li&gt;
&lt;li&gt;(3) 화면 UI와 DataCollection 바인딩&lt;/li&gt;
&lt;li&gt;(4) Submission 생성&lt;/li&gt;
&lt;li&gt;(5) Submission 실행 이벤트 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Submission 생성 방법&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 모듈 패널에서 생성&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-05-24 오전 1.28.14.png&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QFbpR/dJMcageyLZd/1BigkMfeP3VEQQLkcS4X1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QFbpR/dJMcageyLZd/1BigkMfeP3VEQQLkcS4X1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QFbpR/dJMcageyLZd/1BigkMfeP3VEQQLkcS4X1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQFbpR%2FdJMcageyLZd%2F1BigkMfeP3VEQQLkcS4X1k%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;529&quot; height=&quot;243&quot; data-filename=&quot;스크린샷 2026-05-24 오전 1.28.14.png&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&amp;nbsp;패널의 좌측에서&amp;nbsp;&lt;b&gt;서브미션&lt;/b&gt;&amp;nbsp;메뉴를 선택합니다.&lt;/li&gt;
&lt;li&gt;우측의&amp;nbsp;&lt;b&gt;서브미션&lt;/b&gt;&amp;nbsp;항목에 있는&amp;nbsp;&lt;b&gt;서브미션 추가&lt;/b&gt;&amp;nbsp;아이콘을 클릭합니다.&lt;/li&gt;
&lt;li&gt;새로 추가된 행에 Submission이 자동 생성되는 것을 확인합니다.&lt;/li&gt;
&lt;li&gt;새로 생성된 Submission의 ID 앞에 표시되는 버튼을 클릭한 후, 아래의 각 탭에서 Submission의 속성과 바인딩 정보를 정의하고 이벤트를 추가합니다.
&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;&amp;nbsp;: ID를 포함한 Submission의 속성을 설정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;바인딩&lt;/b&gt;&amp;nbsp;: 서버로 전송(&lt;b&gt;ref&lt;/b&gt;)하고 서버로부터 수신(&lt;b&gt;target&lt;/b&gt;)할 데이터 형식을 선택합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이벤트&lt;/b&gt;&amp;nbsp;: 우측의&amp;nbsp;&lt;b&gt;스크립트&lt;/b&gt;&amp;nbsp;버튼을 클릭하여 Submission 관련 이벤트를 추가합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-05-24 오전 1.29.36.png&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWwsQS/dJMcahYOSv8/nFFtsY1u7qORdl2HGEShd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWwsQS/dJMcahYOSv8/nFFtsY1u7qORdl2HGEShd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWwsQS/dJMcahYOSv8/nFFtsY1u7qORdl2HGEShd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWwsQS%2FdJMcahYOSv8%2FnFFtsY1u7qORdl2HGEShd0%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;520&quot; height=&quot;237&quot; data-filename=&quot;스크린샷 2026-05-24 오전 1.29.36.png&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&amp;nbsp;패널의&amp;nbsp;&lt;b&gt;모듈&lt;/b&gt;&amp;nbsp;메뉴에서&amp;nbsp;&lt;b&gt;Submission&lt;/b&gt;을 우클릭합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Submission&amp;nbsp;추가&lt;/b&gt;&amp;nbsp;메뉴를 선택합니다.&lt;/li&gt;
&lt;li&gt;Submission 추가 창이 표시되면 아래 항목을 정의합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GridView&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WebSquare에서 그리드뷰는 웹 개발에 가장 비중 있게 사용되는 컴포넌트입니다.&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;GridView 속성&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;autoFit(그리드 너비 조정(option: allColumn), autoFitMinWidth, fixedColumn&lt;/li&gt;
&lt;li&gt;readOnly&lt;/li&gt;
&lt;li&gt;columnMove (컬럼 이동)&lt;/li&gt;
&lt;li&gt;sortable (헤더를 더블클릭 했을 때의 데이터 정렬)&lt;/li&gt;
&lt;li&gt;useFilter (항목별 필터 기능)&lt;/li&gt;
&lt;li&gt;rowNumVisible (그리드 순번 지정)&lt;/li&gt;
&lt;li&gt;rowStatusVisible (데이터 수정 시 상태값 변경) (수정 시 rowStatus : U, 수정 안 할 시 rowStatus: R)&lt;/li&gt;
&lt;li&gt;editModeEvent (default: doubleClick)&lt;/li&gt;
&lt;li&gt;keyMoveEditMode (Tab으로 다음 항목 edit)&lt;/li&gt;
&lt;li&gt;focusFlow (linear, Tab으로 항목 이동 후 아래항목으로 떨어뜨리는 속성)&lt;/li&gt;
&lt;li&gt;visibleRowNum (한 화면에서 보여줄 Row 개수 설정, 일반적으로 10~15개를 설정합니다)&lt;/li&gt;
&lt;li&gt;inputType (입력타입 설정)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;select 선택 시, bindItemSet 설정 (NodeSet, Label, Value 설정)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GridView 크기 설정&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;header 선택 후 드래그로 너비 조정이 가능합니다.&lt;/li&gt;
&lt;li&gt;GridView 더블클릭 후 각 column 별 width를 적용합니다.&lt;/li&gt;
&lt;li&gt;autoFit 설정 (autoFit + autoFitMinWidth: 데이터 확인을 위해 함께 사용하는 것을 권장)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GridView API&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;oncellclick (셀을 클릭했을 때의 이벤트)&lt;/li&gt;
&lt;li&gt;oncelldblclick (셀을 더블클릭했을 때의 이벤트)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1779553950774&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;scwin.btnInsert_onclick = function(e) {
	var focusIdx = ui_memberList.getFocusedRowIndex();
	dc_userInfoList.insertRow(focusIdx);
}

scwin.btnDelete_onclick = function(e) {
	var focusIdx = ui_memberList.getFocusedRowIndex();
	dc_userInfoList.deleteRow(focusIdx);
}

scwin.btnRemove_onclick = function(e) {
	var focusIdx = ui_memberList.getFocusedRowIndex();
	var obj = dc_userInfoList.removeRow(focusIdx);
}

scwin.btnDeleteRows_onclick = function(e) {
	var focusIdx = ui_memberList.getCheckedIndex(&quot;CHK&quot;);
	dc_userInfoList.deleteRows(focusIdx);
}

scwin.btnRemoveRows_onclick = function(e) {
	var focusIdx = ui_memberList.getCheckedIndex(&quot;CHK&quot;);
	var obj = dc_userInfoList.removeRows(focusIdx);
}

scwin.btninit_onclick = function(e) {
	// var obj = dc_userInfoList.removeAll();
	
	dc_userInfoList.setData([]);
}

// excel download
scwin.btnExcelDown_onclick = function(e) {
	ui_memberList.advancedExcelDownload([]);
}

// excel upload
scwin.btnExcelUpload_onclick = function(e) {
	var options = {};
	options.headerExist = &quot;1&quot;;
	options.type = &quot;1&quot;;
	ui_memberList.advancedExcelUpload(options);
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Frontend</category>
      <category>DataCollection</category>
      <category>GridView</category>
      <category>scwin</category>
      <category>submission</category>
      <category>websquare</category>
      <category>websquare5</category>
      <category>웹스퀘어</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/443</guid>
      <comments>https://daily1313.tistory.com/entry/WebSquare-%EC%9B%B9%EC%8A%A4%ED%80%98%EC%96%B4%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90#entry443comment</comments>
      <pubDate>Sun, 24 May 2026 01:39:18 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] Amazon Web Services Solutions Architect - Associate (AWS-SAA-C03) 자격증 합격 후기 및 공부법 (전공자)</title>
      <link>https://daily1313.tistory.com/entry/AWS-Amazon-Web-Services-Solutions-Architect-Associate-AWS-SAA-C03-%EC%9E%90%EA%B2%A9%EC%A6%9D-%ED%95%A9%EA%B2%A9-%ED%9B%84%EA%B8%B0-%EB%B0%8F-%EA%B3%B5%EB%B6%80%EB%B2%95</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 AWS 자격증 합격 후기로 포스팅을 시작하려고 합니다.&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;사전 지식이 많은 상태는 아니였으며, AWS 서비스에 대해서 학부 시절에 간단하게 사용해본 수준이였습니다. (EC2, S3, RDS, Route53, ACM)&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;시험 후기에 앞서 AWS-SAA-C03 자격증이 어떤 자격증인지 간단하게 설명드리겠습니다.&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;AWS-SAA&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-26 오전 9.39.40.png&quot; data-origin-width=&quot;1982&quot; data-origin-height=&quot;1206&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caqc9I/dJMcacXbaYe/TxZwkuIBhAbNKK1u4Bcq2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caqc9I/dJMcacXbaYe/TxZwkuIBhAbNKK1u4Bcq2k/img.png&quot; data-alt=&quot;https://aws.amazon.com/ko/certification/certified-solutions-architect-associate/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caqc9I/dJMcacXbaYe/TxZwkuIBhAbNKK1u4Bcq2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcaqc9I%2FdJMcacXbaYe%2FTxZwkuIBhAbNKK1u4Bcq2k%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;692&quot; height=&quot;421&quot; data-filename=&quot;스크린샷 2026-04-26 오전 9.39.40.png&quot; data-origin-width=&quot;1982&quot; data-origin-height=&quot;1206&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://aws.amazon.com/ko/certification/certified-solutions-architect-associate/&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;AWS 서비스 전반에서 기술 지식과 기술 능력을 검증하는 시험으로서 비용 및 성능 최적화 솔루션 설계에 중점을 둡니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 4지선다 (or 5지선다) 에서 최적화된(비용 or 성능) 솔루션을 찾는다거나, 사용되는 AWS 서비스는 어떤건지 요구하는 시험입니다.&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;참고로 저는 어제 응시했던 시험에서 6지선다에 3개 고르는 문제가 2개 나와서 조금 당황했던 것 같습니다. 물론 채점은 되었는지 안되었는지 모릅니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;AWS-SAA 시험 시간 및 내용&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAJHbA/dJMcajvd9de/5Khw0ZllEvaiK9mkaliBDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAJHbA/dJMcajvd9de/5Khw0ZllEvaiK9mkaliBDK/img.png&quot; data-origin-width=&quot;1892&quot; data-origin-height=&quot;1076&quot; data-is-animation=&quot;false&quot; data-filename=&quot;스크린샷 2026-04-26 오전 9.44.47.png&quot; style=&quot;width: 47.2028%; margin-right: 10px;&quot; data-widthpercent=&quot;47.76&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAJHbA/dJMcajvd9de/5Khw0ZllEvaiK9mkaliBDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAJHbA%2FdJMcajvd9de%2F5Khw0ZllEvaiK9mkaliBDK%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;1892&quot; height=&quot;1076&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOFUzI/dJMcacJGHU1/LwQeb2Yxk2BzdPZXwFVhk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOFUzI/dJMcacJGHU1/LwQeb2Yxk2BzdPZXwFVhk0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1608&quot; data-origin-height=&quot;836&quot; data-filename=&quot;스크린샷 2026-04-19 오후 11.07.11.png&quot; style=&quot;width: 51.6344%;&quot; data-widthpercent=&quot;52.24&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOFUzI/dJMcacJGHU1/LwQeb2Yxk2BzdPZXwFVhk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOFUzI%2FdJMcacJGHU1%2FLwQeb2Yxk2BzdPZXwFVhk0%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;1608&quot; height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;시험 방식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시험 시간은 130분이며, 65개 문항 중에서 50개만 채점이 되며 15개는 더미 문제로 채점이 되지 않습니다. 저의 경우는 한국어로 시험 신청을 하였으며, 오프라인으로 시험을 응시하였습니다.&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;집에서 간편하게 온라인으로 응시하는 것도 좋지만, 주변에 있는 책이나 사소한 물건도 배치하지 못한다는 소리를 듣고 근처에 존재하는 시험장에서 시험을 보는게 좋다고 생각했기에 오프라인으로 지원하였습니다. (가산 팀스카이, 18층)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AWS-SAA 공부법 (2주 - 1주 개념정리, 1주 덤프 문제풀이)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 이번 시험을 응시한 이유는 AWS에 관한 이론적 지식을 더 깊게 공부하기 위한 목적이 컸습니다. 그래서 단순하게 ExamTopic에서 더미 문제만 푸는 것이 아닌, 키워드별로 개념정리를 우선으로 하였습니다. &lt;a href=&quot;https://velog.io/@gagaeun/series/AWS-SAA-TIL&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;블로그에 개념을 상세하게 정리해두신 분의 자료&lt;/a&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;개인적인 생각이고, 사람마다 기본지식이 다르지만 AWS를 깊게 다뤄보지 않은 사람이 사전 지식 없이 문제만 푼다면 조금 어려울 수 있다고 생각합니다. 나중에 이론을 기반으로 나중에 실서비스에 적용해야 하는 사람이라면, 개념정리하는 시간을 최소 1주일 정도 잡는 것을 추천드립니다. 문제를 바라보는 시야가 넓어지며 덤프도 속도감 있게 풀 수 있을 것 같습니다.&amp;nbsp;&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bseFtr/dJMcabDZk5U/0Eoop3AvtbegnCsTy9cVM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bseFtr/dJMcabDZk5U/0Eoop3AvtbegnCsTy9cVM1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;1354&quot; data-filename=&quot;스크린샷 2026-04-26 오전 9.55.26.png&quot; width=&quot;440&quot; height=&quot;643&quot; style=&quot;width: 39.5372%; margin-right: 10px;&quot; data-widthpercent=&quot;40&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bseFtr/dJMcabDZk5U/0Eoop3AvtbegnCsTy9cVM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbseFtr%2FdJMcabDZk5U%2F0Eoop3AvtbegnCsTy9cVM1%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;926&quot; height=&quot;1354&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxmD5W/dJMcadBIxVK/akEKwg4w819ZkjCxAPBTI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxmD5W/dJMcadBIxVK/akEKwg4w819ZkjCxAPBTI0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;932&quot; data-filename=&quot;스크린샷 2026-04-26 오전 10.01.27.png&quot; style=&quot;width: 59.3001%;&quot; data-widthpercent=&quot;60&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxmD5W/dJMcadBIxVK/akEKwg4w819ZkjCxAPBTI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxmD5W%2FdJMcadBIxVK%2FakEKwg4w819ZkjCxAPBTI0%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;956&quot; height=&quot;932&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Notion에 키워드별로 개념 정리&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;개념 정리를 마친 후 ExamTopic과 별도 덤프를 통해서 문제를 대략 700문제 정도 풀었습니다. 헷갈리거나 틀린 문제들은 별도로 정리하면서 반복해서 복습하였습니다. 덤프는 2회독을 하였습니다. (1회독때는 빠르게 풀고 틀린 부분을 체크하고, 2회독때는 틀렸던 문제들을 내가 잘못 체크한 선지와 비교하면서 확실하게 저의 것으로 만들어 갔습니다.)&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;iam, ec2, s3, cloudfront, db, scalaility &amp;amp; ha, sqs, sns, kms, vpc, nat..&lt;/li&gt;
&lt;li&gt;이 외에도 중요한 키워드가 있을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AWS-SAA 체감 난이도&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;금액에 대한 압박감과 더불어 문제당 20점도 한 몫 했던 것 같습니다. (채점되는 문제들 중 15개 이상 틀리면 탈락입니다.)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덤프와 동일하게 50% 정도 나왔으며, 30%는 조금 말장난을 섞어서 함정문제처럼 내었으며, 20%는 아예 완전한 새로운 문제였습니다. (실제로 20%중에서 확실하게 모른다고 생각되는 문제는 찍었습니다.)&amp;nbsp;&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;합격&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오전 12시쯤 시험에 응시하고 밤 9시에 다음과 같은 메일이 도착하였습니다. 시험을 응시하고 조금 애매하다고 생각되어서 걱정을 했었는데 한편으로 다행스럽게 생각이 듭니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-26 오전 8.59.13.png&quot; data-origin-width=&quot;1422&quot; data-origin-height=&quot;948&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cENyIr/dJMcahYuOYb/Py2638lNDzXRb54odIEYs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cENyIr/dJMcahYuOYb/Py2638lNDzXRb54odIEYs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cENyIr/dJMcahYuOYb/Py2638lNDzXRb54odIEYs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcENyIr%2FdJMcahYuOYb%2FPy2638lNDzXRb54odIEYs1%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;549&quot; height=&quot;366&quot; data-filename=&quot;스크린샷 2026-04-26 오전 8.59.13.png&quot; data-origin-width=&quot;1422&quot; data-origin-height=&quot;948&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&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQRLSg/dJMcabcSQSG/1UO1SSKMh56BiIhh4dfskk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQRLSg/dJMcabcSQSG/1UO1SSKMh56BiIhh4dfskk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;748&quot; data-filename=&quot;KakaoTalk_Photo_2026-04-26-10-24-08.png&quot; style=&quot;width: 52.3041%; margin-right: 10px;&quot; data-widthpercent=&quot;52.92&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQRLSg/dJMcabcSQSG/1UO1SSKMh56BiIhh4dfskk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQRLSg%2FdJMcabcSQSG%2F1UO1SSKMh56BiIhh4dfskk%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;864&quot; height=&quot;748&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MGr50/dJMcafzDlOf/OYhi7NIZ3KbkhYqChrfQDk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MGr50/dJMcafzDlOf/OYhi7NIZ3KbkhYqChrfQDk/img.jpg&quot; data-origin-width=&quot;2975&quot; data-origin-height=&quot;2895&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2026-04-26-09-06-47.jpeg&quot; data-widthpercent=&quot;47.08&quot; style=&quot;width: 46.5331%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MGr50/dJMcafzDlOf/OYhi7NIZ3KbkhYqChrfQDk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMGr50%2FdJMcafzDlOf%2FOYhi7NIZ3KbkhYqChrfQDk%2Fimg.jpg&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;2975&quot; height=&quot;2895&quot;/&gt;&lt;/span&gt;&lt;/div&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;이상으로 포스팅 마치겠습니다.&lt;/p&gt;</description>
      <category>Certificate</category>
      <category>Amazon Web Services Solutions Architect - Associate</category>
      <category>aws-saa</category>
      <category>AWS-SAA 자격증 공부법</category>
      <category>AWS-SAA 자격증 취득</category>
      <category>AWS-SAA-C03 후기</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/442</guid>
      <comments>https://daily1313.tistory.com/entry/AWS-Amazon-Web-Services-Solutions-Architect-Associate-AWS-SAA-C03-%EC%9E%90%EA%B2%A9%EC%A6%9D-%ED%95%A9%EA%B2%A9-%ED%9B%84%EA%B8%B0-%EB%B0%8F-%EA%B3%B5%EB%B6%80%EB%B2%95#entry442comment</comments>
      <pubDate>Sun, 26 Apr 2026 10:26:36 +0900</pubDate>
    </item>
    <item>
      <title>[KMI 여의도 건강검진] 채용검진 후기 (대기시간, 검사항목, 꿀팁)</title>
      <link>https://daily1313.tistory.com/entry/KMI-%EC%97%AC%EC%9D%98%EB%8F%84-%EA%B1%B4%EA%B0%95%EA%B2%80%EC%A7%84-%EC%B1%84%EC%9A%A9%EA%B2%80%EC%A7%84-%ED%9B%84%EA%B8%B0-%EB%8C%80%EA%B8%B0%EC%8B%9C%EA%B0%84-%EA%B2%80%EC%82%AC%ED%95%AD%EB%AA%A9-%EA%BF%80%ED%8C%81</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 곧 입사할 회사의 채용검진을 위해 &lt;b&gt;KMI한국의학연구소 여의도검진센터&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;여의도는 사람이 많을 것 같아서 광화문으로 가려고 했지만, 다른 사람들의 블로그 후기를 보니 여의도가 많은 사람들에 비해 최소 시간으로 검진을 받을 수 있을 것 같아서 해당 장소로 이동했습니다. 실제로 사람들은 많았지만 빠르게 진행되었습니다.(전체 검진 시간:1시간 이내)&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;저는 사전에 따로 예약은 하지 않고, 전화로 문의하니 주의사항과 안내사항을 문자로 받을 수 있었습니다. 반드시 주의사항을 숙지하여 전날 9시간 이상 공복을 유지하셔야 합니다. 저 또한 전날 간단히 저녁 식사 후 8시 이후부터 공복 상태를 유지하였습니다.&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2026-04-18-15-38-50 001.png&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;1334&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HYWrS/dJMcaakGoff/oGvgScMs95aehsVa5jIgrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HYWrS/dJMcaakGoff/oGvgScMs95aehsVa5jIgrk/img.png&quot; data-alt=&quot;민증 꼭 챙겨가셔야 합니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HYWrS/dJMcaakGoff/oGvgScMs95aehsVa5jIgrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHYWrS%2FdJMcaakGoff%2FoGvgScMs95aehsVa5jIgrk%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;402&quot; height=&quot;715&quot; data-filename=&quot;KakaoTalk_Photo_2026-04-18-15-38-50 001.png&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;1334&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;저는 8시 30분쯤 여의도역에 도착해서 빠르게 17층으로 올라갔습니다. 아니다 다를까 수많은 사람들이 엘리베이터를 기다리고 있음을 확인하였습니다.&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgJTbU/dJMcad2Jep9/jQDiF7Hapafveup2Bs652k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgJTbU/dJMcad2Jep9/jQDiF7Hapafveup2Bs652k/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;KakaoTalk_Photo_2026-04-18-15-38-51 004.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgJTbU/dJMcad2Jep9/jQDiF7Hapafveup2Bs652k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgJTbU%2FdJMcad2Jep9%2FjQDiF7Hapafveup2Bs652k%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PxYN7/dJMcabcNuPF/xKj162NJPQzI5VawKFkMy1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PxYN7/dJMcabcNuPF/xKj162NJPQzI5VawKFkMy1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2026-04-18-15-38-51 003.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PxYN7/dJMcabcNuPF/xKj162NJPQzI5VawKFkMy1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPxYN7%2FdJMcabcNuPF%2FxKj162NJPQzI5VawKFkMy1%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;검진장소: BNK 금융타워 17층&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;도착하고 17층으로 가서, 순서표를 뽑은 이후 카운터에서 신분증과 검진안내 메일을 안내원분께 보여드렸습니다. 이후 키를 받고 환복 후에 검진을 속전속결로 진행하게 됩니다. 검진 전에 바깥에서 간단한 문진표를 태블릿으로 작성하게 됩니다.&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;size18&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;/li&gt;
&lt;li&gt;혈액검사&lt;/li&gt;
&lt;li&gt;심전도 검사&lt;/li&gt;
&lt;li&gt;간단한 상담 (질병 유무(고혈압, 당뇨), 약 복용 등..)&lt;/li&gt;
&lt;li&gt;시력, 청력 검사&lt;/li&gt;
&lt;li&gt;X-ray&lt;/li&gt;
&lt;li&gt;흉부방사선촬영&lt;/li&gt;
&lt;li&gt;요검사 (마무리)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2026-04-18-15-38-51 002.jpeg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfnVXu/dJMcacW5Eyc/TNXynukWCDAIEErWwPlLVK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfnVXu/dJMcacW5Eyc/TNXynukWCDAIEErWwPlLVK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfnVXu/dJMcacW5Eyc/TNXynukWCDAIEErWwPlLVK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfnVXu%2FdJMcacW5Eyc%2FTNXynukWCDAIEErWwPlLVK%2Fimg.jpg&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;455&quot; height=&quot;607&quot; data-filename=&quot;KakaoTalk_Photo_2026-04-18-15-38-51 002.jpeg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&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;검진 과정에서 각 검사실마다 대기 및 진료 시 키(카드)를 찍는 시스템이 신기했습니다. 대부분의 직원분들이 친절히 응대해 줘서 어렵지 않게 진행할 수 있었던 것 같습니다. 검진 항목별 대기 시간은 5분 이내로 빠르게 진행되었던 것 같습니다. 검진 항목의 대기 시간도 대부분 5분 이내로 짧은 편이었고, 안내받은 번호를 잘 확인하면서 카드를 찍고 이동하면 훨씬 수월하게 진행할 수 있습니다.&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;</description>
      <category>Daily</category>
      <category>KMI 건강검진</category>
      <category>KMI 한국의학연구소</category>
      <category>여의도 KMI 건강검진</category>
      <category>여의도 KMI 한국의학연구소 채용검진 후기</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/441</guid>
      <comments>https://daily1313.tistory.com/entry/KMI-%EC%97%AC%EC%9D%98%EB%8F%84-%EA%B1%B4%EA%B0%95%EA%B2%80%EC%A7%84-%EC%B1%84%EC%9A%A9%EA%B2%80%EC%A7%84-%ED%9B%84%EA%B8%B0-%EB%8C%80%EA%B8%B0%EC%8B%9C%EA%B0%84-%EA%B2%80%EC%82%AC%ED%95%AD%EB%AA%A9-%EA%BF%80%ED%8C%81#entry441comment</comments>
      <pubDate>Sat, 18 Apr 2026 16:23:26 +0900</pubDate>
    </item>
    <item>
      <title>[Oracle] CTE를 활용한 계층 쿼리 구현 (재귀 CTE vs CONNECT BY)</title>
      <link>https://daily1313.tistory.com/entry/Oracle-CTE%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EA%B3%84%EC%B8%B5-%EC%BF%BC%EB%A6%AC-%EA%B5%AC%ED%98%84-%EC%9E%AC%EA%B7%80-CTE-vs-CONNECT-BY</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-05 오후 6.10.55.png&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wlRtQ/dJMcag57rY4/S6fQ2atN0bzCrypWLVicT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wlRtQ/dJMcag57rY4/S6fQ2atN0bzCrypWLVicT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wlRtQ/dJMcag57rY4/S6fQ2atN0bzCrypWLVicT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwlRtQ%2FdJMcag57rY4%2FS6fQ2atN0bzCrypWLVicT1%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;330&quot; height=&quot;252&quot; data-filename=&quot;스크린샷 2026-04-05 오후 6.10.55.png&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;252&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;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 WITH CTE 구문을 활용하여 조직도 쿼리를 작성해 볼 예정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle에서는 사전에 미리 WITH문을 통한 결과집합을 만들어두고, 임시 테이블을 다시 사용할 수 있는 기능을 제공하는데, 해당 기능 덕분에 재사용성을 높일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계층 쿼리를 작성하기 전에, CTE 구문을 먼저 설명드리겠습니다.&amp;nbsp;&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;CTE (Common Table Expression)&lt;/h2&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;CTE(Common Table Expression)는 크게 두 가지 형태로 구분됩니다.&amp;nbsp;
&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: #333333; text-align: start;&quot;&gt;하나는 임시 테이블처럼 활용되는&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;&quot;&gt;일반 CTE&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이고,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;다른 하나는 계층 구조나 반복적인 데이터를 처리하기 위한&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;&quot;&gt;재귀 CTE&lt;/span&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;/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;h3 data-ke-size=&quot;size23&quot;&gt;일반 CTE&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;with 키워드로 시작합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1775380916457&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WITH 임시테이블명 AS (
    SELECT 쿼리
)
SELECT * FROM 임시테이블명;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;재귀 CTE&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자기 자신을 참조하는 특별한 형태의 CTE입니다.&lt;/li&gt;
&lt;li&gt;조직도, 카테고리 트리, 경로 찾기 같은 계층 구조 데이터를 다룰 때 필수적입니다.&lt;/li&gt;
&lt;li&gt;재귀 쿼리는 재귀의 시작점인 &lt;b&gt;&lt;u&gt;앵커 멤버(Anchor Member)&lt;/u&gt;와&lt;/b&gt;,&amp;nbsp; 이전 결과를 참조하여 다음 단계를 생성하는 &lt;b&gt;&lt;u&gt;재귀 멤버(Recursive Member)&lt;/u&gt;로 구성됩니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1775380944132&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WITH RECURSIVE 테이블명 AS (
    -- 기본 케이스 (시작점)
    SELECT 초기 데이터
    UNION ALL
    -- 재귀 케이스 (반복)
    SELECT ... FROM 테이블명 WHERE 조건
)
SELECT * FROM 테이블명;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CTE vs SubQuery&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;CTE는 WITH 절을 사용하여 임시 테이블처럼 쿼리를 정의해 재사용성과 가독성을 높일 수 있으며 재귀 쿼리를 지원합니다. 반면 서브쿼리는 SELECT 내부에서 사용되는 단일 쿼리로 재사용이 어렵고 재귀 처리는 지원하지 않습니다.&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;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;둘 다 쿼리 내부에서 임시 결과 집합을 만들어 사용&lt;/b&gt;&lt;/u&gt;하는 방법입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;차이점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CTE&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WITH 절을 사용하여 &lt;u&gt;&lt;b&gt;임시 테이블처럼 이름을 붙여 사용하는 쿼리&lt;/b&gt;&lt;/u&gt;입니다&lt;b&gt;.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;임시 테이블처럼 이름을 사용할 수 있으며 재귀 처리가 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SubQuery&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼리 안에 포함된 select로서 select 내부에서 사용할 수 있으며 재귀처리가 불가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;/td&gt;
&lt;td&gt;CTE&lt;/td&gt;
&lt;td&gt;서브쿼리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;정의 위치&lt;/td&gt;
&lt;td&gt;WITH 절&lt;/td&gt;
&lt;td&gt;SELECT 내부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&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;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&gt;불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&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;/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;&amp;nbsp;&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;실습 환경 구축&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 가볍게 CTE, 재귀 CTE 쿼리 테스트를 위해 docker 컨테이너 기반으로 oracle db를 구축하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;docker pull &lt;a href=&quot;http://container-registry.oracle.com/database/free&quot;&gt;container-registry.oracle.com/database/free&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;docker run &amp;mdash;name oracle -p 1521:1521 -p 5500:5500 -e ORACLE_PWD=password -e INIT_SGA_SIZE=3000 -e INIT_PGA_SIZE=1000 -d &lt;a href=&quot;http://container-registry.oracle.com/database/free:latest&quot;&gt;container-registry.oracle.com/database/free:latest&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-05 오후 6.38.33.png&quot; data-origin-width=&quot;1850&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LbzVb/dJMcaiix7BP/DGH0fxa9J92wUmFgAEhd20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LbzVb/dJMcaiix7BP/DGH0fxa9J92wUmFgAEhd20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LbzVb/dJMcaiix7BP/DGH0fxa9J92wUmFgAEhd20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLbzVb%2FdJMcaiix7BP%2FDGH0fxa9J92wUmFgAEhd20%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;1850&quot; height=&quot;382&quot; data-filename=&quot;스크린샷 2026-04-05 오후 6.38.33.png&quot; data-origin-width=&quot;1850&quot; data-origin-height=&quot;382&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;일반 CTE&amp;nbsp;&lt;/h3&gt;
&lt;pre id=&quot;code_1775381991845&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 부서 테이블 생성
CREATE TABLE departments (
    dept_id INT PRIMARY KEY,
    dept_name VARCHAR(50)
);

-- 직원 테이블 생성
CREATE TABLE employees (
    emp_id INT PRIMARY KEY,
    emp_name VARCHAR(50),
    dept_id INT,
    salary INT,
    manager_id INT
);

-- 샘플 데이터 입력
INSERT INTO departments VALUES 
(1, '개발팀'), (2, '디자인팀'), (3, '기획팀');

INSERT INTO employees VALUES 
(1, '김대표', 1, 10000000, NULL),
(2, '박팀장', 1, 7000000, 1),
(3, '이선임', 1, 5000000, 2),
(4, '최주임', 1, 4000000, 2),
(5, '정대리', 2, 4500000, 1);

-- CTE를 사용한 쿼리
WITH dept_avg AS (
    SELECT 
        dept_id,
        AVG(salary) as avg_salary
    FROM employees
    GROUP BY dept_id
)
SELECT 
    e.emp_name,
    e.salary,
    d.avg_salary,
    CASE 
        WHEN e.salary &amp;gt; d.avg_salary THEN '평균 이상'
        ELSE '평균 이하'
    END as salary_level
FROM employees e
JOIN dept_avg d ON e.dept_id = d.dept_id;&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;다음과 같이 WITH 구문을 통한 CTE를 통해 임시 테이블을 생성해 두고, 부서별 평균 급여를 보여주는 쿼리를 작성해 보았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-05 오후 6.41.45.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLpkEs/dJMcagdZiNu/OIqC10i3B8YkgoE2ku9pQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLpkEs/dJMcagdZiNu/OIqC10i3B8YkgoE2ku9pQ0/img.png&quot; data-alt=&quot;CTE를 활용한 부서별 평균 급여 집계 쿼리 테스트 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLpkEs/dJMcagdZiNu/OIqC10i3B8YkgoE2ku9pQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLpkEs%2FdJMcagdZiNu%2FOIqC10i3B8YkgoE2ku9pQ0%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;1268&quot; height=&quot;268&quot; data-filename=&quot;스크린샷 2026-04-05 오후 6.41.45.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CTE를 활용한 부서별 평균 급여 집계 쿼리 테스트 결과&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;재귀 CTE&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Oracle&lt;/h4&gt;
&lt;pre id=&quot;code_1775382862108&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 김대표(emp_id=1) 아래의 모든 직원 조회
WITH org_tree (emp_id, emp_name, manager_id, lvl, path) AS (
    -- 앵커 멤버: 시작점 (김대표)
    SELECT
        emp_id,
        emp_name,
        manager_id,
        1,
        CAST(emp_name AS VARCHAR2(200))
    FROM employees
    WHERE emp_id = 1

    UNION ALL

    -- 재귀 멤버: 이전 결과의 하위 직원 찾기
    SELECT
        e.emp_id,
        e.emp_name,
        e.manager_id,
        ot.lvl + 1,
        ot.path || ' &amp;gt; ' || e.emp_name
    FROM employees e
    JOIN org_tree ot
      ON e.manager_id = ot.emp_id
)
SELECT
    emp_id,
    emp_name,
    lvl AS 직급단계,
    path AS 조직경로
FROM org_tree
ORDER BY lvl, emp_id;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;MySQL, MariaDB&lt;/h4&gt;
&lt;pre id=&quot;code_1775382278190&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 김대표(emp_id=1) 아래의 모든 직원 조회
WITH RECURSIVE org_tree AS (
    -- 앵커 멤버: 시작점 (김대표)
    SELECT 
        emp_id,
        emp_name,
        manager_id,
        1 as level,
        CAST(emp_name AS CHAR(200)) as path
    FROM employees
    WHERE emp_id = 1

    UNION ALL // 중복 유지

    -- 재귀 멤버: 이전 결과의 하위 직원 찾기
    SELECT 
        e.emp_id,
        e.emp_name,
        e.manager_id,
        ot.level + 1,
        CONCAT(ot.path, ' &amp;gt; ', e.emp_name)
    FROM employees e
    INNER JOIN org_tree ot ON e.manager_id = ot.emp_id
)
SELECT 
    emp_id,
    emp_name,
    level as 직급단계,
    path as 조직경로
FROM org_tree
ORDER BY level, emp_id;&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;h4 data-ke-size=&quot;size20&quot;&gt;ORACLE (CONNECT BY 활용)&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;CONNECT BY 사용법&lt;/h4&gt;
&lt;pre id=&quot;code_1775383092486&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT 컬럼명
FROM 테이블명
START WITH 루트 조건
CONNECT BY PRIOR 부모_컬럼 = 자식_컬럼;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1775382620519&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT
    emp_id,
    emp_name,
    LEVEL AS 직급단계,
    SUBSTR(SYS_CONNECT_BY_PATH(emp_name, ' &amp;gt; '), 4) AS 조직경로 
FROM employees
START WITH emp_id = 1
CONNECT BY PRIOR emp_id = manager_id
ORDER SIBLINGS BY emp_id;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-05 오후 6.54.55.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t0hp4/dJMcac3GyCg/qJhel8krQ94JJbikPhsQn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t0hp4/dJMcac3GyCg/qJhel8krQ94JJbikPhsQn0/img.png&quot; data-alt=&quot;재귀 CTE 조회 쿼리 테스트 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t0hp4/dJMcac3GyCg/qJhel8krQ94JJbikPhsQn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft0hp4%2FdJMcac3GyCg%2FqJhel8krQ94JJbikPhsQn0%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;1212&quot; height=&quot;272&quot; data-filename=&quot;스크린샷 2026-04-05 오후 6.54.55.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;272&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;재귀 CTE 조회 쿼리 테스트 결과&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;CONNECT BY의 경우는 문법이 간결하고 빠르게 계층 구조를 조회할 수 있지만, Recursive CTE와는 다르게 확장성과 유연성 측면에서는 한계가 존재합니다. Recursive CTE의 경우는 구조는 복잡하지만, 다양한 조건처리가 가능하며 확장성이 뛰어나 복잡한 계층 구조의 데이터를 다룰 때 유용합니다.&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://wikidocs.net/306494&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wikidocs.net/306494&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775383645540&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;10. CTE와 재귀 쿼리&quot; data-og-description=&quot;## 도입 스토리 &amp;quot;김개발 씨, 조직도 조회 기능 구현 좀 부탁해요.&amp;quot; 최기획이 새로운 요구사항을 들고 왔다. &amp;quot;각 직원의 상사를 보여주고, 그 상사의 상사까지... 최상위 임&amp;hellip;&quot; data-og-host=&quot;wikidocs.net&quot; data-og-source-url=&quot;https://wikidocs.net/306494&quot; data-og-url=&quot;https://wikidocs.net/306494&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eeTidc/dJMb82MDF2V/zy8nOQBZpuogUe6wxPqXfK/img.png?width=282&amp;amp;height=366&amp;amp;face=0_0_282_366&quot;&gt;&lt;a href=&quot;https://wikidocs.net/306494&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://wikidocs.net/306494&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eeTidc/dJMb82MDF2V/zy8nOQBZpuogUe6wxPqXfK/img.png?width=282&amp;amp;height=366&amp;amp;face=0_0_282_366');&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;10. CTE와 재귀 쿼리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;## 도입 스토리 &quot;김개발 씨, 조직도 조회 기능 구현 좀 부탁해요.&quot; 최기획이 새로운 요구사항을 들고 왔다. &quot;각 직원의 상사를 보여주고, 그 상사의 상사까지... 최상위 임&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;wikidocs.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Database</category>
      <category>connect by</category>
      <category>oracle</category>
      <category>Recursive CTE</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/440</guid>
      <comments>https://daily1313.tistory.com/entry/Oracle-CTE%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EA%B3%84%EC%B8%B5-%EC%BF%BC%EB%A6%AC-%EA%B5%AC%ED%98%84-%EC%9E%AC%EA%B7%80-CTE-vs-CONNECT-BY#entry440comment</comments>
      <pubDate>Sun, 5 Apr 2026 19:10:10 +0900</pubDate>
    </item>
    <item>
      <title>[Oracle] 오라클 기본문법 및 함수 정리 (vs MySQL)</title>
      <link>https://daily1313.tistory.com/entry/Oracle-%EC%98%A4%EB%9D%BC%ED%81%B4-%EA%B8%B0%EB%B3%B8%EB%AC%B8%EB%B2%95-%EB%B0%8F-%ED%95%A8%EC%88%98-%EC%A0%95%EB%A6%AC-vs-MySQL</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-05 오후 4.53.53.png&quot; data-origin-width=&quot;304&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r8tMY/dJMcafF8Yfc/rrG5NGAPXg4i6vuKoyQHZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r8tMY/dJMcafF8Yfc/rrG5NGAPXg4i6vuKoyQHZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r8tMY/dJMcafF8Yfc/rrG5NGAPXg4i6vuKoyQHZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr8tMY%2FdJMcafF8Yfc%2FrrG5NGAPXg4i6vuKoyQHZ0%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;304&quot; height=&quot;258&quot; data-filename=&quot;스크린샷 2026-04-05 오후 4.53.53.png&quot; data-origin-width=&quot;304&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본 문법 정리&lt;/h2&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. SELECT 1 FROM dual;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;oracle에서 쿼리 테스트를 위해 사용합니다.&lt;/li&gt;
&lt;li&gt;dual: &lt;b&gt;테이블 없이 SELECT를 가능하게 해주는 Oracle 전용 가상 테이블입니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. select distinct column as col from t1.table&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중복 제거 및 별칭을 적용합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. CASE 문&lt;/h3&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;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;조건 충족 여부에 따라 설정한 값을 반환하는 조건을 변환합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;만족하는 조건이 없을 경우에는 null을 리턴합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;조건 내에 언급된 컬럼의 레코드에 대해서&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조건1을 만족 시 출력1&amp;nbsp; &amp;nbsp;..&lt;/li&gt;
&lt;li&gt;조건2를 만족 시 출력2&amp;nbsp; ..&lt;/li&gt;
&lt;li&gt;조건3을 만족 시 출력3&amp;nbsp; ..&lt;/li&gt;
&lt;li&gt;조건N을 만족 시 출력N ..&lt;/li&gt;
&lt;li&gt;모든 조건을 만족하지 않을 시 출력 N+1&lt;/li&gt;
&lt;li&gt;위 모든 내용을 컬럼Z로 출력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1775376332638&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select case when 조건1 then 출력1
            when 조건2 then 출력2
            ...
            when 조건N then 출력N
	        else 출력N+1
       end &quot;출력할컬럼이름Z&quot;
from 테이블이름;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;if문 방식&lt;/h4&gt;
&lt;pre id=&quot;code_1775375836610&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT ename
     , deptno
     , CASE WHEN deptno = '10' (조건문) THEN 'New York' (리턴값)
            WHEN deptno = '20' (조건문) THEN 'Dallas'   (리턴값)
            ELSE 'Unknown'
       END AS loc_name
  FROM emp
 WHERE job = 'MANAGER'&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;switch문 방식&lt;/h4&gt;
&lt;pre id=&quot;code_1775375903009&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT ename
     , deptno
     , CASE deptno 
            WHEN 10 THEN 'New York'
            WHEN 20 THEN 'Dallas'
            ELSE 'Unknown'
       END AS loc_name
  FROM scott.emp
 WHERE job = 'MANAGER'&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. DECODE 함수&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DECODE(expression, search1, result1, search2, result2 &amp;hellip; searchN, resultN, default)&lt;/li&gt;
&lt;li&gt;expression이 search1 &amp;hellip; N과 같을 경우 반환되는 result1 &amp;hellip; N 값&lt;/li&gt;
&lt;li&gt;같지 않을 경우, default를 반환하게 되는데, default를 지정하지 않으면 null을 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Example&lt;/h4&gt;
&lt;pre id=&quot;code_1775376257599&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT DECODE('ORACLE', 'ORACLE', 'TRUE')
FROM DUAL; // true

SELECT DECODE('ORACLE', 'MY SQL', 'TRUE', 'FALSE')
FROM DUAL; // false

SELECT DECODE(3,  1, 'ONE', 2, 'TWO', 3, 'THREE', 'NOT EXISTS')
FROM DUAL; // THREE&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. NULL 관련 함수&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;NVL(주어진 값, 대체 값): 주어진 값이 NULL이면 대체 값을 반환합니다. 아닐 경우 주어진 값 반환&lt;/li&gt;
&lt;li&gt;NVL2(주어진 값, 대체 값1, 대체 값2): 주어진 값이 NULL이 아니면 대체값 1, NULL이면 대체값 2 반환&lt;/li&gt;
&lt;li&gt;COALESCE(주어진 값1, 주어진 값2, ... 주어진 값n): 함수의 인자들 중에서 NULL이 아닌 첫 번째 값 반환&lt;/li&gt;
&lt;li&gt;NULLIF(비교값1, 비교값2): 비교값1, 2가 같으면 NULL을 반환하고, 다를 경우 비교값1을 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. DISTINCT&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;널이 아니면서 고유한 expression의 개수를 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7.&amp;nbsp; 집계함수&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;COUNT(행의 개수), MIN(최솟값), MAX(최댓값), AVG(평균값)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. merge into 구문 (upsert)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Oracle에서 제공하는 구문으로 insert와 update를 동시에 처리할 수 있는 구문입니다.&lt;/li&gt;
&lt;li&gt;merge into
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;merge: 문법으로 테이블에 행을 삽입하거나 수정합니다.&lt;/li&gt;
&lt;li&gt;into: 값을 조작할 테이블 지정합니다.&lt;/li&gt;
&lt;li&gt;using: 값을 조작할 테이블과 조인할 테이블 지정합니다.&lt;/li&gt;
&lt;li&gt;on: 조인 조건을 지정합니다.&lt;/li&gt;
&lt;li&gt;merge update: 조건을 만족하면 수행합니다. (WHEN MATCHED THEN)&lt;/li&gt;
&lt;li&gt;merge insert: 조건을 만족하지 못하면 수행합니다. (WHEN NOT MATCHED THEN)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Example&lt;/h3&gt;
&lt;pre id=&quot;code_1775376700121&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MERGE INTO t1
USING t2
ON t1.a = t2.a
WHEN MATCHED THEN
    UPDATE SET t1.a = 10;
WHEN NOT MATCHED THEN
    INSERT (a) VALUES (1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9. 날짜 함수&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SYSDATE: 시스템의 현재 날짜 및 시간(yyyy-mm-dd hh24:mi:ss)을 반환합니다.&lt;/li&gt;
&lt;li&gt;SYSTIMESTAMP: 현재 날짜 및 시간을 밀리세컨드까지 표현하여 반환합니다.&lt;/li&gt;
&lt;li&gt;ADD_MONTHS(날짜, 숫자): 날짜 데이터의 월 단위를 입력 숫자만큼 더하거나 뺀 날짜를 계산하여 반환합니다.&lt;/li&gt;
&lt;li&gt;MONTHS_BETWEEN(날짜1, 날짜2): 날짜1에서 날짜2를 뺄셈 연산을 하여 개월 수 차이를 반환합니다.&lt;/li&gt;
&lt;li&gt;LAST_DAY(날짜): 월별 마지막 날짜 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10. 문자열 함수&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LOWER(문자열): 문자열을 모두 소문자로 변환하여 반환합니다.&lt;/li&gt;
&lt;li&gt;UPPER(문자열): 문자열을 모두 대문자로 변환하여 반환합니다.&lt;/li&gt;
&lt;li&gt;INITCAP(문자열): 문자열의 첫 번째 문자는 대문자, 나머지는 소문자로 변환합니다.&lt;/li&gt;
&lt;li&gt;CONCAT(문자열1, 문자열2): 문자열1과 문자열2를 합칩니다.&lt;/li&gt;
&lt;li&gt;SUBSTR(문자열, 시작 위치, 길이): 문자열의 시작 위치부터 길이만큼 자른 후 반환합니다.&lt;/li&gt;
&lt;li&gt;LPAD(문자열, 길이, 문자): 왼쪽에 입력 문자를 채웁니다.&lt;/li&gt;
&lt;li&gt;RPAD(문자열, 길이, 문자): 오른쪽에 입력 문자를 채웁니다.&lt;/li&gt;
&lt;li&gt;TRIM(문자열): 문자열의 양쪽 공백(&amp;rsquo; &amp;lsquo;)을 제거합니다.&lt;/li&gt;
&lt;li&gt;LTRIM(문자열, 옵션): 문자열의 왼쪽 공백을 제거하거나, 특정 문자나 반복적인 문자를 제거합니다.&lt;/li&gt;
&lt;li&gt;RTRIM(문자열, 옵션): 문자열의 오른쪽 공백을 제거하거나, 특정 문자나 반복적인 문자를 제거합니다.&lt;/li&gt;
&lt;li&gt;TRANSLATE(문자열1, 문자열2, 문자열3): 문자열1에서 문자열2를 문자열3으로 대체합니다.&lt;/li&gt;
&lt;li&gt;REPLACE(문자열1, 문자열2, 문자열3): 문자열1에서 문자열2를 문자열3으로 대체합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문자열1에 존재하는 문자열2가 완벽히 일치하지 않으면 대체 불가합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;11. 형변환 함수&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TO_NUMBER(문자열): 입력받은 문자열을 숫자로 변환하여 반환합니다.&lt;/li&gt;
&lt;li&gt;TO_DATE(문자열, 포맷): 문자열을 입력받아 포맷에 알맞은 날짜 데이터(DATE)로 변환하여 반환합니다.&lt;/li&gt;
&lt;li&gt;TO_CHAR(날짜 또는 숫자, 포맷): 날짜 데이터(DATE) 또는 숫자 데이터(NUMBER)를 입력받아 문자열로 변환하여 반환합니다.&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;Oracle의 기본적인 구문에 대해서 정리하였으니, 이제 MYSQL과 문법적인 차이점에 대해서 간략하게 설명드리겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Oracle vs MySQL&lt;/h2&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;Oracle : SYSDATE&lt;/li&gt;
&lt;li&gt;MySQL : now()&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 날짜 포맷팅&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Date to String
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Oracle : TO_CHAR(날짜, 형식)&lt;/li&gt;
&lt;li&gt;MySQL : DATE_FORMAT(날짜, 형식)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;String To Date
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Oracle : TO_DATE('문자열', '형식')&lt;/li&gt;
&lt;li&gt;MySQL : STR_TO_CHAR('문자열', '형식')&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. NULL 처리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Oracle : NVL(컬럼, '문자열')&lt;/li&gt;
&lt;li&gt;MySQL : IFNULL(컬럼, '문자열')&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;Oracle
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TO_CHAR() - Date to String 또는 Number to String&lt;/li&gt;
&lt;li&gt;TO_NUMBER('문자열') - String to Number&lt;/li&gt;
&lt;li&gt;TO_DATE('문자열', '형식') - String to Date&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;MySQL
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CAST(a AS b)&lt;/li&gt;
&lt;li&gt;CONVERT(a, b)&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;Oracle : CONCAT(&amp;rsquo;A&amp;rsquo;, &amp;lsquo;B&amp;rsquo;), &amp;lsquo;A&amp;rsquo; || &amp;lsquo;B&amp;rsquo; || &amp;lsquo;C&amp;rsquo; &amp;hellip;&lt;/li&gt;
&lt;li&gt;MySQL : CONCAT(&amp;rsquo;A&amp;rsquo;, &amp;lsquo;B&amp;rsquo;, &amp;lsquo;C&amp;rsquo;, &amp;hellip;)&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;Oracle : ROWNUM&lt;/li&gt;
&lt;li&gt;MySQL : LIMIT&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;lt;참고자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ittrue.tistory.com/353&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ittrue.tistory.com/353&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775377036839&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[SQL] Oracle과 MySQL의 문법 차이 정리&quot; data-og-description=&quot;현재 날짜 확인 Oracle : SYSDATE MySQL : now() 날짜 포맷 Date to String Oracle : TO_CHAR(날짜, &amp;lsquo;형식&amp;rsquo;) TO_CHAR(SYSDATE, 'YYYYMMDDHH24MISS') -- 2023-04-18 -&amp;gt; 20230418162030 TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') -- 2023-04-18 -&amp;gt; 2023-04-&quot; data-og-host=&quot;ittrue.tistory.com&quot; data-og-source-url=&quot;https://ittrue.tistory.com/353&quot; data-og-url=&quot;https://ittrue.tistory.com/353&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hm8Zp/dJMb9frF2oT/DC19QERvTcTfqYMDzBGeNk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/7VwZV/dJMb9eTQffF/XB5FAj32H2hKNsqTlSq250/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bncQ1E/dJMb9iaRIIe/YVeZZvrvFcaGU19CzZb6Kk/img.jpg?width=360&amp;amp;height=360&amp;amp;face=0_0_360_360&quot;&gt;&lt;a href=&quot;https://ittrue.tistory.com/353&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ittrue.tistory.com/353&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hm8Zp/dJMb9frF2oT/DC19QERvTcTfqYMDzBGeNk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/7VwZV/dJMb9eTQffF/XB5FAj32H2hKNsqTlSq250/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bncQ1E/dJMb9iaRIIe/YVeZZvrvFcaGU19CzZb6Kk/img.jpg?width=360&amp;amp;height=360&amp;amp;face=0_0_360_360');&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;[SQL] Oracle과 MySQL의 문법 차이 정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;현재 날짜 확인 Oracle : SYSDATE MySQL : now() 날짜 포맷 Date to String Oracle : TO_CHAR(날짜, &amp;lsquo;형식&amp;rsquo;) TO_CHAR(SYSDATE, 'YYYYMMDDHH24MISS') -- 2023-04-18 -&amp;gt; 20230418162030 TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') -- 2023-04-18 -&amp;gt; 2023-04-&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ittrue.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://13months.tistory.com/584&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://13months.tistory.com/584&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775377046157&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Oracle] 기초 문법 정리&quot; data-og-description=&quot;select distinct column as col from t1.table tbl; select 문법으로 테이블 또는 스키마의 테이블에서 열들을 조회할 수 있다. (schema.table.column 구조를 가진다) distinct, unique 를 함께 사용해 중복된 조회 시 나오&quot; data-og-host=&quot;13months.tistory.com&quot; data-og-source-url=&quot;https://13months.tistory.com/584&quot; data-og-url=&quot;https://13months.tistory.com/584&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cbuqZx/dJMb85WTUR7/iYALq0qTFTe8gp5ywCvM3K/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/bEoLMV/dJMb84p9fQH/jtHe7DuhQWLqgYvZuo16Z0/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://13months.tistory.com/584&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://13months.tistory.com/584&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cbuqZx/dJMb85WTUR7/iYALq0qTFTe8gp5ywCvM3K/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/bEoLMV/dJMb84p9fQH/jtHe7DuhQWLqgYvZuo16Z0/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&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;[Oracle] 기초 문법 정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;select distinct column as col from t1.table tbl; select 문법으로 테이블 또는 스키마의 테이블에서 열들을 조회할 수 있다. (schema.table.column 구조를 가진다) distinct, unique 를 함께 사용해 중복된 조회 시 나오&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;13months.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Database</category>
      <category>oracle</category>
      <category>oracle vs mysql</category>
      <category>Oracle 문법 정리</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/439</guid>
      <comments>https://daily1313.tistory.com/entry/Oracle-%EC%98%A4%EB%9D%BC%ED%81%B4-%EA%B8%B0%EB%B3%B8%EB%AC%B8%EB%B2%95-%EB%B0%8F-%ED%95%A8%EC%88%98-%EC%A0%95%EB%A6%AC-vs-MySQL#entry439comment</comments>
      <pubDate>Sun, 5 Apr 2026 17:22:21 +0900</pubDate>
    </item>
    <item>
      <title>[Java] gc에서 사용하지 않는 객체를 어떻게 구분하는가</title>
      <link>https://daily1313.tistory.com/entry/Java-gc%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EA%B0%9D%EC%B2%B4%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B5%AC%EB%B6%84%ED%95%98%EB%8A%94%EA%B0%80</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 Java의 핵심 개념 중 하나인 Garbage Collector(GC)가 사용하지 않는 객체를 어떤 기준으로 구분하는지&amp;nbsp;정리해보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그동안은 GC를 단순히 &quot;더 이상 필요 없는 객체를 자동으로 제거해 주는 기능&quot; 정도로만 이해하고 있었지만, 한 스타트업의 기술면접을 계기로 객체의 생존 여부를 판단하는 방식과 Reachability 개념을 좀 더 깊이 있게 살펴보게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 GC가 불필요한 객체를 어떻게 구분하는지와 자바의 참조 타입을 기반으로 동작 과정을 하나씩 정리해 보겠습니다. 정리 내용은 내용이 너무 유익해서 &lt;a href=&quot;https://d2.naver.com/helloworld/329631&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;해당 링크&lt;/a&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 data-ke-size=&quot;size26&quot;&gt;GC Reachability&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GC(Garbage Collection)에서 사용하지 않는 객체를 구분하는 기준은 핵심적으로 도달 가능성(&lt;b&gt;Reachability&lt;/b&gt;) 입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GC의 일반적인 과정&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;힙(heap) 내의 객체 중에서 가비지(garbage)를 찾아냅니다.&lt;/li&gt;
&lt;li&gt;찾아낸 가비지를 처리해서 힙의 메모리를 회수합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GC 사용자 코드 관여 시점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JDK 1.2 이전: 가비지 컬렉션(Garbage Collection, 이하 GC) 작업에 애플리케이션의 사용자 코드가 관여하지 않도록 구현되어 있었습니다.&lt;/li&gt;
&lt;li&gt;JDK 1.2 이후: JDK 1.2부터는 java.lang.ref 패키지를 추가해 사용자 코드와 GC가 상호작용할 수 있게 하고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;java.lang.ref 패키지는 일반적인 객체 참조인 strong reference 외에도 soft, weak, phantom 3가지의 새로운 참조 방식을 각각의 Reference 클래스로 제공합니다.&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;Java GC는 객체가 가비지인지 판별하기 위해서 reachability(도달 가능성)라는 개념을 사용하게 됩니다. 어떤 객체에 유효한 참조가 있으면 '&lt;b&gt;reachable&lt;/b&gt;'로, 없으면 '&lt;b&gt;unreachable&lt;/b&gt;'로 구별하고, &lt;b&gt;unreachable 객체를 가비지로 간주해 GC를 수행&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;Java GC는 Reachability Analysis를 사용해 객체의 생존 여부를 판단합니다. GC Root Set(참조의 시작점)를 시작점으로 객체 그래프를 탐색하여 도달 가능한 객체는 reachable로 유지하고, 도달할 수 없는 객체는 unreachable로 판단해 가비지로 수거합니다.&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;이제 Runtime Area에서 객체 참조를 할 수 있는 4가지 영역에 대해서 설명드리겠습니다.&amp;nbsp;&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;JVM Runtime Data Area&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-03-18 오전 12.15.35.png&quot; data-origin-width=&quot;838&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mbGCv/dJMcaiP50xV/q6yxKmEgZI1u82pMDltle0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mbGCv/dJMcaiP50xV/q6yxKmEgZI1u82pMDltle0/img.png&quot; data-alt=&quot;JVM Runtime Data Area&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mbGCv/dJMcaiP50xV/q6yxKmEgZI1u82pMDltle0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmbGCv%2FdJMcaiP50xV%2Fq6yxKmEgZI1u82pMDltle0%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;595&quot; height=&quot;369&quot; data-filename=&quot;스크린샷 2026-03-18 오전 12.15.35.png&quot; data-origin-width=&quot;838&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JVM Runtime Data Area&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙 메모리에 존재하는 객체 참조는 4가지로 분류됩니다.&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;Java 스택, 즉 Java 메서드 실행 시에 사용하는 지역 변수와 파라미터들에 의한 참조&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네이티브 스택, 즉 JNI(Java Native Interface)에 의해 생성된 객체에 대한 참조&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;힙 내의 다른 객체에 의한 참조를 제외한 나머지 3개가 root set으로, reachability를 판가름하는 기준이 됩니다. 즉, GC는 Root Set을 시작점으로 객체 그래프를 탐색하여, 도달 가능한 객체는 유지하고 도달할 수 없는 객체를 수거 대상으로 판단합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;여기서 한 가지 중요한 점은, 객체가 단순히 참조되고 있는지 여부뿐만 아니라 &quot;어떤 타입의 참조로 연결되어 있는지&quot;에 따라서도 수거 여부가 달라진다는 것입니다. 이때 등장하는 개념이 바로 Java의 Reference Type입니다.&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;Java Reference Type&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바의 참조타입은 총 4가지로 분류됩니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Strong References&lt;/li&gt;
&lt;li&gt;Weak References&lt;/li&gt;
&lt;li&gt;Soft References&lt;/li&gt;
&lt;li&gt;Phantom References&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Strong References&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-03-18 오전 12.20.22.png&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEdhNk/dJMcaaLlqaa/UG2lsS4W8yFBMZPONPmu50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEdhNk/dJMcaaLlqaa/UG2lsS4W8yFBMZPONPmu50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEdhNk/dJMcaaLlqaa/UG2lsS4W8yFBMZPONPmu50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEdhNk%2FdJMcaaLlqaa%2FUG2lsS4W8yFBMZPONPmu50%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;524&quot; height=&quot;343&quot; data-filename=&quot;스크린샷 2026-03-18 오전 12.20.22.png&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;536&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;root set으로부터 시작한 참조 사슬에 속한 객체들은 reachable 객체이고, 이 참조 사슬과 무관한 객체들이 unreachable 객체로 GC 대상입니다. 오른쪽 아래 객체처럼 reachable 객체를 참조하더라도, 다른 reachable 객체가 이 객체를 참조하지 않는다면 이 객체는 unreachable 객체입니다.&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;또한 강한 참조가 활성화된 객체는 가비지 수집 대상이 아닙니다. 강하게 참조된 변수가 null을 가리킬 때만 객체가 수집됩니다.&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;size18&quot;&gt;Example&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-03-18 오전 12.21.38.png&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MQwUY/dJMcacoRd90/uYzb1SHs4JcKFukPGTY601/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MQwUY/dJMcacoRd90/uYzb1SHs4JcKFukPGTY601/img.png&quot; data-alt=&quot;Strong References&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MQwUY/dJMcacoRd90/uYzb1SHs4JcKFukPGTY601/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMQwUY%2FdJMcacoRd90%2FuYzb1SHs4JcKFukPGTY601%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;627&quot; height=&quot;311&quot; data-filename=&quot;스크린샷 2026-03-18 오전 12.21.38.png&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;372&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Strong References&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1773760885386&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MyClass obj = new MyClass (); // 가비지 컬렉터 수집 X (강한 참조)
obj = null; // 강한 참조 변수가 null을 가리키기 때문에 가비지 컬렉터 수집 O

// Java program to illustrate Strong reference
class Geeks
{
    //Code..
}
public class Example
{
    public static void main(String[] args)
    {
         //Strong Reference - by default
        Geeks g = new Geeks();    
        
        //Now, object to which 'g' was pointing earlier is 
        //eligible for garbage collection.
        g = null; 
    }
}&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;new 키워드로 생성된 객체는 강한 참조를 나타내기에 가비지 컬렉션의 대상이 될 수 없지만, 해당 객체가 null을 가리키는 순간 가비지 컬렉션의 대상이 될 수 있습니다.&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;span data-token-index=&quot;0&quot;&gt;Weak References&lt;/span&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;대상 객체를 참조하는 경우가 WeakReference 객체만 존재하는 경우 GC의 대상이 됩니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 케이스에서 GC 실행 시 무조건 힙 메모리에서 제거됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Example&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-03-18 오전 12.24.02.png&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;778&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbNtTO/dJMcaiiit4Q/yPjuy6tdySmvwXNPoCwqlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbNtTO/dJMcaiiit4Q/yPjuy6tdySmvwXNPoCwqlk/img.png&quot; data-alt=&quot;Weak References&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbNtTO/dJMcaiiit4Q/yPjuy6tdySmvwXNPoCwqlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbNtTO%2FdJMcaiiit4Q%2FyPjuy6tdySmvwXNPoCwqlk%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;654&quot; height=&quot;360&quot; data-filename=&quot;스크린샷 2026-03-18 오전 12.24.02.png&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;778&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Weak References&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1773761073346&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//Java Code to illustrate Weak reference
import java.lang.ref.WeakReference;
class Gfg
{
    //code
    public void x()
    {
        System.out.println(&quot;GeeksforGeeks&quot;);
    }
}

public class Geeks
{
    public static void main(String[] args)
    {
        // Strong Reference
        Gfg g = new Gfg();     
        g.x();
        
        // Creating Weak Reference to Gfg-type object to which 'g' 
        // is also pointing.
        WeakReference&amp;lt;Gfg&amp;gt; weakref = new WeakReference&amp;lt;Gfg&amp;gt;(g);
        
        //Now, Gfg-type object to which 'g' was pointing earlier
        //is available for garbage collection.
        //But, it will be garbage collected only when JVM needs memory.
        g = null; 
        
        // You can retrieve back the object which
        // has been weakly referenced.
        // It successfully calls the method.
        g = weakref.get(); 
        
        g.x();
    }
}&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;강한 참조를 제거하고 약한 참조만 남게 되면, 해당 객체는 더 이상 GC Root와 강하게 연결되어 있지 않으므로 가비지 컬렉션의 대상이 될 수 있는 상태(weakly reachable)가 됩니다. 다만, 실제로 언제 수거될지는 GC 실행 시점에 따라 달라집니다.&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;span data-token-index=&quot;0&quot;&gt;Soft References&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Soft References에서는 객체가 가비지 컬렉션 대상이 되어도 JVM에 메모리가 절실히 필요할 때까지는 가비지 컬렉션 되지 않습니다.&lt;/li&gt;
&lt;li&gt;JVM의 메모리가 부족해지면 해당 객체가 메모리에서 제거됩니다.&lt;/li&gt;
&lt;li&gt;이러한 참조를 만들기 위해 SoftReference가 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Example&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-03-18 오전 12.28.07.png&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkd4OB/dJMcaivNVh8/GbX88kvdK553JrvBde3sK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkd4OB/dJMcaivNVh8/GbX88kvdK553JrvBde3sK1/img.png&quot; data-alt=&quot;Soft References&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkd4OB/dJMcaivNVh8/GbX88kvdK553JrvBde3sK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdkd4OB%2FdJMcaivNVh8%2FGbX88kvdK553JrvBde3sK1%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;652&quot; height=&quot;389&quot; data-filename=&quot;스크린샷 2026-03-18 오전 12.28.07.png&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;786&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Soft References&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1773761349484&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//Code to illustrate Soft reference
import java.lang.ref.SoftReference;
class Gfg
{
    public void x()
    {
        System.out.println(&quot;GeeksforGeeks&quot;);
    }
}

public class Geeks
{
    public static void main(String[] args)
    {
        // Strong Reference
        Gfg g = new Gfg();     
        g.x();
        
        // Creating Soft Reference to Gfg-type object to which 'g' 
        // is also pointing.
        SoftReference&amp;lt;Gfg&amp;gt; softref = new SoftReference&amp;lt;Gfg&amp;gt;(g);
        
        // Now, Gfg-type object to which 'g' was pointing
        // earlier is available for garbage collection.
        g = null; 
        
        // You can retrieve back the object which
        // has been weakly referenced.
        // It successfully calls the method.
        g = softref.get(); 
        
        g.x();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;260&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;260&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;Soft Reference 역시 강한 참조가 제거되면 해당 객체는 GC의 대상이 될 수 있는 상태가 됩니다.&lt;/p&gt;
&lt;p data-end=&quot;260&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;하지만, Weak Reference와 달리, Soft Reference는 메모리가 부족한 상황에서만 GC에 의해 객체가 수거됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;260&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;260&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;즉, 현재와 같이 g = null로 강한 참조를 제거한 이후에도, JVM의 메모리가 충분하다면 해당 객체는 유지되면 softref.get() 메서드를 통해 다시 해당 객체에 접근할 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;260&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;260&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;따라서 Soft Reference는 캐시와 같이 가능하면 유지하고, 메모리가 부족할 때만 제거해도 되는 데이터를 관리할 때 주로 사용됩니다.&lt;/p&gt;
&lt;p data-end=&quot;260&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;260&quot; data-start=&quot;99&quot; data-ke-size=&quot;size18&quot;&gt;soft reference vs weak reference&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;260&quot; data-start=&quot;99&quot;&gt;Soft Reference는 Strong Reference가 제거되면 GC 대상이 될 수 있다는 점에서는 Weak Reference와 동일합니다.&lt;/li&gt;
&lt;li data-end=&quot;260&quot; data-start=&quot;99&quot;&gt;하지만 Weak Reference는 GC가 수행되면 바로 수거되는 반면, Soft Reference는 메모리가 부족해질 때까지 최대한 유지된다는 차이가 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Phantom References&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;Phantom References로&lt;/b&gt; 참조되는 객체는 가비지 컬렉션 대상이 될 수 있습니다.&lt;/li&gt;
&lt;li&gt;JVM은 메모리에서 해당 객체를 제거하기 전에 'ReferenceQueue'라는 곳에 보관합니다.&lt;/li&gt;
&lt;li&gt;해당 객체에 대해 finalize() 메서드(GC 수행 이전에 리소스 정리를 위한 메서드)를 호출한 후에 생성됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1773761671226&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//Code to illustrate Phantom reference
import java.lang.ref.*;

class Gfg
{
    //code
    public void x()
    {
        System.out.println(&quot;GeeksforGeeks&quot;);
    }
}

public class Geeks
{
    public static void main(String[] args)
    {
        //Strong Reference
        Gfg g = new Gfg();     
        g.x();
        
        //Creating reference queue
        ReferenceQueue&amp;lt;Gfg&amp;gt; refQueue = new ReferenceQueue&amp;lt;Gfg&amp;gt;();

        //Creating Phantom Reference to Gfg-type object to which 'g' 
        //is also pointing.
        PhantomReference&amp;lt;Gfg&amp;gt; phantomRef = null;
        
        phantomRef = new PhantomReference&amp;lt;Gfg&amp;gt;(g,refQueue);
        
        //Now, Gfg-type object to which 'g' was pointing
        //earlier is available for garbage collection.
        //But, this object is kept in 'refQueue' before 
        //removing it from the memory.
        g = null; 
        
        //It always returns null. 
        g = phantomRef.get(); 
        
        //It shows NullPointerException.
        g.x();
    }
}&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;이번 정리를 통해 GC는 단순히 &quot;안 쓰는 객체를 제거한다&quot;는 개념이 아니라, Root Set을 기준으로 Reachability를 분석하고, Reference Type에 따라 객체의 생명주기를 다르게 관리하는 메커니즘이라는 점을 이해할 수 있었습니다.&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://d2.naver.com/helloworld/329631&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://d2.naver.com/helloworld/329631&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/java/types-references-java/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.geeksforgeeks.org/java/types-references-java/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1773761832787&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Types of References in Java - GeeksforGeeks&quot; data-og-description=&quot;Your All-in-One Learning Portal: GeeksforGeeks is a comprehensive educational platform that empowers learners across domains-spanning computer science and programming, school education, upskilling, commerce, software tools, competitive exams, and more.&quot; data-og-host=&quot;www.geeksforgeeks.org&quot; data-og-source-url=&quot;https://www.geeksforgeeks.org/java/types-references-java/&quot; data-og-url=&quot;https://www.geeksforgeeks.org/java/types-references-java/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eHuyE/dJMb8SpGSkI/pSvm18UCNxj2yXM2eEieW1/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200,https://scrap.kakaocdn.net/dn/c90yqt/dJMb8SpGSkJ/E5FPYC8GyaC8xVUlUrYue0/img.png?width=424&amp;amp;height=222&amp;amp;face=0_0_424_222,https://scrap.kakaocdn.net/dn/gcgg1/dJMb8WMourz/mvhWWLpDruWXpFepMtwwa1/img.png?width=678&amp;amp;height=420&amp;amp;face=0_0_678_420&quot;&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/java/types-references-java/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.geeksforgeeks.org/java/types-references-java/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eHuyE/dJMb8SpGSkI/pSvm18UCNxj2yXM2eEieW1/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200,https://scrap.kakaocdn.net/dn/c90yqt/dJMb8SpGSkJ/E5FPYC8GyaC8xVUlUrYue0/img.png?width=424&amp;amp;height=222&amp;amp;face=0_0_424_222,https://scrap.kakaocdn.net/dn/gcgg1/dJMb8WMourz/mvhWWLpDruWXpFepMtwwa1/img.png?width=678&amp;amp;height=420&amp;amp;face=0_0_678_420');&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;Types of References in Java - GeeksforGeeks&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Your All-in-One Learning Portal: GeeksforGeeks is a comprehensive educational platform that empowers learners across domains-spanning computer science and programming, school education, upskilling, commerce, software tools, competitive exams, and more.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.geeksforgeeks.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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>Backend/Java Concept</category>
      <category>garbage collection</category>
      <category>GC Reachability</category>
      <category>Java Reference Type</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/438</guid>
      <comments>https://daily1313.tistory.com/entry/Java-gc%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EA%B0%9D%EC%B2%B4%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B5%AC%EB%B6%84%ED%95%98%EB%8A%94%EA%B0%80#entry438comment</comments>
      <pubDate>Wed, 18 Mar 2026 00:39:40 +0900</pubDate>
    </item>
    <item>
      <title>[CS] float 타입 변수의 연산은 왜 부정확할까</title>
      <link>https://daily1313.tistory.com/entry/CS-float-%ED%83%80%EC%9E%85-%EB%B3%80%EC%88%98%EC%9D%98-%EC%97%B0%EC%82%B0%EC%9D%80-%EC%99%9C-%EB%B6%80%EC%A0%95%ED%99%95%ED%95%A0%EA%B9%8C</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&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;업무 시스템에서 값을 계산하는 과정에서 float 타입 변수들끼리 연산을 수행했는데, 예상과 달리 계산 결과의 정확도가 깨지게 되었습니다. 디버깅을 진행하면서 실제 연산 결과가 기대한 값과 다르다는 것을 확인할 수 있었습니다.&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;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;p data-ke-size=&quot;size16&quot;&gt;세 변수(출장비, 식비, 숙박비) 모두 float 타입이었고, 단순한 덧셈 연산이었음에도 불구하고 결과 값에 오차가 발생했습니다.&lt;/p&gt;
&lt;p data-end=&quot;633&quot; data-start=&quot;430&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;633&quot; data-start=&quot;430&quot; data-ke-size=&quot;size16&quot;&gt;컴퓨터는 숫자를 포함한 모든 데이터를 이진법(2진수)으로 표현하고 처리합니다. 하지만 이진법으로는 모든 실수를 정확하게 표현할 수 없습니다. 특히 10진수 소수(예: 0.1, 187.17 등)는 2진수로 변환했을 때 무한 소수가 되는 경우가 많습니다. 이 때문에 float과 같은 부동소수점 타입에서는 단순한 실수 연산에서도 미세한 오차가 발생할 수 있습니다.&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;그렇다면 왜 이런 현상이 발생하는지, 그리고 float 타입에서 값의 계산이 부정확해지는 이유는 무엇인지 하나씩 살펴보겠습니다.&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;자료형&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정수 자료형 (ex: int)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소수점 없는 정수 표현을 위한 자료형 (32 bit)&amp;nbsp;&lt;/li&gt;
&lt;li&gt;첫째 자리는 부호 표현을 위한 bit (0(양수), 1(음수)), 그 외 31 bit는 절댓값을 표현하기 위해 존재합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;-2^31 ~ 2^31 - 1 범위의 정수를 표현합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LPrGp/dJMcaibhEuR/arkJskBDtm1KVlzKCchiL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LPrGp/dJMcaibhEuR/arkJskBDtm1KVlzKCchiL0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2472&quot; data-origin-height=&quot;692&quot; data-filename=&quot;스크린샷 2026-02-21 오후 4.51.27.png&quot; style=&quot;width: 47.5749%; margin-right: 10px;&quot; data-widthpercent=&quot;48.13&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LPrGp/dJMcaibhEuR/arkJskBDtm1KVlzKCchiL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLPrGp%2FdJMcaibhEuR%2FarkJskBDtm1KVlzKCchiL0%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;2472&quot; height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/10NiZ/dJMcabJ0mmm/atscBkCEfSQiml5kQD0HfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/10NiZ/dJMcabJ0mmm/atscBkCEfSQiml5kQD0HfK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1786&quot; data-origin-height=&quot;464&quot; data-filename=&quot;스크린샷 2026-02-21 오후 4.53.42.png&quot; style=&quot;width: 51.2624%;&quot; data-widthpercent=&quot;51.87&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/10NiZ/dJMcabJ0mmm/atscBkCEfSQiml5kQD0HfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F10NiZ%2FdJMcabJ0mmm%2FatscBkCEfSQiml5kQD0HfK%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;1786&quot; height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;/div&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;표현하려는 값의 범위에 따라 적절한 자료형을 선택할 수 있습니다. 비교적 작은 값은 byte나 short를 사용해 메모리를 절약할 수 있고, 더 큰 값이 필요할 경우에는 long 타입을 사용합니다. 하지만, 해당 type만으로는 9.7, 8.125와 같은 숫자들을 표현하기에는 어려움이 있습니다. 이러한 숫자들을 표현하기 위해서 고정 소수점, 부동 소수점(Floating Point) 자료형이 존재합니다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;실수 자료형 (ex: float)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실수 자료형 표현 방식에는 고정소수점, 부동소수점 표현방식 총 2가지가 존재합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;고정소수점 방식 (1bit(부호) + 15bit(정수) + 15bit(소수))&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmE9sW/dJMcajgTjDu/KP3QC9CrXqk3Ardb1KgZb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmE9sW/dJMcajgTjDu/KP3QC9CrXqk3Ardb1KgZb0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2158&quot; data-origin-height=&quot;596&quot; data-filename=&quot;스크린샷 2026-02-21 오후 5.11.10.png&quot; style=&quot;width: 49.6204%; margin-right: 10px;&quot; data-widthpercent=&quot;50.2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmE9sW/dJMcajgTjDu/KP3QC9CrXqk3Ardb1KgZb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmE9sW%2FdJMcajgTjDu%2FKP3QC9CrXqk3Ardb1KgZb0%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;2158&quot; height=&quot;596&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7YTVs/dJMcahclBS0/kkBknKwk3hOZHSJit1VqQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7YTVs/dJMcahclBS0/kkBknKwk3hOZHSJit1VqQk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2162&quot; data-origin-height=&quot;602&quot; data-filename=&quot;스크린샷 2026-02-21 오후 5.12.56.png&quot; style=&quot;width: 49.2169%;&quot; data-widthpercent=&quot;49.8&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7YTVs/dJMcahclBS0/kkBknKwk3hOZHSJit1VqQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7YTVs%2FdJMcahclBS0%2FkkBknKwk3hOZHSJit1VqQk%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;2162&quot; height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;고정소수점 표현 방식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제일 첫 번째 bit를 부호비트 (0(양수), 1(음수))로 활용하고, 15bit, 16bit를 각각 정수부, 소수부로 활용하는 표현방식입니다.&lt;/li&gt;
&lt;li&gt;5.625의 경우에는 다음처럼 고정소수점 방식으로 표현할 수 있습니다.&lt;/li&gt;
&lt;li&gt;고정소수점 방식에서는 정수부를 2^15 - 1까지 밖에 표현하지 못하고, 소수 부분에도 정밀한 값을 표현하지 못하는 치명적인 단점이 존재합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;이러한 단점을 보완하기 위해서 컴퓨터에서는 부동소수점(Floating Point) 표현방식으로 실수를 표현합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;float형 부동소수점 방식 (1bit(부호) + 8bit(지수) + 23bit(가수))&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-02-21 오후 5.38.40.png&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DD9X3/dJMcaaqL6rj/5Jj8HCuFixl5XLfIEjmTsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DD9X3/dJMcaaqL6rj/5Jj8HCuFixl5XLfIEjmTsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DD9X3/dJMcaaqL6rj/5Jj8HCuFixl5XLfIEjmTsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDD9X3%2FdJMcaaqL6rj%2F5Jj8HCuFixl5XLfIEjmTsK%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;635&quot; height=&quot;268&quot; data-filename=&quot;스크린샷 2026-02-21 오후 5.38.40.png&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소수점이 떠나닌다(Floating)는 의미의 실수 표현 방식이며, 제일 첫 번째 bit를 부호비트 (0(양수), 1(음수))로 활용하고, 8bit, 23bit를 각각 지수부, 가수부로 활용하는 표현방식입니다. (double: 11bit (지수), 52bit (가수))&lt;/li&gt;
&lt;li&gt;23비트를 숫자 표현을 위해 사용할 수 있으며, 소수점의 자리도 필요한 대로 움직일 수 있기 때문에 고정소수점보다 훨씬 더 넓은 범위의 숫자를 표현할 수 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;지수부: 127과의 차이를 통해 소수점이 몇 칸 왼쪽으로 움직이는지 표현하기 위한 비트 (양수: 왼쪽, 음수: 오른쪽)&lt;/li&gt;
&lt;li&gt;가수부: 소수점 뒤로 오는 부분을 표현하기 위한 비트&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;부동소수점 계산 예시&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EgSow/dJMcadOviuI/9Ke0tGF2TuA7rEyau6uiCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EgSow/dJMcadOviuI/9Ke0tGF2TuA7rEyau6uiCK/img.png&quot; style=&quot;width: 49.596%; margin-right: 10px;&quot; data-widthpercent=&quot;50.18&quot; data-filename=&quot;스크린샷 2026-02-21 오후 5.19.52.png&quot; data-origin-height=&quot;810&quot; data-origin-width=&quot;1776&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EgSow/dJMcadOviuI/9Ke0tGF2TuA7rEyau6uiCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEgSow%2FdJMcadOviuI%2F9Ke0tGF2TuA7rEyau6uiCK%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;1776&quot; height=&quot;810&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wmw5C/dJMcabXvEUg/IzDVYOuROk8RLSB9l27nU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wmw5C/dJMcabXvEUg/IzDVYOuROk8RLSB9l27nU0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1772&quot; data-origin-height=&quot;814&quot; data-filename=&quot;스크린샷 2026-02-21 오후 5.27.32.png&quot; style=&quot;width: 49.2412%;&quot; data-widthpercent=&quot;49.82&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wmw5C/dJMcabXvEUg/IzDVYOuROk8RLSB9l27nU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwmw5C%2FdJMcabXvEUg%2FIzDVYOuROk8RLSB9l27nU0%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;1772&quot; height=&quot;814&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;부동소수점 표현방식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1) 9.625의 경우에는 2진수로 1001.101로 표현되며 부동소수점 방식으로는 0 10000010 (130) 0011010... 이 됩니다.&lt;/li&gt;
&lt;li&gt;2) 0.1의 경우에서는 2진수로 표현하면 다음처럼 0.000110011... 의 무한소수로 표현되며 부동소수점 방식으로는 0 01111011 (123) 100110011... 이 됩니다.&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;h2 data-ke-size=&quot;size26&quot;&gt;부동소수점 정밀도 테스트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;float 변수끼리의 연산을 수행했을 때와 Math.round() 함수, BigDecimal 타입을 적용했을 때의 테스트를 적용하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-02-21 오후 5.40.12.png&quot; data-origin-width=&quot;1598&quot; data-origin-height=&quot;1624&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5Xdye/dJMcafMo81a/k74x0xq7kSBuyJH3X6K2mK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5Xdye/dJMcafMo81a/k74x0xq7kSBuyJH3X6K2mK/img.png&quot; data-alt=&quot;float type 테스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5Xdye/dJMcafMo81a/k74x0xq7kSBuyJH3X6K2mK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5Xdye%2FdJMcafMo81a%2Fk74x0xq7kSBuyJH3X6K2mK%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;452&quot; height=&quot;459&quot; data-filename=&quot;스크린샷 2026-02-21 오후 5.40.12.png&quot; data-origin-width=&quot;1598&quot; data-origin-height=&quot;1624&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;float type 테스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-02-21 오후 5.45.40.png&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nig4G/dJMcafMo9gZ/tgIIGMWi3QsfP2ibNLPQs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nig4G/dJMcafMo9gZ/tgIIGMWi3QsfP2ibNLPQs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nig4G/dJMcafMo9gZ/tgIIGMWi3QsfP2ibNLPQs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnig4G%2FdJMcafMo9gZ%2FtgIIGMWi3QsfP2ibNLPQs1%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;564&quot; height=&quot;264&quot; data-filename=&quot;스크린샷 2026-02-21 오후 5.45.40.png&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;314&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;다음과 같이 float 타입으로 선언했을 때 정확하게 427.17이 나오지 않음을 확인하였습니다. 자바에서는 float 타입 실수를 표현할 때 32비트 부동소수점 방식으로 표현하기에 오차가 발생하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, &lt;u&gt;&lt;b&gt;자바스크립트에서는 변수가 float 타입으로 DB에 정의되어 있더라도 IEEE 754 표준의 64비트 부동소수점 방식으로 표현돼서&lt;/b&gt;&lt;/u&gt; Math.round() 함수를 활용하거나, 외부 라이브러리를 활용해서 이러한 문제를 개선할 수 있습니다.&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://bigtop.tistory.com/47&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://bigtop.tistory.com/47&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771664953271&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[JavaScript] 자바스크립트 소수점 계산 오류 가볍게 이해하기&quot; data-og-description=&quot;자바스크립트로 숫자를 조금 다루다 보면, 분명히 우리가 상식적으로 생각했을 때 너무나도 당연하고 올바른 계산식을 컴퓨터에게 맡겼는데, 흔하지 않지만 나름 흔한 확률(?)로 계산 오류가 발&quot; data-og-host=&quot;bigtop.tistory.com&quot; data-og-source-url=&quot;https://bigtop.tistory.com/47&quot; data-og-url=&quot;https://bigtop.tistory.com/47&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ct1W9h/dJMb9b3OMJZ/nDxsOLIIWkvEBULuCJCJa1/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/mLa0B/dJMb9cBEPrd/iGdzSRDSd7ovQFusDZ6vDk/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://bigtop.tistory.com/47&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://bigtop.tistory.com/47&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ct1W9h/dJMb9b3OMJZ/nDxsOLIIWkvEBULuCJCJa1/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/mLa0B/dJMb9cBEPrd/iGdzSRDSd7ovQFusDZ6vDk/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;[JavaScript] 자바스크립트 소수점 계산 오류 가볍게 이해하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트로 숫자를 조금 다루다 보면, 분명히 우리가 상식적으로 생각했을 때 너무나도 당연하고 올바른 계산식을 컴퓨터에게 맡겼는데, 흔하지 않지만 나름 흔한 확률(?)로 계산 오류가 발&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;bigtop.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;a href=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%8B%A4%EC%88%98-%ED%91%9C%ED%98%84%EB%B6%80%EB%8F%99-%EC%86%8C%EC%88%98%EC%A0%90-%EC%9B%90%EB%A6%AC-%ED%95%9C%EB%88%88%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%8B%A4%EC%88%98-%ED%91%9C%ED%98%84%EB%B6%80%EB%8F%99-%EC%86%8C%EC%88%98%EC%A0%90-%EC%9B%90%EB%A6%AC-%ED%95%9C%EB%88%88%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771663025967&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;☕ 실수 표현(부동 소수점) 원리 한눈에 이해하기&quot; data-og-description=&quot;실수의 2진수 표현 10진수의 정수를 2진수의 정수로 변환할 수 있듯이, 10진수의 소수를 2진수의 소수로 변환할 수 있다. 예를들어 10진수 11.765625 를 2진수 소수로 변환하는 방법은 다음과 같다. 먼&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%8B%A4%EC%88%98-%ED%91%9C%ED%98%84%EB%B6%80%EB%8F%99-%EC%86%8C%EC%88%98%EC%A0%90-%EC%9B%90%EB%A6%AC-%ED%95%9C%EB%88%88%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%8B%A4%EC%88%98-%ED%91%9C%ED%98%84%EB%B6%80%EB%8F%99-%EC%86%8C%EC%88%98%EC%A0%90-%EC%9B%90%EB%A6%AC-%ED%95%9C%EB%88%88%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eOjse/dJMb8950jia/ZMcnTzNAGGIJ4BYJzbadZk/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/cXUWJB/dJMb85vLB0h/goFYPrxk3pDrEXI15Sodck/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/9UDYM/dJMb89yaf3E/u1scCBRwdnoYjoYvpK6sw1/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%8B%A4%EC%88%98-%ED%91%9C%ED%98%84%EB%B6%80%EB%8F%99-%EC%86%8C%EC%88%98%EC%A0%90-%EC%9B%90%EB%A6%AC-%ED%95%9C%EB%88%88%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%8B%A4%EC%88%98-%ED%91%9C%ED%98%84%EB%B6%80%EB%8F%99-%EC%86%8C%EC%88%98%EC%A0%90-%EC%9B%90%EB%A6%AC-%ED%95%9C%EB%88%88%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eOjse/dJMb8950jia/ZMcnTzNAGGIJ4BYJzbadZk/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/cXUWJB/dJMb85vLB0h/goFYPrxk3pDrEXI15Sodck/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/9UDYM/dJMb89yaf3E/u1scCBRwdnoYjoYvpK6sw1/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&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;☕ 실수 표현(부동 소수점) 원리 한눈에 이해하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;실수의 2진수 표현 10진수의 정수를 2진수의 정수로 변환할 수 있듯이, 10진수의 소수를 2진수의 소수로 변환할 수 있다. 예를들어 10진수 11.765625 를 2진수 소수로 변환하는 방법은 다음과 같다. 먼&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=ZQDsWySjY6g&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=ZQDsWySjY6g&lt;/a&gt;&lt;/p&gt;</description>
      <category>CS</category>
      <category>고정소수점</category>
      <category>부동소수점</category>
      <category>소수점 계산 오류 분석</category>
      <category>실수 표현 방식</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/437</guid>
      <comments>https://daily1313.tistory.com/entry/CS-float-%ED%83%80%EC%9E%85-%EB%B3%80%EC%88%98%EC%9D%98-%EC%97%B0%EC%82%B0%EC%9D%80-%EC%99%9C-%EB%B6%80%EC%A0%95%ED%99%95%ED%95%A0%EA%B9%8C#entry437comment</comments>
      <pubDate>Sat, 21 Feb 2026 18:27:12 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Mybatis Framework에 대해서 알아보자</title>
      <link>https://daily1313.tistory.com/entry/Spring-Mybatis-Framework%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-02-07 오후 5.59.09.png&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bC5piI/dJMcahQQqVi/5AgTobeDFNqfg2Ba6Cl6ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bC5piI/dJMcahQQqVi/5AgTobeDFNqfg2Ba6Cl6ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bC5piI/dJMcahQQqVi/5AgTobeDFNqfg2Ba6Cl6ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbC5piI%2FdJMcahQQqVi%2F5AgTobeDFNqfg2Ba6Cl6ik%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;590&quot; height=&quot;156&quot; data-filename=&quot;스크린샷 2026-02-07 오후 5.59.09.png&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;156&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyBatis는 JPA처럼 Hibernate와 같은 ORM 구현체가 SQL을 자동으로 생성해 주는 방식이 아니라, 개발자가 직접 XML 파일에 SQL을 작성하고 그 결과를 Java 객체에 매핑하는 SQL Mapper 프레임워크입니다.&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;이러한 특성 덕분에 JPA에 비해 복잡한 SQL을 세밀하게 제어할 수 있다는 장점이 있으며, 도메인 로직이나 쿼리 튜닝이 중요한 환경에서 이점을 누릴 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;353&quot; data-start=&quot;345&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;353&quot; data-start=&quot;345&quot; data-ke-size=&quot;size16&quot;&gt;이 글에서는 MyBatis의 기본 개념과 주요 구성 컴포넌트를 살펴보고, 컴포넌트 기반으로 동작과정을 순수 MyBatis와 Mybatis-Spring과 비교해서 보겠습니다.&lt;/p&gt;
&lt;p data-end=&quot;353&quot; data-start=&quot;345&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;353&quot; data-start=&quot;345&quot; data-ke-size=&quot;size26&quot;&gt;Mybatis&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발자가 직접 SQL을 작성하고, 해당 SQL 결과를 자바 객체로 매핑시켜주는 Persistence Framework입니다. (SQL Mapper)&lt;/li&gt;
&lt;li&gt;JPA와는 다르게 ORM 기술을 이용해 SQL을 자동으로 생성하는 것이 아니라, SQL을 직접 세밀하게 제어한다는 특징을 가지고 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이러한 이유로 복잡한 쿼리와 SQL 제어가 필요한 경우에 사용, 특정 DB에 최적화된 SQL 작성이 필요한 경우에 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;JDBC 프로그래밍을 단순화하여, 불필요한 Boilerplate 코드를 제거하고, Java 소스코드에서 SQL 문을 분리하여 별도의 XML 파일로 저장하고, 이 둘을 서로 연결시켜 주는 기능을 제공합니다.&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;Mybatis는 JPA와 다르게 DB에 종속돼야 하는 단점은 존재하지만, JPA에 비해서 학습 곡선이 낮은 편이라고 생각합니다. JPA는 OSIV, N+1 문제, 연관관계 매핑뿐만 아니라 지연 로딩으로 인한 예상하지 못한 쿼리 실행, 영속성 컨텍스트의 객체 상태 관리, 그리고 대용량 데이터 처리 시의 성능 이슈 등 고려해야 할 요소가 매우 많습니다. 반면, Mybatis는 SQL문을 잘 작성하여 성능을 최적화할 수 있으며 SQL에 강점을 가진 개발자라면 학습하기 더욱 수월합니다.&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 data-ke-size=&quot;size26&quot;&gt;Mybatis 특징&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MyBatis에서 Java 메서드와 SQL 간의 매핑을 시켜서 Java 메서드 선언과 SQL 문만 만들면 MyBatis가 자동으로 그 둘 간 연결을 시켜줍니다.&lt;/li&gt;
&lt;li&gt;SQL 문을 별도의 Java 코드에서 분리해 두어서 관리가 편하게 하였으며, 분리된 SQL문을 MyBatis가 찾아서 실행해 줍니다.&lt;/li&gt;
&lt;li&gt;동적인 SQL(Dynamic SQL) 생성 기능을 제공하여 프로그램 실행 중에 입력되는 파라미터에 따라 서로 다른 SQL문을 동적으로 생성해 내는 기능을 제공해 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mapper 클래스에서 @Mapper 어노테이션과 관련 Java 메서드를 정의하고, xml 파일에서 쿼리를 아래 예시와 같이 직접 작성해 주면 됩니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;BoardMapper.class&lt;/h3&gt;
&lt;pre id=&quot;code_1770457051761&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.board.mapper;

import com.example.board.domain.Board;
import com.example.board.domain.BoardFile;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface BoardMapper {

    // &amp;lt;insert id=&quot;save&quot; parameterType=&quot;board&quot; useGeneratedKeys=&quot;true&quot; keyProperty=&quot;id&quot;&amp;gt;
    int save(Board board); // useGeneratedKeys로 board.id에 PK가 세팅됨

    // &amp;lt;select id=&quot;findAll&quot; resultType=&quot;board&quot;&amp;gt;
    List&amp;lt;Board&amp;gt; findAll();

    // &amp;lt;update id=&quot;updateHits&quot; parameterType=&quot;Long&quot;&amp;gt;
    int updateHits(@Param(&quot;id&quot;) Long id);

    // &amp;lt;select id=&quot;findById&quot; resultType=&quot;board&quot;&amp;gt;
    Board findById(@Param(&quot;id&quot;) Long id);

    // &amp;lt;update id=&quot;update&quot;&amp;gt;
    int update(Board board);

    // &amp;lt;delete id=&quot;delete&quot; parameterType=&quot;Long&quot;&amp;gt;
    int delete(@Param(&quot;id&quot;) Long id);

    // &amp;lt;insert id=&quot;saveFile&quot; parameterType=&quot;boardFile&quot;&amp;gt;
    int saveFile(BoardFile boardFile);

    // &amp;lt;select id=&quot;findFile&quot; parameterType=&quot;Long&quot; resultType=&quot;boardFile&quot;&amp;gt;
    List&amp;lt;BoardFile&amp;gt; findFile(@Param(&quot;id&quot;) Long id);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;board-mapper.xml&lt;/h3&gt;
&lt;pre id=&quot;code_1770456846779&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;mapper namespace=&quot;Board&quot;&amp;gt;
    &amp;lt;insert id=&quot;save&quot; parameterType=&quot;board&quot; useGeneratedKeys=&quot;true&quot; keyProperty=&quot;id&quot;&amp;gt;
        insert into board_table(boardTitle, boardWriter, boardPass, boardContents, fileAttached)
        values(#{boardTitle}, #{boardWriter}, #{boardPass}, #{boardContents}, #{fileAttached})
    &amp;lt;/insert&amp;gt;
    &amp;lt;select id=&quot;findAll&quot; resultType=&quot;board&quot;&amp;gt;
        select id, boardTitle, boardWriter, boardHits, date_format(createdAt, &quot;%Y-%m-%d&quot;) as createdAt
        from board_table order by id desc
    &amp;lt;/select&amp;gt;
    &amp;lt;update id=&quot;updateHits&quot; parameterType=&quot;Long&quot;&amp;gt;
        update board_table set boardHits = boardHits + 1 where id = #{id}
    &amp;lt;/update&amp;gt;
    &amp;lt;select id=&quot;findById&quot; resultType=&quot;board&quot;&amp;gt;
        select id, boardTitle, boardWriter, boardPass, boardContents, boardHits,
        date_format(createdAt, &quot;%Y-%m-%d %H:%i:%s&quot;) as createdAt, fileAttached
        from board_table where id = #{id}
    &amp;lt;/select&amp;gt;
    &amp;lt;update id=&quot;update&quot;&amp;gt;
        update board_table set boardTitle=#{boardTitle}, boardContents=#{boardContents}
        where id = #{id}
    &amp;lt;/update&amp;gt;
    &amp;lt;delete id=&quot;delete&quot; parameterType=&quot;Long&quot;&amp;gt;
        delete from board_table where id = #{id}
    &amp;lt;/delete&amp;gt;
    &amp;lt;insert id=&quot;saveFile&quot; parameterType=&quot;boardFile&quot;&amp;gt;
        insert into board_file_table(originalFileName, storedFileName, boardId)
        values (#{originalFileName}, #{storedFileName}, #{boardId})
    &amp;lt;/insert&amp;gt;
    &amp;lt;select id=&quot;findFile&quot; parameterType=&quot;Long&quot; resultType=&quot;boardFile&quot;&amp;gt;
        select * from board_file_table where boardId = #{id}
    &amp;lt;/select&amp;gt;
&amp;lt;/mapper&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동적 SQL 생성 기능&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;if 조건같이 사용자가 입력하는 값에 따라 서로 다른 SQL 문장이 생성되어 실행됩니다.&lt;/li&gt;
&lt;li&gt;아래 예시와 같이 제목을 입력하여 검색하면 다음과 같이 제목을 검색하는 SQL이 만들어집니다.&lt;/li&gt;
&lt;li&gt;if 문, choose, when, otherwise, foreach 등의 문법을 지원해서 동적인 SQL 문 생성이 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1770457312443&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;select id=&quot;findActiveBlogwithTitleLike&quot; resultType=&quot;Blog&quot;&amp;gt;
	SELECT * FROM BLOG WHERE state = 'ACTIVE' 
	&amp;lt;if test=&quot;title != null&quot;&amp;gt;
		AND title like #{title}
	&amp;lt;/if&amp;gt;
&amp;lt;select&amp;gt;

&amp;lt;select id=&quot;findActiveBlogLike&quot;
     resultType=&quot;Blog&quot;&amp;gt;
  SELECT * FROM BLOG WHERE state = &amp;lsquo;ACTIVE&amp;rsquo;
  &amp;lt;choose&amp;gt;
    &amp;lt;when test=&quot;title != null&quot;&amp;gt;
      AND title like #{title}
    &amp;lt;/when&amp;gt;
    &amp;lt;when test=&quot;author != null and author.name != null&quot;&amp;gt;
      AND author_name like #{author.name}
    &amp;lt;/when&amp;gt;
    &amp;lt;otherwise&amp;gt;
      AND featured = 1
    &amp;lt;/otherwise&amp;gt;
  &amp;lt;/choose&amp;gt;
&amp;lt;/select&amp;gt;

&amp;lt;select id=&quot;findActiveBlogLike&quot;
     resultType=&quot;Blog&quot;&amp;gt;
  SELECT * FROM BLOG
  &amp;lt;where&amp;gt;
    &amp;lt;if test=&quot;state != null&quot;&amp;gt;
         state = #{state}
    &amp;lt;/if&amp;gt;
    &amp;lt;if test=&quot;title != null&quot;&amp;gt;
        AND title like #{title}
    &amp;lt;/if&amp;gt;
    &amp;lt;if test=&quot;author != null and author.name != null&quot;&amp;gt;
        AND author_name like #{author.name}
    &amp;lt;/if&amp;gt;
  &amp;lt;/where&amp;gt;
&amp;lt;/select&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Mybatis Configuration&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mybatis configuration xml file&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;xml 설정 파일에 관한 내용은 &lt;a href=&quot;https://www.egovframe.go.kr/wiki/doku.php?id=egovframework:rte2:psl:dataaccess:configuration_xml&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;해당 링크&lt;/a&gt;를 참고하시면 됩니다.&lt;/li&gt;
&lt;li&gt;cache, lazy loading, multipleResultSets, typeHandlers, typeAliases 등의 다양한 설정의 사용여부를 결정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1770561385336&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;!DOCTYPE configuration PUBLIC &quot;-//mybatis.org//DTD Config 3.0//EN&quot;
&quot;http://mybatis.org/dtd/mybatis-3-config.dtd&quot;&amp;gt;
 
&amp;lt;configuration&amp;gt;
 
	&amp;lt;properties resource=&quot;org/mybatis/example/config.properties&quot;&amp;gt;
		&amp;lt;property name=&quot;username&quot; value=&quot;dev_user&quot;/&amp;gt;
		&amp;lt;property name=&quot;password&quot; value=&quot;F2Fa3!33TYyg&quot;/&amp;gt;
	&amp;lt;/properties&amp;gt;
 
	&amp;lt;settings&amp;gt;
		&amp;lt;setting name=&quot;cacheEnabled&quot; value=&quot;true&quot;/&amp;gt;
		&amp;lt;setting name=&quot;lazyLoadingEnabled&quot; value=&quot;true&quot;/&amp;gt;
		&amp;lt;setting name=&quot;multipleResultSetsEnabled&quot; value=&quot;true&quot;/&amp;gt;
	&amp;lt;/settings&amp;gt;
 
	&amp;lt;typeHandlers&amp;gt;
		&amp;lt;typeHandler handler=&quot;egovframework.rte.psl.dataaccess.typehandler.CalendarMapperTypeHandler&quot; /&amp;gt;
	&amp;lt;/typeHandlers&amp;gt;
 
	&amp;lt;typeAliases&amp;gt;
		&amp;lt;typeAlias alias=&quot;deptVO&quot; type=&quot;egovframework.rte.psl.dataaccess.vo.DeptVO&quot; /&amp;gt;
		&amp;lt;typeAlias alias=&quot;empVO&quot; type=&quot;egovframework.rte.psl.dataaccess.vo.EmpVO&quot; /&amp;gt;
	.
	.
	. 
 
&amp;lt;/typeAliases&amp;gt;

&amp;lt;/configuration&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;MybatisConfiguration.class&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring에서 Mybatis를 사용하기 위한 bean (SqlSessionFactoryBean)을 등록합니다.&lt;/li&gt;
&lt;li&gt;Datasource 설정(DB 연동) 주입, sqlmap-config, mapper xml 파일 위치 지정, typeAliases 등록을 담당합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1770561968873&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@MapperScan(
        basePackages = &quot;example.**.mapper&quot;,
        annotationClass = Mapper.class
)
public class MybatisConfiguration {

    @Autowired
    private MybatisProperties properties;

    /**
     * local 환경: 개발 편의를 위해 Mapper XML 변경을 감지해 SqlSessionFactory를 재로딩하는 FactoryBean 사용
     * dev/prod 환경: 기본 SqlSessionFactoryBean 사용 (안정성과 성능 우선)
     *
     * - DataSource: MyBatis가 사용할 DB 커넥션 소스
     * - configLocation: MyBatis 전역 설정 파일 위치 (예: settings, typeHandlers 등)
     * - typeAliasesPackage: DTO/VO 등을 별칭으로 등록해 XML에서 짧은 이름으로 사용
     * - mapperLocations: SQL이 정의된 Mapper XML 경로
     */

    @Bean
    @Profile(&quot;local&quot;)
    public SqlSessionFactoryBean sqlSessionFactoryBeanLocal(
            DataSource dataSource,
            ApplicationContext applicationContext
    ) throws IOException {

        // RefreshableSqlSessionFactoryBean은 프로젝트 커스텀 확장 클래스라고 가정
        // (운영 환경에는 사용하지 않는 것이 일반적)
        SqlSessionFactoryBean factoryBean = new RefreshableSqlSessionFactoryBean();

        factoryBean.setDataSource(dataSource);
        factoryBean.setConfigLocation(applicationContext.getResource(&quot;classpath:sqlmap/sqlmap-config.xml&quot;));
        factoryBean.setTypeAliasesPackage(properties.getTypeAliasesPackage());
        factoryBean.setMapperLocations(applicationContext.getResources(&quot;classpath*:sqlmap/mappers/**/*.xml&quot;));

        // XML 리프레시 주기 (너무 짧으면 불필요한 리소스 사용 가능)
        ((RefreshableSqlSessionFactoryBean) factoryBean).setInterval(1000);

        return factoryBean;
    }

    @Bean
    @Profile({&quot;dev&quot;, &quot;prod&quot;})
    public SqlSessionFactoryBean sqlSessionFactoryBeanDevProd(
            DataSource dataSource,
            ApplicationContext applicationContext
    ) throws IOException {

        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();

        factoryBean.setDataSource(dataSource);
        factoryBean.setConfigLocation(applicationContext.getResource(&quot;classpath:sqlmap/sqlmap-config.xml&quot;));
        factoryBean.setTypeAliasesPackage(properties.getTypeAliasesPackage());
        factoryBean.setMapperLocations(applicationContext.getResources(&quot;classpath*:sqlmap/mappers/**/*.xml&quot;));

        return factoryBean;
    }

    /**
     * SqlSessionTemplate은 MyBatis의 SqlSession을 스프링에 맞게 래핑한 구현체.
     * - 스레드 세이프(Thread-safe)하며
     * - 스프링 트랜잭션과 자연스럽게 연동되고
     * - Mapper 호출 시 내부적으로 이 SqlSessionTemplate이 사용됨
     */
    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}&lt;/code&gt;&lt;/pre&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;Mybatis Component&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;SqlMapConfig.xml&lt;/b&gt; : MyBatis의 전역 설정을 정의하는 XML 파일&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SqlSessionFactoryBuilder&lt;/b&gt; : &lt;u&gt;&lt;b&gt;MyBatis 설정 파일을 읽어&lt;/b&gt;&lt;/u&gt; SqlSessionFactory를 생성하는 빌더 클래스, 일반적으로 애플리케이션 시작 시 &lt;u&gt;&lt;b&gt;한 번만 사용&lt;/b&gt;&lt;/u&gt;되며, SqlSessionFactory 생성 후에는 더 이상 필요하지 않습니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SqlSessionFactoryBean&lt;/b&gt; : SqlSessionFactoryBean은 MyBatis 설정과 DataSource를 기반으로 &lt;u&gt;&lt;b&gt;SqlSessionFactory를 빌드한 뒤, Spring DI 컨테이너에 빈으로 등록&lt;/b&gt;&lt;/u&gt;하는 역할을 담당합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SqlSessionFactory&lt;/b&gt; : SqlSession 인스턴스를 생성하는 팩토리 클래스입니다. &lt;u&gt;&lt;b&gt;애플리케이션 생명주기 동안 단 하나만 존재하는 싱글톤으로 관리되며 Thread-Safe 하므로&lt;/b&gt;&lt;/u&gt; 여러 스레드에서 안전하게 공유할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SqlSessionTemplate&lt;/b&gt; : SqlSession 인터페이스를 구현한 싱글톤 기반의 Sql Session 컴포넌트입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SqlSession&lt;/b&gt; : 데이터베이스와의 상호작용을 담당하는 핵심 인터페이스입니다. &lt;u&gt;&lt;b&gt;SQL 실행&lt;/b&gt;&lt;/u&gt;, 트랜잭션 관리(commit/rollback), Mapper 인터페이스 획득 등의 기능을 제공합니다. &lt;u&gt;&lt;b&gt;Thread-Safe 하지 않으므로 각 스레드(요청)마다 새로운 인스턴스를 생성하여 사용해야 합니다.&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MapperFactoryBean : &lt;/b&gt;싱글톤 형태의 Mapper 객체를 생성하여 Spring DI 컨테이너에 등록하는 컴포넌트입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Mapping 파일 (Mapper XML)&lt;/b&gt; : &lt;u&gt;&lt;b&gt;SQL 쿼리문과 Java 객체 간의 매핑 관계를 정의하는 XML 파일&lt;/b&gt;&lt;/u&gt;입니다. SELECT, INSERT, UPDATE, DELETE 등의 SQL문을 정의하고, 파라미터 타입과 결과 타입을 지정합니다. 동적 SQL 작성을 위한 &amp;lt;if&amp;gt; &amp;lt;choose&amp;gt; &amp;lt;foreach&amp;gt; 등의 태그를 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Mybatis Flow (Client - Server 관점)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-02-07 오후 6.45.30.png&quot; data-origin-width=&quot;1852&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LbMyT/dJMcagxB73V/Q9qczAPw7j5JJauubFgZJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LbMyT/dJMcagxB73V/Q9qczAPw7j5JJauubFgZJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LbMyT/dJMcagxB73V/Q9qczAPw7j5JJauubFgZJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLbMyT%2FdJMcagxB73V%2FQ9qczAPw7j5JJauubFgZJk%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;531&quot; height=&quot;58&quot; data-filename=&quot;스크린샷 2026-02-07 오후 6.45.30.png&quot; data-origin-width=&quot;1852&quot; data-origin-height=&quot;202&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Controller &amp;rarr; Service&lt;/b&gt; : 클라이언트로부터 HTTP(S) 요청이 들어오면 Controller는 이를 Service 계층으로 전달합니다. Service는 비즈니스 로직을 처리하는 과정에서 데이터베이스 접근이 필요할 때 Mapper를 호출합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Service &amp;rarr; Mapper&lt;/b&gt; : Serivce는 Repository 역할을 하는 Mapper 인터페이스의 메서드를 호출합니다. 개발자는 SQL을 직접 작성하지 않고, MyBatis가 Mapper 인터페이스와 XML(또는 애노테이션)을 통해 SQL 실행을 처리하도록 위임합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SqlSession이 MappedStatement 조회&lt;/b&gt;: Mapper 인터페이스가 호출되면 내부적으로 SqlSession이 해당 메서드와 매핑된 SQL 정보(MappedStatement)를 Configuration에서 조회합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mapper 인터페이스의 메서드명은 XML에 정의된 SQL 구문과 매핑&lt;/li&gt;
&lt;li&gt;xml과 Mapper 인터페이스의 메서드에서 정의한 파라미터 타입 (parameterType)과 리턴 타입 (resultType, resultMap) 검증&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SQL 실행&lt;/b&gt; : MyBatis는 XML(또는 애노테이션)에 정의된 SQL을 JDBC를 통해 실제 데이터베이스에 전달하고 실행합니다. select, insert, update, delete 등 모든 종류의 쿼리가 이 단계에서 수행됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SQL 결과를 자바 객체로 매핑&lt;/b&gt; : DB에서 가져온 ResultSet은 MyBatis의 매핑 기능을 통해 DTO/VO/엔티티 형태의 자바 객체로 변환됩니다. resultType, resultMap 등을 활용해 자동 또는 수동으로 매핑됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;결과 반환&lt;/b&gt; : 매핑된 Java 객체가 Mapper에서 Service로 반환되고, Service는 필요한 비즈니스 로직을 수행한 후 최종 결과를 Controller로 전달합니다. Controller는 이를 클라이언트에게 응답으로 반환합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mybatis의 설정파일, 컴포넌트와 Client-Server 관점에서의 동작과정을 알아보았으니, 이제 컴포넌트 기반으로 순수 Mybatis와 Spring-Mybatis의 Architecture에 대해서도 살펴보겠습니다.&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;Mybatis Architecture&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;순수 Mybatis Architecture&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-02-09 오전 12.27.01.png&quot; data-origin-width=&quot;1918&quot; data-origin-height=&quot;1144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FOjNL/dJMcadnllnj/vyaIQByB3pfJU7IAC4elC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FOjNL/dJMcadnllnj/vyaIQByB3pfJU7IAC4elC1/img.png&quot; data-alt=&quot;순수 Mybatis 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FOjNL/dJMcadnllnj/vyaIQByB3pfJU7IAC4elC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFOjNL%2FdJMcadnllnj%2FvyaIQByB3pfJU7IAC4elC1%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;585&quot; height=&quot;349&quot; data-filename=&quot;스크린샷 2026-02-09 오전 12.27.01.png&quot; data-origin-width=&quot;1918&quot; data-origin-height=&quot;1144&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;순수 Mybatis 구조&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;&lt;b&gt;Processing to run once (초기화 - 한 번만 실행) (1-3)&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;1. 애플리케이션은 SqlSessionFactoryBuilder에게 SqlSessionFactory 생성을 요청합니다.&lt;/li&gt;
&lt;li&gt;2. SqlSessionFactoryBuilder는 SqlSessionFactory 생성을 위해 MyBatis 설정 파일(MyBatis Configuration File)을 읽습니다.&lt;/li&gt;
&lt;li&gt;3. 설정 파일에 정의된 내용을 기반으로 SqlSessionFactory를 생성합니다.&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;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;Processing to run per requests (요청마다 실행) (4-10)&lt;/span&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;4. 클라이언트가 애플리케이션에 요청을 보냅니다.&lt;/li&gt;
&lt;li&gt;5. 애플리케이션은 SqlSessionFactoryBuilder에 의해 이미 생성된 SqlSessionFactory로부터 SqlSession을 요청합니다.&lt;/li&gt;
&lt;li&gt;6. SqlSessionFactory는 새로운 SqlSession을 생성하여 애플리케이션에 반환합니다.&lt;/li&gt;
&lt;li&gt;7. 애플리케이션은 SqlSession으로부터 Mapper 인터페이스의 구현 객체를 조회합니다.&lt;/li&gt;
&lt;li&gt;8. 애플리케이션은 Mapper 인터페이스의 메서드를 호출합니다.&lt;/li&gt;
&lt;li&gt;9. Mapper 인터페이스의 구현 객체는 내부적으로 SqlSession의 메서드를 호출하여 SQL 실행을 요청합니다.&lt;/li&gt;
&lt;li&gt;10. SqlSession은 매핑 파일(XML)에서 실행할 SQL을 조회한 뒤, 이를 데이터베이스에 전달하여 실행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mybatis-Spring Architecture&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-02-09 오전 12.37.11.png&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;1060&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/COKsK/dJMcagEqLSm/MrT191nOJsDoQhUlwPquZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/COKsK/dJMcagEqLSm/MrT191nOJsDoQhUlwPquZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/COKsK/dJMcagEqLSm/MrT191nOJsDoQhUlwPquZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCOKsK%2FdJMcagEqLSm%2FMrT191nOJsDoQhUlwPquZK%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;569&quot; height=&quot;421&quot; data-filename=&quot;스크린샷 2026-02-09 오전 12.37.11.png&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;1060&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;Processing to run once (초기화 - 한 번만 실행) (1-4)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1. SqlSessionFactoryBean이 SqlSessionFactoryBuilder에게 SqlSessionFactory 생성을 요청합니다.&lt;/li&gt;
&lt;li&gt;2. SqlSessionFactoryBuilder는 &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;MyBatis 설정 파일을 읽어 SqlSessionFactory 생성을 준비합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;3. 설정 정보에 따라 &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;SqlSessionFactory를 생성합니다. 이후 &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;생성된 SqlSessionFactory는 &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Spring DI 컨테이너에 저장됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;4. MapperFactoryBean은 스레드 안전한 SqlSessionTemplate과 Mapper 인터페이스의 프록시 객체를 생성합니다. 생성된 Mapper 객체는 Spring DI 컨테이너에 등록되며, Service 등 싱글톤 컴포넌트에 주입됩니다. Mapper 객체는 Thread-safe한 SqlSessionTemplate을 사용하여 동작합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;Processing to run per requests (요청마다 실행) (5-11)&lt;/span&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;5. 클라이언트가 애플리케이션에 요청을 보냅니다.&lt;/li&gt;
&lt;li&gt;6. 애플리케이션(Service)은 &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Spring DI 컨테이너로부터 주입받은 Mapper 객체(프록시)의 메서드를 호출합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;7. Mapper 객체는 &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;호출된 메서드에 대응하는 SqlSession(SqlSessionTemplate)의 메서드를 실행합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;8. SqlSession(SqlSessionTemplate)은 &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;트랜잭션과 연동된 프록시 기반의 스레드 안전한 SqlSession 메서드를 호출합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;9. 프록시가 적용된 스레드 안전한 SqlSession은 트랜잭션에 할당된 MyBatis3 표준 SqlSession을 사용합니다. (트랜잭션에 할당된 SqlSession이 존재하지 않는 경우, SqlSessionFactory의 메서드가 호출되어 MyBatis3 표준 SqlSession을 조회합니다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;10. SqlSessionFactory는 MyBatis3 표준 SqlSession을 반환합니다. (반환된 MyBatis3 표준 SqlSession은&lt;br /&gt;트랜잭션에 할당되며, 동일한 트랜잭션 내에서는 새로운 인스턴스를 생성하지 않고 동일한 SqlSession이 재사용됩니다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;11. MyBatis3 표준 SqlSession은 매핑 파일에서 실행할 SQL을 조회한 후, 해당 SQL을 실행합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyBatis의 개념과 동작 과정을 상세히 살펴보며 매우 유용한 기술이라는 생각이 들었습니다. 해당 기술을 회사에서 사용하면서 내부 동작 과정과 구조를 이해하는 것이 중요하다고 느꼈고, 이를 정리하는 과정에서 전반적으로 MyBatis를 대략적으로나마 이해하는 데 도움이 많이 되었던 것 같습니다. JPA, Mybatis 모두 각각의 장단점이 있지만, 상황과 요구사항에 맞게 선택적으로 사용하는 것도 좋을 것 같습니다.&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;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;lt;참고 자료&amp;gt;&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;&lt;a href=&quot;https://coding-factory.tistory.com/1171&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://coding-factory.tistory.com/1171&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1770565874539&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Spring] 스프링 MyBatis란 무엇인가? - 동작 원리와 처리 흐름 정리&quot; data-og-description=&quot;스프링에서 DB 연동을 할 때 JPA와 함께 가장 많이 사용되는 기술이 바로 MyBatis(마이바티스)입니다. 실무에서는 JPA보다 훨씬 높은 비중으로 MyBatis를 사용하고 있어요. 특히 MyBatis는 SQL을 직접 작성&quot; data-og-host=&quot;coding-factory.tistory.com&quot; data-og-source-url=&quot;https://coding-factory.tistory.com/1171&quot; data-og-url=&quot;https://coding-factory.tistory.com/1171&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/p958y/dJMb83Sev4D/8nW7VeFY7SvvKhPtiMKE9K/img.png?width=800&amp;amp;height=158&amp;amp;face=0_0_800_158,https://scrap.kakaocdn.net/dn/cgl3GS/dJMb9aKANmo/MXzv0KPTKKXeuBKiZKPfi0/img.png?width=800&amp;amp;height=158&amp;amp;face=0_0_800_158,https://scrap.kakaocdn.net/dn/KTD34/dJMb83koDmC/Ww9yibZP5Myz7NeeHZypZ1/img.png?width=1280&amp;amp;height=254&amp;amp;face=0_0_1280_254&quot;&gt;&lt;a href=&quot;https://coding-factory.tistory.com/1171&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://coding-factory.tistory.com/1171&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/p958y/dJMb83Sev4D/8nW7VeFY7SvvKhPtiMKE9K/img.png?width=800&amp;amp;height=158&amp;amp;face=0_0_800_158,https://scrap.kakaocdn.net/dn/cgl3GS/dJMb9aKANmo/MXzv0KPTKKXeuBKiZKPfi0/img.png?width=800&amp;amp;height=158&amp;amp;face=0_0_800_158,https://scrap.kakaocdn.net/dn/KTD34/dJMb83koDmC/Ww9yibZP5Myz7NeeHZypZ1/img.png?width=1280&amp;amp;height=254&amp;amp;face=0_0_1280_254');&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;[Spring] 스프링 MyBatis란 무엇인가? - 동작 원리와 처리 흐름 정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;스프링에서 DB 연동을 할 때 JPA와 함께 가장 많이 사용되는 기술이 바로 MyBatis(마이바티스)입니다. 실무에서는 JPA보다 훨씬 높은 비중으로 MyBatis를 사용하고 있어요. 특히 MyBatis는 SQL을 직접 작성&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;coding-factory.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://terasolunaorg.github.io/guideline/5.2.1.RELEASE/en/ArchitectureInDetail/DataAccessDetail/DataAccessMyBatis3.html#component-structure-of-mybatis-spring&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://terasolunaorg.github.io/guideline/5.2.1.RELEASE/en/ArchitectureInDetail/DataAccessDetail/DataAccessMyBatis3.html#component-structure-of-mybatis-spring&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1770565879558&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;6.2. Database Access (MyBatis3) &amp;mdash; TERASOLUNA Server Framework for Java (5.x) Development Guideline 5.2.1.RELEASE documentation&quot; data-og-description=&quot;How to implement a search process of Entity for different purposes, is explained below. The explanation below is the example wherein a setting is enabled to automatically map column name separated by an underscore in property name with camel case. Implemen&quot; data-og-host=&quot;terasolunaorg.github.io&quot; data-og-source-url=&quot;https://terasolunaorg.github.io/guideline/5.2.1.RELEASE/en/ArchitectureInDetail/DataAccessDetail/DataAccessMyBatis3.html#component-structure-of-mybatis-spring&quot; data-og-url=&quot;https://terasolunaorg.github.io/guideline/5.2.1.RELEASE/en/ArchitectureInDetail/DataAccessDetail/DataAccessMyBatis3.html#component-structure-of-mybatis-spring&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cc7Gvv/dJMb8RRNBJF/wWu2p2FsLJ2uR5PBmCyBy1/img.png?width=978&amp;amp;height=693&amp;amp;face=0_0_978_693,https://scrap.kakaocdn.net/dn/YN1Z0/dJMb8QehSXQ/N6IEKk0kGDE3e6u9FExBDk/img.png?width=932&amp;amp;height=709&amp;amp;face=0_0_932_709,https://scrap.kakaocdn.net/dn/cxeWlk/dJMb8U8Pkah/3UQmS70JeJszZO9SYrxOCk/img.png?width=873&amp;amp;height=537&amp;amp;face=0_0_873_537&quot;&gt;&lt;a href=&quot;https://terasolunaorg.github.io/guideline/5.2.1.RELEASE/en/ArchitectureInDetail/DataAccessDetail/DataAccessMyBatis3.html#component-structure-of-mybatis-spring&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://terasolunaorg.github.io/guideline/5.2.1.RELEASE/en/ArchitectureInDetail/DataAccessDetail/DataAccessMyBatis3.html#component-structure-of-mybatis-spring&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cc7Gvv/dJMb8RRNBJF/wWu2p2FsLJ2uR5PBmCyBy1/img.png?width=978&amp;amp;height=693&amp;amp;face=0_0_978_693,https://scrap.kakaocdn.net/dn/YN1Z0/dJMb8QehSXQ/N6IEKk0kGDE3e6u9FExBDk/img.png?width=932&amp;amp;height=709&amp;amp;face=0_0_932_709,https://scrap.kakaocdn.net/dn/cxeWlk/dJMb8U8Pkah/3UQmS70JeJszZO9SYrxOCk/img.png?width=873&amp;amp;height=537&amp;amp;face=0_0_873_537');&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;6.2. Database Access (MyBatis3) &amp;mdash; TERASOLUNA Server Framework for Java (5.x) Development Guideline 5.2.1.RELEASE documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;How to implement a search process of Entity for different purposes, is explained below. The explanation below is the example wherein a setting is enabled to automatically map column name separated by an underscore in property name with camel case. Implemen&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;terasolunaorg.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.elancer.co.kr/blog/detail/231&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.elancer.co.kr/blog/detail/231&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1770565890496&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;JPA vs Mybatis, 현직 개발자는 이럴 때 사용합니다. I 이랜서 블로그&quot; data-og-description=&quot;서버에서 데이터 베이스를 효율적으로 사용하기 위해 사용하는 JPA와 Mybatis를 실무에서는 언제 어떻게 사용할까요? 이랜서에서 알려드립니다. I 소트프웨어, 소프트웨어 개발자, 네이버 소프트&quot; data-og-host=&quot;www.elancer.co.kr&quot; data-og-source-url=&quot;https://www.elancer.co.kr/blog/detail/231&quot; data-og-url=&quot;https://www.elancer.co.kr/blog/detail/231&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zDkHr/dJMb8Xka5P7/jQTlkroCMJBh6sKA4UlF8K/img.jpg?width=500&amp;amp;height=281&amp;amp;face=0_0_500_281,https://scrap.kakaocdn.net/dn/cFtw4w/dJMb8RRNBJL/9YnY92a7gky1v6h0vCB381/img.jpg?width=500&amp;amp;height=281&amp;amp;face=0_0_500_281,https://scrap.kakaocdn.net/dn/cXZMkj/dJMb8RjXCFl/cuqJapYSOL1FydBKxIrr31/img.jpg?width=1920&amp;amp;height=1200&amp;amp;face=0_0_1920_1200&quot;&gt;&lt;a href=&quot;https://www.elancer.co.kr/blog/detail/231&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.elancer.co.kr/blog/detail/231&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zDkHr/dJMb8Xka5P7/jQTlkroCMJBh6sKA4UlF8K/img.jpg?width=500&amp;amp;height=281&amp;amp;face=0_0_500_281,https://scrap.kakaocdn.net/dn/cFtw4w/dJMb8RRNBJL/9YnY92a7gky1v6h0vCB381/img.jpg?width=500&amp;amp;height=281&amp;amp;face=0_0_500_281,https://scrap.kakaocdn.net/dn/cXZMkj/dJMb8RjXCFl/cuqJapYSOL1FydBKxIrr31/img.jpg?width=1920&amp;amp;height=1200&amp;amp;face=0_0_1920_1200');&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;JPA vs Mybatis, 현직 개발자는 이럴 때 사용합니다. I 이랜서 블로그&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;서버에서 데이터 베이스를 효율적으로 사용하기 위해 사용하는 JPA와 Mybatis를 실무에서는 언제 어떻게 사용할까요? 이랜서에서 알려드립니다. I 소트프웨어, 소프트웨어 개발자, 네이버 소프트&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.elancer.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://coding-factory.tistory.com/1171&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://coding-factory.tistory.com/1171&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1770565903481&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Spring] 스프링 MyBatis란 무엇인가? - 동작 원리와 처리 흐름 정리&quot; data-og-description=&quot;스프링에서 DB 연동을 할 때 JPA와 함께 가장 많이 사용되는 기술이 바로 MyBatis(마이바티스)입니다. 실무에서는 JPA보다 훨씬 높은 비중으로 MyBatis를 사용하고 있어요. 특히 MyBatis는 SQL을 직접 작성&quot; data-og-host=&quot;coding-factory.tistory.com&quot; data-og-source-url=&quot;https://coding-factory.tistory.com/1171&quot; data-og-url=&quot;https://coding-factory.tistory.com/1171&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/p958y/dJMb83Sev4D/8nW7VeFY7SvvKhPtiMKE9K/img.png?width=800&amp;amp;height=158&amp;amp;face=0_0_800_158,https://scrap.kakaocdn.net/dn/cgl3GS/dJMb9aKANmo/MXzv0KPTKKXeuBKiZKPfi0/img.png?width=800&amp;amp;height=158&amp;amp;face=0_0_800_158,https://scrap.kakaocdn.net/dn/KTD34/dJMb83koDmC/Ww9yibZP5Myz7NeeHZypZ1/img.png?width=1280&amp;amp;height=254&amp;amp;face=0_0_1280_254&quot;&gt;&lt;a href=&quot;https://coding-factory.tistory.com/1171&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://coding-factory.tistory.com/1171&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/p958y/dJMb83Sev4D/8nW7VeFY7SvvKhPtiMKE9K/img.png?width=800&amp;amp;height=158&amp;amp;face=0_0_800_158,https://scrap.kakaocdn.net/dn/cgl3GS/dJMb9aKANmo/MXzv0KPTKKXeuBKiZKPfi0/img.png?width=800&amp;amp;height=158&amp;amp;face=0_0_800_158,https://scrap.kakaocdn.net/dn/KTD34/dJMb83koDmC/Ww9yibZP5Myz7NeeHZypZ1/img.png?width=1280&amp;amp;height=254&amp;amp;face=0_0_1280_254');&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;[Spring] 스프링 MyBatis란 무엇인가? - 동작 원리와 처리 흐름 정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;스프링에서 DB 연동을 할 때 JPA와 함께 가장 많이 사용되는 기술이 바로 MyBatis(마이바티스)입니다. 실무에서는 JPA보다 훨씬 높은 비중으로 MyBatis를 사용하고 있어요. 특히 MyBatis는 SQL을 직접 작성&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;coding-factory.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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>Backend/Spring</category>
      <category>mybatis</category>
      <category>MyBatis configuration</category>
      <category>Mybatis Framework</category>
      <category>SQL Mapper</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/436</guid>
      <comments>https://daily1313.tistory.com/entry/Spring-Mybatis-Framework%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90#entry436comment</comments>
      <pubDate>Mon, 9 Feb 2026 01:12:56 +0900</pubDate>
    </item>
    <item>
      <title>2025년 12월 회고록</title>
      <link>https://daily1313.tistory.com/entry/2025%EB%85%84-12%EC%9B%94-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2025-12-29-23-56-00.jpeg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4PTQM/dJMcaaqoqiM/UjDX4HXhwDodJAEVRcN3Pk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4PTQM/dJMcaaqoqiM/UjDX4HXhwDodJAEVRcN3Pk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4PTQM/dJMcaaqoqiM/UjDX4HXhwDodJAEVRcN3Pk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4PTQM%2FdJMcaaqoqiM%2FUjDX4HXhwDodJAEVRcN3Pk%2Fimg.jpg&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;409&quot; data-filename=&quot;KakaoTalk_Photo_2025-12-29-23-56-00.jpeg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&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;돌이켜 봤을 때, 현재 회사에서 얻어간 것들이 너무 많았으며 정말 가치 있는 시간들이었던 것 같습니다. 비록 블로그에 포스팅하지는 않았지만, 개인적으로는 Notion에 꾸준히 정리해 왔고, 이 기록들이 앞으로도 분명 도움이 될 지식이라고 생각하고 있습니다.&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년 반의 시간, 그리고 그 경험을 바탕으로 이직에 성공하기까지의 과정을 조금 더 자세히 정리해 회고록으로 남겨보려 합니다.&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;저는 보안 설루션 회사에서 웹 풀스택 개발 업무를 담당해 왔습니다. 처음에는 백엔드 개발을 중심으로 커리어를 쌓고 싶었지만, 풀스택 업무를 맡게 되면서 생각보다 많은 장점을 경험할 수 있었습니다. 웹 개발에 있어서 전체적인 흐름과 client-server 간 데이터 이동 과정을 쉽게 파악할 수 있었으며, 무엇보다 기능 개발 중 발생하는 오류나 이슈를 분석하고 디버깅하기가 수월했던 것 같습니다. 추가로, 학부 시절에는 단편적으로 rdb(mariadb, mysql)만 사용했었는데, 실무에서 실시간 통계, 로그, 트래픽 데이터를 저장하기 위해 elasticsearch도 사용해 보면서 지식의 폭을 넓힐 수 있었습니다.&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;처음 받았던 업무는 보안 취약점을 개선하는 업무였습니다. csrf 취약점 대응과 디스크 용량 포화 시 third-party 오래된 로그를 제거하는 업무였는데, 지금 돌아보면 당시의 저는 많이 서툴렀던 것 같습니다. 행동 하나하나에 조심해야 한다고 생각했었으며, 신입사원이었던 저는 git에 저의 소스를 올리는 것도 조심스러웠습니다.&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;시간이 지나며 업무에 적응하면서, 커다란 업무도 맡게 되고 책임감도 가지게 되었습니다. 대표적으로 일본 대규모 운영사이트에 단일 장비만 지원했던 기능을 N개의 장비로 확장 지원하도록 db 구조를 개편도 해보고, 설계부터 기능 개발까지의 전 과정인 1-Z를 경험에 있어, 사람들과 아이디어 공유도 하고, 저의 의견을 표출하면서 의사소통의 중요성을 가장 크게 느꼈던 것 같습니다.&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;이러한 과정을 거치면서 이직에 대한 생각도 자연스럽게 들었던 것 같습니다. 현재 업무 특성상, 특정 도메인에 국한되었으며 새로운 도메인과 개발 문화를 경험해보고 싶었습니다. 그래서 본격적으로 올해 5월부터 준비를 했던 것 같습니다. (중간에 드문드문 쉬어간 적도 종종 있었습니다.)&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;처음에는 채용 플랫폼(사람인, 원티드, 잡코리아) 3대장을 기반으로 웹 백엔드 서버개발 직군(주니어 연차 경력직) or 일부 풀스택 직군에 지원했습니다. 조금 할만하다고 생각했던 기업(스스로의 행복한 상상) 들도 허무하게 서류 탈락을 하는 것을 보고 채용 시장이 쉽지 않다는 것을 깨달았습니다. 이뿐만 아니라, 10~20개씩 서류 탈락을 할 땐 오히려 채용 시장을 위안 삼아 더욱 열심히 해야겠다는 마음가짐을 가졌습니다.&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QOR5h/dJMcai2YyLO/a10po1ZzRq2IRBDpvF3Stk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QOR5h/dJMcai2YyLO/a10po1ZzRq2IRBDpvF3Stk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1368&quot; data-origin-height=&quot;1172&quot; data-filename=&quot;KakaoTalk_Photo_2025-12-29-22-09-04 004.png&quot; style=&quot;width: 49.7493%; margin-right: 10px;&quot; data-widthpercent=&quot;50.93&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QOR5h/dJMcai2YyLO/a10po1ZzRq2IRBDpvF3Stk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQOR5h%2FdJMcai2YyLO%2Fa10po1ZzRq2IRBDpvF3Stk%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;1368&quot; height=&quot;1172&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cU2vZN/dJMcafL0g4p/UVMKrKxXWFMIE4SpvXuzIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cU2vZN/dJMcafL0g4p/UVMKrKxXWFMIE4SpvXuzIk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;1334&quot; data-filename=&quot;KakaoTalk_Photo_2025-12-29-22-09-04 005.png&quot; style=&quot;width: 23.9626%; margin-right: 10px;&quot; data-widthpercent=&quot;24.53&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cU2vZN/dJMcafL0g4p/UVMKrKxXWFMIE4SpvXuzIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcU2vZN%2FdJMcafL0g4p%2FUVMKrKxXWFMIE4SpvXuzIk%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;750&quot; height=&quot;1334&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NRJfi/dJMb9955Coo/JLI34JjfRSzMa9VPTbzjuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NRJfi/dJMb9955Coo/JLI34JjfRSzMa9VPTbzjuk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;1334&quot; data-filename=&quot;KakaoTalk_Photo_2025-12-29-22-09-05 006.png&quot; style=&quot;width: 23.9626%;&quot; data-widthpercent=&quot;24.54&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NRJfi/dJMb9955Coo/JLI34JjfRSzMa9VPTbzjuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNRJfi%2FdJMb9955Coo%2FJLI34JjfRSzMa9VPTbzjuk%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;750&quot; height=&quot;1334&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGqAsn/dJMcacBKsB1/mqprpyKzA1m1stPQ2WxBBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGqAsn/dJMcacBKsB1/mqprpyKzA1m1stPQ2WxBBk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;1334&quot; data-filename=&quot;KakaoTalk_Photo_2025-12-29-23-30-38.png&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGqAsn/dJMcacBKsB1/mqprpyKzA1m1stPQ2WxBBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGqAsn%2FdJMcacBKsB1%2FmqprpyKzA1m1stPQ2WxBBk%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;750&quot; height=&quot;1334&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lhaZz/dJMcaaKG7Yn/LvOMqTZ3VVTukEPwQ5OsL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lhaZz/dJMcaaKG7Yn/LvOMqTZ3VVTukEPwQ5OsL0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;1334&quot; data-filename=&quot;KakaoTalk_Photo_2025-12-29-22-09-03 001.png&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lhaZz/dJMcaaKG7Yn/LvOMqTZ3VVTukEPwQ5OsL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlhaZz%2FdJMcaaKG7Yn%2FLvOMqTZ3VVTukEPwQ5OsL0%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;750&quot; height=&quot;1334&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3jTB4/dJMcaaKG7Yo/iGKfkVkTI5C0Dipwx3UA51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3jTB4/dJMcaaKG7Yo/iGKfkVkTI5C0Dipwx3UA51/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;1334&quot; data-filename=&quot;KakaoTalk_Photo_2025-12-29-22-09-04 002.png&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3jTB4/dJMcaaKG7Yo/iGKfkVkTI5C0Dipwx3UA51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3jTB4%2FdJMcaaKG7Yo%2FiGKfkVkTI5C0Dipwx3UA51%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;750&quot; height=&quot;1334&quot;/&gt;&lt;/span&gt;&lt;/div&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;가장 처음 봤던 면접은 금융권 쪽 계정계 개발 직군이었는데, 기술면접 준비와 더불어 금융 IT 시스템(채널계 - 계정계 - 정보계) 구조와 대해서 숙지하고 들어갔습니다. 하지만, 아쉽게도 1차 면접 후 응답이 없었습니다. 그래도 해당 면접에서는 후회가 없었고, 얻었던 교훈 중 하나는 면접 분위기가 합격을 뜻하지 않는다는 것이었습니다.(면까몰)&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;N사 서비스 기업의 과제 전형을 수행하며 단순히 평가를 넘어 많은 것을 얻을 수 있었습니다. 그동안은 Spring Boot에 내장된 Embedded Tomcat을 주로 사용해 왔지만, 과제 요구사항을 충족하기 위해 Tomcat의 주요 컴포넌트와 내부 동작 과정을 직접 분석하게 되었습니다. 또한 POJO 기반의 소켓 프로그래밍을 구현해 보고, 전체 시스템 아키텍처를 설계하는 과정까지 경험할 수 있어 기술적으로 큰 성장을 느낄 수 있었습니다.&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2025-12-29-22-09-04 003.png&quot; data-origin-width=&quot;3470&quot; data-origin-height=&quot;1380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/egT7CO/dJMcaiPrYGg/w04IaYJNT98In5VPUaO2W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/egT7CO/dJMcaiPrYGg/w04IaYJNT98In5VPUaO2W0/img.png&quot; data-alt=&quot;과제 아키텍처 일부분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/egT7CO/dJMcaiPrYGg/w04IaYJNT98In5VPUaO2W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FegT7CO%2FdJMcaiPrYGg%2Fw04IaYJNT98In5VPUaO2W0%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;729&quot; height=&quot;290&quot; data-filename=&quot;KakaoTalk_Photo_2025-12-29-22-09-04 003.png&quot; data-origin-width=&quot;3470&quot; data-origin-height=&quot;1380&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;면접 과정에서 받은 질문과 답변을 머릿속으로 한 번 정리한 뒤 노션에 기록해 두었습니다.&lt;br /&gt;면접에서 답변을 잘했는지 여부와 관계없이, 이후 면접에서도 유사한 질문이 다시 나올 수 있다고 생각했기 때문입니다.&lt;br /&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;면접 때 받은 질문 목록 (10개만 추출)&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;null, isempty() 변수와 메모리 관점에서 설명해 보세요.&lt;/li&gt;
&lt;li&gt;빈 정의, 목적, 사용이유, 장점, 생명주기에 대해 설명해 보세요.&lt;/li&gt;
&lt;li&gt;JPA saveAll() vs JDBC batchUpdate() 차이와 동작과정 설명해 보세요.&lt;/li&gt;
&lt;li&gt;브라우저 단에서 url 입력 시 이후 과정을 순수 mvc 패턴 과정에서 설명해 보세요.&lt;/li&gt;
&lt;li&gt;springboot에서의 singleton 패턴에 대해 설명해 보세요.&lt;/li&gt;
&lt;li&gt;java에서의 call by value vs call by reference&lt;/li&gt;
&lt;li&gt;stream, collection에서의 foreach 동작차이에 대해 설명해 보세요.&lt;/li&gt;
&lt;li&gt;functional interface 아는 거 설명해 보세요.&lt;/li&gt;
&lt;li&gt;try ~ with ~ resource 이점에 대해 설명해 보세요.&lt;/li&gt;
&lt;li&gt;index 걸 때 기준과 다중 컬럼 인덱스를 걸 때 주의사항에 대해 설명해 보세요.&amp;nbsp;&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;부득이하게, 아직 새로운 회사에 입사하지 않은 상태라 새로운 회사에 대한 내용은 완전히 배제하였습니다. 경력직 입사다 보니 많이 긴장도 되고 떨리지만, 새로운 도메인에서 제 역량을 발휘할 수 있는 좋은 기회이므로 열심히 해야겠네요. 끝.&amp;nbsp; &amp;nbsp;&lt;/p&gt;</description>
      <category>Daily/Retrospect</category>
      <category>2025년 12월 회고록</category>
      <category>개발자 이직</category>
      <category>개발자 회고록</category>
      <category>이직</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/435</guid>
      <comments>https://daily1313.tistory.com/entry/2025%EB%85%84-12%EC%9B%94-%ED%9A%8C%EA%B3%A0%EB%A1%9D#entry435comment</comments>
      <pubDate>Mon, 29 Dec 2025 23:57:49 +0900</pubDate>
    </item>
    <item>
      <title>[Linux] OpenStack VM에서 wget 명령어로 local 서버 파일 다운로드</title>
      <link>https://daily1313.tistory.com/entry/Linux-OpenStack-VM%EC%97%90%EC%84%9C-wget-%EB%AA%85%EB%A0%B9%EC%96%B4%EB%A1%9C-local-%EC%84%9C%EB%B2%84-%ED%8C%8C%EC%9D%BC-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;323&quot; data-origin-height=&quot;334&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q0RGb/dJMcaaRdRyP/iCKuKZ4lPueztY4RNjN6vK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q0RGb/dJMcaaRdRyP/iCKuKZ4lPueztY4RNjN6vK/img.png&quot; data-alt=&quot;https://www.booleanworld.com/download-files-web-pages-wget/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q0RGb/dJMcaaRdRyP/iCKuKZ4lPueztY4RNjN6vK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq0RGb%2FdJMcaaRdRyP%2FiCKuKZ4lPueztY4RNjN6vK%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;323&quot; height=&quot;334&quot; data-origin-width=&quot;323&quot; data-origin-height=&quot;334&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.booleanworld.com/download-files-web-pages-wget/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenStack 환경에서 배포나 테스트 작업을 위해 local 환경에서 패키징한 WAR 파일을 VM으로 옮겨야 했습니다. 보통은 scp 명령을 떠올리지만, 실제로는 SSH 공개키 인증 문제, 키페어 불일치, Security Group 설정 문제 등이 겹치면 생각보다 번거롭습니다.&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;기존에는 pem key를 이용해서 로컬 서버에서 원격 서버로 파일을 전송하려고 했지만, 권한 문제로 인해 파일 전송에 실패하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1763542067310&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PS C:\&amp;gt; scp -i .\br2_rocky9_zt_1104.pem .\project.war rocky@192.168.239.190:/project
Bad permissions. Try removing permissions for user: BUILTIN\\Users (S-1-5-32-545) on file C:/br2_rocky9_zt_1104.pem.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions for './br2_rocky9_zt_1104.pem' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key &quot;./br2_rocky9_zt_1104.pem&quot;: bad permissions
rocky@192.168.239.190: Permission denied (publickey).&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;사실 war 파일만 로컬 서버에서 가져오면 되는 상황이였기에, 복잡한 SSH 키 권한 설정을 맞추며 SCP를 사용하는 대신 더 간단한 방식인 wget으로 직접 파일을 다운로드하는 방법을 선택했습니다.&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;이번 글에서는 이러한 이유로 SCP 대신 wget을 활용해 손쉽게 파일을 전송하는 과정을 정리해 보려고 합니다.&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;사전 세팅&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로컬 PC에서 파일을 제공하는 HTTP 웹 서버를 8000 포트로 실행합니다.&lt;/li&gt;
&lt;li&gt;8000 포트를 통해 Openstack VM에서 wget 명령어로 파일을 다운로드하기 위해 HTTP server를 열어주었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763542128718&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;python -m http.server 8000&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;h2 data-ke-size=&quot;size26&quot;&gt;Openstack VM 서버에서 wget 명령어를 이용한 파일 다운로드 (vm &amp;rarr; windows)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;VM&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP 웹서버를 통해 local 시스템에 존재하는 war 파일을 다운로드할 수 있습니다.&lt;/li&gt;
&lt;li&gt;local system에 HTTP Request를 보냅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763542188846&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[rocky@rocky9-3 target]$ sudo wget http://192.168.229.182:8000/project.war
--2025-11-17 17:08:51--  http://192.168.229.182:8000/project.war
Connecting to 192.168.229.182:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 260118025 (248M) [application/octet-stream]
Saving to: &amp;lsquo;project.war&amp;rsquo;

project.war              100%[=====================================================================================================&amp;gt;] 248.07M   111MB/s    in 2.2s&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Windows&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Windows에서는 python -m http.server 8000 으로 실행한 HTTP 서버가 VM에서 들어오는 HTTP 요청을 받고, 해당 파일을 HTTP Response로 반환합니다.&lt;/li&gt;
&lt;li&gt;즉, VM의 HTTP Request에 대한 응답(Response)로 해당 war파일을 전송하게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763542207022&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PS C:\&amp;gt;  python -m http.server 8000
Serving HTTP on :: port 8000 (http://[::]:8000/) ...
::ffff:192.168.239.190 - - [17/Nov/2025 17:08:40] &quot;GET /project.war HTTP/1.1&quot; 200 -
----------------------------------------
Exception occurred during processing of request from ('::ffff:192.168.239.190', 41750, 0, 0)
Traceback (most recent call last):
  File &quot;C:\Python312\Lib\socketserver.py&quot;, line 692, in process_request_thread
    self.finish_request(request, client_address)
  File &quot;C:\Python312\Lib\http\server.py&quot;, line 1311, in finish_request
    self.RequestHandlerClass(request, client_address, self,
  File &quot;C:\Python312\Lib\http\server.py&quot;, line 672, in __init__
    super().__init__(*args, **kwargs)
  File &quot;C:\Python312\Lib\socketserver.py&quot;, line 761, in __init__
    self.handle()
  File &quot;C:\Python312\Lib\http\server.py&quot;, line 436, in handle
    self.handle_one_request()
  File &quot;C:\Python312\Lib\http\server.py&quot;, line 424, in handle_one_request
    method()
  File &quot;C:\Python312\Lib\http\server.py&quot;, line 679, in do_GET
    self.copyfile(f, self.wfile)
  File &quot;C:\Python312\Lib\http\server.py&quot;, line 878, in copyfile
    shutil.copyfileobj(source, outputfile)
  File &quot;C:\Python312\Lib\shutil.py&quot;, line 204, in copyfileobj
    fdst_write(buf)
  File &quot;C:\Python312\Lib\socketserver.py&quot;, line 840, in write&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;이상으로 VM에서 간단하게 로컬 서버의 war 파일을 가져오는 과정에 대해 알아보았습니다.&lt;/p&gt;</description>
      <category>Linux</category>
      <category>openstack vm</category>
      <category>scp</category>
      <category>wget</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/433</guid>
      <comments>https://daily1313.tistory.com/entry/Linux-OpenStack-VM%EC%97%90%EC%84%9C-wget-%EB%AA%85%EB%A0%B9%EC%96%B4%EB%A1%9C-local-%EC%84%9C%EB%B2%84-%ED%8C%8C%EC%9D%BC-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C#entry433comment</comments>
      <pubDate>Thu, 20 Nov 2025 18:52:12 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] SSL/TLS certificate 무중단 hot-reload 구현 과정</title>
      <link>https://daily1313.tistory.com/entry/Spring-SSLTLS-certificate-%EB%AC%B4%EC%A4%91%EB%8B%A8-hot-reload-%EA%B5%AC%ED%98%84-%EA%B3%BC%EC%A0%95</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사내 웹 솔루션은 HTTPS 요청 처리 시 SSL 인증서를 기반으로 동작합니다. 따라서 인증서의 유효기간이 만료되면, 각 사이트 담당 엔지니어가 직접 인증서를 갱신하는 절차를 수행해야 합니다. 이 과정에서 인증서가 잘못 적용되거나 구성 오류가 발생할 경우, 시스템 전체 장애로 이어질 수 있는 위험이 존재합니다.&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;이를 예방하기 위해 인증서를 자동으로 갱신하고, 애플리케이션 재시작 없이 즉시 반영(hot-swap)할 수 있는 기능을 도입했습니다. 일반적으로 hot-swap은 시스템의 전원을 끄지 않고 구성 요소를 교체하여 계속 사용할 수 있는 기능을 의미합니다. 웹 애플리케이션 관점에서는 애플리케이션을 중단하지 않고 구성 요소를 변경하고 즉시 반영할 수 있는 기능을 의미합니다.&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 3.2 버전부터는 SSL Bundle 기반의 SSL hot reload 기능을 공식적으로 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(공식 문서 링크: &lt;a href=&quot;https://spring.io/blog/2023/11/07/ssl-hot-reload-in-spring-boot-3-2-0&quot;&gt;https://spring.io/blog/2023/11/07/ssl-hot-reload-in-spring-boot-3-2-0&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 사내 웹 애플리케이션은 해당 버전보다 낮은 Spring Boot를 사용하고 있어, 공식 기능을 활용할 수 없었습니다. 따라서 이를 대체하기 위해 자체 구현한 SSL hot-swap 방식을 도입하였습니다. 간략한 아키텍처에 대한 설명 이후, ssl reload 과정에 대해서도 알아보겠습니다.&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;Architecture&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;architecture.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMRSDN/dJMcabbv8Cb/tKDKAK6pvEXKCQKUzCwhuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMRSDN/dJMcabbv8Cb/tKDKAK6pvEXKCQKUzCwhuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMRSDN/dJMcabbv8Cb/tKDKAK6pvEXKCQKUzCwhuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMRSDN%2FdJMcabbv8Cb%2FtKDKAK6pvEXKCQKUzCwhuK%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;653&quot; height=&quot;151&quot; data-filename=&quot;architecture.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;294&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;운영 환경(Linux)에서는 systemd timer가 주기적으로 실행되며, 인증서의 만료일을 확인해 30일 이하로 남았을 경우 자동으로 SSL 인증서를 갱신합니다. 인증서 갱신이 완료되면 시스템은 웹 애플리케이션의 내부 REST API(/api/ssl-reload)를 호출하여 새로운 인증서를 반영하도록 요청합니다.&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;웹 애플리케이션은 이 요청을 SslReloadRestController에서 받아 SslReloader.reloadSslContext()를 실행합니다. 해당 메서드의 실행 과정은 3단계로 나눠집니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ServletWebServerApplicationContext를 통해 현재 Embedded Tomcat 인스턴스를 가져옵니다.&lt;/li&gt;
&lt;li&gt;가져온 Embedded Tomcat의 모든 HTTPS Connector를 찾아 갱신된 keystore 정보를 반영합니다.&lt;/li&gt;
&lt;li&gt;reloadSslHostConfigs()를 호출해 Tomcat의 SSL 설정과 SSLContext를 새 인증서 기준으로 다시 로딩합니다.&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;이 과정을 통해 JVM이나 애플리케이션 재기동 없이 수행되며, JSSE(Tomcat의 SSL 엔진)가 즉시 새로운 인증서로 TLS 통신을 이어갈 수 있습니다.&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;해당 내용을 쉽게 파악하기 위해, Java 기반 웹 애플리케이션에서 SSL/TLS 설정을 위한 기술인 JSSE와 현재 실행 중인 Embedded Tomcat의 컴포넌트들을 조회하기 위한 클래스인 ServletWebServerApplicationContext에 대해 간단히 설명드리겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ServletWebServerApplicationContext&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WebServer를 생성하는 Factory 객체를 통해 내장 톰캣을 생성하는 클래스&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JSSE&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.ibm.com/docs/ko/i/7.6.0?topic=security-java-secure-socket-extension&quot;&gt;https://www.ibm.com/docs/ko/i/7.6.0?topic=security-java-secure-socket-extension&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JSSE (Java&amp;trade; Secure Socket Extension) 는 TLS (Transport Layer Security)의 기본 메커니즘을 추상화하는 프레임워크&lt;/li&gt;
&lt;li&gt;기본 프로토콜의 복잡도와 특수성을 요약하여 JSSE는 프로그래머가 보안 암호화된 통신을 사용할 수 있도록 하는 동시에 가능한 보안 취약성을 최소화할 수 있게 합니다.&lt;/li&gt;
&lt;li&gt;JSSE (Java Secure Socket Extension) 는 TLS 프로토콜을 사용하여 클라이언트와 서버 간에 보안 암호화된 통신을 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS는 개인정보 보호 및 데이터 무결성을 제공하기 위해 서버 및 클라이언트를 인증하는 방법을 제공합니다. 모든 TLS 통신은 서버와 클라이언트 사이의 &quot;핸드셰이크&quot;로 시작합니다. 핸드셰이크 중에 TLS는 클라이언트와 서버가 서로 통신하기 위해 사용하는 암호 스위트를 협상합니다. 이 암호 스위트는 TLS를 통해 사용 가능한 다양한 보안 기능의 조합입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSSE는 애플리케이션의 보안을 개선하기 위해 다음을 수행합니다.&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;리모트 사용자 ID를 인증합니다.&lt;/li&gt;
&lt;li&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;이제 SslReloader 클래스가 어떻게 런타임중에 SSL/TLS 인증서를 hot-reload 하는지 알아보겠습니다.&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;SslReloadRestController&lt;/h2&gt;
&lt;pre id=&quot;code_1763455437492&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RequestMapping(&quot;/api&quot;)
@RestController
public class SslReloadRestController {

    private static final org.slf4j.Logger logger = getLogger(SslReloadRestController.class);

    @Autowired private SslReloader sslReloader;

    @PostMapping(&quot;/ssl-reload&quot;)
    public ResponseEntity&amp;lt;Void&amp;gt; reloadSsl() {
        logger.warn(&quot;ssl-reload triggered&quot;);
        try {
            sslReloader.reloadSslContext();
            logger.warn(&quot;successfully reloaded ssl&quot;);
            return ResponseEntity.ok().build();
        } catch (Exception e) {
            logger.error(&quot;failed to renew ssl&quot;, e);
            throw new SslReloadFailException();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;reloadSslContext()&lt;/h2&gt;
&lt;pre id=&quot;code_1763455461556&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void reloadSslContext() {
    Tomcat tomcat = ((TomcatWebServer) webServerAppCtx.getWebServer()).getTomcat();
    String newPassword = sslPasswordLoader.loadSslPassword();
    log.info(&quot;New SSL password: {}&quot;, newPassword);

    /**
     * Hot-reload SSL without restarting JVM.
     * Requires the keystore to be already updated externally.
     */
    for (Connector connector : tomcat.getService().findConnectors()) {
        if (HTTPS.equalsIgnoreCase(connector.getScheme()) || connector.getSecure()) {
            try {
                AbstractHttp11JsseProtocol&amp;lt;?&amp;gt; protocol = (AbstractHttp11JsseProtocol&amp;lt;?&amp;gt;) connector.getProtocolHandler();
                protocol.setKeystorePass(newPassword);
                protocol.setKeyPass(newPassword);
                protocol.reloadSslHostConfigs();

                log.info(&quot;reloadSslHostConfigs() called on connector port {}&quot;, connector.getPort());
            } catch (Exception e) {
                log.error(&quot;Failed to reload SSL on port {}&quot;, connector.getPort(), e);
              }
      }
    }
  }&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;AbstractHttp11JsseProtocol
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apache Tomcat(coyote) HTTP/1.1 프로토콜의 추상화 클래스&lt;/li&gt;
&lt;li&gt;JSSE(Java Secure Socket Extension)을 사용하여 HTTPS 연결을 처리하는데 필요한 기능을 제공합니다.&lt;/li&gt;
&lt;li&gt;Tomcat 웹 서버의 SSL/TLS 설정 / 키 설정 / 인증서 관련 설정을 담당합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;setKeyStorePass, setKeyPass
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로 읽어온 패스워드를 Tomcat 프로토콜에 반영합니다.&lt;/li&gt;
&lt;li&gt;Tomcat 내부적으로 SSL 설정 객체(SSLHostConfig)가 이것을 사용해 keystore를 다시 엽니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;reloadSslHostConfigs()
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Tomcat이 기존 SSL 구성을 날리고, keystore 파일/패스워드를 기반으로 SSLContext를 다시 초기화합니다. (&lt;a href=&quot;https://tomcat.apache.org/tomcat-8.5-doc/api/org/apache/tomcat/util/net/AbstractEndpoint.html&quot;&gt;https://tomcat.apache.org/tomcat-8.5-doc/api/org/apache/tomcat/util/net/AbstractEndpoint.html&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;이때, keystore 안에 들어있는 key / crt도 새로 읽습니다.&lt;/li&gt;
&lt;li&gt;해당 커넥터를 통해 처리되는 모든 HTTPS 트래픽(일반 HTTP 요청 + WebSocket 업그레이드 포함)에 새 인증서가 적용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SSLContext
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SSL/TLS 연결에 필요한 암호화 알고리즘, 프로토콜 버전, 인증서 및 키 관리를 설정하는 데 사용되는 모듈&lt;/li&gt;
&lt;li&gt;클라이언트/서버 간의 통신이 보안상 안전하게 이루어질 수 있습니다.&lt;/li&gt;
&lt;li&gt;SSL/TLS 웹사이트와 클라이언트 간의 전송되는 데이터를 암호화하여 인터넷 연결을 보호하기 위한 기술&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;NioEndpoint
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Http11NioProtocol에서 소켓을 수신하며 처리하는 모듈&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테스트 결과&lt;/h2&gt;
&lt;pre id=&quot;code_1763455555876&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;11-14 12:59:39.310  INFO 35784 [https-jsse-nio-8443-exec-6] o.a.t.util.net.NioEndpoint.certificate  :173 - Connector [https-jsse-nio-8443], TLS virtual host [_default_], certificate type [UNDEFINED] configured from keystore [file:/C:/project/target/classes/keystore.jks] using alias [alias] with trust store [null]
11-14 12:59:39.310 DEBUG 35784 [https-jsse-nio-8443-exec-6] o.a.t.util.net.NioEndpoint.certificate  :173 - 
[
SHA-256 fingerprint: 77e072575fe08b627981403c0f1e989f6e70194f3d4cb42764f3a8ecd4be6066
SHA-1 fingerprint: 8dd80833a6378e6e953974cae949e55235a221ad
[
[
  Version: V3
  Subject: CN=none, OU=none, O=none, L=Seoul, ST=Seoul, C=KR
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

  Key:  Sun RSA public key, 2048 bits
  params: null
  modulus: 16497042240796911050787389462675891976338213680411636554357277049075603725957713430218630685336331322564281850705541147745095982431713258030959228068969244979329396317672001436874969102725909310805561870656985888524666236532166710887135714960155276145008500711287423375994968419509997404831166207249793381355923645558646542348374671349136649340942795648129440335665044993665872339251797524752406644009875844524425352334388955404223435203488186054051184558111689223174965649296938627311713724432037018754133295408906813767104384992988306996355012088144295065031729124852078738080237612755179825426930564403567186567947
  public exponent: 65537
  Validity: [From: Tue Nov 26 10:23:00 KST 2024,
               To: Fri Nov 24 10:23:00 KST 2034]
  Issuer: CN=none, OU=none, O=none, L=Seoul, ST=Seoul, C=KR
  SerialNumber: [    01a897b6]

Certificate Extensions: 1
[1]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: C6 0F B6 7B A8 E2 4B 28   6F B3 50 D3 B7 54 21 F0  ......K(o.P..T!.
0010: 21 0B 75 63                                        !.uc
]
]

]
  Algorithm: [SHA256withRSA]
  Signature:
0000: 71 AE D2 1C 55 48 47 36   BC D7 4A 76 65 95 51 7F  q...UHG6..Jve.Q.
0010: 26 2E B3 DA 1F 61 31 31   6C 5D D4 08 6D 0C C2 AD  &amp;amp;....a11l]..m...
0020: 61 62 37 81 BF 86 01 62   54 FE 5D 72 F9 BC 21 36  ab7....bT.]r..!6
0030: D0 A6 7F D9 72 11 8D CC   27 C5 0E 2E FA 4D 15 6C  ....r...'....M.l
0040: B4 B6 BB 3C 54 94 9C 70   0A 82 0D D8 1E C4 5B 87  ...&amp;lt;T..p......[.
0050: 13 59 5B BA 2E 02 88 F9   BF A8 69 3A 41 DB 3A 18  .Y[.......i:A.:.
0060: 35 57 B0 57 E1 08 F0 C8   3B 98 D4 5E 47 7B E4 F0  5W.W....;..^G...
0070: 7B 75 CC 41 0B 75 64 00   38 F4 6D 94 64 6B 2C A2  .u.A.ud.8.m.dk,.
0080: F4 EC 1E 21 D0 A7 D1 7E   09 DE D0 FD B4 AC A5 E2  ...!............
0090: 29 0A 22 36 91 4F D7 F2   C1 43 06 60 66 63 98 86  ).&quot;6.O...C.`fc..
00A0: 1B B0 ED 9A 2E 63 A9 BC   BA B8 D3 01 BB 97 56 2C  .....c........V,
00B0: DA 8C 29 2C 1D 9A 2E 16   87 47 04 CA E2 A8 CF 9E  ..),.....G......
00C0: 2B 8D 73 D0 1D CF 2A 59   62 5A AD 29 1D 14 93 2D  +.s...*YbZ.)...-
00D0: F2 DD E9 3F 85 14 D4 BA   C3 70 C8 53 F3 E6 37 71  ...?.....p.S..7q
00E0: 9D B3 F1 4E CC 06 36 CB   19 CE 6B 88 CC 97 B9 0B  ...N..6...k.....
00F0: A6 F2 DE C3 C2 14 11 98   8B C1 C6 8C A1 86 8F 6D  ...............m

]
]
11-14 12:59:39.311  INFO 35784 [https-jsse-nio-8443-exec-6] com.example.api.ssl.SslReloader      :49 - reloadSslHostConfigs() ca0lled on connector port 8443&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;systemd timer에 의해 서버 시스템에서 SSL 인증서가 갱신된 후, 내부 REST API(/api/ssl-reload, POST)를 호출하면 위와 같이 Embedded Tomcat 로그에서 새로운 keystore 기반으로 인증서가 재로딩되는 것을 확인할 수 있습니다.&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>embedded tomcat</category>
      <category>hot-swap</category>
      <category>JSSE</category>
      <category>reloadSslHostConfigs()</category>
      <category>ssl hot-reload</category>
      <category>SSL/TLS</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/432</guid>
      <comments>https://daily1313.tistory.com/entry/Spring-SSLTLS-certificate-%EB%AC%B4%EC%A4%91%EB%8B%A8-hot-reload-%EA%B5%AC%ED%98%84-%EA%B3%BC%EC%A0%95#entry432comment</comments>
      <pubDate>Tue, 18 Nov 2025 22:53:00 +0900</pubDate>
    </item>
    <item>
      <title>[백예린 단독 콘서트] &amp;lt;Flash and Core&amp;gt; In Inspire Arena 후기</title>
      <link>https://daily1313.tistory.com/entry/%EB%B0%B1%EC%98%88%EB%A6%B0-%EB%8B%A8%EB%8F%85-%EC%BD%98%EC%84%9C%ED%8A%B8-Flash-and-Core-In-Inspire-Arena-%ED%9B%84%EA%B8%B0</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;2025/10/26일에 오랜만에 열린 백예린 단독 콘서트를 다녀왔습니다. (3번째)&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;&lt;a href=&quot;https://map.naver.com/p/search/%EC%9D%B8%EC%8A%A4%ED%8C%8C%EC%9D%B4%EC%96%B4%20%EC%95%84%EB%A0%88%EB%82%98/place/1997613956?placePath=%3Fentry%3Dpll%26from%3Dnx%26fromNxList%3Dtrue&amp;amp;placeSearchOption=entry%3Dpll%26fromNxList%3Dtrue%26x%3D126.891732%26y%3D37.476909&amp;amp;searchType=place&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://map.naver.com/p/search/%EC%9D%B8%EC%8A%A4%ED%8C%8C%EC%9D%B4%EC%96%B4%20%EC%95%84%EB%A0%88%EB%82%98/place/1997613956?placePath=%3Fentry%3Dpll%26from%3Dnx%26fromNxList%3Dtrue&amp;amp;placeSearchOption=entry%3Dpll%26fromNxList%3Dtrue%26x%3D126.891732%26y%3D37.476909&amp;amp;searchType=place&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1761555847465&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;네이버 지도&quot; data-og-description=&quot;인스파이어 아레나&quot; data-og-host=&quot;map.naver.com&quot; data-og-source-url=&quot;https://map.naver.com/p/search/%EC%9D%B8%EC%8A%A4%ED%8C%8C%EC%9D%B4%EC%96%B4%20%EC%95%84%EB%A0%88%EB%82%98/place/1997613956?placePath=%3Fentry%3Dpll%26from%3Dnx%26fromNxList%3Dtrue&amp;amp;placeSearchOption=entry%3Dpll%26fromNxList%3Dtrue%26x%3D126.891732%26y%3D37.476909&amp;amp;searchType=place&quot; data-og-url=&quot;https://map.naver.com/p/search/%EC%9D%B8%EC%8A%A4%ED%8C%8C%EC%9D%B4%EC%96%B4%20%EC%95%84%EB%A0%88%EB%82%98/place/1997613956?placePath=?entry=pll&amp;amp;from=nx&amp;amp;fromNxList=true&amp;amp;placeSearchOption=entry=pll&amp;amp;fromNxList=true&amp;amp;x=126.891732&amp;amp;y=37.476909&amp;amp;searchType=place&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cChXdz/hyZMsOPJz3/jt3Je8oJkK3FzFIKGsaYKk/img.jpg?width=256&amp;amp;height=256&amp;amp;face=0_0_256_256,https://scrap.kakaocdn.net/dn/xG45O/hyZMg9Sg9B/BLrkap1psbuQcA1GlfNIh1/img.jpg?width=256&amp;amp;height=256&amp;amp;face=0_0_256_256&quot;&gt;&lt;a href=&quot;https://map.naver.com/p/search/%EC%9D%B8%EC%8A%A4%ED%8C%8C%EC%9D%B4%EC%96%B4%20%EC%95%84%EB%A0%88%EB%82%98/place/1997613956?placePath=%3Fentry%3Dpll%26from%3Dnx%26fromNxList%3Dtrue&amp;amp;placeSearchOption=entry%3Dpll%26fromNxList%3Dtrue%26x%3D126.891732%26y%3D37.476909&amp;amp;searchType=place&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://map.naver.com/p/search/%EC%9D%B8%EC%8A%A4%ED%8C%8C%EC%9D%B4%EC%96%B4%20%EC%95%84%EB%A0%88%EB%82%98/place/1997613956?placePath=%3Fentry%3Dpll%26from%3Dnx%26fromNxList%3Dtrue&amp;amp;placeSearchOption=entry%3Dpll%26fromNxList%3Dtrue%26x%3D126.891732%26y%3D37.476909&amp;amp;searchType=place&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cChXdz/hyZMsOPJz3/jt3Je8oJkK3FzFIKGsaYKk/img.jpg?width=256&amp;amp;height=256&amp;amp;face=0_0_256_256,https://scrap.kakaocdn.net/dn/xG45O/hyZMg9Sg9B/BLrkap1psbuQcA1GlfNIh1/img.jpg?width=256&amp;amp;height=256&amp;amp;face=0_0_256_256');&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;네이버 지도&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;인스파이어 아레나&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;map.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-10-27 175555.png&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;818&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WlZWA/dJMcaajfNWN/cwqF7poC3EjL0tTKwSxSi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WlZWA/dJMcaajfNWN/cwqF7poC3EjL0tTKwSxSi0/img.png&quot; data-alt=&quot;인스파이어 아레나 셔틀 버스 장소&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WlZWA/dJMcaajfNWN/cwqF7poC3EjL0tTKwSxSi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWlZWA%2FdJMcaajfNWN%2FcwqF7poC3EjL0tTKwSxSi0%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;590&quot; height=&quot;818&quot; data-filename=&quot;스크린샷 2025-10-27 175555.png&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;818&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;p data-ke-size=&quot;size16&quot;&gt;도착 이후, 물품 보관소에 짐을 맡기고 공연장 입실을 위해 대기하였습니다. (줄이 매우 길었지만, 생각보다 빨리 내부에 들어갈 수 있었습니다.) 공연 시작 시간보다 2시간 일찍 도착하였지만, 크게 의미가 없어 보였습니다.&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cckOMh/dJMcabJetzn/7xiWyxI2sWQHqsgYKju331/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cckOMh/dJMcabJetzn/7xiWyxI2sWQHqsgYKju331/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_20251027_175406937_04.jpg&quot; width=&quot;485&quot; height=&quot;647&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cckOMh/dJMcabJetzn/7xiWyxI2sWQHqsgYKju331/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcckOMh%2FdJMcabJetzn%2F7xiWyxI2sWQHqsgYKju331%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/58hgy/dJMcafkyWiK/kjWYMxxMB2RuE2JTJuPJq1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/58hgy/dJMcafkyWiK/kjWYMxxMB2RuE2JTJuPJq1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_20251027_175406937_11.jpg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/58hgy/dJMcafkyWiK/kjWYMxxMB2RuE2JTJuPJq1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F58hgy%2FdJMcafkyWiK%2FkjWYMxxMB2RuE2JTJuPJq1%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;INSPIRE ARENA &amp;amp; 대기열&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;p data-ke-size=&quot;size16&quot;&gt;저랑 친구는 D 스탠딩석에서 관람을 진행하였습니다. 정말 자리가 좋은 자리였지만, 다리가 너무 아파서 스탠딩 좌석으로 재관람은 어려울 것 같습니다. 다들 붙어 있기에 생각보다 불편한 것도 컸지만, 퀄리티 좋은 공연이 이를 전부 다 상쇄시켰습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-10-27 175612.png&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MLRrP/dJMcahbBYfx/ia2XviVwmXwmT1oyoXTbQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MLRrP/dJMcahbBYfx/ia2XviVwmXwmT1oyoXTbQ1/img.png&quot; data-alt=&quot;백예린 콘서트 좌석표&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MLRrP/dJMcahbBYfx/ia2XviVwmXwmT1oyoXTbQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMLRrP%2FdJMcahbBYfx%2Fia2XviVwmXwmT1oyoXTbQ1%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;584&quot; height=&quot;520&quot; data-filename=&quot;스크린샷 2025-10-27 175612.png&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;520&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20251027_175406937_08.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BQsN5/dJMcaeTuzWJ/VjS1bHMbJrvKHouZ6dnmm0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BQsN5/dJMcaeTuzWJ/VjS1bHMbJrvKHouZ6dnmm0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BQsN5/dJMcaeTuzWJ/VjS1bHMbJrvKHouZ6dnmm0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBQsN5%2FdJMcaeTuzWJ%2FVjS1bHMbJrvKHouZ6dnmm0%2Fimg.jpg&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;640&quot; height=&quot;853&quot; data-filename=&quot;KakaoTalk_20251027_175406937_08.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/efX4kU/dJMcajtJlXE/qr8fto4d2bpmwGANIgn310/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/efX4kU/dJMcajtJlXE/qr8fto4d2bpmwGANIgn310/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_20251027_175406937_06.jpg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/efX4kU/dJMcajtJlXE/qr8fto4d2bpmwGANIgn310/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FefX4kU%2FdJMcajtJlXE%2Fqr8fto4d2bpmwGANIgn310%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd3ois/dJMcae61Lqk/tPwXD7Z4lAkVjuGlFssq4K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd3ois/dJMcae61Lqk/tPwXD7Z4lAkVjuGlFssq4K/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_20251027_175406937_07.jpg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd3ois/dJMcae61Lqk/tPwXD7Z4lAkVjuGlFssq4K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd3ois%2FdJMcae61Lqk%2FtPwXD7Z4lAkVjuGlFssq4K%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;INSPIRE ARENA 내부&lt;/figcaption&gt;
&lt;/figure&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;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9xhGJ/dJMcacuBBNX/SesZ52VkQAFHmVFIb63ibK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9xhGJ/dJMcacuBBNX/SesZ52VkQAFHmVFIb63ibK/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;KakaoTalk_20251027_175406937_02.jpg&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9xhGJ/dJMcacuBBNX/SesZ52VkQAFHmVFIb63ibK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9xhGJ%2FdJMcacuBBNX%2FSesZ52VkQAFHmVFIb63ibK%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bH8lo3/dJMcajHgw4x/CSuc7mPA70dhh77haouImk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bH8lo3/dJMcajHgw4x/CSuc7mPA70dhh77haouImk/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;KakaoTalk_20251027_175406937_03.jpg&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bH8lo3/dJMcajHgw4x/CSuc7mPA70dhh77haouImk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbH8lo3%2FdJMcajHgw4x%2FCSuc7mPA70dhh77haouImk%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;1&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&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xJHZy/dJMcacuBBOd/AWBlpkE30i0nvlOi2oaOF0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xJHZy/dJMcacuBBOd/AWBlpkE30i0nvlOi2oaOF0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;KakaoTalk_20251027_175406937_01.jpg&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xJHZy/dJMcacuBBOd/AWBlpkE30i0nvlOi2oaOF0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxJHZy%2FdJMcacuBBOd%2FAWBlpkE30i0nvlOi2oaOF0%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zQv9m/dJMcahinyPD/CDrd3T49Oy9jEeCdkZkr2K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zQv9m/dJMcahinyPD/CDrd3T49Oy9jEeCdkZkr2K/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;KakaoTalk_20251027_175406937.jpg&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zQv9m/dJMcahinyPD/CDrd3T49Oy9jEeCdkZkr2K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzQv9m%2FdJMcahinyPD%2FCDrd3T49Oy9jEeCdkZkr2K%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;2&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;아무래도 콘서트명이 신규 앨범명인 &quot;Flash And Core&quot;이다 보니, 신곡 위주의 노래로 진행되었습니다. 모르는 노래가 많았지만, 음색이 너무 좋아서 대부분 인상깊게 들었던 것 같습니다. (노래 내용에 대한 스포는 하지 않겠습니다.) 콘서트가 끝나고 라이브때 들었던 노래를 다시 듣기 위해 플레이리스트에 백예린 신곡 노래를 다시 추가하였습니다.&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;</description>
      <category>Daily/Review</category>
      <category>2025 백예린 콘서트</category>
      <category>Flash and Core</category>
      <category>inspire arena</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/431</guid>
      <comments>https://daily1313.tistory.com/entry/%EB%B0%B1%EC%98%88%EB%A6%B0-%EB%8B%A8%EB%8F%85-%EC%BD%98%EC%84%9C%ED%8A%B8-Flash-and-Core-In-Inspire-Arena-%ED%9B%84%EA%B8%B0#entry431comment</comments>
      <pubDate>Tue, 28 Oct 2025 00:07:58 +0900</pubDate>
    </item>
    <item>
      <title>[CS] Socket Communication</title>
      <link>https://daily1313.tistory.com/entry/CS-Socket-Communication</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;오늘은 네트워크 프로그래밍에서 중요한 소켓 통신(Socket Communication)에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;315&quot; data-start=&quot;124&quot; data-ke-size=&quot;size16&quot;&gt;먼저 소켓 통신이 무엇인지 간단히 살펴본 뒤, 네트워크에서 통신이 성립되는 과정인 TCP 3-way Handshake 절차를 설명드리겠습니다. 이어서 소켓 통신에서 Client와 Server의 역할과 흐름이 어떻게 구분되는지 살펴본 후, Java에서 이를 구현하는 예제를 통해 구체적인 통신 과정을 정리해 보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소켓 통신 (Socket Communication)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-26 오전 12.57.54.png&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MHxYs/btsQQiaUHae/2oNs6mgpEDccv9kw7KpOg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MHxYs/btsQQiaUHae/2oNs6mgpEDccv9kw7KpOg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MHxYs/btsQQiaUHae/2oNs6mgpEDccv9kw7KpOg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMHxYs%2FbtsQQiaUHae%2F2oNs6mgpEDccv9kw7KpOg0%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;576&quot; height=&quot;185&quot; data-filename=&quot;스크린샷 2025-09-26 오전 12.57.54.png&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;/figure&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;양방향 통신: 상호 간의 데이터를 송수신하는 기술&lt;/li&gt;
&lt;li&gt;두 컴퓨터 간에 IP주소 + 포트번호의 조합으로 네트워크 간의 연결을 수행하며 수신자는 데이터를 요청하면 서버에서 응답을 제공해 주는 구조로 데이터를 송수신할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소켓 통신 관련 개념&lt;/h2&gt;
&lt;table id=&quot;26fac551-863b-802f-8df2-fbd99e92ad40&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr id=&quot;26fac551-863b-80a1-8f4c-e1da1fc70e4d&quot;&gt;
&lt;td id=&quot;_]]w&quot;&gt;용어&lt;/td&gt;
&lt;td id=&quot;bCvY&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;26fac551-863b-807b-a4b8-e07154491674&quot;&gt;
&lt;td id=&quot;_]]w&quot;&gt;패킷(Packet)&lt;/td&gt;
&lt;td id=&quot;bCvY&quot;&gt;네트워크를 통해 전송되는 데이터의 기본 단위 (출발지, 목적지 정보, 전송되는 데이터)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;26fac551-863b-8048-9686-f783587d1d55&quot;&gt;
&lt;td id=&quot;_]]w&quot;&gt;소켓(Socket)&lt;/td&gt;
&lt;td id=&quot;bCvY&quot;&gt;네트워크를 통해 데이터(패킷)를 전송하기 위한 통로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;26fac551-863b-8011-aa59-e6c3263ba958&quot;&gt;
&lt;td id=&quot;_]]w&quot;&gt;채널(Channel)&lt;/td&gt;
&lt;td id=&quot;bCvY&quot;&gt;서버와 클라이언트 간에 데이터를 주고 받는 전송 경로, 이 채널을 통해 데이터는 송신자에서 수신자로 전달되며, 데이터의 전송방향(일방향 또는 양방향)과 전송 모드에 따라 구분&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;26fac551-863b-806d-bbbf-dd2a202c62c0&quot;&gt;
&lt;td id=&quot;_]]w&quot;&gt;클라이언트(Client)&lt;/td&gt;
&lt;td id=&quot;bCvY&quot;&gt;데이터를 요청하는 측으로 서버와 연결을 하여 서버로부터 데이터를 받아들이는 역할을 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;26fac551-863b-80ee-b1c7-e7d8ad913d3c&quot;&gt;
&lt;td id=&quot;_]]w&quot;&gt;서버(Server)&lt;/td&gt;
&lt;td id=&quot;bCvY&quot;&gt;데이터를 요청 받아 클라이언트에게 전송하는 역할 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;26fac551-863b-80da-96bd-c4a8ee0a4cf4&quot;&gt;
&lt;td id=&quot;_]]w&quot;&gt;TCP(Transmission Control Protocol)&lt;/td&gt;
&lt;td id=&quot;bCvY&quot;&gt;연결 지향적인 프로토콜, 데이터를 전송하기 전에 송신자와 수신자 간의 연결을 설정합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;26fac551-863b-80bd-ae41-fdd258bd23a3&quot;&gt;
&lt;td id=&quot;_]]w&quot;&gt;UDP(User Datagram Protocol)&lt;/td&gt;
&lt;td id=&quot;bCvY&quot;&gt;비연결 지향적인 프로토콜, 패킷을 수신자에게 보내고 패킷이 정확하게 도착했는지 확인하지 않습니다. (TCP보다 속도가 빠르지만, 패킷 순서나 손실에 대한 보장이 없어 신뢰도가 낮습니다.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;26fac551-863b-80f3-8b70-f12af11da102&quot;&gt;
&lt;td id=&quot;_]]w&quot;&gt;HandShake&lt;/td&gt;
&lt;td id=&quot;bCvY&quot;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소켓 통신의 정의와 사용되는 개념에 대해 알아보았으니, 이제 네트워크 연결 수립을 위한 TCP 3 way handshake 과정과 Client-Server 간 어떻게 통신이 이루어지는 지에 대해서 상세히 설명드리겠습니다. 연결 종료 과정의 TCP 4 way handshake 과정은 생략하도록 하겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;네트워크 연결: TCP 3 way handshake&lt;/h2&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;SYN: 동기화 패킷으로 TCP 연결을 초기화하는 데 사용하며, 클라이언트가 서버에 연결을 시작하고자 할 때 보내는 패킷입니다.&lt;/li&gt;
&lt;li&gt;SYN-ACK: 동기화 패킷에 대한 응답으로 SYN 패킷을 받은 서버가 클라이언트에게 보내는 패킷, 이 패킷을 통해 서버가 클라이언트의 연결 요청을 수락하고, 클라이언트와 서버 간의 연결이 성립됩니다.&lt;/li&gt;
&lt;li&gt;ACK: 확인 패킷으로 데이터가 성공적으로 수신되었음을 보내는 측에 알리는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Client-Server 간 Socket Communication 과정&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-26 오전 1.15.22.png&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;1158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VsRGG/btsQPKFw7xM/YsfWKuqUuk5KkNasbNqm80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VsRGG/btsQPKFw7xM/YsfWKuqUuk5KkNasbNqm80/img.png&quot; data-alt=&quot;소켓 통신 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VsRGG/btsQPKFw7xM/YsfWKuqUuk5KkNasbNqm80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVsRGG%2FbtsQPKFw7xM%2FYsfWKuqUuk5KkNasbNqm80%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;536&quot; height=&quot;592&quot; data-filename=&quot;스크린샷 2025-09-26 오전 1.15.22.png&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;1158&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;소켓 통신 과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Client&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-26 오전 1.14.59.png&quot; data-origin-width=&quot;344&quot; data-origin-height=&quot;934&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBcVwb/btsQOja3owt/zUpgsmKczuI3NkAxpqYhIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBcVwb/btsQOja3owt/zUpgsmKczuI3NkAxpqYhIK/img.png&quot; data-alt=&quot;TCP Client&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBcVwb/btsQOja3owt/zUpgsmKczuI3NkAxpqYhIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBcVwb%2FbtsQOja3owt%2FzUpgsmKczuI3NkAxpqYhIK%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;175&quot; height=&quot;475&quot; data-filename=&quot;스크린샷 2025-09-26 오전 1.14.59.png&quot; data-origin-width=&quot;344&quot; data-origin-height=&quot;934&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TCP Client&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;socket(): client socket 생성, 서버에 연결 요청을 보내기 위해서 사용합니다.&lt;/li&gt;
&lt;li&gt;connect(): 클라이언트가 생성한 소켓을 사용하여 서버에 연결 요청을 보냅니다. (TCP 3-way handshake)&lt;/li&gt;
&lt;li&gt;write(): 클라이언트에서 소켓을 통해 출발지, 목적지 IP, Port 정보와 데이터를 서버로 전송합니다.&lt;/li&gt;
&lt;li&gt;read(): 클라이언트 요청에 따른 서버의 응답 값을 전달받습니다.&lt;/li&gt;
&lt;li&gt;close(): 연결을 끊기 위해 FIN 패킷 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(TCP 4-way handshake에서 사용되는 패킷)을&lt;/span&gt; 서버로 전송합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Server&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-26 오전 1.17.02.png&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;1418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3BvxE/btsQQJTCYqg/VTeWB8RsMyxxWJWSeUOA5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3BvxE/btsQQJTCYqg/VTeWB8RsMyxxWJWSeUOA5k/img.png&quot; data-alt=&quot;TCP Server&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3BvxE/btsQQJTCYqg/VTeWB8RsMyxxWJWSeUOA5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3BvxE%2FbtsQQJTCYqg%2FVTeWB8RsMyxxWJWSeUOA5k%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;223&quot; height=&quot;673&quot; data-filename=&quot;스크린샷 2025-09-26 오전 1.17.02.png&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;1418&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TCP Server&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;socket(): 클라이언트의 연결 요청을 확인하기 위해 서버 소켓을 생성합니다.&lt;/li&gt;
&lt;li&gt;bind(): 생성한 소켓을 특정 IP와 포트번호에 연결합니다. 이 과정을 통해 외부의 연결 요청을 해당 서버의 IP 주소와 Port 번호로 받을 수 있게 됩니다.&lt;/li&gt;
&lt;li&gt;listen(): 클라이언트 요청을 기다립니다. (해당 과정에서 동시에 처리 가능한 연결 요청 수를 지정합니다.)&lt;/li&gt;
&lt;li&gt;accept(): 서버가 클라이언트의 요청을 수락합니다. 연결이 수락되면, 클라이언트와의 통신을 위한 새로운 소켓을 생성합니다.&lt;/li&gt;
&lt;li&gt;read(): 클라이언트로부터 데이터를 수신하고, 이를 잘 가공하여 응답을 생성합니다.&lt;/li&gt;
&lt;li&gt;write(): 클라이언트에게 데이터를 응답합니다.&lt;/li&gt;
&lt;li&gt;read(): 서버가 클라이언트의 추가 요청이나 응답을 읽습니다.&lt;/li&gt;
&lt;li&gt;close(): 소켓 통신을 종료합니다. (입출력 종료)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Java Socket Communication&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;java.net.ServerSocket
&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;java.net.Socket
&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;binding port: 클라이언트가 서버에 접속할 포트
&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;서버가 실행되면 클라이언트는 서버의 IP주소와 바인딩 포트번호로 Socket을 생성하여 연결을 요청합니다.&lt;/li&gt;
&lt;li&gt;이후, SocketSocket은 클라이언트가 연결 요청을 하면 accept() 메서드로 연결을 수락합니다.&lt;/li&gt;
&lt;li&gt;BufferedReader/BufferedWriter, InputStreamReader, OutputStreamWriter
&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;데이터 입출력의 효율을 높이기 위해 데이터를 바로 전달하기 않고, 버퍼를 활용하는 BufferedReader와 BufferedWriter와 함께 사용합니다.&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;이제 Client-Server 관점에서 Socket 통신 과정도 알아보았으니, Java에서 TCP Server, Client를 직접 구현한 코드를 보여드리겠습니다.&amp;nbsp; 소스 코드에 대한 설명은 생략하고, 각 단계별로 주석을 남겼습니다.&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;Java Network Programming - Socket Communication&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Server.class&lt;/h3&gt;
&lt;pre id=&quot;code_1758818160666&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.logging.Logger;

public class Server implements Runnable {

    private static final Logger log = Logger.getLogger(Server.class.getName());
    private static final int PORT = 8000;
    private static final String QUIT_OPTION = &quot;q&quot;;

    @Override
    public void run() {

        // 1. create serversocket (port binding)
        try(ServerSocket serverSocket = new ServerSocket(PORT)) {
            log.info(&quot;[Server]: create server socket(port binding), port number : &quot; + PORT);

            // 2. wait client connection
            Socket socket = serverSocket.accept();
            log.info(&quot;[Client]: connected - IP address : &quot; + socket.getRemoteSocketAddress());

            // 3. create inputstream, outputstream for data transmission/reception
            try(Socket s = socket; Scanner scanner = new Scanner(System.in);
                BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
                BufferedWriter out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()))) {

                while(true) {
                    // 4. receive client message
                    String message = in.readLine();
                    log.info(&quot;[Server]: received message from client: &quot; + message);

                    if(message.equalsIgnoreCase(QUIT_OPTION)) {
                        log.info(&quot;[Server]: quit server by client&quot;);
                        break;
                    }

                    log.info(&quot;[Server]: send message to client&quot;);

                    // 5. send server message
                    String sendMessage = scanner.nextLine();
                    out.write(sendMessage + &quot;\n&quot;);
                    out.flush();

                    if (sendMessage.equalsIgnoreCase(QUIT_OPTION)) {
                        log.info(&quot;[Server]: request stop&quot;);
                        break;
                    }
                }
            }
        } catch (IOException e) {
            log.severe(&quot;I/O error occurred while socket connection&quot;);
            throw new RuntimeException(e);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Client.class&lt;/h3&gt;
&lt;pre id=&quot;code_1758818248118&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import java.util.logging.Logger;

public class Client implements Runnable {

    private static final Logger log = Logger.getLogger(Client.class.getName());
    private static final String HOST = &quot;127.0.0.1&quot;;
    private static final int PORT = 8000;
    private static final String QUIT_OPTION = &quot;q&quot;;

    @Override
    public void run() {
        // 1. create client socket
        // 2. create inputstream, outputstream for data transmission/reception
        try(Socket socket = new Socket(HOST, PORT);
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            Scanner scanner = new Scanner(System.in)) {

            log.info(&quot;[Client]: connect to server - IP : &quot; + HOST + &quot;, port : &quot; + PORT);

            while(true) {
                // 3. send client message
                log.info(&quot;[Client]: send message to server&quot;);

                String sendMessage = scanner.nextLine();

                if(sendMessage.equalsIgnoreCase(QUIT_OPTION)) {
                    log.info(&quot;[Client]: quit client socket communication&quot;);
                }

                out.write(sendMessage + &quot;\n&quot;);
                out.flush();
                
                // 4. receive server message
                String message = in.readLine();
                log.info(&quot;[Client]: received message from server: &quot; + message);

                if(!scanner.hasNextLine()) {
                    break;
                }
            }

        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            log.severe(&quot;I/O error occurred while socket connection&quot;);
            throw new RuntimeException(e);
        }
    }
}&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;h2 data-ke-size=&quot;size26&quot;&gt;실행 결과&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9Pw48/btsQRq0lfZD/ulVXzcWGWnygxcEQ38KMK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9Pw48/btsQRq0lfZD/ulVXzcWGWnygxcEQ38KMK0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;662&quot; data-filename=&quot;스크린샷 2025-09-26 오전 1.28.38.png&quot; style=&quot;width: 48.1275%; margin-right: 10px;&quot; data-widthpercent=&quot;48.69&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9Pw48/btsQRq0lfZD/ulVXzcWGWnygxcEQ38KMK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9Pw48%2FbtsQRq0lfZD%2FulVXzcWGWnygxcEQ38KMK0%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;1060&quot; height=&quot;662&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NqTR4/btsQQiWk79i/0nUiETMURhktRHaU77gyfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NqTR4/btsQQiWk79i/0nUiETMURhktRHaU77gyfK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;652&quot; data-filename=&quot;스크린샷 2025-09-26 오전 1.28.47.png&quot; style=&quot;width: 50.7097%;&quot; data-widthpercent=&quot;51.31&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NqTR4/btsQQiWk79i/0nUiETMURhktRHaU77gyfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNqTR4%2FbtsQQiWk79i%2F0nUiETMURhktRHaU77gyfK%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;1100&quot; height=&quot;652&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;client가 server에게 메시지를 전송하게 되면, server는 client의 메시지를 전달받고 다시 회신할 수 있게 됩니다.&lt;/li&gt;
&lt;li&gt;client, server가 &quot;q&quot;를 전달 및 전송하게 된다면, 소켓 통신이 종료되도록 구성하였습니다.&amp;nbsp;&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;lt;참고 자료&amp;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;a href=&quot;https://adjh54.tistory.com/516&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://adjh54.tistory.com/516&lt;/a&gt;&lt;/p&gt;</description>
      <category>CS</category>
      <category>ServerSocket</category>
      <category>socket</category>
      <category>Socket Communication</category>
      <category>TCP 3 Handshake</category>
      <category>소켓 통신</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/430</guid>
      <comments>https://daily1313.tistory.com/entry/CS-Socket-Communication#entry430comment</comments>
      <pubDate>Fri, 26 Sep 2025 01:48:29 +0900</pubDate>
    </item>
    <item>
      <title>[CS] Concurrency, Parallelism</title>
      <link>https://daily1313.tistory.com/entry/CS-Concurrency-Parallelism</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;동시성 (Concurrency)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Task들이 빠르게 전환되면서 실행되어 동시에 실행되는 것처럼 보이는 것&lt;/li&gt;
&lt;li&gt;싱글 코어(cpu 내에서 실제로 연산 및 명 실행을 담당하는 장치)에서는 여러 작업이 동시적으로 실행되며 Task 간 Context Switch가 발생하면서 여러 작업들을 동시에 실행하는 것처럼 보입니다.&lt;/li&gt;
&lt;li&gt;Context Switch: CPU/코어에서 실행 중이던 프로세스/스레드가 다른 프로세스/스레드로 교체되는 현상&lt;/li&gt;
&lt;li&gt;유휴 시간(idle time)을 최소화하여 CPU 활용도를 높이는 것이 목표입니다. (유후 시간: 컴퓨터가 작동 가능한대도 작업을 하지 않는 시간)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-24 오전 12.45.22.png&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;646&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLhGLy/btsQMDzhHOs/8Ycmv4A89NVKlkafRP6Mk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLhGLy/btsQMDzhHOs/8Ycmv4A89NVKlkafRP6Mk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLhGLy/btsQMDzhHOs/8Ycmv4A89NVKlkafRP6Mk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLhGLy%2FbtsQMDzhHOs%2F8Ycmv4A89NVKlkafRP6Mk0%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;753&quot; height=&quot;409&quot; data-filename=&quot;스크린샷 2025-09-24 오전 12.45.22.png&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;646&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 개 이상의 작업이 싱글 코어 or 다중 코어 CPU의 동일한 코어에서 실행되는 경우, 동시에 동일한 리소스에 접근할 수 있습니다.&lt;/li&gt;
&lt;li&gt;데이터 읽기 작업은 병렬로 수행되고 안전하지만, 쓰기 액세스 시에는 프로그래머가 데이터 무결성을 유지해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;병렬성(Parallellism)&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Example: 분산 시스템&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;시스템에 속한 컴퓨터들은 물리적으로 가까이에 위치하여 로컬 네트워크로 연결될 수도 있으며, 멀리 떨어져 있어 광역 네트워크(WAN)로 연결될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-24 오전 12.46.38.png&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ubFUA/btsQL7gvxLG/nM6Xk72RuKoVAKUFLOGye1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ubFUA/btsQL7gvxLG/nM6Xk72RuKoVAKUFLOGye1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ubFUA/btsQL7gvxLG/nM6Xk72RuKoVAKUFLOGye1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FubFUA%2FbtsQL7gvxLG%2FnM6Xk72RuKoVAKUFLOGye1%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;772&quot; height=&quot;463&quot; data-filename=&quot;스크린샷 2025-09-24 오전 12.46.38.png&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Concurrency, Parellelism 비교 정리&lt;/h3&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;/td&gt;
&lt;td&gt;병렬성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;동시에 실행되는 것 같이 보이는 것&lt;/td&gt;
&lt;td&gt;실제로 동시에 여러 작업이 처리되는 것&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;싱글 코어에서 멀티 쓰레드(Multi thread)를 동작 시키는 방식&lt;/td&gt;
&lt;td&gt;멀티 코어에서 멀티 쓰레드(Multi thread)를 동작시키는 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;한번에 많은 것을 처리&lt;/td&gt;
&lt;td&gt;한번에 많은 일을 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&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;&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;Concurrency vs Parellelism&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2개의 코어 있다고 가정합니다.&lt;/li&gt;
&lt;li&gt;동시성의 경우에는 Core-1, 2에서 시간의 흐름에 따라 Task-1, 2를 번갈아가면서 실행합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) 텍스트 편집기 (파일을 저장 및 인쇄할 때 텍스트를 입력할 수도 있습니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;병렬성의 경우는 각 코어에서 Task를 독립적으로 실행합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) Hadoop 기반의 분산 데이터 처리 (클러스터에서 대 규모 데이터 처리를 수반)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-24 오전 12.50.42.png&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z3hWx/btsQM9LkMux/ZGVHwwwMQzzWXM2ZhqGim0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z3hWx/btsQM9LkMux/ZGVHwwwMQzzWXM2ZhqGim0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z3hWx/btsQM9LkMux/ZGVHwwwMQzzWXM2ZhqGim0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz3hWx%2FbtsQM9LkMux%2FZGVHwwwMQzzWXM2ZhqGim0%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;1202&quot; height=&quot;482&quot; data-filename=&quot;스크린샷 2025-09-24 오전 12.50.42.png&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;482&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동시성과 병렬성은 복잡한 개념인 만큼, 깊은 개발 지식을 요구합니다. 동시성 환경을 잘 고려하지 못한다면, deadlocks, race conditions이 발생할 수 있습니다.&lt;/li&gt;
&lt;/ul&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;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크 프로그래밍에서는 여러 클라이언트 요청을 처리하거나 다양한 I/O 작업을 동시에 수행하여 애플리케이션의 성능과 응답성을 크게 향상시킬 수 있습니다.&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;여러 클라이언트 연결 관리, 비동기 데이터 전송, I/O 작업 처리와 같은 작업에 필수적입니다.&lt;/li&gt;
&lt;/ul&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;네트워크를 통해 수신된 대량의 데이터를 처리하여 CPU 사용량이 높은 계산을 수행하는 것과 같은 작업에 유용합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;Thread&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Thread의 2가지 생성 방식&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;1. Thread class를 상속받은 후 run() 메서드 오버라이딩&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class MyThread extends Thread {
    public void run() {
        // Code that runs in the new thread
    }
}
&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;2. Runnable Interface 구현 후, Thread 객체에 전달&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class MyRunnable implements Runnable {
    public void run() {
    // Code that runs in the new thread
    }
}

Thread myThread = new Thread(new MyRunnable());
myThread.start();&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;Thread Safety&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;이를 어길 경우, 위에서 설명드린 race condition, deadlock, data corruption 등이 발생할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Thread Safety를 얻기 위해 자바에서 제공하는 메커니즘&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;1. Synchronized Methods and Blocks&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;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;public synchronized void synchronizedMethod() {
    // Thread-safe code
}
&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;size14&quot;&gt;2. Volatile Variable&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;h3 data-ke-size=&quot;size23&quot;&gt;Synchronization&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;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Java에서 제공하는 동시성 유틸리티&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Executors: 스레드의 수명 주기 관리를 추상화하고, 실행을 위해 작업을 제출하는 방법을 제공합니다.&lt;/li&gt;
&lt;li&gt;Concurrent Collections: 동시 접근에 더 나은 확장성을 제공하는 표준화된 Collection을 제공합니다. (ex) ConcurrentHashMap)&lt;/li&gt;
&lt;li&gt;Future and Callable: 해당 인터페이스들은 비동기 계산을 나타냅니다. Callable은 결과를 반환하고, Future는 Callable의 결과를 보유하고 있습니다.&lt;/li&gt;
&lt;li&gt;Locks: java.util.concurrent.locks 패키지는 synchronized methods/block과 같은 내재된 locks보다 훨씬 더 유연한 locking mechanism을 제공합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동시성 사례: 여러 클라이언트의 연결 관리 (with ThreadPool)&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;ex) ThreadPool을 사용하여 서버에서 들어오는 클라이언트의 연결을 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1758642968747&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ExecutorService executor = Executors.newFixedThreadPool(10);
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
    Socket clientSocket = serverSocket.accept();
    executor.submit(new ClientHandler(clientSocket));
}&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;Asynchronous I/O Operations&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java의 Non-blocking I/O( NIO)와 Asynchronous I/O(AIO)는 동시성 지향 API로, 효율적인 I/O 작업을 가능하게 하여 고성능 네트워크 애플리케이션에 이상적입니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NIO: 비차단 작업을 위해 채널과 버퍼를 활용하여 단일 스레드가 여러 I/O 채널을 관리할 수 있습니다.&lt;/li&gt;
&lt;li&gt;AIO: 비동기 채널을 도입하여 스레드를 차단하지 않고도 확장성이 뛰어난 I/O 작업이 가능합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;병렬성 사례: Parallel Streams, Fork/Join Framework, ComparableFuture&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;1. Parallel Streams&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Parallel Stream은 데이터를 여러 세그먼트로 나누어 각 세그먼트를 병렬로 처리하기에 대용량 데이터세트에 대한 작업 속도가 크게 향상됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; myList = Arrays.asList( &quot;a1&quot; , &quot;a2&quot; , &quot;b1&quot; , &quot;c2&quot; , &quot;c1&quot; ); 
myList.parallelStream() 
      .filter(s -&amp;gt; s.startsWith( &quot;c&quot; )) 
      .forEach(System.out::println); // 병렬 처리
&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;size14&quot;&gt;2. Fork/Join Framework&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;ExecutorService Interface를 구현한 것으로, 스레드 간 부하 분산을 위해 work-stealing algorithm을 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class MyRecursiveTask extends RecursiveTask&amp;lt;Integer&amp;gt; {
    @Override
    protected Integer compute() {
        // Divide task and/or compute
    }
}
&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;size14&quot;&gt;3. CompletableFuture&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java8에서 소개된 비동기 프로그래밍을 위한 강력한 도구입니다.&lt;/li&gt;
&lt;li&gt;이를 통해 Non-blocking 로직을 더욱 쉽게 작성하고, 관리하기 쉽고 깔끔한 방식으로 병렬 작업을 수행할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1758643089004&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CompletableFuture&amp;lt;String&amp;gt; future1 = CompletableFuture.supplyAsync(() -&amp;gt; &quot;Result1&quot;);
CompletableFuture&amp;lt;String&amp;gt; future2 = CompletableFuture.supplyAsync(() -&amp;gt; &quot;Result2&quot;);
CompletableFuture&amp;lt;String&amp;gt; combinedFuture = future1.thenCombine(future2, (r1, r2) -&amp;gt; r1 + &quot; &quot; + r2);&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/cs/concurrency-vs-parallelism&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.baeldung.com/cs/concurrency-vs-parallelism&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/@AlexanderObregon/java-networking-concurrency-vs-parallelism-in-java-70e892e4efe3&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://medium.com/@AlexanderObregon/java-networking-concurrency-vs-parallelism-in-java-70e892e4efe3&lt;/a&gt;&lt;/p&gt;</description>
      <category>CS</category>
      <category>Concurrency</category>
      <category>parallelism</category>
      <category>동시성</category>
      <category>병렬성</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/429</guid>
      <comments>https://daily1313.tistory.com/entry/CS-Concurrency-Parallelism#entry429comment</comments>
      <pubDate>Wed, 24 Sep 2025 01:04:12 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java] 10장. 예외</title>
      <link>https://daily1313.tistory.com/entry/Effective-Java-10%EC%9E%A5-%EC%98%88%EC%99%B8</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;207&quot; data-origin-height=&quot;325&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OJT6a/btsQHefJzAq/pwPnBKYsjySVtCLmKirjQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OJT6a/btsQHefJzAq/pwPnBKYsjySVtCLmKirjQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OJT6a/btsQHefJzAq/pwPnBKYsjySVtCLmKirjQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOJT6a%2FbtsQHefJzAq%2FpwPnBKYsjySVtCLmKirjQK%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;207&quot; height=&quot;325&quot; data-origin-width=&quot;207&quot; data-origin-height=&quot;325&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-69] 예외는 진짜 예외 상황에만 사용하라&lt;/b&gt;&lt;/span&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;잘 설계된 API라면 클라이언트가 정상적인 제어 흐름에서 예외를 사용할 일이 없어야 합니다.
&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;try-catch 블록 안에 들어가면 JVM이 적용할 수 있는 최적화 범위가 제한됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;잘못된 예외 사용 예시&lt;/h4&gt;
&lt;pre id=&quot;code_1758270305543&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try {
    int i = 0;
    while(true)
        range[i++].climb();
} catch (ArrayIndexOutOfBoundsException e) {

}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;표준적인 관용구&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1758270327391&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 표준적인 관용구로 작성한 예
for (Mountain m : range)
	m.climb();&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;Iterable 인터페이스에서 상태 검사 메서드는 hasNext, 상태 의존적 메서드는 next&lt;/li&gt;
&lt;li&gt;외부 동기화 없이 여러 스레드가 동시 접근하는 경우 옵셔널이나 특정 값을 사용합니다.&lt;/li&gt;
&lt;li&gt;성능이 중요한 상황에서 상태 검사 메서드가 상태 의존적 메서드에서의 작업 일부를 중복 수행한다면, Optional이나 특정 값을 선택합니다.&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-70] 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검사 예외 (checked Exception)
&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;try-catch로 붙잡아 처리하거나, throws 적용하여 더 바깥으로 전파해야 합니다.&lt;/li&gt;
&lt;li&gt;해당 메서드를 호출했을 때 발생할 수 있는 유력한 결과임을 API 사용자에게 알려주는 것
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) IOException, SQLException, ClassNotFoundException, FileNotFoundException&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;비검사 예외 (Unchecked Exception)
&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;비검사 예외는 RuntimeException을 상속받는 클래스&lt;/li&gt;
&lt;li&gt;ex) NullPointerException, ArrayIndexOutOfBoundsException, ArithmeticException, IllegalArgumentException&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;활용 방법&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호출하는 쪽에서 복구할 것이라고 판단되면, 검사 예외를 사용합니다. (try~catch, throw())&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;확실하지 않다면 비검사 예외를 사용합니다. (unchecked)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Throwable 구현은 지양하는 것이 좋습니다.&lt;/li&gt;
&lt;li&gt;throwable 클래스는 JVM이나 릴리스에 따라 포맷이 달라질 수 있으므로 메시지 포맷을 기재하지 않습니다.&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-71] 필요 없는 검사 예외 사용은 피하라&lt;/b&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;/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;제대로 활용하면 API와 프로그램의 품질을 높일 수 있습니다.&lt;/li&gt;
&lt;/ul&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;API를 제대로 사용해도 발생할 수 있거나 개발자가 의미 있는 조치를 할 수 있다면 검사 예외를 사용합니다.&lt;/li&gt;
&lt;li&gt;그렇지 않은 경우에는 비검사 예외를 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Example&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AS-IS: 검사 예외를 던지는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1758270398287&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try {
    obj.action(args);
} catch (TheCheckedException e) {
    // 예외 핸들링
}&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;TO-BE-1: 상태 검사 메서드 + 비검사 예외
&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;/ul&gt;
&lt;pre id=&quot;code_1758270421879&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; if (obj.actionPermitted(args)) {
    obj.action(args);
} else {
    // 예외 핸들링
}&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;TO-BE-2: optional 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1758270442495&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public &amp;lt;S extends T&amp;gt; Optional&amp;lt;S&amp;gt; findOne(Example&amp;lt;S&amp;gt; example) {
    try {
        return Optional.of(this.getQuery(new ExampleSpecification(example, this.escapeCharacter), example.getProbeType(), (Sort)Sort.unsorted()).getSingleResult());
    } catch (NoResultException var3) {
        return Optional.empty();
    }
}&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-72] 표준 예외를 사용하라&lt;/b&gt;&lt;/span&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;h4 data-ke-size=&quot;size20&quot;&gt;예외 계층&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;569&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QmF05/btsQHetgPF7/Nlsee7ukp1J2xPmeCKkit0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QmF05/btsQHetgPF7/Nlsee7ukp1J2xPmeCKkit0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QmF05/btsQHetgPF7/Nlsee7ukp1J2xPmeCKkit0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQmF05%2FbtsQHetgPF7%2FNlsee7ukp1J2xPmeCKkit0%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;569&quot; height=&quot;326&quot; data-origin-width=&quot;569&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Throwable: 예외처리를 할 수 있는 최상위 클래스 (Exception, Error가 이를 상속받습니다.)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Exception: 프로그램 실행 도중 발생하는 문제로 에러보다 심각도는 낮으며 수습될 수 있는 문제&lt;/li&gt;
&lt;li&gt;Error: 프로그램 실행 도중 발생하는 문제로, 수습할 수 없는 심각한 문제&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;널리 재사용되는 예외 목록&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IllegalArgumentException
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;허용되지 않는 값이 인수로 건네졌을 때 발생합니다. (null인 경우에는 NullPointerException이 처리)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;IllegalStateException
&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;NullPointerException
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;null을 허용하지 않는 메서드에 null을 건냈을 때 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;IndexOutOfBoundsException
&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;ConcurrentModificationException
&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;UnsupportedOperationException
&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;/li&gt;
&lt;li&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-73] 추상화 수준에 맞는 예외를 던지라&lt;/b&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;예외 번역 (exception translation)&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;pre id=&quot;code_1758270533103&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try {
    // 저수준 추상화를 이용한다.
} catch (LowerLevelException e) {
    // 추상화 수준에 맞게 번역한다.
    throw new HigherLevelException(...);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예외 번역의 대표적인 예시: AbstractSequentialList&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;List&amp;lt;E&amp;gt; 인터페이스의 get 메서드는 예외 번역의 대표적인 예시입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1758270545599&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public E get(int idex) {
   ListIterator&amp;lt;E&amp;gt; i = listIterator(index);
   try {
      return i.next();
   } catch (NoSuchElementException e) {
   	  throw new IndexOutOfBoundsException(&quot;idex: &quot; + idex);
   }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예외 연쇄 (exception chaining)&lt;/h4&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;pre id=&quot;code_1758270560902&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try {
    // 저수준 추상화를 이용한다.
} catch (LowerLevelException e) {
    // 저수준 예외를 고수준 예외에 실어 보낸다.
    throw new HigherLevelException(e);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Example&lt;/h4&gt;
&lt;pre id=&quot;code_1758270574503&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class UserRepository {
    public void save(User u) {
        try {
            // ... JDBC/ORM 저장
        } catch (java.sql.SQLException e) {
		        SQLException -&amp;gt; DataAccessLayerException 으로 전파
            throw new DataAccessLayerException(&quot;Insert user failed&quot;, e.getSQLState(), e);
        }
    }
}&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;li&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-74] 메서드가 던지는 모든 예외를 문서화하라&lt;/b&gt;&lt;/span&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;여기에서 개발자가 직면하는 오류 (Error)와 예외 (Exception)에 대한 구분이 필요합니다. 오류의 경우는 시스템적으로 정상적이지 않은 상황을 말합니다.&lt;/li&gt;
&lt;li&gt;오류 (Error): 시스템적으로 정상적이지 않은 상황을 말합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;application level이 아닌, 더 낮은 low system level에서 발생하기 때문에 처리하기가 더 어렵습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;예외 (Exception): 개발자가 구현한 로직의 코드에서 발생할 수 있습니다.
&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;/ul&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;검사 예외 (checked exception): 항상 따로 하나씩 선언하고 각 예외가 발생하는 상황을 javadoc의 @throws 태그를 사용하여 문서화합니다. (Javadoc 자동 생성: /** + enter)&lt;/li&gt;
&lt;li&gt;비검사 예외 (unchecked exception): 문서화를 진행하면 좋습니다. 일반적으로 프로그래밍의 오류를 뜻하는데 발생할 수 있는 오류를 명시하면 자연스럽게 해당 오류가 발생하지 않도록 개발할 수 있습니다.&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-75] 예외의 상세 메시지에 실패 관련 정보를 담으라&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예외를 잡지 못해 프로그램에 오류가 발생하면, 시스템에서 자동으로 스택 추적 (stack trace) 정보를 출력해 줍니다.&lt;/li&gt;
&lt;li&gt;이때 출력되는 문자열은 Throwable 클래스의 toString 메서드에서 반환하는 클래스 이름과 상세 메시지입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bilWGm/btsQHFjOZgU/JwXOjQfOH8TQpPgV0X32lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bilWGm/btsQHFjOZgU/JwXOjQfOH8TQpPgV0X32lk/img.png&quot; data-alt=&quot;Throwable toString 메서드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bilWGm/btsQHFjOZgU/JwXOjQfOH8TQpPgV0X32lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbilWGm%2FbtsQHFjOZgU%2FJwXOjQfOH8TQpPgV0X32lk%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;440&quot; height=&quot;266&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;266&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Throwable toString 메서드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;예를 들어, IndexOutOfBoundsException이라면, 범위의 최솟값, 최댓값 그리고 범위를 벗어난 인덱스의 값을 담아야 합니다.&lt;/li&gt;
&lt;li&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-76] 가능한 한 실패 원자적으로 만들라&lt;/b&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;실패 원자적 (failure-atomic)&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;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;/li&gt;
&lt;li&gt;로직을 수행하기 전에 매개변수의 유효성을 검사: 내부 상태를 변경하기 전에 예외를 터트립니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Example&lt;/h4&gt;
&lt;pre id=&quot;code_1758270662311&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public Object pop() {
    if (size == 0)
        throw new EmptyStackException();
    Object result = elements[--size];
    elements[size] = null; // 다 쓴 참조 해제
    return result;
}&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-77] 예외를 무시하지 말라&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예외가 선언된 API는 그 메서드를 사용할 때 적절한 조치를 해야 한다는 뜻입니다. &amp;rarr; catch 블록을 비워두면 예외가 존재할 이유가 없습니다.&lt;/li&gt;
&lt;li&gt;예외적인 상황: FileInputStream
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예외를 무시하기로 했다면 catch 블록 안에서 그렇게 결정한 이유를 주석으로 남기며, 예외 변수의 이름도 변경합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1758270680799&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try {
    ...
} catch (SomeException ignored) {
    // 변수 이름은 ignored 등으로 바꾸고,
    // 예외를 무시하되 관련 로그를 남겨둔다.
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Backend/Effective Java</category>
      <category>10장 예외</category>
      <category>Effective Java</category>
      <category>이펙티브 자바</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/428</guid>
      <comments>https://daily1313.tistory.com/entry/Effective-Java-10%EC%9E%A5-%EC%98%88%EC%99%B8#entry428comment</comments>
      <pubDate>Sat, 20 Sep 2025 13:38:37 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java] 9장. 일반적인 프로그래밍 원칙</title>
      <link>https://daily1313.tistory.com/entry/Effective-Java-9%EC%9E%A5-%EC%9D%BC%EB%B0%98%EC%A0%81%EC%9D%B8-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%9B%90%EC%B9%99</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;214&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbGr57/btsQFqNQSKM/IahmuQcQWdOiM55es2ku40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbGr57/btsQFqNQSKM/IahmuQcQWdOiM55es2ku40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbGr57/btsQFqNQSKM/IahmuQcQWdOiM55es2ku40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbGr57%2FbtsQFqNQSKM%2FIahmuQcQWdOiM55es2ku40%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;214&quot; height=&quot;333&quot; data-origin-width=&quot;214&quot; data-origin-height=&quot;333&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-57] 지역변수의 범위를 최소화하라&lt;/b&gt;&lt;/span&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;옛날 방식의 습관으로는 코드 블록이 첫 부분에 변수를 선언하는 경우가 많았습니다.
&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;지역변수를 초기화할 수 없다면, 초기화할 수 있을 때 선언하면 됩니다. (예외 : try-catch)&lt;/li&gt;
&lt;/ul&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;반복문은 while보다 for문을 권장: while문을 사용하면 반복문 밖으로 불필요한 변수가 선언됩니다.&lt;/li&gt;
&lt;li&gt;for문을 사용하면 반복 변수 (loop variable)의 범위가 반복문 내부로 제한됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Example&lt;/h4&gt;
&lt;pre id=&quot;code_1758185369696&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Iterator&amp;lt;Element&amp;gt; i = c.iterator(); // 불필요하다.
while (i.hasNext()) {
    doSomething(i.next());
}

for (Iterator&amp;lt;Element&amp;gt; i = c.iterator(); i.hasNext(); ) {
    Element e = i.next();
    // e와 i로 무언가 한다.
}&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-58] 전통적인 for문보다는 for-each문을 사용하라&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열과 컬렉션의 요소를 탐색할 때 일반적으로 for문을 사용합니다.&lt;/li&gt;
&lt;li&gt;향상된 for문 (for-each)문을 사용하면 명료하고 유연하며 버그를 예방해 줄 뿐만 아니라, 성능 저하도 없습니다.
&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;사용할 수 없는 상황: filtering (필터링), Transforming (변경), Parallel iteration (병렬 순회)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;전통적인 for문&lt;/h4&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;li&gt;컬렉션, 배열 등 컨테이너 종류에 따라 코드 형태가 달라질 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Collection 및 배열 순회 -&amp;gt; for-each 문으로 해결&lt;/h4&gt;
&lt;pre id=&quot;code_1758185432201&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (Iterator&amp;lt;Element&amp;gt; i = c.iterator(); i.hasNext();) {
  Element e = i.next();
}

for (int i = 0; i&amp;lt; a.length; i++) {
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;for-each&lt;/h4&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;h4 data-ke-size=&quot;size20&quot;&gt;Example&lt;/h4&gt;
&lt;pre id=&quot;code_1758185464041&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (Element e : elements) {
}

for (Suit suit : suits)
  for (Rank rank : ranks)
    deck.add(new Card(suit, rank));&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-59] 라이브러리를 익히고 사용하라&lt;/b&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;/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;핵심적인 일과 관련 없는 시간 절약을 할 수 있습니다.&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;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;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;java.lang, java.util, &lt;a href=&quot;http://java.io&quot;&gt;java.io&lt;/a&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;&lt;b&gt;[item-60] 정확한 답이 필요하다면 float와 double은 피하라&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;float과 double은 과학과 공학 계산용도로 설계되었습니다.&lt;/li&gt;
&lt;li&gt;넓은 범위의 수를 빠르고 정밀한 &amp;ldquo;근사치&amp;rdquo;로 계산하도록 설계되었기에 0.1 또는 10의 음이 거듭제곱 등을 표현할 수 없습니다.&lt;/li&gt;
&lt;li&gt;정확한 계산을 위해서 BigDecimal (18자리 초과 10진수) (primitive type보다 사용하기 불편하고 성능적으로 훨씬 느립니다), int(9자리 10진수), long (18자리 10진수) 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 예시&lt;/p&gt;
&lt;pre id=&quot;code_1758185550056&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;System.out.println(1.03 - 0.42); // expected: 0.61, actual: 0.6100000000000001 
System.out.println(1.00 - 9 * 0.10); // expected: 0.1, actual: 0.09999999999999998&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-61] 박싱된 기본 타입보다는 기본 타입을 사용하라 &lt;/b&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;Java의 데이터 타입&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;primitive: int, double, boolean&lt;/li&gt;
&lt;li&gt;reference: String List&lt;/li&gt;
&lt;li&gt;박싱된 기본 타입: Integer, Double, Boolean&lt;/li&gt;
&lt;li&gt;박싱된 기본 타입은 null을 가질 수 있으며, 기본 타입이 박싱된 기본 타입보다 상대적으로 시간, 메모리 사용면에서 더 효율적입니다.&lt;/li&gt;
&lt;/ul&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;박싱된 기본타입의 == 비교 (NullPointerException 발생)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 타입과 박싱된 기본 타입이 혼용되어 연산되기 때문에 NPE가 발생됩니다.&lt;/li&gt;
&lt;li&gt;npe를 방지하기 위해 언박싱된 기본 타입인 int로 선언합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1758185592800&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Unbelievable {
    static Integer i;

    public static void main(String[] args) {
      if (i == 42) {
        System.out.println(&quot;Hello!&quot;);
      } 
    }
}&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;2. 성능 저하&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sum을 박싱된 기본 타입으로 선언하면, 박싱과 언박싱이 반복해서 일어나 성능이 저하됩니다.&lt;/li&gt;
&lt;li&gt;이를 언박싱된 기본 타입인 long으로 변환해 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1758185619688&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static long sum() {
    Long sum = 0L; -&amp;gt; long 변환
    for (long i = 0; i &amp;lt;= Integer.MAX_VALUE; i++&amp;gt;) {
      sum += i;
    }
    return sum;
}&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;컬렉션의 원소 (key, value)에 적용할 경우&lt;/li&gt;
&lt;li&gt;컬렉션에서는 기본 타입을 담을 수 없기 때문에 박싱된 기본 타입을 사용해야 합니다.&lt;/li&gt;
&lt;li&gt;또한 매개변수화 타입이나 메서드의 타입 매개변수로는 박싱된 기본 타입을 사용해야 합니다.&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-62] 다른 타입이 적절하다면 문자열 사용을 피하라&lt;/b&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;/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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;다른 값 타입을 대신할 경우 (int, float, enum, boolean으로 대체가 가능할 경우)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터가 수치형이라면, int, float 등을 사용하고, 예/아니요 형태라면 enum, boolean을 사용해야 합니다.&lt;/li&gt;
&lt;/ul&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;enum type이 월등히 낫습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;혼합 타입을 대신할 경우 (ex) String compundKey = className + &amp;ldquo;#&amp;rdquo; + i.next())
&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문자열을 key로 사용할 경우 전역 이름공간에서 이를 공유하게 됩니다. (같은 이름을 사용할 경우, 충돌 가능성이 있습니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AS-IS&lt;/h4&gt;
&lt;pre id=&quot;code_1758185673520&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ThreadLocal {
    private ThreadLocal() { } // 객체 생성 불가

    // 현재 스레드의 값을 키로 구분해 저장한다.
    public static void set(String key, Object value);

    // 키가 가리키는 현재 스레드의 값을 반환한다.
    public static Object get(String key);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TO-BE&lt;/h4&gt;
&lt;pre id=&quot;code_1758185687664&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ThreadLocal {
    private ThreadLocal() { } // 객체 생성 불가

    public static class Key { // 권한
        Key() { }
    }

    public static Key getKey() {
        return new Key();
    }

    public static void set(Key key, Object value);
    public static Object get(Key key);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;key 자체를 ThreadLocal로 만들기&lt;/h4&gt;
&lt;pre id=&quot;code_1758185700672&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public final class ThreadLocal {
    public ThreadLocal();
    public void set(Object value);
    public Object get();
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;타입 안정성 확보를 위해 제네릭으로 표현 (실제 타입으로 캐스팅)&lt;/h4&gt;
&lt;pre id=&quot;code_1758185715512&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public final class ThreadLocal&amp;lt;T&amp;gt; {
    public ThreadLocal();
    public void set(T value);
    public T get();
}&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-63] 문자열 연결은 느리니 주의하라&lt;/b&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;문자열 연결 연산자로 문자열 n개를 잇는 시간은 n^2에 비례합니다. 문자열은 불변이기 때문에 두 문자열을 연결하려는 경우에는 양쪽의 내용을 모두 복사해야 하기 때문에 성능 저하가 발생합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;String
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;immutable (불변), 그래서 한 번 생성되면 변경도 불가능합니다. (메모리 주소 고정)&lt;/li&gt;
&lt;li&gt;문자열에 + 연산자 등을 이용하여 다른 문자열을 추가할 때 기존 문자열에 새로운 문자열이 추가되는 것이 아니라, 새로운 문자열 객체를 만들고 그 객체를 참조하게 됩니다.&lt;/li&gt;
&lt;li&gt;이전 문자열은 Reference가 사라져 Unreachable 상태가 되어 GC의 대상이 됩니다.&lt;/li&gt;
&lt;li&gt;String을 조작하는 연산은 시간과 자원 (메모리)를 사용하게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;StringBuilder
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동기화 보장 X&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;StringBuffer
&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;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dC9WFm/btsQFvO7vQk/hHHFvN3skHqriRqtNC14ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dC9WFm/btsQFvO7vQk/hHHFvN3skHqriRqtNC14ik/img.png&quot; data-origin-width=&quot;462&quot; data-origin-height=&quot;121&quot; data-is-animation=&quot;false&quot; style=&quot;width: 43.9928%; margin-right: 10px;&quot; data-widthpercent=&quot;44.51&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dC9WFm/btsQFvO7vQk/hHHFvN3skHqriRqtNC14ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdC9WFm%2FbtsQFvO7vQk%2FhHHFvN3skHqriRqtNC14ik%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;462&quot; height=&quot;121&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r63h2/btsQEGXVqEW/Ife9D4igp6vBTLXz3bEyy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r63h2/btsQEGXVqEW/Ife9D4igp6vBTLXz3bEyy0/img.png&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;100&quot; data-is-animation=&quot;false&quot; style=&quot;width: 54.8444%;&quot; data-widthpercent=&quot;55.49&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r63h2/btsQEGXVqEW/Ife9D4igp6vBTLXz3bEyy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr63h2%2FbtsQEGXVqEW%2FIfe9D4igp6vBTLXz3bEyy0%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;476&quot; height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;/div&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;StringBuilder &amp;gt; StringBuffer &amp;gt; String&lt;/li&gt;
&lt;li&gt;String의 경우 연산을 할 때마다 새로운 문제열 객체를 생성해야 하므로 수행 속도가 매우 느립니다.&lt;/li&gt;
&lt;li&gt;StringBuffer의 경우 동기화 기능에 의해 상대적으로 StringBuilder보다 느립니다.&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;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;color: #ee2323;&quot;&gt;&lt;b&gt;[item-64] 객체는 인터페이스를 사용해 참조하라&lt;/b&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;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Example&lt;/h4&gt;
&lt;pre id=&quot;code_1758185859480&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 좋은 예시. 인터페이스 타입으로 사용했다.
Set&amp;lt;Fruit&amp;gt; fruitSet = new LinkedHashSet&amp;lt;&amp;gt;();

// 나쁜 예시. 클래스를 타입으로 사용했다.
LinkedHashSet&amp;lt;Fruit&amp;gt; fruitSet = new LinkedHashSet&amp;lt;&amp;gt;();&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;interface 타입을 사용하면 좋은 점&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;차후에 구현 클래스를 교체할 때 유연합니다. &amp;rarr; 손쉬운 교체로 성능 향상 또는 새로운 성능을 기대할 수 있게 됩니다. (LinkedHashSet &amp;rarr; HashSet, HashMap &amp;rarr; EnumMap)&lt;/li&gt;
&lt;li&gt;가능하다면 인터페이스 타입으로 선언해 사용하는 것이 좋습니다.&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-65] 리플렉션보다는 인터페이스를 사용하라&lt;/b&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;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java Reflection: 이미 로딩이 완료된 클래스에서 또 다른 클래스를 동적으로 로딩하여 생성자, 멤버 필드, 그리고 멤버 메서드 등을 사용하기 위한 기술&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;단점&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;컴파일타임의 타입 검사가 주는 이점이 없어져 각종 런타임 오류가 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;코드가 지저분하고 장황해져 가독성이 떨어질 수 있습니다.&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-66] 네이티브 메서드는 신중히 사용하라&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네이티브 메서드: C, C++과 같이 네이티브 프로그래밍 언어로 작성한 메서드를 말합니다.&lt;/li&gt;
&lt;li&gt;JNI (Java Native Interface) : 자바 프로그램에서 네이티브 메서드를 호출하는 기술&lt;/li&gt;
&lt;/ul&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;/li&gt;
&lt;li&gt;Java보다 플랫폼 종속성이 높고 이식성도 낮으며 디버깅하기도 어렵습니다.&lt;/li&gt;
&lt;li&gt;성능적인 측면에서 속도가 느려질 수 있으며 가비지 컬렉터가 네이티브 메모리는 자동 회수하지 못하며 심지어 추적할 수도 없습니다.&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-67] 최적화는 신중히 하라&lt;/b&gt;&lt;/span&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;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;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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;API를 설계할 때 성능에 주는 영향을 고려해야 합니다.&lt;/li&gt;
&lt;li&gt;각각의 최적화 시도 전후로 성능을 측정해야 합니다.&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;[item-68] 일반적으로 통용되는 명명 규칙을 따르라&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;표준 명명 규칙을 습관화해야 합니다. 자바는 명명 규칙이 잘 정립되어 있기에 이들을 잘 적용하면 됩니다. ex) camelCase&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;자바 명명 규칙&lt;/h4&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;h4 data-ke-size=&quot;size20&quot;&gt;패키지와 모듈&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 요소를 점(.)으로 구분하고 계층으로 짓습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;java.util.function&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;요소는 모두 소문자 알파벳 또는 숫자로 짓습니다.&lt;/li&gt;
&lt;li&gt;외부에서도 사용될 패키지라면, 조직의 인터넷 도메인 이름을 역순으로 사용합니다. (com.google, com.naver)&lt;/li&gt;
&lt;li&gt;각 요소는 일반적으로 8글자 이하의 짧은 단어로 짓습니다. (utilities X, util O)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;클래스, 인터페이스&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나 이상의 단어로 이루어지며, 각 단어는 대문자로 시작합니다. (List, Array)&lt;/li&gt;
&lt;li&gt;여러 단어의 첫 글자는 딴 약자나 max, min과 같이 통용되는 줄임말을 제외하고는 단어를 줄여 쓰지 않습니다.&lt;/li&gt;
&lt;li&gt;약자의 경우는 첫 글자만 대문자로 하는 경우가 많습니다. (HttpUrl)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;메서드와 필드&lt;/h4&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;Camel Case로 작성합니다. (requireNonNull)&lt;/li&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;(NUM_COUNT)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;지역변수&lt;/h4&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;h4 data-ke-size=&quot;size20&quot;&gt;타입 매개변수&lt;/h4&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;T: 임의의 타입 (Type)&lt;/li&gt;
&lt;li&gt;E: 컬렉션 원소 타입 (Element)&lt;/li&gt;
&lt;li&gt;K: 맵의 키 (Key)&lt;/li&gt;
&lt;li&gt;V: 맵의 값 (Value)&lt;/li&gt;
&lt;li&gt;X: 예외 (Exception)&lt;/li&gt;
&lt;li&gt;R: 메서드의 반환 타입 (Return)&lt;/li&gt;
&lt;li&gt;T, U, V, T1, T2, T3: 임의 타입의 시퀀스&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;문법 규칙&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체를 생성할 수 있는 클래스는 단수 명사 또는 명사구를 사용합니다. (Thread, PriorityQueue)&lt;/li&gt;
&lt;li&gt;객체를 생성할 수 없는 클래스는 복수 명사를 사용합니다. (Collectors, Collections)&lt;/li&gt;
&lt;li&gt;인터페이스는 클래스와 동일하며 able, ible로 끝나는 형용사를 사용합니다. (Collection, Comparator, Runnable, Accessible)&lt;/li&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;동사나 동사구를 사용합니다. (append, start)&lt;/li&gt;
&lt;li&gt;boolean을 반환할 경우에는 is, has로 시작하고 명사, 명사구, 형용사로 끝납니다. (isBlank, isEmpty, hasKey)&lt;/li&gt;
&lt;li&gt;boolean을 반환하지 않거나 인스턴스의 속성을 반환한다면 명사, 명사구, get으로 시작하는 동사구를 사용합니다. (size, getList)&lt;/li&gt;
&lt;/ul&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;타입을 바꿔서 달은 타입의 객체를 반환할 경우에는 to로 시작합니다. (toArray, toString)&lt;/li&gt;
&lt;li&gt;객체의 내용을 다른 뷰로 보여주는 메서드는 as로 시작합니다. (asList)&lt;/li&gt;
&lt;li&gt;객체의 값을 기본형(primitive) 타입으로 반환할 경우에는 Value로 끝납니다. (intValue)&lt;/li&gt;
&lt;li&gt;정적 팩터리 메서드의 경우는 from, of, newInstance 등 다양합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Backend/Effective Java</category>
      <category>9장 일반적인 프로그래밍 원칙</category>
      <category>Effective Java</category>
      <category>이펙티브 자바</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/427</guid>
      <comments>https://daily1313.tistory.com/entry/Effective-Java-9%EC%9E%A5-%EC%9D%BC%EB%B0%98%EC%A0%81%EC%9D%B8-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%9B%90%EC%B9%99#entry427comment</comments>
      <pubDate>Thu, 18 Sep 2025 22:46:48 +0900</pubDate>
    </item>
    <item>
      <title>[Design Pattern] Chain of Responsibility Pattern(책임 연쇄 패턴)</title>
      <link>https://daily1313.tistory.com/entry/Design-Pattern-Chain-of-Responsibility-Pattern%EC%B1%85%EC%9E%84-%EC%97%B0%EC%87%84-%ED%8C%A8%ED%84%B4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&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;최근 기업 기술과제를 수행하면서, HTTP Request 시에 보안 규칙 검사를 확장성을 고려하여 책임 연쇄 패턴을 적용하였습니다. 해당 디자인 패턴을 적용하게 된 배경을 설명드리기 전, 개념과 예시를 Spring Security에서 사용되는 Servlet Filter로 들어서 사용 방법을 알아보도록 하겠습니다.&amp;nbsp;&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;Chain of Responsibility (책임 연쇄 패턴)&lt;/h2&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;span style=&quot;background-color: #ffffff; color: #444444; text-align: justify;&quot;&gt;각 핸들러는 요청을 받으면 요청을 처리할지 아니면 체인의 다음 핸들러로 전달할지를&amp;nbsp;결정합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #444444; text-align: justify;&quot;&gt;Spring Security Framework의 Servlet Filter도 책임 연쇄 패턴이 적용된 하나의 좋은 예시입니다.&amp;nbsp;&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;background-color: #ffffff; color: #444444; text-align: justify;&quot;&gt;Spring Security에는 매우 많은 필터가 하나의 Filter Chain으로 구성되어 있습니다. (해당 포스팅에서는 상세한 내용은 생략)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #444444; text-align: justify;&quot;&gt;ex) &lt;span style=&quot;background-color: #ffffff; color: #3d4144; text-align: left;&quot;&gt;SecurityContextHolderFilter, &lt;span style=&quot;background-color: #ffffff; color: #3d4144; text-align: left;&quot;&gt;CsrfFilter, &lt;span style=&quot;background-color: #ffffff; color: #3d4144; text-align: left;&quot;&gt;LogoutFilter, &lt;span style=&quot;background-color: #ffffff; color: #3d4144; text-align: left;&quot;&gt;UsernamePasswordAuthenticationFilter&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Chain of Responsibility 사용 이전&lt;/h2&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-15 오전 1.26.44.png&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;766&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CJ30z/btsQzmkeOmV/aLPYlm7FMKNV7k7XT0Zzdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CJ30z/btsQzmkeOmV/aLPYlm7FMKNV7k7XT0Zzdk/img.png&quot; data-alt=&quot;https://refactoring.guru/ko/design-patterns/chain-of-responsibility&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CJ30z/btsQzmkeOmV/aLPYlm7FMKNV7k7XT0Zzdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCJ30z%2FbtsQzmkeOmV%2FaLPYlm7FMKNV7k7XT0Zzdk%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;672&quot; height=&quot;357&quot; data-filename=&quot;스크린샷 2025-09-15 오전 1.26.44.png&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;766&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://refactoring.guru/ko/design-patterns/chain-of-responsibility&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Chain of Responsibility를 적용하지 않은 상태였다면, 새로운 기능을 추가할 때마다 각각의 기능에 영향을 줄 수도 있습니다. 더불어, 유지보수성과 확장성이 매우 떨어지는 구조가 됩니다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Chain of Responsibility 사용 이후&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-15 오전 1.29.33.png&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m5A7L/btsQx8tuS2B/0qKvteaHf9QjbfXpenGi31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m5A7L/btsQx8tuS2B/0qKvteaHf9QjbfXpenGi31/img.png&quot; data-alt=&quot;https://refactoring.guru/ko/design-patterns/chain-of-responsibility&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m5A7L/btsQx8tuS2B/0qKvteaHf9QjbfXpenGi31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm5A7L%2FbtsQx8tuS2B%2F0qKvteaHf9QjbfXpenGi31%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;1418&quot; height=&quot;368&quot; data-filename=&quot;스크린샷 2025-09-15 오전 1.29.33.png&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;368&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://refactoring.guru/ko/design-patterns/chain-of-responsibility&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 기능을 독립적으로 실행하기 위한 핸들러(=Filter)라는 개념을 도입합니다.&lt;/li&gt;
&lt;li&gt;각 요청마다 데이터와 함께 메서드에 인수로 전달됩니다. (Filter Chain을 통해)&lt;/li&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;&lt;span style=&quot;background-color: #ffffff; color: #444444; text-align: justify;&quot;&gt;Spring Security Filter Interface&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-14 오후 11.09.50.png&quot; data-origin-width=&quot;2154&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqBw6H/btsQzzQ9DHB/J7kRGBInhZlkFVhsshKYKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqBw6H/btsQzzQ9DHB/J7kRGBInhZlkFVhsshKYKK/img.png&quot; data-alt=&quot;jakarta.servlet Filter Interface&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqBw6H/btsQzzQ9DHB/J7kRGBInhZlkFVhsshKYKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqBw6H%2FbtsQzzQ9DHB%2FJ7kRGBInhZlkFVhsshKYKK%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;2154&quot; height=&quot;580&quot; data-filename=&quot;스크린샷 2025-09-14 오후 11.09.50.png&quot; data-origin-width=&quot;2154&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;jakarta.servlet Filter Interface&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-14 오후 11.10.04.png&quot; data-origin-width=&quot;1840&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQiodj/btsQzNPix2R/kwXrVDyIL28O9tQdZdK4uK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQiodj/btsQzNPix2R/kwXrVDyIL28O9tQdZdK4uK/img.png&quot; data-alt=&quot;jakarta.servlet FilterChain&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQiodj/btsQzNPix2R/kwXrVDyIL28O9tQdZdK4uK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQiodj%2FbtsQzNPix2R%2FkwXrVDyIL28O9tQdZdK4uK%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;1840&quot; height=&quot;316&quot; data-filename=&quot;스크린샷 2025-09-14 오후 11.10.04.png&quot; data-origin-width=&quot;1840&quot; data-origin-height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;jakarta.servlet FilterChain&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;메서드에 대한 설명을 보기 위해서는 &lt;a href=&quot;https://daily1313.tistory.com/entry/Spring-Filter-Interceptor-ArgumentResolver&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;해당 자료를 참고&lt;/a&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;클라이언트 요청이 서블릿으로 전달되기 전, 서블릿 컨테이너가 관리하는 필터 체인을 따라 구현된 각 Filter의 doFilter() 메서드가 순차적으로 실행됩니다.&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;Filter 동작 흐름&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청 가로채기 -&amp;gt; 요청 처리 (보안 검사, 로깅, 인증, 인가) -&amp;gt; 필터 체인을 통해 다음 필터로 전달 -&amp;gt; 응답 가로채기 (선택) -&amp;gt; 응답&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;책임 연쇄 패턴을 적용하게 된 이유&lt;/h2&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://curiousjinan.tistory.com/entry/spring-filterchain-dofilter&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://curiousjinan.tistory.com/entry/spring-filterchain-dofilter&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1757865607618&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Spring Boot: 필터에서 doFilter와 FilterChain이란?&quot; data-og-description=&quot;이번 포스트에서는 Spring Filter에 대한 심화적인 이해를 해보도록 하자   서론 Spring Framework에서 필터는 웹 애플리케이션의 요청과 응답을 조작하는 강력한 도구이다. 필터는 DispatcherServlet으로 &quot; data-og-host=&quot;curiousjinan.tistory.com&quot; data-og-source-url=&quot;https://curiousjinan.tistory.com/entry/spring-filterchain-dofilter&quot; data-og-url=&quot;https://curiousjinan.tistory.com/entry/spring-filterchain-dofilter&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bwHaQI/hyZI9QiL0o/NPIybHTG6lCEVRDQEkRaP0/img.png?width=800&amp;amp;height=424&amp;amp;face=0_0_800_424,https://scrap.kakaocdn.net/dn/Jtsfu/hyZJlh1bFq/oq3eKvYgCmsUWKoQrNwIb1/img.png?width=800&amp;amp;height=424&amp;amp;face=0_0_800_424,https://scrap.kakaocdn.net/dn/bXjnRy/hyZI4VMikR/U9ufF52XKO1QTKvS5M2bc0/img.png?width=1634&amp;amp;height=1606&amp;amp;face=0_0_1634_1606&quot;&gt;&lt;a href=&quot;https://curiousjinan.tistory.com/entry/spring-filterchain-dofilter&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://curiousjinan.tistory.com/entry/spring-filterchain-dofilter&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bwHaQI/hyZI9QiL0o/NPIybHTG6lCEVRDQEkRaP0/img.png?width=800&amp;amp;height=424&amp;amp;face=0_0_800_424,https://scrap.kakaocdn.net/dn/Jtsfu/hyZJlh1bFq/oq3eKvYgCmsUWKoQrNwIb1/img.png?width=800&amp;amp;height=424&amp;amp;face=0_0_800_424,https://scrap.kakaocdn.net/dn/bXjnRy/hyZI4VMikR/U9ufF52XKO1QTKvS5M2bc0/img.png?width=1634&amp;amp;height=1606&amp;amp;face=0_0_1634_1606');&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;Spring Boot: 필터에서 doFilter와 FilterChain이란?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 포스트에서는 Spring Filter에 대한 심화적인 이해를 해보도록 하자   서론 Spring Framework에서 필터는 웹 애플리케이션의 요청과 응답을 조작하는 강력한 도구이다. 필터는 DispatcherServlet으로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;curiousjinan.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://junhyunny.github.io/design-pattern/chain-of-responsibility-pattern/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://junhyunny.github.io/design-pattern/chain-of-responsibility-pattern/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1757866953072&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Chain of Responsibility Pattern&quot; data-og-description=&quot;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&quot; data-og-host=&quot;junhyunny.github.io&quot; data-og-source-url=&quot;https://junhyunny.github.io/design-pattern/chain-of-responsibility-pattern/&quot; data-og-url=&quot;https://junhyunny.github.io/design-pattern/chain-of-responsibility-pattern/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/KeULr/hyZIPSxI13/6YjoNykCPQjCFZEw4mNb11/img.png?width=1229&amp;amp;height=466&amp;amp;face=0_0_1229_466,https://scrap.kakaocdn.net/dn/bYM2nV/hyZJmBea6V/0CBkivCcWpnkpQh3kushH0/img.png?width=1136&amp;amp;height=463&amp;amp;face=0_0_1136_463,https://scrap.kakaocdn.net/dn/coOjuY/hyZJs9hID9/SClvkzgbqLnW90aXRwEpTk/img.png?width=981&amp;amp;height=434&amp;amp;face=0_0_981_434&quot;&gt;&lt;a href=&quot;https://junhyunny.github.io/design-pattern/chain-of-responsibility-pattern/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://junhyunny.github.io/design-pattern/chain-of-responsibility-pattern/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/KeULr/hyZIPSxI13/6YjoNykCPQjCFZEw4mNb11/img.png?width=1229&amp;amp;height=466&amp;amp;face=0_0_1229_466,https://scrap.kakaocdn.net/dn/bYM2nV/hyZJmBea6V/0CBkivCcWpnkpQh3kushH0/img.png?width=1136&amp;amp;height=463&amp;amp;face=0_0_1136_463,https://scrap.kakaocdn.net/dn/coOjuY/hyZJs9hID9/SClvkzgbqLnW90aXRwEpTk/img.png?width=981&amp;amp;height=434&amp;amp;face=0_0_981_434');&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;Chain of Responsibility Pattern&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;junhyunny.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://refactoring.guru/ko/design-patterns/chain-of-responsibility&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://refactoring.guru/ko/design-patterns/chain-of-responsibility&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1757866965900&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;책임 연쇄 패턴&quot; data-og-description=&quot;/ 디자인 패턴들 / 행동 패턴 책임 연쇄 패턴 다음 이름으로도 불립니다: CoR,&amp;nbsp;커맨드 사슬,&amp;nbsp;Chain of Responsibility 의도 책임 연쇄 패턴은 핸들러들의 체인​(사슬)​을 따라 요청을 전달할 수 있게 &quot; data-og-host=&quot;refactoring.guru&quot; data-og-source-url=&quot;https://refactoring.guru/ko/design-patterns/chain-of-responsibility&quot; data-og-url=&quot;https://refactoring.guru/ko/design-patterns/chain-of-responsibility&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/5IKWl/hyZIMVOUuj/m3M9HF5qJQIbzng9Qd9g60/img.png?width=640&amp;amp;height=400&amp;amp;face=0_0_640_400,https://scrap.kakaocdn.net/dn/cuHWO5/hyZI0mcShz/6dcEXJjmWS8AILCHkqT4E1/img.png?width=610&amp;amp;height=370&amp;amp;face=0_0_610_370&quot;&gt;&lt;a href=&quot;https://refactoring.guru/ko/design-patterns/chain-of-responsibility&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://refactoring.guru/ko/design-patterns/chain-of-responsibility&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/5IKWl/hyZIMVOUuj/m3M9HF5qJQIbzng9Qd9g60/img.png?width=640&amp;amp;height=400&amp;amp;face=0_0_640_400,https://scrap.kakaocdn.net/dn/cuHWO5/hyZI0mcShz/6dcEXJjmWS8AILCHkqT4E1/img.png?width=610&amp;amp;height=370&amp;amp;face=0_0_610_370');&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;책임 연쇄 패턴&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;/ 디자인 패턴들 / 행동 패턴 책임 연쇄 패턴 다음 이름으로도 불립니다: CoR,&amp;nbsp;커맨드 사슬,&amp;nbsp;Chain of Responsibility 의도 책임 연쇄 패턴은 핸들러들의 체인​(사슬)​을 따라 요청을 전달할 수 있게&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;refactoring.guru&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/Design Pattern</category>
      <category>Chain of Responsibility</category>
      <category>Design Pattern</category>
      <category>Filter</category>
      <category>FilterChain</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/426</guid>
      <comments>https://daily1313.tistory.com/entry/Design-Pattern-Chain-of-Responsibility-Pattern%EC%B1%85%EC%9E%84-%EC%97%B0%EC%87%84-%ED%8C%A8%ED%84%B4#entry426comment</comments>
      <pubDate>Mon, 15 Sep 2025 01:39:30 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] Rocky Linux 9.2에서 docker-compose 기반 db, elasticsearch 구축 과정</title>
      <link>https://daily1313.tistory.com/entry/Docker-Rocky-Linux-92%EC%97%90%EC%84%9C-docker-compose-%EA%B8%B0%EB%B0%98-db-elasticsearch-%EA%B5%AC%EC%B6%95-%EA%B3%BC%EC%A0%95</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사내에서 로컬 환경에서 docker-desktop을 통해 docker-compose 기반으로 mariadb, elasticsearch를 띄운 후 애플리케이션을 실행하는 방식으로 개발 환경을 구성해 왔습니다.&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;하지만, Docker Desktop은 로컬 메모리를 많이 차지하고, WSL2와 Docker를 조합하는 방식도 큰 차이가 없다고 판단하여 별도의 Rocky Linux 서버에 Docker 환경을 구축했습니다.&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;이번 포스팅에서는 Rocky Linux 서버에서 Docker 환경을 구축하는 과정을 상세히 다루고자 합니다. 또한, 단순 설치 과정뿐만 아니라, 외부에서 Web Application과 안전하게 상호작용할 수 있도록 방화벽 설정에 대해서도 함께 설명드리겠습니다. 리눅스 배포판에서는 inbound 트래픽을 전부 차단하도록 방화벽이 설정되어 있기에, 필요한 포트만 명시적으로 열어주어야 외부에서 정상적으로 통신할 수 있습니다.&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 data-ke-size=&quot;size26&quot;&gt;Architecture&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-02 오후 11.31.38.png&quot; data-origin-width=&quot;2532&quot; data-origin-height=&quot;1434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvYpYL/btsQgg0nfLr/9KH9oLm6MKnGnsnUJmZhW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvYpYL/btsQgg0nfLr/9KH9oLm6MKnGnsnUJmZhW0/img.png&quot; data-alt=&quot;Architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvYpYL/btsQgg0nfLr/9KH9oLm6MKnGnsnUJmZhW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvYpYL%2FbtsQgg0nfLr%2F9KH9oLm6MKnGnsnUJmZhW0%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;649&quot; height=&quot;368&quot; data-filename=&quot;스크린샷 2025-09-02 오후 11.31.38.png&quot; data-origin-width=&quot;2532&quot; data-origin-height=&quot;1434&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Rocky Linux 9.2에서 Docker 환경 구축 과정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;OS&amp;nbsp; 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1756825302227&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[root@sb ~]# cat /etc/os-release
NAME=&quot;Rocky Linux&quot;
VERSION=&quot;9.2 (Blue Onyx)&quot;
ID=&quot;rocky&quot;
ID_LIKE=&quot;rhel centos fedora&quot;
VERSION_ID=&quot;9.2&quot;
PLATFORM_ID=&quot;platform:el9&quot;
PRETTY_NAME=&quot;Rocky Linux 9.2 (Blue Onyx)&quot;
ANSI_COLOR=&quot;0;32&quot;
LOGO=&quot;fedora-logo-icon&quot;
CPE_NAME=&quot;cpe:/o:rocky:rocky:9::baseos&quot;
HOME_URL=&quot;https://rockylinux.org/&quot;
BUG_REPORT_URL=&quot;https://bugs.rockylinux.org/&quot;
SUPPORT_END=&quot;2032-05-31&quot;
ROCKY_SUPPORT_PRODUCT=&quot;Rocky-Linux-9&quot;
ROCKY_SUPPORT_PRODUCT_VERSION=&quot;9.2&quot;
REDHAT_SUPPORT_PRODUCT=&quot;Rocky Linux&quot;
REDHAT_SUPPORT_PRODUCT_VERSION=&quot;9.2&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Docker Repository 구축&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sudo dnf -y install dnf-plugins-core&lt;/li&gt;
&lt;li&gt;sudo dnf config-manager --add-repo &lt;a href=&quot;https://download.docker.com/linux/centos/docker-ce.repo&quot;&gt;https://download.docker.com/linux/centos/docker-ce.repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1756825391772&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[root@sb ~]# sudo dnf -y install dnf-plugins-core
Last metadata expiration check: 3:52:39 ago on Tue Sep  2 01:48:50 2025.
Package dnf-plugins-core-4.3.0-5.el9_2.noarch is already installed.
Dependencies resolved.
====================================================================================================================================================================
 Package                                            Architecture                     Version                                 Repository                        Size
====================================================================================================================================================================
Upgrading:
 dnf-plugins-core                                   noarch                           4.3.0-20.el9                            baseos                            36 k
 python3-dnf-plugins-core                           noarch                           4.3.0-20.el9                            baseos                           246 k
 yum-utils                                          noarch                           4.3.0-20.el9                            baseos                            35 k

Transaction Summary
====================================================================================================================================================================
Upgrade  3 Packages

Total download size: 317 k
Downloading Packages:
(1/3): yum-utils-4.3.0-20.el9.noarch.rpm                                                                                            117 kB/s |  35 kB     00:00
(2/3): dnf-plugins-core-4.3.0-20.el9.noarch.rpm                                                                                     106 kB/s |  36 kB     00:00
(3/3): python3-dnf-plugins-core-4.3.0-20.el9.noarch.rpm                                                                             696 kB/s | 246 kB     00:00
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
Total                                                                                                                               348 kB/s | 317 kB     00:00
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  Preparing        :                                                                                                                                            1/1
  Upgrading        : python3-dnf-plugins-core-4.3.0-20.el9.noarch                                                                                               1/6
  Upgrading        : dnf-plugins-core-4.3.0-20.el9.noarch                                                                                                       2/6
  Upgrading        : yum-utils-4.3.0-20.el9.noarch                                                                                                              3/6
  Cleanup          : yum-utils-4.3.0-5.el9_2.noarch                                                                                                             4/6
  Cleanup          : dnf-plugins-core-4.3.0-5.el9_2.noarch                                                                                                      5/6
  Cleanup          : python3-dnf-plugins-core-4.3.0-5.el9_2.noarch                                                                                              6/6
  Running scriptlet: python3-dnf-plugins-core-4.3.0-5.el9_2.noarch                                                                                              6/6
  Verifying        : yum-utils-4.3.0-20.el9.noarch                                                                                                              1/6
  Verifying        : yum-utils-4.3.0-5.el9_2.noarch                                                                                                             2/6
  Verifying        : python3-dnf-plugins-core-4.3.0-20.el9.noarch                                                                                               3/6
  Verifying        : python3-dnf-plugins-core-4.3.0-5.el9_2.noarch                                                                                              4/6
  Verifying        : dnf-plugins-core-4.3.0-20.el9.noarch                                                                                                       5/6
  Verifying        : dnf-plugins-core-4.3.0-5.el9_2.noarch                                                                                                      6/6

Upgraded:
  dnf-plugins-core-4.3.0-20.el9.noarch                  python3-dnf-plugins-core-4.3.0-20.el9.noarch                  yum-utils-4.3.0-20.el9.noarch

Complete!
[root@sb ~]# sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
Adding repo from: https://download.docker.com/linux/centos/docker-ce.repo&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Docker Package 설치&lt;/h3&gt;
&lt;pre id=&quot;code_1756825463891&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[root@sb ~]# sudo dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Docker CE Stable - x86_64                                                                                                           115 kB/s |  78 kB     00:00
Dependencies resolved.
====================================================================================================================================================================
 Package                                         Architecture                 Version                                  Repository                              Size
====================================================================================================================================================================
Installing:
 containerd.io                                   x86_64                       1.7.27-3.1.el9                           docker-ce-stable                        44 M
 docker-buildx-plugin                            x86_64                       0.26.1-1.el9                             docker-ce-stable                        16 M
 docker-ce                                       x86_64                       3:28.3.3-1.el9                           docker-ce-stable                        21 M
 docker-ce-cli                                   x86_64                       1:28.3.3-1.el9                           docker-ce-stable                       8.6 M
 docker-compose-plugin                           x86_64                       2.39.1-1.el9                             docker-ce-stable                        15 M
Upgrading:
 selinux-policy                                  noarch                       38.1.53-5.el9_6                          baseos                                  44 k
 selinux-policy-targeted                         noarch                       38.1.53-5.el9_6                          baseos                                 6.5 M
Installing dependencies:
 container-selinux                               noarch                       4:2.237.0-1.el9_6                        appstream                               58 k
 fuse-common                                     x86_64                       3.10.2-9.el9                             baseos                                 7.3 k
 fuse-overlayfs                                  x86_64                       1.14-1.el9                               appstream                               66 k
 fuse3                                           x86_64                       3.10.2-9.el9                             appstream                               53 k
 fuse3-libs                                      x86_64                       3.10.2-9.el9                             appstream                               91 k
 libslirp                                        x86_64                       4.4.0-8.el9                              appstream                               67 k
 slirp4netns                                     x86_64                       1.3.2-1.el9                              appstream                               46 k
Installing weak dependencies:
 docker-ce-rootless-extras                       x86_64                       28.3.3-1.el9                             docker-ce-stable                       3.4 M

Transaction Summary
====================================================================================================================================================================
Install  13 Packages
Upgrade   2 Packages

Total download size: 115 M
Is this ok [y/N]: y
Downloading Packages:
(1/15): docker-ce-28.3.3-1.el9.x86_64.rpm                                                                                            11 MB/s |  21 MB     00:01
(2/15): docker-buildx-plugin-0.26.1-1.el9.x86_64.rpm                                                                                7.7 MB/s |  16 MB     00:02
(3/15): docker-ce-rootless-extras-28.3.3-1.el9.x86_64.rpm                                                                           3.2 MB/s | 3.4 MB     00:01
(4/15): docker-ce-cli-28.3.3-1.el9.x86_64.rpm                                                                                       5.5 MB/s | 8.6 MB     00:01
(5/15): fuse-common-3.10.2-9.el9.x86_64.rpm                                                                                          57 kB/s | 7.3 kB     00:00
(6/15): libslirp-4.4.0-8.el9.x86_64.rpm                                                                                             207 kB/s |  67 kB     00:00
(7/15): container-selinux-2.237.0-1.el9_6.noarch.rpm                                                                                1.0 MB/s |  58 kB     00:00
(8/15): fuse3-libs-3.10.2-9.el9.x86_64.rpm                                                                                          1.2 MB/s |  91 kB     00:00
(9/15): docker-compose-plugin-2.39.1-1.el9.x86_64.rpm                                                                                15 MB/s |  15 MB     00:01
(10/15): fuse3-3.10.2-9.el9.x86_64.rpm                                                                                              225 kB/s |  53 kB     00:00
(11/15): containerd.io-1.7.27-3.1.el9.x86_64.rpm                                                                                    8.3 MB/s |  44 MB     00:05
(12/15): slirp4netns-1.3.2-1.el9.x86_64.rpm                                                                                          41 kB/s |  46 kB     00:01
(13/15): fuse-overlayfs-1.14-1.el9.x86_64.rpm                                                                                        59 kB/s |  66 kB     00:01
(14/15): selinux-policy-38.1.53-5.el9_6.noarch.rpm                                                                                  589 kB/s |  44 kB     00:00
(15/15): selinux-policy-targeted-38.1.53-5.el9_6.noarch.rpm                                                                         7.3 MB/s | 6.5 MB     00:00
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
Total                                                                                                                                16 MB/s | 115 MB     00:07
Docker CE Stable - x86_64                                                                                                            22 kB/s | 1.6 kB     00:00
Importing GPG key 0x621E9F35:
 Userid     : &quot;Docker Release (CE rpm) &amp;lt;docker@docker.com&amp;gt;&quot;
 Fingerprint: 060A 61C5 1B55 8A7F 742B 77AA C52F EB6B 621E 9F35
 From       : https://download.docker.com/linux/centos/gpg
Is this ok [y/N]: y
Key imported successfully
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  Running scriptlet: selinux-policy-targeted-38.1.53-5.el9_6.noarch                                                                                             1/1
  Preparing        :                                                                                                                                            1/1
  Upgrading        : selinux-policy-38.1.53-5.el9_6.noarch                                                                                                     1/17
  Running scriptlet: selinux-policy-38.1.53-5.el9_6.noarch                                                                                                     1/17
  Running scriptlet: selinux-policy-targeted-38.1.53-5.el9_6.noarch                                                                                            2/17
  Upgrading        : selinux-policy-targeted-38.1.53-5.el9_6.noarch                                                                                            2/17
  Running scriptlet: selinux-policy-targeted-38.1.53-5.el9_6.noarch                                                                                            2/17
  Running scriptlet: container-selinux-4:2.237.0-1.el9_6.noarch                                                                                                3/17
  Installing       : container-selinux-4:2.237.0-1.el9_6.noarch                                                                                                3/17
  Running scriptlet: container-selinux-4:2.237.0-1.el9_6.noarch                                                                                                3/17
  Installing       : fuse3-libs-3.10.2-9.el9.x86_64                                                                                                            4/17
  Installing       : docker-buildx-plugin-0.26.1-1.el9.x86_64                                                                                                  5/17
  Running scriptlet: docker-buildx-plugin-0.26.1-1.el9.x86_64                                                                                                  5/17
  Installing       : docker-compose-plugin-2.39.1-1.el9.x86_64                                                                                                 6/17
  Running scriptlet: docker-compose-plugin-2.39.1-1.el9.x86_64                                                                                                 6/17
  Installing       : docker-ce-cli-1:28.3.3-1.el9.x86_64                                                                                                       7/17
  Running scriptlet: docker-ce-cli-1:28.3.3-1.el9.x86_64                                                                                                       7/17
  Installing       : containerd.io-1.7.27-3.1.el9.x86_64                                                                                                       8/17
  Running scriptlet: containerd.io-1.7.27-3.1.el9.x86_64                                                                                                       8/17
  Installing       : libslirp-4.4.0-8.el9.x86_64                                                                                                               9/17
  Installing       : slirp4netns-1.3.2-1.el9.x86_64                                                                                                           10/17
  Installing       : fuse-common-3.10.2-9.el9.x86_64                                                                                                          11/17
  Installing       : fuse3-3.10.2-9.el9.x86_64                                                                                                                12/17
  Installing       : fuse-overlayfs-1.14-1.el9.x86_64                                                                                                         13/17
  Running scriptlet: fuse-overlayfs-1.14-1.el9.x86_64                                                                                                         13/17
  Installing       : docker-ce-rootless-extras-28.3.3-1.el9.x86_64                                                                                            14/17
  Running scriptlet: docker-ce-rootless-extras-28.3.3-1.el9.x86_64                                                                                            14/17
  Installing       : docker-ce-3:28.3.3-1.el9.x86_64                                                                                                          15/17
  Running scriptlet: docker-ce-3:28.3.3-1.el9.x86_64                                                                                                          15/17
  Running scriptlet: selinux-policy-38.1.11-2.el9_2.2.noarch                                                                                                  16/17
  Cleanup          : selinux-policy-38.1.11-2.el9_2.2.noarch                                                                                                  16/17
  Running scriptlet: selinux-policy-38.1.11-2.el9_2.2.noarch                                                                                                  16/17
  Cleanup          : selinux-policy-targeted-38.1.11-2.el9_2.2.noarch                                                                                         17/17
  Running scriptlet: selinux-policy-targeted-38.1.11-2.el9_2.2.noarch                                                                                         17/17
  Running scriptlet: selinux-policy-targeted-38.1.53-5.el9_6.noarch                                                                                           17/17
  Running scriptlet: container-selinux-4:2.237.0-1.el9_6.noarch&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Docker 테스트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;hello-world 이미지를 실행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1756825513347&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[root@sb ~]# sudo docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the &quot;hello-world&quot; image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;docker-compose.yml&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;저는 로컬(windows)에 존재하는 docker-compose.yml 파일을 linux server로 옮겨주었습니다.&lt;/li&gt;
&lt;li&gt;추가로, 볼륨(volume) 설정이 있다면 compose 파일에 맞게 서버 내 디렉토리와 설정 파일, SQL 파일 등도 함께 준비해주셔야 합니다. (이 부분은 생략)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1756825580603&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PS C:\Users\admin&amp;gt; scp C:\docker-compose.yml root@192.168.217.190:docker/
root@192.168.217.190's password:
docker-compose.yml                                                                                                                100% 2200     1.1MB/s   00:00&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;방화벽 설정을 위한 firewall 설치 및 포트 개방&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설치: sudo dnf install -y firewalld&lt;/li&gt;
&lt;li&gt;실행: sudo systemctl enable firewalld --now&lt;/li&gt;
&lt;li&gt;상태 확인: sudo systemctl status firewalld&lt;/li&gt;
&lt;li&gt;변경사항 적용: sudo firewall-cmd --reload&lt;/li&gt;
&lt;li&gt;개방된 포트 확인: sudo firewall-cmd --list-ports&lt;/li&gt;
&lt;li&gt;외부에서 서버로 들어오는(Inbound) 트래픽을 허용할 포트 지정: sudo firewall-cmd --add-port={port_number}
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;--permanant: 서버 재부팅 시에도 설정 유지하기 위한 옵션&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1756825785555&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[root@sb docker]# sudo dnf install -y firewalld
Last metadata expiration check: 0:34:01 ago on Tue Sep  2 05:58:16 2025.
Package firewalld-1.3.4-9.el9_5.noarch is already installed.
Dependencies resolved.
Nothing to do.
Complete!
[root@sb docker]# sudo systemctl enable firewalld --now
[root@sb docker]# sudo systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
     Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; preset: enabled)
     Active: active (running) since Tue 2025-09-02 06:32:56 UTC; 11s ago
       Docs: man:firewalld(1)
   Main PID: 126067 (firewalld)
      Tasks: 2 (limit: 48688)
     Memory: 25.1M
        CPU: 2.939s
     CGroup: /system.slice/firewalld.service
             └─126067 /usr/bin/python3 -s /usr/sbin/firewalld --nofork --nopid

Sep 02 06:32:54 sb.kim-test.novalocal systemd[1]: Starting firewalld - dynamic firewall daemon...
Sep 02 06:32:56 sb.kim-test.novalocal systemd[1]: Started firewalld - dynamic firewall daemon.
[root@sb docker]# sudo firewall-cmd --add-port=3306/tcp --add-port=3307/tcp --add-port=9000/tcp \
                  --add-port=9001/tcp --add-port=9002/tcp --add-port=8443/tcp \
                  --add-port=9200/tcp --add-port=9300/tcp --permanent
success
[root@sb docker]# sudo firewall-cmd --reload
success
[root@sb docker]# sudo firewall-cmd --list-ports
3306/tcp 3307/tcp 8443/tcp 9000/tcp 9001/tcp 9002/tcp 9200/tcp 9300/tcp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;방화벽 (firewall)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 유형의 네트워크 유형을 차단하기 위한 장치&amp;nbsp;
&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;inbound, outbound 규칙을 설정하여 내, 외부 트래픽을 제어할 수 있을 뿐만 아니라, 특정 포트 및 프로토콜을 설정을 지원합니다.&lt;/li&gt;
&lt;li&gt;리눅스에서 방화벽 설정을 위한 관리 도구로 firewalld를 제공하며, firewall-cmd 명령어를 통해 방화벽 설정을 할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;firewall-cmd 명령어&lt;/h3&gt;
&lt;pre id=&quot;code_1756826898969&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;--- 포트 개방 및 개방된 포트 차단---
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https


firewall-cmd --permanent --remove-port=80/tcp
firewall-cmd --permanent --remove-service=http
firewall-cmd --permanent --remove-service=https

--- 특정 IP에 대해 허용 및 정책 삭제 ---
firewall-cmd --permanent --add-source=127.0.0.1
firewall-cmd --permanent --remove-source=127.0.0.1

--- 정책 확인 및 반영 ---
firewall-cmd --reload
firewall-cmd --list-all&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.tosspayments.com/resources/glossary/firewall&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.tosspayments.com/resources/glossary/firewall&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1756826256254&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;방화벽(Firewall) | 토스페이먼츠 개발자센터&quot; data-og-description=&quot;방화벽은 특정 유형의 네트워크 트래픽을 차단하는 장치를 말해요. 신뢰할 수 있는 네트워크 트래픽은 허용하고, 신뢰할 수 없는 악성 트래픽을 막는 것이죠.&quot; data-og-host=&quot;docs.tosspayments.com&quot; data-og-source-url=&quot;https://docs.tosspayments.com/resources/glossary/firewall&quot; data-og-url=&quot;https://docs.tosspayments.com/resources/glossary/firewall&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gixGC/hyZGmCgcMs/R7tmbOl45KdMZBGFEVqyK1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://docs.tosspayments.com/resources/glossary/firewall&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.tosspayments.com/resources/glossary/firewall&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gixGC/hyZGmCgcMs/R7tmbOl45KdMZBGFEVqyK1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;방화벽(Firewall) | 토스페이먼츠 개발자센터&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;방화벽은 특정 유형의 네트워크 트래픽을 차단하는 장치를 말해요. 신뢰할 수 있는 네트워크 트래픽은 허용하고, 신뢰할 수 없는 악성 트래픽을 막는 것이죠.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.tosspayments.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://eveningdev.tistory.com/242&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://eveningdev.tistory.com/242&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1756826269840&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[NCLOUD] Rocky Linux 8.10에서 Docker 설치하는 방법 알아보기&quot; data-og-description=&quot;안녕하세요.&amp;nbsp;이번 시간에는 NCLOUD 서버 중에서 Rocky Linux 8.10에서 Docker를 설치하는 방법을 알아보겠습니다.추가적인 확인을 위해 Rocky Linux 8.8과 9.4 버전도 준비하였으니 참고해주시면 됩니다.0. &quot; data-og-host=&quot;eveningdev.tistory.com&quot; data-og-source-url=&quot;https://eveningdev.tistory.com/242&quot; data-og-url=&quot;https://eveningdev.tistory.com/242&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/9MU2l/hyZGhnmIeB/GX7lrJPFTyRQJ8BXchxmk1/img.png?width=800&amp;amp;height=437&amp;amp;face=0_0_800_437,https://scrap.kakaocdn.net/dn/bWmeoJ/hyZF8jGHi9/dKuXdJpFuNqBTXpi8Zn5j0/img.png?width=800&amp;amp;height=437&amp;amp;face=0_0_800_437,https://scrap.kakaocdn.net/dn/bMysXg/hyZF8DZygY/BTSKyLoHmRSqCgsMye44EK/img.png?width=1453&amp;amp;height=795&amp;amp;face=0_0_1453_795&quot;&gt;&lt;a href=&quot;https://eveningdev.tistory.com/242&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://eveningdev.tistory.com/242&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/9MU2l/hyZGhnmIeB/GX7lrJPFTyRQJ8BXchxmk1/img.png?width=800&amp;amp;height=437&amp;amp;face=0_0_800_437,https://scrap.kakaocdn.net/dn/bWmeoJ/hyZF8jGHi9/dKuXdJpFuNqBTXpi8Zn5j0/img.png?width=800&amp;amp;height=437&amp;amp;face=0_0_800_437,https://scrap.kakaocdn.net/dn/bMysXg/hyZF8DZygY/BTSKyLoHmRSqCgsMye44EK/img.png?width=1453&amp;amp;height=795&amp;amp;face=0_0_1453_795');&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;[NCLOUD] Rocky Linux 8.10에서 Docker 설치하는 방법 알아보기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요.&amp;nbsp;이번 시간에는 NCLOUD 서버 중에서 Rocky Linux 8.10에서 Docker를 설치하는 방법을 알아보겠습니다.추가적인 확인을 위해 Rocky Linux 8.8과 9.4 버전도 준비하였으니 참고해주시면 됩니다.0.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;eveningdev.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jbground.tistory.com/90&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jbground.tistory.com/90&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1756826278485&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Linux] EC2 Ubuntu 리눅스 Docker 설치 방법 및 도커 주요 명령어&quot; data-og-description=&quot;1. Docker 설치 방법1.1. apt 업데이트 및 필수 패키지 설치#apt 업데이트sudo apt-get update#필수 패키지 설치sudo apt-get install -y apt-transport-https ca-certificates curl gnupg&amp;nbsp;1.2. 공개키 다운로드 및 저장소 등록#공&quot; data-og-host=&quot;jbground.tistory.com&quot; data-og-source-url=&quot;https://jbground.tistory.com/90&quot; data-og-url=&quot;https://jbground.tistory.com/90&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/4HKnZ/hyZGXaEx2D/L1UZnKFLrh1t7eHbwaPp51/img.jpg?width=800&amp;amp;height=562&amp;amp;face=0_0_800_562,https://scrap.kakaocdn.net/dn/B4QE2/hyZGahvpT6/CePYx0BrVhqwHdReUbszFk/img.jpg?width=800&amp;amp;height=562&amp;amp;face=0_0_800_562&quot;&gt;&lt;a href=&quot;https://jbground.tistory.com/90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jbground.tistory.com/90&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/4HKnZ/hyZGXaEx2D/L1UZnKFLrh1t7eHbwaPp51/img.jpg?width=800&amp;amp;height=562&amp;amp;face=0_0_800_562,https://scrap.kakaocdn.net/dn/B4QE2/hyZGahvpT6/CePYx0BrVhqwHdReUbszFk/img.jpg?width=800&amp;amp;height=562&amp;amp;face=0_0_800_562');&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;[Linux] EC2 Ubuntu 리눅스 Docker 설치 방법 및 도커 주요 명령어&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. Docker 설치 방법1.1. apt 업데이트 및 필수 패키지 설치#apt 업데이트sudo apt-get update#필수 패키지 설치sudo apt-get install -y apt-transport-https ca-certificates curl gnupg&amp;nbsp;1.2. 공개키 다운로드 및 저장소 등록#공&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jbground.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.fedoralinux.or.kr/forums/topic/119641&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.fedoralinux.or.kr/forums/topic/119641&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1756827126178&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;페도라 리눅스 방화벽 firewalld 설정 제어하기 | 페도라 한국 사용자 모임&quot; data-og-description=&quot;안녕하세요 운영자 태랑 입니다. 이번글은 방화벽 프로그램인 firewalld 에 대해서 간략하게 확인 해보도록 하겠습니다. firewalld는 리눅스에서 방화벽을 설정하고 관리하는 도구입니다. firewalld는 i&quot; data-og-host=&quot;www.fedoralinux.or.kr&quot; data-og-source-url=&quot;https://www.fedoralinux.or.kr/forums/topic/119641&quot; data-og-url=&quot;https://www.fedoralinux.or.kr/forums/topic/119641&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/daCyZ2/hyZD0tki9A/mVUiGxJoyG1iJtbKoOljbk/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200,https://scrap.kakaocdn.net/dn/c3gHI6/hyZG2JNM7c/k8BIX8swrdNrfmDCRnKpuk/img.png?width=388&amp;amp;height=90&amp;amp;face=0_0_388_90&quot;&gt;&lt;a href=&quot;https://www.fedoralinux.or.kr/forums/topic/119641&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.fedoralinux.or.kr/forums/topic/119641&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/daCyZ2/hyZD0tki9A/mVUiGxJoyG1iJtbKoOljbk/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200,https://scrap.kakaocdn.net/dn/c3gHI6/hyZG2JNM7c/k8BIX8swrdNrfmDCRnKpuk/img.png?width=388&amp;amp;height=90&amp;amp;face=0_0_388_90');&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;페도라 리눅스 방화벽 firewalld 설정 제어하기 | 페도라 한국 사용자 모임&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요 운영자 태랑 입니다. 이번글은 방화벽 프로그램인 firewalld 에 대해서 간략하게 확인 해보도록 하겠습니다. firewalld는 리눅스에서 방화벽을 설정하고 관리하는 도구입니다. firewalld는 i&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.fedoralinux.or.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/Docker</category>
      <category>docker-compose</category>
      <category>firewall</category>
      <category>firewall-cmd 명령어</category>
      <category>firewalld</category>
      <category>Rocky Linux</category>
      <category>Rocky Linux Docker 환경 구축</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/425</guid>
      <comments>https://daily1313.tistory.com/entry/Docker-Rocky-Linux-92%EC%97%90%EC%84%9C-docker-compose-%EA%B8%B0%EB%B0%98-db-elasticsearch-%EA%B5%AC%EC%B6%95-%EA%B3%BC%EC%A0%95#entry425comment</comments>
      <pubDate>Wed, 3 Sep 2025 00:32:35 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Collection.foreach(), Stream.foreach()를 비교해보자</title>
      <link>https://daily1313.tistory.com/entry/Java-Collectionforeach-Streamforeach%EB%A5%BC-%EB%B9%84%EA%B5%90%ED%95%B4%EB%B3%B4%EC%9E%90</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Foreach&lt;/h2&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;Java8에서 추가된 Iterable 인터페이스의 기본 메서드인 forEach() 메서드를 제공하며, 해당 메서드를 통해 컬렉션을 반복하며 각 항목에 대해 반복 작업을 수행할 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 요소 간 수정/삭제 작업이 필요하다면, forEach 대신 iterator()를 활용할 필요가 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;향상된 for문 (enhanced for loop)이라고도 부릅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Collection.foreach(),&amp;nbsp;Stream.foreach()&lt;/h2&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;Collection의 forEach()의 경우에는 forEach() 메서드를 제공하는 Iterable 인터페이스를 상속받고 있기에 즉시 forEach() 메서드를 호출할 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Iterator를 사용하므로 처리 순서가 정해져 있습니다.&lt;/li&gt;
&lt;li&gt;하지만, forEach문에서 컬렉션을 수정하려고 하면 ConcurrentModificationException이 즉시 발생하게 됩니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Stream.forEach()의 경우는 Stream을 직접 생성해야만 forEach() 메서드를 호출할 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Stream의 경우는 실행 순서가 정해지지 않습니다. (순서를 보장하기 위해서는 forEachOrdered 메서드를 활용해야 합니다.)&lt;/li&gt;
&lt;li&gt;Stream의 경우는 종료시키는 방법이 없기 때문에, for-loop에 비해서 효율성이 떨어집니다. (조건문에 의해 빠져나와야 하는 경우에도 끝까지 작업을 수행합니다.)&lt;/li&gt;
&lt;li&gt;forEach는 스트림의 최종 연산이며 조건문을 수행하고 계산하는 과정은 중간 연산에서 이루어져야 합니다.&lt;/li&gt;
&lt;li&gt;Effective Java item-46 장에도 forEach는 &quot;주로 최종 연산 중 가장 기능이 작고 덜 스트림 답기 때문에 스트림 결과를 보고(print)할 때마다 사용하고, 계산하는 데는 사용하지 말라&quot;라고 명시되어 있습니다.&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;h2 data-ke-size=&quot;size26&quot;&gt;Collection.Foreach()&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-01 오후 11.26.13.png&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;906&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k5zwo/btsQgUIw2v0/kA0UHVPJ6nwkBtK9SLyXmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k5zwo/btsQgUIw2v0/kA0UHVPJ6nwkBtK9SLyXmk/img.png&quot; data-alt=&quot;Iterable default forEach 메서드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k5zwo/btsQgUIw2v0/kA0UHVPJ6nwkBtK9SLyXmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk5zwo%2FbtsQgUIw2v0%2FkA0UHVPJ6nwkBtK9SLyXmk%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;581&quot; height=&quot;364&quot; data-filename=&quot;스크린샷 2025-09-01 오후 11.26.13.png&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;906&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Iterable default forEach 메서드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1756738214448&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    List&amp;lt;String&amp;gt; list = Arrays.asList(&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;);

    // enhanced for-loop
    for(String str : list) {
        System.out.println(str);
    }

    // forEach
    Consumer&amp;lt;String&amp;gt; consumer = System.out::println;
    list.forEach(consumer);
    
    // forEach
    list.forEach(s -&amp;gt; System.out.println(s));
}&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;객체를 생성하지 않고 바로 forEach문을 호출합니다.&lt;/li&gt;
&lt;li&gt;Iterable 인터페이스의 기본으로 제공해주는 forEach 메서드는 Consumer를 매개변수로 받아서 요소를 하나씩 처리합니다.&lt;/li&gt;
&lt;li&gt;이를 제공하기에 때문에 별도의 메서드를 구현하지 않아도 바로 활용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;위의 예시 처럼 메서드 참조를 활용한 Consumer 구현체를 직접 forEach에 전달해도 되며, 혹은 람다식으로 처리해도 상관없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Stream.forEach()&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OSWVK/btsQhnwVExu/iMmotskubABXkfs2KOuBs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OSWVK/btsQhnwVExu/iMmotskubABXkfs2KOuBs1/img.png&quot; width=&quot;721&quot; height=&quot;456&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;920&quot; data-filename=&quot;스크린샷 2025-09-01 오후 11.55.26.png&quot; data-is-animation=&quot;false&quot; style=&quot;width: 53.0298%; margin-right: 10px;&quot; data-widthpercent=&quot;53.65&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OSWVK/btsQhnwVExu/iMmotskubABXkfs2KOuBs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOSWVK%2FbtsQhnwVExu%2FiMmotskubABXkfs2KOuBs1%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;1456&quot; height=&quot;920&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNh03w/btsQfDUPKDr/oCT101VkKuExQICFyOG6U0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNh03w/btsQfDUPKDr/oCT101VkKuExQICFyOG6U0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1594&quot; data-origin-height=&quot;1166&quot; data-filename=&quot;스크린샷 2025-09-02 오전 12.08.06.png&quot; style=&quot;width: 45.8074%;&quot; data-widthpercent=&quot;46.35&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNh03w/btsQfDUPKDr/oCT101VkKuExQICFyOG6U0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNh03w%2FbtsQfDUPKDr%2FoCT101VkKuExQICFyOG6U0%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;1594&quot; height=&quot;1166&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;stream forEach&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Stream.forEach의 경우는 Collection 인터페이스의 default 메서드인 stream()으로 Stream 객체를 생성해야 forEach문을 호출할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;stream 객체 생성으로 인한 오버헤드 발생을 막기 위해 filter, map 등의 Stream 기능들과 함께 사용할 때만 Stream.forEach를 사용하고 나머지에서는 Collection.forEach를 사용하는 것이 좋습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;parallelStream&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;parallelStream 메서드로 forEach문을 호출할 경우, 여러 스레드에서 스트림을 실행하기 때문에 실행 순서가 달라지며 예측이 불가능합니다.&lt;/li&gt;
&lt;li&gt;Collection이 수정될 경우 ConcurrentModificationException이 발생됩니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Collection이 수정되자마자 예외를 던집니다.&lt;/li&gt;
&lt;li&gt;Collections의 synchronizedList, synchronizedSet ..를 활용하여 멀티쓰레드에서 안전하게 사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Stream의 forEach의 경우에는 NullPointerException이 발생됩니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;list를 끝까지 돌고 예외를 던집니다.&lt;/li&gt;
&lt;li&gt;Stream.forEach는 반복 도중에 다른 쓰레드에 의해 변경될 수 있으며, 무조건 요소의 끝까지 돌게 됩니다.&lt;/li&gt;
&lt;li&gt;해당 과정에서 예상하지 못한 값이 반환될 수 있게 됩니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&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;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순 컬렉션의 순회에서는 Collection.forEach를 사용하는 것이 효율적입니다.&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/java-collection-stream-foreach&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.baeldung.com/java-collection-stream-foreach&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/java-iterator-vs-foreach&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.baeldung.com/java-iterator-vs-foreach&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2020-09-30-collection-stream-for-each/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://tecoble.techcourse.co.kr/post/2020-09-30-collection-stream-for-each/&lt;/a&gt;&lt;/p&gt;</description>
      <category>Backend/Java Concept</category>
      <category>Collection Foreach</category>
      <category>foreach</category>
      <category>Stream Foreach</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/424</guid>
      <comments>https://daily1313.tistory.com/entry/Java-Collectionforeach-Streamforeach%EB%A5%BC-%EB%B9%84%EA%B5%90%ED%95%B4%EB%B3%B4%EC%9E%90#entry424comment</comments>
      <pubDate>Mon, 1 Sep 2025 23:57:32 +0900</pubDate>
    </item>
    <item>
      <title>[CS] Call by Value, Call by Reference</title>
      <link>https://daily1313.tistory.com/entry/CS-Call-by-Value-Call-by-Reference</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Call by Value, Call by Reference&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메서드를 호출할 때 파라미터를 전달하는 방법&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Call by Value&lt;/h2&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;메서드를 호출하는 호출자 (Caller)의 변수와 호출당하는 수신자 (Callee)의 파라미터는 복사된 서로 다른 변수입니다.&lt;/li&gt;
&lt;li&gt;값만을 전달하기에 수신자의 파라미터를 수정해도 호출자의 변수는 변경되지 않습니다.&lt;/li&gt;
&lt;li&gt;메서드를 호출하는 순간에 복사본이 생성되며, 복사본의 값만 변경되며 원본의 값은 변경되지 않습니다.&lt;/li&gt;
&lt;li&gt;Java에서는 call-by-value로 동작합니다. (값을 복사, 주소값을 복사)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Call by Reference (Java X, 100% Call by Value 방식)&amp;nbsp;&lt;/h2&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;li&gt;값 자체를 복사해야 하는 방식의 Call by Value와 비교하면, 주소(포인터)만 복사하면 되기에 &lt;b&gt;&lt;u&gt;일반적으로 복사 비용은 적습니다.&lt;/u&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Java에서는 C/C++ 처럼 포인터를 노출하지 않고, 주소연산자(&amp;amp;)를 이용해 직접적인 메모리 참조가 불가능하기에 call by reference라는 개념이 존재하지 않습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Java에서의 Parameter 전달 메커니즘&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 글 (JVM Memory 구조 포스팅)에서 reference type, primitive type은 모두 stack area에 존재하는 것을 확인할 수 있습니다. (객체 정보는 제외)&lt;/li&gt;
&lt;li&gt;Primitive Type
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메서드를 인자와 함께 호출하는 순간 primitive type의 경우에는 각 변수에 대한 복사본을 생성하여, stack memory 다른 위치에 저장되게 됩니다. 그래서 원본 변수는 값을 그대로 유지하게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Reference Type&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;reference type의 경우는 객체 정보는 heap area, 객체를 참조 값은 stack area에 동적으로 저장됩니다. 이러한 객체들은 참조 변수(reference variable)를 통해 접근합니다.&lt;/li&gt;
&lt;li&gt;Java의 객체는 Primitive Type과 달리 두 단계로 저장됩니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참조 변수는 stack area, 객체는 heap 메모리에 저장됩니다.&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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgYcHm/btsQemkwOG3/qFKzEGQVk5ND8kKlFjVw80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgYcHm/btsQemkwOG3/qFKzEGQVk5ND8kKlFjVw80/img.png&quot; data-alt=&quot;https://www.baeldung.com/java-pass-by-value-or-pass-by-reference&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgYcHm/btsQemkwOG3/qFKzEGQVk5ND8kKlFjVw80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgYcHm%2FbtsQemkwOG3%2FqFKzEGQVk5ND8kKlFjVw80%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;1020&quot; height=&quot;426&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.baeldung.com/java-pass-by-value-or-pass-by-reference&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Primitive Type Test&lt;/h2&gt;
&lt;pre id=&quot;code_1756624217857&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class PrimitiveType {

    public static void main(String[] args) {
        int x = 1;
        int y = 2;

        // before modification
        System.out.println(x == 1);
        System.out.println(y == 2);

        modify(x, y);

        System.out.println(x == 1);
        System.out.println(y == 2);
    }

    private static void modify(int x1, int x2) {
        x1 = 5;
        x2 = 10;
    }
}&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o462T/btsQfkUxbDp/shNLvJImmwbI1jmSKkBlR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o462T/btsQfkUxbDp/shNLvJImmwbI1jmSKkBlR1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1134&quot; data-origin-height=&quot;1020&quot; data-filename=&quot;스크린샷 2025-08-31 오후 4.12.30.png&quot; style=&quot;width: 35.732%; margin-right: 10px;&quot; data-widthpercent=&quot;36.15&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o462T/btsQfkUxbDp/shNLvJImmwbI1jmSKkBlR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo462T%2FbtsQfkUxbDp%2FshNLvJImmwbI1jmSKkBlR1%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;1134&quot; height=&quot;1020&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uMKSO/btsQcVVYdW0/wm7n8GnEX10sBbFglPVHs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uMKSO/btsQcVVYdW0/wm7n8GnEX10sBbFglPVHs1/img.png&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;602&quot; data-is-animation=&quot;false&quot; data-filename=&quot;스크린샷 2025-08-31 오후 4.12.39.png&quot; style=&quot;width: 63.1052%;&quot; data-widthpercent=&quot;63.85&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uMKSO/btsQcVVYdW0/wm7n8GnEX10sBbFglPVHs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuMKSO%2FbtsQcVVYdW0%2Fwm7n8GnEX10sBbFglPVHs1%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;1182&quot; height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;modify 메서드 호출 후 원본 값 그대로 유지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stack memory에 저장된 원본의 값은 변동되지 않습니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;modify 메서드 호출 후에 생성되는 복사본의 값이 각각 5, 10으로 변동됩니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Reference Type Test&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E1GIL/btsQd8gg5o7/dWMxSli0JGTp8AEkpcoyl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E1GIL/btsQd8gg5o7/dWMxSli0JGTp8AEkpcoyl0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1294&quot; data-origin-height=&quot;1508&quot; data-filename=&quot;스크린샷 2025-08-31 오후 4.33.21.png&quot; width=&quot;734&quot; height=&quot;855&quot; style=&quot;width: 28.0712%; margin-right: 10px;&quot; data-widthpercent=&quot;28.4&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E1GIL/btsQd8gg5o7/dWMxSli0JGTp8AEkpcoyl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE1GIL%2FbtsQd8gg5o7%2FdWMxSli0JGTp8AEkpcoyl0%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;1294&quot; height=&quot;1508&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eGN8ZH/btsQcXTPabP/68ITaedR2QalmcYKbejsC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eGN8ZH/btsQcXTPabP/68ITaedR2QalmcYKbejsC0/img.png&quot; data-filename=&quot;스크린샷 2025-08-31 오후 4.33.43.png&quot; data-origin-height=&quot;674&quot; data-origin-width=&quot;1458&quot; data-is-animation=&quot;false&quot; width=&quot;551&quot; height=&quot;255&quot; style=&quot;width: 70.7661%;&quot; data-widthpercent=&quot;71.6&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eGN8ZH/btsQcXTPabP/68ITaedR2QalmcYKbejsC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeGN8ZH%2FbtsQcXTPabP%2F68ITaedR2QalmcYKbejsC0%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;1458&quot; height=&quot;674&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;modify 메서드 호출 시 A 클래스의 num 변수만 2로 변경&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;Reference Type의 동작과정&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cM6MDk/btsQejPkBrl/JdgWLEqoGmk7iXUsrasgh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cM6MDk/btsQejPkBrl/JdgWLEqoGmk7iXUsrasgh1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1784&quot; data-origin-height=&quot;758&quot; data-filename=&quot;스크린샷 2025-08-31 오후 4.36.05.png&quot; style=&quot;width: 58.016%; margin-right: 10px;&quot; data-widthpercent=&quot;58.7&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cM6MDk/btsQejPkBrl/JdgWLEqoGmk7iXUsrasgh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcM6MDk%2FbtsQejPkBrl%2FJdgWLEqoGmk7iXUsrasgh1%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;1784&quot; height=&quot;758&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/edrrFC/btsQdzZQgJL/lafHvHtsbBtYVuLl8JldG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/edrrFC/btsQdzZQgJL/lafHvHtsbBtYVuLl8JldG1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1762&quot; data-origin-height=&quot;1064&quot; data-filename=&quot;스크린샷 2025-08-31 오후 4.39.48.png&quot; style=&quot;width: 40.8212%;&quot; data-widthpercent=&quot;41.3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/edrrFC/btsQdzZQgJL/lafHvHtsbBtYVuLl8JldG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FedrrFC%2FbtsQdzZQgJL%2FlafHvHtsbBtYVuLl8JldG1%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;1762&quot; height=&quot;1064&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;1. new 키워드를 통한 객체 생성 시, 2. modify 호출 시점 시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1번의 경우 A, B 객체 생성 시 stack 메모리에 존재하는 참조 변수가 heap 메모리에 존재하는 각각의 객체를 가리키고 있는 상태입니다.&lt;/li&gt;
&lt;li&gt;2번의 경우는 modify 메서드 호출 시점에 Call By Value를 통해 참조 변수의 복사본을 생성하여 stack 메모리에 저장합니다.
&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;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메서드 내에서 참조 변수가 새로운 객체를 할당할 경우&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-31 오후 4.44.09.png&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;1094&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pup8J/btsQgdtTeuU/lMah07LrGOgokhT1VnLo3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pup8J/btsQgdtTeuU/lMah07LrGOgokhT1VnLo3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pup8J/btsQgdtTeuU/lMah07LrGOgokhT1VnLo3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpup8J%2FbtsQgdtTeuU%2FlMah07LrGOgokhT1VnLo3K%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;677&quot; height=&quot;430&quot; data-filename=&quot;스크린샷 2025-08-31 오후 4.44.09.png&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;1094&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위의 명시한 예제와 같은 형태입니다. (modify 메서드 내부에서 새로운 객체를 할당)&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/java-pass-by-value-or-pass-by-reference&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.baeldung.com/java-pass-by-value-or-pass-by-reference&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://bcp0109.tistory.com/360&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://bcp0109.tistory.com/360&lt;/a&gt;&lt;/p&gt;</description>
      <category>CS</category>
      <category>call by reference</category>
      <category>call by value</category>
      <category>heap memory</category>
      <category>primitive type</category>
      <category>reference type</category>
      <category>stack memory</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/423</guid>
      <comments>https://daily1313.tistory.com/entry/CS-Call-by-Value-Call-by-Reference#entry423comment</comments>
      <pubDate>Sun, 31 Aug 2025 16:53:36 +0900</pubDate>
    </item>
    <item>
      <title>[Java] final, interface, abstract class</title>
      <link>https://daily1313.tistory.com/entry/Java-final-interface-abstract-class</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Final Keyword&lt;/h2&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;table id=&quot;25aac551-863b-8028-b52e-d853874e90a2&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr id=&quot;25aac551-863b-805f-8b1d-f6022810fedd&quot;&gt;
&lt;td id=&quot;qszZ&quot;&gt;위치&lt;/td&gt;
&lt;td id=&quot;aTZl&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;25aac551-863b-80f1-8702-dd452b48f926&quot;&gt;
&lt;td id=&quot;qszZ&quot;&gt;클래스&lt;/td&gt;
&lt;td id=&quot;aTZl&quot;&gt;변경 또는 확장이 불가능한 클래스, 상속 불가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;25aac551-863b-80f2-9426-f5a42a8d4695&quot;&gt;
&lt;td id=&quot;qszZ&quot;&gt;메서드&lt;/td&gt;
&lt;td id=&quot;aTZl&quot;&gt;오버라이딩 불가한 메서드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;25aac551-863b-80fa-b3b0-c54e8a7d92cf&quot;&gt;
&lt;td id=&quot;qszZ&quot;&gt;변수&lt;/td&gt;
&lt;td id=&quot;aTZl&quot;&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;pre id=&quot;code_1756221935956&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;final class Final { // 확장/상속 불가능
   final int x = 1; // 변경할 수 없는 상수

   final void getConstant() { // 오버라이딩 불가한 메서드
       final int CONSTANT = x; // 상수
       return x;
   }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Interface&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-27 오전 1.04.07.png&quot; data-origin-width=&quot;536&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qj2ON/btsP6Q1jg91/uI95wsvQgTJopEeLGJtww1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qj2ON/btsP6Q1jg91/uI95wsvQgTJopEeLGJtww1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qj2ON/btsP6Q1jg91/uI95wsvQgTJopEeLGJtww1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqj2ON%2FbtsP6Q1jg91%2FuI95wsvQgTJopEeLGJtww1%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;536&quot; height=&quot;458&quot; data-filename=&quot;스크린샷 2025-08-27 오전 1.04.07.png&quot; data-origin-width=&quot;536&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스가 구현해야 하는 동작을 선언하는 데 사용되는 추상 유형 (wikipedia)&lt;/li&gt;
&lt;li&gt;interface 키워드를 사용하여 정의하며, 오직 추상 메서드와 상수(static final)만을 가지고 있는 것&lt;/li&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;b&gt;public static final이어야&lt;/b&gt; 하며, 생략이 가능합니다.&lt;/li&gt;
&lt;li&gt;모든 메서드는 &lt;b&gt;public abstract&lt;/b&gt;이어야 하며, 생략이 가능합니다.&lt;/li&gt;
&lt;li&gt;Java 8 부터는 static, default method를 사용할 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하위 호환성을 위해 default 메서드를 제공합니다. &amp;rarr; 기존 인터페이스를 확장해서 새로운 기능을 추가하기 위해서 추상 메서드를 하나 더 정의를 할 경우, 기존에 인터페이스를 구현하는 모든 클래스에 추상 메서드를 구현해야 하기 때문입니다.&lt;/li&gt;
&lt;li&gt;default mehtod: 구체 메서드지만, 선택적으로 오버라이딩이 가능한 메서드&lt;/li&gt;
&lt;li&gt;static method: 인스턴스 생성 없이 즉시 호출이 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;상속 키워드로 implements를 사용합니다.&lt;/li&gt;
&lt;li&gt;다중 상속이 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Interface Simple Example&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Payment.class&lt;/h4&gt;
&lt;pre id=&quot;code_1756225076169&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface Payment {

    void pay();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;BankTransfer.class&lt;/h4&gt;
&lt;pre id=&quot;code_1756225113351&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class BankTransfer implements Payment {

    @Override
    public void pay() {
        System.out.println(&quot;bank transfer&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;CreditCard.class&lt;/h4&gt;
&lt;pre id=&quot;code_1756225129337&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class CreditCard implements Payment {

    @Override
    public void pay() {
        System.out.println(&quot;credit card&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;KakaoPay.class&lt;/h4&gt;
&lt;pre id=&quot;code_1756225165984&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class KakaoPay implements Payment {

    @Override
    public void pay() {
        System.out.println(&quot;kakao pay&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;OrderPayment.class&lt;/h4&gt;
&lt;pre id=&quot;code_1756225188502&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class OrderPayment {

    private Payment payment;

    public OrderPayment(Payment payment) {
        this.payment = payment;
    }

    public void changePaymentMethod(Payment payment) {
        this.payment = payment;
    }

    public void pay() {
        payment.pay();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Application.class&lt;/h4&gt;
&lt;pre id=&quot;code_1756225210069&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Application {

    public static void main(String[] args) {
        OrderPayment payment = new OrderPayment(new BankTransfer());
        payment.pay();

        payment.changePaymentMethod(new CreditCard());
        payment.pay();

        payment.changePaymentMethod(new KakaoPay());
        payment.pay();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Abstract Class&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-27 오전 1.03.42.png&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k2NgG/btsP61hzS1V/4ACUOtkBxrqCfKVQ64A9R1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k2NgG/btsP61hzS1V/4ACUOtkBxrqCfKVQ64A9R1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k2NgG/btsP61hzS1V/4ACUOtkBxrqCfKVQ64A9R1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk2NgG%2FbtsP61hzS1V%2F4ACUOtkBxrqCfKVQ64A9R1%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;594&quot; height=&quot;470&quot; data-filename=&quot;스크린샷 2025-08-27 오전 1.03.42.png&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 앞에 abstract 키워드를 사용하여 정의하며 하나 이상의 추상 메서드를 가지고 있거나 abstract로 정의가 된 클래스&lt;/li&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;추상 클래스에서는 default 메서드에 final keyword를 적용해 주면 하위 클래스에서 오버라이딩이 불가합니다.&lt;/li&gt;
&lt;li&gt;상속은 되지만, 동작은 바꿀 수 없습니다.&lt;/li&gt;
&lt;/ul&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;객체를 만들 수 없는 클래스 (new 키워드로 객체 생성 불가)&lt;/li&gt;
&lt;li&gt;추상 메서드를 포함하는 클래스는 반드시 추상 클래스여야 합니다.&lt;/li&gt;
&lt;li&gt;추상 클래스는 선언부만 존재하며 구현부는 존재할 수 없습니다.&lt;/li&gt;
&lt;li&gt;일반 클래스 상속과 동일하게 상속 키워드로 extends를 사용합니다.&lt;/li&gt;
&lt;li&gt;다중 상속이 불가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Abstract Simple Example&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Animal.class&lt;/h4&gt;
&lt;pre id=&quot;code_1756223787423&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public abstract class Animal {

    void eat() {
        System.out.println(&quot;eat&quot;);
    }

    void work() {
        System.out.println(&quot;work&quot;);
    }

    void sleep() {
        System.out.println(&quot;sleep&quot;);
    }

    // override x
    final void awake() {
        System.out.println(&quot;awake&quot;);
    }

    abstract void makeSound();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Cat.class&lt;/h4&gt;
&lt;pre id=&quot;code_1756223812570&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Cat extends Animal {

    @Override
    void makeSound() {
        System.out.println(&quot;meow&quot;);
    }

    @Override
    void eat() {
        System.out.println(&quot;eat chur&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Dog.class&lt;/h4&gt;
&lt;pre id=&quot;code_1756223865655&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Dog extends Animal {

    @Override
    void makeSound() {
        System.out.println(&quot;woof&quot;);
    }

    @Override
    void eat() {
        System.out.println(&quot;eat dog chew&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Application&lt;/h4&gt;
&lt;pre id=&quot;code_1756223889472&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Application {

    public static void main(String[] args) {
        Animal cat = new Cat();
        Animal dog = new Dog();

        cat.makeSound();
        dog.makeSound();

        cat.eat();
        dog.eat();
    }
}&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developshrimp.com/entry/JAVA-%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4Abstract-%EC%99%84%EB%B2%BD-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developshrimp.com/entry/JAVA-%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4Abstract-%EC%99%84%EB%B2%BD-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1756221790845&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[JAVA] 추상 클래스(Abstract) 완벽 이해하기&quot; data-og-description=&quot;추상 클래스란? 추상 클래스는 추상 메서드를 하나라도 가지고 있는 클래스를 만한다. 추상 메서드는 &amp;quot;메서드가 완성되지 않은, 껍데기만 있는 메서드&amp;quot; 이다. 쉽게 이해하기 위해서 예를 들자면 &quot; data-og-host=&quot;developshrimp.com&quot; data-og-source-url=&quot;https://developshrimp.com/entry/JAVA-%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4Abstract-%EC%99%84%EB%B2%BD-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://developshrimp.com/entry/JAVA-%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4Abstract-%EC%99%84%EB%B2%BD-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ckTUXw/hyZCZtUOgS/wkNZms17rkwN1r6qA6EkvK/img.png?width=800&amp;amp;height=488&amp;amp;face=0_0_800_488,https://scrap.kakaocdn.net/dn/6JsB5/hyZGfBRkLH/uKFiowseMqKMBld8JsHUT1/img.png?width=800&amp;amp;height=488&amp;amp;face=0_0_800_488,https://scrap.kakaocdn.net/dn/fxljz/hyZGdjJOSX/2N72tLlz2vpTPHY42r9iwK/img.png?width=1800&amp;amp;height=1098&amp;amp;face=0_0_1800_1098&quot;&gt;&lt;a href=&quot;https://developshrimp.com/entry/JAVA-%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4Abstract-%EC%99%84%EB%B2%BD-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developshrimp.com/entry/JAVA-%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4Abstract-%EC%99%84%EB%B2%BD-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ckTUXw/hyZCZtUOgS/wkNZms17rkwN1r6qA6EkvK/img.png?width=800&amp;amp;height=488&amp;amp;face=0_0_800_488,https://scrap.kakaocdn.net/dn/6JsB5/hyZGfBRkLH/uKFiowseMqKMBld8JsHUT1/img.png?width=800&amp;amp;height=488&amp;amp;face=0_0_800_488,https://scrap.kakaocdn.net/dn/fxljz/hyZGdjJOSX/2N72tLlz2vpTPHY42r9iwK/img.png?width=1800&amp;amp;height=1098&amp;amp;face=0_0_1800_1098');&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;[JAVA] 추상 클래스(Abstract) 완벽 이해하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;추상 클래스란? 추상 클래스는 추상 메서드를 하나라도 가지고 있는 클래스를 만한다. 추상 메서드는 &quot;메서드가 완성되지 않은, 껍데기만 있는 메서드&quot; 이다. 쉽게 이해하기 위해서 예를 들자면&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developshrimp.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developshrimp.com/entry/JAVA-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4Interface-%ED%95%B5%EC%8B%AC-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developshrimp.com/entry/JAVA-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4Interface-%ED%95%B5%EC%8B%AC-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1756221798758&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[JAVA] 인터페이스(Interface) 핵심 이해하기&quot; data-og-description=&quot;인터페이스(Interface)란 인터페이스는 프로그램 내 다양한 기능을 하는 클래스들에게 기본이 되는 틀(구조)를 제공하는 역할을 한다. 이전의 포스팅을 보았다면 알겠지만, 추상 클래스와 비슷한 &quot; data-og-host=&quot;developshrimp.com&quot; data-og-source-url=&quot;https://developshrimp.com/entry/JAVA-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4Interface-%ED%95%B5%EC%8B%AC-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://developshrimp.com/entry/JAVA-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4Interface-%ED%95%B5%EC%8B%AC-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bsbumL/hyZDdr9vn9/uMFnPUw6K2dAHwS1NBOPK1/img.png?width=800&amp;amp;height=347&amp;amp;face=0_0_800_347,https://scrap.kakaocdn.net/dn/ccnom4/hyZGg8BFbd/ovWMqfW1cKHpQrolw5tcV0/img.png?width=800&amp;amp;height=347&amp;amp;face=0_0_800_347,https://scrap.kakaocdn.net/dn/7lwhD/hyZDc7QqBZ/Eko1mf8EgXtvAexV5C1DLk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024&quot;&gt;&lt;a href=&quot;https://developshrimp.com/entry/JAVA-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4Interface-%ED%95%B5%EC%8B%AC-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developshrimp.com/entry/JAVA-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4Interface-%ED%95%B5%EC%8B%AC-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bsbumL/hyZDdr9vn9/uMFnPUw6K2dAHwS1NBOPK1/img.png?width=800&amp;amp;height=347&amp;amp;face=0_0_800_347,https://scrap.kakaocdn.net/dn/ccnom4/hyZGg8BFbd/ovWMqfW1cKHpQrolw5tcV0/img.png?width=800&amp;amp;height=347&amp;amp;face=0_0_800_347,https://scrap.kakaocdn.net/dn/7lwhD/hyZDc7QqBZ/Eko1mf8EgXtvAexV5C1DLk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024');&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;[JAVA] 인터페이스(Interface) 핵심 이해하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;인터페이스(Interface)란 인터페이스는 프로그램 내 다양한 기능을 하는 클래스들에게 기본이 되는 틀(구조)를 제공하는 역할을 한다. 이전의 포스팅을 보았다면 알겠지만, 추상 클래스와 비슷한&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developshrimp.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/java/difference-between-abstract-class-and-interface-in-java/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.geeksforgeeks.org/java/difference-between-abstract-class-and-interface-in-java/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1756224443527&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Difference Between Abstract Class and Interface in Java - GeeksforGeeks&quot; data-og-description=&quot;Your All-in-One Learning Portal: GeeksforGeeks is a comprehensive educational platform that empowers learners across domains-spanning computer science and programming, school education, upskilling, commerce, software tools, competitive exams, and more.&quot; data-og-host=&quot;www.geeksforgeeks.org&quot; data-og-source-url=&quot;https://www.geeksforgeeks.org/java/difference-between-abstract-class-and-interface-in-java/&quot; data-og-url=&quot;https://www.geeksforgeeks.org/java/difference-between-abstract-class-and-interface-in-java/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nysz1/hyZGirOLsf/0zdxcKjLJao9k88QqTCOP1/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200,https://scrap.kakaocdn.net/dn/LbqWf/hyZCXiBGlr/9gJcYauKimUXHwykQCkjtk/img.png?width=603&amp;amp;height=240&amp;amp;face=0_0_603_240,https://scrap.kakaocdn.net/dn/bKEZpa/hyZCY2RX5h/IR0kf3IZdHAEhceKYxvj4k/img.png?width=603&amp;amp;height=240&amp;amp;face=0_0_603_240&quot;&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/java/difference-between-abstract-class-and-interface-in-java/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.geeksforgeeks.org/java/difference-between-abstract-class-and-interface-in-java/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nysz1/hyZGirOLsf/0zdxcKjLJao9k88QqTCOP1/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200,https://scrap.kakaocdn.net/dn/LbqWf/hyZCXiBGlr/9gJcYauKimUXHwykQCkjtk/img.png?width=603&amp;amp;height=240&amp;amp;face=0_0_603_240,https://scrap.kakaocdn.net/dn/bKEZpa/hyZCY2RX5h/IR0kf3IZdHAEhceKYxvj4k/img.png?width=603&amp;amp;height=240&amp;amp;face=0_0_603_240');&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;Difference Between Abstract Class and Interface in Java - GeeksforGeeks&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Your All-in-One Learning Portal: GeeksforGeeks is a comprehensive educational platform that empowers learners across domains-spanning computer science and programming, school education, upskilling, commerce, software tools, competitive exams, and more.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.geeksforgeeks.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend</category>
      <category>abstract class</category>
      <category>final</category>
      <category>interface</category>
      <category>다형성</category>
      <category>인터페이스</category>
      <category>추상클래스</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/422</guid>
      <comments>https://daily1313.tistory.com/entry/Java-final-interface-abstract-class#entry422comment</comments>
      <pubDate>Wed, 27 Aug 2025 01:24:33 +0900</pubDate>
    </item>
    <item>
      <title>[CS] clustered Index, non-clustered Index</title>
      <link>https://daily1313.tistory.com/entry/CS-clustered-Index-non-clustered-Index</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Index&lt;/h2&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;대표 자료 구조: B+ Tree
&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;/li&gt;
&lt;li&gt;검색, 삽입, 삭제 연산을 로그 시간에 수행이 가능합니다.&lt;/li&gt;
&lt;li&gt;트리 구조에서 leaf node 간 연결되기 때문에 범위 검색이 효율적입니다.&lt;/li&gt;
&lt;li&gt;인덱스를 추가하면, 쓰기 시점에 B+ tree 구조의 정렬된 상태의 데이터가 생성됩니다.&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;MySQL의 기본 스토리지 엔진&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;innoDB&lt;/li&gt;
&lt;li&gt;스토리지 엔진(Storage Engine): DB에서 데이터 저장 및 관리 장치&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;InnoDB는 테이블마다 Clustered Index를 자동 생성&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Primary Key를 기준으로 정렬된 Clustered Index
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Primary Index(주 인덱스)라고도 불립니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;정확히는 규칙이 있지만, 일반적으로 Primary Key에 생성됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Clustered Index는 leaf node의 값으로 행 데이터(row data)를 가집니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1756220602449&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mysql&amp;gt; explain select * from article where article_id = 167598086197714944;
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | article | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)
-&amp;gt; 자동으로 clustered Index 생성 (Primary Key를 이용한 조회)&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;h2 data-ke-size=&quot;size26&quot;&gt;인덱스의 종류&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot;&gt;1. clustered Index (=Primary Key 인덱스, 클러스터형 인덱스)&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/raQZe/btsP6VuIaxm/zFf8xlzzNdLjvnXWKtunFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/raQZe/btsP6VuIaxm/zFf8xlzzNdLjvnXWKtunFK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;698&quot; data-filename=&quot;스크린샷 2025-08-27 오전 12.07.56.png&quot; style=&quot;width: 45.0099%; margin-right: 10px;&quot; data-widthpercent=&quot;45.54&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/raQZe/btsP6VuIaxm/zFf8xlzzNdLjvnXWKtunFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FraQZe%2FbtsP6VuIaxm%2FzFf8xlzzNdLjvnXWKtunFK%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;1184&quot; height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WYrsU/btsP66wnBME/vLPQfoQx2PxOdrDk8uL03K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WYrsU/btsP66wnBME/vLPQfoQx2PxOdrDk8uL03K/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1136&quot; data-origin-height=&quot;560&quot; data-filename=&quot;스크린샷 2025-08-27 오전 12.08.08.png&quot; style=&quot;width: 53.8273%;&quot; data-widthpercent=&quot;54.46&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WYrsU/btsP66wnBME/vLPQfoQx2PxOdrDk8uL03K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWYrsU%2FbtsP66wnBME%2FvLPQfoQx2PxOdrDk8uL03K%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;1136&quot; height=&quot;560&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;clustered Index&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;InnoDB에서 기본으로 제공하는 인덱스 (Table당 1개만 생성)&lt;/li&gt;
&lt;li&gt;Primary Key 기준으로 정렬된 인덱스&lt;/li&gt;
&lt;li&gt;Leaf Node에 실제 데이터 row가 포함됨 &amp;rarr; 빠른 데이터 접근이 가능합니다.&lt;/li&gt;
&lt;li&gt;테이블 데이터는 PK 순으로 저장되기 때문에 PRIMARY KEY 값이 변경되면 그 레코드의 물리적인 저장 위치가 바뀌어야 합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;테이블의 데이터가 정렬되어 있기에 속도면에서 우수한 성능을 보이지만, 데이터의 추가/수정/삭제 시 레코드를 매번 정렬해야 하기 때문에 추가/수정/삭제의 성능이 저하됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. &lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot;&gt;Non-clustered &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot;&gt;Index (Secondary Index = , 보조 인덱스)&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAqEoN/btsP7vWA9xc/o4qXeTuHOArGlgqqWZXJC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAqEoN/btsP7vWA9xc/o4qXeTuHOArGlgqqWZXJC0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;796&quot; data-filename=&quot;스크린샷 2025-08-27 오전 12.14.05.png&quot; style=&quot;width: 41.8661%; margin-right: 10px;&quot; data-widthpercent=&quot;42.36&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAqEoN/btsP7vWA9xc/o4qXeTuHOArGlgqqWZXJC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAqEoN%2FbtsP7vWA9xc%2Fo4qXeTuHOArGlgqqWZXJC0%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;1076&quot; height=&quot;796&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bO15pb/btsP6LlLNtv/lK0Ch816NKnopFFFRtfFUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bO15pb/btsP6LlLNtv/lK0Ch816NKnopFFFRtfFUk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;598&quot; data-filename=&quot;스크린샷 2025-08-27 오전 12.14.17.png&quot; style=&quot;width: 56.9711%;&quot; data-widthpercent=&quot;57.64&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bO15pb/btsP6LlLNtv/lK0Ch816NKnopFFFRtfFUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbO15pb%2FbtsP6LlLNtv%2FlK0Ch816NKnopFFFRtfFUk%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;1100&quot; height=&quot;598&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;non-clustered Index&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Table 당 여러 개 생성이 가능합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Leaf Node에 실제 row가 없습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스의 key 값과 데이터 행을 가리키는 포인터가 존재합니다. (인덱스 Key 값 + Primary Key 컬럼 값)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;특정 데이터를 access 하기 위해서는 secondary Index의 key 컬럼에서 Primary Key 값을 access 하고, 해당 값으로 Primary Key를 검색하여 원하는 데이터를 찾아가는 형태로 처리됩니다. (clustered Index와는 다르게 tree를 2번 타야 합니다.)&lt;/li&gt;
&lt;li&gt;테이블 데이터가 대용량으로 변경될 경우 secondary Index를 재구성하기 위해 많은 자원이 사용됩니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;물리적으로 레코드를 정렬하지 않기 때문에 clustered index보다 속도면에서는 성능이 떨어지지만, 추가/수정/삭제의 성능은 뛰어납니다.&lt;/li&gt;
&lt;li&gt;unique 제약조건을 설정한 컬럼에 대해 자동으로 Non-clustered Index를 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ol&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. id값을 주로 Primary Key로 두는 이유&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;각각의 테이블마다 PK로 id값을 가지고 있다 보니 일관성이 생깁니다. (다른 컬럼이 PK라면, 각각의 테이블의 PK를 알아내기 위해 테이블을 확인할 필요가 있습니다.)&lt;/li&gt;
&lt;li&gt;MySQL에서 PK를 설정하면 해당 값을 Index로 잡아 데이터를 B-tree 구조로 저장합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정수형 id는 정렬/저장에 최적화되어 있어서 검색, 범위조회, JOIN 모두 빠르게 동작합니다.&lt;/li&gt;
&lt;li&gt;id 컬럼이 자동 증가하는 순서로 삽입되면, 트리의 균형을 유지하는데 있어서 추가적인 비용이 줄어들 수 있습니다. (O(logN) 보장)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;대규모 데이터베이스에서 샤딩이나 파티셔닝과 같이 데이터를 쪼개고 나눌 때 유리합니다.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>CS</category>
      <category>clustered index</category>
      <category>index</category>
      <category>Non-clustered Index</category>
      <category>primary key</category>
      <category>unique key</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/421</guid>
      <comments>https://daily1313.tistory.com/entry/CS-clustered-Index-non-clustered-Index#entry421comment</comments>
      <pubDate>Wed, 27 Aug 2025 00:21:47 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Functional Interface</title>
      <link>https://daily1313.tistory.com/entry/Java-Functional-Interface</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;함수형 인터페이스&amp;nbsp;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추상 메서드(구현 없이 선언만 된 메서드)가 1개만 정의된 인터페이스&lt;/li&gt;
&lt;li&gt;default 메서드: 인터페이스 안에서 메서드 구현을 가질 수 있도록 허용하는 문법&lt;/li&gt;
&lt;li&gt;static 메서드: 객체를 생성하지 않고도 호출할 수 있는 메서드 (공용 기능, 유틸리티 기능을 활용할 때 사용)&lt;/li&gt;
&lt;li&gt;람다 표현식: 자바의 메소드를 간결한 함수식으로 표현한 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;함수형 인터페이스의 표준 API 종류&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 71.3952%; height: 121px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 9.42382%; text-align: center; height: 19px;&quot;&gt;인터페이스&lt;/td&gt;
&lt;td style=&quot;width: 14.319%; text-align: center; height: 19px;&quot;&gt;추상 메서드&lt;/td&gt;
&lt;td style=&quot;width: 14.319%; text-align: center; height: 19px;&quot;&gt;lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.42382%; height: 17px;&quot;&gt;Runnable&lt;/td&gt;
&lt;td style=&quot;width: 14.319%; height: 17px;&quot;&gt;void run()&lt;/td&gt;
&lt;td style=&quot;width: 14.319%; height: 17px;&quot;&gt;() -&amp;gt; void&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.42382%; height: 17px;&quot;&gt;Consumer&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.319%; height: 17px;&quot;&gt;void accept(T t)&lt;/td&gt;
&lt;td style=&quot;width: 14.319%; height: 17px;&quot;&gt;T -&amp;gt; void&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.42382%; height: 17px;&quot;&gt;Supplier&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.319%; height: 17px;&quot;&gt;T get()&lt;/td&gt;
&lt;td style=&quot;width: 14.319%; height: 17px;&quot;&gt;() -&amp;gt; T&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.42382%; height: 17px;&quot;&gt;Function&amp;lt;T, R&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.319%; height: 17px;&quot;&gt;R apply(T t)&lt;/td&gt;
&lt;td style=&quot;width: 14.319%; height: 17px;&quot;&gt;T -&amp;gt; R&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.42382%; height: 17px;&quot;&gt;Predicate&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.319%; height: 17px;&quot;&gt;boolean test(T t)&lt;/td&gt;
&lt;td style=&quot;width: 14.319%; height: 17px;&quot;&gt;T -&amp;gt; boolean&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.42382%; height: 17px;&quot;&gt;Comparator&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.319%; height: 17px;&quot;&gt;int compare(T o1, T o2)&lt;/td&gt;
&lt;td style=&quot;width: 14.319%; height: 17px;&quot;&gt;(T, T) -&amp;gt; int&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Runnable&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-25 오후 11.58.43.png&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/znZOb/btsP7ncw8nN/YK3q5wKKe2gp6hl4m0Um1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/znZOb/btsP7ncw8nN/YK3q5wKKe2gp6hl4m0Um1K/img.png&quot; data-alt=&quot;Runnable interface&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/znZOb/btsP7ncw8nN/YK3q5wKKe2gp6hl4m0Um1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FznZOb%2FbtsP7ncw8nN%2FYK3q5wKKe2gp6hl4m0Um1K%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;772&quot; height=&quot;627&quot; data-filename=&quot;스크린샷 2025-08-25 오후 11.58.43.png&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;694&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Runnable interface&lt;/figcaption&gt;
&lt;/figure&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;void run(): 인수 없이 리턴을 하지 않는 함수 형태, 대표적으로 스레드의 매개 변수로 이용합니다.&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;2. Consumer&amp;lt;T&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-25 오후 11.59.32.png&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7Nmtn/btsP5nLC06R/OXkyOFNpqWDa3gfRk3mG2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7Nmtn/btsP5nLC06R/OXkyOFNpqWDa3gfRk3mG2k/img.png&quot; data-alt=&quot;Consumer interface&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7Nmtn/btsP5nLC06R/OXkyOFNpqWDa3gfRk3mG2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7Nmtn%2FbtsP5nLC06R%2FOXkyOFNpqWDa3gfRk3mG2k%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;828&quot; height=&quot;382&quot; data-filename=&quot;스크린샷 2025-08-25 오후 11.59.32.png&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Consumer interface&lt;/figcaption&gt;
&lt;/figure&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;void accept(T 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;3. Supplier&amp;lt;T&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-26 오전 12.00.21.png&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;664&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chmp4I/btsP6UaHZeE/0lRKC6lNkdPu1dVW172nx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chmp4I/btsP6UaHZeE/0lRKC6lNkdPu1dVW172nx0/img.png&quot; data-alt=&quot;Supplier Interface&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chmp4I/btsP6UaHZeE/0lRKC6lNkdPu1dVW172nx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fchmp4I%2FbtsP6UaHZeE%2F0lRKC6lNkdPu1dVW172nx0%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;792&quot; height=&quot;629&quot; data-filename=&quot;스크린샷 2025-08-26 오전 12.00.21.png&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;664&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Supplier Interface&lt;/figcaption&gt;
&lt;/figure&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;T get(): 매개변수를 사용하지 않고, 리턴만 하는 함수 형태입니다.&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;4. Function&amp;lt;T, R&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-26 오전 12.01.08.png&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;526&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BRfJo/btsP4jputDg/oy6d9fdBciAn25kgN7UGo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BRfJo/btsP4jputDg/oy6d9fdBciAn25kgN7UGo1/img.png&quot; data-alt=&quot;Function Interface&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BRfJo/btsP4jputDg/oy6d9fdBciAn25kgN7UGo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBRfJo%2FbtsP4jputDg%2Foy6d9fdBciAn25kgN7UGo1%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;745&quot; height=&quot;463&quot; data-filename=&quot;스크린샷 2025-08-26 오전 12.01.08.png&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;526&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Function Interface&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 인수(T)를 받아들이고 결과를 생성(R)하는 함수를 제공합니다. (인수를 하나의 결과로 반환할 때 사용)&lt;/li&gt;
&lt;li&gt;R apply(T 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;5. Predicate&amp;lt;T&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-26 오전 12.02.04.png&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caoZZa/btsP7pnUGN9/jfE6yXg4iKR9HwmprBEe31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caoZZa/btsP7pnUGN9/jfE6yXg4iKR9HwmprBEe31/img.png&quot; data-alt=&quot;Predicate Interface&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caoZZa/btsP7pnUGN9/jfE6yXg4iKR9HwmprBEe31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaoZZa%2FbtsP7pnUGN9%2FjfE6yXg4iKR9HwmprBEe31%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;818&quot; height=&quot;586&quot; data-filename=&quot;스크린샷 2025-08-26 오전 12.02.04.png&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Predicate Interface&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 인수를 받아 true/false를 리턴하는 함수를 제공합니다. (조건에 맞는 객체를 필터링하는 경우)&lt;/li&gt;
&lt;li&gt;boolean test(T, t): 매개값이 조건에 맞는지 단정해서 boolean 리턴&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;6. Comparator&amp;lt;T&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-26 오전 12.02.57.png&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;894&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beC0Oe/btsP5gMgzmW/81dmjrghiVDQkkG8vnNYA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beC0Oe/btsP5gMgzmW/81dmjrghiVDQkkG8vnNYA1/img.png&quot; data-alt=&quot;Comparator Interface&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beC0Oe/btsP5gMgzmW/81dmjrghiVDQkkG8vnNYA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeC0Oe%2FbtsP5gMgzmW%2F81dmjrghiVDQkkG8vnNYA1%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;752&quot; height=&quot;782&quot; data-filename=&quot;스크린샷 2025-08-26 오전 12.02.57.png&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;894&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Comparator Interface&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;T타입 인수 값을 연산해서 int 타입을 리턴하는 함수를 제공합니다. (객체 비교)&lt;/li&gt;
&lt;li&gt;int compare(T o1, T o2)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;음수: o1 &amp;lt; o2, 0: o1 = o2, 양수: o1 &amp;gt; o2&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>CS</category>
      <category>Comparator</category>
      <category>consumer</category>
      <category>function</category>
      <category>Functional Interface</category>
      <category>Predicate</category>
      <category>Runnable</category>
      <category>SUPPLIER</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/420</guid>
      <comments>https://daily1313.tistory.com/entry/Java-Functional-Interface#entry420comment</comments>
      <pubDate>Tue, 26 Aug 2025 00:32:06 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Stream, Collection</title>
      <link>https://daily1313.tistory.com/entry/Java-Stream-Collection</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서는 방대한 데이터를 효율적으로 다루기 위해 Collection과 Stream이라는 강력한 도구를 제공합니다. Collection은 데이터를 저장하고 관리하기 위한 자료구조의 집합이며, Stream은 이러한 데이터를 보다 선언적이고 간결하게 처리할 수 있도록 돕는 API입니다. 이 두 가지를 이해하면 반복문과 조건문에 의존하던 기존 방식에서 벗어나, 가독성과 유지보수성이 높은 코드를 작성할 수 있게 됩니다.&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;이해를 돕기 위해 Stream, Collection 관련 예제 코드를 작성해 보았는데, 첨부된 링크를 통해 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/daily1313/java-stream-collection&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/daily1313/java-stream-collection&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1755236493976&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - daily1313/java-stream-collection: java stream and collection (java 17)&quot; data-og-description=&quot;java stream and collection (java 17). Contribute to daily1313/java-stream-collection development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/daily1313/java-stream-collection&quot; data-og-url=&quot;https://github.com/daily1313/java-stream-collection&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eqYB9e/hyZzz8IG3y/jejKskELBu4UUPVJzaRmM0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/gdE2f/hyZyeDRdp9/VgTIBxyy03uYbTcFebsJN1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/daily1313/java-stream-collection&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/daily1313/java-stream-collection&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eqYB9e/hyZzz8IG3y/jejKskELBu4UUPVJzaRmM0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/gdE2f/hyZyeDRdp9/VgTIBxyy03uYbTcFebsJN1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - daily1313/java-stream-collection: java stream and collection (java 17)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;java stream and collection (java 17). Contribute to daily1313/java-stream-collection development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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 data-ke-size=&quot;size26&quot;&gt;Stream&lt;/h2&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;요소들의 Stream에 함수형 연산을 지원하는 클래스&lt;/li&gt;
&lt;li&gt;Stream API는 이러한 작업을 간편하게 수행할 수 있도록 다양한 기능을 제공할 뿐만 아니라, 병렬 처리를 통해 처리 속도를 높일 수 있습니다.&lt;/li&gt;
&lt;li&gt;따라서, Collection F/W를 통해 관리하는 데이터를 처리하기 위해 주로 사용합니다.&lt;/li&gt;
&lt;li&gt;Stream API의 다양한 기능들은 대부분 람다를 필요로 하기 때문에 람다를 이해하고 사용할 수 있어야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stream 연산&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-15 오후 2.40.26.png&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VLenr/btsPTCB7Bbr/P3Vd2KTky1w9zA9cmb9F3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VLenr/btsPTCB7Bbr/P3Vd2KTky1w9zA9cmb9F3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VLenr/btsPTCB7Bbr/P3Vd2KTky1w9zA9cmb9F3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVLenr%2FbtsPTCB7Bbr%2FP3Vd2KTky1w9zA9cmb9F3K%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;641&quot; height=&quot;132&quot; data-filename=&quot;스크린샷 2025-08-15 오후 2.40.26.png&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;/figure&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;파이프라인을 구성할 수 있다는 것은 스트림 대상 데이터에 대한 다양한 연산을 조합할 수 있다는 것을 의미합니다.&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;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 스트림 생성 -&amp;gt; 가공(중간 연산) -&amp;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;Stream 생성&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터의 컬렉션(집합)을 Stream으로 변환하는 과정입니다.&lt;/li&gt;
&lt;li&gt;Stream API를 사용하기 위해서 최초 1번 수행되어야 하며, 생성 단계에서는 모든 데이터를 한꺼번에 불러오지 않고 필요할 때만 불러옵니다.
&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;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stream 가공(중간 연산)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가공(중간연산): 소스의 데이터 집합을 원하는 형태로 가공하는 것
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;filter, map과 같은 연산으로 Stream을 반환합니다.&lt;/li&gt;
&lt;li&gt;중간 연산은 연속에서 호출하는 메소드 체이닝으로 구현이 가능합니다.&lt;/li&gt;
&lt;li&gt;최종 연산이 실행되어야 중간연산이 처리되므로 중간연산들로만 구성된 메서드 체인은 실행되지 않습니다. (Lazy Evaluation)&lt;/li&gt;
&lt;li&gt;Lazy Evaluation: 최종 연산을 호출하기 전까지 중간 연산을 지연시키는 행위&lt;/li&gt;
&lt;/ul&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;filter: 필터 처리 (조건문을 통한 데이터 선별)&lt;/li&gt;
&lt;li&gt;map: 데이터 변환&lt;/li&gt;
&lt;li&gt;sorted: 정렬&lt;/li&gt;
&lt;li&gt;peek: 가공된 데이터를 파악하기 위한 용도&lt;/li&gt;
&lt;li&gt;disctinct: 중복 제거&lt;/li&gt;
&lt;li&gt;limit: 개수 제한&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stream 최종 연산&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최종연산: Stream에 대한 최종 연산을 수행하는 것. (최종적인 목적물을 얻는 처리과정)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;forEach, collect와 같은 연산으로 void를 반환하거나 컬렉션 타입을 반환&lt;/li&gt;
&lt;li&gt;스트림이 관리하는 전체 데이터에 대한 순회 작업은 최종 연산인 forEach() 메서드를 이용합니다.&lt;/li&gt;
&lt;li&gt;collect() 메소드는 스트림 처리 이후 처리된 데이터에 대해 Collection 객체로 반환하는 메서드입니다.&lt;/li&gt;
&lt;li&gt;스트림의 최종 연산은 forEach()와 같은 스트림 처리 결과를 바로 확인할 수 있는 연산이 있고, 데이터를 모두 소모한 이후에 그 결과를 알 수 있는 count()와 같은 연산이 있습니다.&lt;/li&gt;
&lt;li&gt;이 외에도 특정 데이터를 검색할 수 있는 allMatch(), anyMatch() 등과 같은 다양한 메서드들을 제공합니다.&lt;/li&gt;
&lt;/ul&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;forEach(): stream의 각 요소를 순회하면서 출력 등의 처리를 위해 사용합니다.&lt;/li&gt;
&lt;li&gt;reduce(): stream의 요소를 줄여나가면서 연산을 수행합니다.&lt;/li&gt;
&lt;li&gt;findFirst(), findAny(): 특정 조건에 맞는 요소를 찾기 위해 사용합니다.&lt;/li&gt;
&lt;li&gt;anyMatch(), allMatch(), noneMatch(): 조건에 맞는지 확인을 위해 사용합니다.&lt;/li&gt;
&lt;li&gt;count(), min(), max(), sum(), average(): 요소의 개수, 최소(대)값, 합계, 평균을 위해 사용합니다.&lt;/li&gt;
&lt;li&gt;collect(): stream의 요소를 수집하여 원하는 형태로 변환하기 위해서 사용합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;toList(), toSet(), toCollection(), toArray(), toMap()&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stream 특징&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;li&gt;병렬처리를 지원합니다. (멀티스레드로 병렬처리 후 대량의 데이터를 빠르고 쉽게 처리) (parallel(), parallelStream())&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream의 전 과정에 대해 상세하게 설명드렸으니, Stream을 활용한 예제 코드를 살펴보겠습니다.&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;Stream Example&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;classpath에 user.csv 파일이 제공되어 있다고 가정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1755237182940&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;이름, 취미, 소개
김프로, 축구:농구:야구, 구기종목 좋아요
정프로, 개발:당구:축구, 개발하는데 뛰긴 싫어
앙몬드, 피아노, 죠르디가 좋아요 좋아좋아너무좋아
죠르디, 스포츠댄스:개발, 개발하는 죠르디 좋아
박프로, 골프:야구, 운동이 좋아요
정프로, 개발:축구:농구, 개발도 좋고 운동도 좋아&lt;/code&gt;&lt;/pre&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.&amp;nbsp; 취미별 선호 인원 구하기&lt;/h4&gt;
&lt;pre id=&quot;code_1755237277610&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;- Solution-1
public Map&amp;lt;String, Integer&amp;gt; quiz1() throws IOException {
    List&amp;lt;String[]&amp;gt; csvLines = readCsvLines();

    Map&amp;lt;String, Integer&amp;gt; results = csvLines.stream() // 스트림 생성
            .flatMap(line -&amp;gt; Arrays.stream(line[1].replaceAll(&quot;\\s&quot;, &quot;&quot;).split(&quot;:&quot;))) // 중간 연산
            .collect(Collectors.toMap(k -&amp;gt; k, v -&amp;gt; 1, Integer::sum)); // 최종 연산

    results.entrySet().forEach(entry -&amp;gt; {
        System.out.println(entry.getKey() + &quot;:&quot; + entry.getValue());
    });

    return results;
}

- Solution-2
public Map&amp;lt;String, Integer&amp;gt; quiz1() throws IOException {
    List&amp;lt;String[]&amp;gt; csvLines = readCsvLines();

    Map&amp;lt;String, Integer&amp;gt; results = new HashMap&amp;lt;&amp;gt;();

    csvLines.stream()
            .flatMap(line -&amp;gt; Arrays.stream(line[1].replaceAll(&quot;\\s&quot;, &quot;&quot;).split(&quot;:&quot;)))
            .forEach(hobby -&amp;gt; results.merge(hobby, 1, Integer::sum));

    results.entrySet().forEach(entry -&amp;gt; {
        System.out.println(entry.getKey() + &quot;:&quot; + entry.getValue());
    });

    return results;
}

private List&amp;lt;String[]&amp;gt; readCsvLines() throws IOException {
    CSVReader csvReader = new CSVReader(new FileReader(getClass().getResource(&quot;/user.csv&quot;).getFile()));
    csvReader.readNext();
    return csvReader.readAll();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;csv 파일을 CsvReaders 클래스를 통해 한 줄씩 파싱합니다.&lt;/li&gt;
&lt;li&gt;flatMap 메서드를 통해 콜론을 기반으로 파싱 한 데이터를 한 번에 불러옵니다.&amp;nbsp;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;flatMap: Stream&amp;lt;String&amp;gt; 형식으로 모든 원소가 한 줄로 펼쳐집니다. --&amp;gt; flatMap을 활용하려면 새롭게 스트림을 생성 (Arrays.stream())을 해주어야 합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Map: Stream&amp;lt;Stream&amp;lt;String&amp;gt;&amp;gt; 형식으로 스트림 안에 스트림이 들어있는 형태입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;collect(), forEach() 메서드를 통해 각 취미별 선호하는 count를 집계합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.&amp;nbsp; List 데이터 가공하기&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1755239524268&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static List&amp;lt;String&amp;gt; WORDS = Arrays.asList(&quot;TONY&quot;, &quot;a&quot;, &quot;hULK&quot;, &quot;B&quot;, &quot;america&quot;, &quot;X&quot;, &quot;nebula&quot;, &quot;Korea&quot;);

    // 2.1 List에 저장된 단어들의 접두사가 각각 몇개씩 있는지 Map&amp;lt;String, Integer&amp;gt;으로 변환하여라.
    // ex) (&quot;T&quot;, 1), (&quot;a&quot;, 2) ...
    public Map&amp;lt;String, Integer&amp;gt; quiz1() {
        Map&amp;lt;String, Integer&amp;gt; results = WORDS.stream()
                .map(word -&amp;gt; word.substring(0, 1))
                .collect(Collectors.toMap(key -&amp;gt; key, value -&amp;gt; 1, Integer::sum));

        results.entrySet().forEach(entry -&amp;gt; {
            System.out.println(entry.getKey() + &quot; : &quot; + entry.getValue());
        });

        return results;
    }

    // 2.2 List에 저장된 단어들 중에서 단어의 길이가 2 이상인 경우에만, 모든 단어를 대문자로 변환하여 스페이스로 구분한 하나의 문자열로 합한 결과를 반환하여라.
    // ex) [&quot;Hello&quot;, &quot;a&quot;, &quot;Island&quot;, &quot;b&quot;] -&amp;gt; &amp;ldquo;H I&amp;rdquo;
    public String quiz2() {
        String result = WORDS.stream()
                .filter(word -&amp;gt; word.length() &amp;gt;= 2)
                .map(word -&amp;gt; word.substring(0, 1).toUpperCase())
                .collect(Collectors.joining(&quot; &quot;));

        System.out.println(result);
        return result;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;조건에 입각하여 filter 처리를 해줍니다. (Prefix 추출, 단어수 2 이상)&lt;/li&gt;
&lt;li&gt;map으로 Prefix 데이터를 변환해 줍니다.&lt;/li&gt;
&lt;li&gt;collect()를 통해 최종 연산의 결과를 테스트 케이스에 맞게 반환합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 문자열 가공, 임의 로또 번호 생성, 주사위 값 6인 리스트 반환&lt;/h4&gt;
&lt;pre id=&quot;code_1755240069481&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Q5 {

    private static final String[] STRING_ARR = {&quot;aaa&quot;, &quot;bb&quot;, &quot;c&quot;, &quot;dddd&quot;};

    // 5.1 모든 문자열의 길이를 더한 결과를 출력하여라.
    public int quiz1() {
        return Arrays.stream(STRING_ARR)
                .mapToInt(String::length)
                .sum();
    }

    // 5.2 문자열 중에서 가장 긴 것의 길이를 출력하시오.
    public int quiz2() {
        return Arrays.stream(STRING_ARR)
                .mapToInt(String::length)
                .max().orElse(0);
    }

    // 5.3 임의의 로또번호(1~45)를 정렬해서 출력하시오.
    public List&amp;lt;Integer&amp;gt; quiz3() {
        return IntStream.rangeClosed(1, 46)
                .distinct()
                .limit(6)
                .sorted()
                .boxed() // IntStream -&amp;gt; Stream&amp;lt;Integer&amp;gt;로 변환
                .collect(Collectors.toList());
    }

    // 5.4 두 개의 주사위를 굴려서 나온 눈의 합이 6인 경우를 모두 출력하시오.
    public List&amp;lt;Integer[]&amp;gt; quiz4() {
        return IntStream.rangeClosed(1, 5) 
            .mapToObj(i -&amp;gt; new Integer[]{i, 6 - i})
            .collect(Collectors.toList());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;5-1, 5-2: 문자열 배열을 Int 형식으로 변환하여 문자열의 길이의 합, 최대 길이를 구해줍니다.&lt;/li&gt;
&lt;li&gt;IntStream을 활용하여 로또 번호를 생성합니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;distinct(): 중복을 제거&lt;/li&gt;
&lt;li&gt;limit(): 6개만 선택&lt;/li&gt;
&lt;li&gt;sorted(): 오름차순 정렬&lt;/li&gt;
&lt;li&gt;boxed(): IntStream -&amp;gt; Stream&amp;lt;Integer&amp;gt;로 변환&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;IntStream을 활용하여 주사위의 눈의 합이 될 수 있는 수를 생성해 줍니다. (1~5)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;mapToObj(): 각 i에 대해 주사위의 눈이 합이 6이 되는 (i, 6-i) 쌍을 Integer[]로 생성합니다.&lt;/li&gt;
&lt;li&gt;이후 List로 수집합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Collection&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-15 오후 2.12.21.png&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;1314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9nZL3/btsPVZv0LxQ/QK6hpWMI5JvkjDDc4Iu9xK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9nZL3/btsPVZv0LxQ/QK6hpWMI5JvkjDDc4Iu9xK/img.png&quot; data-alt=&quot;Collection, Map 인터페이스 및 구현체&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9nZL3/btsPVZv0LxQ/QK6hpWMI5JvkjDDc4Iu9xK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9nZL3%2FbtsPVZv0LxQ%2FQK6hpWMI5JvkjDDc4Iu9xK%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;588&quot; height=&quot;545&quot; data-filename=&quot;스크린샷 2025-08-15 오후 2.12.21.png&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;1314&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Collection, Map 인터페이스 및 구현체&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Java Collection Framework (JCF)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 crud 하는데 필요한 자료구조와 알고리즘을 표준화해서 제공해 주는 클래스, 인터페이스의 집합&lt;/li&gt;
&lt;li&gt;Collection 인터페이스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;List, Set, Queue 인터페이스의 구현체가 존재합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Map 인터페이스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Collection과는 별개로 Map 인터페이스의 구현체가 존재합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;Iterable Interface&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;컬렉션 인터페이스들의 가장 최상의 인터페이스&lt;/li&gt;
&lt;li&gt;컬렉션들을 배우다 보면 자료들을 순회할 때 이터레이터 객체를 다뤄보게 되는데, 이터레이터 객체를 관리하는 인터페이스&lt;/li&gt;
&lt;/ol&gt;
&lt;table id=&quot;241ac551-863b-801d-b0f4-f1127202dde3&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr id=&quot;241ac551-863b-8053-8655-db06d1dc4260&quot;&gt;
&lt;td id=&quot;KdJ{&quot;&gt;메서드&lt;/td&gt;
&lt;td id=&quot;CmN&amp;#96;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;241ac551-863b-803d-af65-cfb9120ed791&quot;&gt;
&lt;td id=&quot;KdJ{&quot;&gt;default void forEach(Consumer&amp;lt;? super T&amp;gt; action)&lt;/td&gt;
&lt;td id=&quot;CmN&amp;#96;&quot;&gt;함수형 프로그래밍 전용 루프 메서드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;241ac551-863b-80ea-8e3a-d13119fafcb4&quot;&gt;
&lt;td id=&quot;KdJ{&quot;&gt;Iterator&amp;lt;T&amp;gt; iterator()&lt;/td&gt;
&lt;td id=&quot;CmN&amp;#96;&quot;&gt;컬렉션에서 이터레이터 구현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;241ac551-863b-80ff-9d9d-cd13ffe01fd8&quot;&gt;
&lt;td id=&quot;KdJ{&quot;&gt;default Spliterator&amp;lt;T&amp;gt; splierator()&lt;/td&gt;
&lt;td id=&quot;CmN&amp;#96;&quot;&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Collection Interface&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-15 오후 3.58.57.png&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2xEjQ/btsPUa6cy8C/4EmmrUqOQc0KAYYPybm130/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2xEjQ/btsPUa6cy8C/4EmmrUqOQc0KAYYPybm130/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2xEjQ/btsPUa6cy8C/4EmmrUqOQc0KAYYPybm130/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2xEjQ%2FbtsPUa6cy8C%2F4EmmrUqOQc0KAYYPybm130%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;658&quot; height=&quot;262&quot; data-filename=&quot;스크린샷 2025-08-15 오후 3.58.57.png&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;List, Set, Queue를 상속하는 상위 컬렉션 타입&lt;/li&gt;
&lt;li&gt;업캐스팅으로 다양한 종류의 컬렉션 자료형을 받아 자료를 crud할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;table id=&quot;241ac551-863b-806b-9c9d-e0d7a898fb70&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr id=&quot;241ac551-863b-80e6-b279-c4d5d7c89cc1&quot;&gt;
&lt;td id=&quot;a:iq&quot;&gt;메서드&lt;/td&gt;
&lt;td id=&quot;]:ex&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;241ac551-863b-8081-bd51-ca1eacd43292&quot;&gt;
&lt;td id=&quot;a:iq&quot;&gt;boolean add(Object o)&lt;br /&gt;boolean addAll(Collection c)&lt;/td&gt;
&lt;td id=&quot;]:ex&quot;&gt;Collection에 객체 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;241ac551-863b-80d6-827a-e4c9435c79d6&quot;&gt;
&lt;td id=&quot;a:iq&quot;&gt;boolean contains(Object o)&lt;br /&gt;boolean containsAll(Collection c)&lt;/td&gt;
&lt;td id=&quot;]:ex&quot;&gt;Collection에 객체들이 포함되어있는지 check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;241ac551-863b-8067-891f-e955788d8cd5&quot;&gt;
&lt;td id=&quot;a:iq&quot;&gt;boolean remove(Object o)&lt;br /&gt;boolean removeAll(Collction c)&lt;/td&gt;
&lt;td id=&quot;]:ex&quot;&gt;Collection에서 객체 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;241ac551-863b-80b8-a580-ee986880d911&quot;&gt;
&lt;td id=&quot;a:iq&quot;&gt;boolean retainAll(Collection c)&lt;/td&gt;
&lt;td id=&quot;]:ex&quot;&gt;Collection에 지정한 객체를 제외한 모든 객체 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;241ac551-863b-80fe-b41d-e6d88c791001&quot;&gt;
&lt;td id=&quot;a:iq&quot;&gt;void clear()&lt;/td&gt;
&lt;td id=&quot;]:ex&quot;&gt;Collection의 모든 객체를 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;241ac551-863b-8063-801d-c9f53056feef&quot;&gt;
&lt;td id=&quot;a:iq&quot;&gt;boolean equals(Object o)&lt;/td&gt;
&lt;td id=&quot;]:ex&quot;&gt;동일한 Collection인지 비교&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;241ac551-863b-80f0-bb52-efa129ce9ca9&quot;&gt;
&lt;td id=&quot;a:iq&quot;&gt;int hashCode()&lt;/td&gt;
&lt;td id=&quot;]:ex&quot;&gt;Collection의 hash code를 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;241ac551-863b-80d3-a369-fc8898cf0fd6&quot;&gt;
&lt;td id=&quot;a:iq&quot;&gt;boolean isEmpty()&lt;/td&gt;
&lt;td id=&quot;]:ex&quot;&gt;Collection이 비어있는지 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;241ac551-863b-802a-96af-c108fae3994d&quot;&gt;
&lt;td id=&quot;a:iq&quot;&gt;Iterator iterator()&lt;/td&gt;
&lt;td id=&quot;]:ex&quot;&gt;Collection의 iterator를 얻어서 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;241ac551-863b-800f-9d52-d9d02816c1b4&quot;&gt;
&lt;td id=&quot;a:iq&quot;&gt;int size()&lt;/td&gt;
&lt;td id=&quot;]:ex&quot;&gt;Collection에 저장된 객체의 개수를 반환&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;h3 data-ke-size=&quot;size23&quot;&gt;List (ArrayList, LinkedList, Vector, Stack)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;ArrayList (조회 성능 우수)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-15 오후 4.16.12.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HUKQQ/btsPUpvJSJC/lMke4jJ0twbXB2AMNKk0Nk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HUKQQ/btsPUpvJSJC/lMke4jJ0twbXB2AMNKk0Nk/img.png&quot; data-alt=&quot;https://www.geeksforgeeks.org/java/custom-arraylist-java/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HUKQQ/btsPUpvJSJC/lMke4jJ0twbXB2AMNKk0Nk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHUKQQ%2FbtsPUpvJSJC%2FlMke4jJ0twbXB2AMNKk0Nk%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;1270&quot; height=&quot;328&quot; data-filename=&quot;스크린샷 2025-08-15 오후 4.16.12.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;328&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.geeksforgeeks.org/java/custom-arraylist-java/&lt;/figcaption&gt;
&lt;/figure&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;데이터의 저장순서가 유지되고 중복을 허용합니다.&lt;/li&gt;
&lt;li&gt;데이터량에 따라 공간(capacity)을 자동으로 설정합니다. (Array와의 큰 차이점)&lt;/li&gt;
&lt;li&gt;단방향 포인터 구조로 자료에 대한 순차적인 접근에 강점이 있어 조회가 빠릅니다. (삽입/삭제는 성능이 떨어집니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;LinkedList (삽입/삭제 성능 우수)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-15 오후 4.16.44.png&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZgOTO/btsPSqP0bzw/GwcZfO4uP0KxsOB2t3IThK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZgOTO/btsPSqP0bzw/GwcZfO4uP0KxsOB2t3IThK/img.png&quot; data-alt=&quot;https://www.geeksforgeeks.org/java/linkedlist-getfirst-method-in-java/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZgOTO/btsPSqP0bzw/GwcZfO4uP0KxsOB2t3IThK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZgOTO%2FbtsPSqP0bzw%2FGwcZfO4uP0KxsOB2t3IThK%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;714&quot; height=&quot;316&quot; data-filename=&quot;스크린샷 2025-08-15 오후 4.16.44.png&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.geeksforgeeks.org/java/linkedlist-getfirst-method-in-java/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드를 연결하여 리스트처럼 만든 컬렉션 (배열 X)&lt;/li&gt;
&lt;li&gt;데이터의 중간 삽입, 삭제가 빈번할 경우 빠른 성능을 보장합니다.&lt;/li&gt;
&lt;li&gt;임의 요소에 대한 조회 성능이 좋지 않습니다.&lt;/li&gt;
&lt;li&gt;자바의 LinkedList는 양방향 포인터 구조로 이루어집니다.&lt;/li&gt;
&lt;li&gt;LinkedList는 리스트 용도 이외에도 스택, 큐, 트리 등의 자료구조의 근간이 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Vector (동기화 보장)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-15 오후 4.17.31.png&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rh4Rw/btsPTB4gVIC/lJcqDQrK8CFo1AfM58J3Lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rh4Rw/btsPTB4gVIC/lJcqDQrK8CFo1AfM58J3Lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rh4Rw/btsPTB4gVIC/lJcqDQrK8CFo1AfM58J3Lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frh4Rw%2FbtsPTB4gVIC%2FlJcqDQrK8CFo1AfM58J3Lk%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;689&quot; height=&quot;548&quot; data-filename=&quot;스크린샷 2025-08-15 오후 4.17.31.png&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;496&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ArrayList의 구형 버전으로서 모든 메서드가 동기화(synchronized) 되어 있어 Thread-safe 합니다.&lt;/li&gt;
&lt;li&gt;구버전 자바와의 호환성을 가집니다.&lt;/li&gt;
&lt;li&gt;내부에서 자동으로 동기화가 일어나, 성능이 좋지 않으며 무거워서 잘 사용하지 않습니다.&lt;/li&gt;
&lt;li&gt;컬렉션에 동기화가 필요하면, Collections.synchronizedList() 메서드를 이용해 ArrayList를 동기화 처리 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Stack&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-15 오후 4.17.55.png&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uNC3j/btsPSML6kAt/KKLWMznetppPOdnVcDXRo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uNC3j/btsPSML6kAt/KKLWMznetppPOdnVcDXRo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uNC3j/btsPSML6kAt/KKLWMznetppPOdnVcDXRo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuNC3j%2FbtsPSML6kAt%2FKKLWMznetppPOdnVcDXRo1%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;782&quot; height=&quot;414&quot; data-filename=&quot;스크린샷 2025-08-15 오후 4.17.55.png&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LIFO(Last-In-First-out) 구조의 자료구조&lt;/li&gt;
&lt;li&gt;마지막에 들어온 원소가 처음으로 나갑니다.&lt;/li&gt;
&lt;li&gt;들어올 때는 push, 나갈 때는 pop을 사용합니다.&lt;/li&gt;
&lt;li&gt;Stack은 Vector를 상속하기 때문에 문제점이 많아 잘 안 쓰입니다. (ArrayDeque로 대체)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Set (HashSet, LinkedHashSet, TreeSet)&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-15 오후 4.25.10.png&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7mjET/btsPSUiZia1/buvlgPd3PoM1m02IoRhIgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7mjET/btsPSUiZia1/buvlgPd3PoM1m02IoRhIgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7mjET/btsPSUiZia1/buvlgPd3PoM1m02IoRhIgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7mjET%2FbtsPSUiZia1%2FbuvlgPd3PoM1m02IoRhIgK%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;994&quot; height=&quot;278&quot; data-filename=&quot;스크린샷 2025-08-15 오후 4.25.10.png&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;/figure&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;/li&gt;
&lt;li&gt;순서 자체가 없으므로 인덱스로 객체를 검색해서 가져오는 get(index) 메서드도 존재하지 않습니다.&lt;/li&gt;
&lt;li&gt;중복 저장이 불가능하기에 심지어 null도 하나만 저장할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Set Method&lt;/h4&gt;
&lt;table id=&quot;245ac551-863b-8048-b6e3-e8952e46e14f&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr id=&quot;245ac551-863b-809e-bb5e-d8c959d41451&quot;&gt;
&lt;td id=&quot;AvRK&quot;&gt;메서드&lt;/td&gt;
&lt;td id=&quot;V?c&amp;gt;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;245ac551-863b-80fb-a04b-e04f4e1b2dc9&quot;&gt;
&lt;td id=&quot;AvRK&quot;&gt;boolean add(E e)&lt;/td&gt;
&lt;td id=&quot;V?c&amp;gt;&quot;&gt;주어진 객체를 저장 후 true, false 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;245ac551-863b-80e2-a1eb-df6295cee840&quot;&gt;
&lt;td id=&quot;AvRK&quot;&gt;boolean contains(Object o)&lt;/td&gt;
&lt;td id=&quot;V?c&amp;gt;&quot;&gt;주어진 객체가 저장되었는지의 여부를 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;245ac551-863b-8090-aec6-c9149485f9ce&quot;&gt;
&lt;td id=&quot;AvRK&quot;&gt;Iterator&amp;lt;E&amp;gt; iterator()&lt;/td&gt;
&lt;td id=&quot;V?c&amp;gt;&quot;&gt;저장된 객체를 한번씩 가져오는 반복자를 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;245ac551-863b-806a-ac09-c35ae44efedb&quot;&gt;
&lt;td id=&quot;AvRK&quot;&gt;isEmpty()&lt;/td&gt;
&lt;td id=&quot;V?c&amp;gt;&quot;&gt;컬렉션이 비었는지 여부를 검사&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;245ac551-863b-8096-bebb-d10e0ef5be83&quot;&gt;
&lt;td id=&quot;AvRK&quot;&gt;int Size()&lt;/td&gt;
&lt;td id=&quot;V?c&amp;gt;&quot;&gt;저장되어 있는 전체 객체 수를 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;245ac551-863b-800c-adfb-c5cc5ad3e443&quot;&gt;
&lt;td id=&quot;AvRK&quot;&gt;void clear()&lt;/td&gt;
&lt;td id=&quot;V?c&amp;gt;&quot;&gt;저장된 모든 객체를 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;245ac551-863b-8033-9f1b-f4d680959ca9&quot;&gt;
&lt;td id=&quot;AvRK&quot;&gt;boolean remove(Object o)&lt;/td&gt;
&lt;td id=&quot;V?c&amp;gt;&quot;&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;h4 data-ke-size=&quot;size20&quot;&gt;HashSet&lt;/h4&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;li&gt;대신 순서를 전혀 예측할 수 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;LinkedHashSet&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순서를 가지는 Set 자료구조&lt;/li&gt;
&lt;li&gt;추가된 순서 또는 가장 최근에 접근한 순서대로 접근 가능&lt;/li&gt;
&lt;li&gt;만일 중복을 제거하는 동시에 저장한 순서를 유지하고 싶다면, HashSet 대신 LinkedHashSet을 사용하면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TreeSet&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이진 검색 트리 (binary search tree) 자료구조 형태로 데이터 저장&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;Map (HashMap, LinkedHashMap, TreeMap, HashTable)&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-15 오후 4.24.52.png&quot; data-origin-width=&quot;1614&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3LHoG/btsPSOpzIT5/ChUzRpVYnBkqWkdAlfx5G1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3LHoG/btsPSOpzIT5/ChUzRpVYnBkqWkdAlfx5G1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3LHoG/btsPSOpzIT5/ChUzRpVYnBkqWkdAlfx5G1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3LHoG%2FbtsPSOpzIT5%2FChUzRpVYnBkqWkdAlfx5G1%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;1614&quot; height=&quot;388&quot; data-filename=&quot;스크린샷 2025-08-15 오후 4.24.52.png&quot; data-origin-width=&quot;1614&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;key와 value 쌍으로 연관 지어 이루어진 데이터의 집합&lt;/li&gt;
&lt;li&gt;value는 중복해서 저장될 수 있지만, key는 고유한 값이어야 합니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Map Method&lt;/h4&gt;
&lt;table id=&quot;24bac551-863b-804a-a909-e9cc8bf10c83&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr id=&quot;24bac551-863b-8099-a19c-ca8835865243&quot;&gt;
&lt;td id=&quot;{\?&amp;lt;&quot;&gt;메서드&lt;/td&gt;
&lt;td id=&quot;Z&amp;#96;eh&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24bac551-863b-80fb-af6b-ec6c9a43564c&quot;&gt;
&lt;td id=&quot;{\?&amp;lt;&quot;&gt;void clear()&lt;/td&gt;
&lt;td id=&quot;Z&amp;#96;eh&quot;&gt;Map의 모든 객체를 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24bac551-863b-80c8-a8c7-c1b00e5e868a&quot;&gt;
&lt;td id=&quot;{\?&amp;lt;&quot;&gt;boolean containsKey()&lt;/td&gt;
&lt;td id=&quot;Z&amp;#96;eh&quot;&gt;지정된 key 객체와 일치하는 객체가 있는지 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24bac551-863b-80fa-b9fe-d20f57b801fe&quot;&gt;
&lt;td id=&quot;{\?&amp;lt;&quot;&gt;boolean containsValue()&lt;/td&gt;
&lt;td id=&quot;Z&amp;#96;eh&quot;&gt;지정된 value 객체와 일치하는 객체가 있는지 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24bac551-863b-8027-91cd-e34d0402899b&quot;&gt;
&lt;td id=&quot;{\?&amp;lt;&quot;&gt;Set entySet()&lt;/td&gt;
&lt;td id=&quot;Z&amp;#96;eh&quot;&gt;Map에 저장된 key-value 쌍을 Map.Entry 타입의 객체로 저장한 Set을 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24bac551-863b-8029-bb36-fecb3f53e059&quot;&gt;
&lt;td id=&quot;{\?&amp;lt;&quot;&gt;boolean equals()&lt;/td&gt;
&lt;td id=&quot;Z&amp;#96;eh&quot;&gt;동일한 Map인지를 비교&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24bac551-863b-804d-aeb9-f03ca4b493b2&quot;&gt;
&lt;td id=&quot;{\?&amp;lt;&quot;&gt;Object get()&lt;/td&gt;
&lt;td id=&quot;Z&amp;#96;eh&quot;&gt;지정한 key 객체에 대응하는 value 객체를 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24bac551-863b-8041-b13a-c29d04f609e6&quot;&gt;
&lt;td id=&quot;{\?&amp;lt;&quot;&gt;int hashCode()&lt;/td&gt;
&lt;td id=&quot;Z&amp;#96;eh&quot;&gt;해시 코드를 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24bac551-863b-803a-975f-f52437c1003e&quot;&gt;
&lt;td id=&quot;{\?&amp;lt;&quot;&gt;boolean isEmpty()&lt;/td&gt;
&lt;td id=&quot;Z&amp;#96;eh&quot;&gt;Map이 비어있는지 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24bac551-863b-8031-8583-d99d4f73a5f1&quot;&gt;
&lt;td id=&quot;{\?&amp;lt;&quot;&gt;Set keySet()&lt;/td&gt;
&lt;td id=&quot;Z&amp;#96;eh&quot;&gt;Map에 저장된 모든 key 객체를 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24bac551-863b-8078-915b-c242e4d4aa4e&quot;&gt;
&lt;td id=&quot;{\?&amp;lt;&quot;&gt;Object put()&lt;/td&gt;
&lt;td id=&quot;Z&amp;#96;eh&quot;&gt;Map에 저장된 key 객체와 value 객체를 연결하여 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24bac551-863b-806f-a88c-d4381a3c2aed&quot;&gt;
&lt;td id=&quot;{\?&amp;lt;&quot;&gt;putAll(Map t)&lt;/td&gt;
&lt;td id=&quot;Z&amp;#96;eh&quot;&gt;지정된 key 객체와 일치하는 key-value 쌍을 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24bac551-863b-8024-8060-fad5cb801360&quot;&gt;
&lt;td id=&quot;{\?&amp;lt;&quot;&gt;remove()&lt;/td&gt;
&lt;td id=&quot;Z&amp;#96;eh&quot;&gt;지정된 key 객체와 일치하는 key-value 객체를 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24bac551-863b-800b-bb90-fbbf577c7d69&quot;&gt;
&lt;td id=&quot;{\?&amp;lt;&quot;&gt;int size()&lt;/td&gt;
&lt;td id=&quot;Z&amp;#96;eh&quot;&gt;Map에 저장된 key-value 쌍의 개수를 반환&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;h3 data-ke-size=&quot;size23&quot;&gt;Map.Entry 인터페이스&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Map.Entry 인터페이스는 Map 인터페이스 안에 있는 내부 인터페이스입니다.&lt;/li&gt;
&lt;li&gt;Map에 저장되는 key-value 쌍의 Node 내부 클래스가 이를 구현하고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1755242818841&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;HashMap&amp;lt;String, Integer&amp;gt; map = new HashMap&amp;lt;&amp;gt;();
map.put(&quot;A&quot;, 0);
map.put(&quot;B&quot;, 0);
map.put(&quot;C&quot;, 0);

Set&amp;lt;Map.Entry&amp;lt;String, Integer&amp;gt;&amp;gt; entry = map.entrySet();

System.out.println(entry);

for (Map.Entry&amp;lt;String, Integer&amp;gt; e : entry) {
    System.out.println(e.getKey() + &quot; = &quot; + e.getValue());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HashMap&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HashTable을 보완한 컬렉션&lt;/li&gt;
&lt;li&gt;배열과 연결이 결합된 Hashing 형태로, key, value를 묶어 하나의 데이터로 저장합니다.&lt;/li&gt;
&lt;li&gt;중복을 허용하지 않고 순서를 보장하지 않습니다.&lt;/li&gt;
&lt;li&gt;key, value 값으로 null을 허용합니다.&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;LinkedHashMap (순서 보장)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HashMap을 상속하고, Entry들이 연결 리스트를 구성하여 데이터의 순서를 보장합니다.&lt;/li&gt;
&lt;li&gt;일반적인 Map 자료구조는 순서를 가지지 않지만, LinkedHashMap은 들어온 순서대로 순서를 가집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TreeMap (정렬 O)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이진 검색 트리의 형태로 키와 값의 쌍으로 이루어진 데이터를 저장합니다. (TreeSet과 동일한 원리)&lt;/li&gt;
&lt;li&gt;TreeMap은 SortedMap 인터페이스의 구현체로서, Key값을 기준으로 정렬되는 특징을 가지고 있습니다.&lt;/li&gt;
&lt;li&gt;정렬된 순서로 key/value 값을 저장하므로 조회 성능이 뛰어나지만, 저장과 동시에 정렬 과정이 일어나므로 저장 시간이 오래 걸립니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HashTable&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;Key를 특정 해시 함수를 통해 해싱한 후 나온 결과를 배열의 인덱스로 사용하여 Value를 찾는 방식으로 동작됩니다.&lt;/li&gt;
&lt;li&gt;HashMap보다는 느리지만 동기화가 기본적으로 지원됩니다.&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.elancer.co.kr/blog/detail/255&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.elancer.co.kr/blog/detail/255&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1755236953129&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;java stream이란 특징부터 사용하는 이유까지 모두 알려드립니다. I 이랜서 블로그&quot; data-og-description=&quot;데이터가 중요해 질수록 강력한 데이터 처리 기능을 선보이며 데이터 처리 효율을 높여주는 &amp;lsquo;Java Stream&amp;rsquo;를 찾는 사람들이 많아지고 있는데요. 이랜서에서 java stream이란 무엇인지 자세히 알려&quot; data-og-host=&quot;www.elancer.co.kr&quot; data-og-source-url=&quot;https://www.elancer.co.kr/blog/detail/255&quot; data-og-url=&quot;https://www.elancer.co.kr/blog/detail/255&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fsLys/hyZzIEAEoG/Bl1695KKXBtXxsu7xpW1pK/img.png?width=500&amp;amp;height=281&amp;amp;face=0_0_500_281,https://scrap.kakaocdn.net/dn/cduJkz/hyZzxC3F7f/qEeier9dKbkSfEE8AsV8l0/img.png?width=500&amp;amp;height=281&amp;amp;face=0_0_500_281,https://scrap.kakaocdn.net/dn/mrdYi/hyZydZemdG/kAU2CnvMIn9sL2eP3WxS4K/img.jpg?width=1920&amp;amp;height=1200&amp;amp;face=0_0_1920_1200&quot;&gt;&lt;a href=&quot;https://www.elancer.co.kr/blog/detail/255&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.elancer.co.kr/blog/detail/255&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fsLys/hyZzIEAEoG/Bl1695KKXBtXxsu7xpW1pK/img.png?width=500&amp;amp;height=281&amp;amp;face=0_0_500_281,https://scrap.kakaocdn.net/dn/cduJkz/hyZzxC3F7f/qEeier9dKbkSfEE8AsV8l0/img.png?width=500&amp;amp;height=281&amp;amp;face=0_0_500_281,https://scrap.kakaocdn.net/dn/mrdYi/hyZydZemdG/kAU2CnvMIn9sL2eP3WxS4K/img.jpg?width=1920&amp;amp;height=1200&amp;amp;face=0_0_1920_1200');&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;java stream이란 특징부터 사용하는 이유까지 모두 알려드립니다. I 이랜서 블로그&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;데이터가 중요해 질수록 강력한 데이터 처리 기능을 선보이며 데이터 처리 효율을 높여주는 &amp;lsquo;Java Stream&amp;rsquo;를 찾는 사람들이 많아지고 있는데요. 이랜서에서 java stream이란 무엇인지 자세히 알려&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.elancer.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jeong-pro.tistory.com/212&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jeong-pro.tistory.com/212&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1755236984993&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;개발자 기술 과제, 라이브 코딩 테스트 후기(자바 스트림 활용 능력 with flatMap)&quot; data-og-description=&quot;과제 겸 라이브 코딩 1. 후기 제가 생각하는 일반적인 개발자 채용 프로세스는 아래와 같습니다. 서류전형 &amp;rarr; 코딩테스트(온라인) &amp;rarr; 기술면접 &amp;rarr; 임원면접 &amp;rarr; 최종합격 여기에 조금 추가되면 코&quot; data-og-host=&quot;jeong-pro.tistory.com&quot; data-og-source-url=&quot;https://jeong-pro.tistory.com/212&quot; data-og-url=&quot;https://jeong-pro.tistory.com/212&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eL889/hyZygBEjRn/VAXTctCyFEUhxctrLSJ7vk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/hxSvE/hyZysB5LXm/09GdzNcNSVjyQmhEyfFDlk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/JkS3c/hyZylJJOso/0cEEO0oCmLTTg4Cb1Z6KUk/img.jpg?width=264&amp;amp;height=200&amp;amp;face=0_0_264_200&quot;&gt;&lt;a href=&quot;https://jeong-pro.tistory.com/212&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jeong-pro.tistory.com/212&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eL889/hyZygBEjRn/VAXTctCyFEUhxctrLSJ7vk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/hxSvE/hyZysB5LXm/09GdzNcNSVjyQmhEyfFDlk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/JkS3c/hyZylJJOso/0cEEO0oCmLTTg4Cb1Z6KUk/img.jpg?width=264&amp;amp;height=200&amp;amp;face=0_0_264_200');&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;개발자 기술 과제, 라이브 코딩 테스트 후기(자바 스트림 활용 능력 with flatMap)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;과제 겸 라이브 코딩 1. 후기 제가 생각하는 일반적인 개발자 채용 프로세스는 아래와 같습니다. 서류전형 &amp;rarr; 코딩테스트(온라인) &amp;rarr; 기술면접 &amp;rarr; 임원면접 &amp;rarr; 최종합격 여기에 조금 추가되면 코&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jeong-pro.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JCF-%F0%9F%A7%B1-Collections-Framework-%EC%A2%85%EB%A5%98-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://inpa.tistory.com/entry/JCF-%F0%9F%A7%B1-Collections-Framework-%EC%A2%85%EB%A5%98-%EC%B4%9D%EC%A0%95%EB%A6%AC&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1755239928697&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;  Java Collections Framework 종류   총정리&quot; data-og-description=&quot;Java Collection Framework 자바 새내기분들은 컬렉션 프레임워크라는 단어에 뭔가 거창하고 어려운 느낌이 들수 있겠지만, 그냥 자료 구조(Data Structure) 종류의 형태들을 자바 클래스로 구현한 모음집 &quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/JCF-%F0%9F%A7%B1-Collections-Framework-%EC%A2%85%EB%A5%98-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot; data-og-url=&quot;https://inpa.tistory.com/entry/JCF-%F0%9F%A7%B1-Collections-Framework-%EC%A2%85%EB%A5%98-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Faxet/hyZynAMEOL/q763zNgEgOzumvXkNk7Fp0/img.jpg?width=800&amp;amp;height=471&amp;amp;face=0_0_800_471,https://scrap.kakaocdn.net/dn/nrB7J/hyZzILlmnD/cf1BS2A7JZksxhnJ2vgdg1/img.jpg?width=800&amp;amp;height=471&amp;amp;face=0_0_800_471,https://scrap.kakaocdn.net/dn/uotIx/hyZzxQA0q5/ATaRjdwiKXkCykX6RZ0wa1/img.jpg?width=1440&amp;amp;height=896&amp;amp;face=0_0_1440_896&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JCF-%F0%9F%A7%B1-Collections-Framework-%EC%A2%85%EB%A5%98-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/JCF-%F0%9F%A7%B1-Collections-Framework-%EC%A2%85%EB%A5%98-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Faxet/hyZynAMEOL/q763zNgEgOzumvXkNk7Fp0/img.jpg?width=800&amp;amp;height=471&amp;amp;face=0_0_800_471,https://scrap.kakaocdn.net/dn/nrB7J/hyZzILlmnD/cf1BS2A7JZksxhnJ2vgdg1/img.jpg?width=800&amp;amp;height=471&amp;amp;face=0_0_800_471,https://scrap.kakaocdn.net/dn/uotIx/hyZzxQA0q5/ATaRjdwiKXkCykX6RZ0wa1/img.jpg?width=1440&amp;amp;height=896&amp;amp;face=0_0_1440_896');&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;  Java Collections Framework 종류   총정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Java Collection Framework 자바 새내기분들은 컬렉션 프레임워크라는 단어에 뭔가 거창하고 어려운 느낌이 들수 있겠지만, 그냥 자료 구조(Data Structure) 종류의 형태들을 자바 클래스로 구현한 모음집&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gangnam-americano.tistory.com/41&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://gangnam-americano.tistory.com/41&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1755241592975&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[JAVA] Java 컬렉션(Collection) 정리&quot; data-og-description=&quot;[JAVA] Java 컬렉션(Collection) 정리 ■ Java Collections Framework(JCF) Java에서 컬렉션(Collection)이란 데이터의 집합, 그룹을 의미하며 JCF(Java Collections Framework)는 이러한 데이터, 자료구조인 컬렌션과 이를 구&quot; data-og-host=&quot;gangnam-americano.tistory.com&quot; data-og-source-url=&quot;https://gangnam-americano.tistory.com/41&quot; data-og-url=&quot;https://gangnam-americano.tistory.com/41&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bCyg2f/hyZysPElGh/WOBBLGtoj2LgsPsb5N1XnK/img.png?width=544&amp;amp;height=294&amp;amp;face=0_0_544_294,https://scrap.kakaocdn.net/dn/bw1yKR/hyZykRBsR6/E4uMKFECsJS8rBTaSZGgvk/img.png?width=544&amp;amp;height=294&amp;amp;face=0_0_544_294,https://scrap.kakaocdn.net/dn/czPTer/hyZylwb9mR/aUc3FD1U4ByJiXgQW9JaYk/img.jpg?width=720&amp;amp;height=960&amp;amp;face=0_0_720_960&quot;&gt;&lt;a href=&quot;https://gangnam-americano.tistory.com/41&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gangnam-americano.tistory.com/41&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bCyg2f/hyZysPElGh/WOBBLGtoj2LgsPsb5N1XnK/img.png?width=544&amp;amp;height=294&amp;amp;face=0_0_544_294,https://scrap.kakaocdn.net/dn/bw1yKR/hyZykRBsR6/E4uMKFECsJS8rBTaSZGgvk/img.png?width=544&amp;amp;height=294&amp;amp;face=0_0_544_294,https://scrap.kakaocdn.net/dn/czPTer/hyZylwb9mR/aUc3FD1U4ByJiXgQW9JaYk/img.jpg?width=720&amp;amp;height=960&amp;amp;face=0_0_720_960');&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;[JAVA] Java 컬렉션(Collection) 정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[JAVA] Java 컬렉션(Collection) 정리 ■ Java Collections Framework(JCF) Java에서 컬렉션(Collection)이란 데이터의 집합, 그룹을 의미하며 JCF(Java Collections Framework)는 이러한 데이터, 자료구조인 컬렌션과 이를 구&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gangnam-americano.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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>Backend</category>
      <category>HashMap</category>
      <category>HashSet</category>
      <category>Hashtable</category>
      <category>java collection</category>
      <category>java stream</category>
      <category>LinkedHashMap</category>
      <category>LinkedHashSet</category>
      <category>stream</category>
      <category>treemap</category>
      <category>treeset</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/419</guid>
      <comments>https://daily1313.tistory.com/entry/Java-Stream-Collection#entry419comment</comments>
      <pubDate>Fri, 15 Aug 2025 16:33:45 +0900</pubDate>
    </item>
    <item>
      <title>[CS] JVM Memory Structure</title>
      <link>https://daily1313.tistory.com/entry/CS-JVM-Memory-Structure</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java는 Garbage Collector(GC)를 통해 객체의 메모리를 자동으로 관리하며, 필요시 공간을 할당하고 사용하지 않는 객체를 제거합니다. 하지만 모든 객체가 GC에 의해 즉시 수거되는 것은 아니므로, 메모리 관리 원리를 이해하는 것은 고성능 시스템 구현에 큰 도움이 됩니다. 또한 이를 잘 이해하면 문제 발생 시 원인 분석을 훨씬 수월하게 할 수 있습니다.&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;GC의 내용보다는 이번 포스팅에서의 목적은 JVM Memory의 구조를 이해하는 것입니다. 그래서 GC의 내용은 생략되고, Java Memory 구조에 대한 설명이 주가 되는 점을 참고 부탁드립니다.&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;JVM의 Memory는 총 5개의 Area로 구분되어집니다. (Method(Static), Heap, Stack, Native Method Stack, PC Registers)&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;JVM이 실행되고 나면, Java Compiler가 Java 파일을 Class 파일로 변환합니다. 이후, 생성된 .class 파일을 JVM의 ClassLoader에 게 보냅니다. .class파일을 전달받은 ClassLoader는 JVM 런타임 영역으로 로딩하여 메모리에 파일을 올리게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JVM이 무엇인지와 Memory 구조에 대해 상세하게 살펴보도록 하겠습니다.&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;JVM&amp;nbsp;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Java로 개발한 프로그램을 컴파일하여 만들어지는(Java Compiler에 의해) 바이트코드를 실행하기 위한 가상 머신&lt;/li&gt;
&lt;li&gt;Java 프로그램을 실행하게 되면, OS로부터 메모리를 할당받게 됩니다.&lt;/li&gt;
&lt;li&gt;OS에 종속되지 않고, 실행할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JVM Memory Structure&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-15 오전 9.34.00.png&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LVOD4/btsPUeHtJD4/kYQ05EouQOgH8wzXgypWm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LVOD4/btsPUeHtJD4/kYQ05EouQOgH8wzXgypWm0/img.png&quot; data-alt=&quot;https://www.scaler.com/topics/memory-management-in-java/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LVOD4/btsPUeHtJD4/kYQ05EouQOgH8wzXgypWm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLVOD4%2FbtsPUeHtJD4%2FkYQ05EouQOgH8wzXgypWm0%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;699&quot; height=&quot;231&quot; data-filename=&quot;스크린샷 2025-08-15 오전 9.34.00.png&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.scaler.com/topics/memory-management-in-java/&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;&lt;b&gt;Runtime Data Area&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;Runtime Data Area&lt;/b&gt;는 JVM이 OS로부터 할당받는 메모리 영역으로, Java 프로그램 실행에 필요한 .class 파일의 바이트코드, 관련 데이터, 그리고 명령어를 저장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Static Area (Method Area)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Heap Area&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Stack Area&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Native Method Stack: 자바 외 언어로 작성된 코드를 위한 영역&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PC Register: JVM의 명령어의 주소를 가지는 영역&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Static (Method) Area: 프로그램을 실행하는데 필요한 공통 데이터 관리, 모든 영역에서 공유&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;저장되는 데이터: Class 정보, Static 변수, 생성자, 메서드 코드,&lt;span&gt;&amp;nbsp;&lt;/span&gt;런타임 상수 풀 등&lt;/li&gt;
&lt;li&gt;JVM이 실행될 때 Class가 load되어 생성되는 클래스의 실행 코드(바이트 코드)를 저장합니다.&lt;/li&gt;
&lt;li&gt;JVM 시작 시, 클래스 로딩과 함게 생성되고 모든 스레드에서 공유합니다.&lt;/li&gt;
&lt;li&gt;JVM 종료 시, 메모리에서 해제 됩니다. 즉, 프로그램이 종료되기 전까지 메모리 상에서 존재하게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stack Area: 실제 프로그램이 실행되는 영역이자 메서드를 실행할 때마다 하나씩 쌓이는 곳&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;저장되는 데이터: 기본 자료형 (int, double, boolean, byte..), 지역변수, 매개변수, Heap 객체 참조 값&lt;/li&gt;
&lt;li&gt;Stack 구조 (LIFO)&lt;/li&gt;
&lt;li&gt;메소드가 호출될 때, 메모리에서 할당되고 메서드 종료 시 메모리에서 삭제됩니다.&lt;/li&gt;
&lt;li&gt;각 Thread마다 자신만의 Stack을 갖고 있습니다. Thread는 내부적으로 Static, Heap, Stack 영역을 가집니다. Thread는 다른 Thread에 접근할 수 없지만, static, heap 영역을 공유하여 사용이 가능합니다.&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Heap Area: 객체(인스턴스)가 저장되는 영역&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;저장되는 데이터: 참조형 데이터(String, Array, Enum, Class, Interface),&amp;nbsp; new 키워드로 생성된 인스턴스&lt;/li&gt;
&lt;li&gt;쓰레드가 몇 개든 단 하나의 영역에서만 존재합니다.&lt;/li&gt;
&lt;li&gt;GC에 의해서 메모리에서 해제되므로, Stack 영역과는 다르게 호출이 종료돼도 삭제되지 않습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Native Method Stack&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;PC Register&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JVM의 명령어의 주소를 가지는 영역&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Method 영역과 Heap 영역간의 관계&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-15 오전 10.29.12.png&quot; data-origin-width=&quot;1106&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pVfPA/btsPUPgkuvL/YYWqYx2KBVjCSjqrjm2rBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pVfPA/btsPUPgkuvL/YYWqYx2KBVjCSjqrjm2rBk/img.png&quot; data-alt=&quot;인프런 김영한님 자료&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pVfPA/btsPUPgkuvL/YYWqYx2KBVjCSjqrjm2rBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpVfPA%2FbtsPUPgkuvL%2FYYWqYx2KBVjCSjqrjm2rBk%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;716&quot; height=&quot;269&quot; data-filename=&quot;스크린샷 2025-08-15 오전 10.29.12.png&quot; data-origin-width=&quot;1106&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인프런 김영한님 자료&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Data라는 인스턴스 생성 시, 힙 메모리에 각각의 인스턴스가 생깁니다.&lt;/li&gt;
&lt;li&gt;각각의 인스턴스는 내부적으로 변수, 메서드를 가집니다.&lt;/li&gt;
&lt;li&gt;같은 클래스로부터 생성된 객체라도, 인스턴스 내부 변수 값은 서로 다를 수 있지만 (위의 value=10, value=20 처럼), 메서드에 대한 새로운 메모리 할당은 없습니다.
&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;/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;지금까지 Java 메모리 구조와 각 영역의 역할, 그리고 Method 영역과 Heap 영역의 관계를 알아보았습니다.&lt;br /&gt;마지막으로, 전체 흐름을 정리하는 의미에서 Java 변수의 종류와 생명주기를 살펴본 뒤 포스팅을 마무리하겠습니다.&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;Java 변수 종류&lt;/h2&gt;
&lt;pre id=&quot;code_1755221689309&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Variable { 
    public static int a = 20; // 클래스 변수(전역 변수)
 
    int b = 60; // 인스턴스 변수(전역 변수)
    
    public static void main(String[] args) { // 매개변수(파라미터)
        int c = 50; // 지역변수
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;table id=&quot;24eac551-863b-8033-beb4-cd75b6e6a1d4&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr id=&quot;24eac551-863b-803d-aa54-cc03b465eda7&quot;&gt;
&lt;td id=&quot;KlZU&quot; style=&quot;width: 13.0233%;&quot;&gt;변수 종류&lt;/td&gt;
&lt;td id=&quot;HjSa&quot; style=&quot;width: 11.3954%;&quot;&gt;선언 위치&lt;/td&gt;
&lt;td id=&quot;TtU|&quot; style=&quot;width: 28.6046%;&quot;&gt;설명&lt;/td&gt;
&lt;td id=&quot;sACn&quot; style=&quot;width: 24.0698%;&quot;&gt;생성 시기&lt;/td&gt;
&lt;td id=&quot;:VaU&quot; style=&quot;width: 14.1861%;&quot;&gt;소멸 시기&lt;/td&gt;
&lt;td id=&quot;d{Vl&quot; style=&quot;width: 8.60465%;&quot;&gt;저장 메모리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24eac551-863b-8071-854d-e28a58561a56&quot;&gt;
&lt;td id=&quot;KlZU&quot; style=&quot;width: 13.0233%;&quot;&gt;클래스 변수&lt;/td&gt;
&lt;td id=&quot;HjSa&quot; style=&quot;width: 11.3954%;&quot;&gt;클래스 영역&lt;/td&gt;
&lt;td id=&quot;TtU|&quot; style=&quot;width: 28.6046%;&quot;&gt;static이 붙고 여러 객체에서 공통 사용&lt;/td&gt;
&lt;td id=&quot;sACn&quot; style=&quot;width: 24.0698%;&quot;&gt;클래스가 메모리에 올라갈 때&lt;/td&gt;
&lt;td id=&quot;:VaU&quot; style=&quot;width: 14.1861%;&quot;&gt;프로그램 종료시&lt;/td&gt;
&lt;td id=&quot;d{Vl&quot; style=&quot;width: 8.60465%;&quot;&gt;static&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24eac551-863b-8060-a688-e78eeb623f1a&quot;&gt;
&lt;td id=&quot;KlZU&quot; style=&quot;width: 13.0233%;&quot;&gt;인스턴스 변수&lt;/td&gt;
&lt;td id=&quot;HjSa&quot; style=&quot;width: 11.3954%;&quot;&gt;클래스 영역&lt;/td&gt;
&lt;td id=&quot;TtU|&quot; style=&quot;width: 28.6046%;&quot;&gt;클래스 영역에서 static이 아닌 경우&lt;/td&gt;
&lt;td id=&quot;sACn&quot; style=&quot;width: 24.0698%;&quot;&gt;인스턴스 생성시&lt;/td&gt;
&lt;td id=&quot;:VaU&quot; style=&quot;width: 14.1861%;&quot;&gt;인스턴스 소멸시&lt;/td&gt;
&lt;td id=&quot;d{Vl&quot; style=&quot;width: 8.60465%;&quot;&gt;heap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;24eac551-863b-8034-a500-f6807ed48a33&quot;&gt;
&lt;td id=&quot;KlZU&quot; style=&quot;width: 13.0233%;&quot;&gt;지역 변수&lt;/td&gt;
&lt;td id=&quot;HjSa&quot; style=&quot;width: 11.3954%;&quot;&gt;메서드 영역&lt;/td&gt;
&lt;td id=&quot;TtU|&quot; style=&quot;width: 28.6046%;&quot;&gt;메서드 내부에서 선언&lt;/td&gt;
&lt;td id=&quot;sACn&quot; style=&quot;width: 24.0698%;&quot;&gt;블록 내의 변수의 선언문 실행시&lt;/td&gt;
&lt;td id=&quot;:VaU&quot; style=&quot;width: 14.1861%;&quot;&gt;블록을 벗어날 때&lt;/td&gt;
&lt;td id=&quot;d{Vl&quot; style=&quot;width: 8.60465%;&quot;&gt;stack&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;큰 틀에서 보면, static 키워드의 유무로 구분할 수 있습니다.&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;클래스 변수: static이 붙은 멤버 변수&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 변수, 정적 변수, static 변수 등으로 부른다.&lt;/li&gt;
&lt;li&gt;static이 붙은 멤버 변수는 인스턴스와 무관하게 클래스에 바로 접근해서 사용이 가능합니다.&lt;/li&gt;
&lt;li&gt;클래스 변수는 자바 프로그램을 시작할 때 딱 1개가 만들어집니다.&lt;/li&gt;
&lt;li&gt;static 영역이 관여합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인스턴스 변수: static이 붙지 않은 멤버 변수&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;static이 붙지 않은 멤버 변수는 인스턴스를 생성해야 사용할 수 있고, 인스턴스에 소속되어 있습니다. 따라서 인스턴스 변수라고 합니다.&lt;/li&gt;
&lt;li&gt;인스턴스 변수는 인스턴스를 만들 때 마다 새로 만들어집니다.&lt;/li&gt;
&lt;li&gt;heap 영역이 관여합니다.&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;블록 실행 중에만 존재하고, 호출이 끝나면 Stack 메모리에서 제거됩니다.&lt;/li&gt;
&lt;li&gt;stack 영역이 관여합니다.&lt;/li&gt;
&lt;/ul&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;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 변수: 클래스 변수는 메서드 영역의 static 영역에 보관되는 변수입니다. 메서드 영역은 프로그램 전체에서 사용하는 공용 공간. 클래스 변수는 해당 클래스가 JVM이 로딩되는 순간 생성되고, JVM이 종료될 때까지 생명주기가 이어집니다.&lt;/li&gt;
&lt;li&gt;인스턴스 변수: 인스턴스에 있는 멤버 변수를 인스턴스 변수라고 합니다. 인스턴스 변수는 힙 영역에서 사용됩니다. 힙 영역은 GC가 발생하기 전까지 생존하기에 생존 주기가 짧습니다.&lt;/li&gt;
&lt;li&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.scaler.com/topics/memory-management-in-java/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.scaler.com/topics/memory-management-in-java/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1755218066111&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;What is Memory Management in Java? - Scaler Topics&quot; data-og-description=&quot;Memory management in Java refers to the process of allocating and freeing up space for objects. Java automatically manages memory.&quot; data-og-host=&quot;www.scaler.com&quot; data-og-source-url=&quot;https://www.scaler.com/topics/memory-management-in-java/&quot; data-og-url=&quot;https://www.scaler.com/topics/memory-management-in-java/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/t5Qo1/hyZyecI6pt/bqCX04LIw3Fhwb9JGFVkk0/img.jpg?width=6000&amp;amp;height=1234&amp;amp;face=0_0_6000_1234,https://scrap.kakaocdn.net/dn/vKGDS/hyZyqRK7ZM/hH2lk2wz9aDgXHfthihNW0/img.jpg?width=6000&amp;amp;height=1234&amp;amp;face=0_0_6000_1234&quot;&gt;&lt;a href=&quot;https://www.scaler.com/topics/memory-management-in-java/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.scaler.com/topics/memory-management-in-java/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/t5Qo1/hyZyecI6pt/bqCX04LIw3Fhwb9JGFVkk0/img.jpg?width=6000&amp;amp;height=1234&amp;amp;face=0_0_6000_1234,https://scrap.kakaocdn.net/dn/vKGDS/hyZyqRK7ZM/hH2lk2wz9aDgXHfthihNW0/img.jpg?width=6000&amp;amp;height=1234&amp;amp;face=0_0_6000_1234');&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;What is Memory Management in Java? - Scaler Topics&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Memory management in Java refers to the process of allocating and freeing up space for objects. Java automatically manages memory.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.scaler.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS</category>
      <category>heap</category>
      <category>java memory</category>
      <category>JVM Memory</category>
      <category>JVM Memory Structure</category>
      <category>JVM 메모리 구조</category>
      <category>stack</category>
      <category>static</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/418</guid>
      <comments>https://daily1313.tistory.com/entry/CS-JVM-Memory-Structure#entry418comment</comments>
      <pubDate>Fri, 15 Aug 2025 10:45:13 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java] 8장. 메소드</title>
      <link>https://daily1313.tistory.com/entry/Effective-Java-8%EC%9E%A5-%EB%A9%94%EC%86%8C%EB%93%9C</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;218&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OlOtB/btsPQyshzWX/Y8kxcFcQCycZcKV2ZwE3L1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OlOtB/btsPQyshzWX/Y8kxcFcQCycZcKV2ZwE3L1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OlOtB/btsPQyshzWX/Y8kxcFcQCycZcKV2ZwE3L1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOlOtB%2FbtsPQyshzWX%2FY8kxcFcQCycZcKV2ZwE3L1%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;218&quot; height=&quot;336&quot; data-origin-width=&quot;218&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-49] 매개변수가 유효한지 검사하라&lt;/b&gt;&lt;/span&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;매개변수의 유효성 검사는 메서드 몸체가 시작되기 전에 해야 하며 매개변수에 대한 제약사항은 문서화가 필요합니다.&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;방법&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;@Nullable: 표준 X&lt;/li&gt;
&lt;li&gt;requireNonNull (Java 7), checkFromIndexSize, checkFromToIndex, checkIndex (array 전용, Java 9)&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;Example&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;BigInteger mod method&lt;/h4&gt;
&lt;pre id=&quot;code_1754989300128&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public BigInteger mod(BigInteger m) {
    if (m.signum &amp;lt;= 0)
        throw new ArithmeticException(&quot;BigInteger: modulus not positive&quot;);

    BigInteger result = this.remainder(m);
    return (result.signum &amp;gt;= 0 ? result : result.add(m));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Objects.requireNonNull&lt;/h4&gt;
&lt;pre id=&quot;code_1754989317817&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ForceInline
public static &amp;lt;T&amp;gt; T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}&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;유효성 검사가 필요없는 경우&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Collections.sort(List): 리스트를 정렬할 때는 정렬 과정에서 모든 객체가 상호 비교됩니다.&lt;/li&gt;
&lt;li&gt;만일 비교할 수 없는 타입의 객체가 있으면 ClassCastException이 발생합니다.&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-50] 적시에 방어적 복사본을 만들라&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java는 안전한 언어이지만, 클라이언트가 언제든지 불변식을 깨뜨릴 수 있다고 가정하고 방어적인 프로그래밍을 하는 것이 좋습니다.&lt;/li&gt;
&lt;li&gt;클래스가 클라이언트로부터 받거나 클라이언트에게 반환하는 구성 요소가 가변적이라면 그 요소는 반드시 방어적으로 복사해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불변식을 지키지 못한 클래스 : Date&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대부분의 메서드는 Deprecated 되었으므로 사용하면 안 됩니다.&lt;/li&gt;
&lt;li&gt;Java 8에서 제공하는 LocalDateTime과 같은 클래스를 사용하는 것을 권장합니다.&lt;/li&gt;
&lt;li&gt;불가피한 상황에서 사용할 경우 생성자에서 매개변수를 방어적으로 복사합니다.&lt;/li&gt;
&lt;li&gt;인스턴스 내부를 보호하기 위해서는 생성자에게 받은 매개변수를 각각 방어적으로 복사(defensive copy) 해야 합니다.
&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Example&lt;/h4&gt;
&lt;pre id=&quot;code_1754989380359&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public Period(Date start, Date end) {
    this.start = new Date(start.getTime());
    this.end = new Date(end.getTime());

    if(start.compareTo(end) &amp;gt; 0) {
        throw new IllegalArgumentException(start + &quot; after &quot; + end);
    }
}

public Date start() {
    return new Date(start.getTime());
}

public Date end() {
    return new Date(end.getTime());
}&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-51] 메서드 시그니처를 신중히 설계하라&lt;/b&gt;&lt;/span&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;표준 명명 규칙에 따라 지으며 긴 이름은 지양하는 것이 좋습니다.&lt;/li&gt;
&lt;li&gt;편의 메서드를 많이 만드는 것은 좋지 않습니다.&lt;/li&gt;
&lt;li&gt;너무 많은 메서드는 그에 따른 문서화, 유지보수, 테스트를 요구합니다. 메서드의 매개변수 목록도 4개 이하가 적절하며, 같은 타입의 매개변수 여러 개가 연달아 나오는 것도 좋지 않습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해결책: List interface&amp;rsquo;s subList, indexOf의 조합, Helper (여러 개의 매개변수를 묶어주는 역할을 하는 클래스), 빌더 패턴 등 ..&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-52] 다중정의는 신중히 사용하라&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;재정의 (overriding)를 한 메서드는 실행 중에 동적으로 선택되지만, 다중 정의 (overloading, 오버로딩)된 메서드의 호출 여부는 컴파일 타임에 정해집니다. 그리고 이러한 다중 정의 메서드는 개발자가 기대한 것처럼 동작하지 않을 수 있습니다.&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;h4 data-ke-size=&quot;size20&quot;&gt;Example&lt;/h4&gt;
&lt;pre id=&quot;code_1754989446191&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ColectionClassifier {
    public static String classify(Set&amp;lt;?&amp;gt; set) {
        return &quot;set&quot;;
    }

    public static String classify(List&amp;lt;?&amp;gt; list) {
        return &quot;list&quot;;
    }

    public static String classify(Collection&amp;lt;?&amp;gt; collection) {
        return &quot;collection&quot;
    }

    public static void main(String[] args) {
        Collection&amp;lt;?&amp;gt;[] collections = {
            new HashSet&amp;lt;String&amp;gt;(),
            new ArrayList&amp;lt;Integer&amp;gt;(),
            new HashMap&amp;lt;String, String&amp;gt;().values()
        };

        for (Collection&amp;lt;?&amp;gt; c : collections) {
            System.out.println(classfy(c));
        }
    }
}&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-53] 가변인수는 신중히 사용하라&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가변인수: 함수에 들어가는 인수(argument)의 개수가 동적으로 변하는 것&lt;/li&gt;
&lt;li&gt;가변인수 메서드를 호출하면 인수의 개수와 길이가 같은 배열을 만들고 인수들을 만들어진 배열에 저장한 후에 가변인수 메서드에 전달해 줍니다. (성능이 안 좋은 이유)&lt;/li&gt;
&lt;li&gt;인수가 1개 이상이어야 할 때는 가변인수 앞에 필수 매개변수를 받도록 해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Example&lt;/h4&gt;
&lt;pre id=&quot;code_1754989488728&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static int min(int firstArg, int... remainingArgs) {
    int min = firstArg;
    for (int arg : remainingArgs) {
        if (arg &amp;lt; min) {
            min = arg;
        }
    }
    return min;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;가변인수 메서드가 호출될 때마다 배열을 새로 할당하고 초기화하기 때문입니다.&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;/p&gt;
&lt;pre id=&quot;code_1754989508791&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void foo() {}
public void foo(int arg1) {}
public void foo(int arg1, arg2) {}
public void foo(int arg1, arg2, arg3) {}
public void foo(int arg1, arg2, arg3, int... restArg) {}&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-54] null이 아닌, 빈 컬렉션이나 배열을 반환하라&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컬렉션이 빈 경우에 null을 사용하는 경우가 비일비재합니다.&lt;/li&gt;
&lt;li&gt;이렇게 되는 경우, 코드를 사용하는 클라이언트에서는 null을 방어하는 코드를 반드시 추가해주어야 합니다.&lt;/li&gt;
&lt;li&gt;null 반환과 빈 컨테이너 반환의 성능 차이는 작기 때문에, null을 반환하지 않아도 됩니다.&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;(ex) Collections.emptyList(), Collections.emptySet(), Collections.emptyMap() (싱글톤 객체))&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;사용 패턴에 따라 빈 컬렉션 할당이 성능을 저하시키는 경우가 있을 때, 이러한 경우에는 매번 같은 불변 컬렉션을 반환하면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Example&lt;/h4&gt;
&lt;pre id=&quot;code_1754989551688&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private final List&amp;lt;Object&amp;gt; list= ...;

// 지양
public List&amp;lt;Object&amp;gt; getList() {
    return list.isEmpty() ? null : new ArrayList&amp;lt;&amp;gt;(list);
}

// 다음과 같이 개선
public List&amp;lt;Object&amp;gt; getList() {
    return new ArrayList&amp;lt;&amp;gt;(list);
}&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-55] 옵셔널 반환은 신중히 하라&lt;/b&gt;&lt;/span&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AS-IS: Java 8 이전에는 예외를 던지거나 null 반환&lt;/li&gt;
&lt;li&gt;TO-BE: Java 8 이후에는 Optional&amp;lt;T&amp;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;h4 data-ke-size=&quot;size20&quot;&gt;AS-IS vs TO-BE&lt;/h4&gt;
&lt;pre id=&quot;code_1754989578976&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 옵셔널을 사용하지 않았을 때
public static &amp;lt;E extends Comparable&amp;lt;E&amp;gt;&amp;gt; E max(Collection&amp;lt;E&amp;gt; c) {
    if (c.isEmpty()) {
        throw new IllegalArgumentException(&quot;빈 컬렉션&quot;);
    }        

    E result = null;
    for (E e : c) {
        if (result == null || e.compareTo(result) &amp;gt; 0)
            result = Objects.requireNonNull(e);
    }
    return result;
}

// 옵셔널 + 스트림을 사용할 때
public static &amp;lt;E extends Comparable&amp;lt;E&amp;gt;&amp;gt;
        Optional&amp;lt;E&amp;gt; max(Collection&amp;lt;E&amp;gt; c) {
    return c.stream().max(Comparator.naturalOrder());
}&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;Optional 활용&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;기본값 설정: 옵셔널을 반환하는 메서드로부터 원하는 값을 받지 못했을 때, 기본 값을 설정할 수 있습니다. (orElse)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;String lastWordInLexicon = max(words).orElse(&amp;rdquo;단어 없음&amp;hellip;&amp;rdquo;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;원하는 예외를 던집니다: (orElseThrow)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;To myToy = max(toys).orElseThrow(TemperTantrunException::new)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;항상 값이 채워짐을 가정: Optional에 항상 값이 있음을 확신할 경우 사용해야 합니다. 값이 없다면 NoSuchElementException이 발생합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Element lastNobleGas = max(Elements.NOBLE_GASES).get();&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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;orElseGet: Supplier&amp;lt;T&amp;gt;를 사용하여 생성하므로 초기 설정 비용을 낮출 수 있습니다.&lt;/li&gt;
&lt;/ul&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;Collection, Stream, Array 등과 같은 컨테이너를 Optional로 감싸면 안 됩니다. 빈 컨테이너를 그대로 반환하면 클라이언트에서는 Optional 처리 코드를 만들지 않아도 됩니다.&lt;/li&gt;
&lt;/ol&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;성능에 민감한 메서드라면, null을 반환하거나 예외를 던지는 것이 낫습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-56] 공개된 API 요소에는 항상 문서화 주석을 작성하라&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&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;li&gt;자바에서 제공하는 자바독(javadoc)은 소스코드 파일에서 문서화 주석 (document comment)를 취합하여 API 문서로 만들어줍니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Backend/Effective Java</category>
      <category>Effective Java</category>
      <category>이펙티브 자바</category>
      <category>이펙티브 자바 8장 메소드</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/417</guid>
      <comments>https://daily1313.tistory.com/entry/Effective-Java-8%EC%9E%A5-%EB%A9%94%EC%86%8C%EB%93%9C#entry417comment</comments>
      <pubDate>Tue, 12 Aug 2025 23:42:37 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java] 7장. 람다와 스트림</title>
      <link>https://daily1313.tistory.com/entry/Effective-Java-7%EC%9E%A5-%EB%9E%8C%EB%8B%A4%EC%99%80-%EC%8A%A4%ED%8A%B8%EB%A6%BC</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qGZlN/btsOpA6Iaom/79jiWgc3ekr4pLlHyV5K2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qGZlN/btsOpA6Iaom/79jiWgc3ekr4pLlHyV5K2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qGZlN/btsOpA6Iaom/79jiWgc3ekr4pLlHyV5K2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqGZlN%2FbtsOpA6Iaom%2F79jiWgc3ekr4pLlHyV5K2k%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;271&quot; height=&quot;342&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;342&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-42] 익명 클래스보다는 람다를 사용하라&lt;/b&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;/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;p data-ke-size=&quot;size16&quot;&gt;익명 클래스 (Anonymous Class)&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;p data-ke-size=&quot;size18&quot;&gt;[구문]&lt;/p&gt;
&lt;pre id=&quot;code_1749028094346&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;인터페이스명 변수명 = new 인터페이스명() {
    // 인터페이스 메서드 구현 (메서드 오버라이드)
    // 추가적인 멤버 변수 또는 메서드도 선언 가능
};&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;JDK 1.1부터 함수 객체를 만들때 주로 사용합니다.&lt;/li&gt;
&lt;li&gt;코드가 너무 길기에 함수형 프로그래밍(Functional Programming)에 적합하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;익명 클래스 사용 Example&lt;/h3&gt;
&lt;pre id=&quot;code_1749028157034&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Main {
    public static void main(String[] args) {
        List&amp;lt;String&amp;gt; words = Arrays.asList(&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;);

        Collections.sort(words, new Comparator&amp;lt;String&amp;gt;() {
            public int compare(String s1, String s2) {
                return Integer.compare(s1.length(), s2.length());
            }
        });
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;람다(lambda)&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;JDK 1.8 버전부터는 추상 메서드 하나짜리 인터페이스, 즉 함수형 인터페이스를 말하는데 그 인터페이스의 인스턴스를 람다를 사용합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(매개변수) -&amp;gt; { 실행문 }의 형태&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;위의 익명 클래스를 람다를 활용하여 구현하면 더욱 간결하게 표현이 가능해집니다.&lt;/li&gt;
&lt;li&gt;메서드를 간결한 함수 식으로 표현한 것이며, 메서드 타입, 메서드 이름, 매개변수 타입, 중괄호, return문을 생략하고 화살표 기호를 넣음으로써 코드를 함축할 수 있습니다. (anonymous function)&lt;/li&gt;
&lt;li&gt;아래 예시에서는 람다의 타입: Comparable&amp;lt;String&amp;gt;, 매개변수 (s1, s2)의 타입은 String, 반환 타입은 int 입니다.&lt;/li&gt;
&lt;li&gt;타입을 명시해야 할 경우 다음과 같이 작성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;lambda Example&lt;/h3&gt;
&lt;pre id=&quot;code_1749028235930&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Main {
    public static void main(String[] args) {
        List&amp;lt;String&amp;gt; words = Arrays.asList(&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;);

        Collections.sort(words,
                (s1, s2) -&amp;gt; Integer.compare(s1.length(), s2.length()));
        
        // Type 명시
        Collections.sort(words, Comparator.comparingInt(String::length));
	    
        // JDK 1.8 이상의 List 인터페이스의 sort 메서드 활용 시
        words.sort(Comparator.comparingInt(String::length));            
    }
}&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;람다의 한계&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;코드 자체로 동작이 명확하게 설명되지 않거나 코드 라인수가 많아지면 사용하는 것을 고려해야 합니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-43] 람다보다는 메서드 참조를 사용하라&lt;/b&gt;&lt;/span&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;pre id=&quot;code_1749028279731&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 람다를 사용한 코드
map.merge(key, 1, (count, incr) -&amp;gt; count + incr);

// 메서드 참조를 사용한 코드
map.merge(key, 1, Integer::sum);

service.execute(GoshThisClassNameIsHumongous::action); // 참조 메소드 사용

service.execute(() -&amp;gt; action()); // 람다 사용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table id=&quot;208ac551-863b-80bd-a394-d311e3cacaa3&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr id=&quot;208ac551-863b-80e5-bf01-db189c1af2a2&quot;&gt;
&lt;td id=&quot;&amp;gt;;f|&quot;&gt;정적&lt;/td&gt;
&lt;td id=&quot;CAP{&quot;&gt;Integer::parseInt&lt;/td&gt;
&lt;td id=&quot;}kTD&quot;&gt;str -&amp;gt; Integer.parseInt(str)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;208ac551-863b-80a4-a03d-ff799d32dc08&quot;&gt;
&lt;td id=&quot;&amp;gt;;f|&quot;&gt;한정적(인스턴스)&lt;/td&gt;
&lt;td id=&quot;CAP{&quot;&gt;Instant.now()::isAfter&lt;/td&gt;
&lt;td id=&quot;}kTD&quot;&gt;Instant then = Instant.now();t -&amp;gt; then.isAfter(t)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;208ac551-863b-8022-83b4-f3a686e97056&quot;&gt;
&lt;td id=&quot;&amp;gt;;f|&quot;&gt;비한정적(인스턴스)&lt;/td&gt;
&lt;td id=&quot;CAP{&quot;&gt;String::toLowerCase&lt;/td&gt;
&lt;td id=&quot;}kTD&quot;&gt;str -&amp;gt; str.toLowerCase()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;208ac551-863b-80cd-a4a2-d770d502f36d&quot;&gt;
&lt;td id=&quot;&amp;gt;;f|&quot;&gt;클래스 생성자&lt;/td&gt;
&lt;td id=&quot;CAP{&quot;&gt;TreeMap&amp;lt;K,V&amp;gt;::new&lt;/td&gt;
&lt;td id=&quot;}kTD&quot;&gt;() -&amp;gt; new TreeMap&amp;lt;K,V&amp;gt;()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;208ac551-863b-80db-aaca-e74629dc867e&quot;&gt;
&lt;td id=&quot;&amp;gt;;f|&quot;&gt;배열 생성자&lt;/td&gt;
&lt;td id=&quot;CAP{&quot;&gt;Int[]::new&lt;/td&gt;
&lt;td id=&quot;}kTD&quot;&gt;len -&amp;gt; new int[len]&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;&amp;nbsp;&lt;/p&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;/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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-44] 표준 함수형 인터페이스를 사용하라&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수형 인터페이스를 직접 구현할 수도 있지만, java.util.function 패키지가 제공하는 표준 함수형 인터페이스로 해결이 가능합니다.&lt;/li&gt;
&lt;li&gt;@FunctionalInterface: 해당 인터페이스가 오직 하나의 추상 메서드만을 가지고 있음을 암시하는 어노테이션&lt;/li&gt;
&lt;/ul&gt;
&lt;table id=&quot;208ac551-863b-80c5-ac62-f2a70f3b9a36&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr id=&quot;208ac551-863b-808a-bd24-d04f00383a20&quot;&gt;
&lt;td id=&quot;\kmK&quot;&gt;&lt;b&gt;인터페이스&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;Lkfk&quot;&gt;&lt;b&gt;함수 시그니처&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;bZJ\&quot;&gt;&lt;b&gt;의미&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;wEg]&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;208ac551-863b-80a7-80ed-f1d766f841cc&quot;&gt;
&lt;td id=&quot;\kmK&quot;&gt;UnaryOperator&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td id=&quot;Lkfk&quot;&gt;T apply(T t)&lt;/td&gt;
&lt;td id=&quot;bZJ\&quot;&gt;반환값과 인수의 타입이 같은 함수, 인수는 1개&lt;/td&gt;
&lt;td id=&quot;wEg]&quot;&gt;String::toLowerCase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;208ac551-863b-8038-b96d-ccdb35c55dda&quot;&gt;
&lt;td id=&quot;\kmK&quot;&gt;BinaryOperator&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td id=&quot;Lkfk&quot;&gt;T apply(T t1, T t2)&lt;/td&gt;
&lt;td id=&quot;bZJ\&quot;&gt;반환값과 인수의 타입이 같은 함수, 인수는 2개&lt;/td&gt;
&lt;td id=&quot;wEg]&quot;&gt;BigInteger::add&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;208ac551-863b-805a-bd0a-c2f142e04556&quot;&gt;
&lt;td id=&quot;\kmK&quot;&gt;Predicate&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td id=&quot;Lkfk&quot;&gt;boolean test(T t)&lt;/td&gt;
&lt;td id=&quot;bZJ\&quot;&gt;한 개의 인수를 받아서 boolean을 반환하는 함수&lt;/td&gt;
&lt;td id=&quot;wEg]&quot;&gt;Collection::isEmpty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;208ac551-863b-80f7-9ae8-f611e5f46119&quot;&gt;
&lt;td id=&quot;\kmK&quot;&gt;Function&amp;lt;T,R&amp;gt;&lt;/td&gt;
&lt;td id=&quot;Lkfk&quot;&gt;R apply(T t)&lt;/td&gt;
&lt;td id=&quot;bZJ\&quot;&gt;인수와 반환 타입이 다른 함수&lt;/td&gt;
&lt;td id=&quot;wEg]&quot;&gt;Arrays::asList&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;208ac551-863b-80e1-8503-fe3c28e7f467&quot;&gt;
&lt;td id=&quot;\kmK&quot;&gt;Supplier&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td id=&quot;Lkfk&quot;&gt;T get()&lt;/td&gt;
&lt;td id=&quot;bZJ\&quot;&gt;인수를 받지 않고 값을 반환, 제공하는 함수&lt;/td&gt;
&lt;td id=&quot;wEg]&quot;&gt;Instant::now&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;208ac551-863b-80be-acda-e87db4640a4d&quot;&gt;
&lt;td id=&quot;\kmK&quot;&gt;Consumer&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td id=&quot;Lkfk&quot;&gt;void accept(T t)&lt;/td&gt;
&lt;td id=&quot;bZJ\&quot;&gt;한 개의 인수를 받고 반환값이 없는 함수&lt;/td&gt;
&lt;td id=&quot;wEg]&quot;&gt;System.out::println&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;&amp;nbsp;&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;p data-ke-size=&quot;size16&quot;&gt;Example&lt;/p&gt;
&lt;pre id=&quot;code_1749028380452&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface ExecutorService extends Executor {
    &amp;lt;T&amp;gt; Future&amp;lt;T&amp;gt; submit(Callback&amp;lt;T&amp;gt; task);
    Future&amp;lt;?&amp;gt; submit(Runnable task);
}&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-45] 스트림은 주의해서 사용하라&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스트림(stream): Java 8에서 추가된 기능으로 함수형 인터페이스인 람다(lambda)를 활용할 수 있는 기술&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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;stream pipeline
&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;lazy evaluation 방식입니다. (불필요한 연산을 피하기 위해 연산을 지연시켜 놓았다가 필요할 때 연산하는 방법)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Example&lt;/h3&gt;
&lt;pre id=&quot;code_1749028451499&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; numbers = Arrays.asList(1, 2, 3, 4, 5);

// 일반 순회 방식
List&amp;lt;Integer&amp;gt; numberList = new ArrayList&amp;lt;&amp;gt;();

  for (Integer number : numbers) {
      if (number % 2 == 0) {
          numberList .add(number * 2);
      }
  }

// Stream 방식
List&amp;lt;Integer&amp;gt; numberList = numbers.stream()
                                .filter(number -&amp;gt; number % 2 == 0)
                                .map(number -&amp;gt; number * 2)
                                .collect(Collectors.toList());


System.out.println(numbersList); // 출력: [4, 8]&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;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;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;color: #ee2323;&quot;&gt;&lt;b&gt;[item-46] 스트림에서는 부작용 없는 함수를 사용하라&lt;/b&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;u&gt;&lt;b&gt;[스트림 패러다임의 핵심]&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;계산을 일련의 변환(transformation)으로 재구성하는 부분입니다.&lt;/li&gt;
&lt;li&gt;변환 단계는 가능한 이전 단계의 결과를 받아서 처리하는 함수여야 합니다.&lt;/li&gt;
&lt;li&gt;스트림 연산에 건네는 함수 객체는 모두 부작용(side effect)이 없어야 합니다.&lt;/li&gt;
&lt;li&gt;forEach 연산은 스트림 계산 결과를 보고할 때만 사용하고, 계산하는 데는 안 쓰는 게 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749028496499&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// forEach 문제 코드
Map&amp;lt;String, Long&amp;gt; freq = new HashMap&amp;lt;&amp;gt;();

try (Stream&amp;lt;String&amp;gt; words = new Scanner(file).tokens()) {
    words.forEach(word -&amp;gt; {
        freq.merge(word.toLowerCase(), 1L, Long::sum);
    });
}

// 정상 코드
Map&amp;lt;String, Long&amp;gt; freq = new HashMap&amp;lt;&amp;gt;();

try (Stream&amp;lt;String&amp;gt; words = new Scanner(file).tokens()) {
    freq = words.collect(groupingBy(String::toLowerCase, counting()));
}&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[item-47] 반환 타입으로는 스트림보다 컬렉션이 낫다&lt;/b&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;u&gt;&lt;b&gt;원소 시퀀스 타입으로 사용해 왔던 것들&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Collection, Iterable, array, stream(Java 8)&lt;/li&gt;
&lt;li&gt;그런데 stream은 반복 (iteration)을 지원하지 않기 때문에 스트림과 반복을 알맞게 조합하여 좋은 코드를 만들어야 합니다.&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;u&gt;&lt;b&gt;반환 타입이 컬렉션이 스트림보다 나은 이유&lt;/b&gt;&lt;/u&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;/li&gt;
&lt;li&gt;Collection은 Iterable의 하위 타입이고 Stream 메서드도 지원합니다.&lt;/li&gt;
&lt;li&gt;공개 API의 반환 타입에는 컬렉션이나 그 하위타입을 쓰는 게 최선입니다.&lt;/li&gt;
&lt;li&gt;Arrays 역시 asList와 Stream.of 메서드로 쉽게 반복문과 Stream을 지원할 수 있습니다.&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;[item-48] 스트림 병렬화는 주의해서 적용하라&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;스트림을 생성하는 데이터 소스가 Stream.iterate이거나 중간 연산으로 limit를 사용하면 파이프라인 병렬화로는 성능 개선을 기대하기가 어렵습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749028552059&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    // java.math.BigInteger.TWO는 자바 9부터 public 접근이 가능하다.
    primes().map(p -&amp;gt; TWO.pow(p.intValueExact()).subtract(ONE))
        .filter(mersenne -&amp;gt; mersenne.isProbablePrime(50))
        .limit(20)
        .forEach(System.out::println);
}

static Stream&amp;lt;BigInteger&amp;gt; primes() {
    return Stream.iterate(TWO, BigInteger::nextProbablePrime);
}&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;u&gt;&lt;b&gt;[병렬화가 좋은 상황]&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;스트림의 소스가 ArrayList, HashMap, HashSet, ConcurrentHashMap의 인스턴스이거나 배열 int, long 일 때 효과가 좋습니다.
&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;종단 연산 중에서는 min, max와 같이 만들어진 모든 원소를 하나로 합치는 축소 연산이 좋으며, anyMatch, allMatch, noneMatch처럼 조건이 맞는 경우 즉시 반환되는 메서드도 병렬화에 적합합니다.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Backend/Effective Java</category>
      <category>Effective Java</category>
      <category>이펙티브 자바</category>
      <category>이펙티브 자바 7장 람다와 스트림</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/416</guid>
      <comments>https://daily1313.tistory.com/entry/Effective-Java-7%EC%9E%A5-%EB%9E%8C%EB%8B%A4%EC%99%80-%EC%8A%A4%ED%8A%B8%EB%A6%BC#entry416comment</comments>
      <pubDate>Tue, 12 Aug 2025 23:40:42 +0900</pubDate>
    </item>
    <item>
      <title>[Tomcat] Tomcat의 핵심 컴포넌트와 동작 과정에 대해 분석해보자</title>
      <link>https://daily1313.tistory.com/entry/Tomcat-Tomcat%EC%9D%98-%ED%95%B5%EC%8B%AC-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%99%80-%EB%8F%99%EC%9E%91-%EA%B3%BC%EC%A0%95%EC%97%90-%EB%8C%80%ED%95%B4-%EB%B6%84%EC%84%9D%ED%95%B4%EB%B3%B4%EC%9E%90</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-09 오후 1.58.14.png&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KAgMp/btsPN63gRG3/lywMASrAReZlJzxUWoZMwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KAgMp/btsPN63gRG3/lywMASrAReZlJzxUWoZMwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KAgMp/btsPN63gRG3/lywMASrAReZlJzxUWoZMwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKAgMp%2FbtsPN63gRG3%2FlywMASrAReZlJzxUWoZMwK%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;725&quot; height=&quot;183&quot; data-filename=&quot;스크린샷 2025-08-09 오후 1.58.14.png&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;412&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;Spring Boot와 같은 프레임워크에서는 Embedded Tomcat을 기본 웹 서버로 제공하여, 개발자가 별도로 외부 톰캣 서버를 설치하거나 설정하지 않아도 애플리케이션을 실행하면 곧바로 웹 서버가 함께 구동되도록 설계되어 있습니다.&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;반면, 기존의 Tomcat(외장 톰캣)은 WAR 파일을 외부 톰캣 서버에 배포하여 운영하는 전통적인 방식입니다. 두 방식 모두 톰캣이라는 서블릿 컨테이너 위에서 동작한다는 공통점을 갖고 있으며, 애플리케이션의 요청 처리 흐름은 결국 동일한 톰캣 구조를 따릅니다. 따라서 Embedded 환경에서도 톰캣의 내부 동작 원리를 명확히 이해하는 것이 중요하다고 생각했기에 해당 글을 작성하게 되었습니다.&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;해당 포스팅에서는 Apache Tomcat의 개념에 대해 간략히 기재하고, Embedded Tomcat과 Tomcat을 비교하고, Tomcat의 동작과정과 컴포넌트에 대해서 정리하려고 합니다.&amp;nbsp; 상세 내용은 톰캣 동작과정을 이해하는데 크게 도움이 된 &lt;a href=&quot;https://jiminbyun.medium.com/apache-tomcat-1-core-components-and-their-interactions-939f1f476544&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DevOps Enginner 님의 자료&lt;/a&gt;를 전적으로 참고하였습니다.&amp;nbsp;&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. Apache Tomcat&amp;nbsp;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Java Servlet, JSP, Expression Language(EL)를 구현한 오픈소스 웹 서버 및 서블릿 컨테이너&lt;/li&gt;
&lt;li&gt;Java 기반 애플리케이션을 실행할 수 있는 HTTP 서버 환경을 제공하며, 고성능의 경량화된 웹 애플리케이션 서버 역할도 수행합니다.&lt;/li&gt;
&lt;li&gt;Tomcat은 Java 서블릿을 실행하고 JSP coding이 포함된 웹 페이지를 렌더링하는 서블릿 컨테이너 역할을 합니다.&lt;/li&gt;
&lt;li&gt;Tomcat은 Apache Software Foundation에서 관리하며, 오픈 커뮤니티에 의해 개발 및 유지됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;841&quot; data-start=&quot;778&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;841&quot; data-start=&quot;778&quot; data-ke-size=&quot;size16&quot;&gt;흔히 Apache Tomcat을 &quot;서블릿 컨테이너(Servlet Container)&quot;라고 정의하기도 하는데, 이 용어가 의미하는 바를 구체적으로 이해하는 것이 중요합니다. 아래에서는 Servlet Container가 무엇인지, 그리고 그 개념을 보다 쉽게 이해할 수 있도록 예시 자료와 함께 설명드리겠습니다.&lt;/p&gt;
&lt;p data-end=&quot;841&quot; data-start=&quot;778&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Servlet Container&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java 기반의 웹 애플리케이션에서 HTTP 요청을 처리하고 Servlet을 실행하는 환경&lt;/li&gt;
&lt;li&gt;HTTP 요청과 응답을 처리하는 Java 웹 서버의 핵심 엔진&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-07 오전 12.22.51.png&quot; data-origin-width=&quot;1610&quot; data-origin-height=&quot;902&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFrnXm/btsPHea0TW5/KKuUrVPDZFRFzCKQs3UN6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFrnXm/btsPHea0TW5/KKuUrVPDZFRFzCKQs3UN6K/img.png&quot; data-alt=&quot;인프런 김영한님 자료&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFrnXm/btsPHea0TW5/KKuUrVPDZFRFzCKQs3UN6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFrnXm%2FbtsPHea0TW5%2FKKuUrVPDZFRFzCKQs3UN6K%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;650&quot; height=&quot;364&quot; data-filename=&quot;스크린샷 2025-08-07 오전 12.22.51.png&quot; data-origin-width=&quot;1610&quot; data-origin-height=&quot;902&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;HelloServlet 구현체&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1754494653945&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@WebServlet(name = &quot;helloServlet&quot;, urlPatterns = &quot;/hello&quot;)
public class HelloServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response){
	PrintWriter writer = response.getWriter(); 
        writer.write(&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;p data-ke-size=&quot;size16&quot;&gt;이 코드는 /hello라는 URL로 HTTP 요청이 들어올 때 실행되는 서블릿 (HelloServlet.class)을 정의한 것입니다. HttpServletRequest와 HttpServletResponse를 통해 개발자는 HTTP 요청과 응답 정보를 손쉽게 다룰 수 있습니다.&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;즉, HttpServlet을 상속받은 클래스를 정의하고, 요청에 맞는 메서드를 오버라이드함으로써 HTTP 기반 웹 로직을 Java 코드로 구현할 수 있게 됩니다. 위의 예시에서는 서블릿만 다루었지만, JSP(Java + html)도 받아서 톰캣이 직접 처리해 줄 수 있습니다. (Tomcat 내부에 존재하는 Jasper 컴포넌트에 의해 Java Servlet으로 변환되어 처리됩니다.)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Servlet Flow&amp;nbsp;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클라이언트가 url을 입력하여 HttpRequest를 Servlet Container로 전송합니다.&lt;/li&gt;
&lt;li&gt;요청을 전송받은 Servlet Container는 HttpServletRequest와 HttpServletResponse 객체를 생성합니다.&lt;/li&gt;
&lt;li&gt;web.xml 기반으로 사용자가 요청한 url이 어느 서블릿에 대한 요청인지 확인합니다.&lt;/li&gt;
&lt;li&gt;해당 서블릿에서 service method를 호출한 후 HttpMethod에 따라 do + HttpMethod()를 호출합니다.&lt;/li&gt;
&lt;li&gt;do + HttpMethod() 메서드는 동적 페이지를 생성한 후 HttpServletResponse 객체에 응답을 보냅니다.&lt;/li&gt;
&lt;li&gt;응답이 끝나면 HttpServletRequest, HttpServletResponse 두 객체를 소멸시킵니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Tomcat vs Embedded Tomcat&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 87px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px;&quot;&gt;항목&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px;&quot;&gt;Embedded Tomcat (내장 톰캣)&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px;&quot;&gt;Tomcat (외장 톰캣)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;배포 방식&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Springboot Application과 Tomcat 서버 전체가 하나의 jar/war에 포함되어 실행됩니다.&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Tomcat을 별도로 설치한 뒤, 빌드된 war 파일을 Tomcat의 webapps 디렉터리에 배포합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;실행 방식&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;java -jar app.jar로 애플리케이션 실행 시 자체적으로 Tomcat이 구동됩니다.&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;톰캣 설치 경로/bin 디렉토리로 이동 후, Tomcat 서버를 직접 실행 (startup.sh)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;설정 및 구성&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;application.properties 및 자동 구성 설정&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;server.xml&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;호스트 관리&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;단일 호스트 지원&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;virtual host 기능을 통해 도메인별, 컨텍스트별 여러 애플리케이션 동시 운영 가능&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Embedded Tomcat과 External Tomcat의 비교를 마쳤으니, Tomcat의 컴포넌트에 대해서 설명드리겠습니다.&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;2. Tomcat Component, Architecture&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Tomcat Component&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Coyote (HTTP Connector)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Tomcat의 HTTP 커넥터(Connector)로, HTTP/1.1 프로토콜을 지원하는 컴포넌트&lt;/li&gt;
&lt;li&gt;Coyote는 서버의 특정 TCP 포트를 통해 들어오는 클라이언트 연결 요청(Http Request)을 listen 하며, 요청을 Tomcat의 엔진(Catalina)으로 전달합니다.&lt;/li&gt;
&lt;li&gt;Catalina는 이를 처리한 후, 응답을 Coyote를 통해 다시 클라이언트에게 전달합니다.&lt;/li&gt;
&lt;li&gt;저수준 네트워크 통신을 처리하며, 요청이 들어오면 적절한 처리 컴포넌트로 경로를 지정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Catalina (The Servlet Engine)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Servlet 및 JSP 요청 처리 로직을 호출하고 실행하는 Tomcat의 핵심 엔진입니다.&lt;/li&gt;
&lt;li&gt;들어온 요청이 Servlet인지 JSP인지 판단하고, Catalina는 로딩, 초기화, 요청 처리 등 Servlet의 LifeCycle을 관리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Jasper (The JSP Engine)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JSP 파일을 자바 Servlet 코드로 변환하는 컴포넌트입니다.&lt;/li&gt;
&lt;li&gt;JSP를 컴파일하여 bytecode로 변환하는 Catalina에게 전달하고, 이후 Catalina가 실행을 담당합니다.&lt;/li&gt;
&lt;li&gt;컴파일된 후 Jasper는 실행을 위해 Java bytecode를 Catalina로 넘겨줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Value
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Tomcat에서 요청을 가로챌 수 있게 해주는 요소로, 로깅, 보안검사, 요청 필터링과 같은 서비스를 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cluster
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 대의 Tomcat 인스턴스를 하나의 클러스터로 묶는 기능입니다.&lt;/li&gt;
&lt;li&gt;로드 밸런싱과 fail‑over 기능을 통해 고가용성을 지원합니다.&lt;/li&gt;
&lt;/ul&gt;
&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;해당 컴포넌트를 기반으로 외부 요청 처리에 대한 다이어그램을 보여드리겠습니다.&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;Tomcat 요청 흐름&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-09 오후 1.44.06.png&quot; data-origin-width=&quot;2126&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ee0V4Y/btsPMQNHf74/RPTeaAWWlN1lioIhomSqL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ee0V4Y/btsPMQNHf74/RPTeaAWWlN1lioIhomSqL1/img.png&quot; data-alt=&quot;Tomcat 요청 흐름 (Client - Coyote(Connector) - Catalina (Engine) - Jasper)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ee0V4Y/btsPMQNHf74/RPTeaAWWlN1lioIhomSqL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fee0V4Y%2FbtsPMQNHf74%2FRPTeaAWWlN1lioIhomSqL1%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;748&quot; height=&quot;222&quot; data-filename=&quot;스크린샷 2025-08-09 오후 1.44.06.png&quot; data-origin-width=&quot;2126&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Tomcat 요청 흐름 (Client - Coyote(Connector) - Catalina (Engine) - Jasper)&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;Tomcat Architecture Elements&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Server
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Tomcat 전체 인스턴스를 대표하는 최상위 요소입니다.&lt;/li&gt;
&lt;li&gt;server.xml의 루트 요소이며, 하나 이상의 Service를 포함할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Service
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나 이상의 Connector와 하나의 Engine을 묶는 단위입니다.&lt;/li&gt;
&lt;li&gt;네트워크와 서블릿 처리 로직 간의 중개자 역할을 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Engine
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Service 내부에서 요청 처리의 핵심 파이프라인 역할을 합니다.&lt;/li&gt;
&lt;li&gt;들어온 요청을 적절한 Host로 전달합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Host
&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;여러 개의 Context(웹 애플리케이션)를 포함할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Context
&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;서블릿과 JSP가 배치되고 실행되는 공간입니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tomcat 구성요소에 따른 Architecture는 다음과 같이 구성됩니다.&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-09 오후 1.46.54.png&quot; data-origin-width=&quot;1866&quot; data-origin-height=&quot;1146&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZmOVv/btsPNFdLvbK/HKFNRlF0h58R4NbmT4CVk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZmOVv/btsPNFdLvbK/HKFNRlF0h58R4NbmT4CVk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZmOVv/btsPNFdLvbK/HKFNRlF0h58R4NbmT4CVk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZmOVv%2FbtsPNFdLvbK%2FHKFNRlF0h58R4NbmT4CVk1%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;684&quot; height=&quot;420&quot; data-filename=&quot;스크린샷 2025-08-09 오후 1.46.54.png&quot; data-origin-width=&quot;1866&quot; data-origin-height=&quot;1146&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;요청 흐름은 HTTP Request로 시작해 HTTP Response로 끝납니다.&lt;br /&gt;클라이언트가 어떻게 HTTP Request를 전송하고, 서버가 이를 처리해 HTTP Response를 생성한 뒤 어떤 방식으로 클라이언트에 전달하는지를 파악하기 위해, 각각의 구조를 차례대로 살펴보겠습니다.&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 data-ke-size=&quot;size26&quot;&gt;3. HTTP Message (HttpRequest, HttpResponse)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTTP Message&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP Message는 서버와 클라이언트 간의 데이터가 교환되는 메커니즘입니다.&lt;/li&gt;
&lt;li&gt;HTTP request: 클라이언트가 서버로 전달해서 서버의 액션이 일어나게끔 하는 메시지&lt;/li&gt;
&lt;li&gt;HTTP response: 요청에 대한 서버의 답변입니다.&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;먼저, 클라이언트가 전송하는 HTTP 요청 메시지의 구조를 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;HttpRequest&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-09 오후 2.05.43.png&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuSkUc/btsPNF5US4l/wDwpodL1HbeGGymacA3KV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuSkUc/btsPNF5US4l/wDwpodL1HbeGGymacA3KV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuSkUc/btsPNF5US4l/wDwpodL1HbeGGymacA3KV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuSkUc%2FbtsPNF5US4l%2FwDwpodL1HbeGGymacA3KV0%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;625&quot; height=&quot;400&quot; data-filename=&quot;스크린샷 2025-08-09 오후 2.05.43.png&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;528&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;StartLine: HTTP Method, Path, HTTP version을 나타내는 단일 줄을 의미합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP Method: HTTP Verb라고도 하고 요청의 의도를 나타냅니다. (GET, POST, PATCH, DELETE..)&lt;/li&gt;
&lt;li&gt;Path: 요청 url 경로 (/login..)&lt;/li&gt;
&lt;li&gt;HTTP Version: 요청에서 사용하는 HTTP 프로토콜 버전 (HTTP/1.1, HTTP/2)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Headers: HTTP Message 교환 과정에서 필요한 추가적인 정보를 담는 부분 (Request Headers + Representation Headers)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-09 오후 2.06.17.png&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gkw2V/btsPLkPCXgB/GAWHrtJfE4eI8RPtkem5V1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gkw2V/btsPLkPCXgB/GAWHrtJfE4eI8RPtkem5V1/img.png&quot; data-alt=&quot;임시 example&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gkw2V/btsPLkPCXgB/GAWHrtJfE4eI8RPtkem5V1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGkw2V%2FbtsPLkPCXgB%2FGAWHrtJfE4eI8RPtkem5V1%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;649&quot; height=&quot;319&quot; data-filename=&quot;스크린샷 2025-08-09 오후 2.06.17.png&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;690&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;임시 example&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Request Headers: 요청 클라이언트에 대한 부가적인 정보를 서버에 전달하기 위한 헤더 필드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Accept: 클라이언트가 수신 가능한 MIME 타입 (application/json)&lt;/li&gt;
&lt;li&gt;Accept-Encoding: 클라이언트가 수신 가능한 압축 방식 (gzip, br)&lt;/li&gt;
&lt;li&gt;Accept-Language: 클라이언트가 사용 가능한 언어 정보 (ko-KR, en-US)&lt;/li&gt;
&lt;li&gt;Host: 요청 대상 서버의 도메인 정보 (Host: &lt;a href=&quot;http://daily1313.tistory.com&quot;&gt;daily1313.tistory.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Origin: 요청이 시작된 출처&lt;/li&gt;
&lt;li&gt;Referer: 현재 요청을 유발한 이전 페이지의 URL&lt;/li&gt;
&lt;li&gt;User-Agent: 클라이언트의 프로그램 정보&lt;/li&gt;
&lt;li&gt;Authorization: 인증 정보 (bearer token ..)&lt;/li&gt;
&lt;li&gt;Cookie: 클라이언트가 보유한 쿠키 전송&lt;/li&gt;
&lt;li&gt;X-Requested-With: Ajax 요청 여부 확인&lt;/li&gt;
&lt;li&gt;Sec-*: 브라우저의 보안 메타 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Representation Headers: 메시지 본문으로 전송된 데이터에 대한 부가적인 정보로 사용되는 헤더 필드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Content-Type: 본문의 미디어 타입 형식 (application/json, text/html)&lt;/li&gt;
&lt;li&gt;Content-Encoding: 본문 압축 방식 (gzip)&lt;/li&gt;
&lt;li&gt;Content-Language: 본문 사용 언어 (en, ko)&lt;/li&gt;
&lt;li&gt;Content-Length: 본문의 바이트 크기&lt;/li&gt;
&lt;li&gt;Content-Location: 본문이 참조하는 리소스의 의 대체 위치&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Request Body: 서버에 정보를 전달하는 요청의 일부입니다. (POST, PATCH, PUT Method만 Body를 가집니다.)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Content-Type에 따라 Text, Form data, Json 등의 형식을 가집니다.&lt;/li&gt;
&lt;li&gt;ex) infos: [{&quot;mac&quot;:&quot;00:00:00:11:11:11&quot;,&quot;ip&quot;:&quot;&quot;,&quot;name&quot;:&quot;client-1&quot;,&quot;value&quot;:&quot;PC&quot;,&quot;client_asset_user_id&quot;:1,&quot;description&quot;:&quot;httpRequest body test&quot;}]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;HttpResponse&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-09 오후 2.08.34.png&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk514N/btsPNgLU4bm/aH8mQK36zu4SbYtPlAgKs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk514N/btsPNgLU4bm/aH8mQK36zu4SbYtPlAgKs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk514N/btsPNgLU4bm/aH8mQK36zu4SbYtPlAgKs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk514N%2FbtsPNgLU4bm%2FaH8mQK36zu4SbYtPlAgKs1%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;720&quot; height=&quot;444&quot; data-filename=&quot;스크린샷 2025-08-09 오후 2.08.34.png&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;444&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;StatusLine
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[HTTP Protocol] [Status Code] [Status Text] 표준을 따릅니다.&lt;/li&gt;
&lt;li&gt;ex) HTTP/1.1 403 Forbidden&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Headers
&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;구조는 Request의 Header와 동일합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Body
&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지는 Embedded Tomcat만 사용하다 보니, Tomcat의 구성 요소나 동작 방식에 대해 깊이 이해하지 못한 상태였습니다. 이번에 좋은 계기를 통해 Tomcat의 아키텍처와 동작 과정을 자세히 살펴볼 수 있었고, 많은 지식을 습득할 수 있었습니다.&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jiminbyun.medium.com/apache-tomcat-1-core-components-and-their-interactions-939f1f476544&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jiminbyun.medium.com/apache-tomcat-1-core-components-and-their-interactions-939f1f476544&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1754492692713&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Apache Tomcat (1): Core Components and Their Interactions&quot; data-og-description=&quot;As a DevOps engineer, navigating the complexities of modern web applications and server management is part of the daily grind. However&amp;hellip;&quot; data-og-host=&quot;jiminbyun.medium.com&quot; data-og-source-url=&quot;https://jiminbyun.medium.com/apache-tomcat-1-core-components-and-their-interactions-939f1f476544&quot; data-og-url=&quot;https://jiminbyun.medium.com/apache-tomcat-1-core-components-and-their-interactions-939f1f476544&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/v76F2/hyZuzagYp3/2GoCw8Ifdhpn1aUDpXV021/img.png?width=600&amp;amp;height=401&amp;amp;face=0_0_600_401,https://scrap.kakaocdn.net/dn/cojyEO/hyZuwq5mhE/M5bUOrv8odalZJdjxfXZ51/img.jpg?width=1358&amp;amp;height=849&amp;amp;face=0_0_1358_849,https://scrap.kakaocdn.net/dn/xpVxh/hyZvmIrSZB/guDCXhhDJGKtcs5OK70GkK/img.jpg?width=1358&amp;amp;height=849&amp;amp;face=0_0_1358_849&quot;&gt;&lt;a href=&quot;https://jiminbyun.medium.com/apache-tomcat-1-core-components-and-their-interactions-939f1f476544&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jiminbyun.medium.com/apache-tomcat-1-core-components-and-their-interactions-939f1f476544&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/v76F2/hyZuzagYp3/2GoCw8Ifdhpn1aUDpXV021/img.png?width=600&amp;amp;height=401&amp;amp;face=0_0_600_401,https://scrap.kakaocdn.net/dn/cojyEO/hyZuwq5mhE/M5bUOrv8odalZJdjxfXZ51/img.jpg?width=1358&amp;amp;height=849&amp;amp;face=0_0_1358_849,https://scrap.kakaocdn.net/dn/xpVxh/hyZvmIrSZB/guDCXhhDJGKtcs5OK70GkK/img.jpg?width=1358&amp;amp;height=849&amp;amp;face=0_0_1358_849');&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;Apache Tomcat (1): Core Components and Their Interactions&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;As a DevOps engineer, navigating the complexities of modern web applications and server management is part of the daily grind. However&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jiminbyun.medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@appti/Tomcat-Embedded-Tomcat&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@appti/Tomcat-Embedded-Tomcat&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1754717384146&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Tomcat, Embedded Tomcat&quot; data-og-description=&quot;Tomcat, Embedded Tomcat&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@appti/Tomcat-Embedded-Tomcat&quot; data-og-url=&quot;https://velog.io/@appti/Tomcat-Embedded-Tomcat&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/yZpty/hyZvjyE8U4/hXFFaD4ruQD1mxo4MLbIc0/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/11d6o/hyZvtacKuG/UrpBj8OewoSK1IKVSqjnJ1/img.png?width=2750&amp;amp;height=964&amp;amp;face=0_0_2750_964,https://scrap.kakaocdn.net/dn/cp7egk/hyZuEiWZAm/xHeIEqpk87eYSjBZ2wCCFk/img.png?width=1700&amp;amp;height=1050&amp;amp;face=0_0_1700_1050&quot;&gt;&lt;a href=&quot;https://velog.io/@appti/Tomcat-Embedded-Tomcat&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@appti/Tomcat-Embedded-Tomcat&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/yZpty/hyZvjyE8U4/hXFFaD4ruQD1mxo4MLbIc0/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/11d6o/hyZvtacKuG/UrpBj8OewoSK1IKVSqjnJ1/img.png?width=2750&amp;amp;height=964&amp;amp;face=0_0_2750_964,https://scrap.kakaocdn.net/dn/cp7egk/hyZuEiWZAm/xHeIEqpk87eYSjBZ2wCCFk/img.png?width=1700&amp;amp;height=1050&amp;amp;face=0_0_1700_1050');&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;Tomcat, Embedded Tomcat&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Tomcat, Embedded Tomcat&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Messages&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Messages&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1754717399539&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;HTTP messages - HTTP | MDN&quot; data-og-description=&quot;HTTP messages are the mechanism used to exchange data between a server and a client in the HTTP protocol. There are two types of messages: requests sent by the client to trigger an action on the server, and responses, the answer that the server sends in re&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Messages&quot; data-og-url=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Messages&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/401te/hyZuAnk2Dw/sHXMSmYaHCGrWNWBLnK4AK/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/ASkip/hyZuHtc4W5/NgwxqRPrfJmku4Db8hvaG1/img.png?width=3840&amp;amp;height=2356&amp;amp;face=0_0_3840_2356&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Messages&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Messages&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/401te/hyZuAnk2Dw/sHXMSmYaHCGrWNWBLnK4AK/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/ASkip/hyZuHtc4W5/NgwxqRPrfJmku4Db8hvaG1/img.png?width=3840&amp;amp;height=2356&amp;amp;face=0_0_3840_2356');&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;HTTP messages - HTTP | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;HTTP messages are the mechanism used to exchange data between a server and a client in the HTTP protocol. There are two types of messages: requests sent by the client to trigger an action on the server, and responses, the answer that the server sends in re&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hahahoho5915.tistory.com/62&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hahahoho5915.tistory.com/62&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1754717402169&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[간단정리] HTTP Request/Response 구조&quot; data-og-description=&quot;개요 HTTP Request(요청)/Response(응답) 구조 알아보기 HTTP HyperText Transfer Protocol 하이퍼텍스트(HTML) 문서를 교환하기 위해 만들어진 protocol(통신 규약). 즉 웹상에서 네트워크로 서버끼리 통신을 할때 &quot; data-og-host=&quot;hahahoho5915.tistory.com&quot; data-og-source-url=&quot;https://hahahoho5915.tistory.com/62&quot; data-og-url=&quot;https://hahahoho5915.tistory.com/62&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/mWofg/hyZuvM7Bf0/HEEdAkwIqe1TkAteSkc54K/img.png?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/BABB2/hyZuwkX0MS/jDZfYM1YUiXzWvns1wbcXk/img.png?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/2mZh8/hyZuCFqZqa/8R8ghBgbDmAUKKOwqEP9G0/img.png?width=960&amp;amp;height=540&amp;amp;face=0_0_960_540&quot;&gt;&lt;a href=&quot;https://hahahoho5915.tistory.com/62&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hahahoho5915.tistory.com/62&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/mWofg/hyZuvM7Bf0/HEEdAkwIqe1TkAteSkc54K/img.png?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/BABB2/hyZuwkX0MS/jDZfYM1YUiXzWvns1wbcXk/img.png?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/2mZh8/hyZuCFqZqa/8R8ghBgbDmAUKKOwqEP9G0/img.png?width=960&amp;amp;height=540&amp;amp;face=0_0_960_540');&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;[간단정리] HTTP Request/Response 구조&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;개요 HTTP Request(요청)/Response(응답) 구조 알아보기 HTTP HyperText Transfer Protocol 하이퍼텍스트(HTML) 문서를 교환하기 위해 만들어진 protocol(통신 규약). 즉 웹상에서 네트워크로 서버끼리 통신을 할때&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hahahoho5915.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aday7.tistory.com/entry/Web-HTTP-Header-%EA%B5%AC%EC%A1%B0-Request-headerResponse-header&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://aday7.tistory.com/entry/Web-HTTP-Header-%EA%B5%AC%EC%A1%B0-Request-headerResponse-header&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1754717409341&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;HTTP Header의 구조와 주요 정보: 웹 서비스 이해하기&quot; data-og-description=&quot;HTTP Header는 웹 통신에서 중요한 역할을 하는 부분입니다. 이를 통해 클라이언트와 서버는 서로에게 필요한 정보를 주고받게 됩니다. 이번 글에서는 HTTP Header의 구조와 주요 정보에 대해 알아보&quot; data-og-host=&quot;aday7.tistory.com&quot; data-og-source-url=&quot;https://aday7.tistory.com/entry/Web-HTTP-Header-%EA%B5%AC%EC%A1%B0-Request-headerResponse-header&quot; data-og-url=&quot;https://aday7.tistory.com/entry/Web-HTTP-Header-%EA%B5%AC%EC%A1%B0-Request-headerResponse-header&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nFFoU/hyZvwEK8CK/XeNobuKXZwIvHzfdVnTkNK/img.png?width=800&amp;amp;height=234&amp;amp;face=0_0_800_234,https://scrap.kakaocdn.net/dn/KkUeG/hyZvsoPem6/l3E9WfMked6XnH8XGgjbuK/img.png?width=800&amp;amp;height=234&amp;amp;face=0_0_800_234&quot;&gt;&lt;a href=&quot;https://aday7.tistory.com/entry/Web-HTTP-Header-%EA%B5%AC%EC%A1%B0-Request-headerResponse-header&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://aday7.tistory.com/entry/Web-HTTP-Header-%EA%B5%AC%EC%A1%B0-Request-headerResponse-header&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nFFoU/hyZvwEK8CK/XeNobuKXZwIvHzfdVnTkNK/img.png?width=800&amp;amp;height=234&amp;amp;face=0_0_800_234,https://scrap.kakaocdn.net/dn/KkUeG/hyZvsoPem6/l3E9WfMked6XnH8XGgjbuK/img.png?width=800&amp;amp;height=234&amp;amp;face=0_0_800_234');&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;HTTP Header의 구조와 주요 정보: 웹 서비스 이해하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;HTTP Header는 웹 통신에서 중요한 역할을 하는 부분입니다. 이를 통해 클라이언트와 서버는 서로에게 필요한 정보를 주고받게 됩니다. 이번 글에서는 HTTP Header의 구조와 주요 정보에 대해 알아보&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;aday7.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS</category>
      <category>catalina</category>
      <category>Coyote</category>
      <category>HttpRequest</category>
      <category>HttpResponse</category>
      <category>Jasper</category>
      <category>Tomcat</category>
      <category>Tomcat Architecture</category>
      <category>Tomcat Component</category>
      <category>Tomcat vs Embedded Tomcat</category>
      <category>Tomcat 동작 과정</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/415</guid>
      <comments>https://daily1313.tistory.com/entry/Tomcat-Tomcat%EC%9D%98-%ED%95%B5%EC%8B%AC-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%99%80-%EB%8F%99%EC%9E%91-%EA%B3%BC%EC%A0%95%EC%97%90-%EB%8C%80%ED%95%B4-%EB%B6%84%EC%84%9D%ED%95%B4%EB%B3%B4%EC%9E%90#entry415comment</comments>
      <pubDate>Sat, 9 Aug 2025 14:37:17 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Guava Library를 활용한 RateLimiter 적용기</title>
      <link>https://daily1313.tistory.com/entry/Spring-Guava-Library%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-RateLimiter-%EC%A0%81%EC%9A%A9%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Rate Limiter 도입 목적&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 애플리케이션의 부하를 막기 위한 하나의 수단으로 트래픽 제어가 있습니다. 트래픽 제어는 DDos(악의적인 의도를 가진 사용자에 의해 대량의 패킷 또는 요청을 보내 시스템을 마비시키는 공격)과 같은 공격을 방지하기 위해 필수적으로 웹서비스에 존재해야 하는 기능입니다.&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;이러한 취약점을 막기 위해, Rate Limiter를 활용하면 클라이언트의 과도한 API 호출을 제한하여, 웹 서버의 리소스 낭비를 방지할 수 있습니다. 이를 통해 애플리케이션 안정성이 향상되며 QoS를 보장할 수 있게 됩니다.&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;RateLimiter는 Guava (Google Library)에서 제공해 주는 API 호출 제한을 위한 클래스입니다. Guava Library가 제공해 주는 기능들을 설명하고, 이를 활용하여 Rate Limit 기능을 도입하는 과정에 대해 상세하게 설명드리겠습니다.&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;구현 내용만 확인하시고 싶으시면, 해당 PR을 참고해 주시면 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/Dongwoongkim/matjido/pull/18&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/Dongwoongkim/matjido/pull/18&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1754491309306&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;[Feature/#17-guava-rate-limiter] RestClient bean 생성 시 read, connect 타임아웃 설정, guava library를 활용한 rate l&quot; data-og-description=&quot;RestClient 모듈을 통한 API 요청시, 응답 지연을 막기 위한 Timeout 설정 추가 read: 2s (2000ms) (응답 읽기) connect: 2s(2000ms) (TCP 연결 시간) Guava Library를 활용한 Rate Limiter 기능 추가 RateLimiter: 초당 api 호출 &quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/Dongwoongkim/matjido/pull/18&quot; data-og-url=&quot;https://github.com/Dongwoongkim/matjido/pull/18&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/COupr/hyZuxKhwoz/HUJ7QjDxlwFy3OYuJGwMw0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/r2i6s/hyZvsaP15O/87dDvuJYSEXgWIWtPKz031/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/Dongwoongkim/matjido/pull/18&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/Dongwoongkim/matjido/pull/18&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/COupr/hyZuxKhwoz/HUJ7QjDxlwFy3OYuJGwMw0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/r2i6s/hyZvsaP15O/87dDvuJYSEXgWIWtPKz031/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;[Feature/#17-guava-rate-limiter] RestClient bean 생성 시 read, connect 타임아웃 설정, guava library를 활용한 rate l&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;RestClient 모듈을 통한 API 요청시, 응답 지연을 막기 위한 Timeout 설정 추가 read: 2s (2000ms) (응답 읽기) connect: 2s(2000ms) (TCP 연결 시간) Guava Library를 활용한 Rate Limiter 기능 추가 RateLimiter: 초당 api 호출&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Guava Library&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구글에서 제공해주는 Java Library Set&lt;/li&gt;
&lt;li&gt;새로운 컬렉션 타입(예: Multimap, Multiset), 불변 컬렉션(Immutable Collections), 그래프 라이브러리를 비롯하여 동시성, I/O, 해싱, 기본 타입(Primitives), 문자열 처리 등을 위한 유틸리티를 포함하고 있습니다.&lt;/li&gt;
&lt;li&gt;Guava는 Google 내부의 대부분의 Java 프로젝트에서 널리 사용되며, 많은 다른 기업들에서도 광범위하게 활용되고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Guava에서 제공하는 RateLimter 구현체를 사용하기 위해서는 Gradle에 해당 의존성을 추가해줘야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1754407623149&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation 'com.google.guava:guava:33.4.8-jre'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Guava RateLimiter API&lt;/h3&gt;
&lt;table id=&quot;246ac551-863b-8009-9cc5-c13c2237ba43&quot; style=&quot;border-collapse: collapse; width: 100.465%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.093%;&quot;&gt;Modifier and Type&lt;/td&gt;
&lt;td style=&quot;width: 30.2326%;&quot;&gt;Method&lt;/td&gt;
&lt;td style=&quot;width: 53.0232%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;246ac551-863b-80bd-a7b4-e2709257807b&quot;&gt;
&lt;td id=&quot;CYie&quot; style=&quot;width: 17.093%;&quot;&gt;static RateLimiter&lt;/td&gt;
&lt;td id=&quot;\NcT&quot; style=&quot;width: 30.2326%;&quot;&gt;create(double permitsPerSecond)&lt;/td&gt;
&lt;td id=&quot;jIMR&quot; style=&quot;width: 53.0232%;&quot;&gt;초당 허용량을 설정하여 RateLimiter 인스턴스 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;246ac551-863b-806f-b508-dc73906e125b&quot;&gt;
&lt;td id=&quot;CYie&quot; style=&quot;width: 17.093%;&quot;&gt;double&lt;/td&gt;
&lt;td id=&quot;\NcT&quot; style=&quot;width: 30.2326%;&quot;&gt;acquire()&lt;/td&gt;
&lt;td id=&quot;jIMR&quot; style=&quot;width: 53.0232%;&quot;&gt;1개의 요청을 허용받기 위해 &lt;b&gt;필요하면 대기&lt;/b&gt;하며, 대기한 시간(초)을 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;246ac551-863b-80f2-be84-fbf257b14ef2&quot;&gt;
&lt;td id=&quot;CYie&quot; style=&quot;width: 17.093%;&quot;&gt;boolean&lt;/td&gt;
&lt;td id=&quot;\NcT&quot; style=&quot;width: 30.2326%;&quot;&gt;tryAcquire()&lt;/td&gt;
&lt;td id=&quot;jIMR&quot; style=&quot;width: 53.0232%;&quot;&gt;허가가 &lt;b&gt;즉시 가능하면 true&lt;/b&gt;, 아니면 false (비차단, non-blocking)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;246ac551-863b-80ec-bf62-d6b89740e015&quot;&gt;
&lt;td id=&quot;CYie&quot; style=&quot;width: 17.093%;&quot;&gt;boolean&lt;/td&gt;
&lt;td id=&quot;\NcT&quot; style=&quot;width: 30.2326%;&quot;&gt;tryAcquire(long timeout, TimeUnit unit)&lt;/td&gt;
&lt;td id=&quot;jIMR&quot; style=&quot;width: 53.0232%;&quot;&gt;정해진 시간 내 허가 가능하면 true, 불가능하면 false (차단, blocking with timeout)&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;RateLimiter 구현체는 기본적으로 Token Bucket Algorithm을 기반으로 동작합니다. Token Bucket Algorithm은 일정 속도로 토큰을 생성해 버킷에 저장하고, 요청이 들어올 때마다 이 토큰을 소모하는 메커니즘이며 토큰이 충분하면 요청은 즉시 처리되고, 토큰이 부족하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대기하거나 거부합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Token Bucket Algorithm&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;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Token Bucket Algorithm이 필요한 이유&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;비디오와 화상 회의는 delay(지연)과 적은 손실이 보장되어야 합니다.&lt;/li&gt;
&lt;li&gt;비디어/오디오 스트리밍은 약간의 지연은 허용되지만, 패킷 손실률은 최소화되어야 합니다.&lt;/li&gt;
&lt;li&gt;실시간 제어와 같은 중요 애플리케이션은 지연 시간이 매우 중요한 요소입니다.&lt;/li&gt;
&lt;li&gt;가치 있는 애플리케이션은 그렇지 않은 애플리케이션보다 더 나은 네트워크 서비스 품질을 보장해야 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Token Bucket Algorithm의 4가지 특징&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-06 오전 12.34.01.png&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diLMd6/btsPHFMxP2y/O9yYYkZkkozMfTQgWIAsGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diLMd6/btsPHFMxP2y/O9yYYkZkkozMfTQgWIAsGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diLMd6/btsPHFMxP2y/O9yYYkZkkozMfTQgWIAsGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdiLMd6%2FbtsPHFMxP2y%2FO9yYYkZkkozMfTQgWIAsGk%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;557&quot; height=&quot;171&quot; data-filename=&quot;스크린샷 2025-08-06 오전 12.34.01.png&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Reliability (신뢰성)
&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;Delay (지연)
&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;Jitter
&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;Jitter가 클수록 지연이 패킷 간 도달 시간 (Delay)가 크기에 데이터 전송에 대해서 예측 불가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Bandwidth (대역폭)
&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;h3 data-ke-size=&quot;size23&quot;&gt;Token Bucket Algorithm Flow&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-06 오전 12.34.31.png&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;664&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPAhcd/btsPGHD3puh/vm8Zqa7jqjUOZZITKjBnP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPAhcd/btsPGHD3puh/vm8Zqa7jqjUOZZITKjBnP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPAhcd/btsPGHD3puh/vm8Zqa7jqjUOZZITKjBnP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPAhcd%2FbtsPGHD3puh%2Fvm8Zqa7jqjUOZZITKjBnP0%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;659&quot; height=&quot;364&quot; data-filename=&quot;스크린샷 2025-08-06 오전 12.34.31.png&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;664&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Step-1: 버킷 생성&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가상의 버킷이 생성되며, 이 버킷에는 고정된 용량(rate limit)이 할당됩니다. 버킷은 일정 개수의 토큰을 담을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Step-2: 버킷 채우기&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;p data-ke-size=&quot;size16&quot;&gt;Step-3: 요청 수신&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;p data-ke-size=&quot;size16&quot;&gt;Step-4: 토큰 소비&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;p data-ke-size=&quot;size16&quot;&gt;Step-5: 버킷 비우기&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;p data-ke-size=&quot;size16&quot;&gt;Token Bucker Algorithm도 상세하게 알아보았으니, 이제 본격적으로 RateLimiter 구현체를 애플리케이션에 적용해 보겠습니다.&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;2. Rate Limiter 구현 과정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RateLimiterInterceptor.class&lt;/h3&gt;
&lt;pre id=&quot;code_1754408249220&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Component
public class RateLimiterInterceptor implements HandlerInterceptor {

    private static final double REQUESTS_PER_SECOND = 5.0;
    private static final long LOG_INTERVAL = TimeUnit.MINUTES.toMillis(1);

    private final ConcurrentHashMap&amp;lt;String, RateLimiter&amp;gt; rateLimiters = new ConcurrentHashMap&amp;lt;&amp;gt;();
    private final ConcurrentHashMap&amp;lt;String, Long&amp;gt; lastLoggedTime = new ConcurrentHashMap&amp;lt;&amp;gt;();

    @Value(&quot;${rate.limit.enabled:false}&quot;)
    private boolean rateLimitEnabled;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(!rateLimitEnabled) {
            return true;
        }

        String ip = IpUtil.getClientIp(request);
        long now = System.currentTimeMillis();

        if (!isRequestAllowed(ip)) {
            if (isLogIntervalElapsed(ip, now)) log.warn(&quot;Rate limit exceeded. ip: {}&quot;, ip);
            response.sendError(HttpStatus.TOO_MANY_REQUESTS.value(), &quot;Too many requests&quot;);
            return false;
        }

        if (isLogIntervalElapsed(ip, now)) log.info(&quot;Rate limit passed. ip: {}&quot;, ip);
        return true;
    }

    private boolean isRequestAllowed(String ip) {
        RateLimiter rateLimiter = rateLimiters.computeIfAbsent(ip, k -&amp;gt; RateLimiter.create(REQUESTS_PER_SECOND));
        return rateLimiter.tryAcquire();
    }

    private boolean isLogIntervalElapsed(String ip, long now) {
        Long last = lastLoggedTime.get(ip);

        if (last == null || now - last &amp;gt; LOG_INTERVAL) {
            lastLoggedTime.put(ip, now);
            return true;
        }

        return false;
    }
}&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;클라이언트 요청에 대한 API 호출 제한은 비즈니스 로직과 분리되어야 하므로, 컨트롤러에 도달하기 전에 Interceptor에서 RateLimiter를 적용했습니다.&lt;/li&gt;
&lt;li&gt;prod 환경에서만 API 호출 제한을 적용하기 위해 flag를 두었습니다. (rate.limit.enabled)&lt;/li&gt;
&lt;li&gt;사용자별로 초당 5건의 API Call을 호출할 수 있도록 설정하였습니다.&lt;/li&gt;
&lt;li&gt;무수히 많은 요청이 올 경우 반복해서 로그를 찍기 때문에, 로그 출력 간격은 1분으로 지정하였습니다.&lt;/li&gt;
&lt;li&gt;tryAcquire() 메서드 호출을 통해 버킷의 토큰을 반환하였으면 Controller로 요청을 전달하며, 토큰을 반환하지 못할 경우 429 Too Many Requests를 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;IpUtil.class&lt;/h3&gt;
&lt;pre id=&quot;code_1754408613129&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class IpUtil {

    public static String getClientIp(HttpServletRequest request) {
        final String[] CLIENT_IP_HEADER_NAMES = {
                &quot;X-Forwarded-For&quot;,
                &quot;Proxy-Client-IP&quot;,
                &quot;WL-Proxy-Client-IP&quot;,
                &quot;HTTP_CLIENT_IP&quot;,
                &quot;HTTP_X_FORWARDED_FOR&quot;
        };

        for (String header : CLIENT_IP_HEADER_NAMES) {
            String ip = request.getHeader(header);
            if (ip != null &amp;amp;&amp;amp; !ip.isEmpty() &amp;amp;&amp;amp; !&quot;unknown&quot;.equalsIgnoreCase(ip)) {
                log.info(&quot;client ip found in header {}: {}&quot;, header, ip);
                return ip;
            }
        }

        String ip = request.getRemoteAddr();
        log.info(&quot;client ip resolved from request.getRemoteAddr(): {}&quot;, ip);
        return ip;
    }
}&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;클라이언트 IP를 정확하게 추출하기 위한 Util Class&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;WebConfig.class&lt;/h3&gt;
&lt;pre id=&quot;code_1754408336461&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RequiredArgsConstructor
@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final CurrentUserIdArgumentResolver currentUserIdArgumentResolver;
    private final RateLimiterInterceptor rateLimiterInterceptor;

    @Override
    public void addArgumentResolvers(List&amp;lt;HandlerMethodArgumentResolver&amp;gt; resolvers) {
        resolvers.add(currentUserIdArgumentResolver);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(rateLimiterInterceptor)
                .addPathPatterns(&quot;/api/restaurants/search&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;외부 API(Kakao Maps API)를 호출하는 PATH에 rateLimiterInterceptor를 적용합니다.&lt;/li&gt;
&lt;li&gt;추후 외부 API를 다른 경로에서도 호출하게 된다면, addIntercetpors 메서드에 경로를 추가해 주시면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-05 오전 12.35.59.png&quot; data-origin-width=&quot;2328&quot; data-origin-height=&quot;114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bydwDA/btsPGEUSFdQ/osesb9VDXZR2SVREfE6De1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bydwDA/btsPGEUSFdQ/osesb9VDXZR2SVREfE6De1/img.png&quot; data-alt=&quot;Local Test(요청 실패시 warn log, 성공시 info log)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bydwDA/btsPGEUSFdQ/osesb9VDXZR2SVREfE6De1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbydwDA%2FbtsPGEUSFdQ%2Fosesb9VDXZR2SVREfE6De1%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;35&quot; data-filename=&quot;스크린샷 2025-08-05 오전 12.35.59.png&quot; data-origin-width=&quot;2328&quot; data-origin-height=&quot;114&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Local Test(요청 실패시 warn log, 성공시 info log)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Side Project</category>
      <category>API 호출 제한을 위한 RateLimiter 도입</category>
      <category>Guava RateLimiter</category>
      <category>Rate Limiting</category>
      <category>Token Bucket Algorithm</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/414</guid>
      <comments>https://daily1313.tistory.com/entry/Spring-Guava-Library%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-RateLimiter-%EC%A0%81%EC%9A%A9%EA%B8%B0#entry414comment</comments>
      <pubDate>Wed, 6 Aug 2025 00:49:07 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Kakao Maps API로 키워드 기반 식당 검색 기능을 구현해보자</title>
      <link>https://daily1313.tistory.com/entry/Spring-Kakao-Maps-API%EB%A1%9C-%ED%82%A4%EC%9B%8C%EB%93%9C-%EA%B8%B0%EB%B0%98-%EC%8B%9D%EB%8B%B9-%EA%B2%80%EC%83%89-%EA%B8%B0%EB%8A%A5%EC%9D%84-%EA%B5%AC%ED%98%84%ED%95%B4%EB%B3%B4%EC%9E%90</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;친구와 함께 사이드 프로젝트(MatJido)를 진행하면서, Keyword를 입력하여 모든 식당을 조회해야 하는 기능이 필요하였습니다.&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;MatJido라는 프로젝트를 기획하게 된 계기와 위의 기능이 필요한 이유에 대해서 간략하게 설명드리겠습니다.&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;MatJido 기획 계기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MatJido는 Tripple 여행 앱에서 영감을 받아 사용자들과 함께 맛집을 공유하기 위한 웹 서비스입니다. Tripple은 여행 일정과 계획을 상세하게 세우고 다양한 사용자들과 함께 공유할 수 있는 기능을 제공합니다. (이 외에도 많은 기능들이 제공됩니다. (항공권 및 숙박시설 예약, AI 기반 여행지 추천 등과 같은 유용한 기능들도 존재합니다.)&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;더불어, 동기화 기능 또한 매우 뛰어납니다. (사용자들 중 한 명이라도 한 팀에서의 여행 Plan을 수정하게 된다면, 다른 사용자들도 실시간으로 변경된 내용을 확인할 수 있습니다.)&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;우선적으로는 Kakao 소셜 로그인을 통해 로그인한 사용자가 식당을 검색 및 추가할 수 있어야 합니다. (이후, 다른 사용자들과 공유할 수 있습니다.) 이번 포스팅에서는 식당 검색 기능에 대해서 상세히 기록하려고 합니다.&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;다양한 Reference를 조사해본 결과, Kakao에서 제공하는 문서가 가장 상세하고 이해하기 쉬웠기 때문에 Kakao Maps API를 활용하기로 결정하였습니다.&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;참고한 Reference&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;&lt;a href=&quot;https://developers.kakao.com/docs/latest/ko/local/dev-guide#search-by-keyword&quot;&gt;https://developers.kakao.com/docs/latest/ko/local/dev-guide#search-by-keyword&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1752913099498&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;Kakao Developers&quot; data-og-description=&quot;카카오 API를 활용하여 다양한 어플리케이션을 개발해 보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.&quot; data-og-host=&quot;developers.kakao.com&quot; data-og-source-url=&quot;https://developers.kakao.com/docs/latest/ko/local/dev-guide#search-by-keyword&quot; data-og-url=&quot;https://developers.kakao.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uf1HH/hyZnhUVpEH/Cdk8KzFa52rLEBgo6q1Ork/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bNuRBI/hyZnvZRhCZ/uOekXFJMTlw6WfjPDKsOQk/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bwCa8R/hyZjdNjQO6/pye3iasLsgo5J6K55UFUwK/img.png?width=1200&amp;amp;height=800&amp;amp;face=0_0_1200_800&quot;&gt;&lt;a href=&quot;https://developers.kakao.com/docs/latest/ko/local/dev-guide#search-by-keyword&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.kakao.com/docs/latest/ko/local/dev-guide#search-by-keyword&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uf1HH/hyZnhUVpEH/Cdk8KzFa52rLEBgo6q1Ork/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bNuRBI/hyZnvZRhCZ/uOekXFJMTlw6WfjPDKsOQk/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bwCa8R/hyZjdNjQO6/pye3iasLsgo5J6K55UFUwK/img.png?width=1200&amp;amp;height=800&amp;amp;face=0_0_1200_800');&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;Kakao Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;카카오 API를 활용하여 다양한 어플리케이션을 개발해 보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 Reference를 참고하면, API의 요청 방식과 Response 응답 format을 예시와 함께 확인하실 수 있습니다.&lt;b&gt;&lt;/b&gt;&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;해당 PR (소스코드는 해당 PR에서 확인 가능합니다.)&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;&lt;b&gt;&lt;a href=&quot;https://github.com/Dongwoongkim/matjido/pull/16&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/Dongwoongkim/matjido/pull/16&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1752913292732&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;[Feature/#11-restaurants] Kakao Maps API를 활용한 식당 검색 API, 식당, 계층형 카테고리 CRUD 기능 개발  by d&quot; data-og-description=&quot;1. KaKao Maps API를 활용한 키워드 기반 식당 검색 기능 reference: https://developers.kakao.com/docs/latest/ko/local/dev-guide#search-by-keyword 식당 검색 api endpoint (GET: /api/restaurants) Rest API 요청 방식 curl requ...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/Dongwoongkim/matjido/pull/16&quot; data-og-url=&quot;https://github.com/Dongwoongkim/matjido/pull/16&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/LjXCk/hyZjp79c32/fzPOMYMoqlENPSDqPfD3C1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/5inE9/hyZjun5WzB/cv09F6ZDFJ6KF7y3PoEup0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/Dongwoongkim/matjido/pull/16&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/Dongwoongkim/matjido/pull/16&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/LjXCk/hyZjp79c32/fzPOMYMoqlENPSDqPfD3C1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/5inE9/hyZjun5WzB/cv09F6ZDFJ6KF7y3PoEup0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;[Feature/#11-restaurants] Kakao Maps API를 활용한 식당 검색 API, 식당, 계층형 카테고리 CRUD 기능 개발 by d&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. KaKao Maps API를 활용한 키워드 기반 식당 검색 기능 reference: https://developers.kakao.com/docs/latest/ko/local/dev-guide#search-by-keyword 식당 검색 api endpoint (GET: /api/restaurants) Rest API 요청 방식 curl requ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Restaurant (Entity)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;user, category와 N:1 매핑됩니다.&lt;/li&gt;
&lt;li&gt;category class는 이번 포스팅과 연관성이 떨어지기에 참고만 부탁드립니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1752913457560&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
@Entity
public class Restaurant {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = &quot;restaurant_id&quot;)
    private Long id;

    private String placeName;

    @Embedded
    private Address address;

    @Embedded
    private GeoLocation geoLocation;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;category_id&quot;)
    @OnDelete(action = OnDeleteAction.NO_ACTION)
    private Category category;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;member_id&quot;)
    @OnDelete(action = OnDeleteAction.CASCADE)
    private User user;

    @Builder
    private Restaurant(Category category, String placeName, Address address, GeoLocation geoLocation, User user) {
        this.category = category;
        this.placeName = placeName;
        this.address = address;
        this.geoLocation = geoLocation;
        this.user = user;
    }

    public String getCategoryName() {
        return category.getName();
    }

    public Long getCategoryId() {
        return category.getId();
    }

    public String getCity() {
        return address.getCity();
    }

    public String getDistrict() {
        return address.getDistrict();
    }

    public String getRoadName() {
        return address.getRoadName();
    }

    public String getLongitude() {
        return geoLocation.getLongitude();
    }

    public String getLatitude() {
        return geoLocation.getLatitude();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;RestaurantRepository (Repository)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 추가한 식당 정보 단건, 전체조회하기 위한 query가 정의되어 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1752913619377&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface RestaurantRepository extends JpaRepository&amp;lt;Restaurant, Long&amp;gt; {

    List&amp;lt;Restaurant&amp;gt; findAllByUserId(Long userId);

    Page&amp;lt;Restaurant&amp;gt; findAllByUserId(Long userId, Pageable pageable);

    Optional&amp;lt;Restaurant&amp;gt; findByIdAndUserId(Long id, Long userId);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;RestaurantService (Service)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;KakaoSearchApiClient 모듈을 통해 Kakao Maps API을 호출합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;url: &lt;span style=&quot;background-color: #ffffff; color: #444444; text-align: left;&quot;&gt;&lt;a href=&quot;https://dapi.kakao.com/v2/local/search/keyword.$&quot;&gt;https://dapi.kakao.com/v2/local/search/keyword.$&lt;/a&gt;{FORMAT} (GET)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #444444; text-align: left;&quot;&gt;header: &lt;span style=&quot;background-color: #ffffff; color: #444444; text-align: left;&quot;&gt;Authorization: KakaoAK ${REST_API_KEY}&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #444444; text-align: left;&quot;&gt;parameter: query (필수)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1752913774894&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RequiredArgsConstructor
@Transactional(readOnly = true)
@Service
public class RestaurantService {

    private final RestaurantRepository restaurantRepository;
    private final UserRepository userRepository;
    private final CategoryRepository categoryRepository;
    private final KakaoSearchApiClient kakaoSearchApiClient;

    public KakaoPlacesSearchResponse search(String query) {
        return kakaoSearchApiClient.searchRestaurants(query);
    }

    @Transactional
    public void addRestaurant(Long userId, RestaurantCreateRequest restaurantCreateRequest) {
        User user = findUser(userId);

        Category category = categoryRepository.findById(restaurantCreateRequest.categoryId())
                .orElseThrow(() -&amp;gt; new CategoryNotFoundException(restaurantCreateRequest.categoryId()));

        Restaurant restaurant = Restaurant.builder()
                .category(category)
                .placeName(restaurantCreateRequest.placeName())
                .address(Address.from(restaurantCreateRequest.roadAddressName()))
                .geoLocation(GeoLocation.of(restaurantCreateRequest.longitude(), restaurantCreateRequest.latitude()))
                .user(user)
                .build();

        restaurantRepository.save(restaurant);
    }

    public RestaurantsResponse getRestaurantsByUserId(Long userId) {
        List&amp;lt;Restaurant&amp;gt; restaurants = restaurantRepository.findAllByUserId(userId);
        return RestaurantsResponse.from(restaurants);
    }

    public Page&amp;lt;Restaurant&amp;gt; getRestaurantsByUserId(Long userId, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.ASC, &quot;id&quot;));
        return restaurantRepository.findAllByUserId(userId, pageable);
    }

    public RestaurantResponse getRestaurantByUserId(Long userId, Long restaurantId) {
        Restaurant restaurant = findRestaurant(restaurantId, userId);
        return RestaurantResponse.from(restaurant);
    }

    @Transactional
    public void deleteRestaurant(Long userId, Long restaurantId) {
        Restaurant restaurant = findRestaurant(restaurantId, userId);
        restaurantRepository.delete(restaurant);
    }

    private Restaurant findRestaurant(Long restaurantId, Long userId) {
        return restaurantRepository.findByIdAndUserId(restaurantId, userId)
                .orElseThrow(() -&amp;gt; new RestaurantNotFoundException(restaurantId));
    }

    private User findUser(Long userId) {
        return userRepository.findById(userId)
                .orElseThrow(UserNotFoundException::new);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;KakaoSearchApiClient&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 API (Kakao Maps API)를 호출하여 식당 검색 기능을 수행하는 클라이언트 모듈입니다.&lt;/li&gt;
&lt;li&gt;kakao developer에서 발급받은 rest api를 Authorization header에 KakaoAK ${restApiKey} 형식으로 담아줍니다.&lt;/li&gt;
&lt;li&gt;해당 기능을 핵심은 &quot;식당&quot; 검색이기에, 불필요한 Response를 막기 위해 GROUP_CODE는 FD6(음식점)으로 고정하였습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1752914037955&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RequiredArgsConstructor
@Component
public class KakaoSearchApiClient {

    private static final String KEYWORD_SEARCH_URL = &quot;https://dapi.kakao.com/v2/local/search/keyword.json&quot;;
    private static final String RESTAURANT_GROUP_CODE = &quot;FD6&quot;;

    @Value(&quot;${spring.security.oauth2.client.registration.kakao.client-id}&quot;)
    private String restApiKey;

    public KakaoPlacesSearchResponse searchRestaurants(String query) {
        try {
            String uri = UriComponentsBuilder.fromUriString(KEYWORD_SEARCH_URL)
                    .queryParam(&quot;query&quot;, query)
                    .queryParam(&quot;category_group_code&quot;, RESTAURANT_GROUP_CODE)
                    .build()
                    .toUriString();

            RestClient restClient = RestClient.builder()
                    .defaultHeader(&quot;Authorization&quot;, &quot;KakaoAK &quot; + restApiKey)
                    .build();

            KakaoPlacesSearchResponse response = restClient.get()
                    .uri(uri)
                    .retrieve()
                    .body(KakaoPlacesSearchResponse.class);

            return response;

        } catch (RestClientException e) {
            log.error(&quot;exception: {}&quot;, e.getMessage(), e);
            throw new RestClientException(e.getMessage());
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;RestaurantController&lt;/h4&gt;
&lt;pre id=&quot;code_1752914899570&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RequestMapping(&quot;/api/restaurants&quot;)
@RequiredArgsConstructor
@RestController
public class RestaurantController {

    private final RestaurantService restaurantService;

    @GetMapping(&quot;/search&quot;)
    public ResponseEntity&amp;lt;KakaoPlacesSearchResponse&amp;gt; searchRestaurants(@RequestParam(&quot;query&quot;) String query) {
        return ResponseEntity.ok(restaurantService.search(query));
    }

    @PostMapping
    public ResponseEntity&amp;lt;Void&amp;gt; addRestaurant(@RequestBody @Valid RestaurantCreateRequest restaurantCreateRequest,
                                              @CurrentUserId Long userId) {
        restaurantService.addRestaurant(userId, restaurantCreateRequest);
        return ResponseEntity.status(HttpStatus.CREATED)
                .build();
    }

    @GetMapping
    public ResponseEntity&amp;lt;RestaurantsResponse&amp;gt; getRestaurantsByUserId(@CurrentUserId Long userId) {
        return ResponseEntity.ok(restaurantService.getRestaurantsByUserId(userId));
    }

    @GetMapping
    public ResponseEntity&amp;lt;RestaurantsPaginatedResponse&amp;gt; getRestaurantsByUserId(
            @CurrentUserId Long userId,
            @RequestParam(defaultValue = &quot;0&quot;) int page,
            @RequestParam(defaultValue = &quot;10&quot;) int size
    ) {
        Page&amp;lt;Restaurant&amp;gt; restaurants = restaurantService.getRestaurantsByUserId(userId, page, size);
        return ResponseEntity.ok(RestaurantsPaginatedResponse.from(restaurants));
    }

    @GetMapping(&quot;/{restaurantId}&quot;)
    public ResponseEntity&amp;lt;RestaurantResponse&amp;gt; getRestaurantByUserId(@CurrentUserId Long userId,
                                                                    @PathVariable Long restaurantId) {
        return ResponseEntity.ok(restaurantService.getRestaurantByUserId(userId, restaurantId));
    }

    @DeleteMapping(&quot;/{restaurantId}&quot;)
    public ResponseEntity&amp;lt;Void&amp;gt; deleteRestaurant(@CurrentUserId Long userId,
                                                 @PathVariable Long restaurantId) {
        restaurantService.deleteRestaurant(userId, restaurantId);

        return ResponseEntity.status(HttpStatus.NO_CONTENT)
                .build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;KaKao Maps Rest API Request&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;referrence를 보시면 필수 입력값은 query 뿐이기에 해당 파라미터만 받아주고 GROUP_CODE는 고정해 주었습니다.&lt;/li&gt;
&lt;li&gt;쿼리 파라미터로 다양한 값을 받을 수 있기에 레퍼런스를 참고하시면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CtjIJ/btsPpvDIp5R/2vs7RdvickHIvHikN3kSgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CtjIJ/btsPpvDIp5R/2vs7RdvickHIvHikN3kSgk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;1410&quot; data-filename=&quot;스크린샷 2025-07-19 오후 5.40.47.png&quot; style=&quot;width: 35.7013%; margin-right: 10px;&quot; data-widthpercent=&quot;36.12&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CtjIJ/btsPpvDIp5R/2vs7RdvickHIvHikN3kSgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCtjIJ%2FbtsPpvDIp5R%2F2vs7RdvickHIvHikN3kSgk%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;1404&quot; height=&quot;1410&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4l1Ev/btsPpx2PEXV/VKjkKIuVKmEjJ4msyZJl5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4l1Ev/btsPpx2PEXV/VKjkKIuVKmEjJ4msyZJl5K/img.png&quot; data-filename=&quot;스크린샷 2025-07-19 오후 5.41.13.png&quot; data-origin-height=&quot;778&quot; data-origin-width=&quot;1370&quot; data-is-animation=&quot;false&quot; style=&quot;width: 63.1359%;&quot; data-widthpercent=&quot;63.88&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4l1Ev/btsPpx2PEXV/VKjkKIuVKmEjJ4msyZJl5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4l1Ev%2FbtsPpx2PEXV%2FVKjkKIuVKmEjJ4msyZJl5K%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;1370&quot; height=&quot;778&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;https://developers.kakao.com/docs/latest/ko/local/dev-guide#search-by-keyword&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;h4 data-ke-size=&quot;size20&quot;&gt;Kakao Maps Rest API Response&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-07-19 오후 5.41.31.png&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;1516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m65U8/btsPqNcbaUO/CVf7KwGjOUMp7dHjzMA5G0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m65U8/btsPqNcbaUO/CVf7KwGjOUMp7dHjzMA5G0/img.png&quot; data-alt=&quot;https://developers.kakao.com/docs/latest/ko/local/dev-guide#search-by-keyword&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m65U8/btsPqNcbaUO/CVf7KwGjOUMp7dHjzMA5G0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm65U8%2FbtsPqNcbaUO%2FCVf7KwGjOUMp7dHjzMA5G0%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;476&quot; height=&quot;542&quot; data-filename=&quot;스크린샷 2025-07-19 오후 5.41.31.png&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;1516&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://developers.kakao.com/docs/latest/ko/local/dev-guide#search-by-keyword&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;h4 data-ke-size=&quot;size20&quot;&gt;KakaoPlacesSearchResponse&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;keyword 기반 search시의 Response 형식과 완전히 일치해야 하기에 반환형식을 맞춰주었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-07-19 오후 5.46.11.png&quot; data-origin-width=&quot;1862&quot; data-origin-height=&quot;1468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yITvM/btsPqWfTd5Y/cqAZpwKYyML1FnoS7DrGIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yITvM/btsPqWfTd5Y/cqAZpwKYyML1FnoS7DrGIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yITvM/btsPqWfTd5Y/cqAZpwKYyML1FnoS7DrGIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyITvM%2FbtsPqWfTd5Y%2FcqAZpwKYyML1FnoS7DrGIK%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;430&quot; data-filename=&quot;스크린샷 2025-07-19 오후 5.46.11.png&quot; data-origin-width=&quot;1862&quot; data-origin-height=&quot;1468&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;h4 data-ke-size=&quot;size20&quot;&gt;API Test (Postman)&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GROUP_CODE를 FD6으로 고정하였기에 식당 정보만 반환되는 것을 확인하실 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-07-19 오후 5.51.24.png&quot; data-origin-width=&quot;2474&quot; data-origin-height=&quot;1372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YkLHq/btsPqx1LGAA/Jmu8cKHB5LzDDrv02Qk4p1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YkLHq/btsPqx1LGAA/Jmu8cKHB5LzDDrv02Qk4p1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YkLHq/btsPqx1LGAA/Jmu8cKHB5LzDDrv02Qk4p1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYkLHq%2FbtsPqx1LGAA%2FJmu8cKHB5LzDDrv02Qk4p1%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;535&quot; height=&quot;297&quot; data-filename=&quot;스크린샷 2025-07-19 오후 5.51.24.png&quot; data-origin-width=&quot;2474&quot; data-origin-height=&quot;1372&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;</description>
      <category>Side Project</category>
      <category>Kakao Maps API</category>
      <category>spring boot kakao maps api</category>
      <category>카카오 지도 api</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/413</guid>
      <comments>https://daily1313.tistory.com/entry/Spring-Kakao-Maps-API%EB%A1%9C-%ED%82%A4%EC%9B%8C%EB%93%9C-%EA%B8%B0%EB%B0%98-%EC%8B%9D%EB%8B%B9-%EA%B2%80%EC%83%89-%EA%B8%B0%EB%8A%A5%EC%9D%84-%EA%B5%AC%ED%98%84%ED%95%B4%EB%B3%B4%EC%9E%90#entry413comment</comments>
      <pubDate>Sat, 19 Jul 2025 17:54:49 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] application.yml과 docker-compose로 dev/prod 환경 분리</title>
      <link>https://daily1313.tistory.com/entry/Spring-applicationyml%EA%B3%BC-docker-compose%EB%A1%9C-devprod-%ED%99%98%EA%B2%BD-%EB%B6%84%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서 개발환경과 배포환경이 달라짐에 따라서 설정 파일을 분리해야 하는 경우가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker로 애플리케이션 환경을 구성할 경우에는 local에서는 db (redis, mariadb..)만 컨테이너로 구성하고, production 환경에서는 RDS와 같은 외부 db 서버를 따로 구축하지 않는다고 가정하고, spring boot까지 함께 DockerFile을 통해 빌드하고 띄울 수 있습니다.&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, docker-compose 파일을 분리하는 과정에 대해 설명드리겠습니다.&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;Architecture&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tEFIi/btsOXIIYnd4/PDGTqkMxtq6qvkGvxq0tzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tEFIi/btsOXIIYnd4/PDGTqkMxtq6qvkGvxq0tzk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;618&quot; data-filename=&quot;스크린샷 2025-06-29 오후 6.50.31.png&quot; style=&quot;width: 43.1083%; margin-right: 10px;&quot; data-widthpercent=&quot;43.62&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tEFIi/btsOXIIYnd4/PDGTqkMxtq6qvkGvxq0tzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtEFIi%2FbtsOXIIYnd4%2FPDGTqkMxtq6qvkGvxq0tzk%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;1012&quot; height=&quot;618&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caU2RP/btsOXSq4JA3/mzBTEt6lVoR7otqgi8MFlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caU2RP/btsOXSq4JA3/mzBTEt6lVoR7otqgi8MFlK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;684&quot; data-filename=&quot;스크린샷 2025-06-29 오후 6.50.41.png&quot; style=&quot;width: 55.729%;&quot; data-widthpercent=&quot;56.38&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caU2RP/btsOXSq4JA3/mzBTEt6lVoR7otqgi8MFlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaU2RP%2FbtsOXSq4JA3%2FmzBTEt6lVoR7otqgi8MFlK%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;1448&quot; height=&quot;684&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;docker-compose로 애플리케이션 환경 구성도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker-compose-dev.yml
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mysql, redis만 container로 구성합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;외부로 노출되서는 안 되는 db 접속정보, 외부 포트 정보가. env 파일을 docker-compose를 통해 컨테이너에 주입합니다.&lt;/li&gt;
&lt;li&gt;spring boot는 개발용 ide에서 직접 띄웁니다. (8080)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;docker-compose-prod.yml
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mysql, redis를 container로 구성합니다. (RDS 사용 X)&lt;/li&gt;
&lt;li&gt;이후, 사전에 정의해둔 DockerFile을 통해 Spring boot를 빌드하고 컨테이너로 실행합니다.&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;docker-compose는 다음과 같이 구성할 수 있습니다. 이제 애플리케이션 환경 설정인 application.yml 파일을 dev/prod 환경별로 분리하는 과정에 대해 설명드리겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application 환경 설정 파일을 분리한 이유는 logging level 설정, db ddl-auto 옵션, show-sql 설정 등을 서로 다르게 설정하기 위함입니다.&amp;nbsp;&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-06-29 오후 7.26.22.png&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crITbd/btsOYciDAED/uvWR9R0J6A3muYfkkiqHbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crITbd/btsOYciDAED/uvWR9R0J6A3muYfkkiqHbk/img.png&quot; data-alt=&quot;application.yml, application-prod.yml, application-prod.yml&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crITbd/btsOYciDAED/uvWR9R0J6A3muYfkkiqHbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrITbd%2FbtsOYciDAED%2FuvWR9R0J6A3muYfkkiqHbk%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;628&quot; height=&quot;250&quot; data-filename=&quot;스크린샷 2025-06-29 오후 7.26.22.png&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;application.yml, application-prod.yml, application-prod.yml&lt;/figcaption&gt;
&lt;/figure&gt;
&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;application.yml&lt;/h4&gt;
&lt;pre id=&quot;code_1751192582696&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  profiles:
    include: secret
    active: dev # aws ec2: prod

  jasypt:
    encryptor:
      bean: jasyptStringEncryptor
      password: ${ENCRYPT_KEY}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;application-dev.yml&lt;/h4&gt;
&lt;pre id=&quot;code_1751192614065&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server:
  port: 8080

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: ENC(/4FdgNloaye9GbvbjOrPwN7NIuSu+5z8/CSWWtwJ8Vo2XeblFzhh9gEJA5htEucA)
    username: ENC(+Okc+NKtnZ7JVPyUPCLquw==)
    password: ENC(xl4yVJDdT9stSX6hlaQu407NOQ51scZ1)

  data:
    redis:
      host: ENC(WG74MTOrnoy3Bg6997kecMSy9HJ5OkPy)
      port: 6379

  jpa:
    open-in-view: false
    show-sql: true
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        format_sql: true
        naming:
          physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    database: mysql
    database-platform: org.hibernate.dialect.MySQL8Dialect
    defer-datasource-initialization: true

  logging:
    level:
      root: INFO&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;application-prod.yml&lt;/h4&gt;
&lt;pre id=&quot;code_1751192639338&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server:
  port: 443

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: # prod url
    username: # prod username
    password: # prod password

  data:
    redis:
      host: ENC(nNs4NX4Zttm/oCIPCLVHJw==)
      port: 6379

  jpa:
    open-in-view: false
    show-sql: false
    hibernate:
      ddl-auto: none
    properties:
      hibernate:
        format_sql: false
        naming:
          physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    database: mysql
    database-platform: org.hibernate.dialect.MySQL8Dialect
    defer-datasource-initialization: true

  logging:
    level:
      root: WARN&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;application.yml: 공통으로 적용되는 환경설정 값과 jasypt (db 프로퍼티 암호화 라이브러리) key 값이 담겨있습니다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 파일에서 spring.profiles.active 설정을 통해 개발(dev) 또는 운영(prod) 환경에 맞는 설정 파일을 선택적으로 적용할 수 있습니다. (application-dev.yml or application-prod.yml)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Side Project</category>
      <category>application-dev.yml</category>
      <category>application-prod.yml</category>
      <category>application.yml 분리</category>
      <category>docker-compose-dev.yml</category>
      <category>docker-compose-prod.yml</category>
      <category>docker-compose.yml</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/412</guid>
      <comments>https://daily1313.tistory.com/entry/Spring-applicationyml%EA%B3%BC-docker-compose%EB%A1%9C-devprod-%ED%99%98%EA%B2%BD-%EB%B6%84%EB%A6%AC#entry412comment</comments>
      <pubDate>Sun, 29 Jun 2025 19:45:11 +0900</pubDate>
    </item>
    <item>
      <title>[MariaDB] MariaDB Client/Server 관점에서의 LOAD DATA INFILE vs LOAD DATA LOCAL INFILE 명령어 비교</title>
      <link>https://daily1313.tistory.com/entry/MariaDB-MariaDB-ClientServer-%EA%B4%80%EC%A0%90%EC%97%90%EC%84%9C%EC%9D%98-LOAD-DATA-INFILE-vs-LOAD-DATA-LOCAL-INFILE-%EB%AA%85%EB%A0%B9%EC%96%B4-%EB%B9%84%EA%B5%90</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-06-22 오후 11.49.59.png&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p1HG2/btsOMFMyUMi/BaZ8qi6wbBMdT09L75qEpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p1HG2/btsOMFMyUMi/BaZ8qi6wbBMdT09L75qEpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p1HG2/btsOMFMyUMi/BaZ8qi6wbBMdT09L75qEpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp1HG2%2FbtsOMFMyUMi%2FBaZ8qi6wbBMdT09L75qEpK%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;514&quot; height=&quot;311&quot; data-filename=&quot;스크린샷 2025-06-22 오후 11.49.59.png&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;506&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;매우 빠른 시간 안에 csv 파일을 db에 import 하기 위한 명령어인 LOAD DATA INFILE 명령어에서 LOCAL 키워드를 붙여야 하는 상황과 붙이지 않아도 되는 상황에 대해서 알아보려고 합니다.&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;LOCAL 키워드 필요 유무를 알기 위해서는 DB Client와 Server에 대해 숙지하고 있어야 합니다. 본론으로 넘어가기 전에 DB Client/Server의 개념에 대해 간략하게 먼저 설명드리겠습니다.&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;MariaDB Server/Client&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MariaDB Server: DB를 실제로 실행하고 데이터를 저장/관리하는 프로그램 (mysqld 프로세스)&lt;/li&gt;
&lt;li&gt;MariaDB Client: DB 서버에 접속해서 명령어(SQL)를 보내는 프로그램 (mysql, mariadb, JDBC, Workbench)&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;mysql -u{username} -p {password} 와 같이 명령을 통해 Server에 접속하기 위한 매개체는 Client가 되고, 실제 서버에서 mysqld 프로세스를 통해 CRUD 쿼리를 날리기 위한 매개체는 Server가 됩니다.&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;LOAD DATA INFILE과 LOAD DATA LOCAL INFILE은 Client, Server의 역할에 따라 결정됩니다.&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;MariaDB 공식 문서에 따르면 LOCAL 키워드를 명시하는 경우와 그렇지 않은 경우에 대해 다음과 같이 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/load-data.html&quot;&gt;https://dev.mysql.com/doc/refman/8.0/en/load-data.html&lt;/a&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;input File Location These rules determine the LOAD DATA input file location:&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로컬 키워드를 명시하지 않을 경우 (LOCAL X)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;If LOCAL is not specified, the file must be located on the server host. The server reads the file directly, locating it as follows:&lt;br /&gt;If the file name is an absolute path name, the server uses it as given.&lt;br /&gt;If the file name is a relative path name with leading components, the server looks for the file relative to its data directory.&lt;br /&gt;If the file name has no leading components, the server looks for the file in the database directory of the default database.&lt;/blockquote&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;li&gt;파일 경로가 절대 경로라면, 서버가 그 경로를 그대로 접근합니다.&lt;/li&gt;
&lt;li&gt;파일 경로가 상대 경로고 디렉터리 경로가 포함되어 있다면, 자신의 데이터 디렉터리를 기준으로 경로를 해석합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로컬 키워드를 명시할 경우 (LOCAL O)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;If LOCAL is specified, the file must be located on the client host. The client program reads the file, locating it as follows:&lt;br /&gt;If the file name is an absolute path name, the client program uses it as given.&lt;br /&gt;If the file name is a relative path name, the client program looks for the file relative to its invocation directory.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트가 파일을 읽어서 서버로 전송합니다. (client &amp;rarr; sever)&lt;/li&gt;
&lt;li&gt;local이 붙으면, 파일은 클라이언트에 있어야 합니다.&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;h4 data-ke-size=&quot;size20&quot;&gt;LOAD DATA INFILE vs LOAD DATA LOCAL INFILE&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LOAD DATA INFILE: Maria DB 서버가 직접 파일을 읽습니다.&lt;/li&gt;
&lt;li&gt;LOAD DATA LOCAL INFILE: 클라이언트 프로그램이 파일을 읽고, 서버에게 전송합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table id=&quot;21aac551-863b-8046-a7bc-e85c513e0b92&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr id=&quot;21aac551-863b-8047-ab51-d1a8190b9def&quot;&gt;
&lt;td id=&quot;VR@~&quot;&gt;구분&lt;/td&gt;
&lt;td id=&quot;Gt|A&quot;&gt;누가 파일을 읽냐&lt;/td&gt;
&lt;td id=&quot;vew;&quot;&gt;파일은 어디에 있어야 하는가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;21aac551-863b-80a6-85bc-e49ee4ebc554&quot;&gt;
&lt;td id=&quot;VR@~&quot;&gt;LOAD DATA INFILE&lt;/td&gt;
&lt;td id=&quot;Gt|A&quot;&gt;서버(mysqld)&lt;/td&gt;
&lt;td id=&quot;vew;&quot;&gt;서버 머신의 디스크&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;21aac551-863b-80fa-8c76-f2aeab238676&quot;&gt;
&lt;td id=&quot;VR@~&quot;&gt;LOAD DATA LOCAL INFILE&lt;/td&gt;
&lt;td id=&quot;Gt|A&quot;&gt;클라이언트 (mysql, CLI, 애플리케이션)&lt;/td&gt;
&lt;td id=&quot;vew;&quot;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 LOCAL 키워드를 언제 사용해야 하는지에 대한 감은 잡히지만, 실제 사용 여부는 어떤 환경에서 명령을 실행하느냐에 따라 달라질 수 있습니다. 다양한 환경에서의 예를 들어 하나씩 설명드리겠습니다.&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. 서버 안에서 접속해서 실행 (SSH 접속 등) (클라이언트, 서버가 한 머신에 모두 들어간 경우)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ssh로 서버에 접속한 경우, MariaDB-Client, Server가 한 머신에 들어가 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750605325539&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;[root@localhost project]# rpm -qa | grap -i MariaDB
MariaDB-common-10.3.39-1.el8.x86_64
MariaDB-shared-10.3.39-1.el8.x86_64
MariaDB-client-10.3.39-1.el8.x86_64
MariaDB-server-10.3.39-1.el8.x86_64&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;ssh ubuntu@서버주소
mysql -u root -p

LOAD DATA INFILE '/path/file.csv' INTO TABLE my_table;
&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;파일도 서버에 있으니까 LOCAL 명령어가 필요 없습니다.&lt;/li&gt;
&lt;li&gt;MySQL 클라이언트가 DB 서버와 같은 머신에서 실행되기 때문입니다.
&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;파일이 서버에 존재하면, LOCAL을 제거해줘도 상관없고, 붙여도 상관없습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LOCAL 붙일 경우: mysql --local-infile=1 -u root -p (&amp;mdash;local-infile=1 옵션 필수)&lt;/li&gt;
&lt;li&gt;클라이언트가 파일을 읽고 서버로 전송합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 로컬 PC에서 실행 (CLI 또는 Workbench, JDBC) (클라이언트, 서버가 한 머신에 모두 들어가 있지 않은 경우)&lt;/h4&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;mysql -h 서버주소 -u user -p
LOAD DATA LOCAL INFILE 'file.csv' INTO TABLE my_table;&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;이때는 클라이언트 = PC가 됩니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;파일도 PC에 있으니까 LOCAL이 반드시 필요합니다.&lt;/li&gt;
&lt;li&gt;서버에는 파일이 없기 때문에 Local 없이 하면 실패합니다.&lt;/li&gt;
&lt;li&gt;클라이언트가 파일을 읽어와 서버로 전송합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. DB Cluster 구조에서 load data 명령어를 사용할 경우 (LOCAL 키워드 사용 권장, ChatGPT 참고)&lt;/h4&gt;
&lt;pre id=&quot;code_1750605713089&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;┌────────────┐         ┌────────────┐
│ App Server │ ─────►  │ DB Cluster │
└────────────┘         └────────────┘
                             ▲
                ┌────────────┴────────────────┐
                │DB 1 (Node-1, Primary/Master)│
                │DB 2 (Node-2, Replica/Slave) │
                │DB 3 (Node-3, Replica/Slave) │            │           
                └─────────────────────────────┘
 
           [App Server]
                │
        ┌───────▼────────┐
        │   DB Cluster   │  &amp;larr;  하나처럼 보이지만 실제론 여러 DB 서버가 동작
        └─────┬──────┬───┘
              │      │
         [Node 1] [Node 2]  &amp;larr; MariaDB/MySQL 서버들 (Primary/Replica 등)&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;LOAD DATA LOCAL INFILE 'file.csv' INTO TABLE my_table;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;App이 직접 file.csv을 열어서 binary로 파일을 읽은 후 연결된 DB에 전송합니다.&lt;/li&gt;
&lt;li&gt;클러스터 구조와 무관하게 항상 안전하게 동작합니다.&lt;/li&gt;
&lt;li&gt;노드 선택 불확실성 제거: LOCAL은 클라이언트가 데이터를 직접 전송하므로 실행 노드가 중요하지 않습니다.&lt;/li&gt;
&lt;li&gt;클러스터 환경 이식성 확보: App만 있으면, 어떤 노드와 연결되든 성공 가능성이 올라갑니다.&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mariadb.com/docs/server/reference/sql-statements/data-manipulation/inserting-loading-data/load-data-into-tables-or-index/load-data-infile&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://mariadb.com/docs/server/reference/sql-statements/data-manipulation/inserting-loading-data/load-data-into-tables-or-index/load-data-infile&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1750603730646&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;LOAD DATA INFILE | MariaDB Documentation&quot; data-og-description=&quot;&quot; data-og-host=&quot;mariadb.com&quot; data-og-source-url=&quot;https://mariadb.com/docs/server/reference/sql-statements/data-manipulation/inserting-loading-data/load-data-into-tables-or-index/load-data-infile&quot; data-og-url=&quot;https://mariadb.com/docs/server/reference/sql-statements/data-manipulation/inserting-loading-data/load-data-into-tables-or-index/load-data-infile&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://mariadb.com/docs/server/reference/sql-statements/data-manipulation/inserting-loading-data/load-data-into-tables-or-index/load-data-infile&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mariadb.com/docs/server/reference/sql-statements/data-manipulation/inserting-loading-data/load-data-into-tables-or-index/load-data-infile&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;LOAD DATA INFILE | MariaDB Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;mariadb.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/load-data.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dev.mysql.com/doc/refman/8.0/en/load-data.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1750603758496&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 :: MySQL 8.0 Reference Manual :: 15.2.9 LOAD DATA Statement&quot; data-og-description=&quot;15.2.9&amp;nbsp;LOAD DATA Statement LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'file_name' [REPLACE | IGNORE] INTO TABLE tbl_name [PARTITION (partition_name [, partition_name] ...)] [CHARACTER SET charset_name] [{FIELDS | COLUMNS} [TERMINATED BY 'string'&quot; data-og-host=&quot;dev.mysql.com&quot; data-og-source-url=&quot;https://dev.mysql.com/doc/refman/8.0/en/load-data.html&quot; data-og-url=&quot;https://dev.mysql.com/doc/refman/8.0/en/load-data.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/load-data.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dev.mysql.com/doc/refman/8.0/en/load-data.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 :: MySQL 8.0 Reference Manual :: 15.2.9 LOAD DATA Statement&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;15.2.9&amp;nbsp;LOAD DATA Statement LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'file_name' [REPLACE | IGNORE] INTO TABLE tbl_name [PARTITION (partition_name [, partition_name] ...)] [CHARACTER SET charset_name] [{FIELDS | COLUMNS} [TERMINATED BY 'string'&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Database</category>
      <category>load data infile</category>
      <category>load data local infile</category>
      <category>mariadb client/server</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/411</guid>
      <comments>https://daily1313.tistory.com/entry/MariaDB-MariaDB-ClientServer-%EA%B4%80%EC%A0%90%EC%97%90%EC%84%9C%EC%9D%98-LOAD-DATA-INFILE-vs-LOAD-DATA-LOCAL-INFILE-%EB%AA%85%EB%A0%B9%EC%96%B4-%EB%B9%84%EA%B5%90#entry411comment</comments>
      <pubDate>Mon, 23 Jun 2025 00:25:12 +0900</pubDate>
    </item>
    <item>
      <title>[Linux] 리눅스 마스터 2급 합격후기 및 공부법 (전공자)</title>
      <link>https://daily1313.tistory.com/entry/Linux-%EB%A6%AC%EB%88%85%EC%8A%A4-%EB%A7%88%EC%8A%A4%ED%84%B0-2%EA%B8%89-%ED%95%A9%EA%B2%A9%ED%9B%84%EA%B8%B0-%EB%B0%8F-%EA%B3%B5%EB%B6%80%EB%B2%95-%EC%A0%84%EA%B3%B5%EC%9E%90</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 오랜만에 리눅스 마스터 2급 자격증 후기로 포스팅을 작성하려고 합니다.&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-06-16 오전 1.09.21.png&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;1246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6WxUj/btsODjbnbJf/EwuS3dvnmQf1dSxvvnqji0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6WxUj/btsODjbnbJf/EwuS3dvnmQf1dSxvvnqji0/img.png&quot; data-alt=&quot;https://www.ihd.or.kr/introducesubject1.do&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6WxUj/btsODjbnbJf/EwuS3dvnmQf1dSxvvnqji0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6WxUj%2FbtsODjbnbJf%2FEwuS3dvnmQf1dSxvvnqji0%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;642&quot; height=&quot;479&quot; data-filename=&quot;스크린샷 2025-06-16 오전 1.09.21.png&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;1246&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.ihd.or.kr/introducesubject1.do&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-06-16 오전 1.15.46.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1018&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ECkpm/btsOBS0mBOo/lJzh3dX5UkgpSUVmKQQKmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ECkpm/btsOBS0mBOo/lJzh3dX5UkgpSUVmKQQKmk/img.png&quot; data-alt=&quot;리눅스 마스터 2급 2차 과목 구성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ECkpm/btsOBS0mBOo/lJzh3dX5UkgpSUVmKQQKmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FECkpm%2FbtsOBS0mBOo%2FlJzh3dX5UkgpSUVmKQQKmk%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;402&quot; data-filename=&quot;스크린샷 2025-06-16 오전 1.15.46.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1018&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;리눅스 마스터 2급 2차 과목 구성&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;프로젝트 배포 환경의 OS가 Linux이고, 업무 시에 종종 Linux 명령어를 다뤄야 하는 경우가 있습니다. 그래서 관련 지식도 터득할 겸 준비하게 되었습니다.&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;IT 관련 전공자이지만, Linux OS 관련하여 기본적으로 가지고 있는 사전 지식은 전무한 상태였습니다. (netstat, ps, cd, ls, chmod, tar, df .. 등 기초적인 지식만 아는 상태)&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;어떠한 방식으로 공부했는지 1, 2차로 나눠서 설명드리겠습니다.&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;1차&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-06-16 오전 1.17.06.png&quot; data-origin-width=&quot;1598&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Yn7wK/btsOBbTEuEI/Za3BQlXkj6998zPaXA4Ptk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Yn7wK/btsOBbTEuEI/Za3BQlXkj6998zPaXA4Ptk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Yn7wK/btsOBbTEuEI/Za3BQlXkj6998zPaXA4Ptk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYn7wK%2FbtsOBbTEuEI%2FZa3BQlXkj6998zPaXA4Ptk%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;1598&quot; height=&quot;152&quot; data-filename=&quot;스크린샷 2025-06-16 오전 1.17.06.png&quot; data-origin-width=&quot;1598&quot; data-origin-height=&quot;152&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;1차는 온라인 시험으로서 오픈북 시험이며, 정해진 기간에 60분 동안 50문제를 풀어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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/0Qj1n/btsOAIxtzd5/J3LQfKdd3wctWWVrX7jNSk/%E1%84%85%E1%85%B5%E1%84%82%E1%85%AE%E1%86%A8%E1%84%89%E1%85%B3%20%E1%84%86%E1%85%A1%E1%84%89%E1%85%B3%E1%84%90%E1%85%A5%202%E1%84%80%E1%85%B3%E1%86%B8%201%E1%84%8E%E1%85%A1%20%E1%84%8C%E1%85%A9%E1%86%A8%E1%84%87%E1%85%A9.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;리눅스 마스터 2급 1차 족보.pdf&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.65MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&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;서칭을 통해 해당 자료를 기반으로 chrome 환경에서 시험을 응시하였습니다. 위에 첨부해둔 리눅스 일반에 대한 내용이 많았으며 관련 명령어를 ctrl + f로 검색하면서 손쉽게 합격할 수 있었습니다.&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차 시험 공부법 (오픈북 시험)&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ChatGPT AI Tool을 통해 검색하거나 모르는 문제를 검색해서 해답을 찾습니다.&lt;/li&gt;
&lt;li&gt;족보를 기반으로 겹치는 문제가 있는지 확인합니다.&lt;/li&gt;
&lt;li&gt;조금 애매하거나 확신이 가지 않는 문제는 구글링을 통해 관련 정보를 찾으면 됩니다.&lt;/li&gt;
&lt;/ol&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차 공부법 및 공부기간 (책 X)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1차에 합격한 직후, 바로 2차 시험을 결제했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시험 장소와 세부 정보는 2차 시험 비용 결제할 때 원하는 구역(서울, 경기..)을 지정하면, 시험 날짜 일주일 전에 개별적으로 알려줍니다. (저는 집에서 1시간 거리의 공업고등학교에서 응시하였습니다.(서울))&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공부 기간은 5일정도 투자했던 것 같습니다. (매일 3시간 이상 -&amp;gt; 대략 15시간 투자, 전공자 기준)&lt;/li&gt;
&lt;li&gt;웹사이트에 관련 족보와 정리본이 있지만, 해당 자료를 보지 않고 개인적으로 공부하였습니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Notion에 모의고사를 4개년치(2020 ~ 2023)를 정리하고, 시험 하루 전날에는 cbt 사이트를 통한 모의고사를 응시하였습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차 공부 방법에 대해 더욱 상세하게 설명드리겠습니다. 개인적인 공부 방법이고 개개인마다 방식이 다를 수도 있으니 참고만 해주시면 좋을 것 같습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1WERh/btsOAWCuFYz/vZXLCG80CTnPYEWr1KBNCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1WERh/btsOAWCuFYz/vZXLCG80CTnPYEWr1KBNCk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;1386&quot; data-filename=&quot;스크린샷 2025-06-16 오전 1.25.56.png&quot; width=&quot;556&quot; height=&quot;718&quot; style=&quot;width: 47.4498%; margin-right: 10px;&quot; data-widthpercent=&quot;48.01&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1WERh/btsOAWCuFYz/vZXLCG80CTnPYEWr1KBNCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1WERh%2FbtsOAWCuFYz%2FvZXLCG80CTnPYEWr1KBNCk%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;1074&quot; height=&quot;1386&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rCCnP/btsOCP9nkOl/gdsKjFBUrF3wpX3b41AFok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rCCnP/btsOCP9nkOl/gdsKjFBUrF3wpX3b41AFok/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1002&quot; data-origin-height=&quot;1194&quot; data-filename=&quot;스크린샷 2025-06-16 오전 1.26.16.png&quot; style=&quot;width: 51.3874%;&quot; data-widthpercent=&quot;51.99&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rCCnP/btsOCP9nkOl/gdsKjFBUrF3wpX3b41AFok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrCCnP%2FbtsOCP9nkOl%2FgdsKjFBUrF3wpX3b41AFok%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;1002&quot; height=&quot;1194&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;notion에 4개년치 모의고사 정리 및 오답정리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;4개년치 모의고사에 대해 답안이 있는 시험지를 눈으로 확인합니다. (더 많이 봐도 상관없습니다.)&lt;/li&gt;
&lt;li&gt;cbt 사이트 (&lt;a href=&quot;https://www.comcbt.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.comcbt.com/&lt;/a&gt;)에서 해설지를 함께 보면서 관련 개념을 정리합니다. (위의 사진과 같이 관련 내용을 간략하게 정리하고 출퇴근 시 종종 보았습니다.)&lt;/li&gt;
&lt;li&gt;문제를 풀다가 다른 년도나 회차의 시험을 볼 때 정리해둔 개념을 확인하고, 시험지에 해당 문제를 체크해 줍니다. (빈번해서 자주 나오는 문제인 경우, 다음 회차에 나올 가능성이 높다고 생각했습니다)&amp;nbsp;&lt;/li&gt;
&lt;li&gt;마지막 날은 모의고사를 다시 풀어보고, 틀리거나 헷갈린 개념 및 문제에 대해 정리를 해줍니다. (시험 당일에 점검하기 위해서 정리)&amp;nbsp;&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;개인적인 생각으로는 책을 사서 공부하는 것도 지식 함양에 있어서는 도움이 되겠지만, 자격증 취득이 목적이신 분들한테는 모의고사 정리를 추천드립니다. 대략 50% 이상 시험 문제가 겹쳐서 (완전 동일하게는 많이 안 나왔던 것 같습니다) 나오며, 나머지 문제들은 개념 정리한 것을 기반으로 커버가 되는 수준으로 나왔습니다. 난이도는 평소 모의고사보다는 조금 어려운 정도였던 것 같습니다.&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;합격 여부는 7/4에 나오게 되는데, 나오게 된다면 합격증을 올리도록 하겠습니다.&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>Certificate</category>
      <category>리눅스 마스터 2급 2차</category>
      <category>리눅스 마스터 2급 취득 후기</category>
      <category>리눅스 마스터 난이도</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/410</guid>
      <comments>https://daily1313.tistory.com/entry/Linux-%EB%A6%AC%EB%88%85%EC%8A%A4-%EB%A7%88%EC%8A%A4%ED%84%B0-2%EA%B8%89-%ED%95%A9%EA%B2%A9%ED%9B%84%EA%B8%B0-%EB%B0%8F-%EA%B3%B5%EB%B6%80%EB%B2%95-%EC%A0%84%EA%B3%B5%EC%9E%90#entry410comment</comments>
      <pubDate>Mon, 16 Jun 2025 23:59:36 +0900</pubDate>
    </item>
    <item>
      <title>[Elasticsearch] ES cluster(2 node) 구조에서의 데이터 정합성 이슈</title>
      <link>https://daily1313.tistory.com/entry/Elasticsearch-ES-cluster2-node-%EA%B5%AC%EC%A1%B0%EC%97%90%EC%84%9C%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%95%ED%95%A9%EC%84%B1-%EC%9D%B4%EC%8A%88</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-06-06 오후 6.34.31.png&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bArE01/btsOsPhxIHy/QILEyuHoQFTDE2PD9Dtjx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bArE01/btsOsPhxIHy/QILEyuHoQFTDE2PD9Dtjx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bArE01/btsOsPhxIHy/QILEyuHoQFTDE2PD9Dtjx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbArE01%2FbtsOsPhxIHy%2FQILEyuHoQFTDE2PD9Dtjx1%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;373&quot; height=&quot;200&quot; data-filename=&quot;스크린샷 2025-06-06 오후 6.34.31.png&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사 프로젝트에서 장비의 실시간 통계 정보를 저장하기 위해 Elasticsearch 검색엔진을 활용합니다.&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;단일 node를 가지는 standalone 구조에서는 primary, replica shard가 각각 1, 0으로 세팅되어 있으며, 클러스터 구조(2 node 이상) 에서는 5, 1로 세팅되어 있습니다. (ES 1.7.x 기준이며 템플릿 설정에 따라 다를 수 있습니다.)&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;Failover를 위한 HA 구조 (ES 1(master), ES 2(backup) 에서 데이터의 정합성 이슈가 발생하였습니다. 해당 이슈에 대해 대략적인 내용을 설명드리겠습니다.&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;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;장비의 CPU, Memory, Port Usage, Connectivity 정보와 같은 통계 정보를 일정 주기에 맞춰서 ES에 저장합니다. (40 ~ 60s 주기)&lt;/li&gt;
&lt;li&gt;해당 정보가 제대로 저장이 되지 않으며, cpu, memory, connectivity 등의 그래프가 간헐적으로 0을 찍는 톱니바퀴 모양으로 생성됩니다. (원래 정상 동작이라면, -자를 표시해야 합니다)&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사내 시스템에서의 Aggregation 주기는 1m이기에 1m 주기로 데이터의 집계를 내린다면, 0을 표시하는 지점이 없어야 합니다. (데이터 저장 주기가 40s ~ 60s 이기 때문)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;standalone에서는 문제가 발현되지 않으며, cluster 구조에서만 발생하였습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-06-06 오후 5.35.32.png&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;796&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuEQD4/btsOrViJtTu/mzkZF0GBdGJ4Nt4GhSsKl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuEQD4/btsOrViJtTu/mzkZF0GBdGJ4Nt4GhSsKl1/img.png&quot; data-alt=&quot;master node, backup node, vip 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuEQD4/btsOrViJtTu/mzkZF0GBdGJ4Nt4GhSsKl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuEQD4%2FbtsOrViJtTu%2FmzkZF0GBdGJ4Nt4GhSsKl1%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;402&quot; height=&quot;369&quot; data-filename=&quot;스크린샷 2025-06-06 오후 5.35.32.png&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;796&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;master node, backup node, vip 구조&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;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 분석 과정&amp;nbsp;&lt;/h3&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. cluster의 각 node 상태 확인 (master node만 on이고, backup node는 off인 상태)&lt;/h4&gt;
&lt;pre id=&quot;code_1749201013274&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -s http://192.168.0.1:9200/_cat/health?v
epoch      timestamp cluster      status node.total node.data shards pri relo init unassign pending_tasks 
1742977690 17:28:10  cluster-1    yellow          1         1     45  45    0    0       45             0&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;해당 정보를 확인한 후, 특징 인덱스의 shards를 확인하니 당연히 Replica shards가 할당되지 않았습니다. (backup node가 off이므로)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749201189996&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl -XGET http://192.168.0.1:9200/_cat/shards/{index-name}?v
index  shard prirep state        docs   store ip          node                   
       4     p      STARTED    524256 201.9mb 172.16.0.65 {node-name}
       4     r      UNASSIGNED
       0     p      STARTED    524045   198mb 172.16.0.65 {node-name}
       0     r      UNASSIGNED
       3     p      STARTED    523872 209.8mb 172.16.0.65 {node-name}
       3     r      UNASSIGNED
       1     p      STARTED    523931 210.1mb 172.16.0.65 {node-name}
       1     r      UNASSIGNED
       2     p      STARTED    523949 199.1mb 172.16.0.65 {node-name}
       2     r      UNASSIGNED&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;h4 data-ke-size=&quot;size20&quot;&gt;2. Master Node의 인덱스 상태 확인 (yellow)&lt;/h4&gt;
&lt;pre id=&quot;code_1749200536334&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl -s http://192.168.0.1:9200/_cat/indices?v
health status index             pri rep docs.count docs.deleted store.size pri.store.size 
yellow open   index-1            5   1     118019            0     31.4mb         31.4mb 
yellow open   index-2            5   1          0            0       720b           720b 
yellow open   index-3            5   1    2310298            0      854mb          854mb 
yellow open   index-4            5   1          0            0       720b           720b 
yellow open   index-5            5   1     233948            0     15.7mb         15.7mb 
yellow open   index-6            5   1      64575            0       16mb           16mb 
yellow open   index-7            5   1     137727            0     32.9mb         32.9mb 
yellow open   index-8            5   1      24885            0      3.9mb          3.9mb 
yellow open   index-9            5   1         10            0     54.4kb         54.4kb&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;master node의 상태가 yellow라는 점에서 의심스러운 부분이 많았지만, yellow 상태는 인덱스의 쓰기/읽기에는 문제가 없는 상태입니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;ES Node Status
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;green: 모든 shards가 정상적으로 동작하고 있는 상태&lt;/li&gt;
&lt;li&gt;yellow: 일부 혹은 모든 replica shards가 정상적으로 동작하고 있는 상태, primary shards에 문제가 생기면 데이터 유실 발생 위험이 존재합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;red: 일부 혹은 모든 인덱스의 primary와 replica shards가 정상적으로 동작하고 있지 않은 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. Split Brain 의심&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;ES cluster 구조에서 split brain이라는 개념을 이해하기 위해선 master, data node에 대해 숙지하고 있어야 합니다.&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;master node는 인덱스의 메타 데이터, 샤드의 위치와 같은 클러스터의 상태를 관리하는 노드이고, data node는 실제로 데이터를 저장하고 있는 노드입니다.&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;elasticsearch.yml 파일의 node.master, node.data 설정을 통해 이를 구분하여 각자의 노드에 맞게 역할 설정을 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(물론, 하나의 노드가 master node, data node의 두 가지 역할을 모두 담당할 수 있습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;node.master의 설정의 기본 값은 true인데, 일반적으로 모든 node는 master node로 선출될 수 있는 마스터 후보 노드입니다. 즉, 하나의 master node가 문제가 생기면, 이를 대체하기 위해 마스터 후보 노드가 master node로 대체됩니다.&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개의 node로 구성된 cluster 구조라서, split brain 현상을 의심하였습니다.&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;Split Brain&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크 단절로 마스터 후보 노드 (node-1, node-2)가 분리되면서 서로 다른 클러스터로 동작할 경우에 발생하는 문제
&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;최소한의 백업용 마스터 후보 노드를 3개 이상의 홀수개를 놓는 것을 권장합니다.&lt;/li&gt;
&lt;li&gt;2개 or 짝수로 운영하는 경우 네트워크 유실로 인한 split brain 문제가 발생할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Elasticsearch 6.x 이전 버전에서의 마스터 후보 노드 설정 방법&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;discovery.zen.minimum_master_nodes&lt;/li&gt;
&lt;li&gt;(전체 마스터 후보 노드 / 2) + 1개로 설정하는 것을 권장&lt;/li&gt;
&lt;li&gt;7.0 부터는 node.master: ture인 노드가 추가되면, 스스로 minimum_master_nodes 값을 변경하도록 설정됩니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단, 최초 마스터 후보로 선출할 cluster.initial_master_nodes: [] 설정이 필요&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;근본적인 문제 해결 방법을 찾고 싶었지만, backup node의 Elasticsearch를 재구동하여 일시적으로 문제를 해결한 상태입니다. 의심스러운 문제나 좋은 해결 방안이 있으면 댓글 부탁드립니다.&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://brunch.co.kr/@alden/43&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://brunch.co.kr/@alden/43&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1749201349085&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;ElasticSearch status 바로 알기&quot; data-og-description=&quot;ElasticSearch | 오늘은 ElasticSearch (이하 ES)의 status 에 대한 이야기를 해볼까 합니다. ES의 status는 무엇을 의미하는지, 그리고 어떤 값들이 있으며 어떻게 확인할 수 있는지 살펴보겠습니다. ES status &quot; data-og-host=&quot;brunch.co.kr&quot; data-og-source-url=&quot;https://brunch.co.kr/@alden/43&quot; data-og-url=&quot;https://brunch.co.kr/@alden/43&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cNl7ww/hyY4aaZrLk/q4dgtj3TXWDsu3zDoM6ML0/img.png?width=938&amp;amp;height=246&amp;amp;face=0_0_938_246,https://scrap.kakaocdn.net/dn/BFNPq/hyY360Ilye/KYCoPkG0kNT41OaZp6pSrK/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/cI4Dag/hyY48jBqo0/yl6aETnnRk8LKKnnktjYu1/img.png?width=938&amp;amp;height=246&amp;amp;face=0_0_938_246&quot;&gt;&lt;a href=&quot;https://brunch.co.kr/@alden/43&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://brunch.co.kr/@alden/43&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cNl7ww/hyY4aaZrLk/q4dgtj3TXWDsu3zDoM6ML0/img.png?width=938&amp;amp;height=246&amp;amp;face=0_0_938_246,https://scrap.kakaocdn.net/dn/BFNPq/hyY360Ilye/KYCoPkG0kNT41OaZp6pSrK/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/cI4Dag/hyY48jBqo0/yl6aETnnRk8LKKnnktjYu1/img.png?width=938&amp;amp;height=246&amp;amp;face=0_0_938_246');&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;ElasticSearch status 바로 알기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ElasticSearch | 오늘은 ElasticSearch (이하 ES)의 status 에 대한 이야기를 해볼까 합니다. ES의 status는 무엇을 의미하는지, 그리고 어떤 값들이 있으며 어떻게 확인할 수 있는지 살펴보겠습니다. ES status&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;brunch.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Database</category>
      <category>ElasticSearch</category>
      <category>elasticsearch cluster 구조</category>
      <category>es 2 node 데이터 정합성 이슈</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/409</guid>
      <comments>https://daily1313.tistory.com/entry/Elasticsearch-ES-cluster2-node-%EA%B5%AC%EC%A1%B0%EC%97%90%EC%84%9C%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%95%ED%95%A9%EC%84%B1-%EC%9D%B4%EC%8A%88#entry409comment</comments>
      <pubDate>Fri, 6 Jun 2025 19:02:45 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] csv 파일의 데이터 파싱 및 저장 성능 개선기 (JPA save() vs JdbcTemplate batchUpdate() vs MariaDB LOAD DATA INFILE)</title>
      <link>https://daily1313.tistory.com/entry/Spring-csv-%ED%8C%8C%EC%9D%BC%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%8C%8C%EC%8B%B1-%EB%B0%8F-%EC%A0%80%EC%9E%A5-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%EA%B8%B0-JPA-save-vs-JdbcTemplate-batchUpdate-vs-MariaDB-LOAD-DATA-INFILE</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;운영 중인 사내 시스템에서 &lt;a href=&quot;https://standards-oui.ieee.org/oui/oui.txt&quot; data-end=&quot;238&quot; data-start=&quot;181&quot;&gt;IEEE OUI 파일&lt;/a&gt;(&lt;a href=&quot;https://standards-oui.ieee.org/oui/oui.txt&quot;&gt;https://standards-oui.ieee.org/oui/oui.txt&lt;/a&gt;)을 다운로드하여 MAC 주소와 제조사 정보를 추출하고, 이를 DB에 저장해야 하는 요구사항이 있었습니다.&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;기존 처리 흐름 (AS-IS)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;oui.txt file -&amp;gt; oui.csv 파일 형식으로 파싱합니다.&lt;/li&gt;
&lt;li&gt;csv 파일을 BufferedReader로 읽어서 JPA save() 반복 호출로 저장합니다.&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;즉, 애플리케이션 런타임 시점에 csv 파일을 oui, vendor 정보로 파싱하여 일일이 save() 메서드를 호출하는 방식이었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 해당 방식은 매우 성능이 낮으며 이에 대한 첫 번째 대안으로는 saveAll() 메서드를 활용할 수 있습니다.&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;먼저, JPA에서 제공하는 save(), saveAll() 메서드의 차이를 알아보고, 핵심 내용인 JdbcTemplate의 batchUpdate() 메서드도 save()와의 성능 비교를 통해서 상세히 설명드리도록 하겠습니다.&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAHhSM/btsN7Bdktxe/twLB8KsPAZj7sVNTjkikNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAHhSM/btsN7Bdktxe/twLB8KsPAZj7sVNTjkikNk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;452&quot; data-filename=&quot;스크린샷 2025-05-22 오전 12.34.30.png&quot; style=&quot;width: 51.3327%; margin-right: 10px;&quot; data-widthpercent=&quot;51.94&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAHhSM/btsN7Bdktxe/twLB8KsPAZj7sVNTjkikNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAHhSM%2FbtsN7Bdktxe%2FtwLB8KsPAZj7sVNTjkikNk%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;1018&quot; height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1ciID/btsN7Wn1OW5/w6kIGSOvq4iofQOIvKZGik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1ciID/btsN7Wn1OW5/w6kIGSOvq4iofQOIvKZGik/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1138&quot; data-origin-height=&quot;546&quot; data-filename=&quot;스크린샷 2025-05-22 오전 12.33.23.png&quot; width=&quot;456&quot; height=&quot;219&quot; data-widthpercent=&quot;48.06&quot; style=&quot;width: 47.5045%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1ciID/btsN7Wn1OW5/w6kIGSOvq4iofQOIvKZGik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1ciID%2FbtsN7Wn1OW5%2Fw6kIGSOvq4iofQOIvKZGik%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;1138&quot; height=&quot;546&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;save() vs saveAll()&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;save(): 내부적으로 EntityManager를 통해 신규 엔티티면, persist() (비영속 -&amp;gt; 영속) 메서드를 호출하고, 영속성 컨텍스트에서 관리된 엔티티면 merge() (준영속 -&amp;gt; 영속) 메서드를 호출합니다.&lt;/li&gt;
&lt;li&gt;saveAll(): 하나의 트랜잭션에서 save() 메서드를 반복해서 호출합니다.&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;같은 클래스에서 save() 메서드를 내부적으로 호출하는 방식이므로 프록시 로직을 타지 않습니다. 그래서 saveAll()이 성능적으로 당연히 뛰어나다고 말할 수 있습니다.&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;하지만, JPA의 save()와 saveAll() 방식은 모두 쿼리가 하나씩 나가는 단건 삽입 방식입니다. (saveAll()도 마찬가지로 메서드 내부에서 save() 메서드를 호출하기에 쿼리 발생 방식은 save()와 동일하게 단건 삽입 방식입니다.)&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;반면에, 대량의 데이터를 저장할 때 Insert 쿼리가 한번 발생하는 방식인 Bulk Insert 방식은 위 두 방식 (JPA save(), saveAll())보다 훨씬 성능이 뛰어나다고 볼 수 있습니다. 따라서, 성능 개선을 위해 JdbcTemplate.batchUpdate() 메서드를 적용하여 다량의 Insert 쿼리를 한번에 처리하도록 변경하였습니다.&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;Bulk Insert 쿼리&lt;/h4&gt;
&lt;pre id=&quot;code_1747920084019&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;INSERT INTO common_mac_to_vendor (mac, vendor) VALUES (?, ?), parameters ['10:C2:BA:00:00:00','UTT Co. Ltd.'],['00:08:13:00:00:00','Diskbank Inc.'],['00:06:74:00:00:00','Spectrum Control Inc.'],['00:02:C6:00:00:00','Data Track Technology PLC'],['88:15:C5:00:00:00','Huawei Device Co. Ltd.'],['58:BA:D3:00:00:00','NANJING CASELA TECHNOLOGIES&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;Hibernate에서 기본키 생성 전략이 GenerationType.IDENTITY (AUTO_INCREMENT)인 경우, batch insert가 비활성화된다고 나와있습니다. 영속성 컨텍스트 내부에서 엔티티를 식별할 때는 엔티티 타입과 id 값으로 식별하지만, IDENTITY에서는 insert 쿼리를 실행해야만 id 값을 확인할 수 있기에 Batch Insert를 비활성화 한다고 나와있습니다.&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Whenever an entity is persisted, Hibernate must attach it to the currently running Persistence Context which acts as a Map of entities. The Map key is formed of the entity type (its Java Class) and the entity identifier.&lt;/p&gt;
&lt;p style=&quot;color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;For IDENTITY columns, the only way to know the identifier value is to execute the SQL INSERT. Hence, the INSERT is executed when the persist method is called and cannot be disabled until flush time.&lt;/p&gt;
&lt;p style=&quot;color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;For this reason, Hibernate disables JDBC batch inserts for entities using the IDENTITY generator strategy.&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;기본키 생성 전략이 Identity가 아니라면, batch_size 조정을 통해 Hibernate의 Batch Insert를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1747921212842&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring.jpa.properties.hibernate.jdbc.batch_size=100&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;첫 번째 개선: JdbcTemplate.batchUpdate() (TO-BE) (AS-IS와 비교해 16배 개선)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 개의 DB 업데이트 작업 (insert, update) 명령을 한 번에 묶어서 처리하기 위한 메서드&lt;/li&gt;
&lt;li&gt;많은 양의 데이터를 DB에 삽입하는 작업인 Bulk Insert 방식으로 동작합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;관련 설정&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;spring.datasource.url: rewriteBatchedStatements = true (MySQL)&lt;/li&gt;
&lt;li&gt;MariaDB Driver는 rewriteBatchStatements 속성을 확인하고, useBatchMultiSend 여부를 판단하여 Batch Insert 작업을 수행하는데 useBatchMultiSend 속성의 default 값이 true이기에 별도 설정이 필요 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot;&gt;Stops checking if every INSERT statement contains the &quot;ON DUPLICATE KEY UPDATE&quot; clause. As a side effect, obtaining the statement's generated keys information will return a list where normally it would not. Also be aware that, in this case, the list of generated keys returned may not be accurate. The effect of this property is canceled if set simultaneously with &quot;rewriteBatchedStatements=true&quot;.&lt;/span&gt;&lt;span style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot;&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;h4 data-ke-size=&quot;size20&quot;&gt;batchUpdate() 메서드&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1747921590635&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void bulkInsert(List&amp;lt;CommonMacToVendor&amp;gt; commonMacToVendors) {
    String sql = &quot;INSERT INTO common_mac_to_vendor (mac, vendor) VALUES (?, ?)&quot;;

    jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
        public void setValues(PreparedStatement ps, int i) throws SQLException {
            CommonMacToVendor v = commonMacToVendors.get(i);
            ps.setString(1, v.getMac());
            ps.setString(2, v.getVendor());
        }

        public int getBatchSize() {
            return commonMacToVendors.size();
        }
    });
}&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;h4 data-ke-size=&quot;size20&quot;&gt;37000 rows 기준: save() vs batchUpdate() 데이터 삽입 수행 시간 비교&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;1. save() 호출 시, 데이터 삽입 Total 소요 시간: 24s&lt;/p&gt;
&lt;pre id=&quot;code_1747921714316&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;05-21 10:41:10.192  INFO 14992 [task-scheduler-7] c.p.c.a.c.s.CommonMacToVendorService    :135 - total save time - 24589 ms (약 24s 소요)&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;2. batchUpdate() 호출 시, 데이터 삽입 Total 소요 시간 - 1.5s (AS-IS에 비해 약 16배 개선)&lt;/p&gt;
&lt;pre id=&quot;code_1747921831631&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;05-21 10:50:16.125  INFO 35236 [task-scheduler-9] c.p.c.a.c.s.CommonMacToVendorService    :137 - total save time - 1576 ms (약 1.5s 소요)&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;JPA의 기본 save 메서드를 다음 insert 쿼리가 계속 발생하지만, JDBC Batch Insert는 다음과 같이 Insert 쿼리 한번 발생합니다. 그래서 save()와 비교했을 때, 약 16배 개선된 것을 확인할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;두 번째 개선: LOAD DATA INFILE (TO-BE) (AS-IS와 비교해 약 42배 개선)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;csv file을 DB에 import 하기 위한 쿼리&lt;/li&gt;
&lt;li&gt;csv file을 읽어와 한번에 insert 쿼리를 발생하기에 성능이 매우 뛰어납니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;명령어&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LOAD DATA INFILE 'file_name': 입력할 파일의 경로&lt;/li&gt;
&lt;li&gt;INTO TABLE 'table_name': 입력받을 테이블의 이름&lt;/li&gt;
&lt;li&gt;FIELDS: 라인 내의 필드들을 구분하는 방법&lt;/li&gt;
&lt;li&gt;TERMINATED BY ',': 각 필드가 끝나는 구분문자&lt;/li&gt;
&lt;li&gt;LINES -- 각 라인을 구분하는 방법&lt;/li&gt;
&lt;li&gt;TERMINATED BY '\n' -- 각 라인이 끝나는 구분문자&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;쿼리문 1: MariaDB(MySQL) 서버에서 적용 시&lt;/h4&gt;
&lt;pre id=&quot;code_1747923244288&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LOAD DATA INFILE '[csv path]'
INTO TABLE '[table name]'
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
IGNORE 0 ROWS
(col1, col2 ..);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;쿼리문 2: Local에서 적용 시&lt;/h4&gt;
&lt;pre id=&quot;code_1747923137193&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- MySQL, MariaDB CLI 접속
mysql --local-infile=1 -u[username] -p[password] schema;

LOAD DATA LOCAL INFILE '[csv path]'
INTO TABLE '[table name]'
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
IGNORE 0 ROWS
(col1, col2 ..);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해당 쿼리 실행 시 37000 rows 일괄 insert 하는 경우, 1s 안에 삽입이 되는 것을 확인할 수 있습니다.&lt;/h4&gt;
&lt;pre id=&quot;code_1747923616120&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MariaDB [cproject]&amp;gt; LOAD DATA INFILE '/cproject/sql/csv/MACToVendor.csv'
    -&amp;gt; INTO TABLE common_mac_to_vendor
    -&amp;gt; FIELDS TERMINATED BY ','
    -&amp;gt; LINES TERMINATED BY '\n'
    -&amp;gt; IGNORE 0 ROWS
    -&amp;gt; (mac, vendor);
Query OK, 37832 rows affected (0.978 sec)
Records: 37832  Deleted: 0  Skipped: 0  Warnings: 0&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.oneschema.co/blog/import-csv-mysql&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.oneschema.co/blog/import-csv-mysql&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1747922218735&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;How to Import CSV into MySQL&quot; data-og-description=&quot;This article explores five methods to import CSV files into MySQL, from simple one-time imports to more complex programmatic data ingestion.&quot; data-og-host=&quot;www.oneschema.co&quot; data-og-source-url=&quot;https://www.oneschema.co/blog/import-csv-mysql&quot; data-og-url=&quot;https://www.oneschema.co/blog/import-csv-mysql&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/DAvFD/hyYU8etAe1/YlXS6HSp2VHVL63MD5qX91/img.png?width=1600&amp;amp;height=800&amp;amp;face=0_0_1600_800,https://scrap.kakaocdn.net/dn/cnnNRQ/hyYWRvX2RS/z8qOfs4epa4Ox7mtKc8tY0/img.png?width=1600&amp;amp;height=800&amp;amp;face=0_0_1600_800,https://scrap.kakaocdn.net/dn/cRZhDq/hyYYDqmrXG/aY5OFk0arQ0TosJUwKK9oK/img.png?width=1480&amp;amp;height=906&amp;amp;face=0_0_1480_906&quot;&gt;&lt;a href=&quot;https://www.oneschema.co/blog/import-csv-mysql&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.oneschema.co/blog/import-csv-mysql&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/DAvFD/hyYU8etAe1/YlXS6HSp2VHVL63MD5qX91/img.png?width=1600&amp;amp;height=800&amp;amp;face=0_0_1600_800,https://scrap.kakaocdn.net/dn/cnnNRQ/hyYWRvX2RS/z8qOfs4epa4Ox7mtKc8tY0/img.png?width=1600&amp;amp;height=800&amp;amp;face=0_0_1600_800,https://scrap.kakaocdn.net/dn/cRZhDq/hyYYDqmrXG/aY5OFk0arQ0TosJUwKK9oK/img.png?width=1480&amp;amp;height=906&amp;amp;face=0_0_1480_906');&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;How to Import CSV into MySQL&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This article explores five methods to import CSV files into MySQL, from simple one-time imports to more complex programmatic data ingestion.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.oneschema.co&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://piaojian.tistory.com/61&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://piaojian.tistory.com/61&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1747922225777&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[MySQL] Error 2068 _데이터 로드 오류&quot; data-og-description=&quot;오류 유형 [MySQL] ERROR 2068 (HY000) : LOAD DATA LOCAL INFILE file request rejected due to restrictions on access. 오류 설명 MySQL에 외부데이터를 넣으려고 할 때 발생하는 에러 csv 파일을 DB 안에 넣으려고 할 때 발생 &quot; data-og-host=&quot;piaojian.tistory.com&quot; data-og-source-url=&quot;https://piaojian.tistory.com/61&quot; data-og-url=&quot;https://piaojian.tistory.com/61&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/mDsLl/hyYW3JUDuh/hmRwFK2h1Yofyql2lYHT7K/img.png?width=745&amp;amp;height=219&amp;amp;face=0_0_745_219,https://scrap.kakaocdn.net/dn/dRmkPZ/hyYYGUUPxw/Kk45euRWRu0LVrXCLBnX1k/img.png?width=745&amp;amp;height=219&amp;amp;face=0_0_745_219&quot;&gt;&lt;a href=&quot;https://piaojian.tistory.com/61&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://piaojian.tistory.com/61&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/mDsLl/hyYW3JUDuh/hmRwFK2h1Yofyql2lYHT7K/img.png?width=745&amp;amp;height=219&amp;amp;face=0_0_745_219,https://scrap.kakaocdn.net/dn/dRmkPZ/hyYYGUUPxw/Kk45euRWRu0LVrXCLBnX1k/img.png?width=745&amp;amp;height=219&amp;amp;face=0_0_745_219');&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] Error 2068 _데이터 로드 오류&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;오류 유형 [MySQL] ERROR 2068 (HY000) : LOAD DATA LOCAL INFILE file request rejected due to restrictions on access. 오류 설명 MySQL에 외부데이터를 넣으려고 할 때 발생하는 에러 csv 파일을 DB 안에 넣으려고 할 때 발생&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;piaojian.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@hyunho058/JdbcTemplate-batchUpdate%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-Bulk-Insert#jpa-saveall%EA%B3%BC-jdbctemplate-batchupdate-%EC%84%B1%EB%8A%A5-%EB%B9%84%EA%B5%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@hyunho058/JdbcTemplate-batchUpdate%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-Bulk-Insert#jpa-saveall%EA%B3%BC-jdbctemplate-batchupdate-%EC%84%B1%EB%8A%A5-%EB%B9%84%EA%B5%90&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1747922240850&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;JdbcTemplate batchUpdate()를 활용한 Bulk Insert&quot; data-og-description=&quot;use case는 다음과 같습니다. 관리자가 공연을 등록할 때 공연장 좌석 정보 데이터도 함께 등록이 되는 상황입니다.위 코드를 보면 jpa에서 제공해주는 saveAll() 을 활용하여, 좌석 정보를 저장 하는 &quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@hyunho058/JdbcTemplate-batchUpdate%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-Bulk-Insert#jpa-saveall%EA%B3%BC-jdbctemplate-batchupdate-%EC%84%B1%EB%8A%A5-%EB%B9%84%EA%B5%90&quot; data-og-url=&quot;https://velog.io/@hyunho058/JdbcTemplate-batchUpdate를-활용한-Bulk-Insert&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pXwyi/hyYWTm1GFa/xXCGFYzUBSL1bpOzdwQk6K/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/q2KWQ/hyYW6UeJg8/pGlQnUczwueF1203aUiQ61/img.jpg?width=945&amp;amp;height=999&amp;amp;face=0_0_945_999,https://scrap.kakaocdn.net/dn/blDLdI/hyYW1ZGfji/YZWY0M4OBKfUK3AmrCkOq0/img.png?width=318&amp;amp;height=206&amp;amp;face=0_0_318_206&quot;&gt;&lt;a href=&quot;https://velog.io/@hyunho058/JdbcTemplate-batchUpdate%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-Bulk-Insert#jpa-saveall%EA%B3%BC-jdbctemplate-batchupdate-%EC%84%B1%EB%8A%A5-%EB%B9%84%EA%B5%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@hyunho058/JdbcTemplate-batchUpdate%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-Bulk-Insert#jpa-saveall%EA%B3%BC-jdbctemplate-batchupdate-%EC%84%B1%EB%8A%A5-%EB%B9%84%EA%B5%90&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pXwyi/hyYWTm1GFa/xXCGFYzUBSL1bpOzdwQk6K/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/q2KWQ/hyYW6UeJg8/pGlQnUczwueF1203aUiQ61/img.jpg?width=945&amp;amp;height=999&amp;amp;face=0_0_945_999,https://scrap.kakaocdn.net/dn/blDLdI/hyYW1ZGfji/YZWY0M4OBKfUK3AmrCkOq0/img.png?width=318&amp;amp;height=206&amp;amp;face=0_0_318_206');&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;JdbcTemplate batchUpdate()를 활용한 Bulk Insert&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;use case는 다음과 같습니다. 관리자가 공연을 등록할 때 공연장 좌석 정보 데이터도 함께 등록이 되는 상황입니다.위 코드를 보면 jpa에서 제공해주는 saveAll() 을 활용하여, 좌석 정보를 저장 하는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://zoetechlog.tistory.com/90&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://zoetechlog.tistory.com/90&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1747923331479&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[MySQL, MariaDB] csv 파일을 DB 테이블로 Import&quot; data-og-description=&quot;엑셀로 정리된 파일을 mariadb(mysql)에 한번에 insert 하기위한 방법을 정리해보려고 한다. 간단하게 순서를 적어보자면, 1. 엑셀파일의 데이터 정리 - insert 될 데이터만 남기고 열 이름은 삭제한다. 2&quot; data-og-host=&quot;zoetechlog.tistory.com&quot; data-og-source-url=&quot;https://zoetechlog.tistory.com/90&quot; data-og-url=&quot;https://zoetechlog.tistory.com/90&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cGHL8g/hyYWYV9DcO/eI70oojZL9fQIrmkiKAbn1/img.png?width=800&amp;amp;height=129&amp;amp;face=0_0_800_129,https://scrap.kakaocdn.net/dn/qH529/hyYU2rOm5g/4ZAoFiK2VrgJOYh1lgG1lK/img.png?width=800&amp;amp;height=129&amp;amp;face=0_0_800_129,https://scrap.kakaocdn.net/dn/bQOLbC/hyYU7fz8DB/8Ig9wREf4B3A3b1B36FAL1/img.png?width=1518&amp;amp;height=376&amp;amp;face=0_0_1518_376&quot;&gt;&lt;a href=&quot;https://zoetechlog.tistory.com/90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://zoetechlog.tistory.com/90&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cGHL8g/hyYWYV9DcO/eI70oojZL9fQIrmkiKAbn1/img.png?width=800&amp;amp;height=129&amp;amp;face=0_0_800_129,https://scrap.kakaocdn.net/dn/qH529/hyYU2rOm5g/4ZAoFiK2VrgJOYh1lgG1lK/img.png?width=800&amp;amp;height=129&amp;amp;face=0_0_800_129,https://scrap.kakaocdn.net/dn/bQOLbC/hyYU7fz8DB/8Ig9wREf4B3A3b1B36FAL1/img.png?width=1518&amp;amp;height=376&amp;amp;face=0_0_1518_376');&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, MariaDB] csv 파일을 DB 테이블로 Import&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;엑셀로 정리된 파일을 mariadb(mysql)에 한번에 insert 하기위한 방법을 정리해보려고 한다. 간단하게 순서를 적어보자면, 1. 엑셀파일의 데이터 정리 - insert 될 데이터만 남기고 열 이름은 삭제한다. 2&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;zoetechlog.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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>Backend/Spring</category>
      <category>Bulk Insert</category>
      <category>jdbctemplate batchupdate()</category>
      <category>load data infile</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/408</guid>
      <comments>https://daily1313.tistory.com/entry/Spring-csv-%ED%8C%8C%EC%9D%BC%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%8C%8C%EC%8B%B1-%EB%B0%8F-%EC%A0%80%EC%9E%A5-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%EA%B8%B0-JPA-save-vs-JdbcTemplate-batchUpdate-vs-MariaDB-LOAD-DATA-INFILE#entry408comment</comments>
      <pubDate>Thu, 22 May 2025 23:21:48 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java] 6장. 열거 타입과 애너테이션</title>
      <link>https://daily1313.tistory.com/entry/Effective-Java-6%EC%9E%A5-%EC%97%B4%EA%B1%B0-%ED%83%80%EC%9E%85%EA%B3%BC-%EC%95%A0%EB%84%88%ED%85%8C%EC%9D%B4%EC%85%98</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;222&quot; data-origin-height=&quot;337&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsEsCS/btsN6szSL7g/6CkV7Qi4qg1NdKfYOSC6U0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsEsCS/btsN6szSL7g/6CkV7Qi4qg1NdKfYOSC6U0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsEsCS/btsN6szSL7g/6CkV7Qi4qg1NdKfYOSC6U0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsEsCS%2FbtsN6szSL7g%2F6CkV7Qi4qg1NdKfYOSC6U0%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;222&quot; height=&quot;337&quot; data-origin-width=&quot;222&quot; data-origin-height=&quot;337&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[item-34] int 상수 대신 열거 타입을 사용하라&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;enum type 나오기 전에는 정수 열거 패턴(int enum pattern)을 사용해 왔습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747731516151&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static final int APPLE_FUJI = 0;
public static final int APPLE_PIPPIN = 1;

public static final int ORANGE_NEVEL = 0;
public static final int ORANGE_TEMPLE = 1;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;위의 대안으로 나온 것이 열거 타입입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;열거 타입&lt;/h4&gt;
&lt;pre id=&quot;code_1747731607686&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public enum Apple {
    FUJI, PIPPIN, GRANNY_SMITH
}
public enum Orange {
    NAVEL, TEMPLE, BLOOD
}&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;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;밖에서 접근할 수 있는 생성자를 제공하지 않으므로 사실상 final로 볼 수 있습니다.&lt;/li&gt;
&lt;li&gt;인스턴스가 하나만 존재합니다.&lt;/li&gt;
&lt;li&gt;열거 타입은 컴파일 시점에서 타입 안정성을 제공합니다.&lt;/li&gt;
&lt;li&gt;열거 타입의 toString 메서드는 출력하기에 적합한 문자열을 제공합니다.&lt;/li&gt;
&lt;li&gt;임의의 메서드나 필드를 추가할 수 있고 임의의 인터페이스를 구현할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Example&lt;/h4&gt;
&lt;pre id=&quot;code_1747731637374&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum Planet {
    MERCURY(3.302e+23, 2.439e6),
    VENUS(4.869e+24, 6.052e6),
    EARTH(5.975e+24, 6.378e6);
    // ...

    private final double mass; // 질량
    private final double radius; // 반지름
    private final double surfaceGravity; // 표면중력

    private static final double G = 6.67300E-11;

    // 생성자
    Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
        surfaceGravity = G * mass / (radius * radius);
    }

    public double mass() { return mass; }
    public double radius() { return radius; }
    public double surfaceGravity() { return surfaceGravity; }

    public double surfaceWeight(double mass) {
        return mass * surfaceGravity; // F = ma
    }
}&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;열거 타입은 근본적으로 불변이므로 모든 필드는 final 이어야 합니다.&lt;/li&gt;
&lt;li&gt;열거 타입은 자신 안에 정의된 상수들의 값을 배열에 담아 반환하는 정적 메서드 values를 제공합니다.&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;[item-35] ordinal 메서드 대신 인스턴스 필드를 사용하라&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;ordinal 메서드: 해당 상수가 열거 타입에서 몇 번째인지 반환하는 메서드&lt;/li&gt;
&lt;li&gt;가장 첫 번째 상수는 0을 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) ordinal 메서드 예시&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;pre id=&quot;code_1747731682295&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public enum Ensemble {
    SOLO, DUET, TRIO, QUARTET, QUINTET,
    SEXTET, SEPTET, OCTET, NONET, DECTET;

    public int numberOfMusicians() { return ordinal() + 1; }   
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해결책&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;열거 타입 상수에 연결된 값은 ordinal 메서드로 얻지 말고, 인스턴스 필드에 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747731709006&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public enum Ensemble {
    SOLO(1), DUET(2), TRIO(3), QUARTET(4), QUINTET(5),
    SEXTET(6), SEPTET(7), OCTET(8), NONET(9), DECTET(10),
    DOUBLE_QUARTET(8), TRIPLE_QUARTET(12);

    private final int int numberOfMusicians;
    Ensemble(int size) { this.numberOfMusicians = size; }
    public int numberOfMusicians() { return numberOfMusicians; }
}&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;b&gt;[item-36] 비트 필드 대신 EnumSet을 사용하라&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;예전에는 정수 열거 패턴에 비트 필드 표시&lt;/p&gt;
&lt;pre id=&quot;code_1747731727664&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Text {
    public static final int STYLE_BOLD          = 1 &amp;lt;&amp;lt; 0;  // 1
    public static final int STYLE_ITALIC        = 1 &amp;lt;&amp;lt; 1;  // 2
    public static final int STYLE_UNDERLINE     = 1 &amp;lt;&amp;lt; 2;  // 4
    public static final int STYLE_STRIKETHROUGH = 1 &amp;lt;&amp;lt; 3; // 8

    // 매개변수 styles는 0개 이상의 STYLE_ 상수를 비트별 OR한 값이다.
    public void applyStyles(int styles) { ... }
}

text.applyStyles(STYLE_BOLD | STYLE_ITALIC);&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;비트 필드: 비트별 OR연산을 이용하여 여러 상수를 하나의 집합으로 모으기 위한 필드&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;단점&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;비트 필드에 포함된 모든 의미상의 원소를 순회하기도 어렵고 최대 몇 비트가 필요한지 미리 예측한 후 타입을 선택해야 합니다.&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;해결책: EnumSet 활용&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;Google의 Guava Library인 Collections.unmodifiableSet을 사용하면 불변 상태로 만들 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747731797134&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Text {
    public enum Style { BOLD, ITALIC, INDERLINE, STRIKETHROUGH }

    // 깔끔하고 안전하다. 어떤 Set을 넘겨도 되나, EnumSet이 가장 좋다.
    // 보통 인터페이스를 전달 받는 것이 좋은 습관이다.
    public void applyStyles(Set&amp;lt;Style&amp;gt; styles) { ... }
}

// Guava 라이브러리 사용
Set immutableEnumSet = Collections.unmodifiableSet(EnumSet.of(Text.Style.BOLD, Text.Style.ITALIC));
immutableEnumSet.add(Text.Style.INDERLINE); // java.lang.UnsupportedOperationException&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;b&gt;[item-37] ordinal 인덱싱 대신 EnumMap을 사용하라&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;ordinal 메서드를 배열 인덱스로 사용하면 위험합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747731822975&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 배열은 제네릭과 호환되지 않으니 비검사 형변환도 필요
Set&amp;lt;Plant&amp;gt;[] plantByLifeCycle = 
    (Set&amp;lt;Plant&amp;gt;[]) new Set[Plant.LifeCycle.values().length];

for (int i = 0; i &amp;lt; plantsByLifeCycle.length; i++) {
    plantsByLifeCycle[i] = new HashSet&amp;lt;&amp;gt;();
}

for (plant p : garden) {
    plantsByLifeCycle[p.lifeCycle.ordinal()].add(p); -&amp;gt; 문제 부분
}

// 결과 출력
for (int i = 0; i &amp;lt; plantsByLifeCycle.length; i++) {
    System.out.printf(&quot;%s: %s%n&quot;, Plant.LifeCycle.values()[i], plantsByLifeCycle[i]);
}&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;배열은 각 인덱스의 의미를 모르기 때문에, 위 코드에서의 %s %s\n과 같은 출력 결과를 포맷팅 해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해결책&lt;/h4&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. EnumMap 사용&lt;/h4&gt;
&lt;pre id=&quot;code_1747731872023&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// EnumMap을 사용하여 데이터와 열거 타입을 매핑한다.
Map&amp;lt;Plant.LifeCycle, Set&amp;lt;Plant&amp;gt;&amp;gt; plantsByLifeCycle =
    new EnumMap&amp;lt;&amp;gt;(Plant.LifeCycle.class);

for (Plant.LifeCycle lc : Plant.LifeCycle.values()) {
    plantsByLifeCycle.put(lc, new HashSet&amp;lt;&amp;gt;());
}

for (Plant p : garden) {
    plantsByLifeCycle.get(p.lifeCycle).add(p);
}
System.out.println(plantsByLifeCycle);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 스트림(Stream) 사용&lt;/h4&gt;
&lt;pre id=&quot;code_1747731898983&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Map을 이용해 데이터와 열거 타입 매핑
Arrays.stream(garden)
    .collect(groupingBy(p -&amp;gt; p.lifeCycle))

// EnumMap을 이용해 데이터와 열거 타입 매핑
Arrays.stream(garden)
    .collect(groupingBy(
        p -&amp;gt; p.lifeCycle, 
        () -&amp;gt; new EnumMap&amp;lt;&amp;gt;(LifeCycle.class),
        toSet())
    );&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;&lt;b&gt;[item-38] 확장할 수 있는 열거타입이 필요하면 인터페이스를 사용하라&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;하지만, 연산 코드(operation code)를 구현할 때는 어울릴 수 있습니다.&lt;/li&gt;
&lt;li&gt;이때는 열거 타입 enum이 인터페이스를 구현(implements)할 수 있다는 점을 이용하면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747731917495&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface Operation {
    double apply(double x, double y);
}

public enum BasicOperation implements Operation {
    PLUS(&quot;+&quot;) {
        public double apply(double x, double y) { return x + y; }
    },
    MINUS(&quot;-&quot;) {
        public double apply(double x, double y) { return x - y; }
    },
    TIMES(&quot;*&quot;) {
        public double apply(double x, double y) { return x * y; }
    },
    DIVIDE(&quot;/&quot;) {
        public double apply(double x, double y) { return x / y; }
    };

    private final String symbol;
    
    BasicOperation(String symbol) { 
	    this.symbol = symbol; 
    }
    
    @Override public String toString() { 
	    return symbol; 
    }
}&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;/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;[item-39] 명명 패턴보다 애너테이션을 사용하라&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;Junit3 version까지는 테스트 메서드의 이름이 test로 시작해야 했습니다.&lt;/li&gt;
&lt;li&gt;test라는 이름이 없거나, 오타가 났다면 테스트 코드는 실행조차 되지 않았습니다.&lt;/li&gt;
&lt;li&gt;Junit4부터 애너테이션을 도입하여 이러한 문제들을 해결할 수 있었습니다.&lt;/li&gt;
&lt;li&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;&lt;b&gt;[item-40] @Override 애너테이션을 일관되게 사용하라&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;@Override: 상위 타입의 메서드를 재정의 하기 위한 애너테이션&lt;/li&gt;
&lt;li&gt;이러한 메서드를 일관되게 사용하면 발생할 수 있는 실수나 버그들을 줄일 수 있습니다.&lt;/li&gt;
&lt;li&gt;추상 메서드를 재정의 할 때는 제외하고는 상위 클래스의 메서드를 재정의하려는 모든 메서드에 @Override 애너테이션을 다는 습관을 가져야 합니다. (위의 이유 때문에)&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;[item-41] 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라&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;Marker Interface: 아무 메서드도 갖고 있지 않고 단지 자신을 구현하는 클래스가 특정 속성을 갖는 것을 표현해 주는 인터페이스&lt;/li&gt;
&lt;li&gt;ex) Serializable, Cloneable, @Target(ElementType.TYPE)&lt;/li&gt;
&lt;li&gt;직렬화: 객체를 바이트 스트림으로 변환하여 파일이나 네트워크 전송 등을 가능하게 하는 기능입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r9gsJ/btsN6AEG1GX/NCpkOW1Dk0eVagyiAMFHKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r9gsJ/btsN6AEG1GX/NCpkOW1Dk0eVagyiAMFHKk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;334&quot; data-filename=&quot;1.png&quot; style=&quot;width: 47.8167%; margin-right: 10px;&quot; data-widthpercent=&quot;48.38&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r9gsJ/btsN6AEG1GX/NCpkOW1Dk0eVagyiAMFHKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr9gsJ%2FbtsN6AEG1GX%2FNCpkOW1Dk0eVagyiAMFHKk%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;663&quot; height=&quot;334&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XY41b/btsN4IDXFix/29QG1KKaTJrB4SKlpnddL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XY41b/btsN4IDXFix/29QG1KKaTJrB4SKlpnddL1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;305&quot; data-filename=&quot;2.png&quot; style=&quot;width: 51.0205%;&quot; data-widthpercent=&quot;51.62&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XY41b/btsN4IDXFix/29QG1KKaTJrB4SKlpnddL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXY41b%2FbtsN4IDXFix%2F29QG1KKaTJrB4SKlpnddL1%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;646&quot; height=&quot;305&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;마커 인터페이스 대표적 예시: Clonable, Serializable&lt;/figcaption&gt;
&lt;/figure&gt;
&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;/li&gt;
&lt;li&gt;적용 대상을 더 정밀하게 지정할 수 있습니다.&lt;/li&gt;
&lt;li&gt;거대한 애너테이션 시스템의 자원을 받습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/Effective Java</category>
      <category>Effective Java</category>
      <category>이펙티브 자바</category>
      <category>이펙티브 자바 6장 열거 타입과 애너테이션</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/407</guid>
      <comments>https://daily1313.tistory.com/entry/Effective-Java-6%EC%9E%A5-%EC%97%B4%EA%B1%B0-%ED%83%80%EC%9E%85%EA%B3%BC-%EC%95%A0%EB%84%88%ED%85%8C%EC%9D%B4%EC%85%98#entry407comment</comments>
      <pubDate>Tue, 20 May 2025 23:31:02 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java] 5장. 제네릭</title>
      <link>https://daily1313.tistory.com/entry/Effective-Java-5%EC%9E%A5-%EC%A0%9C%EB%84%A4%EB%A6%AD</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;263&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bm7IUa/btsN1zzw36I/n66gscn6wVGMc8WpR44rE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bm7IUa/btsN1zzw36I/n66gscn6wVGMc8WpR44rE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bm7IUa/btsN1zzw36I/n66gscn6wVGMc8WpR44rE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbm7IUa%2FbtsN1zzw36I%2Fn66gscn6wVGMc8WpR44rE0%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;263&quot; height=&quot;351&quot; data-origin-width=&quot;263&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[item-26] 로 타입은 사용하지 말라&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;제네릭 타입: 클래스 혹은 인터페이스 선언에 타입 매개변수가 쓰이는 경우&lt;/p&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;ex) List, Set ..(제네릭 타입) &amp;rarr; List&amp;lt;String&amp;gt;, Set&amp;lt;String&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;제네릭 타입을 하나 정의하면 Raw Type도 함께 정의됩니다.&lt;/li&gt;
&lt;li&gt;Raw Type은 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을 때를 의미합니다.&lt;/li&gt;
&lt;li&gt;Raw Type은 타입 선언에서 제네릭 타입 정보가 전부 지워진 것처럼 동작합니다.&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;/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;로 타입을 쓰면 제네릭의 안정성과 표현력을 모두 잃게 됩니다.&lt;/li&gt;
&lt;li&gt;로 타입은 호환성 때문에 만들어졌습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;로 타입을 사용&lt;/h4&gt;
&lt;pre id=&quot;code_1747386086001&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private final Collection stamps = ...;
stamps.add(new Coin(...));
// unchecked call &quot;경고&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;add 메서드 호출 시, ClassCastException 오류 발생 (Runtime Exception)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;제네릭 사용&lt;/h4&gt;
&lt;pre id=&quot;code_1747386180545&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private final Collection&amp;lt;Stamp&amp;gt; stamps = ...;
stamps.add(new Coin()); // 컴파일 오류 발생&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;로 타입과 Object&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;List 같은 로 타입은 사용해서는 안되지만, List &amp;lt;Object&amp;gt;처럼 임의 객체를 허용하는 매개변수화 타입은 괜찮습니다.&lt;/li&gt;
&lt;li&gt;List는 제네릭 타입에서 완전히 발을 뺀 것입니다.&lt;/li&gt;
&lt;li&gt;List&amp;lt;Object&amp;gt;는 모든 타입을 허용한다는 의사를 컴파일러에게 명확히 전달해야 합니다.&lt;/li&gt;
&lt;li&gt;매개변수로 List를 받는 메서드에 List&amp;lt;String&amp;gt;을 넘길 수 있지만, List&amp;lt;Object&amp;gt;를 받는 메서드에는 넘길 수 없습니다.&lt;/li&gt;
&lt;li&gt;rawType을 사용하면 컴파일은 되지만, 실행하면 ClassCastException이 발생합니다. 하지만, 매개변수화 타입을 사용하면 컴파일조차 되지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;로 타입을 사용한 경우 (list)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;런타임 시점에, strings.get(0) 호출 시 ClassCastException 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747386286169&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    List&amp;lt;String&amp;gt; strings = new ArrayList&amp;lt;&amp;gt;();
    
    add(strings, Integer.valueOf(0));
    String s = strings.get(0);
}

// raw type
private static void add(final List list, final Object o) {
    list.add(o);
}&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;h4 data-ke-size=&quot;size20&quot;&gt;List&amp;lt;Object&amp;gt; 변경 시&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴파일 에러, incompatible types: List&amp;lt;String&amp;gt; cannot be converted to List&amp;lt;Object&amp;gt; 메시지 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747386339264&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    List&amp;lt;String&amp;gt; strings = new ArrayList&amp;lt;&amp;gt;();
    
    add(strings, Integer.valueOf(0));
    String s = strings.get(0);
}

// raw type
private static void add(final List&amp;lt;Object&amp;gt; list, final Object o) {
    list.add(o);
}&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;Raw 타입 예시 (잘못 사용 예시)&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;그렇기에 비한정적 와일드 카드 타입인 ?를 대신해서 사용하는 게 좋습니다.
&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;Set&amp;lt;E&amp;gt;의 비한정적 와일드카이드 타입은 Set&amp;lt;?&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Set&amp;lt;?&amp;gt;은 어떤 &lt;span&gt;타입의 &lt;/span&gt;&lt;span&gt;Set&lt;/span&gt;&lt;span&gt;이든 &lt;/span&gt;&lt;span&gt;참조할 &lt;/span&gt;&lt;span&gt;수 &lt;/span&gt;&lt;span&gt;있는 &lt;/span&gt;&lt;span&gt;범용 &lt;/span&gt;&lt;span&gt;타입이지만, &lt;/span&gt;&lt;span&gt;타입을 &lt;/span&gt;&lt;span&gt;모르므로 &lt;/span&gt;&lt;span&gt;요소는 &lt;/span&gt;&lt;span&gt;추가할 &lt;/span&gt;&lt;span&gt;수 &lt;/span&gt;&lt;span&gt;없습니다(&lt;/span&gt;&lt;span&gt;null &lt;/span&gt;&lt;span&gt;제외)&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;Example&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1747386507241&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class TypeTest {
    private static void addtoObjList(final List&amp;lt;Object&amp;gt; list, final Object o) {
        list.add(o);
    }

    private static void addToWildList(final List&amp;lt;?&amp;gt; list, final Object o) {
        // null 외에 허용되지 않는다
        list.add(o);
    }

    private static &amp;lt;T&amp;gt; void addToGenericList(final List&amp;lt;T&amp;gt; list, final T o) {
        list.add(o);
    }


    public static void main(String[] args) {
        List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
        String s = &quot;string&quot;;

        // 메서드에서 Error
        addToWildList(list, s);

        // List&amp;lt;Object&amp;gt; 이므로 incompatible types 오류
        addtoObjList(list, s);
        
        // 가능
        addToGenericList(list, s);
    }
}&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;h4 data-ke-size=&quot;size20&quot;&gt;로 타입을 사용할 수 있는 예외사항: instanceof&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;instanceof: 부모를 상속해서 만들어진 자식 객체가 여러 타입인 경우에 특정 클래스가 맞는지 확인하기 위한 메서드&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;ex 1) piece 객체가 Empty라는 클래스 타입인지를 확인하는 메서드&lt;/p&gt;
&lt;pre id=&quot;code_1747386569649&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (piece instanceof Empty) {
    return;
}&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;ex 2) o 타입이 set 클래스 타입인지 확인하는 메서드&lt;/p&gt;
&lt;pre id=&quot;code_1747386604537&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// o의 타입이 Set인지 확인한 다음, 와일드카드 타입으로 형변환해야 합니다.
// 여기서 로 타입인 Set이 아닌 와일드카드 타입으로 변환함에 주의!
if( o instanceof Set) {
    Set&amp;lt;?&amp;gt; s = (Set&amp;lt;?&amp;gt;) o;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;다형성 적용 vs instanceof&lt;/h4&gt;
&lt;pre id=&quot;code_1747386687505&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 다형성
public abstract class Piece {
    public abstract int calculate(int point);
}

public class King extends Piece {
    public int calculate(int point) {
        return point + 10;
    }
}

public class Pawn extends Piece {
    public int calculate(int point) {
        return point + 1;
    }
}

public class Empty extends Piece {
    public int calculate(int point) {
        return point;
    }
}

public class Point {
  public int calculate(Piece p, int point) {
    return p.calculate(point);
  }
}

// instanceof
public class Point {
    public int calculate(Piece p, int point) {
        if(p instanceof King) {
            return point + 10;
        } else if(p instanceof Pawn) {
            return point + 1;
        } else if(p instanceof Empty) {
            return point;
        }
    }
}&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;instanceof 대신 다형성을 권장합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;instanceof 사용 시, 캡슐화를 깨뜨릴 뿐만 아니라, OCP, SRP 원칙에 위배됩니다.&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;&lt;b&gt;[item-27] 비검사 경고를 제거하라&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;비검사 경고 (unchecked warnings)를 제거하면 런타임에 형변환 관련 예외(ClassCastException)가 발생할 일이 없으며 코드의 올바른 동작도 기대할 수 있게 합니다.&lt;/li&gt;
&lt;li&gt;@SuppressWarnings(&amp;rdquo;unchecked&amp;rdquo;) 어노테이션을 붙여 경고를 숨기는게 좋습니다.&lt;/li&gt;
&lt;li&gt;@SuppressWarnings: 컴파일 경고를 사용하지 않도록 설정해 주는 어노테이션
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;unused, serial, all, deprecation, null, unchecked ..&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;&lt;b&gt;[item-28] 배열보다는 리스트를 사용하라&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;배열 vs 제네릭&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공변(covariant): Sub 클래스가 Super라는 클래스의 하위 타입이라면, 배열 Sub[]는 배열 Super[]의 상위 타입이 되는 것&lt;/li&gt;
&lt;/ul&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;불공변(invariant): 서로 다른 Type1, Type2가 있을 때, List&amp;lt;Type1&amp;gt;, List&amp;lt;Type2&amp;gt;는 서로 상, 하위 타입이 될 수 없는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747386822769&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Object[] array = new Long[1];
array[0] = &quot;String&quot;; // 컴파일은 되지만, 런타임 시에 오류가 발생한다.

List&amp;lt;Object&amp;gt; list = new ArrayList&amp;lt;Long&amp;gt;();
list.add(&quot;String&quot;); // 컴파일조차 되지 않는다.&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;배열은 런타임 시점에도 자신이 담기로 한 원소의 타입인지를 확인합니다. 반면, 제네릭은 타입 정보가 런타임 시점에는 소거됩니다. 이를 통해 런타임 시점에 ClassCastException을 만나지 않고, 컴파일 시점에 오류를 잡을 수 있습니다.&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[item-29] 이왕이면 제네릭 타입으로 만들라&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;그러므로 새로운 타입을 설계할 때는 형변환 없이도 사용할 수 있도록 하는 것이 좋습니다.&lt;/li&gt;
&lt;li&gt;과정
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클래스 선언에 타입 매개변수를 추가합니다. (class {name}&amp;lt;T&amp;gt;)&lt;/li&gt;
&lt;li&gt;일반 타입을 타입 매개변수로 바꾸면 됩니다. (T {variable name})&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;클라이언트에서 형 변환을 해야 하는 경우&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타입 안정성이 없으며, 실수로 잘못된 형 변환 시 ClassCastException 예외 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747386883642&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Box {
    
    private Object item;

    public void set(Object item) {
        this.item = item;
    }

    public Object get() {
        return item;
    }
}

Box box = new Box();
box.set(&quot;item&quot;);
String message = (String) box.get(); // 형변환 필요&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;제네릭을 활용한 개선&lt;/h4&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;pre id=&quot;code_1747386910912&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Box&amp;lt;T&amp;gt; {
    private T item;

    public void set(T item) {
        this.item = item;
    }

    public T get() {
        return item;
    }
}

Box&amp;lt;String&amp;gt; box = new Box&amp;lt;&amp;gt;();
box.set(&quot;item&quot;);
String message = box.get(); // 형변환 불필요&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;b&gt;[item-30] 이왕이면 제네릭 메서드로 만들라&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;ex) 타입 매개변수 목록 &amp;lt;E&amp;gt;, 반환 타입 Set&amp;lt;E&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747386961058&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 제네릭 메서드
public static &amp;lt;E&amp;gt; Set&amp;lt;E&amp;gt; union(Set&amp;lt;E&amp;gt; s1, Set&amp;lt;E&amp;gt; s2) {
    Set&amp;lt;E&amp;gt; result = new HashSet&amp;lt;&amp;gt;(s1);
    result.addAll(s2);
    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;제네릭은 런타임에 타입 정보가 소거되므로 하나의 객체를 어떤 타입으로는 매개변수화 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;이를 통해 불변 객체를 여러 타입으로 만들 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;재귀적 타입한정 적용&lt;/h4&gt;
&lt;pre id=&quot;code_1747387006705&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;puiblic static &amp;lt;E extends Comparable&amp;lt;E&amp;gt;&amp;gt; E max(Collection&amp;lt;E&amp;gt; c);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;E로 받을 타입은 오직 Comparable &amp;lt;E&amp;gt;를 구현한 타입만 가능하다는 뜻입니다.&lt;/li&gt;
&lt;li&gt;즉, Comparable을 구현한 타입만 가능하다는 뜻입니다.&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;[item-31] 한정적 와일드카드를 사용해 API의 유연성을 높여라&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;이러한 경우에는 매개변수에 한정적 와일드 카드를 적용함으로써 하위 객체 또는 상위 객체까지 연산을 적용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;반환 값에 한정적 와일드카드를 적용하는 것은 오히려 유연성을 깨뜨립니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747387094994&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Iterable&amp;lt;? extends E&amp;gt; src;        // E의 하위 타입
Iterable&amp;lt;? super E&amp;gt; src;        // E의 상위 타입&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;h4 data-ke-size=&quot;size20&quot;&gt;PECS (Producer-Extends-Consumer-Super)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매개변수 타입이 생산자를 나타내면 &amp;lt;? extends T&amp;gt;를 사용합니다.&lt;/li&gt;
&lt;li&gt;매개변수 타입이 소비자를 나타내면 &amp;lt;? super T&amp;gt;를 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;lt;? extends T&amp;gt; (stack: push)&lt;/h4&gt;
&lt;pre id=&quot;code_1747387130761&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void pushAll(Iterable&amp;lt;E&amp;gt; src) {
    for (E e : src) {
        push(e);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;lt;? super T&amp;gt; (stack: pop)&lt;/h4&gt;
&lt;pre id=&quot;code_1747387142386&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void popAll(Collection&amp;lt;? super E&amp;gt; dst) {
    while(!isEmpty()) {
        dst.add(pop());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Advanced&lt;/h4&gt;
&lt;pre id=&quot;code_1747387163249&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Union {
    public static &amp;lt;E&amp;gt; Set&amp;lt;E&amp;gt; union(Set&amp;lt;? extends E&amp;gt; s1, Set&amp;lt;? extends E&amp;gt; s2) {
        Set&amp;lt;E&amp;gt; result = new HashSet&amp;lt;&amp;gt;(s1);
        result.addAll(s2);
        return result;
    }

    public static void main(String[] args) {
        // Set.of 메서드는 java 9 이상부터 지원
        Set&amp;lt;Double&amp;gt; doubleSet = Set.of(1.0, 2.1);
        Set&amp;lt;Integer&amp;gt; integerSet = Set.of(1, 2);
        Set&amp;lt;Number&amp;gt; unionSet = union(doubleSet, integerSet);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;재귀적 한정 타입을 적용한 부분 Comparable은 E 인스턴스를 소비하는 소비자이므로 super가 적용해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747387199641&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 변경 전
public static &amp;lt;E extends Comparable&amp;lt;E&amp;gt;&amp;gt; E max(Collection&amp;lt;E&amp;gt; collection)

// 변경 후(PECS 공식 2번 적용)
public static &amp;lt;E extends Comparable&amp;lt;? super E&amp;gt;&amp;gt; E max(Collection&amp;lt;? extends E&amp;gt; collection)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;타입 매개변수가 한 번만 나오는 경우&lt;/h4&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;pre id=&quot;code_1747387224091&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static &amp;lt;E&amp;gt; void swap(List&amp;lt;E&amp;gt; list, int i, int j); (X)
public static void swap(List&amp;lt;?&amp;gt; list, int i, int j); (O)&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;&lt;b&gt;[item-32] 제네릭과 가변인수를 함께 쓸 때는 신중하라&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;가변인수(Varargs): &lt;b&gt;매개변수의 개수가 정해져 있지 않은 메서드 인자&lt;/b&gt;를 받을 때 사용하는 문법&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747387245409&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void dangerous(List&amp;lt;String&amp;gt;... stringLists) {
        List&amp;lt;Integer&amp;gt; intList = List.of(42);
        Object[] objects = stringLists;
        objects[0] = intList; // 힙 오염 발생
        String s = stringLists[0].get(0); // ClassCastException
    }

    public static void main(String[] args) {
        List&amp;lt;String&amp;gt; stringList = new ArrayList&amp;lt;&amp;gt;();
        stringList.add(&quot;Hi there&quot;);
        dangerous(stringList);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;제네릭이나 매개변수화 타입의 varargs 매개변수를 받는 메서드 대표적 예시&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Arrays.list(T&amp;hellip; a), EnumSet.of(E first, E&amp;hellip; set)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747387261474&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SafeVarargs // 제네릭 가변인수 관련 컴파일 경고를 숨기기 위한 어노테이션
@SuppressWarnings(&quot;varargs&quot;)
public static &amp;lt;T&amp;gt; List&amp;lt;T&amp;gt; asList(T... a) {
    return new ArrayList&amp;lt;&amp;gt;(a);
}

@SafeVarargs
public static &amp;lt;E extends Enum&amp;lt;E&amp;gt;&amp;gt; EnumSet&amp;lt;E&amp;gt; of(E first, E... rest) {
    EnumSet&amp;lt;E&amp;gt; result = noneOf(first.getDeclaringClass());
    result.add(first);
    for (E e : rest)
        result.add(e);
    return result;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Backend/Effective Java</category>
      <category>Effective Java</category>
      <category>이펙티브 자바</category>
      <category>이펙티브 자바 5장 제네릭</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/406</guid>
      <comments>https://daily1313.tistory.com/entry/Effective-Java-5%EC%9E%A5-%EC%A0%9C%EB%84%A4%EB%A6%AD#entry406comment</comments>
      <pubDate>Sat, 17 May 2025 18:54:47 +0900</pubDate>
    </item>
    <item>
      <title>[DB] Spring boot 2.x에서 기본으로 지원하는 HikariCP에 대해 알아보자</title>
      <link>https://daily1313.tistory.com/entry/DB-Spring-boot-2x%EC%97%90%EC%84%9C-%EA%B8%B0%EB%B3%B8%EC%9C%BC%EB%A1%9C-%EC%A7%80%EC%9B%90%ED%95%98%EB%8A%94-HikariCP%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring boot 2.x 버전에서는 DB Connection Pool로 HikariCP를 지원합니다. 이를 통해 매번 Connection을 생성하지 않아도 되며, Connection Pool에서 Connection을 가져와 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;우선 HikariCP가 무엇인지 설명드리고, 연관된 개념과 Connection Pool의 동작 과정에 대해 알아보겠습니다.&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;u&gt;&lt;b&gt;HikariCP (HikariCP Connection Pool)&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DB Connection을 관리해 주는 도구&lt;/li&gt;
&lt;li&gt;Connection Pool이 설정된 connection의 사이즈만큼 연결을 허용하여 http 요청에 대해 순차적으로 db 커넥션을 처리해 주는 기능입니다.&lt;/li&gt;
&lt;li&gt;DB Connection Pool이며, Common CP 등 다양한 라이브러리들 중 가볍고 빠르게 처리가 가능합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;overhead가 없으며, 약 130KB의 경량화된 라이브러리 입니다.&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;HikariCP는 DB의 커넥션 정보를 효율적으로 관리해 주는 커넥션 풀 도구입니다. 이제 실제로 데이터베이스와 커넥션을 맺는 과정이 어떻게 이루어지는지 설명드리겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이 과정에는 JDBC(Java Database Connectivity)라는 개념이 포함되어 있으며, 먼저 JDBC가 무엇인지 간단히 살펴본 후, 커넥션이 맺어지는 과정을 단계적으로 알아보겠습니다.&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;&lt;u&gt;&lt;b&gt;JDBC(Java Database Connectivity)&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java에서 데이터베이스에 접속할 수 있도록 하는 자바 API&lt;/li&gt;
&lt;li&gt;JDBC는 데이터베이스에서 자료를 쿼리 하거나 업데이트하는 방법을 제공합니다.&lt;/li&gt;
&lt;li&gt;DB 드라이버를 로드하고, 연결을 통해 커넥션 객체를 얻는 과정이 포함됩니다.&lt;/li&gt;
&lt;li&gt;이후, 커넥션 객체를 통해 DB에 연결하고, 작업 후 연결을 닫는 과정이 필요합니다.&lt;/li&gt;
&lt;li&gt;이러한 반복적인 커넥션 생성 비용을 줄이기 위해, HikariCP와 같은 DB 커넥션 풀 라이브러리를 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;JDBC 표준 인터페이스&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-05-15 오후 9.32.48.png&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I5oPq/btsNYXOHmNu/S8xGMFswzTk6FkpoqUJzIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I5oPq/btsNYXOHmNu/S8xGMFswzTk6FkpoqUJzIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I5oPq/btsNYXOHmNu/S8xGMFswzTk6FkpoqUJzIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI5oPq%2FbtsNYXOHmNu%2FS8xGMFswzTk6FkpoqUJzIK%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;1102&quot; height=&quot;504&quot; data-filename=&quot;스크린샷 2025-05-15 오후 9.32.48.png&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;java.sql.Connection - 연결&lt;/li&gt;
&lt;li&gt;java.sql.Statement - SQL을 담은 내용&lt;/li&gt;
&lt;li&gt;java.sql.ResultSet - SQL 요청 응답&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 Data JDBC, JPA 등과 같은 기술이 등장하면서 JDBC API를 직접적으로 사용할 일은 줄어들었습니다. 하지만, JDBC API를 사용하기 위해서는 JDBC 드라이버(DB와 통신을 담당하는 인터페이스)를 로딩한 후 DB와 Connection을 맺게 됩니다. 위 사진을 보면, JDBC 표준 인터페이스를 통해 MySQL, Oracle 등 여러 DBMS의 드라이버와 독립적으로 애플리케이션을 개발할 수 있도록 추상화를 지원합니다.&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;자바 소스코드로 DB Connection을 맺는 경우를 간단하게 보여드리겠습니다. (예시: MySQL Driver)&lt;/p&gt;
&lt;pre id=&quot;code_1747312781036&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class JdbcExample {
    public static void main(String[] args) throws Exception {
        String url = &quot;jdbc:mysql://localhost:3306/mydb&quot;;
        String username = &quot;root&quot;;
        String password = &quot;password&quot;;

        Connection conn = DriverManager.getConnection(url, username, password);
        PreparedStatement stmt = conn.prepareStatement(&quot;SELECT * FROM users WHERE id = ?&quot;);
        stmt.setInt(1, 1);

        ResultSet rs = stmt.executeQuery();
        while (rs.next()) {
            System.out.println(rs.getString(&quot;name&quot;));
        }

        rs.close();
        stmt.close();
        conn.close();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;DriverManager를 통해 드라이버를 가져온 후, Connection 객체 생성&lt;/li&gt;
&lt;li&gt;Statement 객체 생성 (쿼리 실행을 위한 SQL문)&lt;/li&gt;
&lt;li&gt;Query 실행 후 ResultSet 객체 반환&lt;/li&gt;
&lt;li&gt;ResultSet 객체 Close&lt;/li&gt;
&lt;li&gt;Statement 객체 Close&lt;/li&gt;
&lt;li&gt;Connection 객체 Close&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;Connection 객체 생성 과정&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1152&quot; data-start=&quot;1059&quot;&gt;애플리케이션에서 DriverManager.getConnection(url, username, password)를 호출하여 커넥션 생성을 요청합니다.&lt;/li&gt;
&lt;li data-end=&quot;1199&quot; data-start=&quot;1153&quot;&gt;DB 드라이버는 데이터베이스와 TCP/IP 소켓 연결을 생성합니다.&lt;/li&gt;
&lt;li data-end=&quot;1291&quot; data-start=&quot;1200&quot;&gt;TCP/IP 연결이 성공하면, 사용자 이름(username), 비밀번호(password), DB URL 등의 인증 정보가 데이터베이스로 전달됩니다.&lt;/li&gt;
&lt;li data-end=&quot;1333&quot; data-start=&quot;1292&quot;&gt;데이터베이스는 전달받은 정보를 기반으로 인증을 수행합니다.&lt;/li&gt;
&lt;li data-end=&quot;1396&quot; data-start=&quot;1334&quot;&gt;인증에 성공하면, DB 드라이버는 Connection 객체를 생성하여 애플리케이션에 반환합니다.&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;이러한 Connection 객체를 매번 생성해 주면, 매우 비효율적이므로 이를 보완하기 위해 도입된 개념이 Connection Pool입니다. Connection Pool을 통해 위 Connection 객체를 생성하는 과정을 최소화하고 자원을 효율적으로 관리하기에 성능이 향상될 수 있습니다. Connection Pool의 동작 과정에 대해서 설명드리겠습니다.&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;&lt;u&gt;&lt;b&gt;Connection Pool Flow&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-05-15 오후 9.49.17.png&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2YY18/btsNZvEiNCZ/6dl8pWVveqFOk8aS4R31b0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2YY18/btsNZvEiNCZ/6dl8pWVveqFOk8aS4R31b0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2YY18/btsNZvEiNCZ/6dl8pWVveqFOk8aS4R31b0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2YY18%2FbtsNZvEiNCZ%2F6dl8pWVveqFOk8aS4R31b0%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;1364&quot; height=&quot;420&quot; data-filename=&quot;스크린샷 2025-05-15 오후 9.49.17.png&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;420&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Application 실행 시점에 Connection Pool에 Connection을 생성합니다.&lt;/li&gt;
&lt;li&gt;http 요청 시, Connection Pool 내에서 Connection 객체를 사용합니다.&lt;/li&gt;
&lt;li&gt;사용이 완료된 Connection 객체는 Connection Pool에 반환합니다. (실제 닫는 것 X)&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;Connection Pool 만으로도 많은 장점을 누릴 수 있지만, 실제 HiKariCP는 다른 해당 Bench Mark를 통해 다른 Connection Pool Library보다 월등히 뛰어나다는 사실을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-05-15 오후 9.53.36.png&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eyasQo/btsNZXAkfjk/LcTQoGKGw2RkAFMjvwO8k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eyasQo/btsNZXAkfjk/LcTQoGKGw2RkAFMjvwO8k1/img.png&quot; data-alt=&quot;Connection Cycle ops/ms: DataSource.getConnection()/Connection.close()의 사이클 Statement Cycle ops/ms: Connection.prepareStatement(), Statement.execute(), Statement.close()의 사이클&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eyasQo/btsNZXAkfjk/LcTQoGKGw2RkAFMjvwO8k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeyasQo%2FbtsNZXAkfjk%2FLcTQoGKGw2RkAFMjvwO8k1%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;1482&quot; height=&quot;620&quot; data-filename=&quot;스크린샷 2025-05-15 오후 9.53.36.png&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Connection Cycle ops/ms: DataSource.getConnection()/Connection.close()의 사이클 Statement Cycle ops/ms: Connection.prepareStatement(), Statement.execute(), Statement.close()의 사이클&lt;/figcaption&gt;
&lt;/figure&gt;
&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;&lt;u&gt;&lt;b&gt;HikariCP Option&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1747313463743&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring: 
    datasource: 
        hikari: 
            driver-class-name: com.mysql.cj.jdbc.Driver 
            jdbc-url: jdbc:mysql://{url}:{port}/{schema} 
            maximum-pool-size: 10 (default)
            minimum-idle: 10 (default)
            max-lifetime: 1800000 (default)
            idle-timeout: 300000 (default)
            connection-timeout: 30000 (default)&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;maximum-pool-size
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;커넥션 풀의 최대 크기 (default: 10)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;minimum-idle
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;커넥션 풀에서 유지할 idle connection 수 (default: 10)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;max-lifetime
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;connection pool에서 살아있을 수 있는 최대 시간 (default: 30m)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;idle-timeout
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;idle connection이 connection pool에서 제거되기 전까지 대기하는 시간입니다.&lt;/li&gt;
&lt;li&gt;minimum-idle을 초과한 커넥션에 대해서만 제거됩니다.&lt;/li&gt;
&lt;li&gt;max-lifetime &amp;gt; idle-timeout 으로 설정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;connection-timeout (default: 30s)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;connection pool에서 connection을 획득하기 위해 기다리는 시간 (default:30s)&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ittrue.tistory.com/250&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ittrue.tistory.com/250&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1747313668017&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Java] JDBC란 무엇인가? - Java Database Connectivity&quot; data-og-description=&quot;JDBC란? JDBC(Java Database Connectivity)는 Java 기반 애플리케이션의 데이터를 데이터베이스에 저장 및 업데이트하거나, 데이터베이스에 저장된 데이터를 Java에서 사용할 수 있도록 하는 자바 API이다. JDB&quot; data-og-host=&quot;ittrue.tistory.com&quot; data-og-source-url=&quot;https://ittrue.tistory.com/250&quot; data-og-url=&quot;https://ittrue.tistory.com/250&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bkJbOS/hyYTb2ucPz/UyEuIK6sCN7ymJeHQNokI0/img.png?width=608&amp;amp;height=276&amp;amp;face=0_0_608_276,https://scrap.kakaocdn.net/dn/QxR7k/hyYRke21o2/tAk8jBqh41YJQeAlHmqtH0/img.png?width=608&amp;amp;height=276&amp;amp;face=0_0_608_276,https://scrap.kakaocdn.net/dn/rkQWu/hyYRuBV5UM/NqXBzJL9AH9ref8rWvnHik/img.png?width=932&amp;amp;height=392&amp;amp;face=0_0_932_392&quot;&gt;&lt;a href=&quot;https://ittrue.tistory.com/250&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ittrue.tistory.com/250&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bkJbOS/hyYTb2ucPz/UyEuIK6sCN7ymJeHQNokI0/img.png?width=608&amp;amp;height=276&amp;amp;face=0_0_608_276,https://scrap.kakaocdn.net/dn/QxR7k/hyYRke21o2/tAk8jBqh41YJQeAlHmqtH0/img.png?width=608&amp;amp;height=276&amp;amp;face=0_0_608_276,https://scrap.kakaocdn.net/dn/rkQWu/hyYRuBV5UM/NqXBzJL9AH9ref8rWvnHik/img.png?width=932&amp;amp;height=392&amp;amp;face=0_0_932_392');&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;[Java] JDBC란 무엇인가? - Java Database Connectivity&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;JDBC란? JDBC(Java Database Connectivity)는 Java 기반 애플리케이션의 데이터를 데이터베이스에 저장 및 업데이트하거나, 데이터베이스에 저장된 데이터를 Java에서 사용할 수 있도록 하는 자바 API이다. JDB&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ittrue.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/73&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://adjh54.tistory.com/73&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1747313674674&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Java] HikariCP 이해하고 적용하기 (with. MyBatis)&quot; data-og-description=&quot;해당 글에서는 HikariCP에 대해 이해하고 영속성 프레임워크(Persistence Framework)인 MyBatis와 연동을 하는 적용 방법에 대해서 공유 목적으로 작성한 글입니다.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  [참고] 이전에 구성하였던 &quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/73&quot; data-og-url=&quot;https://adjh54.tistory.com/73&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bhfapH/hyYU9iBaNl/eJvbfX6dHgZwhtkElSkKDk/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/bIobhm/hyYU4uPAIo/v8UTw1ByVXSABwutfZj5n1/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/pgHAQ/hyYRtXmuCi/GAs9KC4icpthj2t8tISph1/img.png?width=1793&amp;amp;height=664&amp;amp;face=0_0_1793_664&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/73&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/73&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bhfapH/hyYU9iBaNl/eJvbfX6dHgZwhtkElSkKDk/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/bIobhm/hyYU4uPAIo/v8UTw1ByVXSABwutfZj5n1/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/pgHAQ/hyYRtXmuCi/GAs9KC4icpthj2t8tISph1/img.png?width=1793&amp;amp;height=664&amp;amp;face=0_0_1793_664');&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;[Java] HikariCP 이해하고 적용하기 (with. MyBatis)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 HikariCP에 대해 이해하고 영속성 프레임워크(Persistence Framework)인 MyBatis와 연동을 하는 적용 방법에 대해서 공유 목적으로 작성한 글입니다.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  [참고] 이전에 구성하였던&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devoong2.tistory.com/entry/Spring-Boot-Hikari-CP-%EC%9D%98-%EC%98%B5%EC%85%98%EA%B3%BC-%EC%84%A4%EC%A0%95%EB%B0%A9%EB%B2%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://devoong2.tistory.com/entry/Spring-Boot-Hikari-CP-%EC%9D%98-%EC%98%B5%EC%85%98%EA%B3%BC-%EC%84%A4%EC%A0%95%EB%B0%A9%EB%B2%95&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1747313681230&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Spring Boot] Hikari CP 의 옵션과 설정방법&quot; data-og-description=&quot;Hikari CP 옵션과 설정 방법Hikari CP 는 SpringBoot 2.0.0 버전 이상부터 디폴트로 설정된 Connection Pool 입니다.이번엔 Hikari CP 의 설정 옵션들은 어떤것들이 있는지, 설정시 고민해야할 부분은 어떤것들이 &quot; data-og-host=&quot;devoong2.tistory.com&quot; data-og-source-url=&quot;https://devoong2.tistory.com/entry/Spring-Boot-Hikari-CP-%EC%9D%98-%EC%98%B5%EC%85%98%EA%B3%BC-%EC%84%A4%EC%A0%95%EB%B0%A9%EB%B2%95&quot; data-og-url=&quot;https://devoong2.tistory.com/entry/Spring-Boot-Hikari-CP-%EC%9D%98-%EC%98%B5%EC%85%98%EA%B3%BC-%EC%84%A4%EC%A0%95%EB%B0%A9%EB%B2%95&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bUIR15/hyYRniwIin/zKyRkirE8py0p7ZRx6MBKk/img.png?width=800&amp;amp;height=394&amp;amp;face=0_0_800_394,https://scrap.kakaocdn.net/dn/PAiRd/hyYTex8emg/xZvEF1ZytdtuKl3UMX8AyK/img.png?width=800&amp;amp;height=394&amp;amp;face=0_0_800_394&quot;&gt;&lt;a href=&quot;https://devoong2.tistory.com/entry/Spring-Boot-Hikari-CP-%EC%9D%98-%EC%98%B5%EC%85%98%EA%B3%BC-%EC%84%A4%EC%A0%95%EB%B0%A9%EB%B2%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devoong2.tistory.com/entry/Spring-Boot-Hikari-CP-%EC%9D%98-%EC%98%B5%EC%85%98%EA%B3%BC-%EC%84%A4%EC%A0%95%EB%B0%A9%EB%B2%95&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bUIR15/hyYRniwIin/zKyRkirE8py0p7ZRx6MBKk/img.png?width=800&amp;amp;height=394&amp;amp;face=0_0_800_394,https://scrap.kakaocdn.net/dn/PAiRd/hyYTex8emg/xZvEF1ZytdtuKl3UMX8AyK/img.png?width=800&amp;amp;height=394&amp;amp;face=0_0_800_394');&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;[Spring Boot] Hikari CP 의 옵션과 설정방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Hikari CP 옵션과 설정 방법Hikari CP 는 SpringBoot 2.0.0 버전 이상부터 디폴트로 설정된 Connection Pool 입니다.이번엔 Hikari CP 의 설정 옵션들은 어떤것들이 있는지, 설정시 고민해야할 부분은 어떤것들이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devoong2.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>DB Connection Pool</category>
      <category>hikaricp</category>
      <category>JDBC</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/405</guid>
      <comments>https://daily1313.tistory.com/entry/DB-Spring-boot-2x%EC%97%90%EC%84%9C-%EA%B8%B0%EB%B3%B8%EC%9C%BC%EB%A1%9C-%EC%A7%80%EC%9B%90%ED%95%98%EB%8A%94-HikariCP%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90#entry405comment</comments>
      <pubDate>Thu, 15 May 2025 21:56:22 +0900</pubDate>
    </item>
    <item>
      <title>[Linux] ssh 원격 접속 실패 시 해결책 (WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!)</title>
      <link>https://daily1313.tistory.com/entry/Linux-ssh-%EC%9B%90%EA%B2%A9-%EC%A0%91%EC%86%8D-%EC%8B%A4%ED%8C%A8-%ED%95%B4%EA%B2%B0%EC%B1%85-WARNING-REMOTE-HOST-IDENTIFICATION-HAS-CHANGED</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-05-13 오후 11.51.39.png&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbyjaD/btsNWNEtU6r/3xmIoWS572WKocNxiFLQc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbyjaD/btsNWNEtU6r/3xmIoWS572WKocNxiFLQc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbyjaD/btsNWNEtU6r/3xmIoWS572WKocNxiFLQc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbyjaD%2FbtsNWNEtU6r%2F3xmIoWS572WKocNxiFLQc0%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;350&quot; height=&quot;192&quot; data-filename=&quot;스크린샷 2025-05-13 오후 11.51.39.png&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;192&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;ssh로 원격 서버에 접속 시, 다음과 같은 로그가 발생하며 접속에 실패하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1747147762238&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PS C:\Users\admin&amp;gt; ssh root@[ip]
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:izr4IOBOHEnoRBx/Us9zrOBASpKg9Y/yWVuniIIkRB0.
Please contact your system administrator.
Add correct host key in C:\\Users\\admin/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in C:\\Users\\admin/.ssh/known_hosts:69
Host key for [ip] has changed and you have requested strict checking.&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;u&gt;&lt;b&gt;[원인]&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SSH Host Key의 기존의 known_hosts에 저장된 값과 달라졌기 때문입니다.&lt;/li&gt;
&lt;li&gt;다른 호스트에서 ssh or os 작업을 새롭게 하면서 인증 정보가 변경되었기 때문입니다.&lt;/li&gt;
&lt;li&gt;즉, 다른 host에서의 서버 재설치 작업에 의해 인증정보가 변경되면서 발생한 로그입니다.&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;u&gt;&lt;b&gt;[ known_hosts 파일]&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;known_hosts 파일은 ssh를 사용해 원격으로 호스트에 연결할 경우, 클라이언트가 연결하려는 호스트의 공개키와 호스트명 (ip or domain)을 저장하는 파일입니다.&lt;/li&gt;
&lt;li&gt;ssh로 연결하면 known_hosts 파일이 사용자의 디렉토리에 저장됩니다.&lt;/li&gt;
&lt;li&gt;path
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;~/.ssh/known_hosts (MacOS)&lt;/li&gt;
&lt;li&gt;C:\Users\&amp;lt;username&amp;gt;\.ssh\known_hosts (Windows)&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;&lt;u&gt;&lt;b&gt;[known_hosts] 역할&lt;/b&gt;&lt;/u&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;[해결책]&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ssh-keygen -R [ip]&lt;/li&gt;
&lt;li&gt;rm -rf [known_hosts 경로]&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1747148601122&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PS C:\Users\admin&amp;gt; ssh-keygen -R [ip]
# Host [ip] found: line 67
# Host [ip] found: line 68
# Host [ip] found: line 69
C:\Users\admin/.ssh/known_hosts updated.
Original contents retained as C:\Users\admin/.ssh/known_hosts.old
PS C:\Users\admin&amp;gt; ssh root@[ip]
The authenticity of host '[ip] ([ip])' can't be established.
ED25519 key fingerprint is SHA256:izr4IOBOHEnoRBx/Us9zrOBASpKg9Y/yWVuniIIkRB0.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[ip]' (ED25519) to the list of known hosts.
root@[ip]'s password:
Activate the web console with: systemctl enable --now cockpit.socket

Last login: Thu Apr 17 17:08:17 2025 from [ip]
[root@sb-kim-test2 ~]#&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;b&gt;&amp;lt;참고자료&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://jibinary.tistory.com/145&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jibinary.tistory.com/145&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1747148697058&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[네트워크] known_hosts 파일이란 (SSH)&quot; data-og-description=&quot;◇ 공부 기록용으로 작성하였으니 틀린 점, 피드백 주시면 감사하겠습니다 ◇ known_hosts 파일 known_hosts 파일은 SSH를 사용해 원격으로 호스트에 연결할 경우,  &amp;zwj; 클라이언트가 연결하려는  &quot; data-og-host=&quot;jibinary.tistory.com&quot; data-og-source-url=&quot;https://jibinary.tistory.com/145&quot; data-og-url=&quot;https://jibinary.tistory.com/145&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/btnFoQ/hyYPjUSoc7/wN9T1DPGkh6kofkjmq7rA0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/jPQoW/hyYPgw5R56/mvwNVZ3TsgAklKfCACRQPK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/dB9Nxw/hyYU7rgTAw/Ds0PN7WzWFCyt68Ilobm2K/img.png?width=1080&amp;amp;height=1080&amp;amp;face=0_0_1080_1080&quot;&gt;&lt;a href=&quot;https://jibinary.tistory.com/145&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jibinary.tistory.com/145&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/btnFoQ/hyYPjUSoc7/wN9T1DPGkh6kofkjmq7rA0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/jPQoW/hyYPgw5R56/mvwNVZ3TsgAklKfCACRQPK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/dB9Nxw/hyYU7rgTAw/Ds0PN7WzWFCyt68Ilobm2K/img.png?width=1080&amp;amp;height=1080&amp;amp;face=0_0_1080_1080');&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;[네트워크] known_hosts 파일이란 (SSH)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;◇ 공부 기록용으로 작성하였으니 틀린 점, 피드백 주시면 감사하겠습니다 ◇ known_hosts 파일 known_hosts 파일은 SSH를 사용해 원격으로 호스트에 연결할 경우,  &amp;zwj; 클라이언트가 연결하려는  &lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jibinary.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://visu4l.tistory.com/entry/ssh-%EC%9B%90%EA%B2%A9-%EC%A0%91%EC%86%8D-%EC%97%90%EB%9F%ACWARNING-REMOTE-HOST-IDENTIFICATION-HAS-CHANGED&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://visu4l.tistory.com/entry/ssh-%EC%9B%90%EA%B2%A9-%EC%A0%91%EC%86%8D-%EC%97%90%EB%9F%ACWARNING-REMOTE-HOST-IDENTIFICATION-HAS-CHANGED&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1747148706840&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;ssh 원격 접속 에러(WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!)&quot; data-og-description=&quot;가끔 가다가 잘 접속되던 서버가 아래와 같은 메세지를 띄우는 경우가 있다. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&quot; data-og-host=&quot;visu4l.tistory.com&quot; data-og-source-url=&quot;https://visu4l.tistory.com/entry/ssh-%EC%9B%90%EA%B2%A9-%EC%A0%91%EC%86%8D-%EC%97%90%EB%9F%ACWARNING-REMOTE-HOST-IDENTIFICATION-HAS-CHANGED&quot; data-og-url=&quot;https://visu4l.tistory.com/entry/ssh-%EC%9B%90%EA%B2%A9-%EC%A0%91%EC%86%8D-%EC%97%90%EB%9F%ACWARNING-REMOTE-HOST-IDENTIFICATION-HAS-CHANGED&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xG9Ly/hyYPfLHElL/RTdWWpm86oZKjAOabaUOrK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/e2Pfb/hyYU7EMNAr/GhwyqVKzeSkDgrxw70uQT0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://visu4l.tistory.com/entry/ssh-%EC%9B%90%EA%B2%A9-%EC%A0%91%EC%86%8D-%EC%97%90%EB%9F%ACWARNING-REMOTE-HOST-IDENTIFICATION-HAS-CHANGED&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://visu4l.tistory.com/entry/ssh-%EC%9B%90%EA%B2%A9-%EC%A0%91%EC%86%8D-%EC%97%90%EB%9F%ACWARNING-REMOTE-HOST-IDENTIFICATION-HAS-CHANGED&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xG9Ly/hyYPfLHElL/RTdWWpm86oZKjAOabaUOrK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/e2Pfb/hyYU7EMNAr/GhwyqVKzeSkDgrxw70uQT0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;ssh 원격 접속 에러(WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;가끔 가다가 잘 접속되던 서버가 아래와 같은 메세지를 띄우는 경우가 있다. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;visu4l.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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>Linux</category>
      <category>known_hosts</category>
      <category>ssh 원격 접속 실패</category>
      <category>warning: remote host identification has changed!</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/404</guid>
      <comments>https://daily1313.tistory.com/entry/Linux-ssh-%EC%9B%90%EA%B2%A9-%EC%A0%91%EC%86%8D-%EC%8B%A4%ED%8C%A8-%ED%95%B4%EA%B2%B0%EC%B1%85-WARNING-REMOTE-HOST-IDENTIFICATION-HAS-CHANGED#entry404comment</comments>
      <pubDate>Wed, 14 May 2025 00:06:52 +0900</pubDate>
    </item>
    <item>
      <title>[JMeter] Apache JMeter를 활용한 성능 테스트</title>
      <link>https://daily1313.tistory.com/entry/JMeter-Apache-JMeter%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%84%B1%EB%8A%A5-%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-18 오전 12.30.31.png&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tcKyh/btsNpA7SnW0/0O8HFdMcQfMHVQskBfHPM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tcKyh/btsNpA7SnW0/0O8HFdMcQfMHVQskBfHPM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tcKyh/btsNpA7SnW0/0O8HFdMcQfMHVQskBfHPM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtcKyh%2FbtsNpA7SnW0%2F0O8HFdMcQfMHVQskBfHPM0%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;600&quot; height=&quot;228&quot; data-filename=&quot;스크린샷 2025-04-18 오전 12.30.31.png&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;/figure&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;p data-ke-size=&quot;size16&quot;&gt;이 과정에 도움이 되는 도구인 JMeter는 JSP, Ajax와 같은 동적 리소스 뿐만 아니라, JavaScript, HTML 파일과 같은 정적 리소스의 성능을 측정할 수 있고, 동시성 및 부하 테스트를 지원합니다. 더불어 테스트에 대한 결과를 Report, Table, Tree, Graph 등 다양한 형태로 추출해줍니다. 이제 장점을 알았으니, JMeter의 개념 및 관련 용어와 설치 방법(Window OS 기준으로 설명), 테스트 과정에 대해 설명드리겠습니다.&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;u&gt;&lt;b&gt;JMeter&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;JMeter&amp;trade;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;애플리케이션은 오픈 소스 소프트웨어로, 기능 동작 부하 테스트 및 성능 측정을 위해 설계된 100% 순수 Java 애플리케이션입니다. 원래는 웹 애플리케이션 테스트용으로 설계되었지만, 이후 다른 테스트 기능으로 확장되었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;a href=&quot;https://jmeter.apache.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jmeter.apache.org/&lt;/a&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;u&gt;&lt;b&gt;설치 및 JMeter 실행 방법&lt;/b&gt;&lt;/u&gt;&lt;u&gt;&lt;b&gt;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;k6, nGrinder와 같은 성능 Test Tool에 비해 간단한 편이라고 생각합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jmeter.apache.org/download_jmeter.cgi&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jmeter.apache.org/download_jmeter.cgi&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1744903907627&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;Apache JMeter
          -
          Download Apache JMeter&quot; data-og-description=&quot;Download Apache JMeter We recommend you use a mirror to download our release builds, but you must verify the integrity of the downloaded files using signatures downloaded from our main distribution directories. Recent releases (48 hours) may not yet be ava&quot; data-og-host=&quot;jmeter.apache.org&quot; data-og-source-url=&quot;https://jmeter.apache.org/download_jmeter.cgi&quot; data-og-url=&quot;https://jmeter.apache.org/download_jmeter.cgi&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://jmeter.apache.org/download_jmeter.cgi&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jmeter.apache.org/download_jmeter.cgi&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;Apache JMeter - Download Apache JMeter&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Download Apache JMeter We recommend you use a mirror to download our release builds, but you must verify the integrity of the downloaded files using signatures downloaded from our main distribution directories. Recent releases (48 hours) may not yet be ava&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jmeter.apache.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 url로 접속하여 zip 파일을 다운로드 해줍니다.&lt;/li&gt;
&lt;li&gt;압축을 해제하고, 터미널에 접속합니다.&lt;/li&gt;
&lt;li&gt;bin 디렉토리로 접근하여 jmeter를 실행해줍니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PS C:\apache-jmeter-5.6.3\bin&amp;gt; ./jmeter&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;&lt;u&gt;&lt;b&gt;테스트 환경 세팅 &lt;/b&gt;&lt;/u&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;u&gt;&lt;b&gt;1. Thread Group 설정&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-18 오전 12.35.37.png&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAttUU/btsNo1p9q0U/GwHI4qErUXVNqoYcqGcor0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAttUU/btsNo1p9q0U/GwHI4qErUXVNqoYcqGcor0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAttUU/btsNo1p9q0U/GwHI4qErUXVNqoYcqGcor0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAttUU%2FbtsNo1p9q0U%2FGwHI4qErUXVNqoYcqGcor0%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;1672&quot; height=&quot;600&quot; data-filename=&quot;스크린샷 2025-04-18 오전 12.35.37.png&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&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;Number of Threads (Users)&lt;/b&gt;: Test에 사용될 Thread(Users) 개수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Ramp-Up Period&lt;/b&gt;: 스레드가 실행되기까지 소요되는 시간(초)&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Loop Count&lt;/b&gt;: 각 스레드가 실행해야 할 반복 횟수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 세팅된 값을 예시로 보면, Number of Threads = 10, Ramp-Up Period = 1, Loop Count = 2 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 10개의 Thread가 1초동안 Test Plan을 2회 실행합니다.&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;u&gt;&lt;b&gt;2. Sampler 추가 (HTTP Request)&lt;/b&gt;&lt;/u&gt;&lt;u&gt;&lt;b&gt;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-18 오전 12.53.44.png&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R8UN4/btsNnqjQR3d/tPr5q0QvgZb28O5x9CCRE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R8UN4/btsNnqjQR3d/tPr5q0QvgZb28O5x9CCRE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R8UN4/btsNnqjQR3d/tPr5q0QvgZb28O5x9CCRE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR8UN4%2FbtsNnqjQR3d%2FtPr5q0QvgZb28O5x9CCRE1%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;1636&quot; height=&quot;1078&quot; data-filename=&quot;스크린샷 2025-04-18 오전 12.53.44.png&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;1078&quot;/&gt;&lt;/span&gt;&lt;/figure&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;Protocol&lt;/b&gt;: http, https&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Server Name or IP&lt;/b&gt;: Host or IP&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Port Number&lt;/b&gt;: Port Number (8080, 443 ..)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Path&lt;/b&gt;: request url&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Parameters&lt;/b&gt;: (선택)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 서버에 HTTP Request 요청을 보내기 위한 설정을 해줍니다.&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;u&gt;&lt;b&gt;3. Listener 추가 (요청에 대한 결과 확인)&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;View Results Tree
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Request, Response에 대한 Header 및 Body 정보를 상세하게 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;View Results in Table
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테이블 형태로 Thread Group의 이름, 테스트 수행 시간, 대기 횟수 등을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Summary Report
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호출 건수, 응답 시간의 통계, Error율, TPS 등을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-18 오전 12.57.35.png&quot; data-origin-width=&quot;1676&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRhild/btsNpaPq26r/XZX3gBh0C1uWvxsS6moUTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRhild/btsNpaPq26r/XZX3gBh0C1uWvxsS6moUTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRhild/btsNpaPq26r/XZX3gBh0C1uWvxsS6moUTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRhild%2FbtsNpaPq26r%2FXZX3gBh0C1uWvxsS6moUTK%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;1676&quot; height=&quot;644&quot; data-filename=&quot;스크린샷 2025-04-18 오전 12.57.35.png&quot; data-origin-width=&quot;1676&quot; data-origin-height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-18 오전 12.56.43.png&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;578&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfLe7Q/btsNpVQq5iy/u3IRD9VdIrhkk16CgHL5k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfLe7Q/btsNpVQq5iy/u3IRD9VdIrhkk16CgHL5k1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfLe7Q/btsNpVQq5iy/u3IRD9VdIrhkk16CgHL5k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfLe7Q%2FbtsNpVQq5iy%2Fu3IRD9VdIrhkk16CgHL5k1%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;1674&quot; height=&quot;578&quot; data-filename=&quot;스크린샷 2025-04-18 오전 12.56.43.png&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;578&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-18 오전 12.56.54.png&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dZwx49/btsNpa2W5x9/jtfn7QJ3oXNuFBFPUg5Yo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dZwx49/btsNpa2W5x9/jtfn7QJ3oXNuFBFPUg5Yo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dZwx49/btsNpa2W5x9/jtfn7QJ3oXNuFBFPUg5Yo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdZwx49%2FbtsNpa2W5x9%2Fjtfn7QJ3oXNuFBFPUg5Yo1%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;1672&quot; height=&quot;342&quot; data-filename=&quot;스크린샷 2025-04-18 오전 12.56.54.png&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;342&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;더불어, TPS를 그래프 형태로 확인하고 싶으면 플러그인을 설치하셔서 초당 요청 개수를 직관적으로 파악할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jmeter-plugins.org/wiki/TransactionsPerSecond/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jmeter-plugins.org/wiki/TransactionsPerSecond/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-18 오전 1.04.05.png&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;1102&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s2AAy/btsNqEAVy5R/4zHjDJfuN5am4onFMW3kE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s2AAy/btsNqEAVy5R/4zHjDJfuN5am4onFMW3kE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s2AAy/btsNqEAVy5R/4zHjDJfuN5am4onFMW3kE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs2AAy%2FbtsNqEAVy5R%2F4zHjDJfuN5am4onFMW3kE1%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;1668&quot; height=&quot;1102&quot; data-filename=&quot;스크린샷 2025-04-18 오전 1.04.05.png&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;1102&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;</description>
      <category>Backend/Spring</category>
      <category>apache jmeter</category>
      <category>JMeter</category>
      <category>동시성 테스트</category>
      <category>부하 테스트</category>
      <category>성능 테스트</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/403</guid>
      <comments>https://daily1313.tistory.com/entry/JMeter-Apache-JMeter%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%84%B1%EB%8A%A5-%ED%85%8C%EC%8A%A4%ED%8A%B8#entry403comment</comments>
      <pubDate>Fri, 18 Apr 2025 01:08:03 +0900</pubDate>
    </item>
    <item>
      <title>[Intellij] JetBrains AI Assistant 사용 방법 및 후기</title>
      <link>https://daily1313.tistory.com/entry/Intellij-JetBrains-AI-Assistant-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95-%EB%B0%8F-%ED%9B%84%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 회사에서 JetBrains&amp;nbsp;AI&amp;nbsp;Assistant를 지원해 주면서 해당 Tool을 사용한 후기에 대해서 포스팅 해보려고 합니다.&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;AI Assistant란 JetBrains 사에서 만든 AI Tool 입니다. 반복적인 업무를 줄여주고 개발 효율성을 향상시켜줄 수 있는 좋은 기능입니다. 각종 소스코드의 분석, 최적화, 문서화, 단위 테스트코드까지 작성해 주는 기능을 지원합니다. 이에 더해 기존 ChatGPT처럼 단순 질의응답도 제공합니다.&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;Al Assistant 가격&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 가격은 개인으로 지불하려면 월에 약 $8를 지불해야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-10 오전 8.12.25.png&quot; data-origin-width=&quot;1544&quot; data-origin-height=&quot;1180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tdotM/btsNeTGkmn4/CTMfETMKPGMQxTNgxDIAX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tdotM/btsNeTGkmn4/CTMfETMKPGMQxTNgxDIAX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tdotM/btsNeTGkmn4/CTMfETMKPGMQxTNgxDIAX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtdotM%2FbtsNeTGkmn4%2FCTMfETMKPGMQxTNgxDIAX0%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;669&quot; height=&quot;511&quot; data-filename=&quot;스크린샷 2025-04-10 오전 8.12.25.png&quot; data-origin-width=&quot;1544&quot; data-origin-height=&quot;1180&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Intellij JetBrains 계정에 해당 서비스가 활성화되어야 합니다. (저는 회사 라이선스라서 AI Pro로 되어 있습니다)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;jetBrains-AI.png&quot; data-origin-width=&quot;374&quot; data-origin-height=&quot;135&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHmtek/btsNfhmo0Jm/DY9wk4ponZ0R1O19kD67Hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHmtek/btsNfhmo0Jm/DY9wk4ponZ0R1O19kD67Hk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHmtek/btsNfhmo0Jm/DY9wk4ponZ0R1O19kD67Hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHmtek%2FbtsNfhmo0Jm%2FDY9wk4ponZ0R1O19kD67Hk%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;374&quot; height=&quot;135&quot; data-filename=&quot;jetBrains-AI.png&quot; data-origin-width=&quot;374&quot; data-origin-height=&quot;135&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Intellij에서 AI Assistant 플러그인을 설치해 줍니다.&lt;/li&gt;
&lt;li&gt;Help -&amp;gt; Register로 진입하여 AI Assistant의 License를 등록해 줍니다. (자동으로 등록해 줍니다)&lt;/li&gt;
&lt;li&gt;오른쪽 탭에 AI Assistant Image가 보이는데, 해당 Image를 클릭하여 기능을 사용하시면 됩니다.&amp;nbsp;&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;/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;AI Assistant 기능&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-10 오전 8.27.19.png&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1oCAF/btsNf0K4LSx/kOwAlFQtoMrB4NbUsZPUTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1oCAF/btsNf0K4LSx/kOwAlFQtoMrB4NbUsZPUTK/img.png&quot; data-alt=&quot;소스 드래그 후 오른쪽 마우스 클릭 -&amp;amp;gt; AI Action&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1oCAF/btsNf0K4LSx/kOwAlFQtoMrB4NbUsZPUTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1oCAF%2FbtsNf0K4LSx%2FkOwAlFQtoMrB4NbUsZPUTK%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;846&quot; height=&quot;458&quot; data-filename=&quot;스크린샷 2025-04-10 오전 8.27.19.png&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;소스 드래그 후 오른쪽 마우스 클릭 -&amp;gt; AI Action&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Explain Code (소스 코드 설명) - 현재 자신이 보고 있는 class 파일을 기반으로 소스 코드를 상세히 분석하고 설명해 줍니다.&lt;/li&gt;
&lt;li&gt;Suggest Refactoring (기존 소스 리팩토링) - 기존 소스 코드를 더욱 가독성 좋고 깔끔하게 리팩토링 해줍니다. (개인적으로 리팩토링을 깔끔하게 잘해준다고 느꼈습니다.)&lt;/li&gt;
&lt;li&gt;Find Problems (소스 코드 문제점 설명) - 추후 발생할 수 있는 문제점과 현재 코드의 잘못된 부분에 대해 짚어주고 리팩토링을 제안합니다.&lt;/li&gt;
&lt;li&gt;Write Documentation (문서화) - 메서드와 파라미터 정보를 기반으로 문서화 작업을 도와줍니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Generate Unit Tests (단위 테스트 작성) - Junit 기반으로 단위 테스트를 작성해 줍니다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-10 오후 11.13.46.png&quot; data-origin-width=&quot;324&quot; data-origin-height=&quot;854&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MT6kx/btsNhI4SASB/lcCn5SSoyGZ9cKdUd3jWk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MT6kx/btsNhI4SASB/lcCn5SSoyGZ9cKdUd3jWk0/img.png&quot; data-alt=&quot;Model 선택&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MT6kx/btsNhI4SASB/lcCn5SSoyGZ9cKdUd3jWk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMT6kx%2FbtsNhI4SASB%2FlcCn5SSoyGZ9cKdUd3jWk0%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;246&quot; height=&quot;648&quot; data-filename=&quot;스크린샷 2025-04-10 오후 11.13.46.png&quot; data-origin-width=&quot;324&quot; data-origin-height=&quot;854&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Model 선택&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI Model도 선택할 수 있습니다.&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;IDE 환경 내에서 AI 도구를 사용하면서 많은 편리함을 느꼈습니다. 특히 리팩토링 과정에서 모듈화를 깔끔하게 해주는 부분이 인상적이었고, 다국어 적용 시 해당 언어에 맞춰 텍스트를 자동으로 작성해 주는 기능이 매우 편하고 만족스러웠습니다. 또한, 디버깅 중에는 직접 추적하기 어려운 오류에 대해 발생 가능성을 제시해 주는 점이 큰 도움이 되었습니다.&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;AI가 알려주는 모든 것이 정답은 아니기에, 참고용으로 사용하면 좋을 것 같다는 생각이 들었습니다.&amp;nbsp;앞으로 사용해 보면서 더 적응해야 할 것 같아 보이네요.&amp;nbsp;&lt;/p&gt;</description>
      <category>Etc</category>
      <category>IntelliJ</category>
      <category>jetbrains ai assistant</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/402</guid>
      <comments>https://daily1313.tistory.com/entry/Intellij-JetBrains-AI-Assistant-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95-%EB%B0%8F-%ED%9B%84%EA%B8%B0#entry402comment</comments>
      <pubDate>Thu, 10 Apr 2025 23:32:44 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] java 메모리에 존재하는 list 데이터 페이징 처리</title>
      <link>https://daily1313.tistory.com/entry/Spring-java-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%97%90-%EC%A1%B4%EC%9E%AC%ED%95%98%EB%8A%94-list-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%8E%98%EC%9D%B4%EC%A7%95-%EC%B2%98%EB%A6%AC</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;프로그램 실행 중에만 존재하는 메모리 데이터에서의 Pagination은 JPA에서 제공하는 Page 객체를 활용하지 못하고, 페이징 처리를 위한 로직을 별도로 구현해줘야 합니다.&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;그래서 DB 조회 없이 메모리 내에서 List 데이터를 페이징처리 하기 위한 Util Class를 만들어봤습니다. 특정 Type에 의존하지 않고, 다양한 타입을 처리할 수 있도록 제네릭을 활용하였습니다.&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KkbNb/btsMhcsbR7g/yxWYQDxGunvB5QJIQ9BOh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KkbNb/btsMhcsbR7g/yxWYQDxGunvB5QJIQ9BOh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KkbNb/btsMhcsbR7g/yxWYQDxGunvB5QJIQ9BOh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKkbNb%2FbtsMhcsbR7g%2FyxWYQDxGunvB5QJIQ9BOh1%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;432&quot; height=&quot;244&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;244&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;pre id=&quot;code_1739351488464&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class PaginatedList&amp;lt;T&amp;gt; {

    private List&amp;lt;T&amp;gt; list;
    private List&amp;lt;List&amp;lt;T&amp;gt;&amp;gt; listOfPages;
    private int pageSize = 10;
    private int currentPage = 0;

    public PaginatedList(List&amp;lt;T&amp;gt; list, int pageSize) {
        this.list = list;
        this.pageSize = pageSize;
        paginateList();
    }
    
    public List&amp;lt;T&amp;gt; getListOfPageNumber(int pageNumber) {
        if (listOfPages == null || pageNumber &amp;gt; listOfPages.size() || pageNumber &amp;lt; 1) {
            return Collections.emptyList();
        }

        currentPage = pageNumber;
        List&amp;lt;T&amp;gt; page = listOfPages.get(--pageNumber);
        return page;
    }
    
    private void paginateList() {
        if (list == null || listOfPages != null) {
            return;
        }

        if (pageSize &amp;lt;= 0 || pageSize &amp;gt; list.size()) {
            pageSize = list.size();
        }

        int numOfPages = (int) Math.ceil((double) list.size() / (double) pageSize);
        listOfPages = new ArrayList&amp;lt;List&amp;lt;T&amp;gt;&amp;gt;(numOfPages);
        for (int pageNum = 0; pageNum &amp;lt; numOfPages;) {
            int from = pageNum * pageSize;
            int to = Math.min(++pageNum * pageSize, list.size());
            listOfPages.add(list.subList(from, to));
        }
    }
}&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;List&amp;lt;T&amp;gt; lists: 전체 리스트 데이터&lt;/li&gt;
&lt;li&gt;List&amp;lt;List&amp;lt;T&amp;gt; listOfPages: lists 데이터를 페이지별로 나눈 데이터&amp;nbsp;&lt;/li&gt;
&lt;li&gt;pageSize: 페이징할 사이즈 (10)&lt;/li&gt;
&lt;li&gt;currentPage: 현재 페이지&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;PaginatedList 생성자&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성자에서 매개변수로 들어오는 list 데이터와 pageSize를 초기화해주고, 페이징 처리를 위한 메서드 (paginateList) 를 호출합니다.&amp;nbsp;&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;paginateList 메서드&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;list에 null이 들어오고, listOfPages 필드가 초기화 되었으면 skip 합니다. (이미 페이징 처리가 된 경우)&lt;/li&gt;
&lt;li&gt;list 크기보다 pageSize가 크면, 하나의 페이지에 표현합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;이후, 전체 List 데이터를 subList 메서드를 통해 from - to로 분할해주고, 각 listOfPages에 추가해 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739352469936&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Data&amp;gt; data = new PaginatedList&amp;lt;&amp;gt;(list, pageSize).getListOfPageNumber(page + 1);
// Paging 처리 및 현재 페이지의 데이터 반환

Pageable pageable = new PageRequest(page, pageSize);

return new PageImpl&amp;lt;&amp;gt;(data, pageable, list.size());&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;이후 Service에서 페이징 Util class를 생성해 준 후 현재 페이지의 데이터를 반환해 줍니다. 그리고, PageImpl 구현체를 이용하여 Page 객체를 반환할 수 있습니다.&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/19688235/how-to-implement-pagination-on-a-list&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://stackoverflow.com/questions/19688235/how-to-implement-pagination-on-a-list&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739352651390&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;How to implement pagination on a list?&quot; data-og-description=&quot;Is there any library that can be used to implement paging for a list? Let' assume I have a space of 10 lines, and the user can select if he wants to scroll forward or backward by page (thus +- 10 ...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/19688235/how-to-implement-pagination-on-a-list&quot; data-og-url=&quot;https://stackoverflow.com/questions/19688235/how-to-implement-pagination-on-a-list&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/prSTg/hyYf4a1BuL/AVJCEFW6yPz3KLbkq6ldv0/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/19688235/how-to-implement-pagination-on-a-list&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/19688235/how-to-implement-pagination-on-a-list&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/prSTg/hyYf4a1BuL/AVJCEFW6yPz3KLbkq6ldv0/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&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;How to implement pagination on a list?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Is there any library that can be used to implement paging for a list? Let' assume I have a space of 10 lines, and the user can select if he wants to scroll forward or backward by page (thus +- 10 ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>pagination</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/401</guid>
      <comments>https://daily1313.tistory.com/entry/Spring-java-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%97%90-%EC%A1%B4%EC%9E%AC%ED%95%98%EB%8A%94-list-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%8E%98%EC%9D%B4%EC%A7%95-%EC%B2%98%EB%A6%AC#entry401comment</comments>
      <pubDate>Wed, 12 Feb 2025 18:30:59 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] @Formula annotation을 활용하여 count 조회 성능 개선</title>
      <link>https://daily1313.tistory.com/entry/Spring-Formula-annotation%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%98%EC%97%AC-count-%EC%A1%B0%ED%9A%8C-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0</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;사내 애플리케이션에 장비와 사용자 수를 count 하는 쿼리가 존재했습니다. 조회 쿼리 성능을 개선하기 위함임은 알 수 있지만, 구체적으로 어떻게 동작하는지도 알아보려고 합니다.&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;647&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSQGba/btsMb2SB8kk/xlTga8W1mTNFQVPPQ2o8B0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSQGba/btsMb2SB8kk/xlTga8W1mTNFQVPPQ2o8B0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSQGba/btsMb2SB8kk/xlTga8W1mTNFQVPPQ2o8B0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSQGba%2FbtsMb2SB8kk%2FxlTga8W1mTNFQVPPQ2o8B0%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;558&quot; height=&quot;442&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;647&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; @Formula&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;entity 내에서 실제로 db 스키마에 존재하지 않는 가상 컬럼 (jpa 상에서만 존재하고, 실제 db에 존재하지 않는 컬럼)을 정의할 수 있는 기능&amp;nbsp;&lt;/li&gt;
&lt;li&gt;다른 컬럼들의 값에 기반하여 계산된 값을 표현할 수 있으며, &lt;b&gt;엔티티를 조회할때만 계산되어 사용&lt;/b&gt;됩니다. (Read-Only)&lt;/li&gt;
&lt;li&gt;Default: EAGER 전략&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;이제 @Formula 어노테이션을 적용하였을 때 조회 쿼리와 lazy loading으로 size() 메서드를 호출하였을 때의 쿼리 차이를 비교해 보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739179222528&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Formula(&quot;(select count(*) from conf_device t2 where t2.region_id = id)&quot;)
public int deviceCount;

@Formula(&quot;(select count(*) from conf_user t3 where t3.region_id = id)&quot;)
public int userCount;&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;&lt;b&gt;@Formula 어노테이션 적용 시 쿼리 (root entity가 1개일 때의 기준: 1방)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739179421513&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Hibernate: 
    select
        region0_.id as id1_12_,
        region0_.ip as ip2_12_,
        region0_.name as name3_12_,
        region0_.port as port4_12_,
        region0_.reg_date as reg_date5_12_,
        region0_.ssl_enabled as ssl_enab6_12_,
        region0_.status as status7_12_,
        region0_.upd_date as upd_date8_12_,
        region0_.uptime as uptime9_12_,
        region0_.version as version10_12_,
        (select
            count(*) 
        from
            conf_device t2 
        where
            t2.region_id = region0_.id) as formula0_,
        (select
            count(*) 
        from
            conf_user t3 
        where
            t3.region_id = region0_.id) as formula1_ 
    from
        conf_region region0_ 
    order by
        region0_.id desc limit ?&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;&lt;b&gt;size() 메서드 호출 시 쿼리 &lt;b&gt;(root entity가 1개일 때의 기준: 7방)&lt;/b&gt; &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739179512118&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public int getDeviceCount() {
    return this.devices.size();   
}
    
public int getUserCount() {
    return this.users.size();      
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739179581950&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Hibernate: 
    select
        region0_.id as id1_12_,
        region0_.ip as ip2_12_,
        region0_.name as name3_12_,
        region0_.port as port4_12_,
        region0_.reg_date as reg_date5_12_,
        region0_.ssl_enabled as ssl_enab6_12_,
        region0_.status as status7_12_,
        region0_.upd_date as upd_date8_12_,
        region0_.uptime as uptime9_12_,
        region0_.version as version10_12_ 
    from
        conf_region region0_ 
    order by
        region0_.id desc limit ?
Hibernate: 
    select
        users0_.region_id as region_i6_12_0_,
        users0_.id as id1_13_0_,
        users0_.id as id1_13_1_,
        users0_.email as email2_13_1_,
        users0_.name as name3_13_1_,
        users0_.passwd as passwd4_13_1_,
        users0_.reg_date as reg_date5_13_1_,
        users0_.region_id as region_i6_13_1_,
        users0_.type as type7_13_1_,
        users0_.upd_date as upd_date8_13_1_,
        users0_.user_id as user_id9_13_1_,
        users0_.user_identifier as user_id10_13_1_ 
    from
        conf_user users0_ 
    where
        users0_.region_id=?
Hibernate: 
    select
        userroles0_.user_id as user_id5_13_0_,
        userroles0_.id as id1_14_0_,
        userroles0_.id as id1_14_1_,
        userroles0_.refer_id as refer_id2_14_1_,
        userroles0_.refer_type as refer_ty3_14_1_,
        userroles0_.type as type4_14_1_,
        userroles0_.user_id as user_id5_14_1_ 
    from
        conf_user_role userroles0_ 
    where
        userroles0_.user_id=?
Hibernate: 
    select
        userroles0_.user_id as user_id5_13_0_,
        userroles0_.id as id1_14_0_,
        userroles0_.id as id1_14_1_,
        userroles0_.refer_id as refer_id2_14_1_,
        userroles0_.refer_type as refer_ty3_14_1_,
        userroles0_.type as type4_14_1_,
        userroles0_.user_id as user_id5_14_1_ 
    from
        conf_user_role userroles0_ 
    where
        userroles0_.user_id=?
Hibernate: 
    select
        userroles0_.user_id as user_id5_13_0_,
        userroles0_.id as id1_14_0_,
        userroles0_.id as id1_14_1_,
        userroles0_.refer_id as refer_id2_14_1_,
        userroles0_.refer_type as refer_ty3_14_1_,
        userroles0_.type as type4_14_1_,
        userroles0_.user_id as user_id5_14_1_ 
    from
        conf_user_role userroles0_ 
    where
        userroles0_.user_id=?
Hibernate: 
    select
        userroles0_.user_id as user_id5_13_0_,
        userroles0_.id as id1_14_0_,
        userroles0_.id as id1_14_1_,
        userroles0_.refer_id as refer_id2_14_1_,
        userroles0_.refer_type as refer_ty3_14_1_,
        userroles0_.type as type4_14_1_,
        userroles0_.user_id as user_id5_14_1_ 
    from
        conf_user_role userroles0_ 
    where
        userroles0_.user_id=?
Hibernate: 
    select
        devices0_.region_id as region_i9_12_0_,
        devices0_.id as id1_3_0_,
        devices0_.id as id1_3_1_,
        devices0_.device_id as device_i2_3_1_,
        devices0_.device_type as device_t3_3_1_,
        devices0_.mac as mac4_3_1_,
        devices0_.model as model5_3_1_,
        devices0_.name as name6_3_1_,
        devices0_.oper_status as oper_sta7_3_1_,
        devices0_.reg_date as reg_date8_3_1_,
        devices0_.region_id as region_i9_3_1_,
        devices0_.serial as serial10_3_1_ 
    from
        conf_device devices0_ 
    where
        devices0_.region_id=?&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;count를 조회하기 위해 불필요한 필드까지 모두 조회하게 됩니다. 이를 해결하기 위해서 SQL 하위 쿼리로 entity를 조회할 때 count도 조회할 수 있는 @Formula annotation을 적용하여 count만 조회할 수 있게 됩니다. 해당 어노테이션은 native sql을 사용하기에 어노테이션 내부에 db에서 직접 실행할 수 있는 sql문을 넣어줘야 합니다.&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;하지만, 조회할 때만 값을 계산해 주기에 값이 업데이트 됐을 때 자동으로 count가 늘어나지 않습니다. refresh를 해주어야 다시 count가 증가하기에 이 부분을 염두해두고 사용하셔야 됩니다.&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dkswnkk.tistory.com/734&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dkswnkk.tistory.com/734&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739180151273&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Hibernate의 @Formula를 이용한 연관 관계 엔티티 집계&quot; data-og-description=&quot;개요 Hibernate의 @Formula 어노테이션은 엔티티 클래스 내에서 실제로 데이터베이스 스키마에 존재하지 않는 '가상 컬럼'을 정의할 수 있는 기능입니다. @Formula를 사용하면 다른 컬럼들의 값에 기반&quot; data-og-host=&quot;dkswnkk.tistory.com&quot; data-og-source-url=&quot;https://dkswnkk.tistory.com/734&quot; data-og-url=&quot;https://dkswnkk.tistory.com/734&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ePjgK/hyYcd1v5XY/60KoYgL4ogg5MprKsKPsEk/img.png?width=204&amp;amp;height=211&amp;amp;face=0_0_204_211,https://scrap.kakaocdn.net/dn/dogR26/hyYf243Wyg/8jBYnsAqVB3GxaDJgU9ww1/img.png?width=204&amp;amp;height=211&amp;amp;face=0_0_204_211&quot;&gt;&lt;a href=&quot;https://dkswnkk.tistory.com/734&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dkswnkk.tistory.com/734&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ePjgK/hyYcd1v5XY/60KoYgL4ogg5MprKsKPsEk/img.png?width=204&amp;amp;height=211&amp;amp;face=0_0_204_211,https://scrap.kakaocdn.net/dn/dogR26/hyYf243Wyg/8jBYnsAqVB3GxaDJgU9ww1/img.png?width=204&amp;amp;height=211&amp;amp;face=0_0_204_211');&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;Hibernate의 @Formula를 이용한 연관 관계 엔티티 집계&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;개요 Hibernate의 @Formula 어노테이션은 엔티티 클래스 내에서 실제로 데이터베이스 스키마에 존재하지 않는 '가상 컬럼'을 정의할 수 있는 기능입니다. @Formula를 사용하면 다른 컬럼들의 값에 기반&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dkswnkk.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.popit.kr/jpa-%EC%97%94%ED%84%B0%ED%8B%B0-%EC%B9%B4%EC%9A%B4%ED%8A%B8-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.popit.kr/jpa-%EC%97%94%ED%84%B0%ED%8B%B0-%EC%B9%B4%EC%9A%B4%ED%8A%B8-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739180159710&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;JPA 엔터티 카운트 성능 개선하기 | Popit&quot; data-og-description=&quot;JPA Java Persistence API 로 애그리게잇 을 구현할 때면 흔히 루트 엔터티(전역 식별성을 지니며 주체로 쓰이는 엔터티)에 연관 엔터티 컬렉션을 매핑한다. 때때로 루트 엔터티는 연관 엔터티 컬렉션&quot; data-og-host=&quot;www.popit.kr&quot; data-og-source-url=&quot;https://www.popit.kr/jpa-%EC%97%94%ED%84%B0%ED%8B%B0-%EC%B9%B4%EC%9A%B4%ED%8A%B8-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0/&quot; data-og-url=&quot;http://www.popit.kr/jpa-엔터티-카운트-성능-개선하기/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dkt0Fv/hyYcksIIfS/VLHWG9YZqvm65hZKFNBCf1/img.png?width=1016&amp;amp;height=678&amp;amp;face=0_0_1016_678,https://scrap.kakaocdn.net/dn/1cFMA/hyYcc2A9bI/eA3rffpuHkEyi47X4KB5Rk/img.png?width=1016&amp;amp;height=678&amp;amp;face=0_0_1016_678,https://scrap.kakaocdn.net/dn/cP5aXB/hyYfZUNoWv/FjrqmuByYsH8vrtMF6kSKK/img.png?width=1024&amp;amp;height=698&amp;amp;face=0_0_1024_698&quot;&gt;&lt;a href=&quot;https://www.popit.kr/jpa-%EC%97%94%ED%84%B0%ED%8B%B0-%EC%B9%B4%EC%9A%B4%ED%8A%B8-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.popit.kr/jpa-%EC%97%94%ED%84%B0%ED%8B%B0-%EC%B9%B4%EC%9A%B4%ED%8A%B8-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dkt0Fv/hyYcksIIfS/VLHWG9YZqvm65hZKFNBCf1/img.png?width=1016&amp;amp;height=678&amp;amp;face=0_0_1016_678,https://scrap.kakaocdn.net/dn/1cFMA/hyYcc2A9bI/eA3rffpuHkEyi47X4KB5Rk/img.png?width=1016&amp;amp;height=678&amp;amp;face=0_0_1016_678,https://scrap.kakaocdn.net/dn/cP5aXB/hyYfZUNoWv/FjrqmuByYsH8vrtMF6kSKK/img.png?width=1024&amp;amp;height=698&amp;amp;face=0_0_1024_698');&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;JPA 엔터티 카운트 성능 개선하기 | Popit&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;JPA Java Persistence API 로 애그리게잇 을 구현할 때면 흔히 루트 엔터티(전역 식별성을 지니며 주체로 쓰이는 엔터티)에 연관 엔터티 컬렉션을 매핑한다. 때때로 루트 엔터티는 연관 엔터티 컬렉션&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.popit.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>@formula</category>
      <category>count 조회 성능 개선</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/400</guid>
      <comments>https://daily1313.tistory.com/entry/Spring-Formula-annotation%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%98%EC%97%AC-count-%EC%A1%B0%ED%9A%8C-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0#entry400comment</comments>
      <pubDate>Mon, 10 Feb 2025 18:36:10 +0900</pubDate>
    </item>
    <item>
      <title>[필리핀 ] 마닐라 3일차 2025.01.20 (베니스 그랜드 캐널 몰, 산티아고 요새, SM몰 오브 아시아)</title>
      <link>https://daily1313.tistory.com/entry/%ED%95%84%EB%A6%AC%ED%95%80%F0%9F%87%B5%F0%9F%87%AD-%EB%A7%88%EB%8B%90%EB%9D%BC-3%EC%9D%BC%EC%B0%A8-20250120-%EB%B2%A0%EB%8B%88%EC%8A%A4-%EA%B7%B8%EB%9E%9C%EB%93%9C-%EC%BA%90%EB%84%90-%EB%AA%B0-%EC%82%B0%ED%8B%B0%EC%95%84%EA%B3%A0-%EC%9A%94%EC%83%88-SM%EB%AA%B0-%EC%98%A4%EB%B8%8C-%EC%95%84%EC%8B%9C%EC%95%84</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;3일 차에는 원래 계획대로 실천했습니다. 개인적으로 가장 알차게 보냈던 날이라고 생각합니다.&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&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgUmjI/btsL3gb4Noq/UKDwtkkGuLvxb0iuOilIQ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgUmjI/btsL3gb4Noq/UKDwtkkGuLvxb0iuOilIQ1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-46 027.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgUmjI/btsL3gb4Noq/UKDwtkkGuLvxb0iuOilIQ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgUmjI%2FbtsL3gb4Noq%2FUKDwtkkGuLvxb0iuOilIQ1%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crttUQ/btsL4ybP2dq/Gih9NeXbBqVeLsQFmW3tw0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crttUQ/btsL4ybP2dq/Gih9NeXbBqVeLsQFmW3tw0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-46 026.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crttUQ/btsL4ybP2dq/Gih9NeXbBqVeLsQFmW3tw0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrttUQ%2FbtsL4ybP2dq%2FGih9NeXbBqVeLsQFmW3tw0%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7N0EY/btsL2Kq8Ggl/84YhyMH2K7G80k9x4b8bP1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7N0EY/btsL2Kq8Ggl/84YhyMH2K7G80k9x4b8bP1/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-44 023.jpeg&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7N0EY/btsL2Kq8Ggl/84YhyMH2K7G80k9x4b8bP1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7N0EY%2FbtsL2Kq8Ggl%2F84YhyMH2K7G80k9x4b8bP1%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjeu30/btsL5s9Cv1F/Iw0oZjleJGaHQtp1MMnzD0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjeu30/btsL5s9Cv1F/Iw0oZjleJGaHQtp1MMnzD0/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-45 024.jpeg&quot; style=&quot;width: 63.2558%; margin-right: 10px;&quot; data-widthpercent=&quot;64&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjeu30/btsL5s9Cv1F/Iw0oZjleJGaHQtp1MMnzD0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjeu30%2FbtsL5s9Cv1F%2FIw0oZjleJGaHQtp1MMnzD0%2Fimg.jpg&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;4032&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjw2IK/btsL3Yvh4RU/yjtyvhfI2THuH7Jmlh7K50/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjw2IK/btsL3Yvh4RU/yjtyvhfI2THuH7Jmlh7K50/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-widthpercent=&quot;36&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-45 025.jpeg&quot; style=&quot;width: 35.5814%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjw2IK/btsL3Yvh4RU/yjtyvhfI2THuH7Jmlh7K50/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjw2IK%2FbtsL3Yvh4RU%2FyjtyvhfI2THuH7Jmlh7K50%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjnVye/btsL24CV2O0/kOqb8w6qEw0TgaABNXEfy0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjnVye/btsL24CV2O0/kOqb8w6qEw0TgaABNXEfy0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-46 028.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjnVye/btsL24CV2O0/kOqb8w6qEw0TgaABNXEfy0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjnVye%2FbtsL24CV2O0%2FkOqb8w6qEw0TgaABNXEfy0%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byyk7R/btsL4aWwHgn/P0vxuDAGqY7Jo4NGrKkeQ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byyk7R/btsL4aWwHgn/P0vxuDAGqY7Jo4NGrKkeQ1/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-47 029.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byyk7R/btsL4aWwHgn/P0vxuDAGqY7Jo4NGrKkeQ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbyyk7R%2FbtsL4aWwHgn%2FP0vxuDAGqY7Jo4NGrKkeQ1%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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;이후 피규어 구경을 하고, 필리핀 유적지인 산티아고 요새로 이동하였습니다.&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZSgtq/btsL40ezq6i/ukiZGyk1kNvoEpfwP7kyXk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZSgtq/btsL40ezq6i/ukiZGyk1kNvoEpfwP7kyXk/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-02-09 004.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZSgtq/btsL40ezq6i/ukiZGyk1kNvoEpfwP7kyXk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZSgtq%2FbtsL40ezq6i%2FukiZGyk1kNvoEpfwP7kyXk%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lcbJG/btsL4x40Fgt/v91KOqMmoDtqgZ05oQ01X1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lcbJG/btsL4x40Fgt/v91KOqMmoDtqgZ05oQ01X1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-48 030.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lcbJG/btsL4x40Fgt/v91KOqMmoDtqgZ05oQ01X1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlcbJG%2FbtsL4x40Fgt%2Fv91KOqMmoDtqgZ05oQ01X1%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmpim3/btsL30fxqvd/RYdD0SmjBSr92vBUxfuwjk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmpim3/btsL30fxqvd/RYdD0SmjBSr92vBUxfuwjk/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-02-09 005.jpeg&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmpim3/btsL30fxqvd/RYdD0SmjBSr92vBUxfuwjk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcmpim3%2FbtsL30fxqvd%2FRYdD0SmjBSr92vBUxfuwjk%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJttjf/btsL5kw3r0D/9SlQ9r5z5hfPFSD5xlfu5K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJttjf/btsL5kw3r0D/9SlQ9r5z5hfPFSD5xlfu5K/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-02-09 006.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJttjf/btsL5kw3r0D/9SlQ9r5z5hfPFSD5xlfu5K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJttjf%2FbtsL5kw3r0D%2F9SlQ9r5z5hfPFSD5xlfu5K%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JugS5/btsL5maANwz/JpqQgR6pK7a5WaLv7eRUE1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JugS5/btsL5maANwz/JpqQgR6pK7a5WaLv7eRUE1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;33.33&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-02-07 001.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JugS5/btsL5maANwz/JpqQgR6pK7a5WaLv7eRUE1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJugS5%2FbtsL5maANwz%2FJpqQgR6pK7a5WaLv7eRUE1%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MImcd/btsL3oHPBgH/gCKgll7cZeav4QaIdwEyP1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MImcd/btsL3oHPBgH/gCKgll7cZeav4QaIdwEyP1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;33.34&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-02-08 002.jpeg&quot; style=&quot;width: 32.5581%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MImcd/btsL3oHPBgH/gCKgll7cZeav4QaIdwEyP1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMImcd%2FbtsL3oHPBgH%2FgCKgll7cZeav4QaIdwEyP1%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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;산티아고 요새는 필리핀의 매우 유명한 유적지인데, 스페인 식민 정부에 대항했던 독립운동가 &quot;호세 리잘&quot;이 수감되었던 장소입니다. 스페인 군대 기지, 미 육군 본부, 지하 감옥 등 다양한 시설로 사용되었다고 합니다.&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;또한 내부에 파시그 강의 풍경도 볼 수 있습니다. 정말 볼거리 가 많았으며, 매년 &quot;호세 리잘&quot;을 추모하기 위해 수많은 사람들이 방문한다고 합니다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-02-08 003.jpeg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2BEFR/btsL2G3mEGX/y2tNmc1pcOQcqZrKmuOdLk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2BEFR/btsL2G3mEGX/y2tNmc1pcOQcqZrKmuOdLk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2BEFR/btsL2G3mEGX/y2tNmc1pcOQcqZrKmuOdLk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2BEFR%2FbtsL2G3mEGX%2Fy2tNmc1pcOQcqZrKmuOdLk%2Fimg.jpg&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;463&quot; height=&quot;617&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-02-08 003.jpeg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&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&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjKzT4/btsL4yJBw0U/hn9ik814BSkfSxQHrmhtC0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjKzT4/btsL4yJBw0U/hn9ik814BSkfSxQHrmhtC0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-02-11 012.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjKzT4/btsL4yJBw0U/hn9ik814BSkfSxQHrmhtC0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjKzT4%2FbtsL4yJBw0U%2Fhn9ik814BSkfSxQHrmhtC0%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgJiaF/btsL3A9ecEJ/AJMNO4eSUh074B0UQ3o0R0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgJiaF/btsL3A9ecEJ/AJMNO4eSUh074B0UQ3o0R0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-02-11 011.jpeg&quot; data-widthpercent=&quot;33.33&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgJiaF/btsL3A9ecEJ/AJMNO4eSUh074B0UQ3o0R0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgJiaF%2FbtsL3A9ecEJ%2FAJMNO4eSUh074B0UQ3o0R0%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/edAeKk/btsL3jT8rdi/uZKggFm7cuGLrjanNshynK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/edAeKk/btsL3jT8rdi/uZKggFm7cuGLrjanNshynK/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-02-11 010.jpeg&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/edAeKk/btsL3jT8rdi/uZKggFm7cuGLrjanNshynK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FedAeKk%2FbtsL3jT8rdi%2FuZKggFm7cuGLrjanNshynK%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjZKKf/btsL4FhxPo2/Syjx2F3UHgbstgbRcxoLiK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjZKKf/btsL4FhxPo2/Syjx2F3UHgbstgbRcxoLiK/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-02-13 017.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjZKKf/btsL4FhxPo2/Syjx2F3UHgbstgbRcxoLiK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjZKKf%2FbtsL4FhxPo2%2FSyjx2F3UHgbstgbRcxoLiK%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjnZuX/btsL3pfHxO5/oKby7K6ErjaUH3OukX8vWK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjnZuX/btsL3pfHxO5/oKby7K6ErjaUH3OukX8vWK/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;1400&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-02-12 014.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjnZuX/btsL3pfHxO5/oKby7K6ErjaUH3OukX8vWK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjnZuX%2FbtsL3pfHxO5%2FoKby7K6ErjaUH3OukX8vWK%2Fimg.jpg&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;1050&quot; height=&quot;1400&quot;/&gt;&lt;/span&gt;&lt;/div&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&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brfWPO/btsL4Hfnc5c/a2x1lAsvlkNhq50FG7wec0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brfWPO/btsL4Hfnc5c/a2x1lAsvlkNhq50FG7wec0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-02-11 013.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brfWPO/btsL4Hfnc5c/a2x1lAsvlkNhq50FG7wec0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrfWPO%2FbtsL4Hfnc5c%2Fa2x1lAsvlkNhq50FG7wec0%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cK26xS/btsL448X72h/cNHOR3ASSZ55vkq0e3hR9K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cK26xS/btsL448X72h/cNHOR3ASSZ55vkq0e3hR9K/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-02-12 015.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cK26xS/btsL448X72h/cNHOR3ASSZ55vkq0e3hR9K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcK26xS%2FbtsL448X72h%2FcNHOR3ASSZ55vkq0e3hR9K%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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;마지막 장소로 SM몰을 갔는데, 진짜 너무 커서 깜짝 놀랐습니다. 전자기기 (Apple, 삼성, LG ..), 디즈니몰, 옷가게, 슈퍼마켓, 식당 등.. 웬만한 거는 전부 다 있었습니다. 너무 커서 그런가 여행 중 못 봤던 한국인도 산티아고 요새 이후로 여기에서 오랜만에 봤는데 조금 반가웠습니다. 여기에서 지인들 선물 구매를 하고 마닐라 여행을 마쳤습니다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Daily/Trip</category>
      <category>SM몰오브아시아</category>
      <category>마닐라 여행</category>
      <category>베니스 그랜드 캐널 몰</category>
      <category>산티아고 요새</category>
      <category>인트라무로스</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/399</guid>
      <comments>https://daily1313.tistory.com/entry/%ED%95%84%EB%A6%AC%ED%95%80%F0%9F%87%B5%F0%9F%87%AD-%EB%A7%88%EB%8B%90%EB%9D%BC-3%EC%9D%BC%EC%B0%A8-20250120-%EB%B2%A0%EB%8B%88%EC%8A%A4-%EA%B7%B8%EB%9E%9C%EB%93%9C-%EC%BA%90%EB%84%90-%EB%AA%B0-%EC%82%B0%ED%8B%B0%EC%95%84%EA%B3%A0-%EC%9A%94%EC%83%88-SM%EB%AA%B0-%EC%98%A4%EB%B8%8C-%EC%95%84%EC%8B%9C%EC%95%84#entry399comment</comments>
      <pubDate>Sat, 1 Feb 2025 02:56:51 +0900</pubDate>
    </item>
    <item>
      <title>[필리핀 ] 마닐라 2일차 2025.01.19 (따가이따이, 그린벨트 쇼핑몰)</title>
      <link>https://daily1313.tistory.com/entry/%ED%95%84%EB%A6%AC%ED%95%80%F0%9F%87%B5%F0%9F%87%AD-%EB%A7%88%EB%8B%90%EB%9D%BC-2%EC%9D%BC%EC%B0%A8-20250119-%EB%94%B0%EA%B0%80%EC%9D%B4%EB%94%B0%EC%9D%B4-%EA%B7%B8%EB%A6%B0%EB%B2%A8%ED%8A%B8-%EC%87%BC%ED%95%91%EB%AA%B0</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;2일 차에는 Grab에서 만난 택시기사님이 추천해 준 따가이따이라는 동네를 방문했습니다. (숙소에서 70km 정도 떨어진 동네였습니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwUdcv/btsL3lLdRBv/1amspe3qNtkbmpC69dzkuK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwUdcv/btsL3lLdRBv/1amspe3qNtkbmpC69dzkuK/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-40 008.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwUdcv/btsL3lLdRBv/1amspe3qNtkbmpC69dzkuK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwUdcv%2FbtsL3lLdRBv%2F1amspe3qNtkbmpC69dzkuK%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjPnhf/btsL3PL1gxP/tLicjczJVvp5IUFZVNRBt1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjPnhf/btsL3PL1gxP/tLicjczJVvp5IUFZVNRBt1/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-40 009.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjPnhf/btsL3PL1gxP/tLicjczJVvp5IUFZVNRBt1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjPnhf%2FbtsL3PL1gxP%2FtLicjczJVvp5IUFZVNRBt1%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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;b&gt;해당 장소로 출발 전에 스타벅스에 들려 커피를 마셨는데, 역시나 경비원분은 계셨습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-01 오전 1.46.49.png&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7jMFt/btsL5rJDqbh/J58sy0AzV1jkXcR9K2UW3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7jMFt/btsL5rJDqbh/J58sy0AzV1jkXcR9K2UW3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7jMFt/btsL5rJDqbh/J58sy0AzV1jkXcR9K2UW3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7jMFt%2FbtsL5rJDqbh%2FJ58sy0AzV1jkXcR9K2UW3k%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;606&quot; height=&quot;459&quot; data-filename=&quot;스크린샷 2025-02-01 오전 1.46.49.png&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;872&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;의도치 않게 친구와 함께 생각지도 못하게 로컬 놀이공원을 가게 되었습니다. 한국으로 치면 롯데월드, 에버랜드가 아닌 경주월드 정도로 생각하면 좋을 것 같습니다. (입장료는 한국 돈으로 3000원이길래 만족스러웠습니다)&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGdrsH/btsL4Jc2BWL/RgaUfO0S3E1jjJbJwIcJ3K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGdrsH/btsL4Jc2BWL/RgaUfO0S3E1jjJbJwIcJ3K/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-42 013.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGdrsH/btsL4Jc2BWL/RgaUfO0S3E1jjJbJwIcJ3K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGdrsH%2FbtsL4Jc2BWL%2FRgaUfO0S3E1jjJbJwIcJ3K%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LQVMy/btsL5kKCyR6/dmWToePgiUW4ZbiviKCd4k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LQVMy/btsL5kKCyR6/dmWToePgiUW4ZbiviKCd4k/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-42 015.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LQVMy/btsL5kKCyR6/dmWToePgiUW4ZbiviKCd4k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLQVMy%2FbtsL5kKCyR6%2FdmWToePgiUW4ZbiviKCd4k%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YU70b/btsL3RbVozp/ES1yKrNhEEURcrkQaA9Vsk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YU70b/btsL3RbVozp/ES1yKrNhEEURcrkQaA9Vsk/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-42 014.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YU70b/btsL3RbVozp/ES1yKrNhEEURcrkQaA9Vsk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYU70b%2FbtsL3RbVozp%2FES1yKrNhEEURcrkQaA9Vsk%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d9UnsQ/btsL4Xa5gZw/bSaTIY3ptDCDwc0iJbkpW1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d9UnsQ/btsL4Xa5gZw/bSaTIY3ptDCDwc0iJbkpW1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-57-43 001.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d9UnsQ/btsL4Xa5gZw/bSaTIY3ptDCDwc0iJbkpW1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd9UnsQ%2FbtsL4Xa5gZw%2FbSaTIY3ptDCDwc0iJbkpW1%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QtQjs/btsL3gpCr60/OGkHUFDKDLau8kHEM4RcE0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QtQjs/btsL3gpCr60/OGkHUFDKDLau8kHEM4RcE0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-57-44 002.jpeg&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QtQjs/btsL3gpCr60/OGkHUFDKDLau8kHEM4RcE0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQtQjs%2FbtsL3gpCr60%2FOGkHUFDKDLau8kHEM4RcE0%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nPtij/btsL3scnTJ0/jkY6vACYJfyJDB3DxzvLn1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nPtij/btsL3scnTJ0/jkY6vACYJfyJDB3DxzvLn1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-43 017.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nPtij/btsL3scnTJ0/jkY6vACYJfyJDB3DxzvLn1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnPtij%2FbtsL3scnTJ0%2FjkY6vACYJfyJDB3DxzvLn1%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LaB1r/btsL4AgnKws/dWA6IsltdkTTtjk04KtIN0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LaB1r/btsL4AgnKws/dWA6IsltdkTTtjk04KtIN0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-43 016.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LaB1r/btsL4AgnKws/dWA6IsltdkTTtjk04KtIN0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLaB1r%2FbtsL4AgnKws%2FdWA6IsltdkTTtjk04KtIN0%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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;친구와 저는 따가이따이에서 힘을 많이 뺏기에 (편도 2시간 소요, 왕복 4시간 소요) , 그린벨트에서는 조금만 둘러보다가 숙소로 복귀하였습니다. 이날은 그린벨트보다도 로컬 동네를 구경해 볼 수 있는 값진 경험을 하게 돼서 기분이 나름 좋았습니다. 다음에 기회가 되면, 놀이공원이 아닌 화산섬 근처로 꼭 가봐야겠네요.&amp;nbsp;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daily/Trip</category>
      <category>그린벨트</category>
      <category>따가이따이</category>
      <category>마닐라 여행</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/398</guid>
      <comments>https://daily1313.tistory.com/entry/%ED%95%84%EB%A6%AC%ED%95%80%F0%9F%87%B5%F0%9F%87%AD-%EB%A7%88%EB%8B%90%EB%9D%BC-2%EC%9D%BC%EC%B0%A8-20250119-%EB%94%B0%EA%B0%80%EC%9D%B4%EB%94%B0%EC%9D%B4-%EA%B7%B8%EB%A6%B0%EB%B2%A8%ED%8A%B8-%EC%87%BC%ED%95%91%EB%AA%B0#entry398comment</comments>
      <pubDate>Sat, 1 Feb 2025 02:07:30 +0900</pubDate>
    </item>
    <item>
      <title>[필리핀 ] 마닐라 1일차 2025.01.18 (니노이 아키노 국제공항, 세인트 자일스 호텔 이동 과정)</title>
      <link>https://daily1313.tistory.com/entry/%ED%95%84%EB%A6%AC%ED%95%80%F0%9F%87%B5%F0%9F%87%AD-%EB%A7%88%EB%8B%90%EB%9D%BC-1%EC%9D%BC%EC%B0%A8-20250118-%EB%8B%88%EB%85%B8%EC%9D%B4-%EC%95%84%ED%82%A4%EB%85%B8-%EA%B5%AD%EC%A0%9C%EA%B3%B5%ED%95%AD-%EC%84%B8%EC%9D%B8%ED%8A%B8-%EC%9E%90%EC%9D%BC%EC%8A%A4-%ED%98%B8%ED%85%94-%EC%9D%B4%EB%8F%99-%EA%B3%BC%EC%A0%95</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;최근에 마닐라를 다녀왔기에 여행 포스팅을 하려고 합니다.&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;비행시간이 오후 1시 차였기에 오전 10시까지 인천 국제공항에서 만났습니다. 만나서 체크인 및 etravel 등록을 미리 하고 커피를 마시러 갔습니다.&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;체크인과 이트래블을 마친 후 11시쯤 되었는데, 친구와 아무 생각 없이 제육과 돈까스를 먹으러 갔습니다. 맛있게 먹고 나니 11시 40분 정도 되었습니다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-37 001.jpeg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WPD2C/btsL3iOxa1l/Y0zgbNekpvhSAXFlSak4IK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WPD2C/btsL3iOxa1l/Y0zgbNekpvhSAXFlSak4IK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WPD2C/btsL3iOxa1l/Y0zgbNekpvhSAXFlSak4IK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWPD2C%2FbtsL3iOxa1l%2FY0zgbNekpvhSAXFlSak4IK%2Fimg.jpg&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;466&quot; height=&quot;621&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-37 001.jpeg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&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;내린 후 터미널에서 나오니 호객행위하는 사람들이 너무 많았습니다. 호객행위 하는 사람 중 한 명이 핸드폰으로 이상한 표를 보여주면서 택시비를 제시했는데, 그냥 저희끼리 간다고 얘기하고 Grab을 통해 바로 숙소로 이동하였습니다. (실제로 Grab과 비교했을 때 5배 이상 불렀습니다)&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;세인트 자일스라는 리뷰 좋은 호텔인데, 가격 대비 매우 만족스러웠습니다. (1박 25000원)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vCoUi/btsL3rEuTIY/IMQGDgQQwDqFVasDwIdKCk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vCoUi/btsL3rEuTIY/IMQGDgQQwDqFVasDwIdKCk/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-41-21 001.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vCoUi/btsL3rEuTIY/IMQGDgQQwDqFVasDwIdKCk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvCoUi%2FbtsL3rEuTIY%2FIMQGDgQQwDqFVasDwIdKCk%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SKCQV/btsL5qRuRjH/7ICIKdGg77Jtloy2RbHNik/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SKCQV/btsL5qRuRjH/7ICIKdGg77Jtloy2RbHNik/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-41-21 002.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SKCQV/btsL5qRuRjH/7ICIKdGg77Jtloy2RbHNik/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSKCQV%2FbtsL5qRuRjH%2F7ICIKdGg77Jtloy2RbHNik%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b67hUG/btsL4cGI7bw/V1zmX3HfWIO222MIXiJQDK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b67hUG/btsL4cGI7bw/V1zmX3HfWIO222MIXiJQDK/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-44 021.jpeg&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b67hUG/btsL4cGI7bw/V1zmX3HfWIO222MIXiJQDK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb67hUG%2FbtsL4cGI7bw%2FV1zmX3HfWIO222MIXiJQDK%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPa3sh/btsL3Ag6Bab/NcdwmV2jkOzrAbijwh9dbk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPa3sh/btsL3Ag6Bab/NcdwmV2jkOzrAbijwh9dbk/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-38 003.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPa3sh/btsL3Ag6Bab/NcdwmV2jkOzrAbijwh9dbk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPa3sh%2FbtsL3Ag6Bab%2FNcdwmV2jkOzrAbijwh9dbk%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbKvV1/btsL3OM1SIq/1SLxKikjGbAKd7ThQuyKtK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbKvV1/btsL3OM1SIq/1SLxKikjGbAKd7ThQuyKtK/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-38 004.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbKvV1/btsL3OM1SIq/1SLxKikjGbAKd7ThQuyKtK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbKvV1%2FbtsL3OM1SIq%2F1SLxKikjGbAKd7ThQuyKtK%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzKzRw/btsL4b8VPUm/wK6W43MeyQNhlMimIcymT1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzKzRw/btsL4b8VPUm/wK6W43MeyQNhlMimIcymT1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-39 005.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzKzRw/btsL4b8VPUm/wK6W43MeyQNhlMimIcymT1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzKzRw%2FbtsL4b8VPUm%2FwK6W43MeyQNhlMimIcymT1%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yr1Fy/btsL3nI1NZg/xZ8U2HMUkbJzoAlkJ6ZNq0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yr1Fy/btsL3nI1NZg/xZ8U2HMUkbJzoAlkJ6ZNq0/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-01-01-39 006.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yr1Fy/btsL3nI1NZg/xZ8U2HMUkbJzoAlkJ6ZNq0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyr1Fy%2FbtsL3nI1NZg%2FxZ8U2HMUkbJzoAlkJ6ZNq0%2Fimg.jpg&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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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;</description>
      <category>Daily/Trip</category>
      <category>마닐라 여행</category>
      <category>마카티</category>
      <category>마카티 한식당</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/397</guid>
      <comments>https://daily1313.tistory.com/entry/%ED%95%84%EB%A6%AC%ED%95%80%F0%9F%87%B5%F0%9F%87%AD-%EB%A7%88%EB%8B%90%EB%9D%BC-1%EC%9D%BC%EC%B0%A8-20250118-%EB%8B%88%EB%85%B8%EC%9D%B4-%EC%95%84%ED%82%A4%EB%85%B8-%EA%B5%AD%EC%A0%9C%EA%B3%B5%ED%95%AD-%EC%84%B8%EC%9D%B8%ED%8A%B8-%EC%9E%90%EC%9D%BC%EC%8A%A4-%ED%98%B8%ED%85%94-%EC%9D%B4%EB%8F%99-%EA%B3%BC%EC%A0%95#entry397comment</comments>
      <pubDate>Sat, 1 Feb 2025 01:43:32 +0900</pubDate>
    </item>
    <item>
      <title>[DB] DB 형상관리를 위한 Flyway 적용</title>
      <link>https://daily1313.tistory.com/entry/DB-DB-%ED%98%95%EC%83%81%EA%B4%80%EB%A6%AC%EB%A5%BC-%EC%9C%84%ED%95%9C-Flyway-%EC%A0%81%EC%9A%A9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;소스코드의 변경사항과 버전은 Git을 통해 관리하지만, DB의 변경사항을 관리할 때는 Flyway를 활용할 수 있습니다.&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;Flyway를 활용한다면, 수동으로 sql문을 실행하고 애플리케이션을 재구동해야하는 번거로움을 해소할 수 있게 됩니다. Flyway에 관련한 개념과 was에서의 설정, Convention 등을 알아보겠습니다.&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;u&gt;&lt;b&gt;Flyway&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-07 오후 10.23.26.png&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q2k4Z/btsLFNHQ0eR/LB6g5AasBH0U9ADYiKV6h0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q2k4Z/btsLFNHQ0eR/LB6g5AasBH0U9ADYiKV6h0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q2k4Z/btsLFNHQ0eR/LB6g5AasBH0U9ADYiKV6h0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq2k4Z%2FbtsLFNHQ0eR%2FLB6g5AasBH0U9ADYiKV6h0%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;1644&quot; height=&quot;670&quot; data-filename=&quot;스크린샷 2025-01-07 오후 10.23.26.png&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;670&quot;/&gt;&lt;/span&gt;&lt;/figure&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;open-source database-migration tool&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DB Migration&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DB Schema의 version을 관리하기 위한 방법&lt;/li&gt;
&lt;li&gt;DB의 변경사항을 추적하고, 업데이트 및 롤백을 보다 쉽게 할 수 있도록 도와줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Schema&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;개념 스키마: 전체적인 view&amp;nbsp;&lt;/li&gt;
&lt;li&gt;내부 스키마: 물리적인 저장장치에서 DB가 저장되는 방법을 기술&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;간략하게 DB Migration을 위한 도구로 말할 수 있을 것 같습니다. 이제 DB Migration이 중요한 이유에 대해서 설명드리겠습니다.&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;u&gt;&lt;b&gt;DB Migration 목적&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트에서 DB가 다양한 환경에서 존재하고, local 환경에서 개발 시 DB Schema의 구조가 변경될 가능성이 매우 높습니다&lt;/li&gt;
&lt;li&gt;배포 환경에서는 스키마 변경에 대한 DDL을 작성해줘야 하는데, Flyway를 통해 해당 불편사항들을 손쉽게 해결할 수 있습니다&lt;/li&gt;
&lt;li&gt;DB에 접속해서 SQL을 수정하지 않고, Git 처럼 SQL을 관리할 수 있게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 운영환경에서 DB Migration을 위해 사용됩니다.&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;u&gt;&lt;b&gt;Flyway 설정 (application.properties)&lt;/b&gt;&lt;/u&gt;&lt;u&gt;&lt;b&gt;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;spring.flyway.enable = true (default)&lt;/li&gt;
&lt;li&gt;spring.flyway.baseline-on-migration: 변경 이력 테이블을 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1736257143277&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#flyway
flyway.enable = true
flyway.baseline-on-migrate=true
flyway.locations=classpath:db/migration
flyway.url=jdbc:mariadb://localhost:3306/tiportal
flyway.schemas=tiportal
flyway.user=root
flyway.password=admin123&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;u&gt;&lt;b&gt;Flyway Script Convention&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-07 오후 10.40.24.png&quot; data-origin-width=&quot;852&quot; data-origin-height=&quot;146&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMQpMJ/btsLFezhEzp/3PEAHCPxNZYK9UhDePI3bK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMQpMJ/btsLFezhEzp/3PEAHCPxNZYK9UhDePI3bK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMQpMJ/btsLFezhEzp/3PEAHCPxNZYK9UhDePI3bK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMQpMJ%2FbtsLFezhEzp%2F3PEAHCPxNZYK9UhDePI3bK%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;852&quot; height=&quot;146&quot; data-filename=&quot;스크린샷 2025-01-07 오후 10.40.24.png&quot; data-origin-width=&quot;852&quot; data-origin-height=&quot;146&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반드시 /src/main/resource/db/migration 경로에 작성해주셔야 합니다.&lt;/li&gt;
&lt;li&gt;[Prefix] [Version] [Separator] [Description] [Suffix]&lt;/li&gt;
&lt;li&gt;ex) V1__init.sql&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;u&gt;&lt;b&gt;주의사항&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DB 스키마에 변경사항이 있을 경우, 반드시 Flyway Script를 작성해줘야 합니다.&lt;/li&gt;
&lt;li&gt;작성하지 않으면, 애플리케이션이 구동되지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB 형상관리 도구에 대해 정리해보았는데, Flyway 관련 설정, 명명 규칙만 잘 준수한다면 손쉽게 적용해 볼 수 있는 좋은 도구라고 생각되네요.&amp;nbsp;&lt;/p&gt;</description>
      <category>Database</category>
      <category>db migration</category>
      <category>flyway</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/396</guid>
      <comments>https://daily1313.tistory.com/entry/DB-DB-%ED%98%95%EC%83%81%EA%B4%80%EB%A6%AC%EB%A5%BC-%EC%9C%84%ED%95%9C-Flyway-%EC%A0%81%EC%9A%A9#entry396comment</comments>
      <pubDate>Tue, 7 Jan 2025 22:49:19 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Json 직렬화, 역직렬화</title>
      <link>https://daily1313.tistory.com/entry/Spring-Json-%EC%A7%81%EB%A0%AC%ED%99%94-%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개발 중 Lazy Loading 된 객체에서 해당 소스코드가 존재했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1735030925880&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public String getRegionName() {
    return region == null ? null : region.getName();
}&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;Intellij에서 no usage로 해당 메서드를 사용하는 클래스가 존재하지 않았지만, 해당 정보를 가져올 수 있었습니다. 그 이유는 Json 직렬화, 역직렬화의 내부적인 동작과정 때문인데요. Json 직렬화, 역직렬화에 대해 알아보고 정리해 보도록 하겠습니다.&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;기본적으로 Spring Application을 구동하기 위한 의존성인 spring-boot-starter-web에 Json 직렬화 및 역직렬화를 할 수 있는 라이브러리가 내장되어 있습니다. (Jackson Library)&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;직렬화: 외부의 시스템에서 사용할 수 있도록 바이트 형태로 데이터를 변환하는 기술 (Object &amp;rarr; Json)&lt;/li&gt;
&lt;li&gt;역직렬화: 외부 시스템의 바이트 형태의 데이터(Json)를 객체나 해시 테이블, 딕셔너리 등으로 변환하는 것을 의미 (Json &amp;rarr; Object)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/err6ZH/btsLwKc1ehM/TKWpNJqiudq2RD09lH4MzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/err6ZH/btsLwKc1ehM/TKWpNJqiudq2RD09lH4MzK/img.png&quot; data-alt=&quot;https://www.linkedin.com/pulse/d365fo-json-serialization-deserialization-wajahat-wasti/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/err6ZH/btsLwKc1ehM/TKWpNJqiudq2RD09lH4MzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ferr6ZH%2FbtsLwKc1ehM%2FTKWpNJqiudq2RD09lH4MzK%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;633&quot; height=&quot;311&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;294&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.linkedin.com/pulse/d365fo-json-serialization-deserialization-wajahat-wasti/&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;p data-ke-size=&quot;size16&quot;&gt;Spring에서는 Jackson Library의 ObjectMapper 클래스를 통해 위 과정들이 발생하는데요. 어떻게 발생하는지 간단히 정리해 보겠습니다.&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;직렬화 (object &amp;rarr; json)&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;ObjectMapper&lt;/b&gt;의 writeValue 메서드를 통해 직렬화합니다.&lt;/li&gt;
&lt;li&gt;getter, setter 필요합니다.&lt;/li&gt;
&lt;li&gt;내부적으로 getXXX, setXXX 형태의 메서드를 찾아, json의 key를 할당합니다.&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;역직렬화 (json &amp;rarr; object)&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;ObjectMapper의 readValue 메서드를 통해 json을 object로 역직렬화합니다.&lt;/li&gt;
&lt;li&gt;@RequestBody를 통해 readValue() 메서드를 수행합니다.&lt;/li&gt;
&lt;li&gt;기본생성자, Getter, Setter가 필요합니다.&lt;/li&gt;
&lt;li&gt;ObjectMapper는 Reflection 기술을 이용해 기본생성자와 Getter, Setter를 참고합니다.&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;즉, 위의 regionName을 호출하는 메서드는 Jackson과 같은 직렬화 라이브러리가 Entity를 Json으로 변환할 때 호출하게 됩니다. 해당 필드를 필요한 경우에만 사용하기 위함과 성능을 고려한 코드라고도 볼 수 있습니다. (불필요한 쿼리 방지)&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에서 기본생성자가 없는대도 역직렬화가 가능한 경우입니다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;com.fasterxml.jackson.module:jackson-module-parameter-names 에 존재하는 ParameterNamesModule 클래스의 동작에 의해 가능하게 됩니다.&lt;/li&gt;
&lt;li&gt;해당 클래스가 JsonCreater를 사용해 기본생성자가 없는 객체도 역직렬화를 가능하게 합니다.&lt;/li&gt;
&lt;li&gt;@RequestBody annotation이 적용된 클래스에만 해당이 됩니다.&amp;nbsp;&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>Backend/Spring</category>
      <category>Jackson</category>
      <category>json 직렬화 역직렬화</category>
      <category>objectmapper</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/395</guid>
      <comments>https://daily1313.tistory.com/entry/Spring-Json-%EC%A7%81%EB%A0%AC%ED%99%94-%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94#entry395comment</comments>
      <pubDate>Tue, 24 Dec 2024 18:25:37 +0900</pubDate>
    </item>
    <item>
      <title>[필리핀 ] 필리핀 마닐라 여행 준비물 및 관광지 정리</title>
      <link>https://daily1313.tistory.com/entry/%ED%95%84%EB%A6%AC%ED%95%80%F0%9F%87%B5%F0%9F%87%AD-%ED%95%84%EB%A6%AC%ED%95%80-%EB%A7%88%EB%8B%90%EB%9D%BC-%EC%97%AC%ED%96%89-%EC%A4%80%EB%B9%84%EB%AC%BC-%EB%B0%8F-%EA%B4%80%EA%B4%91%EC%A7%80-%EC%A0%95%EB%A6%AC</link>
      <description>&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-22 오전 11.51.38.png&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BLAaW/btsLrAPkPkK/KNIIn800jrVnn9zMScqqBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BLAaW/btsLrAPkPkK/KNIIn800jrVnn9zMScqqBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BLAaW/btsLrAPkPkK/KNIIn800jrVnn9zMScqqBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBLAaW%2FbtsLrAPkPkK%2FKNIIn800jrVnn9zMScqqBK%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;624&quot; height=&quot;444&quot; data-filename=&quot;스크린샷 2024-12-22 오전 11.51.38.png&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;802&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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;돼지코 (110V 일수도 있기에 사전 대비), 비에 젖어도 되는 신발 우비, 상비약,&amp;nbsp; 쪼리, 선크림, 필기구, &lt;b&gt;샤워기필터 (필수)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;여권, 왕복항공권&lt;/li&gt;
&lt;li&gt;ESIM, 보조배터리&lt;/li&gt;
&lt;li&gt;의류 되도록 한두 장 챙기고, 이외에는&amp;nbsp;필리핀에서 구매&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESIM&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참고: &lt;a href=&quot;https://blog.naver.com/sol2roo/223494309495?trackingCode=rss&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.naver.com/sol2roo/223494309495?trackingCode=rss&lt;/a&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;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;ETravel 전자입국신고서 (입국 72시간 이내)&lt;/span&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;a href=&quot;https://blog.naver.com/gung52/223354453887&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.naver.com/gung52/223354453887&lt;/a&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;Agoda 숙소 예약&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;니노이아키오 국제공항과 교통이 원활한 곳으로 숙소 예약&amp;nbsp;&lt;/li&gt;
&lt;li&gt;관광 및 쇼핑이 목적이므로 치안이 안전한 곳으로 예약&lt;/li&gt;
&lt;li&gt;참고: &lt;a href=&quot;https://terms.naver.com/entry.naver?docId=6085047&amp;amp;cid=67006&amp;amp;categoryId=67382&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://terms.naver.com/entry.naver?docId=6085047&amp;amp;cid=67006&amp;amp;categoryId=67382&lt;/a&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;필리핀 교통카드 Beep 카드 구매&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;필리핀 전철에서 구매&amp;nbsp;&lt;/li&gt;
&lt;li&gt;필리핀식 T-money 카드&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;/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;카사 마닐라, 리잘 공원, 산티아고 요새 (역사 공원)&lt;/li&gt;
&lt;li&gt;필리핀 오션 파크 (아쿠아리움)&lt;/li&gt;
&lt;li&gt;Tripple 앱 활용 -&amp;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;[참고 자료]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://untactrip.com/%EB%A7%88%EB%8B%90%EB%9D%BC-%EC%97%AC%ED%96%89-%EC%9D%B8%EA%B8%B0-%EA%B4%80%EA%B4%91%EC%A7%80-%EC%9E%90%EC%9C%A0%EC%97%AC%ED%96%89-%EC%BD%94%EC%8A%A4-%EC%B6%94%EC%B2%9C/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://untactrip.com/%EB%A7%88%EB%8B%90%EB%9D%BC-%EC%97%AC%ED%96%89-%EC%9D%B8%EA%B8%B0-%EA%B4%80%EA%B4%91%EC%A7%80-%EC%9E%90%EC%9C%A0%EC%97%AC%ED%96%89-%EC%BD%94%EC%8A%A4-%EC%B6%94%EC%B2%9C/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1734837509430&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;마닐라 여행 인기 관광지 자유여행 코스 추천&quot; data-og-description=&quot;마닐라의 정신과 매력을 담은 마닐라 여행 최고의 관광 명소 10곳을 소개해 드리고자 합니다. 역사 애호가, 음식 애호가, 쇼핑 중독자, 현지 분위기를 만끽하고 싶은 모험가 등 모든 여행자의 취&quot; data-og-host=&quot;untactrip.com&quot; data-og-source-url=&quot;https://untactrip.com/%EB%A7%88%EB%8B%90%EB%9D%BC-%EC%97%AC%ED%96%89-%EC%9D%B8%EA%B8%B0-%EA%B4%80%EA%B4%91%EC%A7%80-%EC%9E%90%EC%9C%A0%EC%97%AC%ED%96%89-%EC%BD%94%EC%8A%A4-%EC%B6%94%EC%B2%9C/&quot; data-og-url=&quot;https://untactrip.com/마닐라-여행-인기-관광지-자유여행-코스-추천/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bqvcgW/hyXOemblTe/tDxfR2twkkGNQffU1sM8hk/img.jpg?width=681&amp;amp;height=1020&amp;amp;face=0_0_681_1020,https://scrap.kakaocdn.net/dn/vVoIW/hyXOmxIpmA/pecebcV7x4xC4FJZz6p540/img.jpg?width=400&amp;amp;height=400&amp;amp;face=110_124_261_289&quot;&gt;&lt;a href=&quot;https://untactrip.com/%EB%A7%88%EB%8B%90%EB%9D%BC-%EC%97%AC%ED%96%89-%EC%9D%B8%EA%B8%B0-%EA%B4%80%EA%B4%91%EC%A7%80-%EC%9E%90%EC%9C%A0%EC%97%AC%ED%96%89-%EC%BD%94%EC%8A%A4-%EC%B6%94%EC%B2%9C/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://untactrip.com/%EB%A7%88%EB%8B%90%EB%9D%BC-%EC%97%AC%ED%96%89-%EC%9D%B8%EA%B8%B0-%EA%B4%80%EA%B4%91%EC%A7%80-%EC%9E%90%EC%9C%A0%EC%97%AC%ED%96%89-%EC%BD%94%EC%8A%A4-%EC%B6%94%EC%B2%9C/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bqvcgW/hyXOemblTe/tDxfR2twkkGNQffU1sM8hk/img.jpg?width=681&amp;amp;height=1020&amp;amp;face=0_0_681_1020,https://scrap.kakaocdn.net/dn/vVoIW/hyXOmxIpmA/pecebcV7x4xC4FJZz6p540/img.jpg?width=400&amp;amp;height=400&amp;amp;face=110_124_261_289');&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;마닐라 여행 인기 관광지 자유여행 코스 추천&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;마닐라의 정신과 매력을 담은 마닐라 여행 최고의 관광 명소 10곳을 소개해 드리고자 합니다. 역사 애호가, 음식 애호가, 쇼핑 중독자, 현지 분위기를 만끽하고 싶은 모험가 등 모든 여행자의 취&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;untactrip.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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>Daily/Trip</category>
      <category>마닐라 여행</category>
      <category>마닐라 여행 준비물</category>
      <category>마닐라 여행지 정리</category>
      <category>필리핀</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/394</guid>
      <comments>https://daily1313.tistory.com/entry/%ED%95%84%EB%A6%AC%ED%95%80%F0%9F%87%B5%F0%9F%87%AD-%ED%95%84%EB%A6%AC%ED%95%80-%EB%A7%88%EB%8B%90%EB%9D%BC-%EC%97%AC%ED%96%89-%EC%A4%80%EB%B9%84%EB%AC%BC-%EB%B0%8F-%EA%B4%80%EA%B4%91%EC%A7%80-%EC%A0%95%EB%A6%AC#entry394comment</comments>
      <pubDate>Sun, 22 Dec 2024 12:19:27 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Garbage Collection</title>
      <link>https://daily1313.tistory.com/entry/Java-Garbage-Collection</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Java 프로그램을 개발하다 보면 OutOfMemoryError을 마주하게 되는데, 이는 JVM Heap 메모리의 부족 현상에 밀접한 연관이 있는 에러입니다.&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;이를 해결하기 위해 Garbage Collection 과정이 발생하게 되는데 해당 개념과 동작원리, 알고리즘에 대해 알아보겠습니다.&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;Garbage Collection&amp;nbsp;&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;메모리 관리 기술중 하나로서 JVM의 Heap 영역에서 동적으로 할당되었던 메모리 중 사용하지 않은 객체를 모아 주기적으로 제거하는 프로세스입니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JVM의 메모리 구조중 일부인 Heap 영역에 관여하는 프로세스입니다. 뒷단에 내용을 수월하게 이해하기 위해 JVM Memory 구조를 설명드린 후 핵심 내용에 대해 알아보겠습니다.&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;JVM Memory 구조&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-04 오후 11.34.34.png&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;816&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cti4VM/btsK8sv7WbY/LoDscSQeUHXSj5ZWGQU7Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cti4VM/btsK8sv7WbY/LoDscSQeUHXSj5ZWGQU7Jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cti4VM/btsK8sv7WbY/LoDscSQeUHXSj5ZWGQU7Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcti4VM%2FbtsK8sv7WbY%2FLoDscSQeUHXSj5ZWGQU7Jk%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;629&quot; height=&quot;455&quot; data-filename=&quot;스크린샷 2024-12-04 오후 11.34.34.png&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;816&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Static (Method) 영역&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;JVM이 실행될 때 Class가 load되어 생성됩니다.&lt;/li&gt;
&lt;li&gt;Class 정보, Static 변수, 생성자, 메서드를 저장합니다.&lt;/li&gt;
&lt;li&gt;어디에서든 접근이 가능합니다.&lt;/li&gt;
&lt;li&gt;JVM 종료 시 메모리에서 해제 됩니다. 즉, 프로그램이 종료되기 전까지 메모리 상에서 존재하게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Heap 영역&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;인스턴스를 생성할 때 사용되는 메모리 영역입니다 (new 키워드를 통해 인스턴스 생성 시)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Stack 영역에서 생성된 객체에 대한 주소값 (Reference)을 저장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;참조형 데이터 타입이 저장됩니다. (String, Array, Enum, Class, Interface.)&lt;/li&gt;
&lt;li&gt;호출이 종료되도 삭제 X &amp;rarr; GC에 의해 메모리에서 해제됩니다.&lt;/li&gt;
&lt;li&gt;스레드가 몇 개든 단 하나의 영역에서만 존재합니다. (Stack 영역은 Thread 별로 하나씩 생성)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Stack 영역&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;기본 자료형 (int, double, boolean, byte..), 지역변수, 매개변수가 저장되는 메모리입니다.&lt;/li&gt;
&lt;li&gt;메서드 내부의 기본자료형에 해당하는 변수를 적재합니다.&lt;/li&gt;
&lt;li&gt;Heap 영역에 생성된 데이터의 참조값이 할당됩니다.&lt;/li&gt;
&lt;li&gt;메서드가 호출될 때, 메모리에서 할당되고 메서드 종료 시 메모리에서 삭제됩니다.&lt;/li&gt;
&lt;li&gt;각 Thread마다 자신만의 Stack을 갖고 있습니다. Thread는 내부적으로 Static, Heap, Stack 영역을 가집니다. Thread는 다른 Thread에 접근할 수 없지만, static, heap 영역을 공유하여 사용이 가능합니다.&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;이제 GC와 연관된 Heap 영역의 세부적인 구조에 대해 알아보겠습니다. (Permanent 영역은 Java8 이후 Native Method Area으로 대체)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-04 오후 11.44.09.png&quot; data-origin-width=&quot;1208&quot; data-origin-height=&quot;736&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz6Il8/btsK6MbywBd/jE8gykEKEuHfF41F82AqW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz6Il8/btsK6MbywBd/jE8gykEKEuHfF41F82AqW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz6Il8/btsK6MbywBd/jE8gykEKEuHfF41F82AqW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz6Il8%2FbtsK6MbywBd%2FjE8gykEKEuHfF41F82AqW1%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;1208&quot; height=&quot;736&quot; data-filename=&quot;스크린샷 2024-12-04 오후 11.44.09.png&quot; data-origin-width=&quot;1208&quot; data-origin-height=&quot;736&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Young Generation (Eden + S0 + S1)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새롭게 생성한 객체의 대부분이 이 영역에 위치합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;대부분의 객체가 금방 접근 불가능한 상태가 되기 때문에 매우 많은 객체가 Young 영역에 생성되었다가 사라집니다.&lt;/li&gt;
&lt;li&gt;객체가 처음 생성되면, Eden 영역에 저장되고 Eden 영역의 메모리가 가득 차면 S0, S1로 옮겨지게 됩니다.&lt;/li&gt;
&lt;li&gt;Minor GC: Eden영역이 가득 찼을 때, Young Generation 영역에서 객체가 사라지는 현상&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Old Generation
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;접근 불가능한 상태로 되지 않아 Young 영역에서 살아남은 객체가 이 영역으로 복사됩니다.&lt;/li&gt;
&lt;li&gt;Young 영역보다 크게 할당되며 크기가 큰 만큼 Young 영역보다 GC는 적게 발생합니다.&lt;/li&gt;
&lt;li&gt;Major (Full) GC: Old Generation이 가득 찼을 때, Old Generation 영역에서 객체가 사라지는 현상&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;세부적인 구조에 대해서도 파악했으니, GC의 공통된 2가지 기능과 알고리즘 종류 5가지에 대해 설명드리겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;stop-the-world&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GC를 실행하기 위해 JVM이 애플리케이션 실행을 멈추는 행위입니다.&lt;/li&gt;
&lt;li&gt;어떤 GC 알고리즘을 사용하더라도 &quot;stop-the-world&quot;는 발생하게 되는데, 대개의 경우 GC 튜닝은 이 &quot;stop-the-world&quot; 시간을 줄이는 것이라고 합니다.&lt;/li&gt;
&lt;li&gt;GC를 해도 더이상 사용 가능한 메모리 영역이 없는데 계속 메모리르 할당하려고 하면, OutOfMemoryError가 발생하여 WAS가 다운될 수도 있습니다. 즉, 서버가 요청을 처리 못하고 있는 상태가 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Mark and Sweep&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mark: 사용되는 메모리와 사용되지 않는 메모리를 식별하는 작업입니다.&lt;/li&gt;
&lt;li&gt;Sweep: Mark 단계에서 사용되지 않음으로 식별된 메모리를 해제하는 작업입니다.&lt;/li&gt;
&lt;li&gt;Stop-The-World를 통해 모든 작업을 중단시키면, GC는 스택의 모든 변수 또는 Reachable 객체를 스캔하면서, 어떤 객체를 참고하고 있는지를 탐색하게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&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;GC Algorithm&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;Serial GC(-XX:+UseSerialGC)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Young 영역: Mark-Sweep 방식으로 GC 수행&lt;/li&gt;
&lt;li&gt;Old 영역: Mark-Sweep-Compact 방식으로 GC 수행
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Old 영역에 살아있는 객체를 식별하고 (Mark), 힙의 앞부분부터 확인하여 살아있는 객체만 남긴 후 (Sweep) 객체가 존재하는 부분과 객체가 없는 부분으로 나눕니다. (Compact)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;적은 메모리, 적은 CPU 코어 개수에 적합합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Parallel GC(-XX:+UseParallelGC)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Serial GC와 알고리즘은 동일하지만, GC를 처리하는 Thread의 개수가 다릅니다.&lt;/li&gt;
&lt;li&gt;Parallel GC는 GC를 처리하는 Thread가 여러 개지만, Serial GC는 한 개입니다.&lt;/li&gt;
&lt;li&gt;속도 측면에서 Parallel GC가 빠르며, 메모리가 충분하고 코어의 개수가 많을 때 적합합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-04 오후 11.55.05.png&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dlmfTz/btsK509okcV/fdmTWXOXiomPiJtJVb2KF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dlmfTz/btsK509okcV/fdmTWXOXiomPiJtJVb2KF1/img.png&quot; data-alt=&quot;Serial GC, Parallel GC 차이&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dlmfTz/btsK509okcV/fdmTWXOXiomPiJtJVb2KF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdlmfTz%2FbtsK509okcV%2FfdmTWXOXiomPiJtJVb2KF1%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;1172&quot; height=&quot;392&quot; data-filename=&quot;스크린샷 2024-12-04 오후 11.55.05.png&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Serial GC, Parallel GC 차이&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Parallel Old GC(-XX:+UseParallelOldGC)&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;JDK 5 update 6부터 제공한 방식입니다.&lt;/li&gt;
&lt;li&gt;Old 영역의 GC 알고리즘이 Mark-Summary-Compaction입니다.&lt;/li&gt;
&lt;li&gt;Summary 단계는 GC를 수행한 영역에 대해 별도로 살아있는 객체를 식별한다는 점에서 Sweep 단계 보다 약간 더 복잡한 단계를 거칩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. CMS GC(-XX:+UseConcMarkSweepGC)&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;Initial Mark 단계:&lt;/b&gt; 클래스 로더에서 가장 가까운 객체 중 살아있는 객체만 찾습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Concurrent Mark 단계&lt;/b&gt;: 방금 살아있다고 확인한 객체에서 참조하고 있는 개체들을 따라가면서 확인합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Remark 단계&lt;/b&gt;: Concurrent Mark 단계에서 새로 추가되거나 참조가 끊긴 객체를 확인합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Concurrent Sweep 단계&lt;/b&gt;: 쓰레기를 정리하는 작업을 실행합니다.&lt;/li&gt;
&lt;li&gt;위 4가지 단계의 과정들을 다른 Thread가 실행 중인 상태에서 동시에 실행됩니다.&lt;/li&gt;
&lt;li&gt;Stop-the-world 시간이 매우 짧으면서, 애플리케이션의 응답 속도가 중요할 때 사용합니다.&lt;/li&gt;
&lt;li&gt;장점만큼 메모리와 CPU를 더 많이 사용해야 한다는 단점이 있습니다.&lt;/li&gt;
&lt;li&gt;Parallel GC와의 차이는 Compaction 작업 유무의 차이입니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Compaction: 메모리 공간에서 사용하지 않는 빈 공간이 없도록 옮겨서 메모리 분산을 제거하는 작업&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Java 9 deprecated, Java 14에서 사용이 중지되었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. G1 GC(-XX:+UseG1GC)&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;G1 GC는 힙을 일정한 크기의 리전(Region)으로 분할합니다.&lt;/li&gt;
&lt;li&gt;각각의 리전은 Young 또는 Old Generation이 될 수 있고, 하나의 리전이 GC가 수행되는 최소 단위가 됩니다. G1 GC는 이 리전들을 가비지의 양과 종류에 따라 우선순위를 두어 효율적으로 처리합니다.&lt;/li&gt;
&lt;li&gt;리전은 통상의 GC와 마찬가지로 young generation, old generation으로 나뉘며 이들 영역의 위치는 정해져 있지 않고 메모리 상태에 따라 유동적으로 정해집니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Region&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;Young generation&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Eden: 객체가 최초에 생성되는 영역&lt;/li&gt;
&lt;li&gt;Survivor: Eden에서 생존한 객체가 이동되는 영역&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Old generation&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;Humongous&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Region 크기의 절반 이상(50%)인 거대한 객체가 할당되는 영역&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;G1 GC 동작과정&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-05 오전 12.00.33.png&quot; data-origin-width=&quot;1090&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VTgiu/btsK6ihJaMW/dfenofShyib139PFzyrEQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VTgiu/btsK6ihJaMW/dfenofShyib139PFzyrEQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VTgiu/btsK6ihJaMW/dfenofShyib139PFzyrEQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVTgiu%2FbtsK6ihJaMW%2FdfenofShyib139PFzyrEQ0%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;548&quot; height=&quot;367&quot; data-filename=&quot;스크린샷 2024-12-05 오전 12.00.33.png&quot; data-origin-width=&quot;1090&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;/figure&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;Young-Only Phase&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Young Generation에서 Normal Young Collection이 반복적으로 수행되며, 이 과정에서 Young Generation에서 Old Generation으로 객체가 승격됩니다. Old Generation의 점유율이 Initiating Heap Occupancy 값에 도달하면, Young-only Phase에서 Space-reclamation Phase로 전환 준비를 시작합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Concurrent Start&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Young GC를 수행하면서 동시 Marking (객체 생존 여부 탐지) Phase가 진행됩니다.&lt;/li&gt;
&lt;li&gt;Concurrent Mark는 Old Region을 GC 하기 위해 현재 도달할 수 있는 live 객체를 결정합니다.&lt;/li&gt;
&lt;li&gt;Concurrent Mark가 진행되는 도중에, Normal Young GC가 동작할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Remark&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;클래스 참조 (객체 참조관계를 확인 및 정리), 클래스 언로딩, 비어있는 Region, 내부 데이터 구조를 정리합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Remark&lt;/b&gt; 단계와 &lt;b&gt;Cleanup&lt;/b&gt; 단계 사이에서 G1은 Old Generation 영역에서 어떤 영역을 수집할지 결정하고, 이를 기반으로 공간을 회수합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Cleanup&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Region 회수를 진행할지를 결정합니다.&lt;/li&gt;
&lt;li&gt;Space-reclamation Phase가 시작될 경우, Prepare Mixed Young Collection이 실행되어 Young-only Phase를 종료합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Space Reclamation&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Young Generation 영역과 함께 Old Generation 영역의 일부도 정리하는 Mixed Collection이 수행됩니다.&lt;/li&gt;
&lt;li&gt;Mixed Collection은 유효 객체를 Old Generation에서 다른 영역으로 이동하고 가비지가 많은 영역을 해제합니다.&lt;/li&gt;
&lt;li&gt;G1은 더 이상 Old Generation 영역에서 회수할 메모리가 충분하지 않다고 판단되면, Space-Reclamation Phase를 종료합니다.&lt;/li&gt;
&lt;li&gt;Space Reclamation 단계가 종료되면 Collection Cycle이 Young Phase 단계에서부터 재시작됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Full GC (back up)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플리케이션 메모리 부족 상태에 도달했거나, 생존 객체 정보를 수집하는 동안 메모리가 부족한 경우, 전체 Heap에 대해 Stop-the-World Full GC를 수행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;GC방식보다 처리 속도가 가장 빠르며 멀티 프로세스 기반으로 운영되는 애플리케이션으로 적합하며, Java 9부터 기본 GC로 지정되었습니다.&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;lt;참고 자료&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lucas-owner.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://lucas-owner.tistory.com/38&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mangkyu.tistory.com/119&quot;&gt;https://mangkyu.tistory.com/119&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/java-system-gc&quot;&gt;https://www.baeldung.com/java-system-gc&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://d2.naver.com/helloworld/1329&quot;&gt;https://d2.naver.com/helloworld/1329&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://s-core.co.kr/insight/view/about-g1-gc/&quot;&gt;https://s-core.co.kr/insight/view/about-g1-gc/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://thinkground.studio/2020/11/07/일반적인-gc-내용과-g1gc-garbage-first-garbage-collector-내용/&quot;&gt;https://thinkground.studio/2020/11/07/일반적인-gc-내용과-g1gc-garbage-first-garbage-collector-내용/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/17/gctuning/garbage-first-g1-garbage-collector1.html#GUID-3A99AE6C-F80A-4565-A27C-B4AEDF5CDF71&quot;&gt;https://docs.oracle.com/en/java/javase/17/gctuning/garbage-first-g1-garbage-collector1.html#GUID-3A99AE6C-F80A-4565-A27C-B4AEDF5CDF71&lt;/a&gt;&lt;/p&gt;</description>
      <category>Backend</category>
      <category>garbage collection</category>
      <category>gc algorithm</category>
      <category>jvm heap memory</category>
      <author>SeungbeomKim</author>
      <guid isPermaLink="true">https://daily1313.tistory.com/393</guid>
      <comments>https://daily1313.tistory.com/entry/Java-Garbage-Collection#entry393comment</comments>
      <pubDate>Thu, 5 Dec 2024 00:05:10 +0900</pubDate>
    </item>
  </channel>
</rss>