<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>erikoding</title>
    <link>https://erika0915.tistory.com/</link>
    <description>백엔드 개발자가 되고 싶어요 . </description>
    <language>ko</language>
    <pubDate>Sat, 6 Jun 2026 16:34:51 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>erika0915</managingEditor>
    <image>
      <title>erikoding</title>
      <url>https://tistory1.daumcdn.net/tistory/7361751/attach/49287153d90b468398db36a189ef89db</url>
      <link>https://erika0915.tistory.com</link>
    </image>
    <item>
      <title>[Spring] JDBC(Java Database Connectivity)</title>
      <link>https://erika0915.tistory.com/154</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;JDBC(Java&amp;nbsp;Database&amp;nbsp;Connectivity)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JDBC는 Java Database Connectivity로 Java 애플리케이션이 데이터베이스와 통신할 수 있도록 도와주는 표준 API이다. DB마다 사용하는 프로토콜과 방식이 다르기 때문에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Java 프로그램이 DB와 직접 대화가 불가능하다.&lt;span&gt; 그래서 자바에서는 공통된 규칙을 만들어 두고 DB마다 그 규칙을 구현한 드라이버를 사용한다. 그리고 그 공통 규칙이 JDBC이다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DzhbM/dJMcagKSAOA/QIkjvWqAEUIZex3mRw9oK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DzhbM/dJMcagKSAOA/QIkjvWqAEUIZex3mRw9oK1/img.png&quot; data-alt=&quot;https://hudi.blog/java-db-connection-pooling/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DzhbM/dJMcagKSAOA/QIkjvWqAEUIZex3mRw9oK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDzhbM%2FdJMcagKSAOA%2FQIkjvWqAEUIZex3mRw9oK1%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;338&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://hudi.blog/java-db-connection-pooling/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조는 위 사진과 같다. 여기서 핵심 포인트는&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;⭐ JDBC는 인터페이스이다. 실제 DB와 통신하는 코드는 JDBC Driver가 담당한다. DB가 달라도 JDBC API는 동일하게 사용한다.&amp;nbsp;&lt;br /&gt;-&amp;gt; 그래서 DB를 MySQL에서 PostgreSQL로 바꿔도 자바 코드의 큰 구조는 거의 바뀌지 않는다.&amp;nbsp;&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JDBC 동작 흐름&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JDBC로 DB 작업을 할 때는 항상 비슷한 흐름을 따른다.&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) JDBC 드라이버 로딩&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) DB 연결&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) SQL 실행&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) 결과 처리&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5) 자원 해제&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예시 코드&amp;nbsp;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;1) DB 연결&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;Connection은 DB와의 연결 통로&amp;nbsp;&lt;/li&gt;
&lt;li&gt;연결에 성공하면 이후 SQL을 실행할 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766563219837&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;String url = &quot;jdbc:mysql://localhost:3306/testdb&quot;;
String user = &quot;root&quot;;
String password = &quot;1234&quot;;

Connection connection = DriverManager.getConnection(url, user, password);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;2) SQL 실행&lt;/u&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`PreparedStatement`을 사용하는 이유&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;SQL Injection 방지&amp;nbsp;&lt;/li&gt;
&lt;li&gt;가독성 및 성능 측면에서 유리&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766563268306&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;String sql = &quot;SELECT id, name FROM member WHERE id = ?&quot;;

PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setInt(1, 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;u&gt;3) 결과 조회&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;ResultSet은 쿼리 결과 집합&amp;nbsp;&lt;/li&gt;
&lt;li&gt;`rs.next()`로 한 행씩 조회&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766563311697&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ResultSet rs = pstmt.executeQuery();

while (rs.next()) {
    int id = rs.getInt(&quot;id&quot;);
    String name = rs.getString(&quot;name&quot;);

    System.out.println(id + &quot; &quot; + name);
}&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;u&gt;4) 자원 해제&lt;/u&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자원 해제는 반드시 필요한 작업이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1766563357336&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rs.close();
pstmt.close();
connection.close();&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JDBC의 단점&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 반복되는 코드가 너무 많다. 그리고 SQL과 자바 코드가 섞여 복잡해진다. 그리고 객체와 테이블 매핑이 불편하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서, 순수 JDBC는 거의 직접 쓰지 않는다. JPA나 Spring JDBC (JdbcTemplate)을 사용한다. 하지만 중요한 점은 Spring JDBC도, JPA도 내부적으로는 JDBC를 사용한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Spring JdbcTemplate란?&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복되는 JDBC 코드를 없애준 도구이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JDBC를 사용하면 항상 반복되는 코드가 있다. (Connection 생성, PreparedStatement 생성, ResultSet 처리, 예외 처리, 자원 해제 ...) 하지만 매번 다른 부분은 사실 SQL과 결과 처리 부분이다. 그래서 Spring JdbcTemplate은 이런 반복되는 것들 대신 핵심 로직에만 집중하게끔 도와주는 템플릿 클래스이다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;-&amp;gt; Spring에서 JDBC 사용을 단순화해주는 템플릿 클래스&amp;nbsp;&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Spring JdbcTemplate의 동작 구조&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBwCfJ/dJMcaiPp4mX/J8RePDt8TzJBpbHdRdpvp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBwCfJ/dJMcaiPp4mX/J8RePDt8TzJBpbHdRdpvp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBwCfJ/dJMcaiPp4mX/J8RePDt8TzJBpbHdRdpvp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBwCfJ%2FdJMcaiPp4mX%2FJ8RePDt8TzJBpbHdRdpvp0%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;300&quot; height=&quot;450&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JdbcTempalte도 결국 JDBC를 사용한다. 차이점은 '누가 관리하는가' 이며, JDBC는 개발자가 JdbcTemplate은 Spring이 관리한다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;예시 코드&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;1) 의존성 주입&lt;/u&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1766562996800&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Autowired
private JdbcTemplate jdbcTemplate;&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;u&gt;2) 조회 쿼리&lt;/u&gt;&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1766563042956&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;String sql = &quot;SELECT id, name FROM member&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`RowMapper` 사용&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;ResultSet 한 줄을 객체로 변환하는 역할&amp;nbsp;&lt;/li&gt;
&lt;li&gt;객체 매핑 책임을 분리할 수 있음&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766563056723&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Member&amp;gt; members = jdbcTemplate.query(sql, (rs, rowNum) -&amp;gt;
    new Member(
        rs.getLong(&quot;id&quot;),
        rs.getString(&quot;name&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;&lt;u&gt;3) 단건조회&lt;/u&gt;&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1766563119350&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;String sql = &quot;SELECT id, name FROM member WHERE id = ?&quot;;

Member member = jdbcTemplate.queryForObject(
    sql,
    (rs, rowNum) -&amp;gt; new Member(
        rs.getLong(&quot;id&quot;),
        rs.getString(&quot;name&quot;)
    ),
    1
);&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JdbcTemplate의 장점 및 한계&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점은 개발자는 SQL만 짜면 되고 다른 것들은 다 스프링에서 하게끔 지원하는 것이다. 그러나, 이 JdbcTemplate도 완벽하지는 않다. SQL을 직접 작성해야 하며 테이블과 객체 매핑은 수동으로 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 이런 테이블과 객체 매핑을 자동으로 해주는 것은 JPA가 있다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring</category>
      <category>Spring</category>
      <category>springboot</category>
      <category>스프링</category>
      <category>스프링부트</category>
      <author>erika0915</author>
      <guid isPermaLink="true">https://erika0915.tistory.com/154</guid>
      <comments>https://erika0915.tistory.com/154#entry154comment</comments>
      <pubDate>Sat, 20 Dec 2025 21:57:16 +0900</pubDate>
    </item>
    <item>
      <title>대용량 데이터 (csv파일) db에 넣기 with JdbcTemplate -2</title>
      <link>https://erika0915.tistory.com/153</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://erika0915.tistory.com/34&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.07.15 - [프로젝트/끼니콩] - [Spring] csv파일 db에 넣기 with JdbcTemplate&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1766122537189&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] csv파일 db에 넣기 with JdbcTemplate&quot; data-og-description=&quot;들어가며아동급식카드가맹점 데이터를 db에 넣어야한다. 데이터는 지역 별로 나누어져 csv파일 형태로 제공되었고, 한 번만 넣는게 아니라 데이터가 변경되거나 지역이 확장이되면 계속적으로 &quot; data-og-host=&quot;erika0915.tistory.com&quot; data-og-source-url=&quot;https://erika0915.tistory.com/34&quot; data-og-url=&quot;https://erika0915.tistory.com/34&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/XsHt2/hyZPPDvpeA/UQkuxKeakGq45Kl8hmjKq0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cuLThf/hyZPLHSysf/tZpdHphWKM7IhnHfYvms9K/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://erika0915.tistory.com/34&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://erika0915.tistory.com/34&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/XsHt2/hyZPPDvpeA/UQkuxKeakGq45Kl8hmjKq0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cuLThf/hyZPLHSysf/tZpdHphWKM7IhnHfYvms9K/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;[Spring] csv파일 db에 넣기 with JdbcTemplate&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;들어가며아동급식카드가맹점 데이터를 db에 넣어야한다. 데이터는 지역 별로 나누어져 csv파일 형태로 제공되었고, 한 번만 넣는게 아니라 데이터가 변경되거나 지역이 확장이되면 계속적으로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;erika0915.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;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아동급식카드가맹점 공공데이터는 간헐적으로 업데이트된다. 가맹점의 정보가 수정되기도 하고, 없어지기도 하며, 새롭게 추가되기도 한다. 지금 운영하고 있는 '끼니콩' 서비스에서는 아래 사진과 같이 `업데이트 날짜`를 제공한다. 이는 해당 가맹점이 언제 마지막으로 업데이트가 되었는지를 나타낸다.&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-origin-width=&quot;746&quot; data-origin-height=&quot;984&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AuWcR/dJMcacuUUHL/b4ks2KKrvrfObASHf1ZJv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AuWcR/dJMcacuUUHL/b4ks2KKrvrfObASHf1ZJv1/img.png&quot; data-alt=&quot;kkinicong.co.kr&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AuWcR/dJMcacuUUHL/b4ks2KKrvrfObASHf1ZJv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAuWcR%2FdJMcacuUUHL%2Fb4ks2KKrvrfObASHf1ZJv1%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;400&quot; height=&quot;528&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;984&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;kkinicong.co.kr&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼, 시간이 지남에 따라 공공데이터는 새롭게 업데이트가 되며 '끼니콩' 서비스 내에서도 이 업데이트를 지원해줘야 한다. 결국, csv 파일을 db에 넣는 작업이 단발성이 아닌, 같은 지역의 가맹점 정보도 지속적으로 업데이트해서 넣어주어야 한다는 것이다.&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;u&gt;그러나 나의 기존 코드는 업데이트와 관련된 구체적인 작업을 수행하지 못한다.&lt;/u&gt; '가맹점명'과 '주소'를 key로 설정해서 동일한 가맹점 정보가 또 다시 들어오면 db에 저장되지 않도록 설정은 해놓았지만, 가맹점의 정보가 변경되거나 가맹점이 없어졌을 경우에는 대응하지 못하는 문제점이 있다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요구사항&amp;nbsp;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;다음과 같은 요구사항이 존재한다.&amp;nbsp;&lt;br /&gt;- 기존에 새롭게 들어오는 가맹점들은 저장되어야 한다.&amp;nbsp;&lt;br /&gt;- 기존에 존재하는 가맹점이지만, 새롭게 들어오는 csv 파일에는 해당 가맹점이 없다면 삭제되어야 한다.&amp;nbsp;&lt;br /&gt;- 기존에 존재하는 가맹점 중, 새롭게 들어오는 csv 파일에 해당 가맹점에 대한 정보가 변경되었다면 변경된 값들만 변경한다.&amp;nbsp;&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;수정 방향&amp;nbsp;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;1) 도메인 : Store 엔티티&lt;/u&gt;&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;플래그 필드인 `is_updated`를 추가하였다. 이는 동기화 과정에서 현재 파일에 포함된 데이터인지 아닌지를 구별하는 표식이다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;복합 유니크 인덱스인 `idx_name_address`를 설정하였다. `name`과 `address`를 묶어서 유니크 설정을 함으로써 db가 이름과 주소가 같으면 동일한 가게라고 인식하게 된다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;인덱스 `idx_region_is_updated`를 설정하였다. 이는 삭제할 때랑 리셋할 때의 쿼리를 보면 `region`과 `is_updated`를 조건으로 사용하는데, 이 인덱스가 없다면 db에서 삭제할 대상을 찾기 위해 테이블 전체를 full table scan하지만 인덱스를 사용해서 해당 지역의 `FALSE` 데이터만 집어서 빠르게 처리할 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주의해야할 점은 연관관계를 보존해야한다는 것이다. `review` 및 `storescrap` 등 연관관계가 존재하기 때문에 기존 가맹점의 `id`를 유지하며 업데이트해야한다.&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;2) `StoreJdbcRepositoryImpl`&lt;/u&gt;&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) `upsertStores`&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 삽입하고 갱신하는 역할을 한다. 여기서는 MySQL의 `ON DUPLICAATE KEY UDPATE` 구문을 활용한다.&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;신규 데이터는 유니크 키 (이름+주소)가 겹치지 않으면 `INSERT`가 수행된다. 이때, 앞서 도메인에서 설정한 `is_updated = TRUE`로 들어가게 된다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;기존 데이터는 유니크 키가 겹치면 에러를 내는 것이 아니라 `UPDATE`를 수행한다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`category` , `latitude`, `longitude`, `updated_date` 중 변동 가능한 정보만 갱신한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;중요한 점은 `is_updated = TRUE`로 상태를 변경하여 이 데이터가 이번 파일에 존재했음을 표시한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) `deleteMissingStores`&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;삭제 : `region = :region AND is_updated = FALSE` 조건을 통해, 방금 업로드한 지역 데이터 중 파일에 없어서 `TRUE`가 되지 못한 데이터만 골라 삭제한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;리셋 : 삭제 후, 남은 데이터들의 `is_updated`를 다시 `FALSE`로 초기화한다. 그래야 다음 번 업로드 시 다시 비교를 시작할 수 있기 때문이다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;3) 서비스 레이어 : `StoreUploadService`&lt;/u&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적인 비즈니스 흐름과 로그가 존재한다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/Heroineeee/BE/pull/119&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/Heroineeee/BE/pull/119&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1766140868922&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;[FEAT] 가맹점 정보 업데이트 및 동기화 로직 구현 by erika0915 &amp;middot; Pull Request #119 &amp;middot; Heroineeee/BE&quot; data-og-description=&quot;  개요 이미 db에 존재하는 지역에 대해 업데이트된 공공데이터의 csv 파일을 업로드 했을 때의 로직을 고려하여 csv 파일 업로드 로직을 구현했습니다.  ️ 작업 사항 가맹점 데이터 upsert 기&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/Heroineeee/BE/pull/119&quot; data-og-url=&quot;https://github.com/Heroineeee/BE/pull/119&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dpXwIa/hyZP4AwbPy/hQHb4JiieRvBKdOFs6gBd1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/cSHCil/hyZPvGvxfp/gFbaDBTQTNfwUfIIFL1i2K/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/Heroineeee/BE/pull/119&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/Heroineeee/BE/pull/119&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dpXwIa/hyZP4AwbPy/hQHb4JiieRvBKdOFs6gBd1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/cSHCil/hyZPvGvxfp/gFbaDBTQTNfwUfIIFL1i2K/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;[FEAT] 가맹점 정보 업데이트 및 동기화 로직 구현 by erika0915 &amp;middot; Pull Request #119 &amp;middot; Heroineeee/BE&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  개요 이미 db에 존재하는 지역에 대해 업데이트된 공공데이터의 csv 파일을 업로드 했을 때의 로직을 고려하여 csv 파일 업로드 로직을 구현했습니다.  ️ 작업 사항 가맹점 데이터 upsert 기&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;</description>
      <category>프로젝트/끼니콩</category>
      <category>Spring</category>
      <category>springboot</category>
      <category>스프링</category>
      <category>스프링부트</category>
      <author>erika0915</author>
      <guid isPermaLink="true">https://erika0915.tistory.com/153</guid>
      <comments>https://erika0915.tistory.com/153#entry153comment</comments>
      <pubDate>Fri, 19 Dec 2025 14:34:15 +0900</pubDate>
    </item>
    <item>
      <title>[TDD] AI시대, 왜 지금 TDD 인가요?</title>
      <link>https://erika0915.tistory.com/152</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;AI 시대, 개발자로 살아간다는 것&lt;/b&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구가 개발의 일상에 깊이 스며들었습니다. 기능을 설명하면 바로 코드가 생성되고, 개발 속도와 생산성은 눈에 띄게 향상됐습니다. 이제 '코드를 짜는 일' 자체는 더 이상 개발자의 핵심 업무가 아니게 된 것이다. 하지만 그만큼 이제 우리는 &lt;u&gt;개발자로서 더 본질적인 질문&lt;/u&gt;에 마주하게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;우리는 지금 무엇을 만들고 있는가 -&amp;gt; 문제 정의&amp;nbsp;&lt;br /&gt;이 코드가 정말 우리가 기대한 대로 동작하고 있는가 -&amp;gt; 결과 검증&amp;nbsp;&lt;br /&gt;문제가 생겼을 때, 어디서부터 점검하고 어떻게 개선할 수 있는가 -&amp;gt; 리팩터링&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 많은 것을 대신하고 있지만, 무엇을 만들지 정의하고, 결과를 검증하는 책임은 여전히 개발자의 몫입니다. AI와 협업하는 지금, 개발자의 역할은 더 명확해졌고, 그 책임을 현실적이고 신뢰할 수 있는 방식으로 실천하는 방법이 필요해졌습니다.&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;b&gt;왜 지금 TDD일까요 ?&lt;/b&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째, 기능을 구현하기 전에 테스트를 먼저 작성하면, '무엇을 만들지' 더 정확히 정의하게 되고, AI에게도 명확한 목표를 전달할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘때, AI가 코드를 빠르게 만들어준다 해도, 그 코드가 기대한 대로 동작하는지는 또 다른 문제입니다. 자동화된 테스트는 이를 반복적으로 검증할 수 있는 가장 확실한 방법입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셋째, 무엇보다 이제는 테스트 코드도 AI가 제안해줍니다. TDD를 시작하는 문턱은 낮아졌고, 예전보다 훨씬 쉽게 실천할 수 있는 환경이 만들어졌습니다.&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;gt; Inflearn `강의 소개` 발췌&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;TDD 절차&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1252&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmhZMq/dJMcadtMSA8/KhJ5MAheBB3D8WRAfJrKq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmhZMq/dJMcadtMSA8/KhJ5MAheBB3D8WRAfJrKq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmhZMq/dJMcadtMSA8/KhJ5MAheBB3D8WRAfJrKq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmhZMq%2FdJMcadtMSA8%2FKhJ5MAheBB3D8WRAfJrKq1%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;316&quot; data-origin-width=&quot;1252&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1648&quot; data-origin-height=&quot;490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZEd4B/dJMcacVZmRD/y0cAgkA2cf80nPteM2YgVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZEd4B/dJMcacVZmRD/y0cAgkA2cf80nPteM2YgVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZEd4B/dJMcacVZmRD/y0cAgkA2cf80nPteM2YgVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZEd4B%2FdJMcacVZmRD%2Fy0cAgkA2cf80nPteM2YgVk%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;178&quot; data-origin-width=&quot;1648&quot; data-origin-height=&quot;490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LLM 코딩 관점에서의 TDD 효과&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;구현 코드의 설계보다 제품의 가치에 집중하게 된다.&amp;nbsp;&lt;/li&gt;
&lt;li&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-origin-width=&quot;1620&quot; data-origin-height=&quot;654&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TO3yB/dJMcacBHkHD/wVAsZrV7YPF3PLU9aI1Jg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TO3yB/dJMcacBHkHD/wVAsZrV7YPF3PLU9aI1Jg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TO3yB/dJMcacBHkHD/wVAsZrV7YPF3PLU9aI1Jg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTO3yB%2FdJMcacBHkHD%2FwVAsZrV7YPF3PLU9aI1Jg1%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;242&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;654&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;느낀점&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;AI 시대에 개발자의 언어는 '코드'에서 '테스트'로 변한다는 말이 크게 와닿았다. AI 시대에서 어쩌면 개발자보다, 아니 이미 개발자보다 구현 능력은 더 뛰어나다. AI가 나보다 코드를 더 빠르고 정확하게 짤 수 있다는 사실은 이제 겸허히 받아들여야 할 현실이다. &lt;u&gt;하지만 역설적으로 개발자의 본질이 더 뚜렷해졌다고 생각한다.&lt;/u&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;AI에게 '로그인 기능 구현해줘' 라고 막연하게 묻는 개발자와 '이러한 조건을 모두 만족하는 로그인 함수를 만들어줘' 라고 묻는 개발자의 결과물은 천차만별이다. 테스트 코드는 AI에게 던지는 가장 수준 높은 질문이자 명령이다. 자연어는 모호하지만, 테스트 코드는 명확하다. AI가 엉뚱한 코드를 생성하지 않도록 가이드라인 역할을 하는 것이다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;구체적인 알고리즘이나 기능 구현 로직은 AI에게 맡기고, 개발자들은 이 제품이 사용자에게 어떤 가치를 주어야 하는가에 더 많은 에너지를 쓸 수 있다. AI가 짜준 코드가 마음에 들지 않아도, 탄탄한 테스트 코드가 있다면 언제든 자신 있게 코드를 갈아 엎고 최적화도 가능한 것이다.&amp;nbsp;&lt;/blockquote&gt;</description>
      <category>Tech/TDD</category>
      <category>TDD</category>
      <author>erika0915</author>
      <guid isPermaLink="true">https://erika0915.tistory.com/152</guid>
      <comments>https://erika0915.tistory.com/152#entry152comment</comments>
      <pubDate>Fri, 19 Dec 2025 13:53:37 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 액츄에이터 (Actuator)</title>
      <link>https://erika0915.tistory.com/151</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;액츄에이터&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 운영에서 '모니터링'은 장애 대응의 핵심이다. 단순히 기능을 개발하는 것을 넘어, 운영 단계에서 서비스가 살아있는지, 로그 설정은 정상인지, 커넥션 풀은 얼마나 사용되는지 확인하는 비기능적 요소들을 '프로덕션 준비 기능'이라 부른다. 스프링 부트 액츄에이터는 이러한 지표나 감사 기능을 편리하게 제공한다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;1) 액츄에이터 시작하기&lt;/u&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액츄에이터를 사용하려면 프로젝트 설정 (`build.gradle`)에 의존성을 추가해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1766144878925&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 후 서버를 실행하고 `http://localhost:8080/actuator`에 접속하면 사용 가능한 엔드포인트 목록을 확인할 수 있다.&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;2) 엔드포인트 설정&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액츄에이더 기능을 사용하기 위해서는 2가지 단계가 필요하다.&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;활성화 : 해당 기능 자체를 켤지 끌지 결정&amp;nbsp;&lt;/li&gt;
&lt;li&gt;노출 : 활성화된 기능을 HTTP나 JMX 어디에 노출할지 결정&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;
&lt;pre id=&quot;code_1766144967182&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;management:
  endpoints:
    web:
      exposure:
        include: &quot;*&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;&lt;u&gt;3) 자주 사용하는 핵심 엔드포인트&lt;/u&gt;&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;`health` : 애플리케이션의 상태를 확인한다. (UP/DOWN) db, 디스크 공간 등 상세 정보를 확인할 수 있다.&lt;/li&gt;
&lt;li&gt;`info` : 자바 버전, OS 정보, 빌드 정보 등 애플리케이션의 기본 정보를 노출한다.&lt;/li&gt;
&lt;li&gt;`loggers` : 현재 로깅 설정을 확인하고 &lt;u&gt;서버 재시작 없이 실시간으로 로그 레벨을 변경&lt;/u&gt;할 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;u&gt;실시간 로그 레벨 변경하기&lt;/u&gt;&amp;nbsp;&lt;br /&gt;운영 중 장애가 발생했을 때 서버를 끄지 않고 `DEBUG`로그를 남기고 싶다면 ?&amp;nbsp;&lt;br /&gt;-&amp;gt; `loggers` 엔드포인트를 활용하면 된다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;Postman을 사용하여&amp;nbsp;&lt;br /&gt;- Method : `POST`&amp;nbsp;&lt;br /&gt;- URL : `http://localhost:8080/actuator/loggers`&lt;br /&gt;- Body (JSON) : `{&quot;configuredLevel&quot; : &quot;TRACE&quot;}`&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;추가) 보안 설정은 필수 !&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액츄에이터는 내부 정보를 많이 노출하므로 보안이 매우 중요하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 포트 분리 : 액츄에이터 전용 포트를 설정하여 내부망에서만 접근하게 한다. (`management.server.port=9292`)&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;3) 경로 변경 : 기본 `/actuator` 경로를 다른 이름으로 변경할 수 있다. (`management.endpoints.web.base-path : &quot;/manage&quot;`)&lt;/p&gt;</description>
      <category>Spring</category>
      <category>Spring</category>
      <category>springboot</category>
      <category>스프링</category>
      <category>스프링부트</category>
      <author>erika0915</author>
      <guid isPermaLink="true">https://erika0915.tistory.com/151</guid>
      <comments>https://erika0915.tistory.com/151#entry151comment</comments>
      <pubDate>Thu, 18 Dec 2025 21:24:20 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 가비지 컬렉션 알고리즘 종류</title>
      <link>https://erika0915.tistory.com/150</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;가비지 컬렉션 알고리즘 종류&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JVM이 메모리를 자동으로 관리해주는 것은 개발자의 입장에서 굉장히 유용하다. 그러나 GC를 수행하기 위해 Stop-The-World가 발생하고 이 때문에 애플리케이션이 중지되는 문제점이 발생하기도 한다. 자바가 발전됨에 따라 힙 영역의 사이즈가 커지면서 애플리케이션의 지연 현상이 두드러지게 되었고, 이를 최적화하기 위해 다양한 &lt;u&gt;가비지 컬렉션 알고리즘&lt;/u&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) Serial GC&amp;nbsp;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;서버의 CPU 코어가 1개일 때 사용하기 위해 개발된 가장 단순한 GC이다. GC를 처리하는 스레드가 1개라 Stop-The-World 시간이 길다. 보통 실무에서는 사용하지 않는다고 한다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmVZDN/dJMcaajxFo6/J7k8gHYAjfDkZSZNOk2PsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmVZDN/dJMcaajxFo6/J7k8gHYAjfDkZSZNOk2PsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmVZDN/dJMcaajxFo6/J7k8gHYAjfDkZSZNOk2PsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmVZDN%2FdJMcaajxFo6%2FJ7k8gHYAjfDkZSZNOk2PsK%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;400&quot; height=&quot;486&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;734&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) Parallel GC&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;nbsp;Java8의 디폴트 GC이며, Serial GC와 기본적인 알고리즘은 같지만, Young 영역의 Minor GC를 멀티 스레드로 수행한다. Serial GC에 비해서는 Stop-The-World 시간이 감소한다.&amp;nbsp;&lt;/blockquote&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) Parallel Old GC (Parallel Compacting Collector)&amp;nbsp;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Parallel GC를 개선한 버전으로 Young 영역 뿐만 아니라, Old 영역에서도 멀티 스레드로 GC 수행이 가능하다. 새로운 가비지 컬렉션 청소 방식인 Mark-Summary-Compact 방식을 이용한다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) CMS GC (Concurrnet Mark Sweep)&amp;nbsp;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;어팰리케이션의 스레드와 GC 스레드가 동시에 실행되어 Stop-The-World 시간을 최대한 줄이기 위해 고안된 GC이다. GC 대상을 파악하는 과정이 복잡한 여러 단계로 수행되기 때문에 다른 GC 대비 CPU 사용량이 높다. GC 과정이 매우 복잡해지며, 메모리 파편화의 문제가 존재한다. Java9 버전부터 deprecated 되었고 결국 Java14에서는 사용이 중지되었다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5) G1 GC (Garbage First)&amp;nbsp;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;CMS GC를 대체하기 위해 JDK7 버전에서 최초로 release된 GC이다. Java9+ 버전의 디폴트 GC이며, 4GB 이상의 힙 메모리, Stop-The-World 시간이 0.5초 정도 필요한 상황에 사용한다. 기존의 GC 알고리즘에서는 힙 영역을 물리적으로 고정된 Young/Old 영역으로 나누어 사용하였지만, G1 GC는 아예 이러한 개념을 뒤엎는 Region 이라는 개념을 도입해서 사용한다.&amp;nbsp;&lt;br /&gt;이는 힙 영역을 Region이라는 영역으로 체스같이 분할하여 상황에 따라 Eden, Survivor, Old 등 역할을 고정이 아닌 동적으로 부여하는 것이다. Garbage로 가득찬 영역을 빠르게 회수하여 빈 공간을 확보하므로, 결국 GC 빈도가 줄어드는 효과를 얻게 되는 원리이다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6) Shenandoah GC&amp;nbsp;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Java12에서 release되었고, 기존 CMS가 가진 단편화, G1이 가진 pause 이슈를 해결한다. 강력한 Concurrency와 가벼운 GC 로직으로 힙 사이즈에 영향을 받지 않고 일정한 pause 시간이 소요되는 것이 특징이다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7) ZGC (Z Garbage Collector)&amp;nbsp;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Java15에 release되었고, 대량의 메모리를 low-latency로 잘 처리하기 위해 디자인 된 GC이다. G1의 Region 처럼, ZGC는 ZPage라는 영역을 사용하여 G1의 Region은 크기가 고정인데 비해, ZPage는 2MB 배수로 동적으로 운영된다. ZGC가 내세우는 최대 장점 중 하나는 힙 크기가 증가하더라도 Stop-The-World의 시간이 절대 10ms를 넘지 않는다는 것이다.&amp;nbsp;&lt;/blockquote&gt;</description>
      <category>Java</category>
      <category>java</category>
      <category>자바</category>
      <author>erika0915</author>
      <guid isPermaLink="true">https://erika0915.tistory.com/150</guid>
      <comments>https://erika0915.tistory.com/150#entry150comment</comments>
      <pubDate>Tue, 16 Dec 2025 22:31:46 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] @RequestBody JSON 바인딩 원리</title>
      <link>https://erika0915.tistory.com/147</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;들어가며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 애플리케이션 개발에서 클라이언트(프론트엔드)와 서버(백엔드)는 서로 정보를 주고 받는다. 이때 클라이언트는 보통 JSON(Javascript Object Notation) 형태로 데이터를 전송한다. 그러나, 서버 특히 자바 기반의 스프링 서버는 이러한 정보를 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;클라이언트 JSON 데이터를 서버의 자바 객체로, 또는 그 반대로 변환하는 과정은 웹 API의 핵심이다. 이 복잡하지만 필수적인 '통역' 작업을 스프링에서는 `@RequestBody` 어노테이션과 `ObjectMapper`의 역할이 중요하다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;@RequestBody&amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;클라이언트(프론트)가 HTTP Body에 JSON 형식의 데이터를 담아 서버로 전송할 때, Spring Controlle에서 `@RequestBody` 어노테이션은 이 JSON 형식의 데이터를 원하는 자바 객체 (DTO)로 변환해주는 역할을 한다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;이 어노테이션이 없다면 개발자는 HTTP 요청의 Body에서 직접 JSON 문자열을 꺼내어 파싱하고 필드별로 객체에 값을 설정하는 번거로운 작업을 수동으로 해야 했을 것이다.&amp;nbsp;&lt;/blockquote&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;span&gt;✅ &lt;/span&gt;요청 처리 흐름의 핵심 경로&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) `HandlerMethodArgumentResolver` : 요청을 처리할 적절한 핸들러 메서드가 결정된 후, 메서드의 각 인자를 처리할 `ArgumentResolver`가 호출된다. `@RequestBody`가 붙은 인자는 `RequestResponseBodyMethodProcessor`에 의해 처리된다.&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) `RequestResponseBodyMethodProcessor` : 이 프로세서는 HTTP Body를 읽어 들이고, 이 Body의 내용을 자바 객체로 변환하기 위해 `HttpMessageConverter` 인터페이스에게 작업을 위임한다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;HttpMessageConverter&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Spring에서는 이 변환 작업을 수행하기 위해 내부적으로 `HttpMessageConverter` 라는 핵심 인터페이스를 사용한다. 이름 그대로 HTTP 메시지(요청 또는 응답)를 자바 객체로 변환하거나 그 반대의 작업을 수행하는 역할을 한다.&amp;nbsp;&lt;/blockquote&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&gt;✅ 컨버터 선택 및 작동 원리&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;`RequestResponseBodyMethodProcessor`는 요청 Body의 JSON String을 읽어 들인 후, 등록된 여러 컨버터 중 다음 기준에 따라 적절한 컨버터를 선택한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;1) 지원 여부 확인 : 현재 컨버터가 해당 미이더 타입과 대상 자바 클래스를 처리할 수 있는지 확인한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;2) 기본 컨버터 : Spring Boot는 JSON 처리를 위해 `MappingJackson2HttpMessaageConverter`를 기본적으로 사용하도록 자동 구성된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Jackson의 ObjectMapper&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;`MappingJackson2HttpMessageConverter`가 JSON 변환을 담당하는 Spring의 겉모습이라면, 그 내부에서 실제 직렬화와 역직렬화를 수행하는 것은 Jackson 라이브러리의 핵심 클래스인 `ObjectMapper`이다.&amp;nbsp;&lt;/blockquote&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;span&gt;✅ &lt;/span&gt;ObjectMapper의 역할&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`ObjectMapper`는 자바 객체와 JSON 데이터 간의 직렬화 및 역직렬화를 처리하는 Jackson 라이브러리의 핵심 클래스이다.&amp;nbsp;&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;역직렬화 (Deserialization) : JSON -&amp;gt; Java Object
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`@RequestBody`가 작동할 때 발생한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;클라이언트가 보낸 JSON 문자열 ObjectMapper가 읽고, 개발자가 지정한 자바 객체의 필드에 대응하는 값을 찾아 넣어 객체를 생성한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;직렬화 (Serialization) : Java Object -&amp;gt; JSON&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`@RequestBody`나 `@RestController`에서 자바 객체를 리턴할 때 발생한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;서버가 처리한 자바 객체를 ObjectMapper가 읽고, 클라이언트에게 응답하기 위한 JSON 문자열로 변환한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Spring</category>
      <category>Spring</category>
      <category>springboot</category>
      <category>스프링</category>
      <category>스프링부트</category>
      <author>erika0915</author>
      <guid isPermaLink="true">https://erika0915.tistory.com/147</guid>
      <comments>https://erika0915.tistory.com/147#entry147comment</comments>
      <pubDate>Fri, 28 Nov 2025 14:43:20 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Spring AI와 LangChain4j 비교</title>
      <link>https://erika0915.tistory.com/146</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;평소 AI 개발을 진행한다고 하면, python을 활용한 FastAPI나 Flask 프레임워크를 주로 활용하여 빠르게 API를 구축하는 방식을 사용했다. 파이썬은 풍부한 라이브러리 생태계 덕분에 AI 개발의 사실상 표준으로 여겨져 왔다. 반면 자바 생태계에서는 오랫동안 머신러닝 관련 라이브러리나 프레임워크의 부재로 인해 LLM 기반 애플리케이션을 구축하는 것이 다소 제한적이었다. 최근 이러한 흐름이 바뀌고, 엔터프라이즈 환경에서 압도적인 점유율을 자랑하는 자바 생태계에서 Spring AI가 등장하면서 자바 개발자들이 기존의 기술 스택을 유지하면서도 최신 AI 기능을 손쉽게 통합할 수 있게 되었다.&amp;nbsp;&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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Spring AI와 LangChain4j는 자바 개발자에게 LLM을 활용한 애플리케이션 개발을 위한 추상화 계층을 제공하며, LLM과 상호작용, RAG(검색 증강 생성), 도구 호출(Tool Calling) 등을 지원한다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Spring AI vs. LangChain4j&amp;nbsp;&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 84px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20.5426%; height: 17px;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 39.8449%; height: 17px;&quot;&gt;Spring AI&lt;/td&gt;
&lt;td style=&quot;width: 39.6124%; height: 17px;&quot;&gt;LangChain4j&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20.5426%; height: 17px;&quot;&gt;기반 생태계&lt;/td&gt;
&lt;td style=&quot;width: 39.8449%; height: 17px;&quot;&gt;Spring Framework (Spring Boot 기반)&lt;/td&gt;
&lt;td style=&quot;width: 39.6124%; height: 17px;&quot;&gt;LangChain (Python) 프로젝트에서 영감을 받은 독자적인 자바 라이브러리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20.5426%; height: 17px;&quot;&gt;핵심 목표&lt;/td&gt;
&lt;td style=&quot;width: 39.8449%; height: 17px;&quot;&gt;Spring 생태계와의 긴밀한 통합 및 Spring 개발자들에게 친숙한 방식으로 AI 기능 제공&lt;/td&gt;
&lt;td style=&quot;width: 39.6124%; height: 17px;&quot;&gt;LLM 통합을 위한 유연하고 추상적인 도구 모음 및 RAG, 에이전트 구축 간소화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20.5426%; height: 17px;&quot;&gt;개발 방식&lt;/td&gt;
&lt;td style=&quot;width: 39.8449%; height: 17px;&quot;&gt;선언적 접근 및 Spring Boot의 자동 구성 활용&lt;/td&gt;
&lt;td style=&quot;width: 39.6124%; height: 17px;&quot;&gt;빌더 패턴 중심의 명시적인 구성, 저수준/고수준 API 모두 제공&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 기능 비교&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;다양한 모델 지원&lt;/u&gt; : 다양한 LLM 제공자와 통합을 지원&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;RAG(검색 증강 생성) 및 벡터 데이터베이스&lt;/u&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;Spring AI : 다양한 벡터 DB에 대한 일관된 API를 제공하며, RAG 구현에 필요한 ETL 파이프라인 및 문서 처리까지 지원한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;LangChain4j : 임베딩 생성, 저장소 검색, LLM 연결 등 RAG 구축에 필요한 핵심 구성요소들을 모듈화하여 제공한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;구조화된 출력&lt;/u&gt; : 두 프레임워크 모두 모델의 출력을 Java POJO(Plain Old Java Object)로 쉽게 매핑할 수 있는 기능을 제공하여, 응답 처리를 용이하게 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;도구 호출&lt;/u&gt; : 외부 API나 클라이언트 측 기능을 실행하도록 요청할 수 있는 도구 호출 기능을 지원&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;어떤 프레임워크를 선택해야 할까 ?&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;Spring AI를 선택해야 하는 경우&lt;/u&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;기존 프로젝트가 Spring Boot 기반이며, AI 기능을 기존 서비스 구조에 가장 빠르고 자연스럽게 통합하고 싶을 때&lt;/li&gt;
&lt;li&gt;Spring의 표준적인 방식 (설정 파일, 자동 구성, 서비스/컨트롤러 구조) 그대로 AI 기능을 관리하고 싶을 때&amp;nbsp;&lt;/li&gt;
&lt;li&gt;유지보수 및 일관성이 중요하며, 모델이 바뀌어도 동일한 코드 구조를 유지하고 싶을 때&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;LangChain4j를 선택해야 하는 경우&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에 의존하지 않는 순수 자바 또는 다른 엔터프라이즈 자바 프레임워크를 사용할 때&amp;nbsp;&lt;/li&gt;
&lt;li&gt;LLM 통합의 가장 낮은 수준까지 세밀하게 제어하고, 구성 요소(LLM, 임베딩, 메모리)를 자유롭게 조합하여 복잡한 에이전트를 만들고 싶을 때&amp;nbsp;&lt;/li&gt;
&lt;li&gt;LLM 프레임워크의 유연성과 확장성을 최우선으로 고려하며, 특정 기능에 특화된 모듈을 사용하고 싶을 때&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Spring</category>
      <category>Spring</category>
      <category>springboot</category>
      <category>스프링</category>
      <category>스프링부트</category>
      <author>erika0915</author>
      <guid isPermaLink="true">https://erika0915.tistory.com/146</guid>
      <comments>https://erika0915.tistory.com/146#entry146comment</comments>
      <pubDate>Mon, 24 Nov 2025 09:50:37 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 임베디드 타입 (@Embeddable, @Embedded)</title>
      <link>https://erika0915.tistory.com/145</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;임베디드 타입은 언제 사용하나요 ?&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔티티 내부에 주소, 기간, 생성일/수정일 등 여러 필드가 하나의 논리적인 묶음으로 사용되는 경우가 존재한다. 예를 들어, Member 엔티티에 city, street, zipcode 등을 개별 필드로 나열하기보다 논리적으로 관련된 필드들을 하나의 값 타입 (Value Type) 객체로 묶어서 관리한다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;@Embeddable &amp;amp; @Embedded 사용법&amp;nbsp;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;1) @Embeddable&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;값 타입으로 사용될 클래스 (예를 들어, Address)에 붙여 JPA에게 '이 클래스는 엔티티에 내장될 수 있는 타입입니다.' 라고 알려준다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;특징&amp;nbsp;&lt;br /&gt;- 기본 생성자가 필수이다.&amp;nbsp;&lt;br /&gt;- 엔티티가 아니므로 `@Id` 가 없다.&amp;nbsp;&lt;/blockquote&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) @Embedded&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;`@Embeddable` 클래스를 사용하는 엔티티의 필드에 붙어 '이 필드에 임베디드 타입을 사용합니다.' 라고 명시한다. 일반적으로는 `@Embeddable` 만 있어도 작동하지만 명시적으로 붙여주는 것을 권장한다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;임베디드 타입을 사용하는 이유&amp;nbsp;&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;&lt;u&gt;1) 높은 응집도&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;2) 재사용성&lt;/u&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;u&gt;3) 의미 있는 메서드 추가&lt;/u&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 타입 자체에 해당 값과 관련된 비즈니스 로직 메서드를 추가할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주의사항&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;1) 값 타입의 불변성&amp;nbsp;&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;-&amp;gt; 임베디드 타입을 불변 객체(Immutable Object)로 설계해야 한다. Setter를 제거하고 생성자를 통해서만 값을 설정하고, 값을 변경해야 할 경우에는 기존 객체를 수정하는 대신, 새로운 객체를 생성하여 갈아끼우는 방식으로 사용한다.&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;2) 속성 재정의 : `@AttributeOverride`&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 엔티티에서 같은 임베디드 타입을 여러 필드로 사용해야 하는 경우에 db 칼럼명이 중복되는 문제가 발생한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; `@AttributeOverride` 와 `@AttributeOverrides`를 사용하여 사용하는 쪽에서 db 칼럼명을 다르게 지정해줄 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예제&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`Address` 임베디드 타입과 이를 사용하는 `Member` 엔티티 코드이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1765783447640&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Embeddable
@Getter 
@NoArgsConstructor(access = AccessLevel.PROTECTED) 
public class Address {
    private String city;
    private String street;
    private String zipcode;

    @Builder 
    public Address(String city, String street, String zipcode) {
        this.city = city;
        this.street = street;
        this.zipcode = zipcode;
    }
    
    public String getFullAddress() {
        return city + &quot; &quot; + street + &quot; (&quot; + zipcode + &quot;)&quot;;
    }
}


@Entity
public class Member {

    @Id @GeneratedValue
    private Long id;
    
    private String name;

    @Embedded
    private Address homeAddress; 
}&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;</description>
      <category>Spring</category>
      <category>Spring</category>
      <category>springboot</category>
      <category>스프링</category>
      <category>스프링부트</category>
      <author>erika0915</author>
      <guid isPermaLink="true">https://erika0915.tistory.com/145</guid>
      <comments>https://erika0915.tistory.com/145#entry145comment</comments>
      <pubDate>Mon, 10 Nov 2025 20:00:23 +0900</pubDate>
    </item>
    <item>
      <title>[Java] JVM GC 작동 원리</title>
      <link>https://erika0915.tistory.com/144</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Garbage Collection (GC)&amp;nbsp;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;가비지 컬렉션 (Garbage Collection)은 자바의 메모리 관리 방법 중 하나로 JVM의 Heap 영역에서 동적으로 할당했던 메모리 중 필요 없게 된 메모리 객체를 모아 주기적으로 제거하는 프로세스를 말한다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;C/C++ 언어에서는 이러한 가비지 컬렉션이 없어 개발자가 수동으로 메모리 할당과 해제를 일일이 해줘야한다. 반면, Java에서는 가비지 컬렉터가 메모리 관리를 대신해주기 때문에 Java 프로세스가 한정된 메모리를 효율적으로 사용할 수 있게 하고, 개발자 입장에서는 메모리 관리, 메모리 누구 문제에 대해서는 관리하지 않아도 된다.&amp;nbsp;&lt;/blockquote&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;메모리 누수 방지 : 개발자가 메모리 해제에 대해 신경 쓰지 않아도 되므로, 메모리 누수와 같은 잠재적인 오류를 줄여준다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;생산성 향상 : 메모리 관리에 들어갈 개발 시간을 핵심 비즈니스 로직 개발에 집중할 수 있게 한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;효율적인 메모리 사용 : JVM 프로세스가 한정된 메모리를 효율적으로 사용할 수 있도록 돕는다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&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;gt; 이를 `Stop-The-World (STW)`라 한다.&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;이로 인해 GC가 너무 자주 실행되면 소프트웨어 성능 하락의 문제가 되기도 한다. 예를 들면, 익스플로러는 이 가비지 컬렉션을 너무 자주 실행하여 성능 문제를 일으키는 것으로 악명이 높았다. 이런 특성에 따라 실시간 성이 매우 강조되는 프로그램일 경우 가비지 컬렉터(GC)에게 메모리를 맞기는 것은 맞지 않을 수 있다. 따라서 애플리케이션의 사용성을 유지하면서 효율적이게 GC를 실행하는 최적화 작업이 개발자의 숙제가 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;GC 최적화 작업 -&amp;gt; GC 튜닝이라고 한다.&amp;nbsp;&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;가비지 컬렉션 대상&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가비지 컬렉션은 어떤 Object를 Garbage로 판단해서 스스로 지우는 것일까 ? 가비지 컬렉션은 특정 객체가 Garbage인지 아닌지 판단하기 위해 도달성, 도달 능력(Reachability) 이라는 개념을 적용한다.&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;객체가 레퍼런스가 있다면 `Reachable`로 구분되고, 객체에 유효한 레퍼런스가 없다면 `Unreachable`로 구분해버리고 수거한다.&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;Reachable : 객체가 참조되고 있는 상태&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Unreachable : 객체가 참조되고 있지 않은 상태 -&amp;gt; GC의 대상이 된다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;가비지 컬렉션 방식&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GC가 Unreachable한 객체를 어떤 방식으로 청소하는가.&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;Mark and Sweep&amp;nbsp;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;가비지 컬렉션이 될 대상 객체를 식별(Mark)하고 제거(Sweep)하며 객체가 제거되어 파편화된 메모리 영역을 앞에서부터 채워나가는 작업을 수행한다.&amp;nbsp;&lt;br /&gt;- Mark 과정 : 먼저 Root Space로부터 그래프 순회를 통해 연결된 객체들을 찾아내어 각각 어떤 객체를 참조하고 있는지 찾아서 마킹한다.&amp;nbsp;&lt;br /&gt;- Sweep 과정 : 참조하고 있지 않은 객체 즉, Unreachable 객체들을 Heap에서 제거한다.&amp;nbsp;&lt;br /&gt;- Compact 과정 : Sweep 후에 분산된 객체들을 Heap의 시작 주소로 모아 메모리가 할당된 부분과 그렇지 않은 부분으로 압축한다.&amp;nbsp;&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;가비지 컬렉션 동작 과정&amp;nbsp;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Heap 메모리 구조&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JVM의 Heap 영역은 동적으로 레퍼런스 데이터가 저장되는 공간으로, 가비지 컬렉션에 대상이 되는 공간이다. Heap 영역은 처음 설계될 때 다음의 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;1) 대부분의 객체는 금방 접근 불가능한 상태가 된다.&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 data-ke-size=&quot;size16&quot;&gt;즉, &lt;u&gt;객체는 대부분 일회성이며, 메모리에 오랫동안 남아있는 경우는 드물다&lt;/u&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;이러한 특성을 이용해 개발자들은 보다 효율적인 메모리 관리를 위해, 객체의 생존 기간에 따라 물리적인 Heap 영역을 나누게 되었고, Young과 Old 총 2가지 영역으로 설계하였다.&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;1360&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DimK7/dJMb99StPWx/2lNLFYvCh6rL7KBDiE7aX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DimK7/dJMb99StPWx/2lNLFYvCh6rL7KBDiE7aX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DimK7/dJMb99StPWx/2lNLFYvCh6rL7KBDiE7aX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDimK7%2FdJMb99StPWx%2F2lNLFYvCh6rL7KBDiE7aX0%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-origin-width=&quot;1360&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;1) Young 영역 (Young Generation)&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새롭게 생성된 객체가 할당되는 영역으로 대부분의 객체가 금방 Unreachable 상태가 되기 때문에 많은 Young 영역에 생성되었다가 사라진다. Young 영역에 대한 가비지 컬렉션을 &lt;u&gt;Minor GC&lt;/u&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;그리고 Young 영역을 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;size18&quot;&gt;&lt;u&gt;Eden&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`new`를 통해 새로 생성된 객체가 위치한다. 정기적인 쓰레기 수집 후 살아남은 객체들은 Survivor 영역으로 보낸다.&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;u&gt;Survivor 0/Survivor 1&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최소 1번의 GC 이상 살아남은 객체가 존재하는 영역으로 Survivor 영역에는 특별한 규칙이 있는데, Survivor 0 또는 Survivor 1 둘 중 하나에는 꼭 비어 있어야 하는 것이다.&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;2) Old 영역 (Old Generation)&lt;/u&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Younng 영역에서 Reachable 상태를 유지하여 살아남은 객체가 복사되는 영역으로 Young 영역보다 크게 할당되며, 영역의 크기가 큰 만큼 가비지는 적게 발생한다. Old 영역에 대한 가비지 컬렉션을 Major GC 또는 Full GC라고 부른다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;✅ &lt;/span&gt;Minor GC 과정&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0fx5t/dJMb99500SN/S0bwGrI8D2rAxc4Kwi9wT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0fx5t/dJMb99500SN/S0bwGrI8D2rAxc4Kwi9wT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0fx5t/dJMb99500SN/S0bwGrI8D2rAxc4Kwi9wT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0fx5t%2FdJMb99500SN%2FS0bwGrI8D2rAxc4Kwi9wT1%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;316&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;모든 객체는 처음에 Young Generation에 생성되게 된다. Young Generation의 공간은 Old Generation에 비해 상대적으로 작기 때문에 메모리 상의 객체를 찾아 제거하는 데 적은 시간이 걸린다. (작은 공간에서 데이터를 찾기 때문.) 이 때문에 Young Generation 영역에서 발생되는 GC를 Minor GC라고 불린다.&amp;nbsp;&lt;/blockquote&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;✅&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Major GC 과정&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1038&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vHMX2/dJMcaiPm5FV/0MDXaY0KBmXMqCkVi6t9Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vHMX2/dJMcaiPm5FV/0MDXaY0KBmXMqCkVi6t9Bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vHMX2/dJMcaiPm5FV/0MDXaY0KBmXMqCkVi6t9Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvHMX2%2FdJMcaiPm5FV%2F0MDXaY0KBmXMqCkVi6t9Bk%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;312&quot; data-origin-width=&quot;1038&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Major GC는 Old 영역의 데이터가 가득 차면 GC를 실행하는 단순한 방식이다. Old 영역에 할당된 메모리가 허용치를 넘게 되면, Old 영역에 있는 모든 객체들을 검사하여 참조되지 않는 객체들을 한꺼번에 삭제하는 Major GC가 실행되게 된다. 하지만 Old Generation은 Young Generation에 비해 상대적으로 큰 공간을 가지고 있어, 이 공간에서 메모리 상의 객체 제거에 많은 시간이 걸리게 된다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;바로 여기서 앞서 말한 `Stop-The-World` 문제가 발생하게 된다. Major GC가 일어나면 Thread가 멈추고 `Mark and Sweep` 작업을 해야 해서 CPU에 부하를 주기 때문에 멈추거나 버벅이는 현상이 일어나기 때문이다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;이것이 &lt;u&gt;자바 가비지 컬렉션 알고리즘&lt;/u&gt;을 발전 시키게 되었다.&amp;nbsp;&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&amp;nbsp;&lt;/h2&gt;
&lt;figure id=&quot;og_1765891786862&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;☕ 가비지 컬렉션 동작 원리 &amp;amp; GC 종류   총정리&quot; data-og-description=&quot;Garbage Collection(GC) 이란? 가비지 컬렉션(Garbage Collection, 이하 GC)은 자바의 메모리 관리 방법 중의 하나로 JVM(자바 가상 머신)의 Heap 영역에서 동적으로 할당했던 메모리 중 필요 없게 된 메모리 객&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98GC-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot; data-og-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98GC-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bmj1Xu/hyZPNyLtNS/adk7TWDgBTDGBWVlnlOg5K/img.png?width=800&amp;amp;height=485&amp;amp;face=0_0_800_485,https://scrap.kakaocdn.net/dn/bdefDc/hyZOFWGfJE/1XOHYTLtF14PgbQzKPiplk/img.png?width=800&amp;amp;height=485&amp;amp;face=0_0_800_485,https://scrap.kakaocdn.net/dn/b130RU/hyZPw5PBsc/ykZNl2PlLZt8aZDgETtDMK/img.png?width=1200&amp;amp;height=728&amp;amp;face=0_0_1200_728&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98GC-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%F0%9F%92%AF-%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/JAVA-%E2%98%95-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98GC-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%F0%9F%92%AF-%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/bmj1Xu/hyZPNyLtNS/adk7TWDgBTDGBWVlnlOg5K/img.png?width=800&amp;amp;height=485&amp;amp;face=0_0_800_485,https://scrap.kakaocdn.net/dn/bdefDc/hyZOFWGfJE/1XOHYTLtF14PgbQzKPiplk/img.png?width=800&amp;amp;height=485&amp;amp;face=0_0_800_485,https://scrap.kakaocdn.net/dn/b130RU/hyZPw5PBsc/ykZNl2PlLZt8aZDgETtDMK/img.png?width=1200&amp;amp;height=728&amp;amp;face=0_0_1200_728');&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;☕ 가비지 컬렉션 동작 원리 &amp;amp; GC 종류   총정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Garbage Collection(GC) 이란? 가비지 컬렉션(Garbage Collection, 이하 GC)은 자바의 메모리 관리 방법 중의 하나로 JVM(자바 가상 머신)의 Heap 영역에서 동적으로 할당했던 메모리 중 필요 없게 된 메모리 객&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java</category>
      <category>java</category>
      <category>자바</category>
      <author>erika0915</author>
      <guid isPermaLink="true">https://erika0915.tistory.com/144</guid>
      <comments>https://erika0915.tistory.com/144#entry144comment</comments>
      <pubDate>Mon, 10 Nov 2025 19:44:11 +0900</pubDate>
    </item>
    <item>
      <title>[Java] JVM(자바 가상 머신)에 대한 모든 것</title>
      <link>https://erika0915.tistory.com/143</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;자바 가상 머신(JVM)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;자바 프로그램을 실행하기 위한 가상 머신이다. 이를 통해 자바 언어로 작성된 프로그램을 특정 운영 체제에 종속되지 않고 실행할 수 있게 해준다.&amp;nbsp;&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;자바 가상 머신(JVM)의 동작 방식&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바 가상 머신(JVM)의 역할은 자바 애플리케이션을 클래스 로더를 통해 읽어 자바 API와 함께 실행하는 것이다.&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-origin-width=&quot;1374&quot; data-origin-height=&quot;1002&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NCt1Y/dJMcajtXGr2/s3lt6aEilb3qQH5UOeStW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NCt1Y/dJMcajtXGr2/s3lt6aEilb3qQH5UOeStW1/img.png&quot; data-alt=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-JVM-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD-%EC%8B%AC%ED%99%94%ED%8E%B8&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NCt1Y/dJMcajtXGr2/s3lt6aEilb3qQH5UOeStW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNCt1Y%2FdJMcajtXGr2%2Fs3lt6aEilb3qQH5UOeStW1%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;438&quot; data-origin-width=&quot;1374&quot; data-origin-height=&quot;1002&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://inpa.tistory.com/entry/JAVA-%E2%98%95-JVM-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD-%EC%8B%AC%ED%99%94%ED%8E%B8&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1) 자바 프로그램을 실행하면 JVM은 OS로부터 메모리를 할당받는다.&amp;nbsp;&lt;br /&gt;2) 자바 컴파일러(javac)가 자바 소스코드(.java)를 자바 바이트 코르(.class)로 컴파일한다. -&amp;gt; &lt;u&gt;컴파일 (Compile)&lt;/u&gt;&amp;nbsp;&lt;br /&gt;3) Class Loader는 동적 로딩을 통해 필요한 클래스들을 로딩 및 링크하여 Runtime Data Area(실질적인 메모리를 할당 받아 관리하는 영역)에 올린다.&amp;nbsp;&lt;br /&gt;4) Runtime Data Area에 로딩 된 바이트 코드는 Execution Engine을 통해 해석된다.&amp;nbsp;&lt;br /&gt;5) 이 과정에서 Execution Engine에 의해 Garbage Collector의 작동과 Thread 동기화가 이루어진다.&amp;nbsp;&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자바 가상 머신(JVM)의 구조&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;1276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qXbqr/dJMcaaRnCit/HK6hvlh8SFoE8Q5JA0bNz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qXbqr/dJMcaaRnCit/HK6hvlh8SFoE8Q5JA0bNz1/img.png&quot; data-alt=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-JVM-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD-%EC%8B%AC%ED%99%94%ED%8E%B8&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qXbqr/dJMcaaRnCit/HK6hvlh8SFoE8Q5JA0bNz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqXbqr%2FdJMcaaRnCit%2FHK6hvlh8SFoE8Q5JA0bNz1%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;550&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;1276&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://inpa.tistory.com/entry/JAVA-%E2%98%95-JVM-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD-%EC%8B%AC%ED%99%94%ED%8E%B8&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;JVM은 아래와 같이 구성되어 있다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1) 클래스 로더 (Class Loader)&amp;nbsp;&lt;br /&gt;2) 실행 엔진 (Execution Engine)&lt;br /&gt;- 인터프리터 (Interpreter)&lt;br /&gt;- JIT 컴파일러 (Just-in-Time)&lt;br /&gt;- 가비지 콜렉터 (Garbage collector)&amp;nbsp;&lt;br /&gt;3) 런타임 데이터 영역 (Runtime Data Area)&amp;nbsp;&lt;br /&gt;- 메소드 영역&lt;br /&gt;- 힙 영역&lt;br /&gt;- PC Register&lt;br /&gt;- 스택 영역&lt;br /&gt;- 네이티브 메소드&amp;nbsp;&lt;br /&gt;4) JNI - 네이티브 메소드 인터페이스 (Native Method Interface)&lt;br /&gt;5) 네이비트 메소드 라이브러리 (Native Method Library)&amp;nbsp;&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;✅ &lt;/span&gt;1) 클래스 로더 (Class Loader)&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;830&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/balzvQ/dJMcai9Fgwi/8cWGf9OMCPlnYkKVuOzZD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/balzvQ/dJMcai9Fgwi/8cWGf9OMCPlnYkKVuOzZD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/balzvQ/dJMcai9Fgwi/8cWGf9OMCPlnYkKVuOzZD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbalzvQ%2FdJMcai9Fgwi%2F8cWGf9OMCPlnYkKVuOzZD0%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;358&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;830&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;`.class` 파일을 읽어 JVM의 메모리 영역(Runtime Data Area)으로 로딩하는 역할을 한다.&amp;nbsp;&lt;/blockquote&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;p data-ke-size=&quot;size16&quot;&gt;클래스 파일 로딩 순서는 다음과 같이 3단계로 구성된다. (Loading -&amp;gt; Linking -&amp;gt; Initialization)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;1) 로딩 (Loading)&lt;/u&gt; : `.class` 파일을 읽어 메모리에 로드를 수행한다. 해당 메모리가 로드되는 시점은 '파일을 실행(Runtime)'할 때이며 Runtime Data Area로 로딩시킨다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;2) 링킹 (Linking)&lt;/u&gt; : 클래스 파일이 사용 가능한지 확인 및 준비&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 검증 (Verifying) : 읽어들인 클래스가 JVM 명세에 명시된 대로 구성되어 있는지 검사&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 준비 (Preparing) : 클래스가 필요로 하는 메모리를 할당&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 분석 (Resolving) : 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;3) 초기화 (Initialization)&lt;/u&gt; : 클래스 변수들을 적절한 값으로 초기화&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;✅ &lt;/span&gt;2) 실행 엔진 (Execution Engine)&amp;nbsp;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;실행 엔진은 클래스 로더를 통해 런타임 데이터 영역에 배치된 바이트 코드를 명령의 단위로 읽어서 실행한다. &lt;br /&gt;&lt;br /&gt;자바 바이트 코드(*.class)는 기계가 바로 수행할 수 있는 언어보다는 가상머신이 이해할 수 있는 중간 레벨로 컴파일 된 코드이다. 그래서 실행 엔진은 이와 같은 바이트 코드를 실제 JVM 내부에서 기계가 실행할 수 있는 형태로 변경해준다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;이 수행 과정에서 실행 엔진은 '인터프리터'와 'JIT 컴파일러' 두 가지 방식을 혼합하여 바이트 코드를 실행한다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;1282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddz4AU/dJMcadmYqUU/H2j5d7wBiG4dxINiPChBl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddz4AU/dJMcadmYqUU/H2j5d7wBiG4dxINiPChBl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddz4AU/dJMcadmYqUU/H2j5d7wBiG4dxINiPChBl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fddz4AU%2FdJMcadmYqUU%2FH2j5d7wBiG4dxINiPChBl1%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;656&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;1282&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;2-1) 인터프리터 (Interpreter)&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이트 코드 명령어를 하나씩 읽어서 해석하고 바로 실행한다. JVM 안에서 바이트코드는 기본적으로 인터프리터 방식으로 동작된다. 다만 같은 메소드라도 여러 번 호출이 된다면 매번 해석하고 수행해야 되서 전체적인 속도는 느리다.&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;2-2) JIT 컴파일러 (Just-In-Time Compiler)&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 인터프리터의 단점을 보완하기 위해 도입된 방식으로 반복되는 코드를 발견하여 바이트 코드 전체를 컴파일하여 Native Code로 변경하고 이후에는 해당 메서드를 더 이상 인터프리팅 하지 않고 캐싱해 두었다가 네이티브 코드로 직접 실행하는 방식이다. 하나씩 인터프리팅하여 실행하는 것이 아니라, 컴파일된 네이티브 코드를 실행하는 것이기 때문에 전체적인 실행 속도는 인터프리팅 방식보다 빠르다. 하지만 바이트코드를 Native Code로 변환하는 데에도 비용이 소요되므로, JVM은 모든 코드를 JIT 컴파일러 방식으로 실행하지 않고 인터프리터 방식을 사용하다 일정 기준이 넘어가면 JIT 컴파일 방식으로 명령어를 실행하는 식으로 진행한다.&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;2-3) 가비지 컬렉터 (Garbage Collector, GC)&amp;nbsp;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바 가상 머신은 가비지 컬렉터를 이용하여 힙 메모리 영역에서 더는 사용하지 않는 메모리를 자동으로 회수해준다. C언어 같은 경우, 개발자가 해제해줘야 하지만, Java는 이 가비지 컬렉터를 이용해서 자동으로 메모리를 실시간 최적화 시켜준다. 따라서 개발자가 따로 메모리를 관리하지 않아도 되므로, 더욱 손쉽게 프로그래밍을 할 수 있도록 해준다. 일반적으로 자동으로 실행되지만, 단 GC(가비지 컬렉터)가 실행되는 시간은 정해져 있지 않다. 특히 Full GC가 발생하는 경우, GC를 제외한 모든 스레드가 중지되기 때문에 장애가 발생할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;✅&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;3) 런타임 데이터 영역 (Runtime Data Area)&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KXtMX/dJMcajtXK7P/EXtxwTKS4aKLGmj4hB8ow1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KXtMX/dJMcajtXK7P/EXtxwTKS4aKLGmj4hB8ow1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KXtMX/dJMcajtXK7P/EXtxwTKS4aKLGmj4hB8ow1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKXtMX%2FdJMcajtXK7P%2FEXtxwTKS4aKLGmj4hB8ow1%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;232&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;런타임 데이터 영역은 쉽게 말하면, JVM의 메모리 영역으로 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;런타임 데이터 영역은 위 그림과 같이 크게 Method Area, Heap Area, Stack Area, PC Register, Native Method Stack으로 나눌 수 있다.&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;이때 Method Area, Heap Area는 모든 스레드(Thread)가 공유하는 영역이고, 나머지 Stack Area, PC Register, Native Method Stack은 각 스레드마다 생성되는 개별 영역이다.&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;1184&quot; data-origin-height=&quot;1052&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqhyb6/dJMcadN2LHT/tDVwQs9vMjRzAhPnIXZGgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqhyb6/dJMcadN2LHT/tDVwQs9vMjRzAhPnIXZGgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqhyb6/dJMcadN2LHT/tDVwQs9vMjRzAhPnIXZGgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbqhyb6%2FdJMcadN2LHT%2FtDVwQs9vMjRzAhPnIXZGgk%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;533&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;1052&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;3-1) 메서드 영역 (Method Area)&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드 영역은 JVM이 시작될 때 생성되는 공간으로 바이트 코드(.class)를 처음 메모리 공간에 올릴 때 초기화되는 대상을 저장하기 위한 메모리 공간이다. JVM이 동작하고 클래스가 로드될 때 적재되서 프로그램이 종료될 때까지 저장된다. 모든 스레드가 공유하는 영역이라, 다음과 같이 초기화 코드 정보들이 저장되게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- field info : 멤버 변수의 이름, 데이터 타입, 접근 제어자 정보&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- method info : 메서드 이름, return 타입, 함수 매개변수, 접근 제어자의 정보&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- type info : class 인지 interface인지 여부 저장, type의 속성, 이름 super class의 이름&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WVpyf/dJMcacBE9wb/8NKJwQ9esuG5fncfFuaJjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WVpyf/dJMcacBE9wb/8NKJwQ9esuG5fncfFuaJjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WVpyf/dJMcacBE9wb/8NKJwQ9esuG5fncfFuaJjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWVpyf%2FdJMcacBE9wb%2F8NKJwQ9esuG5fncfFuaJjk%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;500&quot; height=&quot;347&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;3-2) 힙 영역(Heap Area)&lt;/u&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙 영역은 메서드 영역과 함께 모든 스레드가 공유하며, JVM이 관리하는 프로그램 상에서 데이터를 저장하기 위해 런타임 시 동적으로 할당하여 사용하는 영역이다. 즉, new 연산자로 생성되는 클래스와 인스턴스 변수, 배열 타입 등 Reference Type이 저장되는 곳이다. 당연히 메서드 영역에 저장된 클래스만이 생성되어 적재된다. 유의할 점은 힙 영역에 생성된 객체와 배열은 Reference Type으로서, JVM 스택 영역의 변수나 다른 객체의 필드에서 참조된다는 점이다. 즉, 힙의 참조 주소는 스택이 갖고 있고 해당 객체를 통해서만 힙 영역에 있는 인스턴스에 접근할 수 있다.&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;1240&quot; data-origin-height=&quot;726&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v3DhL/dJMcag48kN9/GwXM6VTWx3h5N3Y2d3BIC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v3DhL/dJMcag48kN9/GwXM6VTWx3h5N3Y2d3BIC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v3DhL/dJMcag48kN9/GwXM6VTWx3h5N3Y2d3BIC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv3DhL%2FdJMcag48kN9%2FGwXM6VTWx3h5N3Y2d3BIC1%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;351&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;726&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 참조하는 변수나 필드가 없다면 의미 없는 객체가 되기 때문에 이것을 쓰레기로 취급하고 JVM은 쓰레기 수집기인 GC를 실행시켜 쓰레기 객체를 힙 영역에서 자동으로 제거한다. -&amp;gt; &lt;u&gt;힙 영역은 가비지 컬렉터에 대상이 되는 공간&lt;/u&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;&lt;u&gt;3-3) 스택 영역 (Stack Area)&lt;/u&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택 영역은 int, long, boolean 등 기본 자료형을 생성할 때 저장되는 공간으로, 임식적으로 사용되는 변수나 정보들이 저장되는 영역이다. 자료구조 Stack은 마지막에 들어온 값이 먼저 나가는 LIFO 구조로 push와 pop 기능 사용방식으로 동작한다. 메서드 호출 시 마다 각각의 스택 프레임이 생성되고 메서드 안에서 사용되는 값들을 저장하고, 호출된 메서드의 매개변수, 지역변수, 리턴 값 및 연산 시 일어나는 값들을 임시로 저장한다.&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-origin-width=&quot;904&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFSf8w/dJMcagcZVUf/oo0kL31uwkCxO3795zbJU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFSf8w/dJMcagcZVUf/oo0kL31uwkCxO3795zbJU1/img.png&quot; data-alt=&quot;new에 의해 생성된 클래스는 Heap Area에 저장되고, Stack Area에는 생성된 클래스의 참조인 p만 저장된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFSf8w/dJMcagcZVUf/oo0kL31uwkCxO3795zbJU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFSf8w%2FdJMcagcZVUf%2Foo0kL31uwkCxO3795zbJU1%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;500&quot; height=&quot;319&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;new에 의해 생성된 클래스는 Heap Area에 저장되고, Stack Area에는 생성된 클래스의 참조인 p만 저장된다.&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택 영역은 각 스레드마다 하나씩 존재하며, 스레드가 시작될 때 할당된다. 프로세스가 메모리에 로드될 때 스택 사이즈가 고정되어 있어, 런타임 시에 스택 사이즈를 바꿀 수 없다. 만일 고정된 크기의 JVM 스택에서 프로그램 실행 중 메모리 크기가 충분하지 않다면 `StackOverFLowError`가 발생하게 된다.&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;3-4) PC 레지스터 (Program Counter Register)&lt;/u&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC 레지스터는 스레드가 시작될 때 생성되며, 현재 수행 중인 JVM 명령어 주소를 저장하는 공간이다. JVM 명령의 주소는 스레드가 어떤 부분을 무슨 명령으로 실행해야할 지에 대한 기록을 가지고 있다.&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;s&gt;일반적으로 프로그램 실행은 CPU에서 명령어를 수행하는 과정으로 이루어진다. 이때 CPU는 연산을 수행하는 동안 필요한 정보를 레지트서라고 하는 CPU 내의 기억장치를 이용하게 된다. 예를 들어, A와 B라는 데이터와 피연산 값이 Operand가 있고 이를 더라하는 연산 Instruction이 있다고 하자. A와 B, 그리고 더하기 연산자가 순차적으로 진행이 되게 되는데, 이때 A를 받고 B를 받는 동안 이 값을 CPU가 어딘가에 기억해두어야 할 필요가 있다. 그리고 이 공간이 CPU 내의 기억장치 Register이다.&amp;nbsp;&lt;/s&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;자바의 PC Register는 위의 CPU Register와 다르다. 자바는 OS나 CPU의 입장에서는 하나의 프로세스이기 때문에 가상 머신(JVM)의 리소스를 이용해야 한다. 그래서 자바는 CPU에 직접 연산을 수행하도록 하는 것이 아닌, 현재 작업하는 내용을 CPU에게 연산으로 제공해야 하며, 이를 위한 버퍼 공간으로 PC Register라는 메모리 영역을 만들게 된 것이다. 따라서 JVM은 스택에서 비연산값 Operand를 뽑아 별도의 메모리 공간인 PC Register에 저장하는 방식을 취한다.&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;3-5) 네이티브 메서드 스택 (Native Method Stack)&lt;/u&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 메서드 스택은 자바 코드가 컴파일되어 생성되는 바이트 코드가 아닌 실제 실행할 수 있는 기계어로 작성된 프로그램을 실행시키는 영역이다. 또한, 자바 이외의 언어로 작성된 네이티브 코드를 실행하기 위한 공간이기도 하다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;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;&lt;span&gt;✅&lt;span&gt;&amp;nbsp; JNI (Java Native Interface)&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;자바가 다른 언어로 만들어진 애플리케이션과 상호 작용할 수 있는 인터페이스를 제공하는 프로그램이다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;596&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XlJDQ/dJMcaihzNJq/rdN49ASKT4EjwvB2knaOg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XlJDQ/dJMcaihzNJq/rdN49ASKT4EjwvB2knaOg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XlJDQ/dJMcaihzNJq/rdN49ASKT4EjwvB2knaOg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXlJDQ%2FdJMcaihzNJq%2FrdN49ASKT4EjwvB2knaOg1%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;500&quot; height=&quot;233&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;596&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;✅&lt;span&gt;&amp;nbsp; Native Method Library&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;C, C++로 작성된 라이브러리를 칭한다. 만일 헤더가 필요하면 JNI는 이 라이브러리를 로딩해 실행한다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&amp;nbsp;&lt;/h2&gt;
&lt;figure id=&quot;og_1765799363084&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] JVM(Java Virtual Machine) 이해하기 -1 : 동작 과정&quot; data-og-description=&quot;해당 글에서는 Java Application 환경에서 Java 소스코드가 JVM 내에서 수행되는 전반적인 과정에 대해서 확인해 봅니다. 1) JVM(Java Virtual Machine)  JVM(Java Virtual Machine) - 자바 프로그램을 실행하기 위한 &quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/279?category=1071591&quot; data-og-url=&quot;https://adjh54.tistory.com/279&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c8KPAt/hyZPK21LIb/gM5HMQ6HUq1RKOMGtwxFmK/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/2dXiI/hyZPqc4cbQ/Y7D9Netj2kBhtZ6q1z6dB0/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/mVkCX/hyZPwj3mYF/vIIDGxbD0d73FDYmuqUPV1/img.png?width=847&amp;amp;height=687&amp;amp;face=0_0_847_687&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/279?category=1071591&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/279?category=1071591&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c8KPAt/hyZPK21LIb/gM5HMQ6HUq1RKOMGtwxFmK/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/2dXiI/hyZPqc4cbQ/Y7D9Netj2kBhtZ6q1z6dB0/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/mVkCX/hyZPwj3mYF/vIIDGxbD0d73FDYmuqUPV1/img.png?width=847&amp;amp;height=687&amp;amp;face=0_0_847_687');&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] JVM(Java Virtual Machine) 이해하기 -1 : 동작 과정&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Java Application 환경에서 Java 소스코드가 JVM 내에서 수행되는 전반적인 과정에 대해서 확인해 봅니다. 1) JVM(Java Virtual Machine)  JVM(Java Virtual Machine) - 자바 프로그램을 실행하기 위한&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;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1765799371569&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;☕ JVM 내부 구조 &amp;amp; 메모리 영역   총정리&quot; data-og-description=&quot;저번 포스팅에서는 JRE / JDK / JVM에 대해서 간략하게 알아보는 시간을 가졌다면, 이번 포스팅에서는 JVM의 내부 구조에 대해 좀 더 자세하게 알아보도록 할 예정이다. JVM(자바 가상 머신)은 자바 언&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-JVM-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD-%EC%8B%AC%ED%99%94%ED%8E%B8&quot; data-og-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-JVM-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD-%EC%8B%AC%ED%99%94%ED%8E%B8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/LYZXI/hyZPuGwGOU/Kwz5Lnl2bYaYwr85iKJgKK/img.png?width=800&amp;amp;height=473&amp;amp;face=0_0_800_473,https://scrap.kakaocdn.net/dn/bXzrdI/hyZPxcbQAO/A5WnywgZSVX95XVmKMvhHK/img.png?width=800&amp;amp;height=473&amp;amp;face=0_0_800_473,https://scrap.kakaocdn.net/dn/3hVnZ/hyZPyhRUag/5bqEf64pr9WuzsdwSlh5wK/img.png?width=1690&amp;amp;height=1000&amp;amp;face=0_0_1690_1000&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-JVM-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD-%EC%8B%AC%ED%99%94%ED%8E%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-JVM-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD-%EC%8B%AC%ED%99%94%ED%8E%B8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/LYZXI/hyZPuGwGOU/Kwz5Lnl2bYaYwr85iKJgKK/img.png?width=800&amp;amp;height=473&amp;amp;face=0_0_800_473,https://scrap.kakaocdn.net/dn/bXzrdI/hyZPxcbQAO/A5WnywgZSVX95XVmKMvhHK/img.png?width=800&amp;amp;height=473&amp;amp;face=0_0_800_473,https://scrap.kakaocdn.net/dn/3hVnZ/hyZPyhRUag/5bqEf64pr9WuzsdwSlh5wK/img.png?width=1690&amp;amp;height=1000&amp;amp;face=0_0_1690_1000');&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;☕ JVM 내부 구조 &amp;amp; 메모리 영역   총정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;저번 포스팅에서는 JRE / JDK / JVM에 대해서 간략하게 알아보는 시간을 가졌다면, 이번 포스팅에서는 JVM의 내부 구조에 대해 좀 더 자세하게 알아보도록 할 예정이다. JVM(자바 가상 머신)은 자바 언&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java</category>
      <category>java</category>
      <category>자바</category>
      <author>erika0915</author>
      <guid isPermaLink="true">https://erika0915.tistory.com/143</guid>
      <comments>https://erika0915.tistory.com/143#entry143comment</comments>
      <pubDate>Mon, 10 Nov 2025 19:43:35 +0900</pubDate>
    </item>
  </channel>
</rss>