<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>엄지월드</title>
    <link>https://kingle1024.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 27 Jul 2026 04:58:05 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>킨글</managingEditor>
    <image>
      <title>엄지월드</title>
      <url>https://tistory1.daumcdn.net/tistory/2744926/attach/0a15f225ade4407386538b7fd14caba9</url>
      <link>https://kingle1024.tistory.com</link>
    </image>
    <item>
      <title>[React] 간헐적으로 데이터가 나오지 않는 현상 수정</title>
      <link>https://kingle1024.tistory.com/617</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;모집글 진입 시 아래와 같이 간헐적으로 boardDetail 데이터가 나오지 않는 현상이 있었음.&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;1052&quot; data-origin-height=&quot;945&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s8m8F/dJMcabQrzjF/0RRKUdHuQDTCoAwvkOood1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s8m8F/dJMcabQrzjF/0RRKUdHuQDTCoAwvkOood1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s8m8F/dJMcabQrzjF/0RRKUdHuQDTCoAwvkOood1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs8m8F%2FdJMcabQrzjF%2F0RRKUdHuQDTCoAwvkOood1%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;327&quot; height=&quot;294&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;945&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;response 데이터는 정상적으로 오고 있었음.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnQ8th/dJMcacV7kph/qpjDP0n1PHnYmKUHa4cBPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnQ8th/dJMcacV7kph/qpjDP0n1PHnYmKUHa4cBPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnQ8th/dJMcacV7kph/qpjDP0n1PHnYmKUHa4cBPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnQ8th%2FdJMcacV7kph%2FqpjDP0n1PHnYmKUHa4cBPK%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;405&quot; height=&quot;285&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;492&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디버깅 확인 시 화면이 여러 번 렌더링 되는것을 확인함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 아래와 같이 조치를 취해서 해결됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 렌더링 점검&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. boardDetail이 변경 되면 다시 렌더링 되도록 수정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;443&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baxcJ2/dJMcabpm1qJ/sh7OlsKJWObIrnFYMAHDK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baxcJ2/dJMcabpm1qJ/sh7OlsKJWObIrnFYMAHDK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baxcJ2/dJMcabpm1qJ/sh7OlsKJWObIrnFYMAHDK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaxcJ2%2FdJMcabpm1qJ%2Fsh7OlsKJWObIrnFYMAHDK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;526&quot; height=&quot;266&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;443&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러번 상세 페이지 진입하여 테스트 시 데이터가 잘 나오는 것을 확인함.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1083&quot; data-origin-height=&quot;907&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsR4W1/dJMb99ZpuHv/qJ6GRH2no74Xe65z7nc6a0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsR4W1/dJMb99ZpuHv/qJ6GRH2no74Xe65z7nc6a0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsR4W1/dJMb99ZpuHv/qJ6GRH2no74Xe65z7nc6a0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsR4W1%2FdJMb99ZpuHv%2FqJ6GRH2no74Xe65z7nc6a0%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;511&quot; height=&quot;428&quot; data-origin-width=&quot;1083&quot; data-origin-height=&quot;907&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Front/React</category>
      <author>킨글</author>
      <guid isPermaLink="true">https://kingle1024.tistory.com/617</guid>
      <comments>https://kingle1024.tistory.com/617#entry617comment</comments>
      <pubDate>Sun, 11 Jan 2026 18:20:16 +0900</pubDate>
    </item>
    <item>
      <title>JPA 동시성 제어 주의점(ConstraintViolationException 에러)</title>
      <link>https://kingle1024.tistory.com/616</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 ConstraintViolationException 에러가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 확인해보니 특정 엔티티를 &lt;b&gt;업데이트&lt;/b&gt;하거나 &lt;b&gt;삭제&lt;/b&gt;하려 할 때, 데이터베이스에 저장된 해당 엔티티의 상태가 애플리케이션이 엔티티를 로드했을 때와 달라졌을 때 발생하는 것이었다. 즉, 다른 트랜잭션에 의해 이미 &lt;b&gt;변경&lt;/b&gt;되었거나 &lt;b&gt;삭제&lt;/b&gt;되었다는 뜻인 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1766443038013&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement [(conn=1338) Duplicate entry 'ejy1024@-ejy1024@_test1234@_...' for key 'PRIMARY'] [/* insert for com.connect.service.chatting.entity.RoomMembership */insert into room_memberships (joined_at,room_name,room_id,user_id) values (?,?,?,?)]
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.connect.service.chatting.entity.RoomMembership#RoomMembershipId(userId=ejy1024, roomId=ejy1024test12341766307683336)]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드로 보자면 existsById로 확인하는 시점과 save를 호출하는 시점 사이에 다른 트랜잭션이 먼저 동일한 기본 키로 RoomMembership 엔티티를 삽입했을 때 발생할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1766448729776&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val exists = roomMembershipRepository.existsById(membershipId) // 해당 유저가 이미 이 방의 멤버인지 DB에서 확인

if (!exists) { // 아직 멤버가 아니라면
   val newMembership = RoomMembership(
       id = membershipId, // 복합 키
       chatRoom = chatRoom, // 관계 매핑된 ChatRoom 엔티티
       roomName = roomName,
       joinedAt = LocalDateTime.now() // 참여 시간 기록
   )
   roomMembershipRepository.save(newMembership) // 새로운 멤버십을 DB에 저장
   return true // 성공적으로 새로 추가됨을 반환
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;순서는 이렇다.&amp;nbsp;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;트랜잭션&amp;nbsp;A&amp;nbsp;:&amp;nbsp;roomMembershipRepository.existsById(membershipId)를&amp;nbsp;호출한다. &lt;br /&gt;이&amp;nbsp;시점에는&amp;nbsp;해당&amp;nbsp;RoomMembership이&amp;nbsp;DB에&amp;nbsp;없어서&amp;nbsp;exists는&amp;nbsp;false를&amp;nbsp;반환한다.&amp;nbsp;그래서&amp;nbsp;if&amp;nbsp;(!exists)&amp;nbsp;블록으로&amp;nbsp;진입한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;트랜잭션&amp;nbsp;B&amp;nbsp;(거의&amp;nbsp;동시에):&amp;nbsp;트랜잭션&amp;nbsp;A가&amp;nbsp;save를&amp;nbsp;호출하기&amp;nbsp;전에,&amp;nbsp;트랜잭션&amp;nbsp;B가&amp;nbsp;먼저&amp;nbsp;같은&amp;nbsp;membershipId를&amp;nbsp;가진&amp;nbsp;RoomMembership을&amp;nbsp;생성하고&amp;nbsp;DB에&amp;nbsp;INSERT하는&amp;nbsp;데&amp;nbsp;성공한다.&lt;/li&gt;
&lt;li&gt;트랜잭션&amp;nbsp;A&amp;nbsp;(다시):&amp;nbsp;이제&amp;nbsp;트랜잭션&amp;nbsp;A가&amp;nbsp;roomMembershipRepository.save(newMembership)를&amp;nbsp;호출하여&amp;nbsp;DB에&amp;nbsp;INSERT를&amp;nbsp;시도한다.&lt;/li&gt;
&lt;li&gt;에러&amp;nbsp;발생:&amp;nbsp;하지만&amp;nbsp;이미&amp;nbsp;트랜잭션&amp;nbsp;B가&amp;nbsp;동일한&amp;nbsp;PRIMARY&amp;nbsp;KEY를&amp;nbsp;가진&amp;nbsp;RoomMembership을&amp;nbsp;삽입했으므로,&amp;nbsp;DB는&amp;nbsp;PRIMARY&amp;nbsp;KEY&amp;nbsp;제약&amp;nbsp;조건&amp;nbsp;위반을&amp;nbsp;감지하고&amp;nbsp;Duplicate&amp;nbsp;entry&amp;nbsp;'...'&amp;nbsp;for&amp;nbsp;key&amp;nbsp;'PRIMARY'&amp;nbsp;에러를&amp;nbsp;발생시킨다.&amp;nbsp;하이버네이트는&amp;nbsp;이를&amp;nbsp;ConstraintViolationException으로&amp;nbsp;변환하여&amp;nbsp;던지게&amp;nbsp;된다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결을 위해서는 아래와 같이 existsById로 먼저 확인하고 save하는 방식(read-then-write) 대신, &lt;b&gt;findById&lt;/b&gt;를 사용하여 엔티티의 존재 여부를 확실히 확인하고 그 결과에 따라 분기 처리하는 것이 가장 안정적이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 existsById는 단순히 존재 여부만 확인하지만, findById는 실제 엔티티를 영속성 컨텍스트로 가져오려고 시도하기 때문이다.&lt;br /&gt;다시 말하면 findById는 단순히 데이터베이스를 조회하는 것을 넘어, JPA/하이버네이트의 영속성 컨텍스트(Persistence Context)와 연동하여 동작한다.&amp;nbsp;&lt;br /&gt;만약 현재 트랜잭션 내에서 동일한 ID를 가진 엔티티가 이미 로드되어 영속성 컨텍스트에 관리되고 있다면, DB 쿼리 없이 해당 엔티티를 즉시 반환하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1766448959083&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val exists = roomMembershipRepository.findById(membershipId) // 해당 유저가 이미 이 방의 멤버인지 DB에서 확인

if (exists.isEmpty) { // 아직 멤버가 아니라면
   val newMembership = RoomMembership(
       id = membershipId, // 복합 키
       chatRoom = chatRoom, // 관계 매핑된 ChatRoom 엔티티
       roomName = roomName,
       joinedAt = LocalDateTime.now() // 참여 시간 기록
   )
   roomMembershipRepository.save(newMembership) // 새로운 멤버십을 DB에 저장
   return true // 성공적으로 새로 추가됨을 반환
}
return false // 이미 존재하는 멤버임을 반환&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;결론&lt;/h4&gt;
&lt;pre id=&quot;code_1766449189042&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[이전]
roomMembershipRepository.existsById(membershipId)

[이후]
roomMembershipRepository.findById(membershipId)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Race Condition 발생 가능성이 있는 &quot;불필요한 INSERT 시도&quot;를 더 효율적으로 차단한다. (이미 존재한다면 findById가 엔티티를 반환하므로, if (isEmpty) 로직에 의해 save가 호출될 기회 자체가 사라진다) &lt;br /&gt;2. 하이버네이트의 영속성 컨텍스트와 더 깊이 통합되어 관리된다. (엔티티 객체 자체를 가져오기 때문에)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 추가적으로 아래와 같이 낙관적 잠금을 활용해도 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1766449341353&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ChatRoom.kt
@Entity
@Table(name = &quot;chat_room&quot;)
class ChatRoom(
    @Id
    val roomId: String,

    // ... 다른 필드들 ...

    @Version // 이 부분을 추가 (Long 타입 필드를 선언하고 @Version 어노테이션 추가)
    var version: Long? = null,
) {
    // ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>java/Spring</category>
      <category>ConstraintViolationException</category>
      <category>staleobjectstateexception</category>
      <author>킨글</author>
      <guid isPermaLink="true">https://kingle1024.tistory.com/616</guid>
      <comments>https://kingle1024.tistory.com/616#entry616comment</comments>
      <pubDate>Tue, 23 Dec 2025 07:39:53 +0900</pubDate>
    </item>
    <item>
      <title>Logstash 설치</title>
      <link>https://kingle1024.tistory.com/613</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1.&amp;nbsp;Elastic&amp;nbsp;GPG&amp;nbsp;키&amp;nbsp;임포트&lt;/p&gt;
&lt;pre id=&quot;code_1758085533334&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install -y apt-transport-https # HTTPS를 통해 저장소에 접근하기 위함
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elastic-keyring.gpg&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Elastic 저장소 추가&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1758085558766&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;echo &quot;deb [signed-by=/usr/share/keyrings/elastic-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main&quot; | sudo tee /etc/apt/sources.list.d/elastic-8.x.list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.&amp;nbsp;패키지&amp;nbsp;목록&amp;nbsp;업데이트&lt;/p&gt;
&lt;pre id=&quot;code_1758085573725&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get update&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;4.&amp;nbsp;Logstash&amp;nbsp;설치&amp;nbsp;시도&lt;/p&gt;
&lt;pre id=&quot;code_1758085587933&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get install logstash&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;5.&amp;nbsp;설치&amp;nbsp;확인&lt;/p&gt;
&lt;pre id=&quot;code_1758085620341&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/usr/share/logstash/bin/logstash --version&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;49&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tuNXa/btsQCrzio5m/UqHKTacRFACNgfGkFzB1Y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tuNXa/btsQCrzio5m/UqHKTacRFACNgfGkFzB1Y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tuNXa/btsQCrzio5m/UqHKTacRFACNgfGkFzB1Y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtuNXa%2FbtsQCrzio5m%2FUqHKTacRFACNgfGkFzB1Y0%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;507&quot; height=&quot;49&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;49&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/etc/logstash/data 디렉토리 생성 및 소유권 설정 (다시 한번 확실하게!)&lt;/p&gt;
&lt;pre id=&quot;code_1758095685084&quot; class=&quot;haskell&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# 디렉토리가 이미 있든 없든, 안전하게 생성하고 소유권을 설정하는 명령어
sudo mkdir -p /etc/logstash/data
sudo chown logstash:logstash /etc/logstash/data
sudo chmod 755 /etc/logstash/data # 디렉토리에 대한 일반적인 읽기/실행 권한 + 소유자 쓰기 권한&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;기존 .logstash_jdbc_last_run_job_mst.yml 파일 삭제 (혹시 있다면!)&lt;br /&gt;만약 이전에 권한 문제 등으로 빈 파일이 잘못 생성되어 접근이 안 되던 경우를 대비해서 삭제하는 거야. 이 파일은 Logstash가 없으면 자동으로 다시 생성한단다.&lt;/p&gt;
&lt;pre id=&quot;code_1758095685084&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;sudo rm -f /etc/logstash/data/.logstash_jdbc_last_run_job_mst.yml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mariaDB 설정&lt;/p&gt;
&lt;pre id=&quot;code_1758095854233&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo vi /etc/logstash/conf.d/logstash-mariadb-job_mst.conf&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1758095883130&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Logstash JDBC Input Plugin을 사용하여 MariaDB에서 데이터 동기화 설정
input {
  jdbc {
    jdbc_driver_library =&amp;gt; &quot;/etc/logstash/mariadb-java-client-3.1.2.jar&quot; # &amp;lt;-- 이 경로가 드라이버를 둔 실제 경로여야 해!

    # MariaDB JDBC 드라이버 클래스 이름
    jdbc_driver_class =&amp;gt; &quot;org.mariadb.jdbc.Driver&quot;

    jdbc_connection_string =&amp;gt; &quot;jdbc:mariadb://domain:30121/travel?autoReconnect=true&amp;amp;serverTimezone=Asia/Seoul&quot;
    jdbc_user =&amp;gt; &quot;id&quot; # MariaDB 사용자 이름
    jdbc_password =&amp;gt; &quot;pw&quot; # MariaDB 비밀번호

    # Logstash 스케줄: */5 * * * * (매 5분마다 실행)
    schedule =&amp;gt; &quot;*/5 * * * *&quot;

    # 동기화할 데이터를 가져올 SQL 쿼리
    # 'job_mst' 테이블에 'updated_at' 컬럼이 있다는 전제로 증분 동기화 쿼리를 작성했어.
    # 실제 job_mst 테이블의 컬럼 이름과 매핑될 JobMstDocument의 필드 이름을 잘 확인해서 쿼리를 구성해줘.
    # 예: id, company_cd, anno_id 등 모든 필요한 컬럼을 명시해주는 것이 좋아.
    statement =&amp;gt; &quot;SELECT id, company_cd, anno_id, class_cd_nm, emp_type_cd_nm, anno_subject, sub_job_cd_nm, sys_company_cd_nm, job_detail_link, end_date, personal_history, personal_history_end, insert_dts, updated_at FROM job_mst WHERE updated_at &amp;gt; :sql_last_value ORDER BY updated_at ASC&quot;

    # 'updated_at' 컬럼 값을 추적해서 :sql_last_value를 업데이트해.
    use_column_value =&amp;gt; true
    tracking_column =&amp;gt; &quot;updated_at&quot;
    tracking_column_type =&amp;gt; &quot;timestamp&quot;

    # 초기 풀 로드: 처음 실행 시 'true', 이후 증분 동기화 시 'false'
    clean_run =&amp;gt; true

    # 마지막 실행 시간과 추적 컬럼 값을 저장할 파일 경로
    last_run_metadata_path =&amp;gt; &quot;/etc/logstash/data/.logstash_jdbc_last_run_job_mst.yml&quot; # &amp;lt;- 이 경로가 쓰기 가능한지 확인

    # 필드 이름 변환: DB 컬럼명이 엘라스틱서치 필드명과 다를 경우 대문자-&amp;gt;소문자, 언더스코어-&amp;gt;카멜케이스 등으로 변환 (선택 사항)
    # statement_filepath =&amp;gt; &quot;/etc/logstash/conf.d/your_query.sql&quot; # 쿼리가 길 경우 별도 파일로 관리할 수도 있음
  }
}

# 데이터를 엘라스틱서치로 보내는 설정
output {
  elasticsearch {
    hosts =&amp;gt; [&quot;http://localhost:9200&quot;] # 네 엘라스틱서치 서버 주소와 포트
    user =&amp;gt; &quot;elastic&quot;
    password =&amp;gt; &quot;changeme&quot;
    index =&amp;gt; &quot;job_mst&quot; # 엘라스틱서치에 생성할 인덱스 이름 (네 JobMstDocument와 동일하게)
    document_id =&amp;gt; &quot;%{id}&quot; # MariaDB `id` 컬럼 값을 엘라스틱서치 문서의 ID로 사용
                           # 이렇게 해야 MariaDB의 데이터 변경 시 엘라스틱서치에서도 해당 문서가 업데이트돼.
  }
  stdout { codec =&amp;gt; rubydebug } # 개발 중에는 Logstash가 어떤 데이터를 처리하는지 터미널에 출력해서 확인
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간 로그 확인&lt;/p&gt;
&lt;pre id=&quot;code_1758099558543&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo tail -f /var/log/logstash/logstash-plain.log&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Server&amp;amp;DevOps</category>
      <author>킨글</author>
      <guid isPermaLink="true">https://kingle1024.tistory.com/613</guid>
      <comments>https://kingle1024.tistory.com/613#entry613comment</comments>
      <pubDate>Wed, 17 Sep 2025 14:07:33 +0900</pubDate>
    </item>
    <item>
      <title>재부팅 시 카프카 자동 실행(systemd)</title>
      <link>https://kingle1024.tistory.com/612</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;주키퍼 systemd 서비스 파일 생성&lt;/p&gt;
&lt;pre id=&quot;code_1758073652757&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo vi /etc/systemd/system/zookeeper.service&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1758073660326&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Unit]
Description=Apache ZooKeeper
After=network.target

[Service]
Type=simple
User=ec2-user # 네 서버의 카프카 실행 사용자 이름으로 변경 (보통 ec2-user)
Group=ec2-user # 네 서버의 카프카 실행 사용자 그룹으로 변경
ExecStart=/home/ec2-user/kafka_2.13-3.2.3/bin/zookeeper-server-start.sh /home/ec2-user/kafka_2.13-3.2.3/config/zookeeper.properties
ExecStop=/home/ec2-user/kafka_2.13-3.2.3/bin/zookeeper-server-stop.sh /home/ec2-user/kafka_2.13-3.2.3/config/zookeeper.properties # stop 스크립트가 있다면 사용 (없으면 비워도 됨)
TimeoutStartSec=60 # 시작 대기 시간
TimeoutStopSec=60  # 종료 대기 시간
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카프카 브로커 systemd 서비스 파일 생성&lt;/p&gt;
&lt;pre id=&quot;code_1758073695245&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo vi /etc/systemd/system/kafka.service&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1758073703989&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Unit]
Description=Apache Kafka
After=zookeeper.service # 주키퍼가 먼저 시작된 후에 카프카가 시작되도록 설정
After=network.target

[Service]
Type=simple
User=ec2-user # 네 서버의 카프카 실행 사용자 이름으로 변경
Group=ec2-user # 네 서버의 카프카 실행 사용자 그룹으로 변경
ExecStart=/home/ec2-user/kafka_2.13-3.2.3/bin/kafka-server-start.sh /home/ec2-user/kafka_2.13-3.2.3/config/server.properties
ExecStop=/home/ec2-user/kafka_2.13-3.2.3/bin/kafka-server-stop.sh /home/ec2-user/kafka_2.13-3.2.3/config/server.properties
TimeoutStartSec=600 # 카프카 시작은 더 오래 걸릴 수 있으니 넉넉하게 설정 (600초 = 10분)
TimeoutStopSec=60
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 모두 저장하고 나면 systemd에게 새로운 서비스 파일을 알리고 활성화&lt;/p&gt;
&lt;pre id=&quot;code_1758073729733&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo systemctl daemon-reload # systemd 설정 새로고침
sudo systemctl enable zookeeper # 재부팅 시 자동 시작 활성화
sudo systemctl enable kafka     # 재부팅 시 자동 시작 활성화&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 시작&lt;/p&gt;
&lt;pre id=&quot;code_1758073743378&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo systemctl start zookeeper # 주키퍼 서비스 시작
sudo systemctl status zookeeper # 주키퍼 서비스 상태 확인 (Active: active (running) 이면 OK)

# 주키퍼가 잘 떴으면 이제 카프카 시작
sudo systemctl start kafka     # 카프카 서비스 시작
sudo systemctl status kafka     # 카프카 서비스 상태 확인 (Active: active (running) 이면 OK)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Server&amp;amp;DevOps</category>
      <author>킨글</author>
      <guid isPermaLink="true">https://kingle1024.tistory.com/612</guid>
      <comments>https://kingle1024.tistory.com/612#entry612comment</comments>
      <pubDate>Wed, 17 Sep 2025 10:49:26 +0900</pubDate>
    </item>
    <item>
      <title>카프카 실행 명령어</title>
      <link>https://kingle1024.tistory.com/611</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주키퍼를 먼저 실행 시켜주어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1758073276007&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nohup bin/zookeeper-server-start.sh config/zookeeper.properties &amp;gt; zookeeper.out 2&amp;gt;&amp;amp;1 &amp;amp;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카프카 브로커 실행&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1758073297139&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nohup bin/kafka-server-start.sh config/server.properties &amp;gt; server.out 2&amp;gt;&amp;amp;1 &amp;amp;&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;jps 입력하여 실행 확인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;62&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUuExU/btsQCwAhHFo/RnaF5pxS6NSSDIHSWuf5Xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUuExU/btsQCwAhHFo/RnaF5pxS6NSSDIHSWuf5Xk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUuExU/btsQCwAhHFo/RnaF5pxS6NSSDIHSWuf5Xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUuExU%2FbtsQCwAhHFo%2FRnaF5pxS6NSSDIHSWuf5Xk%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;331&quot; height=&quot;62&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;62&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Server&amp;amp;DevOps</category>
      <author>킨글</author>
      <guid isPermaLink="true">https://kingle1024.tistory.com/611</guid>
      <comments>https://kingle1024.tistory.com/611#entry611comment</comments>
      <pubDate>Wed, 17 Sep 2025 10:42:34 +0900</pubDate>
    </item>
    <item>
      <title>[홈서버] DNS 자동 매핑 처리</title>
      <link>https://kingle1024.tistory.com/610</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;홈서버의 경우 고정 아이피가 아닌 경우가 대부분이라 아이피가 매번 바뀔 수 있기 때문에 DNS와 IP를 매칭시켜주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;duckdns.org에 들어가서 domain을 생성한 후 token을 생성하고 아래와 같이 sh 파일을 만들어 스케줄러 돌리면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 생성&lt;/p&gt;
&lt;pre id=&quot;code_1758007804845&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vi duck.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 내용&lt;/p&gt;
&lt;pre id=&quot;code_1758007792969&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
#
# Simple shell script to run DuckDNS update
# Requires curl to be installed
#

# Configuration variables
# Set your domain or comma separated list of domains and subdomains
# (for example: mytestdomain.duckdns.org,other.duckdns.org)
DOMAIN=&quot;domain.duckdns.org&quot;
# Set your token as per your account for example from the duckdns.org home page
TOKEN=&quot;111111-296e-4c07-9cab-222222&quot;

# Check this file is actually being executed directly
[[ &quot;$BASH_SOURCE&quot; == &quot;$0&quot; ]] || return

# Check for root if using system-wide configuration
if [[ $EUID != 0 ]]
then
    echo 'ERROR: Requires root privileges (sudo).'
    exit 1
fi

# Check if curl is installed
if ! command -v curl &amp;amp;&amp;gt; /dev/null
then
    echo &quot;ERROR: 'curl' command not found. Please install curl.&quot;
    exit 1
fi

# Get IP addresses, filter to current, filter to IPv4 &amp;amp; IPv6
WAN_IPS=&quot;$(ip -o -f inet addr | awk '{print $4}' | cut -d/ -f1)&quot;

if [[ -z &quot;$WAN_IPS&quot; ]]
then
    echo 'ERROR: No public IP address was found for WAN interface.'
    exit 2
fi

for CURRENT_IP in $WAN_IPS
do
    # Check IPv4 and IPv6
    if [[ &quot;$CURRENT_IP&quot; == *.* ]] # Is this IPv4?
    then
        echo -n &quot;Updating with IPv4 address ($CURRENT_IP)... &quot;
        URL=&quot;https://www.duckdns.org/update?domains=$DOMAIN&amp;amp;token=$TOKEN&amp;amp;ip=$CURRENT_IP&quot;
        RES=&quot;$(curl -s -L &quot;$URL&quot;)&quot;
    elif [[ &quot;$CURRENT_IP&quot; == *:* ]] # Is this IPv6?
    then
        echo -n &quot;Updating with IPv6 address ($CURRENT_IP)... &quot;
        URL=&quot;https://www.duckdns.org/update?domains=$DOMAIN&amp;amp;token=$TOKEN&amp;amp;ipv6=$CURRENT_IP&quot;
        RES=&quot;$(curl -s -L &quot;$URL&quot;)&quot;
    fi

    if [[ &quot;$RES&quot; == &quot;OK&quot; ]]
    then
        echo 'OK'
    elif [[ &quot;$RES&quot; == &quot;KO&quot; ]]
    then
        echo 'ERROR: Server returned KO'
    else
        echo &quot;ERROR: Unknown server response ($RES)&quot;
    fi
done

# Re-check server status for this IP to confirm changes
echo -n 'Checking server status... '
CHECK_URL=&quot;https://www.duckdns.org/update?domains=$DOMAIN&amp;amp;token=$TOKEN&amp;amp;check=true&quot;
CHECK_RES=&quot;$(curl -s -L &quot;$CHECK_URL&quot;)&quot;
if [[ &quot;$CHECK_RES&quot; == *&quot;$CURRENT_IP&quot;* ]]
then
    echo 'OK'
else
    echo &quot;ERROR: Could not verify that IP ($CURRENT_IP) has been updated.&quot;
fi

echo 'Done.'&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;crontab 설정 진입&lt;/p&gt;
&lt;pre id=&quot;code_1758007926249&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo crontab -e&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨 아래에 아래 명령어 추가&lt;/p&gt;
&lt;pre id=&quot;code_1758007754993&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;0 * * * * /home/kingle/duckdns/duck.sh &amp;gt; /var/log/duckdns_hourly_update.log 2&amp;gt;&amp;amp;1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0&amp;nbsp;*&amp;nbsp;*&amp;nbsp;*&amp;nbsp;*:&amp;nbsp;&quot;매&amp;nbsp;시간&amp;nbsp;0분(정각)에&amp;nbsp;실행&quot; &lt;br /&gt;첫&amp;nbsp;번째&amp;nbsp;0:&amp;nbsp;매&amp;nbsp;시&amp;nbsp;정각&amp;nbsp;(분) &lt;br /&gt;두&amp;nbsp;번째&amp;nbsp;*:&amp;nbsp;매&amp;nbsp;시간&amp;nbsp;(시) &lt;br /&gt;세&amp;nbsp;번째&amp;nbsp;*:&amp;nbsp;매일&amp;nbsp;(일) &lt;br /&gt;네&amp;nbsp;번째&amp;nbsp;*:&amp;nbsp;매월&amp;nbsp;(월) &lt;br /&gt;다섯&amp;nbsp;번째&amp;nbsp;*:&amp;nbsp;매&amp;nbsp;요일&amp;nbsp;(요일) &lt;/p&gt;</description>
      <category>Server&amp;amp;DevOps</category>
      <author>킨글</author>
      <guid isPermaLink="true">https://kingle1024.tistory.com/610</guid>
      <comments>https://kingle1024.tistory.com/610#entry610comment</comments>
      <pubDate>Tue, 16 Sep 2025 16:33:08 +0900</pubDate>
    </item>
    <item>
      <title>우분투(Ubuntu) 노트북 덮개를 닫아도 절전모드가 안되도록 하기</title>
      <link>https://kingle1024.tistory.com/609</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개인 홈 서버를 구축하였는데 노트북 덮개를 덮었을 때에 절전모드로 전환하는 현상이 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 절전모드를 풀어주기로 했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 진입&lt;/p&gt;
&lt;pre id=&quot;code_1757816225629&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo vi /etc/systemd/logind.conf&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;#HandleLidSwitch=suspend 이라고 되어 있는 부분들을 HandleLidSwitch=ignore으로 변경해준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HandleLidSwitch=ignore &lt;br /&gt;HandleLidSwitchExternalPower=ignore &lt;br /&gt;HandleLidSwitchDocked=ignore &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1029&quot; data-origin-height=&quot;970&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brwu9j/btsQyBIxyQ3/YpdKlMdGmiqQf2kzjdc2y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brwu9j/btsQyBIxyQ3/YpdKlMdGmiqQf2kzjdc2y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brwu9j/btsQyBIxyQ3/YpdKlMdGmiqQf2kzjdc2y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbrwu9j%2FbtsQyBIxyQ3%2FYpdKlMdGmiqQf2kzjdc2y1%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;1029&quot; height=&quot;970&quot; data-origin-width=&quot;1029&quot; data-origin-height=&quot;970&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div id=&quot;SE-0591a10a-a75c-4b69-999f-4b19e11a097f&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-637c5571-0ab2-47e8-a38d-11b1593451d4&quot; style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;HandleLidSwitch 에서 설정할 수 있는 값&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-3d477e73-3d1f-4df5-a276-9cd0c47c84cf&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;table style=&quot;text-align: left; border-collapse: collapse; width: 100%; height: 126px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;설정값&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;설정결과&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;poweroff&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;시스템 잠그고 꺼짐&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;hibernate&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;최대 절전모드&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;suspend&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;절전모드&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;ignore&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;정상적으로 서비스됨&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;lock&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;덮개가 닫힐 때마다 시스템 자동 잠금&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-69203285-1259-4083-aed5-85f9e80c7284&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;p id=&quot;SE-c6c562f1-001c-4c8c-be45-c9b005851961&quot; style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-435b74bb-ef13-47ca-ae64-6887c7751c66&quot; style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아래 명령으로 변경한 설정을 적용해주면 노트북을 닫아도 정상적으로 서비스가 되는 것을 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1757816308613&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo systemctl restart systemd-login.service&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</description>
      <category>Server&amp;amp;DevOps</category>
      <author>킨글</author>
      <guid isPermaLink="true">https://kingle1024.tistory.com/609</guid>
      <comments>https://kingle1024.tistory.com/609#entry609comment</comments>
      <pubDate>Sun, 14 Sep 2025 11:18:57 +0900</pubDate>
    </item>
    <item>
      <title>ubuntu 절전 모드 처리</title>
      <link>https://kingle1024.tistory.com/608</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;ubuntu 노트북을 서버로 구축할 때 절전모드가 되어 불편한 상황이 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럴 때 해결하기 위한 방법이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;절전 모드 확인&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1757816021815&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ systemctl status sleep.target suspend.target hibernate.target hybrid-sleep.target&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IIyl8/btsQxbjwx9u/DR4MV4idRvGAd6mKjExTfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IIyl8/btsQxbjwx9u/DR4MV4idRvGAd6mKjExTfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IIyl8/btsQxbjwx9u/DR4MV4idRvGAd6mKjExTfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIIyl8%2FbtsQxbjwx9u%2FDR4MV4idRvGAd6mKjExTfk%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;1070&quot; height=&quot;549&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;549&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;절전 모드 처리&lt;/h4&gt;
&lt;pre id=&quot;code_1757816082190&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;339&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCiAmC/btsQxAKtK6j/jewrikwHu7z1X2CLHz09Qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCiAmC/btsQxAKtK6j/jewrikwHu7z1X2CLHz09Qk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCiAmC/btsQxAKtK6j/jewrikwHu7z1X2CLHz09Qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCiAmC%2FbtsQxAKtK6j%2FjewrikwHu7z1X2CLHz09Qk%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;1062&quot; height=&quot;339&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;339&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 절전모드가 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Server&amp;amp;DevOps</category>
      <author>킨글</author>
      <guid isPermaLink="true">https://kingle1024.tistory.com/608</guid>
      <comments>https://kingle1024.tistory.com/608#entry608comment</comments>
      <pubDate>Sun, 14 Sep 2025 11:15:56 +0900</pubDate>
    </item>
    <item>
      <title>지연 초기화 문제(Initializing Spring DispatcherServlet)</title>
      <link>https://kingle1024.tistory.com/607</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 운영 환경에서 처음 요청이 와야&amp;nbsp;Initializing Spring DispatcherServlet 'dispatcherServlet'이 실행되는 현상이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인해보니 Spring Boot의 기본 설정에서는 DispatcherServlet이 지연 초기화(lazy initialization)되도록 설정되어 있다고 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1751267570689&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;2025-06-30 05:24:27 2025-06-30T05:24:27.537+09:00  INFO 1 --- [] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path ''
2025-06-30 05:24:27 2025-06-30T05:24:27.937+09:00  INFO 1 --- [] [           main]      : Started NklcbdtyApplication in 194.784 seconds (process running for 211.547)
2025-06-30 16:01:35 2025-06-30T16:01:35.742+09:00  INFO 1 --- [] [nio-8080-exec-6] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-06-30 16:01:35 2025-06-30T16:01:35.841+09:00  INFO 1 --- [] [nio-8080-exec-6] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로 인해 첫 요청을 보내는 유저의 경우 사이트가 작동하지 않는 것으로 착각하여 이탈할 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 application.properties에 아래 설정을 통해 처리해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1751267813334&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring.mvc.servlet.load-on-startup=1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>java/Spring</category>
      <author>킨글</author>
      <guid isPermaLink="true">https://kingle1024.tistory.com/607</guid>
      <comments>https://kingle1024.tistory.com/607#entry607comment</comments>
      <pubDate>Mon, 30 Jun 2025 16:17:08 +0900</pubDate>
    </item>
    <item>
      <title>Spring Data JPA 메서드 명명 규칙</title>
      <link>https://kingle1024.tistory.com/606</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring&amp;nbsp;Data&amp;nbsp;JPA의&amp;nbsp;레포지토리&amp;nbsp;메서드&amp;nbsp;명명&amp;nbsp;규칙과&amp;nbsp;프로젝션을&amp;nbsp;함께&amp;nbsp;고려할&amp;nbsp;때,&amp;nbsp;email만&amp;nbsp;반환하는&amp;nbsp;메서드의&amp;nbsp;이름으로&amp;nbsp;findEmailByUserId는&amp;nbsp;매우&amp;nbsp;일반적이고&amp;nbsp;권장되는&amp;nbsp;명명&amp;nbsp;방식입니다. &lt;br /&gt;&lt;br /&gt;Spring&amp;nbsp;Data&amp;nbsp;JPA의&amp;nbsp;메서드&amp;nbsp;이름은&amp;nbsp;일반적으로&amp;nbsp;다음과&amp;nbsp;같은&amp;nbsp;구조를&amp;nbsp;따릅니다. &lt;br /&gt;&lt;br /&gt;[Prefix][Distinct]By[Criteria][Keywords][Order] &lt;br /&gt;&lt;br /&gt;[Prefix]:&amp;nbsp;find,&amp;nbsp;get,&amp;nbsp;read,&amp;nbsp;query,&amp;nbsp;count,&amp;nbsp;stream&amp;nbsp;등&amp;nbsp;조회&amp;nbsp;작업을&amp;nbsp;나타내는&amp;nbsp;접두사입니다. &lt;br /&gt;[Distinct]:&amp;nbsp;결과에서&amp;nbsp;중복을&amp;nbsp;제거할&amp;nbsp;때&amp;nbsp;사용합니다. &lt;br /&gt;By:&amp;nbsp;검색&amp;nbsp;조건(WHERE&amp;nbsp;절)의&amp;nbsp;시작을&amp;nbsp;나타내는&amp;nbsp;구분자입니다. &lt;br /&gt;[Criteria]:&amp;nbsp;By&amp;nbsp;뒤에&amp;nbsp;오는&amp;nbsp;부분으로,&amp;nbsp;WHERE&amp;nbsp;절에&amp;nbsp;사용될&amp;nbsp;엔티티&amp;nbsp;필드&amp;nbsp;이름과&amp;nbsp;조건을&amp;nbsp;명시합니다&amp;nbsp;(UserId,&amp;nbsp;UsernameAndAge,&amp;nbsp;LastNameLike&amp;nbsp;등).&amp;nbsp;이것이&amp;nbsp;메서드의&amp;nbsp;파라미터와&amp;nbsp;연결됩니다. &lt;br /&gt;[Keywords]:&amp;nbsp;And,&amp;nbsp;Or,&amp;nbsp;LessThan,&amp;nbsp;GreaterThan&amp;nbsp;등&amp;nbsp;조건을&amp;nbsp;결합하거나&amp;nbsp;상세화하는&amp;nbsp;키워드입니다. &lt;br /&gt;[Order]:&amp;nbsp;OrderBy&amp;nbsp;뒤에&amp;nbsp;오는&amp;nbsp;부분으로,&amp;nbsp;정렬&amp;nbsp;순서를&amp;nbsp;지정합니다. &lt;br /&gt;여기서&amp;nbsp;중요한&amp;nbsp;부분은&amp;nbsp;**[Prefix]&amp;nbsp;바로&amp;nbsp;뒤에&amp;nbsp;오는&amp;nbsp;부분(혹은&amp;nbsp;Distinct&amp;nbsp;뒤)**이&amp;nbsp;레포지토리&amp;nbsp;메서드가&amp;nbsp;무엇을&amp;nbsp;반환하는지&amp;nbsp;나타낼&amp;nbsp;수&amp;nbsp;있다는&amp;nbsp;것입니다.&amp;nbsp;전체&amp;nbsp;엔티티를&amp;nbsp;반환할&amp;nbsp;때는&amp;nbsp;이&amp;nbsp;부분을&amp;nbsp;생략하거나&amp;nbsp;엔티티&amp;nbsp;이름을&amp;nbsp;포함할&amp;nbsp;수&amp;nbsp;있지만&amp;nbsp;(예:&amp;nbsp;findUserVoByUserId),&amp;nbsp;프로젝션을&amp;nbsp;사용할&amp;nbsp;때는&amp;nbsp;조회하려는&amp;nbsp;필드나&amp;nbsp;프로젝션의&amp;nbsp;이름을&amp;nbsp;명시적으로&amp;nbsp;넣어주는&amp;nbsp;것이&amp;nbsp;일반적입니다. &lt;br /&gt;&lt;br /&gt;따라서&amp;nbsp;findEmailByUserId라는&amp;nbsp;이름은&amp;nbsp;다음과&amp;nbsp;같이&amp;nbsp;해석됩니다. &lt;br /&gt;&lt;br /&gt;find:&amp;nbsp;조회한다. &lt;br /&gt;Email:&amp;nbsp;(결과로)&amp;nbsp;email&amp;nbsp;필드(또는&amp;nbsp;email&amp;nbsp;프로젝션)를&amp;nbsp;가져온다. &lt;br /&gt;By:&amp;nbsp;조건은... &lt;br /&gt;UserId:&amp;nbsp;userId&amp;nbsp;필드를&amp;nbsp;기준으로&amp;nbsp;한다. &lt;br /&gt;즉,&amp;nbsp;이&amp;nbsp;메서드&amp;nbsp;이름은&amp;nbsp;&quot;userId를&amp;nbsp;기준으로&amp;nbsp;email&amp;nbsp;정보를&amp;nbsp;찾아온다&quot;는&amp;nbsp;의미를&amp;nbsp;명확하게&amp;nbsp;전달합니다.&amp;nbsp;요청&amp;nbsp;조건(UserId)과&amp;nbsp;반환&amp;nbsp;데이터의&amp;nbsp;핵심(Email)을&amp;nbsp;모두&amp;nbsp;포함하고&amp;nbsp;있으며,&amp;nbsp;특히&amp;nbsp;반환되는&amp;nbsp;데이터가&amp;nbsp;전체&amp;nbsp;엔티티가&amp;nbsp;아닌&amp;nbsp;특정&amp;nbsp;필드(또는&amp;nbsp;프로젝션)임을&amp;nbsp;이름으로&amp;nbsp;표현하는&amp;nbsp;좋은&amp;nbsp;방법입니다. &lt;br /&gt;&lt;br /&gt;다른&amp;nbsp;일반적인&amp;nbsp;명명&amp;nbsp;예시로는&amp;nbsp;findNameAndEmailByUserId,&amp;nbsp;findUserEmailProjectionByUserId&amp;nbsp;등이&amp;nbsp;있을&amp;nbsp;수&amp;nbsp;있지만,&amp;nbsp;findEmailByUserId는&amp;nbsp;간결하면서도&amp;nbsp;의미&amp;nbsp;전달이&amp;nbsp;확실하여&amp;nbsp;널리&amp;nbsp;사용됩니다. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>java/Spring</category>
      <category>jpa 네이밍</category>
      <category>jpa 메서드 명명 규칙</category>
      <author>킨글</author>
      <guid isPermaLink="true">https://kingle1024.tistory.com/606</guid>
      <comments>https://kingle1024.tistory.com/606#entry606comment</comments>
      <pubDate>Tue, 3 Jun 2025 12:04:14 +0900</pubDate>
    </item>
  </channel>
</rss>